请求上下文总结分析
from flask import Flask
app = Flask(__name__)
if __name__ == '__main__':
app.__call__
app.run()
'''
整个flask的源码:
def wsgi_app(self, environ, start_response):
# ctx就是 RequestContext的一个对象 里面包含了请求相关的所有东西
ctx = self.request_context(environ)
error = None
try:
try:
# 把ctx放到了Local对象里
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)---》ctx本质是RequestContext的对象
1.1 RequestContext(self, environ):self是当前的app,environ是请求相关的
1.2 RequestContext(self, environ)执行的结果是RequestContext类的对象,该对象中包含了请求相关和当前app
1.3 所以这个ctx就是RequestContext的对象
2 ctx.push():当前ctx是RequestContext的对象,那就是执行RequestContext对象的push方法
2.1 ctx.push方法中有一个_request_ctx_stack.push(self);这个self是ctx,那就是把ctx传递个_request_ctx_stack
2.2 _request_ctx_stack就是LocalStack的对象
2.3 _request_ctx_stack.push
源码如下:
#obj就是_request_ctx_stack.push(self)传过来的self,也就是ctx
def push(self, obj):
rv = getattr(self._local, "stack", None)
if rv is None:
#_local=local -->setattr
# storage[线程/协程id][stack] = []
self._local.stack = rv = []
# storage[线程/协程id][stack] = [obj,]
rv.append(obj)
return rv
2.3.1 在2.3的代码中self._local.stack做的事情
发现self._local是Local对象,所以self._local就执行了Local对象的__setattr__:
代码如下:
def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value}
总结:
就是将ctx放到Local对象以storage[ident][stack] = [ctx,]的形式存储
3 response = self.full_dispatch_request() 这里面是所有请求扩展以及真正的响应函数
源码如下:
def full_dispatch_request(self):
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
rv = self.preprocess_request()
#如果请求扩展中的,请求之前的所有函数,如果没有返回值,就会执行下面的
#下面的是:请求路由中的函数
if rv is None:
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
return self.finalize_request(rv)
3.1 self.try_trigger_before_first_request_functions()
这是在请求扩展中的before_first_request
他是如何判断项目启动后只执行一次里面的方法:
通过self._got_first_request变量来判断的,初始值为False,一旦执行过了该函数,在函数的末尾将self._got_first_request设置成True
源码如下:
def try_trigger_before_first_request_functions(self):
if self._got_first_request:
return
with self._before_request_lock:
if self._got_first_request:
return
#这里去循环第一次执行的函数,请求扩展的里面
for func in self.before_first_request_funcs:
func()
self._got_first_request = True
3.2 rv = self.preprocess_request() 这里执行的是befor_request的函数
源码如下:
def preprocess_request(self):
bp = _request_ctx_stack.top.request.blueprint
funcs = self.url_value_preprocessors.get(None, ())
if bp is not None and bp in self.url_value_preprocessors:
funcs = chain(funcs, self.url_value_preprocessors[bp])
for func in funcs:
func(request.endpoint, request.view_args)
# 请求之前要做的事情,请求之前的请求扩展
funcs = self.before_request_funcs.get(None, ())
if bp is not None and bp in self.before_request_funcs:
funcs = chain(funcs, self.before_request_funcs[bp])
for func in funcs:
rv = func()
if rv is not None:
return rv
3.2.1 funcs = self.before_request_funcs.get(None, ())这里是获取所有注册进来的befor_request
3.2.2 下面的代码可以看出:如果befor_request函数有一个返回值,那么后面的函数都不执行,并且把返回值给rv = self.preprocess_request()里的rv
for func in funcs:
rv = func()
if rv is not None:
return rv
3.3 if rv is None: #这个rv是3.2中before_request返回的,如果没有返回值,才会执行rv = self.dispatch_request(),有返回值就不会执行
rv = self.dispatch_request()#这个是真正的响应函数
通过这个代码:知道如果before_request有返回值,就不会执行真正的响应函数
3.4 return self.finalize_request(rv):这个rv是3.2或3.3的返回值
源码如下:
def finalize_request(self, rv, from_error_handler=False):
response = self.make_response(rv)
try:
# 这里执行的是执行完请求视图后,after_request的请求
response = self.process_response(response)
request_finished.send(self, response=response)
except Exception:
if not from_error_handler:
raise
self.logger.exception(
"Request finalizing failed with an error while handling an error"
)
return response
3.4.1 response = self.process_response(response):这里做after_request请求扩展的
源码如下:
def process_response(self, response):
ctx = _request_ctx_stack.top
bp = ctx.request.blueprint
funcs = ctx._after_request_functions
if bp is not None and bp in self.after_request_funcs:
funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
if None in self.after_request_funcs:
funcs = chain(funcs, reversed(self.after_request_funcs[None]))
for handler in funcs:
response = handler(response)
if not self.session_interface.is_null_session(ctx.session):
self.session_interface.save_session(self, ctx.session, response)
return response
在上述代码有两行代码
if None in self.after_request_funcs:
#funcs是所有after_request的函数列表,并用reversed做了反转
#这里就知道在after_request里为什么先注册的后执行
funcs = chain(funcs, reversed(self.after_request_funcs[None]))
for handler in funcs:
#在这里知道after_request的函数必须接受这response,并且要做返回
response = handler(response)
4 我们知道3中就可以调用request属性,那么是怎么做到的呢
当我们在3中的任意位置,都能调用request或者其他的。比如我们调用request.methons,他是如何找到当前请求的request的呢
4.1 当我们调用request.methons的时候,我们先要看看request是什么
源码如下:
request = LocalProxy(partial(_lookup_req_object, "request"))#也就是LocalProxy对象
4.2 当我们调用request.method就会返回属性methons属性给我吗,但是我们在LocalProxy根本没有methons属性,那么我们想到,既然没有这个属性,一定会走__getattr__
源码如下:
def __getattr__(self, name):
#name就是methons
if name == "__members__":
return dir(self._get_current_object())
#_get_current_object中来取name也就是methods
#_get_current_object()执行的结果是partial(_lookup_req_object, "request")()==》就是从ctx中取到request
#下面的意思就是getattr(request,methons)
return getattr(self._get_current_object(), name)
上述代码的name,及时我们要找的methons,那他是从self._get_current_object()通过反射来拿的
从下面的分析中 我们知道self._get_current_object()就是request
4.2.1 self._get_current_object()的返回值是什么:
通过下面的源码我们可以看到就是self.__local()的执行结果
源码如下:
def _get_current_object(self):
if not hasattr(self.__local, "__release_local__"):
return self.__local()
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError("no object bound to %s" % self.__name__)
4.2.1.1 self.__local()的执行结果是什么?首先要搞清楚self.__local是什么
我们发现self.__local是通过如下初始化得到的
def __init__(self, local, name=None):
object.__setattr__(self, "_LocalProxy__local", local)
那我们可以知道self.__local就是4.1中LocalProxy(partial(_lookup_req_object, "request"))的参数
也就是partial(_lookup_req_object, "request")偏函数
4.2.1.2 我们现在知道self.__local是partial(_lookup_req_object, "request")
那self.__local()就是partial(_lookup_req_object, "request")()执行
4.2.1.3 partial(_lookup_req_object, "request")相当于给_lookup_req_object函数传递了一个“request”参数
_lookup_req_object,源码如下:
#name就是request
def _lookup_req_object(name):
#Local中取得ctx
top = _request_ctx_stack.top
#top就是ctx
if top is None:
raise RuntimeError(_request_ctx_err_msg)
#ctx中去request 在吧request返回回去
return getattr(top, name)
我们从4.2.1.3.1得知 top就是ctx,getattr(top, name)就是从ctx找request,并且返回
4.2.1.2中self._local执行的结果就是request
4.2.1.3.1
上述代码中_request_ctx_stack.top的源码如下:
@property
def top(self):
try:
#返回了一开始存进去的ctx对象
return self._local.stack[-1]
except (AttributeError, IndexError):
return None
self._local也是Local对象 .stack就是在执行__getattr__
代码如下:
def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
这样_request_ctx_stack.top得到的结果就是ctx,4.2.1.3中的top = _request_ctx_stack.top;top就是ctx
'''
g对象
from flask import Flask,g
'''
专门用来给开发者存储信息的,他只在同一个请求内有效
'''
app=Flask(__name__)
@app.before_request
def b_r():
g.name="egon is sb"#存
g.name="egon is dsb"
@app.after_request
def a_r(response):
print(g.name)
print(g.age)
return response
@app.route("/")
def index():
g.age="egon"
print(g.name)#取
return "ok"
@app.route("/login")
def login():
return "login"
if __name__ == '__main__':
app.run()
flask-script
'''
pip install flask-script
bu用右键启动 改为命令行启动
命令行:python s3.py runserver 或者 python3 s3.py runserver s3是py文件的文件名
'''
from flask import Flask
from flask_script import Manager
app=Flask(__name__)
manager=Manager(app)
@app.route("/")
def index():
return "ojbk"
'''
命令行启动下面的函数
python s3.py etom 123
s3是py文件名 etom是函数名 123是参数 函数没有参数就不用谢
'''
@manager.command
def etom(arg):
print("我把excl导入到myslq")
print(arg)
'''
命令行启动:
python s3.py cmd -n egon -y haha
python s3.py cmd -y hah -n egon
python s3.py cmd --you hah -n egon
python s3.py cmd --you hah --name egon
-n --name -y --you:就相当于形参
函数里的形参名要和dest一致
'''
@manager.option("-n","--name",dest="name")
@manager.option("-y","--you",dest="you")
def cmd(name,you):
print(name,you)
if __name__ == '__main__':
manager.run()