]> git.ipfire.org Git - thirdparty/starlette.git/commitdiff
feat: implement rfc9110 http status names (#2939)
authorMichiel W. Beijen <mb@x14.nl>
Sat, 13 Sep 2025 08:30:18 +0000 (10:30 +0200)
committerGitHub <noreply@github.com>
Sat, 13 Sep 2025 08:30:18 +0000 (10:30 +0200)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
starlette/status.py
tests/test_status.py [new file with mode: 0644]

index 54c1fb7d0df128bfe16fb4cb03cdf23b8225af94..0160aac354f7fcf2041de3e8dd3244ed5a6ab455 100644 (file)
@@ -3,11 +3,94 @@ HTTP codes
 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
 
+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
@@ -44,14 +127,14 @@ HTTP_409_CONFLICT = 409
 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
@@ -93,3 +176,34 @@ WS_1012_SERVICE_RESTART = 1012
 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 (file)
index 0000000..52b0a25
--- /dev/null
@@ -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])