Skip to content

Commit

Permalink
Deprecate WS_1004_NO_STATUS_RCVD and WS_1005_ABNORMAL_CLOSURE (#1580
Browse files Browse the repository at this point in the history
)

* Deprecate `WS_1004_NO_STATUS_RCVD` and `WS_1005_ABNORMAL_CLOSURE`

* Fix linter

* Fix coverage
  • Loading branch information
Kludex committed Apr 22, 2022
1 parent 5b56e7d commit 9329160
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 2 deletions.
62 changes: 62 additions & 0 deletions starlette/_pep562.py
@@ -0,0 +1,62 @@
# flake8: noqa
"""
Backport of PEP 562.
https://pypi.org/search/?q=pep562
Licensed under MIT
Copyright (c) 2018 Isaac Muse <isaacmuse@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
"""
import sys
from typing import Any, Callable, List, Optional


class Pep562:
"""
Backport of PEP 562 <https://pypi.org/search/?q=pep562>.
Wraps the module in a class that exposes the mechanics to override `__dir__` and `__getattr__`.
The given module will be searched for overrides of `__dir__` and `__getattr__` and use them when needed.
"""

def __init__(self, name: str) -> None: # pragma: no cover
"""Acquire `__getattr__` and `__dir__`, but only replace module for versions less than Python 3.7."""

self._module = sys.modules[name]
self._get_attr = getattr(self._module, "__getattr__", None)
self._get_dir: Optional[Callable[..., List[str]]] = getattr(
self._module, "__dir__", None
)
sys.modules[name] = self # type: ignore[assignment]

def __dir__(self) -> List[str]: # pragma: no cover
"""Return the overridden `dir` if one was provided, else apply `dir` to the module."""

return self._get_dir() if self._get_dir else dir(self._module)

def __getattr__(self, name: str) -> Any: # pragma: no cover
"""
Attempt to retrieve the attribute from the module, and if missing, use the overridden function if present.
"""

try:
return getattr(self._module, name)
except AttributeError:
if self._get_attr:
return self._get_attr(name)
raise


def pep562(module_name: str) -> None: # pragma: no cover
"""Helper function to apply PEP 562."""

if sys.version_info < (3, 7):
Pep562(module_name)
118 changes: 116 additions & 2 deletions starlette/status.py
Expand Up @@ -5,6 +5,93 @@
And RFC 2324 - https://tools.ietf.org/html/rfc2324
"""
import sys
import warnings
from typing import List

from starlette._pep562 import pep562

__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_REQUEST_ENTITY_TOO_LARGE",
"HTTP_414_REQUEST_URI_TOO_LONG",
"HTTP_415_UNSUPPORTED_MEDIA_TYPE",
"HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE",
"HTTP_417_EXPECTATION_FAILED",
"HTTP_418_IM_A_TEAPOT",
"HTTP_421_MISDIRECTED_REQUEST",
"HTTP_422_UNPROCESSABLE_ENTITY",
"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
Expand Down Expand Up @@ -79,8 +166,8 @@
WS_1001_GOING_AWAY = 1001
WS_1002_PROTOCOL_ERROR = 1002
WS_1003_UNSUPPORTED_DATA = 1003
WS_1004_NO_STATUS_RCVD = 1004
WS_1005_ABNORMAL_CLOSURE = 1005
WS_1005_NO_STATUS_RCVD = 1005
WS_1006_ABNORMAL_CLOSURE = 1006
WS_1007_INVALID_FRAME_PAYLOAD_DATA = 1007
WS_1008_POLICY_VIOLATION = 1008
WS_1009_MESSAGE_TOO_BIG = 1009
Expand All @@ -90,3 +177,30 @@
WS_1013_TRY_AGAIN_LATER = 1013
WS_1014_BAD_GATEWAY = 1014
WS_1015_TLS_HANDSHAKE = 1015


__deprecated__ = {"WS_1004_NO_STATUS_RCVD": 1004, "WS_1005_ABNORMAL_CLOSURE": 1005}


def __getattr__(name: str) -> int:
deprecation_changes = {
"WS_1004_NO_STATUS_RCVD": "WS_1005_NO_STATUS_RCVD",
"WS_1005_ABNORMAL_CLOSURE": "WS_1006_ABNORMAL_CLOSURE",
}
deprecated = __deprecated__.get(name)
if deprecated:
stacklevel = 3 if sys.version_info >= (3, 7) else 4
warnings.warn(
f"'{name}' is deprecated. Use '{deprecation_changes[name]}' instead.",
category=DeprecationWarning,
stacklevel=stacklevel,
)
return deprecated
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")


def __dir__() -> List[str]:
return sorted(list(__all__) + list(__deprecated__.keys())) # pragma: no cover


pep562(__name__)
25 changes: 25 additions & 0 deletions tests/test_status.py
@@ -0,0 +1,25 @@
import importlib

import pytest


@pytest.mark.parametrize(
"constant,msg",
(
(
"WS_1004_NO_STATUS_RCVD",
"'WS_1004_NO_STATUS_RCVD' is deprecated. "
"Use 'WS_1005_NO_STATUS_RCVD' instead.",
),
(
"WS_1005_ABNORMAL_CLOSURE",
"'WS_1005_ABNORMAL_CLOSURE' is deprecated. "
"Use 'WS_1006_ABNORMAL_CLOSURE' 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])

0 comments on commit 9329160

Please sign in to comment.