diff --git a/stubs/WebOb/webob/cookies.pyi b/stubs/WebOb/webob/cookies.pyi index f0bfb5830..7104a281d 100644 --- a/stubs/WebOb/webob/cookies.pyi +++ b/stubs/WebOb/webob/cookies.pyi @@ -1,13 +1,24 @@ -from _typeshed import Incomplete +from _typeshed import sentinel from _typeshed.wsgi import WSGIEnvironment -from collections.abc import ItemsView, Iterator, KeysView, MutableMapping, ValuesView +from collections.abc import Callable, Collection, ItemsView, Iterator, KeysView, MutableMapping, ValuesView from datetime import date, datetime, timedelta -from typing import TypeVar, overload -from typing_extensions import Literal +from hashlib import _Hash +from typing import Any, Protocol, TypeVar, overload +from typing_extensions import Literal, TypeAlias from webob.descriptors import _AsymmetricProperty +from webob.request import Request +from webob.response import Response _T = TypeVar("_T") +# we accept both the official spelling and the one used in the WebOb docs +# the implementation compares after lower() so technically there are more +# valid spellings, but it seems mor natural to support these two spellings +_SameSitePolicy: TypeAlias = Literal["Strict", "Lax", "None", "strict", "lax", "none"] + +class _Serializer(Protocol): + def loads(self, __appstruct: Any) -> bytes: ... + def dumps(self, __bstruct: bytes) -> Any: ... class RequestCookies(MutableMapping[str, str]): def __init__(self, environ: WSGIEnvironment) -> None: ... @@ -61,7 +72,7 @@ class Morsel(dict[bytes, bytes | bool | None]): def secure(self) -> bool | None: ... @secure.setter def secure(self, v: bool) -> None: ... - samesite: _AsymmetricProperty[bytes | None, Literal["strict", "lax", "none"] | None] + samesite: _AsymmetricProperty[bytes | None, _SameSitePolicy | None] def serialize(self, full: bool = True) -> str: ... def __str__(self, full: bool = True) -> str: ... @@ -74,75 +85,98 @@ def make_cookie( secure: bool = False, httponly: bool = False, comment: str | None = None, - samesite: Literal["strict", "lax", "none"] | None = None, + samesite: _SameSitePolicy | None = None, ) -> str: ... class JSONSerializer: - def dumps(self, appstruct): ... - def loads(self, bstruct): ... + def dumps(self, appstruct: Any) -> bytes: ... + def loads(self, bstruct: bytes | str) -> Any: ... class Base64Serializer: - serializer: Incomplete - def __init__(self, serializer: Incomplete | None = None) -> None: ... - def dumps(self, appstruct): ... - def loads(self, bstruct): ... + serializer: _Serializer + def __init__(self, serializer: _Serializer | None = None) -> None: ... + def dumps(self, appstruct: Any) -> bytes: ... + def loads(self, bstruct: bytes) -> Any: ... 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): ... + salt: str + secret: str + hashalg: str + salted_secret: bytes + digestmod: Callable[[bytes], _Hash] + digest_size: int + serializer: _Serializer + def __init__(self, secret: str, salt: str, hashalg: str = "sha512", serializer: _Serializer | None = None) -> None: ... + def dumps(self, appstruct: Any) -> bytes: ... + def loads(self, bstruct: bytes) -> Any: ... class CookieProfile: - cookie_name: Incomplete - secure: Incomplete - max_age: Incomplete - httponly: Incomplete - samesite: Incomplete - path: Incomplete - domains: Incomplete - serializer: Incomplete - request: Incomplete + cookie_name: str + secure: bool + max_age: int | timedelta | None + httponly: bool | None + samesite: _SameSitePolicy | None + path: str + domains: Collection[str] | None + serializer: _Serializer + request: Request | None def __init__( self, - cookie_name, + cookie_name: str, secure: bool = False, - max_age: Incomplete | None = None, - httponly: Incomplete | None = None, - samesite: Incomplete | None = None, + max_age: int | timedelta | None = None, + httponly: bool | None = None, + samesite: _SameSitePolicy | None = None, path: str = "/", - domains: Incomplete | None = None, - serializer: Incomplete | None = None, + # even though the docs claim any iterable is fine, that is + # clearly not the case judging by the implementation + domains: Collection[str] | None = None, + serializer: _Serializer | 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=...): ... + def __call__(self, request: Request) -> CookieProfile: ... + def bind(self, request: Request) -> CookieProfile: ... + def get_value(self) -> Any | None: ... + def set_cookies( + self, + response: Response, + value: Any, + domains: Collection[str] = sentinel, + max_age: int | timedelta | None = sentinel, + path: str = sentinel, + secure: bool = sentinel, + httponly: bool = sentinel, + samesite: _SameSitePolicy | None = sentinel, + ) -> Response: ... + def get_headers( + self, + value: Any, + domains: Collection[str] = sentinel, + max_age: int | timedelta | None = sentinel, + path: str = sentinel, + secure: bool = sentinel, + httponly: bool = sentinel, + samesite: _SameSitePolicy | None = sentinel, + ) -> list[tuple[str, str]]: ... class SignedCookieProfile(CookieProfile): - secret: Incomplete - salt: Incomplete - hashalg: Incomplete - original_serializer: Incomplete + secret: str + salt: str + hashalg: str + serializer: SignedSerializer + original_serializer: _Serializer def __init__( self, - secret, - salt, - cookie_name, + secret: str, + salt: str, + cookie_name: str, secure: bool = False, - max_age: Incomplete | None = None, + max_age: int | timedelta | None = None, httponly: bool = False, - samesite: Incomplete | None = None, + samesite: _SameSitePolicy | None = None, path: str = "/", - domains: Incomplete | None = None, + domains: Collection[str] | None = None, hashalg: str = "sha512", - serializer: Incomplete | None = None, + serializer: _Serializer | None = None, ) -> None: ... - def bind(self, request): ... + def __call__(self, request: Request) -> SignedCookieProfile: ... + def bind(self, request: Request) -> SignedCookieProfile: ... diff --git a/stubs/WebOb/webob/response.pyi b/stubs/WebOb/webob/response.pyi index 7b0ed4d98..ebd11adba 100644 --- a/stubs/WebOb/webob/response.pyi +++ b/stubs/WebOb/webob/response.pyi @@ -7,6 +7,7 @@ from typing_extensions import Literal, TypeAlias, TypedDict 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.headers import ResponseHeaders from webob.request import Request @@ -134,7 +135,7 @@ class Response: httponly: bool = False, comment: str | None = None, overwrite: bool = False, - samesite: Literal["strict", "lax", "none"] | None = None, + samesite: _SameSitePolicy | None = None, ) -> None: ... def delete_cookie(self, name: str, path: str = "/", domain: str | None = None) -> None: ... def unset_cookie(self, name: str, strict: bool = True) -> None: ...