Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
config.py371 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        len_prefix = len(prefix)154 155        for key in sorted(os.environ):156            if not key.startswith(prefix):157                continue158 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                pass166 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] = value173                continue174 175            # Traverse nested dictionaries with keys separated by "__".176            current = self177            *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] = value187 188        return True189 190    def from_pyfile(191        self, filename: str | os.PathLike[str], silent: bool = False192    ) -> bool:193        """Updates the values in the config from a Python file.  This function194        behaves as if the file was imported as module with the195        :meth:`from_object` function.196 197        :param filename: the filename of the config.  This can either be an198                         absolute filename or a filename relative to the199                         root path.200        :param silent: set to ``True`` if you want silent failure for missing201                       files.202        :return: ``True`` if the file was loaded successfully.203 204        .. versionadded:: 0.7205           `silent` parameter.206        """207        filename = os.path.join(self.root_path, filename)208        d = types.ModuleType("config")209        d.__file__ = filename210        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 False216            e.strerror = f"Unable to load configuration file ({e.strerror})"217            raise218        self.from_object(d)219        return True220 221    def from_object(self, obj: object | str) -> None:222        """Updates the values from the given object.  An object can be of one223        of the following two types:224 225        -   a string: in this case the object with that name will be imported226        -   an actual object reference: that object is used directly227 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 a231        ``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_config237            app.config.from_object(default_config)238 239        Nothing is done to the object before loading. If the object is a240        class and has ``@property`` attributes, it needs to be241        instantiated before being passed to this method.242 243        You should not use this function to load the actual configuration but244        rather configuration defaults.  The actual config should be loaded245        with :meth:`from_pyfile` and ideally from a location not within the246        package because the package might be installed system wide.247 248        See :ref:`config-dev-prod` for an example of class-based configuration249        using :meth:`from_object`.250 251        :param obj: an import name or object252        """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 loaded267        using the ``load`` parameter. The loaded data is passed to the268        :meth:`from_mapping` method.269 270        .. code-block:: python271 272            import json273            app.config.from_file("config.json", load=json.load)274 275            import tomllib276            app.config.from_file("config.toml", load=tomllib.load, text=False)277 278        :param filename: The path to the data file. This can be an279            absolute path or relative to the config root path.280        :param load: A callable that takes a file handle and returns a281            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.3289            The ``text`` parameter was added.290 291        .. versionadded:: 2.0292        """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 False301 302            e.strerror = f"Unable to load configuration file ({e.strerror})"303            raise304 305        return self.from_mapping(obj)306 307    def from_mapping(308        self, mapping: t.Mapping[str, t.Any] | None = None, **kwargs: t.Any309    ) -> bool:310        """Updates the config like :meth:`update` ignoring items with311        non-upper keys.312 313        :return: Always returns ``True``.314 315        .. versionadded:: 0.11316        """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] = value324        return True325 326    def get_namespace(327        self, namespace: str, lowercase: bool = True, trim_namespace: bool = True328    ) -> dict[str, t.Any]:329        """Returns a dictionary containing a subset of configuration options330        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 to346        keyword arguments in functions or class constructors.347 348        :param namespace: a configuration namespace349        :param lowercase: a flag indicating if the keys of the resulting350                          dictionary should be lowercase351        :param trim_namespace: a flag indicating if the keys of the resulting352                          dictionary should not include the namespace353 354        .. versionadded:: 0.11355        """356        rv = {}357        for k, v in self.items():358            if not k.startswith(namespace):359                continue360            if trim_namespace:361                key = k[len(namespace) :]362            else:363                key = k364            if lowercase:365                key = key.lower()366            rv[key] = v367        return rv368 369    def __repr__(self) -> str:370        return f"<{type(self).__name__} {dict.__repr__(self)}>"371 
codekingpro/portable-devtools · Team Ai