Skip to content

Commit b1e4d68

Browse files
committed
Bump pytest-beartype-tests to 2026.4.26; revert subprocess workaround in ci/test_custom_linters.py
1 parent 2165b37 commit b1e4d68

2 files changed

Lines changed: 61 additions & 64 deletions

File tree

ci/test_custom_linters.py

Lines changed: 60 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
"""Custom lint tests."""
22

3-
import subprocess
4-
import sys
53
from pathlib import Path
64

75
import pytest
@@ -21,61 +19,52 @@ def _ci_patterns(*, repository_root: Path) -> set[str]:
2119
return ci_patterns
2220

2321

22+
class _CollectPlugin:
23+
"""Pytest plugin that records the node IDs of collected items."""
24+
25+
def __init__(self) -> None:
26+
"""Start with an empty set of collected node IDs."""
27+
self.collected: set[str] = set()
28+
29+
def pytest_itemcollected(self, item: pytest.Item) -> None:
30+
"""Record each collected item's node ID."""
31+
self.collected.add(item.nodeid)
32+
33+
2434
@beartype
25-
def _collect(
26-
*, ci_pattern: str, repository_root: Path
27-
) -> subprocess.CompletedProcess[str]:
28-
"""Run ``pytest --collect-only`` for ``ci_pattern`` in a fresh
29-
subprocess.
30-
31-
A real subprocess (not ``pytest.main``) is used so that plugin state
32-
-- notably ``pytest-beartype-tests`` re-wrapping the same test
33-
functions -- does not accumulate across iterations and trigger
34-
``Cannot stringify annotation containing string formatting`` under
35-
Python 3.14 deferred annotations.
36-
See https://github.com/adamtheturtle/pytest-beartype-tests/issues/30.
37-
"""
38-
return subprocess.run(
35+
def _tests_from_pattern(*, ci_pattern: str) -> set[str]:
36+
"""From a CI pattern, get all tests ``pytest`` would collect."""
37+
plugin = _CollectPlugin()
38+
pytest.main(
3939
args=[
40-
sys.executable,
41-
"-m",
42-
"pytest",
4340
"-q",
4441
"--collect-only",
4542
# Disable pytest-retry to avoid:
4643
# ```
4744
# ValueError: no option named 'filtered_exceptions'
4845
# ```
46+
# which causes the nested run to exit with INTERNAL_ERROR
47+
# before any items are collected.
4948
"-p",
5049
"no:pytest-retry",
50+
# Disable pytest-beartype-tests to avoid
51+
# https://github.com/beartype/beartype/issues/637 — wrapping
52+
# collected items with @beartype installs a buggy
53+
# __annotate_beartype__ closure on the underlying test
54+
# function, which crashes a subsequent nested collection on
55+
# Python 3.14.
56+
"-p",
57+
"no:pytest_beartype_tests",
5158
# Disable warnings to avoid many instances of:
5259
# ```
5360
# Unknown config option: retry_delay
5461
# ```
5562
"--disable-warnings",
5663
ci_pattern,
5764
],
58-
check=False,
59-
cwd=repository_root,
60-
capture_output=True,
61-
text=True,
65+
plugins=[plugin],
6266
)
63-
64-
65-
@beartype
66-
def _tests_from_pattern(*, ci_pattern: str, repository_root: Path) -> set[str]:
67-
"""From a CI pattern, get all tests ``pytest`` would collect."""
68-
result = _collect(
69-
ci_pattern=ci_pattern,
70-
repository_root=repository_root,
71-
)
72-
tests: set[str] = set()
73-
for line in result.stdout.splitlines():
74-
# We filter empty lines and lines which look like
75-
# "9 tests collected in 0.01s".
76-
if line and "collected in" not in line:
77-
tests.add(line)
78-
return tests
67+
return plugin.collected
7968

8069

8170
def test_ci_patterns_valid(request: pytest.FixtureRequest) -> None:
@@ -84,45 +73,53 @@ def test_ci_patterns_valid(request: pytest.FixtureRequest) -> None:
8473
test in
8574
the test suite.
8675
"""
87-
repository_root = request.config.rootpath
88-
ci_patterns = _ci_patterns(repository_root=repository_root)
76+
ci_patterns = _ci_patterns(repository_root=request.config.rootpath)
8977

9078
for ci_pattern in ci_patterns:
91-
result = _collect(
92-
ci_pattern=ci_pattern,
93-
repository_root=repository_root,
94-
)
95-
message = (
96-
f'"{ci_pattern}" does not match any tests.\n'
97-
f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}"
79+
collect_only_result = pytest.main(
80+
args=[
81+
"--collect-only",
82+
ci_pattern,
83+
# Disable pytest-retry to avoid:
84+
# ```
85+
# ValueError: no option named 'filtered_exceptions'
86+
# ````
87+
"-p",
88+
"no:pytest-retry",
89+
# Disable pytest-beartype-tests to avoid
90+
# https://github.com/beartype/beartype/issues/637 —
91+
# wrapping collected items with @beartype installs a
92+
# buggy __annotate_beartype__ closure on the underlying
93+
# test function, which crashes a subsequent nested
94+
# collection on Python 3.14.
95+
"-p",
96+
"no:pytest_beartype_tests",
97+
# Disable warnings to avoid many instances of:
98+
# ```
99+
# Unknown config option: retry_delay
100+
# ```
101+
"--disable-warnings",
102+
],
98103
)
99-
assert result.returncode == 0, message
104+
105+
message = f'"{ci_pattern}" does not match any tests.'
106+
assert collect_only_result == 0, message
100107

101108

102109
def test_tests_collected_once(request: pytest.FixtureRequest) -> None:
103110
"""Each test in the test suite is collected exactly once.
104111
105112
This does not necessarily mean that they are run - they may be skipped.
106113
"""
107-
repository_root = request.config.rootpath
108-
ci_patterns = _ci_patterns(repository_root=repository_root)
109-
all_tests = _tests_from_pattern(
110-
ci_pattern=".",
111-
repository_root=repository_root,
112-
)
114+
ci_patterns = _ci_patterns(repository_root=request.config.rootpath)
115+
all_tests = _tests_from_pattern(ci_pattern=".")
113116
assert all_tests
114117
tests_to_patterns: dict[str, set[str]] = {}
115118

116119
for pattern in ci_patterns:
117-
tests = _tests_from_pattern(
118-
ci_pattern=pattern,
119-
repository_root=repository_root,
120-
)
120+
tests = _tests_from_pattern(ci_pattern=pattern)
121121
for test in tests:
122-
if test in tests_to_patterns:
123-
tests_to_patterns[test].add(pattern)
124-
else:
125-
tests_to_patterns[test] = {pattern}
122+
tests_to_patterns.setdefault(test, set()).add(pattern)
126123

127124
for test_name, patterns in tests_to_patterns.items():
128125
message = (

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ optional-dependencies.dev = [
7676
"pyright==1.1.409",
7777
"pyroma==5.0.1",
7878
"pytest==9.0.3",
79-
"pytest-beartype-tests==2026.4.20",
79+
"pytest-beartype-tests==2026.4.26",
8080
"pytest-retry==1.7.0",
8181
"pytest-xdist==3.8.0",
8282
"pyyaml==6.0.3",

0 commit comments

Comments
 (0)