Skip to content

Commit 743fac7

Browse files
authored
Merge pull request #71 from graingert/configure-strict-pytest
2 parents a28a7a3 + 4a03fae commit 743fac7

File tree

6 files changed

+71
-33
lines changed

6 files changed

+71
-33
lines changed

pytest_httpbin/plugin.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,14 @@
66

77
@pytest.fixture(scope="session")
88
def httpbin(request):
9-
server = serve.Server(application=httpbin_app)
10-
server.start()
11-
request.addfinalizer(server.stop)
12-
return server
9+
with serve.Server(application=httpbin_app) as server:
10+
yield server
1311

1412

1513
@pytest.fixture(scope="session")
1614
def httpbin_secure(request):
17-
server = serve.SecureServer(application=httpbin_app)
18-
server.start()
19-
request.addfinalizer(server.stop)
20-
return server
15+
with serve.SecureServer(application=httpbin_app) as server:
16+
yield server
2117

2218

2319
@pytest.fixture(scope="session", params=["http", "https"])

pytest_httpbin/serve.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,16 @@ def finish_request(self, request, client_address):
6060
"""
6161
request.settimeout(1.0)
6262
try:
63-
ssock = ssl.wrap_socket(
64-
request,
65-
keyfile=os.path.join(CERT_DIR, "key.pem"),
66-
certfile=os.path.join(CERT_DIR, "cert.pem"),
67-
server_side=True,
68-
suppress_ragged_eofs=False,
63+
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
64+
context.load_cert_chain(
65+
os.path.join(CERT_DIR, "cert.pem"),
66+
os.path.join(CERT_DIR, "key.pem"),
6967
)
70-
self.base_environ["HTTPS"] = "yes"
71-
self.RequestHandlerClass(ssock, client_address, self)
68+
with context.wrap_socket(
69+
request, server_side=True, suppress_ragged_eofs=False
70+
) as ssock:
71+
self.base_environ["HTTPS"] = "yes"
72+
self.RequestHandlerClass(ssock, client_address, self)
7273
except Exception as e:
7374
print("pytest-httpbin server hit an exception serving request: %s" % e)
7475
print("attempting to ignore so the rest of the tests can run")
@@ -106,6 +107,16 @@ def __del__(self):
106107
def start(self):
107108
self._thread.start()
108109

110+
def __enter__(self):
111+
self.start()
112+
return self
113+
114+
def __exit__(self, *args, **kwargs):
115+
self.stop()
116+
suppress_exc = self._server.__exit__(*args, **kwargs)
117+
self._thread.join()
118+
return suppress_exc
119+
109120
def __add__(self, other):
110121
return self.url + other
111122

setup.cfg

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,9 @@ disable-noqa = True
99
max-line-length = 88
1010
extend-ignore =
1111
E203, # whitespace before : is not PEP8 compliant (& conflicts with black)
12+
13+
14+
[tool:pytest]
15+
addopts = --strict-config --strict-markers
16+
filterwarnings = error
17+
xfail_strict = true

tests/test_server.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import contextlib
12
import os
3+
import re
4+
import socket
25

36
import pytest
4-
import requests
7+
import requests.exceptions
58
from httpbin import app as httpbin_app
69
from util import get_raw_http_response
710

@@ -40,9 +43,33 @@ def test_server_should_be_http_1_1(httpbin):
4043

4144

4245
def test_dont_crash_on_certificate_problems(httpbin_secure):
43-
with pytest.raises(Exception):
46+
with pytest.raises(requests.exceptions.SSLError):
4447
# this request used to hang
4548
requests.get(httpbin_secure + "/get", verify=True, cert=__file__)
49+
50+
# and this request would never happen
51+
requests.get(
52+
httpbin_secure + "/get",
53+
verify=True,
54+
)
55+
56+
57+
def test_dont_crash_on_handshake_timeout(httpbin_secure, capsys):
58+
with socket.socket() as sock:
59+
sock.connect((httpbin_secure.host, httpbin_secure.port))
60+
# this request used to hang
61+
assert sock.recv(1) == b""
62+
63+
assert (
64+
re.match(
65+
r"pytest-httpbin server hit an exception serving request: .* The "
66+
"handshake operation timed out\nattempting to ignore so the rest "
67+
"of the tests can run\n",
68+
capsys.readouterr().out,
69+
)
70+
is not None
71+
)
72+
4673
# and this request would never happen
4774
requests.get(
4875
httpbin_secure + "/get",
@@ -68,6 +95,7 @@ def test_fixed_port_environment_variables(protocol):
6895
# just have different port to avoid adrress already in use
6996
# if the second test run too fast after the first one (happens on pypy)
7097
port = 12345 + len(protocol)
98+
server = contextlib.nullcontext()
7199

72100
try:
73101
envvar_original = os.environ.get(envvar, None)
@@ -76,10 +104,7 @@ def test_fixed_port_environment_variables(protocol):
76104
assert server.port == port
77105
finally:
78106
# if we don't do this, it blocks:
79-
try:
80-
server.start()
81-
server.stop()
82-
except UnboundLocalError:
107+
with server:
83108
pass
84109

85110
# restore the original environ:

tests/util.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ def get_raw_http_response(host, port, path):
1414
]
1515

1616
# Connect to the server
17-
s = socket.socket()
18-
s.connect((host, port))
17+
with socket.socket() as s:
18+
s.connect((host, port))
1919

20-
# Send an HTTP request
21-
s.send(CRLF.join(request))
20+
# Send an HTTP request
21+
s.send(CRLF.join(request))
2222

23-
# Get the response (in several parts, if necessary)
24-
response = b""
25-
buffer = s.recv(4096)
26-
while buffer:
27-
response += buffer
23+
# Get the response (in several parts, if necessary)
24+
response = b""
2825
buffer = s.recv(4096)
26+
while buffer:
27+
response += buffer
28+
buffer = s.recv(4096)
2929

30-
return response
30+
return response

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ envlist = py37, py38, py39, py310, pypy3
1212
wheel = True
1313
wheel_build_env = build
1414
extras = test
15-
commands = pytest -v -s
15+
commands = pytest -v -s {posargs}
1616

1717
[testenv:build]
1818
# empty environment to build universal wheel once per tox invocation

0 commit comments

Comments
 (0)