Skip to content

Commit 485e870

Browse files
committed
Fixture based server options
Enable users to specify options for the server via fixtures. We use a dataclass which keeps the options (so it is extendable later on), which is returned by a fixture. This fixture can be overridden by the user to customize the options. The other options: host, port, ssl_context are still provided by their own fixtures and this is kept for backward compatibility.
1 parent 310529c commit 485e870

5 files changed

Lines changed: 92 additions & 3 deletions

File tree

pytest_httpserver/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"BlockingHTTPServer",
1111
"BlockingRequestHandler",
1212
"Error",
13+
"ExtraOptions",
1314
"HTTPServer",
1415
"HTTPServerError",
1516
"HeaderValueMatcher",
@@ -27,6 +28,7 @@
2728
from .httpserver import METHOD_ALL
2829
from .httpserver import URI_DEFAULT
2930
from .httpserver import Error
31+
from .httpserver import ExtraOptions
3032
from .httpserver import HeaderValueMatcher
3133
from .httpserver import HTTPServer
3234
from .httpserver import HTTPServerError

pytest_httpserver/httpserver.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from contextlib import contextmanager
1919
from contextlib import suppress
2020
from copy import copy
21+
from dataclasses import dataclass
2122
from enum import Enum
2223
from http import HTTPStatus
2324
from re import Pattern
@@ -947,6 +948,13 @@ def format_host(host: str) -> str:
947948
return host
948949

949950

951+
@dataclass
952+
class ExtraOptions:
953+
default_waiting_settings: WaitingSettings | None = None
954+
threaded: bool = False
955+
startup_timeout: float | None = None
956+
957+
950958
class HTTPServer(HTTPServerBase): # pylint: disable=too-many-instance-attributes
951959
"""
952960
Server instance which manages handlers to serve pre-defined requests.
@@ -1001,6 +1009,19 @@ def __init__(
10011009
self.startup_timeout = startup_timeout
10021010
self._readiness_check_pending = False
10031011

1012+
@classmethod
1013+
def with_extra_options(
1014+
cls, host: str, port: int, ssl_context: SSLContext | None, extra_options: ExtraOptions
1015+
) -> Self:
1016+
return cls(
1017+
host,
1018+
port,
1019+
ssl_context,
1020+
default_waiting_settings=extra_options.default_waiting_settings,
1021+
threaded=extra_options.threaded,
1022+
startup_timeout=extra_options.startup_timeout,
1023+
)
1024+
10041025
def start(self) -> None:
10051026
super().start()
10061027
self._readiness_check_pending = self.startup_timeout is not None

pytest_httpserver/pytest_plugin.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import pytest
77

8+
from .httpserver import ExtraOptions
89
from .httpserver import HTTPServer
910

1011
if TYPE_CHECKING:
@@ -44,18 +45,29 @@ def httpserver_ssl_context() -> None:
4445
return None
4546

4647

48+
@pytest.fixture(scope="session")
49+
def httpserver_extra_options() -> ExtraOptions:
50+
return ExtraOptions()
51+
52+
4753
@pytest.fixture(scope="session")
4854
def make_httpserver(
4955
httpserver_listen_address: tuple[str | None, int | None],
5056
httpserver_ssl_context: SSLContext | None,
57+
httpserver_extra_options: ExtraOptions,
5158
) -> Generator[HTTPServer, None, None]:
5259
host, port = httpserver_listen_address
5360
if not host:
5461
host = HTTPServer.DEFAULT_LISTEN_HOST
5562
if not port:
5663
port = HTTPServer.DEFAULT_LISTEN_PORT
5764

58-
server = HTTPServer(host=host, port=port, ssl_context=httpserver_ssl_context)
65+
server = HTTPServer.with_extra_options(
66+
host=host,
67+
port=port,
68+
ssl_context=httpserver_ssl_context,
69+
extra_options=httpserver_extra_options,
70+
)
5971
server.start()
6072
yield server
6173
server.clear()
@@ -80,8 +92,14 @@ def httpserver(make_httpserver: HTTPServer) -> HTTPServer:
8092
@pytest.fixture(scope="session")
8193
def make_httpserver_ipv4(
8294
httpserver_ssl_context: SSLContext | None,
95+
httpserver_extra_options: ExtraOptions,
8396
) -> Generator[HTTPServer, None, None]:
84-
server = HTTPServer(host="127.0.0.1", port=0, ssl_context=httpserver_ssl_context)
97+
server = HTTPServer.with_extra_options(
98+
host="127.0.0.1",
99+
port=0,
100+
ssl_context=httpserver_ssl_context,
101+
extra_options=httpserver_extra_options,
102+
)
85103
server.start()
86104
yield server
87105
server.clear()
@@ -99,8 +117,14 @@ def httpserver_ipv4(make_httpserver_ipv4: HTTPServer) -> HTTPServer:
99117
@pytest.fixture(scope="session")
100118
def make_httpserver_ipv6(
101119
httpserver_ssl_context: SSLContext | None,
120+
httpserver_extra_options: ExtraOptions,
102121
) -> Generator[HTTPServer, None, None]:
103-
server = HTTPServer(host="::1", port=0, ssl_context=httpserver_ssl_context)
122+
server = HTTPServer.with_extra_options(
123+
host="::1",
124+
port=0,
125+
ssl_context=httpserver_ssl_context,
126+
extra_options=httpserver_extra_options,
127+
)
104128
server.start()
105129
yield server
106130
server.clear()

tests/test_extra_options.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from inspect import signature
2+
3+
from pytest_httpserver import ExtraOptions
4+
from pytest_httpserver import HTTPServer
5+
from pytest_httpserver import WaitingSettings
6+
7+
8+
def test_httpserver_extra_options():
9+
extra_options = ExtraOptions(
10+
startup_timeout=60,
11+
threaded=True,
12+
default_waiting_settings=WaitingSettings(timeout=5),
13+
)
14+
15+
server = HTTPServer.with_extra_options(
16+
host="localhost",
17+
port=0,
18+
ssl_context=None,
19+
extra_options=extra_options,
20+
)
21+
22+
assert server.startup_timeout == 60
23+
assert server.threaded is True
24+
assert server.default_waiting_settings.timeout == 5
25+
26+
27+
def test_constructor_signature_matches_with_extra_options():
28+
# compares that ExtraOptions default values matches with HTTPServer
29+
# constructor defaults
30+
31+
httpserver_sig = signature(HTTPServer.__init__)
32+
extra_options_sig = signature(ExtraOptions.__init__)
33+
for param_name, extra_param in extra_options_sig.parameters.items():
34+
if param_name == "self":
35+
continue
36+
httpserver_param = httpserver_sig.parameters.get(param_name)
37+
assert httpserver_param is not None, f"Parameter {param_name} missing in HTTPServer"
38+
assert httpserver_param.default == extra_param.default, (
39+
f"Default value for parameter {param_name} does not match: "
40+
f"{httpserver_param.default} != {extra_param.default}"
41+
)

tests/test_release.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ def test_sdist_contents(build: Build, version: str):
219219
"examples",
220220
"test_bake.py",
221221
"test_blocking_httpserver.py",
222+
"test_extra_options.py",
222223
"test_handler_errors.py",
223224
"test_headers.py",
224225
"test_hooks.py",

0 commit comments

Comments
 (0)