Inside Flask - 配置的实现

Inside Flask - 配置的实现

flask 的配置对象 app.config 本身使用很简单,无非就是以字典的形式使用,而它的实现,本身就是以字典的形式的。

flask/config.py 文件中,包含了 flask 配置的实现代码,就两个类 ConfigAttribute 和 Config 。

ConfigAttribute 表示一个配置属性,它的代码只有简单几行,但由于使用了 descriptor 模式,对 python 不熟悉的人不容易理解。先看看 descriptor 是个什么东西。

descriptor 属于 python 元编程方面的概念(即是对编程的编程,%>_<%),本质上是提供一种编程的协议,也就是制定某种框架供业务编程时使用(像 C# / java 的反射也属于元编程范畴)。由于动态语言的特性,元编程在 python 里面实现起来非常简单。作为 descriptor 的对象,它提供的描述属性的协议,它需要实现 __get__ __set____delete__,来描述某个某个对象(被描述对象)应该如何获取数据、设置数据和删除对象。

现在再来看看 ConfigAttribute 类 ::

class ConfigAttribute(object):
"""Makes an attribute forward to the config"""

def __init__(self, name, get_converter=None):
    self.__name__ = name
    self.get_converter = get_converter

def __get__(self, obj, type=None):
    if obj is None:
        return self
    rv = obj.config[self.__name__]
    if self.get_converter is not None:
        rv = self.get_converter(rv)
    return rv

def __set__(self, obj, value):
    obj.config[self.__name__] = value

这里的约定是定义 ConfigAttribute 的对象里,应该包含一个 config 字典,然后取值或设置值时,改为取或设置 config 中对应的 key 的值(在 flask 的 Flask 类里面,包含 config,类型就是接下来要看的 Config 类)。

Config 类继承 dict ,就是一个保存了 ConfigAttribute 的字典 ::

class Config(dict):
    ...

同时,Config 类提供几个加载配置的方法,分别是 ::

from_envvar(...)    # 从环境变量读取所要加载的 py 文件路径,然后用 from_pyfile 加载
from_pyfile(...)    # 从 py 文件中加载文件,然后用 from_object 加载
from_object(...)    # 从对象加载,把 dir(obj) 中,名字是大写的数据载入到 self
from_json(...)      # 从 json 文件加载,然后调用 from_mapping 加载
from_mapping(...)   # 从多个字典,或 **kwargs 形式的参数,且名字是大写的数据加载到 self

在加载文件时,可以使用该 flask 应用内的相对路径(见下文),或者是使用绝对路径,代码在处理时,都用 ::

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

当文件为相对路径时,就会用当前 flask app 的路径结合相对路径来定位文件。

Config 中的配置有个 namespace 的概念,通过下划线对参数进行分类,方便一次获取多个配置项 ::

def get_namespace(self, namespace, lowercase=True, trim_namespace=True):
    """Returns a dictionary containing a subset of configuration options
    that match the specified namespace/prefix. Example usage::

        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_')

    The resulting dictionary `image_store_config` would look like::

        {
            'type': 'fs',
            'path': '/var/app/images',
            'base_url': 'http://img.website.com'
        }

从源代码层面看,这里有几点是需要注意的:(1)最后起作用的 from_objectfrom_mapping,其它几个是做预处理;(2)配置的 key 应该是大写的,然后以下划线做分隔

OK,现在看看 flask 里面怎样使用配置。

首先在 flask/app.py 里面,Flask 类代码中包含了一个 config_class ,它默认就是 Config 类。然后 config 属性通过 make_config 生成 ::

self.config = self.make_config(instance_relative_config)

make_config 的代码如下 ::

def make_config(self, instance_relative=False):
    """Used to create the config attribute by the Flask constructor.
    The `instance_relative` parameter is passed in from the constructor
    of Flask (there named `instance_relative_config`) and indicates if
    the config should be relative to the instance path or the root path
    of the application.

    .. versionadded:: 0.8
    """
    root_path = self.root_path
    if instance_relative:
        root_path = self.instance_path
    return self.config_class(root_path, self.default_config)

这里有个概念是 instance_relative 的配置,即与实例相关的配置。如果在一个应用里面用到了几个 flask app ,并且这些 app 的配置不一样时,就要用到实例相关的配置,将当前的 root_path 设置为初始化 flask app 时的参数 instance_path(可自由设置)。

self.default_config 是一个默认配置的字典,如下 ::

default_config = ImmutableDict({
    'DEBUG':                                get_debug_flag(default=False),
    'TESTING':                              False,
    'PROPAGATE_EXCEPTIONS':                 None,
    'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
    'SECRET_KEY':                           None,
    'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
    'USE_X_SENDFILE':                       False,
    ...
})

而使用到 ConfigAttribute 的几个代码如下 ::

debug = ConfigAttribute('DEBUG')
...
testing = ConfigAttribute('TESTING')
...

flask 设置完配置项后,用户可根据自己的需要,用操作字典的方式修改、更新配置 ::

app.config.update({'DEBUG': False})
app.config.from_object(config_obj)
app.config.setdefault('DEBUG', False)
...
posted @ 2016-09-11 14:22  drop *  阅读(436)  评论(0编辑  收藏  举报