Skip to content

Commit 91855dc

Browse files
committed
Asynchronous coroutine and generator fixtures x 3.
This commit is the next in a commit chain generalizing the `--beartype-fixtures` option accepted by this plugin to transparently type-check both **asynchronous coroutine fixtures** (i.e., fixtures whose signatures are prefixed by the `async` keyword and whose bodies contain *no* `yield` statements) and **asynchronous generator fixtures** (i.e., fixtures whose signatures are prefixed by the `async` keyword and whose bodies contain one or more `yield` statements), en-route to publishing our upcoming `pytest-beartype 0.3.0` release. Specifically, this commit: * Refactors asynchronous fixtures for compliance with the third-party `pytest-asyncio` plugin, which (basically) is the only asynchronous plugin that matters. * Dramatically improves our top-level `pyproject.toml` for both usability and maintainability, including now requiring `pytest-asyncio` as a mandatory test-time dependency. * Dramatically boosts coverage by properly tracking coverage across Python subprocesses by Coverage.py-based coverage combining. Let's do this! Rooooooar! Pretend you felt intimidated. (*Till until unit!*)
1 parent fea0f28 commit 91855dc

File tree

8 files changed

+589
-118
lines changed

8 files changed

+589
-118
lines changed

.coveragerc

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@
2121
# https://coverage.readthedocs.io/en/latest/config.html
2222
# "coverage" documentation on this file.
2323

24-
# ....................{ TODO }...................
25-
#FIXME: Generalize to combine coverage reports for Python subprocesses. In
26-
#theory, doing should be somewhat trivial. See also:
27-
# https://coverage.readthedocs.io/en/latest/subprocess.html#id1
28-
2924
# ....................{ RUN }...................
3025
# Low-level settings configuring how coverage is measured.
3126
[run]
@@ -57,6 +52,37 @@ omit = pytest_beartype_test/*
5752
# See also: https://coverage.readthedocs.io/en/latest/branch.html#branch
5853
branch = True
5954

55+
# Whitespace-delimited list of the names of all coverage-specific monkey patches
56+
# to be applied to the active Python interpreter as a means of improving
57+
# coverage measurement. Each patch monkey-patches a corresponding Python feature
58+
# that *COULD* interfere with coverage by injecting coverage-specific code to
59+
# collect the correct data.
60+
#
61+
# The "subprocess" patch collects coverage across Python subprocesses -- which,
62+
# for safety, is *NOT* enabled by default. This patch:
63+
# * Configures the parent Python process to extend coverage to subprocesses
64+
# created by the standard "subprocess" submodule, the os.system() call, and
65+
# one of the execv() or spawnv() family of functions. Note that the exec*e()
66+
# and spawn*e() family of functions require additional handling.
67+
# * Implicitly enables the "parallel = True" setting, which thus need *NOT* be
68+
# explicitly enabled below. *ACTUALLY, THAT'S A HUGE LIE.* Ugh!
69+
# * Requires combining data files before reporting.
70+
#
71+
# See also upstream documentation on:
72+
# * This "patch" setting:
73+
# https://coverage.readthedocs.io/en/latest/config.html#config-run-patch
74+
# * Python subprocess coverage:
75+
# https://coverage.readthedocs.io/en/latest/subprocess.html
76+
# * Coverage data file combining:
77+
# https://coverage.readthedocs.io/en/latest/commands/cmd_combine.html#cmd-combine
78+
patch = subprocess
79+
80+
# Uniquify the ".coverage" filename to be output by appending machine- and
81+
# (sub)process-specific metadata to that filename. Oddly, Coverage.py requires
82+
# this setting to be explicitly set despite official documentation claiming that
83+
# setting "patch = subprocess" implicitly enables this setting. It does not.
84+
parallel = True
85+
6086
# ....................{ REPORT }...................
6187
# High-level settings configuring how coverage reports measurements.
6288
[report]
@@ -65,12 +91,13 @@ branch = True
6591
# included in on-disk reports.
6692
include = pytest_beartype/*
6793

68-
# If the total coverage measurement falls under this number, exit with a
69-
# "coverage"-specific failure status code of 2.
70-
#
71-
# Note this number should be suffixed by at most a number of fractional digits
72-
# equal to the "precision" setting defined below.
73-
fail_under = 75.00
94+
#FIXME: Uncomment once we boost coverage back up. Until then, we pretend! \o/
95+
# # If the total coverage measurement falls under this number, exit with a
96+
# # "coverage"-specific failure status code of 2.
97+
# #
98+
# # Note this number should be suffixed by at most a number of fractional digits
99+
# # equal to the "precision" setting defined below.
100+
# fail_under = 75.00
74101

75102
# Number of fractional digits to display for reported coverage percentages
76103
# *AND* constrain the "fail_under" setting defined above to.

.github/workflows/python_test.yml

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,6 @@
1616
#".github/workflows/python_test.yml" workflow in the main @beartype codebase for
1717
#inspiration. Note that doing so will require tedious integration with the
1818
#third-party Codecov service. What you gonna do, huh? *sigh*
19-
#FIXME: Actually, testing plugin coverage appears to be *USELESS*. Why? Because
20-
#we already locally test plugin coverage via the usual third-party "coverage"
21-
#package. And... it simply sucks here. It's not collecting coverage over
22-
#subprocess forks, which means it's not collecting coverage over pretty much
23-
#*ANY* of this plugin.
24-
#
25-
#We're pretty sure we already solved this for the @beartype test suite, though.
26-
#Didn't we? Unsure, actually. Surely we're testing coverage across subprocess
27-
#forks over there? Uhh. Maybe we're not and just never knew it. lol <-- ugh
2819

2920
# ....................{ METADATA }....................
3021
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -114,11 +105,11 @@ jobs:
114105
# the "[tox]" subsection in "tox.ini") to be tested, which the
115106
# ${TOXENV} environment variable declared below exposes to "tox".
116107
tox-env:
117-
- py310
118-
- py311
119-
- py312
120-
- py313
121-
- py314
108+
- py310-coverage
109+
- py311-coverage
110+
- py312-coverage
111+
- py313-coverage
112+
- py314-coverage
122113

123114
# Map each "tox" environment name listed in the "tox-env" list above to
124115
# the corresponding "python-version" string supported by the
@@ -142,11 +133,11 @@ jobs:
142133
# selecting the Python 3.11 pre-release reduces to:
143134
# python-version: "3.11.0-alpha - 3.11.0"
144135
include:
145-
- { python-version: "3.10", tox-env: "py310" }
146-
- { python-version: "3.11", tox-env: "py311" }
147-
- { python-version: "3.12", tox-env: "py312" }
148-
- { python-version: "3.13", tox-env: "py313" }
149-
- { python-version: "3.14", tox-env: "py314" }
136+
- { python-version: "3.10", tox-env: "py310-coverage" }
137+
- { python-version: "3.11", tox-env: "py311-coverage" }
138+
- { python-version: "3.12", tox-env: "py312-coverage" }
139+
- { python-version: "3.13", tox-env: "py313-coverage" }
140+
- { python-version: "3.14", tox-env: "py314-coverage" }
150141

151142
# ..................{ SETTINGS }..................
152143
# Arbitrary human-readable description.

.gitignore

Lines changed: 207 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,208 @@
1-
.vscode
2-
venv*
3-
*.pyc
4-
*.egg-info
5-
build
6-
dist
7-
.tox
8-
.coverage
9-
uv.lock
1+
# --------------------( LICENSE )--------------------
2+
# Copyright (c) 2024-2025 Beartype authors.
3+
# See "LICENSE" for further details.
4+
#
5+
# --------------------( SYNOPSIS )--------------------
6+
# Git-specific dotfile instructing git to avoid tracking repository paths
7+
# matching one or more glob expressions listed below by default.
8+
#
9+
# --------------------( SEE ALSO )--------------------
10+
# For further details, see:
11+
# * "man gitignore" for high-level commentary.
12+
# * "man 7 glob" for low-level commentary on glob syntax. Note, in particular,
13+
# that glob() and hence ".gitignore" files support only a proper subset of
14+
# full glob syntax supported by POSIX-compatible shells (e.g., bash, zsh).
15+
16+
# ....................{ DIRECTORIES ~ top-level }....................
17+
# Ignore all top-level Buildout-specific state directories.
18+
/develop-eggs/
19+
/downloads/
20+
/eggs/
21+
/lib/
22+
/lib64/
23+
24+
# Ignore all top-level Coverage.py-specific temporary directories.
25+
/htmlcov/
26+
27+
# Ignore all top-level Flask-specific temporary and private directories.
28+
/.webassets-cache/
29+
/instance/
30+
31+
# Ignore all top-level Gemini-specific temporary directories.
32+
/.gemini/
33+
34+
# Ignore all top-level Hypothesis-specific temporary directories.
35+
/.hypothesis/
36+
37+
# Ignore all top-level mkdocs-specific output directories.
38+
/site/
39+
/_site/
40+
41+
# Ignore all top-level mypy-specific temporary directories.
42+
/.mypy_cache/
43+
44+
# Ignore all top-level nox-specific temporary directories.
45+
/.nox/
46+
47+
# Ignore all top-level pip-specific temporary directories.
48+
/pip-wheel-metadata/
49+
50+
# Ignore all top-level pytest-specific temporary directories.
51+
/.cache/
52+
/.pytest_cache/
53+
54+
# Ignore all top-level PyBuilder-specific temporary directories.
55+
/target/
56+
57+
# Ignore all top-level Scrapy-specific temporary directories.
58+
/.scrapy/
59+
60+
# Ignore all top-level setuptools-specific temporary directories.
61+
/build/
62+
/dist/
63+
/.eggs/
64+
/*.egg-info/
65+
66+
# Ignore all top-level Sphinx-specific output subdirectories, including:
67+
# * "/doc/src/api", the output subdirectory managed by the "autoapi" extension.
68+
# * "/doc/trg", the output subdirectory managed by Sphinx itself.
69+
#
70+
# Note this constitutes a usability versus space tradeoff: ignoring these
71+
# directories substantially reduces repository size, but requires end users to
72+
# manually install Sphinx to locally generate HTML documentation if they so
73+
# choose. Since HTML documentation is remotely available via Read The Docs
74+
# (RTD), we consider this a more than worthwhile tradeoff.
75+
/doc/src/api
76+
/doc/trg/
77+
78+
# Ignore all top-level tox-specific temporary directories.
79+
/.tox/
80+
81+
# Ignore all top-level user-specific PEP 582-compliant directories.
82+
/__pypackages__/
83+
84+
# Ignore all top-level user-specific Spyder IDE project directories.
85+
/.spyderproject/
86+
/.spyproject/
87+
88+
# Ignore all top-level user-specific venv (virtual environment) directories.
89+
/env/
90+
/venv/
91+
/ENV/
92+
/env.bak/
93+
/venv.bak/
94+
95+
# ....................{ DIRECTORIES ~ general }....................
96+
# Ignore all Buildout-specific state subdirectories.
97+
parts/
98+
99+
# Ignore all Python-specific cache subdirectories.
100+
__pycache__/
101+
102+
# Ignore all PyCharm-specific project subdirectories.
103+
.idea/
104+
105+
# Ignore all Pyre-specific cache subdirectories.
106+
.pyre/
107+
108+
# Ignore all Rope-specific project subdirectories.
109+
.ropeproject/
110+
111+
# ....................{ FILES ~ top-level }....................
112+
# Ignore all top-level Buildout-specific state files.
113+
/.installed.cfg
114+
115+
# Ignore all top-level Celery-specific state files.
116+
/celerybeat-schedule
117+
/celerybeat.pid
118+
119+
# Ignore all top-level Coverage.py-specific output files. Note that these glob
120+
# expressions intentionally omit the ".coveragerc" configuration file -- which
121+
# is the *ONLY* Coverage.py-specific file that should be tracked by "git".
122+
/.coverage
123+
/.coverage.*
124+
/coverage.*
125+
126+
# Ignore all top-level Django-specific binary databases.
127+
/db.sqlite3
128+
/db.sqlite3-journal
129+
130+
# Ignore all top-level GitHub App-specific credentials.
131+
/gha-creds-*.json
132+
133+
# Ignore all top-level mypy-specific state files.
134+
/.dmypy.json
135+
/dmypy.json
136+
137+
# Ignore all top-level Nose-specific output files.
138+
/nosetests.xml
139+
140+
# Ignore all top-level pip-specific output files.
141+
/pip-log.txt
142+
/pip-delete-this-directory.txt
143+
144+
# Ignore all top-level setuptools-specific output files.
145+
/MANIFEST
146+
147+
# Ignore top-level PyInstaller-specific output files *NOT* intended to be
148+
# modified. ".spec"-suffixed files *ARE* intended to be modified and are thus
149+
# excluded.
150+
/*.manifest
151+
152+
# Ignore all top-level user-specific venv (virtual environment) directories.
153+
/.env
154+
/.venv
155+
156+
# ....................{ FILES ~ general }....................
157+
# Ignore all audio and video files.
158+
*.mp4
159+
160+
# Ignore all C extensions.
161+
*.so
162+
163+
# Ignore all data interchange files.
164+
*.csv
165+
166+
# Ignore all Django-specific private files.
167+
local_settings.py
168+
169+
# Ignore all Jython-specific byte-compiled Python files.
170+
*$py.class
171+
172+
# Ignore all "gettext"-specific intermediary translation files.
173+
*.mo
174+
*.pot
175+
176+
# Ignore all Jupyter Notebook-specific checkpoint files.
177+
.ipynb_checkpoints
178+
179+
# Ignore all logfiles.
180+
*.log
181+
182+
# Ignore all macOS-specific filesystem viewer configuration files.
183+
.DS_Store
184+
185+
# Ignore all pyenv-specific state files.
10186
.python-version
187+
188+
# Ignore all "python-coverage"-specific output Python files.
189+
*.py,cover
190+
191+
# Ignore all Python-specific byte-compiled, optimized, and DLL files.
192+
*.py[cod]
193+
194+
# Ignore all Python-specific EGG packages.
195+
*.egg
196+
197+
# Ignore all SageMath-specific output Python files.
198+
*.sage.py
199+
200+
# Ignore all temporary files.
201+
*~
202+
*.sw?
203+
204+
# Ignore all "trace"-specific output files.
205+
*.cover
206+
207+
.gemini/
208+
gha-creds-*.json

0 commit comments

Comments
 (0)