diff --git a/sanic/app.py b/sanic/app.py index 26f1b9d9..cb6f76b3 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -47,7 +47,7 @@ from sanic_routing.exceptions import FinalizationError, NotFound from sanic_routing.route import Route from sanic.application.ext import setup_ext -from sanic.application.state import ApplicationState, Mode, ServerStage +from sanic.application.state import ApplicationState, ServerStage from sanic.asgi import ASGIApp from sanic.base.root import BaseSanic from sanic.blueprint_group import BlueprintGroup @@ -158,7 +158,6 @@ class Sanic(BaseSanic, StartupMixin, metaclass=TouchUpMeta): ) _app_registry: Dict[str, "Sanic"] = {} - _uvloop_setting = None # TODO: Remove in v22.6 test_mode = False def __init__( @@ -394,8 +393,8 @@ class Sanic(BaseSanic, StartupMixin, metaclass=TouchUpMeta): routes = [routes] for r in routes: - r.ctx.websocket = websocket - r.ctx.static = params.get("static", False) + r.extra.websocket = websocket + r.extra.static = params.get("static", False) r.ctx.__dict__.update(ctx) return routes @@ -589,7 +588,7 @@ class Sanic(BaseSanic, StartupMixin, metaclass=TouchUpMeta): uri = route.path - if getattr(route.ctx, "static", None): + if getattr(route.extra, "static", None): filename = kwargs.pop("filename", "") # it's static folder if "__file_uri__" in uri: @@ -622,18 +621,18 @@ class Sanic(BaseSanic, StartupMixin, metaclass=TouchUpMeta): host = kwargs.pop("_host", None) external = kwargs.pop("_external", False) or bool(host) scheme = kwargs.pop("_scheme", "") - if route.ctx.hosts and external: - if not host and len(route.ctx.hosts) > 1: + if route.extra.hosts and external: + if not host and len(route.extra.hosts) > 1: raise ValueError( - f"Host is ambiguous: {', '.join(route.ctx.hosts)}" + f"Host is ambiguous: {', '.join(route.extra.hosts)}" ) - elif host and host not in route.ctx.hosts: + elif host and host not in route.extra.hosts: raise ValueError( f"Requested host ({host}) is not available for this " - f"route: {route.ctx.hosts}" + f"route: {route.extra.hosts}" ) elif not host: - host = list(route.ctx.hosts)[0] + host = list(route.extra.hosts)[0] if scheme and not external: raise ValueError("When specifying _scheme, _external must be True") @@ -884,7 +883,7 @@ class Sanic(BaseSanic, StartupMixin, metaclass=TouchUpMeta): if ( request.stream and request.stream.request_body - and not route.ctx.ignore_body + and not route.extra.ignore_body ): if hasattr(handler, "is_stream"): @@ -1346,18 +1345,6 @@ class Sanic(BaseSanic, StartupMixin, metaclass=TouchUpMeta): def debug(self): return self.state.is_debug - @debug.setter - def debug(self, value: bool): - deprecation( - "Setting the value of a Sanic application's debug value directly " - "is deprecated and will be removed in v22.9. Please set it using " - "the CLI, app.run, app.prepare, or directly set " - "app.state.mode to Mode.DEBUG.", - 22.9, - ) - mode = Mode.DEBUG if value else Mode.PRODUCTION - self.state.mode = mode - @property def auto_reload(self): return self.config.AUTO_RELOAD @@ -1374,58 +1361,6 @@ class Sanic(BaseSanic, StartupMixin, metaclass=TouchUpMeta): """ return self._state - @property - def is_running(self): - deprecation( - "Use of the is_running property is no longer used by Sanic " - "internally. The property is now deprecated and will be removed " - "in version 22.9. You may continue to set the property for your " - "own needs until that time. If you would like to check whether " - "the application is operational, please use app.state.stage. More " - "information is available at ___.", - 22.9, - ) - return self.state.is_running - - @is_running.setter - def is_running(self, value: bool): - deprecation( - "Use of the is_running property is no longer used by Sanic " - "internally. The property is now deprecated and will be removed " - "in version 22.9. You may continue to set the property for your " - "own needs until that time. If you would like to check whether " - "the application is operational, please use app.state.stage. More " - "information is available at ___.", - 22.9, - ) - self.state.is_running = value - - @property - def is_stopping(self): - deprecation( - "Use of the is_stopping property is no longer used by Sanic " - "internally. The property is now deprecated and will be removed " - "in version 22.9. You may continue to set the property for your " - "own needs until that time. If you would like to check whether " - "the application is operational, please use app.state.stage. More " - "information is available at ___.", - 22.9, - ) - return self.state.is_stopping - - @is_stopping.setter - def is_stopping(self, value: bool): - deprecation( - "Use of the is_stopping property is no longer used by Sanic " - "internally. The property is now deprecated and will be removed " - "in version 22.9. You may continue to set the property for your " - "own needs until that time. If you would like to check whether " - "the application is operational, please use app.state.stage. More " - "information is available at ___.", - 22.9, - ) - self.state.is_stopping = value - @property def reload_dirs(self): return self.state.reload_dirs @@ -1520,6 +1455,16 @@ class Sanic(BaseSanic, StartupMixin, metaclass=TouchUpMeta): return cls(name) raise SanicException(f'Sanic app name "{name}" not found.') + @classmethod + def _check_uvloop_conflict(cls) -> None: + values = {app.config.USE_UVLOOP for app in cls._app_registry.values()} + if len(values) > 1: + error_logger.warning( + "It looks like you're running several apps with different " + "uvloop settings. This is not supported and may lead to " + "unintended behaviour." + ) + # -------------------------------------------------------------------- # # Lifecycle # -------------------------------------------------------------------- # @@ -1569,17 +1514,7 @@ class Sanic(BaseSanic, StartupMixin, metaclass=TouchUpMeta): 23.3, ) - # TODO: Replace in v22.6 to check against apps in app registry - if ( - self.__class__._uvloop_setting is not None - and self.__class__._uvloop_setting != self.config.USE_UVLOOP - ): - error_logger.warning( - "It looks like you're running several apps with different " - "uvloop settings. This is not supported and may lead to " - "unintended behaviour." - ) - self.__class__._uvloop_setting = self.config.USE_UVLOOP + Sanic._check_uvloop_conflict() # Startup time optimizations if self.state.primary: diff --git a/sanic/blueprints.py b/sanic/blueprints.py index 271306cb..0cca4bd4 100644 --- a/sanic/blueprints.py +++ b/sanic/blueprints.py @@ -406,7 +406,7 @@ class Blueprint(BaseSanic): self.routes += [route for route in routes if isinstance(route, Route)] self.websocket_routes += [ - route for route in self.routes if route.ctx.websocket + route for route in self.routes if route.extra.websocket ] self.middlewares += middleware self.exceptions += exception_handlers diff --git a/sanic/config.py b/sanic/config.py index 5c90086d..8f0e8de4 100644 --- a/sanic/config.py +++ b/sanic/config.py @@ -12,7 +12,7 @@ from sanic.constants import LocalCertCreator from sanic.errorpages import DEFAULT_FORMAT, check_error_format from sanic.helpers import Default, _default from sanic.http import Http -from sanic.log import deprecation, error_logger +from sanic.log import error_logger from sanic.utils import load_module_from_file_location, str_to_bool @@ -71,10 +71,6 @@ DEFAULT_CONFIG = { "WEBSOCKET_PING_TIMEOUT": 20, } -# These values will be removed from the Config object in v22.6 and moved -# to the application state -DEPRECATED_CONFIG = ("SERVER_RUNNING", "RELOADER_PROCESS", "RELOADED_FILES") - class DescriptorMeta(type): def __init__(cls, *_): @@ -132,6 +128,7 @@ class Config(dict, metaclass=DescriptorMeta): ): defaults = defaults or {} super().__init__({**DEFAULT_CONFIG, **defaults}) + self._configure_warnings() self._converters = [str, str_to_bool, float, int] @@ -149,7 +146,6 @@ class Config(dict, metaclass=DescriptorMeta): self.load_environment_vars(SANIC_PREFIX) self._configure_header_size() - self._configure_warnings() self._check_error_format() self._init = True @@ -241,7 +237,9 @@ class Config(dict, metaclass=DescriptorMeta): """ Looks for prefixed environment variables and applies them to the configuration if present. This is called automatically when Sanic - starts up to load environment variables into config. + starts up to load environment variables into config. Environment + variables should start with the defined prefix and should only + contain uppercase letters. It will automatically hydrate the following types: @@ -267,12 +265,9 @@ class Config(dict, metaclass=DescriptorMeta): `See user guide re: config `__ """ - lower_case_var_found = False for key, value in environ.items(): - if not key.startswith(prefix): + if not key.startswith(prefix) or not key.isupper(): continue - if not key.isupper(): - lower_case_var_found = True _, config_key = key.split(prefix, 1) @@ -282,12 +277,6 @@ class Config(dict, metaclass=DescriptorMeta): break except ValueError: pass - if lower_case_var_found: - deprecation( - "Lowercase environment variables will not be " - "loaded into Sanic config beginning in v22.9.", - 22.9, - ) def update_config(self, config: Union[bytes, str, dict, Any]): """ diff --git a/sanic/errorpages.py b/sanic/errorpages.py index e5d123b1..c035dce1 100644 --- a/sanic/errorpages.py +++ b/sanic/errorpages.py @@ -448,8 +448,8 @@ def exception_response( # from the route if request.route: try: - if request.route.ctx.error_format: - render_format = request.route.ctx.error_format + if request.route.extra.error_format: + render_format = request.route.extra.error_format except AttributeError: ... diff --git a/sanic/mixins/routes.py b/sanic/mixins/routes.py index 2fb1e837..c38b32ee 100644 --- a/sanic/mixins/routes.py +++ b/sanic/mixins/routes.py @@ -10,6 +10,7 @@ from textwrap import dedent from typing import ( Any, Callable, + Dict, Iterable, List, Optional, @@ -38,14 +39,6 @@ from sanic.types import HashableDict RouteWrapper = Callable[ [RouteHandler], Union[RouteHandler, Tuple[Route, RouteHandler]] ] -RESTRICTED_ROUTE_CONTEXT = ( - "ignore_body", - "stream", - "hosts", - "static", - "error_format", - "websocket", -) class RouteMixin(metaclass=SanicMeta): @@ -1046,24 +1039,12 @@ class RouteMixin(metaclass=SanicMeta): return types - def _build_route_context(self, raw): + def _build_route_context(self, raw: Dict[str, Any]) -> HashableDict: ctx_kwargs = { key.replace("ctx_", ""): raw.pop(key) for key in {**raw}.keys() if key.startswith("ctx_") } - restricted = [ - key for key in ctx_kwargs.keys() if key in RESTRICTED_ROUTE_CONTEXT - ] - if restricted: - restricted_arguments = ", ".join(restricted) - raise AttributeError( - "Cannot use restricted route context: " - f"{restricted_arguments}. This limitation is only in place " - "until v22.9 when the restricted names will no longer be in" - "conflict. See https://github.com/sanic-org/sanic/issues/2303 " - "for more information." - ) if raw: unexpected_arguments = ", ".join(raw.keys()) raise TypeError( diff --git a/sanic/mixins/startup.py b/sanic/mixins/startup.py index 5365431c..38414f39 100644 --- a/sanic/mixins/startup.py +++ b/sanic/mixins/startup.py @@ -559,7 +559,6 @@ class StartupMixin(metaclass=SanicMeta): def motd( self, - serve_location: str = "", server_settings: Optional[Dict[str, Any]] = None, ): if ( @@ -569,14 +568,7 @@ class StartupMixin(metaclass=SanicMeta): or os.environ.get("SANIC_SERVER_RUNNING") ): return - if serve_location: - deprecation( - "Specifying a serve_location in the MOTD is deprecated and " - "will be removed.", - 22.9, - ) - else: - serve_location = self.get_server_location(server_settings) + serve_location = self.get_server_location(server_settings) if self.config.MOTD: logo = get_logo(coffee=self.state.coffee) display, extra = self.get_motd_data(server_settings) diff --git a/sanic/router.py b/sanic/router.py index ec4d852f..b68fde3b 100644 --- a/sanic/router.py +++ b/sanic/router.py @@ -133,14 +133,14 @@ class Router(BaseRouter): params.update({"requirements": {"host": host}}) route = super().add(**params) # type: ignore - route.ctx.ignore_body = ignore_body - route.ctx.stream = stream - route.ctx.hosts = hosts - route.ctx.static = static - route.ctx.error_format = error_format + route.extra.ignore_body = ignore_body + route.extra.stream = stream + route.extra.hosts = hosts + route.extra.static = static + route.extra.error_format = error_format if error_format: - check_error_format(route.ctx.error_format) + check_error_format(route.extra.error_format) routes.append(route) diff --git a/tests/test_app.py b/tests/test_app.py index 333ea51a..0e71f9f7 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -525,7 +525,7 @@ def test_multiple_uvloop_configs_display_warning(caplog): counter = Counter([(r[1], r[2]) for r in caplog.record_tuples]) - assert counter[(logging.WARNING, message)] == 2 + assert counter[(logging.WARNING, message)] == 3 def test_cannot_run_fast_and_workers(app: Sanic): diff --git a/tests/test_config.py b/tests/test_config.py index e18660c2..a702bc88 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -125,14 +125,9 @@ def test_env_w_custom_converter(): def test_env_lowercase(): - with pytest.warns(None) as record: - environ["SANIC_test_answer"] = "42" - app = Sanic(name="Test") - assert app.config.test_answer == 42 - assert str(record[0].message) == ( - "[DEPRECATION v22.9] Lowercase environment variables will not be " - "loaded into Sanic config beginning in v22.9." - ) + environ["SANIC_test_answer"] = "42" + app = Sanic(name="Test") + assert "test_answer" not in app.config del environ["SANIC_test_answer"] diff --git a/tests/test_errorpages.py b/tests/test_errorpages.py index ce246894..fda629ca 100644 --- a/tests/test_errorpages.py +++ b/tests/test_errorpages.py @@ -97,15 +97,15 @@ def test_auto_fallback_with_content_type(app): def test_route_error_format_set_on_auto(app): @app.get("/text") def text_response(request): - return text(request.route.ctx.error_format) + return text(request.route.extra.error_format) @app.get("/json") def json_response(request): - return json({"format": request.route.ctx.error_format}) + return json({"format": request.route.extra.error_format}) @app.get("/html") def html_response(request): - return html(request.route.ctx.error_format) + return html(request.route.extra.error_format) _, response = app.test_client.get("/text") assert response.text == "text"