flask-上下文2-梳理顺序
from flask import Flask,request
app = Flask(__name__)
@app.route('/')
def hi():
print(request)
return 'hi'
if __name__ == '__main__':
app.run()
---------------------------------
/////////实例对象能像函数一样传参并被调用,就是__call__()
__call__()无需传参数,则 对象()即会调用该方法。
app.run()---〉 run_simple(t.cast(str, host), port, self, **options)
self即是app对象,当请求到来时,拿到第三个参数加括号,即app() ???
即进入app.__call__
import sys class A(object): def __init__(self, x): print('x in __init__', x) def __new__(cls, y): print('y in __new__', y) return super(A, cls).__new__(cls) def __call__(self, z=1): print('z in __call__', z) def ru(self): print('sdfs') # A('123')('abc') a = A('1') if __name__ == '__main__': a(1) print('aaa') a() print('aaa') a.ru() # a = A('1') # a是对象,A是类 init 和new 是生成对象时调用 # print(type(a),' ... ',type(A)) # print(type(object)) # a('3') # call 是对象生成后,使用对象时调用 print(sys.modules[__name__]) print(sys.modules[__name__].__file__) print(type(sys.modules[__name__].__file__)) --------------------------- /Users/user/data/flask/venv/bin/python /Users/user/data/flask/class22.py y in __new__ 1 x in __init__ 1 z in __call__ 1 aaa z in __call__ 1 aaa sdfs <module '__main__' from '/Users/user/data/flask/class22.py'> /Users/user/data/flask/class22.py <class 'str'>
__name__ == '__main__'
app = Flask(__name__) ---> app = Flask('__main__') 实例化Flask对象,app即是该对象名
进入Flask类
if self.import_name == "__main__": fn = getattr(sys.modules["__main__"], "__file__", None) //--》getattr(x, 'y') is equivalent to x.y. ''' print(sys.modules[__name__]) print(sys.modules[__name__].__file__) --- <module '__main__' from '/Users/user/data/flask/class22.py'> /Users/user/data/flask/class22.py ''' # 所以得到fn = 当前文件名 if fn is None: return "__main__" return os.path.splitext(os.path.basename(fn))[0] return self.import_name
app.run() ---> run_simple # Start a WSGI application.
from werkzeug.serving import run_simple try: run_simple(t.cast(str, host), port, self, **options) finally: self._got_first_request = False
__call__
1.app.__call__ ---》 return self.wsgi_app(environ, start_response)
2.app.wsgi_app ---》
ctx = self.request_context(environ) error: t.Optional[BaseException] = 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)
2.1 ctx = self.request_context(environ) ---》 return RequestContext(self, environ) ---> RequestContext(app, environ)
---> ctx = RequestContext(app,environ)
初始化RequestContext对象
//ctx即是该对象,且其中包含了request对象为其request属性。
class 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. """ def __init__( self, app: "Flask", environ: dict, request: t.Optional["Request"] = None, session: t.Optional["SessionMixin"] = None, ) -> 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
app: "Flask", --对应参数app environ: dict, --对应参数environ
self.app = app # 此时的self是RequestContext对象ctx,并将参数的app对象赋值给 ctx.app
if request is None:
request = app.request_class(environ) ---》 request_class = Request ---》class Request(RequestBase)
--->request = Request(environ)
self.request = request # ctx.request
self.session = session # ctx.session
2.2 ctx.push() --- RequestContext的push方法
def push(self) -> None: """Binds the request context to the current context.""" top = _request_ctx_stack.top if top is not None and top.preserved: top.pop(top._preserved_exc) # Before we push the request context we have to ensure that there # is an application context. 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) _request_ctx_stack.push(self) if self.url_adapter is not None: self.match_request() # Open the session at the moment that the request context is available. # This allows a custom open_session method to use the request context. # Only open a new session if this is the first time the request was # pushed, otherwise stream_with_context loses the session. 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)
_request_ctx_stack.push(self) ---》 _request_ctx_stack = LocalStack()
'''
初始化LocalStack对象
def __init__(self) -> None: self._local = Local()
self._local = Local()
初始化Local对象
'''
_request_ctx_stack.push(self) ---》 LocalStack.push(ctx)
def push(self, obj: t.Any) -> t.List[t.Any]: """Pushes a new item to the stack""" rv = getattr(self._local, "stack", []).copy() rv.append(obj) self._local.stack = rv return rv # type: ignore
(
def push(self, obj: t.Any) -> t.List[t.Any]: # import typing as t
很多人在写完代码一段时间后回过头看代码,很可能忘记了自己写的函数需要传什么参数,返回什么类型的结果。
因此typing作用于提示参数和返回参数类型。
)
rv = getattr(self._local, "stack", []).copy() ---> getattr(x, 'y') is equivalent to x.y || self._local = Local()
---> rv = Local().stack对应的值,如果无stack值则rv = []
rv.append(obj) # obj = ctx ,即添加 ctx至rv
self._local.stack = rv # 赋值 ---> Local().stack=rv ---> 调用Local的__setattr__方法。 (对象.xx,即调用对象的__setattr__方法)
Local对象初始化
def __init__(self) -> None: object.__setattr__(self, "_storage", ContextVar("local_storage"))
Local()._storage = ContextVar("local_storage") ???
ContextVar这个模块提供了一组接口,可用于管理、储存、访问局部Context的状态。
-----
https://blog.csdn.net/weixin_39727934/article/details/111439409
def __setattr__(self, name: str, value: t.Any) -> None: values = self._storage.get({}).copy() values[name] = value self._storage.set(values)
values = self._storage.get({}).copy() ---》
name=stack,value=rv ... 等同于 _storage[ident][stack] = value. ??? https://www.cnblogs.com/zhuleixiao/p/9041066.html
---> 即将value值封装到 Local()._storage[ident][stack] 中 ??? 根据ident值可做不同请求到隔离
(可参考旧版源码,即Local()[ident][stack] = obj, obj=ctx)
2.3 执行路由系统方法 print(request)
request: "Request" = LocalProxy(partial(_lookup_req_object, "request"))
偏函数---》partial(_lookup_req_object, "request") 即执行 _lookup_req_object('request'), name='request'
def _lookup_req_object(name): top = _request_ctx_stack.top if top is None: raise RuntimeError(_request_ctx_err_msg) return getattr(top, name)
top = _request_ctx_stack.top ---> _request_ctx_stack = LocalStack()
即执行 LocalStack().top --->return self._local.stack[-1] || self._local = Local(),即取Local对象中的stack[-1]的索引返回 ---> 即 top = Local().stack[-1] 即top = ctx对象
(---> 执行Local().__getattr__方法,top = Local().stack[-1])
def __getattr__(self, name: str) -> t.Any: values = self._storage.get({}) try: return values[name] except KeyError: raise AttributeError(name)
return getattr(top, name) ---> return top.request || name=request ---> ctx.request (即第一步中封装的request对象)
实例化---》request = LocalProxy(函数) ---〉 函数对应传递给Localproxy对象中的local参数
def __init__( self, local: t.Union["Local", t.Callable[[], t.Any]], name: t.Optional[str] = None, ) -> None: object.__setattr__(self, "_LocalProxy__local", local) object.__setattr__(self, "_LocalProxy__name", name)
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)
print(request)执行对象的__str__方法---Localproxy的__str__
(旧版源码,_get_current_object--即会调用执行local这个函数参数----—???)
》〉》〉》〉》最终request=ctx.request. (即第一步中封装的request对象)
所以视图函数中的request 就是 一个包含了请求参数的Request对象
流程图
2.3 ctx.auto_pop(error)