Cleanup implementation
This commit is contained in:
parent
8b970dd490
commit
38b4ccf2bc
290
sanic/app.py
290
sanic/app.py
|
@ -21,7 +21,6 @@ from functools import partial
|
||||||
from inspect import isawaitable
|
from inspect import isawaitable
|
||||||
from os import environ
|
from os import environ
|
||||||
from socket import socket
|
from socket import socket
|
||||||
from traceback import format_exc
|
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
|
@ -54,13 +53,8 @@ from sanic.blueprint_group import BlueprintGroup
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.compat import OS_IS_WINDOWS, enable_windows_color_support
|
from sanic.compat import OS_IS_WINDOWS, enable_windows_color_support
|
||||||
from sanic.config import SANIC_PREFIX, Config
|
from sanic.config import SANIC_PREFIX, Config
|
||||||
from sanic.exceptions import (
|
from sanic.exceptions import BadRequest, SanicException, URLBuildError
|
||||||
BadRequest,
|
from sanic.handlers import ErrorHandler
|
||||||
SanicException,
|
|
||||||
ServerError,
|
|
||||||
URLBuildError,
|
|
||||||
)
|
|
||||||
from sanic.handlers import ErrorHandler, RequestManager
|
|
||||||
from sanic.helpers import _default
|
from sanic.helpers import _default
|
||||||
from sanic.http import Stage
|
from sanic.http import Stage
|
||||||
from sanic.log import (
|
from sanic.log import (
|
||||||
|
@ -83,7 +77,7 @@ from sanic.models.futures import (
|
||||||
from sanic.models.handler_types import ListenerType, MiddlewareType
|
from sanic.models.handler_types import ListenerType, MiddlewareType
|
||||||
from sanic.models.handler_types import Sanic as SanicVar
|
from sanic.models.handler_types import Sanic as SanicVar
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
from sanic.response import BaseHTTPResponse, HTTPResponse, ResponseStream
|
from sanic.response import BaseHTTPResponse
|
||||||
from sanic.router import Router
|
from sanic.router import Router
|
||||||
from sanic.server.websockets.impl import ConnectionClosed
|
from sanic.server.websockets.impl import ConnectionClosed
|
||||||
from sanic.signals import Signal, SignalRouter
|
from sanic.signals import Signal, SignalRouter
|
||||||
|
@ -716,284 +710,8 @@ class Sanic(BaseSanic, StartupMixin, metaclass=TouchUpMeta):
|
||||||
): # no cov
|
): # no cov
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def _handle_exception(
|
|
||||||
self,
|
|
||||||
request: Request,
|
|
||||||
exception: BaseException,
|
|
||||||
run_middleware: bool = True,
|
|
||||||
): # no cov
|
|
||||||
"""
|
|
||||||
A handler that catches specific exceptions and outputs a response.
|
|
||||||
|
|
||||||
:param request: The current request object
|
|
||||||
:param exception: The exception that was raised
|
|
||||||
:raises ServerError: response 500
|
|
||||||
"""
|
|
||||||
response = None
|
|
||||||
await self.dispatch(
|
|
||||||
"http.lifecycle.exception",
|
|
||||||
inline=True,
|
|
||||||
context={"request": request, "exception": exception},
|
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
|
||||||
request.stream is not None
|
|
||||||
and request.stream.stage is not Stage.HANDLER
|
|
||||||
):
|
|
||||||
error_logger.exception(exception, exc_info=True)
|
|
||||||
logger.error(
|
|
||||||
"The error response will not be sent to the client for "
|
|
||||||
f'the following exception:"{exception}". A previous response '
|
|
||||||
"has at least partially been sent."
|
|
||||||
)
|
|
||||||
|
|
||||||
handler = self.error_handler._lookup(
|
|
||||||
exception, request.name if request else None
|
|
||||||
)
|
|
||||||
if handler:
|
|
||||||
logger.warning(
|
|
||||||
"An error occurred while handling the request after at "
|
|
||||||
"least some part of the response was sent to the client. "
|
|
||||||
"The response from your custom exception handler "
|
|
||||||
f"{handler.__name__} will not be sent to the client."
|
|
||||||
"Exception handlers should only be used to generate the "
|
|
||||||
"exception responses. If you would like to perform any "
|
|
||||||
"other action on a raised exception, consider using a "
|
|
||||||
"signal handler like "
|
|
||||||
'`@app.signal("http.lifecycle.exception")`\n'
|
|
||||||
"For further information, please see the docs: "
|
|
||||||
"https://sanicframework.org/en/guide/advanced/"
|
|
||||||
"signals.html",
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
# -------------------------------------------- #
|
|
||||||
# Request Middleware
|
|
||||||
# -------------------------------------------- #
|
|
||||||
if run_middleware:
|
|
||||||
middleware = (
|
|
||||||
request.route and request.route.extra.request_middleware
|
|
||||||
) or self.request_middleware
|
|
||||||
response = await self._run_request_middleware(request, middleware)
|
|
||||||
# No middleware results
|
|
||||||
if not response:
|
|
||||||
try:
|
|
||||||
response = self.error_handler.response(request, exception)
|
|
||||||
if isawaitable(response):
|
|
||||||
response = await response
|
|
||||||
except Exception as e:
|
|
||||||
if isinstance(e, SanicException):
|
|
||||||
response = self.error_handler.default(request, e)
|
|
||||||
elif self.debug:
|
|
||||||
response = HTTPResponse(
|
|
||||||
(
|
|
||||||
f"Error while handling error: {e}\n"
|
|
||||||
f"Stack: {format_exc()}"
|
|
||||||
),
|
|
||||||
status=500,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
response = HTTPResponse(
|
|
||||||
"An error occurred while handling an error", status=500
|
|
||||||
)
|
|
||||||
if response is not None:
|
|
||||||
try:
|
|
||||||
request.reset_response()
|
|
||||||
response = await request.respond(response)
|
|
||||||
except BaseException:
|
|
||||||
# Skip response middleware
|
|
||||||
if request.stream:
|
|
||||||
request.stream.respond(response)
|
|
||||||
await response.send(end_stream=True)
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
if request.stream:
|
|
||||||
response = request.stream.response
|
|
||||||
|
|
||||||
# Marked for cleanup and DRY with handle_request/handle_exception
|
|
||||||
# when ResponseStream is no longer supporder
|
|
||||||
if isinstance(response, BaseHTTPResponse):
|
|
||||||
await self.dispatch(
|
|
||||||
"http.lifecycle.response",
|
|
||||||
inline=True,
|
|
||||||
context={
|
|
||||||
"request": request,
|
|
||||||
"response": response,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
await response.send(end_stream=True)
|
|
||||||
elif isinstance(response, ResponseStream):
|
|
||||||
resp = await response(request)
|
|
||||||
await self.dispatch(
|
|
||||||
"http.lifecycle.response",
|
|
||||||
inline=True,
|
|
||||||
context={
|
|
||||||
"request": request,
|
|
||||||
"response": resp,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
await response.eof()
|
|
||||||
else:
|
|
||||||
raise ServerError(
|
|
||||||
f"Invalid response type {response!r} (need HTTPResponse)"
|
|
||||||
)
|
|
||||||
|
|
||||||
async def handle_request(self, request: Request): # no cov
|
async def handle_request(self, request: Request): # no cov
|
||||||
"""Take a request from the HTTP Server and return a response object
|
raise NotImplementedError
|
||||||
to be sent back The HTTP Server only expects a response object, so
|
|
||||||
exception handling must be done here
|
|
||||||
|
|
||||||
:param request: HTTP Request object
|
|
||||||
:return: Nothing
|
|
||||||
"""
|
|
||||||
|
|
||||||
async def _handle_request(self, request: Request): # no cov
|
|
||||||
await self.dispatch(
|
|
||||||
"http.lifecycle.handle",
|
|
||||||
inline=True,
|
|
||||||
context={"request": request},
|
|
||||||
)
|
|
||||||
|
|
||||||
# Define `response` var here to remove warnings about
|
|
||||||
# allocation before assignment below.
|
|
||||||
response: Optional[
|
|
||||||
Union[
|
|
||||||
BaseHTTPResponse,
|
|
||||||
Coroutine[Any, Any, Optional[BaseHTTPResponse]],
|
|
||||||
]
|
|
||||||
] = None
|
|
||||||
run_middleware = True
|
|
||||||
try:
|
|
||||||
|
|
||||||
await self.dispatch(
|
|
||||||
"http.routing.before",
|
|
||||||
inline=True,
|
|
||||||
context={"request": request},
|
|
||||||
)
|
|
||||||
# Fetch handler from router
|
|
||||||
route, handler, kwargs = self.router.get(
|
|
||||||
request.path,
|
|
||||||
request.method,
|
|
||||||
request.headers.getone("host", None),
|
|
||||||
)
|
|
||||||
|
|
||||||
request._match_info = {**kwargs}
|
|
||||||
request.route = route
|
|
||||||
|
|
||||||
await self.dispatch(
|
|
||||||
"http.routing.after",
|
|
||||||
inline=True,
|
|
||||||
context={
|
|
||||||
"request": request,
|
|
||||||
"route": route,
|
|
||||||
"kwargs": kwargs,
|
|
||||||
"handler": handler,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
|
||||||
request.stream
|
|
||||||
and request.stream.request_body
|
|
||||||
and not route.ctx.ignore_body
|
|
||||||
):
|
|
||||||
|
|
||||||
if hasattr(handler, "is_stream"):
|
|
||||||
# Streaming handler: lift the size limit
|
|
||||||
request.stream.request_max_size = float("inf")
|
|
||||||
else:
|
|
||||||
# Non-streaming handler: preload body
|
|
||||||
await request.receive_body()
|
|
||||||
|
|
||||||
# -------------------------------------------- #
|
|
||||||
# Request Middleware
|
|
||||||
# -------------------------------------------- #
|
|
||||||
run_middleware = False
|
|
||||||
if request.route.extra.request_middleware:
|
|
||||||
response = await self._run_request_middleware(
|
|
||||||
request, request.route.extra.request_middleware
|
|
||||||
)
|
|
||||||
|
|
||||||
# No middleware results
|
|
||||||
if not response:
|
|
||||||
# -------------------------------------------- #
|
|
||||||
# Execute Handler
|
|
||||||
# -------------------------------------------- #
|
|
||||||
|
|
||||||
if handler is None:
|
|
||||||
raise ServerError(
|
|
||||||
(
|
|
||||||
"'None' was returned while requesting a "
|
|
||||||
"handler from the router"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Run response handler
|
|
||||||
await self.dispatch(
|
|
||||||
"http.handler.before",
|
|
||||||
inline=True,
|
|
||||||
context={"request": request},
|
|
||||||
)
|
|
||||||
response = handler(request, **request.match_info)
|
|
||||||
if isawaitable(response):
|
|
||||||
response = await response
|
|
||||||
await self.dispatch(
|
|
||||||
"http.handler.after",
|
|
||||||
inline=True,
|
|
||||||
context={"request": request},
|
|
||||||
)
|
|
||||||
|
|
||||||
if request.responded:
|
|
||||||
if response is not None:
|
|
||||||
error_logger.error(
|
|
||||||
"The response object returned by the route handler "
|
|
||||||
"will not be sent to client. The request has already "
|
|
||||||
"been responded to."
|
|
||||||
)
|
|
||||||
if request.stream is not None:
|
|
||||||
response = request.stream.response
|
|
||||||
elif response is not None:
|
|
||||||
response = await request.respond(response) # type: ignore
|
|
||||||
elif not hasattr(handler, "is_websocket"):
|
|
||||||
response = request.stream.response # type: ignore
|
|
||||||
|
|
||||||
# Marked for cleanup and DRY with handle_request/handle_exception
|
|
||||||
# when ResponseStream is no longer supporder
|
|
||||||
if isinstance(response, BaseHTTPResponse):
|
|
||||||
await self.dispatch(
|
|
||||||
"http.lifecycle.response",
|
|
||||||
inline=True,
|
|
||||||
context={
|
|
||||||
"request": request,
|
|
||||||
"response": response,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
...
|
|
||||||
await response.send(end_stream=True)
|
|
||||||
elif isinstance(response, ResponseStream):
|
|
||||||
resp = await response(request) # type: ignore
|
|
||||||
await self.dispatch(
|
|
||||||
"http.lifecycle.response",
|
|
||||||
inline=True,
|
|
||||||
context={
|
|
||||||
"request": request,
|
|
||||||
"response": resp,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
await response.eof() # type: ignore
|
|
||||||
else:
|
|
||||||
if not hasattr(handler, "is_websocket"):
|
|
||||||
raise ServerError(
|
|
||||||
f"Invalid response type {response!r} "
|
|
||||||
"(need HTTPResponse)"
|
|
||||||
)
|
|
||||||
|
|
||||||
except CancelledError:
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
|
||||||
# Response Generation Failed
|
|
||||||
await self.handle_exception(
|
|
||||||
request, e, run_middleware=run_middleware
|
|
||||||
)
|
|
||||||
|
|
||||||
async def _websocket_handler(
|
async def _websocket_handler(
|
||||||
self, handler, request, *args, subprotocols=None, **kwargs
|
self, handler, request, *args, subprotocols=None, **kwargs
|
||||||
|
|
|
@ -20,11 +20,12 @@ from sanic.log import deprecation, error_logger, logger
|
||||||
from sanic.models.handler_types import RouteHandler
|
from sanic.models.handler_types import RouteHandler
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
from sanic.response import BaseHTTPResponse, HTTPResponse, ResponseStream, text
|
from sanic.response import BaseHTTPResponse, HTTPResponse, ResponseStream, text
|
||||||
|
from sanic.touchup import TouchUpMeta
|
||||||
|
|
||||||
|
|
||||||
class RequestHandler:
|
class RequestHandler:
|
||||||
def __init__(self, func, request_middleware, response_middleware):
|
def __init__(self, func, request_middleware, response_middleware):
|
||||||
self.func = func
|
self.func = func.func if isinstance(func, RequestHandler) else func
|
||||||
self.request_middleware = request_middleware
|
self.request_middleware = request_middleware
|
||||||
self.response_middleware = response_middleware
|
self.response_middleware = response_middleware
|
||||||
|
|
||||||
|
@ -32,7 +33,20 @@ class RequestHandler:
|
||||||
return self.func(*args, **kwargs)
|
return self.func(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RequestManager:
|
class RequestManager(metaclass=TouchUpMeta):
|
||||||
|
__touchup__ = (
|
||||||
|
"cleanup",
|
||||||
|
"run_request_middleware",
|
||||||
|
"run_response_middleware",
|
||||||
|
)
|
||||||
|
__slots__ = (
|
||||||
|
"handler",
|
||||||
|
"request_middleware_run",
|
||||||
|
"request_middleware",
|
||||||
|
"request",
|
||||||
|
"response_middleware_run",
|
||||||
|
"response_middleware",
|
||||||
|
)
|
||||||
request: Request
|
request: Request
|
||||||
|
|
||||||
def __init__(self, request: Request):
|
def __init__(self, request: Request):
|
||||||
|
@ -76,28 +90,35 @@ class RequestManager:
|
||||||
partial(self.handler, self.request, **self.request.match_info)
|
partial(self.handler, self.request, **self.request.match_info)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def lifecycle(self, handler):
|
async def lifecycle(self, handler, raise_exception: bool = False):
|
||||||
response: Optional[BaseHTTPResponse] = None
|
response: Optional[BaseHTTPResponse] = None
|
||||||
if not self.request_middleware_run and self.request_middleware:
|
if not self.request_middleware_run and self.request_middleware:
|
||||||
response = await self.run(self.run_request_middleware)
|
response = await self.run(
|
||||||
|
self.run_request_middleware, raise_exception
|
||||||
|
)
|
||||||
|
|
||||||
if not response:
|
if not response:
|
||||||
# Run response handler
|
# Run response handler
|
||||||
response = await self.run(handler)
|
response = await self.run(handler, raise_exception)
|
||||||
|
|
||||||
if not self.response_middleware_run and self.response_middleware:
|
if not self.response_middleware_run and self.response_middleware:
|
||||||
response = await self.run(
|
response = await self.run(
|
||||||
partial(self.run_response_middleware, response)
|
partial(self.run_response_middleware, response),
|
||||||
|
raise_exception,
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.cleanup(response)
|
await self.cleanup(response)
|
||||||
|
|
||||||
async def run(self, operation) -> Optional[BaseHTTPResponse]:
|
async def run(
|
||||||
|
self, operation, raise_exception: bool = False
|
||||||
|
) -> Optional[BaseHTTPResponse]:
|
||||||
try:
|
try:
|
||||||
response = operation()
|
response = operation()
|
||||||
if isawaitable(response):
|
if isawaitable(response):
|
||||||
response = await response
|
response = await response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
if raise_exception:
|
||||||
|
raise
|
||||||
response = await self.error(e)
|
response = await self.error(e)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -136,12 +157,9 @@ class RequestManager:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await self.lifecycle(
|
await self.lifecycle(
|
||||||
partial(error_handler.response, self.request, exception)
|
partial(error_handler.response, self.request, exception), True
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await self.lifecycle(
|
|
||||||
partial(error_handler.default, self.request, e)
|
|
||||||
)
|
|
||||||
if isinstance(e, SanicException):
|
if isinstance(e, SanicException):
|
||||||
response = error_handler.default(self.request, e)
|
response = error_handler.default(self.request, e)
|
||||||
elif self.request.app.debug:
|
elif self.request.app.debug:
|
||||||
|
@ -153,6 +171,7 @@ class RequestManager:
|
||||||
status=500,
|
status=500,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
error_logger.exception(e)
|
||||||
response = HTTPResponse(
|
response = HTTPResponse(
|
||||||
"An error occurred while handling an error", status=500
|
"An error occurred while handling an error", status=500
|
||||||
)
|
)
|
||||||
|
@ -175,30 +194,21 @@ class RequestManager:
|
||||||
elif not hasattr(self.handler, "is_websocket"):
|
elif not hasattr(self.handler, "is_websocket"):
|
||||||
response = self.request.stream.response # type: ignore
|
response = self.request.stream.response # type: ignore
|
||||||
|
|
||||||
# Marked for cleanup and DRY with handle_request/handle_exception
|
|
||||||
# when ResponseStream is no longer supporder
|
|
||||||
if isinstance(response, BaseHTTPResponse):
|
if isinstance(response, BaseHTTPResponse):
|
||||||
# await self.dispatch(
|
await self.request.app.dispatch(
|
||||||
# "http.lifecycle.response",
|
"http.lifecycle.response",
|
||||||
# inline=True,
|
inline=True,
|
||||||
# context={
|
context={"request": self.request, "response": response},
|
||||||
# "request": self.request,
|
)
|
||||||
# "response": response,
|
|
||||||
# },
|
|
||||||
# )
|
|
||||||
...
|
|
||||||
await response.send(end_stream=True)
|
await response.send(end_stream=True)
|
||||||
elif isinstance(response, ResponseStream):
|
elif isinstance(response, ResponseStream):
|
||||||
await response(self.request) # type: ignore
|
await response(self.request) # type: ignore
|
||||||
# await self.dispatch(
|
|
||||||
# "http.lifecycle.response",
|
|
||||||
# inline=True,
|
|
||||||
# context={
|
|
||||||
# "request": self.request,
|
|
||||||
# "response": resp,
|
|
||||||
# },
|
|
||||||
# )
|
|
||||||
await response.eof() # type: ignore
|
await response.eof() # type: ignore
|
||||||
|
await self.request.app.dispatch(
|
||||||
|
"http.lifecycle.response",
|
||||||
|
inline=True,
|
||||||
|
context={"request": self.request, "response": response},
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if not hasattr(self.handler, "is_websocket"):
|
if not hasattr(self.handler, "is_websocket"):
|
||||||
raise ServerError(
|
raise ServerError(
|
||||||
|
@ -219,27 +229,27 @@ class RequestManager:
|
||||||
self.request_middleware_run = True
|
self.request_middleware_run = True
|
||||||
|
|
||||||
for middleware in self.request_middleware:
|
for middleware in self.request_middleware:
|
||||||
# await self.dispatch(
|
await self.request.app.dispatch(
|
||||||
# "http.middleware.before",
|
"http.middleware.before",
|
||||||
# inline=True,
|
inline=True,
|
||||||
# context={
|
context={"request": self.request, "response": None},
|
||||||
# "request": request,
|
condition={"attach_to": "request"},
|
||||||
# "response": None,
|
)
|
||||||
# },
|
|
||||||
# condition={"attach_to": "request"},
|
|
||||||
# )
|
|
||||||
|
|
||||||
response = await self.run(partial(middleware, self.request))
|
try:
|
||||||
|
response = await self.run(partial(middleware, self.request))
|
||||||
|
except Exception:
|
||||||
|
error_logger.exception(
|
||||||
|
"Exception occurred in one of request middleware handlers"
|
||||||
|
)
|
||||||
|
raise
|
||||||
|
|
||||||
# await self.dispatch(
|
await self.request.app.dispatch(
|
||||||
# "http.middleware.after",
|
"http.middleware.after",
|
||||||
# inline=True,
|
inline=True,
|
||||||
# context={
|
context={"request": self.request, "response": None},
|
||||||
# "request": request,
|
condition={"attach_to": "request"},
|
||||||
# "response": None,
|
)
|
||||||
# },
|
|
||||||
# condition={"attach_to": "request"},
|
|
||||||
# )
|
|
||||||
|
|
||||||
if response:
|
if response:
|
||||||
return response
|
return response
|
||||||
|
@ -250,46 +260,34 @@ class RequestManager:
|
||||||
) -> BaseHTTPResponse:
|
) -> BaseHTTPResponse:
|
||||||
self.response_middleware_run = True
|
self.response_middleware_run = True
|
||||||
for middleware in self.response_middleware:
|
for middleware in self.response_middleware:
|
||||||
# await self.dispatch(
|
await self.request.app.dispatch(
|
||||||
# "http.middleware.before",
|
"http.middleware.before",
|
||||||
# inline=True,
|
inline=True,
|
||||||
# context={
|
context={"request": self.request, "response": None},
|
||||||
# "request": request,
|
condition={"attach_to": "request"},
|
||||||
# "response": None,
|
)
|
||||||
# },
|
|
||||||
# condition={"attach_to": "request"},
|
|
||||||
# )
|
|
||||||
|
|
||||||
resp = await self.run(partial(middleware, self.request, response))
|
try:
|
||||||
|
resp = await self.run(
|
||||||
|
partial(middleware, self.request, response), True
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
error_logger.exception(
|
||||||
|
"Exception occurred in one of response middleware handlers"
|
||||||
|
)
|
||||||
|
await self.error(e)
|
||||||
|
resp = None
|
||||||
|
|
||||||
# await self.dispatch(
|
await self.request.app.dispatch(
|
||||||
# "http.middleware.after",
|
"http.middleware.after",
|
||||||
# inline=True,
|
inline=True,
|
||||||
# context={
|
context={"request": self.request, "response": None},
|
||||||
# "request": request,
|
condition={"attach_to": "request"},
|
||||||
# "response": None,
|
)
|
||||||
# },
|
|
||||||
# condition={"attach_to": "request"},
|
|
||||||
# )
|
|
||||||
|
|
||||||
if resp:
|
if resp:
|
||||||
return resp
|
return resp
|
||||||
return response
|
return response
|
||||||
# try:
|
|
||||||
# middleware = (
|
|
||||||
# self.route and self.route.extra.response_middleware
|
|
||||||
# ) or self.app.response_middleware
|
|
||||||
# if middleware:
|
|
||||||
# response = await self.app._run_response_middleware(
|
|
||||||
# self, response, middleware
|
|
||||||
# )
|
|
||||||
# except CancelledErrors:
|
|
||||||
# raise
|
|
||||||
# except Exception:
|
|
||||||
# error_logger.exception(
|
|
||||||
# "Exception occurred in one of response middleware handlers"
|
|
||||||
# )
|
|
||||||
# return None
|
|
||||||
|
|
||||||
def resolve_route(self) -> Route:
|
def resolve_route(self) -> Route:
|
||||||
# Fetch handler from router
|
# Fetch handler from router
|
||||||
|
@ -303,11 +301,11 @@ class RequestManager:
|
||||||
self.request.route = route
|
self.request.route = route
|
||||||
self.handler = handler
|
self.handler = handler
|
||||||
|
|
||||||
if route.handler and route.handler.request_middleware:
|
if handler and handler.request_middleware:
|
||||||
self.request_middleware = route.handler.request_middleware
|
self.request_middleware = handler.request_middleware
|
||||||
|
|
||||||
if route.handler and route.handler.response_middleware:
|
if handler and handler.response_middleware:
|
||||||
self.response_middleware = route.handler.response_middleware
|
self.response_middleware = handler.response_middleware
|
||||||
|
|
||||||
return route
|
return route
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ class MiddlewareLocation(IntEnum):
|
||||||
|
|
||||||
|
|
||||||
class Middleware:
|
class Middleware:
|
||||||
counter = count()
|
_counter = count()
|
||||||
|
|
||||||
__slots__ = ("func", "priority", "location", "definition")
|
__slots__ = ("func", "priority", "location", "definition")
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class Middleware:
|
||||||
self.func = func
|
self.func = func
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
self.location = location
|
self.location = location
|
||||||
self.definition = next(Middleware.counter)
|
self.definition = next(Middleware._counter)
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
return self.func(*args, **kwargs)
|
return self.func(*args, **kwargs)
|
||||||
|
@ -60,3 +60,7 @@ class Middleware:
|
||||||
for middleware in collection
|
for middleware in collection
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def reset_count(cls):
|
||||||
|
cls._counter = count()
|
||||||
|
|
|
@ -13,6 +13,7 @@ from sanic_routing.route import Route
|
||||||
from sanic.constants import HTTP_METHODS
|
from sanic.constants import HTTP_METHODS
|
||||||
from sanic.errorpages import check_error_format
|
from sanic.errorpages import check_error_format
|
||||||
from sanic.exceptions import MethodNotAllowed, NotFound, SanicException
|
from sanic.exceptions import MethodNotAllowed, NotFound, SanicException
|
||||||
|
from sanic.handlers import RequestHandler
|
||||||
from sanic.models.handler_types import RouteHandler
|
from sanic.models.handler_types import RouteHandler
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,9 +32,11 @@ class Router(BaseRouter):
|
||||||
|
|
||||||
def _get(
|
def _get(
|
||||||
self, path: str, method: str, host: Optional[str]
|
self, path: str, method: str, host: Optional[str]
|
||||||
) -> Tuple[Route, RouteHandler, Dict[str, Any]]:
|
) -> Tuple[Route, RequestHandler, Dict[str, Any]]:
|
||||||
try:
|
try:
|
||||||
return self.resolve(
|
# We know this will always be RequestHandler, so we can ignore
|
||||||
|
# typing issue here
|
||||||
|
return self.resolve( # type: ignore
|
||||||
path=path,
|
path=path,
|
||||||
method=method,
|
method=method,
|
||||||
extra={"host": host} if host else None,
|
extra={"host": host} if host else None,
|
||||||
|
@ -50,7 +53,7 @@ class Router(BaseRouter):
|
||||||
@lru_cache(maxsize=ROUTER_CACHE_SIZE)
|
@lru_cache(maxsize=ROUTER_CACHE_SIZE)
|
||||||
def get( # type: ignore
|
def get( # type: ignore
|
||||||
self, path: str, method: str, host: Optional[str]
|
self, path: str, method: str, host: Optional[str]
|
||||||
) -> Tuple[Route, RouteHandler, Dict[str, Any]]:
|
) -> Tuple[Route, RequestHandler, Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Retrieve a `Route` object containing the details about how to handle
|
Retrieve a `Route` object containing the details about how to handle
|
||||||
a response for a given request
|
a response for a given request
|
||||||
|
@ -59,7 +62,7 @@ class Router(BaseRouter):
|
||||||
:type request: Request
|
:type request: Request
|
||||||
:return: details needed for handling the request and returning the
|
:return: details needed for handling the request and returning the
|
||||||
correct response
|
correct response
|
||||||
:rtype: Tuple[ Route, RouteHandler, Dict[str, Any]]
|
:rtype: Tuple[ Route, RequestHandler, Dict[str, Any]]
|
||||||
"""
|
"""
|
||||||
return self._get(path, method, host)
|
return self._get(path, method, host)
|
||||||
|
|
||||||
|
@ -114,7 +117,7 @@ class Router(BaseRouter):
|
||||||
|
|
||||||
params = dict(
|
params = dict(
|
||||||
path=uri,
|
path=uri,
|
||||||
handler=handler,
|
handler=RequestHandler(handler, [], []),
|
||||||
methods=frozenset(map(str, methods)) if methods else None,
|
methods=frozenset(map(str, methods)) if methods else None,
|
||||||
name=name,
|
name=name,
|
||||||
strict=strict_slashes,
|
strict=strict_slashes,
|
||||||
|
|
|
@ -4,12 +4,20 @@ from asyncio import CancelledError
|
||||||
from itertools import count
|
from itertools import count
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from sanic.exceptions import NotFound
|
from sanic.exceptions import NotFound
|
||||||
from sanic.middleware import Middleware, MiddlewareLocation
|
from sanic.middleware import Middleware
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
from sanic.response import HTTPResponse, json, text
|
from sanic.response import HTTPResponse, json, text
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def reset_middleware():
|
||||||
|
yield
|
||||||
|
Middleware.reset_count()
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
# GET
|
# GET
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
@ -185,7 +193,7 @@ def test_middleware_response_raise_exception(app, caplog):
|
||||||
with caplog.at_level(logging.ERROR):
|
with caplog.at_level(logging.ERROR):
|
||||||
reqrequest, response = app.test_client.get("/fail")
|
reqrequest, response = app.test_client.get("/fail")
|
||||||
|
|
||||||
assert response.status == 404
|
assert response.status == 500
|
||||||
# 404 errors are not logged
|
# 404 errors are not logged
|
||||||
assert (
|
assert (
|
||||||
"sanic.error",
|
"sanic.error",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user