资讯专栏INFORMATION COLUMN

flask源码阅读系列一config模块

DirtyMind / 1467人阅读

摘要:首先假设是一个包名或者模块名非对象名。参数可以为或者字节码对象,返回。这里可以理解为解释字节码,并将字节码执行过程中的全局变量存入字典中。

import_string

先来分析一下这个动态导入函数:werkzeug.utils.import_string

def import_string(import_name, silent=False):
    """基于字符串动态导入对象。字符串有两种形式写法用点来分割对象名(``xml.sax.saxutils.escape``)和用冒号来分割对象名
(``xml.sax.saxutils:escape``).
silent决定发生异常时是返回None还是抛出一些异常(安静,挺形象的。)
    """

    import_name = str(import_name).replace(":", ".")
    try:
        try:
            #首先假设import_name是一个包名或者模块名(非对象名)。
            __import__(import_name)
        except ImportError: 
            if "." not in import_name: #没有点说明即使我们假设错误,也没法继续尝试。故而立即抛出异常
                raise
        else:
        #如果导入正确,那么可以从sys.modules字典里面获取已经导入的模块或包.
        #为什么要通过sys.modules[import_name]返回而不是直接return  __import__(import_name)呢?因为__import__在没有提供fromlist参数时,返回值策略为返回第一个点之前的顶级包对象,比如"os.path"返回的是os而不是os.path.
            return sys.modules[import_name]
        #如果import_name不是包名或模块名,而是具体的对象、函数名。
        module_name, obj_name = import_name.rsplit(".", 1)
        try:
        #相当于from module_name import obj_name
            module = __import__(module_name, None, None, [obj_name])
        except ImportError:
            #如果支持的模块没有被父模块设置好的情形中会导入失败,这时需要先导入父模块(或包)。典型的例子就是logging与logging.config。
            module = import_string(module_name)

        try:
        #通过getarr从模块中返回具体对象
            return getattr(module, obj_name)
        except AttributeError as e:
            raise ImportError(e)

    except ImportError as e:
        if not silent:
            reraise(
                ImportStringError,
                ImportStringError(import_name, e),
                sys.exc_info()[2]) 
flask.config模块
import os
import types
import errno

from werkzeug.utils import import_string
from ._compat import string_types, iteritems
from . import json

class Config(dict):
    """config类除了正常的dict职责外,还具有从py文件(模块)、对象或字典填充值的能力。
        from_envvar会调用from_pyfile会调用from_object,另还有from_json会调用from_mapping
       还有一个便利的get_namespace方法。
    """

    def __init__(self, root_path, defaults=None): # root_path查找py文件的基路径。
        dict.__init__(self, defaults or {})
        self.root_path = root_path
    def from_envvar(self, variable_name, silent=False):
        """variable_name是一个环境变量名,指向一个py文件。返回bool值,是否加载填充成功。
        """
        rv = os.environ.get(variable_name)
        #...省略部分校验逻辑...
        #这里调用from_pyfile
        return self.from_pyfile(rv, silent=silent)

    def from_pyfile(self, filename, silent=False):
        filename = os.path.join(self.root_path, filename)
        #利用标准库types来动态创建一个模块对象,名为config,文件属性(__file__)指向设置为前面的filename
        d = types.ModuleType("config")
        d.__file__ = filename
        try:
            with open(filename) as config_file:
                #exec,compile都是内置函数。
                #exec(object[, globals[, locals]]) 参数object可以为str或者字节码对象,返回None。这里可以理解为解释字节码,并将字节码执行过程中的全局变量存入d.__dict__字典中。
                #compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
                #compile用于动态编译文件为字节码对象,其中"exec"为编译模式,代表整个文件(多行)编译,其余模式还有"eval"单行语句编译,"single"单行交互式语句编译。
                exec(compile(config_file.read(), filename, "exec"), d.__dict__)
        except IOError as e:
            ...略pass...
        #这里调用from_object,from_object才是关键。
        self.from_object(d)
        return True

    def from_object(self, obj):
        """参数obj可以是str(需要被导入的对象)或者一个对象。它只允许模块或对象中的大写属性填充进来,小写的忽略。
        你可以这样来使用本方法
            app.config.from_object("yourapplication.default_config")或者
            from yourapplication import default_config
            app.config.from_object(default_config)
        """
        if isinstance(obj, string_types):
            obj = import_string(obj)
        for key in dir(obj):
            if key.isupper(): #只允许模块或对象中的大写属性填充进来,小写的忽略。
                self[key] = getattr(obj, key)

    def from_json(self, filename, silent=False):
        """filename是json文件名。这是flask0.11添加的方法。
        """
        filename = os.path.join(self.root_path, filename)
        try:
            with open(filename) as json_file:
                obj = json.loads(json_file.read()) #注意,如果不知道json.loads的type参数,那么默认加载为字典。即obj是一个字典
        except IOError as e:
            ...略pass...
        return self.from_mapping(obj) #这里调用from_mapping

    def from_mapping(self, *mapping, **kwargs):
        """行为类似于dict的update方法,只不过忽略小写属性.flask0.11添加的方法
        """
        mappings = []
        if len(mapping) == 1:
            if hasattr(mapping[0], "items"):
                mappings.append(mapping[0].items())
            else:
                mappings.append(mapping[0])
        elif len(mapping) > 1:
            raise TypeError(
                "expected at most 1 positional argument, got %d" % len(mapping)
            )
        mappings.append(kwargs.items())
        for mapping in mappings:
            for (key, value) in mapping:
                if key.isupper():
                    self[key] = value
        return True

    def get_namespace(self, namespace, lowercase=True, trim_namespace=True):
        """返回一个字典,这个字典包含该命名空间的items。这个命名空间是以_作为分隔标识的.比如
            app.config["IMAGE_STORE_TYPE"] = "fs"
            app.config["IMAGE_STORE_PATH"] = "/var/app/images"
            app.config["IMAGE_STORE_BASE_URL"] = "http://img.website.com"
            image_store_config = app.config.get_namespace("IMAGE_STORE_")
        结果 `image_store_config` 会是这样::

            {
                "type": "fs",
                "path": "/var/app/images",
                "base_url": "http://img.website.com"
            }
        当需要利用这个进行传参时是比较方便的。
        trim_namespace参数为True时,行为就是上面所述,为False时返回的key会包含namespace前缀。
        """
        rv = {}
        for k, v in iteritems(self):
            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):
        return "<%s %s>" % (self.__class__.__name__, dict.__repr__(self))
        
config模块阅读配置到此结束,下一篇主题待定。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/38364.html

相关文章

  • Flask 插件系列 - Flask-Mail

    摘要:带附件的邮件有时候,我们发邮件的时候需要添加附件,比如文档和图片等,这也很简单,代码如下邮件服务器地址邮件服务器端口启用上面的代码中,我们通过打开了本机的某张图片,然后通过方法将附件内容添加到对象。 前往本文博客 简介 给用户发送邮件是 Web 应用中最常见的任务之一,比如用户注册,找回密码等。Python 内置了一个 smtplib 的模块,可以用来发送邮件,这里我们使用 Flask...

    ingood 评论0 收藏0
  • Flask编写1个百度编辑器的插件

    原文地址: http://52sox.com/write-a-flask-plugin-for-ueditor/ 近期项目开发中,隔壁那个搞python的哥们竟然笑着对我说,希望我能给他写1个百度编辑器的demo,方便他直接调用。 当时真的受宠若惊,这哥们实力不在我之下,只能答应它了。上网搜索下,有1篇文章Flask项目集成富文本编辑器UEditor实现图片上传功能已经有1个现成的例子了。 这...

    xbynet 评论0 收藏0
  • Flask编写1个百度编辑器的插件

    原文地址: http://52sox.com/write-a-flask-plugin-for-ueditor/ 近期项目开发中,隔壁那个搞python的哥们竟然笑着对我说,希望我能给他写1个百度编辑器的demo,方便他直接调用。 当时真的受宠若惊,这哥们实力不在我之下,只能答应它了。上网搜索下,有1篇文章Flask项目集成富文本编辑器UEditor实现图片上传功能已经有1个现成的例子了。 这...

    wqj97 评论0 收藏0
  • flask 源码解析:简介

    摘要:简介官网上对它的定位是一个微开发框架。另外一个必须理解的概念是,简单来说就是一套和框架应用之间的协议。功能比较丰富,支持解析自动防止攻击继承变量过滤器流程逻辑支持代码逻辑集成等等。那么,从下一篇文章,我们就正式开始源码之旅了 文章属于作者原创,原文发布在个人博客。 flask 简介 Flask 官网上对它的定位是一个微 python web 开发框架。 Flask is a micro...

    megatron 评论0 收藏0
  • Flask 源码阅读笔记 开篇

    摘要:官方示例第一行类对象,这个无需解释。请求对象的端点请求视图函数的参数通过源码的注释我们可以知道,都只是对库的进行了一层包装并加入一些属性。接下来会有更多关于和相关文章放出来,敬请期待参考文档项目源码版本注释版 Flask 是一个 Python 实现的 Web 开发微框架, 有丰富的生态资源。本文从一段官方的示例代码通过一步步打断点方式解释 Flask 内部的运行机制,在一些关键概念会...

    mikyou 评论0 收藏0

发表评论

0条评论

DirtyMind

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<