from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World" if __name__ == "__main__": app.run()
When the program is running, executing app.run() will first execute the Flask.__call__ method
def __call__(self, environ, start_response): """Shortcut for :attr:`wsgi_app`.""" return self.wsgi_app(environ, start_response)
Call the Flask.wsgi_app method in the call method
def wsgi_app(self, environ, start_response): ctx = self.request_context(environ) # Get the information in environ, encapsulated in request and session ctx.push() # Put it in the __storage__ dictionary in Local, the dictionary format is {"stack":[ctx, ]} Here, the stack is stored in the form of a list==stack. For details, see the article "Flask More Detailed explanation of the "stack" of app applications" error = None try: try: 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) # response finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error) # After the request ends, delete the information in __storage__
deep ctx = request_context(environ)
def request_context(self, environ): return RequestContext(self, environ) # self is the Flask object = app, environ is the request information
So here ctx is the RequestContext object!
Next, let's look at what is encapsulated in RequestContext:
Dig deeper into Request Context(self, environ)
The source code can be seen, the RequestContext encapsulates the reqeust, session, so: request = ctx.request, session = ctx.reqeust
class RequestContext(object): def __init__(self, app, environ, request=None): self.app = app if request is None: request = app.request_class(environ) self.request = request # request字段 self.url_adapter = app.create_url_adapter(self.request) self.flashes = None self.session = None # session字段 self._implicit_app_ctx_stack = [] self.preserved = False self._preserved_exc = None self._after_request_functions = [] self.match_request()
1.1 In-depth ctx.push()
def push(self): # The push method is a method of the RuquestContext class 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() # AppContext object, application 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) # request context, self.session = self.app.open_session(self.request) if self.session is None: self.session = self.app.make_null_session()
def app_context(self): return AppContext(self)
Continue to go deep into AppContext(), which encapsulates some parameters
def __init__(self, app): self.app = app # app object self.url_adapter = app.create_url_adapter(None) self.g = app.app_ctx_globals_class() # g, remember that there is a g field in it, we will use it later self._refcnt = 0
Let's continue our process:
Dig deeper into app_ctx.push()
def push(self): # push is a method of the AppContext() class self._refcnt += 1 if hasattr(sys, 'exc_clear'): sys.exc_clear() _app_ctx_stack.push(self) #We need in-depth methods, self is app_ctx appcontext_pushed.send(self.app
Dig deeper into _app_ctx_stack.push(self)
_app_ctx_stack = LocalStack()
_app_ctx_stack is: the object of LocalStack, here is the global variable, we will use it below, push is the method of LocalStack
Let's take a look at the construction method of LocalStack
def __init__(self): self._local = Local() # This Local is the core technology of Flask context management. For details, please see "Thredading.local Extension of Flask Context Source Code Analysis"
Let's see what LocalStack.push does
def push(self, obj): rv = getattr(self._local, 'stack', None) if rv is None: self._local.stack = rv = [] # Equivalent to rv = [] self._local.stack = rv, when assigning a value to rv, it will assign a value to self._local.stack rv.append(obj) # self._local.stack = [obj,] return rv
self._local.stack is equivalent to executing the __setattr__ method in Local
def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] # __storage__ = {"唯一标识":{"stack": [app_ctx]}} except KeyError: raise AttributeError(name)
Now we start using the above mentioned g
from flask import Flask,request,g app = Flask(__name__) @app.before_request def before(): g.permission_code_list = ['list','add'] # Set parameters @app.route('/',methods=['GET',"POST"]) def index(): print(g.permission_code_list) # Get parameters return "index" if __name__ == '__main__': app.run()
The above is a small example, let's take a look at what this g is and why it can get the value like this
g = LocalProxy(partial(_lookup_app_object, 'g'))
g is an object of the LocalProxy class, partial is a partial function, example
from functools import partial def foo(name): print(name) partial(foo, 'i am pyton') # execute foo and pass parameters in
so: This partial function executes _lokkup_app_object, the parameter is g, we now go deep into _locakup_app_object
def _lookup_app_object(name): top = _app_ctx_stack.top # This _app_ctx_stack is a LocalStack object again, if top is None: raise RuntimeError(_app_ctx_err_msg) return getattr(top, name) # getattr(app_ctx, "g")
deep top = _app_ctx_stack.top
def top(self): try: return self._local.stack[-1] # This is equivalent to executing the __getattr__ method in Local, get __storage__["unique id"]["stack"] = [app_ctx, ],[-1] the last value of the list except (AttributeError, IndexError): return None
def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] #__storage__["唯一id"]["stack"] = [app_ctx, ] except KeyError: raise AttributeError(name)
Through the source code analysis, top is the app_ctx object, app_ctx is the AppContext object, and the parameters encapsulated in it are explained above.
return getattr(top, name) # getattr(app_ctx, "g")
=======================Gorgeous dividing line========================
request context start >>>>>>>
Then 1.1 go deep into ctx.push() to start
deep into _request_ctx_stack.push( self )
_request_ctx_stack is also a LocalStack object, push is a method of the LocalStack class, self is ctx, and ctx encapsulates reqeust, session, etc.
The push method has been explained above. The final operation is to put ctx in the dictionary __staorage__ in Local, for example:
__staorage__ = { "Uniquely identifies":{ ”stask“:[ctx] } }
request, session application example
from flask import g, Flask, request, session app = Flask(__name__) @app.route("/") def hello(): print(request.method) return "Hello Word"
request source code
request, session are the same as g, they are also LocalProxy objects, and the partial function is also to obtain the request and session in the ctx object.
The assignment executes __setattr__ of LocalProxy, and the value executes __getattr__
End of request context >>>>>>>>>>>>>>>>
End of request context flow
ctx.auto_pop(error)
Going deep into auto_pop, auto_pop finally calls its own pop method
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) # exec
def pop(self, exc=_sentinel): app_ctx = self._implicit_app_ctx_stack.pop() 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) if request_close is not None: request_close() clear_request = True finally: rv = _request_ctx_stack.pop() # Clear the application context {"unique id":{"task":[ctx]}}, because it is a dictionary type, so pop # get rid of circular dependencies at the end of the request # so that we don't require the GC to be active. if clear_request: rv.request.environ['werkzeug.request'] = None # Get rid of the app as well if necessary. if app_ctx is not None: app_ctx.pop(exc) # Clear the application context {"unique id":{"task":[app_ctx]}}, because it is a dictionary type, so pop assert rv is self, 'Popped wrong request context. ' \ '(%r instead of %r)' % (rv, self)
_request_ctx_stack is the LocalStack object, the LocalStack method pop
def pop(self): stack = getattr(self._local, 'stack', None) if stack is None: return None elif len(stack) == 1: # There is only one request at a time, so in general there is only one release_local(self._local) # 深入release_local return stack[-1] # return the last one else: return stack.pop() # Clear all, next time stack becomes None
release_local(self._local) finally executes the method in the Local class
def __release_local__(self): self.__storage__.pop(self.__ident_func__(), None) # Clear the information related to the current request
over~