Add error format commit and merge conflicts
This commit is contained in:
parent
5e12edbc38
commit
71d845786d
20
sanic/app.py
20
sanic/app.py
|
@ -173,18 +173,16 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
self.asgi = False
|
self.asgi = False
|
||||||
self.auto_reload = False
|
self.auto_reload = False
|
||||||
self.blueprints: Dict[str, Blueprint] = {}
|
self.blueprints: Dict[str, Blueprint] = {}
|
||||||
self.config = config or Config(
|
self.config: Config = config or Config(
|
||||||
load_env=load_env, env_prefix=env_prefix
|
load_env=load_env,
|
||||||
|
env_prefix=env_prefix,
|
||||||
|
app=self,
|
||||||
)
|
)
|
||||||
self.configure_logging = configure_logging
|
self.configure_logging: bool = configure_logging
|
||||||
self.ctx = ctx or SimpleNamespace()
|
self.ctx: Any = ctx or SimpleNamespace()
|
||||||
self.debug = None
|
self.debug = False
|
||||||
self.error_handler = error_handler or ErrorHandler(
|
self.error_handler: ErrorHandler = error_handler or ErrorHandler()
|
||||||
fallback=self.config.FALLBACK_ERROR_FORMAT,
|
self.listeners: Dict[str, List[ListenerType[Any]]] = defaultdict(list)
|
||||||
)
|
|
||||||
self.is_running = False
|
|
||||||
self.is_stopping = False
|
|
||||||
self.listeners: Dict[str, List[ListenerType]] = defaultdict(list)
|
|
||||||
self.named_request_middleware: Dict[str, Deque[MiddlewareType]] = {}
|
self.named_request_middleware: Dict[str, Deque[MiddlewareType]] = {}
|
||||||
self.named_response_middleware: Dict[str, Deque[MiddlewareType]] = {}
|
self.named_response_middleware: Dict[str, Deque[MiddlewareType]] = {}
|
||||||
self.reload_dirs: Set[Path] = set()
|
self.reload_dirs: Set[Path] = set()
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from inspect import isclass
|
from inspect import isclass
|
||||||
from os import environ
|
from os import environ
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Optional, Union
|
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
from sanic.errorpages import check_error_format
|
from sanic.errorpages import check_error_format
|
||||||
|
@ -10,6 +12,10 @@ from sanic.http import Http
|
||||||
from .utils import load_module_from_file_location, str_to_bool
|
from .utils import load_module_from_file_location, str_to_bool
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING: # no cov
|
||||||
|
from sanic import Sanic
|
||||||
|
|
||||||
|
|
||||||
SANIC_PREFIX = "SANIC_"
|
SANIC_PREFIX = "SANIC_"
|
||||||
BASE_LOGO = """
|
BASE_LOGO = """
|
||||||
|
|
||||||
|
@ -71,11 +77,14 @@ class Config(dict):
|
||||||
load_env: Optional[Union[bool, str]] = True,
|
load_env: Optional[Union[bool, str]] = True,
|
||||||
env_prefix: Optional[str] = SANIC_PREFIX,
|
env_prefix: Optional[str] = SANIC_PREFIX,
|
||||||
keep_alive: Optional[bool] = None,
|
keep_alive: Optional[bool] = None,
|
||||||
|
*,
|
||||||
|
app: Optional[Sanic] = None,
|
||||||
):
|
):
|
||||||
defaults = defaults or {}
|
defaults = defaults or {}
|
||||||
super().__init__({**DEFAULT_CONFIG, **defaults})
|
super().__init__({**DEFAULT_CONFIG, **defaults})
|
||||||
|
|
||||||
self.LOGO = BASE_LOGO
|
self._app = app
|
||||||
|
self._LOGO = ""
|
||||||
|
|
||||||
if keep_alive is not None:
|
if keep_alive is not None:
|
||||||
self.KEEP_ALIVE = keep_alive
|
self.KEEP_ALIVE = keep_alive
|
||||||
|
@ -97,6 +106,7 @@ class Config(dict):
|
||||||
|
|
||||||
self._configure_header_size()
|
self._configure_header_size()
|
||||||
self._check_error_format()
|
self._check_error_format()
|
||||||
|
self._init = True
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
try:
|
try:
|
||||||
|
@ -104,8 +114,20 @@ class Config(dict):
|
||||||
except KeyError as ke:
|
except KeyError as ke:
|
||||||
raise AttributeError(f"Config has no '{ke.args[0]}'")
|
raise AttributeError(f"Config has no '{ke.args[0]}'")
|
||||||
|
|
||||||
def __setattr__(self, attr, value):
|
def __setattr__(self, attr, value) -> None:
|
||||||
self[attr] = value
|
self.update({attr: value})
|
||||||
|
|
||||||
|
def __setitem__(self, attr, value) -> None:
|
||||||
|
self.update({attr: value})
|
||||||
|
|
||||||
|
def update(self, *other, **kwargs) -> None:
|
||||||
|
other_mapping = {k: v for item in other for k, v in dict(item).items()}
|
||||||
|
super().update(*other, **kwargs)
|
||||||
|
for attr, value in {**other_mapping, **kwargs}.items():
|
||||||
|
self._post_set(attr, value)
|
||||||
|
|
||||||
|
def _post_set(self, attr, value) -> None:
|
||||||
|
if self.get("_init"):
|
||||||
if attr in (
|
if attr in (
|
||||||
"REQUEST_MAX_HEADER_SIZE",
|
"REQUEST_MAX_HEADER_SIZE",
|
||||||
"REQUEST_BUFFER_SIZE",
|
"REQUEST_BUFFER_SIZE",
|
||||||
|
@ -114,6 +136,29 @@ class Config(dict):
|
||||||
self._configure_header_size()
|
self._configure_header_size()
|
||||||
elif attr == "FALLBACK_ERROR_FORMAT":
|
elif attr == "FALLBACK_ERROR_FORMAT":
|
||||||
self._check_error_format()
|
self._check_error_format()
|
||||||
|
if self.app and value != self.app.error_handler.fallback:
|
||||||
|
if self.app.error_handler.fallback != "auto":
|
||||||
|
warn(
|
||||||
|
"Overriding non-default ErrorHandler fallback "
|
||||||
|
"value. Changing from "
|
||||||
|
f"{self.app.error_handler.fallback} to {value}."
|
||||||
|
)
|
||||||
|
self.app.error_handler.fallback = value
|
||||||
|
elif attr == "LOGO":
|
||||||
|
self._LOGO = value
|
||||||
|
warn(
|
||||||
|
"Setting the config.LOGO is deprecated and will no longer "
|
||||||
|
"be supported starting in v22.6.",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def app(self):
|
||||||
|
return self._app
|
||||||
|
|
||||||
|
@property
|
||||||
|
def LOGO(self):
|
||||||
|
return self._LOGO
|
||||||
|
|
||||||
def _configure_header_size(self):
|
def _configure_header_size(self):
|
||||||
Http.set_header_max_size(
|
Http.set_header_max_size(
|
||||||
|
|
|
@ -383,6 +383,7 @@ def exception_response(
|
||||||
"""
|
"""
|
||||||
content_type = None
|
content_type = None
|
||||||
|
|
||||||
|
print("exception_response", fallback)
|
||||||
if not renderer:
|
if not renderer:
|
||||||
# Make sure we have something set
|
# Make sure we have something set
|
||||||
renderer = base
|
renderer = base
|
||||||
|
@ -393,6 +394,7 @@ def exception_response(
|
||||||
# from the route
|
# from the route
|
||||||
if request.route:
|
if request.route:
|
||||||
try:
|
try:
|
||||||
|
if request.route.ctx.error_format:
|
||||||
render_format = request.route.ctx.error_format
|
render_format = request.route.ctx.error_format
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
...
|
...
|
||||||
|
|
|
@ -918,7 +918,7 @@ class RouteMixin:
|
||||||
|
|
||||||
return route
|
return route
|
||||||
|
|
||||||
def _determine_error_format(self, handler) -> str:
|
def _determine_error_format(self, handler) -> Optional[str]:
|
||||||
if not isinstance(handler, CompositionView):
|
if not isinstance(handler, CompositionView):
|
||||||
try:
|
try:
|
||||||
src = dedent(getsource(handler))
|
src = dedent(getsource(handler))
|
||||||
|
@ -930,7 +930,7 @@ class RouteMixin:
|
||||||
except (OSError, TypeError):
|
except (OSError, TypeError):
|
||||||
...
|
...
|
||||||
|
|
||||||
return "auto"
|
return None
|
||||||
|
|
||||||
def _get_response_types(self, node):
|
def _get_response_types(self, node):
|
||||||
types = set()
|
types = set()
|
||||||
|
|
|
@ -139,10 +139,9 @@ class Router(BaseRouter):
|
||||||
route.ctx.stream = stream
|
route.ctx.stream = stream
|
||||||
route.ctx.hosts = hosts
|
route.ctx.hosts = hosts
|
||||||
route.ctx.static = static
|
route.ctx.static = static
|
||||||
route.ctx.error_format = (
|
route.ctx.error_format = error_format
|
||||||
error_format or self.ctx.app.config.FALLBACK_ERROR_FORMAT
|
|
||||||
)
|
|
||||||
|
|
||||||
|
if error_format:
|
||||||
check_error_format(route.ctx.error_format)
|
check_error_format(route.ctx.error_format)
|
||||||
|
|
||||||
routes.append(route)
|
routes.append(route)
|
||||||
|
|
|
@ -3,6 +3,7 @@ from os import environ
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
from unittest.mock import Mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -350,3 +351,40 @@ def test_update_from_lowercase_key(app):
|
||||||
d = {"test_setting_value": 1}
|
d = {"test_setting_value": 1}
|
||||||
app.update_config(d)
|
app.update_config(d)
|
||||||
assert "test_setting_value" not in app.config
|
assert "test_setting_value" not in app.config
|
||||||
|
|
||||||
|
|
||||||
|
def test_deprecation_notice_when_setting_logo(app):
|
||||||
|
message = (
|
||||||
|
"Setting the config.LOGO is deprecated and will no longer be "
|
||||||
|
"supported starting in v22.6."
|
||||||
|
)
|
||||||
|
with pytest.warns(DeprecationWarning, match=message):
|
||||||
|
app.config.LOGO = "My Custom Logo"
|
||||||
|
|
||||||
|
|
||||||
|
def test_config_set_methods(app, monkeypatch):
|
||||||
|
post_set = Mock()
|
||||||
|
monkeypatch.setattr(Config, "_post_set", post_set)
|
||||||
|
|
||||||
|
app.config.FOO = 1
|
||||||
|
post_set.assert_called_once_with("FOO", 1)
|
||||||
|
post_set.reset_mock()
|
||||||
|
|
||||||
|
app.config["FOO"] = 2
|
||||||
|
post_set.assert_called_once_with("FOO", 2)
|
||||||
|
post_set.reset_mock()
|
||||||
|
|
||||||
|
app.config.update({"FOO": 3})
|
||||||
|
post_set.assert_called_once_with("FOO", 3)
|
||||||
|
post_set.reset_mock()
|
||||||
|
|
||||||
|
app.config.update([("FOO", 4)])
|
||||||
|
post_set.assert_called_once_with("FOO", 4)
|
||||||
|
post_set.reset_mock()
|
||||||
|
|
||||||
|
app.config.update(FOO=5)
|
||||||
|
post_set.assert_called_once_with("FOO", 5)
|
||||||
|
post_set.reset_mock()
|
||||||
|
|
||||||
|
app.config.update_config({"FOO": 6})
|
||||||
|
post_set.assert_called_once_with("FOO", 6)
|
||||||
|
|
|
@ -3,6 +3,7 @@ import pytest
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.errorpages import HTMLRenderer, exception_response
|
from sanic.errorpages import HTMLRenderer, exception_response
|
||||||
from sanic.exceptions import NotFound, SanicException
|
from sanic.exceptions import NotFound, SanicException
|
||||||
|
from sanic.handlers import ErrorHandler
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
from sanic.response import HTTPResponse, html, json, text
|
from sanic.response import HTTPResponse, html, json, text
|
||||||
|
|
||||||
|
@ -271,3 +272,44 @@ def test_combinations_for_auto(fake_request, accept, content_type, expected):
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.content_type == expected
|
assert response.content_type == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_allow_fallback_error_format_set_main_process_start(app):
|
||||||
|
@app.main_process_start
|
||||||
|
async def start(app, _):
|
||||||
|
app.config.FALLBACK_ERROR_FORMAT = "text"
|
||||||
|
|
||||||
|
request, response = app.test_client.get("/error")
|
||||||
|
assert request.app.error_handler.fallback == "text"
|
||||||
|
assert response.status == 500
|
||||||
|
assert response.content_type == "text/plain; charset=utf-8"
|
||||||
|
|
||||||
|
|
||||||
|
def test_setting_fallback_to_non_default_raise_warning(app):
|
||||||
|
app.error_handler = ErrorHandler(fallback="text")
|
||||||
|
|
||||||
|
assert app.error_handler.fallback == "text"
|
||||||
|
|
||||||
|
with pytest.warns(
|
||||||
|
UserWarning,
|
||||||
|
match=(
|
||||||
|
"Overriding non-default ErrorHandler fallback value. "
|
||||||
|
"Changing from text to auto."
|
||||||
|
),
|
||||||
|
):
|
||||||
|
app.config.FALLBACK_ERROR_FORMAT = "auto"
|
||||||
|
|
||||||
|
assert app.error_handler.fallback == "auto"
|
||||||
|
|
||||||
|
app.config.FALLBACK_ERROR_FORMAT = "text"
|
||||||
|
|
||||||
|
with pytest.warns(
|
||||||
|
UserWarning,
|
||||||
|
match=(
|
||||||
|
"Overriding non-default ErrorHandler fallback value. "
|
||||||
|
"Changing from text to json."
|
||||||
|
),
|
||||||
|
):
|
||||||
|
app.config.FALLBACK_ERROR_FORMAT = "json"
|
||||||
|
|
||||||
|
assert app.error_handler.fallback == "json"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user