flask的request_context原理 基于flask v0.1

一来就上代码,问你怕没有

_request_ctx_stack = LocalStack()
current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
request = LocalProxy(lambda: _request_ctx_stack.top.request)
session = LocalProxy(lambda: _request_ctx_stack.top.session)
g = LocalProxy(lambda: _request_ctx_stack.top.g)
class _RequestContext(object):
    def __init__(self, app, environ):
        self.app = app
        self.url_adapter = app.url_map.bind_to_environ(environ)
        self.request = app.request_class(environ)
        self.session = app.open_session(self.request)
        self.g = _RequestGlobals()
        self.flashes = None

    def __enter__(self):
        _request_ctx_stack.push(self)

    def __exit__(self, exc_type, exc_value, tb):
        if tb is None or not self.app.debug:
            _request_ctx_stack.pop()

这两段代码显示本文的的重点LocalStack_RequestContext

    先介绍一下_RequestContext,官方文档是这么说的:

"""
The request context contains all request relevant information.  It is
    created at the beginning of the request and pushed to the
    `_request_ctx_stack` and removed at the end of it.  It will create the
    URL adapter and request object for the WSGI environment provided.
 """
    简单地说,就是_RequestContext是保留一个request过程中所有的相关信息,如 url adapter和WSGI environment等。app会在处理一个request前将request的相关信息压到_request_ctx_stack栈顶,等到处理完这个request的时候将它弹出。所以回过头来看代码

current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
request = LocalProxy(lambda: _request_ctx_stack.top.request)
当我们访问flask.request的时候,其实是访问_request_ctx_stack.top这个对象里面的request属性。_request_ctx_stack.top自然就是栈顶了,那么栈顶是什么样一个对象呢?对了,就是刚才介绍的_RequestContext对象了,它在dispatch_request的之前就压入。这个_RequestContext对象里面有request这个属性。所以flask.request其实就是_RequestContext.request。

    接下来,我们就要介绍flask的精髓了,LocalLocalStackLocalProxy这三个类。

class Local(object):
    __slots__ = ('__storage__', '__ident_func__')

    def __init__(self):
        object.__setattr__(self, '__storage__', {})
        object.__setattr__(self, '__ident_func__', get_ident)

    def __getattr__(self, name):
        try:
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)
    这个Local类的核心是__ident_func__()函数,这个函数保证了即使在多线程环境下,保存在Local里面的变量不会混乱,而且支持greenlet这个异步协程库。

    那么LocalStack是用来干嘛的呢。它的作用是将上面的Local().key = value 变成LocalStack().push(value) 通过list来模拟了堆栈的操作。也是基于Local这个类的。但是为什么需要使用栈呢?

class LocalStack(object):
    def __init__(self):
        self._local = Local()

    def push(self, obj):
        """Pushes a new item to the stack"""
        rv = getattr(self._local, 'stack', None)
        if rv is None:
            self._local.stack = rv = []
        rv.append(obj)
        return rv

    那么我们继续看看LocalProxy类。代理模式就不多说了,一种设计模式。我们看看代码:

_request_ctx_stack = LocalStack()
_request_ctx_stack.push(_RequestContext())

    由上面可以知道我们需要访问的request属性是在_requestContext()里面那么访问request的时候只能_request_ctx_stack.top.request了。这样子写法没有问题,就是缺少了点优雅。那么如果直接request=_request_ctx_stack.top.request呢。这肯定是不行的,因为在多线程的环境下,request是需要根据当前环境来获得对应的值,这样写相当于写死了request的值了。那么LocalProxy是怎么做的呢?当对LocalProxy属性进行访问的时候,它都会执行一次_get_current_object函数,该函数就是执行我们传入的函数得到访问对象,比如上面的lambda函数就是得到_request_ctx_context.top.request对象。最后再对得到的对象进行属性访问。这样子就可以很优雅地实现了动态获取当前线程的request对象了。高,实在是高!

class LocalProxy(object):    
    def _get_current_object(self):
        if not hasattr(self.__local, '__release_local__'):
            return self.__local()
        try:
            return getattr(self.__local, self.__name__)
        except AttributeError:
            raise RuntimeError('no object bound to %s' % self.__name__)

   到这里,其实是很清晰这整个过程了。为什么其实在多线程环境下,request对象是根据当前环境取得的值。


参考博文:

flask request g session的实现原理

werkzeug源码分析(local.py

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值