WebOb: Complete the stubs and activate stricter pyright config (#11460)

This commit is contained in:
David Salvisberg
2024-02-29 07:57:50 +01:00
committed by GitHub
parent 3a06fc7c1a
commit fa164b2419
13 changed files with 448 additions and 113 deletions

View File

@@ -78,7 +78,6 @@
"stubs/tqdm",
"stubs/ttkthemes",
"stubs/vobject",
"stubs/WebOb",
"stubs/workalendar",
],
"typeCheckingMode": "strict",

View File

@@ -137,6 +137,17 @@ webob.request.AdhocAttrMixin.__setattr__
# make sense to annotate them and pretend they're part of the API.
webob.request.BaseRequest.__init__
# We needed to add a dummy *_: _P.args in order to support ParamSpec
webob.dec.wsgify.middleware
webob.dec._MiddlewareFactory.__call__
# We renamed some of the arguments in positional only overloads for greater
# clarity about what the arguments mean, stubtest should probably be a bit
# more lenient here, since this is only unsafe if that overload accepts
# arbitrary named arguments, that could overlap with the argument in that
# specific position
webob.dec.wsgify.__call__
# Error: is not present at runtime
# =============================
# This attribute is there to help mypy type narrow NoVars based on its static

View File

@@ -0,0 +1,121 @@
from __future__ import annotations
from _typeshed.wsgi import StartResponse, WSGIApplication, WSGIEnvironment
from collections.abc import Iterable # noqa: F401
from typing_extensions import assert_type
from webob.dec import _AnyResponse, wsgify
from webob.request import Request
class App:
@wsgify
def __call__(self, request: Request) -> str:
return "hello"
env: WSGIEnvironment = {}
start_response: StartResponse = lambda x, y, z=None: lambda b: None
application: WSGIApplication = lambda e, s: [b""]
request: Request = Request(env)
x = App()
# since we wsgified our __call__ we should now be a valid WSGIApplication
application = x
assert_type(x(env, start_response), "Iterable[bytes]")
# currently we lose the exact response type, but that should be fine in
# most use-cases, since middlewares operate on an application level, not
# on these raw intermediary functions
assert_type(x(request), _AnyResponse)
# accessing the method from the class should work as you expect it to
assert_type(App.__call__(x, env, start_response), "Iterable[bytes]")
assert_type(App.__call__(x, request), _AnyResponse)
# but we can also wrap it with a middleware that expects to deal with requests
class Middleware:
@wsgify.middleware
def restrict_ip(self, req: Request, app: WSGIApplication, ips: list[str]) -> WSGIApplication:
return app
__call__ = restrict_ip(x, ips=["127.0.0.1"])
# and we still end up with a valid WSGIApplication
m = Middleware()
application = m
assert_type(m(env, start_response), "Iterable[bytes]")
assert_type(m(request), _AnyResponse)
# the same should work with plain functions
@wsgify
def app(request: Request) -> str:
return "hello"
application = app
assert_type(app, "wsgify[Request, []]")
assert_type(app(env, start_response), "Iterable[bytes]")
assert_type(app(request), _AnyResponse)
# FIXME: For some reason pyright complains here with
# mismatch: expected "wsgify[Request, ()]" but received "wsgify[Request, ()]"
# can you spot the difference?
# assert_type(app(application), "wsgify[Request, []]")
application = app(application)
@wsgify.middleware
def restrict_ip(req: Request, app: WSGIApplication, ips: list[str]) -> WSGIApplication:
return app
@restrict_ip(ips=["127.0.0.1"])
@wsgify
def m_app(request: Request) -> str:
return "hello"
application = m_app
# FIXME: same weird pyright error where it complains about the types
# being the same
# assert_type(m_app, "wsgify[Request, [WSGIApplication]]")
assert_type(m_app(env, start_response), "Iterable[bytes]")
assert_type(m_app(request), _AnyResponse)
# FIXME: and also here
# assert_type(m_app(application), "wsgify[Request, [WSGIApplication]]")
application = m_app(application)
# custom request
class MyRequest(Request):
pass
@wsgify(RequestClass=MyRequest)
def my_request_app(request: MyRequest) -> None:
pass
application = my_request_app
assert_type(my_request_app, "wsgify[MyRequest, []]")
# we are allowed to accept a less specific request class
@wsgify(RequestClass=MyRequest)
def valid_request_app(request: Request) -> None:
pass
# but the opposite is not allowed
@wsgify # type:ignore
def invalid_request_app(request: MyRequest) -> None:
pass
# we can't really make passing extra arguments directly work
# otherwise we have to give up most of our type safety for
# something that should only be used through wsgify.middleware
wsgify(args=(1,)) # type:ignore
wsgify(kwargs={"ips": ["127.0.0.1"]}) # type:ignore

View File

@@ -6,23 +6,23 @@ class Range:
start: int | None
end: int | None
@overload
def __init__(self, start: None, end: None): ...
def __init__(self, start: None, end: None) -> None: ...
@overload
def __init__(self, start: int, end: int | None): ...
def __init__(self, start: int, end: int | None) -> 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: ...
def parse(cls, header: str | None) -> Self | None: ...
class ContentRange:
start: int | None
stop: int | None
length: int | None
@overload
def __init__(self, start: None, stop: None, length: int | None): ...
def __init__(self, start: None, stop: None, length: int | None) -> None: ...
@overload
def __init__(self, start: int, stop: int, length: int | None): ...
def __init__(self, start: int, stop: int, length: int | None) -> None: ...
def __iter__(self) -> Iterator[int | None]: ...
@classmethod
def parse(cls, value: str | None) -> Self: ...
def parse(cls, value: str | None) -> Self | None: ...

View File

@@ -18,7 +18,7 @@ class UpdateDict(dict[_KT, _VT]):
class exists_property:
prop: str
type: str | None
def __init__(self, prop: str, type: str | None = None): ...
def __init__(self, prop: str, type: str | None = None) -> None: ...
@overload
def __get__(self, obj: None, type: _Type | None = None) -> Self: ...
@overload
@@ -32,9 +32,9 @@ class value_property(Generic[_T, _NoneLiteral]):
none: _NoneLiteral
type: str | None
@overload
def __init__(self, prop: str, default: None = None, none: None = None, type: str | None = None): ...
def __init__(self, prop: str, default: None = None, none: None = None, type: str | None = None) -> None: ...
@overload
def __init__(self, prop: str, default: _T, none: _NoneLiteral, type: str | None = None): ...
def __init__(self, prop: str, default: _T, none: _NoneLiteral, type: str | None = None) -> None: ...
@overload
def __get__(self, obj: None, type: _Type | None = None) -> Self: ...
@overload

View File

@@ -1,10 +1,10 @@
from _typeshed.wsgi import StartResponse, WSGIEnvironment
from collections.abc import Iterable
from http.client import HTTPMessage
from http.client import HTTPConnection, HTTPMessage, HTTPSConnection
from typing import ClassVar
class SendRequest:
def __init__(self, HTTPConnection=..., HTTPSConnection=...) -> None: ...
def __init__(self, HTTPConnection: type[HTTPConnection] = ..., HTTPSConnection: type[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]]: ...

View File

@@ -1,4 +1,5 @@
from datetime import datetime, timedelta, tzinfo
from datetime import date, datetime, timedelta, tzinfo
from time import _TimeTuple, struct_time
class _UTC(tzinfo):
def dst(self, dt: datetime | None) -> timedelta: ...
@@ -17,7 +18,7 @@ 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: ...
def parse_date(value: str | bytes | None) -> datetime | None: ...
def serialize_date(dt: datetime | date | timedelta | _TimeTuple | struct_time | float | str | bytes) -> str: ...
def parse_date_delta(value: str | bytes | None) -> datetime | None: ...
def serialize_date_delta(value: datetime | date | timedelta | _TimeTuple | struct_time | float | str | bytes) -> str: ...

View File

@@ -1,49 +1,196 @@
from _typeshed import Incomplete
from _typeshed.wsgi import WSGIApplication
from typing import ClassVar, overload
from typing_extensions import Self
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 webob.request import Request
from webob.response import Response
class wsgify:
RequestClass: ClassVar[type[Request]]
func: Incomplete
args: Incomplete
kwargs: Incomplete
middleware_wraps: Incomplete
_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)
_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]
)
class wsgify(Generic[_RequestT_contra, _P]):
RequestClass: type[Request]
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
# middleware decorator, where we can be more type safe
@overload
def __init__(
self,
func: Incomplete | None = None,
RequestClass: Incomplete | None = None,
args=...,
kwargs: Incomplete | None = None,
middleware_wraps: Incomplete | None = None,
self: wsgify[Request, []],
func: _RequestHandler[Request, []] | None = None,
RequestClass: None = None,
args: tuple[()] = (),
kwargs: None = None,
middleware_wraps: None = None,
) -> None: ...
@overload
def __get__(self, obj: None, type: type | None = None) -> Self: ...
def __init__(
self: wsgify[_RequestT_contra, []],
func: _RequestHandler[_RequestT_contra, []] | None,
RequestClass: type[_RequestT_contra],
args: tuple[()] = (),
kwargs: None = None,
middleware_wraps: None = None,
) -> None: ...
@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): ...
def __init__(
self: wsgify[_RequestT_contra, []],
func: _RequestHandler[_RequestT_contra, []] | None = None,
*,
RequestClass: type[_RequestT_contra],
args: tuple[()] = (),
kwargs: None = None,
middleware_wraps: None = None,
) -> None: ...
@overload
def __init__(
self: wsgify[Request, [_AppT_contra]],
func: _Middleware[Request, _AppT_contra, []] | None = None,
RequestClass: None = None,
args: tuple[()] = (),
kwargs: None = None,
*,
middleware_wraps: _AppT_contra,
) -> None: ...
@overload
def __init__(
self: wsgify[_RequestT_contra, [_AppT_contra]],
func: _Middleware[_RequestT_contra, _AppT_contra, []] | None,
RequestClass: type[_RequestT_contra],
args: tuple[()] = (),
kwargs: None = None,
*,
middleware_wraps: _AppT_contra,
) -> None: ...
@overload
def __init__(
self: wsgify[_RequestT_contra, [_AppT_contra]],
func: _Middleware[_RequestT_contra, _AppT_contra, []] | None = None,
*,
RequestClass: type[_RequestT_contra],
args: tuple[()] = (),
kwargs: None = None,
middleware_wraps: _AppT_contra,
) -> None: ...
@overload
def __get__(self, obj: None, type: type[_S]) -> _unbound_wsgify[_RequestT_contra, _P, _S]: ...
@overload
def __get__(self, obj: object, type: type | None = None) -> Self: ...
@overload
def __call__(self, env: WSGIEnvironment, /, start_response: StartResponse) -> Iterable[bytes]: ...
@overload
def __call__(self, func: _RequestHandler[_RequestT_contra, _P], /) -> Self: ...
@overload
def __call__(self, req: _RequestT_contra) -> _AnyResponse: ...
@overload
def __call__(self, req: _RequestT_contra, *args: _P.args, **kw: _P.kwargs) -> _AnyResponse: ...
def get(self, url: str, **kw: Any) -> _AnyResponse: ...
def post(
self, url: str, POST: str | bytes | Mapping[Any, Any] | Mapping[Any, list[Any] | tuple[Any, ...]] | None = None, **kw: Any
) -> _AnyResponse: ...
def request(self, url: str, **kw: Any) -> _AnyResponse: ...
def call_func(self, req: _RequestT_contra, *args: _P.args, **kwargs: _P.kwargs) -> _AnyResponse: ...
# technically this could bind different type vars, but we disallow it for safety
def clone(self, func: _RequestHandler[_RequestT_contra, _P] | None = None, **kw: Never) -> Self: ...
@property
def undecorated(self): ...
def undecorated(self) -> _RequestHandler[_RequestT_contra, _P] | None: ...
@overload
@classmethod
def middleware(cls, middle_func: Incomplete | None = None, app: Incomplete | None = None, **kw): ...
def middleware(
cls, middle_func: None = None, app: None | _AppT = None, *_: _P.args, **kw: _P.kwargs
) -> _UnboundMiddleware[Any, _AppT, _P]: ...
@overload
@classmethod
def middleware(
cls, middle_func: _MiddlewareCallable[_RequestT, _AppT, _P2], app: None = None
) -> _MiddlewareFactory[_RequestT, _AppT, _P2]: ...
@overload
@classmethod
def middleware(
cls, middle_func: _MiddlewareMethod[_RequestT, _AppT, _P2], app: None = None
) -> _MiddlewareFactory[_RequestT, _AppT, _P2]: ...
@overload
@classmethod
def middleware(
cls, middle_func: _MiddlewareMethod[_RequestT, _AppT, _P2], app: None = None, *_: _P2.args, **kw: _P2.kwargs
) -> _MiddlewareFactory[_RequestT, _AppT, _P2]: ...
@overload
@classmethod
def middleware(
cls, middle_func: _MiddlewareMethod[_RequestT, _AppT, _P2], app: _AppT
) -> type[wsgify[_RequestT, Concatenate[_AppT, _P2]]]: ...
@overload
@classmethod
def middleware(
cls, middle_func: _MiddlewareMethod[_RequestT, _AppT, _P2], app: _AppT, *_: _P2.args, **kw: _P2.kwargs
) -> type[wsgify[_RequestT, Concatenate[_AppT, _P2]]]: ...
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 _unbound_wsgify(wsgify[_RequestT_contra, _P], Generic[_RequestT_contra, _P, _S]):
@overload # type: ignore[override]
def __call__(self, __self: _S, env: WSGIEnvironment, /, start_response: StartResponse) -> Iterable[bytes]: ...
@overload
def __call__(self, __self: _S, func: _RequestHandler[_RequestT_contra, _P], /) -> Self: ...
@overload
def __call__(self, __self: _S, req: _RequestT_contra) -> _AnyResponse: ...
@overload
def __call__(self, __self: _S, req: _RequestT_contra, *args: _P.args, **kw: _P.kwargs) -> _AnyResponse: ...
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): ...
class _UnboundMiddleware(Generic[_RequestT_contra, _AppT_contra, _P]):
wrapper_class: type[wsgify[_RequestT_contra, Concatenate[_AppT_contra, _P]]]
app: _AppT_contra | None
kw: dict[str, Any]
def __init__(
self,
wrapper_class: type[wsgify[_RequestT_contra, Concatenate[_AppT_contra, _P]]],
app: _AppT_contra | None,
kw: dict[str, Any],
) -> None: ...
@overload
def __call__(self, func: None, app: _AppT_contra | None = None) -> Self: ...
@overload
def __call__(
self, func: _Middleware[_RequestT_contra, _AppT_contra, _P], app: None = None
) -> wsgify[_RequestT_contra, Concatenate[_AppT_contra, _P]]: ...
@overload
def __call__(
self, func: _Middleware[_RequestT_contra, _AppT_contra, _P], app: _AppT_contra
) -> wsgify[_RequestT_contra, Concatenate[_AppT_contra, _P]]: ...
class _MiddlewareFactory(Generic[_RequestT_contra, _AppT_contra, _P]):
wrapper_class: type[wsgify[_RequestT_contra, Concatenate[_AppT_contra, _P]]]
middleware: _Middleware[_RequestT_contra, _AppT_contra, _P]
kw: dict[str, Any]
def __init__(
self,
wrapper_class: type[wsgify[_RequestT_contra, Concatenate[_AppT_contra, _P]]],
middleware: _Middleware[_RequestT_contra, _AppT_contra, _P],
kw: dict[str, Any],
) -> None: ...
# NOTE: Technically you are not allowed to pass args, but we give up all kinds
# of other safety if we don't use ParamSpec
@overload
def __call__(
self, app: None = None, *_: _P.args, **config: _P.kwargs
) -> _MiddlewareFactory[_RequestT_contra, _AppT_contra, []]: ...
@overload
def __call__(self, app: _AppT_contra, *_: _P.args, **config: _P.kwargs) -> wsgify[_RequestT_contra, [_AppT_contra]]: ...

View File

@@ -1,11 +1,30 @@
from _typeshed import Incomplete
from collections.abc import Iterable
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_extensions import TypeAlias
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")
_ContentRangeParams: TypeAlias = (
ContentRange
| list[int]
| list[None]
| list[int | None]
| tuple[int, int]
| tuple[None, None]
| tuple[int, int, int | None]
| tuple[None, None, int | None]
| str
| None
)
class _AsymmetricProperty(Generic[_GetterReturnType, _SetterValueType]):
@overload
@@ -17,36 +36,67 @@ class _AsymmetricProperty(Generic[_GetterReturnType, _SetterValueType]):
class _AsymmetricPropertyWithDelete(_AsymmetricProperty[_GetterReturnType, _SetterValueType]):
def __delete__(self, __obj: Any) -> None: ...
class _StringProperty(_AsymmetricPropertyWithDelete[str | None, str | 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]
): ...
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): ...
@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]: ...
@overload
def environ_getter(key: str, *, rfc_section: str | None = None) -> _SymmetricProperty[Any]: ...
@overload
def environ_decoder(
key: str, default: str, rfc_section: str | None = None, encattr: str | None = 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: ...
def header_getter(header: str, rfc_section: str) -> _StringProperty: ...
def converter(prop, parse, serialize, convert_name: Incomplete | None = None): ...
@overload
def converter(
prop: _AsymmetricPropertyWithDelete[_GetterReturnType, _SetterValueType],
parse: Callable[[_GetterReturnType], _ConvertedGetterReturnType],
serialize: Callable[[_ConvertedSetterValueType], _SetterValueType],
convert_name: str | None = None,
) -> _AsymmetricPropertyWithDelete[_ConvertedGetterReturnType, _ConvertedSetterValueType]: ...
@overload
def converter(
prop: _AsymmetricProperty[_GetterReturnType, _SetterValueType],
parse: Callable[[_GetterReturnType], _ConvertedGetterReturnType],
serialize: Callable[[_ConvertedSetterValueType], _SetterValueType],
convert_name: str | None = None,
) -> _AsymmetricProperty[_ConvertedGetterReturnType, _ConvertedSetterValueType]: ...
def list_header(header: str, rfc_section: str) -> _ListProperty: ...
def parse_list(value): ...
def serialize_list(value): ...
def converter_date(prop): ...
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, 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): ...
def parse_etag_response(value: str | None, strong: bool = False) -> str | None: ...
def serialize_etag_response(value: str | tuple[str, bool]) -> 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: ...
def parse_int(value: str | None) -> int | None: ...
def parse_int_safe(value: str | None) -> int | None: ...
serialize_int = str
serialize_int: Callable[[int], str]
def parse_content_range(value): ...
def serialize_content_range(value): ...
def parse_auth_params(params): ...
def parse_content_range(value: str | None) -> ContentRange | None: ...
def serialize_content_range(value: _ContentRangeParams) -> str | None: ...
def parse_auth_params(params: str) -> dict[str, str]: ...
known_auth_schemes: dict[str, None]
@@ -54,5 +104,5 @@ class _authorization(NamedTuple):
authtype: str
params: dict[str, str] | str
def parse_auth(val): ...
def serialize_auth(val): ...
def parse_auth(val: str | None) -> _authorization | None: ...
def serialize_auth(val: tuple[str, dict[str, str] | str] | list[Any] | str | None) -> str | None: ...

View File

@@ -11,9 +11,9 @@ _VT = TypeVar("_VT")
class MultiDict(MutableMapping[_KT, _VT]):
@overload
def __init__(self, __m: SupportsItems[_KT, _VT], **kwargs: _VT): ...
def __init__(self, __m: SupportsItems[_KT, _VT], **kwargs: _VT) -> None: ...
@overload
def __init__(self, __m: Iterable[tuple[_KT, _VT]], **kwargs: _VT): ...
def __init__(self, __m: Iterable[tuple[_KT, _VT]], **kwargs: _VT) -> None: ...
@overload
def __init__(self, **kwargs: _VT) -> None: ...
@classmethod

View File

@@ -1,14 +1,6 @@
import datetime
import io
from _typeshed import (
ExcInfo,
Incomplete,
ReadableBuffer,
SupportsItems,
SupportsKeysAndGetItem,
SupportsNoArgReadline,
SupportsRead,
)
from _typeshed import ExcInfo, ReadableBuffer, SupportsItems, SupportsKeysAndGetItem, SupportsNoArgReadline, SupportsRead
from _typeshed.wsgi import WSGIApplication, WSGIEnvironment
from cgi import FieldStorage
from collections.abc import Iterable, Mapping
@@ -55,8 +47,11 @@ class BaseRequest:
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: ...
@overload
def encget(self, key: str, default: _T, encattr: str | None = None) -> str | _T: ...
@overload
def encget(self, key: str, *, encattr: str | None = None) -> str: ...
def encset(self, key: str, val: str, encattr: str | None = None) -> None: ...
@property
def charset(self) -> str | None: ...
def decode(self, charset: str | None = None, errors: str = "strict") -> Self: ...
@@ -96,7 +91,10 @@ class BaseRequest:
@path_info.setter
def path_info(self, value: str | None) -> None: ...
uscript_name: str # bw compat
upath_info = path_info # 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]]]
@property
@@ -166,7 +164,7 @@ class BaseRequest:
]
max_forwards: int | None
pragma: str | None
range: _AsymmetricPropertyWithDelete[Range, tuple[int, int | None] | list[int | None] | 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
@@ -198,20 +196,31 @@ class BaseRequest:
base_url: str | None = None,
headers: Mapping[str, str] | None = None,
POST: str | bytes | Mapping[Any, Any] | Mapping[Any, _ListOrTuple[Any]] | None = None,
**kw,
**kw: Any,
) -> Self: ...
class LegacyRequest(BaseRequest):
uscript_name: Incomplete
upath_info: Incomplete
def encget(self, key, default=..., encattr: Incomplete | None = None): ...
@property
def uscript_name(self) -> str: ...
@uscript_name.setter
def uscript_name(self, value: str) -> None: ...
@property # type:ignore[override]
def upath_info(self) -> str: ...
@upath_info.setter
def upath_info(self, value: str) -> None: ...
def encget(self, key: str, default: Any = ..., encattr: str | None = None) -> Any: ...
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 Request(AdhocAttrMixin, BaseRequest):
# this is so Request doesn't count as callable, it's not very pretty
# but we run into trouble with overlapping overloads in wsgify if we
# don't exclude __call__ from arbitrary attribute access
__call__: None
class DisconnectionError(IOError): ...
def environ_from_url(path: str) -> WSGIEnvironment: ...

View File

@@ -8,7 +8,14 @@ from typing_extensions import TypeAlias
from webob.byterange import ContentRange
from webob.cachecontrol import _ResponseCacheControl
from webob.cookies import _SameSitePolicy
from webob.descriptors import _AsymmetricProperty, _AsymmetricPropertyWithDelete, _authorization, _DateProperty, _ListProperty
from webob.descriptors import (
_AsymmetricProperty,
_AsymmetricPropertyWithDelete,
_authorization,
_ContentRangeParams,
_DateProperty,
_ListProperty,
)
from webob.headers import ResponseHeaders
from webob.request import Request
@@ -46,16 +53,6 @@ class _ResponseCacheControlDict(TypedDict, total=False):
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
@@ -154,7 +151,7 @@ class ResponseBodyFile:
mode: Literal["wb"]
closed: Literal[False]
response: Response
def __init__(self, response: Response): ...
def __init__(self, response: Response) -> None: ...
@property
def encoding(self) -> str | None: ...
def write(self, text: str | bytes) -> int: ...

View File

@@ -5,8 +5,8 @@ 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: ...
def header_docstring(header: str, rfc_section: str) -> str: ...
def warn_deprecation(text: str, version: str, stacklevel: int) -> None: ...
status_reasons: dict[int, str]
status_generic_reasons: dict[int, str]