diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index a7b38ea33..c77923d8c 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -213,7 +213,7 @@ jobs: DIFF_AGAINST: HEAD - name: 📤 Store macOS crash reports if: always() && matrix.diagnostics && runner.os == 'macOS' - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: crash-reports-${{ matrix.py }}-${{ matrix.os }} path: ~/Library/Logs/DiagnosticReports/ @@ -221,7 +221,7 @@ jobs: retention-days: 14 - name: 📤 Store pytest diagnostics if: always() && matrix.diagnostics - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: hang-diagnostics-${{ matrix.py }}-${{ matrix.os }} path: | @@ -230,10 +230,10 @@ jobs: .tox/3.13t/log/ .tox/junit.3.13t.xml include-hidden-files: true - if-no-files-found: warn + if-no-files-found: ignore retention-days: 14 - name: 📤 Store coverage data - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: coverage-${{ matrix.py }}-${{ matrix.os }}-${{ strategy.job-index }} # combined .coverage. files and the per-process files of envs that do not combine them @@ -262,7 +262,7 @@ jobs: - name: 📦 Install tox run: uv tool install --python-preference only-managed --python 3.14 "tox>=4.45" --with tox-uv - name: 📥 Download coverage data - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: # one directory per test run: runs on different OSes write the same .coverage. file name pattern: coverage-* @@ -336,6 +336,7 @@ jobs: runs-on: ubuntu-24.04 permissions: contents: read # check out the queries and their test fixtures + security-events: read # codeql-action needs it to reach its API endpoints steps: - name: 📥 Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -343,7 +344,7 @@ jobs: persist-credentials: false - name: 🔎 Install CodeQL id: codeql - uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1 + uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 with: languages: python - name: 🏃 Run query tests diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index 258af68ea..fb8c2f5d9 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -27,13 +27,13 @@ jobs: with: persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1 + uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml # the custom pack holds Python queries only, so the actions analysis must not load it queries: ${{ matrix.language == 'python' && '+./.github/codeql/queries' || '' }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1 + uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 with: category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/pre-release.yaml b/.github/workflows/pre-release.yaml index 6089c9b45..e680d148b 100644 --- a/.github/workflows/pre-release.yaml +++ b/.github/workflows/pre-release.yaml @@ -57,7 +57,7 @@ jobs: "$RUNNER_TEMP/release/bin/python" tasks/release.py --version "$BUMP" --no-push echo "version=$(git describe --tags --exact-match)" >> "$GITHUB_OUTPUT" - name: Store the changelog - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: release-changelog path: docs/changelog.rst @@ -78,7 +78,7 @@ jobs: allowed-endpoints: >- api.github.com:443 - name: Download the changelog - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: release-changelog path: ${{ runner.temp }}/changelog diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index fbb8f3cc7..f873dd302 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -77,19 +77,19 @@ jobs: subject-path: virtualenv.pyz sbom-path: virtualenv.pyz.cdx.json - name: Store the distribution packages - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ env.dists-artifact-name }} path: dist/* - name: Store the SBOM - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: virtualenv-sbom path: | virtualenv.cdx.json virtualenv.spdx.json - name: Store the zipapp - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: virtualenv-zipapp path: | @@ -119,7 +119,7 @@ jobs: fetch-depth: 0 persist-credentials: false - name: Download all the dists - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ${{ env.dists-artifact-name }} path: dist/ @@ -128,11 +128,11 @@ jobs: with: attestations: true - name: Download the zipapp - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: virtualenv-zipapp - name: Download the SBOMs - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: virtualenv-sbom # immutable releases reject assets added after creation, so every asset goes into this one call @@ -201,12 +201,12 @@ jobs: uv venv --python 3.14 --python-preference only-managed "$RUNNER_TEMP/tools" uv pip sync --python "$RUNNER_TEMP/tools" --require-hashes tasks/release-requirements.txt - name: Download expected distributions - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ${{ env.dists-artifact-name }} path: expected/ - name: Download expected SBOMs - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: virtualenv-sbom path: expected/ diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index 36197aca5..ab40a6206 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -14,7 +14,7 @@ jobs: analysis: name: 🔎 scorecard analysis if: github.repository_owner == 'pypa' - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 permissions: actions: read # read workflow runs for the Packaging check checks: read # read check runs for the CI-Tests and SAST checks @@ -33,6 +33,6 @@ jobs: results_file: results.sarif results_format: sarif publish_results: true - - uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1 + - uses: github/codeql-action/upload-sarif@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 with: sarif_file: results.sarif diff --git a/.github/workflows/upgrade.yaml b/.github/workflows/upgrade.yaml index 0fce1ff62..b957d16bd 100644 --- a/.github/workflows/upgrade.yaml +++ b/.github/workflows/upgrade.yaml @@ -24,15 +24,17 @@ jobs: disable-sudo-and-containers: true allowed-endpoints: >- api.github.com:443 files.pythonhosted.org:443 github.com:443 pypi.org:443 raw.githubusercontent.com:443 registry.npmjs.org:443 release-assets.githubusercontent.com:443 releases.astral.sh:443 - - name: Install uv - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0 - - name: Install tox - run: uv tool install --python-preference only-managed --python 3.14 "tox>=4.32" --with tox-uv - name: Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false + - name: Install uv + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0 + with: + enable-cache: false # the egress allowlist above blocks the cache service + - name: Install tox + run: uv tool install --python-preference only-managed --python 3.14 "tox>=4.32" --with tox-uv - name: Fetch upstream tags for versioning run: git fetch --force --tags https://github.com/pypa/virtualenv.git - name: Pin the newest CI test tools @@ -75,7 +77,7 @@ jobs: run: git diff --cached --binary --no-ext-diff --no-textconv > "$RUNNER_TEMP/upgrade.patch" - name: Store upgrade patch if: steps.upgrade.outputs.changed == 'true' && steps.age-check.outputs.skip == 'false' - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: upgrade-patch path: ${{ runner.temp }}/upgrade.patch @@ -106,7 +108,7 @@ jobs: fetch-depth: 0 persist-credentials: true # zizmor: ignore[artipacked] create-pull-request and the changelog rename push with it - name: Download upgrade patch - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: upgrade-patch path: ${{ runner.temp }}/upgrade-patch @@ -114,7 +116,7 @@ jobs: run: python tasks/apply_upgrade_patch.py "$RUNNER_TEMP/upgrade-patch/upgrade.patch" - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8 + uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: commit-message: "Upgrade embedded dependencies" # the checkout pins github.sha, a detached HEAD, so the action cannot infer the base branch diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6ba557954..9d669a003 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,35 +1,35 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 + rev: v6.0.0 hooks: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 4f85d46a92dc17713078e5de73f8a13190edf6fc # frozen: 0.38.2 + rev: "0.38.2" hooks: - id: check-github-workflows args: ["--verbose"] - repo: https://github.com/codespell-project/codespell - rev: 57b21406f092110c18776e39b0bda50d37c945c8 # frozen: v2.4.3 + rev: v2.4.3 hooks: - id: codespell args: ["--write-changes"] - repo: https://github.com/tox-dev/tox-toml-fmt - rev: "2863761a0c1b4a6e9fea638b27657b81f400543b" # frozen: v1.10.3 + rev: "v1.10.3" hooks: - id: tox-toml-fmt - repo: https://github.com/tox-dev/pyproject-fmt - rev: "59e7b26529dc6f15c43063064b8b06ded60d0910" # frozen: v2.29.4 + rev: "v2.29.4" hooks: - id: pyproject-fmt - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "a56c0b927e6465d37cae3e97d35d4d18ab2b96cd" # frozen: v0.16.9 + rev: "v0.16.10" hooks: - id: ruff-format - id: ruff args: ["--fix", "--unsafe-fixes", "--exit-non-zero-on-fix"] - repo: https://github.com/google/yamlfmt - rev: "b5ca1890231d5e1e5181fef75a1be609d1e25029" # frozen: v0.21.0 + rev: "v0.21.0" hooks: - id: yamlfmt - repo: local @@ -51,17 +51,17 @@ repos: files: '^docs/changelog/' exclude: '^docs/changelog/(examples\.rst|template\.jinja2|\d+\.(feature|bugfix|doc|deprecation|removal)\.rst)$' - repo: https://github.com/LilSpazJoekp/docstrfmt - rev: c1813841673cdcd6cf602dde1edb78a5d5e45235 # frozen: v2.2.1 + rev: v2.2.1 hooks: - id: docstrfmt args: ["-l", "120"] additional_dependencies: ["sphinx>=9.1"] - repo: https://github.com/zizmorcore/zizmor-pre-commit - rev: fa412071e4f5d44d44f9e365f4676f9df92456a2 # frozen: v1.30.1 + rev: v1.30.1 hooks: - id: zizmor - repo: https://github.com/crate-ci/typos - rev: 4141ff14cb566df76c473c786332c186a4fd71d1 # frozen: v1.50.3 + rev: typos-dict-v0.14.2 hooks: - id: typos - repo: meta diff --git a/pyproject.toml b/pyproject.toml index e8f54393a..57d32874d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,8 @@ [build-system] build-backend = "hatchling.build" requires = [ - "hatch-vcs>=0.4", - "hatchling>=1.27,<1.28; python_version<'3.10'", + "hatch-vcs>=0.5", + "hatchling<1.28,>=1.27; python_version<'3.10'", "hatchling>=1.28; python_version>='3.10'", ] @@ -50,13 +50,13 @@ dynamic = [ "version", ] dependencies = [ - "distlib>=0.3.7,<1", - "filelock>=3.16.1,<=3.19.1; python_version<'3.10'", + "distlib<1,>=0.4.3", + "filelock<=3.19.1,>=3.19.1; python_version<'3.10'", "filelock>=3.24.2,<5; python_version>='3.10'", - "packaging>=23.1", - "platformdirs>=3.9.1,<5", - "python-discovery>=1.6", - "typing-extensions>=4.13.2; python_version<'3.11'", + "packaging>=26.3", + "platformdirs<5,>=4.4", + "python-discovery>=1.6.1", + "typing-extensions>=4.16; python_version<'3.11'", ] urls."thanks.dev" = "https://thanks.dev/u/gh/gaborbernat" urls.Changelog = "https://virtualenv.pypa.io/en/latest/changelog.html" @@ -113,49 +113,49 @@ dev = [ test = [ "coverage-enable-subprocess>=1", { include-group = "coverage" }, - "packaging>=23.1", - "pytest>=7.4", - "pytest-env>=0.8.2", + "packaging>=26.3", + "pytest>=8.4.2", + "pytest-env>=1.1.5", """\ pytest-freezer>=0.4.8; platform_python_implementation=='PyPy' or platform_python_implementation=='GraalVM' or \ platform_python_implementation=='RustPython' or (platform_python_implementation=='CPython' and sys_platform=='win32' \ and python_version>='3.13')\ """, - "pytest-mock>=3.11.1", - "pytest-randomly>=3.12", - "pytest-rerunfailures>=15", - "pytest-timeout>=2.1", - "pytest-xdist>=3.5", - "setuptools>=68", - "time-machine>=2.10; platform_python_implementation=='CPython'", - "tomli>=2.0.1; python_version<'3.11'", + "pytest-mock>=3.15.1", + "pytest-randomly>=4.0.1", + "pytest-rerunfailures>=16.0.1", + "pytest-timeout>=2.4", + "pytest-xdist>=3.8", + "setuptools>=82.0.1", + "time-machine>=2.19; platform_python_implementation=='CPython'", + "tomli>=2.4.1; python_version<'3.11'", ] type = [ - "ty>=0.0.19", + "ty>=0.0.84", { include-group = "test" }, ] docs = [ - "furo>=2023.7.26", + "furo>=2025.12.19", "pre-commit-uv>=4.2", - "proselint>=0.13", - "sphinx>=7.1.2,!=7.3", + "proselint>=0.14", + "sphinx!=7.3,>=7.4.7", "sphinx-argparse>=0.4", "sphinx-autodoc-typehints>=3.6.2", "sphinx-copybutton>=0.5.2", "sphinx-inline-tabs>=2025.12.21.14", - "sphinx-llm>=0.4.1,!=1.1", + "sphinx-llm!=1.1,>=1", "sphinxcontrib-mermaid>=2", "sphinxcontrib-towncrier>=0.2.1a0", - "towncrier>=23.6", + "towncrier>=26.9", ] coverage = [ "covdefaults>=2.3", - "coverage>=7.2.7", + "coverage>=7.10.7", ] fuzz = [ # atheris only ships prebuilt Linux wheels for these CPython versions; everywhere else it needs a # custom clang+libFuzzer build, so skip it there rather than triggering a doomed source build. - "atheris>=2.3; sys_platform=='linux' and python_version>='3.12' and python_version<'3.15'", + "atheris>=3.1; sys_platform=='linux' and python_version>='3.12' and python_version<'3.15'", ] lint = [ "pre-commit-uv>=4.2", @@ -164,24 +164,24 @@ pkg-meta = [ "check-sdist>=1.6", "check-wheel-contents>=0.6.3", "twine>=6.2", - "uv>=0.10.2", + "uv>=0.12.22", { include-group = "sbom" }, ] property = [ - "hypothesis>=6.140", + "hypothesis>=6.141.1", { include-group = "test" }, ] release = [ "docstrfmt>=2.2.1", - "gitpython>=3.1.44", - "hatch-vcs>=0.4", + "gitpython>=3.2", + "hatch-vcs>=0.5", "hatchling>=1.28", - "packaging>=25", + "packaging>=26.3", "pypi-attestations>=0.0.28", - "towncrier>=24.8", + "towncrier>=26.9", "tox>=4.45", - "tox-uv>=1.28", - "uv>=0.12.17", + "tox-uv>=1.28.1", + "uv>=0.12.22", { include-group = "sbom" }, ] sbom = [ @@ -190,11 +190,11 @@ sbom = [ ] tasks = [ "docstrfmt>=2.2.1", - "gitpython>=3.1.44", + "gitpython>=3.2", "hatchling>=1.28", "pre-commit-uv>=4.2", - "ruff>=0.16.8", - "towncrier>=24.8", + "ruff>=0.16.10", + "towncrier>=26.9", { include-group = "test" }, ] diff --git a/src/virtualenv/util/lock.py b/src/virtualenv/util/lock.py index edadca2e7..7ac956864 100644 --- a/src/virtualenv/util/lock.py +++ b/src/virtualenv/util/lock.py @@ -13,7 +13,7 @@ from filelock import FileLock, Timeout if TYPE_CHECKING: - from collections.abc import Iterator + from collections.abc import Generator, Iterator from types import TracebackType LOGGER = logging.getLogger(__name__) @@ -145,7 +145,7 @@ def _release(lock: _CountedFileLock) -> None: lock.release() @contextmanager - def lock_for_key(self, name: str, no_block: bool = False) -> Iterator[None]: # ruff:ignore[boolean-default-value-positional-argument] + def lock_for_key(self, name: str, no_block: bool = False) -> Generator[None, None, None]: # ruff:ignore[boolean-default-value-positional-argument] lock = self._create_lock(name) try: with self._lock_and_yield(lock, no_block): @@ -155,7 +155,7 @@ def lock_for_key(self, name: str, no_block: bool = False) -> Iterator[None]: # lock = None @contextmanager - def _lock_and_yield(self, lock: _CountedFileLock, no_block: bool) -> Iterator[None]: + def _lock_and_yield(self, lock: _CountedFileLock, no_block: bool) -> Generator[None, None, None]: self._lock_file(lock, no_block) try: yield @@ -163,7 +163,7 @@ def _lock_and_yield(self, lock: _CountedFileLock, no_block: bool) -> Iterator[No self._release(lock) @contextmanager - def non_reentrant_lock_for_key(self, name: str) -> Iterator[None]: + def non_reentrant_lock_for_key(self, name: str) -> Generator[None, None, None]: with _CountedFileLock(str(self.path / f"{name}.lock")): yield @@ -178,11 +178,11 @@ def __exit__( raise NotImplementedError @contextmanager - def lock_for_key(self, name: str, no_block: bool = False) -> Iterator[None]: # ruff:ignore[unused-method-argument, boolean-default-value-positional-argument] + def lock_for_key(self, name: str, no_block: bool = False) -> Generator[None, None, None]: # ruff:ignore[unused-method-argument, boolean-default-value-positional-argument] yield @contextmanager - def non_reentrant_lock_for_key(self, name: str) -> Iterator[None]: # ruff:ignore[unused-method-argument] + def non_reentrant_lock_for_key(self, name: str) -> Generator[None, None, None]: # ruff:ignore[unused-method-argument] yield diff --git a/tox.toml b/tox.toml index d8e434bc5..a9de670c4 100644 --- a/tox.toml +++ b/tox.toml @@ -255,7 +255,7 @@ base = [ "type" ] [env.upgrade] description = "upgrade pip/wheels/setuptools to latest" skip_install = true -deps = [ "pre-commit-uv>=4.2", "ruff>=0.12.4" ] +deps = [ "pre-commit-uv>=4.2", "ruff>=0.16.10" ] pass_env = [ "UPGRADE_ADVISORY" ] change_dir = "{tox_root}{/}tasks" commands = [ [ "python", "upgrade_wheels.py" ] ] @@ -265,7 +265,7 @@ uv_seed = true description = "generate a zipapp, embed its SBOM and validate the SBOM" base_python = [ "3.14" ] skip_install = true -deps = [ "hatchling>=1.28", "packaging>=25", "uv>=0.12.17" ] +deps = [ "hatchling>=1.28", "packaging>=26.3", "uv>=0.12.19" ] dependency_groups = [ "sbom" ] pass_env = [ "SOURCE_DATE_EPOCH" ] set_env.PYTHONPATH = "{tox_root}"