Merge remote-tracking branch 'origin/2.0.x' into main
This commit is contained in:
commit
ba6db2e307
15 changed files with 118 additions and 43 deletions
|
|
@ -61,7 +61,6 @@ from .templating import Environment
|
|||
from .typing import AfterRequestCallable
|
||||
from .typing import BeforeFirstRequestCallable
|
||||
from .typing import BeforeRequestCallable
|
||||
from .typing import ErrorHandlerCallable
|
||||
from .typing import ResponseReturnValue
|
||||
from .typing import TeardownCallable
|
||||
from .typing import TemplateContextProcessorCallable
|
||||
|
|
@ -78,6 +77,7 @@ if t.TYPE_CHECKING:
|
|||
from .blueprints import Blueprint
|
||||
from .testing import FlaskClient
|
||||
from .testing import FlaskCliRunner
|
||||
from .typing import ErrorHandlerCallable
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
iscoroutinefunction = inspect.iscoroutinefunction
|
||||
|
|
@ -1268,7 +1268,9 @@ class Flask(Scaffold):
|
|||
self.shell_context_processors.append(f)
|
||||
return f
|
||||
|
||||
def _find_error_handler(self, e: Exception) -> t.Optional[ErrorHandlerCallable]:
|
||||
def _find_error_handler(
|
||||
self, e: Exception
|
||||
) -> t.Optional["ErrorHandlerCallable[Exception]"]:
|
||||
"""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 an exception class, app handler for an exception
|
||||
|
|
@ -1276,7 +1278,7 @@ class Flask(Scaffold):
|
|||
"""
|
||||
exc_class, code = self._get_exc_class_and_code(type(e))
|
||||
|
||||
for c in [code, None]:
|
||||
for c in [code, None] if code is not None else [None]:
|
||||
for name in chain(request.blueprints, [None]):
|
||||
handler_map = self.error_handler_spec[name][c]
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ from .scaffold import Scaffold
|
|||
from .typing import AfterRequestCallable
|
||||
from .typing import BeforeFirstRequestCallable
|
||||
from .typing import BeforeRequestCallable
|
||||
from .typing import ErrorHandlerCallable
|
||||
from .typing import TeardownCallable
|
||||
from .typing import TemplateContextProcessorCallable
|
||||
from .typing import TemplateFilterCallable
|
||||
|
|
@ -19,6 +18,7 @@ from .typing import URLValuePreprocessorCallable
|
|||
|
||||
if t.TYPE_CHECKING:
|
||||
from .app import Flask
|
||||
from .typing import ErrorHandlerCallable
|
||||
|
||||
DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable]
|
||||
|
||||
|
|
@ -293,7 +293,6 @@ class Blueprint(Scaffold):
|
|||
Registering the same blueprint with the same name multiple
|
||||
times is deprecated and will become an error in Flask 2.1.
|
||||
"""
|
||||
first_registration = not any(bp is self for bp in app.blueprints.values())
|
||||
name_prefix = options.get("name_prefix", "")
|
||||
self_name = options.get("name", self.name)
|
||||
name = f"{name_prefix}.{self_name}".lstrip(".")
|
||||
|
|
@ -318,9 +317,12 @@ class Blueprint(Scaffold):
|
|||
stacklevel=4,
|
||||
)
|
||||
|
||||
first_bp_registration = not any(bp is self for bp in app.blueprints.values())
|
||||
first_name_registration = name not in app.blueprints
|
||||
|
||||
app.blueprints[name] = self
|
||||
self._got_registered_once = True
|
||||
state = self.make_setup_state(app, options, first_registration)
|
||||
state = self.make_setup_state(app, options, first_bp_registration)
|
||||
|
||||
if self.has_static_folder:
|
||||
state.add_url_rule(
|
||||
|
|
@ -330,7 +332,7 @@ class Blueprint(Scaffold):
|
|||
)
|
||||
|
||||
# Merge blueprint data into parent.
|
||||
if first_registration:
|
||||
if first_bp_registration or first_name_registration:
|
||||
|
||||
def extend(bp_dict, parent_dict):
|
||||
for key, values in bp_dict.items():
|
||||
|
|
@ -581,7 +583,9 @@ class Blueprint(Scaffold):
|
|||
handler is used for all requests, even if outside of the blueprint.
|
||||
"""
|
||||
|
||||
def decorator(f: ErrorHandlerCallable) -> ErrorHandlerCallable:
|
||||
def decorator(
|
||||
f: "ErrorHandlerCallable[Exception]",
|
||||
) -> "ErrorHandlerCallable[Exception]":
|
||||
self.record_once(lambda s: s.app.errorhandler(code)(f))
|
||||
return f
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ from .templating import _default_template_ctx_processor
|
|||
from .typing import AfterRequestCallable
|
||||
from .typing import AppOrBlueprintKey
|
||||
from .typing import BeforeRequestCallable
|
||||
from .typing import ErrorHandlerCallable
|
||||
from .typing import GenericException
|
||||
from .typing import TeardownCallable
|
||||
from .typing import TemplateContextProcessorCallable
|
||||
from .typing import URLDefaultCallable
|
||||
|
|
@ -29,6 +29,7 @@ from .typing import URLValuePreprocessorCallable
|
|||
|
||||
if t.TYPE_CHECKING:
|
||||
from .wrappers import Response
|
||||
from .typing import ErrorHandlerCallable
|
||||
|
||||
# a singleton sentinel value for parameter defaults
|
||||
_sentinel = object()
|
||||
|
|
@ -144,7 +145,10 @@ class Scaffold:
|
|||
#: directly and its format may change at any time.
|
||||
self.error_handler_spec: t.Dict[
|
||||
AppOrBlueprintKey,
|
||||
t.Dict[t.Optional[int], t.Dict[t.Type[Exception], ErrorHandlerCallable]],
|
||||
t.Dict[
|
||||
t.Optional[int],
|
||||
t.Dict[t.Type[Exception], "ErrorHandlerCallable[Exception]"],
|
||||
],
|
||||
] = defaultdict(lambda: defaultdict(dict))
|
||||
|
||||
#: A data structure of functions to call at the beginning of
|
||||
|
|
@ -643,8 +647,11 @@ class Scaffold:
|
|||
|
||||
@setupmethod
|
||||
def errorhandler(
|
||||
self, code_or_exception: t.Union[t.Type[Exception], int]
|
||||
) -> t.Callable[[ErrorHandlerCallable], ErrorHandlerCallable]:
|
||||
self, code_or_exception: t.Union[t.Type[GenericException], int]
|
||||
) -> t.Callable[
|
||||
["ErrorHandlerCallable[GenericException]"],
|
||||
"ErrorHandlerCallable[GenericException]",
|
||||
]:
|
||||
"""Register a function to handle errors by code or exception class.
|
||||
|
||||
A decorator that is used to register a function given an
|
||||
|
|
@ -674,7 +681,9 @@ class Scaffold:
|
|||
an arbitrary exception
|
||||
"""
|
||||
|
||||
def decorator(f: ErrorHandlerCallable) -> ErrorHandlerCallable:
|
||||
def decorator(
|
||||
f: "ErrorHandlerCallable[GenericException]",
|
||||
) -> "ErrorHandlerCallable[GenericException]":
|
||||
self.register_error_handler(code_or_exception, f)
|
||||
return f
|
||||
|
||||
|
|
@ -683,8 +692,8 @@ class Scaffold:
|
|||
@setupmethod
|
||||
def register_error_handler(
|
||||
self,
|
||||
code_or_exception: t.Union[t.Type[Exception], int],
|
||||
f: ErrorHandlerCallable,
|
||||
code_or_exception: t.Union[t.Type[GenericException], int],
|
||||
f: "ErrorHandlerCallable[GenericException]",
|
||||
) -> None:
|
||||
"""Alternative error attach function to the :meth:`errorhandler`
|
||||
decorator that is more straightforward to use for non decorator
|
||||
|
|
@ -708,7 +717,9 @@ class Scaffold:
|
|||
" instead."
|
||||
)
|
||||
|
||||
self.error_handler_spec[None][code][exc_class] = f
|
||||
self.error_handler_spec[None][code][exc_class] = t.cast(
|
||||
"ErrorHandlerCallable[Exception]", f
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_exc_class_and_code(
|
||||
|
|
|
|||
|
|
@ -33,11 +33,12 @@ ResponseReturnValue = t.Union[
|
|||
"WSGIApplication",
|
||||
]
|
||||
|
||||
GenericException = t.TypeVar("GenericException", bound=Exception, contravariant=True)
|
||||
|
||||
AppOrBlueprintKey = t.Optional[str] # The App key is None, whereas blueprints are named
|
||||
AfterRequestCallable = t.Callable[["Response"], "Response"]
|
||||
BeforeFirstRequestCallable = t.Callable[[], None]
|
||||
BeforeRequestCallable = t.Callable[[], t.Optional[ResponseReturnValue]]
|
||||
ErrorHandlerCallable = t.Callable[[Exception], ResponseReturnValue]
|
||||
TeardownCallable = t.Callable[[t.Optional[BaseException]], None]
|
||||
TemplateContextProcessorCallable = t.Callable[[], t.Dict[str, t.Any]]
|
||||
TemplateFilterCallable = t.Callable[..., t.Any]
|
||||
|
|
@ -45,3 +46,11 @@ 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:
|
||||
...
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import typing as t
|
||||
|
||||
from .globals import current_app
|
||||
from .globals import request
|
||||
from .typing import ResponseReturnValue
|
||||
|
||||
|
|
@ -80,7 +81,7 @@ class View:
|
|||
|
||||
def view(*args: t.Any, **kwargs: t.Any) -> ResponseReturnValue:
|
||||
self = view.view_class(*class_args, **class_kwargs) # type: ignore
|
||||
return self.dispatch_request(*args, **kwargs)
|
||||
return current_app.ensure_sync(self.dispatch_request)(*args, **kwargs)
|
||||
|
||||
if cls.decorators:
|
||||
view.__name__ = name
|
||||
|
|
@ -154,4 +155,4 @@ class MethodView(View, metaclass=MethodViewType):
|
|||
meth = getattr(self, "get", None)
|
||||
|
||||
assert meth is not None, f"Unimplemented method {request.method!r}"
|
||||
return meth(*args, **kwargs)
|
||||
return current_app.ensure_sync(meth)(*args, **kwargs)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue