Python 21 Flask(二)上下文管理详解
上下文管理
对于上下文管理我没有找到明确的定义,但是经过源码流程的学习后,我觉得所谓的上下文管理应该就是Flask对请求和应用相关数据的一种处理方式,它不是像Django一样通过参数的传导,而是创建了全局变量直接引用,Flask为每个线程开辟一块独立的内存空间,将请求和应用相关的信息放到这个空间中,全局变量直接从这个空间中取出数据。
一、上下文管理流程梳理:
二、上下文管理图示:
三、源码分析
try: from greenlet import getcurrent as get_ident except ImportError: try: from thread import get_ident except ImportError: from _thread import get_ident class Local(object): __slots__ = ("__storage__", "__ident_func__") #表示只能有这两个变量 def __init__(self): object.__setattr__(self, "__storage__", {}) object.__setattr__(self, "__ident_func__", get_ident) #这里调用父类的方法 def __iter__(self): return iter(self.__storage__.items()) def __call__(self, proxy): """Create a proxy for a name.""" return LocalProxy(self, proxy) def __release_local__(self): self.__storage__.pop(self.__ident_func__(), None) def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): ident = self.__ident_func__() storage = self.__storage__ try: storage[ident][name] = value except KeyError: storage[ident] = {name: value} def __delattr__(self, name): try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name)
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 def pop(self): """Removes the topmost item from the stack, will return the old value or `None` if the stack was already empty. """ stack = getattr(self._local, "stack", None) if stack is None: return None elif len(stack) == 1: release_local(self._local) return stack[-1] else: return stack.pop() @property def top(self): """The topmost item on the stack. If the stack is empty, `None` is returned. """ try: return self._local.stack[-1] except (AttributeError, IndexError): return None
这个对象是帮助我们去Local中取ctx或app_ctx对象的。
当我们调用request.method的时候,会调用LocalProxy对象的__getattr__方法,这个方法会用LocalStack取出ctx,然后用ctx.request.method取到数据返回。
其他的同理
def __call__(self, environ, start_response): """The WSGI server calls the Flask application object as the WSGI application. This calls :meth:`wsgi_app` which can be wrapped to applying middleware.""" return self.wsgi_app(environ, start_response)
def wsgi_app(self, environ, start_response): ctx = self.request_context(environ) error = None try: try: ctx.push() response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error)
ReuqestContext函数中封装了request数据和session
request通过传入environ原始数据实例化Request对象,处理这些原始数据,将处理好的数据返回给RequestContext的request
session一开始是空的,会去cookie中解密并反序列化
#实际上就是RequestContext(ctx).push() #首先是实例化一个app_ctx对象,并将这个对象放入Local中 if app_ctx is None or app_ctx.app != self.app: app_ctx = self.app.app_context() app_ctx.push() self._implicit_app_ctx_stack.append(app_ctx) else: self._implicit_app_ctx_stack.append(None) #然后将ctx放入Local中 _request_ctx_stack.push(self)