Skip to content

Commit 3b84156

Browse files
committed
typing: fix HeaderValueMatcher typing
Replace HeaderValueMatcher with a callable as it impplements the callable protocol and users can provide a callable also, as it is described in the howto.
1 parent 2f3f1f4 commit 3b84156

5 files changed

Lines changed: 63 additions & 10 deletions

File tree

CHANGES.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22
Release Notes
33
=============
44

5+
.. _Release Notes_1.0.6-12:
6+
7+
1.0.6-12
8+
========
9+
10+
.. _Release Notes_1.0.6-12_Bug Fixes:
11+
12+
Bug Fixes
13+
---------
14+
15+
- Type hinting for header_value_matcher has been fixed. From now, specifying a
16+
callable as `Callable[[str, Optional[str], str], bool]` will be accepted
17+
also. Providing a `HeaderValueMatcher` object will be also accepted as
18+
before, as it provides the same callable signature.
19+
20+
521
.. _Release Notes_1.0.6:
622

723
1.0.6

pytest_httpserver/httpserver.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
Iterable[Tuple[str, Union[str, int]]],
3838
]
3939

40+
HVMATCHER_T = Callable[[str, Optional[str], str], bool]
41+
4042

4143
class Undefined:
4244
def __repr__(self):
@@ -289,6 +291,13 @@ class RequestMatcher:
289291
specified in the request. If multiple values specified for a given key, the first
290292
value will be used. If multiple values needed to be handled, use ``MultiDict``
291293
object from werkzeug.
294+
:param header_value_matcher: :py:class:`HeaderValueMatcher` that matches
295+
values of headers, or a ``Callable[[str, Optional[str], str], bool]``
296+
receiving the header key (from `headers`), header value (or `None`) and the expected
297+
value (from `headers`) and should return ``True`` if the header matches, ``False`` otherwise.
298+
:param json: a python object (eg. a dict) whose value will be compared to the request body after it
299+
is loaded as json. If load fails, this matcher will be failed also. *Content-Type* is not checked.
300+
If that's desired, add it to the headers parameter.
292301
"""
293302

294303
def __init__(
@@ -299,7 +308,7 @@ def __init__(
299308
data_encoding: str = "utf-8",
300309
headers: Optional[Mapping[str, str]] = None,
301310
query_string: Union[None, QueryMatcher, str, bytes, Mapping] = None,
302-
header_value_matcher: Optional[HeaderValueMatcher] = None,
311+
header_value_matcher: Optional[HVMATCHER_T] = None,
303312
json: Any = UNDEFINED,
304313
):
305314

@@ -322,7 +331,10 @@ def __init__(
322331
self.data = data
323332
self.data_encoding = data_encoding
324333

325-
self.header_value_matcher = HeaderValueMatcher() if header_value_matcher is None else header_value_matcher
334+
self.header_value_matcher: HVMATCHER_T = HeaderValueMatcher()
335+
336+
if header_value_matcher is not None:
337+
self.header_value_matcher = header_value_matcher
326338

327339
def __repr__(self):
328340
"""
@@ -939,7 +951,7 @@ def expect_request(
939951
data_encoding: str = "utf-8",
940952
headers: Optional[Mapping[str, str]] = None,
941953
query_string: Union[None, QueryMatcher, str, bytes, Mapping] = None,
942-
header_value_matcher: Optional[HeaderValueMatcher] = None,
954+
header_value_matcher: Optional[HVMATCHER_T] = None,
943955
handler_type: HandlerType = HandlerType.PERMANENT,
944956
json: Any = UNDEFINED,
945957
) -> RequestHandler:
@@ -973,7 +985,10 @@ def expect_request(
973985
specified in the request. If multiple values specified for a given key, the first
974986
value will be used. If multiple values needed to be handled, use ``MultiDict``
975987
object from werkzeug.
976-
:param header_value_matcher: :py:class:`HeaderValueMatcher` that matches values of headers.
988+
:param header_value_matcher: :py:class:`HeaderValueMatcher` that matches
989+
values of headers, or a ``Callable[[str, Optional[str], str], bool]``
990+
receiving the header key (from `headers`), header value (or `None`) and the expected
991+
value (from `headers`) and should return ``True`` if the header matches, ``False`` otherwise.
977992
:param handler_type: type of handler
978993
:param json: a python object (eg. a dict) whose value will be compared to the request body after it
979994
is loaded as json. If load fails, this matcher will be failed also. *Content-Type* is not checked.
@@ -1011,7 +1026,7 @@ def expect_oneshot_request(
10111026
data_encoding: str = "utf-8",
10121027
headers: Optional[Mapping[str, str]] = None,
10131028
query_string: Union[None, QueryMatcher, str, bytes, Mapping] = None,
1014-
header_value_matcher: Optional[HeaderValueMatcher] = None,
1029+
header_value_matcher: Optional[HVMATCHER_T] = None,
10151030
json: Any = UNDEFINED,
10161031
) -> RequestHandler:
10171032
"""
@@ -1033,7 +1048,10 @@ def expect_oneshot_request(
10331048
specified in the request. If multiple values specified for a given key, the first
10341049
value will be used. If multiple values needed to be handled, use ``MultiDict``
10351050
object from werkzeug.
1036-
:param header_value_matcher: :py:class:`HeaderValueMatcher` that matches values of headers.
1051+
:param header_value_matcher: :py:class:`HeaderValueMatcher` that matches
1052+
values of headers, or a ``Callable[[str, Optional[str], str], bool]``
1053+
receiving the header key (from `headers`), header value (or `None`) and the expected
1054+
value (from `headers`) and should return ``True`` if the header matches, ``False`` otherwise.
10371055
:param json: a python object (eg. a dict) whose value will be compared to the request body after it
10381056
is loaded as json. If load fails, this matcher will be failed also. *Content-Type* is not checked.
10391057
If that's desired, add it to the headers parameter.
@@ -1063,7 +1081,7 @@ def expect_ordered_request(
10631081
data_encoding: str = "utf-8",
10641082
headers: Optional[Mapping[str, str]] = None,
10651083
query_string: Union[None, QueryMatcher, str, bytes, Mapping] = None,
1066-
header_value_matcher: Optional[HeaderValueMatcher] = None,
1084+
header_value_matcher: Optional[HVMATCHER_T] = None,
10671085
json: Any = UNDEFINED,
10681086
) -> RequestHandler:
10691087
"""
@@ -1085,7 +1103,10 @@ def expect_ordered_request(
10851103
specified in the request. If multiple values specified for a given key, the first
10861104
value will be used. If multiple values needed to be handled, use ``MultiDict``
10871105
object from werkzeug.
1088-
:param header_value_matcher: :py:class:`HeaderValueMatcher` that matches values of headers.
1106+
:param header_value_matcher: :py:class:`HeaderValueMatcher` that matches
1107+
values of headers, or a ``Callable[[str, Optional[str], str], bool]``
1108+
receiving the header key (from `headers`), header value (or `None`) and the expected
1109+
value (from `headers`) and should return ``True`` if the header matches, ``False`` otherwise.
10891110
:param json: a python object (eg. a dict) whose value will be compared to the request body after it
10901111
is loaded as json. If load fails, this matcher will be failed also. *Content-Type* is not checked.
10911112
If that's desired, add it to the headers parameter.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fixes:
2+
- |
3+
Type hinting for header_value_matcher has been fixed. From now, specifying a
4+
callable as ``Callable[[str, Optional[str], str], bool]`` will be accepted
5+
also. Providing a ``HeaderValueMatcher`` object will be also accepted as
6+
before, as it provides the same callable signature.

tests/examples/test_howto_case_insensitive_matcher.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
from typing import Optional
2+
13
import requests
24

35
from pytest_httpserver import HTTPServer
46

57

6-
def case_insensitive_matcher(header_name: str, actual: str, expected: str) -> bool:
8+
def case_insensitive_matcher(header_name: str, actual: Optional[str], expected: str) -> bool:
9+
if actual is None:
10+
return False
11+
712
if header_name == "X-Foo":
813
return actual.lower() == expected.lower()
914
else:

tests/examples/test_howto_header_value_matcher.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
from typing import Optional
2+
13
import requests
24

35
from pytest_httpserver import HeaderValueMatcher
46
from pytest_httpserver import HTTPServer
57

68

7-
def case_insensitive_compare(actual: str, expected: str) -> bool:
9+
def case_insensitive_compare(actual: Optional[str], expected: str) -> bool:
10+
# actual is `None` if it is not specified
11+
if actual is None:
12+
return False
813
return actual.lower() == expected.lower()
914

1015

0 commit comments

Comments
 (0)