Set multiprocessing start method early (#2776)

This commit is contained in:
Adam Hopkins 2023-07-09 22:34:15 +03:00 committed by GitHub
parent 4a2b82e42e
commit 666371bb92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 35 additions and 6 deletions

View File

@ -29,6 +29,7 @@ from typing import (
AnyStr, AnyStr,
Awaitable, Awaitable,
Callable, Callable,
ClassVar,
Coroutine, Coroutine,
Deque, Deque,
Dict, Dict,
@ -158,8 +159,8 @@ class Sanic(StaticHandleMixin, BaseSanic, StartupMixin, metaclass=TouchUpMeta):
"websocket_tasks", "websocket_tasks",
) )
_app_registry: Dict[str, "Sanic"] = {} _app_registry: ClassVar[Dict[str, "Sanic"]] = {}
test_mode = False test_mode: ClassVar[bool] = False
def __init__( def __init__(
self, self,

View File

@ -16,7 +16,13 @@ from asyncio import (
from contextlib import suppress from contextlib import suppress
from functools import partial from functools import partial
from importlib import import_module from importlib import import_module
from multiprocessing import Manager, Pipe, get_context from multiprocessing import (
Manager,
Pipe,
get_context,
get_start_method,
set_start_method,
)
from multiprocessing.context import BaseContext from multiprocessing.context import BaseContext
from pathlib import Path from pathlib import Path
from socket import SHUT_RDWR, socket from socket import SHUT_RDWR, socket
@ -25,6 +31,7 @@ from typing import (
TYPE_CHECKING, TYPE_CHECKING,
Any, Any,
Callable, Callable,
ClassVar,
Dict, Dict,
List, List,
Mapping, Mapping,
@ -81,13 +88,17 @@ else: # no cov
class StartupMixin(metaclass=SanicMeta): class StartupMixin(metaclass=SanicMeta):
_app_registry: Dict[str, Sanic] _app_registry: ClassVar[Dict[str, Sanic]]
config: Config config: Config
listeners: Dict[str, List[ListenerType[Any]]] listeners: Dict[str, List[ListenerType[Any]]]
state: ApplicationState state: ApplicationState
websocket_enabled: bool websocket_enabled: bool
multiplexer: WorkerMultiplexer multiplexer: WorkerMultiplexer
start_method: StartMethod = _default
test_mode: ClassVar[bool]
start_method: ClassVar[StartMethod] = _default
START_METHOD_SET: ClassVar[bool] = False
def setup_loop(self): def setup_loop(self):
if not self.asgi: if not self.asgi:
@ -691,11 +702,26 @@ class StartupMixin(metaclass=SanicMeta):
else "spawn" else "spawn"
) )
@classmethod
def _set_startup_method(cls) -> None:
if cls.START_METHOD_SET and not cls.test_mode:
return
method = cls._get_startup_method()
set_start_method(method, force=cls.test_mode)
cls.START_METHOD_SET = True
@classmethod @classmethod
def _get_context(cls) -> BaseContext: def _get_context(cls) -> BaseContext:
method = cls._get_startup_method() method = cls._get_startup_method()
logger.debug("Creating multiprocessing context using '%s'", method) logger.debug("Creating multiprocessing context using '%s'", method)
return get_context(method) actual = get_start_method()
if method != actual:
raise RuntimeError(
f"Start method '{method}' was requested, but '{actual}' "
"was actually set."
)
return get_context()
@classmethod @classmethod
def serve( def serve(
@ -705,6 +731,7 @@ class StartupMixin(metaclass=SanicMeta):
app_loader: Optional[AppLoader] = None, app_loader: Optional[AppLoader] = None,
factory: Optional[Callable[[], Sanic]] = None, factory: Optional[Callable[[], Sanic]] = None,
) -> None: ) -> None:
cls._set_startup_method()
os.environ["SANIC_MOTD_OUTPUT"] = "true" os.environ["SANIC_MOTD_OUTPUT"] = "true"
apps = list(cls._app_registry.values()) apps = list(cls._app_registry.values())
if factory: if factory:

View File

@ -3,6 +3,7 @@ import logging
import pytest import pytest
import sanic import sanic
from sanic import Sanic from sanic import Sanic
from sanic.config import Config from sanic.config import Config
from sanic.errorpages import TextRenderer, exception_response, guess_mime from sanic.errorpages import TextRenderer, exception_response, guess_mime