uvicorn 0.31.1__tar.gz → 0.32.0__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.
Files changed (77) hide show
  1. {uvicorn-0.31.1 → uvicorn-0.32.0}/PKG-INFO +2 -1
  2. {uvicorn-0.31.1 → uvicorn-0.32.0}/pyproject.toml +1 -0
  3. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/test_server.py +27 -1
  4. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/__init__.py +1 -1
  5. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/server.py +6 -2
  6. {uvicorn-0.31.1 → uvicorn-0.32.0}/.gitignore +0 -0
  7. {uvicorn-0.31.1 → uvicorn-0.32.0}/LICENSE.md +0 -0
  8. {uvicorn-0.31.1 → uvicorn-0.32.0}/README.md +0 -0
  9. {uvicorn-0.31.1 → uvicorn-0.32.0}/requirements.txt +0 -0
  10. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/__init__.py +0 -0
  11. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/conftest.py +0 -0
  12. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/importer/__init__.py +0 -0
  13. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/importer/circular_import_a.py +0 -0
  14. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/importer/circular_import_b.py +0 -0
  15. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/importer/raise_import_error.py +0 -0
  16. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/importer/test_importer.py +0 -0
  17. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/middleware/__init__.py +0 -0
  18. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/middleware/test_logging.py +0 -0
  19. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/middleware/test_message_logger.py +0 -0
  20. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/middleware/test_proxy_headers.py +0 -0
  21. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/middleware/test_wsgi.py +0 -0
  22. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/protocols/__init__.py +0 -0
  23. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/protocols/test_http.py +0 -0
  24. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/protocols/test_utils.py +0 -0
  25. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/protocols/test_websocket.py +0 -0
  26. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/response.py +0 -0
  27. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/supervisors/__init__.py +0 -0
  28. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/supervisors/test_multiprocess.py +0 -0
  29. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/supervisors/test_reload.py +0 -0
  30. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/supervisors/test_signal.py +0 -0
  31. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/test_auto_detection.py +0 -0
  32. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/test_cli.py +0 -0
  33. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/test_config.py +0 -0
  34. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/test_default_headers.py +0 -0
  35. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/test_lifespan.py +0 -0
  36. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/test_main.py +0 -0
  37. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/test_ssl.py +0 -0
  38. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/test_subprocess.py +0 -0
  39. {uvicorn-0.31.1 → uvicorn-0.32.0}/tests/utils.py +0 -0
  40. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/__main__.py +0 -0
  41. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/_subprocess.py +0 -0
  42. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/_types.py +0 -0
  43. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/config.py +0 -0
  44. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/importer.py +0 -0
  45. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/lifespan/__init__.py +0 -0
  46. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/lifespan/off.py +0 -0
  47. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/lifespan/on.py +0 -0
  48. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/logging.py +0 -0
  49. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/loops/__init__.py +0 -0
  50. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/loops/asyncio.py +0 -0
  51. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/loops/auto.py +0 -0
  52. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/loops/uvloop.py +0 -0
  53. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/main.py +0 -0
  54. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/middleware/__init__.py +0 -0
  55. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/middleware/asgi2.py +0 -0
  56. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/middleware/message_logger.py +0 -0
  57. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/middleware/proxy_headers.py +0 -0
  58. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/middleware/wsgi.py +0 -0
  59. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/protocols/__init__.py +0 -0
  60. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/protocols/http/__init__.py +0 -0
  61. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/protocols/http/auto.py +0 -0
  62. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/protocols/http/flow_control.py +0 -0
  63. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/protocols/http/h11_impl.py +0 -0
  64. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/protocols/http/httptools_impl.py +0 -0
  65. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/protocols/utils.py +0 -0
  66. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/protocols/websockets/__init__.py +0 -0
  67. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/protocols/websockets/auto.py +0 -0
  68. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/protocols/websockets/websockets_impl.py +0 -0
  69. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/protocols/websockets/wsproto_impl.py +0 -0
  70. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/py.typed +0 -0
  71. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/supervisors/__init__.py +0 -0
  72. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/supervisors/basereload.py +0 -0
  73. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/supervisors/multiprocess.py +0 -0
  74. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/supervisors/statreload.py +0 -0
  75. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/supervisors/watchfilesreload.py +0 -0
  76. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/supervisors/watchgodreload.py +0 -0
  77. {uvicorn-0.31.1 → uvicorn-0.32.0}/uvicorn/workers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: uvicorn
3
- Version: 0.31.1
3
+ Version: 0.32.0
4
4
  Summary: The lightning-fast ASGI server.
5
5
  Project-URL: Changelog, https://github.com/encode/uvicorn/blob/master/CHANGELOG.md
6
6
  Project-URL: Funding, https://github.com/sponsors/encode
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.9
20
20
  Classifier: Programming Language :: Python :: 3.10
21
21
  Classifier: Programming Language :: Python :: 3.11
22
22
  Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
23
24
  Classifier: Programming Language :: Python :: Implementation :: CPython
24
25
  Classifier: Programming Language :: Python :: Implementation :: PyPy
25
26
  Classifier: Topic :: Internet :: WWW/HTTP
@@ -25,6 +25,7 @@ classifiers = [
25
25
  "Programming Language :: Python :: 3.10",
26
26
  "Programming Language :: Python :: 3.11",
27
27
  "Programming Language :: Python :: 3.12",
28
+ "Programming Language :: Python :: 3.13",
28
29
  "Programming Language :: Python :: Implementation :: CPython",
29
30
  "Programming Language :: Python :: Implementation :: PyPy",
30
31
  "Topic :: Internet :: WWW/HTTP",
@@ -2,15 +2,23 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import contextlib
5
+ import logging
5
6
  import signal
6
7
  import sys
7
8
  from typing import Callable, ContextManager, Generator
8
9
 
10
+ import httpx
9
11
  import pytest
10
12
 
13
+ from tests.utils import run_server
14
+ from uvicorn._types import ASGIReceiveCallable, ASGISendCallable, Scope
11
15
  from uvicorn.config import Config
16
+ from uvicorn.protocols.http.h11_impl import H11Protocol
17
+ from uvicorn.protocols.http.httptools_impl import HttpToolsProtocol
12
18
  from uvicorn.server import Server
13
19
 
20
+ pytestmark = pytest.mark.anyio
21
+
14
22
 
15
23
  # asyncio does NOT allow raising in signal handlers, so to detect
16
24
  # raised signals raised a mutable `witness` receives the signal
@@ -37,6 +45,12 @@ async def dummy_app(scope, receive, send): # pragma: py-win32
37
45
  pass
38
46
 
39
47
 
48
+ async def app(scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable) -> None:
49
+ assert scope["type"] == "http"
50
+ await send({"type": "http.response.start", "status": 200, "headers": []})
51
+ await send({"type": "http.response.body", "body": b"", "more_body": False})
52
+
53
+
40
54
  if sys.platform == "win32": # pragma: py-not-win32
41
55
  signals = [signal.SIGBREAK]
42
56
  signal_captures = [capture_signal_sync]
@@ -45,7 +59,6 @@ else: # pragma: py-win32
45
59
  signal_captures = [capture_signal_sync, capture_signal_async]
46
60
 
47
61
 
48
- @pytest.mark.anyio
49
62
  @pytest.mark.parametrize("exception_signal", signals)
50
63
  @pytest.mark.parametrize("capture_signal", signal_captures)
51
64
  async def test_server_interrupt(
@@ -65,3 +78,16 @@ async def test_server_interrupt(
65
78
  assert witness
66
79
  # set by the server's graceful exit handler
67
80
  assert server.should_exit
81
+
82
+
83
+ async def test_request_than_limit_max_requests_warn_log(
84
+ unused_tcp_port: int, http_protocol_cls: type[H11Protocol | HttpToolsProtocol], caplog: pytest.LogCaptureFixture
85
+ ):
86
+ caplog.set_level(logging.WARNING, logger="uvicorn.error")
87
+ config = Config(app=app, limit_max_requests=1, port=unused_tcp_port, http=http_protocol_cls)
88
+ async with run_server(config):
89
+ async with httpx.AsyncClient() as client:
90
+ tasks = [client.get(f"http://127.0.0.1:{unused_tcp_port}") for _ in range(2)]
91
+ responses = await asyncio.gather(*tasks)
92
+ assert len(responses) == 2
93
+ assert "Maximum request limit of 1 exceeded. Terminating process." in caplog.text
@@ -1,5 +1,5 @@
1
1
  from uvicorn.config import Config
2
2
  from uvicorn.main import Server, main, run
3
3
 
4
- __version__ = "0.31.1"
4
+ __version__ = "0.32.0"
5
5
  __all__ = ["main", "run", "Config", "Server"]
@@ -250,8 +250,12 @@ class Server:
250
250
  # Determine if we should exit.
251
251
  if self.should_exit:
252
252
  return True
253
- if self.config.limit_max_requests is not None:
254
- return self.server_state.total_requests >= self.config.limit_max_requests
253
+
254
+ max_requests = self.config.limit_max_requests
255
+ if max_requests is not None and self.server_state.total_requests >= max_requests:
256
+ logger.warning(f"Maximum request limit of {max_requests} exceeded. Terminating process.")
257
+ return True
258
+
255
259
  return False
256
260
 
257
261
  async def shutdown(self, sockets: list[socket.socket] | None = None) -> None:
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