flask上下文

来源:转载

在看flask web开发的时候,测试模块有如下代码:


import unittest
from flask import current_app
from app import create_app, db
class BasicsTestCase(unittest.TestCase):
def setUp(self):
self.app = create_app('testing')
self.app_context = self.app.app_context() # app.context()??
self.app_context.push() # 这是做什么??
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
self.app_context.pop()
def test_app_exists(self):
self.assertFalse(current_app is None) # current_app ??
def test_app_is_testing(self):
self.assertTrue(current_app.config['TESTING'])

在app_context.push()以前全局变量current_app代表的是none,当push以后代表当前flask应用对象


Flask中的请求上下文和应用上下文


原文链接:https://segmentfault.com/a/1190000009152655


在Flask中处理请求时,应用会生成一个“请求上下文”对象。整个请求的处理过程,都会在这个上下文对象中进行。这保证了请求的处理过程不被干扰。处理请求的具体代码如下:


def wsgi_app(self, environ, start_response):
with self.request_context(environ):
# with语句中生成一个`response`对象
...
return response(environ, start_response)


在Flask 0.9版本之前,应用只有“请求上下文”对象,它包含了和请求处理相关的信息。同时Flask还根据werkzeug.local模块中实现的一种数据结构LocalStack用来存储“请求上下文”对象。这在一个Flask应用运行过程剖析
中有所介绍。在0.9版本中,Flask又引入了“应用上下文”的概念。本文主要Flask中的这两个“上下文”对象。


LocalStack


在介绍“请求上下文”和“应用上下文”之前,我们对LocalStack简要做一个回顾。在Werkzeug库——local模块
一文中,我们讲解了werkzeug.local
模块中实现的三个类Local、LocalStack
和LocalProxy。关于它们的概念和详细介绍,可以查看上面的文章。这里,我们用一个例子来说明Flask中使用的一种数据结构LocalStack。


>>> from werkzeug.local import LocalStack
>>> import threading

# 创建一个`LocalStack`对象
>>> local_stack = LocalStack()
# 查看local_stack中存储的信息
>>> local_stack._local.__storage__
{}

# 定义一个函数,这个函数可以向`LocalStack`中添加数据
>>> def worker(i):
local_stack.push(i)

# 使用3个线程运行函数`worker`
>>> for i in range(3):
t = threading.Thread(target=worker, args=(i,))
t.start()

# 再次查看local_stack中存储的信息
>>> local_stack._local.__storage__
{<greenlet.greenlet at 0x4bee5a0>: {'stack': [2]},
<greenlet.greenlet at 0x4bee638>: {'stack': [1]},
<greenlet.greenlet at 0x4bee6d0>: {'stack': [0]}
}

由上面的例子可以看出,存储在LocalStack中的信息以字典的形式存在:键为线程/协程的标识数值,值也是字典形式。每当有一个线程/协程上要将一个对象push进LocalStack栈中,会形成如上一个“键-值”对。这样的一种结构很好地实现了线程/协程的隔离,每个线程/协程都会根据自己线程/协程的标识数值确定存储在栈结构中的值。



LocalStack还实现了push、pop、top等方法。其中top方法永远指向栈顶的元素。栈顶的元素是指当前线程/协程中最后被推入栈中的元素,即local_stack._local.stack-1。


请求上下文

Flask中所有的请求处理都在“请求上下文”中进行,在它设计之初便就有这个概念。由于0.9版本代码比较复杂,这里还是以0.1版本的代码为例进行说明。本质上这两个版本的“请求上下文”的运行原理没有变化,只是新版本增加了一些功能,这点在后面再进行解释。


请求上下文——0.1版本
# Flask v0.1
class _RequestContext(object):
"""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, 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):
# do not pop the request stack if we are in debug mode and an
# exception happened.This will allow the debugger to still
# access the request object in the interactive shell.
if tb is None or not self.app.debug:
_request_ctx_stack.pop()

由上面“请求上下文”的实现可知:


“请求上下文”是一个上下文对象,实现了enter
和exit
方法。可以使用with语句构造一个上下文环境。
进入上下文环境时,_request_ctx_stack这个栈中会推入一个_RequestContext对象。这个栈结构就是上面讲的LocalStack栈。
推入栈中的_RequestContext对象有一些属性,包含了请求的的所有相关信息。例如app、request、session、g、flashes。还有一个url_adapter,这个对象可以进行URL匹配。
在with语句构造的上下文环境中可以进行请求处理。当退出上下文环境时,_request_ctx_stack这个栈会销毁刚才存储的上下文对象。

以上的运行逻辑使得请求的处理始终在一个上下文环境中,这保证了请求处理过程不被干扰,而且请求上下文对象保存在LocalStack栈中,也很好地实现了线程/协程的隔离。


以下是一个简单的例子:


# example - Flask v0.1
>>> from flask import Flask, _request_ctx_stack
>>> import threading
>>> app = Flask(__name__)
# 先观察_request_ctx_stack中包含的信息
>>> _request_ctx_stack._local.__storage__
{}

# 创建一个函数,用于向栈中推入请求上下文
# 本例中不使用`with`语句
>>> def worker():
# 使用应用的test_request_context()方法创建请求上下文
request_context = app.test_request_context()
_request_ctx_stack.push(request_context)

# 创建3个进程分别执行worker方法
>>> for i in range(3):
t = threading.Thread(target=worker)
t.start()

# 再观察_request_ctx_stack中包含的信息
>>> _request_ctx_stack._local.__storage__
{<greenlet.greenlet at 0x5e45df0>: {'stack': [<flask._RequestContext at 0x710c668>]},
<greenlet.greenlet at 0x5e45e88>: {'stack': [<flask._RequestContext at 0x7107f28>]},
<greenlet.greenlet at 0x5e45f20>: {'stack': [<flask._RequestContext at 0x71077f0>]}
}

上面的结果显示:_request_ctx_stack中为每一个线程创建了一个“键-值”对,每一“键-值”对中包含一个请求上下文对象。如果使用with语句,在离开上下文环境时栈中销毁存储的上下文对象信息。


请求上下文——0.9版本

在0.9版本中,Flask引入了“应用上下文”的概念,这对“请求上下文”的实现有一定的改变。这个版本的“请求上下文”也是一个上下文对象。在使用with语句进入上下文环境后,_request_ctx_stack会存储这个上下文对象。不过与0.1版本相比,有以下几点改变:


请求上下文实现了push、pop方法,这使得对于请求上下文的操作更加的灵活;
伴随着请求上下文对象的生成并存储在栈结构中,Flask还会生成一个“应用上下文”对象,而且“应用上下文”对象也会存储在另一个栈结构中去。这是两个版本最大的不同。

我们先看一下0.9版本相关的代码:


# Flask v0.9
def push(self):
"""Binds the request context to the current context."""
top = _request_ctx_stack.top
if top is not None and top.preserved:
top.pop()

# 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)

self.session = self.app.open_session(self.request)
if self.session is None:
self.session = self.app.make_null_session()

我们注意到,0.9版本的“请求上下文”的pop方法中,当要将一个“请求上下文”推入_request_ctx_stack栈中的时候,会先检查另一个栈_app_ctx_stack的栈顶是否存在“应用上下文”对象或者栈顶的“应用上下文”对象的应用是否是当前应用。如果不存在或者不是当前对象,Flask会自动先生成一个“应用上下文”对象,并将其推入_app_ctx_stack中。


我们再看离开上下文时的相关代码:


# Flask v0.9
def pop(self, exc=None):
"""Pops the request context and unbinds it by doing that.This will
also trigger the execution of functions registered by the
:meth:`~flask.Flask.teardown_request` decorator.

.. versionchanged:: 0.9
Added the `exc` argument.
"""
app_ctx = self._implicit_app_ctx_stack.pop()

clear_request = False
if not self._implicit_app_ctx_stack:
self.preserved = False
if exc is None:
exc = sys.exc_info()[1]
self.app.do_teardown_request(exc)
clear_request = True

rv = _request_ctx_stack.pop()
assert rv is self, 'Popped wrong request context.(%r instead of %r)'
% (rv, self)

# get rid of circular dependencies at the end of the request
# so that we don't require the GC to be active.
if clear_request:
rv.request.environ['werkzeug.request'] = None

# Get rid of the app as well if necessary.
if app_ctx is not None:
app_ctx.pop(exc)

上面代码中的细节先不讨论。注意到当要离开以上“请求上下文”环境的时候,Flask会先将“请求上下文”对象从_request_ctx_stack栈中销毁,之后会根据实际的情况确定销毁“应用上下文”对象。


以下还是以一个简单的例子进行说明:


# example - Flask v0.9
>>> from flask import Flask, _request_ctx_stack, _app_ctx_stack
>>> app = Flask(__name__)

# 先检查两个栈的内容
>>> _request_ctx_stack._local.__storage__
{}
>>> _app_ctx_stack._local.__storage__
{}

# 生成一个请求上下文对象
>>> request_context = app.test_request_context()
>>> request_context.push()

# 请求上下文推入栈后,再次查看两个栈的内容
>>> _request_ctx_stack._local.__storage__
{<greenlet.greenlet at 0x6eb32a8>: {'stack': [<RequestContext 'http://localhost/' [GET] of __main__>]}}
>>> _app_ctx_stack._local.__storage__
{<greenlet.greenlet at 0x6eb32a8>: {'stack': [<flask.ctx.AppContext at 0x5c96a58>]}}

>>> request_context.pop()

# 销毁请求上下文时,再次查看两个栈的内容
>>> _request_ctx_stack._local.__storage__
{}
>>> _app_ctx_stack._local.__storage__
{}
应用上下文

上部分中简单介绍了“应用上下文”和“请求上下文”的关系。那什么是“应用上下文”呢?我们先看一下它的类:


class AppContext(object):
"""The application context binds an application object implicitly
to the current thread or greenlet, similar to how the
:class:`RequestContext` binds request information.The application
context is also implicitly created if a request context is created
but the application is not on top of the individual application
context.
"""

def __init__(self, app):
self.app = app
self.url_adapter = app.create_url_adapter(None)

# Like request context, app contexts can be pushed multiple times
# but there a basic "refcount" is enough to track them.
self._refcnt = 0

def push(self):
"""Binds the app context to the current context."""
self._refcnt += 1
_app_ctx_stack.push(self)

def pop(self, exc=None):
"""Pops the app context."""
self._refcnt -= 1
if self._refcnt <= 0:
if exc is None:
exc = sys.exc_info()[1]
self.app.do_teardown_appcontext(exc)
rv = _app_ctx_stack.pop()
assert rv is self, 'Popped wrong app context.(%r instead of %r)' /
% (rv, self)

def __enter__(self):

由以上代码可以看出:“应用上下文”也是一个上下文对象,可以使用with语句构造一个上下文环境,它也实现了push、pop等方法。“应用上下文”的构造函数也和“请求上下文”类似,都有app、url_adapter等属性。“应用上下文”存在的一个主要功能就是确定请求所在的应用。


然而,以上的论述却又让人产生这样的疑问:既然“请求上下文”中也包含app等和当前应用相关的信息,那么只要调用_request_ctx_stack.top.app或者魔法current_app就可以确定请求所在的应用了,那为什么还需要“应用上下文”对象呢?对于单应用单请求来说,使用“请求上下文”确实就可以了。然而,Flask的设计理念之一就是多应用的支持。当在一个应用的请求上下文环境中,需要嵌套处理另一个应用的相关操作时,“请求上下文”显然就不能很好地解决问题了。如何让请求找到“正确”的应用呢?我们可能会想到,可以再增加一个请求上下文环境,并将其推入_request_ctx_stack栈中。由于两个上下文环境的运行是独立的,不会相互干扰,所以通过调用_request_ctx_stack.top.app或者魔法current_app也可以获得当前上下文环境正在处理哪个应用。这种办法在一定程度上可行,但是如果对于第二个应用的处理不涉及到相关请求,那也就无从谈起“请求上下文”。


为了应对这个问题,Flask中将应用相关的信息单独拿出来,形成一个“应用上下文”对象。这个对象可以和“请求上下文”一起使用,也可以单独拿出来使用。不过有一点需要注意的是:在创建“请求上下文”时一定要创建一个“应用上下文”对象。有了“应用上下文”对象,便可以很容易地确定当前处理哪个应用,这就是魔法current_app。在0.1版本中,current_app是对_request_ctx_stack.top.app的引用,而在0.9版本中current_app是对_app_ctx_stack.top.app的引用。


下面以一个多应用的例子进行说明:


# example - Flask v0.9
>>> from flask import Flask, _request_ctx_stack, _app_ctx_stack
# 创建两个Flask应用
>>> app = Flask(__name__)
>>> app2 = Flask(__name__)
# 先查看两个栈中的内容
>>> _request_ctx_stack._local.__storage__
{}
>>> _app_ctx_stack._local.__storage__
{}
# 构建一个app的请求上下文环境,在这个环境中运行app2的相关操作
>>> with app.test_request_context():
print "Enter app's Request Context:"
print _request_ctx_stack._local.__storage__
print _app_ctx_stack._local.__storage__
print
with app2.app_context():
print "Enter app2's App Context:"
print _request_ctx_stack._local.__storage__
print _app_ctx_stack._local.__storage__
print
# do something
print "Exit app2's App Context:"
print _request_ctx_stack._local.__storage__
print _app_ctx_stack._local.__storage__
print
# Result
Enter app's Request Context:
{<greenlet.greenlet object at 0x000000000727A178>: {'stack': [<RequestContext 'http://localhost/' [GET] of __main__>]}}
{<greenlet.greenlet object at 0x000000000727A178>: {'stack': [<flask.ctx.AppContext object at 0x0000000005DD0DD8>]}}
Enter app2's App Context:
{<greenlet.greenlet object at 0x000000000727A178>: {'stack': [<RequestContext 'http://localhost/' [GET] of __main__>]}}
{<greenlet.greenlet object at 0x000000000727A178>: {'stack': [<flask.ctx.AppContext object at 0x0000000005DD0DD8>, <flask.ctx.AppContext object at 0x0000000007313198>]}}
Exit app2's App Context
{<greenlet.greenlet object at 0x000000000727A178>: {'stack': [<RequestContext 'http://localhost/' [GET] of __main__>]}}
{<greenlet.greenlet object at 0x000000000727A178>: {'stack': [<flask.ctx.AppContext object at 0x0000000005DD0DD8>]}}

在以上的例子中:


我们首先创建了两个Flask应用app和app2;
接着我们构建了一个app的请求上下文环境。当进入这个环境中时,这时查看两个栈的内容,发现两个栈中已经有了当前请求的请求上下文对象和应用上下文对象。并且栈顶的元素都是app的请求上下文和应用上下文;
之后,我们再在这个环境中嵌套app2的应用上下文。当进入app2的应用上下文环境时,两个上下文环境便隔离开来,此时再查看两个栈的内容,发现_app_ctx_stack中推入了app2的应用上下文对象,并且栈顶指向它。这时在app2的应用上下文环境中,current_app便会一直指向app2;
当离开app2的应用上下文环境,_app_ctx_stack栈便会销毁app2的应用上下文对象。这时查看两个栈的内容,发现两个栈中只有app的请求的请求上下文对象和应用上下文对象。
最后,离开app的请求上下文环境后,两个栈便会销毁app的请求的请求上下文对象和应用上下文对象,栈为空。
与上下文对象有关的“全局变量”

在Flask中,为了更加方便地处理一些变量,特地提出了“全局变量”的概念。这些全局变量有:


# Flask v0.9
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_object, 'request'))
session = LocalProxy(partial(_lookup_object, 'session'))
g = LocalProxy(partial(_lookup_object, 'g'))

# 辅助函数
def _lookup_object(name):
top = _request_ctx_stack.top
if top is None:
raise RuntimeError('working outside of request context')
return getattr(top, name)


def _find_app():
top = _app_ctx_stack.top
if top is None:
raise RuntimeError('working outside of application context')
return top.app

可以看出,Flask中使用的一些“全局变量”,包括current_app、request、session、g等都来自于上下文对象。其中current_app一直指向_app_ctx_stack栈顶的“应用上下文”对象,是对当前应用的引用。而request、session、g等一直指向_request_ctx_stack栈顶的“请求上下文”对象,分别引用请求上下文的request、session和g。不过,从 Flask 0.10 起,对象 g 存储在应用上下文中而不再是请求上下文中。


另外一个问题,在形成这些“全局变量”的时候,使用了werkzeug.local模块的LocalProxy类。之所以要用该类,主要是为了动态地实现对栈顶元素的引用。如果不使用这个类,在生成上述“全局变量”的时候,它们因为指向栈顶元素,而栈顶元素此时为None,所以这些变量也会被设置为None常量。后续即使有上下文对象被推入栈中,相应的“全局变量”也不会发生改变。为了动态地实现对栈顶元素的引用,这里必须使用werkzeug.local模块的LocalProxy类。


关于如何阅读源码


github搜索flask源码-> clone-> checkout 0.1 -> 在0.1的发行版里面找到第一次提交的哈希代号
-> 再次checkout该代号就是第一次commit


第一次提交源码

做了稍微整理,主要是一些替换掉的模块


import pkg_resources
from jinja2 import Environment, PackageLoader
from werkzeug.wrappers import Request, Response
from werkzeug.local import LocalStack, LocalProxy
from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException
from werkzeug.contrib.securecookie import SecureCookie
# try to import the json helpers
try:
from simplejson import loads as load_json, dumps as dump_json
except ImportError:
try:
from json import loads as load_json, dumps as dump_json
except ImportError:
pass
from werkzeug.wrappers import cached_property
class FlaskRequest(Request):
def __init__(self, environ):
Request.__init__(self, environ)
self.endpoint = None
self.view_args = None
class FlaskResponse(Response):
default_mimetype = 'text/html'
class _RequestGlobals(object):
pass
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 url_for(endpoint, **values):
# endpoint 把函数作为路由的一部分
# values 作为参数
# 猜测:当客户端在浏览器输入127.0.0.1:5000后访问的页面有url_for这个代码,
# 自动就执行返回栈顶适配器(url_adapter)也就是该请求(127.0.0.1:5000)加入127.0.0.1:5000/endpoint/values
# 所以_request_ctx_stack.top代表的就是客户端最近的一次请求???
return _request_ctx_stack.top.url_adapter.build(endpoint, values)
def jsonified(**values):
"""Returns a json response"""
return current_app.response_class(dump_json(values),
mimetype='application/json')
def flash(message):
session['_flashes'] = (session.get('_flashes', [])) + [message]
def get_flashed_messages():
flashes = _request_ctx_stack.top.flashes
if flashes is None:
_request_ctx_stack.top.flashes = flashes = /
session.pop('_flashes', [])
return flashes
def render_template(template_name, **context):
return current_app.jinja_env.get_template(template_name).render(context)
def render_template_string(source, **context):
# example: return render_template_string('<h1>{{ error_info }}</h1>', error_info=error), 401
return current_app.jinja_env.from_string(source).render(context)
class Flask(object):
#: 上面定义的FlaskRequest,它的父类是werkzeug.Response
request_class = FlaskRequest
#: the class that is used for response objects
response_class = FlaskResponse
static_path = '/static'
secret_key = None
session_cookie_name = 'session'#cookie保存session的名字
#: options that are passed directly to the Jinja2 environment
jinja_options = dict(
autoescape=True,
extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
)
def __init__(self, package_name):
self.debug = False
self.package_name = package_name
self.view_functions = {}
self.error_handlers = {}
self.request_init_funcs = []
self.request_shutdown_funcs = []
self.url_map = Map()
# 待深入,处理静态文件路由映射
if self.static_path is not None:
self.url_map.add(Rule(self.static_path + '/<filename>',
build_only=True, endpoint='static'))
# 创建模版配置环境
self.jinja_env = Environment(loader=self.create_jinja_loader(),
**self.jinja_options)
# 把请求的全局变量加入模版中
self.jinja_env.globals.update(
url_for=url_for,
request=request,
session=session,
g=g,
get_flashed_messages=get_flashed_messages
)
def create_jinja_loader(self):
# jinja2的类,实现引入templates文件夹
return PackageLoader(self.package_name)
def run(self, host='localhost', port=5000, **options):
"""Runs the application on a local development server"""
from werkzeug.serving import run_simple
# 如果传递了debug变量,就把debug变量的值赋给全局的self.debug
if 'debug' in options:
self.debug = options.pop('debug')
# 如果static_files变量不是空就使用static文件夹作为静态文件目录
if self.static_path is not None:
options['static_files'] = {
self.static_path: (self.package_name, 'static')
}
# 设置默认值
options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
# print(options)
return run_simple(host, port, self, **options)
@cached_property
def test(self):
"""A test client for this application"""
from werkzeug.test import Client
return Client(self, self.response_class, use_cookies=True)
def open_resource(self, resource):
"""Opens a resource from the application's resource folder"""
# 上面导入的python内置类
return pkg_resources.resource_stream(self.package_name, resource)
def open_session(self, request):
# 依赖werkzeug.contrib.securecookie,应该是给cookie保存的session名字进行加密或解密
key = self.secret_key
if key is not None:
return SecureCookie.load_cookie(request, self.session_cookie_name,
secret_key=key)
# 更新session
def save_session(self, session, response):
"""Saves the session if it needs updates."""
if session is not None:
session.save_cookie(response, self.session_cookie_name)
def route(self, rule, **options):
"""A decorator that is used to register a view function for a
given URL rule.Example::
@app.route('/')
def index():
return 'Hello World'
"""
def decorator(f):
if 'endpoint' not in options:
options['endpoint'] = f.__name__
self.url_map.add(Rule(rule, **options))
self.view_functions[options['endpoint']] = f
return f
return decorator
def errorhandler(self, code):
"""A decorator that is used to register a function give a given
error code.Example::
@app.errorhandler(404)
def page_not_found():
return 'This page does not exist', 404
"""
def decorator(f):
# 注意code存储的是个函数引用
self.error_handlers[code] = f
# print(self.error_handlers)
return f
return decorator
def request_init(self, f):
"""Registers a function to run before each request."""
# 注意request_init_funcs存储的也是函数引用
self.request_init_funcs.append(f)
# print(self.request_init_funcs)
return f
def request_shutdown(self, f):
"""Register a function to be run after each request."""
self.request_shutdown_funcs.append(f)
return f
# 匹配错误的请求获取endpoint和args,为下面的请求调度使用,比如:自定义的404页面
def match_request(self):
"""Matches the current request against the URL map and also
stores the endpoint and view arguments on the request object
is successful, otherwise the exception is stored.
"""
rv = _request_ctx_stack.top.url_adapter.match()
request.endpoint, request.view_args = rv
return rv
# 如果endpointm values组合的URL存在就调用正常的页面处理,如果不存在就捕获异常代码
# 调用@errorhandler修饰器修饰的函数注意:self.error_handlers.get(e.code)返回是是个函数
def dispatch_request(self):
"""Does the request dispatching.Matches the URL and returns the
return value of the view or error handler.This does not have to
be a response object.In order to convert the return value to a
proper response object, call :func:`make_response`.
"""
try:
endpoint, values = self.match_request()
return self.view_functions[endpoint](**values)
except HTTPException, e:
handler = self.error_handlers.get(e.code)
if handler is None:
return e
return handler(e)
except Exception, e:
handler = self.error_handlers.get(500)
if self.debug or handler is None:
raise
return handler(e)
# 按照指定的类型返回,如果都没有就按照请求类型返回
def make_response(self, rv):
"""Converts the return value from a view function to a real
response object that is an instance of :attr:`response_class`.
"""
if isinstance(rv, self.response_class):
return rv
if isinstance(rv, basestring):
return self.response_class(rv)
if isinstance(rv, tuple):
return self.response_class(*rv)
return self.response_class.force_type(rv, request.environ)
# 预处理请求,在前面request_init_funcs数组中定义的初始化请求被优先执行
def preprocess_request(self):
for func in self.request_init_funcs:
rv = func()
if rv is not None:
return rv
# 在回复前处理
def process_response(self, response):
"""Can be overridden in order to modify the response object
before it's sent to the WSGI server.
"""
session = _request_ctx_stack.top.session
if session is not None:
self.save_session(session, response)
for handler in self.request_shutdown_funcs:
response = handler(response)
return response
def wsgi_app(self, environ, start_response):
"""The actual WSGI application.This is not implemented in
`__call__` so that middlewares can be applied:
app.wsgi_app = MyMiddleware(app.wsgi_app)
"""
_request_ctx_stack.push(_RequestContext(self, environ))
try:
# 执行预处理请求返回结果,如果结果是空就执行请求调度,该正常调用正常调用,该404就404,该500就500
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)
finally:
_request_ctx_stack.pop()
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`"""
return self.wsgi_app(environ, start_response)
# context locals 本地请求栈,就是说一个客户端发送的请求都放入该客户端的本地栈
_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)

分享给朋友:
您可能感兴趣的文章:
随机阅读: