pytest_httpserver 1.0.9__tar.gz → 1.0.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/CHANGES.rst +23 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/PKG-INFO +15 -9
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/README.md +14 -8
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/doc/conf.py +1 -1
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/pyproject.toml +54 -1
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/pytest_httpserver/blocking_httpserver.py +18 -16
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/pytest_httpserver/httpserver.py +76 -78
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/pytest_httpserver/pytest_plugin.py +4 -4
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_blocking_httpserver.py +5 -2
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_handler_errors.py +6 -6
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_json_matcher.py +1 -1
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_parse_qs.py +4 -4
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_port_changing.py +2 -2
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_querymatcher.py +1 -1
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_release.py +7 -6
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/CONTRIBUTION.md +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/LICENSE +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/doc/Makefile +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/doc/_static/.placeholder +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/doc/api.rst +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/doc/background.rst +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/doc/changes.rst +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/doc/fixtures.rst +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/doc/guide.rst +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/doc/howto.rst +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/doc/index.rst +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/doc/tutorial.rst +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/doc/upgrade.rst +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/example.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/example_pytest.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/pytest_httpserver/__init__.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/pytest_httpserver/py.typed +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/assets/Makefile +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/assets/README +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/assets/rootCA.cnf +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/assets/rootCA.crt +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/assets/rootCA.key +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/assets/rootCA.srl +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/assets/server.cnf +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/assets/server.crt +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/assets/server.csr +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/assets/server.key +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/assets/v3.ext +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/conftest.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_example_blocking_httpserver.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_example_query_params1.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_example_query_params2.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_authorization_headers.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_case_insensitive_matcher.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_check.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_check_handler_errors.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_custom_handler.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_header_value_matcher.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_json_matcher.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_query_params_dict.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_query_params_never_do_this.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_query_params_proper_use.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_regexp.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_timeout_requests.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_url_matcher.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_wait_success.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_headers.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_ip_protocols.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_mixed.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_oneshot.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_ordered.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_permanent.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_querystring.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_ssl.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_urimatch.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_wait.py +0 -0
- {pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/test_with_statement.py +0 -0
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
Release Notes
|
|
3
3
|
=============
|
|
4
4
|
|
|
5
|
+
.. _Release Notes_1.0.10:
|
|
6
|
+
|
|
7
|
+
1.0.10
|
|
8
|
+
======
|
|
9
|
+
|
|
10
|
+
.. _Release Notes_1.0.10_New Features:
|
|
11
|
+
|
|
12
|
+
New Features
|
|
13
|
+
------------
|
|
14
|
+
|
|
15
|
+
- When there's no handler for the request, add more details to the response
|
|
16
|
+
sent by the server about the request to help debugging.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
.. _Release Notes_1.0.10_Other Notes:
|
|
20
|
+
|
|
21
|
+
Other Notes
|
|
22
|
+
-----------
|
|
23
|
+
|
|
24
|
+
- Use ruff for linting. It includes some source code changes which should not
|
|
25
|
+
introduce functional changes, or API changes.
|
|
26
|
+
|
|
27
|
+
|
|
5
28
|
.. _Release Notes_1.0.9:
|
|
6
29
|
|
|
7
30
|
1.0.9
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pytest_httpserver
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.10
|
|
4
4
|
Summary: pytest-httpserver is a httpserver for pytest
|
|
5
5
|
Home-page: https://github.com/csernazs/pytest-httpserver
|
|
6
6
|
License: MIT
|
|
@@ -27,10 +27,12 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|
|
|
28
28
|
[](https://github.com/csernazs/pytest-httpserver/actions?query=workflow%3Abuild+branch%3Amaster)
|
|
29
29
|
[](https://pytest-httpserver.readthedocs.io/en/latest/?badge=latest)
|
|
30
|
-
|
|
31
|
-
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=K6PU3AGBZW4QC&item_name=pytest-httpserver¤cy_code=EUR&source=url)
|
|
30
|
+
[](https://opensource.org/licenses/MIT)
|
|
32
31
|
[](https://codecov.io/gh/csernazs/pytest-httpserver)
|
|
33
32
|
[](https://github.com/psf/black)
|
|
33
|
+
[](https://github.com/astral-sh/ruff)
|
|
34
|
+
[](https://pepy.tech/project/pytest-httpserver)
|
|
35
|
+
[](https://github.com/pre-commit/pre-commit)
|
|
34
36
|
|
|
35
37
|
## pytest_httpserver
|
|
36
38
|
|
|
@@ -144,12 +146,16 @@ documentation](https://pytest-httpserver.readthedocs.io/en/latest/api.html#block
|
|
|
144
146
|
|
|
145
147
|
### Donation
|
|
146
148
|
|
|
147
|
-
|
|
148
|
-
of the README.
|
|
149
|
+
Currently, this project is based heavily on werkzeug and pytest.
|
|
149
150
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
Werkzeug does all the heavy lifting behind the scenes, parsing HTTP request and
|
|
152
|
+
defining Request and Response objects, which are currently transparent in the
|
|
153
|
+
API.
|
|
153
154
|
|
|
154
|
-
If you wish to donate
|
|
155
|
+
If you wish to donate to werkzeug: https://palletsprojects.com/donate
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
Pytest is the de-facto test library for python.
|
|
159
|
+
|
|
160
|
+
If you wish to donate to pytest: https://opencollective.com/pytest
|
|
155
161
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
[](https://github.com/csernazs/pytest-httpserver/actions?query=workflow%3Abuild+branch%3Amaster)
|
|
2
2
|
[](https://pytest-httpserver.readthedocs.io/en/latest/?badge=latest)
|
|
3
|
-
|
|
4
|
-
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=K6PU3AGBZW4QC&item_name=pytest-httpserver¤cy_code=EUR&source=url)
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
4
|
[](https://codecov.io/gh/csernazs/pytest-httpserver)
|
|
6
5
|
[](https://github.com/psf/black)
|
|
6
|
+
[](https://github.com/astral-sh/ruff)
|
|
7
|
+
[](https://pepy.tech/project/pytest-httpserver)
|
|
8
|
+
[](https://github.com/pre-commit/pre-commit)
|
|
7
9
|
|
|
8
10
|
## pytest_httpserver
|
|
9
11
|
|
|
@@ -117,11 +119,15 @@ documentation](https://pytest-httpserver.readthedocs.io/en/latest/api.html#block
|
|
|
117
119
|
|
|
118
120
|
### Donation
|
|
119
121
|
|
|
120
|
-
|
|
121
|
-
of the README.
|
|
122
|
+
Currently, this project is based heavily on werkzeug and pytest.
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
Werkzeug does all the heavy lifting behind the scenes, parsing HTTP request and
|
|
125
|
+
defining Request and Response objects, which are currently transparent in the
|
|
126
|
+
API.
|
|
126
127
|
|
|
127
|
-
If you wish to donate
|
|
128
|
+
If you wish to donate to werkzeug: https://palletsprojects.com/donate
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
Pytest is the de-facto test library for python.
|
|
132
|
+
|
|
133
|
+
If you wish to donate to pytest: https://opencollective.com/pytest
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pytest_httpserver"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.10"
|
|
4
4
|
description = "pytest-httpserver is a httpserver for pytest"
|
|
5
5
|
authors = ["Zsolt Cserna <cserna.zsolt@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -51,6 +51,7 @@ coverage = ">=6.4.4,<8.0.0"
|
|
|
51
51
|
types-toml = "^0.10.8"
|
|
52
52
|
toml = "^0.10.2"
|
|
53
53
|
black = "^23.1.0"
|
|
54
|
+
ruff = "^0.2.1"
|
|
54
55
|
|
|
55
56
|
|
|
56
57
|
[tool.poetry.group.doc]
|
|
@@ -88,3 +89,55 @@ markers = [
|
|
|
88
89
|
[tool.mypy]
|
|
89
90
|
files = ["pytest_httpserver", "scripts", "tests", "doc"]
|
|
90
91
|
implicit_reexport = false
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
[tool.black]
|
|
95
|
+
line-length = 120
|
|
96
|
+
safe = true
|
|
97
|
+
|
|
98
|
+
[tool.ruff]
|
|
99
|
+
lint.select = ["ALL"]
|
|
100
|
+
lint.ignore = [
|
|
101
|
+
"I",
|
|
102
|
+
"D",
|
|
103
|
+
|
|
104
|
+
"ANN",
|
|
105
|
+
"ARG005",
|
|
106
|
+
"B011",
|
|
107
|
+
"B904",
|
|
108
|
+
"C408",
|
|
109
|
+
"C901",
|
|
110
|
+
"COM812",
|
|
111
|
+
"EM101",
|
|
112
|
+
"EM103",
|
|
113
|
+
"FBT002",
|
|
114
|
+
"FIX002",
|
|
115
|
+
"INP001",
|
|
116
|
+
"PGH003",
|
|
117
|
+
"PLR0912",
|
|
118
|
+
"PLR0913",
|
|
119
|
+
"PLR2004",
|
|
120
|
+
"PLW2901",
|
|
121
|
+
"PT004",
|
|
122
|
+
"PT012",
|
|
123
|
+
"PT013",
|
|
124
|
+
"PTH118",
|
|
125
|
+
"PTH120",
|
|
126
|
+
"RET504",
|
|
127
|
+
"RET505",
|
|
128
|
+
"RET506",
|
|
129
|
+
"RUF005",
|
|
130
|
+
"S101",
|
|
131
|
+
"S113",
|
|
132
|
+
"S603",
|
|
133
|
+
"S607",
|
|
134
|
+
"SIM108",
|
|
135
|
+
"T201",
|
|
136
|
+
"TD002",
|
|
137
|
+
"TD003",
|
|
138
|
+
"TRY003",
|
|
139
|
+
"UP032",
|
|
140
|
+
]
|
|
141
|
+
line-length = 120
|
|
142
|
+
target-version = "py38"
|
|
143
|
+
exclude = ["doc", "example*.py", "tests/examples/*.py"]
|
{pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/pytest_httpserver/blocking_httpserver.py
RENAMED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from queue import Empty
|
|
2
4
|
from queue import Queue
|
|
3
|
-
from
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
4
6
|
from typing import Any
|
|
5
|
-
from typing import Dict
|
|
6
7
|
from typing import Mapping
|
|
7
|
-
from typing import Optional
|
|
8
8
|
from typing import Pattern
|
|
9
|
-
from typing import Union
|
|
10
|
-
|
|
11
|
-
from werkzeug.wrappers import Request
|
|
12
|
-
from werkzeug.wrappers import Response
|
|
13
9
|
|
|
14
10
|
from pytest_httpserver.httpserver import METHOD_ALL
|
|
15
11
|
from pytest_httpserver.httpserver import UNDEFINED
|
|
@@ -19,6 +15,12 @@ from pytest_httpserver.httpserver import QueryMatcher
|
|
|
19
15
|
from pytest_httpserver.httpserver import RequestHandlerBase
|
|
20
16
|
from pytest_httpserver.httpserver import URIPattern
|
|
21
17
|
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from ssl import SSLContext
|
|
20
|
+
|
|
21
|
+
from werkzeug.wrappers import Request
|
|
22
|
+
from werkzeug.wrappers import Response
|
|
23
|
+
|
|
22
24
|
|
|
23
25
|
class BlockingRequestHandler(RequestHandlerBase):
|
|
24
26
|
"""
|
|
@@ -59,23 +61,23 @@ class BlockingHTTPServer(HTTPServerBase):
|
|
|
59
61
|
self,
|
|
60
62
|
host=DEFAULT_LISTEN_HOST,
|
|
61
63
|
port=DEFAULT_LISTEN_PORT,
|
|
62
|
-
ssl_context:
|
|
64
|
+
ssl_context: SSLContext | None = None,
|
|
63
65
|
timeout: int = 30,
|
|
64
66
|
):
|
|
65
67
|
super().__init__(host, port, ssl_context)
|
|
66
68
|
self.timeout = timeout
|
|
67
69
|
self.request_queue: Queue[Request] = Queue()
|
|
68
|
-
self.request_handlers:
|
|
70
|
+
self.request_handlers: dict[Request, Queue[BlockingRequestHandler]] = {}
|
|
69
71
|
|
|
70
72
|
def assert_request(
|
|
71
73
|
self,
|
|
72
|
-
uri:
|
|
74
|
+
uri: str | URIPattern | Pattern[str],
|
|
73
75
|
method: str = METHOD_ALL,
|
|
74
|
-
data:
|
|
76
|
+
data: str | bytes | None = None,
|
|
75
77
|
data_encoding: str = "utf-8",
|
|
76
|
-
headers:
|
|
77
|
-
query_string:
|
|
78
|
-
header_value_matcher:
|
|
78
|
+
headers: Mapping[str, str] | None = None,
|
|
79
|
+
query_string: None | QueryMatcher | str | bytes | Mapping = None,
|
|
80
|
+
header_value_matcher: HeaderValueMatcher | None = None,
|
|
79
81
|
json: Any = UNDEFINED,
|
|
80
82
|
timeout: int = 30,
|
|
81
83
|
) -> BlockingRequestHandler:
|
|
@@ -127,7 +129,7 @@ class BlockingHTTPServer(HTTPServerBase):
|
|
|
127
129
|
try:
|
|
128
130
|
request = self.request_queue.get(timeout=timeout)
|
|
129
131
|
except Empty:
|
|
130
|
-
raise AssertionError(f"Waiting for request {matcher} timed out")
|
|
132
|
+
raise AssertionError(f"Waiting for request {matcher} timed out") # noqa: EM102
|
|
131
133
|
|
|
132
134
|
diff = matcher.difference(request)
|
|
133
135
|
|
|
@@ -137,7 +139,7 @@ class BlockingHTTPServer(HTTPServerBase):
|
|
|
137
139
|
|
|
138
140
|
if diff:
|
|
139
141
|
request_handler.respond_with_response(self.respond_nohandler(request))
|
|
140
|
-
raise AssertionError(f"Request {matcher} does not match: {diff}")
|
|
142
|
+
raise AssertionError(f"Request {matcher} does not match: {diff}") # noqa: EM102
|
|
141
143
|
|
|
142
144
|
return request_handler
|
|
143
145
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import abc
|
|
2
4
|
import ipaddress
|
|
3
5
|
import json
|
|
@@ -11,11 +13,11 @@ from contextlib import contextmanager
|
|
|
11
13
|
from contextlib import suppress
|
|
12
14
|
from copy import copy
|
|
13
15
|
from enum import Enum
|
|
14
|
-
from
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
15
17
|
from typing import Any
|
|
16
18
|
from typing import Callable
|
|
19
|
+
from typing import ClassVar
|
|
17
20
|
from typing import Iterable
|
|
18
|
-
from typing import List
|
|
19
21
|
from typing import Mapping
|
|
20
22
|
from typing import MutableMapping
|
|
21
23
|
from typing import Optional
|
|
@@ -30,6 +32,9 @@ from werkzeug.serving import make_server
|
|
|
30
32
|
from werkzeug.wrappers import Request
|
|
31
33
|
from werkzeug.wrappers import Response
|
|
32
34
|
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from ssl import SSLContext
|
|
37
|
+
|
|
33
38
|
URI_DEFAULT = ""
|
|
34
39
|
METHOD_ALL = "__ALL"
|
|
35
40
|
|
|
@@ -54,32 +59,24 @@ class Error(Exception):
|
|
|
54
59
|
Base class for all exception defined in this package.
|
|
55
60
|
"""
|
|
56
61
|
|
|
57
|
-
pass
|
|
58
|
-
|
|
59
62
|
|
|
60
63
|
class NoHandlerError(Error):
|
|
61
64
|
"""
|
|
62
65
|
Raised when a :py:class:`RequestHandler` has no registered method to serve the request.
|
|
63
66
|
"""
|
|
64
67
|
|
|
65
|
-
pass
|
|
66
|
-
|
|
67
68
|
|
|
68
69
|
class HTTPServerError(Error):
|
|
69
70
|
"""
|
|
70
71
|
Raised when there's a problem with HTTP server.
|
|
71
72
|
"""
|
|
72
73
|
|
|
73
|
-
pass
|
|
74
|
-
|
|
75
74
|
|
|
76
75
|
class NoMethodFoundForMatchingHeaderValueError(Error):
|
|
77
76
|
"""
|
|
78
77
|
Raised when a :py:class:`HeaderValueMatcher` has no registered method to match the header value.
|
|
79
78
|
"""
|
|
80
79
|
|
|
81
|
-
pass
|
|
82
|
-
|
|
83
80
|
|
|
84
81
|
class WaitingSettings:
|
|
85
82
|
"""Class for providing default settings and storing them in HTTPServer
|
|
@@ -89,7 +86,12 @@ class WaitingSettings:
|
|
|
89
86
|
:param timeout: time (in seconds) until time is out
|
|
90
87
|
"""
|
|
91
88
|
|
|
92
|
-
def __init__(
|
|
89
|
+
def __init__(
|
|
90
|
+
self,
|
|
91
|
+
raise_assertions: bool = True, # noqa: FBT001
|
|
92
|
+
stop_on_nohandler: bool = True, # noqa: FBT001
|
|
93
|
+
timeout: float = 5,
|
|
94
|
+
):
|
|
93
95
|
self.raise_assertions = raise_assertions
|
|
94
96
|
self.stop_on_nohandler = stop_on_nohandler
|
|
95
97
|
self.timeout = timeout
|
|
@@ -105,7 +107,7 @@ class Waiting:
|
|
|
105
107
|
self._start = time.monotonic()
|
|
106
108
|
self._stop = None
|
|
107
109
|
|
|
108
|
-
def complete(self, result: bool):
|
|
110
|
+
def complete(self, result: bool): # noqa: FBT001
|
|
109
111
|
self._result = result
|
|
110
112
|
self._stop = time.monotonic()
|
|
111
113
|
|
|
@@ -127,23 +129,23 @@ class HeaderValueMatcher:
|
|
|
127
129
|
and return whether they are equal as bool.
|
|
128
130
|
"""
|
|
129
131
|
|
|
130
|
-
DEFAULT_MATCHERS: MutableMapping[str, Callable[[
|
|
132
|
+
DEFAULT_MATCHERS: ClassVar[MutableMapping[str, Callable[[str | None, str], bool]]] = {}
|
|
131
133
|
|
|
132
|
-
def __init__(self, matchers:
|
|
134
|
+
def __init__(self, matchers: Mapping[str, Callable[[str | None, str], bool]] | None = None):
|
|
133
135
|
self.matchers = self.DEFAULT_MATCHERS if matchers is None else matchers
|
|
134
136
|
|
|
135
137
|
@staticmethod
|
|
136
|
-
def authorization_header_value_matcher(actual:
|
|
137
|
-
|
|
138
|
-
if
|
|
139
|
-
|
|
140
|
-
return
|
|
138
|
+
def authorization_header_value_matcher(actual: str | None, expected: str) -> bool:
|
|
139
|
+
func = getattr(Authorization, "from_header", None)
|
|
140
|
+
if func is None: # Werkzeug < 2.3.0
|
|
141
|
+
func = werkzeug.http.parse_authorization_header # type: ignore[attr-defined]
|
|
142
|
+
return func(actual) == func(expected)
|
|
141
143
|
|
|
142
144
|
@staticmethod
|
|
143
|
-
def default_header_value_matcher(actual:
|
|
145
|
+
def default_header_value_matcher(actual: str | None, expected: str) -> bool:
|
|
144
146
|
return actual == expected
|
|
145
147
|
|
|
146
|
-
def __call__(self, header_name: str, actual:
|
|
148
|
+
def __call__(self, header_name: str, actual: str | None, expected: str) -> bool:
|
|
147
149
|
try:
|
|
148
150
|
matcher = self.matchers[header_name]
|
|
149
151
|
except KeyError:
|
|
@@ -181,7 +183,7 @@ class StringQueryMatcher(QueryMatcher):
|
|
|
181
183
|
Matches a query for a string or bytes specified
|
|
182
184
|
"""
|
|
183
185
|
|
|
184
|
-
def __init__(self, query_string:
|
|
186
|
+
def __init__(self, query_string: bytes | str):
|
|
185
187
|
"""
|
|
186
188
|
:param query_string: the query string will be compared to this string or bytes.
|
|
187
189
|
If string is specified, it will be encoded by the encode() method.
|
|
@@ -209,7 +211,7 @@ class MappingQueryMatcher(QueryMatcher):
|
|
|
209
211
|
Matches a query string to a dictionary or MultiDict specified
|
|
210
212
|
"""
|
|
211
213
|
|
|
212
|
-
def __init__(self, query_dict:
|
|
214
|
+
def __init__(self, query_dict: Mapping | MultiDict):
|
|
213
215
|
"""
|
|
214
216
|
:param query_dict: if dictionary (Mapping) is specified, it will be used as a
|
|
215
217
|
key-value mapping where both key and value should be string. If there are multiple
|
|
@@ -232,26 +234,26 @@ class BooleanQueryMatcher(QueryMatcher):
|
|
|
232
234
|
Matches the query depending on the boolean value
|
|
233
235
|
"""
|
|
234
236
|
|
|
235
|
-
def __init__(self, result: bool):
|
|
237
|
+
def __init__(self, result: bool): # noqa: FBT001
|
|
236
238
|
"""
|
|
237
239
|
:param result: if this parameter is true, the query match will be always
|
|
238
240
|
successful. Otherwise, no query match will be successful.
|
|
239
241
|
"""
|
|
240
242
|
self.result = result
|
|
241
243
|
|
|
242
|
-
def get_comparing_values(self, request_query_string):
|
|
244
|
+
def get_comparing_values(self, request_query_string): # noqa: ARG002
|
|
243
245
|
if self.result:
|
|
244
246
|
return (True, True)
|
|
245
247
|
else:
|
|
246
248
|
return (True, False)
|
|
247
249
|
|
|
248
250
|
|
|
249
|
-
def _create_query_matcher(query_string:
|
|
251
|
+
def _create_query_matcher(query_string: None | QueryMatcher | str | bytes | Mapping) -> QueryMatcher:
|
|
250
252
|
if isinstance(query_string, QueryMatcher):
|
|
251
253
|
return query_string
|
|
252
254
|
|
|
253
255
|
if query_string is None:
|
|
254
|
-
return BooleanQueryMatcher(True)
|
|
256
|
+
return BooleanQueryMatcher(result=True)
|
|
255
257
|
|
|
256
258
|
if isinstance(query_string, (str, bytes)):
|
|
257
259
|
return StringQueryMatcher(query_string)
|
|
@@ -272,7 +274,6 @@ class URIPattern(abc.ABC):
|
|
|
272
274
|
with "/" and does not contain the query part.
|
|
273
275
|
:return: True if there's a match, False otherwise
|
|
274
276
|
"""
|
|
275
|
-
pass
|
|
276
277
|
|
|
277
278
|
|
|
278
279
|
class RequestMatcher:
|
|
@@ -306,13 +307,13 @@ class RequestMatcher:
|
|
|
306
307
|
|
|
307
308
|
def __init__(
|
|
308
309
|
self,
|
|
309
|
-
uri:
|
|
310
|
+
uri: str | URIPattern | Pattern[str],
|
|
310
311
|
method: str = METHOD_ALL,
|
|
311
|
-
data:
|
|
312
|
+
data: str | bytes | None = None,
|
|
312
313
|
data_encoding: str = "utf-8",
|
|
313
|
-
headers:
|
|
314
|
-
query_string:
|
|
315
|
-
header_value_matcher:
|
|
314
|
+
headers: Mapping[str, str] | None = None,
|
|
315
|
+
query_string: None | QueryMatcher | str | bytes | Mapping = None,
|
|
316
|
+
header_value_matcher: HVMATCHER_T | None = None,
|
|
316
317
|
json: Any = UNDEFINED,
|
|
317
318
|
):
|
|
318
319
|
if json is not UNDEFINED and data is not None:
|
|
@@ -383,7 +384,7 @@ class RequestMatcher:
|
|
|
383
384
|
#
|
|
384
385
|
# also, python will raise TypeError when self.uri is a conflicting type
|
|
385
386
|
|
|
386
|
-
return self.uri
|
|
387
|
+
return self.uri in (URI_DEFAULT, path)
|
|
387
388
|
|
|
388
389
|
def match_json(self, request: Request) -> bool:
|
|
389
390
|
"""
|
|
@@ -409,7 +410,7 @@ class RequestMatcher:
|
|
|
409
410
|
|
|
410
411
|
return json_received == self.json
|
|
411
412
|
|
|
412
|
-
def difference(self, request: Request) ->
|
|
413
|
+
def difference(self, request: Request) -> list[tuple]:
|
|
413
414
|
"""
|
|
414
415
|
Calculates the difference between the matcher and the request.
|
|
415
416
|
|
|
@@ -421,12 +422,12 @@ class RequestMatcher:
|
|
|
421
422
|
matches the fields set in the matcher object.
|
|
422
423
|
"""
|
|
423
424
|
|
|
424
|
-
retval:
|
|
425
|
+
retval: list[tuple] = []
|
|
425
426
|
|
|
426
427
|
if not self.match_uri(request):
|
|
427
428
|
retval.append(("uri", request.path, self.uri))
|
|
428
429
|
|
|
429
|
-
if self.method
|
|
430
|
+
if self.method not in (METHOD_ALL, request.method):
|
|
430
431
|
retval.append(("method", request.method, self.method))
|
|
431
432
|
|
|
432
433
|
if not self.query_matcher.match(request.query_string):
|
|
@@ -468,7 +469,7 @@ class RequestHandlerBase(abc.ABC):
|
|
|
468
469
|
self,
|
|
469
470
|
response_json,
|
|
470
471
|
status: int = 200,
|
|
471
|
-
headers:
|
|
472
|
+
headers: Mapping[str, str] | None = None,
|
|
472
473
|
content_type: str = "application/json",
|
|
473
474
|
):
|
|
474
475
|
"""
|
|
@@ -485,11 +486,11 @@ class RequestHandlerBase(abc.ABC):
|
|
|
485
486
|
|
|
486
487
|
def respond_with_data(
|
|
487
488
|
self,
|
|
488
|
-
response_data:
|
|
489
|
+
response_data: str | bytes = "",
|
|
489
490
|
status: int = 200,
|
|
490
|
-
headers:
|
|
491
|
-
mimetype:
|
|
492
|
-
content_type:
|
|
491
|
+
headers: HEADERS_T | None = None,
|
|
492
|
+
mimetype: str | None = None,
|
|
493
|
+
content_type: str | None = None,
|
|
493
494
|
):
|
|
494
495
|
"""
|
|
495
496
|
Prepares a response with raw data.
|
|
@@ -514,8 +515,6 @@ class RequestHandlerBase(abc.ABC):
|
|
|
514
515
|
:param response: the response object which will be responded
|
|
515
516
|
"""
|
|
516
517
|
|
|
517
|
-
pass
|
|
518
|
-
|
|
519
518
|
|
|
520
519
|
class RequestHandler(RequestHandlerBase):
|
|
521
520
|
"""
|
|
@@ -529,7 +528,7 @@ class RequestHandler(RequestHandlerBase):
|
|
|
529
528
|
|
|
530
529
|
def __init__(self, matcher: RequestMatcher):
|
|
531
530
|
self.matcher = matcher
|
|
532
|
-
self.request_handler:
|
|
531
|
+
self.request_handler: Callable[[Request], Response] | None = None
|
|
533
532
|
|
|
534
533
|
def respond(self, request: Request) -> Response:
|
|
535
534
|
"""
|
|
@@ -576,7 +575,7 @@ class RequestHandlerList(list):
|
|
|
576
575
|
|
|
577
576
|
"""
|
|
578
577
|
|
|
579
|
-
def match(self, request: Request) ->
|
|
578
|
+
def match(self, request: Request) -> RequestHandler | None:
|
|
580
579
|
"""
|
|
581
580
|
Returns the first request handler which matches the specified request. Otherwise, it returns `None`.
|
|
582
581
|
"""
|
|
@@ -619,7 +618,7 @@ class HTTPServerBase(abc.ABC): # pylint: disable=too-many-instance-attributes
|
|
|
619
618
|
self,
|
|
620
619
|
host: str,
|
|
621
620
|
port: int,
|
|
622
|
-
ssl_context:
|
|
621
|
+
ssl_context: SSLContext | None = None,
|
|
623
622
|
):
|
|
624
623
|
"""
|
|
625
624
|
Initializes the instance.
|
|
@@ -629,9 +628,9 @@ class HTTPServerBase(abc.ABC): # pylint: disable=too-many-instance-attributes
|
|
|
629
628
|
self.port = port
|
|
630
629
|
self.server = None
|
|
631
630
|
self.server_thread = None
|
|
632
|
-
self.assertions:
|
|
633
|
-
self.handler_errors:
|
|
634
|
-
self.log:
|
|
631
|
+
self.assertions: list[str] = []
|
|
632
|
+
self.handler_errors: list[Exception] = []
|
|
633
|
+
self.log: list[tuple[Request, Response]] = []
|
|
635
634
|
self.ssl_context = ssl_context
|
|
636
635
|
self.no_handler_status_code = 500
|
|
637
636
|
|
|
@@ -821,9 +820,9 @@ class HTTPServerBase(abc.ABC): # pylint: disable=too-many-instance-attributes
|
|
|
821
820
|
As the result, there's an assertion added (which can be raised by :py:meth:`check_assertions`).
|
|
822
821
|
|
|
823
822
|
"""
|
|
824
|
-
text = "No handler found for request {!r}
|
|
823
|
+
text = "No handler found for request {!r} with data {!r}.".format(request, request.data)
|
|
825
824
|
self.add_assertion(text + extra_message)
|
|
826
|
-
return Response(
|
|
825
|
+
return Response(text + extra_message, self.no_handler_status_code)
|
|
827
826
|
|
|
828
827
|
@abc.abstractmethod
|
|
829
828
|
def dispatch(self, request: Request) -> Response:
|
|
@@ -833,7 +832,6 @@ class HTTPServerBase(abc.ABC): # pylint: disable=too-many-instance-attributes
|
|
|
833
832
|
:param request: the request object from the werkzeug library
|
|
834
833
|
:return: the response object what the handler responded, or a response which contains the error
|
|
835
834
|
"""
|
|
836
|
-
pass
|
|
837
835
|
|
|
838
836
|
@Request.application # type: ignore
|
|
839
837
|
def application(self, request: Request):
|
|
@@ -916,15 +914,15 @@ class HTTPServer(HTTPServerBase): # pylint: disable=too-many-instance-attribute
|
|
|
916
914
|
self,
|
|
917
915
|
host=DEFAULT_LISTEN_HOST,
|
|
918
916
|
port=DEFAULT_LISTEN_PORT,
|
|
919
|
-
ssl_context:
|
|
920
|
-
default_waiting_settings:
|
|
917
|
+
ssl_context: SSLContext | None = None,
|
|
918
|
+
default_waiting_settings: WaitingSettings | None = None,
|
|
921
919
|
):
|
|
922
920
|
"""
|
|
923
921
|
Initializes the instance.
|
|
924
922
|
"""
|
|
925
923
|
super().__init__(host, port, ssl_context)
|
|
926
924
|
|
|
927
|
-
self.ordered_handlers:
|
|
925
|
+
self.ordered_handlers: list[RequestHandler] = []
|
|
928
926
|
self.oneshot_handlers = RequestHandlerList()
|
|
929
927
|
self.handlers = RequestHandlerList()
|
|
930
928
|
self.permanently_failed = False
|
|
@@ -957,13 +955,13 @@ class HTTPServer(HTTPServerBase): # pylint: disable=too-many-instance-attribute
|
|
|
957
955
|
|
|
958
956
|
def expect_request(
|
|
959
957
|
self,
|
|
960
|
-
uri:
|
|
958
|
+
uri: str | URIPattern | Pattern[str],
|
|
961
959
|
method: str = METHOD_ALL,
|
|
962
|
-
data:
|
|
960
|
+
data: str | bytes | None = None,
|
|
963
961
|
data_encoding: str = "utf-8",
|
|
964
|
-
headers:
|
|
965
|
-
query_string:
|
|
966
|
-
header_value_matcher:
|
|
962
|
+
headers: Mapping[str, str] | None = None,
|
|
963
|
+
query_string: None | QueryMatcher | str | bytes | Mapping = None,
|
|
964
|
+
header_value_matcher: HVMATCHER_T | None = None,
|
|
967
965
|
handler_type: HandlerType = HandlerType.PERMANENT,
|
|
968
966
|
json: Any = UNDEFINED,
|
|
969
967
|
) -> RequestHandler:
|
|
@@ -1040,13 +1038,13 @@ class HTTPServer(HTTPServerBase): # pylint: disable=too-many-instance-attribute
|
|
|
1040
1038
|
|
|
1041
1039
|
def expect_oneshot_request(
|
|
1042
1040
|
self,
|
|
1043
|
-
uri:
|
|
1041
|
+
uri: str | URIPattern | Pattern[str],
|
|
1044
1042
|
method: str = METHOD_ALL,
|
|
1045
|
-
data:
|
|
1043
|
+
data: str | bytes | None = None,
|
|
1046
1044
|
data_encoding: str = "utf-8",
|
|
1047
|
-
headers:
|
|
1048
|
-
query_string:
|
|
1049
|
-
header_value_matcher:
|
|
1045
|
+
headers: Mapping[str, str] | None = None,
|
|
1046
|
+
query_string: None | QueryMatcher | str | bytes | Mapping = None,
|
|
1047
|
+
header_value_matcher: HVMATCHER_T | None = None,
|
|
1050
1048
|
json: Any = UNDEFINED,
|
|
1051
1049
|
) -> RequestHandler:
|
|
1052
1050
|
"""
|
|
@@ -1095,13 +1093,13 @@ class HTTPServer(HTTPServerBase): # pylint: disable=too-many-instance-attribute
|
|
|
1095
1093
|
|
|
1096
1094
|
def expect_ordered_request(
|
|
1097
1095
|
self,
|
|
1098
|
-
uri:
|
|
1096
|
+
uri: str | URIPattern | Pattern[str],
|
|
1099
1097
|
method: str = METHOD_ALL,
|
|
1100
|
-
data:
|
|
1098
|
+
data: str | bytes | None = None,
|
|
1101
1099
|
data_encoding: str = "utf-8",
|
|
1102
|
-
headers:
|
|
1103
|
-
query_string:
|
|
1104
|
-
header_value_matcher:
|
|
1100
|
+
headers: Mapping[str, str] | None = None,
|
|
1101
|
+
query_string: None | QueryMatcher | str | bytes | Mapping = None,
|
|
1102
|
+
header_value_matcher: HVMATCHER_T | None = None,
|
|
1105
1103
|
json: Any = UNDEFINED,
|
|
1106
1104
|
) -> RequestHandler:
|
|
1107
1105
|
"""
|
|
@@ -1185,7 +1183,7 @@ class HTTPServer(HTTPServerBase): # pylint: disable=too-many-instance-attribute
|
|
|
1185
1183
|
|
|
1186
1184
|
"""
|
|
1187
1185
|
if self._waiting_settings.stop_on_nohandler:
|
|
1188
|
-
self._set_waiting_result(False)
|
|
1186
|
+
self._set_waiting_result(value=False)
|
|
1189
1187
|
|
|
1190
1188
|
return super().respond_nohandler(request, self.format_matchers() + extra_message)
|
|
1191
1189
|
|
|
@@ -1263,7 +1261,7 @@ class HTTPServer(HTTPServerBase): # pylint: disable=too-many-instance-attribute
|
|
|
1263
1261
|
|
|
1264
1262
|
return response
|
|
1265
1263
|
|
|
1266
|
-
def _set_waiting_result(self, value: bool) -> None:
|
|
1264
|
+
def _set_waiting_result(self, value: bool) -> None: # noqa: FBT001
|
|
1267
1265
|
"""Set waiting_result
|
|
1268
1266
|
|
|
1269
1267
|
Setting is implemented as putting value to queue without waiting. If queue is full we simply ignore the
|
|
@@ -1274,14 +1272,14 @@ class HTTPServer(HTTPServerBase): # pylint: disable=too-many-instance-attribute
|
|
|
1274
1272
|
|
|
1275
1273
|
def _update_waiting_result(self) -> None:
|
|
1276
1274
|
if not self.oneshot_handlers and not self.ordered_handlers:
|
|
1277
|
-
self._set_waiting_result(True)
|
|
1275
|
+
self._set_waiting_result(value=True)
|
|
1278
1276
|
|
|
1279
1277
|
@contextmanager
|
|
1280
1278
|
def wait(
|
|
1281
1279
|
self,
|
|
1282
|
-
raise_assertions:
|
|
1283
|
-
stop_on_nohandler:
|
|
1284
|
-
timeout:
|
|
1280
|
+
raise_assertions: bool | None = None,
|
|
1281
|
+
stop_on_nohandler: bool | None = None,
|
|
1282
|
+
timeout: float | None = None,
|
|
1285
1283
|
):
|
|
1286
1284
|
"""Context manager to wait until the first of following event occurs: all ordered and oneshot handlers were
|
|
1287
1285
|
executed, unexpected request was received (if `stop_on_nohandler` is set to `True`), or time was out
|
|
@@ -1331,7 +1329,7 @@ class HTTPServer(HTTPServerBase): # pylint: disable=too-many-instance-attribute
|
|
|
1331
1329
|
waiting.complete(result=False)
|
|
1332
1330
|
if self._waiting_settings.raise_assertions:
|
|
1333
1331
|
raise AssertionError(
|
|
1334
|
-
"Wait timeout occurred, but some handlers left:\n
|
|
1332
|
+
"Wait timeout occurred, but some handlers left:\n{}".format(self.format_matchers())
|
|
1335
1333
|
)
|
|
1336
1334
|
if self._waiting_settings.raise_assertions and not waiting.result:
|
|
1337
1335
|
self.check_assertions()
|
|
@@ -54,14 +54,14 @@ def make_httpserver(httpserver_listen_address, httpserver_ssl_context):
|
|
|
54
54
|
server.stop()
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def pytest_sessionfinish(session, exitstatus): #
|
|
57
|
+
def pytest_sessionfinish(session, exitstatus): # noqa: ARG001
|
|
58
58
|
if Plugin.SERVER is not None:
|
|
59
59
|
Plugin.SERVER.clear()
|
|
60
60
|
if Plugin.SERVER.is_running():
|
|
61
61
|
Plugin.SERVER.stop()
|
|
62
62
|
|
|
63
63
|
|
|
64
|
-
@pytest.fixture
|
|
64
|
+
@pytest.fixture()
|
|
65
65
|
def httpserver(make_httpserver):
|
|
66
66
|
server = make_httpserver
|
|
67
67
|
yield server
|
|
@@ -78,7 +78,7 @@ def make_httpserver_ipv4(httpserver_ssl_context):
|
|
|
78
78
|
server.stop()
|
|
79
79
|
|
|
80
80
|
|
|
81
|
-
@pytest.fixture
|
|
81
|
+
@pytest.fixture()
|
|
82
82
|
def httpserver_ipv4(make_httpserver_ipv4):
|
|
83
83
|
server = make_httpserver_ipv4
|
|
84
84
|
yield server
|
|
@@ -95,7 +95,7 @@ def make_httpserver_ipv6(httpserver_ssl_context):
|
|
|
95
95
|
server.stop()
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
@pytest.fixture
|
|
98
|
+
@pytest.fixture()
|
|
99
99
|
def httpserver_ipv6(make_httpserver_ipv6):
|
|
100
100
|
server = make_httpserver_ipv6
|
|
101
101
|
yield server
|
|
@@ -40,7 +40,7 @@ def then_the_response_is_got_from(server_connection, response):
|
|
|
40
40
|
assert server_connection.get(timeout=9).json() == response
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
@pytest.fixture
|
|
43
|
+
@pytest.fixture()
|
|
44
44
|
def httpserver():
|
|
45
45
|
server = BlockingHTTPServer(timeout=1)
|
|
46
46
|
server.start()
|
|
@@ -97,7 +97,10 @@ def test_ignores_when_request_is_not_asserted(httpserver: BlockingHTTPServer):
|
|
|
97
97
|
)
|
|
98
98
|
|
|
99
99
|
with when_a_request_is_being_sent_to_the_server(request) as server_connection:
|
|
100
|
-
assert
|
|
100
|
+
assert (
|
|
101
|
+
server_connection.get(timeout=9).text == "No handler found for request "
|
|
102
|
+
f"<Request 'http://localhost:{httpserver.port}/my/path' [GET]> with data b''."
|
|
103
|
+
)
|
|
101
104
|
|
|
102
105
|
|
|
103
106
|
def test_raises_assertion_error_when_request_was_not_responded(httpserver: BlockingHTTPServer):
|
|
@@ -7,7 +7,7 @@ from pytest_httpserver import HTTPServer
|
|
|
7
7
|
|
|
8
8
|
def test_check_assertions_raises_handler_assertions(httpserver: HTTPServer):
|
|
9
9
|
def handler(_):
|
|
10
|
-
assert
|
|
10
|
+
assert False # noqa: PT015
|
|
11
11
|
|
|
12
12
|
httpserver.expect_request("/foobar").respond_with_handler(handler)
|
|
13
13
|
|
|
@@ -29,7 +29,7 @@ def test_check_handler_errors_raises_handler_error(httpserver: HTTPServer):
|
|
|
29
29
|
|
|
30
30
|
httpserver.check_assertions()
|
|
31
31
|
|
|
32
|
-
with pytest.raises(ValueError):
|
|
32
|
+
with pytest.raises(ValueError): # noqa: PT011
|
|
33
33
|
httpserver.check_handler_errors()
|
|
34
34
|
|
|
35
35
|
|
|
@@ -48,10 +48,10 @@ def test_check_handler_errors_correct_order(httpserver: HTTPServer):
|
|
|
48
48
|
|
|
49
49
|
httpserver.check_assertions()
|
|
50
50
|
|
|
51
|
-
with pytest.raises(ValueError):
|
|
51
|
+
with pytest.raises(ValueError): # noqa: PT011
|
|
52
52
|
httpserver.check_handler_errors()
|
|
53
53
|
|
|
54
|
-
with pytest.raises(OSError):
|
|
54
|
+
with pytest.raises(OSError): # noqa: PT011
|
|
55
55
|
httpserver.check_handler_errors()
|
|
56
56
|
|
|
57
57
|
httpserver.check_handler_errors()
|
|
@@ -69,7 +69,7 @@ def test_missing_matcher_raises_exception(httpserver):
|
|
|
69
69
|
|
|
70
70
|
def test_check_raises_errors_in_order(httpserver):
|
|
71
71
|
def handler1(_):
|
|
72
|
-
assert
|
|
72
|
+
assert False # noqa: PT015
|
|
73
73
|
|
|
74
74
|
def handler2(_):
|
|
75
75
|
pass # does nothing
|
|
@@ -88,5 +88,5 @@ def test_check_raises_errors_in_order(httpserver):
|
|
|
88
88
|
with pytest.raises(AssertionError):
|
|
89
89
|
httpserver.check()
|
|
90
90
|
|
|
91
|
-
with pytest.raises(ValueError):
|
|
91
|
+
with pytest.raises(ValueError): # noqa: PT011
|
|
92
92
|
httpserver.check()
|
|
@@ -36,5 +36,5 @@ def test_json_matcher_with_invalid_json(httpserver: HTTPServer):
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def test_data_and_json_mutually_exclusive(httpserver: HTTPServer):
|
|
39
|
-
with pytest.raises(ValueError):
|
|
39
|
+
with pytest.raises(ValueError): # noqa: PT011
|
|
40
40
|
httpserver.expect_request("/foo", json={}, data="foo")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import urllib.parse
|
|
2
|
-
from typing import List
|
|
3
|
-
from typing import Tuple
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
@@ -17,6 +17,6 @@ parse_qsl_semicolon_cases = [
|
|
|
17
17
|
]
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
@pytest.mark.parametrize("qs,expected", parse_qsl_semicolon_cases)
|
|
21
|
-
def test_qsl(qs: str, expected:
|
|
20
|
+
@pytest.mark.parametrize(("qs", "expected"), parse_qsl_semicolon_cases)
|
|
21
|
+
def test_qsl(qs: str, expected: list[tuple[bytes, bytes]]):
|
|
22
22
|
assert urllib.parse.parse_qsl(qs, keep_blank_values=True) == expected
|
|
@@ -9,7 +9,7 @@ PORT_KEY = "PYTEST_HTTPSERVER_PORT"
|
|
|
9
9
|
HOST_KEY = "PYTEST_HTTPSERVER_HOST"
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
@pytest.fixture
|
|
12
|
+
@pytest.fixture()
|
|
13
13
|
def tmpenv():
|
|
14
14
|
old_vars = {}
|
|
15
15
|
for key in (HOST_KEY, PORT_KEY):
|
|
@@ -37,7 +37,7 @@ def test_port_changing_by_environment(httpserver: HTTPServer):
|
|
|
37
37
|
assert httpserver.port == int(os.environ[PORT_KEY])
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
def test_get_httpserver_listen_address_with_env(tmpenv):
|
|
40
|
+
def test_get_httpserver_listen_address_with_env(tmpenv): # noqa: ARG001
|
|
41
41
|
address = get_httpserver_listen_address()
|
|
42
42
|
assert address[0] == "5.5.5.5"
|
|
43
43
|
assert address[1] == 12345
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
from __future__ import annotations
|
|
3
2
|
|
|
4
3
|
import email
|
|
5
4
|
import re
|
|
@@ -10,11 +9,13 @@ import zipfile
|
|
|
10
9
|
from dataclasses import dataclass
|
|
11
10
|
from pathlib import Path
|
|
12
11
|
from typing import Iterable
|
|
13
|
-
from typing import Tuple
|
|
14
12
|
|
|
15
13
|
import pytest
|
|
16
14
|
import toml
|
|
17
15
|
|
|
16
|
+
# TODO: skip if poetry is not available or add mark to test it explicitly
|
|
17
|
+
|
|
18
|
+
|
|
18
19
|
pytestmark = pytest.mark.release
|
|
19
20
|
|
|
20
21
|
NAME = "pytest-httpserver"
|
|
@@ -45,7 +46,7 @@ class Wheel:
|
|
|
45
46
|
|
|
46
47
|
def extract(self):
|
|
47
48
|
with zipfile.ZipFile(self.path) as zf:
|
|
48
|
-
zf.extractall(self.wheel_out_dir)
|
|
49
|
+
zf.extractall(self.wheel_out_dir) # noqa: S202
|
|
49
50
|
|
|
50
51
|
def get_meta(self, version: str, name: str = NAME_UNDERSCORE) -> email.message.Message:
|
|
51
52
|
metadata_path = self.wheel_out_dir.joinpath(f"{name}-{version}.dist-info", "METADATA")
|
|
@@ -65,7 +66,7 @@ class Sdist:
|
|
|
65
66
|
|
|
66
67
|
def extract(self):
|
|
67
68
|
with tarfile.open(self.path, mode="r:gz") as tf:
|
|
68
|
-
tf.extractall(self.sdist_out_dir)
|
|
69
|
+
tf.extractall(self.sdist_out_dir) # noqa: S202
|
|
69
70
|
|
|
70
71
|
|
|
71
72
|
@dataclass
|
|
@@ -104,7 +105,7 @@ def version(pyproject) -> str:
|
|
|
104
105
|
return pyproject["tool"]["poetry"]["version"]
|
|
105
106
|
|
|
106
107
|
|
|
107
|
-
def version_to_tuple(version: str) ->
|
|
108
|
+
def version_to_tuple(version: str) -> tuple:
|
|
108
109
|
return tuple([int(x) for x in version.split(".")])
|
|
109
110
|
|
|
110
111
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_example_query_params1.py
RENAMED
|
File without changes
|
{pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_example_query_params2.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_custom_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_json_matcher.py
RENAMED
|
File without changes
|
{pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_query_params_dict.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_timeout_requests.py
RENAMED
|
File without changes
|
{pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_url_matcher.py
RENAMED
|
File without changes
|
{pytest_httpserver-1.0.9 → pytest_httpserver-1.0.10}/tests/examples/test_howto_wait_success.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|