From 2eb305f4e0353a0b0c65fd1a00dea8498c8a0a29 Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Mon, 20 Jul 2026 14:25:42 +0100 Subject: [PATCH 1/9] Add stubs for HTTPretty --- .../@tests/test_cases/check_usage.py | 17 ++ stubs/httpretty/METADATA.toml | 6 + stubs/httpretty/httpretty/__init__.pyi | 39 ++++ stubs/httpretty/httpretty/compat.pyi | 7 + stubs/httpretty/httpretty/core.pyi | 203 ++++++++++++++++++ stubs/httpretty/httpretty/errors.pyi | 4 + stubs/httpretty/httpretty/http.pyi | 21 ++ stubs/httpretty/httpretty/utils.pyi | 2 + stubs/httpretty/httpretty/version.pyi | 1 + 9 files changed, 300 insertions(+) create mode 100644 stubs/httpretty/@tests/test_cases/check_usage.py create mode 100644 stubs/httpretty/METADATA.toml create mode 100644 stubs/httpretty/httpretty/__init__.pyi create mode 100644 stubs/httpretty/httpretty/compat.pyi create mode 100644 stubs/httpretty/httpretty/core.pyi create mode 100644 stubs/httpretty/httpretty/errors.pyi create mode 100644 stubs/httpretty/httpretty/http.pyi create mode 100644 stubs/httpretty/httpretty/utils.pyi create mode 100644 stubs/httpretty/httpretty/version.pyi diff --git a/stubs/httpretty/@tests/test_cases/check_usage.py b/stubs/httpretty/@tests/test_cases/check_usage.py new file mode 100644 index 000000000000..664d8dfbf7fa --- /dev/null +++ b/stubs/httpretty/@tests/test_cases/check_usage.py @@ -0,0 +1,17 @@ +from typing_extensions import assert_type + +import httpretty + + +@httpretty.activate(allow_net_connect=False) +def decorated() -> int: + httpretty.register_uri(httpretty.GET, "https://example.com/", body="ok", status=200, content_type="text/plain") + assert_type(httpretty.last_request(), httpretty.HTTPrettyRequest | httpretty.HTTPrettyRequestEmpty) + assert_type(httpretty.latest_requests(), list[httpretty.HTTPrettyRequest]) + assert_type(httpretty.has_request(), bool) + return 1 + + +with httpretty.enabled(allow_net_connect=False): + response = httpretty.Response("ok", status=201) + assert_type(response, httpretty.Entry) diff --git a/stubs/httpretty/METADATA.toml b/stubs/httpretty/METADATA.toml new file mode 100644 index 000000000000..84ef9a4c2452 --- /dev/null +++ b/stubs/httpretty/METADATA.toml @@ -0,0 +1,6 @@ +version = "1.1.4" +upstream-repository = "https://github.com/gabrielfalcao/HTTPretty" +partial-stub = true + +[tool.stubtest] +ignore-missing-stub = true diff --git a/stubs/httpretty/httpretty/__init__.pyi b/stubs/httpretty/httpretty/__init__.pyi new file mode 100644 index 000000000000..920463afee4b --- /dev/null +++ b/stubs/httpretty/httpretty/__init__.pyi @@ -0,0 +1,39 @@ +from typing import Literal + +from .core import ( + EmptyRequestHeaders as EmptyRequestHeaders, + Entry as Entry, + HTTPrettyRequest as HTTPrettyRequest, + HTTPrettyRequestEmpty as HTTPrettyRequestEmpty, + URIInfo as URIInfo, + URIMatcher as URIMatcher, + get_default_thread_timeout as get_default_thread_timeout, + httprettified as httprettified, + httprettized as httprettized, + httpretty as httpretty, + set_default_thread_timeout as set_default_thread_timeout, +) +from .errors import HTTPrettyError as HTTPrettyError, UnmockedError as UnmockedError + +__version__: str +HTTPretty = httpretty +activate = httprettified +enabled = httprettized +enable = httpretty.enable +register_uri = httpretty.register_uri +disable = httpretty.disable +is_enabled = httpretty.is_enabled +reset = httpretty.reset +Response = httpretty.Response +GET: Literal["GET"] +PUT: Literal["PUT"] +POST: Literal["POST"] +DELETE: Literal["DELETE"] +HEAD: Literal["HEAD"] +PATCH: Literal["PATCH"] +OPTIONS: Literal["OPTIONS"] +CONNECT: Literal["CONNECT"] + +def last_request() -> HTTPrettyRequest | HTTPrettyRequestEmpty: ... +def latest_requests() -> list[HTTPrettyRequest]: ... +def has_request() -> bool: ... diff --git a/stubs/httpretty/httpretty/compat.pyi b/stubs/httpretty/httpretty/compat.pyi new file mode 100644 index 000000000000..aa9ef01dc4b4 --- /dev/null +++ b/stubs/httpretty/httpretty/compat.pyi @@ -0,0 +1,7 @@ +from typing import TypeVar + +_T = TypeVar("_T") + +class BaseClass: ... + +def encode_obj(in_obj: _T) -> _T: ... diff --git a/stubs/httpretty/httpretty/core.pyi b/stubs/httpretty/httpretty/core.pyi new file mode 100644 index 000000000000..d421bba73e79 --- /dev/null +++ b/stubs/httpretty/httpretty/core.pyi @@ -0,0 +1,203 @@ +import re +from collections.abc import Callable, Iterable, Mapping +from contextlib import AbstractContextManager +from http.client import HTTPMessage +from types import TracebackType +from typing import Any, Literal, TypeAlias, overload +from typing_extensions import ParamSpec + +from .http import HttpBaseClass + +_P = ParamSpec("_P") +_HTTPMethod: TypeAlias = Literal["GET", "PUT", "POST", "DELETE", "HEAD", "PATCH", "OPTIONS", "CONNECT"] +_URI: TypeAlias = str | re.Pattern[str] +_HeaderValue: TypeAlias = str | int | bool | None +_Headers: TypeAlias = Mapping[str, _HeaderValue] +_Body: TypeAlias = str | bytes +_ResponseBody: TypeAlias = _Body | Callable[[HTTPrettyRequest, str, _Headers], tuple[int, _Headers, _Body]] + +def set_default_thread_timeout(timeout: float) -> None: ... +def get_default_thread_timeout() -> float: ... + +class HTTPrettyRequest(HttpBaseClass): + headers: HTTPMessage + raw_headers: str + path: str + querystring: dict[str, list[str]] + parsed_body: Any + created_at: float + def __init__( + self, headers: str | bytes, body: _Body = "", sock: object | None = None, path_encoding: str = "iso-8859-1" + ) -> None: ... + @property + def method(self) -> str: ... + @property + def protocol(self) -> str: ... + + @property + def body(self) -> str: ... + @body.setter + def body(self, value: _Body) -> None: ... + + @property + def url(self) -> str: ... + @property + def host(self) -> str: ... + def parse_querystring(self, qs: str) -> dict[str, list[str]]: ... + def parse_request_body(self, body: str) -> Any: ... + +class EmptyRequestHeaders(dict[str, str]): ... + +class HTTPrettyRequestEmpty: + method: str | None + url: str | None + body: str + headers: EmptyRequestHeaders + +class Entry(HttpBaseClass): + method: _HTTPMethod + uri: str + request: HTTPrettyRequest + body: _Body + status: int + streaming: bool + adding_headers: dict[str, str] + forcing_headers: dict[str, str] + def __init__( + self, + method: str, + uri: str, + body: _ResponseBody, + adding_headers: _Headers | None = None, + forcing_headers: _Headers | None = None, + status: int = 200, + streaming: bool = False, + **headers: str, + ) -> None: ... + def validate(self) -> None: ... + def normalize_headers(self, headers: _Headers) -> dict[str, str]: ... + def fill_filekind(self, fk: Any) -> None: ... + +class URIInfo(HttpBaseClass): + default_str_attrs: tuple[str, ...] + username: str + password: str + hostname: str + port: int + path: str + query: str + scheme: str + fragment: str + last_request: HTTPrettyRequest | None + def __init__( + self, + username: str = "", + password: str = "", + hostname: str = "", + port: int = 80, + path: str = "/", + query: str = "", + fragment: str = "", + scheme: str = "", + last_request: HTTPrettyRequest | None = None, + ) -> None: ... + def to_str(self, attrs: Iterable[str]) -> str: ... + def str_with_query(self) -> str: ... + def full_url(self, use_querystring: bool = True) -> str: ... + def get_full_domain(self) -> str: ... + @classmethod + def from_uri(cls, uri: str, entry: Entry) -> URIInfo: ... + +class URIMatcher: + regex: re.Pattern[str] | None + info: URIInfo | None + entries: list[Entry] + priority: int + uri: _URI + def __init__(self, uri: _URI, entries: Iterable[Entry], match_querystring: bool = False, priority: int = 0) -> None: ... + def matches(self, info: URIInfo) -> bool: ... + def get_next_entry(self, method: _HTTPMethod, info: URIInfo, request: HTTPrettyRequest) -> Entry: ... + +class httpretty(HttpBaseClass): + GET: Literal["GET"] + PUT: Literal["PUT"] + POST: Literal["POST"] + DELETE: Literal["DELETE"] + HEAD: Literal["HEAD"] + PATCH: Literal["PATCH"] + OPTIONS: Literal["OPTIONS"] + CONNECT: Literal["CONNECT"] + METHODS: tuple[_HTTPMethod, ...] + latest_requests: list[HTTPrettyRequest] + last_request: HTTPrettyRequest | HTTPrettyRequestEmpty + allow_net_connect: bool + @classmethod + def match_uriinfo(cls, info: URIInfo) -> tuple[Entry | None, list[str]]: ... + @classmethod + def match_https_hostname(cls, hostname: str) -> bool: ... + @classmethod + def match_http_address(cls, hostname: str, port: int) -> bool: ... + @classmethod + def record( + cls, + filename: str, + indentation: int = 4, + encoding: str = "utf-8", + verbose: bool = False, + allow_net_connect: bool = True, + pool_manager_params: Mapping[str, Any] | None = None, + ) -> AbstractContextManager[None]: ... + @classmethod + def playback(cls, filename: str, allow_net_connect: bool = True, verbose: bool = False) -> AbstractContextManager[None]: ... + @classmethod + def reset(cls) -> None: ... + @classmethod + def historify_request(cls, headers: str | bytes, body: _Body = "", sock: object | None = None) -> HTTPrettyRequest: ... + @classmethod + def register_uri( + cls, + method: str, + uri: _URI, + body: _ResponseBody = '{"message": "HTTPretty :)"}', + adding_headers: _Headers | None = None, + forcing_headers: _Headers | None = None, + status: int = 200, + responses: Iterable[Entry] | None = None, + match_querystring: bool = False, + priority: int = 0, + **headers: str, + ) -> None: ... + @classmethod + def Response( + cls, + body: _ResponseBody, + method: _HTTPMethod | None = None, + uri: str | None = None, + adding_headers: _Headers | None = None, + forcing_headers: _Headers | None = None, + status: int = 200, + streaming: bool = False, + **headers: str, + ) -> Entry: ... + @classmethod + def disable(cls) -> None: ... + @classmethod + def is_enabled(cls) -> bool: ... + @classmethod + def enable(cls, allow_net_connect: bool = True, verbose: bool = False) -> None: ... + +class httprettized: + allow_net_connect: bool + verbose: bool + def __init__(self, allow_net_connect: bool = True, verbose: bool = False) -> None: ... + def __enter__(self) -> None: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None + ) -> None: ... + +@overload +def httprettified(test: Callable[_P, Any]) -> Callable[_P, Any]: ... +@overload +def httprettified( + test: None = None, allow_net_connect: bool = True, verbose: bool = False +) -> Callable[[Callable[_P, Any]], Callable[_P, Any]]: ... diff --git a/stubs/httpretty/httpretty/errors.pyi b/stubs/httpretty/httpretty/errors.pyi new file mode 100644 index 000000000000..4fb6e583a119 --- /dev/null +++ b/stubs/httpretty/httpretty/errors.pyi @@ -0,0 +1,4 @@ +class HTTPrettyError(Exception): ... + +class UnmockedError(HTTPrettyError): + def __init__(self, message: str = ..., request: object | None = None, address: object | None = None) -> None: ... diff --git a/stubs/httpretty/httpretty/http.pyi b/stubs/httpretty/httpretty/http.pyi new file mode 100644 index 000000000000..660b3545fa53 --- /dev/null +++ b/stubs/httpretty/httpretty/http.pyi @@ -0,0 +1,21 @@ +from collections.abc import Sequence +from typing import Literal +from typing_extensions import TypeVar + +_T = TypeVar("_T", str, bytes) + +STATUSES: dict[int, str] + +class HttpBaseClass: + GET: Literal["GET"] + PUT: Literal["PUT"] + POST: Literal["POST"] + DELETE: Literal["DELETE"] + HEAD: Literal["HEAD"] + PATCH: Literal["PATCH"] + OPTIONS: Literal["OPTIONS"] + CONNECT: Literal["CONNECT"] + METHODS: tuple[Literal["GET", "PUT", "POST", "DELETE", "HEAD", "PATCH", "OPTIONS", "CONNECT"], ...] + +def parse_requestline(s: str) -> tuple[str, str, str]: ... +def last_requestline(sent_data: Sequence[_T]) -> _T | None: ... diff --git a/stubs/httpretty/httpretty/utils.pyi b/stubs/httpretty/httpretty/utils.pyi new file mode 100644 index 000000000000..b38062b2ea81 --- /dev/null +++ b/stubs/httpretty/httpretty/utils.pyi @@ -0,0 +1,2 @@ +def utf8(s: str | bytes) -> bytes: ... +def decode_utf8(s: str | bytes) -> str: ... diff --git a/stubs/httpretty/httpretty/version.pyi b/stubs/httpretty/httpretty/version.pyi new file mode 100644 index 000000000000..c2ee2cab489b --- /dev/null +++ b/stubs/httpretty/httpretty/version.pyi @@ -0,0 +1 @@ +version: str From e8a8d37d7a186f56f935a7a3c274c546419e1b91 Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Mon, 20 Jul 2026 20:17:19 +0100 Subject: [PATCH 2/9] Update stubs/httpretty/httpretty/__init__.pyi\ Co-authored-by: Semyon Moroz --- stubs/httpretty/httpretty/__init__.pyi | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/stubs/httpretty/httpretty/__init__.pyi b/stubs/httpretty/httpretty/__init__.pyi index 920463afee4b..10c32a092904 100644 --- a/stubs/httpretty/httpretty/__init__.pyi +++ b/stubs/httpretty/httpretty/__init__.pyi @@ -25,14 +25,14 @@ disable = httpretty.disable is_enabled = httpretty.is_enabled reset = httpretty.reset Response = httpretty.Response -GET: Literal["GET"] -PUT: Literal["PUT"] -POST: Literal["POST"] -DELETE: Literal["DELETE"] -HEAD: Literal["HEAD"] -PATCH: Literal["PATCH"] -OPTIONS: Literal["OPTIONS"] -CONNECT: Literal["CONNECT"] +GET: Final = "GET" +PUT: Final = "PUT" +POST: Final = "POST" +DELETE: Final = "DELETE" +HEAD: Final = "HEAD" +PATCH: Final = "PATCH" +OPTIONS: Final = "OPTIONS" +CONNECT: Final = "CONNECT" def last_request() -> HTTPrettyRequest | HTTPrettyRequestEmpty: ... def latest_requests() -> list[HTTPrettyRequest]: ... From fc10a19b51a7ccf8ddf7ef5a206934252528bd6b Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Mon, 20 Jul 2026 20:17:29 +0100 Subject: [PATCH 3/9] Update stubs/httpretty/httpretty/core.pyi Co-authored-by: Semyon Moroz --- stubs/httpretty/httpretty/core.pyi | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/stubs/httpretty/httpretty/core.pyi b/stubs/httpretty/httpretty/core.pyi index d421bba73e79..a6f52cf9d9c6 100644 --- a/stubs/httpretty/httpretty/core.pyi +++ b/stubs/httpretty/httpretty/core.pyi @@ -119,14 +119,14 @@ class URIMatcher: def get_next_entry(self, method: _HTTPMethod, info: URIInfo, request: HTTPrettyRequest) -> Entry: ... class httpretty(HttpBaseClass): - GET: Literal["GET"] - PUT: Literal["PUT"] - POST: Literal["POST"] - DELETE: Literal["DELETE"] - HEAD: Literal["HEAD"] - PATCH: Literal["PATCH"] - OPTIONS: Literal["OPTIONS"] - CONNECT: Literal["CONNECT"] + GET: Final = "GET" + PUT: Final = "PUT" + POST: Final = "POST" + DELETE: Final = "DELETE" + HEAD: Final = "HEAD" + PATCH: Final = "PATCH" + OPTIONS: Final = "OPTIONS" + CONNECT: Final = "CONNECT" METHODS: tuple[_HTTPMethod, ...] latest_requests: list[HTTPrettyRequest] last_request: HTTPrettyRequest | HTTPrettyRequestEmpty From 4a6c7fecc76d160b80d1618b24acb3bb7993a818 Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Mon, 20 Jul 2026 20:17:39 +0100 Subject: [PATCH 4/9] Update stubs/httpretty/httpretty/http.pyi Co-authored-by: Semyon Moroz --- stubs/httpretty/httpretty/http.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/httpretty/httpretty/http.pyi b/stubs/httpretty/httpretty/http.pyi index 660b3545fa53..0398d13b8377 100644 --- a/stubs/httpretty/httpretty/http.pyi +++ b/stubs/httpretty/httpretty/http.pyi @@ -15,7 +15,7 @@ class HttpBaseClass: PATCH: Literal["PATCH"] OPTIONS: Literal["OPTIONS"] CONNECT: Literal["CONNECT"] - METHODS: tuple[Literal["GET", "PUT", "POST", "DELETE", "HEAD", "PATCH", "OPTIONS", "CONNECT"], ...] + METHODS: tuple[_HTTPMethod, ...] def parse_requestline(s: str) -> tuple[str, str, str]: ... def last_requestline(sent_data: Sequence[_T]) -> _T | None: ... From 323320f3ef64b4cd05b20a1ff67a73c3fed70d0c Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Mon, 20 Jul 2026 20:17:47 +0100 Subject: [PATCH 5/9] Update stubs/httpretty/httpretty/http.pyi Co-authored-by: Semyon Moroz --- stubs/httpretty/httpretty/http.pyi | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/stubs/httpretty/httpretty/http.pyi b/stubs/httpretty/httpretty/http.pyi index 0398d13b8377..38feb43ee6c1 100644 --- a/stubs/httpretty/httpretty/http.pyi +++ b/stubs/httpretty/httpretty/http.pyi @@ -7,14 +7,14 @@ _T = TypeVar("_T", str, bytes) STATUSES: dict[int, str] class HttpBaseClass: - GET: Literal["GET"] - PUT: Literal["PUT"] - POST: Literal["POST"] - DELETE: Literal["DELETE"] - HEAD: Literal["HEAD"] - PATCH: Literal["PATCH"] - OPTIONS: Literal["OPTIONS"] - CONNECT: Literal["CONNECT"] + GET: Final = "GET" + PUT: Final = "PUT" + POST: Final = "POST" + DELETE: Final = "DELETE" + HEAD: Final = "HEAD" + PATCH: Final = "PATCH" + OPTIONS: Final = "OPTIONS" + CONNECT: Final = "CONNECT" METHODS: tuple[_HTTPMethod, ...] def parse_requestline(s: str) -> tuple[str, str, str]: ... From 28ef15512cf51676828d327a28d2977c4b7b53e0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 19:19:20 +0000 Subject: [PATCH 6/9] [pre-commit.ci] auto fixes from pre-commit.com hooks --- stubs/httpretty/httpretty/__init__.pyi | 2 -- 1 file changed, 2 deletions(-) diff --git a/stubs/httpretty/httpretty/__init__.pyi b/stubs/httpretty/httpretty/__init__.pyi index 10c32a092904..1bea66e0886d 100644 --- a/stubs/httpretty/httpretty/__init__.pyi +++ b/stubs/httpretty/httpretty/__init__.pyi @@ -1,5 +1,3 @@ -from typing import Literal - from .core import ( EmptyRequestHeaders as EmptyRequestHeaders, Entry as Entry, From 2e411bff9404d06c7476bd3de865c760ef19133a Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Mon, 20 Jul 2026 20:44:42 +0100 Subject: [PATCH 7/9] Update stubs/httpretty/httpretty/core.pyi Co-authored-by: Semyon Moroz --- stubs/httpretty/httpretty/core.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/httpretty/httpretty/core.pyi b/stubs/httpretty/httpretty/core.pyi index a6f52cf9d9c6..086327b4e642 100644 --- a/stubs/httpretty/httpretty/core.pyi +++ b/stubs/httpretty/httpretty/core.pyi @@ -24,7 +24,7 @@ class HTTPrettyRequest(HttpBaseClass): raw_headers: str path: str querystring: dict[str, list[str]] - parsed_body: Any + parsed_body: Any # It can be any object after parsing raw (str) body created_at: float def __init__( self, headers: str | bytes, body: _Body = "", sock: object | None = None, path_encoding: str = "iso-8859-1" From 912e1bb87f2a56b95a7a7a73b8041f59f6b0eded Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Mon, 20 Jul 2026 20:44:53 +0100 Subject: [PATCH 8/9] Update stubs/httpretty/httpretty/core.pyi Co-authored-by: Semyon Moroz --- stubs/httpretty/httpretty/core.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/httpretty/httpretty/core.pyi b/stubs/httpretty/httpretty/core.pyi index 086327b4e642..6a880997776a 100644 --- a/stubs/httpretty/httpretty/core.pyi +++ b/stubs/httpretty/httpretty/core.pyi @@ -76,7 +76,7 @@ class Entry(HttpBaseClass): ) -> None: ... def validate(self) -> None: ... def normalize_headers(self, headers: _Headers) -> dict[str, str]: ... - def fill_filekind(self, fk: Any) -> None: ... + def fill_filekind(self, fk: _WritableFileobj) -> None: ... class URIInfo(HttpBaseClass): default_str_attrs: tuple[str, ...] From 2006c89121aa25254325a12490a7f6eab560c1a6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 19:46:46 +0000 Subject: [PATCH 9/9] [pre-commit.ci] auto fixes from pre-commit.com hooks --- stubs/httpretty/httpretty/http.pyi | 1 - 1 file changed, 1 deletion(-) diff --git a/stubs/httpretty/httpretty/http.pyi b/stubs/httpretty/httpretty/http.pyi index 38feb43ee6c1..c6669544ce5d 100644 --- a/stubs/httpretty/httpretty/http.pyi +++ b/stubs/httpretty/httpretty/http.pyi @@ -1,5 +1,4 @@ from collections.abc import Sequence -from typing import Literal from typing_extensions import TypeVar _T = TypeVar("_T", str, bytes)