您的位置:首页 > 产品设计 > UI/UE

flask的request_context原理 基于flask v0.1

2017-02-27 22:32 399 查看
一来就上代码,问你怕没有

_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的精髓了,Local,LocalStack和LocalProxy这三个类。

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python flask