From 46b39e2698697408c6f8f8693e526cd1bb085684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bojan=20Deli=C4=87?= Date: Thu, 7 Oct 2021 01:09:32 +0200 Subject: [PATCH 1/3] fix errorhandler type check --- CHANGES.rst | 1 + src/flask/typing.py | 9 +-------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 98139ae0..db65ee16 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,6 +9,7 @@ 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` Version 2.0.2 diff --git a/src/flask/typing.py b/src/flask/typing.py index f1c84670..93896f80 100644 --- a/src/flask/typing.py +++ b/src/flask/typing.py @@ -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] From e679a85b80df354f8632f8ab3e40135f16f5e6d0 Mon Sep 17 00:00:00 2001 From: Henry Kobin Date: Mon, 25 Oct 2021 11:18:48 -0700 Subject: [PATCH 2/3] made ImportError verbose in cli.py --- CHANGES.rst | 2 ++ src/flask/cli.py | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index db65ee16..05609229 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,8 @@ Unreleased 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 diff --git a/src/flask/cli.py b/src/flask/cli.py index 7ab4fa1c..8e215322 100644 --- a/src/flask/cli.py +++ b/src/flask/cli.py @@ -258,15 +258,16 @@ def locate_app(script_info, 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 From 282d8621dd4bdc3ee74e0d67b180e03f5f23899f Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 31 Oct 2021 20:51:16 +0000 Subject: [PATCH 3/3] document session lifetime and possible concurrency issues Co-authored-by: Evgeny Prigorodov --- src/flask/sessions.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/flask/sessions.py b/src/flask/sessions.py index 34b1d0ce..20648dea 100644 --- a/src/flask/sessions.py +++ b/src/flask/sessions.py @@ -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()