资讯专栏INFORMATION COLUMN

深入 Flask 源码理解 Context

ninefive / 3010人阅读

摘要:主要的作用是将维护的字典中键为对应的值定义为。如果没有,则会将当前到中,同时将加入列表中否则添加。注意清理之后的动作。上述代码涉及到,它强调必须是一个可调用对象。后期的工作之一是了解。这仅仅是我的个人理解。实际上这是解决多个实例运行的问题。

Flask 中的上下文对象

知乎问题 编程中什么是「Context(上下文)」 已经能够简单地说明什么是 Context,它是一个程序需要的外部对象,类似于一个全局变量。而这个变量的值会根据提供的值而改变。

Flask 中有分为请求上下文和应用上下文:

对象 Context类型 说明
current_app AppContext 当前的应用对象
g AppContext 处理请求时用作临时存储的对象
request RequestContext 请求对象,封装了Http请求的内容
session RequestContext 用于存储请求之间需要记住的值

Flask 分发请求之前激活程序请求上下文,请求处理完成后再将其删除。

Flask 中的 Context 是通过栈来实现。


Flask 的 Context 实现

Flask 的核心功能依赖于 Werkzeug 库。

_app_ctx_stack & _request_ctx_stack

这两种栈定义在 flask/global.py 中。

_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()

首先需要了解一下 Werkzeug 中关于 LcoalStack 的相关内容。

Local

Local 是定义了一个 __storage__ 字典,其中的键为 threadid 值。

class Local(object):
    __slots__ = ("__storage__", "__ident_func__")
    
    def __init__(self):
        object.__setattr__(self, "__storage__", {})
        object.__setattr__(self, "__ident_func__", get_ident)
        
    def __setattr__(self, name, value):
        ident  = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except KeyError:
            raise AttributeError(name)
    ...

LocalStack

LocalStack 则内部维护一个 Local 实例。主要的作用是将 Local 维护的 __storage__ 字典中键为 __ident_func__() 对应的值定义为 {"stack" : [] }

class LocalStack(object):
    def __init__(self):
        self._local = Local()
        
    def push(self, obj):
        rv = getattr(self._local, "stack", None)
        if rv is None:
            self._local.stack = rv = []
        rv.append(obj)
        return rv
        
    def pop(self, obj):
        pass

LocalProxy

LocalProxy类是一个代理类,应用到设计模式当中的代理模式。简单地讲,我们不需要去了解当前的环境,而直接去操作这个 Proxy 类,这个 Proxy 类会将所有的操作反馈给正确的对象。

class LocalProxy(object):
    __slots__ = ("__local", "__dict__", "__name__")
    def __init__(self, local, name=None):
        object.__setattr__(self, "_LocalProxy__local", local)
        object.__setattr__(self, "__name__", name)
    
    def _get_current_object(self):
        # 通过此方法获取被代理的对象
        if not hasattr(self.__local, "__release_local__")
            return self.__local
        try:
            return gerattr(self.__local,self.__name__)
        except Attribute:
            raise RuntimeError("no object bound to %s" % self.__name__)
    ...
    # 其他操作
request & RequestContext

Flask 源码中关于 request 的定义:

def _lookup_req_object(name):
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
    return getattr(top, name)
    
request = LocalProxy(partial(_lookup_req_object, "request"))

从源码可以看出,request_request_ctx_stack 栈顶元素的一个属性。实际上 _request_ctx_stack 栈中的元素是 ReuqestContext 对象的实例, 而 ReuqestContext 中包含了 request 请求的所有信息,包括 Session 信息。

class ReuqestContext(object):
    def __init__(self, app, environ, request=None):
        if reuqest is None:
            request  = Request(environ)
        self.requst = request
        self.app = app 
        self.session = None
        ...
        # 这个列表包含了与 request 相关联的 Application
        self._implicit_app_ctx_stack = []
        self.match_request()

    def push(self, object):
        """
        这里需要实现的是:当 RequestContext push 到
        _request_ctx_stack 时, 需要检测是否有对应的
        AppContext。如果没有,则会将当前 self.app push
        到 AppContext 中,同时将self.app 加入
        _implicit_app_ctx_stack 列表中; 否则
        _implicit_app_ctx_stack 添加 None。
        """
        pass
        
    def pop(self):
        """
        当 ReuqestContext 弹出 _request_ctx_stack 的
        方法。注意:request 清理之后的动作。如执行
        teardown_request。
        """
        pass

这里传入的 app,就是 Flask 的程序实例。
RequestContext 实例的创建在 Flask 类方法中。

class Flask(_PackageBoundObject):
    ...
    request_class = ReuqestContext
    def wsgi_app(self, environ, start_response):
        ctx = self.request_class(environ)
        ctx.push
        ...
        
    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)

Flask 中 Request 对象继承了 Werkzeug 中的 Request 对象。
上述代码涉及到 WSGI,它强调 Appication 必须是一个可调用对象。
后期的工作之一是了解 WSGI

Session

在 session.py 文件中定义了 有关Session的内容。Flask 中 Session 是构建在 Cookie 上面的。其中定义了关于 Session 的接口。

class SessionMixin(object):
    """定义了Session的最小属性"""
    
class SecureCookieSession(CallDict, SessionMixin):
    """ CallDict 是 werkzeug 中的数据结构 """

class NullSession(SecureCookieSession):
    """ 定义了空 session 结构 """
    
class SessionInterface(object):
    """ 定义了 Session接口的属性,依赖于 app.config 
    中的信息。同时,规定了只要是继承SessionInterface
    必须实现 open_session 和 save_session 方法
    """
class SecureCookieSessionInterface(SessionInterface):
    """ 
    主要是实现了 open_session 和 save_session 方法
    """

如下代码则是 session 的应用。

# flask/app.py
class Flask(_PackageBoundObject):
    session_interface = SecureCookieSessionInterface()
    def open_session(self, request):
        return self.session_interface.open_session(self, request)
        
    def save_session(self, session, response)
        return self.session_interface.save_session(
            self, session, response)
            
    def process_response(self, response):
        ctx = _request_ctx_stack.top
        ...
        if not self.session_interface.is_null_session(ctx.session):
            self.save_session(ctx.session, response)

#ReuqestContext
class ReuqestContext():
    def push(self, object):
        ...
        self.session = self.app.open_session(self.reuqest)
        if self.session is None:
            self.session = self.app.make_null_session()
        ...

sessionRequestContext 中属性,所以代理说明如下:

session = LocalProxy(partial(_lookup_req_object,"session")
current_app & g

一般来讲, 在 Flask Web 开发时, Flask的实例是延迟创建的。也就是说 AppContext还没有压入 _app_ctx_stack 中,所以我们在编写代码时,是无法获取完整的 Flask 实例的属性。而当用户访问时,程序的实例已经初始化完成了,因此我们采用 current_app代理获取当前 app。这仅仅是我的个人理解。实际上这是解决 多个 Flask 实例运行的问题。

current_app是获取 _app_ctx_stack 栈顶 AppContext实例元素的代理.

def _find_app():
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError(_app_ctx_err_msg)
    return top.app
current_app = LocalProxy(_find_app)

flask.g 是存储一下资源信息的,如数据库连接信息。更多应用的则是体现在 Flask 扩展当中。

def _lookup_app_object(name):
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError(_app_ctx_err_msg)
        return getattr(top,name)
g = LocalProxy(partical(_lookup_app_object, "g"))

# flask.app.py
class Flask(_PackageBoundObject):
    app_ctx_globals_class = _AppCtxGlobals #实现的是类似字典的功能

# AppContext
class AppContext(object):
    def __init__(self, app):
        self.g = self.app.app_ctx_globals_class()

#RequestContext
class RequestContext(object):
    #定义与request相关的 g 变量
    def _get_g(self):
        return _app_ctx_stack.top.g
    def _set_g(self, value):
        _app_ctx_stack.top.g = value
    g = property(_get_g, _set_g)
    del _get_g, _set_g   

上述代码存在一个疑问是 g 对象是基于请求的,每次请求都会重置。那么 g 为什么不是 RequestContext 而是 AppContext ?
flask.g API 文档 中说明了 g 变量的改动。


个人博客

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

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

相关文章

  • 深入理解flask框架(2):应用上下文与请求上下文

    摘要:实现一个进程中拥有多个应用上下文机制依赖的数据结构上下文机制的实现基于的。 什么是上下文? flask框架中的上下文本质上就是两个类,我们可以先看一下他的初始化函数:应用上下文 class AppContext(object): The application context binds an application object implicitly to the c...

    wushuiyong 评论0 收藏0
  • 深入理解flask框架(4):session

    摘要:中的实现是基于。打开源码的文件,我们可以看到最后的接口类中,主要有两个函数。这个系列就此完结。 flask中session的实现是基于cookie。打开flask源码的session.py文件,我们可以看到最后的接口类中,主要有open_session,save_session两个函数。 class SecureCookieSessionInterface(SessionInterfa...

    PingCAP 评论0 收藏0
  • flask 源码解析:应用启动流程

    摘要:中有一个非常重要的概念每个应用都是一个可调用的对象。它规定了的接口,会调用,并传给它两个参数包含了请求的所有信息,是处理完之后需要调用的函数,参数是状态码响应头部还有错误信息。一般来说,嵌套的最后一层是业务应用,中间就是。 文章属于作者原创,原文发布在个人博客。 WSGI 所有的 python web 框架都要遵循 WSGI 协议,如果对 WSGI 不清楚,可以查看我之前的介绍文章。 ...

    whatsns 评论0 收藏0
  • flask 源码解析:上下文

    摘要:但是这些对象和全局变量不同的是它们必须是动态的,因为在多线程或者多协程的情况下,每个线程或者协程获取的都是自己独特的对象,不会互相干扰。中有两种上下文和。就是实现了类似的效果多线程或者多协程情况下全局变量的隔离效果。 这是 flask 源码解析系列文章的其中一篇,本系列所有文章列表: flask 源码解析:简介 flask 源码解析:应用启动流程 flask 源码解析:路由 flas...

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

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

    megatron 评论0 收藏0

发表评论

0条评论

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