Skip to content

Commit b7fadfe

Browse files
committed
Add type hints to mocket.plugins.httpretty
1 parent 2ad3bec commit b7fadfe

File tree

5 files changed

+66
-30
lines changed

5 files changed

+66
-30
lines changed

mocket/mocket.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import itertools
55
import os
66
from pathlib import Path
7-
from typing import TYPE_CHECKING, ClassVar
7+
from typing import TYPE_CHECKING, Any, ClassVar
88

99
import mocket.inject
1010
from mocket.recording import MocketRecordStorage
@@ -99,12 +99,12 @@ def reset(cls) -> None:
9999
cls._record_storage = None
100100

101101
@classmethod
102-
def last_request(cls):
102+
def last_request(cls) -> Any:
103103
if cls.has_requests():
104104
return cls._requests[-1]
105105

106106
@classmethod
107-
def request_list(cls):
107+
def request_list(cls) -> list[Any]:
108108
return cls._requests
109109

110110
@classmethod

mocket/mocks/mockhttp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def __init__(self, body="", status=200, headers=None):
8888

8989
self.data = self.get_protocol_data() + self.body
9090

91-
def get_protocol_data(self, str_format_fun_name="capitalize"):
91+
def get_protocol_data(self, str_format_fun_name: str = "capitalize") -> bytes:
9292
status_line = f"HTTP/1.1 {self.status} {STATUS[self.status]}"
9393
header_lines = CRLF.join(
9494
(

mocket/plugins/httpretty/__init__.py

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any, Dict, Optional
2+
13
from mocket import mocketize
24
from mocket.async_mocket import async_mocketize
35
from mocket.compat import ENCODING
@@ -7,33 +9,35 @@
79
from mocket.mockhttp import Response as MocketHttpResponse
810

911

10-
def httprettifier_headers(headers):
12+
def httprettifier_headers(headers: Dict[str, str]) -> Dict[str, str]:
1113
return {k.lower().replace("_", "-"): v for k, v in headers.items()}
1214

1315

1416
class Request(MocketHttpRequest):
1517
@property
16-
def body(self):
17-
return super().body.encode(ENCODING)
18+
def body(self) -> bytes:
19+
return super().body.encode(ENCODING) # type: ignore[no-any-return]
1820

1921
@property
20-
def headers(self):
22+
def headers(self) -> Dict[str, str]:
2123
return httprettifier_headers(super().headers)
2224

2325

2426
class Response(MocketHttpResponse):
25-
def get_protocol_data(self, str_format_fun_name="lower"):
27+
headers: Dict[str, str]
28+
29+
def get_protocol_data(self, str_format_fun_name: str = "lower") -> bytes:
2630
if "server" in self.headers and self.headers["server"] == "Python/Mocket":
2731
self.headers["server"] = "Python/HTTPretty"
28-
return super().get_protocol_data(str_format_fun_name=str_format_fun_name)
32+
return super().get_protocol_data(str_format_fun_name=str_format_fun_name) # type: ignore[no-any-return]
2933

30-
def set_base_headers(self):
34+
def set_base_headers(self) -> None:
3135
super().set_base_headers()
3236
self.headers = httprettifier_headers(self.headers)
3337

3438
original_set_base_headers = set_base_headers
3539

36-
def set_extra_headers(self, headers):
40+
def set_extra_headers(self, headers: Dict[str, str]) -> None:
3741
self.headers.update(headers)
3842

3943

@@ -60,17 +64,17 @@ class Entry(MocketHttpEntry):
6064

6165

6266
def register_uri(
63-
method,
64-
uri,
65-
body="HTTPretty :)",
66-
adding_headers=None,
67-
forcing_headers=None,
68-
status=200,
69-
responses=None,
70-
match_querystring=False,
71-
priority=0,
72-
**headers,
73-
):
67+
method: str,
68+
uri: str,
69+
body: str = "HTTPretty :)",
70+
adding_headers: Optional[Dict[str, str]] = None,
71+
forcing_headers: Optional[Dict[str, str]] = None,
72+
status: int = 200,
73+
responses: Any = None,
74+
match_querystring: bool = False,
75+
priority: int = 0,
76+
**headers: str,
77+
) -> None:
7478
headers = httprettifier_headers(headers)
7579

7680
if adding_headers is not None:
@@ -81,9 +85,9 @@ def register_uri(
8185
def force_headers(self):
8286
self.headers = httprettifier_headers(forcing_headers)
8387

84-
Response.set_base_headers = force_headers
88+
Response.set_base_headers = force_headers # type: ignore[method-assign]
8589
else:
86-
Response.set_base_headers = Response.original_set_base_headers
90+
Response.set_base_headers = Response.original_set_base_headers # type: ignore[method-assign]
8791

8892
if responses:
8993
Entry.register(method, uri, *responses)
@@ -110,7 +114,7 @@ def __getattr__(self, name):
110114

111115

112116
HTTPretty = MocketHTTPretty()
113-
HTTPretty.register_uri = register_uri
117+
HTTPretty.register_uri = register_uri # type: ignore[attr-defined]
114118
httpretty = HTTPretty
115119

116120
__all__ = (

mocket/utils.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,34 @@
22

33
import binascii
44
import contextlib
5-
from typing import Callable
5+
from typing import Any, Callable, Protocol, TypeVar, overload
66

77
import decorator
8+
from typing_extensions import ParamSpec
89

910
from mocket.compat import decode_from_bytes, encode_to_bytes
1011

12+
_P = ParamSpec("_P")
13+
_R = TypeVar("_R")
14+
15+
16+
class MocketizeDecorator(Protocol):
17+
"""
18+
This is a generic decorator signature, currently applicable to get_mocketize.
19+
20+
Decorators can be used as:
21+
1. A function that transforms func (the parameter) into func1 (the returned object).
22+
2. A function that takes keyword arguments and returns 1.
23+
"""
24+
25+
@overload
26+
def __call__(self, func: Callable[_P, _R], /) -> Callable[_P, _R]: ...
27+
28+
@overload
29+
def __call__(
30+
self, **kwargs: Any
31+
) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: ...
32+
1133

1234
def hexdump(binary_string: bytes) -> str:
1335
r"""
@@ -30,11 +52,11 @@ def hexload(string: str) -> bytes:
3052
raise ValueError from e
3153

3254

33-
def get_mocketize(wrapper_: Callable) -> Callable:
55+
def get_mocketize(wrapper_: Callable) -> MocketizeDecorator:
3456
# trying to support different versions of `decorator`
3557
with contextlib.suppress(TypeError):
36-
return decorator.decorator(wrapper_, kwsyntax=True) # type: ignore[call-arg,unused-ignore]
37-
return decorator.decorator(wrapper_)
58+
return decorator.decorator(wrapper_, kwsyntax=True) # type: ignore[return-value, call-arg]
59+
return decorator.decorator(wrapper_) # type: ignore[return-value]
3860

3961

4062
__all__ = (

pyproject.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ files = [
123123
"mocket/exceptions.py",
124124
"mocket/compat.py",
125125
"mocket/utils.py",
126+
"mocket/plugins/httpretty/__init__.py",
127+
"tests/test_httpretty.py",
126128
# "tests/"
127129
]
128130
strict = true
@@ -140,3 +142,11 @@ disable_error_code = ["no-untyped-def"] # enable this once full type-coverage is
140142
[[tool.mypy.overrides]]
141143
module = "tests.*"
142144
disable_error_code = ['type-arg', 'no-untyped-def']
145+
146+
[[tool.mypy.overrides]]
147+
module = "mocket.plugins.*"
148+
disallow_subclassing_any = false # mypy doesn't support dynamic imports
149+
150+
[[tool.mypy.overrides]]
151+
module = "tests.test_httpretty"
152+
disallow_untyped_decorators = true

0 commit comments

Comments
 (0)