Table of contents
Source series:
Flask source code articles: wsgi, Werkzeug and Flask startup workflow
Flask source code articles: Flask routing rules and request matching process
When talking about context in Flask, we need to discuss the following topics.
Understanding ThreadLocal
ThreadLocal
It is a global object based on thread isolation. Each thread can have its own variables without interfering with each other.
For ThreadLocal
details, please refer to the following:
https://blog.csdn.net/qq_43745578/article/details/129369272?spm=1001.2014.3001.5501
Local class
Local
werkzeug
Classes are located in local files under the Flask package. Its source code is as follows:
class Local:
__slots__ = ("_storage",)
def __init__(self) -> None:
object.__setattr__(self, "_storage", ContextVar("local_storage"))
@property
def __storage__(self) -> t.Dict[str, t.Any]:
warnings.warn(
"'__storage__' is deprecated and will be removed in Werkzeug 2.1.",
DeprecationWarning,
stacklevel=2,
)
return self._storage.get({
}) # type: ignore
@property
def __ident_func__(self) -> t.Callable[[], int]:
warnings.warn(
"'__ident_func__' is deprecated and will be removed in"
" Werkzeug 2.1. It should not be used in Python 3.7+.",
DeprecationWarning,
stacklevel=2,
)
return _get_ident # type: ignore
@__ident_func__.setter
def __ident_func__(self, func: t.Callable[[], int]) -> None:
warnings.warn(
"'__ident_func__' is deprecated and will be removed in"
" Werkzeug 2.1. Setting it no longer has any effect.",
DeprecationWarning,
stacklevel=2,
)
def __iter__(self) -> t.Iterator[t.Tuple[int, t.Any]]:
return iter(self._storage.get({
}).items())
def __call__(self, proxy: str) -> "LocalProxy":
"""Create a proxy for a name."""
return LocalProxy(self, proxy)
def __release_local__(self) -> None:
__release_local__(self._storage)
def __getattr__(self, name: str) -> t.Any:
values = self._storage.get({
})
try:
return values[name]
except KeyError:
raise AttributeError(name) from None
def __setattr__(self, name: str, value: t.Any) -> None:
values = self._storage.get({
}).copy()
values[name] = value
self._storage.set(values)
def __delattr__(self, name: str) -> None:
values = self._storage.get({
}).copy()
try:
del values[name]
self._storage.set(values)
except KeyError:
raise AttributeError(name) from None
(1) Analysis __init__
method
First look at the Local
initialization method of the class __init__
, the code is as follows:
def __init__(self) -> None:
object.__setattr__(self, "_storage", ContextVar("local_storage"))
The initialization method just sets a single underscore attribute for him _storage
, and the attribute value object is ContextVar
a class .
Its source code is as follows:
class ContextVar: # type: ignore
"""A fake ContextVar based on the previous greenlet/threading
ident function. Used on Python 3.6, eventlet, and old versions
of gevent.
"""
def __init__(self, _name: str) -> None:
self.storage: t.Dict[int, t.Dict[str, t.Any]] = {
}
def get(self, default: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]:
return self.storage.get(_get_ident(), default)
def set(self, value: t.Dict[str, t.Any]) -> None:
self.storage[_get_ident()] = value
You can see ContextVar
that the object of the class has get
two set
methods, and the variables of the thread are maintained in self.storage
the variables, self.storage
which is a dictionary.
As we said before, ThreadLocal
the thread id will be used as the key, and then the variable of the thread will be saved. The similarity here is that get
both set
methods 属性获取信息,且根据
return values from the ``storage_get_ident()` method as keys.
This _get_ident()
method is equivalent to getting the current thread id. It’s just a bit more advanced here. Look at the top of the local file and find _get_ident()
the import. The code is as follows:
try:
from greenlet import getcurrent as _get_ident
except ImportError:
from threading import get_ident as _get_ident
def get_ident() -> int:
warnings.warn(
"'get_ident' is deprecated and will be removed in Werkzeug"
" 2.1. Use 'greenlet.getcurrent' or 'threading.get_ident' for"
" previous behavior.",
DeprecationWarning,
stacklevel=2,
)
return _get_ident() # type: ignore
It can be seen that the local file greenlet
imports _get_ident
the method from the module first, and then uses the thread when it cannot be found, so Local
the high level is that it can be used not only in the thread, but also in the coroutine.
( 2) Analysis property setting, acquisition and deletion methods
According to the source code, we can see Local
that the class implements three methods for attribute setting ( __setattr__
), getting ( __getattr__
) and deleting ( __delattr__
).
And note that the above three methods actually call self._storage
the ContextVar
objects get
and set
methods to complete data maintenance.
LocalStack class
LocalStack
It is also located in the local file under the Flask werkzeug
package, which is the combination of the above Local and the stack. The source code is as follows:
class LocalStack:
def __init__(self) -> None:
self._local = Local()
def __release_local__(self) -> None:
self._local.__release_local__()
@property
def __ident_func__(self) -> t.Callable[[], int]:
return self._local.__ident_func__
@__ident_func__.setter
def __ident_func__(self, value: t.Callable[[], int]) -> None:
object.__setattr__(self._local, "__ident_func__", value)
def __call__(self) -> "LocalProxy":
def _lookup() -> t.Any:
rv = self.top
if rv is None:
raise RuntimeError("object unbound")
return rv
return LocalProxy(_lookup)
def push(self, obj: t.Any) -> t.List[t.Any]:
"""Pushes a new item to the stack"""
rv = getattr(self._local, "stack", []).copy()
rv.append(obj)
self._local.stack = rv
return rv # type: ignore
def pop(self) -> t.Any:
"""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) -> t.Any:
"""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
One of the private variables self._local = Local()
maintains an Local
object.
There are also 3 methods related to the stack: push, pop, top. You can see that the LocalStack class self._local
maintains a property in it stack
, and defaults to the first empty list. Then the three methods of push, pop, and top are to push an item from the top of the list, take an item from the top of the list, and get an item from the top of the list, and the list is still there.
So in fact, the data structure LocalStack
maintained inside the object self._local
is similar to the following:
self._local = {
"2491238714": {
# 线程获取协程id
"stack": [] # 栈信息
}
}
The most important thing is that LocalStack uses Local to maintain a stack (the name of the key is stack), so this stack is still isolated from threads or coroutines! That is, using LocalStack
classes can maintain a stack of its own for each thread or coroutine!
LocalProxy class
Before understanding LocalProxy, we can briefly understand the lower partial function:
Partial functions are actually functools
implemented using the partial method of the module. Its main usage scenario is to preset parameters for a function, see the following example:
from functools import partial
def my_func(name, age):
print(f'my_func:name-{
name},age-{
age}')
new_func = partial(my_func, 'ethan')
print(new_func)
# functools.partial(<function my_func at 0x7fb9c00e8040>, 'ethan')
new_func(18)
# my_func:name-ethan,age-18
It can be seen that when using the partial function, the first parameter is a function name or more broadly speaking, a callable object, and the second parameter is a preset parameter for the function passed in. In the above example, the name parameter of my_func is given The value of ethan is preset, and when new_func is called later, the name will always be ethan.
After understanding the partial function, let's look at it again LocalProxy
.
LocalProxy
It is mainly used for proxy Local
objects or LocalStack
objects, and is responsible for forwarding all operations on itself to internal Local
or LocalStack
objects.
In the above Local
class and LocalStack
the source code of the class, we can see that both classes have __call__
methods and both return LocalProxy
objects.
code show as below:
def __call__(self, proxy: str) -> "LocalProxy":
"""Create a proxy for a name."""
return LocalProxy(self, proxy)
The first parameter is Local
the object, and the second is the name to be proxied.
LocalProxy
The main source code is as follows (deleted):
class LocalProxy:
"""A proxy to the object bound to a :class:`Local`.
.. code-block:: python
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()
# a proxy to the session attribute of the request proxy
session = LocalProxy(lambda: request.session)
:param local: The :class:`Local` or callable that provides the
proxied object.
:param name: The attribute name to look up on a :class:`Local`. Not
used if a callable is given.
"""
__slots__ = ("__local", "__name", "__wrapped__")
def __init__(
self,
local: t.Union["Local", t.Callable[[], t.Any]],
name: t.Optional[str] = None,
) -> None:
object.__setattr__(self, "_LocalProxy__local", local)
object.__setattr__(self, "_LocalProxy__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) -> t.Any:
"""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__"): # type: ignore
return self.__local() # type: ignore
try:
# 这里主要是从local对象里拿药代理的对象数据
return getattr(self.__local, self.__name) # type: ignore
except AttributeError:
name = self.__name # type: ignore
raise RuntimeError(f"no object bound to {
name}") from None
# 以下是一些操作符定义,都转为_ProxyLookup对象了
__str__ = _ProxyLookup(str) # type: ignore
__bytes__ = _ProxyLookup(bytes)
__format__ = _ProxyLookup() # type: ignore
In the initialization method, two double-underlined properties and the sum object.__setattr__()
of the double underscore are set for the current object through the method , which are the name of the object and the agent to be proxied. In fact, these two properties are to set private properties for the current object for use inside the class._LocalProxy__local
_LocalProxy__name
Local
LocalProxy
The constructor has a callable parameter, which needs to return an Local
instance , and all subsequent attribute operations will be forwarded to the object returned by the callable.
LocalProxy
The _get_current_object()
method gets self._local
the object to be proxied corresponding to the current thread or coroutine. The main code is return getattr(self.__local, self.__name)
.
At the bottom of the source code, some underlined and underlined attributes are defined. When a class is used _ProxyLookup
, this class will not be expanded. Its main function is to call the method of the object LocalProxy
when operating on the object, so as to obtain the proxy object, and then dialing the object. Take the appropriate action.LocalProxy
_get_current_object()
From the above, LocalProxy
it seems that it has nothing to do with partial functions, so let's look at the following example:
from werkzeug.local import LocalStack, LocalProxy
from functools import partial
def my_proxy(name):
rv = ls.top
return rv[name]
# 创建一个LocalStack对象
ls = LocalStack()
# 调用push方法,则会在内部变量self._local里维护我们的数据
ls.push({
"request": {
"method": "GET", "path": "/"}})
# 创建一个LocalProxy对象,传了一个偏函数,设置name参数为request,则表示这个代理对象要代理request
# 执行my_proxy函数,并且name参数为request,则从LocalStack对象pop方法获取栈最顶的数据,再获取request
l_proxy = LocalProxy(partial(my_proxy, "request"))
# 看一下我们的代理对象
print(l_proxy)
# {'method': 'GET', 'path': '/'}
# 最后我们的代理对象就成了如上一个字典,那么直接对代理对象的操作就是代替对原对象的操作
print(l_proxy.get("method"))
# GET
It is clear from the comments that when used in combination with the partial function, the operation on the object LocalProxy
is realized instead of the operation on the proxy object.LocalProxy
In the context of Flask, such as the request context, in fact, the content of the above push is replaced by our Request
object from the dictionary. Explain in detail below.
context globals file
Several global variables are defined in advance in flask.globals
the module, which are the context objects in Flask. The source code is as follows:
import typing as t
from functools import partial
from werkzeug.local import LocalProxy
from werkzeug.local import LocalStack
if t.TYPE_CHECKING:
from .app import Flask
from .ctx import _AppCtxGlobals
from .sessions import SessionMixin
from .wrappers import Request
_request_ctx_err_msg = """\
Working outside of request context.
This typically means that you attempted to use functionality that needed
an active HTTP request. Consult the documentation on testing for
information about how to avoid this problem.\
"""
_app_ctx_err_msg = """\
Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.\
"""
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: "Flask" = LocalProxy(_find_app) # type: ignore
request: "Request" = LocalProxy(partial(_lookup_req_object, "request")) # type: ignore
session: "SessionMixin" = LocalProxy( # type: ignore
partial(_lookup_req_object, "session")
)
g: "_AppCtxGlobals" = LocalProxy(partial(_lookup_app_object, "g")) # type: ignore
_request_ctx_stack
First, two objects and are initialized _app_ctx_stack
, both of which are LocalStack
objects. As mentioned earlier, LocalStack
objects are Local
the combination of stack and can be used to store variables of different threads without interfering with each other.
Then LocalProxy
4 proxy objects are created by combining with the partial function:
current_app
_find_app
: The object is proxied through the function_app_ctx_stack.top
;request
: The object_lookup_req_object
proxied_request_ctx_stack.top
by the functionrequest
;session
: The object_lookup_req_object
proxied_request_ctx_stack.top
by the functionsession
;g
: The object_lookup_app_object
proxied_app_ctx_stack.top
by the functiong
;
The creation of these four is very similar to our example in the previous section. They are all used to proxy the LocalProxy
desired objects and complete the variable isolation between threads.
Among them request
, and session
belong to the request context and are related to the request, current_app
and g
belong to the application context and are related to the Flask app application.
request context
Now go back and look at what the request context is.
In flask, the view function needs to know the request information of its execution (request url, parameters, methods, etc.) and application information (database initialized in the application, etc.) in order to run correctly.
The most intuitive way is to encapsulate this information into an object and pass it to the view function as a parameter. But in this case, all view functions need to add corresponding parameters, even if it is not used inside the function.
Flask's approach is to use this information as something similar to global variables , which can be from flask import request
obtained . But the difference between these objects and global variables is that they must be dynamic, because in the case of multi-threads or multi-coroutines, each thread or coroutine gets its own unique object and will not interfere with each other.
This is what our Local
, LocalStack
, and LocalProxy
ask for cooperation to accomplish.
Now look back at the process of our request, you can review the following article: Flask source code: Flask routing rules and request matching process .
The relationship between the request process and the upper and lower levels is mainly reflected in the following steps:
(1)wsgi_app
As mentioned in the previous article, when a request comes in, it will go through wsgi_app
the method of the Falsk object, and then look at the source code.
Among them are only the main codes related to this analysis:
class Flask(Scaffold):
# 有几个类变量
# 请求对象类
request_class = Request
# 响应对象类
response_class = Response
# 配置类
config_class = Config
# 上篇文章分析过Rule类
url_rule_class = Rule
# 上篇文章分析过 Map类
url_map_class = Map
def __init__(
self,
import_name: str,
static_url_path: t.Optional[str] = None,
static_folder: t.Optional[t.Union[str, os.PathLike]] = "static",
static_host: t.Optional[str] = None,
host_matching: bool = False,
subdomain_matching: bool = False,
template_folder: t.Optional[str] = "templates",
instance_path: t.Optional[str] = None,
instance_relative_config: bool = False,
root_path: t.Optional[str] = None,
):
# 有几个成员变量
# 这里放被before_first_request装饰器装饰的函数
self.before_first_request_funcs: t.List[BeforeFirstRequestCallable] = []
# 初始化Map对象
self.url_map = self.url_map_class()
def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any:
# 这里初始化了一个RequestContext对象,上一章也分析过这个类
ctx = self.request_context(environ)
error: t.Optional[BaseException] = None
try:
try:
# 调用了push方法
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)
def request_context(self, environ: dict) -> RequestContext:
return RequestContext(self, environ)
def full_dispatch_request(self) -> Response:
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 dispatch_request(self) -> ResponseReturnValue:
req = _request_ctx_stack.top.request
if req.routing_exception is not None:
self.raise_routing_exception(req)
rule = req.url_rule
if (
getattr(rule, "provide_automatic_options", False)
and req.method == "OPTIONS"
):
return self.make_default_options_response()
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
See that the code first gets an RequestContext
object and then executes its push
methods. Let's analyze it in detail below.
(2)ctx.push()
Let's take a closer look at what this method does that is related to the context:
class RequestContext:
def __init__(
self,
app: "Flask",
environ: dict,
request: t.Optional["Request"] = None,
session: t.Optional["SessionMixin"] = None,
) -> None:
# Flask应用对象
self.app = app
# 这里是初始化request对象,调用了app.request_class方法,由上面的代码Flask源码看到就是实例化了Request对象
if request is None:
request = app.request_class(environ)
self.request = request
# 初始化路由分配对象
self.url_adapter = None
try:
self.url_adapter = app.create_url_adapter(self.request)
except HTTPException as e:
self.request.routing_exception = e
# 初始化session对象
self.session = session
def push(self) -> None:
# _request_ctx_stack局势我们的全局上下文变量(LocalStack对象)
# 先从栈顶拿一个元素
top = _request_ctx_stack.top
if top is not None and top.preserved:
top.pop(top._preserved_exc)
# 在把请求上下文压栈之前,必须保证有应用上下文,没有的话会初始化一个并执行其push方法
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)
# 执行请求上下文的push方法,传的参数是RequestContext对象自己,把自己压入到_request_ctx_stack的栈中了
_request_ctx_stack.push(self)
# 匹配请求,找到rule对象和参数对象并放到请求上下文的request属性中
if self.url_adapter is not None:
self.match_request()
One of the steps is _request_ctx_stack.push(self)
: execute the push method of the request context, the parameter passed is the RequestContext object itself, and push itself into the stack of _request_ctx_stack.
(3)app.full_dispatch_request
This method is ctx.push()
executed later, and its code is as follows:
def full_dispatch_request(self) -> Response:
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
rv = self.preprocess_request()
if rv is None:
# 执行dispatch_request方法
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
return self.finalize_request(rv)
The most important thing is to execute self.dispatch_request()
the method to get the response. Its code is as follows.
(4)app.dispatch_request
def dispatch_request(self) -> ResponseReturnValue:
# 从请求下文中拿到Request对象
req = _request_ctx_stack.top.request
if req.routing_exception is not None:
self.raise_routing_exception(req)
# 从请求下文中拿到Rule对象,这也是在ctx.push时放进请求下文中的
rule = req.url_rule
if (
getattr(rule, "provide_automatic_options", False)
and req.method == "OPTIONS"
):
return self.make_default_options_response()
# 找到匹配的路由函数并执行,返回结果
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
This method mainly does the following things:
- Get the Request object from the request context;
- Get the Rule object from the request context (this is also put into the request context when ctx.push);
- Find the matching routing function and execute it, and return the result.
So the request context also plays a very important role in our route matching.
(5) Use request context
The following example uses the request context in a view function:
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello_world():
# 当导入我们的request对象,就可以拿到当前请求线程的变量了
print(request)
return 'Hello World!'
if __name__ == '__main__':
app.run('127.0.0.1', 9000)
When we import our request
object in the view function, we can get the variables of the current request thread. Note that request
Flask globals
defines global variables in the file to proxy request objects, as mentioned above.
request: "Request" = LocalProxy(partial(_lookup_req_object, "request"))
(6)ctx.auto_pop(error)
At wsgi
the end of the method, when the request is processed and the response is obtained, this method is called to delete the contents of the stack to prevent overflow of stack data.
application context
(1) Source code analysis
In addition to the request context mentioned above, there is also an application context application context
.
The application context is mainly used to determine the application where the request is located and to store variables in the application.
globals
Define two objects _app_ctx_stack
and their proxy objects in the Flask global variable file current_app
. When there is still g
an object, it will be discussed in the next section.
Similar to the request context, the application context also uses Local
and LocalProxy
to complete the push and pop of data.
It's worth noting that the application context is request context
a proxy to the app in the context. Its role is mainly to help the request get the current application. In Flask, the application-related information is taken out separately to form an "application context" object. This object can be used together with the "request context", or it can be used separately.
I still remember that when I talked about the request up and down above, I said that before pushing the request context onto the stack, there must be an application context. If not, one will be initialized and its push
method will be executed. code show as below:
class RequestContext:
def push(self) -> None:
# 省略前面的...
# 这里在push之前保证有应用上下文,没有的话会初始化一个并执行其`push`方法
app_ctx = _app_ctx_stack.top
if app_ctx is None or app_ctx.app != self.app:
# 初始化一个app_ctx
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)
# 执行请求上下文栈的push方法
_request_ctx_stack.push(self)
The self.app.app_context()
method is used to return an application context object, the code is as follows:
def app_context(self) -> AppContext:
return AppContext(self)
AppContext
The source code is as follows:
class AppContext:
"""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: "Flask") -> None:
self.app = app
self.url_adapter = app.create_url_adapter(None)
self.g = app.app_ctx_globals_class()
# 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) -> None:
"""Binds the app context to the current context."""
self._refcnt += 1
_app_ctx_stack.push(self)
appcontext_pushed.send(self.app)
def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore
"""Pops the app context."""
try:
self._refcnt -= 1
if self._refcnt <= 0:
if exc is _sentinel:
exc = sys.exc_info()[1]
self.app.do_teardown_appcontext(exc)
finally:
rv = _app_ctx_stack.pop()
assert rv is self, f"Popped wrong app context. ({
rv!r} instead of {
self!r})"
appcontext_popped.send(self.app)
def __enter__(self) -> "AppContext":
self.push()
return self
def __exit__(
self, exc_type: type, exc_value: BaseException, tb: TracebackType
) -> None:
self.pop(exc_value)
It can be seen from the above code: "application context" is also a context object, and it also implements methods such as push
, pop
and so on.
self.g
is a global object that stores values. Then the application context is also stored LocalStack()
.
The constructor of the application context is also similar to the request context, and has attributes such as app
, url_adapter
and so on.
(2) Application context usage
Which application the current context is in can also be obtained by calling _request_ctx_stack.top.app
or .current_app
You can also use with
statements to construct a context, automatically execute push
the method after with, and automatically execute pop
the method after the end:
from flask import Flask
from flask.globals import current_app
app = Flask(__name__)
print(current_app)
print(current_app.name)
if __name__ == '__main__':
app.run(debug=True)
Start the program and find that the program can start successfully, but when executing the code in the with statement, an error will be reported:
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.
This is because the application context in the current main thread has not been pushed onto the stack. Then why can we use it directly in the view function? as follows:
from flask import Flask
from flask.globals import current_app
app = Flask(__name__)
@app.route('/')
def hello_world():
print(current_app)
return 'Hello World!'
if __name__ == '__main__':
app.run(debug=True)
This is because of what we analyzed above: before requesting the context, push
it will check whether there is an application context, and if it does not, it will automatically initialize the application context and call push
the method for us.
Then we have no request environment, such as offline files, there are two ways to solve the above problems:
(1) Manually call the push method
from flask import Flask
from flask.globals import current_app
app = Flask(__name__)
app.app_context().push()
print(current_app) # <Flask 'test'>:当前的app对象
print(current_app.name) # test:app的名字
if __name__ == '__main__':
app.run(debug=True)
(2) Use the with statement
After with, the automatic execution push
method helps us push the stack, and the automatic execution method after the end pop
:
from flask import Flask
from flask.globals import current_app
app = Flask(__name__)
with app.app_context():
print(current_app) # <Flask 'test'>
print(current_app.name) # test
if __name__ == '__main__':
app.run(debug=True)
(3) Why do you need an application context
Another question is, since there is already a request context, why do we need an application context?
This is because Flask supports multi-application, that is, multiple apps. To achieve app isolation, Flask uses the LocalStack data structure to record the context of each app.
When in the request context of an application, it is necessary to nest and process related operations of another application, how can the request find the "correct" application?
as follows:
First build the first app:
from flask import Flask
from flask.globals import current_app
app = Flask(__name__)
app.mysql_conn = "mysql1"
if __name__ == '__main__':
app.run(debug=True)
Start the application.
Then build the second app:
from flask import Flask
from flask.globals import current_app
from app_ctx_test1 import app
app2 = Flask("app2")
app2.mysql_conn = "mysql2"
@app2.route('/')
def hello_world():
print(current_app)
print(current_app.mysql_conn)
with app.app_context():
print(current_app)
print(current_app.mysql_conn)
return 'Hello World!'
if __name__ == '__main__':
app2.run(debug=True, port=8000)
After startup, the request comes in and prints as follows:
<Flask 'app2'> # 第二个应用
mysql2 # 第二个应用的mysql信息
<Flask 'app_ctx_test1'> # # 第一个应用
mysql1 # 第一个应用的mysql信息
It can be seen that the information of the first app can be obtained through the second app current_app
, which is the information isolation of the app.
So it is necessary to use the application context.
g object
g
Objects are also a type of application context. As you can see from the source code of the above application context, this is an Flask.app_ctx_globals_class
instance of default ctx._AppCtxGlobals
.
def _lookup_app_object(name):
top = _app_ctx_stack.top
if top is None:
raise RuntimeError(_app_ctx_err_msg)
return getattr(top, name)
g: "_AppCtxGlobals" = LocalProxy(partial(_lookup_app_object, "g"))
It can be seen that g
the object is used to represent _app_ctx_stack
the g attribute of the application context app in the proxy.
g
Objects are generally used to store resources during a request and act as an intermediary, through which some data is passed between multiple functions called by a request. This variable is reset on every request.
from flask import Flask, g
app = Flask(__name__)
def db_query():
user_id = g.user_id
user_name = g.user_name
print('user_id={} user_name={}'.format(user_id, user_name))
@app.route('/')
def get_user_profile():
g.user_id = 123
g.user_name = 'itcast'
db_query()
return 'hello world'
app.run()
reference:
https://cizixs.com/2017/01/13/flask-insight-context/
https://www.cnblogs.com/hq82/p/10451483.html
https://zhuanlan.zhihu.com/p/26097310
https://www.jianshu.com/p/7a7efbb7205f