Source code for flask_constance.storage

import typing as t

from flask import current_app, g

from .backends import Backend, BackendCache
from .signals import constance_get, constance_set


[docs]class Storage: """Flask-Constance settings storage. This is access point for getting and setting constance settings. :param backend: Backend instance to use. By default is Memory backend. :param cache: Caching backend to use. This is optional. """ # NOTE: This hack prevents setting undeclared constance settings. __slots__ = ["_backend", "_cache"] def __init__(self, backend: Backend, cache: t.Optional[BackendCache] = None): self._backend: Backend = backend self._cache: t.Optional[BackendCache] = cache @property def _payload(self) -> t.Dict[str, t.Any]: """Constance payload. Just proxy to CONSTANCE_PAYLOAD application config variable.""" return current_app.config["CONSTANCE_PAYLOAD"] @property def _ctx_cache(self) -> t.Dict[str, t.Any]: """Application context cache. This is used to prevent unnessesary lookups to backend.""" if not hasattr(g, "_constance_runtime_cache"): g._constance_runtime_cache = {} return g._constance_runtime_cache @property def _mut_log(self) -> t.Dict[str, t.Any]: """Mutations log with name of the setting and it's value.""" if not hasattr(g, "_constance_mut_watchdog"): g._constance_mut_watchdog = {} return g._constance_mut_watchdog def __dir__(self) -> t.Iterable[str]: return tuple(self._payload.keys()) def __getattr__(self, name: str) -> t.Any: if name not in self._payload: raise AttributeError( f"'{self.__class__.__name__}' object has no attribute '{name}'" ) constance_get.send(self, name=name) # First of all - try to get value from runtime cache. if name in self._ctx_cache: return self._ctx_cache[name] # Then try to find value in backend cache. if self._cache is not None: try: value = self._cache.get(name) except KeyError: pass else: self._ctx_cache[name] = value return value # Finally go to the actual backend. try: value = self._backend.get(name) except KeyError: value = self._payload[name] self._backend.set(name, value) if self._cache is not None: self._cahce.set(name, value) self._ctx_cache[name] = value if isinstance(value, (dict, list, set)): self._mut_log[name] = value return value else: if self._cache is not None: self._cache.set(name, value) self._ctx_cache[name] = value if isinstance(value, (dict, list, set)): self._mut_log[name] = value return value def __setattr__(self, name: str, value: t.Any) -> None: if name in self.__slots__: return super().__setattr__(name, value) if name not in self._payload: return super().__setattr__(name, value) constance_set.send(self, name=name, value=value) self._backend.set(name, value) if self._cache is not None: self._cache.invalidate(name) self._ctx_cache[name] = value def __delattr__(self, name: str) -> None: if name in self.__slots__: return super().__delattr__(name) if name not in self._payload: return super().__delattr__(name) return self.__setattr__(name, self._payload[name])