From: Michiel W. Beijen Date: Sat, 13 Sep 2025 08:30:18 +0000 (+0200) Subject: feat: implement rfc9110 http status names (#2939) X-Git-Tag: 0.48.0~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=40a81479c7146bab21cc58ccb92b017cf177a077;p=thirdparty%2Fstarlette.git feat: implement rfc9110 http status names (#2939) Co-authored-by: Marcelo Trylesinski --- diff --git a/starlette/status.py b/starlette/status.py index 54c1fb7d..0160aac3 100644 --- a/starlette/status.py +++ b/starlette/status.py @@ -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 index 00000000..52b0a251 --- /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])