当前位置:网站首页>Flask源码分析(三):上下文
Flask源码分析(三):上下文
2022-07-17 16:57:00 【mofei12138】
在Flask源码解析(二):Flask的工作流程中说过,关于上下文的内容会专门写一篇文章来讲,今天就把这个内容写一写。再次声明,本文涉及到的Flask源码都是出自Flask0.1的源码,部分代码为了方便理解只保留核心部分。
首先,在了解上下文之前,必须要弄清楚Local、LocalStack、LocalProxy这三个概念。
Local
Python中的thread locals实现了线程隔离的数据访问方式,但是在web应用中存在以下问题:
- 多个greenlet协程可以在同一个线程中,所以无法保证协程之间数据的隔离
- 多个http请求会复用线程,之前请求的数据会残留在thread locals中
WerkZeug的Local类解决了这两个问题。
Local的实现
try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident
class Local(object):
__slots__ = ('__storage__', '__ident_func__')
def __init__(self):
object.__setattr__(self, '__storage__', {
})
object.__setattr__(self, '__ident_func__', get_ident)
def __iter__(self):
return iter(self.__storage__.items())
def __call__(self, proxy):
"""Create a proxy for a name."""
return LocalProxy(self, proxy)
def __release_local__(self):
self.__storage__.pop(self.__ident_func__(), None)
def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {
name: value}
def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
- Local类使用__storage__字典实现greentlet或thread之间的数据隔离,这个字典的key是greenlet id或thread id(有greenlet模块时是greent id)
- 通过重写__getattr__和__setattr__方法,当我们使用Local对象访问属性时,会自动获取当前greentlet或thread存储空间里的属性值
- 通过__release_local__方法可以释放当前greentlet或thread的数据
LocalStack
LocalStack是基于Local实现的栈,提供了栈的push,pop,top方法。
LocalStack的实现
class LocalStack(object):
def __init__(self):
self._local = Local()
def __release_local__(self):
self._local.__release_local__()
def __call__(self):
def _lookup():
rv = self.top
if rv is None:
raise RuntimeError('object unbound')
return rv
return LocalProxy(_lookup)
def push(self, obj):
"""Pushes a new item to the stack"""
rv = getattr(self._local, 'stack', None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv
def pop(self):
"""Removes the topmost item from the stack, will return the old value or `None` if the stack was already empty. """
stack = getattr(self._local, 'stack', None)
if stack is None:
return None
elif len(stack) == 1:
release_local(self._local)
return stack[-1]
else:
return stack.pop()
@property
def top(self):
"""The topmost item on the stack. If the stack is empty, `None` is returned. """
try:
return self._local.stack[-1]
except (AttributeError, IndexError):
return None
- LocalStack内部维护的是_local对象的stack属性,所以它在greentlet或thread之间是数据隔离的
- 当栈中元素为空时,会自动调用release_local方法释放当前greentlet或thread的数据
LocalStack的用法
>>> ls = LocalStack()
>>> ls.push(42)
>>> ls.top
42
>>> ls.push(23)
>>> ls.top
23
>>> ls.pop()
23
>>> ls.top
42
LocalProxy
LocalProxy用于代理Local对象和LocalStack对象
LocalProxy的实现
@implements_bool
class LocalProxy(object):
__slots__ = ('__local', '__dict__', '__name__', '__wrapped__')
def __init__(self, local, name=None):
object.__setattr__(self, '_LocalProxy__local', local)
object.__setattr__(self, '__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)
def _get_current_object(self):
"""Return the current object. This is useful if you want the real object behind the proxy at a time for performance reasons or because you want to pass the object into a different context. """
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__)
@property
def __dict__(self):
try:
return self._get_current_object().__dict__
except RuntimeError:
raise AttributeError('__dict__')
def __repr__(self):
try:
obj = self._get_current_object()
except RuntimeError:
return '<%s unbound>' % self.__class__.__name__
return repr(obj)
def __bool__(self):
try:
return bool(self._get_current_object())
except RuntimeError:
return False
def __unicode__(self):
try:
return unicode(self._get_current_object()) # noqa
except RuntimeError:
return repr(self)
def __dir__(self):
try:
return dir(self._get_current_object())
except RuntimeError:
return []
def __getattr__(self, name):
if name == '__members__':
return dir(self._get_current_object())
return getattr(self._get_current_object(), name)
def __setitem__(self, key, value):
self._get_current_object()[key] = value
def __delitem__(self, key):
del self._get_current_object()[key]
......
- LocalProxy重载了所有的基本方法,使得对LocalProxy对象执行的任何操作,都是通过是_get_current_object来获取被代理对象,然后执行相应的操作,从而实现了动态更新被代理对象的效果
LocalProxy的用法
使用Local、LocalStack对象的__call__ 方法
from werkzeug.local import Local
l = Local()
# a proxy to whatever l.user is set to
user = l("user")
from werkzeug.local import LocalStack
_request_stack = LocalStack()
# a proxy to _request_stack.top
request = _request_stack()
使用Local对象作为参数
l = Local()
user = LocalProxy(l, 'user')
使用callable对象作为参数
# a proxy to the session attribute of the request proxy
session = LocalProxy(lambda: request.session)
了解完Local、LocalStack、LocalProxy,接下来看一下上下文。
什么是上下文
程序中的上下文代表了程序当下所运行的环境,存储了一些程序运行的信息。
Flask中的上下文
在Flask0.1中只有请求上下文,请求上下文中的对象有:
- request:封装了HTTP请求的内容,针对的是http请求。比如request.args.get(‘user’)可以获取get请求的参数。
- session:用来记录请求会话中的信息,针对的是用户信息。比如session[‘name’] = user.id可以记录用户信息。
- current_app:表示当前运行程序文件的程序实例,比如可以通过current_app.name打印出当前应用程序实例的名字。
- g:用于临时存储的对象,每次请求都会重置这个变量。比如可以通过它传递一些临时数据。
特别说明:在Flask0.9增加了应用上下文并将current_app移动到应用上下文中,在Flask1.0中g对象也被移动到应用上下文中。
Flask中上下文的定义
_request_ctx_stack = LocalStack()
current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
request = LocalProxy(lambda: _request_ctx_stack.top.request)
session = LocalProxy(lambda: _request_ctx_stack.top.session)
g = LocalProxy(lambda: _request_ctx_stack.top.g)
上下文处理流程
在上一篇文章2. Flask源码解析(二):Flask的工作流程.note中说到,wsgi_app方法中会创建上下文:
def wsgi_app(self, environ, start_response):
with self.request_context(environ):
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
response = self.make_response(rv)
response = self.process_response(response)
return response(environ, start_response)
request_context()方法中会创建一个_RequestContext对象,_RequestContext源码如下:
class _RequestContext(object):
def __init__(self, app, environ):
self.app = app
self.url_adapter = app.url_map.bind_to_environ(environ)
self.request = app.request_class(environ)
self.session = app.open_session(self.request)
self.g = _RequestGlobals()
self.flashes = None
def __enter__(self):
_request_ctx_stack.push(self)
def __exit__(self, exc_type, exc_value, tb):
if tb is None or not self.app.debug:
_request_ctx_stack.pop()
从上面的代码可以看出,with self.request_context(environ)的作用就是对每个http请求,先创建请求上下文对象,放到_request_ctx_stack栈对象中,在响应返回之后把请求上下文对象弹出,这样在视图函数处理期间就可以从栈上获取request、session、current_app、g这几个对象
疑问解答
1.在Web应用运行时一个线程同时只处理一个请求,那么 _req_ctx_stack和 _app_ctx_stack肯定都是只有一个栈顶元素的。那么为什么还要用栈这种结构?
答:Flask的设计理念之一就是多应用的支持。当在一个应用的上下文环境中,需要嵌套处理另一个应用的相关操作时,这时就能体现出栈的好处了,只要通过调用_request_ctx_stack.top.app或者current_app可以获得当前上下文环境正在处理哪个应用。
边栏推荐
- 关于“抄底”心态,让你筋疲力尽 2020-03-15
- 可视化ETL工具Kettle概念、安装及实战案例
- Relationship and difference between wav and PCM
- 结构体内存对齐、位段、联合
- Impulse function, step function, ramp function and impulse response
- When will the deflationary market reverse? How should we operate? 2020-03-13
- 超声波传感器系列文章汇总
- mycat 根据字符串范围hash值分库分表
- Preparation Notes: Matplotlib learning notes a
- Growth of operation and maintenance Xiaobai - week 6 of Architecture
猜你喜欢

标签球问题

Lazy to the bone, I am too lazy to write articles in CSDN. I write articles based on selenium simulation

深度梳理:机器学习建模调参方法总结

国产API工具哪家强?原来是它…

第二天实验

MyCat2搭建mysql主从分离

Return to risk ratio: the most important indicator of investment opportunities 2020-03-14

Day 4 homework

第四天作业
MIHA tour 2023 autumn recruitment officially begins ~ early approval has the opportunity to avoid written examination!
随机推荐
O & M LITTLE WHITE Growth record - architecture week 6
Thoughts on ahe investment
Yunxi focuses on store broadcast solutions to accelerate global layout
Acwing785. Quick sort
Enrollment publicity - Jiangnan University
MYCAT divides the database and table according to the hash value of the string range
mycat 根据字符串范围hash值分库分表
RingBuffer
二叉树2—对称性递归问题
Brief analysis of circuit fault
Ultrasonic sensor (chx01) learning notes Ⅲ - I2C reading and writing operation
云犀&腾讯云达成战略合作,加速拓展全球直播市场
Binary tree 2-symmetry recursion problem
Array de duplication array sorting maximum sum class array conversion filter array
Redis logical cluster creation
Talk about the redis cache penetration scenario and the corresponding solutions
Committer identity unknown *** Please tell me who you are...
go web
go web
Mysql的知识梳理