diff --git a/stubs/WebOb/@tests/stubtest_allowlist.txt b/stubs/WebOb/@tests/stubtest_allowlist.txt index adb9b93c7..dc716bbab 100644 --- a/stubs/WebOb/@tests/stubtest_allowlist.txt +++ b/stubs/WebOb/@tests/stubtest_allowlist.txt @@ -1,18 +1,3 @@ -# TODO: missing from stub -webob.__all__ -webob.byterange.__all__ -webob.client.__all__ -webob.cookies.__all__ -webob.datetime_utils.__all__ -webob.dec.__all__ -webob.etag.__all__ -webob.exc.__all__ -webob.headers.__all__ -webob.multidict.__all__ -webob.request.__all__ -webob.response.__all__ -webob.static.__all__ - # Error: is not present in stub # ============================= # These are plain strings, regex strings or compiled regex patterns @@ -62,7 +47,21 @@ webob.descriptors.SCHEME_RE webob.acceptparse.MIMEAccept # Deprecated API # PY2 compat stuff that has already been removed upstream -webob.compat +webob.compat.PY2 +webob.compat.PY3 +webob.compat.bytes_ +webob.compat.class_types +webob.compat.integer_types +webob.compat.iteritems_ +webob.compat.itervalues_ +webob.compat.long +webob.compat.native_ +webob.compat.parse_qsl_text +webob.compat.reraise +webob.compat.string_types +webob.compat.text_ +webob.compat.text_type +webob.compat.unquote webob.multidict.MultiDict.iteritems webob.multidict.MultiDict.iterkeys webob.multidict.MultiDict.itervalues @@ -71,6 +70,9 @@ webob.multidict.NestedMultiDict.iterkeys webob.multidict.NestedMultiDict.itervalues webob.multidict.NoVars.iterkeys +# The implementation details of cgi_FieldStorage shouldn't matter +webob.compat.cgi_FieldStorage.read_multi + # NoVars implements the MultiDict interface for better runtime errors # but it is annoying for type checking, so the methods that are not # valid to call on NoVars have been removed. In the future we would @@ -96,41 +98,18 @@ webob.response.ResponseBodyFile.close webob.Response.set_cookie webob.response.Response.set_cookie -# These methods have been moved from their subclasses to the shared hidden superclass -# since the method signatures are the same, so this saves some copy pasta and should -# not affect type checking or runtime behavior in any way -webob.acceptparse._AcceptCharsetInvalidOrNoHeader.__add__ -webob.acceptparse._AcceptCharsetInvalidOrNoHeader.__radd__ -webob.acceptparse._AcceptCharsetInvalidOrNoHeader.copy -webob.acceptparse._AcceptCharsetInvalidOrNoHeader.parsed -webob.acceptparse._AcceptEncodingInvalidOrNoHeader.__add__ -webob.acceptparse._AcceptEncodingInvalidOrNoHeader.__radd__ -webob.acceptparse._AcceptEncodingInvalidOrNoHeader.copy -webob.acceptparse._AcceptEncodingInvalidOrNoHeader.parsed -webob.acceptparse._AcceptInvalidOrNoHeader.__add__ -webob.acceptparse._AcceptInvalidOrNoHeader.__radd__ -webob.acceptparse._AcceptInvalidOrNoHeader.copy -webob.acceptparse._AcceptInvalidOrNoHeader.parsed -webob.acceptparse._AcceptLanguageInvalidOrNoHeader.__add__ -webob.acceptparse._AcceptLanguageInvalidOrNoHeader.__radd__ -webob.acceptparse._AcceptLanguageInvalidOrNoHeader.copy -webob.acceptparse._AcceptLanguageInvalidOrNoHeader.lookup -webob.acceptparse._AcceptLanguageInvalidOrNoHeader.parsed - # These are here due to the slightly more strict nature of the type annotation # of these descriptors for type checking, it does not really have any runtime # consequences since `_IntValueProperty` derives from `value_property` and # only makes `__set__` slightly more strict. -webob.cachecontrol.CacheControl.max_age -webob.cachecontrol.CacheControl.max_stale -webob.cachecontrol.CacheControl.min_fresh -webob.cachecontrol.CacheControl.s_max_age -webob.cachecontrol.CacheControl.s_maxage -webob.cachecontrol.CacheControl.stale_if_error -webob.cachecontrol.CacheControl.stale_while_revalidate -webob.cachecontrol.CacheControl.update_dict webob.cachecontrol.UpdateDict.setdefault +# Even though at runtime the default argument has a default value of `None` +# that will cause an exception, so we're better off pretending the argument +# is required, and that it can't be `None` +webob.headers.ResponseHeaders.setdefault +webob.multidict.GetDict.setdefault + # These need to be ignored due to how WebOb decided to let people know # that certain methods on `NestedMultiDict` should not be called since # they are immutable, compared to a MultiDict, but still can be used @@ -138,8 +117,14 @@ webob.cachecontrol.UpdateDict.setdefault # that accept any parameters and assign them to methods which should still # satisfy the same interface. The type annotations enforce the correct # input arguments instead of the generic ones. -webob.multidict.NestedMultiDict.popitem +webob.multidict.NestedMultiDict.__delitem__ +webob.multidict.NestedMultiDict.__setitem__ +webob.multidict.NestedMultiDict.add webob.multidict.NestedMultiDict.clear +webob.multidict.NestedMultiDict.pop +webob.multidict.NestedMultiDict.popitem +webob.multidict.NestedMultiDict.setdefault +webob.multidict.NestedMultiDict.update # The `DEFAULT` parameter on these dunder methods don't really make sense as # part of the public API, so they have been removed from the stubs @@ -177,3 +162,6 @@ webob.multidict.NoVars.__bool__ # with a use-case where the distinction matters, besides inheriting from # the class and overwriting the __init__ and forgetting to populate `write`. webob.response.ResponseBodyFile.write + +# A couple of utility types we use in multiple modules +webob._types diff --git a/stubs/WebOb/@tests/test_cases/check_cachecontrol.py b/stubs/WebOb/@tests/test_cases/check_cachecontrol.py new file mode 100644 index 000000000..dd20460e4 --- /dev/null +++ b/stubs/WebOb/@tests/test_cases/check_cachecontrol.py @@ -0,0 +1,102 @@ +from __future__ import annotations + +from typing import Any, Literal, Union +from typing_extensions import assert_type + +from webob.cachecontrol import CacheControl +from webob.request import BaseRequest +from webob.response import Response + +req = BaseRequest({}) +res = Response() +assert_type(req.cache_control, CacheControl[Literal["request"]]) +assert_type(res.cache_control, CacheControl[Literal["response"]]) + +assert_type(CacheControl.parse(""), CacheControl[None]) +assert_type(CacheControl.parse("", type="request"), CacheControl[Literal["request"]]) +assert_type(CacheControl.parse("", type="response"), CacheControl[Literal["response"]]) + +req_cc = req.cache_control +res_cc = res.cache_control +shared_cc = CacheControl.parse("") +assert_type(req_cc, CacheControl[Literal["request"]]) +assert_type(res_cc, CacheControl[Literal["response"]]) +assert_type(shared_cc, CacheControl[None]) +any_cc = CacheControl[Any]({}, None) + +assert_type(req_cc.max_stale, Union[int, Literal["*"], None]) +res_cc.max_stale # type: ignore +shared_cc.max_stale # type: ignore +assert_type(any_cc.max_stale, Union[int, Literal["*"], None]) + +assert_type(req_cc.min_fresh, Union[int, None]) +res_cc.min_fresh # type: ignore +shared_cc.min_fresh # type: ignore +assert_type(any_cc.min_fresh, Union[int, None]) + +assert_type(req_cc.only_if_cached, bool) +res_cc.only_if_cached # type: ignore +shared_cc.only_if_cached # type: ignore +assert_type(any_cc.only_if_cached, bool) + +req_cc.public # type: ignore +assert_type(res_cc.public, bool) +shared_cc.public # type: ignore +assert_type(any_cc.public, bool) + +# NOTE: pyright gets confused about the `Literal["*"]` the types match +req_cc.private # type: ignore +assert_type(res_cc.private, Union[str, Literal["*"], None]) # pyright: ignore +shared_cc.private # type: ignore +assert_type(any_cc.private, Union[str, Literal["*"], None]) # pyright: ignore + +assert_type(req_cc.no_cache, Union[str, Literal["*"], None]) # pyright: ignore +assert_type(res_cc.no_cache, Union[str, Literal["*"], None]) # pyright: ignore +assert_type(shared_cc.no_cache, Union[str, Literal["*"], None]) # pyright: ignore +assert_type(any_cc.no_cache, Union[str, Literal["*"], None]) # pyright: ignore + +assert_type(req_cc.no_store, bool) +assert_type(res_cc.no_store, bool) +assert_type(shared_cc.no_store, bool) +assert_type(any_cc.no_store, bool) + +assert_type(req_cc.no_transform, bool) +assert_type(res_cc.no_transform, bool) +assert_type(shared_cc.no_transform, bool) +assert_type(any_cc.no_transform, bool) + +req_cc.must_revalidate # type: ignore +assert_type(res_cc.must_revalidate, bool) +shared_cc.must_revalidate # type: ignore +assert_type(any_cc.must_revalidate, bool) + +req_cc.proxy_revalidate # type: ignore +assert_type(res_cc.proxy_revalidate, bool) +shared_cc.proxy_revalidate # type: ignore +assert_type(any_cc.proxy_revalidate, bool) + +# NOTE: pyright gets confused about the `Literal[-1]` the types match +assert_type(req_cc.max_age, Union[int, Literal[-1], None]) # pyright: ignore +assert_type(res_cc.max_age, Union[int, Literal[-1], None]) # pyright: ignore +assert_type(shared_cc.max_age, Union[int, Literal[-1], None]) # pyright: ignore +assert_type(any_cc.max_age, Union[int, Literal[-1], None]) # pyright: ignore + +req_cc.s_maxage # type: ignore +assert_type(res_cc.s_maxage, Union[int, None]) +shared_cc.s_maxage # type: ignore +assert_type(any_cc.s_maxage, Union[int, None]) + +req_cc.s_max_age # type: ignore +assert_type(res_cc.s_max_age, Union[int, None]) +shared_cc.s_max_age # type: ignore +assert_type(any_cc.s_max_age, Union[int, None]) + +req_cc.stale_while_revalidate # type: ignore +assert_type(res_cc.stale_while_revalidate, Union[int, None]) +shared_cc.stale_while_revalidate # type: ignore +assert_type(any_cc.stale_while_revalidate, Union[int, None]) + +req_cc.stale_if_error # type: ignore +assert_type(res_cc.stale_if_error, Union[int, None]) +shared_cc.stale_if_error # type: ignore +assert_type(any_cc.stale_if_error, Union[int, None]) diff --git a/stubs/WebOb/@tests/test_cases/check_wsgify.py b/stubs/WebOb/@tests/test_cases/check_wsgify.py index 0ca91e28a..93de11429 100644 --- a/stubs/WebOb/@tests/test_cases/check_wsgify.py +++ b/stubs/WebOb/@tests/test_cases/check_wsgify.py @@ -56,10 +56,10 @@ def app(request: Request) -> str: application = app -assert_type(app, "wsgify[Request, []]") +assert_type(app, "wsgify[[], Request]") assert_type(app(env, start_response), "Iterable[bytes]") assert_type(app(request), _AnyResponse) -assert_type(app(application), "wsgify[Request, []]") +assert_type(app(application), "wsgify[[], Request]") application = app(application) @@ -75,10 +75,10 @@ def m_app(request: Request) -> str: application = m_app -assert_type(m_app, "wsgify[Request, [WSGIApplication]]") +assert_type(m_app, "wsgify[[WSGIApplication], Request]") assert_type(m_app(env, start_response), "Iterable[bytes]") assert_type(m_app(request), _AnyResponse) -assert_type(m_app(application), "wsgify[Request, [WSGIApplication]]") +assert_type(m_app(application), "wsgify[[WSGIApplication], Request]") application = m_app(application) @@ -93,7 +93,7 @@ def my_request_app(request: MyRequest) -> None: application = my_request_app -assert_type(my_request_app, "wsgify[MyRequest, []]") +assert_type(my_request_app, "wsgify[[], MyRequest]") # we are allowed to accept a less specific request class diff --git a/stubs/WebOb/webob/__init__.pyi b/stubs/WebOb/webob/__init__.pyi index 308be3055..b190ee568 100644 --- a/stubs/WebOb/webob/__init__.pyi +++ b/stubs/WebOb/webob/__init__.pyi @@ -11,3 +11,18 @@ from webob.datetime_utils import ( from webob.request import LegacyRequest as LegacyRequest, Request as Request from webob.response import Response as Response from webob.util import html_escape as html_escape + +__all__ = [ + "Request", + "LegacyRequest", + "Response", + "UTC", + "day", + "week", + "hour", + "minute", + "second", + "month", + "year", + "html_escape", +] diff --git a/stubs/WebOb/webob/_types.pyi b/stubs/WebOb/webob/_types.pyi new file mode 100644 index 000000000..08fdc54b3 --- /dev/null +++ b/stubs/WebOb/webob/_types.pyi @@ -0,0 +1,21 @@ +from typing import Protocol, TypeVar, overload +from typing_extensions import TypeAlias + +_T = TypeVar("_T") +_GetterReturnType_co = TypeVar("_GetterReturnType_co", covariant=True) +_SetterValueType_contra = TypeVar("_SetterValueType_contra", contravariant=True) + +class AsymmetricProperty(Protocol[_GetterReturnType_co, _SetterValueType_contra]): + @overload + def __get__(self, obj: None, type: type[object] | None = ..., /) -> property: ... + @overload + def __get__(self, obj: object, type: type[object] | None = ..., /) -> _GetterReturnType_co: ... + def __set__(self, obj: object, value: _SetterValueType_contra, /) -> None: ... + +class AsymmetricPropertyWithDelete( + AsymmetricProperty[_GetterReturnType_co, _SetterValueType_contra], Protocol[_GetterReturnType_co, _SetterValueType_contra] +): + def __delete__(self, obj: object, /) -> None: ... + +SymmetricProperty: TypeAlias = AsymmetricProperty[_T, _T] +SymmetricPropertyWithDelete: TypeAlias = AsymmetricPropertyWithDelete[_T, _T] diff --git a/stubs/WebOb/webob/acceptparse.pyi b/stubs/WebOb/webob/acceptparse.pyi index 56d5e5853..5b3043e7b 100644 --- a/stubs/WebOb/webob/acceptparse.pyi +++ b/stubs/WebOb/webob/acceptparse.pyi @@ -1,12 +1,67 @@ from _typeshed import SupportsItems -from collections.abc import Callable, Iterable, Iterator -from typing import Any, Literal, NamedTuple, TypeVar, overload +from collections.abc import Callable, Iterable, Iterator, Sequence +from typing import Any, Literal, NamedTuple, Protocol, TypeVar, overload from typing_extensions import Self, TypeAlias +from webob._types import AsymmetricPropertyWithDelete + _T = TypeVar("_T") _ListOrTuple: TypeAlias = list[_T] | tuple[_T, ...] _ParsedAccept: TypeAlias = tuple[str, float, list[tuple[str, str]], list[str | tuple[str, str]]] +class _SupportsStr(Protocol): + def __str__(self) -> str: ... # noqa: Y029 + +_AnyAcceptHeader: TypeAlias = AcceptValidHeader | AcceptInvalidHeader | AcceptNoHeader +_AnyAcceptCharsetHeader: TypeAlias = AcceptCharsetValidHeader | AcceptCharsetInvalidHeader | AcceptCharsetNoHeader +_AnyAcceptEncodingHeader: TypeAlias = AcceptEncodingValidHeader | AcceptEncodingInvalidHeader | AcceptEncodingNoHeader +_AnyAcceptLanguageHeader: TypeAlias = AcceptLanguageValidHeader | AcceptLanguageInvalidHeader | AcceptLanguageNoHeader + +_AcceptProperty: TypeAlias = AsymmetricPropertyWithDelete[ + _AnyAcceptHeader, + ( + _AnyAcceptHeader + | SupportsItems[str, float | tuple[float, str]] + | _ListOrTuple[str | tuple[str, float, str] | list[Any]] + | _SupportsStr + | str + | None + ), +] +_AcceptCharsetProperty: TypeAlias = AsymmetricPropertyWithDelete[ + _AnyAcceptCharsetHeader, + ( + _AnyAcceptCharsetHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), +] +_AcceptEncodingProperty: TypeAlias = AsymmetricPropertyWithDelete[ + _AnyAcceptEncodingHeader, + ( + _AnyAcceptEncodingHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), +] +_AcceptLanguageProperty: TypeAlias = AsymmetricPropertyWithDelete[ + _AnyAcceptLanguageHeader, + ( + _AnyAcceptLanguageHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), +] + class AcceptOffer(NamedTuple): type: str subtype: str @@ -22,30 +77,38 @@ class AcceptValidHeader(Accept): @property def header_value(self) -> str: ... @property - def parsed(self) -> list[_ParsedAccept] | None: ... + def parsed(self) -> list[_ParsedAccept]: ... def __init__(self, header_value: str) -> None: ... def copy(self) -> Self: ... - @overload - def __add__(self, other: str | None) -> Self: ... - @overload - def __add__(self, other: AcceptNoHeader | AcceptValidHeader | AcceptInvalidHeader) -> Self: ... - @overload - def __add__(self, other: SupportsItems[str, float | tuple[float, str]]) -> Self: ... - @overload - def __add__(self, other: _ListOrTuple[str | tuple[str, float, str] | list[Any]]) -> Self: ... + def __add__( + self, + other: ( + _AnyAcceptHeader + | SupportsItems[str, float | tuple[float, str]] + | _ListOrTuple[str | tuple[str, float, str] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self: ... def __bool__(self) -> Literal[True]: ... def __contains__(self, offer: str) -> bool: ... def __iter__(self) -> Iterator[str]: ... - @overload - def __radd__(self, other: str | None) -> Self: ... - @overload - def __radd__(self, other: SupportsItems[str, float | tuple[float, str]]) -> Self: ... - @overload - def __radd__(self, other: _ListOrTuple[str | tuple[str, float, str] | list[Any]]) -> Self: ... + def __radd__( + self, + other: ( + _AnyAcceptHeader + | SupportsItems[str, float | tuple[float, str]] + | _ListOrTuple[str | tuple[str, float, str] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self: ... def accept_html(self) -> bool: ... @property def accepts_html(self) -> bool: ... - def acceptable_offers(self, offers: Iterable[str | AcceptOffer]) -> list[tuple[str, float]]: ... + def acceptable_offers(self, offers: Sequence[str]) -> list[tuple[str, float]]: ... @overload def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: None = None) -> str | None: ... @overload @@ -53,40 +116,13 @@ class AcceptValidHeader(Accept): def quality(self, offer: str) -> float | None: ... class _AcceptInvalidOrNoHeader(Accept): - @property - def parsed(self) -> None: ... - def copy(self) -> Self: ... - @overload - def __add__(self, other: None) -> AcceptNoHeader: ... - @overload - def __add__(self, other: str) -> AcceptValidHeader | AcceptNoHeader: ... - @overload - def __add__(self, other: AcceptValidHeader) -> AcceptValidHeader: ... - @overload - def __add__(self, other: AcceptNoHeader | AcceptInvalidHeader) -> AcceptNoHeader: ... - @overload - def __add__(self, other: SupportsItems[str, float | tuple[float, str]]) -> AcceptValidHeader | AcceptNoHeader: ... - @overload - def __add__(self, other: _ListOrTuple[str | tuple[str, float, str] | list[Any]]) -> AcceptValidHeader | AcceptNoHeader: ... - @overload - def __radd__(self, other: None) -> AcceptNoHeader: ... - @overload - def __radd__(self, other: str) -> AcceptValidHeader | AcceptNoHeader: ... - @overload - def __radd__(self, other: AcceptValidHeader) -> AcceptValidHeader: ... - @overload - def __radd__(self, other: AcceptNoHeader | AcceptInvalidHeader) -> AcceptNoHeader: ... - @overload - def __radd__(self, other: SupportsItems[str, float | tuple[float, str]]) -> AcceptValidHeader | AcceptNoHeader: ... - @overload - def __radd__(self, other: _ListOrTuple[str | tuple[str, float, str] | list[Any]]) -> AcceptValidHeader | AcceptNoHeader: ... def __bool__(self) -> Literal[False]: ... def __contains__(self, offer: str) -> Literal[True]: ... def __iter__(self) -> Iterator[str]: ... def accept_html(self) -> bool: ... @property def accepts_html(self) -> bool: ... - def acceptable_offers(self, offers: Iterable[str | AcceptOffer]) -> list[tuple[str, float]]: ... + def acceptable_offers(self, offers: Sequence[str]) -> list[tuple[str, float]]: ... @overload def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: None = None) -> str | None: ... @overload @@ -96,33 +132,93 @@ class _AcceptInvalidOrNoHeader(Accept): class AcceptNoHeader(_AcceptInvalidOrNoHeader): @property def header_value(self) -> None: ... + @property + def parsed(self) -> None: ... def __init__(self) -> None: ... + def copy(self) -> Self: ... + @overload + def __add__(self, other: AcceptValidHeader | Literal[""]) -> AcceptValidHeader: ... + @overload + def __add__(self, other: AcceptNoHeader | AcceptInvalidHeader | None) -> Self: ... + @overload + def __add__( + self, + other: ( + _AnyAcceptHeader + | SupportsItems[str, float | tuple[float, str]] + | _ListOrTuple[str | tuple[str, float, str] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self | AcceptValidHeader: ... + @overload + def __radd__(self, other: AcceptValidHeader | Literal[""]) -> AcceptValidHeader: ... + @overload + def __radd__(self, other: AcceptNoHeader | AcceptInvalidHeader | None) -> Self: ... + @overload + def __radd__( + self, + other: ( + _AnyAcceptHeader + | SupportsItems[str, float | tuple[float, str]] + | _ListOrTuple[str | tuple[str, float, str] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self | AcceptValidHeader: ... class AcceptInvalidHeader(_AcceptInvalidOrNoHeader): @property def header_value(self) -> str: ... + @property + def parsed(self) -> None: ... def __init__(self, header_value: str) -> None: ... + def copy(self) -> Self: ... + @overload + def __add__(self, other: AcceptValidHeader | Literal[""]) -> AcceptValidHeader: ... + @overload + def __add__(self, other: AcceptInvalidHeader | AcceptNoHeader | None) -> AcceptNoHeader: ... + @overload + def __add__( + self, + other: ( + _AnyAcceptHeader + | SupportsItems[str, float | tuple[float, str]] + | _ListOrTuple[str | tuple[str, float, str] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> AcceptValidHeader | AcceptNoHeader: ... + @overload + def __radd__(self, other: AcceptValidHeader | Literal[""]) -> AcceptValidHeader: ... + @overload + def __radd__(self, other: AcceptInvalidHeader | AcceptNoHeader | None) -> AcceptNoHeader: ... + @overload + def __radd__( + self, + other: ( + _AnyAcceptHeader + | SupportsItems[str, float | tuple[float, str]] + | _ListOrTuple[str | tuple[str, float, str] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> AcceptValidHeader | AcceptNoHeader: ... @overload -def create_accept_header(header_value: None) -> AcceptNoHeader: ... +def create_accept_header(header_value: AcceptValidHeader | Literal[""]) -> AcceptValidHeader: ... +@overload +def create_accept_header(header_value: AcceptInvalidHeader) -> AcceptInvalidHeader: ... +@overload +def create_accept_header(header_value: None | AcceptNoHeader) -> AcceptNoHeader: ... @overload def create_accept_header(header_value: str) -> AcceptValidHeader | AcceptInvalidHeader: ... - -class _AcceptProperty: - @overload - def __get__(self, obj: None, type: type | None = ..., /) -> property: ... - @overload - def __get__(self, obj: Any, type: type | None = ..., /) -> AcceptNoHeader | AcceptValidHeader | AcceptInvalidHeader: ... - @overload - def __set__(self, obj: Any, value: str | None, /) -> None: ... - @overload - def __set__(self, obj: Any, value: AcceptNoHeader | AcceptValidHeader | AcceptInvalidHeader, /) -> None: ... - @overload - def __set__(self, obj: Any, value: SupportsItems[str, float | tuple[float, str]], /) -> None: ... - @overload - def __set__(self, obj: Any, value: _ListOrTuple[str | tuple[str, float, str] | list[Any]], /) -> None: ... - def __delete__(self, obj: Any, /) -> None: ... - +@overload +def create_accept_header(header_value: _AnyAcceptHeader | str | None) -> _AnyAcceptHeader: ... def accept_property() -> _AcceptProperty: ... class AcceptCharset: @@ -136,108 +232,143 @@ class AcceptCharsetValidHeader(AcceptCharset): def parsed(self) -> list[tuple[str, float]]: ... def __init__(self, header_value: str) -> None: ... def copy(self) -> Self: ... - @overload - def __add__(self, other: str | None) -> Self: ... - @overload - def __add__(self, other: AcceptCharsetValidHeader | AcceptCharsetNoHeader | AcceptCharsetInvalidHeader) -> Self: ... - @overload - def __add__(self, other: SupportsItems[str, float]) -> Self: ... - @overload - def __add__(self, other: _ListOrTuple[str | tuple[str, float] | list[Any]]) -> Self: ... + def __add__( + self, + other: ( + _AnyAcceptCharsetHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self: ... def __bool__(self) -> Literal[True]: ... def __contains__(self, offer: str) -> bool: ... def __iter__(self) -> Iterator[str]: ... + def __radd__( + self, + other: ( + _AnyAcceptCharsetHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self: ... + def acceptable_offers(self, offers: Sequence[str]) -> list[tuple[str, float]]: ... @overload - def __radd__(self, other: str | None) -> Self: ... + def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: None = None) -> str | None: ... @overload - def __radd__(self, other: AcceptCharsetValidHeader | AcceptCharsetNoHeader | AcceptCharsetInvalidHeader) -> Self: ... - @overload - def __radd__(self, other: SupportsItems[str, float]) -> Self: ... - @overload - def __radd__(self, other: _ListOrTuple[str | tuple[str, float] | list[Any]]) -> Self: ... - def acceptable_offers(self, offers: Iterable[str]) -> list[tuple[str, float]]: ... - @overload - def best_match(self, offers: Iterable[str], default_match: None = None) -> str | None: ... - @overload - def best_match(self, offers: Iterable[str], default_match: str) -> str: ... + def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: str) -> str: ... def quality(self, offer: str) -> float | None: ... class _AcceptCharsetInvalidOrNoHeader(AcceptCharset): - @property - def parsed(self) -> None: ... def __bool__(self) -> Literal[False]: ... def __contains__(self, offer: str) -> Literal[True]: ... def __iter__(self) -> Iterator[str]: ... - @overload - def __add__(self, other: None) -> AcceptCharsetNoHeader: ... - @overload - def __add__(self, other: str) -> AcceptCharsetValidHeader | AcceptCharsetNoHeader: ... - @overload - def __add__(self, other: AcceptCharsetValidHeader) -> AcceptCharsetValidHeader: ... - @overload - def __add__(self, other: AcceptCharsetNoHeader | AcceptCharsetInvalidHeader) -> AcceptCharsetNoHeader: ... - @overload - def __add__(self, other: SupportsItems[str, float]) -> AcceptCharsetValidHeader | AcceptCharsetNoHeader: ... - @overload - def __add__( - self, other: _ListOrTuple[str | tuple[str, float] | list[Any]] - ) -> AcceptCharsetValidHeader | AcceptCharsetNoHeader: ... - @overload - def __radd__(self, other: None) -> AcceptCharsetNoHeader: ... - @overload - def __radd__(self, other: str) -> AcceptCharsetValidHeader | AcceptCharsetNoHeader: ... - @overload - def __radd__(self, other: AcceptCharsetValidHeader) -> AcceptCharsetValidHeader: ... - @overload - def __radd__(self, other: AcceptCharsetNoHeader | AcceptCharsetInvalidHeader) -> AcceptCharsetNoHeader: ... - @overload - def __radd__(self, other: SupportsItems[str, float]) -> AcceptCharsetValidHeader | AcceptCharsetNoHeader: ... - @overload - def __radd__( - self, other: _ListOrTuple[str | tuple[str, float] | list[Any]] - ) -> AcceptCharsetValidHeader | AcceptCharsetNoHeader: ... - def copy(self) -> Self: ... def acceptable_offers(self, offers: Iterable[str]) -> list[tuple[str, float]]: ... @overload - def best_match(self, offers: Iterable[str], default_match: None = None) -> str | None: ... + def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: None = None) -> str | None: ... @overload - def best_match(self, offers: Iterable[str], default_match: str) -> str: ... + def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: str) -> str: ... def quality(self, offer: str) -> float | None: ... class AcceptCharsetNoHeader(_AcceptCharsetInvalidOrNoHeader): @property def header_value(self) -> None: ... + @property + def parsed(self) -> None: ... def __init__(self) -> None: ... + def copy(self) -> Self: ... + @overload + def __add__(self, other: AcceptCharsetValidHeader) -> AcceptCharsetValidHeader: ... + @overload + def __add__(self, other: AcceptCharsetInvalidHeader | AcceptCharsetNoHeader | Literal[""] | None) -> Self: ... + @overload + def __add__( + self, + other: ( + _AnyAcceptCharsetHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self | AcceptCharsetValidHeader: ... + @overload + def __radd__(self, other: AcceptCharsetValidHeader) -> AcceptCharsetValidHeader: ... + @overload + def __radd__(self, other: AcceptCharsetInvalidHeader | AcceptCharsetNoHeader | Literal[""] | None) -> Self: ... + @overload + def __radd__( + self, + other: ( + _AnyAcceptCharsetHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self | AcceptCharsetValidHeader: ... class AcceptCharsetInvalidHeader(_AcceptCharsetInvalidOrNoHeader): @property def header_value(self) -> str: ... + @property + def parsed(self) -> None: ... def __init__(self, header_value: str) -> None: ... + def copy(self) -> Self: ... + @overload + def __add__(self, other: AcceptCharsetValidHeader) -> AcceptCharsetValidHeader: ... + @overload + def __add__( + self, other: AcceptCharsetInvalidHeader | AcceptCharsetNoHeader | Literal[""] | None + ) -> AcceptCharsetNoHeader: ... + @overload + def __add__( + self, + other: ( + _AnyAcceptCharsetHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> AcceptCharsetValidHeader | AcceptCharsetNoHeader: ... + @overload + def __radd__(self, other: AcceptCharsetValidHeader) -> AcceptCharsetValidHeader: ... + @overload + def __radd__( + self, other: AcceptCharsetInvalidHeader | AcceptCharsetNoHeader | Literal[""] | None + ) -> AcceptCharsetNoHeader: ... + @overload + def __radd__( + self, + other: ( + _AnyAcceptCharsetHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> AcceptCharsetValidHeader | AcceptCharsetNoHeader: ... @overload -def create_accept_charset_header(header_value: None) -> AcceptCharsetNoHeader: ... +def create_accept_charset_header(header_value: AcceptCharsetValidHeader | Literal[""]) -> AcceptCharsetValidHeader: ... +@overload +def create_accept_charset_header(header_value: AcceptCharsetInvalidHeader) -> AcceptCharsetInvalidHeader: ... +@overload +def create_accept_charset_header(header_value: AcceptCharsetNoHeader | None) -> AcceptCharsetNoHeader: ... @overload def create_accept_charset_header(header_value: str) -> AcceptCharsetValidHeader | AcceptCharsetInvalidHeader: ... - -class _AcceptCharsetProperty: - @overload - def __get__(self, obj: None, type: type | None = ..., /) -> property: ... - @overload - def __get__( - self, obj: Any, type: type | None = ..., / - ) -> AcceptCharsetNoHeader | AcceptCharsetValidHeader | AcceptCharsetInvalidHeader: ... - @overload - def __set__(self, obj: Any, value: str | None, /) -> None: ... - @overload - def __set__( - self, obj: Any, value: AcceptCharsetNoHeader | AcceptCharsetValidHeader | AcceptCharsetInvalidHeader, / - ) -> None: ... - @overload - def __set__(self, obj: Any, value: SupportsItems[str, float], /) -> None: ... - @overload - def __set__(self, obj: Any, value: _ListOrTuple[str | tuple[str, float] | list[Any]], /) -> None: ... - def __delete__(self, obj: Any, /) -> None: ... - +@overload +def create_accept_charset_header(header_value: _AnyAcceptCharsetHeader | str | None) -> _AnyAcceptCharsetHeader: ... def accept_charset_property() -> _AcceptCharsetProperty: ... class AcceptEncoding: @@ -251,108 +382,139 @@ class AcceptEncodingValidHeader(AcceptEncoding): def parsed(self) -> list[tuple[str, float]]: ... def __init__(self, header_value: str) -> None: ... def copy(self) -> Self: ... - @overload - def __add__(self, other: str | None) -> Self: ... - @overload - def __add__(self, other: AcceptEncodingValidHeader | AcceptEncodingNoHeader | AcceptEncodingInvalidHeader) -> Self: ... - @overload - def __add__(self, other: SupportsItems[str, float]) -> Self: ... - @overload - def __add__(self, other: _ListOrTuple[str | tuple[str, float] | list[Any]]) -> Self: ... + def __add__( + self, + other: ( + _AnyAcceptEncodingHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self: ... def __bool__(self) -> Literal[True]: ... def __contains__(self, offer: str) -> bool: ... def __iter__(self) -> Iterator[str]: ... + def __radd__( + self, + other: ( + _AnyAcceptEncodingHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self: ... + def acceptable_offers(self, offers: Sequence[str]) -> list[tuple[str, float]]: ... @overload - def __radd__(self, other: str | None) -> Self: ... + def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: None = None) -> str | None: ... @overload - def __radd__(self, other: AcceptEncodingValidHeader | AcceptEncodingNoHeader | AcceptEncodingInvalidHeader) -> Self: ... - @overload - def __radd__(self, other: SupportsItems[str, float]) -> Self: ... - @overload - def __radd__(self, other: _ListOrTuple[str | tuple[str, float] | list[Any]]) -> Self: ... - def acceptable_offers(self, offers: Iterable[str]) -> list[tuple[str, float]]: ... - @overload - def best_match(self, offers: Iterable[str], default_match: None = None) -> str | None: ... - @overload - def best_match(self, offers: Iterable[str], default_match: str) -> str: ... + def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: str) -> str: ... def quality(self, offer: str) -> float | None: ... class _AcceptEncodingInvalidOrNoHeader(AcceptEncoding): - @property - def parsed(self) -> None: ... def __bool__(self) -> Literal[False]: ... def __contains__(self, offer: str) -> Literal[True]: ... def __iter__(self) -> Iterator[str]: ... - @overload - def __add__(self, other: None) -> AcceptEncodingNoHeader: ... - @overload - def __add__(self, other: str) -> AcceptEncodingValidHeader | AcceptEncodingNoHeader: ... - @overload - def __add__(self, other: AcceptEncodingValidHeader) -> AcceptEncodingValidHeader: ... - @overload - def __add__(self, other: AcceptEncodingNoHeader | AcceptEncodingInvalidHeader) -> AcceptEncodingNoHeader: ... - @overload - def __add__(self, other: SupportsItems[str, float]) -> AcceptEncodingValidHeader | AcceptEncodingNoHeader: ... - @overload - def __add__( - self, other: _ListOrTuple[str | tuple[str, float] | list[Any]] - ) -> AcceptEncodingValidHeader | AcceptEncodingNoHeader: ... - @overload - def __radd__(self, other: None) -> AcceptEncodingNoHeader: ... - @overload - def __radd__(self, other: str) -> AcceptEncodingValidHeader | AcceptEncodingNoHeader: ... - @overload - def __radd__(self, other: AcceptEncodingValidHeader) -> AcceptEncodingValidHeader: ... - @overload - def __radd__(self, other: AcceptEncodingNoHeader | AcceptEncodingInvalidHeader) -> AcceptEncodingNoHeader: ... - @overload - def __radd__(self, other: SupportsItems[str, float]) -> AcceptEncodingValidHeader | AcceptEncodingNoHeader: ... - @overload - def __radd__( - self, other: _ListOrTuple[str | tuple[str, float] | list[Any]] - ) -> AcceptEncodingValidHeader | AcceptEncodingNoHeader: ... - def copy(self) -> Self: ... def acceptable_offers(self, offers: Iterable[str]) -> list[tuple[str, float]]: ... @overload - def best_match(self, offers: Iterable[str], default_match: None = None) -> str | None: ... + def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: None = None) -> str | None: ... @overload - def best_match(self, offers: Iterable[str], default_match: str) -> str: ... + def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: str) -> str: ... def quality(self, offer: str) -> float | None: ... class AcceptEncodingNoHeader(_AcceptEncodingInvalidOrNoHeader): @property def header_value(self) -> None: ... + @property + def parsed(self) -> None: ... def __init__(self) -> None: ... + def copy(self) -> Self: ... + @overload + def __add__(self, other: AcceptEncodingValidHeader | Literal[""]) -> AcceptEncodingValidHeader: ... + @overload + def __add__(self, other: AcceptEncodingInvalidHeader | AcceptEncodingNoHeader | None) -> Self: ... + @overload + def __add__( + self, + other: ( + _AnyAcceptEncodingHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self | AcceptEncodingValidHeader: ... + @overload + def __radd__(self, other: AcceptEncodingValidHeader | Literal[""]) -> AcceptEncodingValidHeader: ... + @overload + def __radd__(self, other: AcceptEncodingInvalidHeader | AcceptEncodingNoHeader | None) -> Self: ... + @overload + def __radd__( + self, + other: ( + _AnyAcceptEncodingHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self | AcceptEncodingValidHeader: ... class AcceptEncodingInvalidHeader(_AcceptEncodingInvalidOrNoHeader): @property def header_value(self) -> str: ... + @property + def parsed(self) -> None: ... def __init__(self, header_value: str) -> None: ... + def copy(self) -> Self: ... + @overload + def __add__(self, other: AcceptEncodingValidHeader | Literal[""]) -> AcceptEncodingValidHeader: ... + @overload + def __add__(self, other: AcceptEncodingInvalidHeader | AcceptEncodingNoHeader | None) -> AcceptEncodingNoHeader: ... + @overload + def __add__( + self, + other: ( + _AnyAcceptEncodingHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> AcceptEncodingValidHeader | AcceptEncodingNoHeader: ... + @overload + def __radd__(self, other: AcceptEncodingValidHeader | Literal[""]) -> AcceptEncodingValidHeader: ... + @overload + def __radd__(self, other: AcceptEncodingInvalidHeader | AcceptEncodingNoHeader | None) -> AcceptEncodingNoHeader: ... + @overload + def __radd__( + self, + other: ( + _AnyAcceptEncodingHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> AcceptEncodingValidHeader | AcceptEncodingNoHeader: ... @overload -def create_accept_encoding_header(header_value: None) -> AcceptEncodingNoHeader: ... +def create_accept_encoding_header(header_value: AcceptEncodingValidHeader | Literal[""]) -> AcceptEncodingValidHeader: ... +@overload +def create_accept_encoding_header(header_value: AcceptEncodingInvalidHeader) -> AcceptEncodingInvalidHeader: ... +@overload +def create_accept_encoding_header(header_value: AcceptEncodingNoHeader | None) -> AcceptEncodingNoHeader: ... @overload def create_accept_encoding_header(header_value: str) -> AcceptEncodingValidHeader | AcceptEncodingInvalidHeader: ... - -class _AcceptEncodingProperty: - @overload - def __get__(self, obj: None, type: type | None = ..., /) -> property: ... - @overload - def __get__( - self, obj: Any, type: type | None = ..., / - ) -> AcceptEncodingNoHeader | AcceptEncodingValidHeader | AcceptEncodingInvalidHeader: ... - @overload - def __set__(self, obj: Any, value: str | None, /) -> None: ... - @overload - def __set__( - self, obj: Any, value: AcceptEncodingNoHeader | AcceptEncodingValidHeader | AcceptEncodingInvalidHeader, / - ) -> None: ... - @overload - def __set__(self, obj: Any, value: SupportsItems[str, float], /) -> None: ... - @overload - def __set__(self, obj: Any, value: _ListOrTuple[str | tuple[str, float] | list[Any]], /) -> None: ... - def __delete__(self, obj: Any, /) -> None: ... - +@overload +def create_accept_encoding_header(header_value: _AnyAcceptEncodingHeader | str | None) -> _AnyAcceptEncodingHeader: ... def accept_encoding_property() -> _AcceptEncodingProperty: ... class AcceptLanguage: @@ -360,144 +522,193 @@ class AcceptLanguage: def parse(cls, value: str) -> Iterator[tuple[str, float]]: ... class AcceptLanguageValidHeader(AcceptLanguage): + def __init__(self, header_value: str) -> None: ... + def copy(self) -> Self: ... @property def header_value(self) -> str: ... @property def parsed(self) -> list[tuple[str, float]]: ... - def __init__(self, header_value: str) -> None: ... - def copy(self) -> Self: ... - @overload - def __add__(self, other: str | None) -> Self: ... - @overload - def __add__(self, other: AcceptLanguageValidHeader | AcceptLanguageNoHeader | AcceptLanguageInvalidHeader) -> Self: ... - @overload - def __add__(self, other: SupportsItems[str, float]) -> Self: ... - @overload - def __add__(self, other: _ListOrTuple[str | tuple[str, float] | list[Any]]) -> Self: ... + def __add__( + self, + other: ( + _AnyAcceptLanguageHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self: ... def __bool__(self) -> Literal[True]: ... def __contains__(self, offer: str) -> bool: ... def __iter__(self) -> Iterator[str]: ... + def __radd__( + self, + other: ( + _AnyAcceptLanguageHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self: ... + def basic_filtering(self, language_tags: Sequence[str]) -> list[tuple[str, float]]: ... @overload - def __radd__(self, other: str | None) -> Self: ... + def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: None = None) -> str | None: ... @overload - def __radd__(self, other: AcceptLanguageValidHeader | AcceptLanguageNoHeader | AcceptLanguageInvalidHeader) -> Self: ... - @overload - def __radd__(self, other: SupportsItems[str, float]) -> Self: ... - @overload - def __radd__(self, other: _ListOrTuple[str | tuple[str, float] | list[Any]]) -> Self: ... - def basic_filtering(self, language_tags: Iterable[str]) -> list[tuple[str, float]]: ... - @overload - def best_match(self, offers: Iterable[str], default_match: None = None) -> str | None: ... - @overload - def best_match(self, offers: Iterable[str], default_match: str) -> str: ... + def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: str) -> str: ... @overload def lookup( - self, - language_tags: Iterable[str], - default_range: str | None = None, - default_tag: str = ..., - default: str | Callable[[], str | None] | None = None, + self, language_tags: Sequence[str], default_range: str | None, default_tag: str, default: None = None ) -> str | None: ... @overload def lookup( - self, - language_tags: Iterable[str], - default_range: str | None = None, - default_tag: str | None = None, - default: str | Callable[[], str | None] = ..., + self, language_tags: Sequence[str], *, default_range: str | None = None, default_tag: str, default: None = None ) -> str | None: ... + @overload + def lookup( + self, language_tags: Sequence[str], default_range: str | None, default_tag: None, default: _T | Callable[[], _T] + ) -> _T | str | None: ... + @overload + def lookup( + self, language_tags: Sequence[str], default_range: str | None, default_tag: str, default: _T | Callable[[], _T] + ) -> _T | str: ... + @overload + def lookup( + self, + language_tags: Sequence[str], + *, + default_range: str | None = None, + default_tag: None = None, + default: _T | Callable[[], _T], + ) -> _T | str | None: ... + @overload + def lookup( + self, language_tags: Sequence[str], *, default_range: str | None = None, default_tag: str, default: _T | Callable[[], _T] + ) -> _T | str: ... def quality(self, offer: str) -> float | None: ... class _AcceptLanguageInvalidOrNoHeader(AcceptLanguage): - @property - def parsed(self) -> None: ... def __bool__(self) -> Literal[False]: ... def __contains__(self, offer: str) -> Literal[True]: ... def __iter__(self) -> Iterator[str]: ... - @overload - def __add__(self, other: None) -> AcceptLanguageNoHeader: ... - @overload - def __add__(self, other: str) -> AcceptLanguageValidHeader | AcceptLanguageNoHeader: ... - @overload - def __add__(self, other: AcceptLanguageValidHeader) -> AcceptLanguageValidHeader: ... - @overload - def __add__(self, other: AcceptLanguageNoHeader | AcceptLanguageInvalidHeader) -> AcceptLanguageNoHeader: ... - @overload - def __add__(self, other: SupportsItems[str, float]) -> AcceptLanguageValidHeader | AcceptLanguageNoHeader: ... - @overload - def __add__( - self, other: _ListOrTuple[str | tuple[str, float] | list[Any]] - ) -> AcceptLanguageValidHeader | AcceptLanguageNoHeader: ... - @overload - def __radd__(self, other: None) -> AcceptLanguageNoHeader: ... - @overload - def __radd__(self, other: str) -> AcceptLanguageValidHeader | AcceptLanguageNoHeader: ... - @overload - def __radd__(self, other: AcceptLanguageValidHeader) -> AcceptLanguageValidHeader: ... - @overload - def __radd__(self, other: AcceptLanguageNoHeader | AcceptLanguageInvalidHeader) -> AcceptLanguageNoHeader: ... - @overload - def __radd__(self, other: SupportsItems[str, float]) -> AcceptLanguageValidHeader | AcceptLanguageNoHeader: ... - @overload - def __radd__( - self, other: _ListOrTuple[str | tuple[str, float] | list[Any]] - ) -> AcceptLanguageValidHeader | AcceptLanguageNoHeader: ... - def copy(self) -> Self: ... def basic_filtering(self, language_tags: Iterable[str]) -> list[tuple[str, float]]: ... @overload - def best_match(self, offers: Iterable[str], default_match: None = None) -> str | None: ... + def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: None = None) -> str | None: ... @overload - def best_match(self, offers: Iterable[str], default_match: str) -> str: ... + def best_match(self, offers: Iterable[str | tuple[str, float] | list[Any]], default_match: str) -> str: ... + @overload + def lookup(self, language_tags: object, default_range: object, default_tag: str, default: object = None) -> str: ... + @overload + def lookup( + self, language_tags: object = None, *, default_range: object = None, default_tag: str, default: object = None + ) -> str: ... + @overload + def lookup(self, language_tags: object, default_range: object, default_tag: None, default: _T | Callable[[], _T]) -> _T: ... @overload def lookup( self, - language_tags: Iterable[str], - default_range: str | None = None, - default_tag: str = ..., - default: str | Callable[[], str | None] | None = None, - ) -> str | None: ... - @overload - def lookup( - self, - language_tags: Iterable[str], - default_range: str | None = None, - default_tag: str | None = None, - default: str | Callable[[], str | None] = ..., - ) -> str | None: ... + language_tags: object = None, + *, + default_range: object = None, + default_tag: None = None, + default: _T | Callable[[], _T], + ) -> _T: ... def quality(self, offer: str) -> float | None: ... class AcceptLanguageNoHeader(_AcceptLanguageInvalidOrNoHeader): def __init__(self) -> None: ... + def copy(self) -> Self: ... @property def header_value(self) -> None: ... + @property + def parsed(self) -> None: ... + @overload + def __add__(self, other: AcceptLanguageValidHeader) -> AcceptLanguageValidHeader: ... + @overload + def __add__(self, other: AcceptLanguageInvalidHeader | AcceptLanguageNoHeader | Literal[""] | None) -> Self: ... + @overload + def __add__( + self, + other: ( + _AnyAcceptLanguageHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self | AcceptLanguageValidHeader: ... + @overload + def __radd__(self, other: AcceptLanguageValidHeader) -> AcceptLanguageValidHeader: ... + @overload + def __radd__(self, other: AcceptLanguageInvalidHeader | AcceptLanguageNoHeader | Literal[""] | None) -> Self: ... + @overload + def __radd__( + self, + other: ( + _AnyAcceptLanguageHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> Self | AcceptLanguageValidHeader: ... class AcceptLanguageInvalidHeader(_AcceptLanguageInvalidOrNoHeader): def __init__(self, header_value: str) -> None: ... + def copy(self) -> Self: ... @property def header_value(self) -> str: ... + @property + def parsed(self) -> None: ... + @overload + def __add__(self, other: AcceptLanguageValidHeader) -> AcceptLanguageValidHeader: ... + @overload + def __add__( + self, other: AcceptLanguageInvalidHeader | AcceptLanguageNoHeader | Literal[""] | None + ) -> AcceptLanguageNoHeader: ... + @overload + def __add__( + self, + other: ( + _AnyAcceptLanguageHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> AcceptLanguageValidHeader | AcceptLanguageNoHeader: ... + @overload + def __radd__(self, other: AcceptLanguageValidHeader) -> AcceptLanguageValidHeader: ... + @overload + def __radd__( + self, other: AcceptLanguageInvalidHeader | AcceptLanguageNoHeader | Literal[""] | None + ) -> AcceptLanguageNoHeader: ... + @overload + def __radd__( + self, + other: ( + _AnyAcceptLanguageHeader + | SupportsItems[str, float] + | _ListOrTuple[str | tuple[str, float] | list[Any]] + | _SupportsStr + | str + | None + ), + ) -> AcceptLanguageValidHeader | AcceptLanguageNoHeader: ... @overload -def create_accept_language_header(header_value: None) -> AcceptLanguageNoHeader: ... +def create_accept_language_header(header_value: AcceptLanguageValidHeader | Literal[""]) -> AcceptLanguageValidHeader: ... +@overload +def create_accept_language_header(header_value: AcceptLanguageNoHeader | None) -> AcceptLanguageNoHeader: ... +@overload +def create_accept_language_header(header_value: AcceptLanguageInvalidHeader) -> AcceptLanguageInvalidHeader: ... @overload def create_accept_language_header(header_value: str) -> AcceptLanguageValidHeader | AcceptLanguageInvalidHeader: ... - -class _AcceptLanguageProperty: - @overload - def __get__(self, obj: None, type: type | None = ..., /) -> property: ... - @overload - def __get__( - self, obj: Any, type: type | None = ..., / - ) -> AcceptLanguageNoHeader | AcceptLanguageValidHeader | AcceptLanguageInvalidHeader: ... - @overload - def __set__(self, obj: Any, value: str | None, /) -> None: ... - @overload - def __set__( - self, obj: Any, value: AcceptLanguageNoHeader | AcceptLanguageValidHeader | AcceptLanguageInvalidHeader, / - ) -> None: ... - @overload - def __set__(self, obj: Any, value: SupportsItems[str, float], /) -> None: ... - @overload - def __set__(self, obj: Any, value: _ListOrTuple[str | tuple[str, float] | list[Any]], /) -> None: ... - def __delete__(self, obj: Any, /) -> None: ... - +@overload +def create_accept_language_header(header_value: _AnyAcceptLanguageHeader | str | None) -> _AnyAcceptLanguageHeader: ... def accept_language_property() -> _AcceptLanguageProperty: ... diff --git a/stubs/WebOb/webob/byterange.pyi b/stubs/WebOb/webob/byterange.pyi index 461d10783..6d1d76e19 100644 --- a/stubs/WebOb/webob/byterange.pyi +++ b/stubs/WebOb/webob/byterange.pyi @@ -2,6 +2,8 @@ from collections.abc import Iterator from typing import overload from typing_extensions import Self +__all__ = ["Range", "ContentRange"] + class Range: start: int | None end: int | None diff --git a/stubs/WebOb/webob/cachecontrol.pyi b/stubs/WebOb/webob/cachecontrol.pyi index b3130de75..5b6df1e85 100644 --- a/stubs/WebOb/webob/cachecontrol.pyi +++ b/stubs/WebOb/webob/cachecontrol.pyi @@ -1,96 +1,96 @@ -from collections.abc import Callable, MutableMapping -from typing import Any, Generic, Literal, TypeVar, overload -from typing_extensions import Self, TypeAlias - -from webob.request import Request -from webob.response import Response +from _typeshed import SupportsItems +from collections.abc import Callable +from typing import Any, Generic, Literal, overload +from typing_extensions import Self, TypeVar _T = TypeVar("_T") -_KT = TypeVar("_KT") -_VT = TypeVar("_VT") -_NoneLiteral = TypeVar("_NoneLiteral") -_Type: TypeAlias = type +_DefaultT = TypeVar("_DefaultT", default=None) +_NoneLiteral = TypeVar("_NoneLiteral", default=None) +_ScopeT = TypeVar("_ScopeT", Literal["request"], Literal["response"], None, default=None) +_ScopeT2 = TypeVar("_ScopeT2", Literal["request"], Literal["response"], None) -class UpdateDict(dict[_KT, _VT]): +class UpdateDict(dict[str, Any]): updated: Callable[..., Any] | None updated_args: tuple[Any, ...] | None -class exists_property: - prop: str - type: str | None - def __init__(self, prop: str, type: str | None = None) -> None: ... +class exists_property(Generic[_ScopeT]): + def __init__(self, prop: str, type: _ScopeT = ...) -> None: ... @overload - def __get__(self, obj: None, type: _Type | None = None) -> Self: ... + def __get__(self, obj: None, type: type[CacheControl[Any]] | None = None) -> Self: ... @overload - def __get__(self, obj: Any, type: _Type | None = None) -> bool: ... - def __set__(self, obj: Any, value: bool | None) -> None: ... - def __delete__(self, obj: Any) -> None: ... + def __get__(self: exists_property[None], obj: CacheControl[Any], type: type[CacheControl[Any]] | None = None) -> bool: ... + @overload + def __get__(self, obj: CacheControl[_ScopeT], type: type[CacheControl[Any]] | None = None) -> bool: ... + @overload + def __set__(self: exists_property[None], obj: CacheControl[Any], value: bool | None) -> None: ... + @overload + def __set__(self, obj: CacheControl[_ScopeT], value: bool | None) -> None: ... + @overload + def __delete__(self, obj: CacheControl[Any]) -> None: ... + @overload + def __delete__(self, obj: CacheControl[_ScopeT]) -> None: ... -class value_property(Generic[_T, _NoneLiteral]): - prop: str - default: _T | None - none: _NoneLiteral - type: str | None +class value_property(Generic[_T, _DefaultT, _NoneLiteral, _ScopeT]): + def __init__(self, prop: str, default: _DefaultT = ..., none: _NoneLiteral = ..., type: _ScopeT = ...) -> None: ... @overload - def __init__(self, prop: str, default: None = None, none: None = None, type: str | None = None) -> None: ... + def __get__(self, obj: None, type: type[CacheControl[Any]] | None = None) -> Self: ... @overload - def __init__(self, prop: str, default: _T, none: _NoneLiteral, type: str | None = None) -> None: ... + def __get__( + self: value_property[_T, _DefaultT, _NoneLiteral, None], + obj: CacheControl[Any] | None, + type: type[CacheControl[Any]] | None = None, + ) -> _T | _DefaultT | _NoneLiteral: ... @overload - def __get__(self, obj: None, type: _Type | None = None) -> Self: ... + def __get__( + self, obj: CacheControl[_ScopeT] | None, type: type[CacheControl[Any]] | None = None + ) -> _T | _DefaultT | _NoneLiteral: ... @overload - def __get__(self, obj: Any, type: _Type | None = None) -> _T | _NoneLiteral | None: ... - def __set__(self, obj: Any, value: _T | Literal[True] | None) -> None: ... - def __delete__(self, obj: Any) -> None: ... + def __set__( + self: value_property[_T, _DefaultT, _NoneLiteral, None], + obj: CacheControl[Any], + value: _T | _DefaultT | Literal[True] | None, + ) -> None: ... + @overload + def __set__(self, obj: CacheControl[_ScopeT], value: _T | _DefaultT | Literal[True] | None) -> None: ... + @overload + def __delete__(self, obj: CacheControl[Any]) -> None: ... + @overload + def __delete__(self, obj: CacheControl[_ScopeT]) -> None: ... -class _IntValueProperty(value_property[int, _NoneLiteral]): - def __set__(self, obj: Any, value: int | None) -> None: ... - -class _BaseCacheControl: - update_dict = UpdateDict - properties: MutableMapping[str, Any] - type: Literal["request", "response"] | None +class CacheControl(Generic[_ScopeT]): + header_value: str + update_dict: type[UpdateDict] + properties: dict[str, Any] + type: _ScopeT + def __init__(self, properties: dict[str, Any], type: _ScopeT) -> None: ... + @overload @classmethod + def parse( + cls, header: str, updates_to: Callable[[dict[str, Any]], Any] | None = None, type: None = None + ) -> CacheControl[None]: ... @overload - def parse(cls, header: str, updates_to: None = None, type: None = None) -> _AnyCacheControl: ... @classmethod + def parse(cls, header: str, updates_to: Callable[[dict[str, Any]], Any] | None, type: _ScopeT2) -> CacheControl[_ScopeT2]: ... @overload - def parse(cls, header: str, updates_to: Request, type: Literal["request"]) -> _RequestCacheControl: ... @classmethod - @overload - def parse(cls, header: str, updates_to: Response, type: Literal["response"]) -> _ResponseCacheControl: ... - - no_cache: value_property[str, Literal["*"]] - no_store: exists_property - no_transform: exists_property - max_age: _IntValueProperty[Literal[-1]] + def parse( + cls, header: str, updates_to: Callable[[dict[str, Any]], Any] | None = None, *, type: _ScopeT2 + ) -> CacheControl[_ScopeT2]: ... + max_stale: value_property[int, None, Literal["*"], Literal["request"]] + min_fresh: value_property[int, None, None, Literal["request"]] + only_if_cached: exists_property[Literal["request"]] + public: exists_property[Literal["response"]] + private: value_property[str, None, Literal["*"], Literal["response"]] + no_cache: value_property[str, None, Literal["*"], None] + no_store: exists_property[None] + no_transform: exists_property[None] + must_revalidate: exists_property[Literal["response"]] + proxy_revalidate: exists_property[Literal["response"]] + max_age: value_property[int, None, Literal[-1], None] + s_maxage: value_property[int, None, None, Literal["response"]] + s_max_age = s_maxage + stale_while_revalidate: value_property[int, None, None, Literal["response"]] + stale_if_error: value_property[int, None, None, Literal["response"]] def copy(self) -> Self: ... -class _RequestCacheControl(_BaseCacheControl): - type: Literal["request"] - max_stale: _IntValueProperty[Literal["*"]] - min_fresh: _IntValueProperty[None] - only_if_cached: exists_property - -class _ResponseCacheControl(_BaseCacheControl): - type: Literal["response"] - public: exists_property - private: value_property[str, Literal["*"]] - must_revalidate: exists_property - proxy_revalidate: exists_property - s_maxage: _IntValueProperty[None] - s_max_age: _IntValueProperty[None] - stale_while_revalidate: _IntValueProperty[None] - stale_if_error: _IntValueProperty[None] - -class _AnyCacheControl(_RequestCacheControl, _ResponseCacheControl): - type: None # type: ignore[assignment] - -class CacheControl(_AnyCacheControl): - @overload - def __init__(self: _AnyCacheControl, properties: MutableMapping[str, Any], type: None) -> None: ... - @overload - def __init__(self: _RequestCacheControl, properties: MutableMapping[str, Any], type: Literal["request"]) -> None: ... - @overload - def __init__(self: _ResponseCacheControl, properties: MutableMapping[str, Any], type: Literal["response"]) -> None: ... - -def serialize_cache_control(properties: MutableMapping[str, Any] | _BaseCacheControl) -> str: ... +def serialize_cache_control(properties: SupportsItems[str, Any] | CacheControl[Any]) -> str: ... diff --git a/stubs/WebOb/webob/client.pyi b/stubs/WebOb/webob/client.pyi index 869bdec63..cdabe6fd5 100644 --- a/stubs/WebOb/webob/client.pyi +++ b/stubs/WebOb/webob/client.pyi @@ -3,6 +3,8 @@ from collections.abc import Iterable from http.client import HTTPConnection, HTTPMessage, HTTPSConnection from typing import ClassVar +__all__ = ["send_request_app", "SendRequest"] + class SendRequest: def __init__(self, HTTPConnection: type[HTTPConnection] = ..., HTTPSConnection: type[HTTPSConnection] = ...) -> None: ... def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: ... diff --git a/stubs/WebOb/webob/compat.pyi b/stubs/WebOb/webob/compat.pyi new file mode 100644 index 000000000..887dbe069 --- /dev/null +++ b/stubs/WebOb/webob/compat.pyi @@ -0,0 +1,22 @@ +import sys +from html import escape as escape +from queue import Empty as Empty, Queue as Queue +from typing import IO + +if sys.version_info >= (3, 13): + # NOTE: These are the only attributes we realistically care about + class cgi_FieldStorage: + filename: str + file: IO[bytes] + + def parse_header(line: str) -> tuple[str, dict[str, str]]: ... + +else: + from cgi import FieldStorage as _cgi_FieldStorage, parse_header as parse_header + + class cgi_FieldStorage(_cgi_FieldStorage): + # NOTE: The only kinds of objects of this type the user is exposed to + # will contain a file with a filename. We're technically lying + # if people create their own instances, but that shouldn't happen + filename: str + file: IO[bytes] diff --git a/stubs/WebOb/webob/cookies.pyi b/stubs/WebOb/webob/cookies.pyi index fa6d349e9..9cc7d7acf 100644 --- a/stubs/WebOb/webob/cookies.pyi +++ b/stubs/WebOb/webob/cookies.pyi @@ -1,15 +1,25 @@ from _typeshed import sentinel from _typeshed.wsgi import WSGIEnvironment -from collections.abc import Callable, Collection, ItemsView, Iterator, KeysView, MutableMapping, ValuesView +from collections.abc import Collection, ItemsView, Iterator, KeysView, MutableMapping, ValuesView from datetime import date, datetime, timedelta -from hashlib import _Hash +from time import _TimeTuple, struct_time from typing import Any, Literal, Protocol, TypeVar, overload from typing_extensions import TypeAlias -from webob.descriptors import _AsymmetricProperty -from webob.request import Request +from webob._types import AsymmetricProperty +from webob.request import BaseRequest from webob.response import Response +__all__ = [ + "Cookie", + "CookieProfile", + "SignedCookieProfile", + "SignedSerializer", + "JSONSerializer", + "Base64Serializer", + "make_cookie", +] + _T = TypeVar("_T") # we accept both the official spelling and the one used in the WebOb docs # the implementation compares after lower() so technically there are more @@ -27,7 +37,7 @@ class RequestCookies(MutableMapping[str, str]): @overload def get(self, name: str, default: None = None) -> str | None: ... @overload - def get(self, name: str, default: str | _T) -> str | _T: ... + def get(self, name: str, default: _T) -> str | _T: ... def __delitem__(self, name: str) -> None: ... def keys(self) -> KeysView[str]: ... def values(self) -> ValuesView[str]: ... @@ -37,11 +47,11 @@ class RequestCookies(MutableMapping[str, str]): def __len__(self) -> int: ... def clear(self) -> None: ... -class Cookie(dict[str, Morsel]): +class Cookie(dict[bytes, Morsel]): def __init__(self, input: str | None = None) -> None: ... def load(self, data: str) -> None: ... - def add(self, key: str | bytes, val: str | bytes) -> Morsel: ... - def __setitem__(self, key: str | bytes, val: str | bytes) -> Morsel: ... # type: ignore[override] + def add(self, key: str | bytes, val: str | bytes) -> Morsel | dict[bytes, bytes]: ... + def __setitem__(self, key: str | bytes, val: str | bytes) -> Morsel | dict[bytes, bytes]: ... # type: ignore[override] def serialize(self, full: bool = True) -> str: ... def values(self) -> list[Morsel]: ... # type: ignore[override] def __str__(self, full: bool = True) -> str: ... @@ -62,17 +72,12 @@ class Morsel(dict[bytes, bytes | bool | None]): def comment(self) -> bytes | None: ... @comment.setter def comment(self, v: bytes | None) -> None: ... - expires: _AsymmetricProperty[bytes | None, datetime | date | timedelta | int | str | bytes | None] - max_age: _AsymmetricProperty[bytes | None, timedelta | int | str | bytes] - @property - def httponly(self) -> bool | None: ... - @httponly.setter - def httponly(self, v: bool) -> None: ... - @property - def secure(self) -> bool | None: ... - @secure.setter - def secure(self, v: bool) -> None: ... - samesite: _AsymmetricProperty[bytes | None, _SameSitePolicy | None] + expires: AsymmetricProperty[bytes | None, datetime | date | timedelta | _TimeTuple | struct_time | int | str | bytes | None] + max_age: AsymmetricProperty[bytes | None, timedelta | int | str | bytes | None] + httponly: AsymmetricProperty[bool, bool | None] + secure: AsymmetricProperty[bool, bool | None] + samesite: AsymmetricProperty[bytes, _SameSitePolicy | bytes] + def __setitem__(self, k: str | bytes, v: bytes | bool | None) -> None: ... def serialize(self, full: bool = True) -> str: ... def __str__(self, full: bool = True) -> str: ... @@ -82,8 +87,8 @@ def make_cookie( max_age: int | timedelta | None = None, path: str = "/", domain: str | None = None, - secure: bool = False, - httponly: bool = False, + secure: bool | None = False, + httponly: bool | None = False, comment: str | None = None, samesite: _SameSitePolicy | None = None, ) -> str: ... @@ -99,14 +104,15 @@ class Base64Serializer: def loads(self, bstruct: bytes) -> Any: ... class SignedSerializer: - salt: str - secret: str + salt: str | bytes + secret: str | bytes hashalg: str salted_secret: bytes - digestmod: Callable[[bytes], _Hash] digest_size: int serializer: _Serializer - def __init__(self, secret: str, salt: str, hashalg: str = "sha512", serializer: _Serializer | None = None) -> None: ... + def __init__( + self, secret: str | bytes, salt: str | bytes, hashalg: str = "sha512", serializer: _Serializer | None = None + ) -> None: ... def dumps(self, appstruct: Any) -> bytes: ... def loads(self, bstruct: bytes) -> Any: ... @@ -119,7 +125,7 @@ class CookieProfile: path: str domains: Collection[str] | None serializer: _Serializer - request: Request | None + request: BaseRequest | None def __init__( self, cookie_name: str, @@ -133,8 +139,8 @@ class CookieProfile: domains: Collection[str] | None = None, serializer: _Serializer | None = None, ) -> None: ... - def __call__(self, request: Request) -> CookieProfile: ... - def bind(self, request: Request) -> CookieProfile: ... + def __call__(self, request: BaseRequest) -> CookieProfile: ... + def bind(self, request: BaseRequest) -> CookieProfile: ... def get_value(self) -> Any | None: ... def set_cookies( self, @@ -159,10 +165,9 @@ class CookieProfile: ) -> list[tuple[str, str]]: ... class SignedCookieProfile(CookieProfile): - secret: str - salt: str + secret: str | bytes + salt: str | bytes hashalg: str - serializer: SignedSerializer original_serializer: _Serializer def __init__( self, @@ -171,12 +176,12 @@ class SignedCookieProfile(CookieProfile): cookie_name: str, secure: bool = False, max_age: int | timedelta | None = None, - httponly: bool = False, + httponly: bool | None = False, samesite: _SameSitePolicy | None = None, path: str = "/", domains: Collection[str] | None = None, hashalg: str = "sha512", serializer: _Serializer | None = None, ) -> None: ... - def __call__(self, request: Request) -> SignedCookieProfile: ... - def bind(self, request: Request) -> SignedCookieProfile: ... + def __call__(self, request: BaseRequest) -> SignedCookieProfile: ... + def bind(self, request: BaseRequest) -> SignedCookieProfile: ... diff --git a/stubs/WebOb/webob/datetime_utils.pyi b/stubs/WebOb/webob/datetime_utils.pyi index a20e8a4d0..68c45d2f1 100644 --- a/stubs/WebOb/webob/datetime_utils.pyi +++ b/stubs/WebOb/webob/datetime_utils.pyi @@ -1,6 +1,22 @@ from datetime import date, datetime, timedelta, tzinfo from time import _TimeTuple, struct_time +__all__ = [ + "UTC", + "timedelta_to_seconds", + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "parse_date", + "serialize_date", + "parse_date_delta", + "serialize_date_delta", +] + class _UTC(tzinfo): def dst(self, dt: datetime | None) -> timedelta: ... def utcoffset(self, dt: datetime | None) -> timedelta: ... diff --git a/stubs/WebOb/webob/dec.pyi b/stubs/WebOb/webob/dec.pyi index 3f3d76fd0..41478ba2f 100644 --- a/stubs/WebOb/webob/dec.pyi +++ b/stubs/WebOb/webob/dec.pyi @@ -1,42 +1,42 @@ from _typeshed.wsgi import StartResponse, WSGIApplication, WSGIEnvironment from collections.abc import Callable, Iterable, Mapping -from typing import Any, Generic, TypeVar, overload -from typing_extensions import Concatenate, Never, ParamSpec, Self, TypeAlias +from typing import Any, Generic, overload +from typing_extensions import Concatenate, Never, ParamSpec, Self, TypeAlias, TypeVar -from webob.request import Request +from webob.request import BaseRequest, Request from webob.response import Response +__all__ = ["wsgify"] + _AnyResponse: TypeAlias = Response | WSGIApplication | str | None _S = TypeVar("_S") _AppT = TypeVar("_AppT", bound=WSGIApplication) _AppT_contra = TypeVar("_AppT_contra", bound=WSGIApplication, contravariant=True) -_RequestT = TypeVar("_RequestT", bound=Request) -_RequestT_contra = TypeVar("_RequestT_contra", bound=Request, contravariant=True) +_RequestT = TypeVar("_RequestT", bound=BaseRequest) +_RequestT_contra = TypeVar("_RequestT_contra", bound=BaseRequest, default=Request, contravariant=True) _P = ParamSpec("_P") _P2 = ParamSpec("_P2") -_RequestHandlerCallable: TypeAlias = Callable[Concatenate[_RequestT_contra, _P], _AnyResponse] -_RequestHandlerMethod: TypeAlias = Callable[Concatenate[Any, _RequestT_contra, _P], _AnyResponse] -_MiddlewareCallable: TypeAlias = Callable[Concatenate[_RequestT_contra, _AppT_contra, _P], _AnyResponse] -_MiddlewareMethod: TypeAlias = Callable[Concatenate[Any, _RequestT_contra, _AppT_contra, _P], _AnyResponse] -_RequestHandler: TypeAlias = _RequestHandlerCallable[_RequestT_contra, _P] | _RequestHandlerMethod[_RequestT_contra, _P] -_Middleware: TypeAlias = ( - _MiddlewareCallable[_RequestT_contra, _AppT_contra, _P] | _MiddlewareMethod[_RequestT_contra, _AppT_contra, _P] -) +_RequestHandlerCallable: TypeAlias = Callable[Concatenate[_RequestT, _P], _AnyResponse] +_RequestHandlerMethod: TypeAlias = Callable[Concatenate[Any, _RequestT, _P], _AnyResponse] +_MiddlewareCallable: TypeAlias = Callable[Concatenate[_RequestT, _AppT, _P], _AnyResponse] +_MiddlewareMethod: TypeAlias = Callable[Concatenate[Any, _RequestT, _AppT, _P], _AnyResponse] +_RequestHandler: TypeAlias = _RequestHandlerCallable[_RequestT, _P] | _RequestHandlerMethod[_RequestT, _P] +_Middleware: TypeAlias = _MiddlewareCallable[_RequestT, _AppT, _P] | _MiddlewareMethod[_RequestT, _AppT, _P] -class wsgify(Generic[_RequestT_contra, _P]): - RequestClass: type[Request] +class wsgify(Generic[_P, _RequestT_contra]): + RequestClass: type[_RequestT_contra] func: _RequestHandler[_RequestT_contra, _P] | None args: tuple[Any, ...] kwargs: dict[str, Any] middleware_wraps: WSGIApplication | None # NOTE: We disallow passing args/kwargs using this direct API, because # we can't really make it work as a decorator this way, these - # arguments should only really be used indrectly through the + # arguments should only really be used indirectly through the # middleware decorator, where we can be more type safe @overload def __init__( - self: wsgify[Request, []], + self: wsgify[[], Request], func: _RequestHandler[Request, []] | None = None, RequestClass: None = None, args: tuple[()] = (), @@ -45,7 +45,7 @@ class wsgify(Generic[_RequestT_contra, _P]): ) -> None: ... @overload def __init__( - self: wsgify[_RequestT_contra, []], # pyright: ignore[reportInvalidTypeVarUse] #11780 + self: wsgify[[], _RequestT_contra], # pyright: ignore[reportInvalidTypeVarUse] #11780 func: _RequestHandler[_RequestT_contra, []] | None, RequestClass: type[_RequestT_contra], args: tuple[()] = (), @@ -54,7 +54,7 @@ class wsgify(Generic[_RequestT_contra, _P]): ) -> None: ... @overload def __init__( - self: wsgify[_RequestT_contra, []], # pyright: ignore[reportInvalidTypeVarUse] #11780 + self: wsgify[[], _RequestT_contra], # pyright: ignore[reportInvalidTypeVarUse] #11780 func: _RequestHandler[_RequestT_contra, []] | None = None, *, RequestClass: type[_RequestT_contra], @@ -64,7 +64,7 @@ class wsgify(Generic[_RequestT_contra, _P]): ) -> None: ... @overload def __init__( - self: wsgify[Request, [_AppT_contra]], + self: wsgify[[_AppT_contra], Request], func: _Middleware[Request, _AppT_contra, []] | None = None, RequestClass: None = None, args: tuple[()] = (), @@ -74,7 +74,7 @@ class wsgify(Generic[_RequestT_contra, _P]): ) -> None: ... @overload def __init__( - self: wsgify[_RequestT_contra, [_AppT_contra]], # pyright: ignore[reportInvalidTypeVarUse] #11780 + self: wsgify[[_AppT_contra], _RequestT_contra], # pyright: ignore[reportInvalidTypeVarUse] #11780 func: _Middleware[_RequestT_contra, _AppT_contra, []] | None, RequestClass: type[_RequestT_contra], args: tuple[()] = (), @@ -84,7 +84,7 @@ class wsgify(Generic[_RequestT_contra, _P]): ) -> None: ... @overload def __init__( - self: wsgify[_RequestT_contra, [_AppT_contra]], # pyright: ignore[reportInvalidTypeVarUse] #11780 + self: wsgify[[_AppT_contra], _RequestT_contra], # pyright: ignore[reportInvalidTypeVarUse] #11780 func: _Middleware[_RequestT_contra, _AppT_contra, []] | None = None, *, RequestClass: type[_RequestT_contra], @@ -93,7 +93,7 @@ class wsgify(Generic[_RequestT_contra, _P]): middleware_wraps: _AppT_contra, ) -> None: ... @overload - def __get__(self, obj: None, type: type[_S]) -> _unbound_wsgify[_RequestT_contra, _P, _S]: ... + def __get__(self, obj: None, type: type[_S]) -> _unbound_wsgify[_P, _S, _RequestT_contra]: ... @overload def __get__(self, obj: object, type: type | None = None) -> Self: ... @overload @@ -118,34 +118,34 @@ class wsgify(Generic[_RequestT_contra, _P]): @classmethod def middleware( cls, middle_func: None = None, app: None | _AppT = None, *_: _P.args, **kw: _P.kwargs - ) -> _UnboundMiddleware[Any, _AppT, _P]: ... + ) -> _UnboundMiddleware[_P, _AppT, Any]: ... @overload @classmethod def middleware( cls, middle_func: _MiddlewareCallable[_RequestT, _AppT, _P2], app: None = None - ) -> _MiddlewareFactory[_RequestT, _AppT, _P2]: ... + ) -> _MiddlewareFactory[_P2, _AppT, _RequestT]: ... @overload @classmethod def middleware( cls, middle_func: _MiddlewareMethod[_RequestT, _AppT, _P2], app: None = None - ) -> _MiddlewareFactory[_RequestT, _AppT, _P2]: ... + ) -> _MiddlewareFactory[_P2, _AppT, _RequestT]: ... @overload @classmethod def middleware( cls, middle_func: _MiddlewareMethod[_RequestT, _AppT, _P2], app: None = None, *_: _P2.args, **kw: _P2.kwargs - ) -> _MiddlewareFactory[_RequestT, _AppT, _P2]: ... + ) -> _MiddlewareFactory[_P2, _AppT, _RequestT]: ... @overload @classmethod def middleware( cls, middle_func: _MiddlewareMethod[_RequestT, _AppT, _P2], app: _AppT - ) -> type[wsgify[_RequestT, Concatenate[_AppT, _P2]]]: ... + ) -> type[wsgify[Concatenate[_AppT, _P2], _RequestT]]: ... @overload @classmethod def middleware( cls, middle_func: _MiddlewareMethod[_RequestT, _AppT, _P2], app: _AppT, *_: _P2.args, **kw: _P2.kwargs - ) -> type[wsgify[_RequestT, Concatenate[_AppT, _P2]]]: ... + ) -> type[wsgify[Concatenate[_AppT, _P2], _RequestT]]: ... -class _unbound_wsgify(wsgify[_RequestT_contra, _P], Generic[_RequestT_contra, _P, _S]): +class _unbound_wsgify(wsgify[_P, _RequestT_contra], Generic[_P, _S, _RequestT_contra]): @overload # type: ignore[override] def __call__(self, __self: _S, env: WSGIEnvironment, /, start_response: StartResponse) -> Iterable[bytes]: ... @overload @@ -155,13 +155,13 @@ class _unbound_wsgify(wsgify[_RequestT_contra, _P], Generic[_RequestT_contra, _P @overload def __call__(self, __self: _S, /, req: _RequestT_contra, *args: _P.args, **kw: _P.kwargs) -> _AnyResponse: ... -class _UnboundMiddleware(Generic[_RequestT_contra, _AppT_contra, _P]): - wrapper_class: type[wsgify[_RequestT_contra, Concatenate[_AppT_contra, _P]]] +class _UnboundMiddleware(Generic[_P, _AppT_contra, _RequestT_contra]): + wrapper_class: type[wsgify[Concatenate[_AppT_contra, _P], _RequestT_contra]] app: _AppT_contra | None kw: dict[str, Any] def __init__( self, - wrapper_class: type[wsgify[_RequestT_contra, Concatenate[_AppT_contra, _P]]], + wrapper_class: type[wsgify[Concatenate[_AppT_contra, _P], _RequestT_contra]], app: _AppT_contra | None, kw: dict[str, Any], ) -> None: ... @@ -170,19 +170,19 @@ class _UnboundMiddleware(Generic[_RequestT_contra, _AppT_contra, _P]): @overload def __call__( self, func: _Middleware[_RequestT_contra, _AppT_contra, _P], app: None = None - ) -> wsgify[_RequestT_contra, Concatenate[_AppT_contra, _P]]: ... + ) -> wsgify[Concatenate[_AppT_contra, _P], _RequestT_contra]: ... @overload def __call__( self, func: _Middleware[_RequestT_contra, _AppT_contra, _P], app: _AppT_contra - ) -> wsgify[_RequestT_contra, Concatenate[_AppT_contra, _P]]: ... + ) -> wsgify[Concatenate[_AppT_contra, _P], _RequestT_contra]: ... -class _MiddlewareFactory(Generic[_RequestT_contra, _AppT_contra, _P]): - wrapper_class: type[wsgify[_RequestT_contra, Concatenate[_AppT_contra, _P]]] +class _MiddlewareFactory(Generic[_P, _AppT_contra, _RequestT_contra]): + wrapper_class: type[wsgify[Concatenate[_AppT_contra, _P], _RequestT_contra]] middleware: _Middleware[_RequestT_contra, _AppT_contra, _P] kw: dict[str, Any] def __init__( self, - wrapper_class: type[wsgify[_RequestT_contra, Concatenate[_AppT_contra, _P]]], + wrapper_class: type[wsgify[Concatenate[_AppT_contra, _P], _RequestT_contra]], middleware: _Middleware[_RequestT_contra, _AppT_contra, _P], kw: dict[str, Any], ) -> None: ... @@ -191,6 +191,6 @@ class _MiddlewareFactory(Generic[_RequestT_contra, _AppT_contra, _P]): @overload def __call__( self, app: None = None, *_: _P.args, **config: _P.kwargs - ) -> _MiddlewareFactory[_RequestT_contra, _AppT_contra, []]: ... + ) -> _MiddlewareFactory[[], _AppT_contra, _RequestT_contra]: ... @overload - def __call__(self, app: _AppT_contra, *_: _P.args, **config: _P.kwargs) -> wsgify[_RequestT_contra, [_AppT_contra]]: ... + def __call__(self, app: _AppT_contra, *_: _P.args, **config: _P.kwargs) -> wsgify[[_AppT_contra], _RequestT_contra]: ... diff --git a/stubs/WebOb/webob/descriptors.pyi b/stubs/WebOb/webob/descriptors.pyi index a80e04287..b9c3e5aff 100644 --- a/stubs/WebOb/webob/descriptors.pyi +++ b/stubs/WebOb/webob/descriptors.pyi @@ -1,18 +1,25 @@ from collections.abc import Callable, Iterable from datetime import date, datetime, timedelta from time import _TimeTuple, struct_time -from typing import Any, Generic, NamedTuple, TypeVar, overload +from typing import Any, NamedTuple, TypeVar, overload from typing_extensions import TypeAlias +from webob._types import AsymmetricProperty, AsymmetricPropertyWithDelete, SymmetricProperty, SymmetricPropertyWithDelete from webob.byterange import ContentRange, Range from webob.etag import IfRange, IfRangeDate -_T = TypeVar("_T") _DefaultT = TypeVar("_DefaultT") _GetterReturnType = TypeVar("_GetterReturnType") _SetterValueType = TypeVar("_SetterValueType") _ConvertedGetterReturnType = TypeVar("_ConvertedGetterReturnType") _ConvertedSetterValueType = TypeVar("_ConvertedSetterValueType") +_DescriptorT = TypeVar("_DescriptorT", bound=AsymmetricPropertyWithDelete[Any, Any]) + +_StringProperty: TypeAlias = SymmetricPropertyWithDelete[str | None] +_ListProperty: TypeAlias = AsymmetricPropertyWithDelete[tuple[str, ...] | None, Iterable[str] | str | None] +_DateProperty: TypeAlias = AsymmetricPropertyWithDelete[ + datetime | None, date | datetime | timedelta | _TimeTuple | struct_time | float | str | None +] _ContentRangeParams: TypeAlias = ( ContentRange | list[int] @@ -26,66 +33,48 @@ _ContentRangeParams: TypeAlias = ( | None ) -class _AsymmetricProperty(Generic[_GetterReturnType, _SetterValueType]): - @overload - def __get__(self, obj: None, type: type | None = ..., /) -> property: ... - @overload - def __get__(self, obj: Any, type: type | None = ..., /) -> _GetterReturnType: ... - def __set__(self, obj: Any, value: _SetterValueType, /) -> None: ... - -class _AsymmetricPropertyWithDelete(_AsymmetricProperty[_GetterReturnType, _SetterValueType]): - def __delete__(self, obj: Any, /) -> None: ... - -class _SymmetricProperty(_AsymmetricProperty[_T, _T]): ... -class _SymmetricPropertyWithDelete(_AsymmetricPropertyWithDelete[_T, _T]): ... -class _StringProperty(_SymmetricPropertyWithDelete[str | None]): ... -class _ListProperty(_AsymmetricPropertyWithDelete[tuple[str, ...] | None, Iterable[str] | str | None]): ... -class _DateProperty( - _AsymmetricPropertyWithDelete[datetime | None, date | datetime | timedelta | _TimeTuple | struct_time | float | str | None] -): ... - @overload -def environ_getter(key: str, default: None, rfc_section: str | None = None) -> _SymmetricPropertyWithDelete[Any | None]: ... +def environ_getter(key: str, *, rfc_section: str | None = None) -> SymmetricProperty[Any]: ... +@overload +def environ_getter(key: str, default: None, rfc_section: str | None = None) -> SymmetricPropertyWithDelete[Any | None]: ... @overload def environ_getter( key: str, default: _DefaultT, rfc_section: str | None = None -) -> _AsymmetricPropertyWithDelete[Any | _DefaultT, Any | _DefaultT | None]: ... +) -> AsymmetricPropertyWithDelete[Any | _DefaultT, Any | _DefaultT | None]: ... @overload -def environ_getter(key: str, *, rfc_section: str | None = None) -> _SymmetricProperty[Any]: ... +def environ_decoder(key: str, *, rfc_section: str | None = None, encattr: str | None = None) -> SymmetricProperty[str]: ... @overload def environ_decoder( key: str, default: str, rfc_section: str | None = None, encattr: str | None = None -) -> _AsymmetricPropertyWithDelete[str, str | None]: ... +) -> AsymmetricPropertyWithDelete[str, str | None]: ... @overload def environ_decoder( key: str, default: None, rfc_section: str | None = None, encattr: str | None = None -) -> _SymmetricPropertyWithDelete[str | None]: ... -@overload -def environ_decoder(key: str, *, rfc_section: str | None = None, encattr: str | None = None) -> _SymmetricProperty[str]: ... -def upath_property(key: str) -> _SymmetricProperty[str]: ... -def deprecated_property(attr: _T, name: str, text: str, version: str) -> _T: ... +) -> SymmetricPropertyWithDelete[str | None]: ... +def upath_property(key: str) -> SymmetricProperty[str]: ... +def deprecated_property(attr: _DescriptorT, name: str, text: str, version: str) -> _DescriptorT: ... def header_getter(header: str, rfc_section: str) -> _StringProperty: ... @overload def converter( - prop: _AsymmetricPropertyWithDelete[_GetterReturnType, _SetterValueType], + prop: AsymmetricPropertyWithDelete[_GetterReturnType, _SetterValueType], parse: Callable[[_GetterReturnType], _ConvertedGetterReturnType], serialize: Callable[[_ConvertedSetterValueType], _SetterValueType], convert_name: str | None = None, -) -> _AsymmetricPropertyWithDelete[_ConvertedGetterReturnType, _ConvertedSetterValueType]: ... +) -> AsymmetricPropertyWithDelete[_ConvertedGetterReturnType, _ConvertedSetterValueType | None]: ... @overload def converter( - prop: _AsymmetricProperty[_GetterReturnType, _SetterValueType], + prop: AsymmetricProperty[_GetterReturnType, _SetterValueType], parse: Callable[[_GetterReturnType], _ConvertedGetterReturnType], serialize: Callable[[_ConvertedSetterValueType], _SetterValueType], convert_name: str | None = None, -) -> _AsymmetricProperty[_ConvertedGetterReturnType, _ConvertedSetterValueType]: ... +) -> AsymmetricProperty[_ConvertedGetterReturnType, _ConvertedSetterValueType | None]: ... def list_header(header: str, rfc_section: str) -> _ListProperty: ... def parse_list(value: str | None) -> tuple[str, ...] | None: ... def serialize_list(value: Iterable[str] | str) -> str: ... def converter_date(prop: _StringProperty) -> _DateProperty: ... def date_header(header: str, rfc_section: str) -> _DateProperty: ... def parse_etag_response(value: str | None, strong: bool = False) -> str | None: ... -def serialize_etag_response(value: str | tuple[str, bool]) -> str: ... +def serialize_etag_response(value: tuple[str, bool] | str) -> str: ... def serialize_if_range(value: IfRange | IfRangeDate | datetime | date | str) -> str | None: ... def parse_range(value: str | None) -> Range | None: ... def serialize_range(value: tuple[int, int | None] | list[int | None] | list[int] | str | None) -> str | None: ... diff --git a/stubs/WebOb/webob/etag.pyi b/stubs/WebOb/webob/etag.pyi index 08a81da59..5f71a2ac1 100644 --- a/stubs/WebOb/webob/etag.pyi +++ b/stubs/WebOb/webob/etag.pyi @@ -1,41 +1,34 @@ from collections.abc import Collection from datetime import datetime -from typing import Any, overload +from typing import Literal from typing_extensions import TypeAlias +from webob._types import AsymmetricPropertyWithDelete from webob.response import Response -_ETag: TypeAlias = _AnyETag | _NoETag | ETagMatcher +__all__ = ["AnyETag", "NoETag", "ETagMatcher", "IfRange", "etag_property"] -class _ETagProperty: - @overload - def __get__(self, obj: None, type: type | None = ..., /) -> property: ... - @overload - def __get__(self, obj: Any, type: type | None = ..., /) -> _ETag: ... - @overload - def __set__(self, obj: Any, value: str | None, /) -> None: ... - @overload - def __set__(self, obj: Any, value: _ETag, /) -> None: ... - def __delete__(self, obj: Any, /) -> None: ... +_ETag: TypeAlias = _AnyETag | _NoETag | ETagMatcher +_ETagProperty: TypeAlias = AsymmetricPropertyWithDelete[_ETag, _ETag | str | None] def etag_property(key: str, default: _ETag, rfc_section: str, strong: bool = True) -> _ETagProperty: ... class _AnyETag: - def __bool__(self) -> bool: ... - def __contains__(self, other: str) -> bool: ... + def __bool__(self) -> Literal[False]: ... + def __contains__(self, other: str | None) -> Literal[True]: ... AnyETag: _AnyETag class _NoETag: - def __bool__(self) -> bool: ... - def __contains__(self, other: str) -> bool: ... + def __bool__(self) -> Literal[False]: ... + def __contains__(self, other: str | None) -> Literal[False]: ... NoETag: _NoETag class ETagMatcher: etags: Collection[str] def __init__(self, etags: Collection[str]) -> None: ... - def __contains__(self, other: str) -> bool: ... + def __contains__(self, other: str | None) -> bool: ... @classmethod def parse(cls, value: str, strong: bool = True) -> ETagMatcher | _AnyETag: ... @@ -43,7 +36,7 @@ class IfRange: etag: _ETag def __init__(self, etag: _ETag) -> None: ... @classmethod - def parse(cls, value: str) -> IfRange | IfRangeDate: ... + def parse(cls, value: str | None) -> IfRange | IfRangeDate: ... def __contains__(self, resp: Response) -> bool: ... def __bool__(self) -> bool: ... diff --git a/stubs/WebOb/webob/exc.pyi b/stubs/WebOb/webob/exc.pyi index 5316b33cb..da9ee25cf 100644 --- a/stubs/WebOb/webob/exc.pyi +++ b/stubs/WebOb/webob/exc.pyi @@ -3,12 +3,73 @@ from _typeshed.wsgi import StartResponse, WSGIApplication, WSGIEnvironment from collections.abc import Iterable from string import Template from typing import Any, Literal, Protocol -from typing_extensions import Self +from typing_extensions import Self, TypeAlias from webob.response import Response +__all__ = [ + "HTTPAccepted", + "HTTPBadGateway", + "HTTPBadRequest", + "HTTPClientError", + "HTTPConflict", + "HTTPCreated", + "HTTPError", + "HTTPExpectationFailed", + "HTTPFailedDependency", + "HTTPForbidden", + "HTTPFound", + "HTTPGatewayTimeout", + "HTTPGone", + "HTTPInsufficientStorage", + "HTTPInternalServerError", + "HTTPLengthRequired", + "HTTPLocked", + "HTTPMethodNotAllowed", + "HTTPMovedPermanently", + "HTTPMultipleChoices", + "HTTPNetworkAuthenticationRequired", + "HTTPNoContent", + "HTTPNonAuthoritativeInformation", + "HTTPNotAcceptable", + "HTTPNotFound", + "HTTPNotImplemented", + "HTTPNotModified", + "HTTPOk", + "HTTPPartialContent", + "HTTPPaymentRequired", + "HTTPPermanentRedirect", + "HTTPPreconditionFailed", + "HTTPPreconditionRequired", + "HTTPProxyAuthenticationRequired", + "HTTPRedirection", + "HTTPRequestEntityTooLarge", + "HTTPRequestHeaderFieldsTooLarge", + "HTTPRequestRangeNotSatisfiable", + "HTTPRequestTimeout", + "HTTPRequestURITooLong", + "HTTPResetContent", + "HTTPSeeOther", + "HTTPServerError", + "HTTPServiceUnavailable", + "HTTPTemporaryRedirect", + "HTTPTooManyRequests", + "HTTPUnauthorized", + "HTTPUnavailableForLegalReasons", + "HTTPUnprocessableEntity", + "HTTPUnsupportedMediaType", + "HTTPUseProxy", + "HTTPVersionNotSupported", + "WSGIHTTPException", + "HTTPException", + "HTTPExceptionMiddleware", + "status_map", +] + +_Headers: TypeAlias = SupportsItems[str, str] | SupportsKeysAndGetItem[str, str] | Iterable[tuple[str, str]] + class _JSONFormatter(Protocol): - def __call__(self, *, body: str, status: str, title: str, environ: WSGIEnvironment) -> str: ... + def __call__(self, *, body: str, status: str, title: str, environ: WSGIEnvironment) -> Any: ... class HTTPException(Exception): wsgi_response: Response @@ -28,7 +89,7 @@ class WSGIHTTPException(Response, HTTPException): def __init__( self, detail: str | None = None, - headers: SupportsItems[str, str] | SupportsKeysAndGetItem[str, str] | Iterable[tuple[str, str]] | None = None, + headers: _Headers | None = None, comment: str | None = None, body_template: str | None = None, json_formatter: _JSONFormatter | None = None, @@ -36,9 +97,10 @@ class WSGIHTTPException(Response, HTTPException): ) -> None: ... def plain_body(self, environ: WSGIEnvironment) -> str: ... def html_body(self, environ: WSGIEnvironment) -> str: ... - def json_formatter(self, body: str, status: str, title: str, environ: WSGIEnvironment) -> str: ... - def json_body(self, environ: WSGIEnvironment) -> str: ... + def json_formatter(self, body: str, status: str, title: str, environ: WSGIEnvironment) -> Any: ... + def json_body(self, environ: WSGIEnvironment) -> str: ... # type: ignore[override] def generate_response(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: ... + def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: ... @property def wsgi_response(self) -> Self: ... # type: ignore[override] def __str__(self) -> str: ... # type: ignore[override] # noqa: Y029 @@ -64,12 +126,13 @@ class _HTTPMove(HTTPRedirection): def __init__( self, detail: str | None = None, - headers: str | None = None, + headers: _Headers | None = None, comment: str | None = None, body_template: str | None = None, location: str | None = None, add_slash: bool = False, ) -> None: ... + def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: ... class HTTPMultipleChoices(_HTTPMove): ... class HTTPMovedPermanently(_HTTPMove): ... @@ -123,4 +186,4 @@ class HTTPExceptionMiddleware: def __init__(self, application: WSGIApplication) -> None: ... def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: ... -status_map: dict[int, type[WSGIHTTPException]] +status_map: dict[int, type[HTTPOk | HTTPRedirection | HTTPClientError | HTTPServerError]] diff --git a/stubs/WebOb/webob/headers.pyi b/stubs/WebOb/webob/headers.pyi index abd938520..8e14bf642 100644 --- a/stubs/WebOb/webob/headers.pyi +++ b/stubs/WebOb/webob/headers.pyi @@ -1,9 +1,27 @@ from _typeshed.wsgi import WSGIEnvironment from collections.abc import Iterator, MutableMapping +from typing import TypeVar, overload from webob.multidict import MultiDict -class ResponseHeaders(MultiDict[str, str]): ... +__all__ = ["ResponseHeaders", "EnvironHeaders"] + +_T = TypeVar("_T") + +class ResponseHeaders(MultiDict[str, str]): + def __getitem__(self, key: str) -> str: ... + def getall(self, key: str) -> list[str]: ... + def mixed(self) -> dict[str, str | list[str]]: ... + def dict_of_lists(self) -> dict[str, list[str]]: ... + def __setitem__(self, key: str, value: str) -> None: ... + def __delitem__(self, key: str) -> None: ... + def __contains__(self, key: object) -> bool: ... + has_key = __contains__ + def setdefault(self, key: str, default: str) -> str: ... # type: ignore[override] + @overload + def pop(self, key: str) -> str: ... + @overload + def pop(self, key: str, default: _T) -> str | _T: ... class EnvironHeaders(MutableMapping[str, str]): environ: WSGIEnvironment @@ -11,7 +29,7 @@ class EnvironHeaders(MutableMapping[str, str]): def __getitem__(self, hname: str) -> str: ... def __setitem__(self, hname: str, value: str) -> None: ... def __delitem__(self, hname: str) -> None: ... - def keys(self) -> list[str]: ... # type: ignore[override] + def keys(self) -> Iterator[str]: ... # type: ignore[override] def __contains__(self, hname: object) -> bool: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[str]: ... diff --git a/stubs/WebOb/webob/multidict.pyi b/stubs/WebOb/webob/multidict.pyi index b1aa4a510..1b5f3a046 100644 --- a/stubs/WebOb/webob/multidict.pyi +++ b/stubs/WebOb/webob/multidict.pyi @@ -1,30 +1,49 @@ -import sys -from _typeshed import SupportsItems, SupportsKeysAndGetItem +from _typeshed import SupportsKeysAndGetItem from _typeshed.wsgi import WSGIEnvironment from collections.abc import Collection, Iterable, Iterator, MutableMapping -from typing import Any, Literal, TypeVar, overload -from typing_extensions import Self, TypeAlias +from typing import Literal, Protocol, TypeVar, overload +from typing_extensions import Self -if sys.version_info >= (3, 13): - _FieldStorage: TypeAlias = Any -else: - from cgi import FieldStorage as _FieldStorage +from webob.compat import cgi_FieldStorage, cgi_FieldStorage as _FieldStorageWithFile + +__all__ = ["MultiDict", "NestedMultiDict", "NoVars", "GetDict"] _T = TypeVar("_T") _KT = TypeVar("_KT") _VT = TypeVar("_VT") +_KT_co = TypeVar("_KT_co", covariant=True) +_VT_co = TypeVar("_VT_co", covariant=True) + +class _SupportsItemsWithIterableResult(Protocol[_KT_co, _VT_co]): + def items(self) -> Iterable[tuple[_KT_co, _VT_co]]: ... class MultiDict(MutableMapping[_KT, _VT]): @overload - def __init__(self, m: SupportsItems[_KT, _VT], /, **kwargs: _VT) -> None: ... + def __init__(self) -> None: ... @overload - def __init__(self, m: Iterable[tuple[_KT, _VT]], /, **kwargs: _VT) -> None: ... + def __init__(self: MultiDict[str, _VT], **kwargs: _VT) -> None: ... # pyright: ignore[reportInvalidTypeVarUse] #11780 @overload - def __init__(self, **kwargs: _VT) -> None: ... + def __init__(self, m: _SupportsItemsWithIterableResult[_KT, _VT], /) -> None: ... + @overload + def __init__( + self: MultiDict[str, _VT], # pyright: ignore[reportInvalidTypeVarUse] #11780 + m: _SupportsItemsWithIterableResult[str, _VT], + /, + **kwargs: _VT, + ) -> None: ... + @overload + def __init__(self, m: Iterable[tuple[_KT, _VT]], /) -> None: ... + @overload + def __init__( + self: MultiDict[str, _VT], # pyright: ignore[reportInvalidTypeVarUse] #11780 + m: Iterable[tuple[str, _VT]], + /, + **kwargs: _VT, + ) -> None: ... @classmethod - def view_list(cls, lst: list[tuple[_KT, _VT]]) -> MultiDict[_KT, _VT]: ... + def view_list(cls, lst: list[tuple[_KT, _VT]]) -> Self: ... @classmethod - def from_fieldstorage(cls, fs: _FieldStorage) -> MultiDict[str, str | _FieldStorage]: ... + def from_fieldstorage(cls, fs: cgi_FieldStorage) -> MultiDict[str, str | _FieldStorageWithFile]: ... def __getitem__(self, key: _KT) -> _VT: ... def __setitem__(self, key: _KT, value: _VT) -> None: ... def add(self, key: _KT, value: _VT) -> None: ... @@ -51,43 +70,90 @@ class MultiDict(MutableMapping[_KT, _VT]): def pop(self, key: _KT, default: _T) -> _VT | _T: ... def popitem(self) -> tuple[_KT, _VT]: ... @overload # type: ignore[override] - def update(self, m: Collection[tuple[_KT, _VT]], /, **kwargs: _VT) -> None: ... + def update(self: MultiDict[str, _VT], **kwargs: _VT) -> None: ... @overload - def update(self, **kwargs: _VT) -> None: ... + def update(self, m: Collection[tuple[_KT, _VT]], /) -> None: ... @overload - def extend(self, other: SupportsItems[_KT, _VT], **kwargs: _VT) -> None: ... + def update(self: MultiDict[str, _VT], m: Collection[tuple[str, _VT]], /, **kwargs: _VT) -> None: ... @overload - def extend(self, other: SupportsKeysAndGetItem[_KT, _VT], **kwargs: _VT) -> None: ... + def extend(self, other: _SupportsItemsWithIterableResult[_KT, _VT]) -> None: ... @overload - def extend(self, other: Iterable[tuple[_KT, _VT]], **kwargs: _VT) -> None: ... + def extend(self: MultiDict[str, _VT], other: _SupportsItemsWithIterableResult[str, _VT], **kwargs: _VT) -> None: ... @overload - def extend(self, other: None = None, **kwargs: _VT) -> None: ... + def extend(self, other: Iterable[tuple[_KT, _VT]]) -> None: ... + @overload + def extend(self: MultiDict[str, _VT], other: Iterable[tuple[str, _VT]], **kwargs: _VT) -> None: ... + @overload + def extend(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> None: ... + @overload + def extend(self: MultiDict[str, _VT], other: SupportsKeysAndGetItem[str, _VT], **kwargs: _VT) -> None: ... + @overload + def extend(self: MultiDict[str, _VT], other: None = None, **kwargs: _VT) -> None: ... def __len__(self) -> int: ... def keys(self) -> Iterator[_KT]: ... # type: ignore[override] __iter__ = keys - def values(self) -> Iterator[_VT]: ... # type: ignore[override] def items(self) -> Iterator[tuple[_KT, _VT]]: ... # type: ignore[override] + def values(self) -> Iterator[_VT]: ... # type: ignore[override] class GetDict(MultiDict[str, str]): env: WSGIEnvironment @overload - def __init__(self, data: SupportsItems[str, str], env: WSGIEnvironment) -> None: ... + def __init__(self, data: _SupportsItemsWithIterableResult[str, str], env: WSGIEnvironment) -> None: ... @overload def __init__(self, data: Iterable[tuple[str, str]], env: WSGIEnvironment) -> None: ... def on_change(self) -> None: ... + def __setitem__(self, key: str, value: str) -> None: ... + def add(self, key: str, value: str) -> None: ... + def __delitem__(self, key: str) -> None: ... + def clear(self) -> None: ... + def setdefault(self, key: str, default: str) -> str: ... # type: ignore[override] + @overload + def pop(self, key: str) -> str: ... + @overload + def pop(self, key: str, default: _T) -> str | _T: ... + def popitem(self) -> tuple[str, str]: ... + @overload # type: ignore[override] + def update(self, **kwargs: str) -> None: ... + @overload + def update(self, m: Collection[tuple[str, str]], /, **kwargs: str) -> None: ... + @overload + def extend(self, other: _SupportsItemsWithIterableResult[str, str], **kwargs: str) -> None: ... + @overload + def extend(self, other: Iterable[tuple[str, str]], **kwargs: str) -> None: ... + @overload + def extend(self, other: SupportsKeysAndGetItem[str, str], **kwargs: str) -> None: ... + @overload + def extend(self, other: None = None, **kwargs: str) -> None: ... + def copy(self) -> MultiDict[str, str]: ... # type: ignore[override] class NestedMultiDict(MultiDict[_KT, _VT]): - dicts: tuple[MultiDict[_KT, _VT] | NoVars, ...] - def __init__(self, *dicts: MultiDict[_KT, _VT] | NoVars) -> None: ... - def __setitem__(self, key: _KT, value: _VT) -> None: ... - def add(self, key: _KT, value: _VT) -> None: ... - def __delitem__(self, key: _KT) -> None: ... - def clear(self) -> None: ... - def setdefault(self, key: _KT, default: _VT | None = ...) -> Any: ... - def pop(self, key: _KT, default: Any = ...) -> Any: ... - def popitem(self) -> tuple[_KT, _VT]: ... - def update(self, *args: Any, **kwargs: _VT) -> None: ... + # FIXME: It would be more accurate to use a Protocol here, which has a + # covariant _VT, instead of MultiDict + dicts: tuple[MultiDict[_KT, _VT], ...] + def __init__(self, *dicts: MultiDict[_KT, _VT]) -> None: ... + def __getitem__(self, key: _KT) -> _VT: ... + # NOTE: These methods all return exceptions, so this will give us + # somewhat sane type checker errors, we would prefer to use + # something like @type_error here, if it existed. + # This is only really necessary, because the inheritance hierachy + # is a mess. + __setitem__: None # type: ignore[assignment] + add: None # type: ignore[assignment] + __delitem__: None # type: ignore[assignment] + clear: None # type: ignore[assignment] + setdefault: None # type: ignore[assignment] + pop: None # type: ignore[assignment] + popitem: None # type: ignore[assignment] + update: None # type: ignore[assignment] + def getall(self, key: _KT) -> list[_VT]: ... def copy(self) -> MultiDict[_KT, _VT]: ... # type: ignore[override] + def __contains__(self, key: object) -> bool: ... + has_key = __contains__ + def __len__(self) -> int: ... + def items(self) -> Iterator[tuple[_KT, _VT]]: ... # type: ignore[override] + def values(self) -> Iterator[_VT]: ... # type: ignore[override] + def keys(self) -> Iterator[_KT]: ... # type: ignore[override] + __iter__ = keys class NoVars: reason: str diff --git a/stubs/WebOb/webob/request.pyi b/stubs/WebOb/webob/request.pyi index 4f9af63f9..c562838f7 100644 --- a/stubs/WebOb/webob/request.pyi +++ b/stubs/WebOb/webob/request.pyi @@ -1,34 +1,33 @@ import datetime import io -import sys -from _typeshed import ExcInfo, ReadableBuffer, SupportsItems, SupportsKeysAndGetItem, SupportsNoArgReadline, SupportsRead +from _typeshed import OptExcInfo, SupportsKeysAndGetItem, SupportsNoArgReadline, SupportsRead, WriteableBuffer from _typeshed.wsgi import WSGIApplication, WSGIEnvironment from collections.abc import Iterable, Mapping from re import Pattern -from tempfile import _TemporaryFileWrapper from typing import IO, Any, ClassVar, Literal, Protocol, TypedDict, TypeVar, overload from typing_extensions import Self, TypeAlias +from webob._types import AsymmetricProperty, AsymmetricPropertyWithDelete, SymmetricProperty, SymmetricPropertyWithDelete from webob.acceptparse import _AcceptCharsetProperty, _AcceptEncodingProperty, _AcceptLanguageProperty, _AcceptProperty from webob.byterange import Range -from webob.cachecontrol import _RequestCacheControl +from webob.cachecontrol import CacheControl +from webob.client import SendRequest +from webob.compat import cgi_FieldStorage from webob.cookies import RequestCookies -from webob.descriptors import _AsymmetricProperty, _AsymmetricPropertyWithDelete, _authorization, _DateProperty +from webob.descriptors import _authorization, _DateProperty from webob.etag import IfRange, IfRangeDate, _ETagProperty from webob.headers import EnvironHeaders from webob.multidict import GetDict, MultiDict, NestedMultiDict, NoVars -from webob.response import Response, _HTTPHeader +from webob.response import Response -if sys.version_info >= (3, 13): - _FieldStorage: TypeAlias = Any -else: - from cgi import FieldStorage as _FieldStorage +__all__ = ["BaseRequest", "Request", "LegacyRequest"] _T = TypeVar("_T") -_HTTPMethod: TypeAlias = Literal["GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"] +_HTTPMethod: TypeAlias = Literal["GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"] _ListOrTuple: TypeAlias = list[_T] | tuple[_T, ...] +_RequestCacheControl: TypeAlias = CacheControl[Literal["request"]] -class _SupportsReadAndNoArgReadline(SupportsRead[bytes], SupportsNoArgReadline[bytes], Protocol): ... +class _SupportsReadAndNoArgReadline(SupportsRead[str | bytes], SupportsNoArgReadline[str | bytes], Protocol): ... class _RequestCacheControlDict(TypedDict, total=False): max_stale: int @@ -39,11 +38,7 @@ class _RequestCacheControlDict(TypedDict, total=False): no_transform: bool max_age: int -# On py313 this subclasses `Any`, hence the type: ignore. -# This is needed for the regr_test.py script, which uses --disallow-subclassing-any -class _FieldStorageWithFile(_FieldStorage): # type: ignore[misc] - file: IO[bytes] - filename: str +_FieldStorageWithFile = cgi_FieldStorage class _NoDefault: ... @@ -52,7 +47,6 @@ NoDefault: _NoDefault class BaseRequest: request_body_tempfile_limit: ClassVar[int] environ: WSGIEnvironment - method: _HTTPMethod def __init__(self, environ: WSGIEnvironment, **kw: Any) -> None: ... @overload def encget(self, key: str, default: _T, encattr: str | None = None) -> str | _T: ... @@ -66,44 +60,29 @@ class BaseRequest: def body_file(self) -> SupportsRead[bytes]: ... @body_file.setter def body_file(self, value: SupportsRead[bytes]) -> None: ... - content_length: int | None - body_file_raw: SupportsRead[bytes] + @body_file.deleter + def body_file(self) -> None: ... + content_length: SymmetricPropertyWithDelete[int | None] + body_file_raw: SymmetricProperty[SupportsRead[bytes]] is_body_seekable: bool @property def body_file_seekable(self) -> IO[bytes]: ... - url_encoding: str - @property - def scheme(self) -> str | None: ... - @scheme.setter - def scheme(self, value: str | None) -> None: ... - @property - def http_version(self) -> str | None: ... - @http_version.setter - def http_version(self, value: str | None) -> None: ... - remote_user: str | None - remote_host: str | None - remote_addr: str | None - query_string: str - @property - def server_name(self) -> str | None: ... - @server_name.setter - def server_name(self, value: str | None) -> None: ... - @property - def server_port(self) -> int | None: ... - @server_port.setter - def server_port(self, value: int | None) -> None: ... - script_name: str - @property - def path_info(self) -> str | None: ... - @path_info.setter - def path_info(self, value: str | None) -> None: ... - uscript_name: str # bw compat - @property - def upath_info(self) -> str | None: ... # bw compat - @upath_info.setter - def upath_info(self, value: str | None) -> None: ... # bw compat - content_type: str | None - headers: _AsymmetricProperty[EnvironHeaders, SupportsItems[str, str] | Iterable[tuple[str, str]]] + url_encoding: AsymmetricPropertyWithDelete[str, str | None] + scheme: SymmetricProperty[str] + method: AsymmetricPropertyWithDelete[_HTTPMethod, _HTTPMethod | None] + http_version: SymmetricProperty[str] + remote_user: SymmetricPropertyWithDelete[str | None] + remote_host: SymmetricPropertyWithDelete[str | None] + remote_addr: SymmetricPropertyWithDelete[str | None] + query_string: AsymmetricPropertyWithDelete[str, str | None] + server_name: SymmetricProperty[str] + server_port: SymmetricProperty[int] + script_name: AsymmetricPropertyWithDelete[str, str | None] + path_info: SymmetricProperty[str] + uscript_name = script_name # bw compat + upath_info = path_info # bw compat + content_type: AsymmetricPropertyWithDelete[str, str | None] + headers: AsymmetricProperty[EnvironHeaders, SupportsKeysAndGetItem[str, str] | Iterable[tuple[str, str]]] @property def client_addr(self) -> str | None: ... @property @@ -123,24 +102,29 @@ class BaseRequest: def relative_url(self, other_url: str, to_application: bool = False) -> str: ... def path_info_pop(self, pattern: Pattern[str] | None = None) -> str | None: ... def path_info_peek(self) -> str | None: ... - urlvars: dict[str, str] - urlargs: tuple[str] + urlvars: SymmetricPropertyWithDelete[dict[str, str]] + urlargs: SymmetricPropertyWithDelete[tuple[str, ...]] @property def is_xhr(self) -> bool: ... - host: str + host: SymmetricPropertyWithDelete[str] @property def domain(self) -> str: ... - body: bytes - json: Any - json_body: Any - text: str + @property + def body(self) -> bytes: ... + @body.setter + def body(self, value: bytes | None) -> None: ... + @body.deleter + def body(self) -> None: ... + json: SymmetricPropertyWithDelete[Any] + json_body: SymmetricPropertyWithDelete[Any] + text: SymmetricPropertyWithDelete[str] @property def POST(self) -> MultiDict[str, str | _FieldStorageWithFile] | NoVars: ... @property def GET(self) -> GetDict: ... @property def params(self) -> NestedMultiDict[str, str | _FieldStorageWithFile]: ... - cookies: _AsymmetricProperty[RequestCookies, SupportsKeysAndGetItem[str, str] | Iterable[tuple[str, str]]] + cookies: AsymmetricProperty[RequestCookies, SupportsKeysAndGetItem[str, str] | Iterable[tuple[str, str]]] def copy(self) -> Self: ... def copy_get(self) -> Self: ... @property @@ -149,7 +133,7 @@ class BaseRequest: def is_body_readable(self, flag: bool) -> None: ... def make_body_seekable(self) -> None: ... def copy_body(self) -> None: ... - def make_tempfile(self) -> _TemporaryFileWrapper[bytes]: ... + def make_tempfile(self) -> io.BufferedRandom: ... def remove_conditional_headers( self, remove_encoding: bool = True, remove_range: bool = True, remove_match: bool = True, remove_modified: bool = True ) -> None: ... @@ -157,24 +141,24 @@ class BaseRequest: accept_charset: _AcceptCharsetProperty accept_encoding: _AcceptEncodingProperty accept_language: _AcceptLanguageProperty - authorization: _AsymmetricPropertyWithDelete[_authorization | None, tuple[str, str | dict[str, str]] | list[Any] | str | None] - cache_control: _AsymmetricPropertyWithDelete[ - _RequestCacheControl | None, _RequestCacheControl | _RequestCacheControlDict | str | None + authorization: AsymmetricPropertyWithDelete[_authorization | None, tuple[str, str | dict[str, str]] | list[Any] | str | None] + cache_control: AsymmetricPropertyWithDelete[ + _RequestCacheControl, _RequestCacheControl | _RequestCacheControlDict | str | None ] if_match: _ETagProperty if_none_match: _ETagProperty date: _DateProperty if_modified_since: _DateProperty if_unmodified_since: _DateProperty - if_range: _AsymmetricPropertyWithDelete[ - IfRange | IfRangeDate | None, IfRange | IfRangeDate | datetime.datetime | datetime.date | str | None + if_range: AsymmetricPropertyWithDelete[ + IfRange | IfRangeDate, IfRange | IfRangeDate | datetime.datetime | datetime.date | str | None ] - max_forwards: int | None - pragma: str | None - range: _AsymmetricPropertyWithDelete[Range | None, tuple[int, int | None] | list[int | None] | list[int] | str | None] - referer: str | None - referrer: str | None - user_agent: str | None + max_forwards: SymmetricPropertyWithDelete[int | None] + pragma: SymmetricPropertyWithDelete[str | None] + range: AsymmetricPropertyWithDelete[Range | None, tuple[int, int | None] | list[int | None] | list[int] | str | None] + referer: SymmetricPropertyWithDelete[str | None] + referrer = referer + user_agent: SymmetricPropertyWithDelete[str | None] def as_bytes(self, skip_body: bool = False) -> bytes: ... def as_text(self) -> str: ... @classmethod @@ -186,15 +170,21 @@ class BaseRequest: @overload def call_application( self, application: WSGIApplication, catch_exc_info: Literal[False] = False - ) -> tuple[str, list[_HTTPHeader], Iterable[bytes]]: ... + ) -> tuple[str, list[tuple[str, str]], Iterable[bytes]]: ... @overload def call_application( self, application: WSGIApplication, catch_exc_info: Literal[True] - ) -> tuple[str, list[_HTTPHeader], Iterable[bytes], ExcInfo | None]: ... + ) -> tuple[str, list[tuple[str, str]], Iterable[bytes], OptExcInfo | None]: ... + @overload + def call_application( + self, application: WSGIApplication, catch_exc_info: bool + ) -> ( + tuple[str, list[tuple[str, str]], Iterable[bytes], OptExcInfo | None] | tuple[str, list[tuple[str, str]], Iterable[bytes]] + ): ... ResponseClass: type[Response] def send(self, application: WSGIApplication | None = None, catch_exc_info: bool = False) -> Response: ... get_response = send - def make_default_send_app(self) -> WSGIApplication: ... + def make_default_send_app(self) -> SendRequest: ... @classmethod def blank( cls, @@ -207,7 +197,7 @@ class BaseRequest: ) -> Self: ... class LegacyRequest(BaseRequest): - @property + @property # type: ignore[override] def uscript_name(self) -> str: ... @uscript_name.setter def uscript_name(self, value: str) -> None: ... @@ -242,13 +232,14 @@ class LimitedLengthFile(io.RawIOBase): maxlen: int remaining: int def __init__(self, file: SupportsRead[bytes], maxlen: int) -> None: ... + def fileno(self) -> int: ... @staticmethod def readable() -> Literal[True]: ... - def readinto(self, buff: ReadableBuffer) -> int: ... + def readinto(self, buff: WriteableBuffer) -> int: ... class Transcoder: charset: str errors: str def __init__(self, charset: str, errors: str = "strict") -> None: ... def transcode_query(self, q: str) -> str: ... - def transcode_fs(self, fs: _FieldStorage, content_type: str) -> io.BytesIO: ... + def transcode_fs(self, fs: cgi_FieldStorage, content_type: str) -> io.BytesIO: ... diff --git a/stubs/WebOb/webob/response.pyi b/stubs/WebOb/webob/response.pyi index afa81270a..4a22b3530 100644 --- a/stubs/WebOb/webob/response.pyi +++ b/stubs/WebOb/webob/response.pyi @@ -2,23 +2,22 @@ from _typeshed import SupportsItems, SupportsRead from _typeshed.wsgi import StartResponse, WSGIApplication, WSGIEnvironment from collections.abc import Iterable, Iterator, Sequence from datetime import timedelta -from typing import IO, Any, Literal, Protocol, TypedDict -from typing_extensions import TypeAlias +from typing import IO, Any, Literal, Protocol, TypedDict, TypeVar, overload +from typing_extensions import Self, TypeAlias +from webob._types import AsymmetricProperty, AsymmetricPropertyWithDelete, SymmetricProperty, SymmetricPropertyWithDelete from webob.byterange import ContentRange -from webob.cachecontrol import _ResponseCacheControl +from webob.cachecontrol import CacheControl from webob.cookies import _SameSitePolicy -from webob.descriptors import ( - _AsymmetricProperty, - _AsymmetricPropertyWithDelete, - _authorization, - _ContentRangeParams, - _DateProperty, - _ListProperty, -) +from webob.descriptors import _authorization, _ContentRangeParams, _DateProperty, _ListProperty from webob.headers import ResponseHeaders from webob.request import Request +__all__ = ["Response"] + +_ResponseT = TypeVar("_ResponseT", bound=Response) +_ResponseCacheControl: TypeAlias = CacheControl[Literal["response"]] + class _ResponseCacheExpires(Protocol): def __call__( self, @@ -52,8 +51,6 @@ class _ResponseCacheControlDict(TypedDict, total=False): stale_while_revalidate: int stale_if_error: int -_HTTPHeader: TypeAlias = tuple[str, str] - class Response: default_content_type: str default_charset: str @@ -62,69 +59,68 @@ class Response: default_body_encoding: str request: Request | None environ: WSGIEnvironment | None - status: str + status: AsymmetricProperty[str, int | str | bytes] conditional_response: bool - def __init__( self, body: bytes | str | None = None, - status: str | None = None, - headerlist: list[_HTTPHeader] | None = None, - app_iter: Iterator[bytes] | None = None, + status: int | str | bytes | None = None, + headerlist: list[tuple[str, str]] | None = None, + app_iter: Iterable[bytes] | None = None, content_type: str | None = None, conditional_response: bool | None = None, charset: str = ..., **kw: Any, ) -> None: ... @classmethod - def from_file(cls, fp: IO[str]) -> Response: ... + def from_file(cls, fp: IO[str] | IO[bytes]) -> Response: ... def copy(self) -> Response: ... - status_code: int - status_int: int - headerlist: _AsymmetricPropertyWithDelete[list[_HTTPHeader], Iterable[_HTTPHeader] | SupportsItems[str, str]] - headers: _AsymmetricProperty[ResponseHeaders, SupportsItems[str, str] | Iterable[tuple[str, str]]] - body: bytes - json: Any - json_body: Any + status_code: SymmetricProperty[int] + status_int: SymmetricProperty[int] + headerlist: AsymmetricPropertyWithDelete[list[tuple[str, str]], Iterable[tuple[str, str]] | SupportsItems[str, str]] + headers: AsymmetricProperty[ResponseHeaders, SupportsItems[str, str] | Iterable[tuple[str, str]]] + body: SymmetricPropertyWithDelete[bytes] + json: SymmetricPropertyWithDelete[Any] + json_body: SymmetricPropertyWithDelete[Any] @property def has_body(self) -> bool: ... - text: str - unicode_body: str # deprecated - ubody: str # deprecated - body_file: _AsymmetricPropertyWithDelete[ResponseBodyFile, SupportsRead[bytes]] - content_length: int | None - def write(self, text: str | bytes) -> None: ... - app_iter: Iterator[bytes] + text: SymmetricPropertyWithDelete[str] + unicode_body: SymmetricPropertyWithDelete[str] # deprecated + ubody: SymmetricPropertyWithDelete[str] # deprecated + body_file: AsymmetricPropertyWithDelete[ResponseBodyFile, SupportsRead[bytes]] + content_length: AsymmetricPropertyWithDelete[int | None, int | str | bytes | None] + def write(self, text: str | bytes) -> int: ... + app_iter: SymmetricPropertyWithDelete[Iterable[bytes]] allow: _ListProperty vary: _ListProperty - content_encoding: str | None - content_language: _ListProperty - content_location: str | None - content_md5: str | None - content_disposition: str | None - accept_ranges: str | None - content_range: _AsymmetricPropertyWithDelete[ContentRange | None, _ContentRangeParams] + content_encoding: SymmetricPropertyWithDelete[str | None] + content_language: SymmetricPropertyWithDelete[str | None] + content_location: SymmetricPropertyWithDelete[str | None] + content_md5: SymmetricPropertyWithDelete[str | None] + content_disposition: SymmetricPropertyWithDelete[str | None] + accept_ranges: SymmetricPropertyWithDelete[str | None] + content_range: AsymmetricPropertyWithDelete[ContentRange | None, _ContentRangeParams] date: _DateProperty expires: _DateProperty last_modified: _DateProperty - etag: _AsymmetricPropertyWithDelete[str | None, tuple[str, bool] | str | None] + etag: AsymmetricPropertyWithDelete[str | None, tuple[str, bool] | str | None] @property def etag_strong(self) -> str | None: ... - location: str | None - pragma: str | None - age: int | None + location: SymmetricPropertyWithDelete[str | None] + pragma: SymmetricPropertyWithDelete[str | None] + age: SymmetricPropertyWithDelete[int | None] retry_after: _DateProperty - server: str | None - www_authenticate: _AsymmetricPropertyWithDelete[ + server: SymmetricPropertyWithDelete[str | None] + www_authenticate: AsymmetricPropertyWithDelete[ _authorization | None, tuple[str, str | dict[str, str]] | list[Any] | str | None ] - charset: str | None - content_type: str | None - content_type_params: _AsymmetricPropertyWithDelete[dict[str, str], SupportsItems[str, str] | None] + charset: SymmetricPropertyWithDelete[str | None] + content_type: SymmetricPropertyWithDelete[str | None] + content_type_params: AsymmetricPropertyWithDelete[dict[str, str], SupportsItems[str, str] | None] def set_cookie( self, - name: str, - value: str | None = "", + name: str | bytes, + value: str | bytes | None = "", max_age: int | timedelta | None = None, path: str = "/", domain: str | None = None, @@ -134,11 +130,14 @@ class Response: overwrite: bool = False, samesite: _SameSitePolicy | None = None, ) -> None: ... - def delete_cookie(self, name: str, path: str = "/", domain: str | None = None) -> None: ... - def unset_cookie(self, name: str, strict: bool = True) -> None: ... - def merge_cookies(self, resp: Response | WSGIApplication) -> None: ... - cache_control: _AsymmetricProperty[_ResponseCacheControl, _ResponseCacheControl | _ResponseCacheControlDict | str | None] - cache_expires: _AsymmetricProperty[_ResponseCacheExpires, timedelta | int | bool | None] + def delete_cookie(self, name: str | bytes, path: str = "/", domain: str | None = None) -> None: ... + def unset_cookie(self, name: str | bytes, strict: bool = True) -> None: ... + @overload + def merge_cookies(self, resp: _ResponseT) -> _ResponseT: ... + @overload + def merge_cookies(self, resp: WSGIApplication) -> WSGIApplication: ... + cache_control: AsymmetricProperty[_ResponseCacheControl, _ResponseCacheControl | _ResponseCacheControlDict | str | None] + cache_expires: AsymmetricProperty[_ResponseCacheExpires, timedelta | int | bool | None] def encode_content(self, encoding: Literal["gzip", "identity"] = "gzip", lazy: bool = False) -> None: ... def decode_content(self) -> None: ... def md5_etag(self, body: bytes | None = None, set_content_md5: bool = False) -> None: ... @@ -154,8 +153,9 @@ class ResponseBodyFile: def __init__(self, response: Response) -> None: ... @property def encoding(self) -> str | None: ... + # NOTE: Technically this is an instance attribute and not a method def write(self, text: str | bytes) -> int: ... - def writelines(self, seq: Sequence[str | bytes]) -> int: ... + def writelines(self, seq: Sequence[str | bytes]) -> None: ... def flush(self) -> None: ... def tell(self) -> int: ... @@ -163,15 +163,15 @@ class AppIterRange: app_iter: Iterator[bytes] start: int stop: int | None - def __init__(self, app_iter: Iterator[bytes], start: int, stop: int | None) -> None: ... - def __iter__(self) -> Iterator[bytes]: ... + def __init__(self, app_iter: Iterable[bytes], start: int, stop: int | None) -> None: ... + def __iter__(self) -> Self: ... def next(self) -> bytes: ... __next__ = next def close(self) -> None: ... class EmptyResponse: - def __init__(self, app_iter: Iterator[bytes] | None = None) -> None: ... - def __iter__(self) -> Iterator[bytes]: ... - def __len__(self) -> int: ... + def __init__(self, app_iter: Iterable[bytes] | None = None) -> None: ... + def __iter__(self) -> Self: ... + def __len__(self) -> Literal[0]: ... def next(self) -> bytes: ... __next__ = next diff --git a/stubs/WebOb/webob/static.pyi b/stubs/WebOb/webob/static.pyi index 0f9e087dc..c74664103 100644 --- a/stubs/WebOb/webob/static.pyi +++ b/stubs/WebOb/webob/static.pyi @@ -1,4 +1,5 @@ -from _typeshed import StrOrBytesPath +from _typeshed import StrPath +from _typeshed.wsgi import WSGIApplication from collections.abc import Iterator from typing import IO, Any @@ -6,14 +7,16 @@ from webob.dec import wsgify from webob.request import Request from webob.response import Response +__all__ = ["FileApp", "DirectoryApp"] + BLOCK_SIZE: int class FileApp: - filename: StrOrBytesPath + filename: StrPath kw: dict[str, Any] - def __init__(self, filename: StrOrBytesPath, **kw: Any) -> None: ... + def __init__(self, filename: StrPath, **kw: Any) -> None: ... @wsgify - def __call__(self, req: Request) -> Response: ... + def __call__(self, req: Request) -> WSGIApplication: ... class FileIter: file: IO[bytes] @@ -24,14 +27,14 @@ class FileIter: __iter__ = app_iter_range class DirectoryApp: - path: str | bytes - index_page: str | None + path: StrPath + index_page: str hide_index_with_redirect: bool fileapp_kw: dict[str, Any] def __init__( - self, path: StrOrBytesPath, index_page: str = "index.html", hide_index_with_redirect: bool = False, **kw: Any + self, path: StrPath, index_page: str = "index.html", hide_index_with_redirect: bool = False, **kw: Any ) -> None: ... - def make_fileapp(self, path: StrOrBytesPath) -> FileApp: ... + def make_fileapp(self, path: StrPath) -> FileApp: ... @wsgify def __call__(self, req: Request) -> Response | FileApp: ... - def index(self, req: Request, path: StrOrBytesPath) -> Response | FileApp: ... + def index(self, req: Request, path: StrPath) -> Response | FileApp: ... diff --git a/stubs/WebOb/webob/util.pyi b/stubs/WebOb/webob/util.pyi index f0354c241..a02d71666 100644 --- a/stubs/WebOb/webob/util.pyi +++ b/stubs/WebOb/webob/util.pyi @@ -1,10 +1,7 @@ from collections.abc import Callable -from typing import AnyStr, Protocol +from typing import AnyStr -class _HasHTML(Protocol): - def __html__(self) -> str: ... - -def html_escape(s: str | bytes | _HasHTML) -> str: ... +def html_escape(s: object) -> str: ... def header_docstring(header: str, rfc_section: str) -> str: ... def warn_deprecation(text: str, version: str, stacklevel: int) -> None: ...