[
  {
    "path": ".deepsource.toml",
    "content": "version = 1\n\ntest_patterns = [\"tests/**\"]\n\n[[analyzers]]\nname = \"python\"\nenabled = true\n\n  [analyzers.meta]\n  runtime_version = \"3.x.x\"\n\n[[transformers]]\nname = \"black\"\nenabled = true\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing to `pipx`\n\nThank you for your interest in contributing to pipx! There are many ways to contribute, and we appreciate all of them.\nAs a reminder, all contributors are expected to follow the [PSF Code of Conduct][coc].\n\n## Development Documentation\n\nOur [development documentation](https://pipx.pypa.io/latest/contributing/) contains details on how to get started with\ncontributing to `pipx`, and details of our development processes.\n\n[coc]: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md\n"
  },
  {
    "path": ".github/FUNDING.yaml",
    "content": "tidelift: \"pypi/pipx\"\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n---\n\n**Issue**\n\nDescribe what's the expected behavior and what you're observing.\n\n**Environment**\n\nProvide at least:\n\n- OS:\n- Shell:\n- Python version and path:\n- `pipx --version` output:\n\n**Output of the failing command**\n\nMake sure to run the command with `--verbose`:\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser\nblank_issues_enabled: true # default\ncontact_links:\n  - name: \"💬 pypa/pipx @ Discord\"\n    url: https://discord.gg/pypa\n    about: Chat with the devs\n  - name: 🤷💻🤦 Discourse\n    url: https://discuss.python.org/c/packaging\n    about: |\n      Please ask typical Q&A here: general ideas for Python packaging, questions about structuring projects and so on\n  - name: 📝 PSF Code of Conduct\n    url: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md\n    about: ❤ Be nice to other members of the community. ☮ Behave.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an enhancement for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n---\n\n**What's the problem this feature will solve?**\n\n<!-- What are you trying to do, that you are unable to achieve with pipx as it currently stands? -->\n\n**Describe the solution you'd like**\n\n<!-- Clear and concise description of what you want to happen. -->\n\n<!-- Provide examples of real world use cases that this would enable and how it solves the problem described above. -->\n\n**Alternative Solutions**\n\n<!-- Have you tried to workaround the problem using pipx or other tools? Or a different approach to solving this issue? -->\n\n**Additional context**\n\n<!-- Add any other context, links, etc. about the feature here. -->\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "### Thanks for contributing, make sure you address all the checklists (for details on how see [development documentation](https://pipx.pypa.io/latest/contributing/))\n\n- [ ] ran the linter to address style issues (`pre-commit run --all-files`)\n- [ ] wrote descriptive pull request text\n- [ ] ensured there are test(s) validating the fix\n- [ ] added news fragment in `changelog.d/` folder\n- [ ] updated/extended the documentation\n"
  },
  {
    "path": ".github/SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\n| Version | Supported          |\n| ------- | ------------------ |\n| 1.7.0+  | :white_check_mark: |\n| < 1.7.0 | :x:                |\n\n## Reporting a Vulnerability\n\nTo report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift\nwill coordinate the fix and disclosure.\n"
  },
  {
    "path": ".github/config.yml",
    "content": "rtd:\n  project: pipx\n"
  },
  {
    "path": ".github/dependabot.yaml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n"
  },
  {
    "path": ".github/release.yml",
    "content": "changelog:\n  exclude:\n    authors:\n      - dependabot[bot]\n      - pre-commit-ci[bot]\n      - github-actions[bot]\n"
  },
  {
    "path": ".github/workflows/create_tests_package_lists.yml",
    "content": "name: Create tests package lists for offline tests\non:\n  workflow_dispatch:\nconcurrency:\n  group: create-tests-package-lists-${{ github.ref }}\n  cancel-in-progress: true\njobs:\n  create_package_lists:\n    name: Create package lists\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest]\n        python-version: [\"3.13\", \"3.12\", \"3.11\", \"3.10\", \"3.9\"]\n        include:\n          - os: macos-latest\n            python-version: \"3.13\"\n          - os: windows-latest\n            python-version: \"3.13\"\n    steps:\n      - uses: actions/checkout@v6\n      - name: Set up Python ${{ matrix.python-version }}\n        uses: actions/setup-python@v6\n        with:\n          python-version: ${{ matrix.python-version }}\n          cache: \"pip\"\n      - name: Install nox\n        run: python -m pip install nox\n      - name: Create lists\n        run: nox --non-interactive --session create_test_package_list-${{ matrix.python-version }} -- ./new_tests_packages\n      - name: Store reports as artifacts\n        uses: actions/upload-artifact@v7\n        with:\n          name: lists-${{ matrix.os }}-${{ matrix.python-version }}\n          path: ./new_tests_packages\n"
  },
  {
    "path": ".github/workflows/exhaustive_package_test.yml",
    "content": "name: Exhaustive Package Test (slow)\non:\n  workflow_dispatch:\nconcurrency:\n  group: exhaustive-package-test-${{ github.ref }}\n  cancel-in-progress: true\njobs:\n  test_all_packages:\n    name: Exhaustive Package Test\n    continue-on-error: true\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macos-latest, windows-latest]\n        python-version: [\"3.12\", \"3.11\", \"3.10\", \"3.9\"]\n    steps:\n      - uses: actions/checkout@v6\n      - name: Set up Python ${{ matrix.python-version }}\n        uses: actions/setup-python@v6\n        with:\n          python-version: ${{ matrix.python-version }}\n          cache: \"pip\"\n      - name: Install nox\n        run: python -m pip install nox\n      - name: Execute Tests\n        continue-on-error: true\n        run: nox --non-interactive --session test_all_packages-${{ matrix.python-version }}\n      - name: Store reports as artifacts\n        uses: actions/upload-artifact@v7\n        with:\n          name: reports-raw-${{ matrix.os }}-${{ matrix.python-version }}\n          path: reports\n  report_all_packages:\n    name: Collate test reports\n    needs: test_all_packages\n    runs-on: ubuntu-latest\n    steps:\n      - name: Get report artifacts\n        uses: actions/download-artifact@v8\n        with:\n          pattern: reports-raw-*\n          merge-multiple: true\n      - name: Collate reports\n        run: |\n          ls # DEBUG\n          mkdir reports\n          cat all_packages_report_legend.txt > all_nodeps_reports_lf.txt\n          cat all_packages_nodeps_report_* >> all_nodeps_reports_lf.txt\n          tr -d '\\r' < all_nodeps_reports_lf.txt > reports/all_nodeps_reports.txt\n          cat all_packages_nodeps_errors_* > all_nodeps_errors_lf.txt\n          tr -d '\\r' < all_nodeps_errors_lf.txt > reports/all_nodeps_errors.txt\n          cat all_packages_report_legend.txt > all_deps_reports_lf.txt\n          cat all_packages_deps_report_* >> all_deps_reports_lf.txt\n          tr -d '\\r' < all_deps_reports_lf.txt > reports/all_deps_reports.txt\n          cat all_packages_deps_errors_* > all_deps_errors_lf.txt\n          tr -d '\\r' < all_deps_errors_lf.txt > reports/all_deps_errors.txt\n      - name: Store collated and raw reports as artifacts\n        uses: actions/upload-artifact@v7\n        with:\n          name: reports-final\n          path: reports\n"
  },
  {
    "path": ".github/workflows/pre-release.yml",
    "content": "name: Pre-release\non:\n  workflow_dispatch:\n    inputs:\n      bump:\n        description: \"Version bump type\"\n        required: true\n        type: choice\n        options:\n          - auto\n          - major\n          - minor\n          - patch\n        default: auto\nenv:\n  default-python: \"3.13\"\njobs:\n  pre-release:\n    runs-on: ubuntu-latest\n    environment: release\n    permissions:\n      contents: write\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n          token: ${{ secrets.GH_RELEASE_TOKEN }}\n      - name: Set up Python ${{ env.default-python }}\n        uses: actions/setup-python@v6\n        with:\n          python-version: ${{ env.default-python }}\n      - name: Install dependencies\n        run: |\n          python -m pip install --upgrade pip\n          pip install GitPython packaging towncrier pre-commit\n      - name: Configure git identity\n        run: |\n          git config user.name \"github-actions[bot]\"\n          git config user.email \"41898282+github-actions[bot]@users.noreply.github.com\"\n      - name: Generate changelog, commit, tag, and push\n        run: python scripts/release.py --version \"${{ inputs.bump }}\"\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\non:\n  push:\n    tags:\n      - \"*.*.*\"\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\nenv:\n  default-python: \"3.14\"\njobs:\n  pypi-publish:\n    name: Publish pipx to PyPI\n    runs-on: ubuntu-latest\n    environment:\n      name: release\n      url: https://pypi.org/p/pipx\n    permissions:\n      id-token: write\n    steps:\n      - name: Checkout ${{ github.ref_name }}\n        uses: actions/checkout@v6\n        with:\n          ref: \"${{ github.ref_name }}\"\n      - name: Set up Python ${{ env.default-python }}\n        uses: actions/setup-python@v6\n        with:\n          python-version: ${{ env.default-python }}\n          cache: \"pip\"\n      - name: Build sdist and wheel\n        run: |\n          pip install build\n          python -m build\n      - name: Publish to PyPI\n        uses: pypa/gh-action-pypi-publish@v1.13.0\n  create-release:\n    name: Create a release on GitHub's UI\n    needs: pypi-publish\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n    steps:\n      - uses: actions/checkout@v6\n      - name: Create release\n        uses: softprops/action-gh-release@v2\n        with:\n          generate_release_notes: true\n          tag_name: \"${{ github.ref_name }}\"\n  upload-zipapp:\n    name: Upload zipapp to GitHub Release\n    needs: create-release\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n    steps:\n      - name: Checkout ${{ github.ref_name }}\n        uses: actions/checkout@v6\n        with:\n          ref: \"${{ github.ref_name }}\"\n      - name: Set up Python ${{ env.default-python }}\n        uses: actions/setup-python@v6\n        with:\n          python-version: ${{ env.default-python }}\n          cache: \"pip\"\n      - name: Install tox\n        run: pip install tox\n      - name: Build zipapp\n        run: tox run -e zipapp\n      - name: Upload to release\n        uses: softprops/action-gh-release@v2\n        with:\n          files: pipx.pyz\n          tag_name: \"${{ github.ref_name }}\"\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: 🧪 tests\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - \"main\"\n  pull_request:\n  schedule:\n    - cron: \"0 8 * * *\"\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\njobs:\n  test:\n    name: 🧪 test ${{ matrix.py }} - ${{ matrix.os }}\n    runs-on: ${{ matrix.os }}\n    timeout-minutes: 40\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-24.04]\n        py: [\"3.13\", \"3.12\", \"3.11\", \"3.10\", \"3.9\"]\n        include:\n          - os: windows-2025\n            py: \"3.13\"\n          - os: macos-15\n            py: \"3.13\"\n    steps:\n      - name: 📥 Checkout code\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - name: 🚀 Install uv\n        uses: astral-sh/setup-uv@v7\n      - name: 🐍 Setup Python ${{ matrix.py }}\n        uses: actions/setup-python@v6\n        with:\n          python-version: ${{ matrix.py }}\n      - name: 📦 Install tox\n        run: uv tool install --python-preference only-managed --python 3.13 \"tox>=4.45\" --with tox-uv\n      - name: Persistent .pipx_tests/package_cache\n        uses: actions/cache@v5\n        with:\n          path: ${{ github.workspace }}/.pipx_tests/package_cache/${{ matrix.py }}\n          key: pipx-tests-package-cache-${{ runner.os }}-${{ matrix.py }}\n      - name: 🏗️ Setup test suite\n        run: tox run -vv --notest --skip-missing-interpreters false -e ${{ matrix.py }}\n      - name: 📦 Seed package cache\n        run: tox exec -e ${{ matrix.py }} -- python scripts/update_package_cache.py testdata/tests_packages .pipx_tests/package_cache/\n      - name: 🏃 Run test suite\n        run: tox run --skip-pkg-install -e ${{ matrix.py }}\n        timeout-minutes: 30\n        env:\n          PYTEST_ADDOPTS: \"-vv --durations=20\"\n  man:\n    name: 📖 Build man page\n    runs-on: ubuntu-24.04\n    steps:\n      - name: 📥 Checkout code\n        uses: actions/checkout@v6\n      - name: 🚀 Install uv\n        uses: astral-sh/setup-uv@v7\n      - name: 📦 Install tox\n        run: uv tool install --python-preference only-managed --python 3.13 \"tox>=4.45\" --with tox-uv\n      - name: 📖 Build man page\n        run: tox run -e man\n      - name: Show man page\n        run: man -l pipx.1\n  zipapp:\n    name: 📦 Build zipapp\n    runs-on: ubuntu-24.04\n    steps:\n      - name: 📥 Checkout code\n        uses: actions/checkout@v6\n      - name: 🐍 Setup Python 3.9\n        uses: actions/setup-python@v6\n        with:\n          python-version: \"3.9\"\n      - name: 📦 Build zipapp\n        run: |\n          pip install shiv\n          shiv -c pipx -o ./pipx.pyz .\n          ./pipx.pyz --version\n      - name: Test zipapp by installing black\n        run: python ./pipx.pyz install black\n      - uses: actions/upload-artifact@v7\n        with:\n          name: pipx.pyz\n          path: pipx.pyz\n          retention-days: 3\n"
  },
  {
    "path": ".gitignore",
    "content": "/.*_cache\n/build\n/dist\n/src/pipx/version.py\n/noxfile.py\n/.nox\n*.py[co]\n__pycache__\n/site\n/.coverage*\n/.pipx_tests\n/testdata/tests_packages/*.txt\n/pipx.pyz\n*.egg-info\nbuild\n*.whl\n/pipx.1\n/docs/_draft_changelog.md\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "repos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v6.0.0\n    hooks:\n      - id: end-of-file-fixer\n      - id: check-added-large-files\n      - id: trailing-whitespace\n      - id: check-merge-conflict\n      - id: check-case-conflict\n  - repo: https://github.com/google/yamlfmt\n    rev: v0.21.0\n    hooks:\n      - id: yamlfmt\n  - repo: https://github.com/hukkin/mdformat\n    rev: 0.7.22\n    hooks:\n      - id: mdformat\n        args: [\"--wrap\", \"120\"]\n        additional_dependencies:\n          - mdformat-mkdocs\n          - mdformat-gfm\n          - mdformat-gfm-alerts\n          - mdformat-footnote\n          - mdformat-config\n          - mdformat-front-matters\n        exclude: ^(docs/changelog\\.md|testdata)\n  - repo: https://github.com/tox-dev/pyproject-fmt\n    rev: v2.20.0\n    hooks:\n      - id: pyproject-fmt\n  - repo: https://github.com/astral-sh/ruff-pre-commit\n    rev: v0.15.6\n    hooks:\n      - id: ruff-check\n        args: [\"--fix\", \"--unsafe-fixes\", \"--show-fixes\", \"--exit-non-zero-on-fix\"]\n      - id: ruff-format\n  - repo: https://github.com/pre-commit/mirrors-mypy\n    rev: v1.19.1\n    hooks:\n      - id: mypy\n        args: ['--warn-unused-ignores', '--strict-equality', '--no-implicit-optional', '--check-untyped-defs']\n        exclude: 'testdata/test_package_specifier/local_extras/setup.py'\n        additional_dependencies:\n          - \"mkdocs-gen-files\"\n          - \"nox\"\n          - \"packaging>=20\"\n          - \"platformdirs>=2.1\"\n          - \"tomli; python_version < '3.11'\"\n  # Configuration for codespell is in pyproject.toml\n  - repo: https://github.com/codespell-project/codespell\n    rev: v2.4.2\n    hooks:\n      - id: codespell\n        additional_dependencies:\n          - tomli\n        exclude: ^testdata\n"
  },
  {
    "path": ".pre-commit-hooks.yaml",
    "content": "- id: pipx\n  name: pipx\n  entry: pipx run\n  require_serial: true\n  language: python\n  minimum_pre_commit_version: '2.9.2'\n"
  },
  {
    "path": ".readthedocs.yml",
    "content": "version: 2\nbuild:\n  os: ubuntu-22.04\n  tools:\n    python: \"3.13\"\n  commands:\n    - pip install \"tox>=4.45\" tox-uv\n    - tox run -e docs\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Chad Smith and contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "changelog.d/.gitignore",
    "content": "!.gitignore\n"
  },
  {
    "path": "changelog.d/1443.bugfix.md",
    "content": "Fix `--global` flag being silently ignored when placed before the subcommand\n"
  },
  {
    "path": "changelog.d/1508.bugfix.md",
    "content": "Respect `--quiet` flag in `install` and suppress animations and success messages\n"
  },
  {
    "path": "changelog.d/1602.bugfix.md",
    "content": "Catch missing Python interpreter in `upgrade` and show helpful error instead of crashing\n"
  },
  {
    "path": "changelog.d/1718.bugfix.md",
    "content": "Prevent data loss by rejecting relative paths in `uninstall`, `reinstall`, and other package-name arguments\n"
  },
  {
    "path": "docs/README.md",
    "content": "<p align=\"center\">\n<a href=\"https://pipx.pypa.io\">\n<img align=\"center\" src=\"https://github.com/pypa/pipx/raw/main/logo.svg\" width=\"200\"/>\n</a>\n</p>\n\n# pipx — Install and Run Python Applications in Isolated Environments\n\n<p align=\"center\">\n<a href=\"https://github.com/pypa/pipx/raw/main/pipx_demo.gif\">\n<img src=\"https://github.com/pypa/pipx/raw/main/pipx_demo.gif\"/>\n</a>\n</p>\n\n<p align=\"center\">\n<a href=\"https://github.com/pypa/pipx/actions/workflows/tests.yml\">\n<img src=\"https://github.com/pypa/pipx/actions/workflows/tests.yml/badge.svg?event=push&branch=main\" alt=\"image\" /></a> <a href=\"https://badge.fury.io/py/pipx\"><img src=\"https://badge.fury.io/py/pipx.svg\" alt=\"PyPI version\"></a> <a href=\"https://badge.fury.io/py/pipx\"><img src=\"https://static.pepy.tech/badge/pipx\"></a>\n\n</p>\n\n**Documentation**: <https://pipx.pypa.io>\n\n**Source Code**: <https://github.com/pypa/pipx>\n\n_For comparison to other tools including pipsi, see\n[Comparison to Other Tools](https://pipx.pypa.io/stable/explanation/comparisons/)._\n\n## Overview: What is `pipx`?\n\npipx is a tool to help you install and run end-user applications written in Python. It's roughly similar to macOS's\n`brew`, JavaScript's [npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b), and\nLinux's `apt`.\n\nIt's closely related to pip. In fact, it uses pip, but is focused on installing and managing Python packages that can be\nrun from the command line directly as applications.\n\n### Features\n\n`pipx` enables you to\n\n- expose CLI entrypoints of packages (\"apps\") installed to isolated environments with the `install` command,\n    guaranteeing no dependency conflicts and clean uninstalls;\n- easily list, upgrade, and uninstall packages that were installed with pipx; and\n- run the latest version of a Python application in a temporary environment with the `run` command.\n\nBest of all, pipx runs with regular user permissions, never calling `sudo pip install`.\n\n## Install pipx\n\n### On macOS\n\n```\nbrew install pipx\npipx ensurepath\n```\n\n### On Linux\n\n```\npython3 -m pip install --user pipx\npython3 -m pipx ensurepath\n```\n\n### On Windows\n\n```\nscoop install pipx\npipx ensurepath\n```\n\nFor more detailed installation instructions, see the\n[full documentation](https://pipx.pypa.io/stable/how-to/install-pipx/).\n\n## Quick Start\n\nInstall an application globally:\n\n```\npipx install pycowsay\npycowsay mooo\n```\n\nRun an application without installing:\n\n```\npipx run pycowsay moo\n```\n\nSee the [full documentation](https://pipx.pypa.io/stable/) for more details.\n\n## Contributing\n\nIssues and Pull Requests are definitely welcome! Check out [Contributing](https://pipx.pypa.io/stable/contributing/) to\nget started. Everyone who interacts with the pipx project via codebase, issue tracker, chat rooms, or otherwise is\nexpected to follow the [PSF Code of Conduct](https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md).\n"
  },
  {
    "path": "docs/changelog.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\nThis project uses [*towncrier*](https://towncrier.readthedocs.io/) for keeping the changelog. DO NOT commit any changes to this file.\n{% include '_draft_changelog.md' ignore missing %}\n\n<!-- towncrier release notes start -->\n\n## [1.10.0](https://github.com/pypa/pipx/tree/1.10.0) - 2026-03-18\n\n### Features\n\n- Add `--with` flag to `pipx run` to allow injecting dependencies ([#1607](https://github.com/pypa/pipx/issues/1607))\n- add more specific directions in the logs towards a resolution if you have a space in the PIX_HOME path ([#1634](https://github.com/pypa/pipx/issues/1634))\n\n### Bugfixes\n\n- Fixed upgrade command failing when package name includes extras (e.g., `pipx upgrade \"coverage[toml]\"`). ([#925](https://github.com/pypa/pipx/issues/925))\n- Fix run command with bash substitution (e.g. `pipx run <(pbpaste)`) ([#1293](https://github.com/pypa/pipx/issues/1293))\n- Allow `pipx runpip` to split single string arguments. ([#1520](https://github.com/pypa/pipx/issues/1520))\n- Fix handling of shared libraries when the original Python interpreter is removed on Windows by detecting stale venv references and recreating the shared libraries with the current Python. ([#1723](https://github.com/pypa/pipx/issues/1723))\n\n### Misc\n\n- [#1638](https://github.com/pypa/pipx/issues/1638), [#1731](https://github.com/pypa/pipx/issues/1731)\n\n\n## [1.9.0](https://github.com/pypa/pipx/tree/1.9.0) - 2026-03-17\n\n### Features\n\n- Add completion choices for `pipx environment --value`. ([#1498](https://github.com/pypa/pipx/issues/1498))\n\n### Bugfixes\n\n- Ignore recursive symlink loops in PIPX_BIN_DIR. ([#1592](https://github.com/pypa/pipx/issues/1592))\n- `pipx reinstall`: An exception will now be raised if package is pinned. ([#1611](https://github.com/pypa/pipx/issues/1611))\n- Stop `pipx run` from leaving bad temporary venvs when first installation was unsuccessful. ([#1709](https://github.com/pypa/pipx/issues/1709))\n\n\n## [1.8.0](https://github.com/pypa/pipx/tree/1.8.0) - 2025-09-30\n\n### Features\n\n- Rename environmental variable `USE_EMOJI` to `PIPX_USE_EMOJI`. ([#1395](https://github.com/pypa/pipx/issues/1395))\n- Add `--all-shells` flag to `pipx ensurepath`. ([#1585](https://github.com/pypa/pipx/issues/1585))\n- Add support for Python 3.13 ([#1647](https://github.com/pypa/pipx/issues/1647))\n\n### Bugfixes\n\n- On Windows, no longer overwrite existing files on upgrade if source and destination are the same ([#683](https://github.com/pypa/pipx/issues/683))\n- Update the logic of finding python interpreter such that `--fetch-missing-python` works on Windows ([#1521](https://github.com/pypa/pipx/issues/1521))\n- Fix no message displayed when no packages are upgraded with `upgrade-all`. ([#1565](https://github.com/pypa/pipx/issues/1565))\n- Fix incorrect order of flags when using `pipx upgrade`. ([#1610](https://github.com/pypa/pipx/issues/1610))\n- Update the archive name of build of Python for Windows ([#1630](https://github.com/pypa/pipx/issues/1630))\n- Set up standalone python fetching to use checksums directly from the GitHub API. ([#1652](https://github.com/pypa/pipx/issues/1652))\n- Fix running a script with explicitly empty ``dependencies = []``. ([#1658](https://github.com/pypa/pipx/issues/1658))\n\n### Improved Documentation\n\n- Fix `/changelog/` and `/contributing/` docs URLs ([#1540](https://github.com/pypa/pipx/issues/1540))\n\n\n## [1.7.1](https://github.com/pypa/pipx/tree/1.7.1) - 2024-08-23\n\n### Bugfixes\n\n- Use minimum supported Python to build zipapp in release action such that `tomli` is included in it. ([#1514](https://github.com/pypa/pipx/issues/1514))\n\n\n## [1.7.0](https://github.com/pypa/pipx/tree/1.7.0) - 2024-08-22\n\n### Features\n\n- Add a `--prepend` option to the `pipx ensurepath` command to allow prepending `pipx`'s location to `PATH` rather than appending to it. This is useful when you want to prioritize `pipx`'s executables over other executables in your `PATH`. ([#1451](https://github.com/pypa/pipx/issues/1451))\n- List `PIPX_GLOBAL_[HOME|BIN_DIR|MAN_DIR]` in `pipx environment`. ([#1492](https://github.com/pypa/pipx/issues/1492))\n\n### Bugfixes\n\n- Introduce `PIPX_HOME_ALLOW_SPACE` environment variable, to silence the spaces in pipx home path warning ([#1320](https://github.com/pypa/pipx/issues/1320))\n- Fix passing constraints file path into `pipx install` operation via `pip` args ([#1389](https://github.com/pypa/pipx/issues/1389))\n- Add help messages for `pipx pin` and `pipx unpin` commands. ([#1438](https://github.com/pypa/pipx/issues/1438))\n- Stop `pipx install --global` from installing files in `~/.local`. ([#1475](https://github.com/pypa/pipx/issues/1475))\n- Fix installation abortion on multiple packages when one or more are already installed. ([#1509](https://github.com/pypa/pipx/issues/1509))\n\n### Improved Documentation\n\n- Move all documentation files to the `docs` directory. ([#1479](https://github.com/pypa/pipx/issues/1479))\n\n\n## [1.6.0](https://github.com/pypa/pipx/tree/1.6.0) - 2024-06-01\n\n\n### Features\n\n- Add `install-all` command to install packages according to spec metadata file. ([#687](https://github.com/pypa/pipx/issues/687))\n- Introduce `pipx pin` and `pipx unpin` commands, which can be used to pin or unpin the version\n  of an installed package, so it will not be upgraded by `pipx upgrade` or `pipx upgrade-all`. ([#891](https://github.com/pypa/pipx/issues/891))\n- Add a new option `--pinned` to `pipx list` command for listing pinned packages only. ([#891](https://github.com/pypa/pipx/issues/891))\n- Add `pipx interpreter upgrade` command to upgrade local standalone python in micro/patch level ([#1249](https://github.com/pypa/pipx/issues/1249))\n- Add `--requirement` option to `inject` command to read list of packages from a text file. ([#1252](https://github.com/pypa/pipx/issues/1252))\n- Add `pipx upgrade-shared` command, to create/upgrade shared libraries as a standalone command. ([#1316](https://github.com/pypa/pipx/issues/1316))\n- Allow `upgrade` command to accept multiple packages as arguments. ([#1336](https://github.com/pypa/pipx/issues/1336))\n- Support Python version for `--python` arg when py launcher is not available ([#1342](https://github.com/pypa/pipx/issues/1342))\n- Make `install-all` gather errors in batch ([#1348](https://github.com/pypa/pipx/issues/1348))\n\n### Bugfixes\n\n- Resolve the `DEFAULT_PYTHON` to the actual absolute path ([#965](https://github.com/pypa/pipx/issues/965))\n- Fix error log overwrite for \"-all\" batch operations. ([#1132](https://github.com/pypa/pipx/issues/1132))\n- Do not reinstall already injected packages without `--force` being passed. ([#1300](https://github.com/pypa/pipx/issues/1300))\n- Only show `--python` and `--force` flag warning if both flags are present ([#1304](https://github.com/pypa/pipx/issues/1304))\n- Don't allow paths to be passed into `pipx reinstall`, as this might wreak havoc. ([#1324](https://github.com/pypa/pipx/issues/1324))\n- Make the Python `venv` module arguments work with `upgrade --install` ([#1344](https://github.com/pypa/pipx/issues/1344))\n- Fix version check for standalone python ([#1349](https://github.com/pypa/pipx/issues/1349))\n- Validate package(s) argument should not be path(s). ([#1354](https://github.com/pypa/pipx/issues/1354))\n- Validate whether a package is an URL correctly. ([#1355](https://github.com/pypa/pipx/issues/1355))\n- Support python3.8 for standalone python builds ([#1375](https://github.com/pypa/pipx/issues/1375))\n- Install specified version of `--preinstall` dependency instead of latest version ([#1377](https://github.com/pypa/pipx/issues/1377))\n- Move `--global` option into shared parser, such that it can be passed after the subcommand, for example `pipx ensurepath --global`. ([#1397](https://github.com/pypa/pipx/issues/1397))\n- Fix discovery of a `pipx run` entry point if a local path was given as package. ([#1422](https://github.com/pypa/pipx/issues/1422))\n\n### Improved Documentation\n\n- Create a dedicated section for manual pages and add an example with `pdm-backend`. ([#1312](https://github.com/pypa/pipx/issues/1312))\n- Add example, test and cli help description how to install multiple packages with the --preinstall flag ([#1321](https://github.com/pypa/pipx/issues/1321))\n- Refine docs generation script and template. ([#1325](https://github.com/pypa/pipx/issues/1325))\n- Add a note about sourcing the shell config file for `ensure_path` ([#1346](https://github.com/pypa/pipx/issues/1346))\n\n\n## [1.5.0](https://github.com/pypa/pipx/tree/1.5.0) - 2024-03-29\n\n\n### Features\n\n- Add `--global` option to `pipx` commands.\n      - This will run the action in a global scope and affect environment for all system users. ([#754](https://github.com/pypa/pipx/issues/754))\n- Add a `--fetch-missing-python` flag to all commands that accept a `--python` flag.\n      - When combined, this will automatically download a standalone copy of the requested python version if it's not already available on the user's system. ([#1242](https://github.com/pypa/pipx/issues/1242))\n- Add commands to list and prune standalone interpreters ([#1248](https://github.com/pypa/pipx/issues/1248))\n- Revert platform-specific directories on MacOS and Windows\n      - They were leading to a lot of issues with Windows sandboxing and spaces in shebangs on MacOS. ([#1257](https://github.com/pypa/pipx/issues/1257))\n- Add `--install` option to `pipx upgrade` command.\n      - This will install the package given as argument if it is not already installed. ([#1262](https://github.com/pypa/pipx/issues/1262))\n\n### Bugfixes\n\n- Correctly resolve home directory in pipx directory environment variables. ([#94](https://github.com/pypa/pipx/issues/94))\n- Pass through `pip` arguments when upgrading shared libraries. ([#964](https://github.com/pypa/pipx/issues/964))\n- Fix installation issues when files in the working directory interfere with venv creation process. ([#1091](https://github.com/pypa/pipx/issues/1091))\n- Report correct filename in tracebacks with `pipx run <scriptname>` ([#1191](https://github.com/pypa/pipx/issues/1191))\n- Let self-managed pipx uninstall itself on windows again. ([#1203](https://github.com/pypa/pipx/issues/1203))\n- Fix path resolution for python executables looked up in PATH on windows. ([#1205](https://github.com/pypa/pipx/issues/1205))\n- Display help message when `pipx install` is run without arguments. ([#1266](https://github.com/pypa/pipx/issues/1266))\n- Fix crashes due to superfluous `-q ` flags by discarding exceeding values ([#1283](https://github.com/pypa/pipx/issues/1283))\n\n### Improved Documentation\n\n- Update the completion instructions for zipapp users. ([#1072](https://github.com/pypa/pipx/issues/1072))\n- Update the example for running scripts with dependencies. ([#1227](https://github.com/pypa/pipx/issues/1227))\n- Update the docs for package developers on the use of configuration using pyproject.toml ([#1229](https://github.com/pypa/pipx/issues/1229))\n- Add installation instructions for Fedora ([#1239](https://github.com/pypa/pipx/issues/1239))\n- Update the examples for installation from local dir ([#1277](https://github.com/pypa/pipx/issues/1277))\n- Fix inconsistent wording in `pipx install` command description. ([#1307](https://github.com/pypa/pipx/issues/1307))\n\n### Deprecations and Removals\n\n- Deprecate `--skip-maintenance` flag of `pipx list`; maintenance is now never executed there ([#1256](https://github.com/pypa/pipx/issues/1256))\n\n### Misc\n\n- [#1296](https://github.com/pypa/pipx/issues/1296)\n\n\n## [1.4.3](https://github.com/pypa/pipx/tree/1.4.3) - 2024-01-16\n\n\n### Bugfixes\n\n- Autofix python version for pylauncher, when version is provided prefixed with `python` ([#1150](https://github.com/pypa/pipx/issues/1150))\n- Support building pipx wheels with setuptools-scm<7, such as on FreeBSD. ([#1208](https://github.com/pypa/pipx/issues/1208))\n\n### Improved Documentation\n\n- Provide useful error messages when unresolvable python version is passed ([#1150](https://github.com/pypa/pipx/issues/1150))\n- Introduce towncrier for managing the changelog ([#1161](https://github.com/pypa/pipx/issues/1161))\n- Add workaround for using pipx applications in shebang under macOS ([#1198](https://github.com/pypa/pipx/issues/1198))\n\n\n## [1.4.2](https://github.com/pypa/pipx/tree/1.4.2)\n\n### Features\n\n- Allow skipping maintenance tasks during list command\n- Raise more user friendly error when provided `--python` version is not found\n- Update `pipx run` on scripts using `/// script` and no `run` table following the updated version of PEP 723 (#1180)\n\n### Bugfixes\n\n- Include `tomli` into `pipx.pyz` (zipapp) so that it can be executed with Python 3.10 or earlier (#1142)\n- Fix resolving the python executable path on linux\n- `pipx run`: Verify whether the script name provided is a file before running it\n- Avoid repeated exception logging in a few rare cases (#1192)\n\n## [1.4.1](https://github.com/pypa/pipx/tree/1.4.1)\n\n### Bugfixes\n\n- Set default logging level to WARNING, so debug log messages won't be shown without passing additional flags such as `--verbose`\n\n## [1.4.0](https://github.com/pypa/pipx/tree/1.4.0)\n\n### Features\n\n- Add `--quiet` and `--verbose` options for the `pipx` subcommands\n- Add ability to install multiple packages at once\n- Delete directories directly instead of spawning rmdir on Windows\n\n### Improved Documentation\n\n- Add Scoop installation instructions\n\n### Bugfixes\n\n- \"Failed to delete\" error when using Microsoft Store Python\n- \"No pyvenv.cfg file\" error when using Microsoft Store Python (#1164)\n\n## [1.3.3](https://github.com/pypa/pipx/tree/1.3.3)\n\n### Improved Documentation\n\n- Make the logo more visible in dark mode\n\n## [1.3.2](https://github.com/pypa/pipx/tree/1.3.2)\n\n### Features\n\n- The project version number is now dynamic and generated from the VCS at build time\n\n### Improved Documentation\n\n- Add additional example for --pip-args option, to docs/examples.md\n\n## [1.3.1](https://github.com/pypa/pipx/tree/1.3.1)\n\n### Bugfixes\n\n- Fix combining of --editable and --force flag\n\n## [1.3.0](https://github.com/pypa/pipx/tree/1.3.0)\n\n### Features\n\n- Allow running `pip` with `pipx run`\n- Add `--with-suffix` for `pipx inject` command\n- `pipx install`: emit a warning when `--force` and `--python` were passed at the same time\n- Add explicit 3.12 support\n- Make usage message in `pipx run` show `package_or_url`, so extra will be printed out as well\n- Use the py launcher, if available, to select Python version with the `--python` option\n- add pre-commit hook support\n- Add `pipx install --preinstall` to support preinstalling build requirements\n- Return an error message when directory can't be added to PATH successfully\n- Expose manual pages included in an application installed with `pipx install`\n- Check whether pip module exists in shared lib before performing any actions, such as `reinstall-all`.\n- Drop `setuptools` and `wheel` from the shared libraries. This results in less time consumption when the libraries are\n  automatically upgraded.\n- Support [inline script metadata](https://packaging.python.org/en/latest/specifications/inline-script-metadata/)\n  in `pipx run`.\n- Imply `--include-apps` when running `pipx inject --include-deps`\n- Add `--force-reinstall` to pip arguments when `--force` was passed\n- Support including requirements in scripts run using `pipx run` (#916)\n- Pass `pip_args` to `shared_libs.upgrade()`\n- Fallback to user's log path if the default log path (`$PIPX_HOME/logs`) is not writable to aid with pipx being used\n  for multi-user (e.g. system-wide) installs of applications\n- Don't show escaped backslashes for paths in console output\n- Move `pipx` paths to ensure compatibility with the platform-specific user directories\n- Pass `--no-input` to pip when output is not piped to parent stdout\n- Print all environment variables in `pipx environment`\n\n### Improved Documentation\n\n- Add more examples for `pipx run`\n- Add subsection to make README easier to read\n\n### Deprecations and Removals\n\n- Drop support for Python 3.7\n\n### Bugfixes\n\n- Fix wrong interpreter usage when injecting local pip-installable dependencies into venvs\n- Fix program name in generated manual page\n\n## [1.2.1](https://github.com/pypa/pipx/tree/1.2.1)\n\n### Bugfixes\n\n- Fix compatibility to packaging 23.2+ by removing reliance on packaging's requirement validation logic and detecting a\n  URL-based requirement in pipx. (#1070)\n\n## [1.2.0](https://github.com/pypa/pipx/tree/1.2.0)\n\n### Features\n\n- Add `pipx uninject` command (#820)\n- Ship a [zipapp](https://docs.python.org/3/library/zipapp.html) of pipx\n- Match pip's behaviour when package name ends with archive extension (treat it as a path)\n- Change the program name to `path/to/python -m pipx` when running as `python -m pipx`\n- Improve the detection logic for MSYS2 to avoid entering infinite loop (#908) (#938)\n- Remove extra trailing quote from exception message\n- Fix EncodingWarning in `pipx_metadata_file`.\n\n### Improved Documentation\n\n- Add an example for installation from source with extras\n- Fix `pipx run` examples and update Python versions used by `pipx install` examples\n\n### Bugfixes\n\n- Add test for pip module in `pipx reinstall` to fix an issue with `pipx reinstall-all` (#935)\n\n## [1.1.0](https://github.com/pypa/pipx/tree/1.1.0)\n\n### Features\n\n- Add `pipx environment` command (#793)\n- Add `list --short` option to list only package names (#804)\n- Improve the behaviour of `shlex.split` on Windows, so paths on Windows can be handled properly when they are passed in\n  `--pip-args`. (#794)\n- [dev] Change github action job names\n- Add additional examples for installation from git repos\n- [packaging] Switch to [PEP 621](https://www.python.org/dev/peps/pep-0621/)\n- Add a CACHEDIR.TAG to the cache directory to prevent it from being included in archives and backups. For more\n  information about cache directory tags, see https://bford.info/cachedir\n\n### Bugfixes\n\n- Fix encoding issue on Windows when pip fails to install a package\n\n### Improved Documentation\n\n- Add more examples\n- Fix the command for\n  [installing development version](https://pipx.pypa.io/stable/installation/#install-pipx-development-versions). (#801)\n- Fix test status badge in readme file\n\n## [1.0.0](https://github.com/pypa/pipx/tree/1.0.0)\n\n### Features\n\n- Support [argcomplete 2.0.0](https://pypi.org/project/argcomplete/2.0.0) (#790)\n- Include machinery to build a manpage for pipx with [argparse-manpage](https://pypi.org/project/argparse-manpage/).\n- Add better handling for 'app not found' when a single app is present in the project, and an improved error message\n  (#733)\n\n### Bugfixes\n\n- Fixed animations sending output to stdout, which can break JSON output. (#769)\n- Fix typo in `pipx upgrade-all` output\n\n## [0.17.0](https://github.com/pypa/pipx/tree/0.17.0)\n\n- Support `pipx run` with version constraints and extras. (#697)\n\n## [0.16.5](https://github.com/pypa/pipx/tree/0.16.5)\n\n- Fixed `pipx list` output phrasing to convey that python version displayed is the one with which package was installed.\n- Fixed `pipx install` to provide return code 0 if venv already exists, similar to pip’s behavior. (#736)\n- [docs] Update ansible's install command in\n  [Programs to Try document](https://pipx.pypa.io/stable/programs-to-try/#ansible) to work with Ansible 2.10+ (#742)\n\n## [0.16.4](https://github.com/pypa/pipx/tree/0.16.4)\n\n- Fix to `pipx ensurepath` to fix behavior in user locales other than UTF-8, to fix #644. The internal change is to use\n  userpath v1.6.0 or greater. (#700)\n- Fix virtual environment inspection for Python releases that uses an int for its release serial number. (#706)\n- Fix PermissionError in windows when pipx manages itself. (#718)\n\n## [0.16.3](https://github.com/pypa/pipx/tree/0.16.3)\n\n- Organization: pipx is extremely pleased to now be a project of the Python Packaging Authority (PyPA)! Note that our\n  github URL has changed to [pypa/pipx](https://github.com/pypa/pipx)\n- Fixed `pipx list --json` to return valid json with no venvs installed. Previously would return an empty string to\n  stdout. (#681)\n- Changed `pipx ensurepath` bash behavior so that only one of {`~/.profile`, `~/.bash\\_profile`} is modified with the\n  extra pipx paths, not both. Previously, if a `.bash_profile` file was created where one didn't exist, it could cause\n  problems, e.g. #456. The internal change is to use userpath v1.5.0 or greater. (#684)\n- Changed default nox tests, Github Workflow tests, and pytest behavior to use local pypi server with fixed lists of\n  available packages. This allows greater test isolation (no network pypi access needed) and determinism (fixed\n  available dependencies.) It also allows running the tests offline with some extra preparation beforehand (See\n  [Running Unit Tests Offline](https://pipx.pypa.io/stable/contributing/#running-unit-tests-offline)). The old style\n  tests that use the internet to access pypi.org are still available using `nox -s tests_internet` or\n  `pytest --net-pypiserver tests`. (#686)\n\n* Colorama is now only installed on Windows. (#691)\n\n## [0.16.2.1](https://github.com/pypa/pipx/tree/0.16.2.1)\n\n- Changed non-venv-info warnings and notices from `pipx list` to print to stderr. This especially prevents\n  `pipx list --json` from printing invalid json to stdout. (#680)\n- Fixed bug that could cause uninstall on Windows with injected packages to uninstall too many apps from the local\n  binary directory. (#679)\n\n## [0.16.2.0](https://github.com/pypa/pipx/tree/0.16.2.0)\n\n- Fixed bug #670 where uninstalling a venv could erroneously uninstall other apps from the local binary directory.\n  (#672)\n- Added `--json` switch to `pipx list` to output rich json-metadata for all venvs.\n- Ensured log files are utf-8 encoded to prevent Unicode encoding errors from occurring with emojis. (#646)\n- Fixed issue which made pipx incorrectly list apps as part of a venv when they were not installed by pipx. (#650)\n- Fixed old regression that would prevent pipx uninstall from cleaning up linked binaries if the venv was old and did\n  not have pipx metadata. (#651)\n- Fixed bugs with suffixed-venvs on Windows. Now properly summarizes install, and actually uninstalls associated\n  binaries for suffixed-venvs. (#653)\n- Changed venv minimum python version to 3.6, removing python 3.5 which is End of Life. (#666)\n\n## [0.16.1.0](https://github.com/pypa/pipx/tree/0.16.1.0)\n\n- Introduce the `pipx.run` entry point group as an alternative way to declare an application for `pipx run`.\n- Fix cursor show/hide to work with older versions of Windows. (#610)\n- Support text colors on Windows. (#612)\n- Better platform unicode detection to avoid errors and allow showing emojis when possible. (#614)\n- Don't emit show cursor or hide cursor codes if STDERR is not a tty. (#620)\n- Sped up `pipx list` (#624).\n- pip errors no longer stream to the shell when pip fails during a pipx install. pip's output is now saved to a log\n  file. In the shell, pipx will tell you the location of the log file and attempt to summarize why pip failed. (#625)\n- For `reinstall-all`, fixed bug where missing python executable would cause error. (#634)\n- Fix regression which prevented pipx from working with pythonloc (and `__pypackages__` folder). (#636)\n\n## [0.16.0.0](https://github.com/pypa/pipx/tree/0.16.0.0)\n\n- New venv inspection! The code that pipx uses to examine and determine metadata in an installed venv has been made\n  faster, better, and more reliable. It now uses modern python libraries like `packaging` and `importlib.metadata` to\n  examine installed venvs. It also now properly handles installed package extras. In addition, some problems pipx has\n  had with certain characters (like periods) in package names should be remedied.\n- Added reinstall command for reinstalling a single venv.\n- Changed `pipx run` on non-Windows systems to actually replace pipx process with the app process instead of running it\n  as a subprocess. (Now using python's `os.exec*`)\n- [bugfix] Fixed bug with reinstall-all command when package have been installed using a specifier. Now the initial\n  specifier is used.\n- [bugfix] Override display of `PIPX_DEFAULT_PYTHON` value when generating web documentation for `pipx install` #523\n- [bugfix] Wrap help documentation for environment variables.\n- [bugfix] Fixed uninstall crash that could happen on Windows for certain packages\n- [feature] Venv package name arguments now do not have to match exactly as pipx has them stored, but can be specified\n  in any python-package-name-equivalent way. (i.e. case does not matter, and `.`, `-`, `_` characters are\n  interchangeable.)\n- [change] Venvs with a suffix: A suffix can contain any characters, but for purposes of uniqueness, python package name\n  rules apply--upper- and lower-case letters are equivalent, and any number of `.`, `-`, or `_` characters in a row are\n  equivalent. (e.g. if you have a suffixed venv `pylint_1.0A` you could not add another suffixed venv called\n  `pylint--1-0a`, as it would not be a unique name.)\n- [implementation detail] Pipx shared libraries (providing pip, setuptools, wheel to pipx) are no longer installed using\n  pip arguments taken from the last regular pipx install. If you need to apply pip arguments to pipx's use of pip for\n  its internal shared libraries, use PIP\\_\\* environment variables.\n- [feature] Autocomplete for venv names is no longer restricted to an exact match to the literal venv name, but will\n  autocomplete any logically-similar python package name (i.e. case does not matter, and `.`, `-`, `_` characters are\n  all equivalent.)\n- pipx now reinstall its internal shared libraries when the user executes `reinstall-all`.\n- Made sure shell exit codes from every pipx command are correct. In the past some (like from `pipx upgrade`) were\n  wrong. The exit code from `pipx runpip` is now the exit code from the `pip` command run. The exit code from\n  `pipx list` will be 1 if one or more venvs have problems that need to be addressed.\n- pipx now writes a log file for each pipx command executed to `$PIPX_HOME/logs`, typically `~/.local/pipx/logs`. pipx\n  keeps the most recent 10 logs and deletes others.\n- `pipx upgrade` and `pipx upgrade-all` now have a `--upgrade-injected` option which directs pipx to also upgrade\n  injected packages.\n- `pipx list` now detects, identifies, and suggests a remedy for venvs with old-internal data (internal venv names) that\n  need to be updated.\n- Added a \"Troubleshooting\" page to the pipx web documentation for common problems pipx users may encounter.\n- pipx error, warning, and other messages now word-wrap so words are not split across lines. Their appearance is also\n  now more consistent.\n\n## [0.15.6.0](https://github.com/pypa/pipx/tree/0.15.6.0)\n\n- [docs] Update license\n- [docs] Display a more idiomatic command for registering completions on fish.\n- [bugfix] Fixed regression in list, inject, upgrade, reinstall-all commands when suffixed packages are used.\n- [bugfix] Do not reset package url during upgrade when main package is `pipx`\n- Updated help text to show description for `ensurepath` and `completions` help\n- Added support for user-defined default python interpreter via new `PIPX_DEFAULT_PYTHON`. Helpful for use with pyenv\n  among other uses.\n- [bugfix] Fixed bug where extras were ignored with a PEP 508 package specification with a URL.\n\n## [0.15.5.1](https://github.com/pypa/pipx/tree/0.15.5.1)\n\n- [bugfix] Fixed regression of 0.15.5.0 which erroneously made installing from a local path with package extras not\n  possible.\n\n## [0.15.5.0](https://github.com/pypa/pipx/tree/0.15.5.0)\n\n- pipx now parses package specification before install. It removes (with warning) the `--editable` install option for\n  any package specification that is not a local path. It also removes (with warning) any environment markers.\n- Disabled animation when we cannot determine terminal size or if the number of columns is too small. (Fixes #444)\n- [feature] Version of each injected package is now listed after name for `pipx list --include-injected`\n- Change metadata recorded from version-specified install to allow upgrades in future. Adds pipx dependency on\n  `packaging` package.\n- [bugfix] Prevent python error in case where package has no pipx metadata and advise user how to fix.\n- [feature] `ensurepath` now also ensures that pip user binary path containing pipx itself is in user's PATH if pipx was\n  installed using `pip install --user`.\n- [bugfix] For `pipx install`, fixed failure to install if user has `PIP_USER=1` or `user=true` in pip.conf. (#110)\n- [bugfix] Requiring userpath v1.4.1 or later so ensure Windows bug is fixed for `ensurepath` (#437)\n- [feature] log pipx version (#423)\n- [feature] `--suffix` option for `install` to allow multiple versions of same tool to be installed (#445)\n- [feature] pipx can now be used with the Windows embeddable Python distribution\n\n## [0.15.4.0](https://github.com/pypa/pipx/tree/0.15.4.0)\n\n- [feature] `list` now has a new option `--include-injected` to show the injected packages in the main apps\n- [bugfix] Fixed bug that can cause crash when installing an app\n\n## [0.15.3.1](https://github.com/pypa/pipx/tree/0.15.3.1)\n\n- [bugfix] Workaround multiprocessing issues on certain platforms (#229)\n\n## [0.15.3.0](https://github.com/pypa/pipx/tree/0.15.3.0)\n\n- [feature] Use symlinks on Windows when symlinks are available\n\n## [0.15.2.0](https://github.com/pypa/pipx/tree/0.15.2.0)\n\n- [bugfix] Improved error reporting during venv metadata inspection.\n- [bugfix] Fixed incompatibility with pypy as venv interpreter (#372).\n- [bugfix] Replaced implicit dependency on setuptools with an explicit dependency on packaging (#339).\n- [bugfix] Continue reinstalling packages after failure\n- [bugfix] Hide cursor while pipx runs\n- [feature] Add environment variable `USE_EMOJI` to allow enabling/disabling emojis (#376)\n- [refactor] Moved all commands to separate files within the commands module (#255).\n- [bugfix] Ignore system shared libraries when installing shared libraries pip, wheel, and setuptools. This also fixes\n  an incompatibility with Debian/Ubuntu's version of pip (#386).\n\n## [0.15.1.3](https://github.com/pypa/pipx/tree/0.15.1.3)\n\n- [bugfix] On Windows, pipx now lists correct Windows apps (#217)\n- [bugfix] Fixed a `pipx install` bug causing incorrect python binary to be used when using the optional --python\n  argument in certain situations, such as running pipx from a Framework python on macOS and specifying a non-Framework\n  python.\n\n## [0.15.1.2](https://github.com/pypa/pipx/tree/0.15.1.2)\n\n- [bugfix] Fix recursive search of dependencies' apps so no apps are missed.\n- `upgrade-all` now skips editable packages, because pip disallows upgrading editable packages.\n\n## [0.15.1.1](https://github.com/pypa/pipx/tree/0.15.1.1)\n\n- [bugfix] fix regression that caused installing with --editable flag to fail package name determination.\n\n## [0.15.1.0](https://github.com/pypa/pipx/tree/0.15.1.0)\n\n- Add Python 3.8 to PyPI classifier and travis test matrix\n- [feature] auto-upgrade shared libraries, including pip, if older than one month. Hide all pip warnings that a new\n  version is available. (#264)\n- [bugfix] pass pip arguments to pip when determining package name (#320)\n\n## [0.15.0.0](https://github.com/pypa/pipx/tree/0.15.0.0)\n\nUpgrade instructions: When upgrading to 0.15.0.0 or above from a pre-0.15.0.0 version, you must re-install all packages\nto take advantage of the new persistent pipx metadata files introduced in this release. These metadata files store pip\nspecification values, injected packages, any custom pip arguments, and more in each main package's venv. You can do this\nby running `pipx reinstall-all` or `pipx uninstall-all`, then reinstalling manually.\n\n- `install` now has no `--spec` option. You may specify any valid pip specification for `install`'s main argument.\n- `inject` will now accept pip specifications for dependency arguments\n- Metadata is now stored for each application installed, including install options like `--spec`, and injected packages.\n  This information allows upgrade, upgrade-all and reinstall-all to work properly even with non-pypi installed packages.\n  (#222)\n- `upgrade` options `--spec` and `--include-deps` were removed. Pipx now uses the original options used to install each\n  application instead. (#222)\n- `upgrade-all` options `--include-deps`, `--system-site-packages`, `--index-url`, `--editable`, and `--pip-args` were\n  removed. Pipx now uses the original options used to install each application instead. (#222)\n- `reinstall-all` options `--include-deps`, `--system-site-packages`, `--index-url`, `--editable`, and `--pip-args` were\n  removed. Pipx now uses the original options used to install each application instead. (#222)\n- Handle missing interpreters more gracefully (#146)\n- Change `reinstall-all` to use system python by default for apps. Now use `--python` option to specify a different\n  python version.\n- Remove the PYTHONPATH environment variable when executing any command to prevent conflicts between pipx dependencies\n  and package dependencies when pipx is installed via homebrew. Homebrew can use PYTHONPATH manipulation instead of\n  virtual environments. (#233)\n- Add printed summary after successful call to `pipx inject`\n- Support associating apps with Python 3.5\n- Improvements to animation status text\n- Make `--python` argument in `reinstall-all` command optional\n- Use threads on OS's without support for semaphores\n- Stricter parsing when passing `--` argument as delimiter\n\n## [0.14.0.0](https://github.com/pypa/pipx/tree/0.14.0.0)\n\n- Speed up operations by using shared venv for `pip`, `setuptools`, and `wheel`. You can see more detail in the 'how\n  pipx works' section of the documentation. (#164, @pfmoore)\n- Breaking change: for the `inject` command, change `--include-binaries` to `--include-apps`\n- Change all terminology from `binary` to `app` or `application`\n- Improve argument parsing for `pipx run` and `pipx runpip`\n- If `--force` is passed, remove existing files in PIPX_BIN_DIR\n- Move animation to start of line, hide cursor when animating\n\n## [0.13.2.3](https://github.com/pypa/pipx/tree/0.13.2.3)\n\n- Fix regression when installing a package that doesn't have any entry points\n\n## [0.13.2.2](https://github.com/pypa/pipx/tree/0.13.2.2)\n\n- Remove unnecessary and sometimes incorrect check after `pipx inject` (#195)\n- Make status text/animation reliably disappear before continuing\n- Update animation symbols\n\n## [0.13.2.1](https://github.com/pypa/pipx/tree/0.13.2.1)\n\n- Remove virtual environment if installation did not complete. For example, if it was interrupted by ctrl+c or if an\n  exception occurred for any reason. (#193)\n\n## [0.13.2.0](https://github.com/pypa/pipx/tree/0.13.2.0)\n\n- Add shell autocompletion. Also add `pipx completions` command to print instructions on how to add pipx completions to\n  your shell.\n- Un-deprecate `ensurepath`. Use `userpath` internally instead of instructing users to run the `userpath` cli command.\n- Improve detection of PIPX_BIN_DIR not being on PATH\n- Improve error message when an existing symlink exists in PIPX_BIN_DIR and points to the wrong location\n- Improve handling of unexpected files in PIPX_HOME (@uranusjr)\n- swap out of order logic in order to correctly recommend --include-deps (@joshuarli)\n- [dev] Migrate from tox to nox\n\n## [0.13.1.1](https://github.com/pypa/pipx/tree/0.13.1.1)\n\n- Do not raise bare exception if no binaries found (#150)\n- Update pipsi migration script\n\n## [0.13.1.0](https://github.com/pypa/pipx/tree/0.13.1.0)\n\n- Deprecate `ensurepath` command. Use `userpath append ~/.local/bin`\n- Support redirects and proxies when downloading python files (i.e. `pipx run http://url/file.py`)\n- Use tox for document generation and CI testing (CI tests are now functional rather than static tests on style and\n  formatting!)\n- Use mkdocs for documentation\n- Change default cache duration for `pipx run` from 2 to 14 days\n\n## [0.13.0.1](https://github.com/pypa/pipx/tree/0.13.0.1)\n\n- Fix upgrade-all and reinstall-all regression\n\n## [0.13.0.0](https://github.com/pypa/pipx/tree/0.13.0.0)\n\n- Add `runpip` command to run arbitrary pip commands in pipx-managed virtual environments\n- Do not raise error when running `pipx install PACKAGE` and the package has already been installed by pipx (#125). This\n  is the cause of the major version change from 0.12 to 0.13.\n- Add `--skip` argument to `upgrade-all` and `reinstall-all` commands, to let the user skip particular packages\n\n## [0.12.3.3](https://github.com/pypa/pipx/tree/0.12.3.3)\n\n- Update logic in determining a package's binaries during installation. This removes spurious binaries from the\n  installation. (#104)\n- Improve compatibility with Debian distributions by using `shutil.which` instead of `distutils.spawn.find_executable`\n  (#102)\n\n## [0.12.3.2](https://github.com/pypa/pipx/tree/0.12.3.2)\n\n- Fix infinite recursion error when installing package such as `cloudtoken==0.1.84` (#103)\n- Fix windows type errors (#96, #98)\n\n## [0.12.3.1](https://github.com/pypa/pipx/tree/0.12.3.1)\n\n- Fix \"WindowsPath is not iterable\" bug\n\n## [0.12.3.0](https://github.com/pypa/pipx/tree/0.12.3.0)\n\n- Add `--include-deps` argument to include binaries of dependent packages when installing with pipx. This improves\n  compatibility with packages that depend on other installed packages, such as `jupyter`.\n- Speed up `pipx list` output (by running multiple processes in parallel) and by collecting all metadata in a single\n  subprocess call\n- More aggressive cache directory removal when `--no-cache` is passed to `pipx run`\n- [dev] Move inline text passed to subprocess calls to their own files to enable autoformatting, linting, unit testing\n\n## [0.12.2.0](https://github.com/pypa/pipx/tree/0.12.2.0)\n\n- Add support for PEP 582's `__pypackages__` (experimental). `pipx run BINARY` will first search in `__pypackages__` for\n  binary, then fallback to installing from PyPI. `pipx run --pypackages BINARY` will raise an error if the binary is not\n  found in `__pypackages__`.\n- Fix regression when installing with `--editable` flag (#93)\n- [dev] improve unit tests\n\n## [0.12.1.0](https://github.com/pypa/pipx/tree/0.12.1.0)\n\n- Cache and reuse temporary Virtual Environments created with `pipx run` (#61)\n- Update binary discovery logic to find \"scripts\" like awscli (#91)\n- Forward `--pip-args` to the pip upgrade command (previously the args were forwarded to install/upgrade commands for\n  packages) (#77)\n- When using environment variable PIPX_HOME, Virtual Environments will now be created at `$PIPX_HOME/venvs` rather than\n  at `$PIPX_HOME`.\n- [dev] refactor into multiple files, add more unit tests\n\n## [0.12.0.4](https://github.com/pypa/pipx/tree/0.12.0.4)\n\n- Fix parsing bug in pipx run\n\n## [0.12.0.3](https://github.com/pypa/pipx/tree/0.12.0.3)\n\n- list python2 as supported language so that pip installs with python2 will no longer install the pipx on PyPI from the\n  original pipx owner. Running pipx with python2 will fail, but at least it will not be as confusing as running the pipx\n  package from the original owner.\n\n## [0.12.0.2](https://github.com/pypa/pipx/tree/0.12.0.2)\n\n- forward arguments to run command correctly #90\n\n## [0.12.0.1](https://github.com/pypa/pipx/tree/0.12.0.1)\n\n- stop using unverified context #89\n\n## [0.12.0.0](https://github.com/pypa/pipx/tree/0.12.0.0)\n\n- Change installation instructions to use `pipx` PyPI name\n- Add `ensurepath` command\n\n## [0.11.0.2](https://github.com/pypa/pipx/tree/0.11.0.2)\n\n- add version argument parsing back in (fixes regression)\n\n## [0.11.0.1](https://github.com/pypa/pipx/tree/0.11.0.1)\n\n- add version check, command check, fix printed version update installation instructions\n\n## [0.11.0.0](https://github.com/pypa/pipx/tree/0.11.0.0)\n\n- Replace `pipx BINARY` with `pipx run BINARY` to run a binary in an ephemeral environment. This is a breaking API\n  change so the major version has been incremented. (Issue #69)\n- upgrade pip when upgrading packages (Issue #72)\n- support --system-site-packages flag (Issue #64)\n\n## [0.10.4.1](https://github.com/pypa/pipx/tree/0.10.4.1)\n\n- Fix version printed when `pipx --version` is run\n\n## [0.10.4.0](https://github.com/pypa/pipx/tree/0.10.4.0)\n\n- Add --index-url, --editable, and --pip-args flags\n- Updated README with pipsi migration instructions\n\n## [0.10.3.0](https://github.com/pypa/pipx/tree/0.10.3.0)\n\n- Display python version in list\n- Do not reinstall package if already installed (added `--force` flag to override)\n- When upgrading all packages, print message only when package is updated\n- Avoid accidental execution of pipx.**main**\n"
  },
  {
    "path": "docs/contributing.md",
    "content": "---\nhide:\n  - navigation\n---\n\nThanks for your interest in contributing to pipx!\n\nEveryone who interacts with the pipx project via codebase, issue tracker, chat rooms, or otherwise is expected to follow\nthe [PSF Code of Conduct](https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md).\n\n## Submitting changes\n\n1. Fork [the GitHub repository](https://github.com/pypa/pipx).\n1. Make a branch off of `main` and commit your changes to it.\n1. Add a changelog entry.\n1. Submit a pull request to the `main` branch on GitHub, referencing an open issue.\n\n### Changelog entries\n\nThe `CHANGELOG.md` file is built by [towncrier](https://pypi.org/project/towncrier/) from news fragments in the\n`changelog.d/` directory. To add an entry, create a news fragment in that directory named `{number}.{type}.md`, where\n`{number}` is the issue number, and `{type}` is one of `feature`, `bugfix`, `doc`, `removal`, or `misc`.\n\nFor example, if your issue number is 1234 and it's fixing a bug, then you would create `changelog.d/1234.bugfix.md`. PRs\ncan span multiple categories by creating multiple files: if you added a feature and deprecated/removed an old feature\nfor issue #5678, you would create `changelog.d/5678.feature.md` and `changelog.d/5678.removal.md`.\n\nA changelog entry is meant for end users and should only contain details relevant to them. In order to maintain a\nconsistent style, please keep the entry to the point, in sentence case, shorter than 80 characters, and in an imperative\ntone. An entry should complete the sentence \"This change will ...\". If one line is not enough, use a summary line in an\nimperative tone, followed by a description of the change in one or more paragraphs, each wrapped at 80 characters and\nseparated by blank lines.\n\nYou don't need to reference the pull request or issue number in a changelog entry, since towncrier will add a link using\nthe number in the file name. Similarly, you don't need to add your name to the entry, since that will be associated with\nthe pull request.\n\n## Codebase Architecture\n\nAll source code lives under `src/pipx/`. The CLI entry point is `main.py`, which dispatches to command modules in\n`commands/`.\n\n```mermaid\nflowchart TD\n    MAIN[\"main.py<br/>CLI parser + dispatch\"] --> CMDS[\"commands/\"]\n    CMDS --> INSTALL[\"install.py\"]\n    CMDS --> RUN[\"run.py\"]\n    CMDS --> UPGRADE[\"upgrade.py\"]\n    CMDS --> INJECT[\"inject.py\"]\n    CMDS --> LIST[\"list_packages.py\"]\n    CMDS --> PIN[\"pin.py\"]\n    CMDS --> INTERP_CMD[\"interpreter.py\"]\n    CMDS --> OTHER[\"uninstall, uninject,<br/>reinstall, ensure_path,<br/>environment, run_pip\"]\n\n    MAIN --> VENV[\"venv.py<br/>Venv + VenvContainer\"]\n    MAIN --> PATHS[\"paths.py<br/>PIPX_HOME resolution\"]\n    VENV --> SHARED[\"shared_libs.py<br/>shared pip venv\"]\n    VENV --> INSPECT[\"venv_inspect.py<br/>metadata reader\"]\n    VENV --> META[\"pipx_metadata_file.py<br/>JSON persistence\"]\n\n    RUN --> SPEC[\"package_specifier.py<br/>PEP 508 parsing\"]\n    RUN --> INTERP[\"interpreter.py<br/>Python resolution\"]\n    INTERP --> STANDALONE[\"standalone_python.py<br/>download builds\"]\n\n    style MAIN fill:#3f72af,color:#fff\n    style CMDS fill:#2a9d8f,color:#fff\n    style INSTALL fill:#2a9d8f,color:#fff\n    style RUN fill:#2a9d8f,color:#fff\n    style UPGRADE fill:#2a9d8f,color:#fff\n    style INJECT fill:#2a9d8f,color:#fff\n    style LIST fill:#2a9d8f,color:#fff\n    style PIN fill:#2a9d8f,color:#fff\n    style INTERP_CMD fill:#2a9d8f,color:#fff\n    style OTHER fill:#2a9d8f,color:#fff\n    style VENV fill:#7c4dff,color:#fff\n    style PATHS fill:#c78c20,color:#fff\n    style SHARED fill:#c78c20,color:#fff\n    style INSPECT fill:#7c4dff,color:#fff\n    style META fill:#7c4dff,color:#fff\n    style SPEC fill:#388e3c,color:#fff\n    style INTERP fill:#388e3c,color:#fff\n    style STANDALONE fill:#388e3c,color:#fff\n```\n\n### Key modules\n\n`venv.py` is the core abstraction. `Venv` wraps a single virtual environment (create, install, uninstall, upgrade) and\n`VenvContainer` manages the collection under `PIPX_HOME/venvs/`. Both delegate pip operations to the shared libraries\nvenv managed by `shared_libs.py`.\n\n`paths.py` resolves all directory locations from environment variables, platform defaults, and legacy fallback paths.\n`pipx_metadata_file.py` serializes install options (spec, pip args, injected packages) to JSON inside each venv so that\n`upgrade` and `reinstall` can reproduce the original install.\n\n`interpreter.py` and `standalone_python.py` handle Python version resolution. When `--fetch-missing-python` is passed,\npipx downloads a standalone build from\n[python-build-standalone](https://github.com/astral-sh/python-build-standalone/releases) and caches it locally. The\n`pipx interpreter` subcommands (list, prune, upgrade) manage these cached interpreters.\n\n`commands/run.py` includes PEP 723 inline script metadata parsing. When you run a `.py` file, pipx scans for a\n`# /// script` block, extracts TOML-declared dependencies, and installs them into a cached temporary venv.\n\n## Running pipx For Development\n\nTo develop `pipx`, either create a [developer environment](#creating-a-developer-environment), or perform an editable\ninstall:\n\n```\npython -m pip install -e .\npython -m pipx --version\n```\n\n## Running Tests\n\n### Setup\n\npipx uses [tox](https://pypi.org/project/tox/) for development, continuous integration testing, and various tasks.\n\n`tox` defines environments in `tox.toml` which can be run with `tox run -e ENV_NAME`. Environment names can be listed\nwith `tox list`.\n\nInstall tox for pipx development:\n\n```\npython -m pip install --user tox\n```\n\nTests are defined as `tox` environments. You can see all tox environments with\n\n```\ntox list\n```\n\nAt the time of this writing, the output looks like this\n\n```\ndefault environments:\n3.13     -> run tests with 3.13\n3.12     -> run tests with 3.12\n3.11     -> run tests with 3.11\n3.10     -> run tests with 3.10\n3.9      -> run tests with 3.9\nlint     -> run pre-commit on the codebase\ndocs     -> build documentation\nman      -> build man page\n```\n\n### Creating a developer environment\n\nFor developing the tool (and to attach to your IDE) we recommend creating a Python environment via `tox run -e dev`,\nafterwards use the Python interpreter available under `.tox/dev/bin/python`.\n\n### Unit Tests\n\nTo run unit tests in Python 3.12, you can run\n\n```\ntox run -e 3.12\n```\n\n> [!TIP]\n> You can run a specific unit test by passing arguments to pytest, the test runner pipx uses:\n>\n> ```\n> tox run -e 3.9 -- -k EXPRESSION\n> ```\n>\n> `EXPRESSION` can be a test name, such as\n>\n> ```\n> tox run -e 3.9 -- -k test_uninstall\n> ```\n>\n> Coverage errors can usually be ignored when only running a subset of tests.\n\n### Running Unit Tests Offline\n\nRunning the unit tests requires a directory `.pipx_tests/package_cache` to be populated from a fixed list of package\ndistribution files (wheels or source files). If you have network access, `tox` automatically makes sure this directory\nis populated (including downloading files if necessary) as a first step. Thus, if you are running the tests with network\naccess, you can ignore the rest of this section.\n\nIf, however, you wish to run tests offline without the need for network access, you can populate\n`.pipx_tests/package_cache` yourself manually beforehand when you do have network access.\n\n### Lint Tests\n\nLinting is done via `pre-commit`, setting it up and running it can be done via `tox` by typing:\n\n```\ntox run -e lint\n```\n\n### Installing or injecting new packages in tests\n\nIf the tests are modified such that a new package / version combination is `pipx install`ed or `pipx inject`ed that\nwasn't used in other tests, then one must make sure it's added properly to the packages lists in\n`testdata/tests_packages`.\n\nTo accomplish this:\n\n- Edit `testdata/tests_packages/primary_packages.txt` to add the new package(s) that you wish to `pipx install` or\n    `pipx inject` in the tests.\n\nThen using Github workflows to generate all platforms in the Github CI:\n\n- Manually activate the Github workflow: Create tests package lists for offline tests\n- Download the artifact `lists` and put the files from it into `testdata/tests_packages/`\n\nFinally, check-in the new or modified list files in the directory `testdata/tests_packages`\n\n## Testing pipx on Continuous Integration builds\n\nUpon opening pull requests GitHub Actions will automatically trigger.\n\n## Building Documentation\n\n`pipx` autogenerates API documentation, and also uses templates.\n\nWhen updating pipx docs, make sure you are modifying the `docs` directory.\n\nYou can generate the documentation with\n\n```\ntox run -e docs\n```\n\nThis will capture CLI documentation for any pipx argument modifications, as well as generate templates to the docs\ndirectory.\n\n## Releasing New `pipx` Versions\n\nThe release process for pipx is designed to be simple and fully automated with a single button press. The workflow\nautomatically determines the next version based on changelog fragments, generates the changelog, creates the release\ncommit, and publishes to PyPI.\n\n### Initiating a Release\n\nNavigate to the **Actions** tab in the GitHub repository and select the **Pre-release** workflow. Click **Run workflow**\nand choose the appropriate version bump strategy. The `auto` option intelligently determines whether a minor or patch\nbump is needed by examining the types of changelog fragments present. If new features or removals exist, it performs a\nminor version bump; otherwise, it increments the patch version. Alternatively, you can explicitly select `major`,\n`minor`, or `patch` to control the version increment directly.\n\n### What Happens During Release\n\nOnce triggered, the pre-release workflow executes the `scripts/release.py` script which collects all changelog fragments\nfrom the `changelog.d/` directory and uses towncrier to generate the updated changelog. It then creates a release commit\nwith the message \"Release {version}\" and tags it with the version number. After running pre-commit hooks to ensure\nformatting, both the commit and tag are pushed to the main branch.\n\nThe act of pushing a version tag (matching the pattern `*.*.*`) automatically triggers the main release workflow. This\nworkflow builds the project distribution files, publishes the package to PyPI using trusted publishing, creates a GitHub\nrelease with auto-generated notes, and builds the zipapp using the minimum supported Python version before uploading it\nto the GitHub release assets.\n\n### Version Calculation Examples\n\nStarting from version `1.8.0`, the version bump types produce the following results: `auto` with feature fragments\nbecomes `1.9.0`, while `auto` with only bugfixes becomes `1.8.1`. Selecting `major` explicitly jumps to `2.0.0`, `minor`\nmoves to `1.9.0`, and `patch` increments to `1.8.1`. This automation eliminates the need for manual version management\nand ensures consistency across releases.\n"
  },
  {
    "path": "docs/explanation/comparisons.md",
    "content": "## pipx vs pip\n\n- pip is a general Python package installer. It can be used to install libraries or CLI applications with entrypoints.\n- pipx is a specialized package installer. It can only be used to install packages with CLI entrypoints.\n- pipx and pip both install packages from PyPI (or locally)\n- pipx relies on pip (and venv)\n- pipx replaces a subset of pip's functionality; it lets you install CLI applications but NOT libraries that you import\n    in your code.\n- you can install pipx with pip\n\nExample interaction: Install pipx with pip: `pip install --user pipx`\n\n## pipx vs poetry and pipenv\n\n- pipx is used solely for application consumption: you install CLI apps with it\n- pipenv and poetry are CLI apps used to develop applications and libraries\n- all three tools wrap pip and virtual environments for more convenient workflows\n\nExample interaction: Install pipenv and poetry with pipx: `pipx install poetry` Run pipenv or poetry with pipx:\n`pipx run poetry --help`\n\n## pipx vs venv\n\n- venv is part of Python's standard library in Python 3.2 and above\n- venv creates \"virtual environments\" which are sandboxed python installations\n- pipx heavily relies on the venv package\n\nExample interaction: pipx installs packages to environments created with venv. `pipx install black --verbose`\n\n## pipx vs pyenv\n\n- pyenv manages python versions on your system. It helps you install versions like Python 3.6, 3.7, etc.\n- pipx installs packages in virtual environments and exposes their entrypoints on your PATH\n\nExample interaction: Install a Python interpreter with pyenv, then install a package using pipx and that new\ninterpreter: `pipx install black --python=python3.11` where python3.11 was installed on the system with pyenv\n\n## pipx vs pipsi\n\n- pipx and pipsi both install packages in a similar way\n- pipx is under active development. pipsi is no longer maintained.\n- pipx always makes sure you're using the latest version of pip\n- pipx has the ability to run an app in one line, leaving your system unchanged after it finishes (`pipx run APP`) where\n    pipsi does not\n- pipx has the ability to recursively install binaries from dependent packages\n- pipx adds more useful information to its output\n- pipx has more CLI options such as upgrade-all, reinstall-all, uninstall-all\n- pipx is more modern. It uses Python 3.6+, and the `venv` package in the Python3 standard library instead of the python\n    2 package `virtualenv`.\n- pipx works with Python homebrew installations while pipsi does not (at least on my machine)\n- pipx defaults to less verbose output\n- pipx allows you to see each command it runs by passing the --verbose flag\n- pipx prints emojis 😀\n\nExample interaction: None. Either one or the other should be used. These tools compete for a similar workflow.\n\n### Migrating to pipx from pipsi\n\nAfter you have installed pipx, run\n[migrate_pipsi_to_pipx.py](https://raw.githubusercontent.com/pypa/pipx/main/scripts/migrate_pipsi_to_pipx.py). Why not\ndo this with your new pipx installation?\n\n```\npipx run https://raw.githubusercontent.com/pypa/pipx/main/scripts/migrate_pipsi_to_pipx.py\n```\n\n## pipx vs brew\n\n- Both brew and pipx install cli tools\n- They install them from different sources. brew uses a curated repository specifically for brew, and pipx generally\n    uses PyPI.\n\nExample interaction: brew can be used to install pipx, but they generally don't interact much.\n\n## pipx vs npx\n\n- Both can run cli tools (npx will search for them in node_modules, and if not found run in a temporary environment.\n    `pipx run` will search in `__pypackages__` and if not found run in a temporary environment)\n- npx works with JavaScript and pipx works with Python\n- Both tools attempt to make running executables written in a dynamic language (JS/Python) as easy as possible\n- pipx can also install tools globally; npx cannot\n\nExample interaction: None. These tools work for different languages.\n\n## pipx vs pip-run\n\n[pip-run](https://github.com/jaraco/pip-run) is focused on running **arbitrary Python code in ephemeral environments**\nwhile pipx is focused on running **Python binaries in ephemeral and non-ephemeral environments**.\n\nFor example these two commands both install poetry to an ephemeral environment and invoke poetry with `--help`.\n\n```\npipx run poetry --help\npip-run poetry -- -m poetry --help\n```\n\nExample interaction: None.\n\n## pipx vs fades\n\n[fades](https://github.com/PyAr/fades) is a tool to run **individual** Python scripts inside automatically provisioned\nvirtualenvs with their dependencies installed.\n\n- Both [fades](https://github.com/PyAr/fades#how-to-mark-the-dependencies-to-be-installed) and\n    [pipx run](../reference/examples.md#pipx-run-examples) allow specifying a script's dependencies in specially\n    formatted comments, but the exact syntax differs. (pipx's syntax is standardized by a\n    [provisional specification](https://packaging.python.org/en/latest/specifications/inline-script-metadata/), fades's\n    syntax is not standardized.)\n- Both tools automatically set up reusable virtualenvs containing the necessary dependencies.\n- Both can download Python scripts/packages to execute from remote resources.\n- fades can only run individual script files while pipx can also run packages.\n\nExample interaction: None.\n\n## pipx vs pae/pactivate\n\n_pae_ is a Bash command-line function distributed with [pactivate](https://github.com/cynic-net/pactivate) that uses\npactivate to create non-ephemeral environments focused on general use, rather than just running command-line\napplications.\n\nThere is [a very detailed comparison here](https://github.com/cynic-net/pactivate/blob/main/doc/vs-pipx.md), but to\nbriefly summarize:\n\nSimilarities:\n\n- Both create isolated environments without having to specify (and remember) a directory in which to store them.\n- Both allow you to use any Python interpreter available on your system (subject to version restrictions below).\n\npae advantages:\n\n- Supports all versions of Python from 2.7 upward. pipx requires ≥3.9.\n- Fewer dependencies. (See the detailed comparison for more information.)\n- Easier to have multiple versions of a single program and/or use different Python versions for a single program.\n- Somewhat more convenient for running arbitrary command-line programs in virtual environments, installing multiple\n    packages in a single environment, and activating virtual environments.\n- Integrates well with source code repos using [pactivate](https://github.com/cynic-net/pactivate).\n\npae disadvantages:\n\n- Usable with Bash shell only.\n- Slightly less quick and convenient for installing/running command-line programs from single Python packages.\n- Can be slower than pipx at creating virtual environments.\n\nExample interaction: None. Either one or the other should be used. These tools compete for a similar workflow.\n"
  },
  {
    "path": "docs/explanation/how-pipx-works.md",
    "content": "## How it Works\n\n### `pipx install`\n\nWhen installing a package and its binaries on Linux (`pipx install package`), pipx first creates or reuses a shared\nvirtual environment at `~/.local/share/pipx/shared/` that contains the packaging library `pip`, ensuring it is updated\nto its latest version. It then creates an isolated virtual environment at `~/.local/share/pipx/venvs/PACKAGE` that uses\nthe shared pip via a [.pth file](https://docs.python.org/3/library/site.html) and installs the desired package into it.\n\nOnce the package is installed, pipx exposes its console scripts and GUI scripts by symlinking them into `~/.local/bin`\n(for example, `~/.local/bin/black` -> `~/.local/share/pipx/venvs/black/bin/black`). It also symlinks any manual pages\ninto `~/.local/share/man/man[1-9]`. As long as `~/.local/bin/` is on your `PATH`, the new commands are available\nglobally, and on systems with `man` support the pages are accessible too.\n\nAdding the `--global` flag to any `pipx` command executes the action in global scope, exposing the app to all system\nusers. See the [configuration reference](../how-to/configure-paths.md#-global-argument) for details. Note that this is\nnot available on Windows.\n\n```mermaid\nflowchart LR\n    A[\"pipx install black\"] --> B[\"shared venv<br/>(pip)\"]\n    B --> C[\"create venv<br/>~/.local/share/pipx/<br/>venvs/black/\"]\n    C --> D[\"pip install black\"]\n    D --> E[\"symlink binaries<br/>~/.local/bin/black\"]\n    D --> F[\"symlink man pages<br/>~/.local/share/man/\"]\n\n    style A fill:#3f72af,color:#fff\n    style B fill:#2a9d8f,color:#fff\n    style C fill:#2a9d8f,color:#fff\n    style D fill:#2a9d8f,color:#fff\n    style E fill:#388e3c,color:#fff\n    style F fill:#388e3c,color:#fff\n```\n\n### `pipx run`\n\n`pipx run BINARY` reuses the same shared pip environment, then either reuses a cached virtual environment or creates a\nnew temporary one. The cache key is a hash of the package name, spec, python version, and pip arguments. pipx creates a\nvirtual environment with `python -m venv`, installs the package, and invokes the binary.\n\nCached environments expire after a few days. On next run, pipx fetches the latest version.\n\n```mermaid\nflowchart LR\n    A[\"pipx run pycowsay\"] --> B[\"shared venv<br/>(pip)\"]\n    B --> C{\"cached<br/>venv?\"}\n    C -- \"yes\" --> E[\"reuse cached venv\"]\n    C -- \"no\" --> D[\"create temp venv<br/>pip install pycowsay\"]\n    D --> F[\"invoke binary\"]\n    E --> F\n\n    style A fill:#3f72af,color:#fff\n    style B fill:#2a9d8f,color:#fff\n    style C fill:#c78c20,color:#fff\n    style D fill:#2a9d8f,color:#fff\n    style E fill:#2a9d8f,color:#fff\n    style F fill:#388e3c,color:#fff\n```\n\n### Directory layout\n\nThe overall directory structure that pipx manages looks like this:\n\n```mermaid\nflowchart TD\n    HOME[\"~\"] --> BIN[\"~/.local/bin/<br/>(on PATH)\"]\n    HOME --> DATA[\"~/.local/share/pipx/\"]\n    DATA --> SHARED[\"shared/<br/>(pip, setuptools)\"]\n    DATA --> VENVS[\"venvs/\"]\n    VENVS --> V1[\"black/\"]\n    VENVS --> V2[\"poetry/\"]\n    VENVS --> V3[\"ruff/\"]\n    V1 --> V1BIN[\"bin/black\"]\n    BIN --> |\"symlink\"| V1BIN\n\n    style HOME fill:#3f72af,color:#fff\n    style BIN fill:#388e3c,color:#fff\n    style DATA fill:#2a9d8f,color:#fff\n    style SHARED fill:#c78c20,color:#fff\n    style VENVS fill:#2a9d8f,color:#fff\n    style V1 fill:#7c4dff,color:#fff\n    style V2 fill:#7c4dff,color:#fff\n    style V3 fill:#7c4dff,color:#fff\n    style V1BIN fill:#7c4dff,color:#fff\n```\n\nYou can do all of this yourself. pipx automates it. Pass `--verbose` to see every command and argument pipx runs.\n"
  },
  {
    "path": "docs/explanation/index.md",
    "content": "# Explanation\n\nDesign rationale and background knowledge. Read these to understand *why* pipx works the way it does.\n\n- [How pipx Works](how-pipx-works.md) — virtual environment architecture and the shared pip library.\n- [Comparisons](comparisons.md) — how pipx relates to pip, poetry, pipenv, pyenv, brew, npx, and more.\n- [Making Packages Compatible](making-packages-compatible.md) — adding entry points and man pages for pipx support.\n"
  },
  {
    "path": "docs/explanation/making-packages-compatible.md",
    "content": "## Developing for pipx\n\nIf you are a developer and want to be able to run\n\n```\npipx install MY_PACKAGE\n```\n\nmake sure you include `scripts` in your main table[^1] in `pyproject.toml` or its legacy equivalents for `setup.cfg` and\n`setup.py`. pipx also exposes `gui-scripts` entry points, which are useful for GUI applications on Windows (they launch\nwithout opening a console window).\n\n=== \"pyproject.toml\"\n\n    ```ini\n    [project.scripts]\n    foo = \"my_package.some_module:main_func\"\n    bar = \"other_module:some_func\"\n\n    [project.gui-scripts]\n    baz = \"my_package_gui:start_func\"\n    ```\n\n=== \"setup.cfg\"\n\n    ```ini\n    [options.entry_points]\n    console_scripts =\n        foo = my_package.some_module:main_func\n        bar = other_module:some_func\n    gui_scripts =\n        baz = my_package_gui:start_func\n    ```\n\n=== \"setup.py\"\n\n    ```python\n    setup(\n        # other arguments here...\n        entry_points={\n            'console_scripts': [\n                'foo = my_package.some_module:main_func',\n                'bar = other_module:some_func',\n            ],\n            'gui_scripts': [\n                'baz = my_package_gui:start_func',\n            ]\n        },\n    )\n    ```\n\nIn this case `foo` and `bar` (and `baz` on Windows) would be available as \"applications\" to pipx after installing the\nabove example package, invoking their corresponding entry point functions.\n\n```mermaid\nflowchart LR\n    TOML[\"pyproject.toml<br/>[project.scripts]\"] --> |\"defines\"| EP[\"entry points<br/>foo, bar\"]\n    EP --> |\"pipx install\"| VENV[\"isolated venv\"]\n    VENV --> |\"symlinks\"| BIN[\"~/.local/bin/<br/>foo, bar\"]\n    BIN --> |\"invokes\"| FUNC[\"my_package:<br/>main_func()\"]\n\n    style TOML fill:#3f72af,color:#fff\n    style EP fill:#2a9d8f,color:#fff\n    style VENV fill:#7c4dff,color:#fff\n    style BIN fill:#388e3c,color:#fff\n    style FUNC fill:#c78c20,color:#fff\n```\n\n### The `pipx.run` entry point group\n\nWhen a user runs `pipx run PACKAGE`, pipx looks for a console script matching the package name. If the package name and\nscript name differ, the user has to write `pipx run --spec PACKAGE SCRIPT`, which is less convenient.\n\nPackage authors can declare a `pipx.run` entry point group to tell pipx which function to invoke for `pipx run`. This\nentry point takes priority over console scripts when present.\n\n=== \"pyproject.toml\"\n\n    ```toml\n    [project.entry-points.\"pipx.run\"]\n    my-package = \"my_package.cli:main\"\n    ```\n\n=== \"setup.cfg\"\n\n    ```ini\n    [options.entry_points]\n    pipx.run =\n        my-package = my_package.cli:main\n    ```\n\nWith this declaration, `pipx run my-package` invokes `my_package.cli:main` even if no console script named `my-package`\nexists. The [build](https://github.com/pypa/build) package uses this pattern so that `pipx run build` works even though\nbuild's console script is named `pyproject-build`.\n\n### Manual pages\n\nIf you wish to provide documentation via `man` pages on UNIX-like systems then these can be added as data files:\n\n=== \"setuptools\"\n\n    ```toml title=\"pyproject.toml\"\n    [tool.setuptools.data-files]\n    \"share/man/man1\" = [\n      \"manpage.1\",\n    ]\n    ```\n\n    ```ini title=\"setup.cfg\"\n    [options.data_files]\n    share/man/man1 =\n        manpage.1\n    ```\n\n    ```python title=\"setup.py\"\n    setup(\n        # other arguments here...\n        data_files=[('share/man/man1', ['manpage.1'])]\n    )\n    ```\n\n    > [!WARNING]\n    > The `data-files` keyword is \"discouraged\" in the\n    > [setuptools documentation](https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration)\n    > but there is no alternative if `man` pages are a requirement.\n\n=== \"pdm-backend\"\n\n    ```toml title=\"pyproject.toml\"\n    [tool.pdm.build]\n    source-includes = [\"share\"]\n\n    [tool.pdm.build.wheel-data]\n    data = [\n      { path = \"share/man/man1/*\", relative-to = \".\" },\n    ]\n    ```\n\nIn this case the manual page `manpage.1` could be accessed by the user after installing the above example package.\n\nFor a real-world example, see [pycowsay](https://github.com/cs01/pycowsay/blob/master/setup.py)'s `setup.py` source\ncode.\n\nYou can read more about entry points\n[here](https://setuptools.pypa.io/en/latest/userguide/quickstart.html#entry-points-and-automatic-script-creation).\n\n[^1]: This is often the `[project]` table, but might also be differently named. Read more in the\n    [PyPUG](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#writing-your-pyproject-toml).\n"
  },
  {
    "path": "docs/how-to/configure-paths.md",
    "content": "## Installation Options\n\nThe default binary location for pipx-installed apps is `~/.local/bin`. This can be overridden with the environment\nvariable `PIPX_BIN_DIR`. The default manual page location for pipx-installed apps is `~/.local/share/man`. This can be\noverridden with the environment variable `PIPX_MAN_DIR`. If the `--global` option is used, the default locations are\n`/usr/local/bin` and `/usr/local/share/man` respectively and can be overridden with `PIPX_GLOBAL_BIN_DIR` and\n`PIPX_GLOBAL_MAN_DIR`.\n\npipx's default virtual environment location is typically `~/.local/share/pipx` on Linux/Unix, `~/.local/pipx` on macOS\nand `~\\pipx` on Windows. For compatibility reasons, if `~/.local/pipx` on Linux, `%USERPROFILE%\\AppData\\Local\\pipx` or\n`~\\.local\\pipx` on Windows or `~/Library/Application Support/pipx` on macOS exists, it will be used as the default\nlocation instead. This can be overridden with the `PIPX_HOME` environment variable. If the `--global` option is used,\nthe default location is always `/opt/pipx` and can be overridden with `PIPX_GLOBAL_HOME`.\n\nIn case one of these fallback locations exist, we recommend either manually moving the pipx files to the new default\nlocation (see the [Moving your pipx installation](move-installation.md) section of the docs), or setting the `PIPX_HOME`\nenvironment variable (discarding files existing in the fallback location).\n\nAs an example, you can install global apps accessible by all users on your system with either of the following commands\n(on MacOS, Linux, and Windows WSL):\n\n```\nsudo PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin PIPX_MAN_DIR=/usr/local/share/man pipx install <PACKAGE>\n# or shorter (with pipx>=1.5.0)\nsudo pipx install --global <PACKAGE>\n```\n\n> [!NOTE]\n> After version 1.2.0, the default pipx paths have been moved from `~/.local/pipx` to specific user data directories on\n> each platform using [platformdirs](https://pypi.org/project/platformdirs/) library\n>\n> | Old Path               | New Path                                   |\n> | ---------------------- | ------------------------------------------ |\n> | `~/.local/pipx/.trash` | `platformdirs.user_data_dir()/pipx/trash`  |\n> | `~/.local/pipx/shared` | `platformdirs.user_data_dir()/pipx/shared` |\n> | `~/.local/pipx/venvs`  | `platformdirs.user_data_dir()/pipx/venvs`  |\n> | `~/.local/pipx/.cache` | `platformdirs.user_cache_dir()/pipx`       |\n> | `~/.local/pipx/logs`   | `platformdirs.user_log_dir()/pipx/log`     |\n>\n> `user_data_dir()`, `user_cache_dir()` and `user_log_dir()` resolve to appropriate platform-specific user data, cache\n> and log directories. See the\n> [platformdirs documentation](https://platformdirs.readthedocs.io/en/latest/api.html#platforms) for details.\n>\n> This was reverted in 1.5.0 for Windows and macOS. We heavily recommend not using these locations on Windows and macOS\n> anymore, due to multiple incompatibilities discovered with these locations, documented\n> [here](troubleshoot.md#why-are-spaces-in-the-pipx_home-path-bad).\n\n### Customising your installation\n\n#### `--global` argument\n\nThe `--global` flag installs applications into a system-wide location accessible to all users. It must be placed\n**after** the subcommand, not before it:\n\n```\n# correct\nsudo pipx install --global pycowsay\nsudo pipx list --global\n\n# wrong (--global is silently ignored when placed before the subcommand)\nsudo pipx --global install pycowsay\n```\n\nDefault global paths are `/usr/local/bin` for binaries, `/usr/local/share/man` for man pages, and `/opt/pipx` for\nvirtual environments. Override them with `PIPX_GLOBAL_BIN_DIR`, `PIPX_GLOBAL_MAN_DIR`, and `PIPX_GLOBAL_HOME`. Run\n`sudo pipx ensurepath --global` to add the global binary directory to the system `PATH`.\n\nThe `--global` flag is not supported on Windows.\n\n#### `--prepend` argument\n\nThe `--prepend` argument can be passed to the `pipx ensurepath` command to prepend the `pipx` bin directory to the\nuser's PATH environment variable instead of appending to it. This can be useful if you want to prioritise\n`pipx`-installed binaries over system-installed binaries of the same name.\n\n### Configuring pip for pipx\n\npipx uses pip internally for all package installs, including its own shared libraries (pip, setuptools). To point pip at\na private index or pass custom options, set `PIP_*` environment variables. pipx forwards them to every pip invocation.\n\nFor example, to use a private package index:\n\n```\nexport PIP_INDEX_URL=https://my-private-index.example.com/simple/\nexport PIP_TRUSTED_HOST=my-private-index.example.com\npipx install my-private-package\n```\n\nThese variables also apply when pipx upgrades its shared libraries (`pipx upgrade-shared`). You can set them permanently\nin your shell profile or in pip's own config file (`pip.conf` / `pip.ini`). See the\n[pip configuration documentation](https://pip.pypa.io/en/stable/topics/configuration/) for details.\n\nPer-command pip options can be passed with `--pip-args`:\n\n```\npipx install my-package --pip-args='--no-cache-dir --trusted-host=my-host'\n```\n"
  },
  {
    "path": "docs/how-to/index.md",
    "content": "# How-to Guides\n\nRecipes for common pipx tasks. Each guide covers a specific goal and assumes you have pipx installed.\n\n- [Install pipx](install-pipx.md) — system requirements and OS-specific installation steps.\n- [Upgrade pipx](upgrade-pipx.md) — keep pipx itself up to date.\n- [Inject Packages](inject-packages.md) — add extra packages into an existing pipx environment.\n- [Pin Packages](pin-packages.md) — hold a package at its current version.\n- [Run Scripts](run-scripts.md) — run specific versions, from URLs, or from source control.\n- [Use with pre-commit](use-with-pre-commit.md) — integrate pipx with pre-commit hooks.\n- [Configure Paths](configure-paths.md) — customise `PIPX_HOME`, `PIPX_BIN_DIR`, and global installs.\n- [Move Installation](move-installation.md) — relocate your pipx directory.\n- [Shell Completions](shell-completions.md) — enable tab completion for your shell.\n- [Troubleshoot](troubleshoot.md) — fix common problems.\n"
  },
  {
    "path": "docs/how-to/inject-packages.md",
    "content": "## Inject a package\n\n`pipx inject` adds extra packages into an existing pipx-managed virtual environment. If you have `ipython` installed and\nwant `matplotlib` available inside it:\n\n```\npipx inject ipython matplotlib\n```\n\nInject multiple packages at once, from a requirements file, or both:\n\n```\npipx inject ipython matplotlib pandas\npipx inject ipython -r useful-packages.txt\npipx inject ipython extra-pkg -r more-packages.txt\n```\n\n### Expose injected apps\n\nBy default, injected packages do not add their entry points to your `PATH`. Use `--include-apps` to expose them:\n\n```\npipx inject ipython black --include-apps\n```\n\n`--include-deps` exposes entry points from the injected package's dependencies too (implies `--include-apps`).\n\n### Other flags\n\n- `--force` / `-f` reinstalls the package even if it is already injected.\n- `--editable` / `-e` installs the package in editable (development) mode.\n- `--with-suffix SUFFIX` targets a suffixed venv (e.g. `ipython_3.11`).\n- `--pip-args` passes extra arguments to pip (e.g. `--pip-args='--no-cache-dir'`).\n- `--index-url` / `-i` sets the PyPI index URL for this inject.\n- `--system-site-packages` gives the venv access to the system site-packages.\n"
  },
  {
    "path": "docs/how-to/install-pipx.md",
    "content": "## System Requirements\n\npython 3.9+ is required to install pipx. pipx can run binaries from packages with Python 3.3+. Don't have Python 3.9 or\nlater? See [Python 3 Installation & Setup Guide](https://realpython.com/installing-python/).\n\nYou also need to have `pip` installed on your machine for `python3`. The installation process varies from system to\nsystem. Consult [pip's installation instructions](https://pip.pypa.io/en/stable/installing/). Installing on Linux works\nbest with a\n[Linux Package Manager](https://packaging.python.org/guides/installing-using-linux-tools/#installing-pip-setuptools-wheel-with-linux-package-managers).\n\npipx works on macOS, Linux, and Windows.\n\n[![Packaging status](https://repology.org/badge/vertical-allrepos/pipx.svg?columns=3&exclude_unsupported=1)](https://repology.org/metapackage/pipx/versions)\n\n## Installing pipx\n\n### On macOS:\n\n```\nbrew install pipx\npipx ensurepath\n```\n\n#### Additional (optional) commands\n\nTo allow pipx actions in global scope (requires pipx 1.5.0+):\n\n```\nsudo pipx ensurepath --global\n```\n\nTo prepend the pipx bin directory to PATH instead of appending it (requires pipx 1.7.0+):\n\n```\nsudo pipx ensurepath --prepend\n```\n\n> [!NOTE]\n> Some distributions ship older pipx versions (e.g. Ubuntu 24.04 ships v1.4.3). If `--global` or `--prepend` fails with\n> \"unrecognized arguments\", upgrade pipx first: `pip install --user --upgrade pipx`, or install a newer version from a\n> different source.\n\nFor more details, refer to [Customising your installation](configure-paths.md).\n\n### On Linux:\n\n- Ubuntu 23.04 or above\n\n```\nsudo apt update\nsudo apt install pipx\npipx ensurepath\n```\n\n- Fedora:\n\n```\nsudo dnf install pipx\npipx ensurepath\n```\n\n- Using `pip` on other distributions:\n\n```\npython3 -m pip install --user pipx\npython3 -m pipx ensurepath\n```\n\n> [!NOTE]\n> Distributions that adopt [PEP 668](https://peps.python.org/pep-0668/) (Ubuntu 23.04+, Debian 12+, Fedora 38+) mark the\n> system Python as externally managed. Running `pip install --user` on these systems fails with an\n> `externally-managed-environment` error. Use your distribution's package manager (`apt install pipx`,\n> `dnf install pipx`) instead. If no distro package exists, install pipx inside its own virtual environment:\n>\n> ```\n> python3 -m venv ~/.local/share/pipx-venv\n> ~/.local/share/pipx-venv/bin/pip install pipx\n> ln -s ~/.local/share/pipx-venv/bin/pipx ~/.local/bin/pipx\n> pipx ensurepath\n> ```\n\n#### Additional (optional) commands\n\nTo allow pipx actions in global scope (requires pipx 1.5.0+):\n\n```\nsudo pipx ensurepath --global\n```\n\nTo prepend the pipx bin directory to PATH instead of appending it (requires pipx 1.7.0+):\n\n```\nsudo pipx ensurepath --prepend\n```\n\nFor more details, refer to [Customising your installation](configure-paths.md).\n\n> [!NOTE]\n> If you installed pipx with `pip install --user`, the `pipx` binary lives in your user directory (e.g.\n> `~/.local/bin/pipx`). Running `sudo pipx` will fail because root's `PATH` does not include your user bin directory.\n> Use the full path instead: `sudo ~/.local/bin/pipx ensurepath --global`. Alternatively, install pipx system-wide with\n> `sudo pip install pipx` (without `--user`) or use your distribution's package manager.\n\n### On Windows:\n\n- Install via [Scoop](https://scoop.sh/):\n\n```\nscoop install pipx\npipx ensurepath\n```\n\n- Install via pip (requires pip 19.0 or later)\n\n```\n# If you installed python using Microsoft Store, replace `py` with `python3` in the next line.\npy -m pip install --user pipx\n```\n\nIt is possible (even most likely) the above finishes with a WARNING looking similar to this:\n\n```\nWARNING: The script pipx.exe is installed in `<USER folder>\\AppData\\Roaming\\Python\\Python3x\\Scripts` which is not on PATH\n```\n\nIf so, go to the mentioned folder, allowing you to run the pipx executable directly. Enter the following line (even if\nyou did not get the warning):\n\n```\n.\\pipx.exe ensurepath\n```\n\nThis will add both the above mentioned path and the `%USERPROFILE%\\.local\\bin` folder to your search path. Restart your\nterminal session and verify `pipx` does run.\n\n### On FreeBSD:\n\n- Install via package manager\n\n```sh\npkg install -y py311-pipx\n```\n\n- Install via pip\n\n```sh\npip install --user pipx\npipx ensurepath\n```\n\n### Using pipx without installing (via zipapp)\n\nThe zipapp can be downloaded from [Github releases](https://github.com/pypa/pipx/releases) and you can invoke it with a\nPython 3.9+ interpreter:\n\n```\npython pipx.pyz ensurepath\n```\n\n### Self-managed pipx\n\nYou can use pipx to manage its own installation. This keeps pipx up to date through `pipx upgrade pipx` and avoids\nrelying on distro packages that may ship older versions. Bootstrap it with a temporary virtual environment:\n\n```\npython3 -m venv /tmp/bootstrap\n/tmp/bootstrap/bin/pip install pipx\n/tmp/bootstrap/bin/pipx install pipx\nrm -rf /tmp/bootstrap\npipx ensurepath\n```\n\nAfter this, `pipx upgrade pipx` upgrades pipx like any other pipx-managed application. On Windows, pipx cannot delete\nits own running executable, so it moves locked files to a trash directory and cleans them up on the next run.\n\n## Installing packages from source control\n\npipx accepts any source pip supports, including git repositories. Using `black` as an example:\n\n```\npipx install git+https://github.com/psf/black.git\npipx install git+ssh://git@github.com/psf/black # using ssh\npipx install git+https://github.com/psf/black.git@branch  # branch of your choice\npipx install git+https://github.com/psf/black.git@ce14fa8b497bae2b50ec48b3bd7022573a59cdb1  # git hash\npipx install https://github.com/psf/black/archive/18.9b0.zip  # install a release\n```\n\nUse pip's `egg` syntax when installing extras:\n\n```\npipx install \"git+https://github.com/psf/black.git#egg=black[jupyter]\"\n```\n\n### Installing from a pull request\n\nTo test a package from an open pull request, find the fork owner and branch name on the PR page, then build the git URL.\nFor example, PR #794 from user `contributor` on branch `fix-something`:\n\n```\npipx install git+https://github.com/contributor/pipx.git@fix-something\n```\n\nIf the PR branch has been merged, use the merge commit hash instead:\n\n```\npipx install git+https://github.com/pypa/pipx.git@abc123def\n```\n"
  },
  {
    "path": "docs/how-to/move-installation.md",
    "content": "## Moving your pipx installation\n\nThe below code snippets show how to move your pipx installation to a new directory. As an example, they move from a\nnon-default location to the current default locations. If you wish to move to a different location, just replace the\n`NEW_LOCATION` value.\n\n### MacOS\n\nCurrent default location: `~/.local`\n\n```bash\nNEW_LOCATION=~/.local\ncache_dir=$(pipx environment --value PIPX_VENV_CACHEDIR)\nlogs_dir=$(pipx environment --value PIPX_LOG_DIR)\ntrash_dir=$(pipx environment --value PIPX_TRASH_DIR)\nhome_dir=$(pipx environment --value PIPX_HOME)\nrm -rf \"$cache_dir\" \"$logs_dir\" \"$trash_dir\"\nmkdir -p $NEW_LOCATION && mv \"$home_dir\" $NEW_LOCATION\npipx reinstall-all\n```\n\n### Linux\n\nCurrent default location: `~/.local/share`\n\n```bash\ncache_dir=$(pipx environment --value PIPX_VENV_CACHEDIR)\nlogs_dir=$(pipx environment --value PIPX_LOG_DIR)\ntrash_dir=$(pipx environment --value PIPX_TRASH_DIR)\nhome_dir=$(pipx environment --value PIPX_HOME)\n# If you wish another location, replace the expression below\n# and set `NEW_LOCATION` explicitly\nNEW_LOCATION=\"${XDG_DATA_HOME:-$HOME/.local/share}\"\nrm -rf \"$cache_dir\" \"$logs_dir\" \"$trash_dir\"\nmkdir -p $NEW_LOCATION && mv \"$home_dir\" $NEW_LOCATION\npipx reinstall-all\n```\n\n### Windows\n\nCurrent default location: `~/pipx`\n\n```powershell\n$NEW_LOCATION = Join-Path \"$HOME\" 'pipx'\n$cache_dir = pipx environment --value PIPX_VENV_CACHEDIR\n$logs_dir = pipx environment --value PIPX_LOG_DIR\n$trash_dir = pipx environment --value PIPX_TRASH_DIR\n$home_dir = pipx environment --value PIPX_HOME\n\nRemove-Item -Recurse -Force -ErrorAction SilentlyContinue \"$cache_dir\", \"$logs_dir\", \"$trash_dir\"\n\n# Remove the destination directory to ensure rename behavior of `Move-Item`\nRemove-Item -Recurse -Force -ErrorAction SilentlyContinue \"$NEW_LOCATION\"\n\nMove-Item -Path $home_dir -Destination \"$NEW_LOCATION\"\npipx reinstall-all\n```\n\nIf you would prefer doing it in bash via git-bash/WSL, feel free to use the MacOS/Linux instructions, changing the\n`$NEW_LOCATION` to the Windows version.\n"
  },
  {
    "path": "docs/how-to/pin-packages.md",
    "content": "## Pin installed packages\n\nUse `pipx pin` when you need to hold an installation at its current version. Pinned packages are skipped by\n`pipx upgrade`, `pipx upgrade-all`, and `pipx reinstall`, so the environment keeps its existing app and dependency\nversions until you unpin it.\n\n- `pipx pin PACKAGE` — pins the main package and any injected packages in that virtual environment.\n- `pipx pin PACKAGE --injected-only` — leaves the main package upgradable but pins every injected package instead.\n- `pipx pin PACKAGE --skip PKG_A PKG_B` — pins injected packages except the ones you list (the flag implies\n    `--injected-only`).\n- `pipx unpin PACKAGE` — re-enables upgrades for the package and anything that was pinned with it.\n- `pipx list --pinned` — shows every pinned environment; add `--include-injected` to see pinned injected packages.\n\npipx tracks the main package and any injected packages. It does not record individual transitive dependencies, so there\nis no way to pin a single dependency in isolation. Pinning the main package protects its dependency set because pipx\nskips running `pip install --upgrade` for that environment.\n"
  },
  {
    "path": "docs/how-to/run-scripts.md",
    "content": "## Running a specific version of a package\n\nThe `PACKAGE` argument is a\n[requirement specifier](https://packaging.python.org/en/latest/glossary/#term-Requirement-Specifier), so you can pin\nversions, ranges, or extras:\n\n```\npipx run mpremote==1.20.0\npipx run --spec esptool==4.6.2 esptool.py\npipx run --spec 'esptool>=4.5' esptool.py\n```\n\nRequirement specifiers containing `>`, `<`, or spaces need quoting.\n\n## Running with extra dependencies\n\n`--with` adds packages to the temporary environment alongside the main app:\n\n```\npipx run --with requests --with rich my-script.py\n```\n\n## Running from source control\n\n`pipx run` accepts git URLs through `--spec`. Using `black` as an example:\n\n```\npipx run --spec git+https://github.com/psf/black.git black\npipx run --spec git+ssh://git@github.com/psf/black black\npipx run --spec git+https://github.com/psf/black.git@branch black\npipx run --spec git+https://github.com/psf/black.git@ce14fa8b497bae2b50ec48b3bd7022573a59cdb1 black\npipx run --spec https://github.com/psf/black/archive/18.9b0.zip black\n```\n\n## Running from URL\n\nYou can run `.py` files hosted anywhere:\n\n```\npipx run https://gist.githubusercontent.com/cs01/fa721a17a326e551ede048c5088f9e0f/raw/6bdfbb6e9c1132b1c38fdd2f195d4a24c540c324/pipx-demo.py\n```\n\n## Running scripts with dependencies (PEP 723)\n\nScripts can declare their own dependencies using\n[inline script metadata](https://packaging.python.org/en/latest/specifications/inline-script-metadata/). pipx reads the\n`# /// script` block and installs the listed packages before running:\n\n```python\n# test.py\n\n# /// script\n# dependencies = [\"requests\"]\n# ///\n\nimport sys\nimport requests\nproject = sys.argv[1]\ndata = requests.get(f\"https://pypi.org/pypi/{project}/json\").json()\nprint(data[\"info\"][\"version\"])\n```\n\n```\n> pipx run test.py pipx\n1.9.0\n```\n\npipx creates a cached virtual environment keyed to the script's dependency list. Changing the dependencies creates a\nfresh environment.\n"
  },
  {
    "path": "docs/how-to/shell-completions.md",
    "content": "## Shell Completion\n\nYou can easily get your shell's tab completions working by following instructions printed with this command:\n\n```\npipx completions\n```\n"
  },
  {
    "path": "docs/how-to/troubleshoot.md",
    "content": "## Wrong package version installed\n\npipx creates venvs using your default Python interpreter. pip resolves the latest package version compatible with that\ninterpreter. If a package drops support for your Python version, pip installs an older release without warning.\n\nCheck which Python pipx uses with `pipx environment --value PIPX_DEFAULT_PYTHON`. To install with a different Python,\npass `--python`:\n\n```\npipx install my-package --python python3.12\n```\n\nIf the desired Python version is not installed, add `--fetch-missing-python` and pipx downloads a standalone build:\n\n```\npipx install my-package --python 3.13 --fetch-missing-python\n```\n\n## `reinstall-all` fixes most issues\n\nThe following command should fix many problems you may encounter as a pipx user:\n\n```\npipx reinstall-all\n```\n\nThis is a good fix for the following problems:\n\n- System python was upgraded and the python used with a pipx-installed package is no longer available\n- pipx upgrade causes issues with old pipx-installed packages\n\npipx has been upgraded a lot over the years. If you are a long-standing pipx user (thanks, by the way!) then you may\nhave old pipx-installed packages that have internal data that is different than what pipx currently expects. By\nexecuting `pipx reinstall-all`, pipx will re-write its internal data and this should fix many issues you may encounter.\n\n**Note:** If your pipx-installed package was installed using a pipx version before 0.15.0.0 and you want to specify\nparticular options, then you may want to uninstall and install it manually:\n\n```\npipx uninstall <mypackage>\npipx install <mypackage>\n```\n\n## Diagnosing problems using `list`\n\n```\npipx list\n```\n\nwill not only list all of your pipx-installed packages, but can also diagnose some problems with them, as well as\nsuggest solutions.\n\n## Specifying pipx options\n\nThe most reliable method to specify command-line options that require an argument is to use an `=`-sign. An example:\n\n```\npipx install pycowsay --pip-args=\"--no-cache-dir\"\n```\n\nAnother example for ignoring ssl/tls errors:\n\n```\npipx install termpair --pip-args '--trusted-host files.pythonhosted.org --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host github.com'\"\n```\n\n## Check for `PIP_*` environment variables\n\npipx uses `pip` to install and manage packages. If you see pipx exhibiting strange behavior on install or upgrade, check\nthat you don't have special environment variables that affect `pip`'s behavior in your environment.\n\nTo check for `pip` environment variables, execute the following depending on your system:\n\n### Unix or macOS\n\n```\nenv | grep '^PIP_'\n```\n\n### Windows PowerShell\n\n```\nls env:PIP_*\n```\n\n### Windows `cmd`\n\n```\nset PIP_\n```\n\nReference: [pip Environment Variables](https://pip.pypa.io/en/stable/user_guide/#environment-variables)\n\n## `pipx` log files\n\npipx records a verbose log file for every `pipx` command invocation. The logs for the last 10 `pipx` commands can be\nfound in `$XDG_STATE_HOME/pipx/logs` or user's log path if the former is not writable by the user.\n\nFor most users this location is `~/.local/state/pipx/logs`, where `~` is your home directory.\n\n## `sudo pipx` not found\n\nIf you installed pipx with `pip install --user`, the binary lives in your user directory (e.g. `~/.local/bin/pipx`).\nRoot's `PATH` does not include that directory, so `sudo pipx` fails with \"command not found\". Use the full path instead:\n\n```\nsudo ~/.local/bin/pipx ensurepath --global\n```\n\nTo avoid this, install pipx through your distribution's package manager (`apt install pipx`, `dnf install pipx`) or\ninstall it system-wide with `sudo pip install pipx` (without `--user`).\n\n## Debian, Ubuntu issues\n\nIf you have issues using pipx on Debian, Ubuntu, or other Debian-based linux distributions, make sure you have the\nfollowing packages installed on your system. (Debian systems do not install these by default with their python\ninstallations.)\n\n```\nsudo apt install python3-venv python3-pip\n```\n\nReference:\n[Python Packaging User Guide: Installing pip/setuptools/wheel with Linux Package Managers](https://packaging.python.org/guides/installing-using-linux-tools)\n\n## macOS issues\n\nIf you want to use a pipx-installed package in a shebang (a common example is the AWS CLI), you will likely not be able\nto, because the binary will be stored under `~/Library/Application Support/pipx/`. The space in the path is not\nsupported in a shebang. A simple solution is symlinking `~/Library/Application Support/pipx` to\n`~/Library/ApplicationSupport/pipx`, and using that as the path in the shebang instead.\n\n```\nmkdir $HOME/Library/ApplicationSupport\nln -s $HOME/Library/Application\\ Support/pipx $HOME/Library/ApplicationSupport/pipx\n```\n\n## Does it work to install your package with `pip`?\n\nThis is a tip for advanced users. An easy way to check if pipx is the problem or a package you're trying to install is\nthe problem, is to try installing it using `pip`. For example:\n\n### Unix or macOS\n\n```\npython3 -m venv test_venv\ntest_venv/bin/python3 -m pip install <problem-package>\n```\n\n### Windows\n\n```\npython -m venv test_venv\ntest_venv/Scripts/python -m pip install <problem-package>\n```\n\nIf installation into this \"virtual environment\" using pip fails, then it's likely that the problem is with the package\nor your host system.\n\nTo clean up after this experiment:\n\n```\nrm -rf test_venv\n```\n\n## pipx files not in expected locations according to documentation\n\npipx versions after 1.2.0 adopt the XDG base directory specification for the location of `PIPX_HOME` and the data,\ncache, and log directories. Version 1.2.0 and earlier use `~/.local/pipx` as the default `PIPX_HOME` and install the\ndata, cache, and log directories under it. To maintain compatibility with older versions, pipx will automatically use\nthis old `PIPX_HOME` path if it exists. For a map of old and new paths, see [Installation Options](configure-paths.md).\n\nIn pipx version 1.5.0, this was reverted for Windows and macOS. It defaults again to `~/.local/pipx` on macOS and to\n`~\\pipx` on Windows.\n\nIf you have a `pipx` version later than 1.2.0 and want to migrate from the old path to the new paths, you can move the\n`~/.local/pipx` directory to the new location (after removing cache, log, and trash directories which will get recreated\nautomatically) and then reinstall all packages.\n\nPlease refer to [Moving your pipx installation](move-installation.md) on how to move it.\n\n## Warning: Found a space in the pipx home path\n\nIn pipx version 1.5, we introduced the warning you're seeing, as multiple incompatibilities with spaces in the pipx home\npath were discovered. You may see this for the following reasons:\n\n1. From pipx version 1.3 to 1.5, we were by default using a path with a space on it on macOS. This unfortunately means\n    that all users who installed pipx in this time frame and were using the default behavior are seeing this warning\n    now.\n1. You set your `PIPX_HOME` to a path with spaces in it explicitly or because your `$HOME` path contains a space.\n\n### Why are spaces in the `PIPX_HOME` path bad\n\nThe main reason we can't support paths with spaces is that shebangs don't support spaces in the interpreter path. All\napplications installed via `pipx` are installed via `pip`, which creates a script with a shebang at the top, defining\nthe interpreter of the `venv` to use.\n\n`pip` does some magic to the shebang for scripts defined as a `script`, that resolves this issue. Unfortunately, many\nlibraries define their scripts as `console_scripts`, where `pip` does not perform this logic. Therefore, these scripts\ncannot be run if installed with `pipx` in a path with spaces because the path to the `venv` (and therefore the\ninterpreter) will contain spaces.\n\nIf you want to use a script installed via pipx in a shebang itself (common for example for the AWS CLI), you run into a\nsimilar problem, as the path to the installed script will contain a space.\n\n### How to fix\n\nYou can generally fix this by using our default locations, as long as your `$HOME` path does not contain spaces. Please\nrefer to our [Moving your pipx installation](move-installation.md) docs on how to move the `pipx` installation.\n\nIf you're really sure you want to stick to your path with spaces, to suppress the warning set the\n`PIPX_HOME_ALLOW_SPACE` environment variable to `true`.\n"
  },
  {
    "path": "docs/how-to/upgrade-pipx.md",
    "content": "## Upgrade pipx\n\nOn macOS:\n\n```\nbrew update && brew upgrade pipx\n```\n\nOn Ubuntu Linux:\n\n```\nsudo apt upgrade pipx\n```\n\nOn Fedora Linux:\n\n```\nsudo dnf update pipx\n```\n\nOn Windows:\n\n```\nscoop update pipx\n```\n\nOtherwise, upgrade via pip:\n\n```\npython3 -m pip install --user -U pipx\n```\n\n### Note: Upgrading pipx from a pre-0.15.0.0 version to 0.15.0.0 or later\n\nAfter upgrading to pipx 0.15.0.0 or above from a pre-0.15.0.0 version, you must re-install all packages to take\nadvantage of the new persistent pipx metadata files introduced in the 0.15.0.0 release. These metadata files store pip\nspecification values, injected packages, any custom pip arguments, and more in each main package's venv.\n\nIf you have no packages installed using the `--spec` option, and no venvs with injected packages, you can do this by\nrunning `pipx reinstall-all`.\n\nIf you have any packages installed using the `--spec` option or venvs with injected packages, you should reinstall\npackages manually using `pipx uninstall-all`, followed by `pipx install` and possibly `pipx inject`.\n"
  },
  {
    "path": "docs/how-to/use-with-pre-commit.md",
    "content": "## Using pipx with pre-commit\n\npipx has [pre-commit](https://pre-commit.com/) support. This lets you run applications:\n\n- that can be run using `pipx run` but don't have native pre-commit support;\n- using its prebuilt wheel from pypi.org instead of building it from source; and\n- using pipx's `--spec` and `--index-url` flags.\n\nExample configuration for use of the code linter [yapf](https://github.com/google/yapf/). This is to be added to your\n`.pre-commit-config.yaml`.\n\n```yaml\n  - repo: https://github.com/pypa/pipx\n    rev: 1.5.0\n    hooks:\n      - id: pipx\n        alias: yapf\n        name: yapf\n        args: [yapf, -i]\n        types: [python]\n```\n"
  },
  {
    "path": "docs/index.md",
    "content": "<p align=\"center\">\n<a href=\"https://pipx.pypa.io\">\n<img align=\"center\" src=\"https://github.com/pypa/pipx/raw/main/logo.svg\" width=\"200\"/>\n</a>\n</p>\n\n# pipx — Install and Run Python Applications in Isolated Environments\n\n<p align=\"center\">\n<a href=\"https://github.com/pypa/pipx/raw/main/pipx_demo.gif\">\n<img src=\"https://github.com/pypa/pipx/raw/main/pipx_demo.gif\"/>\n</a>\n</p>\n\npipx installs and runs end-user Python applications in isolated environments. It fills the same role as macOS's `brew`,\nJavaScript's [npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b), and Linux's `apt`.\nUnder the hood it uses pip, but unlike pip it creates a separate virtual environment for each application, keeping your\nsystem clean.\n\n## Documentation\n\n- **[Tutorials](tutorial/index.md)** — install your first application and run commands in temporary environments.\n- **[How-to Guides](how-to/index.md)** — recipes for installing pipx, injecting packages, configuring paths, and more.\n- **[Reference](reference/index.md)** — CLI flags, examples, environment variables, and programs to try.\n- **[Explanation](explanation/index.md)** — how pipx works under the hood and how it compares to other tools.\n\n```mermaid\nflowchart LR\n    USER[\"you\"] --> |\"pipx install\"| PIPX[\"pipx\"]\n    USER --> |\"pipx run\"| PIPX\n    PIPX --> |\"fetches from\"| PYPI[\"PyPI\"]\n    PIPX --> |\"creates\"| VENV[\"isolated venvs\"]\n    VENV --> |\"exposes on PATH\"| APPS[\"black, ruff,<br/>poetry, ...\"]\n\n    style USER fill:#3f72af,color:#fff\n    style PIPX fill:#2a9d8f,color:#fff\n    style PYPI fill:#c78c20,color:#fff\n    style VENV fill:#7c4dff,color:#fff\n    style APPS fill:#388e3c,color:#fff\n```\n\n## pip vs pipx\n\npip installs both libraries and applications into your current environment with no isolation. pipx installs only\napplications, each in its own virtual environment, and exposes their commands on your `PATH`. You get clean uninstalls\nand zero dependency conflicts between tools.\n\n## Where do apps come from?\n\npipx pulls packages from [PyPI](https://pypi.org/) by default, but accepts any source pip supports: local directories,\nwheels, and git URLs. Any package that declares\n[console script entry points](https://python-packaging.readthedocs.io/en/latest/command-line-scripts.html#the-console-scripts-entry-point)\nworks with pipx. [Poetry](https://python-poetry.org/docs/pyproject/#scripts) and\n[hatch](https://hatch.pypa.io/latest/config/metadata/#cli) users can add entry points the same way.\n\n## Features\n\npipx lets you\n\n- install CLI apps into isolated environments with `pipx install`, guaranteeing no dependency conflicts and clean\n    uninstalls;\n- list, upgrade, and uninstall packages in one command; and\n- run the latest version of any app in a temporary environment with `pipx run`.\n\npipx runs with regular user permissions and never calls `sudo pip install`.\n\n## Testimonials\n\n> \"Thanks for improving the workflow that pipsi has covered in the past. Nicely done!\"\n>\n> — *[Jannis Leidel](https://twitter.com/jezdez), PSF fellow, former pip and Django core developer, and founder of the\n> Python Packaging Authority (PyPA)*\n\n> \"My setup pieces together pyenv, poetry, and pipx. [...] For the things I need, it's perfect.\"\n>\n> — *[Jacob Kaplan-Moss](https://twitter.com/jacobian), co-creator of Django in blog post\n> [My Python Development Environment, 2020 Edition](https://jacobian.org/2019/nov/11/python-environment-2020/)*\n\n> \"I'm a big fan of pipx. I think pipx is super cool.\"\n>\n> — *[Michael Kennedy](https://twitter.com/mkennedy), co-host of PythonBytes podcast in\n> [episode 139](https://pythonbytes.fm/episodes/transcript/139/f-yes-for-the-f-strings)*\n\n## Credits\n\npipx was inspired by [pipsi](https://github.com/mitsuhiko/pipsi) and [npx](https://github.com/npm/npx). It was created\nby [Chad Smith](https://github.com/cs01/) and has had lots of help from\n[contributors](https://github.com/pypa/pipx/graphs/contributors). The logo was created by\n[@IrishMorales](https://github.com/IrishMorales).\n\npipx is maintained by a team of volunteers (in alphabetical order)\n\n- [Bernát Gábor](https://github.com/gaborbernat)\n- [Chad Smith](https://github.com/cs01) - co-lead maintainer\n- [Chrysle](https://github.com/chrysle)\n- [Jason Lam](https://github.com/dukecat0)\n- [Matthew Clapp](https://github.com/itsayellow) - co-lead maintainer\n- [Robert Offner](https://github.com/gitznik)\n- [Tzu-ping Chung](https://github.com/uranusjr)\n"
  },
  {
    "path": "docs/reference/environment-variables.md",
    "content": "## Environment Variables\n\npipx reads the following environment variables. All are optional.\n\n| Variable                    | Description                                                  | Default                                                                    |\n| --------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------- |\n| `PIPX_HOME`                 | Root directory for pipx virtual environments.                | `~/.local/share/pipx` (Linux), `~/.local/pipx` (macOS), `~\\pipx` (Windows) |\n| `PIPX_BIN_DIR`              | Directory where application entry-point symlinks are placed. | `~/.local/bin`                                                             |\n| `PIPX_MAN_DIR`              | Directory where man page symlinks are placed.                | `~/.local/share/man`                                                       |\n| `PIPX_GLOBAL_HOME`          | Root directory for global (`--global`) virtual environments. | `/opt/pipx`                                                                |\n| `PIPX_GLOBAL_BIN_DIR`       | Binary directory for global installs.                        | `/usr/local/bin`                                                           |\n| `PIPX_GLOBAL_MAN_DIR`       | Man page directory for global installs.                      | `/usr/local/share/man`                                                     |\n| `PIPX_DEFAULT_PYTHON`       | Python interpreter to use when `--python` is not passed.     | `python3` (or `py` on Windows)                                             |\n| `PIPX_SHARED_LIBS`          | Override the shared libraries directory.                     | `PIPX_HOME/shared`                                                         |\n| `PIPX_FETCH_MISSING_PYTHON` | Fetch missing Python versions when `--python` is used.       | _(unset)_                                                                  |\n| `PIPX_HOME_ALLOW_SPACE`     | Set to `true` to suppress the \"space in PIPX_HOME\" warning.  | _(unset)_                                                                  |\n| `PIPX_USE_EMOJI`            | Set to `0` to disable emoji output.                          | `1`                                                                        |\n\n### Notes\n\n`PIPX_HOME` has platform-specific fallback logic. If a legacy directory (e.g. `~/.local/pipx` on Linux) already exists,\npipx uses it instead of the new default. See [Configure Paths](../how-to/configure-paths.md) for details.\n\nStandard `PIP_*` environment variables (e.g. `PIP_INDEX_URL`) are forwarded to pip when pipx invokes it. See\n[Troubleshooting](../how-to/troubleshoot.md#check-for-pip_-environment-variables) if unexpected pip behaviour occurs.\n\nRun `pipx environment` to see the resolved value of every directory variable on your system.\n"
  },
  {
    "path": "docs/reference/examples.md",
    "content": "## `pipx install` examples\n\n```\npipx install pycowsay\npipx install --python python3.10 pycowsay\npipx install --python 3.12 pycowsay\npipx install --fetch-missing-python --python 3.12 pycowsay\npipx install git+https://github.com/psf/black\npipx install git+https://github.com/psf/black.git@branch-name\npipx install git+https://github.com/psf/black.git@git-hash\npipx install git+ssh://<username>@<private-repo-domain>/<path-to-package.git>\npipx install https://github.com/psf/black/archive/18.9b0.zip\npipx install black[d]\npipx install --preinstall ansible-lint --preinstall mitogen ansible-core\npipx install 'black[d] @ git+https://github.com/psf/black.git@branch-name'\npipx install --suffix @branch-name 'black[d] @ git+https://github.com/psf/black.git@branch-name'\npipx install --include-deps jupyter\npipx install --pip-args='--pre' poetry\npipx install --pip-args='--index-url=<private-repo-host>:<private-repo-port> --trusted-host=<private-repo-host>:<private-repo-port>' private-repo-package\npipx install --index-url https://test.pypi.org/simple/ --pip-args='--extra-index-url https://pypi.org/simple/' some-package\npipx install --global pycowsay\npipx install .\npipx install path/to/some-project\n```\n\n## `pipx run` examples\n\npipx enables you to test various combinations of Python versions and package versions in ephemeral environments:\n\n```\npipx run BINARY  # latest version of binary is run with python3\npipx run --spec PACKAGE==2.0.0 BINARY  # specific version of package is run\npipx run --python python3.10 BINARY  # Installed and invoked with specific Python version\npipx run --python python3.9 --spec PACKAGE=1.7.3 BINARY\npipx run --spec git+https://url.git BINARY  # latest version on default branch is run\npipx run --spec git+https://url.git@branch BINARY\npipx run --spec git+https://url.git@hash BINARY\npipx run pycowsay moo\npipx --version  # prints pipx version\npipx run pycowsay --version  # prints pycowsay version\npipx run --python pythonX pycowsay\npipx run pycowsay==2.0 --version\npipx run pycowsay[dev] --version\npipx run --spec git+https://github.com/psf/black.git black\npipx run --spec git+https://github.com/psf/black.git@branch-name black\npipx run --spec git+https://github.com/psf/black.git@git-hash black\npipx run --spec https://github.com/psf/black/archive/18.9b0.zip black --help\npipx run https://gist.githubusercontent.com/cs01/fa721a17a326e551ede048c5088f9e0f/raw/6bdfbb6e9c1132b1c38fdd2f195d4a24c540c324/pipx-demo.py\n```\n\nYou can run local files, or scripts hosted on the internet, and you can run them with arguments:\n\n```\npipx run test.py\npipx run test.py 1 2 3\npipx run https://example.com/test.py\npipx run https://example.com/test.py 1 2 3\n```\n\nA simple filename is ambiguous - it could be a file, or a package on PyPI. It will be treated as a filename if the file\nexists, or as a package if not. To force interpretation as a local path, use `--path`, and to force interpretation as a\npackage name, use `--spec` (with the PyPI name of the package).\n\n```\npipx run myscript.py # Local file, if myscript.py exists\npipx run doesnotexist.py # Package, because doesnotexist.py is not a local file\npipx run --path test.py # Always a local file\npipx run --spec test-py test.py # Always a package on PyPI\n```\n\nYou can also run scripts that have dependencies:\n\nIf you have a script `test.py` that needs 3rd party libraries, you can add\n[inline script metadata](https://packaging.python.org/en/latest/specifications/inline-script-metadata/) in the style of\nPEP 723.\n\n```\n# test.py\n\n# /// script\n# dependencies = [\"requests\"]\n# ///\n\nimport sys\nimport requests\nproject = sys.argv[1]\npipx_data = requests.get(f\"https://pypi.org/pypi/{project}/json\").json()\nprint(pipx_data[\"info\"][\"version\"])\n```\n\nThen you can run it as follows:\n\n```\n> pipx run test.py pipx\n1.1.0\n```\n\n## `pipx inject` example\n\nOne use of the inject command is setting up a REPL with some useful extra packages.\n\n```\n> pipx install ptpython\n> pipx inject ptpython requests pendulum\n```\n\nAfter running the above commands, you will be able to import and use the `requests` and `pendulum` packages inside a\n`ptpython` repl.\n\nEquivalently, the extra packages can be listed in a text file (e.g. `useful-packages.txt`). Each line is a separate\npackage specifier with the same syntax as the command line. Comments are supported with a `#` prefix. Hence, the syntax\nis a strict subset of the pip [requirements file format][pip-requirements] syntax.\n\n```\n# Additional packages\nrequests\n\npendulum # for easier datetimes\n```\n\nThis file can then be given to `pipx inject` on the command line:\n\n```shell\n> pipx inject ptpython --requirement useful-packages.txt\n# or:\n> pipx inject ptpython -r useful-packages.txt\n```\n\nNote that these options can be repeated and used together, e.g.\n\n```\n> pipx inject ptpython package-1 -r extra-packages-1.txt -r extra-packages-2.txt package-2\n```\n\nIf you require full pip functionality, then use the `runpip` command instead; however, the installed packages won't be\nrecognised as \"injected\".\n\n## `pipx list` example\n\n```\n> pipx list\nvenvs are in /Users/user/.local/pipx/venvs\nbinaries are exposed on your $PATH at /Users/user/.local/bin\n   package black 18.9b0, Python 3.10.0\n    - black\n    - blackd\n   package pipx 0.10.0, Python 3.10.0\n    - pipx\n\n> pipx list --short\nblack 18.9b0\npipx 0.10.0\n```\n\n## `pipx install-all` example\n\n```shell\n> pipx list --json > pipx.json\n> pipx install-all pipx.json\n'black' already seems to be installed. Not modifying existing installation in '/usr/local/pipx/venvs/black'. Pass '--force' to force installation.\n'pipx' already seems to be installed. Not modifying existing installation in '/usr/local/pipx/venvs/black'. Pass '--force' to force installation.\n> pipx install-all pipx.json --force\nInstalling to existing venv 'black'\n  installed package black 24.3.0, installed using Python 3.10.12\n  These apps are now globally available\n    - black\n    - blackd\ndone! ✨ 🌟 ✨\nInstalling to existing venv 'pipx'\n  installed package pipx 1.4.3, installed using Python 3.10.12\n  These apps are now globally available\n    - pipx\ndone! ✨ 🌟 ✨\n```\n\n## `pipx upgrade-shared` examples\n\nOne use of the upgrade-shared command is to force a `pip` upgrade.\n\n```shell\n> pipx upgrade-shared\n```\n\nThis example pins `pip` (temporarily, until the next automatic upgrade, if that is not explicitly turned off) to a\nspecific version.\n\n```shell\n> pipx upgrade-shared --pip-args=pip==24.0\n```\n\n## `pipx pin` examples\n\nPinning keeps an installation at its current version until you call `pipx unpin`.\n\nPin the entire environment (main package plus anything injected):\n\n```shell\n> pipx install httpie\n> pipx pin httpie\n> pipx list --pinned\nhttpie <current version>\n```\n\nPin only injected packages so the main package can continue to receive upgrades:\n\n```shell\n> pipx inject poetry poetry-plugin-export poetry-plugin-app\n> pipx pin poetry --injected-only\nPinned 2 packages in venv poetry\n  - poetry-plugin-export <current version>\n  - poetry-plugin-app <current version>\n```\n\nSkip selected injected packages when pinning:\n\n```shell\n> pipx inject pdm pdm-django pdm-pytorch\n> pipx pin pdm --skip pdm-django\nPinned 1 packages in venv pdm\n  - pdm-pytorch <current version>\n```\n\nUnpin everything so upgrades resume:\n\n```shell\n> pipx unpin poetry\nUnpinned 2 packages in venv poetry\n  - poetry\n  - poetry-plugin-export\n  - poetry-plugin-app\n```\n\n[pip-requirements]: https://pip.pypa.io/en/stable/reference/requirements-file-format/\n"
  },
  {
    "path": "docs/reference/index.md",
    "content": "# Reference\n\nTechnical reference for pipx: CLI flags, environment variables, and runnable examples.\n\n- [CLI Reference](cli.md) — auto-generated help text for every pipx command.\n- [Examples](examples.md) — copy-paste command examples for install, run, inject, list, and more.\n- [Programs to Try](programs-to-try.md) — Python applications worth installing with pipx.\n- [Environment Variables](environment-variables.md) — every `PIPX_*` variable and what it controls.\n"
  },
  {
    "path": "docs/reference/programs-to-try.md",
    "content": "## Programs\n\nHere are some programs you can try out. If you've never used the program before, make sure you add the `--help` flag so\nit doesn't do something you don't expect. If you decide you want to install, you can run `pipx install PACKAGE` instead.\n\n### ansible\n\nIT automation\n\n```\npipx install --include-deps ansible\n```\n\n### asciinema\n\nRecord and share your terminal sessions, the right way.\n\n```\npipx run asciinema\n```\n\n### black\n\nuncompromising Python code formatter\n\n```\npipx run black\n```\n\n### pybabel\n\ninternationalizing and localizing Python applications\n\n```\npipx run --spec=babel pybabel --help\n```\n\n### chardetect\n\ndetect file encoding\n\n```\npipx run --spec=chardet chardetect --help\n```\n\n### cookiecutter\n\ncreates projects from project templates\n\n```\npipx run cookiecutter\n```\n\n### create-python-package\n\neasily create and publish new Python packages\n\n```\npipx run create-python-package\n```\n\n### flake8\n\ntool for style guide enforcement\n\n```\npipx run flake8\n```\n\n### gdbgui\n\nbrowser-based gdb debugger\n\n```\npipx run gdbgui\n```\n\n### hatch\n\nPython project manager that lets you build & publish packages, run tasks in environments and more\n\n```\npipx run hatch\n```\n\n### hexsticker\n\ncreate hexagon stickers automatically\n\n```\npipx run hexsticker\n```\n\n### ipython\n\npowerful interactive Python shell\n\n```\npipx run ipython\n```\n\n### jupyter\n\nweb-based notebook environment for interactive computing\n\n```\npipx run jupyter\n```\n\n### pipenv\n\npython dependency/environment management\n\n```\npipx run pipenv\n```\n\n### poetry\n\npython dependency/environment/packaging management\n\n```\npipx run poetry\n```\n\n### pylint\n\nsource code analyzer\n\n```\npipx run pylint\n```\n\n### pyinstaller\n\nbundles a Python application and all its dependencies into a single package\n\n```\npipx run pyinstaller\n```\n\n### pyxtermjs\n\nfully functional terminal in the browser\n\n```\npipx run pyxtermjs\n```\n\n### ruff\n\nAn extremely fast Python linter\n\n```\npipx run ruff\n```\n\n### shell-functools\n\nFunctional programming tools for the shell\n\n```\npipx install shell-functools\n```\n"
  },
  {
    "path": "docs/tutorial/getting-started.md",
    "content": "## Getting Started with pipx\n\nThis tutorial covers the core pipx workflow: installing an application, running it, and managing it. You need\n[pipx installed](../how-to/install-pipx.md) before continuing.\n\n### Install your first application\n\nPick a small package to try. `pycowsay` works well:\n\n```\npipx install pycowsay\n```\n\npipx creates an isolated virtual environment for `pycowsay`, installs it there, and links the `pycowsay` command into a\ndirectory on your `PATH`. Run it from anywhere:\n\n```\npycowsay \"Hello, pipx!\"\n```\n\n### List installed applications\n\n```\npipx list\n```\n\nThe output shows the virtual environment location, exposed commands, and the Python version each package uses.\n\n### Run an application without installing\n\n`pipx run` executes an application in a temporary environment and cleans up after itself:\n\n```\npipx run pycowsay moooo!\n```\n\n### Upgrade an installed application\n\n```\npipx upgrade pycowsay\n```\n\nOr upgrade everything at once:\n\n```\npipx upgrade-all\n```\n\n### Uninstall an application\n\n```\npipx uninstall pycowsay\n```\n\npipx deletes the isolated environment and removes the command from your `PATH`.\n\n### Next steps\n\nContinue with the [install applications](install-applications.md) and [run applications](run-applications.md) tutorials\nfor a closer look at the two core commands. The [how-to guides](../how-to/index.md) cover tasks like injecting packages\nand configuring paths. The full [CLI reference](../reference/cli.md) documents every flag.\n"
  },
  {
    "path": "docs/tutorial/index.md",
    "content": "# Tutorials\n\nStep-by-step lessons that take you from zero to productive with pipx. Start from the top if you are new.\n\n- [Getting Started](getting-started.md) — install your first application and learn the core commands.\n- [Install Applications](install-applications.md) — `pipx install` with real examples.\n- [Run Applications](run-applications.md) — `pipx run` for one-off execution in temporary environments.\n"
  },
  {
    "path": "docs/tutorial/install-applications.md",
    "content": "## Installing a Package and its Applications\n\nInstall an application with:\n\n```\npipx install PACKAGE\n```\n\npipx creates a virtual environment, installs the package, and adds its entry points to a location on your `PATH`.\n`pipx install pycowsay` makes the `pycowsay` command available system-wide while sandboxing pycowsay in its own virtual\nenvironment. No `sudo` required. To install for all users on the system, pass `--global` after the subcommand (see\n[Configure Paths](../how-to/configure-paths.md#-global-argument)).\n\n```\n>> pipx install pycowsay\n  installed package pycowsay 2.0.3, Python 3.10.3\n  These apps are now globally available\n    - pycowsay\ndone! ✨ 🌟 ✨\n\n\n>> pipx list\nvenvs are in /home/user/.local/share/pipx/venvs\napps are exposed on your $PATH at /home/user/.local/bin\n   package pycowsay 2.0.3, Python 3.10.3\n    - pycowsay\n\n\n# Now you can run pycowsay from anywhere\n>> pycowsay mooo\n  ____\n< mooo >\n  ====\n         \\\n          \\\n            ^__^\n            (oo)\\_______\n            (__)\\       )\\/\\\n                ||----w |\n                ||     ||\n\n```\n"
  },
  {
    "path": "docs/tutorial/run-applications.md",
    "content": "## Running an Application in a Temporary Virtual Environment\n\n`pipx run` downloads and runs Python applications in a one-time, temporary environment, then leaves your system\nuntouched. Use it to initialize a new project, check an app's help text, or try a tool without committing to an install.\n\nThe blog post [How to set up a perfect Python project](https://sourcery.ai/blog/python-best-practices/) uses `pipx run`\nto kickstart a new project with [cookiecutter](https://github.com/cookiecutter/cookiecutter).\n\n```\npipx run APP [ARGS...]\n```\n\npipx installs the package in an isolated, temporary directory and invokes the app:\n\n```\n> pipx run pycowsay moo\n\n  ---\n< moo >\n  ---\n   \\   ^__^\n    \\  (oo)\\_______\n       (__)\\       )\\/\\\n           ||----w |\n           ||     ||\n\n\n```\n\nArguments after the application name pass straight through:\n\n```\n> pipx run pycowsay these arguments are all passed to pycowsay!\n\n  -------------------------------------------\n< these arguments are all passed to pycowsay! >\n  -------------------------------------------\n   \\   ^__^\n    \\  (oo)\\_______\n       (__)\\       )\\/\\\n           ||----w |\n           ||     ||\n\n```\n\npipx caches virtual environments per app for a few days. After they expire, pipx fetches the latest version.\n\n### Ambiguous arguments\n\npipx can consume arguments meant for the application:\n\n```\n> pipx run pycowsay --py\n\nusage: pipx run [-h] [--no-cache] [--pypackages] [--spec SPEC] [--verbose] [--python PYTHON]\n                [--system-site-packages] [--index-url INDEX_URL] [--editable] [--pip-args PIP_ARGS]\n                app ...\npipx run: error: ambiguous option: --py could match --pypackages, --python\n```\n\nPlace `--` before the app name to forward all arguments verbatim:\n\n```\n> pipx run -- pycowsay --py\n\n\n  ----\n< --py >\n  ----\n   \\   ^__^\n    \\  (oo)\\_______\n       (__)\\       )\\/\\\n           ||----w |\n           ||     ||\n\n\n```\n\n### App name differs from package name\n\nUse `--spec` to specify the package and provide the app name separately:\n\n```\npipx run --spec PACKAGE APP\n```\n\nThe [esptool](https://github.com/espressif/esptool) package, for example, exposes executables with different names:\n\n```\n>> pipx run esptool\n'esptool' executable script not found in package 'esptool'.\nAvailable executable scripts:\n    esp_rfc2217_server.py - usage: 'pipx run --spec esptool esp_rfc2217_server.py [arguments?]'\n    espefuse.py - usage: 'pipx run --spec esptool espefuse.py [arguments?]'\n    espsecure.py - usage: 'pipx run --spec esptool espsecure.py [arguments?]'\n    esptool.py - usage: 'pipx run --spec esptool esptool.py [arguments?]'\n```\n\nRun them with `--spec`:\n\n```\npipx run --spec esptool esptool.py\n```\n\nThe `.py` is part of the executable name as declared by the package. The\n[pymodbus](https://github.com/pymodbus-dev/pymodbus) package shows a similar pattern:\n\n```\npipx run --spec pymodbus[repl] pymodbus.console\npipx run --spec pymodbus[repl] pymodbus.server\npipx run --spec pymodbus[repl] pymodbus.simulator\n```\n\nPackage authors can avoid this `--spec` requirement by declaring a\n[`pipx.run` entry point](../explanation/making-packages-compatible.md#the-pipxrun-entry-point-group) in their package\nmetadata.\n"
  },
  {
    "path": "get-pipx.py",
    "content": "#!/usr/bin/env python3\nimport sys\n\n\ndef fail(msg):\n    sys.stderr.write(msg + \"\\n\")\n    sys.stderr.flush()\n    sys.exit(1)\n\n\ndef main():\n    fail(\n        \"This installation method has been deprecated. \"\n        \"See https://github.com/pypa/pipx for current installation \"\n        \"instructions.\"\n    )\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "mkdocs.yml",
    "content": "site_name: pipx\nsite_description: execute binaries from Python packages in isolated environments\ntheme:\n  name: \"material\"\n  palette:\n    - media: \"(prefers-color-scheme: light)\"\n      scheme: default\n      toggle:\n        icon: material/brightness-7\n        name: Switch to dark mode\n    - media: \"(prefers-color-scheme: dark)\"\n      scheme: slate\n      toggle:\n        icon: material/brightness-4\n        name: Switch to light mode\n  features:\n    - navigation.tabs\n    - navigation.tabs.sticky\n    - navigation.indexes\n    - toc.integrate\n    - content.tabs.link\n    - content.code.copy\nrepo_name: pypa/pipx\nrepo_url: https://github.com/pypa/pipx\nedit_uri: edit/main/docs/\nextra:\n  analytics:\n    provider: 'google'\n    property: 'UA-90243909-2'\nnav:\n  - Home: \"index.md\"\n  - Tutorials:\n      - tutorial/index.md\n      - Getting Started: \"tutorial/getting-started.md\"\n      - Install Applications: \"tutorial/install-applications.md\"\n      - Run Applications: \"tutorial/run-applications.md\"\n  - How-to Guides:\n      - how-to/index.md\n      - Install pipx: \"how-to/install-pipx.md\"\n      - Upgrade pipx: \"how-to/upgrade-pipx.md\"\n      - Inject Packages: \"how-to/inject-packages.md\"\n      - Pin Packages: \"how-to/pin-packages.md\"\n      - Run Scripts: \"how-to/run-scripts.md\"\n      - Use with pre-commit: \"how-to/use-with-pre-commit.md\"\n      - Configure Paths: \"how-to/configure-paths.md\"\n      - Move Installation: \"how-to/move-installation.md\"\n      - Shell Completions: \"how-to/shell-completions.md\"\n      - Troubleshoot: \"how-to/troubleshoot.md\"\n  - Reference:\n      - reference/index.md\n      - CLI Reference: \"reference/cli.md\"\n      - Examples: \"reference/examples.md\"\n      - Programs to Try: \"reference/programs-to-try.md\"\n      - Environment Variables: \"reference/environment-variables.md\"\n  - Explanation:\n      - explanation/index.md\n      - How pipx Works: \"explanation/how-pipx-works.md\"\n      - Comparisons: \"explanation/comparisons.md\"\n      - Making Packages Compatible: \"explanation/making-packages-compatible.md\"\n  - Contributing: \"contributing.md\"\n  - Changelog: \"changelog.md\"\nmarkdown_extensions:\n  - markdown_gfm_admonition # GitHub's admonition (alert) syntax\n  - footnotes\n  - pymdownx.superfences:\n      custom_fences:\n        - name: mermaid\n          class: mermaid\n          format: !!python/name:pymdownx.superfences.fence_code_format\n  - pymdownx.tabbed:\n      alternate_style: true\nplugins:\n  - search:\n      lang: en\n  - gen-files:\n      scripts:\n        - scripts/gen_doc_pages.py\n  - macros\nexclude_docs: |\n  README.md\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nbuild-backend = \"hatchling.build\"\nrequires = [\n  \"hatch-vcs>=0.4\",\n  \"hatchling>=1.27\",\n]\n\n[project]\nname = \"pipx\"\ndescription = \"Install and Run Python Applications in Isolated Environments\"\nreadme = \"docs/README.md\"\nkeywords = [\n  \"cli\",\n  \"install\",\n  \"pip\",\n  \"Virtual Environment\",\n  \"workflow\",\n]\nlicense = \"MIT\"\nlicense-files = [ \"LICENSE\" ]\nauthors = [\n  { name = \"Chad Smith\", email = \"chadsmith.software@gmail.com\" },\n]\nrequires-python = \">=3.9\"\nclassifiers = [\n  \"Operating System :: OS Independent\",\n  \"Programming Language :: Python\",\n  \"Programming Language :: Python :: 3 :: Only\",\n  \"Programming Language :: Python :: 3.9\",\n  \"Programming Language :: Python :: 3.10\",\n  \"Programming Language :: Python :: 3.11\",\n  \"Programming Language :: Python :: 3.12\",\n  \"Programming Language :: Python :: 3.13\",\n  \"Programming Language :: Python :: 3.14\",\n]\ndynamic = [\n  \"version\",\n]\ndependencies = [\n  \"argcomplete>=1.9.4\",\n  \"colorama>=0.4.4; sys_platform=='win32'\",\n  \"packaging>=20\",\n  \"platformdirs>=2.1\",\n  \"tomli; python_version<'3.11'\",\n  \"userpath>=1.6,!=1.9\",\n]\nurls.\"Issue Tracker\" = \"https://github.com/pypa/pipx/issues\"\nurls.\"Release Notes\" = \"https://pipx.pypa.io/latest/changelog/\"\nurls.\"Source Code\" = \"https://github.com/pypa/pipx\"\nurls.Documentation = \"https://pipx.pypa.io\"\nurls.Homepage = \"https://pipx.pypa.io\"\nscripts.pipx = \"pipx.main:cli\"\n\n[dependency-groups]\ndev = [\n  { include-group = \"docs\" },\n  { include-group = \"lint\" },\n  { include-group = \"man\" },\n  { include-group = \"test\" },\n]\ntest = [\n  \"pypiserver[cache,passlib]>=2.3.2\",\n  \"pytest>=8\",\n  \"pytest-cov>=4\",\n  \"pytest-mock>=3.11.1\",\n  \"pytest-xdist>=3.5\",\n  \"setuptools>=68; python_version>='3.12'\",\n  \"watchdog>=4\",\n]\ndocs = [\n  \"jinja2\",\n  \"markdown-gfm-admonition\",\n  \"mkdocs<2\",\n  \"mkdocs-gen-files\",\n  \"mkdocs-macros-plugin\",\n  \"mkdocs-material\",\n  \"towncrier>=23.6\",\n]\nlint = [\n  \"pre-commit>=3\",\n]\nman = [\n  \"argparse-manpage[setuptools]\",\n]\nzipapp = [\n  \"shiv>=1.0.6\",\n]\n\n[tool.hatch]\nbuild.hooks.vcs.version-file = \"src/pipx/version.py\"\nbuild.targets.sdist.include = [\n  \"/src\",\n  \"/logo.png\",\n  \"/pipx_demo.gif\",\n  \"/*.md\",\n]\nversion.source = \"vcs\"\n\n[tool.ruff]\nline-length = 121\nlint.extend-select = [\n  \"A\",\n  \"B\",\n  \"C4\",\n  \"C9\",\n  \"I\",\n  \"ISC\",\n  \"PERF\",\n  \"PGH\",\n  \"PIE\",\n  \"PLC\",\n  \"PLE\",\n  \"PLW\",\n  \"RSE\",\n  \"RUF\",\n  \"TC\",\n  \"UP\",\n  \"W\",\n]\nlint.ignore = [\n  \"PERF203\",\n  \"PLW1508\",\n  \"RUF005\",\n]\nlint.per-file-ignores.\"src/pipx/venv.py\" = [\n  \"A005\",\n]\nlint.isort = { known-first-party = [\n  \"helpers\",\n  \"package_info\",\n  \"pipx\",\n] }\nlint.mccabe.max-complexity = 15\n\n[tool.codespell]\n# Ref: https://github.com/codespell-project/codespell#using-a-config-file\nskip = \".git,*.pdf,*.svg,.nox,testdata,.mypy_cache\"\ncheck-hidden = true\n# case sensitive etc\nignore-regex = \"\\\\b(UE|path/doesnt/exist)\\\\b\"\n\n[tool.mypy]\noverrides = [ { module = [\n  \"pycowsay.*\",\n], ignore_missing_imports = true } ]\nwarn_unreachable = true\nenable_error_code = [ \"ignore-without-code\", \"redundant-expr\", \"truthy-bool\" ]\n\n[tool.pytest]\nini_options.minversion = \"8\"\nini_options.log_cli_level = \"INFO\"\nini_options.markers = [\n  \"all_packages: test install with maximum number of packages\",\n]\nini_options.testpaths = [ \"tests\" ]\nini_options.addopts = [ \"-ra\", \"--strict-config\", \"--strict-markers\" ]\n\n[tool.towncrier]\ndirectory = \"changelog.d\"\nfilename = \"docs/changelog.md\"\nstart_string = \"<!-- towncrier release notes start -->\\n\"\nunderlines = [\n  \"\",\n  \"\",\n  \"\",\n]\ntitle_format = \"## [{version}](https://github.com/pypa/pipx/tree/{version}) - {project_date}\"\nissue_format = \"[#{issue}](https://github.com/pypa/pipx/issues/{issue})\"\npackage = \"pipx\"\n"
  },
  {
    "path": "scripts/gen_doc_pages.py",
    "content": "import os\nimport sys\nfrom pathlib import Path\nfrom subprocess import check_output\nfrom typing import Optional\n\nimport mkdocs_gen_files\nfrom jinja2 import Environment, FileSystemLoader\n\n\ndef get_help(cmd: Optional[str]) -> str:\n    base = [\"pipx\"]\n    args = base + ([cmd] if cmd else []) + [\"--help\"]\n    env_patch = os.environ.copy()\n    env_patch[\"PATH\"] = os.pathsep.join([str(Path(sys.executable).parent)] + env_patch[\"PATH\"].split(os.pathsep))\n    content = check_output(args, text=True, env=env_patch)\n    content = content.replace(str(Path(\"~\").expanduser()), \"~\")\n    return f\"\"\"\n```\n{content}\n```\n\"\"\"\n\n\nparams = {\n    \"install\": get_help(\"install\"),\n    \"installall\": get_help(\"install-all\"),\n    \"uninject\": get_help(\"uninject\"),\n    \"inject\": get_help(\"inject\"),\n    \"upgrade\": get_help(\"upgrade\"),\n    \"upgradeall\": get_help(\"upgrade-all\"),\n    \"upgradeshared\": get_help(\"upgrade-shared\"),\n    \"uninstall\": get_help(\"uninstall\"),\n    \"uninstallall\": get_help(\"uninstall-all\"),\n    \"reinstall\": get_help(\"reinstall\"),\n    \"reinstallall\": get_help(\"reinstall-all\"),\n    \"list\": get_help(\"list\"),\n    \"interpreter\": get_help(\"interpreter\"),\n    \"run\": get_help(\"run\"),\n    \"runpip\": get_help(\"runpip\"),\n    \"ensurepath\": get_help(\"ensurepath\"),\n    \"environment\": get_help(\"environment\"),\n    \"completions\": get_help(\"completions\"),\n    \"usage\": get_help(None),\n}\n\n\nenv = Environment(loader=FileSystemLoader(Path(__file__).parent / \"templates\"))\n\nwith mkdocs_gen_files.open(\"reference/cli.md\", \"wt\") as file_handler:\n    file_handler.write(env.get_template(\"docs.md\").render(**params))\n    file_handler.write(\"\\n\")\n"
  },
  {
    "path": "scripts/generate_man.py",
    "content": "#!/usr/bin/env python3\n\nimport os.path\nimport sys\nimport textwrap\nfrom typing import cast\n\nfrom build_manpages.manpage import Manpage  # type: ignore[import-not-found]\n\nfrom pipx.main import get_command_parser\n\n\ndef main():\n    sys.argv[0] = \"pipx\"\n    parser, _ = get_command_parser()\n    parser.man_short_description = cast(\"str\", parser.description).splitlines()[1]  # type: ignore[attr-defined]\n\n    manpage = Manpage(parser)\n    body = str(manpage)\n\n    # Avoid hardcoding build paths in manpages (and improve readability)\n    body = body.replace(os.path.expanduser(\"~\").replace(\"-\", \"\\\\-\"), \"~\")\n\n    # Add a credit section\n    body += textwrap.dedent(\n        \"\"\"\n        .SH AUTHORS\n        .IR pipx (1)\n        was written by Chad Smith and contributors.\n        The project can be found online at\n        .UR https://pipx.pypa.io\n        .UE\n        .SH SEE ALSO\n        .IR pip (1),\n        .IR virtualenv (1)\n        \"\"\"\n    )\n\n    with open(\"pipx.1\", \"w\") as f:\n        f.write(body)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/list_test_packages.py",
    "content": "#!/usr/bin/env python3\nimport argparse\nimport re\nimport subprocess\nimport sys\nimport tempfile\nfrom concurrent.futures import ThreadPoolExecutor, as_completed\nfrom pathlib import Path\nfrom typing import Any\n\nfrom test_packages_support import get_platform_list_path\n\n\ndef process_command_line(argv: list[str]) -> argparse.Namespace:\n    \"\"\"Process command line invocation arguments and switches.\n\n    Args:\n        argv: list of arguments, or `None` from ``sys.argv[1:]``.\n\n    Returns:\n        argparse.Namespace: named attributes of arguments and switches\n    \"\"\"\n    # script_name = argv[0]\n    argv = argv[1:]\n\n    # initialize the parser object:\n    parser = argparse.ArgumentParser(\n        description=\"Create list of needed test packages for pipx tests and local pypiserver.\"\n    )\n\n    # specifying nargs= puts outputs of parser in list (even if nargs=1)\n\n    # required arguments\n    parser.add_argument(\n        \"primary_package_list\",\n        help=\"Main packages to examine, getting list of matching distribution files and dependencies.\",\n    )\n    parser.add_argument(\"package_list_dir\", help=\"Directory to output package distribution lists.\")\n\n    # switches/options:\n    parser.add_argument(\"-v\", \"--verbose\", action=\"store_true\", help=\"Maximum verbosity, especially for pip operations.\")\n\n    return parser.parse_args(argv)\n\n\ndef parse_package_list(package_list_file: Path) -> list[dict[str, Any]]:\n    output_list: list[dict[str, Any]] = []\n    try:\n        with package_list_file.open(\"r\") as package_list_fh:\n            for line in package_list_fh:\n                line_parsed = re.sub(r\"#.+$\", \"\", line)\n                if not re.search(r\"\\S\", line_parsed):\n                    continue\n                line_list = line_parsed.strip().split()\n                if len(line_list) == 1:\n                    output_list.append({\"spec\": line_list[0]})\n                elif len(line_list) == 2:\n                    output_list.append({\"spec\": line_list[0], \"no-deps\": line_list[1].lower() == \"true\"})\n                else:\n                    print(f\"ERROR: Unable to parse primary package list line:\\n    {line.strip()}\")\n                    return []\n    except OSError:\n        print(\"ERROR: File problem reading primary package list.\")\n        return []\n    return output_list\n\n\ndef create_test_packages_list(package_list_dir_path: Path, primary_package_list_path: Path, verbose: bool) -> int:\n    exit_code = 0\n    package_list_dir_path.mkdir(exist_ok=True)\n    platform_package_list_path = get_platform_list_path(package_list_dir_path)\n\n    primary_test_packages = parse_package_list(primary_package_list_path)\n    if not primary_test_packages:\n        print(f\"ERROR: Problem reading {primary_package_list_path}.  Exiting.\", file=sys.stderr)\n        return 1\n\n    with ThreadPoolExecutor(max_workers=12) as pool:\n        futures = {pool.submit(download, pkg, verbose) for pkg in primary_test_packages}\n        downloaded_list = set()\n        for future in as_completed(futures):\n            downloaded_list.update(future.result())\n\n    all_packages = []\n    for downloaded_path in downloaded_list:\n        wheel_re = re.search(r\"([^-]+)-([^-]+)-([^-]+)\\-([^-]+)-([^-]+)(-[^-]+)?\\.whl$\", downloaded_path)\n        src_re = re.search(r\"(.+)-([^-]+)\\.(?:tar.gz|zip)$\", downloaded_path)\n        if wheel_re:\n            package_name = wheel_re.group(1)\n            package_version = wheel_re.group(2)\n        elif src_re:\n            package_name = src_re.group(1)\n            package_version = src_re.group(2)\n        else:\n            print(f\"ERROR: cannot parse: {downloaded_path}\", file=sys.stderr)\n            continue\n\n        all_packages.append(f\"{package_name}=={package_version}\")\n\n    with platform_package_list_path.open(\"w\") as package_list_fh:\n        for package in sorted(all_packages):\n            print(package, file=package_list_fh)\n\n    return exit_code\n\n\ndef download(test_package: dict[str, str], verbose: bool) -> set[str]:\n    no_deps = test_package.get(\"no-deps\", False)\n    test_package_option_string = \" (no-deps)\" if no_deps else \"\"\n    verbose_this_iteration = False\n    with tempfile.TemporaryDirectory() as download_dir:\n        cmd_list = [\"pip\", \"download\"] + ([\"--no-deps\"] if no_deps else []) + [test_package[\"spec\"], \"-d\", download_dir]\n        if verbose:\n            print(f\"CMD: {' '.join(cmd_list)}\")\n        pip_download_process = subprocess.run(cmd_list, capture_output=True, text=True, check=False)\n        if pip_download_process.returncode == 0:\n            print(f\"Examined {test_package['spec']}{test_package_option_string}\")\n        else:\n            print(f\"ERROR with {test_package['spec']}{test_package_option_string}\", file=sys.stderr)\n            verbose_this_iteration = True\n        if verbose or verbose_this_iteration:\n            print(pip_download_process.stdout)\n            print(pip_download_process.stderr)\n        return {i.name for i in Path(download_dir).iterdir()}\n\n\ndef main(argv: list[str]) -> int:\n    args = process_command_line(argv)\n\n    return create_test_packages_list(Path(args.package_list_dir), Path(args.primary_package_list), args.verbose)\n\n\nif __name__ == \"__main__\":\n    try:\n        status = main(sys.argv)\n    except KeyboardInterrupt:\n        print(\"Stopped by Keyboard Interrupt\", file=sys.stderr)\n        status = 130\n\n    sys.exit(status)\n"
  },
  {
    "path": "scripts/migrate_pipsi_to_pipx.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"\nScript to migrate from pipsi to pipx\n\"\"\"\n\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom shutil import which\n\n\ndef main():\n    if not which(\"pipx\"):\n        sys.exit(\"pipx must be installed to migrate from pipsi to pipx\")\n\n    if not sys.stdout.isatty():\n        sys.exit(\"Must be run from a terminal, not a script\")\n\n    pipsi_home = os.environ.get(\"PIPSI_HOME\", os.path.expanduser(\"~/.local/venvs/\"))\n    packages = [p.name for p in Path(pipsi_home).iterdir()]\n\n    if not packages:\n        print(\"No packages installed with pipsi\")\n        sys.exit(0)\n\n    print(\"Attempting to migrate the following packages from pipsi to pipx:\")\n    for package in packages:\n        print(f\"  - {package}\")\n\n    answer = None\n    while answer not in [\"y\", \"n\"]:\n        answer = input(\"Continue? [y/n] \")\n\n    if answer == \"n\":\n        sys.exit(0)\n\n    error = False\n    for package in packages:\n        ret = subprocess.run([\"pipsi\", \"uninstall\", \"--yes\", package], check=False)\n        if ret.returncode:\n            error = True\n            print(f\"Failed to uninstall {package!r} with pipsi. Not attempting to install with pipx.\")\n        else:\n            print(f\"uninstalled {package!r} with pipsi. Now attempting to install with pipx.\")\n            ret = subprocess.run([\"pipx\", \"install\", package], check=False)\n            if ret.returncode:\n                error = True\n                print(f\"Failed to install {package!r} with pipx.\")\n            else:\n                print(f\"Successfully installed {package} with pipx\")\n\n    print(f\"Done migrating {len(packages)} packages!\")\n    print(\"You may still need to run `pipsi uninstall pipsi` or `pip uninstall pipsi`. Refer to pipsi's documentation.\")\n\n    if error:\n        print(\"Note: Finished with errors. Review output to manually complete migration.\")\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/release.py",
    "content": "\"\"\"Handles creating a release.\"\"\"\n\nfrom __future__ import annotations\n\nfrom pathlib import Path\nfrom subprocess import call, check_call\n\nfrom git import Commit, Remote, Repo, TagReference  # type: ignore[import-not-found]\nfrom packaging.version import Version\n\nROOT_DIR = Path(__file__).resolve().parents[1]\nCHANGELOG_DIR = ROOT_DIR / \"changelog.d\"\n\n\ndef main(version_str: str, *, push: bool) -> None:\n    repo = Repo(str(ROOT_DIR))\n    if repo.is_dirty():\n        msg = \"Current repository is dirty. Please commit any changes and try again.\"\n        raise RuntimeError(msg)\n    remote = get_remote(repo)\n    remote.fetch()\n    version = resolve_version(version_str, repo)\n    print(f\"Releasing {version}\")\n    release_commit = release_changelog(repo, version)\n    tag = tag_release_commit(release_commit, repo, version)\n    if push:\n        print(\"Pushing release commit\")\n        repo.git.push(remote.name, \"HEAD:main\")\n        print(\"Pushing release tag\")\n        repo.git.push(remote.name, tag)\n    print(\"All done! ✨ 🍰 ✨\")\n\n\ndef resolve_version(version_str: str, repo: Repo) -> Version:\n    if version_str not in {\"auto\", \"major\", \"minor\", \"patch\"}:\n        return Version(version_str)\n    latest_tag = repo.git.describe(\"--tags\", \"--abbrev=0\")\n    parts = [int(x) for x in latest_tag.split(\".\")]\n    if version_str == \"major\":\n        parts = [parts[0] + 1, 0, 0]\n    elif version_str == \"minor\":\n        parts = [parts[0], parts[1] + 1, 0]\n    elif version_str == \"patch\":\n        parts[2] += 1\n    elif any(CHANGELOG_DIR.glob(\"*.feature.md\")) or any(CHANGELOG_DIR.glob(\"*.removal.md\")):\n        parts = [parts[0], parts[1] + 1, 0]\n    else:\n        parts[2] += 1\n    return Version(\".\".join(str(p) for p in parts))\n\n\ndef get_remote(repo: Repo) -> Remote:\n    upstream_remote = \"pypa/pipx\"\n    urls = set()\n    for remote in repo.remotes:\n        for url in remote.urls:\n            if url.rstrip(\".git\").endswith(upstream_remote):\n                return remote\n            urls.add(url)\n    msg = f\"Could not find {upstream_remote} remote, has {urls}\"\n    raise RuntimeError(msg)\n\n\ndef release_changelog(repo: Repo, version: Version) -> Commit:\n    print(\"Generating release commit\")\n    check_call([\"towncrier\", \"build\", \"--yes\", \"--version\", version.public], cwd=str(ROOT_DIR))\n    call([\"pre-commit\", \"run\", \"--all-files\"], cwd=str(ROOT_DIR))\n    repo.git.add(\".\")\n    check_call([\"pre-commit\", \"run\", \"--all-files\"], cwd=str(ROOT_DIR))\n    return repo.index.commit(f\"Release {version}\")\n\n\ndef tag_release_commit(release_commit: Commit, repo: Repo, version: Version) -> TagReference:\n    print(\"Tagging release commit\")\n    existing_tags = [x.name for x in repo.tags]\n    if str(version) in existing_tags:\n        print(f\"Deleting existing tag {version}\")\n        repo.delete_tag(str(version))\n    print(f\"Creating tag {version}\")\n    return repo.create_tag(str(version), ref=release_commit, force=True)\n\n\nif __name__ == \"__main__\":\n    import argparse\n\n    parser = argparse.ArgumentParser(prog=\"release\")\n    parser.add_argument(\"--version\", default=\"auto\")\n    parser.add_argument(\"--no-push\", action=\"store_true\")\n    options = parser.parse_args()\n    main(options.version, push=not options.no_push)\n"
  },
  {
    "path": "scripts/templates/docs.md",
    "content": "{{usage}}\n\n### pipx install\n\n{{install}}\n\n### pipx install-all\n\n{{installall}}\n\n### pipx uninject\n\n{{uninject}}\n\n### pipx inject\n\n{{inject}}\n\n### pipx upgrade\n\n{{upgrade}}\n\n### pipx upgrade-all\n\n{{upgradeall}}\n\n### pipx upgrade-shared\n\n{{upgradeshared}}\n\n### pipx uninstall\n\n{{uninstall}}\n\n### pipx uninstall-all\n\n{{uninstallall}}\n\n### pipx reinstall\n\n{{reinstall}}\n\n### pipx reinstall-all\n\n{{reinstallall}}\n\n### pipx list\n\n{{list}}\n\n### pipx interpreter\n\n{{interpreter}}\n\n### pipx run\n\n{{run}}\n\n### pipx runpip\n\n{{runpip}}\n\n### pipx ensurepath\n\n{{ensurepath}}\n\n### pipx environment\n\n{{environment}}\n\n### pipx completions\n\n{{completions}}\n"
  },
  {
    "path": "scripts/test_packages_support.py",
    "content": "import platform\nimport sys\nfrom pathlib import Path\n\nPYTHON_VERSION_STR = f\"{sys.version_info[0]}.{sys.version_info[1]}\"\n\n# Platform logic\nif sys.platform == \"darwin\":\n    FULL_PLATFORM = \"macos\" + platform.release().split(\".\")[0]\nelif sys.platform == \"win32\":\n    FULL_PLATFORM = \"win\"\nelse:\n    FULL_PLATFORM = \"unix\"\n\n\ndef get_platform_list_path(package_list_dir_path: Path) -> Path:\n    return package_list_dir_path / f\"{FULL_PLATFORM}-python{PYTHON_VERSION_STR}.txt\"\n\n\ndef get_platform_packages_dir_path(pipx_package_cache_path: Path) -> Path:\n    return pipx_package_cache_path / f\"{PYTHON_VERSION_STR}\"\n"
  },
  {
    "path": "scripts/update_package_cache.py",
    "content": "#!/usr/bin/env python3\nimport argparse\nimport re\nimport subprocess\nimport sys\nfrom concurrent.futures import ThreadPoolExecutor, as_completed\nfrom pathlib import Path\n\nfrom list_test_packages import create_test_packages_list\nfrom test_packages_support import get_platform_list_path, get_platform_packages_dir_path\n\n\ndef process_command_line(argv: list[str]) -> argparse.Namespace:\n    \"\"\"Process command line invocation arguments and switches.\n\n    Args:\n        argv: list of arguments, or `None` from ``sys.argv[1:]``.\n\n    Returns:\n        argparse.Namespace: named attributes of arguments and switches\n    \"\"\"\n    # script_name = argv[0]\n    argv = argv[1:]\n\n    # initialize the parser object:\n    parser = argparse.ArgumentParser(\n        description=\"Check and update as needed the pipx tests package cache \"\n        \"for use with the pipx tests local pypiserver.\"\n    )\n\n    # specifying nargs= puts outputs of parser in list (even if nargs=1)\n\n    # required arguments\n    parser.add_argument(\n        \"package_list_dir\",\n        help=\"Directory where platform- and python-specific package lists are found for pipx tests.\",\n    )\n    parser.add_argument(\n        \"pipx_package_cache_dir\",\n        help=\"Directory to store the packages distribution files.\",\n    )\n\n    # switches/options:\n    parser.add_argument(\n        \"-c\",\n        \"--check-only\",\n        action=\"store_true\",\n        help=\"Only check to see if needed packages are in PACKAGES_DIR, do not download or delete files.\",\n    )\n\n    return parser.parse_args(argv)\n\n\ndef update_test_packages_cache(package_list_dir_path: Path, pipx_package_cache_path: Path, check_only: bool) -> int:\n    exit_code = 0\n\n    platform_package_list_path = get_platform_list_path(package_list_dir_path)\n    packages_dir_path = get_platform_packages_dir_path(pipx_package_cache_path)\n    packages_dir_path.mkdir(exist_ok=True, parents=True)\n\n    packages_dir_files = list(packages_dir_path.iterdir())\n\n    if not platform_package_list_path.exists():\n        print(\n            f\"WARNING.  File {platform_package_list_path!s}\\n    does not exist.  Creating now...\",\n            file=sys.stderr,\n        )\n        create_list_returncode = create_test_packages_list(\n            package_list_dir_path,\n            package_list_dir_path / \"primary_packages.txt\",\n            verbose=False,\n        )\n        if create_list_returncode == 0:\n            print(\n                f\"File {platform_package_list_path!s}\\n\"\n                \"    successfully created.  Please check this file in to the\"\n                \"    repository for future use.\",\n                file=sys.stderr,\n            )\n        else:\n            print(\n                f\"ERROR.  Unable to create {platform_package_list_path!s}\\n    Cannot continue.\\n\",\n                file=sys.stderr,\n            )\n            return 1\n\n    try:\n        platform_package_list_fh = platform_package_list_path.open(\"r\")\n    except OSError:\n        print(\n            f\"ERROR.  File {platform_package_list_path!s}\\n    is not readable.  Cannot continue.\\n\",\n            file=sys.stderr,\n        )\n        return 1\n    else:\n        platform_package_list_fh.close()\n\n    print(\"Using the following file to specify needed package files:\")\n    print(f\"    {platform_package_list_path!s}\")\n    print(\"Ensuring the following directory contains necessary package files:\")\n    print(f\"    {packages_dir_path!s}\")\n\n    packages_dir_hits = []\n    packages_dir_missing = []\n    with platform_package_list_path.open(\"r\") as platform_package_list_fh:\n        for line in platform_package_list_fh:\n            package_spec = line.strip()\n            package_spec_re = re.search(r\"^(.+)==(.+)$\", package_spec)\n            if not package_spec_re:\n                print(f\"ERROR: CANNOT PARSE {package_spec}\", file=sys.stderr)\n                exit_code = 1\n                continue\n\n            package_name = package_spec_re.group(1)\n            package_ver = package_spec_re.group(2)\n            package_dist_patt = re.escape(package_name) + r\"-\" + re.escape(package_ver) + r\"(.tar.gz|.zip|-)\"\n            matches = [\n                output_dir_file\n                for output_dir_file in packages_dir_files\n                if re.search(package_dist_patt, output_dir_file.name)\n            ]\n            if len(matches) == 1:\n                packages_dir_files.remove(matches[0])\n                packages_dir_hits.append(matches[0])\n                continue\n            elif len(matches) > 1:\n                print(f\"ERROR: more than one match for {package_spec}.\", file=sys.stderr)\n                print(f\"    {matches}\", file=sys.stderr)\n                exit_code = 1\n                continue\n\n            packages_dir_missing.append(package_spec)\n\n    print(f\"MISSING FILES: {len(packages_dir_missing)}\")\n    print(f\"EXISTING (found) FILES: {len(packages_dir_hits)}\")\n    print(f\"LEFTOVER (unused) FILES: {len(packages_dir_files)}\")\n\n    if check_only:\n        return 0 if len(packages_dir_missing) == 0 else 1\n    else:\n        with ThreadPoolExecutor(max_workers=4) as pool:\n            futures = {pool.submit(download, pkg, packages_dir_path) for pkg in packages_dir_missing}\n            for future in as_completed(futures):\n                exit_code = future.result() or exit_code\n\n        for unused_file in packages_dir_files:\n            print(f\"Deleting {unused_file}...\")\n            unused_file.unlink()\n\n    return exit_code\n\n\ndef download(package_spec: str, packages_dir_path: Path) -> int:\n    pip_download_process = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"pip\",\n            \"download\",\n            \"--no-deps\",\n            package_spec,\n            \"-d\",\n            str(packages_dir_path),\n        ],\n        capture_output=True,\n        text=True,\n        check=False,\n    )\n    if pip_download_process.returncode == 0:\n        print(f\"Successfully downloaded {package_spec}\")\n        return 0\n\n    print(f\"ERROR downloading {package_spec}\", file=sys.stderr)\n    print(pip_download_process.stdout, file=sys.stderr)\n    print(pip_download_process.stderr, file=sys.stderr)\n    return 1\n\n\ndef main(argv: list[str]) -> int:\n    args = process_command_line(argv)\n    return update_test_packages_cache(Path(args.package_list_dir), Path(args.pipx_package_cache_dir), args.check_only)\n\n\nif __name__ == \"__main__\":\n    try:\n        status = main(sys.argv)\n    except KeyboardInterrupt:\n        print(\"Stopped by Keyboard Interrupt\", file=sys.stderr)\n        status = 130\n\n    sys.exit(status)\n"
  },
  {
    "path": "src/pipx/__init__.py",
    "content": "import sys\n\nif sys.version_info < (3, 9, 0):  # noqa: UP036\n    sys.exit(\"Python 3.9 or later is required. See https://github.com/pypa/pipx for installation instructions.\")\n"
  },
  {
    "path": "src/pipx/__main__.py",
    "content": "import os\nimport sys\n\nif not __spec__ or not __spec__.parent:\n    # Running from source. Add pipx's source code to the system\n    # path to allow direct invocation, such as:\n    #   python src/pipx --help\n    pipx_package_source_path = os.path.dirname(os.path.dirname(__file__))\n    sys.path.insert(0, pipx_package_source_path)\n\nfrom pipx.main import cli\n\nif __name__ == \"__main__\":\n    sys.exit(cli())\n"
  },
  {
    "path": "src/pipx/animate.py",
    "content": "import logging\nimport shutil\nimport sys\nfrom collections.abc import Generator\nfrom contextlib import contextmanager\nfrom threading import Event, Thread\n\nfrom pipx.constants import WINDOWS\nfrom pipx.emojis import EMOJI_SUPPORT\n\nlogger = logging.getLogger(__name__)\n\nstderr_is_tty = bool(sys.stderr and sys.stderr.isatty())\n\nCLEAR_LINE = \"\\033[K\"\nEMOJI_ANIMATION_FRAMES = [\"⣷\", \"⣯\", \"⣟\", \"⡿\", \"⢿\", \"⣻\", \"⣽\", \"⣾\"]\nNONEMOJI_ANIMATION_FRAMES = [\"\", \".\", \"..\", \"...\"]\nEMOJI_FRAME_PERIOD = 0.1\nNONEMOJI_FRAME_PERIOD = 1\nMINIMUM_COLS_ALLOW_ANIMATION = 16\n\n\nif WINDOWS:\n    import ctypes\n\n    class _CursorInfo(ctypes.Structure):\n        _fields_ = ((\"size\", ctypes.c_int), (\"visible\", ctypes.c_byte))\n\n\ndef _env_supports_animation() -> bool:\n    (term_cols, _) = shutil.get_terminal_size(fallback=(0, 0))\n    return stderr_is_tty and term_cols > MINIMUM_COLS_ALLOW_ANIMATION\n\n\n@contextmanager\ndef animate(message: str, do_animation: bool, *, delay: float = 0) -> Generator[None, None, None]:\n    pipx_logger = logging.getLogger(\"pipx\")\n    handler_level = pipx_logger.handlers[0].level if pipx_logger.handlers else 0\n    if pipx_logger.handlers and handler_level > logging.WARNING:\n        yield\n        return\n\n    if not do_animation or not _env_supports_animation():\n        sys.stderr.write(f\"{message}...\\n\")\n        yield\n        return\n\n    event = Event()\n\n    if EMOJI_SUPPORT:\n        animate_at_beginning_of_line = True\n        symbols = EMOJI_ANIMATION_FRAMES\n        period = EMOJI_FRAME_PERIOD\n    else:\n        animate_at_beginning_of_line = False\n        symbols = NONEMOJI_ANIMATION_FRAMES\n        period = NONEMOJI_FRAME_PERIOD\n\n    thread_kwargs = {\n        \"message\": message,\n        \"event\": event,\n        \"symbols\": symbols,\n        \"delay\": delay,\n        \"period\": period,\n        \"animate_at_beginning_of_line\": animate_at_beginning_of_line,\n    }\n\n    t = Thread(target=print_animation, kwargs=thread_kwargs)\n    t.start()\n\n    try:\n        yield\n    finally:\n        event.set()\n        clear_line()\n\n\ndef print_animation(\n    *,\n    message: str,\n    event: Event,\n    symbols: list[str],\n    delay: float,\n    period: float,\n    animate_at_beginning_of_line: bool,\n) -> None:\n    (term_cols, _) = shutil.get_terminal_size(fallback=(9999, 24))\n    event.wait(delay)\n    while not event.wait(0):\n        for s in symbols:\n            if animate_at_beginning_of_line:\n                max_message_len = term_cols - len(f\"{s} ... \")\n                cur_line = f\"{s} {message:.{max_message_len}}\"\n                if len(message) > max_message_len:\n                    cur_line += \"...\"\n            else:\n                max_message_len = term_cols - len(\"... \")\n                cur_line = f\"{message:.{max_message_len}}{s}\"\n\n            clear_line()\n            sys.stderr.write(cur_line)\n            sys.stderr.flush()\n            if event.wait(period):\n                break\n\n\n# for Windows pre-ANSI-terminal-support (before Windows 10 TH2 (v1511))\n# https://stackoverflow.com/a/10455937\ndef win_cursor(visible: bool) -> None:\n    if sys.platform != \"win32\":  # hello mypy\n        return\n    ci = _CursorInfo()  # type: ignore[unreachable]\n    handle = ctypes.windll.kernel32.GetStdHandle(-11)\n    ctypes.windll.kernel32.GetConsoleCursorInfo(handle, ctypes.byref(ci))\n    ci.visible = visible\n    ctypes.windll.kernel32.SetConsoleCursorInfo(handle, ctypes.byref(ci))\n\n\ndef hide_cursor() -> None:\n    if stderr_is_tty:\n        if WINDOWS:\n            win_cursor(visible=False)\n        else:\n            sys.stderr.write(\"\\033[?25l\")\n            sys.stderr.flush()\n\n\ndef show_cursor() -> None:\n    if stderr_is_tty:\n        if WINDOWS:\n            win_cursor(visible=True)\n        else:\n            sys.stderr.write(\"\\033[?25h\")\n            sys.stderr.flush()\n\n\ndef clear_line() -> None:\n    \"\"\"Clears current line and positions cursor at start of line\"\"\"\n    sys.stderr.write(f\"\\r{CLEAR_LINE}\")\n    sys.stdout.write(f\"\\r{CLEAR_LINE}\")\n"
  },
  {
    "path": "src/pipx/colors.py",
    "content": "import sys\nfrom typing import Callable\n\ntry:\n    import colorama  # type: ignore[import-untyped]\nexcept ImportError:  # Colorama is Windows only package\n    colorama = None\n\nPRINT_COLOR = bool(sys.stdout and sys.stdout.isatty())\n\nif PRINT_COLOR and colorama:\n    colorama.init()\n\n\nclass c:\n    header = \"\\033[95m\"\n    blue = \"\\033[94m\"\n    green = \"\\033[92m\"\n    yellow = \"\\033[93m\"\n    red = \"\\033[91m\"\n    bold = \"\\033[1m\"\n    cyan = \"\\033[96m\"\n    underline = \"\\033[4m\"\n    end = \"\\033[0m\"\n\n\ndef mkcolorfunc(style: str) -> Callable[[str], str]:\n    def stylize_text(x: str) -> str:\n        if PRINT_COLOR:\n            return f\"{style}{x}{c.end}\"\n        else:\n            return x\n\n    return stylize_text\n\n\nbold = mkcolorfunc(c.bold)\nred = mkcolorfunc(c.red)\nblue = mkcolorfunc(c.cyan)\ncyan = mkcolorfunc(c.blue)\ngreen = mkcolorfunc(c.green)\n"
  },
  {
    "path": "src/pipx/commands/__init__.py",
    "content": "from pipx.commands.ensure_path import ensure_pipx_paths\nfrom pipx.commands.environment import environment\nfrom pipx.commands.inject import inject\nfrom pipx.commands.install import install, install_all\nfrom pipx.commands.interpreter import list_interpreters, prune_interpreters, upgrade_interpreters\nfrom pipx.commands.list_packages import list_packages\nfrom pipx.commands.pin import pin, unpin\nfrom pipx.commands.reinstall import reinstall, reinstall_all\nfrom pipx.commands.run import run\nfrom pipx.commands.run_pip import run_pip\nfrom pipx.commands.uninject import uninject\nfrom pipx.commands.uninstall import uninstall, uninstall_all\nfrom pipx.commands.upgrade import upgrade, upgrade_all, upgrade_shared\n\n__all__ = [\n    \"ensure_pipx_paths\",\n    \"environment\",\n    \"inject\",\n    \"install\",\n    \"install_all\",\n    \"list_interpreters\",\n    \"list_packages\",\n    \"pin\",\n    \"prune_interpreters\",\n    \"reinstall\",\n    \"reinstall_all\",\n    \"run\",\n    \"run_pip\",\n    \"uninject\",\n    \"uninstall\",\n    \"uninstall_all\",\n    \"unpin\",\n    \"upgrade\",\n    \"upgrade_all\",\n    \"upgrade_interpreters\",\n    \"upgrade_shared\",\n]\n"
  },
  {
    "path": "src/pipx/commands/common.py",
    "content": "import filecmp\nimport logging\nimport os\nimport shlex\nimport shutil\nimport sys\nimport tempfile\nimport time\nfrom pathlib import Path\nfrom shutil import which\nfrom tempfile import TemporaryDirectory\nfrom typing import Optional\n\nimport userpath  # type: ignore[import-not-found]\nfrom packaging.utils import canonicalize_name\n\nfrom pipx import paths\nfrom pipx.colors import bold, red\nfrom pipx.constants import MAN_SECTIONS, WINDOWS\nfrom pipx.emojis import hazard, stars\nfrom pipx.package_specifier import parse_specifier_for_install, valid_pypi_name\nfrom pipx.pipx_metadata_file import PackageInfo\nfrom pipx.util import PipxError, mkdir, pipx_wrap, rmdir, safe_unlink\nfrom pipx.venv import Venv\n\nlogger = logging.getLogger(__name__)\n\n\nclass VenvProblems:\n    def __init__(\n        self,\n        bad_venv_name: bool = False,\n        invalid_interpreter: bool = False,\n        missing_metadata: bool = False,\n        not_installed: bool = False,\n    ) -> None:\n        self.bad_venv_name = bad_venv_name\n        self.invalid_interpreter = invalid_interpreter\n        self.missing_metadata = missing_metadata\n        self.not_installed = not_installed\n\n    def any_(self) -> bool:\n        return any(self.__dict__.values())\n\n    def or_(self, venv_problems: \"VenvProblems\") -> None:\n        for attribute in self.__dict__:\n            setattr(\n                self,\n                attribute,\n                getattr(self, attribute) or getattr(venv_problems, attribute),\n            )\n\n\ndef expose_resources_globally(\n    resource_type: str,\n    local_resource_dir: Path,\n    paths: list[Path],\n    *,\n    force: bool,\n    suffix: str = \"\",\n) -> None:\n    for path in paths:\n        src = path.resolve()\n        if resource_type == \"man\":\n            dest_dir = local_resource_dir / src.parent.name\n        else:\n            dest_dir = local_resource_dir\n        if not dest_dir.is_dir():\n            mkdir(dest_dir)\n        if not can_symlink(dest_dir):\n            _copy_package_resource(dest_dir, path, suffix=suffix)\n        else:\n            _symlink_package_resource(\n                dest_dir,\n                path,\n                force=force,\n                suffix=suffix,\n                executable=(resource_type == \"app\"),\n            )\n\n\n_can_symlink_cache: dict[Path, bool] = {}\n\n\ndef can_symlink(local_resource_dir: Path) -> bool:\n    if not WINDOWS:\n        # Technically, even on Unix this depends on the filesystem\n        return True\n\n    if local_resource_dir not in _can_symlink_cache:\n        with TemporaryDirectory(dir=local_resource_dir) as d:\n            p = Path(d)\n            target = p / \"a\"\n            target.touch()\n            lnk = p / \"b\"\n            try:\n                lnk.symlink_to(target)\n                _can_symlink_cache[local_resource_dir] = True\n            except (OSError, NotImplementedError):\n                _can_symlink_cache[local_resource_dir] = False\n\n    return _can_symlink_cache[local_resource_dir]\n\n\ndef _copy_package_resource(dest_dir: Path, path: Path, suffix: str = \"\") -> None:\n    src = path.resolve()\n    name = src.name\n    dest = Path(dest_dir / add_suffix(name, suffix))\n    if not dest.parent.is_dir():\n        mkdir(dest.parent)\n    if dest.exists():\n        if filecmp.cmp(dest, src, shallow=False):\n            return\n        logger.warning(f\"{hazard}  Overwriting file {dest!s} with {src!s}\")\n        safe_unlink(dest)\n    if src.exists():\n        shutil.copy(src, dest)\n\n\ndef _symlink_package_resource(\n    dest_dir: Path,\n    path: Path,\n    *,\n    force: bool,\n    suffix: str = \"\",\n    executable: bool = False,\n) -> None:\n    name_suffixed = add_suffix(path.name, suffix)\n    symlink_path = Path(dest_dir / name_suffixed)\n\n    if not symlink_path.parent.is_dir():\n        mkdir(symlink_path.parent)\n\n    if force:\n        logger.info(f\"Force is true. Removing {symlink_path!s}.\")\n        try:\n            symlink_path.unlink()\n        except (FileNotFoundError, RuntimeError):\n            pass\n        except IsADirectoryError:\n            rmdir(symlink_path)\n\n    exists = symlink_path.exists()\n    is_symlink = symlink_path.is_symlink()\n    if exists:\n        if symlink_path.samefile(path):\n            logger.info(f\"Same path {symlink_path!s} and {path!s}\")\n        else:\n            logger.warning(\n                pipx_wrap(\n                    f\"\"\"\n                    {hazard}  File exists at {symlink_path!s} and points\n                    to {symlink_path.resolve()}, not {path!s}. Not\n                    modifying.\n                    \"\"\",\n                    subsequent_indent=\" \" * 4,\n                )\n            )\n        return\n    if is_symlink and not exists:\n        logger.info(f\"Removing existing symlink {symlink_path!s} since it pointed non-existent location\")\n        symlink_path.unlink()\n\n    if executable:\n        existing_executable_on_path = which(name_suffixed)\n    else:\n        existing_executable_on_path = None\n    symlink_path.symlink_to(path)\n\n    if executable and existing_executable_on_path:\n        logger.warning(\n            pipx_wrap(\n                f\"\"\"\n                {hazard}  Note: {name_suffixed} was already on your\n                PATH at {existing_executable_on_path}\n                \"\"\",\n                subsequent_indent=\" \" * 4,\n            )\n        )\n\n\ndef venv_health_check(venv: Venv, package_name: Optional[str] = None) -> tuple[VenvProblems, str]:\n    venv_dir = venv.root\n    python_path = venv.python_path.resolve()\n\n    if package_name is None:\n        package_name = venv.main_package_name\n\n    if not python_path.is_file():\n        return (\n            VenvProblems(invalid_interpreter=True),\n            f\"   package {red(bold(venv_dir.name))} has invalid interpreter {python_path!s}\\r{hazard}\",\n        )\n    if not venv.package_metadata:\n        return (\n            VenvProblems(missing_metadata=True),\n            f\"   package {red(bold(venv_dir.name))} has missing internal pipx metadata.\\r{hazard}\",\n        )\n    if venv_dir.name != canonicalize_name(venv_dir.name):\n        return (\n            VenvProblems(bad_venv_name=True),\n            f\"   package {red(bold(venv_dir.name))} needs its internal data updated.\\r{hazard}\",\n        )\n    if venv.package_metadata[package_name].package_version == \"\":\n        return (\n            VenvProblems(not_installed=True),\n            f\"   package {red(bold(package_name))} {red('is not installed')} in the venv {venv_dir.name}\\r{hazard}\",\n        )\n    return (VenvProblems(), \"\")\n\n\ndef get_venv_summary(\n    venv_dir: Path,\n    *,\n    package_name: Optional[str] = None,\n    new_install: bool = False,\n    include_injected: bool = False,\n) -> tuple[str, VenvProblems]:\n    venv = Venv(venv_dir)\n\n    if package_name is None:\n        package_name = venv.main_package_name\n\n    (venv_problems, warning_message) = venv_health_check(venv, package_name)\n    if venv_problems.any_():\n        return (warning_message, venv_problems)\n\n    package_metadata = venv.package_metadata[package_name]\n    apps = package_metadata.apps\n    man_pages = package_metadata.man_pages\n    if package_metadata.include_dependencies:\n        apps += package_metadata.apps_of_dependencies\n        man_pages += package_metadata.man_pages_of_dependencies\n\n    exposed_app_paths = get_exposed_paths_for_package(\n        venv.bin_path,\n        paths.ctx.bin_dir,\n        [add_suffix(app, package_metadata.suffix) for app in apps],\n    )\n    exposed_binary_names = sorted(p.name for p in exposed_app_paths)\n    unavailable_binary_names = sorted(\n        {add_suffix(name, package_metadata.suffix) for name in package_metadata.apps} - set(exposed_binary_names)\n    )\n    exposed_man_paths = set()\n    for man_section in MAN_SECTIONS:\n        exposed_man_paths |= get_exposed_man_paths_for_package(\n            venv.man_path / man_section,\n            paths.ctx.man_dir / man_section,\n            man_pages,\n        )\n    exposed_man_pages = sorted(str(Path(p.parent.name) / p.name) for p in exposed_man_paths)\n    unavailable_man_pages = sorted(set(package_metadata.man_pages) - set(exposed_man_pages))\n    # The following is to satisfy mypy that python_version is str and not\n    #   Optional[str]\n    python_version = venv.pipx_metadata.python_version if venv.pipx_metadata.python_version is not None else \"\"\n    source_interpreter = venv.pipx_metadata.source_interpreter\n    is_standalone = (\n        str(source_interpreter).startswith(str(paths.ctx.standalone_python_cachedir.resolve()))\n        if source_interpreter\n        else False\n    )\n    return (\n        _get_list_output(\n            python_version,\n            is_standalone,\n            package_metadata.package_version,\n            package_name,\n            new_install,\n            exposed_binary_names,\n            unavailable_binary_names,\n            exposed_man_pages,\n            unavailable_man_pages,\n            venv.pipx_metadata.injected_packages if include_injected else None,\n            suffix=package_metadata.suffix,\n        ),\n        venv_problems,\n    )\n\n\ndef get_exposed_paths_for_package(\n    venv_resource_path: Path,\n    local_resource_dir: Path,\n    package_resource_names: Optional[list[str]] = None,\n) -> set[Path]:\n    # package_binary_names is used only if local_bin_path cannot use symlinks.\n    # It is necessary for non-symlink systems to return valid app_paths.\n    if package_resource_names is None:\n        package_resource_names = []\n\n    if not local_resource_dir.exists():\n        return set()\n\n    symlinks = set()\n    for b in local_resource_dir.iterdir():\n        try:\n            # sometimes symlinks can resolve to a file of a different name\n            # (in the case of ansible for example) so checking the resolved paths\n            # is not a reliable way to determine if the symlink exists.\n            # We always use the stricter check on non-Windows systems. On\n            # Windows, we use a less strict check if we don't have a symlink.\n            is_same_file = False\n            if can_symlink(local_resource_dir) and b.is_symlink():\n                is_same_file = b.resolve().parent.samefile(venv_resource_path)\n            elif not can_symlink(local_resource_dir):\n                is_same_file = b.name in package_resource_names\n\n            if is_same_file:\n                symlinks.add(b)\n\n        except (FileNotFoundError, RuntimeError):\n            pass\n    return symlinks\n\n\ndef get_exposed_man_paths_for_package(\n    venv_man_path: Path,\n    local_man_dir: Path,\n    package_man_pages: Optional[list[str]] = None,\n) -> set[Path]:\n    man_section = venv_man_path.name\n    prefix = man_section + os.sep\n    return get_exposed_paths_for_package(\n        venv_man_path,\n        local_man_dir,\n        [name.removeprefix(prefix) for name in package_man_pages or [] if name.startswith(prefix)],\n    )\n\n\ndef _get_list_output(\n    python_version: str,\n    python_is_standalone: bool,\n    package_version: str,\n    package_name: str,\n    new_install: bool,\n    exposed_binary_names: list[str],\n    unavailable_binary_names: list[str],\n    exposed_man_pages: list[str],\n    unavailable_man_pages: list[str],\n    injected_packages: Optional[dict[str, PackageInfo]] = None,\n    suffix: str = \"\",\n) -> str:\n    output = []\n    suffix = f\" ({bold(shlex.quote(package_name + suffix))})\" if suffix else \"\"\n    output.append(\n        f\"  {'installed' if new_install else ''} package {bold(shlex.quote(package_name))}\"\n        f\" {bold(package_version)}{suffix}, installed using {python_version}\"\n        + (\" (standalone)\" if python_is_standalone else \"\")\n    )\n\n    if new_install and (exposed_binary_names or unavailable_binary_names):\n        output.append(\"  These apps are now available\")\n    output.extend(f\"    - {name}\" for name in exposed_binary_names)\n    output.extend(\n        f\"    - {red(name)} (symlink missing or pointing to unexpected location)\" for name in unavailable_binary_names\n    )\n    if new_install and (exposed_man_pages or unavailable_man_pages):\n        output.append(\"  These manual pages are now available\")\n    output.extend(f\"    - {name}\" for name in exposed_man_pages)\n    output.extend(\n        f\"    - {red(name)} (symlink missing or pointing to unexpected location)\" for name in unavailable_man_pages\n    )\n    if injected_packages:\n        output.append(\"    Injected Packages:\")\n        output.extend(f\"      - {name} {injected_packages[name].package_version}\" for name in injected_packages)\n    return \"\\n\".join(output)\n\n\ndef package_name_from_spec(package_spec: str, python: str, *, pip_args: list[str], verbose: bool) -> str:\n    start_time = time.time()\n\n    # shortcut if valid PyPI name\n    pypi_name = valid_pypi_name(package_spec)\n    if pypi_name is not None:\n        # NOTE: if pypi name and installed package name differ, this means pipx\n        #       will use the pypi name\n        package_name = pypi_name\n        logger.info(f\"Determined package name: {package_name}\")\n        logger.info(f\"Package name determined in {time.time() - start_time:.1f}s\")\n        return package_name\n\n    # check syntax and clean up spec and pip_args\n    (package_spec, pip_args) = parse_specifier_for_install(package_spec, pip_args)\n\n    with tempfile.TemporaryDirectory() as temp_venv_dir:\n        venv = Venv(Path(temp_venv_dir), python=python, verbose=verbose)\n        venv.create_venv(venv_args=[], pip_args=[])\n        package_name = venv.install_package_no_deps(package_or_url=package_spec, pip_args=pip_args)\n\n    logger.info(f\"Package name determined in {time.time() - start_time:.1f}s\")\n    return package_name\n\n\ndef run_post_install_actions(\n    venv: Venv,\n    package_name: str,\n    local_bin_dir: Path,\n    local_man_dir: Path,\n    venv_dir: Path,\n    include_dependencies: bool,\n    *,\n    force: bool,\n) -> None:\n    package_metadata = venv.package_metadata[package_name]\n\n    display_name = f\"{package_name}{package_metadata.suffix}\"\n\n    if (\n        not venv.main_package_name == package_name\n        and venv.package_metadata[venv.main_package_name].suffix == package_metadata.suffix\n    ):\n        package_name = display_name\n\n    if not package_metadata.apps:\n        if not package_metadata.apps_of_dependencies:\n            if venv.safe_to_remove():\n                venv.remove_venv()\n            raise PipxError(\n                f\"\"\"\n                No apps associated with package {display_name} or its\n                dependencies. If you are attempting to install a library, pipx\n                should not be used. Consider using pip or a similar tool instead.\n                \"\"\"\n            )\n        if package_metadata.apps_of_dependencies and not include_dependencies:\n            for (\n                dep,\n                dependent_apps,\n            ) in package_metadata.app_paths_of_dependencies.items():\n                print(f\"Note: Dependent package '{dep}' contains {len(dependent_apps)} apps\")\n                for app in dependent_apps:\n                    print(f\"  - {app.name}\")\n            if venv.safe_to_remove():\n                venv.remove_venv()\n            raise PipxError(\n                f\"\"\"\n                No apps associated with package {display_name}. Try again\n                with '--include-deps' to include apps of dependent packages,\n                which are listed above. If you are attempting to install a\n                library, pipx should not be used. Consider using pip or a\n                similar tool instead.\n                \"\"\"\n            )\n\n    expose_resources_globally(\n        \"app\",\n        local_bin_dir,\n        package_metadata.app_paths,\n        force=force,\n        suffix=package_metadata.suffix,\n    )\n    expose_resources_globally(\"man\", local_man_dir, package_metadata.man_paths, force=force)\n\n    if include_dependencies:\n        for app_paths in package_metadata.app_paths_of_dependencies.values():\n            expose_resources_globally(\n                \"app\",\n                local_bin_dir,\n                app_paths,\n                force=force,\n                suffix=package_metadata.suffix,\n            )\n        for man_paths in package_metadata.man_paths_of_dependencies.values():\n            expose_resources_globally(\"man\", local_man_dir, man_paths, force=force)\n\n    package_summary, _ = get_venv_summary(venv_dir, package_name=package_name, new_install=True)\n    pipx_logger = logging.getLogger(\"pipx\")\n    if not pipx_logger.handlers or pipx_logger.handlers[0].level <= logging.WARNING:\n        print(package_summary)\n        warn_if_not_on_path(local_bin_dir)\n        print(f\"done! {stars}\", file=sys.stderr)\n\n\ndef warn_if_not_on_path(local_bin_dir: Path) -> None:\n    if not userpath.in_current_path(str(local_bin_dir)):\n        logger.warning(\n            pipx_wrap(\n                f\"\"\"\n                {hazard}  Note: '{local_bin_dir}' is not on your PATH\n                environment variable. These apps will not be globally\n                accessible until your PATH is updated. Run `pipx ensurepath` to\n                automatically add it, or manually modify your PATH in your\n                shell's config file (e.g. ~/.bashrc).\n                \"\"\",\n                subsequent_indent=\" \" * 4,\n            )\n        )\n\n\ndef add_suffix(name: str, suffix: str) -> str:\n    \"\"\"Add suffix to app.\"\"\"\n\n    app = Path(name)\n    return f\"{app.stem}{suffix}{app.suffix}\"\n"
  },
  {
    "path": "src/pipx/commands/ensure_path.py",
    "content": "import logging\nimport site\nimport sys\nfrom pathlib import Path\nfrom typing import Optional\n\nimport userpath  # type: ignore[import-not-found]\n\nfrom pipx import paths\nfrom pipx.constants import EXIT_CODE_OK, ExitCode\nfrom pipx.emojis import hazard, stars\nfrom pipx.util import pipx_wrap\n\nlogger = logging.getLogger(__name__)\n\n\ndef get_pipx_user_bin_path() -> Optional[Path]:\n    \"\"\"Returns None if pipx is not installed using `pip --user`\n    Otherwise returns parent dir of pipx binary\n    \"\"\"\n    # NOTE: using this method to detect pip user-installed pipx will return\n    #   None if pipx was installed as editable using `pip install --user -e`\n\n    # https://docs.python.org/3/install/index.html#inst-alt-install-user\n    #   Linux + Mac:\n    #       scripts in <userbase>/bin\n    #   Windows:\n    #       scripts in <userbase>/Python<XY>/Scripts\n    #       modules in <userbase>/Python<XY>/site-packages\n\n    pipx_bin_path = None\n\n    script_path = Path(__file__).resolve()\n    userbase_path = Path(site.getuserbase()).resolve()\n    try:\n        _ = script_path.relative_to(userbase_path)\n    except ValueError:\n        pip_user_installed = False\n    else:\n        pip_user_installed = True\n    if pip_user_installed:\n        test_paths = (\n            userbase_path / \"bin\" / \"pipx\",\n            Path(site.getusersitepackages()).resolve().parent / \"Scripts\" / \"pipx.exe\",\n        )\n        for test_path in test_paths:\n            if test_path.exists():\n                pipx_bin_path = test_path.parent\n                break\n\n    return pipx_bin_path\n\n\ndef ensure_path(location: Path, *, force: bool, prepend: bool = False, all_shells: bool = False) -> tuple[bool, bool]:\n    \"\"\"Ensure location is in user's PATH or add it to PATH.\n    If prepend is True, location will be prepended to PATH, else appended.\n    Returns True if location was added to PATH\n    \"\"\"\n    location_str = str(location)\n    path_added = False\n    need_shell_restart = userpath.need_shell_restart(location_str)\n    in_current_path = userpath.in_current_path(location_str)\n\n    if force or (not in_current_path and not need_shell_restart):\n        if prepend:\n            path_added = userpath.prepend(location_str, \"pipx\", all_shells=all_shells)\n        else:\n            path_added = userpath.append(location_str, \"pipx\", all_shells=all_shells)\n        if not path_added:\n            print(\n                pipx_wrap(\n                    f\"{hazard}  {location_str} is not added to the PATH environment variable successfully. \"\n                    f\"You may need to add it to PATH manually.\",\n                    subsequent_indent=\" \" * 4,\n                )\n            )\n        else:\n            print(\n                pipx_wrap(\n                    f\"Success! Added {location_str} to the PATH environment variable.\",\n                    subsequent_indent=\" \" * 4,\n                )\n            )\n        need_shell_restart = userpath.need_shell_restart(location_str)\n    elif not in_current_path and need_shell_restart:\n        print(\n            pipx_wrap(\n                f\"\"\"\n                {location_str} has been added to PATH, but you need to\n                open a new terminal or re-login for this PATH change to take\n                effect. Alternatively, you can source your shell's config file\n                with e.g. 'source ~/.bashrc'.\n                \"\"\",\n                subsequent_indent=\" \" * 4,\n            )\n        )\n    else:\n        print(pipx_wrap(f\"{location_str} is already in PATH.\", subsequent_indent=\" \" * 4))\n\n    return (path_added, need_shell_restart)\n\n\ndef ensure_pipx_paths(force: bool, prepend: bool = False, all_shells: bool = False) -> ExitCode:\n    \"\"\"Returns pipx exit code.\"\"\"\n    bin_paths = {paths.ctx.bin_dir}\n\n    pipx_user_bin_path = get_pipx_user_bin_path()\n    if pipx_user_bin_path is not None:\n        bin_paths.add(pipx_user_bin_path)\n\n    path_added = False\n    need_shell_restart = False\n    path_action_str = \"prepended to\" if prepend else \"appended to\"\n\n    for bin_path in bin_paths:\n        (path_added_current, need_shell_restart_current) = ensure_path(\n            bin_path, prepend=prepend, force=force, all_shells=all_shells\n        )\n        path_added |= path_added_current\n        need_shell_restart |= need_shell_restart_current\n\n    print()\n\n    if path_added:\n        print(\n            pipx_wrap(\n                \"\"\"\n                Consider adding shell completions for pipx. Run 'pipx\n                completions' for instructions.\n                \"\"\"\n            )\n            + \"\\n\"\n        )\n    elif not need_shell_restart:\n        sys.stdout.flush()\n        logger.warning(\n            pipx_wrap(\n                f\"\"\"\n                {hazard}  All pipx binary directories have been {path_action_str} PATH. If you\n                are sure you want to proceed, try again with the '--force'\n                flag.\n                \"\"\"\n            )\n            + \"\\n\"\n        )\n\n    if need_shell_restart:\n        print(\n            pipx_wrap(\n                \"\"\"\n                You will need to open a new terminal or re-login for the PATH\n                changes to take effect. Alternatively, you can source your shell's\n                config file with e.g. 'source ~/.bashrc'.\n                \"\"\"\n            )\n            + \"\\n\"\n        )\n\n    print(f\"Otherwise pipx is ready to go! {stars}\")\n\n    return EXIT_CODE_OK\n"
  },
  {
    "path": "src/pipx/commands/environment.py",
    "content": "import os\n\nfrom pipx import paths\nfrom pipx.constants import EXIT_CODE_OK, ExitCode\nfrom pipx.emojis import EMOJI_SUPPORT\nfrom pipx.interpreter import DEFAULT_PYTHON\nfrom pipx.util import PipxError\n\nENVIRONMENT_VARIABLES = [\n    \"PIPX_HOME\",\n    \"PIPX_GLOBAL_HOME\",\n    \"PIPX_BIN_DIR\",\n    \"PIPX_GLOBAL_BIN_DIR\",\n    \"PIPX_MAN_DIR\",\n    \"PIPX_GLOBAL_MAN_DIR\",\n    \"PIPX_SHARED_LIBS\",\n    \"PIPX_DEFAULT_PYTHON\",\n    \"PIPX_FETCH_MISSING_PYTHON\",\n    \"PIPX_USE_EMOJI\",\n    \"PIPX_HOME_ALLOW_SPACE\",\n]\nDERIVED_ENVIRONMENT_VARIABLES = [\n    \"PIPX_LOCAL_VENVS\",\n    \"PIPX_LOG_DIR\",\n    \"PIPX_TRASH_DIR\",\n    \"PIPX_VENV_CACHEDIR\",\n    \"PIPX_STANDALONE_PYTHON_CACHEDIR\",\n]\nENVIRONMENT_VALUE_CHOICES = ENVIRONMENT_VARIABLES + DERIVED_ENVIRONMENT_VARIABLES\n\n\ndef environment(value: str) -> ExitCode:\n    \"\"\"Print a list of environment variables and paths used by pipx\"\"\"\n    derived_values = {\n        \"PIPX_HOME\": paths.ctx.home,\n        \"PIPX_BIN_DIR\": paths.ctx.bin_dir,\n        \"PIPX_MAN_DIR\": paths.ctx.man_dir,\n        \"PIPX_SHARED_LIBS\": paths.ctx.shared_libs,\n        \"PIPX_LOCAL_VENVS\": paths.ctx.venvs,\n        \"PIPX_LOG_DIR\": paths.ctx.logs,\n        \"PIPX_TRASH_DIR\": paths.ctx.trash,\n        \"PIPX_VENV_CACHEDIR\": paths.ctx.venv_cache,\n        \"PIPX_STANDALONE_PYTHON_CACHEDIR\": paths.ctx.standalone_python_cachedir,\n        \"PIPX_DEFAULT_PYTHON\": DEFAULT_PYTHON,\n        \"PIPX_USE_EMOJI\": str(EMOJI_SUPPORT).lower(),\n        \"PIPX_HOME_ALLOW_SPACE\": str(paths.ctx.allow_spaces_in_home_path).lower(),\n    }\n    if value is None:\n        print(\"Environment variables (set by user):\")  # type: ignore[unreachable]\n        print(\"\")\n        for env_variable in ENVIRONMENT_VARIABLES:\n            env_value = os.getenv(env_variable, \"\")\n            print(f\"{env_variable}={env_value}\")\n        print(\"\")\n        print(\"Derived values (computed by pipx):\")\n        print(\"\")\n        for env_variable, derived_value in derived_values.items():\n            print(f\"{env_variable}={derived_value}\")\n    elif value in derived_values:\n        print(derived_values[value])\n    else:\n        raise PipxError(\"Variable not found.\")\n\n    return EXIT_CODE_OK\n"
  },
  {
    "path": "src/pipx/commands/inject.py",
    "content": "import logging\nimport os\nimport re\nimport sys\nfrom collections.abc import Generator, Iterable\nfrom pathlib import Path\nfrom typing import Optional, Union\n\nfrom pipx import paths\nfrom pipx.colors import bold\nfrom pipx.commands.common import package_name_from_spec, run_post_install_actions\nfrom pipx.constants import EXIT_CODE_INJECT_ERROR, EXIT_CODE_OK, ExitCode\nfrom pipx.emojis import hazard, stars\nfrom pipx.util import PipxError, pipx_wrap\nfrom pipx.venv import Venv\n\nlogger = logging.getLogger(__name__)\n\nCOMMENT_RE = re.compile(r\"(^|\\s+)#.*$\")\n\n\ndef inject_dep(\n    venv_dir: Path,\n    package_name: Optional[str],\n    package_spec: str,\n    pip_args: list[str],\n    *,\n    verbose: bool,\n    include_apps: bool,\n    include_dependencies: bool,\n    force: bool,\n    suffix: bool = False,\n) -> bool:\n    logger.debug(\"Injecting package %s\", package_spec)\n\n    if not venv_dir.exists() or next(venv_dir.iterdir(), None) is None:\n        raise PipxError(\n            f\"\"\"\n            Can't inject {package_spec!r} into nonexistent Virtual Environment\n            {venv_dir.name!r}. Be sure to install the package first with 'pipx\n            install {venv_dir.name}' before injecting into it.\n            \"\"\"\n        )\n\n    venv = Venv(venv_dir, verbose=verbose)\n    venv.check_upgrade_shared_libs(pip_args=pip_args, verbose=verbose)\n\n    if not venv.package_metadata:\n        raise PipxError(\n            f\"\"\"\n            Can't inject {package_spec!r} into Virtual Environment\n            {venv.name!r}. {venv.name!r} has missing internal pipx metadata. It\n            was likely installed using a pipx version before 0.15.0.0. Please\n            uninstall and install {venv.name!r}, or reinstall-all to fix.\n            \"\"\"\n        )\n\n    # package_spec is anything pip-installable, including package_name, vcs spec,\n    #   zip file, or tar.gz file.\n    if package_name is None:\n        package_name = package_name_from_spec(\n            package_spec,\n            os.fspath(venv.python_path),\n            pip_args=pip_args,\n            verbose=verbose,\n        )\n\n    if not force and venv.has_package(package_name):\n        logger.info(\"Package %s has already been injected\", package_name)\n        print(\n            pipx_wrap(\n                f\"\"\"\n                {hazard} {package_name} already seems to be injected in {venv.name!r}.\n                Not modifying existing installation in '{venv_dir}'.\n                Pass '--force' to force installation.\n                \"\"\"\n            )\n        )\n        return True\n\n    if suffix:\n        venv_suffix = venv.package_metadata[venv.main_package_name].suffix\n    else:\n        venv_suffix = \"\"\n    venv.install_package(\n        package_name=package_name,\n        package_or_url=package_spec,\n        pip_args=pip_args,\n        include_dependencies=include_dependencies,\n        include_apps=include_apps,\n        is_main_package=False,\n        suffix=venv_suffix,\n    )\n    if include_apps:\n        run_post_install_actions(\n            venv,\n            package_name,\n            paths.ctx.bin_dir,\n            paths.ctx.man_dir,\n            venv_dir,\n            include_dependencies,\n            force=force,\n        )\n\n    print(f\"  injected package {bold(package_name)} into venv {bold(venv.name)}\")\n    print(f\"done! {stars}\", file=sys.stderr)\n\n    # Any failure to install will raise PipxError, otherwise success\n    return True\n\n\ndef inject(\n    venv_dir: Path,\n    package_specs: Iterable[str],\n    requirement_files: Iterable[str],\n    pip_args: list[str],\n    *,\n    verbose: bool,\n    include_apps: bool,\n    include_dependencies: bool,\n    force: bool,\n    suffix: bool = False,\n) -> ExitCode:\n    \"\"\"Returns pipx exit code.\"\"\"\n    # Combined collection of package specifications\n    packages = list(package_specs)\n    for filename in requirement_files:\n        packages.extend(parse_requirements(filename))\n\n    # Remove duplicates and order deterministically\n    packages = sorted(set(packages))\n\n    if not packages:\n        raise PipxError(\"No packages have been specified.\")\n    logger.info(\"Injecting packages: %r\", packages)\n\n    # Inject packages\n    if not include_apps and include_dependencies:\n        include_apps = True\n    all_success = True\n    for dep in packages:\n        all_success &= inject_dep(\n            venv_dir,\n            package_name=None,\n            package_spec=dep,\n            pip_args=pip_args,\n            verbose=verbose,\n            include_apps=include_apps,\n            include_dependencies=include_dependencies,\n            force=force,\n            suffix=suffix,\n        )\n\n    # Any failure to install will raise PipxError, otherwise success\n    return EXIT_CODE_OK if all_success else EXIT_CODE_INJECT_ERROR\n\n\ndef parse_requirements(filename: Union[str, os.PathLike]) -> Generator[str, None, None]:\n    \"\"\"\n    Extract package specifications from requirements file.\n\n    Return all of the non-empty lines with comments removed.\n    \"\"\"\n    # Based on https://github.com/pypa/pip/blob/main/src/pip/_internal/req/req_file.py\n    with open(filename) as f:\n        for line in f:\n            # Strip comments and filter empty lines\n            if pkgspec := COMMENT_RE.sub(\"\", line).strip():\n                yield pkgspec\n"
  },
  {
    "path": "src/pipx/commands/install.py",
    "content": "import json\nimport sys\nfrom collections.abc import Iterator\nfrom pathlib import Path\nfrom typing import Optional\n\nfrom pipx import commands, paths\nfrom pipx.commands.common import package_name_from_spec, run_post_install_actions\nfrom pipx.constants import (\n    EXIT_CODE_INSTALL_VENV_EXISTS,\n    EXIT_CODE_OK,\n    ExitCode,\n)\nfrom pipx.emojis import sleep\nfrom pipx.interpreter import DEFAULT_PYTHON\nfrom pipx.pipx_metadata_file import PackageInfo, PipxMetadata, _json_decoder_object_hook\nfrom pipx.util import PipxError, pipx_wrap\nfrom pipx.venv import Venv, VenvContainer\n\n\ndef install(\n    venv_dir: Optional[Path],\n    package_names: Optional[list[str]],\n    package_specs: list[str],\n    local_bin_dir: Path,\n    local_man_dir: Path,\n    python: Optional[str],\n    pip_args: list[str],\n    venv_args: list[str],\n    verbose: bool,\n    *,\n    force: bool,\n    reinstall: bool,\n    include_dependencies: bool,\n    preinstall_packages: Optional[list[str]],\n    suffix: str = \"\",\n    python_flag_passed=False,\n) -> ExitCode:\n    \"\"\"Returns pipx exit code.\"\"\"\n    # package_spec is anything pip-installable, including package_name, vcs spec,\n    #   zip file, or tar.gz file.\n\n    python = python or DEFAULT_PYTHON\n\n    package_names = package_names or []\n    if len(package_names) != len(package_specs):\n        package_names = [\n            package_name_from_spec(package_spec, python, pip_args=pip_args, verbose=verbose)\n            for package_spec in package_specs\n        ]\n\n    for package_name, package_spec in zip(package_names, package_specs):\n        if venv_dir is None:\n            venv_container = VenvContainer(paths.ctx.venvs)\n            venv_dir = venv_container.get_venv_dir(f\"{package_name}{suffix}\")\n\n        try:\n            exists = venv_dir.exists() and bool(next(venv_dir.iterdir()))\n        except StopIteration:\n            exists = False\n\n        venv = Venv(venv_dir, python=python, verbose=verbose)\n        venv.check_upgrade_shared_libs(pip_args=pip_args, verbose=verbose)\n        if exists:\n            if not reinstall and force and python_flag_passed:\n                print(\n                    pipx_wrap(\n                        f\"\"\"\n                        --python is ignored when --force is passed.\n                        If you want to reinstall {package_name} with {python},\n                        run `pipx reinstall {package_spec} --python {python}` instead.\n                        \"\"\"\n                    )\n                )\n            if force:\n                print(f\"Installing to existing venv {venv.name!r}\")\n                pip_args = [\"--force-reinstall\"] + pip_args\n            else:\n                print(\n                    pipx_wrap(\n                        f\"\"\"\n                        {venv.name!r} already seems to be installed. Not modifying\n                        existing installation in '{venv_dir}'. Pass '--force'\n                        to force installation.\n                        \"\"\"\n                    )\n                )\n                if len(package_specs) == 1:\n                    return EXIT_CODE_INSTALL_VENV_EXISTS\n                # Reset venv_dir to None ready to install the next package in the list\n                venv_dir = None\n                continue\n\n        try:\n            # Enable installing shared library `pip` with `pipx`\n            override_shared = package_name == \"pip\"\n            venv.create_venv(venv_args, pip_args, override_shared)\n            for dep in preinstall_packages or []:\n                venv.upgrade_package_no_metadata(dep, [])\n            venv.install_package(\n                package_name=package_name,\n                package_or_url=package_spec,\n                pip_args=pip_args,\n                include_dependencies=include_dependencies,\n                include_apps=True,\n                is_main_package=True,\n                suffix=suffix,\n            )\n            run_post_install_actions(\n                venv,\n                package_name,\n                local_bin_dir,\n                local_man_dir,\n                venv_dir,\n                include_dependencies,\n                force=force,\n            )\n        except (Exception, KeyboardInterrupt):\n            print()\n            venv.remove_venv()\n            raise\n\n        # Reset venv_dir to None ready to install the next package in the list\n        venv_dir = None\n\n    # Any failure to install will raise PipxError, otherwise success\n    return EXIT_CODE_OK\n\n\ndef extract_venv_metadata(spec_metadata_file: Path) -> Iterator[PipxMetadata]:\n    \"\"\"Extract venv metadata from spec metadata file.\"\"\"\n    with open(spec_metadata_file) as spec_metadata_fh:\n        try:\n            spec_metadata_dict = json.load(spec_metadata_fh, object_hook=_json_decoder_object_hook)\n        except json.decoder.JSONDecodeError as exc:\n            raise PipxError(\"The spec metadata file is an invalid JSON file.\") from exc\n\n        if not (\"venvs\" in spec_metadata_dict and len(spec_metadata_dict[\"venvs\"])):\n            raise PipxError(\"No packages found in the spec metadata file.\")\n\n        venvs_metadata_dict = spec_metadata_dict[\"venvs\"]\n\n        if not isinstance(venvs_metadata_dict, dict):\n            raise PipxError(\"The spec metadata file is invalid.\")\n\n        for package_path_name in venvs_metadata_dict:\n            venv_dir = paths.ctx.venvs.joinpath(package_path_name)\n            venv_metadata = PipxMetadata(venv_dir, read=False)\n            venv_metadata.from_dict(venvs_metadata_dict[package_path_name][\"metadata\"])\n            yield venv_metadata\n\n\ndef generate_package_spec(package_info: PackageInfo) -> str:\n    \"\"\"Generate more precise package spec from package info.\"\"\"\n    if not package_info.package_or_url:\n        raise PipxError(f\"A package spec is not available for {package_info.package}\")\n\n    if package_info.package == package_info.package_or_url:\n        return f\"{package_info.package}=={package_info.package_version}\"\n    return package_info.package_or_url\n\n\ndef get_python_interpreter(\n    source_interpreter: Optional[Path],\n) -> Optional[str]:\n    \"\"\"Get appropriate python interpreter.\"\"\"\n    if source_interpreter is not None and source_interpreter.is_file():\n        return str(source_interpreter)\n\n    print(\n        pipx_wrap(\n            f\"\"\"\n            The exported python interpreter '{source_interpreter}' is ignored\n            as not found.\n            \"\"\"\n        )\n    )\n\n    return None\n\n\ndef install_all(\n    spec_metadata_file: Path,\n    local_bin_dir: Path,\n    local_man_dir: Path,\n    python: Optional[str],\n    pip_args: list[str],\n    venv_args: list[str],\n    verbose: bool,\n    *,\n    force: bool,\n) -> ExitCode:\n    \"\"\"Return pipx exit code.\"\"\"\n    venv_container = VenvContainer(paths.ctx.venvs)\n    failed: list[str] = []\n    installed: list[str] = []\n\n    for venv_metadata in extract_venv_metadata(spec_metadata_file):\n        # Install the main package\n        main_package = venv_metadata.main_package\n        venv_dir = venv_container.get_venv_dir(f\"{main_package.package}{main_package.suffix}\")\n        try:\n            install(\n                venv_dir,\n                None,\n                [generate_package_spec(main_package)],\n                local_bin_dir,\n                local_man_dir,\n                python or get_python_interpreter(venv_metadata.source_interpreter),\n                pip_args,\n                venv_args,\n                verbose,\n                force=force,\n                reinstall=False,\n                include_dependencies=main_package.include_dependencies,\n                preinstall_packages=[],\n                suffix=main_package.suffix,\n            )\n\n            # Install the injected packages\n            for inject_package in venv_metadata.injected_packages.values():\n                commands.inject(\n                    venv_dir=venv_dir,\n                    package_specs=[generate_package_spec(inject_package)],\n                    requirement_files=[],\n                    pip_args=pip_args,\n                    verbose=verbose,\n                    include_apps=inject_package.include_apps,\n                    include_dependencies=inject_package.include_dependencies,\n                    force=force,\n                    suffix=inject_package.suffix == main_package.suffix,\n                )\n        except PipxError as e:\n            print(e, file=sys.stderr)\n            failed.append(venv_dir.name)\n        else:\n            installed.append(venv_dir.name)\n    if len(installed) == 0:\n        print(f\"No packages installed after running 'pipx install-all {spec_metadata_file}' {sleep}\")\n    if len(failed) > 0:\n        raise PipxError(f\"The following package(s) failed to install: {', '.join(failed)}\")\n    # Any failure to install will raise PipxError, otherwise success\n    return EXIT_CODE_OK\n"
  },
  {
    "path": "src/pipx/commands/interpreter.py",
    "content": "import logging\nimport subprocess\nfrom pathlib import Path\n\nfrom packaging import version\n\nfrom pipx import commands, constants, paths, standalone_python\nfrom pipx.animate import animate\nfrom pipx.pipx_metadata_file import PipxMetadata\nfrom pipx.util import is_paths_relative, rmdir\nfrom pipx.venv import Venv, VenvContainer\n\nlogger = logging.getLogger(__name__)\n\n\ndef get_installed_standalone_interpreters() -> list[Path]:\n    return [python_dir for python_dir in paths.ctx.standalone_python_cachedir.iterdir() if python_dir.is_dir()]\n\n\ndef get_venvs_using_standalone_interpreter(venv_container: VenvContainer) -> list[Venv]:\n    venvs: list[Venv] = []\n    for venv_dir in venv_container.iter_venv_dirs():\n        venv = Venv(venv_dir)\n        if venv.pipx_metadata.source_interpreter:\n            venvs.append(venv)\n    return venvs\n\n\ndef get_interpreter_users(interpreter: Path, venvs: list[Venv]) -> list[PipxMetadata]:\n    return [\n        venv.pipx_metadata\n        for venv in venvs\n        if venv.pipx_metadata.source_interpreter\n        and is_paths_relative(venv.pipx_metadata.source_interpreter, interpreter)\n    ]\n\n\ndef list_interpreters(\n    venv_container: VenvContainer,\n):\n    interpreters = get_installed_standalone_interpreters()\n    venvs = get_venvs_using_standalone_interpreter(venv_container)\n    output: list[str] = []\n    output.append(f\"Standalone interpreters are in {paths.ctx.standalone_python_cachedir}\")\n    for interpreter in interpreters:\n        output.append(f\"Python {interpreter.name}\")\n        used_in = get_interpreter_users(interpreter, venvs)\n        if used_in:\n            output.append(\"    Used in:\")\n            output.extend(f\"     - {p.main_package.package} {p.main_package.package_version}\" for p in used_in)\n        else:\n            output.append(\"    Unused\")\n\n    print(\"\\n\".join(output))\n    return constants.EXIT_CODE_OK\n\n\ndef prune_interpreters(\n    venv_container: VenvContainer,\n):\n    interpreters = get_installed_standalone_interpreters()\n    venvs = get_venvs_using_standalone_interpreter(venv_container)\n    removed = []\n    for interpreter in interpreters:\n        if get_interpreter_users(interpreter, venvs):\n            continue\n        rmdir(interpreter, safe_rm=True)\n        removed.append(interpreter.name)\n    if removed:\n        print(\"Successfully removed:\")\n        for interpreter_name in removed:\n            print(f\" - Python {interpreter_name}\")\n    else:\n        print(\"Nothing to remove\")\n    return constants.EXIT_CODE_OK\n\n\ndef get_latest_micro_version(\n    current_version: version.Version, latest_python_versions: list[version.Version]\n) -> version.Version:\n    for latest_python_version in latest_python_versions:\n        if current_version.major == latest_python_version.major and current_version.minor == latest_python_version.minor:\n            return latest_python_version\n    return current_version\n\n\ndef upgrade_interpreters(venv_container: VenvContainer, verbose: bool):\n    with animate(\"Getting the index of the latest standalone python builds\", not verbose):\n        latest_pythons = standalone_python.list_pythons(use_cache=False)\n\n    parsed_latest_python_versions = []\n    for latest_python_version in latest_pythons:\n        try:\n            parsed_latest_python_versions.append(version.parse(latest_python_version))\n        except version.InvalidVersion:\n            logger.info(f\"Invalid version found in latest pythons: {latest_python_version}. Skipping.\")\n\n    upgraded = []\n\n    for interpreter_dir in paths.ctx.standalone_python_cachedir.iterdir():\n        if not interpreter_dir.is_dir():\n            continue\n\n        interpreter_python = interpreter_dir / \"python.exe\" if constants.WINDOWS else interpreter_dir / \"bin\" / \"python3\"\n        interpreter_full_version = (\n            subprocess.run([str(interpreter_python), \"--version\"], stdout=subprocess.PIPE, check=True, text=True)\n            .stdout.strip()\n            .split()[1]\n        )\n        try:\n            parsed_interpreter_full_version = version.parse(interpreter_full_version)\n        except version.InvalidVersion:\n            logger.info(f\"Invalid version found in interpreter at {interpreter_dir}. Skipping.\")\n            continue\n        latest_micro_version = get_latest_micro_version(parsed_interpreter_full_version, parsed_latest_python_versions)\n        if latest_micro_version > parsed_interpreter_full_version:\n            standalone_python.download_python_build_standalone(\n                f\"{latest_micro_version.major}.{latest_micro_version.minor}\",\n                override=True,\n            )\n\n            for venv_dir in venv_container.iter_venv_dirs():\n                venv = Venv(venv_dir)\n                if venv.pipx_metadata.source_interpreter is not None and is_paths_relative(\n                    venv.pipx_metadata.source_interpreter, interpreter_dir\n                ):\n                    print(\n                        f\"Upgrade the interpreter of {venv.name} from {interpreter_full_version} to {latest_micro_version}\"\n                    )\n                    commands.reinstall(\n                        venv_dir=venv_dir,\n                        local_bin_dir=paths.ctx.bin_dir,\n                        local_man_dir=paths.ctx.man_dir,\n                        python=str(interpreter_python),\n                        verbose=verbose,\n                    )\n                    upgraded.append((venv.name, interpreter_full_version, latest_micro_version))\n\n    if upgraded:\n        print(\"Successfully upgraded the interpreter(s):\")\n        for venv_name, old_version, new_version in upgraded:\n            print(f\" - {venv_name}: {old_version} -> {new_version}\")\n    else:\n        print(\"Nothing to upgrade\")\n\n    # Any failure to upgrade will raise PipxError, otherwise success\n    return constants.EXIT_CODE_OK\n"
  },
  {
    "path": "src/pipx/commands/list_packages.py",
    "content": "import json\nimport logging\nimport sys\nfrom collections.abc import Collection\nfrom pathlib import Path\nfrom typing import Any\n\nfrom pipx import paths\nfrom pipx.colors import bold\nfrom pipx.commands.common import VenvProblems, get_venv_summary, venv_health_check\nfrom pipx.constants import EXIT_CODE_LIST_PROBLEM, EXIT_CODE_OK, ExitCode\nfrom pipx.emojis import sleep\nfrom pipx.pipx_metadata_file import JsonEncoderHandlesPath, PipxMetadata\nfrom pipx.venv import Venv, VenvContainer\n\nlogger = logging.getLogger(__name__)\n\nPIPX_SPEC_VERSION = \"0.1\"\n\n\ndef get_venv_metadata_summary(venv_dir: Path) -> tuple[PipxMetadata, VenvProblems, str]:\n    venv = Venv(venv_dir)\n\n    (venv_problems, warning_message) = venv_health_check(venv)\n    if venv_problems.any_():\n        return (PipxMetadata(venv_dir, read=False), venv_problems, warning_message)\n\n    return (venv.pipx_metadata, venv_problems, \"\")\n\n\ndef list_short(venv_dirs: Collection[Path]) -> VenvProblems:\n    all_venv_problems = VenvProblems()\n    for venv_dir in venv_dirs:\n        venv_metadata, venv_problems, warning_str = get_venv_metadata_summary(venv_dir)\n        if venv_problems.any_():\n            logger.warning(warning_str)\n        else:\n            print(\n                venv_metadata.main_package.package,\n                venv_metadata.main_package.package_version,\n            )\n        all_venv_problems.or_(venv_problems)\n\n    return all_venv_problems\n\n\ndef list_text(venv_dirs: Collection[Path], include_injected: bool, venv_root_dir: str) -> VenvProblems:\n    print(f\"venvs are in {bold(venv_root_dir)}\")\n    print(f\"apps are exposed on your $PATH at {bold(str(paths.ctx.bin_dir))}\")\n    print(f\"manual pages are exposed at {bold(str(paths.ctx.man_dir))}\")\n\n    all_venv_problems = VenvProblems()\n    for venv_dir in venv_dirs:\n        package_summary, venv_problems = get_venv_summary(venv_dir, include_injected=include_injected)\n        if venv_problems.any_():\n            logger.warning(package_summary)\n        else:\n            print(package_summary)\n        all_venv_problems.or_(venv_problems)\n\n    return all_venv_problems\n\n\ndef list_json(venv_dirs: Collection[Path]) -> VenvProblems:\n    warning_messages = []\n    spec_metadata: dict[str, Any] = {\n        \"pipx_spec_version\": PIPX_SPEC_VERSION,\n        \"venvs\": {},\n    }\n    all_venv_problems = VenvProblems()\n    for venv_dir in venv_dirs:\n        (venv_metadata, venv_problems, warning_str) = get_venv_metadata_summary(venv_dir)\n        all_venv_problems.or_(venv_problems)\n        if venv_problems.any_():\n            warning_messages.append(warning_str)\n            continue\n\n        spec_metadata[\"venvs\"][venv_dir.name] = {}\n        spec_metadata[\"venvs\"][venv_dir.name][\"metadata\"] = venv_metadata.to_dict()\n\n    print(json.dumps(spec_metadata, indent=4, sort_keys=True, cls=JsonEncoderHandlesPath))\n    for warning_message in warning_messages:\n        logger.warning(warning_message)\n\n    return all_venv_problems\n\n\ndef list_pinned(venv_dirs: Collection[Path], include_injected: bool) -> VenvProblems:\n    all_venv_problems = VenvProblems()\n    for venv_dir in venv_dirs:\n        venv_metadata, venv_problems, warning_str = get_venv_metadata_summary(venv_dir)\n        if venv_problems.any_():\n            logger.warning(warning_str)\n        else:\n            if venv_metadata.main_package.pinned:\n                print(\n                    venv_metadata.main_package.package,\n                    venv_metadata.main_package.package_version,\n                )\n            if include_injected:\n                for pkg, info in venv_metadata.injected_packages.items():\n                    if info.pinned:\n                        print(pkg, info.package_version, f\"(injected in venv {venv_dir.name})\")\n        all_venv_problems.or_(venv_problems)\n\n    return all_venv_problems\n\n\ndef list_packages(\n    venv_container: VenvContainer,\n    include_injected: bool,\n    json_format: bool,\n    short_format: bool,\n    pinned_only: bool,\n) -> ExitCode:\n    \"\"\"Returns pipx exit code.\"\"\"\n    venv_dirs: Collection[Path] = sorted(venv_container.iter_venv_dirs())\n    if not venv_dirs:\n        print(f\"nothing has been installed with pipx {sleep}\", file=sys.stderr)\n\n    if json_format:\n        all_venv_problems = list_json(venv_dirs)\n    elif short_format:\n        all_venv_problems = list_short(venv_dirs)\n    elif pinned_only:\n        all_venv_problems = list_pinned(venv_dirs, include_injected)\n    else:\n        if not venv_dirs:\n            return EXIT_CODE_OK\n        all_venv_problems = list_text(venv_dirs, include_injected, str(venv_container))\n\n    if all_venv_problems.bad_venv_name:\n        logger.warning(\n            \"\\nOne or more packages contain out-of-date internal data installed from a\\n\"\n            \"previous pipx version and need to be updated.\\n\"\n            \"    To fix, execute: pipx reinstall-all\"\n        )\n    if all_venv_problems.invalid_interpreter:\n        logger.warning(\n            \"\\nOne or more packages have a missing python interpreter.\\n    To fix, execute: pipx reinstall-all\"\n        )\n    if all_venv_problems.missing_metadata:\n        logger.warning(\n            \"\\nOne or more packages have a missing internal pipx metadata.\\n\"\n            \"   They were likely installed using a pipx version before 0.15.0.0.\\n\"\n            \"   Please uninstall and install these package(s) to fix.\"\n        )\n    if all_venv_problems.not_installed:\n        logger.warning(\n            \"\\nOne or more packages are not installed properly.\\n\"\n            \"   Please uninstall and install these package(s) to fix.\"\n        )\n\n    if all_venv_problems.any_():\n        print(\"\", file=sys.stderr)\n        return EXIT_CODE_LIST_PROBLEM\n\n    return EXIT_CODE_OK\n"
  },
  {
    "path": "src/pipx/commands/pin.py",
    "content": "import logging\nfrom collections.abc import Sequence\nfrom pathlib import Path\n\nfrom pipx.colors import bold\nfrom pipx.constants import ExitCode\nfrom pipx.emojis import sleep\nfrom pipx.util import PipxError\nfrom pipx.venv import Venv\n\nlogger = logging.getLogger(__name__)\n\n\ndef _update_pin_info(venv: Venv, package_name: str, is_main_package: bool, unpin: bool) -> int:\n    package_metadata = venv.package_metadata[package_name]\n    venv.update_package_metadata(\n        package_name=str(package_metadata.package),\n        package_or_url=str(package_metadata.package_or_url),\n        pip_args=package_metadata.pip_args,\n        include_dependencies=package_metadata.include_dependencies,\n        include_apps=package_metadata.include_apps,\n        is_main_package=is_main_package,\n        suffix=package_metadata.suffix,\n        pinned=not unpin,\n    )\n    return 1\n\n\ndef pin(\n    venv_dir: Path,\n    verbose: bool,\n    skip: Sequence[str],\n    injected_only: bool = False,\n) -> ExitCode:\n    venv = Venv(venv_dir, verbose=verbose)\n    try:\n        main_package_metadata = venv.package_metadata[venv.main_package_name]\n    except KeyError as e:\n        raise PipxError(f\"Package {venv.name} is not installed\") from e\n\n    if main_package_metadata.pinned:\n        logger.warning(f\"Package {main_package_metadata.package} already pinned {sleep}\")\n    elif injected_only or skip:\n        pinned_packages_count = 0\n        pinned_packages_list = []\n        for package_name in venv.package_metadata:\n            if package_name == venv.main_package_name or package_name in skip:\n                continue\n\n            if venv.package_metadata[package_name].pinned:\n                print(f\"{package_name} was pinned. Not modifying.\")\n                continue\n\n            pinned_packages_count += _update_pin_info(venv, package_name, is_main_package=False, unpin=False)\n            pinned_packages_list.append(f\"{package_name} {venv.package_metadata[package_name].package_version}\")\n\n        if pinned_packages_count != 0:\n            print(bold(f\"Pinned {pinned_packages_count} packages in venv {venv.name}\"))\n            for package in pinned_packages_list:\n                print(\"  -\", package)\n    else:\n        for package_name in venv.package_metadata:\n            if package_name == venv.main_package_name:\n                _update_pin_info(venv, venv.main_package_name, is_main_package=True, unpin=False)\n            else:\n                _update_pin_info(venv, package_name, is_main_package=False, unpin=False)\n\n    return ExitCode(0)\n\n\ndef unpin(venv_dir: Path, verbose: bool) -> ExitCode:\n    venv = Venv(venv_dir, verbose=verbose)\n    try:\n        main_package_metadata = venv.package_metadata[venv.main_package_name]\n    except KeyError as e:\n        raise PipxError(f\"Package {venv.name} is not installed\") from e\n\n    unpinned_packages_count = 0\n    unpinned_packages_list = []\n\n    for package_name in venv.package_metadata:\n        if package_name == main_package_metadata.package and main_package_metadata.pinned:\n            unpinned_packages_count += _update_pin_info(venv, package_name, is_main_package=True, unpin=True)\n        elif venv.package_metadata[package_name].pinned:\n            unpinned_packages_count += _update_pin_info(venv, package_name, is_main_package=False, unpin=True)\n        unpinned_packages_list.append(package_name)\n\n    if unpinned_packages_count != 0:\n        print(bold(f\"Unpinned {unpinned_packages_count} packages in venv {venv.name}\"))\n        for package in unpinned_packages_list:\n            print(\"  -\", package)\n    else:\n        logger.warning(f\"No packages to unpin in venv {venv.name}\")\n\n    return ExitCode(0)\n"
  },
  {
    "path": "src/pipx/commands/reinstall.py",
    "content": "import sys\nfrom collections.abc import Sequence\nfrom pathlib import Path\n\nfrom packaging.utils import canonicalize_name\n\nfrom pipx.commands.inject import inject_dep\nfrom pipx.commands.install import install\nfrom pipx.commands.uninstall import uninstall\nfrom pipx.constants import (\n    EXIT_CODE_OK,\n    EXIT_CODE_REINSTALL_INVALID_PYTHON,\n    EXIT_CODE_REINSTALL_VENV_NONEXISTENT,\n    ExitCode,\n)\nfrom pipx.emojis import error, sleep\nfrom pipx.util import PipxError\nfrom pipx.venv import Venv, VenvContainer\n\n\ndef reinstall(\n    *,\n    venv_dir: Path,\n    local_bin_dir: Path,\n    local_man_dir: Path,\n    python: str,\n    verbose: bool,\n    force_reinstall_shared_libs: bool = False,\n    python_flag_passed: bool = False,\n) -> ExitCode:\n    \"\"\"Returns pipx exit code.\"\"\"\n    if not venv_dir.exists():\n        print(f\"Nothing to reinstall for {venv_dir.name} {sleep}\")\n        return EXIT_CODE_REINSTALL_VENV_NONEXISTENT\n\n    try:\n        Path(python).relative_to(venv_dir)\n    except ValueError:\n        pass\n    else:\n        print(\n            f\"{error} Error, the python executable would be deleted!\",\n            \"Change it using the --python option or PIPX_DEFAULT_PYTHON environment variable.\",\n        )\n        return EXIT_CODE_REINSTALL_INVALID_PYTHON\n\n    venv = Venv(venv_dir, verbose=verbose)\n    venv.check_upgrade_shared_libs(\n        pip_args=venv.pipx_metadata.main_package.pip_args, verbose=verbose, force_upgrade=force_reinstall_shared_libs\n    )\n\n    if venv.pipx_metadata.main_package.package_or_url is not None:\n        package_or_url = venv.pipx_metadata.main_package.package_or_url\n    else:\n        package_or_url = venv.main_package_name\n\n    if venv.pipx_metadata.main_package.pinned:\n        raise PipxError(f\"{error} Package {venv_dir} is pinned. Run `pipx unpin {venv_dir.name}` to unpin it first.\")\n\n    uninstall(venv_dir, local_bin_dir, local_man_dir, verbose)\n\n    # in case legacy original dir name\n    venv_dir = venv_dir.with_name(canonicalize_name(venv_dir.name))\n\n    # install main package first\n    install(\n        venv_dir,\n        [venv.main_package_name],\n        [package_or_url],\n        local_bin_dir,\n        local_man_dir,\n        python,\n        venv.pipx_metadata.main_package.pip_args,\n        venv.pipx_metadata.venv_args,\n        verbose,\n        force=True,\n        reinstall=True,\n        include_dependencies=venv.pipx_metadata.main_package.include_dependencies,\n        preinstall_packages=[],\n        suffix=venv.pipx_metadata.main_package.suffix,\n        python_flag_passed=python_flag_passed,\n    )\n\n    # now install injected packages\n    for injected_name, injected_package in venv.pipx_metadata.injected_packages.items():\n        if injected_package.package_or_url is None:\n            # This should never happen, but package_or_url is type\n            #   Optional[str] so mypy thinks it could be None\n            raise PipxError(f\"Internal Error injecting package {injected_package} into {venv.name}\")\n        inject_dep(\n            venv_dir,\n            injected_name,\n            injected_package.package_or_url,\n            injected_package.pip_args,\n            verbose=verbose,\n            include_apps=injected_package.include_apps,\n            include_dependencies=injected_package.include_dependencies,\n            force=True,\n        )\n\n    # Any failure to install will raise PipxError, otherwise success\n    return EXIT_CODE_OK\n\n\ndef reinstall_all(\n    venv_container: VenvContainer,\n    local_bin_dir: Path,\n    local_man_dir: Path,\n    python: str,\n    verbose: bool,\n    *,\n    skip: Sequence[str],\n    python_flag_passed: bool = False,\n) -> ExitCode:\n    \"\"\"Returns pipx exit code.\"\"\"\n    failed: list[str] = []\n    reinstalled: list[str] = []\n\n    # iterate on all packages and reinstall them\n    # for the first one, we also trigger\n    # a reinstall of shared libs beforehand\n    first_reinstall = True\n    for venv_dir in venv_container.iter_venv_dirs():\n        if venv_dir.name in skip:\n            continue\n        try:\n            reinstall(\n                venv_dir=venv_dir,\n                local_bin_dir=local_bin_dir,\n                local_man_dir=local_man_dir,\n                python=python,\n                verbose=verbose,\n                force_reinstall_shared_libs=first_reinstall,\n                python_flag_passed=python_flag_passed,\n            )\n        except PipxError as e:\n            print(e, file=sys.stderr)\n            failed.append(venv_dir.name)\n        else:\n            first_reinstall = False\n            reinstalled.append(venv_dir.name)\n    if len(reinstalled) == 0:\n        print(f\"No packages reinstalled after running 'pipx reinstall-all' {sleep}\")\n    if len(failed) > 0:\n        raise PipxError(f\"The following package(s) failed to reinstall: {', '.join(failed)}\")\n    # Any failure to install will raise PipxError, otherwise success\n    return EXIT_CODE_OK\n"
  },
  {
    "path": "src/pipx/commands/run.py",
    "content": "import datetime\nimport hashlib\nimport logging\nimport re\nimport sys\nimport time\nimport urllib.parse\nimport urllib.request\nfrom pathlib import Path\nfrom shutil import which\nfrom typing import NoReturn, Optional, Union\n\nfrom packaging.requirements import InvalidRequirement, Requirement\n\nfrom pipx import paths\nfrom pipx.commands.common import package_name_from_spec\nfrom pipx.commands.inject import inject_dep\nfrom pipx.constants import TEMP_VENV_EXPIRATION_THRESHOLD_DAYS, WINDOWS\nfrom pipx.emojis import hazard\nfrom pipx.util import (\n    PipxError,\n    exec_app,\n    get_pypackage_bin_path,\n    pipx_wrap,\n    rmdir,\n    run_pypackage_bin,\n)\nfrom pipx.venv import Venv\n\nif sys.version_info < (3, 11):\n    import tomli as tomllib\nelse:\n    import tomllib\n\nlogger = logging.getLogger(__name__)\n\n\nVENV_EXPIRED_FILENAME = \"pipx_expired_venv\"\n\nAPP_NOT_FOUND_ERROR_MESSAGE = \"\"\"\\\n'{app}' executable script not found in package '{package_name}'.\nAvailable executable scripts:\n    {app_lines}\"\"\"\n\n\ndef maybe_script_content(app: str, is_path: bool) -> Optional[Union[str, Path]]:\n    \"\"\"If the app is a script, return its content.\n    Return None if it should be treated as a package name.\"\"\"\n\n    # Look for a local file first.\n    app_path = Path(app)\n    if app_path.is_file():\n        return app_path\n    # In case it's a named pipe, read it out to pass to the interpreter\n    if app_path.is_fifo():\n        return app_path.read_text(encoding=\"utf-8\")\n\n    if is_path:\n        raise PipxError(f\"The specified path {app} does not exist\")\n\n    # Check for a URL\n    if urllib.parse.urlparse(app).scheme:\n        if not app.endswith(\".py\"):\n            raise PipxError(\n                \"\"\"\n                pipx will only execute apps from the internet directly if they\n                end with '.py'. To run from an SVN, try pipx --spec URL BINARY\n                \"\"\"\n            )\n        logger.info(\"Detected url. Downloading and executing as a Python file.\")\n\n        return _http_get_request(app)\n\n    # Otherwise, it's a package\n    return None\n\n\ndef run_script(\n    content: Union[str, Path],\n    app_args: list[str],\n    python: str,\n    pip_args: list[str],\n    venv_args: list[str],\n    verbose: bool,\n    use_cache: bool,\n) -> NoReturn:\n    requirements = _get_requirements_from_script(content)\n    if not requirements:\n        python_path = Path(python)\n    else:\n        # Note that the environment name is based on the identified\n        # requirements, and *not* on the script name. This is deliberate, as\n        # it ensures that two scripts with the same requirements can use the\n        # same environment, which means fewer environments need to be\n        # managed. The requirements are normalised (in\n        # _get_requirements_from_script), so that irrelevant differences in\n        # whitespace, and similar, don't prevent environment sharing.\n        venv_dir = _get_temporary_venv_path(requirements, python, pip_args, venv_args)\n        venv = Venv(venv_dir)\n        _prepare_venv_cache(venv, None, use_cache)\n        if venv_dir.exists():\n            logger.info(f\"Reusing cached venv {venv_dir}\")\n        else:\n            venv = Venv(venv_dir, python=python, verbose=verbose)\n            venv.check_upgrade_shared_libs(pip_args=pip_args, verbose=verbose)\n            venv.create_venv(venv_args, pip_args)\n            try:\n                venv.install_unmanaged_packages(requirements, pip_args)\n            except:\n                # Package installation failed, so mark the cache as expired.\n                # This ensures an attempt is made to re-install requirements\n                # when `pipx run` is next executed, rather than just failing.\n                (venv_dir / VENV_EXPIRED_FILENAME).touch()\n                raise\n        python_path = venv.python_path\n\n    if isinstance(content, Path):\n        exec_app([python_path, content, *app_args])\n    else:\n        exec_app([python_path, \"-c\", content, *app_args])\n\n\ndef run_package(\n    app: str,\n    package_or_url: str,\n    dependencies: list[str],\n    app_args: list[str],\n    python: str,\n    pip_args: list[str],\n    venv_args: list[str],\n    pypackages: bool,\n    verbose: bool,\n    use_cache: bool,\n) -> NoReturn:\n    if which(app):\n        logger.warning(\n            pipx_wrap(\n                f\"\"\"\n                {hazard}  {app} is already on your PATH and installed at\n                {which(app)}. Downloading and running anyway.\n                \"\"\",\n                subsequent_indent=\" \" * 4,\n            )\n        )\n\n    if WINDOWS:\n        app_filename = f\"{app}.exe\"\n        logger.info(f\"Assuming app is {app_filename!r} (Windows only)\")\n    else:\n        app_filename = app\n\n    pypackage_bin_path = get_pypackage_bin_path(app)\n    if pypackage_bin_path.exists():\n        logger.info(f\"Using app in local __pypackages__ directory at '{pypackage_bin_path}'\")\n        run_pypackage_bin(pypackage_bin_path, app_args)\n    if pypackages:\n        raise PipxError(\n            f\"\"\"\n            '--pypackages' flag was passed, but '{pypackage_bin_path}' was\n            not found. See https://github.com/cs01/pythonloc to learn how to\n            install here, or omit the flag.\n            \"\"\"\n        )\n\n    venv_dir = _get_temporary_venv_path([package_or_url], python, pip_args, venv_args)\n\n    venv = Venv(venv_dir)\n    bin_path = venv.bin_path / app_filename\n    _prepare_venv_cache(venv, bin_path, use_cache)\n\n    if venv.has_app(app, app_filename):\n        logger.info(f\"Reusing cached venv {venv_dir}\")\n    else:\n        logger.info(f\"venv location is {venv_dir}\")\n        venv, app, app_filename = _prepare_venv(\n            Path(venv_dir),\n            package_or_url,\n            app,\n            app_filename,\n            python,\n            pip_args,\n            venv_args,\n            use_cache,\n            verbose,\n        )\n\n    for dep in dependencies:\n        inject_dep(\n            venv_dir=venv_dir,\n            package_name=None,\n            package_spec=dep,\n            pip_args=pip_args,\n            verbose=verbose,\n            include_apps=False,\n            include_dependencies=False,\n            force=False,\n        )\n    venv.run_app(app, app_filename, app_args)\n\n\ndef run(\n    app: str,\n    spec: str,\n    dependencies: list[str],\n    is_path: bool,\n    app_args: list[str],\n    python: str,\n    pip_args: list[str],\n    venv_args: list[str],\n    pypackages: bool,\n    verbose: bool,\n    use_cache: bool,\n) -> NoReturn:\n    \"\"\"Installs venv to temporary dir (or reuses cache), then runs app from\n    package\n    \"\"\"\n\n    # For any package, we need to just use the name\n    try:\n        package_name = Requirement(app).name\n    except InvalidRequirement:\n        # Raw URLs to scripts are supported, too, so continue if\n        # we can't parse this as a package\n        package_name = app\n\n    content = None if spec is not None else maybe_script_content(app, is_path)  # type: ignore[redundant-expr]\n    if content is not None:\n        run_script(content, app_args, python, pip_args, venv_args, verbose, use_cache)\n    else:\n        package_or_url = spec if spec is not None else app  # type: ignore[redundant-expr]\n        run_package(\n            package_name,\n            package_or_url,\n            dependencies,\n            app_args,\n            python,\n            pip_args,\n            venv_args,\n            pypackages,\n            verbose,\n            use_cache,\n        )\n\n\ndef _prepare_venv(\n    venv_dir: Path,\n    package_or_url: str,\n    app: str,\n    app_filename: str,\n    python: str,\n    pip_args: list[str],\n    venv_args: list[str],\n    use_cache: bool,\n    verbose: bool,\n) -> tuple[Venv, str, str]:\n    venv = Venv(venv_dir, python=python, verbose=verbose)\n    venv.check_upgrade_shared_libs(pip_args=pip_args, verbose=verbose)\n\n    if venv.pipx_metadata.main_package.package is not None:\n        package_name = venv.pipx_metadata.main_package.package\n    else:\n        package_name = package_name_from_spec(package_or_url, python, pip_args=pip_args, verbose=verbose)\n\n    override_shared = package_name == \"pip\"\n\n    venv.create_venv(venv_args, pip_args, override_shared)\n    venv.install_package(\n        package_name=package_name,\n        package_or_url=package_or_url,\n        pip_args=pip_args,\n        include_dependencies=False,\n        include_apps=True,\n        is_main_package=True,\n    )\n\n    if not venv.has_app(app, app_filename):\n        apps = venv.pipx_metadata.main_package.apps\n\n        # If there's a single app inside the package, run that by default\n        if app == package_name and len(apps) == 1:\n            app = apps[0]\n            print(f\"NOTE: running app {app!r} from {package_name!r}\")\n            if WINDOWS:\n                app_filename = f\"{app}.exe\"\n                logger.info(f\"Assuming app is {app_filename!r} (Windows only)\")\n            else:\n                app_filename = app\n        else:\n            all_apps = (f\"{a} - usage: 'pipx run --spec {package_or_url} {a} [arguments?]'\" for a in apps)\n            raise PipxError(\n                APP_NOT_FOUND_ERROR_MESSAGE.format(\n                    app=app,\n                    package_name=package_name,\n                    app_lines=\"\\n    \".join(all_apps),\n                ),\n                wrap_message=False,\n            )\n\n    if not use_cache:\n        # Let future _remove_all_expired_venvs know to remove this\n        (venv_dir / VENV_EXPIRED_FILENAME).touch()\n\n    return venv, app, app_filename\n\n\ndef _get_temporary_venv_path(requirements: list[str], python: str, pip_args: list[str], venv_args: list[str]) -> Path:\n    \"\"\"Computes deterministic path using hashing function on arguments relevant\n    to virtual environment's end state. Arguments used should result in idempotent\n    virtual environment. (i.e. args passed to app aren't relevant, but args\n    passed to venv creation are.)\n    \"\"\"\n    m = hashlib.sha256()\n    m.update(\"\".join(requirements).encode())\n    m.update(python.encode())\n    m.update(\"\".join(pip_args).encode())\n    m.update(\"\".join(venv_args).encode())\n    venv_folder_name = m.hexdigest()[:15]  # 15 chosen arbitrarily\n    return Path(paths.ctx.venv_cache) / venv_folder_name\n\n\ndef _is_temporary_venv_expired(venv_dir: Path) -> bool:\n    created_time_sec = venv_dir.stat().st_ctime\n    current_time_sec = time.mktime(datetime.datetime.now().timetuple())\n    age = current_time_sec - created_time_sec\n    expiration_threshold_sec = 60 * 60 * 24 * TEMP_VENV_EXPIRATION_THRESHOLD_DAYS\n    return age > expiration_threshold_sec or (venv_dir / VENV_EXPIRED_FILENAME).exists()\n\n\ndef _prepare_venv_cache(venv: Venv, bin_path: Optional[Path], use_cache: bool) -> None:\n    venv_dir = venv.root\n    if not use_cache and (bin_path is None or bin_path.exists()):\n        logger.info(f\"Removing cached venv {venv_dir!s}\")\n        rmdir(venv_dir)\n    _remove_all_expired_venvs()\n\n\ndef _remove_all_expired_venvs() -> None:\n    for venv_dir in Path(paths.ctx.venv_cache).iterdir():\n        if _is_temporary_venv_expired(venv_dir):\n            logger.info(f\"Removing expired venv {venv_dir!s}\")\n            rmdir(venv_dir)\n\n\ndef _http_get_request(url: str) -> str:\n    try:\n        res = urllib.request.urlopen(url)\n        charset = res.headers.get_content_charset() or \"utf-8\"\n        return res.read().decode(charset)\n    except Exception as e:\n        logger.debug(\"Uncaught Exception:\", exc_info=True)\n        raise PipxError(str(e)) from e\n\n\n# This regex comes from the inline script metadata spec\nINLINE_SCRIPT_METADATA = re.compile(r\"(?m)^# /// (?P<type>[a-zA-Z0-9-]+)$\\s(?P<content>(^#(| .*)$\\s)+)^# ///$\")\n\n\ndef _get_requirements_from_script(content: Union[str, Path]) -> Optional[list[str]]:\n    \"\"\"\n    Supports inline script metadata.\n    \"\"\"\n\n    if isinstance(content, Path):\n        content = content.read_text(encoding=\"utf-8\")\n\n    name = \"script\"\n\n    # Windows is currently getting un-normalized line endings, so normalize\n    content = content.replace(\"\\r\\n\", \"\\n\")\n\n    matches = [m for m in INLINE_SCRIPT_METADATA.finditer(content) if m.group(\"type\") == name]\n\n    if not matches:\n        pyproject_matches = [m for m in INLINE_SCRIPT_METADATA.finditer(content) if m.group(\"type\") == \"pyproject\"]\n        if pyproject_matches:\n            logger.error(\n                pipx_wrap(\n                    f\"\"\"\n                    {hazard}  Using old form of requirements table. Use updated PEP\n                    723 syntax by replacing `# /// pyproject` with `# /// script`\n                    and `run.dependencies` (or `run.requirements`) with\n                    `dependencies`.\n                    \"\"\",\n                    subsequent_indent=\" \" * 4,\n                )\n            )\n            raise ValueError(\"Old 'pyproject' table found\")\n        return None\n\n    if len(matches) > 1:\n        raise ValueError(f\"Multiple {name} blocks found\")\n\n    content = \"\".join(\n        line[2:] if line.startswith(\"# \") else line[1:] for line in matches[0].group(\"content\").splitlines(keepends=True)\n    )\n\n    pyproject = tomllib.loads(content)\n\n    requirements = []\n    for requirement in pyproject.get(\"dependencies\", []):\n        # Validate the requirement\n        try:\n            req = Requirement(requirement)\n        except InvalidRequirement as e:\n            raise PipxError(f\"Invalid requirement {requirement}: {e}\") from e\n\n        # Use the normalised form of the requirement\n        requirements.append(str(req))\n\n    return requirements\n"
  },
  {
    "path": "src/pipx/commands/run_pip.py",
    "content": "from pathlib import Path\n\nfrom pipx.constants import ExitCode\nfrom pipx.util import PipxError\nfrom pipx.venv import Venv\n\n\ndef run_pip(package: str, venv_dir: Path, pip_args: list[str], verbose: bool) -> ExitCode:\n    \"\"\"Returns pipx exit code.\"\"\"\n    venv = Venv(venv_dir, verbose=verbose)\n    if not venv.python_path.exists():\n        raise PipxError(f\"venv for {package!r} was not found. Was {package!r} installed with pipx?\")\n    venv.verbose = True\n    return venv.run_pip_get_exit_code(pip_args)\n"
  },
  {
    "path": "src/pipx/commands/uninject.py",
    "content": "import logging\nimport os\nfrom pathlib import Path\n\nfrom packaging.utils import canonicalize_name\n\nfrom pipx.colors import bold\nfrom pipx.commands.uninstall import (\n    _get_package_bin_dir_app_paths,\n    _get_package_man_paths,\n)\nfrom pipx.constants import (\n    EXIT_CODE_OK,\n    EXIT_CODE_UNINJECT_ERROR,\n    MAN_SECTIONS,\n    ExitCode,\n)\nfrom pipx.emojis import stars\nfrom pipx.util import PipxError, pipx_wrap\nfrom pipx.venv import Venv\n\nlogger = logging.getLogger(__name__)\n\n\ndef get_include_resource_paths(package_name: str, venv: Venv, local_bin_dir: Path, local_man_dir: Path) -> set[Path]:\n    bin_dir_app_paths = _get_package_bin_dir_app_paths(\n        venv, venv.package_metadata[package_name], venv.bin_path, local_bin_dir\n    )\n    man_paths = set()\n    for man_section in MAN_SECTIONS:\n        man_paths |= _get_package_man_paths(\n            venv,\n            venv.package_metadata[package_name],\n            venv.man_path / man_section,\n            local_man_dir / man_section,\n        )\n\n    need_to_remove = set()\n    for bin_dir_app_path in bin_dir_app_paths:\n        if bin_dir_app_path.name in venv.package_metadata[package_name].apps:\n            need_to_remove.add(bin_dir_app_path)\n    for man_path in man_paths:\n        path = Path(man_path.parent.name) / man_path.name\n        if str(path) in venv.package_metadata[package_name].man_pages:\n            need_to_remove.add(path)\n\n    return need_to_remove\n\n\ndef uninject_dep(\n    venv: Venv,\n    package_name: str,\n    *,\n    local_bin_dir: Path,\n    local_man_dir: Path,\n    leave_deps: bool = False,\n) -> bool:\n    package_name = canonicalize_name(package_name)\n\n    if package_name == venv.pipx_metadata.main_package.package:\n        logger.warning(\n            pipx_wrap(\n                f\"\"\"\n            {package_name} is the main package of {venv.root.name}\n            venv. Use `pipx uninstall {venv.root.name}` to uninstall instead of uninject.\n            \"\"\",\n                subsequent_indent=\" \" * 4,\n            )\n        )\n        return False\n\n    if package_name not in venv.pipx_metadata.injected_packages:\n        logger.warning(f\"{package_name} is not in the {venv.root.name} venv. Skipping.\")\n        return False\n\n    need_app_uninstall = venv.package_metadata[package_name].include_apps\n\n    new_resource_paths = get_include_resource_paths(package_name, venv, local_bin_dir, local_man_dir)\n\n    if not leave_deps:\n        orig_not_required_packages = venv.list_installed_packages(not_required=True)\n        logger.info(f\"Original not required packages: {orig_not_required_packages}\")\n\n    venv.uninstall_package(package=package_name, was_injected=True)\n\n    if not leave_deps:\n        new_not_required_packages = venv.list_installed_packages(not_required=True)\n        logger.info(f\"New not required packages: {new_not_required_packages}\")\n\n        deps_of_uninstalled = new_not_required_packages - orig_not_required_packages\n        if len(deps_of_uninstalled) == 0:\n            pass\n        else:\n            logger.info(f\"Dependencies of uninstalled package: {deps_of_uninstalled}\")\n\n        for dep_package_name in deps_of_uninstalled:\n            venv.uninstall_package(package=dep_package_name, was_injected=False)\n\n        deps_string = \" and its dependencies\"\n    else:\n        deps_string = \"\"\n\n    if need_app_uninstall:\n        for path in new_resource_paths:\n            try:\n                os.unlink(path)\n            except FileNotFoundError:\n                logger.info(f\"tried to remove but couldn't find {path}\")\n            else:\n                logger.info(f\"removed file {path}\")\n\n    print(f\"Uninjected package {bold(package_name)}{deps_string} from venv {bold(venv.root.name)} {stars}\")\n    return True\n\n\ndef uninject(\n    venv_dir: Path,\n    dependencies: list[str],\n    *,\n    local_bin_dir: Path,\n    local_man_dir: Path,\n    leave_deps: bool,\n    verbose: bool,\n) -> ExitCode:\n    \"\"\"Returns pipx exit code\"\"\"\n\n    if not venv_dir.exists() or next(venv_dir.iterdir(), None) is None:\n        raise PipxError(f\"Virtual environment {venv_dir.name} does not exist.\")\n\n    venv = Venv(venv_dir, verbose=verbose)\n\n    if not venv.package_metadata:\n        raise PipxError(\n            f\"\"\"\n            Can't uninject from Virtual Environment {venv_dir.name!r}.\n            {venv_dir.name!r} has missing internal pipx metadata.\n            It was likely installed using a pipx version before 0.15.0.0.\n            Please uninstall and install {venv_dir.name!r} manually to fix.\n            \"\"\"\n        )\n\n    all_success = True\n    for dep in dependencies:\n        all_success &= uninject_dep(\n            venv,\n            dep,\n            local_bin_dir=local_bin_dir,\n            local_man_dir=local_man_dir,\n            leave_deps=leave_deps,\n        )\n\n    if all_success:\n        return EXIT_CODE_OK\n    else:\n        return EXIT_CODE_UNINJECT_ERROR\n"
  },
  {
    "path": "src/pipx/commands/uninstall.py",
    "content": "import logging\nfrom pathlib import Path\nfrom shutil import which\nfrom typing import TYPE_CHECKING, Optional\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\nfrom pipx.commands.common import (\n    add_suffix,\n    can_symlink,\n    get_exposed_man_paths_for_package,\n    get_exposed_paths_for_package,\n)\nfrom pipx.constants import (\n    EXIT_CODE_OK,\n    EXIT_CODE_UNINSTALL_ERROR,\n    EXIT_CODE_UNINSTALL_VENV_NONEXISTENT,\n    MAN_SECTIONS,\n    ExitCode,\n)\nfrom pipx.emojis import hazard, sleep, stars\nfrom pipx.pipx_metadata_file import PackageInfo\nfrom pipx.util import rmdir, safe_unlink\nfrom pipx.venv import Venv, VenvContainer\nfrom pipx.venv_inspect import VenvMetadata\n\nlogger = logging.getLogger(__name__)\n\n\ndef _venv_metadata_to_package_info(\n    venv_metadata: VenvMetadata,\n    package_name: str,\n    package_or_url: str = \"\",\n    pip_args: Optional[list[str]] = None,\n    include_apps: bool = True,\n    include_dependencies: bool = False,\n    suffix: str = \"\",\n) -> PackageInfo:\n    if pip_args is None:\n        pip_args = []\n\n    return PackageInfo(\n        package=package_name,\n        package_or_url=package_or_url,\n        pip_args=pip_args,\n        include_apps=include_apps,\n        include_dependencies=include_dependencies,\n        apps=venv_metadata.apps,\n        app_paths=venv_metadata.app_paths,\n        apps_of_dependencies=venv_metadata.apps_of_dependencies,\n        app_paths_of_dependencies=venv_metadata.app_paths_of_dependencies,\n        man_pages=venv_metadata.man_pages,\n        man_paths=venv_metadata.man_paths,\n        man_pages_of_dependencies=venv_metadata.man_pages_of_dependencies,\n        man_paths_of_dependencies=venv_metadata.man_paths_of_dependencies,\n        package_version=venv_metadata.package_version,\n        suffix=suffix,\n    )\n\n\ndef _get_package_bin_dir_app_paths(\n    venv: Venv, package_info: PackageInfo, venv_bin_path: Path, local_bin_dir: Path\n) -> set[Path]:\n    suffix = package_info.suffix\n    apps = []\n    if package_info.include_apps:\n        apps += package_info.apps\n    if package_info.include_dependencies:\n        apps += package_info.apps_of_dependencies\n    return get_exposed_paths_for_package(venv_bin_path, local_bin_dir, [add_suffix(app, suffix) for app in apps])\n\n\ndef _get_package_man_paths(venv: Venv, package_info: PackageInfo, venv_man_path: Path, local_man_dir: Path) -> set[Path]:\n    man_pages = []\n    if package_info.include_apps:\n        man_pages += package_info.man_pages\n    if package_info.include_dependencies:\n        man_pages += package_info.man_pages_of_dependencies\n    return get_exposed_man_paths_for_package(venv_man_path, local_man_dir, man_pages)\n\n\ndef _get_venv_resource_paths(\n    resource_type: str, venv: Venv, venv_resource_path: Path, local_resource_dir: Path\n) -> set[Path]:\n    resource_paths = set()\n    assert resource_type in (\"app\", \"man\"), \"invalid resource type\"\n    get_package_resource_paths: Callable[[Venv, PackageInfo, Path, Path], set[Path]]\n    get_package_resource_paths = {\n        \"app\": _get_package_bin_dir_app_paths,\n        \"man\": _get_package_man_paths,\n    }[resource_type]\n\n    if venv.pipx_metadata.main_package.package is not None:\n        # Valid metadata for venv\n        for package_info in venv.package_metadata.values():\n            resource_paths |= get_package_resource_paths(venv, package_info, venv_resource_path, local_resource_dir)\n    elif venv.python_path.is_file():\n        # No metadata from pipx_metadata.json, but valid python interpreter.\n        # In pre-metadata-pipx venv.root.name is name of main package\n        # In pre-metadata-pipx there is no suffix\n        # We make the conservative assumptions: no injected packages,\n        # not include_dependencies.  Other PackageInfo fields are irrelevant\n        # here.\n        venv_metadata = venv.get_venv_metadata_for_package(venv.root.name, set())\n        main_package_info = _venv_metadata_to_package_info(venv_metadata, venv.root.name)\n        resource_paths = get_package_resource_paths(venv, main_package_info, venv_resource_path, local_resource_dir)\n    else:\n        # No metadata and no valid python interpreter.\n        # We'll take our best guess on what to uninstall here based on symlink\n        # location for symlink-capable systems.\n        # The heuristic here is any symlink in ~/.local/bin pointing to\n        # .local/share/pipx/venvs/VENV_NAME/{bin,Scripts} should be uninstalled.\n\n        # For non-symlink systems we give up and return an empty set.\n        if not local_resource_dir.is_dir() or not can_symlink(local_resource_dir):\n            return set()\n\n        resource_paths = get_exposed_paths_for_package(venv_resource_path, local_resource_dir)\n\n    return resource_paths\n\n\ndef uninstall(venv_dir: Path, local_bin_dir: Path, local_man_dir: Path, verbose: bool) -> ExitCode:\n    \"\"\"Uninstall entire venv_dir, including main package and all injected\n    packages.\n\n    Returns pipx exit code.\n    \"\"\"\n    if not venv_dir.exists():\n        print(f\"Nothing to uninstall for {venv_dir.name} {sleep}\")\n        app = which(venv_dir.name)\n        if app:\n            print(f\"{hazard}  Note: '{app}' still exists on your system and is on your PATH\")\n        return EXIT_CODE_UNINSTALL_VENV_NONEXISTENT\n\n    venv = Venv(venv_dir, verbose=verbose)\n\n    bin_dir_app_paths = _get_venv_resource_paths(\"app\", venv, venv.bin_path, local_bin_dir)\n    man_dir_paths = set()\n    for man_section in MAN_SECTIONS:\n        man_dir_paths |= _get_venv_resource_paths(\"man\", venv, venv.man_path / man_section, local_man_dir / man_section)\n\n    for path in bin_dir_app_paths | man_dir_paths:\n        try:\n            safe_unlink(path)\n        except FileNotFoundError:\n            logger.info(f\"tried to remove but couldn't find {path}\")\n        else:\n            logger.info(f\"removed file {path}\")\n\n    rmdir(venv_dir)\n    print(f\"uninstalled {venv.name}! {stars}\")\n    return EXIT_CODE_OK\n\n\ndef uninstall_all(\n    venv_container: VenvContainer,\n    local_bin_dir: Path,\n    local_man_dir: Path,\n    verbose: bool,\n) -> ExitCode:\n    \"\"\"Returns pipx exit code.\"\"\"\n    all_success = True\n    for venv_dir in venv_container.iter_venv_dirs():\n        return_val = uninstall(venv_dir, local_bin_dir, local_man_dir, verbose)\n        all_success &= return_val == 0\n\n    return EXIT_CODE_OK if all_success else EXIT_CODE_UNINSTALL_ERROR\n"
  },
  {
    "path": "src/pipx/commands/upgrade.py",
    "content": "import logging\nimport os\nimport sys\nfrom collections.abc import Sequence\nfrom pathlib import Path\nfrom typing import Optional\n\nfrom pipx import commands, paths\nfrom pipx.colors import bold, red\nfrom pipx.commands.common import expose_resources_globally\nfrom pipx.constants import EXIT_CODE_OK, ExitCode\nfrom pipx.emojis import sleep\nfrom pipx.package_specifier import parse_specifier_for_upgrade\nfrom pipx.util import PipxError, pipx_wrap\nfrom pipx.venv import Venv, VenvContainer\n\nlogger = logging.getLogger(__name__)\n\n\ndef _upgrade_package(\n    venv: Venv,\n    package_name: str,\n    pip_args: list[str],\n    is_main_package: bool,\n    force: bool,\n    upgrading_all: bool,\n) -> int:\n    \"\"\"Returns 1 if package version changed, 0 if same version\"\"\"\n    package_metadata = venv.package_metadata[package_name]\n\n    if package_metadata.package_or_url is None:\n        raise PipxError(f\"Internal Error: package {package_name} has corrupt pipx metadata.\")\n    elif package_metadata.pinned:\n        if package_metadata.package != venv.main_package_name:\n            logger.warning(\n                f\"Not upgrading pinned package {package_metadata.package} in venv {venv.name}. \"\n                f\"Run `pipx unpin {venv.name}` to unpin it.\"\n            )\n        else:\n            logger.warning(f\"Not upgrading pinned package {venv.name}. Run `pipx unpin {venv.name}` to unpin it.\")\n        return 0\n\n    package_or_url = parse_specifier_for_upgrade(package_metadata.package_or_url)\n    old_version = package_metadata.package_version\n\n    venv.upgrade_package(\n        package_name,\n        package_or_url,\n        pip_args,\n        include_dependencies=package_metadata.include_dependencies,\n        include_apps=package_metadata.include_apps,\n        is_main_package=is_main_package,\n        suffix=package_metadata.suffix,\n    )\n\n    package_metadata = venv.package_metadata[package_name]\n\n    display_name = f\"{package_metadata.package}{package_metadata.suffix}\"\n    new_version = package_metadata.package_version\n\n    if package_metadata.include_apps:\n        expose_resources_globally(\n            \"app\",\n            paths.ctx.bin_dir,\n            package_metadata.app_paths,\n            force=force,\n            suffix=package_metadata.suffix,\n        )\n        expose_resources_globally(\"man\", paths.ctx.man_dir, package_metadata.man_paths, force=force)\n\n    if package_metadata.include_dependencies:\n        for app_paths in package_metadata.app_paths_of_dependencies.values():\n            expose_resources_globally(\n                \"app\",\n                paths.ctx.bin_dir,\n                app_paths,\n                force=force,\n                suffix=package_metadata.suffix,\n            )\n        for man_paths in package_metadata.man_paths_of_dependencies.values():\n            expose_resources_globally(\"man\", paths.ctx.man_dir, man_paths, force=force)\n\n    if old_version == new_version:\n        if upgrading_all:\n            pass\n        else:\n            print(\n                pipx_wrap(\n                    f\"\"\"\n                    {display_name} is already at latest version {old_version}\n                    (location: {venv.root!s})\n                    \"\"\"\n                )\n            )\n        return 0\n    else:\n        print(\n            pipx_wrap(\n                f\"\"\"\n                upgraded package {display_name} from {old_version} to\n                {new_version} (location: {venv.root!s})\n                \"\"\"\n            )\n        )\n        return 1\n\n\ndef _upgrade_venv(\n    venv_dir: Path,\n    pip_args: list[str],\n    verbose: bool,\n    *,\n    include_injected: bool,\n    upgrading_all: bool,\n    force: bool,\n    install: bool = False,\n    venv_args: Optional[list[str]] = None,\n    python: Optional[str] = None,\n    python_flag_passed: bool = False,\n) -> int:\n    \"\"\"Return number of packages with changed versions.\"\"\"\n    if not venv_dir.is_dir():\n        if install:\n            if venv_args is None:\n                venv_args = []\n            commands.install(\n                venv_dir=None,\n                venv_args=venv_args,\n                package_names=None,\n                package_specs=[str(venv_dir).split(os.path.sep)[-1]],\n                local_bin_dir=paths.ctx.bin_dir,\n                local_man_dir=paths.ctx.man_dir,\n                python=python,\n                pip_args=pip_args,\n                verbose=verbose,\n                force=force,\n                reinstall=False,\n                include_dependencies=False,\n                preinstall_packages=None,\n                python_flag_passed=python_flag_passed,\n            )\n            return 0\n        else:\n            raise PipxError(\n                f\"\"\"\n                Package is not installed. Expected to find {venv_dir!s}, but it\n                does not exist.\n                \"\"\"\n            )\n\n    if venv_args and not install:\n        logger.info(\"Ignoring \" + \", \".join(venv_args) + \" as not combined with --install\")\n\n    if python and not install:\n        logger.info(\"Ignoring --python as not combined with --install\")\n\n    venv = Venv(venv_dir, verbose=verbose)\n    venv.check_upgrade_shared_libs(pip_args=pip_args, verbose=verbose)\n\n    if not venv.python_path.is_file():\n        raise PipxError(\n            f\"Not upgrading {red(bold(venv_dir.name))}. It has an invalid python interpreter {venv.python_path}.\\n\"\n            f\"This usually happens after a system Python upgrade.\\n\"\n            f\"To fix, execute: pipx reinstall-all\",\n            wrap_message=False,\n        )\n\n    if not venv.package_metadata:\n        raise PipxError(\n            f\"Not upgrading {red(bold(venv_dir.name))}. It has missing internal pipx metadata.\\n\"\n            f\"It was likely installed using a pipx version before 0.15.0.0.\\n\"\n            f\"Please uninstall and install this package to fix.\",\n            wrap_message=False,\n        )\n\n    # Upgrade shared libraries (pip, setuptools and wheel)\n    venv.upgrade_packaging_libraries(pip_args)\n\n    versions_updated = 0\n\n    package_name = venv.main_package_name\n    versions_updated += _upgrade_package(\n        venv,\n        package_name,\n        pip_args,\n        is_main_package=True,\n        force=force,\n        upgrading_all=upgrading_all,\n    )\n\n    if include_injected:\n        for package_name in venv.package_metadata:\n            if package_name == venv.main_package_name:\n                continue\n            versions_updated += _upgrade_package(\n                venv,\n                package_name,\n                pip_args,\n                is_main_package=False,\n                force=force,\n                upgrading_all=upgrading_all,\n            )\n\n    return versions_updated\n\n\ndef upgrade(\n    venv_dirs: dict[str, Path],\n    python: Optional[str],\n    pip_args: list[str],\n    venv_args: list[str],\n    verbose: bool,\n    *,\n    include_injected: bool,\n    force: bool,\n    install: bool,\n    python_flag_passed: bool = False,\n) -> ExitCode:\n    \"\"\"Return pipx exit code.\"\"\"\n\n    for venv_dir in venv_dirs.values():\n        _ = _upgrade_venv(\n            venv_dir,\n            pip_args,\n            verbose,\n            include_injected=include_injected,\n            upgrading_all=False,\n            force=force,\n            install=install,\n            venv_args=venv_args,\n            python=python,\n            python_flag_passed=python_flag_passed,\n        )\n\n    # Any error in upgrade will raise PipxError (e.g. from venv.upgrade_package())\n    return EXIT_CODE_OK\n\n\ndef upgrade_all(\n    venv_container: VenvContainer,\n    verbose: bool,\n    *,\n    pip_args: list[str],\n    include_injected: bool,\n    skip: Sequence[str],\n    force: bool,\n    python_flag_passed: bool = False,\n) -> ExitCode:\n    \"\"\"Return pipx exit code.\"\"\"\n    failed: list[str] = []\n    upgraded: list[str] = []\n\n    for venv_dir in venv_container.iter_venv_dirs():\n        venv = Venv(venv_dir, verbose=verbose)\n        venv.check_upgrade_shared_libs(pip_args=pip_args, verbose=verbose)\n        if venv_dir.name in skip or \"--editable\" in venv.pipx_metadata.main_package.pip_args:\n            continue\n        try:\n            versions_updated = _upgrade_venv(\n                venv_dir,\n                venv.pipx_metadata.main_package.pip_args,\n                verbose=verbose,\n                include_injected=include_injected,\n                upgrading_all=True,\n                force=force,\n                python_flag_passed=python_flag_passed,\n            )\n            if versions_updated > 0:\n                upgraded.append(venv_dir.name)\n        except PipxError as e:\n            print(e, file=sys.stderr)\n            failed.append(venv_dir.name)\n    if len(upgraded) == 0:\n        print(f\"No packages upgraded after running 'pipx upgrade-all' {sleep}\")\n    if len(failed) > 0:\n        raise PipxError(f\"The following package(s) failed to upgrade: {','.join(failed)}\")\n    # Any failure to install will raise PipxError, otherwise success\n    return EXIT_CODE_OK\n\n\ndef upgrade_shared(\n    verbose: bool,\n    pip_args: list[str],\n) -> ExitCode:\n    \"\"\"Return pipx exit code.\"\"\"\n    from pipx.shared_libs import shared_libs  # noqa: PLC0415\n\n    shared_libs.upgrade(verbose=verbose, pip_args=pip_args, raises=True)\n\n    return EXIT_CODE_OK\n"
  },
  {
    "path": "src/pipx/constants.py",
    "content": "import os\nimport platform\nimport sysconfig\nfrom textwrap import dedent\nfrom typing import NewType\n\nPIPX_SHARED_PTH = \"pipx_shared.pth\"\nTEMP_VENV_EXPIRATION_THRESHOLD_DAYS = 14\nMINIMUM_PYTHON_VERSION = \"3.9\"\nMAN_SECTIONS = [f\"man{i}\" for i in range(1, 10)]\nFETCH_MISSING_PYTHON = os.environ.get(\"PIPX_FETCH_MISSING_PYTHON\", False)\n\n\nExitCode = NewType(\"ExitCode\", int)\n# pipx shell exit codes\nEXIT_CODE_OK = ExitCode(0)\nEXIT_CODE_INJECT_ERROR = ExitCode(1)\nEXIT_CODE_UNINJECT_ERROR = ExitCode(1)\nEXIT_CODE_INSTALL_VENV_EXISTS = ExitCode(0)\nEXIT_CODE_LIST_PROBLEM = ExitCode(1)\nEXIT_CODE_UNINSTALL_VENV_NONEXISTENT = ExitCode(1)\nEXIT_CODE_UNINSTALL_ERROR = ExitCode(1)\nEXIT_CODE_REINSTALL_VENV_NONEXISTENT = ExitCode(1)\nEXIT_CODE_REINSTALL_INVALID_PYTHON = ExitCode(1)\nEXIT_CODE_SPECIFIED_PYTHON_EXECUTABLE_NOT_FOUND = ExitCode(1)\n\n\ndef is_windows() -> bool:\n    return platform.system() == \"Windows\"\n\n\ndef is_macos() -> bool:\n    return platform.system() == \"Darwin\"\n\n\ndef is_linux() -> bool:\n    return platform.system() == \"Linux\"\n\n\ndef is_mingw() -> bool:\n    return sysconfig.get_platform().startswith(\"mingw\")\n\n\nWINDOWS: bool = is_windows()\nMACOS: bool = is_macos()\nLINUX: bool = is_linux()\nMINGW: bool = is_mingw()\n\ncompletion_instructions = dedent(\n    \"\"\"\nIf you encountered register-python-argcomplete command not found error,\nor if you are using zipapp, run\n\n    pipx install argcomplete\n\nbefore running any of the following commands.\n\nAdd the appropriate command to your shell's config file\nso that it is run on startup. You will likely have to restart\nor re-login for the autocompletion to start working.\n\nbash:\n    eval \"$(register-python-argcomplete pipx)\"\n\nzsh:\n    To activate completions in zsh, first make sure compinit is marked for\n    autoload and run autoload:\n\n    autoload -U compinit && compinit\n\n    Afterwards you can enable completions for pipx:\n\n    eval \"$(register-python-argcomplete pipx)\"\n\n    NOTE: If your version of argcomplete is earlier than v3, you may need to\n    have bashcompinit enabled in zsh by running:\n\n    autoload -U bashcompinit\n    bashcompinit\n\n\ntcsh:\n    eval `register-python-argcomplete --shell tcsh pipx`\n\nfish:\n    # Not required to be in the config file, only run once\n    register-python-argcomplete --shell fish pipx >~/.config/fish/completions/pipx.fish\n\n\"\"\"\n)\n"
  },
  {
    "path": "src/pipx/emojis.py",
    "content": "import os\nimport sys\n\n\ndef strtobool(val: str) -> bool:\n    val = val.lower()\n    if val in (\"y\", \"yes\", \"t\", \"true\", \"on\", \"1\"):\n        return True\n    elif val in (\"n\", \"no\", \"f\", \"false\", \"off\", \"0\"):\n        return False\n    else:\n        return False\n\n\ndef use_emojis() -> bool:\n    # All emojis that pipx might possibly use\n    emoji_test_str = \"✨🌟⚠️😴⣷⣯⣟⡿⢿⣻⣽⣾\"\n    try:\n        emoji_test_str.encode(sys.stderr.encoding)\n        platform_emoji_support = True\n    except UnicodeEncodeError:\n        platform_emoji_support = False\n    use_emoji = os.getenv(\"PIPX_USE_EMOJI\")\n    if use_emoji is None:\n        use_emoji = str(os.getenv(\"USE_EMOJI\", platform_emoji_support))\n    return strtobool(use_emoji)\n\n\nEMOJI_SUPPORT = use_emojis()\n\nif EMOJI_SUPPORT:\n    stars = \"✨ 🌟 ✨\"\n    hazard = \"⚠️\"\n    error = \"⛔\"\n    sleep = \"😴\"\nelse:\n    stars = \"\"\n    hazard = \"\"\n    error = \"\"\n    sleep = \"\"\n"
  },
  {
    "path": "src/pipx/interpreter.py",
    "content": "import logging\nimport os\nimport shutil\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom typing import Optional\n\nfrom packaging import version\n\nfrom pipx.constants import FETCH_MISSING_PYTHON, WINDOWS\nfrom pipx.standalone_python import download_python_build_standalone\nfrom pipx.util import PipxError\n\nlogger = logging.getLogger(__name__)\n\n\ndef has_venv() -> bool:\n    try:\n        import venv  # noqa: F401, PLC0415\n    except ImportError:\n        return False\n    return True\n\n\nclass InterpreterResolutionError(PipxError):\n    def __init__(self, source: str, version: str, wrap_message: bool = True):\n        self.source = source\n        self.version = version\n        potentially_path = \"/\" in version\n        potentially_pylauncher = \"python\" not in version and not potentially_path\n\n        message = (\n            f\"No executable for the provided Python version '{version}' found in {source}.\"\n            \" Please make sure the provided version is \"\n        )\n        if source == \"py launcher\":\n            message += \"listed when running `py --list`.\"\n        if source == \"PATH\":\n            message += \"on your PATH or the file path is valid. \"\n            if potentially_path:\n                message += \"The provided version looks like a path, but no executable was found there.\"\n            if potentially_pylauncher:\n                message += (\n                    \"The provided version looks like a version, \"\n                    \"but both the python command and the Python Launcher were not found on PATH.\"\n                )\n        if source == \"the python-build-standalone project\":\n            message += \"listed in https://github.com/astral-sh/python-build-standalone/releases/latest.\"\n        super().__init__(message, wrap_message)\n\n\ndef find_unix_command_python(python_version: str) -> Optional[str]:\n    try:\n        parsed_python_version = version.parse(python_version)\n    except version.InvalidVersion:\n        logger.info(f\"Invalid Python version: {python_version}\")\n        return None\n\n    if (\n        parsed_python_version.epoch != 0\n        or parsed_python_version.is_devrelease\n        or parsed_python_version.is_postrelease\n        or parsed_python_version.is_prerelease\n    ):\n        logger.info(f\"Unsupported Python version: {python_version}\")\n        return None\n\n    # Python command could be `python3` or `python3.x` without micro version component\n    python_command = f\"python{'.'.join(python_version.split('.')[:2])}\"\n\n    python_path = shutil.which(python_command)\n    if not python_path:\n        logger.info(f\"Command `{python_command}` was not found on the system\")\n        return None\n\n    if parsed_python_version.micro != 0:\n        logger.warning(\n            f\"The command '{python_command}' located at '{python_path}' will be used. \"\n            f\"It may not match the specified version {python_version} at the micro/patch level.\"\n        )\n\n    return python_path\n\n\ndef find_python_interpreter(python_version: str, fetch_missing_python: bool = False) -> str:\n    if Path(python_version).is_file() or shutil.which(python_version):\n        return python_version\n\n    if not WINDOWS:\n        python_unix_command = find_unix_command_python(python_version)\n        if python_unix_command:\n            return python_unix_command\n\n    try:\n        py_executable = find_py_launcher_python(python_version)\n        if py_executable:\n            return py_executable\n    except (subprocess.CalledProcessError, FileNotFoundError) as e:\n        if not fetch_missing_python and not FETCH_MISSING_PYTHON:\n            raise InterpreterResolutionError(source=\"py launcher\", version=python_version) from e\n\n    if fetch_missing_python or FETCH_MISSING_PYTHON:\n        try:\n            return download_python_build_standalone(python_version)\n        except PipxError as e:\n            raise InterpreterResolutionError(source=\"the python-build-standalone project\", version=python_version) from e\n\n    raise InterpreterResolutionError(source=\"PATH\", version=python_version)\n\n\n# The following code was copied from https://github.com/uranusjr/pipx-standalone\n# which uses the same technique to build a completely standalone pipx\n# distribution.\n#\n# If we are running under the Windows embeddable distribution,\n# venv isn't available (and we probably don't want to use the\n# embeddable distribution as our applications' base Python anyway)\n# so we try to locate the system Python and use that instead.\n\n\ndef find_py_launcher_python(python_version: Optional[str] = None) -> Optional[str]:\n    py = shutil.which(\"py\")\n    if py and python_version:\n        python_semver = python_version\n        if python_version.startswith(\"python\"):\n            logger.warning(\n                \"Removing `python` from the start of the version, as pylauncher just expects the semantic version\"\n            )\n            python_semver = python_semver.lstrip(\"python\")\n        py = subprocess.run(\n            [py, f\"-{python_semver}\", \"-c\", \"import sys; print(sys.executable)\"],\n            capture_output=True,\n            text=True,\n            check=True,\n        ).stdout.strip()\n    return py\n\n\ndef _find_default_windows_python() -> str:\n    if has_venv():\n        return sys.executable\n    python = find_py_launcher_python() or shutil.which(\"python\")\n\n    if python is None:\n        raise PipxError(\"No suitable Python found\")\n\n    # If the path contains \"WindowsApps\", it's the store python\n    if \"WindowsApps\" not in python:\n        return python\n\n    # Special treatment to detect Windows Store stub.\n    # https://twitter.com/zooba/status/1212454929379581952\n\n    proc = subprocess.run([python, \"-V\"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=False)\n    if proc.returncode != 0:\n        # Cover the 9009 return code preemptively.\n        raise PipxError(\"No suitable Python found\")\n    if not proc.stdout.strip():\n        # A real Python should print version, Windows Store stub won't.\n        raise PipxError(\"No suitable Python found\")\n    return python  # This executable seems to work.\n\n\ndef _get_sys_executable() -> str:\n    if WINDOWS:\n        return _find_default_windows_python()\n    else:\n        return str(Path(sys.executable).resolve())\n\n\ndef _get_absolute_python_interpreter(env_python: str) -> str:\n    which_python = shutil.which(env_python)\n    if not which_python:\n        raise PipxError(f\"Default python interpreter '{env_python}' is invalid.\")\n    return which_python\n\n\nenv_default_python = os.environ.get(\"PIPX_DEFAULT_PYTHON\")\n\nif not env_default_python:\n    DEFAULT_PYTHON = _get_sys_executable()\nelse:\n    DEFAULT_PYTHON = _get_absolute_python_interpreter(env_default_python)\n"
  },
  {
    "path": "src/pipx/main.py",
    "content": "# PYTHON_ARGCOMPLETE_OK\n\n\"\"\"The command line interface to pipx\"\"\"\n\nimport argparse\nimport logging\nimport logging.config\nimport os\nimport re\nimport shlex\nimport sys\nimport textwrap\nimport time\nimport urllib.parse\nfrom pathlib import Path\nfrom typing import Any, Callable, Optional\n\nimport argcomplete\nimport platformdirs\nfrom packaging.utils import canonicalize_name\n\nfrom pipx import commands, constants, paths\nfrom pipx.animate import hide_cursor, show_cursor\nfrom pipx.colors import bold, green\nfrom pipx.commands.environment import ENVIRONMENT_VALUE_CHOICES, ENVIRONMENT_VARIABLES\nfrom pipx.constants import (\n    EXIT_CODE_OK,\n    EXIT_CODE_SPECIFIED_PYTHON_EXECUTABLE_NOT_FOUND,\n    MINIMUM_PYTHON_VERSION,\n    WINDOWS,\n    ExitCode,\n)\nfrom pipx.emojis import hazard\nfrom pipx.interpreter import (\n    DEFAULT_PYTHON,\n    InterpreterResolutionError,\n    find_python_interpreter,\n)\nfrom pipx.package_specifier import valid_pypi_name\nfrom pipx.util import PipxError, mkdir, pipx_wrap, rmdir\nfrom pipx.venv import VenvContainer\nfrom pipx.version import version as __version__\n\nlogger = logging.getLogger(__name__)\n\nVenvCompleter = Callable[[str], list[str]]\n\n\ndef print_version() -> None:\n    print(__version__)\n\n\ndef prog_name() -> str:\n    try:\n        prog = os.path.basename(sys.argv[0])\n        if prog == \"__main__.py\":\n            return f\"{sys.executable} -m pipx\"\n        else:\n            return prog\n    except Exception:\n        pass\n    return \"pipx\"\n\n\nSPEC_HELP = textwrap.dedent(\n    \"\"\"\\\n    The package name or specific installation source passed to pip.\n    Runs `pip install -U SPEC`.\n    For example `--spec mypackage==2.0.0` or `--spec  git+https://github.com/user/repo.git@branch`\n    \"\"\"\n)\n\nPIPX_DESCRIPTION = textwrap.dedent(\n    f\"\"\"\n    Install and execute apps from Python packages.\n\n    Binaries can either be installed globally into isolated Virtual Environments\n    or run directly in a temporary Virtual Environment.\n\n    Virtual Environment location is {paths.ctx.venvs!s}.\n    Symlinks to apps are placed in {paths.ctx.bin_dir!s}.\n    Symlinks to manual pages are placed in {paths.ctx.man_dir!s}.\n\n    \"\"\"\n)\nPIPX_DESCRIPTION += pipx_wrap(\n    \"\"\"\n    optional environment variables:\n      PIPX_HOME              Overrides default pipx location. Virtual Environments will be installed to $PIPX_HOME/venvs.\n      PIPX_GLOBAL_HOME       Used instead of PIPX_HOME when the `--global` option is given.\n      PIPX_BIN_DIR           Overrides location of app installations. Apps are symlinked or copied here.\n      PIPX_GLOBAL_BIN_DIR    Used instead of PIPX_BIN_DIR when the `--global` option is given.\n      PIPX_MAN_DIR           Overrides location of manual pages installations. Manual pages are symlinked or copied here.\n      PIPX_GLOBAL_MAN_DIR    Used instead of PIPX_MAN_DIR when the `--global` option is given.\n      PIPX_DEFAULT_PYTHON    Overrides default python used for commands.\n      PIPX_USE_EMOJI         Overrides emoji behavior. Default value varies based on platform.\n      PIPX_HOME_ALLOW_SPACE  Overrides default warning on spaces in the home path\n    \"\"\",\n    subsequent_indent=\" \" * 24,  # match the indent of argparse options\n    keep_newlines=True,\n)\n\nDOC_DEFAULT_PYTHON = os.getenv(\"PIPX__DOC_DEFAULT_PYTHON\", DEFAULT_PYTHON)\n\nINSTALL_DESCRIPTION = textwrap.dedent(\n    f\"\"\"\n    The install command is the preferred way to globally install apps\n    from python packages on your system. It creates an isolated virtual\n    environment for the package, then ensures the package's apps are\n    accessible on your $PATH. The package's manual pages installed in\n    share/man/man[1-9] can be viewed with man on an operating system where\n    it is available and the path in the environment variable `PIPX_MAN_DIR`\n    (default: {paths.DEFAULT_PIPX_MAN_DIR}) is in the man search path\n    ($MANPATH).\n\n    The result: apps you can run from anywhere, located in packages\n    you can cleanly upgrade or uninstall. Guaranteed to not have\n    dependency version conflicts or interfere with your OS's python\n    packages. 'sudo' is not required to do this.\n\n    pipx install PACKAGE_SPEC ...\n    pipx install --python PYTHON PACKAGE_SPEC\n    pipx install VCS_URL\n    pipx install ./LOCAL_PATH\n    pipx install ZIP_FILE\n    pipx install TAR_GZ_FILE\n\n    The PACKAGE_SPEC argument is passed directly to `pip install`.\n\n    Virtual Environments will be installed to `$PIPX_HOME/venvs`.\n    The default pipx home location is {paths.DEFAULT_PIPX_HOME} and can\n    be overridden by setting the environment variable `PIPX_HOME`.\n    If the `--global` option is used, the default pipx home location\n    instead is {paths.DEFAULT_PIPX_GLOBAL_HOME} and can be overridden\n    by setting the environment variable `PIPX_GLOBAL_HOME`.\n\n    The default app location is {paths.DEFAULT_PIPX_BIN_DIR} and can be\n    overridden by setting the environment variable `PIPX_BIN_DIR`.\n    If the `--global` option is used, the default app location instead\n    is {paths.DEFAULT_PIPX_GLOBAL_BIN_DIR} and can be overridden by\n    setting the environment variable `PIPX_GLOBAL_BIN_DIR`.\n\n    The default manual pages location is {paths.DEFAULT_PIPX_MAN_DIR} and\n    can be overridden by setting the environment variable `PIPX_MAN_DIR`.\n    If the `--global` option is used, the default manual pages location\n    instead is {paths.DEFAULT_PIPX_GLOBAL_MAN_DIR} and can be overridden\n    by setting the environment variable `PIPX_GLOBAL_MAN_DIR`.\n\n    The default python executable used to install a package is\n    {DOC_DEFAULT_PYTHON} and can be overridden\n    by setting the environment variable `PIPX_DEFAULT_PYTHON`.\n    \"\"\"\n)\n\n\nclass LineWrapRawTextHelpFormatter(argparse.RawDescriptionHelpFormatter):\n    def _split_lines(self, text: str, width: int) -> list[str]:\n        text = self._whitespace_matcher.sub(\" \", text).strip()\n        return textwrap.wrap(text, width)\n\n\nclass InstalledVenvsCompleter:\n    def __init__(self, venv_container: VenvContainer) -> None:\n        self.packages = [str(p.name) for p in sorted(venv_container.iter_venv_dirs())]\n\n    def use(self, prefix: str, **kwargs: Any) -> list[str]:\n        return [f\"{prefix}{x[len(prefix) :]}\" for x in self.packages if x.startswith(canonicalize_name(prefix))]\n\n\ndef get_pip_args(parsed_args: dict[str, str]) -> list[str]:\n    pip_args: list[str] = []\n    if parsed_args.get(\"index_url\"):\n        pip_args += [\"--index-url\", parsed_args[\"index_url\"]]\n\n    if parsed_args.get(\"pip_args\"):\n        # Stripping the single quote that can be parsed from several shells\n        pip_args_striped = parsed_args[\"pip_args\"].strip(\"'\")\n        pip_args += shlex.split(pip_args_striped, posix=not WINDOWS)\n\n    # make sure --editable is last because it needs to be right before\n    #   package specification\n    if parsed_args.get(\"editable\"):\n        pip_args += [\"--editable\"]\n    return pip_args\n\n\ndef get_runpip_args(pip_args: list[str]) -> list[str]:\n    if len(pip_args) != 1:\n        return pip_args\n\n    candidate = pip_args[0]\n    # Allow a single quoted string like \"install black\".\n    if not any(char.isspace() for char in candidate):\n        return pip_args\n\n    split_args = shlex.split(candidate, posix=not WINDOWS)\n    return split_args or pip_args\n\n\ndef get_venv_args(parsed_args: dict[str, str]) -> list[str]:\n    venv_args: list[str] = []\n    if parsed_args.get(\"system_site_packages\"):\n        venv_args += [\"--system-site-packages\"]\n    return venv_args\n\n\ndef package_is_url(package: str, raise_error: bool = True) -> bool:\n    url_parse_package = urllib.parse.urlparse(package)\n    if url_parse_package.scheme and url_parse_package.netloc:\n        if not raise_error:\n            return True\n        raise PipxError(\"Package cannot be a URL. A valid package name should be passed instead.\")\n    return False\n\n\ndef package_is_path(package: str):\n    if os.path.sep in package or Path(package).exists():\n        raise PipxError(\n            pipx_wrap(\n                f\"\"\"\n                Error: '{package}' looks like a path.\n                Expected the name of an installed package.\n                \"\"\"\n            )\n        )\n\n\ndef run_pipx_command(args: argparse.Namespace, subparsers: dict[str, argparse.ArgumentParser]) -> ExitCode:  # noqa: C901\n    verbose = args.verbose if \"verbose\" in args else False\n\n    pip_args = get_pip_args(vars(args))\n    venv_args = get_venv_args(vars(args))\n\n    venv_container = VenvContainer(paths.ctx.venvs)\n\n    if \"package\" in args:\n        package = args.package\n        package_is_url(package)\n        package_is_path(package)\n\n        if \"spec\" in args and args.spec is not None:\n            if package_is_url(args.spec, raise_error=False):\n                if \"#egg=\" not in args.spec:\n                    args.spec = args.spec + f\"#egg={package}\"\n\n        venv_dir = venv_container.get_venv_dir(valid_pypi_name(package) or package)\n        logger.info(f\"Virtual Environment location is {venv_dir}\")\n\n    if \"packages\" in args:\n        for package in args.packages:\n            package_is_url(package)\n            package_is_path(package)\n        venv_dirs = {\n            package: venv_container.get_venv_dir(valid_pypi_name(package) or package) for package in args.packages\n        }\n        venv_dirs_msg = \"\\n\".join(f\"- {key} : {value}\" for key, value in venv_dirs.items())\n        logger.info(f\"Virtual Environment locations are:\\n{venv_dirs_msg}\")\n\n    if \"skip\" in args:\n        skip_list = [canonicalize_name(x) for x in args.skip]\n\n    python_flag_passed = False\n\n    if \"python\" in args:\n        python_flag_passed = bool(args.python)\n        fetch_missing_python = args.fetch_missing_python\n        try:\n            interpreter = find_python_interpreter(\n                args.python or DEFAULT_PYTHON, fetch_missing_python=fetch_missing_python\n            )\n            args.python = interpreter\n        except InterpreterResolutionError as e:\n            logger.debug(\"Failed to resolve interpreter:\", exc_info=True)\n            print(\n                pipx_wrap(\n                    f\"{hazard} {e}\",\n                    subsequent_indent=\" \" * 4,\n                )\n            )\n            return EXIT_CODE_SPECIFIED_PYTHON_EXECUTABLE_NOT_FOUND\n\n    if args.command == \"run\":\n        commands.run(\n            args.app_with_args[0],\n            args.spec,\n            args.with_,\n            args.path,\n            args.app_with_args[1:],\n            args.python,\n            pip_args,\n            venv_args,\n            args.pypackages,\n            verbose,\n            not args.no_cache,\n        )\n        # We should never reach here because run() is NoReturn.\n        return ExitCode(1)  # type: ignore[unreachable]\n    elif args.command == \"install\":\n        return commands.install(\n            None,\n            None,\n            args.package_spec,\n            paths.ctx.bin_dir,\n            paths.ctx.man_dir,\n            args.python,\n            pip_args,\n            venv_args,\n            verbose,\n            force=args.force,\n            reinstall=False,\n            include_dependencies=args.include_deps,\n            preinstall_packages=args.preinstall,\n            suffix=args.suffix,\n            python_flag_passed=python_flag_passed,\n        )\n    elif args.command == \"install-all\":\n        return commands.install_all(\n            args.spec_metadata_file,\n            paths.ctx.bin_dir,\n            paths.ctx.man_dir,\n            args.python,\n            pip_args,\n            venv_args,\n            verbose,\n            force=args.force,\n        )\n    elif args.command == \"inject\":\n        return commands.inject(\n            venv_dir,\n            args.dependencies,\n            args.requirements,\n            pip_args,\n            verbose=verbose,\n            include_apps=args.include_apps,\n            include_dependencies=args.include_deps,\n            force=args.force,\n            suffix=args.with_suffix,\n        )\n    elif args.command == \"uninject\":\n        return commands.uninject(\n            venv_dir,\n            args.dependencies,\n            local_bin_dir=paths.ctx.bin_dir,\n            local_man_dir=paths.ctx.man_dir,\n            leave_deps=args.leave_deps,\n            verbose=verbose,\n        )\n    elif args.command == \"upgrade\":\n        return commands.upgrade(\n            venv_dirs,\n            args.python,\n            pip_args,\n            venv_args,\n            verbose,\n            include_injected=args.include_injected,\n            force=args.force,\n            install=args.install,\n            python_flag_passed=python_flag_passed,\n        )\n    elif args.command == \"upgrade-all\":\n        return commands.upgrade_all(\n            venv_container,\n            verbose,\n            include_injected=args.include_injected,\n            skip=skip_list,\n            force=args.force,\n            pip_args=pip_args,\n            python_flag_passed=python_flag_passed,\n        )\n    elif args.command == \"upgrade-shared\":\n        return commands.upgrade_shared(\n            verbose,\n            pip_args,\n        )\n    elif args.command == \"list\":\n        return commands.list_packages(\n            venv_container,\n            args.include_injected,\n            args.json,\n            args.short,\n            args.pinned,\n        )\n    elif args.command == \"interpreter\":\n        if args.interpreter_command == \"list\":\n            return commands.list_interpreters(venv_container)\n        elif args.interpreter_command == \"prune\":\n            return commands.prune_interpreters(venv_container)\n        elif args.interpreter_command == \"upgrade\":\n            return commands.upgrade_interpreters(venv_container, verbose)\n        elif args.interpreter_command is None:\n            subparsers[\"interpreter\"].print_help()\n            return EXIT_CODE_OK\n        else:\n            raise PipxError(f\"Unknown interpreter command {args.interpreter_command}\")\n    elif args.command == \"pin\":\n        return commands.pin(venv_dir, verbose, skip_list, args.injected_only)\n    elif args.command == \"unpin\":\n        return commands.unpin(venv_dir, verbose)\n    elif args.command == \"uninstall\":\n        return commands.uninstall(venv_dir, paths.ctx.bin_dir, paths.ctx.man_dir, verbose)\n    elif args.command == \"uninstall-all\":\n        return commands.uninstall_all(\n            venv_container,\n            paths.ctx.bin_dir,\n            paths.ctx.man_dir,\n            verbose,\n        )\n    elif args.command == \"reinstall\":\n        return commands.reinstall(\n            venv_dir=venv_dir,\n            local_bin_dir=paths.ctx.bin_dir,\n            local_man_dir=paths.ctx.man_dir,\n            python=args.python,\n            verbose=verbose,\n            python_flag_passed=python_flag_passed,\n        )\n    elif args.command == \"reinstall-all\":\n        return commands.reinstall_all(\n            venv_container,\n            paths.ctx.bin_dir,\n            paths.ctx.man_dir,\n            args.python,\n            verbose,\n            skip=skip_list,\n            python_flag_passed=python_flag_passed,\n        )\n    elif args.command == \"runpip\":\n        if not venv_dir:  # type: ignore[truthy-bool]\n            raise PipxError(\"Developer error: venv_dir is not defined.\")\n        runpip_args = get_runpip_args(args.pipargs)\n        return commands.run_pip(package, venv_dir, runpip_args, args.verbose)\n    elif args.command == \"ensurepath\":\n        try:\n            return commands.ensure_pipx_paths(prepend=args.prepend, force=args.force, all_shells=args.all_shells)\n        except Exception as e:\n            logger.debug(\"Uncaught Exception:\", exc_info=True)\n            raise PipxError(str(e), wrap_message=False) from None\n    elif args.command == \"completions\":\n        print(constants.completion_instructions)\n        return ExitCode(0)\n    elif args.command == \"environment\":\n        return commands.environment(value=args.value)\n    else:\n        raise PipxError(f\"Unknown command {args.command}\")\n\n\ndef add_pip_venv_args(parser: argparse.ArgumentParser) -> None:\n    parser.add_argument(\n        \"--system-site-packages\",\n        action=\"store_true\",\n        help=\"Give the virtual environment access to the system site-packages dir.\",\n    )\n    parser.add_argument(\"--index-url\", \"-i\", help=\"Base URL of Python Package Index\")\n    parser.add_argument(\n        \"--editable\",\n        \"-e\",\n        help=\"Install a project in editable mode\",\n        action=\"store_true\",\n    )\n    parser.add_argument(\n        \"--pip-args\",\n        help=\"Arbitrary pip arguments to pass directly to pip install/upgrade commands\",\n    )\n\n\ndef add_include_dependencies(parser: argparse.ArgumentParser) -> None:\n    parser.add_argument(\"--include-deps\", help=\"Include apps of dependent packages\", action=\"store_true\")\n\n\ndef add_python_options(parser: argparse.ArgumentParser) -> None:\n    parser.add_argument(\n        \"--python\",\n        help=(\n            \"Python to install with. Possible values can be the executable name (python3.11), \"\n            \"the version of an available system Python or to pass to py launcher (3.11), \"\n            f\"or the full path to the executable. Requires Python {MINIMUM_PYTHON_VERSION} or above.\"\n        ),\n    )\n    parser.add_argument(\n        \"--fetch-missing-python\",\n        action=\"store_true\",\n        help=(\n            \"Whether to fetch a standalone python build from GitHub if the specified python version is not found locally on the system.\"\n        ),\n    )\n\n\ndef _add_install(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"install\",\n        help=\"Install a package\",\n        formatter_class=LineWrapRawTextHelpFormatter,\n        description=INSTALL_DESCRIPTION,\n        parents=[shared_parser],\n    )\n    p.add_argument(\"package_spec\", help=\"package name(s) or pip installation spec(s)\", nargs=\"+\")\n    add_include_dependencies(p)\n    p.add_argument(\n        \"--force\",\n        \"-f\",\n        action=\"store_true\",\n        help=\"Modify existing virtual environment and files in PIPX_BIN_DIR and PIPX_MAN_DIR\",\n    )\n    p.add_argument(\n        \"--suffix\",\n        default=\"\",\n        help=(\n            \"Optional suffix for virtual environment and executable names. \"\n            \"NOTE: The suffix feature is experimental and subject to change.\"\n        ),\n    )\n    add_python_options(p)\n    p.add_argument(\n        \"--preinstall\",\n        action=\"append\",\n        help=(\n            \"Optional package to be installed into the Virtual Environment before \"\n            \"installing the main package. Use this flag multiple times if you want to preinstall multiple packages.\"\n        ),\n    )\n    add_pip_venv_args(p)\n\n\ndef _add_install_all(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"install-all\",\n        help=\"Install all packages\",\n        formatter_class=LineWrapRawTextHelpFormatter,\n        description=\"Installs all the packages according to spec metadata file.\",\n        parents=[shared_parser],\n    )\n    p.add_argument(\"spec_metadata_file\", help=\"Spec metadata file generated from pipx list --json\")\n    p.add_argument(\n        \"--force\",\n        \"-f\",\n        action=\"store_true\",\n        help=\"Modify existing virtual environment and files in PIPX_BIN_DIR and PIPX_MAN_DIR\",\n    )\n    add_python_options(p)\n    add_pip_venv_args(p)\n\n\ndef _add_inject(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"inject\",\n        help=\"Install packages into an existing Virtual Environment\",\n        description=\"Installs packages to an existing pipx-managed virtual environment.\",\n        parents=[shared_parser],\n    )\n    p.add_argument(\n        \"package\",\n        help=\"Name of the existing pipx-managed Virtual Environment to inject into\",\n    ).completer = venv_completer\n    p.add_argument(\n        \"dependencies\",\n        nargs=\"*\",\n        help=\"the packages to inject into the Virtual Environment--either package name or pip package spec\",\n    )\n    p.add_argument(\n        \"-r\",\n        \"--requirement\",\n        dest=\"requirements\",\n        action=\"append\",\n        default=[],\n        metavar=\"file\",\n        help=(\n            \"file containing the packages to inject into the Virtual Environment--\"\n            \"one package name or pip package spec per line. \"\n            \"May be specified multiple times.\"\n        ),\n    )\n    p.add_argument(\n        \"--include-apps\",\n        action=\"store_true\",\n        help=\"Add apps from the injected packages onto your PATH and expose their manual pages\",\n    )\n    p.add_argument(\n        \"--include-deps\",\n        help=\"Include apps of dependent packages. Implies --include-apps\",\n        action=\"store_true\",\n    )\n    add_pip_venv_args(p)\n    p.add_argument(\n        \"--force\",\n        \"-f\",\n        action=\"store_true\",\n        help=\"Modify existing virtual environment and files in PIPX_BIN_DIR and PIPX_MAN_DIR\",\n    )\n    p.add_argument(\n        \"--with-suffix\",\n        action=\"store_true\",\n        help=\"Add the suffix (if given) of the Virtual Environment to the packages to inject\",\n    )\n\n\ndef _add_uninject(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser):\n    p = subparsers.add_parser(\n        \"uninject\",\n        help=\"Uninstall injected packages from an existing Virtual Environment\",\n        description=\"Uninstalls injected packages from an existing pipx-managed virtual environment.\",\n        parents=[shared_parser],\n    )\n    p.add_argument(\n        \"package\",\n        help=\"Name of the existing pipx-managed Virtual Environment to inject into\",\n    ).completer = venv_completer\n    p.add_argument(\n        \"dependencies\",\n        nargs=\"+\",\n        help=\"the package names to uninject from the Virtual Environment\",\n    )\n    p.add_argument(\n        \"--leave-deps\",\n        action=\"store_true\",\n        help=\"Only uninstall the main injected package but leave its dependencies installed.\",\n    )\n\n\ndef _add_pin(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"pin\",\n        help=\"Pin the specified package to prevent it from being upgraded\",\n        description=\"Pin the specified package to prevent it from being upgraded\",\n        parents=[shared_parser],\n    )\n    p.add_argument(\"package\", help=\"Installed package to pin\")\n    p.add_argument(\n        \"--injected-only\",\n        action=\"store_true\",\n        help=(\n            \"Pin injected packages in venv only, so that they will not be upgraded during upgrade operations. \"\n            \"Note that this should not be passed if you wish to pin both main package and injected packages.\"\n        ),\n    )\n    p.add_argument(\n        \"--skip\",\n        nargs=\"+\",\n        default=[],\n        help=\"Skip these packages. Implies `--injected-only`.\",\n    )\n\n\ndef _add_unpin(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"unpin\",\n        help=\"Unpin the specified package\",\n        description=\"Unpin the specified package and all injected packages in its venv to allow them to be upgraded\",\n        parents=[shared_parser],\n    )\n    p.add_argument(\"package\", help=\"Installed package to unpin\")\n\n\ndef _add_upgrade(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"upgrade\",\n        help=\"Upgrade a package\",\n        description=\"Upgrade package(s) in pipx-managed Virtual Environment(s) by running 'pip install --upgrade PACKAGE'\",\n        parents=[shared_parser],\n    )\n    p.add_argument(\"packages\", help=\"package names(s) to upgrade\", nargs=\"+\").completer = venv_completer\n    p.add_argument(\n        \"--include-injected\",\n        action=\"store_true\",\n        help=\"Also upgrade packages injected into the main app's environment\",\n    )\n    p.add_argument(\n        \"--force\",\n        \"-f\",\n        action=\"store_true\",\n        help=\"Modify existing virtual environment and files in PIPX_BIN_DIR and PIPX_MAN_DIR\",\n    )\n    add_pip_venv_args(p)\n    p.add_argument(\n        \"--install\",\n        action=\"store_true\",\n        help=\"Install package spec if missing\",\n    )\n    add_python_options(p)\n\n\ndef _add_upgrade_all(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"upgrade-all\",\n        help=\"Upgrade all packages. Runs `pip install -U <pkgname>` for each package.\",\n        description=\"Upgrades all packages within their virtual environments by running 'pip install --upgrade PACKAGE'\",\n        parents=[shared_parser],\n    )\n    p.add_argument(\n        \"--include-injected\",\n        action=\"store_true\",\n        help=\"Also upgrade packages injected into the main app's environment\",\n    )\n    p.add_argument(\"--skip\", nargs=\"+\", default=[], help=\"skip these packages\")\n    p.add_argument(\n        \"--force\",\n        \"-f\",\n        action=\"store_true\",\n        help=\"Modify existing virtual environment and files in PIPX_BIN_DIR and PIPX_MAN_DIR\",\n    )\n\n\ndef _add_upgrade_shared(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"upgrade-shared\",\n        help=\"Upgrade shared libraries.\",\n        description=\"Upgrade shared libraries.\",\n        parents=[shared_parser],\n    )\n    p.add_argument(\n        \"--pip-args\",\n        help=\"Arbitrary pip arguments to pass directly to pip install/upgrade commands\",\n    )\n\n\ndef _add_uninstall(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"uninstall\",\n        help=\"Uninstall a package\",\n        description=\"Uninstalls a pipx-managed Virtual Environment by deleting it and any files that point to its apps.\",\n        parents=[shared_parser],\n    )\n    p.add_argument(\"package\").completer = venv_completer\n\n\ndef _add_uninstall_all(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None:\n    subparsers.add_parser(\n        \"uninstall-all\",\n        help=\"Uninstall all packages\",\n        description=\"Uninstall all pipx-managed packages\",\n        parents=[shared_parser],\n    )\n\n\ndef _add_reinstall(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"reinstall\",\n        formatter_class=LineWrapRawTextHelpFormatter,\n        help=\"Reinstall a package\",\n        description=textwrap.dedent(\n            \"\"\"\n            Reinstalls a package.\n\n            Package is uninstalled, then installed with pipx install PACKAGE\n            with the same options used in the original install of PACKAGE.\n\n            \"\"\"\n        ),\n        parents=[shared_parser],\n    )\n    p.add_argument(\"package\").completer = venv_completer\n    add_python_options(p)\n\n\ndef _add_reinstall_all(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"reinstall-all\",\n        formatter_class=LineWrapRawTextHelpFormatter,\n        help=\"Reinstall all packages\",\n        description=textwrap.dedent(\n            \"\"\"\n            Reinstalls all packages.\n\n            Packages are uninstalled, then installed with pipx install PACKAGE\n            with the same options used in the original install of PACKAGE.\n            This is useful if you upgraded to a new version of Python and want\n            all your packages to use the latest as well.\n\n            \"\"\"\n        ),\n        parents=[shared_parser],\n    )\n    add_python_options(p)\n    p.add_argument(\"--skip\", nargs=\"+\", default=[], help=\"skip these packages\")\n\n\ndef _add_list(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"list\",\n        help=\"List installed packages\",\n        description=\"List packages and apps installed with pipx\",\n        parents=[shared_parser],\n    )\n    p.add_argument(\n        \"--include-injected\",\n        action=\"store_true\",\n        help=\"Show packages injected into the main app's environment\",\n    )\n    g = p.add_mutually_exclusive_group()\n    g.add_argument(\"--json\", action=\"store_true\", help=\"Output rich data in json format.\")\n    g.add_argument(\"--short\", action=\"store_true\", help=\"List packages only.\")\n    g.add_argument(\n        \"--pinned\",\n        action=\"store_true\",\n        help=\"List pinned packages only. Pass --include-injected at the same time to list injected packages that were pinned.\",\n    )\n    g.add_argument(\"--skip-maintenance\", action=\"store_true\", help=\"(deprecated) No-op\")\n\n\ndef _add_interpreter(\n    subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser\n) -> argparse.ArgumentParser:\n    p: argparse.ArgumentParser = subparsers.add_parser(\n        \"interpreter\",\n        help=\"Interact with interpreters managed by pipx\",\n        description=\"Interact with interpreters managed by pipx\",\n        parents=[shared_parser],\n    )\n    s = p.add_subparsers(\n        title=\"subcommands\",\n        description=\"Get help for commands with pipx interpreter COMMAND --help\",\n        dest=\"interpreter_command\",\n    )\n    s.add_parser(\"list\", help=\"List available interpreters\", description=\"List available interpreters\")\n    s.add_parser(\"prune\", help=\"Prune unused interpreters\", description=\"Prune unused interpreters\")\n    s.add_parser(\n        \"upgrade\",\n        help=\"Upgrade installed interpreters to the latest available micro/patch version\",\n        description=\"Upgrade installed interpreters to the latest available micro/patch version\",\n    )\n    return p\n\n\ndef _add_run(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"run\",\n        formatter_class=LineWrapRawTextHelpFormatter,\n        help=(\n            \"Download the latest version of a package to a temporary virtual environment, \"\n            \"then run an app from it. Also compatible with local `__pypackages__` \"\n            \"directory (experimental).\"\n        ),\n        description=textwrap.dedent(\n            f\"\"\"\n            Download the latest version of a package to a temporary virtual environment,\n            then run an app from it. The environment will be cached\n            and reused for up to {constants.TEMP_VENV_EXPIRATION_THRESHOLD_DAYS} days. This\n            means subsequent calls to 'run' for the same package will be faster\n            since they can reuse the cached Virtual Environment.\n\n            In support of PEP 582 'run' will use apps found in a local __pypackages__\n            directory, if present. Please note that this behavior is experimental,\n            and acts as a companion tool to pythonloc. It may be modified or\n            removed in the future. See https://github.com/cs01/pythonloc.\n            \"\"\"\n        ),\n        parents=[shared_parser],\n    )\n    p.add_argument(\n        \"--no-cache\",\n        action=\"store_true\",\n        help=\"Do not reuse cached virtual environment if it exists\",\n    )\n    p.add_argument(\n        \"app_with_args\",\n        metavar=\"app ...\",\n        nargs=argparse.REMAINDER,\n        help=\"app/package name and any arguments to be passed to it\",\n        default=[],\n    )\n    p.add_argument(\"--path\", action=\"store_true\", help=\"Interpret app name as a local path\")\n    p.add_argument(\n        \"--pypackages\",\n        action=\"store_true\",\n        help=\"Require app to be run from local __pypackages__ directory\",\n    )\n    p.add_argument(\n        \"--with\",\n        dest=\"with_\",\n        action=\"append\",\n        default=[],\n        help=\"Extra dependencies to add to the temporary environment\",\n    )\n    p.add_argument(\"--spec\", help=SPEC_HELP)\n    add_python_options(p)\n    add_pip_venv_args(p)\n    p.set_defaults(subparser=p)\n\n    # modify usage text to show required app argument\n    p.usage = re.sub(r\"^usage: \", \"\", p.format_usage())\n    # add a double-dash to usage text to show requirement before app\n    p.usage = re.sub(r\"\\.\\.\\.\", \"app ...\", p.usage)\n\n\ndef _add_runpip(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"runpip\",\n        help=\"Run pip in an existing pipx-managed Virtual Environment\",\n        description=\"Run pip in an existing pipx-managed Virtual Environment\",\n        parents=[shared_parser],\n    )\n    p.add_argument(\n        \"package\",\n        help=\"Name of the existing pipx-managed Virtual Environment to run pip in\",\n    ).completer = venv_completer\n    p.add_argument(\n        \"pipargs\",\n        nargs=argparse.REMAINDER,\n        default=[],\n        help=\"Arguments to forward to pip command\",\n    )\n\n\ndef _add_ensurepath(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"ensurepath\",\n        help=(\"Ensure directories necessary for pipx operation are in your PATH environment variable.\"),\n        description=(\n            \"Ensure directory where pipx stores apps is in your \"\n            \"PATH environment variable. Also if pipx was installed via \"\n            \"`pip install --user`, ensure pipx itself is in your PATH. \"\n            \"Note that running this may modify \"\n            \"your shell's configuration file(s) such as '~/.bashrc'.\"\n        ),\n        parents=[shared_parser],\n    )\n    p.add_argument(\n        \"--prepend\",\n        action=\"store_true\",\n        help=(\n            \"Prepend directories to your PATH instead of appending. \"\n            \"This is useful if you want to prioritize pipx apps over system apps.\"\n        ),\n    )\n    p.add_argument(\n        \"--force\",\n        \"-f\",\n        action=\"store_true\",\n        help=(\n            \"Add text to your shell's config file even if it looks like your \"\n            \"PATH already contains paths to pipx and pipx-install apps.\"\n        ),\n    )\n    p.add_argument(\n        \"--all-shells\",\n        action=\"store_true\",\n        help=(\"Add directories to PATH in all shells instead of just the current one.\"),\n    )\n\n\ndef _add_environment(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None:\n    p = subparsers.add_parser(\n        \"environment\",\n        formatter_class=LineWrapRawTextHelpFormatter,\n        help=\"Print a list of environment variables and paths used by pipx.\",\n        description=textwrap.dedent(\n            \"\"\"\n            Prints the names and current values of environment variables used by pipx,\n            followed by internal pipx variables which are derived from the environment\n            variables and platform specific default values.\n\n            Available variables:\n            \"\"\"\n        )\n        + textwrap.fill(\", \".join(ENVIRONMENT_VARIABLES), break_long_words=False),\n        parents=[shared_parser],\n    )\n    p.add_argument(\n        \"--value\",\n        \"-V\",\n        choices=ENVIRONMENT_VALUE_CHOICES,\n        metavar=\"VARIABLE\",\n        help=\"Print the value of the variable.\",\n    )\n\n\ndef get_command_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentParser]]:\n    venv_container = VenvContainer(paths.ctx.venvs)\n\n    completer_venvs = InstalledVenvsCompleter(venv_container)\n\n    shared_parser = argparse.ArgumentParser(add_help=False)\n\n    shared_parser.add_argument(\n        \"--quiet\",\n        \"-q\",\n        action=\"count\",\n        default=0,\n        help=(\n            \"Give less output. May be used multiple times corresponding to the\"\n            \" ERROR and CRITICAL logging levels. The count maxes out at 2.\"\n        ),\n    )\n\n    shared_parser.add_argument(\n        \"--verbose\",\n        \"-v\",\n        action=\"count\",\n        default=0,\n        help=(\n            \"Give more output. May be used multiple times corresponding to the\"\n            \" INFO, DEBUG and NOTSET logging levels. The count maxes out at 3.\"\n        ),\n    )\n\n    if not constants.WINDOWS:\n        shared_parser.add_argument(\n            \"--global\",\n            action=\"store_true\",\n            dest=\"is_global\",\n            help=\"Perform action globally for all users.\",\n        )\n\n    parser = argparse.ArgumentParser(\n        prog=prog_name(),\n        formatter_class=LineWrapRawTextHelpFormatter,\n        description=PIPX_DESCRIPTION,\n    )\n    parser.man_short_description = PIPX_DESCRIPTION.splitlines()[1]  # type: ignore[attr-defined]\n\n    parser.add_argument(\n        \"--quiet\",\n        \"-q\",\n        action=\"count\",\n        default=0,\n        help=(\n            \"Give less output. May be used multiple times corresponding to the\"\n            \" ERROR and CRITICAL logging levels. The count maxes out at 2.\"\n        ),\n    )\n\n    parser.add_argument(\n        \"--verbose\",\n        \"-v\",\n        action=\"count\",\n        default=0,\n        help=(\n            \"Give more output. May be used multiple times corresponding to the\"\n            \" INFO, DEBUG and NOTSET logging levels. The count maxes out at 3.\"\n        ),\n    )\n\n    subparsers = parser.add_subparsers(dest=\"command\", description=\"Get help for commands with pipx COMMAND --help\")\n\n    subparsers_with_subcommands = {}\n    _add_install(subparsers, shared_parser)\n    _add_install_all(subparsers, shared_parser)\n    _add_uninject(subparsers, completer_venvs.use, shared_parser)\n    _add_inject(subparsers, completer_venvs.use, shared_parser)\n    _add_pin(subparsers, completer_venvs.use, shared_parser)\n    _add_unpin(subparsers, completer_venvs.use, shared_parser)\n    _add_upgrade(subparsers, completer_venvs.use, shared_parser)\n    _add_upgrade_all(subparsers, shared_parser)\n    _add_upgrade_shared(subparsers, shared_parser)\n    _add_uninstall(subparsers, completer_venvs.use, shared_parser)\n    _add_uninstall_all(subparsers, shared_parser)\n    _add_reinstall(subparsers, completer_venvs.use, shared_parser)\n    _add_reinstall_all(subparsers, shared_parser)\n    _add_list(subparsers, shared_parser)\n    subparsers_with_subcommands[\"interpreter\"] = _add_interpreter(subparsers, shared_parser)\n    _add_run(subparsers, shared_parser)\n    _add_runpip(subparsers, completer_venvs.use, shared_parser)\n    _add_ensurepath(subparsers, shared_parser)\n    _add_environment(subparsers, shared_parser)\n\n    parser.add_argument(\"--version\", action=\"store_true\", help=\"Print version and exit\")\n    subparsers.add_parser(\n        \"completions\",\n        help=\"Print instructions on enabling shell completions for pipx\",\n        description=\"Print instructions on enabling shell completions for pipx\",\n        parents=[shared_parser],\n    )\n    return parser, subparsers_with_subcommands\n\n\ndef delete_oldest_logs(file_list: list[Path], keep_number: int) -> None:\n    file_list = sorted(file_list)\n    if len(file_list) > keep_number:\n        for existing_file in file_list[:-keep_number]:\n            try:\n                existing_file.unlink()\n            except FileNotFoundError:\n                pass\n\n\ndef _setup_log_file(pipx_log_dir: Optional[Path] = None) -> Path:\n    max_logs = 10\n    pipx_log_dir = pipx_log_dir or paths.ctx.logs\n    # don't use utils.mkdir, to prevent emission of log message\n    pipx_log_dir.mkdir(parents=True, exist_ok=True)\n\n    delete_oldest_logs(list(pipx_log_dir.glob(\"cmd_*[0-9].log\")), max_logs)\n    delete_oldest_logs(list(pipx_log_dir.glob(\"cmd_*_pip_errors.log\")), max_logs)\n\n    datetime_str = time.strftime(\"%Y-%m-%d_%H.%M.%S\")\n    log_file = pipx_log_dir / f\"cmd_{datetime_str}.log\"\n    counter = 1\n    while log_file.exists() and counter < 10:\n        log_file = pipx_log_dir / f\"cmd_{datetime_str}_{counter}.log\"\n        counter += 1\n\n    log_file.touch()\n\n    return log_file\n\n\ndef setup_log_file() -> Path:\n    try:\n        return _setup_log_file()\n    except PermissionError:\n        return _setup_log_file(platformdirs.user_log_path(\"pipx\"))\n\n\ndef setup_logging(verbose: int) -> None:\n    pipx_str = (sys.stdout and sys.stdout.isatty() and bold(green(\"pipx >\"))) or \"pipx >\"\n    paths.ctx.log_file = setup_log_file()\n\n    # Determine logging level, a value between 0 and 50\n    level_number = min(max(0, logging.WARNING - 10 * verbose), 50)\n\n    level = logging.getLevelName(level_number)\n\n    # \"incremental\" is False so previous pytest tests don't accumulate handlers\n    logging_config = {\n        \"version\": 1,\n        \"formatters\": {\n            \"stream_nonverbose\": {\n                \"class\": \"logging.Formatter\",\n                \"format\": \"{message}\",\n                \"style\": \"{\",\n            },\n            \"stream_verbose\": {\n                \"class\": \"logging.Formatter\",\n                \"format\": pipx_str + \"({funcName}:{lineno}): {message}\",\n                \"style\": \"{\",\n            },\n            \"file\": {\n                \"class\": \"logging.Formatter\",\n                \"format\": \"{relativeCreated: >8.1f}ms ({funcName}:{lineno}): {message}\",\n                \"style\": \"{\",\n            },\n        },\n        \"handlers\": {\n            \"stream\": {\n                \"class\": \"logging.StreamHandler\",\n                \"formatter\": \"stream_verbose\" if verbose else \"stream_nonverbose\",\n                \"level\": level,\n            },\n            \"file\": {\n                \"class\": \"logging.FileHandler\",\n                \"formatter\": \"file\",\n                \"filename\": str(paths.ctx.log_file),\n                \"encoding\": \"utf-8\",\n                \"level\": \"DEBUG\",\n            },\n        },\n        \"loggers\": {\"pipx\": {\"handlers\": [\"stream\", \"file\"], \"level\": \"DEBUG\"}},\n        \"incremental\": False,\n    }\n    logging.config.dictConfig(logging_config)\n\n\ndef setup(args: argparse.Namespace) -> None:\n    if \"version\" in args and args.version:\n        print_version()\n        sys.exit(0)\n\n    if not constants.WINDOWS and args.is_global:\n        paths.ctx.make_global()\n\n    verbose = args.verbose - args.quiet\n\n    setup_logging(verbose)\n\n    logger.debug(f\"{time.strftime('%Y-%m-%d %H:%M:%S')}\")\n    logger.debug(f\"{' '.join(sys.argv)}\")\n    logger.info(f\"pipx version is {__version__}\")\n    logger.info(f\"Default python interpreter is '{DEFAULT_PYTHON}'\")\n\n    mkdir(paths.ctx.venvs)\n    mkdir(paths.ctx.bin_dir)\n    mkdir(paths.ctx.man_dir)\n    mkdir(paths.ctx.venv_cache)\n    mkdir(paths.ctx.standalone_python_cachedir)\n\n    for cachedir in [\n        paths.ctx.venv_cache,\n        paths.ctx.standalone_python_cachedir,\n    ]:\n        cachedir_tag = cachedir / \"CACHEDIR.TAG\"\n        if not cachedir_tag.exists():\n            logger.debug(\"Adding CACHEDIR.TAG to cache directory\")\n            signature = (\n                \"Signature: 8a477f597d28d172789f06886806bc55\\n\"\n                \"# This file is a cache directory tag created by pipx.\\n\"\n                \"# For information about cache directory tags, see:\\n\"\n                \"#       https://bford.info/cachedir/\\n\"\n            )\n            with open(cachedir_tag, \"w\") as file:\n                file.write(signature)\n\n    rmdir(paths.ctx.trash, False)\n\n    old_pipx_venv_location = paths.ctx.venvs / \"pipx-app\"\n    if old_pipx_venv_location.exists():\n        logger.warning(\n            pipx_wrap(\n                f\"\"\"\n                {hazard}  A virtual environment for pipx was detected at\n                {old_pipx_venv_location!s}. The 'pipx-app' package has been\n                renamed back to 'pipx'\n                (https://github.com/pypa/pipx/issues/82).\n                \"\"\",\n                subsequent_indent=\" \" * 4,\n            )\n        )\n\n\ndef check_args(parsed_pipx_args: argparse.Namespace) -> None:\n    if parsed_pipx_args.command == \"run\":\n        # we manually discard a first -- because using nargs=argparse.REMAINDER\n        #   will not do it automatically\n        if parsed_pipx_args.app_with_args and parsed_pipx_args.app_with_args[0] == \"--\":\n            parsed_pipx_args.app_with_args.pop(0)\n        # since we would like app to be required but not in a separate argparse\n        #   add_argument, we implement our own missing required arg error\n        if not parsed_pipx_args.app_with_args:\n            parsed_pipx_args.subparser.error(\"the following arguments are required: app\")\n\n\ndef cli() -> ExitCode:\n    \"\"\"Entry point from command line\"\"\"\n    try:\n        hide_cursor()\n        parser, subparsers = get_command_parser()\n        argcomplete.autocomplete(parser, always_complete_options=False)\n        parsed_pipx_args = parser.parse_args()\n        setup(parsed_pipx_args)\n        check_args(parsed_pipx_args)\n        if not parsed_pipx_args.command:\n            parser.print_help()\n            return ExitCode(1)\n        return run_pipx_command(parsed_pipx_args, subparsers)\n    except PipxError as e:\n        print(str(e), file=sys.stderr)\n        logger.debug(f\"PipxError: {e}\", exc_info=True)\n        return ExitCode(1)\n    except KeyboardInterrupt:\n        return ExitCode(1)\n    except Exception:\n        logger.debug(\"Uncaught Exception:\", exc_info=True)\n        raise\n    finally:\n        logger.debug(\"pipx finished.\")\n        show_cursor()\n\n\nif __name__ == \"__main__\":\n    sys.exit(cli())\n"
  },
  {
    "path": "src/pipx/package_specifier.py",
    "content": "# Valid package specifiers for pipx:\n#   PEP508-compliant\n#   git+<URL>\n#   <URL>\n#   <pypi_package_name>\n#   <pypi_package_name><version_specifier>\n#   <local_path>\n\nimport logging\nimport re\nimport urllib.parse\nfrom dataclasses import dataclass\nfrom pathlib import Path\nfrom typing import Optional\n\nfrom packaging.requirements import InvalidRequirement, Requirement\nfrom packaging.specifiers import SpecifierSet\nfrom packaging.utils import canonicalize_name\n\nfrom pipx.emojis import hazard\nfrom pipx.util import PipxError, pipx_wrap\n\nlogger = logging.getLogger(__name__)\n\nARCHIVE_EXTENSIONS = (\".whl\", \".tar.gz\", \".zip\")\n\n\n@dataclass(frozen=True)\nclass ParsedPackage:\n    valid_pep508: Optional[Requirement]\n    valid_url: Optional[str]\n    valid_local_path: Optional[str]\n\n\ndef _split_path_extras(package_spec: str) -> tuple[str, str]:\n    \"\"\"Returns (path, extras_string)\"\"\"\n    package_spec_extras_re = re.search(r\"(.+)(\\[.+\\])\", package_spec)\n    if package_spec_extras_re:\n        return (package_spec_extras_re.group(1), package_spec_extras_re.group(2))\n    else:\n        return (package_spec, \"\")\n\n\ndef _check_package_path(package_path: str) -> tuple[Path, bool]:\n    pkg_path = Path(package_path)\n    pkg_path_exists = pkg_path.exists()\n\n    return (pkg_path, pkg_path_exists)\n\n\ndef _parse_specifier(package_spec: str) -> ParsedPackage:\n    \"\"\"Parse package_spec as would be given to pipx\"\"\"\n    # If package_spec is valid pypi name, pip will always treat it as a\n    #       pypi package, not checking for local path.\n    #       We replicate pypi precedence here (only non-valid-pypi names\n    #       initiate check for local path, e.g. './package-name')\n    valid_pep508 = None\n    valid_url = None\n    valid_local_path = None\n\n    try:\n        package_req = Requirement(package_spec)\n    except InvalidRequirement:\n        # not a valid PEP508 package specification\n        pass\n    else:\n        # valid PEP508 package specification\n        valid_pep508 = package_req\n\n    if valid_pep508 and package_req.name.endswith(ARCHIVE_EXTENSIONS):\n        # It might be a local archive\n        (package_path, package_path_exists) = _check_package_path(package_req.name)\n\n        if package_path_exists:\n            valid_local_path = str(package_path.resolve())\n        else:\n            raise PipxError(f\"{package_path} does not exist\")\n\n    # If this looks like a URL, treat it as such.\n    if not valid_pep508:\n        parsed_url = urllib.parse.urlsplit(package_spec)\n        if parsed_url.scheme and parsed_url.netloc:\n            valid_url = package_spec\n\n    # Treat the input as a local path if it does not look like a PEP 508\n    # specifier nor a URL. In this case we want to split out the extra part.\n    if not valid_pep508 and not valid_url:\n        (package_path_str, package_extras_str) = _split_path_extras(package_spec)\n        (package_path, package_path_exists) = _check_package_path(package_path_str)\n        if package_path_exists:\n            valid_local_path = str(package_path.resolve()) + package_extras_str\n\n    if not valid_pep508 and not valid_url and not valid_local_path:\n        raise PipxError(f\"Unable to parse package spec: {package_spec}\")\n\n    if valid_pep508 and valid_local_path:\n        # It is a valid local path without \"./\"\n        # Use valid_local_path\n        valid_pep508 = None\n\n    return ParsedPackage(\n        valid_pep508=valid_pep508,\n        valid_url=valid_url,\n        valid_local_path=valid_local_path,\n    )\n\n\ndef package_or_url_from_pep508(requirement: Requirement, remove_version_specifiers: bool = False) -> str:\n    requirement.marker = None\n    requirement.name = canonicalize_name(requirement.name)\n    if remove_version_specifiers:\n        requirement.specifier = SpecifierSet(\"\")\n    return str(requirement)\n\n\ndef _parsed_package_to_package_or_url(parsed_package: ParsedPackage, remove_version_specifiers: bool) -> str:\n    if parsed_package.valid_pep508 is not None:\n        if parsed_package.valid_pep508.marker is not None:\n            logger.warning(\n                pipx_wrap(\n                    f\"\"\"\n                    {hazard}  Ignoring environment markers\n                    ({parsed_package.valid_pep508.marker}) in package\n                    specification. Use pipx options to specify this type of\n                    information.\n                    \"\"\",\n                    subsequent_indent=\" \" * 4,\n                )\n            )\n        package_or_url = package_or_url_from_pep508(\n            parsed_package.valid_pep508,\n            remove_version_specifiers=remove_version_specifiers,\n        )\n    elif parsed_package.valid_url is not None:\n        package_or_url = parsed_package.valid_url\n    elif parsed_package.valid_local_path is not None:\n        package_or_url = parsed_package.valid_local_path\n\n    logger.info(f\"cleaned package spec: {package_or_url}\")\n    return package_or_url\n\n\ndef parse_specifier_for_install(package_spec: str, pip_args: list[str]) -> tuple[str, list[str]]:\n    \"\"\"Return package_or_url and pip_args suitable for pip install\n\n    Specifically:\n    * Strip any markers (e.g. python_version > \"3.4\")\n    * Ensure --editable is removed for any package_spec not a local path\n    * Convert local paths to absolute paths\n    \"\"\"\n    parsed_package = _parse_specifier(package_spec)\n    package_or_url = _parsed_package_to_package_or_url(parsed_package, remove_version_specifiers=False)\n    if \"--editable\" in pip_args and not parsed_package.valid_local_path:\n        logger.warning(\n            pipx_wrap(\n                f\"\"\"\n                {hazard}  Ignoring --editable install option. pipx disallows it\n                for anything but a local path, to avoid having to create a new\n                src/ directory.\n                \"\"\",\n                subsequent_indent=\" \" * 4,\n            )\n        )\n        pip_args.remove(\"--editable\")\n\n    for index, option in enumerate(pip_args):\n        if not option.startswith((\"-c\", \"--constraint\")):\n            continue\n\n        if option in (\"-c\", \"--constraint\"):\n            argument_index = index + 1\n            if argument_index < len(pip_args):\n                constraints_file = pip_args[argument_index]\n                pip_args[argument_index] = str(Path(constraints_file).expanduser().resolve())\n\n        else:  # option == \"--constraint=some_path\"\n            option_list = option.split(\"=\")\n\n            if len(option_list) == 2:\n                key, value = option_list\n                value_path = Path(value).expanduser().resolve()\n                pip_args[index] = f\"{key}={value_path}\"\n\n        break\n\n    return package_or_url, pip_args\n\n\ndef parse_specifier_for_metadata(package_spec: str) -> str:\n    \"\"\"Return package_or_url suitable for pipx metadata\n\n    Specifically:\n    * Strip any markers (e.g. python_version > 3.4)\n    * Convert local paths to absolute paths\n    \"\"\"\n    parsed_package = _parse_specifier(package_spec)\n    return _parsed_package_to_package_or_url(parsed_package, remove_version_specifiers=False)\n\n\ndef parse_specifier_for_upgrade(package_spec: str) -> str:\n    \"\"\"Return package_or_url suitable for pip upgrade\n\n    Specifically:\n    * Strip any version specifiers (e.g. package == 1.5.4)\n    * Strip any markers (e.g. python_version > 3.4)\n    * Convert local paths to absolute paths\n    \"\"\"\n    parsed_package = _parse_specifier(package_spec)\n    return _parsed_package_to_package_or_url(parsed_package, remove_version_specifiers=True)\n\n\ndef get_extras(package_spec: str) -> set[str]:\n    parsed_package = _parse_specifier(package_spec)\n    if parsed_package.valid_pep508 and parsed_package.valid_pep508.extras is not None:\n        return parsed_package.valid_pep508.extras\n    elif parsed_package.valid_local_path:\n        (_, package_extras_str) = _split_path_extras(parsed_package.valid_local_path)\n        return Requirement(\"notapackage\" + package_extras_str).extras\n\n    return set()\n\n\ndef valid_pypi_name(package_spec: str) -> Optional[str]:\n    try:\n        package_req = Requirement(package_spec)\n    except InvalidRequirement:\n        # not a valid PEP508 package specification\n        return None\n\n    if package_req.url or package_req.name.endswith(ARCHIVE_EXTENSIONS):\n        # package name supplied by user might not match package found in URL,\n        # also if package name ends with archive extension, it might be a local archive file,\n        #   so force package name determination the long way\n        return None\n\n    return canonicalize_name(package_req.name)\n\n\ndef fix_package_name(package_or_url: str, package_name: str) -> str:\n    try:\n        package_req = Requirement(package_or_url)\n    except InvalidRequirement:\n        # not a valid PEP508 package specification\n        return package_or_url\n\n    if package_req.name.endswith(ARCHIVE_EXTENSIONS):\n        return str(package_req)\n\n    if canonicalize_name(package_req.name) != canonicalize_name(package_name):\n        logger.warning(\n            pipx_wrap(\n                f\"\"\"\n                {hazard}  Name supplied in package specifier was\n                {package_req.name!r} but package found has name {package_name!r}.\n                Using {package_name!r}.\n                \"\"\",\n                subsequent_indent=\" \" * 4,\n            )\n        )\n    package_req.name = package_name\n\n    return str(package_req)\n"
  },
  {
    "path": "src/pipx/paths.py",
    "content": "import logging\nimport os\nfrom pathlib import Path\nfrom typing import Optional\n\nfrom platformdirs import user_cache_path, user_data_path, user_log_path\n\nfrom pipx.constants import LINUX, WINDOWS\nfrom pipx.emojis import hazard, strtobool\nfrom pipx.util import pipx_wrap\n\nif LINUX:\n    DEFAULT_PIPX_HOME = Path(user_data_path(\"pipx\"))\n    FALLBACK_PIPX_HOMES = [Path.home() / \".local/pipx\"]\nelif WINDOWS:\n    DEFAULT_PIPX_HOME = Path.home() / \"pipx\"\n    FALLBACK_PIPX_HOMES = [Path.home() / \".local/pipx\", Path(user_data_path(\"pipx\"))]\nelse:\n    DEFAULT_PIPX_HOME = Path.home() / \".local/pipx\"\n    FALLBACK_PIPX_HOMES = [Path(user_data_path(\"pipx\"))]\n\nDEFAULT_PIPX_BIN_DIR = Path.home() / \".local/bin\"\nDEFAULT_PIPX_MAN_DIR = Path.home() / \".local/share/man\"\nDEFAULT_PIPX_GLOBAL_HOME = Path(\"/opt/pipx\")\nDEFAULT_PIPX_GLOBAL_BIN_DIR = Path(\"/usr/local/bin\")\nDEFAULT_PIPX_GLOBAL_MAN_DIR = Path(\"/usr/local/share/man\")\n\n# Overrides for testing\nOVERRIDE_PIPX_HOME = None\nOVERRIDE_PIPX_BIN_DIR = None\nOVERRIDE_PIPX_MAN_DIR = None\nOVERRIDE_PIPX_SHARED_LIBS = None\nOVERRIDE_PIPX_GLOBAL_HOME = None\nOVERRIDE_PIPX_GLOBAL_BIN_DIR = None\nOVERRIDE_PIPX_GLOBAL_MAN_DIR = None\n\nlogger = logging.getLogger(__name__)\n\n\ndef get_expanded_environ(env_name: str) -> Optional[Path]:\n    val = os.environ.get(env_name)\n    if val is not None:\n        return Path(val).expanduser().resolve()\n    return val\n\n\nclass _PathContext:\n    _base_home: Optional[Path]\n    _default_home: Path\n    _base_bin: Optional[Path]\n    _default_bin: Path\n    _base_man: Optional[Path]\n    _default_man: Path\n    _default_log: Path\n    _default_cache: Path\n    _default_trash: Path\n    _base_shared_libs: Optional[Path]\n    _fallback_home: Optional[Path]\n    _home_exists: bool\n    log_file: Optional[Path] = None\n\n    def __init__(self):\n        self.make_local()\n\n    @property\n    def venvs(self) -> Path:\n        return self.home / \"venvs\"\n\n    @property\n    def logs(self) -> Path:\n        if self._home_exists or not LINUX:\n            return self.home / \"logs\"\n        return self._default_log\n\n    @property\n    def trash(self) -> Path:\n        if self._home_exists:\n            return self.home / \".trash\"\n        return self._default_trash\n\n    @property\n    def venv_cache(self) -> Path:\n        if self._home_exists or not LINUX:\n            return self.home / \".cache\"\n        return self._default_cache\n\n    @property\n    def bin_dir(self) -> Path:\n        return (self._base_bin or self._default_bin).resolve()\n\n    @property\n    def man_dir(self) -> Path:\n        return (self._base_man or self._default_man).resolve()\n\n    @property\n    def home(self) -> Path:\n        if self._base_home:\n            home = Path(self._base_home)\n        elif self._fallback_home:\n            home = self._fallback_home\n        else:\n            home = self._default_home\n        return home.resolve()\n\n    @property\n    def shared_libs(self) -> Path:\n        return (self._base_shared_libs or self.home / \"shared\").resolve()\n\n    def make_local(self) -> None:\n        self._base_home = OVERRIDE_PIPX_HOME or get_expanded_environ(\"PIPX_HOME\")  # type: ignore[redundant-expr]\n        self._default_home = DEFAULT_PIPX_HOME\n        self._base_bin = OVERRIDE_PIPX_BIN_DIR or get_expanded_environ(\"PIPX_BIN_DIR\")  # type: ignore[redundant-expr]\n        self._default_bin = DEFAULT_PIPX_BIN_DIR\n        self._base_man = OVERRIDE_PIPX_MAN_DIR or get_expanded_environ(\"PIPX_MAN_DIR\")  # type: ignore[redundant-expr]\n        self._default_man = DEFAULT_PIPX_MAN_DIR\n        self._base_shared_libs = OVERRIDE_PIPX_SHARED_LIBS or get_expanded_environ(\"PIPX_SHARED_LIBS\")  # type: ignore[redundant-expr]\n        self._default_log = Path(user_log_path(\"pipx\"))\n        self._default_cache = Path(user_cache_path(\"pipx\"))\n        self._default_trash = self._default_home / \"trash\"\n        self._fallback_home = next(iter([fallback for fallback in FALLBACK_PIPX_HOMES if fallback.exists()]), None)\n        self._home_exists = self._base_home is not None or any(fallback.exists() for fallback in FALLBACK_PIPX_HOMES)\n\n    def make_global(self) -> None:\n        self._base_home = OVERRIDE_PIPX_GLOBAL_HOME or get_expanded_environ(\"PIPX_GLOBAL_HOME\")  # type: ignore[redundant-expr]\n        self._default_home = DEFAULT_PIPX_GLOBAL_HOME\n        self._base_bin = OVERRIDE_PIPX_GLOBAL_BIN_DIR or get_expanded_environ(\"PIPX_GLOBAL_BIN_DIR\")  # type: ignore[redundant-expr]\n        self._default_bin = DEFAULT_PIPX_GLOBAL_BIN_DIR\n        self._base_man = OVERRIDE_PIPX_GLOBAL_MAN_DIR or get_expanded_environ(\"PIPX_GLOBAL_MAN_DIR\")  # type: ignore[redundant-expr]\n        self._default_man = DEFAULT_PIPX_GLOBAL_MAN_DIR\n        self._default_log = self._default_home / \"logs\"\n        self._default_cache = self._default_home / \".cache\"\n        self._default_trash = self._default_home / \"trash\"\n        self._base_shared_libs = None\n        self._fallback_home = None\n        self._home_exists = self._base_home is not None\n\n    @property\n    def standalone_python_cachedir(self) -> Path:\n        return self.home / \"py\"\n\n    @property\n    def allow_spaces_in_home_path(self) -> bool:\n        return strtobool(os.getenv(\"PIPX_HOME_ALLOW_SPACE\", \"0\"))\n\n    def log_warnings(self):\n        if \" \" in str(self.home) and not self.allow_spaces_in_home_path:\n            logger.warning(\n                pipx_wrap(\n                    (\n                        f\"{hazard} Found a space in the pipx home path. We heavily discourage this, due to \"\n                        \"multiple incompatibilities. Please check our docs for more information on this, \"\n                        \"as well as some pointers on how to migrate to a different home path.\"\n                    ),\n                    subsequent_indent=\" \" * 4,\n                )\n            )\n            logger.warning(\n                pipx_wrap(\n                    (f\"{hazard} To see your PIPX_HOME dir: pipx environment --value PIPX_HOME\"),\n                    subsequent_indent=\" \" * 4,\n                )\n            )\n            logger.warning(\n                pipx_wrap(\n                    (f\"{hazard} Most likely fix on macOS: mv ~/Library/Application\\\\ Support/pipx ~/.local/\"),\n                    subsequent_indent=\" \" * 4,\n                )\n            )\n\n        fallback_home_exists = self._fallback_home is not None and self._fallback_home.exists()\n        specific_home_exists = self.home != self._fallback_home\n        if fallback_home_exists and specific_home_exists:\n            logger.info(\n                pipx_wrap(\n                    (\n                        f\"Both a specific pipx home folder ({self.home}) and the fallback \"\n                        f\"pipx home folder ({self._fallback_home}) exist. If you are done migrating from the\"\n                        \"fallback to the new location, it is safe to delete the fallback location.\"\n                    ),\n                    subsequent_indent=\" \" * 4,\n                )\n            )\n\n\nctx = _PathContext()\nctx.log_warnings()\n"
  },
  {
    "path": "src/pipx/pipx_metadata_file.py",
    "content": "import json\nimport logging\nfrom dataclasses import asdict, dataclass, field\nfrom pathlib import Path\nfrom typing import Any, Optional, Union\n\nfrom pipx.emojis import hazard\nfrom pipx.util import PipxError, pipx_wrap\n\nlogger = logging.getLogger(__name__)\n\n\nPIPX_INFO_FILENAME = \"pipx_metadata.json\"\n\n\nclass JsonEncoderHandlesPath(json.JSONEncoder):\n    def default(self, obj: Any) -> Any:\n        # only handles what json.JSONEncoder doesn't understand by default\n        if isinstance(obj, Path):\n            return {\"__type__\": \"Path\", \"__Path__\": str(obj)}\n        return super().default(obj)\n\n\ndef _json_decoder_object_hook(json_dict: dict[str, Any]) -> Union[dict[str, Any], Path]:\n    if json_dict.get(\"__type__\") == \"Path\" and \"__Path__\" in json_dict:\n        return Path(json_dict[\"__Path__\"])\n    return json_dict\n\n\n@dataclass(frozen=True)\nclass PackageInfo:\n    package: Optional[str]\n    package_or_url: Optional[str]\n    pip_args: list[str]\n    include_dependencies: bool\n    include_apps: bool\n    apps: list[str]\n    app_paths: list[Path]\n    apps_of_dependencies: list[str]\n    app_paths_of_dependencies: dict[str, list[Path]]\n    package_version: str\n    man_pages: list[str] = field(default_factory=list)\n    man_paths: list[Path] = field(default_factory=list)\n    man_pages_of_dependencies: list[str] = field(default_factory=list)\n    man_paths_of_dependencies: dict[str, list[Path]] = field(default_factory=dict)\n    suffix: str = \"\"\n    pinned: bool = False\n\n\nclass PipxMetadata:\n    # Only change this if file format changes\n    # V0.1 -> original version\n    # V0.2 -> Improve handling of suffixes\n    # V0.3 -> Add man pages fields\n    # V0.4 -> Add source interpreter\n    # V0.5 -> Add pinned\n    __METADATA_VERSION__: str = \"0.5\"\n\n    def __init__(self, venv_dir: Path, read: bool = True):\n        self.venv_dir = venv_dir\n        # We init this instance with reasonable fallback defaults for all\n        #   members, EXCEPT for those we cannot know:\n        #       self.main_package.package=None\n        #       self.main_package.package_or_url=None\n        #       self.python_version=None\n        self.main_package = PackageInfo(\n            package=None,\n            package_or_url=None,\n            pip_args=[],\n            include_dependencies=False,\n            include_apps=True,  # always True for main_package\n            apps=[],\n            app_paths=[],\n            apps_of_dependencies=[],\n            app_paths_of_dependencies={},\n            man_pages=[],\n            man_paths=[],\n            man_pages_of_dependencies=[],\n            man_paths_of_dependencies={},\n            package_version=\"\",\n        )\n        self.python_version: Optional[str] = None\n        self.source_interpreter: Optional[Path] = None\n        self.venv_args: list[str] = []\n        self.injected_packages: dict[str, PackageInfo] = {}\n\n        if read:\n            self.read()\n\n    def to_dict(self) -> dict[str, Any]:\n        return {\n            \"main_package\": asdict(self.main_package),\n            \"python_version\": self.python_version,\n            \"source_interpreter\": self.source_interpreter,\n            \"venv_args\": self.venv_args,\n            \"injected_packages\": {name: asdict(data) for (name, data) in self.injected_packages.items()},\n            \"pipx_metadata_version\": self.__METADATA_VERSION__,\n        }\n\n    def _convert_legacy_metadata(self, metadata_dict: dict[str, Any]) -> dict[str, Any]:\n        if metadata_dict[\"pipx_metadata_version\"] in (self.__METADATA_VERSION__):\n            pass\n        elif metadata_dict[\"pipx_metadata_version\"] == \"0.4\":\n            metadata_dict[\"pinned\"] = False\n        elif metadata_dict[\"pipx_metadata_version\"] in (\"0.2\", \"0.3\"):\n            metadata_dict[\"source_interpreter\"] = None\n        elif metadata_dict[\"pipx_metadata_version\"] == \"0.1\":\n            main_package_data = metadata_dict[\"main_package\"]\n            if main_package_data[\"package\"] != self.venv_dir.name:\n                # handle older suffixed packages gracefully\n                main_package_data[\"suffix\"] = self.venv_dir.name.replace(main_package_data[\"package\"], \"\")\n            metadata_dict[\"source_interpreter\"] = None\n        else:\n            raise PipxError(\n                f\"\"\"\n                {self.venv_dir.name}: Unknown metadata version\n                {metadata_dict[\"pipx_metadata_version\"]}. Perhaps it was\n                installed with a later version of pipx.\n                \"\"\"\n            )\n        return metadata_dict\n\n    def from_dict(self, input_dict: dict[str, Any]) -> None:\n        input_dict = self._convert_legacy_metadata(input_dict)\n        self.main_package = PackageInfo(**input_dict[\"main_package\"])\n        self.python_version = input_dict[\"python_version\"]\n        self.source_interpreter = (\n            Path(input_dict[\"source_interpreter\"]) if input_dict.get(\"source_interpreter\") else None\n        )\n        self.venv_args = input_dict[\"venv_args\"]\n        self.injected_packages = {\n            f\"{name}{data.get('suffix', '')}\": PackageInfo(**data)\n            for (name, data) in input_dict[\"injected_packages\"].items()\n        }\n\n    def _validate_before_write(self) -> None:\n        if (\n            self.main_package.package is None\n            or self.main_package.package_or_url is None\n            or not self.main_package.include_apps\n        ):\n            logger.debug(f\"PipxMetadata corrupt:\\n{self.to_dict()}\")\n            raise PipxError(\"Internal Error: PipxMetadata is corrupt, cannot write.\")\n\n    def write(self) -> None:\n        self._validate_before_write()\n        try:\n            with open(self.venv_dir / PIPX_INFO_FILENAME, \"w\", encoding=\"utf-8\") as pipx_metadata_fh:\n                json.dump(\n                    self.to_dict(),\n                    pipx_metadata_fh,\n                    indent=4,\n                    sort_keys=True,\n                    cls=JsonEncoderHandlesPath,\n                )\n        except OSError:\n            logger.warning(\n                pipx_wrap(\n                    f\"\"\"\n                    {hazard}  Unable to write {PIPX_INFO_FILENAME} to\n                    {self.venv_dir}.  This may cause future pipx operations\n                    involving {self.venv_dir.name} to fail or behave\n                    incorrectly.\n                    \"\"\",\n                    subsequent_indent=\" \" * 4,\n                )\n            )\n\n    def read(self, verbose: bool = False) -> None:\n        try:\n            with open(self.venv_dir / PIPX_INFO_FILENAME, \"rb\") as pipx_metadata_fh:\n                self.from_dict(json.load(pipx_metadata_fh, object_hook=_json_decoder_object_hook))\n        except OSError:  # Reset self if problem reading\n            if verbose:\n                logger.warning(\n                    pipx_wrap(\n                        f\"\"\"\n                        {hazard}  Unable to read {PIPX_INFO_FILENAME} in\n                        {self.venv_dir}.  This may cause this or future pipx\n                        operations involving {self.venv_dir.name} to fail or\n                        behave incorrectly.\n                        \"\"\",\n                        subsequent_indent=\" \" * 4,\n                    )\n                )\n            return\n"
  },
  {
    "path": "src/pipx/shared_libs.py",
    "content": "import datetime\nimport logging\nimport time\nfrom configparser import ConfigParser\nfrom contextlib import suppress\nfrom pathlib import Path\n\nfrom packaging.requirements import InvalidRequirement, Requirement\nfrom packaging.specifiers import SpecifierSet\n\nfrom pipx import paths\nfrom pipx.animate import animate\nfrom pipx.constants import WINDOWS\nfrom pipx.interpreter import DEFAULT_PYTHON\nfrom pipx.util import (\n    get_site_packages,\n    get_venv_paths,\n    run_subprocess,\n    subprocess_post_check,\n)\n\nlogger = logging.getLogger(__name__)\n\n\nSHARED_LIBS_MAX_AGE_SEC = datetime.timedelta(days=30).total_seconds()\n\n\ndef _venv_python_is_valid(python_path: Path) -> bool:\n    \"\"\"Check if a venv's Python is valid and its underlying interpreter exists.\n\n    On Windows, a venv's python.exe is a wrapper that uses pyvenv.cfg to find\n    the actual Python installation. If the original Python is uninstalled,\n    the wrapper exists but cannot execute. This function checks that the\n    underlying interpreter referenced in pyvenv.cfg still exists.\n    \"\"\"\n    if not WINDOWS:\n        return True\n\n    pyvenv_cfg = python_path.parent.parent / \"pyvenv.cfg\"\n    if not pyvenv_cfg.is_file():\n        return True\n\n    try:\n        config = ConfigParser()\n        with open(pyvenv_cfg, encoding=\"utf-8\") as f:\n            # ConfigParser needs a section header, pyvenv.cfg doesn't have one\n            config.read_string(\"[DEFAULT]\\n\" + f.read())\n        home = config.get(\"DEFAULT\", \"home\", fallback=None)\n        if home:\n            # The home path points to the directory containing the original python.exe\n            original_python = Path(home) / \"python.exe\"\n            if not original_python.is_file():\n                logger.info(f\"Shared libs venv references a missing Python interpreter: {original_python}\")\n                return False\n    except Exception:\n        # If we can't read pyvenv.cfg, assume the venv is valid\n        pass\n\n    return True\n\n\nclass _SharedLibs:\n    def __init__(self) -> None:\n        self._site_packages: dict[Path, Path] = {}\n        self.has_been_updated_this_run = False\n        self.has_been_logged_this_run = False\n\n    @property\n    def root(self) -> Path:\n        return paths.ctx.shared_libs\n\n    @property\n    def bin_path(self) -> Path:\n        bin_path, _, _ = get_venv_paths(self.root)\n        return bin_path\n\n    @property\n    def python_path(self) -> Path:\n        _, python_path, _ = get_venv_paths(self.root)\n        return python_path\n\n    @property\n    def man_path(self) -> Path:\n        _, _, man_path = get_venv_paths(self.root)\n        return man_path\n\n    @property\n    def pip_path(self) -> Path:\n        return self.bin_path / (\"pip\" if not WINDOWS else \"pip.exe\")\n\n    @property\n    def site_packages(self) -> Path:\n        if self.python_path not in self._site_packages:\n            self._site_packages[self.python_path] = get_site_packages(self.python_path)\n\n        return self._site_packages[self.python_path]\n\n    def create(self, pip_args: list[str], verbose: bool = False) -> None:\n        if not self.is_valid:\n            with animate(\"creating shared libraries\", not verbose):\n                create_process = run_subprocess(\n                    [DEFAULT_PYTHON, \"-m\", \"venv\", \"--clear\", self.root], run_dir=str(self.root)\n                )\n            subprocess_post_check(create_process)\n\n            # ignore installed packages to ensure no unexpected patches from the OS vendor\n            # are used\n            pip_args = pip_args or []\n            pip_args.append(\"--force-reinstall\")\n            self.upgrade(pip_args=pip_args, verbose=verbose, raises=True)\n\n    @property\n    def is_valid(self) -> bool:\n        if self.python_path.is_file():\n            # On Windows, check that the venv's underlying Python still exists\n            if not _venv_python_is_valid(self.python_path):\n                return False\n\n            check_pip = \"import importlib.util; print(importlib.util.find_spec('pip'))\"\n            out = run_subprocess(\n                [self.python_path, \"-c\", check_pip],\n                capture_stderr=False,\n                log_cmd_str=\"<checking pip's availability>\",\n            ).stdout.strip()\n\n            return self.pip_path.is_file() and out != \"None\"\n        else:\n            return False\n\n    @property\n    def needs_upgrade(self) -> bool:\n        if self.has_been_updated_this_run:\n            return False\n\n        if not self.pip_path.is_file():\n            return True\n\n        now = time.time()\n        time_since_last_update_sec = now - self.pip_path.stat().st_mtime\n        if not self.has_been_logged_this_run:\n            logger.info(\n                f\"Time since last upgrade of shared libs, in seconds: {time_since_last_update_sec:.0f}. \"\n                f\"Upgrade will be run by pipx if greater than {SHARED_LIBS_MAX_AGE_SEC:.0f}.\"\n            )\n            self.has_been_logged_this_run = True\n        return time_since_last_update_sec > SHARED_LIBS_MAX_AGE_SEC\n\n    def upgrade(self, *, pip_args: list[str], verbose: bool = False, raises: bool = False) -> None:\n        if not self.is_valid:\n            self.create(verbose=verbose, pip_args=pip_args)\n            return\n\n        # Don't try to upgrade multiple times per run\n        if self.has_been_updated_this_run:\n            logger.info(f\"Already upgraded libraries in {self.root}\")\n            return\n\n        if pip_args is None:\n            pip_args = []  # type: ignore[unreachable]\n\n        logger.info(f\"Upgrading shared libraries in {self.root}\")\n\n        ignored_args = [\"--editable\"]\n        _pip_args = [arg for arg in pip_args if arg not in ignored_args]\n        if not verbose:\n            _pip_args.append(\"-q\")\n\n        user_pip_req = None\n        for arg in _pip_args:\n            with suppress(InvalidRequirement):\n                if (req := Requirement(arg)).name == \"pip\":\n                    user_pip_req = req\n                    break\n\n        add_default = not user_pip_req or not (user_pip_req.specifier & SpecifierSet(\">=23.1\"))\n        install_args = [*_pip_args, \"pip >= 23.1\"] if add_default else _pip_args\n\n        try:\n            with animate(\"upgrading shared libraries\", not verbose):\n                upgrade_process = run_subprocess(\n                    [\n                        self.python_path,\n                        \"-m\",\n                        \"pip\",\n                        \"--no-input\",\n                        \"--disable-pip-version-check\",\n                        \"install\",\n                        \"--upgrade\",\n                        *install_args,\n                    ]\n                )\n            subprocess_post_check(upgrade_process)\n\n            self.has_been_updated_this_run = True\n            self.pip_path.touch()\n\n        except Exception:\n            logger.error(\"Failed to upgrade shared libraries\", exc_info=not raises)\n            if raises:\n                raise\n\n\nshared_libs = _SharedLibs()\n"
  },
  {
    "path": "src/pipx/standalone_python.py",
    "content": "import datetime\nimport hashlib\nimport json\nimport logging\nimport platform\nimport re\nimport shutil\nimport tarfile\nimport tempfile\nimport urllib.error\nfrom functools import partial\nfrom pathlib import Path\nfrom typing import Any\nfrom urllib.request import urlopen\n\nfrom pipx import constants, paths\nfrom pipx.animate import animate\nfrom pipx.util import PipxError\n\nlogger = logging.getLogger(__name__)\n\n# Much of the code in this module is adapted with extreme gratitude from\n# https://github.com/tusharsadhwani/yen/blob/main/src/yen/github.py\n\nMACHINE_SUFFIX: dict[str, dict[str, Any]] = {\n    \"Darwin\": {\n        \"arm64\": [\"aarch64-apple-darwin-install_only.tar.gz\"],\n        \"x86_64\": [\"x86_64-apple-darwin-install_only.tar.gz\"],\n    },\n    \"Linux\": {\n        \"aarch64\": {\n            \"glibc\": [\"aarch64-unknown-linux-gnu-install_only.tar.gz\"],\n            # musl doesn't exist\n        },\n        \"x86_64\": {\n            \"glibc\": [\n                \"x86_64_v3-unknown-linux-gnu-install_only.tar.gz\",\n                \"x86_64-unknown-linux-gnu-install_only.tar.gz\",\n            ],\n            \"musl\": [\"x86_64_v3-unknown-linux-musl-install_only.tar.gz\"],\n        },\n    },\n    \"Windows\": {\"AMD64\": [\"x86_64-pc-windows-msvc-install_only.tar.gz\"]},\n}\n\nGITHUB_API_URL = \"https://api.github.com/repos/astral-sh/python-build-standalone/releases/latest\"\nPYTHON_VERSION_REGEX = re.compile(r\"cpython-(\\d+\\.\\d+\\.\\d+)\")\n\n\ndef download_python_build_standalone(python_version: str, override: bool = False):\n    \"\"\"When all other python executable resolutions have failed,\n    attempt to download and use an appropriate python build\n    from https://github.com/astral-sh/python-build-standalone\n    and unpack it into the pipx shared directory.\"\"\"\n\n    # python_version can be a bare version number like \"3.9\" or a \"binary name\" like python3.10\n    # we'll convert it to a bare version number\n    python_version = re.sub(r\"[c]?python\", \"\", python_version)\n\n    install_dir = paths.ctx.standalone_python_cachedir / python_version\n    installed_python = install_dir / \"python.exe\" if constants.WINDOWS else install_dir / \"bin\" / \"python3\"\n\n    if override:\n        if install_dir.exists():\n            shutil.rmtree(install_dir)\n    else:\n        if installed_python.exists():\n            return str(installed_python)\n\n        if install_dir.exists():\n            logger.warning(f\"A previous attempt to install python {python_version} failed. Retrying.\")\n            shutil.rmtree(install_dir)\n\n    full_version, (download_link, digest) = resolve_python_version(python_version)\n\n    with tempfile.TemporaryDirectory() as tempdir:\n        archive = Path(tempdir) / f\"python-{full_version}.tar.gz\"\n        download_dir = Path(tempdir) / \"download\"\n\n        # download the python build gz\n        _download(full_version, download_link, archive)\n\n        # unpack the python build\n        _unpack(full_version, download_link, archive, download_dir, digest)\n\n        # the python installation we want is nested in the tarball\n        # under a directory named 'python'. We move it to the install\n        # directory\n        extracted_dir = download_dir / \"python\"\n        shutil.move(extracted_dir, install_dir)\n\n    return str(installed_python)\n\n\ndef _download(full_version: str, download_link: str, archive: Path):\n    with animate(f\"Downloading python {full_version} build\", True):\n        try:\n            # python standalone builds are typically ~32MB in size. to avoid\n            # ballooning memory usage, we read the file in chunks\n            with urlopen(download_link) as response, open(archive, \"wb\") as file_handle:\n                for data in iter(partial(response.read, 32768), b\"\"):\n                    file_handle.write(data)\n        except urllib.error.URLError as e:\n            raise PipxError(f\"Unable to download python {full_version} build.\") from e\n\n\ndef _unpack(full_version, download_link, archive: Path, download_dir: Path, expected_checksum: str):\n    with animate(f\"Unpacking python {full_version} build\", True):\n        # Calculate checksum efficiently\n        sha256_hash = hashlib.sha256()\n        with open(archive, \"rb\") as python_zip:\n            # Read in chunks to avoid loading the whole file into memory\n            for chunk in iter(lambda: python_zip.read(32768), b\"\"):\n                sha256_hash.update(chunk)\n\n        checksum = \"sha256:\" + sha256_hash.hexdigest()\n\n        # Validate checksum\n        if checksum != expected_checksum:\n            raise PipxError(\n                f\"Checksum mismatch for python {full_version} build. Expected {expected_checksum}, got {checksum}.\"\n            )\n\n        with tarfile.open(archive, mode=\"r:gz\") as tar:\n            tar.extractall(download_dir)\n\n\ndef get_or_update_index(use_cache: bool = True):\n    \"\"\"Get or update the index of available python builds from\n    the python-build-standalone repository.\"\"\"\n    index_file = paths.ctx.standalone_python_cachedir / \"index.json\"\n    if use_cache and index_file.exists():\n        index = json.loads(index_file.read_text())\n        # update index after 30 days\n        fetched = datetime.datetime.fromtimestamp(index[\"fetched\"])\n        if datetime.datetime.now() - fetched > datetime.timedelta(days=30):\n            index = {}\n    else:\n        index = {}\n    if not index:\n        releases = get_latest_python_releases()\n        index = {\"fetched\": datetime.datetime.now().timestamp(), \"releases\": releases}\n        # update index\n        index_file.write_text(json.dumps(index))\n    return index\n\n\ndef get_latest_python_releases() -> list[tuple[str, str]]:\n    \"\"\"Returns the list of python download links from the latest github release.\"\"\"\n    try:\n        with urlopen(GITHUB_API_URL) as response:\n            release_data = json.load(response)\n\n    except urllib.error.URLError as e:\n        # raise\n        raise PipxError(f\"Unable to fetch python-build-standalone release data (from {GITHUB_API_URL}).\") from e\n\n    return [(asset[\"browser_download_url\"], asset[\"digest\"]) for asset in release_data[\"assets\"]]\n\n\ndef list_pythons(use_cache: bool = True) -> dict[str, tuple[str, str]]:\n    \"\"\"Returns available python versions for your machine and their download links.\"\"\"\n    system, machine = platform.system(), platform.machine()\n    download_link_suffixes = MACHINE_SUFFIX[system][machine]\n    # linux suffixes are nested under glibc or musl builds\n    if system == \"Linux\":\n        # fallback to musl if libc version is not found\n        libc_version = platform.libc_ver()[0] or \"musl\"\n        download_link_suffixes = download_link_suffixes[libc_version]\n\n    python_releases = get_or_update_index(use_cache)[\"releases\"]\n\n    available_python_links = [\n        (link, digest)\n        # Suffixes are in order of preference.\n        for download_link_suffix in download_link_suffixes\n        for link, digest in python_releases\n        if link.endswith(download_link_suffix)\n    ]\n\n    python_versions: dict[str, tuple[str, str]] = {}\n    for link, digest in available_python_links:\n        match = PYTHON_VERSION_REGEX.search(link)\n        assert match is not None\n        python_version = match[1]\n        # Don't override already found versions, they are in order of preference\n        if python_version in python_versions:\n            continue\n\n        python_versions[python_version] = link, digest\n\n    return {\n        version: python_versions[version]\n        for version in sorted(\n            python_versions,\n            # sort by semver\n            key=lambda version: [int(k) for k in version.split(\".\")],\n            reverse=True,\n        )\n    }\n\n\ndef resolve_python_version(requested_version: str):\n    pythons = list_pythons()\n    requested_release = requested_version.split(\".\")\n\n    for full_version, download_link in pythons.items():\n        standalone_release = full_version.split(\".\")\n        if requested_release == standalone_release[: len(requested_release)]:\n            return full_version, download_link\n\n    raise PipxError(f\"Unable to acquire a standalone python build matching {requested_version}.\")\n"
  },
  {
    "path": "src/pipx/util.py",
    "content": "import logging\nimport os\nimport random\nimport re\nimport shutil\nimport string\nimport subprocess\nimport sys\nimport textwrap\nfrom collections.abc import Sequence\nfrom dataclasses import dataclass\nfrom pathlib import Path\nfrom re import Pattern\nfrom typing import (\n    Any,\n    NoReturn,\n    Optional,\n    Union,\n)\n\nfrom pipx import paths\nfrom pipx.animate import show_cursor\nfrom pipx.constants import MINGW, WINDOWS\n\nlogger = logging.getLogger(__name__)\n\n\nclass PipxError(Exception):\n    def __init__(self, message: str, wrap_message: bool = True):\n        if wrap_message:\n            super().__init__(pipx_wrap(message))\n        else:\n            super().__init__(message)\n\n\n@dataclass(frozen=True)\nclass RelevantSearch:\n    pattern: Pattern[str]\n    category: str\n\n\ndef _get_trash_file(path: Path) -> Path:\n    if not paths.ctx.trash.is_dir():\n        paths.ctx.trash.mkdir()\n    prefix = \"\".join(random.choices(string.ascii_lowercase, k=8))\n    return paths.ctx.trash / f\"{prefix}.{path.name}\"\n\n\ndef rmdir(path: Path, safe_rm: bool = True) -> None:\n    if not path.is_dir():\n        return\n\n    logger.info(f\"removing directory {path}\")\n    # Windows doesn't let us delete or overwrite files that are being run\n    # But it does let us rename/move it. To get around this issue, we can move\n    # the file to a temporary folder (to be deleted at a later time)\n    # So, if safe_rm is True, we ignore any errors and move the file to the trash with below code\n    shutil.rmtree(path, ignore_errors=safe_rm)\n\n    # move it to be deleted later if it still exists\n    if path.is_dir():\n        if safe_rm:\n            logger.warning(f\"Failed to delete {path}. Will move it to a temp folder to delete later.\")\n\n            path.rename(_get_trash_file(path))\n        else:\n            logger.warning(f\"Failed to delete {path}. You may need to delete it manually.\")\n\n\ndef mkdir(path: Path) -> None:\n    if path.is_dir():\n        return\n    logger.info(f\"creating directory {path}\")\n    path.mkdir(parents=True, exist_ok=True)\n\n\ndef safe_unlink(file: Path) -> None:\n    # Windows doesn't let us delete or overwrite files that are being run\n    # But it does let us rename/move it. To get around this issue, we can move\n    # the file to a temporary folder (to be deleted at a later time)\n\n    if not file.is_file():\n        return\n    try:\n        file.unlink()\n    except PermissionError:\n        file.rename(_get_trash_file(file))\n\n\ndef get_pypackage_bin_path(binary_name: str) -> Path:\n    return (\n        Path(\"__pypackages__\")\n        / (str(sys.version_info.major) + \".\" + str(sys.version_info.minor))\n        / \"lib\"\n        / \"bin\"\n        / binary_name\n    )\n\n\ndef run_pypackage_bin(bin_path: Path, args: list[str]) -> NoReturn:\n    exec_app(\n        [str(bin_path.resolve())] + args,\n        extra_python_paths=[\".\", str(bin_path.parent.parent)],\n    )\n\n\nif WINDOWS:\n\n    def get_venv_paths(root: Path) -> tuple[Path, Path, Path]:\n        # Make sure to use the real root path. This matters especially on Windows when using the packaged app\n        # (Microsoft Store) version of Python, which uses path redirection for sandboxing.\n        # See https://github.com/pypa/pipx/issues/1164\n        root = root.resolve()\n        bin_path = root / \"Scripts\" if not MINGW else root / \"bin\"\n        python_path = bin_path / \"python.exe\"\n        man_path = root / \"share\" / \"man\"\n        return bin_path, python_path, man_path\n\nelse:\n\n    def get_venv_paths(root: Path) -> tuple[Path, Path, Path]:\n        bin_path = root / \"bin\"\n        python_path = bin_path / \"python\"\n        man_path = root / \"share\" / \"man\"\n        return bin_path, python_path, man_path\n\n\ndef get_site_packages(python: Path) -> Path:\n    output = run_subprocess(\n        [python, \"-c\", \"import sysconfig; print(sysconfig.get_path('purelib'))\"],\n        capture_stderr=False,\n    ).stdout\n    path = Path(output.strip())\n    path.mkdir(parents=True, exist_ok=True)\n    return path\n\n\ndef _fix_subprocess_env(env: dict[str, str]) -> dict[str, str]:\n    # Remove PYTHONPATH because some platforms (macOS with Homebrew) add pipx\n    #   directories to it, and can make it appear to venvs as though pipx\n    #   dependencies are in the venv path (#233)\n    # Remove __PYVENV_LAUNCHER__ because it can cause the wrong python binary\n    #   to be used (#334)\n    env_blocklist = [\"PYTHONPATH\", \"__PYVENV_LAUNCHER__\"]\n    for env_to_remove in env_blocklist:\n        env.pop(env_to_remove, None)\n\n    env[\"PIP_DISABLE_PIP_VERSION_CHECK\"] = \"1\"\n    # Make sure that Python writes output in UTF-8\n    env[\"PYTHONIOENCODING\"] = \"utf-8\"\n    env[\"PYTHONLEGACYWINDOWSSTDIO\"] = \"utf-8\"\n    # Make sure we install package to venv, not userbase dir\n    env[\"PIP_USER\"] = \"0\"\n    return env\n\n\ndef run_subprocess(\n    cmd: Sequence[Union[str, Path]],\n    capture_stdout: bool = True,\n    capture_stderr: bool = True,\n    log_cmd_str: Optional[str] = None,\n    log_stdout: bool = True,\n    log_stderr: bool = True,\n    run_dir: Optional[str] = None,\n) -> \"subprocess.CompletedProcess[str]\":\n    \"\"\"Run arbitrary command as subprocess, capturing stderr and stout\"\"\"\n    env = dict(os.environ)\n    env = _fix_subprocess_env(env)\n\n    if log_cmd_str is None:\n        log_cmd_str = \" \".join(str(c) for c in cmd)\n    logger.info(f\"running {log_cmd_str}\")\n    if run_dir:\n        os.makedirs(run_dir, exist_ok=True)\n    # windows cannot take Path objects, only strings\n    cmd_str_list = [str(c) for c in cmd]\n\n    # TODO: Switch to using `-P` / PYTHONSAFEPATH instead of running in\n    # separate directory in Python 3.11\n    completed_process = subprocess.run(\n        cmd_str_list,\n        env=env,\n        stdout=subprocess.PIPE if capture_stdout else None,\n        stderr=subprocess.PIPE if capture_stderr else None,\n        encoding=\"utf-8\",\n        text=True,\n        check=False,\n        cwd=run_dir,\n    )\n\n    if capture_stdout and log_stdout:\n        logger.debug(f\"stdout: {completed_process.stdout}\".rstrip())\n    if capture_stderr and log_stderr:\n        logger.debug(f\"stderr: {completed_process.stderr}\".rstrip())\n    logger.debug(f\"returncode: {completed_process.returncode}\")\n\n    return completed_process\n\n\ndef subprocess_post_check(completed_process: \"subprocess.CompletedProcess[str]\", raise_error: bool = True) -> None:\n    if completed_process.returncode:\n        if completed_process.stdout is not None:\n            print(completed_process.stdout, file=sys.stdout, end=\"\")\n        if completed_process.stderr is not None:\n            print(completed_process.stderr, file=sys.stderr, end=\"\")\n        if raise_error:\n            raise PipxError(f\"{' '.join([str(x) for x in completed_process.args])!r} failed\")\n        else:\n            logger.info(f\"{' '.join(completed_process.args)!r} failed\")\n\n\ndef dedup_ordered(input_list: list[tuple[str, Any]]) -> list[tuple[str, Any]]:\n    output_list = []\n    seen = set()\n    for x in input_list:\n        if x[0] not in seen:\n            output_list.append(x)\n            seen.add(x[0])\n\n    return output_list\n\n\ndef analyze_pip_output(pip_stdout: str, pip_stderr: str) -> None:\n    r\"\"\"Extract useful errors from pip output of failed install\n\n    Print the module that failed to build\n    Print some of the most relevant errors from the pip output\n\n    Example pip stderr line for each \"relevant\" type:\n        not_found\n            Package cairo was not found in the pkg-config search path.\n            src/common.h:34:10: fatal error: 'stdio.h' file not found\n            The headers or library files could not be found for zlib,\n        no_such\n            unable to execute 'gcc': No such file or directory\n            build\\test1.c(2): fatal error C1083: Cannot open include file: 'cpuid.h': No such file ...\n        exception_error\n            Exception: Unable to find OpenSSL >= 1.0 headers. (Looked here: ...\n        fatal_error\n            LINK : fatal error LNK1104: cannot open file 'kernel32.lib'\n        conflict_\n            ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/...\n        error_\n            error: can't copy 'lib\\ansible\\module_utils\\ansible_release.py': doesn't exist ...\n            build\\test1.c(4): error C2146: syntax error: missing ';' before identifier 'x'\n    \"\"\"\n    max_relevant_errors = 10\n\n    failed_build_stdout: list[str] = []\n    last_collecting_dep: Optional[str] = None\n    # for any useful information in stdout, `pip install` must be run without\n    #   the -q option\n    for line in pip_stdout.split(\"\\n\"):\n        failed_match = re.search(r\"Failed to build\\s+(\\S.+)$\", line)\n        collecting_match = re.search(r\"^\\s*Collecting\\s+(\\S+)\", line)\n        if failed_match:\n            failed_build_stdout = failed_match.group(1).strip().split()\n        if collecting_match:\n            last_collecting_dep = collecting_match.group(1)\n\n    # In order of most useful to least useful\n    relevant_searches = [\n        RelevantSearch(re.compile(r\"not (?:be )?found\", re.I), \"not_found\"),\n        RelevantSearch(re.compile(r\"no such\", re.I), \"no_such\"),\n        RelevantSearch(re.compile(r\"(Exception|Error):\\s*\\S+\"), \"exception_error\"),\n        RelevantSearch(re.compile(r\"fatal error\", re.I), \"fatal_error\"),\n        RelevantSearch(re.compile(r\"conflict\", re.I), \"conflict_\"),\n        RelevantSearch(\n            re.compile(\n                r\"error:\"\n                r\"(?!.+Command errored out)\"\n                r\"(?!.+failed building wheel for)\"\n                r\"(?!.+could not build wheels? for)\"\n                r\"(?!.+failed to build one or more wheels)\"\n                r\".+[^:]$\",\n                re.I,\n            ),\n            \"error_\",\n        ),\n    ]\n\n    failed_stderr_patt = re.compile(r\"Failed to build\\s+(?!one or more packages)(\\S+)\")\n\n    relevants_saved = []\n    failed_build_stderr = set()\n    for line in pip_stderr.split(\"\\n\"):\n        failed_build_match = failed_stderr_patt.search(line)\n        if failed_build_match:\n            failed_build_stderr.add(failed_build_match.group(1))\n\n        for relevant_search in relevant_searches:\n            if relevant_search.pattern.search(line):\n                relevants_saved.append((line.strip(), relevant_search.category))\n                break\n\n    if failed_build_stdout:\n        failed_to_build_str = \"\\n    \".join(failed_build_stdout)\n        plural_str = \"s\" if len(failed_build_stdout) > 1 else \"\"\n        print(\"\", file=sys.stderr)\n        logger.error(f\"pip failed to build package{plural_str}:\\n    {failed_to_build_str}\")\n    elif failed_build_stderr:\n        failed_to_build_str = \"\\n    \".join(failed_build_stderr)\n        plural_str = \"s\" if len(failed_build_stderr) > 1 else \"\"\n        print(\"\", file=sys.stderr)\n        logger.error(f\"pip seemed to fail to build package{plural_str}:\\n    {failed_to_build_str}\")\n    elif last_collecting_dep is not None:\n        print(\"\", file=sys.stderr)\n        logger.error(f\"pip seemed to fail to build package:\\n    {last_collecting_dep}\")\n\n    relevants_saved = dedup_ordered(relevants_saved)\n\n    if relevants_saved:\n        print(\"\\nSome possibly relevant errors from pip install:\", file=sys.stderr)\n\n        print_categories = [x.category for x in relevant_searches]\n        relevants_saved_filtered = relevants_saved.copy()\n        while (len(print_categories) > 1) and (len(relevants_saved_filtered) > max_relevant_errors):\n            print_categories.pop(-1)\n            relevants_saved_filtered = [x for x in relevants_saved if x[1] in print_categories]\n\n        for relevant_saved in relevants_saved_filtered:\n            print(f\"    {relevant_saved[0]}\", file=sys.stderr)\n\n\ndef subprocess_post_check_handle_pip_error(\n    completed_process: \"subprocess.CompletedProcess[str]\",\n) -> None:\n    if completed_process.returncode:\n        logger.info(f\"{' '.join(completed_process.args)!r} failed\")\n        # Save STDOUT and STDERR to file in pipx/logs/\n        if paths.ctx.log_file is None:\n            raise PipxError(\"Pipx internal error: No log_file present.\")\n        pip_error_file = paths.ctx.log_file.parent / (paths.ctx.log_file.stem + \"_pip_errors.log\")\n        with pip_error_file.open(\"a\", encoding=\"utf-8\") as pip_error_fh:\n            print(\"PIP STDOUT\", file=pip_error_fh)\n            print(\"----------\", file=pip_error_fh)\n            if completed_process.stdout is not None:\n                print(completed_process.stdout, file=pip_error_fh, end=\"\")\n            print(\"\\nPIP STDERR\", file=pip_error_fh)\n            print(\"----------\", file=pip_error_fh)\n            if completed_process.stderr is not None:\n                print(completed_process.stderr, file=pip_error_fh, end=\"\")\n\n        logger.error(f\"Fatal error from pip prevented installation. Full pip output in file:\\n    {pip_error_file}\")\n\n        analyze_pip_output(completed_process.stdout, completed_process.stderr)\n\n\ndef exec_app(\n    cmd: Sequence[Union[str, Path]],\n    env: Optional[dict[str, str]] = None,\n    extra_python_paths: Optional[list[str]] = None,\n) -> NoReturn:\n    \"\"\"Run command, do not return\n\n    POSIX: replace current process with command using os.exec*()\n    Windows: Use subprocess and sys.exit() to run command\n    \"\"\"\n\n    if env is None:\n        env = dict(os.environ)\n    env = _fix_subprocess_env(env)\n\n    if extra_python_paths is not None:\n        env[\"PYTHONPATH\"] = os.path.pathsep.join(\n            extra_python_paths + (os.getenv(\"PYTHONPATH\", \"\").split(os.path.pathsep) if os.getenv(\"PYTHONPATH\") else [])\n        )\n\n    # make sure we show cursor again before handing over control\n    show_cursor()\n\n    logger.info(\"exec_app: \" + \" \".join([str(c) for c in cmd]))\n\n    if WINDOWS:\n        sys.exit(\n            subprocess.run(\n                cmd,\n                env=env,\n                stdout=None,\n                stderr=None,\n                encoding=\"utf-8\",\n                text=True,\n                check=False,\n            ).returncode\n        )\n    else:\n        os.execvpe(str(cmd[0]), [str(x) for x in cmd], env)\n\n\ndef full_package_description(package_name: str, package_spec: str) -> str:\n    if package_name == package_spec:\n        return package_name\n    else:\n        return f\"{package_name} from spec {package_spec!r}\"\n\n\ndef pipx_wrap(text: str, subsequent_indent: str = \"\", keep_newlines: bool = False) -> str:\n    \"\"\"Dedent, strip, wrap to shell width. Don't break on hyphens, only spaces\"\"\"\n    minimum_width = 40\n    width = max(shutil.get_terminal_size((80, 40)).columns, minimum_width) - 2\n\n    text = textwrap.dedent(text).strip()\n    if keep_newlines:\n        return \"\\n\".join(\n            [\n                textwrap.fill(\n                    line,\n                    width=width,\n                    subsequent_indent=subsequent_indent,\n                    break_on_hyphens=False,\n                )\n                for line in text.splitlines()\n            ]\n        )\n    else:\n        return textwrap.fill(\n            text,\n            width=width,\n            subsequent_indent=subsequent_indent,\n            break_on_hyphens=False,\n        )\n\n\ndef is_paths_relative(path: Path, parent: Path):\n    return path.is_relative_to(parent)\n"
  },
  {
    "path": "src/pipx/venv.py",
    "content": "import json\nimport logging\nimport re\nimport shutil\nimport time\nfrom collections.abc import Generator\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING, NoReturn, Optional\n\nif TYPE_CHECKING:\n    from subprocess import CompletedProcess\n\ntry:\n    from importlib.metadata import Distribution, EntryPoint\nexcept ImportError:\n    from importlib_metadata import Distribution, EntryPoint  # type: ignore[import-not-found,no-redef]\n\nfrom packaging.utils import canonicalize_name\n\nfrom pipx.animate import animate\nfrom pipx.constants import PIPX_SHARED_PTH, ExitCode\nfrom pipx.emojis import hazard\nfrom pipx.interpreter import DEFAULT_PYTHON\nfrom pipx.package_specifier import (\n    fix_package_name,\n    get_extras,\n    parse_specifier_for_install,\n    parse_specifier_for_metadata,\n)\nfrom pipx.pipx_metadata_file import PackageInfo, PipxMetadata\nfrom pipx.shared_libs import shared_libs\nfrom pipx.util import (\n    PipxError,\n    exec_app,\n    full_package_description,\n    get_site_packages,\n    get_venv_paths,\n    pipx_wrap,\n    rmdir,\n    run_subprocess,\n    subprocess_post_check,\n    subprocess_post_check_handle_pip_error,\n)\nfrom pipx.venv_inspect import VenvMetadata, inspect_venv\n\nlogger = logging.getLogger(__name__)\n\n_entry_point_value_pattern = re.compile(\n    r\"\"\"\n    ^(?P<module>[\\w.]+)\\s*\n    (:\\s*(?P<attr>[\\w.]+))?\\s*\n    (?P<extras>\\[.*\\])?\\s*$\n    \"\"\",\n    re.VERBOSE,\n)\n\n\nclass VenvContainer:\n    \"\"\"A collection of venvs managed by pipx.\"\"\"\n\n    def __init__(self, root: Path):\n        self._root = root\n\n    def __repr__(self) -> str:\n        return f\"VenvContainer({str(self._root)!r})\"\n\n    def __str__(self) -> str:\n        return str(self._root)\n\n    def iter_venv_dirs(self) -> Generator[Path, None, None]:\n        \"\"\"Iterate venv directories in this container.\"\"\"\n        if not self._root.is_dir():\n            return\n        for entry in self._root.iterdir():\n            if not entry.is_dir():\n                continue\n            yield entry\n\n    def get_venv_dir(self, package_name: str) -> Path:\n        \"\"\"Return the expected venv path for given `package_name`.\"\"\"\n        return self._root.joinpath(canonicalize_name(package_name))\n\n\nclass Venv:\n    \"\"\"Abstraction for a virtual environment with various useful methods for pipx\"\"\"\n\n    def __init__(self, path: Path, *, verbose: bool = False, python: str = DEFAULT_PYTHON) -> None:\n        self.root = path\n        self.python = python\n        self.bin_path, self.python_path, self.man_path = get_venv_paths(self.root)\n        self.pipx_metadata = PipxMetadata(venv_dir=path)\n        self.verbose = verbose\n        self.do_animation = not verbose\n        try:\n            self._existing = self.root.exists() and bool(next(self.root.iterdir()))\n        except StopIteration:\n            self._existing = False\n\n    def check_upgrade_shared_libs(self, verbose: bool, pip_args: list[str], force_upgrade: bool = False):\n        \"\"\"\n        If necessary, run maintenance tasks to keep the shared libs up-to-date.\n\n        This can trigger `pip install`/`pip install --upgrade` operations,\n        so we expect the caller to provide sensible `pip_args`\n        ( provided by the user in the current CLI call\n        or retrieved from the metadata of a previous installation)\n        \"\"\"\n        if self._existing and self.uses_shared_libs:\n            if shared_libs.is_valid:\n                if force_upgrade or shared_libs.needs_upgrade:\n                    shared_libs.upgrade(verbose=verbose, pip_args=pip_args)\n            else:\n                shared_libs.create(verbose=verbose, pip_args=pip_args)\n\n            if not shared_libs.is_valid:\n                raise PipxError(\n                    pipx_wrap(\n                        f\"\"\"\n                        Error: pipx's shared venv {shared_libs.root} is invalid\n                        and needs re-installation. To fix this, install or\n                        reinstall a package. For example:\n                        \"\"\"\n                    )\n                    + f\"\\n  pipx install {self.root.name} --force\",\n                    wrap_message=False,\n                )\n\n    @property\n    def name(self) -> str:\n        if self.pipx_metadata.main_package.package is not None:\n            venv_name = f\"{self.pipx_metadata.main_package.package}{self.pipx_metadata.main_package.suffix}\"\n        else:\n            venv_name = self.root.name\n        return venv_name\n\n    @property\n    def uses_shared_libs(self) -> bool:\n        if self._existing:\n            pth_files = self.root.glob(\"**/\" + PIPX_SHARED_PTH)\n            return next(pth_files, None) is not None\n        else:\n            # always use shared libs when creating a new venv\n            return True\n\n    @property\n    def package_metadata(self) -> dict[str, PackageInfo]:\n        return_dict = self.pipx_metadata.injected_packages.copy()\n        if self.pipx_metadata.main_package.package is not None:\n            return_dict[self.pipx_metadata.main_package.package] = self.pipx_metadata.main_package\n        return return_dict\n\n    @property\n    def main_package_name(self) -> str:\n        if self.pipx_metadata.main_package.package is None:\n            # This is OK, because if no metadata, we are pipx < v0.15.0.0 and\n            #   venv_name==main_package_name\n            return self.root.name\n        else:\n            return self.pipx_metadata.main_package.package\n\n    def create_venv(self, venv_args: list[str], pip_args: list[str], override_shared: bool = False) -> None:\n        \"\"\"\n        override_shared -- Override installing shared libraries to the pipx shared directory (default False)\n        \"\"\"\n        logger.info(\"Creating virtual environment\")\n        with animate(\"creating virtual environment\", self.do_animation):\n            cmd = [self.python, \"-m\", \"venv\"]\n            if not override_shared:\n                cmd.append(\"--without-pip\")\n            venv_process = run_subprocess(cmd + venv_args + [str(self.root)], run_dir=str(self.root))\n        subprocess_post_check(venv_process)\n\n        shared_libs.create(verbose=self.verbose, pip_args=pip_args)\n        if not override_shared:\n            pipx_pth = get_site_packages(self.python_path) / PIPX_SHARED_PTH\n            # write path pointing to the shared libs site-packages directory\n            # example pipx_pth location:\n            #   ~/.local/share/pipx/venvs/black/lib/python3.8/site-packages/pipx_shared.pth\n            # example shared_libs.site_packages location:\n            #   ~/.local/share/pipx/shared/lib/python3.6/site-packages\n            #\n            # https://docs.python.org/3/library/site.html\n            # A path configuration file is a file whose name has the form 'name.pth'.\n            # its contents are additional items (one per line) to be added to sys.path\n            pipx_pth.write_text(f\"{shared_libs.site_packages}\\n\", encoding=\"utf-8\")\n\n        self.pipx_metadata.venv_args = venv_args\n        self.pipx_metadata.python_version = self.get_python_version()\n        source_interpreter = shutil.which(self.python)\n        if source_interpreter:\n            self.pipx_metadata.source_interpreter = Path(source_interpreter)\n\n    def safe_to_remove(self) -> bool:\n        return not self._existing\n\n    def remove_venv(self) -> None:\n        if self.safe_to_remove():\n            rmdir(self.root)\n        else:\n            logger.warning(\n                pipx_wrap(\n                    f\"\"\"\n                    {hazard}  Not removing existing venv {self.root} because it\n                    was not created in this session\n                    \"\"\",\n                    subsequent_indent=\" \" * 4,\n                )\n            )\n\n    def upgrade_packaging_libraries(self, pip_args: list[str]) -> None:\n        if self.uses_shared_libs:\n            shared_libs.upgrade(pip_args=pip_args, verbose=self.verbose)\n        else:\n            # TODO: setuptools and wheel? Original code didn't bother\n            # but shared libs code does.\n            self.upgrade_package_no_metadata(\"pip\", pip_args)\n\n    def uninstall_package(self, package: str, was_injected: bool = False):\n        try:\n            logger.info(\"Uninstalling %s\", package)\n            with animate(f\"uninstalling {package}\", self.do_animation):\n                cmd = [\"uninstall\", \"-y\"] + [package]\n                self._run_pip(cmd)\n        except PipxError as e:\n            logger.info(e)\n            raise PipxError(f\"Error uninstalling {package}.\") from None\n\n        if was_injected:\n            self.pipx_metadata.injected_packages.pop(package)\n            self.pipx_metadata.write()\n\n    def install_package(\n        self,\n        package_name: str,\n        package_or_url: str,\n        pip_args: list[str],\n        include_dependencies: bool,\n        include_apps: bool,\n        is_main_package: bool,\n        suffix: str = \"\",\n    ) -> None:\n        # package_name in package specifier can mismatch URL due to user error\n        package_or_url = fix_package_name(package_or_url, package_name)\n\n        # check syntax and clean up spec and pip_args\n        (package_or_url, pip_args) = parse_specifier_for_install(package_or_url, pip_args)\n\n        logger.info(\"Installing %s\", package_descr := full_package_description(package_name, package_or_url))\n        with animate(f\"installing {package_descr}\", self.do_animation):\n            # do not use -q with `pip install` so subprocess_post_check_pip_errors\n            #   has more information to analyze in case of failure.\n            cmd = [\n                str(self.python_path),\n                \"-m\",\n                \"pip\",\n                \"--no-input\",\n                \"install\",\n                *pip_args,\n                package_or_url,\n            ]\n            # no logging because any errors will be specially logged by\n            #   subprocess_post_check_handle_pip_error()\n            pip_process = run_subprocess(cmd, log_stdout=False, log_stderr=False, run_dir=str(self.root))\n        subprocess_post_check_handle_pip_error(pip_process)\n        if pip_process.returncode:\n            raise PipxError(f\"Error installing {full_package_description(package_name, package_or_url)}.\")\n\n        self.update_package_metadata(\n            package_name=package_name,\n            package_or_url=package_or_url,\n            pip_args=pip_args,\n            include_dependencies=include_dependencies,\n            include_apps=include_apps,\n            is_main_package=is_main_package,\n            suffix=suffix,\n        )\n\n        # Verify package installed ok\n        if self.package_metadata[package_name].package_version is None:\n            raise PipxError(\n                f\"Unable to install \"\n                f\"{full_package_description(package_name, package_or_url)}.\\n\"\n                f\"Check the name or spec for errors, and verify that it can \"\n                f\"be installed with pip.\",\n                wrap_message=False,\n            )\n\n    def install_unmanaged_packages(self, requirements: list[str], pip_args: list[str]) -> None:\n        \"\"\"Install packages in the venv, but do not record them in the metadata.\"\"\"\n\n        # Note: We want to install everything at once, as that lets\n        # pip resolve conflicts correctly.\n        logger.info(\"Installing %s\", package_descr := \", \".join(requirements))\n        with animate(f\"installing {package_descr}\", self.do_animation):\n            # do not use -q with `pip install` so subprocess_post_check_pip_errors\n            #   has more information to analyze in case of failure.\n            cmd = [\n                str(self.python_path),\n                \"-m\",\n                \"pip\",\n                \"--no-input\",\n                \"install\",\n                *pip_args,\n                *requirements,\n            ]\n            # no logging because any errors will be specially logged by\n            #   subprocess_post_check_handle_pip_error()\n            pip_process = run_subprocess(cmd, log_stdout=False, log_stderr=False, run_dir=str(self.root))\n        subprocess_post_check_handle_pip_error(pip_process)\n        if pip_process.returncode:\n            raise PipxError(f\"Error installing {', '.join(requirements)}.\")\n\n    def install_package_no_deps(self, package_or_url: str, pip_args: list[str]) -> str:\n        with animate(f\"determining package name from {package_or_url!r}\", self.do_animation):\n            old_package_set = self.list_installed_packages()\n            cmd = [\n                \"--no-input\",\n                \"install\",\n                \"--no-dependencies\",\n                *pip_args,\n                package_or_url,\n            ]\n            pip_process = self._run_pip(cmd)\n        subprocess_post_check(pip_process, raise_error=False)\n        if pip_process.returncode:\n            raise PipxError(\n                f\"\"\"\n                Cannot determine package name from spec {package_or_url!r}.\n                Check package spec for errors.\n                \"\"\"\n            )\n\n        installed_packages = self.list_installed_packages() - old_package_set\n        if len(installed_packages) == 1:\n            package_name = installed_packages.pop()\n            logger.info(f\"Determined package name: {package_name}\")\n        else:\n            logger.info(f\"old_package_set = {old_package_set}\")\n            logger.info(f\"install_packages = {installed_packages}\")\n            raise PipxError(\n                f\"\"\"\n                Cannot determine package name from spec {package_or_url!r}.\n                Check package spec for errors.\n                \"\"\"\n            )\n\n        return package_name\n\n    def get_venv_metadata_for_package(self, package_name: str, package_extras: set[str]) -> VenvMetadata:\n        data_start = time.time()\n        venv_metadata = inspect_venv(package_name, package_extras, self.bin_path, self.python_path, self.man_path)\n        logger.info(f\"get_venv_metadata_for_package: {1e3 * (time.time() - data_start):.0f}ms\")\n        return venv_metadata\n\n    def update_package_metadata(\n        self,\n        package_name: str,\n        package_or_url: str,\n        pip_args: list[str],\n        include_dependencies: bool,\n        include_apps: bool,\n        is_main_package: bool,\n        suffix: str = \"\",\n        pinned: bool = False,\n    ) -> None:\n        venv_package_metadata = self.get_venv_metadata_for_package(package_name, get_extras(package_or_url))\n        package_info = PackageInfo(\n            package=package_name,\n            package_or_url=parse_specifier_for_metadata(package_or_url),\n            pip_args=pip_args,\n            include_apps=include_apps,\n            include_dependencies=include_dependencies,\n            apps=venv_package_metadata.apps,\n            app_paths=venv_package_metadata.app_paths,\n            apps_of_dependencies=venv_package_metadata.apps_of_dependencies,\n            app_paths_of_dependencies=venv_package_metadata.app_paths_of_dependencies,\n            man_pages=venv_package_metadata.man_pages,\n            man_paths=venv_package_metadata.man_paths,\n            man_pages_of_dependencies=venv_package_metadata.man_pages_of_dependencies,\n            man_paths_of_dependencies=venv_package_metadata.man_paths_of_dependencies,\n            package_version=venv_package_metadata.package_version,\n            suffix=suffix,\n            pinned=pinned,\n        )\n        if is_main_package:\n            self.pipx_metadata.main_package = package_info\n        else:\n            self.pipx_metadata.injected_packages[package_name] = package_info\n\n        self.pipx_metadata.write()\n\n    def get_python_version(self) -> str:\n        return run_subprocess([str(self.python_path), \"--version\"]).stdout.strip()\n\n    def list_installed_packages(self, not_required=False) -> set[str]:\n        cmd_run = run_subprocess(\n            [str(self.python_path), \"-m\", \"pip\", \"list\", \"--format=json\"] + ([\"--not-required\"] if not_required else [])\n        )\n        pip_list = json.loads(cmd_run.stdout.strip())\n        return {x[\"name\"] for x in pip_list}\n\n    def _find_entry_point(self, app: str) -> Optional[EntryPoint]:\n        if not self.python_path.exists():\n            return None\n        dists = Distribution.discover(name=self.main_package_name, path=[str(get_site_packages(self.python_path))])\n        for dist in dists:\n            for ep in dist.entry_points:\n                if ep.group == \"pipx.run\":\n                    if ep.name == app:\n                        return ep\n                    # Try to infer app name from dist's metadata if given\n                    # local path\n                    if Path(app).exists() and dist.metadata[\"Name\"] == ep.name:\n                        return ep\n        return None\n\n    def run_app(self, app: str, filename: str, app_args: list[str]) -> NoReturn:\n        entry_point = self._find_entry_point(app)\n\n        # No [pipx.run] entry point; default to run console script.\n        if entry_point is None:\n            exec_app([str(self.bin_path / filename)] + app_args)\n\n        # Evaluate and execute the entry point.\n        # TODO: After dropping support for Python < 3.9, use\n        # \"entry_point.module\" and \"entry_point.attr\" instead.\n        match = _entry_point_value_pattern.match(entry_point.value)\n        assert match is not None, \"invalid entry point\"\n        logger.info(\"Using discovered entry point for 'pipx run'\")\n        module, attr = match.group(\"module\", \"attr\")\n        code = f\"import sys, {module}\\nsys.argv[0] = {entry_point.name!r}\\nsys.exit({module}.{attr}())\\n\"\n        exec_app([str(self.python_path), \"-c\", code] + app_args)\n\n    def has_app(self, app: str, filename: str) -> bool:\n        if self._find_entry_point(app) is not None:\n            return True\n        return (self.bin_path / filename).is_file()\n\n    def has_package(self, package_name: str) -> bool:\n        return bool(list(Distribution.discover(name=package_name, path=[str(get_site_packages(self.python_path))])))\n\n    def upgrade_package_no_metadata(self, package_name: str, pip_args: list[str]) -> None:\n        logger.info(\"Upgrading %s\", package_descr := full_package_description(package_name, package_name))\n        with animate(f\"upgrading {package_descr}\", self.do_animation):\n            pip_process = self._run_pip([\"--no-input\", \"install\", \"--upgrade\"] + pip_args + [package_name])\n        subprocess_post_check(pip_process)\n\n    def upgrade_package(\n        self,\n        package_name: str,\n        package_or_url: str,\n        pip_args: list[str],\n        include_dependencies: bool,\n        include_apps: bool,\n        is_main_package: bool,\n        suffix: str = \"\",\n    ) -> None:\n        logger.info(\"Upgrading %s\", package_descr := full_package_description(package_name, package_or_url))\n        with animate(f\"upgrading {package_descr}\", self.do_animation):\n            pip_process = self._run_pip([\"--no-input\", \"install\", \"--upgrade\"] + pip_args + [package_or_url])\n        subprocess_post_check(pip_process)\n\n        self.update_package_metadata(\n            package_name=package_name,\n            package_or_url=package_or_url,\n            pip_args=pip_args,\n            include_dependencies=include_dependencies,\n            include_apps=include_apps,\n            is_main_package=is_main_package,\n            suffix=suffix,\n        )\n\n    def _run_pip(self, cmd: list[str]) -> \"CompletedProcess[str]\":\n        cmd = [str(self.python_path), \"-m\", \"pip\"] + cmd\n        if not self.verbose:\n            cmd.append(\"-q\")\n        return run_subprocess(cmd, run_dir=str(self.root))\n\n    def run_pip_get_exit_code(self, cmd: list[str]) -> ExitCode:\n        cmd = [str(self.python_path), \"-m\", \"pip\"] + cmd\n        if not self.verbose:\n            cmd.append(\"-q\")\n        returncode = run_subprocess(cmd, capture_stdout=False, capture_stderr=False).returncode\n        if returncode:\n            cmd_str = \" \".join(str(c) for c in cmd)\n            logger.error(f\"{cmd_str!r} failed\")\n        return ExitCode(returncode)\n"
  },
  {
    "path": "src/pipx/venv_inspect.py",
    "content": "import json\nimport logging\nimport textwrap\nfrom collections.abc import Collection\nfrom pathlib import Path\nfrom typing import NamedTuple, Optional\n\nfrom packaging.requirements import Requirement\nfrom packaging.utils import canonicalize_name\n\ntry:\n    from importlib import metadata\nexcept ImportError:\n    import importlib_metadata as metadata  # type: ignore[import-not-found,no-redef]\n\nfrom pipx.constants import MAN_SECTIONS, WINDOWS\nfrom pipx.util import PipxError, run_subprocess\n\nlogger = logging.getLogger(__name__)\n\n\nclass VenvInspectInformation(NamedTuple):\n    distributions: Collection[metadata.Distribution]\n    env: dict[str, str]\n    bin_path: Path\n    man_path: Path\n\n\nclass VenvMetadata(NamedTuple):\n    apps: list[str]\n    app_paths: list[Path]\n    apps_of_dependencies: list[str]\n    app_paths_of_dependencies: dict[str, list[Path]]\n    man_pages: list[str]\n    man_paths: list[Path]\n    man_pages_of_dependencies: list[str]\n    man_paths_of_dependencies: dict[str, list[Path]]\n    package_version: str\n    python_version: str\n\n\ndef get_dist(package: str, distributions: Collection[metadata.Distribution]) -> Optional[metadata.Distribution]:\n    \"\"\"Find matching distribution in the canonicalized sense.\"\"\"\n    for dist in distributions:\n        if canonicalize_name(dist.metadata[\"name\"]) == canonicalize_name(package):\n            return dist\n    return None\n\n\ndef get_package_dependencies(dist: metadata.Distribution, extras: set[str], env: dict[str, str]) -> list[Requirement]:\n    eval_env = env.copy()\n    # Add an empty extra to enable evaluation of non-extra markers\n    if not extras:\n        extras.add(\"\")\n    dependencies = []\n    for req in map(Requirement, dist.requires or []):\n        if not req.marker:\n            dependencies.append(req)\n        else:\n            for extra in extras:\n                eval_env[\"extra\"] = extra\n                if req.marker.evaluate(eval_env):\n                    dependencies.append(req)\n                    break\n\n    return dependencies\n\n\ndef get_apps_from_entry_points(dist: metadata.Distribution, bin_path: Path):\n    app_names = set()\n    sections = {\"console_scripts\", \"gui_scripts\"}\n    # \"entry_points\" entry in setup.py are found here\n    for ep in dist.entry_points:\n        if ep.group not in sections:\n            continue\n        if (bin_path / ep.name).exists():\n            app_names.add(ep.name)\n        if WINDOWS and (bin_path / (ep.name + \".exe\")).exists():\n            # WINDOWS adds .exe to entry_point name\n            app_names.add(ep.name + \".exe\")\n    return app_names\n\n\ndef get_resources_from_dist_files(dist: metadata.Distribution, bin_path: Path, man_path: Path):\n    app_names = set()\n    man_names = set()\n    # search installed files\n    # \"scripts\" entry in setup.py is found here (test w/ awscli)\n    for path in dist.files or []:\n        # vast speedup by ignoring all paths not above distribution root dir\n        #   (venv/bin or venv/Scripts is above distribution root)\n        if Path(path).parts[0] != \"..\":\n            continue\n\n        dist_file_path = Path(str(dist.locate_file(path)))\n        try:\n            if dist_file_path.parent.samefile(bin_path):\n                app_names.add(path.name)\n            if dist_file_path.parent.name in MAN_SECTIONS and dist_file_path.parent.parent.samefile(man_path):\n                man_names.add(str(Path(dist_file_path.parent.name) / path.name))\n        except FileNotFoundError:\n            pass\n    return app_names, man_names\n\n\ndef get_resources_from_inst_files(dist: metadata.Distribution, bin_path: Path, man_path: Path):\n    app_names = set()\n    man_names = set()\n    # not sure what is found here\n    inst_files = dist.read_text(\"installed-files.txt\") or \"\"\n    for line in inst_files.splitlines():\n        entry = line.split(\",\")[0]\n        inst_file_path = Path(str(dist.locate_file(entry))).resolve()\n        try:\n            if inst_file_path.parent.samefile(bin_path):\n                app_names.add(inst_file_path.name)\n            if inst_file_path.parent.name in MAN_SECTIONS and inst_file_path.parent.parent.samefile(man_path):\n                man_names.add(str(Path(inst_file_path.parent.name) / inst_file_path.name))\n        except FileNotFoundError:\n            pass\n    return app_names, man_names\n\n\ndef get_resources(dist: metadata.Distribution, bin_path: Path, man_path: Path) -> tuple[list[str], list[str]]:\n    app_names = set()\n    man_names = set()\n    app_names_ep = get_apps_from_entry_points(dist, bin_path)\n    app_names_df, man_names_df = get_resources_from_dist_files(dist, bin_path, man_path)\n    app_names_if, man_names_if = get_resources_from_inst_files(dist, bin_path, man_path)\n    app_names = app_names_ep | app_names_df | app_names_if\n    man_names = man_names_df | man_names_if\n    return sorted(app_names), sorted(man_names)\n\n\ndef _dfs_package_resources(\n    dist: metadata.Distribution,\n    package_req: Requirement,\n    venv_inspect_info: VenvInspectInformation,\n    app_paths_of_dependencies: dict[str, list[Path]],\n    man_paths_of_dependencies: dict[str, list[Path]],\n    dep_visited: Optional[dict[str, bool]] = None,\n) -> tuple[dict[str, list[Path]], dict[str, list[Path]]]:\n    if dep_visited is None:\n        # Initialize: we have already visited root\n        dep_visited = {canonicalize_name(package_req.name): True}\n\n    dependencies = get_package_dependencies(dist, package_req.extras, venv_inspect_info.env)\n    for dep_req in dependencies:\n        dep_name = canonicalize_name(dep_req.name)\n        if dep_name in dep_visited:\n            # avoid infinite recursion, avoid duplicates in info\n            continue\n\n        dep_dist = get_dist(dep_req.name, venv_inspect_info.distributions)\n        if dep_dist is None:\n            raise PipxError(f\"Pipx Internal Error: cannot find package {dep_req.name!r} metadata.\")\n        app_names, man_names = get_resources(dep_dist, venv_inspect_info.bin_path, venv_inspect_info.man_path)\n        if app_names:\n            app_paths_of_dependencies[dep_name] = [venv_inspect_info.bin_path / name for name in app_names]\n        if man_names:\n            man_paths_of_dependencies[dep_name] = [venv_inspect_info.man_path / name for name in man_names]\n        # recursively search for more\n        dep_visited[dep_name] = True\n        app_paths_of_dependencies, man_paths_of_dependencies = _dfs_package_resources(\n            dep_dist,\n            dep_req,\n            venv_inspect_info,\n            app_paths_of_dependencies,\n            man_paths_of_dependencies,\n            dep_visited,\n        )\n    return app_paths_of_dependencies, man_paths_of_dependencies\n\n\ndef _windows_extra_app_paths(app_paths: list[Path]) -> list[Path]:\n    # In Windows, editable package have additional files starting with the\n    #   same name that are required to be in the same dir to run the app\n    # Add \"*-script.py\", \"*.exe.manifest\" only to app_paths to make\n    #   execution work; do not add them to apps to ensure they are not listed\n    app_paths_output = app_paths.copy()\n    for app_path in app_paths:\n        win_app_path = app_path.parent / (app_path.stem + \"-script.py\")\n        if win_app_path.exists():\n            app_paths_output.append(win_app_path)\n        win_app_path = app_path.parent / (app_path.stem + \".exe.manifest\")\n        if win_app_path.exists():\n            app_paths_output.append(win_app_path)\n    return app_paths_output\n\n\ndef fetch_info_in_venv(venv_python_path: Path) -> tuple[list[str], dict[str, str], str]:\n    command_str = textwrap.dedent(\n        \"\"\"\n        import json\n        import os\n        import platform\n        import sys\n\n        impl_ver = sys.implementation.version\n        implementation_version = \"{0.major}.{0.minor}.{0.micro}\".format(impl_ver)\n        if impl_ver.releaselevel != \"final\":\n            implementation_version = \"{}{}{}\".format(\n                implementation_version,\n                impl_ver.releaselevel[0],\n                impl_ver.serial,\n            )\n\n        sys_path = sys.path\n        try:\n            sys_path.remove(\"\")\n        except ValueError:\n            pass\n\n        print(\n            json.dumps(\n                {\n                    \"sys_path\": sys_path,\n                    \"python_version\": \"{0.major}.{0.minor}.{0.micro}\".format(sys.version_info),\n                    \"environment\": {\n                        \"implementation_name\": sys.implementation.name,\n                        \"implementation_version\": implementation_version,\n                        \"os_name\": os.name,\n                        \"platform_machine\": platform.machine(),\n                        \"platform_release\": platform.release(),\n                        \"platform_system\": platform.system(),\n                        \"platform_version\": platform.version(),\n                        \"python_full_version\": platform.python_version(),\n                        \"platform_python_implementation\": platform.python_implementation(),\n                        \"python_version\": \".\".join(platform.python_version_tuple()[:2]),\n                        \"sys_platform\": sys.platform,\n                    },\n                }\n            )\n        )\n        \"\"\"\n    )\n    venv_info = json.loads(\n        run_subprocess(\n            [venv_python_path, \"-c\", command_str],\n            capture_stderr=False,\n            log_cmd_str=\"<fetch_info_in_venv commands>\",\n        ).stdout\n    )\n    return (\n        venv_info[\"sys_path\"],\n        venv_info[\"environment\"],\n        f\"Python {venv_info['python_version']}\",\n    )\n\n\ndef inspect_venv(\n    root_package_name: str,\n    root_package_extras: set[str],\n    venv_bin_path: Path,\n    venv_python_path: Path,\n    venv_man_path: Path,\n) -> VenvMetadata:\n    app_paths_of_dependencies: dict[str, list[Path]] = {}\n    apps_of_dependencies: list[str] = []\n    man_paths_of_dependencies: dict[str, list[Path]] = {}\n    man_pages_of_dependencies: list[str] = []\n\n    root_req = Requirement(root_package_name)\n    root_req.extras = root_package_extras\n\n    (venv_sys_path, venv_env, venv_python_version) = fetch_info_in_venv(venv_python_path)\n\n    # Collect the generator created from metadata.distributions()\n    # (see `itertools.chain.from_iterable`) into a tuple because we\n    # need to iterate over it multiple times in `_dfs_package_apps`.\n\n    # Tuple is chosen over a list because the program only iterate over\n    # the distributions and never modify it.\n    distributions = tuple(metadata.distributions(path=venv_sys_path))\n\n    venv_inspect_info = VenvInspectInformation(\n        bin_path=venv_bin_path,\n        man_path=venv_man_path,\n        env=venv_env,\n        distributions=distributions,\n    )\n\n    root_dist = get_dist(root_req.name, venv_inspect_info.distributions)\n    if root_dist is None:\n        raise PipxError(f\"Pipx Internal Error: cannot find package {root_req.name!r} metadata.\")\n    app_paths_of_dependencies, man_paths_of_dependencies = _dfs_package_resources(\n        root_dist,\n        root_req,\n        venv_inspect_info,\n        app_paths_of_dependencies,\n        man_paths_of_dependencies,\n    )\n\n    apps, man_pages = get_resources(root_dist, venv_bin_path, venv_man_path)\n    app_paths = [venv_bin_path / app for app in apps]\n    man_paths = [venv_man_path / man_page for man_page in man_pages]\n    if WINDOWS:\n        app_paths = _windows_extra_app_paths(app_paths)\n\n    for dep in app_paths_of_dependencies:\n        apps_of_dependencies += [dep_path.name for dep_path in app_paths_of_dependencies[dep]]\n        if WINDOWS:\n            app_paths_of_dependencies[dep] = _windows_extra_app_paths(app_paths_of_dependencies[dep])\n    for dep in man_paths_of_dependencies:\n        man_pages_of_dependencies += [\n            str(Path(dep_path.parent.name) / dep_path.name) for dep_path in man_paths_of_dependencies[dep]\n        ]\n\n    return VenvMetadata(\n        apps=apps,\n        app_paths=app_paths,\n        apps_of_dependencies=apps_of_dependencies,\n        app_paths_of_dependencies=app_paths_of_dependencies,\n        man_pages=man_pages,\n        man_paths=man_paths,\n        man_pages_of_dependencies=man_pages_of_dependencies,\n        man_paths_of_dependencies=man_paths_of_dependencies,\n        package_version=root_dist.version,\n        python_version=venv_python_version,\n    )\n"
  },
  {
    "path": "src/pipx/version.pyi",
    "content": "version: str\nversion_tuple: tuple[int, int, int, str, str] | tuple[int, int, int]\n\n# Note that newer versions of setuptools_scm also add __version__, but we are\n# not forcing new versions of setuptools_scm, so only these imports are allowed.\n"
  },
  {
    "path": "testdata/empty_project/README.md",
    "content": "Empty project used for testing only\n"
  },
  {
    "path": "testdata/empty_project/empty_project/__init__.py",
    "content": ""
  },
  {
    "path": "testdata/empty_project/empty_project/main.py",
    "content": "def main():\n    pass\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "testdata/empty_project/pyproject.toml",
    "content": "[build-system]\nbuild-backend = \"setuptools.build_meta\"\nrequires = [\n  \"setuptools\",\n]\n\n[project]\nname = \"empty-project\"\nversion = \"0.1.0\"\ndescription = \"Empty Python Project\"\nauthors = [\n  { name = \"My Name\", email = \"me@example.com\" },\n]\nrequires-python = \">=3.9\"\nclassifiers = [\n  \"Programming Language :: Python :: 3 :: Only\",\n  \"Programming Language :: Python :: 3.9\",\n  \"Programming Language :: Python :: 3.10\",\n  \"Programming Language :: Python :: 3.11\",\n  \"Programming Language :: Python :: 3.12\",\n  \"Programming Language :: Python :: 3.13\",\n  \"Programming Language :: Python :: 3.14\",\n]\nscripts.empty-project = \"empty_project.main:cli\"\nentry-points.\"pipx.run\".empty-project = \"empty_project.main:cli\"\n"
  },
  {
    "path": "testdata/pipx_metadata_multiple_errors.json",
    "content": "{\n    \"pipx_spec_version\": \"0.1\",\n    \"venvs\": {\n        \"dotenv\": {\n            \"metadata\": {\n                \"injected_packages\": {},\n                \"main_package\": {\n                    \"app_paths\": [\n                    ],\n                    \"app_paths_of_dependencies\": {},\n                    \"apps\": [\n                    ],\n                    \"apps_of_dependencies\": [],\n                    \"include_apps\": true,\n                    \"include_dependencies\": false,\n                    \"man_pages\": [],\n                    \"man_pages_of_dependencies\": [],\n                    \"man_paths\": [],\n                    \"man_paths_of_dependencies\": {},\n                    \"package\": \"dotenv\",\n                    \"package_or_url\": \"dotenv\",\n                    \"package_version\": \"0.0.5\",\n                    \"pip_args\": [],\n                    \"suffix\": \"\"\n                },\n                \"pipx_metadata_version\": \"0.4\",\n                \"python_version\": \"Python 3.10.12\",\n                \"source_interpreter\": {\n                },\n                \"venv_args\": []\n            }\n        },\n        \"weblate\": {\n            \"metadata\": {\n                \"injected_packages\": {},\n                \"main_package\": {\n                    \"app_paths\": [\n                    ],\n                    \"app_paths_of_dependencies\": {},\n                    \"apps\": [\n                    ],\n                    \"apps_of_dependencies\": [],\n                    \"include_apps\": true,\n                    \"include_dependencies\": false,\n                    \"man_pages\": [\n                    ],\n                    \"man_pages_of_dependencies\": [],\n                    \"man_paths\": [\n                    ],\n                    \"man_paths_of_dependencies\": {},\n                    \"package\": \"weblate\",\n                    \"package_or_url\": \"weblate\",\n                    \"package_version\": \"4.3.1\",\n                    \"pip_args\": [],\n                    \"suffix\": \"\"\n                },\n                \"pipx_metadata_version\": \"0.4\",\n                \"python_version\": \"Python 3.10.12\",\n                \"source_interpreter\": {\n                },\n                \"venv_args\": []\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "testdata/standalone_python_index_20250818.json",
    "content": "{\"fetched\": 1756161930.970887, \"releases\": [[\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-apple-darwin-debug-full.tar.zst\", \"sha256:6b5e7c29d4e5a699462fa84c838d8bb3eaf03789025091213abab7983c5907e5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-apple-darwin-install_only.tar.gz\", \"sha256:e15d48951b0f21f92d99e84c2d3d0ab4b3db69d7d4c8cb52eb16a85f17cf8d61\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:f38f5fcbe39e657742e21a12c890f9f12d20d2c0eefaa2e6cd4a975f3f7f9dcd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:118b40f74789c322dd05eda06cae84a7fe28bf27fd70b722b63500781d1952f3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:77897dd2bef7ab973a25f4bfb5b28b2a21c1dd8de995f24d40e27d37f3a74d5c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:fc6af1f9fa05e5cd48741c703f53656b1f0e6320fd498fbb95856719ef2949f4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:76308719c53363488950927c1d7311ad09d15a495bc3ea0738f32fb0630c4729\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:d3af6d835d9c056c211653b422eb9df613fb90a6e13c335aa714d503709b3228\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabi-debug-full.tar.zst\", \"sha256:44876914d4c8474c0693f2b198dda6e6deb0af4364be00935c26e878922654d9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabi-install_only.tar.gz\", \"sha256:954543fb308006f04bff4974af20f56753b7fe1abe601269ed10843309f1108a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz\", \"sha256:e696aa719c8c0631ddb942502894529f530e7bbaaf94fa9ff8152b593779f972\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabi-lto-full.tar.zst\", \"sha256:070b336a88221dec9752e63114b6be432bdabd0cf8a612c6e432c8d881f613da\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabi-noopt-full.tar.zst\", \"sha256:4d6cf8fad5b259f04d4c6eb98b904270210ce0bd7681a97863cf707be95e0633\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabihf-debug-full.tar.zst\", \"sha256:749f9c3b3ade288b92a4050e25563e30bc22793327acf8969819f5c221b87992\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabihf-install_only.tar.gz\", \"sha256:66a147b8c51a8ec5ff26b3f64779a7c8a28358f64a1ef81cf274bed863771a9c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz\", \"sha256:d73cb27efeda5878660b5fbdc999dab6d175c0e7c8719f6407c9f898084e93e9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabihf-lto-full.tar.zst\", \"sha256:8b87b531174a097f974bf355238eb7a314cc3261c9cc91bad660caf713907040\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabihf-noopt-full.tar.zst\", \"sha256:8eb43d825a6c249d046502fea146dd99f29a8654ed325ccf69b1d6a24bef76d9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-i686-pc-windows-msvc-install_only.tar.gz\", \"sha256:0250eb3c14e74879bded3143400c268e78b46f70dbf3c7e307d7806029bf39c7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-i686-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:1463aceb81085a5cef3d6fb7513bb4b34548941b79ba5e4d89e0a439193f8663\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-i686-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:0a4bebdb6340e79dbfe9093b7d52c86abb6b9bec0cf8ffad991ffc48abcc46b8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-ppc64le-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:e90ebb19f4a7eb2824885e8eff328e671beaf61f0fdface14af60f59fd759556\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-ppc64le-unknown-linux-gnu-install_only.tar.gz\", \"sha256:a0da3d9f9153a523be5225d8922675330e4866b904167db5ebe5d1ca203d52d7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:7828a0da33d1564e138c71c9a7d8fce18c7bef862ad10aa8fbb2a607a026e1e1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-ppc64le-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:5d3c87e0aa4d3ae6c97189619fc2024032379e2b171047b333a2a591ceeeea3a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-ppc64le-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:f409cde263169051e6f2bf848ffdfc3ba46220da4b4ab020da3fa53f2040ea51\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-riscv64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:46566bf3d29011dd50e4fcc726905568340f2ce7a64ba887581a9ceaec6aaeb4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-riscv64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:e575b75dcf2672d97c150d9d58d5177d8de0dd36b9628376586e0eee9fa7f83f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-riscv64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:0422bd3037d3f9e61377ccaf0b45bc444558387de1446d900c2a52c512ee6fd9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-riscv64-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:fdb551adce7e5690506e43edb8158209f244ccb66467d1e14748a62ab3268d47\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-riscv64-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:7c32e98bcf922d547d7317a312c62f610408780bfb050c63e668039a322c6233\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-s390x-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:b76f0efec27857353ee5cfd932c8be1cc4028cd093e097c0b5a70f24f685a9bc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-s390x-unknown-linux-gnu-install_only.tar.gz\", \"sha256:7ca911668434797866e079fb38e7271763b88225a5475937530aef649e4ab99e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-s390x-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:bfc51d57439be87b5cfdb9b849be22194c887c048e661d0fb3956b2071efa03e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-s390x-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:13b74902fad64af56b04c9c7a6b418ba925fc98cf021fd33fc0549323fb999bd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-s390x-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:b2015772b7ee404630ab6ba81deb088039a6506fe0bb4f1c2e87825fb440897b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-apple-darwin-debug-full.tar.zst\", \"sha256:240138dd668c385a7ec84141ae915a0fcbaa88a239c0671a9b48f58cefac7547\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-apple-darwin-install_only.tar.gz\", \"sha256:0229f4524603efdfde8f050bbf01ce040daa9eaa31ce6c3ce815783478b21aa5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:49b56e8e06479f2107a0539fead39f04dcf4354ba7f3444482d8bb50ab99d489\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:495a446704a4f63d9978e3c129c20da44ec0413ff1bfc5d033e3df63fa93df0c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-pc-windows-msvc-install_only.tar.gz\", \"sha256:a0c5d3ecaf8956bcf731c48ca69eaba2cc23836db9145cbae8d36a4108f914f2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:05bdc00aab4472b2334285ec70f10c1c908f2441660dfd766ca5d1fa83eca06e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:9b93db727eefc58a2a961d6bc724a8b768d28ba059fda296723d1daee381458a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:b382c072b333310910c6d44f68c5db603307d1118988e6feeafaa8be6d348d4c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:7ac2b25f02d5a6d3dcee6c7725c364bd048ebb3ed6768706a7d6e8466f551053\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:7dc5b808f8c33101e954c9ec10b6128bd81f659eb00ef1837ebf86e9bf922d65\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:8f669274b242eb3227c26a80abf4f605b5b65822cee10a541bdf27fe1ebda45a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:82c23eaff1e5350b500a01df615c6f231bf8a4d9128e60fd4601865483abb053\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-debug-full.tar.zst\", \"sha256:fb5d905cf98c606f849fd7f658491bc21b1a7351c472a3d2be38daac5f401abf\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-install_only.tar.gz\", \"sha256:e717e6429a0b10bcba6d24b881f2cead7df0f5b50165b9e24dce5398aa5f4103\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:c652f38bb3b24dc1c8e0a706d8ded2447ca234acdeeb01d307300f2c54107e91\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:d50821f155455c74a9f3d9a96d61ee01eb08ff89e2cfb8f2933e6ce30280d773\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-lto-full.tar.zst\", \"sha256:9d09d3097070fb38c055c812e2da43da4e99a13394f45bfc1b8df5f27160320c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:bad412518fd598e6a91e8cc314b86e59ddce1b4b3580d560fd7eeb39e5fa8721\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:b4fe263ac532f6a18dc9a66395ec91724360300a7d6ef2011937e1d1e022456f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:1495d6fb8e485df22e1a8948b38fa5d696608d7698bbf6b3caba9acb130eeec4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-gnu-install_only.tar.gz\", \"sha256:8abe99a9d122c2244e6babe0f46e077de23c8a11ff11bd5240209596a3bb5193\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:0d1387fc0366db3fd0b309841d3fbb5084cb576983f7bb7c4ee02f1574baedeb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:9b733175b730fa0a8159ae324d8571fba40b71334b3588a292a5717786b45791\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:0dfbc47a4a168ef46a2c652890dfa133577338556c371f9353577b2f5b67ccc5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-debug-full.tar.zst\", \"sha256:c66f400af6fb38c8938f89c063b3548cef0507d5ed48ac7cbbd03ee1740b4585\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-install_only.tar.gz\", \"sha256:9d9d8995ac62d160f33ae9f94027b3d1752c7786cc3ab673847081bf6ebc5e9c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:d83c2d448eddab7172a73182233149016985ddb72374b24c3e0d56996ede80f1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:3add84d8379c6e520db45930cc0aab55db7d47c73eea425ed30f62c51bfc5269\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-lto-full.tar.zst\", \"sha256:45e162f9c2f53dbf0f1a8fe746c3c8b5c4399f80d26b5a2696548d5adab0a0d2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:7f5ff8aeccefc3f87c59897ff2b6659f3c8394f3ad806037a0f2937970621e3a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:3e24f85804c0e5197ad7d79287352ae63f7b96ace8705f01e038c36ab1a7434f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:4db7c3b6c8f1717f164676662f5b94fb88fbad85d6c7017d229c2a051ad61dcc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-gnu-install_only.tar.gz\", \"sha256:140572f7593550e7fc032bb87cfaf0b00e554fd93eb212c57701934ba9c8d2ff\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:681fa31e6c14fcf315bd48bd845cd68101ec90957fbf4a2018ae2313a27648b1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:d7e8b90648fd735e194f199017fc7f81ae374b788dc78358c3537cf2780567cf\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:1d9d586fa892f06df82ad860a349f3ce1c83a7429d42074d4a180635fae02d32\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-debug-full.tar.zst\", \"sha256:f75e359f94e2245bef672fda83ac45a979c75e50187fe0d19d2139a7c22469b0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-install_only.tar.gz\", \"sha256:38e4dc14a3d238958877645b68c2dfe5cd88ac8a4e0de50d495835a8c79ddaf0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:3bca2c8f36acd45c4d357716b9d9b84dc1e1fb938b7f3a91984e35772b779106\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:464cad4772b63f00538654f587fcf4d8ac807099b80772f0e9052559aadd610e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-lto-full.tar.zst\", \"sha256:1a2e26c8af794defdfca168c9324b60fff4d7459e1c6cdee8a9454104e2ab712\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:fa455c494ef56bfabb8aa81de1089a88c14c90cc5dc0c2791c5eaaae95c2deef\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:06a46801926e90f8c93557b6d4773644f70f37b62b8d05ba3e38683dda6c6fad\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:9a9be19e6402d731df2f4cc90bfbe9e0566f3dedc219f6300a311f6c6487ea27\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-gnu-install_only.tar.gz\", \"sha256:39ec935aca22d78b3f7113d64551d7757024455400e67d3258c76f680e65a72f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:a635e9af57b90e571b6f3e9c75d06b8e051bc8a4a1f82c454982fdd4d092caa0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:809a532c3828849a4addd5b572b377d691eb37fb6cf1e2d0c2095c0813aa029a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:7fd66f342e2b75954bd11010d1a1cba19b8add90db8065498390b8a03927f118\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-debug-full.tar.zst\", \"sha256:a1fce517ce62a5d629913fe7727849fe90de17563bdc16bd6979bb2c0ba4d19c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-install_only.tar.gz\", \"sha256:3e87c42524f8e6e1703751bcb6e278fd71498e9bfb1d191f1c8b5b28b7d48032\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:7632b2190781f4dc92e22d3e348d43079c0e3a0f85b7461ae247f53fb9806283\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:19c6949ead1af5003a336f07cbff25d58150fc0954c4a4c7e998e0052cb363bd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-lto-full.tar.zst\", \"sha256:e95e371eb54a097d09cd4f4575558399af9ff5e850f8c049941891b787c46378\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:2d834db5e7965618dd574f6a5fcb9ec978de75f1e518d249c05217ff2a3dcba8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:4fb5e0510fd299990e416c4330230ce5b5f871b8863859196a2fb55797ff811c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-apple-darwin-debug-full.tar.zst\", \"sha256:153b93aab639aa8440bb7e60acf0951ee0d8d329c1fd9df630fd486226d62402\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-apple-darwin-install_only.tar.gz\", \"sha256:317fda280cb51852a346da5f595131fcd3e0dfda421b3983f675cb1994159838\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:e6015b4a1e74d1bcc70f6117ca22d5c5c778b393e4b9d6fef1000fc3f016b6bc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:3bd5e4d9f3da431dfc014a73e2d86375d7c91aa8fa3a8492e20b8a38f5762448\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-pc-windows-msvc-install_only.tar.gz\", \"sha256:61187f99352429e8f87fb305a8b995c8cf5b70509c59b409dc80787148920e66\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:8e059bd27bfc1638cf49d7b0701f0e695403b3dfeaf077353807680dddd28bf9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:b7fcba65043af0f2a470d62a74012da21d6e89d71fbe45a1a71056f70d149224\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:e5cfcb8bd56616423a4b02f4cef31f146917eacabdee902dff02a4efd3b89c59\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:c04b98b4332ea0d8be0222e7ca7959e1398c6ebf7f2102b47f9e4fb85f841f59\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:a82a9e73747188e3408ffa416a294256f0bf1d712b3df202b9fe2bb68d3faa6d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:9eead91f702a907b4bd124b2337f8060f77c16a459f8efbd5fb37db751863461\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabi-debug-full.tar.zst\", \"sha256:024b0ef24c2affb1080abf1fd5e9f51e4bed33aa61d32dcd466133b9a4a20652\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabi-install_only.tar.gz\", \"sha256:2706c023ef78dea8ad9e7348525d9bd7a74789c67159c83bb4f7dc00c4f4d684\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz\", \"sha256:c9c8a2cba2667ec19c0db88a5d7bd3a7647a719656c4ca874215a9f984dd00e8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabi-lto-full.tar.zst\", \"sha256:601f89f632e39dc33fc05f6175d1d86d822d446fc4c4fcdb008318deab5ef90b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabi-noopt-full.tar.zst\", \"sha256:ccc52c7bb15c2537fa142fd23b28e2055ec595e686b16463566a15686ffbe2e9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabihf-debug-full.tar.zst\", \"sha256:a8aa258c4e891d5ce9235f8e631ed6f103aea71aabc473655a8c39ba60f295a6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabihf-install_only.tar.gz\", \"sha256:a649cd2656cae6b9ed00263be33bb09170a3e1bbe9087cc642d011e46567e327\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz\", \"sha256:baec13fd3bca3722d906a38850737432ed8f8fa9b2f43cadfcc7faed4be08912\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabihf-lto-full.tar.zst\", \"sha256:a9bfb16d21626f87f41845031a8893f02f224ddbd53ecd801eec638e82b02b36\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabihf-noopt-full.tar.zst\", \"sha256:02d4cef8fce6f3b14fdf080f24a1732ae7519786e55b411cc552118868cc9918\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-i686-pc-windows-msvc-install_only.tar.gz\", \"sha256:e3a44cbe769c3127dea917985ae73667285e2560d382e4619e89f5f47b456c65\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-i686-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:ca5d9dcfac27e47aed9bb29d9fcc4cc60ece9d7d5cf4456c41bdfb4cb114ea8a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-i686-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:059cd3204e788c1b3ac19a1a9c641cd81d0ca4ff9b7294aa1b19bcac04c31dcf\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-ppc64le-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:d766a47d9ee3fdfd7965aa22aac0b3e189c649201077169d245401ee5dcdae6a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-ppc64le-unknown-linux-gnu-install_only.tar.gz\", \"sha256:dca9fb36c4c79fee1cdd8eb6557791a49cd40728b916dacf4780d2e9d9921cd5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:10179aeeb9750a6c510cbe745cc8bb153e3791410da1160e42c1bec774fdbb9d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-ppc64le-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:c14c39362984cebff8700228d4f1f60620baf29465991d6625456833fc758d6a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-ppc64le-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:f572b88d0107debf2785b04b08cf9bd8098f2be9b92bc79e7f03cd81b9a1c955\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-riscv64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:68b3e0d1effaf23590efa9686333182b4a3011b783ef77550b9903a9372410cd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-riscv64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:31e16f87484aa6f870ac3fdc4bf17b454bc796f4ed5559b023b725b6ce2ce161\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-riscv64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:8260dffbb7807600762b2cceab15c60507933df1193510caa37adfeaad6db5df\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-riscv64-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:0184ed657ce75dcf1f6520943016ec900f4b4f49b2cc59e00a23a5f3e87aa44b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-riscv64-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:f357caadeb586fcbb9a92a1f712ab68987c0ea858a0b73c9b1371b0f741b22ec\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-s390x-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:901dc1ba24af638e172935f1b27d2e2d488fadc0e8777a76521e1651373df9db\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-s390x-unknown-linux-gnu-install_only.tar.gz\", \"sha256:325ba5fb7c6a435ed67f02578e504648985365444e41306c7ee9de6c02a64c32\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-s390x-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:8d07af60762fa18b1aa4c4fa562360ad0e30b0d1c9695e7355a6e01e87645bca\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-s390x-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:177da773fdd10dd37edf167c4ee130427195e369c2f6e923ce4246f060f018c8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-s390x-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:6569f04df391a0b8225ec93a5a4f092d3876848042414ea483b716c932e61456\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-apple-darwin-debug-full.tar.zst\", \"sha256:80e646a23765b96234daa509403c489bd1ddac3d3f06ecf43eff564b00a0dea4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-apple-darwin-install_only.tar.gz\", \"sha256:eb5feaf842207e374791ba5652621de61d55dacbdf765ae97f28630b33cca514\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:e494d718ea2b98b720f15f1e150ec5d440cf26efc36c38cac1203b173608efab\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:50ea66f34549ba1a641016076b6a86907f2f231405f425db6be5ab3e7869f5b7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-pc-windows-msvc-install_only.tar.gz\", \"sha256:008bab1b41dd88a831477af3deb3b10f056f02e3db8313f506e21b77ff2ae660\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:7c5ef9e80b5a1f1b69eff0761b98d9b9892d6bba317a0dbf92e8259ea939a2e9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:808dfce63127ab1da245cc9b152d1b66e9f333df55ef07484c690c3465133a34\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:1399b0cbfeb55fb0b0c8f18012b5430d54088786075eaf00d5f8b60806019e9c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:b3d07471abdf1b3d2867dd44f095c891fb072bab5667b9322355546f9f9c5dda\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:22232b7e892726fbf898e449cdae9ccfabf080319655575a8c5e54a39b553c96\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:fde49f6369f4d36a0d6aaedde461c609d280a325f794301ec5fb815f497f5057\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:16e30d22e0bd72f1c9152ecdd004a0cf03e6734dfb402492558d6e9c467c15f8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-debug-full.tar.zst\", \"sha256:ae1d4c8b35403e306bdec404a2330d9bd1f223c2d160b0c597d4fcfa338589ec\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-install_only.tar.gz\", \"sha256:46d727605a15b4da7162187ac2a0fcc7ea87c2f2be9407e2b38b0dcd001dc021\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:096f2705757d21d058643e46666ec56abcf35ed48d18860c88c9bc836d6cb051\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:57fb77244ed3ec848813acda956eba053ce48bdce65d7d9e1459ef131ba5ab5b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-lto-full.tar.zst\", \"sha256:4cb61b1740d9f6d2dcfa4a0dfb72918f11a047b8a794a4823873baec3b9e0bf5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:ce02f93a1d7c2b35a9090fa175842bd41df52a30e0e0009fe8e238150141b549\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:4535a00fd497a2b527d9773eaa38f3801ffa687452c12d4a1a453d210ec8f695\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:70a2641eca9899265010a19e371c6254827f7feedd020c5c7d60da9c8279aafd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-gnu-install_only.tar.gz\", \"sha256:e5555d98172df323b8068af9048f8504a3e511962f93a35933d6633df101aab4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:3a7c4a1b27e6f537fa8804f1d77de0d1e45d32ae75e32c6155737aab529aa42e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:2fd1cc28ea73dbf45887bffc6d99ace1f30ac1db5910b2aeed020bd606f22374\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:47f432c417ac9671c6a1251505d6f1af4207dee027c6978840f8f40cb75c7491\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-debug-full.tar.zst\", \"sha256:5cba599b5acf33a9995c61a1e6f74a52616be2bfa80b5e41a80d03d158cc49c0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-install_only.tar.gz\", \"sha256:93ffa2abe5970da46eb1b502c90f52b6f25ff300f937cebaf5b347d0bd43ad88\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:17f10aba81aff77ffcd55c14ba2662240fcd219d6c27a977bf8b1f7dff90938e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:58974bfdb3626e8286aded3e8b3aad396f9f673b56362a83648b53a025dd12ae\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-lto-full.tar.zst\", \"sha256:46eb3121c14e89d0463a689f810cbe5f063468ae0b1b48ab495a80af5c509ff5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:37d0a1892d6f0a8ed3adba378bd9de3ddf3a4aeb97bc6e1a6b06a8e048473d5b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:d4d8e33d7c0b95ff1768e6ab5e98c610ed39e59ccf0627516ee4ce7f847efa67\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:b573dde17f4b14917869523812a1fd726c1289c3fe64786fa707486a23515950\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-gnu-install_only.tar.gz\", \"sha256:34796680dc1f421362d5bfd470657750934578f0718e938ee72ebf20771971a4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:f53abb43d7afb425f21c17a545355a8567714b50011c6068e595abbcc43f6038\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:08c981c559326a00cb6852150490ec33c652dc78f19e9ff0b2473ed276d7ec94\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:c9ce0495f85f163bd8107c55ac2a4dc0dbda939d826df9e601be0b007899511a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-debug-full.tar.zst\", \"sha256:c5632aa794079af22d93d681ff71bae2a9a4f35b54beca09b7bc618272e66a4c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-install_only.tar.gz\", \"sha256:9c80a62fafeeb6f4011331d4dc0cb641c841bf99ec7f35b3d8d3847e69dc1289\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:f20c286f6365a43a814994c38409d5628d847b8e8e29a55f8774d0f9a376e7b2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:8dc2a80915f0d8270b7bc0e11e0eb9fed316da416e84c02221880732df6435dd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-lto-full.tar.zst\", \"sha256:49d3c4816c470681120ee65c2c6400d874b103ffca16a49dbadb42eec24a2cae\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:9f412a22730a02735f3e42c686325ca1f71a6b938cde5f6196c1daf44dadf449\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:4fd7981fc378f75eb1fec0762f9d0fabfc0791a2b385f400e867465c474db867\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:d7f26d46393a9805f96224d0dcf6985cbda00a17985b8483f19f426be4e3b754\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-gnu-install_only.tar.gz\", \"sha256:34c66a0f3344011f1eb1d026c4057fba02aada10b1ea11f6ef2f34ea61cec65f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:348cd5c8292aaa448a177c048b0bc48c9c073f1bffeea0998eef5e363fa19564\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:8b380dd1112dc300791f890bf7af4bea0a52bad9ba61f515e7497d660684b787\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:97966ec2fa90953ff90417101036174bb8afb622d4e686e0457721333fcf2068\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-debug-full.tar.zst\", \"sha256:abebc1473c9fba1150b28a74a9df00786d592ec79ca9cf2f791e1612e4cfb2f4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-install_only.tar.gz\", \"sha256:a3442cf2af7822f67ce99a83d1ca83f4aa21ecaaa0cd613ae0461fdd54f2c315\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:cde91b08e1e4307a77cd95ff1da8318ae9562994d3c747f3feae7be42866d260\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:545163f4071e01076c306d84bf9dbf7656c28c14ac4bea1841ed25a5812c6a98\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-lto-full.tar.zst\", \"sha256:5c46a03537f6ad8727cc0a671d1a810b3ad6483c0180778596e57a0baa419d61\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:ede5dbb27f5868af01df76b0ed18fa5ac7d02dc0453d18ac4119968fa3ec11a3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:b02696f917d84fbac4b8b31425db452cf67bd792fa546a17db3f22dd9234318a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-apple-darwin-debug-full.tar.zst\", \"sha256:4b8c1fc6e9cf32bb5a2d95d25db58fc76cc7dcd2b3f74a41b223b97fce85f329\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-apple-darwin-install_only.tar.gz\", \"sha256:fabb5fd4de54c68ce7e70d19fb08127549da5787cd38a34d00000749f4fde478\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:bbf0c85d09a8173e50d18a0198f14d1de91eab17a593ccf9445f214fb0555547\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:29fb93c5166340d0d68a77f3aef2bdb8fd65a988e84746ce69d8cd5ec6b57a27\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-pc-windows-msvc-install_only.tar.gz\", \"sha256:6af9d77e969e31d2b68e78fb431e59553a3cf34c9e1c5cee6ed4ce2c3d63974d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:c2fd87e1039048e8e3045a4b949f0b9a8e5a382d192b2cc7de138eebbb235e44\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:af4c20f18f254d37ade9ba31419510bb2b66e4a0b51f4a17277b5f581f82e7d1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:e8a19eeda0a9b350cd1d23e1f334c4638a19c76388135ac09ac1ce164b050efb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:b3df3317e101cadcc56f08912fe0f68c5fbe1649a035585d650891ecd60a7d0a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:f3dbedb0819ddd8c16f6f3cc9671874a9a35342d7d3d7ba6ecbaa5ed655259ef\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:1b78a02ea8ab445b99570577fa8888146ebb9f3c5db1e2f69c290a8b14b6dd4b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabi-debug-full.tar.zst\", \"sha256:9c0d24e1a34076511ddc6a0596144f9acb1e3456e5a91d064b9abb90887fef16\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabi-install_only.tar.gz\", \"sha256:888c74953946094f1eed38eee443aec70f066ef01ce1503912c126f11ca9ebbe\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz\", \"sha256:7de3c0e4b98331bdab777f20f25557f33134bf35e5af5bc1a30ad889ee47b30b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabi-lto-full.tar.zst\", \"sha256:a38e201fa00fbae323bb8dc45f06d8b1236ea97cb73691d1cb3c5c76452a04df\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabi-noopt-full.tar.zst\", \"sha256:fe60dc7c6d4eb52e4ceb4e9336c447f49e0eeec0b99581a70b95e2718b808588\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabihf-debug-full.tar.zst\", \"sha256:0bb9a88a97c0b2fd9afd8f45e611a4054a12de9d9afa5754377f2ec8c47055a1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabihf-install_only.tar.gz\", \"sha256:60720ad0301206a8ac7f02fd9a8c2f391da604e5be7360d8419a0caa121a061e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz\", \"sha256:f1050c822b878818728d96f125a77dd6c66352f2658c94bbc34a016158f9d527\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabihf-lto-full.tar.zst\", \"sha256:d8cbbf4878b557dc180596208420fffd0e80a1875ee53079cf35c4ffa6946f9e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabihf-noopt-full.tar.zst\", \"sha256:4abc93fcc85410e6369fb55f03c31b01f323832a8b9e67afe9e0270ac19d27c9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-i686-pc-windows-msvc-install_only.tar.gz\", \"sha256:26d15598858c657644c84cc80bc61af3d8ebcbf40700a5576ed12b17086e0fa1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-i686-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:c015bd9455ab2ef8fc3ba1d8ef3d346d84699440f305798177a2e7a64de13f2c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-i686-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:eff3b14eda764b4f94aad57daa833201c5e2a01fe693adf86ae60c42f6c5250d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-ppc64le-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:676400f7fd07bdaef0f100197316657a9e8ef20f30fe3151ec510c61c72c7d17\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-ppc64le-unknown-linux-gnu-install_only.tar.gz\", \"sha256:f366d3b811ba88f0426d4657d06bd1993a49524b8e83506afee56274550a3c6e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:e60071ce581eae85fa159d9991e0a2f05781deb6780ac7f0f895cf28b020ca93\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-ppc64le-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:3448f02bf82097a4186b45fa60324a6677f39b809fac051820012fe824db89dc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-ppc64le-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:c00b923eaa24cb3990dad56938e6d6bc67bd082a3ff451b9afa22cfe80cab4cb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-riscv64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:92c7a787b9164554a65d7bcfe337bf5bcd44d2d898487f1ab9b0f29f2146828c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-riscv64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:7ee5ec8d5feb24152a7413f1090f6651cca60e2af2d4a748e9f28d041c4695ca\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-riscv64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:fd16bbbd6c479d7879c55890f1df1a028b62106031bc4b69ce17ff220a523813\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-riscv64-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:7d7fe2aff1818582976177ce6891581c4d68ca85799e822a8dd9d7f846eb574b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-riscv64-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:fbf455a013b65190a1c762e3fe155a304d00212867faad29564a9d87ff4d1446\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-s390x-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:aaaa937b9763c3952f4748e2cc32ea7768af94aef5132c8d4ca8a4ae4f5dc85d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-s390x-unknown-linux-gnu-install_only.tar.gz\", \"sha256:c7fffe139b878a746b4df1b7c0c53a9c04565d72491fbddef0f06050575e1953\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-s390x-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:ae2ad403e316834cfeda9f6517e0217833d369ec91f978fa90903104d7ac10be\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-s390x-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:37ba137dc1a672e6e5278f6fcd70602042dd90cd97b66f144c9c701694b15530\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-s390x-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:dd38dc551f3dcb51ecbc6b586ee8665dda16c12c44edb6d4e758f8b33b3865bb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-apple-darwin-debug-full.tar.zst\", \"sha256:1ee397343f8de83c6d0e4fe8ebb1ba7333de9f7efa200e2f4fb3ceb197a402fc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-apple-darwin-install_only.tar.gz\", \"sha256:896add7763faa8012ba6a37346718b32ed0ac041230979cfe8fc802fbb0daeef\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:296af6b9dd16f16dca2503a9a1cfc8593e4cd79ed19ee20cb2557da0912cf6b2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:330455827333437f47e148e0a92c3eb65d13604426a3e056f6f867875e5a9ba0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-pc-windows-msvc-install_only.tar.gz\", \"sha256:a6bc8c4658a758ec0c111b5f887f80595943fd84f28bbbaea6e7e30c7815dd26\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:9624883ba5ad269282028e17db800f7ddfa8f57007742a868c2acab0eb3c91c2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:d37038d1476a11dcdcdfdc46eeff2198e617d5d56112a3dc99179192c2aaa35d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:aae842cc66597e5ea43c62dd55dd9e34e9ef73dcae7f8be4ebfd69c1aeeaa9c6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:98229938166f51deff81b00d71455fac84a57290b71089bd5fe673738557f053\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:b5a4f189f25cbacba0f76c9bd6f3ea8c35d2064068aa74ccbb6863068caababd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:7702c1cc77708922c8c0a0b4e19ddc6ab0b38dc0375eca434a10d23956a490be\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:cfc80e799c177f336867f35d513f7b9e1015771cad2aa028422721d6d67691e6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-debug-full.tar.zst\", \"sha256:6fb9648f858b32f968828cfd49a18afadeb755e73654d8bfe2b800a1b972ca1d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-install_only.tar.gz\", \"sha256:b7c463ead2f602726eaaf6bd549bb19b89571c1c2309af5ee665d51983c22377\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:d88201548c561f347b5b9206885efaeb06fcb4ddc39968c8b872ae0f8c92c9f8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:4930aa7e8b3ab662db187bf9a0a62be9c772c9f4ffd771afa7c7f7933d02ac22\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-lto-full.tar.zst\", \"sha256:91059889754bf42493d526ef459d3201c59211e04cedfa9e4c3cc28537b766c8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:bd570be19f1670d45e3440ee77a904d0fb1b85e72c8ada1cd0e3b1e62df8f025\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:f4e0d699059ba36f24f11f39f434c47a19536b9d89edc8079b462aa40d3b7f3e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:1aa352bd9fe78f3ad34554eb2d7cfeaf2038f14ac9111d441d9bdd32e8fd5f64\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-gnu-install_only.tar.gz\", \"sha256:b76d60c2207dc86648bd6b4c1f6f4a8c8e74f2593c63c3bcac8c2a6b172b312c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:a1fb9e8f108ab31ca72ed488e79370873721a2a4fe7e625b07964c380721a87f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:78d1b479d756b61c43f75c57d9839ab478a10cd5e00ee2d903eba8ef57396363\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:62f66df2603dcb61769e2e9dd2a8a75e0b82ee8ff2002a4624d8e1207e0b5ca5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-debug-full.tar.zst\", \"sha256:b69a6d71624ecf8fdd076633ebbef8d451771fd4e9b92a6134ea352a94f79f38\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-install_only.tar.gz\", \"sha256:7e39780613300214b6708b5a550a20a8057521d0f678d69d7dd0b44593991ff1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:160b53af128dfad058d69562e9f1b0a46391c11052d7e8ab5ee63c4cf589ec4d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:3ab2019730d2782dc8e2de243b630c0ed1cb31741cde722302fb06043539724f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-lto-full.tar.zst\", \"sha256:00f452d4f632dfc197cb71247901c4c022ace1db02c2d38a4e8acba59d95817c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:098d43b7c6fd29df8a053e9f732b3861eccc7497fe3422a776ad4f19e665d502\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:18f58ed8674480b7ee0c0cf461ccb83a61f4c5240e4842168e229c0191912e43\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:40c2b32e2dc7e8cc4a24cf6e742ac992266e692ceff09665fcaf44de49793a5e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-gnu-install_only.tar.gz\", \"sha256:cb160f93bad00019ff873775a336855794e0f2f02b68352da907280cfe6939e9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:7a63b513786912dec54a5f3363e5f53d31ff0d64917d57bebf1d613549d57a28\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:233d7ff666eb6b33299b55bb5c129ed89e992f8c0ac7ea544c5c2d0cf89920ee\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:6fb2d19ce491bd989c417722c565822a9c49e3ec78970347c50920bc4d7e5766\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-debug-full.tar.zst\", \"sha256:7c4d97c66066dbd2b94f2b6dfc279f1536d4ca03b3bf20b9284621f31e95bd62\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-install_only.tar.gz\", \"sha256:d46c6e561c1473329c117606e79f86205701e072600fcbb0217cbfd8005adbe4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:b798bd625469d821de501ab5efa81d8a68ee183565982e9d4f74d4758872c1e2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:d25e76bb34cb9164d20338e845f6aff970643072b9c0c80cfce88a0fa0f97fdc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-lto-full.tar.zst\", \"sha256:f8b23b9b47343fffbedee24bb1fc40d1f0bb81ebf04253ba0bdf4c486d7726b0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:2470bd27e25a826cf5b2642ab1d2e0a7536d3cbfc6a3c3ed0a81675528313d13\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:198baae8cb78527da65d975b862ce426416a888b9510b3edc2e1c60c79cd9d35\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:81953a427a76fa18c67b987569e99dbd87f0a74c54f4fbb5855bd4f13fcf3ee9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-gnu-install_only.tar.gz\", \"sha256:233f725edb49e5226fdfbb6423ab71b33eee48e0e26da2b4559911799d6a7921\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:560547b4b9db8a22bbe96f2a8ad69ba17752786e0f6820da75e928babb0a1d5d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:a6db36c5d36ac37f6bba36bd559785b1f24f8f3131f2e4dcd55bea77f728ae9a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:78af33974a7fab5ab025ad014f304a2e77c74ca88d3e2d11e032c7dc7ce63d7f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-debug-full.tar.zst\", \"sha256:fe57b8c68b91854ce2d916ac118c7389540cef1c17503d725d34af45b147a7b3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-install_only.tar.gz\", \"sha256:cbbb3f296435adae786bb016389be15e3fc9ff9986f72be540b6abd5aaf6ac29\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:93839c39865588ba431af8783691595d7c6e5b26629c42256ae3c7a195b21409\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:ff53702f9e014389e02387b0b1a03752af32fc01f9dd75ebd5b1340d79b406e0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-lto-full.tar.zst\", \"sha256:a0fa0931128154f562bfbc28da28a46e8fd3962683c0ff0ce40ce98e5f762fe1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:4e5e45daa9770c7c5239b8af89878c46070a259a218e9e92a297801b3a4aff4f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:549ca7aa119b60ee373544b3307586a7e2ad39e34b0df929912afb112f1accf1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-apple-darwin-debug-full.tar.zst\", \"sha256:de58d40c2a4902c266bb94fad29625d5e8f01b5b6241d77c59154481e1e00893\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-apple-darwin-freethreaded%2Bdebug-full.tar.zst\", \"sha256:ac5ec6375c4cdf5818ef0dd186ddb6bdf19419ca2be6c3348cfeb54c6fc17397\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:8fd44f324a4e91c2b91458b36c2720e57b94a3716ad0585766a4520a2582ea08\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-apple-darwin-install_only.tar.gz\", \"sha256:4774f275706f3e250bad636d7cc6bf0e43c88611b7e4ea0418a631e743a3d5d5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:024a3a1c95f171e97a4eaa6d2d289baf6802b72e4767023e3d9e4fa246be11bb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:3f63f4370a1d59d3c6856ac5315473f8d265589d13d783e8f54816bafb8470fd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst\", \"sha256:392c8acc39129abe3b1ba96c486cade638d4db67e3ad5fba7c0533774ebe8f74\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-pc-windows-msvc-install_only.tar.gz\", \"sha256:ad7ee2718a8e3576751bcfd3e4c66315a7ec3cf537485165a5e53a5c4bc1fc80\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:76ff47b0cc0ae364f2f93b00f41abd75a41d8107b79641a73d342308c3d57318\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:06e817fddb9df851639b18595da34f7fbbf2ec0b67708ac2edf1f324eff25786\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:f3d5eed0764f2a93d54044d6cb1ce0fb83b30894fb68e33be9e1d2f057dba263\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:6477a4c7c525b6fa9763b8f0ba0934562e1c2f5bc471d8950d7166f3380ddaaa\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:c7d6c9ff4ba750ecf66514eb01b14b354057fa3e2e52c5649177ded5814754e2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:cea4f6769a84c7ca79a167b40a2fd06dbefc8cec9e36195d3ffeaf992ab581ba\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:6724fd90a45d34bce09bce319ea330a1485735270da620a4310366d79a0577d8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:d9041f00a86ca28de0464bba8838263aa7e662ce31cbf37e71ddc76940a41528\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-debug-full.tar.zst\", \"sha256:7d5bccb1f7d70da1fb2d98921bd4a5995d91f4845b9f50e039bc3ef734dd7795\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-freethreaded%2Bdebug-full.tar.zst\", \"sha256:9191bc7579bc04fe8416b04222c854aca6ce89b25aa4120b2f6f353e37990467\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-freethreaded%2Blto-full.tar.zst\", \"sha256:b544da0e90faea1a2fc5c0fb0dbd49910342cc506fc1e9982f169f970e291e07\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:d5f40b95104cde0e61404c7c1441ca20ed36cafe8571c92bf4733c68b727effc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-install_only.tar.gz\", \"sha256:0a5be388174cdd5001dc5145f8eaa39588d1d63957ee46cf95dbdf51f1ed1367\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz\", \"sha256:2b410948bbe552ecd9a39ff562cc148fa448956d2d0eb2cebff85ca3af6546e4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-lto-full.tar.zst\", \"sha256:a5314aeb37f83e16f1e5afe1c3de1bc0bb0050860d4ea9aef50a187e12e6fc56\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-noopt-full.tar.zst\", \"sha256:0d16385f4f734a01182295f7fee08cfc5341558e4f8c4534d17a88936d5622dc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-debug-full.tar.zst\", \"sha256:c0c3c932b8a564740bf6f18d760e522c4c1934f76721538cbffe2fb7e43d3c59\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-freethreaded%2Bdebug-full.tar.zst\", \"sha256:87b1c9a39697e5f689dc4d033497d4eb61e4a22fd76a55e5ddb864f8f64d8bb7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-freethreaded%2Blto-full.tar.zst\", \"sha256:8721f2884e32f84453c48e1cf6e7338e6fc23e222af7eb59f28a662b600c12c3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:8b65603be59b3afec72be49eb6a9260fb73786ed3d2a9007e07b21ff9813e703\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-install_only.tar.gz\", \"sha256:8053aa20c7a94e1f0472dc42eafea0a4f4fe2b994e668f677a6d28d8b28d9e87\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz\", \"sha256:b3470afbeb4308a62de685194007a1fd3ef500f39c7b84defbdb71d7b9f06cc8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-lto-full.tar.zst\", \"sha256:a099a8323f3e5831f4167e9e8ce8c1065b4796166364dbaedc4e2a25cc6daaf3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-noopt-full.tar.zst\", \"sha256:2f690ac13e32279d8ae14a94db9ce26484a5d110112bdf10783c799e00b7c079\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-i686-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst\", \"sha256:2caa9f6b7483afbdbfc0aecf4681fce0741324c8da67b5e2c5779ccd05526481\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-i686-pc-windows-msvc-install_only.tar.gz\", \"sha256:afddb2b6908297637a38858a07a5b1b1df8f23920745abc72d1cca6da6609f92\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-i686-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:96a64898961ad676026cdb430e2523f9f8d7ee101ae5a48b892e9cae689a86cf\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-i686-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:34a1ae65cd7f85498aa7acea705999e977754a64c1cdbb78aa34344b7f8083ce\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:0e0d697a39336494a63c42ac6a36b737e94b7417515768161d08d6147627e7e6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:61ebeb58df2cb46adb19c101ef74449dc50b45420058677db7ec10f3210f4e26\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst\", \"sha256:789900ee365744bcfea0e052f1a971a96070bc710ba49e184721ca9f85e9f4de\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:03142b036f145c6baef9b6e8812cb3688700f6425debeb245d721aa5ee97eaf2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-install_only.tar.gz\", \"sha256:d49d150d37e6e0ae2a7dac1678b9053aacc6c597e77fb90a85ed73269e8b42ef\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:fc009d69a94fb3f125fa2f3cb6510cc2c8fcdd40bfc4f3a15c4d00c83e91df40\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:2662e4ab3395c16a273ecb4dce7abc8c941cb72b9026e38f4a8168b8d29826a1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:09fe05d3f4d10ba2ec6c4a4c935525c29831c1c79fe18a03d96bf1477be0cdc8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:69a4705d3463a9fe4b600a82420699ec3f434092cf32b508d3b0470daaced9b5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:ce1967455ad984ac15f54095689d04d4c228bff81bf428d2e15e6a4f76717c42\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst\", \"sha256:22f0cd8650709300bdd9b63ab8ee25f587c02e14ae6eaa94b26352cc99a2199c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:09b02210e209e32318ca1111463fbaf7aa45ca6bee41a5ba3c307d065b233455\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:0fa16da5b4670197a932d60d22f7b75cd4bbaafa2b84d16769a89bb3d564c83d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:dd4ba93b60016f103c9fd386a7be65f7ca0ac311957b366d705335b176e6532f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:2d323313692b850ee3521eba2642c9f444cd565f7c2c17bde6e5e430db9b425c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:6c7cb001672561781f81f03c7062d09da159a3c6f4fa57b935750f27390d5de4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:2c12640f17fab2622963efe1b332713ba34d5d3e50ce79eb5715c6839f74eae0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:b78f3dd9c50ffaf40bb689631ecc7a351ca9d9561cd1752f193648fde3cd3acb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst\", \"sha256:58f47ab62f6909c1d7accd698b41a82843a531f33a4f3b567a6103312e451dc6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:a53a5afe8a92c6a9e6a61abf9e2632202a799f4d1eabe51de1c3843bd82a29e5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-install_only.tar.gz\", \"sha256:ddddba68f2d2d1c91bcbe4b23e87d8aa4e5e5e0e4bebd8598e6eb9678dd3fb2d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:2f87f6314c858a4611bdeb231b26e1dba3bce79e0bef50fe26b911fa58e02530\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:8c760418a65830401b0744655011e467643ebf5acbad27bc4808af592f36eb64\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:4ca5536d7d0ce16cd4ce54f2f1abe2a1fd677930e12e11fa6aa2b7536791b679\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-apple-darwin-debug-full.tar.zst\", \"sha256:a707f99c5c22cac6af9b70b4c632e3127ccac3048a232c28652505a1952a846d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-apple-darwin-freethreaded%2Bdebug-full.tar.zst\", \"sha256:ca0fb03b247202ed5db411ca2a13136f8fd64b1303bca3d4577ed7b041d63051\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:b25e5b7cff072b5366f37e215f86e4e2e461b84dd96060e4fe206024f830ecfa\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-apple-darwin-install_only.tar.gz\", \"sha256:ee27eca1374af8421d37dee40f1489ede86cdc511dc2c6229b4f9c6cd275dc22\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:b4ca23de90fdeaaee2efcd8d604d8336572ee54aeca29cec56b148171f6eaace\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:3bbd475f896616582d90e442c1c9a7ec8a4df7317472501ec506b86e5e9da8c4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst\", \"sha256:c72bcd147cde726050ef75154138fe1be6d23cd40225782fe29c0ee10506aca4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-pc-windows-msvc-install_only.tar.gz\", \"sha256:95258d68b4857873035201f86dfdfc6fcfeb311c8b9ba4610e54e9f9c5604b06\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:d60173a7693c3134b8364814b80f98ddde0638e0e75e5b004f37452fccf67a33\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:630a98a7b9e02dd2025fd0d2869d6f2eb562b441a5ef65313ff44f65a7092e65\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:0af338d3eb2da3a0d675dd5387a3129e879e1535b2697877d2d1b3b989a6edc6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:43a64609a1349d57c989746c8fcc4a11ae92df17ff77ffffb9b98cbcb3442781\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:78da0a6aa7a19b8d4198a89a093961fc1376a3925bce16c04b22213fd7386f13\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:f3488a36ef5c10d01d346b9e540727d0807202c9bef52f40a987dc59ddc4ecee\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:2968f91b0c2b5e85dae64950b7629052d90d2fd868ad90deeef343ba610a4a0c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:2f54e69a2abef95f12d6879af81b279a9718d65a188fcaf289b523157e85ac46\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:93ca459f073ebf7171e5dc3dcac67ed6601d9d0ccd5484dca07e02d208ffb024\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-debug-full.tar.zst\", \"sha256:32e29af41d692c6ec6429e07af11b2c6af4d568a81eb5ca95374fbabeb7de293\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:3a2fedabf6a1f8c5cce0ef5289c938204844a6b8ee91e1ede1e335de07d926db\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:6f4e98431081bfca9cf413d5db749e4736819db7ac48d7c2f5252c700ef59612\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:fc327cd3a7983fb10b28a79852cb52d470f9c2c0bc188c7b148c97b9ab00453c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-install_only.tar.gz\", \"sha256:938f1f9c7d516964509100ad3f1e047d8b221b1f22ca5b298f416ebb372affbc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:36410646df35f72ade553c4f1f770ee533d5f624f66aaec5e3f4c4fac49dd86d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:fe36860228b1fbe7d9ea4477ef011dbdf2d2684046b6c28e1c744f18b98031f0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-lto-full.tar.zst\", \"sha256:12c9da04d3933c54cb16091923aa537738f6d150f837fe2b3acd0e59de41abf4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:acc6b71cf4c42d2acc7e916782a390fe0cba16d3ac0fc7f4c79c5cbc742810b1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:e5b5b5f0426d337d646763ccbcb9423c33ed6d5b8576ba03317d6d9974e225c6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:915803e57dc244603722b22ea915c5ab715aab38e617eb0128768085a038c4b0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:ba7a27a1414ac0a2144c49b44a84111ef4fec8dfa5a178e0cdc3aaafad0fcf5c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:4be2be4440804e7b1dc4cbbfa445e0996c8ae54229a35dda796c310317507f9f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-gnu-install_only.tar.gz\", \"sha256:ffeb6041db83f69c39f58e798b8809194824c7cee9ce60f34906e0a889b0a738\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:365c6f4d8b4e0425f465be6a76c8d90cd1c1070ebdbd7ece97922e07db56e619\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:23ae7e028e97a8eadc5f87ed7cdd07e8078a8eafe933089e16dd47683f5f4dfe\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:708600ea8550c53caad68722246ff45e85549351159239bb182b7c9f88f841ae\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-debug-full.tar.zst\", \"sha256:6361010be044b3565e65e64bdf8bef73b9e2906349d8be1df839a9f30f36961b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:5570c8dd8628383536548e27150c40e2f74ecd7adace15383202adc7f089f8f7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:11178f25a28ebc33d02bdc8a0c514cb8af71997e35d69eb16923f031efe08852\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:e13d278cd64a07c2ac32d6030cda460f5661e2c1c8aef0123526442df3397664\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-install_only.tar.gz\", \"sha256:0b8d5d4d636914cfcef5339b2cb8ab64f01a5f52dd2bac8591042d1c6ec673cc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:5459173cfd3cdc1ac4e86e53ddf4eeb5306ba8f733290e321181b73fc64455e4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:c8d80add023a329fe32a5efc3082483bd9ef7f818633bd20f7858d4555df3ebb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-lto-full.tar.zst\", \"sha256:76e7719c8bd7f6335424ef77dbc205f5614dc050e3c64db2d9bcecb5bc7a771c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:e919945248b5f1901ccb90f0f8c947aea9a303e58ac7d78639d13dc6523da0ca\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:55745c0d599e8002b868c2396fc04d63022ab5d10ba1bb8524e7fde3d52aeb4b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:400f5f76353856e6fd3986499cd57f808de9c6cfbc1a0d84428976e2e1ba0cce\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:5c84059a7c94f635f950a84d9152b4fb43e4d743be44704c67e4b35605190916\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:ec39d81f0e7cb3264368768e6a9bb1680f642978460a3502859cfc6dc99f663b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-gnu-install_only.tar.gz\", \"sha256:52cdbac00fac049b1d9b8ac0814ba927e11247f20abcc183c35b48eab6dc0dd8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:7c82d0aadee9b9ed1cf9b23ab93aca49f3a3a94531f7ea711fb9df4863182be4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:a9495b9270fcbef0c337f4cad6040ed62fe9f3e95b980041012628da2c5dd99d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:0c29a37cd5c5ed6740d6f4852cb66f9f69e31f3ed07460e050f02a16ae2deca3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-debug-full.tar.zst\", \"sha256:98618dfed670adb0a9e1766de287d059791f3c37f26653ab771f1b2b49f23ca4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:cd06842ec071b533bfcea8897f6be64354e0446e7831b1c0eae8b668f0073ace\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:6f37fcb6756519dafba3cac38b8bae236ed6b465a46580a2f502e22ac736b558\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:4c4e71c421b909a92ad442d1ad1fed857f514eca327e8f24d139c3bae428b9de\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-install_only.tar.gz\", \"sha256:4bd4ddbea23accfd7420454d86a5c099955b31005049924bef02d69221a4b13d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:6b6be1808ee93482da2d73da28ccd64792eb1859131ec0e9508a01d13c5ab464\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:10db567e94e5c753b9136b6993464274f8a6d4a0e9ec1cb4681ab7d4c4701b8d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-lto-full.tar.zst\", \"sha256:224ac123b384fe530c63f00b021ac9fae1fc8e95dfeec2555050d094f225cb38\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:55e40059f304f91e6411e867ff5204587117078acf5282ec0da4dc99dd560d92\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:4e85db65fa079bd7a916588e91d7de204e963b8e21f85e7c249c26dc9699e556\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:cc788cc520a5f3d258510cd8519fef6628aefb698b960ba6787b59be1f150d45\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:1333f9abeebeb002bd65bdfe0f9dbd7f60eebc47f27b8f0595b42d2c3d61cf48\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:4b49715b32edc1691b29fc0b5879939f5796cf1f3bd82dbcca652c0fd88c7787\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-gnu-install_only.tar.gz\", \"sha256:993ec595c5663202295e27ced14911169f1ffbf6aff8b57c443fee2103683c3e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:54369e420c1f06e4102bd0d03ea495f953594f1e45313a0793f4cd4f1c93e07e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:52b06c881d6edba5e3f5b7fc8e8505b3e504ce0a4aebc8e660c8696200ed95c7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:adebbde26cb376eca03e5631f036df559b0cbaa132a87a99767052739c85091c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-debug-full.tar.zst\", \"sha256:adfef706a323dffff55bc91ff0536cfad98ba4d14c12e53eda1a763e967d42a3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:c2a954b6898e567688ec980d0b3ba4f17d31652f15992280c5456a9cf36d0ed5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:3ebde8b19d4bd9c4cf1cc69c1d57eef5c73b00a2970ef34b08b141db20e3386b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:ea631b90114cba329f003296e8816e56839296beddfe67ce0ff2cfcbe0fcdbd8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-install_only.tar.gz\", \"sha256:da06f8a70a3abb30575e880e90e7ea22b4f4ee13f667a784707f49392236449c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:66054a946cf3626a332e1173a8ed096245233126e30dbfef788fa2acd8921b63\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:bae92f1893daa2fdd59798d083dd94764c48cdb6e078512f2191943c0fb2d15e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-lto-full.tar.zst\", \"sha256:02bd0b69d9ce110cb6422418bd4e2f2fddcef76633681c188caa86cf419a89ea\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:b18aa8d8a0946d7565d444d3e9e456b9df65c4a7dfc3dca7e385f7543de0e6ed\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:b9e2c47ea0bf408cf2ad0aa9b8cb5020b5787e8251bdf7cfe15863b25a687d61\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-apple-darwin-debug-full.tar.zst\", \"sha256:44f7d506915fea2f5bad606a09482b6cc01ca754f18e16e55ce2cf1efce6f5de\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-apple-darwin-freethreaded%2Bdebug-full.tar.zst\", \"sha256:06978356386cf3e6c8cafc0a2129a5b4b1c39c4e0d6b93635dfd2bdec1bed59b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:e84cf332dd6b80d097409cb0dadd0dfe1c3c29931bb4421a922c2649ef537dd5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-apple-darwin-install_only.tar.gz\", \"sha256:7816b56230f5e95826f24d31baa91f16f8e2d8c8ee6797687422edb263ed7775\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:06ff382e96de920cc7305e1272b811c7ea14ec1503dc446c06653918e194187e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:1b5ecc766f26689ab2bbf8a45a73fc3c3203df803a454d8482db6830eff23013\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst\", \"sha256:2ad634311f9a8bc9beb0caa390ae63a05c8c5df5dea0d60a1714356e1ff4f844\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-pc-windows-msvc-install_only.tar.gz\", \"sha256:ce19f64f5e0fb488ee8bff1a57e3511319dfe79e9e0ef58f7cadeb437cb87634\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:ad06e2b720e720781b3a7000ebedcc9200f33f6898ddb9e8d28c87a77883767d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:00522053a07158621b654cc537608cbe996e11f193e805e5dfe6d314bf549492\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:ec42a5687e5bd9ac114febdefa4a01d9e88bfa5874d7358d5dda9ea2713366bd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:174681c59854ca6305cffe6deb8f4a7a0f80d211fc84333c2de35ea4b2de046e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:3dd408d9ed53000bde24012f2849f0580936bf1e77c939f73771c3cbd44a288e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:d5348b1e334f8e460d84ece38eb1f0a29fc3ec8272a526f00a707a8b1c856231\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:3c65f72e00596fbbff66b9dcf07d6ee7817fed247a090c283a8e0d3adfa972e3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:39142fb98bb51578f74283cb8793854554805ae082e154aefd47677ea19ee8b6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-debug-full.tar.zst\", \"sha256:00f11921bcd1fa091577bd4bf615beae8ea75b16f5185a0d3e7dc8d2eaef0247\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-freethreaded%2Bdebug-full.tar.zst\", \"sha256:5063b36942d0607ad22197fd14642cdfc4744d3d7e8702bec269fca2502dd8d2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-freethreaded%2Blto-full.tar.zst\", \"sha256:7f2a7341664dcc4b009437c7eeb9fffd57099b2edb2d160b30c7284b395814d8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:b850e1d1b81a1f47a7e2717d6c92b1530a02de0fa3453f249251b736a1d3e935\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-install_only.tar.gz\", \"sha256:27ba44f22431b0771dd8c09f1c409aebd07dca9baa2c60641eb1a43e311f8291\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz\", \"sha256:140a1078007da076d5d0244d4f67e7fdbee0fbeb2f5cdaa515334b7cc1bae4d5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-lto-full.tar.zst\", \"sha256:776d49a2cd77f437e8357dac56aaeb67460e70c08d6b323b8064a8b68f656633\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-noopt-full.tar.zst\", \"sha256:535f09e5b50b44477c6534de30d8ad8a564e011c887d501b776d50c51ab5e8ac\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-debug-full.tar.zst\", \"sha256:c605d85be3d5d138797eb46d0839bbca5e5565d90371f3031129480cb3c410a1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-freethreaded%2Bdebug-full.tar.zst\", \"sha256:70655d7de79893ef2020eb0a382cc6819a08d071272e9043c00a3c6bafff9c4a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-freethreaded%2Blto-full.tar.zst\", \"sha256:a54be21e79f5373dd5050305ad9b21d8fe61a0fceece099aa32c04ba1aa8a1c9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:1325381ca1da0f25f8a8e5e644338837fdd577d34428162281dfc1271d7a0340\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-install_only.tar.gz\", \"sha256:b79e6e6439d93b65404a795e637b84c1afd6b188c513942eef5a29a65550ff27\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz\", \"sha256:e0372e2e723fdea0e887a80a49c8c854bcb8b8abff63fe9a7f68b2026049072d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-lto-full.tar.zst\", \"sha256:92bc9b99ab020123375f173c1b633eb7552cae58adb6015049f0018b3b66e44d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-noopt-full.tar.zst\", \"sha256:cc31c0220f574c1bd0060b08f67d029111792be39b4b3e64c24ea12df971a636\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-i686-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst\", \"sha256:698c4966970c33b02dfd69df95c0e218e94d332d46dae1d92f2208b6ab56b984\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-i686-pc-windows-msvc-install_only.tar.gz\", \"sha256:810b68cb2ba3fc98a59a02fc0972d1a53c65501d28ce5d792b75b27dee8ef031\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-i686-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:6366250c4607e43b0f02fa969963d7d17868e21c3524338955c2f7498037f023\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-i686-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:c3c6cc74e0ed2e59bf7064b41f92bc41bc2a9e98b3cbebb6a94255c0a14efb4c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:920a3b737c927739d2d92ea748ed47a8ea2dfa702571b995024cf9ff16192a29\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:2be91ca30bb11084b457f48d81d0a3319c25203bdd523a237b4c9611384f73be\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst\", \"sha256:7245f2b7e26121c7b126beedfc7c3a2f3fbe596a16bb162013b061e85caebb2c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:d8d5cc2a1db5f6329164cadd587f2fa9fed259fdc181a47d74f524c4734eeabb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-install_only.tar.gz\", \"sha256:4cf3ddef9d3d2c14d0d0d80c55233cd4a4ff1feef58450af469b149a2afa6a52\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:1f3dfcd6fc5278b4212c6e951e37a0ad62123989eb58ff290f31b26849158a4a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:2baf2e091c202c4af0dc3fcaa40f9605dab120634fe77313334bf8893fc7a164\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:a8d8097e46aea8e00b752818cbeadc0f55e6836ba01f235d6d1de87081325fd9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:6029956cd96513afee72c214824ff45e028d17ca78428632d3146dfa84976907\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:1ed636a0399c7574c4d43867320d7e2640959a0d9a3abe872f321562db2e219f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst\", \"sha256:204b656048211cd48fec5e99ca399a293b3cadd95663c4c280e982d28eac87c3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:d737dfaecdf0d99f05fd16043ff3462b90ba18337c4c1b62e243666b34bee575\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:5fc8a508877098a613c625086e62f8bb6bb18af91251bba62d694db5b98433de\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:87e57af208ef79efebdea9e5c0f11c98ed2627455bab45d9b719d8eeac5cf48e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:7ae8d3de45d1456a90aa45bd3ff7ac5284caef228d408dccdee9b94cdec7f76d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:a4035c9f1dd8aef759939b413d45db2535ed28067822558d98f320a0c9d55ccd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:b8bb6be759029b391d5f85a1573f506c9ebe8a7ddc403a7d32a7f5bd5817d424\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:f02643b7989f66bca53c6be78c16aaa947b243dd60989e334989d66a51297f13\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst\", \"sha256:d3821241d9ce003438b9da72553053e53514c2755cde4d3912090088e18c78ae\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:1f241c7770eee42efe8cf5f9fe3e34397dc9d5e6992f56d2762c9d53e65210b6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-install_only.tar.gz\", \"sha256:323905f4617760ec36bb0f33fc5491fa1f8aaa18438ad627aa8b553d3ce0239f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:a270221c9b4acacb9cd70006036859e713feed5a6d1a485a55082eada7bb5103\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:e75aaeaadd4a12ef7c1650e43da98c4814607a2027f0ed2a86a83edd32110248\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:d250d83ab84802d17e1b2a875448bc8b73e1554eed3fce4aad70f69b66081458\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-apple-darwin-debug-full.tar.zst\", \"sha256:f62d3035f48b0848032e4cdd01b663d2f2fafed35576dc1c88b702b0550711e5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-apple-darwin-freethreaded%2Bdebug-full.tar.zst\", \"sha256:386bb22b28355be2ed5bc6b5103917b3204ec72beca099653d0c98d17514a340\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:960589d9672f12b40dff6a44f22eb2f6725d89a6194c6cc128fe7a610ce0f971\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-apple-darwin-install_only.tar.gz\", \"sha256:f2e91a67c7513b46574a1d24018faf79e08bf25c5cf8d48bb9d49f946e736401\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:7127c66e6f3e43fd70bd87c72530caf76a7862a53e0136f428ebb0c224bc0d39\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:a91c8e63db72ead2dfe3d94536123c9f99958a8c3979afb9976b0758d393cb7f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst\", \"sha256:017d79c6cc42ba8d1ddf1320b1f102e83a5bb2ae7ac591f67aa8ea52f30dcbed\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-pc-windows-msvc-install_only.tar.gz\", \"sha256:a401068b61f0a229f99f255f73a444f65284f72bb1ed59f189d98185e7e41e61\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:361a49531a5d4bdc72bbccc81f7b5881b5eb751e3b47683b233cacae5aeeaeed\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:264b5c5a08cf05abcabb93cffa9ffb5ade0b715db1260b51263e71cf6a9684c5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:71ccff17601312304cd5e9c88be2d36a3158f7f2375988d784df938606f9f034\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:5942fb406a77d3d9305499443e53dbb1fcbd012c2666434bcecd0c4467c7e810\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:c4a44848e859a6ca4c5dc5d0720c9ae858d160201180953e4b30adeb1302efa3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:daee4fd6a65c7485f8e37c0fd91cba126d96687c5b94db30c741a1b6095ed50a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:c6135aa7e455cfc5b324c0cf620d789a96116123d9c30891917bab7ebce027d7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:641b2b30146a08feb76b767126b891326f7dc75d487c8443b0a3674da2f5df5a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:78b998af86f6d7d064533a749ee84a683a770786548ab8818ec93876b06a41a6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-debug-full.tar.zst\", \"sha256:4a376f1aae60158c274787d95dd8944beeb4358eb68fd041837284764dabf167\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:6c9df02d5bc0b5a389cab828a29a53fdcaac7805e451d5834d86dbb087fa8e56\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:212af714fbca97ec5796c5316dd592fa4e61cf60be4aef2093955a736cac85d5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:0153ce85a76241fb2c1f0ec931cecc2db24f5aea098e59e6b80ee126f2e0c745\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-install_only.tar.gz\", \"sha256:77d7029f0451fd93f791e756ad8f3cb7800b46ee0646230b674f54fc45f9e647\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:cb9cb5bef248a13e26fc4fd28b17abc88af5f5690872e392aa2ed0705b0fbfd5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:ecfdbd0e93077db61b24b542d8c5ceb991e1dd623bdeb7759f444553c5971fd4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-lto-full.tar.zst\", \"sha256:2c89dc8132d226a5574e478f03d85710b443a3bb8f055c03a9f1e04a9044bf7c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:0afd50a4f2ee258a96dbc7dd9191d5f8744e5a0bca4e609e5c0ded8a3d74d209\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:39515d5c1fd9f3ba1b9b4c43f46095f82fe548513ecf418bfc1bb8b44bdf1675\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:ea41bc736a83278cf69ce92a7e53c16509eba97a3ceb0f3db2f88ad2143810ef\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:ff0d208c7de1c875efd0cd8634259b629cbeda2e2a875c89cf584cbe2dedd0ba\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:ac0bd6fd4c7e749685b879488a849eacb14c0a38bd4b44d94c5057a981f1311c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-gnu-install_only.tar.gz\", \"sha256:70084ff730249607401c82ecfdfc74aab1d161afe76377d84920781998946da8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:7ced21526f62aad9768ad7e50f349f7f88bf9d539858438095473afabdd1849e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:5578c902af2ba02a997eee5570d82b1d158f374b909b6855bbfe9ad0198d45fa\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:47070e061f9d3282f0562e50a28d840c3a2c3fcdf6d9e96ae60e9efc373b6f95\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-debug-full.tar.zst\", \"sha256:3739331323a983ef9e184726a77b7d392fde931fe0b5bf330c884be81385d534\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:2271b9f94da8dff2ff3dab7b23dce293afdf1175fbff87504fc31537d6ed25ac\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:c26923671fb7a55818d1922af4e703875554fd09f29755f6d2242af69a11b4e6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:8f51d6bacdfad47cd2e1cf648c732bdbf66bca4040a429639dc5bae2d047368d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-install_only.tar.gz\", \"sha256:7ef2b8729d83e852ce66217e501525393c8788fd4d61037c9babd13f3544131b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:222f00f6f594d2d9036a425379061048f092c4c5d3606307bc0b61d3432bb2ea\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:4f3fdce31bbd154587bebd02fb5d3d4c68cb934434de3c8e71ec52f7829b17f4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-lto-full.tar.zst\", \"sha256:05c42fafc119a8fcede483d63bc89fa9564758ffcce26389a2324b9473ad9d85\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:7f6dada5faaadf0b63530d76a19634fdfae0732a51553cc37157afd4f3b43ce2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:70923d7c96d8c24d954f95df5d7e956dc5fabb2eb3bb7104453b766707106d68\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:14676d14e9620665b34a00ee9405821cec1065a0740d1cb09cf8438253d6735f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:7ed87dfadcb8ff7afdfd914b0f083905215d096ae640d15a3113e96f5c1f4340\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:722e34ea232dd2970cc7d30cbdeef7d0dea36e3fbd9eeb0d6daf91b03633bfd9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-gnu-install_only.tar.gz\", \"sha256:0c5e31f49654bb48cf57e18e82b317d978e7b5f5bfa1f2050227f673403e14df\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:9f324bb13243070e7874e21dabc1f6627e7f4cb4e263810b33751d969e924525\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:8dff32c84773888b783d2a5849397f99b9e03f074f1b3af9e7fe61696623d672\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:764cfca7cbae2784ef3568b1c28d21a3e14fa332dd856ea0afd0fa9d2c121454\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-debug-full.tar.zst\", \"sha256:0051e53a2d260399916ba5ab99427a52c9710ae7f7e661624c580fc7d663ccee\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:6a8f1c8305e959ff24ebde45fff2ef226eb38ed381ffe8a03537a2e85f4140f9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:8d91696384d992951e94e740956a9e98f577f785d5554d941241a68fa9c54d7c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:be6e51a0904e4a2502a30174ac1fc61abcc064cbcc68679470c110406401690a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-install_only.tar.gz\", \"sha256:024124a475851864525d45c40d9ac8cd625864d45bd391a7295c43717359072d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:e2969cb39eb8126f689b850585b88d292034443347458732f0a6e4a0a73cc1b1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:5a83a9fcfe7301ed5d6a23d0c15125ff851d8bcc33648c062c41bfb8fde5532c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-lto-full.tar.zst\", \"sha256:1d234f775f311d2b32cfbc2c1b201b489d5d5d20176d36e209d7fb06ff976b71\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:c81fddcb15339be28515e2ec698b8a2f35d583a6abf634ee017eed3ce3b844f3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:67792c577624419dc40ab2452d9ba414c0ba3ea46cd652f2f68f5933f90771a3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:4beac49dcbd80c323cb62de62484ae721658a71c0e7a15178d668d98e87d6743\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:8cf727b86ed45d0803617a843b6c0e532daf0ce27fbe0a783e1b01afc81397ae\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:505d120a7da2921d7dc20dac4b978ba2e9ac71fba8f6a8cb71d23dbe2dbbcef7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-gnu-install_only.tar.gz\", \"sha256:012d74010b61f004e991f23c1499d9c0d03bde2cab3d1f032f1af81a518fcd69\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:d14bcdf034e99965bd8c9e2e86b5e87270600455c9a77c386e7913712fca589b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:2d713d1ee5518363bd2a5da23ce5c08de3d2ed926ae9bd1d82992a9e12ac7216\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:174ffeda1ba7cc6fff6172a40fad6237567b2cf1856942559bd95db7e70b2388\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-debug-full.tar.zst\", \"sha256:74fb955ed3b2b573fc8eadf581fd1ad8d0642e1240caeff1789caccf9198b05e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:4b2ddbe61d8443f986abf60f50497e54b8f32431a5c7b946424c872d93f97132\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:782688cbcc2e9ec4977d00f6ef5b15860a8f426a051d3f577182d7f7fabee811\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:7f0b845c4eb5d16107df8ad657c0d7a2f4c5485b714daca2a7410973e58c95aa\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-install_only.tar.gz\", \"sha256:085927f6744bae43380a1b771653267ad2279f53c515684ca1562e845f001ff1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:5f4107afaf934ed6b52568aaf4fba43ea624687d136b82968c7036f8aee9d8b5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:fd00711dc6e8327c714164743b55e161c403eafc1e1f13a99ca926e77345d948\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-lto-full.tar.zst\", \"sha256:290703c5fe6757de0dcc4e6d8714a60aa28bc0a930397b5ce2893e2c52430d43\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:be2d6987c2171a21fedfeb1350822f63246c1b5606dbd22029e12f0785c1ddd2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:ae03a36cfd8ecfdf6e6d49c42fa606058453d0004b338263c5f4ba9e5025f046\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-apple-darwin-debug-full.tar.zst\", \"sha256:c2c0fe31b2ba2c48066847763a8aac2839ede63da3fc3a1ed998114a82cb4d07\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-apple-darwin-install_only.tar.gz\", \"sha256:a6cba6f41d00a07fe907beffc7d719afafd5ac9241abab15f5539d1d08c5a88a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:b4885039c9341dee671515a181a3db6cf16dbc5530f919f63dcaa8be4b8b0f43\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:1e3dbc151131e1707b602ec4d549019f2da299a3f87f4627e15b626d1a4b676d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:fb5f16dcbfb314064ccd9c0d5595acb9574601a3d7c829ba82a444eb29570dba\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:bb8e157a9fe5c8b35810f8673998130ea212b6d19bef6f66bd4801f0900af6b1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:9d37ddc3e3c93935d63f6ec373b6da4717a725eb105c073aaf0c1252c4011ade\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:44315d86dc93c78913f0fffa1f9103c2cb48ef003ed8f919f01d68f0c8e14403\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabi-debug-full.tar.zst\", \"sha256:559d024d6b4fd254ff3caf08f579e97a5589ac494f503b2a0d60fd45e671bfb9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabi-install_only.tar.gz\", \"sha256:8ed785a92647e24b2ff5ae1665ba96a33d7b3ea4be4a33ee1f24d516d148759f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz\", \"sha256:d17b0812320c5429b6c6b06a8ebaabdc3cca1f49c7b4defea60ee666074205dd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabi-lto-full.tar.zst\", \"sha256:a43fec14bf613bfcf70eb7fb9b851be37880e7ab0e4dc978532191ba6f7eb3d8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabi-noopt-full.tar.zst\", \"sha256:e2f7d87adcd11d539f7d949d7f4a9b8db48d81cb69307e4dd6f8565ffc1865ba\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabihf-debug-full.tar.zst\", \"sha256:6432a7708b254e4137c5309edd1788a0cf079704645a783c9468c8699c593101\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabihf-install_only.tar.gz\", \"sha256:d25ba140e3417c6f6b1522b3c5414559af3f236c435bc93e7c50d49fb6862bd3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz\", \"sha256:e462dd10dae601da9fea971bd5c1d8b305c2c4fb66ca4d5ff413b1879d08ac4b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabihf-lto-full.tar.zst\", \"sha256:ecd4fa0198d63d2ac08786097a98ba3035fab8fa2c4875785b4f461bde8a8145\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabihf-noopt-full.tar.zst\", \"sha256:105c56a463d6bdf23545130dd2b0c55af35dae643c8793bfe334874521b0aa76\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-i686-pc-windows-msvc-install_only.tar.gz\", \"sha256:0ceb24be141f3f10fd97822ef62055b602494bd91cacd2e64b951b1afff4435f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-i686-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:4875f430712f04a94f306a4d8bdca95b791c0f5b3bbb9fcee59eaf6562a6298a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-i686-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:f7e1b093fa262d7f77347d23a37d2509b7c0efd8557f759059ee07e9d1cc1943\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-ppc64le-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:0a5bcac14a060b62c5ed7b131d8aaa65c57b8035f80caf5025255529a8f08710\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-ppc64le-unknown-linux-gnu-install_only.tar.gz\", \"sha256:3b7bd053713d70deffb7fc9d391e99cb6d1ca112924f9968ff5137cb7b437136\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:3a416305ba0d4d2d0c3127845bf6135d727a6c3890820b5ca61441f6fd81676d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-ppc64le-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:2b8ff5a64dddd35176f25b9fb17a0a16d1cad32f4c39cf1c6f9a48288196db98\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-ppc64le-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:e8dac83951e2704a153d1a974664c8bc9bc8b79740a28b9fb94821c56b2b28d2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-riscv64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:d49cbe79e4814e1d21f034eddc4a8632c3a75a66cbf61266acb9dc5255cb320b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-riscv64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:9b78ccffedaf97fea3c1dd22eaae7e6679c1264173926befd936500e2355c2ae\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-riscv64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:6053e7f50f5acbac135ba69835e0bf65d2c8794ec64a8fb21d434930e126ffb4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-riscv64-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:ea07654215f8d81f6a1709b4e42ae5951401ff4ab205acc6dc78c8de10aecfd2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-riscv64-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:52b31119ebd1823cc541cf1d0edac9321102ec2a7ac0de5dd83a661495cf5de2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-s390x-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:f120a02cbe37cc4f188042739b6d988c12a014a92c5dc760335b3775e0ebb80d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-s390x-unknown-linux-gnu-install_only.tar.gz\", \"sha256:25d2b973d5fb670d4a6f50c80c72cf9b67952a4bc55b787075dcd00cdc5ae6a6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-s390x-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:7445c08ca60624f0b0785d845897e18b2cd7d087a6029d63ec65803c0647d05b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-s390x-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:7fb69011af1eacca0b5f5f6232ade20e6e244572e7cc8eb476f4ddf0528cf13e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-s390x-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:68e31d7dc4e138515c891ac6aade48ab183f8b40af592c01ec4e38ea3e395f5e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-apple-darwin-debug-full.tar.zst\", \"sha256:b169399a02736d6b977e7c49039a46de2edfc6e4005c4b4b60f8b4e7a04e41b2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-apple-darwin-install_only.tar.gz\", \"sha256:ecbde9952cf790b1a532303a60a71d2fe0f8e61b6569a3fcbb214154c37c0e06\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:7daa836b18e9b942779f35a92ed75973765d0cbb962e4606641c79ee6440e715\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:57683b3377b30e9333548a799ddc9169eba3327c5c3b7e5398566826a74807c2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-pc-windows-msvc-install_only.tar.gz\", \"sha256:e047cfe9ce478248b3605a754fd66ba08f91a11b51aba25498023d23a914520f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:5f11a34f02a60d3a012beaf2f4367a87d737bae49b52fe71a2389bdaa43afb8c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:fb34d054ba91d5820609eb7f8c6117154eab5169812fce11ccc72634b9aefe2d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:b3cb22ca087e949655491f67228df38a1eee9db8fbb530d6ff438038cae6d77a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:d2251aea8f4c8df45d04ed9462c1a121c923aac61c0c77da44758511c976bd4a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:efef0f804d57e47691320e99f103f202db59f1d947d9164ca2059d6e0d20d8f7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:ab3f97c19cc79c95d7e1f73817e377a2584a3102dc49f4dd29db42da508ffc60\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:d3874de5d44cd7fb5d7d6dd24308a5eb0501aac0a12f98a2e2977cdb9619f1a6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-debug-full.tar.zst\", \"sha256:1d18814d15c94e0a178e97acd6e55f9eba19f641dbc46bf35b36fc40bdbbf85f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-install_only.tar.gz\", \"sha256:4088a8b4d7063fdd81a12ab7dbda311e10b4b7536a73589d3f4b5412dda7873d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:d09cdd0b5d6723781ad1e3fd561a2587c79131a8a32af13f8140c4a495c00847\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:2fbc238a5108d4ef2f542beaa918c198851b380300a21135885320c18cdccb1f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-lto-full.tar.zst\", \"sha256:600d34779eb263642b5731363e68471ab6e9a6b07f87a21b04fe246ab05aaee2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:66ee1746949151ca581fd2648af1a0c41979a8958c988249f0b04388ab65df4a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:779bc1b15509799f73307219fc08a527591acd18134a57790158f1345e5c25e2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:0766115a0b376c40e656974c57059336bbf581fd51f6b256dbbce6a95c516d25\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-gnu-install_only.tar.gz\", \"sha256:46546e3055a82a4c2d7bde1ebab2ddfb4385801f99ac17575ec9a46d431064ac\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:c18cc6dbe7511d3e0aaf0db62cf9acbdc965f5597465bffd10594199c5d49b6f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:558e5279ccdd70ef4536809f65f240b9cbea8294277bff9964e31027bfb099cc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:15307722bd2ea8e0c7d4b0b4da0b1324e4c65dc4b9d0ae6a7066e2426d4046bd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-debug-full.tar.zst\", \"sha256:bb1d1773fc69ec578d439cfb07f0f29889ab8c11677a15feec9bc7fe8f207cd2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-install_only.tar.gz\", \"sha256:2724f2a4deeaf89652161491535b262128eb7e7cbdfc5a176f7db5f42ec091f5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:ac57db449393e21edb638a499ec287baaff9738a6fce669d862dfb2d0f8c336e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:fed3ce78675ca05d9c150922016ac5ec6eba9f5f181287a951dd70a4e4692c7b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-lto-full.tar.zst\", \"sha256:128caf162b917ad0dbc6d5ad7179968bccfdf5bcc2d40a3fff4f710888b0b8b5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:3df7b87cab4dff4fa47646a6b57c63da268595d478d31cd4742dc5baee9af91c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:d655223747b80654efabbb3563f2ef933068770c1f894e2f6d15712745cb92d2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:12a76e319ba14cb28f685fa23282382307199f36ad84d1ee5738b5c5b53e9cd2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-gnu-install_only.tar.gz\", \"sha256:dae20efff21f0abac3d4eb5fc1beb500a1452d182fb3ffbb9890525d941f3332\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:0e290d911132b85a0459feb5e1222189305fabd3dff1f93d5049973931ecdec7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:33e25966d7f721407ee878100375f86f90d6c47c1f8515bb6a79dce147fc340b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:fb126fedeb80767f320ce6bdaee537b42deb3544932f0d488f49a6668c94dcbd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-debug-full.tar.zst\", \"sha256:5abe82dffcbeea76d0aed7d82003edc36a2357714ddcfe769f63804c6ff17db7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-install_only.tar.gz\", \"sha256:a5268f432d6359a1a403ed367a838786d0fd067dcfc63b62e62c9d9cd1c70268\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:ac48d1714b5f4cddc894e74155c2b3f8816a0a8a45acddaaaf154e217b1e577e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:a082bf434ab1f2cdf21559e775447bcf1ba66d8adf8ca0f68e93548281d2b551\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-lto-full.tar.zst\", \"sha256:0b5496fce6ce8afe3f31a8e78b5f49f227f0518af92530f38423b7c20b662d50\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:a50395ca08bd46adce242548fabd190cd73ce882bcf77e739dedb75d5981cb6c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:9c4d1a40af3cc9f2294d9d8ec213859bcf0f30b80417b356895fc3090d32aa26\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:f16dd10a0abd668d0f1afdf804fbc44f00447cb8d4e3f4070df7db39cc6396b5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-gnu-install_only.tar.gz\", \"sha256:ecab2f675011f18cb51d5dd110b6016657874cd893c661406e1162b08fa7107e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:ed96e8c03a0c62d587aad6aa1faacdbab4495cc1806ad450c83a99059d37efd4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:d52fff372db6a9545c248d26f1475a238800b219c430a07a539200c7e9f6906a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:a7f90191e3781dd9db0994111452467c38126f91abcc64a29c8967f0bdebef51\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-debug-full.tar.zst\", \"sha256:5a97924d4e979aa24f7a16bcddded00561aedf87e617b66224ee79e690fa907e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-install_only.tar.gz\", \"sha256:4b2a44200561f7df7956ec2752bd6f8192b30051585572f46a33d0008862cae3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:af555665085721b76be7b8d61e4468d163a4792140e1e8602777bebdc2ad8af5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:11bd11a047e9f093e84354f7e57f05f5db967bb19f3eedd40c7c51c8d3195c44\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-lto-full.tar.zst\", \"sha256:412d77e44b8b2b3ab5e8ddeeb846abab04c3be4ccb99007ba9926757c3e9da5a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:91efe33ab8c7a70c681f999428f15d5a4ad42e3288f0ae104c6c0bd237e3bd6e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:44b41e3e98f1fe408cca1dbb5463cebc3ab4a21a80660c7edaff2f15fe20eb0d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/SHA256SUMS\", \"sha256:87482f19522742956f41753960813b664ec5d2ed5deadf9ac9df52e35cdde105\"]]}\n"
  },
  {
    "path": "testdata/standalone_python_index_20250828.json",
    "content": "{\"fetched\": 1756410972.792111, \"releases\": [[\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-apple-darwin-debug-full.tar.zst\", \"sha256:6b5e7c29d4e5a699462fa84c838d8bb3eaf03789025091213abab7983c5907e5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-apple-darwin-install_only.tar.gz\", \"sha256:e15d48951b0f21f92d99e84c2d3d0ab4b3db69d7d4c8cb52eb16a85f17cf8d61\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:f38f5fcbe39e657742e21a12c890f9f12d20d2c0eefaa2e6cd4a975f3f7f9dcd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:118b40f74789c322dd05eda06cae84a7fe28bf27fd70b722b63500781d1952f3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:77897dd2bef7ab973a25f4bfb5b28b2a21c1dd8de995f24d40e27d37f3a74d5c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:fc6af1f9fa05e5cd48741c703f53656b1f0e6320fd498fbb95856719ef2949f4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:76308719c53363488950927c1d7311ad09d15a495bc3ea0738f32fb0630c4729\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-aarch64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:d3af6d835d9c056c211653b422eb9df613fb90a6e13c335aa714d503709b3228\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabi-debug-full.tar.zst\", \"sha256:44876914d4c8474c0693f2b198dda6e6deb0af4364be00935c26e878922654d9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabi-install_only.tar.gz\", \"sha256:954543fb308006f04bff4974af20f56753b7fe1abe601269ed10843309f1108a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz\", \"sha256:e696aa719c8c0631ddb942502894529f530e7bbaaf94fa9ff8152b593779f972\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabi-lto-full.tar.zst\", \"sha256:070b336a88221dec9752e63114b6be432bdabd0cf8a612c6e432c8d881f613da\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabi-noopt-full.tar.zst\", \"sha256:4d6cf8fad5b259f04d4c6eb98b904270210ce0bd7681a97863cf707be95e0633\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabihf-debug-full.tar.zst\", \"sha256:749f9c3b3ade288b92a4050e25563e30bc22793327acf8969819f5c221b87992\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabihf-install_only.tar.gz\", \"sha256:66a147b8c51a8ec5ff26b3f64779a7c8a28358f64a1ef81cf274bed863771a9c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz\", \"sha256:d73cb27efeda5878660b5fbdc999dab6d175c0e7c8719f6407c9f898084e93e9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabihf-lto-full.tar.zst\", \"sha256:8b87b531174a097f974bf355238eb7a314cc3261c9cc91bad660caf713907040\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-armv7-unknown-linux-gnueabihf-noopt-full.tar.zst\", \"sha256:8eb43d825a6c249d046502fea146dd99f29a8654ed325ccf69b1d6a24bef76d9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-i686-pc-windows-msvc-install_only.tar.gz\", \"sha256:0250eb3c14e74879bded3143400c268e78b46f70dbf3c7e307d7806029bf39c7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-i686-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:1463aceb81085a5cef3d6fb7513bb4b34548941b79ba5e4d89e0a439193f8663\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-i686-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:0a4bebdb6340e79dbfe9093b7d52c86abb6b9bec0cf8ffad991ffc48abcc46b8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-ppc64le-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:e90ebb19f4a7eb2824885e8eff328e671beaf61f0fdface14af60f59fd759556\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-ppc64le-unknown-linux-gnu-install_only.tar.gz\", \"sha256:a0da3d9f9153a523be5225d8922675330e4866b904167db5ebe5d1ca203d52d7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:7828a0da33d1564e138c71c9a7d8fce18c7bef862ad10aa8fbb2a607a026e1e1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-ppc64le-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:5d3c87e0aa4d3ae6c97189619fc2024032379e2b171047b333a2a591ceeeea3a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-ppc64le-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:f409cde263169051e6f2bf848ffdfc3ba46220da4b4ab020da3fa53f2040ea51\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-riscv64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:46566bf3d29011dd50e4fcc726905568340f2ce7a64ba887581a9ceaec6aaeb4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-riscv64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:e575b75dcf2672d97c150d9d58d5177d8de0dd36b9628376586e0eee9fa7f83f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-riscv64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:0422bd3037d3f9e61377ccaf0b45bc444558387de1446d900c2a52c512ee6fd9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-riscv64-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:fdb551adce7e5690506e43edb8158209f244ccb66467d1e14748a62ab3268d47\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-riscv64-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:7c32e98bcf922d547d7317a312c62f610408780bfb050c63e668039a322c6233\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-s390x-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:b76f0efec27857353ee5cfd932c8be1cc4028cd093e097c0b5a70f24f685a9bc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-s390x-unknown-linux-gnu-install_only.tar.gz\", \"sha256:7ca911668434797866e079fb38e7271763b88225a5475937530aef649e4ab99e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-s390x-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:bfc51d57439be87b5cfdb9b849be22194c887c048e661d0fb3956b2071efa03e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-s390x-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:13b74902fad64af56b04c9c7a6b418ba925fc98cf021fd33fc0549323fb999bd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-s390x-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:b2015772b7ee404630ab6ba81deb088039a6506fe0bb4f1c2e87825fb440897b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-apple-darwin-debug-full.tar.zst\", \"sha256:240138dd668c385a7ec84141ae915a0fcbaa88a239c0671a9b48f58cefac7547\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-apple-darwin-install_only.tar.gz\", \"sha256:0229f4524603efdfde8f050bbf01ce040daa9eaa31ce6c3ce815783478b21aa5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:49b56e8e06479f2107a0539fead39f04dcf4354ba7f3444482d8bb50ab99d489\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:495a446704a4f63d9978e3c129c20da44ec0413ff1bfc5d033e3df63fa93df0c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-pc-windows-msvc-install_only.tar.gz\", \"sha256:a0c5d3ecaf8956bcf731c48ca69eaba2cc23836db9145cbae8d36a4108f914f2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:05bdc00aab4472b2334285ec70f10c1c908f2441660dfd766ca5d1fa83eca06e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:9b93db727eefc58a2a961d6bc724a8b768d28ba059fda296723d1daee381458a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:b382c072b333310910c6d44f68c5db603307d1118988e6feeafaa8be6d348d4c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:7ac2b25f02d5a6d3dcee6c7725c364bd048ebb3ed6768706a7d6e8466f551053\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:7dc5b808f8c33101e954c9ec10b6128bd81f659eb00ef1837ebf86e9bf922d65\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:8f669274b242eb3227c26a80abf4f605b5b65822cee10a541bdf27fe1ebda45a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:82c23eaff1e5350b500a01df615c6f231bf8a4d9128e60fd4601865483abb053\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-debug-full.tar.zst\", \"sha256:fb5d905cf98c606f849fd7f658491bc21b1a7351c472a3d2be38daac5f401abf\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-install_only.tar.gz\", \"sha256:e717e6429a0b10bcba6d24b881f2cead7df0f5b50165b9e24dce5398aa5f4103\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:c652f38bb3b24dc1c8e0a706d8ded2447ca234acdeeb01d307300f2c54107e91\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:d50821f155455c74a9f3d9a96d61ee01eb08ff89e2cfb8f2933e6ce30280d773\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-lto-full.tar.zst\", \"sha256:9d09d3097070fb38c055c812e2da43da4e99a13394f45bfc1b8df5f27160320c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:bad412518fd598e6a91e8cc314b86e59ddce1b4b3580d560fd7eeb39e5fa8721\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:b4fe263ac532f6a18dc9a66395ec91724360300a7d6ef2011937e1d1e022456f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:1495d6fb8e485df22e1a8948b38fa5d696608d7698bbf6b3caba9acb130eeec4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-gnu-install_only.tar.gz\", \"sha256:8abe99a9d122c2244e6babe0f46e077de23c8a11ff11bd5240209596a3bb5193\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:0d1387fc0366db3fd0b309841d3fbb5084cb576983f7bb7c4ee02f1574baedeb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:9b733175b730fa0a8159ae324d8571fba40b71334b3588a292a5717786b45791\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:0dfbc47a4a168ef46a2c652890dfa133577338556c371f9353577b2f5b67ccc5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-debug-full.tar.zst\", \"sha256:c66f400af6fb38c8938f89c063b3548cef0507d5ed48ac7cbbd03ee1740b4585\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-install_only.tar.gz\", \"sha256:9d9d8995ac62d160f33ae9f94027b3d1752c7786cc3ab673847081bf6ebc5e9c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:d83c2d448eddab7172a73182233149016985ddb72374b24c3e0d56996ede80f1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:3add84d8379c6e520db45930cc0aab55db7d47c73eea425ed30f62c51bfc5269\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-lto-full.tar.zst\", \"sha256:45e162f9c2f53dbf0f1a8fe746c3c8b5c4399f80d26b5a2696548d5adab0a0d2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:7f5ff8aeccefc3f87c59897ff2b6659f3c8394f3ad806037a0f2937970621e3a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v2-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:3e24f85804c0e5197ad7d79287352ae63f7b96ace8705f01e038c36ab1a7434f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:4db7c3b6c8f1717f164676662f5b94fb88fbad85d6c7017d229c2a051ad61dcc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-gnu-install_only.tar.gz\", \"sha256:140572f7593550e7fc032bb87cfaf0b00e554fd93eb212c57701934ba9c8d2ff\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:681fa31e6c14fcf315bd48bd845cd68101ec90957fbf4a2018ae2313a27648b1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:d7e8b90648fd735e194f199017fc7f81ae374b788dc78358c3537cf2780567cf\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:1d9d586fa892f06df82ad860a349f3ce1c83a7429d42074d4a180635fae02d32\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-debug-full.tar.zst\", \"sha256:f75e359f94e2245bef672fda83ac45a979c75e50187fe0d19d2139a7c22469b0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-install_only.tar.gz\", \"sha256:38e4dc14a3d238958877645b68c2dfe5cd88ac8a4e0de50d495835a8c79ddaf0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:3bca2c8f36acd45c4d357716b9d9b84dc1e1fb938b7f3a91984e35772b779106\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:464cad4772b63f00538654f587fcf4d8ac807099b80772f0e9052559aadd610e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-lto-full.tar.zst\", \"sha256:1a2e26c8af794defdfca168c9324b60fff4d7459e1c6cdee8a9454104e2ab712\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:fa455c494ef56bfabb8aa81de1089a88c14c90cc5dc0c2791c5eaaae95c2deef\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v3-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:06a46801926e90f8c93557b6d4773644f70f37b62b8d05ba3e38683dda6c6fad\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:9a9be19e6402d731df2f4cc90bfbe9e0566f3dedc219f6300a311f6c6487ea27\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-gnu-install_only.tar.gz\", \"sha256:39ec935aca22d78b3f7113d64551d7757024455400e67d3258c76f680e65a72f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:a635e9af57b90e571b6f3e9c75d06b8e051bc8a4a1f82c454982fdd4d092caa0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:809a532c3828849a4addd5b572b377d691eb37fb6cf1e2d0c2095c0813aa029a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:7fd66f342e2b75954bd11010d1a1cba19b8add90db8065498390b8a03927f118\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-debug-full.tar.zst\", \"sha256:a1fce517ce62a5d629913fe7727849fe90de17563bdc16bd6979bb2c0ba4d19c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-install_only.tar.gz\", \"sha256:3e87c42524f8e6e1703751bcb6e278fd71498e9bfb1d191f1c8b5b28b7d48032\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:7632b2190781f4dc92e22d3e348d43079c0e3a0f85b7461ae247f53fb9806283\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:19c6949ead1af5003a336f07cbff25d58150fc0954c4a4c7e998e0052cb363bd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-lto-full.tar.zst\", \"sha256:e95e371eb54a097d09cd4f4575558399af9ff5e850f8c049941891b787c46378\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:2d834db5e7965618dd574f6a5fcb9ec978de75f1e518d249c05217ff2a3dcba8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.10.18%2B20250818-x86_64_v4-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:4fb5e0510fd299990e416c4330230ce5b5f871b8863859196a2fb55797ff811c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-apple-darwin-debug-full.tar.zst\", \"sha256:153b93aab639aa8440bb7e60acf0951ee0d8d329c1fd9df630fd486226d62402\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-apple-darwin-install_only.tar.gz\", \"sha256:317fda280cb51852a346da5f595131fcd3e0dfda421b3983f675cb1994159838\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:e6015b4a1e74d1bcc70f6117ca22d5c5c778b393e4b9d6fef1000fc3f016b6bc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:3bd5e4d9f3da431dfc014a73e2d86375d7c91aa8fa3a8492e20b8a38f5762448\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-pc-windows-msvc-install_only.tar.gz\", \"sha256:61187f99352429e8f87fb305a8b995c8cf5b70509c59b409dc80787148920e66\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:8e059bd27bfc1638cf49d7b0701f0e695403b3dfeaf077353807680dddd28bf9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:b7fcba65043af0f2a470d62a74012da21d6e89d71fbe45a1a71056f70d149224\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:e5cfcb8bd56616423a4b02f4cef31f146917eacabdee902dff02a4efd3b89c59\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:c04b98b4332ea0d8be0222e7ca7959e1398c6ebf7f2102b47f9e4fb85f841f59\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:a82a9e73747188e3408ffa416a294256f0bf1d712b3df202b9fe2bb68d3faa6d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-aarch64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:9eead91f702a907b4bd124b2337f8060f77c16a459f8efbd5fb37db751863461\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabi-debug-full.tar.zst\", \"sha256:024b0ef24c2affb1080abf1fd5e9f51e4bed33aa61d32dcd466133b9a4a20652\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabi-install_only.tar.gz\", \"sha256:2706c023ef78dea8ad9e7348525d9bd7a74789c67159c83bb4f7dc00c4f4d684\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz\", \"sha256:c9c8a2cba2667ec19c0db88a5d7bd3a7647a719656c4ca874215a9f984dd00e8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabi-lto-full.tar.zst\", \"sha256:601f89f632e39dc33fc05f6175d1d86d822d446fc4c4fcdb008318deab5ef90b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabi-noopt-full.tar.zst\", \"sha256:ccc52c7bb15c2537fa142fd23b28e2055ec595e686b16463566a15686ffbe2e9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabihf-debug-full.tar.zst\", \"sha256:a8aa258c4e891d5ce9235f8e631ed6f103aea71aabc473655a8c39ba60f295a6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabihf-install_only.tar.gz\", \"sha256:a649cd2656cae6b9ed00263be33bb09170a3e1bbe9087cc642d011e46567e327\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz\", \"sha256:baec13fd3bca3722d906a38850737432ed8f8fa9b2f43cadfcc7faed4be08912\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabihf-lto-full.tar.zst\", \"sha256:a9bfb16d21626f87f41845031a8893f02f224ddbd53ecd801eec638e82b02b36\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-armv7-unknown-linux-gnueabihf-noopt-full.tar.zst\", \"sha256:02d4cef8fce6f3b14fdf080f24a1732ae7519786e55b411cc552118868cc9918\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-i686-pc-windows-msvc-install_only.tar.gz\", \"sha256:e3a44cbe769c3127dea917985ae73667285e2560d382e4619e89f5f47b456c65\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-i686-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:ca5d9dcfac27e47aed9bb29d9fcc4cc60ece9d7d5cf4456c41bdfb4cb114ea8a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-i686-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:059cd3204e788c1b3ac19a1a9c641cd81d0ca4ff9b7294aa1b19bcac04c31dcf\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-ppc64le-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:d766a47d9ee3fdfd7965aa22aac0b3e189c649201077169d245401ee5dcdae6a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-ppc64le-unknown-linux-gnu-install_only.tar.gz\", \"sha256:dca9fb36c4c79fee1cdd8eb6557791a49cd40728b916dacf4780d2e9d9921cd5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:10179aeeb9750a6c510cbe745cc8bb153e3791410da1160e42c1bec774fdbb9d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-ppc64le-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:c14c39362984cebff8700228d4f1f60620baf29465991d6625456833fc758d6a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-ppc64le-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:f572b88d0107debf2785b04b08cf9bd8098f2be9b92bc79e7f03cd81b9a1c955\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-riscv64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:68b3e0d1effaf23590efa9686333182b4a3011b783ef77550b9903a9372410cd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-riscv64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:31e16f87484aa6f870ac3fdc4bf17b454bc796f4ed5559b023b725b6ce2ce161\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-riscv64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:8260dffbb7807600762b2cceab15c60507933df1193510caa37adfeaad6db5df\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-riscv64-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:0184ed657ce75dcf1f6520943016ec900f4b4f49b2cc59e00a23a5f3e87aa44b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-riscv64-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:f357caadeb586fcbb9a92a1f712ab68987c0ea858a0b73c9b1371b0f741b22ec\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-s390x-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:901dc1ba24af638e172935f1b27d2e2d488fadc0e8777a76521e1651373df9db\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-s390x-unknown-linux-gnu-install_only.tar.gz\", \"sha256:325ba5fb7c6a435ed67f02578e504648985365444e41306c7ee9de6c02a64c32\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-s390x-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:8d07af60762fa18b1aa4c4fa562360ad0e30b0d1c9695e7355a6e01e87645bca\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-s390x-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:177da773fdd10dd37edf167c4ee130427195e369c2f6e923ce4246f060f018c8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-s390x-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:6569f04df391a0b8225ec93a5a4f092d3876848042414ea483b716c932e61456\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-apple-darwin-debug-full.tar.zst\", \"sha256:80e646a23765b96234daa509403c489bd1ddac3d3f06ecf43eff564b00a0dea4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-apple-darwin-install_only.tar.gz\", \"sha256:eb5feaf842207e374791ba5652621de61d55dacbdf765ae97f28630b33cca514\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:e494d718ea2b98b720f15f1e150ec5d440cf26efc36c38cac1203b173608efab\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:50ea66f34549ba1a641016076b6a86907f2f231405f425db6be5ab3e7869f5b7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-pc-windows-msvc-install_only.tar.gz\", \"sha256:008bab1b41dd88a831477af3deb3b10f056f02e3db8313f506e21b77ff2ae660\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:7c5ef9e80b5a1f1b69eff0761b98d9b9892d6bba317a0dbf92e8259ea939a2e9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:808dfce63127ab1da245cc9b152d1b66e9f333df55ef07484c690c3465133a34\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:1399b0cbfeb55fb0b0c8f18012b5430d54088786075eaf00d5f8b60806019e9c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:b3d07471abdf1b3d2867dd44f095c891fb072bab5667b9322355546f9f9c5dda\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:22232b7e892726fbf898e449cdae9ccfabf080319655575a8c5e54a39b553c96\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:fde49f6369f4d36a0d6aaedde461c609d280a325f794301ec5fb815f497f5057\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:16e30d22e0bd72f1c9152ecdd004a0cf03e6734dfb402492558d6e9c467c15f8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-debug-full.tar.zst\", \"sha256:ae1d4c8b35403e306bdec404a2330d9bd1f223c2d160b0c597d4fcfa338589ec\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-install_only.tar.gz\", \"sha256:46d727605a15b4da7162187ac2a0fcc7ea87c2f2be9407e2b38b0dcd001dc021\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:096f2705757d21d058643e46666ec56abcf35ed48d18860c88c9bc836d6cb051\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:57fb77244ed3ec848813acda956eba053ce48bdce65d7d9e1459ef131ba5ab5b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-lto-full.tar.zst\", \"sha256:4cb61b1740d9f6d2dcfa4a0dfb72918f11a047b8a794a4823873baec3b9e0bf5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:ce02f93a1d7c2b35a9090fa175842bd41df52a30e0e0009fe8e238150141b549\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:4535a00fd497a2b527d9773eaa38f3801ffa687452c12d4a1a453d210ec8f695\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:70a2641eca9899265010a19e371c6254827f7feedd020c5c7d60da9c8279aafd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-gnu-install_only.tar.gz\", \"sha256:e5555d98172df323b8068af9048f8504a3e511962f93a35933d6633df101aab4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:3a7c4a1b27e6f537fa8804f1d77de0d1e45d32ae75e32c6155737aab529aa42e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:2fd1cc28ea73dbf45887bffc6d99ace1f30ac1db5910b2aeed020bd606f22374\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:47f432c417ac9671c6a1251505d6f1af4207dee027c6978840f8f40cb75c7491\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-debug-full.tar.zst\", \"sha256:5cba599b5acf33a9995c61a1e6f74a52616be2bfa80b5e41a80d03d158cc49c0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-install_only.tar.gz\", \"sha256:93ffa2abe5970da46eb1b502c90f52b6f25ff300f937cebaf5b347d0bd43ad88\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:17f10aba81aff77ffcd55c14ba2662240fcd219d6c27a977bf8b1f7dff90938e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:58974bfdb3626e8286aded3e8b3aad396f9f673b56362a83648b53a025dd12ae\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-lto-full.tar.zst\", \"sha256:46eb3121c14e89d0463a689f810cbe5f063468ae0b1b48ab495a80af5c509ff5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:37d0a1892d6f0a8ed3adba378bd9de3ddf3a4aeb97bc6e1a6b06a8e048473d5b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v2-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:d4d8e33d7c0b95ff1768e6ab5e98c610ed39e59ccf0627516ee4ce7f847efa67\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:b573dde17f4b14917869523812a1fd726c1289c3fe64786fa707486a23515950\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-gnu-install_only.tar.gz\", \"sha256:34796680dc1f421362d5bfd470657750934578f0718e938ee72ebf20771971a4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:f53abb43d7afb425f21c17a545355a8567714b50011c6068e595abbcc43f6038\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:08c981c559326a00cb6852150490ec33c652dc78f19e9ff0b2473ed276d7ec94\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:c9ce0495f85f163bd8107c55ac2a4dc0dbda939d826df9e601be0b007899511a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-debug-full.tar.zst\", \"sha256:c5632aa794079af22d93d681ff71bae2a9a4f35b54beca09b7bc618272e66a4c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-install_only.tar.gz\", \"sha256:9c80a62fafeeb6f4011331d4dc0cb641c841bf99ec7f35b3d8d3847e69dc1289\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:f20c286f6365a43a814994c38409d5628d847b8e8e29a55f8774d0f9a376e7b2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:8dc2a80915f0d8270b7bc0e11e0eb9fed316da416e84c02221880732df6435dd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-lto-full.tar.zst\", \"sha256:49d3c4816c470681120ee65c2c6400d874b103ffca16a49dbadb42eec24a2cae\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:9f412a22730a02735f3e42c686325ca1f71a6b938cde5f6196c1daf44dadf449\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v3-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:4fd7981fc378f75eb1fec0762f9d0fabfc0791a2b385f400e867465c474db867\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:d7f26d46393a9805f96224d0dcf6985cbda00a17985b8483f19f426be4e3b754\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-gnu-install_only.tar.gz\", \"sha256:34c66a0f3344011f1eb1d026c4057fba02aada10b1ea11f6ef2f34ea61cec65f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:348cd5c8292aaa448a177c048b0bc48c9c073f1bffeea0998eef5e363fa19564\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:8b380dd1112dc300791f890bf7af4bea0a52bad9ba61f515e7497d660684b787\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:97966ec2fa90953ff90417101036174bb8afb622d4e686e0457721333fcf2068\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-debug-full.tar.zst\", \"sha256:abebc1473c9fba1150b28a74a9df00786d592ec79ca9cf2f791e1612e4cfb2f4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-install_only.tar.gz\", \"sha256:a3442cf2af7822f67ce99a83d1ca83f4aa21ecaaa0cd613ae0461fdd54f2c315\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:cde91b08e1e4307a77cd95ff1da8318ae9562994d3c747f3feae7be42866d260\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:545163f4071e01076c306d84bf9dbf7656c28c14ac4bea1841ed25a5812c6a98\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-lto-full.tar.zst\", \"sha256:5c46a03537f6ad8727cc0a671d1a810b3ad6483c0180778596e57a0baa419d61\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:ede5dbb27f5868af01df76b0ed18fa5ac7d02dc0453d18ac4119968fa3ec11a3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.11.13%2B20250818-x86_64_v4-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:b02696f917d84fbac4b8b31425db452cf67bd792fa546a17db3f22dd9234318a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-apple-darwin-debug-full.tar.zst\", \"sha256:4b8c1fc6e9cf32bb5a2d95d25db58fc76cc7dcd2b3f74a41b223b97fce85f329\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-apple-darwin-install_only.tar.gz\", \"sha256:fabb5fd4de54c68ce7e70d19fb08127549da5787cd38a34d00000749f4fde478\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:bbf0c85d09a8173e50d18a0198f14d1de91eab17a593ccf9445f214fb0555547\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:29fb93c5166340d0d68a77f3aef2bdb8fd65a988e84746ce69d8cd5ec6b57a27\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-pc-windows-msvc-install_only.tar.gz\", \"sha256:6af9d77e969e31d2b68e78fb431e59553a3cf34c9e1c5cee6ed4ce2c3d63974d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:c2fd87e1039048e8e3045a4b949f0b9a8e5a382d192b2cc7de138eebbb235e44\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:af4c20f18f254d37ade9ba31419510bb2b66e4a0b51f4a17277b5f581f82e7d1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:e8a19eeda0a9b350cd1d23e1f334c4638a19c76388135ac09ac1ce164b050efb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:b3df3317e101cadcc56f08912fe0f68c5fbe1649a035585d650891ecd60a7d0a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:f3dbedb0819ddd8c16f6f3cc9671874a9a35342d7d3d7ba6ecbaa5ed655259ef\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-aarch64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:1b78a02ea8ab445b99570577fa8888146ebb9f3c5db1e2f69c290a8b14b6dd4b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabi-debug-full.tar.zst\", \"sha256:9c0d24e1a34076511ddc6a0596144f9acb1e3456e5a91d064b9abb90887fef16\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabi-install_only.tar.gz\", \"sha256:888c74953946094f1eed38eee443aec70f066ef01ce1503912c126f11ca9ebbe\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz\", \"sha256:7de3c0e4b98331bdab777f20f25557f33134bf35e5af5bc1a30ad889ee47b30b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabi-lto-full.tar.zst\", \"sha256:a38e201fa00fbae323bb8dc45f06d8b1236ea97cb73691d1cb3c5c76452a04df\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabi-noopt-full.tar.zst\", \"sha256:fe60dc7c6d4eb52e4ceb4e9336c447f49e0eeec0b99581a70b95e2718b808588\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabihf-debug-full.tar.zst\", \"sha256:0bb9a88a97c0b2fd9afd8f45e611a4054a12de9d9afa5754377f2ec8c47055a1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabihf-install_only.tar.gz\", \"sha256:60720ad0301206a8ac7f02fd9a8c2f391da604e5be7360d8419a0caa121a061e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz\", \"sha256:f1050c822b878818728d96f125a77dd6c66352f2658c94bbc34a016158f9d527\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabihf-lto-full.tar.zst\", \"sha256:d8cbbf4878b557dc180596208420fffd0e80a1875ee53079cf35c4ffa6946f9e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-armv7-unknown-linux-gnueabihf-noopt-full.tar.zst\", \"sha256:4abc93fcc85410e6369fb55f03c31b01f323832a8b9e67afe9e0270ac19d27c9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-i686-pc-windows-msvc-install_only.tar.gz\", \"sha256:26d15598858c657644c84cc80bc61af3d8ebcbf40700a5576ed12b17086e0fa1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-i686-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:c015bd9455ab2ef8fc3ba1d8ef3d346d84699440f305798177a2e7a64de13f2c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-i686-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:eff3b14eda764b4f94aad57daa833201c5e2a01fe693adf86ae60c42f6c5250d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-ppc64le-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:676400f7fd07bdaef0f100197316657a9e8ef20f30fe3151ec510c61c72c7d17\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-ppc64le-unknown-linux-gnu-install_only.tar.gz\", \"sha256:f366d3b811ba88f0426d4657d06bd1993a49524b8e83506afee56274550a3c6e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:e60071ce581eae85fa159d9991e0a2f05781deb6780ac7f0f895cf28b020ca93\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-ppc64le-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:3448f02bf82097a4186b45fa60324a6677f39b809fac051820012fe824db89dc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-ppc64le-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:c00b923eaa24cb3990dad56938e6d6bc67bd082a3ff451b9afa22cfe80cab4cb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-riscv64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:92c7a787b9164554a65d7bcfe337bf5bcd44d2d898487f1ab9b0f29f2146828c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-riscv64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:7ee5ec8d5feb24152a7413f1090f6651cca60e2af2d4a748e9f28d041c4695ca\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-riscv64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:fd16bbbd6c479d7879c55890f1df1a028b62106031bc4b69ce17ff220a523813\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-riscv64-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:7d7fe2aff1818582976177ce6891581c4d68ca85799e822a8dd9d7f846eb574b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-riscv64-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:fbf455a013b65190a1c762e3fe155a304d00212867faad29564a9d87ff4d1446\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-s390x-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:aaaa937b9763c3952f4748e2cc32ea7768af94aef5132c8d4ca8a4ae4f5dc85d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-s390x-unknown-linux-gnu-install_only.tar.gz\", \"sha256:c7fffe139b878a746b4df1b7c0c53a9c04565d72491fbddef0f06050575e1953\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-s390x-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:ae2ad403e316834cfeda9f6517e0217833d369ec91f978fa90903104d7ac10be\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-s390x-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:37ba137dc1a672e6e5278f6fcd70602042dd90cd97b66f144c9c701694b15530\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-s390x-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:dd38dc551f3dcb51ecbc6b586ee8665dda16c12c44edb6d4e758f8b33b3865bb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-apple-darwin-debug-full.tar.zst\", \"sha256:1ee397343f8de83c6d0e4fe8ebb1ba7333de9f7efa200e2f4fb3ceb197a402fc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-apple-darwin-install_only.tar.gz\", \"sha256:896add7763faa8012ba6a37346718b32ed0ac041230979cfe8fc802fbb0daeef\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:296af6b9dd16f16dca2503a9a1cfc8593e4cd79ed19ee20cb2557da0912cf6b2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:330455827333437f47e148e0a92c3eb65d13604426a3e056f6f867875e5a9ba0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-pc-windows-msvc-install_only.tar.gz\", \"sha256:a6bc8c4658a758ec0c111b5f887f80595943fd84f28bbbaea6e7e30c7815dd26\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:9624883ba5ad269282028e17db800f7ddfa8f57007742a868c2acab0eb3c91c2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:d37038d1476a11dcdcdfdc46eeff2198e617d5d56112a3dc99179192c2aaa35d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:aae842cc66597e5ea43c62dd55dd9e34e9ef73dcae7f8be4ebfd69c1aeeaa9c6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:98229938166f51deff81b00d71455fac84a57290b71089bd5fe673738557f053\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:b5a4f189f25cbacba0f76c9bd6f3ea8c35d2064068aa74ccbb6863068caababd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:7702c1cc77708922c8c0a0b4e19ddc6ab0b38dc0375eca434a10d23956a490be\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:cfc80e799c177f336867f35d513f7b9e1015771cad2aa028422721d6d67691e6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-debug-full.tar.zst\", \"sha256:6fb9648f858b32f968828cfd49a18afadeb755e73654d8bfe2b800a1b972ca1d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-install_only.tar.gz\", \"sha256:b7c463ead2f602726eaaf6bd549bb19b89571c1c2309af5ee665d51983c22377\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:d88201548c561f347b5b9206885efaeb06fcb4ddc39968c8b872ae0f8c92c9f8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:4930aa7e8b3ab662db187bf9a0a62be9c772c9f4ffd771afa7c7f7933d02ac22\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-lto-full.tar.zst\", \"sha256:91059889754bf42493d526ef459d3201c59211e04cedfa9e4c3cc28537b766c8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:bd570be19f1670d45e3440ee77a904d0fb1b85e72c8ada1cd0e3b1e62df8f025\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:f4e0d699059ba36f24f11f39f434c47a19536b9d89edc8079b462aa40d3b7f3e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:1aa352bd9fe78f3ad34554eb2d7cfeaf2038f14ac9111d441d9bdd32e8fd5f64\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-gnu-install_only.tar.gz\", \"sha256:b76d60c2207dc86648bd6b4c1f6f4a8c8e74f2593c63c3bcac8c2a6b172b312c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:a1fb9e8f108ab31ca72ed488e79370873721a2a4fe7e625b07964c380721a87f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:78d1b479d756b61c43f75c57d9839ab478a10cd5e00ee2d903eba8ef57396363\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:62f66df2603dcb61769e2e9dd2a8a75e0b82ee8ff2002a4624d8e1207e0b5ca5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-debug-full.tar.zst\", \"sha256:b69a6d71624ecf8fdd076633ebbef8d451771fd4e9b92a6134ea352a94f79f38\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-install_only.tar.gz\", \"sha256:7e39780613300214b6708b5a550a20a8057521d0f678d69d7dd0b44593991ff1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:160b53af128dfad058d69562e9f1b0a46391c11052d7e8ab5ee63c4cf589ec4d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:3ab2019730d2782dc8e2de243b630c0ed1cb31741cde722302fb06043539724f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-lto-full.tar.zst\", \"sha256:00f452d4f632dfc197cb71247901c4c022ace1db02c2d38a4e8acba59d95817c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:098d43b7c6fd29df8a053e9f732b3861eccc7497fe3422a776ad4f19e665d502\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v2-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:18f58ed8674480b7ee0c0cf461ccb83a61f4c5240e4842168e229c0191912e43\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:40c2b32e2dc7e8cc4a24cf6e742ac992266e692ceff09665fcaf44de49793a5e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-gnu-install_only.tar.gz\", \"sha256:cb160f93bad00019ff873775a336855794e0f2f02b68352da907280cfe6939e9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:7a63b513786912dec54a5f3363e5f53d31ff0d64917d57bebf1d613549d57a28\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:233d7ff666eb6b33299b55bb5c129ed89e992f8c0ac7ea544c5c2d0cf89920ee\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:6fb2d19ce491bd989c417722c565822a9c49e3ec78970347c50920bc4d7e5766\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-debug-full.tar.zst\", \"sha256:7c4d97c66066dbd2b94f2b6dfc279f1536d4ca03b3bf20b9284621f31e95bd62\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-install_only.tar.gz\", \"sha256:d46c6e561c1473329c117606e79f86205701e072600fcbb0217cbfd8005adbe4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:b798bd625469d821de501ab5efa81d8a68ee183565982e9d4f74d4758872c1e2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:d25e76bb34cb9164d20338e845f6aff970643072b9c0c80cfce88a0fa0f97fdc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-lto-full.tar.zst\", \"sha256:f8b23b9b47343fffbedee24bb1fc40d1f0bb81ebf04253ba0bdf4c486d7726b0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:2470bd27e25a826cf5b2642ab1d2e0a7536d3cbfc6a3c3ed0a81675528313d13\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v3-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:198baae8cb78527da65d975b862ce426416a888b9510b3edc2e1c60c79cd9d35\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:81953a427a76fa18c67b987569e99dbd87f0a74c54f4fbb5855bd4f13fcf3ee9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-gnu-install_only.tar.gz\", \"sha256:233f725edb49e5226fdfbb6423ab71b33eee48e0e26da2b4559911799d6a7921\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:560547b4b9db8a22bbe96f2a8ad69ba17752786e0f6820da75e928babb0a1d5d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:a6db36c5d36ac37f6bba36bd559785b1f24f8f3131f2e4dcd55bea77f728ae9a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:78af33974a7fab5ab025ad014f304a2e77c74ca88d3e2d11e032c7dc7ce63d7f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-debug-full.tar.zst\", \"sha256:fe57b8c68b91854ce2d916ac118c7389540cef1c17503d725d34af45b147a7b3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-install_only.tar.gz\", \"sha256:cbbb3f296435adae786bb016389be15e3fc9ff9986f72be540b6abd5aaf6ac29\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:93839c39865588ba431af8783691595d7c6e5b26629c42256ae3c7a195b21409\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:ff53702f9e014389e02387b0b1a03752af32fc01f9dd75ebd5b1340d79b406e0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-lto-full.tar.zst\", \"sha256:a0fa0931128154f562bfbc28da28a46e8fd3962683c0ff0ce40ce98e5f762fe1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:4e5e45daa9770c7c5239b8af89878c46070a259a218e9e92a297801b3a4aff4f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.12.11%2B20250818-x86_64_v4-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:549ca7aa119b60ee373544b3307586a7e2ad39e34b0df929912afb112f1accf1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-apple-darwin-debug-full.tar.zst\", \"sha256:de58d40c2a4902c266bb94fad29625d5e8f01b5b6241d77c59154481e1e00893\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-apple-darwin-freethreaded%2Bdebug-full.tar.zst\", \"sha256:ac5ec6375c4cdf5818ef0dd186ddb6bdf19419ca2be6c3348cfeb54c6fc17397\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:8fd44f324a4e91c2b91458b36c2720e57b94a3716ad0585766a4520a2582ea08\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-apple-darwin-install_only.tar.gz\", \"sha256:4774f275706f3e250bad636d7cc6bf0e43c88611b7e4ea0418a631e743a3d5d5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:024a3a1c95f171e97a4eaa6d2d289baf6802b72e4767023e3d9e4fa246be11bb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:3f63f4370a1d59d3c6856ac5315473f8d265589d13d783e8f54816bafb8470fd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst\", \"sha256:392c8acc39129abe3b1ba96c486cade638d4db67e3ad5fba7c0533774ebe8f74\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-pc-windows-msvc-install_only.tar.gz\", \"sha256:ad7ee2718a8e3576751bcfd3e4c66315a7ec3cf537485165a5e53a5c4bc1fc80\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:76ff47b0cc0ae364f2f93b00f41abd75a41d8107b79641a73d342308c3d57318\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:06e817fddb9df851639b18595da34f7fbbf2ec0b67708ac2edf1f324eff25786\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:f3d5eed0764f2a93d54044d6cb1ce0fb83b30894fb68e33be9e1d2f057dba263\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:6477a4c7c525b6fa9763b8f0ba0934562e1c2f5bc471d8950d7166f3380ddaaa\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:c7d6c9ff4ba750ecf66514eb01b14b354057fa3e2e52c5649177ded5814754e2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:cea4f6769a84c7ca79a167b40a2fd06dbefc8cec9e36195d3ffeaf992ab581ba\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:6724fd90a45d34bce09bce319ea330a1485735270da620a4310366d79a0577d8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-aarch64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:d9041f00a86ca28de0464bba8838263aa7e662ce31cbf37e71ddc76940a41528\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-debug-full.tar.zst\", \"sha256:7d5bccb1f7d70da1fb2d98921bd4a5995d91f4845b9f50e039bc3ef734dd7795\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-freethreaded%2Bdebug-full.tar.zst\", \"sha256:9191bc7579bc04fe8416b04222c854aca6ce89b25aa4120b2f6f353e37990467\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-freethreaded%2Blto-full.tar.zst\", \"sha256:b544da0e90faea1a2fc5c0fb0dbd49910342cc506fc1e9982f169f970e291e07\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:d5f40b95104cde0e61404c7c1441ca20ed36cafe8571c92bf4733c68b727effc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-install_only.tar.gz\", \"sha256:0a5be388174cdd5001dc5145f8eaa39588d1d63957ee46cf95dbdf51f1ed1367\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz\", \"sha256:2b410948bbe552ecd9a39ff562cc148fa448956d2d0eb2cebff85ca3af6546e4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-lto-full.tar.zst\", \"sha256:a5314aeb37f83e16f1e5afe1c3de1bc0bb0050860d4ea9aef50a187e12e6fc56\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabi-noopt-full.tar.zst\", \"sha256:0d16385f4f734a01182295f7fee08cfc5341558e4f8c4534d17a88936d5622dc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-debug-full.tar.zst\", \"sha256:c0c3c932b8a564740bf6f18d760e522c4c1934f76721538cbffe2fb7e43d3c59\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-freethreaded%2Bdebug-full.tar.zst\", \"sha256:87b1c9a39697e5f689dc4d033497d4eb61e4a22fd76a55e5ddb864f8f64d8bb7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-freethreaded%2Blto-full.tar.zst\", \"sha256:8721f2884e32f84453c48e1cf6e7338e6fc23e222af7eb59f28a662b600c12c3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:8b65603be59b3afec72be49eb6a9260fb73786ed3d2a9007e07b21ff9813e703\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-install_only.tar.gz\", \"sha256:8053aa20c7a94e1f0472dc42eafea0a4f4fe2b994e668f677a6d28d8b28d9e87\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz\", \"sha256:b3470afbeb4308a62de685194007a1fd3ef500f39c7b84defbdb71d7b9f06cc8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-lto-full.tar.zst\", \"sha256:a099a8323f3e5831f4167e9e8ce8c1065b4796166364dbaedc4e2a25cc6daaf3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-armv7-unknown-linux-gnueabihf-noopt-full.tar.zst\", \"sha256:2f690ac13e32279d8ae14a94db9ce26484a5d110112bdf10783c799e00b7c079\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-i686-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst\", \"sha256:2caa9f6b7483afbdbfc0aecf4681fce0741324c8da67b5e2c5779ccd05526481\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-i686-pc-windows-msvc-install_only.tar.gz\", \"sha256:afddb2b6908297637a38858a07a5b1b1df8f23920745abc72d1cca6da6609f92\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-i686-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:96a64898961ad676026cdb430e2523f9f8d7ee101ae5a48b892e9cae689a86cf\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-i686-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:34a1ae65cd7f85498aa7acea705999e977754a64c1cdbb78aa34344b7f8083ce\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:0e0d697a39336494a63c42ac6a36b737e94b7417515768161d08d6147627e7e6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:61ebeb58df2cb46adb19c101ef74449dc50b45420058677db7ec10f3210f4e26\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst\", \"sha256:789900ee365744bcfea0e052f1a971a96070bc710ba49e184721ca9f85e9f4de\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:03142b036f145c6baef9b6e8812cb3688700f6425debeb245d721aa5ee97eaf2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-install_only.tar.gz\", \"sha256:d49d150d37e6e0ae2a7dac1678b9053aacc6c597e77fb90a85ed73269e8b42ef\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:fc009d69a94fb3f125fa2f3cb6510cc2c8fcdd40bfc4f3a15c4d00c83e91df40\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:2662e4ab3395c16a273ecb4dce7abc8c941cb72b9026e38f4a8168b8d29826a1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-ppc64le-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:09fe05d3f4d10ba2ec6c4a4c935525c29831c1c79fe18a03d96bf1477be0cdc8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:69a4705d3463a9fe4b600a82420699ec3f434092cf32b508d3b0470daaced9b5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:ce1967455ad984ac15f54095689d04d4c228bff81bf428d2e15e6a4f76717c42\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst\", \"sha256:22f0cd8650709300bdd9b63ab8ee25f587c02e14ae6eaa94b26352cc99a2199c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:09b02210e209e32318ca1111463fbaf7aa45ca6bee41a5ba3c307d065b233455\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:0fa16da5b4670197a932d60d22f7b75cd4bbaafa2b84d16769a89bb3d564c83d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:dd4ba93b60016f103c9fd386a7be65f7ca0ac311957b366d705335b176e6532f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:2d323313692b850ee3521eba2642c9f444cd565f7c2c17bde6e5e430db9b425c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-riscv64-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:6c7cb001672561781f81f03c7062d09da159a3c6f4fa57b935750f27390d5de4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:2c12640f17fab2622963efe1b332713ba34d5d3e50ce79eb5715c6839f74eae0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:b78f3dd9c50ffaf40bb689631ecc7a351ca9d9561cd1752f193648fde3cd3acb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst\", \"sha256:58f47ab62f6909c1d7accd698b41a82843a531f33a4f3b567a6103312e451dc6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:a53a5afe8a92c6a9e6a61abf9e2632202a799f4d1eabe51de1c3843bd82a29e5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-install_only.tar.gz\", \"sha256:ddddba68f2d2d1c91bcbe4b23e87d8aa4e5e5e0e4bebd8598e6eb9678dd3fb2d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:2f87f6314c858a4611bdeb231b26e1dba3bce79e0bef50fe26b911fa58e02530\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:8c760418a65830401b0744655011e467643ebf5acbad27bc4808af592f36eb64\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-s390x-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:4ca5536d7d0ce16cd4ce54f2f1abe2a1fd677930e12e11fa6aa2b7536791b679\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-apple-darwin-debug-full.tar.zst\", \"sha256:a707f99c5c22cac6af9b70b4c632e3127ccac3048a232c28652505a1952a846d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-apple-darwin-freethreaded%2Bdebug-full.tar.zst\", \"sha256:ca0fb03b247202ed5db411ca2a13136f8fd64b1303bca3d4577ed7b041d63051\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:b25e5b7cff072b5366f37e215f86e4e2e461b84dd96060e4fe206024f830ecfa\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-apple-darwin-install_only.tar.gz\", \"sha256:ee27eca1374af8421d37dee40f1489ede86cdc511dc2c6229b4f9c6cd275dc22\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:b4ca23de90fdeaaee2efcd8d604d8336572ee54aeca29cec56b148171f6eaace\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:3bbd475f896616582d90e442c1c9a7ec8a4df7317472501ec506b86e5e9da8c4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst\", \"sha256:c72bcd147cde726050ef75154138fe1be6d23cd40225782fe29c0ee10506aca4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-pc-windows-msvc-install_only.tar.gz\", \"sha256:95258d68b4857873035201f86dfdfc6fcfeb311c8b9ba4610e54e9f9c5604b06\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:d60173a7693c3134b8364814b80f98ddde0638e0e75e5b004f37452fccf67a33\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:630a98a7b9e02dd2025fd0d2869d6f2eb562b441a5ef65313ff44f65a7092e65\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:0af338d3eb2da3a0d675dd5387a3129e879e1535b2697877d2d1b3b989a6edc6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:43a64609a1349d57c989746c8fcc4a11ae92df17ff77ffffb9b98cbcb3442781\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:78da0a6aa7a19b8d4198a89a093961fc1376a3925bce16c04b22213fd7386f13\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:f3488a36ef5c10d01d346b9e540727d0807202c9bef52f40a987dc59ddc4ecee\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:2968f91b0c2b5e85dae64950b7629052d90d2fd868ad90deeef343ba610a4a0c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:2f54e69a2abef95f12d6879af81b279a9718d65a188fcaf289b523157e85ac46\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:93ca459f073ebf7171e5dc3dcac67ed6601d9d0ccd5484dca07e02d208ffb024\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-debug-full.tar.zst\", \"sha256:32e29af41d692c6ec6429e07af11b2c6af4d568a81eb5ca95374fbabeb7de293\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:3a2fedabf6a1f8c5cce0ef5289c938204844a6b8ee91e1ede1e335de07d926db\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:6f4e98431081bfca9cf413d5db749e4736819db7ac48d7c2f5252c700ef59612\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:fc327cd3a7983fb10b28a79852cb52d470f9c2c0bc188c7b148c97b9ab00453c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-install_only.tar.gz\", \"sha256:938f1f9c7d516964509100ad3f1e047d8b221b1f22ca5b298f416ebb372affbc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:36410646df35f72ade553c4f1f770ee533d5f624f66aaec5e3f4c4fac49dd86d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:fe36860228b1fbe7d9ea4477ef011dbdf2d2684046b6c28e1c744f18b98031f0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-lto-full.tar.zst\", \"sha256:12c9da04d3933c54cb16091923aa537738f6d150f837fe2b3acd0e59de41abf4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:acc6b71cf4c42d2acc7e916782a390fe0cba16d3ac0fc7f4c79c5cbc742810b1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:e5b5b5f0426d337d646763ccbcb9423c33ed6d5b8576ba03317d6d9974e225c6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:915803e57dc244603722b22ea915c5ab715aab38e617eb0128768085a038c4b0\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:ba7a27a1414ac0a2144c49b44a84111ef4fec8dfa5a178e0cdc3aaafad0fcf5c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:4be2be4440804e7b1dc4cbbfa445e0996c8ae54229a35dda796c310317507f9f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-gnu-install_only.tar.gz\", \"sha256:ffeb6041db83f69c39f58e798b8809194824c7cee9ce60f34906e0a889b0a738\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:365c6f4d8b4e0425f465be6a76c8d90cd1c1070ebdbd7ece97922e07db56e619\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:23ae7e028e97a8eadc5f87ed7cdd07e8078a8eafe933089e16dd47683f5f4dfe\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:708600ea8550c53caad68722246ff45e85549351159239bb182b7c9f88f841ae\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-debug-full.tar.zst\", \"sha256:6361010be044b3565e65e64bdf8bef73b9e2906349d8be1df839a9f30f36961b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:5570c8dd8628383536548e27150c40e2f74ecd7adace15383202adc7f089f8f7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:11178f25a28ebc33d02bdc8a0c514cb8af71997e35d69eb16923f031efe08852\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:e13d278cd64a07c2ac32d6030cda460f5661e2c1c8aef0123526442df3397664\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-install_only.tar.gz\", \"sha256:0b8d5d4d636914cfcef5339b2cb8ab64f01a5f52dd2bac8591042d1c6ec673cc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:5459173cfd3cdc1ac4e86e53ddf4eeb5306ba8f733290e321181b73fc64455e4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:c8d80add023a329fe32a5efc3082483bd9ef7f818633bd20f7858d4555df3ebb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-lto-full.tar.zst\", \"sha256:76e7719c8bd7f6335424ef77dbc205f5614dc050e3c64db2d9bcecb5bc7a771c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:e919945248b5f1901ccb90f0f8c947aea9a303e58ac7d78639d13dc6523da0ca\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v2-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:55745c0d599e8002b868c2396fc04d63022ab5d10ba1bb8524e7fde3d52aeb4b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:400f5f76353856e6fd3986499cd57f808de9c6cfbc1a0d84428976e2e1ba0cce\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:5c84059a7c94f635f950a84d9152b4fb43e4d743be44704c67e4b35605190916\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:ec39d81f0e7cb3264368768e6a9bb1680f642978460a3502859cfc6dc99f663b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-gnu-install_only.tar.gz\", \"sha256:52cdbac00fac049b1d9b8ac0814ba927e11247f20abcc183c35b48eab6dc0dd8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:7c82d0aadee9b9ed1cf9b23ab93aca49f3a3a94531f7ea711fb9df4863182be4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:a9495b9270fcbef0c337f4cad6040ed62fe9f3e95b980041012628da2c5dd99d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:0c29a37cd5c5ed6740d6f4852cb66f9f69e31f3ed07460e050f02a16ae2deca3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-debug-full.tar.zst\", \"sha256:98618dfed670adb0a9e1766de287d059791f3c37f26653ab771f1b2b49f23ca4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:cd06842ec071b533bfcea8897f6be64354e0446e7831b1c0eae8b668f0073ace\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:6f37fcb6756519dafba3cac38b8bae236ed6b465a46580a2f502e22ac736b558\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:4c4e71c421b909a92ad442d1ad1fed857f514eca327e8f24d139c3bae428b9de\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-install_only.tar.gz\", \"sha256:4bd4ddbea23accfd7420454d86a5c099955b31005049924bef02d69221a4b13d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:6b6be1808ee93482da2d73da28ccd64792eb1859131ec0e9508a01d13c5ab464\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:10db567e94e5c753b9136b6993464274f8a6d4a0e9ec1cb4681ab7d4c4701b8d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-lto-full.tar.zst\", \"sha256:224ac123b384fe530c63f00b021ac9fae1fc8e95dfeec2555050d094f225cb38\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:55e40059f304f91e6411e867ff5204587117078acf5282ec0da4dc99dd560d92\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v3-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:4e85db65fa079bd7a916588e91d7de204e963b8e21f85e7c249c26dc9699e556\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:cc788cc520a5f3d258510cd8519fef6628aefb698b960ba6787b59be1f150d45\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:1333f9abeebeb002bd65bdfe0f9dbd7f60eebc47f27b8f0595b42d2c3d61cf48\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:4b49715b32edc1691b29fc0b5879939f5796cf1f3bd82dbcca652c0fd88c7787\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-gnu-install_only.tar.gz\", \"sha256:993ec595c5663202295e27ced14911169f1ffbf6aff8b57c443fee2103683c3e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:54369e420c1f06e4102bd0d03ea495f953594f1e45313a0793f4cd4f1c93e07e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:52b06c881d6edba5e3f5b7fc8e8505b3e504ce0a4aebc8e660c8696200ed95c7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:adebbde26cb376eca03e5631f036df559b0cbaa132a87a99767052739c85091c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-debug-full.tar.zst\", \"sha256:adfef706a323dffff55bc91ff0536cfad98ba4d14c12e53eda1a763e967d42a3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:c2a954b6898e567688ec980d0b3ba4f17d31652f15992280c5456a9cf36d0ed5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:3ebde8b19d4bd9c4cf1cc69c1d57eef5c73b00a2970ef34b08b141db20e3386b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:ea631b90114cba329f003296e8816e56839296beddfe67ce0ff2cfcbe0fcdbd8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-install_only.tar.gz\", \"sha256:da06f8a70a3abb30575e880e90e7ea22b4f4ee13f667a784707f49392236449c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:66054a946cf3626a332e1173a8ed096245233126e30dbfef788fa2acd8921b63\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:bae92f1893daa2fdd59798d083dd94764c48cdb6e078512f2191943c0fb2d15e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-lto-full.tar.zst\", \"sha256:02bd0b69d9ce110cb6422418bd4e2f2fddcef76633681c188caa86cf419a89ea\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:b18aa8d8a0946d7565d444d3e9e456b9df65c4a7dfc3dca7e385f7543de0e6ed\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.13.7%2B20250818-x86_64_v4-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:b9e2c47ea0bf408cf2ad0aa9b8cb5020b5787e8251bdf7cfe15863b25a687d61\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-apple-darwin-debug-full.tar.zst\", \"sha256:44f7d506915fea2f5bad606a09482b6cc01ca754f18e16e55ce2cf1efce6f5de\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-apple-darwin-freethreaded%2Bdebug-full.tar.zst\", \"sha256:06978356386cf3e6c8cafc0a2129a5b4b1c39c4e0d6b93635dfd2bdec1bed59b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:e84cf332dd6b80d097409cb0dadd0dfe1c3c29931bb4421a922c2649ef537dd5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-apple-darwin-install_only.tar.gz\", \"sha256:7816b56230f5e95826f24d31baa91f16f8e2d8c8ee6797687422edb263ed7775\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:06ff382e96de920cc7305e1272b811c7ea14ec1503dc446c06653918e194187e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:1b5ecc766f26689ab2bbf8a45a73fc3c3203df803a454d8482db6830eff23013\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst\", \"sha256:2ad634311f9a8bc9beb0caa390ae63a05c8c5df5dea0d60a1714356e1ff4f844\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-pc-windows-msvc-install_only.tar.gz\", \"sha256:ce19f64f5e0fb488ee8bff1a57e3511319dfe79e9e0ef58f7cadeb437cb87634\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:ad06e2b720e720781b3a7000ebedcc9200f33f6898ddb9e8d28c87a77883767d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:00522053a07158621b654cc537608cbe996e11f193e805e5dfe6d314bf549492\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:ec42a5687e5bd9ac114febdefa4a01d9e88bfa5874d7358d5dda9ea2713366bd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:174681c59854ca6305cffe6deb8f4a7a0f80d211fc84333c2de35ea4b2de046e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:3dd408d9ed53000bde24012f2849f0580936bf1e77c939f73771c3cbd44a288e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:d5348b1e334f8e460d84ece38eb1f0a29fc3ec8272a526f00a707a8b1c856231\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:3c65f72e00596fbbff66b9dcf07d6ee7817fed247a090c283a8e0d3adfa972e3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-aarch64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:39142fb98bb51578f74283cb8793854554805ae082e154aefd47677ea19ee8b6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-debug-full.tar.zst\", \"sha256:00f11921bcd1fa091577bd4bf615beae8ea75b16f5185a0d3e7dc8d2eaef0247\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-freethreaded%2Bdebug-full.tar.zst\", \"sha256:5063b36942d0607ad22197fd14642cdfc4744d3d7e8702bec269fca2502dd8d2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-freethreaded%2Blto-full.tar.zst\", \"sha256:7f2a7341664dcc4b009437c7eeb9fffd57099b2edb2d160b30c7284b395814d8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:b850e1d1b81a1f47a7e2717d6c92b1530a02de0fa3453f249251b736a1d3e935\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-install_only.tar.gz\", \"sha256:27ba44f22431b0771dd8c09f1c409aebd07dca9baa2c60641eb1a43e311f8291\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz\", \"sha256:140a1078007da076d5d0244d4f67e7fdbee0fbeb2f5cdaa515334b7cc1bae4d5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-lto-full.tar.zst\", \"sha256:776d49a2cd77f437e8357dac56aaeb67460e70c08d6b323b8064a8b68f656633\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabi-noopt-full.tar.zst\", \"sha256:535f09e5b50b44477c6534de30d8ad8a564e011c887d501b776d50c51ab5e8ac\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-debug-full.tar.zst\", \"sha256:c605d85be3d5d138797eb46d0839bbca5e5565d90371f3031129480cb3c410a1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-freethreaded%2Bdebug-full.tar.zst\", \"sha256:70655d7de79893ef2020eb0a382cc6819a08d071272e9043c00a3c6bafff9c4a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-freethreaded%2Blto-full.tar.zst\", \"sha256:a54be21e79f5373dd5050305ad9b21d8fe61a0fceece099aa32c04ba1aa8a1c9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:1325381ca1da0f25f8a8e5e644338837fdd577d34428162281dfc1271d7a0340\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-install_only.tar.gz\", \"sha256:b79e6e6439d93b65404a795e637b84c1afd6b188c513942eef5a29a65550ff27\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz\", \"sha256:e0372e2e723fdea0e887a80a49c8c854bcb8b8abff63fe9a7f68b2026049072d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-lto-full.tar.zst\", \"sha256:92bc9b99ab020123375f173c1b633eb7552cae58adb6015049f0018b3b66e44d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-armv7-unknown-linux-gnueabihf-noopt-full.tar.zst\", \"sha256:cc31c0220f574c1bd0060b08f67d029111792be39b4b3e64c24ea12df971a636\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-i686-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst\", \"sha256:698c4966970c33b02dfd69df95c0e218e94d332d46dae1d92f2208b6ab56b984\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-i686-pc-windows-msvc-install_only.tar.gz\", \"sha256:810b68cb2ba3fc98a59a02fc0972d1a53c65501d28ce5d792b75b27dee8ef031\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-i686-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:6366250c4607e43b0f02fa969963d7d17868e21c3524338955c2f7498037f023\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-i686-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:c3c6cc74e0ed2e59bf7064b41f92bc41bc2a9e98b3cbebb6a94255c0a14efb4c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:920a3b737c927739d2d92ea748ed47a8ea2dfa702571b995024cf9ff16192a29\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:2be91ca30bb11084b457f48d81d0a3319c25203bdd523a237b4c9611384f73be\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst\", \"sha256:7245f2b7e26121c7b126beedfc7c3a2f3fbe596a16bb162013b061e85caebb2c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:d8d5cc2a1db5f6329164cadd587f2fa9fed259fdc181a47d74f524c4734eeabb\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-install_only.tar.gz\", \"sha256:4cf3ddef9d3d2c14d0d0d80c55233cd4a4ff1feef58450af469b149a2afa6a52\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:1f3dfcd6fc5278b4212c6e951e37a0ad62123989eb58ff290f31b26849158a4a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:2baf2e091c202c4af0dc3fcaa40f9605dab120634fe77313334bf8893fc7a164\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-ppc64le-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:a8d8097e46aea8e00b752818cbeadc0f55e6836ba01f235d6d1de87081325fd9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:6029956cd96513afee72c214824ff45e028d17ca78428632d3146dfa84976907\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:1ed636a0399c7574c4d43867320d7e2640959a0d9a3abe872f321562db2e219f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst\", \"sha256:204b656048211cd48fec5e99ca399a293b3cadd95663c4c280e982d28eac87c3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:d737dfaecdf0d99f05fd16043ff3462b90ba18337c4c1b62e243666b34bee575\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:5fc8a508877098a613c625086e62f8bb6bb18af91251bba62d694db5b98433de\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:87e57af208ef79efebdea9e5c0f11c98ed2627455bab45d9b719d8eeac5cf48e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:7ae8d3de45d1456a90aa45bd3ff7ac5284caef228d408dccdee9b94cdec7f76d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-riscv64-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:a4035c9f1dd8aef759939b413d45db2535ed28067822558d98f320a0c9d55ccd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:b8bb6be759029b391d5f85a1573f506c9ebe8a7ddc403a7d32a7f5bd5817d424\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:f02643b7989f66bca53c6be78c16aaa947b243dd60989e334989d66a51297f13\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst\", \"sha256:d3821241d9ce003438b9da72553053e53514c2755cde4d3912090088e18c78ae\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:1f241c7770eee42efe8cf5f9fe3e34397dc9d5e6992f56d2762c9d53e65210b6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-install_only.tar.gz\", \"sha256:323905f4617760ec36bb0f33fc5491fa1f8aaa18438ad627aa8b553d3ce0239f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:a270221c9b4acacb9cd70006036859e713feed5a6d1a485a55082eada7bb5103\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:e75aaeaadd4a12ef7c1650e43da98c4814607a2027f0ed2a86a83edd32110248\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-s390x-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:d250d83ab84802d17e1b2a875448bc8b73e1554eed3fce4aad70f69b66081458\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-apple-darwin-debug-full.tar.zst\", \"sha256:f62d3035f48b0848032e4cdd01b663d2f2fafed35576dc1c88b702b0550711e5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-apple-darwin-freethreaded%2Bdebug-full.tar.zst\", \"sha256:386bb22b28355be2ed5bc6b5103917b3204ec72beca099653d0c98d17514a340\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:960589d9672f12b40dff6a44f22eb2f6725d89a6194c6cc128fe7a610ce0f971\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-apple-darwin-install_only.tar.gz\", \"sha256:f2e91a67c7513b46574a1d24018faf79e08bf25c5cf8d48bb9d49f946e736401\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:7127c66e6f3e43fd70bd87c72530caf76a7862a53e0136f428ebb0c224bc0d39\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:a91c8e63db72ead2dfe3d94536123c9f99958a8c3979afb9976b0758d393cb7f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst\", \"sha256:017d79c6cc42ba8d1ddf1320b1f102e83a5bb2ae7ac591f67aa8ea52f30dcbed\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-pc-windows-msvc-install_only.tar.gz\", \"sha256:a401068b61f0a229f99f255f73a444f65284f72bb1ed59f189d98185e7e41e61\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:361a49531a5d4bdc72bbccc81f7b5881b5eb751e3b47683b233cacae5aeeaeed\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:264b5c5a08cf05abcabb93cffa9ffb5ade0b715db1260b51263e71cf6a9684c5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:71ccff17601312304cd5e9c88be2d36a3158f7f2375988d784df938606f9f034\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:5942fb406a77d3d9305499443e53dbb1fcbd012c2666434bcecd0c4467c7e810\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:c4a44848e859a6ca4c5dc5d0720c9ae858d160201180953e4b30adeb1302efa3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:daee4fd6a65c7485f8e37c0fd91cba126d96687c5b94db30c741a1b6095ed50a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:c6135aa7e455cfc5b324c0cf620d789a96116123d9c30891917bab7ebce027d7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:641b2b30146a08feb76b767126b891326f7dc75d487c8443b0a3674da2f5df5a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:78b998af86f6d7d064533a749ee84a683a770786548ab8818ec93876b06a41a6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-debug-full.tar.zst\", \"sha256:4a376f1aae60158c274787d95dd8944beeb4358eb68fd041837284764dabf167\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:6c9df02d5bc0b5a389cab828a29a53fdcaac7805e451d5834d86dbb087fa8e56\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:212af714fbca97ec5796c5316dd592fa4e61cf60be4aef2093955a736cac85d5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:0153ce85a76241fb2c1f0ec931cecc2db24f5aea098e59e6b80ee126f2e0c745\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-install_only.tar.gz\", \"sha256:77d7029f0451fd93f791e756ad8f3cb7800b46ee0646230b674f54fc45f9e647\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:cb9cb5bef248a13e26fc4fd28b17abc88af5f5690872e392aa2ed0705b0fbfd5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:ecfdbd0e93077db61b24b542d8c5ceb991e1dd623bdeb7759f444553c5971fd4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-lto-full.tar.zst\", \"sha256:2c89dc8132d226a5574e478f03d85710b443a3bb8f055c03a9f1e04a9044bf7c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:0afd50a4f2ee258a96dbc7dd9191d5f8744e5a0bca4e609e5c0ded8a3d74d209\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:39515d5c1fd9f3ba1b9b4c43f46095f82fe548513ecf418bfc1bb8b44bdf1675\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:ea41bc736a83278cf69ce92a7e53c16509eba97a3ceb0f3db2f88ad2143810ef\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:ff0d208c7de1c875efd0cd8634259b629cbeda2e2a875c89cf584cbe2dedd0ba\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:ac0bd6fd4c7e749685b879488a849eacb14c0a38bd4b44d94c5057a981f1311c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-gnu-install_only.tar.gz\", \"sha256:70084ff730249607401c82ecfdfc74aab1d161afe76377d84920781998946da8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:7ced21526f62aad9768ad7e50f349f7f88bf9d539858438095473afabdd1849e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:5578c902af2ba02a997eee5570d82b1d158f374b909b6855bbfe9ad0198d45fa\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:47070e061f9d3282f0562e50a28d840c3a2c3fcdf6d9e96ae60e9efc373b6f95\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-debug-full.tar.zst\", \"sha256:3739331323a983ef9e184726a77b7d392fde931fe0b5bf330c884be81385d534\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:2271b9f94da8dff2ff3dab7b23dce293afdf1175fbff87504fc31537d6ed25ac\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:c26923671fb7a55818d1922af4e703875554fd09f29755f6d2242af69a11b4e6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:8f51d6bacdfad47cd2e1cf648c732bdbf66bca4040a429639dc5bae2d047368d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-install_only.tar.gz\", \"sha256:7ef2b8729d83e852ce66217e501525393c8788fd4d61037c9babd13f3544131b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:222f00f6f594d2d9036a425379061048f092c4c5d3606307bc0b61d3432bb2ea\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:4f3fdce31bbd154587bebd02fb5d3d4c68cb934434de3c8e71ec52f7829b17f4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-lto-full.tar.zst\", \"sha256:05c42fafc119a8fcede483d63bc89fa9564758ffcce26389a2324b9473ad9d85\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:7f6dada5faaadf0b63530d76a19634fdfae0732a51553cc37157afd4f3b43ce2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v2-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:70923d7c96d8c24d954f95df5d7e956dc5fabb2eb3bb7104453b766707106d68\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:14676d14e9620665b34a00ee9405821cec1065a0740d1cb09cf8438253d6735f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:7ed87dfadcb8ff7afdfd914b0f083905215d096ae640d15a3113e96f5c1f4340\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:722e34ea232dd2970cc7d30cbdeef7d0dea36e3fbd9eeb0d6daf91b03633bfd9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-gnu-install_only.tar.gz\", \"sha256:0c5e31f49654bb48cf57e18e82b317d978e7b5f5bfa1f2050227f673403e14df\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:9f324bb13243070e7874e21dabc1f6627e7f4cb4e263810b33751d969e924525\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:8dff32c84773888b783d2a5849397f99b9e03f074f1b3af9e7fe61696623d672\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:764cfca7cbae2784ef3568b1c28d21a3e14fa332dd856ea0afd0fa9d2c121454\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-debug-full.tar.zst\", \"sha256:0051e53a2d260399916ba5ab99427a52c9710ae7f7e661624c580fc7d663ccee\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:6a8f1c8305e959ff24ebde45fff2ef226eb38ed381ffe8a03537a2e85f4140f9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:8d91696384d992951e94e740956a9e98f577f785d5554d941241a68fa9c54d7c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:be6e51a0904e4a2502a30174ac1fc61abcc064cbcc68679470c110406401690a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-install_only.tar.gz\", \"sha256:024124a475851864525d45c40d9ac8cd625864d45bd391a7295c43717359072d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:e2969cb39eb8126f689b850585b88d292034443347458732f0a6e4a0a73cc1b1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:5a83a9fcfe7301ed5d6a23d0c15125ff851d8bcc33648c062c41bfb8fde5532c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-lto-full.tar.zst\", \"sha256:1d234f775f311d2b32cfbc2c1b201b489d5d5d20176d36e209d7fb06ff976b71\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:c81fddcb15339be28515e2ec698b8a2f35d583a6abf634ee017eed3ce3b844f3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v3-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:67792c577624419dc40ab2452d9ba414c0ba3ea46cd652f2f68f5933f90771a3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:4beac49dcbd80c323cb62de62484ae721658a71c0e7a15178d668d98e87d6743\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-gnu-freethreaded%2Bdebug-full.tar.zst\", \"sha256:8cf727b86ed45d0803617a843b6c0e532daf0ce27fbe0a783e1b01afc81397ae\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst\", \"sha256:505d120a7da2921d7dc20dac4b978ba2e9ac71fba8f6a8cb71d23dbe2dbbcef7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-gnu-install_only.tar.gz\", \"sha256:012d74010b61f004e991f23c1499d9c0d03bde2cab3d1f032f1af81a518fcd69\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:d14bcdf034e99965bd8c9e2e86b5e87270600455c9a77c386e7913712fca589b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:2d713d1ee5518363bd2a5da23ce5c08de3d2ed926ae9bd1d82992a9e12ac7216\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:174ffeda1ba7cc6fff6172a40fad6237567b2cf1856942559bd95db7e70b2388\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-debug-full.tar.zst\", \"sha256:74fb955ed3b2b573fc8eadf581fd1ad8d0642e1240caeff1789caccf9198b05e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-freethreaded%2Bdebug-full.tar.zst\", \"sha256:4b2ddbe61d8443f986abf60f50497e54b8f32431a5c7b946424c872d93f97132\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-freethreaded%2Blto-full.tar.zst\", \"sha256:782688cbcc2e9ec4977d00f6ef5b15860a8f426a051d3f577182d7f7fabee811\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-freethreaded%2Bnoopt-full.tar.zst\", \"sha256:7f0b845c4eb5d16107df8ad657c0d7a2f4c5485b714daca2a7410973e58c95aa\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-install_only.tar.gz\", \"sha256:085927f6744bae43380a1b771653267ad2279f53c515684ca1562e845f001ff1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:5f4107afaf934ed6b52568aaf4fba43ea624687d136b82968c7036f8aee9d8b5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:fd00711dc6e8327c714164743b55e161c403eafc1e1f13a99ca926e77345d948\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-lto-full.tar.zst\", \"sha256:290703c5fe6757de0dcc4e6d8714a60aa28bc0a930397b5ce2893e2c52430d43\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:be2d6987c2171a21fedfeb1350822f63246c1b5606dbd22029e12f0785c1ddd2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.14.0rc2%2B20250818-x86_64_v4-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:ae03a36cfd8ecfdf6e6d49c42fa606058453d0004b338263c5f4ba9e5025f046\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-apple-darwin-debug-full.tar.zst\", \"sha256:c2c0fe31b2ba2c48066847763a8aac2839ede63da3fc3a1ed998114a82cb4d07\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-apple-darwin-install_only.tar.gz\", \"sha256:a6cba6f41d00a07fe907beffc7d719afafd5ac9241abab15f5539d1d08c5a88a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:b4885039c9341dee671515a181a3db6cf16dbc5530f919f63dcaa8be4b8b0f43\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:1e3dbc151131e1707b602ec4d549019f2da299a3f87f4627e15b626d1a4b676d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:fb5f16dcbfb314064ccd9c0d5595acb9574601a3d7c829ba82a444eb29570dba\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:bb8e157a9fe5c8b35810f8673998130ea212b6d19bef6f66bd4801f0900af6b1\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:9d37ddc3e3c93935d63f6ec373b6da4717a725eb105c073aaf0c1252c4011ade\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-aarch64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:44315d86dc93c78913f0fffa1f9103c2cb48ef003ed8f919f01d68f0c8e14403\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabi-debug-full.tar.zst\", \"sha256:559d024d6b4fd254ff3caf08f579e97a5589ac494f503b2a0d60fd45e671bfb9\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabi-install_only.tar.gz\", \"sha256:8ed785a92647e24b2ff5ae1665ba96a33d7b3ea4be4a33ee1f24d516d148759f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz\", \"sha256:d17b0812320c5429b6c6b06a8ebaabdc3cca1f49c7b4defea60ee666074205dd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabi-lto-full.tar.zst\", \"sha256:a43fec14bf613bfcf70eb7fb9b851be37880e7ab0e4dc978532191ba6f7eb3d8\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabi-noopt-full.tar.zst\", \"sha256:e2f7d87adcd11d539f7d949d7f4a9b8db48d81cb69307e4dd6f8565ffc1865ba\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabihf-debug-full.tar.zst\", \"sha256:6432a7708b254e4137c5309edd1788a0cf079704645a783c9468c8699c593101\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabihf-install_only.tar.gz\", \"sha256:d25ba140e3417c6f6b1522b3c5414559af3f236c435bc93e7c50d49fb6862bd3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz\", \"sha256:e462dd10dae601da9fea971bd5c1d8b305c2c4fb66ca4d5ff413b1879d08ac4b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabihf-lto-full.tar.zst\", \"sha256:ecd4fa0198d63d2ac08786097a98ba3035fab8fa2c4875785b4f461bde8a8145\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-armv7-unknown-linux-gnueabihf-noopt-full.tar.zst\", \"sha256:105c56a463d6bdf23545130dd2b0c55af35dae643c8793bfe334874521b0aa76\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-i686-pc-windows-msvc-install_only.tar.gz\", \"sha256:0ceb24be141f3f10fd97822ef62055b602494bd91cacd2e64b951b1afff4435f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-i686-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:4875f430712f04a94f306a4d8bdca95b791c0f5b3bbb9fcee59eaf6562a6298a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-i686-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:f7e1b093fa262d7f77347d23a37d2509b7c0efd8557f759059ee07e9d1cc1943\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-ppc64le-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:0a5bcac14a060b62c5ed7b131d8aaa65c57b8035f80caf5025255529a8f08710\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-ppc64le-unknown-linux-gnu-install_only.tar.gz\", \"sha256:3b7bd053713d70deffb7fc9d391e99cb6d1ca112924f9968ff5137cb7b437136\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:3a416305ba0d4d2d0c3127845bf6135d727a6c3890820b5ca61441f6fd81676d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-ppc64le-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:2b8ff5a64dddd35176f25b9fb17a0a16d1cad32f4c39cf1c6f9a48288196db98\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-ppc64le-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:e8dac83951e2704a153d1a974664c8bc9bc8b79740a28b9fb94821c56b2b28d2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-riscv64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:d49cbe79e4814e1d21f034eddc4a8632c3a75a66cbf61266acb9dc5255cb320b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-riscv64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:9b78ccffedaf97fea3c1dd22eaae7e6679c1264173926befd936500e2355c2ae\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-riscv64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:6053e7f50f5acbac135ba69835e0bf65d2c8794ec64a8fb21d434930e126ffb4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-riscv64-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:ea07654215f8d81f6a1709b4e42ae5951401ff4ab205acc6dc78c8de10aecfd2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-riscv64-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:52b31119ebd1823cc541cf1d0edac9321102ec2a7ac0de5dd83a661495cf5de2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-s390x-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:f120a02cbe37cc4f188042739b6d988c12a014a92c5dc760335b3775e0ebb80d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-s390x-unknown-linux-gnu-install_only.tar.gz\", \"sha256:25d2b973d5fb670d4a6f50c80c72cf9b67952a4bc55b787075dcd00cdc5ae6a6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-s390x-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:7445c08ca60624f0b0785d845897e18b2cd7d087a6029d63ec65803c0647d05b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-s390x-unknown-linux-gnu-lto-full.tar.zst\", \"sha256:7fb69011af1eacca0b5f5f6232ade20e6e244572e7cc8eb476f4ddf0528cf13e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-s390x-unknown-linux-gnu-noopt-full.tar.zst\", \"sha256:68e31d7dc4e138515c891ac6aade48ab183f8b40af592c01ec4e38ea3e395f5e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-apple-darwin-debug-full.tar.zst\", \"sha256:b169399a02736d6b977e7c49039a46de2edfc6e4005c4b4b60f8b4e7a04e41b2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-apple-darwin-install_only.tar.gz\", \"sha256:ecbde9952cf790b1a532303a60a71d2fe0f8e61b6569a3fcbb214154c37c0e06\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-apple-darwin-install_only_stripped.tar.gz\", \"sha256:7daa836b18e9b942779f35a92ed75973765d0cbb962e4606641c79ee6440e715\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-apple-darwin-pgo%2Blto-full.tar.zst\", \"sha256:57683b3377b30e9333548a799ddc9169eba3327c5c3b7e5398566826a74807c2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-pc-windows-msvc-install_only.tar.gz\", \"sha256:e047cfe9ce478248b3605a754fd66ba08f91a11b51aba25498023d23a914520f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-pc-windows-msvc-install_only_stripped.tar.gz\", \"sha256:5f11a34f02a60d3a012beaf2f4367a87d737bae49b52fe71a2389bdaa43afb8c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-pc-windows-msvc-pgo-full.tar.zst\", \"sha256:fb34d054ba91d5820609eb7f8c6117154eab5169812fce11ccc72634b9aefe2d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:b3cb22ca087e949655491f67228df38a1eee9db8fbb530d6ff438038cae6d77a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-gnu-install_only.tar.gz\", \"sha256:d2251aea8f4c8df45d04ed9462c1a121c923aac61c0c77da44758511c976bd4a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:efef0f804d57e47691320e99f103f202db59f1d947d9164ca2059d6e0d20d8f7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:ab3f97c19cc79c95d7e1f73817e377a2584a3102dc49f4dd29db42da508ffc60\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:d3874de5d44cd7fb5d7d6dd24308a5eb0501aac0a12f98a2e2977cdb9619f1a6\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-debug-full.tar.zst\", \"sha256:1d18814d15c94e0a178e97acd6e55f9eba19f641dbc46bf35b36fc40bdbbf85f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-install_only.tar.gz\", \"sha256:4088a8b4d7063fdd81a12ab7dbda311e10b4b7536a73589d3f4b5412dda7873d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:d09cdd0b5d6723781ad1e3fd561a2587c79131a8a32af13f8140c4a495c00847\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:2fbc238a5108d4ef2f542beaa918c198851b380300a21135885320c18cdccb1f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-lto-full.tar.zst\", \"sha256:600d34779eb263642b5731363e68471ab6e9a6b07f87a21b04fe246ab05aaee2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:66ee1746949151ca581fd2648af1a0c41979a8958c988249f0b04388ab65df4a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:779bc1b15509799f73307219fc08a527591acd18134a57790158f1345e5c25e2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:0766115a0b376c40e656974c57059336bbf581fd51f6b256dbbce6a95c516d25\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-gnu-install_only.tar.gz\", \"sha256:46546e3055a82a4c2d7bde1ebab2ddfb4385801f99ac17575ec9a46d431064ac\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:c18cc6dbe7511d3e0aaf0db62cf9acbdc965f5597465bffd10594199c5d49b6f\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:558e5279ccdd70ef4536809f65f240b9cbea8294277bff9964e31027bfb099cc\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:15307722bd2ea8e0c7d4b0b4da0b1324e4c65dc4b9d0ae6a7066e2426d4046bd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-debug-full.tar.zst\", \"sha256:bb1d1773fc69ec578d439cfb07f0f29889ab8c11677a15feec9bc7fe8f207cd2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-install_only.tar.gz\", \"sha256:2724f2a4deeaf89652161491535b262128eb7e7cbdfc5a176f7db5f42ec091f5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:ac57db449393e21edb638a499ec287baaff9738a6fce669d862dfb2d0f8c336e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:fed3ce78675ca05d9c150922016ac5ec6eba9f5f181287a951dd70a4e4692c7b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-lto-full.tar.zst\", \"sha256:128caf162b917ad0dbc6d5ad7179968bccfdf5bcc2d40a3fff4f710888b0b8b5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:3df7b87cab4dff4fa47646a6b57c63da268595d478d31cd4742dc5baee9af91c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v2-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:d655223747b80654efabbb3563f2ef933068770c1f894e2f6d15712745cb92d2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:12a76e319ba14cb28f685fa23282382307199f36ad84d1ee5738b5c5b53e9cd2\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-gnu-install_only.tar.gz\", \"sha256:dae20efff21f0abac3d4eb5fc1beb500a1452d182fb3ffbb9890525d941f3332\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:0e290d911132b85a0459feb5e1222189305fabd3dff1f93d5049973931ecdec7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:33e25966d7f721407ee878100375f86f90d6c47c1f8515bb6a79dce147fc340b\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:fb126fedeb80767f320ce6bdaee537b42deb3544932f0d488f49a6668c94dcbd\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-debug-full.tar.zst\", \"sha256:5abe82dffcbeea76d0aed7d82003edc36a2357714ddcfe769f63804c6ff17db7\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-install_only.tar.gz\", \"sha256:a5268f432d6359a1a403ed367a838786d0fd067dcfc63b62e62c9d9cd1c70268\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:ac48d1714b5f4cddc894e74155c2b3f8816a0a8a45acddaaaf154e217b1e577e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:a082bf434ab1f2cdf21559e775447bcf1ba66d8adf8ca0f68e93548281d2b551\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-lto-full.tar.zst\", \"sha256:0b5496fce6ce8afe3f31a8e78b5f49f227f0518af92530f38423b7c20b662d50\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:a50395ca08bd46adce242548fabd190cd73ce882bcf77e739dedb75d5981cb6c\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v3-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:9c4d1a40af3cc9f2294d9d8ec213859bcf0f30b80417b356895fc3090d32aa26\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst\", \"sha256:f16dd10a0abd668d0f1afdf804fbc44f00447cb8d4e3f4070df7db39cc6396b5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-gnu-install_only.tar.gz\", \"sha256:ecab2f675011f18cb51d5dd110b6016657874cd893c661406e1162b08fa7107e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz\", \"sha256:ed96e8c03a0c62d587aad6aa1faacdbab4495cc1806ad450c83a99059d37efd4\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-gnu-pgo%2Blto-full.tar.zst\", \"sha256:d52fff372db6a9545c248d26f1475a238800b219c430a07a539200c7e9f6906a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-debug%2Bstatic-full.tar.zst\", \"sha256:a7f90191e3781dd9db0994111452467c38126f91abcc64a29c8967f0bdebef51\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-debug-full.tar.zst\", \"sha256:5a97924d4e979aa24f7a16bcddded00561aedf87e617b66224ee79e690fa907e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-install_only.tar.gz\", \"sha256:4b2a44200561f7df7956ec2752bd6f8192b30051585572f46a33d0008862cae3\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz\", \"sha256:af555665085721b76be7b8d61e4468d163a4792140e1e8602777bebdc2ad8af5\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-lto%2Bstatic-full.tar.zst\", \"sha256:11bd11a047e9f093e84354f7e57f05f5db967bb19f3eedd40c7c51c8d3195c44\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-lto-full.tar.zst\", \"sha256:412d77e44b8b2b3ab5e8ddeeb846abab04c3be4ccb99007ba9926757c3e9da5a\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-noopt%2Bstatic-full.tar.zst\", \"sha256:91efe33ab8c7a70c681f999428f15d5a4ad42e3288f0ae104c6c0bd237e3bd6e\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/cpython-3.9.23%2B20250818-x86_64_v4-unknown-linux-musl-noopt-full.tar.zst\", \"sha256:44b41e3e98f1fe408cca1dbb5463cebc3ab4a21a80660c7edaff2f15fe20eb0d\"], [\"https://github.com/astral-sh/python-build-standalone/releases/download/20250818/SHA256SUMS\", \"sha256:87482f19522742956f41753960813b664ec5d2ed5deadf9ac9df52e35cdde105\"]]}\n"
  },
  {
    "path": "testdata/test_package_specifier/local_extras/repeatme/__init__.py",
    "content": ""
  },
  {
    "path": "testdata/test_package_specifier/local_extras/repeatme/main.py",
    "content": "import sys\n\ntry:\n    import pycowsay.main\n\n    has_pycowsay = True\nexcept ImportError:\n    has_pycowsay = False\n\n\ndef main():\n    print(f\"You said:\\n    {' '.join(sys.argv[1:])}\")\n\n    if has_pycowsay:\n        print()\n        print(\"In cow, you said:\")\n        pycowsay.main.main()\n"
  },
  {
    "path": "testdata/test_package_specifier/local_extras/setup.py",
    "content": "from setuptools import setup\n\nsetup(\n    name=\"repeatme\",\n    version=0.1,\n    description=\"Repeat arguments.\",\n    packages=[\"repeatme\"],\n    extras_require={\"cow\": [\"pycowsay==0.0.0.2\"]},\n    entry_points={\"console_scripts\": [\"repeatme=repeatme.main:main\"]},\n)\n"
  },
  {
    "path": "testdata/tests_packages/README.md",
    "content": "# Introduction\n\n`primary_packages.txt` is the master list, containing all packages\ninstalled or injected in the pipx tests `tests`.  Platform-specific list files\nlisting both these primary packages and their dependencies are generated from\nit.  These platform-specific list files are used to populate the directory\n`.pipx_tests/package_cache`.\n\n# Generating the platform-specific lists from the master list\n\nUsing the Github Workflow\n* Make sure that the file in this directory `primary_packages.txt` is up to date for every package & version that is installed or injected in the tests.\n* Manually activate the Github workflow: Create tests package lists for offline tests\n* Download the artifact `lists` and put the files from it into this directory.\n\nOr to locally generate these lists, on the target platform execute:\n* `nox -s create_test_package_list`\n\n# Updating / Populating the directory `.pipx_tests/package_cache` before running the tests\nPre-populating this directory allows the pipx `tests` to run completely offline.\n\nNox instructions\n* execute `nox -s refresh_packages_cache`\n\nOr manually execute from the top-level pipx repo directory:\n* `mkdir -p .pipx_tests/package_cache`\n* `python3 scripts/update_package_cache.py testdata/tests_packages .pipx_tests/package_cache`\n"
  },
  {
    "path": "tests/conftest.py",
    "content": "import json\nimport os\nimport shutil\nimport socket\nimport subprocess\nimport sys\nfrom collections.abc import Iterator\nfrom contextlib import closing\nfrom http import HTTPStatus\nfrom pathlib import Path\nfrom urllib.error import HTTPError, URLError\nfrom urllib.request import urlopen\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom helpers import WIN\nfrom pipx import commands, interpreter, paths, shared_libs, standalone_python, venv\n\nPIPX_TESTS_DIR = Path(\".pipx_tests\")\nPIPX_TESTS_PACKAGE_LIST_DIR = Path(\"testdata/tests_packages\")\n\n\n@pytest.fixture(scope=\"session\")\ndef root() -> Path:\n    return Path(__file__).parents[1]\n\n\n@pytest.fixture()\ndef mocked_github_api(monkeypatch, root):\n    \"\"\"\n    Fixture to replace the github index with a local copy,\n    to prevent unit tests from exceeding github's API request limit.\n    \"\"\"\n    with open(root / \"testdata\" / \"standalone_python_index_20250818.json\") as f:\n        index = json.load(f)\n    monkeypatch.setattr(standalone_python, \"get_or_update_index\", lambda _: index)\n\n\ndef pytest_addoption(parser):\n    parser.addoption(\n        \"--all-packages\",\n        action=\"store_true\",\n        dest=\"all_packages\",\n        default=False,\n        help=\"Run only the long, slow tests installing the maximum list of packages.\",\n    )\n    parser.addoption(\n        \"--net-pypiserver\",\n        action=\"store_true\",\n        dest=\"net_pypiserver\",\n        default=False,\n        help=\"Start local pypi server and use in tests.\",\n    )\n\n\ndef pytest_configure(config):\n    markexpr = getattr(config.option, \"markexpr\", \"\")\n\n    if config.option.all_packages:\n        new_markexpr = (f\"{markexpr} or \" if markexpr else \"\") + \"all_packages\"\n    else:\n        new_markexpr = (f\"{markexpr} and \" if markexpr else \"\") + \"not all_packages\"\n\n    config.option.markexpr = new_markexpr\n\n\ndef pipx_temp_env_helper(pipx_shared_dir, tmp_path, monkeypatch, request, utils_temp_dir, pypi):\n    home_dir = Path(tmp_path) / \"subdir\" / \"pipxhome\"\n    bin_dir = Path(tmp_path) / \"otherdir\" / \"pipxbindir\"\n    man_dir = Path(tmp_path) / \"otherdir\" / \"pipxmandir\"\n\n    global_home_dir = Path(tmp_path) / \"global\" / \"pipxhome\"\n    global_bin_dir = Path(tmp_path) / \"global_otherdir\" / \"pipxbindir\"\n    global_man_dir = Path(tmp_path) / \"global_otherdir\" / \"pipxmandir\"\n\n    # Patch in test specific paths\n    monkeypatch.setattr(paths, \"OVERRIDE_PIPX_HOME\", home_dir)\n    monkeypatch.setattr(paths, \"OVERRIDE_PIPX_BIN_DIR\", bin_dir)\n    monkeypatch.setattr(paths, \"OVERRIDE_PIPX_MAN_DIR\", man_dir)\n    monkeypatch.setattr(paths, \"OVERRIDE_PIPX_SHARED_LIBS\", pipx_shared_dir)\n    monkeypatch.setattr(paths, \"OVERRIDE_PIPX_GLOBAL_HOME\", global_home_dir)\n    monkeypatch.setattr(paths, \"OVERRIDE_PIPX_GLOBAL_BIN_DIR\", global_bin_dir)\n    monkeypatch.setattr(paths, \"OVERRIDE_PIPX_GLOBAL_MAN_DIR\", global_man_dir)\n    # Refresh paths.ctx to commit the overrides\n    paths.ctx.make_local()\n\n    # Reset internal state of shared_libs\n    monkeypatch.setattr(shared_libs, \"shared_libs\", shared_libs._SharedLibs())\n    monkeypatch.setattr(venv, \"shared_libs\", shared_libs.shared_libs)\n\n    monkeypatch.setattr(interpreter, \"DEFAULT_PYTHON\", sys.executable)\n\n    if \"PIPX_DEFAULT_PYTHON\" in os.environ:\n        monkeypatch.delenv(\"PIPX_DEFAULT_PYTHON\")\n\n    # macOS needs /usr/bin in PATH to compile certain packages, but\n    #   applications in /usr/bin cause test_install.py tests to raise warnings\n    #   which make tests fail (e.g. on Github ansible apps exist in /usr/bin)\n    monkeypatch.setenv(\"PATH_ORIG\", str(paths.ctx.bin_dir) + os.pathsep + os.environ[\"PATH\"])\n    monkeypatch.setenv(\"PATH_TEST\", str(paths.ctx.bin_dir))\n    monkeypatch.setenv(\"PATH\", str(paths.ctx.bin_dir) + os.pathsep + str(utils_temp_dir))\n    # On Windows, monkeypatch pipx.commands.common._can_symlink_cache to\n    #   indicate that paths.ctx.bin_dir and paths.ctx.man_dir\n    #   cannot use symlinks, even if we're running as administrator and\n    #   symlinks are actually possible.\n    if WIN:\n        monkeypatch.setitem(commands.common._can_symlink_cache, paths.ctx.bin_dir, False)\n        monkeypatch.setitem(commands.common._can_symlink_cache, paths.ctx.man_dir, False)\n    if not request.config.option.net_pypiserver:\n        # IMPORTANT: use 127.0.0.1 not localhost\n        #   Using localhost on Windows creates enormous slowdowns\n        #   (for some reason--perhaps IPV6/IPV4 tries, timeouts?)\n        monkeypatch.setenv(\"PIP_INDEX_URL\", pypi)\n\n\n@pytest.fixture(scope=\"session\", autouse=True)\ndef pipx_local_pypiserver(request, root: Path, tmp_path_factory) -> Iterator[str]:\n    \"\"\"Starts local pypiserver once per session unless --net-pypiserver was\n    passed to pytest\"\"\"\n    if request.config.option.net_pypiserver:\n        # need both yield and return because other codepath has both\n        yield \"\"\n        return\n\n    pipx_cache_dir = root / PIPX_TESTS_DIR / \"package_cache\"\n    check_test_packages_cmd = [\n        sys.executable,\n        \"scripts/update_package_cache.py\",\n        \"--check-only\",\n        str(PIPX_TESTS_PACKAGE_LIST_DIR),\n        str(pipx_cache_dir),\n    ]\n    update_test_packages_cmd = [\n        sys.executable,\n        \"scripts/update_package_cache.py\",\n        str(PIPX_TESTS_PACKAGE_LIST_DIR),\n        str(pipx_cache_dir),\n    ]\n    check_test_packages_process = subprocess.run(check_test_packages_cmd, check=False, cwd=root)\n    if check_test_packages_process.returncode != 0:\n        subprocess.run(update_test_packages_cmd, check=True, cwd=root)\n\n    def find_free_port():\n        with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:\n            s.bind((\"\", 0))\n            return s.getsockname()[1]\n\n    server_log = tmp_path_factory.mktemp(\"log\") / \"pypiserver.log\"\n    if server_log.exists():\n        server_log.unlink()\n    port = find_free_port()\n    os.environ[\"NO_PROXY\"] = \"127.0.0.1\"\n    cache = str(pipx_cache_dir / f\"{sys.version_info[0]}.{sys.version_info[1]}\")\n    server = str(Path(sys.executable).parent / \"pypi-server\")\n    cmd = [\n        server,\n        \"run\",\n        \"--verbose\",\n        \"--disable-fallback\",\n        \"--backend\",\n        \"cached-dir\",\n        \"--cache-control=3600\",\n        \"--host\",\n        \"127.0.0.1\",\n        \"--port\",\n        str(port),\n        cache,\n    ]\n    cmd += [\"--log-file\", str(server_log)]\n    pypiserver_process = subprocess.Popen(cmd, cwd=root)\n    url = f\"http://127.0.0.1:{port}/simple/\"\n    while True:\n        try:\n            with urlopen(url) as response:\n                if response.code == HTTPStatus.OK:\n                    break\n        except (URLError, HTTPError):\n            continue\n    yield url\n    pypiserver_process.terminate()\n\n\n@pytest.fixture(scope=\"session\")\ndef pipx_session_shared_dir(tmp_path_factory):\n    \"\"\"Makes a temporary pipx shared libs directory only once per session\"\"\"\n    return tmp_path_factory.mktemp(\"session_shareddir\")\n\n\n@pytest.fixture(scope=\"session\")\ndef utils_temp_dir(tmp_path_factory):\n    tmp_path = tmp_path_factory.mktemp(\"session_utilstempdir\")\n    utils = [\"git\"]\n    for util in utils:\n        at_path = shutil.which(util)\n        assert at_path is not None\n        util_path = Path(at_path)\n        try:\n            (tmp_path / util_path.name).symlink_to(util_path)\n        except FileExistsError:\n            pass\n    return tmp_path\n\n\n@pytest.fixture\ndef pipx_temp_env(tmp_path, monkeypatch, pipx_session_shared_dir, request, utils_temp_dir, pipx_local_pypiserver):\n    \"\"\"Sets up temporary paths for pipx to install into.\n\n    Shared libs are setup once per session, all other pipx dirs, constants are\n    recreated for every test function.\n\n    Also adds environment variables as necessary to make pip installations\n    seamless.\n    \"\"\"\n    pipx_temp_env_helper(pipx_session_shared_dir, tmp_path, monkeypatch, request, utils_temp_dir, pipx_local_pypiserver)\n    yield\n    paths.ctx.make_local()\n\n\n@pytest.fixture\ndef pipx_ultra_temp_env(tmp_path, monkeypatch, request, utils_temp_dir, pipx_local_pypiserver):\n    \"\"\"Sets up temporary paths for pipx to install into.\n\n    Fully temporary environment, every test function starts as if pipx has\n    never been run before, including empty shared libs directory.\n\n    Also adds environment variables as necessary to make pip installations\n    seamless.\n    \"\"\"\n    shared_dir = Path(tmp_path) / \"shareddir\"\n    pipx_temp_env_helper(shared_dir, tmp_path, monkeypatch, request, utils_temp_dir, pipx_local_pypiserver)\n    yield\n    paths.ctx.make_local()\n"
  },
  {
    "path": "tests/helpers.py",
    "content": "import json\nimport os\nimport re\nimport sys\nfrom dataclasses import replace\nfrom pathlib import Path\nfrom typing import Any, Optional\nfrom unittest import mock\n\nimport pytest  # type: ignore[import-not-found]\nfrom packaging.utils import canonicalize_name\n\nfrom package_info import PKG\nfrom pipx import constants, main, paths, pipx_metadata_file, util\n\nWIN = sys.platform.startswith(\"win\")\n\nPIPX_METADATA_LEGACY_VERSIONS = [None, \"0.1\", \"0.2\", \"0.3\"]\n\nMOCK_PIPXMETADATA_0_1: dict[str, Any] = {\n    \"main_package\": None,\n    \"python_version\": None,\n    \"venv_args\": [],\n    \"injected_packages\": {},\n    \"pipx_metadata_version\": \"0.1\",\n}\n\nMOCK_PIPXMETADATA_0_2: dict[str, Any] = {\n    \"main_package\": None,\n    \"python_version\": None,\n    \"venv_args\": [],\n    \"injected_packages\": {},\n    \"pipx_metadata_version\": \"0.2\",\n}\n\nMOCK_PIPXMETADATA_0_3: dict[str, Any] = {\n    \"main_package\": None,\n    \"python_version\": None,\n    \"venv_args\": [],\n    \"injected_packages\": {},\n    \"pipx_metadata_version\": \"0.3\",\n    \"man_pages\": [],\n    \"man_paths\": [],\n    \"man_pages_of_dependencies\": [],\n    \"man_paths_of_dependencies\": {},\n}\n\nMOCK_PACKAGE_INFO_0_1: dict[str, Any] = {\n    \"package\": None,\n    \"package_or_url\": None,\n    \"pip_args\": [],\n    \"include_dependencies\": False,\n    \"include_apps\": True,\n    \"apps\": [],\n    \"app_paths\": [],\n    \"apps_of_dependencies\": [],\n    \"app_paths_of_dependencies\": {},\n    \"package_version\": \"\",\n}\n\nMOCK_PACKAGE_INFO_0_2: dict[str, Any] = {\n    \"package\": None,\n    \"package_or_url\": None,\n    \"pip_args\": [],\n    \"include_dependencies\": False,\n    \"include_apps\": True,\n    \"apps\": [],\n    \"app_paths\": [],\n    \"apps_of_dependencies\": [],\n    \"app_paths_of_dependencies\": {},\n    \"package_version\": \"\",\n    \"suffix\": \"\",\n}\n\n\ndef app_name(app: str) -> str:\n    return f\"{app}.exe\" if WIN else app\n\n\ndef run_pipx_cli(pipx_args: list[str]) -> int:\n    with mock.patch.object(sys, \"argv\", [\"pipx\"] + pipx_args):\n        return main.cli()\n\n\ndef unwrap_log_text(log_text: str):\n    \"\"\"Remove line-break + indent space from log messages\n\n    Captured log lines always start with the 'severity' so if a line starts\n    with any spaces assume it is due to an indented pipx wrapped message.\n    \"\"\"\n\n    return re.sub(r\"\\n\\s+\", \" \", log_text)\n\n\ndef _mock_legacy_package_info(modern_package_info: dict[str, Any], metadata_version: str) -> dict[str, Any]:\n    if metadata_version in [\"0.2\", \"0.3\"]:\n        mock_package_info_template = MOCK_PACKAGE_INFO_0_2\n    elif metadata_version == \"0.1\":\n        mock_package_info_template = MOCK_PACKAGE_INFO_0_1\n    else:\n        raise Exception(f\"Internal Test Error: Unknown metadata_version={metadata_version}\")\n\n    mock_package_info = {}\n    for key in mock_package_info_template:\n        mock_package_info[key] = modern_package_info[key]\n\n    return mock_package_info\n\n\ndef mock_legacy_venv(venv_name: str, metadata_version: Optional[str] = None) -> None:\n    \"\"\"Convert a venv installed with the most recent pipx to look like\n    one with a previous metadata version.\n    metadata_version=None refers to no metadata file (pipx pre-0.15.0.0)\n    \"\"\"\n    venv_dir = Path(paths.ctx.venvs) / canonicalize_name(venv_name)\n\n    if metadata_version == \"0.4\":\n        # Current metadata version, do nothing\n        return\n    elif metadata_version == \"0.3\":\n        mock_pipx_metadata_template = MOCK_PIPXMETADATA_0_3\n    elif metadata_version == \"0.2\":\n        mock_pipx_metadata_template = MOCK_PIPXMETADATA_0_2\n    elif metadata_version == \"0.1\":\n        mock_pipx_metadata_template = MOCK_PIPXMETADATA_0_1\n    elif metadata_version is None:\n        # No metadata\n        os.remove(venv_dir / \"pipx_metadata.json\")\n        return\n    else:\n        raise Exception(f\"Internal Test Error: Unknown metadata_version={metadata_version}\")\n\n    modern_metadata = pipx_metadata_file.PipxMetadata(venv_dir).to_dict()\n\n    # Convert to mock old metadata\n    mock_pipx_metadata: dict[str, Any] = {}\n    for key in mock_pipx_metadata_template:\n        if key == \"main_package\":\n            mock_pipx_metadata[key] = _mock_legacy_package_info(modern_metadata[key], metadata_version=metadata_version)\n        if key == \"injected_packages\":\n            mock_pipx_metadata[key] = {}\n            for injected in modern_metadata[key]:\n                mock_pipx_metadata[key][injected] = _mock_legacy_package_info(\n                    modern_metadata[key][injected], metadata_version=metadata_version\n                )\n        else:\n            mock_pipx_metadata[key] = modern_metadata.get(key)\n    mock_pipx_metadata[\"pipx_metadata_version\"] = mock_pipx_metadata_template[\"pipx_metadata_version\"]\n\n    # replicate pipx_metadata_file.PipxMetadata.write()\n    with open(venv_dir / \"pipx_metadata.json\", \"w\") as pipx_metadata_fh:\n        json.dump(\n            mock_pipx_metadata,\n            pipx_metadata_fh,\n            indent=4,\n            sort_keys=True,\n            cls=pipx_metadata_file.JsonEncoderHandlesPath,\n        )\n\n\ndef create_package_info_ref(venv_name, package_name, pipx_venvs_dir, **field_overrides):\n    \"\"\"Create reference PackageInfo to check against\n\n    Overridable fields to be used in field_overrides:\n        pip_args (default: [])\n        include_apps (default: True)\n        include_dependencies (default: False)\n        app_paths_of_dependencies (default: {})\n    \"\"\"\n    venv_bin_dir = \"Scripts\" if constants.WINDOWS else \"bin\"\n    return pipx_metadata_file.PackageInfo(\n        package=package_name,\n        package_or_url=PKG[package_name][\"spec\"],\n        pip_args=field_overrides.get(\"pip_args\", []),\n        include_apps=field_overrides.get(\"include_apps\", True),\n        include_dependencies=field_overrides.get(\"include_dependencies\", False),\n        apps=PKG[package_name][\"apps\"],\n        app_paths=[pipx_venvs_dir / venv_name / venv_bin_dir / app for app in PKG[package_name][\"apps\"]],\n        apps_of_dependencies=PKG[package_name][\"apps_of_dependencies\"],\n        app_paths_of_dependencies=field_overrides.get(\"app_paths_of_dependencies\", {}),\n        man_pages=PKG[package_name].get(\"man_pages\", []),\n        man_paths=[\n            pipx_venvs_dir / venv_name / \"share\" / \"man\" / man_page\n            for man_page in PKG[package_name].get(\"man_pages\", [])\n        ],\n        man_pages_of_dependencies=PKG[package_name].get(\"man_pages_of_dependencies\", []),\n        man_paths_of_dependencies=field_overrides.get(\"man_paths_of_dependencies\", {}),\n        package_version=PKG[package_name][\"spec\"].split(\"==\")[-1],\n    )\n\n\ndef assert_package_metadata(test_metadata, ref_metadata):\n    # only compare sorted versions of apps, app_paths so order is not important\n\n    assert test_metadata.package_version != \"\"\n    assert isinstance(test_metadata.apps, list)\n    assert isinstance(test_metadata.app_paths, list)\n\n    test_metadata_replaced = replace(\n        test_metadata,\n        apps=sorted(test_metadata.apps),\n        app_paths=sorted(test_metadata.app_paths),\n        apps_of_dependencies=sorted(test_metadata.apps_of_dependencies),\n        app_paths_of_dependencies={key: sorted(value) for key, value in test_metadata.app_paths_of_dependencies.items()},\n    )\n    ref_metadata_replaced = replace(\n        ref_metadata,\n        apps=sorted(ref_metadata.apps),\n        app_paths=sorted(ref_metadata.app_paths),\n        apps_of_dependencies=sorted(ref_metadata.apps_of_dependencies),\n        app_paths_of_dependencies={key: sorted(value) for key, value in ref_metadata.app_paths_of_dependencies.items()},\n    )\n    assert test_metadata_replaced == ref_metadata_replaced\n\n\ndef remove_venv_interpreter(venv_name):\n    _, venv_python_path, _ = util.get_venv_paths(paths.ctx.venvs / venv_name)\n    assert venv_python_path.is_file()\n    venv_python_path.unlink()\n    assert not venv_python_path.is_file()\n\n\nskip_if_windows = pytest.mark.skipif(sys.platform.startswith(\"win\"), reason=\"This behavior is undefined on Windows\")\n"
  },
  {
    "path": "tests/package_info.py",
    "content": "import sys\nfrom pathlib import Path\nfrom typing import Any\n\nWIN = sys.platform.startswith(\"win\")\n\n\ndef _exe_if_win(apps):\n    return [f\"{app}.exe\" if WIN else app for app in apps]\n\n\n# Versions of all packages possibly used in our tests\n# Only apply _exe_if_win to entry_points, NOT scripts\nPKG: dict[str, dict[str, Any]] = {\n    \"ansible\": {\n        \"spec\": \"ansible==6.7.0\",\n        \"apps\": [\n            \"ansible\",\n            \"ansible-config\",\n            \"ansible-connection\",\n            \"ansible-console\",\n            \"ansible-doc\",\n            \"ansible-galaxy\",\n            \"ansible-inventory\",\n            \"ansible-playbook\",\n            \"ansible-pull\",\n            \"ansible-test\",\n            \"ansible-vault\",\n        ],\n        \"apps_of_dependencies\": [],\n    },\n    \"awscli\": {\n        \"spec\": \"awscli==1.18.168\",\n        \"apps\": [\n            \"aws\",\n            \"aws.cmd\",\n            \"aws_bash_completer\",\n            \"aws_completer\",\n            \"aws_zsh_completer.sh\",\n        ],\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"pyrsa-decrypt\",  # rsa EXE\n                \"pyrsa-encrypt\",  # rsa EXE\n                \"pyrsa-keygen\",  # rsa EXE\n                \"pyrsa-priv2pub\",  # rsa EXE\n                \"pyrsa-sign\",  # rsa EXE\n                \"pyrsa-verify\",  # rsa EXE\n            ]\n        )\n        + [\n            \"jp.py\",  # jmespath.py NO_EXE\n            \"rst2html.py\",  # docutils NO_EXE\n            \"rst2html4.py\",  # docutils NO_EXE\n            \"rst2html5.py\",  # docutils NO_EXE\n            \"rst2latex.py\",  # docutils NO_EXE\n            \"rst2man.py\",  # docutils NO_EXE\n            \"rst2odt.py\",  # docutils NO_EXE\n            \"rst2odt_prepstyles.py\",  # docutils NO_EXE\n            \"rst2pseudoxml.py\",  # docutils NO_EXE\n            \"rst2s5.py\",  # docutils NO_EXE\n            \"rst2xetex.py\",  # docutils NO_EXE\n            \"rst2xml.py\",  # docutils NO_EXE\n            \"rstpep2html.py\",  # docutils NO_EXE\n        ],\n    },\n    \"b2\": {\n        \"spec\": \"b2==3.12.0\",\n        \"apps\": _exe_if_win([\"b2\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"docutils\", \"rst2ansi\", \"tabulate\", \"tqdm\", \"normalizer\"])\n        + [\n            \"activate-global-python-argcomplete\",\n            \"python-argcomplete-check-easy-install-script\",\n            \"register-python-argcomplete\",\n            \"rst2html4.py\",\n            \"rst2html5.py\",\n            \"rst2html.py\",\n            \"rst2latex.py\",\n            \"rst2man.py\",\n            \"rst2odt_prepstyles.py\",\n            \"rst2odt.py\",\n            \"rst2pseudoxml.py\",\n            \"rst2s5.py\",\n            \"rst2xetex.py\",\n            \"rst2xml.py\",\n            \"rstpep2html.py\",\n        ],\n    },\n    \"beancount\": {\n        \"spec\": \"beancount==2.3.6\",\n        \"apps\": _exe_if_win(\n            [\n                \"bean-bake\",\n                \"bean-check\",\n                \"bean-doctor\",\n                \"bean-example\",\n                \"bean-extract\",\n                \"bean-file\",\n                \"bean-format\",\n                \"bean-identify\",\n                \"bean-price\",\n                \"bean-query\",\n                \"bean-report\",\n                \"bean-sql\",\n                \"bean-web\",\n                \"treeify\",\n                \"upload-to-sheets\",\n            ]\n        ),\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"chardetect\",  # chardet EXE\n                \"py.test\",  # pytest EXE\n                \"pyrsa-decrypt\",  # rsa EXE\n                \"pyrsa-encrypt\",  # rsa EXE\n                \"pyrsa-keygen\",  # rsa EXE\n                \"pyrsa-priv2pub\",  # rsa EXE\n                \"pyrsa-sign\",  # rsa EXE\n                \"pyrsa-verify\",  # rsa EXE\n                \"pytest\",  # pytest EXE\n                \"normalizer\",\n                \"py.test\",\n            ]\n        )\n        + [\"bottle.py\", \"dumppdf.py\", \"latin2ascii.py\", \"pdf2txt.py\"],  # bottle NO_EXE\n    },\n    \"beets\": {\n        \"spec\": \"beets==1.4.9\",\n        \"apps\": _exe_if_win([\"beet\"]),\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"mid3cp\",\n                \"mid3iconv\",\n                \"mid3v2\",\n                \"moggsplit\",\n                \"mutagen-inspect\",\n                \"mutagen-pony\",\n                \"unidecode\",  # unidecode EXE\n            ]\n        ),\n        \"man_pages\": [],\n        \"man_pages_of_dependencies\": [\n            str(Path(\"man1\") / \"mutagen-pony.1\"),\n            str(Path(\"man1\") / \"mutagen-inspect.1\"),\n            str(Path(\"man1\") / \"moggsplit.1\"),\n            str(Path(\"man1\") / \"mid3v2.1\"),\n            str(Path(\"man1\") / \"mid3iconv.1\"),\n            str(Path(\"man1\") / \"mid3cp.1\"),\n        ],\n    },\n    \"black\": {\n        \"spec\": \"black==22.8.0\",\n        \"apps\": _exe_if_win([\"black\", \"blackd\"]),\n        \"apps_of_dependencies\": [],\n    },\n    \"cactus\": {\n        \"spec\": \"cactus==3.3.3\",\n        \"apps\": _exe_if_win([\"cactus\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"keyring\", \"markdown2\"])\n        + [\n            \"asadmin\",\n            \"bundle_image\",\n            \"cfadmin\",\n            \"cq\",\n            \"cwutil\",\n            \"django-admin.py\",\n            \"dynamodb_dump\",\n            \"dynamodb_load\",\n            \"elbadmin\",\n            \"fetch_file\",\n            \"glacier\",\n            \"instance_events\",\n            \"keyring\",\n            \"kill_instance\",\n            \"launch_instance\",\n            \"list_instances\",\n            \"lss3\",\n            \"mturk\",\n            \"pyami_sendmail\",\n            \"route53\",\n            \"s3put\",\n            \"sdbadmin\",\n            \"taskadmin\",\n        ],\n    },\n    \"chert\": {\n        \"spec\": \"chert==19.1.0\",\n        \"apps\": _exe_if_win([\"chert\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"ashes\", \"markdown_py\"]) + [\"ashes.py\"],\n    },\n    # \"cloudtoken\": {\n    #     \"spec\": \"cloudtoken==2.1.0\",\n    #     \"apps\": [\"awstoken\", \"cloudtoken\", \"cloudtoken.app\", \"cloudtoken_proxy.sh\"],\n    #     \"apps_of_dependencies\": _exe_if_win([\"flask\", \"keyring\", \"normalizer\"]) + [\"jp.py\"],\n    # },\n    \"coala\": {\n        \"spec\": \"coala==0.11.0\",\n        \"apps\": _exe_if_win([\"coala\", \"coala-ci\", \"coala-delete-orig\", \"coala-format\", \"coala-json\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"normalizer\", \"pygmentize\"]) + [\"unidiff\"],\n    },\n    \"cookiecutter\": {\n        \"spec\": \"cookiecutter==2.4.0\",\n        \"apps\": _exe_if_win([\"cookiecutter\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"chardetect\", \"normalizer\", \"markdown-it\", \"pygmentize\", \"slugify\"]),\n    },\n    \"cython\": {\n        \"spec\": \"cython==0.29.21\",\n        \"apps\": _exe_if_win([\"cygdb\", \"cython\", \"cythonize\"]),\n        \"apps_of_dependencies\": [],\n    },\n    \"datasette\": {\n        \"spec\": \"datasette==0.50.2\",\n        \"apps\": _exe_if_win([\"datasette\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"httpx\", \"hupper\", \"uvicorn\"]) + [\"pint-convert\"],\n    },\n    \"diffoscope\": {\n        \"spec\": \"diffoscope==154\",\n        \"apps\": _exe_if_win([\"diffoscope\"]),\n        \"apps_of_dependencies\": [],\n    },\n    \"doc2dash\": {\n        \"spec\": \"doc2dash==3.0.0\",\n        \"apps\": _exe_if_win([\"doc2dash\"]),\n        \"apps_of_dependencies\": _exe_if_win(\n            [\"markdown-it\", \"pygmentize\"]  # pygments EXE\n        ),\n    },\n    \"doitlive\": {\n        \"spec\": \"doitlive==4.3.0\",\n        \"apps\": _exe_if_win([\"doitlive\"]),\n        \"apps_of_dependencies\": [],\n    },\n    \"gdbgui\": {\n        \"spec\": \"gdbgui==0.14.0.1\",\n        \"apps\": _exe_if_win([\"gdbgui\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"flask\", \"pygmentize\"]),\n    },\n    \"gns3-gui\": {\n        \"spec\": \"gns3-gui==2.2.15\",\n        \"apps\": _exe_if_win([\"gns3\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"distro\", \"jsonschema\"]),\n    },\n    \"grow\": {\n        \"spec\": \"grow==1.0.0a10\",\n        \"apps\": [\"grow\"],\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"chardetect\",  # chardet EXE\n                \"gen_protorpc\",  # EXE\n                \"html2text\",  # html2text EXE\n                \"markdown_py\",  # Markdown EXE\n                \"pybabel\",  # babel EXE\n                \"pygmentize\",  # pygments EXE\n                \"pyrsa-decrypt\",  # rsa EXE\n                \"pyrsa-encrypt\",  # rsa EXE\n                \"pyrsa-keygen\",  # rsa EXE\n                \"pyrsa-priv2pub\",  # rsa EXE\n                \"pyrsa-sign\",  # rsa EXE\n                \"pyrsa-verify\",  # rsa EXE\n                \"slugify\",  # python_slugify EXE\n                \"watchmedo\",  # watchdog EXE\n            ]\n        ),\n    },\n    \"guake\": {\n        \"spec\": \"guake==3.7.0\",\n        \"apps\": _exe_if_win([\"guake\", \"guake-toggle\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"pbr\"]),\n    },\n    \"gunicorn\": {\n        \"spec\": \"gunicorn==20.0.4\",\n        \"apps\": _exe_if_win([\"gunicorn\"]),\n        \"apps_of_dependencies\": [],\n    },\n    \"howdoi\": {\n        \"spec\": \"howdoi==2.0.20\",\n        \"apps\": _exe_if_win([\"howdoi\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"markdown-it\", \"keep\", \"normalizer\", \"pygmentize\"]),\n    },\n    \"httpie\": {\n        \"spec\": \"httpie==3.2.2\",\n        \"apps\": _exe_if_win([\"http\", \"httpie\", \"https\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"markdown-it\", \"normalizer\", \"pygmentize\"]),\n        \"man_pages\": [\n            str(Path(\"man1\") / \"http.1\"),\n            str(Path(\"man1\") / \"httpie.1\"),\n            str(Path(\"man1\") / \"https.1\"),\n        ],\n        \"man_pages_of_dependencies\": [],\n    },\n    \"hyde\": {\n        \"spec\": \"hyde==0.8.9\",\n        \"apps\": _exe_if_win([\"hyde\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"markdown_py\", \"pygmentize\"]) + [\"smartypants\"],\n    },\n    \"ipython\": {\n        \"spec\": \"ipython==7.16.1\",\n        \"apps\": _exe_if_win([\"iptest\", \"iptest3\", \"ipython\", \"ipython3\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"pygmentize\"]),  # pygments EXE\n        \"man_pages\": [str(Path(\"man1\") / \"ipython.1.gz\")],\n        \"man_pages_of_dependencies\": [],\n    },\n    \"isort\": {\n        \"spec\": \"isort==5.6.4\",\n        \"apps\": _exe_if_win([\"isort\"]),\n        \"apps_of_dependencies\": [],\n    },\n    \"zest-releaser\": {\n        \"spec\": \"zest.releaser==9.1.2\",\n        \"apps\": _exe_if_win(\n            [\n                \"addchangelogentry\",\n                \"bumpversion\",\n                \"fullrelease\",\n                \"lasttagdiff\",\n                \"lasttaglog\",\n                \"longtest\",\n                \"postrelease\",\n                \"prerelease\",\n                \"release\",\n            ]\n        ),\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"markdown-it\",\n                \"normalizer\",\n                \"twine\",\n                \"pkginfo\",\n                \"pyproject-build\",\n                \"docutils\",\n                \"pygmentize\",\n                \"keyring\",\n            ]\n        )\n        + [\n            \"rst2html.py\",\n            \"rst2html4.py\",\n            \"rst2html5.py\",\n            \"rst2latex.py\",\n            \"rst2man.py\",\n            \"rst2odt.py\",\n            \"rst2odt_prepstyles.py\",\n            \"rst2pseudoxml.py\",\n            \"rst2s5.py\",\n            \"rst2xetex.py\",\n            \"rst2xml.py\",\n            \"rstpep2html.py\",\n        ],\n    },\n    \"jupyter\": {\n        \"spec\": \"jupyter==1.0.0\",\n        \"apps\": [],\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"iptest\",  # EXE\n                \"iptest3\",  # EXE\n                \"ipython\",  # EXE\n                \"ipython3\",  # EXE\n                \"jsonschema\",  # jsonschema EXE\n                \"jupyter\",  # EXE\n                \"jupyter-bundlerextension\",  # EXE\n                \"jupyter-console\",  # EXE\n                \"jupyter-kernel\",  # EXE\n                \"jupyter-kernelspec\",  # EXE\n                \"jupyter-migrate\",  # EXE\n                \"jupyter-nbconvert\",  # EXE\n                \"jupyter-nbextension\",  # EXE\n                \"jupyter-notebook\",  # EXE\n                \"jupyter-qtconsole\",  # EXE\n                \"jupyter-run\",  # EXE\n                \"jupyter-serverextension\",  # EXE\n                \"jupyter-troubleshoot\",  # EXE\n                \"jupyter-trust\",  # EXE\n                \"pygmentize\",  # pygments EXE\n            ]\n        ),\n    },\n    \"kaggle\": {\n        \"spec\": \"kaggle==1.6.11\",\n        \"apps\": _exe_if_win([\"kaggle\"]),\n        \"apps_of_dependencies\": list(set(_exe_if_win([\"slugify\", \"normalizer\", \"tqdm\"]))),\n    },\n    \"kibitzr\": {\n        \"spec\": \"kibitzr==7.0.5\",\n        \"apps\": _exe_if_win([\"kibitzr\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"doesitcache\", \"httpx\", \"normalizer\"]),\n    },\n    \"klaus\": {\n        \"spec\": \"klaus==1.5.2\",\n        \"apps\": [\"klaus\"],\n        \"apps_of_dependencies\": _exe_if_win([\"dulwich\", \"flask\", \"pygmentize\"])\n        + [\"dul-receive-pack\", \"dul-upload-pack\"],\n    },\n    \"kolibri\": {\n        \"spec\": \"kolibri==0.14.3\",\n        \"apps\": _exe_if_win([\"kolibri\"]),\n        \"apps_of_dependencies\": [],\n    },\n    \"lektor\": {\n        \"spec\": \"Lektor==3.3.10\",\n        \"apps\": _exe_if_win([\"lektor\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"filetype\", \"flask\", \"pybabel\", \"normalizer\", \"slugify\", \"watchmedo\"])\n        + [\"EXIF.py\"],\n    },\n    \"localstack\": {\n        \"spec\": \"localstack==0.12.1\",\n        \"apps\": [\"localstack\", \"localstack.bat\"],\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"dotenv\",\n                \"markdown-it\",\n                \"pbr\",\n                \"pygmentize\",\n                \"pyrsa-decrypt\",\n                \"pyrsa-encrypt\",\n                \"pyrsa-keygen\",\n                \"pyrsa-priv2pub\",\n                \"pyrsa-sign\",\n                \"pyrsa-verify\",\n                \"pysemver\",\n                \"pytail\",\n                \"tabulate\",\n                \"normalizer\",\n            ]\n        )\n        + [\"get_objgraph\", \"jp.py\", \"localstack-supervisor\", \"undill\"],\n    },\n    \"mackup\": {\n        \"spec\": \"mackup==0.8.29\",\n        \"apps\": _exe_if_win([\"mackup\"]),\n        \"apps_of_dependencies\": [],\n    },  # ONLY FOR mac, linux\n    \"magic-wormhole\": {\n        \"spec\": \"magic-wormhole==0.13.0\",\n        \"apps\": _exe_if_win([\"wormhole\"]),\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"automat-visualize\",  # EXE\n                \"cftp\",  # EXE\n                \"ckeygen\",  # EXE\n                \"conch\",  # EXE\n                \"mailmail\",  # EXE\n                \"pyhtmlizer\",  # EXE\n                \"tkconch\",  # EXE\n                \"tqdm\",  # tqdm EXE\n                \"trial\",  # EXE\n                \"twist\",  # EXE\n                \"twistd\",  # EXE\n                \"wamp\",  # EXE\n                \"xbrnetwork\",  # EXE\n                \"xbrnetwork-ui\",  # EXE\n            ]\n        )\n        + ([\"pywin32_postinstall.py\", \"pywin32_testall.py\"] if WIN else []),\n    },\n    \"mayan-edms\": {\n        \"spec\": \"mayan-edms==3.5.2\",\n        \"apps\": [\"mayan-edms.py\"],\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"celery\",  # EXE\n                \"chardetect\",  # chardet EXE\n                \"django-admin\",  # EXE\n                \"gunicorn\",  # EXE\n                \"jsonschema\",  # jsonschema EXE\n                \"sqlformat\",  # sqlparse EXE\n                \"swagger-flex\",  # EXE\n                \"update-tld-names\",  # # EXE\n            ]\n        )\n        + [\"django-admin.py\", \"jsonpointer\"],\n    },\n    \"mkdocs\": {\n        \"spec\": \"mkdocs==1.1.2\",\n        \"apps\": _exe_if_win([\"mkdocs\"]),\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"livereload\",  # EXE\n                \"futurize\",  # future EXE\n                \"pasteurize\",  # future EXE\n                \"nltk\",  # EXE\n                \"tqdm\",  # tqdm EXE\n                \"markdown_py\",  # Markdown EXE\n            ]\n        ),\n    },\n    \"mycli\": {\n        \"spec\": \"mycli==1.22.2\",\n        \"apps\": _exe_if_win([\"mycli\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"pygmentize\", \"sqlformat\", \"tabulate\"]),\n    },\n    \"nikola\": {\n        \"spec\": \"nikola==8.2.4\",\n        \"apps\": _exe_if_win([\"nikola\"]),\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"docutils\",\n                \"doit\",  # EXE\n                \"mako-render\",  # mako EXE\n                \"markdown_py\",  # Markdown EXE\n                \"natsort\",  # EXE\n                \"pybabel\",  # babel EXE\n                \"pygmentize\",  # pygments EXE\n                \"unidecode\",  # unidecode EXE\n                \"normalizer\",\n            ]\n        )\n        + [\n            \"rst2html.py\",  # docutils NO_EXE\n            \"rst2html4.py\",  # docutils NO_EXE\n            \"rst2html5.py\",  # docutils NO_EXE\n            \"rst2latex.py\",  # docutils NO_EXE\n            \"rst2man.py\",  # docutils NO_EXE\n            \"rst2odt.py\",  # docutils NO_EXE\n            \"rst2odt_prepstyles.py\",  # docutils NO_EXE\n            \"rst2pseudoxml.py\",  # docutils NO_EXE\n            \"rst2s5.py\",  # docutils NO_EXE\n            \"rst2xetex.py\",  # docutils NO_EXE\n            \"rst2xml.py\",  # docutils NO_EXE\n            \"rstpep2html.py\",  # docutils NO_EXE\n        ],\n        \"man_pages\": [str(Path(\"man1\") / \"nikola.1.gz\")],\n        \"man_pages_of_dependencies\": [],\n    },\n    \"nox\": {\n        \"spec\": \"nox==2023.4.22\",\n        \"apps\": _exe_if_win([\"nox\", \"tox-to-nox\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"virtualenv\"])\n        + [\n            \"activate-global-python-argcomplete\",\n            \"python-argcomplete-check-easy-install-script\",\n            \"register-python-argcomplete\",\n        ],  # from argcomplete\n    },\n    \"pbr\": {\"spec\": \"pbr==5.6.0\", \"apps\": _exe_if_win([\"pbr\"])},\n    \"pelican\": {\n        \"spec\": \"pelican==4.8.0\",\n        \"apps\": _exe_if_win(\n            [\n                \"pelican\",\n                \"pelican-import\",\n                \"pelican-plugins\",\n                \"pelican-quickstart\",\n                \"pelican-themes\",\n            ]\n        ),\n        \"apps_of_dependencies\": _exe_if_win([\"docutils\", \"markdown-it\", \"pygmentize\", \"unidecode\"])\n        + [\n            \"rst2html.py\",  # docutils NO_EXE\n            \"rst2html4.py\",  # docutils NO_EXE\n            \"rst2html5.py\",  # docutils NO_EXE\n            \"rst2latex.py\",  # docutils NO_EXE\n            \"rst2man.py\",  # docutils NO_EXE\n            \"rst2odt.py\",  # docutils NO_EXE\n            \"rst2odt_prepstyles.py\",  # docutils NO_EXE\n            \"rst2pseudoxml.py\",  # docutils NO_EXE\n            \"rst2s5.py\",  # docutils NO_EXE\n            \"rst2xetex.py\",  # docutils NO_EXE\n            \"rst2xml.py\",  # docutils NO_EXE\n            \"rstpep2html.py\",  # docutils NO_EXE\n        ],\n    },\n    \"platformio\": {\n        \"spec\": \"platformio==6.1.11\",\n        \"apps\": _exe_if_win([\"pio\", \"piodebuggdb\", \"platformio\"]),\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"async-json-rpc-server\",\n                \"pyserial-miniterm\",\n                \"pyserial-ports\",\n                \"tabulate\",\n                \"uvicorn\",\n                \"normalizer\",\n            ]\n        )\n        + [\"bottle.py\", \"readelf.py\"],\n    },\n    \"ppci\": {\n        \"spec\": \"ppci==0.5.8\",\n        \"apps\": _exe_if_win(\n            [\n                \"ppci-archive\",\n                \"ppci-asm\",\n                \"ppci-build\",\n                \"ppci-c3c\",\n                \"ppci-cc\",\n                \"ppci-dbg\",\n                \"ppci-disasm\",\n                \"ppci-hexdump\",\n                \"ppci-hexutil\",\n                \"ppci-java\",\n                \"ppci-ld\",\n                \"ppci-llc\",\n                \"ppci-mkuimage\",\n                \"ppci-objcopy\",\n                \"ppci-objdump\",\n                \"ppci-ocaml\",\n                \"ppci-opt\",\n                \"ppci-pascal\",\n                \"ppci-pedump\",\n                \"ppci-pycompile\",\n                \"ppci-readelf\",\n                \"ppci-wabt\",\n                \"ppci-wasm2wat\",\n                \"ppci-wasmcompile\",\n                \"ppci-wat2wasm\",\n                \"ppci-yacc\",\n            ]\n        ),\n        \"apps_of_dependencies\": [],\n    },\n    \"prosopopee\": {\n        \"spec\": \"prosopopee==1.1.3\",\n        \"apps\": _exe_if_win([\"prosopopee\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"futurize\", \"pasteurize\", \"pybabel\"]),\n    },\n    \"ptpython\": {\n        \"spec\": \"ptpython==3.0.7\",\n        \"apps\": _exe_if_win(\n            [\n                \"ptipython\",\n                \"ptipython3\",\n                \"ptipython3.8\",\n                \"ptpython\",\n                \"ptpython3\",\n                \"ptpython3.8\",\n            ]\n        ),\n        \"apps_of_dependencies\": _exe_if_win([\"pygmentize\"]),  # pygments EXE\n    },\n    \"pycowsay\": {\n        \"spec\": \"pycowsay==0.0.0.2\",\n        \"apps\": _exe_if_win([\"pycowsay\"]),\n        \"apps_of_dependencies\": [],\n        \"man_pages\": [str(Path(\"man6\") / \"pycowsay.6\")],\n        \"man_pages_of_dependencies\": [],\n    },\n    \"pygdbmi\": {\"spec\": \"pygdbmi==0.10.0.0\", \"apps\": [], \"apps_of_dependencies\": []},\n    \"pylint\": {\n        \"spec\": \"pylint==3.0.4\",\n        \"apps\": _exe_if_win([\"pylint\", \"pylint-config\", \"pyreverse\", \"symilar\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"isort\", \"isort-identify-imports\"])\n        + [\"get_gprof\", \"get_objgraph\", \"undill\"],\n    },\n    \"retext\": {\n        \"spec\": \"ReText==8.0.1\",\n        \"apps\": _exe_if_win([\"retext\"]),\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"docutils\",\n                \"markdown_py\",  # Markdown EXE\n                \"pygmentize\",  # pygments EXE\n                \"pylupdate6\",  # EXE\n                \"pyuic6\",  # EXE\n                \"chardetect\",\n            ]\n        )\n        + [\n            \"rst2html.py\",  # docutils NO_EXE\n            \"rst2html4.py\",  # docutils NO_EXE\n            \"rst2html5.py\",  # docutils NO_EXE\n            \"rst2latex.py\",  # docutils NO_EXE\n            \"rst2man.py\",  # docutils NO_EXE\n            \"rst2odt.py\",  # docutils NO_EXE\n            \"rst2odt_prepstyles.py\",  # docutils NO_EXE\n            \"rst2pseudoxml.py\",  # docutils NO_EXE\n            \"rst2s5.py\",  # docutils NO_EXE\n            \"rst2xetex.py\",  # docutils NO_EXE\n            \"rst2xml.py\",  # docutils NO_EXE\n            \"rstpep2html.py\",  # docutils NO_EXE\n        ],\n    },\n    \"robotframework\": {\n        \"spec\": \"robotframework==3.2.2\",\n        \"apps\": _exe_if_win([\"rebot\", \"robot\"]),\n        \"apps_of_dependencies\": [],\n    },\n    \"shell-functools\": {\n        \"spec\": \"shell-functools==0.3.0\",\n        \"apps\": [\n            \"filter\",\n            \"foldl\",\n            \"foldl1\",\n            \"ft-functions\",\n            \"map\",\n            \"sort_by\",\n            \"take_while\",\n        ],\n        \"apps_of_dependencies\": [],\n    },\n    \"speedtest-cli\": {\n        \"spec\": \"speedtest-cli==2.1.2\",\n        \"apps\": _exe_if_win([\"speedtest\", \"speedtest-cli\"]),\n        \"apps_of_dependencies\": [],\n    },\n    \"sphinx\": {\n        \"spec\": \"Sphinx==7.2.6\",\n        \"apps\": _exe_if_win([\"sphinx-apidoc\", \"sphinx-autogen\", \"sphinx-build\", \"sphinx-quickstart\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"docutils\", \"pybabel\", \"normalizer\", \"pygmentize\"])\n        + [\n            \"rst2html.py\",  # docutils NO_EXE\n            \"rst2html4.py\",  # docutils NO_EXE\n            \"rst2html5.py\",  # docutils NO_EXE\n            \"rst2latex.py\",  # docutils NO_EXE\n            \"rst2man.py\",  # docutils NO_EXE\n            \"rst2odt.py\",  # docutils NO_EXE\n            \"rst2odt_prepstyles.py\",  # docutils NO_EXE\n            \"rst2pseudoxml.py\",  # docutils NO_EXE\n            \"rst2s5.py\",  # docutils NO_EXE\n            \"rst2xetex.py\",  # docutils NO_EXE\n            \"rst2xml.py\",  # docutils NO_EXE\n            \"rstpep2html.py\",  # docutils NO_EXE\n        ],\n    },\n    \"sqlmap\": {\n        \"spec\": \"sqlmap==1.4.10\",\n        \"apps\": _exe_if_win([\"sqlmap\"]),\n        \"apps_of_dependencies\": [],\n    },\n    \"streamlink\": {\n        \"spec\": \"streamlink==6.3.1\",\n        \"apps\": _exe_if_win([\"streamlink\"] + ([\"streamlinkw\"] if WIN else [])),\n        \"apps_of_dependencies\": _exe_if_win([\"normalizer\", \"wsdump\"]),\n        \"man_pages\": [str(Path(\"man1\") / \"streamlink.1\")],\n        \"man_pages_of_dependencies\": [],\n    },\n    \"taguette\": {\n        \"spec\": \"taguette==0.9.2\",\n        \"apps\": _exe_if_win([\"taguette\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"alembic\", \"mako-render\"]) + [\"vba_extract.py\"],\n    },\n    \"term2048\": {\n        \"spec\": \"term2048==0.2.7\",\n        \"apps\": _exe_if_win([\"term2048\"]),\n        \"apps_of_dependencies\": [],\n    },\n    \"tox-ini-fmt\": {\n        \"spec\": \"tox-ini-fmt==0.5.0\",\n        \"apps\": _exe_if_win([\"tox-ini-fmt\"]),\n        \"apps_of_dependencies\": _exe_if_win([\"py.test\", \"pytest\"]),  # pytest EXE\n    },\n    \"visidata\": {\n        \"spec\": \"visidata==2.0.1\",\n        \"apps\": _exe_if_win([\"visidata\"]) + [\"vd\"],\n        \"apps_of_dependencies\": [],\n        \"man_pages\": [str(Path(\"man1\") / \"vd.1\")],\n        \"man_pages_of_dependencies\": [],\n    },\n    \"vulture\": {\n        \"spec\": \"vulture==2.1\",\n        \"apps\": _exe_if_win([\"vulture\"]),\n        \"apps_of_dependencies\": [],\n    },\n    \"weblate\": {\n        \"spec\": \"Weblate==4.3.1\",\n        \"apps\": _exe_if_win([\"weblate\"]),\n        \"apps_of_dependencies\": _exe_if_win(  # TODO: check if _exe_if_win (can't install)\n            [\n                \"borg\",\n                \"borgfs\",\n                \"build_firefox.sh\",\n                \"build_tmdb\",\n                \"buildxpi.py\",\n                \"celery\",\n                \"chardetect\",  # chardet EXE\n                \"csv2po\",\n                \"csv2tbx\",\n                \"cygdb\",\n                \"cython\",\n                \"cythonize\",\n                \"django-admin\",\n                \"django-admin.py\",  # NO_EXE\n                \"flatxml2po\",\n                \"get_moz_enUS.py\",\n                \"html2po\",\n                \"html2text\",  # html2text EXE\n                \"ical2po\",\n                \"idml2po\",\n                \"ini2po\",\n                \"json2po\",\n                \"jsonschema\",  # jsonschema EXE\n                \"junitmsgfmt\",\n                \"misaka\",\n                \"moz2po\",\n                \"mozlang2po\",\n                \"odf2xliff\",\n                \"oo2po\",\n                \"oo2xliff\",\n                \"php2po\",\n                \"phppo2pypo\",\n                \"po2csv\",\n                \"po2flatxml\",\n                \"po2html\",\n                \"po2ical\",\n                \"po2idml\",\n                \"po2ini\",\n                \"po2json\",\n                \"po2moz\",\n                \"po2mozlang\",\n                \"po2oo\",\n                \"po2php\",\n                \"po2prop\",\n                \"po2rc\",\n                \"po2resx\",\n                \"po2sub\",\n                \"po2symb\",\n                \"po2tiki\",\n                \"po2tmx\",\n                \"po2ts\",\n                \"po2txt\",\n                \"po2web2py\",\n                \"po2wordfast\",\n                \"po2xliff\",\n                \"po2yaml\",\n                \"poclean\",\n                \"pocommentclean\",\n                \"pocompendium\",\n                \"pocompile\",\n                \"poconflicts\",\n                \"pocount\",\n                \"podebug\",\n                \"pofilter\",\n                \"pogrep\",\n                \"pomerge\",\n                \"pomigrate2\",\n                \"popuretext\",\n                \"poreencode\",\n                \"porestructure\",\n                \"posegment\",\n                \"posplit\",\n                \"poswap\",\n                \"pot2po\",\n                \"poterminology\",\n                \"pretranslate\",\n                \"prop2po\",\n                \"pydiff\",\n                \"pypo2phppo\",\n                \"rc2po\",\n                \"resx2po\",\n                \"sqlformat\",  # sqlparse EXE\n                \"sub2po\",\n                \"symb2po\",\n                \"tbx2po\",\n                \"tiki2po\",\n                \"tmserver\",\n                \"ts2po\",\n                \"txt2po\",\n                \"web2py2po\",\n                \"weblate-discover\",\n                \"xliff2odf\",\n                \"xliff2oo\",\n                \"xliff2po\",\n                \"yaml2po\",\n            ]\n        ),\n    },\n    \"youtube-dl\": {\n        \"spec\": \"youtube-dl==2020.9.20\",\n        \"apps\": _exe_if_win([\"youtube-dl\"]),\n        \"apps_of_dependencies\": [],\n        \"man_pages\": [str(Path(\"man1\") / \"youtube-dl.1\")],\n        \"man_pages_of_dependencies\": [],\n    },\n    \"zeo\": {\n        \"spec\": \"ZEO==5.2.2\",\n        \"apps\": _exe_if_win([\"runzeo\", \"zeo-nagios\", \"zeoctl\", \"zeopack\"]),\n        \"apps_of_dependencies\": _exe_if_win(\n            [\n                \"fsdump\",\n                \"fsoids\",\n                \"fsrefs\",\n                \"fstail\",\n                \"repozo\",\n                \"zconfig\",\n                \"zconfig_schema2html\",\n                \"zdaemon\",\n            ]\n        ),\n    },\n}\n"
  },
  {
    "path": "tests/test_animate.py",
    "content": "import time\nfrom timeit import default_timer\n\nimport pytest  # type: ignore[import-not-found]\n\nimport pipx.animate\nfrom pipx.animate import (\n    CLEAR_LINE,\n    EMOJI_ANIMATION_FRAMES,\n    EMOJI_FRAME_PERIOD,\n    NONEMOJI_ANIMATION_FRAMES,\n    NONEMOJI_FRAME_PERIOD,\n)\n\n# 40-char test_string counts columns e.g.: \"0204060810 ... 363840\"\nTEST_STRING_40_CHAR = \"\".join([f\"{x:02}\" for x in range(2, 41, 2)])\n\n\ndef check_animate_output(\n    capsys,\n    test_string,\n    frame_strings,\n    frame_period,\n    frames_to_test,\n    extra_animate_time=0.4,\n    extra_after_thread_time=0.1,\n):\n    \"\"\"\n    Refactored to use polling instead of rigid sleeps.\n    \"\"\"\n    expected_string = \"\".join(frame_strings)\n\n    # FIX: Calculate exact length required. Removed the \"+ 1\" that caused flakes.\n    chars_to_test = len(\"\".join(frame_strings[:frames_to_test]))\n\n    total_err = \"\"\n    # Generous timeout for slow CI environments (e.g. Windows/Mac runners)\n    timeout = 5.0\n    start_time = default_timer()\n\n    with pipx.animate.animate(test_string, do_animation=True):\n        # POLLING LOOP: Keep reading until we get the expected data\n        while default_timer() - start_time < timeout:\n            captured = capsys.readouterr()\n            total_err += captured.err\n\n            # If we have enough data, stop waiting immediately\n            if len(total_err) >= chars_to_test:\n                break\n\n            # Tiny sleep to avoid 100% CPU usage loop\n            time.sleep(0.01)\n\n    # Capture any final output after loop or context manager exit\n    captured = capsys.readouterr()\n    total_err += captured.err\n\n    print(\"check_animate_output() Test Debug Output:\")\n    if len(total_err) < chars_to_test:\n        print(\"Not enough captured characters--Timed out waiting for output\")\n\n    print(f\"captured characters: {len(total_err)}\")\n    print(f\"chars_to_test: {chars_to_test}\")\n\n    for i in range(0, chars_to_test, 40):\n        i_end = min(i + 40, chars_to_test)\n        print(f\"expected_string[{i}:{i_end}]: {expected_string[i:i_end]!r}\")\n        print(f\"captured.err[{i}:{i_end}]    : {total_err[i:i_end]!r}\")\n\n    assert total_err[:chars_to_test] == expected_string[:chars_to_test]\n\n\ndef test_delay_suppresses_output(capsys, monkeypatch):\n    monkeypatch.setattr(pipx.animate, \"stderr_is_tty\", True)\n    monkeypatch.setenv(\"COLUMNS\", \"80\")\n\n    test_string = \"asdf\"\n\n    # We keep sleep here because we are testing the ABSENCE of output during a delay.\n    with pipx.animate.animate(test_string, do_animation=True, delay=0.9):\n        time.sleep(0.5)\n    captured = capsys.readouterr()\n    assert test_string not in captured.err\n\n\n@pytest.mark.parametrize(\n    \"env_columns,expected_frame_message\",\n    [\n        (45, f\"{TEST_STRING_40_CHAR:.{45 - 6}}...\"),\n        (46, f\"{TEST_STRING_40_CHAR}\"),\n        (47, f\"{TEST_STRING_40_CHAR}\"),\n    ],\n)\ndef test_line_lengths_emoji(capsys, monkeypatch, env_columns, expected_frame_message):\n    monkeypatch.setattr(pipx.animate, \"stderr_is_tty\", True)\n    monkeypatch.setattr(pipx.animate, \"EMOJI_SUPPORT\", True)\n\n    monkeypatch.setenv(\"COLUMNS\", str(env_columns))\n\n    frames_to_test = 4\n    frame_strings = [f\"\\r{CLEAR_LINE}{x} {expected_frame_message}\" for x in EMOJI_ANIMATION_FRAMES]\n    check_animate_output(capsys, TEST_STRING_40_CHAR, frame_strings, EMOJI_FRAME_PERIOD, frames_to_test)\n\n\n@pytest.mark.parametrize(\n    \"env_columns,expected_frame_message\",\n    [\n        (43, f\"{TEST_STRING_40_CHAR:.{43 - 4}}\"),\n        (44, f\"{TEST_STRING_40_CHAR}\"),\n        (45, f\"{TEST_STRING_40_CHAR}\"),\n    ],\n)\ndef test_line_lengths_no_emoji(capsys, monkeypatch, env_columns, expected_frame_message):\n    monkeypatch.setattr(pipx.animate, \"stderr_is_tty\", True)\n    monkeypatch.setattr(pipx.animate, \"EMOJI_SUPPORT\", False)\n\n    monkeypatch.setenv(\"COLUMNS\", str(env_columns))\n\n    frames_to_test = 2\n    frame_strings = [f\"\\r{CLEAR_LINE}{expected_frame_message}{x}\" for x in NONEMOJI_ANIMATION_FRAMES]\n\n    check_animate_output(\n        capsys,\n        TEST_STRING_40_CHAR,\n        frame_strings,\n        NONEMOJI_FRAME_PERIOD,\n        frames_to_test,\n    )\n\n\n@pytest.mark.parametrize(\"env_columns,stderr_is_tty\", [(0, True), (8, True), (16, True), (17, False)])\ndef test_env_no_animate(capsys, monkeypatch, env_columns, stderr_is_tty):\n    monkeypatch.setattr(pipx.animate, \"stderr_is_tty\", stderr_is_tty)\n    monkeypatch.setenv(\"COLUMNS\", str(env_columns))\n\n    expected_string = f\"{TEST_STRING_40_CHAR}...\\n\"\n\n    # Replaced complex sleep math with a simple short wait.\n    # We just need the context manager to run and exit to verify the static output.\n    with pipx.animate.animate(TEST_STRING_40_CHAR, do_animation=True):\n        time.sleep(0.1)\n\n    captured = capsys.readouterr()\n\n    assert captured.out == \"\"\n    assert captured.err == expected_string\n"
  },
  {
    "path": "tests/test_common.py",
    "content": "from helpers import skip_if_windows\nfrom pipx.commands.common import get_exposed_paths_for_package\n\n\n@skip_if_windows\ndef test_get_exposed_paths_ignores_recursive_symlink(tmp_path):\n    venv_resource_path = tmp_path / \"venv_bin\"\n    venv_resource_path.mkdir()\n    local_resource_dir = tmp_path / \"bin\"\n    local_resource_dir.mkdir()\n    loop = local_resource_dir / \"recursiveexample\"\n    loop.symlink_to(loop.name)\n\n    exposed = get_exposed_paths_for_package(venv_resource_path, local_resource_dir)\n\n    assert loop not in exposed\n"
  },
  {
    "path": "tests/test_completions.py",
    "content": "from helpers import run_pipx_cli\n\n\ndef test_cli(monkeypatch, capsys):\n    assert not run_pipx_cli([\"completions\"])\n    captured = capsys.readouterr()\n    assert \"Add the appropriate command\" in captured.out\n"
  },
  {
    "path": "tests/test_emojis.py",
    "content": "import sys\nfrom io import BytesIO, TextIOWrapper\nfrom unittest import mock\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom pipx.emojis import use_emojis\n\n\n@pytest.mark.parametrize(\n    \"PIPX_USE_EMOJI, encoding, expected\",\n    [\n        # utf-8\n        (None, \"utf-8\", True),\n        (\"\", \"utf-8\", False),\n        (\"0\", \"utf-8\", False),\n        (\"1\", \"utf-8\", True),\n        (\"true\", \"utf-8\", True),\n        (\"tru\", \"utf-8\", False),  # codespell:ignore tru\n        (\"True\", \"utf-8\", True),\n        (\"false\", \"utf-8\", False),\n        # latin_1 (alias: iso-8859-1)\n        (None, \"latin_1\", False),\n        (\"\", \"latin_1\", False),\n        (\"0\", \"latin_1\", False),\n        (\"1\", \"latin_1\", True),\n        (\"true\", \"latin_1\", True),\n        (\"tru\", \"latin_1\", False),  # codespell:ignore tru\n        (\"True\", \"latin_1\", True),\n        (\"false\", \"latin_1\", False),\n        # cp1252\n        (None, \"cp1252\", False),\n        (\"\", \"cp1252\", False),\n        (\"0\", \"cp1252\", False),\n        (\"1\", \"cp1252\", True),\n        (\"true\", \"cp1252\", True),\n        (\"tru\", \"cp1252\", False),  # codespell:ignore tru\n        (\"True\", \"cp1252\", True),\n        (\"false\", \"cp1252\", False),\n    ],\n)\ndef test_use_emojis(monkeypatch, PIPX_USE_EMOJI, encoding, expected):\n    with mock.patch.object(sys, \"stderr\", TextIOWrapper(BytesIO(), encoding=encoding)):\n        if PIPX_USE_EMOJI is not None:\n            monkeypatch.setenv(\"PIPX_USE_EMOJI\", PIPX_USE_EMOJI)\n        assert use_emojis() is expected\n"
  },
  {
    "path": "tests/test_environment.py",
    "content": "import fnmatch\nfrom pathlib import Path\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom helpers import run_pipx_cli, skip_if_windows\nfrom pipx import paths\nfrom pipx.commands.environment import ENVIRONMENT_VARIABLES\nfrom pipx.paths import get_expanded_environ\n\n\ndef test_cli(pipx_temp_env, monkeypatch, capsys):\n    assert not run_pipx_cli([\"environment\"])\n    captured = capsys.readouterr()\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_HOME=*subdir/pipxhome*\")\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_BIN_DIR=*otherdir/pipxbindir*\")\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_MAN_DIR=*otherdir/pipxmandir*\")\n    assert \"PIPX_SHARED_LIBS\" in captured.out\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_LOCAL_VENVS=*subdir/pipxhome/venvs*\")\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_LOG_DIR=*subdir/pipxhome/logs*\")\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_TRASH_DIR=*subdir/pipxhome/.trash*\")\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_VENV_CACHEDIR=*subdir/pipxhome/.cache*\")\n    # Checking just for the sake of completeness\n    for env_var in ENVIRONMENT_VARIABLES:\n        assert env_var in captured.out\n\n\ndef test_cli_with_args(monkeypatch, capsys):\n    assert not run_pipx_cli([\"environment\", \"--value\", \"PIPX_HOME\"])\n    assert not run_pipx_cli([\"environment\", \"--value\", \"PIPX_BIN_DIR\"])\n    assert not run_pipx_cli([\"environment\", \"--value\", \"PIPX_MAN_DIR\"])\n    assert not run_pipx_cli([\"environment\", \"--value\", \"PIPX_SHARED_LIBS\"])\n    assert not run_pipx_cli([\"environment\", \"--value\", \"PIPX_LOCAL_VENVS\"])\n    assert not run_pipx_cli([\"environment\", \"--value\", \"PIPX_LOG_DIR\"])\n    assert not run_pipx_cli([\"environment\", \"--value\", \"PIPX_TRASH_DIR\"])\n    assert not run_pipx_cli([\"environment\", \"--value\", \"PIPX_VENV_CACHEDIR\"])\n    assert not run_pipx_cli([\"environment\", \"--value\", \"PIPX_DEFAULT_PYTHON\"])\n    assert not run_pipx_cli([\"environment\", \"--value\", \"PIPX_USE_EMOJI\"])\n    assert not run_pipx_cli([\"environment\", \"--value\", \"PIPX_HOME_ALLOW_SPACE\"])\n\n    with pytest.raises(SystemExit) as excinfo:\n        run_pipx_cli([\"environment\", \"--value\", \"SSS\"])\n    assert excinfo.value.code == 2\n    captured = capsys.readouterr()\n    assert \"invalid choice\" in captured.err\n\n\ndef test_resolve_user_dir_in_env_paths(monkeypatch):\n    monkeypatch.setenv(\"TEST_DIR\", \"~/test\")\n    home = Path.home()\n    env_dir = get_expanded_environ(\"TEST_DIR\")\n    assert \"~\" not in str(env_dir)\n    assert env_dir == home / \"test\"\n    env_dir = get_expanded_environ(\"THIS_SHOULD_NOT_EXIST\")\n    assert env_dir is None\n\n\ndef test_allow_space_in_pipx_home(\n    monkeypatch,\n    capsys,\n    tmp_path,\n):\n    home_dir = Path(tmp_path) / \"path with space\"\n    monkeypatch.setattr(paths.ctx, \"_base_home\", home_dir)\n    assert not run_pipx_cli([\"environment\", \"--value\", \"PIPX_HOME_ALLOW_SPACE\"])\n    paths.ctx.log_warnings()\n    captured = capsys.readouterr()\n    assert \"Found a space\" in captured.err\n    assert \"false\" in captured.out\n\n    monkeypatch.setenv(\"PIPX_HOME_ALLOW_SPACE\", \"1\")\n    assert not run_pipx_cli([\"environment\", \"--value\", \"PIPX_HOME_ALLOW_SPACE\"])\n    paths.ctx.log_warnings()\n    captured = capsys.readouterr()\n    assert \"Found a space\" not in captured.err\n    assert \"true\" in captured.out\n\n    paths.ctx.make_local()\n\n\n@skip_if_windows\ndef test_cli_global(pipx_temp_env, monkeypatch, capsys):\n    assert not run_pipx_cli([\"environment\", \"--global\"])\n    captured = capsys.readouterr()\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_HOME=*global/pipxhome*\")\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_BIN_DIR=*global_otherdir/pipxbindir*\")\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_MAN_DIR=*global_otherdir/pipxmandir*\")\n    assert \"PIPX_SHARED_LIBS\" in captured.out\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_LOCAL_VENVS=*global/pipxhome/venvs*\")\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_LOG_DIR=*global/pipxhome/logs*\")\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_TRASH_DIR=*global/pipxhome/.trash*\")\n    assert fnmatch.fnmatch(captured.out, \"*PIPX_VENV_CACHEDIR=*global/pipxhome/.cache*\")\n    # Checking just for the sake of completeness\n    for env_var in ENVIRONMENT_VARIABLES:\n        assert env_var in captured.out\n"
  },
  {
    "path": "tests/test_inject.py",
    "content": "import logging\nimport re\nimport textwrap\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom helpers import PIPX_METADATA_LEGACY_VERSIONS, mock_legacy_venv, run_pipx_cli, skip_if_windows\nfrom package_info import PKG\n\n\n# Note that this also checks that packages used in other tests can be injected individually\n@pytest.mark.parametrize(\n    \"pkg_spec,\",\n    [\n        PKG[\"black\"][\"spec\"],\n        PKG[\"nox\"][\"spec\"],\n        PKG[\"pylint\"][\"spec\"],\n        PKG[\"ipython\"][\"spec\"],\n        \"jaraco.clipboard==2.0.1\",  # tricky character\n    ],\n)\ndef test_inject_single_package(pipx_temp_env, capsys, caplog, pkg_spec):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"inject\", \"pycowsay\", pkg_spec])\n\n    # Check arguments have been parsed correctly\n    assert f\"Injecting packages: {[pkg_spec]!r}\" in caplog.text\n\n    # Check it's actually being installed and into correct venv\n    captured = capsys.readouterr()\n    injected = re.findall(r\"injected package (.+?) into venv pycowsay\", captured.out)\n    pkg_name = pkg_spec.split(\"=\", 1)[0].replace(\".\", \"-\")  # assuming spec is always of the form <name>==<version>\n    assert set(injected) == {pkg_name}\n\n\n@skip_if_windows\ndef test_inject_simple_global(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"--global\", \"pycowsay\"])\n    assert not run_pipx_cli([\"inject\", \"--global\", \"pycowsay\", PKG[\"black\"][\"spec\"]])\n\n\n@pytest.mark.parametrize(\"metadata_version\", PIPX_METADATA_LEGACY_VERSIONS)\ndef test_inject_simple_legacy_venv(pipx_temp_env, capsys, metadata_version):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    mock_legacy_venv(\"pycowsay\", metadata_version=metadata_version)\n    if metadata_version is not None:\n        assert not run_pipx_cli([\"inject\", \"pycowsay\", PKG[\"black\"][\"spec\"]])\n    else:\n        # no metadata in venv should result in PipxError with message\n        assert run_pipx_cli([\"inject\", \"pycowsay\", PKG[\"black\"][\"spec\"]])\n        assert \"Please uninstall and install\" in capsys.readouterr().err\n\n\n@pytest.mark.parametrize(\"with_suffix,\", [(False,), (True,)])\ndef test_inject_include_apps(pipx_temp_env, capsys, with_suffix):\n    install_args = []\n    suffix = \"\"\n\n    if with_suffix:\n        suffix = \"_x\"\n        install_args = [f\"--suffix={suffix}\"]\n\n    assert not run_pipx_cli([\"install\", \"pycowsay\", *install_args])\n    assert not run_pipx_cli([\"inject\", f\"pycowsay{suffix}\", PKG[\"black\"][\"spec\"], \"--include-deps\"])\n\n    if suffix:\n        assert run_pipx_cli([\"inject\", \"pycowsay\", PKG[\"black\"][\"spec\"], \"--include-deps\"])\n\n    assert not run_pipx_cli([\"inject\", f\"pycowsay{suffix}\", PKG[\"black\"][\"spec\"], \"--include-deps\"])\n\n\n@pytest.mark.parametrize(\n    \"with_packages,\",\n    [\n        (),  # no extra packages\n        (\"black\",),  # duplicate from requirements file\n        (\"ipython\",),  # additional package\n    ],\n)\ndef test_inject_with_req_file(pipx_temp_env, capsys, caplog, tmp_path, with_packages):\n    caplog.set_level(logging.INFO)\n\n    req_file = tmp_path / \"inject-requirements.txt\"\n    req_file.write_text(\n        textwrap.dedent(\n            f\"\"\"\n                {PKG[\"black\"][\"spec\"]} # a comment inline\n                {PKG[\"nox\"][\"spec\"]}\n\n                {PKG[\"pylint\"][\"spec\"]}\n                # comment on separate line\n            \"\"\"\n        ).strip()\n    )\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n\n    assert not run_pipx_cli(\n        [\"inject\", \"pycowsay\", *(PKG[pkg][\"spec\"] for pkg in with_packages), \"--requirement\", str(req_file)]\n    )\n\n    packages = [\n        (\"black\", PKG[\"black\"][\"spec\"]),\n        (\"nox\", PKG[\"nox\"][\"spec\"]),\n        (\"pylint\", PKG[\"pylint\"][\"spec\"]),\n    ]\n    packages.extend((pkg, PKG[pkg][\"spec\"]) for pkg in with_packages)\n    packages = sorted(set(packages))\n\n    # Check arguments and files have been parsed correctly\n    assert f\"Injecting packages: {[p for _, p in packages]!r}\" in caplog.text\n\n    # Check they're actually being installed and into correct venv\n    captured = capsys.readouterr()\n    injected = re.findall(r\"injected package (.+?) into venv pycowsay\", captured.out)\n    assert set(injected) == {pkg for pkg, _ in packages}\n"
  },
  {
    "path": "tests/test_install.py",
    "content": "import os\nimport re\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom unittest import mock\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom helpers import app_name, run_pipx_cli, skip_if_windows, unwrap_log_text\nfrom package_info import PKG\nfrom pipx import paths, shared_libs\n\nTEST_DATA_PATH = \"./testdata/test_package_specifier\"\n\n\ndef test_help_text(monkeypatch, capsys):\n    mock_exit = mock.Mock(side_effect=ValueError(\"raised in test to exit early\"))\n    with mock.patch.object(sys, \"exit\", mock_exit), pytest.raises(ValueError, match=\"raised in test to exit early\"):\n        run_pipx_cli([\"install\", \"--help\"])\n    captured = capsys.readouterr()\n    assert \"apps you can run from anywhere\" in captured.out\n\n\ndef install_packages(capsys, pipx_temp_env, caplog, packages, package_names=()):\n    if len(package_names) != len(packages):\n        package_names = packages\n\n    run_pipx_cli([\"install\", *packages, \"--verbose\"])\n    captured = capsys.readouterr()\n    for package_name in package_names:\n        assert f\"installed package {package_name}\" in captured.out\n    if not sys.platform.startswith(\"win\"):\n        # TODO assert on windows too\n        # https://github.com/pypa/pipx/issues/217\n        assert \"symlink missing or pointing to unexpected location\" not in captured.out\n    assert \"not modifying\" not in captured.out\n    assert \"is not on your PATH environment variable\" not in captured.out\n    assert \"⚠️\" not in caplog.text\n    assert \"WARNING\" not in caplog.text\n\n\n@pytest.mark.parametrize(\n    \"package_name, package_spec\",\n    [(\"pycowsay\", \"pycowsay\"), (\"black\", PKG[\"black\"][\"spec\"])],\n)\ndef test_install_easy_packages(capsys, pipx_temp_env, caplog, package_name, package_spec):\n    install_packages(capsys, pipx_temp_env, caplog, [package_spec], [package_name])\n\n\ndef test_install_easy_multiple_packages(capsys, pipx_temp_env, caplog):\n    install_packages(\n        capsys,\n        pipx_temp_env,\n        caplog,\n        [\"pycowsay\", PKG[\"black\"][\"spec\"]],\n        [\"pycowsay\", \"black\"],\n    )\n\n\n@pytest.mark.parametrize(\n    \"package_name, package_spec\",\n    [(\"pycowsay\", \"pycowsay\"), (\"black\", PKG[\"black\"][\"spec\"])],\n)\n@skip_if_windows\ndef test_install_easy_packages_globally(capsys, pipx_temp_env, caplog, package_name, package_spec):\n    install_packages(capsys, pipx_temp_env, caplog, [package_spec], [package_name])\n\n\n@pytest.mark.parametrize(\n    \"package_name, package_spec\",\n    [\n        # (\"cloudtoken\", PKG[\"cloudtoken\"][\"spec\"]),\n        (\"awscli\", PKG[\"awscli\"][\"spec\"]),\n        (\"ansible\", PKG[\"ansible\"][\"spec\"]),\n        (\"shell-functools\", PKG[\"shell-functools\"][\"spec\"]),\n    ],\n)\ndef test_install_tricky_packages(capsys, pipx_temp_env, caplog, package_name, package_spec):\n    if os.getenv(\"FAST\"):\n        pytest.skip(\"skipping slow tests\")\n    if sys.platform.startswith(\"win\") and package_name == \"ansible\":\n        pytest.skip(\"Ansible is not installable on Windows\")\n\n    install_packages(capsys, pipx_temp_env, caplog, [package_spec], [package_name])\n\n\ndef test_install_multiple_packages_when_some_already_installed(capsys, pipx_temp_env, caplog):\n    run_pipx_cli([\"install\", \"black\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"installed package black\" in captured.out\n    assert \"installed package pycowsay\" in captured.out\n\n    run_pipx_cli([\"install\", \"black\", \"pycowsay\", \"isort\"])\n    captured = capsys.readouterr()\n    assert \"'black' already seems to be installed\" in captured.out\n    assert \"'pycowsay' already seems to be installed\" in captured.out\n    assert \"installed package isort\" in captured.out\n\n\ndef test_install_tricky_multiple_packages(capsys, pipx_temp_env, caplog):\n    if os.getenv(\"FAST\"):\n        pytest.skip(\"skipping slow tests\")\n\n    packages = [\"awscli\", \"shell-functools\"]  # cloudtoken is temporarily removed\n    package_specs = [PKG[package][\"spec\"] for package in packages]\n\n    install_packages(capsys, pipx_temp_env, caplog, package_specs, packages)\n\n\n@pytest.mark.parametrize(\n    \"package_name, package_spec\",\n    [\n        (\"pycowsay\", \"git+https://github.com/cs01/pycowsay.git@master\"),\n        (\"pylint\", PKG[\"pylint\"][\"spec\"]),\n        (\"nox\", \"https://github.com/wntrblm/nox/archive/2022.1.7.zip\"),\n    ],\n)\ndef test_install_package_specs(capsys, pipx_temp_env, caplog, package_name, package_spec):\n    install_packages(capsys, pipx_temp_env, caplog, [package_spec], [package_name])\n\n\ndef test_force_install(pipx_temp_env, capsys):\n    run_pipx_cli([\"install\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    # print(captured.out)\n    assert \"installed package\" in captured.out\n\n    run_pipx_cli([\"install\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"installed package\" not in captured.out\n    assert \"'pycowsay' already seems to be installed\" in captured.out\n\n    run_pipx_cli([\"install\", \"pycowsay\", \"--force\"])\n    captured = capsys.readouterr()\n    assert \"Installing to existing venv\" in captured.out\n\n\ndef test_install_no_packages_found(pipx_temp_env, capsys):\n    run_pipx_cli([\"install\", PKG[\"pygdbmi\"][\"spec\"]])\n    captured = capsys.readouterr()\n    assert \"No apps associated with package pygdbmi\" in captured.err\n\n\ndef test_install_same_package_twice_no_force(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"'pycowsay' already seems to be installed. Not modifying existing installation\" in captured.out\n\n\ndef test_include_deps(pipx_temp_env, capsys):\n    assert run_pipx_cli([\"install\", PKG[\"jupyter\"][\"spec\"]]) == 1\n    assert not run_pipx_cli([\"install\", PKG[\"jupyter\"][\"spec\"], \"--include-deps\"])\n\n\n@pytest.mark.parametrize(\n    \"package_name, package_spec\",\n    [\n        (\"zest-releaser\", PKG[\"zest-releaser\"][\"spec\"]),\n        (\"tox-ini-fmt\", PKG[\"tox-ini-fmt\"][\"spec\"]),\n    ],\n)\ndef test_name_tricky_characters(caplog, capsys, pipx_temp_env, package_name, package_spec):\n    if sys.platform == \"darwin\" and package_name == \"zest-releaser\":\n        pytest.skip(\"Skipping zest-releaser due to missing Python 3.13 wheel for cmarkgfm on macOS\")\n\n    install_packages(capsys, pipx_temp_env, caplog, [package_spec], [package_name])\n\n\ndef test_extra(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"nox[tox_to_nox]==2023.4.22\", \"--include-deps\"])\n    captured = capsys.readouterr()\n    assert f\"- {app_name('tox')}\\n\" in captured.out\n\n\ndef test_install_local_extra(pipx_temp_env, capsys, monkeypatch, root):\n    assert not run_pipx_cli([\"install\", str(root / f\"{TEST_DATA_PATH}/local_extras[cow]\"), \"--include-deps\"])\n    captured = capsys.readouterr()\n    assert f\"- {app_name('pycowsay')}\\n\" in captured.out\n    assert f\"- {Path('man6/pycowsay.6')}\\n\" in captured.out\n\n\ndef test_path_warning(pipx_temp_env, capsys, monkeypatch, caplog):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert \"is not on your PATH environment variable\" not in unwrap_log_text(caplog.text)\n\n    monkeypatch.setenv(\"PATH\", \"\")\n    assert not run_pipx_cli([\"install\", \"pycowsay\", \"--force\"])\n    assert \"is not on your PATH environment variable\" in unwrap_log_text(caplog.text)\n\n\n@skip_if_windows\ndef test_existing_symlink_points_to_existing_wrong_location_warning(pipx_temp_env, caplog, capsys):\n    paths.ctx.bin_dir.mkdir(exist_ok=True, parents=True)\n    (paths.ctx.bin_dir / \"pycowsay\").symlink_to(os.devnull)\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"File exists at\" in unwrap_log_text(caplog.text)\n    assert \"symlink missing or pointing to unexpected location\" in captured.out\n    # bin dir was on path, so the warning should NOT appear (even though the symlink\n    # pointed to the wrong location)\n    assert \"is not on your PATH environment variable\" not in captured.err\n\n\n@skip_if_windows\ndef test_existing_man_page_symlink_points_to_existing_wrong_location_warning(pipx_temp_env, caplog, capsys):\n    (paths.ctx.man_dir / \"man6\").mkdir(exist_ok=True, parents=True)\n    (paths.ctx.man_dir / \"man6\" / \"pycowsay.6\").symlink_to(os.devnull)\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"File exists at\" in unwrap_log_text(caplog.text)\n    assert \"symlink missing or pointing to unexpected location\" in captured.out\n\n\n@skip_if_windows\ndef test_existing_symlink_points_to_nothing(pipx_temp_env, capsys):\n    paths.ctx.bin_dir.mkdir(exist_ok=True, parents=True)\n    (paths.ctx.bin_dir / \"pycowsay\").symlink_to(\"/asdf/jkl\")\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    # pipx should realize the symlink points to nothing and replace it,\n    # so no warning should be present\n    assert \"symlink missing or pointing to unexpected location\" not in captured.out\n\n\n@skip_if_windows\ndef test_existing_man_page_symlink_points_to_nothing(pipx_temp_env, capsys):\n    (paths.ctx.man_dir / \"man6\").mkdir(exist_ok=True, parents=True)\n    (paths.ctx.man_dir / \"man6\" / \"pycowsay.6\").symlink_to(\"/asdf/jkl\")\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    # pipx should realize the symlink points to nothing and replace it,\n    # so no warning should be present\n    assert \"symlink missing or pointing to unexpected location\" not in captured.out\n\n\ndef test_pip_args_forwarded_to_shared_libs(pipx_ultra_temp_env, capsys, caplog):\n    # strategy:\n    # 1. start from an empty env to ensure the next command would trigger a shared lib update\n    assert shared_libs.shared_libs.needs_upgrade\n    # 2. install any package with --no-index\n    # and check that the shared library update phase fails\n    return_code = run_pipx_cli([\"install\", \"--verbose\", \"--pip-args=--no-index\", \"pycowsay\"])\n    assert \"Upgrading shared libraries in\" in caplog.text\n\n    captured = capsys.readouterr()\n    assert return_code != 0\n    assert \"ERROR: Could not find a version that satisfies the requirement pip\" in captured.err\n    assert \"Failed to upgrade shared libraries\" in caplog.text\n\n\ndef test_pip_args_forwarded_to_package_name_determination(pipx_temp_env, capsys):\n    assert run_pipx_cli(\n        [\n            \"install\",\n            # use a valid spec and invalid pip args\n            \"https://github.com/psf/black/archive/22.8.0.zip\",\n            \"--verbose\",\n            \"--pip-args='--asdf'\",\n        ]\n    )\n    captured = capsys.readouterr()\n    assert \"Cannot determine package name from spec\" in captured.err\n\n\ndef test_pip_args_with_windows_path(pipx_temp_env, capsys):\n    if not sys.platform.startswith(\"win\"):\n        pytest.skip(\"Test pip arguments with Windows path on Windows only.\")\n\n    assert run_pipx_cli(\n        [\n            \"install\",\n            \"pycowsay\",\n            \"--verbose\",\n            \"--pip-args='--no-index --find-links=D:\\\\TEST\\\\DIR'\",\n        ]\n    )\n    captured = capsys.readouterr()\n    assert r\"D:\\\\TEST\\\\DIR\" in captured.err\n\n\n@pytest.mark.parametrize(\"constraint_flag\", [\"-c \", \"--constraint \", \"--constraint=\"])\ndef test_pip_args_with_constraint_relative_path(constraint_flag, pipx_temp_env, tmp_path, caplog):\n    constraint_file_name = \"constraints.txt\"\n    package_name = \"ipython\"\n    package_version = \"8.23.0\"\n\n    os.chdir(tmp_path)\n    constraints_file = tmp_path / constraint_file_name\n    constraints_file.write_text(f\"{package_name}!={package_version}\")\n    constraints_file.touch()\n\n    assert not run_pipx_cli([\"install\", f\"--pip-args='{constraint_flag}{constraint_file_name}'\", package_name])\n\n    assert f\"{constraint_flag}{constraints_file}\" in caplog.text\n\n    subprocess_package_version = subprocess.run([package_name, \"--version\"], capture_output=True, text=True, check=False)\n    subprocess_package_version_output = subprocess_package_version.stdout.strip()\n    assert subprocess_package_version_output != package_version\n\n\n@pytest.mark.parametrize(\"constraint_flag\", [\"-c \", \"--constraint \", \"--constraint=\"])\ndef test_pip_args_with_wrong_constraint_fail(constraint_flag, pipx_ultra_temp_env, tmp_path, capsys):\n    constraint_file_name = \"constraints.txt\"\n    os.chdir(tmp_path)\n\n    assert run_pipx_cli([\"install\", f\"--pip-args='{constraint_flag}{constraint_file_name}'\", \"pycowsay\"])\n\n    assert (\n        f\"ERROR: Could not open requirements file: [Errno 2] No such file or directory: '{constraint_file_name}'\"\n        in capsys.readouterr().err\n    )\n\n\ndef test_install_suffix(pipx_temp_env, capsys):\n    name = \"pbr\"\n\n    suffix = \"_a\"\n    assert not run_pipx_cli([\"install\", PKG[name][\"spec\"], f\"--suffix={suffix}\"])\n    captured = capsys.readouterr()\n    name_a = app_name(f\"{name}{suffix}\")\n    assert f\"- {name_a}\" in captured.out\n\n    suffix = \"_b\"\n    assert not run_pipx_cli([\"install\", PKG[name][\"spec\"], f\"--suffix={suffix}\"])\n    captured = capsys.readouterr()\n    name_b = app_name(f\"{name}{suffix}\")\n    assert f\"- {name_b}\" in captured.out\n\n    assert (paths.ctx.bin_dir / name_a).exists()\n    assert (paths.ctx.bin_dir / name_b).exists()\n\n\ndef test_man_page_install(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert f\"- {Path('man6/pycowsay.6')}\" in captured.out\n    assert (paths.ctx.man_dir / \"man6\" / \"pycowsay.6\").exists()\n\n\ndef test_install_pip_failure(pipx_temp_env, capsys):\n    assert run_pipx_cli([\"install\", \"weblate==4.3.1\", \"--verbose\"])\n    captured = capsys.readouterr()\n\n    assert \"Fatal error from pip\" in captured.err\n\n    pip_log_file_match = re.search(r\"Full pip output in file:\\s+(\\S.+)$\", captured.err, re.MULTILINE)\n    assert pip_log_file_match\n    assert Path(pip_log_file_match[1]).exists()\n\n    assert re.search(r\"pip (failed|seemed to fail) to build package\", captured.err)\n\n\ndef test_install_local_archive(pipx_temp_env, monkeypatch, capsys, root):\n    monkeypatch.chdir(root / TEST_DATA_PATH / \"local_extras\")\n\n    subprocess.run([sys.executable, \"-m\", \"pip\", \"wheel\", \".\"], check=True)\n    assert not run_pipx_cli([\"install\", \"repeatme-0.1-py3-none-any.whl\"])\n    captured = capsys.readouterr()\n    assert f\"- {app_name('repeatme')}\\n\" in captured.out\n\n\ndef test_force_install_changes(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"https://github.com/wntrblm/nox/archive/2022.1.7.zip\"])\n    captured = capsys.readouterr()\n    assert \"2022.1.7\" in captured.out\n\n    assert not run_pipx_cli([\"install\", \"nox\", \"--force\"])\n    captured = capsys.readouterr()\n    assert \"2022.1.7\" not in captured.out\n\n\ndef test_force_install_changes_editable(pipx_temp_env, root, capsys):\n    empty_project_path_as_string = (root / \"testdata\" / \"empty_project\").as_posix()\n    assert not run_pipx_cli([\"install\", \"--editable\", empty_project_path_as_string])\n    captured = capsys.readouterr()\n    assert \"empty-project\" in captured.out\n\n    assert not run_pipx_cli([\"install\", \"--editable\", empty_project_path_as_string, \"--force\"])\n    captured = capsys.readouterr()\n    assert \"Installing to existing venv 'empty-project'\" in captured.out\n\n\ndef test_preinstall(pipx_temp_env, caplog):\n    assert not run_pipx_cli([\"install\", \"--preinstall\", \"black\", \"nox\"])\n    assert \"black\" in caplog.text\n\n\ndef test_preinstall_multiple(pipx_temp_env, caplog):\n    assert not run_pipx_cli([\"install\", \"--preinstall\", \"chardet\", \"--preinstall\", \"colorama\", \"nox\"])\n    assert \"chardet\" in caplog.text\n    assert \"colorama\" in caplog.text\n\n\ndef test_preinstall_specific_version(pipx_temp_env, caplog):\n    assert not run_pipx_cli([\"install\", \"--preinstall\", \"black==22.8.0\", \"nox\"])\n    assert \"black==22.8.0\" in caplog.text\n\n\n@pytest.mark.xfail\ndef test_do_not_wait_for_input(pipx_temp_env, pipx_session_shared_dir, monkeypatch):\n    monkeypatch.setenv(\"PIP_INDEX_URL\", \"http://127.0.0.1:8080/simple\")\n    run_pipx_cli([\"install\", \"pycowsay\"])\n\n\ndef test_passed_python_and_force_flag_warning(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"black\"])\n    assert not run_pipx_cli([\"install\", \"--python\", sys.executable, \"--force\", \"black\"])\n    captured = capsys.readouterr()\n    assert \"--python is ignored when --force is passed.\" in captured.out\n\n    assert not run_pipx_cli([\"install\", \"black\", \"--force\"])\n    captured = capsys.readouterr()\n    assert \"--python is ignored when --force is passed.\" not in captured.out, (\n        \"Should only print warning if both flags present\"\n    )\n\n    assert not run_pipx_cli([\"install\", \"pycowsay\", \"--force\"])\n    captured = capsys.readouterr()\n    assert \"--python is ignored when --force is passed.\" not in captured.out, (\n        \"Should not print warning if package does not exist yet\"\n    )\n\n\n@pytest.mark.parametrize(\n    \"python_version\",\n    [\"3.0\", \"3.1\"],\n)\ndef test_install_fetch_missing_python_invalid(capsys, python_version):\n    assert run_pipx_cli([\"install\", \"--python\", python_version, \"--fetch-missing-python\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert f\"No executable for the provided Python version '{python_version}' found\" in captured.out\n\n\ndef test_install_run_in_separate_directory(caplog, capsys, pipx_temp_env, monkeypatch, tmp_path):\n    monkeypatch.chdir(tmp_path)\n    f = Path(\"argparse.py\")\n    f.touch()\n\n    install_packages(capsys, pipx_temp_env, caplog, [\"pycowsay\"], [\"pycowsay\"])\n\n\n@skip_if_windows\n@pytest.mark.parametrize(\n    \"python_version\",\n    [\n        str(sys.version_info.major),\n        f\"{sys.version_info.major}.{sys.version_info.minor}\",\n        f\"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}\",\n    ],\n)\ndef test_install_python_command_version(pipx_temp_env, monkeypatch, capsys, python_version):\n    monkeypatch.setenv(\"PATH\", os.getenv(\"PATH_ORIG\"))\n    assert not run_pipx_cli([\"install\", \"--python\", python_version, \"--verbose\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert python_version in captured.out\n\n\n@skip_if_windows\ndef test_install_python_command_version_invalid(pipx_temp_env, capsys):\n    python_version = \"3.x\"\n    assert run_pipx_cli([\"install\", \"--python\", python_version, \"--verbose\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert f\"Invalid Python version: {python_version}\" in captured.err\n\n\n@skip_if_windows\ndef test_install_python_command_version_unsupported(pipx_temp_env, capsys):\n    python_version = f\"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}.dev\"\n    assert run_pipx_cli([\"install\", \"--python\", python_version, \"--verbose\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert f\"Unsupported Python version: {python_version}\" in captured.err\n\n\n@skip_if_windows\ndef test_install_python_command_version_missing(pipx_temp_env, monkeypatch, capsys):\n    monkeypatch.setenv(\"PATH\", os.getenv(\"PATH_ORIG\"))\n    python_version = f\"{sys.version_info.major + 99}.{sys.version_info.minor}\"\n    assert run_pipx_cli([\"install\", \"--python\", python_version, \"--verbose\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert f\"Command `python{python_version}` was not found\" in captured.err\n\n\n@skip_if_windows\ndef test_install_python_command_version_micro_mismatch(pipx_temp_env, monkeypatch, capsys):\n    monkeypatch.setenv(\"PATH\", os.getenv(\"PATH_ORIG\"))\n    python_version = f\"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro + 1}\"\n    assert not run_pipx_cli([\"install\", \"--python\", python_version, \"--verbose\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert f\"It may not match the specified version {python_version} at the micro/patch level\" in captured.err\n\n\n@skip_if_windows\ndef test_global_flag_before_subcommand_rejected(pipx_temp_env, capsys):\n    with pytest.raises(SystemExit) as exc_info:\n        run_pipx_cli([\"--global\", \"install\", \"pycowsay\"])\n    assert exc_info.value.code == 2, \"argparse error should exit with code 2\"\n    captured = capsys.readouterr()\n    assert \"unrecognized arguments: --global\" in captured.err\n\n\ndef test_install_quiet_flag(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"--quiet\", \"--quiet\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"installed package\" not in captured.out\n    assert \"These apps are now\" not in captured.out\n    assert \"done!\" not in captured.out\n    assert \"done!\" not in captured.err\n"
  },
  {
    "path": "tests/test_install_all.py",
    "content": "from pathlib import Path\n\nfrom helpers import run_pipx_cli\nfrom pipx import paths\n\n\ndef test_install_all(pipx_temp_env, tmp_path, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"install\", \"black\"])\n    _ = capsys.readouterr()\n\n    assert not run_pipx_cli([\"list\", \"--json\"])\n    captured = capsys.readouterr()\n\n    pipx_list_path = Path(tmp_path) / \"pipx_list.json\"\n    with open(pipx_list_path, \"w\") as pipx_list_fh:\n        pipx_list_fh.write(captured.out)\n\n    assert not run_pipx_cli([\"install-all\", str(pipx_list_path)])\n\n    captured = capsys.readouterr()\n    assert \"black\" in captured.out\n    assert \"pycowsay\" in captured.out\n\n\ndef test_install_all_multiple_errors(pipx_temp_env, root, capsys):\n    pipx_metadata_path = root / \"testdata\" / \"pipx_metadata_multiple_errors.json\"\n    assert run_pipx_cli([\"install-all\", str(pipx_metadata_path)])\n    captured = capsys.readouterr()\n    assert \"The following package(s) failed to install: dotenv, weblate\" in captured.err\n    assert f\"No packages installed after running 'pipx install-all {pipx_metadata_path}'\" in captured.out\n    if paths.ctx.log_file:\n        with open(paths.ctx.log_file.parent / (paths.ctx.log_file.stem + \"_pip_errors.log\")) as log_fh:\n            log_contents = log_fh.read()\n            assert \"dotenv\" in log_contents\n            assert \"weblate\" in log_contents\n"
  },
  {
    "path": "tests/test_install_all_packages.py",
    "content": "\"\"\"\nThis module uses the pytest infrastructure to produce reports on a large list\nof packages.  It verifies installation with and without an intact system PATH.\nIt also generates report summaries and error reports files.\n\nTest pytest outcomes:\n    PASS - if no pip errors, and no pipx issues, and package apps verified\n            all installed correctly\n    XFAIL - if there is a pip error, i.e. an installation problem out of pipx's\n            control\n    FAIL - if there is no pip error, but there is a problem due to pipx,\n            including a pipx error or warning, incorrect list of\n            installed apps, etc.\n\"\"\"\n\nimport io\nimport os\nimport re\nimport subprocess\nimport sys\nimport textwrap\nimport time\nfrom datetime import datetime, timedelta\nfrom pathlib import Path\nfrom typing import Optional\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom helpers import run_pipx_cli\nfrom package_info import PKG\n\nREPORTS_DIR = \"./reports\"\nREPORT_FILENAME_ROOT = \"all_packages\"\nPACKAGE_NAME_LIST = [\n    \"ansible\",\n    \"awscli\",\n    \"b2\",\n    \"beancount\",\n    \"beets\",\n    \"black\",\n    \"cactus\",\n    \"chert\",\n    # \"cloudtoken\",\n    \"coala\",\n    \"cookiecutter\",\n    \"cython\",\n    \"datasette\",\n    \"diffoscope\",\n    \"doc2dash\",\n    \"doitlive\",\n    \"gdbgui\",\n    \"gns3-gui\",\n    \"grow\",\n    \"guake\",\n    \"gunicorn\",\n    \"howdoi\",\n    \"httpie\",\n    \"hyde\",\n    \"ipython\",\n    \"isort\",\n    \"zest-releaser\",\n    \"kaggle\",\n    \"kibitzr\",\n    \"klaus\",\n    \"kolibri\",\n    \"lektor\",\n    \"localstack\",\n    \"mackup\",\n    \"magic-wormhole\",\n    \"mayan-edms\",\n    \"mkdocs\",\n    \"mycli\",\n    \"nikola\",\n    \"nox\",\n    \"pelican\",\n    \"platformio\",\n    \"ppci\",\n    \"prosopopee\",\n    \"ptpython\",\n    \"pycowsay\",\n    \"pylint\",\n    \"retext\",\n    \"robotframework\",\n    \"shell-functools\",\n    \"speedtest-cli\",\n    \"sphinx\",\n    \"sqlmap\",\n    \"streamlink\",\n    \"taguette\",\n    \"term2048\",\n    \"tox-ini-fmt\",\n    \"visidata\",\n    \"vulture\",\n    \"weblate\",\n    \"youtube-dl\",\n    \"zeo\",\n]\n\n\nclass PackageData:\n    def __init__(self):\n        self.package_name: str = \"\"\n        self.package_spec: str = \"\"\n        self.clear_elapsed_time: Optional[float] = None\n        self.clear_pip_pass: Optional[bool] = None\n        self.clear_pipx_pass: Optional[bool] = None\n        self.sys_elapsed_time: Optional[float] = None\n        self.sys_pip_pass: Optional[bool] = None\n        self.sys_pipx_pass: Optional[bool] = None\n        self.overall_pass: Optional[bool] = None\n\n    @property\n    def clear_pip_pf_str(self) -> str:\n        return self._get_pass_fail_str(\"clear_pip_pass\")\n\n    @property\n    def clear_pipx_pf_str(self) -> str:\n        return self._get_pass_fail_str(\"clear_pipx_pass\")\n\n    @property\n    def sys_pip_pf_str(self) -> str:\n        return self._get_pass_fail_str(\"sys_pip_pass\")\n\n    @property\n    def sys_pipx_pf_str(self) -> str:\n        return self._get_pass_fail_str(\"sys_pipx_pass\")\n\n    @property\n    def overall_pf_str(self) -> str:\n        return self._get_pass_fail_str(\"overall_pass\")\n\n    def _get_pass_fail_str(self, test_attr: str) -> str:\n        if getattr(self, test_attr) is not None:\n            return \"PASS\" if getattr(self, test_attr) else \"FAIL\"\n        else:\n            return \"\"\n\n\nclass ModuleGlobalsData:\n    def __init__(self):\n        self.errors_path = Path(\".\")\n        self.install_data: list[PackageData] = []\n        pyver = sys.version_info\n        self.py_version_display = f\"Python {pyver.major}.{pyver.minor}.{pyver.micro}\"\n        self.py_version_short = f\"{pyver.major}.{pyver.minor}\"\n        self.report_path = Path(\".\")\n        self.sys_platform = sys.platform\n        self.test_class = \"\"\n        self.test_start = datetime.now()\n        self.test_end = datetime.now()  # default, must be set later\n\n    def reset(self, test_class: str = \"\") -> None:\n        self.errors_path = Path(\".\")\n        self.install_data = []\n        self.report_path = Path(\".\")\n        self.test_class = test_class\n        self.test_start = datetime.now()\n\n\n@pytest.fixture(scope=\"module\")\ndef module_globals() -> ModuleGlobalsData:\n    return ModuleGlobalsData()\n\n\ndef pip_cache_purge() -> None:\n    subprocess.run([sys.executable, \"-m\", \"pip\", \"cache\", \"purge\"], check=True)\n\n\ndef write_report_legend(report_legend_path: Path) -> None:\n    with report_legend_path.open(\"w\", encoding=\"utf-8\") as report_legend_fh:\n        print(\n            textwrap.dedent(\n                \"\"\"\n                LEGEND\n                ===========\n                cleared_PATH = PATH used for pipx tests with only pipx bin dir and nothing else\n                sys_PATH = Normal system PATH with all default directories included\n\n                overall = PASS or FAIL for complete end-to-end pipx install, PASS if no errors\n                        or warnings and all the proper apps were installed and linked\n                pip = PASS or FAIL sub-category based only if pip inside of pipx installs\n                        package with/without error\n                pipx = PASS or FAIL sub-category based on the non-pip parts of pipx, including\n                        whether any errors or warnings are present, and if all the proper apps\n                        were installed and linked\n                \"\"\"\n            ).strip(),\n            file=report_legend_fh,\n        )\n\n\ndef format_report_table_header(module_globals: ModuleGlobalsData) -> str:\n    header_string = \"\\n\\n\"\n    header_string += \"=\" * 79 + \"\\n\"\n\n    header_string += f\"{module_globals.sys_platform:16}\"\n    header_string += f\"{module_globals.py_version_display:16}\"\n    header_string += f\"{module_globals.test_start.strftime('%Y-%m-%d %H:%M:%S')}\\n\\n\"\n\n    header_string += f\"{'package_spec':24}{'overall':12}{'cleared_PATH':24}\"\n    header_string += f\"{'system_PATH':24}\\n\"\n\n    header_string += f\"{'':24}{'':12}{'pip':8}{'pipx':8}{'time':8}\"\n    header_string += f\"{'pip':8}{'pipx':8}{'time':8}\\n\"\n\n    header_string += \"-\" * 79\n\n    return header_string\n\n\ndef format_report_table_row(package_data: PackageData) -> str:\n    clear_install_time = f\"{package_data.clear_elapsed_time:>3.0f}s\"\n    if package_data.sys_elapsed_time is not None:\n        sys_install_time = f\"{package_data.sys_elapsed_time:>3.0f}s\"\n    else:\n        sys_install_time = \"\"\n\n    return (\n        f\"{package_data.package_spec:24}{package_data.overall_pf_str:12}\"\n        f\"{package_data.clear_pip_pf_str:8}{package_data.clear_pipx_pf_str:8}\"\n        f\"{clear_install_time:8}\"\n        f\"{package_data.sys_pip_pf_str:8}{package_data.sys_pipx_pf_str:8}\"\n        f\"{sys_install_time:8}\"\n    )\n\n\ndef format_report_table_footer(module_globals: ModuleGlobalsData) -> str:\n    fail_list = []\n    prebuild_list = []\n\n    footer_string = \"\\nSummary\\n\"\n    footer_string += \"-\" * 79 + \"\\n\"\n    for package_data in module_globals.install_data:\n        clear_pip_pass = package_data.clear_pip_pass\n        clear_pipx_pass = package_data.clear_pipx_pass\n        sys_pip_pass = package_data.sys_pip_pass\n        sys_pipx_pass = package_data.sys_pipx_pass\n\n        if clear_pip_pass and clear_pipx_pass:\n            continue\n        elif not clear_pip_pass and sys_pip_pass and sys_pipx_pass:\n            prebuild_list.append(package_data.package_spec)\n        else:\n            fail_list.append(package_data.package_spec)\n    if fail_list:\n        footer_string += \"FAILS:\\n\"\n        for failed_package_spec in sorted(fail_list, key=str.lower):\n            footer_string += f\"    {failed_package_spec}\\n\"\n    if prebuild_list:\n        footer_string += \"Needs prebuilt wheel:\\n\"\n        for prebuild_package_spec in sorted(prebuild_list, key=str.lower):\n            footer_string += f\"    {prebuild_package_spec}\\n\"\n\n    dt_string = module_globals.test_end.strftime(\"%Y-%m-%d %H:%M:%S\")\n    el_datetime = module_globals.test_end - module_globals.test_start\n    el_datetime = el_datetime - timedelta(microseconds=el_datetime.microseconds)\n    footer_string += f\"\\nFinished {dt_string}\\n\"\n    footer_string += f\"Elapsed: {el_datetime}\"\n\n    return footer_string\n\n\ndef verify_installed_resources(\n    resource_type: str,\n    captured_outerr,\n    package_name: str,\n    test_error_fh: io.StringIO,\n    deps: bool = False,\n) -> bool:\n    resource_name = {\"app\": \"apps\", \"man\": \"man_pages\"}[resource_type]\n    resource_name_long = {\"app\": \"apps\", \"man\": \"manual pages\"}[resource_type]\n    package_resources = PKG[package_name][resource_name].copy()\n    if deps:\n        package_resources += PKG[package_name][f\"{resource_name}_of_dependencies\"]\n    if len(package_resources) == 0:\n        return True\n\n    reported_resources_re = re.search(\n        r\"These \" + resource_name_long + r\" are now available\\n((?:    - [^\\n]+\\n)*)\",\n        captured_outerr.out,\n        re.DOTALL,\n    )\n    if reported_resources_re:\n        reported_resources = [x.strip()[2:] for x in reported_resources_re.group(1).strip().split(\"\\n\")]\n        if set(reported_resources) != set(package_resources):\n            resource_success = False\n            print(\"verify_install: REPORTED APPS DO NOT MATCH PACKAGE\", file=test_error_fh)\n            print(\n                f\"pipx reported %s: {reported_resources}\" % resource_name,\n                file=test_error_fh,\n            )\n            print(\n                f\" true package %s: {package_resources}\" % resource_name,\n                file=test_error_fh,\n            )\n        else:\n            resource_success = True\n    else:\n        resource_success = False\n        print(\"verify_install: APPS TESTING ERROR\", file=test_error_fh)\n\n    return resource_success\n\n\ndef verify_post_install(\n    pipx_exit_code: int,\n    captured_outerr,\n    caplog,\n    package_name: str,\n    test_error_fh: io.StringIO,\n    using_clear_path: bool,\n    deps: bool = False,\n) -> tuple[bool, Optional[bool], Optional[Path]]:\n    pip_error_file = None\n    caplog_problem = False\n    install_success = f\"installed package {package_name}\" in captured_outerr.out\n    for record in caplog.records:\n        if \"⚠️\" in record.message or \"WARNING\" in record.message:\n            if using_clear_path or \"was already on your PATH\" not in record.message:\n                caplog_problem = True\n            print(\"verify_install: WARNING IN CAPLOG:\", file=test_error_fh)\n            print(record.message, file=test_error_fh)\n        if \"Fatal error from pip prevented installation\" in record.message:\n            pip_error_file_re = re.search(r\"pip output in file:\\s+(\\S.+)$\", record.message)\n            if pip_error_file_re:\n                pip_error_file = Path(pip_error_file_re.group(1))\n\n    if install_success and PKG[package_name].get(\"apps\", None) is not None:\n        app_success = verify_installed_resources(\"app\", captured_outerr, package_name, test_error_fh, deps=deps)\n    else:\n        app_success = True\n    if install_success and (\n        PKG[package_name].get(\"man_pages\", None) is not None\n        or PKG[package_name].get(\"man_pages_of_dependencies\", None) is not None\n    ):\n        man_success = verify_installed_resources(\"man\", captured_outerr, package_name, test_error_fh, deps=deps)\n    else:\n        man_success = True\n\n    pip_pass = not ((pipx_exit_code != 0) and f\"Error installing {package_name}\" in captured_outerr.err)\n    pipx_pass: Optional[bool]\n    if pip_pass:\n        pipx_pass = install_success and not caplog_problem and app_success and man_success\n    else:\n        pipx_pass = None\n\n    return pip_pass, pipx_pass, pip_error_file\n\n\ndef print_error_report(\n    module_globals: ModuleGlobalsData,\n    command_captured,\n    test_error_fh: io.StringIO,\n    package_spec: str,\n    test_type: str,\n    pip_error_file: Optional[Path],\n) -> None:\n    with module_globals.errors_path.open(\"a\", encoding=\"utf-8\") as errors_fh:\n        print(\"\\n\\n\", file=errors_fh)\n        print(\"=\" * 79, file=errors_fh)\n        print(\n            f\"{package_spec:24}{test_type:16}{module_globals.sys_platform:16}{module_globals.py_version_display}\",\n            file=errors_fh,\n        )\n        print(\"\\nSTDOUT:\", file=errors_fh)\n        print(\"-\" * 76, file=errors_fh)\n        print(command_captured.out, end=\"\", file=errors_fh)\n        print(\"\\nSTDERR:\", file=errors_fh)\n        print(\"-\" * 76, file=errors_fh)\n        print(command_captured.err, end=\"\", file=errors_fh)\n        if pip_error_file is not None:\n            print(\"\\nPIP ERROR LOG FILE:\", file=errors_fh)\n            print(\"-\" * 76, file=errors_fh)\n            with pip_error_file.open(\"r\") as pip_error_fh:\n                print(pip_error_fh.read(), end=\"\", file=errors_fh)\n        print(\"\\n\\nTEST WARNINGS / ERRORS:\", file=errors_fh)\n        print(\"-\" * 76, file=errors_fh)\n        print(test_error_fh.getvalue(), end=\"\", file=errors_fh)\n\n\ndef install_and_verify(\n    capsys: pytest.CaptureFixture,\n    caplog,\n    monkeypatch,\n    module_globals: ModuleGlobalsData,\n    using_clear_path: bool,\n    package_data: PackageData,\n    deps: bool,\n) -> tuple[bool, Optional[bool], float]:\n    _ = capsys.readouterr()\n    caplog.clear()\n\n    test_error_fh = io.StringIO()\n\n    monkeypatch.setenv(\"PATH\", os.getenv(\"PATH_TEST\" if using_clear_path else \"PATH_ORIG\"))\n\n    start_time = time.time()\n    pipx_exit_code = run_pipx_cli(\n        [\"install\", package_data.package_spec, \"--verbose\"] + ([\"--include-deps\"] if deps else [])\n    )\n    elapsed_time = time.time() - start_time\n    captured = capsys.readouterr()\n\n    pip_pass, pipx_pass, pip_error_file = verify_post_install(\n        pipx_exit_code,\n        captured,\n        caplog,\n        package_data.package_name,\n        test_error_fh,\n        using_clear_path=using_clear_path,\n        deps=deps,\n    )\n\n    if not pip_pass or not pipx_pass:\n        print_error_report(\n            module_globals,\n            captured,\n            test_error_fh,\n            package_data.package_spec,\n            \"clear PATH\" if using_clear_path else \"sys PATH\",\n            pip_error_file,\n        )\n\n    return pip_pass, pipx_pass, elapsed_time\n\n\ndef install_package_both_paths(\n    monkeypatch,\n    capsys: pytest.CaptureFixture,\n    caplog,\n    module_globals: ModuleGlobalsData,\n    pipx_temp_env,\n    package_name: str,\n    deps: bool = False,\n) -> bool:\n    package_data = PackageData()\n    module_globals.install_data.append(package_data)\n    package_data.package_name = package_name\n    package_data.package_spec = PKG[package_name][\"spec\"]\n\n    (\n        package_data.clear_pip_pass,\n        package_data.clear_pipx_pass,\n        package_data.clear_elapsed_time,\n    ) = install_and_verify(\n        capsys,\n        caplog,\n        monkeypatch,\n        module_globals,\n        using_clear_path=True,\n        package_data=package_data,\n        deps=deps,\n    )\n    if not package_data.clear_pip_pass:\n        # if we fail to install due to pip install error, try again with\n        #   full system path\n        (\n            package_data.sys_pip_pass,\n            package_data.sys_pipx_pass,\n            package_data.sys_elapsed_time,\n        ) = install_and_verify(\n            capsys,\n            caplog,\n            monkeypatch,\n            module_globals,\n            using_clear_path=False,\n            package_data=package_data,\n            deps=deps,\n        )\n\n    package_data.overall_pass = bool(\n        (package_data.clear_pip_pass and package_data.clear_pipx_pass)\n        or (package_data.sys_pip_pass and package_data.sys_pipx_pass)\n    )\n\n    with module_globals.report_path.open(\"a\", encoding=\"utf-8\") as report_fh:\n        print(format_report_table_row(package_data), file=report_fh, flush=True)\n\n    if not package_data.clear_pip_pass and not package_data.sys_pip_pass:\n        # Use xfail to specify error that is from a pip installation problem\n        pytest.xfail(\"pip installation error\")\n\n    return package_data.overall_pass\n\n\n# use class scope to start and finish at end of all parametrized tests\n@pytest.fixture(scope=\"class\")\ndef start_end_test_class(module_globals: ModuleGlobalsData, request):\n    reports_path = Path(REPORTS_DIR)\n    reports_path.mkdir(exist_ok=True, parents=True)\n\n    module_globals.reset()\n    module_globals.test_class = getattr(request.cls, \"test_class\", \"unknown\")\n    report_filename = (\n        f\"{REPORT_FILENAME_ROOT}_\"\n        f\"{module_globals.test_class}_\"\n        f\"report_\"\n        f\"{module_globals.sys_platform}_\"\n        f\"{module_globals.py_version_short}_\"\n        f\"{module_globals.test_start.strftime('%Y%m%d')}.txt\"\n    )\n    errors_filename = (\n        f\"{REPORT_FILENAME_ROOT}_\"\n        f\"{module_globals.test_class}_\"\n        f\"errors_\"\n        f\"{module_globals.sys_platform}_\"\n        f\"{module_globals.py_version_short}_\"\n        f\"{module_globals.test_start.strftime('%Y%m%d')}.txt\"\n    )\n    module_globals.report_path = reports_path / report_filename\n    module_globals.errors_path = reports_path / errors_filename\n\n    write_report_legend(reports_path / f\"{REPORT_FILENAME_ROOT}_report_legend.txt\")\n\n    with module_globals.report_path.open(\"a\", encoding=\"utf-8\") as report_fh:\n        print(format_report_table_header(module_globals), file=report_fh)\n\n    yield\n\n    module_globals.test_end = datetime.now()\n    with module_globals.report_path.open(\"a\", encoding=\"utf-8\") as report_fh:\n        print(format_report_table_footer(module_globals), file=report_fh)\n\n\nclass TestAllPackagesNoDeps:\n    test_class = \"nodeps\"\n\n    @pytest.mark.parametrize(\"package_name\", PACKAGE_NAME_LIST)\n    @pytest.mark.all_packages\n    def test_all_packages(\n        self,\n        monkeypatch,\n        capsys: pytest.CaptureFixture,\n        caplog,\n        module_globals: ModuleGlobalsData,\n        start_end_test_class,\n        pipx_temp_env,\n        package_name: str,\n    ):\n        pip_cache_purge()\n        assert install_package_both_paths(\n            monkeypatch,\n            capsys,\n            caplog,\n            module_globals,\n            pipx_temp_env,\n            package_name,\n            deps=False,\n        )\n\n\nclass TestAllPackagesDeps:\n    test_class = \"deps\"\n\n    @pytest.mark.parametrize(\"package_name\", PACKAGE_NAME_LIST)\n    @pytest.mark.all_packages\n    def test_deps_all_packages(\n        self,\n        monkeypatch,\n        capsys: pytest.CaptureFixture,\n        caplog,\n        module_globals: ModuleGlobalsData,\n        start_end_test_class,\n        pipx_temp_env,\n        package_name: str,\n    ):\n        pip_cache_purge()\n        assert install_package_both_paths(\n            monkeypatch,\n            capsys,\n            caplog,\n            module_globals,\n            pipx_temp_env,\n            package_name,\n            deps=True,\n        )\n"
  },
  {
    "path": "tests/test_interpreter.py",
    "content": "import shutil\nimport subprocess\nimport sys\nfrom unittest.mock import Mock\n\nimport pytest  # type: ignore[import-not-found]\n\nimport pipx.interpreter\nimport pipx.paths\nimport pipx.standalone_python\nfrom pipx.constants import WINDOWS\nfrom pipx.interpreter import (\n    InterpreterResolutionError,\n    _find_default_windows_python,\n    _get_absolute_python_interpreter,\n    find_python_interpreter,\n)\nfrom pipx.util import PipxError\n\noriginal_which = shutil.which\n\n\n@pytest.mark.skipif(not sys.platform.startswith(\"win\"), reason=\"Looks for Python.exe\")\n@pytest.mark.parametrize(\"venv\", [True, False])\ndef test_windows_python_with_version(monkeypatch, venv):\n    def which(name):\n        if name == \"py\":\n            return \"py\"\n        return original_which(name)\n\n    major = sys.version_info.major\n    minor = sys.version_info.minor\n    monkeypatch.setattr(pipx.interpreter, \"has_venv\", lambda: venv)\n    monkeypatch.setattr(shutil, \"which\", which)\n    python_path = find_python_interpreter(f\"{major}.{minor}\")\n    assert python_path is not None\n    assert f\"{major}.{minor}\" in python_path or f\"{major}{minor}\" in python_path\n    assert python_path.endswith(\"python.exe\")\n\n\n@pytest.mark.skipif(not sys.platform.startswith(\"win\"), reason=\"Looks for Python.exe\")\n@pytest.mark.parametrize(\"venv\", [True, False])\ndef test_windows_python_with_python_and_version(monkeypatch, venv):\n    def which(name):\n        if name == \"py\":\n            return \"py\"\n        return original_which(name)\n\n    major = sys.version_info.major\n    minor = sys.version_info.minor\n    monkeypatch.setattr(pipx.interpreter, \"has_venv\", lambda: venv)\n    monkeypatch.setattr(shutil, \"which\", which)\n    python_path = find_python_interpreter(f\"python{major}.{minor}\")\n    assert python_path is not None\n    assert f\"{major}.{minor}\" in python_path or f\"{major}{minor}\" in python_path\n    assert python_path.endswith(\"python.exe\")\n\n\n@pytest.mark.skipif(not sys.platform.startswith(\"win\"), reason=\"Looks for Python.exe\")\n@pytest.mark.parametrize(\"venv\", [True, False])\ndef test_windows_python_with_python_and_unavailable_version(monkeypatch, venv):\n    def which(name):\n        if name == \"py\":\n            return \"py\"\n        return original_which(name)\n\n    major = sys.version_info.major + 99\n    minor = sys.version_info.minor\n    monkeypatch.setattr(pipx.interpreter, \"has_venv\", lambda: venv)\n    monkeypatch.setattr(shutil, \"which\", which)\n    with pytest.raises(InterpreterResolutionError) as e:\n        find_python_interpreter(f\"python{major}.{minor}\")\n        assert \"py --list\" in str(e)\n\n\ndef test_windows_python_no_version_with_venv(monkeypatch):\n    monkeypatch.setattr(pipx.interpreter, \"has_venv\", lambda: True)\n    assert _find_default_windows_python() == sys.executable\n\n\ndef test_windows_python_no_version_no_venv_with_py(monkeypatch):\n    def which(name):\n        return \"py\"\n\n    monkeypatch.setattr(pipx.interpreter, \"has_venv\", lambda: False)\n    monkeypatch.setattr(shutil, \"which\", which)\n    assert _find_default_windows_python() == \"py\"\n\n\ndef test_windows_python_no_version_no_venv_python_present(monkeypatch):\n    def which(name):\n        if name == \"python\":\n            return \"python\"\n        # Note: returns False for \"py\"\n\n    monkeypatch.setattr(pipx.interpreter, \"has_venv\", lambda: False)\n    monkeypatch.setattr(shutil, \"which\", which)\n    assert _find_default_windows_python() == \"python\"\n\n\ndef test_windows_python_no_version_no_venv_no_python(monkeypatch):\n    def which(name):\n        return None\n\n    monkeypatch.setattr(pipx.interpreter, \"has_venv\", lambda: False)\n    monkeypatch.setattr(shutil, \"which\", which)\n    with pytest.raises(PipxError):\n        _find_default_windows_python()\n\n\n# Test the checks for the store Python.\ndef test_windows_python_no_venv_store_python(monkeypatch):\n    def which(name):\n        if name == \"python\":\n            return \"WindowsApps\"\n\n    class dummy_runner:\n        def __init__(self, rc, out):\n            self.rc = rc\n            self.out = out\n\n        def __call__(self, *args, **kw):\n            ret = Mock()\n            ret.returncode = self.rc\n            ret.stdout = self.out\n            return ret\n\n    monkeypatch.setattr(pipx.interpreter, \"has_venv\", lambda: False)\n    monkeypatch.setattr(shutil, \"which\", which)\n\n    # Store version stub gives return code 9009\n    monkeypatch.setattr(subprocess, \"run\", dummy_runner(9009, \"\"))\n    with pytest.raises(PipxError):\n        _find_default_windows_python()\n\n    # Even if it doesn't, it returns no output\n    monkeypatch.setattr(subprocess, \"run\", dummy_runner(0, \"\"))\n    with pytest.raises(PipxError):\n        _find_default_windows_python()\n\n    # If it *does* pass the tests, we use it as it's not the stub\n    monkeypatch.setattr(subprocess, \"run\", dummy_runner(0, \"3.8\"))\n    assert _find_default_windows_python() == \"WindowsApps\"\n\n\ndef test_bad_env_python(monkeypatch):\n    with pytest.raises(PipxError):\n        _get_absolute_python_interpreter(\"bad_python\")\n\n\ndef test_good_env_python(monkeypatch, capsys):\n    good_exec = _get_absolute_python_interpreter(sys.executable)\n    assert good_exec == sys.executable\n\n\ndef test_find_python_interpreter_by_path(monkeypatch):\n    interpreter_path = sys.executable\n    assert interpreter_path == find_python_interpreter(interpreter_path)\n\n\ndef test_find_python_interpreter_by_version(monkeypatch):\n    major = sys.version_info.major\n    minor = sys.version_info.minor\n    python_path = find_python_interpreter(f\"python{major}.{minor}\")\n    assert python_path == f\"python{major}.{minor}\" or f\"Python\\\\{major}.{minor}\" in python_path\n\n\ndef test_find_python_interpreter_by_wrong_path_raises(monkeypatch):\n    interpreter_path = sys.executable + \"99\"\n    with pytest.raises(InterpreterResolutionError) as e:\n        find_python_interpreter(interpreter_path)\n        assert \"like a path\" in str(e)\n\n\ndef test_find_python_interpreter_missing_on_path_raises(monkeypatch):\n    interpreter = \"1.1\"\n    with pytest.raises(InterpreterResolutionError) as e:\n        find_python_interpreter(interpreter)\n        assert \"Python Launcher\" in str(e)\n        assert \"on your PATH\" in str(e)\n\n\ndef test_fetch_missing_python(monkeypatch, mocked_github_api):\n    def which(name):\n        return None\n\n    monkeypatch.setattr(shutil, \"which\", which)\n\n    major = sys.version_info.major\n    minor = sys.version_info.minor\n    target_python = f\"{major}.{minor}\"\n\n    python_path = find_python_interpreter(target_python, fetch_missing_python=True)\n    assert python_path is not None\n    assert target_python in python_path\n    assert str(pipx.paths.ctx.standalone_python_cachedir) in python_path\n    if WINDOWS:\n        assert python_path.endswith(\"python.exe\")\n    else:\n        assert python_path.endswith(\"python3\")\n    subprocess.run([python_path, \"-c\", \"import sys; print(sys.executable)\"], check=True)\n"
  },
  {
    "path": "tests/test_list.py",
    "content": "import json\nimport os\nimport re\nimport shutil\nimport sys\nimport time\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom helpers import (\n    PIPX_METADATA_LEGACY_VERSIONS,\n    app_name,\n    assert_package_metadata,\n    create_package_info_ref,\n    mock_legacy_venv,\n    remove_venv_interpreter,\n    run_pipx_cli,\n    skip_if_windows,\n)\nfrom package_info import PKG\nfrom pipx import constants, paths, shared_libs\nfrom pipx.pipx_metadata_file import PackageInfo, _json_decoder_object_hook\n\n\ndef test_cli(pipx_temp_env, monkeypatch, capsys):\n    assert not run_pipx_cli([\"list\"])\n    captured = capsys.readouterr()\n    assert \"nothing has been installed with pipx\" in captured.err\n\n\n@skip_if_windows\ndef test_cli_global(pipx_temp_env, monkeypatch, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"installed package\" in captured.out\n\n    assert not run_pipx_cli([\"list\", \"--global\"])\n    captured = capsys.readouterr()\n    assert \"nothing has been installed with pipx\" in captured.err\n\n\ndef test_missing_interpreter(pipx_temp_env, monkeypatch, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n\n    assert not run_pipx_cli([\"list\"])\n    captured = capsys.readouterr()\n    assert \"package pycowsay has invalid interpreter\" not in captured.err\n\n    remove_venv_interpreter(\"pycowsay\")\n\n    assert run_pipx_cli([\"list\"])\n    captured = capsys.readouterr()\n    assert \"package pycowsay has invalid interpreter\" in captured.err\n\n\ndef test_list_suffix(pipx_temp_env, monkeypatch, capsys):\n    suffix = \"_x\"\n    assert not run_pipx_cli([\"install\", \"pycowsay\", f\"--suffix={suffix}\"])\n\n    assert not run_pipx_cli([\"list\"])\n    captured = capsys.readouterr()\n    assert f\"package pycowsay 0.0.0.2 (pycowsay{suffix}),\" in captured.out\n\n\n@pytest.mark.parametrize(\"metadata_version\", PIPX_METADATA_LEGACY_VERSIONS)\ndef test_list_legacy_venv(pipx_temp_env, monkeypatch, capsys, metadata_version):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    mock_legacy_venv(\"pycowsay\", metadata_version=metadata_version)\n\n    if metadata_version is None:\n        assert run_pipx_cli([\"list\"])\n        captured = capsys.readouterr()\n        assert \"package pycowsay has missing internal pipx metadata\" in captured.err\n    else:\n        assert not run_pipx_cli([\"list\"])\n        captured = capsys.readouterr()\n        assert \"package pycowsay 0.0.0.2,\" in captured.out\n\n\n@pytest.mark.parametrize(\"metadata_version\", [\"0.1\"])\ndef test_list_suffix_legacy_venv(pipx_temp_env, monkeypatch, capsys, metadata_version):\n    suffix = \"_x\"\n    assert not run_pipx_cli([\"install\", \"pycowsay\", f\"--suffix={suffix}\"])\n    mock_legacy_venv(f\"pycowsay{suffix}\", metadata_version=metadata_version)\n\n    assert not run_pipx_cli([\"list\"])\n    captured = capsys.readouterr()\n    assert f\"package pycowsay 0.0.0.2 (pycowsay{suffix}),\" in captured.out\n\n\ndef test_list_json(pipx_temp_env, capsys):\n    pipx_venvs_dir = paths.ctx.home / \"venvs\"\n    venv_bin_dir = \"Scripts\" if constants.WINDOWS else \"bin\"\n\n    assert not run_pipx_cli([\"install\", PKG[\"pycowsay\"][\"spec\"]])\n    assert not run_pipx_cli([\"install\", PKG[\"pylint\"][\"spec\"]])\n    assert not run_pipx_cli([\"inject\", \"pylint\", PKG[\"black\"][\"spec\"]])\n    captured = capsys.readouterr()\n\n    assert not run_pipx_cli([\"list\", \"--json\"])\n    captured = capsys.readouterr()\n\n    assert not re.search(r\"\\S\", captured.err)\n    json_parsed = json.loads(captured.out, object_hook=_json_decoder_object_hook)\n\n    # raises error if not valid json\n    assert sorted(json_parsed[\"venvs\"].keys()) == [\"pycowsay\", \"pylint\"]\n\n    # pycowsay venv\n    pycowsay_package_ref = create_package_info_ref(\"pycowsay\", \"pycowsay\", pipx_venvs_dir)\n    assert_package_metadata(\n        PackageInfo(**json_parsed[\"venvs\"][\"pycowsay\"][\"metadata\"][\"main_package\"]),\n        pycowsay_package_ref,\n    )\n    assert json_parsed[\"venvs\"][\"pycowsay\"][\"metadata\"][\"injected_packages\"] == {}\n\n    # pylint venv\n    pylint_package_ref = create_package_info_ref(\n        \"pylint\",\n        \"pylint\",\n        pipx_venvs_dir,\n        app_paths_of_dependencies={\n            \"dill\": [\n                pipx_venvs_dir / \"pylint\" / venv_bin_dir / \"get_gprof\",\n                pipx_venvs_dir / \"pylint\" / venv_bin_dir / \"get_objgraph\",\n                pipx_venvs_dir / \"pylint\" / venv_bin_dir / \"undill\",\n            ],\n            \"isort\": [\n                pipx_venvs_dir / \"pylint\" / venv_bin_dir / app_name(\"isort\"),\n                pipx_venvs_dir / \"pylint\" / venv_bin_dir / app_name(\"isort-identify-imports\"),\n            ],\n        },\n    )\n    assert_package_metadata(\n        PackageInfo(**json_parsed[\"venvs\"][\"pylint\"][\"metadata\"][\"main_package\"]),\n        pylint_package_ref,\n    )\n    assert sorted(json_parsed[\"venvs\"][\"pylint\"][\"metadata\"][\"injected_packages\"].keys()) == [\"black\"]\n    black_package_ref = create_package_info_ref(\"pylint\", \"black\", pipx_venvs_dir, include_apps=False)\n    assert_package_metadata(\n        PackageInfo(**json_parsed[\"venvs\"][\"pylint\"][\"metadata\"][\"injected_packages\"][\"black\"]),\n        black_package_ref,\n    )\n\n\ndef test_list_short(pipx_temp_env, monkeypatch, capsys):\n    assert not run_pipx_cli([\"install\", PKG[\"pycowsay\"][\"spec\"]])\n    assert not run_pipx_cli([\"install\", PKG[\"pylint\"][\"spec\"]])\n    captured = capsys.readouterr()\n\n    assert not run_pipx_cli([\"list\", \"--short\"])\n    captured = capsys.readouterr()\n\n    assert \"pycowsay 0.0.0.2\" in captured.out\n    assert \"pylint 3.0.4\" in captured.out\n\n\ndef test_list_standalone_interpreter(pipx_temp_env, monkeypatch, mocked_github_api, capsys):\n    def which(name):\n        return None\n\n    monkeypatch.setattr(shutil, \"which\", which)\n\n    major = sys.version_info.major\n    minor = sys.version_info.minor\n    target_python = f\"{major}.{minor}\"\n\n    assert not run_pipx_cli(\n        [\n            \"install\",\n            \"--fetch-missing-python\",\n            \"--python\",\n            target_python,\n            PKG[\"pycowsay\"][\"spec\"],\n        ]\n    )\n    captured = capsys.readouterr()\n\n    assert not run_pipx_cli([\"list\"])\n    captured = capsys.readouterr()\n\n    assert \"standalone\" in captured.out\n\n\ndef test_list_does_not_trigger_maintenance(pipx_temp_env, caplog):\n    assert not run_pipx_cli([\"install\", PKG[\"pycowsay\"][\"spec\"]])\n    assert not run_pipx_cli([\"install\", PKG[\"pylint\"][\"spec\"]])\n\n    now = time.time()\n    shared_libs.shared_libs.create(verbose=True, pip_args=[])\n    shared_libs.shared_libs.has_been_updated_this_run = False\n\n    access_time = now  # this can be anything\n    os.utime(\n        shared_libs.shared_libs.pip_path,\n        (access_time, -shared_libs.SHARED_LIBS_MAX_AGE_SEC - 5 * 60 + now),\n    )\n    assert shared_libs.shared_libs.needs_upgrade\n    run_pipx_cli([\"list\"])\n    assert not shared_libs.shared_libs.has_been_updated_this_run\n    assert shared_libs.shared_libs.needs_upgrade\n\n    # same test with --skip-maintenance, which is a no-op\n    # we expect the same result, along with a warning\n    os.utime(\n        shared_libs.shared_libs.pip_path,\n        (access_time, -shared_libs.SHARED_LIBS_MAX_AGE_SEC - 5 * 60 + now),\n    )\n    shared_libs.shared_libs.has_been_updated_this_run = False\n    assert shared_libs.shared_libs.needs_upgrade\n    run_pipx_cli([\"list\", \"--skip-maintenance\"])\n    assert not shared_libs.shared_libs.has_been_updated_this_run\n    assert shared_libs.shared_libs.needs_upgrade\n\n\ndef test_list_pinned_packages(pipx_temp_env, monkeypatch, capsys):\n    assert not run_pipx_cli([\"install\", PKG[\"pycowsay\"][\"spec\"]])\n    assert not run_pipx_cli([\"install\", PKG[\"black\"][\"spec\"]])\n    captured = capsys.readouterr()\n\n    assert not run_pipx_cli([\"pin\", \"black\"])\n    assert not run_pipx_cli([\"list\", \"--pinned\"])\n\n    captured = capsys.readouterr()\n    assert \"black 22.8.0\" in captured.out\n    assert \"pycowsay 0.0.0.2\" not in captured.out\n\n\ndef test_list_pinned_packages_include_injected(pipx_temp_env, monkeypatch, capsys):\n    assert not run_pipx_cli([\"install\", PKG[\"pylint\"][\"spec\"], PKG[\"nox\"][\"spec\"]])\n    assert not run_pipx_cli([\"inject\", \"pylint\", PKG[\"black\"][\"spec\"]])\n\n    assert not run_pipx_cli([\"pin\", \"pylint\"])\n    assert not run_pipx_cli([\"pin\", \"nox\"])\n\n    captured = capsys.readouterr()\n\n    assert not run_pipx_cli([\"list\", \"--pinned\", \"--include-injected\"])\n\n    captured = capsys.readouterr()\n\n    assert \"nox 2023.4.22\" in captured.out\n    assert \"pylint 3.0.4\" in captured.out\n    assert \"black 22.8.0 (injected in venv pylint)\" in captured.out\n"
  },
  {
    "path": "tests/test_main.py",
    "content": "import sys\nfrom unittest import mock\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom helpers import run_pipx_cli\nfrom pipx import main\n\n\ndef test_help_text(monkeypatch, capsys):\n    mock_exit = mock.Mock(side_effect=ValueError(\"raised in test to exit early\"))\n    with mock.patch.object(sys, \"exit\", mock_exit), pytest.raises(ValueError, match=\"raised in test to exit early\"):\n        assert not run_pipx_cli([\"--help\"])\n    captured = capsys.readouterr()\n    assert \"usage: pipx\" in captured.out\n\n\ndef test_version(monkeypatch, capsys):\n    mock_exit = mock.Mock(side_effect=ValueError(\"raised in test to exit early\"))\n    with mock.patch.object(sys, \"exit\", mock_exit), pytest.raises(ValueError, match=\"raised in test to exit early\"):\n        assert not run_pipx_cli([\"--version\"])\n    captured = capsys.readouterr()\n    mock_exit.assert_called_with(0)\n    assert main.__version__ in captured.out.strip()\n\n\n@pytest.mark.parametrize(\n    (\"argv\", \"executable\", \"expected\"),\n    [\n        (\"/usr/bin/pipx\", \"\", \"pipx\"),\n        (\"__main__.py\", \"/usr/bin/python\", \"/usr/bin/python -m pipx\"),\n    ],\n)\ndef test_prog_name(monkeypatch, argv, executable, expected):\n    monkeypatch.setattr(\"pipx.main.sys.argv\", [argv])\n    monkeypatch.setattr(\"pipx.main.sys.executable\", executable)\n    assert main.prog_name() == expected\n\n\ndef test_limit_verbosity():\n    assert not run_pipx_cli([\"list\", \"-qqq\"])\n    assert not run_pipx_cli([\"list\", \"-vvvv\"])\n"
  },
  {
    "path": "tests/test_package_specifier.py",
    "content": "from pathlib import Path\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom pipx.package_specifier import (\n    fix_package_name,\n    parse_specifier_for_install,\n    parse_specifier_for_metadata,\n    parse_specifier_for_upgrade,\n    valid_pypi_name,\n)\nfrom pipx.util import PipxError\n\nTEST_DATA_PATH = \"./testdata/test_package_specifier\"\n\n\n@pytest.mark.parametrize(\n    \"package_spec_in,package_name_out\",\n    [\n        (\"Black\", \"black\"),\n        (\"https://github.com/ambv/black/archive/18.9b0.zip\", None),\n        (\"black @ https://github.com/ambv/black/archive/18.9b0.zip\", None),\n        (\"black-18.9b0-py36-none-any.whl\", None),\n        (\"black-18.9b0.tar.gz\", None),\n    ],\n)\ndef test_valid_pypi_name(package_spec_in, package_name_out):\n    assert valid_pypi_name(package_spec_in) == package_name_out\n\n\n@pytest.mark.parametrize(\n    \"package_spec_in,package_name,package_spec_out\",\n    [\n        (\n            \"https://github.com/ambv/black/archive/18.9b0.zip\",\n            \"black\",\n            \"https://github.com/ambv/black/archive/18.9b0.zip\",\n        ),\n        (\n            \"nox@https://github.com/ambv/black/archive/18.9b0.zip\",\n            \"black\",\n            \"black @ https://github.com/ambv/black/archive/18.9b0.zip\",\n        ),\n        (\n            \"nox[extra]@https://github.com/ambv/black/archive/18.9b0.zip\",\n            \"black\",\n            \"black[extra] @ https://github.com/ambv/black/archive/18.9b0.zip\",\n        ),\n    ],\n)\ndef test_fix_package_name(package_spec_in, package_name, package_spec_out):\n    assert fix_package_name(package_spec_in, package_name) == package_spec_out\n\n\n_ROOT = Path(__file__).parents[1]\n\n\n@pytest.mark.parametrize(\n    \"package_spec_in,package_or_url_correct,valid_spec\",\n    [\n        (\"pipx\", \"pipx\", True),\n        (\"PiPx_stylized.name\", \"pipx-stylized-name\", True),\n        (\"pipx==0.15.0\", \"pipx==0.15.0\", True),\n        (\"pipx>=0.15.0\", \"pipx>=0.15.0\", True),\n        (\"pipx<=0.15.0\", \"pipx<=0.15.0\", True),\n        ('pipx;python_version>=\"3.6\"', \"pipx\", True),\n        ('pipx==0.15.0;python_version>=\"3.6\"', \"pipx==0.15.0\", True),\n        (\"pipx[extra1]\", \"pipx[extra1]\", True),\n        (\"pipx[extra1, extra2]\", \"pipx[extra1,extra2]\", True),\n        (\"src/pipx\", str((_ROOT / \"src\" / \"pipx\").resolve()), True),\n        (\n            \"git+https://github.com/cs01/nox.git@5ea70723e9e6\",\n            \"git+https://github.com/cs01/nox.git@5ea70723e9e6\",\n            True,\n        ),\n        (\n            \"nox@git+https://github.com/cs01/nox.git@5ea70723e9e6\",\n            \"nox @ git+https://github.com/cs01/nox.git@5ea70723e9e6\",\n            True,\n        ),\n        (\n            \"https://github.com/ambv/black/archive/18.9b0.zip\",\n            \"https://github.com/ambv/black/archive/18.9b0.zip\",\n            True,\n        ),\n        (\n            \"black@https://github.com/ambv/black/archive/18.9b0.zip\",\n            \"black @ https://github.com/ambv/black/archive/18.9b0.zip\",\n            True,\n        ),\n        (\n            \"black @ https://github.com/ambv/black/archive/18.9b0.zip\",\n            \"black @ https://github.com/ambv/black/archive/18.9b0.zip\",\n            True,\n        ),\n        (\n            \"black[extra] @ https://github.com/ambv/black/archive/18.9b0.zip\",\n            \"black[extra] @ https://github.com/ambv/black/archive/18.9b0.zip\",\n            True,\n        ),\n        (\n            'my-project[cli] @ git+ssh://git@bitbucket.org/my-company/myproject.git ; python_version<\"3.8\"',\n            \"my-project[cli] @ git+ssh://git@bitbucket.org/my-company/myproject.git\",\n            True,\n        ),\n        (\"path/doesnt/exist\", \"non-existent-path\", False),\n        (\n            \"https:/github.com/ambv/black/archive/18.9b0.zip\",\n            \"URL-syntax-error-slash\",\n            False,\n        ),\n    ],\n)\ndef test_parse_specifier_for_metadata(package_spec_in, package_or_url_correct, valid_spec, monkeypatch, root):\n    monkeypatch.chdir(root)\n    if valid_spec:\n        package_or_url = parse_specifier_for_metadata(package_spec_in)\n        assert package_or_url == package_or_url_correct\n    else:\n        # print package_spec_in for info in case no error is raised\n        print(f\"package_spec_in = {package_spec_in}\")\n        with pytest.raises(PipxError, match=r\"^Unable to parse package spec\"):\n            package_or_url = parse_specifier_for_metadata(package_spec_in)\n\n\n@pytest.mark.parametrize(\n    \"package_spec_in,package_or_url_correct,valid_spec\",\n    [\n        (\"pipx\", \"pipx\", True),\n        (\"PiPx_stylized.name\", \"pipx-stylized-name\", True),\n        (\"pipx==0.15.0\", \"pipx\", True),\n        (\"pipx>=0.15.0\", \"pipx\", True),\n        (\"pipx<=0.15.0\", \"pipx\", True),\n        ('pipx;python_version>=\"3.6\"', \"pipx\", True),\n        ('pipx==0.15.0;python_version>=\"3.6\"', \"pipx\", True),\n        (\"pipx[extra1]\", \"pipx[extra1]\", True),\n        (\"pipx[extra1, extra2]\", \"pipx[extra1,extra2]\", True),\n        (\"src/pipx\", str((_ROOT / \"src\" / \"pipx\").resolve()), True),\n        (\n            \"git+https://github.com/cs01/nox.git@5ea70723e9e6\",\n            \"git+https://github.com/cs01/nox.git@5ea70723e9e6\",\n            True,\n        ),\n        (\n            \"nox@git+https://github.com/cs01/nox.git@5ea70723e9e6\",\n            \"nox @ git+https://github.com/cs01/nox.git@5ea70723e9e6\",\n            True,\n        ),\n        (\n            \"https://github.com/ambv/black/archive/18.9b0.zip\",\n            \"https://github.com/ambv/black/archive/18.9b0.zip\",\n            True,\n        ),\n        (\n            \"black@https://github.com/ambv/black/archive/18.9b0.zip\",\n            \"black @ https://github.com/ambv/black/archive/18.9b0.zip\",\n            True,\n        ),\n        (\n            \"black @ https://github.com/ambv/black/archive/18.9b0.zip\",\n            \"black @ https://github.com/ambv/black/archive/18.9b0.zip\",\n            True,\n        ),\n        (\n            \"black[extra] @ https://github.com/ambv/black/archive/18.9b0.zip\",\n            \"black[extra] @ https://github.com/ambv/black/archive/18.9b0.zip\",\n            True,\n        ),\n        (\n            'my-project[cli] @ git+ssh://git@bitbucket.org/my-company/myproject.git ; python_version<\"3.8\"',\n            \"my-project[cli] @ git+ssh://git@bitbucket.org/my-company/myproject.git\",\n            True,\n        ),\n        (\"path/doesnt/exist\", \"non-existent-path\", False),\n        (\n            \"https:/github.com/ambv/black/archive/18.9b0.zip\",\n            \"URL-syntax-error-slash\",\n            False,\n        ),\n    ],\n)\ndef test_parse_specifier_for_upgrade(package_spec_in, package_or_url_correct, valid_spec, monkeypatch, root):\n    monkeypatch.chdir(root)\n    if valid_spec:\n        package_or_url = parse_specifier_for_upgrade(package_spec_in)\n        assert package_or_url == package_or_url_correct\n    else:\n        # print package_spec_in for info in case no error is raised\n        print(f\"package_spec_in = {package_spec_in}\")\n        with pytest.raises(PipxError, match=r\"^Unable to parse package spec\"):\n            package_or_url = parse_specifier_for_upgrade(package_spec_in)\n\n\n@pytest.mark.parametrize(\n    \"package_spec_in,pip_args_in,package_spec_expected,pip_args_expected,warning_str\",\n    [\n        ('pipx==0.15.0;python_version>=\"3.6\"', [], \"pipx==0.15.0\", [], None),\n        (\"pipx==0.15.0\", [\"--editable\"], \"pipx==0.15.0\", [], \"Ignoring --editable\"),\n        (\n            'pipx==0.15.0;python_version>=\"3.6\"',\n            [],\n            \"pipx==0.15.0\",\n            [],\n            'Ignoring environment markers (python_version >= \"3.6\") in package',\n        ),\n        (\n            \"pipx==0.15.0\",\n            [\"--no-cache-dir\", \"--editable\"],\n            \"pipx==0.15.0\",\n            [\"--no-cache-dir\"],\n            \"Ignoring --editable\",\n        ),\n        (\n            \"git+https://github.com/cs01/nox.git@5ea70723e9e6\",\n            [\"--editable\"],\n            \"git+https://github.com/cs01/nox.git@5ea70723e9e6\",\n            [],\n            \"Ignoring --editable\",\n        ),\n        (\n            \"https://github.com/ambv/black/archive/18.9b0.zip\",\n            [\"--editable\"],\n            \"https://github.com/ambv/black/archive/18.9b0.zip\",\n            [],\n            \"Ignoring --editable\",\n        ),\n        (\n            \"src/pipx\",\n            [\"--editable\"],\n            str(Path(\"src/pipx\").resolve()),\n            [\"--editable\"],\n            None,\n        ),\n        (\n            TEST_DATA_PATH + \"/local_extras\",\n            [],\n            str(Path(TEST_DATA_PATH + \"/local_extras\").resolve),\n            [],\n            None,\n        ),\n        (\n            TEST_DATA_PATH + \"/local_extras[cow]\",\n            [],\n            str(Path(TEST_DATA_PATH + \"/local_extras\").resolve) + \"[cow]\",\n            [],\n            None,\n        ),\n        (\n            TEST_DATA_PATH + \"/local_extras\",\n            [\"--editable\"],\n            str(Path(TEST_DATA_PATH + \"/local_extras\").resolve),\n            [\"--editable\"],\n            None,\n        ),\n        (\n            TEST_DATA_PATH + \"/local_extras[cow]\",\n            [\"--editable\"],\n            str(Path(TEST_DATA_PATH + \"/local_extras\").resolve) + \"[cow]\",\n            [\"--editable\"],\n            None,\n        ),\n    ],\n)\ndef test_parse_specifier_for_install(\n    caplog,\n    package_spec_in,\n    pip_args_in,\n    package_spec_expected,\n    pip_args_expected,\n    warning_str,\n    monkeypatch,\n    root,\n):\n    monkeypatch.chdir(root)\n    parse_specifier_for_install(package_spec_in, pip_args_in)\n    if warning_str is not None:\n        assert warning_str in caplog.text\n"
  },
  {
    "path": "tests/test_pin.py",
    "content": "from helpers import run_pipx_cli\nfrom package_info import PKG\n\n\ndef test_pin(capsys, pipx_temp_env, caplog):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"pin\", \"pycowsay\"])\n    assert not run_pipx_cli([\"upgrade\", \"pycowsay\"])\n\n    assert \"Not upgrading pinned package pycowsay\" in caplog.text\n\n\ndef test_pin_with_suffix(capsys, pipx_temp_env, caplog):\n    assert not run_pipx_cli([\"install\", PKG[\"black\"][\"spec\"], \"--suffix\", \"@1\"])\n    assert not run_pipx_cli([\"pin\", \"black@1\"])\n    assert not run_pipx_cli([\"upgrade\", \"black@1\"])\n\n    assert \"Not upgrading pinned package black@1\" in caplog.text\n\n\ndef test_pin_warning(capsys, pipx_temp_env, caplog):\n    assert not run_pipx_cli([\"install\", PKG[\"nox\"][\"spec\"]])\n    assert not run_pipx_cli([\"pin\", \"nox\"])\n    assert not run_pipx_cli([\"pin\", \"nox\"])\n\n    assert \"Package nox already pinned 😴\" in caplog.text\n\n\ndef test_pin_not_installed_package(capsys, pipx_temp_env):\n    assert run_pipx_cli([\"pin\", \"abc\"])\n\n    captured = capsys.readouterr()\n    assert \"Package abc is not installed\" in captured.err\n\n\ndef test_pin_injected_packages_only(capsys, pipx_temp_env, caplog):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"inject\", \"pycowsay\", \"black\", PKG[\"pylint\"][\"spec\"]])\n\n    assert not run_pipx_cli([\"pin\", \"pycowsay\", \"--injected-only\"])\n\n    captured = capsys.readouterr()\n\n    assert \"Pinned 2 packages in venv pycowsay\" in captured.out\n    assert \"black\" in captured.out\n    assert \"pylint\" in captured.out\n\n    assert not run_pipx_cli([\"upgrade\", \"pycowsay\", \"--include-injected\"])\n\n    assert \"Not upgrading pinned package black in venv pycowsay\" in caplog.text\n    assert \"Not upgrading pinned package pylint in venv pycowsay\" in caplog.text\n\n\ndef test_pin_injected_packages_with_skip(capsys, pipx_temp_env):\n    assert not run_pipx_cli([\"install\", \"black\"])\n    assert not run_pipx_cli([\"inject\", \"black\", PKG[\"pylint\"][\"spec\"], PKG[\"isort\"][\"spec\"]])\n\n    _ = capsys.readouterr()\n\n    assert not run_pipx_cli([\"pin\", \"black\", \"--injected-only\", \"--skip\", \"isort\"])\n\n    captured = capsys.readouterr()\n\n    assert \"pylint\" in captured.out\n    assert \"isort\" not in captured.out\n"
  },
  {
    "path": "tests/test_pipx_metadata_file.py",
    "content": "import sys\nfrom dataclasses import replace\nfrom pathlib import Path\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom helpers import assert_package_metadata, create_package_info_ref, run_pipx_cli\nfrom package_info import PKG\nfrom pipx import paths\nfrom pipx.pipx_metadata_file import PackageInfo, PipxMetadata\nfrom pipx.util import PipxError\n\nTEST_PACKAGE1 = PackageInfo(\n    package=\"test_package\",\n    package_or_url=\"test_package_url\",\n    pip_args=[],\n    include_apps=True,\n    include_dependencies=False,\n    apps=[\"testapp\"],\n    app_paths=[Path(\"/usr/bin\")],\n    apps_of_dependencies=[\"dep1\"],\n    app_paths_of_dependencies={\"dep1\": [Path(\"bin\")]},\n    man_pages=[str(Path(\"man1/testapp.1\"))],\n    man_pages_of_dependencies=[str(Path(\"man1/dep1.1\"))],\n    man_paths_of_dependencies={\"dep1\": [Path(\"man1/dep1.1\")]},\n    package_version=\"0.1.2\",\n)\nTEST_PACKAGE2 = PackageInfo(\n    package=\"inj_package\",\n    package_or_url=\"inj_package_url\",\n    pip_args=[\"-e\"],\n    include_apps=True,\n    include_dependencies=False,\n    apps=[\"injapp\"],\n    app_paths=[Path(\"/usr/bin\")],\n    apps_of_dependencies=[\"dep2\"],\n    app_paths_of_dependencies={\"dep2\": [Path(\"bin\")]},\n    man_pages=[str(Path(\"man1/injapp.1\"))],\n    man_pages_of_dependencies=[str(Path(\"man1/dep2.1\"))],\n    man_paths_of_dependencies={\"dep2\": [Path(\"man1/dep2.1\")]},\n    package_version=\"6.7.8\",\n)\n\n\ndef test_pipx_metadata_file_create(tmp_path):\n    venv_dir = tmp_path / TEST_PACKAGE1.package\n    venv_dir.mkdir()\n\n    pipx_metadata = PipxMetadata(venv_dir)\n    pipx_metadata.main_package = TEST_PACKAGE1\n    pipx_metadata.python_version = \"3.4.5\"\n    pipx_metadata.source_interpreter = Path(sys.executable)\n    pipx_metadata.venv_args = [\"--system-site-packages\"]\n    pipx_metadata.injected_packages = {\"injected\": TEST_PACKAGE2}\n    pipx_metadata.write()\n\n    pipx_metadata2 = PipxMetadata(venv_dir)\n\n    for attribute in [\n        \"venv_dir\",\n        \"main_package\",\n        \"python_version\",\n        \"venv_args\",\n        \"injected_packages\",\n    ]:\n        assert getattr(pipx_metadata, attribute) == getattr(pipx_metadata2, attribute)\n\n\n@pytest.mark.parametrize(\n    \"test_package\",\n    [\n        replace(TEST_PACKAGE1, include_apps=False),\n        replace(TEST_PACKAGE1, package=None),\n        replace(TEST_PACKAGE1, package_or_url=None),\n    ],\n)\ndef test_pipx_metadata_file_validation(tmp_path, test_package):\n    venv_dir = tmp_path / \"venv\"\n    venv_dir.mkdir()\n\n    pipx_metadata = PipxMetadata(venv_dir)\n    pipx_metadata.main_package = test_package\n    pipx_metadata.python_version = \"3.4.5\"\n    pipx_metadata.source_interpreter = Path(sys.executable)\n    pipx_metadata.venv_args = [\"--system-site-packages\"]\n    pipx_metadata.injected_packages = {}\n\n    with pytest.raises(PipxError):\n        pipx_metadata.write()\n\n\ndef test_package_install(monkeypatch, tmp_path, pipx_temp_env):\n    pipx_venvs_dir = paths.ctx.home / \"venvs\"\n\n    run_pipx_cli([\"install\", PKG[\"pycowsay\"][\"spec\"]])\n    assert (pipx_venvs_dir / \"pycowsay\" / \"pipx_metadata.json\").is_file()\n\n    pipx_metadata = PipxMetadata(pipx_venvs_dir / \"pycowsay\")\n    pycowsay_package_ref = create_package_info_ref(\"pycowsay\", \"pycowsay\", pipx_venvs_dir)\n    assert_package_metadata(pipx_metadata.main_package, pycowsay_package_ref)\n    assert pipx_metadata.injected_packages == {}\n\n\ndef test_package_inject(monkeypatch, tmp_path, pipx_temp_env):\n    pipx_venvs_dir = paths.ctx.home / \"venvs\"\n\n    run_pipx_cli([\"install\", PKG[\"pycowsay\"][\"spec\"]])\n    run_pipx_cli([\"inject\", \"pycowsay\", PKG[\"black\"][\"spec\"]])\n\n    assert (pipx_venvs_dir / \"pycowsay\" / \"pipx_metadata.json\").is_file()\n    pipx_metadata = PipxMetadata(pipx_venvs_dir / \"pycowsay\")\n\n    assert pipx_metadata.injected_packages.keys() == {\"black\"}\n    black_package_ref = create_package_info_ref(\"pycowsay\", \"black\", pipx_venvs_dir, include_apps=False)\n    assert_package_metadata(pipx_metadata.injected_packages[\"black\"], black_package_ref)\n"
  },
  {
    "path": "tests/test_reinstall.py",
    "content": "import sys\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom helpers import PIPX_METADATA_LEGACY_VERSIONS, mock_legacy_venv, run_pipx_cli, skip_if_windows\n\n\ndef test_reinstall(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"reinstall\", \"--python\", sys.executable, \"pycowsay\"])\n\n\n@skip_if_windows\ndef test_reinstall_global(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"--global\", \"pycowsay\"])\n    assert not run_pipx_cli([\"reinstall\", \"--global\", \"--python\", sys.executable, \"pycowsay\"])\n\n\ndef test_reinstall_nonexistent(pipx_temp_env, capsys):\n    assert run_pipx_cli([\"reinstall\", \"--python\", sys.executable, \"nonexistent\"])\n    assert \"Nothing to reinstall for nonexistent\" in capsys.readouterr().out\n\n\n@pytest.mark.parametrize(\"metadata_version\", PIPX_METADATA_LEGACY_VERSIONS)\ndef test_reinstall_legacy_venv(pipx_temp_env, capsys, metadata_version):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    mock_legacy_venv(\"pycowsay\", metadata_version=metadata_version)\n\n    assert not run_pipx_cli([\"reinstall\", \"--python\", sys.executable, \"pycowsay\"])\n\n\ndef test_reinstall_suffix(pipx_temp_env, capsys):\n    suffix = \"_x\"\n    assert not run_pipx_cli([\"install\", \"pycowsay\", f\"--suffix={suffix}\"])\n\n    assert not run_pipx_cli([\"reinstall\", \"--python\", sys.executable, f\"pycowsay{suffix}\"])\n\n\n@pytest.mark.parametrize(\"metadata_version\", [\"0.1\"])\ndef test_reinstall_suffix_legacy_venv(pipx_temp_env, capsys, metadata_version):\n    suffix = \"_x\"\n    assert not run_pipx_cli([\"install\", \"pycowsay\", f\"--suffix={suffix}\"])\n    mock_legacy_venv(f\"pycowsay{suffix}\", metadata_version=metadata_version)\n\n    assert not run_pipx_cli([\"reinstall\", \"--python\", sys.executable, f\"pycowsay{suffix}\"])\n\n\ndef test_reinstall_specifier(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"pylint==3.0.4\"])\n\n    # clear capsys before reinstall\n    captured = capsys.readouterr()\n\n    assert not run_pipx_cli([\"reinstall\", \"--python\", sys.executable, \"pylint\"])\n    captured = capsys.readouterr()\n    assert \"installed package pylint 3.0.4\" in captured.out\n\n\ndef test_reinstall_with_path(pipx_temp_env, capsys, tmp_path):\n    path = tmp_path / \"some\" / \"path\"\n\n    assert run_pipx_cli([\"reinstall\", str(path)])\n    captured = capsys.readouterr()\n\n    assert \"Expected the name of an installed package\" in captured.err.replace(\"\\n\", \" \")\n\n    assert run_pipx_cli([\"reinstall\", str(path.resolve())])\n    captured = capsys.readouterr()\n\n    assert \"Expected the name of an installed package\" in captured.err.replace(\"\\n\", \" \")\n\n\ndef test_reinstall_pinned_package(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"black\"])\n    assert not run_pipx_cli([\"pin\", \"black\"])\n    assert run_pipx_cli([\"reinstall\", \"black\"])\n    captured = capsys.readouterr()\n    assert \"pinned\" in captured.err\n\n    assert not run_pipx_cli([\"unpin\", \"black\"])\n    assert not run_pipx_cli([\"reinstall\", \"black\"])\n    captured = capsys.readouterr()\n    assert \"installed package black\" in captured.out\n"
  },
  {
    "path": "tests/test_reinstall_all.py",
    "content": "import sys\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom helpers import PIPX_METADATA_LEGACY_VERSIONS, mock_legacy_venv, run_pipx_cli\nfrom pipx import shared_libs\n\n\ndef test_reinstall_all(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"reinstall-all\", \"--python\", sys.executable])\n\n\ndef test_reinstall_all_none(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"reinstall-all\"])\n    captured = capsys.readouterr()\n    assert \"No packages reinstalled after running 'pipx reinstall-all'\" in captured.out\n\n\n@pytest.mark.parametrize(\"metadata_version\", PIPX_METADATA_LEGACY_VERSIONS)\ndef test_reinstall_all_legacy_venv(pipx_temp_env, capsys, metadata_version):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    mock_legacy_venv(\"pycowsay\", metadata_version=metadata_version)\n\n    assert not run_pipx_cli([\"reinstall-all\", \"--python\", sys.executable])\n\n\ndef test_reinstall_all_suffix(pipx_temp_env, capsys):\n    suffix = \"_x\"\n    assert not run_pipx_cli([\"install\", \"pycowsay\", f\"--suffix={suffix}\"])\n\n    assert not run_pipx_cli([\"reinstall-all\", \"--python\", sys.executable])\n\n\n@pytest.mark.parametrize(\"metadata_version\", [\"0.1\"])\ndef test_reinstall_all_suffix_legacy_venv(pipx_temp_env, capsys, metadata_version):\n    suffix = \"_x\"\n    assert not run_pipx_cli([\"install\", \"pycowsay\", f\"--suffix={suffix}\"])\n    mock_legacy_venv(f\"pycowsay{suffix}\", metadata_version=metadata_version)\n\n    assert not run_pipx_cli([\"reinstall-all\", \"--python\", sys.executable])\n\n\ndef test_reinstall_all_triggers_shared_libs_upgrade(pipx_temp_env, caplog, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n\n    shared_libs.shared_libs.has_been_updated_this_run = False\n    caplog.clear()\n\n    assert not run_pipx_cli([\"reinstall-all\"])\n    assert \"Upgrading shared libraries in\" in caplog.text\n"
  },
  {
    "path": "tests/test_run.py",
    "content": "import logging\nimport os\nimport subprocess\nimport sys\nimport textwrap\nfrom pathlib import Path\nfrom unittest import mock\n\nimport pytest  # type: ignore[import-not-found]\n\nimport pipx.main\nimport pipx.util\nfrom helpers import run_pipx_cli\nfrom package_info import PKG\nfrom pipx import paths, shared_libs\n\n\ndef test_help_text(pipx_temp_env, monkeypatch, capsys):\n    mock_exit = mock.Mock(side_effect=ValueError(\"raised in test to exit early\"))\n    with mock.patch.object(sys, \"exit\", mock_exit), pytest.raises(ValueError, match=\"raised in test to exit early\"):\n        run_pipx_cli([\"run\", \"--help\"])\n    captured = capsys.readouterr()\n    assert \"Download the latest version of a package\" in captured.out\n\n\ndef execvpe_mock(cmd_path, cmd_args, env):\n    return_code = subprocess.run(\n        [str(x) for x in cmd_args],\n        env=env,\n        capture_output=False,\n        encoding=\"utf-8\",\n        text=True,\n        check=False,\n    ).returncode\n    sys.exit(return_code)\n\n\ndef run_pipx_cli_exit(pipx_cmd_list, assert_exit=None):\n    with pytest.raises(SystemExit) as sys_exit:\n        run_pipx_cli(pipx_cmd_list)\n    if assert_exit is not None:\n        assert sys_exit.type is SystemExit\n        assert sys_exit.value.code == assert_exit\n\n\n@pytest.mark.parametrize(\"package_name\", [\"pycowsay\", \"pycowsay==0.0.0.2\", \"pycowsay>=0.0.0.2\"])\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_simple_run(pipx_temp_env, monkeypatch, capsys, package_name):\n    run_pipx_cli_exit([\"run\", package_name, \"--help\"])\n    captured = capsys.readouterr()\n    assert \"Download the latest version of a package\" not in captured.out\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_cache(pipx_temp_env, monkeypatch, capsys, caplog):\n    run_pipx_cli_exit([\"run\", \"pycowsay\", \"cowsay\", \"args\"])\n    caplog.set_level(logging.DEBUG)\n    run_pipx_cli_exit([\"run\", \"--verbose\", \"pycowsay\", \"cowsay\", \"args\"], assert_exit=0)\n    assert \"Reusing cached venv\" in caplog.text\n\n    run_pipx_cli_exit([\"run\", \"--no-cache\", \"pycowsay\", \"cowsay\", \"args\"])\n    assert \"Removing cached venv\" in caplog.text\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_cachedir_tag(pipx_ultra_temp_env, monkeypatch, capsys, caplog):\n    tag_path = paths.ctx.venv_cache / \"CACHEDIR.TAG\"\n    assert not tag_path.exists()\n\n    # Run pipx to create tag\n    caplog.set_level(logging.DEBUG)\n    run_pipx_cli_exit([\"run\", \"pycowsay\", \"cowsay\", \"args\"])\n    assert \"Adding CACHEDIR.TAG to cache directory\" in caplog.text\n    assert tag_path.exists()\n    caplog.clear()\n\n    # Run pipx again to verify the tag file is not recreated\n    run_pipx_cli_exit([\"run\", \"pycowsay\", \"cowsay\", \"args\"])\n    assert \"Adding CACHEDIR.TAG to cache directory\" not in caplog.text\n    assert tag_path.exists()\n\n    # Verify the tag file starts with the required signature.\n    with tag_path.open(\"r\") as tag_file:\n        assert tag_file.read().startswith(\"Signature: 8a477f597d28d172789f06886806bc55\")\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_script_from_internet(pipx_temp_env, capsys):\n    run_pipx_cli_exit(\n        [\n            \"run\",\n            \"https://gist.githubusercontent.com/cs01/\"\n            \"fa721a17a326e551ede048c5088f9e0f/raw/\"\n            \"6bdfbb6e9c1132b1c38fdd2f195d4a24c540c324/pipx-demo.py\",\n        ],\n        assert_exit=0,\n    )\n\n\n@pytest.mark.parametrize(\n    \"input_run_args,expected_app_with_args\",\n    [\n        ([\"--\", \"pycowsay\", \"--\", \"hello\"], [\"pycowsay\", \"--\", \"hello\"]),\n        ([\"--\", \"pycowsay\", \"--\", \"--\", \"hello\"], [\"pycowsay\", \"--\", \"--\", \"hello\"]),\n        ([\"--\", \"pycowsay\", \"hello\", \"--\"], [\"pycowsay\", \"hello\", \"--\"]),\n        ([\"--\", \"pycowsay\", \"hello\", \"--\", \"--\"], [\"pycowsay\", \"hello\", \"--\", \"--\"]),\n        ([\"--\", \"pycowsay\", \"--\"], [\"pycowsay\", \"--\"]),\n        ([\"--\", \"pycowsay\", \"--\", \"--\"], [\"pycowsay\", \"--\", \"--\"]),\n        ([\"pycowsay\", \"--\", \"hello\"], [\"pycowsay\", \"--\", \"hello\"]),\n        ([\"pycowsay\", \"--\", \"--\", \"hello\"], [\"pycowsay\", \"--\", \"--\", \"hello\"]),\n        ([\"pycowsay\", \"hello\", \"--\"], [\"pycowsay\", \"hello\", \"--\"]),\n        ([\"pycowsay\", \"hello\", \"--\", \"--\"], [\"pycowsay\", \"hello\", \"--\", \"--\"]),\n        ([\"pycowsay\", \"--\"], [\"pycowsay\", \"--\"]),\n        ([\"pycowsay\", \"--\", \"--\"], [\"pycowsay\", \"--\", \"--\"]),\n        ([\"--\", \"--\", \"pycowsay\", \"--\"], [\"--\", \"pycowsay\", \"--\"]),\n    ],\n)\ndef test_appargs_doubledash(pipx_temp_env, capsys, monkeypatch, input_run_args, expected_app_with_args):\n    parser, _ = pipx.main.get_command_parser()\n    monkeypatch.setattr(sys, \"argv\", [\"pipx\", \"run\"] + input_run_args)\n    parsed_pipx_args = parser.parse_args()\n    pipx.main.check_args(parsed_pipx_args)\n    assert parsed_pipx_args.app_with_args == expected_app_with_args\n\n\ndef test_run_ensure_null_pythonpath():\n    env = os.environ.copy()\n    env[\"PYTHONPATH\"] = \"test\"\n    assert (\n        \"None\"\n        in subprocess.run(\n            [\n                sys.executable,\n                \"-m\",\n                \"pipx\",\n                \"run\",\n                \"ipython\",\n                \"-c\",\n                \"import os; print(os.environ.get('PYTHONPATH'))\",\n            ],\n            env=env,\n            capture_output=True,\n            text=True,\n            check=True,\n        ).stdout\n    )\n\n\n# packages listed roughly in order of increasing test duration\n@pytest.mark.parametrize(\n    \"package, package_or_url, app_appargs, skip_win\",\n    [\n        (\"pycowsay\", \"pycowsay\", [\"pycowsay\", \"hello\"], False),\n        (\"shell-functools\", PKG[\"shell-functools\"][\"spec\"], [\"filter\", \"--help\"], True),\n        (\"black\", PKG[\"black\"][\"spec\"], [\"black\", \"--help\"], False),\n        (\"pylint\", PKG[\"pylint\"][\"spec\"], [\"pylint\", \"--help\"], False),\n        (\"kaggle\", PKG[\"kaggle\"][\"spec\"], [\"kaggle\", \"--help\"], False),\n        (\"ipython\", PKG[\"ipython\"][\"spec\"], [\"ipython\", \"--version\"], False),\n        # (\"cloudtoken\", PKG[\"cloudtoken\"][\"spec\"], [\"cloudtoken\", \"--help\"], True),\n        (\"awscli\", PKG[\"awscli\"][\"spec\"], [\"aws\", \"--help\"], True),\n        # (\"ansible\", PKG[\"ansible\"][\"spec\"], [\"ansible\", \"--help\"]), # takes too long\n    ],\n)\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_package_determination(caplog, pipx_temp_env, package, package_or_url, app_appargs, skip_win):\n    if sys.platform.startswith(\"win\") and skip_win:\n        # Skip packages with 'scripts' in setup.py that don't work on Windows\n        pytest.skip()\n\n    caplog.set_level(logging.INFO)\n\n    run_pipx_cli_exit([\"run\", \"--verbose\", \"--spec\", package_or_url, \"--\"] + app_appargs)\n\n    assert \"Cannot determine package name\" not in caplog.text\n    assert f\"Determined package name: {package}\" in caplog.text\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_without_requirements(caplog, pipx_temp_env, tmp_path):\n    script = tmp_path / \"test.py\"\n    out = tmp_path / \"output.txt\"\n    test_str = \"Hello, world!\"\n    script.write_text(\n        textwrap.dedent(\n            f\"\"\"\n                from pathlib import Path\n                Path({str(out)!r}).write_text({test_str!r})\n            \"\"\"\n        ).strip()\n    )\n    run_pipx_cli_exit([\"run\", script.as_uri()])\n    assert out.read_text() == test_str\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\n@pytest.mark.parametrize(\n    \"script_text, expected_output\",\n    [\n        pytest.param(\n            \"\"\"\n            # /// script\n            # dependencies = []\n            # ///\n            from pathlib import Path\n            Path({out!r}).write_text(\"explicit-empty\")\n            \"\"\",\n            \"explicit-empty\",\n            id=\"explicit-empty-dependencies\",\n        ),\n        pytest.param(\n            \"\"\"\n            # /// script\n            # ///\n            from pathlib import Path\n            Path({out!r}).write_text(\"implicit-empty\")\n            \"\"\",\n            \"implicit-empty\",\n            id=\"implicit-empty-dependencies\",\n        ),\n        pytest.param(\n            \"\"\"\n            # /// script\n            # dependencies = [\"requests==2.31.0\"]\n            # ///\n\n            # Check requests can be imported\n            import requests\n            # Check dependencies of requests can be imported\n            import certifi\n            # Check the installed version\n            from pathlib import Path\n            Path({out!r}).write_text(requests.__version__)\n            \"\"\",\n            \"2.31.0\",\n            id=\"non-empty-dependencies\",\n        ),\n    ],\n)\ndef test_run_with_requirements(script_text, expected_output, caplog, pipx_temp_env, tmp_path):\n    script = tmp_path / \"test.py\"\n    out = tmp_path / \"output.txt\"\n    script.write_text(\n        textwrap.dedent(script_text.format(out=str(out))).strip(),\n        encoding=\"utf-8\",\n    )\n    run_pipx_cli_exit([\"run\", script.as_uri()])\n    assert out.read_text() == expected_output\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_with_requirements_old(caplog, pipx_temp_env, tmp_path):\n    script = tmp_path / \"test.py\"\n    out = tmp_path / \"output.txt\"\n    script.write_text(\n        textwrap.dedent(\n            f\"\"\"\n                # /// pyproject\n                # run.requirements = [\"requests==2.31.0\"]\n                # ///\n\n                # Check requests can be imported\n                import requests\n                # Check dependencies of requests can be imported\n                import certifi\n                # Check the installed version\n                from pathlib import Path\n                Path({str(out)!r}).write_text(requests.__version__)\n            \"\"\"\n        ).strip(),\n        encoding=\"utf-8\",\n    )\n    with pytest.raises(ValueError):\n        run_pipx_cli_exit([\"run\", script.as_uri()])\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_correct_traceback(capfd, pipx_temp_env, tmp_path):\n    script = tmp_path / \"test.py\"\n    script.write_text(\n        textwrap.dedent(\n            \"\"\"\n                raise RuntimeError(\"Should fail\")\n            \"\"\"\n        ).strip()\n    )\n\n    with pytest.raises(SystemExit):\n        run_pipx_cli([\"run\", str(script)])\n\n    captured = capfd.readouterr()\n    assert \"test.py\" in captured.err\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_with_args(caplog, pipx_temp_env, tmp_path):\n    script = tmp_path / \"test.py\"\n    out = tmp_path / \"output.txt\"\n    script.write_text(\n        textwrap.dedent(\n            f\"\"\"\n                import sys\n                from pathlib import Path\n                Path({str(out)!r}).write_text(str(int(sys.argv[1]) + 1))\n            \"\"\"\n        ).strip()\n    )\n    run_pipx_cli_exit([\"run\", script.as_uri(), \"1\"])\n    assert out.read_text() == \"2\"\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_with_requirements_and_args(caplog, pipx_temp_env, tmp_path):\n    script = tmp_path / \"test.py\"\n    out = tmp_path / \"output.txt\"\n    script.write_text(\n        textwrap.dedent(\n            f\"\"\"\n                # /// script\n                # dependencies = [\"packaging\"]\n                # ///\n                import packaging\n                import sys\n                from pathlib import Path\n                Path({str(out)!r}).write_text(str(int(sys.argv[1]) + 1))\n            \"\"\"\n        ).strip()\n    )\n    run_pipx_cli_exit([\"run\", script.as_uri(), \"1\"])\n    assert out.read_text() == \"2\"\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_with_failing_requirements(capfd, pipx_temp_env, tmp_path):\n    script = tmp_path / \"test.py\"\n    script.write_text(\n        textwrap.dedent(\n            \"\"\"\n                # /// script\n                # dependencies = [\"will_fail @ git+https://0.0.0.0/will_fail.git\"]\n                # ///\n                import will_fail\n            \"\"\"\n        ).strip()\n    )\n\n    # Attempt first invocation of `pipx run`.\n    # This should fail as the `will_fail` package will not be able to be installed.\n    return_code = run_pipx_cli([\"run\", str(script)])\n    captured = capfd.readouterr()\n\n    assert return_code != 0\n    assert \"Error installing will_fail @ git+https://0.0.0.0/will_fail.git.\" in captured.err\n\n    # Attempt second invocation of `pipx run`.\n    # If above failure was detected and the temporary venv marked for deletion,\n    # then this should fail in the same manner.\n    # If the above failure was not detected, then a ModuleNotFoundError will be raised.\n    return_code = run_pipx_cli([\"run\", str(script)])\n    captured = capfd.readouterr()\n\n    assert return_code != 0\n    assert \"ModuleNotFoundError: No module named 'will_fail'\" not in captured.err\n    assert \"Error installing will_fail @ git+https://0.0.0.0/will_fail.git.\" in captured.err\n\n\ndef test_pip_args_forwarded_to_shared_libs(pipx_ultra_temp_env, capsys, caplog):\n    # strategy:\n    # 1. start from an empty env to ensure the next command would trigger a shared lib update\n    assert shared_libs.shared_libs.needs_upgrade\n    # 2. install any package with --no-index\n    # and check that the shared library update phase fails\n    return_code = run_pipx_cli([\"run\", \"--verbose\", \"--pip-args=--no-index\", \"pycowsay\", \"hello\"])\n    assert \"Upgrading shared libraries in\" in caplog.text\n\n    captured = capsys.readouterr()\n    assert return_code != 0\n    assert \"ERROR: Could not find a version that satisfies the requirement pip\" in captured.err\n    assert \"Failed to upgrade shared libraries\" in caplog.text\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_with_invalid_requirement(capsys, pipx_temp_env, tmp_path):\n    script = tmp_path / \"test.py\"\n    script.write_text(\n        textwrap.dedent(\n            \"\"\"\n                # /// script\n                # dependencies = [\"this is an invalid requirement\"]\n                # ///\n                print()\n            \"\"\"\n        ).strip()\n    )\n    ret = run_pipx_cli([\"run\", script.as_uri()])\n    assert ret == 1\n\n    captured = capsys.readouterr()\n    assert \"Invalid requirement this is an invalid requirement\" in captured.err\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_script_by_absolute_name(caplog, pipx_temp_env, tmp_path):\n    script = tmp_path / \"test.py\"\n    out = tmp_path / \"output.txt\"\n    test_str = \"Hello, world!\"\n    script.write_text(\n        textwrap.dedent(\n            f\"\"\"\n                from pathlib import Path\n                Path({str(out)!r}).write_text({test_str!r})\n            \"\"\"\n        ).strip()\n    )\n    run_pipx_cli_exit([\"run\", \"--path\", str(script)])\n    assert out.read_text() == test_str\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_script_by_relative_name(caplog, pipx_temp_env, monkeypatch, tmp_path):\n    script = tmp_path / \"test.py\"\n    out = tmp_path / \"output.txt\"\n    test_str = \"Hello, world!\"\n    script.write_text(\n        textwrap.dedent(\n            f\"\"\"\n                from pathlib import Path\n                Path({str(out)!r}).write_text({test_str!r})\n            \"\"\"\n        ).strip()\n    )\n    with monkeypatch.context() as m:\n        m.chdir(tmp_path)\n        run_pipx_cli_exit([\"run\", \"test.py\"])\n    assert out.read_text() == test_str\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\n@pytest.mark.skipif(sys.platform.startswith(\"win\"), reason=\"uses file descriptor\")\ndef test_run_script_by_file_descriptor(caplog, pipx_temp_env, monkeypatch, tmp_path):\n    read_fd, write_fd = os.pipe()\n    out = tmp_path / \"output.txt\"\n    test_str = \"Hello, world!\"\n\n    os.write(\n        write_fd,\n        textwrap.dedent(\n            f\"\"\"\n                from pathlib import Path\n                Path({str(out)!r}).write_text({test_str!r})\n            \"\"\"\n        )\n        .strip()\n        .encode(\"utf-8\"),\n    )\n    os.close(write_fd)\n\n    with monkeypatch.context() as m:\n        m.chdir(tmp_path)\n        try:\n            run_pipx_cli_exit([\"run\", f\"/dev/fd/{read_fd}\"])\n        finally:\n            os.close(read_fd)\n    assert out.read_text() == test_str\n\n\n@pytest.mark.skipif(not sys.platform.startswith(\"win\"), reason=\"uses windows version format\")\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_with_windows_python_version(caplog, pipx_temp_env, tmp_path):\n    script = tmp_path / \"test.py\"\n    out = tmp_path / \"output.txt\"\n    script.write_text(\n        textwrap.dedent(\n            f\"\"\"\n                import sys\n                from pathlib import Path\n                Path({str(out)!r}).write_text(sys.version)\n            \"\"\"\n        ).strip()\n    )\n    run_pipx_cli_exit([\"run\", script.as_uri(), \"--python\", \"3.13\"])\n    assert \"3.13\" in out.read_text()\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_verify_script_name_provided(pipx_temp_env, capsys, tmpdir):\n    tmpdir.mkdir(\"black\")\n    run_pipx_cli_exit([\"run\", \"black\"])\n    captured = capsys.readouterr()\n    assert \"black\" in captured.err\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_shared_lib_as_app(pipx_temp_env, monkeypatch, capfd):\n    run_pipx_cli_exit([\"run\", \"pip\", \"--help\"])\n    captured = capfd.readouterr()\n    assert \"pip <command> [options]\" in captured.out\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_local_path_entry_point(pipx_temp_env, caplog, root):\n    empty_project_path = (Path(\"testdata\") / \"empty_project\").as_posix()\n    os.chdir(root)\n\n    caplog.set_level(logging.INFO)\n\n    run_pipx_cli_exit([\"run\", empty_project_path])\n\n    assert \"Using discovered entry point for 'pipx run'\" in caplog.text\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_with(capsys):\n    run_pipx_cli_exit([\"run\", \"--with\", \"black\", \"pycowsay\", \"--help\"])\n    captured = capsys.readouterr()\n    assert \"injected package black into venv pycowsay\" in captured.out\n\n\n@mock.patch(\"os.execvpe\", new=execvpe_mock)\ndef test_run_with_cache(capsys, caplog):\n    # Maybe there's a better way to remove the previous venv cache?\n    run_pipx_cli_exit([\"run\", \"--no-cache\", \"pycowsay\", \"cowsay\", \"args\"])\n    run_pipx_cli_exit([\"run\", \"pycowsay\", \"cowsay\", \"args\"], assert_exit=0)\n\n    caplog.set_level(logging.DEBUG)\n    caplog.clear()\n    run_pipx_cli_exit([\"run\", \"--verbose\", \"--with\", \"black\", \"pycowsay\", \"args\"], assert_exit=0)\n    captured = capsys.readouterr()\n    assert \"Reusing cached venv\" in caplog.text\n    assert \"injected package black into venv pycowsay\" in captured.out\n"
  },
  {
    "path": "tests/test_runpip.py",
    "content": "from helpers import run_pipx_cli, skip_if_windows\n\n\ndef test_runpip(pipx_temp_env, monkeypatch, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"runpip\", \"pycowsay\", \"list\"])\n\n\ndef test_runpip_splits_single_argument(pipx_temp_env, monkeypatch, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"runpip\", \"pycowsay\", \"list --format=freeze\"])\n\n\n@skip_if_windows\ndef test_runpip_global(pipx_temp_env, monkeypatch, capsys):\n    assert not run_pipx_cli([\"install\", \"--global\", \"pycowsay\"])\n    assert not run_pipx_cli([\"runpip\", \"--global\", \"pycowsay\", \"list\"])\n"
  },
  {
    "path": "tests/test_shared_libs.py",
    "content": "import os\nimport time\nfrom pathlib import Path\nfrom unittest.mock import patch\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom pipx import shared_libs\nfrom pipx.constants import WINDOWS\n\n\n@pytest.mark.parametrize(\n    \"mtime_minus_now,needs_upgrade\",\n    [\n        (-shared_libs.SHARED_LIBS_MAX_AGE_SEC - 5 * 60, True),\n        (-shared_libs.SHARED_LIBS_MAX_AGE_SEC + 5 * 60, False),\n    ],\n)\ndef test_auto_update_shared_libs(capsys, pipx_ultra_temp_env, mtime_minus_now, needs_upgrade):\n    now = time.time()\n    shared_libs.shared_libs.create(verbose=True, pip_args=[])\n    shared_libs.shared_libs.has_been_updated_this_run = False\n\n    access_time = now  # this can be anything\n    os.utime(shared_libs.shared_libs.pip_path, (access_time, mtime_minus_now + now))\n\n    assert shared_libs.shared_libs.needs_upgrade is needs_upgrade\n\n\n@pytest.mark.skipif(not WINDOWS, reason=\"Windows-specific test\")\ndef test_venv_python_is_valid_missing_interpreter(tmp_path: Path) -> None:\n    \"\"\"Test that _venv_python_is_valid returns False when the underlying Python is missing.\"\"\"\n    # Create a fake venv structure\n    venv_path = tmp_path / \"test_venv\"\n    scripts_path = venv_path / \"Scripts\"\n    scripts_path.mkdir(parents=True)\n    python_exe = scripts_path / \"python.exe\"\n    python_exe.touch()\n\n    # Create a pyvenv.cfg pointing to a non-existent Python\n    pyvenv_cfg = venv_path / \"pyvenv.cfg\"\n    pyvenv_cfg.write_text(\"home = C:\\\\NonExistent\\\\Python\\\\Path\\nversion = 3.14.0\\n\")\n\n    assert shared_libs._venv_python_is_valid(python_exe) is False\n\n\n@pytest.mark.skipif(not WINDOWS, reason=\"Windows-specific test\")\ndef test_venv_python_is_valid_existing_interpreter(tmp_path: Path) -> None:\n    \"\"\"Test that _venv_python_is_valid returns True when the underlying Python exists.\"\"\"\n    # Create a fake venv structure\n    venv_path = tmp_path / \"test_venv\"\n    scripts_path = venv_path / \"Scripts\"\n    scripts_path.mkdir(parents=True)\n    python_exe = scripts_path / \"python.exe\"\n    python_exe.touch()\n\n    # Create the \"original\" Python installation\n    original_python_dir = tmp_path / \"original_python\"\n    original_python_dir.mkdir()\n    original_python_exe = original_python_dir / \"python.exe\"\n    original_python_exe.touch()\n\n    # Create a pyvenv.cfg pointing to the existing Python\n    pyvenv_cfg = venv_path / \"pyvenv.cfg\"\n    pyvenv_cfg.write_text(f\"home = {original_python_dir}\\nversion = 3.12.0\\n\")\n\n    assert shared_libs._venv_python_is_valid(python_exe) is True\n\n\ndef test_venv_python_is_valid_non_windows() -> None:\n    \"\"\"Test that _venv_python_is_valid always returns True on non-Windows platforms.\"\"\"\n    with patch.object(shared_libs, \"WINDOWS\", False):\n        # Should return True regardless of the path\n        assert shared_libs._venv_python_is_valid(Path(\"/fake/path/python\")) is True\n"
  },
  {
    "path": "tests/test_standalone_interpreter.py",
    "content": "import json\nimport shutil\nimport sys\n\nfrom helpers import (\n    run_pipx_cli,\n)\nfrom package_info import PKG\nfrom pipx import standalone_python\n\nMAJOR_PYTHON_VERSION = sys.version_info.major\nMINOR_PYTHON_VERSION = sys.version_info.minor\nTARGET_PYTHON_VERSION = f\"{MAJOR_PYTHON_VERSION}.{MINOR_PYTHON_VERSION}\"\n\noriginal_which = shutil.which\n\n\ndef mock_which(name):\n    if name == TARGET_PYTHON_VERSION:\n        return None\n    return original_which(name)\n\n\ndef test_list_no_standalone_interpreters(pipx_temp_env, monkeypatch, capsys):\n    assert not run_pipx_cli([\"interpreter\", \"list\"])\n\n    captured = capsys.readouterr()\n    assert \"Standalone interpreters\" in captured.out\n    assert len(captured.out.splitlines()) == 1\n\n\ndef test_list_used_standalone_interpreters(pipx_temp_env, monkeypatch, mocked_github_api, capsys):\n    monkeypatch.setattr(shutil, \"which\", mock_which)\n\n    assert not run_pipx_cli(\n        [\n            \"install\",\n            \"--fetch-missing-python\",\n            \"--python\",\n            TARGET_PYTHON_VERSION,\n            PKG[\"pycowsay\"][\"spec\"],\n        ]\n    )\n\n    capsys.readouterr()\n    assert not run_pipx_cli([\"interpreter\", \"list\"])\n\n    captured = capsys.readouterr()\n    assert TARGET_PYTHON_VERSION in captured.out\n    assert \"pycowsay\" in captured.out\n\n\ndef test_list_unused_standalone_interpreters(pipx_temp_env, monkeypatch, mocked_github_api, capsys):\n    monkeypatch.setattr(shutil, \"which\", mock_which)\n\n    assert not run_pipx_cli(\n        [\n            \"install\",\n            \"--fetch-missing-python\",\n            \"--python\",\n            TARGET_PYTHON_VERSION,\n            PKG[\"pycowsay\"][\"spec\"],\n        ]\n    )\n\n    assert not run_pipx_cli([\"uninstall\", \"pycowsay\"])\n    capsys.readouterr()\n    assert not run_pipx_cli([\"interpreter\", \"list\"])\n\n    captured = capsys.readouterr()\n    assert TARGET_PYTHON_VERSION in captured.out\n    assert \"pycowsay\" not in captured.out\n    assert \"Unused\" in captured.out\n\n\ndef test_prune_unused_standalone_interpreters(pipx_temp_env, monkeypatch, mocked_github_api, capsys):\n    monkeypatch.setattr(shutil, \"which\", mock_which)\n\n    assert not run_pipx_cli(\n        [\n            \"install\",\n            \"--fetch-missing-python\",\n            \"--python\",\n            TARGET_PYTHON_VERSION,\n            PKG[\"pycowsay\"][\"spec\"],\n        ]\n    )\n\n    capsys.readouterr()\n    assert not run_pipx_cli([\"interpreter\", \"prune\"])\n    captured = capsys.readouterr()\n    assert \"Nothing to remove\" in captured.out\n\n    assert not run_pipx_cli([\"uninstall\", \"pycowsay\"])\n    capsys.readouterr()\n\n    assert not run_pipx_cli([\"interpreter\", \"prune\"])\n    captured = capsys.readouterr()\n    assert \"Successfully removed:\" in captured.out\n    assert f\"- Python {TARGET_PYTHON_VERSION}\" in captured.out\n\n    assert not run_pipx_cli([\"interpreter\", \"list\"])\n    captured = capsys.readouterr()\n    assert \"Standalone interpreters\" in captured.out\n    assert len(captured.out.splitlines()) == 1\n\n    assert not run_pipx_cli([\"interpreter\", \"prune\"])\n    captured = capsys.readouterr()\n    assert \"Nothing to remove\" in captured.out\n\n\ndef test_upgrade_standalone_interpreter(pipx_temp_env, root, monkeypatch, capsys):\n    monkeypatch.setattr(shutil, \"which\", mock_which)\n\n    with open(root / \"testdata\" / \"standalone_python_index_20250818.json\") as f:\n        new_index = json.load(f)\n    monkeypatch.setattr(standalone_python, \"get_or_update_index\", lambda _: new_index)\n\n    assert not run_pipx_cli(\n        [\n            \"install\",\n            \"--fetch-missing-python\",\n            \"--python\",\n            TARGET_PYTHON_VERSION,\n            PKG[\"pycowsay\"][\"spec\"],\n        ]\n    )\n\n    with open(root / \"testdata\" / \"standalone_python_index_20250828.json\") as f:\n        new_index = json.load(f)\n    monkeypatch.setattr(standalone_python, \"get_or_update_index\", lambda _: new_index)\n\n    assert not run_pipx_cli([\"interpreter\", \"upgrade\"])\n\n\ndef test_upgrade_standalone_interpreter_nothing_to_upgrade(pipx_temp_env, capsys, mocked_github_api):\n    assert not run_pipx_cli([\"interpreter\", \"upgrade\"])\n    captured = capsys.readouterr()\n    assert \"Nothing to upgrade\" in captured.out\n"
  },
  {
    "path": "tests/test_uninject.py",
    "content": "from helpers import run_pipx_cli, skip_if_windows\nfrom package_info import PKG\n\n\ndef test_uninject_simple(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"inject\", \"pycowsay\", PKG[\"black\"][\"spec\"]])\n    assert not run_pipx_cli([\"uninject\", \"pycowsay\", \"black\"])\n    captured = capsys.readouterr()\n    assert \"Uninjected package black\" in captured.out\n    assert not run_pipx_cli([\"list\", \"--include-injected\"])\n    captured = capsys.readouterr()\n    assert \"black\" not in captured.out\n\n\n@skip_if_windows\ndef test_uninject_simple_global(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"--global\", \"pycowsay\"])\n    assert not run_pipx_cli([\"inject\", \"--global\", \"pycowsay\", PKG[\"black\"][\"spec\"]])\n    assert not run_pipx_cli([\"uninject\", \"--global\", \"pycowsay\", \"black\"])\n    captured = capsys.readouterr()\n    assert \"Uninjected package black\" in captured.out\n    assert not run_pipx_cli([\"list\", \"--global\", \"--include-injected\"])\n    captured = capsys.readouterr()\n    assert \"black\" not in captured.out\n\n\ndef test_uninject_with_include_apps(pipx_temp_env, capsys, caplog):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"inject\", \"pycowsay\", PKG[\"black\"][\"spec\"], \"--include-deps\", \"--include-apps\"])\n    assert not run_pipx_cli([\"uninject\", \"pycowsay\", \"black\", \"--verbose\"])\n    assert \"removed file\" in caplog.text\n\n\ndef test_uninject_leave_deps(pipx_temp_env, capsys, caplog):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"inject\", \"pycowsay\", PKG[\"black\"][\"spec\"]])\n    assert not run_pipx_cli([\"uninject\", \"pycowsay\", \"black\", \"--leave-deps\", \"--verbose\"])\n    captured = capsys.readouterr()\n    assert \"Uninjected package black from venv pycowsay\" in captured.out\n    assert \"Dependencies of uninstalled package:\" not in caplog.text\n"
  },
  {
    "path": "tests/test_uninstall.py",
    "content": "import sys\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom helpers import (\n    PIPX_METADATA_LEGACY_VERSIONS,\n    app_name,\n    mock_legacy_venv,\n    remove_venv_interpreter,\n    run_pipx_cli,\n    skip_if_windows,\n)\nfrom package_info import PKG\nfrom pipx import paths\n\n\ndef file_or_symlink(filepath):\n    # Returns True for file or broken symlink or non-broken symlink\n    # Returns False for no file and no symlink\n\n    # filepath.exists() returns True for regular file or non-broken symlink\n    # filepath.exists() returns False for no regular file or broken symlink\n    # filepath.is_symlink() returns True for broken or non-broken symlink\n    return filepath.exists() or filepath.is_symlink()\n\n\ndef test_uninstall(pipx_temp_env):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"uninstall\", \"pycowsay\"])\n\n\n@skip_if_windows\ndef test_uninstall_global(pipx_temp_env):\n    assert not run_pipx_cli([\"install\", \"--global\", \"pycowsay\"])\n    assert not run_pipx_cli([\"uninstall\", \"--global\", \"pycowsay\"])\n\n\n# TODO: We can add this test back once a suitable substitute for cloudtoken is found\n# def test_uninstall_circular_deps(pipx_temp_env):\n#     assert not run_pipx_cli([\"install\", PKG[\"cloudtoken\"][\"spec\"]])\n#     assert not run_pipx_cli([\"uninstall\", \"cloudtoken\"])\n\n\n@pytest.mark.parametrize(\"metadata_version\", PIPX_METADATA_LEGACY_VERSIONS)\ndef test_uninstall_legacy_venv(pipx_temp_env, metadata_version):\n    executable_path = paths.ctx.bin_dir / app_name(\"pycowsay\")\n\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert executable_path.exists()\n\n    mock_legacy_venv(\"pycowsay\", metadata_version=metadata_version)\n    assert not run_pipx_cli([\"uninstall\", \"pycowsay\"])\n    assert not file_or_symlink(executable_path)\n\n\ndef test_uninstall_suffix(pipx_temp_env):\n    name = \"pbr\"\n    suffix = \"_a\"\n    executable_path = paths.ctx.bin_dir / app_name(f\"{name}{suffix}\")\n\n    assert not run_pipx_cli([\"install\", PKG[name][\"spec\"], f\"--suffix={suffix}\"])\n    assert executable_path.exists()\n\n    assert not run_pipx_cli([\"uninstall\", f\"{name}{suffix}\"])\n    assert not file_or_symlink(executable_path)\n\n\ndef test_uninstall_man_page(pipx_temp_env):\n    man_page_path = paths.ctx.man_dir / \"man6\" / \"pycowsay.6\"\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert man_page_path.exists()\n    assert not run_pipx_cli([\"uninstall\", \"pycowsay\"])\n    assert not file_or_symlink(man_page_path)\n\n\ndef test_uninstall_injected(pipx_temp_env):\n    pycowsay_app_paths = [paths.ctx.bin_dir / app for app in PKG[\"pycowsay\"][\"apps\"]]\n    pycowsay_man_page_paths = [paths.ctx.man_dir / man_page for man_page in PKG[\"pycowsay\"][\"man_pages\"]]\n    pylint_app_paths = [paths.ctx.bin_dir / app for app in PKG[\"pylint\"][\"apps\"]]\n    app_paths = pycowsay_app_paths + pylint_app_paths\n    man_page_paths = pycowsay_man_page_paths\n\n    assert not run_pipx_cli([\"install\", PKG[\"pycowsay\"][\"spec\"]])\n    assert not run_pipx_cli([\"inject\", \"--include-apps\", \"pycowsay\", PKG[\"pylint\"][\"spec\"]])\n\n    for app_path in app_paths:\n        assert app_path.exists()\n\n    for man_page_path in man_page_paths:\n        assert man_page_path.exists()\n\n    assert not run_pipx_cli([\"uninstall\", \"pycowsay\"])\n\n    for app_path in app_paths:\n        assert not file_or_symlink(app_path)\n\n    for man_page_path in man_page_paths:\n        assert not file_or_symlink(man_page_path)\n\n\n@pytest.mark.parametrize(\"metadata_version\", [\"0.1\"])\ndef test_uninstall_suffix_legacy_venv(pipx_temp_env, metadata_version):\n    name = \"pbr\"\n    # legacy uninstall on Windows only works with \"canonical name characters\"\n    #   in suffix\n    suffix = \"-a\"\n    executable_path = paths.ctx.bin_dir / app_name(f\"{name}{suffix}\")\n\n    assert not run_pipx_cli([\"install\", PKG[name][\"spec\"], f\"--suffix={suffix}\"])\n    mock_legacy_venv(f\"{name}{suffix}\", metadata_version=metadata_version)\n    assert executable_path.exists()\n\n    assert not run_pipx_cli([\"uninstall\", f\"{name}{suffix}\"])\n    assert not file_or_symlink(executable_path)\n\n\n@pytest.mark.parametrize(\"metadata_version\", PIPX_METADATA_LEGACY_VERSIONS)\ndef test_uninstall_with_missing_interpreter(pipx_temp_env, metadata_version):\n    executable_path = paths.ctx.bin_dir / app_name(\"pycowsay\")\n\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert executable_path.exists()\n\n    mock_legacy_venv(\"pycowsay\", metadata_version=metadata_version)\n    remove_venv_interpreter(\"pycowsay\")\n\n    assert not run_pipx_cli([\"uninstall\", \"pycowsay\"])\n    # On Windows we cannot remove app binaries if no metadata and no python\n    if not (sys.platform.startswith(\"win\") and metadata_version is None):\n        assert not file_or_symlink(executable_path)\n\n\n@pytest.mark.parametrize(\"metadata_version\", PIPX_METADATA_LEGACY_VERSIONS)\ndef test_uninstall_proper_dep_behavior(pipx_temp_env, metadata_version):\n    # isort is a dependency of pylint.  Make sure that uninstalling pylint\n    #   does not also uninstall isort app in LOCAL_BIN_DIR\n    isort_app_paths = [paths.ctx.bin_dir / app for app in PKG[\"isort\"][\"apps\"]]\n    pylint_app_paths = [paths.ctx.bin_dir / app for app in PKG[\"pylint\"][\"apps\"]]\n\n    assert not run_pipx_cli([\"install\", PKG[\"pylint\"][\"spec\"]])\n    assert not run_pipx_cli([\"install\", PKG[\"isort\"][\"spec\"]])\n    mock_legacy_venv(\"pylint\", metadata_version=metadata_version)\n    mock_legacy_venv(\"isort\", metadata_version=metadata_version)\n    for pylint_app_path in pylint_app_paths:\n        assert pylint_app_path.exists()\n    for isort_app_path in isort_app_paths:\n        assert isort_app_path.exists()\n\n    assert not run_pipx_cli([\"uninstall\", \"pylint\"])\n\n    for pylint_app_path in pylint_app_paths:\n        assert not file_or_symlink(pylint_app_path)\n    # THIS is what we're making sure is true:\n    for isort_app_path in isort_app_paths:\n        assert isort_app_path.exists()\n\n\ndef test_uninstall_rejects_existing_directory_name(pipx_temp_env, tmp_path, monkeypatch):\n    local_dir = tmp_path / \"myproject\"\n    local_dir.mkdir()\n    (local_dir / \"pyproject.toml\").write_text(\n        '[project]\\nname = \"myproject\"\\nversion = \"0.1.0\"\\n[project.scripts]\\nmycmd = \"myproject:main\"\\n'\n    )\n    (local_dir / \"myproject.py\").write_text(\"def main(): pass\")\n\n    assert not run_pipx_cli([\"install\", \"--editable\", str(local_dir)])\n\n    monkeypatch.chdir(tmp_path)\n    result = run_pipx_cli([\"uninstall\", \"myproject\"])\n    assert result != 0, \"uninstall should reject directory name when cwd contains that directory\"\n    assert local_dir.exists(), \"source directory should not be deleted\"\n\n\n@pytest.mark.parametrize(\"metadata_version\", PIPX_METADATA_LEGACY_VERSIONS)\ndef test_uninstall_proper_dep_behavior_missing_interpreter(pipx_temp_env, metadata_version):\n    # isort is a dependency of pylint.  Make sure that uninstalling pylint\n    #   does not also uninstall isort app in LOCAL_BIN_DIR\n    isort_app_paths = [paths.ctx.bin_dir / app for app in PKG[\"isort\"][\"apps\"]]\n    pylint_app_paths = [paths.ctx.bin_dir / app for app in PKG[\"pylint\"][\"apps\"]]\n\n    assert not run_pipx_cli([\"install\", PKG[\"pylint\"][\"spec\"]])\n    assert not run_pipx_cli([\"install\", PKG[\"isort\"][\"spec\"]])\n    mock_legacy_venv(\"pylint\", metadata_version=metadata_version)\n    mock_legacy_venv(\"isort\", metadata_version=metadata_version)\n    remove_venv_interpreter(\"pylint\")\n    remove_venv_interpreter(\"isort\")\n    for pylint_app_path in pylint_app_paths:\n        assert pylint_app_path.exists()\n    for isort_app_path in isort_app_paths:\n        assert isort_app_path.exists()\n\n    assert not run_pipx_cli([\"uninstall\", \"pylint\"])\n\n    # Do not check the following on Windows without metadata, we do not\n    #   remove bin dir links by design for missing interpreter in that case\n    if not (sys.platform.startswith(\"win\") and metadata_version is None):\n        for pylint_app_path in pylint_app_paths:\n            assert not file_or_symlink(pylint_app_path)\n    # THIS is what we're making sure is true:\n    for isort_app_path in isort_app_paths:\n        assert isort_app_path.exists()\n"
  },
  {
    "path": "tests/test_uninstall_all.py",
    "content": "import pytest  # type: ignore[import-not-found]\n\nfrom helpers import PIPX_METADATA_LEGACY_VERSIONS, mock_legacy_venv, run_pipx_cli\n\n\ndef test_uninstall_all(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"uninstall-all\"])\n\n\n@pytest.mark.parametrize(\"metadata_version\", PIPX_METADATA_LEGACY_VERSIONS)\ndef test_uninstall_all_legacy_venv(pipx_temp_env, capsys, metadata_version):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    mock_legacy_venv(\"pycowsay\", metadata_version=metadata_version)\n    assert not run_pipx_cli([\"uninstall-all\"])\n"
  },
  {
    "path": "tests/test_unpin.py",
    "content": "from helpers import run_pipx_cli\nfrom package_info import PKG\n\n\ndef test_unpin(capsys, pipx_temp_env, caplog):\n    assert not run_pipx_cli([\"install\", PKG[\"nox\"][\"spec\"]])\n    assert not run_pipx_cli([\"pin\", \"nox\"])\n\n    assert not run_pipx_cli([\"unpin\", \"nox\"])\n    assert not run_pipx_cli([\"upgrade\", \"nox\"])\n\n    captured = capsys.readouterr()\n    assert \"nox is already at latest version\" in captured.out\n\n\ndef test_unpin_with_suffix(capsys, pipx_temp_env):\n    assert not run_pipx_cli([\"install\", PKG[\"black\"][\"spec\"], \"--suffix\", \"@1\"])\n    assert not run_pipx_cli([\"pin\", \"black@1\"])\n    assert not run_pipx_cli([\"unpin\", \"black@1\"])\n\n    captured = capsys.readouterr()\n    assert \"Unpinned 1 packages in venv black@1\" in captured.out\n\n    assert not run_pipx_cli([\"upgrade\", \"black@1\"])\n\n    captured = capsys.readouterr()\n    assert \"upgraded package black@1 from 22.8.0 to 22.10.0\" in captured.out\n\n\ndef test_unpin_warning(capsys, pipx_temp_env, caplog):\n    assert not run_pipx_cli([\"install\", PKG[\"nox\"][\"spec\"]])\n    assert not run_pipx_cli([\"pin\", \"nox\"])\n    assert not run_pipx_cli([\"unpin\", \"nox\"])\n    assert not run_pipx_cli([\"unpin\", \"nox\"])\n\n    assert \"No packages to unpin in venv nox\" in caplog.text\n\n\ndef test_unpin_not_installed_package(capsys, pipx_temp_env):\n    assert run_pipx_cli([\"unpin\", \"abc\"])\n\n    captured = capsys.readouterr()\n    assert \"Package abc is not installed\" in captured.err\n\n\ndef test_unpin_injected_packages(capsys, pipx_temp_env):\n    assert not run_pipx_cli([\"install\", \"black\"])\n    assert not run_pipx_cli([\"inject\", \"black\", \"nox\", \"pylint\"])\n    assert not run_pipx_cli([\"pin\", \"black\"])\n    assert not run_pipx_cli([\"unpin\", \"black\"])\n\n    captured = capsys.readouterr()\n    assert \"Unpinned 3 packages in venv black\" in captured.out\n"
  },
  {
    "path": "tests/test_upgrade.py",
    "content": "import pytest  # type: ignore[import-not-found]\n\nfrom helpers import (\n    PIPX_METADATA_LEGACY_VERSIONS,\n    mock_legacy_venv,\n    remove_venv_interpreter,\n    run_pipx_cli,\n    skip_if_windows,\n)\nfrom package_info import PKG\n\n\ndef test_upgrade(pipx_temp_env, capsys):\n    assert run_pipx_cli([\"upgrade\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"Package is not installed\" in captured.err\n\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"installed package pycowsay\" in captured.out\n\n    assert not run_pipx_cli([\"upgrade\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"pycowsay is already at latest version\" in captured.out\n\n\n@skip_if_windows\ndef test_upgrade_global(pipx_temp_env, capsys):\n    assert run_pipx_cli([\"upgrade\", \"--global\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"Package is not installed\" in captured.err\n\n    assert not run_pipx_cli([\"install\", \"--global\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"installed package pycowsay\" in captured.out\n\n    assert not run_pipx_cli([\"upgrade\", \"--global\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"pycowsay is already at latest version\" in captured.out\n\n\n@pytest.mark.parametrize(\"metadata_version\", PIPX_METADATA_LEGACY_VERSIONS)\ndef test_upgrade_legacy_venv(pipx_temp_env, capsys, metadata_version):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    mock_legacy_venv(\"pycowsay\", metadata_version=metadata_version)\n    captured = capsys.readouterr()\n    if metadata_version is None:\n        assert run_pipx_cli([\"upgrade\", \"pycowsay\"])\n        captured = capsys.readouterr()\n        assert \"Not upgrading pycowsay. It has missing internal pipx metadata.\" in captured.err\n    else:\n        assert not run_pipx_cli([\"upgrade\", \"pycowsay\"])\n        captured = capsys.readouterr()\n\n\ndef test_upgrade_suffix(pipx_temp_env, capsys):\n    name = \"pycowsay\"\n    suffix = \"_a\"\n\n    assert not run_pipx_cli([\"install\", name, f\"--suffix={suffix}\"])\n    assert run_pipx_cli([\"upgrade\", f\"{name}\"])\n    assert not run_pipx_cli([\"upgrade\", f\"{name}{suffix}\"])\n\n\n@pytest.mark.parametrize(\"metadata_version\", [\"0.1\"])\ndef test_upgrade_suffix_legacy_venv(pipx_temp_env, capsys, metadata_version):\n    name = \"pycowsay\"\n    suffix = \"_a\"\n\n    assert not run_pipx_cli([\"install\", name, f\"--suffix={suffix}\"])\n    mock_legacy_venv(f\"{name}{suffix}\", metadata_version=metadata_version)\n    assert run_pipx_cli([\"upgrade\", f\"{name}\"])\n    assert not run_pipx_cli([\"upgrade\", f\"{name}{suffix}\"])\n\n\ndef test_upgrade_specifier(pipx_temp_env, capsys):\n    name = \"pylint\"\n    pkg_spec = PKG[name][\"spec\"]\n    initial_version = pkg_spec.split(\"==\")[-1]\n\n    assert not run_pipx_cli([\"install\", f\"{pkg_spec}\"])\n    assert not run_pipx_cli([\"upgrade\", f\"{name}\"])\n    captured = capsys.readouterr()\n    assert f\"upgraded package {name} from {initial_version} to\" in captured.out\n\n\ndef test_upgrade_missing_interpreter(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    remove_venv_interpreter(\"pycowsay\")\n\n    result = run_pipx_cli([\"upgrade\", \"pycowsay\"])\n    assert result != 0, \"upgrade should fail when Python interpreter is missing\"\n    captured = capsys.readouterr()\n    assert \"invalid python interpreter\" in captured.err\n    assert \"pipx reinstall-all\" in captured.err\n\n\ndef test_upgrade_editable(pipx_temp_env, capsys, root):\n    empty_project_path_as_string = (root / \"testdata\" / \"empty_project\").as_posix()\n    assert not run_pipx_cli([\"install\", \"--editable\", empty_project_path_as_string, \"--force\"])\n    assert not run_pipx_cli([\"upgrade\", \"--editable\", \"empty_project\"])\n    captured = capsys.readouterr()\n    assert \"empty-project is already at latest version\" in captured.out\n\n\ndef test_upgrade_include_injected(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", PKG[\"pylint\"][\"spec\"]])\n    assert not run_pipx_cli([\"inject\", \"pylint\", PKG[\"black\"][\"spec\"]])\n    captured = capsys.readouterr()\n    assert not run_pipx_cli([\"upgrade\", \"--include-injected\", \"pylint\"])\n    captured = capsys.readouterr()\n    assert \"upgraded package pylint\" in captured.out\n    assert \"upgraded package black\" in captured.out\n\n\ndef test_upgrade_no_include_injected(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", PKG[\"pylint\"][\"spec\"]])\n    assert not run_pipx_cli([\"inject\", \"pylint\", PKG[\"black\"][\"spec\"]])\n    captured = capsys.readouterr()\n    assert not run_pipx_cli([\"upgrade\", \"pylint\"])\n    captured = capsys.readouterr()\n    assert \"upgraded package pylint\" in captured.out\n    assert \"upgraded package black\" not in captured.out\n\n\ndef test_upgrade_install_missing(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"upgrade\", \"pycowsay\", \"--install\"])\n    captured = capsys.readouterr()\n    assert \"installed package pycowsay\" in captured.out\n\n\ndef test_upgrade_multiple(pipx_temp_env, capsys):\n    name = \"pylint\"\n    pkg_spec = PKG[name][\"spec\"]\n    initial_version = pkg_spec.split(\"==\")[-1]\n    assert not run_pipx_cli([\"install\", pkg_spec])\n\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n\n    assert not run_pipx_cli([\"upgrade\", name, \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert f\"upgraded package {name} from {initial_version} to\" in captured.out\n    assert \"pycowsay is already at latest version\" in captured.out\n\n\ndef test_upgrade_absolute_path(pipx_temp_env, capsys, root):\n    assert run_pipx_cli([\"upgrade\", \"--verbose\", str((root / \"testdata\" / \"empty_project\").resolve())])\n    captured = capsys.readouterr()\n    assert \"Package cannot be a URL\" not in captured.err\n\n\ndef test_upgrade_with_extras(pipx_temp_env, capsys):\n    \"\"\"Test that upgrading a package with extras in the name works correctly.\n\n    Regression test for https://github.com/pypa/pipx/issues/925\n    \"\"\"\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    captured = capsys.readouterr()\n    assert \"installed package pycowsay\" in captured.out\n\n    assert not run_pipx_cli([\"upgrade\", \"pycowsay[test_extra]\"])\n    captured = capsys.readouterr()\n    assert \"pycowsay is already at latest version\" in captured.out\n    assert \"Package is not installed\" not in captured.err\n"
  },
  {
    "path": "tests/test_upgrade_all.py",
    "content": "import pytest  # type: ignore[import-not-found]\n\nfrom helpers import PIPX_METADATA_LEGACY_VERSIONS, mock_legacy_venv, run_pipx_cli\n\n\ndef test_upgrade_all(pipx_temp_env, capsys):\n    assert run_pipx_cli([\"upgrade\", \"pycowsay\"])\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"upgrade-all\"])\n\n\ndef test_upgrade_all_none(pipx_temp_env, capsys):\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    assert not run_pipx_cli([\"upgrade-all\"])\n    captured = capsys.readouterr()\n    assert \"No packages upgraded after running 'pipx upgrade-all'\" in captured.out\n\n\n@pytest.mark.parametrize(\"metadata_version\", PIPX_METADATA_LEGACY_VERSIONS)\ndef test_upgrade_all_legacy_venv(pipx_temp_env, capsys, caplog, metadata_version):\n    assert run_pipx_cli([\"upgrade\", \"pycowsay\"])\n    assert not run_pipx_cli([\"install\", \"pycowsay\"])\n    mock_legacy_venv(\"pycowsay\", metadata_version=metadata_version)\n    if metadata_version is None:\n        capsys.readouterr()\n        assert run_pipx_cli([\"upgrade-all\"])\n        assert \"The following package(s) failed to upgrade: pycowsay\" in caplog.text\n    else:\n        assert not run_pipx_cli([\"upgrade-all\"])\n"
  },
  {
    "path": "tests/test_upgrade_shared.py",
    "content": "import subprocess\n\nimport pytest  # type: ignore[import-not-found]\n\nfrom helpers import run_pipx_cli\n\n\n@pytest.fixture\ndef shared_libs(pipx_ultra_temp_env):\n    # local import to get the shared_libs object patched by fixtures\n    from pipx.shared_libs import shared_libs as _shared_libs  # noqa: PLC0415\n\n    yield _shared_libs\n\n\ndef test_upgrade_shared(shared_libs, capsys, caplog):\n    assert shared_libs.has_been_updated_this_run is False\n    assert shared_libs.is_valid is False\n    assert run_pipx_cli([\"upgrade-shared\", \"-v\"]) == 0\n    captured = capsys.readouterr()\n    assert \"creating shared libraries\" in captured.err\n    assert \"upgrading shared libraries\" in captured.err\n    assert \"Upgrading shared libraries in\" in caplog.text\n    assert \"Already upgraded libraries in\" not in caplog.text\n    assert shared_libs.has_been_updated_this_run is True\n    assert shared_libs.is_valid is True  # type: ignore[unreachable]\n    shared_libs.has_been_updated_this_run = False\n    assert run_pipx_cli([\"upgrade-shared\", \"-v\"]) == 0\n    captured = capsys.readouterr()\n    assert \"creating shared libraries\" not in captured.err\n    assert \"upgrading shared libraries\" in captured.err\n    assert \"Upgrading shared libraries in\" in caplog.text\n    assert \"Already upgraded libraries in\" not in caplog.text\n    assert shared_libs.has_been_updated_this_run is True\n    assert run_pipx_cli([\"upgrade-shared\", \"-v\"]) == 0\n    assert \"Already upgraded libraries in\" in caplog.text\n\n\ndef test_upgrade_shared_pip_args(shared_libs, capsys, caplog):\n    assert shared_libs.has_been_updated_this_run is False\n    assert shared_libs.is_valid is False\n    assert run_pipx_cli([\"upgrade-shared\", \"-v\", \"--pip-args='--no-index'\"]) == 1\n    captured = capsys.readouterr()\n    assert \"creating shared libraries\" in captured.err\n    assert \"upgrading shared libraries\" in captured.err\n    assert \"Upgrading shared libraries in\" in caplog.text\n    assert \"Already upgraded libraries in\" not in caplog.text\n    assert shared_libs.has_been_updated_this_run is False\n    assert shared_libs.is_valid is True\n\n\ndef test_upgrade_shared_pin_pip(shared_libs):\n    def pip_version():\n        cmd = \"from importlib.metadata import version; print(version('pip'))\"\n        ret = subprocess.run([shared_libs.python_path, \"-c\", cmd], check=True, capture_output=True, text=True)\n        return ret.stdout.strip()\n\n    assert shared_libs.has_been_updated_this_run is False\n    assert shared_libs.is_valid is False\n    assert run_pipx_cli([\"upgrade-shared\", \"-v\", \"--pip-args=pip==24.0\"]) == 0\n    assert shared_libs.is_valid is True\n    assert pip_version() == \"24.0\"  # type: ignore[unreachable]\n    shared_libs.has_been_updated_this_run = False  # reset for next run\n    assert run_pipx_cli([\"upgrade-shared\", \"-v\", \"--pip-args=pip==23.3.2\"]) == 0\n    assert shared_libs.is_valid is True\n    assert pip_version() == \"23.3.2\"\n"
  },
  {
    "path": "tox.toml",
    "content": "requires = [\"tox>=4.45\"]\n\nenv_list = [\n  \"3.13\",\n  \"3.12\",\n  \"3.11\",\n  \"3.10\",\n  \"3.9\",\n  \"lint\",\n  \"docs\",\n  \"man\",\n]\n\nskip_missing_interpreters = true\n\n[env_run_base]\ndescription = \"run tests with {env_name}\"\npackage = \"wheel\"\ndependency_groups = [\"test\"]\nset_env.COVERAGE_FILE = \"{work_dir}{/}.coverage.{env_name}\"\ncommands = [\n  [\n    \"pytest\",\n    {replace = \"posargs\", default = [\n      \"--cov=pipx\",\n      \"--cov-report=term-missing:skip-covered\",\n      \"--cov-report=xml:{work_dir}{/}coverage.{env_name}.xml\",\n      \"-n\", \"auto\",\n      \"--dist\", \"loadfile\",\n      \"tests\",\n    ], extend = true},\n  ],\n]\nuv_seed = true\n\n[env.dev]\ndescription = \"generate a DEV environment\"\npackage = \"editable\"\ndependency_groups = [\"dev\"]\ncommands = [\n  [\"uv\", \"pip\", \"tree\"],\n  [\"python\", \"-c\", \"import sys; print(sys.executable)\"],\n]\n\n[env.lint]\ndescription = \"run pre-commit on the codebase\"\nskip_install = true\ndependency_groups = [\"lint\"]\ncommands = [[\"pre-commit\", \"run\", \"--all-files\"]]\n\n[env.docs]\ndescription = \"build documentation\"\ndependency_groups = [\"docs\"]\nset_env.NO_MKDOCS_2_WARNING = \"true\"\ncommands = [\n  [\"mkdocs\", \"build\", \"--strict\", \"--site-dir\", \"{env:READTHEDOCS_OUTPUT:site}{/}html\"],\n  [\"python\", \"-c\", \"print('docs built to: file://{tox_root}{/}{env:READTHEDOCS_OUTPUT:site}{/}html{/}index.html')\"],\n]\n\n[env.docs-live]\ndescription = \"serve documentation with live reload\"\ndependency_groups = [\"docs\"]\nset_env.NO_MKDOCS_2_WARNING = \"true\"\ncommands = [\n  [\"mkdocs\", \"serve\", \"--dirty\", \"--open\"],\n]\n\n[env.man]\ndescription = \"build man page\"\ndependency_groups = [\"man\"]\ncommands = [\n  [\"python\", \"scripts/generate_man.py\"],\n]\n\n[env.zipapp]\ndescription = \"build zipapp via shiv\"\nskip_install = true\ndependency_groups = [\"zipapp\"]\nallowlist_externals = [\"{tox_root}{/}pipx.pyz\"]\ncommands = [\n  [\"shiv\", \"-c\", \"pipx\", \"-o\", \"{tox_root}{/}pipx.pyz\", \".\"],\n  [\"{tox_root}{/}pipx.pyz\", \"--version\"],\n]\n"
  }
]