Skip to content

Commit 27c6c0b

Browse files
committed
build(deps): drop Python 3.9, bump httpx/pytest-httpx, adapt async mocker
Raise the runtime floor to Python 3.10 (verified on 3.10 and 3.12) and modernize the HTTP toolchain: - python >=3.10,<4 (drop 3.9 from deps and CI matrix) - httpx >=0.28, pytest-httpx >=0.35, inflect >=7 - dev: pytest >=8,<9, pytest-asyncio >=0.24,<2, pytest-cov <7 pytest-httpx >=0.35 (which pulls httpx 0.28) is the only path to httpx 0.28, and it forces the pytest/pytest-asyncio bumps transitively. Its 0.31+ API changes break the async mocker, so adapt it: - HTTPXMock now requires _HTTPXMockOptions; construct it with assert_all_requests_were_expected=False and can_send_already_matched_responses=True to preserve the previous behaviour (one registered response answers retried requests). - reset() no longer performs assertions; call _assert_options() explicitly, gated by success, to keep failing on unrequested mocks. - match_content now serializes JSON with compact separators to match httpx 0.28's compact request-body serialization. Mark test_execute_error_with_reason (native httpx_mock fixture, retries on 500) with the matching httpx_mock options. All 399 tests pass.
1 parent 8d3ff5f commit 27c6c0b

5 files changed

Lines changed: 87 additions & 95 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
matrix:
17-
python-version: ['3.9', '3.10', '3.11', '3.12']
17+
python-version: ['3.10', '3.11', '3.12']
1818
steps:
1919
- name: Checkout
2020
uses: actions/checkout@v3

connect/client/testing/fluent.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import responses
1111
from pytest import MonkeyPatch
1212
from pytest_httpx import HTTPXMock
13+
from pytest_httpx._options import _HTTPXMockOptions
1314
from responses import matchers
1415

1516
from connect.client.fluent import _ConnectClientBase
@@ -165,7 +166,15 @@ def _get_namespace_class(self):
165166

166167

167168
_monkeypatch = MonkeyPatch()
168-
_async_mocker = HTTPXMock()
169+
# Preserve the pre-0.31 pytest-httpx behaviour our mocker relies on: a single
170+
# registered response answers repeated (retried) requests, and unmatched
171+
# requests don't fail teardown by themselves.
172+
_async_mocker = HTTPXMock(
173+
_HTTPXMockOptions(
174+
assert_all_requests_were_expected=False,
175+
can_send_already_matched_responses=True,
176+
),
177+
)
169178

170179

171180
class AsyncConnectClientMocker(ConnectClientMocker):
@@ -194,8 +203,14 @@ async def mocked_handle_async_request(
194203
)
195204

196205
def reset(self, success=True):
197-
_async_mocker.reset(success)
198-
_monkeypatch.undo()
206+
try:
207+
if success:
208+
# pytest-httpx>=0.31 no longer asserts on reset(); do it explicitly
209+
# so unrequested mocks / unexpected requests still fail the test.
210+
_async_mocker._assert_options()
211+
finally:
212+
_async_mocker.reset()
213+
_monkeypatch.undo()
199214

200215
def mock(
201216
self,
@@ -222,7 +237,12 @@ def mock(
222237

223238
if match_body:
224239
if isinstance(match_body, (dict, list, tuple)):
225-
kwargs['match_content'] = json.dumps(match_body).encode('utf-8')
240+
# httpx>=0.28 serializes JSON request bodies compactly, so match
241+
# the same separators or match_content won't compare equal.
242+
kwargs['match_content'] = json.dumps(
243+
match_body,
244+
separators=(',', ':'),
245+
).encode('utf-8')
226246
else:
227247
kwargs['match_content'] = match_body
228248

poetry.lock

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

pyproject.toml

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ classifiers = [
1717
"Environment :: Console",
1818
"Operating System :: OS Independent",
1919
"Intended Audience :: Developers",
20-
"Programming Language :: Python :: 3.9",
2120
"Programming Language :: Python :: 3.10",
2221
"Programming Language :: Python :: 3.11",
2322
"Programming Language :: Python :: 3.12",
@@ -35,20 +34,20 @@ keywords = [
3534
]
3635

3736
[tool.poetry.dependencies]
38-
python = ">=3.9,<4"
37+
python = ">=3.10,<4"
3938
connect-markdown-renderer = "^3"
4039
PyYAML = ">=5.3.1"
4140
requests = ">=2.23"
42-
inflect = ">=4.1"
43-
httpx = ">=0.23"
41+
inflect = ">=7"
42+
httpx = ">=0.28"
4443
asgiref = "^3.3.4"
4544
responses = ">=0.14.0,<1"
46-
pytest-httpx = ">=0.27,<0.30"
45+
pytest-httpx = ">=0.35"
4746

4847
[tool.poetry.group.test.dependencies]
4948
black = "23.*"
50-
pytest = ">=6.1.2,<8"
51-
pytest-cov = ">=2.10.1,<5"
49+
pytest = ">=8,<9"
50+
pytest-cov = ">=2.10.1,<7"
5251
pytest-mock = "^3.10"
5352
coverage = {extras = ["toml"], version = ">=5.3,<7"}
5453
flake8 = ">=6"
@@ -62,7 +61,7 @@ flake8-isort = "^6.0"
6261
flake8-broken-line = ">=1.0"
6362
flake8-pyproject = "^1.2.3"
6463
isort = "^5.10"
65-
pytest-asyncio = "^0.15.1"
64+
pytest-asyncio = ">=0.24,<2"
6665

6766
[tool.poetry.group.docs.dependencies]
6867
mkdocs = "^1.3.1"

tests/async_client/test_fluent.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,10 @@ async def test_execute_uparseable_connect_error(httpx_mock):
348348

349349

350350
@pytest.mark.asyncio
351+
@pytest.mark.httpx_mock(
352+
assert_all_requests_were_expected=False,
353+
can_send_already_matched_responses=True,
354+
)
351355
@pytest.mark.parametrize('encoding', ('utf-8', 'iso-8859-1'))
352356
async def test_execute_error_with_reason(httpx_mock, encoding):
353357
httpx_mock.add_response(

0 commit comments

Comments
 (0)