v23.3 Deprecation Removal (#2717)
This commit is contained in:
19
sanic/app.py
19
sanic/app.py
@@ -64,12 +64,7 @@ from sanic.exceptions import (
|
||||
from sanic.handlers import ErrorHandler
|
||||
from sanic.helpers import Default, _default
|
||||
from sanic.http import Stage
|
||||
from sanic.log import (
|
||||
LOGGING_CONFIG_DEFAULTS,
|
||||
deprecation,
|
||||
error_logger,
|
||||
logger,
|
||||
)
|
||||
from sanic.log import LOGGING_CONFIG_DEFAULTS, error_logger, logger
|
||||
from sanic.middleware import Middleware, MiddlewareLocation
|
||||
from sanic.mixins.listeners import ListenerEvent
|
||||
from sanic.mixins.startup import StartupMixin
|
||||
@@ -1584,17 +1579,19 @@ class Sanic(StaticHandleMixin, BaseSanic, StartupMixin, metaclass=TouchUpMeta):
|
||||
self.signalize(self.config.TOUCHUP)
|
||||
self.finalize()
|
||||
|
||||
route_names = [route.name for route in self.router.routes]
|
||||
route_names = [route.extra.ident for route in self.router.routes]
|
||||
duplicates = {
|
||||
name for name in route_names if route_names.count(name) > 1
|
||||
}
|
||||
if duplicates:
|
||||
names = ", ".join(duplicates)
|
||||
deprecation(
|
||||
f"Duplicate route names detected: {names}. In the future, "
|
||||
"Sanic will enforce uniqueness in route naming.",
|
||||
23.3,
|
||||
message = (
|
||||
f"Duplicate route names detected: {names}. You should rename "
|
||||
"one or more of them explicitly by using the `name` param, "
|
||||
"or changing the implicit name derived from the class and "
|
||||
"function name. For more details, please see ___."
|
||||
)
|
||||
raise ServerError(message)
|
||||
|
||||
Sanic._check_uvloop_conflict()
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ class Blueprint(BaseSanic):
|
||||
"_future_listeners",
|
||||
"_future_exceptions",
|
||||
"_future_signals",
|
||||
"copied_from",
|
||||
"ctx",
|
||||
"exceptions",
|
||||
"host",
|
||||
@@ -118,6 +119,7 @@ class Blueprint(BaseSanic):
|
||||
):
|
||||
super().__init__(name=name)
|
||||
self.reset()
|
||||
self.copied_from = ""
|
||||
self.ctx = SimpleNamespace()
|
||||
self.host = host
|
||||
self.strict_slashes = strict_slashes
|
||||
@@ -213,6 +215,7 @@ class Blueprint(BaseSanic):
|
||||
self.reset()
|
||||
new_bp = deepcopy(self)
|
||||
new_bp.name = name
|
||||
new_bp.copied_from = self.name
|
||||
|
||||
if not isinstance(url_prefix, Default):
|
||||
new_bp.url_prefix = url_prefix
|
||||
@@ -352,6 +355,16 @@ class Blueprint(BaseSanic):
|
||||
|
||||
registered.add(apply_route)
|
||||
route = app._apply_route(apply_route)
|
||||
|
||||
# If it is a copied BP, then make sure all of the names of routes
|
||||
# matchup with the new BP name
|
||||
if self.copied_from:
|
||||
for r in route:
|
||||
r.name = r.name.replace(self.copied_from, self.name)
|
||||
r.extra.ident = r.extra.ident.replace(
|
||||
self.copied_from, self.name
|
||||
)
|
||||
|
||||
operation = (
|
||||
routes.extend if isinstance(route, list) else routes.append
|
||||
)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
@@ -6,7 +5,7 @@ import sys
|
||||
from argparse import Namespace
|
||||
from functools import partial
|
||||
from textwrap import indent
|
||||
from typing import List, Union, cast
|
||||
from typing import List, Union
|
||||
|
||||
from sanic.app import Sanic
|
||||
from sanic.application.logo import get_logo
|
||||
@@ -14,7 +13,7 @@ from sanic.cli.arguments import Group
|
||||
from sanic.cli.base import SanicArgumentParser, SanicHelpFormatter
|
||||
from sanic.cli.inspector import make_inspector_parser
|
||||
from sanic.cli.inspector_client import InspectorClient
|
||||
from sanic.log import Colors, error_logger
|
||||
from sanic.log import error_logger
|
||||
from sanic.worker.loader import AppLoader
|
||||
|
||||
|
||||
@@ -103,10 +102,6 @@ Or, a path to a directory to run as a simple HTTP server:
|
||||
self.args.target, self.args.factory, self.args.simple, self.args
|
||||
)
|
||||
|
||||
if self.args.inspect or self.args.inspect_raw or self.args.trigger:
|
||||
self._inspector_legacy(app_loader)
|
||||
return
|
||||
|
||||
try:
|
||||
app = self._get_app(app_loader)
|
||||
kwargs = self._build_run_kwargs()
|
||||
@@ -117,38 +112,10 @@ Or, a path to a directory to run as a simple HTTP server:
|
||||
app.prepare(**kwargs, version=http_version)
|
||||
if self.args.single:
|
||||
serve = Sanic.serve_single
|
||||
elif self.args.legacy:
|
||||
serve = Sanic.serve_legacy
|
||||
else:
|
||||
serve = partial(Sanic.serve, app_loader=app_loader)
|
||||
serve(app)
|
||||
|
||||
def _inspector_legacy(self, app_loader: AppLoader):
|
||||
host = port = None
|
||||
target = cast(str, self.args.target)
|
||||
if ":" in target:
|
||||
maybe_host, maybe_port = target.rsplit(":", 1)
|
||||
if maybe_port.isnumeric():
|
||||
host, port = maybe_host, int(maybe_port)
|
||||
if not host:
|
||||
app = self._get_app(app_loader)
|
||||
host, port = app.config.INSPECTOR_HOST, app.config.INSPECTOR_PORT
|
||||
|
||||
action = self.args.trigger or "info"
|
||||
|
||||
InspectorClient(
|
||||
str(host), int(port or 6457), False, self.args.inspect_raw, ""
|
||||
).do(action)
|
||||
sys.stdout.write(
|
||||
f"\n{Colors.BOLD}{Colors.YELLOW}WARNING:{Colors.END} "
|
||||
"You are using the legacy CLI command that will be removed in "
|
||||
f"{Colors.RED}v23.3{Colors.END}. See "
|
||||
"https://sanic.dev/en/guide/release-notes/v22.12.html"
|
||||
"#deprecations-and-removals or checkout the new "
|
||||
"style commands:\n\n\t"
|
||||
f"{Colors.YELLOW}sanic inspect --help{Colors.END}\n"
|
||||
)
|
||||
|
||||
def _inspector(self):
|
||||
args = sys.argv[2:]
|
||||
self.args, unknown = self.parser.parse_known_args(args=args)
|
||||
@@ -202,8 +169,6 @@ Or, a path to a directory to run as a simple HTTP server:
|
||||
)
|
||||
error_logger.error(message)
|
||||
sys.exit(1)
|
||||
if self.args.inspect or self.args.inspect_raw:
|
||||
logging.disable(logging.CRITICAL)
|
||||
|
||||
def _get_app(self, app_loader: AppLoader):
|
||||
try:
|
||||
@@ -251,7 +216,6 @@ Or, a path to a directory to run as a simple HTTP server:
|
||||
"workers": self.args.workers,
|
||||
"auto_tls": self.args.auto_tls,
|
||||
"single_process": self.args.single,
|
||||
"legacy": self.args.legacy,
|
||||
}
|
||||
|
||||
for maybe_arg in ("auto_reload", "dev"):
|
||||
|
||||
@@ -93,32 +93,6 @@ class ApplicationGroup(Group):
|
||||
"a directory\n(module arg should be a path)"
|
||||
),
|
||||
)
|
||||
group.add_argument(
|
||||
"--inspect",
|
||||
dest="inspect",
|
||||
action="store_true",
|
||||
help=("Inspect the state of a running instance, human readable"),
|
||||
)
|
||||
group.add_argument(
|
||||
"--inspect-raw",
|
||||
dest="inspect_raw",
|
||||
action="store_true",
|
||||
help=("Inspect the state of a running instance, JSON output"),
|
||||
)
|
||||
group.add_argument(
|
||||
"--trigger-reload",
|
||||
dest="trigger",
|
||||
action="store_const",
|
||||
const="reload",
|
||||
help=("Trigger worker processes to reload"),
|
||||
)
|
||||
group.add_argument(
|
||||
"--trigger-shutdown",
|
||||
dest="trigger",
|
||||
action="store_const",
|
||||
const="shutdown",
|
||||
help=("Trigger all processes to shutdown"),
|
||||
)
|
||||
|
||||
|
||||
class HTTPVersionGroup(Group):
|
||||
@@ -247,11 +221,6 @@ class WorkerGroup(Group):
|
||||
action="store_true",
|
||||
help="Do not use multiprocessing, run server in a single process",
|
||||
)
|
||||
self.container.add_argument(
|
||||
"--legacy",
|
||||
action="store_true",
|
||||
help="Use the legacy server manager",
|
||||
)
|
||||
self.add_bool_arguments(
|
||||
"--access-logs",
|
||||
dest="access_log",
|
||||
|
||||
@@ -3,7 +3,8 @@ from __future__ import annotations
|
||||
from typing import Dict, List, Optional, Tuple, Type
|
||||
|
||||
from sanic.errorpages import BaseRenderer, TextRenderer, exception_response
|
||||
from sanic.log import deprecation, error_logger
|
||||
from sanic.exceptions import ServerError
|
||||
from sanic.log import error_logger
|
||||
from sanic.models.handler_types import RouteHandler
|
||||
from sanic.response import text
|
||||
|
||||
@@ -43,16 +44,11 @@ class ErrorHandler:
|
||||
if name is None:
|
||||
name = "__ALL_ROUTES__"
|
||||
|
||||
error_logger.warning(
|
||||
message = (
|
||||
f"Duplicate exception handler definition on: route={name} "
|
||||
f"and exception={exc}"
|
||||
)
|
||||
deprecation(
|
||||
"A duplicate exception handler definition was discovered. "
|
||||
"This may cause unintended consequences. A warning has been "
|
||||
"issued now, but it will not be allowed starting in v23.3.",
|
||||
23.3,
|
||||
)
|
||||
raise ServerError(message)
|
||||
self.cached_handlers[key] = handler
|
||||
|
||||
def add(self, exception, handler, route_names: Optional[List[str]] = None):
|
||||
|
||||
@@ -47,17 +47,16 @@ from sanic.helpers import Default, _default
|
||||
from sanic.http.constants import HTTP
|
||||
from sanic.http.tls import get_ssl_context, process_to_context
|
||||
from sanic.http.tls.context import SanicSSLContext
|
||||
from sanic.log import Colors, deprecation, error_logger, logger
|
||||
from sanic.log import Colors, error_logger, logger
|
||||
from sanic.models.handler_types import ListenerType
|
||||
from sanic.server import Signal as ServerSignal
|
||||
from sanic.server import try_use_uvloop
|
||||
from sanic.server.async_server import AsyncioServer
|
||||
from sanic.server.events import trigger_events
|
||||
from sanic.server.legacy import watchdog
|
||||
from sanic.server.loop import try_windows_loop
|
||||
from sanic.server.protocols.http_protocol import HttpProtocol
|
||||
from sanic.server.protocols.websocket_protocol import WebSocketProtocol
|
||||
from sanic.server.runners import serve, serve_multiple, serve_single
|
||||
from sanic.server.runners import serve
|
||||
from sanic.server.socket import configure_socket, remove_unix_socket
|
||||
from sanic.worker.loader import AppLoader
|
||||
from sanic.worker.manager import WorkerManager
|
||||
@@ -135,7 +134,6 @@ class StartupMixin(metaclass=SanicMeta):
|
||||
motd_display: Optional[Dict[str, str]] = None,
|
||||
auto_tls: bool = False,
|
||||
single_process: bool = False,
|
||||
legacy: bool = False,
|
||||
) -> None:
|
||||
"""
|
||||
Run the HTTP Server and listen until keyboard interrupt or term
|
||||
@@ -197,13 +195,10 @@ class StartupMixin(metaclass=SanicMeta):
|
||||
motd_display=motd_display,
|
||||
auto_tls=auto_tls,
|
||||
single_process=single_process,
|
||||
legacy=legacy,
|
||||
)
|
||||
|
||||
if single_process:
|
||||
serve = self.__class__.serve_single
|
||||
elif legacy:
|
||||
serve = self.__class__.serve_legacy
|
||||
else:
|
||||
serve = self.__class__.serve
|
||||
serve(primary=self) # type: ignore
|
||||
@@ -235,7 +230,6 @@ class StartupMixin(metaclass=SanicMeta):
|
||||
coffee: bool = False,
|
||||
auto_tls: bool = False,
|
||||
single_process: bool = False,
|
||||
legacy: bool = False,
|
||||
) -> None:
|
||||
if version == 3 and self.state.server_info:
|
||||
raise RuntimeError(
|
||||
@@ -264,13 +258,10 @@ class StartupMixin(metaclass=SanicMeta):
|
||||
"or auto-reload"
|
||||
)
|
||||
|
||||
if single_process and legacy:
|
||||
raise RuntimeError("Cannot run single process and legacy mode")
|
||||
|
||||
if register_sys_signals is False and not (single_process or legacy):
|
||||
if register_sys_signals is False and not single_process:
|
||||
raise RuntimeError(
|
||||
"Cannot run Sanic.serve with register_sys_signals=False. "
|
||||
"Use either Sanic.serve_single or Sanic.serve_legacy."
|
||||
"Use Sanic.serve_single."
|
||||
)
|
||||
|
||||
if motd_display:
|
||||
@@ -956,76 +947,6 @@ class StartupMixin(metaclass=SanicMeta):
|
||||
cls._cleanup_env_vars()
|
||||
cls._cleanup_apps()
|
||||
|
||||
@classmethod
|
||||
def serve_legacy(cls, primary: Optional[Sanic] = None) -> None:
|
||||
apps = list(cls._app_registry.values())
|
||||
|
||||
if not primary:
|
||||
try:
|
||||
primary = apps[0]
|
||||
except IndexError:
|
||||
raise RuntimeError("Did not find any applications.")
|
||||
|
||||
reloader_start = primary.listeners.get("reload_process_start")
|
||||
reloader_stop = primary.listeners.get("reload_process_stop")
|
||||
# We want to run auto_reload if ANY of the applications have it enabled
|
||||
if (
|
||||
cls.should_auto_reload()
|
||||
and os.environ.get("SANIC_SERVER_RUNNING") != "true"
|
||||
): # no cov
|
||||
loop = new_event_loop()
|
||||
trigger_events(reloader_start, loop, primary)
|
||||
reload_dirs: Set[Path] = primary.state.reload_dirs.union(
|
||||
*(app.state.reload_dirs for app in apps)
|
||||
)
|
||||
watchdog(1.0, reload_dirs)
|
||||
trigger_events(reloader_stop, loop, primary)
|
||||
return
|
||||
|
||||
# This exists primarily for unit testing
|
||||
if not primary.state.server_info: # no cov
|
||||
for app in apps:
|
||||
app.state.server_info.clear()
|
||||
return
|
||||
|
||||
primary_server_info = primary.state.server_info[0]
|
||||
primary.before_server_start(partial(primary._start_servers, apps=apps))
|
||||
|
||||
deprecation(
|
||||
f"{Colors.YELLOW}Running {Colors.SANIC}Sanic {Colors.YELLOW}w/ "
|
||||
f"LEGACY manager.{Colors.END} Support for will be dropped in "
|
||||
"version 23.3.",
|
||||
23.3,
|
||||
)
|
||||
try:
|
||||
primary_server_info.stage = ServerStage.SERVING
|
||||
|
||||
if primary.state.workers > 1 and os.name != "posix": # no cov
|
||||
logger.warning(
|
||||
f"Multiprocessing is currently not supported on {os.name},"
|
||||
" using workers=1 instead"
|
||||
)
|
||||
primary.state.workers = 1
|
||||
if primary.state.workers == 1:
|
||||
serve_single(primary_server_info.settings)
|
||||
elif primary.state.workers == 0:
|
||||
raise RuntimeError("Cannot serve with no workers")
|
||||
else:
|
||||
serve_multiple(
|
||||
primary_server_info.settings, primary.state.workers
|
||||
)
|
||||
except BaseException:
|
||||
error_logger.exception(
|
||||
"Experienced exception while trying to serve"
|
||||
)
|
||||
raise
|
||||
finally:
|
||||
primary_server_info.stage = ServerStage.STOPPED
|
||||
logger.info("Server Stopped")
|
||||
|
||||
cls._cleanup_env_vars()
|
||||
cls._cleanup_apps()
|
||||
|
||||
async def _start_servers(
|
||||
self,
|
||||
primary: Sanic,
|
||||
|
||||
@@ -3,7 +3,7 @@ from functools import partial, wraps
|
||||
from mimetypes import guess_type
|
||||
from os import PathLike, path
|
||||
from pathlib import Path, PurePath
|
||||
from typing import Optional, Sequence, Set, Union, cast
|
||||
from typing import Optional, Sequence, Set, Union
|
||||
from urllib.parse import unquote
|
||||
|
||||
from sanic_routing.route import Route
|
||||
@@ -14,7 +14,7 @@ from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE
|
||||
from sanic.exceptions import FileNotFound, HeaderNotFound, RangeNotSatisfiable
|
||||
from sanic.handlers import ContentRangeHandler
|
||||
from sanic.handlers.directory import DirectoryHandler
|
||||
from sanic.log import deprecation, error_logger
|
||||
from sanic.log import error_logger
|
||||
from sanic.mixins.base import BaseMixin
|
||||
from sanic.models.futures import FutureStatic
|
||||
from sanic.request import Request
|
||||
@@ -31,7 +31,7 @@ class StaticMixin(BaseMixin, metaclass=SanicMeta):
|
||||
def static(
|
||||
self,
|
||||
uri: str,
|
||||
file_or_directory: Union[PathLike, str, bytes],
|
||||
file_or_directory: Union[PathLike, str],
|
||||
pattern: str = r"/?.+",
|
||||
use_modified_since: bool = True,
|
||||
use_content_range: bool = False,
|
||||
@@ -94,14 +94,12 @@ class StaticMixin(BaseMixin, metaclass=SanicMeta):
|
||||
f"Static route must be a valid path, not {file_or_directory}"
|
||||
)
|
||||
|
||||
if isinstance(file_or_directory, bytes):
|
||||
deprecation(
|
||||
"Serving a static directory with a bytes string is "
|
||||
"deprecated and will be removed in v22.9.",
|
||||
22.9,
|
||||
try:
|
||||
file_or_directory = Path(file_or_directory)
|
||||
except TypeError:
|
||||
raise TypeError(
|
||||
"Static file or directory must be a path-like object or string"
|
||||
)
|
||||
file_or_directory = cast(str, file_or_directory.decode())
|
||||
file_or_directory = Path(file_or_directory)
|
||||
|
||||
if directory_handler and (directory_view or index):
|
||||
raise ValueError(
|
||||
|
||||
@@ -55,7 +55,7 @@ from sanic.headers import (
|
||||
parse_xforwarded,
|
||||
)
|
||||
from sanic.http import Stage
|
||||
from sanic.log import deprecation, error_logger
|
||||
from sanic.log import error_logger
|
||||
from sanic.models.protocol_types import TransportProtocol
|
||||
from sanic.response import BaseHTTPResponse, HTTPResponse
|
||||
|
||||
@@ -205,16 +205,6 @@ class Request:
|
||||
def generate_id(*_):
|
||||
return uuid.uuid4()
|
||||
|
||||
@property
|
||||
def request_middleware_started(self):
|
||||
deprecation(
|
||||
"Request.request_middleware_started has been deprecated and will"
|
||||
"be removed. You should set a flag on the request context using"
|
||||
"either middleware or signals if you need this feature.",
|
||||
23.3,
|
||||
)
|
||||
return self._request_middleware_started
|
||||
|
||||
@property
|
||||
def stream_id(self):
|
||||
"""
|
||||
|
||||
@@ -44,7 +44,9 @@ class Router(BaseRouter):
|
||||
raise MethodNotAllowed(
|
||||
f"Method {method} not allowed for URL {path}",
|
||||
method=method,
|
||||
allowed_methods=e.allowed_methods,
|
||||
allowed_methods=tuple(e.allowed_methods)
|
||||
if e.allowed_methods
|
||||
else None,
|
||||
) from None
|
||||
|
||||
@lru_cache(maxsize=ROUTER_CACHE_SIZE)
|
||||
@@ -133,7 +135,16 @@ class Router(BaseRouter):
|
||||
if host:
|
||||
params.update({"requirements": {"host": host}})
|
||||
|
||||
ident = name
|
||||
if len(hosts) > 1:
|
||||
ident = (
|
||||
f"{name}_{host.replace('.', '_')}"
|
||||
if name
|
||||
else "__unnamed__"
|
||||
)
|
||||
|
||||
route = super().add(**params) # type: ignore
|
||||
route.extra.ident = ident
|
||||
route.extra.ignore_body = ignore_body
|
||||
route.extra.stream = stream
|
||||
route.extra.hosts = hosts
|
||||
|
||||
@@ -2,7 +2,7 @@ from sanic.models.server_types import ConnInfo, Signal
|
||||
from sanic.server.async_server import AsyncioServer
|
||||
from sanic.server.loop import try_use_uvloop
|
||||
from sanic.server.protocols.http_protocol import HttpProtocol
|
||||
from sanic.server.runners import serve, serve_multiple, serve_single
|
||||
from sanic.server.runners import serve
|
||||
|
||||
|
||||
__all__ = (
|
||||
@@ -11,7 +11,5 @@ __all__ = (
|
||||
"HttpProtocol",
|
||||
"Signal",
|
||||
"serve",
|
||||
"serve_multiple",
|
||||
"serve_single",
|
||||
"try_use_uvloop",
|
||||
)
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
import itertools
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from time import sleep
|
||||
|
||||
|
||||
def _iter_module_files():
|
||||
"""This iterates over all relevant Python files.
|
||||
It goes through all
|
||||
loaded files from modules, all files in folders of already loaded modules
|
||||
as well as all files reachable through a package.
|
||||
"""
|
||||
# The list call is necessary on Python 3 in case the module
|
||||
# dictionary modifies during iteration.
|
||||
for module in list(sys.modules.values()):
|
||||
if module is None:
|
||||
continue
|
||||
filename = getattr(module, "__file__", None)
|
||||
if filename:
|
||||
old = None
|
||||
while not os.path.isfile(filename):
|
||||
old = filename
|
||||
filename = os.path.dirname(filename)
|
||||
if filename == old:
|
||||
break
|
||||
else:
|
||||
if filename[-4:] in (".pyc", ".pyo"):
|
||||
filename = filename[:-1]
|
||||
yield filename
|
||||
|
||||
|
||||
def _get_args_for_reloading():
|
||||
"""Returns the executable."""
|
||||
main_module = sys.modules["__main__"]
|
||||
mod_spec = getattr(main_module, "__spec__", None)
|
||||
if sys.argv[0] in ("", "-c"):
|
||||
raise RuntimeError(
|
||||
f"Autoreloader cannot work with argv[0]={sys.argv[0]!r}"
|
||||
)
|
||||
if mod_spec:
|
||||
# Parent exe was launched as a module rather than a script
|
||||
return [sys.executable, "-m", mod_spec.name] + sys.argv[1:]
|
||||
return [sys.executable] + sys.argv
|
||||
|
||||
|
||||
def restart_with_reloader(changed=None):
|
||||
"""Create a new process and a subprocess in it with the same arguments as
|
||||
this one.
|
||||
"""
|
||||
reloaded = ",".join(changed) if changed else ""
|
||||
return subprocess.Popen( # nosec B603
|
||||
_get_args_for_reloading(),
|
||||
env={
|
||||
**os.environ,
|
||||
"SANIC_SERVER_RUNNING": "true",
|
||||
"SANIC_RELOADER_PROCESS": "true",
|
||||
"SANIC_RELOADED_FILES": reloaded,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _check_file(filename, mtimes):
|
||||
need_reload = False
|
||||
|
||||
mtime = os.stat(filename).st_mtime
|
||||
old_time = mtimes.get(filename)
|
||||
if old_time is None:
|
||||
mtimes[filename] = mtime
|
||||
elif mtime > old_time:
|
||||
mtimes[filename] = mtime
|
||||
need_reload = True
|
||||
|
||||
return need_reload
|
||||
|
||||
|
||||
def watchdog(sleep_interval, reload_dirs):
|
||||
"""Watch project files, restart worker process if a change happened.
|
||||
:param sleep_interval: interval in second.
|
||||
:return: Nothing
|
||||
"""
|
||||
|
||||
def interrupt_self(*args):
|
||||
raise KeyboardInterrupt
|
||||
|
||||
mtimes = {}
|
||||
signal.signal(signal.SIGTERM, interrupt_self)
|
||||
if os.name == "nt":
|
||||
signal.signal(signal.SIGBREAK, interrupt_self)
|
||||
|
||||
worker_process = restart_with_reloader()
|
||||
|
||||
try:
|
||||
while True:
|
||||
changed = set()
|
||||
for filename in itertools.chain(
|
||||
_iter_module_files(),
|
||||
*(d.glob("**/*") for d in reload_dirs),
|
||||
):
|
||||
try:
|
||||
if _check_file(filename, mtimes):
|
||||
path = (
|
||||
filename
|
||||
if isinstance(filename, str)
|
||||
else filename.resolve()
|
||||
)
|
||||
changed.add(str(path))
|
||||
except OSError:
|
||||
continue
|
||||
|
||||
if changed:
|
||||
worker_process.terminate()
|
||||
worker_process.wait()
|
||||
worker_process = restart_with_reloader(changed)
|
||||
|
||||
sleep(sleep_interval)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
worker_process.terminate()
|
||||
worker_process.wait()
|
||||
@@ -9,19 +9,17 @@ from sanic.config import Config
|
||||
from sanic.exceptions import ServerError
|
||||
from sanic.http.constants import HTTP
|
||||
from sanic.http.tls import get_ssl_context
|
||||
from sanic.server.events import trigger_events
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sanic.app import Sanic
|
||||
|
||||
import asyncio
|
||||
import multiprocessing
|
||||
import os
|
||||
import socket
|
||||
|
||||
from functools import partial
|
||||
from signal import SIG_IGN, SIGINT, SIGTERM, Signals
|
||||
from signal import SIG_IGN, SIGINT, SIGTERM
|
||||
from signal import signal as signal_func
|
||||
|
||||
from sanic.application.ext import setup_ext
|
||||
@@ -31,11 +29,7 @@ from sanic.log import error_logger, server_logger
|
||||
from sanic.models.server_types import Signal
|
||||
from sanic.server.async_server import AsyncioServer
|
||||
from sanic.server.protocols.http_protocol import Http3Protocol, HttpProtocol
|
||||
from sanic.server.socket import (
|
||||
bind_socket,
|
||||
bind_unix_socket,
|
||||
remove_unix_socket,
|
||||
)
|
||||
from sanic.server.socket import bind_unix_socket, remove_unix_socket
|
||||
|
||||
|
||||
try:
|
||||
@@ -319,94 +313,6 @@ def _serve_http_3(
|
||||
)
|
||||
|
||||
|
||||
def serve_single(server_settings):
|
||||
main_start = server_settings.pop("main_start", None)
|
||||
main_stop = server_settings.pop("main_stop", None)
|
||||
|
||||
if not server_settings.get("run_async"):
|
||||
# create new event_loop after fork
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
server_settings["loop"] = loop
|
||||
|
||||
trigger_events(main_start, server_settings["loop"])
|
||||
serve(**server_settings)
|
||||
trigger_events(main_stop, server_settings["loop"])
|
||||
|
||||
server_settings["loop"].close()
|
||||
|
||||
|
||||
def serve_multiple(server_settings, workers):
|
||||
"""Start multiple server processes simultaneously. Stop on interrupt
|
||||
and terminate signals, and drain connections when complete.
|
||||
|
||||
:param server_settings: kw arguments to be passed to the serve function
|
||||
:param workers: number of workers to launch
|
||||
:param stop_event: if provided, is used as a stop signal
|
||||
:return:
|
||||
"""
|
||||
server_settings["reuse_port"] = True
|
||||
server_settings["run_multiple"] = True
|
||||
|
||||
main_start = server_settings.pop("main_start", None)
|
||||
main_stop = server_settings.pop("main_stop", None)
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
trigger_events(main_start, loop)
|
||||
|
||||
# Create a listening socket or use the one in settings
|
||||
sock = server_settings.get("sock")
|
||||
unix = server_settings["unix"]
|
||||
backlog = server_settings["backlog"]
|
||||
if unix:
|
||||
sock = bind_unix_socket(unix, backlog=backlog)
|
||||
server_settings["unix"] = unix
|
||||
if sock is None:
|
||||
sock = bind_socket(
|
||||
server_settings["host"], server_settings["port"], backlog=backlog
|
||||
)
|
||||
sock.set_inheritable(True)
|
||||
server_settings["sock"] = sock
|
||||
server_settings["host"] = None
|
||||
server_settings["port"] = None
|
||||
|
||||
processes = []
|
||||
|
||||
def sig_handler(signal, frame):
|
||||
server_logger.info(
|
||||
"Received signal %s. Shutting down.", Signals(signal).name
|
||||
)
|
||||
for process in processes:
|
||||
os.kill(process.pid, SIGTERM)
|
||||
|
||||
signal_func(SIGINT, lambda s, f: sig_handler(s, f))
|
||||
signal_func(SIGTERM, lambda s, f: sig_handler(s, f))
|
||||
mp = multiprocessing.get_context("fork")
|
||||
|
||||
for _ in range(workers):
|
||||
process = mp.Process(
|
||||
target=serve,
|
||||
kwargs=server_settings,
|
||||
)
|
||||
process.daemon = True
|
||||
process.start()
|
||||
processes.append(process)
|
||||
|
||||
for process in processes:
|
||||
process.join()
|
||||
|
||||
# the above processes will block this until they're stopped
|
||||
for process in processes:
|
||||
process.terminate()
|
||||
|
||||
trigger_events(main_stop, loop)
|
||||
|
||||
sock.close()
|
||||
loop.close()
|
||||
remove_unix_socket(unix)
|
||||
|
||||
|
||||
def _build_protocol_kwargs(
|
||||
protocol: Type[asyncio.Protocol], config: Config
|
||||
) -> Dict[str, Union[int, float]]:
|
||||
|
||||
@@ -29,7 +29,7 @@ except ImportError: # websockets >= 11.0
|
||||
|
||||
from websockets.typing import Data
|
||||
|
||||
from sanic.log import deprecation, error_logger, logger
|
||||
from sanic.log import error_logger, logger
|
||||
from sanic.server.protocols.base_protocol import SanicProtocol
|
||||
|
||||
from ...exceptions import ServerError, WebsocketClosed
|
||||
@@ -99,15 +99,6 @@ class WebsocketImplProtocol:
|
||||
def subprotocol(self):
|
||||
return self.ws_proto.subprotocol
|
||||
|
||||
@property
|
||||
def connection(self):
|
||||
deprecation(
|
||||
"The connection property has been deprecated and will be removed. "
|
||||
"Please use the ws_proto property instead going forward.",
|
||||
22.6,
|
||||
)
|
||||
return self.ws_proto
|
||||
|
||||
def pause_frames(self):
|
||||
if not self.can_pause:
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user