十 .Flask上下文管理 local 源码解析(一)
一 .上下文管理 local和源码解析
https://www.cnblogs.com/Zzbj/p/10207128.html#autoid-6-0-0
1. 上下文管理概念
上下文即语境、语意,是一句话中的语境,也就是语言环境. 一句莫名其妙的话出现会让人不理解什么意思,
如果有语言环境的说明, 则会更好, 这就是语境对语意的影响. 而对应到程序里往往就是程序中需要共享的信息,
保存着程序运行或交互中需要保持或传递的信息.
Flask中有两种上下文分别为:应用上下文(AppContext)和请求上下文(RequestContext).
按照上面提到的我们很容易就联想到:应用上下文就是保存着应用运行或交互中需要保持或传递的信息,
如当前应用的应用名, 当前应用注册了什么路由, 又有什么视图函数等. 而请求上下文就保存着处理请求过程中需要保持或传递的信息,
如这次请求的url是什么, 参数又是什么, 请求的method又是什么等.
上下文生命周期:
RequestContext: 生命周期在处理一次请求期间, 请求处理完成后生命周期也就结束了.
AppContext: 生命周期最长, 只要当前应用还在运行, 就一直存在. (应用未运行前并不存在)
2. local
""" { 1232:{k:v} } """ try: from greenlet import getcurrent as get_ident except: from threading import get_ident """ class Local(object): def __init__(self): object.__setattr__(self,'storage',{}) def __setattr__(self, key, value): ident = get_ident() if ident not in self.storage: self.storage[ident] = {key:value} else: self.storage[ident][key] = value def __getattr__(self, item): ident = get_ident() if ident in self.storage: return self.storage[ident].get(item) """ class Local(object): __slots__ = ('__storage__', '__ident_func__') def __init__(self): # __storage__ = {1231:{'stack':[]}} 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) 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) obj = Local() obj.stack = [] obj.stack.append('111') obj.stack.append('222') print(obj.stack) print(obj.stack.pop()) print(obj.stack)
根据源码进行自己剖析
import functools try: from greenlet import getcurrent as get_ident except: from threading import get_ident class Local(object): __slots__ = ('__storage__', '__ident_func__') def __init__(self): # __storage__ = {1231:{'stack':[]}} 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) def __setattr__(self, name, value): # name=stack # 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) """ __storage__ = { 12312: {stack:[ctx(session/request) ,]} } """ # obj = Local() # obj.stack = [] # obj.stack.append('11') # obj.stack.append('22') # print(obj.stack) # print(obj.stack.pop()) # print(obj.stack) class LocalStack(object): def __init__(self): self._local = Local() def push(self,value): rv = getattr(self._local, 'stack', None) # self._local.stack =>local.getattr if rv is None: self._local.stack = rv = [] # self._local.stack =>local.setattr rv.append(value) # self._local.stack.append(666) 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: return stack[-1] else: return stack.pop() def top(self): try: return self._local.stack[-1] except (AttributeError, IndexError): return None class RequestContext(object): def __init__(self): self.request = "xx" self.session = 'oo' _request_ctx_stack = LocalStack() _request_ctx_stack.push(RequestContext()) def _lookup_req_object(arg): ctx = _request_ctx_stack.top() return getattr(ctx,arg) # ctx.request / ctx.session request = functools.partial(_lookup_req_object,'request') session = functools.partial(_lookup_req_object,'session') print(request()) print(session())
3. 上下文 源码解析(按照步骤走)
之前分析了flask项目从启动到请求结束实际是执行了wsgi_app函数,在“请求流程”中也分析了self.full_dispatch_request()
是执行了请求的视图函数以及请求扩展。
现在分析其它源码:
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: # noqa: B001 error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error)
1. ctx=self.request_context(environ)
def request_context(self, environ): return RequestContext(self, environ) def __init__(self, app, environ, request=None, session=None): self.app = app if request is None: request = app.request_class(environ) self.request = request self.url_adapter = None try: self.url_adapter = app.create_url_adapter(self.request) except HTTPException as e: self.request.routing_exception = e self.flashes = None self.session = session self._implicit_app_ctx_stack = [] self.preserved = False self._preserved_exc = None self._after_request_functions = []
ctx 就是RequestContext的一个对象,封装了一些参数,包括处理后的request、session...
2. ctx.push()
ctx是RequestContext对象,实际是执行了RequestContext的push方法,在push方法里主要看执行了_request_ctx_stack.push(self)
方法
def push(self): top = _request_ctx_stack.top if top is not None and top.preserved: top.pop(top._preserved_exc) app_ctx = _app_ctx_stack.top 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) if hasattr(sys, "exc_clear"): sys.exc_clear() _request_ctx_stack.push(self) if self.session is None: session_interface = self.app.session_interface self.session = session_interface.open_session(self.app, self.request) if self.session is None: self.session = session_interface.make_null_session(self.app) if self.url_adapter is not None: self.match_request()
2.1 _request_ctx_stack是什么?
_request_ctx_stack
是LocalSrack()的对象,也就是说_request_ctx_stack.push(self)
执行的是LocalSrack的push方法,self就是ctx(请求相关的)
def push(self, obj): # 从self._local获取"stack"值,若为None,就设为空列表[];否则将obj即ctx放入“stack”的列表中去 rv = getattr(self._local, "stack", None) if rv is None: self._local.stack = rv = [] rv.append(obj) return rv
self._local
就是local对象。local中没有stack属性,所以执行local的__getattr__()
方法。这就是flask上篇介绍到的local对象。
通过线程id或协程id来开辟不同的存储空间,放入request、session等相关的信息。这样就可以实现多线程或多协程
3.response = self.full_dispatch_request()
请看“请求流程“篇,这里不再重复介绍
4.ctx.auto_pop(error)
def auto_pop(self, exc): if self.request.environ.get("flask._preserve_context") or ( exc is not None and self.app.preserve_context_on_exception ): self.preserved = True self._preserved_exc = exc else: self.pop(exc) # self是ctx,所以执行的是RequestContext的pop方法 def pop(self, exc=_sentinel): app_ctx = self._implicit_app_ctx_stack.pop() # self._implicit_app_ctx_stack = [] try: clear_request = False if not self._implicit_app_ctx_stack: self.preserved = False self._preserved_exc = None if exc is _sentinel: exc = sys.exc_info()[1] self.app.do_teardown_request(exc) if hasattr(sys, "exc_clear"): sys.exc_clear() request_close = getattr(self.request, "close", None) # 如果有request.close就执行request_close()方法 if request_close is not None: request_close() clear_request = True finally: rv = _request_ctx_stack.pop() # 主要是执行了这句 if clear_request: rv.request.environ["werkzeug.request"] = None if app_ctx is not None: app_ctx.pop(exc) assert rv is self, "Popped wrong request context. (%r instead of %r)" % (rv,self,)
4.1 rv =_request_ctx_stack.pop()
刚刚提到过_request_ctx_stack是LocalStack对象,所以执行的是LocalStack的pop方法
def pop(self): # self._local是Local对象,获取stack属性值,如果有就return stack[-1] 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()
总结:在该请求执行完之后pop掉ctx请求