forked from orbit-oss/flask
merge app and request context
This commit is contained in:
parent
330123258e
commit
c2705ffd9c
36 changed files with 779 additions and 1007 deletions
176
src/flask/app.py
176
src/flask/app.py
|
|
@ -29,20 +29,15 @@ from werkzeug.wsgi import get_host
|
|||
from . import cli
|
||||
from . import typing as ft
|
||||
from .ctx import AppContext
|
||||
from .ctx import RequestContext
|
||||
from .globals import _cv_app
|
||||
from .globals import _cv_request
|
||||
from .globals import current_app
|
||||
from .globals import g
|
||||
from .globals import request
|
||||
from .globals import request_ctx
|
||||
from .globals import session
|
||||
from .helpers import get_debug_flag
|
||||
from .helpers import get_flashed_messages
|
||||
from .helpers import get_load_dotenv
|
||||
from .helpers import send_from_directory
|
||||
from .sansio.app import App
|
||||
from .sansio.scaffold import _sentinel
|
||||
from .sessions import SecureCookieSessionInterface
|
||||
from .sessions import SessionInterface
|
||||
from .signals import appcontext_tearing_down
|
||||
|
|
@ -295,7 +290,7 @@ class Flask(App):
|
|||
|
||||
.. versionadded:: 0.9
|
||||
"""
|
||||
value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"]
|
||||
value = self.config["SEND_FILE_MAX_AGE_DEFAULT"]
|
||||
|
||||
if value is None:
|
||||
return None
|
||||
|
|
@ -517,8 +512,8 @@ class Flask(App):
|
|||
names: t.Iterable[str | None] = (None,)
|
||||
|
||||
# A template may be rendered outside a request context.
|
||||
if request:
|
||||
names = chain(names, reversed(request.blueprints))
|
||||
if (ctx := _cv_app.get(None)) is not None and ctx.has_request:
|
||||
names = chain(names, reversed(ctx.request.blueprints))
|
||||
|
||||
# The values passed to render_template take precedence. Keep a
|
||||
# copy to re-apply after all context functions.
|
||||
|
|
@ -886,7 +881,8 @@ class Flask(App):
|
|||
This no longer does the exception handling, this code was
|
||||
moved to the new :meth:`full_dispatch_request`.
|
||||
"""
|
||||
req = request_ctx.request
|
||||
req = _cv_app.get().request
|
||||
|
||||
if req.routing_exception is not None:
|
||||
self.raise_routing_exception(req)
|
||||
rule: Rule = req.url_rule # type: ignore[assignment]
|
||||
|
|
@ -957,7 +953,7 @@ class Flask(App):
|
|||
|
||||
.. versionadded:: 0.7
|
||||
"""
|
||||
adapter = request_ctx.url_adapter
|
||||
adapter = _cv_app.get().url_adapter
|
||||
methods = adapter.allowed_methods() # type: ignore[union-attr]
|
||||
rv = self.response_class()
|
||||
rv.allow.update(methods)
|
||||
|
|
@ -1057,11 +1053,9 @@ class Flask(App):
|
|||
.. versionadded:: 2.2
|
||||
Moved from ``flask.url_for``, which calls this method.
|
||||
"""
|
||||
req_ctx = _cv_request.get(None)
|
||||
|
||||
if req_ctx is not None:
|
||||
url_adapter = req_ctx.url_adapter
|
||||
blueprint_name = req_ctx.request.blueprint
|
||||
if (ctx := _cv_app.get(None)) is not None and ctx.has_request:
|
||||
url_adapter = ctx.url_adapter
|
||||
blueprint_name = ctx.request.blueprint
|
||||
|
||||
# If the endpoint starts with "." and the request matches a
|
||||
# blueprint, the endpoint is relative to the blueprint.
|
||||
|
|
@ -1076,13 +1070,11 @@ class Flask(App):
|
|||
if _external is None:
|
||||
_external = _scheme is not None
|
||||
else:
|
||||
app_ctx = _cv_app.get(None)
|
||||
|
||||
# If called by helpers.url_for, an app context is active,
|
||||
# use its url_adapter. Otherwise, app.url_for was called
|
||||
# directly, build an adapter.
|
||||
if app_ctx is not None:
|
||||
url_adapter = app_ctx.url_adapter
|
||||
if ctx is not None:
|
||||
url_adapter = ctx.url_adapter
|
||||
else:
|
||||
url_adapter = self.create_url_adapter(None)
|
||||
|
||||
|
|
@ -1278,12 +1270,13 @@ class Flask(App):
|
|||
value is handled as if it was the return value from the view, and
|
||||
further request handling is stopped.
|
||||
"""
|
||||
names = (None, *reversed(request.blueprints))
|
||||
req = _cv_app.get().request
|
||||
names = (None, *reversed(req.blueprints))
|
||||
|
||||
for name in names:
|
||||
if name in self.url_value_preprocessors:
|
||||
for url_func in self.url_value_preprocessors[name]:
|
||||
url_func(request.endpoint, request.view_args)
|
||||
url_func(req.endpoint, req.view_args)
|
||||
|
||||
for name in names:
|
||||
if name in self.before_request_funcs:
|
||||
|
|
@ -1308,12 +1301,12 @@ class Flask(App):
|
|||
:return: a new response object or the same, has to be an
|
||||
instance of :attr:`response_class`.
|
||||
"""
|
||||
ctx = request_ctx._get_current_object() # type: ignore[attr-defined]
|
||||
ctx = _cv_app.get()
|
||||
|
||||
for func in ctx._after_request_functions:
|
||||
response = self.ensure_sync(func)(response)
|
||||
|
||||
for name in chain(request.blueprints, (None,)):
|
||||
for name in chain(ctx.request.blueprints, (None,)):
|
||||
if name in self.after_request_funcs:
|
||||
for func in reversed(self.after_request_funcs[name]):
|
||||
response = self.ensure_sync(func)(response)
|
||||
|
|
@ -1323,77 +1316,57 @@ class Flask(App):
|
|||
|
||||
return response
|
||||
|
||||
def do_teardown_request(
|
||||
self,
|
||||
exc: BaseException | None = _sentinel, # type: ignore[assignment]
|
||||
) -> None:
|
||||
"""Called after the request is dispatched and the response is
|
||||
returned, right before the request context is popped.
|
||||
def do_teardown_request(self, exc: BaseException | None = None) -> None:
|
||||
"""Called after the request is dispatched and the response is finalized,
|
||||
right before the request context is popped. Called by
|
||||
:meth:`.AppContext.pop`.
|
||||
|
||||
This calls all functions decorated with
|
||||
:meth:`teardown_request`, and :meth:`Blueprint.teardown_request`
|
||||
if a blueprint handled the request. Finally, the
|
||||
:data:`request_tearing_down` signal is sent.
|
||||
This calls all functions decorated with :meth:`teardown_request`, and
|
||||
:meth:`Blueprint.teardown_request` if a blueprint handled the request.
|
||||
Finally, the :data:`request_tearing_down` signal is sent.
|
||||
|
||||
This is called by
|
||||
:meth:`RequestContext.pop() <flask.ctx.RequestContext.pop>`,
|
||||
which may be delayed during testing to maintain access to
|
||||
resources.
|
||||
|
||||
:param exc: An unhandled exception raised while dispatching the
|
||||
request. Detected from the current exception information if
|
||||
not passed. Passed to each teardown function.
|
||||
:param exc: An unhandled exception raised while dispatching the request.
|
||||
Passed to each teardown function.
|
||||
|
||||
.. versionchanged:: 0.9
|
||||
Added the ``exc`` argument.
|
||||
"""
|
||||
if exc is _sentinel:
|
||||
exc = sys.exc_info()[1]
|
||||
req = _cv_app.get().request
|
||||
|
||||
for name in chain(request.blueprints, (None,)):
|
||||
for name in chain(req.blueprints, (None,)):
|
||||
if name in self.teardown_request_funcs:
|
||||
for func in reversed(self.teardown_request_funcs[name]):
|
||||
self.ensure_sync(func)(exc)
|
||||
|
||||
request_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc)
|
||||
|
||||
def do_teardown_appcontext(
|
||||
self,
|
||||
exc: BaseException | None = _sentinel, # type: ignore[assignment]
|
||||
) -> None:
|
||||
"""Called right before the application context is popped.
|
||||
def do_teardown_appcontext(self, exc: BaseException | None = None) -> None:
|
||||
"""Called right before the application context is popped. Called by
|
||||
:meth:`.AppContext.pop`.
|
||||
|
||||
When handling a request, the application context is popped
|
||||
after the request context. See :meth:`do_teardown_request`.
|
||||
This calls all functions decorated with :meth:`teardown_appcontext`.
|
||||
Then the :data:`appcontext_tearing_down` signal is sent.
|
||||
|
||||
This calls all functions decorated with
|
||||
:meth:`teardown_appcontext`. Then the
|
||||
:data:`appcontext_tearing_down` signal is sent.
|
||||
|
||||
This is called by
|
||||
:meth:`AppContext.pop() <flask.ctx.AppContext.pop>`.
|
||||
:param exc: An unhandled exception raised while the context was active.
|
||||
Passed to each teardown function.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
"""
|
||||
if exc is _sentinel:
|
||||
exc = sys.exc_info()[1]
|
||||
|
||||
for func in reversed(self.teardown_appcontext_funcs):
|
||||
self.ensure_sync(func)(exc)
|
||||
|
||||
appcontext_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc)
|
||||
|
||||
def app_context(self) -> AppContext:
|
||||
"""Create an :class:`~flask.ctx.AppContext`. Use as a ``with``
|
||||
block to push the context, which will make :data:`current_app`
|
||||
point at this application.
|
||||
"""Create an :class:`.AppContext`. When the context is pushed,
|
||||
:data:`.current_app` and :data:`.g` become available.
|
||||
|
||||
An application context is automatically pushed by
|
||||
:meth:`RequestContext.push() <flask.ctx.RequestContext.push>`
|
||||
when handling a request, and when running a CLI command. Use
|
||||
this to manually create a context outside of these situations.
|
||||
A context is automatically pushed when handling each request, and when
|
||||
running any ``flask`` CLI command. Use this as a ``with`` block to
|
||||
manually push a context outside of those situations, such as during
|
||||
setup or testing.
|
||||
|
||||
::
|
||||
.. code-block:: python
|
||||
|
||||
with app.app_context():
|
||||
init_db()
|
||||
|
|
@ -1404,44 +1377,37 @@ class Flask(App):
|
|||
"""
|
||||
return AppContext(self)
|
||||
|
||||
def request_context(self, environ: WSGIEnvironment) -> RequestContext:
|
||||
"""Create a :class:`~flask.ctx.RequestContext` representing a
|
||||
WSGI environment. Use a ``with`` block to push the context,
|
||||
which will make :data:`request` point at this request.
|
||||
def request_context(self, environ: WSGIEnvironment) -> AppContext:
|
||||
"""Create an :class:`.AppContext` with request information representing
|
||||
the given WSGI environment. A context is automatically pushed when
|
||||
handling each request. When the context is pushed, :data:`.request`,
|
||||
:data:`.session`, :data:`g:, and :data:`.current_app` become available.
|
||||
|
||||
See :doc:`/reqcontext`.
|
||||
This method should not be used in your own code. Creating a valid WSGI
|
||||
environ is not trivial. Use :meth:`test_request_context` to correctly
|
||||
create a WSGI environ and request context instead.
|
||||
|
||||
Typically you should not call this from your own code. A request
|
||||
context is automatically pushed by the :meth:`wsgi_app` when
|
||||
handling a request. Use :meth:`test_request_context` to create
|
||||
an environment and context instead of this method.
|
||||
See :doc:`/appcontext`.
|
||||
|
||||
:param environ: a WSGI environment
|
||||
:param environ: A WSGI environment.
|
||||
"""
|
||||
return RequestContext(self, environ)
|
||||
return AppContext.from_environ(self, environ)
|
||||
|
||||
def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> RequestContext:
|
||||
"""Create a :class:`~flask.ctx.RequestContext` for a WSGI
|
||||
environment created from the given values. This is mostly useful
|
||||
during testing, where you may want to run a function that uses
|
||||
request data without dispatching a full request.
|
||||
def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> AppContext:
|
||||
"""Create an :class:`.AppContext` with request information created from
|
||||
the given arguments. When the context is pushed, :data:`.request`,
|
||||
:data:`.session`, :data:`g:, and :data:`.current_app` become available.
|
||||
|
||||
See :doc:`/reqcontext`.
|
||||
This is useful during testing to run a function that uses request data
|
||||
without dispatching a full request. Use this as a ``with`` block to push
|
||||
a context.
|
||||
|
||||
Use a ``with`` block to push the context, which will make
|
||||
:data:`request` point at the request for the created
|
||||
environment. ::
|
||||
.. code-block:: python
|
||||
|
||||
with app.test_request_context(...):
|
||||
generate_report()
|
||||
|
||||
When using the shell, it may be easier to push and pop the
|
||||
context manually to avoid indentation. ::
|
||||
|
||||
ctx = app.test_request_context(...)
|
||||
ctx.push()
|
||||
...
|
||||
ctx.pop()
|
||||
See :doc:`/appcontext`.
|
||||
|
||||
Takes the same arguments as Werkzeug's
|
||||
:class:`~werkzeug.test.EnvironBuilder`, with some defaults from
|
||||
|
|
@ -1451,20 +1417,18 @@ class Flask(App):
|
|||
:param path: URL path being requested.
|
||||
:param base_url: Base URL where the app is being served, which
|
||||
``path`` is relative to. If not given, built from
|
||||
:data:`PREFERRED_URL_SCHEME`, ``subdomain``,
|
||||
:data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`.
|
||||
:param subdomain: Subdomain name to append to
|
||||
:data:`SERVER_NAME`.
|
||||
:data:`PREFERRED_URL_SCHEME`, ``subdomain``, :data:`SERVER_NAME`,
|
||||
and :data:`APPLICATION_ROOT`.
|
||||
:param subdomain: Subdomain name to prepend to :data:`SERVER_NAME`.
|
||||
:param url_scheme: Scheme to use instead of
|
||||
:data:`PREFERRED_URL_SCHEME`.
|
||||
:param data: The request body, either as a string or a dict of
|
||||
form keys and values.
|
||||
:param data: The request body text or bytes,or a dict of form data.
|
||||
:param json: If given, this is serialized as JSON and passed as
|
||||
``data``. Also defaults ``content_type`` to
|
||||
``application/json``.
|
||||
:param args: other positional arguments passed to
|
||||
:param args: Other positional arguments passed to
|
||||
:class:`~werkzeug.test.EnvironBuilder`.
|
||||
:param kwargs: other keyword arguments passed to
|
||||
:param kwargs: Other keyword arguments passed to
|
||||
:class:`~werkzeug.test.EnvironBuilder`.
|
||||
"""
|
||||
from .testing import EnvironBuilder
|
||||
|
|
@ -1472,10 +1436,12 @@ class Flask(App):
|
|||
builder = EnvironBuilder(self, *args, **kwargs)
|
||||
|
||||
try:
|
||||
return self.request_context(builder.get_environ())
|
||||
environ = builder.get_environ()
|
||||
finally:
|
||||
builder.close()
|
||||
|
||||
return self.request_context(environ)
|
||||
|
||||
def wsgi_app(
|
||||
self, environ: WSGIEnvironment, start_response: StartResponse
|
||||
) -> cabc.Iterable[bytes]:
|
||||
|
|
@ -1496,7 +1462,6 @@ class Flask(App):
|
|||
Teardown events for the request and app contexts are called
|
||||
even if an unhandled error occurs. Other events may not be
|
||||
called depending on when an error occurs during dispatch.
|
||||
See :ref:`callbacks-and-errors`.
|
||||
|
||||
:param environ: A WSGI environment.
|
||||
:param start_response: A callable accepting a status code,
|
||||
|
|
@ -1519,7 +1484,6 @@ class Flask(App):
|
|||
finally:
|
||||
if "werkzeug.debug.preserve_context" in environ:
|
||||
environ["werkzeug.debug.preserve_context"](_cv_app.get())
|
||||
environ["werkzeug.debug.preserve_context"](_cv_request.get())
|
||||
|
||||
if error is not None and self.should_ignore_error(error):
|
||||
error = None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue