uvicorn 0.34.1__tar.gz → 0.34.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {uvicorn-0.34.1 → uvicorn-0.34.3}/PKG-INFO +2 -2
- {uvicorn-0.34.1 → uvicorn-0.34.3}/pyproject.toml +1 -1
- {uvicorn-0.34.1 → uvicorn-0.34.3}/requirements.txt +5 -6
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/middleware/test_logging.py +4 -3
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/protocols/test_websocket.py +5 -5
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/supervisors/test_reload.py +6 -14
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/test_config.py +6 -6
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/__init__.py +1 -1
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/config.py +1 -1
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/protocols/websockets/auto.py +2 -2
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/protocols/websockets/websockets_impl.py +4 -3
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/protocols/websockets/wsproto_impl.py +11 -11
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/supervisors/basereload.py +4 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/supervisors/watchfilesreload.py +1 -4
- {uvicorn-0.34.1 → uvicorn-0.34.3}/.gitignore +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/LICENSE.md +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/README.md +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/__init__.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/conftest.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/importer/__init__.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/importer/circular_import_a.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/importer/circular_import_b.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/importer/raise_import_error.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/importer/test_importer.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/middleware/__init__.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/middleware/test_message_logger.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/middleware/test_proxy_headers.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/middleware/test_wsgi.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/protocols/__init__.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/protocols/test_http.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/protocols/test_utils.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/response.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/supervisors/__init__.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/supervisors/test_multiprocess.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/supervisors/test_signal.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/test_auto_detection.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/test_cli.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/test_default_headers.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/test_lifespan.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/test_main.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/test_server.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/test_ssl.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/test_subprocess.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/tests/utils.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/__main__.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/_subprocess.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/_types.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/importer.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/lifespan/__init__.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/lifespan/off.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/lifespan/on.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/logging.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/loops/__init__.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/loops/asyncio.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/loops/auto.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/loops/uvloop.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/main.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/middleware/__init__.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/middleware/asgi2.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/middleware/message_logger.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/middleware/proxy_headers.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/middleware/wsgi.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/protocols/__init__.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/protocols/http/__init__.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/protocols/http/auto.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/protocols/http/flow_control.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/protocols/http/h11_impl.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/protocols/http/httptools_impl.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/protocols/utils.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/protocols/websockets/__init__.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/py.typed +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/server.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/supervisors/__init__.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/supervisors/multiprocess.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/supervisors/statreload.py +0 -0
- {uvicorn-0.34.1 → uvicorn-0.34.3}/uvicorn/workers.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: uvicorn
|
3
|
-
Version: 0.34.
|
3
|
+
Version: 0.34.3
|
4
4
|
Summary: The lightning-fast ASGI server.
|
5
5
|
Project-URL: Changelog, https://www.uvicorn.org/release-notes
|
6
6
|
Project-URL: Funding, https://github.com/sponsors/encode
|
@@ -32,7 +32,7 @@ Requires-Dist: colorama>=0.4; (sys_platform == 'win32') and extra == 'standard'
|
|
32
32
|
Requires-Dist: httptools>=0.6.3; extra == 'standard'
|
33
33
|
Requires-Dist: python-dotenv>=0.13; extra == 'standard'
|
34
34
|
Requires-Dist: pyyaml>=5.1; extra == 'standard'
|
35
|
-
Requires-Dist: uvloop
|
35
|
+
Requires-Dist: uvloop>=0.15.1; (sys_platform != 'win32' and (sys_platform != 'cygwin' and platform_python_implementation != 'PyPy')) and extra == 'standard'
|
36
36
|
Requires-Dist: watchfiles>=0.13; extra == 'standard'
|
37
37
|
Requires-Dist: websockets>=10.4; extra == 'standard'
|
38
38
|
Description-Content-Type: text/markdown
|
@@ -41,7 +41,7 @@ standard = [
|
|
41
41
|
"httptools>=0.6.3",
|
42
42
|
"python-dotenv>=0.13",
|
43
43
|
"PyYAML>=5.1",
|
44
|
-
"uvloop>=0.
|
44
|
+
"uvloop>=0.15.1; sys_platform != 'win32' and (sys_platform != 'cygwin' and platform_python_implementation != 'PyPy')",
|
45
45
|
"watchfiles>=0.13",
|
46
46
|
"websockets>=10.4",
|
47
47
|
]
|
@@ -1,8 +1,7 @@
|
|
1
1
|
-e .[standard]
|
2
2
|
|
3
|
-
# TODO: Remove this after h11 makes a release. By this writing, h11 was on 0.14.0.
|
4
3
|
# Core dependencies
|
5
|
-
h11
|
4
|
+
h11==0.16.0
|
6
5
|
|
7
6
|
# Explicit optionals
|
8
7
|
a2wsgi==1.10.8
|
@@ -14,15 +13,15 @@ build==1.2.2.post1
|
|
14
13
|
twine==6.1.0
|
15
14
|
|
16
15
|
# Testing
|
17
|
-
ruff==0.11.
|
16
|
+
ruff==0.11.9
|
18
17
|
pytest==8.3.5
|
19
18
|
pytest-mock==3.14.0
|
20
19
|
pytest-xdist[psutil]==3.6.1
|
21
20
|
mypy==1.15.0
|
22
21
|
types-click==7.1.8
|
23
|
-
types-pyyaml==6.0.12.
|
22
|
+
types-pyyaml==6.0.12.20250402
|
24
23
|
trustme==1.2.1
|
25
|
-
cryptography==44.0.
|
24
|
+
cryptography==44.0.3
|
26
25
|
coverage==7.8.0
|
27
26
|
coverage-conditional-plugin==0.9.0
|
28
27
|
coverage-enable-subprocess==1.0
|
@@ -30,4 +29,4 @@ httpx==0.28.1
|
|
30
29
|
|
31
30
|
# Documentation
|
32
31
|
mkdocs==1.6.1
|
33
|
-
mkdocs-material==9.6.
|
32
|
+
mkdocs-material==9.6.13
|
@@ -4,7 +4,8 @@ import contextlib
|
|
4
4
|
import logging
|
5
5
|
import socket
|
6
6
|
import sys
|
7
|
-
import
|
7
|
+
from collections.abc import Iterator
|
8
|
+
from typing import TYPE_CHECKING
|
8
9
|
|
9
10
|
import httpx
|
10
11
|
import pytest
|
@@ -15,7 +16,7 @@ from tests.utils import run_server
|
|
15
16
|
from uvicorn import Config
|
16
17
|
from uvicorn._types import ASGIReceiveCallable, ASGISendCallable, Scope
|
17
18
|
|
18
|
-
if
|
19
|
+
if TYPE_CHECKING:
|
19
20
|
import sys
|
20
21
|
|
21
22
|
from uvicorn.protocols.websockets.websockets_impl import WebSocketProtocol
|
@@ -32,7 +33,7 @@ pytestmark = pytest.mark.anyio
|
|
32
33
|
|
33
34
|
|
34
35
|
@contextlib.contextmanager
|
35
|
-
def caplog_for_logger(caplog: pytest.LogCaptureFixture, logger_name: str) ->
|
36
|
+
def caplog_for_logger(caplog: pytest.LogCaptureFixture, logger_name: str) -> Iterator[pytest.LogCaptureFixture]:
|
36
37
|
logger = logging.getLogger(logger_name)
|
37
38
|
logger.propagate, old_propagate = False, logger.propagate
|
38
39
|
logger.addHandler(caplog.handler)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import asyncio
|
4
|
-
import typing
|
5
4
|
from copy import deepcopy
|
5
|
+
from typing import TYPE_CHECKING, Any, TypedDict
|
6
6
|
|
7
7
|
import httpx
|
8
8
|
import pytest
|
@@ -35,7 +35,7 @@ try:
|
|
35
35
|
except ModuleNotFoundError: # pragma: no cover
|
36
36
|
skip_if_no_wsproto = pytest.mark.skipif(True, reason="wsproto is not installed.")
|
37
37
|
|
38
|
-
if
|
38
|
+
if TYPE_CHECKING:
|
39
39
|
import sys
|
40
40
|
|
41
41
|
from uvicorn.protocols.http.h11_impl import H11Protocol
|
@@ -776,7 +776,7 @@ async def test_server_reject_connection(
|
|
776
776
|
assert disconnected_message == {"type": "websocket.disconnect", "code": 1006}
|
777
777
|
|
778
778
|
|
779
|
-
class EmptyDict(
|
779
|
+
class EmptyDict(TypedDict): ...
|
780
780
|
|
781
781
|
|
782
782
|
async def test_server_reject_connection_with_response(
|
@@ -1142,12 +1142,12 @@ async def test_multiple_server_header(
|
|
1142
1142
|
|
1143
1143
|
|
1144
1144
|
async def test_lifespan_state(ws_protocol_cls: WSProtocol, http_protocol_cls: HTTPProtocol, unused_tcp_port: int):
|
1145
|
-
expected_states: list[dict[str,
|
1145
|
+
expected_states: list[dict[str, Any]] = [
|
1146
1146
|
{"a": 123, "b": [1]},
|
1147
1147
|
{"a": 123, "b": [1, 2]},
|
1148
1148
|
]
|
1149
1149
|
|
1150
|
-
actual_states: list[dict[str,
|
1150
|
+
actual_states: list[dict[str, Any]] = []
|
1151
1151
|
|
1152
1152
|
async def lifespan_app(scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable):
|
1153
1153
|
message = await receive()
|
@@ -306,24 +306,17 @@ class TestBaseReload:
|
|
306
306
|
|
307
307
|
|
308
308
|
@pytest.mark.skipif(WatchFilesReload is None, reason="watchfiles not available")
|
309
|
-
def
|
309
|
+
def test_should_watch_cwd(mocker: MockerFixture, reload_directory_structure: Path):
|
310
310
|
mock_watch = mocker.patch("uvicorn.supervisors.watchfilesreload.watch")
|
311
|
-
app_dir = reload_directory_structure / "app"
|
312
|
-
app_first_dir = reload_directory_structure / "app_first"
|
313
311
|
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
reload_dirs=[str(app_dir), str(app_first_dir)],
|
319
|
-
)
|
320
|
-
WatchFilesReload(config, target=run, sockets=[])
|
321
|
-
mock_watch.assert_called_once()
|
322
|
-
assert mock_watch.call_args[0] == (Path.cwd(),)
|
312
|
+
config = Config(app="tests.test_config:asgi_app", reload=True, reload_dirs=[])
|
313
|
+
WatchFilesReload(config, target=run, sockets=[])
|
314
|
+
mock_watch.assert_called_once()
|
315
|
+
assert mock_watch.call_args[0] == (Path.cwd(),)
|
323
316
|
|
324
317
|
|
325
318
|
@pytest.mark.skipif(WatchFilesReload is None, reason="watchfiles not available")
|
326
|
-
def
|
319
|
+
def test_should_watch_multiple_dirs(mocker: MockerFixture, reload_directory_structure: Path):
|
327
320
|
mock_watch = mocker.patch("uvicorn.supervisors.watchfilesreload.watch")
|
328
321
|
app_dir = reload_directory_structure / "app"
|
329
322
|
app_first_dir = reload_directory_structure / "app_first"
|
@@ -337,7 +330,6 @@ def test_should_watch_separate_dirs_outside_cwd(mocker: MockerFixture, reload_di
|
|
337
330
|
assert set(mock_watch.call_args[0]) == {
|
338
331
|
app_dir,
|
339
332
|
app_first_dir,
|
340
|
-
Path.cwd(),
|
341
333
|
}
|
342
334
|
|
343
335
|
|
@@ -7,9 +7,9 @@ import logging
|
|
7
7
|
import os
|
8
8
|
import socket
|
9
9
|
import sys
|
10
|
-
import
|
10
|
+
from collections.abc import Iterator
|
11
11
|
from pathlib import Path
|
12
|
-
from typing import Any, Literal
|
12
|
+
from typing import IO, Any, Callable, Literal
|
13
13
|
from unittest.mock import MagicMock
|
14
14
|
|
15
15
|
import pytest
|
@@ -291,7 +291,7 @@ def test_ssl_config_combined(tls_certificate_key_and_chain_path: str) -> None:
|
|
291
291
|
assert config.is_ssl is True
|
292
292
|
|
293
293
|
|
294
|
-
def asgi2_app(scope: Scope) ->
|
294
|
+
def asgi2_app(scope: Scope) -> Callable:
|
295
295
|
async def asgi(receive: ASGIReceiveCallable, send: ASGISendCallable) -> None: # pragma: nocover
|
296
296
|
pass
|
297
297
|
|
@@ -374,7 +374,7 @@ def test_log_config_yaml(
|
|
374
374
|
@pytest.mark.parametrize("config_file", ["log_config.ini", configparser.ConfigParser(), io.StringIO()])
|
375
375
|
def test_log_config_file(
|
376
376
|
mocked_logging_config_module: MagicMock,
|
377
|
-
config_file: str | configparser.RawConfigParser |
|
377
|
+
config_file: str | configparser.RawConfigParser | IO[Any],
|
378
378
|
) -> None:
|
379
379
|
"""
|
380
380
|
Test that one can load a configparser config from disk.
|
@@ -386,14 +386,14 @@ def test_log_config_file(
|
|
386
386
|
|
387
387
|
|
388
388
|
@pytest.fixture(params=[0, 1])
|
389
|
-
def web_concurrency(request: pytest.FixtureRequest) ->
|
389
|
+
def web_concurrency(request: pytest.FixtureRequest) -> Iterator[int]:
|
390
390
|
yield request.param
|
391
391
|
if os.getenv("WEB_CONCURRENCY"):
|
392
392
|
del os.environ["WEB_CONCURRENCY"]
|
393
393
|
|
394
394
|
|
395
395
|
@pytest.fixture(params=["127.0.0.1", "127.0.0.2"])
|
396
|
-
def forwarded_allow_ips(request: pytest.FixtureRequest) ->
|
396
|
+
def forwarded_allow_ips(request: pytest.FixtureRequest) -> Iterator[str]:
|
397
397
|
yield request.param
|
398
398
|
if os.getenv("FORWARDED_ALLOW_IPS"):
|
399
399
|
del os.environ["FORWARDED_ALLOW_IPS"]
|
@@ -1,9 +1,9 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import asyncio
|
4
|
-
import
|
4
|
+
from typing import Callable
|
5
5
|
|
6
|
-
AutoWebSocketsProtocol:
|
6
|
+
AutoWebSocketsProtocol: Callable[..., asyncio.Protocol] | None
|
7
7
|
try:
|
8
8
|
import websockets # noqa
|
9
9
|
except ImportError: # pragma: no cover
|
@@ -34,6 +34,7 @@ from uvicorn.config import Config
|
|
34
34
|
from uvicorn.logging import TRACE_LOG_LEVEL
|
35
35
|
from uvicorn.protocols.utils import (
|
36
36
|
ClientDisconnected,
|
37
|
+
get_client_addr,
|
37
38
|
get_local_addr,
|
38
39
|
get_path_with_query_string,
|
39
40
|
get_remote_addr,
|
@@ -271,7 +272,7 @@ class WebSocketProtocol(WebSocketServerProtocol):
|
|
271
272
|
message = cast("WebSocketAcceptEvent", message)
|
272
273
|
self.logger.info(
|
273
274
|
'%s - "WebSocket %s" [accepted]',
|
274
|
-
self.scope
|
275
|
+
get_client_addr(self.scope),
|
275
276
|
get_path_with_query_string(self.scope),
|
276
277
|
)
|
277
278
|
self.initial_response = None
|
@@ -289,7 +290,7 @@ class WebSocketProtocol(WebSocketServerProtocol):
|
|
289
290
|
message = cast("WebSocketCloseEvent", message)
|
290
291
|
self.logger.info(
|
291
292
|
'%s - "WebSocket %s" 403',
|
292
|
-
self.scope
|
293
|
+
get_client_addr(self.scope),
|
293
294
|
get_path_with_query_string(self.scope),
|
294
295
|
)
|
295
296
|
self.initial_response = (http.HTTPStatus.FORBIDDEN, [], b"")
|
@@ -300,7 +301,7 @@ class WebSocketProtocol(WebSocketServerProtocol):
|
|
300
301
|
message = cast("WebSocketResponseStartEvent", message)
|
301
302
|
self.logger.info(
|
302
303
|
'%s - "WebSocket %s" %d',
|
303
|
-
self.scope
|
304
|
+
get_client_addr(self.scope),
|
304
305
|
get_path_with_query_string(self.scope),
|
305
306
|
message["status"],
|
306
307
|
)
|
@@ -2,8 +2,7 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import asyncio
|
4
4
|
import logging
|
5
|
-
import
|
6
|
-
from typing import Literal, cast
|
5
|
+
from typing import Any, Literal, cast
|
7
6
|
from urllib.parse import unquote
|
8
7
|
|
9
8
|
import wsproto
|
@@ -27,6 +26,7 @@ from uvicorn.config import Config
|
|
27
26
|
from uvicorn.logging import TRACE_LOG_LEVEL
|
28
27
|
from uvicorn.protocols.utils import (
|
29
28
|
ClientDisconnected,
|
29
|
+
get_client_addr,
|
30
30
|
get_local_addr,
|
31
31
|
get_path_with_query_string,
|
32
32
|
get_remote_addr,
|
@@ -40,7 +40,7 @@ class WSProtocol(asyncio.Protocol):
|
|
40
40
|
self,
|
41
41
|
config: Config,
|
42
42
|
server_state: ServerState,
|
43
|
-
app_state: dict[str,
|
43
|
+
app_state: dict[str, Any],
|
44
44
|
_loop: asyncio.AbstractEventLoop | None = None,
|
45
45
|
) -> None:
|
46
46
|
if not config.loaded:
|
@@ -255,10 +255,10 @@ class WSProtocol(asyncio.Protocol):
|
|
255
255
|
|
256
256
|
if not self.handshake_complete:
|
257
257
|
if message_type == "websocket.accept":
|
258
|
-
message =
|
258
|
+
message = cast(WebSocketAcceptEvent, message)
|
259
259
|
self.logger.info(
|
260
260
|
'%s - "WebSocket %s" [accepted]',
|
261
|
-
self.scope
|
261
|
+
get_client_addr(self.scope),
|
262
262
|
get_path_with_query_string(self.scope),
|
263
263
|
)
|
264
264
|
subprotocol = message.get("subprotocol")
|
@@ -281,7 +281,7 @@ class WSProtocol(asyncio.Protocol):
|
|
281
281
|
self.queue.put_nowait({"type": "websocket.disconnect", "code": 1006})
|
282
282
|
self.logger.info(
|
283
283
|
'%s - "WebSocket %s" 403',
|
284
|
-
self.scope
|
284
|
+
get_client_addr(self.scope),
|
285
285
|
get_path_with_query_string(self.scope),
|
286
286
|
)
|
287
287
|
self.handshake_complete = True
|
@@ -292,14 +292,14 @@ class WSProtocol(asyncio.Protocol):
|
|
292
292
|
self.transport.close()
|
293
293
|
|
294
294
|
elif message_type == "websocket.http.response.start":
|
295
|
-
message =
|
295
|
+
message = cast(WebSocketResponseStartEvent, message)
|
296
296
|
# ensure status code is in the valid range
|
297
297
|
if not (100 <= message["status"] < 600):
|
298
298
|
msg = "Invalid HTTP status code '%d' in response."
|
299
299
|
raise RuntimeError(msg % message["status"])
|
300
300
|
self.logger.info(
|
301
301
|
'%s - "WebSocket %s" %d',
|
302
|
-
self.scope
|
302
|
+
get_client_addr(self.scope),
|
303
303
|
get_path_with_query_string(self.scope),
|
304
304
|
message["status"],
|
305
305
|
)
|
@@ -324,7 +324,7 @@ class WSProtocol(asyncio.Protocol):
|
|
324
324
|
elif not self.close_sent and not self.response_started:
|
325
325
|
try:
|
326
326
|
if message_type == "websocket.send":
|
327
|
-
message =
|
327
|
+
message = cast(WebSocketSendEvent, message)
|
328
328
|
bytes_data = message.get("bytes")
|
329
329
|
text_data = message.get("text")
|
330
330
|
data = text_data if bytes_data is None else bytes_data
|
@@ -333,7 +333,7 @@ class WSProtocol(asyncio.Protocol):
|
|
333
333
|
self.transport.write(output)
|
334
334
|
|
335
335
|
elif message_type == "websocket.close":
|
336
|
-
message =
|
336
|
+
message = cast(WebSocketCloseEvent, message)
|
337
337
|
self.close_sent = True
|
338
338
|
code = message.get("code", 1000)
|
339
339
|
reason = message.get("reason", "") or ""
|
@@ -350,7 +350,7 @@ class WSProtocol(asyncio.Protocol):
|
|
350
350
|
raise ClientDisconnected from exc
|
351
351
|
elif self.response_started:
|
352
352
|
if message_type == "websocket.http.response.body":
|
353
|
-
message =
|
353
|
+
message = cast("WebSocketResponseBodyEvent", message)
|
354
354
|
body_finished = not message.get("more_body", False)
|
355
355
|
reject_data = events.RejectData(data=message["body"], body_finished=body_finished)
|
356
356
|
output = self.conn.send(reject_data)
|
@@ -90,6 +90,10 @@ class BaseReload:
|
|
90
90
|
self.is_restarting = True
|
91
91
|
assert self.process.pid is not None
|
92
92
|
os.kill(self.process.pid, signal.CTRL_C_EVENT)
|
93
|
+
|
94
|
+
# This is a workaround to ensure the Ctrl+C event is processed
|
95
|
+
sys.stdout.write(" ") # This has to be a non-empty string
|
96
|
+
sys.stdout.flush()
|
93
97
|
else: # pragma: py-win32
|
94
98
|
self.process.terminate()
|
95
99
|
self.process.join()
|
@@ -63,10 +63,7 @@ class WatchFilesReload(BaseReload):
|
|
63
63
|
self.reloader_name = "WatchFiles"
|
64
64
|
self.reload_dirs = []
|
65
65
|
for directory in config.reload_dirs:
|
66
|
-
|
67
|
-
self.reload_dirs.append(directory)
|
68
|
-
if Path.cwd() not in self.reload_dirs:
|
69
|
-
self.reload_dirs.append(Path.cwd())
|
66
|
+
self.reload_dirs.append(directory)
|
70
67
|
|
71
68
|
self.watch_filter = FileFilter(config)
|
72
69
|
self.watcher = watch(
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|