From ac87bf835f36e03e28a3546868dd730824e85fc3 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 4 Nov 2024 09:12:23 -0800 Subject: [PATCH] Headers is not MutableMapping --- CHANGES.rst | 3 ++- src/werkzeug/datastructures/headers.py | 20 ++++++++++---------- src/werkzeug/utils.py | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index b85611630..5555f01ef 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,7 +7,8 @@ Unreleased - Improve type annotation for ``TypeConversionDict.get`` to allow the ``type`` parameter to be a callable. :issue:`2988` - +- ``Headers`` does not inherit from ``MutableMapping``, as it is does not + exactly match that interface. :issue:`2989` Version 3.1.1 diff --git a/src/werkzeug/datastructures/headers.py b/src/werkzeug/datastructures/headers.py index 96c2a0cd6..e8cbdd8e8 100644 --- a/src/werkzeug/datastructures/headers.py +++ b/src/werkzeug/datastructures/headers.py @@ -17,7 +17,7 @@ T = t.TypeVar("T") -class Headers(cabc.MutableMapping[str, str]): +class Headers: """An object that stores some headers. It has a dict-like interface, but is ordered, can store the same key multiple times, and iterating yields ``(key, value)`` pairs instead of only keys. @@ -107,7 +107,7 @@ def lowered(item: tuple[str, ...]) -> tuple[str, ...]: __hash__ = None # type: ignore[assignment] - @t.overload # type: ignore[override] + @t.overload def get(self, key: str) -> str | None: ... @t.overload def get(self, key: str, default: str) -> str: ... @@ -208,17 +208,17 @@ def get_all(self, name: str) -> list[str]: """ return self.getlist(name) - def items(self, lower: bool = False) -> t.Iterable[tuple[str, str]]: # type: ignore[override] + def items(self, lower: bool = False) -> t.Iterable[tuple[str, str]]: for key, value in self: if lower: key = key.lower() yield key, value - def keys(self, lower: bool = False) -> t.Iterable[str]: # type: ignore[override] + def keys(self, lower: bool = False) -> t.Iterable[str]: for key, _ in self.items(lower): yield key - def values(self) -> t.Iterable[str]: # type: ignore[override] + def values(self) -> t.Iterable[str]: for _, value in self.items(): yield value @@ -322,7 +322,7 @@ def popitem(self) -> tuple[str, str]: """Removes a key or index and returns a (key, value) item.""" return self._list.pop() - def __contains__(self, key: str) -> bool: # type: ignore[override] + def __contains__(self, key: str) -> bool: """Check if a key is present.""" try: self._get_key(key) @@ -331,7 +331,7 @@ def __contains__(self, key: str) -> bool: # type: ignore[override] return True - def __iter__(self) -> t.Iterator[tuple[str, str]]: # type: ignore[override] + def __iter__(self) -> t.Iterator[tuple[str, str]]: """Yield ``(key, value)`` tuples.""" return iter(self._list) @@ -486,7 +486,7 @@ def __setitem__( else: self._list[key] = [(k, _str_header_value(v)) for k, v in value] # type: ignore[misc] - def update( # type: ignore[override] + def update( self, arg: ( Headers @@ -562,7 +562,7 @@ def to_wsgi_list(self) -> list[tuple[str, str]]: :return: list """ - return list(self) # type: ignore[arg-type] + return list(self) def copy(self) -> te.Self: return self.__class__(self._list) @@ -640,7 +640,7 @@ def _get_key(self, key: str) -> str: def __len__(self) -> int: return sum(1 for _ in self) - def __iter__(self) -> cabc.Iterator[tuple[str, str]]: # type: ignore[override] + def __iter__(self) -> cabc.Iterator[tuple[str, str]]: for key, value in self.environ.items(): if key.startswith("HTTP_") and key not in { "HTTP_CONTENT_TYPE", diff --git a/src/werkzeug/utils.py b/src/werkzeug/utils.py index 59b97b732..3d3bbf066 100644 --- a/src/werkzeug/utils.py +++ b/src/werkzeug/utils.py @@ -150,7 +150,7 @@ def lookup(self, obj: Request) -> WSGIEnvironment: class header_property(_DictAccessorProperty[_TAccessorValue]): """Like `environ_property` but for headers.""" - def lookup(self, obj: Request | Response) -> Headers: + def lookup(self, obj: Request | Response) -> Headers: # type: ignore[override] return obj.headers