Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask/config.py: 21%
107 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
1import errno
2import json
3import os
4import types
5import typing as t
7from werkzeug.utils import import_string
10class ConfigAttribute:
11 """Makes an attribute forward to the config"""
13 def __init__(self, name: str, get_converter: t.Optional[t.Callable] = None) -> None:
14 self.__name__ = name
15 self.get_converter = get_converter
17 def __get__(self, obj: t.Any, owner: t.Any = None) -> t.Any:
18 if obj is None:
19 return self
20 rv = obj.config[self.__name__]
21 if self.get_converter is not None:
22 rv = self.get_converter(rv)
23 return rv
25 def __set__(self, obj: t.Any, value: t.Any) -> None:
26 obj.config[self.__name__] = value
29class Config(dict):
30 """Works exactly like a dict but provides ways to fill it from files
31 or special dictionaries. There are two common patterns to populate the
32 config.
34 Either you can fill the config from a config file::
36 app.config.from_pyfile('yourconfig.cfg')
38 Or alternatively you can define the configuration options in the
39 module that calls :meth:`from_object` or provide an import path to
40 a module that should be loaded. It is also possible to tell it to
41 use the same module and with that provide the configuration values
42 just before the call::
44 DEBUG = True
45 SECRET_KEY = 'development key'
46 app.config.from_object(__name__)
48 In both cases (loading from any Python file or loading from modules),
49 only uppercase keys are added to the config. This makes it possible to use
50 lowercase values in the config file for temporary values that are not added
51 to the config or to define the config keys in the same file that implements
52 the application.
54 Probably the most interesting way to load configurations is from an
55 environment variable pointing to a file::
57 app.config.from_envvar('YOURAPPLICATION_SETTINGS')
59 In this case before launching the application you have to set this
60 environment variable to the file you want to use. On Linux and OS X
61 use the export statement::
63 export YOURAPPLICATION_SETTINGS='/path/to/config/file'
65 On windows use `set` instead.
67 :param root_path: path to which files are read relative from. When the
68 config object is created by the application, this is
69 the application's :attr:`~flask.Flask.root_path`.
70 :param defaults: an optional dictionary of default values
71 """
73 def __init__(self, root_path: str, defaults: t.Optional[dict] = None) -> None:
74 super().__init__(defaults or {})
75 self.root_path = root_path
77 def from_envvar(self, variable_name: str, silent: bool = False) -> bool:
78 """Loads a configuration from an environment variable pointing to
79 a configuration file. This is basically just a shortcut with nicer
80 error messages for this line of code::
82 app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
84 :param variable_name: name of the environment variable
85 :param silent: set to ``True`` if you want silent failure for missing
86 files.
87 :return: ``True`` if the file was loaded successfully.
88 """
89 rv = os.environ.get(variable_name)
90 if not rv:
91 if silent:
92 return False
93 raise RuntimeError(
94 f"The environment variable {variable_name!r} is not set"
95 " and as such configuration could not be loaded. Set"
96 " this variable and make it point to a configuration"
97 " file"
98 )
99 return self.from_pyfile(rv, silent=silent)
101 def from_prefixed_env(
102 self, prefix: str = "FLASK", *, loads: t.Callable[[str], t.Any] = json.loads
103 ) -> bool:
104 """Load any environment variables that start with ``FLASK_``,
105 dropping the prefix from the env key for the config key. Values
106 are passed through a loading function to attempt to convert them
107 to more specific types than strings.
109 Keys are loaded in :func:`sorted` order.
111 The default loading function attempts to parse values as any
112 valid JSON type, including dicts and lists.
114 Specific items in nested dicts can be set by separating the
115 keys with double underscores (``__``). If an intermediate key
116 doesn't exist, it will be initialized to an empty dict.
118 :param prefix: Load env vars that start with this prefix,
119 separated with an underscore (``_``).
120 :param loads: Pass each string value to this function and use
121 the returned value as the config value. If any error is
122 raised it is ignored and the value remains a string. The
123 default is :func:`json.loads`.
125 .. versionadded:: 2.1
126 """
127 prefix = f"{prefix}_"
128 len_prefix = len(prefix)
130 for key in sorted(os.environ):
131 if not key.startswith(prefix):
132 continue
134 value = os.environ[key]
136 try:
137 value = loads(value)
138 except Exception:
139 # Keep the value as a string if loading failed.
140 pass
142 # Change to key.removeprefix(prefix) on Python >= 3.9.
143 key = key[len_prefix:]
145 if "__" not in key:
146 # A non-nested key, set directly.
147 self[key] = value
148 continue
150 # Traverse nested dictionaries with keys separated by "__".
151 current = self
152 *parts, tail = key.split("__")
154 for part in parts:
155 # If an intermediate dict does not exist, create it.
156 if part not in current:
157 current[part] = {}
159 current = current[part]
161 current[tail] = value
163 return True
165 def from_pyfile(self, filename: str, silent: bool = False) -> bool:
166 """Updates the values in the config from a Python file. This function
167 behaves as if the file was imported as module with the
168 :meth:`from_object` function.
170 :param filename: the filename of the config. This can either be an
171 absolute filename or a filename relative to the
172 root path.
173 :param silent: set to ``True`` if you want silent failure for missing
174 files.
175 :return: ``True`` if the file was loaded successfully.
177 .. versionadded:: 0.7
178 `silent` parameter.
179 """
180 filename = os.path.join(self.root_path, filename)
181 d = types.ModuleType("config")
182 d.__file__ = filename
183 try:
184 with open(filename, mode="rb") as config_file:
185 exec(compile(config_file.read(), filename, "exec"), d.__dict__)
186 except OSError as e:
187 if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR):
188 return False
189 e.strerror = f"Unable to load configuration file ({e.strerror})"
190 raise
191 self.from_object(d)
192 return True
194 def from_object(self, obj: t.Union[object, str]) -> None:
195 """Updates the values from the given object. An object can be of one
196 of the following two types:
198 - a string: in this case the object with that name will be imported
199 - an actual object reference: that object is used directly
201 Objects are usually either modules or classes. :meth:`from_object`
202 loads only the uppercase attributes of the module/class. A ``dict``
203 object will not work with :meth:`from_object` because the keys of a
204 ``dict`` are not attributes of the ``dict`` class.
206 Example of module-based configuration::
208 app.config.from_object('yourapplication.default_config')
209 from yourapplication import default_config
210 app.config.from_object(default_config)
212 Nothing is done to the object before loading. If the object is a
213 class and has ``@property`` attributes, it needs to be
214 instantiated before being passed to this method.
216 You should not use this function to load the actual configuration but
217 rather configuration defaults. The actual config should be loaded
218 with :meth:`from_pyfile` and ideally from a location not within the
219 package because the package might be installed system wide.
221 See :ref:`config-dev-prod` for an example of class-based configuration
222 using :meth:`from_object`.
224 :param obj: an import name or object
225 """
226 if isinstance(obj, str):
227 obj = import_string(obj)
228 for key in dir(obj):
229 if key.isupper():
230 self[key] = getattr(obj, key)
232 def from_file(
233 self,
234 filename: str,
235 load: t.Callable[[t.IO[t.Any]], t.Mapping],
236 silent: bool = False,
237 ) -> bool:
238 """Update the values in the config from a file that is loaded
239 using the ``load`` parameter. The loaded data is passed to the
240 :meth:`from_mapping` method.
242 .. code-block:: python
244 import json
245 app.config.from_file("config.json", load=json.load)
247 import toml
248 app.config.from_file("config.toml", load=toml.load)
250 :param filename: The path to the data file. This can be an
251 absolute path or relative to the config root path.
252 :param load: A callable that takes a file handle and returns a
253 mapping of loaded data from the file.
254 :type load: ``Callable[[Reader], Mapping]`` where ``Reader``
255 implements a ``read`` method.
256 :param silent: Ignore the file if it doesn't exist.
257 :return: ``True`` if the file was loaded successfully.
259 .. versionadded:: 2.0
260 """
261 filename = os.path.join(self.root_path, filename)
263 try:
264 with open(filename) as f:
265 obj = load(f)
266 except OSError as e:
267 if silent and e.errno in (errno.ENOENT, errno.EISDIR):
268 return False
270 e.strerror = f"Unable to load configuration file ({e.strerror})"
271 raise
273 return self.from_mapping(obj)
275 def from_mapping(
276 self, mapping: t.Optional[t.Mapping[str, t.Any]] = None, **kwargs: t.Any
277 ) -> bool:
278 """Updates the config like :meth:`update` ignoring items with
279 non-upper keys.
281 :return: Always returns ``True``.
283 .. versionadded:: 0.11
284 """
285 mappings: t.Dict[str, t.Any] = {}
286 if mapping is not None:
287 mappings.update(mapping)
288 mappings.update(kwargs)
289 for key, value in mappings.items():
290 if key.isupper():
291 self[key] = value
292 return True
294 def get_namespace(
295 self, namespace: str, lowercase: bool = True, trim_namespace: bool = True
296 ) -> t.Dict[str, t.Any]:
297 """Returns a dictionary containing a subset of configuration options
298 that match the specified namespace/prefix. Example usage::
300 app.config['IMAGE_STORE_TYPE'] = 'fs'
301 app.config['IMAGE_STORE_PATH'] = '/var/app/images'
302 app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com'
303 image_store_config = app.config.get_namespace('IMAGE_STORE_')
305 The resulting dictionary `image_store_config` would look like::
307 {
308 'type': 'fs',
309 'path': '/var/app/images',
310 'base_url': 'http://img.website.com'
311 }
313 This is often useful when configuration options map directly to
314 keyword arguments in functions or class constructors.
316 :param namespace: a configuration namespace
317 :param lowercase: a flag indicating if the keys of the resulting
318 dictionary should be lowercase
319 :param trim_namespace: a flag indicating if the keys of the resulting
320 dictionary should not include the namespace
322 .. versionadded:: 0.11
323 """
324 rv = {}
325 for k, v in self.items():
326 if not k.startswith(namespace):
327 continue
328 if trim_namespace:
329 key = k[len(namespace) :]
330 else:
331 key = k
332 if lowercase:
333 key = key.lower()
334 rv[key] = v
335 return rv
337 def __repr__(self) -> str:
338 return f"<{type(self).__name__} {dict.__repr__(self)}>"