From 461d70289e882bc32eabc2b8507439b999463fa8 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 30 Mar 2025 16:38:43 -0400 Subject: [PATCH 01/68] build: bump version to 7.8.1 --- CHANGES.rst | 6 ++++++ coverage/version.py | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index a2b172dc6..10bfe47ca 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -20,6 +20,12 @@ upgrading your version of coverage.py. .. Version 9.8.1 — 2027-07-27 .. -------------------------- +Unreleased +---------- + +Nothing yet. + + .. start-releases .. _changes_7-8-0: diff --git a/coverage/version.py b/coverage/version.py index fe08b5f98..867dcb8dd 100644 --- a/coverage/version.py +++ b/coverage/version.py @@ -8,8 +8,8 @@ # version_info: same semantics as sys.version_info. # _dev: the .devN suffix if any. -version_info = (7, 8, 0, "final", 0) -_dev = 0 +version_info = (7, 8, 1, "alpha", 0) +_dev = 1 def _make_version( From 2e16f8b27b976252e398d6d06f3a18e70e5bf941 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 18:35:28 -0400 Subject: [PATCH 02/68] chore: bump the action-dependencies group with 2 updates (#1945) Bumps the action-dependencies group with 2 updates: [github/codeql-action](https://github.com/github/codeql-action) and [actions/setup-python](https://github.com/actions/setup-python). Updates `github/codeql-action` from 3.28.12 to 3.28.13 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/5f8171a638ada777af81d42b55959a643bb29017...1b549b9259bda1cb5ddde3b41741a82a2d15a841) Updates `actions/setup-python` from 5.4.0 to 5.5.0 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/42375524e23c412d93fb67b49958b491fce71c38...8d9ed9ac5c53483de85588cdf95a591a75ab9f55) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch dependency-group: action-dependencies - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-minor dependency-group: action-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/coverage.yml | 4 ++-- .github/workflows/kit.yml | 6 +++--- .github/workflows/python-nightly.yml | 2 +- .github/workflows/quality.yml | 6 +++--- .github/workflows/testsuite.yml | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 25330d632..02d6b3ad5 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -51,7 +51,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@5f8171a638ada777af81d42b55959a643bb29017 # v3 + uses: github/codeql-action/init@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -62,7 +62,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@5f8171a638ada777af81d42b55959a643bb29017 # v3 + uses: github/codeql-action/autobuild@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -76,4 +76,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@5f8171a638ada777af81d42b55959a643bb29017 # v3 + uses: github/codeql-action/analyze@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 9a3cc59a9..5654fcf66 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -84,7 +84,7 @@ jobs: persist-credentials: false - name: "Set up Python" - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 + uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 with: python-version: "${{ matrix.python-version }}" allow-prereleases: true @@ -147,7 +147,7 @@ jobs: persist-credentials: false - name: "Set up Python" - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 + uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 with: python-version: "3.9" # Minimum of PYVERSIONS # At a certain point, installing dependencies failed on pypy 3.9 and diff --git a/.github/workflows/kit.yml b/.github/workflows/kit.yml index f5f45ef92..ba034963d 100644 --- a/.github/workflows/kit.yml +++ b/.github/workflows/kit.yml @@ -152,7 +152,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 + uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 with: python-version: "3.9" # Minimum of PYVERSIONS cache: pip @@ -198,7 +198,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 + uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 with: python-version: "3.9" # Minimum of PYVERSIONS cache: pip @@ -239,7 +239,7 @@ jobs: persist-credentials: false - name: "Install PyPy" - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 + uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 with: python-version: "pypy-3.9" # Minimum of PyPy PYVERSIONS cache: pip diff --git a/.github/workflows/python-nightly.yml b/.github/workflows/python-nightly.yml index 99e19d091..826ac4d9d 100644 --- a/.github/workflows/python-nightly.yml +++ b/.github/workflows/python-nightly.yml @@ -92,7 +92,7 @@ jobs: nogil: "${{ matrix.nogil || false }}" - name: "Install ${{ matrix.python-version }} with setup-python" - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 + uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 if: "startsWith(matrix.python-version, 'pypy-')" with: python-version: "${{ matrix.python-version }}" diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 56f063c0d..708c89d6d 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -42,7 +42,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 + uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 with: python-version: "3.9" # Minimum of PYVERSIONS cache: pip @@ -67,7 +67,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 + uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 with: python-version: "3.9" # Minimum of PYVERSIONS cache: pip @@ -92,7 +92,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 + uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 with: python-version: "3.11" # Doc version from PYVERSIONS cache: pip diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 0f831e81d..92ad7d376 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -78,7 +78,7 @@ jobs: persist-credentials: false - name: "Set up Python" - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 + uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 with: python-version: "${{ matrix.python-version }}" allow-prereleases: true From 7e3acd9c2ac8ef65c0e835313a06526d0e234f79 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 31 Mar 2025 18:43:17 -0400 Subject: [PATCH 03/68] fix: check for safe_path in a safer way. #1946 --- coverage/execfile.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/coverage/execfile.py b/coverage/execfile.py index b44c95280..23928c02e 100644 --- a/coverage/execfile.py +++ b/coverage/execfile.py @@ -17,7 +17,6 @@ from types import CodeType, ModuleType from typing import Any -from coverage import env from coverage.exceptions import CoverageException, _ExceptionDuringRun, NoCode, NoSource from coverage.files import canonical_filename, python_reported_file from coverage.misc import isolate_module @@ -90,7 +89,7 @@ def prepare(self) -> None: This needs to happen before any importing, and without importing anything. """ path0: str | None - if env.PYVERSION >= (3, 11) and getattr(sys.flags, "safe_path"): + if getattr(sys.flags, "safe_path", False): # See https://docs.python.org/3/using/cmdline.html#cmdoption-P path0 = None elif self.as_module: From 7059cc283109a93ff0999d9be3b7e6e81c18d45b Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 31 Mar 2025 18:46:16 -0400 Subject: [PATCH 04/68] test: ignore a SyntaxWarning caused by one of our tests --- tests/conftest.py | 3 +++ tests/test_parser.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 876dc0827..b75f714eb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -60,6 +60,9 @@ def set_warnings() -> None: warnings.filterwarnings("ignore", r".*no-sysmon") + # We have a test that has a return in a finally: test_bug_1891. + warnings.filterwarnings("ignore", "'return' in a 'finally' block", category=SyntaxWarning) + @pytest.fixture(autouse=True) def reset_sys_path() -> Iterator[None]: diff --git a/tests/test_parser.py b/tests/test_parser.py index a9a247ffd..8c9f2e7ad 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -207,6 +207,8 @@ def test_bug_1891(self) -> None: ) """) assert parser.exit_counts() == {1: 1} + # In conftest.py, we silence the SyntaxWarning this code causes. If + # we remove this code, we can probably remove that warning. parser = self.parse_text("""\ def g2(): try: From cfb3f50e62a81936b403910c663b26e52ea72ecc Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 2 Apr 2025 06:41:36 -0400 Subject: [PATCH 05/68] docs: tweak up the quick start about the html report --- doc/index.rst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index ea3486e12..6bca07edb 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -90,7 +90,7 @@ Getting started is easy: .. tab:: unittest - Change "python" to "coverage run", so this:: + Change your python command name to "coverage run", so this:: $ python3 -m unittest discover @@ -134,20 +134,21 @@ Getting started is easy: listings detailing missed lines:: $ coverage html + Wrote HTML report to htmlcov/index.html .. ifconfig:: not prerelease - Then open htmlcov/index.html in your browser, to see a - `report like this`_. + Then open `htmlcov/index.html `__ in your browser + to see a `report like this `__. .. ifconfig:: prerelease - Then open htmlcov/index.html in your browser, to see a - `report like this one`_. + Then open `htmlcov/index.html `__ in your browser + to see a `report like this `__. -.. _report like this: https://nedbatchelder.com/files/sample_coverage_html/index.html -.. _report like this one: https://nedbatchelder.com/files/sample_coverage_html_beta/index.html +.. _htmlreport: https://nedbatchelder.com/files/sample_coverage_html/index.html +.. _betahtmlreport: https://nedbatchelder.com/files/sample_coverage_html_beta/index.html .. _nose state: https://github.com/nose-devs/nose/commit/0f40fa995384afad77e191636c89eb7d5b8870ca .. _include tests: https://nedbatchelder.com/blog/202008/you_should_include_your_tests_in_coverage.html From 41f1860aa3107d3603a80f9bfde365c9b9b8c7e0 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 2 Apr 2025 06:53:23 -0400 Subject: [PATCH 06/68] docs: latest genuine tox output --- doc/contributing.rst | 136 ++++++++++++++++++++++--------------------- 1 file changed, 69 insertions(+), 67 deletions(-) diff --git a/doc/contributing.rst b/doc/contributing.rst index 368029081..8abdad53f 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -73,6 +73,7 @@ Running the tests ----------------- .. To get the test output: + # Use the lowest of the PYVERSIONS # Resize terminal width to 95 % make sterile @@ -81,66 +82,65 @@ Running the tests The tests are written mostly as standard unittest-style tests, and are run with pytest running under `tox`_:: - $ python3 -m tox -e py38 - py38: wheel-0.43.0-py3-none-any.whl already present in /Users/ned/Library/Application Support/virtualenv/wheel/3.8/embed/3/wheel.json - py38: pip-24.0-py3-none-any.whl already present in /Users/ned/Library/Application Support/virtualenv/wheel/3.8/embed/3/pip.json - py38: setuptools-69.2.0-py3-none-any.whl already present in /Users/ned/Library/Application Support/virtualenv/wheel/3.8/embed/3/setuptools.json - py38: install_deps> python -m pip install -U -r requirements/pip.pip -r requirements/pytest.pip -r requirements/light-threads.pip + % python3 -m tox -e py39 + py39: wheel-0.45.1-py3-none-any.whl already present in /Users/ned/Library/Application Support/virtualenv/wheel/3.9/embed/3/wheel.json + py39: pip-25.0.1-py3-none-any.whl already present in /Users/ned/Library/Application Support/virtualenv/wheel/3.9/embed/3/pip.json + py39: setuptools-78.1.0-py3-none-any.whl already present in /Users/ned/Library/Application Support/virtualenv/wheel/3.9/embed/3/setuptools.json + py39: install_deps> python -m pip install -U -r requirements/pip.pip -r requirements/pytest.pip -r requirements/light-threads.pip .pkg: install_requires> python -I -m pip install setuptools - .pkg: _optional_hooks> python /usr/local/virtualenvs/coverage/lib/python3.8/site-packages/pyproject_api/_backend.py True setuptools.build_meta - .pkg: get_requires_for_build_editable> python /usr/local/virtualenvs/coverage/lib/python3.8/site-packages/pyproject_api/_backend.py True setuptools.build_meta - .pkg: install_requires_for_build_editable> python -I -m pip install wheel - .pkg: build_editable> python /usr/local/virtualenvs/coverage/lib/python3.8/site-packages/pyproject_api/_backend.py True setuptools.build_meta - py38: install_package_deps> python -m pip install -U 'tomli; python_full_version <= "3.11.0a6"' - py38: install_package> python -m pip install -U --force-reinstall --no-deps .tox/.tmp/package/1/coverage-7.4.5a0.dev1-0.editable-cp38-cp38-macosx_14_0_arm64.whl - py38: commands[0]> python igor.py zip_mods - py38: commands[1]> python setup.py --quiet build_ext --inplace - ld: warning: duplicate -rpath '/usr/local/pyenv/pyenv/versions/3.8.18/lib' ignored - ld: warning: duplicate -rpath '/opt/homebrew/lib' ignored - py38: commands[2]> python -m pip install -q -e . - py38: commands[3]> python igor.py test_with_core ctrace - === CPython 3.8.18 with C tracer (.tox/py38/bin/python) === + .pkg: _optional_hooks> python /usr/local/virtualenvs/coverage/lib/python3.9/site-packages/pyproject_api/_backend.py True setuptools.build_meta + .pkg: get_requires_for_build_editable> python /usr/local/virtualenvs/coverage/lib/python3.9/site-packages/pyproject_api/_backend.py True setuptools.build_meta + .pkg: build_editable> python /usr/local/virtualenvs/coverage/lib/python3.9/site-packages/pyproject_api/_backend.py True setuptools.build_meta + py39: install_package_deps> python -m pip install -U 'tomli; python_full_version <= "3.11.0a6"' + py39: install_package> python -m pip install -U --force-reinstall --no-deps .tox/.tmp/package/1/coverage-7.8.1a0.dev1-0.editable-cp39-cp39-macosx_15_0_arm64.whl + py39: commands[0]> python igor.py zip_mods + py39: commands[1]> python igor.py remove_extension + py39: commands[2]> python igor.py test_with_core pytrace + === CPython 3.9.21 (gil) with Python tracer (.tox/py39/bin/python) === bringing up nodes... - ....................................................................................... [ 6%] - .....................................................x...x............s......s.s....s.. [ 12%] - ....................................................................................... [ 18%] - ....................................................................................... [ 25%] - ....................................................................................... [ 31%] - ....................................................................................... [ 37%] - ....................................................................................... [ 44%] - ....................................................................................... [ 50%] - ....................................................................................... [ 56%] - ........................s...........s.................................................. [ 63%] - ...........................................................................s........... [ 69%] - .................................s............s.s.................s.................... [ 75%] - ...........................................s........................................s.. [ 81%] - ................................s...................................................... [ 88%] + ....................................................................................... [ 5%] + ..................................................................x................s... [ 11%] + ......s...s.....s....s......s.s.s.s.................................................... [ 17%] + ...........................................s..ss...ss.ss.ss............................ [ 23%] + ....................................................................................... [ 29%] + ....................................................................................... [ 35%] + ....................................................................................... [ 41%] + ................................................s...................................... [ 47%] + ....................................................................................... [ 53%] + ...................................................s..........s........................ [ 59%] + ....................................................................................... [ 65%] + ..........................ssss......................................................... [ 71%] + ..s.....s.ss..........................ss...................s.s..sssssss.ssssss.sssss... [ 77%] + .........ss..........................s...s.s......s........s........................s.. [ 83%] + .............................s......................................................... [ 88%] ....................................................................................... [ 94%] - ............................................................s................... [100%] - 1368 passed, 15 skipped, 2 xfailed in 13.10s - py38: commands[4]> python igor.py remove_extension - py38: commands[5]> python igor.py test_with_core pytrace - === CPython 3.8.18 with Python tracer (.tox/py38/bin/python) === + .............................................s.......................ss.... [100%] + 1403 passed, 63 skipped, 1 xfailed in 15.05s + py39: commands[3]> python setup.py --quiet build_ext --inplace + py39: commands[4]> python -m pip install -q -e . + py39: commands[5]> python igor.py test_with_core ctrace + === CPython 3.9.21 (gil) with C tracer (.tox/py39/bin/python) === bringing up nodes... - ....................................................................................... [ 6%] - ....................x..x.............................................s.ss...s.......... [ 12%] - ..........................................................................s.ss.s..s.... [ 18%] - s........s........s..s...s............................................................. [ 25%] - ................s...................................................................... [ 31%] - ...................s......ss..........................ssss...........................s. [ 37%] - ....................................................................................... [ 43%] - ....................................................................................... [ 50%] - .................................................................s..................... [ 56%] - ........s..s.........sss.s............................................................. [ 62%] - ...................................................................ss.................. [ 69%] - ..............................................ss...........s.s......................... [ 75%] - ................................ssssss................................................. [ 81%] - ......s...ss........ss................................................................. [ 88%] - .............................................s......................................... [ 94%] - .......................................................................ss....... [100%] - 1333 passed, 50 skipped, 2 xfailed in 11.17s - py38: OK (37.60=setup[9.10]+cmd[0.11,0.49,2.83,13.59,0.11,11.39] seconds) - congratulations :) (37.91 seconds) + ....................................................................................... [ 5%] + ..........................................sx................................s.......... [ 11%] + ..........ss........s....................................s............................. [ 17%] + ..............................sss...................................................... [ 23%] + ..............................................................s........................ [ 29%] + ....................................................................................... [ 35%] + ....................................................................................... [ 41%] + ......................................................s................................ [ 47%] + .............................................s......................................... [ 53%] + .......s..................s............................................................ [ 59%] + ....................................................................................s.. [ 65%] + .......................................................ss.......................s...... [ 71%] + ....................................................s............................ss.... [ 77%] + ..........................s...................s........................................ [ 83%] + ....................................................................................... [ 88%] + ............................s......s................................................... [ 94%] + .................................................................s......... [100%] + 1440 passed, 26 skipped, 1 xfailed in 12.38s + py39: OK (40.04=setup[9.03]+cmd[0.17,0.09,15.40,0.13,2.47,12.77] seconds) + congratulations :) (40.61 seconds) Tox runs the complete test suite a few times for each version of Python you have installed. The first run uses the C implementation of the trace function, @@ -165,15 +165,17 @@ respectively. The pytest ``-k`` option selects tests based on a word in their name, which can be very convenient for ad-hoc test selection. Of course you can combine tox and pytest options:: - $ python3 -m tox -q -e py310 -- -n 0 -vv -k hash - ================================== test session starts =================================== - platform darwin -- Python 3.10.13, pytest-8.1.1, pluggy-1.4.0 -- /Users/ned/coverage/trunk/.tox/py310/bin/python + % python3 -m tox -q -e py310 -- -n 0 -vv -k hash + Skipping tests with Python tracer: Only one core: not running pytrace + === CPython 3.10.16 (gil) with C tracer (.tox/py310/bin/python) === + ===================================== test session starts ===================================== + platform darwin -- Python 3.10.16, pytest-8.3.5, pluggy-1.5.0 -- /Users/ned/coverage/trunk/.tox/py310/bin/python cachedir: .tox/py310/.pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase(PosixPath('/Users/ned/coverage/trunk/.hypothesis/examples')) rootdir: /Users/ned/coverage/trunk configfile: pyproject.toml - plugins: flaky-3.8.1, xdist-3.5.0, hypothesis-6.99.6 - collected 1385 items / 1375 deselected / 10 selected + plugins: flaky-3.8.1, hypothesis-6.128.1, xdist-3.6.1 + collected 1467 items / 1457 deselected / 10 selected run-last-failure: no previously failed tests, not deselecting items. tests/test_data.py::CoverageDataTest::test_add_to_hash_with_lines PASSED [ 10%] @@ -187,11 +189,9 @@ can combine tox and pytest options:: tests/test_misc.py::HasherTest::test_dict_hashing PASSED [ 90%] tests/test_misc.py::HasherTest::test_dict_collision PASSED [100%] - ========================== 10 passed, 1375 deselected in 0.60s =========================== - Skipping tests with Python tracer: Only one core: not running pytrace - py310: OK (6.41 seconds) - congratulations :) (6.72 seconds) - + ============================= 10 passed, 1457 deselected in 3.13s ============================= + py310: OK (16.62 seconds) + congratulations :) (16.97 seconds) You can also affect the test runs with environment variables: @@ -237,7 +237,8 @@ Other style questions are best answered by looking at the existing code. Formatting of docstrings, comments, long lines, and so on, should match the code that already exists. -Many people love `black`_, but I would prefer not to run it on coverage.py. +Many people love auto-formatting with `black`_ or `ruff`_, but I would prefer +not to on coverage.py. Continuous integration @@ -299,6 +300,7 @@ fixes. If you need help writing tests, please ask. .. _editorconfig.org: http://editorconfig.org .. _tox: https://tox.readthedocs.io/ .. _black: https://pypi.org/project/black/ +.. _ruff: https://pypi.org/project/ruff/ .. _set_env.py: https://nedbatchelder.com/blog/201907/set_envpy.html .. _pytest test selectors: https://doc.pytest.org/en/stable/usage.html#specifying-which-tests-to-run .. _sys.monitoring: https://docs.python.org/3/library/sys.monitoring.html From f4024e8afc76992cfbd6707a9ce8adb39a32ff6f Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 2 Apr 2025 07:05:00 -0400 Subject: [PATCH 07/68] docs: a Bluesky badge --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index cf1e856f5..ba20fb28c 100644 --- a/README.rst +++ b/README.rst @@ -16,6 +16,7 @@ Code coverage measurement for Python. | |kit| |license| |versions| | |test-status| |quality-status| |docs| |metacov| | |tidelift| |sponsor| |stars| |mastodon-coveragepy| |mastodon-nedbat| + |bluesky-nedbat| Coverage.py measures code coverage, typically during test execution. It uses the code analysis tools and tracing hooks provided in the Python standard @@ -159,6 +160,9 @@ Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_. .. |mastodon-coveragepy| image:: https://img.shields.io/badge/dynamic/json?style=flat&labelColor=450657&logo=mastodon&logoColor=ffffff&label=@coveragepy&query=followers_count&url=https%3A%2F%2Fhachyderm.io%2Fapi%2Fv1%2Faccounts%2Flookup%3Facct=coveragepy :target: https://hachyderm.io/@coveragepy :alt: coveragepy on Mastodon +.. |bluesky-nedbat| image:: https://img.shields.io/badge/dynamic/json?style=flat&color=96a3b0&labelColor=3686f7&logo=icloud&logoColor=white&label=@nedbat&url=https%3A%2F%2Fpublic.api.bsky.app%2Fxrpc%2Fapp.bsky.actor.getProfile%3Factor=nedbat.com&query=followersCount + :target: https://bsky.app/profile/nedbat.com + :alt: nedbat on Bluesky .. |sponsor| image:: https://img.shields.io/badge/%E2%9D%A4-Sponsor%20me-brightgreen?style=flat&logo=GitHub :target: https://github.com/sponsors/nedbat :alt: Sponsor me on GitHub From de741ccb4707e50c7551bc77916eccf5d272aab4 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 2 Apr 2025 07:20:47 -0400 Subject: [PATCH 08/68] docs: make the github stars badge look like the others --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index ba20fb28c..f71e01dbe 100644 --- a/README.rst +++ b/README.rst @@ -151,7 +151,7 @@ Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_. .. |tidelift| image:: https://tidelift.com/badges/package/pypi/coverage :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme :alt: Tidelift -.. |stars| image:: https://img.shields.io/github/stars/nedbat/coveragepy.svg?logo=github +.. |stars| image:: https://img.shields.io/github/stars/nedbat/coveragepy.svg?logo=github&style=flat :target: https://github.com/nedbat/coveragepy/stargazers :alt: GitHub stars .. |mastodon-nedbat| image:: https://img.shields.io/badge/dynamic/json?style=flat&labelColor=450657&logo=mastodon&logoColor=ffffff&label=@nedbat&query=followers_count&url=https%3A%2F%2Fhachyderm.io%2Fapi%2Fv1%2Faccounts%2Flookup%3Facct=nedbat From 461179c0fff3dd0e46964ddd80fd3b44e9fc118c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 16:59:40 -0400 Subject: [PATCH 09/68] chore: bump the action-dependencies group with 2 updates (#1949) Bumps the action-dependencies group with 2 updates: [github/codeql-action](https://github.com/github/codeql-action) and [actions/dependency-review-action](https://github.com/actions/dependency-review-action). Updates `github/codeql-action` from 3.28.13 to 3.28.14 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/1b549b9259bda1cb5ddde3b41741a82a2d15a841...fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2) Updates `actions/dependency-review-action` from 4.5.0 to 4.6.0 - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/3b139cfc5fae8b618d3eae3675e383bb1769c019...ce3cf9537a52e8119d91fd484ab5b8a807627bf8) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.28.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: action-dependencies - dependency-name: actions/dependency-review-action dependency-version: 4.6.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: action-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/dependency-review.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 02d6b3ad5..e533a63d3 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -51,7 +51,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3 + uses: github/codeql-action/init@fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2 # v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -62,7 +62,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3 + uses: github/codeql-action/autobuild@fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2 # v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -76,4 +76,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3 + uses: github/codeql-action/analyze@fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2 # v3 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 032745854..2c5057f45 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -28,7 +28,7 @@ jobs: persist-credentials: false - name: 'Dependency Review' - uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0 + uses: actions/dependency-review-action@ce3cf9537a52e8119d91fd484ab5b8a807627bf8 # v4.6.0 with: base-ref: ${{ github.event.pull_request.base.sha || 'master' }} head-ref: ${{ github.event.pull_request.head.sha || github.ref }} From 5a2b83de1eb112c341663e059f9c03d2dbdbd9f3 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 8 Apr 2025 06:06:36 -0400 Subject: [PATCH 10/68] chore: make upgrade doc_upgrade --- doc/requirements.pip | 6 +++--- requirements/dev.pip | 20 ++++++++++---------- requirements/kit.pip | 14 +++++++------- requirements/light-threads.pip | 2 +- requirements/mypy.pip | 10 +++++----- requirements/pip-tools.pip | 2 +- requirements/pip.pip | 6 +++--- requirements/pytest.pip | 6 +++--- requirements/tox.pip | 6 +++--- 9 files changed, 36 insertions(+), 36 deletions(-) diff --git a/doc/requirements.pip b/doc/requirements.pip index ae5de5420..dbc988fc6 100644 --- a/doc/requirements.pip +++ b/doc/requirements.pip @@ -6,7 +6,7 @@ # alabaster==1.0.0 # via sphinx -anyio==4.8.0 +anyio==4.9.0 # via # starlette # watchfiles @@ -109,7 +109,7 @@ starlette==0.46.1 # via sphinx-autobuild stevedore==5.4.1 # via doc8 -typing-extensions==4.12.2 +typing-extensions==4.13.1 # via anyio urllib3==2.3.0 # via requests @@ -121,5 +121,5 @@ websockets==15.0.1 # via sphinx-autobuild # The following packages are considered to be unsafe in a requirements file: -setuptools==76.0.0 +setuptools==78.1.0 # via pbr diff --git a/requirements/dev.pip b/requirements/dev.pip index 13e6b0d81..a42f0a2f7 100644 --- a/requirements/dev.pip +++ b/requirements/dev.pip @@ -6,13 +6,13 @@ # astroid==3.3.9 # via pylint -attrs==25.1.0 +attrs==25.3.0 # via hypothesis backports-tarfile==1.2.0 # via jaraco-context build==1.2.2.post1 # via check-manifest -cachetools==5.5.2 +cachetools==6.0.0b2 # via tox certifi==2025.1.31 # via requests @@ -41,7 +41,7 @@ exceptiongroup==1.2.2 # pytest execnet==2.1.1 # via pytest-xdist -filelock==3.17.0 +filelock==3.18.0 # via # tox # virtualenv @@ -49,7 +49,7 @@ flaky==3.8.1 # via -r /Users/ned/coverage/trunk/requirements/pytest.in greenlet==3.1.1 # via -r requirements/dev.in -hypothesis==6.128.1 +hypothesis==6.130.10 # via -r /Users/ned/coverage/trunk/requirements/pytest.in id==1.5.0 # via twine @@ -60,7 +60,7 @@ importlib-metadata==8.6.1 # build # keyring # twine -iniconfig==2.0.0 +iniconfig==2.1.0 # via pytest isort==6.0.1 # via pylint @@ -98,7 +98,7 @@ packaging==24.2 # twine parso==0.8.4 # via jedi -platformdirs==4.3.6 +platformdirs==4.3.7 # via # pylint # tox @@ -115,7 +115,7 @@ pygments==2.19.1 # pudb # readme-renderer # rich -pylint==3.3.5 +pylint==3.3.6 # via -r requirements/dev.in pyproject-api==1.9.0 # via tox @@ -141,7 +141,7 @@ requests-toolbelt==1.0.0 # via twine rfc3986==2.0.0 # via twine -rich==13.9.4 +rich==14.0.0 # via twine sortedcontainers==2.4.0 # via hypothesis @@ -165,7 +165,7 @@ tox-gh==1.5.0 # via -r /Users/ned/coverage/trunk/requirements/tox.in twine==6.1.0 # via -r requirements/dev.in -typing-extensions==4.12.2 +typing-extensions==4.13.1 # via # astroid # pylint @@ -195,7 +195,7 @@ zipp==3.21.0 # The following packages are considered to be unsafe in a requirements file: pip==25.0.1 # via -r /Users/ned/coverage/trunk/requirements/pip.in -setuptools==76.0.0 +setuptools==78.1.0 # via # -r /Users/ned/coverage/trunk/requirements/pip.in # check-manifest diff --git a/requirements/kit.pip b/requirements/kit.pip index 7b7c8a6a3..471417f20 100644 --- a/requirements/kit.pip +++ b/requirements/kit.pip @@ -4,7 +4,7 @@ # # make upgrade # -auditwheel==6.2.0 +auditwheel==6.3.0 # via -r requirements/kit.in backports-tarfile==1.2.0 # via jaraco-context @@ -20,7 +20,7 @@ certifi==2025.1.31 # requests charset-normalizer==3.4.1 # via requests -cibuildwheel==2.23.0 +cibuildwheel==2.23.2 # via -r requirements/kit.in colorama==0.4.6 # via -r requirements/kit.in @@ -28,7 +28,7 @@ dependency-groups==1.3.0 # via cibuildwheel docutils==0.21.2 # via readme-renderer -filelock==3.17.0 +filelock==3.18.0 # via cibuildwheel id==1.5.0 # via twine @@ -64,7 +64,7 @@ packaging==24.2 # cibuildwheel # dependency-groups # twine -platformdirs==4.3.6 +platformdirs==4.3.7 # via cibuildwheel pyelftools==0.32 # via auditwheel @@ -85,7 +85,7 @@ requests-toolbelt==1.0.0 # via twine rfc3986==2.0.0 # via twine -rich==13.9.4 +rich==14.0.0 # via twine tomli==2.2.1 # via @@ -94,7 +94,7 @@ tomli==2.2.1 # dependency-groups twine==6.1.0 # via -r requirements/kit.in -typing-extensions==4.12.2 +typing-extensions==4.13.1 # via # cibuildwheel # rich @@ -108,5 +108,5 @@ zipp==3.21.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -setuptools==76.0.0 +setuptools==78.1.0 # via -r requirements/kit.in diff --git a/requirements/light-threads.pip b/requirements/light-threads.pip index aff5c3b49..98054ea1f 100644 --- a/requirements/light-threads.pip +++ b/requirements/light-threads.pip @@ -25,7 +25,7 @@ zope-interface==7.2 # via gevent # The following packages are considered to be unsafe in a requirements file: -setuptools==76.0.0 +setuptools==78.1.0 # via # zope-event # zope-interface diff --git a/requirements/mypy.pip b/requirements/mypy.pip index 6d63badc7..c12a50f10 100644 --- a/requirements/mypy.pip +++ b/requirements/mypy.pip @@ -4,7 +4,7 @@ # # make upgrade # -attrs==25.1.0 +attrs==25.3.0 # via hypothesis colorama==0.4.6 # via -r /Users/ned/coverage/trunk/requirements/pytest.in @@ -16,9 +16,9 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r /Users/ned/coverage/trunk/requirements/pytest.in -hypothesis==6.128.1 +hypothesis==6.130.10 # via -r /Users/ned/coverage/trunk/requirements/pytest.in -iniconfig==2.0.0 +iniconfig==2.1.0 # via pytest mypy==1.15.0 # via -r requirements/mypy.in @@ -42,11 +42,11 @@ tomli==2.2.1 # via # mypy # pytest -types-requests==2.32.0.20250306 +types-requests==2.32.0.20250328 # via -r requirements/mypy.in types-tabulate==0.9.0.20241207 # via -r requirements/mypy.in -typing-extensions==4.12.2 +typing-extensions==4.13.1 # via mypy urllib3==2.3.0 # via types-requests diff --git a/requirements/pip-tools.pip b/requirements/pip-tools.pip index 72ccd349b..07e9851a6 100644 --- a/requirements/pip-tools.pip +++ b/requirements/pip-tools.pip @@ -30,5 +30,5 @@ zipp==3.21.0 # The following packages are considered to be unsafe in a requirements file: pip==25.0.1 # via pip-tools -setuptools==76.0.0 +setuptools==78.1.0 # via pip-tools diff --git a/requirements/pip.pip b/requirements/pip.pip index 4827fc3c1..e14aed3ae 100644 --- a/requirements/pip.pip +++ b/requirements/pip.pip @@ -6,9 +6,9 @@ # distlib==0.3.9 # via virtualenv -filelock==3.17.0 +filelock==3.18.0 # via virtualenv -platformdirs==4.3.6 +platformdirs==4.3.7 # via virtualenv virtualenv==20.28.1 # via @@ -18,5 +18,5 @@ virtualenv==20.28.1 # The following packages are considered to be unsafe in a requirements file: pip==25.0.1 # via -r requirements/pip.in -setuptools==76.0.0 +setuptools==78.1.0 # via -r requirements/pip.in diff --git a/requirements/pytest.pip b/requirements/pytest.pip index 4d12a374a..a4288fcfc 100644 --- a/requirements/pytest.pip +++ b/requirements/pytest.pip @@ -4,7 +4,7 @@ # # make upgrade # -attrs==25.1.0 +attrs==25.3.0 # via hypothesis colorama==0.4.6 # via -r requirements/pytest.in @@ -16,9 +16,9 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.128.1 +hypothesis==6.130.10 # via -r requirements/pytest.in -iniconfig==2.0.0 +iniconfig==2.1.0 # via pytest packaging==24.2 # via pytest diff --git a/requirements/tox.pip b/requirements/tox.pip index a2387c529..c1766adad 100644 --- a/requirements/tox.pip +++ b/requirements/tox.pip @@ -14,7 +14,7 @@ colorama==0.4.6 # tox distlib==0.3.9 # via virtualenv -filelock==3.17.0 +filelock==3.18.0 # via # tox # virtualenv @@ -22,7 +22,7 @@ packaging==24.2 # via # pyproject-api # tox -platformdirs==4.3.6 +platformdirs==4.3.7 # via # tox # virtualenv @@ -40,7 +40,7 @@ tox==4.24.1 # tox-gh tox-gh==1.5.0 # via -r requirements/tox.in -typing-extensions==4.12.2 +typing-extensions==4.13.1 # via tox virtualenv==20.28.1 # via From 5f1d0f4b0218480aea607c9e583f514b745812ea Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 8 Apr 2025 06:13:58 -0400 Subject: [PATCH 11/68] test: let nightly PyPy lag more than CPython --- .github/workflows/python-nightly.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/python-nightly.yml b/.github/workflows/python-nightly.yml index 826ac4d9d..6e32a1528 100644 --- a/.github/workflows/python-nightly.yml +++ b/.github/workflows/python-nightly.yml @@ -71,8 +71,6 @@ jobs: exclude: - python-version: "3.12-dev" nogil: true - - python-version: "pypy-3.9-nightly" - nogil: true - python-version: "pypy-3.10-nightly" nogil: true @@ -109,7 +107,6 @@ jobs: env | sort - name: "Check build recency" - #if: "!startsWith(matrix.python-version, 'pypy-')" shell: python run: | import platform @@ -119,10 +116,11 @@ jobs: built = datetime.strptime(platform.python_build()[1], fmt) except ValueError: continue - now = datetime.now() - days = (now - built).days - print(f"Days since Python was built: {days}") - assert days <= 7 + days = (datetime.now() - built).days + impl = platform.python_implementation() + recency = 7 if (impl == "CPython") else 21 + print(f"Days since {impl} was built: {days}, need within {recency}") + assert days <= recency - name: "Install dependencies" run: | From 563eacf92cb70e9d5ffb1c70ea6b71ac9bcb9849 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 9 Apr 2025 04:40:18 -0400 Subject: [PATCH 12/68] refactor: be safe with a regex special character --- coverage/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage/misc.py b/coverage/misc.py index c5ce7f4ae..06e83eee5 100644 --- a/coverage/misc.py +++ b/coverage/misc.py @@ -246,7 +246,7 @@ def substitute_variables(text: str, variables: Mapping[str, str]) -> str: (?: # then (?P\$) | # a dollar sign, or (?P\w+) | # a plain word, or - { # a {-wrapped + \{ # a {-wrapped (?P\w+) # word, (?: (?P\?) | # with a strict marker From 59c724d979e49da2f0beed1b41fe0d3ce0f7a6c5 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 9 Apr 2025 05:20:36 -0400 Subject: [PATCH 13/68] docs: more words about the public api --- doc/api.rst | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 7d04f03ee..04c52d639 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -37,11 +37,17 @@ If you want to access the data that coverage.py has collected, the :class:`coverage.CoverageData` class provides an API to read coverage.py data files. -.. note:: +.. warning:: - Only the documented portions of the API are supported. Other names you may - find in modules or objects can change their behavior at any time. Please - limit yourself to documented methods to avoid problems. + Only the published documented portions of the API are supported. Other + names you may find in modules or objects can change their behavior at any + time. Please limit yourself to documented methods to avoid problems. + + All internal code in coverage.py has docstrings; this does not make them + part of the public supported API. Many internal names have no leading + underscore; this does not make them part of the public supported API. If + classes or functions are not documented in this published documentation, + they are not supported. For more intensive data use, you might want to access the coverage.py database file directly. The schema is subject to change, so this is for advanced uses From 523059b8d4280dd52c15896cfb39ac6f4bba089c Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 13 Apr 2025 09:54:51 -0400 Subject: [PATCH 14/68] refactor: fix a C warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` In file included from /usr/include/python3.13/Python.h:72, from coverage/ctracer/util.h:7, from coverage/ctracer/tracer.c:6: /usr/include/python3.13/object.h:952:23: warning: ‘Py_DECREF’ is static but used in inline function ‘MyFrame_BorrowCode’ which is not static 952 | #define Py_DECREF(op) Py_DECREF(_PyObject_CAST(op)) | ^~~~~~~~~ coverage/ctracer/tracer.c:298:5: note: in expansion of macro ‘Py_DECREF’ 298 | Py_DECREF(pCode); | ^~~~~~~~~ ``` --- coverage/ctracer/tracer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage/ctracer/tracer.c b/coverage/ctracer/tracer.c index 4fca2fee7..c05dff643 100644 --- a/coverage/ctracer/tracer.c +++ b/coverage/ctracer/tracer.c @@ -289,7 +289,7 @@ CTracer_set_pdata_stack(CTracer *self) } // Thanks for the idea, memray! -inline PyCodeObject* +static inline PyCodeObject* MyFrame_BorrowCode(PyFrameObject* frame) { // Return a borrowed reference. From 287d0f9aedcc72e54d91a84d09e38e87e1b2a43b Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 14 Apr 2025 11:38:00 -0400 Subject: [PATCH 15/68] test: don't check for code object leaks on 3.13t --- .github/workflows/python-nightly.yml | 2 +- coverage/env.py | 3 +++ tests/test_oddball.py | 13 ++++++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python-nightly.yml b/.github/workflows/python-nightly.yml index 6e32a1528..9eab004bc 100644 --- a/.github/workflows/python-nightly.yml +++ b/.github/workflows/python-nightly.yml @@ -128,4 +128,4 @@ jobs: - name: "Run tox" run: | - python -m tox -- -rfsEX + python -m tox -v -- -rfsEX diff --git a/coverage/env.py b/coverage/env.py index a88161b38..365da7f93 100644 --- a/coverage/env.py +++ b/coverage/env.py @@ -40,6 +40,9 @@ else: PYPYVERSION = (0,) +# Do we have a GIL? +GIL = getattr(sys, '_is_gil_enabled', lambda: True)() + # Python behavior. class PYBEHAVIOR: """Flags indicating this Python's behavior.""" diff --git a/tests/test_oddball.py b/tests/test_oddball.py index 21d1fc421..7d3199fb5 100644 --- a/tests/test_oddball.py +++ b/tests/test_oddball.py @@ -210,9 +210,16 @@ def once(x): # line 301 if fails > 8: pytest.fail("RAM grew by %d" % (ram_growth)) # pragma: only failure - @pytest.mark.skipif(not testenv.C_TRACER, reason="Only the C tracer has refcounting issues") - # In fact, sysmon explicitly holds onto all code objects, - # so this will definitely fail with sysmon. + @pytest.mark.skipif( + not testenv.C_TRACER, + reason="Only the C tracer has refcounting issues", + # In fact, sysmon explicitly holds onto all code objects, + # so this will definitely fail with sysmon. + ) + @pytest.mark.skipif( + env.PYVERSION[:2] == (3, 13) and not env.GIL, + reason = "3.13t never frees code objects: https://github.com/python/cpython/pull/131989", + ) @pytest.mark.parametrize("branch", [False, True]) def test_eval_codeobject_leak(self, branch: bool) -> None: # https://github.com/nedbat/coveragepy/issues/1924 From 007f580a9a9d3f94b88c987301bbf8a35125e8d2 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 18 Apr 2025 11:09:45 -0400 Subject: [PATCH 16/68] refactor: make the regex look nicer with spaces --- coverage/misc.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/coverage/misc.py b/coverage/misc.py index 06e83eee5..32c91cca1 100644 --- a/coverage/misc.py +++ b/coverage/misc.py @@ -244,13 +244,13 @@ def substitute_variables(text: str, variables: Mapping[str, str]) -> str: dollar_pattern = r"""(?x) # Use extended regex syntax \$ # A dollar sign, (?: # then - (?P\$) | # a dollar sign, or - (?P\w+) | # a plain word, or + (?P \$ ) | # a dollar sign, or + (?P \w+ ) | # a plain word, or \{ # a {-wrapped - (?P\w+) # word, - (?: - (?P\?) | # with a strict marker - -(?P[^}]*) # or a default value + (?P \w+ ) # word, + (?: # either + (?P \? ) | # with a strict marker + -(?P [^}]* ) # or a default value )? # maybe. } ) From dd5f892b02f588242865746f5c1981d890102799 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 20 Apr 2025 07:22:49 -0400 Subject: [PATCH 17/68] build: more checking of actions with Zizmor --- .github/workflows/zizmor.yml | 38 ++++++++++++++++++++++++++++++++++++ .github/zizmor.yml | 8 ++++++++ 2 files changed, 46 insertions(+) create mode 100644 .github/workflows/zizmor.yml create mode 100644 .github/zizmor.yml diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 000000000..282419445 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,38 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +name: GitHub actions security + +on: + push: + branches: + - master + - nedbat/* + pull_request: + workflow_dispatch: + +permissions: {} + +jobs: + zizmor: + name: zizmor + runs-on: ubuntu-latest + permissions: + contents: read + actions: read + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: Install the latest version of uv + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 #v5.4.2 + with: + enable-cache: false + + - name: Run zizmor + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: uvx zizmor --pedantic .github/workflows diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 000000000..9606c3719 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,8 @@ +# Rules for checking workflows +# https://woodruffw.github.io/zizmor + +rules: + unpinned-uses: + config: + policies: + actions/*: hash-pin From 6ada1eb3bea7bb62e1e2f304583155fb05a2732e Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 20 Apr 2025 07:38:54 -0400 Subject: [PATCH 18/68] style: format .yml more uniformly --- .github/workflows/codeql-analysis.yml | 54 +++++------ .github/workflows/publish.yml | 130 +++++++++++++------------- 2 files changed, 92 insertions(+), 92 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e533a63d3..2f55973c7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -44,36 +44,36 @@ jobs: # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: - - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2 # v3 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2 # v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2 # v3 + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2 # v3 - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language - #- run: | - # make bootstrap - # make release + #- run: | + # make bootstrap + # make release - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2 # v3 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2 # v3 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4e6032605..454104287 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -34,21 +34,21 @@ jobs: run-id: ${{ steps.run-id.outputs.run-id }} steps: - - name: "Find latest kit.yml run" - id: runs - uses: octokit/request-action@dad4362715b7fb2ddedf9772c8670824af564f0d # v2.4.0 - with: - route: GET /repos/nedbat/coveragepy/actions/workflows/kit.yml/runs - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: "Record run id" # zizmor: ignore[template-injection] - id: run-id - run: | - # There must be a shorter way to write this... - [ "${{ fromJson(steps.runs.outputs.data).workflow_runs[0].status}}" = "completed" ] || exit 1 - [ "${{ fromJson(steps.runs.outputs.data).workflow_runs[0].conclusion}}" = "success" ] || exit 1 - echo "run-id=${{ fromJson(steps.runs.outputs.data).workflow_runs[0].id }}" >> "$GITHUB_OUTPUT" + - name: "Find latest kit.yml run" + id: runs + uses: octokit/request-action@dad4362715b7fb2ddedf9772c8670824af564f0d # v2.4.0 + with: + route: GET /repos/nedbat/coveragepy/actions/workflows/kit.yml/runs + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: "Record run id" # zizmor: ignore[template-injection] + id: run-id + run: | + # There must be a shorter way to write this... + [ "${{ fromJson(steps.runs.outputs.data).workflow_runs[0].status}}" = "completed" ] || exit 1 + [ "${{ fromJson(steps.runs.outputs.data).workflow_runs[0].conclusion}}" = "success" ] || exit 1 + echo "run-id=${{ fromJson(steps.runs.outputs.data).workflow_runs[0].id }}" >> "$GITHUB_OUTPUT" publish-to-test-pypi: name: "Publish to Test PyPI" @@ -63,32 +63,32 @@ jobs: - find-run steps: - - name: "Download dists" - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 - with: - repository: "nedbat/coveragepy" - run-id: ${{ needs.find-run.outputs.run-id }} - github-token: ${{ secrets.GITHUB_TOKEN }} - pattern: "dist-*" - merge-multiple: true - path: "dist/" - - - name: "What did we get?" - run: | - ls -alR - echo "Number of dists, should be $EXPECTED:" - ls -1 dist | wc -l - files=$(ls dist 2>/dev/null | wc -l) && [ "$files" -eq $EXPECTED ] || exit 1 - - - name: "Generate attestations" - uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3 - with: - subject-path: "dist/*" - - - name: "Publish dists to Test PyPI" - uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 - with: - repository-url: https://test.pypi.org/legacy/ + - name: "Download dists" + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + with: + repository: "nedbat/coveragepy" + run-id: ${{ needs.find-run.outputs.run-id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + pattern: "dist-*" + merge-multiple: true + path: "dist/" + + - name: "What did we get?" + run: | + ls -alR + echo "Number of dists, should be $EXPECTED:" + ls -1 dist | wc -l + files=$(ls dist 2>/dev/null | wc -l) && [ "$files" -eq $EXPECTED ] || exit 1 + + - name: "Generate attestations" + uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3 + with: + subject-path: "dist/*" + + - name: "Publish dists to Test PyPI" + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 + with: + repository-url: https://test.pypi.org/legacy/ publish-to-pypi: name: "Publish to PyPI" @@ -103,27 +103,27 @@ jobs: - find-run steps: - - name: "Download dists" - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 - with: - repository: "nedbat/coveragepy" - run-id: ${{ needs.find-run.outputs.run-id }} - github-token: ${{ secrets.GITHUB_TOKEN }} - pattern: "dist-*" - merge-multiple: true - path: "dist/" - - - name: "What did we get?" - run: | - ls -alR - echo "Number of dists, should be $EXPECTED:" - ls -1 dist | wc -l - files=$(ls dist 2>/dev/null | wc -l) && [ "$files" -eq $EXPECTED ] || exit 1 - - - name: "Generate attestations" - uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3 - with: - subject-path: "dist/*" - - - name: "Publish dists to PyPI" - uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 + - name: "Download dists" + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + with: + repository: "nedbat/coveragepy" + run-id: ${{ needs.find-run.outputs.run-id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + pattern: "dist-*" + merge-multiple: true + path: "dist/" + + - name: "What did we get?" + run: | + ls -alR + echo "Number of dists, should be $EXPECTED:" + ls -1 dist | wc -l + files=$(ls dist 2>/dev/null | wc -l) && [ "$files" -eq $EXPECTED ] || exit 1 + + - name: "Generate attestations" + uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3 + with: + subject-path: "dist/*" + + - name: "Publish dists to PyPI" + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 From b4efb448e5b790c8d90eb922ffc34c01460ae2c4 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 20 Apr 2025 14:42:18 -0400 Subject: [PATCH 19/68] build: use scriv for changelog reading, drop our own parsing code --- Makefile | 21 +++--- ci/comment_on_fixes.py | 18 +++-- ci/github_releases.py | 168 ----------------------------------------- ci/parse_relnotes.py | 123 ------------------------------ pyproject.toml | 14 ++++ requirements/dev.in | 3 +- requirements/dev.pip | 21 +++++- 7 files changed, 58 insertions(+), 310 deletions(-) delete mode 100644 ci/github_releases.py delete mode 100644 ci/parse_relnotes.py diff --git a/Makefile b/Makefile index e03d5e5f7..37392c236 100644 --- a/Makefile +++ b/Makefile @@ -102,7 +102,7 @@ PIP_COMPILE = pip-compile ${COMPILE_OPTS} --allow-unsafe --resolver=backtracking upgrade: ## Update the *.pip files with the latest packages satisfying *.in files. $(MAKE) _upgrade COMPILE_OPTS="--upgrade" -upgrade_one: ## Update the *.pip files for one package. `make upgrade-one package=...` +upgrade_one: ## Update the *.pip files for one package. `make upgrade_one package=...` @test -n "$(package)" || { echo "\nUsage: make upgrade-one package=...\n"; exit 1; } $(MAKE) _upgrade COMPILE_OPTS="--upgrade-package $(package)" @@ -267,7 +267,7 @@ docspell: $(DOCBIN) ## Run the spell checker on the docs. ##@ Publishing docs -.PHONY: publish publishbeta relnotes_json github_releases comment_on_fixes +.PHONY: publish publishbeta github_releases comment_on_fixes WEBHOME = ~/web/stellated WEBSAMPLE = $(WEBHOME)/files/sample_coverage_html @@ -284,19 +284,20 @@ publishbeta: cp doc/sample_html_beta/*.* $(WEBSAMPLEBETA) CHANGES_MD = tmp/rst_rst/changes.md -RELNOTES_JSON = tmp/relnotes.json +SCRIV_SOURCE = tmp/only-changes.md $(CHANGES_MD): CHANGES.rst $(DOCBIN) $(SPHINXBUILD) -b rst doc tmp/rst_rst - pandoc --version pandoc -frst -tmarkdown_strict --markdown-headings=atx --wrap=none tmp/rst_rst/changes.rst > $(CHANGES_MD) -relnotes_json: $(RELNOTES_JSON) ## Convert changelog to JSON for further parsing. -$(RELNOTES_JSON): $(CHANGES_MD) - $(DOCBIN)/python ci/parse_relnotes.py tmp/rst_rst/changes.md $(RELNOTES_JSON) +$(SCRIV_SOURCE): $(CHANGES_MD) + @# Trim parts of the file that aren't changelog entries. + sed -n -e '/## Version/,/## Earlier/p' < $(CHANGES_MD) > tmp/trimmed.md + @# Replace sphinx references with published URLs. + sed -r -e 's@]\(([a-zA-Z0-9_]+)\.rst#([^)]+)\)@](https://coverage.readthedocs.io/en/latest/\1.html#\2)@g' < tmp/trimmed.md > $(SCRIV_SOURCE) -github_releases: $(RELNOTES_JSON) ## Update GitHub releases. - $(DOCBIN)/python ci/github_releases.py $(RELNOTES_JSON) $(REPO_OWNER) +github_releases: $(SCRIV_SOURCE) ## Update GitHub releases. + $(DOCBIN)/python -m scriv github-release --all -comment_on_fixes: $(RELNOTES_JSON) ## Add a comment to issues that were fixed. +comment_on_fixes: $(SCRIV_SOURCE) ## Add a comment to issues that were fixed. python ci/comment_on_fixes.py $(REPO_OWNER) diff --git a/ci/comment_on_fixes.py b/ci/comment_on_fixes.py index 7debf0bfe..d159dd157 100644 --- a/ci/comment_on_fixes.py +++ b/ci/comment_on_fixes.py @@ -3,17 +3,23 @@ """Add a release comment to all the issues mentioned in the latest release.""" -import json import re import sys +from scriv.scriv import Scriv + from session import get_session -with open("tmp/relnotes.json") as frn: - relnotes = json.load(frn) +scriv = Scriv() +changelog = scriv.changelog() +changelog.read() + +# Get the first entry in the changelog: +for etitle, sections in changelog.entries().items(): + version = etitle.split()[1] # particular to our title format. + text = "\n".join(sections) + break -latest = relnotes[0] -version = latest["version"] comment = ( f"This is now released as part of [coverage {version}]" + f"(https://pypi.org/project/coverage/{version})." @@ -21,7 +27,7 @@ print(f"Comment will be:\n\n{comment}\n") repo_owner = sys.argv[1] -url_matches = re.finditer(fr"https://github.com/{repo_owner}/(issues|pull)/(\d+)", latest["text"]) +url_matches = re.finditer(fr"https://github.com/{repo_owner}/(issues|pull)/(\d+)", text) urls = set((m[0], m[1], m[2]) for m in url_matches) for url, kind, number in urls: diff --git a/ci/github_releases.py b/ci/github_releases.py deleted file mode 100644 index eec267bc9..000000000 --- a/ci/github_releases.py +++ /dev/null @@ -1,168 +0,0 @@ -# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 -# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - -"""Upload release notes into GitHub releases.""" - -import json -import os -import shlex -import subprocess -import sys -import time - -import pkg_resources -import requests - - -RELEASES_URL = "https://api.github.com/repos/{repo}/releases" - -def run_command(cmd): - """ - Run a command line (with no shell). - - Returns a tuple: - bool: true if the command succeeded. - str: the output of the command. - - """ - proc = subprocess.run( - shlex.split(cmd), - shell=False, - check=False, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) - output = proc.stdout.decode("utf-8") - succeeded = proc.returncode == 0 - return succeeded, output - -def does_tag_exist(tag_name): - """ - Does `tag_name` exist as a tag in git? - """ - return run_command(f"git rev-parse --verify {tag_name}")[0] - -def check_ok(resp): - """ - Check that the Requests response object was successful. - - Raise an exception if not. - """ - if not resp: - print(f"text: {resp.text!r}") - resp.raise_for_status() - -def get_session(): - """ - Get an authenticated GitHub requests session. - """ - gh_session = requests.Session() - token = os.environ.get("GITHUB_TOKEN", "") - if token: - gh_session.headers["Authorization"] = f"Bearer {token}" - return gh_session - -def github_paginated(session, url): - """ - Get all the results from a paginated GitHub url. - """ - while True: - resp = session.get(url) - check_ok(resp) - yield from resp.json() - next_link = resp.links.get("next", None) - if not next_link: - break - url = next_link["url"] - -def get_releases(session, repo): - """ - Get all the releases from a name/project repo. - - Returns: - A dict mapping tag names to release dictionaries. - """ - url = RELEASES_URL.format(repo=repo) - releases = { r['tag_name']: r for r in github_paginated(session, url) } - return releases - -RELEASE_BODY_FMT = """\ -## Version {version} \N{EM DASH} {when} - -{relnote_text} - -:arrow_right:\xa0 PyPI page: [coverage {version}](https://pypi.org/project/coverage/{version}). -:arrow_right:\xa0 To install: `python3 -m pip install coverage=={version}` -""" - -def release_for_relnote(relnote, tag): - """ - Turn a release note dict into the data needed by GitHub for a release. - """ - relnote_text = relnote["text"] - version = relnote["version"] - body = RELEASE_BODY_FMT.format( - relnote_text=relnote_text, - version=version, - when=relnote["when"], - ) - return { - "tag_name": tag, - "name": version, - "body": body, - "draft": False, - "prerelease": relnote["prerelease"], - } - -def create_release(session, repo, release_data): - """ - Create a new GitHub release. - """ - print(f"Creating {release_data['name']}") - resp = session.post(RELEASES_URL.format(repo=repo), json=release_data) - check_ok(resp) - -def update_release(session, url, release_data): - """ - Update an existing GitHub release. - """ - print(f"Updating {release_data['name']}") - resp = session.patch(url, json=release_data) - check_ok(resp) - -def update_github_releases(json_filename, repo): - """ - Read the json file, and create or update releases in GitHub. - """ - gh_session = get_session() - releases = get_releases(gh_session, repo) - if 0: # if you need to delete all the releases! - for release in releases.values(): - print(release["tag_name"]) - resp = gh_session.delete(release["url"]) - check_ok(resp) - return - - with open(json_filename) as jf: - relnotes = json.load(jf) - relnotes.sort(key=lambda rel: pkg_resources.parse_version(rel["version"])) - for relnote in relnotes: - tag = relnote["version"] - if not does_tag_exist(tag): - tag = f"coverage-{tag}" - if not does_tag_exist(tag): - continue - release_data = release_for_relnote(relnote, tag) - exists = tag in releases - if not exists: - create_release(gh_session, repo, release_data) - time.sleep(3) - else: - release = releases[tag] - if release["body"] != release_data["body"]: - url = release["url"] - update_release(gh_session, url, release_data) - time.sleep(3) - -if __name__ == "__main__": - update_github_releases(*sys.argv[1:3]) diff --git a/ci/parse_relnotes.py b/ci/parse_relnotes.py deleted file mode 100644 index 6ba32e6f0..000000000 --- a/ci/parse_relnotes.py +++ /dev/null @@ -1,123 +0,0 @@ -# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 -# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - -""" -Parse CHANGES.md into a JSON structure. - -Run with two arguments: the .md file to parse, and the JSON file to write: - - python parse_relnotes.py CHANGES.md relnotes.json - -Every section that has something that looks like a version number in it will -be recorded as the release notes for that version. - -""" - -import json -import re -import sys - - -class TextChunkBuffer: - """Hold onto text chunks until needed.""" - def __init__(self): - self.buffer = [] - - def append(self, text): - """Add `text` to the buffer.""" - self.buffer.append(text) - - def clear(self): - """Clear the buffer.""" - self.buffer = [] - - def flush(self): - """Produce a ("text", text) tuple if there's anything here.""" - buffered = "".join(self.buffer).strip() - if buffered: - yield ("text", buffered) - self.clear() - - -def parse_md(lines): - """Parse markdown lines, producing (type, text) chunks.""" - buffer = TextChunkBuffer() - - for line in lines: - if header_match := re.search(r"^(#+) (.+)$", line): - yield from buffer.flush() - hashes, text = header_match.groups() - yield (f"h{len(hashes)}", text) - else: - buffer.append(line) - - yield from buffer.flush() - - -def sections(parsed_data): - """Convert a stream of parsed tokens into sections with text and notes. - - Yields a stream of: - ('h-level', 'header text', 'text') - - """ - header = None - text = [] - for ttype, ttext in parsed_data: - if ttype.startswith('h'): - if header: - yield (*header, "\n".join(text)) - text = [] - header = (ttype, ttext) - elif ttype == "text": - text.append(ttext) - else: - raise RuntimeError(f"Don't know ttype {ttype!r}") - yield (*header, "\n".join(text)) - - -def refind(regex, text): - """Find a regex in some text, and return the matched text, or None.""" - if m := re.search(regex, text): - return m.group() - else: - return None - - -def fix_ref_links(text, version): - """Find links to .rst files, and make them full RTFD links.""" - def new_link(m): - return f"](https://coverage.readthedocs.io/en/{version}/{m[1]}.html{m[2]})" - return re.sub(r"\]\((\w+)\.rst(#.*?)\)", new_link, text) - - -def relnotes(mdlines): - r"""Yield (version, text) pairs from markdown lines. - - Each tuple is a separate version mentioned in the release notes. - - A version is any section with \d\.\d in the header text. - - """ - for _, htext, text in sections(parse_md(mdlines)): - version = refind(r"\d+\.\d[^ ]*", htext) - if version: - prerelease = any(c in version for c in "abc") - when = refind(r"\d+-\d+-\d+", htext) - text = fix_ref_links(text, version) - yield { - "version": version, - "text": text, - "prerelease": prerelease, - "when": when, - } - -def parse(md_filename, json_filename): - """Main function: parse markdown and write JSON.""" - with open(md_filename) as mf: - markdown = mf.read() - with open(json_filename, "w") as jf: - json.dump(list(relnotes(markdown.splitlines(True))), jf, indent=4) - -if __name__ == "__main__": - parse(*sys.argv[1:3]) diff --git a/pyproject.toml b/pyproject.toml index 1d3199d12..54114d1db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -147,6 +147,20 @@ balanced_clumps = [ "GetZipBytesTest", ] +## Scriv + +[tool.scriv] +changelog = "tmp/only-changes.md" +ghrel_template = """ +## {{title}} + +{{body}} + +:arrow_right:\u00a0 PyPI page: [coverage {{version}}](https://pypi.org/project/coverage/{{version}}). +:arrow_right:\u00a0 To install: `python3 -m pip install coverage=={{version}}` + +""" + ## RUFF # We aren't using ruff for real yet... diff --git a/requirements/dev.in b/requirements/dev.in index 3e5c45c2c..366206ed9 100644 --- a/requirements/dev.in +++ b/requirements/dev.in @@ -19,9 +19,10 @@ pylint readme_renderer # for kitting. +libsass requests +scriv twine -libsass # Just so I have a debugger if I want it. pudb diff --git a/requirements/dev.pip b/requirements/dev.pip index a42f0a2f7..eb3deea47 100644 --- a/requirements/dev.pip +++ b/requirements/dev.pip @@ -7,7 +7,9 @@ astroid==3.3.9 # via pylint attrs==25.3.0 - # via hypothesis + # via + # hypothesis + # scriv backports-tarfile==1.2.0 # via jaraco-context build==1.2.2.post1 @@ -22,6 +24,12 @@ charset-normalizer==3.4.1 # via requests check-manifest==0.50 # via -r requirements/dev.in +click==8.1.8 + # via + # click-log + # scriv +click-log==0.4.0 + # via scriv cogapp==3.4.1 # via -r requirements/dev.in colorama==0.4.6 @@ -72,12 +80,18 @@ jaraco-functools==4.1.0 # via keyring jedi==0.19.2 # via pudb +jinja2==3.1.6 + # via scriv keyring==25.6.0 # via twine libsass==0.23.0 # via -r requirements/dev.in markdown-it-py==3.0.0 - # via rich + # via + # rich + # scriv +markupsafe==3.0.2 + # via jinja2 mccabe==0.7.0 # via pylint mdurl==0.1.2 @@ -136,6 +150,7 @@ requests==2.32.3 # -r requirements/dev.in # id # requests-toolbelt + # scriv # twine requests-toolbelt==1.0.0 # via twine @@ -143,6 +158,8 @@ rfc3986==2.0.0 # via twine rich==14.0.0 # via twine +scriv==1.7.0 + # via -r requirements/dev.in sortedcontainers==2.4.0 # via hypothesis tabulate==0.9.0 From 1eac1352364c39b448bdd37b30c020bfca960ffa Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 21 Apr 2025 09:24:06 -0400 Subject: [PATCH 20/68] build: dependabot should run on sunday --- .github/dependabot.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c6841b18a..764f927e2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,6 +9,7 @@ updates: schedule: # Check for updates to GitHub Actions once a week interval: "weekly" + day: "sunday" groups: action-dependencies: patterns: From 3d76f8a76883ad0663f38e2338f037c12636a60c Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Tue, 22 Apr 2025 03:59:05 -0600 Subject: [PATCH 21/68] test: add free-threaded CI jobs (#1956) * add free-threaded CI jobs * remove 3.14t from nightly CI --- .github/workflows/coverage.yml | 2 ++ .github/workflows/testsuite.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5654fcf66..13223bc66 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -51,7 +51,9 @@ jobs: - "3.11" - "3.12" - "3.13" + - "3.13t" - "3.14" + - "3.14t" - "pypy-3.9" - "pypy-3.10" exclude: diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 92ad7d376..4b3237d41 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -51,7 +51,9 @@ jobs: - "3.11" - "3.12" - "3.13" + - "3.13t" - "3.14" + - "3.14t" - "pypy-3.9" - "pypy-3.10" - "pypy-3.11" From 221aff66480d29c264bf224e6b6d6e5656a2de58 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 22 Apr 2025 06:00:06 -0400 Subject: [PATCH 22/68] docs: thanks Nathan Goldbaum for the free-threaded CI jobs --- CONTRIBUTORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 12fc1dab5..37fd37662 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -170,6 +170,7 @@ Michał Górny Mickie Betz Mike Fiedler Min ho Kim +Nathan Goldbaum Nathan Land Naveen Srinivasan Naveen Yadav From 62891c81d2c7f175ff14f0d492b5695f507780e8 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 22 Apr 2025 06:49:39 -0400 Subject: [PATCH 23/68] build: tailor workflows to the files that have changed --- .github/workflows/quality.yml | 33 +++++++++++++++++++++++++++++++++ .github/workflows/testsuite.yml | 31 +++++++++++++++++++++++++++---- .github/workflows/zizmor.yml | 24 ++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 708c89d6d..d8dc3f891 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -26,6 +26,30 @@ concurrency: cancel-in-progress: true jobs: + changed: + name: "Check what files changed" + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + python: ${{ steps.filter.outputs.python }} + docs: ${{ steps.filter.outputs.docs }} + steps: + - name: "Check out the repo" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: "Examine changed files" + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: filter + with: + filters: | + python: + - "**.py" + docs: + - "doc/**" + lint: name: "Pylint etc" # Because pylint can report different things on different OS's (!) @@ -35,6 +59,9 @@ jobs: # https://mastodon.social/@hugovk/112320493602782374 runs-on: macos-13 + needs: changed + if: ${{ needs.changed.outputs.python == 'true' }} + steps: - name: "Check out the repo" uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -60,6 +87,9 @@ jobs: name: "Check types" runs-on: ubuntu-latest + needs: changed + if: ${{ needs.changed.outputs.python == 'true' }} + steps: - name: "Check out the repo" uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -85,6 +115,9 @@ jobs: name: "Build docs" runs-on: ubuntu-latest + needs: changed + if: ${{ needs.changed.outputs.docs == 'true' }} + steps: - name: "Check out the repo" uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 4b3237d41..d7bed6207 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -28,12 +28,37 @@ concurrency: cancel-in-progress: true jobs: + changed: + name: "Check what files changed" + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + python: ${{ steps.filter.outputs.python }} + steps: + - name: "Check out the repo" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: "Examine changed files" + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: filter + with: + filters: | + python: + - "**.py" + tests: name: "${{ matrix.python-version }} on ${{ matrix.os }}" runs-on: "${{ matrix.os }}-${{ matrix.os-version || 'latest' }}" timeout-minutes: 30 - # Don't run tests if the branch name includes "-notests" - if: "!contains(github.ref, '-notests')" + + # Don't run tests if the branch name includes "-notests". + # Only run tests if Python files changed. + needs: changed + if: ${{ !contains(github.ref, '-notests') && needs.changed.outputs.python == 'true' }} + strategy: matrix: os: @@ -119,8 +144,6 @@ jobs: # https://github.com/orgs/community/discussions/33579 success: name: Tests successful - # The tests didn't run if the branch name includes "-notests" - if: "!contains(github.ref, '-notests')" needs: - tests runs-on: ubuntu-latest diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 282419445..3829f2099 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -14,6 +14,27 @@ on: permissions: {} jobs: + changed: + name: "Check what files changed" + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + actions: ${{ steps.filter.outputs.actions }} + steps: + - name: "Check out the repo" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: "Examine changed files" + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: filter + with: + filters: | + actions: + - ".github/workflows/**" + zizmor: name: zizmor runs-on: ubuntu-latest @@ -21,6 +42,9 @@ jobs: contents: read actions: read + needs: changed + if: ${{ needs.changed.outputs.actions == 'true' }} + steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 From 675c2f8c6c36771c85929ac9e1190b13e0269344 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 22 Apr 2025 19:46:55 -0400 Subject: [PATCH 24/68] build: put Zizmor into the Quality workflow --- .github/workflows/quality.yml | 29 ++++++++++++++++ .github/workflows/zizmor.yml | 62 ----------------------------------- 2 files changed, 29 insertions(+), 62 deletions(-) delete mode 100644 .github/workflows/zizmor.yml diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index d8dc3f891..1f3f54055 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -34,6 +34,7 @@ jobs: outputs: python: ${{ steps.filter.outputs.python }} docs: ${{ steps.filter.outputs.docs }} + actions: ${{ steps.filter.outputs.actions }} steps: - name: "Check out the repo" uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -49,6 +50,8 @@ jobs: - "**.py" docs: - "doc/**" + actions: + - ".github/workflows/**" lint: name: "Pylint etc" @@ -146,3 +149,29 @@ jobs: - name: "Tox doc" run: | python -m tox -e doc + + zizmor: + name: "Zizmor GHA security check" + runs-on: ubuntu-latest + permissions: + contents: read + actions: read + + needs: changed + if: ${{ needs.changed.outputs.actions == 'true' }} + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: Install the latest version of uv + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 #v5.4.2 + with: + enable-cache: false + + - name: Run zizmor + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: uvx zizmor --pedantic .github/workflows diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml deleted file mode 100644 index 3829f2099..000000000 --- a/.github/workflows/zizmor.yml +++ /dev/null @@ -1,62 +0,0 @@ -# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 -# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - -name: GitHub actions security - -on: - push: - branches: - - master - - nedbat/* - pull_request: - workflow_dispatch: - -permissions: {} - -jobs: - changed: - name: "Check what files changed" - runs-on: ubuntu-latest - permissions: - pull-requests: read - outputs: - actions: ${{ steps.filter.outputs.actions }} - steps: - - name: "Check out the repo" - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: "Examine changed files" - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: filter - with: - filters: | - actions: - - ".github/workflows/**" - - zizmor: - name: zizmor - runs-on: ubuntu-latest - permissions: - contents: read - actions: read - - needs: changed - if: ${{ needs.changed.outputs.actions == 'true' }} - - steps: - - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: Install the latest version of uv - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 #v5.4.2 - with: - enable-cache: false - - - name: Run zizmor - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: uvx zizmor --pedantic .github/workflows From 51ec826535ce43055c22de59982a157b40087d58 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 23 Apr 2025 06:15:13 -0400 Subject: [PATCH 25/68] build: don't run coverage workflow if no python changed --- .github/workflows/coverage.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 13223bc66..d818cd01f 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -28,10 +28,36 @@ concurrency: cancel-in-progress: true jobs: + changed: + name: "Check what files changed" + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + python: ${{ steps.filter.outputs.python }} + steps: + - name: "Check out the repo" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: "Examine changed files" + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: filter + with: + filters: | + python: + - "**.py" + coverage: name: "${{ matrix.python-version }} on ${{ matrix.os }}" runs-on: "${{ matrix.os }}-${{ matrix.os-version || 'latest' }}" timeout-minutes: 30 + + # Only run coverage if Python files changed. + needs: changed + if: ${{ needs.changed.outputs.python == 'true' }} + env: MATRIX_ID: "${{ matrix.python-version }}.${{ matrix.os }}" From 9cb9bdc6847115c1a357ec1cb95c697e49a434ae Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 23 Apr 2025 06:36:49 -0400 Subject: [PATCH 26/68] build: fine-tune the changed condition to include the workflows --- .github/workflows/coverage.yml | 13 ++++++++++--- .github/workflows/quality.yml | 29 ++++++++++++++++++++++++----- .github/workflows/testsuite.yml | 16 +++++++++++++--- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index d818cd01f..014479d9a 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -29,12 +29,13 @@ concurrency: jobs: changed: - name: "Check what files changed" + name: "Check changed files" runs-on: ubuntu-latest permissions: pull-requests: read outputs: python: ${{ steps.filter.outputs.python }} + workflow: ${{ steps.filter.outputs.workflow }} steps: - name: "Check out the repo" uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -48,15 +49,21 @@ jobs: filters: | python: - "**.py" + workflow: + - ".github/workflows/coverage.yml" coverage: name: "${{ matrix.python-version }} on ${{ matrix.os }}" runs-on: "${{ matrix.os }}-${{ matrix.os-version || 'latest' }}" timeout-minutes: 30 - # Only run coverage if Python files changed. + # Only run coverage if Python files or this workflow changed. needs: changed - if: ${{ needs.changed.outputs.python == 'true' }} + if: | + ${{ + needs.changed.outputs.python == 'true' + || needs.changed.outputs.workflow == 'true' + }} env: MATRIX_ID: "${{ matrix.python-version }}.${{ matrix.os }}" diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 1f3f54055..b78ff2a2a 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -27,7 +27,7 @@ concurrency: jobs: changed: - name: "Check what files changed" + name: "Check changed files" runs-on: ubuntu-latest permissions: pull-requests: read @@ -35,6 +35,7 @@ jobs: python: ${{ steps.filter.outputs.python }} docs: ${{ steps.filter.outputs.docs }} actions: ${{ steps.filter.outputs.actions }} + workflow: ${{ steps.filter.outputs.workflow }} steps: - name: "Check out the repo" uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -52,6 +53,8 @@ jobs: - "doc/**" actions: - ".github/workflows/**" + workflow: + - ".github/workflows/quality.yml" lint: name: "Pylint etc" @@ -63,7 +66,11 @@ jobs: runs-on: macos-13 needs: changed - if: ${{ needs.changed.outputs.python == 'true' }} + if: | + ${{ + needs.changed.outputs.python == 'true' + || needs.changed.outputs.workflow == 'true' + }} steps: - name: "Check out the repo" @@ -91,7 +98,11 @@ jobs: runs-on: ubuntu-latest needs: changed - if: ${{ needs.changed.outputs.python == 'true' }} + if: | + ${{ + needs.changed.outputs.python == 'true' + || needs.changed.outputs.workflow == 'true' + }} steps: - name: "Check out the repo" @@ -119,7 +130,11 @@ jobs: runs-on: ubuntu-latest needs: changed - if: ${{ needs.changed.outputs.docs == 'true' }} + if: | + ${{ + needs.changed.outputs.docs == 'true' + || needs.changed.outputs.workflow == 'true' + }} steps: - name: "Check out the repo" @@ -158,7 +173,11 @@ jobs: actions: read needs: changed - if: ${{ needs.changed.outputs.actions == 'true' }} + if: | + ${{ + needs.changed.outputs.actions == 'true' + || needs.changed.outputs.workflow == 'true' + }} steps: - name: Checkout repository diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index d7bed6207..dc0985548 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -29,12 +29,13 @@ concurrency: jobs: changed: - name: "Check what files changed" + name: "Check changed files" runs-on: ubuntu-latest permissions: pull-requests: read outputs: python: ${{ steps.filter.outputs.python }} + workflow: ${{ steps.filter.outputs.workflow }} steps: - name: "Check out the repo" uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -48,6 +49,8 @@ jobs: filters: | python: - "**.py" + workflow: + - ".github/workflows/testsuite.yml" tests: name: "${{ matrix.python-version }} on ${{ matrix.os }}" @@ -55,9 +58,16 @@ jobs: timeout-minutes: 30 # Don't run tests if the branch name includes "-notests". - # Only run tests if Python files changed. + # Only run tests if Python files or this workflow changed. needs: changed - if: ${{ !contains(github.ref, '-notests') && needs.changed.outputs.python == 'true' }} + if: | + ${{ + !contains(github.ref, '-notests') + && ( + needs.changed.outputs.python == 'true' + || needs.changed.outputs.workflow == 'true' + ) + }} strategy: matrix: From f9e2afd5b4dce90dea9eceabf6c0dd1de19891d9 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 24 Apr 2025 06:41:59 -0400 Subject: [PATCH 27/68] style: fix some formatting --- coverage/pytracer.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/coverage/pytracer.py b/coverage/pytracer.py index 7c119c070..eae848d28 100644 --- a/coverage/pytracer.py +++ b/coverage/pytracer.py @@ -147,8 +147,8 @@ def _trace( self, frame: FrameType, event: str, - arg: Any, # pylint: disable=unused-argument - lineno: TLineNo | None = None, # pylint: disable=unused-argument + arg: Any, # pylint: disable=unused-argument + lineno: TLineNo | None = None, # pylint: disable=unused-argument ) -> TTraceFn | None: """The trace function passed to sys.settrace.""" @@ -162,7 +162,7 @@ def _trace( # The PyTrace.stop() method has been called, possibly by another # thread, let's deactivate ourselves now. if 0: - f = frame # type: ignore[unreachable] + f = frame # type: ignore[unreachable] self.log("---\nX", f.f_code.co_filename, f.f_lineno) while f: self.log(">", f.f_code.co_filename, f.f_lineno, f.f_code.co_name, f.f_trace) @@ -306,6 +306,7 @@ def _trace( assert self.switch_context is not None self.context = None self.switch_context(None) # pylint: disable=not-callable + return self._cached_bound_method_trace def start(self) -> TTraceFn: From a9c1cb005160f92636475cc22899ac3531653d04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 27 Apr 2025 10:48:31 -0400 Subject: [PATCH 28/68] chore: bump the action-dependencies group with 4 updates (#1958) Bumps the action-dependencies group with 4 updates: [github/codeql-action](https://github.com/github/codeql-action), [actions/setup-python](https://github.com/actions/setup-python), [actions/download-artifact](https://github.com/actions/download-artifact) and [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv). Updates `github/codeql-action` from 3.28.14 to 3.28.16 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2...28deaeda66b76a05916b6923827895f2b14ab387) Updates `actions/setup-python` from 5.5.0 to 5.6.0 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/8d9ed9ac5c53483de85588cdf95a591a75ab9f55...a26af69be951a213d495a4c3e4e4022e16d87065) Updates `actions/download-artifact` from 4.2.1 to 4.3.0 - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/95815c38cf2ff2164869cbab79da8d1f422bc89e...d3f86a106a0bac45b974a628896c90dbdf5c8093) Updates `astral-sh/setup-uv` from 5.4.2 to 6.0.0 - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86...c7f87aa956e4c323abf06d5dec078e358f6b4d04) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.28.16 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: action-dependencies - dependency-name: actions/setup-python dependency-version: 5.6.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: action-dependencies - dependency-name: actions/download-artifact dependency-version: 4.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: action-dependencies - dependency-name: astral-sh/setup-uv dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: action-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/coverage.yml | 8 ++++---- .github/workflows/kit.yml | 8 ++++---- .github/workflows/publish.yml | 4 ++-- .github/workflows/python-nightly.yml | 2 +- .github/workflows/quality.yml | 8 ++++---- .github/workflows/testsuite.yml | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2f55973c7..a480b9ee1 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -51,7 +51,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2 # v3 + uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 # v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -62,7 +62,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2 # v3 + uses: github/codeql-action/autobuild@28deaeda66b76a05916b6923827895f2b14ab387 # v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -76,4 +76,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@fc7e4a0fa01c3cca5fd6a1fddec5c0740c977aa2 # v3 + uses: github/codeql-action/analyze@28deaeda66b76a05916b6923827895f2b14ab387 # v3 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 014479d9a..6e60490bc 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -119,7 +119,7 @@ jobs: persist-credentials: false - name: "Set up Python" - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "${{ matrix.python-version }}" allow-prereleases: true @@ -182,7 +182,7 @@ jobs: persist-credentials: false - name: "Set up Python" - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.9" # Minimum of PYVERSIONS # At a certain point, installing dependencies failed on pypy 3.9 and @@ -205,7 +205,7 @@ jobs: python igor.py zip_mods - name: "Download coverage data" - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: pattern: metacov-* merge-multiple: true @@ -274,7 +274,7 @@ jobs: - name: "Download coverage HTML report" if: ${{ github.ref == 'refs/heads/master' }} - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: html_report path: reports_repo/${{ env.report_dir }} diff --git a/.github/workflows/kit.yml b/.github/workflows/kit.yml index ba034963d..d56705f87 100644 --- a/.github/workflows/kit.yml +++ b/.github/workflows/kit.yml @@ -152,7 +152,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.9" # Minimum of PYVERSIONS cache: pip @@ -198,7 +198,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.9" # Minimum of PYVERSIONS cache: pip @@ -239,7 +239,7 @@ jobs: persist-credentials: false - name: "Install PyPy" - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "pypy-3.9" # Minimum of PyPy PYVERSIONS cache: pip @@ -286,7 +286,7 @@ jobs: id-token: write steps: - name: "Download artifacts" - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: pattern: dist-* merge-multiple: true diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 454104287..b2e0a78ef 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -64,7 +64,7 @@ jobs: steps: - name: "Download dists" - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: repository: "nedbat/coveragepy" run-id: ${{ needs.find-run.outputs.run-id }} @@ -104,7 +104,7 @@ jobs: steps: - name: "Download dists" - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: repository: "nedbat/coveragepy" run-id: ${{ needs.find-run.outputs.run-id }} diff --git a/.github/workflows/python-nightly.yml b/.github/workflows/python-nightly.yml index 9eab004bc..03ff73956 100644 --- a/.github/workflows/python-nightly.yml +++ b/.github/workflows/python-nightly.yml @@ -90,7 +90,7 @@ jobs: nogil: "${{ matrix.nogil || false }}" - name: "Install ${{ matrix.python-version }} with setup-python" - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 if: "startsWith(matrix.python-version, 'pypy-')" with: python-version: "${{ matrix.python-version }}" diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index b78ff2a2a..a657cd0e7 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -79,7 +79,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.9" # Minimum of PYVERSIONS cache: pip @@ -111,7 +111,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.9" # Minimum of PYVERSIONS cache: pip @@ -143,7 +143,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.11" # Doc version from PYVERSIONS cache: pip @@ -186,7 +186,7 @@ jobs: persist-credentials: false - name: Install the latest version of uv - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 #v5.4.2 + uses: astral-sh/setup-uv@c7f87aa956e4c323abf06d5dec078e358f6b4d04 #v6.0.0 with: enable-cache: false diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index dc0985548..a83af4274 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -115,7 +115,7 @@ jobs: persist-credentials: false - name: "Set up Python" - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "${{ matrix.python-version }}" allow-prereleases: true From 18f2240406d18417eac3497df6b2d184c719a7cc Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 27 Apr 2025 10:45:42 -0400 Subject: [PATCH 29/68] build: more files need to be checked for changes --- .github/workflows/coverage.yml | 14 ++++++-------- .github/workflows/quality.yml | 3 +++ .github/workflows/testsuite.yml | 18 ++++++++---------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 6e60490bc..3926c9460 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -34,7 +34,7 @@ jobs: permissions: pull-requests: read outputs: - python: ${{ steps.filter.outputs.python }} + run_coverage: ${{ steps.filter.outputs.run_coverage }} workflow: ${{ steps.filter.outputs.workflow }} steps: - name: "Check out the repo" @@ -47,10 +47,12 @@ jobs: id: filter with: filters: | - python: + run_coverage: - "**.py" - workflow: - ".github/workflows/coverage.yml" + - "tox.ini" + - "requirements/*.pip" + - "tests/gold/**" coverage: name: "${{ matrix.python-version }} on ${{ matrix.os }}" @@ -59,11 +61,7 @@ jobs: # Only run coverage if Python files or this workflow changed. needs: changed - if: | - ${{ - needs.changed.outputs.python == 'true' - || needs.changed.outputs.workflow == 'true' - }} + if: ${{ needs.changed.outputs.run_coverage == 'true' }} env: MATRIX_ID: "${{ matrix.python-version }}.${{ matrix.os }}" diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index a657cd0e7..a2bb421bf 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -51,10 +51,13 @@ jobs: - "**.py" docs: - "doc/**" + - "coverage/**.py" actions: - ".github/workflows/**" workflow: - ".github/workflows/quality.yml" + - "tox.ini" + - "requirements/*.pip" lint: name: "Pylint etc" diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index a83af4274..7c1542dcb 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -34,8 +34,7 @@ jobs: permissions: pull-requests: read outputs: - python: ${{ steps.filter.outputs.python }} - workflow: ${{ steps.filter.outputs.workflow }} + run_tests: ${{ steps.filter.outputs.run_tests }} steps: - name: "Check out the repo" uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -47,10 +46,12 @@ jobs: id: filter with: filters: | - python: + run_tests: - "**.py" - workflow: - ".github/workflows/testsuite.yml" + - "tox.ini" + - "requirements/*.pip" + - "tests/gold/**" tests: name: "${{ matrix.python-version }} on ${{ matrix.os }}" @@ -58,15 +59,12 @@ jobs: timeout-minutes: 30 # Don't run tests if the branch name includes "-notests". - # Only run tests if Python files or this workflow changed. + # Only run tests if files that affect tests have changed. needs: changed if: | ${{ - !contains(github.ref, '-notests') - && ( - needs.changed.outputs.python == 'true' - || needs.changed.outputs.workflow == 'true' - ) + needs.changed.outputs.run_tests == 'true' + && !contains(github.ref, '-notests') }} strategy: From 82b0e940a81318ed258d1d033c445ee54ab18b29 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 27 Apr 2025 12:13:56 -0400 Subject: [PATCH 30/68] chore: make upgrade doc_upgrade --- doc/requirements.pip | 18 +++++++++--------- requirements/dev.pip | 24 ++++++++++++------------ requirements/kit.pip | 16 ++++++++-------- requirements/light-threads.pip | 6 +++--- requirements/mypy.pip | 10 +++++----- requirements/pip-tools.pip | 8 ++++---- requirements/pip.pip | 4 ++-- requirements/pytest.pip | 4 ++-- requirements/tox.pip | 4 ++-- 9 files changed, 47 insertions(+), 47 deletions(-) diff --git a/doc/requirements.pip b/doc/requirements.pip index dbc988fc6..364334efa 100644 --- a/doc/requirements.pip +++ b/doc/requirements.pip @@ -12,7 +12,7 @@ anyio==4.9.0 # watchfiles babel==2.17.0 # via sphinx -certifi==2025.1.31 +certifi==2025.4.26 # via requests charset-normalizer==3.4.1 # via requests @@ -30,7 +30,7 @@ docutils==0.21.2 # restructuredtext-lint # sphinx # sphinx-rtd-theme -h11==0.14.0 +h11==0.16.0 # via uvicorn idna==3.10 # via @@ -42,7 +42,7 @@ jinja2==3.1.6 # via sphinx markupsafe==3.0.2 # via jinja2 -packaging==24.2 +packaging==25.0 # via sphinx pbr==6.1.1 # via stevedore @@ -105,21 +105,21 @@ sphinxcontrib-serializinghtml==2.0.0 # via sphinx sphinxcontrib-spelling==8.0.1 # via -r doc/requirements.in -starlette==0.46.1 +starlette==0.46.2 # via sphinx-autobuild stevedore==5.4.1 # via doc8 -typing-extensions==4.13.1 +typing-extensions==4.13.2 # via anyio -urllib3==2.3.0 +urllib3==2.4.0 # via requests -uvicorn==0.34.0 +uvicorn==0.34.2 # via sphinx-autobuild -watchfiles==1.0.4 +watchfiles==1.0.5 # via sphinx-autobuild websockets==15.0.1 # via sphinx-autobuild # The following packages are considered to be unsafe in a requirements file: -setuptools==78.1.0 +setuptools==79.0.1 # via pbr diff --git a/requirements/dev.pip b/requirements/dev.pip index eb3deea47..6e3c0119e 100644 --- a/requirements/dev.pip +++ b/requirements/dev.pip @@ -14,9 +14,9 @@ backports-tarfile==1.2.0 # via jaraco-context build==1.2.2.post1 # via check-manifest -cachetools==6.0.0b2 +cachetools==6.0.0b3 # via tox -certifi==2025.1.31 +certifi==2025.4.26 # via requests chardet==5.2.0 # via tox @@ -37,7 +37,7 @@ colorama==0.4.6 # -r /Users/ned/coverage/trunk/requirements/pytest.in # -r /Users/ned/coverage/trunk/requirements/tox.in # tox -dill==0.3.9 +dill==0.4.0 # via pylint distlib==0.3.9 # via virtualenv @@ -55,15 +55,15 @@ filelock==3.18.0 # virtualenv flaky==3.8.1 # via -r /Users/ned/coverage/trunk/requirements/pytest.in -greenlet==3.1.1 +greenlet==3.2.1 # via -r requirements/dev.in -hypothesis==6.130.10 +hypothesis==6.131.9 # via -r /Users/ned/coverage/trunk/requirements/pytest.in id==1.5.0 # via twine idna==3.10 # via requests -importlib-metadata==8.6.1 +importlib-metadata==8.7.0 # via # build # keyring @@ -96,13 +96,13 @@ mccabe==0.7.0 # via pylint mdurl==0.1.2 # via markdown-it-py -more-itertools==10.6.0 +more-itertools==10.7.0 # via # jaraco-classes # jaraco-functools nh3==0.2.21 # via readme-renderer -packaging==24.2 +packaging==25.0 # via # build # pudb @@ -182,14 +182,14 @@ tox-gh==1.5.0 # via -r /Users/ned/coverage/trunk/requirements/tox.in twine==6.1.0 # via -r requirements/dev.in -typing-extensions==4.13.1 +typing-extensions==4.13.2 # via # astroid # pylint # rich # tox # urwid -urllib3==2.3.0 +urllib3==2.4.0 # via # requests # twine @@ -210,9 +210,9 @@ zipp==3.21.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -pip==25.0.1 +pip==25.1 # via -r /Users/ned/coverage/trunk/requirements/pip.in -setuptools==78.1.0 +setuptools==79.0.1 # via # -r /Users/ned/coverage/trunk/requirements/pip.in # check-manifest diff --git a/requirements/kit.pip b/requirements/kit.pip index 471417f20..447a60162 100644 --- a/requirements/kit.pip +++ b/requirements/kit.pip @@ -14,13 +14,13 @@ bracex==2.5.post1 # via cibuildwheel build==1.2.2.post1 # via -r requirements/kit.in -certifi==2025.1.31 +certifi==2025.4.26 # via # cibuildwheel # requests charset-normalizer==3.4.1 # via requests -cibuildwheel==2.23.2 +cibuildwheel==2.23.3 # via -r requirements/kit.in colorama==0.4.6 # via -r requirements/kit.in @@ -34,7 +34,7 @@ id==1.5.0 # via twine idna==3.10 # via requests -importlib-metadata==8.6.1 +importlib-metadata==8.7.0 # via # build # keyring @@ -51,13 +51,13 @@ markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -more-itertools==10.6.0 +more-itertools==10.7.0 # via # jaraco-classes # jaraco-functools nh3==0.2.21 # via readme-renderer -packaging==24.2 +packaging==25.0 # via # auditwheel # build @@ -94,11 +94,11 @@ tomli==2.2.1 # dependency-groups twine==6.1.0 # via -r requirements/kit.in -typing-extensions==4.13.1 +typing-extensions==4.13.2 # via # cibuildwheel # rich -urllib3==2.3.0 +urllib3==2.4.0 # via # requests # twine @@ -108,5 +108,5 @@ zipp==3.21.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -setuptools==78.1.0 +setuptools==79.0.1 # via -r requirements/kit.in diff --git a/requirements/light-threads.pip b/requirements/light-threads.pip index 98054ea1f..598a012dd 100644 --- a/requirements/light-threads.pip +++ b/requirements/light-threads.pip @@ -10,9 +10,9 @@ dnspython==2.7.0 # via eventlet eventlet==0.39.1 # via -r requirements/light-threads.in -gevent==24.11.1 +gevent==25.4.2 # via -r requirements/light-threads.in -greenlet==3.1.1 +greenlet==3.2.1 # via # -r requirements/light-threads.in # eventlet @@ -25,7 +25,7 @@ zope-interface==7.2 # via gevent # The following packages are considered to be unsafe in a requirements file: -setuptools==78.1.0 +setuptools==79.0.1 # via # zope-event # zope-interface diff --git a/requirements/mypy.pip b/requirements/mypy.pip index c12a50f10..d8c4097bb 100644 --- a/requirements/mypy.pip +++ b/requirements/mypy.pip @@ -16,15 +16,15 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r /Users/ned/coverage/trunk/requirements/pytest.in -hypothesis==6.130.10 +hypothesis==6.131.9 # via -r /Users/ned/coverage/trunk/requirements/pytest.in iniconfig==2.1.0 # via pytest mypy==1.15.0 # via -r requirements/mypy.in -mypy-extensions==1.0.0 +mypy-extensions==1.1.0 # via mypy -packaging==24.2 +packaging==25.0 # via pytest pluggy==1.5.0 # via pytest @@ -46,7 +46,7 @@ types-requests==2.32.0.20250328 # via -r requirements/mypy.in types-tabulate==0.9.0.20241207 # via -r requirements/mypy.in -typing-extensions==4.13.1 +typing-extensions==4.13.2 # via mypy -urllib3==2.3.0 +urllib3==2.4.0 # via types-requests diff --git a/requirements/pip-tools.pip b/requirements/pip-tools.pip index 07e9851a6..a08d3905d 100644 --- a/requirements/pip-tools.pip +++ b/requirements/pip-tools.pip @@ -8,9 +8,9 @@ build==1.2.2.post1 # via pip-tools click==8.1.8 # via pip-tools -importlib-metadata==8.6.1 +importlib-metadata==8.7.0 # via build -packaging==24.2 +packaging==25.0 # via build pip-tools==7.4.1 # via -r requirements/pip-tools.in @@ -28,7 +28,7 @@ zipp==3.21.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -pip==25.0.1 +pip==25.1 # via pip-tools -setuptools==78.1.0 +setuptools==79.0.1 # via pip-tools diff --git a/requirements/pip.pip b/requirements/pip.pip index e14aed3ae..e4fc6ab82 100644 --- a/requirements/pip.pip +++ b/requirements/pip.pip @@ -16,7 +16,7 @@ virtualenv==20.28.1 # -r requirements/pip.in # The following packages are considered to be unsafe in a requirements file: -pip==25.0.1 +pip==25.1 # via -r requirements/pip.in -setuptools==78.1.0 +setuptools==79.0.1 # via -r requirements/pip.in diff --git a/requirements/pytest.pip b/requirements/pytest.pip index a4288fcfc..462358238 100644 --- a/requirements/pytest.pip +++ b/requirements/pytest.pip @@ -16,11 +16,11 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.130.10 +hypothesis==6.131.9 # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest -packaging==24.2 +packaging==25.0 # via pytest pluggy==1.5.0 # via pytest diff --git a/requirements/tox.pip b/requirements/tox.pip index c1766adad..64de6fbb1 100644 --- a/requirements/tox.pip +++ b/requirements/tox.pip @@ -18,7 +18,7 @@ filelock==3.18.0 # via # tox # virtualenv -packaging==24.2 +packaging==25.0 # via # pyproject-api # tox @@ -40,7 +40,7 @@ tox==4.24.1 # tox-gh tox-gh==1.5.0 # via -r requirements/tox.in -typing-extensions==4.13.1 +typing-extensions==4.13.2 # via tox virtualenv==20.28.1 # via From 5fa2bf3a2b80fb020e6bddd1241df15f182bc6ae Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 1 May 2025 18:47:52 -0400 Subject: [PATCH 31/68] chore: make upgrade doc_upgrade --- doc/requirements.pip | 4 ++-- requirements/dev.pip | 8 ++++---- requirements/kit.pip | 6 +++--- requirements/light-threads.pip | 2 +- requirements/pip-tools.pip | 4 ++-- requirements/pip.pip | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/requirements.pip b/doc/requirements.pip index 364334efa..5e2eb2684 100644 --- a/doc/requirements.pip +++ b/doc/requirements.pip @@ -14,7 +14,7 @@ babel==2.17.0 # via sphinx certifi==2025.4.26 # via requests -charset-normalizer==3.4.1 +charset-normalizer==3.4.2 # via requests click==8.1.8 # via uvicorn @@ -121,5 +121,5 @@ websockets==15.0.1 # via sphinx-autobuild # The following packages are considered to be unsafe in a requirements file: -setuptools==79.0.1 +setuptools==80.3.0 # via pbr diff --git a/requirements/dev.pip b/requirements/dev.pip index 6e3c0119e..36dac1e3c 100644 --- a/requirements/dev.pip +++ b/requirements/dev.pip @@ -14,13 +14,13 @@ backports-tarfile==1.2.0 # via jaraco-context build==1.2.2.post1 # via check-manifest -cachetools==6.0.0b3 +cachetools==6.0.0b4 # via tox certifi==2025.4.26 # via requests chardet==5.2.0 # via tox -charset-normalizer==3.4.1 +charset-normalizer==3.4.2 # via requests check-manifest==0.50 # via -r requirements/dev.in @@ -210,9 +210,9 @@ zipp==3.21.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -pip==25.1 +pip==25.1.1 # via -r /Users/ned/coverage/trunk/requirements/pip.in -setuptools==79.0.1 +setuptools==80.3.0 # via # -r /Users/ned/coverage/trunk/requirements/pip.in # check-manifest diff --git a/requirements/kit.pip b/requirements/kit.pip index 447a60162..bad3712ad 100644 --- a/requirements/kit.pip +++ b/requirements/kit.pip @@ -18,13 +18,13 @@ certifi==2025.4.26 # via # cibuildwheel # requests -charset-normalizer==3.4.1 +charset-normalizer==3.4.2 # via requests cibuildwheel==2.23.3 # via -r requirements/kit.in colorama==0.4.6 # via -r requirements/kit.in -dependency-groups==1.3.0 +dependency-groups==1.3.1 # via cibuildwheel docutils==0.21.2 # via readme-renderer @@ -108,5 +108,5 @@ zipp==3.21.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -setuptools==79.0.1 +setuptools==80.3.0 # via -r requirements/kit.in diff --git a/requirements/light-threads.pip b/requirements/light-threads.pip index 598a012dd..0b0a654de 100644 --- a/requirements/light-threads.pip +++ b/requirements/light-threads.pip @@ -25,7 +25,7 @@ zope-interface==7.2 # via gevent # The following packages are considered to be unsafe in a requirements file: -setuptools==79.0.1 +setuptools==80.3.0 # via # zope-event # zope-interface diff --git a/requirements/pip-tools.pip b/requirements/pip-tools.pip index a08d3905d..4e8d1571f 100644 --- a/requirements/pip-tools.pip +++ b/requirements/pip-tools.pip @@ -28,7 +28,7 @@ zipp==3.21.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -pip==25.1 +pip==25.1.1 # via pip-tools -setuptools==79.0.1 +setuptools==80.3.0 # via pip-tools diff --git a/requirements/pip.pip b/requirements/pip.pip index e4fc6ab82..562acafb7 100644 --- a/requirements/pip.pip +++ b/requirements/pip.pip @@ -16,7 +16,7 @@ virtualenv==20.28.1 # -r requirements/pip.in # The following packages are considered to be unsafe in a requirements file: -pip==25.1 +pip==25.1.1 # via -r requirements/pip.in -setuptools==79.0.1 +setuptools==80.3.0 # via -r requirements/pip.in From c755fb35c931d67964949bce64d289446754275e Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 3 May 2025 16:59:09 -0400 Subject: [PATCH 32/68] build: use `uv pip compile` instead of pip-tools --- Makefile | 11 ++++----- doc/requirements.pip | 12 +++------- requirements/dev.pip | 44 +++++++++++++++------------------- requirements/kit.pip | 12 +++------- requirements/light-threads.pip | 16 ++++--------- requirements/mypy.pip | 18 ++++++-------- requirements/pip-tools.in | 8 ------- requirements/pip-tools.pip | 34 -------------------------- requirements/pip.pip | 18 +++++--------- requirements/pytest.pip | 6 +---- requirements/tox.pip | 8 ++----- 11 files changed, 50 insertions(+), 137 deletions(-) delete mode 100644 requirements/pip-tools.in delete mode 100644 requirements/pip-tools.pip diff --git a/Makefile b/Makefile index 37392c236..4f72e345f 100644 --- a/Makefile +++ b/Makefile @@ -98,7 +98,7 @@ metasmoke: DOCBIN = .tox/doc/bin -PIP_COMPILE = pip-compile ${COMPILE_OPTS} --allow-unsafe --resolver=backtracking +PIP_COMPILE = uv pip compile -q ${COMPILE_OPTS} upgrade: ## Update the *.pip files with the latest packages satisfying *.in files. $(MAKE) _upgrade COMPILE_OPTS="--upgrade" @@ -106,10 +106,8 @@ upgrade_one: ## Update the *.pip files for one package. `make upgrade_one pac @test -n "$(package)" || { echo "\nUsage: make upgrade-one package=...\n"; exit 1; } $(MAKE) _upgrade COMPILE_OPTS="--upgrade-package $(package)" -_upgrade: export CUSTOM_COMPILE_COMMAND=make upgrade +_upgrade: export UV_CUSTOM_COMPILE_COMMAND=make upgrade _upgrade: - pip install -q -r requirements/pip-tools.pip - $(PIP_COMPILE) -o requirements/pip-tools.pip requirements/pip-tools.in $(PIP_COMPILE) -o requirements/pip.pip requirements/pip.in $(PIP_COMPILE) -o requirements/pytest.pip requirements/pytest.in $(PIP_COMPILE) -o requirements/kit.pip requirements/kit.in @@ -118,10 +116,9 @@ _upgrade: $(PIP_COMPILE) -o requirements/light-threads.pip requirements/light-threads.in $(PIP_COMPILE) -o requirements/mypy.pip requirements/mypy.in -doc_upgrade: export CUSTOM_COMPILE_COMMAND=make doc_upgrade +doc_upgrade: export UV_CUSTOM_COMPILE_COMMAND=make doc_upgrade doc_upgrade: $(DOCBIN) ## Update the doc/requirements.pip file - $(DOCBIN)/pip install -q -r requirements/pip-tools.pip - $(DOCBIN)/$(PIP_COMPILE) --upgrade -o doc/requirements.pip doc/requirements.in + $(PIP_COMPILE) -p $(DOCBIN)/python3 --upgrade -o doc/requirements.pip doc/requirements.in diff_upgrade: ## Summarize the last `make upgrade` @# The sort flags sort by the package name first, then by the -/+, and diff --git a/doc/requirements.pip b/doc/requirements.pip index 5e2eb2684..b6cd132b9 100644 --- a/doc/requirements.pip +++ b/doc/requirements.pip @@ -1,9 +1,5 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# +# This file was autogenerated by uv via the following command: # make doc_upgrade -# alabaster==1.0.0 # via sphinx anyio==4.9.0 @@ -66,6 +62,8 @@ restructuredtext-lint==1.4.0 # via doc8 roman-numerals-py==3.1.0 # via sphinx +setuptools==80.3.0 + # via pbr sniffio==1.3.1 # via anyio snowballstemmer==2.2.0 @@ -119,7 +117,3 @@ watchfiles==1.0.5 # via sphinx-autobuild websockets==15.0.1 # via sphinx-autobuild - -# The following packages are considered to be unsafe in a requirements file: -setuptools==80.3.0 - # via pbr diff --git a/requirements/dev.pip b/requirements/dev.pip index 36dac1e3c..9c712ce88 100644 --- a/requirements/dev.pip +++ b/requirements/dev.pip @@ -1,9 +1,5 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# +# This file was autogenerated by uv via the following command: # make upgrade -# astroid==3.3.9 # via pylint attrs==25.3.0 @@ -14,7 +10,7 @@ backports-tarfile==1.2.0 # via jaraco-context build==1.2.2.post1 # via check-manifest -cachetools==6.0.0b4 +cachetools==5.5.2 # via tox certifi==2025.4.26 # via requests @@ -34,8 +30,8 @@ cogapp==3.4.1 # via -r requirements/dev.in colorama==0.4.6 # via - # -r /Users/ned/coverage/trunk/requirements/pytest.in - # -r /Users/ned/coverage/trunk/requirements/tox.in + # -r requirements/pytest.in + # -r requirements/tox.in # tox dill==0.4.0 # via pylint @@ -54,11 +50,11 @@ filelock==3.18.0 # tox # virtualenv flaky==3.8.1 - # via -r /Users/ned/coverage/trunk/requirements/pytest.in + # via -r requirements/pytest.in greenlet==3.2.1 # via -r requirements/dev.in hypothesis==6.131.9 - # via -r /Users/ned/coverage/trunk/requirements/pytest.in + # via -r requirements/pytest.in id==1.5.0 # via twine idna==3.10 @@ -112,6 +108,8 @@ packaging==25.0 # twine parso==0.8.4 # via jedi +pip==25.1.1 + # via -r requirements/pip.in platformdirs==4.3.7 # via # pylint @@ -125,7 +123,7 @@ pudb==2024.1.3 # via -r requirements/dev.in pygments==2.19.1 # via - # -r /Users/ned/coverage/trunk/requirements/pytest.in + # -r requirements/pytest.in # pudb # readme-renderer # rich @@ -137,10 +135,10 @@ pyproject-hooks==1.2.0 # via build pytest==8.3.5 # via - # -r /Users/ned/coverage/trunk/requirements/pytest.in + # -r requirements/pytest.in # pytest-xdist pytest-xdist==3.6.1 - # via -r /Users/ned/coverage/trunk/requirements/pytest.in + # via -r requirements/pytest.in readme-renderer==44.0 # via # -r requirements/dev.in @@ -160,6 +158,10 @@ rich==14.0.0 # via twine scriv==1.7.0 # via -r requirements/dev.in +setuptools==80.3.0 + # via + # -r requirements/pip.in + # check-manifest sortedcontainers==2.4.0 # via hypothesis tabulate==0.9.0 @@ -176,10 +178,10 @@ tomlkit==0.13.2 # via pylint tox==4.24.1 # via - # -r /Users/ned/coverage/trunk/requirements/tox.in + # -r requirements/tox.in # tox-gh tox-gh==1.5.0 - # via -r /Users/ned/coverage/trunk/requirements/tox.in + # via -r requirements/tox.in twine==6.1.0 # via -r requirements/dev.in typing-extensions==4.13.2 @@ -201,18 +203,10 @@ urwid-readline==0.15.1 # via pudb virtualenv==20.28.1 # via - # -c /Users/ned/coverage/trunk/requirements/pins.pip - # -r /Users/ned/coverage/trunk/requirements/pip.in + # -c requirements/pins.pip + # -r requirements/pip.in # tox wcwidth==0.2.13 # via urwid zipp==3.21.0 # via importlib-metadata - -# The following packages are considered to be unsafe in a requirements file: -pip==25.1.1 - # via -r /Users/ned/coverage/trunk/requirements/pip.in -setuptools==80.3.0 - # via - # -r /Users/ned/coverage/trunk/requirements/pip.in - # check-manifest diff --git a/requirements/kit.pip b/requirements/kit.pip index bad3712ad..93652076e 100644 --- a/requirements/kit.pip +++ b/requirements/kit.pip @@ -1,9 +1,5 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# +# This file was autogenerated by uv via the following command: # make upgrade -# auditwheel==6.3.0 # via -r requirements/kit.in backports-tarfile==1.2.0 @@ -87,6 +83,8 @@ rfc3986==2.0.0 # via twine rich==14.0.0 # via twine +setuptools==80.3.0 + # via -r requirements/kit.in tomli==2.2.1 # via # build @@ -106,7 +104,3 @@ wheel==0.45.1 # via -r requirements/kit.in zipp==3.21.0 # via importlib-metadata - -# The following packages are considered to be unsafe in a requirements file: -setuptools==80.3.0 - # via -r requirements/kit.in diff --git a/requirements/light-threads.pip b/requirements/light-threads.pip index 0b0a654de..23785f283 100644 --- a/requirements/light-threads.pip +++ b/requirements/light-threads.pip @@ -1,9 +1,5 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# +# This file was autogenerated by uv via the following command: # make upgrade -# cffi==1.17.1 # via -r requirements/light-threads.in dnspython==2.7.0 @@ -19,13 +15,11 @@ greenlet==3.2.1 # gevent pycparser==2.22 # via cffi -zope-event==5.0 - # via gevent -zope-interface==7.2 - # via gevent - -# The following packages are considered to be unsafe in a requirements file: setuptools==80.3.0 # via # zope-event # zope-interface +zope-event==5.0 + # via gevent +zope-interface==7.2 + # via gevent diff --git a/requirements/mypy.pip b/requirements/mypy.pip index d8c4097bb..67038240b 100644 --- a/requirements/mypy.pip +++ b/requirements/mypy.pip @@ -1,13 +1,9 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# +# This file was autogenerated by uv via the following command: # make upgrade -# attrs==25.3.0 # via hypothesis colorama==0.4.6 - # via -r /Users/ned/coverage/trunk/requirements/pytest.in + # via -r requirements/pytest.in exceptiongroup==1.2.2 # via # hypothesis @@ -15,9 +11,9 @@ exceptiongroup==1.2.2 execnet==2.1.1 # via pytest-xdist flaky==3.8.1 - # via -r /Users/ned/coverage/trunk/requirements/pytest.in + # via -r requirements/pytest.in hypothesis==6.131.9 - # via -r /Users/ned/coverage/trunk/requirements/pytest.in + # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest mypy==1.15.0 @@ -29,13 +25,13 @@ packaging==25.0 pluggy==1.5.0 # via pytest pygments==2.19.1 - # via -r /Users/ned/coverage/trunk/requirements/pytest.in + # via -r requirements/pytest.in pytest==8.3.5 # via - # -r /Users/ned/coverage/trunk/requirements/pytest.in + # -r requirements/pytest.in # pytest-xdist pytest-xdist==3.6.1 - # via -r /Users/ned/coverage/trunk/requirements/pytest.in + # via -r requirements/pytest.in sortedcontainers==2.4.0 # via hypothesis tomli==2.2.1 diff --git a/requirements/pip-tools.in b/requirements/pip-tools.in deleted file mode 100644 index 4a6755620..000000000 --- a/requirements/pip-tools.in +++ /dev/null @@ -1,8 +0,0 @@ -# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 -# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - --c pins.pip - -# "make upgrade" turns this into requirements/pip-tools.pip. - -pip-tools diff --git a/requirements/pip-tools.pip b/requirements/pip-tools.pip deleted file mode 100644 index 4e8d1571f..000000000 --- a/requirements/pip-tools.pip +++ /dev/null @@ -1,34 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# -# make upgrade -# -build==1.2.2.post1 - # via pip-tools -click==8.1.8 - # via pip-tools -importlib-metadata==8.7.0 - # via build -packaging==25.0 - # via build -pip-tools==7.4.1 - # via -r requirements/pip-tools.in -pyproject-hooks==1.2.0 - # via - # build - # pip-tools -tomli==2.2.1 - # via - # build - # pip-tools -wheel==0.45.1 - # via pip-tools -zipp==3.21.0 - # via importlib-metadata - -# The following packages are considered to be unsafe in a requirements file: -pip==25.1.1 - # via pip-tools -setuptools==80.3.0 - # via pip-tools diff --git a/requirements/pip.pip b/requirements/pip.pip index 562acafb7..8294fab2b 100644 --- a/requirements/pip.pip +++ b/requirements/pip.pip @@ -1,22 +1,16 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# +# This file was autogenerated by uv via the following command: # make upgrade -# distlib==0.3.9 # via virtualenv filelock==3.18.0 # via virtualenv +pip==25.1.1 + # via -r requirements/pip.in platformdirs==4.3.7 # via virtualenv +setuptools==80.3.0 + # via -r requirements/pip.in virtualenv==20.28.1 # via - # -c /Users/ned/coverage/trunk/requirements/pins.pip + # -c requirements/pins.pip # -r requirements/pip.in - -# The following packages are considered to be unsafe in a requirements file: -pip==25.1.1 - # via -r requirements/pip.in -setuptools==80.3.0 - # via -r requirements/pip.in diff --git a/requirements/pytest.pip b/requirements/pytest.pip index 462358238..4b6c646a4 100644 --- a/requirements/pytest.pip +++ b/requirements/pytest.pip @@ -1,9 +1,5 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# +# This file was autogenerated by uv via the following command: # make upgrade -# attrs==25.3.0 # via hypothesis colorama==0.4.6 diff --git a/requirements/tox.pip b/requirements/tox.pip index 64de6fbb1..0701e1e08 100644 --- a/requirements/tox.pip +++ b/requirements/tox.pip @@ -1,9 +1,5 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# +# This file was autogenerated by uv via the following command: # make upgrade -# cachetools==5.5.2 # via tox chardet==5.2.0 @@ -44,5 +40,5 @@ typing-extensions==4.13.2 # via tox virtualenv==20.28.1 # via - # -c /Users/ned/coverage/trunk/requirements/pins.pip + # -c requirements/pins.pip # tox From 4c9ec38c2417e4fccacc94f90b273f058abea73e Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 3 May 2025 20:49:41 -0400 Subject: [PATCH 33/68] build: use uv to install dev dependencies --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4f72e345f..557483a15 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ sterile: clean ## Remove all non-controlled content, even if expensive. rm -f cheats.txt install: ## Install the developer tools - python3 -m pip install -r requirements/dev.pip + uv pip sync requirements/dev.pip ##@ Tests and quality checks From 902ef7a111a0fb5b48828f4502b01db1e9e002aa Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 4 May 2025 06:31:31 -0400 Subject: [PATCH 34/68] build: fix the changed-files checks They weren't working, maybe because GitHub doesn't like the multi-line strings? --- .github/workflows/quality.yml | 24 ++++-------------------- .github/workflows/testsuite.yml | 6 +----- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index a2bb421bf..e5645585a 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -69,11 +69,7 @@ jobs: runs-on: macos-13 needs: changed - if: | - ${{ - needs.changed.outputs.python == 'true' - || needs.changed.outputs.workflow == 'true' - }} + if: ${{ needs.changed.outputs.python == 'true' || needs.changed.outputs.workflow == 'true' }} steps: - name: "Check out the repo" @@ -101,11 +97,7 @@ jobs: runs-on: ubuntu-latest needs: changed - if: | - ${{ - needs.changed.outputs.python == 'true' - || needs.changed.outputs.workflow == 'true' - }} + if: ${{ needs.changed.outputs.python == 'true' || needs.changed.outputs.workflow == 'true' }} steps: - name: "Check out the repo" @@ -133,11 +125,7 @@ jobs: runs-on: ubuntu-latest needs: changed - if: | - ${{ - needs.changed.outputs.docs == 'true' - || needs.changed.outputs.workflow == 'true' - }} + if: ${{ needs.changed.outputs.docs == 'true' || needs.changed.outputs.workflow == 'true' }} steps: - name: "Check out the repo" @@ -176,11 +164,7 @@ jobs: actions: read needs: changed - if: | - ${{ - needs.changed.outputs.actions == 'true' - || needs.changed.outputs.workflow == 'true' - }} + if: ${{ needs.changed.outputs.actions == 'true' || needs.changed.outputs.workflow == 'true' }} steps: - name: Checkout repository diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 7c1542dcb..fb1806045 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -61,11 +61,7 @@ jobs: # Don't run tests if the branch name includes "-notests". # Only run tests if files that affect tests have changed. needs: changed - if: | - ${{ - needs.changed.outputs.run_tests == 'true' - && !contains(github.ref, '-notests') - }} + if: ${{ needs.changed.outputs.run_tests == 'true' && !contains(github.ref, '-notests') }} strategy: matrix: From 802d09ea7b41e68498528eeaf9bd81c334b4987a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 4 May 2025 09:03:20 -0400 Subject: [PATCH 35/68] chore: bump the action-dependencies group with 3 updates (#1961) Bumps the action-dependencies group with 3 updates: [github/codeql-action](https://github.com/github/codeql-action), [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) and [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv). Updates `github/codeql-action` from 3.28.16 to 3.28.17 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/28deaeda66b76a05916b6923827895f2b14ab387...60168efe1c415ce0f5521ea06d5c2062adbeed1b) Updates `actions/attest-build-provenance` from 2.2.3 to 2.3.0 - [Release notes](https://github.com/actions/attest-build-provenance/releases) - [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md) - [Commits](https://github.com/actions/attest-build-provenance/compare/c074443f1aee8d4aeeae555aebba3282517141b2...db473fddc028af60658334401dc6fa3ffd8669fd) Updates `astral-sh/setup-uv` from 6.0.0 to 6.0.1 - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/c7f87aa956e4c323abf06d5dec078e358f6b4d04...6b9c6063abd6010835644d4c2e1bef4cf5cd0fca) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.28.17 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: action-dependencies - dependency-name: actions/attest-build-provenance dependency-version: 2.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: action-dependencies - dependency-name: astral-sh/setup-uv dependency-version: 6.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: action-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/publish.yml | 4 ++-- .github/workflows/quality.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a480b9ee1..a47292b10 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -51,7 +51,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 # v3 + uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -62,7 +62,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@28deaeda66b76a05916b6923827895f2b14ab387 # v3 + uses: github/codeql-action/autobuild@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -76,4 +76,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@28deaeda66b76a05916b6923827895f2b14ab387 # v3 + uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b2e0a78ef..5c2bc4686 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -81,7 +81,7 @@ jobs: files=$(ls dist 2>/dev/null | wc -l) && [ "$files" -eq $EXPECTED ] || exit 1 - name: "Generate attestations" - uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3 + uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0 with: subject-path: "dist/*" @@ -121,7 +121,7 @@ jobs: files=$(ls dist 2>/dev/null | wc -l) && [ "$files" -eq $EXPECTED ] || exit 1 - name: "Generate attestations" - uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3 + uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0 with: subject-path: "dist/*" diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index e5645585a..2d0ca0176 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -173,7 +173,7 @@ jobs: persist-credentials: false - name: Install the latest version of uv - uses: astral-sh/setup-uv@c7f87aa956e4c323abf06d5dec078e358f6b4d04 #v6.0.0 + uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca #v6.0.1 with: enable-cache: false From 8da4a00b8e439b49dd5f207b6e2bc323d8e6c39c Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 7 May 2025 13:35:40 -0700 Subject: [PATCH 36/68] test: force local .pyc files in a stronger way. On one machine, the built-in value of sys.pycache_prefix was '/Users/nbatchelder/Library/Caches/com.apple.python' --- tests/conftest.py | 11 +++++++++-- tox.ini | 3 --- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index b75f714eb..9099801d8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -82,10 +82,17 @@ def reset_environment() -> Iterator[None]: @pytest.fixture(autouse=True) -def reset_filesdotpy_globals() -> Iterator[None]: +def reset_filesdotpy_globals() -> None: """coverage/files.py has some unfortunate globals. Reset them every test.""" set_relative_directory() - yield + +@pytest.fixture(autouse=True) +def force_local_pyc_files() -> None: + """Ensure that .pyc files are written next to source files.""" + # For some tests, we need .pyc files written in the current directory, + # so override any local setting. + sys.pycache_prefix = None + WORKER = os.getenv("PYTEST_XDIST_WORKER", "none") diff --git a/tox.ini b/tox.ini index 330a967a4..36c01712e 100644 --- a/tox.ini +++ b/tox.ini @@ -27,9 +27,6 @@ install_command = python -m pip install -U {opts} {packages} passenv = * setenv = pypy3{,9,10,11}: COVERAGE_TEST_CORES=pytrace - # For some tests, we need .pyc files written in the current directory, - # so override any local setting. - PYTHONPYCACHEPREFIX= # If we ever need a stronger way to suppress warnings: #PYTHONWARNINGS=ignore:removed in Python 3.14; use ast.Constant:DeprecationWarning # Disable CPython's color output From e308e7cc6142cc0f05d9bb2c5f11e83d23a863c2 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 10 May 2025 12:17:48 -0400 Subject: [PATCH 37/68] test: do real installs for tox test runs use_develop=True I think is part of our problem getting free-threading and gil builds to both test properly in all environments, so I'm ditching it. --- coverage/cmdline.py | 4 ++-- coverage/control.py | 4 ++-- coverage/core.py | 12 ++++++------ igor.py | 17 ++++++++++++----- tests/helpers.py | 10 ++++++++++ tests/test_debug.py | 5 ++--- tox.ini | 1 - 7 files changed, 34 insertions(+), 19 deletions(-) diff --git a/coverage/cmdline.py b/coverage/cmdline.py index 783345e01..998e6b398 100644 --- a/coverage/cmdline.py +++ b/coverage/cmdline.py @@ -21,7 +21,7 @@ from coverage import env from coverage.config import CoverageConfig from coverage.control import DEFAULT_DATAFILE -from coverage.core import HAS_CTRACER +from coverage.core import CTRACER_FILE from coverage.data import combinable_files, debug_data_file from coverage.debug import info_header, short_stack, write_formatted_info from coverage.exceptions import _BaseCoverageException, _ExceptionDuringRun, NoSource @@ -574,7 +574,7 @@ def show_help( help_params = dict(coverage.__dict__) help_params["__url__"] = __url__ help_params["program_name"] = program_name - if HAS_CTRACER: + if CTRACER_FILE: help_params["extension_modifier"] = "with C extension" else: help_params["extension_modifier"] = "without C extension" diff --git a/coverage/control.py b/coverage/control.py index 16c99f7f0..281c3aec9 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -27,7 +27,7 @@ from coverage.collector import Collector from coverage.config import CoverageConfig, read_coverage_config from coverage.context import should_start_context_test_function, combine_context_switchers -from coverage.core import Core, HAS_CTRACER +from coverage.core import Core, CTRACER_FILE from coverage.data import CoverageData, combine_parallel_data from coverage.debug import ( DebugControl, NoDebugging, short_stack, write_formatted_info, relevant_environment_display, @@ -1336,7 +1336,7 @@ def plugin_info(plugins: list[Any]) -> list[str]: ("coverage_version", covmod.__version__), ("coverage_module", covmod.__file__), ("core", self._collector.tracer_name() if self._collector is not None else "-none-"), - ("CTracer", "available" if HAS_CTRACER else "unavailable"), + ("CTracer", f"available from {CTRACER_FILE}" if CTRACER_FILE else "unavailable"), ("plugins.file_tracers", plugin_info(self._plugins.file_tracers)), ("plugins.configurers", plugin_info(self._plugins.configurers)), ("plugins.context_switchers", plugin_info(self._plugins.context_switchers)), diff --git a/coverage/core.py b/coverage/core.py index 38c27578b..c46fd241e 100644 --- a/coverage/core.py +++ b/coverage/core.py @@ -27,8 +27,8 @@ try: # Use the C extension code when we can, for speed. - from coverage.tracer import CTracer, CFileDisposition - HAS_CTRACER = True + import coverage.tracer + CTRACER_FILE: str | None = coverage.tracer.__file__ except ImportError: # Couldn't import the C extension, maybe it isn't built. if os.getenv("COVERAGE_CORE") == "ctrace": # pragma: part covered @@ -40,7 +40,7 @@ # exception here causes all sorts of other noise in unittest. sys.stderr.write("*** COVERAGE_CORE is 'ctrace' but can't import CTracer!\n") sys.exit(1) - HAS_CTRACER = False + CTRACER_FILE = None class Core: @@ -84,7 +84,7 @@ def __init__( # Someday we will default to sysmon, but it's still experimental: # if not reason_no_sysmon: # core_name = "sysmon" - if HAS_CTRACER: + if CTRACER_FILE: core_name = "ctrace" else: core_name = "pytrace" @@ -99,8 +99,8 @@ def __init__( self.packed_arcs = False self.systrace = False elif core_name == "ctrace": - self.tracer_class = CTracer - self.file_disposition_class = CFileDisposition + self.tracer_class = coverage.tracer.CTracer + self.file_disposition_class = coverage.tracer.CFileDisposition self.supports_plugins = True self.packed_arcs = True self.systrace = True diff --git a/igor.py b/igor.py index aecd46299..83c9dae76 100644 --- a/igor.py +++ b/igor.py @@ -14,6 +14,7 @@ import inspect import itertools import os +import os.path import platform import pprint import re @@ -47,6 +48,7 @@ def ignore_warnings(): yield +# $set_env.py: COVERAGE_IGOR_VERBOSE - More output from igor.py VERBOSITY = int(os.getenv("COVERAGE_IGOR_VERBOSE", "0")) # Functions named do_* are executable from the command line: do_blah is run @@ -86,21 +88,26 @@ def do_remove_extension(*args): ) roots = [root] else: - roots = ["coverage", "build/*/coverage"] + roots = [ + "coverage", + "build/*/coverage", + ".tox/*/[Ll]ib/*/site-packages/coverage", + ".tox/*/[Ll]ib/site-packages/coverage", + ] for root, pattern in itertools.product(roots, so_patterns): - pattern = os.path.join(root, pattern.strip()) + pattern = os.path.join(root, pattern) if VERBOSITY: - print(f"Searching for {pattern}") + print(f"Searching for {pattern} from {os.getcwd()}") for filename in glob.glob(pattern): if os.path.exists(filename): if VERBOSITY: - print(f"Removing {filename}") + print(f"Removing {os.path.abspath(filename)}") try: os.remove(filename) except OSError as exc: if VERBOSITY: - print(f"Couldn't remove {filename}: {exc}") + print(f"Couldn't remove {os.path.abspath(filename)}: {exc}") def label_for_core(core): diff --git a/tests/helpers.py b/tests/helpers.py index d1412afcd..637232ab2 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -379,9 +379,19 @@ def _decorator(fn: TestMethod) -> TestMethod: def all_our_source_files() -> Iterator[tuple[Path, str]]: """Iterate over all of our own source files. + This is used in tests that need a bunch of Python code to analyze, so we + might as well use our own source code as the subject. + Produces a stream of (filename, file contents) tuples. """ + print(f"all_our_source_files: {coverage.__file__ = }") cov_dir = Path(coverage.__file__).parent.parent + if ".tox" in cov_dir.parts: + # We are in a tox-installed environment, look above the .tox dir to + # also find the uninstalled source files. + cov_dir = Path(os.fspath(cov_dir).partition(".tox")[0]) + + print(f"all_our_source_files: {os.path.abspath(cov_dir) = }") # To run against all the files in the tox venvs: # for source_file in cov_dir.rglob("*.py"): for sub in [".", "benchmark", "ci", "coverage", "lab", "tests"]: diff --git a/tests/test_debug.py b/tests/test_debug.py index 651c7d7ea..163f9423f 100644 --- a/tests/test_debug.py +++ b/tests/test_debug.py @@ -199,10 +199,9 @@ def test_debug_sys_ctracer(self) -> None: out_text = self.f1_debug_output(["sys"]) tracer_line = re_line(r"CTracer:", out_text).strip() if testenv.C_TRACER or testenv.SYS_MON: - expected = "CTracer: available" + assert tracer_line.startswith("CTracer: available from ") else: - expected = "CTracer: unavailable" - assert expected == tracer_line + assert tracer_line == "CTracer: unavailable" def test_debug_pybehave(self) -> None: out_text = self.f1_debug_output(["pybehave"]) diff --git a/tox.ini b/tox.ini index 36c01712e..2c6e1831f 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,6 @@ skip_missing_interpreters = {env:COVERAGE_SKIP_MISSING_INTERPRETERS:True} toxworkdir = {env:TOXWORKDIR:.tox} [testenv] -usedevelop = True download = True extras = toml From b00205dcc1deab57a86a7a0d556b639b0053bf11 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 10 May 2025 13:55:25 -0400 Subject: [PATCH 38/68] build: use a fixed tox fork --- requirements/dev.pip | 1 + requirements/pins.pip | 4 ++-- requirements/tox.pip | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/requirements/dev.pip b/requirements/dev.pip index 9c712ce88..516b044d8 100644 --- a/requirements/dev.pip +++ b/requirements/dev.pip @@ -178,6 +178,7 @@ tomlkit==0.13.2 # via pylint tox==4.24.1 # via + # -c requirements/pins.pip # -r requirements/tox.in # tox-gh tox-gh==1.5.0 diff --git a/requirements/pins.pip b/requirements/pins.pip index 8c2147b76..0a8427b04 100644 --- a/requirements/pins.pip +++ b/requirements/pins.pip @@ -3,5 +3,5 @@ # Version pins, for use as a constraints file. -# https://github.com/pypa/virtualenv/issues/2829 -virtualenv<20.29.0 +# We need this until https://github.com/tox-dev/tox/issues/3523 is fixed. +tox @ git+https://github.com/The-Compiler/tox@python314 diff --git a/requirements/tox.pip b/requirements/tox.pip index 0701e1e08..0f1fc3f71 100644 --- a/requirements/tox.pip +++ b/requirements/tox.pip @@ -32,6 +32,7 @@ tomli==2.2.1 # tox tox==4.24.1 # via + # -c requirements/pins.pip # -r requirements/tox.in # tox-gh tox-gh==1.5.0 From 3acedae6005ba48e520839d03f9f52baa2d704d0 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 10 May 2025 14:00:42 -0400 Subject: [PATCH 39/68] chore: make upgrade --- requirements/dev.pip | 25 ++++++++++++------------- requirements/kit.pip | 4 ++-- requirements/light-threads.pip | 4 ++-- requirements/mypy.pip | 8 +++++--- requirements/pip.pip | 10 ++++------ requirements/pytest.pip | 6 ++++-- requirements/tox.pip | 10 ++++------ 7 files changed, 33 insertions(+), 34 deletions(-) diff --git a/requirements/dev.pip b/requirements/dev.pip index 516b044d8..a7c563cef 100644 --- a/requirements/dev.pip +++ b/requirements/dev.pip @@ -1,6 +1,6 @@ # This file was autogenerated by uv via the following command: # make upgrade -astroid==3.3.9 +astroid==3.3.10 # via pylint attrs==25.3.0 # via @@ -39,7 +39,7 @@ distlib==0.3.9 # via virtualenv docutils==0.21.2 # via readme-renderer -exceptiongroup==1.2.2 +exceptiongroup==1.3.0 # via # hypothesis # pytest @@ -51,9 +51,9 @@ filelock==3.18.0 # virtualenv flaky==3.8.1 # via -r requirements/pytest.in -greenlet==3.2.1 +greenlet==3.2.2 # via -r requirements/dev.in -hypothesis==6.131.9 +hypothesis==6.131.15 # via -r requirements/pytest.in id==1.5.0 # via twine @@ -110,7 +110,7 @@ parso==0.8.4 # via jedi pip==25.1.1 # via -r requirements/pip.in -platformdirs==4.3.7 +platformdirs==4.3.8 # via # pylint # tox @@ -119,7 +119,7 @@ pluggy==1.5.0 # via # pytest # tox -pudb==2024.1.3 +pudb==2025.1 # via -r requirements/dev.in pygments==2.19.1 # via @@ -127,7 +127,7 @@ pygments==2.19.1 # pudb # readme-renderer # rich -pylint==3.3.6 +pylint==3.3.7 # via -r requirements/dev.in pyproject-api==1.9.0 # via tox @@ -158,7 +158,7 @@ rich==14.0.0 # via twine scriv==1.7.0 # via -r requirements/dev.in -setuptools==80.3.0 +setuptools==80.4.0 # via # -r requirements/pip.in # check-manifest @@ -176,7 +176,7 @@ tomli==2.2.1 # tox tomlkit==0.13.2 # via pylint -tox==4.24.1 +tox @ git+https://github.com/The-Compiler/tox@3c5d56bf8bb8405d0eceef912ba4cad7d077448c # via # -c requirements/pins.pip # -r requirements/tox.in @@ -188,23 +188,22 @@ twine==6.1.0 typing-extensions==4.13.2 # via # astroid + # exceptiongroup # pylint # rich # tox - # urwid urllib3==2.4.0 # via # requests # twine -urwid==2.6.16 +urwid==3.0.2 # via # pudb # urwid-readline urwid-readline==0.15.1 # via pudb -virtualenv==20.28.1 +virtualenv==20.31.2 # via - # -c requirements/pins.pip # -r requirements/pip.in # tox wcwidth==0.2.13 diff --git a/requirements/kit.pip b/requirements/kit.pip index 93652076e..75404420c 100644 --- a/requirements/kit.pip +++ b/requirements/kit.pip @@ -60,7 +60,7 @@ packaging==25.0 # cibuildwheel # dependency-groups # twine -platformdirs==4.3.7 +platformdirs==4.3.8 # via cibuildwheel pyelftools==0.32 # via auditwheel @@ -83,7 +83,7 @@ rfc3986==2.0.0 # via twine rich==14.0.0 # via twine -setuptools==80.3.0 +setuptools==80.4.0 # via -r requirements/kit.in tomli==2.2.1 # via diff --git a/requirements/light-threads.pip b/requirements/light-threads.pip index 23785f283..09cc51e33 100644 --- a/requirements/light-threads.pip +++ b/requirements/light-threads.pip @@ -8,14 +8,14 @@ eventlet==0.39.1 # via -r requirements/light-threads.in gevent==25.4.2 # via -r requirements/light-threads.in -greenlet==3.2.1 +greenlet==3.2.2 # via # -r requirements/light-threads.in # eventlet # gevent pycparser==2.22 # via cffi -setuptools==80.3.0 +setuptools==80.4.0 # via # zope-event # zope-interface diff --git a/requirements/mypy.pip b/requirements/mypy.pip index 67038240b..5a4ee9495 100644 --- a/requirements/mypy.pip +++ b/requirements/mypy.pip @@ -4,7 +4,7 @@ attrs==25.3.0 # via hypothesis colorama==0.4.6 # via -r requirements/pytest.in -exceptiongroup==1.2.2 +exceptiongroup==1.3.0 # via # hypothesis # pytest @@ -12,7 +12,7 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.131.9 +hypothesis==6.131.15 # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest @@ -43,6 +43,8 @@ types-requests==2.32.0.20250328 types-tabulate==0.9.0.20241207 # via -r requirements/mypy.in typing-extensions==4.13.2 - # via mypy + # via + # exceptiongroup + # mypy urllib3==2.4.0 # via types-requests diff --git a/requirements/pip.pip b/requirements/pip.pip index 8294fab2b..a9b7e28eb 100644 --- a/requirements/pip.pip +++ b/requirements/pip.pip @@ -6,11 +6,9 @@ filelock==3.18.0 # via virtualenv pip==25.1.1 # via -r requirements/pip.in -platformdirs==4.3.7 +platformdirs==4.3.8 # via virtualenv -setuptools==80.3.0 +setuptools==80.4.0 + # via -r requirements/pip.in +virtualenv==20.31.2 # via -r requirements/pip.in -virtualenv==20.28.1 - # via - # -c requirements/pins.pip - # -r requirements/pip.in diff --git a/requirements/pytest.pip b/requirements/pytest.pip index 4b6c646a4..b39b9e180 100644 --- a/requirements/pytest.pip +++ b/requirements/pytest.pip @@ -4,7 +4,7 @@ attrs==25.3.0 # via hypothesis colorama==0.4.6 # via -r requirements/pytest.in -exceptiongroup==1.2.2 +exceptiongroup==1.3.0 # via # hypothesis # pytest @@ -12,7 +12,7 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.131.9 +hypothesis==6.131.15 # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest @@ -32,3 +32,5 @@ sortedcontainers==2.4.0 # via hypothesis tomli==2.2.1 # via pytest +typing-extensions==4.13.2 + # via exceptiongroup diff --git a/requirements/tox.pip b/requirements/tox.pip index 0f1fc3f71..26f42bce5 100644 --- a/requirements/tox.pip +++ b/requirements/tox.pip @@ -18,7 +18,7 @@ packaging==25.0 # via # pyproject-api # tox -platformdirs==4.3.7 +platformdirs==4.3.8 # via # tox # virtualenv @@ -30,7 +30,7 @@ tomli==2.2.1 # via # pyproject-api # tox -tox==4.24.1 +tox @ git+https://github.com/The-Compiler/tox@3c5d56bf8bb8405d0eceef912ba4cad7d077448c # via # -c requirements/pins.pip # -r requirements/tox.in @@ -39,7 +39,5 @@ tox-gh==1.5.0 # via -r requirements/tox.in typing-extensions==4.13.2 # via tox -virtualenv==20.28.1 - # via - # -c requirements/pins.pip - # tox +virtualenv==20.31.2 + # via tox From 7407d113ce0fe16c5677b123d23fb52081ff63a6 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 9 May 2025 17:51:53 -0400 Subject: [PATCH 40/68] build: free-threaded builds run from tox https://github.com/robsdedude/tox-gh-nogil-test was very helpful in working it out. --- .github/workflows/coverage.yml | 9 ++++----- .github/workflows/python-nightly.yml | 3 +++ .github/workflows/testsuite.yml | 3 +++ tox.ini | 14 ++++++++++++-- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 3926c9460..57448aba6 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -65,6 +65,7 @@ jobs: env: MATRIX_ID: "${{ matrix.python-version }}.${{ matrix.os }}" + TOX_GH_MAJOR_MINOR: "${{ matrix.python-version }}" strategy: matrix: @@ -130,22 +131,20 @@ jobs: - name: "Show environment" run: | set -xe + echo matrix id: $MATRIX_ID python -VV python -m site - env + env | sort - name: "Install dependencies" run: | - echo matrix id: $MATRIX_ID set -xe - python -VV - python -m site python -m pip install -r requirements/tox.pip - name: "Run tox coverage for ${{ matrix.python-version }}" env: COVERAGE_COVERAGE: "yes" - COVERAGE_CONTEXT: "${{ matrix.python-version }}.${{ matrix.os }}" + COVERAGE_CONTEXT: "${{ env.MATRIX_ID }}" run: | set -xe python -m tox diff --git a/.github/workflows/python-nightly.yml b/.github/workflows/python-nightly.yml index 03ff73956..1ef680e6e 100644 --- a/.github/workflows/python-nightly.yml +++ b/.github/workflows/python-nightly.yml @@ -37,6 +37,9 @@ jobs: # hours needlessly. timeout-minutes: 60 + env: + TOX_GH_MAJOR_MINOR: "${{ matrix.python-version }}" + strategy: matrix: os: diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index fb1806045..f33a2aeba 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -63,6 +63,9 @@ jobs: needs: changed if: ${{ needs.changed.outputs.run_tests == 'true' && !contains(github.ref, '-notests') }} + env: + TOX_GH_MAJOR_MINOR: "${{ matrix.python-version }}" + strategy: matrix: os: diff --git a/tox.ini b/tox.ini index 2c6e1831f..966ba3997 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ [tox] # When changing this list, be sure to check the [gh] list below. # PYVERSIONS -envlist = py3{9,10,11,12,13,14}, pypy3, anypy, doc, lint, mypy +envlist = py3{9,10,11,12,13,14}, py3{13,14}t, pypy3, anypy, doc, lint, mypy skip_missing_interpreters = {env:COVERAGE_SKIP_MISSING_INTERPRETERS:True} toxworkdir = {env:TOXWORKDIR:.tox} @@ -48,13 +48,21 @@ commands = python -m pip install {env:COVERAGE_PIP_ARGS} -q -e . python igor.py test_with_core ctrace {posargs} - py3{12,13,14},anypy: python igor.py test_with_core sysmon {posargs} + py3{12,13,14}{,t},anypy: python igor.py test_with_core sysmon {posargs} + +# Until tox properly supports no-gil interpreter selection +[testenv:py313t] +basepython = python3.13t + +[testenv:py314t] +basepython = python3.14t [testenv:anypy] # $set_env.py: COVERAGE_ANYPY - The custom Python for "tox -e anypy" # For running against my own builds of CPython, or any other specific Python. basepython = {env:COVERAGE_ANYPY} + [testenv:doc] # One of the PYVERSIONS, that's currently supported by Sphinx. Make sure it # matches the `python:version:` in the .readthedocs.yml file, and the @@ -124,5 +132,7 @@ python = 3.11 = py311 3.12 = py312 3.13 = py313 + 3.13t = py313t 3.14 = py314 + 3.14t = py314t pypy-3 = pypy3 From b7cf7d099960289e0c534e2939a803a1d92863e6 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 10 May 2025 15:13:44 -0400 Subject: [PATCH 41/68] build: skip windows 3.14 for now 3.14.0b1 is confused about -t vs non-t: https://github.com/python/cpython/issues/133779 --- .github/workflows/coverage.yml | 6 ++++++ .github/workflows/testsuite.yml | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 57448aba6..4863574a6 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -101,6 +101,12 @@ jobs: python-version: "pypy-3.9" - os: windows python-version: "pypy-3.10" + # Windows 3.14.0b1 seems confused somehow about t vs not-t: + # https://github.com/python/cpython/issues/133779 + - os: windows + python-version: "3.14" + - os: windows + python-version: "3.14t" # If we need to tweak the os version we can do it with an include like # this: # include: diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index f33a2aeba..a27e41f99 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -89,6 +89,13 @@ jobs: - "pypy-3.9" - "pypy-3.10" - "pypy-3.11" + exclude: + # Windows 3.14.0b1 seems confused somehow about t vs not-t: + # https://github.com/python/cpython/issues/133779 + - os: windows + python-version: "3.14" + - os: windows + python-version: "3.14t" # # If we need to exclude any combinations, do it like this: # exclude: From 9ba968358b21e18f673686eb0b4314d61c1885ac Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 10 May 2025 16:57:08 -0400 Subject: [PATCH 42/68] build: nightly wasn't running the correct tox environments --- .github/workflows/python-nightly.yml | 20 ++++++++++---------- tox.ini | 1 + 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/python-nightly.yml b/.github/workflows/python-nightly.yml index 1ef680e6e..4e4c9fc0a 100644 --- a/.github/workflows/python-nightly.yml +++ b/.github/workflows/python-nightly.yml @@ -38,7 +38,7 @@ jobs: timeout-minutes: 60 env: - TOX_GH_MAJOR_MINOR: "${{ matrix.python-version }}" + TOX_GH_MAJOR_MINOR: "${{ matrix.python-version }}${{ matrix.nogil && 't' || '' }}" strategy: matrix: @@ -59,22 +59,22 @@ jobs: # tox.ini so that tox will run properly. PYVERSIONS # Available versions: # https://launchpad.net/~deadsnakes/+archive/ubuntu/nightly/+packages - - "3.12-dev" - - "3.13-dev" - - "3.14-dev" + - "3.12" + - "3.13" + - "3.14" # https://github.com/actions/setup-python#available-versions-of-pypy - - "pypy-3.10-nightly" + - "pypy-3.10" nogil: - false - true include: - - python-version: "pypy-3.10-nightly" + - python-version: "pypy-3.10" os: "windows-latest" os-short: "windows" exclude: - - python-version: "3.12-dev" + - python-version: "3.12" nogil: true - - python-version: "pypy-3.10-nightly" + - python-version: "pypy-3.10" nogil: true fail-fast: false @@ -89,14 +89,14 @@ jobs: uses: deadsnakes/action@e640ac8743173a67cca4d7d77cd837e514bf98e8 # v3.2.0 if: "!startsWith(matrix.python-version, 'pypy-')" with: - python-version: "${{ matrix.python-version }}" + python-version: "${{ matrix.python-version }}-dev" nogil: "${{ matrix.nogil || false }}" - name: "Install ${{ matrix.python-version }} with setup-python" uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 if: "startsWith(matrix.python-version, 'pypy-')" with: - python-version: "${{ matrix.python-version }}" + python-version: "${{ matrix.python-version }}-nightly" - name: "Show diagnostic info" run: | diff --git a/tox.ini b/tox.ini index 966ba3997..e1c918e20 100644 --- a/tox.ini +++ b/tox.ini @@ -136,3 +136,4 @@ python = 3.14 = py314 3.14t = py314t pypy-3 = pypy3 + pypy-3.10 = pypy3 From aef89c04e416b65f98064d073d83d2a36b0a8f56 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 10 May 2025 17:17:21 -0400 Subject: [PATCH 43/68] test: switch to pypy3.11-nightly Also, Windows builds seem to lag farther behind. I'm tired of getting failure emails every day about the nightly tests failing because the builds are old. --- .github/workflows/python-nightly.yml | 12 ++++++------ tox.ini | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/python-nightly.yml b/.github/workflows/python-nightly.yml index 4e4c9fc0a..99d407291 100644 --- a/.github/workflows/python-nightly.yml +++ b/.github/workflows/python-nightly.yml @@ -63,18 +63,18 @@ jobs: - "3.13" - "3.14" # https://github.com/actions/setup-python#available-versions-of-pypy - - "pypy-3.10" + - "pypy-3.11" nogil: - false - true - include: - - python-version: "pypy-3.10" - os: "windows-latest" - os-short: "windows" + # include: + # - python-version: "pypy-3.11" + # os: "windows-latest" + # os-short: "windows" exclude: - python-version: "3.12" nogil: true - - python-version: "pypy-3.10" + - python-version: "pypy-3.11" nogil: true fail-fast: false diff --git a/tox.ini b/tox.ini index e1c918e20..ef96551cc 100644 --- a/tox.ini +++ b/tox.ini @@ -136,4 +136,4 @@ python = 3.14 = py314 3.14t = py314t pypy-3 = pypy3 - pypy-3.10 = pypy3 + pypy-3.11 = pypy3 From 7740bd4a07a1439ec269452fd4d74c233108cfa6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 May 2025 09:57:03 -0400 Subject: [PATCH 44/68] chore: bump actions/dependency-review-action (#1965) Bumps the action-dependencies group with 1 update: [actions/dependency-review-action](https://github.com/actions/dependency-review-action). Updates `actions/dependency-review-action` from 4.6.0 to 4.7.0 - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/ce3cf9537a52e8119d91fd484ab5b8a807627bf8...38ecb5b593bf0eb19e335c03f97670f792489a8b) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-version: 4.7.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: action-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependency-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 2c5057f45..84a28187a 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -28,7 +28,7 @@ jobs: persist-credentials: false - name: 'Dependency Review' - uses: actions/dependency-review-action@ce3cf9537a52e8119d91fd484ab5b8a807627bf8 # v4.6.0 + uses: actions/dependency-review-action@38ecb5b593bf0eb19e335c03f97670f792489a8b # v4.7.0 with: base-ref: ${{ github.event.pull_request.base.sha || 'master' }} head-ref: ${{ github.event.pull_request.head.sha || github.ref }} From 66476401e7b631c70dac211322530e8d466488fc Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 11 May 2025 06:22:56 -0400 Subject: [PATCH 45/68] build: add a few PHONY targets --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 557483a15..de21393dc 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ ##@ Utilities -.PHONY: help clean_platform clean sterile install +.PHONY: help _clean_platform debug_clean clean_platform clean sterile install help: ## Show this help. @# Adapted from https://www.thapaliya.com/en/writings/well-documented-makefiles/ @@ -93,7 +93,7 @@ metasmoke: # in requirements/pins.pip, and search for "windows" in .in files to find pins # and extra requirements that have been needed, but might be obsolete. -.PHONY: upgrade doc_upgrade diff_upgrade +.PHONY: upgrade upgrade_one _upgrade doc_upgrade diff_upgrade DOCBIN = .tox/doc/bin From f3eeab8e098caeb1fbd334c38fab9e5db0149aa2 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 11 May 2025 13:17:03 -0400 Subject: [PATCH 46/68] build: don't require uv just to install the dev tooling --- Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index de21393dc..bd0019d3d 100644 --- a/Makefile +++ b/Makefile @@ -50,8 +50,16 @@ sterile: clean ## Remove all non-controlled content, even if expensive. rm -rf .tox rm -f cheats.txt +# For installing development tooling, use uv if it's available, otherwise pip. +HAS_UV := $(shell command -v uv 2>/dev/null) +ifdef HAS_UV + INSTALL := uv pip sync +else + INSTALL := python -m pip install -r +endif + install: ## Install the developer tools - uv pip sync requirements/dev.pip + $(INSTALL) requirements/dev.pip ##@ Tests and quality checks From ee1761f395b4bb426a6504058e4862feac382ec8 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 13 May 2025 12:29:30 -0400 Subject: [PATCH 47/68] build: unpin tox --- requirements/dev.pip | 7 +++---- requirements/light-threads.pip | 4 ++-- requirements/mypy.pip | 2 +- requirements/pins.pip | 3 --- requirements/pytest.pip | 2 +- requirements/tox.pip | 5 ++--- 6 files changed, 9 insertions(+), 14 deletions(-) diff --git a/requirements/dev.pip b/requirements/dev.pip index a7c563cef..b22756117 100644 --- a/requirements/dev.pip +++ b/requirements/dev.pip @@ -53,7 +53,7 @@ flaky==3.8.1 # via -r requirements/pytest.in greenlet==3.2.2 # via -r requirements/dev.in -hypothesis==6.131.15 +hypothesis==6.131.16 # via -r requirements/pytest.in id==1.5.0 # via twine @@ -129,7 +129,7 @@ pygments==2.19.1 # rich pylint==3.3.7 # via -r requirements/dev.in -pyproject-api==1.9.0 +pyproject-api==1.9.1 # via tox pyproject-hooks==1.2.0 # via build @@ -176,9 +176,8 @@ tomli==2.2.1 # tox tomlkit==0.13.2 # via pylint -tox @ git+https://github.com/The-Compiler/tox@3c5d56bf8bb8405d0eceef912ba4cad7d077448c +tox==4.26.0 # via - # -c requirements/pins.pip # -r requirements/tox.in # tox-gh tox-gh==1.5.0 diff --git a/requirements/light-threads.pip b/requirements/light-threads.pip index 09cc51e33..c0bbab38f 100644 --- a/requirements/light-threads.pip +++ b/requirements/light-threads.pip @@ -4,9 +4,9 @@ cffi==1.17.1 # via -r requirements/light-threads.in dnspython==2.7.0 # via eventlet -eventlet==0.39.1 +eventlet==0.40.0 # via -r requirements/light-threads.in -gevent==25.4.2 +gevent==25.5.1 # via -r requirements/light-threads.in greenlet==3.2.2 # via diff --git a/requirements/mypy.pip b/requirements/mypy.pip index 5a4ee9495..59577725e 100644 --- a/requirements/mypy.pip +++ b/requirements/mypy.pip @@ -12,7 +12,7 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.131.15 +hypothesis==6.131.16 # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest diff --git a/requirements/pins.pip b/requirements/pins.pip index 0a8427b04..584cd28bd 100644 --- a/requirements/pins.pip +++ b/requirements/pins.pip @@ -2,6 +2,3 @@ # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt # Version pins, for use as a constraints file. - -# We need this until https://github.com/tox-dev/tox/issues/3523 is fixed. -tox @ git+https://github.com/The-Compiler/tox@python314 diff --git a/requirements/pytest.pip b/requirements/pytest.pip index b39b9e180..776d89dcb 100644 --- a/requirements/pytest.pip +++ b/requirements/pytest.pip @@ -12,7 +12,7 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.131.15 +hypothesis==6.131.16 # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest diff --git a/requirements/tox.pip b/requirements/tox.pip index 26f42bce5..cfad1d495 100644 --- a/requirements/tox.pip +++ b/requirements/tox.pip @@ -24,15 +24,14 @@ platformdirs==4.3.8 # virtualenv pluggy==1.5.0 # via tox -pyproject-api==1.9.0 +pyproject-api==1.9.1 # via tox tomli==2.2.1 # via # pyproject-api # tox -tox @ git+https://github.com/The-Compiler/tox@3c5d56bf8bb8405d0eceef912ba4cad7d077448c +tox==4.26.0 # via - # -c requirements/pins.pip # -r requirements/tox.in # tox-gh tox-gh==1.5.0 From fd8be1ddb525c524a6de19cd11493340f8ee99dd Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 13 May 2025 12:29:43 -0400 Subject: [PATCH 48/68] docs: fix a broken link --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 10bfe47ca..9288f854a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -139,7 +139,7 @@ Version 7.6.10 — 2024-12-26 .. _issue 1875: https://github.com/nedbat/coveragepy/issues/1875 .. _issue 1902: https://github.com/nedbat/coveragepy/issues/1902 .. _issue 1908: https://github.com/nedbat/coveragepy/issues/1908 -.. _pep649: https://docs.python.org/3.14/whatsnew/3.14.html#pep-649-deferred-evaluation-of-annotations +.. _pep649: https://docs.python.org/3.14/whatsnew/3.14.html#whatsnew314-pep649 .. _changes_7-6-9: From 2f1da955653547df7ed42b2246220b21024485ac Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Fri, 16 May 2025 06:40:37 -0400 Subject: [PATCH 49/68] fix: add encoding arguments, fixes #1966 * fix: add encoding arguments Signed-off-by: Henry Schreiner * fix: apply patch from review Signed-off-by: Henry Schreiner * Update tests/helpers.py * fix: erase the type to get past unreachable error Signed-off-by: Henry Schreiner --------- Signed-off-by: Henry Schreiner --- benchmark/benchmark.py | 12 ++++++------ coverage/html.py | 8 ++++---- coverage/pytracer.py | 2 +- coverage/sysmon.py | 2 +- doc/cog_helpers.py | 2 +- doc/conf.py | 2 +- igor.py | 6 +++--- lab/branch_trace.py | 2 +- lab/extract_code.py | 2 +- lab/goals.py | 2 +- lab/run_sysmon.py | 2 +- lab/run_trace.py | 2 +- lab/show_pyc.py | 2 +- setup.py | 4 ++-- tests/balance_xdist_plugin.py | 6 +++--- tests/conftest.py | 5 +++-- tests/goldtest.py | 16 ++++++++-------- tests/helpers.py | 15 ++++++++++++--- tests/osinfo.py | 2 +- tests/test_arcs.py | 14 +++++++------- tests/test_concurrency.py | 2 +- tests/test_debug.py | 4 ++-- tests/test_execfile.py | 2 +- tests/test_goldtest.py | 2 +- tests/test_html.py | 24 ++++++++++++------------ tests/test_json.py | 2 +- tests/test_lcov.py | 2 +- tests/test_oddball.py | 2 +- tests/test_parser.py | 2 +- tests/test_phystokens.py | 2 +- tests/test_plugins.py | 4 ++-- tests/test_process.py | 34 +++++++++++++++++----------------- tests/test_python.py | 6 +++--- tests/test_report.py | 2 +- tests/test_report_common.py | 2 +- tests/test_venv.py | 4 ++-- tox.ini | 8 ++++++++ 37 files changed, 115 insertions(+), 97 deletions(-) diff --git a/benchmark/benchmark.py b/benchmark/benchmark.py index 35cf938c7..726f686ff 100644 --- a/benchmark/benchmark.py +++ b/benchmark/benchmark.py @@ -148,16 +148,16 @@ def file_replace(file_name: Path, old_text: str, new_text: str) -> Iterator[None """ file_text = "" if old_text: - file_text = file_name.read_text() + file_text = file_name.read_text(encoding="utf-8") if old_text not in file_text: raise Exception("Old text {old_text!r} not found in {file_name}") updated_text = file_text.replace(old_text, new_text) - file_name.write_text(updated_text) + file_name.write_text(updated_text, encoding="utf-8") try: yield finally: if old_text: - file_name.write_text(file_text) + file_name.write_text(file_text, encoding="utf-8") def file_must_exist(file_name: str, kind: str = "file") -> Path: @@ -624,7 +624,7 @@ def run_no_coverage(self, env: Env) -> float: def run_with_coverage(self, env: Env, cov_ver: Coverage) -> float: env.shell.run_command(f"{env.python} -m pip install {cov_ver.pip_args}") pforce = Path("force.ini") - pforce.write_text("[run]\nbranch=false\n") + pforce.write_text("[run]\nbranch=false\n", encoding="utf-8") with env.shell.set_env({"COVERAGE_FORCE_CONFIG": str(pforce.resolve())}): env.shell.run_command(f"{env.python} -m pytest {self.FAST} --cov") duration = env.shell.last_duration @@ -907,13 +907,13 @@ def __init__( def save_results(self) -> None: """Save current results to the JSON file.""" - with self.results_file.open("w") as f: + with self.results_file.open("w", encoding="utf-8") as f: json.dump({" ".join(k): v for k, v in self.result_data.items()}, f) def load_results(self) -> dict[ResultKey, list[float]]: """Load results from the JSON file if it exists.""" if self.results_file.exists(): - with self.results_file.open("r") as f: + with self.results_file.open("r", encoding="utf-8") as f: data: dict[str, list[float]] = json.load(f) return { (k.split()[0], k.split()[1], k.split()[2]): v for k, v in data.items() diff --git a/coverage/html.py b/coverage/html.py index 2cc68ac1d..55dd32e2e 100644 --- a/coverage/html.py +++ b/coverage/html.py @@ -51,7 +51,7 @@ def data_filename(fname: str) -> str: def read_data(fname: str) -> str: """Return the contents of a data file of ours.""" - with open(data_filename(fname)) as data_file: + with open(data_filename(fname), encoding="utf-8") as data_file: return data_file.read() @@ -412,7 +412,7 @@ def make_local_static_report_files(self) -> None: # .gitignore can't be copied from the source tree because if it was in # the source tree, it would stop the static files from being checked in. if self.directory_was_empty: - with open(os.path.join(self.directory, ".gitignore"), "w") as fgi: + with open(os.path.join(self.directory, ".gitignore"), "w", encoding="utf-8") as fgi: fgi.write("# Created by coverage.py\n*\n") def should_report(self, analysis: Analysis, index_page: IndexPage) -> bool: @@ -706,7 +706,7 @@ def read(self) -> None: """Read the information we stored last time.""" try: status_file = os.path.join(self.directory, self.STATUS_FILE) - with open(status_file) as fstatus: + with open(status_file, encoding="utf-8") as fstatus: status = json.load(fstatus) except (OSError, ValueError): # Status file is missing or malformed. @@ -747,7 +747,7 @@ def write(self) -> None: for fname, finfo in self.files.items() }, } - with open(status_file, "w") as fout: + with open(status_file, "w", encoding="utf-8") as fout: json.dump(status_data, fout, separators=(",", ":")) def check_global_data(self, *data: Any) -> None: diff --git a/coverage/pytracer.py b/coverage/pytracer.py index eae848d28..31b4866cb 100644 --- a/coverage/pytracer.py +++ b/coverage/pytracer.py @@ -126,7 +126,7 @@ def __repr__(self) -> str: def log(self, marker: str, *args: Any) -> None: """For hard-core logging of what this tracer is doing.""" - with open("/tmp/debug_trace.txt", "a") as f: + with open("/tmp/debug_trace.txt", "a", encoding="utf-8") as f: f.write(f"{marker} {self.id}[{len(self.data_stack)}]") if 0: # if you want thread ids.. f.write(".{:x}.{:x}".format( # type: ignore[unreachable] diff --git a/coverage/sysmon.py b/coverage/sysmon.py index 8e5376cf0..b6b0d9060 100644 --- a/coverage/sysmon.py +++ b/coverage/sysmon.py @@ -104,7 +104,7 @@ def log(msg: str) -> None: # f"{root}-{pid}.out", # f"{root}-{pid}-{tslug}.out", ]: - with open(filename, "a") as f: + with open(filename, "a", encoding="utf-8") as f: try: print(f"{pid}:{tslug}: {msg}", file=f, flush=True) except UnicodeError: diff --git a/doc/cog_helpers.py b/doc/cog_helpers.py index d30030875..70161ab0d 100644 --- a/doc/cog_helpers.py +++ b/doc/cog_helpers.py @@ -52,7 +52,7 @@ def _read_config(text, fname): text = textwrap.dedent(text[1:]) os.makedirs("tmp", exist_ok=True) - with open(f"tmp/{fname}", "w") as f: + with open(f"tmp/{fname}", "w", encoding="utf-8") as f: f.write(text) config = read_coverage_config(f"tmp/{fname}", warn=cog.error) diff --git a/doc/conf.py b/doc/conf.py index 57a1ffd00..7b7040054 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -215,7 +215,7 @@ # missing, so only use the extension if we are specifically spell-checking. extensions += ['sphinxcontrib.spelling'] names_file = tempfile.NamedTemporaryFile(mode='w', prefix="coverage_names_", suffix=".txt") - with open("../CONTRIBUTORS.txt") as contributors: + with open("../CONTRIBUTORS.txt", encoding="utf-8") as contributors: names = set(re.split(r"[^\w']", contributors.read())) names = [n for n in names if len(n) >= 2 and n[0].isupper()] names_file.write("\n".join(names)) diff --git a/igor.py b/igor.py index 83c9dae76..9f7b23e12 100644 --- a/igor.py +++ b/igor.py @@ -196,7 +196,7 @@ def run_tests_with_coverage(core, *runner_args): # There's an entry in "make clean" to get rid of this file. pth_dir = sysconfig.get_path("purelib") pth_path = os.path.join(pth_dir, "zzz_metacov.pth") - with open(pth_path, "w") as pth_file: + with open(pth_path, "w", encoding="utf-8") as pth_file: pth_file.write("import coverage; coverage.process_startup()\n") suffix = f"{make_env_id(core)}_{platform.platform()}" @@ -374,14 +374,14 @@ def get_release_facts(): def update_file(fname, pattern, replacement): """Update the contents of a file, replacing pattern with replacement.""" - with open(fname) as fobj: + with open(fname, encoding="utf-8") as fobj: old_text = fobj.read() new_text = re.sub(pattern, replacement, old_text, count=1) if new_text != old_text: print(f"Updating {fname}") - with open(fname, "w") as fobj: + with open(fname, "w", encoding="utf-8") as fobj: fobj.write(new_text) diff --git a/lab/branch_trace.py b/lab/branch_trace.py index 7e8e88f9a..c2623c477 100644 --- a/lab/branch_trace.py +++ b/lab/branch_trace.py @@ -11,7 +11,7 @@ def trace(frame, event, arg): last = this return trace -code = open(sys.argv[1]).read() +code = open(sys.argv[1], encoding="utf-8").read() sys.settrace(trace) exec(code) print(sorted(pairs)) diff --git a/lab/extract_code.py b/lab/extract_code.py index 3940a2042..cf32c1730 100644 --- a/lab/extract_code.py +++ b/lab/extract_code.py @@ -52,7 +52,7 @@ def f(a, b): fname, lineno = sys.argv[1:] lineno = int(lineno) -with open(fname) as code_file: +with open(fname, encoding="utf-8") as code_file: lines = ["", *code_file] # Find opening triple-quote diff --git a/lab/goals.py b/lab/goals.py index 4bda0f0f5..13f3f68a5 100644 --- a/lab/goals.py +++ b/lab/goals.py @@ -64,7 +64,7 @@ def main(argv): print("Need either --file or --group") return 1 - with open("coverage.json") as j: + with open("coverage.json", encoding="utf-8") as j: data = json.load(j) all_files = list(data["files"].keys()) selected = select_files(all_files, args.pattern) diff --git a/lab/run_sysmon.py b/lab/run_sysmon.py index f88988bbe..fa7d44d7b 100644 --- a/lab/run_sysmon.py +++ b/lab/run_sysmon.py @@ -9,7 +9,7 @@ print(sys.version) the_program = sys.argv[1] -code = compile(open(the_program).read(), filename=the_program, mode="exec") +code = compile(open(the_program, encoding="utf-8").read(), filename=the_program, mode="exec") my_id = sys.monitoring.COVERAGE_ID sys.monitoring.use_tool_id(my_id, "run_sysmon.py") diff --git a/lab/run_trace.py b/lab/run_trace.py index 54e6a53f0..7e8354877 100644 --- a/lab/run_trace.py +++ b/lab/run_trace.py @@ -32,6 +32,6 @@ def trace(frame, event, arg): print(sys.version) the_program = sys.argv[1] -code = open(the_program).read() +code = open(the_program, encoding="utf-8").read() sys.settrace(trace) exec(code) diff --git a/lab/show_pyc.py b/lab/show_pyc.py index 0585b937f..a044eca2c 100644 --- a/lab/show_pyc.py +++ b/lab/show_pyc.py @@ -44,7 +44,7 @@ def show_pyc_file(fname): show_code(code) def show_py_file(fname): - text = open(fname).read().replace('\r\n', '\n') + text = open(fname, encoding="utf-8").read().replace('\r\n', '\n') show_py_text(text, fname=fname) def show_py_text(text, fname=""): diff --git a/setup.py b/setup.py index 8fcec5e2d..92fad6502 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ """ cov_ver_py = os.path.join(os.path.split(__file__)[0], "coverage/version.py") -with open(cov_ver_py) as version_file: +with open(cov_ver_py, encoding="utf-8") as version_file: # __doc__ will be overwritten by version.py. doc = __doc__ # Keep pylint happy. @@ -43,7 +43,7 @@ # Execute the code in version.py. exec(compile(version_file.read(), cov_ver_py, "exec", dont_inherit=True)) -with open("README.rst") as readme: +with open("README.rst", encoding="utf-8") as readme: readme_text = readme.read() temp_url = __url__.replace("readthedocs", "@@") diff --git a/tests/balance_xdist_plugin.py b/tests/balance_xdist_plugin.py index 64a8c85f1..114d4ad3b 100644 --- a/tests/balance_xdist_plugin.py +++ b/tests/balance_xdist_plugin.py @@ -71,7 +71,7 @@ def pytest_sessionstart(self, session): if self.worker == "none": if tests_csv_dir.exists(): for csv_file in tests_csv_dir.iterdir(): - with csv_file.open(newline="") as fcsv: + with csv_file.open(newline="", encoding="utf-8") as fcsv: reader = csv.reader(fcsv) for row in reader: self.times[row[1]] += float(row[3]) @@ -81,7 +81,7 @@ def write_duration_row(self, item, phase, duration): """Helper to write a row to the tracked-test csv file.""" if self.running_all: self.tests_csv.parent.mkdir(parents=True, exist_ok=True) - with self.tests_csv.open("a", newline="") as fcsv: + with self.tests_csv.open("a", newline="", encoding="utf-8") as fcsv: csv.writer(fcsv).writerow([self.worker, item.nodeid, phase, duration]) @pytest.hookimpl(hookwrapper=True) @@ -171,7 +171,7 @@ def show_worker_times(): # pragma: debugging tests_csv_dir = Path("tmp/tests_csv") for csv_file in tests_csv_dir.iterdir(): - with csv_file.open(newline="") as fcsv: + with csv_file.open(newline="", encoding="utf-8") as fcsv: reader = csv.reader(fcsv) for row in reader: worker = row[0] diff --git a/tests/conftest.py b/tests/conftest.py index 9099801d8..9cd953bbd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -104,7 +104,8 @@ def pytest_sessionstart() -> None: # Create a .pth file for measuring subprocess coverage. pth_dir = find_writable_pth_directory() assert pth_dir - (pth_dir / "subcover.pth").write_text("import coverage; coverage.process_startup()\n") + sub_dir = pth_dir / "subcover.pth" + sub_dir.write_text("import coverage; coverage.process_startup()\n", encoding="utf-8") # subcover.pth is deleted by pytest_sessionfinish below. @@ -137,7 +138,7 @@ def find_writable_pth_directory() -> Path | None: for pth_dir in possible_pth_dirs(): # pragma: part covered try_it = pth_dir / f"touch_{WORKER}.it" try: - try_it.write_text("foo") + try_it.write_text("foo", encoding="utf-8") except OSError: # pragma: cant happen continue diff --git a/tests/goldtest.py b/tests/goldtest.py index 7ca4af159..4ff8276f2 100644 --- a/tests/goldtest.py +++ b/tests/goldtest.py @@ -59,8 +59,8 @@ def save_mismatch(f: str) -> None: save_path = expected_dir.replace(os_sep("/gold/"), os_sep("/actual/")) os.makedirs(save_path, exist_ok=True) save_file = os.path.join(save_path, f) - with open(save_file, "w") as savef: - with open(os.path.join(actual_dir, f)) as readf: + with open(save_file, "w", encoding="utf-8") as savef: + with open(os.path.join(actual_dir, f), encoding="utf-8") as readf: savef.write(readf.read()) print(os_sep(f"Saved actual output to '{save_file}': see tests/gold/README.rst")) @@ -70,13 +70,13 @@ def save_mismatch(f: str) -> None: text_diff = [] for f in diff_files: expected_file = os.path.join(expected_dir, f) - with open(expected_file) as fobj: + with open(expected_file, encoding="utf-8") as fobj: expected = fobj.read() if expected_file.endswith(".xml"): expected = canonicalize_xml(expected) actual_file = os.path.join(actual_dir, f) - with open(actual_file) as fobj: + with open(actual_file, encoding="utf-8") as fobj: actual = fobj.read() if actual_file.endswith(".xml"): actual = canonicalize_xml(actual) @@ -114,7 +114,7 @@ def contains(filename: str, *strlist: str) -> None: """ __tracebackhide__ = True # pytest, please don't show me this function. - with open(filename) as fobj: + with open(filename, encoding="utf-8") as fobj: text = fobj.read() for s in strlist: assert s in text, f"Missing content in {filename}: {s!r}" @@ -128,7 +128,7 @@ def contains_rx(filename: str, *rxlist: str) -> None: """ __tracebackhide__ = True # pytest, please don't show me this function. - with open(filename) as fobj: + with open(filename, encoding="utf-8") as fobj: lines = fobj.readlines() for rx in rxlist: assert any(re.search(rx, line) for line in lines), ( @@ -144,7 +144,7 @@ def contains_any(filename: str, *strlist: str) -> None: """ __tracebackhide__ = True # pytest, please don't show me this function. - with open(filename) as fobj: + with open(filename, encoding="utf-8") as fobj: text = fobj.read() for s in strlist: if s in text: @@ -161,7 +161,7 @@ def doesnt_contain(filename: str, *strlist: str) -> None: """ __tracebackhide__ = True # pytest, please don't show me this function. - with open(filename) as fobj: + with open(filename, encoding="utf-8") as fobj: text = fobj.read() for s in strlist: assert s not in text, f"Forbidden content in {filename}: {s!r}" diff --git a/tests/helpers.py b/tests/helpers.py index 637232ab2..b3ab529cf 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -15,6 +15,7 @@ import re import shutil import subprocess +import sys import textwrap import warnings @@ -42,10 +43,18 @@ def run_command(cmd: str) -> tuple[int, str]: # Subprocesses are expensive, but convenient, and so may be over-used in # the test suite. Use these lines to get a list of the tests using them: if 0: # pragma: debugging - with open("/tmp/processes.txt", "a") as proctxt: # type: ignore[unreachable] + pth = "/tmp/processes.txt" # type: ignore[unreachable] + with open(pth, "a", encoding="utf-8") as proctxt: print(os.getenv("PYTEST_CURRENT_TEST", "unknown"), file=proctxt, flush=True) - encoding = os.device_encoding(1) or locale.getpreferredencoding() + # Type checking trick due to "unreachable" being set + _locale_type_erased: Any = locale + + encoding = os.device_encoding(1) or ( + _locale_type_erased.getpreferredencoding() + if sys.version_info < (3, 11) + else _locale_type_erased.getencoding() + ) # In some strange cases (PyPy3 in a virtualenv!?) the stdout encoding of # the subprocess is set incorrectly to ascii. Use an environment variable @@ -113,7 +122,7 @@ def make_file( if text and basename.endswith(".py") and SHOW_DIS: # pragma: debugging os.makedirs("/tmp/dis", exist_ok=True) - with open(f"/tmp/dis/{basename}.dis", "w") as fdis: + with open(f"/tmp/dis/{basename}.dis", "w", encoding="utf-8") as fdis: print(f"# {os.path.abspath(filename)}", file=fdis) cur_test = os.getenv("PYTEST_CURRENT_TEST", "unknown") print(f"# PYTEST_CURRENT_TEST = {cur_test}", file=fdis) diff --git a/tests/osinfo.py b/tests/osinfo.py index f55fe88c1..e90d5dcf2 100644 --- a/tests/osinfo.py +++ b/tests/osinfo.py @@ -64,7 +64,7 @@ def _VmB(key: str) -> int: """Read the /proc/PID/status file to find memory use.""" try: # Get pseudo file /proc//status - with open(f"/proc/{os.getpid()}/status") as t: + with open(f"/proc/{os.getpid()}/status", encoding="utf-8") as t: v = t.read() except OSError: # pragma: cant happen return 0 # non-Linux? diff --git a/tests/test_arcs.py b/tests/test_arcs.py index 7ec0e663a..966f80241 100644 --- a/tests/test_arcs.py +++ b/tests/test_arcs.py @@ -188,7 +188,7 @@ class WithTest(CoverageTest): def test_with(self) -> None: self.check_coverage("""\ def example(): - with open("test", "w") as f: + with open("test", "w", encoding="utf-8") as f: f.write("3") a = 4 @@ -201,7 +201,7 @@ def example(): def test_with_return(self) -> None: self.check_coverage("""\ def example(): - with open("test", "w") as f: + with open("test", "w", encoding="utf-8") as f: f.write("3") return 4 @@ -215,7 +215,7 @@ def test_bug_146(self) -> None: # https://github.com/nedbat/coveragepy/issues/146 self.check_coverage("""\ for i in range(2): - with open("test", "w") as f: + with open("test", "w", encoding="utf-8") as f: print(3) print(4) print(5) @@ -228,9 +228,9 @@ def test_bug_146(self) -> None: def test_nested_with_return(self) -> None: self.check_coverage("""\ def example(x): - with open("test", "w") as f2: + with open("test", "w", encoding="utf-8") as f2: a = 3 - with open("test2", "w") as f4: + with open("test2", "w", encoding="utf-8") as f4: f2.write("5") return 6 @@ -243,7 +243,7 @@ def example(x): def test_break_through_with(self) -> None: self.check_coverage("""\ for i in range(1+1): - with open("test", "w") as f: + with open("test", "w", encoding="utf-8") as f: print(3) break print(5) @@ -255,7 +255,7 @@ def test_break_through_with(self) -> None: def test_continue_through_with(self) -> None: self.check_coverage("""\ for i in range(1+1): - with open("test", "w") as f: + with open("test", "w", encoding="utf-8") as f: print(3) continue print(5) diff --git a/tests/test_concurrency.py b/tests/test_concurrency.py index 98f72f77a..c9c25d934 100644 --- a/tests/test_concurrency.py +++ b/tests/test_concurrency.py @@ -653,7 +653,7 @@ def test_thread_safe_save_data(tmp_path: pathlib.Path) -> None: modules_dir.mkdir() module_names = [f"m{i:03d}" for i in range(1000)] for module_name in module_names: - (modules_dir / (module_name + ".py")).write_text("def f(): pass\n") + (modules_dir / (module_name + ".py")).write_text("def f(): pass\n", encoding="utf-8") # Shared variables for threads should_run = [True] diff --git a/tests/test_debug.py b/tests/test_debug.py index 163f9423f..d1ab6f57c 100644 --- a/tests/test_debug.py +++ b/tests/test_debug.py @@ -268,14 +268,14 @@ def test_envvar(self) -> None: self.set_environ("COVERAGE_DEBUG_FILE", "debug.out") self.debug_sys() assert ("", "") == self.stdouterr() - with open("debug.out") as f: + with open("debug.out", encoding="utf-8") as f: assert_good_debug_sys(f.read()) def test_config_file(self) -> None: self.make_file(".coveragerc", "[run]\ndebug_file = lotsa_info.txt") self.debug_sys() assert ("", "") == self.stdouterr() - with open("lotsa_info.txt") as f: + with open("lotsa_info.txt", encoding="utf-8") as f: assert_good_debug_sys(f.read()) def test_stdout_alias(self) -> None: diff --git a/tests/test_execfile.py b/tests/test_execfile.py index cd12dea99..930a31a0d 100644 --- a/tests/test_execfile.py +++ b/tests/test_execfile.py @@ -89,7 +89,7 @@ def test_missing_final_newline(self) -> None: a = 1 print(f"a is {a!r}") #""") - with open("abrupt.py") as f: + with open("abrupt.py", encoding="utf-8") as f: abrupt = f.read() assert abrupt[-1] == '#' run_python_file(["abrupt.py"]) diff --git a/tests/test_goldtest.py b/tests/test_goldtest.py index f4972ab1c..113ecc8c6 100644 --- a/tests/test_goldtest.py +++ b/tests/test_goldtest.py @@ -80,7 +80,7 @@ def test_bad(self) -> None: assert " D/D/D, Gxxx, Pennsylvania" in stdout # The actual file was saved. - with open(ACTUAL_GETTY_FILE) as f: + with open(ACTUAL_GETTY_FILE, encoding="utf-8") as f: saved = f.read() assert saved == BAD_GETTY diff --git a/tests/test_html.py b/tests/test_html.py index 67b8933aa..627690bd7 100644 --- a/tests/test_html.py +++ b/tests/test_html.py @@ -72,7 +72,7 @@ def get_html_report_content(self, module: str) -> str: """Return the content of the HTML report for `module`.""" filename = flat_rootname(module) + ".html" filename = os.path.join("htmlcov", filename) - with open(filename) as f: + with open(filename, encoding="utf-8") as f: return f.read() def get_html_index_content(self) -> str: @@ -81,7 +81,7 @@ def get_html_index_content(self) -> str: Time stamps are replaced with a placeholder so that clocks don't matter. """ - with open("htmlcov/index.html") as f: + with open("htmlcov/index.html", encoding="utf-8") as f: index = f.read() index = re.sub( r"created at \d{4}-\d{2}-\d{2} \d{2}:\d{2} \+\d{4}", @@ -122,7 +122,7 @@ def assert_valid_hrefs(self, directory: str = "htmlcov") -> None: """ hrefs = collections.defaultdict(set) for fname in glob.glob(f"{directory}/*.html"): - with open(fname) as fhtml: + with open(fname, encoding="utf-8") as fhtml: html = fhtml.read() for href in re.findall(r""" href=['"]([^'"]*)['"]""", html): if href.startswith("#"): @@ -182,11 +182,11 @@ class FileWriteTracker: def __init__(self, written: set[str]) -> None: self.written = written - def open(self, filename: str, mode: str = "r") -> IO[str]: + def open(self, filename: str, mode: str = "r", encoding: str | None = None) -> IO[str]: """Be just like `open`, but write written file names to `self.written`.""" if mode.startswith("w"): self.written.add(filename.replace('\\', '/')) - return open(filename, mode) + return open(filename, mode, encoding=encoding) class HtmlDeltaTest(HtmlTestHelpers, CoverageTest): @@ -361,12 +361,12 @@ def test_status_format_change(self) -> None: self.create_initial_files() self.run_coverage() - with open("htmlcov/status.json") as status_json: + with open("htmlcov/status.json", encoding="utf-8") as status_json: status_data = json.load(status_json) assert status_data['format'] == 5 status_data['format'] = 99 - with open("htmlcov/status.json", "w") as status_json: + with open("htmlcov/status.json", "w", encoding="utf-8") as status_json: json.dump(status_data, status_json) self.run_coverage() @@ -382,7 +382,7 @@ def test_dont_overwrite_gitignore(self) -> None: self.create_initial_files() self.make_file("htmlcov/.gitignore", "# ignore nothing") self.run_coverage() - with open("htmlcov/.gitignore") as fgi: + with open("htmlcov/.gitignore", encoding="utf-8") as fgi: assert fgi.read() == "# ignore nothing" def test_dont_write_gitignore_into_existing_directory(self) -> None: @@ -609,9 +609,9 @@ def test_has_date_stamp_in_files(self) -> None: self.create_initial_files() self.run_coverage() - with open("htmlcov/index.html") as f: + with open("htmlcov/index.html", encoding="utf-8") as f: self.assert_correct_timestamp(f.read()) - with open("htmlcov/main_file_py.html") as f: + with open("htmlcov/main_file_py.html", encoding="utf-8") as f: self.assert_correct_timestamp(f.read()) def test_reporting_on_unmeasured_file(self) -> None: @@ -1259,7 +1259,7 @@ def test_accented_dot_py(self) -> None: cov.load() cov.html_report() self.assert_exists("htmlcov/h\xe2t_py.html") - with open("htmlcov/index.html") as indexf: + with open("htmlcov/index.html", encoding="utf-8") as indexf: index = indexf.read() assert 'hât.py' in index @@ -1273,7 +1273,7 @@ def test_accented_directory(self) -> None: cov.load() cov.html_report() self.assert_exists("htmlcov/z_5786906b6f0ffeb4_accented_py.html") - with open("htmlcov/index.html") as indexf: + with open("htmlcov/index.html", encoding="utf-8") as indexf: index = indexf.read() expected = 'â%saccented.py' assert expected % os.sep in index diff --git a/tests/test_json.py b/tests/test_json.py index e5b116ac4..aeeb81c35 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -83,7 +83,7 @@ def _compare_json_reports( mod = self.start_import_stop(cov, mod_name) output_path = os.path.join(self.temp_dir, f"{mod_name}.json") cov.json_report(mod, outfile=output_path) - with open(output_path) as result_file: + with open(output_path, encoding="utf-8") as result_file: parsed_result = json.load(result_file) self.assert_recent_datetime( datetime.strptime(parsed_result['meta']['timestamp'], "%Y-%m-%dT%H:%M:%S.%f"), diff --git a/tests/test_lcov.py b/tests/test_lcov.py index 76e99e91d..f4dff3801 100644 --- a/tests/test_lcov.py +++ b/tests/test_lcov.py @@ -43,7 +43,7 @@ def test_volume(self): def get_lcov_report_content(self, filename: str = "coverage.lcov") -> str: """Return the content of an LCOV report.""" - with open(filename) as file: + with open(filename, encoding="utf-8") as file: return file.read() def test_lone_file(self) -> None: diff --git a/tests/test_oddball.py b/tests/test_oddball.py index 7d3199fb5..b253b8927 100644 --- a/tests/test_oddball.py +++ b/tests/test_oddball.py @@ -599,7 +599,7 @@ def test_correct_filename(self) -> None: """) self.make_file("main.py", """\ namespace = {'var': 17} - with open("to_exec.py") as to_exec_py: + with open("to_exec.py", encoding="utf-8") as to_exec_py: code = compile(to_exec_py.read(), 'to_exec.py', 'exec') exec(code, globals(), namespace) \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n diff --git a/tests/test_parser.py b/tests/test_parser.py index 8c9f2e7ad..1e9a5db72 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -1183,7 +1183,7 @@ def test_missing_line_ending(self) -> None: stderr=subprocess.PIPE).communicate()""") # no final newline. # Double-check that some test helper wasn't being helpful. - with open("abrupt.py") as f: + with open("abrupt.py", encoding="utf-8") as f: assert f.read()[-1] == ")" parser = self.parse_file("abrupt.py") diff --git a/tests/test_phystokens.py b/tests/test_phystokens.py index 0a863ab6e..49d8d25a7 100644 --- a/tests/test_phystokens.py +++ b/tests/test_phystokens.py @@ -128,7 +128,7 @@ def test_stress(self, fname: str) -> None: stress = os.path.join(TESTS_DIR, fname) self.check_file_tokenization(stress) - with open(stress) as fstress: + with open(stress, encoding="utf-8") as fstress: assert re.search(r"(?m) $", fstress.read()), f"{stress} needs a trailing space." diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 44b139e57..a94104a01 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -171,7 +171,7 @@ class Plugin(CoveragePlugin): pass def coverage_init(reg, options): reg.add_noop(Plugin()) - with open("evidence.out", "w") as f: + with open("evidence.out", "w", encoding="utf-8") as f: f.write("we are here!") """) @@ -181,7 +181,7 @@ def coverage_init(reg, options): cov.start() cov.stop() # pragma: nested - with open("evidence.out") as f: + with open("evidence.out", encoding="utf-8") as f: assert f.read() == "we are here!" def test_missing_plugin_raises_import_error(self) -> None: diff --git a/tests/test_process.py b/tests/test_process.py index 2466de081..d2c95bbda 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -410,7 +410,7 @@ def test_fork(self) -> None: data.read() assert line_counts(data)["fork.py"] == total_lines - debug_text = Path("debug.out").read_text() + debug_text = Path("debug.out").read_text(encoding="utf-8") ppid = pids["parent"] cpid = pids["child"] assert ppid != cpid @@ -670,14 +670,14 @@ def assert_tryexecfile_output(self, expected: str, actual: str) -> None: assert actual == expected def test_coverage_run_is_like_python(self) -> None: - with open(TRY_EXECFILE) as f: + with open(TRY_EXECFILE, encoding="utf-8") as f: self.make_file("run_me.py", f.read()) expected = self.run_command("python run_me.py") actual = self.run_command("coverage run run_me.py") self.assert_tryexecfile_output(expected, actual) def test_coverage_run_far_away_is_like_python(self) -> None: - with open(TRY_EXECFILE) as f: + with open(TRY_EXECFILE, encoding="utf-8") as f: self.make_file("sub/overthere/prog.py", f.read()) expected = self.run_command("python sub/overthere/prog.py") actual = self.run_command("coverage run sub/overthere/prog.py") @@ -685,7 +685,7 @@ def test_coverage_run_far_away_is_like_python(self) -> None: @pytest.mark.skipif(not env.WINDOWS, reason="This is about Windows paths") def test_coverage_run_far_away_is_like_python_windows(self) -> None: - with open(TRY_EXECFILE) as f: + with open(TRY_EXECFILE, encoding="utf-8") as f: self.make_file("sub/overthere/prog.py", f.read()) expected = self.run_command("python sub\\overthere\\prog.py") actual = self.run_command("coverage run sub\\overthere\\prog.py") @@ -698,7 +698,7 @@ def test_coverage_run_dashm_is_like_python_dashm(self) -> None: self.assert_tryexecfile_output(expected, actual) def test_coverage_run_dir_is_like_python_dir(self) -> None: - with open(TRY_EXECFILE) as f: + with open(TRY_EXECFILE, encoding="utf-8") as f: self.make_file("with_main/__main__.py", f.read()) expected = self.run_command("python with_main") @@ -706,7 +706,7 @@ def test_coverage_run_dir_is_like_python_dir(self) -> None: self.assert_tryexecfile_output(expected, actual) def test_coverage_run_dashm_dir_no_init_is_like_python(self) -> None: - with open(TRY_EXECFILE) as f: + with open(TRY_EXECFILE, encoding="utf-8") as f: self.make_file("with_main/__main__.py", f.read()) expected = self.run_command("python -m with_main") @@ -714,7 +714,7 @@ def test_coverage_run_dashm_dir_no_init_is_like_python(self) -> None: self.assert_tryexecfile_output(expected, actual) def test_coverage_run_dashm_dir_with_init_is_like_python(self) -> None: - with open(TRY_EXECFILE) as f: + with open(TRY_EXECFILE, encoding="utf-8") as f: self.make_file("with_main/__main__.py", f.read()) self.make_file("with_main/__init__.py", "") @@ -782,7 +782,7 @@ def test_coverage_run_script_imports_doubledashsource(self) -> None: def test_coverage_run_dashm_is_like_python_dashm_off_path(self) -> None: # https://github.com/nedbat/coveragepy/issues/242 self.make_file("sub/__init__.py", "") - with open(TRY_EXECFILE) as f: + with open(TRY_EXECFILE, encoding="utf-8") as f: self.make_file("sub/run_me.py", f.read()) expected = self.run_command("python -m sub.run_me") @@ -801,7 +801,7 @@ def test_coverage_zip_is_like_python(self) -> None: # Test running coverage from a zip file itself. Some environments # (windows?) zip up the coverage main to be used as the coverage # command. - with open(TRY_EXECFILE) as f: + with open(TRY_EXECFILE, encoding="utf-8") as f: self.make_file("run_me.py", f.read()) expected = self.run_command("python run_me.py") cov_main = os.path.join(TESTS_DIR, "covmain.zip") @@ -814,7 +814,7 @@ def test_coverage_zip_is_like_python(self) -> None: reason="Windows gets this wrong: https://github.com/python/cpython/issues/131484", ) def test_pythonsafepath(self) -> None: - with open(TRY_EXECFILE) as f: + with open(TRY_EXECFILE, encoding="utf-8") as f: self.make_file("run_me.py", f.read()) self.set_environ("PYTHONSAFEPATH", "1") expected = self.run_command("python run_me.py") @@ -823,7 +823,7 @@ def test_pythonsafepath(self) -> None: @pytest.mark.skipif(env.PYVERSION < (3, 11), reason="PYTHONSAFEPATH is new in 3.11") def test_pythonsafepath_dashm_runme(self) -> None: - with open(TRY_EXECFILE) as f: + with open(TRY_EXECFILE, encoding="utf-8") as f: self.make_file("run_me.py", f.read()) self.set_environ("PYTHONSAFEPATH", "1") expected = self.run_command("python run_me.py") @@ -832,7 +832,7 @@ def test_pythonsafepath_dashm_runme(self) -> None: @pytest.mark.skipif(env.PYVERSION < (3, 11), reason="PYTHONSAFEPATH is new in 3.11") def test_pythonsafepath_dashm(self) -> None: - with open(TRY_EXECFILE) as f: + with open(TRY_EXECFILE, encoding="utf-8") as f: self.make_file("with_main/__main__.py", f.read()) self.set_environ("PYTHONSAFEPATH", "1") @@ -1256,7 +1256,7 @@ def setUp(self) -> None: """) # sub.py will write a few lines. self.make_file("sub.py", """\ - f = open("out.txt", "w") + f = open("out.txt", "w", encoding="utf-8") f.write("Hello, world!\\n") f.close() """) @@ -1276,7 +1276,7 @@ def test_subprocess_with_pth_files(self) -> None: self.set_environ("COVERAGE_PROCESS_START", "coverage.ini") import main # pylint: disable=unused-import, import-error - with open("out.txt") as f: + with open("out.txt", encoding="utf-8") as f: assert f.read() == "Hello, world!\n" # Read the data from .coverage @@ -1295,7 +1295,7 @@ def test_subprocess_with_pth_files_and_parallel(self) -> None: self.set_environ("COVERAGE_PROCESS_START", "coverage.ini") self.run_command("coverage run main.py") - with open("out.txt") as f: + with open("out.txt", encoding="utf-8") as f: assert f.read() == "Hello, world!\n" self.run_command("coverage combine") @@ -1368,7 +1368,7 @@ def path(basename: str) -> str: self.make_file(path("__init__.py"), "") # sub.py will write a few lines. self.make_file(path("sub.py"), """\ - f = open("out.txt", "w") + f = open("out.txt", "w", encoding="utf-8") f.write("Hello, world!") f.close() """) @@ -1386,7 +1386,7 @@ def path(basename: str) -> str: self.run_command(cmd) - with open("out.txt") as f: + with open("out.txt", encoding="utf-8") as f: assert f.read() == "Hello, world!" # Read the data from .coverage diff --git a/tests/test_python.py b/tests/test_python.py index 6a8362919..7c8666ac5 100644 --- a/tests/test_python.py +++ b/tests/test_python.py @@ -57,11 +57,11 @@ def test_source_for_file_windows(tmp_path: pathlib.Path) -> None: # On windows if a pyw exists, it is an acceptable source path_windows = tmp_path / "a.pyw" - path_windows.write_text("") + path_windows.write_text("", encoding="utf-8") assert str(path_windows) == source_for_file(src + 'c') # If both pyw and py exist, py is preferred - a_py.write_text("") + a_py.write_text("", encoding="utf-8") assert source_for_file(src + 'c') == src @@ -81,6 +81,6 @@ def test_runpy_path(self, convert_to: str) -> None: import runpy from pathlib import Path pyfile = Path('script.py') - pyfile.write_text('') + pyfile.write_text('', encoding='utf-8') runpy.run_path({convert_to}(pyfile)) """) diff --git a/tests/test_report.py b/tests/test_report.py index fca027f9b..06d095e81 100644 --- a/tests/test_report.py +++ b/tests/test_report.py @@ -787,7 +787,7 @@ def test_report_with_chdir(self) -> None: print("Line One") os.chdir("subdir") print("Line Two") - print(open("something").read()) + print(open("something", encoding="utf-8").read()) """) self.make_file("subdir/something", "hello") out = self.run_command("coverage run --source=. chdir.py") diff --git a/tests/test_report_common.py b/tests/test_report_common.py index 20c54e323..4583fe17d 100644 --- a/tests/test_report_common.py +++ b/tests/test_report_common.py @@ -265,7 +265,7 @@ def test_lcov(self) -> None: cov = coverage.Coverage() cov.load() cov.lcov_report() - with open("coverage.lcov") as lcov: + with open("coverage.lcov", encoding="utf-8") as lcov: actual = lcov.read() expected = textwrap.dedent("""\ SF:good.j2 diff --git a/tests/test_venv.py b/tests/test_venv.py index 70b2e4c4d..bd30b0e1b 100644 --- a/tests/test_venv.py +++ b/tests/test_venv.py @@ -62,7 +62,7 @@ def third(x): return 3 * x """) # Use plugin2.py as third.plugin - with open(os.path.join(os.path.dirname(__file__), "plugin2.py")) as f: + with open(os.path.join(os.path.dirname(__file__), "plugin2.py"), encoding="utf-8") as f: make_file("third_pkg/third/plugin.py", f.read()) # A render function for plugin2 to use for dynamic file names. make_file("third_pkg/third/render.py", """\ @@ -194,7 +194,7 @@ def in_venv_world_fixture(self, venv_world: Path) -> Iterator[None]: def get_trace_output(self) -> str: """Get the debug output of coverage.py""" - with open("debug_out.txt") as f: + with open("debug_out.txt", encoding="utf-8") as f: return f.read() @pytest.mark.parametrize('install_source_in_venv', [True, False]) diff --git a/tox.ini b/tox.ini index ef96551cc..dbc0f1f97 100644 --- a/tox.ini +++ b/tox.ini @@ -28,6 +28,14 @@ setenv = pypy3{,9,10,11}: COVERAGE_TEST_CORES=pytrace # If we ever need a stronger way to suppress warnings: #PYTHONWARNINGS=ignore:removed in Python 3.14; use ast.Constant:DeprecationWarning + # We want to know about missing encoding arguments, but we need to silence + # some warnings that aren't ours. We can't silence them in 3.9 because + # EncodingWarning doesn't exist yet, and it's hard to suppress them in some + # environments and not others. So by default, don't warn, and will enable + # the warning in a handful of environments to catch the problems. + PYTHONWARNDEFAULTENCODING= + py3{10,11,12,13,14}: PYTHONWARNDEFAULTENCODING=1 + py3{10,11,12,13,14}: PYTHONWARNINGS=ignore::EncodingWarning:pip._internal.utils.subprocess # Disable CPython's color output PYTHON_COLORS=0 From 6999a4ebf397bcbca8e4f345882a7b5ff3654365 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 16 May 2025 06:44:38 -0400 Subject: [PATCH 50/68] test: suppress one more EncodingWarning #1966 Without this, I got: ``` tests/test_oddball.py::DoctestTest::test_doctest /usr/local/pyenv/pyenv/versions/3.10.17/lib/python3.10/pdb.py:160: EncodingWarning: 'encoding' argument not specified with open(os.path.expanduser('~/.pdbrc')) as rcFile: ``` --- CHANGES.rst | 6 +++++- CONTRIBUTORS.txt | 1 + tests/test_oddball.py | 7 ++++++- tox.ini | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9288f854a..6a2b5a507 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -23,8 +23,12 @@ upgrading your version of coverage.py. Unreleased ---------- -Nothing yet. +- A number of EncodingWarnings were fixed that could appear if you've enabled + PYTHONWARNDEFAULTENCODING, fixing `issue 1966`_. Thanks, `Henry Schreiner + `_. +.. _issue 1966: https://github.com/nedbat/coveragepy/issues/1966 +.. _pull 1967: https://github.com/nedbat/coveragepy/pull/1967 .. start-releases diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 37fd37662..08c52c0e9 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -95,6 +95,7 @@ Greg Rogers Guido van Rossum Guillaume Chazarain Guillaume Pujol +Henry Schreiner Holger Krekel Hugo van Kemenade Ian Moore diff --git a/tests/test_oddball.py b/tests/test_oddball.py index b253b8927..6882b6668 100644 --- a/tests/test_oddball.py +++ b/tests/test_oddball.py @@ -8,6 +8,7 @@ import os.path import re import sys +import warnings from flaky import flaky import pytest @@ -465,7 +466,11 @@ def return_arg_or_void(arg): doctest.testmod(sys.modules[__name__]) # we're not __main__ :( ''') cov = coverage.Coverage() - self.start_import_stop(cov, "the_doctest") + with warnings.catch_warnings(): + # Doctest calls pdb which opens ~/.pdbrc without an encoding argument, + # but we don't care. PYVERSIONS: this was needed for 3.10 only. + warnings.filterwarnings("ignore", r".*'encoding' argument not specified.*") + self.start_import_stop(cov, "the_doctest") data = cov.get_data() assert len(data.measured_files()) == 1 lines = sorted_lines(data, data.measured_files().pop()) diff --git a/tox.ini b/tox.ini index dbc0f1f97..eb6a0a20e 100644 --- a/tox.ini +++ b/tox.ini @@ -31,7 +31,7 @@ setenv = # We want to know about missing encoding arguments, but we need to silence # some warnings that aren't ours. We can't silence them in 3.9 because # EncodingWarning doesn't exist yet, and it's hard to suppress them in some - # environments and not others. So by default, don't warn, and will enable + # environments and not others. So by default, don't warn, and we'll enable # the warning in a handful of environments to catch the problems. PYTHONWARNDEFAULTENCODING= py3{10,11,12,13,14}: PYTHONWARNDEFAULTENCODING=1 From 9bdf3665853eb19211e5ca6cb844a426455cdf24 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 18 May 2025 05:24:26 -0400 Subject: [PATCH 51/68] chore: make upgrade doc_upgrade --- doc/requirements.pip | 6 +++--- requirements/dev.pip | 6 +++--- requirements/kit.pip | 2 +- requirements/light-threads.pip | 2 +- requirements/mypy.pip | 6 +++--- requirements/pip.pip | 2 +- requirements/pytest.pip | 4 ++-- requirements/tox.pip | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/requirements.pip b/doc/requirements.pip index b6cd132b9..f759c91ee 100644 --- a/doc/requirements.pip +++ b/doc/requirements.pip @@ -12,7 +12,7 @@ certifi==2025.4.26 # via requests charset-normalizer==3.4.2 # via requests -click==8.1.8 +click==8.2.0 # via uvicorn cogapp==3.4.1 # via -r doc/requirements.in @@ -62,11 +62,11 @@ restructuredtext-lint==1.4.0 # via doc8 roman-numerals-py==3.1.0 # via sphinx -setuptools==80.3.0 +setuptools==80.7.1 # via pbr sniffio==1.3.1 # via anyio -snowballstemmer==2.2.0 +snowballstemmer==3.0.1 # via sphinx sphinx==8.2.3 # via diff --git a/requirements/dev.pip b/requirements/dev.pip index b22756117..ca6a44709 100644 --- a/requirements/dev.pip +++ b/requirements/dev.pip @@ -53,7 +53,7 @@ flaky==3.8.1 # via -r requirements/pytest.in greenlet==3.2.2 # via -r requirements/dev.in -hypothesis==6.131.16 +hypothesis==6.131.18 # via -r requirements/pytest.in id==1.5.0 # via twine @@ -115,7 +115,7 @@ platformdirs==4.3.8 # pylint # tox # virtualenv -pluggy==1.5.0 +pluggy==1.6.0 # via # pytest # tox @@ -158,7 +158,7 @@ rich==14.0.0 # via twine scriv==1.7.0 # via -r requirements/dev.in -setuptools==80.4.0 +setuptools==80.7.1 # via # -r requirements/pip.in # check-manifest diff --git a/requirements/kit.pip b/requirements/kit.pip index 75404420c..c1c039f87 100644 --- a/requirements/kit.pip +++ b/requirements/kit.pip @@ -83,7 +83,7 @@ rfc3986==2.0.0 # via twine rich==14.0.0 # via twine -setuptools==80.4.0 +setuptools==80.7.1 # via -r requirements/kit.in tomli==2.2.1 # via diff --git a/requirements/light-threads.pip b/requirements/light-threads.pip index c0bbab38f..c0993f6ca 100644 --- a/requirements/light-threads.pip +++ b/requirements/light-threads.pip @@ -15,7 +15,7 @@ greenlet==3.2.2 # gevent pycparser==2.22 # via cffi -setuptools==80.4.0 +setuptools==80.7.1 # via # zope-event # zope-interface diff --git a/requirements/mypy.pip b/requirements/mypy.pip index 59577725e..00251be56 100644 --- a/requirements/mypy.pip +++ b/requirements/mypy.pip @@ -12,7 +12,7 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.131.16 +hypothesis==6.131.18 # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest @@ -22,7 +22,7 @@ mypy-extensions==1.1.0 # via mypy packaging==25.0 # via pytest -pluggy==1.5.0 +pluggy==1.6.0 # via pytest pygments==2.19.1 # via -r requirements/pytest.in @@ -38,7 +38,7 @@ tomli==2.2.1 # via # mypy # pytest -types-requests==2.32.0.20250328 +types-requests==2.32.0.20250515 # via -r requirements/mypy.in types-tabulate==0.9.0.20241207 # via -r requirements/mypy.in diff --git a/requirements/pip.pip b/requirements/pip.pip index a9b7e28eb..2cc5b4f92 100644 --- a/requirements/pip.pip +++ b/requirements/pip.pip @@ -8,7 +8,7 @@ pip==25.1.1 # via -r requirements/pip.in platformdirs==4.3.8 # via virtualenv -setuptools==80.4.0 +setuptools==80.7.1 # via -r requirements/pip.in virtualenv==20.31.2 # via -r requirements/pip.in diff --git a/requirements/pytest.pip b/requirements/pytest.pip index 776d89dcb..6b5b1ae51 100644 --- a/requirements/pytest.pip +++ b/requirements/pytest.pip @@ -12,13 +12,13 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.131.16 +hypothesis==6.131.18 # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest packaging==25.0 # via pytest -pluggy==1.5.0 +pluggy==1.6.0 # via pytest pygments==2.19.1 # via -r requirements/pytest.in diff --git a/requirements/tox.pip b/requirements/tox.pip index cfad1d495..3017d6bb3 100644 --- a/requirements/tox.pip +++ b/requirements/tox.pip @@ -22,7 +22,7 @@ platformdirs==4.3.8 # via # tox # virtualenv -pluggy==1.5.0 +pluggy==1.6.0 # via tox pyproject-api==1.9.1 # via tox From 6376e358f05d3749dfcb12907904c2761f331195 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 18 May 2025 05:47:26 -0400 Subject: [PATCH 52/68] build: clarify a .ignore rule We weren't finding strings in tests/gold/html/Makefile --- .ignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ignore b/.ignore index f37cd43a5..e4a6d1f79 100644 --- a/.ignore +++ b/.ignore @@ -1,7 +1,7 @@ # .ignore for coverage: controls what files get searched. build/ htmlcov/ -html0 +html0/ .tox* .coverage* .metacov @@ -10,7 +10,7 @@ coverage.xml coverage.lcov *.min.js style.css -gold/ +tests/gold/*/*/*.* sample_html/ sample_html_beta/ *.so From 6dec28bb8fa0a1875b9c5646f4ca5145cbdfc2fb Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 18 May 2025 05:49:03 -0400 Subject: [PATCH 53/68] build: delete unused code in igor.py --- igor.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/igor.py b/igor.py index 9f7b23e12..2ca821eb7 100644 --- a/igor.py +++ b/igor.py @@ -8,7 +8,6 @@ """ -import contextlib import datetime import glob import inspect @@ -23,7 +22,6 @@ import sysconfig import textwrap import types -import warnings import zipfile try: @@ -40,14 +38,6 @@ PYPY = platform.python_implementation() == "PyPy" -@contextlib.contextmanager -def ignore_warnings(): - """Context manager to ignore warning within the with statement.""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - yield - - # $set_env.py: COVERAGE_IGOR_VERBOSE - More output from igor.py VERBOSITY = int(os.getenv("COVERAGE_IGOR_VERBOSE", "0")) From 9975d0c8df66ddbf61bf783c11691b59bd377dfe Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 18 May 2025 05:51:36 -0400 Subject: [PATCH 54/68] build: no need for a separate doc_upgrade target --- Makefile | 7 ++----- doc/requirements.in | 2 +- doc/requirements.pip | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index bd0019d3d..426e43b46 100644 --- a/Makefile +++ b/Makefile @@ -101,7 +101,7 @@ metasmoke: # in requirements/pins.pip, and search for "windows" in .in files to find pins # and extra requirements that have been needed, but might be obsolete. -.PHONY: upgrade upgrade_one _upgrade doc_upgrade diff_upgrade +.PHONY: upgrade upgrade_one _upgrade diff_upgrade DOCBIN = .tox/doc/bin @@ -123,10 +123,7 @@ _upgrade: $(PIP_COMPILE) -o requirements/dev.pip requirements/dev.in $(PIP_COMPILE) -o requirements/light-threads.pip requirements/light-threads.in $(PIP_COMPILE) -o requirements/mypy.pip requirements/mypy.in - -doc_upgrade: export UV_CUSTOM_COMPILE_COMMAND=make doc_upgrade -doc_upgrade: $(DOCBIN) ## Update the doc/requirements.pip file - $(PIP_COMPILE) -p $(DOCBIN)/python3 --upgrade -o doc/requirements.pip doc/requirements.in + $(PIP_COMPILE) -p $(DOCBIN)/python3 -o doc/requirements.pip doc/requirements.in diff_upgrade: ## Summarize the last `make upgrade` @# The sort flags sort by the package name first, then by the -/+, and diff --git a/doc/requirements.in b/doc/requirements.in index 4486c06ac..1c0504fe2 100644 --- a/doc/requirements.in +++ b/doc/requirements.in @@ -2,7 +2,7 @@ # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt # PyPI requirements input for building documentation for coverage.py -# "make doc_upgrade" turns this into doc/requirements.pip +# "make upgrade" turns this into doc/requirements.pip -c ../requirements/pins.pip diff --git a/doc/requirements.pip b/doc/requirements.pip index f759c91ee..71f52d5ef 100644 --- a/doc/requirements.pip +++ b/doc/requirements.pip @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# make doc_upgrade +# make upgrade alabaster==1.0.0 # via sphinx anyio==4.9.0 From 66e4f8d9ad61e3100af263011a89cbbe476970eb Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 18 May 2025 06:25:23 -0400 Subject: [PATCH 55/68] test: try to unflake a test On 3.14t, this test fails occasionally: ``` > assert any(d < 50 * 1024 for d in deltas) E assert False E + where False = any(. at 0x42d3c112300>) /home/runner/work/coveragepy/coveragepy/tests/test_oddball.py:242: AssertionError ----------------------------- Captured stdout call ----------------------------- Mem delta: 6912 Mem delta: 6912 Mem delta: 7808 Mem delta: 8916 Mem delta: 7936 Mem delta: 7808 Mem delta: 7936 Mem delta: 7808 Mem delta: 7936 Mem delta: 1728 ``` --- tests/test_oddball.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_oddball.py b/tests/test_oddball.py index 6882b6668..eabaa9758 100644 --- a/tests/test_oddball.py +++ b/tests/test_oddball.py @@ -233,7 +233,7 @@ def test_eval_codeobject_leak(self, branch: bool) -> None: # one of our loops only increased the footprint by a small amount. base = osinfo.process_ram() deltas = [] - for _ in range(10): + for _ in range(30): self.check_coverage(code, lines=[1, 2, 3], missing="", branch=branch) now = osinfo.process_ram() deltas.append(now - base) From 8b9cecc43a8f2491b3d63f549cc3809a9f2b2b24 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 21 May 2025 06:48:01 -0400 Subject: [PATCH 56/68] fix: close a sys.monitoring race condition with free-threading. #1970 --- CHANGES.rst | 4 +++ coverage/sysmon.py | 67 +++++++++++++++++++++++----------------------- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 6a2b5a507..664282622 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -27,8 +27,12 @@ Unreleased PYTHONWARNDEFAULTENCODING, fixing `issue 1966`_. Thanks, `Henry Schreiner `_. +- Fixed a race condition when using sys.monitoring with free-threading Python, + closing `issue 1970`_. + .. _issue 1966: https://github.com/nedbat/coveragepy/issues/1966 .. _pull 1967: https://github.com/nedbat/coveragepy/pull/1967 +.. _issue 1970: https://github.com/nedbat/coveragepy/issues/1970 .. start-releases diff --git a/coverage/sysmon.py b/coverage/sysmon.py index b6b0d9060..2c4602765 100644 --- a/coverage/sysmon.py +++ b/coverage/sysmon.py @@ -233,7 +233,6 @@ def __init__(self, tool_id: int) -> None: 0, ) - self.stopped = False self._activity = False def __repr__(self) -> str: @@ -244,43 +243,43 @@ def __repr__(self) -> str: @panopticon() def start(self) -> None: """Start this Tracer.""" - self.stopped = False - - assert sys_monitoring is not None - sys_monitoring.use_tool_id(self.myid, "coverage.py") - register = functools.partial(sys_monitoring.register_callback, self.myid) - events = sys.monitoring.events - - sys_monitoring.set_events(self.myid, events.PY_START) - register(events.PY_START, self.sysmon_py_start) - if self.trace_arcs: - register(events.PY_RETURN, self.sysmon_py_return) - register(events.LINE, self.sysmon_line_arcs) - if env.PYBEHAVIOR.branch_right_left: - register( - events.BRANCH_RIGHT, # type:ignore[attr-defined] - self.sysmon_branch_either, - ) - register( - events.BRANCH_LEFT, # type:ignore[attr-defined] - self.sysmon_branch_either, - ) - else: - register(events.LINE, self.sysmon_line_lines) - sys_monitoring.restart_events() - self.sysmon_on = True + with self.lock: + assert sys_monitoring is not None + sys_monitoring.use_tool_id(self.myid, "coverage.py") + register = functools.partial(sys_monitoring.register_callback, self.myid) + events = sys.monitoring.events + + sys_monitoring.set_events(self.myid, events.PY_START) + register(events.PY_START, self.sysmon_py_start) + if self.trace_arcs: + register(events.PY_RETURN, self.sysmon_py_return) + register(events.LINE, self.sysmon_line_arcs) + if env.PYBEHAVIOR.branch_right_left: + register( + events.BRANCH_RIGHT, # type:ignore[attr-defined] + self.sysmon_branch_either, + ) + register( + events.BRANCH_LEFT, # type:ignore[attr-defined] + self.sysmon_branch_either, + ) + else: + register(events.LINE, self.sysmon_line_lines) + sys_monitoring.restart_events() + self.sysmon_on = True @panopticon() def stop(self) -> None: """Stop this Tracer.""" - if not self.sysmon_on: - # In forking situations, we might try to stop when we are not - # started. Do nothing in that case. - return - assert sys_monitoring is not None - sys_monitoring.set_events(self.myid, 0) - self.sysmon_on = False - sys_monitoring.free_tool_id(self.myid) + with self.lock: + if not self.sysmon_on: + # In forking situations, we might try to stop when we are not + # started. Do nothing in that case. + return + assert sys_monitoring is not None + sys_monitoring.set_events(self.myid, 0) + self.sysmon_on = False + sys_monitoring.free_tool_id(self.myid) @panopticon() def post_fork(self) -> None: From 277441030e56ff14dc1f6612cd3a5771353ef28a Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 21 May 2025 06:53:33 -0400 Subject: [PATCH 57/68] test: simplify skipper, and make it suppressable --- igor.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/igor.py b/igor.py index 2ca821eb7..8bc0c7086 100644 --- a/igor.py +++ b/igor.py @@ -38,8 +38,8 @@ PYPY = platform.python_implementation() == "PyPy" -# $set_env.py: COVERAGE_IGOR_VERBOSE - More output from igor.py -VERBOSITY = int(os.getenv("COVERAGE_IGOR_VERBOSE", "0")) +# $set_env.py: COVERAGE_IGOR_VERBOSE - How much chatter from igor.py (default 1) +VERBOSITY = int(os.getenv("COVERAGE_IGOR_VERBOSE", "1")) # Functions named do_* are executable from the command line: do_blah is run # by "python igor.py blah". @@ -87,16 +87,16 @@ def do_remove_extension(*args): for root, pattern in itertools.product(roots, so_patterns): pattern = os.path.join(root, pattern) - if VERBOSITY: + if VERBOSITY > 1: print(f"Searching for {pattern} from {os.getcwd()}") for filename in glob.glob(pattern): if os.path.exists(filename): - if VERBOSITY: + if VERBOSITY > 1: print(f"Removing {os.path.abspath(filename)}") try: os.remove(filename) except OSError as exc: - if VERBOSITY: + if VERBOSITY > 1: print(f"Couldn't remove {os.path.abspath(filename)}: {exc}") @@ -140,13 +140,9 @@ def should_skip(core): skipper = f"No C core for {platform.python_implementation()}" if skipper: - msg = "Skipping tests " + label_for_core(core) - if len(skipper) > 1: - msg += ": " + skipper + return f"Skipping tests {label_for_core(core)}: {skipper}" else: - msg = "" - - return msg + return "" def make_env_id(core): @@ -253,7 +249,8 @@ def do_test_with_core(core, *runner_args): # If we should skip these tests, skip them. skip_msg = should_skip(core) if skip_msg: - print(skip_msg) + if VERBOSITY > 0: + print(skip_msg) return None os.environ["COVERAGE_CORE"] = core From ecbd4daf401664bbabcbb1e954855b0dbe92878e Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 21 May 2025 07:47:35 -0400 Subject: [PATCH 58/68] build: make a step more explicit --- howto.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/howto.txt b/howto.txt index 70a209339..5b3955e19 100644 --- a/howto.txt +++ b/howto.txt @@ -1,6 +1,8 @@ * Release checklist - Check that the current virtualenv matches the current coverage branch. + $ python -V # should match lowest PYVERSIONS + $ make install - start branch for release work $ make relbranch - check version number in coverage/version.py @@ -33,9 +35,7 @@ - IF PRE-RELEASE: $ make sample_html_beta - IF NOT PRE-RELEASE: - $ make sample_html - - check in the new sample html - $ make relcommit2 + $ make sample_html relcommit2 - Build and publish docs: - IF PRE-RELEASE: $ make publishbeta From b98bc9b9878ff8c23bcaa0d7c5b2a55269c6783f Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 21 May 2025 07:53:39 -0400 Subject: [PATCH 59/68] docs: prep for 7.8.1 --- CHANGES.rst | 10 ++++++---- README.rst | 2 +- coverage/version.py | 4 ++-- doc/conf.py | 6 +++--- doc/index.rst | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 664282622..ecc4aefef 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -20,8 +20,12 @@ upgrading your version of coverage.py. .. Version 9.8.1 — 2027-07-27 .. -------------------------- -Unreleased ----------- +.. start-releases + +.. _changes_7-8-1: + +Version 7.8.1 — 2025-05-21 +-------------------------- - A number of EncodingWarnings were fixed that could appear if you've enabled PYTHONWARNDEFAULTENCODING, fixing `issue 1966`_. Thanks, `Henry Schreiner @@ -34,8 +38,6 @@ Unreleased .. _pull 1967: https://github.com/nedbat/coveragepy/pull/1967 .. _issue 1970: https://github.com/nedbat/coveragepy/issues/1970 -.. start-releases - .. _changes_7-8-0: Version 7.8.0 — 2025-03-30 diff --git a/README.rst b/README.rst index f71e01dbe..79e0c5cb6 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ Coverage.py runs on these versions of Python: .. PYVERSIONS -* Python 3.9 through 3.14 alpha 6, including free-threading. +* Python 3.9 through 3.14 beta 1, including free-threading. * PyPy3 versions 3.9, 3.10, and 3.11. Documentation is on `Read the Docs`_. Code repository and issue tracker are on diff --git a/coverage/version.py b/coverage/version.py index 867dcb8dd..de077a9dc 100644 --- a/coverage/version.py +++ b/coverage/version.py @@ -8,8 +8,8 @@ # version_info: same semantics as sys.version_info. # _dev: the .devN suffix if any. -version_info = (7, 8, 1, "alpha", 0) -_dev = 1 +version_info = (7, 8, 1, "final", 0) +_dev = 0 def _make_version( diff --git a/doc/conf.py b/doc/conf.py index 7b7040054..9983aea7b 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -67,11 +67,11 @@ # @@@ editable copyright = "2009–2025, Ned Batchelder" # pylint: disable=redefined-builtin # The short X.Y.Z version. -version = "7.8.0" +version = "7.8.1" # The full version, including alpha/beta/rc tags. -release = "7.8.0" +release = "7.8.1" # The date of release, in "monthname day, year" format. -release_date = "March 30, 2025" +release_date = "May 21, 2025" # @@@ end rst_epilog = f""" diff --git a/doc/index.rst b/doc/index.rst index 6bca07edb..dd4489b7c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -18,7 +18,7 @@ supported on: .. PYVERSIONS -* Python 3.9 through 3.14 alpha 6, including free-threading. +* Python 3.9 through 3.14 beta 1, including free-threading. * PyPy3 versions 3.9, 3.10, and 3.11. .. ifconfig:: prerelease From ed98b8708ccc380bcb1490cd73b3e476f69c234f Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 21 May 2025 07:54:32 -0400 Subject: [PATCH 60/68] docs: sample HTML for 7.8.1 --- doc/sample_html/class_index.html | 8 ++++---- doc/sample_html/function_index.html | 8 ++++---- doc/sample_html/index.html | 8 ++++---- doc/sample_html/status.json | 2 +- doc/sample_html/z_7b071bdc2a35fa80___init___py.html | 8 ++++---- doc/sample_html/z_7b071bdc2a35fa80___main___py.html | 8 ++++---- doc/sample_html/z_7b071bdc2a35fa80_cogapp_py.html | 8 ++++---- doc/sample_html/z_7b071bdc2a35fa80_makefiles_py.html | 8 ++++---- .../z_7b071bdc2a35fa80_test_cogapp_py.html | 12 ++++++------ .../z_7b071bdc2a35fa80_test_makefiles_py.html | 8 ++++---- .../z_7b071bdc2a35fa80_test_whiteutils_py.html | 8 ++++---- doc/sample_html/z_7b071bdc2a35fa80_utils_py.html | 8 ++++---- .../z_7b071bdc2a35fa80_whiteutils_py.html | 8 ++++---- 13 files changed, 51 insertions(+), 51 deletions(-) diff --git a/doc/sample_html/class_index.html b/doc/sample_html/class_index.html index c4f4afb5e..fe53c6d7c 100644 --- a/doc/sample_html/class_index.html +++ b/doc/sample_html/class_index.html @@ -56,8 +56,8 @@

Classes

- coverage.py v7.8.0, - created at 2025-03-30 15:44 -0400 + coverage.py v7.8.1, + created at 2025-05-21 07:54 -0400

@@ -537,8 +537,8 @@

- coverage.py v7.8.0, - created at 2025-03-30 15:44 -0400 + coverage.py v7.8.1, + created at 2025-05-21 07:54 -0400

diff --git a/doc/sample_html/z_7b071bdc2a35fa80_makefiles_py.html b/doc/sample_html/z_7b071bdc2a35fa80_makefiles_py.html index 43a29b1e3..2fbdaaaf5 100644 --- a/doc/sample_html/z_7b071bdc2a35fa80_makefiles_py.html +++ b/doc/sample_html/z_7b071bdc2a35fa80_makefiles_py.html @@ -66,8 +66,8 @@

^ index     » next       - coverage.py v7.8.0, - created at 2025-03-30 15:44 -0400 + coverage.py v7.8.1, + created at 2025-05-21 07:54 -0400