Merge branch '2.0.x'

This commit is contained in:
David Lord 2021-11-16 06:30:19 -08:00
commit 6b0c8cdac1
No known key found for this signature in database
GPG key ID: 7A1C87E3F5BC42A8
4 changed files with 29 additions and 20 deletions

View file

@ -39,6 +39,9 @@ Unreleased
removed in Werkzeug 2.1. It is now also deprecated in Flask, to be
removed in Flask 2.1, while remaining compatible with both in
2.0.x. Use ``response.request.environ`` instead. :pr:`4341`
- Fix type annotation for ``errorhandler`` decorator. :issue:`4295`
- Revert a change to the CLI that caused it to hide ``ImportError``
tracebacks when importing the application. :issue:`4307`
Version 2.0.2

View file

@ -222,15 +222,16 @@ def locate_app(module_name, app_name, raise_if_not_found=True):
try:
__import__(module_name)
except ImportError as e:
except ImportError:
# Reraise the ImportError if it occurred within the imported module.
# Determine this by checking whether the trace has a depth > 1.
if sys.exc_info()[2].tb_next:
raise NoAppException(
f"While importing {module_name!r}, an ImportError was raised."
) from e
f"While importing {module_name!r}, an ImportError was"
f" raised:\n\n{traceback.format_exc()}"
) from None
elif raise_if_not_found:
raise NoAppException(f"Could not import {module_name!r}.") from e
raise NoAppException(f"Could not import {module_name!r}.") from None
else:
return

View file

@ -131,6 +131,13 @@ class SessionInterface:
app = Flask(__name__)
app.session_interface = MySessionInterface()
Multiple requests with the same session may be sent and handled
concurrently. When implementing a new session interface, consider
whether reads or writes to the backing store must be synchronized.
There is no guarantee on the order in which the session for each
request is opened or saved, it will occur in the order that requests
begin and end processing.
.. versionadded:: 0.8
"""
@ -292,20 +299,25 @@ class SessionInterface:
def open_session(
self, app: "Flask", request: "Request"
) -> t.Optional[SessionMixin]:
"""This method has to be implemented and must either return ``None``
in case the loading failed because of a configuration error or an
instance of a session object which implements a dictionary like
interface + the methods and attributes on :class:`SessionMixin`.
"""This is called at the beginning of each request, after
pushing the request context, before matching the URL.
This must return an object which implements a dictionary-like
interface as well as the :class:`SessionMixin` interface.
This will return ``None`` to indicate that loading failed in
some way that is not immediately an error. The request
context will fall back to using :meth:`make_null_session`
in this case.
"""
raise NotImplementedError()
def save_session(
self, app: "Flask", session: SessionMixin, response: "Response"
) -> None:
"""This is called for actual sessions returned by :meth:`open_session`
at the end of the request. This is still called during a request
context so if you absolutely need access to the request you can do
that.
"""This is called at the end of each request, after generating
a response, before removing the request context. It is skipped
if :meth:`is_null_session` returns ``True``.
"""
raise NotImplementedError()

View file

@ -46,11 +46,4 @@ TemplateGlobalCallable = t.Callable[..., t.Any]
TemplateTestCallable = t.Callable[..., bool]
URLDefaultCallable = t.Callable[[str, dict], None]
URLValuePreprocessorCallable = t.Callable[[t.Optional[str], t.Optional[dict]], None]
if t.TYPE_CHECKING:
import typing_extensions as te
class ErrorHandlerCallable(te.Protocol[GenericException]):
def __call__(self, error: GenericException) -> ResponseReturnValue:
...
ErrorHandlerCallable = t.Callable[[GenericException], ResponseReturnValue]