flask源码分析
Flask上下文管理流程图
路由源码分析
# 第一步
@app.route('/')
# 第二步
def route(self, rule, **options):
def decorator(f):
endpoint = options.pop('endpoint', None)
# 1. 路由声明@app.route('/')本质内部就是调用了add_url_rule, 因此路由写法也可以写成:
# app.add_url_rule(rule, endpoint, 路由对应的视图函数名, **以关键字形式传参的请求方式methods=[''] 或者 用于反向解析的别名endpoint='别名')
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
# 第三步:
@setupmethod
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
if endpoint is None:
# 2. _endpoint_from_view_func实现了endpoint没有传递时默认以函数名称作为方向解析的别名
'''
def _endpoint_from_view_func(view_func):
assert view_func is not None, 'expected view func if endpoint ' \
'is not provided.'
return view_func.__name__
'''
endpoint = _endpoint_from_view_func(view_func)
options['endpoint'] = endpoint
methods = options.pop('methods', None)
if methods is None:
# 3. 这里实现了如果路由没有指定methods请求方式, 默认就是get请求
methods = getattr(view_func, 'methods', None) or ('GET',)
if isinstance(methods, string_types):
raise TypeError('Allowed methods have to be iterables of strings, '
'for example: @app.route(..., methods=["POST"])')
# 4. 这里实现了控制请求方式无论指定大写还是小写都可以如: methods=["POST", 'get']
methods = set(item.upper() for item in methods)
...
app.run()源码分析
# 流程: app.run() -> run_simple -> app(request) -> app.__call__()
# 第一步: 从run_simple('localhost', 4000, hello) 推理成 app(request)
from werkzeug.wrappers import Request, Response
@Request.application
def hello(request):
return Response('Hello World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, hello)
# 第二步: app.__call__()
def __call__(self, environ, start_response):
"""
environ: {'wsgi.version': (1, 0), 'wsgi.url_scheme': 'http', ...}
start_response: <function WSGIRequestHandler.run_wsgi.<locals>.start_response at 0x00000171291CB378>
"""
return self.wsgi_app(environ, start_response)
# 第三步: self.wsgi_app (注意: Flask源码核心)
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
ctx.push()
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)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
CBV源码分析
1. View源码
# as_view('index') -> View -> as_view -> cls.decorators -> view -> self.dispatch_request
class View(object):
# 如果书写CBV那么必须指定请求方式: methods=['POST']
methods = None
decorators = ()
def dispatch_request(self):
# 如果继承View那么必须重写dispatch_request方法, 如果不重写, 就会抛出异常
raise NotImplementedError()
@classmethod
def as_view(cls, name, *class_args, **class_kwargs):
def view(*args, **kwargs):
self = view.view_class(*class_args, **class_kwargs)
return self.dispatch_request(*args, **kwargs)
if cls.decorators:
# name是指定函数别名: 本质就是防止反向解析的别名都是同一个
'''
def _endpoint_from_view_func(view_func):
assert view_func is not None, 'expected view func if endpoint ' \
'is not provided.'
return view_func.__name__
'''
view.__name__ = name
view.__module__ = cls.__module__
for decorator in cls.decorators:
# 这里就是循环去执行配置的装饰器列表
view = decorator(view)
# cls就是你自定义的类
view.view_class = cls
view.__name__ = name
view.__doc__ = cls.__doc__
view.__module__ = cls.__module__
view.methods = cls.methods
return view
2. MethodView源码
# 流程: as_view('index') -> View -> as_view -> cls.decorators -> view -> dispatch_request
def view(*args, **kwargs):
# self就是自定以类实例化出来的对象. view_class就是自定义的视图类
self = view.view_class(*class_args, **class_kwargs)
return self.dispatch_request(*args, **kwargs)
class MethodView(with_metaclass(MethodViewType, View)):
def dispatch_request(self, *args, **kwargs):
# 将你自定义类中的methods=['POST', 'get']字符串请求方式变成小写获取对应的请求方法
meth = getattr(self, request.method.lower(), None)
if meth is None and request.method == 'HEAD':
meth = getattr(self, 'get', None)
assert meth is not None, 'Unimplemented method %r' % request.method
# 再执行对应的方法
return meth(*args, **kwargs)
session源码分析
# 分析: app.session_interface
session_interface=SecureCookieSessionInterface()
get_signing_serializer方法: 请求来是将加密的session字符串解密成字典(s.loads). 请求走时将字典加密成字符串(s.dumps)
open_session方法: 请求来时的session的解密操作
save_session方法: 请求走时的session的加密操作
# 第一步: app.session_interface
# 第二步: session_interface=SecureCookieSessionInterface()
# 第三步: SecureCookieSessionInterface
class SecureCookieSessionInterface(SessionInterface):
salt = 'cookie-session'
digest_method = staticmethod(hashlib.sha1)
key_derivation = 'hmac'
serializer = session_json_serializer
session_class = SecureCookieSession
def get_signing_serializer(self, app):
# 如果使用session不指定app.secret_key那么就无法使用session
if not app.secret_key:
return None
signer_kwargs = dict(
key_derivation=self.key_derivation,
digest_method=self.digest_method
)
# 解密session与加密session的类: URLSafeTimedSerializer
return URLSafeTimedSerializer(app.secret_key, salt=self.salt,
serializer=self.serializer,
signer_kwargs=signer_kwargs)
def open_session(self, app, request):
s = self.get_signing_serializer(app)
if s is None:
return None
# 请求来的时候获取session字符串(提示: 也就是获取cookie)
val = request.cookies.get(app.session_cookie_name)
if not val:
# 请求来的时候如果没有获取到cookie那么, 就会生成一个空的session字典{}, 目的是防止session.get('key')取值时抛出异常
return self.session_class()
# app.permanent_session_lifetime获取的就是app.config配置文件中的session过期时间. 默认就是31天
max_age = total_seconds(app.permanent_session_lifetime)
try:
# 请求来时将加密的session字符串解密成data字典格式的数据
data = s.loads(val, max_age=max_age)
return self.session_class(data)
except BadSignature:
return self.session_class()
def save_session(self, app, session, response):
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)
if not session:
# 如果session['key']=value操作就会自动触发session类的__setitem__方法, 就会将之前字典中对应的key的value清空, 换成新的
if session.modified:
# app.session_cookie_name获取在cookies中存放session加密字符串的名字
response.delete_cookie(app.session_cookie_name,
domain=domain, path=path)
return
# app.config['SESSION_REFRESH_EACH_REQUEST']控制会话如何刷新
if not self.should_set_cookie(app, session):
return
# 控制 cookie 是否应被设置 httponly 的标. 默认: True
httponly = self.get_cookie_httponly(app)
# 控制 cookie 是否应被设置安全标志. 默认: False
secure = self.get_cookie_secure(app)
# 控制Session的生命周期(天). 默认31天
expires = self.get_expiration_time(app, session)
# 将session字典格式的数据进行加密, 再设置到cookie中
val = self.get_signing_serializer(app).dumps(dict(session))
response.set_cookie(app.session_cookie_name, val,
expires=expires, httponly=httponly,
domain=domain, path=path, secure=secure)
# 什么时候执行了open_session
# 流程:
'''
ctx = self.request_context(environ)
request_context
RequestContext
push
self.session = self.app.open_session(self.request)
'''
# 什么时候执行了save_session
'''
response = self.full_dispatch_request()
full_dispatch_request
return self.finalize_request(rv)
finalize_request
response = self.process_response(response)
process_response
self.save_session(ctx.session, response)
'''
请求扩展源码分析
before_first_request源码
# 第一步: self.before_first_request_funcs.append(f)
@setupmethod
def before_first_request(self, f):
# 把f这个被装饰的路由函数放到before_first_request_funcs列表中
self.before_first_request_funcs.append(f)
return f
# 第二步: self._got_first_request = False
def run(self, host=None, port=None, debug=None, **options):
from werkzeug.serving import run_simple
if host is None:
host = '127.0.0.1'
if port is None:
server_name = self.config['SERVER_NAME']
if server_name and ':' in server_name:
port = int(server_name.rsplit(':', 1)[1])
else:
port = 5000
if debug is not None:
self.debug = bool(debug)
options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
try:
run_simple(host, port, self, **options)
finally:
# 项目已启动就为self这个flask对象赋值一个_got_first_request属性, 初始化为False
self._got_first_request = False
# 第三步:
app(request)
# 第四步:
app.__call__()
# 第五步:
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
# 第六步: full_dispatch_request
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
ctx.push()
error = None
try:
try:
# 执行flask对应的完整的请求分发方法full_dispatch_request
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)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
# 第七步: try_trigger_before_first_request_functions(翻译: 第一个请求函数之前尝试触发)
def full_dispatch_request(self):
# 初始化在第一个请求函数之前尝试触发的这个方法: try_trigger_before_first_request_functions
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)
# 第八步:
def try_trigger_before_first_request_functions(self):
# 项目第一次启动_got_first_request就默认为False
if self._got_first_request:
return
with self._before_request_lock:
if self._got_first_request:
return
# for循环self.before_first_request_funcs中那些被before_first_request装饰器装的路由函数加括号一个一个执行
for func in self.before_first_request_funcs:
func()
# 执行完毕以后将_got_first_request执行为True. 从第二次请求开始self._got_first_request就是True, 以后被before_first_request装饰器装饰的路由函数再也不执行了
self._got_first_request = True
threading.local
多个线程修改同一个数据,复制多份变量给每个线程用,为每个线程开辟一块空间进行数据存储
1. 不用threading.local
# 不用local
from threading import Thread,current_thread
import time
yang = -1
def task(arg):
global yang
yang = arg
time.sleep(2)
print(f'{current_thread()}:{yang}')
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
# 10个线程执行 task 的打印结果
'''
<Thread(Thread-6, started 14272)>:9
<Thread(Thread-3, started 1656)>:9
<Thread(Thread-5, started 7636)>:9
<Thread(Thread-2, started 10408)>:9
<Thread(Thread-10, started 6728)>:9
<Thread(Thread-9, started 11316)>:9
<Thread(Thread-8, started 11088)>:9
<Thread(Thread-7, started 14228)>:9
<Thread(Thread-4, started 10540)>:9
<Thread(Thread-1, started 1540)>:9
'''
2. threading.local使用
# 使用local
from threading import Thread, current_thread
from threading import local
import time
from threading import get_ident
# 特殊的对象
yang = local()
# yang中的格式类似于: {'线程id':{value:1},'线程id':{value:2}....}
def task(arg):
yang.value = arg
time.sleep(2)
print(f'{current_thread()}: {yang.value}')
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
# 10个线程执行 task 的打印结果
'''
<Thread(Thread-10, started 10440)>: 9
<Thread(Thread-8, started 8772)>: 7
<Thread(Thread-7, started 13432)>: 6
<Thread(Thread-5, started 14772)>: 4
<Thread(Thread-4, started 1388)>: 3
<Thread(Thread-3, started 9492)>: 2
<Thread(Thread-1, started 13488)>: 0
<Thread(Thread-9, started 3744)>: 8
<Thread(Thread-2, started 9108)>: 1
<Thread(Thread-6, started 5492)>: 5
'''
3. 通过字典自定义threading.local(函数)
# 自己写一个类似local的东西
from threading import get_ident, Thread
import time
storage = {}
# {'线程id':{value:1},'线程id':{value:2}....}
def set(k, v):
ident = get_ident()
if ident in storage:
storage[ident][k] = v
else:
storage[ident] = {k: v}
def get(k):
ident = get_ident()
return storage[ident][k]
def task(arg):
set('val', arg)
v = get('val')
print(f'{get_ident()}:{v}')
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
# 10个线程执行 task 的打印结果
'''
4360:0
3816:1
10736:2
1580:3
4772:4
4024:5
9476:6
11036:7
12316:8
7372:9
'''
print(storage)
'''
{
11080: {'val': 0}, 2000: {'val': 1}, 13944: {'val': 2},
12552: {'val': 3}, 13736: {'val': 4}, 12476: {'val': 5},
9392: {'val': 6}, 14900: {'val': 7}, 12660: {'val': 8}, 8648: {'val': 9}
}
'''
4. 面向对象版
# 面向对象版本
from threading import get_ident, Thread
import time
class Local(object):
storage = {}
def set(self, k, v):
ident = get_ident()
if ident in Local.storage:
Local.storage[ident][k] = v
else:
Local.storage[ident] = {k: v}
def get(self, k):
ident = get_ident()
return Local.storage[ident][k]
obj = Local()
def task(arg):
obj.set('val', arg)
time.sleep(1)
v = obj.get('val')
print(f'{get_ident()}:{v}')
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
# 10个线程执行 task 的打印结果
'''
1844:0
7868:1
6040:9
11020:7
6780:8
8988:6
13136:5
14468:4
10892:3
12196:2
'''
print(obj.storage)
'''
{
1844: {'val': 0}, 7868: {'val': 1}, 12196: {'val': 2},
10892: {'val': 3}, 14468: {'val': 4}, 13136: {'val': 5},
8988: {'val': 6}, 11020: {'val': 7}, 6780: {'val': 8}, 6040: {'val': 9}
}
'''
5. 通过setattr和getattr实现
# 自己写的像local的类
from threading import get_ident, Thread
import time
class Local(object):
storage = {}
def __setattr__(self, k, v):
ident = get_ident()
if ident in Local.storage:
Local.storage[ident][k] = v
else:
Local.storage[ident] = {k: v}
def __getattr__(self, k):
ident = get_ident()
return Local.storage[ident][k]
obj = Local()
def task(arg):
obj.val = arg
time.sleep(1)
print(f'{get_ident()}:{obj.val}')
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
# 10个线程执行 task 的打印结果
'''
236:0
10448:6
8852:4
10728:2
4040:1
3780:5
10800:3
7560:9
10580:8
7664:7
'''
print(obj.storage)
'''
{
236: {'val': 0}, 4040: {'val': 1}, 10728: {'val': 2},
10800: {'val': 3}, 8852: {'val': 4}, 3780: {'val': 5},
10448: {'val': 6}, 7664: {'val': 7}, 10580: {'val': 8}, 7560: {'val': 9}
}
'''
6. 每个对象有自己的存储空间(字典)
# 每次实例化得到一个local对象,用自己的字典存储
from threading import get_ident, Thread
import time
class Local(object):
def __init__(self):
object.__setattr__(self, 'storage', {})
# self.storage={} # 这种方式不能用, 否则会出现递归调用问题
def __setattr__(self, k, v):
ident = get_ident()
if ident in self.storage:
self.storage[ident][k] = v
else:
self.storage[ident] = {k: v}
def __getattr__(self, k):
ident = get_ident()
return self.storage[ident][k]
obj = Local()
obj1 = Local()
def task(arg):
obj.val = arg
time.sleep(1)
print(obj.val)
def task1(arg):
obj1.val = arg
time.sleep(1)
print(obj1.val)
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
for i in range(10):
t = Thread(target=task1, args=(i * 100,))
t.start()
print(obj.storage)
'''
{
13440: {'val': 0}, 13092: {'val': 1}, 10404: {'val': 2},
5528: {'val': 3}, 14272: {'val': 4}, 14128: {'val': 5},
2312: {'val': 6}, 13736: {'val': 7}, 12620: {'val': 8}, 9524: {'val': 9}
}
'''
print(obj1.storage)
'''
{
12208: {'val': 0}, 10808: {'val': 100}, 13180: {'val': 200},
5292: {'val': 300}, 792: {'val': 400}, 1900: {'val': 500},
12212: {'val': 600}, 12680: {'val': 700}, 624: {'val': 800}, 1596: {'val': 900}
}
'''
7. 兼容线程和协程
关于greenlet与gevent: https://www.cnblogs.com/linhaifeng/articles/7429894.html#_label3
try:
# from greenlet import getcurrent as get_ident
from gevent import getcurrent as get_ident
except Exception as e:
from threading import get_ident
from gevent import monkey;monkey.patch_all()
import gevent
from threading import Thread
import time
from functools import partial
class Local(object):
def __init__(self):
object.__setattr__(self, 'storage', {})
def __setattr__(self, k, v):
ident = get_ident()
# 方式一:
# if ident in self.storage:
# self.storage[ident][k] = v
# else:
# self.storage[ident] = {k: v}
# 方式二:
try:
self.storage[ident][k] = v
except KeyError:
self.storage[ident] = {k: v}
def __getattr__(self, k):
ident = get_ident()
return self.storage[ident][k]
obj = Local()
obj1 = Local()
def task(arg):
obj.val = arg
# obj.xxx = arg
print(obj.val)
def task1(arg):
obj1.val = arg
# obj.xxx = arg
print(obj1.val)
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
g_li = []
for i in range(10):
g = gevent.spawn(partial(task1, i * 100))
g_li.append(g)
gevent.joinall(g_li)
print(obj.storage)
'''
{
<Greenlet at 0x2473b552148: _run>: {'val': 0}, <Greenlet at 0x2473b552248: _run>: {'val': 1},
<Greenlet at 0x2473b552448: _run>: {'val': 2}, <Greenlet at 0x2473b552648: _run>: {'val': 3},
<Greenlet at 0x2473b552748: _run>: {'val': 4}, <Greenlet at 0x2473b552848: _run>: {'val': 5},
<Greenlet at 0x2473b552948: _run>: {'val': 6}, <Greenlet at 0x2473b552a48: _run>: {'val': 7},
<Greenlet at 0x2473b552b48: _run>: {'val': 8}, <Greenlet at 0x2473b552c48: _run>: {'val': 9}
}
'''
print(obj1.storage)
'''
{
<Greenlet at 0x2473b552d48: _run>: {'val': 0}, <Greenlet at 0x2473b552e48: _run>: {'val': 100},
<Greenlet at 0x2473be62048: _run>: {'val': 200}, <Greenlet at 0x2473be62148: _run>: {'val': 300},
<Greenlet at 0x2473be62248: _run>: {'val': 400}, <Greenlet at 0x2473be62348: _run>: {'val': 500},
<Greenlet at 0x2473be62448: _run>: {'val': 600}, <Greenlet at 0x2473be62548: _run>: {'val': 700},
<Greenlet at 0x2473be62648: _run>: {'val': 800}, <Greenlet at 0x2473be62748: _run>: {'val': 900}
}
'''