Flask request context management
A partial function
Using this partial embodiment can generate a new function
from functools import partial def mod( n, m ): return n % m mod_by_100 = partial( mod, 100 ) # 100传给n print mod( 100, 7 ) # 2 print mod_by_100( 7 ) # 2
2 thread-safe
import time
from threading import local
class Foo(local):
# 继承local,保证线程安全,也保证了处理速度,threading.local()这个方法的特点用来保存一个全局变量,但是这个全局变量只有在当前线程才能访问,
num = 0
foo = Foo()
def add(i):
foo.num =i
time.sleep(0.5)
print(foo.num)
from threading import Thread
for i in range(20):
task = Thread(target=add,args=(i,))
task.start()
Request context 3
3.1 Flask above request
- When a request comes in, app (), Flask instantiate the object app ** performed __call __ **
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)
Wsgi_app performed to obtain a target ctx RequestContext (the request and package the session)
ctx = self.request_context(environ)
class RequestContext(object): #此时的self是RequestContext对象 -->ctx 中封装了request/session def __init__(self, app, environ, request=None): self.app = app #app = Flask对象 if request is None: #请求的原始信息通过request_class后此时request已经存在,request.methods等 request = app.request_class(environ) self.request = request self.url_adapter = app.create_url_adapter(self.request) self.flashes = None self.session = None
ctx execution ctx.push ():
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)
object push method RequestContext
def push(self): # _request_ctx_stack = LocalStack()一个LocalStack对象 # _request_ctx_stack._local = LocalStack()._loacl = {"__storage__":{},"__ident_func__":get_ident} top = _request_ctx_stack.top #top =None if top is not None and top.preserved: top.pop(top._preserved_exc)
- _ Request_ctx_stack is a LocalStack objects, LocalStack () is a local ._ i.e. Local Local Object ()
class LocalStack(object): def __init__(self): self._local = Local() #self._loacl = {"__storage__":{},"__ident_func__":get_ident}
- _request_ctx_stack the top method returns None (cry, but no tears, this method, wondering for half an hour)
Local object is initialized values obtained in dictionary
class Local(object): #限定键槽,当前只能由两个属性值__storage__,__ident_func__ __slots__ = ('__storage__', '__ident_func__') def __init__(self): object.__setattr__(self, '__storage__', {}) object.__setattr__(self, '__ident_func__', get_ident) # {"__storage__":{},"__ident_func__":get_ident} #此时get_dient 是个没有执行的函数,内存地址
- _request_ctx_stack the top method returns None (not fooled second)
@property def top(self): """The topmost item on the stack. If the stack is empty, `None` is returned. """ try: # self._local 即Local对象调用__getattr__方法 #在下文时候Local对象{"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident} # [ctx->request/session] return self._local.stack[-1] #得到ctx对象 except (AttributeError, IndexError): return None
_request_ctx_stack object to perform push method
_request_ctx_stack.push(self) #当前的self为ctx
def push(self, obj): #此时的self是LocalStack对象, obj为ctx """Pushes a new item to the stack""" # self._local = {"__storage__":{},"__ident_func__":get_ident} #找不到返回值是None rv = getattr(self._local, 'stack', None) if rv is None: #由于.stack后面有等号,执行的时候Local()对象的__setattr__方法 #实际上是地址的赋值,此时stack和rv都指向空列表 self._local.stack = rv = [] #{"__storage__":{8080:{stack:rv=[]}},"__ident_func__":get_ident} rv.append(obj) # {"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident} # 应用上下文时候{"__storage__":{8080:{stack:rv=[app_ctx->(app/g)]}},"__ident_func__":get_ident} return rv #rv=[ctx->request/session]
def __setattr__(self, name, value): #name=stack value=rv=[] #self是Local对象 {"__storage__":{},"__ident_func__":get_ident} ident = self.__ident_func__() #执行get_ident函数获取当前线程id 8080 storage = self.__storage__ #storge ={8080:{stack:rv=[]}} try: storage[ident][name] = value except KeyError: storage[ident] = {name: value} #storage={} # {"__storage__":{8080:{stack:rv=[]}},"__ident_func__":get_ident}
- Executing the method of requesting push ends above:
#当请求进来,第一件事就是要把当前这个请求在我服务器上的线程开辟一个空间(线程对应的空间,必须含有stack对应一个列表存放ctx(request/session) # {"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident}
Hereinafter request 3.3.2Flask
Import request started, in the request
#此时request是一个函数包裹一个偏函数 LocalProxy()是一个代理 #当前的request是一个LocalProxy() request.method 执行__getattr__方法 request = LocalProxy( partial(_lookup_req_object, 'request') #return request对象 )
Partial function in the request passed to the _lookup_req_object: At this time to give a request object
def _lookup_req_object(name): # _request_ctx_stack是LocalStack对象 top = _request_ctx_stack.top #下文[ctx->request/session] if top is None: raise RuntimeError(_request_ctx_err_msg) #此时的name是request,从ctx对象中找出request对象 return getattr(top, name)
...
@property def top(self): """The topmost item on the stack. If the stack is empty, `None` is returned. """ try: # self._local 即Local对象调用__getattr__方法 #在下文时候Local对象{"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident} # [ctx->request/session] return self._local.stack[-1] #得到ctx对象 except (AttributeError, IndexError): return None
- None of the top case is not already present value (0.0)
partial (_lookup_req_object, 'request') to give this layer a reauest executing the object will be passed to the partial function of LocalProxy
@implements_bool class LocalProxy(object): __slots__ = ('__local', '__dict__', '__name__', '__wrapped__') def __init__(self, local, name=None): #local是request偏函数 object.__setattr__(self, '_LocalProxy__local', local) #__local = request偏函数 object.__setattr__(self, '__name__', name) #当前偏函数可以执行而且判断loacl中是否有 __release_local__ ==>这句话成立 if callable(local) and not hasattr(local, '__release_local__'): # "local" is a callable that is not an instance of Local or # LocalManager: mark it as a wrapped function. object.__setattr__(self, '__wrapped__', local) #__warpped__还是local偏函数
The current request is a LocalProxy () method __getattr__ request.method performed in LocalProxy
def __getattr__(self, name): # name是method(举例) if name == '__members__': return dir(self._get_current_object()) #此时self._get_current_object()是经过_local 执行后得到的request对象,从request对象中去取出method return getattr(self._get_current_object(), name)
...
def _get_current_object(self): #self._local是偏函数 if not hasattr(self.__local, '__release_local__'): #执行偏函数,返回request对象 return self.__local() try: return getattr(self.__local, self.__name__) except AttributeError: raise RuntimeError('no object bound to %s' % self.__name__)
3.3.3 Summary
Seen in this light, falsk context management can be divided into three stages:
- Above Request ->
When a request comes in, the first thing is to take the current thread on the server request to open a space (space corresponding to the thread, must contain a list corresponding to the stack storage ctx (request / session), specifically - >: the request, session encapsulated in RequestContext class
app, g AppContext encapsulated in the class, and by the requestcontext LocalStack local and appcontext into classes
in the local class, a thread ID number as the key of the dictionary, - Request following:
by localproxy ---> partial function ---> localstack ---> local values - 'When a request response': -> Data To clear the context management
performed first save.session () then each perform pop (), the data in the local clearance
Detailed look at the source code
3.4 Application Context
Wsgi_app execution method
#ctx为一个RequestContext的对象,参数为environ 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)
Perform push method, _app_ctx_stack also LocalStck object is initialized when the top is None
def push(self): app_ctx = _app_ctx_stack.top #app_ctx = None if app_ctx is None or app_ctx.app != self.app: app_ctx = self.app.app_context() #app_context是AppContext对象 与RequestContenx一样,知识序列化出app和g app_ctx.push() # 应用上文时候{"__storage__":{8080:{stack:rv=[app_ctx->(app/g)]}},"__ident_func__":get_ident} self._implicit_app_ctx_stack.append(app_ctx) else: self._implicit_app_ctx_stack.append(None)
- And then execute app_ctx.push ** _ app_ctx_stack.push **
def push(self): """Binds the app context to the current context.""" self._refcnt += 1 if hasattr(sys, 'exc_clear'): sys.exc_clear() #将AppContext存在LocalStack对象中 _app_ctx_stack.push(self) appcontext_pushed.send(self.app)
def push(self, obj): #此时的self是LocalStack对象, obj为ctx """Pushes a new item to the stack""" # self._local = {"__storage__":{},"__ident_func__":get_ident} #找不到返回值是None rv = getattr(self._local, 'stack', None) if rv is None: #由于.stack后面有等号,执行的时候Local()对象的__setattr__方法 #实际上是地址的赋值,此时stack和rv都指向改空列表 self._local.stack = rv = [] #{"__storage__":{8080:{stack:rv=[]}},"__ident_func__":get_ident} rv.append(obj) # {"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident} # 应用上下文时候{"__storage__":{8080:{stack:rv=[app_ctx->(app/g)]}},"__ident_func__":get_ident} return rv #rv=[ctx->request/session]
This, Push completed, ends the above application, the application used offline scripts below when further: in the global.py
def _find_app():
top = _app_ctx_stack.top #得到app_ctx(app / g)
if top is None:
raise RuntimeError(_app_ctx_err_msg)
return top.app #返回一个app即flask对象 只不过此时的flask对象 是公共的,与初始化的相同
# 但是是独立出来已经被配置好的Flask对象
# LocalStack是 针对当前这个线程对独立的Flask_app进行修改, 不影响现在运行的app =>离线脚本
#但是这个app 在请求结束后会从LocalStack中通过 __delattr__ 删除
# context locals
_request_ctx_stack = LocalStack() #LocalStark = self._loacl = {"__storage__":{},"__ident_func__":get_ident}
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app) # current_app可以点 .run | .route 等