WebOb: Fix various issues and refactor some things (#13487)

This commit is contained in:
David Salvisberg
2025-02-25 12:38:27 +01:00
committed by GitHub
parent af32625bd4
commit c4d7283c75
22 changed files with 1258 additions and 754 deletions
+34 -46
View File
@@ -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
@@ -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])
@@ -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