· 6 years ago · Nov 07, 2019, 12:30 PM
1# -*- coding: utf-8 -*-
2"""
3 flask.config
4 ~~~~~~~~~~~~
5
6 Implements the configuration related objects.
7
8 :copyright: 2010 Pallets
9 :license: BSD-3-Clause
10"""
11import errno
12import os
13import types
14
15from werkzeug.utils import import_string
16
17from . import json
18from ._compat import iteritems
19from ._compat import string_types
20
21
22class ConfigAttribute(object):
23 """Makes an attribute forward to the config"""
24
25 def __init__(self, name, get_converter=None):
26 self.__name__ = name
27 self.get_converter = get_converter
28
29 def __get__(self, obj, type=None):
30 if obj is None:
31 return self
32 rv = obj.config[self.__name__]
33 if self.get_converter is not None:
34 rv = self.get_converter(rv)
35 return rv
36
37 def __set__(self, obj, value):
38 obj.config[self.__name__] = value
39
40
41class Config(dict):
42 """Works exactly like a dict but provides ways to fill it from files
43 or special dictionaries. There are two common patterns to populate the
44 config.
45
46 Either you can fill the config from a config file::
47
48 app.config.from_pyfile('yourconfig.cfg')
49
50 Or alternatively you can define the configuration options in the
51 module that calls :meth:`from_object` or provide an import path to
52 a module that should be loaded. It is also possible to tell it to
53 use the same module and with that provide the configuration values
54 just before the call::
55
56 DEBUG = True
57 SECRET_KEY = 'development key'
58 app.config.from_object(__name__)
59
60 In both cases (loading from any Python file or loading from modules),
61 only uppercase keys are added to the config. This makes it possible to use
62 lowercase values in the config file for temporary values that are not added
63 to the config or to define the config keys in the same file that implements
64 the application.
65
66 Probably the most interesting way to load configurations is from an
67 environment variable pointing to a file::
68
69 app.config.from_envvar('YOURAPPLICATION_SETTINGS')
70
71 In this case before launching the application you have to set this
72 environment variable to the file you want to use. On Linux and OS X
73 use the export statement::
74
75 export YOURAPPLICATION_SETTINGS='/path/to/config/file'
76
77 On windows use `set` instead.
78
79 :param root_path: path to which files are read relative from. When the
80 config object is created by the application, this is
81 the application's :attr:`~flask.Flask.root_path`.
82 :param defaults: an optional dictionary of default values
83 """
84
85 def __init__(self, root_path, defaults=None):
86 dict.__init__(self, defaults or {})
87 self.root_path = root_path
88
89 def from_envvar(self, variable_name, silent=False):
90 """Loads a configuration from an environment variable pointing to
91 a configuration file. This is basically just a shortcut with nicer
92 error messages for this line of code::
93
94 app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
95
96 :param variable_name: name of the environment variable
97 :param silent: set to ``True`` if you want silent failure for missing
98 files.
99 :return: bool. ``True`` if able to load config, ``False`` otherwise.
100 """
101 rv = os.environ.get(variable_name)
102 if not rv:
103 if silent:
104 return False
105 raise RuntimeError(
106 "The environment variable %r is not set "
107 "and as such configuration could not be "
108 "loaded. Set this variable and make it "
109 "point to a configuration file" % variable_name
110 )
111 return self.from_pyfile(rv, silent=silent)
112
113 def from_pyfile(self, filename, silent=False):
114 """Updates the values in the config from a Python file. This function
115 behaves as if the file was imported as module with the
116 :meth:`from_object` function.
117
118 :param filename: the filename of the config. This can either be an
119 absolute filename or a filename relative to the
120 root path.
121 :param silent: set to ``True`` if you want silent failure for missing
122 files.
123
124 .. versionadded:: 0.7
125 `silent` parameter.
126 """
127 filename = os.path.join(self.root_path, filename)
128 d = types.ModuleType("config")
129 d.__file__ = filename
130 try:
131 with open(filename, mode="rb") as config_file:
132 exec(compile(config_file.read(), filename, "exec"), d.__dict__)
133 except IOError as e:
134 if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR):
135 return False
136 e.strerror = "Unable to load configuration file (%s)" % e.strerror
137 raise
138 self.from_object(d)
139 return True
140
141 def from_object(self, obj):
142 """Updates the values from the given object. An object can be of one
143 of the following two types:
144
145 - a string: in this case the object with that name will be imported
146 - an actual object reference: that object is used directly
147
148 Objects are usually either modules or classes. :meth:`from_object`
149 loads only the uppercase attributes of the module/class. A ``dict``
150 object will not work with :meth:`from_object` because the keys of a
151 ``dict`` are not attributes of the ``dict`` class.
152
153 Example of module-based configuration::
154
155 app.config.from_object('yourapplication.default_config')
156 from yourapplication import default_config
157 app.config.from_object(default_config)
158
159 Nothing is done to the object before loading. If the object is a
160 class and has ``@property`` attributes, it needs to be
161 instantiated before being passed to this method.
162
163 You should not use this function to load the actual configuration but
164 rather configuration defaults. The actual config should be loaded
165 with :meth:`from_pyfile` and ideally from a location not within the
166 package because the package might be installed system wide.
167
168 See :ref:`config-dev-prod` for an example of class-based configuration
169 using :meth:`from_object`.
170
171 :param obj: an import name or object
172 """
173 if isinstance(obj, string_types):
174 obj = import_string(obj)
175 for key in dir(obj):
176 if key.isupper():
177 self[key] = getattr(obj, key)
178
179 def from_json(self, filename, silent=False):
180 """Updates the values in the config from a JSON file. This function
181 behaves as if the JSON object was a dictionary and passed to the
182 :meth:`from_mapping` function.
183
184 :param filename: the filename of the JSON file. This can either be an
185 absolute filename or a filename relative to the
186 root path.
187 :param silent: set to ``True`` if you want silent failure for missing
188 files.
189
190 .. versionadded:: 0.11
191 """
192 filename = os.path.join(self.root_path, filename)
193
194 try:
195 with open(filename) as json_file:
196 obj = json.loads(json_file.read())
197 except IOError as e:
198 if silent and e.errno in (errno.ENOENT, errno.EISDIR):
199 return False
200 e.strerror = "Unable to load configuration file (%s)" % e.strerror
201 raise
202 return self.from_mapping(obj)
203
204 def from_mapping(self, *mapping, **kwargs):
205 """Updates the config like :meth:`update` ignoring items with non-upper
206 keys.
207
208 .. versionadded:: 0.11
209 """
210 mappings = []
211 if len(mapping) == 1:
212 if hasattr(mapping[0], "items"):
213 mappings.append(mapping[0].items())
214 else:
215 mappings.append(mapping[0])
216 elif len(mapping) > 1:
217 raise TypeError(
218 "expected at most 1 positional argument, got %d" % len(mapping)
219 )
220 mappings.append(kwargs.items())
221 for mapping in mappings:
222 for (key, value) in mapping:
223 if key.isupper():
224 self[key] = value
225 return True
226
227 def get_namespace(self, namespace, lowercase=True, trim_namespace=True):
228 """Returns a dictionary containing a subset of configuration options
229 that match the specified namespace/prefix. Example usage::
230
231 app.config['IMAGE_STORE_TYPE'] = 'fs'
232 app.config['IMAGE_STORE_PATH'] = '/var/app/images'
233 app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com'
234 image_store_config = app.config.get_namespace('IMAGE_STORE_')
235
236 The resulting dictionary `image_store_config` would look like::
237
238 {
239 'type': 'fs',
240 'path': '/var/app/images',
241 'base_url': 'http://img.website.com'
242 }
243
244 This is often useful when configuration options map directly to
245 keyword arguments in functions or class constructors.
246
247 :param namespace: a configuration namespace
248 :param lowercase: a flag indicating if the keys of the resulting
249 dictionary should be lowercase
250 :param trim_namespace: a flag indicating if the keys of the resulting
251 dictionary should not include the namespace
252
253 .. versionadded:: 0.11
254 """
255 rv = {}
256 for k, v in iteritems(self):
257 if not k.startswith(namespace):
258 continue
259 if trim_namespace:
260 key = k[len(namespace) :]
261 else:
262 key = k
263 if lowercase:
264 key = key.lower()
265 rv[key] = v
266 return rv
267
268 def __repr__(self):
269 return "<%s %s>" % (self.__class__.__name__, dict.__repr__(self))