relax errorhandler function arg type

This commit is contained in:
David Lord 2022-03-15 07:35:59 -07:00
parent 0ef1e65f6a
commit 8886328822
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
5 changed files with 19 additions and 27 deletions

View File

@ -47,6 +47,9 @@ Unreleased
loader thread. :issue:`4460` loader thread. :issue:`4460`
- Deleting the session cookie uses the ``httponly`` flag. - Deleting the session cookie uses the ``httponly`` flag.
:issue:`4485` :issue:`4485`
- Relax typing for ``errorhandler`` to allow the user to use more
precise types and decorate the same function multiple times.
:issue:`4095, 4295, 4297`
Version 2.0.3 Version 2.0.3

View File

@ -1265,9 +1265,7 @@ class Flask(Scaffold):
self.shell_context_processors.append(f) self.shell_context_processors.append(f)
return f return f
def _find_error_handler( def _find_error_handler(self, e: Exception) -> t.Optional["ErrorHandlerCallable"]:
self, e: Exception
) -> t.Optional["ErrorHandlerCallable[Exception]"]:
"""Return a registered error handler for an exception in this order: """Return a registered error handler for an exception in this order:
blueprint handler for a specific code, app handler for a specific code, blueprint handler for a specific code, app handler for a specific code,
blueprint handler for an exception class, app handler for an exception blueprint handler for an exception class, app handler for an exception

View File

@ -574,9 +574,7 @@ class Blueprint(Scaffold):
handler is used for all requests, even if outside of the blueprint. handler is used for all requests, even if outside of the blueprint.
""" """
def decorator( def decorator(f: "ErrorHandlerCallable") -> "ErrorHandlerCallable":
f: "ErrorHandlerCallable[Exception]",
) -> "ErrorHandlerCallable[Exception]":
self.record_once(lambda s: s.app.errorhandler(code)(f)) self.record_once(lambda s: s.app.errorhandler(code)(f))
return f return f

View File

@ -21,7 +21,6 @@ from .templating import _default_template_ctx_processor
from .typing import AfterRequestCallable from .typing import AfterRequestCallable
from .typing import AppOrBlueprintKey from .typing import AppOrBlueprintKey
from .typing import BeforeRequestCallable from .typing import BeforeRequestCallable
from .typing import GenericException
from .typing import TeardownCallable from .typing import TeardownCallable
from .typing import TemplateContextProcessorCallable from .typing import TemplateContextProcessorCallable
from .typing import URLDefaultCallable from .typing import URLDefaultCallable
@ -145,10 +144,7 @@ class Scaffold:
#: directly and its format may change at any time. #: directly and its format may change at any time.
self.error_handler_spec: t.Dict[ self.error_handler_spec: t.Dict[
AppOrBlueprintKey, AppOrBlueprintKey,
t.Dict[ t.Dict[t.Optional[int], t.Dict[t.Type[Exception], "ErrorHandlerCallable"]],
t.Optional[int],
t.Dict[t.Type[Exception], "ErrorHandlerCallable[Exception]"],
],
] = defaultdict(lambda: defaultdict(dict)) ] = defaultdict(lambda: defaultdict(dict))
#: A data structure of functions to call at the beginning of #: A data structure of functions to call at the beginning of
@ -652,11 +648,8 @@ class Scaffold:
@setupmethod @setupmethod
def errorhandler( def errorhandler(
self, code_or_exception: t.Union[t.Type[GenericException], int] self, code_or_exception: t.Union[t.Type[Exception], int]
) -> t.Callable[ ) -> t.Callable[["ErrorHandlerCallable"], "ErrorHandlerCallable"]:
["ErrorHandlerCallable[GenericException]"],
"ErrorHandlerCallable[GenericException]",
]:
"""Register a function to handle errors by code or exception class. """Register a function to handle errors by code or exception class.
A decorator that is used to register a function given an A decorator that is used to register a function given an
@ -686,9 +679,7 @@ class Scaffold:
an arbitrary exception an arbitrary exception
""" """
def decorator( def decorator(f: "ErrorHandlerCallable") -> "ErrorHandlerCallable":
f: "ErrorHandlerCallable[GenericException]",
) -> "ErrorHandlerCallable[GenericException]":
self.register_error_handler(code_or_exception, f) self.register_error_handler(code_or_exception, f)
return f return f
@ -697,8 +688,8 @@ class Scaffold:
@setupmethod @setupmethod
def register_error_handler( def register_error_handler(
self, self,
code_or_exception: t.Union[t.Type[GenericException], int], code_or_exception: t.Union[t.Type[Exception], int],
f: "ErrorHandlerCallable[GenericException]", f: "ErrorHandlerCallable",
) -> None: ) -> None:
"""Alternative error attach function to the :meth:`errorhandler` """Alternative error attach function to the :meth:`errorhandler`
decorator that is more straightforward to use for non decorator decorator that is more straightforward to use for non decorator
@ -722,9 +713,7 @@ class Scaffold:
" instead." " instead."
) from None ) from None
self.error_handler_spec[None][code][exc_class] = t.cast( self.error_handler_spec[None][code][exc_class] = f
"ErrorHandlerCallable[Exception]", f
)
@staticmethod @staticmethod
def _get_exc_class_and_code( def _get_exc_class_and_code(

View File

@ -33,8 +33,6 @@ ResponseReturnValue = t.Union[
"WSGIApplication", "WSGIApplication",
] ]
GenericException = t.TypeVar("GenericException", bound=Exception, contravariant=True)
AppOrBlueprintKey = t.Optional[str] # The App key is None, whereas blueprints are named AppOrBlueprintKey = t.Optional[str] # The App key is None, whereas blueprints are named
AfterRequestCallable = t.Callable[["Response"], "Response"] AfterRequestCallable = t.Callable[["Response"], "Response"]
BeforeFirstRequestCallable = t.Callable[[], None] BeforeFirstRequestCallable = t.Callable[[], None]
@ -46,4 +44,10 @@ TemplateGlobalCallable = t.Callable[..., t.Any]
TemplateTestCallable = t.Callable[..., bool] TemplateTestCallable = t.Callable[..., bool]
URLDefaultCallable = t.Callable[[str, dict], None] URLDefaultCallable = t.Callable[[str, dict], None]
URLValuePreprocessorCallable = t.Callable[[t.Optional[str], t.Optional[dict]], None] URLValuePreprocessorCallable = t.Callable[[t.Optional[str], t.Optional[dict]], None]
ErrorHandlerCallable = t.Callable[[GenericException], ResponseReturnValue] # This should take Exception, but that either breaks typing the argument
# with a specific exception, or decorating multiple times with different
# exceptions (and using a union type on the argument).
# https://github.com/pallets/flask/issues/4095
# https://github.com/pallets/flask/issues/4295
# https://github.com/pallets/flask/issues/4297
ErrorHandlerCallable = t.Callable[[t.Any], ResponseReturnValue]