22.3 Deprecations and changes (#2362)
This commit is contained in:
parent
8b0eaa097c
commit
8dfa49b648
|
@ -1061,6 +1061,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
host: Optional[str] = None,
|
||||
port: Optional[int] = None,
|
||||
*,
|
||||
dev: bool = False,
|
||||
debug: bool = False,
|
||||
auto_reload: Optional[bool] = None,
|
||||
ssl: Union[None, SSLContext, dict, str, list, tuple] = None,
|
||||
|
@ -1143,10 +1144,12 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
"#asynchronous-support"
|
||||
)
|
||||
|
||||
if auto_reload or auto_reload is None and debug:
|
||||
if dev:
|
||||
debug = True
|
||||
auto_reload = True
|
||||
if os.environ.get("SANIC_SERVER_RUNNING") != "true":
|
||||
return reloader_helpers.watchdog(1.0, self)
|
||||
|
||||
if auto_reload and os.environ.get("SANIC_SERVER_RUNNING") != "true":
|
||||
return reloader_helpers.watchdog(1.0, self)
|
||||
|
||||
if sock is None:
|
||||
host, port = host or "127.0.0.1", port or 8000
|
||||
|
|
|
@ -79,13 +79,6 @@ Or, a path to a directory to run as a simple HTTP server:
|
|||
error_logger.exception("Failed to run app")
|
||||
|
||||
def _precheck(self):
|
||||
if self.args.debug and self.main_process:
|
||||
error_logger.warning(
|
||||
"Starting in v22.3, --debug will no "
|
||||
"longer automatically run the auto-reloader.\n Switch to "
|
||||
"--dev to continue using that functionality."
|
||||
)
|
||||
|
||||
# # Custom TLS mismatch handling for better diagnostics
|
||||
if self.main_process and (
|
||||
# one of cert/key missing
|
||||
|
@ -174,8 +167,9 @@ Or, a path to a directory to run as a simple HTTP server:
|
|||
"workers": self.args.workers,
|
||||
}
|
||||
|
||||
if self.args.auto_reload:
|
||||
kwargs["auto_reload"] = True
|
||||
for maybe_arg in ("auto_reload", "dev"):
|
||||
if getattr(self.args, maybe_arg, False):
|
||||
kwargs[maybe_arg] = True
|
||||
|
||||
if self.args.path:
|
||||
if self.args.auto_reload or self.args.debug:
|
||||
|
|
|
@ -180,19 +180,18 @@ class DevelopmentGroup(Group):
|
|||
"--debug",
|
||||
dest="debug",
|
||||
action="store_true",
|
||||
help="Run the server in debug mode",
|
||||
help=(
|
||||
"Run the server in DEBUG mode. It includes DEBUG logging, "
|
||||
"additional context on exceptions, and other settings "
|
||||
"not-safe for PRODUCTION, but helpful for debugging problems."
|
||||
),
|
||||
)
|
||||
self.container.add_argument(
|
||||
"-d",
|
||||
"--dev",
|
||||
dest="debug",
|
||||
dest="dev",
|
||||
action="store_true",
|
||||
help=(
|
||||
"Currently is an alias for --debug. But starting in v22.3, \n"
|
||||
"--debug will no longer automatically trigger auto_restart. \n"
|
||||
"However, --dev will continue, effectively making it the \n"
|
||||
"same as debug + auto_reload."
|
||||
),
|
||||
help=("Debug + auto_reload."),
|
||||
)
|
||||
self.container.add_argument(
|
||||
"-r",
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from inspect import signature
|
||||
from typing import Dict, List, Optional, Tuple, Type, Union
|
||||
|
||||
from sanic.config import Config
|
||||
from sanic.errorpages import (
|
||||
DEFAULT_FORMAT,
|
||||
BaseRenderer,
|
||||
HTMLRenderer,
|
||||
TextRenderer,
|
||||
exception_response,
|
||||
)
|
||||
from sanic.exceptions import (
|
||||
|
@ -35,13 +34,11 @@ class ErrorHandler:
|
|||
|
||||
"""
|
||||
|
||||
# Beginning in v22.3, the base renderer will be TextRenderer
|
||||
def __init__(
|
||||
self,
|
||||
fallback: Union[str, Default] = _default,
|
||||
base: Type[BaseRenderer] = HTMLRenderer,
|
||||
base: Type[BaseRenderer] = TextRenderer,
|
||||
):
|
||||
self.handlers: List[Tuple[Type[BaseException], RouteHandler]] = []
|
||||
self.cached_handlers: Dict[
|
||||
Tuple[Type[BaseException], Optional[str]], Optional[RouteHandler]
|
||||
] = {}
|
||||
|
@ -95,8 +92,8 @@ class ErrorHandler:
|
|||
def finalize(
|
||||
cls,
|
||||
error_handler: ErrorHandler,
|
||||
config: Config,
|
||||
fallback: Optional[str] = None,
|
||||
config: Optional[Config] = None,
|
||||
):
|
||||
if fallback:
|
||||
deprecation(
|
||||
|
@ -107,14 +104,10 @@ class ErrorHandler:
|
|||
22.6,
|
||||
)
|
||||
|
||||
if config is None:
|
||||
deprecation(
|
||||
"Starting in v22.3, config will be a required argument "
|
||||
"for ErrorHandler.finalize().",
|
||||
22.3,
|
||||
)
|
||||
if not fallback:
|
||||
fallback = config.FALLBACK_ERROR_FORMAT
|
||||
|
||||
if fallback and fallback != DEFAULT_FORMAT:
|
||||
if fallback != DEFAULT_FORMAT:
|
||||
if error_handler._fallback is not _default:
|
||||
error_logger.warning(
|
||||
f"Setting the fallback value to {fallback}. This changes "
|
||||
|
@ -128,27 +121,9 @@ class ErrorHandler:
|
|||
f"Error handler is non-conforming: {type(error_handler)}"
|
||||
)
|
||||
|
||||
sig = signature(error_handler.lookup)
|
||||
if len(sig.parameters) == 1:
|
||||
deprecation(
|
||||
"You are using a deprecated error handler. The lookup "
|
||||
"method should accept two positional parameters: "
|
||||
"(exception, route_name: Optional[str]). "
|
||||
"Until you upgrade your ErrorHandler.lookup, Blueprint "
|
||||
"specific exceptions will not work properly. Beginning "
|
||||
"in v22.3, the legacy style lookup method will not "
|
||||
"work at all.",
|
||||
22.3,
|
||||
)
|
||||
legacy_lookup = error_handler._legacy_lookup
|
||||
error_handler._lookup = legacy_lookup # type: ignore
|
||||
|
||||
def _full_lookup(self, exception, route_name: Optional[str] = None):
|
||||
return self.lookup(exception, route_name)
|
||||
|
||||
def _legacy_lookup(self, exception, route_name: Optional[str] = None):
|
||||
return self.lookup(exception)
|
||||
|
||||
def add(self, exception, handler, route_names: Optional[List[str]] = None):
|
||||
"""
|
||||
Add a new exception handler to an already existing handler object.
|
||||
|
@ -162,9 +137,6 @@ class ErrorHandler:
|
|||
|
||||
:return: None
|
||||
"""
|
||||
# self.handlers is deprecated and will be removed in version 22.3
|
||||
self.handlers.append((exception, handler))
|
||||
|
||||
if route_names:
|
||||
for route in route_names:
|
||||
self.cached_handlers[(exception, route)] = handler
|
||||
|
|
|
@ -5,7 +5,7 @@ from websockets.server import ServerConnection
|
|||
from websockets.typing import Subprotocol
|
||||
|
||||
from sanic.exceptions import ServerError
|
||||
from sanic.log import deprecation, error_logger
|
||||
from sanic.log import error_logger
|
||||
from sanic.server import HttpProtocol
|
||||
|
||||
from ..websockets.impl import WebsocketImplProtocol
|
||||
|
@ -29,9 +29,6 @@ class WebSocketProtocol(HttpProtocol):
|
|||
*args,
|
||||
websocket_timeout: float = 10.0,
|
||||
websocket_max_size: Optional[int] = None,
|
||||
websocket_max_queue: Optional[int] = None, # max_queue is deprecated
|
||||
websocket_read_limit: Optional[int] = None, # read_limit is deprecated
|
||||
websocket_write_limit: Optional[int] = None, # write_limit deprecated
|
||||
websocket_ping_interval: Optional[float] = 20.0,
|
||||
websocket_ping_timeout: Optional[float] = 20.0,
|
||||
**kwargs,
|
||||
|
@ -40,27 +37,6 @@ class WebSocketProtocol(HttpProtocol):
|
|||
self.websocket: Optional[WebsocketImplProtocol] = None
|
||||
self.websocket_timeout = websocket_timeout
|
||||
self.websocket_max_size = websocket_max_size
|
||||
if websocket_max_queue is not None and websocket_max_queue > 0:
|
||||
# TODO: Reminder remove this warning in v22.3
|
||||
deprecation(
|
||||
"Websocket no longer uses queueing, so websocket_max_queue"
|
||||
" is no longer required.",
|
||||
22.3,
|
||||
)
|
||||
if websocket_read_limit is not None and websocket_read_limit > 0:
|
||||
# TODO: Reminder remove this warning in v22.3
|
||||
deprecation(
|
||||
"Websocket no longer uses read buffers, so "
|
||||
"websocket_read_limit is not required.",
|
||||
22.3,
|
||||
)
|
||||
if websocket_write_limit is not None and websocket_write_limit > 0:
|
||||
# TODO: Reminder remove this warning in v22.3
|
||||
deprecation(
|
||||
"Websocket no longer uses write buffers, so "
|
||||
"websocket_write_limit is not required.",
|
||||
22.3,
|
||||
)
|
||||
self.websocket_ping_interval = websocket_ping_interval
|
||||
self.websocket_ping_timeout = websocket_ping_timeout
|
||||
|
||||
|
|
34
tests/asyncmock.py
Normal file
34
tests/asyncmock.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
"""
|
||||
For 3.7 compat
|
||||
|
||||
"""
|
||||
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
|
||||
class AsyncMock(Mock):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.await_count = 0
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
self.call_count += 1
|
||||
parent = super(AsyncMock, self)
|
||||
|
||||
async def dummy():
|
||||
self.await_count += 1
|
||||
return parent.__call__(*args, **kwargs)
|
||||
|
||||
return dummy()
|
||||
|
||||
def __await__(self):
|
||||
return self().__await__()
|
||||
|
||||
def assert_awaited_once(self):
|
||||
if not self.await_count == 1:
|
||||
msg = (
|
||||
f"Expected to have been awaited once."
|
||||
f" Awaited {self.await_count} times."
|
||||
)
|
||||
raise AssertionError(msg)
|
|
@ -103,7 +103,7 @@ def test_tls_wrong_options(cmd):
|
|||
assert not out
|
||||
lines = err.decode().split("\n")
|
||||
|
||||
errmsg = lines[8]
|
||||
errmsg = lines[6]
|
||||
assert errmsg == "TLS certificates must be specified by either of:"
|
||||
|
||||
|
||||
|
@ -200,13 +200,25 @@ def test_num_workers(num, cmd):
|
|||
assert len(worker_lines) == num * 2, f"Lines found: {lines}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("cmd", ("--debug", "-d"))
|
||||
@pytest.mark.parametrize("cmd", ("--debug",))
|
||||
def test_debug(cmd):
|
||||
command = ["sanic", "fake.server.app", cmd]
|
||||
out, err, exitcode = capture(command)
|
||||
lines = out.split(b"\n")
|
||||
info = read_app_info(lines)
|
||||
|
||||
assert info["debug"] is True
|
||||
assert info["auto_reload"] is False
|
||||
assert "dev" not in info
|
||||
|
||||
|
||||
@pytest.mark.parametrize("cmd", ("--dev", "-d"))
|
||||
def test_dev(cmd):
|
||||
command = ["sanic", "fake.server.app", cmd]
|
||||
out, err, exitcode = capture(command)
|
||||
lines = out.split(b"\n")
|
||||
info = read_app_info(lines)
|
||||
|
||||
assert info["debug"] is True
|
||||
assert info["auto_reload"] is True
|
||||
|
||||
|
@ -220,6 +232,7 @@ def test_auto_reload(cmd):
|
|||
|
||||
assert info["debug"] is False
|
||||
assert info["auto_reload"] is True
|
||||
assert "dev" not in info
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
@ -67,7 +67,7 @@ def test_auto_fallback_with_data(app):
|
|||
|
||||
_, response = app.test_client.get("/error")
|
||||
assert response.status == 500
|
||||
assert response.content_type == "text/html; charset=utf-8"
|
||||
assert response.content_type == "text/plain; charset=utf-8"
|
||||
|
||||
_, response = app.test_client.post("/error", json={"foo": "bar"})
|
||||
assert response.status == 500
|
||||
|
@ -75,7 +75,7 @@ def test_auto_fallback_with_data(app):
|
|||
|
||||
_, response = app.test_client.post("/error", data={"foo": "bar"})
|
||||
assert response.status == 500
|
||||
assert response.content_type == "text/html; charset=utf-8"
|
||||
assert response.content_type == "text/plain; charset=utf-8"
|
||||
|
||||
|
||||
def test_auto_fallback_with_content_type(app):
|
||||
|
@ -91,7 +91,7 @@ def test_auto_fallback_with_content_type(app):
|
|||
"/error", headers={"content-type": "foo/bar", "accept": "*/*"}
|
||||
)
|
||||
assert response.status == 500
|
||||
assert response.content_type == "text/html; charset=utf-8"
|
||||
assert response.content_type == "text/plain; charset=utf-8"
|
||||
|
||||
|
||||
def test_route_error_format_set_on_auto(app):
|
||||
|
@ -174,6 +174,17 @@ def test_route_error_format_unknown(app):
|
|||
...
|
||||
|
||||
|
||||
def test_fallback_with_content_type_html(app):
|
||||
app.config.FALLBACK_ERROR_FORMAT = "auto"
|
||||
|
||||
_, response = app.test_client.get(
|
||||
"/error",
|
||||
headers={"content-type": "application/json", "accept": "text/html"},
|
||||
)
|
||||
assert response.status == 500
|
||||
assert response.content_type == "text/html; charset=utf-8"
|
||||
|
||||
|
||||
def test_fallback_with_content_type_mismatch_accept(app):
|
||||
app.config.FALLBACK_ERROR_FORMAT = "auto"
|
||||
|
||||
|
@ -186,10 +197,10 @@ def test_fallback_with_content_type_mismatch_accept(app):
|
|||
|
||||
_, response = app.test_client.get(
|
||||
"/error",
|
||||
headers={"content-type": "text/plain", "accept": "foo/bar"},
|
||||
headers={"content-type": "text/html", "accept": "foo/bar"},
|
||||
)
|
||||
assert response.status == 500
|
||||
assert response.content_type == "text/html; charset=utf-8"
|
||||
assert response.content_type == "text/plain; charset=utf-8"
|
||||
|
||||
app.router.reset()
|
||||
|
||||
|
@ -208,7 +219,7 @@ def test_fallback_with_content_type_mismatch_accept(app):
|
|||
headers={"accept": "foo/bar"},
|
||||
)
|
||||
assert response.status == 500
|
||||
assert response.content_type == "text/html; charset=utf-8"
|
||||
assert response.content_type == "text/plain; charset=utf-8"
|
||||
_, response = app.test_client.get(
|
||||
"/alt1",
|
||||
headers={"accept": "foo/bar,*/*"},
|
||||
|
@ -221,7 +232,7 @@ def test_fallback_with_content_type_mismatch_accept(app):
|
|||
headers={"accept": "foo/bar"},
|
||||
)
|
||||
assert response.status == 500
|
||||
assert response.content_type == "text/html; charset=utf-8"
|
||||
assert response.content_type == "text/plain; charset=utf-8"
|
||||
_, response = app.test_client.get(
|
||||
"/alt2",
|
||||
headers={"accept": "foo/bar,*/*"},
|
||||
|
@ -234,6 +245,13 @@ def test_fallback_with_content_type_mismatch_accept(app):
|
|||
headers={"accept": "foo/bar"},
|
||||
)
|
||||
assert response.status == 500
|
||||
assert response.content_type == "text/plain; charset=utf-8"
|
||||
|
||||
_, response = app.test_client.get(
|
||||
"/alt3",
|
||||
headers={"accept": "foo/bar,text/html"},
|
||||
)
|
||||
assert response.status == 500
|
||||
assert response.content_type == "text/html; charset=utf-8"
|
||||
|
||||
|
||||
|
@ -288,6 +306,10 @@ def test_allow_fallback_error_format_set_main_process_start(app):
|
|||
def test_setting_fallback_on_config_changes_as_expected(app):
|
||||
app.error_handler = ErrorHandler()
|
||||
|
||||
_, response = app.test_client.get("/error")
|
||||
assert response.content_type == "text/plain; charset=utf-8"
|
||||
|
||||
app.config.FALLBACK_ERROR_FORMAT = "html"
|
||||
_, response = app.test_client.get("/error")
|
||||
assert response.content_type == "text/html; charset=utf-8"
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ class SanicExceptionTestException(Exception):
|
|||
@pytest.fixture(scope="module")
|
||||
def exception_app():
|
||||
app = Sanic("test_exceptions")
|
||||
app.config.FALLBACK_ERROR_FORMAT = "html"
|
||||
|
||||
@app.route("/")
|
||||
def handler(request):
|
||||
|
|
|
@ -216,31 +216,6 @@ def test_exception_handler_processed_request_middleware(
|
|||
assert response.text == "Done."
|
||||
|
||||
|
||||
def test_single_arg_exception_handler_notice(
|
||||
exception_handler_app: Sanic, caplog: LogCaptureFixture
|
||||
):
|
||||
class CustomErrorHandler(ErrorHandler):
|
||||
def lookup(self, exception):
|
||||
return super().lookup(exception, None)
|
||||
|
||||
exception_handler_app.error_handler = CustomErrorHandler()
|
||||
|
||||
message = (
|
||||
"[DEPRECATION v22.3] You are using a deprecated error handler. The "
|
||||
"lookup method should accept two positional parameters: (exception, "
|
||||
"route_name: Optional[str]). Until you upgrade your "
|
||||
"ErrorHandler.lookup, Blueprint specific exceptions will not work "
|
||||
"properly. Beginning in v22.3, the legacy style lookup method will "
|
||||
"not work at all."
|
||||
)
|
||||
with pytest.warns(DeprecationWarning) as record:
|
||||
_, response = exception_handler_app.test_client.get("/1")
|
||||
|
||||
assert len(record) == 1
|
||||
assert record[0].message.args[0] == message
|
||||
assert response.status == 400
|
||||
|
||||
|
||||
def test_error_handler_noisy_log(
|
||||
exception_handler_app: Sanic, monkeypatch: MonkeyPatch
|
||||
):
|
||||
|
@ -279,7 +254,7 @@ def test_exception_handler_response_was_sent(
|
|||
|
||||
@app.route("/2")
|
||||
async def handler2(request: Request):
|
||||
response = await request.respond()
|
||||
await request.respond()
|
||||
raise ServerError("Exception")
|
||||
|
||||
with caplog.at_level(logging.WARNING):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import re
|
||||
|
||||
from asyncio import Event, Queue, TimeoutError
|
||||
from unittest.mock import AsyncMock, Mock, call
|
||||
from unittest.mock import Mock, call
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -11,6 +11,12 @@ from sanic.exceptions import ServerError
|
|||
from sanic.server.websockets.frame import WebsocketFrameAssembler
|
||||
|
||||
|
||||
try:
|
||||
from unittest.mock import AsyncMock
|
||||
except ImportError:
|
||||
from asyncmock import AsyncMock # type: ignore
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ws_frame_get_message_incomplete_timeout_0():
|
||||
assembler = WebsocketFrameAssembler(Mock())
|
||||
|
|
Loading…
Reference in New Issue
Block a user