Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
config.py368 linesDownload Raw Back to flask
1from __future__ import annotations2 3import errno4import json5import os6import types7import typing as t8 9from werkzeug.utils import import_string10 11if t.TYPE_CHECKING:12    import typing_extensions as te13 14    from .sansio.app import App15 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 = None25    ) -> None:26        self.__name__ = name27        self.get_converter = get_converter28 29    @t.overload30    def __get__(self, obj: None, owner: None) -> te.Self: ...31 32    @t.overload33    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 self38 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__] = value48 49 50class Config(dict):  # type: ignore[type-arg]51    """Works exactly like a dict but provides ways to fill it from files52    or special dictionaries.  There are two common patterns to populate the53    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 the60    module that calls :meth:`from_object` or provide an import path to61    a module that should be loaded.  It is also possible to tell it to62    use the same module and with that provide the configuration values63    just before the call::64 65        DEBUG = True66        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 use71    lowercase values in the config file for temporary values that are not added72    to the config or to define the config keys in the same file that implements73    the application.74 75    Probably the most interesting way to load configurations is from an76    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 this81    environment variable to the file you want to use.  On Linux and OS X82    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 the89                      config object is created by the application, this is90                      the application's :attr:`~flask.Flask.root_path`.91    :param defaults: an optional dictionary of default values92    """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_path101 102    def from_envvar(self, variable_name: str, silent: bool = False) -> bool:103        """Loads a configuration from an environment variable pointing to104        a configuration file.  This is basically just a shortcut with nicer105        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 variable110        :param silent: set to ``True`` if you want silent failure for missing111                       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 False118            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.loads128    ) -> bool:129        """Load any environment variables that start with ``FLASK_``,130        dropping the prefix from the env key for the config key. Values131        are passed through a loading function to attempt to convert them132        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 any137        valid JSON type, including dicts and lists.138 139        Specific items in nested dicts can be set by separating the140        keys with double underscores (``__``). If an intermediate key141        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 use146            the returned value as the config value. If any error is147            raised it is ignored and the value remains a string. The148            default is :func:`json.loads`.149 150        .. versionadded:: 2.1151        """152        prefix = f"{prefix}_"153 154        for key in sorted(os.environ):155            if not key.startswith(prefix):156                continue157 158            value = os.environ[key]159            key = key.removeprefix(prefix)160 161            try:162                value = loads(value)163            except Exception:164                # Keep the value as a string if loading failed.165                pass166 167            if "__" not in key:168                # A non-nested key, set directly.169                self[key] = value170                continue171 172            # Traverse nested dictionaries with keys separated by "__".173            current = self174            *parts, tail = key.split("__")175 176            for part in parts:177                # If an intermediate dict does not exist, create it.178                if part not in current:179                    current[part] = {}180 181                current = current[part]182 183            current[tail] = value184 185        return True186 187    def from_pyfile(188        self, filename: str | os.PathLike[str], silent: bool = False189    ) -> bool:190        """Updates the values in the config from a Python file.  This function191        behaves as if the file was imported as module with the192        :meth:`from_object` function.193 194        :param filename: the filename of the config.  This can either be an195                         absolute filename or a filename relative to the196                         root path.197        :param silent: set to ``True`` if you want silent failure for missing198                       files.199        :return: ``True`` if the file was loaded successfully.200 201        .. versionadded:: 0.7202           `silent` parameter.203        """204        filename = os.path.join(self.root_path, filename)205        d = types.ModuleType("config")206        d.__file__ = filename207        try:208            with open(filename, mode="rb") as config_file:209                exec(compile(config_file.read(), filename, "exec"), d.__dict__)210        except OSError as e:211            if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR):212                return False213            e.strerror = f"Unable to load configuration file ({e.strerror})"214            raise215        self.from_object(d)216        return True217 218    def from_object(self, obj: object | str) -> None:219        """Updates the values from the given object.  An object can be of one220        of the following two types:221 222        -   a string: in this case the object with that name will be imported223        -   an actual object reference: that object is used directly224 225        Objects are usually either modules or classes. :meth:`from_object`226        loads only the uppercase attributes of the module/class. A ``dict``227        object will not work with :meth:`from_object` because the keys of a228        ``dict`` are not attributes of the ``dict`` class.229 230        Example of module-based configuration::231 232            app.config.from_object('yourapplication.default_config')233            from yourapplication import default_config234            app.config.from_object(default_config)235 236        Nothing is done to the object before loading. If the object is a237        class and has ``@property`` attributes, it needs to be238        instantiated before being passed to this method.239 240        You should not use this function to load the actual configuration but241        rather configuration defaults.  The actual config should be loaded242        with :meth:`from_pyfile` and ideally from a location not within the243        package because the package might be installed system wide.244 245        See :ref:`config-dev-prod` for an example of class-based configuration246        using :meth:`from_object`.247 248        :param obj: an import name or object249        """250        if isinstance(obj, str):251            obj = import_string(obj)252        for key in dir(obj):253            if key.isupper():254                self[key] = getattr(obj, key)255 256    def from_file(257        self,258        filename: str | os.PathLike[str],259        load: t.Callable[[t.IO[t.Any]], t.Mapping[str, t.Any]],260        silent: bool = False,261        text: bool = True,262    ) -> bool:263        """Update the values in the config from a file that is loaded264        using the ``load`` parameter. The loaded data is passed to the265        :meth:`from_mapping` method.266 267        .. code-block:: python268 269            import json270            app.config.from_file("config.json", load=json.load)271 272            import tomllib273            app.config.from_file("config.toml", load=tomllib.load, text=False)274 275        :param filename: The path to the data file. This can be an276            absolute path or relative to the config root path.277        :param load: A callable that takes a file handle and returns a278            mapping of loaded data from the file.279        :type load: ``Callable[[Reader], Mapping]`` where ``Reader``280            implements a ``read`` method.281        :param silent: Ignore the file if it doesn't exist.282        :param text: Open the file in text or binary mode.283        :return: ``True`` if the file was loaded successfully.284 285        .. versionchanged:: 2.3286            The ``text`` parameter was added.287 288        .. versionadded:: 2.0289        """290        filename = os.path.join(self.root_path, filename)291 292        try:293            with open(filename, "r" if text else "rb") as f:294                obj = load(f)295        except OSError as e:296            if silent and e.errno in (errno.ENOENT, errno.EISDIR):297                return False298 299            e.strerror = f"Unable to load configuration file ({e.strerror})"300            raise301 302        return self.from_mapping(obj)303 304    def from_mapping(305        self, mapping: t.Mapping[str, t.Any] | None = None, **kwargs: t.Any306    ) -> bool:307        """Updates the config like :meth:`update` ignoring items with308        non-upper keys.309 310        :return: Always returns ``True``.311 312        .. versionadded:: 0.11313        """314        mappings: dict[str, t.Any] = {}315        if mapping is not None:316            mappings.update(mapping)317        mappings.update(kwargs)318        for key, value in mappings.items():319            if key.isupper():320                self[key] = value321        return True322 323    def get_namespace(324        self, namespace: str, lowercase: bool = True, trim_namespace: bool = True325    ) -> dict[str, t.Any]:326        """Returns a dictionary containing a subset of configuration options327        that match the specified namespace/prefix. Example usage::328 329            app.config['IMAGE_STORE_TYPE'] = 'fs'330            app.config['IMAGE_STORE_PATH'] = '/var/app/images'331            app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com'332            image_store_config = app.config.get_namespace('IMAGE_STORE_')333 334        The resulting dictionary `image_store_config` would look like::335 336            {337                'type': 'fs',338                'path': '/var/app/images',339                'base_url': 'http://img.website.com'340            }341 342        This is often useful when configuration options map directly to343        keyword arguments in functions or class constructors.344 345        :param namespace: a configuration namespace346        :param lowercase: a flag indicating if the keys of the resulting347                          dictionary should be lowercase348        :param trim_namespace: a flag indicating if the keys of the resulting349                          dictionary should not include the namespace350 351        .. versionadded:: 0.11352        """353        rv = {}354        for k, v in self.items():355            if not k.startswith(namespace):356                continue357            if trim_namespace:358                key = k[len(namespace) :]359            else:360                key = k361            if lowercase:362                key = key.lower()363            rv[key] = v364        return rv365 366    def __repr__(self) -> str:367        return f"<{type(self).__name__} {dict.__repr__(self)}>"368 
codekingpro/portable-devtools · Team Ai