From db042b16bce1c37ecd5e4ad616d4df27543915aa Mon Sep 17 00:00:00 2001 From: "Michiel W. Beijen" Date: Sun, 18 May 2025 18:46:08 +0200 Subject: [PATCH 1/3] feat: implement rfc9110 http status names rfc9110 obsoletes the earlier rfc 7231. This document also includes some status codes that were previously only used for WebDAV and assigns more generic names to these status codes. ref: https://www.rfc-editor.org/rfc/rfc9110.html#name-changes-from-rfc-7231 The status code constants are updated to reflect the new naming and the old constants are kept for backwards compatibility - HTTP_413_CONTENT_TOO_LARGE, previously HTTP_413_REQUEST_ENTITY_TOO_LARGE - HTTP_414_URI_TOO_LONG, previously HTTP_414_REQUEST_URI_TOO_LONG - HTTP_416_RANGE_NOT_SATISFIABLE, previously HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE - HTTP_422_UNPROCESSABLE_CONTENT, previously HTTP_422_UNPROCESSABLE_ENTITY --- starlette/status.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/starlette/status.py b/starlette/status.py index 54c1fb7d0..ddcf5865a 100644 --- a/starlette/status.py +++ b/starlette/status.py @@ -3,7 +3,7 @@ See HTTP Status Code Registry: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml -And RFC 2324 - https://tools.ietf.org/html/rfc2324 +And RFC 9110 - https://www.rfc-editor.org/rfc/rfc9110 """ from __future__ import annotations @@ -44,14 +44,14 @@ HTTP_410_GONE = 410 HTTP_411_LENGTH_REQUIRED = 411 HTTP_412_PRECONDITION_FAILED = 412 -HTTP_413_REQUEST_ENTITY_TOO_LARGE = 413 -HTTP_414_REQUEST_URI_TOO_LONG = 414 +HTTP_413_CONTENT_TOO_LARGE = 413 +HTTP_414_URI_TOO_LONG = 414 HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415 -HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416 +HTTP_416_RANGE_NOT_SATISFIABLE = 416 HTTP_417_EXPECTATION_FAILED = 417 HTTP_418_IM_A_TEAPOT = 418 HTTP_421_MISDIRECTED_REQUEST = 421 -HTTP_422_UNPROCESSABLE_ENTITY = 422 +HTTP_422_UNPROCESSABLE_CONTENT = 422 HTTP_423_LOCKED = 423 HTTP_424_FAILED_DEPENDENCY = 424 HTTP_425_TOO_EARLY = 425 @@ -72,6 +72,11 @@ HTTP_510_NOT_EXTENDED = 510 HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511 +# for backwards compatibility +HTTP_413_REQUEST_ENTITY_TOO_LARGE = HTTP_413_CONTENT_TOO_LARGE +HTTP_414_REQUEST_URI_TOO_LONG = HTTP_414_URI_TOO_LONG +HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = HTTP_416_RANGE_NOT_SATISFIABLE +HTTP_422_UNPROCESSABLE_ENTITY = HTTP_422_UNPROCESSABLE_CONTENT """ WebSocket codes From bbcdef102dc7ca8d5091ae7930050161495a143d Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sat, 13 Sep 2025 10:27:51 +0200 Subject: [PATCH 2/3] Make it backwards compitable --- starlette/status.py | 120 ++++++++++++++++++++++++++++++++++++++++--- tests/test_status.py | 23 +++++++++ 2 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 tests/test_status.py diff --git a/starlette/status.py b/starlette/status.py index ddcf5865a..89f559d58 100644 --- a/starlette/status.py +++ b/starlette/status.py @@ -8,6 +8,89 @@ from __future__ import annotations +import warnings + +__all__ = [ + "HTTP_100_CONTINUE", + "HTTP_101_SWITCHING_PROTOCOLS", + "HTTP_102_PROCESSING", + "HTTP_103_EARLY_HINTS", + "HTTP_200_OK", + "HTTP_201_CREATED", + "HTTP_202_ACCEPTED", + "HTTP_203_NON_AUTHORITATIVE_INFORMATION", + "HTTP_204_NO_CONTENT", + "HTTP_205_RESET_CONTENT", + "HTTP_206_PARTIAL_CONTENT", + "HTTP_207_MULTI_STATUS", + "HTTP_208_ALREADY_REPORTED", + "HTTP_226_IM_USED", + "HTTP_300_MULTIPLE_CHOICES", + "HTTP_301_MOVED_PERMANENTLY", + "HTTP_302_FOUND", + "HTTP_303_SEE_OTHER", + "HTTP_304_NOT_MODIFIED", + "HTTP_305_USE_PROXY", + "HTTP_306_RESERVED", + "HTTP_307_TEMPORARY_REDIRECT", + "HTTP_308_PERMANENT_REDIRECT", + "HTTP_400_BAD_REQUEST", + "HTTP_401_UNAUTHORIZED", + "HTTP_402_PAYMENT_REQUIRED", + "HTTP_403_FORBIDDEN", + "HTTP_404_NOT_FOUND", + "HTTP_405_METHOD_NOT_ALLOWED", + "HTTP_406_NOT_ACCEPTABLE", + "HTTP_407_PROXY_AUTHENTICATION_REQUIRED", + "HTTP_408_REQUEST_TIMEOUT", + "HTTP_409_CONFLICT", + "HTTP_410_GONE", + "HTTP_411_LENGTH_REQUIRED", + "HTTP_412_PRECONDITION_FAILED", + "HTTP_413_CONTENT_TOO_LARGE", + "HTTP_414_URI_TOO_LONG", + "HTTP_415_UNSUPPORTED_MEDIA_TYPE", + "HTTP_416_RANGE_NOT_SATISFIABLE", + "HTTP_417_EXPECTATION_FAILED", + "HTTP_418_IM_A_TEAPOT", + "HTTP_421_MISDIRECTED_REQUEST", + "HTTP_422_UNPROCESSABLE_CONTENT", + "HTTP_423_LOCKED", + "HTTP_424_FAILED_DEPENDENCY", + "HTTP_425_TOO_EARLY", + "HTTP_426_UPGRADE_REQUIRED", + "HTTP_428_PRECONDITION_REQUIRED", + "HTTP_429_TOO_MANY_REQUESTS", + "HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE", + "HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS", + "HTTP_500_INTERNAL_SERVER_ERROR", + "HTTP_501_NOT_IMPLEMENTED", + "HTTP_502_BAD_GATEWAY", + "HTTP_503_SERVICE_UNAVAILABLE", + "HTTP_504_GATEWAY_TIMEOUT", + "HTTP_505_HTTP_VERSION_NOT_SUPPORTED", + "HTTP_506_VARIANT_ALSO_NEGOTIATES", + "HTTP_507_INSUFFICIENT_STORAGE", + "HTTP_508_LOOP_DETECTED", + "HTTP_510_NOT_EXTENDED", + "HTTP_511_NETWORK_AUTHENTICATION_REQUIRED", + "WS_1000_NORMAL_CLOSURE", + "WS_1001_GOING_AWAY", + "WS_1002_PROTOCOL_ERROR", + "WS_1003_UNSUPPORTED_DATA", + "WS_1005_NO_STATUS_RCVD", + "WS_1006_ABNORMAL_CLOSURE", + "WS_1007_INVALID_FRAME_PAYLOAD_DATA", + "WS_1008_POLICY_VIOLATION", + "WS_1009_MESSAGE_TOO_BIG", + "WS_1010_MANDATORY_EXT", + "WS_1011_INTERNAL_ERROR", + "WS_1012_SERVICE_RESTART", + "WS_1013_TRY_AGAIN_LATER", + "WS_1014_BAD_GATEWAY", + "WS_1015_TLS_HANDSHAKE", +] + HTTP_100_CONTINUE = 100 HTTP_101_SWITCHING_PROTOCOLS = 101 HTTP_102_PROCESSING = 102 @@ -72,12 +155,6 @@ HTTP_510_NOT_EXTENDED = 510 HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511 -# for backwards compatibility -HTTP_413_REQUEST_ENTITY_TOO_LARGE = HTTP_413_CONTENT_TOO_LARGE -HTTP_414_REQUEST_URI_TOO_LONG = HTTP_414_URI_TOO_LONG -HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = HTTP_416_RANGE_NOT_SATISFIABLE -HTTP_422_UNPROCESSABLE_ENTITY = HTTP_422_UNPROCESSABLE_CONTENT - """ WebSocket codes https://www.iana.org/assignments/websocket/websocket.xml#close-code-number @@ -98,3 +175,34 @@ WS_1013_TRY_AGAIN_LATER = 1013 WS_1014_BAD_GATEWAY = 1014 WS_1015_TLS_HANDSHAKE = 1015 + +__deprecated__ = { + "HTTP_413_REQUEST_ENTITY_TOO_LARGE": 413, + "HTTP_414_REQUEST_URI_TOO_LONG": 414, + "HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE": 416, + "HTTP_422_UNPROCESSABLE_ENTITY": 422, +} + + +def __getattr__(name: str) -> int: + deprecation_changes = { + "HTTP_413_REQUEST_ENTITY_TOO_LARGE": "HTTP_413_CONTENT_TOO_LARGE", + "HTTP_414_REQUEST_URI_TOO_LONG": "HTTP_414_URI_TOO_LONG", + "HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE": "HTTP_416_RANGE_NOT_SATISFIABLE", + "HTTP_422_UNPROCESSABLE_ENTITY": "HTTP_422_UNPROCESSABLE_CONTENT", + } + + deprecated = __deprecated__.get(name) + if deprecated: + warnings.warn( + f"'{name}' is deprecated. Use '{deprecation_changes[name]}' instead.", + category=DeprecationWarning, + stacklevel=3, + ) + return deprecated + + raise AttributeError(f"module 'starlette.status' has no attribute '{name}'") + + +def __dir__() -> list[str]: + return sorted(list(__all__) + list(__deprecated__.keys())) # pragma: no cover diff --git a/tests/test_status.py b/tests/test_status.py new file mode 100644 index 000000000..52b0a2513 --- /dev/null +++ b/tests/test_status.py @@ -0,0 +1,23 @@ +import importlib + +import pytest + + +@pytest.mark.parametrize( + "constant,msg", + ( + ( + "HTTP_413_REQUEST_ENTITY_TOO_LARGE", + "'HTTP_413_REQUEST_ENTITY_TOO_LARGE' is deprecated. Use 'HTTP_413_CONTENT_TOO_LARGE' instead.", + ), + ( + "HTTP_414_REQUEST_URI_TOO_LONG", + "'HTTP_414_REQUEST_URI_TOO_LONG' is deprecated. Use 'HTTP_414_URI_TOO_LONG' instead.", + ), + ), +) +def test_deprecated_types(constant: str, msg: str) -> None: + with pytest.warns(DeprecationWarning) as record: + getattr(importlib.import_module("starlette.status"), constant) + assert len(record) == 1 + assert msg in str(record.list[0]) From 3aacfa836aa328a6d13a6bed143bcfc748280b96 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sat, 13 Sep 2025 10:28:23 +0200 Subject: [PATCH 3/3] readd empty line --- starlette/status.py | 1 + 1 file changed, 1 insertion(+) diff --git a/starlette/status.py b/starlette/status.py index 89f559d58..0160aac35 100644 --- a/starlette/status.py +++ b/starlette/status.py @@ -155,6 +155,7 @@ HTTP_510_NOT_EXTENDED = 510 HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511 + """ WebSocket codes https://www.iana.org/assignments/websocket/websocket.xml#close-code-number