diff --git a/pyrightconfig.stricter.json b/pyrightconfig.stricter.json index dd114d964..83fdbfd14 100644 --- a/pyrightconfig.stricter.json +++ b/pyrightconfig.stricter.json @@ -76,6 +76,7 @@ "stubs/ttkthemes", "stubs/urllib3", "stubs/vobject", + "stubs/WebOb" ], "typeCheckingMode": "strict", // TODO: Complete incomplete stubs diff --git a/stubs/WebOb/@tests/stubtest_allowlist.txt b/stubs/WebOb/@tests/stubtest_allowlist.txt new file mode 100644 index 000000000..0607a4b60 --- /dev/null +++ b/stubs/WebOb/@tests/stubtest_allowlist.txt @@ -0,0 +1,153 @@ +# Error: is not present in stub +# ============================= +# These are plain strings, regex strings or compiled regex patterns +# which are used internally for parsing, so they should not be public API +webob.acceptparse.Accept.accept_compiled_re +webob.acceptparse.Accept.accept_ext_compiled_re +webob.acceptparse.Accept.accept_ext_re +webob.acceptparse.Accept.accept_params_re +webob.acceptparse.Accept.media_range_n_accept_params_compiled_re +webob.acceptparse.Accept.media_range_n_accept_params_re +webob.acceptparse.Accept.media_range_re +webob.acceptparse.Accept.media_type_compiled_re +webob.acceptparse.Accept.media_type_re +webob.acceptparse.Accept.obs_text_re +webob.acceptparse.Accept.parameter_re +webob.acceptparse.Accept.parameters_compiled_re +webob.acceptparse.Accept.qdtext_re +webob.acceptparse.Accept.quoted_pair_re +webob.acceptparse.Accept.quoted_string_re +webob.acceptparse.Accept.subtype_re +webob.acceptparse.Accept.type_re +webob.acceptparse.Accept.vchar_re +webob.acceptparse.AcceptCharset.accept_charset_compiled_re +webob.acceptparse.AcceptCharset.charset_n_weight_compiled_re +webob.acceptparse.AcceptCharset.charset_n_weight_re +webob.acceptparse.AcceptCharset.charset_re +webob.acceptparse.AcceptEncoding.accept_encoding_compiled_re +webob.acceptparse.AcceptEncoding.codings_n_weight_compiled_re +webob.acceptparse.AcceptEncoding.codings_n_weight_re +webob.acceptparse.AcceptEncoding.codings_re +webob.acceptparse.AcceptLanguage.accept_language_compiled_re +webob.acceptparse.AcceptLanguage.lang_range_n_weight_compiled_re +webob.acceptparse.AcceptLanguage.lang_range_n_weight_re +webob.acceptparse.AcceptLanguage.lang_range_re +webob.acceptparse.OWS_re +webob.acceptparse.qvalue_re +webob.acceptparse.tchar_re +webob.acceptparse.token_compiled_re +webob.acceptparse.token_re +webob.acceptparse.weight_re +webob.cachecontrol.need_quote_re +webob.cachecontrol.token_re +webob.client.SendRequest.MULTILINE_RE +webob.descriptors.CHARSET_RE +webob.descriptors.SCHEME_RE + +webob.acceptparse.MIMEAccept # Deprecated API + +# PY2 compat stuff that has already been removed upstream +webob.compat +webob.multidict.MultiDict.iteritems +webob.multidict.MultiDict.iterkeys +webob.multidict.MultiDict.itervalues +webob.multidict.NestedMultiDict.iteritems +webob.multidict.NestedMultiDict.iterkeys +webob.multidict.NestedMultiDict.itervalues +webob.multidict.NoVars.iterkeys + +# 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 +# like to switch to a @type_error() decorator +webob.multidict.NoVars.__getitem__ +webob.multidict.NoVars.__setitem__ +webob.multidict.NoVars.__delitem__ +webob.multidict.NoVars.add +webob.multidict.NoVars.setdefault +webob.multidict.NoVars.update +webob.multidict.NoVars.clear +webob.multidict.NoVars.pop +webob.multidict.NoVars.popitem +webob.multidict.NoVars.getone + +# ResponseBodyFile cannot be closed and emits an Exception, so we're better +# off pretending the method doesn't exist +webob.response.ResponseBodyFile.close + +# Error: is inconsistent +# ====================== +# set_cookie has a deprecated argument `expires` which has been removed upstream +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 + +# 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 +# interchangeably in some parts of the API. So they re-use generic functions +# 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.clear + +# 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 +webob.request.AdhocAttrMixin.__delattr__ +webob.request.AdhocAttrMixin.__getattr__ +webob.request.AdhocAttrMixin.__setattr__ + +# BaseRequest has a bunch of named parameters that have been deprecated and +# removed upstream, since there's a `**kwargs` anyways, it doesn't really +# make sense to annotate them and pretend they're part of the API. +webob.request.BaseRequest.__init__ + +# Error: is not present at runtime +# ============================= +# This attribute is there to help mypy type narrow NoVars based on its static +# falsyness, so it's to make it possible to narrow the type union in request.POST +# without importing MultiDict or NoVars +webob.multidict.NoVars.__bool__ + +# This attribute is set on the instance instead of the class in the `__init__` +# so the type annotation is technically wrong, however I am unsure about +# whether the ResponseBodyFile would satisfy some of the IO Protocols if +# `write` was defined as a Callable instance attribute. It's hard to come up +# with a use-case where the distinction matters, besides inherting from +# the class and overwriting the __init__ and forgetting to populate `write`. +webob.response.ResponseBodyFile.write diff --git a/stubs/WebOb/METADATA.toml b/stubs/WebOb/METADATA.toml new file mode 100644 index 000000000..690242a33 --- /dev/null +++ b/stubs/WebOb/METADATA.toml @@ -0,0 +1 @@ +version = "1.8.*" diff --git a/stubs/WebOb/webob/__init__.pyi b/stubs/WebOb/webob/__init__.pyi new file mode 100644 index 000000000..308be3055 --- /dev/null +++ b/stubs/WebOb/webob/__init__.pyi @@ -0,0 +1,13 @@ +from webob.datetime_utils import ( + UTC as UTC, + day as day, + hour as hour, + minute as minute, + month as month, + second as second, + week as week, + year as year, +) +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 diff --git a/stubs/WebOb/webob/acceptparse.pyi b/stubs/WebOb/webob/acceptparse.pyi new file mode 100644 index 000000000..4dba5387c --- /dev/null +++ b/stubs/WebOb/webob/acceptparse.pyi @@ -0,0 +1,503 @@ +from _typeshed import SupportsItems +from collections.abc import Callable, Iterable, Iterator +from typing import Any, NamedTuple, TypeVar, overload +from typing_extensions import Literal, Self, TypeAlias + +_T = TypeVar("_T") +_ListOrTuple: TypeAlias = list[_T] | tuple[_T, ...] +_ParsedAccept: TypeAlias = tuple[str, float, list[tuple[str, str]], list[str | tuple[str, str]]] + +class AcceptOffer(NamedTuple): + type: str + subtype: str + params: tuple[tuple[str, str], ...] + +class Accept: + @classmethod + def parse(cls, value: str) -> Iterator[_ParsedAccept]: ... + @classmethod + def parse_offer(cls, offer: str | AcceptOffer) -> AcceptOffer: ... + +class AcceptValidHeader(Accept): + @property + def header_value(self) -> str: ... + @property + def parsed(self) -> list[_ParsedAccept] | None: ... + 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 __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 accept_html(self) -> bool: ... + @property + def accepts_html(self) -> bool: ... + def acceptable_offers(self, offers: Iterable[str | AcceptOffer]) -> list[tuple[str, float]]: ... + @overload + 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 | tuple[str, float] | list[Any]], default_match: str) -> str: ... + 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]]: ... + @overload + 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 | tuple[str, float] | list[Any]], default_match: str) -> str: ... + def quality(self, offer: str) -> float: ... + +class AcceptNoHeader(_AcceptInvalidOrNoHeader): + @property + def header_value(self) -> None: ... + def __init__(self) -> None: ... + +class AcceptInvalidHeader(_AcceptInvalidOrNoHeader): + @property + def header_value(self) -> str: ... + def __init__(self, header_value: str) -> None: ... + +@overload +def create_accept_header(header_value: None) -> 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: ... + +def accept_property() -> _AcceptProperty: ... + +class AcceptCharset: + @classmethod + def parse(cls, value: str) -> Iterator[tuple[str, float]]: ... + +class AcceptCharsetValidHeader(AcceptCharset): + @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: 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 __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: 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 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: ... + @overload + def best_match(self, offers: Iterable[str], default_match: str) -> str: ... + def quality(self, offer: str) -> float | None: ... + +class AcceptCharsetNoHeader(_AcceptCharsetInvalidOrNoHeader): + @property + def header_value(self) -> None: ... + def __init__(self) -> None: ... + +class AcceptCharsetInvalidHeader(_AcceptCharsetInvalidOrNoHeader): + @property + def header_value(self) -> str: ... + def __init__(self, header_value: str) -> None: ... + +@overload +def create_accept_charset_header(header_value: 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: ... + +def accept_charset_property() -> _AcceptCharsetProperty: ... + +class AcceptEncoding: + @classmethod + def parse(cls, value: str) -> Iterator[tuple[str, float]]: ... + +class AcceptEncodingValidHeader(AcceptEncoding): + @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: 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 __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: 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 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: ... + @overload + def best_match(self, offers: Iterable[str], default_match: str) -> str: ... + def quality(self, offer: str) -> float | None: ... + +class AcceptEncodingNoHeader(_AcceptEncodingInvalidOrNoHeader): + @property + def header_value(self) -> None: ... + def __init__(self) -> None: ... + +class AcceptEncodingInvalidHeader(_AcceptEncodingInvalidOrNoHeader): + @property + def header_value(self) -> str: ... + def __init__(self, header_value: str) -> None: ... + +@overload +def create_accept_encoding_header(header_value: 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: ... + +def accept_encoding_property() -> _AcceptEncodingProperty: ... + +class AcceptLanguage: + @classmethod + def parse(cls, value: str) -> Iterator[tuple[str, float]]: ... + +class AcceptLanguageValidHeader(AcceptLanguage): + @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 __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: 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: ... + @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: ... + 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: ... + @overload + def best_match(self, offers: Iterable[str], 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, + ) -> 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: ... + def quality(self, offer: str) -> float | None: ... + +class AcceptLanguageNoHeader(_AcceptLanguageInvalidOrNoHeader): + def __init__(self) -> None: ... + @property + def header_value(self) -> None: ... + +class AcceptLanguageInvalidHeader(_AcceptLanguageInvalidOrNoHeader): + def __init__(self, header_value: str) -> None: ... + @property + def header_value(self) -> str: ... + +@overload +def create_accept_language_header(header_value: None) -> AcceptLanguageNoHeader: ... +@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: ... + +def accept_language_property() -> _AcceptLanguageProperty: ... diff --git a/stubs/WebOb/webob/byterange.pyi b/stubs/WebOb/webob/byterange.pyi new file mode 100644 index 000000000..c63cf6257 --- /dev/null +++ b/stubs/WebOb/webob/byterange.pyi @@ -0,0 +1,28 @@ +from collections.abc import Iterator +from typing import overload +from typing_extensions import Self + +class Range: + start: int | None + end: int | None + @overload + def __init__(self, start: None, end: None): ... + @overload + def __init__(self, start: int, end: int | None): ... + def range_for_length(self, length: int | None) -> tuple[int, int] | None: ... + def content_range(self, length: int | None) -> ContentRange | None: ... + def __iter__(self) -> Iterator[int | None]: ... + @classmethod + def parse(cls, header: str | None) -> Self: ... + +class ContentRange: + start: int | None + stop: int | None + length: int | None + @overload + def __init__(self, start: None, stop: None, length: int | None): ... + @overload + def __init__(self, start: int, stop: int, length: int | None): ... + def __iter__(self) -> Iterator[int | None]: ... + @classmethod + def parse(cls, value: str | None) -> Self: ... diff --git a/stubs/WebOb/webob/cachecontrol.pyi b/stubs/WebOb/webob/cachecontrol.pyi new file mode 100644 index 000000000..9e42ae605 --- /dev/null +++ b/stubs/WebOb/webob/cachecontrol.pyi @@ -0,0 +1,96 @@ +from collections.abc import Callable, MutableMapping +from typing import Any, Generic, TypeVar, overload +from typing_extensions import Literal, Self, TypeAlias + +from webob.request import Request +from webob.response import Response + +_T = TypeVar("_T") +_KT = TypeVar("_KT") +_VT = TypeVar("_VT") +_NoneLiteral = TypeVar("_NoneLiteral") +_Type: TypeAlias = type + +class UpdateDict(dict[_KT, _VT]): + 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): ... + @overload + def __get__(self, obj: None, type: _Type | None = None) -> Self: ... + @overload + def __get__(self, obj: Any, type: _Type | None = None) -> bool: ... + def __set__(self, obj: Any, value: bool) -> None: ... + def __delete__(self, obj: Any) -> None: ... + +class value_property(Generic[_T, _NoneLiteral]): + prop: str + default: _T | None + none: _NoneLiteral + type: str | None + @overload + def __init__(self, prop: str, default: None = None, none: None = None, type: str | None = None): ... + @overload + def __init__(self, prop: str, default: _T, none: _NoneLiteral, type: str | None = None): ... + @overload + def __get__(self, obj: None, type: _Type | None = None) -> Self: ... + @overload + def __get__(self, obj: Any, type: _Type | None = None) -> _T | _NoneLiteral | None: ... + def __set__(self, obj: Any, value: _T | Literal[True]) -> None: ... + def __delete__(self, obj: Any) -> None: ... + +class _IntValueProperty(value_property[int, _NoneLiteral]): + def __set__(self, obj: Any, value: int) -> None: ... + +class _BaseCacheControl: + update_dict = UpdateDict + properties: MutableMapping[str, Any] + type: Literal["request", "response"] | None + @classmethod + @overload + def parse(cls, header: str, updates_to: None = None, type: None = None) -> _AnyCacheControl: ... + @classmethod + @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 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: ... diff --git a/stubs/WebOb/webob/client.pyi b/stubs/WebOb/webob/client.pyi new file mode 100644 index 000000000..81f54880b --- /dev/null +++ b/stubs/WebOb/webob/client.pyi @@ -0,0 +1,12 @@ +from _typeshed.wsgi import StartResponse, WSGIEnvironment +from collections.abc import Iterable +from http.client import HTTPMessage +from typing import ClassVar + +class SendRequest: + def __init__(self, HTTPConnection=..., HTTPSConnection=...) -> None: ... + def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: ... + filtered_headers: ClassVar[tuple[str, ...]] + def parse_headers(self, message: HTTPMessage) -> list[tuple[str, str]]: ... + +send_request_app: SendRequest diff --git a/stubs/WebOb/webob/cookies.pyi b/stubs/WebOb/webob/cookies.pyi new file mode 100644 index 000000000..f0bfb5830 --- /dev/null +++ b/stubs/WebOb/webob/cookies.pyi @@ -0,0 +1,148 @@ +from _typeshed import Incomplete +from _typeshed.wsgi import WSGIEnvironment +from collections.abc import ItemsView, Iterator, KeysView, MutableMapping, ValuesView +from datetime import date, datetime, timedelta +from typing import TypeVar, overload +from typing_extensions import Literal + +from webob.descriptors import _AsymmetricProperty + +_T = TypeVar("_T") + +class RequestCookies(MutableMapping[str, str]): + def __init__(self, environ: WSGIEnvironment) -> None: ... + def __setitem__(self, name: str, value: str) -> None: ... + def __getitem__(self, name: 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 __delitem__(self, name: str) -> None: ... + def keys(self) -> KeysView[str]: ... + def values(self) -> ValuesView[str]: ... + def items(self) -> ItemsView[str, str]: ... + def __contains__(self, name: object) -> bool: ... + def __iter__(self) -> Iterator[str]: ... + def __len__(self) -> int: ... + def clear(self) -> None: ... + +class Cookie(dict[str, 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 serialize(self, full: bool = True) -> str: ... + def values(self) -> list[Morsel]: ... # type:ignore[override] + def __str__(self, full: bool = True) -> str: ... + +class Morsel(dict[bytes, bytes | bool | None]): + name: bytes + value: bytes + def __init__(self, name: str | bytes, value: str | bytes) -> None: ... + @property + def path(self) -> bytes | None: ... + @path.setter + def path(self, v: bytes | None) -> None: ... + @property + def domain(self) -> bytes | None: ... + @domain.setter + def domain(self, v: bytes | None) -> None: ... + @property + 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, Literal["strict", "lax", "none"] | None] + def serialize(self, full: bool = True) -> str: ... + def __str__(self, full: bool = True) -> str: ... + +def make_cookie( + name: str | bytes, + value: str | bytes | None, + max_age: int | timedelta | None = None, + path: str = "/", + domain: str | None = None, + secure: bool = False, + httponly: bool = False, + comment: str | None = None, + samesite: Literal["strict", "lax", "none"] | None = None, +) -> str: ... + +class JSONSerializer: + def dumps(self, appstruct): ... + def loads(self, bstruct): ... + +class Base64Serializer: + serializer: Incomplete + def __init__(self, serializer: Incomplete | None = None) -> None: ... + def dumps(self, appstruct): ... + def loads(self, bstruct): ... + +class SignedSerializer: + salt: Incomplete + secret: Incomplete + hashalg: Incomplete + salted_secret: Incomplete + digestmod: Incomplete + digest_size: Incomplete + serializer: Incomplete + def __init__(self, secret, salt, hashalg: str = "sha512", serializer: Incomplete | None = None) -> None: ... + def dumps(self, appstruct): ... + def loads(self, bstruct): ... + +class CookieProfile: + cookie_name: Incomplete + secure: Incomplete + max_age: Incomplete + httponly: Incomplete + samesite: Incomplete + path: Incomplete + domains: Incomplete + serializer: Incomplete + request: Incomplete + def __init__( + self, + cookie_name, + secure: bool = False, + max_age: Incomplete | None = None, + httponly: Incomplete | None = None, + samesite: Incomplete | None = None, + path: str = "/", + domains: Incomplete | None = None, + serializer: Incomplete | None = None, + ) -> None: ... + def __call__(self, request): ... + def bind(self, request): ... + def get_value(self): ... + def set_cookies(self, response, value, domains=..., max_age=..., path=..., secure=..., httponly=..., samesite=...): ... + def get_headers(self, value, domains=..., max_age=..., path=..., secure=..., httponly=..., samesite=...): ... + +class SignedCookieProfile(CookieProfile): + secret: Incomplete + salt: Incomplete + hashalg: Incomplete + original_serializer: Incomplete + def __init__( + self, + secret, + salt, + cookie_name, + secure: bool = False, + max_age: Incomplete | None = None, + httponly: bool = False, + samesite: Incomplete | None = None, + path: str = "/", + domains: Incomplete | None = None, + hashalg: str = "sha512", + serializer: Incomplete | None = None, + ) -> None: ... + def bind(self, request): ... diff --git a/stubs/WebOb/webob/datetime_utils.pyi b/stubs/WebOb/webob/datetime_utils.pyi new file mode 100644 index 000000000..6a57d83d9 --- /dev/null +++ b/stubs/WebOb/webob/datetime_utils.pyi @@ -0,0 +1,23 @@ +from datetime import datetime, timedelta, tzinfo + +class _UTC(tzinfo): + def dst(self, dt: datetime | None) -> timedelta: ... + def utcoffset(self, dt: datetime | None) -> timedelta: ... + def tzname(self, dt: datetime | None) -> str: ... + +UTC: _UTC + +def timedelta_to_seconds(td: timedelta) -> int: ... + +day: timedelta +week: timedelta +hour: timedelta +minute: timedelta +second: timedelta +month: timedelta +year: timedelta + +def parse_date(value: str | bytes) -> datetime | None: ... +def serialize_date(dt) -> str: ... +def parse_date_delta(value: str | bytes) -> datetime | None: ... +def serialize_date_delta(value) -> str: ... diff --git a/stubs/WebOb/webob/dec.pyi b/stubs/WebOb/webob/dec.pyi new file mode 100644 index 000000000..a08ed4926 --- /dev/null +++ b/stubs/WebOb/webob/dec.pyi @@ -0,0 +1,49 @@ +from _typeshed import Incomplete +from _typeshed.wsgi import WSGIApplication +from typing import ClassVar, overload +from typing_extensions import Self + +from webob.request import Request + +class wsgify: + RequestClass: ClassVar[type[Request]] + func: Incomplete + args: Incomplete + kwargs: Incomplete + middleware_wraps: Incomplete + def __init__( + self, + func: Incomplete | None = None, + RequestClass: Incomplete | None = None, + args=..., + kwargs: Incomplete | None = None, + middleware_wraps: Incomplete | None = None, + ) -> None: ... + @overload + def __get__(self, obj: None, type: type | None = None) -> Self: ... + @overload + def __get__(self, obj: object, type: type | None = None) -> WSGIApplication: ... + def __call__(self, req, *args, **kw): ... + def get(self, url, **kw): ... + def post(self, url, POST: Incomplete | None = None, **kw): ... + def request(self, url, **kw): ... + def call_func(self, req, *args, **kwargs): ... + def clone(self, func: Incomplete | None = None, **kw): ... + @property + def undecorated(self): ... + @classmethod + def middleware(cls, middle_func: Incomplete | None = None, app: Incomplete | None = None, **kw): ... + +class _UnboundMiddleware: + wrapper_class: Incomplete + app: Incomplete + kw: Incomplete + def __init__(self, wrapper_class, app, kw) -> None: ... + def __call__(self, func, app: Incomplete | None = None): ... + +class _MiddlewareFactory: + wrapper_class: Incomplete + middleware: Incomplete + kw: Incomplete + def __init__(self, wrapper_class, middleware, kw) -> None: ... + def __call__(self, app: Incomplete | None = None, **config): ... diff --git a/stubs/WebOb/webob/descriptors.pyi b/stubs/WebOb/webob/descriptors.pyi new file mode 100644 index 000000000..6119896b6 --- /dev/null +++ b/stubs/WebOb/webob/descriptors.pyi @@ -0,0 +1,58 @@ +from _typeshed import Incomplete +from collections.abc import Iterable +from datetime import date, datetime, timedelta +from time import _TimeTuple, struct_time +from typing import Any, Generic, NamedTuple, TypeVar, overload + +_GetterReturnType = TypeVar("_GetterReturnType") +_SetterValueType = TypeVar("_SetterValueType") + +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 _StringProperty(_AsymmetricPropertyWithDelete[str | None, 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] +): ... + +def environ_getter(key, default=..., rfc_section: Incomplete | None = None): ... +def environ_decoder(key, default=..., rfc_section: Incomplete | None = None, encattr: Incomplete | None = None): ... +def upath_property(key): ... +def deprecated_property(attr, name, text, version): ... +def header_getter(header: str, rfc_section: str) -> _StringProperty: ... +def converter(prop, parse, serialize, convert_name: Incomplete | None = None): ... +def list_header(header: str, rfc_section: str) -> _ListProperty: ... +def parse_list(value): ... +def serialize_list(value): ... +def converter_date(prop): ... +def date_header(header: str, rfc_section: str) -> _DateProperty: ... +def parse_etag_response(value, strong: bool = False): ... +def serialize_etag_response(value): ... +def serialize_if_range(value): ... +def parse_range(value): ... +def serialize_range(value): ... +def parse_int(value): ... +def parse_int_safe(value): ... + +serialize_int = str + +def parse_content_range(value): ... +def serialize_content_range(value): ... +def parse_auth_params(params): ... + +known_auth_schemes: dict[str, None] + +class _authorization(NamedTuple): + authtype: str + params: dict[str, str] | str + +def parse_auth(val): ... +def serialize_auth(val): ... diff --git a/stubs/WebOb/webob/etag.pyi b/stubs/WebOb/webob/etag.pyi new file mode 100644 index 000000000..800570393 --- /dev/null +++ b/stubs/WebOb/webob/etag.pyi @@ -0,0 +1,53 @@ +from collections.abc import Collection +from datetime import datetime +from typing import Any, overload +from typing_extensions import TypeAlias + +from webob.response import Response + +_ETag: TypeAlias = _AnyETag | _NoETag | ETagMatcher + +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: ... + +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: ... + +AnyETag: _AnyETag + +class _NoETag: + def __bool__(self) -> bool: ... + def __contains__(self, other: str) -> bool: ... + +NoETag: _NoETag + +class ETagMatcher: + etags: Collection[str] + def __init__(self, etags: Collection[str]) -> None: ... + def __contains__(self, other: str) -> bool: ... + @classmethod + def parse(cls, value: str, strong: bool = True) -> ETagMatcher | _AnyETag: ... + +class IfRange: + etag: _ETag + def __init__(self, etag: _ETag) -> None: ... + @classmethod + def parse(cls, value: str) -> IfRange | IfRangeDate: ... + def __contains__(self, resp: Response) -> bool: ... + def __bool__(self) -> bool: ... + +class IfRangeDate: + date: datetime + def __init__(self, date: datetime) -> None: ... + def __contains__(self, resp: Response) -> bool: ... diff --git a/stubs/WebOb/webob/exc.pyi b/stubs/WebOb/webob/exc.pyi new file mode 100644 index 000000000..b662ea0ff --- /dev/null +++ b/stubs/WebOb/webob/exc.pyi @@ -0,0 +1,126 @@ +from _typeshed import SupportsItems, SupportsKeysAndGetItem +from _typeshed.wsgi import StartResponse, WSGIApplication, WSGIEnvironment +from collections.abc import Iterable +from string import Template +from typing import Any, Protocol +from typing_extensions import Literal, Self + +from webob.response import Response + +class _JSONFormatter(Protocol): + def __call__(self, body: str, status: str, title: str, environ: WSGIEnvironment) -> str: ... + +class HTTPException(Exception): + wsgi_response: Response + def __init__(self, message: str, wsgi_response: Response) -> None: ... + def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: ... + +class WSGIHTTPException(Response, HTTPException): + code: int + title: str + explanation: str + body_template_obj: Template + plain_template_obj: Template + html_template_obj: Template + empty_body: bool + detail: str | None + comment: str | None + def __init__( + self, + detail: str | None = None, + headers: SupportsItems[str, str] | SupportsKeysAndGetItem[str, str] | Iterable[tuple[str, str]] | None = None, + comment: str | None = None, + body_template: str | None = None, + json_formatter: _JSONFormatter | None = None, + **kw: Any, + ) -> 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 generate_response(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: ... + @property + def wsgi_response(self) -> Self: ... # type:ignore[override] + def __str__(self) -> str: ... # type:ignore[override] # noqaY029 + +class HTTPError(WSGIHTTPException): ... +class HTTPRedirection(WSGIHTTPException): ... +class HTTPOk(WSGIHTTPException): ... +class HTTPCreated(HTTPOk): ... +class HTTPAccepted(HTTPOk): ... +class HTTPNonAuthoritativeInformation(HTTPOk): ... + +class HTTPNoContent(HTTPOk): + empty_body: Literal[True] + +class HTTPResetContent(HTTPOk): + empty_body: Literal[True] + +class HTTPPartialContent(HTTPOk): ... + +class _HTTPMove(HTTPRedirection): + explanation: str + add_slash: bool + def __init__( + self, + detail: str | None = None, + headers: str | None = None, + comment: str | None = None, + body_template: str | None = None, + location: str | None = None, + add_slash: bool = False, + ) -> None: ... + +class HTTPMultipleChoices(_HTTPMove): ... +class HTTPMovedPermanently(_HTTPMove): ... +class HTTPFound(_HTTPMove): ... +class HTTPSeeOther(_HTTPMove): ... + +class HTTPNotModified(HTTPRedirection): + empty_body: Literal[True] + +class HTTPUseProxy(_HTTPMove): ... +class HTTPTemporaryRedirect(_HTTPMove): ... +class HTTPPermanentRedirect(_HTTPMove): ... +class HTTPClientError(HTTPError): ... +class HTTPBadRequest(HTTPClientError): ... +class HTTPUnauthorized(HTTPClientError): ... +class HTTPPaymentRequired(HTTPClientError): ... +class HTTPForbidden(HTTPClientError): ... +class HTTPNotFound(HTTPClientError): ... +class HTTPMethodNotAllowed(HTTPClientError): ... +class HTTPNotAcceptable(HTTPClientError): ... +class HTTPProxyAuthenticationRequired(HTTPClientError): ... +class HTTPRequestTimeout(HTTPClientError): ... +class HTTPConflict(HTTPClientError): ... +class HTTPGone(HTTPClientError): ... +class HTTPLengthRequired(HTTPClientError): ... +class HTTPPreconditionFailed(HTTPClientError): ... +class HTTPRequestEntityTooLarge(HTTPClientError): ... +class HTTPRequestURITooLong(HTTPClientError): ... +class HTTPUnsupportedMediaType(HTTPClientError): ... +class HTTPRequestRangeNotSatisfiable(HTTPClientError): ... +class HTTPExpectationFailed(HTTPClientError): ... +class HTTPUnprocessableEntity(HTTPClientError): ... +class HTTPLocked(HTTPClientError): ... +class HTTPFailedDependency(HTTPClientError): ... +class HTTPPreconditionRequired(HTTPClientError): ... +class HTTPTooManyRequests(HTTPClientError): ... +class HTTPRequestHeaderFieldsTooLarge(HTTPClientError): ... +class HTTPUnavailableForLegalReasons(HTTPClientError): ... +class HTTPServerError(HTTPError): ... +class HTTPInternalServerError(HTTPServerError): ... +class HTTPNotImplemented(HTTPServerError): ... +class HTTPBadGateway(HTTPServerError): ... +class HTTPServiceUnavailable(HTTPServerError): ... +class HTTPGatewayTimeout(HTTPServerError): ... +class HTTPVersionNotSupported(HTTPServerError): ... +class HTTPInsufficientStorage(HTTPServerError): ... +class HTTPNetworkAuthenticationRequired(HTTPServerError): ... + +class HTTPExceptionMiddleware: + application: WSGIApplication + def __init__(self, application: WSGIApplication) -> None: ... + def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: ... + +status_map: dict[int, WSGIHTTPException] diff --git a/stubs/WebOb/webob/headers.pyi b/stubs/WebOb/webob/headers.pyi new file mode 100644 index 000000000..35859b327 --- /dev/null +++ b/stubs/WebOb/webob/headers.pyi @@ -0,0 +1,17 @@ +from _typeshed.wsgi import WSGIEnvironment +from collections.abc import Iterator, MutableMapping + +from webob.multidict import MultiDict + +class ResponseHeaders(MultiDict[str, str]): ... + +class EnvironHeaders(MutableMapping[str, str]): + environ: WSGIEnvironment + def __init__(self, environ: WSGIEnvironment) -> None: ... + 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 __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 new file mode 100644 index 000000000..3486d6ae3 --- /dev/null +++ b/stubs/WebOb/webob/multidict.pyi @@ -0,0 +1,105 @@ +from _typeshed import SupportsItems, SupportsKeysAndGetItem +from _typeshed.wsgi import WSGIEnvironment +from cgi import FieldStorage +from collections.abc import Collection, Iterable, Iterator, MutableMapping +from typing import Any, TypeVar, overload +from typing_extensions import Literal, Self + +_T = TypeVar("_T") +_KT = TypeVar("_KT") +_VT = TypeVar("_VT") + +class MultiDict(MutableMapping[_KT, _VT]): + @overload + def __init__(self, __m: SupportsItems[_KT, _VT], **kwargs: _VT): ... + @overload + def __init__(self, __m: Iterable[tuple[_KT, _VT]], **kwargs: _VT): ... + @overload + def __init__(self, **kwargs: _VT) -> None: ... + @classmethod + def view_list(cls, lst: list[tuple[_KT, _VT]]) -> MultiDict[_KT, _VT]: ... + @classmethod + def from_fieldstorage(cls, fs: FieldStorage) -> MultiDict[str, str | FieldStorage]: ... + def __getitem__(self, key: _KT) -> _VT: ... + def __setitem__(self, key: _KT, value: _VT) -> None: ... + def add(self, key: _KT, value: _VT) -> None: ... + @overload + def get(self, key: _KT) -> _VT: ... + @overload + def get(self, key: _KT, default: _T) -> _VT | _T: ... + def getall(self, key: _KT) -> list[_VT]: ... + def getone(self, key: _KT) -> _VT: ... + def mixed(self) -> dict[_KT, _VT | list[_VT]]: ... + def dict_of_lists(self) -> dict[_KT, list[_VT]]: ... + def __delitem__(self, key: _KT) -> None: ... + def __contains__(self, key: object) -> bool: ... + has_key = __contains__ + def clear(self) -> None: ... + def copy(self) -> Self: ... + @overload + def setdefault(self, key: _KT, default: None = None) -> _VT | None: ... # type:ignore[misc] + @overload + def setdefault(self, key: _KT, default: _VT) -> _VT: ... + @overload + def pop(self, key: _KT) -> _VT: ... + @overload + 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: ... + @overload + def update(self, **kwargs: _VT) -> None: ... + @overload + def extend(self, other: SupportsItems[_KT, _VT], **kwargs: _VT) -> None: ... + @overload + def extend(self, other: SupportsKeysAndGetItem[_KT, _VT], **kwargs: _VT) -> None: ... + @overload + def extend(self, other: Iterable[tuple[_KT, _VT]], **kwargs: _VT) -> None: ... + @overload + def extend(self, 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] + +class GetDict(MultiDict[str, str]): + env: WSGIEnvironment + @overload + def __init__(self, data: SupportsItems[str, str], env: WSGIEnvironment) -> None: ... + @overload + def __init__(self, data: Iterable[tuple[str, str]], env: WSGIEnvironment) -> None: ... + def on_change(self) -> None: ... + +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: ... + def copy(self) -> MultiDict[_KT, _VT]: ... # type:ignore[override] + +class NoVars: + reason: str + def __init__(self, reason: str | None = None) -> None: ... + @overload + def get(self, key: str, default: None = None) -> None: ... + @overload + def get(self, key: str, default: _T) -> _T: ... + 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 __contains__(self, key: object) -> Literal[False]: ... + has_key = __contains__ + def copy(self) -> Self: ... + def __len__(self) -> Literal[0]: ... + def __iter__(self) -> Iterator[str]: ... + def keys(self) -> Iterator[str]: ... + def values(self) -> Iterator[str]: ... + def items(self) -> Iterator[tuple[str, str]]: ... + def __bool__(self) -> Literal[False]: ... diff --git a/stubs/WebOb/webob/request.pyi b/stubs/WebOb/webob/request.pyi new file mode 100644 index 000000000..d14ff8038 --- /dev/null +++ b/stubs/WebOb/webob/request.pyi @@ -0,0 +1,238 @@ +import datetime +import io +from _typeshed import ( + ExcInfo, + Incomplete, + ReadableBuffer, + SupportsItems, + SupportsKeysAndGetItem, + SupportsNoArgReadline, + SupportsRead, +) +from _typeshed.wsgi import WSGIApplication, WSGIEnvironment +from cgi import FieldStorage +from collections.abc import Iterable, Mapping +from re import Pattern +from tempfile import _TemporaryFileWrapper +from typing import IO, Any, ClassVar, Protocol, TypeVar, overload +from typing_extensions import Literal, Self, TypeAlias, TypedDict + +from webob.acceptparse import _AcceptCharsetProperty, _AcceptEncodingProperty, _AcceptLanguageProperty, _AcceptProperty +from webob.byterange import Range +from webob.cachecontrol import _RequestCacheControl +from webob.cookies import RequestCookies +from webob.descriptors import _AsymmetricProperty, _AsymmetricPropertyWithDelete, _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 + +_T = TypeVar("_T") +_HTTPMethod: TypeAlias = Literal["GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"] +_ListOrTuple: TypeAlias = list[_T] | tuple[_T, ...] + +class _SupportsReadAndNoArgReadline(SupportsRead[bytes], SupportsNoArgReadline[bytes], Protocol): ... + +class _RequestCacheControlDict(TypedDict, total=False): + max_stale: int + min_stale: int + only_if_cached: bool + no_cache: Literal[True] | str + no_store: bool + no_transform: bool + max_age: int + +class _FieldStorageWithFile(FieldStorage): + file: IO[bytes] + filename: str + +class _NoDefault: ... + +NoDefault: _NoDefault + +class BaseRequest: + request_body_tempfile_limit: ClassVar[int] + environ: WSGIEnvironment + method: _HTTPMethod + def __init__(self, environ: WSGIEnvironment, **kw: Any) -> None: ... + def encget(self, key: str, default: Any = ..., encattr: str | None = None) -> Any: ... + def encset(self, key: str, val: Any, encattr: str | None = None) -> None: ... + @property + def charset(self) -> str | None: ... + def decode(self, charset: str | None = None, errors: str = "strict") -> Self: ... + @property + 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] + 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 + upath_info = path_info # bw compat + content_type: str | None + headers: _AsymmetricProperty[EnvironHeaders, SupportsItems[str, str] | Iterable[tuple[str, str]]] + @property + def client_addr(self) -> str | None: ... + @property + def host_port(self) -> str: ... + @property + def host_url(self) -> str: ... + @property + def application_url(self) -> str: ... + @property + def path_url(self) -> str: ... + @property + def path(self) -> str: ... + @property + def path_qs(self) -> str: ... + @property + def url(self) -> str: ... + 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] + @property + def is_xhr(self) -> bool: ... + host: str + @property + def domain(self) -> str: ... + body: bytes + json: Any + json_body: Any + text: 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]]] + def copy(self) -> Self: ... + def copy_get(self) -> Self: ... + @property + def is_body_readable(self) -> bool: ... + @is_body_readable.setter + 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 remove_conditional_headers( + self, remove_encoding: bool = True, remove_range: bool = True, remove_match: bool = True, remove_modified: bool = True + ) -> None: ... + accept: _AcceptProperty + 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 + ] + 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 + ] + max_forwards: int | None + pragma: str | None + range: _AsymmetricPropertyWithDelete[Range, tuple[int, int | None] | list[int | None] | str | None] + referer: str | None + referrer: str | None + user_agent: str | None + def as_bytes(self, skip_body: bool = False) -> bytes: ... + def as_text(self) -> str: ... + @classmethod + def from_bytes(cls, b: bytes) -> Self: ... + @classmethod + def from_text(cls, s: str) -> Self: ... + @classmethod + def from_file(cls, fp: _SupportsReadAndNoArgReadline) -> Self: ... + @overload + def call_application( + self, application: WSGIApplication, catch_exc_info: Literal[False] = False + ) -> tuple[str, list[_HTTPHeader], Iterable[bytes]]: ... + @overload + def call_application( + self, application: WSGIApplication, catch_exc_info: Literal[True] + ) -> tuple[str, list[_HTTPHeader], Iterable[bytes], ExcInfo | None]: ... + 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: ... + @classmethod + def blank( + cls, + path: str, + environ: dict[str, None] | None = None, + base_url: str | None = None, + headers: Mapping[str, str] | None = None, + POST: str | bytes | Mapping[Any, Any] | Mapping[Any, _ListOrTuple[Any]] | None = None, + **kw, + ) -> Self: ... + +class LegacyRequest(BaseRequest): + uscript_name: Incomplete + upath_info: Incomplete + def encget(self, key, default=..., encattr: Incomplete | None = None): ... + +class AdhocAttrMixin: + def __setattr__(self, attr: str, value: Any) -> None: ... + def __getattr__(self, attr: str) -> Any: ... + def __delattr__(self, attr: str) -> None: ... + +class Request(AdhocAttrMixin, BaseRequest): ... +class DisconnectionError(IOError): ... + +def environ_from_url(path: str) -> WSGIEnvironment: ... +def environ_add_POST( + env: WSGIEnvironment, + data: str | bytes | Mapping[Any, Any] | Mapping[Any, _ListOrTuple[Any]] | None, + content_type: str | None = None, +) -> None: ... + +class LimitedLengthFile(io.RawIOBase): + file: SupportsRead[bytes] + maxlen: int + remaining: int + def __init__(self, file: SupportsRead[bytes], maxlen: int) -> None: ... + @staticmethod + def readable() -> Literal[True]: ... + def readinto(self, buff: ReadableBuffer) -> 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: ... diff --git a/stubs/WebOb/webob/response.pyi b/stubs/WebOb/webob/response.pyi new file mode 100644 index 000000000..7b0ed4d98 --- /dev/null +++ b/stubs/WebOb/webob/response.pyi @@ -0,0 +1,179 @@ +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, Protocol +from typing_extensions import Literal, TypeAlias, TypedDict + +from webob.byterange import ContentRange +from webob.cachecontrol import _ResponseCacheControl +from webob.descriptors import _AsymmetricProperty, _AsymmetricPropertyWithDelete, _authorization, _DateProperty, _ListProperty +from webob.headers import ResponseHeaders +from webob.request import Request + +class _ResponseCacheExpires(Protocol): + def __call__( + self, + seconds: int | timedelta = 0, + *, + public: bool = ..., + private: Literal[True] | str = ..., + no_cache: Literal[True] | str = ..., + no_store: bool = ..., + no_transform: bool = ..., + must_revalidate: bool = ..., + proxy_revalidate: bool = ..., + max_age: int = ..., + s_maxage: int = ..., + s_max_age: int = ..., + stale_while_revalidate: int = ..., + stale_if_error: int = ..., + ) -> None: ... + +class _ResponseCacheControlDict(TypedDict, total=False): + public: bool + private: Literal[True] | str + no_cache: Literal[True] | str + no_store: bool + no_transform: bool + must_revalidate: bool + proxy_revalidate: bool + max_age: int + s_maxage: int + s_max_age: int + stale_while_revalidate: int + stale_if_error: int + +_HTTPHeader: TypeAlias = tuple[str, str] +_ContentRangeParams: TypeAlias = ( + ContentRange + | list[int | None] + | tuple[int, int] + | tuple[None, None] + | tuple[int, int, int | None] + | tuple[None, None, int | None] + | str + | None +) + +class Response: + default_content_type: str + default_charset: str + unicode_errors: str + default_conditional_response: bool + default_body_encoding: str + request: Request | None + environ: WSGIEnvironment | None + status: str + 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, + 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 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 + @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] + 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] + date: _DateProperty + expires: _DateProperty + last_modified: _DateProperty + 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 + retry_after: _DateProperty + server: 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] + def set_cookie( + self, + name: str, + value: str | None = "", + max_age: int | timedelta | None = None, + path: str = "/", + domain: str | None = None, + secure: bool = False, + httponly: bool = False, + comment: str | None = None, + overwrite: bool = False, + samesite: Literal["strict", "lax", "none"] | 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 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: ... + def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: ... + def conditional_response_app(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: ... + def app_iter_range(self, start: int, stop: int | None) -> AppIterRange: ... + def __str__(self, skip_body: bool = False) -> str: ... + +class ResponseBodyFile: + mode: Literal["wb"] + closed: Literal[False] + response: Response + def __init__(self, response: Response): ... + @property + def encoding(self) -> str | None: ... + def write(self, text: str | bytes) -> int: ... + def writelines(self, seq: Sequence[str | bytes]) -> int: ... + def flush(self) -> None: ... + def tell(self) -> int: ... + +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 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 next(self) -> bytes: ... + __next__ = next diff --git a/stubs/WebOb/webob/static.pyi b/stubs/WebOb/webob/static.pyi new file mode 100644 index 000000000..0f9e087dc --- /dev/null +++ b/stubs/WebOb/webob/static.pyi @@ -0,0 +1,37 @@ +from _typeshed import StrOrBytesPath +from collections.abc import Iterator +from typing import IO, Any + +from webob.dec import wsgify +from webob.request import Request +from webob.response import Response + +BLOCK_SIZE: int + +class FileApp: + filename: StrOrBytesPath + kw: dict[str, Any] + def __init__(self, filename: StrOrBytesPath, **kw: Any) -> None: ... + @wsgify + def __call__(self, req: Request) -> Response: ... + +class FileIter: + file: IO[bytes] + def __init__(self, file: IO[bytes]) -> None: ... + def app_iter_range( + self, seek: int | None = None, limit: int | None = None, block_size: int | None = None + ) -> Iterator[bytes]: ... + __iter__ = app_iter_range + +class DirectoryApp: + path: str | bytes + index_page: str | None + 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 + ) -> None: ... + def make_fileapp(self, path: StrOrBytesPath) -> FileApp: ... + @wsgify + def __call__(self, req: Request) -> Response | FileApp: ... + def index(self, req: Request, path: StrOrBytesPath) -> Response | FileApp: ... diff --git a/stubs/WebOb/webob/util.pyi b/stubs/WebOb/webob/util.pyi new file mode 100644 index 000000000..ea8ccec42 --- /dev/null +++ b/stubs/WebOb/webob/util.pyi @@ -0,0 +1,14 @@ +from collections.abc import Callable +from typing import AnyStr, Protocol + +class _HasHTML(Protocol): + def __html__(self) -> str: ... + +def html_escape(s: str | bytes | _HasHTML) -> str: ... +def header_docstring(header, rfc_section): ... +def warn_deprecation(text, version, stacklevel) -> None: ... + +status_reasons: dict[int, str] +status_generic_reasons: dict[int, str] + +def strings_differ(string1: AnyStr, string2: AnyStr, compare_digest: Callable[[AnyStr, AnyStr], bool] = ...) -> bool: ...