flask/config.py 分析

请添加图片描述

flask/config.py 这段代码是关于 Flask 应用的配置类 Config 的实现。Config 类继承了字典(dict)类,提供了一些特殊的方法,来方便进行配置参数的设置和获取。以下是这些方法的作用:

  • Config.from_envvar():从环境变量载入配置,变量名为 variable_name;
  • Config.from_prefixed_env():快速从以某个前缀(prefix)开头的环境变量中载入配置;
  • Config.from_pyfile():从一个 Python 文件中载入配置,文件名为 filename;
  • Config.from_object():从 Python 对象(obj)中载入配置,对象可以是一个字符串,也可以是一个实际对象的引用;
  • Config.from_file():从一个文件中根据指定函数(load)将数据载入到配置中;
  • Config.from_mapping():从一个 Mapping 对象或一组关键字参数中载入配置;
  • Config.get_namespace():获取和指定命名空间(namespace)匹配的配置参数字典。

另外,这个 Config 类还提供了 ConfigAttribute 类,可以将类的属性转为对应 Config 对象中的 key,实现方便的调用和设置。

以下是代码具体注释:

# 引入一些依赖库
import errno
import json
import os
import types
import typing as t

from werkzeug.utils import import_string


class ConfigAttribute:
    """
    使属性可以指向配置文件中的相应值
    """

    def __init__(self, name: str, get_converter: t.Callable | None = None) -> None:
        self.__name__ = name  # 属性名称
        self.get_converter = get_converter  # 根据需要,对属性进行转换的函数

    def __get__(self, obj: t.Any, owner: t.Any = None) -> t.Any:
        """
        访问器方法,用于获取对象的属性值
        """

        if obj is None:
            return self  # 如果 obj 是 None,则返回自身

        rv = obj.config[self.__name__]  # 从 config 字典中获取相应的值

        if self.get_converter is not None:  # 如果 get_converter 函数存在
            rv = self.get_converter(rv)  # 调用转换函数

        return rv

    def __set__(self, obj: t.Any, value: t.Any) -> None:
        """
        访问器方法,用于设置对象的属性值
        """

        obj.config[self.__name__] = value  # 将 value 设置为 config 字典中相应的值


class Config(dict):
    """
    提供了一种从文件中加载配置的方式
    """

    def __init__(self, root_path: str | os.PathLike,
                 defaults: dict | None = None) -> None:
        """
        构造函数,初始化对象
        """

        super().__init__(defaults or {})
        self.root_path = root_path  # 设置根目录

    def from_envvar(self, variable_name: str, silent: bool = False) -> bool:
        """
        从环境变量中指定的文件加载配置
        """

        rv = os.environ.get(variable_name)

        if not rv:
            if silent:
                return False
            raise RuntimeError(
                f"The environment variable {variable_name!r} is not set"
                " and as such configuration could not be loaded. Set"
                " this variable and make it point to a configuration"
                " file"
            )

        return self.from_pyfile(rv, silent=silent)  # 从 Python 文件中加载配置

    def from_prefixed_env(self, prefix: str = "FLASK", *,
                          loads: t.Callable[[str], t.Any] = json.loads) -> bool:
        """
        从以指定前缀开头的环境变量中加载配置
        """

        prefix = f"{prefix}_"
        len_prefix = len(prefix)

        for key in sorted(os.environ):
            if not key.startswith(prefix):
                continue

            value = os.environ[key]

            try:
                value = loads(value)
            except Exception:
                pass

            key = key[len_prefix:]

            if "__" not in key:
                self[key] = value
                continue

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

            for part in parts:
                if part not in current:
                    current[part] = {}

                current = current[part]

            current[tail] = value

        return True

    def from_pyfile(self, filename: str | os.PathLike, silent: bool = False) -> bool:
        """
        从 Python 文件中更新配置
        """

        filename = os.path.join(self.root_path, filename)
        d = types.ModuleType("config")  # 创建模块对象
        d.__file__ = filename

        try:
            with open(filename, mode="rb") as config_file:  # 打开文件
                exec(compile(config_file.read(), filename, "exec"), d.__dict__)
        except OSError as e:
            if silent and e.errno in (
                errno.ENOENT, errno.EISDIR,
                errno.ENOTDIR):  # 如果发生错误并设置了 silent,则返回 False
                return False
            e.strerror = f"Unable to load configuration file ({e.strerror})"
            raise

        self.from_object(d)  # 从对象中更新配置

        return True

    def from_object(self, obj: object | str) -> None:
        """
        从给定的对象或名称中更新配置
        """

        if isinstance(obj, str):
            obj = import_string(obj)

        for key in dir(obj):
            if key.isupper():
                self[key] = getattr(obj, key)

    def from_file(
        self,
        filename: str | os.PathLike,
        load: t.Callable[[t.IO[t.Any]], t.Mapping],
        silent: bool = False,
        text: bool = True,
    ) -> bool:
        """
        从指定文件中加载配置
        """

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

        try:
            with open(filename, "r" if text else "rb") as f:  # 打开文件,如果 text 为真则以文本模式打开
                obj = load(f)  # 通过 load 函数将文件内容加载到对象中
        except OSError as e:
            if silent and e.errno in (errno.ENOENT, errno.EISDIR):
                return False
            e.strerror = f"Unable to load configuration file ({e.strerror})"
            raise

        return self.from_mapping(obj)  # 从对象中更新配置

    def from_mapping(
        self, mapping: t.Mapping[str, t.Any] | None = None, **kwargs: t.Any
    ) -> bool:
        """
        更新配置映射的值
        """

        mappings: dict[str, t.Any] = {}
        if mapping is not None:
            mappings.update(mapping)
        mappings.update(kwargs)
        for key, value in mappings.items():
            if key.isupper():
                self[key] = value
        return True

    def get_namespace(
        self, namespace: str, lowercase: bool = True, trim_namespace: bool = True
    ) -> dict[str, t.Any]:
        """
        获取指定命名空间的配置字典
        """

        rv = {}
        for k, v in self.items():
            if not k.startswith(namespace):
                continue
            if trim_namespace:
                key = k[len(namespace):]
            else:
                key = k
            if lowercase:
                key = key.lower()
            rv[key] = v
        return rv

    def __repr__(self) -> str:
        """
        返回对象的字符串表示形式
        """

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

「已注销」

不打赏也没关系,点点关注呀

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值