Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask/config.py: 25%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

116 statements  

1from __future__ import annotations 

2 

3import errno 

4import json 

5import os 

6import types 

7import typing as t 

8 

9from werkzeug.utils import import_string 

10 

11if t.TYPE_CHECKING: 

12 import typing_extensions as te 

13 

14 from .sansio.app import App 

15 

16 

17T = t.TypeVar("T") 

18 

19 

20class ConfigAttribute(t.Generic[T]): 

21 """Makes an attribute forward to the config""" 

22 

23 def __init__( 

24 self, name: str, get_converter: t.Callable[[t.Any], T] | None = None 

25 ) -> None: 

26 self.__name__ = name 

27 self.get_converter = get_converter 

28 

29 @t.overload 

30 def __get__(self, obj: None, owner: None) -> te.Self: ... 

31 

32 @t.overload 

33 def __get__(self, obj: App, owner: type[App]) -> T: ... 

34 

35 def __get__(self, obj: App | None, owner: type[App] | None = None) -> T | te.Self: 

36 if obj is None: 

37 return self 

38 

39 rv = obj.config[self.__name__] 

40 

41 if self.get_converter is not None: 

42 rv = self.get_converter(rv) 

43 

44 return rv # type: ignore[no-any-return] 

45 

46 def __set__(self, obj: App, value: t.Any) -> None: 

47 obj.config[self.__name__] = value 

48 

49 

50class Config(dict): # type: ignore[type-arg] 

51 """Works exactly like a dict but provides ways to fill it from files 

52 or special dictionaries. There are two common patterns to populate the 

53 config. 

54 

55 Either you can fill the config from a config file:: 

56 

57 app.config.from_pyfile('yourconfig.cfg') 

58 

59 Or alternatively you can define the configuration options in the 

60 module that calls :meth:`from_object` or provide an import path to 

61 a module that should be loaded. It is also possible to tell it to 

62 use the same module and with that provide the configuration values 

63 just before the call:: 

64 

65 DEBUG = True 

66 SECRET_KEY = 'development key' 

67 app.config.from_object(__name__) 

68 

69 In both cases (loading from any Python file or loading from modules), 

70 only uppercase keys are added to the config. This makes it possible to use 

71 lowercase values in the config file for temporary values that are not added 

72 to the config or to define the config keys in the same file that implements 

73 the application. 

74 

75 Probably the most interesting way to load configurations is from an 

76 environment variable pointing to a file:: 

77 

78 app.config.from_envvar('YOURAPPLICATION_SETTINGS') 

79 

80 In this case before launching the application you have to set this 

81 environment variable to the file you want to use. On Linux and OS X 

82 use the export statement:: 

83 

84 export YOURAPPLICATION_SETTINGS='/path/to/config/file' 

85 

86 On windows use `set` instead. 

87 

88 :param root_path: path to which files are read relative from. When the 

89 config object is created by the application, this is 

90 the application's :attr:`~flask.Flask.root_path`. 

91 :param defaults: an optional dictionary of default values 

92 """ 

93 

94 def __init__( 

95 self, 

96 root_path: str | os.PathLike[str], 

97 defaults: dict[str, t.Any] | None = None, 

98 ) -> None: 

99 super().__init__(defaults or {}) 

100 self.root_path = root_path 

101 

102 def from_envvar(self, variable_name: str, silent: bool = False) -> bool: 

103 """Loads a configuration from an environment variable pointing to 

104 a configuration file. This is basically just a shortcut with nicer 

105 error messages for this line of code:: 

106 

107 app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS']) 

108 

109 :param variable_name: name of the environment variable 

110 :param silent: set to ``True`` if you want silent failure for missing 

111 files. 

112 :return: ``True`` if the file was loaded successfully. 

113 """ 

114 rv = os.environ.get(variable_name) 

115 if not rv: 

116 if silent: 

117 return False 

118 raise RuntimeError( 

119 f"The environment variable {variable_name!r} is not set" 

120 " and as such configuration could not be loaded. Set" 

121 " this variable and make it point to a configuration" 

122 " file" 

123 ) 

124 return self.from_pyfile(rv, silent=silent) 

125 

126 def from_prefixed_env( 

127 self, prefix: str = "FLASK", *, loads: t.Callable[[str], t.Any] = json.loads 

128 ) -> bool: 

129 """Load any environment variables that start with ``FLASK_``, 

130 dropping the prefix from the env key for the config key. Values 

131 are passed through a loading function to attempt to convert them 

132 to more specific types than strings. 

133 

134 Keys are loaded in :func:`sorted` order. 

135 

136 The default loading function attempts to parse values as any 

137 valid JSON type, including dicts and lists. 

138 

139 Specific items in nested dicts can be set by separating the 

140 keys with double underscores (``__``). If an intermediate key 

141 doesn't exist, it will be initialized to an empty dict. 

142 

143 :param prefix: Load env vars that start with this prefix, 

144 separated with an underscore (``_``). 

145 :param loads: Pass each string value to this function and use 

146 the returned value as the config value. If any error is 

147 raised it is ignored and the value remains a string. The 

148 default is :func:`json.loads`. 

149 

150 .. versionadded:: 2.1 

151 """ 

152 prefix = f"{prefix}_" 

153 len_prefix = len(prefix) 

154 

155 for key in sorted(os.environ): 

156 if not key.startswith(prefix): 

157 continue 

158 

159 value = os.environ[key] 

160 

161 try: 

162 value = loads(value) 

163 except Exception: 

164 # Keep the value as a string if loading failed. 

165 pass 

166 

167 # Change to key.removeprefix(prefix) on Python >= 3.9. 

168 key = key[len_prefix:] 

169 

170 if "__" not in key: 

171 # A non-nested key, set directly. 

172 self[key] = value 

173 continue 

174 

175 # Traverse nested dictionaries with keys separated by "__". 

176 current = self 

177 *parts, tail = key.split("__") 

178 

179 for part in parts: 

180 # If an intermediate dict does not exist, create it. 

181 if part not in current: 

182 current[part] = {} 

183 

184 current = current[part] 

185 

186 current[tail] = value 

187 

188 return True 

189 

190 def from_pyfile( 

191 self, filename: str | os.PathLike[str], silent: bool = False 

192 ) -> bool: 

193 """Updates the values in the config from a Python file. This function 

194 behaves as if the file was imported as module with the 

195 :meth:`from_object` function. 

196 

197 :param filename: the filename of the config. This can either be an 

198 absolute filename or a filename relative to the 

199 root path. 

200 :param silent: set to ``True`` if you want silent failure for missing 

201 files. 

202 :return: ``True`` if the file was loaded successfully. 

203 

204 .. versionadded:: 0.7 

205 `silent` parameter. 

206 """ 

207 filename = os.path.join(self.root_path, filename) 

208 d = types.ModuleType("config") 

209 d.__file__ = filename 

210 try: 

211 with open(filename, mode="rb") as config_file: 

212 exec(compile(config_file.read(), filename, "exec"), d.__dict__) 

213 except OSError as e: 

214 if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR): 

215 return False 

216 e.strerror = f"Unable to load configuration file ({e.strerror})" 

217 raise 

218 self.from_object(d) 

219 return True 

220 

221 def from_object(self, obj: object | str) -> None: 

222 """Updates the values from the given object. An object can be of one 

223 of the following two types: 

224 

225 - a string: in this case the object with that name will be imported 

226 - an actual object reference: that object is used directly 

227 

228 Objects are usually either modules or classes. :meth:`from_object` 

229 loads only the uppercase attributes of the module/class. A ``dict`` 

230 object will not work with :meth:`from_object` because the keys of a 

231 ``dict`` are not attributes of the ``dict`` class. 

232 

233 Example of module-based configuration:: 

234 

235 app.config.from_object('yourapplication.default_config') 

236 from yourapplication import default_config 

237 app.config.from_object(default_config) 

238 

239 Nothing is done to the object before loading. If the object is a 

240 class and has ``@property`` attributes, it needs to be 

241 instantiated before being passed to this method. 

242 

243 You should not use this function to load the actual configuration but 

244 rather configuration defaults. The actual config should be loaded 

245 with :meth:`from_pyfile` and ideally from a location not within the 

246 package because the package might be installed system wide. 

247 

248 See :ref:`config-dev-prod` for an example of class-based configuration 

249 using :meth:`from_object`. 

250 

251 :param obj: an import name or object 

252 """ 

253 if isinstance(obj, str): 

254 obj = import_string(obj) 

255 for key in dir(obj): 

256 if key.isupper(): 

257 self[key] = getattr(obj, key) 

258 

259 def from_file( 

260 self, 

261 filename: str | os.PathLike[str], 

262 load: t.Callable[[t.IO[t.Any]], t.Mapping[str, t.Any]], 

263 silent: bool = False, 

264 text: bool = True, 

265 ) -> bool: 

266 """Update the values in the config from a file that is loaded 

267 using the ``load`` parameter. The loaded data is passed to the 

268 :meth:`from_mapping` method. 

269 

270 .. code-block:: python 

271 

272 import json 

273 app.config.from_file("config.json", load=json.load) 

274 

275 import tomllib 

276 app.config.from_file("config.toml", load=tomllib.load, text=False) 

277 

278 :param filename: The path to the data file. This can be an 

279 absolute path or relative to the config root path. 

280 :param load: A callable that takes a file handle and returns a 

281 mapping of loaded data from the file. 

282 :type load: ``Callable[[Reader], Mapping]`` where ``Reader`` 

283 implements a ``read`` method. 

284 :param silent: Ignore the file if it doesn't exist. 

285 :param text: Open the file in text or binary mode. 

286 :return: ``True`` if the file was loaded successfully. 

287 

288 .. versionchanged:: 2.3 

289 The ``text`` parameter was added. 

290 

291 .. versionadded:: 2.0 

292 """ 

293 filename = os.path.join(self.root_path, filename) 

294 

295 try: 

296 with open(filename, "r" if text else "rb") as f: 

297 obj = load(f) 

298 except OSError as e: 

299 if silent and e.errno in (errno.ENOENT, errno.EISDIR): 

300 return False 

301 

302 e.strerror = f"Unable to load configuration file ({e.strerror})" 

303 raise 

304 

305 return self.from_mapping(obj) 

306 

307 def from_mapping( 

308 self, mapping: t.Mapping[str, t.Any] | None = None, **kwargs: t.Any 

309 ) -> bool: 

310 """Updates the config like :meth:`update` ignoring items with 

311 non-upper keys. 

312 

313 :return: Always returns ``True``. 

314 

315 .. versionadded:: 0.11 

316 """ 

317 mappings: dict[str, t.Any] = {} 

318 if mapping is not None: 

319 mappings.update(mapping) 

320 mappings.update(kwargs) 

321 for key, value in mappings.items(): 

322 if key.isupper(): 

323 self[key] = value 

324 return True 

325 

326 def get_namespace( 

327 self, namespace: str, lowercase: bool = True, trim_namespace: bool = True 

328 ) -> dict[str, t.Any]: 

329 """Returns a dictionary containing a subset of configuration options 

330 that match the specified namespace/prefix. Example usage:: 

331 

332 app.config['IMAGE_STORE_TYPE'] = 'fs' 

333 app.config['IMAGE_STORE_PATH'] = '/var/app/images' 

334 app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com' 

335 image_store_config = app.config.get_namespace('IMAGE_STORE_') 

336 

337 The resulting dictionary `image_store_config` would look like:: 

338 

339 { 

340 'type': 'fs', 

341 'path': '/var/app/images', 

342 'base_url': 'http://img.website.com' 

343 } 

344 

345 This is often useful when configuration options map directly to 

346 keyword arguments in functions or class constructors. 

347 

348 :param namespace: a configuration namespace 

349 :param lowercase: a flag indicating if the keys of the resulting 

350 dictionary should be lowercase 

351 :param trim_namespace: a flag indicating if the keys of the resulting 

352 dictionary should not include the namespace 

353 

354 .. versionadded:: 0.11 

355 """ 

356 rv = {} 

357 for k, v in self.items(): 

358 if not k.startswith(namespace): 

359 continue 

360 if trim_namespace: 

361 key = k[len(namespace) :] 

362 else: 

363 key = k 

364 if lowercase: 

365 key = key.lower() 

366 rv[key] = v 

367 return rv 

368 

369 def __repr__(self) -> str: 

370 return f"<{type(self).__name__} {dict.__repr__(self)}>"