We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
config cache prevents updating a value before expiration
Motivation
----------
```python
config.set('throttler', 'release_strategy', 'fifo')
config.set('throttler', 'release_strategy', 'grouped_fifo')
```
will result in a database duplicate exception because `config.set()` checks if there is already a value which will be `False` if you try to set a new value under the expiration time because it got set to `False` with the first command.


Modification
------------
change the `has_option()` check in `config.set()` to `has_option(use_cache=False)`



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/rucio/core/config.py`
Content:
```
1 # Copyright 2014-2019 CERN for the benefit of the ATLAS collaboration.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 #
15 # Authors:
16 # - Mario Lassnig <mario.lassnig@cern.ch>, 2014-2019
17 # - Vincent Garonne <vgaronne@gmail.com>, 2015-2017
18 # - Cedric Serfon <cedric.serfon@cern.ch>, 2017
19 # - Hannes Hansen <hannes.jakob.hansen@cern.ch>, 2018-2019
20 #
21 # PY3K COMPATIBLE
22 
23 from dogpile.cache import make_region
24 from dogpile.cache.api import NoValue
25 
26 from rucio.common.exception import ConfigNotFound
27 from rucio.db.sqla import models
28 from rucio.db.sqla.session import read_session, transactional_session
29 
30 
31 REGION = make_region().configure('dogpile.cache.memcached',
32                                  expiration_time=3600,
33                                  arguments={'url': "127.0.0.1:11211", 'distributed_lock': True})
34 
35 
36 @read_session
37 def sections(use_cache=True, expiration_time=3600, session=None):
38     """
39     Return a list of the sections available.
40 
41     :param use_cache: Boolean if the cache should be used.
42     :param expiration_time: Time after that the cached value gets ignored.
43     :param session: The database session in use.
44     :returns: ['section_name', ...]
45     """
46 
47     sections_key = 'sections'
48     all_sections = NoValue()
49     if use_cache:
50         all_sections = read_from_cache(sections_key, expiration_time)
51     if isinstance(all_sections, NoValue):
52         query = session.query(models.Config.section).distinct().all()
53         all_sections = [section[0] for section in query]
54         write_to_cache(sections_key, all_sections)
55 
56     return all_sections
57 
58 
59 @transactional_session
60 def add_section(section, session=None):
61     """
62     Add a section to the configuration.
63     :param session: The database session in use.
64     :param section: The name of the section.
65     """
66 
67     raise NotImplementedError('Irrelevant - sections cannot exist without options')
68 
69 
70 @read_session
71 def has_section(section, use_cache=True, expiration_time=3600, session=None):
72     """
73     Indicates whether the named section is present in the configuration.
74 
75     :param section: The name of the section.
76     :param use_cache: Boolean if the cache should be used.
77     :param expiration_time: Time after that the cached value gets ignored.
78     :param session: The database session in use.
79     :returns: True/False
80     """
81     has_section_key = 'has_section_%s' % section
82     has_section = NoValue()
83     if use_cache:
84         has_section = read_from_cache(has_section_key, expiration_time)
85     if isinstance(has_section, NoValue):
86         query = session.query(models.Config).filter_by(section=section)
87         has_section = True if query.first() else False
88         write_to_cache(has_section_key, has_section)
89     return has_section
90 
91 
92 @read_session
93 def options(section, use_cache=True, expiration_time=3600, session=None):
94     """
95     Returns a list of options available in the specified section.
96 
97     :param section: The name of the section.
98     :param use_cache: Boolean if the cache should be used.
99     :param expiration_time: Time after that the cached value gets ignored.
100     :param session: The database session in use.
101     :returns: ['option', ...]
102     """
103     options_key = 'options'
104     options = NoValue()
105     if use_cache:
106         options = read_from_cache(options_key, expiration_time)
107     if isinstance(options, NoValue):
108         query = session.query(models.Config.opt).filter_by(section=section).distinct().all()
109         options = [option[0] for option in query]
110         write_to_cache(options_key, options)
111     return options
112 
113 
114 @read_session
115 def has_option(section, option, use_cache=True, expiration_time=3600, session=None):
116     """
117     Check if the given section exists and contains the given option.
118 
119     :param section: The name of the section.
120     :param option: The name of the option.
121     :param use_cache: Boolean if the cache should be used.
122     :param expiration_time: Time after that the cached value gets ignored.
123     :param session: The database session in use.
124     :returns: True/False
125     """
126     has_option_key = 'has_option_%s_%s' % (section, option)
127     has_option = NoValue()
128     if use_cache:
129         has_option = read_from_cache(has_option_key, expiration_time)
130     if isinstance(has_option, NoValue):
131         query = session.query(models.Config).filter_by(section=section, opt=option)
132         has_option = True if query.first() else False
133         write_to_cache(has_option_key, has_option)
134     return has_option
135 
136 
137 @read_session
138 def get(section, option, default=None, use_cache=True, expiration_time=3600, session=None):
139     """
140     Get an option value for the named section. Value can be auto-coerced to string, int, float, bool, None.
141 
142     Caveat emptor: Strings, regardless the case, matching 'on'/off', 'true'/'false', 'yes'/'no' are converted to bool.
143                    0/1 are converted to int, and not to bool.
144 
145     :param section: The name of the section.
146     :param option: The name of the option.
147     :param default: The default value if no value is found.
148     :param use_cache: Boolean if the cache should be used.
149     :param expiration_time: Time after that the cached value gets ignored.
150     :param session: The database session in use.
151     :returns: The auto-coerced value.
152     """
153     value_key = 'get_%s_%s' % (section, option)
154     value = NoValue()
155     if use_cache:
156         value = read_from_cache(value_key, expiration_time)
157     if isinstance(value, NoValue):
158         tmp = session.query(models.Config.value).filter_by(section=section, opt=option).first()
159         if tmp is not None:
160             value = __convert_type(tmp[0])
161             write_to_cache(value_key, tmp[0])
162         elif default is None:
163             raise ConfigNotFound
164         else:
165             value = default
166     else:
167         value = __convert_type(value)
168     return value
169 
170 
171 @read_session
172 def items(section, use_cache=True, expiration_time=3600, session=None):
173     """
174     Return a list of (option, value) pairs for each option in the given section. Values are auto-coerced as in get().
175 
176     :param section: The name of the section.
177     :param use_cache: Boolean if the cache should be used.
178     :param expiration_time: Time after that the cached value gets ignored.
179     :param session: The database session in use.
180     :returns: [('option', auto-coerced value), ...]
181     """
182     items_key = 'items_%s' % section
183     items = NoValue()
184     if use_cache:
185         items = read_from_cache(items_key, expiration_time)
186     if isinstance(items, NoValue):
187         items = session.query(models.Config.opt, models.Config.value).filter_by(section=section).all()
188         write_to_cache(items_key, items)
189     return [(item[0], __convert_type(item[1])) for item in items]
190 
191 
192 @transactional_session
193 def set(section, option, value, session=None):
194     """
195     Set the given option to the specified value. If the option doesn't exist, it is created.
196 
197     :param section: The name of the section.
198     :param option: The name of the option.
199     :param value: The content of the value.
200     :param session: The database session in use.
201     """
202 
203     if not has_option(section=section, option=option, session=session):
204         new_option = models.Config(section=section, opt=option, value=value)
205         new_option.save(session=session)
206     else:
207         old_option = models.Config.__history_mapper__.class_(section=section,
208                                                              opt=option,
209                                                              value=session.query(models.Config.value).filter_by(section=section,
210                                                                                                                 opt=option).first()[0])
211         old_option.save(session=session)
212         session.query(models.Config).filter_by(section=section, opt=option).update({'value': str(value)})
213 
214 
215 @transactional_session
216 def remove_section(section, session=None):
217     """
218     Remove the specified section from the specified section.
219 
220     :param section: The name of the section.
221     :param session: The database session in use.
222     :returns: True/False.
223     """
224 
225     if not has_section(section=section, session=session):
226         return False
227     else:
228         for old in session.query(models.Config.value).filter_by(section=section).all():
229             old_option = models.Config.__history_mapper__.class_(section=old[0],
230                                                                  opt=old[1],
231                                                                  value=old[2])
232             old_option.save(session=session)
233         session.query(models.Config).filter_by(section=section).delete()
234         return True
235 
236 
237 @transactional_session
238 def remove_option(section, option, session=None):
239     """
240     Remove the specified option from the configuration.
241 
242     :param section: The name of the section.
243     :param option: The name of the option.
244     :param session: The database session in use.
245     :returns: True/False
246     """
247 
248     if not has_option(section=section, option=option, session=session):
249         return False
250     else:
251         old_option = models.Config.__history_mapper__.class_(section=section,
252                                                              opt=option,
253                                                              value=session.query(models.Config.value).filter_by(section=section,
254                                                                                                                 opt=option).first()[0])
255         old_option.save(session=session)
256         session.query(models.Config).filter_by(section=section, opt=option).delete()
257         return True
258 
259 
260 def __convert_type(value):
261     '''
262     __convert_type
263     '''
264     if value.lower() in ['true', 'yes', 'on']:
265         return True
266     elif value.lower() in ['false', 'no', 'off']:
267         return False
268 
269     for conv in (int, float):
270         try:
271             return conv(value)
272         except:
273             pass
274 
275     return value
276 
277 
278 def read_from_cache(key, expiration_time=3600):
279     """
280     Try to read a value from a cache.
281 
282     :param key: Key that stores the value.
283     :param expiration_time: Time in seconds that a value should not be older than.
284     """
285     key = key.replace(' ', '')
286     value = REGION.get(key, expiration_time=expiration_time)
287     return value
288 
289 
290 def write_to_cache(key, value):
291     """
292     Set a value on a key in a cache.
293 
294     :param key: Key that stores the value.
295     :param value: Value to be stored.
296     """
297     key = key.replace(' ', '')
298     REGION.set(key, value)
299
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/rucio/core/config.py b/lib/rucio/core/config.py
--- a/lib/rucio/core/config.py
+++ b/lib/rucio/core/config.py
@@ -200,7 +200,7 @@
     :param session: The database session in use.
     """
 
-    if not has_option(section=section, option=option, session=session):
+    if not has_option(section=section, option=option, use_cache=False, session=session):
         new_option = models.Config(section=section, opt=option, value=value)
         new_option.save(session=session)
     else:

{"golden_diff": "diff --git a/lib/rucio/core/config.py b/lib/rucio/core/config.py\n--- a/lib/rucio/core/config.py\n+++ b/lib/rucio/core/config.py\n@@ -200,7 +200,7 @@\n     :param session: The database session in use.\n     \"\"\"\n \n-    if not has_option(section=section, option=option, session=session):\n+    if not has_option(section=section, option=option, use_cache=False, session=session):\n         new_option = models.Config(section=section, opt=option, value=value)\n         new_option.save(session=session)\n     else:\n", "issue": "config cache prevents updating a value before expiration\nMotivation\r\n----------\r\n```python\r\nconfig.set('throttler', 'release_strategy', 'fifo')\r\nconfig.set('throttler', 'release_strategy', 'grouped_fifo')\r\n```\r\nwill result in a database duplicate exception because `config.set()` checks if there is already a value which will be `False` if you try to set a new value under the expiration time because it got set to `False` with the first command.\r\n\r\n\r\nModification\r\n------------\r\nchange the `has_option()` check in `config.set()` to `has_option(use_cache=False)`\r\n\r\n\n", "before_files": [{"content": "# Copyright 2014-2019 CERN for the benefit of the ATLAS collaboration.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# Authors:\n# - Mario Lassnig <mario.lassnig@cern.ch>, 2014-2019\n# - Vincent Garonne <vgaronne@gmail.com>, 2015-2017\n# - Cedric Serfon <cedric.serfon@cern.ch>, 2017\n# - Hannes Hansen <hannes.jakob.hansen@cern.ch>, 2018-2019\n#\n# PY3K COMPATIBLE\n\nfrom dogpile.cache import make_region\nfrom dogpile.cache.api import NoValue\n\nfrom rucio.common.exception import ConfigNotFound\nfrom rucio.db.sqla import models\nfrom rucio.db.sqla.session import read_session, transactional_session\n\n\nREGION = make_region().configure('dogpile.cache.memcached',\n                                 expiration_time=3600,\n                                 arguments={'url': \"127.0.0.1:11211\", 'distributed_lock': True})\n\n\n@read_session\ndef sections(use_cache=True, expiration_time=3600, session=None):\n    \"\"\"\n    Return a list of the sections available.\n\n    :param use_cache: Boolean if the cache should be used.\n    :param expiration_time: Time after that the cached value gets ignored.\n    :param session: The database session in use.\n    :returns: ['section_name', ...]\n    \"\"\"\n\n    sections_key = 'sections'\n    all_sections = NoValue()\n    if use_cache:\n        all_sections = read_from_cache(sections_key, expiration_time)\n    if isinstance(all_sections, NoValue):\n        query = session.query(models.Config.section).distinct().all()\n        all_sections = [section[0] for section in query]\n        write_to_cache(sections_key, all_sections)\n\n    return all_sections\n\n\n@transactional_session\ndef add_section(section, session=None):\n    \"\"\"\n    Add a section to the configuration.\n    :param session: The database session in use.\n    :param section: The name of the section.\n    \"\"\"\n\n    raise NotImplementedError('Irrelevant - sections cannot exist without options')\n\n\n@read_session\ndef has_section(section, use_cache=True, expiration_time=3600, session=None):\n    \"\"\"\n    Indicates whether the named section is present in the configuration.\n\n    :param section: The name of the section.\n    :param use_cache: Boolean if the cache should be used.\n    :param expiration_time: Time after that the cached value gets ignored.\n    :param session: The database session in use.\n    :returns: True/False\n    \"\"\"\n    has_section_key = 'has_section_%s' % section\n    has_section = NoValue()\n    if use_cache:\n        has_section = read_from_cache(has_section_key, expiration_time)\n    if isinstance(has_section, NoValue):\n        query = session.query(models.Config).filter_by(section=section)\n        has_section = True if query.first() else False\n        write_to_cache(has_section_key, has_section)\n    return has_section\n\n\n@read_session\ndef options(section, use_cache=True, expiration_time=3600, session=None):\n    \"\"\"\n    Returns a list of options available in the specified section.\n\n    :param section: The name of the section.\n    :param use_cache: Boolean if the cache should be used.\n    :param expiration_time: Time after that the cached value gets ignored.\n    :param session: The database session in use.\n    :returns: ['option', ...]\n    \"\"\"\n    options_key = 'options'\n    options = NoValue()\n    if use_cache:\n        options = read_from_cache(options_key, expiration_time)\n    if isinstance(options, NoValue):\n        query = session.query(models.Config.opt).filter_by(section=section).distinct().all()\n        options = [option[0] for option in query]\n        write_to_cache(options_key, options)\n    return options\n\n\n@read_session\ndef has_option(section, option, use_cache=True, expiration_time=3600, session=None):\n    \"\"\"\n    Check if the given section exists and contains the given option.\n\n    :param section: The name of the section.\n    :param option: The name of the option.\n    :param use_cache: Boolean if the cache should be used.\n    :param expiration_time: Time after that the cached value gets ignored.\n    :param session: The database session in use.\n    :returns: True/False\n    \"\"\"\n    has_option_key = 'has_option_%s_%s' % (section, option)\n    has_option = NoValue()\n    if use_cache:\n        has_option = read_from_cache(has_option_key, expiration_time)\n    if isinstance(has_option, NoValue):\n        query = session.query(models.Config).filter_by(section=section, opt=option)\n        has_option = True if query.first() else False\n        write_to_cache(has_option_key, has_option)\n    return has_option\n\n\n@read_session\ndef get(section, option, default=None, use_cache=True, expiration_time=3600, session=None):\n    \"\"\"\n    Get an option value for the named section. Value can be auto-coerced to string, int, float, bool, None.\n\n    Caveat emptor: Strings, regardless the case, matching 'on'/off', 'true'/'false', 'yes'/'no' are converted to bool.\n                   0/1 are converted to int, and not to bool.\n\n    :param section: The name of the section.\n    :param option: The name of the option.\n    :param default: The default value if no value is found.\n    :param use_cache: Boolean if the cache should be used.\n    :param expiration_time: Time after that the cached value gets ignored.\n    :param session: The database session in use.\n    :returns: The auto-coerced value.\n    \"\"\"\n    value_key = 'get_%s_%s' % (section, option)\n    value = NoValue()\n    if use_cache:\n        value = read_from_cache(value_key, expiration_time)\n    if isinstance(value, NoValue):\n        tmp = session.query(models.Config.value).filter_by(section=section, opt=option).first()\n        if tmp is not None:\n            value = __convert_type(tmp[0])\n            write_to_cache(value_key, tmp[0])\n        elif default is None:\n            raise ConfigNotFound\n        else:\n            value = default\n    else:\n        value = __convert_type(value)\n    return value\n\n\n@read_session\ndef items(section, use_cache=True, expiration_time=3600, session=None):\n    \"\"\"\n    Return a list of (option, value) pairs for each option in the given section. Values are auto-coerced as in get().\n\n    :param section: The name of the section.\n    :param use_cache: Boolean if the cache should be used.\n    :param expiration_time: Time after that the cached value gets ignored.\n    :param session: The database session in use.\n    :returns: [('option', auto-coerced value), ...]\n    \"\"\"\n    items_key = 'items_%s' % section\n    items = NoValue()\n    if use_cache:\n        items = read_from_cache(items_key, expiration_time)\n    if isinstance(items, NoValue):\n        items = session.query(models.Config.opt, models.Config.value).filter_by(section=section).all()\n        write_to_cache(items_key, items)\n    return [(item[0], __convert_type(item[1])) for item in items]\n\n\n@transactional_session\ndef set(section, option, value, session=None):\n    \"\"\"\n    Set the given option to the specified value. If the option doesn't exist, it is created.\n\n    :param section: The name of the section.\n    :param option: The name of the option.\n    :param value: The content of the value.\n    :param session: The database session in use.\n    \"\"\"\n\n    if not has_option(section=section, option=option, session=session):\n        new_option = models.Config(section=section, opt=option, value=value)\n        new_option.save(session=session)\n    else:\n        old_option = models.Config.__history_mapper__.class_(section=section,\n                                                             opt=option,\n                                                             value=session.query(models.Config.value).filter_by(section=section,\n                                                                                                                opt=option).first()[0])\n        old_option.save(session=session)\n        session.query(models.Config).filter_by(section=section, opt=option).update({'value': str(value)})\n\n\n@transactional_session\ndef remove_section(section, session=None):\n    \"\"\"\n    Remove the specified section from the specified section.\n\n    :param section: The name of the section.\n    :param session: The database session in use.\n    :returns: True/False.\n    \"\"\"\n\n    if not has_section(section=section, session=session):\n        return False\n    else:\n        for old in session.query(models.Config.value).filter_by(section=section).all():\n            old_option = models.Config.__history_mapper__.class_(section=old[0],\n                                                                 opt=old[1],\n                                                                 value=old[2])\n            old_option.save(session=session)\n        session.query(models.Config).filter_by(section=section).delete()\n        return True\n\n\n@transactional_session\ndef remove_option(section, option, session=None):\n    \"\"\"\n    Remove the specified option from the configuration.\n\n    :param section: The name of the section.\n    :param option: The name of the option.\n    :param session: The database session in use.\n    :returns: True/False\n    \"\"\"\n\n    if not has_option(section=section, option=option, session=session):\n        return False\n    else:\n        old_option = models.Config.__history_mapper__.class_(section=section,\n                                                             opt=option,\n                                                             value=session.query(models.Config.value).filter_by(section=section,\n                                                                                                                opt=option).first()[0])\n        old_option.save(session=session)\n        session.query(models.Config).filter_by(section=section, opt=option).delete()\n        return True\n\n\ndef __convert_type(value):\n    '''\n    __convert_type\n    '''\n    if value.lower() in ['true', 'yes', 'on']:\n        return True\n    elif value.lower() in ['false', 'no', 'off']:\n        return False\n\n    for conv in (int, float):\n        try:\n            return conv(value)\n        except:\n            pass\n\n    return value\n\n\ndef read_from_cache(key, expiration_time=3600):\n    \"\"\"\n    Try to read a value from a cache.\n\n    :param key: Key that stores the value.\n    :param expiration_time: Time in seconds that a value should not be older than.\n    \"\"\"\n    key = key.replace(' ', '')\n    value = REGION.get(key, expiration_time=expiration_time)\n    return value\n\n\ndef write_to_cache(key, value):\n    \"\"\"\n    Set a value on a key in a cache.\n\n    :param key: Key that stores the value.\n    :param value: Value to be stored.\n    \"\"\"\n    key = key.replace(' ', '')\n    REGION.set(key, value)\n", "path": "lib/rucio/core/config.py"}], "after_files": [{"content": "# Copyright 2014-2019 CERN for the benefit of the ATLAS collaboration.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# Authors:\n# - Mario Lassnig <mario.lassnig@cern.ch>, 2014-2019\n# - Vincent Garonne <vgaronne@gmail.com>, 2015-2017\n# - Cedric Serfon <cedric.serfon@cern.ch>, 2017\n# - Hannes Hansen <hannes.jakob.hansen@cern.ch>, 2018-2019\n#\n# PY3K COMPATIBLE\n\nfrom dogpile.cache import make_region\nfrom dogpile.cache.api import NoValue\n\nfrom rucio.common.exception import ConfigNotFound\nfrom rucio.db.sqla import models\nfrom rucio.db.sqla.session import read_session, transactional_session\n\n\nREGION = make_region().configure('dogpile.cache.memcached',\n                                 expiration_time=3600,\n                                 arguments={'url': \"127.0.0.1:11211\", 'distributed_lock': True})\n\n\n@read_session\ndef sections(use_cache=True, expiration_time=3600, session=None):\n    \"\"\"\n    Return a list of the sections available.\n\n    :param use_cache: Boolean if the cache should be used.\n    :param expiration_time: Time after that the cached value gets ignored.\n    :param session: The database session in use.\n    :returns: ['section_name', ...]\n    \"\"\"\n\n    sections_key = 'sections'\n    all_sections = NoValue()\n    if use_cache:\n        all_sections = read_from_cache(sections_key, expiration_time)\n    if isinstance(all_sections, NoValue):\n        query = session.query(models.Config.section).distinct().all()\n        all_sections = [section[0] for section in query]\n        write_to_cache(sections_key, all_sections)\n\n    return all_sections\n\n\n@transactional_session\ndef add_section(section, session=None):\n    \"\"\"\n    Add a section to the configuration.\n    :param session: The database session in use.\n    :param section: The name of the section.\n    \"\"\"\n\n    raise NotImplementedError('Irrelevant - sections cannot exist without options')\n\n\n@read_session\ndef has_section(section, use_cache=True, expiration_time=3600, session=None):\n    \"\"\"\n    Indicates whether the named section is present in the configuration.\n\n    :param section: The name of the section.\n    :param use_cache: Boolean if the cache should be used.\n    :param expiration_time: Time after that the cached value gets ignored.\n    :param session: The database session in use.\n    :returns: True/False\n    \"\"\"\n    has_section_key = 'has_section_%s' % section\n    has_section = NoValue()\n    if use_cache:\n        has_section = read_from_cache(has_section_key, expiration_time)\n    if isinstance(has_section, NoValue):\n        query = session.query(models.Config).filter_by(section=section)\n        has_section = True if query.first() else False\n        write_to_cache(has_section_key, has_section)\n    return has_section\n\n\n@read_session\ndef options(section, use_cache=True, expiration_time=3600, session=None):\n    \"\"\"\n    Returns a list of options available in the specified section.\n\n    :param section: The name of the section.\n    :param use_cache: Boolean if the cache should be used.\n    :param expiration_time: Time after that the cached value gets ignored.\n    :param session: The database session in use.\n    :returns: ['option', ...]\n    \"\"\"\n    options_key = 'options'\n    options = NoValue()\n    if use_cache:\n        options = read_from_cache(options_key, expiration_time)\n    if isinstance(options, NoValue):\n        query = session.query(models.Config.opt).filter_by(section=section).distinct().all()\n        options = [option[0] for option in query]\n        write_to_cache(options_key, options)\n    return options\n\n\n@read_session\ndef has_option(section, option, use_cache=True, expiration_time=3600, session=None):\n    \"\"\"\n    Check if the given section exists and contains the given option.\n\n    :param section: The name of the section.\n    :param option: The name of the option.\n    :param use_cache: Boolean if the cache should be used.\n    :param expiration_time: Time after that the cached value gets ignored.\n    :param session: The database session in use.\n    :returns: True/False\n    \"\"\"\n    has_option_key = 'has_option_%s_%s' % (section, option)\n    has_option = NoValue()\n    if use_cache:\n        has_option = read_from_cache(has_option_key, expiration_time)\n    if isinstance(has_option, NoValue):\n        query = session.query(models.Config).filter_by(section=section, opt=option)\n        has_option = True if query.first() else False\n        write_to_cache(has_option_key, has_option)\n    return has_option\n\n\n@read_session\ndef get(section, option, default=None, use_cache=True, expiration_time=3600, session=None):\n    \"\"\"\n    Get an option value for the named section. Value can be auto-coerced to string, int, float, bool, None.\n\n    Caveat emptor: Strings, regardless the case, matching 'on'/off', 'true'/'false', 'yes'/'no' are converted to bool.\n                   0/1 are converted to int, and not to bool.\n\n    :param section: The name of the section.\n    :param option: The name of the option.\n    :param default: The default value if no value is found.\n    :param use_cache: Boolean if the cache should be used.\n    :param expiration_time: Time after that the cached value gets ignored.\n    :param session: The database session in use.\n    :returns: The auto-coerced value.\n    \"\"\"\n    value_key = 'get_%s_%s' % (section, option)\n    value = NoValue()\n    if use_cache:\n        value = read_from_cache(value_key, expiration_time)\n    if isinstance(value, NoValue):\n        tmp = session.query(models.Config.value).filter_by(section=section, opt=option).first()\n        if tmp is not None:\n            value = __convert_type(tmp[0])\n            write_to_cache(value_key, tmp[0])\n        elif default is None:\n            raise ConfigNotFound\n        else:\n            value = default\n    else:\n        value = __convert_type(value)\n    return value\n\n\n@read_session\ndef items(section, use_cache=True, expiration_time=3600, session=None):\n    \"\"\"\n    Return a list of (option, value) pairs for each option in the given section. Values are auto-coerced as in get().\n\n    :param section: The name of the section.\n    :param use_cache: Boolean if the cache should be used.\n    :param expiration_time: Time after that the cached value gets ignored.\n    :param session: The database session in use.\n    :returns: [('option', auto-coerced value), ...]\n    \"\"\"\n    items_key = 'items_%s' % section\n    items = NoValue()\n    if use_cache:\n        items = read_from_cache(items_key, expiration_time)\n    if isinstance(items, NoValue):\n        items = session.query(models.Config.opt, models.Config.value).filter_by(section=section).all()\n        write_to_cache(items_key, items)\n    return [(item[0], __convert_type(item[1])) for item in items]\n\n\n@transactional_session\ndef set(section, option, value, session=None):\n    \"\"\"\n    Set the given option to the specified value. If the option doesn't exist, it is created.\n\n    :param section: The name of the section.\n    :param option: The name of the option.\n    :param value: The content of the value.\n    :param session: The database session in use.\n    \"\"\"\n\n    if not has_option(section=section, option=option, use_cache=False, session=session):\n        new_option = models.Config(section=section, opt=option, value=value)\n        new_option.save(session=session)\n    else:\n        old_option = models.Config.__history_mapper__.class_(section=section,\n                                                             opt=option,\n                                                             value=session.query(models.Config.value).filter_by(section=section,\n                                                                                                                opt=option).first()[0])\n        old_option.save(session=session)\n        session.query(models.Config).filter_by(section=section, opt=option).update({'value': str(value)})\n\n\n@transactional_session\ndef remove_section(section, session=None):\n    \"\"\"\n    Remove the specified section from the specified section.\n\n    :param section: The name of the section.\n    :param session: The database session in use.\n    :returns: True/False.\n    \"\"\"\n\n    if not has_section(section=section, session=session):\n        return False\n    else:\n        for old in session.query(models.Config.value).filter_by(section=section).all():\n            old_option = models.Config.__history_mapper__.class_(section=old[0],\n                                                                 opt=old[1],\n                                                                 value=old[2])\n            old_option.save(session=session)\n        session.query(models.Config).filter_by(section=section).delete()\n        return True\n\n\n@transactional_session\ndef remove_option(section, option, session=None):\n    \"\"\"\n    Remove the specified option from the configuration.\n\n    :param section: The name of the section.\n    :param option: The name of the option.\n    :param session: The database session in use.\n    :returns: True/False\n    \"\"\"\n\n    if not has_option(section=section, option=option, session=session):\n        return False\n    else:\n        old_option = models.Config.__history_mapper__.class_(section=section,\n                                                             opt=option,\n                                                             value=session.query(models.Config.value).filter_by(section=section,\n                                                                                                                opt=option).first()[0])\n        old_option.save(session=session)\n        session.query(models.Config).filter_by(section=section, opt=option).delete()\n        return True\n\n\ndef __convert_type(value):\n    '''\n    __convert_type\n    '''\n    if value.lower() in ['true', 'yes', 'on']:\n        return True\n    elif value.lower() in ['false', 'no', 'off']:\n        return False\n\n    for conv in (int, float):\n        try:\n            return conv(value)\n        except:\n            pass\n\n    return value\n\n\ndef read_from_cache(key, expiration_time=3600):\n    \"\"\"\n    Try to read a value from a cache.\n\n    :param key: Key that stores the value.\n    :param expiration_time: Time in seconds that a value should not be older than.\n    \"\"\"\n    key = key.replace(' ', '')\n    value = REGION.get(key, expiration_time=expiration_time)\n    return value\n\n\ndef write_to_cache(key, value):\n    \"\"\"\n    Set a value on a key in a cache.\n\n    :param key: Key that stores the value.\n    :param value: Value to be stored.\n    \"\"\"\n    key = key.replace(' ', '')\n    REGION.set(key, value)\n", "path": "lib/rucio/core/config.py"}]}