Skip to content

Commit 5e2c658

Browse files
ci: Remove pytest-localserver from requirements-testing.txt (#6612)
Remove the package from `requirements-testing.txt` and add it only as a dependency of the test suites that rely on it. Add a dependency on `werkzeug`, which was transitively pulled in by `pytest-localserver`. Add `dataclasses` as a dependency for `sanic` tests on Python 3.6, to avoid an unhandled `ImportError` that arose after moving the `pytest-localserver` dependency. This is part of environment cleanup before moving the remaining testing dependencies to a uv dependency group.
1 parent c04b944 commit 5e2c658

9 files changed

Lines changed: 1971 additions & 2775 deletions

File tree

requirements-testing.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
pytest>=6.0.0
22
pytest-cov
33
pytest-forked
4-
pytest-localserver
54
pytest-timeout
65
executing
76
asttokens

scripts/populate_tox/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@
433433
"deps": {
434434
"*": ["websockets<11.0", "aiohttp"],
435435
">=22": ["sanic-testing"],
436-
"py3.6": ["aiocontextvars==0.2.1"],
436+
"py3.6": ["aiocontextvars==0.2.1", "dataclasses"],
437437
"py3.8": ["tracerite<1.1.2"],
438438
},
439439
"num_versions": 4,

scripts/populate_tox/package_dependencies.jsonl

Lines changed: 469 additions & 396 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/populate_tox/releases.jsonl

Lines changed: 5 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/populate_tox/tox.jinja

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,10 @@ deps =
8383
common: jsonschema
8484
common: pysocks
8585
common: pytest-asyncio
86+
common: pytest_localserver
8687
common: httpcore[asyncio]
8788
common: socksio
89+
common: werkzeug
8890
# See https://github.com/pytest-dev/pytest/issues/9621
8991
# and https://github.com/pytest-dev/pytest-forked/issues/67
9092
# for justification of the upper bound on pytest
@@ -105,8 +107,10 @@ deps =
105107
gevent: jsonschema
106108
gevent: pysocks
107109
gevent: pytest-asyncio
110+
gevent: pytest_localserver
108111
gevent: setuptools<82
109112
gevent: socksio
113+
gevent: werkzeug
110114
{py3.10,py3.11}-gevent: zope.event<5.0.0
111115
{py3.10,py3.11}-gevent: zope.interface<8.0
112116

tests/conftest.py

Lines changed: 66 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,17 @@
2424
jsonschema = None
2525

2626
import pytest
27-
from pytest_localserver.http import WSGIServer
28-
from werkzeug.wrappers import Request, Response
27+
28+
try:
29+
from pytest_localserver.http import WSGIServer
30+
except ImportError:
31+
WSGIServer = None
32+
33+
try:
34+
from werkzeug.wrappers import Request, Response
35+
except ImportError:
36+
Request = None
37+
Response = None
2938

3039
try:
3140
from starlette.testclient import TestClient
@@ -1624,53 +1633,61 @@ def __ne__(self, other):
16241633
CapturedData = namedtuple("CapturedData", ["path", "event", "envelope", "compressed"])
16251634

16261635

1627-
class CapturingServer(WSGIServer):
1628-
def __init__(self, host="127.0.0.1", port=0, ssl_context=None):
1629-
WSGIServer.__init__(self, host, port, self, ssl_context=ssl_context)
1630-
self.code = 204
1631-
self.headers = {}
1632-
self.captured = []
1633-
1634-
def respond_with(self, code=200, headers=None):
1635-
self.code = code
1636-
if headers:
1637-
self.headers = headers
1638-
1639-
def clear_captured(self):
1640-
del self.captured[:]
1636+
@pytest.fixture(scope="module")
1637+
def wsgi_capturing_server():
1638+
assert WSGIServer is not None
1639+
1640+
class CapturingServer(WSGIServer):
1641+
def __init__(self, host="127.0.0.1", port=0, ssl_context=None):
1642+
WSGIServer.__init__(self, host, port, self, ssl_context=ssl_context)
1643+
self.code = 204
1644+
self.headers = {}
1645+
self.captured = []
1646+
1647+
def respond_with(self, code=200, headers=None):
1648+
self.code = code
1649+
if headers:
1650+
self.headers = headers
1651+
1652+
def clear_captured(self):
1653+
del self.captured[:]
1654+
1655+
def __call__(self, environ, start_response):
1656+
"""
1657+
This is the WSGI application.
1658+
"""
1659+
assert Request is not None
1660+
assert Response is not None
1661+
1662+
request = Request(environ)
1663+
event = envelope = None
1664+
content_encoding = request.headers.get("content-encoding")
1665+
if content_encoding == "gzip":
1666+
rdr = gzip.GzipFile(fileobj=io.BytesIO(request.data))
1667+
compressed = True
1668+
elif content_encoding == "br":
1669+
rdr = io.BytesIO(brotli.decompress(request.data))
1670+
compressed = True
1671+
else:
1672+
rdr = io.BytesIO(request.data)
1673+
compressed = False
16411674

1642-
def __call__(self, environ, start_response):
1643-
"""
1644-
This is the WSGI application.
1645-
"""
1646-
request = Request(environ)
1647-
event = envelope = None
1648-
content_encoding = request.headers.get("content-encoding")
1649-
if content_encoding == "gzip":
1650-
rdr = gzip.GzipFile(fileobj=io.BytesIO(request.data))
1651-
compressed = True
1652-
elif content_encoding == "br":
1653-
assert brotli is not None
1654-
rdr = io.BytesIO(brotli.decompress(request.data))
1655-
compressed = True
1656-
else:
1657-
rdr = io.BytesIO(request.data)
1658-
compressed = False
1659-
1660-
if request.mimetype == "application/json":
1661-
event = parse_json(rdr.read())
1662-
else:
1663-
envelope = Envelope.deserialize_from(rdr)
1664-
1665-
self.captured.append(
1666-
CapturedData(
1667-
path=request.path,
1668-
event=event,
1669-
envelope=envelope,
1670-
compressed=compressed,
1675+
if request.mimetype == "application/json":
1676+
event = parse_json(rdr.read())
1677+
else:
1678+
envelope = Envelope.deserialize_from(rdr)
1679+
1680+
self.captured.append(
1681+
CapturedData(
1682+
path=request.path,
1683+
event=event,
1684+
envelope=envelope,
1685+
compressed=compressed,
1686+
)
16711687
)
1672-
)
16731688

1674-
response = Response(status=self.code)
1675-
response.headers.extend(self.headers)
1676-
return response(environ, start_response)
1689+
response = Response(status=self.code)
1690+
response.headers.extend(self.headers)
1691+
return response(environ, start_response)
1692+
1693+
return CapturingServer()

tests/test_gevent.py

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

77
import sentry_sdk
88
from sentry_sdk._compat import PY37, PY38
9-
from tests.conftest import CapturingServer
109

1110
pytest.importorskip("gevent")
1211

@@ -25,18 +24,17 @@ def monkeypatched_gevent():
2524

2625

2726
@pytest.fixture
28-
def capturing_server(request):
29-
server = CapturingServer()
30-
server.start()
31-
request.addfinalizer(server.stop)
32-
return server
27+
def capturing_server(request, wsgi_capturing_server):
28+
wsgi_capturing_server.start()
29+
request.addfinalizer(wsgi_capturing_server.stop)
30+
return wsgi_capturing_server
3331

3432

3533
@pytest.fixture
36-
def make_client(request, capturing_server):
34+
def make_client(request, wsgi_capturing_server):
3735
def inner(**kwargs):
3836
return sentry_sdk.Client(
39-
"http://foobar@{}/132".format(capturing_server.url[len("http://") :]),
37+
"http://foobar@{}/132".format(wsgi_capturing_server.url[len("http://") :]),
4038
**kwargs,
4139
)
4240

tests/test_transport.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010

1111
import pytest
1212

13-
from tests.conftest import CapturingServer
14-
1513
try:
1614
import httpcore
1715
except (ImportError, ModuleNotFoundError):
@@ -73,9 +71,9 @@ def _make_async_transport_options(**overrides):
7371

7472

7573
@pytest.fixture(scope="module", autouse=True)
76-
def make_capturing_server(request):
74+
def make_capturing_server(request, wsgi_capturing_server):
7775
global server
78-
server = CapturingServer()
76+
server = wsgi_capturing_server
7977
server.start()
8078
request.addfinalizer(server.stop)
8179

0 commit comments

Comments
 (0)