Optional uvloop use (#2264)
Co-authored-by: Adam Hopkins <adam@amhopkins.com> Co-authored-by: Adam Hopkins <admhpkns@gmail.com>
This commit is contained in:
parent
4659069350
commit
98ce4bdeb2
44
sanic/app.py
44
sanic/app.py
|
@ -69,6 +69,7 @@ from sanic.exceptions import (
|
|||
URLBuildError,
|
||||
)
|
||||
from sanic.handlers import ErrorHandler
|
||||
from sanic.helpers import _default
|
||||
from sanic.http import Stage
|
||||
from sanic.log import LOGGING_CONFIG_DEFAULTS, Colors, error_logger, logger
|
||||
from sanic.mixins.listeners import ListenerEvent
|
||||
|
@ -88,7 +89,7 @@ from sanic.response import BaseHTTPResponse, HTTPResponse
|
|||
from sanic.router import Router
|
||||
from sanic.server import AsyncioServer, HttpProtocol
|
||||
from sanic.server import Signal as ServerSignal
|
||||
from sanic.server import serve, serve_multiple, serve_single
|
||||
from sanic.server import serve, serve_multiple, serve_single, try_use_uvloop
|
||||
from sanic.server.protocols.websocket_protocol import WebSocketProtocol
|
||||
from sanic.server.websockets.impl import ConnectionClosed
|
||||
from sanic.signals import Signal, SignalRouter
|
||||
|
@ -130,6 +131,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
"_task_registry",
|
||||
"_test_client",
|
||||
"_test_manager",
|
||||
"_uvloop_setting", # TODO: Remove in v22.6
|
||||
"asgi",
|
||||
"auto_reload",
|
||||
"auto_reload",
|
||||
|
@ -159,6 +161,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
)
|
||||
|
||||
_app_registry: Dict[str, "Sanic"] = {}
|
||||
_uvloop_setting = None
|
||||
test_mode = False
|
||||
|
||||
def __init__(
|
||||
|
@ -1142,6 +1145,11 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
register_sys_signals=register_sys_signals,
|
||||
)
|
||||
|
||||
if self.config.USE_UVLOOP is True or (
|
||||
self.config.USE_UVLOOP is _default and not OS_IS_WINDOWS
|
||||
):
|
||||
try_use_uvloop()
|
||||
|
||||
try:
|
||||
self.is_running = True
|
||||
self.is_stopping = False
|
||||
|
@ -1239,12 +1247,13 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
WebSocketProtocol if self.websocket_enabled else HttpProtocol
|
||||
)
|
||||
|
||||
# if access_log is passed explicitly change config.ACCESS_LOG
|
||||
if access_log is not None:
|
||||
self.config.ACCESS_LOG = access_log
|
||||
|
||||
if noisy_exceptions is not None:
|
||||
self.config.NOISY_EXCEPTIONS = noisy_exceptions
|
||||
# Set explicitly passed configuration values
|
||||
for attribute, value in {
|
||||
"ACCESS_LOG": access_log,
|
||||
"NOISY_EXCEPTIONS": noisy_exceptions,
|
||||
}.items():
|
||||
if value is not None:
|
||||
setattr(self.config, attribute, value)
|
||||
|
||||
server_settings = self._helper(
|
||||
host=host,
|
||||
|
@ -1259,6 +1268,14 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
run_async=return_asyncio_server,
|
||||
)
|
||||
|
||||
if self.config.USE_UVLOOP is not _default:
|
||||
error_logger.warning(
|
||||
"You are trying to change the uvloop configuration, but "
|
||||
"this is only effective when using the run(...) method. "
|
||||
"When using the create_server(...) method Sanic will use "
|
||||
"the already existing loop."
|
||||
)
|
||||
|
||||
main_start = server_settings.pop("main_start", None)
|
||||
main_stop = server_settings.pop("main_stop", None)
|
||||
if main_start or main_stop:
|
||||
|
@ -1833,6 +1850,19 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
self._future_registry.clear()
|
||||
self.signalize()
|
||||
self.finalize()
|
||||
|
||||
# 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
|
||||
|
||||
ErrorHandler.finalize(self.error_handler, config=self.config)
|
||||
TouchUp.run(self)
|
||||
self.state.is_started = True
|
||||
|
|
|
@ -7,6 +7,7 @@ import sanic.app # noqa
|
|||
|
||||
from sanic.compat import Header
|
||||
from sanic.exceptions import ServerError
|
||||
from sanic.helpers import _default
|
||||
from sanic.http import Stage
|
||||
from sanic.models.asgi import ASGIReceive, ASGIScope, ASGISend, MockTransport
|
||||
from sanic.request import Request
|
||||
|
@ -53,6 +54,13 @@ class Lifespan:
|
|||
await self.asgi_app.sanic_app._server_event("init", "before")
|
||||
await self.asgi_app.sanic_app._server_event("init", "after")
|
||||
|
||||
if self.asgi_app.sanic_app.config.USE_UVLOOP is not _default:
|
||||
warnings.warn(
|
||||
"You have set the USE_UVLOOP configuration option, but Sanic "
|
||||
"cannot control the event loop when running in ASGI mode."
|
||||
"This option will be ignored."
|
||||
)
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
"""
|
||||
Gather the listeners to fire on server stop.
|
||||
|
|
|
@ -8,6 +8,14 @@ from multidict import CIMultiDict # type: ignore
|
|||
|
||||
|
||||
OS_IS_WINDOWS = os.name == "nt"
|
||||
UVLOOP_INSTALLED = False
|
||||
|
||||
try:
|
||||
import uvloop # type: ignore # noqa
|
||||
|
||||
UVLOOP_INSTALLED = True
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def enable_windows_color_support():
|
||||
|
|
|
@ -7,7 +7,7 @@ from typing import Any, Callable, Dict, Optional, Sequence, Union
|
|||
from warnings import warn
|
||||
|
||||
from sanic.errorpages import DEFAULT_FORMAT, check_error_format
|
||||
from sanic.helpers import _default
|
||||
from sanic.helpers import Default, _default
|
||||
from sanic.http import Http
|
||||
from sanic.log import error_logger
|
||||
from sanic.utils import load_module_from_file_location, str_to_bool
|
||||
|
@ -38,6 +38,7 @@ DEFAULT_CONFIG = {
|
|||
"REQUEST_MAX_SIZE": 100000000, # 100 megabytes
|
||||
"REQUEST_TIMEOUT": 60, # 60 seconds
|
||||
"RESPONSE_TIMEOUT": 60, # 60 seconds
|
||||
"USE_UVLOOP": _default,
|
||||
"WEBSOCKET_MAX_SIZE": 2 ** 20, # 1 megabyte
|
||||
"WEBSOCKET_PING_INTERVAL": 20,
|
||||
"WEBSOCKET_PING_TIMEOUT": 20,
|
||||
|
@ -79,6 +80,7 @@ class Config(dict, metaclass=DescriptorMeta):
|
|||
REQUEST_TIMEOUT: int
|
||||
RESPONSE_TIMEOUT: int
|
||||
SERVER_NAME: str
|
||||
USE_UVLOOP: Union[Default, bool]
|
||||
WEBSOCKET_MAX_SIZE: int
|
||||
WEBSOCKET_PING_INTERVAL: int
|
||||
WEBSOCKET_PING_TIMEOUT: int
|
||||
|
|
|
@ -1,20 +1,10 @@
|
|||
import asyncio
|
||||
|
||||
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
|
||||
|
||||
|
||||
try:
|
||||
import uvloop # type: ignore
|
||||
|
||||
if not isinstance(asyncio.get_event_loop_policy(), uvloop.EventLoopPolicy):
|
||||
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
__all__ = (
|
||||
"AsyncioServer",
|
||||
"ConnInfo",
|
||||
|
@ -23,4 +13,5 @@ __all__ = (
|
|||
"serve",
|
||||
"serve_multiple",
|
||||
"serve_single",
|
||||
"try_use_uvloop",
|
||||
)
|
||||
|
|
49
sanic/server/loop.py
Normal file
49
sanic/server/loop.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
import asyncio
|
||||
|
||||
from distutils.util import strtobool
|
||||
from os import getenv
|
||||
|
||||
from sanic.compat import OS_IS_WINDOWS
|
||||
from sanic.log import error_logger
|
||||
|
||||
|
||||
def try_use_uvloop() -> None:
|
||||
"""
|
||||
Use uvloop instead of the default asyncio loop.
|
||||
"""
|
||||
if OS_IS_WINDOWS:
|
||||
error_logger.warning(
|
||||
"You are trying to use uvloop, but uvloop is not compatible "
|
||||
"with your system. You can disable uvloop completely by setting "
|
||||
"the 'USE_UVLOOP' configuration value to false, or simply not "
|
||||
"defining it and letting Sanic handle it for you. Sanic will now "
|
||||
"continue to run using the default event loop."
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
import uvloop # type: ignore
|
||||
except ImportError:
|
||||
error_logger.warning(
|
||||
"You are trying to use uvloop, but uvloop is not "
|
||||
"installed in your system. In order to use uvloop "
|
||||
"you must first install it. Otherwise, you can disable "
|
||||
"uvloop completely by setting the 'USE_UVLOOP' "
|
||||
"configuration value to false. Sanic will now continue "
|
||||
"to run with the default event loop."
|
||||
)
|
||||
return
|
||||
|
||||
uvloop_install_removed = strtobool(getenv("SANIC_NO_UVLOOP", "no"))
|
||||
if uvloop_install_removed:
|
||||
error_logger.info(
|
||||
"You are requesting to run Sanic using uvloop, but the "
|
||||
"install-time 'SANIC_NO_UVLOOP' environment variable (used to "
|
||||
"opt-out of installing uvloop with Sanic) is set to true. If "
|
||||
"you want to prevent Sanic from overriding the event loop policy "
|
||||
"during runtime, set the 'USE_UVLOOP' configuration value to "
|
||||
"false."
|
||||
)
|
||||
|
||||
if not isinstance(asyncio.get_event_loop_policy(), uvloop.EventLoopPolicy):
|
||||
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
|
@ -7,8 +7,9 @@ import traceback
|
|||
|
||||
from gunicorn.workers import base # type: ignore
|
||||
|
||||
from sanic.compat import UVLOOP_INSTALLED
|
||||
from sanic.log import logger
|
||||
from sanic.server import HttpProtocol, Signal, serve
|
||||
from sanic.server import HttpProtocol, Signal, serve, try_use_uvloop
|
||||
from sanic.server.protocols.websocket_protocol import WebSocketProtocol
|
||||
|
||||
|
||||
|
@ -17,12 +18,8 @@ try:
|
|||
except ImportError:
|
||||
ssl = None # type: ignore
|
||||
|
||||
try:
|
||||
import uvloop # type: ignore
|
||||
|
||||
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
||||
except ImportError:
|
||||
pass
|
||||
if UVLOOP_INSTALLED:
|
||||
try_use_uvloop()
|
||||
|
||||
|
||||
class GunicornWorker(base.Worker):
|
||||
|
|
|
@ -2,16 +2,20 @@ import asyncio
|
|||
import logging
|
||||
import re
|
||||
|
||||
from collections import Counter
|
||||
from inspect import isawaitable
|
||||
from os import environ
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
import sanic
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.compat import OS_IS_WINDOWS, UVLOOP_INSTALLED
|
||||
from sanic.config import Config
|
||||
from sanic.exceptions import SanicException
|
||||
from sanic.response import text
|
||||
from sanic.helpers import _default
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
@ -19,15 +23,6 @@ def clear_app_registry():
|
|||
Sanic._app_registry = {}
|
||||
|
||||
|
||||
def uvloop_installed():
|
||||
try:
|
||||
import uvloop # noqa
|
||||
|
||||
return True
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
|
||||
def test_app_loop_running(app):
|
||||
@app.get("/test")
|
||||
async def handler(request):
|
||||
|
@ -472,6 +467,102 @@ def test_custom_context():
|
|||
assert app.ctx == ctx
|
||||
|
||||
|
||||
def test_uvloop_config(app, monkeypatch):
|
||||
@app.get("/test")
|
||||
def handler(request):
|
||||
return text("ok")
|
||||
|
||||
try_use_uvloop = Mock()
|
||||
monkeypatch.setattr(sanic.app, "try_use_uvloop", try_use_uvloop)
|
||||
|
||||
# Default config
|
||||
app.test_client.get("/test")
|
||||
if OS_IS_WINDOWS:
|
||||
try_use_uvloop.assert_not_called()
|
||||
else:
|
||||
try_use_uvloop.assert_called_once()
|
||||
|
||||
try_use_uvloop.reset_mock()
|
||||
app.config["USE_UVLOOP"] = False
|
||||
app.test_client.get("/test")
|
||||
try_use_uvloop.assert_not_called()
|
||||
|
||||
try_use_uvloop.reset_mock()
|
||||
app.config["USE_UVLOOP"] = True
|
||||
app.test_client.get("/test")
|
||||
try_use_uvloop.assert_called_once()
|
||||
|
||||
|
||||
def test_uvloop_cannot_never_called_with_create_server(caplog, monkeypatch):
|
||||
apps = (
|
||||
Sanic("default-uvloop"),
|
||||
Sanic("no-uvloop"),
|
||||
Sanic("yes-uvloop")
|
||||
)
|
||||
|
||||
apps[1].config.USE_UVLOOP = False
|
||||
apps[2].config.USE_UVLOOP = True
|
||||
|
||||
try_use_uvloop = Mock()
|
||||
monkeypatch.setattr(sanic.app, "try_use_uvloop", try_use_uvloop)
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
with caplog.at_level(logging.WARNING):
|
||||
for app in apps:
|
||||
srv_coro = app.create_server(
|
||||
return_asyncio_server=True,
|
||||
asyncio_server_kwargs=dict(start_serving=False)
|
||||
)
|
||||
loop.run_until_complete(srv_coro)
|
||||
|
||||
try_use_uvloop.assert_not_called() # Check it didn't try to change policy
|
||||
|
||||
message = (
|
||||
"You are trying to change the uvloop configuration, but "
|
||||
"this is only effective when using the run(...) method. "
|
||||
"When using the create_server(...) method Sanic will use "
|
||||
"the already existing loop."
|
||||
)
|
||||
|
||||
counter = Counter([(r[1], r[2]) for r in caplog.record_tuples])
|
||||
modified = sum(1 for app in apps if app.config.USE_UVLOOP is not _default)
|
||||
|
||||
assert counter[(logging.WARNING, message)] == modified
|
||||
|
||||
|
||||
def test_multiple_uvloop_configs_display_warning(caplog):
|
||||
Sanic._uvloop_setting = None # Reset the setting (changed in prev tests)
|
||||
|
||||
default_uvloop = Sanic("default-uvloop")
|
||||
no_uvloop = Sanic("no-uvloop")
|
||||
yes_uvloop = Sanic("yes-uvloop")
|
||||
|
||||
no_uvloop.config.USE_UVLOOP = False
|
||||
yes_uvloop.config.USE_UVLOOP = True
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
with caplog.at_level(logging.WARNING):
|
||||
for app in (default_uvloop, no_uvloop, yes_uvloop):
|
||||
srv_coro = app.create_server(
|
||||
return_asyncio_server=True,
|
||||
asyncio_server_kwargs=dict(start_serving=False)
|
||||
)
|
||||
srv = loop.run_until_complete(srv_coro)
|
||||
loop.run_until_complete(srv.startup())
|
||||
|
||||
message = (
|
||||
"It looks like you're running several apps with different "
|
||||
"uvloop settings. This is not supported and may lead to "
|
||||
"unintended behaviour."
|
||||
)
|
||||
|
||||
counter = Counter([(r[1], r[2]) for r in caplog.record_tuples])
|
||||
|
||||
assert counter[(logging.WARNING, message)] == 2
|
||||
|
||||
|
||||
def test_cannot_run_fast_and_workers(app):
|
||||
message = "You cannot use both fast=True and workers=X"
|
||||
with pytest.raises(RuntimeError, match=message):
|
||||
|
|
|
@ -145,6 +145,37 @@ def test_listeners_triggered_async(app):
|
|||
assert after_server_stop
|
||||
|
||||
|
||||
def test_non_default_uvloop_config_raises_warning(app):
|
||||
app.config.USE_UVLOOP = True
|
||||
|
||||
class CustomServer(uvicorn.Server):
|
||||
def install_signal_handlers(self):
|
||||
pass
|
||||
|
||||
config = uvicorn.Config(app=app, loop="asyncio", limit_max_requests=0)
|
||||
server = CustomServer(config=config)
|
||||
|
||||
with pytest.warns(UserWarning) as records:
|
||||
server.run()
|
||||
|
||||
all_tasks = asyncio.all_tasks(asyncio.get_event_loop())
|
||||
for task in all_tasks:
|
||||
task.cancel()
|
||||
|
||||
msg = ""
|
||||
for record in records:
|
||||
_msg = str(record.message)
|
||||
if _msg.startswith("You have set the USE_UVLOOP configuration"):
|
||||
msg = _msg
|
||||
break
|
||||
|
||||
assert msg == (
|
||||
"You have set the USE_UVLOOP configuration option, but Sanic "
|
||||
"cannot control the event loop when running in ASGI mode."
|
||||
"This option will be ignored."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mockprotocol_events(protocol):
|
||||
assert protocol._not_paused.is_set()
|
||||
|
|
|
@ -271,9 +271,13 @@ def test_exception_in_ws_logged(caplog):
|
|||
with caplog.at_level(logging.INFO):
|
||||
app.test_client.websocket("/feed")
|
||||
|
||||
error_logs = [r for r in caplog.record_tuples if r[0] == "sanic.error"]
|
||||
assert error_logs[1][1] == logging.ERROR
|
||||
assert "Exception occurred while handling uri:" in error_logs[1][2]
|
||||
for record in caplog.record_tuples:
|
||||
if record[2].startswith("Exception occurred"):
|
||||
break
|
||||
|
||||
assert record[0] == "sanic.error"
|
||||
assert record[1] == logging.ERROR
|
||||
assert "Exception occurred while handling uri:" in record[2]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("debug", (True, False))
|
||||
|
|
|
@ -2,7 +2,6 @@ import asyncio
|
|||
import logging
|
||||
import time
|
||||
|
||||
from collections import Counter
|
||||
from multiprocessing import Process
|
||||
|
||||
import httpx
|
||||
|
@ -36,11 +35,14 @@ def test_no_exceptions_when_cancel_pending_request(app, caplog):
|
|||
|
||||
p.kill()
|
||||
|
||||
counter = Counter([r[1] for r in caplog.record_tuples])
|
||||
|
||||
assert counter[logging.INFO] == 11
|
||||
assert logging.ERROR not in counter
|
||||
assert (
|
||||
caplog.record_tuples[9][2]
|
||||
== "Request: GET http://127.0.0.1:8000/ stopped. Transport is closed."
|
||||
info = 0
|
||||
for record in caplog.record_tuples:
|
||||
assert record[1] != logging.ERROR
|
||||
if record[1] == logging.INFO:
|
||||
info += 1
|
||||
if record[2].startswith("Request:"):
|
||||
assert record[2] == (
|
||||
"Request: GET http://127.0.0.1:8000/ stopped. "
|
||||
"Transport is closed."
|
||||
)
|
||||
assert info == 11
|
||||
|
|
116
tests/test_server_loop.py
Normal file
116
tests/test_server_loop.py
Normal file
|
@ -0,0 +1,116 @@
|
|||
import logging
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic.server import loop
|
||||
from sanic.compat import OS_IS_WINDOWS, UVLOOP_INSTALLED
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not OS_IS_WINDOWS,
|
||||
reason="Not testable with current client",
|
||||
)
|
||||
def test_raises_warning_if_os_is_windows(caplog):
|
||||
with caplog.at_level(logging.WARNING):
|
||||
loop.try_use_uvloop()
|
||||
|
||||
for record in caplog.records:
|
||||
if record.message.startswith("You are trying to use"):
|
||||
break
|
||||
|
||||
assert record.message == (
|
||||
"You are trying to use uvloop, but uvloop is not compatible "
|
||||
"with your system. You can disable uvloop completely by setting "
|
||||
"the 'USE_UVLOOP' configuration value to false, or simply not "
|
||||
"defining it and letting Sanic handle it for you. Sanic will now "
|
||||
"continue to run using the default event loop."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
OS_IS_WINDOWS or UVLOOP_INSTALLED,
|
||||
reason="Not testable with current client",
|
||||
)
|
||||
def test_raises_warning_if_uvloop_not_installed(caplog):
|
||||
with caplog.at_level(logging.WARNING):
|
||||
loop.try_use_uvloop()
|
||||
|
||||
for record in caplog.records:
|
||||
if record.message.startswith("You are trying to use"):
|
||||
break
|
||||
|
||||
assert record.message == (
|
||||
"You are trying to use uvloop, but uvloop is not "
|
||||
"installed in your system. In order to use uvloop "
|
||||
"you must first install it. Otherwise, you can disable "
|
||||
"uvloop completely by setting the 'USE_UVLOOP' "
|
||||
"configuration value to false. Sanic will now continue "
|
||||
"to run with the default event loop."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
OS_IS_WINDOWS or not UVLOOP_INSTALLED,
|
||||
reason="Not testable with current client",
|
||||
)
|
||||
def test_logs_when_install_and_runtime_config_mismatch(caplog, monkeypatch):
|
||||
getenv = Mock(return_value="no")
|
||||
monkeypatch.setattr(loop, "getenv", getenv)
|
||||
|
||||
with caplog.at_level(logging.INFO):
|
||||
loop.try_use_uvloop()
|
||||
|
||||
getenv.assert_called_once_with("SANIC_NO_UVLOOP", "no")
|
||||
assert caplog.record_tuples == []
|
||||
|
||||
getenv = Mock(return_value="yes")
|
||||
monkeypatch.setattr(loop, "getenv", getenv)
|
||||
with caplog.at_level(logging.INFO):
|
||||
loop.try_use_uvloop()
|
||||
|
||||
getenv.assert_called_once_with("SANIC_NO_UVLOOP", "no")
|
||||
for record in caplog.records:
|
||||
if record.message.startswith("You are requesting to run"):
|
||||
break
|
||||
|
||||
assert record.message == (
|
||||
"You are requesting to run Sanic using uvloop, but the "
|
||||
"install-time 'SANIC_NO_UVLOOP' environment variable (used to "
|
||||
"opt-out of installing uvloop with Sanic) is set to true. If "
|
||||
"you want to prevent Sanic from overriding the event loop policy "
|
||||
"during runtime, set the 'USE_UVLOOP' configuration value to "
|
||||
"false."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
OS_IS_WINDOWS or not UVLOOP_INSTALLED,
|
||||
reason="Not testable with current client",
|
||||
)
|
||||
def test_sets_loop_policy_only_when_not_already_set(monkeypatch):
|
||||
import uvloop # type: ignore
|
||||
|
||||
# Existing policy is not uvloop.EventLoopPolicy
|
||||
get_event_loop_policy = Mock(return_value=None)
|
||||
monkeypatch.setattr(
|
||||
loop.asyncio, "get_event_loop_policy", get_event_loop_policy
|
||||
)
|
||||
|
||||
with patch("asyncio.set_event_loop_policy") as set_event_loop_policy:
|
||||
loop.try_use_uvloop()
|
||||
set_event_loop_policy.assert_called_once()
|
||||
args, _ = set_event_loop_policy.call_args
|
||||
policy = args[0]
|
||||
assert isinstance(policy, uvloop.EventLoopPolicy)
|
||||
|
||||
# Existing policy is uvloop.EventLoopPolicy
|
||||
get_event_loop_policy = Mock(return_value=policy)
|
||||
monkeypatch.setattr(
|
||||
loop.asyncio, "get_event_loop_policy", get_event_loop_policy
|
||||
)
|
||||
|
||||
with patch("asyncio.set_event_loop_policy") as set_event_loop_policy:
|
||||
loop.try_use_uvloop()
|
||||
set_event_loop_policy.assert_not_called()
|
Loading…
Reference in New Issue
Block a user