一、上下文(Context)

什么是上下文:

每一段程序都有很多外部变量。只有像Add这种简单的函数才是没有外部变量的。一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行。你为了使他们运行,就要给所有的外部变量一个一个写一些值进去。这些值的集合就叫上下文。

譬如说在C++的lambda表达是里面,[写在这里的就是上下文](int a, int b){ ... }。--- 引用  vczh

当接受到一个请求时,我们通常将请求封装成一个HttpRequest对象,然后将对象传递给每一个视图函数,我们从request对象中获取相关的请求信息,这样做的缺点就是所有的视图函数都需要添加reqeust参数,即使视图函数中并没有使用到它。

而在Flask中,将这些信息封装成类似全局变量的东西,视图函数需要的时候,可以使用from flask import request获取。但是这些对象和全局变量不同的是:他们必须是动态的,也就是说在多线程/多协程的情况下,每个线程获取的request都是属于自己的,不会相互干扰。

Flask提供了应用上下文(Application Context)和请求上下文(Reqeust Context).

上下文的有关定义在:flask/globals.py中

def _lookup_req_object(name):
top = _request_ctx_stack.top
if top is None:
raise RuntimeError(_request_ctx_err_msg)
return getattr(top, name) def _lookup_app_object(name):
top = _app_ctx_stack.top
if top is None:
raise RuntimeError(_app_ctx_err_msg)
return getattr(top, name) def _find_app():
top = _app_ctx_stack.top
if top is None:
raise RuntimeError(_app_ctx_err_msg)
return top.app # context locals
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_app_object, 'g'))

Flask提供了两种上下文:request context 和 application context 。request context有衍生出request和session,appliction context衍生出current_app 和 g.

请求上下文和应用上下文是通过LocalStack()和LocalProxy()来实现的

LocalStack()和LocalProxy()是由Werkzeug提供,定义在werkzeug/local.py中,在分析这两个类之前,我们需要先分析另一个类Local。

二、Local类

Local类位于werkzeug/local.py中,它为LocalStack和LocalProxy提供了基础,Local就是实现了threading.local的效果——在多线程情况下全局变量的隔离效果。

class Local(object):
__slots__ = ('__storage__', '__ident_func__') def __init__(self):
# 用于数据的保存
object.__setattr__(self, '__storage__', {})
# 用于获取当前线程的id
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:
# 通过线程id获取数据
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:
# 通过线程id绑定数据
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)

存储形式键为线程/协程标识数值,值为字典类型的数据。

三、LocalStack

class LocalStack(object):

    def __init__(self):
self._local = Local() def __release_local__(self):
self._local.__release_local__() def _get__ident_func__(self):
return self._local.__ident_func__ def _set__ident_func__(self, value):
object.__setattr__(self._local, '__ident_func__', value)
__ident_func__ = property(_get__ident_func__, _set__ident_func__)
del _get__ident_func__, _set__ident_func__ 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中
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

通过Local和LocalStack,我们可以了解到数据是以字典的形式存储在LocalStack中的,键为线程/协程id,值也是字典类型。LocalStack还实现了pushpoptop等方法。其中top方法永远指向栈顶的元素。栈顶的元素是指当前线程/协程中最后被推入栈中的元素。

我们在flask/globals.py中看到请求上下文的定义,他就是一个LocalStack的实例:

_request_ctx_stack = LocalStack()

至于为什么要用到栈的结构,而不是直接使用Local()呢?

四、LocalProxy类

LocalProxy类是一个Local对象的代理,负责把所有对自己的操作都转发个内部的Local对象

class LocalProxy(object):
"""Acts as a proxy for a werkzeug local.
Forwards all operations to a proxied object. """
__slots__ = ('__local', '__dict__', '__name__') def __init__(self, local, name=None):
object.__setattr__(self, '_LocalProxy__local', local)
object.__setattr__(self, '__name__', name) def _get_current_object(self):
"""Return the current object."""
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 __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

这里实现的关键是把通过参数传递进来的 Local 实例保存在 __local 属性中,并定义了 _get_current_object() 方法获取当前线程或者协程对应的对象。

继续回到request context的实现:

_request_ctx_stack = LocalStack()

request = LocalProxy(partial(_lookup_req_object, 'request'))

session = LocalProxy(partial(_lookup_req_object, 'session'))

_request_ctx_stack 是多线程或者协程隔离的栈结构

request 每次都会调用 _lookup_req_object 栈头部的数据来获取保存在里面的 requst context。

Flask源码解析:Flask上下文的更多相关文章

  1. flask 源码解析:上下文(一)

    文章出处  https://www.cnblogs.com/jackchengcc/archive/2018/11/29/10025949.html 一:什么是上下文 每一段程序都有很多外部变量.只有 ...

  2. Flask源码解析:Flask应用执行流程及原理

    WSGI WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述服务器端如何与web应用程序通信的 ...

  3. flask源码解析之上下文为什么用栈

    楔子 我在之前的文章<flask源码解析之上下文>中对flask上下文流程进行了详细的说明,但是在学习的过程中我一直在思考flask上下文中为什么要使用栈完成对请求上下文和应用上下文的入栈 ...

  4. flask源码解析之上下文

    引入 对于flask而言,其请求过程与django有着截然不同的流程.在django中是将请求一步步封装最终传入视图函数的参数中,但是在flask中,视图函数中并没有请求参数,而是将请求通过上下文机制 ...

  5. Flask 源码流程,上下文管理

    源码流程 创建对象 from flask import Flask """ 1 实例化对象 app """ app = Flask(__na ...

  6. flask源码解析之session

    内容回顾 cookie与session的区别: 1. session 是保存在服务端的键值对 2. cookie 只能保存4096个字节的数据,但是session不受限制 3. cookie保存在浏览 ...

  7. 【Flask源码分析——请求上下文与应用上下文】

    Flask中有两种上下文,请求上下文和应用上下文.两者的作用域都处于一个请求的局部中. 查看源代码,上下文类在flask.ctx模块中定义 AppContext类定义应用上下文,app是当前应用Web ...

  8. 源码解析Flask的配置文件

    在flask里,我们常在主文件中定义某些配置,比如: app.debug = True app.secret_key = 'helloworld!!' 实际上,flask中默认可以进行可选的配置项有很 ...

  9. 源码解析flask的路由系统

    源码解析flask的路由系统 当我们新建一个flask项目时,pycharm通常已经为项目定义了一个基本路由 @app.route('/') def hello_world(): return 'He ...

随机推荐

  1. PAT 甲级 1117 Eddington Number

    https://pintia.cn/problem-sets/994805342720868352/problems/994805354762715136 British astronomer Edd ...

  2. mysql 清空数据

    清空数据有2 个命令 -- 清空全部数据,不写日志,不可恢复,速度极快 truncate table 表名; -- 清空全部数据,写日志,数据可恢复,速度慢 delete from 表名 业务需求:清 ...

  3. Python网络编程:IO多路复用

    io多路复用:可以监听多个文件描述符(socket对象)(文件句柄),一旦文件句柄出现变化,即可感知. sk1 = socket.socket() sk1.bind(('127.0.0.1',8001 ...

  4. Dubbo和Spring Cloud微服务架构比较

    Dubbo 出生于阿里系,是阿里巴巴服务化治理的核心框架,并被广泛应用于中国各互联网公司:只需要通过 Spring 配置的方式即可完成服务化,对于应用无入侵,设计的目的还是服务于自身的业务为主. 微服 ...

  5. 解决sublime text3下中文无法输入的问题(Ubuntu)

    sublime-text-imfix,非常无脑.就喜欢这样的.

  6. SVN Update Error: Please execute the 'Cleanup' command

    尝试用下面两种方法 svn clean up 中有一个选项break lock勾选上 把对应的文件来里的.svn里面的lock文件删除. svn local delete, incoming dele ...

  7. Windows 10 中的存储空间

    存储空间有助于保护你的数据免受驱动器故障的影响,并随着你向电脑添加驱动器而扩展存储.你可以使用存储空间将两个或多个驱动器一起分组到一个存储池中,然后使用该池的容量来创建称为存储空间的虚拟驱动器.这些存 ...

  8. MT【165】分段函数

    (2018浙江省赛12题改编)设$a\in R$,且对任意的实数$b$均有$\max\limits_{x\in[0,1]}|x^2+ax+b|\ge\dfrac{1}{4}$求$a$ 的范围. 提示: ...

  9. SpringBoot整合Swagger-ui

    SpringBoot整合Swagger-ui 引入依赖 <dependency> <groupId>org.springframework.boot</groupId&g ...

  10. 《剑指offer》— JavaScript(27)字符串的排列

    字符串的排列 题目描述 输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba. ...