pax_global_header00006660000000000000000000000064152170662300014514gustar00rootroot0000000000000052 comment=9ba7965d4cb964ec58d3231cde437083f9e8cb8b pipx-1.15.0/000077500000000000000000000000001521706623000125605ustar00rootroot00000000000000pipx-1.15.0/.deepsource.toml000066400000000000000000000002701521706623000156700ustar00rootroot00000000000000version = 1 test_patterns = ["tests/**"] [[analyzers]] name = "python" enabled = true [analyzers.meta] runtime_version = "3.x.x" [[transformers]] name = "black" enabled = true pipx-1.15.0/.github/000077500000000000000000000000001521706623000141205ustar00rootroot00000000000000pipx-1.15.0/.github/CONTRIBUTING.md000066400000000000000000000010051521706623000163450ustar00rootroot00000000000000# Contributing to `pipx` Thank you for your interest in contributing to pipx! There are many ways to contribute, and we appreciate all of them. As a reminder, all contributors are expected to follow the [PSF Code of Conduct][coc]. ## Development Documentation Our [development documentation](https://pipx.pypa.io/latest/contributing/) contains details on how to get started with contributing to `pipx`, and details of our development processes. [coc]: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md pipx-1.15.0/.github/FUNDING.yaml000066400000000000000000000000261521706623000160740ustar00rootroot00000000000000tidelift: "pypi/pipx" pipx-1.15.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001521706623000163035ustar00rootroot00000000000000pipx-1.15.0/.github/ISSUE_TEMPLATE/bug.md000066400000000000000000000005611521706623000174040ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **Issue** Describe what's the expected behavior and what you're observing. **Environment** Provide at least: - OS: - Shell: - Python version and path: - `pipx --version` output: **Output of the failing command** Make sure to run the command with `--verbose`: pipx-1.15.0/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000012611521706623000202730ustar00rootroot00000000000000# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser blank_issues_enabled: true # default contact_links: - name: "๐Ÿ’ฌ pypa/pipx @ Discord" url: https://discord.gg/pypa about: Chat with the devs - name: ๐Ÿคท๐Ÿ’ป๐Ÿคฆ Discourse url: https://discuss.python.org/c/packaging about: | Please ask typical Q&A here: general ideas for Python packaging, questions about structuring projects and so on - name: ๐Ÿ“ PSF Code of Conduct url: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md about: โค Be nice to other members of the community. โ˜ฎ Behave. pipx-1.15.0/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000013501521706623000220270ustar00rootroot00000000000000--- name: Feature request about: Suggest an enhancement for this project title: '' labels: enhancement assignees: '' --- **What's the problem this feature will solve?** **Describe the solution you'd like** **Alternative Solutions** **Additional context** pipx-1.15.0/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000007561521706623000177310ustar00rootroot00000000000000### Thanks for contributing, make sure you address all the checklists (for details on how see [development documentation](https://pipx.pypa.io/latest/contributing/)) - [ ] ran the linter to address style issues (`pre-commit run --all-files`) - [ ] wrote descriptive pull request text - [ ] ensured there are test(s) validating the fix - [ ] added news fragment in `changelog.d/` folder (choose a type: `feature`, `bugfix`, `doc`, `removal`, or `misc`) - [ ] updated/extended the documentation pipx-1.15.0/.github/SECURITY.md000066400000000000000000000005551521706623000157160ustar00rootroot00000000000000# Security Policy ## Supported Versions | Version | Supported | | ------- | ------------------ | | 1.7.0+ | :white_check_mark: | | < 1.7.0 | :x: | ## Reporting a Vulnerability To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. pipx-1.15.0/.github/config.yml000066400000000000000000000000251521706623000161050ustar00rootroot00000000000000rtd: project: pipx pipx-1.15.0/.github/dependabot.yaml000066400000000000000000000003231521706623000171070ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: github-actions directory: "/" schedule: interval: weekly groups: actions: patterns: - "*" cooldown: default-days: 7 pipx-1.15.0/.github/release.yml000066400000000000000000000001621521706623000162620ustar00rootroot00000000000000changelog: exclude: authors: - dependabot[bot] - pre-commit-ci[bot] - github-actions[bot] pipx-1.15.0/.github/workflows/000077500000000000000000000000001521706623000161555ustar00rootroot00000000000000pipx-1.15.0/.github/workflows/create_tests_package_lists.yml000066400000000000000000000025571521706623000242670ustar00rootroot00000000000000name: Create tests package lists for offline tests on: workflow_dispatch: concurrency: group: create-tests-package-lists-${{ github.ref }} cancel-in-progress: true permissions: contents: read jobs: create_package_lists: name: Create package lists runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest] python-version: ["3.13", "3.12", "3.11", "3.10"] include: - os: macos-latest python-version: "3.13" - os: windows-latest python-version: "3.13" steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python-version }} cache: "pip" - name: Install nox run: python -m pip install nox - name: Create lists run: nox --non-interactive --session create_test_package_list-${{ matrix.python-version }} -- ./new_tests_packages - name: Store reports as artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: lists-${{ matrix.os }}-${{ matrix.python-version }} path: ./new_tests_packages pipx-1.15.0/.github/workflows/exhaustive_package_test.yml000066400000000000000000000051141521706623000236000ustar00rootroot00000000000000name: Exhaustive Package Test (slow) on: workflow_dispatch: concurrency: group: exhaustive-package-test-${{ github.ref }} cancel-in-progress: true permissions: contents: read jobs: test_all_packages: name: Exhaustive Package Test continue-on-error: true runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] python-version: ["3.12", "3.11", "3.10"] steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python-version }} cache: "pip" - name: Install nox run: python -m pip install nox - name: Execute Tests continue-on-error: true run: nox --non-interactive --session test_all_packages-${{ matrix.python-version }} - name: Store reports as artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: reports-raw-${{ matrix.os }}-${{ matrix.python-version }} path: reports report_all_packages: name: Collate test reports needs: test_all_packages runs-on: ubuntu-latest steps: - name: Get report artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: reports-raw-* merge-multiple: true - name: Collate reports run: | ls # DEBUG mkdir reports cat all_packages_report_legend.txt > all_nodeps_reports_lf.txt cat all_packages_nodeps_report_* >> all_nodeps_reports_lf.txt tr -d '\r' < all_nodeps_reports_lf.txt > reports/all_nodeps_reports.txt cat all_packages_nodeps_errors_* > all_nodeps_errors_lf.txt tr -d '\r' < all_nodeps_errors_lf.txt > reports/all_nodeps_errors.txt cat all_packages_report_legend.txt > all_deps_reports_lf.txt cat all_packages_deps_report_* >> all_deps_reports_lf.txt tr -d '\r' < all_deps_reports_lf.txt > reports/all_deps_reports.txt cat all_packages_deps_errors_* > all_deps_errors_lf.txt tr -d '\r' < all_deps_errors_lf.txt > reports/all_deps_errors.txt - name: Store collated and raw reports as artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: reports-final path: reports pipx-1.15.0/.github/workflows/pre-release.yml000066400000000000000000000025751521706623000211150ustar00rootroot00000000000000name: Pre-release on: workflow_dispatch: inputs: bump: description: "Version bump type" required: true type: choice options: - auto - major - minor - patch default: auto env: default-python: "3.13" jobs: pre-release: runs-on: ubuntu-latest environment: release permissions: contents: write steps: - name: Checkout code uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 token: ${{ secrets.GH_RELEASE_TOKEN }} persist-credentials: false - name: Set up Python ${{ env.default-python }} uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.default-python }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install GitPython packaging towncrier pre-commit - name: Configure git identity run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - name: Generate changelog, commit, tag, and push run: python scripts/release.py --version "${{ inputs.bump }}" env: GH_RELEASE_TOKEN: ${{ secrets.GH_RELEASE_TOKEN }} pipx-1.15.0/.github/workflows/release.yml000066400000000000000000000056461521706623000203330ustar00rootroot00000000000000name: Release on: push: tags: - "*.*.*" concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: default-python: "3.14" jobs: build-package: name: Build pipx package runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout ${{ github.ref_name }} uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: "${{ github.ref_name }}" persist-credentials: false - name: Set up Python ${{ env.default-python }} uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.default-python }} - name: Build sdist and wheel run: | pip install build python -m build - name: Upload distributions uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: dist path: dist/ pypi-publish: name: Publish pipx to PyPI needs: build-package runs-on: ubuntu-latest environment: name: release url: https://pypi.org/p/pipx permissions: id-token: write steps: - name: Download distributions uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: dist path: dist/ - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 create-release: name: Create a release on GitHub's UI needs: pypi-publish runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Create release run: | gh release create --repo pypa/pipx "${RELEASE_TAG}" --generate-notes env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_TAG: ${{ github.ref_name }} upload-zipapp: name: Upload zipapp to GitHub Release needs: create-release runs-on: ubuntu-latest permissions: contents: write steps: - name: Checkout ${{ github.ref_name }} uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: "${{ github.ref_name }}" persist-credentials: false - name: Set up Python ${{ env.default-python }} uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.default-python }} - name: Install tox run: pip install tox - name: Build zipapp run: tox run -e zipapp - name: Upload to release run: | gh release upload --repo pypa/pipx "${RELEASE_TAG}" pipx.pyz env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_TAG: ${{ github.ref_name }} pipx-1.15.0/.github/workflows/tests.yml000066400000000000000000000066051521706623000200510ustar00rootroot00000000000000name: ๐Ÿงช tests on: workflow_dispatch: push: branches: - "main" pull_request: schedule: - cron: "0 8 * * *" concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: contents: read jobs: test: name: ๐Ÿงช test ${{ matrix.py }} - ${{ matrix.os }} runs-on: ${{ matrix.os }} timeout-minutes: 40 strategy: fail-fast: false matrix: os: [ubuntu-24.04] py: ["3.13", "3.12", "3.11", "3.10"] include: - os: windows-2025 py: "3.13" - os: macos-15 py: "3.13" steps: - name: ๐Ÿ“ฅ Checkout code uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 persist-credentials: false - name: ๐Ÿš€ Install uv uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 - name: ๐Ÿ Setup Python ${{ matrix.py }} uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.py }} - name: ๐Ÿ“ฆ Install tox run: uv tool install --python-preference only-managed --python 3.13 "tox>=4.45" --with tox-uv - name: Persistent .pipx_tests/package_cache uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ${{ github.workspace }}/.pipx_tests/package_cache/${{ matrix.py }} key: pipx-tests-package-cache-${{ runner.os }}-${{ matrix.py }} - name: ๐Ÿ—๏ธ Setup test suite run: tox run -vv --notest --skip-missing-interpreters false -e ${{ matrix.py }} - name: ๐Ÿ“ฆ Seed package cache run: tox exec -e ${{ matrix.py }} -- python scripts/update_package_cache.py testdata/tests_packages .pipx_tests/package_cache/ - name: ๐Ÿƒ Run test suite run: tox run --skip-pkg-install -e ${{ matrix.py }} timeout-minutes: 30 env: PYTEST_ADDOPTS: "-vv --durations=20" man: name: ๐Ÿ“– Build man page runs-on: ubuntu-24.04 steps: - name: ๐Ÿ“ฅ Checkout code uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: ๐Ÿš€ Install uv uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 - name: ๐Ÿ“ฆ Install tox run: uv tool install --python-preference only-managed --python 3.13 "tox>=4.45" --with tox-uv - name: ๐Ÿ“– Build man page run: tox run -e man - name: Show man page run: man -l pipx.1 zipapp: name: ๐Ÿ“ฆ Build zipapp runs-on: ubuntu-24.04 steps: - name: ๐Ÿ“ฅ Checkout code uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: ๐Ÿ Setup Python 3.10 uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.10" - name: ๐Ÿ“ฆ Build zipapp run: | pip install shiv shiv -c pipx -o ./pipx.pyz . ./pipx.pyz --version - name: Test zipapp by installing black run: python ./pipx.pyz install black - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: pipx.pyz path: pipx.pyz retention-days: 3 pipx-1.15.0/.github/workflows/zizmor.yml000066400000000000000000000011661521706623000202360ustar00rootroot00000000000000name: GitHub Actions Security Analysis with zizmor ๐ŸŒˆ on: push: branches: ["main"] pull_request: branches: ["**"] permissions: {} jobs: zizmor: name: Run zizmor ๐ŸŒˆ runs-on: ubuntu-latest permissions: security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files. steps: - name: Checkout repository uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Run zizmor ๐ŸŒˆ uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 pipx-1.15.0/.gitignore000066400000000000000000000003241521706623000145470ustar00rootroot00000000000000/.*_cache /build /dist /src/pipx/version.py /noxfile.py /.nox *.py[co] __pycache__ /site /.coverage* /.pipx_tests /testdata/tests_packages/*.txt /pipx.pyz *.egg-info build *.whl /pipx.1 /docs/_draft_changelog.md pipx-1.15.0/.pre-commit-config.yaml000066400000000000000000000034311521706623000170420ustar00rootroot00000000000000repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - id: end-of-file-fixer - id: check-added-large-files - id: trailing-whitespace - id: check-merge-conflict - id: check-case-conflict - repo: https://github.com/google/yamlfmt rev: v0.21.0 hooks: - id: yamlfmt - repo: https://github.com/hukkin/mdformat rev: 1.0.0 hooks: - id: mdformat args: ["--wrap", "120"] additional_dependencies: - mdformat-mkdocs - mdformat-gfm - mdformat-gfm-alerts - mdformat-footnote - mdformat-config - mdformat-front-matters exclude: ^(docs/changelog\.md|testdata) - repo: https://github.com/tox-dev/pyproject-fmt rev: v2.25.0 hooks: - id: pyproject-fmt - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.15.18 hooks: - id: ruff-check args: ["--fix", "--unsafe-fixes", "--show-fixes", "--exit-non-zero-on-fix"] - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy rev: v2.1.0 hooks: - id: mypy args: ['--warn-unused-ignores', '--strict-equality', '--no-implicit-optional', '--check-untyped-defs'] exclude: 'testdata/test_package_specifier/(local_extras|local_manpage)/setup.py' additional_dependencies: - "mkdocs-gen-files" - "nox" - "packaging>=20" - "platformdirs>=2.1" - "pytest" - "pytest-mock" - "tomli; python_version < '3.11'" # Configuration for codespell is in pyproject.toml - repo: https://github.com/codespell-project/codespell rev: v2.4.2 hooks: - id: codespell additional_dependencies: - tomli exclude: ^testdata pipx-1.15.0/.pre-commit-hooks.yaml000066400000000000000000000001721521706623000167170ustar00rootroot00000000000000- id: pipx name: pipx entry: pipx run require_serial: true language: python minimum_pre_commit_version: '2.9.2' pipx-1.15.0/.readthedocs.yml000066400000000000000000000002101521706623000156370ustar00rootroot00000000000000version: 2 build: os: ubuntu-22.04 tools: python: "3.13" commands: - pip install "tox>=4.45" tox-uv - tox run -e docs pipx-1.15.0/LICENSE000066400000000000000000000020741521706623000135700ustar00rootroot00000000000000MIT License Copyright (c) 2018 Chad Smith and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pipx-1.15.0/changelog.d/000077500000000000000000000000001521706623000147315ustar00rootroot00000000000000pipx-1.15.0/changelog.d/.gitignore000066400000000000000000000000141521706623000167140ustar00rootroot00000000000000!.gitignore pipx-1.15.0/docs/000077500000000000000000000000001521706623000135105ustar00rootroot00000000000000pipx-1.15.0/docs/README.md000066400000000000000000000055301521706623000147720ustar00rootroot00000000000000

# pipx โ€” Install and Run Python Applications in Isolated Environments

image PyPI version

**Documentation**: **Source Code**: _For comparison to other tools including pipsi, see [Comparison to Other Tools](https://pipx.pypa.io/stable/explanation/comparisons/)._ ## Overview: What is `pipx`? pipx is a tool to help you install and run end-user applications written in Python. It's roughly similar to macOS's `brew`, JavaScript's [npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b), and Linux's `apt`. It's closely related to pip. In fact, it uses pip, but is focused on installing and managing Python packages that can be run from the command line directly as applications. ### Features `pipx` enables you to - expose CLI entrypoints of packages ("apps") installed to isolated environments with the `install` command, guaranteeing no dependency conflicts and clean uninstalls; - easily list, upgrade, and uninstall packages that were installed with pipx; and - run the latest version of a Python application in a temporary environment with the `run` command. Best of all, pipx runs with regular user permissions, never calling `sudo pip install`. ## Install pipx ### On macOS ``` brew install pipx pipx ensurepath ``` ### On Linux ``` python3 -m pip install --user pipx python3 -m pipx ensurepath ``` ### On Windows ``` scoop install pipx pipx ensurepath ``` For more detailed installation instructions, see the [full documentation](https://pipx.pypa.io/stable/how-to/install-pipx/). ## Quick Start Install an application globally: ``` pipx install pycowsay pycowsay mooo ``` Run an application without installing: ``` pipx run pycowsay moo ``` See the [full documentation](https://pipx.pypa.io/stable/) for more details. ## Contributing Issues and Pull Requests are definitely welcome! Check out [Contributing](https://pipx.pypa.io/stable/contributing/) to get started. Everyone who interacts with the pipx project via codebase, issue tracker, chat rooms, or otherwise is expected to follow the [PSF Code of Conduct](https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md). pipx-1.15.0/docs/changelog.md000066400000000000000000001403101521706623000157600ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The 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). This project uses [*towncrier*](https://towncrier.readthedocs.io/) for keeping the changelog. DO NOT commit any changes to this file. {% include '_draft_changelog.md' ignore missing %} ## [1.15.0](https://github.com/pypa/pipx/tree/1.15.0) - 2026-06-24 ### Features - Add a `--dry-run` flag to `pipx ensurepath` that reports which directories would be added to `PATH` without modifying `PATH` or any shell configuration file. ([#1014](https://github.com/pypa/pipx/issues/1014)) ### Bugfixes - Fix `pipx uninject` for uv-backed environments, where uv lacks pip's `list --not-required` option. Both backends now derive the not-required set from installed distribution metadata, so they behave identically, and a dependency pulled in only by another package's extra is kept instead of being treated as removable. ([#1841](https://github.com/pypa/pipx/issues/1841)) ## [1.14.1](https://github.com/pypa/pipx/tree/1.14.1) - 2026-06-17 ### Bugfixes - Restore the original package if `pipx reinstall-all` is interrupted during reinstall. ([#966](https://github.com/pypa/pipx/issues/966)) - Allow importing `pipx.util` before `pipx.paths`. ([#1333](https://github.com/pypa/pipx/issues/1333)) - Ensure `pipx inject --force` reinstalls injected packages without persisting the force-reinstall option in package metadata. ([#1545](https://github.com/pypa/pipx/issues/1545)) - Preserve installer errors when package-name detection fails during local package installs. ([#1567](https://github.com/pypa/pipx/issues/1567)) - Expose manual pages for editable local package installs. ([#1793](https://github.com/pypa/pipx/issues/1793)) - Fixed `pipx pin --injected-only` so it can pin injected packages when the main app is already pinned. ([#1835](https://github.com/pypa/pipx/issues/1835)) ## [1.14.0](https://github.com/pypa/pipx/tree/1.14.0) - 2026-06-04 ### Features - Add `--no-path-check` to `pipx run` to skip the warning when the app is already on `PATH`. Useful for shim scripts that wrap `pipx run ` under the same name as the app. ([#1824](https://github.com/pypa/pipx/issues/1824)) ### Bugfixes - Add regression coverage for `pipx inject` accepting `--force` before or after injected dependencies. ([#1444](https://github.com/pypa/pipx/issues/1444)) ### Improved Documentation - Use the bootstrap pipx executable when adding PATH in self-managed installs. ([#1481](https://github.com/pypa/pipx/issues/1481)) ## [1.13.0](https://github.com/pypa/pipx/tree/1.13.0) - 2026-05-30 ### Features - Add `PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE` to skip automatic shared library upgrades. ([#1650](https://github.com/pypa/pipx/issues/1650)) - Update the error message in get-pipx.py; it is obsoleted, not deprecated. ([#1813](https://github.com/pypa/pipx/issues/1813)) ### Bugfixes - Install app scripts with shebangs that ignore `PYTHONPATH`. ([#1584](https://github.com/pypa/pipx/issues/1584)) - Refresh cached standalone Python indexes written by older pipx versions before using `--fetch-missing-python`. ([#1774](https://github.com/pypa/pipx/issues/1774)) ### Improved Documentation - Document how to clear cache warnings from `runpip`. ([#1802](https://github.com/pypa/pipx/issues/1802)) ## [1.12.0](https://github.com/pypa/pipx/tree/1.12.0) - 2026-05-06 ### Features - Add `--fetch-python` and `PIPX_FETCH_PYTHON` to control standalone Python downloads, with values `always`, `missing`, or `never`. ([#1663](https://github.com/pypa/pipx/issues/1663)) - Add an opt-in `uv` backend. Install `pipx[uv]` (or put `uv` on `PATH`) and pipx will create venvs with `uv venv`, manage packages with `uv pip`, and run ephemeral apps via `uv tool run`. **Default change:** when `uv` is reachable, pipx uses it for **new** venvs by default. Existing venvs keep their recorded backend; flipping a venv requires `pipx reinstall NAME --backend X`. Set `PIPX_DEFAULT_BACKEND=pip` to force pip even when uv is available, or pass `--backend pip` per command. `pipx install pip` always picks the pip backend (uv venvs have no pip). Selection precedence: `--backend` > recorded venv metadata > `PIPX_DEFAULT_BACKEND` > auto-detect. The env var sits *below* metadata so setting `PIPX_DEFAULT_BACKEND=uv` does not silently retarget existing pip-backed venvs; the recorded backend wins until you flip it explicitly. `pipx environment` exposes the resolved backend and its source as `PIPX_RESOLVED_BACKEND` and `PIPX_BACKEND_SOURCE`. **Metadata version bump:** the venv metadata file format moves from `0.5` to `0.6` to record the chosen backend. Running this pipx version against an existing venv rewrites its metadata to `0.6` on the next install/upgrade. If you downgrade pipx after that, the older version raises `Unknown metadata version 0.6` and you'll need to reinstall the affected venvs from the older pipx (`pipx reinstall NAME`). ([#1808](https://github.com/pypa/pipx/issues/1808)) ### Deprecations and Removals - Deprecate `--fetch-missing-python` and `PIPX_FETCH_MISSING_PYTHON`; both are now aliases for `--fetch-python=missing` / `PIPX_FETCH_PYTHON=missing`. `PIPX_FETCH_MISSING_PYTHON=0` (and other falsy values) now disables fetching, where previously any value enabled it. The `pipx.interpreter.find_python_interpreter` keyword `fetch_missing_python: bool` is replaced by `fetch_python: FetchPythonOptions`. ([#1663](https://github.com/pypa/pipx/issues/1663)) ## [1.11.2](https://github.com/pypa/pipx/tree/1.11.2) - 2026-05-05 ### Features - Add ``pipx help`` and ``pipx help `` aliases for existing help output. ([#1535](https://github.com/pypa/pipx/issues/1535)) - Drop support for Python 3.9. ([#1786](https://github.com/pypa/pipx/issues/1786)) - Replace if/elif command dispatch in main.py with argparse set_defaults. ([#1794](https://github.com/pypa/pipx/issues/1794)) ## [1.11.1](https://github.com/pypa/pipx/tree/1.11.1) - 2026-03-31 ### Bugfixes - Ignore `PIP_TARGET` environment variable to prevent pip from installing outside the venv. ([#735](https://github.com/pypa/pipx/issues/735)) - Fix `pipx install` failing on Windows when the username contains non-Latin characters (e.g. cyrillic, Chinese). ([#780](https://github.com/pypa/pipx/issues/780)) - Show installed version and suggest `upgrade` when `install` detects an existing installation. ([#795](https://github.com/pypa/pipx/issues/795)) - `--verbose` and `--quiet` flags before the subcommand are no longer silently ignored. ([#1282](https://github.com/pypa/pipx/issues/1282)) - Remove dependency app symlinks when uninjecting a package that was injected with `--include-deps`. ([#1364](https://github.com/pypa/pipx/issues/1364)) - Remove `setuptools` from shared libs to prevent version conflicts when app venvs use a different Python. ([#1539](https://github.com/pypa/pipx/issues/1539)) - Prevent `uninject` from removing dependencies still needed by other packages in the venv. ([#1672](https://github.com/pypa/pipx/issues/1672)) ## [1.11.0](https://github.com/pypa/pipx/tree/1.11.0) - 2026-03-23 ### Features - Add `PIPX_MAX_LOGS` environment variable to configure the maximum number of log files to keep. ([#1570](https://github.com/pypa/pipx/issues/1570)) ### Bugfixes - Allow `--pip-args` to be passed to `upgrade-all`. ([#1503](https://github.com/pypa/pipx/issues/1503)) - Fix `--pip-args='-c '` breaking when constraint target is a URL instead of a local path. ([#1582](https://github.com/pypa/pipx/issues/1582)) - Enable keyring authentication for private package indexes by setting `PIP_KEYRING_PROVIDER=subprocess` ([#1603](https://github.com/pypa/pipx/issues/1603)) - Added error handling for `pip list` command failures ([#1698](https://github.com/pypa/pipx/issues/1698)) ### Improved Documentation - Remove outdated "experimental" note from `--suffix` help text. ([#1621](https://github.com/pypa/pipx/issues/1621)) ## [1.10.1](https://github.com/pypa/pipx/tree/1.10.1) - 2026-03-20 ### Bugfixes - Use stored pip_args from install metadata when running `pipx upgrade` without explicit `--pip-args` ([#1441](https://github.com/pypa/pipx/issues/1441)) - Fix `--global` flag being silently ignored when placed before the subcommand ([#1443](https://github.com/pypa/pipx/issues/1443)) - Respect `--quiet` flag in `install` and suppress animations and success messages ([#1508](https://github.com/pypa/pipx/issues/1508)) - Catch missing Python interpreter in `upgrade` and show helpful error instead of crashing ([#1602](https://github.com/pypa/pipx/issues/1602)) - Prevent data loss by rejecting relative paths in `uninstall`, `reinstall`, and other package-name arguments ([#1718](https://github.com/pypa/pipx/issues/1718)) ## [1.10.0](https://github.com/pypa/pipx/tree/1.10.0) - 2026-03-18 ### Features - Add `--with` flag to `pipx run` to allow injecting dependencies ([#1607](https://github.com/pypa/pipx/issues/1607)) - 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)) ### Bugfixes - Fixed upgrade command failing when package name includes extras (e.g., `pipx upgrade "coverage[toml]"`). ([#925](https://github.com/pypa/pipx/issues/925)) - Fix run command with bash substitution (e.g. `pipx run <(pbpaste)`) ([#1293](https://github.com/pypa/pipx/issues/1293)) - Allow `pipx runpip` to split single string arguments. ([#1520](https://github.com/pypa/pipx/issues/1520)) - 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)) ### Misc - [#1638](https://github.com/pypa/pipx/issues/1638), [#1731](https://github.com/pypa/pipx/issues/1731) ## [1.9.0](https://github.com/pypa/pipx/tree/1.9.0) - 2026-03-17 ### Features - Add completion choices for `pipx environment --value`. ([#1498](https://github.com/pypa/pipx/issues/1498)) ### Bugfixes - Ignore recursive symlink loops in PIPX_BIN_DIR. ([#1592](https://github.com/pypa/pipx/issues/1592)) - `pipx reinstall`: An exception will now be raised if package is pinned. ([#1611](https://github.com/pypa/pipx/issues/1611)) - Stop `pipx run` from leaving bad temporary venvs when first installation was unsuccessful. ([#1709](https://github.com/pypa/pipx/issues/1709)) ## [1.8.0](https://github.com/pypa/pipx/tree/1.8.0) - 2025-09-30 ### Features - Rename environmental variable `USE_EMOJI` to `PIPX_USE_EMOJI`. ([#1395](https://github.com/pypa/pipx/issues/1395)) - Add `--all-shells` flag to `pipx ensurepath`. ([#1585](https://github.com/pypa/pipx/issues/1585)) - Add support for Python 3.13 ([#1647](https://github.com/pypa/pipx/issues/1647)) ### Bugfixes - On Windows, no longer overwrite existing files on upgrade if source and destination are the same ([#683](https://github.com/pypa/pipx/issues/683)) - Update the logic of finding python interpreter such that `--fetch-missing-python` works on Windows ([#1521](https://github.com/pypa/pipx/issues/1521)) - Fix no message displayed when no packages are upgraded with `upgrade-all`. ([#1565](https://github.com/pypa/pipx/issues/1565)) - Fix incorrect order of flags when using `pipx upgrade`. ([#1610](https://github.com/pypa/pipx/issues/1610)) - Update the archive name of build of Python for Windows ([#1630](https://github.com/pypa/pipx/issues/1630)) - Set up standalone python fetching to use checksums directly from the GitHub API. ([#1652](https://github.com/pypa/pipx/issues/1652)) - Fix running a script with explicitly empty ``dependencies = []``. ([#1658](https://github.com/pypa/pipx/issues/1658)) ### Improved Documentation - Fix `/changelog/` and `/contributing/` docs URLs ([#1540](https://github.com/pypa/pipx/issues/1540)) ## [1.7.1](https://github.com/pypa/pipx/tree/1.7.1) - 2024-08-23 ### Bugfixes - 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)) ## [1.7.0](https://github.com/pypa/pipx/tree/1.7.0) - 2024-08-22 ### Features - 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)) - List `PIPX_GLOBAL_[HOME|BIN_DIR|MAN_DIR]` in `pipx environment`. ([#1492](https://github.com/pypa/pipx/issues/1492)) ### Bugfixes - Introduce `PIPX_HOME_ALLOW_SPACE` environment variable, to silence the spaces in pipx home path warning ([#1320](https://github.com/pypa/pipx/issues/1320)) - Fix passing constraints file path into `pipx install` operation via `pip` args ([#1389](https://github.com/pypa/pipx/issues/1389)) - Add help messages for `pipx pin` and `pipx unpin` commands. ([#1438](https://github.com/pypa/pipx/issues/1438)) - Stop `pipx install --global` from installing files in `~/.local`. ([#1475](https://github.com/pypa/pipx/issues/1475)) - Fix installation abortion on multiple packages when one or more are already installed. ([#1509](https://github.com/pypa/pipx/issues/1509)) ### Improved Documentation - Move all documentation files to the `docs` directory. ([#1479](https://github.com/pypa/pipx/issues/1479)) ## [1.6.0](https://github.com/pypa/pipx/tree/1.6.0) - 2024-06-01 ### Features - Add `install-all` command to install packages according to spec metadata file. ([#687](https://github.com/pypa/pipx/issues/687)) - Introduce `pipx pin` and `pipx unpin` commands, which can be used to pin or unpin the version 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)) - Add a new option `--pinned` to `pipx list` command for listing pinned packages only. ([#891](https://github.com/pypa/pipx/issues/891)) - Add `pipx interpreter upgrade` command to upgrade local standalone python in micro/patch level ([#1249](https://github.com/pypa/pipx/issues/1249)) - Add `--requirement` option to `inject` command to read list of packages from a text file. ([#1252](https://github.com/pypa/pipx/issues/1252)) - Add `pipx upgrade-shared` command, to create/upgrade shared libraries as a standalone command. ([#1316](https://github.com/pypa/pipx/issues/1316)) - Allow `upgrade` command to accept multiple packages as arguments. ([#1336](https://github.com/pypa/pipx/issues/1336)) - Support Python version for `--python` arg when py launcher is not available ([#1342](https://github.com/pypa/pipx/issues/1342)) - Make `install-all` gather errors in batch ([#1348](https://github.com/pypa/pipx/issues/1348)) ### Bugfixes - Resolve the `DEFAULT_PYTHON` to the actual absolute path ([#965](https://github.com/pypa/pipx/issues/965)) - Fix error log overwrite for "-all" batch operations. ([#1132](https://github.com/pypa/pipx/issues/1132)) - Do not reinstall already injected packages without `--force` being passed. ([#1300](https://github.com/pypa/pipx/issues/1300)) - Only show `--python` and `--force` flag warning if both flags are present ([#1304](https://github.com/pypa/pipx/issues/1304)) - Don't allow paths to be passed into `pipx reinstall`, as this might wreak havoc. ([#1324](https://github.com/pypa/pipx/issues/1324)) - Make the Python `venv` module arguments work with `upgrade --install` ([#1344](https://github.com/pypa/pipx/issues/1344)) - Fix version check for standalone python ([#1349](https://github.com/pypa/pipx/issues/1349)) - Validate package(s) argument should not be path(s). ([#1354](https://github.com/pypa/pipx/issues/1354)) - Validate whether a package is an URL correctly. ([#1355](https://github.com/pypa/pipx/issues/1355)) - Support python3.8 for standalone python builds ([#1375](https://github.com/pypa/pipx/issues/1375)) - Install specified version of `--preinstall` dependency instead of latest version ([#1377](https://github.com/pypa/pipx/issues/1377)) - 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)) - Fix discovery of a `pipx run` entry point if a local path was given as package. ([#1422](https://github.com/pypa/pipx/issues/1422)) ### Improved Documentation - Create a dedicated section for manual pages and add an example with `pdm-backend`. ([#1312](https://github.com/pypa/pipx/issues/1312)) - Add example, test and cli help description how to install multiple packages with the --preinstall flag ([#1321](https://github.com/pypa/pipx/issues/1321)) - Refine docs generation script and template. ([#1325](https://github.com/pypa/pipx/issues/1325)) - Add a note about sourcing the shell config file for `ensure_path` ([#1346](https://github.com/pypa/pipx/issues/1346)) ## [1.5.0](https://github.com/pypa/pipx/tree/1.5.0) - 2024-03-29 ### Features - Add `--global` option to `pipx` commands. - This will run the action in a global scope and affect environment for all system users. ([#754](https://github.com/pypa/pipx/issues/754)) - Add a `--fetch-missing-python` flag to all commands that accept a `--python` flag. - 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)) - Add commands to list and prune standalone interpreters ([#1248](https://github.com/pypa/pipx/issues/1248)) - Revert platform-specific directories on MacOS and Windows - 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)) - Add `--install` option to `pipx upgrade` command. - This will install the package given as argument if it is not already installed. ([#1262](https://github.com/pypa/pipx/issues/1262)) ### Bugfixes - Correctly resolve home directory in pipx directory environment variables. ([#94](https://github.com/pypa/pipx/issues/94)) - Pass through `pip` arguments when upgrading shared libraries. ([#964](https://github.com/pypa/pipx/issues/964)) - Fix installation issues when files in the working directory interfere with venv creation process. ([#1091](https://github.com/pypa/pipx/issues/1091)) - Report correct filename in tracebacks with `pipx run ` ([#1191](https://github.com/pypa/pipx/issues/1191)) - Let self-managed pipx uninstall itself on windows again. ([#1203](https://github.com/pypa/pipx/issues/1203)) - Fix path resolution for python executables looked up in PATH on windows. ([#1205](https://github.com/pypa/pipx/issues/1205)) - Display help message when `pipx install` is run without arguments. ([#1266](https://github.com/pypa/pipx/issues/1266)) - Fix crashes due to superfluous `-q ` flags by discarding exceeding values ([#1283](https://github.com/pypa/pipx/issues/1283)) ### Improved Documentation - Update the completion instructions for zipapp users. ([#1072](https://github.com/pypa/pipx/issues/1072)) - Update the example for running scripts with dependencies. ([#1227](https://github.com/pypa/pipx/issues/1227)) - Update the docs for package developers on the use of configuration using pyproject.toml ([#1229](https://github.com/pypa/pipx/issues/1229)) - Add installation instructions for Fedora ([#1239](https://github.com/pypa/pipx/issues/1239)) - Update the examples for installation from local dir ([#1277](https://github.com/pypa/pipx/issues/1277)) - Fix inconsistent wording in `pipx install` command description. ([#1307](https://github.com/pypa/pipx/issues/1307)) ### Deprecations and Removals - Deprecate `--skip-maintenance` flag of `pipx list`; maintenance is now never executed there ([#1256](https://github.com/pypa/pipx/issues/1256)) ### Misc - [#1296](https://github.com/pypa/pipx/issues/1296) ## [1.4.3](https://github.com/pypa/pipx/tree/1.4.3) - 2024-01-16 ### Bugfixes - Autofix python version for pylauncher, when version is provided prefixed with `python` ([#1150](https://github.com/pypa/pipx/issues/1150)) - Support building pipx wheels with setuptools-scm<7, such as on FreeBSD. ([#1208](https://github.com/pypa/pipx/issues/1208)) ### Improved Documentation - Provide useful error messages when unresolvable python version is passed ([#1150](https://github.com/pypa/pipx/issues/1150)) - Introduce towncrier for managing the changelog ([#1161](https://github.com/pypa/pipx/issues/1161)) - Add workaround for using pipx applications in shebang under macOS ([#1198](https://github.com/pypa/pipx/issues/1198)) ## [1.4.2](https://github.com/pypa/pipx/tree/1.4.2) ### Features - Allow skipping maintenance tasks during list command - Raise more user friendly error when provided `--python` version is not found - Update `pipx run` on scripts using `/// script` and no `run` table following the updated version of PEP 723 (#1180) ### Bugfixes - Include `tomli` into `pipx.pyz` (zipapp) so that it can be executed with Python 3.10 or earlier (#1142) - Fix resolving the python executable path on linux - `pipx run`: Verify whether the script name provided is a file before running it - Avoid repeated exception logging in a few rare cases (#1192) ## [1.4.1](https://github.com/pypa/pipx/tree/1.4.1) ### Bugfixes - Set default logging level to WARNING, so debug log messages won't be shown without passing additional flags such as `--verbose` ## [1.4.0](https://github.com/pypa/pipx/tree/1.4.0) ### Features - Add `--quiet` and `--verbose` options for the `pipx` subcommands - Add ability to install multiple packages at once - Delete directories directly instead of spawning rmdir on Windows ### Improved Documentation - Add Scoop installation instructions ### Bugfixes - "Failed to delete" error when using Microsoft Store Python - "No pyvenv.cfg file" error when using Microsoft Store Python (#1164) ## [1.3.3](https://github.com/pypa/pipx/tree/1.3.3) ### Improved Documentation - Make the logo more visible in dark mode ## [1.3.2](https://github.com/pypa/pipx/tree/1.3.2) ### Features - The project version number is now dynamic and generated from the VCS at build time ### Improved Documentation - Add additional example for --pip-args option, to docs/examples.md ## [1.3.1](https://github.com/pypa/pipx/tree/1.3.1) ### Bugfixes - Fix combining of --editable and --force flag ## [1.3.0](https://github.com/pypa/pipx/tree/1.3.0) ### Features - Allow running `pip` with `pipx run` - Add `--with-suffix` for `pipx inject` command - `pipx install`: emit a warning when `--force` and `--python` were passed at the same time - Add explicit 3.12 support - Make usage message in `pipx run` show `package_or_url`, so extra will be printed out as well - Use the py launcher, if available, to select Python version with the `--python` option - add pre-commit hook support - Add `pipx install --preinstall` to support preinstalling build requirements - Return an error message when directory can't be added to PATH successfully - Expose manual pages included in an application installed with `pipx install` - Check whether pip module exists in shared lib before performing any actions, such as `reinstall-all`. - Drop `setuptools` and `wheel` from the shared libraries. This results in less time consumption when the libraries are automatically upgraded. - Support [inline script metadata](https://packaging.python.org/en/latest/specifications/inline-script-metadata/) in `pipx run`. - Imply `--include-apps` when running `pipx inject --include-deps` - Add `--force-reinstall` to pip arguments when `--force` was passed - Support including requirements in scripts run using `pipx run` (#916) - Pass `pip_args` to `shared_libs.upgrade()` - Fallback to user's log path if the default log path (`$PIPX_HOME/logs`) is not writable to aid with pipx being used for multi-user (e.g. system-wide) installs of applications - Don't show escaped backslashes for paths in console output - Move `pipx` paths to ensure compatibility with the platform-specific user directories - Pass `--no-input` to pip when output is not piped to parent stdout - Print all environment variables in `pipx environment` ### Improved Documentation - Add more examples for `pipx run` - Add subsection to make README easier to read ### Deprecations and Removals - Drop support for Python 3.7 ### Bugfixes - Fix wrong interpreter usage when injecting local pip-installable dependencies into venvs - Fix program name in generated manual page ## [1.2.1](https://github.com/pypa/pipx/tree/1.2.1) ### Bugfixes - Fix compatibility to packaging 23.2+ by removing reliance on packaging's requirement validation logic and detecting a URL-based requirement in pipx. (#1070) ## [1.2.0](https://github.com/pypa/pipx/tree/1.2.0) ### Features - Add `pipx uninject` command (#820) - Ship a [zipapp](https://docs.python.org/3/library/zipapp.html) of pipx - Match pip's behaviour when package name ends with archive extension (treat it as a path) - Change the program name to `path/to/python -m pipx` when running as `python -m pipx` - Improve the detection logic for MSYS2 to avoid entering infinite loop (#908) (#938) - Remove extra trailing quote from exception message - Fix EncodingWarning in `pipx_metadata_file`. ### Improved Documentation - Add an example for installation from source with extras - Fix `pipx run` examples and update Python versions used by `pipx install` examples ### Bugfixes - Add test for pip module in `pipx reinstall` to fix an issue with `pipx reinstall-all` (#935) ## [1.1.0](https://github.com/pypa/pipx/tree/1.1.0) ### Features - Add `pipx environment` command (#793) - Add `list --short` option to list only package names (#804) - Improve the behaviour of `shlex.split` on Windows, so paths on Windows can be handled properly when they are passed in `--pip-args`. (#794) - [dev] Change github action job names - Add additional examples for installation from git repos - [packaging] Switch to [PEP 621](https://www.python.org/dev/peps/pep-0621/) - Add a CACHEDIR.TAG to the cache directory to prevent it from being included in archives and backups. For more information about cache directory tags, see https://bford.info/cachedir ### Bugfixes - Fix encoding issue on Windows when pip fails to install a package ### Improved Documentation - Add more examples - Fix the command for [installing development version](https://pipx.pypa.io/stable/installation/#install-pipx-development-versions). (#801) - Fix test status badge in readme file ## [1.0.0](https://github.com/pypa/pipx/tree/1.0.0) ### Features - Support [argcomplete 2.0.0](https://pypi.org/project/argcomplete/2.0.0) (#790) - Include machinery to build a manpage for pipx with [argparse-manpage](https://pypi.org/project/argparse-manpage/). - Add better handling for 'app not found' when a single app is present in the project, and an improved error message (#733) ### Bugfixes - Fixed animations sending output to stdout, which can break JSON output. (#769) - Fix typo in `pipx upgrade-all` output ## [0.17.0](https://github.com/pypa/pipx/tree/0.17.0) - Support `pipx run` with version constraints and extras. (#697) ## [0.16.5](https://github.com/pypa/pipx/tree/0.16.5) - Fixed `pipx list` output phrasing to convey that python version displayed is the one with which package was installed. - Fixed `pipx install` to provide return code 0 if venv already exists, similar to pipโ€™s behavior. (#736) - [docs] Update ansible's install command in [Programs to Try document](https://pipx.pypa.io/stable/programs-to-try/#ansible) to work with Ansible 2.10+ (#742) ## [0.16.4](https://github.com/pypa/pipx/tree/0.16.4) - Fix to `pipx ensurepath` to fix behavior in user locales other than UTF-8, to fix #644. The internal change is to use userpath v1.6.0 or greater. (#700) - Fix virtual environment inspection for Python releases that uses an int for its release serial number. (#706) - Fix PermissionError in windows when pipx manages itself. (#718) ## [0.16.3](https://github.com/pypa/pipx/tree/0.16.3) - Organization: pipx is extremely pleased to now be a project of the Python Packaging Authority (PyPA)! Note that our github URL has changed to [pypa/pipx](https://github.com/pypa/pipx) - Fixed `pipx list --json` to return valid json with no venvs installed. Previously would return an empty string to stdout. (#681) - Changed `pipx ensurepath` bash behavior so that only one of {`~/.profile`, `~/.bash\_profile`} is modified with the extra pipx paths, not both. Previously, if a `.bash_profile` file was created where one didn't exist, it could cause problems, e.g. #456. The internal change is to use userpath v1.5.0 or greater. (#684) - Changed default nox tests, Github Workflow tests, and pytest behavior to use local pypi server with fixed lists of available packages. This allows greater test isolation (no network pypi access needed) and determinism (fixed available dependencies.) It also allows running the tests offline with some extra preparation beforehand (See [Running Unit Tests Offline](https://pipx.pypa.io/stable/contributing/#running-unit-tests-offline)). The old style tests that use the internet to access pypi.org are still available using `nox -s tests_internet` or `pytest --net-pypiserver tests`. (#686) * Colorama is now only installed on Windows. (#691) ## [0.16.2.1](https://github.com/pypa/pipx/tree/0.16.2.1) - Changed non-venv-info warnings and notices from `pipx list` to print to stderr. This especially prevents `pipx list --json` from printing invalid json to stdout. (#680) - Fixed bug that could cause uninstall on Windows with injected packages to uninstall too many apps from the local binary directory. (#679) ## [0.16.2.0](https://github.com/pypa/pipx/tree/0.16.2.0) - Fixed bug #670 where uninstalling a venv could erroneously uninstall other apps from the local binary directory. (#672) - Added `--json` switch to `pipx list` to output rich json-metadata for all venvs. - Ensured log files are utf-8 encoded to prevent Unicode encoding errors from occurring with emojis. (#646) - Fixed issue which made pipx incorrectly list apps as part of a venv when they were not installed by pipx. (#650) - Fixed old regression that would prevent pipx uninstall from cleaning up linked binaries if the venv was old and did not have pipx metadata. (#651) - Fixed bugs with suffixed-venvs on Windows. Now properly summarizes install, and actually uninstalls associated binaries for suffixed-venvs. (#653) - Changed venv minimum python version to 3.6, removing python 3.5 which is End of Life. (#666) ## [0.16.1.0](https://github.com/pypa/pipx/tree/0.16.1.0) - Introduce the `pipx.run` entry point group as an alternative way to declare an application for `pipx run`. - Fix cursor show/hide to work with older versions of Windows. (#610) - Support text colors on Windows. (#612) - Better platform unicode detection to avoid errors and allow showing emojis when possible. (#614) - Don't emit show cursor or hide cursor codes if STDERR is not a tty. (#620) - Sped up `pipx list` (#624). - pip errors no longer stream to the shell when pip fails during a pipx install. pip's output is now saved to a log file. In the shell, pipx will tell you the location of the log file and attempt to summarize why pip failed. (#625) - For `reinstall-all`, fixed bug where missing python executable would cause error. (#634) - Fix regression which prevented pipx from working with pythonloc (and `__pypackages__` folder). (#636) ## [0.16.0.0](https://github.com/pypa/pipx/tree/0.16.0.0) - New venv inspection! The code that pipx uses to examine and determine metadata in an installed venv has been made faster, better, and more reliable. It now uses modern python libraries like `packaging` and `importlib.metadata` to examine installed venvs. It also now properly handles installed package extras. In addition, some problems pipx has had with certain characters (like periods) in package names should be remedied. - Added reinstall command for reinstalling a single venv. - Changed `pipx run` on non-Windows systems to actually replace pipx process with the app process instead of running it as a subprocess. (Now using python's `os.exec*`) - [bugfix] Fixed bug with reinstall-all command when package have been installed using a specifier. Now the initial specifier is used. - [bugfix] Override display of `PIPX_DEFAULT_PYTHON` value when generating web documentation for `pipx install` #523 - [bugfix] Wrap help documentation for environment variables. - [bugfix] Fixed uninstall crash that could happen on Windows for certain packages - [feature] Venv package name arguments now do not have to match exactly as pipx has them stored, but can be specified in any python-package-name-equivalent way. (i.e. case does not matter, and `.`, `-`, `_` characters are interchangeable.) - [change] Venvs with a suffix: A suffix can contain any characters, but for purposes of uniqueness, python package name rules apply--upper- and lower-case letters are equivalent, and any number of `.`, `-`, or `_` characters in a row are equivalent. (e.g. if you have a suffixed venv `pylint_1.0A` you could not add another suffixed venv called `pylint--1-0a`, as it would not be a unique name.) - [implementation detail] Pipx shared libraries (providing pip, setuptools, wheel to pipx) are no longer installed using pip arguments taken from the last regular pipx install. If you need to apply pip arguments to pipx's use of pip for its internal shared libraries, use PIP\_\* environment variables. - [feature] Autocomplete for venv names is no longer restricted to an exact match to the literal venv name, but will autocomplete any logically-similar python package name (i.e. case does not matter, and `.`, `-`, `_` characters are all equivalent.) - pipx now reinstall its internal shared libraries when the user executes `reinstall-all`. - Made sure shell exit codes from every pipx command are correct. In the past some (like from `pipx upgrade`) were wrong. The exit code from `pipx runpip` is now the exit code from the `pip` command run. The exit code from `pipx list` will be 1 if one or more venvs have problems that need to be addressed. - pipx now writes a log file for each pipx command executed to `$PIPX_HOME/logs`, typically `~/.local/pipx/logs`. pipx keeps the most recent 10 logs and deletes others. - `pipx upgrade` and `pipx upgrade-all` now have a `--upgrade-injected` option which directs pipx to also upgrade injected packages. - `pipx list` now detects, identifies, and suggests a remedy for venvs with old-internal data (internal venv names) that need to be updated. - Added a "Troubleshooting" page to the pipx web documentation for common problems pipx users may encounter. - pipx error, warning, and other messages now word-wrap so words are not split across lines. Their appearance is also now more consistent. ## [0.15.6.0](https://github.com/pypa/pipx/tree/0.15.6.0) - [docs] Update license - [docs] Display a more idiomatic command for registering completions on fish. - [bugfix] Fixed regression in list, inject, upgrade, reinstall-all commands when suffixed packages are used. - [bugfix] Do not reset package url during upgrade when main package is `pipx` - Updated help text to show description for `ensurepath` and `completions` help - Added support for user-defined default python interpreter via new `PIPX_DEFAULT_PYTHON`. Helpful for use with pyenv among other uses. - [bugfix] Fixed bug where extras were ignored with a PEP 508 package specification with a URL. ## [0.15.5.1](https://github.com/pypa/pipx/tree/0.15.5.1) - [bugfix] Fixed regression of 0.15.5.0 which erroneously made installing from a local path with package extras not possible. ## [0.15.5.0](https://github.com/pypa/pipx/tree/0.15.5.0) - pipx now parses package specification before install. It removes (with warning) the `--editable` install option for any package specification that is not a local path. It also removes (with warning) any environment markers. - Disabled animation when we cannot determine terminal size or if the number of columns is too small. (Fixes #444) - [feature] Version of each injected package is now listed after name for `pipx list --include-injected` - Change metadata recorded from version-specified install to allow upgrades in future. Adds pipx dependency on `packaging` package. - [bugfix] Prevent python error in case where package has no pipx metadata and advise user how to fix. - [feature] `ensurepath` now also ensures that pip user binary path containing pipx itself is in user's PATH if pipx was installed using `pip install --user`. - [bugfix] For `pipx install`, fixed failure to install if user has `PIP_USER=1` or `user=true` in pip.conf. (#110) - [bugfix] Requiring userpath v1.4.1 or later so ensure Windows bug is fixed for `ensurepath` (#437) - [feature] log pipx version (#423) - [feature] `--suffix` option for `install` to allow multiple versions of same tool to be installed (#445) - [feature] pipx can now be used with the Windows embeddable Python distribution ## [0.15.4.0](https://github.com/pypa/pipx/tree/0.15.4.0) - [feature] `list` now has a new option `--include-injected` to show the injected packages in the main apps - [bugfix] Fixed bug that can cause crash when installing an app ## [0.15.3.1](https://github.com/pypa/pipx/tree/0.15.3.1) - [bugfix] Workaround multiprocessing issues on certain platforms (#229) ## [0.15.3.0](https://github.com/pypa/pipx/tree/0.15.3.0) - [feature] Use symlinks on Windows when symlinks are available ## [0.15.2.0](https://github.com/pypa/pipx/tree/0.15.2.0) - [bugfix] Improved error reporting during venv metadata inspection. - [bugfix] Fixed incompatibility with pypy as venv interpreter (#372). - [bugfix] Replaced implicit dependency on setuptools with an explicit dependency on packaging (#339). - [bugfix] Continue reinstalling packages after failure - [bugfix] Hide cursor while pipx runs - [feature] Add environment variable `USE_EMOJI` to allow enabling/disabling emojis (#376) - [refactor] Moved all commands to separate files within the commands module (#255). - [bugfix] Ignore system shared libraries when installing shared libraries pip, wheel, and setuptools. This also fixes an incompatibility with Debian/Ubuntu's version of pip (#386). ## [0.15.1.3](https://github.com/pypa/pipx/tree/0.15.1.3) - [bugfix] On Windows, pipx now lists correct Windows apps (#217) - [bugfix] Fixed a `pipx install` bug causing incorrect python binary to be used when using the optional --python argument in certain situations, such as running pipx from a Framework python on macOS and specifying a non-Framework python. ## [0.15.1.2](https://github.com/pypa/pipx/tree/0.15.1.2) - [bugfix] Fix recursive search of dependencies' apps so no apps are missed. - `upgrade-all` now skips editable packages, because pip disallows upgrading editable packages. ## [0.15.1.1](https://github.com/pypa/pipx/tree/0.15.1.1) - [bugfix] fix regression that caused installing with --editable flag to fail package name determination. ## [0.15.1.0](https://github.com/pypa/pipx/tree/0.15.1.0) - Add Python 3.8 to PyPI classifier and travis test matrix - [feature] auto-upgrade shared libraries, including pip, if older than one month. Hide all pip warnings that a new version is available. (#264) - [bugfix] pass pip arguments to pip when determining package name (#320) ## [0.15.0.0](https://github.com/pypa/pipx/tree/0.15.0.0) Upgrade instructions: When upgrading to 0.15.0.0 or above from a pre-0.15.0.0 version, you must re-install all packages to take advantage of the new persistent pipx metadata files introduced in this release. These metadata files store pip specification values, injected packages, any custom pip arguments, and more in each main package's venv. You can do this by running `pipx reinstall-all` or `pipx uninstall-all`, then reinstalling manually. - `install` now has no `--spec` option. You may specify any valid pip specification for `install`'s main argument. - `inject` will now accept pip specifications for dependency arguments - Metadata is now stored for each application installed, including install options like `--spec`, and injected packages. This information allows upgrade, upgrade-all and reinstall-all to work properly even with non-pypi installed packages. (#222) - `upgrade` options `--spec` and `--include-deps` were removed. Pipx now uses the original options used to install each application instead. (#222) - `upgrade-all` options `--include-deps`, `--system-site-packages`, `--index-url`, `--editable`, and `--pip-args` were removed. Pipx now uses the original options used to install each application instead. (#222) - `reinstall-all` options `--include-deps`, `--system-site-packages`, `--index-url`, `--editable`, and `--pip-args` were removed. Pipx now uses the original options used to install each application instead. (#222) - Handle missing interpreters more gracefully (#146) - Change `reinstall-all` to use system python by default for apps. Now use `--python` option to specify a different python version. - Remove the PYTHONPATH environment variable when executing any command to prevent conflicts between pipx dependencies and package dependencies when pipx is installed via homebrew. Homebrew can use PYTHONPATH manipulation instead of virtual environments. (#233) - Add printed summary after successful call to `pipx inject` - Support associating apps with Python 3.5 - Improvements to animation status text - Make `--python` argument in `reinstall-all` command optional - Use threads on OS's without support for semaphores - Stricter parsing when passing `--` argument as delimiter ## [0.14.0.0](https://github.com/pypa/pipx/tree/0.14.0.0) - Speed up operations by using shared venv for `pip`, `setuptools`, and `wheel`. You can see more detail in the 'how pipx works' section of the documentation. (#164, @pfmoore) - Breaking change: for the `inject` command, change `--include-binaries` to `--include-apps` - Change all terminology from `binary` to `app` or `application` - Improve argument parsing for `pipx run` and `pipx runpip` - If `--force` is passed, remove existing files in PIPX_BIN_DIR - Move animation to start of line, hide cursor when animating ## [0.13.2.3](https://github.com/pypa/pipx/tree/0.13.2.3) - Fix regression when installing a package that doesn't have any entry points ## [0.13.2.2](https://github.com/pypa/pipx/tree/0.13.2.2) - Remove unnecessary and sometimes incorrect check after `pipx inject` (#195) - Make status text/animation reliably disappear before continuing - Update animation symbols ## [0.13.2.1](https://github.com/pypa/pipx/tree/0.13.2.1) - Remove virtual environment if installation did not complete. For example, if it was interrupted by ctrl+c or if an exception occurred for any reason. (#193) ## [0.13.2.0](https://github.com/pypa/pipx/tree/0.13.2.0) - Add shell autocompletion. Also add `pipx completions` command to print instructions on how to add pipx completions to your shell. - Un-deprecate `ensurepath`. Use `userpath` internally instead of instructing users to run the `userpath` cli command. - Improve detection of PIPX_BIN_DIR not being on PATH - Improve error message when an existing symlink exists in PIPX_BIN_DIR and points to the wrong location - Improve handling of unexpected files in PIPX_HOME (@uranusjr) - swap out of order logic in order to correctly recommend --include-deps (@joshuarli) - [dev] Migrate from tox to nox ## [0.13.1.1](https://github.com/pypa/pipx/tree/0.13.1.1) - Do not raise bare exception if no binaries found (#150) - Update pipsi migration script ## [0.13.1.0](https://github.com/pypa/pipx/tree/0.13.1.0) - Deprecate `ensurepath` command. Use `userpath append ~/.local/bin` - Support redirects and proxies when downloading python files (i.e. `pipx run http://url/file.py`) - Use tox for document generation and CI testing (CI tests are now functional rather than static tests on style and formatting!) - Use mkdocs for documentation - Change default cache duration for `pipx run` from 2 to 14 days ## [0.13.0.1](https://github.com/pypa/pipx/tree/0.13.0.1) - Fix upgrade-all and reinstall-all regression ## [0.13.0.0](https://github.com/pypa/pipx/tree/0.13.0.0) - Add `runpip` command to run arbitrary pip commands in pipx-managed virtual environments - Do not raise error when running `pipx install PACKAGE` and the package has already been installed by pipx (#125). This is the cause of the major version change from 0.12 to 0.13. - Add `--skip` argument to `upgrade-all` and `reinstall-all` commands, to let the user skip particular packages ## [0.12.3.3](https://github.com/pypa/pipx/tree/0.12.3.3) - Update logic in determining a package's binaries during installation. This removes spurious binaries from the installation. (#104) - Improve compatibility with Debian distributions by using `shutil.which` instead of `distutils.spawn.find_executable` (#102) ## [0.12.3.2](https://github.com/pypa/pipx/tree/0.12.3.2) - Fix infinite recursion error when installing package such as `cloudtoken==0.1.84` (#103) - Fix windows type errors (#96, #98) ## [0.12.3.1](https://github.com/pypa/pipx/tree/0.12.3.1) - Fix "WindowsPath is not iterable" bug ## [0.12.3.0](https://github.com/pypa/pipx/tree/0.12.3.0) - Add `--include-deps` argument to include binaries of dependent packages when installing with pipx. This improves compatibility with packages that depend on other installed packages, such as `jupyter`. - Speed up `pipx list` output (by running multiple processes in parallel) and by collecting all metadata in a single subprocess call - More aggressive cache directory removal when `--no-cache` is passed to `pipx run` - [dev] Move inline text passed to subprocess calls to their own files to enable autoformatting, linting, unit testing ## [0.12.2.0](https://github.com/pypa/pipx/tree/0.12.2.0) - Add support for PEP 582's `__pypackages__` (experimental). `pipx run BINARY` will first search in `__pypackages__` for binary, then fallback to installing from PyPI. `pipx run --pypackages BINARY` will raise an error if the binary is not found in `__pypackages__`. - Fix regression when installing with `--editable` flag (#93) - [dev] improve unit tests ## [0.12.1.0](https://github.com/pypa/pipx/tree/0.12.1.0) - Cache and reuse temporary Virtual Environments created with `pipx run` (#61) - Update binary discovery logic to find "scripts" like awscli (#91) - Forward `--pip-args` to the pip upgrade command (previously the args were forwarded to install/upgrade commands for packages) (#77) - When using environment variable PIPX_HOME, Virtual Environments will now be created at `$PIPX_HOME/venvs` rather than at `$PIPX_HOME`. - [dev] refactor into multiple files, add more unit tests ## [0.12.0.4](https://github.com/pypa/pipx/tree/0.12.0.4) - Fix parsing bug in pipx run ## [0.12.0.3](https://github.com/pypa/pipx/tree/0.12.0.3) - list python2 as supported language so that pip installs with python2 will no longer install the pipx on PyPI from the original pipx owner. Running pipx with python2 will fail, but at least it will not be as confusing as running the pipx package from the original owner. ## [0.12.0.2](https://github.com/pypa/pipx/tree/0.12.0.2) - forward arguments to run command correctly #90 ## [0.12.0.1](https://github.com/pypa/pipx/tree/0.12.0.1) - stop using unverified context #89 ## [0.12.0.0](https://github.com/pypa/pipx/tree/0.12.0.0) - Change installation instructions to use `pipx` PyPI name - Add `ensurepath` command ## [0.11.0.2](https://github.com/pypa/pipx/tree/0.11.0.2) - add version argument parsing back in (fixes regression) ## [0.11.0.1](https://github.com/pypa/pipx/tree/0.11.0.1) - add version check, command check, fix printed version update installation instructions ## [0.11.0.0](https://github.com/pypa/pipx/tree/0.11.0.0) - Replace `pipx BINARY` with `pipx run BINARY` to run a binary in an ephemeral environment. This is a breaking API change so the major version has been incremented. (Issue #69) - upgrade pip when upgrading packages (Issue #72) - support --system-site-packages flag (Issue #64) ## [0.10.4.1](https://github.com/pypa/pipx/tree/0.10.4.1) - Fix version printed when `pipx --version` is run ## [0.10.4.0](https://github.com/pypa/pipx/tree/0.10.4.0) - Add --index-url, --editable, and --pip-args flags - Updated README with pipsi migration instructions ## [0.10.3.0](https://github.com/pypa/pipx/tree/0.10.3.0) - Display python version in list - Do not reinstall package if already installed (added `--force` flag to override) - When upgrading all packages, print message only when package is updated - Avoid accidental execution of pipx.**main** pipx-1.15.0/docs/contributing.md000066400000000000000000000251121521706623000165420ustar00rootroot00000000000000--- hide: - navigation --- Thanks for your interest in contributing to pipx! Everyone who interacts with the pipx project via codebase, issue tracker, chat rooms, or otherwise is expected to follow the [PSF Code of Conduct](https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md). ## Submitting changes 1. Fork [the GitHub repository](https://github.com/pypa/pipx). 1. Make a branch off of `main` and commit your changes to it. 1. Add a changelog entry. 1. Submit a pull request to the `main` branch on GitHub, referencing an open issue. ### Changelog entries The `CHANGELOG.md` file is built by [towncrier](https://pypi.org/project/towncrier/) from news fragments in the `changelog.d/` directory. To add an entry, create a news fragment in that directory named `{number}.{type}.md`, where `{number}` is the issue number, and `{type}` is one of `feature`, `bugfix`, `doc`, `removal`, or `misc`. For example, if your issue number is 1234 and it's fixing a bug, then you would create `changelog.d/1234.bugfix.md`. PRs can span multiple categories by creating multiple files: if you added a feature and deprecated/removed an old feature for issue #5678, you would create `changelog.d/5678.feature.md` and `changelog.d/5678.removal.md`. A changelog entry is meant for end users and should only contain details relevant to them. In order to maintain a consistent style, please keep the entry to the point, in sentence case, shorter than 80 characters, and in an imperative tone. An entry should complete the sentence "This change will ...". If one line is not enough, use a summary line in an imperative tone, followed by a description of the change in one or more paragraphs, each wrapped at 80 characters and separated by blank lines. You don't need to reference the pull request or issue number in a changelog entry, since towncrier will add a link using the number in the file name. Similarly, you don't need to add your name to the entry, since that will be associated with the pull request. ## Codebase Architecture All source code lives under `src/pipx/`. The CLI entry point is `main.py`, which dispatches to command modules in `commands/`. ```mermaid flowchart TD MAIN["main.py
CLI parser + dispatch"] --> CMDS["commands/"] CMDS --> INSTALL["install.py"] CMDS --> RUN["run.py"] CMDS --> UPGRADE["upgrade.py"] CMDS --> INJECT["inject.py"] CMDS --> LIST["list_packages.py"] CMDS --> PIN["pin.py"] CMDS --> INTERP_CMD["interpreter.py"] CMDS --> OTHER["uninstall, uninject,
reinstall, ensure_path,
environment, run_pip"] MAIN --> VENV["venv.py
Venv + VenvContainer"] MAIN --> PATHS["paths.py
PIPX_HOME resolution"] VENV --> SHARED["shared_libs.py
shared pip venv"] VENV --> INSPECT["venv_inspect.py
metadata reader"] VENV --> META["pipx_metadata_file.py
JSON persistence"] RUN --> SPEC["package_specifier.py
PEP 508 parsing"] RUN --> INTERP["interpreter.py
Python resolution"] INTERP --> STANDALONE["standalone_python.py
download builds"] style MAIN fill:#3f72af,color:#fff style CMDS fill:#2a9d8f,color:#fff style INSTALL fill:#2a9d8f,color:#fff style RUN fill:#2a9d8f,color:#fff style UPGRADE fill:#2a9d8f,color:#fff style INJECT fill:#2a9d8f,color:#fff style LIST fill:#2a9d8f,color:#fff style PIN fill:#2a9d8f,color:#fff style INTERP_CMD fill:#2a9d8f,color:#fff style OTHER fill:#2a9d8f,color:#fff style VENV fill:#7c4dff,color:#fff style PATHS fill:#c78c20,color:#fff style SHARED fill:#c78c20,color:#fff style INSPECT fill:#7c4dff,color:#fff style META fill:#7c4dff,color:#fff style SPEC fill:#388e3c,color:#fff style INTERP fill:#388e3c,color:#fff style STANDALONE fill:#388e3c,color:#fff ``` ### Key modules `venv.py` is the core abstraction. `Venv` wraps a single virtual environment (create, install, uninstall, upgrade) and `VenvContainer` manages the collection under `PIPX_HOME/venvs/`. Both delegate pip operations to the shared libraries venv managed by `shared_libs.py`. `paths.py` resolves all directory locations from environment variables, platform defaults, and legacy fallback paths. `pipx_metadata_file.py` serializes install options (spec, pip args, injected packages) to JSON inside each venv so that `upgrade` and `reinstall` can reproduce the original install. `interpreter.py` and `standalone_python.py` handle Python version resolution. When `--fetch-python` is `missing` or `always`, pipx downloads a standalone build from [python-build-standalone](https://github.com/astral-sh/python-build-standalone/releases) and caches it locally. The `pipx interpreter` subcommands (list, prune, upgrade) manage these cached interpreters. `commands/run.py` includes PEP 723 inline script metadata parsing. When you run a `.py` file, pipx scans for a `# /// script` block, extracts TOML-declared dependencies, and installs them into a cached temporary venv. ## Running pipx For Development To develop `pipx`, either create a [developer environment](#creating-a-developer-environment), or perform an editable install: ``` python -m pip install -e . python -m pipx --version ``` ## Running Tests ### Setup pipx uses [tox](https://pypi.org/project/tox/) for development, continuous integration testing, and various tasks. `tox` defines environments in `tox.toml` which can be run with `tox run -e ENV_NAME`. Environment names can be listed with `tox list`. Install tox for pipx development: ``` python -m pip install --user tox ``` Tests are defined as `tox` environments. You can see all tox environments with ``` tox list ``` At the time of this writing, the output looks like this ``` default environments: 3.13 -> run tests with 3.13 3.12 -> run tests with 3.12 3.11 -> run tests with 3.11 3.10 -> run tests with 3.10 lint -> run pre-commit on the codebase docs -> build documentation man -> build man page ``` ### Creating a developer environment For developing the tool (and to attach to your IDE) we recommend creating a Python environment via `tox run -e dev`, afterwards use the Python interpreter available under `.tox/dev/bin/python`. ### Unit Tests To run unit tests in Python 3.12, you can run ``` tox run -e 3.12 ``` > [!TIP] > You can run a specific unit test by passing arguments to pytest, the test runner pipx uses: > > ``` > tox run -e 3.10 -- -k EXPRESSION > ``` > > `EXPRESSION` can be a test name, such as > > ``` > tox run -e 3.10 -- -k test_uninstall > ``` > > Coverage errors can usually be ignored when only running a subset of tests. ### Running Unit Tests Offline Running the unit tests requires a directory `.pipx_tests/package_cache` to be populated from a fixed list of package distribution files (wheels or source files). If you have network access, `tox` automatically makes sure this directory is populated (including downloading files if necessary) as a first step. Thus, if you are running the tests with network access, you can ignore the rest of this section. If, however, you wish to run tests offline without the need for network access, you can populate `.pipx_tests/package_cache` yourself manually beforehand when you do have network access. ### Lint Tests Linting is done via `pre-commit`, setting it up and running it can be done via `tox` by typing: ``` tox run -e lint ``` ### Installing or injecting new packages in tests If the tests are modified such that a new package / version combination is `pipx install`ed or `pipx inject`ed that wasn't used in other tests, then one must make sure it's added properly to the packages lists in `testdata/tests_packages`. To accomplish this: - Edit `testdata/tests_packages/primary_packages.txt` to add the new package(s) that you wish to `pipx install` or `pipx inject` in the tests. Then using Github workflows to generate all platforms in the Github CI: - Manually activate the Github workflow: Create tests package lists for offline tests - Download the artifact `lists` and put the files from it into `testdata/tests_packages/` Finally, check-in the new or modified list files in the directory `testdata/tests_packages` ## Testing pipx on Continuous Integration builds Upon opening pull requests GitHub Actions will automatically trigger. ## Building Documentation `pipx` autogenerates API documentation, and also uses templates. When updating pipx docs, make sure you are modifying the `docs` directory. You can generate the documentation with ``` tox run -e docs ``` This will capture CLI documentation for any pipx argument modifications, as well as generate templates to the docs directory. ## Releasing New `pipx` Versions The release process for pipx is designed to be simple and fully automated with a single button press. The workflow automatically determines the next version based on changelog fragments, generates the changelog, creates the release commit, and publishes to PyPI. ### Initiating a Release Navigate to the **Actions** tab in the GitHub repository and select the **Pre-release** workflow. Click **Run workflow** and choose the appropriate version bump strategy. The `auto` option intelligently determines whether a minor or patch bump is needed by examining the types of changelog fragments present. If new features or removals exist, it performs a minor version bump; otherwise, it increments the patch version. Alternatively, you can explicitly select `major`, `minor`, or `patch` to control the version increment directly. ### What Happens During Release Once triggered, the pre-release workflow executes the `scripts/release.py` script which collects all changelog fragments from the `changelog.d/` directory and uses towncrier to generate the updated changelog. It then creates a release commit with the message "Release {version}" and tags it with the version number. After running pre-commit hooks to ensure formatting, both the commit and tag are pushed to the main branch. The act of pushing a version tag (matching the pattern `*.*.*`) automatically triggers the main release workflow. This workflow builds the project distribution files, publishes the package to PyPI using trusted publishing, creates a GitHub release with auto-generated notes, and builds the zipapp using the minimum supported Python version before uploading it to the GitHub release assets. ### Version Calculation Examples Starting from version `1.8.0`, the version bump types produce the following results: `auto` with feature fragments becomes `1.9.0`, while `auto` with only bugfixes becomes `1.8.1`. Selecting `major` explicitly jumps to `2.0.0`, `minor` moves to `1.9.0`, and `patch` increments to `1.8.1`. This automation eliminates the need for manual version management and ensures consistency across releases. pipx-1.15.0/docs/explanation/000077500000000000000000000000001521706623000160325ustar00rootroot00000000000000pipx-1.15.0/docs/explanation/comparisons.md000066400000000000000000000350601521706623000207150ustar00rootroot00000000000000## pipx vs pip - pip is a general Python package installer. It can be used to install libraries or CLI applications with entrypoints. - pipx is a specialized package installer. It can only be used to install packages with CLI entrypoints. - pipx and pip both install packages from PyPI (or locally) - pipx relies on pip (and venv) - pipx replaces a subset of pip's functionality; it lets you install CLI applications but NOT libraries that you import in your code. - you can install pipx with pip Example interaction: Install pipx with pip: `pip install --user pipx` ## pipx vs uv tool Both [`uv tool`](https://docs.astral.sh/uv/concepts/tools/) and pipx install a Python tool into its own virtual environment and expose the tool's console scripts on `PATH`. Both have a one-shot run mode (`uvx` and `pipx run`). `uvx` is `uv tool run`. They differ in where state lives, which extra commands they ship, and how they handle managed Python. pipx keeps the same CLI across pip and uv backends; `pipx install pipx[uv]` opts you into uv-speed venv creation without changing any commands. `uv tool` ships a smaller per-tool surface, then reuses the rest of `uv` for free: managed Python, content-addressed cache, lockfiles, PEP 723 script handling. ### Where state lives | | pipx | uv tool | | ---------------------- | ---------------------------------------------- | ------------------------------------------ | | Per-tool venvs | `$PIPX_HOME/venvs/` (`PIPX_HOME`) | `$UV_TOOL_DIR/` (`UV_TOOL_DIR`) | | Exposed binaries | `$PIPX_BIN_DIR` (default `~/.local/bin`) | `$UV_TOOL_BIN_DIR` (default same) | | Man pages | `$PIPX_MAN_DIR` (default `~/.local/share/man`) | _not exposed_ | | Shared pip/setup/wheel | `$PIPX_HOME/shared` (pip-backend only) | _none; uv venvs ship without pip_ | | Ephemeral run cache | `$PIPX_HOME/.cache` (TTL 14 days) | `$UV_CACHE_DIR` (no TTL; `uv cache prune`) | | Standalone Python | `$PIPX_HOME/py` (`PIPX_FETCH_PYTHON`) | `$UV_PYTHON_INSTALL_DIR` | | System-wide install | `--global`, `PIPX_GLOBAL_*` | _not supported_ | `PIPX_BIN_DIR` and `UV_TOOL_BIN_DIR` both default to `~/.local/bin` on Unix, so installing the same tool with both managers writes the same filename. Each manager refuses to overwrite a binary the other one wrote without `--force`. Use `pipx install --suffix=...` to keep two copies side-by-side; uv has no equivalent. ### Subcommand mapping | Task | pipx | uv tool | | ------------------------------ | ------------------------------------------------- | ------------------------------------------------------------------- | | Install from PyPI | `pipx install ruff` | `uv tool install ruff` (or `uvx ruff` for one-off) | | Install from a git URL | `pipx install 'git+https://โ€ฆ'` | `uv tool install 'git+https://โ€ฆ'` | | Install editable from path | `pipx install -e ./mypkg` | `uv tool install -e ./mypkg` | | One-off run (no install) | `pipx run black .` | `uvx black .` | | One-off run with extra dep | _no clean equivalent_ | `uvx --with mkdocs-material mkdocs` | | Pinned-version one-off | `pipx run --spec 'ruff==0.6.0' ruff check` | `uvx ruff@0.6.0 check` | | Add a dep to existing tool | `pipx inject mkdocs mkdocs-material` | `uv tool install mkdocs --with mkdocs-material` (rebuilds) | | Remove an injected dep | `pipx uninject mkdocs mkdocs-material` | _rebuild without `--with`_ | | Upgrade one | `pipx upgrade ruff` | `uv tool upgrade ruff` | | Upgrade all | `pipx upgrade-all` | `uv tool upgrade --all` | | List installed | `pipx list` | `uv tool list` (`--show-with`, `--outdated`, โ€ฆ) | | Reinstall (e.g. after Py bump) | `pipx reinstall ruff` / `reinstall-all` | `uv tool upgrade --reinstall ruff` / `--all -p 3.13` | | Run pip inside a venv | `pipx runpip -- pip ...` | _not supported (no pip in uv venvs)_ | | PATH setup | `pipx ensurepath` | `uv tool update-shell` | | Show resolved env | `pipx environment` | `uv tool dir`, `uv tool dir --bin`, `uv cache dir`, `uv python dir` | | PEP 723 inline script | `pipx run script.py` (with uv backend โ†’ `uv run`) | `uv run --script script.py` | ### Only in pipx - `pipx inject` / `uninject` add or remove a package in place. `uv tool install --with` reaches the same end state by rebuilding the venv. - `pipx runpip -- ...` runs pip inside a tool's venv. uv venvs have no pip. - `--include-deps` exposes entry points from every dependency. uv requires you to enumerate dep packages with `--with-executables-from`. - `--suffix` keeps two copies of the same tool side-by-side. - `--global` and the `PIPX_GLOBAL_*` variables drive a system-wide install. - Manual pages get symlinked under `$PIPX_MAN_DIR`. - `pipx install-all ` rebuilds every venv from a `pipx list --json` snapshot for cross-machine migration. - `[project.entry-points."pipx.run"]` declares pipx-specific runtime extras in the package metadata. - `pipx environment` prints every variable and its resolved value in one place. ### Only in uv tool - `uv tool list --outdated` queries newer versions without upgrading. - `uv tool list` toggles columns via `--show-with`, `--show-paths`, `--show-version-specifiers`, `--show-extras`, `--show-python`. - `uvx --with-editable PATH` adds editable extras for a one-off run. - `uv tool upgrade --all -p 3.13` re-pins every tool to a different Python in one shot. - `uv python install/list/find/pin/upgrade/uninstall` integrates managed Python; uv auto-fetches when the requested Python isn't installed. - `--exclude-newer`, `--torch-backend`, and `--isolated` add reproducibility knobs that pipx forwards but doesn't add on its own. - The content-addressed cache spans `uv pip`, `uv tool`, `uv run`, and `uv venv`. Wheels downloaded once get reused everywhere. ### Gotchas - `uvx` reuses cached envs across invocations until you prune the cache (`uv cache clean`), pin a new version (`uvx black@latest`), or pass `--refresh`. `pipx run` also caches, but the temp venv expires after 14 days idle. - `uvx` prefers a persistent install when one exists. After `uv tool install ruff`, plain `uvx ruff` reuses that env instead of building an ephemeral one. Pass `--isolated` to bypass. - `uv tool` ignores project-local `.python-version` files. `uv run` honors them; tool envs do not. pipx never reads them; pass `--python` or set `PIPX_DEFAULT_PYTHON`. - `uv python upgrade` only bumps patch versions. To move a tool from 3.12 to 3.13 run `uv tool upgrade --all -p 3.13`. pipx's equivalent is `reinstall-all --python python3.13`. - `uv run --script` needs a real on-disk path. When `pipx run script.py` content arrives via URL or named pipe, the uv backend falls back to building a venv. ### Picking one Pipx wins when you need its tool-specific extras: `inject`/`uninject`, `--global`, `--suffix`, manual pages, or `pipx install-all` for migration. Install `pipx[uv]` to keep that surface and pick up uv-speed venv creation. Reach for `uv tool` when you already drive uv for managed Python or `uv run --script` and want one binary for everything. Running both is fine; the only collision point is the shared bin dir, and both sides refuse to overwrite without `--force`. ## pipx vs poetry and pipenv - pipx is used solely for application consumption: you install CLI apps with it - pipenv and poetry are CLI apps used to develop applications and libraries - all three tools wrap pip and virtual environments for more convenient workflows Example interaction: Install pipenv and poetry with pipx: `pipx install poetry` Run pipenv or poetry with pipx: `pipx run poetry --help` ## pipx vs venv - venv is part of Python's standard library in Python 3.2 and above - venv creates "virtual environments" which are sandboxed python installations - pipx heavily relies on the venv package Example interaction: pipx installs packages to environments created with venv. `pipx install black --verbose` ## pipx vs pyenv - pyenv manages python versions on your system. It helps you install versions like Python 3.6, 3.7, etc. - pipx installs packages in virtual environments and exposes their entrypoints on your PATH Example interaction: Install a Python interpreter with pyenv, then install a package using pipx and that new interpreter: `pipx install black --python=python3.11` where python3.11 was installed on the system with pyenv ## pipx vs pipsi - pipx and pipsi both install packages in a similar way - pipx is under active development. pipsi is no longer maintained. - pipx always makes sure you're using the latest version of pip - pipx has the ability to run an app in one line, leaving your system unchanged after it finishes (`pipx run APP`) where pipsi does not - pipx has the ability to recursively install binaries from dependent packages - pipx adds more useful information to its output - pipx has more CLI options such as upgrade-all, reinstall-all, uninstall-all - pipx is more modern. It uses Python 3.6+, and the `venv` package in the Python3 standard library instead of the python 2 package `virtualenv`. - pipx works with Python homebrew installations while pipsi does not (at least on my machine) - pipx defaults to less verbose output - pipx allows you to see each command it runs by passing the --verbose flag - pipx prints emojis ๐Ÿ˜€ Example interaction: None. Either one or the other should be used. These tools compete for a similar workflow. ### Migrating to pipx from pipsi After you have installed pipx, run [migrate_pipsi_to_pipx.py](https://raw.githubusercontent.com/pypa/pipx/main/scripts/migrate_pipsi_to_pipx.py). Why not do this with your new pipx installation? ``` pipx run https://raw.githubusercontent.com/pypa/pipx/main/scripts/migrate_pipsi_to_pipx.py ``` ## pipx vs brew - Both brew and pipx install cli tools - They install them from different sources. brew uses a curated repository specifically for brew, and pipx generally uses PyPI. Example interaction: brew can be used to install pipx, but they generally don't interact much. ## pipx vs npx - Both can run cli tools (npx will search for them in node_modules, and if not found run in a temporary environment. `pipx run` will search in `__pypackages__` and if not found run in a temporary environment) - npx works with JavaScript and pipx works with Python - Both tools attempt to make running executables written in a dynamic language (JS/Python) as easy as possible - pipx can also install tools globally; npx cannot Example interaction: None. These tools work for different languages. ## pipx vs pip-run [pip-run](https://github.com/jaraco/pip-run) is focused on running **arbitrary Python code in ephemeral environments** while pipx is focused on running **Python binaries in ephemeral and non-ephemeral environments**. For example these two commands both install poetry to an ephemeral environment and invoke poetry with `--help`. ``` pipx run poetry --help pip-run poetry -- -m poetry --help ``` Example interaction: None. ## pipx vs fades [fades](https://github.com/PyAr/fades) is a tool to run **individual** Python scripts inside automatically provisioned virtualenvs with their dependencies installed. - Both [fades](https://github.com/PyAr/fades#how-to-mark-the-dependencies-to-be-installed) and [pipx run](../reference/examples.md#pipx-run-examples) allow specifying a script's dependencies in specially formatted comments, but the exact syntax differs. (pipx's syntax is standardized by a [provisional specification](https://packaging.python.org/en/latest/specifications/inline-script-metadata/), fades's syntax is not standardized.) - Both tools automatically set up reusable virtualenvs containing the necessary dependencies. - Both can download Python scripts/packages to execute from remote resources. - fades can only run individual script files while pipx can also run packages. Example interaction: None. ## pipx vs pae/pactivate _pae_ is a Bash command-line function distributed with [pactivate](https://github.com/cynic-net/pactivate) that uses pactivate to create non-ephemeral environments focused on general use, rather than just running command-line applications. There is [a very detailed comparison here](https://github.com/cynic-net/pactivate/blob/main/doc/vs-pipx.md), but to briefly summarize: Similarities: - Both create isolated environments without having to specify (and remember) a directory in which to store them. - Both allow you to use any Python interpreter available on your system (subject to version restrictions below). pae advantages: - Supports all versions of Python from 2.7 upward. pipx requires โ‰ฅ3.10. - Fewer dependencies. (See the detailed comparison for more information.) - Easier to have multiple versions of a single program and/or use different Python versions for a single program. - Somewhat more convenient for running arbitrary command-line programs in virtual environments, installing multiple packages in a single environment, and activating virtual environments. - Integrates well with source code repos using [pactivate](https://github.com/cynic-net/pactivate). pae disadvantages: - Usable with Bash shell only. - Slightly less quick and convenient for installing/running command-line programs from single Python packages. - Can be slower than pipx at creating virtual environments. Example interaction: None. Either one or the other should be used. These tools compete for a similar workflow. pipx-1.15.0/docs/explanation/how-pipx-works.md000066400000000000000000000112401521706623000212700ustar00rootroot00000000000000## How it Works ### `pipx install` When installing a package and its binaries on Linux (`pipx install package`), pipx first creates or reuses a shared virtual environment at `~/.local/share/pipx/shared/` that contains the packaging library `pip`, ensuring it is updated to its latest version. It then creates an isolated virtual environment at `~/.local/share/pipx/venvs/PACKAGE` that uses the shared pip via a [.pth file](https://docs.python.org/3/library/site.html) and installs the desired package into it. Once the package is installed, pipx exposes its console scripts and GUI scripts by symlinking them into `~/.local/bin` (for example, `~/.local/bin/black` -> `~/.local/share/pipx/venvs/black/bin/black`). It also symlinks any manual pages into `~/.local/share/man/man[1-9]`. As long as `~/.local/bin/` is on your `PATH`, the new commands are available globally, and on systems with `man` support the pages are accessible too. Adding the `--global` flag to any `pipx` command executes the action in global scope, exposing the app to all system users. See the [configuration reference](../how-to/configure-paths.md#-global-argument) for details. Note that this is not available on Windows. ```mermaid flowchart LR A["pipx install black"] --> B["shared venv
(pip)"] B --> C["create venv
~/.local/share/pipx/
venvs/black/"] C --> D["pip install black"] D --> E["symlink binaries
~/.local/bin/black"] D --> F["symlink man pages
~/.local/share/man/"] style A fill:#3f72af,color:#fff style B fill:#2a9d8f,color:#fff style C fill:#2a9d8f,color:#fff style D fill:#2a9d8f,color:#fff style E fill:#388e3c,color:#fff style F fill:#388e3c,color:#fff ``` ### `pipx run` `pipx run BINARY` reuses the same shared pip environment, then either reuses a cached virtual environment or creates a new temporary one. The cache key is a hash of the package name, spec, python version, and pip arguments. pipx creates a virtual environment with `python -m venv`, installs the package, and invokes the binary. Cached environments expire after a few days. On next run, pipx fetches the latest version. ```mermaid flowchart LR A["pipx run pycowsay"] --> B["shared venv
(pip)"] B --> C{"cached
venv?"} C -- "yes" --> E["reuse cached venv"] C -- "no" --> D["create temp venv
pip install pycowsay"] D --> F["invoke binary"] E --> F style A fill:#3f72af,color:#fff style B fill:#2a9d8f,color:#fff style C fill:#c78c20,color:#fff style D fill:#2a9d8f,color:#fff style E fill:#2a9d8f,color:#fff style F fill:#388e3c,color:#fff ``` ### Directory layout The overall directory structure that pipx manages looks like this: ```mermaid flowchart TD HOME["~"] --> BIN["~/.local/bin/
(on PATH)"] HOME --> DATA["~/.local/share/pipx/"] DATA --> SHARED["shared/
(pip, setuptools)"] DATA --> VENVS["venvs/"] VENVS --> V1["black/"] VENVS --> V2["poetry/"] VENVS --> V3["ruff/"] V1 --> V1BIN["bin/black"] BIN --> |"symlink"| V1BIN style HOME fill:#3f72af,color:#fff style BIN fill:#388e3c,color:#fff style DATA fill:#2a9d8f,color:#fff style SHARED fill:#c78c20,color:#fff style VENVS fill:#2a9d8f,color:#fff style V1 fill:#7c4dff,color:#fff style V2 fill:#7c4dff,color:#fff style V3 fill:#7c4dff,color:#fff style V1BIN fill:#7c4dff,color:#fff ``` You can do all of this yourself. pipx automates it. Pass `--verbose` to see every command and argument pipx runs. ### Resolving the Python interpreter When you pass `--python `, pipx walks a fixed search order: 1. Treat the argument as a literal path; use it when the file exists. 1. Call `shutil.which()` to find a matching command on `PATH`. 1. On non-Windows, try `python` derived from the version string. 1. If `py` is available (Windows), ask the launcher for that version. Only after every step fails does pipx consider downloading a build from [python-build-standalone](https://github.com/astral-sh/python-build-standalone). The `--fetch-python` flag (or `PIPX_FETCH_PYTHON`) decides when that download is allowed: - `never` (the default) keeps pipx local: the search above must succeed or pipx errors out. - `missing` falls back to a download when every step above fails. - `always` skips the search and uses a fresh standalone build for the requested version. Use `always` when the system Python is patched in ways that break installed applications. Some distros strip optional modules (`tkinter`, `lzma`), change the site-packages layout, or backport behaviour changes; a found system Python is worse than no Python at all. Otherwise, use `missing` so pipx reuses what the system already provides. pipx-1.15.0/docs/explanation/index.md000066400000000000000000000006741521706623000174720ustar00rootroot00000000000000# Explanation Design rationale and background knowledge. Read these to understand *why* pipx works the way it does. - [How pipx Works](how-pipx-works.md) โ€” virtual environment architecture and the shared pip library. - [Comparisons](comparisons.md) โ€” how pipx relates to pip, poetry, pipenv, pyenv, brew, npx, and more. - [Making Packages Compatible](making-packages-compatible.md) โ€” adding entry points and man pages for pipx support. pipx-1.15.0/docs/explanation/making-packages-compatible.md000066400000000000000000000105771521706623000235250ustar00rootroot00000000000000## Developing for pipx If you are a developer and want to be able to run ``` pipx install MY_PACKAGE ``` make sure you include `scripts` in your main table[^1] in `pyproject.toml` or its legacy equivalents for `setup.cfg` and `setup.py`. pipx also exposes `gui-scripts` entry points, which are useful for GUI applications on Windows (they launch without opening a console window). === "pyproject.toml" ```ini [project.scripts] foo = "my_package.some_module:main_func" bar = "other_module:some_func" [project.gui-scripts] baz = "my_package_gui:start_func" ``` === "setup.cfg" ```ini [options.entry_points] console_scripts = foo = my_package.some_module:main_func bar = other_module:some_func gui_scripts = baz = my_package_gui:start_func ``` === "setup.py" ```python setup( # other arguments here... entry_points={ 'console_scripts': [ 'foo = my_package.some_module:main_func', 'bar = other_module:some_func', ], 'gui_scripts': [ 'baz = my_package_gui:start_func', ] }, ) ``` In this case `foo` and `bar` (and `baz` on Windows) would be available as "applications" to pipx after installing the above example package, invoking their corresponding entry point functions. ```mermaid flowchart LR TOML["pyproject.toml
[project.scripts]"] --> |"defines"| EP["entry points
foo, bar"] EP --> |"pipx install"| VENV["isolated venv"] VENV --> |"symlinks"| BIN["~/.local/bin/
foo, bar"] BIN --> |"invokes"| FUNC["my_package:
main_func()"] style TOML fill:#3f72af,color:#fff style EP fill:#2a9d8f,color:#fff style VENV fill:#7c4dff,color:#fff style BIN fill:#388e3c,color:#fff style FUNC fill:#c78c20,color:#fff ``` ### The `pipx.run` entry point group When a user runs `pipx run PACKAGE`, pipx looks for a console script matching the package name. If the package name and script name differ, the user has to write `pipx run --spec PACKAGE SCRIPT`, which is less convenient. Package authors can declare a `pipx.run` entry point group to tell pipx which function to invoke for `pipx run`. This entry point takes priority over console scripts when present. === "pyproject.toml" ```toml [project.entry-points."pipx.run"] my-package = "my_package.cli:main" ``` === "setup.cfg" ```ini [options.entry_points] pipx.run = my-package = my_package.cli:main ``` With this declaration, `pipx run my-package` invokes `my_package.cli:main` even if no console script named `my-package` exists. The [build](https://github.com/pypa/build) package uses this pattern so that `pipx run build` works even though build's console script is named `pyproject-build`. ### Manual pages If you wish to provide documentation via `man` pages on UNIX-like systems then these can be added as data files: === "setuptools" ```toml title="pyproject.toml" [tool.setuptools.data-files] "share/man/man1" = [ "manpage.1", ] ``` ```ini title="setup.cfg" [options.data_files] share/man/man1 = manpage.1 ``` ```python title="setup.py" setup( # other arguments here... data_files=[('share/man/man1', ['manpage.1'])] ) ``` > [!WARNING] > The `data-files` keyword is "discouraged" in the > [setuptools documentation](https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration) > but there is no alternative if `man` pages are a requirement. === "pdm-backend" ```toml title="pyproject.toml" [tool.pdm.build] source-includes = ["share"] [tool.pdm.build.wheel-data] data = [ { path = "share/man/man1/*", relative-to = "." }, ] ``` In this case the manual page `manpage.1` could be accessed by the user after installing the above example package. For a real-world example, see [pycowsay](https://github.com/cs01/pycowsay/blob/master/setup.py)'s `setup.py` source code. You can read more about entry points [here](https://setuptools.pypa.io/en/latest/userguide/quickstart.html#entry-points-and-automatic-script-creation). [^1]: This is often the `[project]` table, but might also be differently named. Read more in the [PyPUG](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#writing-your-pyproject-toml). pipx-1.15.0/docs/how-to/000077500000000000000000000000001521706623000147255ustar00rootroot00000000000000pipx-1.15.0/docs/how-to/configure-paths.md000066400000000000000000000126531521706623000203540ustar00rootroot00000000000000## Installation Options The default binary location for pipx-installed apps is `~/.local/bin`. This can be overridden with the environment variable `PIPX_BIN_DIR`. The default manual page location for pipx-installed apps is `~/.local/share/man`. This can be overridden with the environment variable `PIPX_MAN_DIR`. If the `--global` option is used, the default locations are `/usr/local/bin` and `/usr/local/share/man` respectively and can be overridden with `PIPX_GLOBAL_BIN_DIR` and `PIPX_GLOBAL_MAN_DIR`. pipx's default virtual environment location is typically `~/.local/share/pipx` on Linux/Unix, `~/.local/pipx` on macOS and `~\pipx` on Windows. For compatibility reasons, if `~/.local/pipx` on Linux, `%USERPROFILE%\AppData\Local\pipx` or `~\.local\pipx` on Windows or `~/Library/Application Support/pipx` on macOS exists, it will be used as the default location instead. This can be overridden with the `PIPX_HOME` environment variable. If the `--global` option is used, the default location is always `/opt/pipx` and can be overridden with `PIPX_GLOBAL_HOME`. In case one of these fallback locations exist, we recommend either manually moving the pipx files to the new default location (see the [Moving your pipx installation](move-installation.md) section of the docs), or setting the `PIPX_HOME` environment variable (discarding files existing in the fallback location). As an example, you can install global apps accessible by all users on your system with either of the following commands (on MacOS, Linux, and Windows WSL): ``` sudo PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin PIPX_MAN_DIR=/usr/local/share/man pipx install # or shorter (with pipx>=1.5.0) sudo pipx install --global ``` > [!NOTE] > After version 1.2.0, the default pipx paths have been moved from `~/.local/pipx` to specific user data directories on > each platform using [platformdirs](https://pypi.org/project/platformdirs/) library > > | Old Path | New Path | > | ---------------------- | ------------------------------------------ | > | `~/.local/pipx/.trash` | `platformdirs.user_data_dir()/pipx/trash` | > | `~/.local/pipx/shared` | `platformdirs.user_data_dir()/pipx/shared` | > | `~/.local/pipx/venvs` | `platformdirs.user_data_dir()/pipx/venvs` | > | `~/.local/pipx/.cache` | `platformdirs.user_cache_dir()/pipx` | > | `~/.local/pipx/logs` | `platformdirs.user_log_dir()/pipx/log` | > > `user_data_dir()`, `user_cache_dir()` and `user_log_dir()` resolve to appropriate platform-specific user data, cache > and log directories. See the > [platformdirs documentation](https://platformdirs.readthedocs.io/en/latest/api.html#platforms) for details. > > This was reverted in 1.5.0 for Windows and macOS. We heavily recommend not using these locations on Windows and macOS > anymore, due to multiple incompatibilities discovered with these locations, documented > [here](troubleshoot.md#why-are-spaces-in-the-pipx_home-path-bad). ### Customising your installation #### `--global` argument The `--global` flag installs applications into a system-wide location accessible to all users. It must be placed **after** the subcommand, not before it: ``` # correct sudo pipx install --global pycowsay sudo pipx list --global # wrong (--global is silently ignored when placed before the subcommand) sudo pipx --global install pycowsay ``` Default global paths are `/usr/local/bin` for binaries, `/usr/local/share/man` for man pages, and `/opt/pipx` for virtual environments. Override them with `PIPX_GLOBAL_BIN_DIR`, `PIPX_GLOBAL_MAN_DIR`, and `PIPX_GLOBAL_HOME`. Run `sudo pipx ensurepath --global` to add the global binary directory to the system `PATH`. The `--global` flag is not supported on Windows. #### `--prepend` argument The `--prepend` argument can be passed to the `pipx ensurepath` command to prepend the `pipx` bin directory to the user's PATH environment variable instead of appending to it. This can be useful if you want to prioritise `pipx`-installed binaries over system-installed binaries of the same name. #### `--dry-run` argument The `--dry-run` argument can be passed to the `pipx ensurepath` command to report which directories would be added to your PATH without modifying PATH or any shell configuration file. This lets you preview the changes before running `pipx ensurepath` for real. ### Configuring pip for pipx pipx uses pip internally for all package installs, including its own shared libraries (pip, setuptools). To point pip at a private index or pass custom options, set `PIP_*` environment variables. pipx forwards them to every pip invocation. For example, to use a private package index: ``` export PIP_INDEX_URL=https://my-private-index.example.com/simple/ export PIP_TRUSTED_HOST=my-private-index.example.com pipx install my-private-package ``` These variables also apply when pipx upgrades its shared libraries (`pipx upgrade-shared`). You can set them permanently in your shell profile or in pip's own config file (`pip.conf` / `pip.ini`). See the [pip configuration documentation](https://pip.pypa.io/en/stable/topics/configuration/) for details. Set `PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE=1` to skip automatic shared library upgrades during commands such as `pipx install` and `pipx upgrade`. The explicit `pipx upgrade-shared` command still upgrades the shared libraries. Per-command pip options can be passed with `--pip-args`: ``` pipx install my-package --pip-args='--no-cache-dir --trusted-host=my-host' ``` pipx-1.15.0/docs/how-to/index.md000066400000000000000000000020711521706623000163560ustar00rootroot00000000000000# How-to Guides Recipes for common pipx tasks. Each guide covers a specific goal and assumes you have pipx installed. - [Install pipx](install-pipx.md) โ€” system requirements and OS-specific installation steps. - [Upgrade pipx](upgrade-pipx.md) โ€” keep pipx itself up to date. - [Inject Packages](inject-packages.md) โ€” add extra packages into an existing pipx environment. - [Pin Packages](pin-packages.md) โ€” hold a package at its current version. - [Run Scripts](run-scripts.md) โ€” run specific versions, from URLs, or from source control. - [Standalone Python](standalone-python.md) โ€” install with a downloaded Python build instead of the system one. - [Use with pre-commit](use-with-pre-commit.md) โ€” integrate pipx with pre-commit hooks. - [Configure Paths](configure-paths.md) โ€” customise `PIPX_HOME`, `PIPX_BIN_DIR`, and global installs. - [Move Installation](move-installation.md) โ€” relocate your pipx directory. - [Shell Completions](shell-completions.md) โ€” enable tab completion for your shell. - [Troubleshoot](troubleshoot.md) โ€” fix common problems. pipx-1.15.0/docs/how-to/inject-packages.md000066400000000000000000000022561521706623000203040ustar00rootroot00000000000000## Inject a package `pipx inject` adds extra packages into an existing pipx-managed virtual environment. If you have `ipython` installed and want `matplotlib` available inside it: ``` pipx inject ipython matplotlib ``` Inject multiple packages at once, from a requirements file, or both: ``` pipx inject ipython matplotlib pandas pipx inject ipython -r useful-packages.txt pipx inject ipython extra-pkg -r more-packages.txt ``` ### Expose injected apps By default, injected packages do not add their entry points to your `PATH`. Use `--include-apps` to expose them: ``` pipx inject ipython black --include-apps ``` `--include-deps` exposes entry points from the injected package's dependencies too (implies `--include-apps`). ### Other flags - `--force` / `-f` reinstalls the package even if it is already injected. - `--editable` / `-e` installs the package in editable (development) mode. - `--with-suffix SUFFIX` targets a suffixed venv (e.g. `ipython_3.11`). - `--pip-args` passes extra arguments to pip (e.g. `--pip-args='--no-cache-dir'`). - `--index-url` / `-i` sets the PyPI index URL for this inject. - `--system-site-packages` gives the venv access to the system site-packages. pipx-1.15.0/docs/how-to/install-pipx.md000066400000000000000000000141161521706623000176760ustar00rootroot00000000000000## System Requirements python 3.10+ is required to install pipx. pipx can run binaries from packages with Python 3.3+. Don't have Python 3.10 or later? See [Python 3 Installation & Setup Guide](https://realpython.com/installing-python/). You also need to have `pip` installed on your machine for `python3`. The installation process varies from system to system. Consult [pip's installation instructions](https://pip.pypa.io/en/stable/installing/). Installing on Linux works best with a [Linux Package Manager](https://packaging.python.org/guides/installing-using-linux-tools/#installing-pip-setuptools-wheel-with-linux-package-managers). pipx works on macOS, Linux, and Windows. [![Packaging status](https://repology.org/badge/vertical-allrepos/pipx.svg?columns=3&exclude_unsupported=1)](https://repology.org/metapackage/pipx/versions) ## Installing pipx ### On macOS: ``` brew install pipx pipx ensurepath ``` #### Additional (optional) commands To allow pipx actions in global scope (requires pipx 1.5.0+): ``` sudo pipx ensurepath --global ``` To prepend the pipx bin directory to PATH instead of appending it (requires pipx 1.7.0+): ``` sudo pipx ensurepath --prepend ``` > [!NOTE] > Some distributions ship older pipx versions (e.g. Ubuntu 24.04 ships v1.4.3). If `--global` or `--prepend` fails with > "unrecognized arguments", upgrade pipx first: `pip install --user --upgrade pipx`, or install a newer version from a > different source. For more details, refer to [Customising your installation](configure-paths.md). ### On Linux: - Ubuntu 23.04 or above ``` sudo apt update sudo apt install pipx pipx ensurepath ``` - Fedora: ``` sudo dnf install pipx pipx ensurepath ``` - Using `pip` on other distributions: ``` python3 -m pip install --user pipx python3 -m pipx ensurepath ``` > [!NOTE] > Distributions that adopt [PEP 668](https://peps.python.org/pep-0668/) (Ubuntu 23.04+, Debian 12+, Fedora 38+) mark the > system Python as externally managed. Running `pip install --user` on these systems fails with an > `externally-managed-environment` error. Use your distribution's package manager (`apt install pipx`, > `dnf install pipx`) instead. If no distro package exists, install pipx inside its own virtual environment: > > ``` > python3 -m venv ~/.local/share/pipx-venv > ~/.local/share/pipx-venv/bin/pip install pipx > ln -s ~/.local/share/pipx-venv/bin/pipx ~/.local/bin/pipx > pipx ensurepath > ``` #### Additional (optional) commands To allow pipx actions in global scope (requires pipx 1.5.0+): ``` sudo pipx ensurepath --global ``` To prepend the pipx bin directory to PATH instead of appending it (requires pipx 1.7.0+): ``` sudo pipx ensurepath --prepend ``` For more details, refer to [Customising your installation](configure-paths.md). > [!NOTE] > If you installed pipx with `pip install --user`, the `pipx` binary lives in your user directory (e.g. > `~/.local/bin/pipx`). Running `sudo pipx` will fail because root's `PATH` does not include your user bin directory. > Use the full path instead: `sudo ~/.local/bin/pipx ensurepath --global`. Alternatively, install pipx system-wide with > `sudo pip install pipx` (without `--user`) or use your distribution's package manager. ### On Windows: - Install via [Scoop](https://scoop.sh/): ``` scoop install pipx pipx ensurepath ``` - Install via pip (requires pip 19.0 or later) ``` # If you installed python using Microsoft Store, replace `py` with `python3` in the next line. py -m pip install --user pipx ``` It is possible (even most likely) the above finishes with a WARNING looking similar to this: ``` WARNING: The script pipx.exe is installed in `\AppData\Roaming\Python\Python3x\Scripts` which is not on PATH ``` If so, go to the mentioned folder, allowing you to run the pipx executable directly. Enter the following line (even if you did not get the warning): ``` .\pipx.exe ensurepath ``` This will add both the above mentioned path and the `%USERPROFILE%\.local\bin` folder to your search path. Restart your terminal session and verify `pipx` does run. ### On FreeBSD: - Install via package manager ```sh pkg install -y py311-pipx ``` - Install via pip ```sh pip install --user pipx pipx ensurepath ``` ### Using pipx without installing (via zipapp) The zipapp can be downloaded from [Github releases](https://github.com/pypa/pipx/releases) and you can invoke it with a Python 3.10+ interpreter: ``` python pipx.pyz ensurepath ``` ### Self-managed pipx You can use pipx to manage its own installation. This keeps pipx up to date through `pipx upgrade pipx` and avoids relying on distro packages that may ship older versions. Bootstrap it with a temporary virtual environment: ``` python3 -m venv /tmp/bootstrap /tmp/bootstrap/bin/pip install pipx /tmp/bootstrap/bin/pipx install pipx /tmp/bootstrap/bin/pipx ensurepath rm -rf /tmp/bootstrap ``` After this, `pipx upgrade pipx` upgrades pipx like any other pipx-managed application. On Windows, pipx cannot delete its own running executable, so it moves locked files to a trash directory and cleans them up on the next run. ## Installing packages from source control pipx accepts any source pip supports, including git repositories. Using `black` as an example: ``` pipx install git+https://github.com/psf/black.git pipx install git+ssh://git@github.com/psf/black # using ssh pipx install git+https://github.com/psf/black.git@branch # branch of your choice pipx install git+https://github.com/psf/black.git@ce14fa8b497bae2b50ec48b3bd7022573a59cdb1 # git hash pipx install https://github.com/psf/black/archive/18.9b0.zip # install a release ``` Use pip's `egg` syntax when installing extras: ``` pipx install "git+https://github.com/psf/black.git#egg=black[jupyter]" ``` ### Installing from a pull request To test a package from an open pull request, find the fork owner and branch name on the PR page, then build the git URL. For example, PR #794 from user `contributor` on branch `fix-something`: ``` pipx install git+https://github.com/contributor/pipx.git@fix-something ``` If the PR branch has been merged, use the merge commit hash instead: ``` pipx install git+https://github.com/pypa/pipx.git@abc123def ``` pipx-1.15.0/docs/how-to/move-installation.md000066400000000000000000000037671521706623000207310ustar00rootroot00000000000000## Moving your pipx installation The below code snippets show how to move your pipx installation to a new directory. As an example, they move from a non-default location to the current default locations. If you wish to move to a different location, just replace the `NEW_LOCATION` value. ### MacOS Current default location: `~/.local` ```bash NEW_LOCATION=~/.local cache_dir=$(pipx environment --value PIPX_VENV_CACHEDIR) logs_dir=$(pipx environment --value PIPX_LOG_DIR) trash_dir=$(pipx environment --value PIPX_TRASH_DIR) home_dir=$(pipx environment --value PIPX_HOME) rm -rf "$cache_dir" "$logs_dir" "$trash_dir" mkdir -p $NEW_LOCATION && mv "$home_dir" $NEW_LOCATION pipx reinstall-all ``` ### Linux Current default location: `~/.local/share` ```bash cache_dir=$(pipx environment --value PIPX_VENV_CACHEDIR) logs_dir=$(pipx environment --value PIPX_LOG_DIR) trash_dir=$(pipx environment --value PIPX_TRASH_DIR) home_dir=$(pipx environment --value PIPX_HOME) # If you wish another location, replace the expression below # and set `NEW_LOCATION` explicitly NEW_LOCATION="${XDG_DATA_HOME:-$HOME/.local/share}" rm -rf "$cache_dir" "$logs_dir" "$trash_dir" mkdir -p $NEW_LOCATION && mv "$home_dir" $NEW_LOCATION pipx reinstall-all ``` ### Windows Current default location: `~/pipx` ```powershell $NEW_LOCATION = Join-Path "$HOME" 'pipx' $cache_dir = pipx environment --value PIPX_VENV_CACHEDIR $logs_dir = pipx environment --value PIPX_LOG_DIR $trash_dir = pipx environment --value PIPX_TRASH_DIR $home_dir = pipx environment --value PIPX_HOME Remove-Item -Recurse -Force -ErrorAction SilentlyContinue "$cache_dir", "$logs_dir", "$trash_dir" # Remove the destination directory to ensure rename behavior of `Move-Item` Remove-Item -Recurse -Force -ErrorAction SilentlyContinue "$NEW_LOCATION" Move-Item -Path $home_dir -Destination "$NEW_LOCATION" pipx reinstall-all ``` If you would prefer doing it in bash via git-bash/WSL, feel free to use the MacOS/Linux instructions, changing the `$NEW_LOCATION` to the Windows version. pipx-1.15.0/docs/how-to/pin-packages.md000066400000000000000000000021751521706623000176160ustar00rootroot00000000000000## Pin installed packages Use `pipx pin` when you need to hold an installation at its current version. Pinned packages are skipped by `pipx upgrade`, `pipx upgrade-all`, and `pipx reinstall`, so the environment keeps its existing app and dependency versions until you unpin it. - `pipx pin PACKAGE` โ€” pins the main package and any injected packages in that virtual environment. - `pipx pin PACKAGE --injected-only` โ€” leaves the main package upgradable but pins every injected package instead. - `pipx pin PACKAGE --skip PKG_A PKG_B` โ€” pins injected packages except the ones you list (the flag implies `--injected-only`). - `pipx unpin PACKAGE` โ€” re-enables upgrades for the package and anything that was pinned with it. - `pipx list --pinned` โ€” shows every pinned environment; add `--include-injected` to see pinned injected packages. pipx tracks the main package and any injected packages. It does not record individual transitive dependencies, so there is no way to pin a single dependency in isolation. Pinning the main package protects its dependency set because pipx skips running `pip install --upgrade` for that environment. pipx-1.15.0/docs/how-to/run-scripts.md000066400000000000000000000036331521706623000175450ustar00rootroot00000000000000## Running a specific version of a package The `PACKAGE` argument is a [requirement specifier](https://packaging.python.org/en/latest/glossary/#term-Requirement-Specifier), so you can pin versions, ranges, or extras: ``` pipx run mpremote==1.20.0 pipx run --spec esptool==4.6.2 esptool.py pipx run --spec 'esptool>=4.5' esptool.py ``` Requirement specifiers containing `>`, `<`, or spaces need quoting. ## Running with extra dependencies `--with` adds packages to the temporary environment alongside the main app: ``` pipx run --with requests --with rich my-script.py ``` ## Running from source control `pipx run` accepts git URLs through `--spec`. Using `black` as an example: ``` pipx run --spec git+https://github.com/psf/black.git black pipx run --spec git+ssh://git@github.com/psf/black black pipx run --spec git+https://github.com/psf/black.git@branch black pipx run --spec git+https://github.com/psf/black.git@ce14fa8b497bae2b50ec48b3bd7022573a59cdb1 black pipx run --spec https://github.com/psf/black/archive/18.9b0.zip black ``` ## Running from URL You can run `.py` files hosted anywhere: ``` pipx run https://gist.githubusercontent.com/cs01/fa721a17a326e551ede048c5088f9e0f/raw/6bdfbb6e9c1132b1c38fdd2f195d4a24c540c324/pipx-demo.py ``` ## Running scripts with dependencies (PEP 723) Scripts can declare their own dependencies using [inline script metadata](https://packaging.python.org/en/latest/specifications/inline-script-metadata/). pipx reads the `# /// script` block and installs the listed packages before running: ```python # test.py # /// script # dependencies = ["requests"] # /// import sys import requests project = sys.argv[1] data = requests.get(f"https://pypi.org/pypi/{project}/json").json() print(data["info"]["version"]) ``` ``` > pipx run test.py pipx 1.9.0 ``` pipx creates a cached virtual environment keyed to the script's dependency list. Changing the dependencies creates a fresh environment. pipx-1.15.0/docs/how-to/shell-completions.md000066400000000000000000000002341521706623000207070ustar00rootroot00000000000000## Shell Completion You can easily get your shell's tab completions working by following instructions printed with this command: ``` pipx completions ``` pipx-1.15.0/docs/how-to/standalone-python.md000066400000000000000000000042171521706623000207220ustar00rootroot00000000000000## Use a Standalone Python Build pipx can install applications under a Python interpreter downloaded from [python-build-standalone](https://github.com/astral-sh/python-build-standalone) instead of the system Python. Reach for this when the system Python is missing the version you asked for, or when the distro patched its Python in ways that break the application. ### Choosing when to download Set `--fetch-python` (or `PIPX_FETCH_PYTHON`) to one of three values: | Value | Behaviour | | --------- | ---------------------------------------------------------------------------------- | | `never` | Default. Don't download. Use Python interpreters from `PATH` or the `py` launcher. | | `missing` | Look for the requested version locally first; download it when it isn't there. | | `always` | Skip the local search and use a standalone build for the requested version. | ``` # Download only when the requested version is missing locally pipx install --python 3.13 --fetch-python=missing my-package # Use a fresh standalone build, ignoring system Python pipx install --python 3.13 --fetch-python=always my-package # Set the policy for your shell session export PIPX_FETCH_PYTHON=missing pipx install --python 3.13 my-package ``` pipx unpacks the interpreter into its standalone cache. Manage cached interpreters with `pipx interpreter list`, `pipx interpreter prune`, and `pipx interpreter upgrade` (see the [reference](../reference/examples.md)). ### When `--fetch-python=always` is the right choice - CI runs where you don't want to depend on the runner's preinstalled Python. - Distros that strip optional modules (`tkinter`, `lzma`) from their packaged Python. - Air-gapped boxes where you have populated the standalone cache and want pipx to ignore the system Python. ### Migrating from `--fetch-missing-python` `--fetch-missing-python` and `PIPX_FETCH_MISSING_PYTHON` still work, but pipx warns and treats them as deprecated. They behave like `--fetch-python=missing` / `PIPX_FETCH_PYTHON=missing`. pipx errors out if you set both `PIPX_FETCH_MISSING_PYTHON` and `PIPX_FETCH_PYTHON`. pipx-1.15.0/docs/how-to/troubleshoot.md000066400000000000000000000220451521706623000200030ustar00rootroot00000000000000## Wrong package version installed pipx creates venvs using your default Python interpreter. pip resolves the latest package version compatible with that interpreter. If a package drops support for your Python version, pip installs an older release without warning. Check which Python pipx uses with `pipx environment --value PIPX_DEFAULT_PYTHON`. To install with a different Python, pass `--python`: ``` pipx install my-package --python python3.12 ``` If you don't have the desired Python version installed, pass `--fetch-python=missing` and pipx downloads a standalone build: ``` pipx install my-package --python 3.13 --fetch-python=missing ``` Pass `--fetch-python=always` to download a standalone build even when the system already has the requested Python. Useful when a distro ships a patched interpreter you'd rather avoid. ## `reinstall-all` fixes most issues The following command should fix many problems you may encounter as a pipx user: ``` pipx reinstall-all ``` This is a good fix for the following problems: - System python was upgraded and the python used with a pipx-installed package is no longer available - pipx upgrade causes issues with old pipx-installed packages pipx has been upgraded a lot over the years. If you are a long-standing pipx user (thanks, by the way!) then you may have old pipx-installed packages that have internal data that is different than what pipx currently expects. By executing `pipx reinstall-all`, pipx will re-write its internal data and this should fix many issues you may encounter. **Note:** If your pipx-installed package was installed using a pipx version before 0.15.0.0 and you want to specify particular options, then you may want to uninstall and install it manually: ``` pipx uninstall pipx install ``` ## Diagnosing problems using `list` ``` pipx list ``` will not only list all of your pipx-installed packages, but can also diagnose some problems with them, as well as suggest solutions. ## Specifying pipx options The most reliable method to specify command-line options that require an argument is to use an `=`-sign. An example: ``` pipx install pycowsay --pip-args="--no-cache-dir" ``` Another example for ignoring ssl/tls errors: ``` pipx install termpair --pip-args '--trusted-host files.pythonhosted.org --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host github.com'" ``` ## Check for `PIP_*` environment variables pipx uses `pip` to install and manage packages. If you see pipx exhibiting strange behavior on install or upgrade, check that you don't have special environment variables that affect `pip`'s behavior in your environment. To check for `pip` environment variables, execute the following depending on your system: ### Unix or macOS ``` env | grep '^PIP_' ``` ### Windows PowerShell ``` ls env:PIP_* ``` ### Windows `cmd` ``` set PIP_ ``` Reference: [pip Environment Variables](https://pip.pypa.io/en/stable/user_guide/#environment-variables) ## Clear `runpip` cache warnings `pipx runpip` runs the `pip` installed in a pipx-managed virtual environment. Warnings such as `WARNING: Cache entry deserialization failed, entry ignored` come from that `pip` process and its own HTTP cache, not from pipx's virtual environment cache. To clear the cache used by a pipx-managed package, run: ``` pipx runpip cache purge ``` To inspect the cache directory first, add `--verbose` so pipx does not quiet `pip`'s output: ``` pipx runpip --verbose cache dir ``` Clearing `$PIPX_HOME/.cache` or the cache for a different Python interpreter will not clear cache entries used by `pipx runpip `. ## `pipx` log files pipx records a verbose log file for every `pipx` command invocation. The logs for the last 10 `pipx` commands can be found in `$XDG_STATE_HOME/pipx/logs` or user's log path if the former is not writable by the user. Set `PIPX_MAX_LOGS` to change how many log files are kept (default: `10`). For most users this location is `~/.local/state/pipx/logs`, where `~` is your home directory. ## `sudo pipx` not found If you installed pipx with `pip install --user`, the binary lives in your user directory (e.g. `~/.local/bin/pipx`). Root's `PATH` does not include that directory, so `sudo pipx` fails with "command not found". Use the full path instead: ``` sudo ~/.local/bin/pipx ensurepath --global ``` To avoid this, install pipx through your distribution's package manager (`apt install pipx`, `dnf install pipx`) or install it system-wide with `sudo pip install pipx` (without `--user`). ## Debian, Ubuntu issues If you have issues using pipx on Debian, Ubuntu, or other Debian-based linux distributions, make sure you have the following packages installed on your system. (Debian systems do not install these by default with their python installations.) ``` sudo apt install python3-venv python3-pip ``` Reference: [Python Packaging User Guide: Installing pip/setuptools/wheel with Linux Package Managers](https://packaging.python.org/guides/installing-using-linux-tools) ## macOS issues If you want to use a pipx-installed package in a shebang (a common example is the AWS CLI), you will likely not be able to, because the binary will be stored under `~/Library/Application Support/pipx/`. The space in the path is not supported in a shebang. A simple solution is symlinking `~/Library/Application Support/pipx` to `~/Library/ApplicationSupport/pipx`, and using that as the path in the shebang instead. ``` mkdir $HOME/Library/ApplicationSupport ln -s $HOME/Library/Application\ Support/pipx $HOME/Library/ApplicationSupport/pipx ``` ## Does it work to install your package with `pip`? This 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 the problem, is to try installing it using `pip`. For example: ### Unix or macOS ``` python3 -m venv test_venv test_venv/bin/python3 -m pip install ``` ### Windows ``` python -m venv test_venv test_venv/Scripts/python -m pip install ``` If installation into this "virtual environment" using pip fails, then it's likely that the problem is with the package or your host system. To clean up after this experiment: ``` rm -rf test_venv ``` ## pipx files not in expected locations according to documentation pipx versions after 1.2.0 adopt the XDG base directory specification for the location of `PIPX_HOME` and the data, cache, and log directories. Version 1.2.0 and earlier use `~/.local/pipx` as the default `PIPX_HOME` and install the data, cache, and log directories under it. To maintain compatibility with older versions, pipx will automatically use this old `PIPX_HOME` path if it exists. For a map of old and new paths, see [Installation Options](configure-paths.md). In pipx version 1.5.0, this was reverted for Windows and macOS. It defaults again to `~/.local/pipx` on macOS and to `~\pipx` on Windows. If 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 `~/.local/pipx` directory to the new location (after removing cache, log, and trash directories which will get recreated automatically) and then reinstall all packages. Please refer to [Moving your pipx installation](move-installation.md) on how to move it. ## Warning: Found a space in the pipx home path In pipx version 1.5, we introduced the warning you're seeing, as multiple incompatibilities with spaces in the pipx home path were discovered. You may see this for the following reasons: 1. 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 that all users who installed pipx in this time frame and were using the default behavior are seeing this warning now. 1. You set your `PIPX_HOME` to a path with spaces in it explicitly or because your `$HOME` path contains a space. ### Why are spaces in the `PIPX_HOME` path bad The main reason we can't support paths with spaces is that shebangs don't support spaces in the interpreter path. All applications installed via `pipx` are installed via `pip`, which creates a script with a shebang at the top, defining the interpreter of the `venv` to use. `pip` does some magic to the shebang for scripts defined as a `script`, that resolves this issue. Unfortunately, many libraries define their scripts as `console_scripts`, where `pip` does not perform this logic. Therefore, these scripts cannot be run if installed with `pipx` in a path with spaces because the path to the `venv` (and therefore the interpreter) will contain spaces. If you want to use a script installed via pipx in a shebang itself (common for example for the AWS CLI), you run into a similar problem, as the path to the installed script will contain a space. ### How to fix You can generally fix this by using our default locations, as long as your `$HOME` path does not contain spaces. Please refer to our [Moving your pipx installation](move-installation.md) docs on how to move the `pipx` installation. If you're really sure you want to stick to your path with spaces, to suppress the warning set the `PIPX_HOME_ALLOW_SPACE` environment variable to `true`. pipx-1.15.0/docs/how-to/upgrade-pipx.md000066400000000000000000000020431521706623000176530ustar00rootroot00000000000000## Upgrade pipx On macOS: ``` brew update && brew upgrade pipx ``` On Ubuntu Linux: ``` sudo apt upgrade pipx ``` On Fedora Linux: ``` sudo dnf update pipx ``` On Windows: ``` scoop update pipx ``` Otherwise, upgrade via pip: ``` python3 -m pip install --user -U pipx ``` ### Note: Upgrading pipx from a pre-0.15.0.0 version to 0.15.0.0 or later After 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 advantage of the new persistent pipx metadata files introduced in the 0.15.0.0 release. These metadata files store pip specification values, injected packages, any custom pip arguments, and more in each main package's venv. If you have no packages installed using the `--spec` option, and no venvs with injected packages, you can do this by running `pipx reinstall-all`. If you have any packages installed using the `--spec` option or venvs with injected packages, you should reinstall packages manually using `pipx uninstall-all`, followed by `pipx install` and possibly `pipx inject`. pipx-1.15.0/docs/how-to/use-uv-backend.md000066400000000000000000000067001521706623000200630ustar00rootroot00000000000000# Use the uv Backend pipx can use [uv](https://github.com/astral-sh/uv) to create virtual environments, install packages, and run ephemeral apps in place of pip and venv. The backend turns on when uv is reachable; flip it per command (`--backend`) or globally (`PIPX_DEFAULT_BACKEND`). > This page covers pipx using uv to create venvs and install packages. The pipx CLI surface stays the same. For a > side-by-side comparison with `uv tool` (Astral's standalone command), see > [pipx vs uv tool](../explanation/comparisons.md#pipx-vs-uv-tool). ## Why use it - uv creates venvs faster than `python -m venv`. The venv ships without pip. - uv resolves and installs faster than pip on large dependency trees. - `pipx run` execs `uv tool run`, picking up uv's cross-invocation cache. ## Enable it Two ways to expose uv to pipx: 1. Install pipx with the `uv` extra. pipx prefers the bundled binary when present: ```shell pipx install pipx[uv] ``` 1. Install uv however you like (`brew install uv`, `cargo install uv`, etc.) and put it on `PATH`. pipx picks it up automatically. When uv is available, pipx defaults to it for new venvs. Existing venvs keep their original backend; the choice lives in each venv's metadata. ## Choose a backend explicitly ```shell pipx install black --backend pip # force pip + venv pipx install ruff --backend uv # force uv (errors if uv is not available) PIPX_DEFAULT_BACKEND=uv pipx install ruff # global override via environment pipx environment --value PIPX_RESOLVED_BACKEND # see what pipx will use right now ``` `install`, `install-all`, `inject`, `upgrade`, `upgrade-all`, `reinstall`, `reinstall-all`, and `run` all accept `--backend`. Run `pipx reinstall NAME --backend uv` to switch an already-installed venv. ## What changes under the uv backend - pipx creates new venvs with `uv venv`. The venv contains no `pip` and no `pipx_shared.pth` file. - `pipx runpip` runs `uv pip --python /bin/python`. uv rejects flags it doesn't understand instead of pipx silently dropping them. - `pipx run` execs `uv tool run`; the cache lives in uv's cache directory. Pass `--no-cache` to skip it. - `pipx run script.py` execs `uv run --script script.py` for PEP 723 inline scripts. ## Limitations - `pipx install pip --backend uv` errors out. A uv venv has no pip, so the result would be inconsistent. Use `--backend pip` instead. - `pipx run --backend uv` does not honor `[pipx.run]` entry points; `uv tool run` only sees standard console scripts. Use `--backend pip` if your package declares them. - Some `--pip-args` values have no `uv tool run` equivalent (for example `--editable`, `--no-build-isolation`). pipx errors instead of silently dropping them. Use `--backend pip` for those flows. - `pipx run --backend uv` against URL or named-pipe scripts falls back to a pipx-managed venv: `uv run --script` reads PEP 723 metadata off disk, and there is no on-disk path for content fetched at runtime. pipx logs a warning when this fallback fires. ## Cache layout `pipx run` under the uv backend caches in `UV_CACHE_DIR` instead of `PIPX_VENV_CACHEDIR`. Switching the default backend on a host that already used `pipx run` leaves the old pipx cache behind: `pipx environment` prints both paths so you can inspect them and remove the unused one manually. The 14-day expiry sweep that pipx applies to its own venv cache does not extend to uv's cache, which uv manages on its own schedule (`uv cache clean`). pipx-1.15.0/docs/how-to/use-with-pre-commit.md000066400000000000000000000012201521706623000210610ustar00rootroot00000000000000## Using pipx with pre-commit pipx has [pre-commit](https://pre-commit.com/) support. This lets you run applications: - that can be run using `pipx run` but don't have native pre-commit support; - using its prebuilt wheel from pypi.org instead of building it from source; and - using pipx's `--spec` and `--index-url` flags. Example configuration for use of the code linter [yapf](https://github.com/google/yapf/). This is to be added to your `.pre-commit-config.yaml`. ```yaml - repo: https://github.com/pypa/pipx rev: 1.5.0 hooks: - id: pipx alias: yapf name: yapf args: [yapf, -i] types: [python] ``` pipx-1.15.0/docs/index.md000066400000000000000000000105561521706623000151500ustar00rootroot00000000000000

# pipx โ€” Install and Run Python Applications in Isolated Environments

pipx installs and runs end-user Python applications in isolated environments. It fills the same role as macOS's `brew`, JavaScript's [npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b), and Linux's `apt`. Under the hood it uses pip, but unlike pip it creates a separate virtual environment for each application, keeping your system clean. ## Documentation - **[Tutorials](tutorial/index.md)** โ€” install your first application and run commands in temporary environments. - **[How-to Guides](how-to/index.md)** โ€” recipes for installing pipx, injecting packages, configuring paths, and more. - **[Reference](reference/index.md)** โ€” CLI flags, examples, environment variables, and programs to try. - **[Explanation](explanation/index.md)** โ€” how pipx works under the hood and how it compares to other tools. ```mermaid flowchart LR USER["you"] --> |"pipx install"| PIPX["pipx"] USER --> |"pipx run"| PIPX PIPX --> |"fetches from"| PYPI["PyPI"] PIPX --> |"creates"| VENV["isolated venvs"] VENV --> |"exposes on PATH"| APPS["black, ruff,
poetry, ..."] style USER fill:#3f72af,color:#fff style PIPX fill:#2a9d8f,color:#fff style PYPI fill:#c78c20,color:#fff style VENV fill:#7c4dff,color:#fff style APPS fill:#388e3c,color:#fff ``` ## pip vs pipx pip installs both libraries and applications into your current environment with no isolation. pipx installs only applications, each in its own virtual environment, and exposes their commands on your `PATH`. You get clean uninstalls and zero dependency conflicts between tools. ## Where do apps come from? pipx pulls packages from [PyPI](https://pypi.org/) by default, but accepts any source pip supports: local directories, wheels, and git URLs. Any package that declares [console script entry points](https://python-packaging.readthedocs.io/en/latest/command-line-scripts.html#the-console-scripts-entry-point) works with pipx. [Poetry](https://python-poetry.org/docs/pyproject/#scripts) and [hatch](https://hatch.pypa.io/latest/config/metadata/#cli) users can add entry points the same way. ## Features pipx lets you - install CLI apps into isolated environments with `pipx install`, guaranteeing no dependency conflicts and clean uninstalls; - list, upgrade, and uninstall packages in one command; and - run the latest version of any app in a temporary environment with `pipx run`. pipx runs with regular user permissions and never calls `sudo pip install`. ## Testimonials > "Thanks for improving the workflow that pipsi has covered in the past. Nicely done!" > > โ€” *[Jannis Leidel](https://twitter.com/jezdez), PSF fellow, former pip and Django core developer, and founder of the > Python Packaging Authority (PyPA)* > "My setup pieces together pyenv, poetry, and pipx. [...] For the things I need, it's perfect." > > โ€” *[Jacob Kaplan-Moss](https://twitter.com/jacobian), co-creator of Django in blog post > [My Python Development Environment, 2020 Edition](https://jacobian.org/2019/nov/11/python-environment-2020/)* > "I'm a big fan of pipx. I think pipx is super cool." > > โ€” *[Michael Kennedy](https://twitter.com/mkennedy), co-host of PythonBytes podcast in > [episode 139](https://pythonbytes.fm/episodes/transcript/139/f-yes-for-the-f-strings)* ## Credits pipx was inspired by [pipsi](https://github.com/mitsuhiko/pipsi) and [npx](https://github.com/npm/npx). It was created by [Chad Smith](https://github.com/cs01/) and has had lots of help from [contributors](https://github.com/pypa/pipx/graphs/contributors). The logo was created by [@IrishMorales](https://github.com/IrishMorales). pipx is maintained by a team of volunteers (in alphabetical order) - [Bernรกt Gรกbor](https://github.com/gaborbernat) - [Chad Smith](https://github.com/cs01) - co-lead maintainer - [Chrysle](https://github.com/chrysle) - [Jason Lam](https://github.com/dukecat0) - [Matthew Clapp](https://github.com/itsayellow) - co-lead maintainer - [Robert Offner](https://github.com/gitznik) - [Tzu-ping Chung](https://github.com/uranusjr) pipx-1.15.0/docs/reference/000077500000000000000000000000001521706623000154465ustar00rootroot00000000000000pipx-1.15.0/docs/reference/environment-variables.md000066400000000000000000000076421521706623000223130ustar00rootroot00000000000000## Environment Variables pipx reads the following environment variables. All are optional. | Variable | Description | Default | | --------------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------- | | `PIPX_HOME` | Root directory for pipx virtual environments. | `~/.local/share/pipx` (Linux), `~/.local/pipx` (macOS), `~\pipx` (Windows) | | `PIPX_BIN_DIR` | Directory where application entry-point symlinks are placed. | `~/.local/bin` | | `PIPX_MAN_DIR` | Directory where man page symlinks are placed. | `~/.local/share/man` | | `PIPX_GLOBAL_HOME` | Root directory for global (`--global`) virtual environments. | `/opt/pipx` | | `PIPX_GLOBAL_BIN_DIR` | Binary directory for global installs. | `/usr/local/bin` | | `PIPX_GLOBAL_MAN_DIR` | Man page directory for global installs. | `/usr/local/share/man` | | `PIPX_DEFAULT_PYTHON` | Python interpreter to use when `--python` is not passed. | `python3` (or `py` on Windows) | | `PIPX_DEFAULT_BACKEND` | Backend for new venvs: `pip` or `uv`. | `uv` when uv is available, else `pip` | | `PIPX_SHARED_LIBS` | Override the shared libraries directory. | `PIPX_HOME/shared` | | `PIPX_FETCH_PYTHON` | When to fetch a standalone Python build: `always`, `missing`, or `never`. | `never` | | `PIPX_FETCH_MISSING_PYTHON` | Deprecated, alias for `PIPX_FETCH_PYTHON=missing`. | _(unset)_ | | `PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE` | Set to `1` to skip automatic shared library upgrades. | _(unset)_ | | `PIPX_HOME_ALLOW_SPACE` | Set to `true` to suppress the "space in PIPX_HOME" warning. | _(unset)_ | | `PIPX_USE_EMOJI` | Set to `0` to disable emoji output. | `1` | | `PIPX_MAX_LOGS` | Maximum number of log files to keep in the logs directory. | `10` | ### Notes `PIPX_HOME` has platform-specific fallback logic. If a legacy directory (e.g. `~/.local/pipx` on Linux) already exists, pipx uses it instead of the new default. See [Configure Paths](../how-to/configure-paths.md) for details. Standard `PIP_*` environment variables (e.g. `PIP_INDEX_URL`) are forwarded to pip when pipx invokes it. See [Troubleshooting](../how-to/troubleshoot.md#check-for-pip_-environment-variables) if unexpected pip behaviour occurs. Run `pipx environment` to see the resolved value of every directory variable on your system. pipx-1.15.0/docs/reference/examples.md000066400000000000000000000165351521706623000176200ustar00rootroot00000000000000## `pipx install` examples ``` pipx install pycowsay pipx install --python python3.10 pycowsay pipx install --python 3.12 pycowsay pipx install --fetch-python=missing --python 3.12 pycowsay pipx install --fetch-python=always --python 3.13 pycowsay pipx install git+https://github.com/psf/black pipx install git+https://github.com/psf/black.git@branch-name pipx install git+https://github.com/psf/black.git@git-hash pipx install git+ssh://@/ pipx install https://github.com/psf/black/archive/18.9b0.zip pipx install black[d] pipx install --preinstall ansible-lint --preinstall mitogen ansible-core pipx install 'black[d] @ git+https://github.com/psf/black.git@branch-name' pipx install --suffix @branch-name 'black[d] @ git+https://github.com/psf/black.git@branch-name' pipx install --include-deps jupyter pipx install --pip-args='--pre' poetry pipx install --pip-args='--index-url=: --trusted-host=:' private-repo-package pipx install --index-url https://test.pypi.org/simple/ --pip-args='--extra-index-url https://pypi.org/simple/' some-package pipx install --global pycowsay pipx install . pipx install path/to/some-project ``` ## `pipx run` examples pipx enables you to test various combinations of Python versions and package versions in ephemeral environments: ``` pipx run BINARY # latest version of binary is run with python3 pipx run --spec PACKAGE==2.0.0 BINARY # specific version of package is run pipx run --python python3.10 BINARY # Installed and invoked with specific Python version pipx run --python python3.10 --spec PACKAGE=1.7.3 BINARY pipx run --spec git+https://url.git BINARY # latest version on default branch is run pipx run --spec git+https://url.git@branch BINARY pipx run --spec git+https://url.git@hash BINARY pipx run pycowsay moo pipx --version # prints pipx version pipx run pycowsay --version # prints pycowsay version pipx run --python pythonX pycowsay pipx run pycowsay==2.0 --version pipx run pycowsay[dev] --version pipx run --spec git+https://github.com/psf/black.git black pipx run --spec git+https://github.com/psf/black.git@branch-name black pipx run --spec git+https://github.com/psf/black.git@git-hash black pipx run --spec https://github.com/psf/black/archive/18.9b0.zip black --help pipx run https://gist.githubusercontent.com/cs01/fa721a17a326e551ede048c5088f9e0f/raw/6bdfbb6e9c1132b1c38fdd2f195d4a24c540c324/pipx-demo.py ``` You can run local files, or scripts hosted on the internet, and you can run them with arguments: ``` pipx run test.py pipx run test.py 1 2 3 pipx run https://example.com/test.py pipx run https://example.com/test.py 1 2 3 ``` A simple filename is ambiguous - it could be a file, or a package on PyPI. It will be treated as a filename if the file exists, or as a package if not. To force interpretation as a local path, use `--path`, and to force interpretation as a package name, use `--spec` (with the PyPI name of the package). ``` pipx run myscript.py # Local file, if myscript.py exists pipx run doesnotexist.py # Package, because doesnotexist.py is not a local file pipx run --path test.py # Always a local file pipx run --spec test-py test.py # Always a package on PyPI ``` You can also run scripts that have dependencies: If you have a script `test.py` that needs 3rd party libraries, you can add [inline script metadata](https://packaging.python.org/en/latest/specifications/inline-script-metadata/) in the style of PEP 723. ``` # test.py # /// script # dependencies = ["requests"] # /// import sys import requests project = sys.argv[1] pipx_data = requests.get(f"https://pypi.org/pypi/{project}/json").json() print(pipx_data["info"]["version"]) ``` Then you can run it as follows: ``` > pipx run test.py pipx 1.1.0 ``` ## `pipx inject` example One use of the inject command is setting up a REPL with some useful extra packages. ``` > pipx install ptpython > pipx inject ptpython requests pendulum ``` After running the above commands, you will be able to import and use the `requests` and `pendulum` packages inside a `ptpython` repl. Equivalently, the extra packages can be listed in a text file (e.g. `useful-packages.txt`). Each line is a separate package specifier with the same syntax as the command line. Comments are supported with a `#` prefix. Hence, the syntax is a strict subset of the pip [requirements file format][pip-requirements] syntax. ``` # Additional packages requests pendulum # for easier datetimes ``` This file can then be given to `pipx inject` on the command line: ```shell > pipx inject ptpython --requirement useful-packages.txt # or: > pipx inject ptpython -r useful-packages.txt ``` Note that these options can be repeated and used together, e.g. ``` > pipx inject ptpython package-1 -r extra-packages-1.txt -r extra-packages-2.txt package-2 ``` If you require full pip functionality, then use the `runpip` command instead; however, the installed packages won't be recognised as "injected". ## `pipx list` example ``` > pipx list venvs are in /Users/user/.local/pipx/venvs binaries are exposed on your $PATH at /Users/user/.local/bin package black 18.9b0, Python 3.10.0 - black - blackd package pipx 0.10.0, Python 3.10.0 - pipx > pipx list --short black 18.9b0 pipx 0.10.0 ``` ## `pipx install-all` example ```shell > pipx list --json > pipx.json > pipx install-all pipx.json 'black' already seems to be installed. Not modifying existing installation in '/usr/local/pipx/venvs/black'. Pass '--force' to force installation. 'pipx' already seems to be installed. Not modifying existing installation in '/usr/local/pipx/venvs/black'. Pass '--force' to force installation. > pipx install-all pipx.json --force Installing to existing venv 'black' installed package black 24.3.0, installed using Python 3.10.12 These apps are now globally available - black - blackd done! โœจ ๐ŸŒŸ โœจ Installing to existing venv 'pipx' installed package pipx 1.4.3, installed using Python 3.10.12 These apps are now globally available - pipx done! โœจ ๐ŸŒŸ โœจ ``` ## `pipx upgrade-shared` examples One use of the upgrade-shared command is to force a `pip` upgrade. ```shell > pipx upgrade-shared ``` This example pins `pip` (temporarily, until the next automatic upgrade, if that is not explicitly turned off) to a specific version. ```shell > pipx upgrade-shared --pip-args=pip==24.0 ``` ## `pipx pin` examples Pinning keeps an installation at its current version until you call `pipx unpin`. Pin the entire environment (main package plus anything injected): ```shell > pipx install httpie > pipx pin httpie > pipx list --pinned httpie ``` Pin only injected packages so the main package can continue to receive upgrades: ```shell > pipx inject poetry poetry-plugin-export poetry-plugin-app > pipx pin poetry --injected-only Pinned 2 packages in venv poetry - poetry-plugin-export - poetry-plugin-app ``` Skip selected injected packages when pinning: ```shell > pipx inject pdm pdm-django pdm-pytorch > pipx pin pdm --skip pdm-django Pinned 1 packages in venv pdm - pdm-pytorch ``` Unpin everything so upgrades resume: ```shell > pipx unpin poetry Unpinned 2 packages in venv poetry - poetry - poetry-plugin-export - poetry-plugin-app ``` [pip-requirements]: https://pip.pypa.io/en/stable/reference/requirements-file-format/ pipx-1.15.0/docs/reference/index.md000066400000000000000000000007321521706623000171010ustar00rootroot00000000000000# Reference Technical reference for pipx: CLI flags, environment variables, and runnable examples. - [CLI Reference](cli.md) โ€” auto-generated help text for every pipx command. - [Examples](examples.md) โ€” copy-paste command examples for install, run, inject, list, and more. - [Programs to Try](programs-to-try.md) โ€” Python applications worth installing with pipx. - [Environment Variables](environment-variables.md) โ€” every `PIPX_*` variable and what it controls. pipx-1.15.0/docs/reference/programs-to-try.md000066400000000000000000000037651521706623000210710ustar00rootroot00000000000000## Programs Here are some programs you can try out. If you've never used the program before, make sure you add the `--help` flag so it doesn't do something you don't expect. If you decide you want to install, you can run `pipx install PACKAGE` instead. ### ansible IT automation ``` pipx install --include-deps ansible ``` ### asciinema Record and share your terminal sessions, the right way. ``` pipx run asciinema ``` ### black uncompromising Python code formatter ``` pipx run black ``` ### pybabel internationalizing and localizing Python applications ``` pipx run --spec=babel pybabel --help ``` ### chardetect detect file encoding ``` pipx run --spec=chardet chardetect --help ``` ### cookiecutter creates projects from project templates ``` pipx run cookiecutter ``` ### create-python-package easily create and publish new Python packages ``` pipx run create-python-package ``` ### flake8 tool for style guide enforcement ``` pipx run flake8 ``` ### gdbgui browser-based gdb debugger ``` pipx run gdbgui ``` ### hatch Python project manager that lets you build & publish packages, run tasks in environments and more ``` pipx run hatch ``` ### hexsticker create hexagon stickers automatically ``` pipx run hexsticker ``` ### ipython powerful interactive Python shell ``` pipx run ipython ``` ### jupyter web-based notebook environment for interactive computing ``` pipx run jupyter ``` ### pipenv python dependency/environment management ``` pipx run pipenv ``` ### poetry python dependency/environment/packaging management ``` pipx run poetry ``` ### pylint source code analyzer ``` pipx run pylint ``` ### pyinstaller bundles a Python application and all its dependencies into a single package ``` pipx run pyinstaller ``` ### pyxtermjs fully functional terminal in the browser ``` pipx run pyxtermjs ``` ### ruff An extremely fast Python linter ``` pipx run ruff ``` ### shell-functools Functional programming tools for the shell ``` pipx install shell-functools ``` pipx-1.15.0/docs/tutorial/000077500000000000000000000000001521706623000153535ustar00rootroot00000000000000pipx-1.15.0/docs/tutorial/getting-started.md000066400000000000000000000027041521706623000210050ustar00rootroot00000000000000## Getting Started with pipx This tutorial covers the core pipx workflow: installing an application, running it, and managing it. You need [pipx installed](../how-to/install-pipx.md) before continuing. ### Install your first application Pick a small package to try. `pycowsay` works well: ``` pipx install pycowsay ``` pipx creates an isolated virtual environment for `pycowsay`, installs it there, and links the `pycowsay` command into a directory on your `PATH`. Run it from anywhere: ``` pycowsay "Hello, pipx!" ``` ### List installed applications ``` pipx list ``` The output shows the virtual environment location, exposed commands, and the Python version each package uses. ### Run an application without installing `pipx run` executes an application in a temporary environment and cleans up after itself: ``` pipx run pycowsay moooo! ``` ### Upgrade an installed application ``` pipx upgrade pycowsay ``` Or upgrade everything at once: ``` pipx upgrade-all ``` ### Uninstall an application ``` pipx uninstall pycowsay ``` pipx deletes the isolated environment and removes the command from your `PATH`. ### Next steps Continue with the [install applications](install-applications.md) and [run applications](run-applications.md) tutorials for a closer look at the two core commands. The [how-to guides](../how-to/index.md) cover tasks like injecting packages and configuring paths. The full [CLI reference](../reference/cli.md) documents every flag. pipx-1.15.0/docs/tutorial/index.md000066400000000000000000000006421521706623000170060ustar00rootroot00000000000000# Tutorials Step-by-step lessons that take you from zero to productive with pipx. Start from the top if you are new. - [Getting Started](getting-started.md) โ€” install your first application and learn the core commands. - [Install Applications](install-applications.md) โ€” `pipx install` with real examples. - [Run Applications](run-applications.md) โ€” `pipx run` for one-off execution in temporary environments. pipx-1.15.0/docs/tutorial/install-applications.md000066400000000000000000000031731521706623000220330ustar00rootroot00000000000000## Installing a Package and its Applications Install an application with: ``` pipx install PACKAGE ``` pipx creates a virtual environment, installs the package, and adds its entry points to a location on your `PATH`. `pipx install pycowsay` makes the `pycowsay` command available system-wide while sandboxing pycowsay in its own virtual environment. No `sudo` required. To install for all users on the system, pass `--global` after the subcommand (see [Configure Paths](../how-to/configure-paths.md#-global-argument)). ``` >> pipx install pycowsay installed package pycowsay 2.0.3, Python 3.10.3 These apps are now globally available - pycowsay done! โœจ ๐ŸŒŸ โœจ >> pipx list venvs are in /home/user/.local/share/pipx/venvs apps are exposed on your $PATH at /home/user/.local/bin package pycowsay 2.0.3, Python 3.10.3 - pycowsay # Now you can run pycowsay from anywhere >> pycowsay mooo ____ < mooo > ==== \ \ ^__^ (oo)\_______ (__)\ )\/\ ||----w | || || ``` ### Picking a Python interpreter Pass `--python` to install with a specific Python version. When that Python isn't on your `PATH`, pipx can download a [python-build-standalone](https://github.com/astral-sh/python-build-standalone) build for you: ``` pipx install --python 3.13 --fetch-python=missing pycowsay ``` Pass `--fetch-python=always` to use a fresh standalone build instead of any system Python. Reach for it when a distro patched the system Python in ways you can't tolerate. See the [Standalone Python](../how-to/standalone-python.md) how-to for more options. pipx-1.15.0/docs/tutorial/run-applications.md000066400000000000000000000061661521706623000211760ustar00rootroot00000000000000## Running an Application in a Temporary Virtual Environment `pipx run` downloads and runs Python applications in a one-time, temporary environment, then leaves your system untouched. Use it to initialize a new project, check an app's help text, or try a tool without committing to an install. The blog post [How to set up a perfect Python project](https://sourcery.ai/blog/python-best-practices/) uses `pipx run` to kickstart a new project with [cookiecutter](https://github.com/cookiecutter/cookiecutter). ``` pipx run APP [ARGS...] ``` pipx installs the package in an isolated, temporary directory and invokes the app: ``` > pipx run pycowsay moo --- < moo > --- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ``` Arguments after the application name pass straight through: ``` > pipx run pycowsay these arguments are all passed to pycowsay! ------------------------------------------- < these arguments are all passed to pycowsay! > ------------------------------------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ``` pipx caches virtual environments per app for a few days. After they expire, pipx fetches the latest version. ### Ambiguous arguments pipx can consume arguments meant for the application: ``` > pipx run pycowsay --py usage: pipx run [-h] [--no-cache] [--pypackages] [--spec SPEC] [--verbose] [--python PYTHON] [--system-site-packages] [--index-url INDEX_URL] [--editable] [--pip-args PIP_ARGS] app ... pipx run: error: ambiguous option: --py could match --pypackages, --python ``` Place `--` before the app name to forward all arguments verbatim: ``` > pipx run -- pycowsay --py ---- < --py > ---- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ``` ### App name differs from package name Use `--spec` to specify the package and provide the app name separately: ``` pipx run --spec PACKAGE APP ``` The [esptool](https://github.com/espressif/esptool) package, for example, exposes executables with different names: ``` >> pipx run esptool 'esptool' executable script not found in package 'esptool'. Available executable scripts: esp_rfc2217_server.py - usage: 'pipx run --spec esptool esp_rfc2217_server.py [arguments?]' espefuse.py - usage: 'pipx run --spec esptool espefuse.py [arguments?]' espsecure.py - usage: 'pipx run --spec esptool espsecure.py [arguments?]' esptool.py - usage: 'pipx run --spec esptool esptool.py [arguments?]' ``` Run them with `--spec`: ``` pipx run --spec esptool esptool.py ``` The `.py` is part of the executable name as declared by the package. The [pymodbus](https://github.com/pymodbus-dev/pymodbus) package shows a similar pattern: ``` pipx run --spec pymodbus[repl] pymodbus.console pipx run --spec pymodbus[repl] pymodbus.server pipx run --spec pymodbus[repl] pymodbus.simulator ``` Package authors can avoid this `--spec` requirement by declaring a [`pipx.run` entry point](../explanation/making-packages-compatible.md#the-pipxrun-entry-point-group) in their package metadata. pipx-1.15.0/get-pipx.py000077500000000000000000000005261521706623000146750ustar00rootroot00000000000000#!/usr/bin/env python3 import sys def fail(msg): sys.stderr.write(msg + "\n") sys.stderr.flush() sys.exit(1) def main(): fail( "This installation method has been obsoleted. " "See https://github.com/pypa/pipx for current installation " "instructions." ) if __name__ == "__main__": main() pipx-1.15.0/logo.svg000066400000000000000000002003701521706623000142430ustar00rootroot00000000000000 pipx-1.15.0/mkdocs.yml000066400000000000000000000050611521706623000145650ustar00rootroot00000000000000site_name: pipx site_description: execute binaries from Python packages in isolated environments theme: name: "material" palette: - media: "(prefers-color-scheme: light)" scheme: default toggle: icon: material/brightness-7 name: Switch to dark mode - media: "(prefers-color-scheme: dark)" scheme: slate toggle: icon: material/brightness-4 name: Switch to light mode features: - navigation.tabs - navigation.tabs.sticky - navigation.indexes - toc.integrate - content.tabs.link - content.code.copy repo_name: pypa/pipx repo_url: https://github.com/pypa/pipx edit_uri: edit/main/docs/ extra: analytics: provider: 'google' property: 'UA-90243909-2' nav: - Home: "index.md" - Tutorials: - tutorial/index.md - Getting Started: "tutorial/getting-started.md" - Install Applications: "tutorial/install-applications.md" - Run Applications: "tutorial/run-applications.md" - How-to Guides: - how-to/index.md - Install pipx: "how-to/install-pipx.md" - Upgrade pipx: "how-to/upgrade-pipx.md" - Inject Packages: "how-to/inject-packages.md" - Pin Packages: "how-to/pin-packages.md" - Run Scripts: "how-to/run-scripts.md" - Use the uv Backend: "how-to/use-uv-backend.md" - Use with pre-commit: "how-to/use-with-pre-commit.md" - Configure Paths: "how-to/configure-paths.md" - Move Installation: "how-to/move-installation.md" - Shell Completions: "how-to/shell-completions.md" - Troubleshoot: "how-to/troubleshoot.md" - Reference: - reference/index.md - CLI Reference: "reference/cli.md" - Examples: "reference/examples.md" - Programs to Try: "reference/programs-to-try.md" - Environment Variables: "reference/environment-variables.md" - Explanation: - explanation/index.md - How pipx Works: "explanation/how-pipx-works.md" - Comparisons: "explanation/comparisons.md" - Making Packages Compatible: "explanation/making-packages-compatible.md" - Contributing: "contributing.md" - Changelog: "changelog.md" markdown_extensions: - markdown_gfm_admonition # GitHub's admonition (alert) syntax - footnotes - pymdownx.superfences: custom_fences: - name: mermaid class: mermaid format: !!python/name:pymdownx.superfences.fence_code_format - pymdownx.tabbed: alternate_style: true plugins: - search: lang: en - gen-files: scripts: - scripts/gen_doc_pages.py - macros exclude_docs: | README.md pipx-1.15.0/pipx_demo.gif000066400000000000000000010075171521706623000152460ustar00rootroot00000000000000GIF89a$Œ๗1� H IN EEP=#E 6(7 7 1*T $"0_# $8$ ('.'%/)#) ) * +(+#,, &,B,:g- - - 1.1#H33>3O44 4,U5)8 8C`99:E;,_;Ar<3D=Pu>>5h>;R??F}A;BC&QFG.GVdH0EH1;JJ+#K(NWsOOH~OR‰P3P0ZQT*"T.7T4JT7VTBnV+WBYY*[fŽ]1^Y_52`Gs`V_actd7CdQvep hDUi3&mSEn:nJ`nขpW`s:1sR to‰uK.uL2uM6w~ตx‡‘zJUzTf{^u{`V|SA}SX}…~bgq}qŒน€†ACpŠODŒQŽ| ^e’es’j]“Ž“ขร”\U—i@—‹ฒ—žจ˜s„™s]™~™คถšxtš€iš…@šกšฝ›ฆูœ‹qžจฤฃtฃฏไฆฒ็งณๆจi^ซ€„ญ นฎwgฎŒdฏฆอฐฝ๏ณ“ณฟูดtด›ดนรต ถsถŒŽถ–ทฅฅธญฯธฐซนš@นดญผnพนดพฮ๘ฟำ�ร•oฦ`<ฦฯ�ศพลศู๙ฬ–„อศํฮทฅฯยูะชะ฿๛ัฐรัผซาีืำฅŸิ›ิฌฑิฑงิฟีฝซึฅfึบทึใ�ุิ๓ฺศถ�ยล�ื��ํ๙�ํ��นž�Ÿ8แฒแิยไธฆๆฌ–ๆป8ๆฺ๐ๆโ๗ๆ๐�ๆ๛�็ฏก็า�็ๅไ้ฌ.๊ฮะ๋ฦ ๋สTฺ๋�๋๓ุ์์ฤฤํฮiํ�ฦ๎ฟฌ๕ยณ๖ห9๖์�๗ฺ,๘ไ ๙ห/๙๘�๚ฺ9๚็๑๛ัท๛�0๛๘��าย�ุั�๏๒��ล�แศ�๖๎�เ฿�ๅฺ�ๆฮ�๎ำ�๏��๐��๚�!� NETSCAPE2.0!๙ ,$Œ�-Hฐ มƒ*\ศฐกร‡#JœHฑขล‹3j�ศฑฃว CŠIฒคษ“(Sช\ษฒฅห—0cสœIณฆอ›8s๊�ษณงฯŸ@ƒ JดจัฃH“*]สดฉำงPฃJJตชีซXณj�สตซืฏ`รŠKถฌูณhำฅกถญ�ทpใสE9ศถs๓๊�หทฏ[u…๘LธฐแรIiุEฬธฑใวGบoไห˜3k†L‡–7‹MบดZ8Lซ^อบต๋ืฐcหžMปถํ�ธs๋�อปท๏฿ภƒ Nผธ๑ใศ“+_ฮผน๓็ะฃKŸNฝบ๕ุ๋ณk฿ฮฝป๗๏เร‹�Oพผ๙๓่ำซ_ฯพฝ๛๗๐ใหŸOฟพ�๛๘๓๋฿ฯฟฟ��(เ€hเ&จเ‚ 6่เƒF(แ„Vhแ…fจแ†v่แ‡ †(โˆ$–hโ‰(ฆจโŠ,ถ่โ‹0ฦ(ใŒ4ึhใ8ๆจใŽ<๖่ใ@)ไDiไ‘H&ฉไ’L6้ไ“PF)ๅ”TViๅ•Xfฉๅ–\v้ๅ—`†)ๆ˜d–iๆ™hฆฉๆšlถ้ๆ›pฦ)็œtึi็xๆฉ็ž|๖้็Ÿ€*่ „j่กˆ&ช่ขŒ6๊่ฃF*้ค”Vj้ฅ˜fช้ฆœv๊้ง †*๊จค–j๊ฉจฆช๊ชฌถ๊๊ซฐฦ�*๋ฌดึj๋ญธๆช๋ฎผ๖๊๋ฏภ+์ฐฤk์ฑศ&ซ์ฒฬ6๋์ณะF+ํดิVkํตุfซํถ�v๋ํทเ†+๎ธไ–k๎น่ฆซ๎บ์ถ๋๎ป๐ฦ+๏ผ๔ึk๏ฝ๘ๆซ๏พ�๖๋๏ฟ,๐ภl๐ม'ฌ๐ย 7์๐รG,๑ฤWl๑ลgฌ๑ฦw์๑ว ‡,๒ศ$—l๒ษ(งฌ๒ส,ท์๒ห0ว,๓ฬ4ืl๓อ8็ฌ๓ฮ<๗์๓ฯ@็์ษ่Zะ’๑J2จt„Xย!พไRด;<�` h€K.พ$ƒ‹ึ"€ร(ฐDcL“- d/ˆ@ ‡ภ"LMำq7�š@D"v฿๎6แ: ท*N#� แบ8๎`เั0~ช˜ ถชโƒ7N*ๆDhฎชแc_ž9ซำwฉB„.zชŠW๎๙็ญฟŽ*ไจfn๛ฉฑ[njํป›Jบณ‹šy๑ผ๋๙ๅฎฃ.8+ศ‡ช;๊‹็^{ซQ�`=ๆ’7่z๐�:๘แ8}๙ ~๚งฏเ๘๎ฟ฿~�ชO�ื฿o��๚h��๔ฟ๖็|<`๓xภ�1p?|`~ (ม๛ฏ‚๚ก ้ม rpฌ�B(B�‘0><ก{.จB๙คฐ…๋้ ู๓ยขg„6l sxC๒P=,�!�}(ฤ๓์ฐˆใฉ!มƒร%–็ˆN�Žฃศ RQšฐ‘|t#$�ธฦIฺQ’–,ไ#3‰HLrr‘�dr )ส�$ฒ”น$*้ษU๚†”ฎฤอ)cIFา๒•›ผฅ)[ฉKYVฒ—ฟ™%0]หaยF˜ฦ\-“i›b2“5ศ|&iT)อฺDณš›q&6งษหmB3”�œ6รฉ™k’2ิ*ำ„fดฆ ])N฿‚าๆ%ฆ>ํŠKƒ U+4=ช[†ชTก๊ดฉe้)TีbิฉR…ฉVลJUณ•คr•,X�๊UŸ*Vก๚ณฌ0%+Z‘zำตUญnฝŠWใส•ฐา•)[ฝซQคชืญไตฏCฑ+`๒ืมeฎ†ฝj[k•ย2–'|}์T+ูœถฒ?ก,fm‚ุอโuฑžuŠfC+“ศ’v)ฃ=ํK.ซฺ›คถต,้,l‰ยฺูxฮไตถE‰isXธ๒ฒ �ญPp+�‘ศถธภต(r•B�ๅ~dทฮ�Isฃห‘ฺR๗$ำฝnFŽซ]šXทปฦ๕-xW{ึ๑๚$ปๆศwำ๋๔ฒ"�}olƒ+_˜ธทพ .~c+��–dฝ�ตศ}|๘˜$>ฐD€!๙ ,#‹!๙ ,$Œ�5Hฐเภ&„`ฐกร‡#JœHฑขล‹3j�ศฑฃว CŠIฒคษ“(Sช\ษฒๅH…0บœIณฆอ›8s๊�ษณงฯŸ@ƒž„ฉPฆะฃH“*]สดฉำงPฃ%บPชีซXณj�สตซWซT�Jถฌูณhำช]๛3์ฟฑlใสKทฎ�ป:)P0A‚ข[ธx Lธฐaตเ<1aโ฿ร#KžLน๒ฬ �ั*#"EŠฦ[Mบด้ำ“4 ๑(ธLE@7บถํ�ธscี{อพrELp˜ฐ๎ใศ“+_^’B<อส5baใqชฦ™k฿ฮฝ{๒WŒี�3๖‡‰i{_ฯพฝ{ศnข"ลyˆ๊฿๋฿ฯฟฟืศ&Q~�hเ๖ฤมp˜เƒF(aMNhแ…fxQ…v่แ‡b โˆ‚hโ‰(ฆจโŠ,ถ่โ‹0ฦ(ใŒ4ึhใ8ๆจใŽ<๖่ใ@)ไDiไ‘H&ฉไ’L6้ไ“PF)ๅ”TViๅ•Xfฉๅ–\v้ๅ—`†)ๆ˜d–iๆ™hฆฉๆšlถ้ๆ›pฦ)็œtึi็xๆฉ็ž|๖้็Ÿ€*่ „j่กˆ&ช่ขŒ6๊่ฃF*้ค”Vj้ฅ˜fช้ฆœv๊้ง †*๊จค–j๊ฉจฆช๊ชฌถ๊๊ซฐฦ�*๋ฌดึj๋ญธๆช๋ฎผ๖๊๋ฏภ+์ฐฤk์ฑศ&ซ์ฒฬ6๋์ณะF+ํดิVkํตุfซํถ�v๋ํทเ†+๎ธไ–k๎น่ฆซ๎บ์ถ๋๎ป๐ฦ+๏ผ๔ึk๏ฝ๘ๆซ๏พ�๖๋๏ฟ,๐ภl๐ม'ฌ๐ย 7์๐รG,๑ฤWl๑ลgฌ๑ฦw์๑ว ‡,๒ศ$—l๒ษ(งฌ๒ส,ท์๒ห0ว,๓ฬ4ืl๓อ8็ฌ๓ฮ<๗์๓ฯ@-๔ะDm๔ัH'ญ๔าL7ํ๔ำPG-๕ิTWm๕ีXgญ๕ึ\wํ๕ื`‡-๖ุd—m๖ูhงญ๖ฺlทํ๖�pว-๗�tืm๗�x็ญ๗�|๗�ํ๗฿€.๘เ„n๘แˆ'ฎ๘โŒ7๎๘ใG.๙ไ”Wn๙ๅ˜gฎ๙ๆœw๎๙็ ‡.๚่ค—n๚้จงฎ๚๊ฌท๎๚๋ฐว.๛์ดืn;‹ฒไ.หํpb ๛๎ผปyม๏มฟI|๑m�šส/Ÿf๓ฮŸ }๔eNO�˜ึ_f๖ฺษ}๗]~�–โŸe๙ๆ_‰~๚Uฎฯ�”๎ฟe�๒?I�M��’๚๏Ÿd��;XคpH<`จภ1ฐ=z w$ม ๆจ‚ผ3Xฃ rpF�`ŒB(ย‘ฐ„-:! Wคยฆจ….< cขาะC6ผก†rจC ๑ฐ‡๚!�%$ฤ!BจˆFL“x %2ฑ@N|bข(ล�PฑŠ๏น"�ฃล-ฎง‹^์รธ1’‘9f<ฃrาจFไฐฑบy#q#ว9ฺฆŽvD ๓hš=๒‘4~�ฃe)Hสฒ’9$"!ฃศEฆ‘Ž$ $#)˜IR/–ผค]2ฉIบpฒ“r๙$(ู"สQชฅ”ฆD *Si–Uฒ’,ฎ|ฅWb)Kฎะฒ–Zน%.ฑขห]Zฅ—พŒ 0ƒ๙”aณ)ฦ<ๆR’ฉฬค0ณ™Gy&4ƒ"อi�คšึ์ 6ณน“mr3'��ๆMย)ฮšณœ39':[ขฮuฎค๎L <ใy’yาณ$๖ผ็H๒ฉ�ฯ๐ณŸ๙'@;"ะnค อBz‘…2ด"}่D"*ัˆPดขน(FขัคฃHCช‘†ิคE้FUŠQ–Vิฅ…้CeสPš&ิฆล้@u Pž๖ำง๚๊=…JOขฦำจ๎D๊:•ŠNฆ–ำฉโ„๊7ฅสMชfำชึฤ๊4ต Mฎ6ำซส๋1ลJLฒำฌพD๋.ีŠKถึาญฒ„๋+ๅสJบฆาฎฆฤ๋(๕ Jพvาฏš์%KIยFาฐŽD์"‹Hฦาฑ‚„์%หGสๆัฒvฤ์5 GฮถัณjํEKFา†ัด^DํU‹EึVัตR„ํeหDฺ&ัถFฤํu D�๖๐w!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,ก5(ยฟƒb Hะ`ย„ |QโDŠ#JtˆQ#CŽ14hแ„ )L€|(าฯ2๕”นQ‰1ฃK็ิd้ิ/ฬJˆ.M{มเC/b?�œฦใย…6ฬ’ฺฺ๔)3 ๒jbšํEธzI�ท๕’ฝD] ๕‹3ถ,ฝy๕ภJั6จ0$ค [“lPฆ๋R �ืใ@ˆ1(^Œq@!๙ ,ถhHฐเฟƒBXฐaย‡ :|จกฤ+$ ร'!0f�Q+�t™qa… `iกกKŠ.cX€O˜›F’ยC'ฎ\…tไB!6ŒPฅ”ฉะ…ElHฅบ๔&DYทV๕š1$8x˜ † $ฎ ‚:ืนyืฉE %:ฝ{w*…�‚ @€ ยฐ฿‚&ถดxQ.ส`ุฬรe!๙ ,#‹!๙ ,#‹!๙ ,"ตะ@ฐ A�*TˆA Aƒ.l8๐แA‰Zิใ?Š9zLrcGŒ4€๐@B†&NJL้g™ฟzส�ฤ๙Q K็ิd้ิ/ŒฬŒ\š๖‚ม‡^ฤŽ2๔ii ฺ0“Jา็าซY™I7’ขาl/0ภี‹kฯค—์%๊ฉ_ทLมPJo^=p€RเMกBฉ0$คฬำ์W ๑zต ๙bYภภน3ล€!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,+บhHฐเฟƒBXฐaย‡ :|จกฤ+^$˜๑`ฤ:�[Xก‚ $B$H B…H’1บกbขฤ-[)^่BG3F)DE�aBง‡ศไ;ฅK:‹ุ˜0aถR>0„์ธP๊„ตฐ;‚‘+1š@รฒl‘:C„ศsŒJ -ื�ุะู`Iพ:!4,่4oˆฮ Dˆเะ!Aƒ–:^ จตlฦuฐX†ฯ 1l!๙ ,6\5(ยฟƒb Hะ`ย„ |QโDŠ#JtˆQ#CŽ1tฐ๒กว†3ZิPฒ"ษ”�Nlฉp%อŒ!^ฆฤ “%L tvด๙“่NฃC%Xสณ)ฯฅ!๙ ,>�5HP€ƒ@๘วฐaC #&DธะกCˆ NSฐlะนP๐ @ม?†82๚?!ซ฿ผ~0ใศ„ย‹ท !๙ ,#‹!๙ ,#‹!๙ ,Pญ5H๐Ÿมƒ!\จกC… :D1bร‰+Fฤ˜ัข@Ž� 6h‚&L€„๘มP5�”)Qษย… y˜�แ๒ +/x8T‹J H„XฉแยŠ[�4…iA‚&Fˆ hัิฯุฬ•8Svๆญะเมƒš1:LLั๋ฦŽHฦ@น2C!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,m%ัHฐ มƒ*\ศฐแA #Jิเฐขล‹1N�˜ฑฃว7NIฒคI‘Mช\yeD–0c"t QฆM›h��ษ๒BN”<ƒ–์ SจัŽ4)]ฒ(ำง “Bš๐'PชX Z™ต+€ญฝb•*–*ูฒP“�[ห–-ดAนhK๗-\จt�ฺฝห4ฏ[พO�ฎ� ุจเ„ =œX๑NฦŽ nY&ไสฮPขDŠฅ4Xd0Hย†  0gJๅIตชีซXqv`‘ฆ5r๎ะ}CG,”:ุ่ Pกƒิฉ=bภเ๓&…)R` y7oึฟ๛ฆH)x/เ‚>™bฯ]cwƒ๐ sดŠ›;H&lภ๐–f\p์„qณDzIV€ศ„แรฐ Žุ3ว5Jำจ_ว�JŠ\ใxฉฑน”l9rข ˜๐ฑณL…ŒPษ!ฐยVฎF๔XcW)#X�šžค๘i>rะaาฃOgXยšผไ๗Nˆaํ>\ภืณIyq็‚0๐ACผvƒญื�{#๑Pcภ=5ิt่Ž;ค กย^ฮลค ึ่aร^ึaง�@U 0้‰$ภ%ไ•ดc~8pE?บ`X‹.๊vะ2เ zM0 8เ‚ 6TเีxcŽ1ฐD>a๔#… Iค‘!1QL‡pบs\œ�่!ยG 4มHWLyB *ะA เpอ"ˆ ย#Œ2สƒzhเ0D{yิ€‚j่(jฅ†"ดฃ0"๔้ˆ �๊่hP`jจ…&ดjซ~@<๐@ฃฃyช€ซ)‹kชqd*-0(Ÿฝ (ˆ`‚ $ จชเSฌฬย\gพๆมuูมg,ฒึ๊ มฬุมAˆŠ Gˆะmฆ›ยธA.่ฎปย›ะดd‚ ฮ*ญดz๒้0ฤ๔‰eท„�{P} ใุˆ${๘˜cŽ๘ภมฏ~h“N=�ผ‘ w€ใฯ8n๘ด2ํ๔ŒO=ฟX7ส5๏�๘�#ฯ/*„=๖ๆ‘ิ€หpญŒSŽๅ~=ใ—฿ *,„Pย-ถ๘z”|:คธ๓ )๊h Iควรฯ9ไ€„T่  ๊ˆ1 โ|เ‚`„k AุคXแ‹7ŒaุC6O๔z$ร@ม 3ไ xุว&ฒp…}4‚Yู<�๘€„N์ร VAพ<`#ƒ8ด!ž๐8ภ@!;ฒG"บ‰~ฤม:vx"ธ@†f`‚`ƒ—เ}4ขp…/dHCvF่ม\q;(8f� Ud 6h„f|0„$€™่ฤ ’&v/ฌฮl7ฃ4ฎฑ˜@&XA'(KIใ9|ฐL ๗H‚ะยฎl= D‚ „‘ˆFDโข@5dก�C๒ธว>๓˜ษ šมˆˆ";0AB:ะฟ�แเ 9พ๙อoฐ/ศ^@€�#“�† :@‚€"*ป€ šก�‡„€Bs…เf`ยiภdyฌใŠQx=cŽQŠLเ๗๘แ0ีi‰lฐำ‰๐  ์ข7ฤ<คเเnฤผ„G0\ใ1ุ†2`‚ ย>pภ1มƒ 4@ฦม๚ะˆข w ภ&ษฃพำp‡จ๔‚—r@Ÿ�ฬ |า‘–๔ค9ธ—B )NยGจD5*ROะƒmะ!$@€xฐั\aLุB:–…y •]yโ๐Š<ะาเAo‰,ฦ"Yฎ๎Œ0‹฿JGyส-wน๕‹ๅิ็2k)เ0ฬ๖Vว9ำ้ว2[X๑„Kํ) œ๋อฺอ@ู…็T7ๆxK๏:G ›ฐ่สัˆห�ู฿ภ5๓2มS:7€.�e€ ์! ค�7ด 1FำCจ@/ุึD?๑‚นธt„svxB 6 Xภภ.๔ฟG#ฌน€เ)ฦย%N‘ �มB@%คAuCnด`าI0ัP๔ผซอฉ `gณ#8,@Œ&ิถH+๑ฎ‡`70ุCIฅ‹•1)lชศฏ}์;R<ใC`Oc`a‹ฐม๑ฒbฐใ๓์™m1[uฐร๔์Hง๚ญไW`‚ธF/˜A<ุrร”*ฃŠg ๛H„๎)NgD‚[4„€xu๗฿qgล2�ภ…๎ธw€๘ย ๎|?›ษ็XC�†L€๘8ลฦ`'๔” ๚ฐEฦ!ศY‘tVศ8@ูปแ $`xฐvฐ_๐^`Fๅq{z {f&}ิg}ๅC๛s๐ฃBHุo0$F�` 2T0tะ™๐U€qฯVฏ‡B`=F|ย๗ฦgษ C(€ใ๖qZฤE^„%ภ�ืƒ—„]ฃ‘วฐUZp 1S6vฯภ�€…ิ@ /N?…`(b๐ฑฉ -<�  ฿c3ใ0_ำ]>๙` ๋โ ผเ€ ™ คA1  �p‡,Gผภ|}๘‡os้�นะ€%a†hจ†™๐%ะ E๓ฺ๐)ะw  ๒เเ 0ฐQ˜๓9์C>†8‚3\`นณa7รภมภ tฑˆ€จฐ‰๏๐ฆ–˜†'ฐ†ณ[ฃ6™?๗B‡vˆ‡Iร‰/๓‰พ"ˆฉwุ66Vฉ07ฆƒ86\{ภŠ�0๖S_ตŽEะ@ณˆƒXˆฆภเFฐ5ฤุวˆ‰—6y3|ใ7ŒœHŒ0ฐ#๔€}ŠcRคฑ0`/an �ภ โ@ }๐"N ’� 0๓(#>q-* .ท/ � ,ฒ@ฒฆฯC`*;น}ฒ2A™Dฉ'Kq0V๑’“3)/ศbT”ข0ม0<ๅ+ฏท'ศโทR)<‰-ฑ”ู•๒’*@•1นL €)ๅb“.ง•รb—ืข0Œf”v/ก’_U–๚,ยย•๑ �๒)&`ั” @,!ฐ ฎ๒– @(Uู'‡7ฌ‚˜ืโ– ำ+ะย‡า๚‚„B โ›ฯ๐ Žภ ต›"๙ G0#1แPVœก)ฑ–.ye0Z0 ๖ะอGœ 1! 9t SฯI6F…(Aœ ษž�‘เ™.๗๓q@ฒีู!เ0›ฑ™’๑Y ฒ0—•"ยฉ `๏ะ้0 f mฑ1 zๆษj ”b@ ๑๙ โะ>เRัUYชX ฺกช]ต-T`|ภ…`J?๘ก,ฺข.๚ข1„๒</€t0šฃ:บฃ<บ=๚ฃ@คB:คDZคFzคHšคJ๚Œ†ฃK๚คPฅO`RzฅX*ค �@ e y!žY:ฆdzˆBงh7eฺฆnJ ŒvhฐๅP&ฐ2ošงzJะxะ ว6เ{Zจ†:pฦPฦ.๐Šดก‡ฉ’Jp ง 7'•“บฉœJ!ภbฺฉข:ช๐ˆงคzชจz!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,$ 1� 8มƒTธแฟ„ !*”ˆะแC‹ flฃG‡†|ˆกคI R!๙ ,$�5H€มƒ�)\ธม‡"dHั!ฤRdh๑bฤŒ66๔(€{œ8„H(๒_ว‹ฎ๔ำฅ’ฅH @x !C *(P€ฅ€67โ๔ณฬ_=en€ฦฐ๖Ÿ0HCถ„ ภา95Y:๕ ยC'ฎ\…Xฉ๕ฆ€Kำ^0๘ะ‹‹FจRหถฅหฎำx\ธะ†Yy๗ฎMZ๑mเม…'ๆห˜ใ�l/0ภe—“`ม0A‚„_ฎ—์%๊ฉ_!8p s ‘›7ฆทพฅ7ฏ8@)ภPขำปwฆ`œ~+L„ )‚ฬ B หแ๒ภ๐ะ ‰ูs/๒๔E1จ_��`@!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,$$จ5(ยฟƒ Hะ`ย„t๘๐`D‰(Vผ(QใCŽ =Blะย R˜‰C?ห�ีSๆfeล–,S“ฅSฟ0nZpiฺ zร ๔†œำx\ธะ†ูBŸ•J•Y‘vY‰f{ั€ฎฅMตฺKิRฟ8Aร^ข7ฏ8@)Xฝ$L„ )๒ฆ%•ฃ^ง„yN‹€c #cp!๙ ,,$ท5H๐Ÿมƒ!\จกC… :D1bร‰+Fฤ˜ัข@Ž� Tจ A‰ จPRdŒnจX„(qหVŠ–B่hฦ(…จh;Lด๔"™|ง๔a้@ขe&LยVส†‹!€ฺฃถsG>r„ ๓(cX–-ฒัขeˆyŽQIกๅฺ-,ษW'„†ๆ i9ก:$hฐฒฅภ ฏŠอ๊๋Dญ"ภภน3 !๙ ,#‹!๙ ,6$ส1\ภ€กC 5Xศpแฟ‡!BHH1aร†3NฌH๑"รŒ7rD่ั!ศ‡"Gะa‡&OฆไHเJ?]/c‚„pc… `i ƒN#5Tˆaํ>aŒ8‰2้=žธrๆTช3^เ@a„*ฎ^ฉ� KqƒYดGC&E่๖lืธCฬฝ:  $Hจ…€a/:ืนy#์…น0”่๔๎)ƒ็*ะ )Td†๙โ่ค%ฟส™š!มื!๙ ,#‹!๙ ,H$�5H€มƒ�)\ธม‡"dHั!ฤRdh๑bฤŒ66๔(dH‘�:^4‰2eƒH"…ภ— HaยDK �hKWฏ�›>ชๆฏŸ2%>QB`้œ$ฉ๔มภ!ณ?\พภaAโง€Kำ^Xq/‡jQI‰f-Mใ1แC4E8ฌธๅOS˜$ขŠœŠ–๏‰ฟ!B$X ES?cPอ6๎ 9ฤƒuAkลC๓^ฮŠ"�ู๓ๅ‰—f˜JK]*X‹ 'บe๒เa2�่ebWj‚*Tlจ03„ rŠ๙งภื๋–ฌ+ั:I–RWš4Xท/H˜pb™ ีjฎSaฃj @A"าาด(๗_D‰u๏ภทฅ฿–|!๙ ,#‹!๙ ,t$Ÿ5H๐Ÿมƒ!\จกC… :D1bร‰!Lˆa >\Bฤ˜qย8pแ"!๒"FˆFฐrข%ษ7xPEำ&IˆF๐ฌ9๒งภ B{}y4)Q—s:๕ษด“hฑ0A‚ฤM…6pศƒ ะ›9^z‰กD*๏Lฅ๘๚ดATฬ5j๊รพ~)†รˆ1 !๙ ,#‹!๙ ,#‹!๙ ,~$ร5H€มƒ�)\ธม‡"dHั!ฤRdh๑bฤŒ66๔(dH‘�:^4‰2%I 

!๙ ,$�hHฐ €ƒ�[ศ!#&Lุฐโรˆ'*ฌ่"ฦ7r�w๑#ศ#–4@J’8lP@‚„)$Pกโ`… Dผ|่มรj๕”อษiศX‹*œ Wf(€x๔mสขฉ_˜lฮ๙X@cB{iฌF8V๊E‚ฝˆธ2๏ศ–tKขฬ#2TCfzxpเะ†YŒ(๙ˆ ยถg ผ#Vk\ |แBแ=่ลyuˆขl>ิฒ}ม`.b#N�ŒJ-PถxXลช•ซื�Qฑ!ู)n0jญ^77&๏ูV$D”t\lX�pฆMœ4@P`ย„๎&ฌฤ€!cย.ƒ{�ุR<ฦ–ษห'0 !๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,ข$ฃ5(ยฟƒb Hะ`ย„ |QโDŠ#JtˆQ#CŽ1Pุ @*T€|ˆมร‰Gึ�ฝWd%ฤx์z๒ ›  š'ๅย$€fฌะใV:TZZไP๚O$FPอBC Hศฐ'Iฦฅ{}นqg_ขฎ ๖T๓็\ฆŸgซVhpR‚„)บZิ ุba†cXฬCโ€!๙ ,ช$ง5H๐Ÿมƒ!\จกC… :D1bร‰+Fฤ˜ัข@Ž� &h‚!B€„€C‰\้�ั๛ใA%Gˆ#บ|‚+[bx มฎV9Vnุ€เถwศฐตzำƒ‡Nว๊bm๊ส '– "Ab@-ฏ7'Œpๅซ—Gฉb„XขS5๕ด�IฑR`’$HHม๗ฆว‹sฏ "ก!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,ณ$มhHp „&ฤ ฐ มƒ 2t๘0ขฤ†!Z�7‘ข;f�ˆa‚*TpธBy$–ฐfJ%‡GฺXไ @คs>&|H& dD†ช์3Aฦพ8F/NˆN+๛คD]`BAั Dšๆckษ†5š•ั†)‡Yž€ฺŒ� แง๋B‡ทyeุKƒแ#^nhก๕KEร1DaVOำ…3ผ ณ@ฯB‹f่น3้าจK!๙ ,#‹!๙ ,ผ$ #5Hฐ มƒ*\ธ๐‚B !"์ภฐขล‹3ppc@!๙ ,#‹!๙ ,6ญ�/\xะ E *\ศฐกร‡#JœHฑขล‹) œ๒(S„CŠIฒคษŒฝ`�gฅรษ—0cสœ ๅส–4s๊�ษ3!†ŸึุUŠIย  qB k�๐แ๊"a k๒�Mใม”€{œ8TํIVฆภ*ุ€๙8บ$_˜‰&ะภ ‰ฑ dเ5mญBW๚้[ถ๐CกƒJ1)€D‘จ"6&Hว6P‚ :l  €ณg*pก/L็ฯฉKK`ก‡ฌ\…๛Sภฅญc+ภา"tย <˜m๐x๒ๅšCคะเๆ)8lP๛ไฯ Sg�Aaปi๐$‡ Bลn๕Ÿ9\่ย„@^ฺ้Cภ พ~XDฦะU��#Lิไทิใฯ8sx ฺ Xใฯ;ใฑมฏดƒ>�ศ๓ j1ไ’N=ฺ’BBx Šnc%$@%}…๗TTยHxœฺœุอ+ะภฺ\จฬA>TมฉคใO>ˆคเwTSฯ‘)$`ˆ1-TUม ๔”แ…b๘%˜่=xยUฆจภhS5€F:๙8ัภ๕๔iž yžlnพŒ1(ฬ˜‘CxุCศ_ภƒศpล>„ขyPฦQ@ยฯtPม/๎�ถะŒŠ*ุรฎภ(ฃ%็ศD*๛ฝ*ศ5™|QE3›ๅPjF`cฬSภ๑E ”๊ณIš๔Flœใร4Lะร=iPj)ฆš&4B_@6|๐@$�N๐A2ขpZำ (๐Y�I`‚ Dซฒฏบ2Š8ภ &๒2‘@BM�ฐฯ?QuมHภ ๅ4ฒ�†ฑฦภม ]ภฐ๊บ๒o/ฐย=‰Tร6‚ Œภ1ษ*9‚/ ภ ยh)ม1ฅผภฝcภ๓ฑE:KD1HL๑�„ม !ฐK+“VฑศฐO %ุ๏g 4ภมฑ ภซน…�@ยตดยC=�’*Zด€ ฬฐ๑B  ๐8้Tร$'ว,ซBzณจ๙XXฒ•ฝั(B#4ะส6ฤ€�‰PตP อ่มรœm0C๙16{Œฯ„Žธโl€ภ!ฺผƒ 6‚;Ž้ŸJแRž{ๆƒhผฃ&จฬ9ใ#เ‚W@šจ`8„„?ฎPฉ€SZฬ ! ,ณŒไ[ˆ�6็›ั tง�‡ฒบ Œn ฎ—\รvษ] z@8ผโด@D6|@๕ตo๏๓@'ŽQ‡ มัซ€ ขa€HิจeP‰>ฌ@€;�‡ jศผ�ฬ๑‹๑ํ|ใA(`ฑืูs!˜ภ Zร0ค`Dy้$€pภ“  L6 า(โล:p0ยd$Ay@:EˆkŠฐุฯศ ม-HAล”ฦด0`ธ ฦNp Pƒ ต…-”ศD8Q8ม2a1๔)‹veะ&r ี`Ÿ`ย้D!*ุ€) ๖‘ˆ1ผแˆป๑$๖ˆ/�เด&@ฒd8๊ ปฤ<๘ฐ„,”CTู๒€ @ผa3#@5‡7„ฦetธ"�€ฬ9ึ1.จุ"ฦ€‡k๐‘!ุภœ๕‡/t›ีบVถย0�‚ �๎*X618^๚˜‰˜ภ\แ‹:pa(‚“(ถ0r!bู@:FA†P šx’)XฐW๐ข‹@F&T0=Tร�G&` L2E tฆ ีฃ่€Kะ”Šœ้ฃMส‡)�4uโ๏0 Š`์๔�ศร>‰wิใ๎ไ๘R,๐ขLฆHA‹๎`ŸบaPุร6Š‚(คˆ p)LeJS” /Eั‚t:~่ขU ๑ Xภ)ลฒภ8ก€J#$o6xzš r€œฮ`งesPCštฆ*ชs^sTา( ฒ›๋,R ‚๏ศญณ้dปftฌ กŽ–C›ฃ]€;ุI0ฐา ฌ๋๙ฌขKยสภi€ฅMX�„ร 6่จD$๑†– นžn1ผ-la Zภ=ภGจˆค!@‰ˆy)r€h#๊่vญP�(ผ†Iฐ+`ˆfศใฎภ ๐ฆเˆD๕ะฤeด €)๘E ภb๛Tธย๙m*zOŒ_ \@l่ศ(‚ จจyYฬใOdล ูฏ‡LdŠ!๙ ,6 8Pศ!„ ฆ 1!D0T�๑ย…ƒJ1)pPT‘ƒ8xiง /๚!ผMบ|N์iศมc*T4HะPFŽ !๙ ,#‹!๙ ,6Ÿ[�จก ม*\ศฐกร‡#JœHฑขล‹3j�ศฑฃว <ฒคษ“(Sช\ษฒฅห”#IพtนaC† :คผpA†:g J4#O ?ƒ]ณเR•F * ฤNˆ…ฐ๚ดซืง<ภฑณ๕+L ะšฝุTแ„ๅ$PjฒยVฎสฎ๕สณATจ˜๋ะ/„"TeุW`"๔R4 A‚„) 0B%‡@ปx%ไ™เo`!่"ฌฐAcภง&H๛4ืฝ�&œ€"๎ฯ” ๓�ฦ]”็N๏�)sณธแ?ี�๛Cxaุ-ท�IŸใaธฤ†���ซื  ี ]Xำc€เข=^ิ;้zๆ3ดปL�ฟ�ั3„{ -‹<�ษˆgM้F�C5PมˆEย('”ำP j`ุd‚ ๎%`‰mี—iEZ $ Xdf5 ‰&ฆะุŽ)ศ„ะ‹,Z8bbpเ˜ &่4›`#–xb•—ฅ กePNฉ Uร6ํT๓N;ุ˜มƒwŸM6๏Œƒ 6|๐ภPTค“K5h๊agETbS�ั"5e ย\ฅ Vื|!“#Œ2สฑuะค†D6๖ุ`่ ฤ“~Œั(ฬtฦมBx๐S@�a„5ภดืะฏ”ฦOฐ@BZ!๙ Cข‘‹?๔0า]TXใO.�|ุAˆ ๘ฑŒ?๕,‡"%ไ’ฒtวมQคr_>ŒŒp—+UฉgŠ bz[6€)โฺ �Bz€ฦ-๕€ƒH{j)ฤSน็žโ๋ะI. ฃคฃD”lแ#6|`HtสqaBY,“ฮผ3ฐ>�Lผ‚5๙4ก_˜กลŒาN)k.4A ฬ0ร…Uด“lภ“ Pะ,ํ`’CจPษ4Hง ุ%q c@•žvน ( บˆFฤ็๎ƒ$8!ฐ‹?ภ 1Uๅ,็mร$ AAม{ ๘กiฅ๎ำxA {AŒ๎`เะvัŠ`า@๓œ*Xƒ/p0 ๔ฆbฺ†  €?๒ภว*ะQรW น$>แE‰`Qภ3ฐ‚-`cL ƒ�N๑Ž@x์6@ ˜€–x€๒ภ[Œ ๕ณEpืŠ?9„h‡/ว\กกh6บ‹}Hกh!@�ำ�dPม‘กhQl [ธlf‚ใrA3๔ƒศฅอ€s3œๆวRผ  `คบ\1 `@ฃN;ก˜ฌั}„!)€ |P†D`ๆฃว~3น`ัฑ!@‚๙ดฃฐี`pž้$ y‘จŽjสQ„๘dอธ‡ E/๔รพ‚ๅ>ฬฐ?z๒Ctภlเ…v�cŠ f „hX๖�ะE. lŒcฃ€f N˜€B๓ Rฺy~�|‚Bภ•=๑9 @Aš?“Dค†|`๋CX7มƒ„*ม๓‘จŸtŸ  ๅฌมD!สฉ*ชศ ฎ[N‡„%Y˜D3ิd� €€�[D(ฐ�ี)-`mะOิ~ิB Aš ‚ฃ๊A JeชZœŠHDuช6่ฤ1๊ XƒซŸkฺ`แ }๐ซ H8๊€ƒ)ุ#pUCA่Š=ํึม &B`6a•‚R ๛A„€�C ฐ!ยใึ ๊fpˆj ฬ )ˆ€5๊!ˆ-"ดzรU=ฑunp!่„๔uC1h฿๛บ&ๆงข(A-๚๑‰ข฿ขห E3ฺQNsใ#ธมC@0ฃ�Hƒ์๖๓ภc�ยผ8E�ƒใ~๐Uไx��rง“๎฿ŽีุS�๎ €,ฌ` ^ฆ@Qํฬ ฅภp `ๆ c๐๊2#Šw›ะUร“h”v'`p0L”;q8e /3=ฐ€ร� ภ7pฆาD์wj#av ั `,ุา ำ*{€ ฺั6จƒw—@๓0๕ฒ*%€�๐ฺ@“ เ๙` ภุg ๔Pš` @ฐแฐฬี*�’ƒ\pBGƒ0Eh J@๐ัั ะ ๐:aฐ†€„P,เศp� ,@#Sเ-3˜ iฤธCAผๅ@ึ�๐๒เ๒D ะ ๚ ฟฯ๓ะ…\q‰™๘ะšภ1ฦเ6Aมw€2N`Q‡มะช1.C๖C๒ี~]p-M่#€}@๑ศ *7w0eส๐6๐„มภ s!ŒฤX4Qแ‡ฺ‘Šzม†qpQa@€ฺ่"qƒ xี6รั‚Mๅ‚ช6‡a5ก0†ฤƒย Dา#+a/ `“’(ภเ%~ฃ˜- tฑ๐ล]ฎ๑)ํE๚%”![แ%ฅม$๒ฑดtr•’เ@า๔P*ๅEฏA‘P#“าKb๐’ “�˜’‚Qญล ^& ตศ~ไ'ซั26!VR*pœ$5Q•9Ž7yู# `’b™gฦk&ษ ฐฉ”#,.่ƒ ๖–€k๗G,s)•#่กg65ฑ[0Z  ๛01! ะ‚้™กrfœๅ หะาPo™“9™A˜“้+ gิก„Z@ บะsF1ฃ›“i—{ษŽA9xษตYP†ะ ๒๐ธเ1&€j{Qdํ  RภdD. \๐qT 0B้ C€uบ้d�"แษำ—D!›ฉœ{‘Hม•ฤกtฒi:‘ฟYž›๘นŸ‘›�๙Ÿz�G‘z *ก4<‚ ก 0ฤ W1~กš€ฐ-ชšก๗นก1ตก!i[๑f ๚Ÿ%` �‘—๑T4+ฺขพฉy1๐ˆ๘€ ำ๗ ๖ภ ภ3๚.ฺ0zv7๚9บฃ)สFเ� (ฃ๋‚KMบก 8 ำฃ/ฺ` Yบฅ]*#G8€ ธ@ผๅ๐Nบค1ฆ\๊ฅF ฆ2Griบฆm ขภOlpk๐APt–า–C’%=�b Pฒจ บ,ู)T™)Ub"�BสJ Q,‚!@,9ช@!!Ki!Œq ’ ฒ)˜š%^๒ (ตนฉ‡ฝ & *ธ–๛ข -Hัซแ‹3’•Cฉใ4Ÿ8โDยCน(ฤ๚ฌีชDาญXะี‘ฌx๊ฌฃบมชŒ ฬ*ขโช�บฎJเNปu!•ฒ6�‘a๑/30๎ก1ััส01� 0๓eช๒B/)ะ*y€ƒ๕0n‚ีr-ูโ�"ฐ�@ฐใ�๒-:ี๒นp.Œฐ1x,ษ›ึแw“Wmฃ7ฦ87ส�ภ/ `ฦะVแ@๔PนŠ/Exณ0dvฺwาม€k๔0@C! ..kด7แฅ+j ะส  E‹aยฐ#ใฐ ะ๏ ๓‘wะ2๑Rถ๕ฒRJ* zƒV;S;ฒ!เดP ๕า•ต\qฐ�ชฐW`Oซ’DKิDO4z�ำ=฿s!ฬ๐\๐pเ+ห=�>ำy$#ฐ<อรkพ€E“08< :ฌใ:ฐณ–‹นškฅ A;ถc_<ภ7 ขฐ&ž@5OHบฮ=๐ฎเฎqฟ1ฟบ�พ๏ฟv‘ฟpง=ž;นก กˆซธ‡6 ฎฐkดQ9ป KดdK,ภ‰C)0n?KณTKท4ย๏JJฆไ!ร3• าเxฉHภH�ฑต`ร8 5H๑1I•ิ|๛ฐIฉd`{ฤˆ a$eR(ลARŒdW0Gฐ้ฐQ0D@iG‚ะเmใ Œว@E<4ธฆk4ไkƒ˜MU�LI\dม]9L>๛ Y\›GQJงdขเ }…c5•@|JGCLมไ;5ๆ4ล‹\I…ผk€ศ๘๊มฐรงh.c4 c4ฦ  Uฤ†ห� /c36h$b$fbœเsฃ‡L8ออ ๅิ�ี(afaๅ„ลํL �๕_B4PfิŠh?๗หฌ๛`1ะ๔ฏpด€C3™)ๆำ ƒ�ะ�`อฐ g}a-ำ‚yสz=6d‘Pฆ b#Vb'6>4พ€ภ&เูYฺšbูรaมpcึ&ู_ญำฌ-GMี4ึง�9ิซRิ”nโถล$เ@!HวภUวnใๆร๑ึ๖ั€(•2 & ศ—@Aฏฬkฒl��€�ๅฅ3' lyคhjsฉt*T|�“้�สํQLMt'p   TP  ` ศ+sฺฆyฤว"5า๐œ4&‚GE|ฤIฬิฤ๐kใฝJ$€ฦ6เ•Pเ0ีญ2 ด ส์ฺUฺเ4�/pmrัำ[ v``เิ�ั !:$hyไnเ$0^�”ึเ ๐เƒ7๎แ$ kมMn)ส�ผ,Hzฆ‡zBp4vฐ_๐^pผฃWzง—zผ…k”gy˜'#ฬพDิบ— w|ทWžๅ[: แใ€'x„—ฮ๋ผึf:iทvm็โฌjt}€R”€ 8�๐? ‹ภ?<*#wแเy ืวฯs๐#'=hฮพซณ]`‡T็ƒWxq้™>M๒&€๐s > �O๔ฅc~yŠPp+ฐ ืHไ{ ทŽyฒร7tp เ๛X‡๊š�wW๊7�้กปยฌ๎๊zฤ#ˆE`�ค €ถMฐธ€}Uะ๊/ู!\ฐo7จ๔ ˆ!@†็>gx9X๘-่๓Q1 �  ้@7ทท=๘ƒ~๛ฐฐ๎ศโ๎R<„๏Pถฟ�+/)ƒไแ1(๎!@๎ฦˆŒสx4{€า!้ฐP๐‰% ๗†ฬฅW˜ ้ะ‹0ฺ่�ฐฉฺ0k9ก๐Ex„0ิ๒/o ฐ‰% Eh 0Tก…š๛Q๏Eุ  8.‘็W˜…3ุ…:!๔ศ๑ฆpp.๓ ฯ๐Zb๓ฺB BO๔\Ÿ๛V†่Žข�ูx=‰ั`ซข•jIงั*’$[1. aช#ี)pิด— ฌY}oฌrBžš%44)เˆ9๏(ฏ QvOฌ •SY•˜1 PโP50h๙PV‘ 0)*[ฉฑฉ&C“ฟ ฐ/๛\‘‘G/๖๗I๘ฐZjฉ˜ม๒งสaู~ฆ‘ƒgษ๛นฏ%$ทพ๘ล�ฅ1ŸŸ>gทๅŠเOŸ ฆ‘�”ูƒY˜‡I€.ถแ�q7 ด๐ กข‚ 0๐  ;…U๊TชUj\Z๕#ร ฏjV์Xฒeอž%›ํZถm�พ…W๎\บuํ�ล›W๏^พ}��X๐`ย… †{แBƒ จขcว*Tpเเx%ม Tค  A‚&*TP @‚„)Ng‘0Aหˆ�m฿ฦ�Vq”T๏�ๅcดฒมp�ฦน้ภแ‘2,|ฐ6วฦCี�๕SฆฤtŒ\้๊iไ:8Q:$€4oHื�ํ�ฟฉ˜เรŒ’๐#็ห›/&ช‘1น†่€ๅ„šPมf๔ฐ!f�เโ 8X aส˜" H๘Yc4exเcฑE b…X๐ "Š ˜EšHเ‘„ bุF%ถจ&8๒มย…cจฐadšH!Bธ€ $`…rq‹kธฐ!‚fแมฦืds0ล๐hง|�ศaฃŠฬฤdล‚@b5 € {Žpก™ 4ั'�-–ัn…[าั$Œ€J „GภIง~B!ศ]6แAL%lฐณMSOๅ๋‚VbŠ0†HAMƒ๐ิ“ >�TP&8Ž\>‘†ZX0!ดะคcด+ p๊ภa {$YI€Aฮ9bZ`0ม9Tฟ—ฎ‰0(ี"…yก2ภเว ‡EPtfšf1็“8xภ+Ži…v)ๅ… >ˆ†Z&ˆย8ข dณp'ฆุฏฎ่G�สxc‚�ฎ@ˆ„ŸB˜DB8มŽ=พxย‹f๒ti>–ศขE:Fฑฦ)Tญธhฃํฺ-•z~3ๅก�ะฦธ9Jเ�z†` Š|ธภƒ=๑วz2a! bะ$�้ๆž9ธ lTไเhป๏vkฃƒ@„ศB 2ษfณฌ ใภpฬ๏•(@MสT`_"8FZ๑ๆผsภพ*( Rศgด F—ๆน6ุ ƒ ฬs‹บV๑ผ‰ˆ qจŸE^Šg!”๘ฦ7คเ?R@C ๘; ๘"ฐ�z๚aY60D”กrdA1~เd@จh�๒‚4<ƒ๐4พAJdภ �  H๐๔ฉ….„ก Cธฤฑฌ’HHSG9‘ผเ ๋ DW†X”ใรจ ิB:pแXP‡;� 8แ‚’กGฌˆE-’€‹^ฃ™ธว&zIMฐ&•k~?ˆE<ผ!ัeเรฦDS‘4‡-hม กFwP -€ <@ป@J‘ŠdไบษI๒ั•RูG|ษi|ซอd๓€ีด†3xภ็œxaV๘ˆ฿~pลu4ำ”๘ม $;H@‰�9C1  oPรiTฃ:ผแ nl๓œp E”ษLgBSšิดๆ_9ฯ™Œ๐ึ๐G.๘ังว8ศ™ ิผำ7ค ! @C.ฤฦˆี ˜ว๔~ˆU|ฃ™อTว:bแ"!  Z8เGPƒ“œŒ‡ัฑRtPixB$JQ‹^4ฃํ่GCJOžลZ๔ุD>ิ�(dโ,q9 !๚รI์P@p 3ำำCญเ-‹ภ )j +ˆ&”Bธ&ู4 'u;โมvด๔CซˆWม*ึf’ีฌ0@ซ<{:X`n/๐ู=1 B]D2’�Kฆ๑ฌเ‰จ@ถa‡ ˜ ƒ0‡XR/�C \ๅˆ�~@Šuxใ"€€ธ ํYไ!/4�ชvฤ•ฎ์ %Ž มช–ตฎ…ญli‹Aย67$5ธ†r0?�j ิ ” “ƒEC|๊>ย6๕ุ* ๖`B�@๒U!|C’e!†'ฯิทพœ่m~๋๊ >ุ7"0ไBภ๊^วฤwพฮE๐sฃห๊~)}๚“Cuษ2๘�oh A!ภ ~แƒ‘dิ๘Z†P0#GE,V!‚Bxฟญ+4คภA>ˆ‰Qฌโ ฐุล F2G0�Š้~เ‰๎ ˆ$ฏ wW๎:ฤgpา$ภย j @€/ฉ๑ hŠ`w2}.1„เทฎtฝ1;œื]`น)๓™ำ|†5ท๙อIFดA€}”" Wุวb+  ฉUF˜"F0:ดใsƒœ ๘ˆj็็!ฯ๘†;พัˆ4Œ™"/๐AšPŒ฿r“ฎ�83ฎ�WJ‰ฐฺีฐ–5ญmhf‹่k�จว8ฆ€งEMฺY@*‚ั‚ œ ™PUเp hwƒ คf€!๒แมV„ ๓}ม'"™๖Xไ*}๘5จฑ &€มิ@G1˜V�Šผ›!๑žwฝ™�์ลH๎1”.#sฯจ'ซq0yษƒ€ด‰อH00„๙Šภ Žx-ึg๔แ`xม(มbœaง[!9CLŽr•/�็QH>”2ซ•!,@ ฑ B‚:ย $`ž^7ฃฮHัฝ‚t(�็_หCย�n0ฝ>เศ1‡ภF 6Ÿ‰ุc vD�ร๎wฟปŠ๋ยฒ{#CH„bƒCฯ„๏tO4�ฏwบฤQ$@ณ~ฬว#>มŠว;ใo3ž[�๓ม|�??๚‰…�๎š'}ู๊d๚ ^๕ฏg๋]{ฺ็F๖ตวฝ{nŸ{��;f๗ฝ>a~|โ�e๘ลGพ^ŽŸ|ๆืe๙อ‡>\ž}๊ฏe๚ีว>YฎŸ}๎ke๛�Tผ2�๐—,!๙ ,#‹!๙ ,~ฒ5H€มƒ�)\ธม‡"dHั!ฤRdh๑bฤŒ66๔(dH‘�:^4‰2% &H@d‰‚8OL`xHS$„ �ั*#"EŠ=7BHะ€ฦฃ|เ2A ฒฅล—ะ์+Wฤ‡ช5R่งYนF,l\๋๓ภc๕Œ�aAขรวŒV�:%DEŠบ%ู* มa็Lม9p๐˜ด"ษปญ>nฬqฅIƒ2kฦp0 !๙ ,#‹!๙ ,~ 5Hฐ มƒ*\ศฐกร‡#J„ b@!๙ ,#‹!๙ ,~ฃ(ยฟƒb Hะ`ย„ |QโDŠ#JtˆQ#CŽ14hแ„ )L€|(าฯ2๕”นQ‰1ฃK็ิd้ิ/Œ„š�0ธ4ํƒฝˆa*ิา4.ดa6ฤ_MกDกJeVค�ีกู^4`€K)ำก๖uิ/ฮฯฏ—่อซPŠ•‡ !ABŠปgณjฤซp่ำมL-ฤภธqฤ€!๙ ,~�5HP€ƒ@๘วฐaC #&DธะกCˆ N๔"F๗cx๕ฆศv ฺ0+�ฤy๗2๖๛823 ๒ยb`A‚ƒ‡Iฐ`˜ Aขoถ เ๊E‰8ะน†ศอ›ึ—์%๊ฉ_œฺ1”่๔๎)}้อซP ไB€BE_a"$HHq=le‚ 7;ถฬั|๚ˆ}ืSt0ใศ„๒๋g!๙ ,#‹!๙ ,+~บhHฐเฟƒBXฐaย‡ :|จกฤ+^$˜๑`ฤ:�[Xก‚ $B$H B…H’1บกbขฤ-[)^่BG3F)DE�aBง‡ศไ;ฅK:‹ุ˜0aถR>0„์ธP๊„ตฐ;‚‘+1š@รฒl‘:C„ศsŒJ -ื�ุะู`Iพ:!4,่4oˆฮ Dˆเะ!Aƒ–:^ จตlฦuฐX†ฯ 1l!๙ ,#‹!๙ ,#‹!๙ ,6~\5(ยฟƒb Hะ`ย„ |QโDŠ#JtˆQ#CŽ1tฐ๒กว†3ZิPฒ"ษ”�Nlฉp%อŒ!^ฆฤ “%L tvด๙“่NฃC%Xสณ)ฯฅ!๙ ,>~ 5ศƒภ&„0p`‡*dุฐขฤ‰-^DaB k๐แ๊"โฦŽhเภ…‹DI 9 ๔0‚•ซ/c�ฃธมƒช›9cRฌ0โ'N“yŠTขRฃAO �ฐ๔(Lก9x˜D‹… t๒ไ 7oยbˆกD*๏Lฅ�ฐARฬ]›Qบ}›N / ˆc !๙ ,#‹!๙ ,F~บะ@ฐ A�*T(ฐแ@ƒ.\่P ฤˆbภ0ะโE 3�ใ่๐ย…ปJ๑8(2!ษ†&ซ`ๆƒeห—$€€cH [ŽฌXยš@a9.ฝYัCžฆญ\*'“*IตYต"มฌ[ฉfด๚U๋ิc’ ’น$H ๘r ‚ุ์K4ๆอ\^5Pˆกษ_>SพT๑‚ DˆH์าแGฎi-ค™๒ญั–•'cF  3ฤฯ;!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,b~ '5Hฐ มƒ*\ˆAแ…†;,œH๑ D„-V�X€ƒ!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,b~ :!�H  Lจ!ร Btศ0โC‹%HœธcB ;R�82คDKƒห—X!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,~m%ัHฐ มƒ*\ศฐแA #Jิเฐขล‹1N�˜ฑฃว7NIฒคI‘Mช\yeD–0c"t QฆM›h��ษ๒BN”<ƒ–์ SจัŽ4)]ฒ(ำง “Bš๐'PชX Z™ต+€ญฝb•*–*ูฒP“�[ห–-ดAนhK๗-\จt�ฺฝห4ฏ[พO�ฎ� ุจเ„ =œX๑NฦŽ nY&ไสJ˜ W?aุศb„ ชะมƒ Pจ`†‹ตธ`ƒ‹p34sUQะภ5Ž˜ย‡!Žจ„ J€ˆ& ฌแ# 0@๘ˆ"ไ’MVxใAF"i‘uะ2„ณ™ ฑู€“˜‰ฆjjiฃฃJ๚žA_†9ๆด‰evโฉ'Pจช�สeŽ$6ภ€˜@‚A‰&i'‰คชŒZ2่ ง†rhlดŠซB๒๗U้ธก์aM’# ‘‹?๔0โมƒ~h“N=�ผ‘]ไใPึๅำฤ'Œ8ธ@นเf‚š—ฐฑ[oT0E*้เ{ ิ;s=ใ`ั@฿ๆผอ;ˆจะ…<ํ�ฃฯ@ฑ�N;๘เcฑผrญ�๒ห•|ฒ ]@@ค“V:‚ึ �๛‡ @)อดำ0Tล23ฒจ�xJดัผ}wH5Mwˆƒ[€-6ูHNน?–;E;๔41A๘tฃฤฎศญ ส†ด๓‹ ŒเK;sุฐ๗ฬ๔� !ๆ๚ 04`H=ูuฐ@รs\ ต?Tcมด]9ฐK,9L๐.ธŒP\sAF3˜๐ €%็ศD*๚€แ/8‚ภ‚žภ๖๒ฤpภเะ#(รข ~ฌแA3lก‡)Œม $์‘ˆ%๘Aธ๋โ € ~ฑฉ+|1†1€‚fศมศเ๓Šไ\KฺG-ฺaภ 0`ํxG7ฐัŽMไภ-พHG?�’qm(‡ภF>xq l ฦ ๘,๐AˆœH7�ศG.ด๑‹`๘ุ/ด‘'Hฐใ`6f)D! ภฦ=˜ภ˜ผฑ�ุ‡+xqŠ"ฤp†tPcZXˆ6คhฅ €HเCศb#Bฐ,j‘‹)ฐc.”q‹k๐ฃ!Hคจ E)R1{'€6บ1Žv`cF*วFฤ|ฃํภžT6@ชHภ‡/†`ƒ0อƒ _ ใหศ๊%%‘8วฉ8T �ฐCL€A˜รธฤ4^@�€�#ตCE4๘ข@แิsR( Q๊๖B 0ๅ˜d›ม‡Œi8F xPˆย,ุ`Bส"€JP&i#pล(<€0ฃ|ัA3์ม j“เG,l0 )ุ6พ้-<"kHโžfp5ธฦ<ŽPžb#Eฐโ jLด@B0ข_?$Aด g Cง ‰{€แ ˆภ5h™ฉ$€ ๘ะ. ›ๆ4ใ2ˆLi*1„0n”‚๕$P~ุc,เชWมšษ @ภศ?l7E"ฤชXEฅ*� 4 ๕ZA3๖AGุrดฅ=->ภŠv�0ฃญ@š`gZSGnขมฎแTจJ• „Jภ t๚๘zฐั#Vฒš๕Pๆด `@ ยƒ �ํ>�A^ฬc๙„‹๖ขกˆ` ุะC์ฐ"˜เทH*ดะ‚Œ5$%x8าQ ~Hb1h†~ีzิƒผ๘(Gษ’žT„ฎ P‹Vˆg!'ยรnxภวจA‚�ะ…ผภg„4ฃ๛ƒ[บเ‡}ค!ุˆ๊ก6‹‰ญ บ฿fEF€ –1บรู–กˆ&ไลrpKac t มs<Pฌ hลฤ\vPˆGผยF‰rRิ๑6ฒแƒ‡†�P€ย‘ตS€$t#l�HฑHpๆQ]@ศถูkใหƒ๏ร ภ–™`A๋กะซCY"ฐ |Dƒ›30H €๛๕#ำค ผ6y`ๆl#—7เ›พ‡lภ%ทƒษN.lv๒ะd|bšุ o„@b7A้ๅ-ูปbcพˆ฿}AdTไk(R*ภQLมกะ˜มุฅอจƒˆ˜˜ œฑpGภŠ ‡€ร6ั-ห]bUณ!ล๐B?œ!?๋๑Cฦุ่"๖ฑ๋๘ั(ƒ&ฎ‡0&ิrpT‘{>แเภG>ไ ม}D™Z�จฑ0 h1ตึ \ฦภ—�โ)U`฿œฎ��FY5ณy(๐6พฐŽ$วภ‡ ถpˆ“6oมธฃ]Qคว˜ๅ‹ฎๅพs`u0`ภ,อ@ysU๒ษฌย5๎9j|,Cโow ]„€ ภ†6ๆ€dภภถ๚xศG>ฝ];ๅKd8F๚1๖ ฤ`‡A พn dฏทฝ(ชa{xฝฎ$ศค์qเ2-/Xฏ$ด“เr�้Ÿxห}tšใ>มฐƒ๐�แ๑่€๗HRcˆpฝL งฬpBtCTlT๔…8�š8(h/|ฟ้จœ`]}�ฐdเ†Hqษ๖กฦ9 ปBz?ฐ@!”ฏฝภ@` wโ \ŽR�u๖E d 4= F\ - 2ลMp๕X; Yโ๗$&๖0 ะXบกิ€ุ๐€- eฑ}ญ5hคeCีZ๒Gเ}ฐƒ฿ิOัะ“ะๆpt๖{มG73qT๘€ ดิg}€}�มeเวั�๐I"C~๚‡~5Wf†W .…ช`)P@๘p s0vเ&`yภl!ะO�p ๛ฤ"{_pwฐ‰ v ๓ภKๅ Ž2nยa�คถPcQr‰0o {๗ึaฝ—0� (3|tมe 08คCฎKn๑he!b_ภ‹€ +ƒ8Œ`Q"๖DISบ๑H[4Cp ำ žเIœPrr๐๗ณ+‡๔F�ะ ^๘ฺ@D…�€™Šขh ิF„ฺG๙ะ ๔€^Žิศ@C€*ฝจฟP –ึ ภ€ก๔่จŽC๐ŠNƒ–ใภCฑภำ๓Œ๛;pK‰!}kม‚ื๐ "%pY*b"ƒ) ม”"ต p2!s�rฉRศ“‡(3p$I"#Z ซ œr)ย"`+w" a` 2’#m€ ฟ€+"ะชaPม\B*`าวษr ”eQ,R@"ว–ผทฐ*ห.7‘ฌ(„ขฤ}๒ษก�nษชข-œ4�0รศ=ใ๐.ไ’’๕  nภต`3ำส | QC5sม๕’ จษ3วb”แX Q”฿๒ฮ!PศะE�ข งเ3อT๓.*์wP ู<)@��อแ Ž ำ0s•�ผ �ะI8ฯwใYZ.็’.ุตc ืย  Eภ pัํ(  ณss<#2s0๏�0ฉ1ด0 (ƒS;?&•" e%ร 0kp–ฑdาEฦpn ว ]ฮ็œ�p6๘€<‚ศPใ๙แโ ฿ม7~s Xƒ/ผฐ5ˆะ฿ฑ3ำ•ล{ฎcน๐F๋| !•ะ๘ข Iy3๕3;ร์b1๘ภ“ [เฎ”6[ญ)ึ=ฃe๑ีัrดl133ใpฮeืuฉื!6 .€ภŒูŒ–u2๘0} CPุ�pุIย?i.C<Œ@D@$B$4“p v>็ Yะ Lโ yภ ภ�ฃฎ 1]๐P bๅฦ7 ข� @c๐'b๕€โC>ธ‹†ไ ลใS>วOฐ็ภ#@oร]�ตŒ›šะ�!๐�ั=�4AtA0 ๅ="Kๅ„† Žc0ืPnศญ>์ใu‹N็ึ ม5>แn>žฐi@W’`e ฤ@Aฉแ“ะ q y1าi&ๅภ‚P,` `ชpsWเิำณ]๚"’y@ี a�ุญ�‡Fp „AฐหQ4EUTwๆ|ๆใH$IpiดF้ะF๕T๔ ึะŸ ส€YL[VโฅC�๐ ต#b๚ ๊œ”@$DD�ด N๐ฉ ฃิฐ4\Eฟเ จฐŸช”ŽฎTnฅ$ๆPฺ‹Eจ˜C;ิC<|Ž~� :ท นภC ๊,ไBพuุ๙e็ฐ4Žž70M๘PM4$LŠQ๑HoฎเA. ๘คO/Eฝ@ ใฒต@“‡^Vf,๗ปะ 9p7๛ฺž{'O๔dO>PNŽO>QO๗ค�๘ฐึBฦ์ ภfHW /ะคำฮึŽํณตQ๕Q'Ot$€ทปB๏.@Aฮf้ลO�ˆ๐†๊1–Hๅ>G๑ฟ๏>p(Qซ7QฯwQ1๘8ฤ�+2`R<๐]ขเ .p `pฐ ?ŽไJ�:7PjY-“ˆ๊ฏ<�ํแ.šเอ _ึี`�Sกๅ‚๐ญ< ื ‹upj… lๅVaBXฐ@ภๅ ฮƒUS์Š`~ฌj„ุภโ6`EuTIu‚&ภ+๐่3าdะบ 6่ช[?ŒZYeญดnU?T1H๗v/€า เ;Pส›ฦดŽqoSx `Gฒ@๘˜ณภ$ส…ฏฌ`V`’P฿๕ๆ ๗3U็eัฬP6ฐำฉ aะ|š€‡  ๏€ ุเap&Wฎ��ฝๅe^Cภิ๘�็ีซิy_ส�x0U่๖hŸo�ศฏ�้ภ�ฮo ๖`�a(€ฑ/ ŽmโัภB† ูท€l์`i ข)๊ขaร 1ภ)"peŸ”%Rดˆั0Ž(ส5บะ!B3Bํผ่วBp`~ฤ(ฯ5†*ˆl)ใ๗x˜Hโัฅ๓@5จ ‡ „ธ@�๔Kฃ2๚Y0„v)ๅ…Ž>ยr”QL แ€Kฬ๑มรl^h€\ˆa!�„ภ Ž (ย–„จGyœท9D…้ดGชถKลเLHเƒฉoุล–Š>:้ฅ}ˆเ˜ X`๊ช)ภz]ฎyH�“„{mA`Hม „ฌ๛8†mยNiš�#๑s/4หะ“เD'�ำ}Xสก?ธ้เ(ตtฏถ0€`ก<1‡‡ฅ ํั!์‘ใ‰ n„iํb ๋ž—*า๕ฯภ›์`ืm†2 แเตBฌr๎0ฦขCsŸ&จ‚Ÿrฐl …�ฺฯj๚`0H_K๘q"˜IX"H  ˆ`*4ฐ(pิ(ฬ�|ภ€o€ำ‘qAฅภCŒH@,hึฟข ด๏} aภม Lp%>ว่\8‚(`ƒNศภ�ว์Šภ@(8(lcEAgเ1 Q{S xณ~„ก๙+GIˆ –๊@iHPฯ<4>,! ๅP–Xม DŒaื่‰‡์‘ˆ.@b€ฒรพ๐/4ฃ‡�cล2�ภ…์ข.ธมŽš7 ะง˜ร์เ=žฅ(II?ะƒHย>๘0 ๑ั€T๔ฑ‰.จ( ˆT$#ฉ<6่รe „0ะก™๘Bš๑9ฏ๊}��#ฅ๔คบ>(ล6ภ‘๔โšทคิv๔QŠ,tA3zโM^yp€#ฏTยP !€์ม ๗ฐ‚H`ตYer ฺษีฎ~B˜HNฒ’]๛™9ไ0†7f}8-ธ?่่‚ˆ>๚Œv๐ฃธฺQ๚�"”ชฺ่#DR€A๔C0ใv๚wtฃสุ r‘ŽeะขํจKbฬวส0ฃภF>บA!ฐ�ฐG4ถัƒ"$,1& B๊C0เืh‡ฎภb OเƒFมัŽขถ*Qช  ภํ่ฦ/<บฺตเh`�z๚ำ1ๆ๓จE;๚ัˆฝuซZๅ๊^ม5€;บ ศ4‘๕E�†6าA@dศC๔˜G=ภˆฤfศP=2Q5ะ1%่ฤ;าQm�!่มAh� !O้J ”T๗บ๕ศn �๊�ุhbIล6–แz�€ัฌQeผมT๘mp‡‚ุ แชา€=TปsHม^.ะ?hรใ˜รฒŠ`ด '่F&<บ—‡(('!ฐ!’ฉไมภŽpVŽ9ุ`n/าซ5ฺัz”ขj ่D…ƒ‡cซญ๙Dึะ…w4,TฬGภ�’€Vแล 9nr—�� ]Xo>Lแzโะฐ๘pCภra_ศ_wว/lเEolธmBบ €ุ>‚ซบh ส{ษ๎ขน ฦฝจIx3•ษk€†+ผ…`€ธ7@วส ‘žน )<๔pกฝY‹๐จ‡|8†vˆX ฟ่‡~ธ…–’ƒƒKภHฎn˜ณฤU (๘2h†บุพx๓8˜Šp}ƒสำ7~›ษธKป๎ชพdŠ+๋ฃ+๐{p—ฝ๐€'˜(ˆ‚$ฐ‡๊๐žd„ฒp‚(ศ•TM@๔€<ฎz7`H8kพ)HึสZ�ฆ ˜‚|ะฐ�(ฅ๒3.‹ฏืŒอ๐.๖b.ร8�dญw8๐€๐ƒ๙๊†?0 ห‡&@’!ซFุ€630ุฤ`7๖„๏pt่Nฺ่|x@ (#๘ฐ~(…ฌ หcะ,ภŸ๎ . X€ˆข€…~(ภห P…Q`ฌs8‚ฐSPB{mHชบ�Œ4Sั}่วฐ;‡sƒea0ู*Qœฃฑi‰ณDJ>,H€‡เซ;จฬ!ิ\อ!ตC๑๓…1ƒI๘ธ€(ƒ)ˆHเ‡5( #ภc๘ƒ)€ƒ/H}{)ุV\ˆ่‚/€‚ำH'<ุ‡�Mศ•h๔ฐƒz.๐หG2ฅ ่Œ~ศ‡\ะ†_่า”าdH‡อโ)}ฐ‚จ‚€•,|ุ^ะ†|p‚`ภ[ศ…\`•ž@–ห€%Iใ"˜ ๐~H„7<ฤ๖ก…: ‡0๐…p(ซ+ดธ„2ห†#`5๗บ๙ใฒ =‚ˆ„s๐ŽHQ๐•ˆ้Q‚๐xเ$่„}ฐ@„zภฆkะ&K89@‚Tะ0เฯฝุพ˜„s`‚[ๅpk๘� jฑ˜I"ฅWJ<ยฑำก* €ศธ‰๑„Zเœ  € }#kฐƒp1ก€@Kส๐š�Žธ‡F(‰mฐฑ‘,ฆฑบ KรLhŠsธ~ภศ{pCุ304@}PจซF€œh%Sุ6่ ษา@ตไธ|ตย -CพธผSุƒwpX'ฐซ ‘ฮ sž ˜?`$8‘ €*แุ‡8ŒY‰กƒต„ง!€ธ‡Dะœ(วะ“˜�บ�ษKฑะvธเ€Xฐ#yWhUฤาN!ญืศย'ิ€”โ–r)UฦT(_0A8€[(*O8YN่รJฌลขกฤi|0ƒJู หІ`p…`@๋p+ธ’+Šภป"๋ทlŽทย<[ �ำน4Kมkจ0tmˆ\Eยˆ งm 82›H$}ƒขถ่(งsสฆ“‚่nZ ุƒ7๊Nเ ๎€ y็��� ;]ถุํ�ฆI8ุา.่„~เ‘šfnn๔Zฏ๖z/ ˆMฐ์๚ƒeฉ€,เ…๋~Vœฐ`P9phไR.ๆr. 8CH‡tศ…อbQ๋ย.ํฺ  ศQH40†"ธ0„7‹ณฉะ33C๘‡?P๋\/ะ†L N0ถth‡dซ2~00ใ…ฐPy  ชนWSถ๘ปNๆชYkตQH‡_Npฐz0๛"h iๅZQ^pฮ‘่ฐžIกร3เ๚๎o ร0 ณ2?Hั๋€$ฟฐ �ฐX4ณว7 Fุ๐€G`ืV€(05eˆุ๐r�็R;?Xฏ,oๆv๓ษ}๎Qจท๏๛Eง;Q์ย~S๐9รƒ= ˆป†#XXฦธ‡8xฏŒณพg –ษŸ�=B9=xธšK9 Pพน3€๔-เ^ใG‚ณน+a7�3H™=�k7ท9…ซ7p�๒€J๐bแ๙sŒฮ{0:พเ€H<›Sย:กƒLฅ=c: ˜d+‹Laฌ๕JoEN๗90FูฃพซG0๓oGภ ™?Œ^–IFD„ฟธ‚'ี‚Qฐ‡&hไ%ฬม‡ูฟlˆI‚WX|0‡ˆwดฟOfA๕c€x}@๓ @�ล…�ฝ๘ฟˆ๗ฟz_@[ดxˆ\p็xส�ร0„wh‡t…ฝ�๘?DฟฤV0’™~DX‚Sภ†R*ดB,™๛ฝ็{ษ ‘`๓#๎:Uัเ€GP†�khฑ0๋ehฺœ2�pˆ:Hจ�้๊๛ฯ�R<๗ก๏TbุRฺ.'kE' X8xะ ศf๘.๘8๘�{า(RัX๘;ัข‰ะ7�ใวDฐMช* d‘†›ฬก‰…วุ‚jˆ8ศ,pc0ŽE@†&จล†.> เWmู‚k™่ —G�๘—�( <˜10ƒx ๘mถ:d<ˆ#.šัค/‘–eJLฌธ•NS˜$L$(๑\บj�Bเ`ื&[ฎ)ฑqก#ส”*Wฒl้๒%ฬ˜2gาฌi๓&ฮœ:w๒์้๓'ะ BY^่ฐศ”0CRtpก&) ูงฆc6๖˜88'ื'ithฑ ธ@‹ฆ~ฦHฎŽ)๖$…!`ะน#‹hม0b(เภ‚.l๘0โ�ฤŠkb่ˆแ$Lณ„ฝ่@"† 1ถ "Aโ(i>ผr6-ย,sŸBpx€แ5ˆcญxุU๊ล„ั่˜ลœh๊..n�8๒ไส—3GเJ?ipพ”y3a‚ kˆฦ บฦ‡G€H� ˜ค‡v๖|yโ*—ๆ๑Y’ฅœข!|eอžE57 x ‚ ๖tมQคR?๙˜าA ๘ก?ใฬAawะ3Qไร์Œ?�ะ“ !Pƒ&้๘๓N7่วม5` (๘#A 9$‘@ด@@ ‚u5$*4ูA จ B Hษ $ $“ๅuQP (y%ฌqม1zHU$œqส9'uฒ๔ฺKMภZฃ‹ู9(ก…z่€!Z“Ž(C 'ข‘J:)ฅ�s)ฆ˜BPฉJM…ะAcœŠ:*ฉBfz๊ฆฅชบ*ซญ*wjฆฉบ:+ญตฺบฌš�บ+ฏฝ๖š๋ฅฒ๚:,ฑลR ์?ยป,ณอ‰ฌฒฮJ;-ตหA[-ถูj{ุต�z๛-ธ:u.นๅš›าธ็ชปnถ้ฒ๛.ผฦบ/ฝ๕ึ:ฏฝ๙๊;*พ๛๚๛ฏก�<0มp \0ย #xฐย ;œ�kใ๙0ล3!๙ ,#‹!๙ ,ุ t5Hมƒ "จpaB‡.@‚ x€๓ฤ†ƒ>€๒GซŒˆ)>Hะ€ฦฃ|เ2Q)ฐโ4๛ส1มกf<อส5baใย+ฦ๊๛ร‚D‡„nข"…ำˆ!8|ฬศก็Dˆ!Jd l@!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,ุ 1� 8มƒTธแฟ„ !*”ˆะแC‹ flฃG‡†|ˆกคI R!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,ุ 5Hฐ มƒ*\ศฐกร‡#J„ b@!๙ ,#‹!๙ ,ุ 1� 8มƒTธแฟ„ !*”ˆะแC‹ flฃG‡†|ˆกคI R!๙ ,ุ•5(ยฟƒb Hะ`ย„ |QโDŠ#JtˆQ#CŽ1\ธะ *D€|(๒B”T๏�ๅcดb$�ˆม๙๒ๆKM…fI{Aข(‰Ÿ#4รฤฃ!ฦคK›D๚CPa/:ศ€*†W๚I�Yๆื‘/๋ษ4uVร“Dˆศ๐ดชE ^๏ๆ• /†ฟ€1๔ !๙ ,ุ�5HP€ƒ@๘วฐaC #&DธะกCˆ N<‚Bˆpฌิ‹ z;c› $€’–Cmfzxpเะ†Š,๛ึ$ ภๆžฟื_ธP๘๐>5‹ู์{์rm� เ"6ขฎ y๗๖ํŒUk—ฎ_'ˆ%k–ฯj•D๏XC๚&EˆฆFฃฮม€Aํ† 2%dฐ‰S'O!€vH\z>% ร'Š�ึฝ+Q/_‚ƒ N ทณ็ฯ C‹]ัˆฟจๅแ๋งd+W,HดMษํZถq“ฆน@P—+_ Lฑ๗๏เ&ะภ  ลN \้ง ฒ@ใภ๛ '.0๙๒ๆฯw‹�Oพผy๑o'v`๑›{kžมbิ(ญ*T่ฐยY 4 •"˜`B H มIิ&P(ะเƒ)$ค@ๆงAB๐€ค`‚@!B€ ‚%` W*๐DA… h%‚ 5Pมˆ O1ˆŸ~lภม†~„U~Vxโˆ%ฆฐรQ†โ‹Lv€ฅ0]a(‹6ฦย•Yโๅkb)ๆb~\ U Dก…Iฆภ@*ˆ0&Ÿ*�yแG‚bัย~กษdยศำ—aBธๆk@ภ`ž็eช้ฆœvช‘nQpAW์#OŒ0ส5๏๘3ฮ!๐�็ท"6` ม†TใO?ส(ฑเฌทิskฎ๊VA นคS6€คภy ใO=ใธA ๘ฑฬตสธaิฎฝ�์‰•ะฌ?๔�แAœ๐H.ญๆรO  ‘ ปŒผค~๋๊้มีิฃฬ)$`ˆ1-๔Tม ๔”eA �ก?๏, TŠw€ใช aIฯ8ฦF+บ้ฐ๋ฎb! ภ„Uฉ\ฆจภhS-�์ณั6ะล;r\ภ่๙4aดณะฆฐฌfยLPอฺฝผnป๏ชฬ28.฿œs›แDซฑธ^์้�pว-๗gตู๖ะY”*ค�สŠ/oŒ1 6`ไ๖$ฒ„๚4โtฬ�มลpฤvxโ‹7~lบ]0BSD ?kTเš/c~<่uŽYtาOะ bผkfDใsr…<@ ุE+r@์#i$Acฐ ;dภ„9|‚˜ฮฃ๐ฎฝฃ/Hภ i€+ฬใ[Hวข0"ภmƒู† :c~™!‚9L๐1ผD6^ะ€ เ‚,8ำ#ษI:D™Iี€*์ ฐ$&7จ:ฺฑฆ…/Š4ƒ<ุ@;๋x�วŸQเ1Lฆ�ไˆmv3Xšf5ฏ™Mqฦpmฃ‹จฤŠZ๔ขu ๒DSE‘Mฑ@-Zมƒ`ฃ๕๐‡?๐Q„`ท๐‡&ยะ˜ ื@ฉJYJฯ่&%x8าQ ~Hb A๐pKห๐ !m`FlS™าิฆ1กw $ภ ๖!‡…„HXJU:!LR$)Ikะ ึ�,ช1ˆB>ˆ0l์a ๐8ยCะบ5eวคŸ2 kXr"๎€|๊ข:U,ขซ_%ฉ@ํB‡V „АŽฉy  •จFํ‰I๕;lฃ& @P‡ZTI�   •ฺ,�ดสUฏ‚‡vชTmPณ๙ 8ี้JหัำŒ:๗นะ="หโD ไอฃ0ฅB ื’6ฃN ˆ@:1`Z๐ž1„EW๐Š—ผt๒iz  pิSpŒv�tธy0!ฒยช,ำ�๕ขห 8๋ฤ1๊ Xƒค•$ซY€ึ~ิB šงญ/ฉ�ล)๋„ƒะ#ฏ8-MทQ ๛Pƒ@‹ฬ๚q๖ล1โc๕‡๋‡-Rgs;ฃ�๛เ�K์ฆQ,ญ |ร™+xก|™๐ฑT:.xภ{ฐป2‘อiัŠ�\ภHuณ,š3๏ะ ฐi�  ้ฐ@—@๓ะ2า‚๚G�+%ะ ๏@5ๆ3@๊“(๙ฒ/๕ะ \wๅ๋็R๏8sึ 0o`?ณฏQ\`V Qะ*ฐ๓ใฉ๙` ำ-๓*9โพข0โยะญ๒ฺ‚ฑ„M๘„�ยฉภ1ฆ๐1hลบpƒท>1P€ƒ€‰'_€,Kจ ึp-t่่๏` 0 „L่„Pธ…‚)ะ-W�.๒fh%p†ƒ˜๗๗ฎ€โท‰œุ‰ŸB]นก ภา7เ |#K*ะaฐ!C2-ฎศ?qb!CBต1#ถ๙!EฃฐM*๐!’'8‹>2‘"yRM>„2"‚)ฑง(DฃŠ9 OHR%N   ข o8 ๒"+ภ ๘ิ@rŒ�ย%?RŽั๘IฃŒbุ๋Žณธ%๘ก†2`iเbก‹5ย‹๚‘pญจก„4ข-€#Œ”") ๘ศŽ เŽ๖๘SB"&๐1�ศ‹า’ท่‰$Y’& ‚‹‘’bV ˜6มHrท5ibใ�(G+q“‘“แด[ฐS ช@oG4ม“)q7 ด๐ 8rใจ”๛ัSฉT$+I~'ู•^้•,y7้A3)C5้\P†ะ ๒๐ฝ€XUQภ ๕  C)]\๙•|ู—~๙—2q“%X ก ‘—ๆ1]Š ˜Œู˜Ž๙˜qƒ˜็ก˜‹ ™–y™JG5–(1–'้™/ม™)G™•‰™ฆyšžข™Kšดมšž(š*›RAš|†šถy›ค‘ชฉšญูDfฑ—ซ้š!œธ(!*งQภ9ป9–ฤ9œฒนดyœธ9ิ9šถฑœอ ษู™ู9~ฯ)ฯGา�ฉœ�ุ๙’�Iž้‰ัY7ี๙ž๐ู›บaœํYuŸ�ั๑#"pยs"_ ทฆŸฑ"&@!Š!๒$๛๑&ม่žษRŸฦ)–oA]*Gก๕9โ)กตน•mA™ฺžๆI›rjก๚’๑ูข.Šœ๓นข&ช˜ ฉP/๗ย-%s24ส๐.g๑ึ06ภ+ีภ",Sƒ4Vเ Z˜ะV ัก2*œ๊กWช›\iฅ๔)ก ช"บข+(Jข*ชก/บฆlšฆ[J™ q ก˜�@ p๐o๐๘ร<๛Cyt '0M Wค6`;“S9$�€B*D:8ฃ ฐ06€eภภojฆีๅฅo:ขธแ™œชฉaJškแฉ&สQhชฆnŠฆmฺชmšฉ๎Y,ภ;ภฃ˜0 าš„mœคmฯไ[P 1๙€.p T`‹€ M H GsฤOxไ[p ?๗ฤdซ:ชvใญ\z]ฃ)บฉฑ:ฎ็ŠฎคjชํŠช2๊ช๒๚ขํชฎ๖9] ํะ๘เ6]ั“„%cŽeGเอš ‰ ห ,0•3US&`ZดลกPฐ ๓sญJ„โ:žเzฆšjฏๆŠž){ŸœY™ Zฎ$kฒ"๊ฎฅ ฏd�:ฏ8๛ž๕ฎŠฏฐ@0a0)ะญuฃญ‹ซc๖ภkน๐ า@๏vฑ่ฅ^ย๒eTf[$4็p‹@ '_#+›กบณ!Z›/‹ฒœjณฺ้Dk ฆเ๊ฒฌบชขšณx{›e๋žบขpค™ซยฐ$mšฤG~„eฮ0 0 ๆ๐ ด๘‡ูhAp๙ k1Q` p Hถ{[ž4ซฆ็๚ฅฐสฎoKฎ๋:ท{kทw›ทฒ{™ชบกฅ[7yำางe๐xํ#y<ฤ…“ „Xวsะ#uฝ7{‰1Hฃ` ๖ n8บf›บทช��บชถฺ�ซบฃ ปง:ปๆ ™|–ฅ$บ˜lQฃฉROX…S8 ‘w@Cภzล`�˜M{ุ€๘.'&uฉDรฒ�Zข™ฺฝl{ฏfณ� มๅ๋บ฿kบXzณ็�มŽYป์�ณ้‹€ป`#ณ่Ÿ๑ุŸ(BŠ"‹ ๎HทH‹เJฌ#ณi ยคkมcšชZŠถข)ม+{ฅv[œ[๊ภ�๋มL�—๊›0มข: ล+‰ข…‹p|V)ƒ„Z@ บp\ลฉ’w›eลทRœฦนaฦชฤkฬข, Šk์ถ�ฤxœวฬ”$ ,0x๚k ้ Cp|ัวz�์ฉ0Zวฑฤ‹ษ็GX%Mq,๊ัšt/ฐ ri’ แR ™Pษ†ษš,ษ›yวฎหMŒ%rดOะสฑNะสR0 ฑ`ห0 YนตŒ€หบ,ห2Qณฬ�ฬเGZ(œผ #'รXถ/ ฯp A ฿๐ R0อค JฐฬปLุ ว/ ก๑gฐ”Y\ฑP๗�0 u.f•\๐X ๎เะ NฐฮฑRสเสา. ำ$ ำ4mำ8-าR=ี9v$ ‘ช™{pฌฮ8๐ฑ� Yœ?0 รp*๒!๐.,ะ-๐ิ๎@ †0เ<Žlึc]ึgึk�ึT}ุˆm’/w”#\B$ ข:ย#๎ˆ7^ฐV`ข>๒-ฝขํ ”๐/ q`ฐqtล ะเ ิเ @ิ๊เ �ภ ฐ� œ ฮu๓ูก=ฺฅ}ฺฉฝฺฏ�;ลœุฬ��5‘*ะ‡นภ#;š-Hอ€ะ o0 �ข/�คึตู-ขaซ๐ ข-ฺ๊ฐฑเไ๐Kย>เิ`ืvA�ิ iiธq�้ฝ�๋ะ�๏฿๓}�๑:พฮแ.0๔ภ=ฃOํใง�<!H ๚ฆณ<€เ<>งูœ-บ๎'B@ ๊ํ�V "z-ฌ]7 ัi0 ่ ฿๎ภ๑ภ์เŸ`ฝLไโ0.ใข 5.7ž•ุม฿:แV~ๅ#แJ› HpภLœฌa{๗เlžJขDJP^pง2ณ1อ?@ ๋เ ? เ\ ™ฌ˜ ๑R ?ฎ์ไD�ฮิ@ G OLฎtn็xฎ็|๎็์ผวSฮณ๏Šๅœ�้!b9`ี%อXGq9‚5˜ZHะ๘ฐV�ะVgAIะด|Sๅ†ใ฿ภึะ0P–ฌ˜/์มฮ ‡^์E๎ | ์) €;ฎ/.น๎ฝnษnŠฤ™พ�žžํฺ.bd‡@ck๊˜q™Va&^APจdA3๐ >เฬ‘0@ ?a r9ฮDG ฑฐ "P�€‰^ไะ |๐๏_ อฎ้๗>๏๕~๏๙žอโkํื�ฺž๑Wrก�%ธ�ๆGaฉNkถ†k  g‘€/pซฮŒ�ภ / ,M ฿Pฺ"p|:�บC๐ฤ^ไDN๐์ส}  X^*๓4o๓g€๓:ศ๑�ซ๑XŸ๑uW YP*_พdภ�แ๔C๒๚ฆ#0tะkืvow ๘บr.GŽ Dฯ๐ ๎๐ 0_/เ6�ล่ฑMไ�@๓?อFปรgu๗yฟ๗}ฏ5ฎk๕�รYฟ๙ฬ3๙w-TุŸ~0ฟ ฑฉ -01� nุpS)ธ‚gม†ฺ‚บkฬถ�ŸฐึPฅำลฺ}๐ ิ@ ›ภ`` ิ€ล ฮ๛Žฒบ๏ษฝฟŸ—™>ล2ห๙ฺๅ๚�ไ5ฌยฑฒ$*–’#;2"”=๛|"›.GC`ษ"`Ž๐ O™ ใฯะ//๐A‰[ฑ3$0h@˜! 5`"Vฝธ แFŽ ~t่$H†#K*์˜RๅJ–-]พ„SๆLš5m�ฤ™S็Nž=}�T่PขEสDุ!้DŽ›ขlบั้J 0eGDฉ'งj มข˜#'$^ฌ2๓"…›b…2ฬฌzubึญ1vผหr๊L’G��X๐`ย… FœX1เ/๋์ะห 0ร๖๑A‚rs^d!ฅ ฮศ“+/~๚๕jึญ]ฟ†[๖lฺต�9ฒ8 “)ร!‰�ˆธJ‚ซN�ฎ๛ฺFž\๙rๆอ?‡ฮ2ฒM /DˆPqม็๔ุ{ฃ^�x๒ๅอวไšQuะ็�๛l�^�|๚๕ํF้h�๛�๕ฎ๗/@$๐?จ"@{8แ วฌ้‚ jุฅ”†`:ภ*rXHฟŸ๘+ะซ<”๊8ผF"ฑ%“\‚ ฤžP$FW ฑFoT Dฎ่G—ฬห& ซภ™\ฐF ’= ”ัภ„.ฬลšฌœqJyาศฤฑฌrKว$< (0„˜h€$ศ *จ@ฐhAฎ4`‚7ใ4! $ !„`H�)/9:†ศกฃ €@L0แPฎ๙ ป,�ƒ1TQuJ)KUTรค1ตEE“ลPERตCUฉLiึ\ฃdย2–9เxย„�Zjภe�ฉG7ๆŒมš๐ฦE“]ถูg+ˆกTXก„[lIกี”J๖arะŽก ฉ€~”Qย„i'฿z๐๙ฅ�^๓ซ๕VZG=5เ6V1u�2aY†˜เ^�ืื†-6ca๛h)C„R8ถ#,9G,:้'Œ<่แ W\ !„NNyๅ–;A‡fIA”hv0ืŽ<O8’0f�เ 8X �i#š!Œ v(tฅˆฟŽุkฐว~ีUS/Ž๕เ&a"ปmŒ^{ืทฦucป๏F-hx$p2)ขdŽธdš๘ bภ�`UhถYย G\qB€Œ|Nั H0ši7บภƒCjก‚d 4ใเšั#แ†ซTทฟ๛๖ฑ<ํ'แNฑ`ฒีธv�๋–๛wผ—gฐ3@cŸrŠ0•Nž† �`ฆผๆ›-ษ~๛๎m˜`‚Iฐ)ล‡สBg๑‚n@“0Z กh ^ืƒ‡sG˜ูLEฐU%Tบ3฿ฦ0ฦํSฉ" ‡'ผเ• mอร`�Bเก�ๅh l0บLN{เž๗ภน›๎„)D_jsx[ M๔ร๕B`็ฟ.5ฐxฤปแKeภฏ ๐ˆพฃ`๏�uฅพXI‚ป‹ข^heๅiะ‹_ฌษฎ`ŒzใTฃ]J—4€ธX 8เIภi"ม�๘ฦลเ 0–ฑด r#rI$ฬฑpเ˜Žั �a@ˆ|xfRไD'>“ต2"ภVJ ‚ศ$Ÿtเฌโcส‚ั•ฏœ nq !จ ยi แ์‘ˆ.@ขq(T#้p Dธแ yผฤ.{๙หšๅแฉำย5�ฐ$0ญ„ �HฤะH;์แ O๐B30Aฤ์ยzร<ETBŒํฌฆฏ(*โญจ”พฒ็'Cy—วฐRK๑„e@ส‘p@p,!=ๆQpข\ม@ :๑Žw˜ ](ฺ€%ไฃ6[@'ๆ1๕Xp#š๐CXฐ= #ฅ๔ศธ”า€.,๙0ล:๗YOx:,‰=M›*็ฉฤ%์P�))ŸฅUโณจ…*T9ย Cƒ™B๚จ ชภูศpt@o*เฉKtสl@ˆ‚ภujฆ” ธ้`u๑ศำ.้ฮ“@h‰NูT�nงบ `[ิฅb‰ฐMZb�๐ข:Yส2ฤ„%ำHษ.�y4yˆg‘27ถๅhŸ�-i%๋!๏๘TฑฆอUIbห—.สVž’}Nพ"Vึทฟ�์K‚›œฺ~(ฐฦ1ib๒Cลด�ึนฯ…nx๒"ล๎5ึU‘Hขป]๎vืป฿oxลห\๊ึ็Lšญx(€�1awผ๏…/~Š{ LBศซ{๊ �ถืฝ๑p€qาสึHˆBฅเมN6P2`#{˜?า฿กdฎGเ๊ษ8เfุข# `ˆ‚แ@Xย�ศ‚œ=’เ ๒W8จ`ƒ4๊ฤล๒๘G?dLใŽXXเŠ†9์แใ)VลNฐ€•�ผไ‡1ฐภ 0| เx„เ‰'l”xข…๘O6pH`G๐W๎ภ/”8ย[NIใฌŒๅ๘`ฃ E?๘1ใŸุฯzๆsGบ�ๅ™ฬfFs“›ˆ9_9ษL†tw฿:ฒ\@ ภT*๐Hน† Oo’ภศะˆ!gUม(`'Jฉญฤฃ“R๒้Vง umบmC€Gๅเ €ฌ]ญงู™๛จ„[†ี‡`R QW€A=ฒิ$vถ›:ญ” p`อธ‡lF<@€€-U}้Vซ€M”@Uฌ€(XCคภ$  ๘Eป•†�]ู&PJ"ร„cว Yw˜r›�ถ�4G�๐+ค�฿๗6ภV‹ f๕พwพ!๎Vaƒ๕PฎvใUำ„ƒืซ�ฎฎห1>ฉปš�ำšฐ<ย�xว8Šฏ๖อำƒ!ˆhณG.Ÿ๙@m‘! ว�8?X์0–x๏{เรYฐ)Cp๔ซ฿ฟฝ�kœCจ†฿๋@จ™-ˆพ้ซ>ภx€t(hh‚ ศ|่% ?]ธ#Ch‡_P _h‡9ฐฝฌ‹)O3ฏ๓‡†‚0„z๘…6[ศ›ƒ >ใร๋aบ!์ุ…Xศ ๘\ภ…จ;h>. qโœ‘$H}nA€Oศภ „'€‘ �2˜‚(€~Xƒ6"[ะƒ)ƒฒ6ุฅ%๐}h„ ่ภ@ฅa1๙V๐…1P`3ศ{Šx…/ป Pถ}จ…vร `€Eh‡w่lh‡Mศ๓…t่‡dธ†mP8lศ^ธlฐ"BXภBศ:!’~ศ‡\ะ†_H|ุ^ะ†|p‚=์‡q`lฐ;{‚(HPภ†{`iณฦZ่‡}p^8…"ศฤM”CˆJดฤƒ0ฟ6›�k|ะdPลF ๘ล`ฦh€(ศeธ…k€Bธ€xG‡›1…ศล]Lย€l่†qhlุ�ฐFlิFฎ˜ˆ”HŠ€MุkW~P‘PGƒฤ@4c+‡Xฬch†v@B˜|˜sT@|๐…!ฐo›&0FdTFfD1"tJ๗€Hจกwิ‡8จ€ุ;ศ€A0˜œ €ธ‡D`Tˆp",๋สy A/‘ @"ฑKbA$8†@เ hQ0ช €J8Dt…Q๐ pfงAŸฑ&8ˆ[€Iเ‡X`€(ภfภ‚ุl8สำ‚พYƒX�ฆt`. ‚ธ†y8›๙Ll‡"่ล˜กLh?ฑฅ�0์I €hpฦ0ˆล)ะ„{ƒh€ธ†rK2ภ]€€ อไฬXXงสl†หd’v์?…ด†ะkเ,P€*เ{ุ่อ฿|Œซ๐ศ~ฐ„H?๔dHะฬอ‡Œศ฿@ำ<6Yfุ‡m€Žyภฑเพ7�ี ๐€?ต(_�7ฐTUUV๕ตๅุ๒ฐหd๘Dย"ญ! “mฏrกŽtRฃD`_๐ย9Y5#@…ธ@‘Hpจ˜{=fเศฏh†:p“ฑ0C๚ˆ:%"dศัS>ญ’ ๐ศฃT‡eC-/g๘B#ไ‡Gล†.@„}Xƒ?ฝิˆ†2ะ‚บีƒPŠ‚•ƒศUM~‚C|ศ9ุC€UfะI5ƒ‰pื=ั€]ล€t[CๅฎHฏ= ผ8ฯdฝ€emึ @_ภ†/Xj�ˆ8|„-8„ฬ๋�pEฝŒxๅƒP]Gu\pƒP�ล��cะ� i=ใ‚�ำˆฎX˜ฌ‚k๘สƒล‡e [ปm-+2่‘‚lะ†9ภ‡d€pฃภ�ยยŽ5฿๏่628†:่‡โ€ุ†C ธLaาhYkeƒjฐ{ƒƒ 7ศ†ณ‡3M8‡(' iRIhณ4=ฺ�R�O M •จ4‡L"ซลฺZ(6เ‡{"  ^/่‘ Mfp‚ ’ำฤ†�ฝฤJ�.จล9G‚h)=J๎ุlศ„hทXศ€0Cฝผ}Pฅ ื4Pีํ,”;"์์ ศ˜�7�‰Œา{ žŒ`ˆˆ„]ย�ํ˜€ˆF.Ÿแ‡&8๘”ฯ่M พO๘6{‚€O]@ว5nใ7ฮŽ 1อmิ€x� #Hdฎ€b)–€€ uไฃหh่‡Ihs8=)ahl,|ภ„‹‚ฎแธa-�ี๐€.k‡mเ‡Bฐ!ฦb#ถ\I<฿_NํˆL|PH ˜:ภ‡S˜ƒ1ฐ'0๛mูหmธรAˆุ@๘‚ธƒ}H=กำK˜>X‚,(EˆŒขลฒ�ฺC}ฐ…2ƒ๛’ZุCpN' ฎ™ฌ�„~…;๒ีสี…PลŠผศ๋ ๙ำ/`Eภ�ฮ[†t`„ฝ�ำ[�ฝ จ‚yิa‡!8ศ๋O`ONธg90ˆO�‡ฦล€0Dฦn่†" f$0Rpะz X[]๘โh cข~˜ž@YไŽนฉn ‡!€Hภ‡~ภ~h‘#zip€ZศืNธC…ืŽ‘Eฆ&ฉ ศh"Šื|RŒฐ˜�™�J–ลตฦI(€Nภ‡FPเN`hึUK‚‰ึื$�ฤ‰ฆ‡eน่๎ๅธ‚v‡3Cว@Q4้ฟฦ6`๖์ๅภ€0„|p๋ม€ธ:f้.ฐ—T†เฺnศkiGH‡/ &mึ†”๚�5Dnั„ปEำ[tPม�(ม.8` €@)œZฉpิ€๘dผฆ, =Eษ„ร์wุj!1พ‹—n„หูƒvp[ร€<‡โ=ภ:ศปฑ]|pƒเˆj€,…w@Rc(ฌh�ฮ@ี~8; `Ch฿สจ€ ศ†P…ziœ)ทJ 5’A จซ”0่ช�6฿้+  ธด๋นA๙oRva‰ร>ณ„8URSCนŽศ`H:›a๔ 6L๓วr๛“‘!€.JขdW8“K˜cx‹“xนญะs5 ว5†ศ S€I‘W{นWซบP๕Ož’ำN€XW ฉทPฐดs‹๔6+๖sรœ+7A“ธJบ_v[dtะ095aˆJx๘ื3t6L๙๖˜6W›Ž�ำๆu8Tƒu{_7p5yท^'™?y็ญแ`ืcูD™ุ’D_n—อํ๕P˜@f‰พลจ๐• กส “€ง‡X š์:-�๒ฌป˜y[\S†Hฏ))�บ �zŠม‰„ฮ๔Vx=ื$๓ๅสˆื�A๙ง˜ˆฃฏyผ` ž ฒ๏ˆฅ๙ด(๊-—x฿โzภุj ร%›€Ÿ›x{yอ‰ไช‰นO๎๐]_ๆˆภw๛€๛คW๛ฤW�Ÿ˜ˆฦ `ใŽk"ฦo|ด�+ ะฯ2๛ฑ฿|•จ|ห_�ะ}๘r๚ั7�ำr™Ž•GยะoŽึ€ื7“๕2ิว}็จ๏�90Ž-)xๆ๒;฿�๐จ/zฮ�ไOL<†‹<6fˆˆL›H_}ไF6zKJก”ซZฐRcˆ@ท‰ ู™๐ฑธ๚ข8ฒ1๔‰๐‚}๘„;ฒs/”ๅศ๕W ›rัˆ*ุ���วค€„ 2lุ๐ย "\ธกlำx ˜ล/M†ผ์๛ร‡*Wฒl้๒%ฬ˜2gาฌi๓&ฮœ:w๒์้๓gฯว6๑j๔ๆ?ฬฺ๕3ๆฤC๏�ี๋†ย :ฅหคก�ษรื/”|aR&๐Co†š.ฌ้ฑๆO� M8 ›žขGlแ !�ม< D๊Wฎˆ‰ ๆi๗+G�พ๙ไจศ0๏^!BœqiT;Š=KŸN}aแร‰;~ญฑcศ’๓๎ํ๛7๐เยUVจ @ )R@T@*thะย R˜ˆk "L˜`™เ:*B„ `\AB๕+J ”aœ๘…pขv�Y]}*ˆะ เ IไตXcุ�"‚x๘%จzข&tŸ~Ja[C`‚ŸL,tbกUx{ $ด!‹์=ส2ค)งžvิO>ฆภ@ซฏฦZ%คฆ8ปํบ๛.ผ?UC.้ิฃ )pเAศ๘Sฯ8ntู€ห�ซŒ�๐!ี๘ำ2žฒTBฝ�ะ๓‡ํ๐H.กั1=Œd์_๛5๐1!g\2�ฟค0ฏ2งxเ�,ฐƒ�QM=สฬม�ย ?qd qK=เ bWะ�๘sห5!ฎิภเ๘pี๙กฝ�ผ1แJ%Xc ‹<ขMPm5ึ,ห/*่šศ|ภq:�ผใ•J…ั2สI๕�BKฑ 0>šฯ;!ณW˜ึT๕GŒ]ไข7/�ผƒH …ํแ08™`ฅะใ�่rR‹Qค๒Ž?๙œย ;�Ž2\˜ะา๏๐b � tA>�xšา�ดƒ >๕ืฃฤฎ�ธ๋>š์ด๛ะ"ฌ…$ลaใภX b=์ioIQ@๖่ก$„p€ ‰�๔Lะ|Œฃ๏8ลิว>๗™! 0ื’ˆ็ลซ….|! ฿u”a Q€?ึPฐยcร$ฎXโjศB'ภลž<0ใ\๘ธWฒ)`]๘๐€ >๐€7ะ‡(j8†' vHศะ Lภ?�ฉภ์P@@˜เมขฐ ;`็`ยx่C ^ƒ3ภร>6‘M(1Lt"นว&{$b ~ะG#&PAิcะ"Qˆฅ8rา“ƒ($ˆx9 !๚๔�  ็ฐ�’!ŠL ”c8%เ `เๅD,‘e =La N A฿<0‰fฤAหII ็ƒ 0@ๅh�@๑จโไYSๆค Aแ‚@๒0บ•X‹Zไ ‚`„k AุAึXย€ภ„ศมํบ1ฟ`F๚@F:๚ัˆ�ƒฬภFฝ์!… ข๔ศ…5๚๑ Y`ฏุหhY…~เใ�่ฦ/vจE}H#๏_F|ัd\cN๘B*rŽpPมเภRQ lะใฎ@E ๒งะ- f =่nV"ภl%„XD;\Šv%+m้KมL�แนศลX“ญjk!&E)6ศ[D๔HชP‰j๊`๘˜G-ฺม5„@–1|,d#+ู—@`€1QVธbง4| €KLใํ12ถˆZPแR Ac�่ฦY .!ุE+rpฅ}ฤS ฑCLภQsศ๚™@ ๖H w›qฤว"ฌa†์ณž g6|c”โ Xj—ดฺึ ถ8F xPˆย'่ม6่ `(<%rท!ˆปข%ํ ฐ‚{$B@*1๋>ภ0๐vษ�oA‘�ผsฦ”uลˆ,7š จฏโds#�่B8Dม 9€ร2‡Nอญ๓๕“‡๊ไ1O‡๋ถลm๖ๅ‚f่!!€ฆcปl„ฃ�•2ฐlุ‚:ธ†>ฐ €*๐รจh)hโ`่=ฐฐ ๐ฃ€ša&ฤฅฐ๘P„r$‚‚Œxxl`‚Œhแ๙Xƒ˜ลว€ ตจฺก d #€ฒ”ฉ|_0`ษM๎LK่ชdl0 )ุ6ๆม;ใYฯP›<ฐƒZ!ขfHœ็—ิ\ 0งvฐE7ฺจ%ธ0~ฤ"c“}6ดฃ-ู”เเHG5๘!‰@r–=‡œ�วˆธˆดEฐม nแM„ก$0q๕๓ B๏@6ZGฯษมX 1‹?๎y !�`'K>๐‚c@แŒช๋ oใ�5 2^]๎sง;์vท jpzิ#เ๘( ฐ4ฬR(Rk;?—I†�™ภขกˆˆR!ศ‡"ฮจœrกcƒ=ย=ฬb–ฃ่€ๅ๊cฑ  ฉ๘ว2\œอ๚� „Cj(< 0#03V'ุy@<แ 3ธ๛ฦz�฿๚Nศ“d…\เ�ฐลˆ˜Œฮ ุศ.fร1ๆ;๐Cษย#V.๚ฑJฐAq�ดXk๔)[žูาuEม)ศH�ฐ๒<`cบ .6BžžbvI๐< |Q๚އฟุ%ท ?œมฝ๓&ธ๓ฏIโvจn#sต ๒+ ๙Mำ& V<lภ;!ีg?tมsi“ฟ�ๆNRŽ:เ` ๖เ„ฤฝอUv�ุธ พซFรSืJ'CXƒพํVฟ๙V?ิ Aะ@ไภฤย1 ะ@4มWh@ิ?œร˜ภfM\ฐ‰ช‰\`›นๅ฿์_Sxสลี\ Aฬฉ๗ู—,�เš ]ธ‘€ฮฑ‡Jh’ DƒDย4๘�]าQำ!€3pQฯ9D,@ม5Cd“1`d@ ค@a๔YภU)FคS้ๅ`ภใ์ll…  ฒ่�KVใeํอž^vYุ60ร์Aใ‘3h๊™@€Ch„Aฐๆํ uภฑ‹g‹ฆย6ิC?ธม้aC ย็hQ‘]I๗Mฦ"uข�$„สA@…šD?8 „€์?ˆ๑yฦผ\” ”ม!ดรFฤลญ!Ÿ&RŸGจ!†ุภ๋ –๗yDHฯ๘Ÿ8Ž#9ๆฤ์Bwแœฮฦ(Œ‚ ๔ื%˜รeร ฐ� .ร’ิG2ร�๑‡l%D_,ƒ ,ฏ ƒ% ~(ฤ!�ศhภ;b@ผU lร!”กเG|ภ=�ภ.ุย ฐฃ;ยฃ๐ท๑็/๙็!้ร&t#…Ah‚.(›่ƒ-”ม‚LภะA;dยTA3ผ�\ƒnา5๔g0ก: ฅ{&„€ย6€ƒ‹‘R“พ(„>”Bt&%&ฦ”&„V @A‚FM*iำB€ฬYJ@�ƒ„DาXฺy่ˆŽ[6„ƒBจ„Rจ๗ํ‚9ศมผA๏aแhQ•คm�6่B*5T? ƒb5ยใX(4าส9œY€ƒ6๔#„D B?ˆยIH/ขU7(รฐ@ฝ,-4C;่›iูฃฦ“6ไC7ผ…n๖ƒ=Dร6๔@�ลเU*„€@*ƒzั5ดƒ›ค„๗yB๛ผT ŒXนิ�”•ฐ”Kล*  €/ดLFvๆลถ‚dpิชถ�๊ฑVbIyเฐ ๋[˜ิb€„_ฃส'มฌ8ฮ‹&ไK�;nืะC ฐวhัร<จพxภ๘‹ลdย’8”@'ผƒฝhรคภ๔@cฆิ~ฤสT…๘œ‰ดl7pA �ภ5”์พhย2ฬ๊(lƒม\ ไฬ๘Ž2|Vdlภัว"ˆัจ�ีpB4ภ๒�K7�ฬJdศึ\อdอคB0ด€ฎtC& ฌCฐS๘=�,Iญmญกl@8Œึfอ คร0 KFtญ=”ย’0@'|ญm๘-เ–Qไ ฤ’๚ผCคDฉ[ธ\จฤว†์ศ–,…tมไƒ)8›C`�–/เƒY€/ศƒ)�<�‚ xภฦ๘พดี”b\DE>pมฬ์ำ^ u๐ZใASBผฺ้1ฬjอœุrฯดƒ)rjถร5ฆิห;รdกTร?ผC�ภ2คร้ฆฎ๖ก+่Š.šฒ€&\C&ค€mTภไS ฏ �รtC ,I๕\๐ADAฝศรฝค/๒ตo;ไC/„ x ๘.,$\[UB @๓Fo:L๏ษT”0ไขม~0O–{‡ศอดcจuŒ–0เŠ„\€8ตˆJ’Uˆฅ4ภ�วพัŠ,‡ม้Gq8Š„8วด�xl  pzเ‡ƒ\€‚lsฤ๐ด41{ด Gวtค €X Kˆืˆt\Drxpู†e`Jโ๘ˆ?ˆ†€G ฬH(@ ฌGURŠAuพ@ด0€txซ�1hา}Pน”ซ0ˆ๑ซ„ งศzีGต`!t>๘ย\„@ภr0ฒg)ˆ�H๘ฑ „‹€ J'kqB$6ะกณ„ {<@+ซJcวr$„.—ะิHx,‡wฤIฐภ@ง#W๒%g2'ข)+/‡16€-฿yœ.'จPKwš€ ‡$SณXsภ2ชห.+0KศŠิด์]ว�ณ<ฟห|๐ .ฤO๚VB|ๅM$™>O]B.„qก้จ ดB๐sB�๓dH.CำDEpโCcๅ h-ฤ <ปDF/Lม5ผYBศ’ˆหDgีp8ณy€“4m๙K�MYค K'™IฯRM„„ฟีดร…C,HCฤR๐๓Ds4I๕>ณ๔ฤดฆ-ฤFฯ3TGu๙ลฑไsp๘ใNฬ(ูs~`ตCx5ดE3ิƒ& f ‡ Nyศgf~„๓r-•ูMV[งAะJV\ƒJ5_๗ต_๓†V๓Bฏฤl๖~ศX๕aทั?ฦฑคิK�ตOHRลQW6ผ@u<�ตป\ถd6h‡6Mh5b'vC6ษถKถj?f‹6lวถlฯ6mล@ท‹usi๔lฑDวL�voืvNP@่6q'ทr/7s๗Fjป ม˜ุ๗mซฤoWwKP๗p7wL4 ยฅpทx7y—๗iถi฿Dk;ฤO–ฬuถBผ7ฬfท|ง๗NŒฐTw”4{๑ิ สDg:หDsL‹x๘สJ TD.•ฺ�^๗D$็๗vฌตJ3xคG7๗ —๐k›ท‡8ˆ+๖zใฤvณ7๎๖o[w}—๘yห7oT$,๘ฬ…cแผmUผ 9„wืKฯ� t๘ฏx€!ค�ƒ<๘C70ย’ิจ>ŸธQ4@ะ‚ฝ L7ฤ๘ A”ผ*0lŒ€๛D=ยM‡ธ™Ÿ9ˆฏธ`\/ˆ 4Hq‡ท-[สO&NŽh@œตฌ‡ก$ศ-ฯHโHฒจ�6#Gt˜!ข( ;�ศ›k4€zxภ†k1D๐น๏นฃรQrœ“CœW=ไร1คF„cCR?๔ร-(–ฐ‡žธMศvร,:M†*ะ|้�Š 1 ษธ€;•0 ปp:หศŽ`‡;r*Žมx‚F%™<ม@A$=žc๕E2B�9ม9š;นรถš�6 ๗หฟL\ฬ‹ใ� ุT@RๅM>€๗ONภไรุ†ลไ้Šฮุ ึŒสวXŒศ„ภoห{*(์)ภ€Œ@๏�Ž tm>4มชO=0ยlŒร TYŒ’€ คCลŸ์6X�•฿†�I8๑C)ฤ’JœW–f๐uฌC,ภ@็ภย^xภL ‰*Œ pGรˆ ูผฺูฬ๊ฌS^๊ชภh้ƒ>ไ8คKี\d มคื€#Sภr�ส%™ๅK@<�€haฮีAyyนฯ=�'๗นqB๔ะ‘L ู้P6รL|A ฐ๐ Lม6Hท~ิSQจ*Rๆ4ษ�ั‘ุ€hD?ไC.hƒหˆ๊CEิD ‚>ภ TA ภม^p>์/hC>8ตดี[ล�bŠ�ผ—'๔ฏ\ภh๑C"�๕C่%-ิ=„/4™ \แซีภ%”n6ถฦฤEtฦ�ิา-แœ.yศ@ๆฤA™(AŒ<๐tย>Xv—ฆRฑ’+มา�G L@œcย@Aƒlเเโฺœ€b›Rคˆธ‘cGA†9’dI“'QฆTน’eK—/aฦ”ฉRCM›7qๆฤ `+W.% ‡ $0@Qฎั†ฐhมHqB . #dอ�N‹‡0<จ‰`WซŽ•z1แรฝFbT$A"ภ s>B่ฌpขถL- ยสV๎แƒH:9ฆŽ €ฐ†I'ก”’J๚ฉƒ!hข :ะ้ห0วจ3๓›hfฆศ"FšูeTเ9‚€8|๒‘.fฐคŒศ eเZ๐Aรšž!hƒ"มFšB`•# jh&bKg›"lจ! ธ‚ŸdjฮƒI` BˆD˜k‚ ‰ฦ€H๒+จผd v‚Œญr5 ZถYรWไ5(ุ€€’e�Ž`– 2Aบ€ 0Gn้M]๕ีYo�๕ื[สW๖{Ge๎ปไ.vQซXI๖‚”ฆบ`ฎ�|€ค>ึ:aAๆ� &#hf“ต>` ฎm!>,L0qพพ2yTA‡f๘iโลส‰ก‡œ้ฉ›]ฒ‰q…‹0„]pllc)PŽกจB†โร9P�A–ีhฐ€ศ๎ุฒf`‚ธ@Žก,ผฮ๐@2ธม`+ฐเ๐ภaฐ: H€ Tเ‘ซQ‡ [บ `7ะG<€คภp0ึ|ถฝ๛ E?—ˆ–%'ฎ‚P€๖qZ2tT`.ศŸ2Oร"ฃั.’_ลx`—G=๎‘}๔cLf7ป}1cฤ5มฌฬƒKศ�B9ฑ lฦ๘รบ0‡ซ\blภรcวŠeผ 5(‹"#ADtม๚hD&@|œbcฐƒยง— (๚ศ…6ฦƒD�@F;๘๑สง`E๐6ดs€[ผฃž0 '&โ �ยุ@เั|˜Z,ศfhรมhfxv๔Œ ซ)ๆ1๙!‡ื๑ุ '”\()Ÿ$€ฌ ฯศf:-—@ฑmP‘R2ฦ0†7l๒ŠOœ€ ฎQศC๒A‘V$'ท ์แ ˜์?ิ5ฦภB D]๕8xัิฆr„\ฺ90์E5๊Q�‘šิyฅEิภŠFฑe€ A(MXร๕จPn*^p๕ฆHมR x€ฃ‡LP‚Nผ#๕ะฦR@"้ศE?‘› !]ํrฉ4 ฃxG–ŒQ„ ฐภึฐ+ Z IMศร &0ฤ?� ‚๘aYาF&`เD!้h‡]3ก ‚ๆ`ืd y0ว๘x\7ภM\#ถนš,ซ:คเ8มE:Š €tB]5ฦd+บ3ัษœ ๑€*xqยˆT้ิrู.`d๘ฃฌ-@ซB๗าLx`ฑ~‡?ฦ1‡�ผท๑๏ า�CT๗สxร…ŠโG4Dถ‰B:�H•jมx 0iูkเ(•ร๖๐‡A|5ีฉP ภ†P€ฮื”ษฑ x€0ข‚�P’< Lะ€œวกŠ@�#W๕ำnŒ‘ุ‹‹/ฦ1%ee)ๅล้ฒ 0(y*P@ˆ,ๅ‚U…เ@ฆพfร๓O1tบ,a—y@ห1.^<2 ๙ฐ`  ”T‚jฒูภ๙Rz„ู# ]Pdุหภฑ`‡Œพw€ำ=๖ด6!F@tน.gƒ˜๙† vYฌเ็ล�6ส‡ถuˆla{ุภ—พF@H$7ฉ๊ Rตš๘&Hภส0-ŒยMSE‘iŸ(6`ูˆน ”$ผb๘0G บ[ข‚…aๆVดBิ0`ะ‡…Or€hฟ"Htำt{$แ ๗ˆร?๑่›ุท๘ล1๎ว}9tC;Y€!�ัŽtŒ‚œ 7JvB’‘ก๘`Ÿ,/,แ8๐ ลCfิCCศฮ3tก่E๏๐ดเAฐ8� Hง6‰Yโ๔’`ˆ้ชฃทยQ–ฃH�่]๗๚ืม๖I}$'r ูลžvตฏํmw๛‡Œm์ทฯ�๎uท๛�๑žwฝ๏๏}๗๛๋ฬ~vลRํฉ๖฿Ÿxล/�๏�เ๏‘”3ž๒•ท�ๅ๛xxEวvช€ฒjQรF๔8ไ  `Zะ๑SŸSเง ˆ€L h1Ÿ{�๏ž๗3™ผไO�๙š% ฉx์อ^ำ€;�wn(ำ#”qยš|ภshYDๆำฺหฦ>เH .ฅ๗ๅ7�๙ัฆ>๘ ฟ@Mrcฏ$ฤjKa)ˆzˆ๔$ฅ,N`Nฦ˜Aฎ!‚+�ข๘a fC`al`^ม4า๏10o�6โ๓Žอ:v€ฦร^ `คแๆb.� HŠ๐รถ b๒ \เฤdก "จ)”‚)fc ฎ l z)ๆ40 •p ีŽี๏ไ€๐�ภAF งx@าE jยqŽภš!4AA –ฎ "BFhฤF๘! ยvAH„P l฿˜0๕pƒฮ › ;`€` ย €Rฦ์ฐP ง =-เ >A่€n‹#f` ฦ$แ5`ncขยโIฑM๑�๐ …jยƒdง…แ์#0`็B(คม(ะฆ!fม>:(Žw|ว‰6‘ˆขภเ aูN๑ก1]gฤ�:B๘ฌQฺฆคจสเ &เฃBส๚๏4" !&A@8b‘้‘ฆž>`ฌมคเP๕q๙ัC๎E$จ‘๐ภญฺ. ๘๊ม๒มหช+ฟr#๎€†€ขธ@่Kฅด*ุ๋ซBภ(j 4*ชIฒ$Mา๗ไ ฯ๓ฎq๘X(ิT,๙B-}`ร๖pจ ‡ #ญลล` }r่#9„๔ O๒(‘2)5ไ๗š!˜2%SRDtรip@ ˆAฤK)ตr+นฒ$˜’��qu(าาA†เชบ2-ีr-‡-6`ุ่2.ๅr.้ฒ.ํ๒.“๐+CL/๑ฒ/��2๗๘๒ร๓/ ณ0+.๒Dฬ^81Q.๐ฬN/S ร2๊’% O2W"2 s3๙P3หN1;n0Ib%ซM๎ “/+“232Wฒ153a“ธw)๔frิJON/๕j‚๕R@๔$@ภ‡#dO4€ิT@|xํฦ2B!ฮŒฬฆ-9ฆSอvํ|p่8ฉฑ%+<ม3_$“<�๐4๒ฦ3*ืณOยเเ Lvˆฯ๘’’o๙๐ห๙8๚คOจฯ๚>ภชม๚Aะะ๕บ๏๛ยo�X'` KNกภaR`6๎ ๊A<E”� ย๙ผ+DGิCไD4�F,>‘.=i๔Dฎ๑<'ำ)mG�SGsด%แ๓FKsv่๓HEโ@มhก D #*T'ๆฏ�๎๏ ˆH�๘เ�p ะ๒€�€ พ((ธฌฦ P)ะ4Lค@ไ ฐฤ๏VC6! 4!]FฐF๊4๐@O๙ิOGKล‘คˆt�ีH…T ซฑG•=ๅ3G‡ดR9I9ต#l!ภ!Š Jsข‰"3…>n๒ใcpk๐s0ภย ไ(M…pHคYคšกฒภAH-`zN€>์ฃ๕�’uY›5ŠžีU}€4u'ถ•ฉ"u0ี3>แS…ิษต[ดSี5"j‚‹ ๖กŠภด5'pŠฺ‡ ฑ‘Yพ0 วฐ ฯะVเาAย €l#Bก&ไp ๏P' xEX‰ีฌ lŠ๒๔ฺ€P ฮƒ›Ec+Š:๖cCv_›]=0๎ฤuH35ฯ๕^8ฯeOฮ=7ี\1“eAs]}ถ]{š)X bํต0_ทPd™ภ!Q)ั’U ๚ิ!61:๑CQ๕p‚’เ˜ภb‹U 8†€โdAV_ฝ0$7๖ิvU๖xึfFิ4=pR�มsf{g!U๘`ถnSำg9๕ฎภ๊ม’(่ueฑ‚j‘Uง(wฑ๑Šโ่ ށI `#๒b’q›ัkฟXคJ่A ข•ˆa(}เucwvk—ŠP—pล•[ท5oypื[�3\}๗5 IเNAใช์%ท๑ บ๑A*KษััQ9 ์`พเ ผiRt†ํMdbyEp(V ๒tบ Oร`>j�UY uOํuP“ทFธoว•x 7fวMS๘w�vy๔#Mีnqb?2!cฃฟ๔‹ิ"r"ฃ ธ`'๗`ฝ��2†(€#>๒Bา4ยDภfˆ€ (2šD-หภ˜k๕B”ƒฉE‡ฝชณํ‡าq“ทษณx ๘)Sฎ€7Šีo•X>!X]O๏5Mไ�&W,Joิ@9ม,'E TฮŒฮพุ#N(R†oO'q2}$i:]mสV๏6aฝฒSืย๘8๗๐พต\‹๔xwy฿]qถŠ—G ู[๓Š!8*า๓žP�.‚*ญ+{„’'N฿@๙ ฮ37ฏ’กาR•๎6๏+GY๒ผ๒3๙‰;PR;]#๙#.“�`Y’}9uฦฒ,ฯ2;ฤ”I4�ล˜�…Rwfo•�9š‡ฮ-ฑฎ^ y)]vฏY^โŽ—U๒•qyGฅyœษนœอ๙œ3n›ฅ1—S™๕™ัyœแู�๙จ๊9ๆ9ž}9ŸQฑ�˜๘;ล๙›มนe๖%๎น•�yฏy5 7%�ะ<…WŸฃY“*}t‰),›ู‘ูู ๙ก™f“ก;:1#ขYข•ชeฅXo๔™๛ึQ�Y3/ฺ3YZ<:‰ŸŽoo– Xxก4Kฺ๑‚—ฃƒดคUšuˆดษ™œษ!�ษYฒDZธ�แ-^โ ฝ'žแห‡�ยžฯํฺบฝ�โ}Y��="lว ฌ;;`ีฎำฺT ล€๒8ฅ3ำิ,Ef“Xป™c๛Qพฦ�ผไทo%ตผ+>่=^฿M^’E็ เ ๊ nมฎ4ก่aH@ฃญ@ด€D]fฑด๙&†EIt*Rฤ>๑s‚žใZ่Oผฦฝrxวœฑ� �พ‰พ้ีตส hก~ม๐!„••๖aBP�ดt~๕~U•๒ํ๗5LDI™ิIกด yผฯฏ�ด ้…~ฏ/;ส){๏๙พS•์ฌก„ำœศถu€”@ h฿> " Zง•YuŠ๒ƒ๘••hˆฏ1เSCuTKีฝํ�ศแ:๕�ผใMŠ๓;ฝAzุQ�๕}šย@*ช™#ศึ…ี‘"˜H–c=6 ` Pคm›acM–ขˆ  ค@แš}ๅŠ˜เ`ฐ €‰'Fิ€`ฦ‹;z�hฐขศŽ"+‚ฤXา"ว”,7žŒ�่ฅส3AถŒ)๑ๆล–W�� 4จะกD‹=Š4ฉาฅL›:} 5ชิฉF เัW.S @š๕ศBOŠ ฏY๎ญัภ�‘ุ๔๐เpก 3Y๖ฉม€Zถnแส๘‘B<อ4baใษ’;qพ| ™งฯ”'%7 9gHŠ•/f๙™rO “ฉš>:ต๊ีฌ[ป~ �h'ะไj๗+LRvฅbฃ€ฎJ”TDb"(i"›ศ`.b#blCNโภrฮKฝˆ>EˆŽฎซg์ <‚ฮ ?~Nั๒{ึ<Ÿ~�š=๋Gึ฿œมtZฑˆ`‚ .ศ`ƒช†� (เ‚.ยฌวาoม ื ,ROE|ภU›dกI?aŒ0ˆˆŒ1ศ5|๐Pข>'ฆจ�x!pห)Bจ{4่_‘ฅ”Y$ I ‘ŒํdไK๏�“IP†๖`–Znษe—^~yT\€มMSคฃ„ Ma60pG5Œ3G!lเมิำ&`ิ@œ�ะi'žwXรงŸtd'‹ฺ7e”๑๑'‘L.IเbGZนง�%9ฉฅ]†ำ€›a fชชฎสjซฎชฆ€&จ‰ ฬZA E๔–€ค @ "„ะมฏ ;lฑq•ขJj“c–Eฆ�)L4mj๊~ฅRŠmงHฎtY“ฺ�๗jป๎พ oผฎbP&Eevะžธะ[้€#ั”{S๕‹ไFขyช.eœ๚�/มœ E*Qเ26i“ๆ๐{Fe,oว rศงEŒšภ^b4๑‚)๋Dฒศ.ฟ sฬ2ฯฌ`ห'ซtฒน'Lsฯ>� tะG้,tัFtาJ/ญฯL? uิROM5ซNWuึZoอuื;่uุbMvูAmvฺjฏอv�ข`‘ไย7ฎ�v฿w�ย �J๚ญwเ‚N๘ฦโ•@ศ"+‚Tฐ๘โ*จxไ?9ฒ“Wพxใณ–KwแขNzแ|ทไ�B:��#ฯ?๘€ณรชฏ�:=CคNป๋ฐหพ:๋�ุŽป๏ฏA่ก—Ž|๒ส“}zJฐ๐D๔าแ๔าG„ #\}๕�?‘�๖�‚Žs๛m๒๒๊ฏฯ�ำอง„๒{€ฏฬ?? ณQ€�๗ใ๏�y€๓รณะbผตo lเฬ�W’ฬnu๘(G xืป฿ A‚ฌเ'ธ:�qฐw,B๙T9p…,lแซ X ภA฿ฐ „œ€8ฐaฌะ‚� †=ฤNฐรึ๐‡AโŠ(<… tกฏˆล,มฐ$๔ยบ€t(ำ3เล2�b@Œ],#ฮ่ล|JRYฌฃ๏›-ŠdŒณืํ๕ฦ@ฦ๐Yฟจ่-›แq‘Œl$R๔ธว>F’(’Œ ุ(ฦ.Gjr“œ� >ษƒคฉฐ“ค,%๙€‰T* e—,สL หXโx{ฅ,o‰ห\ถห–บ์ฅ/้ ^s˜ฤ$f qิห’้+ฆ3Ÿ‰ห่`i Š0ก‰อlŠ †่3R�ฮล‘ œภ‚0ม)U tfK›์lgวฎฮPQ๑Iyœ |aย|�ํJ‰ฅ;� ะ/5aˆLาตา0ร-Ud@ Q 4\ 5h<92€Wุ‚…dD? า’ฉะ�\ง2�๙JเGx~ ฉสtฆฎ™(I_ฺQ‡„30‹๘Ÿ˜าtจDefสrŠฎ‰๕ณ -PA‚?uดจTญ๊R�นOคฎJŽi*–AผœZuฌdลL+ท%7lกี3หŒXi๙”uฎt-ฤฒjา••๔"x0B‰W Zดฎ„-lkฮ๚ฐL&E`ร<˜ฐLษlข†ญฌe_ƒUกเ๗TฎyูะŠvd.‰Jd™ฒฒัชvตฌmญk_ �ุสvถดญํว.ภCภb6ฐญo๛ฅ1uE68-p‹�`ฅlrŸ �ื”ˆฅดŒฎuฏkDAa�Hvฟ �ฅ\€Hฦ'‚๐ชwฝBนภฏ๒ฐ � ‡ฝ๔ญoEฦไ‚kืพ�ต/2บั� ˜ฝy™วŒ`๐2เ�ะอ| แไNเฤ แ ๛vLd`r แ�ถ7.`!rะซ8ถK๑Š_ ใหxฦ4ฎฑoŒใŸ)VฤยpQะe๙8วDึๅ�ัŽ97) 0D=๊d�"K–<}‡6เ๑๚eภม5–ฑŒ!€xสdŽe.ม !ุฃ์ู๒F €#c.ณ7๙8-\ƒ )๐3ภP็กเ ๗ร๎@"ุ้ฮŒfdbเ‹Cุ ๐�0-”ๅฦ" ฐ.<�F‹บŽ8 ˆะ Lแ 0๒ONฐ%$็P€จG๋+ส$(*@ฌ(ท ภฌzใล๒ๅzูu#ณŸ ํhK{ฺิฎถตฏด T V™๛bท“%|a{�๋�ภrัปึษรง@ทบYWSˆ›�๔F�˜j๘ฝเ›{;cฝ�=บ1้๗* ผ ๐„็mเ• ร็ญ๐ˆ›ํ~€€‚ฐ‚[8|ฎ–ธวณ6ฆํƒk�ฒัŒ• ทบๅํว_.ต�Tแ„xณถฬ b`˜ˆp›A�‹ร|่@#ภ ์ ๔Ndx–ฐˆ�5‘่*YฝN„ฦ9ŠIOdนอ%บื‘F€;�C ˜ฏกชQeฬ!ฝ 8ๅ†yW€zๆBoIดา#T๗๋|�™,ํ\ผ�E*ม\#9๗ฟปุ`ตห]„๗}๒2@ใy๐เ๎ผ  ่1N`At ; ศ†๖.€K�ULไ5ฏา)๛—9`˜ศมƒk๔ทp@.tูว&’ตT$‘˜F(9`?๘ส7๖ฬ™ๅ1yŠิ@ฟ<๏๋ขเ๋ๅภ€%€แƒ๔โWฟwoพ๙ใ๕w๐WD๓เกN่ฎi„cB@-Ja๛‰�Wฃ?ฟ�]d`Rj‚ท ]€�#,‚x12#>wธg0โDNๆไc0ฝ`a “ๅƒyga7veงsWww7]zื. ๖„O=9ภ`qเ”๙ไ“Œ–~+I�’–็xะฑ]�ี.pๅ B™P ต’yL@i้Yษh^}L‘zซ7^ๅu^ศ*6ษ P5_ถ’‚Y„)—v6{ตwAA|ฦศwสท!ญ‚'จเ ,จR,ตLใ็9ฐw™ูR‡YfฯW—Kแ}เ'~_๛ๅ*xย ชภ‚<ๅSั’๙`ฒน‹)š1–~0)^`๘7_๛`ฎาiๅะ0ุTOUะQ^ข`NฑœPฅ&ปYd8€|๙Œุ 8ŽUซา €๚€w่U`ๅ]ไนaqM9_E<ึYdธบ™)ธ‚|้‚ถ*\†ฅ็›๖โVกT�Žyทwšr\IŸ3Fƒ6ˆƒดุCX„|‰„†Ÿ ‚'„h%ัW•’ถQ๖ก ฺ 2ๆ„†…ฐhˆ,j(9"P…Wุaถ*วค| e"ัX%\ไe? Y'šcsX‡pn้ฦ:’๘n๒oฐzศ‡>๖‡'V^RU  6d›ีY[ตภ[CสYžeค9ถ‰๗vŠฑศŠยแ‰ืbซR& :lปจ?!กi๚bธ˜Œ4dวงƒJจ…jจ‡zER๗เ–^ขจˆ๊‘ |๚จ]Bฉ’ฺ|@€ ภฑจ–บ@691Y œศฉžชฉๆ—ฉŒ๊จกj�ฦะ้%N๔Pจฺ ชบชฬ—ฉJ๙ชด™b๏9&1oรFfฌ๘’ษด40=pi@&7‘ฉปJyฉ ฎzฉ๔ว�ฺญหิญ;ภ=z�๚ญฎใฺpต้ฐQ0Dะkิฺฉ5jญ}ืช J `ญำr@Kc‚ํะ๘ะ™ภ๒ฏ�ฐ{{ฐIH@NฐุฐcภR\‘ซ๘šฏ_ืซ�: ฐS0k0+ื+SCะ‡$kฒ(ซฒ,๋ฒ0ห2@q๐ y@ ˆ <€ซบ๚ฑ๚zฏŸ๚ซ>FหT/Pฤ(H‚ได)Nฏ ย@ต� "ŠŽุZดืฺฑHปญง๑8ƒ€ ะK&Jี๚ตDฒI‹ฝฑ�`B@ฉ%แถo s^Kฺบฝ๑ฒbถb{+€๋ท^ทฏr›ฦu}�ธทcซ%”[นืpฌาน› บก+บฃKบฅƒ‹นฑ!ธ‹ถฆซp—ห พŠนš๋บืฦธฐบ ’ปm{ดต pจห ป;ด๋ปิป "ป|�บลKoท+ผ๒ผทหผุผ ฝห;ฝิ‹ฝษ‹ผ"�ถุ›ฝึๆผ�‹ ั�ปแkปุ๛ผชKพBด่kผ��พฑมฝAผ๐›kใ นะ�พา‹ฟัVฝบ�ฟเ๛�ฟะvผ Rฟ฿๛พ�lŸซ*ฬภ,มLมlมŒมฌมฬม์ม ย!,ย#Lย%lย'Œย)ฌย+ฬย-์ย/ ร1,ร3Lร5lร7Œร9ฌร;ฬร=์ร? ฤA,ฤCLฤElฤGŒฤIฌฤKฬฤM์ฤO3$ลปลิ&ลDลU,mW�;Yฌละฦลซใล_ฬla�cLฦนfฦhœฦ36’wk�ฦ8ๆp๐r<ว6๖ เดP"l-กว{Lc@FsVWศ†Lcก แv: ษ2&„œˆ‘ฅ—ฦlœษ"V็‘๋‡˜<ส*ฦ#>$$mFศกผส1&Š{ ช<หF-DหนLdฝ์ห8ฬม<`!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,z 5Hฐ มƒ*\ศฐกร‡#JL!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,z *� 8มƒTธแฟ„ !*”ˆะแC‹ flฃG‡†|!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,z�5H€มƒ�)\ธม‡"dHั!ฤRdh๑bฤŒ66๔(€{œ8„H(๒_ว‹ฎ๔ำฅ’ฅH @x !C *(P€ฅ€67โ๔ณฬ_=en€ฦฐ๖Ÿ0HCถ„ ภา95Y:๕ ยC'ฎ\…Xฉ๕ฆ€Kำ^0๘ะ‹‹FจRหถฅหฎำx\ธะ†Yy๗ฎMZ๑mเม…'ๆห˜ใ�l/0ภe—“`ม0A‚„_ฎ—์%๊ฉ_!8p s ‘›7ฆทพฅ7ฏ8@)ภPขำปwงRœ~+L„ )‚ฬ ‚ ๅบแ๒ภ๐ะ ‰หตs/๒t@!๙ ,#‹!๙ ,#‹!๙ ,"zฌะ@ฐ A�*TˆA Aƒ.l8๐แA‰Zิใ?Š9zLrcGŒ4€๐@B†&NJL้g™ฟzส�ฤ๙Q K็ิd้ิ/ŒฬŒ\š๖‚ม‡^ฤŽ2๔ii ฺ0“Jา็าซY™I7’ขาl/0ภี‹kฯค—์%๊ฉ_ทLมPJo^=p€RเMกBฉ0$คฬำ์W ๑zต ๙bู€!๙ ,#‹!๙ ,+zฏhHฐเฟƒBXฐaย‡ :|จกฤ+^$˜๑`ฤ:�[Xก‚ $B$H B…H’1บกbขฤ-[)^่BG3F)DE�aBง‡ศไ;ฅK:‹ุ˜0aถR>0„์ธP๊„ตฐ;‚‘+1š@รฒl‘:C„ศsŒJ -ื�ุะู`Iพ:!4,่4oˆฮ Dˆเะ!Aƒ–:^ จตlฦ_!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,6zT5(ยฟƒb Hะ`ย„ |QโDŠ#JtˆQ#CŽ1tฐ๒กว†3ZิPฒ"ษ”�Nlฉp%อŒ!^ฆฤ “%L tvด๙“่Nฃ!๙ ,>z5H๐Ÿมƒ!\จกC… :D1bร‰+Fฤ˜ัข@Ž� lุ AB‡ จPRไcจSvๆญะเมƒš1:LLั๋F !๙ ,tzฃ5H๐Ÿมƒ!\จกC… :D1bร‰+Fฤ˜ัข@Ž� t่ "T„ย…%rฅ๓G๏–!ะ0ขห(xฐ๑แัr$†์j•ฃๅi{‡ [+ข97x่tฌN ึฎถœpb™ $ิ›“,ซeoธิXŠ#0;ฝKWO�Ÿ-W60 A‚„€szผhwqห€!๙ ,~z‹5(ยฟƒb Hะ`ย„ |QโDŠ1”ฐ&ภ4 bฤฐ@Pำ|\‰ƒ€KCbฬ(P@ฅ”+)bกมๆวœ1t่y“ะŠDjฉณfัฃ จ$ํ LƒJลณ/ะœ7Y‘Rˆ‘*]>Saฃj @A"าาด(๗_D‰u1!๙ ,#‹!๙ ,†zผ5HP€ƒ@๘วฐaC #&DธะกCˆ Nิฒ}ม`.b#N�ŒJ-PถxXลช•ซื�Qฑ!ู)n0jญ^77&๏ูV$D”t\lX�pฆMœ4@P`ย„๎&ฌฤ€!cย.ƒ{�ุrh@!๙ ,#‹!๙ ,ดzš5(ยฟƒb Hะ`ย„ |QโDŠ#JtˆQ#CŽ1Pุ @*T€|ˆมร‰Gึ�ฝWd%ฤx์z๒ ›  š'ๅย$€fฌะใV:TZZไP๚O$FPอBC Hศฐ'Iฦฅ{}นqg_ขฎ ๖T๓็\ฆŸgซVhpR‚„)บZิ ุbื€!๙ ,#‹!๙ ,#‹!๙ ,ผzž5H๐Ÿมƒ!\จกC… :D1bร‰+Fฤ˜ัข@Ž� &h‚!B€„€C‰\้�ั๛ใA%Gˆ#บ|‚+[bx มฎV9Vnุ€เถwศฐตzำƒ‡Nว๊bm๊ส '– "Ab@-ฏ7'Œpๅซ—Gฉb„XขS5๕ด�IฑR`’$HHม๗ฆว‹sฏ !๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,ฦzค5(ยฟƒb Hะ`ย„ |QโDŠ#JtˆQ#CŽ1$h B‡ RbฬXยš)“i๙C€H็|L๘LMˆช์3Aฦพ8?NˆN+๛ค`X๙ร„ ‚ขˆ4อGาŒk4+ฃ SŽฏU ต ˜ณT#@ยOื…hๅสฐ—fa�7ดะ๚ฅขƒ†ผQ˜ีำ4$‡฿ธ!๙ ,#‹!๙ ,$Œ�Hฐ มƒ*<ˆE !ฒศฐ@‡$H@|มIฮ ˆ)2tจXฐแรˆ&’\ษฒฅห—0cสœIณฆอ›8s๊�ษณงฯŸ@ƒ JT(†@bุ๑„โย,œ8•B.ิG0”œ\y4iDฆN‹ŠKถฌูณhำช]หถญ[6dศภa$K x๓ๆฤธ๊#D"ฐฤ๑š๐EจgGR๚๖ํŒ ค ) ๛”D฿ญ�}๋3ฏฮj=ƒMบด้ำจS�0Q!Zzฦป—Eฑ!f…ชลV„/า<ฃDมดoไ(Ipย +ก�ฮ `w๏฿ชeฮฮ>t;๗๏เร‹��}ย‡r’,"œอ^4ม Tจx๙โฬ:J2(ซ�ฝauภฐA]/`กŽ;๎@#‚“…P`E๕�—฿~��เ€ูตท{yุ�ghฑ'‡ ภˆไต่โ‹0ฦ(ำ( ‡]hbA ์‘ด„มฑฤใ๚e๐ร0รœ…เA,ดะย 0่5Xด ฐpัBCyd’K6๙คx;ฆจbB;ฒXขwmย ขœ2ๆฉ็ž|ขถ $@ม ๅ4rQ @|! มHB x5Pมˆ B!เ€๛XA�Š:j๚ƒ}๋ค๊ %?ผ AvH�๐ะˆg 4�Pใอ‚ ชใ7��๚ '‰yg*ชชฒ๊*ฌฒb #Š9Š๘์จ�4ํ�้xโตฃ’๊�ดึ‚๋-ทด‘-นิา mŸ์ถ๋๎ป7ฑ๖ˆ5�ไยก4p8�Œใฦก~h“N=�ผAiพhไ=ŒL้)จข–;โฌ๒MชฉชณN,ŽADhิ>8B–Zฦร :,ฃC 4i|ดลcผŽฦ{ ฒศ่ฎ‡ฎดz๙gะ�’[ดบ�Ž›.žqซtฯฉ๘๓ัKื ๏ีXgตƒะณI�[ม‚ิƒศƒ\รXrŽHคข`G=€pAF3˜๐�๐pจNหมd )olล“_ 1k{7vฦ0่ ์;๑ฐรŽหŸHq‡ƒ~xโฑ,.AใCํฯืฎŽโทฌƒ+uถฺ ปำQ�t์TWซ๕๏ภ�bวl๒�ำศ1l#F€’\2อ ฌpO"๔ฐ˜ภ ๆ๘ะi^�#Eเ�ฮ๕)๋x๓ƒxภ…={ั R@Sน:์ธœๆุA JAฮ‚–๛เ'?๚ูธ]ธnW;‚<๋hฝ›šาฦeAคYญƒl]๎บ4 fPwยKก WHš\C9 z€„}ฌ"`ƒ=Žะถi๐ภ<ัP �๛ั๓^{0?�7œ/ย! ั ถธEN๐‹›๓ธ˜|๎MRคขฑ84ฆ0„oกฤชFต�Amvฐ[WำXวคๅฑ'D! IศBล…z๘a€ชA 9�a๕|8qˆu h„&จ  ภ ~แƒุeิ๘ชซคฎ=GE,V!‚Bx�4คภ‡Y~Agไ)S  d •~iฃiBืaƒะ„ใ3{wB7™ˆuป{#35(.Cz๓›เฤ ๑Bร๓HขอDFข' ๊Y๏‡"šwคเ0 ๐’�€`แ%บ0Nผ@๕กฦ7X%t6x.1Ž —๔Ht…๚."=hB:?‡�Q/‚ดc3]งฬ>พŽv|�่ฏ 4˜ึิฅF งNwสS–๚(EฎฐๅU@ื8[ฺึ6Ixq C;N1‡1ุม ๛ิภR@pŠup„ˆ‡g|ร฿hDชE ๔ล ฎpษ ƒพภsyhา”"Vฒšญjek +8Sœ’จฐ-5์`ESปพQ„Tฌ{Jูส๖tุ2�Qqฬa$ Xฟ>�ค"-จภ บ‘‰ ไ ทเl7ธU�"nศโฯธ€ล|ขI �ณfี‡oPƒ›`lA t#1ฏŒ#x๛฿WธุาฃUชบวาTฒ'Eibญiด ฦ�uฌeืห^nเ P€ขG-jSH@ไณ D๊P—‚”ค(ฅฤGทรภฐ(+8โAโ๗Œ>ผ /๘%ธQŒ3๐Lป fฐƒ!lตeึัi„๕.cั+ง{๐ฐถ[๑w_^�ษ๑‚ํอฑŽืk—`ว%{=A €�ฐ`08ŠฺัH`G8/X…^7ฃN ฒAˆld$ฏN9ัฒํ”bูภ๘&ฺMQดœู4œˆ9�G;Žณœ็ ”‹�“Codr|&c/ศ€คเa™ุ๙*จมq[าL็F;๚ั,dž๒›!$ย "(ฒCk"้๖อga4คGM๊RณkL/‰จฆ€‰TC๚ฬฆŽตฌg-ผ‘‰ึธฮตฎwอ๋^๛๚ืภn4t`Np 68až5�\tูธ@ vQ ฌไF B‚อํn{›ืธB?tq์œL`4ภ.pAh_  ุ†Vโk่ม“�ถพ๗อoฌQ€&€Iฉ คภจ€ะ%๕€เ08ย�ฒ่W>zFภ W|U๚…†ƒ,"Dฟ8�ธฆE๐Inฐ฿0นฬรใ8<ม๙.Hh—มYeธแ1X6>„ะF๙็๕๐9ะป „ ทฐG„YUxฤKดRy#4ƒ`ยฺmbถ›�๔จ็ |h๐ˆ|€#Eจป๕Ž๗^ร้ฌ™<ฒ'I{�O)dศว)๔Œ<+๗Wื�P‰ฬkเ8D-จ@ ฮอ€กC/Rๆิ{�๛เwษ�ฟ€†}”ฃ&เภAzศƒhทEฐ๒9%xK๘ะ�๐ทมน'R๘"ณ3์ฃฬ็|ฯ'�ฐGุ—HU#Sแ๗€@=€อP(,`ทVM…๑'€‚๗Nจ=P ุpG0Mค"€žQ€~“Rคู๗‚�$6xƒฆwW` ๕` ภ›†ี“ /ะ € ทg}0 ฐGCX„Gx{!=ฦ€หฐ6ะ8ฐ„ดภO่, ๆ๐ก~๐9‚28Fกƒ8X‡v๘mp ง ๖„Bx ๖] �ํฦ@Iๅo…€�(ˆ„œ’ว@) ื๐๘ฆ#ภy€ €๐s@žย๛c๐ †ั2ป`zŠ‚ฅ^wX‹ถศk!ภ9gีC๓Pเ) %ะ ๏๐ฆฝ๘‹ม˜ ฐ๙PฑฑD�…% �€ŒF1  h ,ฐJ ะ<—ฆ0‹าu‹๎๘Žฒ†ˆ-Q=ย ‘2)a_) ๔h6  )u๒๑ฯb_๖„ลฑp™ไx T €)šด™‘ฝึT“ฦ!Yธ‹:‘X ฆ‘&y’ฆ๖‘ก’0kฺก;ฅ'd(9“4‰z฿B*!�"csd%M8Y“@”ม–].YX'F‡lMBน”Lฉ'Y-๕!�E^U๓o!’.b•ั”\Y‹Œต7U”�ไ€ค“0€ ศึ'g™–kู•p)_้fHchwcymำVmO๙4�EุเC0 �oษภ7nN/เฬ` <ฐ ภ#!1˜…y˜๑—imQW �ะแ@6d4šขIšฆ‰š‰ฉ ‹y่˜‰• ม™>—บ้}ifu‰Xw‰—สo๒๖”ํมž€ „ภ˜i˜@1žะ…Pn6๑^‘€ ๐p˜wPvืœš้#�œห‰0ฃ@ �ภฆ™็™žู๋žาIaุฉ4œส้™ป๙Ÿd(“าฐp๚V‘แ ฆr๑กฒp๓5 ‘p wFp๓•ƒ_zB!7r%๗|๐%_‹2v๑,็ษ๛ &0˜{ข—(™๒I๕•‘‚)*!ง ถฦ3ะ ๊0‘ ‹’Šข๒(˜"+Š2)  8๐kPr ส ะ๘ *ฃ 3`ัข/jจ)ม2ฅi› ( @ฃG๚Iบค7`kฺฆoJ คD*qAก๗ล)UzฅY ƒ๊ค›�bq๛ฅFh'f ฃU*ฐ gO?ฺจyJฅ‰_'‹Zกบ›]n Pดึฃ#0 ำ0 ส]€[ฮย๙ะp๓ฒใ{น00ฺ Œx๑Pฌ�˜ ฌ๊hp ภˆ›8"Y๗[ืuฯ็~ ญฃUtำBl0nN8˜๘@ำš#๑~P �๒ีา๚ผ` ๙ภœฒขE™›ัฦ๑6o:ฉ�x `โซ๎ฺYXpŽนฐผฐ ๏€*ะ๒ะ� Cถ*v๘€ุบซๆŠฎเ€๊:โJฎ&@|*Ÿฌแึ@0€s�[ฐ ห`ฌŒ€ฆ-๛ฒp:�ฐฌqี`ฐ�œฒ›ฑ+žHซดLQะ๔ะ๘ะ J p)ซ Nhํ๐ *yพะs`#ะฎ�ภฏNื† 0�Œ0 ท๕๐ ฑา: ฐœ5XภŠงส•ฐ ฑPN๋6`๏0]{ƒ เ 0€y๐S๖@O๐p q#PSaณฎ%ƒถ S0OI‰ฐ~ pคุ•wyฮyฌเ c0 ภ fs8๐ า$2บตะ�ฐภ‹ะ๏ะ ุะ›€้ ้ะษ�p � ,pุผp ุ™<ฐ€„ o�น  ฟ\ƒ๛ภ ฺNC�0ฬ€ ลjRฉ ุpLZ \ �ฐฎภ งPอ๛ผัKผห›s0B+x‘€๚€ ฿�!ฐ๔kฟ๘๋ŒQ สp ืภ…p! ยn*Ÿํ๛๏ฟ• ุะ ใะุฐ < ภ ์ภ#ฤC\ฤGฬ๚ฺฎu� k้ม7Œ:Ÿ-Z;0ๆ›ฦะ ํธ0 ๘0ฌ €๘เ C`C:Lฐฟ�๛ฟNฐ … ”eˆ‚"ฌqP=`7แ3>ๆ #�€ ัเt2ธœใ3žƒป[‰ั†ภ‹$!oุu”์ว< ‘ข` @8"ัๆ‚xyฎ0 €ภ }ฃb€xNไ,bบ“ภฑภQ€ ฬ€)ฐุ`ว€ฉญทๆ๛OฬภDเB๓pฑแหุE ฟ'`‚™ะbO™�’@ะ) ภa`พS  ๗ะp L< d€บ0)นผหฑ0%aหLฐ‹AปรCซ5Œ P�`;ภ�์อภฉP?�` แฒMึŒอ1tBŒihPฬG€(+ะ ๛๐ภเฤ$mา๐ฌะฬ�ะญ ฐ� ทŒอ™ฒข0Kp ฯอื`วbšƒภห๐พ€๗ฐฮใ\ฮรุว]ICั›C๐ภADG„DC—`!€5€ 0d�€~„| ้€ Zะ]— P้P � ŸGƒเB๕PF„็๗qํกสเปGP ญเŸT“pxo<J˜ฉ9ฤบ`^ภฮ„ ผ`˜]เ๛Ÿp@vุŠญ๚ะ๚SPp=ภw vห M›-€™ฺ7โืฌ›ญ ํv™„ฉูlภูœBู–ฆขรo$^€ ำ�ฃษแ€ Pนm�€ฑ�ภู@ฺอร@�ถPNœH~*>ฆ�\�า"ึ�เ —{ธ�Pฺุะˆ`C•อฺ� e xฎ-0.q�= ภ@pเ€๙� 9ดฤอ Z€ฺf๐๚#อศึœอ�lbŽ d>บฝ�ะ�ำC#พ€ _ฐ.ฤƒ‚ฐ‡@ฎ�งั็แฤ|ฤCฺ’Nค,m๋ธ~ บ.mอฐž\0ด›�้_\ืP>Žหp็y�อYd0nนุ  s€ษ6`„…~่‰Nธ'”JวP�P์ ‡0wO+{RlP v``เ,GJภฆ๖ภืุ�มิ๖@$ r่ืPžฤ๓ ~‘้U†g˜]_.ูGS:ภ๗ฃx1ํ^0n๐หฬ ๐fฬœ�u๐‰_ภ๊;"€)�58gmว1'ฐ ุ /Pก ฐ`œ-ฎ๛ #A�iฤำX)NH๗� …าžแhฎ QHภ๑่ท จOฎ5=Pภผี"€x�ะภะฝ��๓ญCjC ญ ฐa๖h/-LาVฬฝ�฿˜f„?M�๔€๓#-ˆoวู �0 ํ`)ุฯ๑0บ™#ภภๅ€˜ โQ๐๒1�…้ำญ๕ํฐ 6P?_8CฯูฎV๎@๙9ฑŒช`)06t€TeUX%!=�p ืS){_pwpŠว‹—0|ฐYPŠpN�พœ์“C๚` e0nษ]‹ŽŠoP๑?A0� N๘๑fพ๚ 0ี{ฝFŒฤ€™HัVู๑…ล"l๘่-Kว(ว… xฐูโ@… U ๊หฅm�ท๊u๓tงl๖ษ!!ž}f0`เ ฿พn�Š„ €ค>pฺ่0‹_š’�tu่P`P?Q0L\Dณ~ๅŠ˜8แ(l๙บั" พ~ศฺ๑kดaƒE|5ยpP _ดNทŒvก•ซWฐ BฬzVืqฺุลโม@ฬ™5w&lqN๘ 52DฅLJ�@!IมhCrL`@ะเ2 ƒ-เๅn‡W�ฺษƒw@ˆศ�ษ)D‡จลGž\๙rๆอ?‡]๚t๊ีญ_วž]๛v๎�kภฐภPพ0!8€€&€\ฐ"๗€ร…G้พุภ กมm��ห†ๅ*ˆA“t�๙ํคVHg�4€๐ะธลฟqธ 5„ฐC ๔'SXุภ^v๛™LT9จ…5:่ฐทwไ``|„๎ƒGช๑ง›@<av�H ๒‡ Fุ#�ด$P๐7rP ศb”w�3ฆˆ�@ศmdเŽ~�ภ ้‡‹8+"—t๒QE 6!kงˆ<ย๑�Ž%‚‘”Œ๛ }2ม ร 0ภกยw‚)‚‚ฑฆ*SL1�ฉงฬฆะีn2!กƒK3Eำ+ณ�Rฑ ๋&Ÿ|Daa8(X‚–t๚ &^}6„.ไูจ%�yว  ˆqฦโ0่Fฬ“J)ีฌr฿�‘ƒ*!—Iฉ3YS3@๏๎ล7_}๗ๅท_�8เโ L TุPƒ &เ $H!…ๅR@ˆil  x8โ8“ฃ ‚Š!€เ` BHภb7คX’6ฎC™)๐ bDP๋aลX@……Ž๋pด�:์€ƒUH ›‘ส๘eจ8b‰*6!�…Œp8ƒˆ ่_†ฐกƒ)  โ‡n€ฌ%ะ:…คUธ8๘ฮl„?9P Ž~้–@h�ถm@้ธ-ฝ eง=ธภlˆ%๎€lณ18ธ]–๎แrผdธ๖lโvฬ n‰BฦปศTg=„@ป‡M0ม๐ใ?)/ฏฮ:โ‚W|ธะ*>ธEโู๖X`้งงพz๋ฏว>๛๋>–๎ฅโ2ฌŽธ„5 ฑรจ JNf๕ื�ๆž€ ˆฬŸฮ}‰โ฿๛จฤ '\—๗”ณ0๙Eฤ8tŽ�œƒภๅˆ…ฑ๐Q‡ =|�ภ+(‘ Fๅ�%|ˆŸฃฟ2ƒฺCa UธBถะ…-T฿wึ7C Hg1จy@?~ลe(ฑjแ sธA๚0 ยห d๘ƒg‚Klขq„จรVัŠWฤbตธE้m่9^ไะ๘jุฝ—xŒ๛bุย2’…P1ก—โˆœ2–ฐŽ\ฤc๕ธG>๖…g„๛ถH?า‡Dd"นHF๚ฑ†cค ตFVฒ8 ม๏ๆXHL>@“†$ RbIR–า”งฤ”ๆ`‚Mขrš‚ฌeIXส๒=…คภ!„นา—ฟฤฃ 9C‰“92›pŒMˆP‘3† �5c“� ฿T&ทŒd"ภ*7‰Oี9€จƒ่€ �ะล Cฤ|‰“ ุ˜†3ณ/์ใNQ ่Jะœnž๚ŒŠ;แฌใ0ฤ"�๘ว>˜ล๊8•hํ‰Oผ& ฝLh?�i‚.ำค'ญž1ณCรbฦP†< ุ2›ูศ๘ํ่‡1ฌ5งw�ใ#ˆ€q6เNคcEฤ1‚? ๚y%P ฯฮ�–V&วึะ~ 3O๔ฃDhwๅ l"ฌึ)@$ชrจ0$ํ๘Eโ๔D&"‡ซ^ๅHฯw\bํศ(wาืฟถ8c-kG…๒QใคuญOAi�d%; ‘) c) X$ :5&'?ึ”5M@C‚๙ "`Q`็ฒำด จึjGฬจฤ๖"ุ๓2W‡™Gšๅl6ภ7อb -C๎vY‰X๓ฬh&EkZ˜ฝึo3nrฬ9’…ภึฌnเ(ฒ:56mc%‹rิB‡k‚ บ V*a0LมMŠ0Nd  A˜ใ ž6�c Oยf@5Hค$นYNมพ™ฺ‚แเ_ธ™k'ฦ4ใR˜`R€Zผ,Ow“{Hฑ l o—ๅ^fภ}๘i>ึฌyก๗]�AkpK›p ‰0๐n>Ik6~�Nศn0a@ญ6#‘ pุรRะ๋10•{0ก8ๆ%q”kฺw๓ดRมKธ์ๅฦฮธฦ&J?ย๐2+1@ศDๆ๓ฎห�ษ๖๙—า“จชฤไจœฒ์A |ภ@๊‡2�DZ ” U๔๘‘<๕ˆ\Œ)฿ Aิใz0‚Hด=ฎzPj"•ฟH”q xภ‡ฎGข+5ิ;Tฃส˜fเhHK๚)๒8กUGoรทธFt•ำ€;€ร‰fšด‘ŽผAbส)5Lกpเฺ(จmmlw`น๘… d๗itภGญzวŠ’(ZŒย)๐dP‰�iไถขˆ L@6๐ย๓XCœ(p\aX๎nฐเม‡KŽฅ„i"๊ํh>๊๑‹ฮ*‡!lภ0|0ม|ผฃิ›”Bี?|ท Rz/ถ๑DดP{€‚†ร ˆB*<}Š�AสpO<tk|š]€(>๘aclมํ(>Tฎศิ�ๆฝ|yฬ}ภ Dภรl–HM4ŽL‹๊RีC฿, N�๏~œฆ๋ย)Aมว8๊๑ŽS แ๋aป ˆ#žmื€XบŸEoส Œ  Sˆ$๘ฑvฑยcร$ฎAฯใ„ ฬ๘พ‡�Q�z]๘ย0เ๚ล้ว๐Šุกˆเš‰บtaฮคO� Dav8ภ"ฮม„Œภ๕ฐ—�WฒMdAr�ฬํsฟ๛�•ฤ‰X‚๔ัˆ T@ฯ‡hฆE›ˆ ๘?DƒAธโK89@‚Tะ0จ: €H8‡™๛€d…0@T@๔`เ @r„J™ [ะƒ)' ใชIh†8ˆQŒ 8จ„rPธ…?ฐU8�q0‹“ลศ }˜3H€<€0ๅ๘ฝเ>v #ธBƒ ุ5B๘XภB8ถ๋ดcภฑะ�d8–F(Efภ=ฑ)€Aจzศk่‡O02€W๐ชK €*ุ‰฿่†_`‡ะi่4๗ธ'_8–dธ†mp‚/H…\‡p ‚ฐแ€)H…ข‡_pTP–ม๐ 54 1$C3T ใฐป.“ˆŸv๘ รhฆCLฤš๘u™‚[ศ…\0 ณ’ˆYฤปจฤBTฆYป[8–O๐QไDOด52gœ‡Z8 5–ัวFb €+)‡ด0?Wุ€ Bi˜;๘€Eจ*H— แ ก—xA€]h… ุ‡8ภQ"๙พ 0<4—บก๏%†ไม ฐ�|Xk0ƒh=udGPศˆ€c(…เ™^ †`‘Gzดวp€cะQ0†่m ƒ `&ˆขˆ„ ! Iธ„ixX{H„�HŽC�0˜ŒƒˆmสกIเ*4ATฃฉX"†ฅ}๓‹qชzำม|๚€.Qุ 80ศ8X…Kย‹cยP;y89�นใะG‰่ว$4h=ศึiนไ`€(ภ†p(‚3L ( ธ}ภจ~ฐP”08l˜Mธ0่0zภ‚~(?‘h{`‚8ษF|PX&ฎ`„� 0{า‚Gศ‡5(M๗`€่ฤO”•Œ!ƒvะศ€ ูมฬ"Žศœฬส”ฎc\<9‘Lfภ‚ุl˜&ศอ�์อrปุ$#Aโ@ฦใ M�Œ‘30� hฮ็ Aœ9K€Ax " วUค(G‡tจ~„๒cWุŒ•˜‡ม<ŽจMƒะ.8mxdภ†V(ม“�ษ ฒ๐�˜‡!h)/ส‰€ฝQ/‹~่ศ ลƒญลL hf(ัtQ5ธ†zจ�ภ‡r@$ุ‡4๐™๒Iไศ Ž‹๛;‚ฬ'�ญˆEภ‹ไธ!pPPย๐าˆ๓86ฐ‡4ม<มFธ€ฐ;clŽ—`.H…XปิAตC•สS‹6`"ช พ€œธ‡@x‚'˜A่ะ}ใUQuQ‰Pฬ O*[ ~ฐ…zซ{*8/ภ†ผ’p0อ;p”{ส‚G๘Rถภ‡ฐร%ฐŠpˆ๚€๋2๐!42HEU"จš{๊?๘า<ภ†œJ%Ž@ฝ'ฐYๅจ๖yีXmUธO“ g๘Ÿ—ŒJfจฺš#‹v8น|OZฌญ Hึ1๕(ิU0ฐe”ล*Šฃ @uุ<*€T‡:ภ�)ฐN(า(‰๕Œฐะ‚๗3†I›กก๊„cจƒ kpั€< ี‰: !h‚ŸAฆM}ู˜ฅ (๛Œ‘sุ?Iว eƒ๕ดา bˆ&}Rž๙ุœ๒“$ญ' "(S5`”žŒขซu0=Mำฃ,มhำ)CŽ�„h0€H˜เฺ<�ำฎ๔SŒ`โkXใ`ˆh ธ`ศ4,‚ hฎ๐ร(สิ%�ิใ‹8 `ธ)”Aข2Y”UYš VuŽltค W™ณี4มm`†)ุƒ^e-ภV3ฐp-ะ‚08V%๒ษ-ป2๐ฉ2;„�mจ‡7IkEŽsฦ H„mŸ ืน๋€ะ}Gฯ•0LŽuๅง~ppษ]เ1W๐!:„28„vศง8YW‰p2l >โx 5(�pp’nตฦ„…wbุ‡ี_>€](I6U@…Q0กผ„†TŽภ<ฅก]YA%YYŒัAศCPุว[ZŸ @m8„ย8�I€ภbธ]ฐ…`6เ†ษ’d€Hแ`ก€Pณๆ‘�„�*ศƒผJA€ำ(BH ึJw4ส5�€ฑฝ[Hา2ะL8!ฅv๔๔/ญ8G5ฒ;ปeŽแR1‰8O0�hKQฐ9@ฐ๖8%dB$0ฐ3eส•ขˆเ ฎ…Tี�%สืก(†จ๕œW ส[อง$�‡&8€ฯ,ั\=โxี4่ฐ~ˆภ€ืฑS~ธถ€ธะM`œูฐ  €า$ม{ห%ูึำ@:ไ(�๖ฑUbh‡ayjdŽBdE1\f พโษ~ภ‚‡๒t‚ Hฯ๕4€]ะM ศถ้พmุ†"‹ะWu=h�ๅR†ฌ8 ƒ ไซ0A]ๆe#VX6ธ‹(�฿}v!ฃœ>X‚,(‡6?f@@$ิฺ;ุƒ/x/˜พ๊[Ÿ�]a…ex.จ|ั–�˜ ˜:ภ‡S˜ƒ1ฐฅํ…i‘&ik!€ฉ"‚H‚}เ 5่�B5ฦ}ุ„.xฟ๒†vhˆฆพ’ะ[(ƒ1ุฅ :h‡L๘‚*his"�ป†ƒ^@hโ|bE �ไภŒmป\Aจj›&>~า‡Rศ‚.ุ?‰ [ณา็กB8€‚‡Ž6ฌ�มโ /ฐMCL‚{ฐ‚i�พไธฎๅใโ h‹ฦhf”]09ƒ7จทไ๐‹ Œมจ {าฌ‹ฒ8‹F`ธ“p๕Kฑ ‹ž่‡o€ฮh ศบ^|PDeุะ“e …fhฅe.i� :รQ่Šฏ‚c๎{ˆ†m่"ภlะl๘ g2พkh‡�()ฎปšˆX\LClุE/่ลnPภ’vXDฒ*g์ๆ‡n‡ซศCฯhgmไฦ~h�ั เๆ‹!D‰ ฃ˜l~p,"Ažเmุ6z„ฎ6B�ƒC#ตL–๒Y (Nxnำ†?H ่Nพ5)i๗˜กืRต๖Hธ'4MX†ู…m` L‹^ e๐ถกŠp�  7dณ 9.คs•`sนะถk›คX€T†W ๐ไศ ฃิแA๒qx๒P‹<'_ัช�ๅ”ŒD9z(…`a€N˜rz๓8Ÿˆศ@จ”ฏ{‡&ะ•(ˆ*H€ฉ‚8ยp ็poฎ.` aะ0ฤ-แ’‰8_$นy๘๐€O *ij{“8™“|เ‚Z�ƒ !ต?PฃFy‰(ทจ“w0†ูพ5�x•�1$Q’ทr’Yั“M๎ X?จ†uถCKว๔‚}๔\‰Š `Mธ†L •ฺZ‚\ภฉY7! –๙€u)“มำyjW฿ko‡|่… ลG‰ม€„ �@h†P๘๕`เˆsi“ก�๘Z-งa—�ิzLผญ3ซ _�ะBฑ ธZ๋˜จ !ŠX›ษ๙ญ ๆ.€v„?3ๅ‰อj ๙ญoBณ๙ฮ:.�โณไ/)๋€ภง‡ xฝvใ4เุz›ฎป!™8ง›W™ธv1‚p~อH›เ‰Šจ/จ ช€›Q# ›sŠy…t�แzš7->ำูB3ไธา๙šฐมqrใ๛u�ญณqบ‰›ฏ‡(ึ‚™ยม>3–฿Œเ{›šo‹อkœnšœ8มฑ ฐท{ต7Yžษ฿อน™jบ.ฯ๑;“�Y๙kขคฒW€ฮ฿™๚=หฎฏ็ใ9šคat๘�วž ษX�่` —า๘฿็ ๅจช"$๕UะPtฃ๛1กXd~ํศฑูคธ- …xณ�ๅศ�๛™‚kอจˆข บ#ดง‚ ฌฃ๑—ฃแO ยฌ#้R ๖7Ž๑ž๘็�‰Aข^ ,HB„H(๐B… ‹ฐูโQฐรŒ7r์่๑#ศ"G’,i๒$ส”*MVมสUˆ'5ะคน๒fม–/cโ์I2 ณzš†ไ่sฃ‚&LmสัA-~i:`tZpBŒZุ๔XดŠชTUฝ’ฤช•+ูดjืฒm๋๖-�ธr็ฤpม!�ผ'/4t๋ะจ�…๘R ฬ๐ฏแ�ฤŠ3n์๘1ไ€#Sฎl๙2ๆฬš7sู AŠ“+ืดููci G)€}๚5์ุฒgำ–�ภฉšSื^ศ�g8ˆ\๗.n�8๒ไสI ธ4ญซฯิชQJ��“B 2dHaรtมดKzc I3˜ื @๚@๋ƒ$€ A„ž i2๘fxช `ษs3UงZ <ฐ]w&˜ถP๕A0ก,•ัใ)0ก *฿r!Š8"‰œ5WเQ x`x8U…&้ผฃฬถXะ#ไQM=�ผรX —K:๕(๓ •๖^|Kt†ค#?�0ย‚L๛iภภ๙„aฺ‰�ะ‘tเ@ dAK‘สธacF ใฯ?�ศƒ8E€แนดg=๐ีค“% :(กสฑxhu๓% jะA T�yฐถ ฅ)„ู@ˆ@ *ฤด ภก tภ้เSu๏ipฉŒชย๊5i„Žnู@ฉhศ๊`ขฎ @~ˆฺaDๅ-u วิ“ฯ1ํ์3U€G?��า?rฤิt,ะษ;�ฬขmˆVทมชะลd4ำ  4ะ!L˜แ.่ซอ ๓ฉA ฌฺจ fD& tศWxbซ,z๐ฤPD‘„=ถx€nuœŒ �O<แฤเ*/อ5S\(ฮ9๋ผo6งeoึ3Ž„PA Dึฃ ) 8E*้๘“ฯpaN0E>™xB D๚Cฯ[oภB�A  ‘หืŒl]+M@-๕;งภเม~Xำใ&,J>M\ฐe๕0ฒม TใฯะX2๏@ ,เB:Oะร6๘(aฃt๑…€ยO) ,]ต๚`q@�8ƒๅ๊ฅ-ฐK?…xเณช2 0ศ9G”`)*pภม#ฺ์ภยป๘เ#็/*4ง>ฝ‘F �Ž?gฟ๊‡6๙่tญS@  ฑmํฉQp�@ฯt›๒ขQ„ภOfถ๐ˆจ1ฐไษไ_Œa “ธ†ฃ2L! เว"elใS€ร4u aผ` �ศ MFะ…/@ุเƒE๐ฐMdแ ๛@D์P@pA_˜เŠ*Pl๔#นะฦ/4 |่้่G# 0}XaU(zw<|€ฺศ‡B€)�"น!x`€Y”ซIๅชc”ม4‡‰Kขj€ Zิa๐E8Š`ƒ�ฅๆy5ธ„/ไ‘#pxจ€*\ฑตใ%/‘8‡&๐dˆยใ๐„ (#—€ะ‰�}XaD=06ˆkเp@็Rก0จNEฮj@ &q&T๒Pจrม5�`ƒฅ �ฐ ฺžrS^�&8ุอ฿ผd0ฆ“†† 0ๅhโ`A คเH FkุกำDศh‚€]ด")ล DyF`ๅš๐;ž9|ภศ@+™hฐ+\ƒXP@์์แC์ร ‡hะ 5ภ๘hะ‚T2 ๐ภฺ`๐เx�ˆลท๚ม‰s‘iฐa๒จ’จ$`ใ{x‡`',2QXbœฬ!…‰า์’™d ’pŒŠ  ๛รdฐ8œ� 7ุว‚|Z"/ ภ ๎‘คmC ้ฒแƒ�5ฏ‰•1—ืŽk˜A2๊(`aƒ ๎@iEd;บัทณณ 'hCK"ฯฦอ%0 มทๆq„”เเHG5๘!‰ด๎ใฐYHs๊ัŽM๐€*จBภ!ด๑d`ƒ_ัC&เNI �ภ‡?ข๋y af!ภ ย*ุเQ•๔6�ฮp` h€*,  Dะ’whG;–กˆ&ไ@„ตภวžฺ๑ พ((๔1๕ ƒ่<œ@๘จKด…z0ZLตชโ€fเ ~ุข ซษฬJCฏIดร�๒่ว)์„p(‚WธวV5@€ภฤV `‹็ทš€ ๖8ย€ž#สh(m*บUบ ฬ4T–Eุ!Œฦ"พ€็+€D?ย T“6‚ข�2˜‘ำeœถว๓`BRŽ:เ` ๖d ืั4วฌธ|๋NฃA‚5Œ‹มไ.—:่Gh ก *ภˆ“ฐซ]๎าd0เฌrทมŒ)d!Œhฦ.– x ย!ภ|ศ+ฬภ† สpา�~€๘@ƒ5H]? ุฦB๐แa อศ&P‚plC‘ปฎษ`p~$รN zฌ&‹ ก"€�Ÿ- DBฐLz๋Žk&œ€ศอ�‡x์c ;'ูํVญฌcฉ `”ขุ๒เใ๗<๛g`@ฬก พภญห 3ฤ#›1`ฃ0`/Q์กCโ `Šิ๓๐;‘ ั >$๔ห�P ใ6cๅ;z’c@)Lqฌjฒ‚Lผ€C"ะA3๘ัุ๑ฃ1่จ้Ql์b’ XA6`~€7ภ6†เž็ุ“ ุ่œ-rPp ๔ธัฐw0.สE0ใˆv‰ภ1\€*ภ[›{�yžฌ€;ภ?†%š+bฎ—+Xฐ:‚ป �ภ„A�Usƒƒy$PกqฬW˜่ผ1‘er Pเฏ�I†.TW ธ ๔๑< ฌn๖ตฟ�ญ�‡ื—่!vxอ$ฎ�ๅ_F5žี 3ty86g|XBสกˆ m„๘Bๆp' [:4ƒสXฑŒ7pกๅ"ADtม๚hDD่€Sฬa vpยฯม‡a=ั8ภภL? นเ_ศa*/Pภ-ด‹'\p‚r๙tร/xLƒ]�ต>˜pUi ภ,เC4ƒ+C†€ื]C;Z%} >,`;€น„ภ�ภ5`ƒjภ๒8q•�หะXC0ะaาinย6l๐ „;%Cฝมิ�)BDย5H_/‘!Žฉ^uPภผ๘u?hคŒ,ศCฺUD@3QiดD๎แญภ uA'๔ƒP““ —ฑ๓Eข$6FรูLKŒย6,รื IP@ h‚5฿PEd/Xƒ?ผCn�M0จ€ฐภ(€ร็”ป‰6�A Pภ B:คC.`‘ซจ �๔ t`€™Œย;D—1มฐ€!์ ำด@†h‚<ฌ‰!�รจ@�hข?hC&�D‘ดราd๛ภ ๐ƒ9ฐภฐฅฦ๒ศCœ๘>ผ�ร็l hย5 #Mะ‹�ิิA t >ŽB:๐ฃฌ@'hใร๗T€-ฑ`M•*๐ย�Ds๑ƒ. mภ2 „)ด+ถ@œ@7hj๘๙ŒรPลIฆไ@xะัDCฮศ|‡`ุ#D๛จFคƒ€OMฅPโคh"Oถ@๛฿t8ขพMขU^ๅ\|–ฯ\โ(<ฉไG'fว†@@ <šฅ4@RPŠ ,@tH†L ˜ภgLHฆXŠ๛๔ภ=ฤXDJฌิๅMyHจ@ฏfhฬ‡„†vค€ 4ภ๔e{�}LŠ6i€lX!pภ‚Dศ„P�ˆy˜ภ% Dh  ”hฆญฆ์สฃ|†Wr‡wTู @%่mภ„่Aศ˜ ุCp@,Šy8K[f”G๘4€ั…@4�%1็_ฮๅH@ƒะๆbฒOCะ‡kB�l'N*ศศๆPZaAษ$ม+,>˜C จ^oถ#&D]ธŠA๐'„‚ฌภ#่.n„1ภ h-�eyDUt@„jฤŒึจoะBLๅo„วƒ:_.่_&จ‚NL‘2 |&�ฉ’6™�'Y,€!ผC;คร(จเhDGU‚_่ >ฐ}‰–|Ypษ)เ€?]้†~PQ )[\›1…ฉ˜ๆ่œ.ฉ�)žJ�่'YdฉHŸj„M<ศZ$Bะg!�ธ้›"iŽ‚z"jคๆ)ฅVชฅ^*ฆfชฆn*งvDv๊HDeoˆjg๘)จž*ช„ฉฆชงฎ๊ฤน*fภ*ซฮ*ฆส*g๘ŒTฒง~jซjฏ’jOฤ)uจ'ˆ ๋๑ัฉฑ–$ฆ็กจjฒา*ดย'ฏพFUbณฺ*ฒฉณ�LŠ`๋GXkฑซฐ.ซJซTŠ)ฐFkฆโ๊ตฆkš�ฬ|ฦ@จ�@ณT ซ<ฯnfIq๎Šฝฺ,Eคf DŠง<Šฟ œ&{ jฯpkถ.$6๋รRe#N๊ถฺŒŠR์ซฦrSฎฉVR์ฤฒซปชk’:�2ฌt FคCŒœ ๔(รึ ฤXรุภB5@?(ร็ า0อภฦ8ˆD'$Pื]€ซ7™๋ลz“สv,ศz–ฎšฦดฒkฦv์อpญำJํณ†ณZm–ฎ'ิšฌ’zํt ฦ@่็ุ? ษแอ ย@Œม.๑ะ,œภ8š0ƒุ@0รภภๅเ#y•&ภยุภผOฉญ|z์ศ:bี6�,ฎ:–ฎnญ็†-ๆ’I้ŽำวN-วz™m.อ\-ฺ�)ๆXMO[ํjMฬ‘4ผภ๘้lA5ฤไธภ1pื" Cˆ†็…€=ญS;ฝ“Qmม5p ๘aญสบฎ้vn7-ฌœZ-่Jl˜ฎ๎ Š๎้๒hธฎ๏๋–ฌu]ํรอ้่พ๏ูฦฎ{ชํ`เ|แƒP‹๘! ถผษ�น@3&่C"hม2|ฮ �B:hBด@\ฒ–kม?„M8ภ.๐–๕* 4Y—_ถสฏฏพkฤ&ะฏฎg๙�o่‚ฎคบฏ้ สฮoุฦ/ฦฎn�ๆ๊็š/�Jซื^@ฌL�ม ๅ0pผฑ1AตภA.|‚4ะ-ธc,€hB?ร็คูšต™=HBLจc๒,ย<™Eขฎ _,๚Zlว1สฑ๗๎p�šชฤฺ* ๏0 ฟฎ๑mlท๎oSป๊ื.+q�zm_4ฤŸสฑะ_e`�ฎ้๙@ๅ:ร4Dภ,˜ร'„<@B€ภ1๐ วy\™ฑL@XDC ไ›#฿ฑรFญ—ๅpลRฬ/ษ่‚ฏ/30r C,ฑŠ,๙š0‡o!ณิ2๒{ช-0ื„ด๕ƒ4 Pผม@ ก๖T$๐C!ภ$่!๘์ม<�!�]‚๕aŸ๖ก�MŒ‚5ุƒ๑์ฺ2�’/ฌ*,๚พ0ึฺq/'kยzฌ�ๆqol๊rํ2ื1iAณื๒ๆฒl*ิริ˜Ba Oฬฦdt�= DA>pxภผษไd–xbŒคขfกMภO5S ฿ฒ ๛๓๘:ดๆNญB#t0ทo s์นณูณ '๔็�ไM#/53C4V’์รM๓เ,€s~eLศkฃ€ๅม6&ะ+†ั…ไG`‡vฐŠˆ2เW0uสN๓"GญSณฯ๕‘�ดN๋p]งฌMkฌ�๖ฐ3๗่ำบpT2๔_;๕ุ2TŸ์ฑช๐ฃฮ„ Š„เจATEเ€ƒ.ิ�,ณ>ถชiนŠ๚u‚iคf๎ุF๖[�uงถฒ๚ฉ/ซ๖|†ชญRoซณฮ๖ kk๋Š*o/6ช“ทBFH[C:ˆยˆlภoท’vฌ๗Š์5] r|B7p_wg`t2๊s[79Ÿ[x๗Z ๊oว…xc7zงjyง7{ท๗ฉžw{รท{ฯ7}Sขtื๗๘โท~๏wfL๋ ภ๐[็ดs๓ดซZ‡›„M�5\๓7ƒC๕—H‚ ๕‚›๖>Ÿ๖}ฟ6้ย0€78‡ใ ๋a�G ฦ\n5tฮ%Aเ+ไ�พฆ€รLณฌXf มŠ€ม�‹ใIG‚๔ๅ:A@h  }๘e‹CŒฤtกฒ4�ษˆ; ๗Nต๋ยu o8“—p Gธ๘ ธ@Ky‡_y‰x<มwxE/ฃงe๕๘๘C�ฮ์�hภอๆ์ฮ.ฮฯmาุbำ อั&m,ํ4mj4ภฐ6œB5€ร๐Š�ะH=Uฬ˜›Q:๚เˆฯKบภ=4•“ถB'6tฯnRW:ช“c9จ/ว€‚?ะBˆ@h0*�สํะํเ"ภ €�๒ญ฿ฎเฎแrมะ. 0n}ะ@ฎไRฎๅ�ฅ�่ƒไ6ภมามท่yqŒภภ:�๒ROแดg&@ต_{.e;น๕gธ]๕งใ2Rธ.?�ซ•‡บผ#‡ฟะภ#ไ8dB0ช๎๒.��I ๔� D6๏๐๏๑&ฏ ,o4ฤ๓F/;นH4Cd=ใBmq/ร ผ_•ž`‰� ผษฃ�_V:ัตlฅห๑บczป๋hทฐs็�ผ=mฐ� ม>”C˜@.ฤป=1 +0;0K0[ฐ `๐kลk€ƒ๐5ˆ0 S3ภXวผ=Xฑqล๓ด3 @ิ�X�s!ภฝ�ำ}�X๗Ryงฏjผ๋�Aทุ*ถ€๏v‘๕… ฝใร๔4ร;ฑภ3Dำ�ฤำ›วLq_qo={1›€ณ™›™q ๑จ1 ฐqญ$ม=0Aฺƒผิ๑ภ`่��?q฿A €�_€Nกp�6€G๘3�<คฒpศ๔ฅ;ี:jง?>๖ว\1ิC Y˜ฤ$ฟ@% สŸ้ภ&w๒'‡๒(—๒)หQว%ิ*w‹+รฒ,ำrjผ�=ห@ ก'%ยฑR/,ภEl€m‚H8Jš‚.ใaฤ‰Ch9’dI“%คTฉ๒dK—/5x™’ไJ›2Qฺ<‰fO˜7i๚น2'ฯ‘A[Uบ”iSงOกF•:•jUซWฑfีบ•�kWฏ_มrEp๋”)Ht`เJ?ipพ”y3a‚ k+๙‰_ก“๔โยฮž/Oผ4รฤ#ๅ<>Kฒ”Sิ!ƒฃฌู“rกCหH๎IIด”x๔m๊ขฉ_˜u๋!3่งSgaํถlฺy…๚\|(๑˜3ฦTj๒ๆNใฯ•7'ฮ๒(u”.รfืพ{w๏฿ม‡?^{ข^ธ%U=๙Lu่ะภ6ใๆจMp‡�Q๒แB๖@ฦ่ษ„…(ˆA“t�yง›?(ใเ‚ฐ†\b ‹|ˆเ๐ฟ|šุภƒ;ฌฉG™7R €;ช๑?สLผ#ฅn��0Aƒ๙`”1ฟฮŒCชบๅ‚๒บœ’Jn("kา้ศ้žc’9ๆฌ[RI#‡$Oห-น์าห/ม “K8๊hD!„PTJ Th€DเภXณอ7)จ@ึฤS๓8(H^bเNDา“ฯ 6T‚ RH!ฅ8uSญ3 JP!เ3 งปRJก”\rJ์vz5บ*ฃƒ.H h-ช(W“Sุa‰-ึุc‘“JSญL  š•)%ตฐ -ˆัลƒฐฬRฉgฅz๖V้pโ๕V์˜:ืสโt.ชeงชต)ๅผๅฉงขMw\|“ํื฿Xเdแ�ๅ๊?kาeˆดบอ—ุใ V6b|'ุโ‹1ฮXใ9&VญUัรu_‚ƒB๖Wฉ*๎Xๅ•Ynูๅ—aŽYๆ™iฎูๆ›qฮY็y๎ู็ŸZ่ก‰.ฺ่ฃ‘NZ้ฅ™nฺ้งกŽZ๊ฉฉฎฺ๊ซทาUVญ‹k๋ฏม[์ฑc-ฒlฎ&[ํตูnปใญ๕E[๎“�ฎ�๎ป๑6ๅณ็ึ:๏ฟ\๐ฐะ5ทู&๛F<ุมo�๑วEฎทJf้M\8ศ1ฯ\sท+wส๒ ฿\๔ัIo๛๓นKO]๕ีญ๎›๏VY]๖ู‹†๘๔™ไฅ]๗�y�rC]Sx „/>-~Mโ•‡เ๘ไ•�ว“Z”Gfฉแ�ฑฯ^๛๎6!—ภ—็y�9ๅ{๑ัฏว|๐วฟ�๓�—G�๏รw฿”ฮฬ.๔ํ๙๏฿�๔.„' €Oุ 8ภ&PLเจ„iวv�ณเ1x๕จ็YเBฯ๔T"vผ‰/ะมv€tแ aCฮ†5ดแ q˜C๎‡=๔แธ;’Œ \‰$ <`N#�ั8 ) tชYŠ๊Pชษ•„0A #๗“+กkJA4ใ฿6’ pเ pRpp‹zhCี วŠ”+žจ*šC .pช;€ใ>nOชeเ1-๑�ž`‚ B็uณฺ1™ษbuQจ-๚๑ €๑&@hF ฑ>b;`�q�lB7ญม›ูิๆ6จกๅn�ิ’€ยด(ƒ:ญ�]N“อt&ฑ(ะƒGXฃ‚ ‚Lภ- ˜ฒY˜ก%$าˆyAะ bœ€#‚`แDฒaƒœ3ฤXะI0`'<"เศD”นฬž<“ ํšA0ภ ่6ะMอ˜2*™ j€G‘ฉ ฬ@A๖ฑ‘€ ๖8B šฑQtด6ภN€†}”ฃ&เP”*ฉธด”ง= ‹PSŽา @ˆจ7�ฅเ‚ft๓kฐษIฑฑั t๔ฃ๛PรHK UฉฒิฅI‰&šQŽFฐภ�UซผๆSตฎต+!๐ภ ะ‹v�" !0A�lbJ*ุ@&ฆg"obฮŒ0คกˆะƒ/|‡I9ށฏ#-'!‡แท๘ล1žqo|s€ฤ9| ^Žœงะม>า@r•ซญฒถฺ๊“จdษฝฅ•)8/‘Mtฏœ็PซXโส+9#=ฅศ‚๐Eซw�sฅโO๘ฯA<ญ0ใQKทzำšNนงำผaภ+lม˜ซiQฏdะ,F—๏ดx€วDNvน๛ฬ�ท Oฐยื^=`MM๚�o3ตkํrห{S*0Z ‚ึ �ใwv^ยoฝzfOIโ้ฐ 5Cž๓‘บบ�œBœ*Pชm:Ÿ๚ฺ™WWกŸน�›"€WhH๕ตฺแสฅ/๊q‡l˜โn{แฯŽ7ธ‡†Ÿ�์_๙อw�๓ก}้OŸ๚_ใ!`1พVŸ๛‚S)๕`ๆwŸ�l3ภ,J!๖๒ฏ฿nงy{™ุ�/Q�ทZ๙>ยฐf๙๏<๔'ชW.ภ2’แ@›๘๏ฟร��ฏ+.ภพ๒`ด๏๏pณF5l+ิรฎ!�(ฐ)ศ/P+0เ๋ยฮMะ+@๐๓�ผ‚คๆแN๐ปย9dN8,๏+เ๘กฎ$yp8~ลWคe\€0ฟb>€Dvฐ™pโฬe๗ภD=ศ€จ šนฮgฮฤ˜๐็ 0 }\๐ ๗oอ0 aF ีฐ [† �09] ŸB=ฅS–(์„O๖Pี฿Pกักๆ@�ฎb ก๒c�1เ‘eL*๘๎ด^ ะจSpเ–a†ภ #ฑฉว™ข.„ภaฆข{FhภŸHัe•ขศดเธ ผ€ภเฃ‚-๎ €?ˆ€2p๑เสPg&(�* |แl ภ€มกโ�b!เตลŸ๑!๖–โNˆเL(` ��N`” …. เ`[ะ๑N•bะ0€ะฃP’)\ภ‹๎ช„ ภี ๋ฯ)�-"-ฒง๑"523r#=r;:๒#E๒+xั)~zЇyขG�h$-2$—ข{่'~�'~ฦงโฃ]า ฒˆ ศ hj'ฒ*6(…Hศ„l""Ž๒)๏P)u(Fจ%ก๒*ั ฆRƒ8.8@'ฑ'R=Nc�:ักl=ฎ/๛ถo,อฐฉชเแVฅ�9เaภ %พ/ฅ‘.ำp `์! 0% ฟ<PO๒จณnnฒ1%ฮ/�S y‘๎ 482๙ศ‰2IF ‰Z*€~‘ ถฯ�Ž�>๓ yQธQ�h+7vใ–bk—PฯY`6A์๎/�n7[ฎ)Š๓8งวœะIุ้!� โษ6เ4ค๐๐1—ณ-ฯvr`zPJฅจ คœJ/ฒj%๖bรO.เ#p<ษ‹s1ิsy@=ฺำชR‚คLj%ภ€มธ%7p.๕“,Q7t%K!ซ#$โฐ๔ J!=‰์x3B_P�€ 4รgi5lฉ7„S์ิฅฬ€Xิ๗\ะDypoชqฐฑกถ๋DRdEZD5cdFจล2ˆ ฒZpPu๔Dัฐ&` ฺัพ4…S&ณ2E%%4DฉeIิbฦQฌ*6๑ฏ๑ ž~ภ^�aGJ"žš'ร;g‹ฌง\ุ›ุ3>ฑ'$%OไG>จKž‚ฮๆำS~ธQ�;|�ษ�ๅ~ๅAฒ9 ฝๆm�ฤ�รใ฿{žมx>LŠ~่‘>้•~้™>��่Iฒ^„พ้3๋็ม„๋ฆž๊/็ษ๋้]๋ี‹๋ฝ=_ฒ์ม\์ลร๋ก�์ซํƒ^๊ฟžํห๊็๎ื^๎�ืŠ๎ฝDํ๏ท�้๛ํ>๐��์�๊ฟ๐OL๏ป„๏฿็�<�๑๗๛๐๋ž์ใž๒i<๒9๓๏^๓Ÿ้๓ทD๔Aฟ๔M�๔Q฿๔ @์รKVฟ๕_�*ภชะ)Jภฤ‡>ัcbภ�pLSŸ† -ta๖นไ๘๛!๙;*\ภ8ถ)@pฆก‚้bืpญ๘Yง‹พ*@ๅ6ฅ`‹์Pฐ เด�ฯ฿Š,‰ ศคงพ6 ‰ไƒ–�ŠR �B–:ะ „$คHq0!D˜0‘ ŽknDˆPักC…>†ƒ€Kำx`8�ภรVฎNยผ‰“Bƒ >ไฐAAŸ)2ชPqฐB… "(U0๑่ม‘M†y”ร…‰!L\ภ 6ฌุฑdหš=‹6ญฺตl�บ} 7ฎ�นtฺ๋ฝ‹7ฏ�ฝ|ษz€๓ฤฤKณ �,๓WO™bX๛‡O˜ƒ #Vฬ8F7T,B”ธe bุ #Œกš�มZ&…ตฅซืํMŠฦ#OŽ่ว5lู?ชๆฏŸ2%& k‡ผพ_:”ศ•ฮฝ?BPiš›<จชiฝl…)ฉข็C”ยƒ‡;ีฯ)jศX ๋Nะ+ใแฤ#k��+’าsัMW] ึ˜ข<ขM๔�a„NHa…^ˆa†nศaZ€โ-eˆ๐ะ`a `ษ9jdัI?a„เAOธ�u)ฎุb„ C3Œค J4;˜ V#ะ‚  ฐห&< xŽHคขฤ8c@)%•Vr3p๑,เAF4CA์@#t๑x`รึaืาMF~gำXJ1Lว)€‡>›dก‰‹ฐqŽ ะ0A๗คมจ=„ง่ƒEq†e�d/I.yŒ“ฯฐย=‰\ภ#4{K/k.R )คD ZนะŒ9Rๅaยƒ`กุาหm0S„ ฬjI5VcmรL‚M)>�Œ“‘H*9@ธOฒฤรชั(ฒใึv-7�ฌp‹?š„ั Dภฏ ฐขอ;ศ`ำสแ 7ฬ๎X ๐œศŽ7ีะ/ ^C๙16{Œ�ฯ๔pK:จhัBlฐxใG>A เ(Bภ๛HAายO|๑ฦ�Z๔€G3ๅ4ย‚ _M ๗ืY�ํl๕\uฆต`sฮd้Š*ท-.�œ0๗Iุท�า๚ํ_ต€‘C�A t~PwS‡ ม‘ปNvื?‡*, ฯกคอ]รp_‹A่‡W‚ˆศ†”ฆ# ย?CPฯ�8@�U@ั0@$ด“ผสp†4ฌก %t€+ฃ†:S๐จ—4€ธP–พ<0 Xภภ๙ZI‡Xฤฮฦภย2aƒ๙Œ‰JL�Fฌธ}`~เโ›จพ1�*โ$x@J@pŒb`|xAพึท Aไkต( P iุ1Nื$hม&’mูภ `Šคเe๙าวฐ€"ŽเทE0จP Pุ‚  @พ.gIเQ$เฃ9Wm`"7ฌฅ-o‰ห\สท8…T€ฑRฯ‰่$๚„@Atธ"�๐ฌ„˜ฦDๆI๒p {iแฐ#ว-`ร_ธ3า?5ž]อ|f4ร˜ท“ุa_x‚š‰ร1ษzรึวŠeผ 58ุแต@ฬแ 6๑†9l ,ุ€8 ๕….H”QŽ‚”‹Fฐา�A�‹ภ!xP=โ 7ธร>1ธยu จAฏŠm€ ดิฅO ิ  ีBห�ฆXVByิ€ 0P‚Nผใฆ€R•สิ4` ๙จƒuะ‰y azaY€€๓Žุค`ฉF ๊ำL�ชRฅ* ุ๊Vธสี{@†?ค“‰ฮค]8L>L1™จพใ5ฺ๘ƒh(ƒ๒ถ:O-A*๚c งr+ ผเUSะ;X6‹QะถQ„D!\ฐม๖KTˆ*p๊ัXั �ะEs† �เ wธฤ ‹‚ิฒaˆ€(7‘R ‚ไ.ื!)˜@<ย‘4@*b�้@)'" <“Gู@’ˆบFฉุy%#กTE'1Hv๒œฤนฬ ฿๐8˜dั‰โŠ]@(D๙ p@˜1$้/ฉ{ฺ# F-ฎ‡? โ็2Œ฿D‰qBึ3๐ ยเิภw|0‡ƒˆิ i€g8ฦ†n‡๋ ‡th‡xจ‡H‰•bเ ภ7]' Fย พ0c ฬ`9& เ }žŠขHŠ6ƒž�*px พAJ0zPSภkะBƒ“ณŠ`๕\@๘๔$ƒ@"ŒB๘e�@ ‰๒_P ๕€c0ื't '0M ภ z`a2&er&ภฟŒรุš C`๐ šx}ูgี6Z ค๐†shxB ~Oi0 ่๐‡๎ภ๑ภ์PˆŸเ2b ษเ )9‘–ศ’-ฉK˜จ‰\Gqไ3 €๎8KZHชH“6‰“<ฐ2-CIqา@7ฐk;r€ภ]ื๎ฃ@‰Œนณ v/+c> �1›-๗ะ0 ‚”/๙า�ภJSไA[P 1c็ูd‹€ Mp/ L@M๙”ฯำ[p ญlยQ^pR`eกj?@ ๋เ ? เ\ €ฆc๐R ฉ์ ‘ษิ@ G Z๖˜‘9™•y™™Il.)›ณiK‰›๘<žXญิG’s@I1ป้J(ล:ฎ;0Gy €้P � RY9คŠปี๘๐W%V50A9 “๗e…™O7ฑ[๛ ืว๖p�š ‰ ห@|“8Qะ)� แMย‚ฉ�า“œI`L 5h‘B๐ r-… f*๖šกœ š๊‘�PGš"Pv8ฆ:jก+I›-๊ขฤ“ธู‰มyร‰‡ฃ›5ฺ›@!4BใPB@Fน7Qฉe€Z0ก€=8•7ŒฺY4 4์ž†3žb ๆ)p้นžิžLเวน๐ า@ˆ„๕s?ฤqคเP80ฉt0แณดฐDึกœฟpƒi‘0@ ?�กplcqก ซ …เ ่@š Rภ‘๚*Pข`Qจ‡šจฐจ๚ขงŠชยƒๆ๐ื9�6๚G‘ภชhIฃฑฺ›ฅtJ2€ง!07 z6I)ภ$#7ทLHไฆsHfI€�p’40ก1ก ž๐<ฌ6–ณ / -n––k าเ้ ำณ`ŸฐLn๔Fqฤฐ ฦ:Fี ึั’€/piภ / G ฿0~"0a/  0/ภกู‘–สwึบ€ฑ)ฑซฐ ๋ฐ”ฑฉชฒ+ซ+iX Yเ;ฏzซฒl๐R�ซรI*ลR.•ภ2™ กMp ๓ภKๅ@7=่P%Qื‡ ตPPt€ง0�c`N@๕ฒ] a๙ep� ‰R�ŽฮDŽๆ”‘ภ…“ „ภ!0O๕tO๙ดHซดLป#ว2 ึ`.C ๘๐ฏูiŽ Dฯ๐ ๎๐ +n>ฐฐ?P ค๙~ษ ๛> j„ทh+น”kน˜ซน, ปฑ�!@‘žี s /ฎภ ิ๖ศฐึYš„ฆภ ปผ๋ป*0ฐ๕Wณด/c~EUošภฐ๑้U—๕YY’…X�•ƒˆั \5|ๅW8Cx/c„xX!ั๊ p๔0 :\}๕W๔X!ผั‘�VูปLPG็90ม†nP€7ม๚Ÿwp†‡๖}๐ ิ@ ›ภ`` ิ€ล k,Šœ|ม,ป/ ร’8  yP^ `ฃศynๆณำยร9paq”7 ฦฃ–ข A๖X= `๔฿ธD1; |ฤ'_ฑ$`F|7Dร'/๒UVLะzิฆ c87ม�E}lCpก"`Ž๐Ÿ*ภฯะ//๐”ภ ลp“hvzœ|์ว€ร•lษzยgAษpบF+,Jวฒคฤ  S8๛T)มดึษA�ุๆ^[gf๐)เลP"{h‰*3ซ,ญ|ษม,ฬฒซฟึข0)‹!1ฐ๗ส}เ๙ยไ0/ภRะศoมฬ‰8ฬ�์อl๑แ,ฮโœจg6มnม*ฆํ�ฮ)แฮC๑Tส�๋�อ๙ฌฯe1ฮ�\ฮ๚ฬพ8แฮ�ฮ ๛›ฬฝฯ ฯ�<ฮ�ฬะbAะmััgแะไœัa1ั�ั!-าhฑัแ ัh1ั’าo1ะf๑ั๐ั041ํฮ#mำ7%�'}+ญ!>� M+ำ3Mำ2ำI=า:อำqิ-ิO]ิ`!ีR ำN��า0]ี[-ิZ�าeมีBmี^ึSญิgัL-ั\ีaฝึeีS ืr]ำ0ิo ืd=ืyึ}อะjํัlญืUื{�ฮmmุ5Mำd}w-ะ‰Mุƒmุ~Mู๚ ุT-ุ|ูีœ-ึP]ืˆ}ุcแู™ุ•ฺ�ฬิH�ุ[-ฺcุœ�ฺc�ีGุ�ีฑฺ’]ฺงฺฟmษซญ�ด-ุ…=ูผm�Œ�=ฺฏMฺ@��ั �ำ ร—�ฺัํุห�/]ึฮ�ธฝ�ฆ]�ิM�*k�เ�ปmืู้n=ุศm�์ื๎]�๕}ช็ฝ�>ฝืz��n]ฺฺ�ู)`ี๑��ณอ�ฎm฿ �ข๘mเ่ฝ฿�ิ��๎Mื_ แEMแ๓ญุ฿ญเฮ’ ฮ฿T}�ฌํิvญQุํัmฝ�ดา�->ิA-เ๎แ5~€ ž���โ.ษ�4nใ?>t!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,z q5Hมƒ "จpaB‡.@‚ x€๓ฤ†ƒ>€๒GซŒˆ)>Hะ€ฦฃ|เ2Q)ฐโ4๛ส1มกf<อส5baใย+ฦ๊๛ร‚D‡„nข"…ำˆ!8|ฬศก็Dˆ!!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,zœ(ยฟƒb Hะ`ย„ |QโDŠ#JtˆQ#CŽ14hแ„ )L€|(าฯ2๕”นQ‰1ฃK็ิd้ิ/Œ„š�0ธ4ํƒฝˆa*ิา4.ดa6ฤ_MกDกJeVค�ีกู^4`€K)ำก๖uิ/ฮฯฏ—่อซPŠ•‡ !ABŠปgณjฤซp่ำมL!๙ ,zฃ5H๐Ÿมƒ!\จกC… :D1bร‰!ะa‡1fHเJ?]CŠ„Pก‚XZh่ #หึเๆๆE›!<๔xโสU?'B`bรUF‘Š�กˆ งPึT เ๊ำจ[ฒ มมร$X0L 1‰8ะน†ศอถ+b(ั้�;S0ฺlะ„*๋]จุb�€!๙ ,#‹!๙ ,#‹!๙ ,$zœ5(ยฟƒb Hะ`ย„ |QโDŠ#JtˆQ#CŽ14hแ„ )L€|(าฯ2๕”นQ‰1ฃK็ิd้ิ/Œ„š�0ธ4ํƒฝˆa*ิา4.ดa6ฤ_MกDกJeVค�ีกู^4`€K)ำก๖uิ/ฮฯฏ—่อซPŠ•‡ !ABŠปgณjฤซp่ำมL!๙ ,,zญ5H๐Ÿมƒ!\จกC… :D1bร‰+Fฤ˜ัข@Ž� Tจ A‰ จPRdŒnจX„(qหVŠ–B่hฦ(…จh;Lด๔"™|ง๔a้@ขe&LยVส†‹!€ฺฃถsG>r„ ๓(cX–-ฒัขeˆyŽQIกๅฺ-,ษW'„†ๆ i9ก:$hฐฒฅภ ฏŠอ๊๋D!๙ ,#‹!๙ ,6zฎ5H€มƒ�)\ธม‡"dHั!ฤRdh๑bฤŒ66๔8!†ต๘pu‘PไฟŽ'L .,Cบ„ะมฃ#Xน ‘ำๅKŸ6xP5ด่NคF0%ฺR$Lˆค6ญบ๑๊รฌSZE vซNซ!|r๐0‰ $H…€ม็yฐz3G๎ฮ >`(‘ส฿;S)ๆ"5ุ *? R1ๅŒs!๙ ,Hz�5H€มƒ�)\ธม‡"dHั!ฤRdh๑bฤŒ66ผpกฦฎR< ‚ฉษ*ุ€๙P™‘ๅ? @ภ1$† @€ A„ 6!”ฐfPXŽ ?ชๆฏŸ2%HYB๐„งi)5ฬร์—/pXHJR@%ฐ/x8T‹J H„H*ะ-�ธ+n๙ำฆ‰ฌ"-๚ +0ม-š๚รš๔g€Hๆ^ มรƒxAkลƒฏ†l๖%๓ฆณ=_žxi†ฉดVbh๒—ฯ‹ {๙๓G/‹ฝท5P๐ @6(ะ แyไ‰= €Wขi Cj !๙ ,#‹!๙ ,YzคhHp „&ฤ ฐ มƒ 2t๘0ขฤ†!Z�7Qร#Tr�ˆฐฃ kzl`ะ ั"ร p\s#B„Š–.@ึฎg=|ฟp^๔ฤH3B`‚์@’# \4ำ“ƒDก Jีรc$ษŽ ฆveูt"†ฐ|^ ตdรปฬ้๓ฆ-G44่ฒฬ_>Svๆญะเมƒš1:LLั๋F !๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,$Œ�Hฐ มƒ*<จกก.เPbลˆ'ฌ‘โC CŠIฒคษ“(Sช\ษฒฅห—0cสœIณฆอ›8sฺไัแDž=fิศS่GH“*]สดฉำงPฃJJ๕ๅลฃNฏ๖๔ฉ๐ชWฎ?ƒฅ@ Uc’5‹6ญ�ทpใสKทnZŽX›z5:”แืฝDตVค0vO6Œ8ฑใว#KžL'^—EC�ีj๐๋… 5v•โมน๋ล ศ`›ฦcภ,~iฯ @ฆŸ.ฒSnเเ…™-m$`@„Hˆ]รฮ=ตjึ \‘๗ฏ_8*6ณ”N�:vํi��ฦpw๏฿มM:_อฃฒ๛๗๐ใหœYฅX›๏๔|ก 6`>”†WŒเ 6„ด๖Zl5 เI?…pภ�I\ 6๐qม@ �ฯ/hงƒx`‚ัอ0 -�๐ƒKจศข‹6ไ „–g!†๊f ‚ํอ'ไD้– คB( € &Pมˆ B ฤ@*ˆะP\ชฐNB *(TANJ ’ 5ะ˜ฉB \ะ€n*นีE $Cไ€Ÿํigšฺm#?๛(a‚k�„ัg ษIง*tB ธIe—วiZๅ•วp34sˆQ�ะภ|ž™ย˜ežฉ‚œTJ€™J& ฌa( 0@๘ˆฒ(ฅ–พ‰้@Fjxuะ2„s]เฺภฅษJซถšzฉถ�z๛โ@ซถ๚๊ไJjยk,Z๔ฺ‹j h6ภ€˜@‚@ีJ*,šิซภฆZ‰ฅบา‚)q‹ะฏ‹้๑ว รืE:nจภ{X#้hไ=Œx%ฒ]ไใฦกๆำฤ'C ล@ํดƒ>`๋์Z�w็-ะ�qG‰ะt๛ญ@๚aM=��aZ–cฎ9 dฑLาฺ0r-๑ฦฟ+ะg’Xเ!ีd� Xnั๚๋ฑ‡;๙ๆฃBํะำฤYเำผ.r3D;~ก‚ Œภํ˜ƒ Fภ7ฦ9๎S†ะFๆภ4ภ๕Qpต9\ภs�8ฒš๐„qqภ.b‘ƒ |ธA์๐p อภ„BbAภ ๐ฤ4�<ุƒO๘`  Œ  Sˆ$๘ฑ† tอฐ…ฆ0' lฐG"–เ}4โ$๘Š‚!’Fd…/ฦ0P0ร 9่@~๐ mˆR๛จE;จ8,ข๏่6ฺฑ‰จฦ้่G2ฎฑ %ฐเุศ/ฎ฿h้ฐภ!rฐ&�๔#นะฦ/R €AเcผะF>œฦ~Œƒุ@š=ค…T€๗`rะหZ๔cฎเล)ŠHARKษ้c!.@ดlM {เ@$๐กdDฒ!ุ€)Qฉส3สธล5๘1อ\ำ]เ๚d(G9ภ�G;ฐฑ� ๔๒—มDN=๏™ฯ}๖3qํpE nม5ศ†~ฤ€x๔จr์เ}™4F3ฺมBะmฮT€_ มญšZ๙สXฮrf(ŒฉLgZ“DโGธฆ>โPภ0Aa…Œัภ 4ิyHแ3lIศg0   เผb3๘064’ภว*ะQƒi๔สgP ึ4ฤ€ฎ…0เf`"Hชˆša& Z ˜?bม€(`ƒXHมฐกRีhแ๙X&ฟ๐„t0ƒ Dจม5ๆqฤแ()OP ldขtบS๛! < )จe09M�� h@ฎPศ*dภ‡. p%Vฐฑ™@๖ฺืj„{๐\๎:ฑ €*๐ร;`AiO›ZsV@@๘a‹฿a“ Ÿ m ํ‰ 7ˆ Œ=‚œVะŒ}m๏{ใK€ฐขฬhG+(7ๆ๚#ž(Fฐ„k\6ณ›eด0ˆม~เพภ‡l น฿ฌถตฏMGLโณ„H "แม๔�ˆฑ?ๆ1\ย !ภ@ ฐก‡ุaE0AOo‘ThกcKHJ๐pคฃ�ฤq"ะŒUDณ๕จGŒ๑QŽ"`้/lu+Gภ W„€จE+๐šื5แแ�?<ะฃrิF~่ย^เ‡3ะ ึ}€A5]๐ร>าlD๕ภุ^ื&‘ผl#ภ ข‹œm�กvหPDrPg9จ& 4:@ๅ`uึ 3็Tcษอpฦ–A:๐ฮomศ ุศ†ฌl@ ”Q’ะ ื�รฮ AญฝwG�‚m๙ีจผ+tฅn1~ู+mj‹แ3ุ>ขqาสUเอภˆ3๑ะPx6๐ยํไA่nAฉ�€�l ๘"nฐ.�ŽLoฺน&NธยŽ^$ใe!ะ:oBšr>cพ๐กษdTm�HHRŽ:เ` ๖…ฎศ >๐ ฉA3๊เ„‹'&ภ๙Wย�ฦ8š9i^sgR๖ฺ\ฮ๘fƒ เ…~8ใ‡*ไวŸฑัD์c o>๔?ขQ-˜]-@ฮปๅ -IGw ษย!ภ|ศŒ๛๐43ด h3`�ฎบฏ@RVG�ฮžj๚=Ÿ�ฤโม๓3ธึ๕Pเ l|a oHށAlแqƒ๎๑ฒvmๅ—ซ›๗๓฿ณ-Pง็ ๕`ภhšั".€K๑ุ`|Eซp s๘ �๘XFูฯ�‚ 6บ@€ mฬษ€ &wบ�„ ฯพ๖พ*2ฃ�ฐ��rค@๖PBศq ภ†jุมฑื€ฌHnูCสฒศD๓FC3Ge6'tฐyŸ๐g|q6e/`F‡%IวfR:ภ๗ ) a|Uง `Xฬเเ•{cƒ…๖\ภIs T2/';ฆRฺ2'ฐ ฆ๕‹’+เp†`goณj€–ณy�€n"7\ฝ€ลท TgฺR)vAถ!ษ b%V4=€K\ - {ลMpึ…];ฐ]:๘hา*๖0 `]บ๎T†ุp†- Ÿ1ƒ๗•_๎eŒu_Jศ„`ƒ๗�tˆ‰H+ �0 ํ`G l˜ยS ฟT๘€ ,่‚ƒ#’jเา�ภNlรƒR„ˆ—�w‹ธ๘1dSW๘  60t€ง0c`N <8’ธ ื0 /'{_pwฐ‰"!—0|ฐYPŠ - ภ B€`คถPcภ Xx—c๐ˆtjF[Qƒะข 7xxบฐpH‰dP< า๖o๖,ฐุP7ืร]๕fž$%เ�”Jใ0p5�เ ๋ฤ x'gQD๛เwDฏิ �เe(๖bเ  ๔@ฉ(uบ…๛�ุ&เ…€E�ะeยใBฟ”�@C M๘ะศ0H@/ฉ P ใึ u“๎๔CY”C $๙•ใฐHฑภ$ค’๛ภ’;ภ^bI– _{N[ฒd[ถ–ฉ !ณ9ซฅ%mk+S ;๊m๛ถ๘8s›ปฏ&h‹qzyดwหcykถˆ›ธŠ;}ฑCซ7นqŸ"€“ถUฑ!วน Aน’ปน‹[บฆ{บ+น๔ท'กบจ๛บฐปฒหpัฒBซณ;ŸA%P2ถฑปะป๏ฑg‘ปฦ{ผIŸs๐<ศ เS€Œง)$ฯฝษ‹qอปฝr! ๖��›ถฌkv �D$rฬ0"%ฐ:จี:ฉฬ*(fƒ]"‹ฉ#qF@9 โก 'ภm‡ห๋-^ฐŸ 7$Tฎ% qญwOา†�8ฯีุ6๖•น$๑�แม‚ืฉม#ย ์ภฬฝ.ผแ‹_‘Wy!พใ� ๆ{ภ•ฑ7ึฦŒ `๏๐ฬƒ0€๚ึ ้05ศa�ฐh๒”1žช~`”พ;.` 6ถ7RX;&$0 ?ู…“t,‚k‹_œ#K{3p ฃะ�Ÿ1วu|ว1ฦ ’ยQf ”L๛ย†lkา&า�๒H‚.hขb๛šsขจํk›&fย)Xฐนย)ฏ:%ง๒eส"ภ‡ˆ&kโ&สืศบ"ี*า’)“,•œ(ฤJซฐzฬะO™ ซ�ƒปฑฟyb&@็หŒขญkาฏ!b*vโ;A/tp €ภ…P ๆSp� AW<ฺ Xx,๐ ภ๗ฐO0๔ฐƒวŽฉd“ฌษ๘า8p _`%ย๑โ*R_ažNdขฐJ๒ฑพ,)p๕ขะ็G-3)’’'ฐ*1Ÿ2' kr‚.ฃ"3,’P’'าุ๐ #ธœ‚*฿ร*� S‡Eฆ6ำ)!(๗Œ* ’ษจrำg 37•๒ฬษBา&tหัƒzศ/l4Hฃ4LรศBใp33s(`}ำส QQC- 5�i8ำ2Œ3X 0š0#3%ซd4สp `6[ :7ำ๚vีPส0ญ7bํd-)ฯk5wณ5Žฝ � Nผœ pเ6n๐ชŠ“4�๐"v^c &ร  Eภ�ู]ญ-  ิt†ร6O5๏05ๅ1ด0 rƒม Dm• PขT&๐6ผ`๓ฐhA ๛+ i_7ฯ๕Œh�9j-3๚ตƒษร9“–๏3X’<หำ<M:ฆƒ:ˆะฒ}C›G<�มะKo} hึ}๓ส`šSƒS8Œ0;`ƒ�pัฐ�ฐOทc�Jภ–ํ}8>“ณ€—ำฑ7}3k�ฮœ>?‘ษ8€ภศ1โฺF�=7๘0 ~ Cแ� แ’‚œ)AXSA ,ีฒ๋DP$ETGs4“p ฌ~yภ ภJ„V›1]๐P๐f6ว7 ขEc๐kb๕€7”Coฤมจๆ€8tWธ vp‹pL0d๖ไQฮU&น Y  •‚%U�~ๅY�Ea4FeิP‚ฐๆc๐f�”%ม8้ˆ0ƒp 6'–pr€ฉ ฑgต6u>เBษฐ` ้œ๎้<D้6yๅ ฃ†EZฤE^T0 อๆ‡'DิV>P ๅภ&p ๐6  ‡~DtืMB(ฆa`& �}\๎ๅ`ภA`ื@`;ะทม $J๋uODนOŸพM�๔Mเ!ท”K้ฐKCU๔ ึะŸpาQำ„jU๐b‰ิ ฟ`@oฆา ๏`šI“ด N๐ฉ ๐ิp9ฅEฟเ จฅ๒^lน$๎>Oฅšูฆ%�‰HŠฤH<  � m:ท นฐH(1_OMปรDF๐ะ�๑”ๆQ๘RƒไPGŽไฏ;UUuUMIfฎP!ฮ. f)ทต@็‡ซๆฒ๑wะฐ ญลณq€๖xTCๅ{u�m‹็fฑ๗ว>v€‹` xdt^ บถyฅ๐ขฺ ฤ€do๖ `%VdeV'ะ�@)@รถ๘๗‚ะ$เ์บฦะ(‰๗3ƒ๓~vMt?x๚ฉฯ๘>เ ต~U’"XฤUPี?%โ @•ใ`ฤlmละแ Xโว9หภ์ƒgํR�€ํ70oไ�˜เ*ถฤ-o๗Xํอะcg†�‘^ขeˆpณ< ื ิ๕†เ!ณ… 1Eำ=03šัรฒฟrJL ฃ™=&˜…O‘‡ "DT ฃ ุ0A›–G๙ึยฦลƒแยQฑัแBƒdฺ้‘!รˆฐฐนษฐ›-0ˆ‚-\‘N!^ล Bณ{L Bลฦ KŠ-ุๆ1มจ‘ฃG(๐ฐฃ 6`Mตr๕šU"E‹j๕ ฃภฟ};6ะด‰ำ้€Y€ โหCˆฌ•-_ฦœY๓fฮ=Z๔hาฅMŸNPโธtี๘I1‚•ซ!�เ™w$๓Š[�4…iAย„โล‰oเ€เถwศฐตโ1แฦ>9, €_?��อั8ฤ์�๐๐‘๚q aไ์ฺทsืhฆ‡G�6ฬŠุเ-฿‚3ก†k๊ฉง;|สA‰}าXo€c6ฑซฒ์๖Yรขุฐ็,™&บขQคส,› p!เŠ}ค(ภม ุฐC<ฉkสiไ‚ถ"„ฮฌcก‹T�YŒจ|�ฉ็”!6ุ  P<€f๔ƒ(ฅเ;$๎ ไ‰'f$ทหSŽ9็ ƒศ…�*ฬ์‚�๘ฑลƒ&( �Tš&/ฐษฦP�›)๎เG•ฒxไม*ฐq๒ŸœTย† *ภƒ.ป ๊ส*8๐€ŒtT„RPฉ ?ฬ3r˜@†ฤrภG•๊ย >๚›sฝ3OQ1�ชซ.ฝเวHpเ˜} ยKิ &0ข|ฺ๙งa๏R๖ชM;ญkฑY๘ICวDมฐ[oฒ"ฦะ�]L<-_}๗ๅท_�8เฌ Hœ:p˜ยNๆฃอ66ฮส,ดะคc2ฮธ <่ไ˜:‚ย่:๋ศCขŸ:h"šศแฦSe–iข œŠ ~ฮูม„ dkxรณ ิ๏‚ ๚๛ObŠ-vฟ:œ‚�"ˆธP y'คซฌปไะรKBไ“ฤฺ.› Aข1 ’i|๘šฦฐภ1บ%ฑf๘เแDอ”^  ฎfษJŒมBˆ ZHah‘บรฆR„ค๑ห0 ธAŸ 1 € xtณlใŽ?N@˜๓Xฬ0jดƒ=๛ -Tวm˜™bFษ`F‹Wอ˜œ0ดะ"Œ0XะดOพ;ภ๕{ค๕ ˜แm๊้ว Vฑ้ ;ี#’o…H้]}เu`y8"ึๅp}ณd๓ ภ‹~œ!vแG P™ฐV ˜ A”แ�โEๆ‡• (ฯ.๓RƒํยมิŠ|˜W @†~�ห�oaE8B–_ุE)^0)bhฃ… Hp€K˜}— ม€€เะษXq๘ด A€~Yษชs ฤ`‡PA  ‡ˆ'c@ม‡˜ภ ๘.ˆqƒ]ุโ/Œแ k่ƒC… X€Y ์ฐy?ไA?๑๋ฑ‰� R ! ฉ",โ A! `l"2�+ƒŸ2hดrb"ฐH0ษ+�าทพIฒ2ศม rO˜ƒLz�ษฐ#{ศแ Aธม lุ%ห‰้3ฺะ็ะv"‰Bj^3๘ƒ  }—QZ ฬ@�สO‚�๐ณ&  qฃึเ”;ฅม �ˆ083F�ธGL 3hK$@GจD `€0ฉ‹J–y ฤjV: Dภธ\ Š-€X;l†(šำ>ฐf`‚ric?ฐัD…Nศ@Yฮb€]hศ็ขฐmA“๚VHG*/พ3ž๓ฤ`cยP€o–CžษByฎ4ิหƒ&„jTฅ:Uช๖ห‘๓เรฒPส†ˆร ฎ$ภ{๘ยผะ ‘8|bล2�ภ…์:า1ูu&0:เใsƒœPล ^ซฏ ์` เzกIุ�bรŠฏ†uฌฏฤƒ>6ั…Š…ก6gM๋Z=บ!}ุข c„&0:ด#_จB3 ‰"ต]ฌbๅ›#ษึยณaF ุ8”t-Aุึฒ|ซŸ>J‘….่ฃั[L™•!PP๋5($€Jผอk^ A„{X2^zัๅฆณฐ E'ˆ๋\๋j:šCcxƒž yN1"ึ –.xะžํ ฃ�hD็จใฬ�<ˆภ�‡6๚มส`� €Z?๓ผฃ�PฦX‹t,ƒอhtš๐๗š|6๒ั z แข�ฐG4ถัƒ"ฌO%๚๛ัง�เํ๘\˜ไ�ๅ |๐ใร1ม"ฺแแv`ƒBฦ‡‡AŒ๘ข�๘…'ฒนไ&w๒ ภ…3 TฦเcตhG?แEฦxฦ5ภ+๘ก†0K๚ญj =hBKต‰šH‡?<�‡nC้ G z[Rํ�กG&ๆุทj%่ฤ;าQm�!่: z 4 น€R7dbลŒuภีน@=d#›šTšX†ˆGฑe๘ƒ A6vkิCohœ,iM‡€Th่?ฦมง4`ีˆ๕R€™๘Aฺžƒ๋Š`ด �ศ /“{๙h=�๗8า*ๅ�่v� า1�\3C[„5ฺัz”bŽ ่Dป�q‰ =ศ xศ€.ผฃ a’i>ยฆ0vฯสงC=๊R ่Bฑ๓aŠษ\ๆPพภ‡Xl๑๘Bฺปƒ<~aœเอึ Zะ€;dฯ" 0D>ธเig๛ุ้๓Kฃœ ‘ถD!๏Pด1D์‡K๛�ฯฺC;ดว‚ถƒ‚ล‰฿ _`~จฦ?�q1bฆ่BoW—sพsˆl€šธF&Rเท ,!qป >๐pw#slฑืมพ๗"„ $–ฉHวท#ฝš|&‡ญ``ฎ•ฎ4ถว=sย๑90�เ๘F� =|โ฿๘ขก@ “จ@ แ <ำ2๐€`ิ†ำๆ‰I"š‚4`‘'Œ�*�”วŸํGฑล&1|๔ง/‚(๛ฺฯ'๒ร>ํใ>�ณถ�c>p>ˆh€ ? ฟฬ?ไฟH€pพ จ€ˆพyฃฅ40ธlพ DŽท0‰จ> T€P้ย *Y> จฉจ`็ฤมjชHธม๊{นย \@ฬ˜ภ๙;คะก๎›ฎ €.ภ_‚œภ d>๒3Bุ€�K€ˆŠขธAp # €ๅk>!L�lpฏ๒ู‰(ภฺx€T \พ€<4ธ@€ภฐˆ ุXมุย(œย*džยKรDด&T๐ข๐{€;ฌ ฅ!Dž2ฤ@\ย:ดฤhร7|99l?QผŠ? ฤh็ปภ๎3คใณล[ฤลโณ †™ดอ@9Z#Žฌะ€๗ปŠZ„ˆ_๔ล`�ŒกQ…†ฑ Yย hฤŠ๔ซ i$ ซ`dฤŠธ- …_ุำ0ฦหภ€)ธ‡ภŠZ<๋ะฦ๒‘ช๖ˆ h=v\iควัธฅ๐ฏjควqคฦc ส8™~lpˆ…gฉฦIA0'ุฯฦ6ีQ]M 5ีS-ห$U ๕€<ธดz7่•&*ฯคKaฌ€)H…DหฤŒฬ ˜‚|ˆท(3ถ?˜Œ €;xีX5‘[36F˜Œี€\�UE;๐€๐ƒf๋†?0uห‡&ธ๚์‚z`„ 0:pƒU,ศผ�z„gqtWT�|ภท*#๘ุ$~(ชาcะ,๐ฆfู4ทZ€xฉ€…~(XU�*Q…Q`Hณs8‚ฐSPถxm15ญกSGา}€h๋ถ;msื้Km€ˆn€ถNc@ 0ขูRหคฆ,H€‡เ€ศิฉƒ>"PUอU]ีงลPTฅฺชๅJจีYู๐…1ƒIธ=ศ•(ƒ)ˆHเ‡5ุ#ภc๘ƒ)€ƒ/H@})ุ†ฟ$Ž่‚/€‚Nแภƒ}ุ„,p‹›;จ@เ‚9mซธฌ€I้‡|ศm๘…ธ|ะdH9ณ0}ฐ‚จ‚€‰Mณ๓เmศ'rผ…\ภ5’๗y,…%ทบฅq$~H„dsซ &Zจ�z_˜ @ฺ j‹ธŸห†#เ€Šญ€fœŒ=‚ˆ„s๐JQ๐‰ƒ[Rงธxเ$่„}ฐ‚ด„zธญkศ-K89@‚Tะ0 Xม„ล˜„s`‚ๆNไpk๘ƒ‚ณL�ฑD฿ฤฺฮPซuเJ~หูPšMbคร 8QWX Aบ HTBฐ;h‚ณŽ)าฐซ€bก{h„ู;0ข4ณกโฝLˆ8œฬ„`ฟ(าkเ,P€oฒ0„}0ƒB4ะ5ธ|h„:jh�€ น€บHณศ๐R๘่?�˜NแX�ืู�6<…=x7€…ppโ�� ˜K1)เaQีภfdH‚c`Šจ‚iฉ•}ˆ�Z/๋ถ„lxX{HD๊E*�ŠไINNm‡k0ฉํดXฐ˜)`�/Wไ”เX.UฆๅZ>>Y&Ž‘ด#Hีhื8ไN� Gช‡v($eKŽๅhŽ็ œ๐ั‘r„๒x’๔ฉ%ธ•m^ฺ1Dม(@ƒ7†ะ๏ฑ฿[ธeP˜I[mแbํ๐…}Hƒ๏sซ  ธ0xp‚ูUี๕˜[๐Zc:๎4R™Oภ“่Mš)ๅ�˜„v๐y่‡S(H‘i‘็AZ˜ นฬ_mก YโจH*‘=ฆN  € 0{.ื้ฤืํห„J ธ€€ภPcสฤๅถๅฃFjB+j]~& ƒA{’กไ_t$_`…{เื‘’ฏฅ•ป!ฐ™ชษ™pคฬษfŸพnถ�ษ‚|`„fุ…e@…ฯB 8pภ‡|ƒ : ั(Zภ4ฐ†ฯมg7Hภi๘๘HN ภlˆŽ‡šส žถ“+เ‡dุ่ิ,e˜X!h0"๕e›H(”™€๕2Ž็<ษ•›_ฝm�Eจ่ค€ €ศ�ก๎ดฮqิอP้a€0]j-๊จM๊๊ถnZ๊€!ข!b)žƒP`ฅ1"�๒HPฆ"ฆD:&ญj ฅvขCุ"˜ ีaŒ . ”~่ฆขЁฐkzจlุ…ๅM€@เ0~(2ุ†!p9‹ข•ศW[จ ห�~ˆ†g b!Ž๒จฅlฃƒ€*g˜Œ๏จา€Xก8<เ‡a˜#โRหdFW`|พ J๊†K’ํฬ ภˆ�Gb!A$#ฒเFbr�"ๅโ  ธdJ]เก ุศ˜นLบ‘|๖r�0s™ๆY ะrํญ๒�ž๎6ฝ๎9งs่ฌrจสซ นชฌฺ*ฺ€ฒ`�/่‚9ุ:ฬ€ฟ5ธ’+บฒซ‹zH„.0็2,ฟ,ม"ฌl\Jั‡ศ€สๅCฐ2 T(_จศธ๓„kNะ_0๓˜ึlฃvภ3่หlณhWU02$c^ ธ&30S_127lv…\็ะ:ีฤkh7�อ็็m˜€฿ข)*ุˆุ‘dเฺ7pฆ)w๒ (ฎ๖อ-x๗ํ;—้=xB๏~ื X ฟ้ปX�\'x™†ƒทํ‚N่‡�ุ๊O|/jŠงฮ:ว๘ŒืOU%U่#ถcM6 ˆMฐ†\๛V€ฯ^ฐE๛หu ๐ppP5R35 8CH‡tศ9ซัW‹ตYป๓Vห‚Q;0†"ธ0คS:*ั„Ÿ3C๘‡?Pn-6ะ†LะV๘ผthัปU ศ0ˆhท๒Xyฐศ2‡0<ฤSผใเึf€:X< ศฟtจ{ XN8ทz0†œ๕i๔…i๑Uเ…iอ z{ช ่B#@ˆ0…pyw;xฃX๐ƒ˜ี7uc7ฮ‡ภ๗0„sหฮ7่L •Gเฒฦ(๐ปภ็ุŸ}�โ่หbk�˜x8‡๓้ึ๘โ7~$w‹/,ํ้ฃลฬL@tŠน|ษ๑Sภ@.๒อ8๖๓ยซํwzห1๑.พฝชั�ล ;ฐเU*dภ,าผ@Bb‚@ย dภีฤG>�Xธp 6,‚LRภ„เภ€€‚r4w[ธlf‚:๑^�G7ถมฬmึฃึฺฐง6ม0ƒ�iž ๑vฉ;ั qIยžล’B๎ัS)\ ฺั|ธA>…™ &‚'œ}จแ `ƒ=Žเ‚fA๚H„–1ต�"šC H`‚”เเHG5๘Šƒ8`›เม•`ƒ HoW($k ฅม–ฐeส›a�ฬ—ถ8ฆzฒ^ษb๖<แm„-\ูไธผ2’T&ผ@V„)„a๙a3‚ธ "‰Lpภ1เ‹OHƒด`ม M๔รS+@*ภQ�Lม’ŠqŽ#,‚00AคG-2SŠ™ตŠ™<z0brหc9วณนKƒ}ฃž2�ุ.W%2nk$8•Bธ\คKลDŸ0^ะd 9๎ƒ(คแ฿9c˜…9><@M 8F+x`€]”โ๘@4nฉ DมpˆF €าวโ๑…ด!…๕ฑ�Œƒ๕•GงนM9vำx"]หผ—Mธ�-œ.m– =А\กา\�pA\šใ‰`๚ B`‡=|แ ^ศ$p‰y๐a Y(‡":|`ึฐ‡ 9Q.Sฆหฤจศ C–V�/m=žq‡LiE๓d|ิf๔Vบึทฝดฏโ'Eใuีร๙0r�   อม5 ธ=†ภ€(ไƒ ๐ภแะ#,b ‰t๘ใ�๘ƒV9p`xp!๏ZHž๔›zฝฮG‘ทื m!•kฦjTW;N๏ถvฆZ้ืๅJ†ว ,Bุธ€‰`+!˜๊ี•‡ฉ  ะ <าˆX—Sพ4ฐODเzbK}kQjฒ-น(คž๖:W=ึ๑�z�ฏg฿ทŽ”ธ+ฤ`6e๛ทฒ2ทม‚j๋‚oตGfRqJ lธŠ-CGปวDงอดตฟ�šแm}‡"โbU8J*b)1ใFvตXลม)€๙2ฟƒd๋‚,ไeฅ๔oŠšฌ5า!Š!`F‰ขc˜Œ�ืg†&™Ÿฒฒ‹ฑ<ไ-sKVแส‘ฆLepฮ)CZฮ2Ž๙tๆ.ณนอnFฑ›ใ,็9ำyBkฎs•๎Œ็=๓นฯj–ฒŸ๓ค็@บะ†ฆะ˜•๕อุ*xยf…ณpอFซีฌ‡พ4ฆ™่dQฺ๓�t1™wืAูnp-uฆSญj Q€&`dไ0ํbE)๛‚€D๓โฏภ!มภ v0‰lญ kw S–˜ศศ8 „ฅ็*ฝฮฎ T@%ŽXม�ถJ\]giš๊Bp5mป่fjkี๎~๗f<‡'ค%ึƒMล; ห 4ภg›=ัŒL $miMซ†?ข65ฎY kZใšืภ–ฑ‘M; iภrแmœขเ˜C pw‡jิC Oฟมแ นFไO3y ุ˜๒• ํ‚|ทคหmn[“มd= ๏ก]3…?hQd$ฬqไ$G9ห ขˆภ ะนฯ…ntฅ;]๊�ภ…สฝ.vณซ�ํrทป��m@‚>ไlภAlG๔ฑ‰,ฐ2 #Aี}บ9>5๏{ื�xซ�”๐โ๖ัqŽ,�J~ธจฮ~�๗ุ้ขsพ๓Šq ‘pdขN๚ิ๗พ๗ �x็EๆWฟ๛ๅo�๛_’C@"P t %HA 6 ๛šัƒ,€ตฝMภzAŒธ^๋Œ_6|เ�@_๚ิ�๕ๅ็ƒื–Tš?—™๙w^'หgฌŽ?ๅ=/�ฮท๚hุG9Š`n'ˆ< _‰ภ5ัEัUัeัuั…ัi@Qญั`�๒)๓YA 4ร{้K0 dม>ฌA'%QŽ ”เ ฆเ มแ�งญฬๅ=šn5ฺ~ฅŸฉ9žIปอ๔4ฑ�ภ)†� b‡=(‰)™*A฿*ตา+ลา,ีา-…@.ํR/�าภ�E�$฿๒U \ร{ญ ขเ ภ^@ 'i€ึ–ฮ๑เ ย_Vž3!’7๙ !Zิdœก$ชฺ\1ิƒ1�ม๋8` “:ฑqTฌละ“=แ“>๑“?”@”A!”B1”CATฎE =Hม๖1ภเ1Œผ�๛ฤ<๕โ/ฃ(ย“<แเ่`�,’�| ]ๅ"2IคeิๆM"7�ย)J4Ycะ”MแิLภๅ\`tŽP•Q!•R1•SA�ีEP•Ua•Vq•W•Xe pเA โ�uA฿ภๅ0�เž�๑]?๘�B ็bะ—ชFเT#€]c6 G๙Eb7–d ํำ้ลฦฝึaนF(cYลcEึdU–`–f๙Cg}VhViVjญVk๑๙ dA>มkีdˆœ5”�ค@0ภศญ\c…€ศ]D7ธ จDฟ!�84–อQSป�๔ ZJZ5� Eถฅไญฅ๒\ำ6š$]๒™พLFxL—CTฟ� จ€P8ŒTxx‰ื^–ืyฅืzตื{ม $[^|—๔ฤดลDต}›v@x$€ทอฺVŒ�eZกFžะ™ๆuไปึ๙ํnE˜6Z]ฦๆชQ#š0šF†}ภ†uุ‡๕Vc\˜cุฆ™ค ]ๅXq<ขญXซ€Zฃ=ส’ Rt�X�˜l^gIกe ™’1ใb–dคsbx†vž'z๚ี—ฑ ฦJ๕๘ษ igzฮ'}ึง}�'~ๆง~๎'๖ง�'€จ€(จ(‚&จ‚bdZฺl.(„F(ŸุGJจ…^่›ฝg!ฒ฿xbจ‡~่๖”%‡"ˆ–จ‰†hงq“ลW�จ‹พhชฌ›"๚ืXมจ�่ชhhE“|แจ�่ฬŒ่E)‘iงดT|�ฉ’.)ฉXd4–'“Fฉ”nFE|ฤC<\้Cค€Xฉ–fฉ–>[—ข—–J'Rg‹อๅ”ฎ)›JฦŒ@.�ƒœสร?ศƒ?œBœาฉžึžสiึ้ๆ้ŸสŸฦ้œช)ศG„จi›6ชฃ"ฦZมL*ฅ>มHjฅN๊ฅfชฆbjฆn*ง*AJNr>ชฉž*cฌลZจ qx†uภชช 9qขkฤ*ซฎชๆชฎ๎*ฏ๖ชฏ�*ฐซฐ+ฑซฑซ”ิœวb8ŒS์ล_fJD�ด�‚@LD[ท ฐดฺซ๑ ฑๆŠ–ฒ–๋uึ<” bเภ-`&T=ฬ�ณb˜ห•�ษๅ…ฯMอน˜œf†ผั[s8จsษงน&ฌ็ตš@ะB?�‚ภZb$_ย>๐ม!,ƒ๘ภ;$†A^D�]เ5žCŠlิโN]า-]ำ ฉยฦlIแ#XC5›‡ fม.3(<“m_๗M_๕…_๖m๔รg ฦB4@่^้Y‡ส,ึ!4=„ h„ล)@v Ue`ิๅ š ช "ัด �–`ุ@ฎิ฿�ๅ฿�—Y:Zึฎช@�•C้‰ษุ.฿..ั๒�ร ฦ�bCฮแฺaไN.3�m�"ก6�ฒ‘r�-I .้Zxภ  A.ดร/คฌ ฬฯ6C�ˆE๒ ํำ๔฿1”/ใ,ฃ1j฿1\ฤ๎:ญ3&D%^b&nbn๑h้6๏lb4€ธ€.ร๋,F๒๕ฯF$ฐิƒbDJdษ„์๘Zว`|c8Žฃอ1(q9ฏ�bš’ภช—4ฦคริฤXƒ1ภศVzภ<ๅVšภq|eL€Sึร[mBคV”&ไ�^๐ก.`|ฤภh@\4@ฐ)ฦฐESXk^๐ฺ_^Dถn&ตุ้]ฺM‚ีVไa0 s^œxว™ศ‘คp๖่ถŒn`ิฐ(ฃฑ7�jFฑ/17ฑ?1GฑO1Wฑ_1gฑo1wฑ๓ C\iDL˜JE?ฤW@ซภ๏ฅ ฬวฑ$บ@:*>€ร,Ajdัฑใฑ๛)๖๑ำ้ลสฑ"]ฐง8rฆ Œภ#G๒#W๒#ฏฦ"w2ัตš„2๙t€t„(‡2yœฒk”๒)ฃ๒้ฎฒ(ปWxฒ-ฟ� ์ฑœโC9ฤ@ ๛้?DV.3/๛ฒ.หฉ03/'฿ฒ3€ HชคbŒภ ะ<5[A ด‡คn3ม d๓6w๓7฿@8?5��=3<�šš@€ฌฺ‡šx@ซชI;A>ฏ ซโsr0ว>็p<๔žฑ๊ ณ*ฺถ*b ๔BชAG4›ํ„š@-ฯŠmฆ•Do๔D ฉZr4H๗•GkpH—t mP� ๔jvจIทดKฟ4LวดL]@ย9๘DฯดNใY่ภ>คมN๕–p”L าF.ฏ๓tœ€/0ดPGu”‘’๚œlAFม‚/03TKตWฦEzดฅ1Fิ€้�฿Wงตฎฐ๛Užc๘Ž- ตZฯ5hlI‰๎๚fbเDNำ5`7็Gว0^ซ[งภ 8nW6cCโไ!uLฝecœ- ‚56fŸ�ฉ‰�ซJู cœ,"g6iOสGฟๆมฮ ษ^iฟv•qvhชvˆY†�Ž\รถn�ฐm!'ง„ฬ�๕n๗ฆภ �ƒ ทsŸ‹q?ทtO7uWทu_7vร๔pภ!ฌ]v7ขฌE๒Ntƒทyc ๚”BnŸ7{๛ �แฤ;ทwc‹๕„L–>„A"หw`ำw„\W%ร'ภฮ๊๗\๔„\ภ็ม.๐ฮb๘N๓w_€ ภก 8๘~C8„`\ฏท…{๕H�vfั<A‡_๘ ขS�?คlƒ—xLY‚ฬIƒq^ศAƒ(ฐภ‹ร8LI๗•ฌ0�ไ€'๙…„‡ 0!(ช’Gน„p์Jน•_9–gน–o9—Ÿ4Kƒ[(@F๔xbฆy—๕—ƒ‡ยšบ B3qlww๓Žห{ธ|†G\!ผ@-O:ภ ์8วŒ7�<ะ“4ฌ€=„‚€฿ปด•pF\fฆU@/Sยฌ_Dzsธิ๋ด•ภp $�;“ฬกœฟฑlTxz]ฤ{A|งฝฺ™ุznฃoB.Eบv9 €กต}ใ7เ5 ฝuใำz_าย�S๖ฝณTฌ�w€—{ไท๔6–&ศˆPะ- บm ๒ฝ�†PMร�…ื(8ƒ�Lk'ใัwผa๊!๋ซ๋'`€%ƒลZL๘้เ~เ�เ#?` #๏๚n<%ฟf^ิB) >\lธ๓??I€•‡„,BŽ์Dšฌำ‡ ‰œ�p“x๘หtปqอ6ศบF00?ฅ2D%@„ะเN5ใๆt่ƒฤp…ศฐแ ~aB,คธ‘cGA†9’dI“'QฆTน’eK—/aฦ”9“fM›7qๆิน“gOŽ€ Yaย–v€l`ฐแ‚‚2คHมฐATˆศศฐ�ค}X:Ld8แก(}ฆUป–m[ทoแฦ•;—n]ปuƒ ‰a!} ล tไฎอฑั๑ย2ฬจุP เje,WN!brๆหž!l๎œณgั”3ซP๖nkืฏaว–=›vm�25ศ›;$ธ``]rร†-ZLค~€ flไ๚7]�?y�NIฏพฝ^๖้ึญcื^^w้ิร{฿n��๗ํ�Lๅ`~›~}๛๗๑็ืฟ6P�%/ฌ%๛hฐ8เ ˆ'l๐‰tA%œ0B )ฌ0C /tP ย๘ QฤI,ัฤQฬ้cฮEฅ† ฌƒฦฦŽ�ขฑFnไ(วkฌR,าศ#‘LRษ%ฤ“tTHD (Hn>&ฑฬRห-น์ง <Š'MZŽ/8ˆ? ะ๘"ฑผŒSฮ9้ฌษ๐ุ‡แR2lf๘เAฉeˆล๊[@“|สHม„…บ:ว2jl…fษ!าIํ์ิำOA ท*ฎ!ไ…ญPฺ€ƒู  ข=’ชD้‡ ” @‡}า๘จ12Žก"_Uูe™m๖ำ์ Eƒ�CI (8ม—Ql˜ f)…�xๅช@[AL@•ข &่ล, ิ๕…]wอW฿}๕หk_๎ธGŠ :�จ๖$ * @‹kธ@OxŽเเพ’p5� ๑ฅ|˜ภข82เ Œ5ๆ˜฿“QNูฎ�๒JO€แม_kย`€]6‘Yขะ' "ํ›เ„r!8ฐ˜ัc\Ž`&(๊iฅUฎฺ๊ซูbูY nฮน`ƒMสK€Kbžักd>N�เe4เ[–�Hnบํžปnฌ๙๎�o˜fn(vม$‡ฏม&)ฏ"™Ff<ธ ]US6รPqE"ฎ๐€ฤฦ\ธฦ$ๆ�๓ฟQO]u‘g–๋ยy@œท’vภ`ะี๔khตฯ0VTูfธg ด˜›ผrŠˆ7�๒ีก��๊ึ›}w�/๓j)ๅp†lึ�>>(งT+–ทM`่lQฺ�(�๕Ih_๚๛๑O™‡�๗n62์!…ตฅค15h†x –ฆอใ๕้ ,๐ ้ฃร2Š4๎# Rขˆ1ด�„หโ&Pธฏ ฤ`‡่ึ๓Fโˆฯ}ม ๏R€"ฺ่\จq]เa๋…XB%.ัNฅ;QŒ‚J™ค+_ หFศB ณเะ.†มC3ภƒŽเ1cธl‘ƒ)’ัŒLtใแhพ๘%ุ@Ÿ“DmdXcmบข>x F`ร<˜+�ีช#†D�ไใIIN๒-พRโใ G9aŽs2u%ู  rc!op9x€‡จลกฆH€Tส’ตดๅ-}$–h€@–(eฃ'๎ŸH ‰Ke.“™*f3กMiBOk4ฃ�4ฑ™Mmnฉš๋ๆ6มNq’h7z๑&ห๒"ซศlคIธิbฃฮ=Ž“ž๕ฤenส™’|b(๒�ล�)ฺ“ คฟxSฮjๅŸ๛์'6ึI‘v”' h€j.–$“!@€่< R‘โฯก mhIgึฯfD”+๎„็N`cด@ ๊ขG8 €Ž”ง=M]๋LิูiMฅ,@�_Zฬ๙xฐRKีHcยTฅFตM“ิh0�# ฦษN}VฑชฌšB *EL๊QฃN4ฉ`ม[฿ ขท๎€„ฌ\ใJ‘นึตธ"ทŽ%DaD Gี าฑ.–ฑห*ซYTกE�i)Eำ๊๔Ck ฺั|ŒŽ9š�g={ะŠvt@=œฐl์a  X›[�ฺ้ฑBฌ9๙้Ÿข2อฅIภ ฆ0…5 aPXฆ†!t๒ธษ]nsŸ�้2@๔ˆร+๒@ DHCP"มํnั›^-Iึฌ uhbูY\Š8‰G! DI_�—9";ม+@!� *ิiฤ!|ี›`+ฉทํี‹B)Vถ๖ddƒภฦZ [B๔ฯ#็]๐‡A"Ÿีคฟm<ซ…ิž0g�ุาฑซrฤร!ถ๑o3b7ฅ�Llต&ฬๆ<@&0เจoม8v๒“iƒNดฮ.$๛d๒Jmถ:˜หฦ@9eแC™ฬeŽ‹•ร†fbyq.ํ๒›แ ู1›™ฮu๖ p๕iYŽ(%ฮ}๖3dy”S;šะ๗‘ฒp;Rb|&zฤzžฒo iI็ไ›ดซ4~.=iMoฺาx๎ดง1}MNšิ)ฑ5JY,วณp ๒oQฬcบิต๑IAอ:ข�ชฺจ*–‹ฏaอๆเ~าถ6ถ‚บห’6ฏvต|™�QSฯมวถ๖nีŒ๊…j@ยะ~vซ;บj'ฎฺื6๗bEญฯƒุpiwธ##�k.ำ็ถทA๋}ฮ�0�ูo้ทN?บํ์ฑ—ส๗68พำj~Vvv�nหฟQœPz[9฿ทธ6+�้†V๖Ÿ�~หปืo‰›ืCM๘ลQ>อ“k|ก�๖ธ[ ๐‘?ธ›ูN๙อฃ™q]Wณ�เ†นวนอๆ™ ธ6ว๙ั•นc• 9โ y[ฺ]Tขณา:G๚ีแจtuŸXๆ—Mชฟฝ�PฃN{ฺ';ึั~ฯ?Ÿ•ฦB๗๚\œ-uFณ��'+O๛�•ธvบร;ห�y:[ข๔ฑ๛าvว๛แGจ๗E›ฝพ‹ฎ >Te์E–uˆทผ$ํ่฿^ž๓๗�็Azั�5—แIŸz{š>6จW�๋รษ๚๔8ืf<์qOOู๋„๊ต?{๎_ฯ�o�ิ฿�ไง\๖ฝฯ�๖มwพส‹�ไpฎฒ๏ŸŸ�e฿ฝ:๎}—Gโzํ� างฒ๗ปŸj๋_?{ไwฟ-นhส#Ÿ์C�|{w��๗๏?๋ๆ๏IฑAB�๘o๏g๗(๐ฺฏ๘„ 'Jnๅฐ)ะo๐"�ฐฏ9u.pe6ฐEkp6JpQ0�UpYฐ]๐a�O.ๆ-c๐้ไืขO}PNtP-xP�~ฐป$ำbp ๋ ๏ฬำ& ฅ๐Dœ๐� ะง0 ฉpฯ์ {P มฐDช'”0 อ๐Hฦp๖ผฯฐ ๛… แข �pG$ yo ้0E ฿B๕๐ํร) ฑ้C! qM ๛ร1%q)ฑ-๑Ytภ8–,-4‘=ฑ%ภŒe$Jภชรq”bbภ�pแŠ0ฑ7‚ฎ taแu‘WยฌVrjd@a|^hpHเmั�ฉฤDH$ช*ed& Z @ย*ถ1*L`d$@ข1@5ิค8าQ!,J5ฒq%@*ผQภQ๋1ฅ‚*ฌ$@ŒŒq@tD@Vฃ,๊*R"clX‘"<`XAsค‘!( ˆฬS€6ภ)<’1ช‚*@ฃ( $Qฃ$2ๆQXไ*Bภ€iSฮเเ x…$ภ–ม๊A�เ_1…มฎ(…’(ฒPB nมผฅ#6`Œ้ฌ!Tเ'ต!๊ก� V๑!)5B,ษา,ฝๅ ม ๚A”ภBซ๊�~`J าม่แ<`" ”q# C0r$*` Ra0๓Rภ<  †rR ฆfชฆNเฆ<เม�แฦก#0ณ03MA&9เดกž 'S๎@มhก D@*’ษvฮA ฒ ๚#< žภ„“8ณƒB@šR@ขa์ง#ๆ„ฃkx@8ๅ RAภ’s9›S<ษำ<1€๒เ9ธเ เ€Hภ‚ภ0 ‚`ˆdบเ  ฐ!P"1G~Fภ1iศ#Fฦฐม�` ฮ$๐D6! 4แ8 € ฎ*ซถ* ๐ฤแ ่�@”@…q$…,’A0 7ฮขhเ๒2A5AB"_ภ!zค2+1/a~๔‚๔0�'NA‚†ไ#างผณเrฦG `๎!V„t=๔ถดK!ว•จ ƒH€E‚Hrn€!`Zแ{toˆด9ฅt|`@6€R€"เJแ`‚ิ�๊kฐ +aๆุดF๊๎4O  \ed`โ@ะh๔�จไะ`สกLเ6ำง1ฺ€Šภ๚TxlงV/เVs^&JS�t}พKร๓Hy€,ขAยbW�“V›๕žต8`nม4! Z ~โM—FUเดแZai๖”1รดA;‚ป๖!h‘! HiไVc ๒,Œถ ฆzเาด r@‡ฬ]ีตYc๗A ธ(Uํzเ‹ฬ‡lเ&“YmWu๕]ิWีดชฐแ$าวTnฎ4K™UhžU"ค•!$ฒYOเfarด`Cแ.fe\= Žก‚ฌa]uV1๙”dAโ T‚์•wx dc ผ๋AผzˆJภP5‡3•–iv]'cขมว‡,๖ๆเ Œก�$ิ>Ÿglฒแ€pAH�ิ&`ภIp—p…4Œ –al€ฆ<ย0R”ugU$L:—qEืZI7เŽj1ภh๙เุTh–Aุtjjชคมv‰[—X€q)ฅ6เ`Z ƒMuQ` wNเ@! ่ฆ(€Mนห$wu—xืw จ ดมp๎็เNATเPahl์!บ๚!ข1A่เม � q/!๗ท%"Š%:ฆHUถLแ ฎ€hึqFทt9 €ธ€Sืgํ`พเ �ผ 0ainฦ๔` �@hXa�€ jO—O๖!ๆเ CU๒แ ๆเๅว8"๔พ  †๘B3tCร`6ภย�@4Œ*`แ nเๆuFภ|กhุ†uถย่}oAPUg/ๆกภฎJ �แLฦŽๅ˜Ž` ๒กb:a†ภcb�ภ �. ‚กิฅ2มฎ์๕"y’+๙’=`ม3คr!  ‚2L๎๘ศR�ภ[( &“•“GJ NำฎาAณ€LSSภ0๎ภสฒ(คล6& bฬ�` ยtEเŽ ขdู[\‹tมhิๅไ$ฦFDภ#7"CCส๙œ๏1& 2Mb$:|ฏ‚S zUc* �2 S@H’P�YBภœย!ตb+*€# !%BัฬV$z"ิน~Cb#ภš)D๒™Š์ศN๚ก๓๙rฅ�qxdภา ร้vvŠศไฆ=ษพ $ช<"๒Ž Gข จš. jฯrชb;โn@ hแVC$H คชฆ/NงGซ•hF;" ˜ก4aิHช…เ,ฤชัz'ฐ`ฃ๎ X ”"HpdH ^`ไเ�”"จ%ึ:๒”โญำzฐ[aฃ์kž xฺ#:€œ€ฑ@ †!>„ฆ ฑมGฑ%›ฐ฿ฏ82 d”Z?†d๔š0D โ€ณAโBแŽ`0(แพA F› A >[$N;ตbต[›ฆA;๕ฒส@mŽไฑ‹แv$DZ จ;โาเ(<พ(!œ€(ˆุžY นŸ;บง›ธ‹{ฌ hฤฅ%Bบrสีr'"WฑผJ‚,สAฤ‘6๊%^เ ึ H  bก๎a๊ฆH!ธเฐ@�ม Aœ€ท3ขช=ขภ�<มผม<ยูปฝ}ช1ชฎ‡%@ร`$˜ักq"Œำ$&�[kg#๖ ฐ@‘ ไb!ผA<~`†แ ๒#:X Zเaร�ภ`ภฮ‡0@ษ™�ษง:สงผสYœง zd4jE`%U 0ุp@ฌๅ๊�*0Jy)`4Tcdภ|ะU =R)*๒"…'"™D Z7ฃ ี)ฒล|Dผฉ6Z+๎9 U#AhRฃ�1(š ฯฑ&ร#ๅ\๐�ข๎�จผ`ฌ $๒ |x�(แ^@โภ$€บ)โ Š มจม�4|รีมผ”�8aถ="ืwฝื=ุ‡ฝุW�องirชี%3�เ๊แ ๆภ•a๑„A>Šษึฝ�‚–ท4O35ณ๒D ฌร�c@0ต™Žข1�ิ9ำn r0ำ=ฒ’฿sกำ•* Rแ��'๚ภแ �[ArผR1ไ’.ํา( ^่x…ภA่9@ ึo}$ฮ<Vแxืีabมˆ€ย@งˆ|ภจมหฝ<6ฌจา@ฏ่€^่‡พ่>้—พฎล]คฌงY\า‡|a ฦ˜ม r?Aภ�ด‚,๒ํใ~๎HิDแ`ฬ@ฎ ‡ƒ ”@y=  ฆ ึ€`างH7ข]+h์  ศ…รs่!C#?ใ-ง๘ฮไ พ€(กa แ…`แF  Tภ’ฦโ๓ๆณ>I`Xภ๑!_๒ C ลไฆv�ึ3X2@H์cม Rฬ…ภุมG!า`ะA๊�โุ๋?a`>b๚ซ�๚y=๛ทบ?�ฯ>šา�ว�๛eGมƒf˜rpธ า4Tแj”y4Oส… $Hธ@เฦพ50tqa P”k�"ฤ†ช\ลlุP€B†&ฤุfงใEs>"�๔‚ม‡{tภ €Yา^t์Xแง Ž@e๓ฑฅZ 8๙ฐธ8Fลฦ"dMR”$’ยส–/7xุr‹อ๑xZภห=)!ข†š!รR๋ผ�ม ,2ฦูกร )ะะนSวฮปั์จQ:ขรeœ‰7~yrๅีดk�พ;ท๎�ผ{๛� <ธ๐แฤ‹?Ž<น๒ๅลxฦc‚หFYู$1 V+ž:6ฌ~=๛๖ =nฅCฅฅEŽ ฬ 5ŠGเาUใ')ๆฬš7qv็I�เใO�ฬ3D ื่‘ƒtๅHโ�ิe|aยSC๎ฃFCฐaฯ.4„&๚$ขล2J˜ฐ‚yš„ั &$P}๖๑JC์ฒ x)a{$a่†AB|sดC!"”D� RJษIhVฒƒŽ7|L™‚์ญVไ‘I.ูไ“ช1‡fšjฎษf›nพ gœrฮ™œฯE็’$เล$w;}7‚u|jวC(`*๏Œ3„„NคN8h1O(ศD“M…๙๗g{ึAƒ4ั„ ๊็ƒ6a…ขbจ!‡L8p น|" ดฐ๐โZhา1'N N8LaO~!0ศ9G,B &ๆ�/>๐–� ิ�„ส PฎvD(ฑฌ"B!� C–ะHมวบ_จ๐ๅj�~๎ธๅžKgภLpมŒpยh๊dฮ |๐าž!๔ฉa$ cจง}6@AG ศะlL� O‘ยป”โ๐ั@L๒ซ ฉ–PUXกฐอ!-ธ๕€ &Jƒ7ยมS‡M%ฬ Ymณ ]Iใรฏ83Mณ˜๓Iฬ”„วlgภส-ฟ\ุQXG4๔—ภXผเ(‘p๒‚/œAอ7”ฅdqล>ิe<่vต๎“ศoH�'{๒ล wเ&3†๎ุ]2KdQŽ"'mภAุ๒ฦญŽว>ฬ๑ฦะฯ)sŒa‡$€‡>ฏwกOS†Gภ�Hวe„? ื 2†A\ƒ‰? €I่ƒ๖๐…'xก๓œ=้…@)ฃฐ†=4ขดคs๛Tn0เ!ฯ๘†;พัˆ4ไํ0/๐฿~P า@รฃแ฿j˜˜๙]…*dก a(C!ชn‰LlขŸEำq`yฐ†?�ั9ศฬผ@>€ŒLจ 0ˆ&�‘S�ฐ`\๔"ล8=Tฃ@เศ Šท# ใŠฆhAV4aลztใ™J >ฆ !eLE:า่จ !�$L ณ=์ัใ˜C!ณ‘(คขh4Efเmpา“Hภ่1D!\€4Y zd‚!(ฃ&าqลAžไ 5ภt†ิ"n8ำnธ`ฆ|bgศภœy(๕แิ ฦ&˜[Pล8ูrอ’H“šึฤf฿ ฯxสsž๔ฬ ๕H@*ุภ$ย$`*X(เ(@"๐ง$0คด PาCSฐศpŸUก๐%ต@�Ÿคจ’TQ jก"ˆI&\4�T@E! ‚–*ฑ!์YภM“โTง h?ภ€œสL๙„P{Y…žT^ไŒย*จ0ภจ"่ †`&Xม/ศWๆH๑g๔แ`xม(มbœ๎คอWร:ึฒึsฏ|ํซ_�J:€�ฦฌฤู้ &Xร(M)—"†.<`Bแhฮ%aAD`ุจ€ลp„๚^ฐ 3ผ n(F!0‡›ษ’อฒ˜ฌk_ �ุสvถ|•ฅ5า!Š!„.9™น[]ณู>๘ #L ว^Haฎฟ้ญนh �่Jwบิญ.sž‚�๖8,�ุn69„Dธกซุ-pบk�๔ชwฝ์mฏ{‹๓ษ� Žก๘‚of฿‹฿�๊wฟ�m๏S�฿ xภ.ฐŒเ+xมม๑‰5�\œ„งจมสdฅ k*0r€ kภ6"�p‰7ฬเซxล,~ํ&@เpกpŒน-œœ*.ฐ†lเ]จ”ธ6;ภ‰3�โ$+yษLF]†=@ปd8 † hXจะจŒƒkŒW$ํภ‘ฯŒf4๋xศDNณš์ๆ†ธ๙ฬMฎณ๏ผ฿ �…?Rึ@ �aฌGร0ฤ๛กŒ-ํht=๐๑‹ ˜yฮ”–3�Š--bS๚ศp�ดงGŒ็P‹zิัอ๐C๘�ไ €ภ ษ๐F๒ภŒ?pแ p` <#๔ Aุ\>=b5gzอรพ ฐ j`“zูฬn๖^๕�ฉ˜ธ๚�AฒQ *ธฅผ$8ศ‡Tธฬ ;ู™.vง/}˜qำivถป฿ ๏ิAีฎถซSไตศ�6ๆฬnvู�E66›ฎndว{แ oธภๆํ)�{สย"–ฑ๘ญ4ƒU?7ย‡M๐ƒ†ำฤNธยŽ๒”ซ|9–อ€u3 ภbV้60ถฒํ฿ekฦ–:ขI๏Xู็Frปฃb๒ค#|ๅLoบำy�ฃ็๊]/{$ศ๐†pง;ซ>0‚คP†Uf=่ฮQt.zœอnvt 9้๊^๚ำ฿๗ธ๋๘‰|‡)เ์หRฎ—�ะ%/อ�€.,ฃ”Nถง?์‚ง›ํeoผศๅ๙ศ3|ห7ฅj†ชP†N‘ฉJzช™+ะœ3ิ‹†฿4โ�n๐ร๚๔E—ผ๋_?y9/'�%7จแ|0$#�๖ฦ๎ ษWc๎W ฝ๑?`�๏�๑#W๛n”ฏ�๔€Fพ๕ฏŸ^wฺให7zนuŸํƒฏ™แฮ@ J‚”Hdภ€$ฐ  ๖๏�R_บุะx๋ƒO�็|๓ิ,ล@� ไเ่๐ ่@ กเ2 l Pศ”๘^Q๑ฅ&ะd‹€Œ‚EW‚kgz8‚ี| ท‚ซ๑‚n1:™มคภ๑เ<่ƒ€รเซภ lpH@1ฃKุ^เp`�ฑ& ˆ`ƒบQn—Qp&0‚ฆรiŠƒ•†#ฐsเ…�gbฬGTh…_H'H ไภƒ๑ภƒิภ— ร@ไะ Q€€LHˆฒ•a๖a€"(‘ตV 6ƒฌ'–ะjใืfFw“8 9ึ& ๖ภ ฤ‚�wˆ˜s๖g๒๐ กaึg‡�pฒ‚x"ึˆฆUCเLšh‹ฝแ‰ (Šวม…ฐƒv่ƒ๑ะMส่๎@ C@U…Hฑีo>dePฑ7 œศ}HrT‰Xศ†@Ž<‡ฦA๖ฃ ล(pทัc?ฆeiถ2€ ฐ‰ ๑b16cฟF†ด˜#jxf�ˆcZต๙P‘ŽยัŽ�๐Ž‘ELP สh‘๎ะ‡้ zเ%ี่‘LdR2%…d p f_@_31 ฃ๐QAepeYฆั 0UAg“B‡b$ "0S QTJำˆ8‰S*`‰�q A9SHVขืR(q79Sลง ม�OAWv …-เ•’C \ๆe/’a6feV”N5FŸ’Žฮe‹8mX9’)aBuฬ#OqbEี•0|ษ“3ฉฐเ%3U@)”yู†%S&0|`ูฏR––‰™๐PpAe�dXะžแƒฯุƒฯh‡ชŽเ๙‘ณ9:จค ้ Ho —dyเะn๐‹ึhํ€๕๐ 1 ื๐8Q๖6hN่ท@Jไ(vq{Tใœ>ฉ{ค๚›)๐% ้ะ€'€‹RZ–‰—@ใPเป “๔ภ%3h@เ€6เ0�๕ึ้(๖`๘ dG–ฉ„›� › Qh‡–h& œŽi’fžภDYึœจ{q5้ZŸชe…ถ �p ื๐—G–†` - ep๔Pฮ ์๑ต@œ๘๐๒๐ *žโIกไนจ4x๕  n€™1ะ ช4PZISZฅ�€ ผTท` (h@ ฿๊@ /D ๊ภƒm ๊ะƒๅD›uj:–pr€ฉ ``’‚PˆD@ aื@ผถ€o“€ ``Kชฦj�F˜โ ษ ฬ`9ภ>๛ฐ ฐ#;‰7l|€ฐVเ(#ะ_�x€ |ภ=๖@O@k0ะ”9q ๖] �0vฐœ\@ด:’Kเ๑ฃ˜ฎฉcฐฉบ 5 =๐ฎข“˜ง{ฺงP=ฑ6kตvkนถkฝVuฎ ซฒJ@…๑:vj)บ!พ๊ฌ๑#>€ @ฒš#ฑ(br>ฐ40=pi@”๊ล+;A `ชจชชฌjX�ชYะ �!เฺสญ�กฑ๋ฑ'กอภ) ัฐ&ฐฐฆiz?`ณnJ5[ณ฿เฆล teงA›0—0 /@+p‰Z๑4]๑ ๑m9�3 ฎ`d8ฑิถ�ฆAb,๓0๗`d 𖐠F‹ด‰๐†ปะ žŠมtEŠ” g9ธ@ ์ v&0€+P( ฦ0];แuGv@ัึgไจถI+3ุฆmoัmตแฆ]oซq;ทOฏ8ฑ)zธ‰ปธ'ะ�@)@P‹†#bp๓ภ[K๓@‘บ)$aฯ‚ถ™หถKดI๑ฝ@ ผดบ$๛ผJ!ฝdง X๐4ซA3รฐ ซ0 ญ้Šเ฿@n๚ Lp_BKฟoข‰ Šp!k`urPปqฏ๓C�ข[{ฉ‚‚„R>ื .aHf‹ŽD.ฃS„‡  ๏€ ุ0ๅq้1h๑9 qmภ (b ’ ืPคไ๘P‹Šภ*ณeWฝ—K‰œฟภ๘ฆo.ภwขpมผมถƒŽกา9œa ๒ย"ร(€๛0‹ทห‚BNฐุฐc�ปฤ k!๏ฑ )มำรฒyถ ฑ&\6รoฬqฬ s�bZ >d2หš)ศ‚์ณiXฟ‡L'D�'ฟข��! กqz ภgoผฃภ าภ2–ฅบฦŒ\ม,ะ วPAึฐ*…(Šย(�ทŠŒว(�u`*ฆ’* \N`*D@#ภ|f„’fNฌ้8 5,ลr"’ผq–jฃ\สงฌฤฉ�ฤ–๛ฤอหปLŽๆƒ>Nณl ถPcBถ&iงa“K�ใoฐุkLม1ฑuDA<vb>' �ฤm�–๚ฉ฿ใจ@=ึƒ=ฺณ!�`� ษำ�0tะ™๐Uะ 9r/ฉ ุเ�K�>|<ษƒ�€-ุN้ซภ*ฌpaUๅ็�ฌqy€) ื แ�…G๐ ์ภC�@ คเV6{ใ6.b@Rฆ-ไiฒฉ ~ฤž™เสจซ$ng)x8e แR–wiดF\ค_F�t‡้ �ะ5vdE~ไ<ฺ ™p~ึ ห๙ฺ๐)ะw  ๒utGคˆJs\ j๚iI&เ„Z ใ`K, ผ`o4FivHonw0€ๆHพๆสw~—]P>xิ%๐ๆธ)็zyP1เH๔)ˆคHj�่œฤ€'G•”Ed !GEะ@—DFfคwG{dw-P้jฎไฮK๓PŸ๗I{ �“nAษพ์tžuP ะ R}NAZโภ � ิฐV"`ณ�๎ํฯ@ เฦCฮ๎ฤ1š�špJ•งS#z8‡^mŽy,ๅPฅ•u–`ฐqฐ=f๗๎๏๓ฮ“Pq๏๚”—ปz–”)ƒป›/"‰Q!UแาO OWPm>t{yS)  ๏ หyNU•Oq๏๐T\&™ตROีB…R๑๑ƒูSษ๑ด.gm 47i6ภH ลR11น5’+Ÿ�ฤ�๐ๅj •7ฟ๓)ฏคO™5  yญdTHy�D โภ๖ฯ๐ Žภ pฯ๖เ� G€IํŽ๗ผ5bฺตŽ#ที—WPS ฃ`Mฐ๎˜Aๅ็}‰E$ุไ]�N๑ฒwn๕ศ{Jร๗ด๗…�wŽ้jEๆ๘อ‡zผ๙ก๏”ฌ˜M๕,ม๎”/๋>๐nฯ๖ๆ>๛ล Ep๗yฏ๛๔ด†๐ํฃ`์�Ze‡?Šg็{‘XsyฮPPb@ ณo�โ \6ณ๛�ฯ้�฿.p‚ืdMส๏&คš&“/๚7€ฯGŠ๓T^BCfภ|Pf )ะแ��$XะเA„ .dุะแCˆ%NคXภ?Œ3BฐุัใD !EŽิ๐ัไษƒ!A’<ฉๅK˜!BจP๑‚GŽ&Hภไูำ็O A…%Zิ(LI9e*ไำฆQA()ี๊K ฎnๅฺี๋Wฐaญ&ีธT,ฬง,ฯฎe�ึํ[ธqๅฮฅห์F‰P[ฆuช–แศพi]ึ%\ุ๐aฤ‰/Žzฃู‡zM ,า!Kม–oๆ�ู๓gะกน:�™`fฬ•Q๛5จ—ฏ๊ีซEฯฆ]�๖m�œI›0U฿ฟบ–�wnไษ•/g�ฉc�ฐQK—๘oใมo็��๛๗ูป‡ง>ช�บฺู๋อƒg฿��{๘WwW-˜ฝ�๘p@ \hพ”ศ�๏ธ…๔nA์4ซฮ@ +ด๐ยๆฤKP=ฯใหพ1qDKLLรึ8 qC๔TฯDc”qฦั ฏ8!ฬ/ฝใ^ค๑G ƒา#๋{ํ?wŒ<‡t๒I(Ÿ,๒ด{3R3+๊i0%๑‹๒K0รqส�ชผ‘หฏ&sM6�d/ !๙ ,#‹!๙ ,#‹!๙ ,zฉ5H€มƒ�)\ธม‡"dHั!ฤRdh๑bฤŒ66๔(dH‘�:^4‰2% &H@d‰‚8OL`xHS$„ �ั*#"EŠ=7BHะ€ฦฃ|เ2A ฒฅล—ะ์+Wฤ‡ช5R่งYนF,l\๋๓ภc๕Œ�aAขรวŒV�:%DEŠบ%ู* มa็Lม9p๐˜ด"ษปญ>nฬ1 !๙ ,z 5Hฐ มƒ*\ศฐกร‡#JL!๙ ,#‹!๙ ,z *� 8มƒTธแฟ„ !*”ˆะแC‹ flฃG‡†|!๙ ,zๆ5H€มƒ�)\ธม‡"dHั!ฤRdh๑bฤŒ66๔(dH‘�:^4‰2ๅ… @€ BDˆ1gชP CK/ฃคz็/ฃ!�็oœ›'EB(„18_�|ฉ0AP=Dc]ใรใงYา^XKขBŒm‚ึ•อวฯอ0๑ Hษ>5ฐฑวไn�ฝ๛� <๘ˆูYย^t ‘ƒ[ธrAIณ‹B€+�คa-๓fBืk`ล’�4Uฝขฆ:เ๔ฃญู้ฌ5, A„อคผk��I๒cFโ$Yv!๙ ,"zผะ@ฐ A�*T(p ม‚.ฤ ๐!D‰+Zิใ?Š-vฤr#G 1pเBˆ$%E”y€ฉแƒต95=‚ค๋ฤˆ&*0ใ”คภ-ีbภษ‡ลEณ&๕l8b5ˆ&}‰ด,c๒#&Žมษ๕IZส‚๐สูดณฬ}Šซ@€H� ˜ิะH‰ �ก7„A”|\g   จ!YaI‹:งิ(Rn@!๙ ,#‹!๙ ,*z�ะ@ฐ AB๘วฐaC  J$˜Pกร‹NวดณO„€Yx๔ำฯ-ํ๐#‡bxีมผำอ,rฆ๑aXŒ  -P|AF3ญ๐@ฏqpiซเ‚>a ™•06ิ€$ช`"cLะ@ˆŒ�q…'๖)œOLEIุc‹†5ม ษ๒ฤN์ภ–l‹6๋l~F+ํดิ๊fิณA•—๕Œใ!Tƒv๕hH BU0E*้๘“R๖M0E>™xB ฺ๙Cฯ๖nภ�z[ghไข/#๖2y‘บ์๚๓ฮ)0x0‚ึL๗‡ ค’OภืE=Œlpย#ี๘ำ-šFฬ;ะ $ธฮฦ๔ฐ >J0็UF|!ย ๐S มzีฆ>XP?ฮดW๔G ,€AภาO!`ซมjชŒย rฮ%XcŠ p๐ˆ6;ฐ€(>๘๘‹ Y้ฃ@oคp]w€�ใภF๚ก@ิก�ช]$Ps>`€Aภ๓v ๛A4uHž๘ฒMjญu่Vk๚้จงn“่๙ภŠ/cŒ1ษ5z7BSD ?k h6ฦ�1_ฤx‰0/LฑMOะลPเ \แฑฯ&Y\ฑhฺQ \LŠ VP6�ไ“‹6ฟฤ >๚ “N?0ˆ>VฌPE p\6>เ…6๒แ„`` ทศE.B`~*@˜ณฐฤF0YแG"Hเธ‹\ ุ E่_„ฃ6ศ\PาVƒK๘Bู8ดถ จย๖ �ุ‰s๘`H†(�<ภh/O‚ฎs x๐ ุ‡&PAิ#Vƒธ๕(s9 !๚ั,Tฆ”`็`ย =๔#\ใ6ŒฐPฐ ;ภ`/maฅบ>๚๑€”ศลย Whา๐ฆ0   ,hƒ\€) มจa ;ด Ž’{ภEฐ‹Vไ@ ฅx๏ัˆ ฤ Ž‰ ›9| Bะœ ุศD ๐)้เ�ภ‚–fbf8D;ะ€}จAžภG#(๐€ด`† P€vะƒaใG,๐ิN*?๐€๔ฦึjเIภฦ)๖๐7ภ"NH!ˆ.p�>™C ตlV oศ$แถg๖† ศ`qnฐ5H.šดD6^@€�#โฺ† :@ eรฝแจGq#2–ญื0้0“ฮQภย9่fjยvtใbฑ™้ ‡ศข๕จง*F*เ:W(O๓8BJ๐pคฃ�„C๗มR๗$+๕hว&xะฮ!เฺx2ฐQ)่!xค$€„~เรx๕ว<†ภ,เแl`ขz'ud8ฐ4ิะ"คศ€;ดฃหPDrเปZเBํุ €_”๚ “pจ„€A๔N _„‚๓�ูย=-๐ฉOฬpภ3๐?lัa„Yiิ$ฺแy๔ใ r%8A€+�  ภ *z�xmŽบŒ[รEภ{2[b4Q' 9)]ct*œฟ€เ…ุr@ขahงย”ชGค๘ภพ 5ภิB>• ๓`BRŽ:เ` ๖d๐ั ~ี…ฌธส๊NฃA‚5ฺJ;ธส•:่Gh ก *ธฮl�ุมz๐‡=i\ทมŒ)d!Œhฦ.– x ย!ภ|ศม•ฬภ† ส Lั~€๘@ƒ5œ์ฮ‹4*ุฦB`\ะ` �อศ&P‚pl…e~็p~$cAภกฉ&‹ ก"H Dโค}™(w1bœ`ฝt�‡ฦ[�๓jeฮ—Vฤ’ฦำข  ˆขp}ห�—๘ƒ@ทๆ,ภถ ห /�ัš` `1ํTYภ ึเ0=‘1ม ภฃ9S…ฒฺ๐)@'p้น@?EB03\เzดฃ๐xe Ep,`c.-ฐš b�๐*01Gฺ่ ใQ� ํP.™`80�`,ะfAQ6๒` �€๏3ภšp “hfS1P)`'ฃ8 ้pŠฐ  &c yใApXR„}‘Nชภ StลบP'ไฑ`ศ ฆะXุp�P/Bแ~3sะNั8แBu `ถ˜oฐ๐[๐F†#Qn 7กŽ์(Ž~p„ๆุญ2ws‡kA๘๙ณั˜lฃ๐;’JXญ-c-าxฑ"*ฐ ๐ซA$` A0า"ฎB=pqภA(‚$Žm*@%,้!ขa)  �0(น‘C*‚G`ยU [˜ขAฅ1&PCแRf-ู$0ฉRb" hก�uP dถZ@ŠQู%๖ ะ ฐ"er‘/Yrฑ7 o!ะ9WCwฉ’๙เ_i“†@Y๙jƒA“†ณ•y‘ํx\hy-ฆ’อšน™x—™! รsgY!EาNืแBง้™™™W€;Z0 ๖ะ^ฅžiฑํ„˜6’C‚‘ฏฐ ๘`-`uา9(9A๕›ง ั› ม+๐๚@† AFpZ@ ฟSqš�๐›!žไษ* 9 ฉƒพ™›ri๛8ร!HทIšœ™Ÿ๚™Ÿ €9ฑ†๐ํฃ`}า้dค2)๘ภ ›๕œ่I™7ง€Ÿt ์FQภ ๕  CŠ– 1Ÿ๏1๛™ข*บขจ#r’ณ›ซƒข0š{wŸ3มœใ)„(*9‘sžMฑwฌRสฃEสขHšคJบคLฺคN๚คPฅ"ฃRฃคฆ:&-WZฅ\ฺฅถฑฅ^๚`Š:$ชฅTฆhšฆL1ฆี‚-�ศGคgš=(eŠบปi@^๖ภm’Ÿ t@ ™˜  šะฦ3Vaf’ •86‹@I&เ‹eปพ๐›นๆ{ฟ|4ฉ๖๛ซ›๛ญuสยคKฑ์ซฌ�๋ซN‹ญจ๛ฝซต7๊ย๘›ฟฏ�ดˆ)"ญ„<$•AUทR็J๋ ำณ`Ÿ๐ฬ วP)ว–l›ำม0Q` p 0jDLน๒ซฐkบ}5ฟ1ผบ?|Ÿฃ:[ๆ�ยw�พ7œฌzจdฤต�ว+ฤ†๊ต)<^W�ะา@ฟgzญp�Šข%,ชDะ๑<ถแซ’1�บฆ2�[ ำถIง” คqšŽZ@ บ ฎ€Zช1ค)ฑž๛ฮDj›6]ี‚ŠิDแ^๖Œฌก ำแึ๘IฉM::�ีS]๔‰พq:Ÿ8ิvฐD-ืปอึข0ิฆp,ฆF!ƒํง,ค‰ํk‹}ืŽอkืฑ—!jุ‡B๚อุk]™�ุž�ู[ ฺข=ฺคุZ]ฺrชืจฝฺฌ}`ช�ฺ๖yฺฐ=�ดํGฏm`œ๋ำx]ุป}ฺใq�\ วAœ�ต]�ด �GeาZK� อ�6-�eํ�œห�ฦ]�๙‰uรF‚ั‘ฝ—�9ญ ฃซ)p*ค1&ตส0’ซ"ฐซ˜ขs^ก(ษHเ?""{กจR"ืก€a๒�อ฿��v์ฝิอฦฦ�หƒš�ร ิึ�เ้p๐k1‚!ฎvˆd�7� ฒ่š0ภฎ๎ ฏ%Cฏ๖:.bx.โยฏ�š; ฐฃ งP เ0Sโw สaIฎยแ€“N=?�1|“n jฐหcฺำ)Mฉˆ,หอ��F}เžๅ>๘ เดP"เ!Zฒ'๛);V„0๐ฒ1;ณ5{ณ9ปณ\๐ด, ดปณE{ดIปด*น8๚ y€ pฐG�€'๚€=#0i�ฒZNxฐ่Y  เ่~E’N=Ž–ปŒเfํเN๎ฯสฝาlJ๊Zพ๊ww)4๐๙™P! ทr›8 ๒JP'ูpทyปท}๛ท6ธMภ›$`ธ€ธIภธŽ นd‹ณRอะYภส$—J� ฤpบNR&ๅ����ํแu'ตฮพ ๅ1Mเฃ.ฯ๐n๊Ÿ๋ฦฑ�จXฮ๊๚พkXwhฐๅP& €1ภ‘i ฌๆปภ+ผฤkผศซผฬkฮkUX%ฝ@ฝึ{ ุซฝp]ึŽํ๖`p6าiำฬ€Y0i|'Ÿ€� ๒,๏๒fฬ mๅZญ๊ฝLฟžซวYอ�๏yขmฝ๏Fw"‰อI,ฝa๐๐ Q+ œ �ภ<ม|ม&ม†a|! #ฬ$IpL0๒ู.5@;< 4฿๒�๒5(�„ฐปฬj8Oน:_ุ<ฯq�๓ถœ๓a[ื`{๔ŽฯkpฦPม3ZI�K บyRQ<ลU|ลYผล]์@ศฆJal'dlฦhฌฦA‘iญœ]H@RP๎ ฐธ@ #ภpNœHธฏ๛ผ฿๙Q—H$|าข>พs์�Eญเฯ:ห<8\ู_�}„ทp B %�|ฦศŽ�€ๆว7/หC–Œษšฬษž สข,ฅlx์%~ สฌ์สw‚ืutu]พM]4๕ 3b‚ zˆฦ บฦ‡‡@‚Y&\ุ๐aD<~RคH%Mš™าcG•#Yฆ,๒ไL’4Iถฤ™๓ใL”:wฦ� TฆJžEEšT้RฆM>…U๊TชUญ^ลšU๋VฎSCpภะ๔ย…(ฉ๊๙หgชC‡~ด7ง€w่ a% ๖ ๓็^&!(ฤะ”ฮ฿ปn:|ฝP0T2ศ’ˆๅผ๙šl๐pวZ=eoR„ะเN5qƒพSฒ›�ุฎn-ทƒOBƒๅ๚ๅM™ร{_iดๆIโม‘็Tsน๑žวavลž]๛v๎�ฝ^|vœ^ฐ "D„ั`=&4Pกข"8xP ๖�ƒ‚ P€ฝ�4๘ŠƒŽัƒ‡–ภO๖๋oƒ ” ƒR(I>฿› ฝ˜ำ@D๚ aทเ„›ฮ&็ฎkฮ%—ข;ล ^ไQ8žˆZฎ8฿’ปn<#D2I%—dฒI'งŠqธ™0ภ 9”Jš >ภA bt๑ „!s<ŠJงจ๔ธิฌDQว(iาฑงŽเTฎฉ7—บ1)ูฌ๓%<ำิP<Ÿ$ดPCE4�QE—ผ“ชผฌIG”!Hศ’(EืlTษ<ฺtQO?5TQG%ติ๎ๆj1ฌ๊~๓tฯD…|*SSgฅตV[oล5W]wๅตW_6Xa‡%ถXcE6Ye—eถYgŸ…6ZiงฅถZkฏล6[mทๅถ[oฟ7\qซ๊๑งr{ƒu\uืeท]wc=wฬsำ}ท^{๏ลY"us^้อ7`&˜ิL�EXึ‚fธa‡ทณTM6ญKุ�"ฦ8c7†*โฃธb่8&นd“9v5P‘y{Ž฿“_†9fŒWFXf›oฦYเ„[ๆ1gŸ:\Lin้น F:iP780A๖žฦเiฉS�Aฉู‹๚jจถ๚๊�ฒไิๅ˜ไ[iณฯF๛ป Fศๅทๅ๙GNinป๋ก�ํธใžป๎ฝๅมปํท๙6%๔ฐป8mลg\ซฑ‚x"rษŸุ๒ษ#ฏ�rฬ-ฟxแ‡'พxใG>yๅ—gพy็Ÿ฿ คุp�ค๛$เ่ใืถ 8เP‚ู\ฐƒ๏้rำ8 (0a๗U}“็๘ห†พ~๛g•žƒ'เXห(nฉ‡64Q zฬzE1_hชAš9คเ)บ8เโ†ตภฆP†˜ภแ� &]x&N�ฯ„'ี๚p€:ะขฟ๐€๛x„ftม๛เร!–ฑเ<กศ&,‚Œ€!ˆD‚8ฤ0ภ'%…?hQ|จJDำ ตธลOQ ฐF5A˜@LEกaฒ@)JPยซ๗ Rผ  ่1NƒmBwE6|@G;โ‘… ๎CƒGไ™(ยฑ82.Vา’MA3่ค ’D#ิh66c Y0I่PDจภฑƒ%เwpwนo~๗�_ฝม ภ�…›~‹ ร๚6๘ท`ๆ‚เ_.&„),a K>๑oื็{`-ธ@‰KL‚€R@ล'NฑŠcุโ?fถ!ฦ๑Woเ_ทแฃ1 ฐ��q— ูว@ๆฑ�ˆ�œdแร9†ฒW1p@rXยN@<มสVhมnน.c'ุr—ฟๆ1?กฬB€A2ฃwบื�๎qฟ‹!8๔๎฿ุXhจธ๐‡'˜fQЁ#�๑ Hฤอ๓xส7‰฿ิฮJ^๔W�๓Fบผยชr-%ใ8ใ็Užะ‹ž*ฐlvัwฏ�๖Yi=ฮท2\�ƒ๐ท>ฤrtญ`เ~๒น2|�oๅ–๓8‚๒ฅฝhึฐฎว nภ&ึ~๚฿WศrดฆY“{lR็ฮ>@ Qฐภ๛เ‡ฟฤิิu&… ฬ Bโฟ�fกวฬ รแฟL3Q(DภTภdภtภฯ7ฑธ,๘๗+Š๛่ |ภ บั๓€Gh‡9(ผฉXCจน0<TAZ�ญฃ่ธwะxxƒ มkX†e�[มD9โSŠธ^{hJฉมP GโA„ย~‘ ๓า‚kเ‚๐fƒ'lŠธ‚{ƒฐ "xŒ(4C)‰Š ˆ_8�H๐`เBฆPผXศX?0Iฝ3์มDŠ 8@ ๔ €)xะ@8=P‚�น(€ƒ0แC(๔รฃ 1 ฐ0+:“`Ÿ„šxฒI\A์C H#ลT„—ๆSลV> tลX์ V”ลZฤ=Zd ฆ้ฉษฏ�ฑEฤล\d›ใ›ผฑ›ธฉตHฤ`$ภ\ŠวัสแœษูOrฦlLŠินูก™เ(mว™ˆ<Šุฦqดล(0Gฃศ;8๘hFuT>ื ุBด๔€Ah‰‹ผ�ปพณวฤ<“@*ธBxฉA8€Aุ0( �มใฅ๚ฃ@๒ฅ€K Œฑ*ฝำ�CฐLพƒุLศฐูฅ^2งYงงZง“€H˜†๛ =ฺรห้Sฒฤณ4‰๐=‹มDงฉฉLอ{„ล�ง"RJฝ 2"ส$Šˆฦ‹]2(Nฐ‹พ฿TM?TรmhCOฺซะา8Ÿd ืศ’-!\E-ูพ๎ฃฮผดฮ ุ‚v„ด,๑“LI)‰_�,(C“P?๖sฟ๕ >Yม€ิ‡@8 H{วํ|„kAžฐ?�ณ]„ญูšชq^ดPฎษะ ีuฏzะ๎29๚a.่วงะ-ะะฮ“๐?f๘œฟ้›ฟQฦผนAฦฟ c๔Qฟนลัย9ภU4ยbŠnค Hะ�jj”ฯ้*�œiดRkฤRjœFะQRสหปิฑM��ฦ“ฐJ;ำ�ืซnd(eำrฬฮ ำ]+วจ@G5”๕ั$ญำVcGwฤSGzฤ @ƒ/๘ฮ?ล5}ไวf €H�ิ;พCอPYMศ‡2H˜‹_ ถฑ‹h†UacTeH‡„H•HŠภHย[PBa€…~เz*dSถขฐฟcศฟcKถTํ. A?=aI_pษ“ŒI฿x…_๐’ธถl�ถ™˜€ ่…๖ร€lีถV5V๋ฒIœิษe�8ข4Jค„8ฅ,•H‚Šฤึ 8ทtใึํ‹ƒ เsC7r-W้๒ึ”เ4-ญไJ๐J:�” 8rPคzณท�™ธ%x`‚ืกXi X่๒ฒไJF7‰หน$ฝ0ฝ‘•ne…–ห’�ไ ˜5 ™ํXีพ๔K‘Y฿Hฬลิ€ฦ|ฬ ะ@Wxฒ‡‹ธะ1M7ศฅDZ‰ซY›ฅLณ์ศ! อึิำ�ฝก๕VP…'๋ธSHZฐ �5ะำจe-ึผVƒึ ภM�ฬใ 8Ž๕<คงVU9–s9‘BYQ0ฃ“ุ�–{9ต}ฎN)ธKิ‰Œfxฮ่„พQ้@}ภ‚ี9žsส�‡0HGศ�ž3�็บฮ์ิS๒4ฯ=ิ>๎XEัpห)Iบป€ค\สTš�ั%]๗„OŒ ��ไฯ=�ฯ๖K[๑ yฺBž :ซลณ…ปๅต[�Eฎ5„ญ€ ๐ว}Prผ€๛ห?P๙5}เœ‰ฏ ;ม;อฑC_ฑฃ^็:ั…Rจ๐€ pQญž @?=”จ9ะฟู:ปด๓ฝซ…พc_ดSป๗u.'ญ)(M4•ฐธ�21ำHKŠZe`แชเ ฆฤชฅ5Kท�7–ฬ„ผ9๋๓ :‰H‚{ศุDaanฐ�ู๘ŽžสŽaDัa�.g�'Yป‘ฦ††ja๖ ์,๓ส0G 8โKb•6)bi๛a†แ%6ch1น6z(ƒ(n$ฎโ� ฟŒฃ–๘ayํb…<œฯ ,ษ’:–-ใธฅ`[˜€ธ‡4ธค๘แ4�.!yบ–‘b4ฮO%VHdIIๆ!๓ฝ€Iฆd“ฐd0ฑธ‚ฐ�‚tX‚(˜"ศDCžbD๖.E&92q:†š5.๎aบ0ธ้90ฑภƒvh|`Zนๅศๅ]พ€^�eฆ%$ 'Xlุƒ1ˆ8ฅ8dVฦ.WF-X~)YFLH.‰X)˜‚5‚์€‚)ƒ!ะNpgr6gtVgvf ‡8x…< D0cGถๆ์าๆ|*โFพTZ–QoŒญู9@74 fhาบถ�Wa ‚Z€…็9ƒ่Dฟ|-ุbo�๓l๘ƒxfB˜�“จๆŽฆ.,–้+�  J8ฎeํ@=ุ'‚tุีล]จ3ฆb˜Ž้๊;-šfƒN„๎๔xpฌศ๊Tกv้U6๊kKu แกn( ๅห๓4�Xธ—ฮj›ay๑๊wฒ%f>๘1๋ณŽ.Ž๒‹ญทž•kfŠญ”W๛kฑk„�kฆ๋๗นแWI๋ย.งLa\qlฦ&.ฝLa“้8Vง ›nใ}‹lษ�"๎š~c’FœFฮึ=}ํหัึbฌ~dฬNอ6 Ž้ก;์ึNต�ฯ~l7&๊$ฆm$ฑํ’˜แN 5�Žฆลพนอn“@ํ%‰n—&j–บ�mๅ~฿>Š”ภํูฮ้#)n‘žโค6บิnhโn”{ Y๖‘๑6’๑ถ้=ู‘ a๎๔^ž๕n๎>c‹5ํ#™๎�ฎn๚ถ’ฅ9�พค๛fม˜๘n€๏๑๏๒&๐คž�Fpƒษn‹Spต6๎็o&)n ˜ožSผpSูpfAแ๎†•ช�6’็o€ฆŽ(ฑ๐•o–�ม4ajแxp๑ฐm#ฆq4˜ว๑EAr [™๐_’!qG์#_๒$?”+/.šY๋๐ำ&้/j’,,๗�-฿๒ผ&?ฺš“ฤF’5นดูบมฆ3฿ภ๓–�ภ�;w>ภ„@๗๓ี๋เA7tัYc74?tไhEgฝ o๔็zt๏P8๚a๔Io์Dทtงใj4ิt5ฎ๐ Šลฺ—R—c๑O๕๊ช๔*Gl‡:ฎฮ๔V๏ญWWk Aj2_uHท๕ไยuฃั๕icc๖�u๎ ๖ญF๕j๓WYตg;vd็๔๎`mhvํ vํฐaXฌ๕kGฌl๙ู8V๗๖ๆwm—๓๐+wญu_๏tr_wๅ‚wC™๗xท๗{ว๗|ืwvMแงn฿w๕๎๗$้*€๘๘7�wI?๘ฏ:๗๑(x�†oxšzx๑ˆ๘zŸ๘ฤช๘E_xŒฯxฐฺ๘Hฟn‰�x ๙๏ธxw/๙~:๙wl_๙™jyj๏x•๙gšy๎H๙›Ÿ์„?’็yแส๙W|y›z.zmฏ๙ฃ�-˜ท<ฃg๚จ—๚ฉง๚‰'ฐNเซซฯ๚ญงŠ0‚๐MŠฐธYฬฐ˜€ฐ†ภ\่ฯชธ‚~ะฐOบท{ผ— ฐZMА…iธV่V.ร\`1น๗>eุข‘่N5/ม‚Hะษฏ|0/์aฑ๐๚šสฺ์Y &ๆ|๑IหW€ฬ฿�๕ |ี‘๕€ (#ฏt�๗Pำง}P๘ ’ธL{“๐€`ฃๅz“ภฉจญเ€ I€็�&ฎ€'ฆ€ q•ค‹฿�`bธ๖Pt�b้ ZะถX ฺPรถวa0_๖we€�่T(Œˆ[ถR(hฐเ†ฦPy่๐มZ& �hKWฏ�›bX๛‡OXร‚+^ฬX๐ƒกj�๚)Sbbฒv2๋แ๛uกC‰\้�ั๛ใ!DA•ฆ๙8ธมƒ*W!€lZฐย”T;๓!Jแมรj๕”อI‘ภฑ@+œ Wฦร‰Gึ�ฝW†œ;{�,aอ” i+ยม)เภ‚.l๘0โ�ฤŠ3n์๘1ไศ’'Sฎl๙2ๆฬš7'�สญ2"Rคภ X€ฅsjฒt๊&„‡O\)eŠZ5kืB่hฦ(…จh;Lฎ0‚* ์ฺฤใถ$ฉ๔ม{vํ ฉฃOฏฮ!ณ?\พภaAยC#อ ฒƒ€]พ@มƒ Bง๑8h<้RƒUPุ๓วp|‘Bx่ณIšธV็๘ฐ ๔pO ฺCศไมP|๔ู‡_‘X8มษˆโg1ส8#5ฺx#Ž9๊ธ#=&–@4<’8™ผuฺ%ำผภภฝรB h[’K6IฬO‘ฯ)๚`ั|�€ืKrหใœUฐย=‰\ภ#L”šl&ย‡ิBEi@ธะŒ9ะข`L ร>q˜ๆ#ฒษ*ป,ณอ:๛,ดัD ฑO9E˜๐—Sจ๑wkฬaƒฅJjษท„;๎L‚M)>+ๆqepๆsI๒ภb4Š์Vฎm่๎๛Aฟpฐย-�hF $ภ zPบขอ;ศ`ำ ฅ“%็ฅ1@l"ปTก<่n �QไCฤ ุ์1Pซ€จด5ฤUŽC.9ั‘e5๐ม ?<๑ลฟุWSฯๆ5๖%ูผะธ< จ“ภƒ ฆ)=๕ึCy(ฦ`ฑฬ"6ˆุQฺร๘ขอฏ"K]�๖��“0?ม๕‡๐ภเ‚cPŠใร �คทeโOจๅ€JH#๐1X๖hม�อห ุภ `ŠคเVฉฐ๋เทE0จP Pุ‚  ภŸFfI,ฐ$x`QVm`"ว;"“จฤ%2qFธล)„ P=z๖HD ั8 zกร5แ†7์๏Wฬโ—’‡c๐I ื๘ƒ :X)lฆ๘ย˜7ภ๕ฯ~ฃษศ?มูa_x‚š J1วzร๔ฦŠeผ 5pฅด@ฬแ ๘เwฐˆ7ฬ!LNฉภ๊x /tก” zP„\3‚� ฤ์*X6มƒ ์_ธม๖‘ˆ ฬอuฐ$&ƒŠm€ Fl"4ฃ)อiR๓ˆ^”ใ`าDyิ€ HAโา‰wผร0ะ&7ฝ™‚,!uส:1!€-0 ๐ƒJ�‘, มhYบ‘‰ˆ“œๆ์็?:P์�เI& ฉta�ศ‡)R‚qZD'b Œ�qI[Lฮโdk)g Žrk\ฤ �โภถQ„D!\ฐม๖ iDุจJ๊แQp �ะลMช)ีฉRตชVล‘^“&aˆ@ อAะ@ *ุjWฟš‚ 4�@"เ@€คฆ7$ซ2@ฮu๐*ขD2ึฒช  ่๋_%$€ฌn]Jค*ะ>6bลซZ ๒:TVฐdโ`(ะ<ภซf�ภkฺ 0 ๐ZHณ`ภe!ฐฺ‚tฮiภๆUƒ+�แทธApๅˆ�ฆิำค‰ฉg —ƒทi˜n8หฃ[9ปueฬn Z�Bัu 2Xใฒทฝ๎}ฏ—[๙ยwF0 L˜QM !ฺ5 „ภ‚๓&ๆพ๕=0‚ฌเ7 "ภฎiXำ่ ›ˆส^ฐ 9ผภ4)Pฏb aH˜ม�&>1ŠSฌโa –๎ž้ฆ,pยŒ …aฤโล0P‚w ำโ‡3ฦ9^1’“ฌไ%oๆgศ\ฃ ฆUt๘X"ภBˆ,˜„โG€ %พ๑ )<™ะPย‘ 3ๅ*ไสY�/“ใ,็9ำ AD 9&ฺธ?ะ2 Tด ศNyAžA x฿ %2เnPฆT3 � hAฺะu�4จC-ฃ[]ฒ—c “ย08 hม.LvTkศใ�a๕AXTI|2jrฅซb^p†uP AbQŽ{ ฃ0จ.ข:.ผ ๊p‡; !' 9ั.�Œฐ‰mld+›ูฮฦตจำญ๎uๆVUภ0Šโด3 …i p€ \tฑ @๑;ฤLปิฐ‡|`กน†มภboแุ๘ม0†q�6!`A Z๐‚F`�ิภB ` ˜ภิ๘ฤ+ž^Œkœใ์พ9ฮAmุน("xำh?Kืธ6�5อญ‚น~าSฒ๚€เ}„๊ํjMใฐโRpกo*@Y๔Œฅภ ธถ^ง$}ฐ* ป]�žี\ 8์:+ภv@XWkฯE๐ษ_$^ุ‡ใึ {Ž๗%~๐ ฤม๐tA�ฮP h@ริ๐ฦตฑญoxƒœ'พ ˜ล7�๑‘Ÿ|ๅ/๎œำพ๖ ^@.สK–|0โ*~ธE=�1Ž9hด#�Fภvฟ฿๘ กZุโ–„ผข๘ภว?ไก๛่Dฉ฿d�œ ฒy‹ นเI๏ฃ4‚่็‚X;s/…TผใขŒXJ๎แปม`PFIภCฬ  „Jฐ„KlD๗iร7m8ˆ\%$ฬำ`€แ!�ร�@ ฌย78žใฉร:ฤ‚#„pq€85”\ษลถกƒ ข5@CtXwi zเ:€ ’  ข ํ !š˜x0์‹มI�‚qฐ‚/Œม€3˜A x<ื=aNกฏเม‡„ฌศ€าB|”มD$๐ร�ฤ„ŒคXฮSL€ิ p(าs =@HพŸห@๐1(ศ|AL€ ิ"Œม \~ะ,œภ4 83่ €‡x‡yl ธ!สแQh,  ภ+$aแž์Y` d€B† ่–ส ๆ™ขคม0 ƒ บ;ฤ;ฐ ~‚ญ�b๊"/:^,�ขใ0!7vใpa�ฎ‡+Œ‚`€'Q LP9š#:2ร"อJญ˜�|pฮ ์รภลn\ภ`€0€ม5B฿dฮAxLซลภ6ุมŸฬŠ9๘€จl‚•�C#๔`ภ,TะŸ�ษFlƒ‘O๋lA5ฤ0œ ฐ‘ ,24AŸtัP@ $ืล\รOEภz๐@ค€�ƒุโ`<ูย:xรˆxH@‰ีUผ€@2ช;(c3ฒ5Pยจซ %Q%R*%Sฆ™7–ฅYZี กrM*ลAPวเก;r[ั.แŒฮ๐L๒ฬ€>D ภ#€C:T?HBA†_ๅดcS๕><ิCอS ˆNˆ#ฏนN.าA4ี>จAแฑ�=ม &่C"hม2ธยครย4Œ $@ fa๒C(„4ิค| _&=0ทF. ม7lค\!<œฝs2''T%t:ฃ7 sฆภฯ ฦp'Y!งržฅw~g5ใZ2!].…].&ฅดฅyยe˜]์ฬNC@>ฎมA@*€Cเ€ฬC(`€๚)ๆ2&๔Cะ€ะ@pโdJŒeZคSdๆฉqฆgJh2ไย'Hhะk.@แ๔รแ˜€}‚Cเภ์ะRภ ภอ"ะ๗E_�๙!Fภ5�Z„› ฦ„B,ฌ‚‚7 รU:�#4H ้จ€รฦๆ่Žf@�(xfฉ–"‘P˜ร ่Z#จgœงE‚—rdyŽ)\�Pษ€Š†ภ�ี# คs ฟ๐šeะaุi$ T lร!ขภ–จ„Be~ืDูEฮ‚0ผ˜LXG~$(Hƒภข3LCฬ‚9|‚ภ.เ)ม fซE5ภA4‚น$ภ`ม „b`pย ˆ€ฐQร7Dž ``ภ ่B ม <ง36#’ฒ‡๕.`Ykฎ๎jฏ�๊Q ๋–n+ทZอ–B(M˜ฆ)™$‰ฉ]R@/�R0%p€8&�’ั\ย<๐มdA9๘K”„า(•Rแแม&uาTเร)ฬมุภ+k่CEบ\\A?Hƒ‚”A$AbQข%๒@Š๐C!ภ$่!p@า!%า"ฅIพ๎kฟ๎“Œ‚5ุƒญ4อค>ภ*P8‚A<ร7ธร74Bุ๊ก๙ฏ�@1\e็5#7่๊ ๘ภ“นu -ั-า*-ำvซุŽ-ดpภไมKuรเ‰+๐‚ž}2DP„”&\”)ฐภดํ�ฦญ L€P=TQล๋ญC•@ล€&ฌลE�A`TJ”HฅIู•๙๙รEpมถ0”C ฿ภ�฿A�ส�F!สHp.ข$ภะร0€หph๎CัCD…�๎„>1ฎ]@ ภ›สB>ธม์5(็ |‚ฦe@bงi๔ม7P5l€-P:ร—mฃS/\o๒./ู†ฏ๘* ล�n๕ีH…ฉจภ^2šlนีฯxี_ดoไึnะ%€iฉ@{’ึvšWำไhภfUึ#B@iฉีฯ(€�ม]`€อ Ve0��๕ผž0–c‘ํ^ž้…จHฬA0@ผc`ภ(งX#ผ@” ๐โ3๔ม €ม �%pC1œ g รล ว๐ �/'๑h๏a1dhํร1‹๖6 “่'1่BJฎš\ฐ@–A1€eš#<ฌ†™ม ค€C!L+v๎จฎxฑ€ฑฯ1ื๑ ณj:ˆยh๋e ญข ฐ@๘ภŸ09ฬม ๘œฑc๘1ฺ1$Gr$GŠท ฏcฐภ๐zื$‚žŸศqa`ฒ$2)—rƒrwํช@)exฎ)ฟ2,วrfDŠหฒ-฿2.็ฒ.๏ฒŠนšGฤSฺฉžšณ1ฤป]|{@�พีp0TึoKึุ้Yฟ4aKsr8U๛๗x๖‡๘cCt[ฯดvกตS3‹†‡๓L8c8‡ฏธœ w‰ธรr‹7๗SCuq'FŽW๔ ิ๒�๙7๕3งuqรtK๗๘BWs’�๘! ๘ภP%)PB` 0@ฐDลน™ืฑZงyŒฃต4ƒ๘“ฏธ›O9 คA1P9ธ:|:C(๘ ่่€T@pžนกณ]**O†h‘†%oxS4๚J:๏๘šwถAHบy#yœ‡V8zด ย2บ บร เม08ย*p�€:ญ3—ภ˜หŒ4Tลฃ7yqภ ˜ภฃ; S[๚ฆงดAŒภฬฑ๋x“#;a๐บฏ‡: 9ภ`<ภ 5ฐม%$ร09tB ภzญ�x1'๓2k!=๏�™0›‹;yi?ตทถ”ฏ๙4†ทิ่Œ€ุ'จ์ผ็ปSฐ{<ด]ิฺญ%ไซ3J›ิ�œZ†GsผŸZชญš๘ Ldผภผe๐@!,ใถŸz<@oษc) ฐฅ{‘�u9วั\พ๛็z3ผีhง|SฃŒปด“ำืฟcฌ.€<\9›3C�[ภ œ6๏[E<L<ดดป�� ื�A๊Kว =ัSCษ‡ฝ;ˆปุ{ƒ\งหฯ๕e-0iHsF๔ธUงŒภ(Œยขfั$]/ ๘v™ทเ๛ถ:ฃU� Y@หฤ฿ฯUฯ‹฿%pีjIณุ=@e๙��jq—ไ�6iหสพฌ}�๒3l@ ด+~„ซภู่หผฬ๓d๗�ŒVๅ—�`€้ไg�NW€ฯไณi RL]ี>โg–`˜พ^ัฃœEB HฟWuัฮ™W๕2ž๚ b�ถฟ #๘ภฯฆ=M„E์S1็8D�Lศ„๕ีร/ว(\ร;Pl4@�–ใ‚† ��ช็o�6xธƒฬ_ฝqn:tะฐ‘#.้ำWฏ�›”ศ•ฮใNผ|7ฎ /q~ผDo\=p€Rl€&zŒ<„เเBpˆlx@จ@ร‡ฑ�`ํ�>aW54๐ฃ-I“?ชๆฏŸ2%& k7ทพ_:จL็�ŸคTšๆใฅ†ˆช\…ฑ๑`SŸPmฆ�ๆ๏ึตM<4fีภฑ‹+œ WฆWTQซ>|�ไ�RRคู“9qŠ]VQ™bp๕๚ะฃn�พwCล"D‰[ถRเ~‰าทx๊Hน๛FJ]=ว‡h˜ ‡{า  ฤ1ๅ€j $ˆ ”่`ว ฅ)T#‹N๚ #์ฤS1“]ถูBะกFR%šL@ฏƒ์ฎ;ใ‡sป#ว\sฟ้ฎ)Hภภฟy้ญื�{๑อW฿}๙ํWฝKฆy€๎�I€ถค%ยpก=rFqญ ่‚ภH4qWF๑˜ม„‡Ž)ๅ… >ธgc9;๚ศ’l&8‘Œ0x`#viๅลAๆ‘โ‚ Hˆ—ใœ@ส€&ภ…˜˜ฐ#ฤ8&*h@cFนไ“S.Q3ƒ8 `N๕)เ ^๊ZจH ’ๆเ‰)&!ฃz�9่8-%ง �ฮsฃฎฟ{์zุ†ŽH@เ˜หŽ�˜ฃฎ˜‡‰-าY"ŠyˆเใK=:š€๖Ycol๙ฤ™๎;gเ๘ b˜cnw๏�๗ค #ŸS๔มba๔H� 7žaVYe˜xผsGGพ!งปo˜ศ�฿๑ษ/฿�๓ัO_�๚๘œญh!‰ุ=-ำผˆ'@|�q์R‘Y‘  ญเA šม‡Lๅh&%น๑f๐ใภpmผุ0`zp‹t B - ศาp"7ี]  ฬ@A๚ฬะ๓B ฎQ…๘ๅธP-6€ ฉ-Gžใ๖d UPเภ nแM„ก$�๔ ถ p ƒ์ uฤบ—ฑSภกgุCฬ/ ภภ6‡™�mD~๔pย"ฐฑ‡1ภใ~ ร”G0ƒุฅ`��ฐuž๖๑เh-,�‚ ศX)›‘จ‘ฬxไ&ภกR๘@^โสฮxฐJQŽ๒]&่ไ๚P™JUฎ’•ญtๅKๆ‘ภO~๛PC�์q?ไo9๑_ศ45 †€€ฤ!ลXIh ‚Z™เfฉฐ วจC€` R€ 0*f2 ๐%*ฌไ ๛Q!ะ`MP๋เ„u#`� P@"ฎ-’€)$4แง˜,@ š่‡1�โ‘]fq#ฉๆ5ณ ฦฤ�ัˆ+#<ๅIzึ๒–€#็|ฉ  qxEhi๐@^~ฤษ๋YฮFsZS*d%IโQ’…–ดฌjs�z”@๖�บ3สPชƒฉไ๑A/_UฉN•ชUตชG ้๘,aIk˜4| ์RPง"aŽ$ํDy*ๆสšฑ‰—”ฃY�ž�yHE@s‚ธฺ[@ค1@๖„$m๊>,€ฤGกฝQภ&`ู'^ฐทค1n˜ฬิ]U็Iภ&Hฺ3๕ช˜<`o 8วšภต’€ฏ‚Hฺ[=U iฬVK™-X`Zฐ,™ๅ €“0คเฃr์_FpX€"Mจ,l'  ญผ มn 0˜Œ)@m@ซ;œ5€RC”oI+�ฆฝ€ฝ๎ AรŒ…e,ยŸ9O๚P� v ˜่ะž#q=uฐcม์ภฤ ฎa O˜ยฆืy€ โงjP’<"Dsศม#=O�๖‘ˆ1ผกญŠyซ"A+"M8Z†ฃหŠeผ 5b๖ˆ/�เ)NŸฮฑ†.0+ ฅ>N1‡1ุม แbƒ>lQ†1BmET1=wื p€vรH€aลุa_x‚ฤ„)๋aละิ1}l@Oล(sะD… @ผa$๘T?lก".O`thG&พP…ธ:ทศ$6�`<BmŸJ๑Š˜ฬdจอjN/!ซtฅ,‘@)d6sฉ/‘j,ลA1y8F�ดpL�Ÿ2'8ย3ุแ`Sƒ/8ื„lqˆAPต๐ณกmi[uฉF B€LŒS,เ`ศ์๚ด.,ใ%0qFpฒ ฤ@�ศ‡)Xภ&^h @F&Tฐ"้ศE?ม๊0cฅฺืM72QtยK๏ะฦRะ€;hCG&`ฐฑ๓ˆe-่ ดB„ &hL๒!ƒด7พWต6 ,�๏0 Žํ \*{ _2ม”4`�๎6ลCh^–‡๋NE:�” �ฐy คา”Žƒ iภึB’9DG:~{:„t”ผR๋n๗ปYะฃ�"6oAฮพmจ‚„๓๐ PฆCs›ร€๏~�Iฤ—:Tjฐ!คำธA�เ†8จั‡ˆเ\•ฏ�3HฑSO›๔ฅ7�้ษ—€จ ฿€2า€@*A4’šฌ'f=uA(เ(@"ุภ^ฯอ,@9�ำ(0ซ8\์ณXQ=๋7เzุw@๖€Nย๖ผ>A!gqf� \อ�–€๚%P�ฃ Ÿ๖ศ๗ฦฟ‘6ฟ 9โˆ์i/Tเ๚Z๏๕žE€๖ŠO1P"�`Cว/�ศŽ›†ั๖$หo่ฏ๖T`# V‹�ศฮ—  ฒ� mภ#‚๏o/ฦฌ€�p๕ p๛้„AH07f$z' Š: €๕าƒ๕(Aค๐žม8ม ฅ๐๒žแL@WP๏ ม0 ล0?rpxํ=ฮ/= /'เ ส` ด`์ก F/'Žล 37๖†>๖p:ะc C*=ด+lBJ<โ๛๐ฮP:„bmุCฬŠ(ัCJŠH>q ‰D0๑ '่ ืฅ#|เจP =ฯ‹AŠ  วฐ]๑aQ•ภ�กาaฬ€ฅZด�#+๑ฎ*1>|๑^:‘=Šั>. ๙ฤ€Lฑลก|ภดbq�ฉฑญั?0เfวูพะฦาp? q= +ย์>ย๑๔๘ฆฒจภ ๘€ ม ” j๊้ฑํ๑๑ั_ฮฑ=‚ั>๖๑๔ƒ๕^€rเค12!r!’^pค!ูC!r")ฒ"-๒"12#5r#9๒ฌ†1?xฑ'ฑf:ฒ$M๒$Qฒ"Eฒ>B๒#=’$S2&er&iRฺŽFm๗pR8€hฏŒhPฒ$PD ฒ* †2D€™˜๕ั%@^„0ถIƒะ  +)ฐ&ม2,ลr,๏ๅhข �ม�A1$ฮ�.ย‰A�B>ภๆภาb-ฺโ-€c%�๊A€8ภA”0 แ๑ ฑ#` rมดแชฦ."๎ ๊Aฦncฺ’!0"$ย213‘,Iณ4M๓4qฃ’€ˆพเ พภาLe >Œ่N`� e๔ภฅP…6€�0 ึ "4†ภเ€aŸคค@ไ ฐ๓`Dfฤ šlfณ6ป๓;›eQ3=ีs=SาfA iุ al‹aฦถ b๒ \เึ4 š@o๘F)$€จ"b ฎ l 3†‘:๗A € z ์A Xฦe๊wN€>ฆaๆC�_ DงูณE]๔ERBว+8 —Ž@b‚@๔!ด`�ŠาaŠชศ าก๘!<ยvแ2T l€NศQIเ0TC9ิ (‹zฒ…^(ฆ>%—บ”พิง`4MีtMฏQFฎFทซL˜ kเ >A่ธˆ”  กL R๊ฆภฐCaจJซRช”’เ˜ C7ดC‰‘z*Lีภ#ฦ๔jผดง‰MIตTM๕๔�S๔‡2’ซ>ฟสžำฆ!fม>A)Xซต^‹ `\ฆ‚Kรขภเ กSŽŽๆวhD ่มC�[†w@jF€>ฝ ภ EหพN๕[ม5\ญ*ฎ คม5หเ 0‰ร<ฬAx€ป๘ก`๔t…อ� ฮT$ๆ– สก‰B wFม:/ช#เJฅ€Yฑด<ป5:•/a7‚ฒ ˆ@๙Z6š€2ญแ2Mb1$n-[˜–$|?6kตvk๕ฃ  !'oo ›Pุห๖–0))@UZejณ‚สภ๖�Ž+3 �ดRวv.ภ+หk7qw>\ฒ0B &7fำ๐Xzด€tA๊ฤ1 %R=:wqYฉJAwt-ฒqOLtใฃeญ!Da‡t๑uawvี%pki—๔€qwฟ1w}๗w—|xƒทxฅเpY้hถqŽ $Ÿท^˜‚ธQชฐy•ท?L•†ww๗{๕ฆ/2@ —#+ม”–ืเภะs{อQvฅื}แ—Eฉ +าw}q—>ไW}บ—๕ฅเยw ส@nฯw#�b๘1zW‚ฯใu๎Z,ฝ่w>Žfๅ„�>@์�8 ฝภจ <"ƒ๗ั‚๙เsSยไแ4๑%ษ‘Kฃ†o':๘ƒ7Fธ„OXh—†1.ภƒ „{7‚7ุื†ๆ'๕ค_้= @”ก�Aธ@ไ‡ˆ%i‡mXำf”I†ุ„๑—>z' † ๘ชŽ๑)ุˆ฿ƒผ@R3ุy›ุ=ކ n-�ท= \uกˆ_ยฌก7ๅล…ษI%k@pฃ‰‹ˆ+๙’7VธŽ ‘9‘XW๘‰ัฐwmJใฺz๒x!๎๎ุ?&่=h! ฆ Š นY’:“!}’I h˜‹น?� ๑ย`ŽงE7เ%ฎY16 ›�ถ๖Rใ%พoŽ๐6'›ฉฑB<|@5FaเVฎ2›_"GAซ๒&เ_“zAXภ&.@lq๏ihO ฒ˜๐๕V‹๕ŽR Z€™6† qเพ ๘๒m�Y�ธ2ืb๎jo:๑�^"n™้&ทrl™PU@1Hบlโณ� 1ภš•าm%š™ด๒�ฒ’#j:  ‚ นo+yา'!(W/โ‚(าm๗ศŽ))๚@4ฎœวvYฑขkฒคฒธH`'๙=†ฒ(‰—๒š;y@Hงฟ=T๏ขร€)Uเก@ข�ว:ฆ{๘iD๐c0=ฤzญ�ึฅัM:บ)เ่ ฏ๕๚XฺถE ูธ๏ฏ!Pฅ7๛šn็™ŸP�j/โpะo~าไจy|๘n,^B/Œย/B@฿มฌŽ&0ไtภJชŒ†�ีฏ!-๗(ณ๓2วŽN>ภฬs฿๊าBฤตกผ0ต�%ึฌdึ,&RŒ8˜ฤI คฤE` /†7๛่V๑D|แ ฦ€CภเE๐ภ;3ถYจำ:ฑS;็ม<ุBMi8• เ 8ี9:—๙(Œแฆภ5อ๋xฤG€dฮqCQš!*ค‚เ>…ฤศา์ Lโ์<พ]„d@Dแ วเ จoJfไ8‡D>Oi๋ป~ พ�(ฃจ^ežูฟ@)�d W~%X0VcC`—lA�� าฌ\f’A”œาษวi ฦพ G€FhฒฆhŽ&์I&f๛‡ `J.&ีG€TมŠTƒd>eT�C@E  Y)•C=tLrŒQB์lS�>ๅ“์o”ีb?Z€คดlเ‰งb‹ฃ]aœTŸ c๔K�w฿'€Dํ๓azถ’†g|F๒G#๐+žs๗�iฝขxŽฯขโfwn‡�6"�ีฆ๗ใS>_5[&?"œ|X\ฃbcฒ&)0` โ  (ืhƒ‡-ืธุˆะŒ ฬ(E„Zบl�‰มมฎ.pๆ1DkDŒm‚8*›+‹ฎผเแP-* †เ  %Uแ  (ค !เRถV�KิกƒQฃ.ธhฆ'G’*xPๅ*Dฃ*MใฑrยN;$Hdฮว�ต๓์๐3จ 7๖ลyXใขŽmzมเรฝF๊�อปทƒส— “จ08ƒ‰ร‰ ฬ’๖ยตJ*ฏqุ!D็R/,่EŒEอ๘ไh[8ภ•}`&ศฐฌyณ๖ํ�ป{�>ผ๘๑ไห›?~ํฅฃฺ0+bม"m๏akลฃB[้Pii‘ƒwhเะJ/่’I๗ศWฑ’�Wิ‚_ pภ๎ก=RัLYุcล ุิS?�เSN!pเึ5nไ@H˜ฐ๔า+�€&aด@‚ Šhฯ‡อ ดมx้ีาJo้1F8!~-)ศ ‚ฎิื4E€ฤ>ฟi๖a#MฐBŸ}Vjะฅš.ตๅ‚Œ4าyษ_mน_’K†ะ’–�5มั(žzึุฬ/น+•yๆ[AhขO"Z,ฃ„ ;ฆใ#&$Pย#เคS ?กด$ำ&ํฐ‡สแ€!i‚ยŒn[ฌ�H(  R•ฅ+#cส*:c˜…9>qG‡,’<0ภ.JัE9Oˆ‚5เ@0 OŒขZด'“Žเ๖p†NxR˜R*f3mมฃ๙˜ƒTย/HFฎ Pด ะห�ล:์เB`ม~™HhF ธ)LPฐC,�0ม7ใฬ|‚ย””น—ทcฦด Fƒi‚ีธ4;ข<„ R๐!’`™tฤU(ฃ0xเ!)ุ"ะEๅ€คcุณPบ‰็ไpซ\ํชWsh‰sฌก ่GpV,ใ \จม.Z!ฅ=โ 7ธร>Q๎`�ฐ…๋ฆุ‹a’จ‰Z๐ MtAnaS5ฆ"•ฉฏc „@(„) iนิ๚! 8|ก oศ๖๐…'xr\›€pB†€ื@„�@ต `ๆร�0ยJrN‚ฝไ`![1‡ภขถcฤ5๘0ฏฯ†vด�I๋Z�๚V;๖นsxƒB ู}pv@t ๋Xหฝdˆถถลํ0!j?๔nVŠdาฎhI; Lีฝ. ~"“ะ!ˆrฺิฎ็𖐅r0ส˜…5Št`ฏ}ฝภัด=ภ �pฦ F0เwL;ฅ‰ฐj)/์ฃY˜N#ฎฒlใ_่ยŠ(n�O;เยVi่{��ฮหนะ}ร*@|œbcฐƒNส˜+วุบฮฑW๊่ร๊ƒว>ุ“ke3ฃน1ศ๒–ป�e้€๖ฟoภฟ’QY�ชyY`ฌ^Pศ์ �ุx@”ฏJzา”ฎt�wเ‡j๘ฃสpƒู:P‚Nผ๏ะฦRะ€;hC�G&` I”†ศGPืษKฒpE AK4โ ^ @F&ธโ;XƒำoHฒฒi€QศG4‚๙c\h.ำ"]ต(ค�ศ‡)<ฐ= ร G&X@‚)ค#T-‘ทlภ | ๕x‡)`pฒtaๅ>๗‚–b{ป๎๕K|๙ˆkœฺ% @ R‘ฃ`Z�žCฃ. ๎ญd@ก~G:๊ajYใๆ๖@๛m—`Zำœ๖ดโ0Prร`ฉF *p‚ndยdZฺ8p'ง"EๆVหฦ;�ž–�C v>บํuซศ�สฑธ&าแwtใมQ ฐ!ค3—ึถ&˜๖J`9ุ่เ0†ห1 จ[t-Hับ?ธN‚ l ึ๛8ๆ0— d…๗w x๎s  �{g าแ†ฌ็ขึนZVžŽ–w hศลฆปม03ๆO?|p๔ck๐hD๋๕p}ฎr�oุวa๓ค7}=Po‚ถ„ผเ/‚‘ึํ๏hDูฟ๗๔JVนฃ ]Hเ‡.‚c้์k๛\m@ ๐ ศ&kZ‰$‚T €ภ๙ำO @@; `€ D0—-]ru‚(�ฏ7p~Da*p �โ—~ ะ* -Q๗#  Gณ \๙โ: ๐} "!  ‚๘hRข‚ืƒ ˜(%์๗!(x „2€9tัะ&#จ้Gqษ“>ฉ40kq7 ด๐ Z•ŽO •Q)•+Qัำ4$ICฅกjC�””V•าำ•“ฆ•ตภ•.3–ei[โjiLๆ”ข0Rถ–0‰•Si—w9Qภ ๕  C †x ˜i—�@˜…Y˜๓ง2R—6t+;9—‹cC2• aI”i™6คs‘™nX‚ šกYq+F)šง‰š[e˜ซ‰˜ฉ๙’Sไšฑi—ฐ)›ตi›ท‰›นนซi˜ญฉ›I›ฟ)œ•‘รiœว‰œษyCผy˜ศ๙™ส ั)ำIี๙UฬI˜พ)œฯi�้฿ žแYุ๙ฺ๙›�)ž้ฉž๋ษžํ•ไ 0™ˆt‰ ž)‘ูCŸ"YŸ๙ษ–๎้Ÿ�  * ้Ÿ๒iฺŸ�‰ž๓I—Š ‹9  ก*ก๊Y  jก๕™ชก�9ก๊ก ขนYกชกJขข)ชข+สขRYก Jข0zŸ2บ  สŸ-Šฃ9ชฃ;jC/jฃ' ™&Yข?JŸzŠ5H๐Ÿมƒ!\จกC… :D1bร‰+Fฤ˜ัข@Ž� 6hแ*R€„๘แQฎt��eRฑRอl้™2ฦ ‰š7›๑ษqแ‰ŸbˆะŒ‚Im‚`ฦ‡G‰Q— ตŠฃาฉNฏ~ฬบ"�บN„˜@A b/z%ุaaM+!๙ ,Hz๒5H€มƒ�)\ธม‡"dHั!ฤRdh๑bฤŒ66๔(dH‘�:^4‰2e‚*TTจAฤ @€@‡–ไ๚%๓D>F<๘นUฯ฿ธ9~ข„ ภ0P”“Ta+_cฦ€bf&ะชWณnํ๊j” ˜aโt€UฌZueๅ*‰ตZัŠ๏Zฏ}C|ถ’น>”ำ‹8„โภƒEBเe_ฉ,W๖Qๆkyqๆ8lศcอ฿ปns8xpล @ˆศ2ฉบgbnุ B จ0;U  Y6')]s@!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,Yz]hHp y�*LˆA`A‚Jl๘ขฤ…+jˆx‘กรŠ;fูัฃฦ%�|๒bร[Nฬ“dษ•eb�ศ2%N‹7yๆ๔ DLขŽ=ฉSแOƒ>!๙ ,#‹!๙ ,az�hธ€Aƒ†‚ Xศ„#Fฤ 0กล„ >”(‘โลnไัใG‹!’œX๑คย#Ištฉ!eLŽ&)4x @‚„)8lPภ'ะ›+V˜’*ฟ|ˆRx๐pงZ=esR eYก‚lฦ�L๓%E<๚6eัิ/ฬึ’*Œ๐ †Š Rˆpฌิ‹ z๓ฐ.๛uaฑF3=<8phรlˆฟย.@C‡ฦื _ธ@นH=ฬ6œ€ชEŠั$๖๖}ม`ฎ^ �ฃXaรฐพt™Cโlฺตm฿๎Vš…—5๏Lฅุ@ีฺี7Zu็l เัปˆอŽนฅมั(` (Rป๙“6ต!๙ ,qzปิ@ฐ ม&„ €ม‡Jd8bA‰ ):ดHใย†;z�ง1dฤ‘$Pก‚`… DŒ$ู0!c-Bhจp‚^™™ ฐ9็c =๎ฅฺะภ•yGถค[eฆcDษGdถ=cเม Fzq^ข…(›ฌฤFœธ*•Z l๑ ;bƒื?*a#ดe77&8pุณญHˆ(้ธุ {A&L(ภl‚,ว'=– 4 !๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,~zš5(ยฟƒb Hะ`ย„ |QโDŠ#JtˆQ#CŽ1Pุ @*T€|ˆมร‰Gึ�ฝWd%ฤx์z๒ ›  š'ๅย$€fฌะใV:TZZไP๚O$FPอBC Hศฐ'Iฦฅ{}นqg_ขฎ ๖T๓็\ฆŸgซVhpR‚„)บZิ ุbื€!๙ ,#‹!๙ ,†zž5H๐Ÿมƒ!\จกC… :D1bร‰+Fฤ˜ัข@Ž� &h‚!B€„€C‰\้�ั๛ใA%Gˆ#บ|‚+[bx มฎV9Vnุ€เถwศฐตzำƒ‡Nว๊bm๊ส '– "Ab@-ฏ7'Œpๅซ—Gฉb„XขS5๕ด�IฑR`’$HHม๗ฆว‹sฏ !๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,zจ5(ยฟƒ Hะ`ย„t๘๐`D‰(Vผ(QใCŽ =BLะ@…  @ศ ฏขลึLไ๐H —D:็cย‡dขD"ฤ ส>0d์‹#ิโ„เธฒOJำ&Tอ@คi>ฎ^ฌัฌŒ6L9ฤ j80iqbH ?]:จี@@†ฝ4ลธก…ึ/2สล…Y=MCr^!๙ ,˜zR5H๐Ÿมƒ!\จกC… :D1bร‰+Fฤ˜ัข@Ž� „0qa#GE8X มƒG–^ž์ ฃF†,=^ดฉ3gอ‰7๚\yฒ็ษ€!๙ ,#‹!๙ ,#‹!๙ ,ขzž9pะ@ฐ „&ฤPฐก†ƒ 2tHbD„)Zผ˜ัแฦˆ~”˜  จP1r!Ž[๕ดiชFoNKŒ@šu1ดฯกeE๊]ฤูฌGzR€0kโo่? 9ฺKฺฬ‰ะกP‹•โขSงYฉุ่ !'•ซอŽ-๋ต)ึ)้”˜ 8ๅšท(01ƒ� xAb๙ะ้SŠ c!๙ ,จz�ะ@ฐ A  „๐ฏกC‡œจ0!ร‡#œhฐโBŒ%r,่ภE�4Ž$้๑$H•+ถDูPe‡ @PกBC‡ $ !E —5†ธyœฟqn:�๔pงZ=esŽาL)p A๏tx๔mสขฉ_ค!(QRMP@e๓แXฉ ๔"ๆaซฦ%Y๖ฉั€!{Gj4ำรƒ‡6ฬ†๘ฃฉฑG–{k:†\e0ฉวYเ�บ$๐‚’ฦื๏  p๕ย`X —ฏ๕ฤ’5‹V-[ทp!ฐฆฤƒชO็tฑชตซoดถn`ยD…œ;E,�pA(QฃษkF”บR &{ol?“s@!๙ ,#‹!๙ ,#‹!๙ ,นz �ิ@ฐ A  „๐ฏกC‡œจ0!ร‡#œhฐโBŒ%r,่ภE�4IาใI*Y,๙cL @€ B…†8(H ABŠ5C ผ€๓8ใ�t๊แNตzสๆ EูPๅˆ‚๊!3่๐่�”ESฟ0I!&<c› $€สๆ#ยฑR/,่Eฬื” Qdูท†`6๖Žิhฆ‡m˜ ๑วUฅโ}jC–Œญ2‡ ™‹ิ๋,ุˆบ‚Hไ%ญ๏฿ เ๊…แฐื aว–=›vm�ทqปvscโgซP็tฑกช5ฌoถฒ�˜sง !€^Z๔hrฤY ฤ ๒�M๕.}!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,$Š�Hฐ มƒ&ิภกย‡#JœHฑขล‹3l่Pฃว CŠIฒคษ“(Sช\ษฒฅห—9ยœIณๆGŽl๊�ษณงฯŸ@ƒ ๚S&ั8nLšs!S‹L*ฤ9P)ษจSฃ65จ๕จืฏ`รŠKฆีฒ*ฑ:}Šฐ+Eญ[—vxVคฺถnน�Eหทฏ฿ฟ€ว|๊6๊… @€ @ˆ 6ๆภaqˆ $P‘€b˜0Qก‚$คHQ:ƒ DVทcํ�y ยล››๎^‰ธƒฆฺ[๗o‚ปๅ&ฬผน๓็ะk — ภ๐…(ฉ�๙ภ(„็;เŒ�sำร#e0k๘`mŽ†ช่งL ้นาีำˆu pขp$๓ qตU7pผฑฅWq :�‚ๆ4]VwœoF็แ‡ †(bUตฦPภU@�วo|Qม‚ิƒศƒ\#ฐœ0B*8ภŒ6ไมฬ| $lภBSD ?klเ&ฐ aรฏรC…`vX‡T=!„†ฉfr RทV]$.7โœtึiงXทฑภย,xขVฬ"อ $JB1l#Hก€’[T๙`แย1Tุฐ2Mค€$„pHภ ๅ4bๅืpaCอ�ยรŸkZHแ›p�ฆ\™ี๊+qiฮ…ซฐสy็ฑศ&ซlZZ†G;ํเใFHฌ˜|ูH์ฃCฐaฯ.4„&๚$ขล2๖ญpK:š„ั &$Pย#เคS ?ก0ไภ.›๐ภช6T๛๋‚ร‹\‚mบQ…ฦฅkฤl๎ชpฑsHqฦหv์๑ว วษิฌฤa ‘ญq€ญถ q๋-ธโ2แภ1pไ๒‰4tะยBฝ hกI?ฦุW@*เิร๖Hr™ƒœsฤ"ดภ`‚z3ุ0ว[ฬฑื [5qpม:lๆ˜ƒ๑๒ฺlท]gmj€มp*ฬ ˆn�3Pฃา๘ภฅ3ำD0‹9Ÿ„ภมŸbย1ญ๐`ภ.ฅผ0มั8ญมQXG4\–5ย[gฝฑQpŠฌ0aฃ‹1รdn%gลnืn๛ํeภ�H๓bo@ใ57ๆศภO!L!„`ว_<แE3ู pษ<|,‘E9ŠtŒb=R�บึ`w…prjฟnฆต๋งNzฤ ท!๎�๗๏?PY; v๊แ|˜E ๐ƒ6�1Ž9 (w ว…|pAุ2ถCLฐ ˆ&าแwtใโใภj€ /q ~er��ย†•ูฑELฑkูฦN— [ตo‡ชS]S�๖๗ฟ"๑ˆ+˜a4ฐ€ฯˆ@—!ˆ  ›จ@ `ภ9เ$`1Oผ Lฃ€ล\ั1”‰ภ1๔3ฺ&~๙cZ–ณ1ŽฒsฃsH?ฅจอ†2Dข IH2W-�c้š๒)…t@ ไรˆก‹๔l$+ Z SW•�‘mlKฏเ8!RrEc1Aๅ`F •Bบ๒•ฐ%'M‚ฆ„Pะ้ลH๐ศดœ๎(v,›"cIฬb“˜œศ#ƒ— q˜>1%D’yฬjZ๓šุฬฆ6ทษอnz๓›เ ง8วIฮrš๓œ่Lง:ืษฮvบ๓๐Œง<หI ˜€9LbฬH�ล}2F”ฑŒw<ณ™๘S"MiN“šีดๆ5Uผ"๊4P<เh&Vณ  ๓งD;ะQ/6ด3ž๙ฬ{9ฯ–บ๔ฅ)๑ž`‚fBD€ู)`wBะ๐0<ๆAฯ@ูใ๘Tร๔ฑข๒ณŸ� @J 7฿4` น๐‡6NQ pฬ!Vบƒ|”1Tซงเ๘)ŠยZz5๘NZวƒ"˜ฺ๕ฎxลศ@แZ”Aซฑ้AVิขลhF5*�5๘ภ๙HB"’‘�ภ%1ษIP’•ฌ„%-q <,๔!‡<`:B๐ Mdahaม‹ฃล�"ตฎ…ญlik<ไ๕ทภ .AdCƒGไ™(‚` จA๊P‰Z ๕จHMชR—สิฆ:๕ฉPชTงJีชZ๕ชXอŠ)ข�‡€ะŒd}jฌ\ะ bœ บyฃฎโ๛‚๙ึ๗พŠสฏฃ|เQแ๘ภ/ญ็ะฐrม.ศห"35$\ใ*ืนาตฎvฝ+^๓ชืฝ๒ตฏ~iเ_Xม˜$�cฝํ}ฏjะ 6Rฆ ฬ@A๖ฑš‹ฦ6ๆŽuฬใ ‹๋Nฒ’฿IเกฉbAม2แTุศ๖ฐฮtฦ3ŸMhD3า”ฦ4ง…jRฃšีฐฦ$แ�L`ฏ{ัWƒkฐ๑0C�๑ท4€แ#ฐะฦศณทฐŒไ%๚ะใ<ภŒQc�I,e๎,๎–ทฝ๕Mบ€�+g8ฤ)ŽqŸz\ไ@9แaNsœ๓่Dว™ฅO�ข‡ิษเ‚#ˆ.ฃ@8�2ภึธึ๕tyํƒ"๚ุศฦ&nq !p†— ู]๏~๗† OฑŒ @$–ื<}@Ozิณ๖x =๎y|โ#Ÿ๙ะง>ปX 1{๗j[��าศFตอ6n_�ุ��พMถภNLล-๗!8% จ@:06œ`น€ nะ�`GXยขq…-ไม ฃย€,ไƒ/�ค`>šVkธ๕ )ภLOฺ@๑ตผ�pƒ ฑ:ผฎบะ‘HŠฤต‰`„"OUJEู\1‹[์โ!F’Q @ืุฦค0ภŠดัิEฐ |TXMuDJล \ภคชA)�3๔บ��LH/#9ษJ“๙ิ3e˜๗ป�๐พe.w้›๑พ๗ศ}-๕ฑ๖1?๓ษ�๊—��๛ษพ๚ฟ—พ๖Q&‡Mิtดw��ุด๛ํ›ฟ๔�ึ๗ซ/~’ ็๊ฉ}™์Oุ—ฟ�ัฬ๚ฟ�lลื:๛7€ขg6ยคDพ‚+่ตGD๘€‡—}ศฑ€ุิ1~๏็€ธ ทdด ‚ ˜"p$(‚)ธQ'XF$xE,}ซฤJXƒฝท# �ฐƒ๒๐๒เง ƒ=8„๕„;่ƒ>„Bˆ„๒P„:ศƒIh ิ"M6X…V˜‡Oฐ…\๘; …]ธ…_†b†a8†dจอd€Wุ†nX‡qำLอิx<ืrุ8P‡"y8‡y๓†‚8ˆ„Xˆ†xˆˆ˜ˆŠธˆŒุˆŽ�๘ˆ…”‡<‡ฒ‘ตR"ี$ลpงv)eFšXR pR QO๗‰จ่yภO@g8p ๛ก ี@s๐hฅV$ๅbๅV_WนHWปุ‹oW!S4eyฉธŒBWO pu@ �๐ €Oม^]`๛ภ‡ฐ |"๏p๓–[�ถ[๘ึ[ใศoaEฑW}๕WลŒ๒HpM๖ึP ‚@ ๕w!gY k@ภ J ๘(`ย_�e_๘5ly ๒E_ฤ๐AG\ฦ…\ส5‰l ะ ๔0ฐ2ส8�ˆ>์ๅ=ƒ†}d"'d9ถck@h*ycฬP6PŠฐ` �๖`–‘Ws!ps9ทs=GsT›ผ่rท’)’ฆ9œ†vฃฑ‡ ะ ลtแv(wฬIRo7ŠqงEGœุ‰h !x  (7q��Y~ูYžๆhž๊)\่IM๋๙ž์I |2Ÿ|D…๐™Ÿ๚นŸ�ูŸ�๙Ÿ : ส6๖W:ฟ4๖็€ไY ๚?ิt0ศI๘๙ Z;ด3 0u`$ฅ‘‚Wดก!(Z ช" ‚$zœ?tก.z;*.PˆเฐK@ฃP(A3Zฃ7šฃGุƒ<ชฃ=hฃE`ฏำ /บคwBDภdฅa&0Q:ฅQzฅQ๚™-สค`ฺ1๎9๕ไf๊'เpฆg*@lŠ"jสฆf๊ฆpzฆ(Bw #aฺง;q:บƒ๘P1คG๘$จ‰�Zจ‡:จ;ธจZจEเ_๊ง˜ฺค‚เZจ…X #p4€O๐ฉVะpZhชX@'@ชฆŠชชสชOเชB`5;IŸ™ฺซษ2K�๙)"(x•ื8€ sธ7pฌPฌŸrฌrำ8สš" โซึŠ;มJ‡แญำr๓ญพ1‡แ‰ฌUๅืzฎš�šIQxŸx–วง่:ฏ๔Zฏ๖zฏ๘šฏ๚บฏ�ฺฏ�๚ฏฺWp> œ{ฐG:ฐi€ฐ๋Jpพ WCš{ฑทS‚เ E ง๛ฑIิž๒ PCp ›ฒ(!ฒ# \b (ซฒ2k,+ƒ�QxG`ฐ3�ณoมฒ#A3€”๋ณF๋{ํ)0ด€ ๔rดP๛ณ5ซด#@หpคQ›ตฒด ๘G6้yP.Œฅตd›6�็Gc’Hh�ฒ!]ณeทžค?Kภ๓ภ<+ท|[p๗ };ธฮฑท„{ธˆ›ธŠปธŒ�ธnศ+ซxYR“Ž[น3a‡ม^Dbธ–�น แ R p๋นค[ฒZ:ปซฅปบ—gคa€คฌ;ปŸ๋&@>ษ๐ –Jปผซern—ป %E�ปฦซ€q.`g”{ผฮ๛0ผ‚/;บฯ[ฝ๒3แ2…fฝ�;?ม7�ภ๋Xผ�{ผVq9ฤ ,@พๅ�ป{dภ Tํ�พ˜ .ภ „@-๕[ฟ-ำHiูฟ<ภ\ภ|ภ<ฆqงฑ์›N'Œภ�z|fๅะ๎มนฑ†Pฤมผฏ *ด๏  ๐๐~be‡ืฐ ห0๔;ย [กP—ภ B`0˜ƒฃ 4€\3Lร‹Ÿฅกญ’^ภ `Pฤฑ;๗ AD >F�ฐบ‡ˆโ ‡`)€ž P, 9@@I–”ลKDp€@qES๐00มq{ ๘tPm์ฦ;ฆf *อ4F๑�8๐๖„–{^Jศ�jรก‡”ŒธJšษซภœ์ธž�ษŒสแ(H‚#ขฌวขŒฏ˜'ฏ8๘„Lจ„L่ƒ๕p@ฌ�ส๗ส€‘…d่…fุ…;Pyบ<ธqx zศ‡Q(ล,ทxy๘Šอ์ณk P ยฑŠ/" ำ|ดซต„ะฌ,์ƒะ ŒีL‘;น�|ฤUp „๐;uƒฐ`ภ™[c6€อ๋<ฏ+`�ขขHŠŸ๘O<'7 ฑฃฦบิ�ฯ�Jw๐bํฦVพWrฅ‹ Q ฤฎra9{ช ั๙ส0ฦm”™ๅ8[็x™�9ฃ-ฏป$�ํฏ ำC+ ‘&I` ก=^‚‡ธซป~Yำ๖๊/ป€ 9@+1ษ’D–”}ฦฺ6 _ยE ผฤ‹ิ๚zำK}^ Qgwๆ”DfaQy!– >Pสหผ\อฏ'อึโkภv—˜Fl�qP ฅะิํ:ฝqฝฏ@่clใXoๅ๘o๙p‡Acfภˆท�;ุ๗ฺ7_ผ2psสs�1s?ทwkŒ ^ พโฯ–=ฏ3ฒํ๛ผูำ‰vž8wฝ$ฐ๛€X<่ซพนœฺLŠฌ�นญZa%p ๎a‡ฟ™rส!˜ส$h‚ะ ‚า‚ิํ‚ัฝ‚ู}ขฝ�ํ�Vศ \ะฌRv[ & ู ŒฟGbฒ<„ด<„ถl„P๘ƒFH฿Mh฿ณœ฿L่„A…� �Žˆฬjโ“ฏŠรมฬ…hx† ^†ฟ�เยแภ�หjHเฉXC๖ษฎ๙คB‡aVfฅฬแ‡ศ�H$>&>‡{˜เ%Žšโฺฌฒซแ}kŠMใqKh๐1‡ใqปše&๐HKฐสx++9pไ๋ใ�ส �ภ5…< �ฐั�˜’ หฐP.นw !โฏ๐ m ;ฑ[ึึ ๊ห7Kฑจๆศ็> ๖,หฑจ=แภ�ฑ฿�m็ค็C 2'>$รs&หF.๐ภ*>้ญ่สว่bmฌเดฝ$ุA๊a๊œ~็๎gณa%จเ ์ˆณ:+Xo=-&๋;›๊ษ‡ฌ๎Vย ชภŽBKดู Zm,5์k่บ�yอ๗!jœ*zบดM๛ด€ปขp5AํNปsอพ{žภฃ…โฌๆU{ตึH๎๛าผดV‹ต฿�้ฌCส}Qv0`ถๆ์[€บ"m�>ถ๓๎zcnษ๗๎O๖ฤแถ^าะณ` 9 Œฟ้?zฐีฝื ‚ST9Pณ"ฐ๚ภ+\w›ท™หผส˜๒z{๑ช็9�เ๘ฝ„Dh .๛K7"Pr@ฟv๘ท๋pต %-ธ‚ ๓ฉ็หdHแ>มภ๓=?1#-๐#�ฎมฯNyซ๘‡t่โมฬ[[Ÿผœคh?|kฏ๖›�๕า‡๐›ไr๗เ�๖9D๗xo๗ำ—็ฒ1ูผ7�f˜L๘ฏผG€ส๗ž—๘ฝž๗€ฟ–‚ฟ็pๆ ๘๐ว๖lห๘CGๆqC‡ ™B+R๙,แtกๆเ< ขOนB|œ฿y๖n1พ๘คO๘-‘†` -เKeะ๚’ฯGnฟ๗ณŸlศ1ท_�ุซ๛ูlŒาโw˜ผาiิ"แBฐ �@ะ๗ฉ™ˆŸ๘H1ษO๛ห}ฎ?๚Q๚…Ÿผz2�‚ฅ'|r๒0�zR�{า'ักภ•yLถค[e<„6*64hxx๑bฤŒ#jฤศQไH’%MžD™RๅJ–-]พ„SๆLš5m�ฤ™S็Nž=}�:dศ™ hาŒสQˆ’�c1!y�๚ษแเย<ํฺแs“ใ€ฉUฏf�ฺ๕kXH่9Y„mฯxGP&ญธ๑จัŽC!๚ X๐`ย… FœX๑bฦ๗ี[ิ่Eผ< 1มŠ)Sึ qxกร Sย Iั,gฯ ตŽ.}:๔�โผสC ‘4Mโต0ำแรGž\๙rๆอ?‡�“ฏฬกz5Lฌั)ิˆ0|เ;�C c!z/�wy*œxJ•ZฐlๅP�›bpศึW.@$ฐ@Dฐ9ใ^ชŽ(เดร,ฐ *›?Zˆ‹NบN)�๖โ ภฟLฐDOD1EW<ฎม˜D|8ฝ.ใ.จฑ๖ุฆˆ€H‡ B(‰29๒ ฦ=b1I%—dฒI'Yt&#ฑ๛pปฬ€๋L0AR๘1ศ๋๖)ฤ้>B๒I5ืdณM7฿�ฉA9็ŒR"๕ฒr0๑ ส$!ณ#’Nกฮ„ณPCE4�Q(้dtNŽd�ˆฦ+“๛ำ†2ฝ42E7ๅดSO?UฌัF‰ษ!Œˆz๎T๐L:U8A…5VYgฅต%9I ดV]wๅตW_6Xa‡%ถXcE6Ye—eVน<›…6ZiงUVRjฏล6[m฿4ำฃgท7\qวฐHฃพ%7]uืe๗าŽะ|wฏxใUk�ล7_}ฑญSฤ~๋ี�}&ธเ`]ta€๑Œะ`‡†V…'žlแHŽ8c7fsฒฟๆ๑ิๅU`ŽOF9ๅO15Ye—_†YอV]ผ7f›oฦล™l9gŸz9T3zจ็ F:้ฦjVบiงŸ†:jฉงฆบ�jซฏฦ:kญทๆบkฏฟMM#4lฐฯF[ั3ห>ฌlถำ†;๎&ืN3ิบ!“;oฝ—ค{lฤ�พ{oม7ฐ๏ก��mยg\่บ' pฟงผ๒ฦ ฟ<๑ภ-็ผ๓ศ฿�0ษ๗ผtำyยœฑัOgฝ๕œR_luืgงAะ']s�k็ฝ๗#Q…|0ู}'พxุํ๖;๘โ—/�xฒu'y้›บ�งวพ�ะณ็พ๛ย”๗>|๑q"q|๓ฯท |๔ืgฟ}๗฿ฟiO๘็งฟป๚๏ว?�๗็ฟ��`8@ะ€D`ธ@6ะ„`%8A Vะ‚ฤ`5ธAvะ�ƒl t`NpHŠ! M(“ 9@I ฌQ•i๔ˆ5�\tเ„ ขp…~่b…($ข๘XC6ŸH €b>xศ&@เ$Xbล๘ Pภ$PIH `𐀅 ?jdฃแX HŒ H€ Tะงˆl`}์@ะAโฑHA โ8ว:�qŒไC๐ ˆ€K€ฤม5�  R‘k„@)C‚๏เ5Fภ Wฌ%h€–๚Il@ ุe )ศ‡L(" €/“,จTe)H\€™!�0yฦ8@ภแ &ˆโHเ‡e๘ฃสpƒ *ร รw$ง9ัฉฮt,A na‹d#0*�๙kdBใิF:๊ั7H’๘p'<บะ†>ไ†จ†?๚ก %˜`ศ๐J;๊_ˆฆนH‡?่๑œP•ธb!= ŠZ†q$˜B*RšDคภธC5ฮ9aย-า{่Qœเึ๐ว;ฦQ„๏œ4ฅ+mฉ Mกpเฺ(BVถIภ€ยด(ƒ"ฮ‡ภ็PC:ั0„ภ=x‚+jฺึทฦuฎa๐กšมˆˆ";0I*0Z ภ�.6มทžCHH…>ภ€ปๆuฏจ์e3ปYไเยเภx Fh!ภ„ q]๘๐€ >๐H/ญaDKS[–dBFภ†1�08|!ภƒ>6‘Mะตl8‡@ƒ ๔เi€ฎ=๑ียเ!ทอํn{€HdwH†(<0ึ.’ศ82Aี’ธr/`ภzA |�“ธ.}%€LŒ–€ ๙8…>ฐะ4�tฝp,d1Y�พ€+ธG".ภ ŒเภmM๐‡CฬขTHมwH@‚jบ zศ ู <เ!ุE+๐Z˜n"�ฌbG�เ Pภ@ @ Hc”โ Xภ€ @#$! ภ ๆ!…ญฬ๘;=�qs€*์ ม>โภV๚๚ฏŒ@ร>สQˆ•#nญแVฺภŒ"ุษล4Mhhqุ(…่ผุฦ>vฆ์+y๐�h(ย‡‡vฉ%jศiOs`ท๐‡&ยะ$6ึƒ‘7ภB๏@6Zadเ๙! 6 m๖‘Fค7ๆAWƒ(ไƒšK]*ะƒ[คZhA Y๋[็zื8‡"†ธ)ฒฮค@๐ะŒr4‚6ะfD�ซ่ บะภญฆ]่๎ึ็ฐ‹qGเ �U่๘าึ๔Nะ้Uโ{� gธ4 …๊ฃฃฒํNฃA‚5v�ึ"าฤIษ็@<ึุืˆ๕ขcะ�ผโบษ†สจ# BชC๘iว?๒oW@ั0@$bznเ ฦจ‡rYkny_"/hpA`{`ฐ€มIเสซg}๋๘<(Œ…e,ยHษผ๖ ำ�@ฤ/0๗ฐฯ๏ภพ‚cŒ็ร fฌ๐ebฦจลศ€JHC๑h์‹'A ˆ=Šุภ `Šค ฬSฎ2€ฐ๕œเ }@a 4€3ฆ=$มxว“�๒’?vด 2=€ธล)„ )Sฝ๊๖HD ั8€ฑซtธ"�๐†ฑ_๚าง�*๓p kแ€b�9ฐl˜โ W`ยGwEฌrฤุื>๗�^�ุa_x/hL0ฒศ2=ƒ7P8VX†7เ‚2#ƒฎ}„9x;šต<ภ@xƒ9จ0‘จ€ `?ๅ๚‚.๐@่’.๊ขซุ€ ๙ศจ€=„/ธ;ถ 8_จƒŒภถ…m0(>ใ๛Ÿเ: Wข‡yจp„~"่„wxS€&tB(LX‚|จ Y€N˜‡!ˆ7ŽX?ศจw`จ�X€T†xnศ„w *ดB,|ร8œร:๔€=@PฉLภ'ีh€.(ง|0…wยC…า†?B ˆRฤ–บรTˆ*SBลส^€ช+LนปkXจt๊*ั‘(่X€=ศ(ะ/๓*ฬจzxD)l ~ะั0B๊*–p%a`ŠˆMJฦa„ค˜€ %8%g‰ฐ=fาฃูXAฺ>’€Dา$T:ฦaz2nดซ_b&RZ%ีจ€\zt M"Fุ9pวod&ๆ3 \RLzฒ ๘ฅ` ุ’.ศuดฦeดGNBHX9{H%์Ešทp’Ÿ‰ไ�m‚ศ‹|็[๒˜ฦ์ศŽtŒญ ‘�H—8€ะZ๘…A: ศoŒศฒH”จIq™/’ˆfจM‚€Hpพ•ศษ™DJDมX‡` pˆ3๛< kxUƒpˆˆษ–Xสฆ€งLสฑt ฆ„ ุ'ศ€ด$‰`'hK‚aˆด„%0I“0Kด$ตœKฒค/Cส€ €F'กฐUะJ฿,ˆฟ,‰…g8‚๏ „o๘)(LR€%ฬ“Hฬล|ˆฦ|ฬŒฬ ๚'D(ฐ)หb๘3€ ThฝŒˆHƒg  ๐h๘�r „ pn ๘@ะdุ”MฺดM�}ˆp}f*ฆ ฃ`บSYขฅUบˆVาด;ฝˆจ€ภsG?M 8vK$9ษSTR=}๙์ชk I ค.DฅNโ’I ฆ5ี€hT‹/ุ+๐—ก ฅจฯuVo „x ˆ;€๏“3(h€o oHPUo๐npึgเ„ส‰^�ี`ึb=ึdีP4�šศ…–||`„Ÿ๒ƒ[จ‡9`D๚a <๕€wืy}งะ{ชจšชz…ฏภ‡u”ฒล(ผน;0.‰�@ƒ\Pฉv  ุ\เ‡F่ิูŠ(H…w๐v]ฅธp€qpƒฬ{ehฉ‹ จ9ฐ‹สจ๊จubXmˆยuQ€ฦ€„2๔Ž[อUŠั! „U๘`VuX‡Xp" ‚0๐#I๕ pjธา+E‡ฑEj€†4ะส’�ฆ}Zจ•ZชตZฌญJsอ๐`ุด•„ลb_ƒ1f0ƒp-๐„บพ๕[ภ�5ร๑"/8เR + ภ %=(ƒ)ˆHเ‡5แช)‰-ฒฤ;จ@เ2ภ z˜.ะูงเbhฎ7๘‚ ˜�AจDƒAธ†‡ X8hpfx"ำB-ีbญ `ฮ��ะ•;M€…!ฐxผตU\ต‚Te”jย€ Rh�XฐNฺR!PึyฌHƒa@‡ฏuvˆv`‡ฒ�2# ๓E_๕V๖u_€฿rฅ�ฌฑ[ผ•Q๔\,Wภ€ๆ%พjข<ฤี€žเ f ณ1;=<%€ุ‡5ธ€ ภ `€m„†;9Gีˆ^ปฺ;˜ฑ03 ฒM€€F˜U91€YฐผXงm่ฝดหน-จ†€u๒ณE@†&ˆ1 ฃ  €~แv“ป-ธ.ฐ�ˆ-ฐ�๐‚{๒•ย�RXo๘€เ xJGIคhธ_u`‡�ๅ_v J8€H:ถc<ึc>๖ใฯ\`ธ€ปอ�vใ�Z๒ฝศใ‹ฐแ ž%O�=ค6kรถ|%€8aHxpH‡jเIแˆญแำี€ถ่|DA,ร€9า�"n5&@ฝh‹}P[e{8 Mะ‡Dะ‚e่จSKUc5H€˜ๅZๆ‡Pธ,0c%€ท^N{`X˜๓‚ohRh(ฆิZy�gN8ไ๎_oHผN6ๅK๔ตgfส็}†ฮK�š�ฮ[ุ�R^ฅSe^๎ไŒๅ  ž๓นqบ(แW‰Hp(ะ‚y ุุ–c e_ฎ‚ๅ%f=xˆcถ�d~]fFg†fxv€c€ƒ\๘i ฮ็‰ซธŽRipจ˜�[%€[ZปTmๅ_ X‹ษ †€ ศ€0…ล|ฯ#…XX(o@‡D๎_h>ฐ๋/P ๅˆด^k˜MธM‰Ž›—2‡8ฯF่่ะh{‰„วžฑษ>e�ร=เ๊บBฅ`Hศฒ2N“ @Bฏ“j3ณ—สปผ @m8„า๋1.กฒP(พvƒF�#žax ƒส&†ˆด“๐^g˜†˜s๘$ผม+<0€]8ํผรe ˜€(ฐ8ˆ†@ แ๘,xwšgเ„๚ค†oVะ1ม€ะ…@‚่�ๅ฿ฝf‡ฌ์]ภK.๔Vo๖v๏<ึ1ฦNค-…,ฐ\ษฦhสehถิlPฆดAL เ€N†ฟๅพธ„yเƒ%ศ‚r๐ด-ญ ์@ฐU< @ œ€  |8…9;p‚ษ‹ฎ๏} โกฎŽ "ihฎีผ!Aศพแฝ>˜ผHเ‡B€IะB0!�@$@W2qWq ฐQฐ{ ณ�ก>€Tภ๐ฎiQมG"ˆƒg๘w๘†FH๓Œ๐๕�bHไgๅ_nHo>/ฬ/MK8—s:ทs<ทC?›^สƒO์†9`1Wเึ�d0จT•DM8YS` ภtี�tƒšWDศ๐๐ญ0‚@ผB9ŒM€ช…๚ƒNล€ภฤ+� IคDSะค‹5งn๐DAœืญŽ‘M…zเ@(yƒDJ€; ‡!`€(.ะ€? ฤAฤงOO)6ฬu$ผ€๘ะdำ0„|pƒ~.เ็๘&อ๏ภัดtˆ>๘j †M`0ฐj@‡bจฬˆ z�{วw}‡๔�ณูiONาฦC‚ ม2ศW฿”#R2ค>’็ฯMB“6&H�G‡vษIี_izี๐�<’E_Uฐ#O€ุSฤ‰P…ะ๘ฬ:S๕sLฅšงD}SbBB ฒX `$๐€ม€!เgฐGx ศัภม๕}†>x0x n(†3˜�”่๚ฏ๛ฑ๘ธQxฑ›ฑš!Š่q>Wq—๑ ฒŸP๘�0—&]ฐDxa’ภฤ>3xLีะั%^NGpธJ3xpƒb(็Kห�ฬDฯ{ม)ŸLมNEูvkHQ‚ฟ �ขH$๒6#แ�>๐cr˜ƒ8&){[แ“๐�ื‡�–P U`•เ ะZณ๙ˆ๘€!H„QZฺ๘ถ$ม�ู�๓๗‰ู้‘‘�๐P๏R๒๛ๅ๛ๆ7๔G เO’]ฝัฑ]ฒ (h๐เม *ิ€๐!ฤˆ'Rฌh๑"ฦŒ7r์่๑#ศ"G’,i๒$ส”*Wฒฉ€ร–2+6$Xsbอœ -ๆ„ูp&ะ B‡-j๔(าคJ�4H@‡=NBะ|ู๑ย…ปJมดxภ•6—^ดšQgฯ‡hืฦฤษv'ูธr็าญk๗.�ผnอ pฅŸฎฉU zฤZ0�Œ.ฌ้ฑฎ�Ÿ฿BNHนฉฤหm๕r๎์๙3่ะขQR `‚ฤ[˜ x !C *(€ฅ…†ช@p �DA H„H `Cภ1$B�$0aย8ŽknDˆPฑWณU๐ส%๓�~9ข๘๒ ลn๏�=�๘๒z€๓ฤฤใท �,๓WO™ฒล`อ?๘ใAr์ื฿VC7จฐB ทุ’‚r•@เ?ยไpAR �(ฃ„ ำ‹๕เ๓ ˆ่ฅทำŒ์ D5f™๓๙๘#A ้ใ ๘CK"คB~9 `ษ9jdัI?a„เAOธโJ!๔d”�SVูA:4รH ขDณƒ kEไA xrVy0๓_ภม pั !`ฑq2๊x(Z๋U–cีhจfCJ:)ฅ•ZZT ะ๐H>เdRD“5 pษ4/0๐A/ฤLธมชp้ฅฃ–zjช„๙œข –(DX=t‡ิB“@ธะŒ9ะn"zํK’๑D™y็u›แฅแŠ;.นๅ2Zฺh์SN&pเค%ำ๐€Uฬaซฎv๙eผ๓^P๏ฝL0 6ฅ๘€Oเ๒tม ท I-ะฆฯ๊มรXŒ๎˜pLุ’7žz฿bฆqz"kk.ส)ซผ๒h�Qะอะ 6\เ$ฉ�Œoซฏ~™3ฝ๖ฺ0Aต`sฮŠฆU– , …&�ƒข@ @‹ฑC›๑ธYxŸ2E?ฌึฃๆqผ1หiซฝ6�Dp…1๕๓Ÿ โœอ 0€‹ชฬz0 ,0œFยจx๋อ7 Š1X,ณˆ -ภ*6E‘˜c<๐€pL+{� /,ซ๕ึ^ึุasK๖Œ7บู6ํต�~;G�rŠ*ค0ํZฃฺ“H๔q™ำq "nผA๘%ร|—yƒฌื�แฐฑ@"cน{|๑„อ`‚๕ป˜ฃวox™ุ๊*ถ์� งŽฃ๋lแ�?xปp TbSศจ่1z€Jะ‰wผร0Hเเภ,!u๐า:1!�Lั„? ย‚ ์)คG&&”œtaศ‡)ๆg-‡๗ใˆ�่ืร๎ๅ๏d<"“. 1[•0D œ%)d5€@ TEd เ:)๏ฬN":,6 €ใ@€;]JNZ๓.€*9jb�x2ฐ๕oi} โถLจฤAฒ๒ม_Ddล„%Œ4ˆ"Y9=ั@็โ‚Hƒtฬ ฑI๋Hศm ‘eณ–!SฉJตEา"ญ�Ÿ ]ยว��Q#F4WzภJฎฒ—พ�ๅสฒถษ’ “lœ\ศข6Rฬ๙P‹>8%(A RP" X’ภ6D`L&8ร)NU.s‡$)g|:ภ‚4ƒไp:พb„ย2ะt`€ |sœ��'|ฐา9j… X’$Ibะ%ั.kYh Zั„ฮ…ZL ;โแบcx†#Vม 6�  @SชRะ v่RPD0 %€ ๐ฃฤ์a:]‰LiJQนจ“ไะh<4J 6\"ร 9:L Ÿ+ฝ*V•๒•ฐค#`…ฯb‚ญ”‚ษะ“ไ…NŽU�$ญ>สSข"l:ู<�!/„M`@ย™0‚cfีศXทrุ‚œ* CxdZ๋‘ธJลŽ%แA!2šTŽฦƒิุฌ;�AŠ!x'ซฆ=-Qำ„yฌ๛๋Œท1-Š\๋-๒J ๘0sํˆjC‘ศ ˜mFเ…ยa ปร †ถŒํ`ขศ๔60–% Šฑู๑บชไ๕†DภKิฒทฝฑขk$ภPX*๐ސ/pGฌล(`_�Gฟฯy V €+�77�Tภ‚ ปํd‹SŒ(ŽS๎ti€0ƒ}’ศKD๑ย�i@6็ฦ$Xn1ƒ๑‘ู@๘6>1oโ;฿๚J@ึมŽv๚KญL]‘ร!่�n=๐ีWM๒ 61}ฅsfถZcig“0ว99`ํเ}„!ฦ–/†#฿ืฤ†ฦ ฐqตึ�›ู‡8ฦนฏ•า> cฃก�3g9บQG๘เป๎=4ข้๋mคฃ�x†pp๘cn –ŠXิ|ิใม(ฎ๑ŽJฯ!ุะ?:๔! หทจฉด�ม…๕ฐ4ตš2*}่รัึM r‘ะใถฺภ a ผc B]A8:๐ฤhศ…?่ม[qภhx�58axภีฐว4cฟH�Šfดฏ1,ขj๘รD(สt‹^tS ;�ฦถฃ*�ƒฬZ_v$ึท(n„‰h�ธล5ช ‘ย’@N@2 Zี ๒j-6ศใZผฏใ็่๐ว?ฺ็–7H@šP….$ Pโ๑P)�๑ RจCฃDญ:6ZŒค%บ้‡“ }€ก#D=1†A\ƒ<๐ำ55(Vฑยoมภ8ษIญhญ/ฦ0P0ร 9๖ฑ‰,\a˜ฐฎ/> ก๛ฐ‚Fะ…/@ุ๐:�ํAˆ'่ ›๑�ŒฏฺแˆเิวƒฐaxK๐ƒ>Q ธ"๎sฏ{V>I,i‰ษ€บิฉŽุ้\ะŸ�(A๊ำ‹o�ใy@•€ห๋ ^5xAHozิ7bภบึoXCGŸ ็๐มh@ด{คq๒D๓ว€๔=A‚:0ชม�๐๎~˜คD%+ั~K0ฝ=”่฿˜”ษ™คษšGDt@ะ���€94`~CัƒภP9ฆ”ฌผฌภ=$ยƒlƒ t ,N6๘@ณXL„ xี(ธ‚h€0&|ศ0ึx•+Œ‚`@ ฎOC)ผภ|ภ=�…�ฦ“เอ† ต`ภ์B+�� ฬƒ B�ํ]‚แL฿œ@ lƒd€ ภ ˜ƒ8ภ1T@ˆ‚1Œภ๔เกdั P@ฯ�_>a"t›ฑ K็,ห B‹ดPKHaATแ๊†�ฺ‘ยกาแ ๔ภ6ะม๏ @a๑€์@\ม<0มครDม<ไ ภVภ ์ร T4ก‚`"Dt ญจJไ‹ํ#ชƒญเŠฎ๐Š J ,`< ร*ฌย0$Uh)‚#|9�70ม(n 8๚Sda4( -vŸ=@XŒ(พ�’™ฺิB่|�I, Dkq ฬc่ิ้ไภ @9H,aL(9*B!ภ!hร; 6„N๔ภ-ค*hA |ˆD(Rะ @๔>๘ƒH๚รีภ5ิฌ๙>”รl€kํฃ๎!B# ๖KBv4Œ?@Œฤ  ;„71คCBd่D[,R"๔€IขคHฎ$  c8Rt‰"Dœ#=8ม"`รŒ<0�$:ล ค#$ๅฬMr_ด๕Kะ� MZ�‹ะ มL"Gะmิx�ๅ]ZเO…#_Š#Yž@9žใ>จ:ฒฃXอลภQ<†?bโใb6ฆIFK@$เIBฆBฒ@'C�XC่”†*4�œ�[zd?ิะ@l6 ๘c8AlŒ œะฃL"e1�e9v‰ำ@ิ HA ๆaอZg~fhๅํฅAฌeAุ&nา€n &aภTŽ" @>ฤม+ไ- ‚4,าtฆ_AะขXFq*ย�ญฆผค%ฯ$%Gบeภอั03R:$Uัแฅ€ชC๚ภ7๖ฅ‚ฎB~@9Ž  ,หโHCกไ@ผ`dง@€ๅ˜Ž†บcg4ร&แ”C#ไฮศKBNภ ,ƒ  แ<ž, ศ€=H‚Yย็ฌ,1ร”แ!๘˜† �แ'ผภ!’ูฝJcNฑŽเ,KƒglN็|NQZ(ˆยจŒ.K”@T‚4€่„u�$ะ ฮ†“*ห –ก ภ@ |็Ctภœ,€B04A-ภ‚-ฤ™~่ฒิั�™� คภ•สgN�์M฿จiเ ŽคŽ0.Nใ„ฯไc—4ฆD=!ค^้ฤฃ๎)+,รpVดฦํ |ม �มฐz!žรtม�ภะ>œยŒ8Aฒ>ุBŒ� A>๒ษ˜Fฤpภ๒4ฯ๓ญฬ๖d%!8ฌฐ+NVf2ศ�ฟ’ฅƒB๊ˆIๆ%ฟ2๓@ฯROX๖hpAม3ฐƒ78-5ย 8เ<ญีŠƒจ@‚ฦ*ื"ัคB0ด@ฦuC&ฬฯ~Pบฑ( ๕)™+๐ต|2d‚ x ฤ แะ ฝญ„€�า-ฒB:คC.๔C#JN|mุŽm&�Œฝผƒ6�A Hš6ศƒ?x $‡š๙�ฝี�„5ภต๙G7p �-ˆไ8pฐ@0๐BA�m�NDMPม€โŠํ -นตHย ัW ๙C�žฺ5ฺไbุ�ฆB:ด-UDP*0›)คน}›๊rn4ภ|n7ฬAš!Dณlotคƒ้Rล�ๆญ)ฐ@`…ธPต@๎2๎�้ึ%(ตฅYํRaP�rQEะzDค€ˆ7pƒ8PCผ€8 +๐3ย๔hืfฐ�™U€pPK}ฉ€˜>6€Š‰ภl@H€—8 ฌx„qG GW,ไ@ +@ผฬ= Oติo€Kณ�\˜ ุ…m’œ™†ัjoผุ€ุ€ฅ๐t„ kไ{IG q_ษู›ฦ™๐•1กYAฆฌŽ1X 4‹฿ื‰ฅ˜Ž}/(€u(˜ ุ@ห๐—๑พ6@L‘ €๑ใ™อWcัษ•šy‰QุฑD�%ˆƒ'?ร38'ˆฒ'3๐3ปhฐ*Rต4โ\žอฅNfD\ALŒ‚=4#rdาz0sฤ0W’พ’ฤPต๒03PpA03ํ’„2ฟ๒n ณ�๎BPา+D๘ภ€ฒ'Op8ƒ,ดหปฌ2:‹Snม:-€!ผC;ค�ร(˜zฆ๓\T๓Fๆฬ6[ฤผฐB8ด8๔œฦZู3Bป\ภ@mmB—‹ฒX˜๐A!˜คภkอาCwดG๔{D—�ื ๐@ผ€A{Hฏt…’Jฌ๓'%qด,ษ4Y E็ŒK๏4๎ธtJภ๔%ัtC uP�๔R๘O'๕ฺt _๘ดGต2๕O๕QMTSS›EV›“I”R‡5ห่d8๕YI๕SwuH˜๕�Q'ฑ๕ึD๕VoŒZณ๓Acu์ˆต^หGiค4a�ซ9™tˆฐฉYๆpภฝ"๛}‡ษF%1šiarƒๅฤปH�E%2GYŒQ,ฌ‘]วŽ๖�ะHํHSdH-)อj“ตท์ะxฬvjˆRฃ์5o‹F}�G“เ6VDA*ŒZ>0B—H8ฤฅqภ#(ƒญฤXรุภผ•ศ‰ˆฐีƒ68ะƒ€ƒ(„Q@‚,ถiร)T8x/ะZ5�‡๗‚ˆrWฺฅuภฌ�‰(รผ"ํถVฬk�[๘_ƒŒJรฒ�ไตnฯNษุvm๗ถ„ƒF‘I’ Z@๐1ภมผมะ*๖ืyภย Œmึ`c๔�๏ํ‰“ฒภ-G$๐รฬš&ภยุภผ0`MM่ƒX�,7ำ้ร�EMุ!๖แ*ไแ’g&€•Lฮต4๘[gyขฬ5k๘ฟv˜'๘˜3uค@๘™Oธš๋EฆlJง|Šy„ฬย™. J((คเTC ภA>` \ ,24AฒวPภf3€@ภฌmม5p ”่หค๙>H4Cd=Hมกำค ’  ข Œz†-@/ภ„x „ใ๖ก�—8˜ป‰€ƒy™yšŸ6�ฌนฐ฿…หP@บฌ‹9oy‰ฎฯBpgฐฎใณ&่C"hม2 HรคONL”ภ#€C:T?„‚@8ภ.–ค+อไD ม=p�บงƒบ๘ใลdNฝxคXJ฿ะ๛๘|ำฝศ:�˜นท�ฎ๏ญ#Sn9xY/|,๑ฏ�๋ ปฤห…หภL3จhอ�Œา,;๗:ชมณ3ยA.|‚4ะ›^วำDอิ˜@ค8ิL=ิQ.า,-–$ม=0AงzจOฆฮ0พwงพื6่๚ฤฟธW?8ม›นม๙ปั5ร“นร�บ–G๘ฤ}RผM�ฬM� ลœ ร 4#ยไ๙ ‚…๖ธ3LCฬ‚9|qฮ–CAภVD4๐จL@XDC ltCๆj€UŠ:พ๊๐อ’ žS�(ซ.ภzฯ#uุณ‡ถืh�ึ#<ฏทถKƒ>ูทพQ่๏๘๐ E�…4xx€ภุชึ•xก๒C!ภ$่!L…ภข๚ฤ%ฬ,A”ƒ"่ฦฉŒ‚5„๚พ้Dปฟป@œใปใ’w“›๋“—๘่I๙’ใืzภ# ฤ๏:(ผฃ �วฟ๊หด๚ป>�™w 7@คช€`‡ �h๓7nŽมw่ a ๖ ๓็^&!(ฤะภ฿ปn:เpก6`<8ะ0“ๆLY๒แ abพ&<�ฑVOู›!4ธSmaร@…่FCMซWฑ^%ธ•�ซฬฌ_ณz[“`ุฑVลขํZlฺณmืบฅน๕-Vทqๅึี€—o_ฟ<˜paร‡'Vผ˜qcว!Gฬaง� €`ˆ lV!"D‡ Tจhภ@…J lAi Dซะ‚r„czxฐฝสตn ซGoุ@ฎ E“๎ฐแs )่๊%{ทฏ๖ฏyฟƒ>v�๖ต{ร‚๗พ7n๚๒ู๋ใ…_r}๛๗๑็ืฟŸ���bo.�4ภƒฎฌ๊ภฆpะ‚]<@Š.แ๘š‰ช;>๚tŒรฒ(d ภE ŒฤรดฐB๓ธโPญ Al=iฌัฦq�ฬQวyŒ,ซ‰ฌIG”!HHฉว#‘D๒ล๎XDQป$กŒRส)ฉฌาJ_Lฉ4 ฏ์าห/[| ฬ1ษ,ำฬ3ัLSอ5ฟ„‹อ7แŒSฮ#)h ์ปเ ŠLO>”๓O ๚Lณฮ;็LLลMหD R.ƒ 8าK1ฝฑ81แQย.๐;<๓STR%๔ิRำ�ดำO3ฝฒ8q4ึ F@ค Rีื_๑ภ’i|Hฌ‚Xq…ีลธแ>ŽMvYษ๔\กBrธฌ1tฐ‡z=ภ*r (Ze{ๅชูg ฤซkไ๙g`U์�x็�ดฺks–ุตธ๕�ภ๖�ลV�1H 5=7›ถ?ƒฌ”้็eธP$–๐^y้�q‚ส‘€�6]ฦ๘ Œ!๊Vๆ4ธ$_ฤฮu1ผธ‡‰œW~LO2Ž!๗1ฎ่G—ทrม=l8hผx๖9fฬdภ”![ ๋ญ‹ํj่ข๛ต9du“^:่ธฦ6บฬwƒiฒGxaมา‡อฬdh cŠ8Šhiฅ™๖Zkฎuœ…’ \ํศX"Ÿ0fฎ�อš…A‚๋๐ต“๕ด_:ฯ!๔ไgถOyสรœๆ8ว=๏ฯHะy๏๐D&๏@ฬ‹๔š—ส4LB PX ข7ฝ๊)Pไ^Dpฒำ�ฤy��บโ€c๔#ี-X@๛ตฏ/!คž๕vบ  ๐duz@ 9w@๖%aไ  …ธภ๐ €๔KJฒ7='ƒํฃ@๗pำ?j-ขƒ NPŽF๐{S�"้>g฿e‘6๙ห 6P€ aศอt—€%&%4๋sƒธ’†™เ`–s�$jFqิ€ภS r‘Ž�!B8ม#ฌQ’q!}ืxAๆP›"q๐L]@p >งvฆlJi R ขฬ!ฟKวZ<4�เ@Dิผ2๐#”าSR๑ไƒž๙€!˜าe(ม8ธE=ดก‰jะcˆ5�axภH @F;เ‰z�uถdHiธหY�E•@\R๎Ž…ธม ฯŒๆ4ซูLj’œ”P?�)มe6“‘œd%{yฮtฎณ IG=บaฎ(“™ฮผ4ฅIMฯ(E ใ (กŒไึ˜ƒ ฎ™อmšภœ™ง%อ QT*˜ว�๖ฤVบrmqภ.คฐเ†9d้@ษ—œ๚C›�\@.~qบP3˜˜B*4™ZีL๚ฉQ`ธwยณ๒ฬ„9ัฉNvrขaซrมFฬ”/WuฉAะ๘ˆ\คดškร(�‘ ~ฌQฃz5Rb‹ะ•VฐZ J:‚ผข๘ภว?ไึ\๎2—๙€b5๙ŽLจ ฆฆศLตQ„˜<’ท๛ฉ™=ัHะ0$Aพ<`c–ภƒ=๑ภ@–/�€I` 9Pะfั[\€u`ษ#ภQž^๗ cุnwซMdA}๔ €ป?่ฃ มLภ�x0a&H?ˆ‡/ผแ ”ษ3�ภ…/ภ!‡@ธF ฑ>bEฐAžเ tฤA0ย5† ์Wฌภฎ{น๋]œ7ฝ&เง?#V ขˆร dษ#Xม 6ศq“ป�Y"ขใJ€L฿wธฎ,๑Dฬ*aCHH…>ภ@(‚@™ r‚�๓ุว@ž%`q‚4A`ิแ ็p,(รข ~ฌ(š€ลl0€Wผ„ เ/y๛"C8,9x-Dหๆ/zสณ„)L‚๗๑@q%ใžฐaŒ?LAม)จ<๘€„N์ร {K‰ะŒ@�จธธr–{ีdsMภ๕ศะ L8น+k๎๑ฆgI่CรHภ ‚๓t=bH›ฺึ~๗Šd+[ศF3ƒy#ƒ+์#A‚:฿๙ึ7 žA3lก‡)Œม ขฎฤ9|0ฒdˆ"˜ฝuxฐ.‘4`ธ ฦG0๐‚ `ญศA1)่i+#pล(.b็Jk€˜A)ˆ๐฿ณเŠช มzOžrfTฺ7ฅxAะ b /ปภ*ะQ#‡3Qด-x0ฬB มีIภขTpH 4ฃY ‡€ภ %ุ@ดชทž\ะ =ไ �ธจsE0U^ฎ ๆ7ฟ๑ผ$ƒmข$8(ฒแƒฉKใX/R2พ๑ŽหXุ]yzฤj&qŠ[œส�ตlณ`๗Hฏry‚,พ๑X7็เฏ~๘ฤoก1€C>ฐเ‚ขู`ศhยืร~ `@ X\#€ฒ…kpมธ:p—ร<�|มภSป‡y0แ†#<แ x์Vl[๏:๐C0ู‘zq’8–/@๔Q ขŽ๘่KŸˆฌr๎rง{๔9 <๔‚ วo์เ๊@ฮ| h&เ๛ O๖๎๖!ภ๎๊ฎฦ76แZๆ๚+pฤ:€(ะ]O๐์ �L`๊Z๏๊TขY๖a ๘$z๎็‚๎โBเ฿๘ [�‹`�ภ`d ฏๆแP1ๆ‡๔ค ˜lA�ฐกx zเาด r g:`j gBA ค.\๎ไ *ฤ ฑฐไ&8ภ Qภ์โbฐก๊a#๐กŠ€7.ภฎม r`'"Žฮ*.`nม4! Zเๆฦฎ์์ํš lW+โ8Rโ ห0bึฐ ฃ๒6f$ภ์แœoูธเจะ ฑPส p+เฎ–ฐ ™มร8Q� fdขA: vัcโ™-ี`ฬ๐๋�โ.4AA –›1"qญงาก๘!œj6”oํ๖$%ฺ%~dˆl@่เ†> fP[‘/๑ฟ๑ๆFญิAฺ๖A�‚mXHํŽ1๛โกo+†qZP ๚6b#ŽŠj๔‘€™ภ �๐ ขฎa๎Jอdฆใ๓F์D27๚!#�แจœ`f`ทBƒ2œะร@€๖i+i๎ม / ™า1~๑~’:a๊ €ภฐFวPแฦaDKZาo็ƒ๐Žภ ‘]เL8€๑@ \`-šกœ@h€ˆ �L`'๒`MA^Q.€N ๊ห”€*ั์่4q"{ๅ฿@้ึา3ฺ’ ฬ-Io�๔1?roR"= Žก*ฏ๒}1 โ2s0}ัl€QY2๔ๆeNเ=6ำRข4534๙‘ $->A่‡ฌgณำ ภกp` ์Aณณลุ’,5ภๅ6ก โ,.o&€ฆ-oภโˆ๎d‚+ฌ€ ะั@ฎnAN<ฅn„แdH 0€7ฒว@@เใŽJใ2๐บฏWชyIณ€ณงnฎท‚ฉš‹ [&5๒hs "ตs€K@;ฏiCJc…Tรซต6D$ภ(ฎoฺ7€ใ9ผŸ๐"ฐํค—*`sยŽท๕๙ Œ8ปช๙Zฑ๋D่:T`XCˆh;[+๛บ๏$]ข็ฐ3{tN›6t ยณใ‚ทOถfC|ใ‚:ถ๛~จy{๔[pvพ๋›^`†�€=ˆwะง:‰sฤG;]คอา 3•บร/ฤ/โฑ1ลฬ�t€า@A�cฤฉˆร_sQ๔ฤย VT|0ฦ“0&ฦฤัG^�$‘i-B|+๊ลH v<ฦ๋ใวืยHZฆA„ฐรX ษƒไ’|ศ�โjXœŠ#ห3„+Žห Cสงh+เด€~มr=|อฅคก;นฤ=ร€นัธ�?�ฮก„ฮkมฮ!ƒฯ��F€DHˆ$วู|Fฃ€๊A†เถ�ั„2ฺC,จC๎�?$}J๖„**ำqDK–า;�@๖DฬE�ิQ=ีU}ีYฝี๏ฃIC+ Cึๅ„ึฯึ]]Qpฝึ—$ื}�?z}0l]ฤƒM†ฝLŠ�ืc}ืใ„,�NDูก]2˜]F4 e˜FืๅK’�0l};Ž�=ไใ�ฑฝI�$;ฆ=LถฝJสฝ?ฮxฮ"ลwฤูล}�Ÿ=ฺE=�คฺำๆฺc�g฿—EะB�ท}�๛=ุษ�ไ/ ^�รใ?ฺ]แแขแ=แ#โํอตาGฝ+X;l Œพwdโใy*บ% ์b{Œฬ&@ ้cW�„ œ�[7สK-Œ‰๎คชw‡sV^J‡H(`9ยˆ-๘วXEƒ.`‘์ฺูฏGฒภwจBi๐~�Nผร�ป>Eบ}เ็#ลฟพใง�I๊ํŸdE�์อ#�ฝ�B,>๎ฟรํ็c์�ีร฿3�WDๅ �ส@๎„ฎฑbหิ ~ถ฿ยC'ฎ\…กAƒK็ิd้ิ/L‡:š1J!*ฺ$�˜ˆfถ๔Lใ„„OภxL@�QNR…ฌ|Š™™@~ฌPม6cฆภ๙’"}›ฒhŠX€อ9 hL่q/R{„ž|#แˆ._ เมฦ‡G€HS'|H&สƒO@šu1ดฯกe;Xz‡hฬ ki•2uqฤˆŽŒn๚4ฬมน=Vnl9aGสœ5l๎�YsๆฮคK›=Z4fฦง}ฎ�:๖�ืฉK7 {๖kู—wวฮอ;ธp�ด‹?Ž<น๒ๅฬ›;=บ๔ใ ะx”\ฆ".ƒpiฺ zcbรU* ?พ<1!ษwJ–$€kู 9\p *ฑไL2เส(`เ�3˜๐DูLพ€ƒ P S ,`žWฬsฤ้,ลปด’CU์ฦ2์Gw•ัีLYะ#ฬ(กD5‚๐w(ู๘๐a)/Œh� ลe)"‰็น—�hซ'›žyffmรฝูšqร๙gœtžv›wค�๙miึ‰ ฆMGhก†ŠhขŠš€ฦ>ๅ`”-4 ถมL6จว�d Y’้ฆฺ0ม“`SŠฐ(4C ภ/•ำศLฌDยตดb+e ™ศD•ี€pภง1D‘ƒ`ณว๐Q�AทคƒŠ-ไฐมขอ;ศ`C์1€ฃW์#EOœีฅค=M6ณD๛จฐaฯ54ฃะrŠB›aฦฌณ Ÿสlyบ–ฆ›{gฑฦpชy$›ลแู&Ÿม]L™œ๋ึ๑ž|z,๒ศ)ฯฌ›(ฃ์2ศ1/สsฯ>� ๔ข๖€G3ผฒ`ร—†ง้œz j{ Wชำง^U 6็ณG"1“–{ ๅบ๋‚ฟ$,ฑ”]@ภ``ฏg5\ใฌฉ1๔@OฏB "W:ช€จผ3ฮxะษ1u„5ํV H4D2คแปค.$™ล=kt4pม5`“0V7L��่[ํiล%{”ฬ5ohom šฑr$slr๎0ซ�หท็|๓๏วULณkฒ��ฑฮสkt๔าOO}๕p…1๕ล qฯ^6/4ภ.็‘ภ“ภƒ †?~๙cVi ห,bC !�.k€<ˆ-€J˜CWเ•ฏฺ“ถb๙ค8,@ั‚ะศC `พœเ j [๐ 0ะฑ์!‰ œ`‚0Pยฌ2hcB—klะ“บ@IJช’4ฐข๑`‹ก ˜.h>๔ธn6)[�วœˆ;&6Qx˜™ำ™ขHผ*>4พร™mŽW'�@๑MZ|ž๕ฮˆฦ4�ชฑ8ธล)„ ‚}|—ฐG"บ‰~ฤ!€ฎ7ผA%uผcใp<ƒ )ะย5�`ƒWก้kตผฐRda^ฝข ‡ฅ@ŸT`[สพะ…9๔g)Iฬฎ๕,„@๖ˆ/�เ๛Hฤไ‹:pก;ฒŠm€ 9จแ sุ .๘ฅLaX‰˜0ฑฏมfdD5ปฺ)‹แ$็อxE�๘ษœM\g๐ืงt’ xณำษUF�ญqŸ�์็ฯ๚hคำ€‡๓เ& R๐ ” ๏x‡)`0ะ‚‚ใ  XB>๊ฐฟtbCPฺ$WŽ9D๒�#ุ@เwtc้s/4‚ #*›k*^X#ฅฆHม™B:”`‚„LมJฐ๎ ีqฬa"? j=ˆšึŽภ3u*^%9ลAฬy๕|'>q+}zqž๙d'๓๒ูุูLžจAํ*Kุ0ฦฮ‹�์ฌg?K(h•๓;—†Pึ€@)PxL+ ค h€DภTG*˜]ะZKr €€x[. @`#ภ€šH(เฑmํ. €TW‚�€ฅL ˜ภPญqe�ีิฎ6ถHAGฺf4Tฒ๏x�ฃ€ŒทผซUBภ@์jฝชแซ€ท๋ฒyบ.ณ ^“cน(<*.Vฐ”�:�ษผั~ร˜•Y,–ฉk -ฑ‰Olู‰j”“*รFจไP9ดฦฎR<$ึฝ›—์ย #Xน๚๊๗e… @@ี{gŽหษ—ผ<-ผphlXlธต8ลZฃ]Xำcใ1Hหุ€๙ะ{แฒjส  & {e…ศhi™2Gส…˜ฎLฎ เ๗lTl\ญ1จ“H‰*bƒแ P8Qฎัั<�!โk‚8ฎ}!ฏโ{๘๑ๅCPจ @"T€_T„‚ $CไpะP ุ *ดเ€xA‚๗1ุึิWaƒ›!ิ@< A)˜pะy๗ๅ๗‚ .(‚Tณ…ะ@ VŸไ!ˆ"’˜‚ เจใC†(d กˆŸ ๆ5€ฃxvฐ|ด1Tมธ OไจBุ)˜฿w-Fy�“D I์รฤoQะภAๆ˜dwd€pX"Pง‚}*ฉb็1ศม๗…`ยqMrเE;๚€A€๘๔“™u#ˆคเบีิsq bL WUp=exเ๋จ๏Œc๙๎KOฟ!8kŠ p๐ˆ6ET�Ё หPwAhค“ ์Qฯแ&TชƒะณI˜Zมvิd4ƒ !)!`ฑ`™o�๙็ � (eL1ฦะ๑#Gุภษ&ง‰๐€ ็๐มh0�# ำณ!ž๐…'"„ƒ่SŸ#ฦด$C@Hœ่ว%@ šŽL`†Dเ›x>pFL �ฐ H€A˜ร‹rA3๔ƒเ6‘œd%/9์#˜$ zHอ่A์!… \@•^GเŠQx`ฦ็…K]๚‰—พฆ0ypศRผ -ฬ^Cp‰i(๒ฝ †ซ0) `ญศf!จ๒“ซ‚5๑‚Q`€gJAˆไŽ�T ข0 (8อjพ€+ธG"ฺ‚ ไ›แ„ฅ,๏๑สk(A ‘ฬ€ F้ฐTๆ0ๅŒB‚@งLๅ$ูู๋๐DณเJ 0#9 xrL_€>ฃ@ Hะฬ$`ู4ภxท…t,! ๓ B%็แ› ’ภ, }(8ฅW…}€a(E%BŠ€ๅ b€“rTศ…ิเœ์ฬZ€„~เรx๕ว<†€M๊7ก+>๎šืฝžtr8 ่Jั™ฺร )f๓fC‚ิข}‘์.)kYฬึ |ศ\%šiZ"xCi3จƒ€Ehใศภf#เยฉ�`ืภ#†”€<้จ?$Aื}Œศ็[๋-เฃEจาใNหƒดDCxBภ!^[ฬฮดฆอXB�ัผ๊•ฏ!ภ Œ›6ฑ')๎ฒ@{0มฑฏlFr:ไ 4ใSศg�š7ีฦ ๙ ย ฐฑ‡1Dญ=ธE:Pก…ไ 'ฺๅฎlซp(‚ย‘Pธ|`hx‡>œ`"ไญ-tะ:ะ@4Bฺlฐ k?โpใ ะ  9x๏Y$PT“ฒคeฅ4{ฬu6ณนœฌมฎ cัz†-�ฅ&j/ Z๊tโuฌ1�ฺฦๆ�ถฐp…>˜…คuภม์Šโช!ษMเg๋เŽrqBฤ\�\—,8sšื์�XfชPฮ‚>l,#๏xฤ!eฏ[‡ุ%๛™พ <ยwฅเไ(‡" Ei J๛bธ๒ ตฬˆA่‡W‚ˆˆ)ฐcTดm[“ดšู\]Fร‘˜ฦj2}Xw่ชณC†Bดjqdถqˆไ„|ฺ |’‡(ศ0@ศPร๗ lข(พ‘เส฿ ๆ0':ญ|ูพ�;เ+-ุWฎL�Dฆฅ;๏*h6^ะเ‚'8ม2แ๎ฬณใฦ Zš ะ๏�่‡ b€ป<๓ัDทกˆTโ3‡�ฤ ฬ2Iˆ0ฺบŠ˜€ฦ9n‚+ร’R€ๅEญ!ˆR้N๏z็‹XŸ2ฑAม61ษ—JCฆ๚ีLา›ฑOค?ƒ(ฬ=”I• ธ๘Np Pƒ ต…-xะ Lาฟ๖๙ะ'yๅ7ฃ ฺภฤ\‚ๆ|‚ t… ’c&ใAฅศย๕H|œbcฐ‹ฝ sศa o˜ภ่ะŽฯ‡žลJŽ/๖!„0V •Zเ>zูlนเจ�}๏‚+ ๔H„#hํ]ข]HyๆฮŠeผ vมlp๚! ๘•แ พg�ั/ุ`–˜ฤ[Ž Oเ�"ฎ‘ภ๊รe „ ่วUณบ0งิg}ุN#ฐ ๖*ฐุ@�ด€๐7pป7ผไ uP€˜Y ฐ เ‹็fต"b›q.{ .์2฿ก+#รmี]0/๙` @นข*Sh6d� €`ˆF†ึฟA1  ยb ,ฐฎภ V๑ศะ3Wม†n˜…q8‡ux‡>s‡้ �ะ%ๅ|๔0๕�S)%ะ ม๒ฺ๐?rƒ-Yธ?s๘ ๔Pz8*#๓GQ†,ฐ ฤ 1x5\0b  มะfำ ™๐‘8‰•˜SJะVS` J`# +๏ะ T๘0๖Wี–q'c•8ี-Pม8Œ"a N`ุHผภ6ฆ็rึ02๖’7{ฐ ั้ฐm ฐ�่เ P„/ฐ c‰ถ‡�  ƒภ,G ษาคถD! า !p�a'Q4'Tr‘๏q$ะไjี'ง"'Pไ‘8R)Gฅำฐ’ ะ’�๐’“9P“ ะwf$f— "p$;rH ะ"8™“4ขรฆp-ะRา"+ภ 2˜7R‘ม'>S8B&Ji'๕‘n๐“„๔O9$๐R๙๑!9’ Rฐ–…ิ– @H˜S ""^bKzb'ฟi~๙Oi9$Eา%d2`iPy แV๘f`vก™QŽฅyฆ™$W;Z0 ๖ะƒ ŒFšั!ด™[ฐS ชฐรxฉ>ัKฦxฆq#v'œ๑ษCมล้ฯ้pZ@ ฟภcœYœโ)ข3I0ฑฉุ้0 fภด)›�ืž*ฑ`อ ๏€ TไนŸLฬPš!!๙ ,#‹!๙ ,zDPศ!„ ฆ1€!‡8/`๔ a@!๙ ,z ,+$*ฌx1ฃF P8@@!๙ ,z 8Pศ!„ ฆศ๚ธy๘มๅ#ฤ฿{7๙ซ%�‡Y}๔ืXdŠ5^lํIืUxฉ}& |�ั— … ^—q�ุPaกฝ‡ิ|"‡ o^ธWษ=จเ๊‡฿]6A๘�mrุaoศ”t�‘X „ี5ธc‹Fึ†#n.F[~I™aˆ ๙”ƒ-Nน‘รM'ฃ”ณัศเ’๗5ycc๑iฆP&ไใ„ทeนีœูั)[‰pน&™ยี๙Ÿf๊šmŠyžŠ้ฅY%€^"jขืM ฉ [–ฆaŸ/f๊g‘†จ™ bjgg)ษฅฆœYigฅ‚ฤ*uฬ,ช่EQฑ่�ซdี็pr”^GTฎ๚#ซธย‘ช๑๔sส.›์ณN…๗P@!๙ ,#‹!๙ ,z 5Hฐ มƒ*\ศฐกร‡#JL!๙ ,zฌ�\B ,aM�ฟi<’ะaฉ9รย๔Y0D—ไ #qย8pแ"v  ฆกMHเJ?]_ล ŽXcP)&(Uฤร เ’ใ ฯ @Pก@‡ d�ฌขB….๔…ัฬูดh  ๔0‚•ซ`w ธ„๕ kXZxFุ „ฐ 7Ž<ƒ๒‡๘�#ฯ/ฅลK:๕hH mเ*ท…•่ๅSN ๓ q~hCb7o @whCก2s๘่PSค’Ž?๙ ’‚�QM=Dฆ€!ฦด U'ะS†NXaG”0ข?๔�แAT™ข<ขMx†F:๙8ัภ๕ไiB yž`ๅ]mพŒ1(ฬ˜‘CxุCศ_ภƒศpล>„ŸyPฦQ@ยฯtPม,โฆŒ†�p`Oธาโ‹–œ#ฉเทช ืd๒Eอl2TCฆ1Lว)@ชฯ&YhาOฐqŽ ะ0A๗คฉค”ZŠะ]|ุ๐มC‘l;มษˆ‚)B (ภYฺI`‚ ๙๋ฑซบ2Š8ภ &๏2>‘@‚ฌ�ฐฯ;EuมHภ ๅ4‚†ญบภภ]ภฌ�z2o/ฐย=‰Tร6‚�‹ภ1ฦ9‚/ ภ h)ม1ฅผภฝcภ๓ฑE:KD18 ๑๏„ม!ฐK+VฑศฐOIๅห $pC(ฐชm!0@-ญ๐�PAทคƒŠ-3\ฌP(<N:ี๐#ษศ-ปšP�)V––`5o4Š€ฤล ภ๓ฑ 1 v"Q)TC3z๐gฬฤE>D ‚อcภsฤ฿^๘ pˆ6๏ ƒM฿K๚ฆRิภ๙`ม๏่ใ„ &_#มตตธw฿„f*๑`ลŠ'T@*เ”ƒ๓„‚A$ป�โ๗ ๅo๙nž;่Dท5 ฤt= ิ H5ธF์|Bป๔€qxล!hˆl๘€ๆC฿8ิ็NฃA‚5˜WADร‘QBNข+เ�แย/sผ@bw฿�xะ HLu๖˜\&pี Œ)@P^๐9I ๐ภ$hมt จ„4~8ฑhฐ€I@|N฿ฺ†"$ถณ.BฐE R1ฃ!ํ X.ˆ1‚�ม B-@a "‘H”ฤNฐ AHl|วz]ด‰$…๙D' APมIQHุฐDŒแ Aฤ &)ฐ@|แwh%80%CQoะ�%ๆม‡%dกŠˆส8lโ s˜๑ภฉ9ผม3*ฃร5แ†`ถ๑nAล61<\รŽ ฉภถฐฌ?|ก ั”ตฌ†l@wPม"ฐA�ฟี๒–นLฤFเ _ิ @้�‹@ฑ p€ม“ ๙ n|’ฑ*„1ะ“Lม‚ ธ‚W�22ก‚ ์ก�๐82ƒ]๚ฤศ )Z`3MLจ�๘CVRฆTศิGUR>Lฑ&”ฉ๏x‡)`ฐ€Tƒฆ'่F&๖Hผฃท8'บง db2E Ttkเิ โภถQ„D!:\ภISบา– @i‰�เ#ั๑Cฉ  X€) v8 ก€D#6uZš rPอT'eเWrJšhฆ*ุ้rXิะ( ฑ–ซ,R ๎ ญฐ้Tปfˆ๔“ซ ‰Ž›ก] ;ีก๐/ั�ซ่น์’›พสภiHe}Pb…์'fฐE!BŸ—l#าEHMd‹โma Sะ.่ฑ=@qทป แ Dพ+‘�@ ดั10+`?�ั๎`ฬ Tภอว;\ฬภ‰3๊ก สค2 !๙ ,z 8Pศ!„ ฆi๖ z0!Q•Dq*ฝจ๓„‡IC๚œxิcTฃA—jุดcรฃช†ญJถ์ุฉgง&ต 6-Zจeษb5๋6๎ฬฎE‚ตห7๎�Ÿ ัŠญ›w0�พY+Fˆ7oเฌ„ํFVผPp�ลBcฆ8๏ไ‰%ฏพผท;D๕๔qC–~๒ฉ๘ัฝ'{‘ ˆZuไ‘V [ …gั@‘$^lั�—ู{_}ิVBnๅ!‚�}(ั†!๙ ,$†�Hฐ มƒ&ิภฐกร‡5ŒHฑ"€‡ 3j�ศฑฃว CŠIฒคษ“(Sช\ษฒฅห—0cสœIณfวŠ8Nฬษ“ขอŸ@ƒ JดจัฃH“*]ส4%ร {JสณฉีซXณj�สตซืฏ`BŒJตฌYaำช]หถญ�ทpใ�๔)ํฮงฯโผ;Vฎ฿ฟ€ Lธp�ˆ ํ–ธXoYร#KžLนฒeŒ๑’uฬนฑโห C‹MบtXฮจ-š^อบต๋ืฐGฆ~:›q์�ธs๋�mXbmชžm๓Nผธ๑ใ5ฬŒน๓็ะฃKืJwบ๕ุ๋ณk7้[๙๖๏เร‹�Oพผ๙๓่ำซ_ฯพฝ๛๗๐ใหŸOฟพ�๛๘๓๋฿ฯ?ฟCŽ๗Fห=ๆ™€ฬ�'\ 6่ f�iเot‚>U๗เ†vˆ`bฆVWˆSyhโ‰�ฉV ‰จญศbO(ฦ(#}*ZจX„$๒Uกz้˜_3)${5!ฒ่ขY>๎ตุPF^‘<พธ—†Uvge_Rv้%tXดe.6iๅ—hฆY�g›ตXˆดU•jึi็j�eคๅ˜ rYf’ ย(เ„Jไ‚bšYRžd๊่ฃ † ้ค”ชeฅ˜fช้ฆœv๊้ง †*๊จค–j:ุร !…ชชฌ’�t€Tไ Q ึศ๓ฯ4<`ภ1X๓>ธtะชฉศ&KW๔ฃKฌE1๋,ด"น`6๘ชะ2เส4>4ม4เ€ .$ซ์บ์R@ $pิ@< A)˜PA (€E t ะผ๕�›๏พHn จ ‚ภmฐAยt@lฏ)คฐoฟ�<0ฝw<2H ‚ &0Œร5nˆ ยรำ มฬ!„€\ยซถx0+ฎไฌ < @ยsฐ 0ร ดo"P๐ด(W ภล(ใยลspส!˜pปlห็O˜ดB ๘ฑŒ?๕(ใ†พม�+Œื}w�{WC7จฐB ทุ’BŒ` *€`M&*ิญM:๕t๓†ว}ใ๓wเ~l�๙็}`H5�๔ฃŒ&,€L;ดืƒฯ/tPB.้๘Cฯ+@%เดชญnBL‘J๏๙ ’‚�QM�sLmˆ1-ดZม ๔”แม Xใฯ;ใฑ๓๎ฝ�<ฎฆจภhSm็�� ๘CK"ุ่� "KœC Y่D?ย๔เ ฎPž@ xภ.ะX:h#R Šh์ภฉภh t`ปุ(($คB`ภ€!(Aฐะ…0ฤ๒ภŒ?pแ p` �<#4ƒ`ยโ5‚.| xภxะชแ๑Š "Lžั˜W#`ร˜พ‚เA›ศ‚&X6œร  มzp4˜ั„xa &>1ŠS @$�8$CะŸ"ูs1<"เศ„๚าณiผ€่19- ฯg—ฬ$1‚2ไใ๚ภBโu๖ข„'<† +๙ฌเ‰ธ(0ON”ถฤ%ˆ0l์a ๐8Bzp‹t B -ศฤฆZีซfXเPณ๖!ˆI๔ฐไก@๐ะŒr4‚6XGqŠR‘๚๔“ฝHE:วZ`ใGPˆ]กŠhึt–8ภ všณห�’Wชeํุ …5�vAYrผNฃA‚5ฒ:Aโ=•ท฿�โA.@€†ย„cฝ†SQƒะ#ฏ8-‘ ธKF@๚†@=฿Wธญ€ ขa€Hฑ๐ฯฎ`Œz€Qˆ†l6^ะเb“ห๔ภ$`x‘ g๛ํ๏wP หX„ บwใ )@€M+™ำศ๖&ฐ9ิœ=เ;ม1ˆ‹�๒แสTํ2กฬิ‚ธ@%คใxu`˜“  ัlเฐE RPeฎณ XภGp‚[€"Tจ(lมƒP@™_ต‡$dLcุวd-ƒ60aซ๘บ9�;ธล)„ ‚tๆwฒ๖HD ั8คk~tธ"�๐†_"ฯ{๎sฮ๒pŒcjแศV…9ฐl˜โ W`ja๋aEไL—tกI�้œูa_x‚š‰งขะzรTหŠeผ 5ภๆSอธ@ฬแ ‹jฐˆ7ฬ•ฉภ* ฦ/tแุfDฃ8‚ ด๕*X6มƒ ์_ธม๖‘ˆ Œภพจฎu=APl`h๓›็-pฆ้=ๆQpโqม@ :๑Žw˜๚ๆทฟSะ€%ไฃญZ@'ๆ1ษd~`�;<—‚ค"-๘^728œเ�‡วA.r’{`ศ๐‡๏2ก85  wห‡)7๐wpN8b๐<ฯไ%Hล๙L๐fๆ3x Žwktno๓�ร6Š‚(คƒ 6XภX็H๒g]=€p บศฝ็๙yคgยิB0ค@xื;วR0ศŒ5๓ฺA:เe”,Qvุ*@ฑภู์๏T3'ๅ%ะ@จ…M9X’๖™ๅฌ๏{ภw`๚c๕ฝฮ AšJfฮ @Mjkƒ€Xึ/–�๑ƒ?Yส‚€ๆฺ# ๘ฆป๒ฏรc "ฯwˆ๓r†Dฟ*์>7˜Blš@��Sj๑+d๛9ภ ด@‹_<,#$ศ@ล–O�5i`๚<ม€œ“H„D๕  Cษ'$ ,€~ั๕ื€‘ม'ุ๗X ๆ็+,พ2PEถJ$เ+/ฐ r๐พ’๒๗Xpธ‚r‘!a.ˆ(ˆ8ฐO8ˆภNภƒ ร 7J`~Qƒ7p:(„,…๏!1ˆgซด "จ-"€qะ„ ๑ก๐ Gฐ3”๐ ฿ UH ะ P˜Yธ…ั…_X€Rx‡โ9ˆPฐ<‚๑ƒล๐ จะJx/ฯ@ �เะ๐ ไ@ เ�@ 0€lpศ8ˆ…xˆ‰ˆ‡ขธ•kฅp\!Ia0v,ยB,b…vนฒ+ฝา…T’ 2]A6*/p๋@ $PฑP๗0 u�'M0ภ/€๊เ๎ "เnาคฟŒรXŒว˜Œหh‡ฃXŽGPU€ ภ.#ม1.ๅr.้"�โ-๏ล€ธ]ปธ๙€ท„? ๑เ B@Œ๐ร0 g2ั@!ภ-ะ/ะิ่ิ๐/0€ ˆƒy  ้iŽ*I™w5" K ภ5P5“€�๘|ไงA “ณ/เ๚]ฃ๛๒“แ+BC4[ด3ฮ7 P'ึz็5pŽ5หuฏzŒg3eรhใz รp6ฃ2พว/K3x.0ผ(เ๛`!3?Œ๋ฐ—�@ ?๐v ก8gP ะ �@ �0ิจ�เ �˜ฯภ exxฉ—|้—€)˜„IŽ+š%ฑนเ~฿“Œ@=~p ๕เใ0<',� 9P} ถš๕ ฐ)ไu้9ฏะ๘€� ฆผฺ/x<]ี|ฤรŽƒนเ;ฉ‘S>� �ะ\iฉ�๐�€šงwเเšn๐c  G–36 :ฌใ:ฐc8ผณv�f8เ ˆ—@q€sY—ซ๐ {น—๊ฐฑเD@a 00ล>เิ‘ิˆŠิ i ‚ๅ๗ช  ฺ กฺข๙ข-!ž 9ล’ Bฌเ c0 ภ fDžภ+ ฃ: =๚ฃGzฉ2pฝA Fๆe0Q �ฐน“E5Dช0vP€ภdภj+4๔F\๊ XIภฤ0Fo๐e*๕€c0ื0Et '0M ภ ุยC>�DBด,ฅ[ฺฅวฃ ฐ060ฏ@ฃrI—Vเ‡a6B@ ( V2)…p“ร€๊์์ภ ๚ �„ขJชฆบ—จชช0ภช  ฃฦฺ2Jฃ๛ˆ‹ฃ5 €‰สffฃccฺฌฯญ<ภO�ิd$ ฬu๛ฐ;c,ฐ  ภะ*ฎี�ฤภฒ v L�d>ฐN›J๗ะบh0 <ฆLสd8�Pf ึ][P 1้f‹€ M€L.@็šฎŽu<[p `GฤS^pRช Q…?@ ๋เ ? เ\ *˜�๓R ฒช์@ซทสิ@ G vจฒ,๋ฒ0+ณ4๛†วฺด#13ZฃŽ…ฃEcf7ฦbŠตd:4U{f�FWv…Wตฉ0แ๊ €้P � ญ•\ห๓ฎd‹�€2'sWา•หš‹ ฒญFnทj —l`GะLA  ๚Zฐ ฐCT้pTIี2%ฐถmหก B<ะฑJYล—๖ภลฃ*฿๐ะ0PX€/Pปตห =›ปธ๊ /Vป)๐’ ฑบญ‹2ฐ+ปญ๊ดศหษ*ต7สต9ใตY[คฮ^[^โ5ไีฎkเ� เP8 ๓ €rKt๋v�P4 4‡สทN๕ท�Z‚‹Š…{ธ9–ธLเวน๐ า@Bึ2ดe[ฐพเP80b–30 ตดP`ญRถฟ ‘0@ ?„ก ‡ qก ซ …เ ่๐ณธ Rภ,�*0ย"Lย`ย(œผBŒฌ•`/p‹@ตฯ{ต !HF<ฐำ๋ต^ฆL pE๙ฌ$($.& d ,P c €�pKFM,ณNกเทเXWx�* /ฐJHฐ าเš๊ ำณ`Ÿ`o(�–b+ฦฐ ํคSp๛+Q` p ฐE ๐X๐%ืœ๐"๐‹ิ๐ ~)ัt๐บC๐ธ‹ซทร์‚}  Xภดฌ,สคlสจ�ฒซ<ฤฤฌZ Y ฅIลL|ฦl@nฒฆฤิ{cช๗mแ6n‰ ฐฌษฐฃ…6—0|ฐYP<U;DlฦึญgŒฝ๖kPt€ง0c`N@ั†ฬ] �ช€ะ,า0Fe0k ‚๖งšญ‘ภ…“ „ภ*ง–jซึj=#ฮไlฮฦ‚Iฃ` ๖๐Osฉ€™Œฒม๋"@q๐ ฿เ฿ะi๐ษ�๑>Pส?P ?ซ˜ทส ฃlำU’8จา,ํา0-ำ4]ฬJMN“Qื s0Lฎภ }๘ศ€9! Cง ๅi ,ฐSญ!`ี˜3c'sffsFsr1  €7�๐๓'pJ'u 1tEทsŒGtอ๕r1็šs๐žฉะš:W1šcุ“w@CภjลAs2G4[�;gื๖v5 Žf5 `๙เลZ\0ป/๐ ™ผพา฿@ ิฐ Lถ@ ่P exผ๑ฺ;�ณ]�Kฝ�U `“ yญ๒N*0ถุ/2#1 ร�`�9ะwเษฯอ4H้“@ผ4#|uถืืหz eZ0ƒ'1 –`‰kณ6ำz_ฉผุ_* รไy7฿ภ–Gู‡๓ณNNE €1"0ณ+Vเ/ ร ฆ๚ }๐`๐?@ �P gเขชkแ;ƒแฮแฬฝ�ฤ-&ยั0n$ˆBBญญ]31˜Tพฤ  ๏yกlr;ร_ธใศ‰Žฯh/nP …ภหมKยฅไ"ภไ1�ๅๆaูึข0รฬำษ I,ะ> LL@s๐X#)>iNข`พ็๎!0ฺุใ"มญญ„C1crg�>‚ฮ็ŒŽ&ˆ สค<37�่–^)3้—พ้œ�้N๛*ซ‚า"แŠรR,วRАŒŠ 1+ตโ้ฎžา๒,ข๒h.่r๊Ž๋ศึ‚-€�๊ภ~๎/๒B2ฃ/l2–W0๘‚์ ณ0 ๓~ั”5ฤ07™“R™–+ำ2 ๐2133•์โพo7 )8xฃ7|๓Šฃ30v“๎…s8‰ณ8ำt†<5„+�0›ต9๔ู:ฏ;ณS;ท#wใ~๐ผม?�@DI„@ ฤ@3A9ำ|oAa€AฤAd8ฆ[คC๚Tลจ?DCTDG”DK4๋�๒ฆัH�I“”o „IšฤIr�|7/JคdJจคJ์Lฎ•}ฐUูGLฦDฑสฤLฮM�๓ReQฅQ0hR•5R;–Y›ฅRภR.u}Dฟพ๔ูGTF…TJ%Mฅ๊S?๗ญกXŒๅXใถ๕—ล๓`ŸR๕YGภE_\W4๏T[�p[Kฅ[N๗ฟ๓U_๗•่t ๆ_d"ฆLฦ_šฯ`aึ=AฦC&P9 OผLpbŒฬb.ึc๙๙ถOq6guf๙า3ˆฦg~foj„fhฟฏh!ภhŽiูRยฆฮว†ธะ�๓Sัชฦฆ๑jฑ๖I�}๛�๖ึฺ ืo�ๆh‡r7� ืp—ี' อtyอtฑี]�ีš=s57—saสC D˜PแB† >„QโDŠ-^ฤ˜QใFŽ=~RไH’%MžD €‡‰. !ABŠ4€!… —0eาœะ@„4Pกข ˜"ผ™SEM<(P0”ร r ‚`…€ ‘RํZถm�พ…W๎\บuํ�•๋rฆ 185จ—„–’ฤ@๏bฦใVจpมฐcส•-_ฦœ9๏ฅฝŠ ๖ฌY๔hาo{๔`1น๔jึญ]ฟ†[๖์ส�:๐๐q†%Rค(ฅม"ƒA6l"l`I[๙rๆอ?‡niŠQ#็�7tฤB๙‘กƒ’KGŸ^�z’�^Kกอะๅำd฿๑>ี’๛๋/ถ Rุ‰ว�aY…6๎@‚€ฮหฏB /ฬ๏เฐ#ฌต€‘�ใจ81D !a9R4 DUด‹:Rศ)0žฉaใ’d†!‡œNข@a WD2I%I;ภ*r(จ‚XqลCƒ$ซa—R๘IK๖Šศ)1!๒rฺ"@{8แ -Œš|๒กฌ‘็Ÿฮ˜ k�ม—�„ƒŽู„ห‰ฐิา�P๘ Œ!<+Sั‹า\ณอx(„@Œ‡j6uวR†PjISOE•1ฌัรยคคาส‚$ซ`ฮ์’3I๓ฉ‡@c‹€+๚ัลRŒVm•ฦ‚Pฬ,h‚ hภ\HขA ญˆV[q-ˆ%๒ ใ ม6–Xc?bข˜M฿u'HxฝัCีRล7_}'Š๊ŸจŠ ‚ค0ชฎ๙b(6a”QX)ฃผยaˆ“ฌค’9ฌ8&xกžbš‰ชฃพR!ฌ (๘8โ†F�ท *0 ญŒ๘๗f R2ฌฐh!d๚yhœ @LH ƒ�Pุถ“!!�ๅถ ฆฌาอ›‹๘f ๅกฃฒ Dh‚Š/N{ƒ\ะ' Ža.น!ข3HมŸZ่ฝ๛†L&l›Nชl„:่C…œำ tฤ‡l๗ล<๓ฬ๐C›t๊้ๆ €๏วŸq�ฐmdฺaŸz~‘r”k�1}ฑๆ�็ar0LC?nฉง๖6๐เd�ฉ็t๘rI}@ rIวz�๐ „ Nxฤ�ง]ขgœzภ`4rฑž‘์9๐ เมAฤ<๘=๘qๆhก ๔ไ“0๚7ดฮ}.tT๙€!ชแ~(C &Pฺ๋Q|�โ˜^๕ฎ—ฝ‚ �ะ2๑TัตYลo~๕#Lทแ[\ƒl I€!Œั‚ดTเ๔(๎tวปด ํภ>�!_๐ไฮƒ^ ?,yสpƒ฿ˆ๒ŒsNฌฅุ Tฐ %ธ…-๘ใJ|#๊ …;พA uˆขR‡ŠqอีัŽ๙๒า9ไ€„T่  ๊ˆ1 โ|เ‚`„k AุรXแ‹7ŒaุC O€ฉ‡“ร@ม 3ไ xุว&ฒp…}4Bh q <๘€„N์ร Aพ<`‘งด!ž๐8ภเe—ฐG"บ‰~ฤAJvx"ธ@†f`‚�`C2—เ}4ขp…/B9สR.ฑ„=x‚+H€<๎ฑ`ภ๒ภŒ?p˜, "ะ GB’ศๅ.{‰ศดxะ“Qมญ„Mmrณจ€ ูหB]ฎ ุ<‡@ƒ ๔เi @8้I–ฌ€•B”ะXฮฒ–ท4g-q5dก�C<€NuZ)5ฝi�คƒf0"ขˆฦLภค๑Œg๘AT�HจB๕m,†H ฌ;vีซาัห ฐ‚{$โ�DHpPdรธ€ šก‡„€ฑs…เf`‚w0(—คไŠQx|ตfŽQŠLเ๗pe�Lูi‰lˆ•ฌ‰ฐ P์ขฆฤ<ค l•!.ฉl&€ bœ �ฐCL€A˜ร8F x@Qc# ฌaปซYq€ ]gXวZV๘ขTJ Wนา•ถ้€f9ู๋$ฐa˜B�„[�๒ึ'่ม6่ €P<ˆiฎ0&l!Kˆย<ˆภ~K’!ภ ๖ฑ†๗„ภK•].f%VF๕‚^ or9๓‚#{ C>Nก,จ•!$pj<†ฑŠU CGกR„#พAŽ6~ƒ ’�jŒeฬœH96Š |ัlาี…๏'ซิิข\๚ฎํ�Zไ#ื |ศมPPI``มบฒฑ"ถ‚€Chใศภฦ‘+ะƒ[คZhEฆ+ษด(ศB?๐แ:๛cCจม5๊<เฃ‘Œ เYโ†น>�ฒ�ฌเ�ะDZ@‚ฅ�xฎ\B—ฝ ๆ#wะฟเ=ด›๒ผg:๛:N๕�˜ๆ˜NX6๖0x๘8ษ!ภ v,เฦYข�ฬŒ"ุ ยˆ�หฐ‹-ญK–ยสjjจ�5mjgF3ฦvถi#˜ œเฦ9�‡xlƒเาcแZX˜ŒไO+9๋ฮ3]ฅLๅW"„��ึ2 :qŒ:ึ82Xf�T€oz]sฒ/เf8๗ฃB ฤ› 'ืม 'Fภ๑ภศ….ศฑ�t๏‡% ะ‚&๚aŒฤ�zภด๖�๏wšํyจ R๑‹ำ ใเ7ถไ„๙ˆร+๒@ DHc07?(ผ๋b฿ฒฒ7›Nlc๋œZw๓ฒ{P lœรวฆ:tิFjฏ]X๕Œต=wบc†�พVฤYQZถJรr07”3pืtฟ๛ใ„9^PZXum�hฦ&๛r4byฐ์๕ปฝ Aศv�  @i {HB, ง๐ˆAŒผ๖Sม–ฆุOผ 1ฅ•คใฯ M�00Ai๏^๒˜%1 8Fง�ไล“`๓{๔๎P i4Ÿ`๐›-X|(6ถ—๎]_+ค`่ๆ์ภN Pฃ ต€…-บ €$�๚!˜ภ ๊fXค`๘Šภ:sน„ิbึย'์๛=๎Cญh€|ฐ`+cภ‚eX ก…`œb`‡ dวI1G01u`dLxบCมฬŒ�ซ€ „k $CBค‚4sƒ1xƒ฿Šรณ9lฺ‡DภมฦSท€H e2„n‹ชบฝศ2๕c…ex.ศ1�@๘‚ธƒฌ7s™ฉ5่‚   |8…9;p‚ูb�}ฐ…2@‚ฦรงukˆ เ:€A7x๘ฟฐฐƒ=๘‚'๐‚jโ’ิ์ถ(œย*ไ’Sฺ‡@˜ƒ7 ไศl„7˜ภฆ~ฐ…P’ร :h‡L๘‚*ˆ<ขCˆผค?ฐX#„GdƒฤAF›ทdฅ>�C&�บdZฆfย–•ะCDเC?D&ebฆ8‹<8่า‚kpEม€#xv๐kคRxจ๚k๔Fq;$วrt‹H…`h๊†Lะ+ฮ)�Yž่'ส.^ฐ@†LH™ชˆM๐‡{dpโ…%ไG�žCH‡tศ…~hlYtTGvฬ�ร(N wะ†?Hธm‡L€ใœสข("˜Xไ้.0 ‘:.๐^ฐฒ„�G†ภ€Œ|‡w0 ศu<vผŸ=8๋ษ/"zดGภ �œŽค ˆTH‡{ไ  L…๏1…ธŸš4.ˆส=Pษn˜ƒ1:ธRห"่ H™L ญH‚์ษ0‚ใ!ส8J‹ ฬฃ‡y8Ÿ๔1ˆ ิHข„๒1L๔๙ศqฉƒดX€Nธณ{9ˆH7nเq †>xˆชฮ์ฬg … เEsdอึD ฤY˜  �ำK›ฒ8 ุ€ ศ H‹�“Sฒ ฌŠ�” –ฮ(Nะ๓จ8ธฎ…€อ M  อฃ๙ฒ€˜ขYˆฝ1ชhA๐—’‘Œ–IŠ�Dš‚XNฬLˆ€ู‰้ฌN๊Šฏธอำำฮ� ‹€)3ฮ๚”™ฃ™ ธ2ฯR™ณเฮยP€ฅัฐ‚(ฮฌจO๐เ ิH•ิึภ€ xq์'T้ฎฅก3เ>(3P‚ภMEีTUีUE‰ฐHŠเxHeีZตี[ลUุา\ๅี^๕ี_ึ`VัhRMึcEึdลถb5Veuึg…V…8Z}ˆŒะ�์€ฺT•ศ +ฉะฉะOฅ๑› ภŠ์ฌ€ต!˜ณ)•„ธO ภ ะ‰๐ ํPmญD่Šึ}ๅW^�'ธ6‡Œ(HฺษFศOา1ิแ€GP€ฐ†9ฐ�Z z �กžzะ†๔๙p…ขHHธฬ�h€%`m8…j‡ถ$ž;จ†,jKรษxDxู˜U†™ํืž๕YV�P๐Z( †(€$เb€ƒ/xƒ/hA‰*คCโ:€…‰ใซV™งzบ'ุา(€~XโัX€W†B+$ะ9ธD8 ู#8%}P%• ƒ฿’Zผ&<ะ�,เ�S�ูรE\ื< x„|(I๑a˜…๊+-ฦ?พk+ุ‚jˆ8ศ,pf|Edh‚่ย–  €we€)kโู‚kเ€ผXŒ/“ h†ศ{ลbฌ�“s0ืB+ตb+ท๒��L�ๅe�ึธ@ƒ}(‡8ตQ€ฆA9•c9(€T‡:ภ)(ฝฐ€A0ปE เcBH‚{`�ี]�768 7๎ฑภ†�บb{เ&vโ;:€+0†z0†?ภ'“\axตส ะ;ฬ๕ปถu†iˆ€Y0‡O˜�H ภK>0-™ผh0=<‰k€ƒhร•:�5 W๋�ลJ=ึj=ดc8^X<า'fไFฦธ…S‚ฉˆN†€a‘†ฆ-ƒ7–ŒAชญ?~(„˜} „6 ฤA,Dkr‰yเƒ%ศ‚rP„?a”Qฐ�ฝ …ศ฿�ล5�•‚ผ�„.เ�1�[P�ฝล)=vไeff๕˜.• ุเนGฺ์œฺ‘š; ‡!`€(ศ.`J:ฃ‡ง ผฌžw่†?๘ธBโทญ�pษ‚| –่ๆ|h‚—ต†,ดษ’Gํ)ž}๎†(‚ๆfF่„–•8X€ฃA‹ฺฤV�um@�„Ÿ๙คš๚คr�—™P,˜k@ e�์อ|ๅ€นึดุ€ ศืU่™ฆiบ�ีล —ภ- ]€XŒธ้ฆhาฤ<่š6๊ฃฦถnถ†t…!จไ?๊1A๊ฉฆ๊r$ฑ(j‹Œfญ๊ฎ๖๊ฏ๋ฐkƒุCตT+ริn9๋หc๗@kู(k\๕๔่อ ศ€ข๋๕0ษ๊ƒะQfŒ๘๋Yไื์ภฝุZ•‘ก๋@„2XยผV2‹ยยŽˆป>ศ์’Œเ'5SfuJa“l“`ŠฯVฮๆƒ €9ฉ“Aๅฺถ“Bfั&„�ฐ์ขS“ิŽˆะํ,ี ุ”Œnีl‘ˆ”‰(P†~xeเศฑJq�ถํธp,z๋ิ^`G้kส‰ฌม์ย๋๐vํ‘ 2hF(แ•b=ํa)–lAWั€๗F๖ๆaagก๐gน์h–๏'Ao\ร๏๐พร Xp๚f9Qว•x^`ดfn]™@Zƒ)ˆƒ"htษ๏eiฟ‹yซฒ๏~๐ๅžq!๓ž ผม˜šชQ–้่คˆQุ™pA l๐„[ ๒™ื)Pyืx�™ฦ“ ่Q`Œ‘hฑืฅ9 ™0 ็่ภแ™ฃ(ˆ�„ฉ†qˆลA๒[yR @o{�ห ุ๒‚A™ฐภ]ฉiน๒,ว๑๘2�O๑ sฝกt/—tP ๙xœ)™@oˆฆ9˜0XU๓ แ™='ำ'G S฿™n™ชak๐b™ €จผAืuๅh”กMšมฯฐVฉเอ 8ภ)Ži๒zล ˆ˜Luธ๊ึ™ด๑#ฟ„๒9Lชศ ๋มํแ๏Ÿ"P๓ูฉ�dC@:ฤ6 ฺุฑ๗�‹œ•YAGูtPY–mหฒwฬ๓ลdดซ€โ%_ �`ฆb‚Jฒ$Lšฟ˜Oฬe‡ N:‡8]�JบคLfยลฉธ�บฝ[†J„m๊ฆ ๘'†&(ˆค]ฺฆ}ฺ•่Z{‚|‚k่Cุ>8„eˆ"6zQŒว„€‡>๛iาOะŒ-zL1†$จtŽ|Œ(�7#5ฺx#Ž9๊ธ#=๚๘#6R•อ L€ 1,„€ม !ฐK+90ศRัLYุ#Eป`๒B ˆb b afหfฬ"อ Xbษ‡ิB`@BJŸฬ(aCkชจm.4ฃG!d9ย(ฎศ๘e ฅนfI5.ตู1l#H$J6>HJฉฅ$ลค“PvภฏM=บูE™ไฉ ต—L๓+�“�]ซะฒ•ZZฐ‚`ylฒ[T๙`แย1Tุฐ2Mคเ)จP€ ๘ึˆd[\ร… 4C\0f™g�‡ปtว๕‘Mบแอย�ณD ๎$#,แNุฦๅฏb& !ึ xภ~เ{ุ›ว๎๓ยฮ jgTƒkภŠf’@๐€ าwD>๚แGฒM!ภ ๖ฑ†“ Oห๛  @ภŒ›d-เPฎp�+t ‘ป%.sฉห]v๎s<�่Xะ‰]ิ!@ฐฦ๊(@๏วZc;„/=:"เq„แU$@ี\ค�€้ผ{HมU๒๊เ!ะ€D R–‡]Xฦ#ž4ษ�๎ค…C $ˆฉ\€๙€i๖ป฿ˆ๔ื�u“šT6ตiห๛่‘O1T [xGtโล๙ษร˜ก�๑7 ˜\ ค˜qอ์ฅŠื}jG]ด~Ncส%๛Qฐ3|‘ŽIศชำ#TrขM็ฬ|๓ศH๖,h,NKุ ะ  9ธง GJFข•ชคXำ‚#ˆh ำ๐/c+�ูาถถD�ˆAŒœ`‚0 0‹ี5€XzkT)2M˜=ิ-3ุD†Vฃu ฬ]]แp‹ค=‘ง๔ฐยœB๑‚)m0ฺ*ปf!Œ+ˆะเ)#J๒ซ)�xภ“€…Vว†ลๅ ณZn5๕ HคpA<{’T‚YYŠืผ@! ฝญทฝpฉoฑ”`งTุ]�4ฏ(ะ{[ไ6I๒/€๋38ย4(Vฑค:L๗ยkX�0fœ1ฬยŸ€ฏง\_`u2กT0(Xัธฦ]้ކcปธ‰^agŒร\�0WYu๘ฮทพRช„9^เขr4BKฐE Rะ8_š๐ฎi6 ๊(š��7ภ$ …-\lใyf’ zี”ห๋ฬ^�"m�ฬนฐŒl‚ธaฺ’=>„ Rะ$เๆ˜ฝ;NK&p}„มŸJAvYŒ$%I,)(A^บ‘™ฺถุฦ>6ฒ}%–5tก ชภ XฑŒ7pกPb�๑…�aึ�qg>C์•< ฝ.6ิก.|( ์๎%๕”ฯŒa €BBภง0H๗q�FOส๐†ๅูa_x‚t_ก_� ฎ7ผมYภ.ฬ!‡1ผAš�lnน<XศรŒ3ษ†*4‡7L&oOบฏ๐๏€ณ<ฺำฎ๖ตeˆ•ฟ!]๚�ฟี2ยs0�ู!Š">๑Š๓9Ž~�yะe(s€Aเ-ฤหํ’๎H๐ฃ˜„>ม�เ GUๆม‡%dก(ด๑FaA…F!แ ผสxฮะ ึ๋ขq ์_PผฐRdBuมˆ๓‡/ta3ž™ำ�๑:š:6ํI—8ล-ž๒@ฌ|!มว)ๆ0;8Aา(1}เ�“5ฦมฝ6R7ใIฯ+=๔ดทฝ”'@ิซž๕x›:อรDณdิปโ๊๖ˆDฤ ิขD ุฦ6จ ผdsฟ๛�—m�P ิCnˆgJะ‰w๘ใฺ๘C pmศรเศ $ใ ^ค๋ศศ„ ไ›!ไรV�™Iจึ2ม™่�…€� d�5”฿คdA>ผ0@ไCHจ?Œxฤ†hSสจYคย๕ไƒ)xภ์2`=dbLA:๐Bเ ุ`ภtB=ผƒ)ภ@-5@,ƒ?ธ� l /ลชภT€ $ ฦ„‰<ย5ภ฿IP@ คB:0a„Ÿ6Œเคะฌ` š‚Gจ฿;คC=ผ_ („‚!†ภ A @๘_๙_ๅ`€! ,@*C @[7d‚Œไแา†.!Žก”แ8œa$ภะรp`>pมBศ ฺ`xก&คC๛uร \@ `ƒf์! `VQ Xรุดƒ1x@„8˜!—กฤˆข?ิ btKXC๛]ข�T@๐3a ""*"#"Jเ@:ธมž„ ฎ!zD†แ Ž!ไ๙uธ1’ก0†@๋ ŽH�†(ึร%M8พ!ฮc 4@:ฎc;B" J"า]€ฬ ถ@N`7œŸBฌ@:ไข-?่ย}GvคGI4<€�อ7†$@€H@ ค@ข<€Jฒd pภH$ฤจภษ5จ€๘@[4<้JMJ@สไdlภ(@”dK2@จ€ (O๚d+*€ค$Uฆ—ษQ‘L-€HB€ˆภ๒4ภV’ๅO†€VFZงดๅVdคd Teu5@|•eทd€เไ่คPฅ๋ั†Vถd.“”%B $๘ค�„ๅX*ๆ/ŽๅJ&(“VJฆค|ฅI $Iš�ไI0f]&@P%HฌdPlf€%ˆeJJ&c:f&Nฅ \ฅYz€SพๆZR@h%WV%าอ‰�„fO�ค6š4@KวJ‚Jl'ั\@คฅdา$Zฆd ุ% %Iฒค ,@OชsJภ0ใtถ$W‰ฆv"ฆdพฤHVๆ7�วs"ฤ4@YŽ0ๅu6fUช…zึ็}ชวผงLาF,€nฦ4ภSz'S:%|*ฤy Jผ‡=คc}ค†n(‡6…%UQ†fN่?คมq‚hŽเ็$]0’ „(�Œฮจ„�จŽŒh‰ž(๗ัF‹†c}( †Zิ๘จ‹ย�(iSจEฺเ€ƒ.๘โๅ$Y0iŠช(๏iWUQŽ(้€)$Œ†จ˜คc!ึ�€ะย/œ\‡ฦฉœฮ้ฑ@Um“lyžฆXฦ™ขฤ�) j@Ž�iŸึ–žึŸnฮก&๊ฑq 5คƒ( ลั)ฅV๊lE3ิƒ& มjZชง~*จ๖ศ๒ุV)DกšO ฆ๊�่ศจvhฉvJ็ผ๊ฉ�’G๘ฯฌ†*ฎๆ* ฉ•๊ชฏ�*ฐส) ฺฑ๚ฉ +ฒ&ซฒ.+ณ6ซณ>+mัhเ(ดVซต^+ถfซถn+๗I+P+ท†ซธŽ+น–+ท€ุ'Œ]ณ๋Ž�้Aอจนฮ�+ฝึซฝ�+GฦR?่ป2ซ ‚kŽP+ผโ+มฌม,น*pแˆgBe›๔ฆ`A @’UŠไgฆภร6g&U๒จgre pฌxV@ˆ€็ํgW๒่eโฅภ'MBจLŠ์�GษRภ@Zv%$€Iˆ@บl,mถbJยำศ่I$@ฌd|ฅll๖็มฌe6-$=mƒZ&ย^-ึfญึbŽภม˜€˜†Ÿ�a›ฤ€5�>CtXฅŒญ๙•m7 ‚’”ภ-ุB|ŽฦlO<ฦ•–J-1 yฒ@ดœF”‚”ฉฤPฬIฐU�>�4‘ภˆ5ล[ญัF@น3ฤ@ม `รŒ6ตฮ๋ฤฮ์tKM๊ฬŒ+ม…HมBม+�๘I3ฐ uŠY�2๓0ƒ๓ˆาba3*Q28‡ณ8ใ*๔4ฑ€ h้IUVw๘�’;�ื6XU ุ*๑@ำเ๏Ÿ[,Ÿ%ม•\)dS ๔=ฤม+- Bฒ`f3=ำ๔WG}2อ ฒ–k+ต ฒ9eม=ŒFฝข)ฅิ6—I+OJ ฤ8วดLฯtGภC=b๐hถ๐ไฎ‘ฆšŒ๕4์Z‹1`ม2,‚ l๎ฦ๔?{Gœuฺท5JDา �Yž5Nฎ-’Œภ �(ิ(@Jq๕ Dˆ™D5 *ฐi&ซท.\~5!๒๙@Wต™ห†๕ต‹ั4aถaว�ย)AชM*EUลUdE€ส๒๔ำA6VhBไ�Aฟค€lแ‹nฬiEต )^๎ํƒ+\MฺD^qPžๅ�pŒภ๔๒จภ"`ลT@ถm[ท%ยl‰/ิฮตBJ\8pฤฑฦk 7ƒฟ0\3pA ณ‡ s๔อ6LPwlศถๆถw7x๓H„๔)Uะร<ิ8B|๎#!–๗yงw�-A>ิAB,@';w EZคIะไ2ฆโ`ส�๕฿�Q่}@ฃ4ถŸ)คภCึCDฦ์ม6pOคฤ,ภŒŸ�แŸ ชŸBฤ!^RFึ>ั(๊ L5œ Pn<6๘ƒC’�_>*q๓B$ †ทŽ๏8FซโUƒDhgŽ�e]๙ห$€šๅl๊์}Lhำ<ƒชคiึไM๎์_ย้hlงtg]>hีB/(@คมeขค$Hภ}น…b่r3๗�ภๅ[6@คyc'˜๙|FRS†y�9 :๘RV]ูาธ๕้qb้ะ†‡N.T‰฿ศšถiเ"ึzสŒb€…ชjบชn:ง‡บช บฉŸ:%::ฒ,Ž`ชฆrช-�+ฉืบญ฿zชขบฎ๏:ฏำฉัŽ)ฏ๖ชฑโ:ฑ;ฉ๗:ฒ'ปฒ๛*˜๒A+ชโ่ฟ6ลฐN๛ฐ๊ฒgปถo๛-ีฎŽธzจz;ท;น—๛hˆ;Ž€;จขปนทปป'�ปป~ปดณ{œย+ป—๚ป็ปพ‹3ญห๋ปz๋ภRชฝ�:ตc๛พ<ย๓ฐผพ๙ฟ<ม[ชฟผB{ยWผล_ญบฌร?ผภ�บฤรyผ_ผศ�ผ๚{l�ชz๊ผsMƒ„oฆคไc€ไW~ ˆ@ไW�>ๅW>]b~ZV~วvคฑใ{ดGฝ่๗}ฎ็<ใ;>๋‡3Eไย?ฤพ<�C�์ฯ>๎ืƒํว>ํำพ?์>๎ำพ๎รพ์๛พ)h๙๗~๊ใœท�ะซ�เท|๋O8ำF<๖g�์ภ๕k?๖sฟ๗๗{?๘‡ฟ�jl)�๓3�คณ�ึC{Œ>;�Ÿ;๕ื�$ำm$ู6@\ธ€ ;PธฐC v ฑbE‹1fิˆQCGhiไF“'QฆTน’eK—/aฦ”9“fM›7qๆิน“gOŸ?:”hQฃG‘&Uบ”iSงOกF•:•jUซWฑfีบ•�kWฏ_ณ~ค(ถ*YฐgัฆUป–m[ทVอ†๔w๎[ปw๑ๆีปภC„4 แม*thะ„ร‰7pP`pŠ 7~�!๒ไส—3R `bโ�ธqy’�(R#jพฏaว–=[่Oเ4ฤˆใV=mšชั›B๑p�ฦนiธมรj๕”อIq@ใศ•sn�<๚๔Œเ<1aะํ้บ>Wห=ฯq=m๗๏แว—@ด‚uh๕๛ๅกดE อบ0d>Yfy‘1น†ภCŸMฒะคŸ0FAzl๐ม'ฌ๐ยBภ่P�กฅ ,#/-๓JRจคๅ’‘ตY�ณัฦีxฬ๑"ืๆ Rศ!‰ไŠ‚ฑฆAˆม„/๚ฏ‡,่‘f”PbI‰%"8ฆ”X bNˆaAธ๔L1ษ4“ ฌƒภhx$p2)ยEด`๔ฑว…x�ัฌmฏ" ‹lิัG! „f่ †:๘ำฟfฆดวสf–ศb5 € {Žจก=xเ€ƒ6˜AAิ52UUYuึ"lะHดะุงœ"Lเเล๕+Q๕ฤJtูTฺiฉญ%$,งO“คคR 8อโZฐ5UlrฝVYGญ๕ิsำ�ตืะzภฃ™raม๊Eึ฿๔–ิูg3�ŠึฺƒNุั<8\ฺ๙%ŒLเทข�จฐ!ก�ฒ\’บ”ๆอ1XbFPSC๙…’Oฆฃฎ0ฆc�`„„ิ t,e ี‘P eิq`TTแฅ™nฺ= (h@tFgŒ0ึ€�ธ@C„PB -ฤp‚ ; DฒGฌณ"n9ERุ™-Ÿrึ$ƒ๎�๏ฟูrh ฆHG šย%l`ภ9ไๆ่ ๆ๎ฐฆžn�@ผธj —œrห1#ษ9ะ๔๎‘�I$‚ูฃ๐ืa�.0q hภ๖ sLโ6ธภ3 ,รฌw~็Lxสˆ #W๑�ย=fำkMiูฑฯ^๛ซข!ž3๊>%๑แ“>o’๖พ~๛๕ูo฿�ฃฬ—Š๏๗้ฏ฿�๛Wr}+�๑๏฿��@€4เ˜@. tเ๙?๘)j~ดเ1H ล5ฬเAยกlฐ(TŸQ˜B�„„D1!�VCF% hLcD  Tเ†7ม#ไ yศ'า‡ �‰–ˆ:ฆะ•ดdODใMz !ธภ&7I†Q”�$(๙3สRJ|q”ไR(yIYฮr&7๐ข๑QŽœj‚-{™ห]�าŠa&sYHสFŽA‰%-กM”`เ.ย"Ÿ€!Œเ4ภ6ƒ`…เื�&ˆp‚n~๓šแg9ŸpN!ภ Xณi&Pž)M}๎๓"@A"ภЁLd )ˆA!ะJfพR)๙ไ็E๗ ๒9  !่A์‘…ิtฅ‘ัใDb”ฅฒ์hHUฒั!�3o*ญcKqšำฅั4ฅ๚CฉNิ™�++๊Q�‘šTฅ.•ฉMu๊SกUฉN•6€ฤ9|PRชn•ซฒ €๖‘†ฎŽ•ฌฑซภ |!ˆ'Iดฌmu+ตv(_(“ญoต๋]‰ิ0ƒUx๕๋_‡4€Wุ‚€5์aซŠxAซˆu์cีB€Œซฎต์eนRะ$@fAZฌh–หxฃhQ›ฺจ`\Rํka›ผ…ํmq๛ฐaLhln�–เ๗ƒp‘›�˜�Vนอu๎sก]้N—บีต๎tmsX a^ื๕.Ё�G6`๎wัซฝ๊อวณ(…mำ฿๗ญW>Zฌฑไ›_๖…7/ ˆ‚>ย�G�8v�ฝห๐d|n#๐ƒ�f`ป\ xyุw+ a Lยพ€ ฎQ� ˜iถ KXฏa&ถ‹ฉๆqฯ˜Z.~ nภ‰e˜ฦ=Ž�2C,เฑ‘lฯ'ฮF d`rd)หศmกŽ ˜AˆXlส]vฅQŸf.{™ฬ\r™ั,ล*ง™อ\s›แผภ7*)tvI\B;ห$D`ƒZH`wไ8บ%sฆ/?ณ|๒ํ˜ƒyฉw#?+d†จGไฮKhMŸQ(9H$�i ‰Zoกฎฉ="ูwh/เไค้ผ เเหXฦขiQ�ชำบ>ฺฆใหบ‚‘šืŸnช��kdk —เ…์ัป1๋gdูภF ๐)ืหV–ท ์๔๛ุแ^vนมm๊tk`‡ZธRเf€!ซC]Ip…{€aw $ทnsฃZ�มwาvํkฝ!\เลwbเ‹Cุเ2žฦH’ลš๖ฦ"ฒ.<[แo.xjUำ๋E‘ูรนรf(…\เ€ ย4@)ผ|พ7kNฐ%LไP€Cศ3>r’Ÿ�ป)ŸtตษMp—7ผไสฎ‹B ไผ-ข๖ะฤ‚Œฆb‰’ฎ๔ƒ3�บไfธฺŸvธ\�ิ-ิยก>w–'šํำ];I๔w˜�๊ ฟปสWN๘จ๓ฝ๏ั�ิๅm๊A�ุjฏ๚ฉ๛ฬ๘ม+ๆลn�uyz€9๑๔}9บ฿pฉ�„-_}ฒM�yฬ๒ตg ฿‘v่๕GE�Xzฏžกฝd �‰|฿้ฦzฟoMกํ|lู็ฟwฎ ๆีrํ\ ‹๒๐ว)ถ฿�+ึรฬŸ#ๅy�๕;OŸ�>ศ5น๘r™ra]์�ิฌYค/}ั�ฏ�P#ย/บ็/*โcJ่<E๚&ฅ"/อขO% ฐ%�ย�T๓žH๊าว้$pฝฌY\โ“ 2อ"�lพ€M&2ฏ๕–Z/๖Dฉ*Pั:๖ฐ๏�า๏""B๖Š%˜cšแAฤŒด‹ป/7๗$�๎�*Ub~X/ฑp#จฃ ฎ^€8Œ`๖ f๎ศ า๒. ๎ [ฮzชะ Š๎bฐ�,ฮ`mๅG`์!4@ไ4 3˜ว2hไ04 4jะjzฅ#ฺ๋ฝ@ะำ�ฐ๔2Q๎โะ๏๐ๅO๊็�ŽWƒเคเ๑ท:ค็ฌ 9ข#*€� l #์๋8@ุPฏ/๏Lฑ=qฉNO๒NOๆŠ๑๑JN,n๔��ฆ>‹ฬ€‚9์…�,bถjK!ฺหr คdธi3xŸpOปำ๗pBQวAก6@DƒD}H4Bku  สดยช๔8)cy‹ ฦ+ฤด•"vซทJ๘,lp‡ืBb%–+^4F=`˜ด๛œT�ไ�<`lG%๊สฒŒJฑขช@ขŒญˆหธ<€ kปJ*๋ห”Œฅ)K฿ฏL฿TM‚K1ข{,•* bdรGO=j#ยx*�—‘๊Nณ๔4์’BY”!%Yƒุวุฬ„xƒgŸP“•ล•Y–cY/ฮ๘%~ั—ƒ$#๗๓•kน+๏/kL8 ๎!j�9mษ^3ณ&@98^a0˜1ั+‰y>๘/ใy‘iYœณ™™ัื™ก๙มLgฒšษ๙š’œั2œฯฑ›{†ณา:]๓:fฟืŸณxฐ!clคะ™kแMpฑ2@ถN6 :c”ฦn๓๙&Ÿฑ1‡y?yึžหŸ๕. aศwปtI?ยœ฿3กี 5ษ" มZ@ไฮŠส`ขฯ๋่›ฑ๓ฃ=๚Yธ9จ5คฟ‚wEบ]ัr•“๐บ Z.J๖™?B :โ=ยชU$ดซน็ีTฐjh`zเา` œ…ง›Zจบ?ูy0SฺJš.F:;]ฎ�_z‘๚ุ*z^Zšชณš›ฐušฐ๋:โ �ฐAฑY€“ เ zk าa ข`ˆ@๋ึบ {‘ฬr;๋k‡บnฏถฎปbเvถไœ‘ฉ‰บดqถฃWŽฅ™อฅ;Bฆฑจไ ฺก๐arqท�Aทy๛|ธ'—€œ`ฐaฦ`ฑކญ—Zด?w๓๔ฃAุดOตตBตีต+2ฏ�Zถฝ๛–“†ถงš ฤbฆ` ึ`ถŽWฆ †S4 เ[พ้[์ฟ๕;@่!^!hคAVฦบน7+ณ›ผฏ{ cฎ+aผนขฏ็š๑ๆnปฉŽ=��๚#Zฐ›<G0ฌSœ V\$&บล5 fOเ@Aจ `ม†๑QJ�งbnŸ;7ถั{ฎ9�ž9w1�๗ฆ๖PฤsุxบถQ\จ๗ฎ6+`ฐแZบ Eย_�ศUฺร/ผจ“�*Lzk[sอ3�ยณึ{Cผ`ฆผฝ๕™ษ\๖`” า\'“��ฎ?œQ๚ื"Oำตำ<ต็pศ๙™ศป�๒ผ7ฅym#‚<ฐ�ปอ o5p๎jวrgWฝƒผ@1\ั;/ฝป›ฬ�EvŸฮZ^=€YP ฆ&ฬY!N<ำ‡O'L‡gœ<ืo&BMรดาˆSณศ[}ง�V]/„ฝฝญBุยŒmlด›]atY/dj+บ]H˜=�ล]Yท}�อ]ัฮ=�ี=ธ„}ื™ย�/ชŸx�=1ฺั™)ค=�ฃš�ฃˆ]C ฝั}Z›cโฏ›๙™฿��/ฦณ๙]อฎฆzยC;�qข�^)เ"จฝแ5wี‘ถ?^ž[ย�~)๒]ใๅใกhwCจ �คsยเ"ใ“‚ๆBๆU~†6๗•บWแฮฯŸ�&,พไ1�โbใs>…v>žษ\ฝUอƒืS�œ‰>)N�่ง>้Uˆๅ[�ัKฺ๋g“'pl�(ส~์ต^w#ฒ๋™ปฃ�'†>k�่o>๋ำ^ํ� tร� ๏อฅ~฿ห…๎โ๊๋๐๏„ธ๐ๅc๛�~ฏฟ๛&ฦพ์โ์�๐ฅ*gSฝึแฐ(โ>แซพ๐็�๒�*๊ี‚ไๅ)k์^๔฿*�`ใ�ณ๖Y๖i_ฮ่š0้Yไร›เ5่๖๋‡๗มข�{ ๘มr@‰?,Ž*’}–ˆ†อLL๘—ฝ๙ัฏ&เ๙ฅ=ธฉwY๗„ฑ�1ถ๗I?'h่›ยู๋้๑:L๚5œ๚ณŸุM๏ัฟฒ�ญ?ั��/ี9™a้๑ub�ล�€ภ <จ!กย… :|hp!ฤ‰+Zผˆ1ฃฦ;z�ฒ"C…Gš�4`ศ[U๛M๋ฒZึ‘อบžู๊6Wว‡+ว�Yxq๎ฮFo.N๖๑ภZ9๕m<1๊้ิซ[ ๚หํ‚G�;บำฒใeถึ-ฺ+nไ่Ÿ+ี\|฿๓’™ห'ฬ>{๘ฑร‰็/z�}๘]G`˜™x%้ิาw%�U‚ฦ›z)ืะq�E๘—v?iล!†ชลU^ูq๘_s$˜Ÿ„๖ญ(\‚าว]๎๖ข„ข„`>�8ฺƒ‰๕ O๖—…ข8ู~ด-‰Ÿ‘6B๙�‘™ม�Eป1i_’3^Fุ“ๅ๑˜ŒBศ”L�ญ‡e„ห gœrj)`pg�ุุ‰jf\ov๊x[]zจกฟ)๖'˜๕ฺงsŒ๊™4ฆ‡ฆ|)ฮ‰iฆš2จy็ตI%ฒ๑นpljhsiŠxhชBZ้sHfษูฃ‹rJ[ค‚Jคt๑m lฐ>๖น*‹]ย'y_%๖jขฎ;&Œ๑šก› =หZฎวโ˜�"ญ`v iฌ\*ฺซeิๆ‰ขถยฎหฎi`…ๆ ฑ.ช๗š{n> –_Š \ฝฒ๎X่UE–ฅง�สz0™^�วpธโvGn†๚n�iบใถ‹qฦpษ+i›จๅฅmลุัวรชห"ษํ]œษ3‘ฤgฉฒฬKrjุ�ŒsU+œนMtั2ž&๔&๗˜tหKฃ๖ฆัRO-2ีV_='วA&…ฉึ'/ˆืJ‰uูSซkvฺjฏอv�nฟ w�rฯMw�v฿w�z๏อw฿~� xเ‚Ž๖เ†ŽxโŠ/�ใŽ?yไ’gส2ล8k๓c]=ฌฐฝ*R�a็๑V>y้ฆŸ>�๗ำ–ำตpยทฎ ทฐg๕:๊ถ฿Ž;ึ’ฮ๚๎็ฎ๛ŽyกภใZg๎ฦ|ฦป�ฮช~ผ<{l ึ60้ษ_}๖ื-.ๆ๔Eฟ4ฟีgปฉkj~๚๊o�ดฟ�’K<สโ?้žŸฝฟ~�๚๏Ÿ๗๑kฟเEŒ~‚ขถ วฟ*p0ฃž‚˜*ฮ�วYร9`๋ˆม fฯfณ๚_ฑีฆpDˆแท๐uA ชp…ท๓ธ<่ฟฃๅ๋i 2๋*7:ž…<์ก๖Lดtฝฌ%saKส\6ฤ�n%7๓š ใร(J}ช“ก�š6ล,j‘‡า*ศ?พF0B`qX�ขฯจภ.�$Œlใใˆฦ8ส1}jร่ฦ9๊q|T[rG1๖q„,คฦ�€@~1†lค#yฒจ%R‘Œ„ค%/‰ษัP€& ฺ๙Jfr”ค,eF<‡'˜Kฅ(M หXส๒ ๐-ส ‚ค€•qฅ, LS& 4xD>ภ‘‰"๐๒ พ ฆ3Ÿ้ศ„l๒hุG9Š`0“’ะ์ฆ7๗(อเกๅh lpm๒•฿lง;yx€+ฃฦ๘ Hะ—5๓�์งp‹SA)ภงผ๖้ฯ„*4!เ+?ษอ…Jtขุใ$‰PŠjtฃฆCdF9 า*ฮ ฃฉIO*ธ€!๙ ,#‹!๙ ,#‹!๙ ,z 5Hฐ มƒ*\ศฐกร‡#JL!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,z *� 8มƒTธแฟ„ !*”ˆะแC‹ flฃG‡†|!๙ ,#‹!๙ ,z 5Hฐ มƒ*\ศฐกร‡#JL!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,z *� 8มƒTธแฟ„ !*”ˆะแC‹ flฃG‡†|!๙ ,#‹!๙ ,#‹!๙ ,z�5H€มƒ�)\ธม‡"dHั!ฤRdh๑bฤŒ66๔(€{œ8„H(๒_ว‹ฎ๔ำฅ’ฅH @x !C *(P€ฅ€67โ๔ณฬ_=en€ฦฐ๖Ÿ0HCถ„ ภา95Y:๕ ยC'ฎ\…Xฉ๕ฆ€Kำ^0๘ะ‹‹FจRหถฅหฎำx\ธะ†Yy๗ฎMZ๑mเม…'ๆห˜ใ�l/0ภe—“`ม0A‚„_ฎ—์%๊ฉ_!8p s ‘›7ฆทพฅ7ฏ8@)ภPขำปwงRœ~+L„ )‚ฬ ‚ ๅบแ๒ภ๐ะ ‰หตs/๒t@!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,"zฌะ@ฐ A�*TˆA Aƒ.l8๐แA‰Zิใ?Š9zLrcGŒ4€๐@B†&NJL้g™ฟzส�ฤ๙Q K็ิd้ิ/ŒฬŒ\š๖‚ม‡^ฤŽ2๔ii ฺ0“Jา็าซY™I7’ขาl/0ภี‹kฯค—์%๊ฉ_ทLมPJo^=p€RเMกBฉ0$คฬำ์W ๑zต ๙bู€!๙ ,+zเhHฐ €๐ฏกC‡4จ0!ร‡#NคXq!ฦŒ7์่๑cC"GvผheJ’,?FฌPA‚!$PกขL“'Tˆั ‹%nูJqp%ะ;„ะัŒQ Qัv˜hZ1&ฦˆB “๏”>,HpU่d&LยVส•]ŸF|;กG-l็Ž� ฑฃ@รฒl‘!ท"DžcTRhน๖วษ’“,ษW'„†ๆ น Y&€ Dˆเะ!Ažk-๊•xเ�มN ‡ู๚k@!๙ ,#‹!๙ ,>z๙5HP€ƒ@๘วฐaC #&DธะกCˆ NSฐlะนP๐ @ม?†82๚?!ซ฿ผ!๙ ,Pzฃ5H๐Ÿมƒ!\จกC… :D1bร‰+Fฤ˜ัข@Ž� 6h‚&L€„๘มP5�”)Qษย… y˜�แ๒ +/x8T‹J H„XฉแยŠ[�4…iA‚&Fˆ hัิฯุฬ•8Svๆญะเมƒš1:LLั๋F !๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,$v�Hฐ มƒtุใฤ!ฤม‡#J˜paร‰ก’cb k๒�Mใภ„ึ�แรีม!ฦ—0cสœIณฆอ›8s๊�ษณงฯŸ@ƒ Jดจัฃ; \้ง๋"า‡J™:ญ้ยš%#.ิ4'L ..Ÿช]หถญ�ทpใสKทฎ]ˆ(˜ ๑ฒA$dHaขB ฐดะA _ภ‚ – m‚*T46ธaๅ4ศ�8ฐ„) #Vฬุ๑_ำจ]C€ A„ —q\s#B„ๆะ!๘—Ffเa+Wฤ'Rh๐@eิ6(Hp=ลๅฬ g�AAป‚ูเˆž=<„่ฬ.ฬaโย�๛๘๓๋฿ฯฟฟ��yว&,Q~,ใO=สธQJ* ใมf*ศ ƒฤะ *,„Pย-ถค๐ะ#ƒส„X“‰ j“N=�ผ‘„๘HHก.ย(ฃ@R?�(ฃ„ ำฮ‘๕เ๓ห”K:�ะ๓‡. PษWmเ*ะฅQSคe>ˆคเwTรเ�bL Up=expย#ึ๘๓ฮ8E็$”RR๙‘)*pภม#ฺม€Œ6๊่ฃF*)~€โ-eˆ€š `ษ9jdัI?a„เAOธาฅ@ž‚**ฉ-้�ะ #)ˆอ&,@ร=�“ฦฟ๖๒Dถ0d.บ๊ฒ@$O๐A2ขx0้ศ$—l๒ษ(O* ไN&}B„�4/0๐A/ฤtHโผU&W๓อฤP๙œข๐uะฎฝ๘ ์1รฮ�+�“ศP0ฯฌ๚LตีxpH-Tค` $ฤ็B3zไ@h`๐€@์าJฝหC–[;�ซ๋พ€ƒ P Sส ,€ณWฬsฤ้,ล`L ร>qpš๒๋ฐว.๛์ดื”ืh์SN&,jง#y�3Eุฐณณภ๓ <๑6ˆ5 6ฅ๘เ๚@ป๖๚๋P›GฃHKวำ›|๗฿sฐย-�hF $ไ ภฺz์ ภ pˆ6๏ ƒM+๒๋อ7ืaภ๊ั‚ิ€m<0ิ๐b…|aุุรเq„ ๔เ้@…Zƒฮุ๚ใ฿Iภกฅ์C ›ฉ WศยบฐQ่šQŽFฐภ๖๙�๖–Wผ๐๕,x^๑�V lœใู•+T๑6์Em{8๗ˆใรฎ$ŠStฯด@0c I @ @ดิ‰cิ!@ฐ�Xu%๙ฑo๔:ศฐบ@�ส€ืˆ฿๒bะzฤแ‡ "ฒแƒผ(ภจุำฮTฦ3ฆQ„D4  ,ฝ๐’˜ฬค&7๙“\ม๕ศืถRจรlผ  ภEฮาๆIภ{!rL‰JUv่ 0–ฑภ้ Zr% R€€์อŒ{ศโZ๙สX“|ฤyภŒ‚cฌaไร ะลemจล€JHc›|้@ุ&A ฤาuุภ `ŠคภshK�โฐ�UŽเทE0จP Pุ‚  ฺhIt๓›$็8Xm`ข#œฬจF7สัM"เง‚ GJฺ#]€D?โ€Cัแˆpรdy‰“ฆtฅฤษร1สฆ…k�+ภไภฐaŠ/\Oผb2Aฌฝ4ฆ3}ๆR‰c‡=|แ ^h&ไ,s่a oˆ"+–๑.ิภn๒๛ื>1‡70†~yภ �0ฅคjพพะ…บ�+`#ี6ม?จ`ุ *ฐ@|แwุG"&0W๘ขfE+ซ@ฑ p€ฃ ญhGKZI…€ำ{r่1z€"Jะ‰w�ผร0Xmk_›‚,!upศ:1!ไ๐ ๐ร�ฃ, มhœบ‘‰ `ถตฝ- ž�้Vื{@†?ข”‰5ฆ]PP>L1!ฺพใEฺ๘Cl)1ญ—Jื-A*๔dŠุ๊* ผศำmS ฅ;XF2ิถQ„D!\ฐม๖๐#ผฌb ํ๊฿ุ �ะ“JKโ›๘ฤq1TL# t‡ @ TภbŸ&h@o8œ๔ค MdRภฯฬl ŸกPpf�ย!Yฆฺ{D@œฦT€:่ qb�bRZvIŒG‘้(6„ปภvบc(เ�6ˆนอ•…|cูะฆฮ ฃ=าZ๛๙ฯ€๔PŽ9=Nบ วํณRRูhz›‰�rŒsฯ„าi4Fp-ะโš‘ 2A›๚ิจNตI‚‘Cซ'"{H˜QM !Š „ภMร$ึฏถฐ‡M์@cA˜. 2P’ะม3i$(ษ V!‡”$ค–ษฑ“ €e๛�เทธg‡@ { ุม2€๎‡t€N`7ค0ŒXœJภ4Fส}nูช[�ใธภฮัฮd <ฎ]าVaํฌˆ q่7D^Šgม8”๘ฦ7คppR@C —ศย.‡G<ื�OนสWn2!ข ๐R์�]Œ„*Z ๏ƒผ ฯ <oƒp7(ปŠœ5ฟyฮw�s–[�๊๗�YKแFhเ๋`Ÿˆอย0„\Ÿ$%+)`†A"V ์a/ˆวส!‰ึ@๎{ุ๕๗šผG3yมึA d€eˆE9๎1Œ:ภาpƒ^€uธระ@ธMD๐„7<โฯxวฃ๋จO} =Wl,แปุ†‰ˆ…,fA‹@ถาKพ]๏™ ่~วปใ="ฒ๗:฿5p“์!X8๎พ‹xxC‡ฯภ†1Œ3ะๆ!ฆ � Zะ‚4โ๒๎ †b`๔ฺื่ฆพ๕ฑ?๊ํw�๛ชฯฟ�9ฺd๑ˆk pก‘€ท†เ9 ่x๑Dˆ๑€q. a0แqึqc ุฯA_g„v Pาf…ณp5หท€0fT (€g(๓_ื˜ั[มQrฦ�แะc*๐uเ๛`ษวwฝ๑ƒทT่ ”๐/ q``1xล ะเ ิเ –wy๊เ �ภ b๘ œpq…SX…W˜…[ุ…งทzธ‡ฐณนjr’Œp&~p ๕เใ0ํ•� 9�p ่…xˆ‰8!๒„'zย'$๒ ํ€๘๐๒ˆ1๐$[_ง%<w๏โpืh Q2ˆ!@"˜˜ �ะ0ธ|žฉ๐� ˆ[vเใเ๊๔ส@%_—"s`=๒#A2$BŠฺ[ขภc ลuฐ„Mธ|สv๐ฐ ฿@…Tจ๋ Ž@D•ก€ฒ็Ž@ ่‡~๑py่ะ่@ ะึviๆˆŽ๊ธ์่Ž๐(ัฦ‡๙ด#ž �3|’ฐ+ฌเ c0 ภ f�ž0_‡‘ษ‘Y:x1ํง2pkA ๑ไe�0Q �ฐL"/]ขŠmT’`๕\@Zต7ƒ@ร“ธุ1XIภฤภ/o๐0‚Pˆ0ƒp Aฐp#ะ*เฬpีr-ูฒ-ภ9น“=ฉ%š C`๐ ฉ„Lh<ƒ`B@ ุ้ŽV@์'^ธ|ฟาi0 ่๎ภ๑ภ์๐Ÿะ9A˜†‰˜T ‹)Yu™šช9)I‘ยGwJ4 €jyQ๑QN$ฉฑ9›ตษšร9๘D H7ฐk@i!p€ภรืTG๙uz๓u'ฑ v€6šc>8›6�p„1hณpNhฃkฒ …K„ดีp}.ฐS6ฐศะfƒhqภœฮYCZฒืa.< •เ๗ ำ)˜`wp?@ ๋เ ? เ\ ห&˜Œั/ ะP™๊ภ—ฉ™์@ ”p*kJกŠกสก!ทš6zฃŒ2Y‘5„‘ะQโฤิ9”บ้?*Q‹uAดAจ0ฦv €้P � ั™Š`WNŠ�€ใ5^ลUyิIžหw [๕u๑a๛ Jศ๖pkš ‰ ห0$็“๊ร>ธQUzฅ� _็ยย�ช8ไคI`Lภสw…)฿เ}ะ0PศFโ๘ข*ชœ`ขฆบ™� Mขš�—šฉณมฉžŠš8ZซถzญY‘p‘FJH:คน้ฃพค ˆคHBœQส|Pฉe€Z0ก€ณ(B™›ึu@B@A€–fช‘ฆRนฆK้ฆy†q:ง’๊วน๐ า@ํ„[ิECโฌเP80 E0GดดK<๘คฟภŠ•สwิ๐ กะp1(G ฑฐ "P�€(บ™ะ |ะฑ_ าwฑ€sKrท:ณ4;V�b/0w ฌ!๐ซฐ18‹6<‹ค ตP2ฐ&q8›$มฒ8�# pZญtฏ ๓ณๆ„N€�p๖$7ท‘8ก€Q5ฤcๅ9 ย๐Iรl๋9ธ$ >ฐ—ฮ0 0 ๆ๐ ง5MิdM<`ปตษคฅ0Q` p  ‚€/0! wœ๐" xิ๐ W(o#{๐บC๐ฅบ™š)ฒ์Pm}  XPฃ1น•{นgน›๛65{ปธ๛เX Y “;�ซ=คs*Y`5ดAJ๕X‘•ภฏ™ 9Sp ๓ภKๅ๐=ณH-rEWย๙ณx�ฐVm5@๘p s0vเไ0ผ�๚0ž็สwฐาภ/0g‚S`y |@N‘ภ…“ „ะUuUYตUศQฝื›ฝ-a3ฃ` ๖ะ9็zฉ€‰๛ ‘‹Ž Dฯ๐ ๎๐  |๐>pน?P (:†šษ •หย่๖ม!<ย%|ยึ•ป@ฤ?‘y `�0aใ ผs€ +ยƒ๔ฅ ฟh ,ฐJ<ตMผ"@aใ™อ๋9F ^ท%]1  y#ƒณต_ทฅ|๔e_ฆเฏธ �ภฝ^โ…ˆs‹|ท‹ฉPq^8ยว ‘w@C�ภQ\ z<^๔P^!ลPย\k|ZPฎ—@`ว†nช‘ v\๐ฉ/๐ �—�8Ž}Gi}๐ ิ@ ›ภ`` ิ€ลpqด๚งlฉผสญ,ฤฤ\ฬ7ฑ6˜ดadžั8*ะคC‡ฝั”Aฉะœ1F_€�qPฌgซกึƒมx_๗eaถ•!@`ฺqฮ"ภ1h เ€/{*@eHˆJ9่R&TVษHงล‰?pวฃ!{Wส0Ÿ*Vเ/‰ฟืw่†˜ฯะ//๐”ภ ลp ๙=ั}ัฦ�า.=ฝl*<๖�มฌฑw|ฑ—ำ?vฬjำ�ฬฌ%แำ:�w๊1mw6#ญฤ  ๘๕ำ!ิ่๖ฒกรืโH$uŽเ$0mf๐)เลPฎ๋ชRmT-8�าl�ึ01ำศ‡ำฅลศึข0ถห6 Žิษ7Y�>€6L@s๐ใ!&-{�}ํึู๛ื—‡1q|,ชฎ,ำ Cผก�)•Œw™]ิ’ฺชป8‹:-ำa๗นฝกฒ›�ญ}ฎซ�บ=2-;O(ื<อwR ‰pืวW�ป�สญxีฏ#Žฝ �ฅl�ฒ�ห}�ท��ม2��-Z๗ท^ทi‘n๊�๙�Qสฮอ�ศ-� ‹�๒�pMั�๐}ฌ็z0QWกู�๑�ฦนแ�๊]์=฿ žyฑ๑ญ~1…q G่uฅ๎C–a€๔|ีq€€ ๅBท‘ป1�ฦmเ๊ ใี}�฿=เ ๎�0Žใำฝเ<~uB ค,˜ฒ ๒ hg#=˜ DŽ!ย!")ฑ๑�ะˆ8ฯัH$F‚$Jฒฒ?-ใ=-ๆ7�ิ9฿gๆ๎�ใlพr•r)™ฒ)‚ู*ก2*ฅย,ชขธt�*a+ณR+ทb๚!้ แ9m� oษ-�.โB.๘ํสd>้W-เjพใh^ๆnเm�้ท2-๓2}2็>c38ฃ3ุชRc๊ACS4Gฃkƒ.G`พLcS6g“6?nSjN้ภ��๋๑mใšฎใ๊้ส>n้Lธฃ;ผCฉ|—<จํ๓u๐#คฬ‡�rMใมw๚ํ™.โศ~์›^ฉห~๏เ–ฮ2ิ โyCฤญwวTํ>D;”ํCิEtD-๏yทธ๙ฺ^$่F ๎ฤ๎ำ์�๎^ใ �ะรพ๐œ�โ้๏๘^๒€ๆI $JƒณtJฉดJ๋ิLh�ำ๒ต”3€KบฤKพฤม็-‘`a(าดท�#ฺู„N่ํํฟ๑ฟ�๑๓ฮ๐ ฟๆf^์&_๕ฆ๖Q!5R+ฯ๒5…R*ลR�T2ES6๕๕9ตS)ะS?ไNl‰0`( ŒU้ช]๕Uol๑พ๔2๕ฅm๏OOเS?๋V๘vZl๏๑ฯZฎeŠ#่^ฺฅ[‚ [พ\<8\�˜์!ลQลแๅ0ษๆแ้๗e์dฎ๔๒4!๘ฟฮ๚Pฏ๚$๘ถฯQ‹b๏ชx -ึญธd4ฦ๛๓<9ถc=&๛I/|Ÿ�แ€a&• แY&‘>๒~ฏ๛ฝ�๖o���o๘?๛ท�ค…ฺแj9mh�ร)‰Vเ7!ฺ–mไเQ�6q��฿๗เ๛๙฿B†ai@เ@‚ D˜PแB† >„QโDŠ-^ฤ˜QใFŽ= pi CŽ,๙QๅJ‡\พtฉฆ††3mยฌ8“ๅN=zฐธภS่PขEEšT้RฆM"ค๙’gฬ–4โ”z‘๊S–:๐๐q†%Rค(ฅม"ƒA6l"lเภU๎\บuํ�ล›W๏^[!ฺ�่—/ล,าฃFฮบo่ˆ…๒#C*ฤœY๓fฮ=/ผpฏE)4H‘"eวำฉ?๏Œชฐu žณ�W�๕ส„ปx๎zป„g˜ฃUœุ�AB ๆือ?‡�ใp์„QดDช?V€ศฤm้<่�๓ะอ.จ‚sF๔‹๕ๆ�๏?0ค๚QF Oผz๐๙ๅ‚สgo�๖จ$า6๐€*d&๊L๏xึ+ ๚ถแ[\Cg<3„1Z œ€eฐๆ4wดิข�๘ภว?ไ๑ ฐฮuๆใ^A๒ถŒำ)ร W๛>ยทตถ๐…�๋*X‚�ย;(๑xจƒ๎๘)ิั%~Iพ)ฦ๎6E*VQ/:‡ }€ก#D=1†A\ƒ<๐@Œp B€!;˜+|๑†1L`ศAAเ‰iภMพร@ม 3ไ xุว&ฒp…}4‚]ซป<๘€„N์ร ขAพ<`ใŒ‡ด!ž๐8ภภ_!ฑG"บ‰~ฤกAvx"ธ@†f`‚`C*—เ}4iฎค ™ƒะ=x‚+๘.Kdq‹]ฤ๒ภŒ?p”,� AะŒ6พ‘Žำ$'=ษƒ์๐o$iฑ#\๊’—˜@ย8ฦN in์dร9|ฐL ๗H‚ฐวs .+`d ‚%t $’คค%ลw,ช! ่GBpฬd.ณขอ(tะ Fค@ัุ า#๑ ?€้ษ๑า—~c‰ล eEž๖ิง;9ษ ฐ‚{$ย{�DHpPdรธ€ šก‡„€ …+< 0šป€9๗ไวธbภ@Wmc”โ๘ภ=ู.03B%j"ผ‚ธปh…!1)„:ตŠABrืLฤ8A� ถa‡ ˜ ƒ0‡pŒ@๐  …1FPึณฆีซO"ศ8@จs"มkQฃwˆZPA5   *Uช’ภ+เซ@�ุ‰• ำrญ@6�ูฯ†๖=ุR@‰ล@ฎ0&l!Kˆย<ˆ€ฐ๖oHก!ภ ๖ฑ†ั๐hCwjQKCQุ&ฉฤุ!r=Z฿�w C>Nก,(5!$hi<†ฑŠU ƒ>^R„#พAŽ%~ƒ ุ�i†5ผaŠ8 ฎัPฐ^|ฺใ.แํuษ๚ฆ  ญซX]โ&‘ภล0ฎA3๘ƒ   ’ $G๒aEp`8„6�� lภธ=ธE:Pก…lpxz M˜‚,๔�ณ?ๆ1„\ฃ ๓>สว7ูฐจ-ˆ~ืษฬ!ฤเภ nแM„ก$P)R,ธ YษL†1Hฬ{�ึ.ณฬgณšQ0โ4เGึลฎˆ้แ„E`cc€‡฿ต่`$ ˆฬฃแqˆYfFl ็�นึฒž”Kแƒ „ฅ^zUฐ…S๕pุุวF๖Ÿ๔4€Xฤ๛PC‰OฌLUล4ฦฮcl�ว,�/ๆA™ฉฺใ?ฒฎชnv‘Yะ‰cิ!@ฐŒ) /# โwC๐€ฒGk.๗ฃB ภ› ‚�ืม 'Fภื� กuž†œn์$`ZะD?Œแ>T[C ป� ๏Dบ „n๔œ ~p$�ูังฎ{  qxEhiสธฅV๏LตlŠ฿Sโฏพ€–e๑ํ;้ฑถ?ksˆ๚ภ”@}–(lญซใฆ>ภpฒล>๖žชฺฮŠ8ช rหTiศ ี�qฎ๊ํh�%ˆ„9^[lื�hฦ&�๚r4Bu71;ˆ™ฝ ATึลฃ@n {Hย่@ bŒ@ฒ‡Pj(2ถ~โฃษmeฆm…Pgฐ€ r›xE`ว`ฃม1�v๏�‹Wป€TBพgW๔&A ศžl ล้q{Uษ )y1;0‚ภมhB-`a b?๏ศมnภ4ด’ ดŸณb/มX<›ส‡ฝ๓๛‚ิฟ!`*cภ‚eX˜ „@›b`‡dต‰0Gp0u` dLxฒรภ ,ปe;ป ˜Aธ†1*ฃ3r‰ุsƒ1xƒัzฟƒฑปcƒ}H์;m€H U2„^€ศฺใ>VX†7เ i…ฃู@๘‚ธL,ลjฆ5่Œ ƒ˜:ภ‡S˜ƒ1ฐ'ฐ,6ะ[(ƒ1! 1lbฝ„P-:A�7x€ฟฐƒ=๘‚'๐‚Z4=PAfB"4ย!9ค}„9xv‹<ภ@xƒ9 \๊[ค2œ€ ƒvศ„/จ‚ภ›9ƒ๐@;๚่4Bฤœม7ภ3rK†@zร8ผฒTZฅVสเ6D7„รK€EVŠ์ศƒcฐ--ธ†P�) 8‚g`oHFj …€ฉPฦh10 ดฦk�•H…`h ๊†Lศทผœq˜ƒรk€.`ก|0˜q^๐Š@†LPค€ะPGุ€vิ€€GyิCH‡tศ…~h„L้›ะFn๔ฦLุœ่„w€wะ†?H�ธm‡L€vAˆผQŸzp!•๊€Xžำ้.0ฺณมแ``เา๘วyT €ศwxS€…์ฦ๘F์ูำižLุก9วt\วส‰HพฉศฉวTHuฤK+T๐SH์™0.Pส=ษn˜ *จ"ห"่ H–ฬŽzผว|์€ะ0ำ้ษส†œ(๙ฃ‡y8žไŒศž„โLไนศQฉƒ์X€Nณ[9›pqเnj่ƒ˜ฒLห|Rุ€หรฦา4อ่ i  ๐ ŽQ œkƒ– ุ€ ด8ะ  H—�ฺ�€`้Xศ฿T€ €Š฿บŠ™Hอ Xอึl˜ฯบ๑—ฉฉฌ๑ภ_a˜ ุlฑอŽˆ�ดฒ„เ˜P้\อR€0„กœฃ™อƒอเ˜š€y ฮ�ดrIจ*OŸ!ƒ๑Oƒ PฐPNฐOีj€˜—๗Ldกฮ`™/๙ ˜จˆ๖,!€šิ˜€€P–@P„@J‡}†gpNศัลฬg8ำา!uŽญฎ^Kศฌ(ˆ'LR)ฮ๔ผ‚2˜-{hา๔5_๐‹-ฅ/ˆ^“LŒX#S0-Š1%�4]$3ER— ข‚ำิL‡๘ƒญัฯ์ำb…"R&%าB5Tฉซ…HT…XCx‡vH‡Q0ƒ@9ิบฉำั7mˆ เM1 …>Uq่='ญิSEี*ยั ฐKUTล-ก3เ>(3P‚€?yี^๕ี_ึปq ์@–เxRฝ‰`eึfๅ wŠE %%;^• kฅ‹ ั€ัะVSuึoืkลV–ึพืc+ืŽHWน่ึv ืw…WŽhmRoฝj•W| z•ˆs%ˆeล“ชะW† ืD๕ื‡เืŒhWmื†uX มŠEMXŒ8X�‹Xื•˜X„ญXw X%T‚•XjญุšธุœXุŽ}ุ”eึySVช pนื„อ“๔• Ec“ =bมฮ์ิ˜ˆ v้ศํ pธะPuอdA—z๙ื“ํึ'5ˆ—NŽตฺฎURฎ�‰ืซ๕ZSตฺ'-[I�‘Uูถ-ิ๊x‚๑ศŠ (H…ศษFภŒ(xeภ—๘k˜@Ÿj๖q๏™zะ†ไ๑pMHˆฬo™‰Xๆั†Sจp8K j ษณ พœย;P\e˜๒ชืตU�e]—ฅ�บโีฐี †อฺ�ˆeุต�ฎ�Zโํแu�ไํี …2ิะ�›(€$เb€ƒ/xƒ/๐ภy€ไ:€…๋ช5™ฆjบ&ุา(€~XาX€W†ฑฒ‰@}E„ฬ5}PคŒซB0#22ฃ[ยฮM๒ม�Mื€ตเ�ล“�ี�ด=ฏฝŠ�uื6^>^ฺU�ฎT”กGศ‡Ž m5€Y8พ�๚&จพตk*ุ‚jˆ8ศ,p`Edh‚�ส”  ฅeฺ‚kเู9�ใ6a&ไ!u‰y;4ุ‡rTฐ ˜<[’Š—ƒA“ช ะ}H-X๗ัณt่ณ? ™xpH‡jเ‡Pp ุ!‰b%ฐอฑ‰@‚{ุโ.�b+ศฑฃE-ใ2Tkง#ุeƒย Yฃใๅาแ&d>ฎZ>�ใqฝZfNfฟ˜ๆ฿=[๖]AไB๖ๆk<ไภƒf(<ˆๅG~‰Hๆ_ d h'&ุ,8ศ…O:`พนธŒ�8(€T‡:ภ)ฐ‡ „ช[Zˆฝ ž H‚{`.๖b�07ค๓ๅ,€6iซlะƒcฆc…เ�@†fe~ย‘ๆ]?X‘ei”}f’6�แEแoฆik<€+0†z0†?ภฆ+ถ‰†0ฐ’ศแฅ…ถห_g˜†˜s๘„ๅx€ัxปcจˆหำ€ ˆk€ƒh„]m่+, ะด0nซ \€ว๒<คาแถ[cภ7f่ ้๎ใ—=fB]ุูีๆk^าฝ�ๆภF�š6lฒC€[8!=฿๊ึ iศ�2จ‘ Aๆ๓ใ‡B€IะBP‘9ฌร;ฤธ„yเƒ%ศ‚rP„I’Qฐ0ถŸ กe[v ณๅC2เ.@`�+œ'ฬv`–`‚>ๆป†ใf๖]ฝ.nlถ�ๅfnม^W >์้Nถๅ˜^;พ‰บM…zˆึิษ9™; ‡!`€(.ะ(J0ฃค น฿้†?ุจฺ่�‹ a€,ศ"pไ๒ฮ‡& ;ฐ’�›์ภH๕!Gpk€nSม6nป^ๆh�c—ฎf —n•�Zแ๎™ฆ๎฿0Z๔ฺ 8NcˆฝM™ืคœขํ?1‰ž๙ฌOญP—rY`ซCn€Qฃี€�œOศ‚Yสู๒\aษ๐ ฿ุใ†r”nn�อZ9�๋e~๎–ฮ� ืr๓ŸBgd๖ˆm%�™ ซ$ม- ]ธJ.�ฺแ SM]Sฌต๓% ŒŽ๓่D dถW8๏‹=d=i&M,2'์˜๓EOY�๎๊t…!€NF7Š’}rŠ�rJื๔w…ึก:฿๔่ศๆP'๕R7๕ฬ์SWu™ ‹๖rUŠx๕V“YQ็hu1Gtฃฐ \ฑอ ศ]๕า๑๕ƒ ๋ˆฟH๖๋ภา็h๖eoŽb/ถถe๏8“iAส~๖a?6 •‰่;Hฏ{เƒq_‰ะXnZOT9‘ั”YjA„(w>`ำ€กŽุ๗กิPwvwƒˆxwˆ'„v็Œิ œํv�•pˆ(P†~xeเ1)Y‘๏wง€ซr๋€ƒ_•H’%9voฯŒK•tG/€h˜ŸŽ `œwr'Cั”Daz_&ƒ—y&่ตFy�แฃ‡”p' ›วy—g็?๙ึszy แFZ|^`X_‰Sy@Zƒ)ˆƒ"hTแyPzคyฒ’—๚ˆ•Ra๙๒x�D™๚œว�ัภชQ๘ื4‡ั™ตZ๐„:qœjqs™Zi€G๖˜�ภž”^๘–˜]๑™ํOŸE™`1dม ‹aใh–g‰–iฉ–k๑๎a|วOˆ–ษิ�]๑d —ะo๑”Y�yI™ฮ๗�ฝZ‰Q}ญะŒใG�’}r��ฟY˜3ฝศ”๑I�์ ฺG’ฤฟฯ„ุฌVA`>ˆZึI๏'�%�๑g—ำx€…ัๅˆ),h0AืยdกขB ฐดะ  .Mใก`ƒ ˆ"„ม”4€A… ‹"*p้ฐรE•6PP€ย‰r Jคh‘เL‰6Uเ 9ฒไษ XบT!bC… ๚…ฉชbŸ$$H๕Aี ภไpมetาญk๗.�ผz๗๒ํ๋๗/เภ‚ nค7ฎ8�@”ศ•ฮฝ?Bl8๑ศšฟwใŠl1ฺ๊;‚sBtะ ก|qNšnํ๚5ˆk|x๐ ฺ฿ธั6xธSญžฒ9)*Lงา4ท€Cdใฃ้ 7๘ลษเ๖B”T๏�ๅct๒ƒกj�๚)Sbวญzฺ4Uฃ7งB k�๐ ๓sฒv๚๑ี๛Uม๖vน‘fPlณแ%@%ำ๘`Pw€ƒ›8I•�v�‘ึุc‘MF‚ ช$uึvXbรล7_}95เ‡6้ิำอ‹buืQศw!4๘เ8r๐ˆ2๐5sุžxไ™Ÿc๊)8ขpะAฬ3ฤ ฆฺIv9ฐ‹4�๐ฐภ‚ ดฐ@ยŽโd’ใ•gยน�๒ะ �!Wมฉ<–")lค>/ฦ˜“N๘้W™ภ'}๖ิแ‚ ก‘ dŒIWš=โิ#นT˜#]]ๆO.� ubฃ9q๊)Ž'50iฅ“นucw#Œ๐J;๘เ๓|ฒL6๔๐„+ฎt €Aq !`ฑCg 3Œฏพ;UP๐ม&HL H=ˆŒ1ˆl<l2ส*ใ4nน็ฮ†R‡X๔qศ^ยญทเ†P๔A-€%็ศD*๚€ANp3Aะุ'ง�…ึ\{ ๖ltภrยMจเ3zุเ2ฬ2Ÿน eL$�ฌม›&ฐ aรฏƒ5ฟ�์ๅ1p,ƒIะbซใ_‡M—Tœว<3 x๒๚(”# DF`cฬS ์็%๐๐D'๛XแจNEัL JA�zฃDุีimG=€pAF3˜ธRใ]๛>�2่#Š่c•o?๐!\แ^‰.ฎ๏}™`ๅืถ.5ธF)I* ‹<%˜๓ใ‹|๔cอ�ธ’†UB€ะื ถ-onl0ร@*8A€+�ร จsbC-๊Q“๚@Y๐[tbuฌY ๔ฤจะฬl•�“ฌwตพn ๐#ภVถ!X๏หหเ�@ .PI3๊เ!ะ€DˆKs๓ฐ‹ี•Fฟ�˜ต01c(�ฝคณฑ^2‹— ',๏EŒฝ^‚{ุ ฆ |9์a๛29ัแM'Ž๑๊X 8ม> %ดอํŸšมf75%| G%แ�ถมE�M๏๙๎;ฮจ].>! :˜ษล w?ศm‚คuภม์!‰“๐GX-``‚ฃโ%ศC.ร2PA }/ฟv ๗ธอR*/‚ แu๊ L\Cฃหปลผˆ5฿H่Gh@ํ XŽP!:ฟ๑ ่{ซ$ู#U�žwรu‹ฌ}ใ`{hะ„hผ nmtA�๎๐I3ฃ <…ˆ ขa€HxจิŽ<ไCฝุซ.€ฤ8ม �–!ณภl(€Hงหผi ์ตบธ‰&เไื|E M#oำ๒€ฎ”ข๏๑mƒX 6ŽdฑJรrยฐ 2€ Ž$# 8)๙ฮ‹P 6๐€& KyอAP‰�ำำž �,H6เ‚<์�m•&"ล)r‚Bhฦ rฉ|ชk‘๓ถ๏๛“EŸ(์œ�LๅY^-’๖q_KIX—Y] !แŸ�a€�ัŸ�ตŽ3LCฬ‚9|๓e„๗Aภ.ภั„อ�OE5ภA4ยะจ๋™]J4ิ.ฬ†ผ‚=8EำEูฯ…า4฿ 2QT�‚9ผ€ ลภ(ด@ H")ุ„I C…กpTr |ฤย*กภ6‘ ˜]lเ6ี>ะ^ธQ(ไภ6ย”xแ๖qž €Uš8‚ คq�ๆ_๒NH‡>„ิS ุ,ภั@ญMJุVt๎ฬEไ}"(†"ต๐อ9ฌAtWTภ ฐย2ผิภeึย�ภ\ู์ตยทi@์�‹๋™ฦฃ Rlฏัžu €NุAฐฟŒม‚„ภiฬWดžภ๔ƒ4 Lผหุม|มxมIฦธำ๕…\"ธม  �ภ.˜ƒŒม่๙\0Dสƒ-0KAจ อมLภึภใ�๐€_qฃ7&ไ*ถโ+ฦโ‡แมAึหZฃL‘ข)ขb aผc<ฮ#FเI0Gฮ6vใ|ฃB CBŽCF?BL‚>„€8’ฃ9สฯFฬ,A”C…MเŒ‚5tื์@/z"]๘€๐ƒ3PŽL>ฮ๏ฬ�d9NPxม>”BlะPŒภ<ฯ|AฬมrลกA„O5 Iส#=d ไL >œยŒ8๊ฤ_nๅ�ไ€•U�ฅฤร˜%ZZ^ยฃ^6ฆ๒อศะA`faš�UKF$[‘T2@ใ<Аฤp… ๐œ€ย6lเ–(ึฆm�ๆ]ฌˆx‡˜ภ””@'lว;hรจลhƒ<๘8d ๐†+๐‚„ภ ฒPใ–ฌJ๘โ๑L9'tJ'u~EoXpฤˆAะฅrื8pมEจหฐ5—iLH*w˜‚lภ ƒpัC&œษคƒyฤ: p|@'ิร;˜ |Ztม2ิงP/เฤt"หT€ pงA,า#\Cqฤ€žไƒ}vภŠhnฬ…aG*—‰^pพƒ‹็bŒh‰ฺgธ็hิJ๘มn*CoNฬ่‚ยภค�B0ด€*vC& ๚จ|ยจ)4‹ฌ(N$ภะร0@ไM~๎g†ภˆj‚ฑtรศ•[ิ6ผŽ^็ีฉD‘‰ จ"8รdฌฐ(˜Œ)d๔' DS`†f่Fd/ ช)ค@’.i“>้ŸคƒศF)ƒ^Žฆƒ‰ฺวซPส‹pมaฮTŠbih–j่oŒi=ไฦšip)ซฦAŽช?”ช\ธ(•Šไ~.h � yvCoฤ คƒ‘€š|๑ƒ.|nN+ตึๆYH@Y-U@ภXD4Ztk pภHPฃฉณh@0€Uฦฏฉb4�B\˜F4EsฅklภˆE”E]ธิฤKฤ„ฮย@ยV|jภด”ห4มBEHฤX์ฮaภZธD ์ท>ลUl@HJ่ซผา๋ฉ€THภ"EO(ลn๋ป4ฌM˜„Ž8ฌธยฌDP,!%,Hดฤ`ซ h๊ถ~,BภVŒNmึ\เ์ร๊,อš•4LดซU,’Oไ์I$มยฤ6ฑŒ•ƒธ+ึ2”4€ทVภXHาีศL]@Lฌฮ’ซฤบD ,Hlั’… ,€ป>�J€Lอิ� ฯ%ญ฿ฦ,ลj ๎ห๊ฤ�.b”„Tฺฟ๎�mมลJ8์วN@ๆŠภๆfอLฎธJE,ุ†@ณฌEท~Eฟ&@ฯภโชฯๅ=คมcV+๐/ฉC�๎_˜FJ€๐C”AฌVoJ\๒aอhฮฝU๏_ะ%`(/๓V*ไIล๕>‡NoAฐ]N|ˆ/ž‘oถ˜ฏJไVแ€ƒ.\Š`˜เ_ภ๏^HQ�ชoฯ1ิ^Ho^ฐ_ิ™๑�ฎGฏNเเ�€ะย/ค๐Vฐ_0ตภ ุaAoต4K#ญฮ‡šsฐm‚p-ˆ๐ต ฐ “š—ZC:ˆยะ#ืฐ OK0C=hยDํ �01ห่D6ถFถd� F�ๆO+c์้ใ]iŒpWฑ gD–๐ฏo1wฑ1‡ฑ1—๑ใน†` ถจ1ณ1ฃฑ_๐k˜ฑวฑ฿๑ อ1`ิ1ต๐1๛q ๋1Wซ 1gใ 'ฒ"งฑ†i€ุ'๔คด�Z‰Qติ1๒.ยฑJ`ฒต€rAXrJp๒'›2!๛šจyr^�b๕Šฒใ2*๏1+/ฒ-หq-๓ล#oฃ.P๒`rr%๒sฒ0ฤ,๏1ป^)3]$๓ด3bๅฒ]ธ2"Cณa�โ7๒-wณำ็mฒ]๋ฟฒ!MP„\ป-ถฆยฎ$-�-SŒ๒kฐ ลKค€€ˆ+นŠY่3[tlU@CPภ(<ฏ$€ร:ฤะดล[œซ\ณEลH-Š:ฌHƒBWฎJ˜ดํ&ด5s3Eทด_3,ซ1L๋qMcฒM;3=[๓๎ีLw2O๛ด+๗ด({๐3ณ4#๓Pห๔M7uNืด7G๕ y< (,SืณD(o" ชคˆiฌW ฉWw*Q �‚-hชภ‹cdฒฤษœจขx€ควŠฺ‡*ใ‰žpGŸ`ฐ‡>‚1ด5ช"=”XขnFF`dHF”€5˜‚ ฐฬ#< | � zจ{ธidJ„์Fo�†`Ÿ.•vฐTก*๕);๕KณSƒ๒lCตP�๔m๓ดmqK+ต๖55๏to๓vPืถq๗PKตr[ห€‚?ะBˆ€@`ตภขdH�อฯ˜iX7vs…\้@30B ˆB4์ภaŠ‘๗ก ฑP๑ˆ”ž%Œ/@#(0ƒ่‹5[Jฯคj’Pฐ - L@�Cจ ป<ฬภ€i น KMย ั๘%�Lฮ์ฬษ;peบเฤคfุ ]�ทjR#๔ๆฒ8wkทธqว8Œ�๖kwหxk฿83 wl๓M็ถ6#7Žyp/�ท‘๛…Eำภ#ไรrVว๖ไaี]Qืuw5I91LFA>œ‚>`A.๕ธiฌท^Yแ{3๓คั#ถRภผ†Iก”Jฑ]แ๎ีัฬรlA:,Aฬ8™น >€i(‘Vม>–`A•,›ฦนa’&)TƒE1•ํูีๅีลœczฝrฃ#ฒoguฏxl‹9‘y575qŸบuฉ 95ซ๚Q๋6Žร:‘9ฎ32ญ์C9€oŠ฿0VชIZŸu๗– žB�K)๘@2 TAนฦา�?ษ  žภ8�•ษ•ื่[q€cลภ—ม `รŒมฐigy�hE“ุeT dฺฆํƒดนภœ[wQ’Qvฦ‚X0Fณ๕tเ…๛ธซš x0+ใ๔pC๕ร72ซ›บ,฿Er๙q 7* ฒ&S|Ž7sL‡�ฌ#ตษฯ8ษป6ว‡|R็บหใ…iP@เA3 ุ๕Vwร๛รํ”•;รx-`ƒอ…z{_ฅตfkL™ต—1ํ‰ัŸ=แ|;žฑlฑว@ะCผย!ะ"๔ญ)€ญแ๖�ภษฺHiจโ1ž�ฺฃ;[%eม=(Yฺ›BZฑ|�aƒธปร;ผK๑งบ%Ÿ|จ[<ซป4ฬ>‹ผฌ+>จฏ:จผงงผสท�ผŽ#/ฤ?๕Ž'�ส็ธQฟ<้ใล\1ิ๔œ ฟฟF3ตk%*R๖ํแบ1@%†ภ.,ร"ุ€ag~ด7รฺ,=�ิ@แำkปk์ฦ H!š๙œOโภ �(ิ(R่ž ศ�อ_ถGฝ€{hƒa๘waœP’คS:๚€$R"1Œ€ฆฟP?ฉ8@h8€4˜aC‡ >”8‘"Bˆ ิธฑโล;†d๘‘cBAR$1ไส"ฒ4 “ๆห‘.qๆิน“gOŸ?:”hQnข"‰\ฒ—จ ค~qH„เภ�ฮ5Dn�ˆ:ต๊ี!๒ฃ’Bหต?60€Œˆ3B๘ี๗D๐`Dกƒx็ญ๗S’8ฆW`SO}ฏาV�‹จdU!๒่;œ,%ISMGu๕ๅ•%ญyี๚Zพ4gœูƒoฟi-ฺ่ฃ‘N๚ั๚ขš†‡ธH‚ZฃฆŸ�่ค�น Vbิ'LS่‚ซ?ย@j—ธ^งnะ‚–_TH'ณ Šซน]2๛'ฝ‡ry้—ๆ๓ูf‰–Yp�็ฉ๐ฏมnuงฦ/ต?ศ็(=˜ƒZ� กWฺ๓ฯA�?ชฯฦ‰tลMžี๏Xฃ`ฆM†ศกt™Ssอ‹Zi๏l๏�๗฿_ี๘แ‰/�xž•�”rXอพภlพ��;�๏ๅ'ว”๚�1’^๛ํฟ‰๛๏ม_|๑g฿�๓ัO_�๕ูo฿�๗แ_�๙้ฏ฿�๛๑ฯ_��๙๏฿��<ญ€4เ˜@ข์z tเ!A NP(ตƒ™ฃฌG3ฺญ”ย™เHตม‡edUกโQ˜BฎPVแy4๘B๓x ?,sก fA๎‡=๔ก ;—CๅฅLg๙)ข BD "1„?tโก(ม-RขขbฬศD็ˆZœbมF1๚๏‹ _˜ล&�N^๔�๕“ฒN‘Žuดc�ส(ฤ ฮ๑‚&‹กฯฺ่ฦ ~Pfิแ™HE/†d�Yฆ�จฤฐ ’doL#%-นHMn’“ปž#UIT�”‚ผ มƒษCv’•ญte๎pืลIF๊�SI&g‰ฤร�L”ฅ4!_La3zŒ{#งP™ห_ฦฒ=๔ฉb,ษฃว-๒‘˜ีดๆ5yฒ ๐๋W�„ผูอˆ€›แg8EN 3Z\[4ฒ8x6ฐซd‰ๅๆIO›`“Ÿ�๔งF60‚\�ƒ ๒๘‡<�qЁ”ก๕P(Azะ„.4ข๒p่@ *QS  ธ�็GAาW-O0้IŸฐƒ’ขิค*eiKWสR—พT ๅS฿'EšS๎ิ'c�๓โb7’�ํงฯ€P�?BT็จ �๔ษSฉN•ชUต๊UฑšUญn•ซ]๕๊WมVฑŽ•ฌe5ซŠ ผmไYลzภขฅV‚A ฎใ@วVนึuwูฑrB ˜เฉg5์aญz<ws nQmhข๔˜ƒฦpp๘cnธ[ ชQeฬม^ศ์f;๛Y%…vดฅu‰เ๐ุฑตตm5ซ€ิ�๘… ๛แtม๛เร!–ฑƒไผƒ9ฮŽt0ณฮŒ@9อ}NtดSณธไ ๐-ส ‚cั๖ถๅ5ฏ")ะƒGXฃ‚ ‚L๐ก•งY ‡€ภ %(กฝN=(ฒแ�ƒฃ/(W/ˆq‚lC�ฐ€ lเ ˜๒-�ณh๐ˆ||ฉไ=๏‡A F4ƒa€A :@^๚fม๘mฦฒฐ55lฐวjะ =๐`+m` bผ†‚ิ๘ฦ9�q™Q–@ร>สQ(…˜สUŽข.SŽLa'+พฏ šQ฿{Dฦ16Ž|YฦCถ๑™ำ์c%ถxhF9มอส}๖ณ Cเ !ํ๘EB`>oD8ฤแšp๖�^เฟา€pฐ\c ฤค+}้dzำvษฎ`Œz@†N๙๓ซa=A ฌK.ะ…0Zํ’F�วIฮršs]่R‡3#˜@uƒ��์rวยAŠR˜โ๊XO›ฺ ์ภOsง9ฤNxาŸ�(%YcPO’๋ž8๎D•{ถh3Pตแ๏FฬGฅ˜ล~…1…ฌ^zฝXฦ6&ฏฟ" 'Q’๗มพ@ะํAธ�D�‘ˆ2แท8U้จ‹oœใฯ8ษ:r‘฿ฯ>›ป]MHxŽœๅ-w๙หas™ฯœๆ5ท๙อqžsฟœ„ข$แ2งiœฺs็EฯนGq(I”้:๙Goซ`ง ข>u T�๊Y๏&ฑ๔ๅsข7์๚F\ŽŒโ;X‚ฺ3J!ค}�ํm;D *wบG”ํEฎ2W^vม'�„IeมKƒเรณ4&โxว#~ป๕tแเ1pnDฐ๐ผ๎ๆ |�๓Y๑H๚ั“ธF�็๏ฆ1TŽ=๓ต‡๕5rธๅˆม�!๚น๋>๘ฝ��๎ :|ไ๗žหอผํก_๑c…*้๓>S>ตฉœผ้œยi+•?อ3•(n# ฃ*jข*๊ ๊มK@!t!Rฎ'H๊ฅR*ฆPjR,B9T(|jฉ๔ฉ6bา:ดDyขจ�ุฒ Š๊AMดEรา R4,9พ Y\ิEฅcกฉ๎าก #.ขqซ1G็Sชเแd(๚๊aภ@l.`ปIแs์!tD ๚ ฏหด.Fิ๊D(@ Gม5 BษQKแ“๎เคภMภtตD‹ดฎๆด8หณ�ค( 2ซQ€แt@5@bS1bุคkจ ุœ‹QOdva5็ฑต4ยR1๕M4`ภ ์ภฬำ,ภ8` ข‚ฏm๛๑;•:ยvr@TŒว8`อ‚ฬอn์  œFlไ2!gต7?๕Vy@W#’�ฦฦW�ฌฬ�์ ภ€ม โ!#๒H—ี4u[1‚TGMำ8อำ@ ,ํCฦjกrต . ห1\K3ศ ล,lุบ :Œ ู&UE/ วฬ/4ย#A_MS5ทก5ฑr�”แ RเC2 �t‰*ฏ  $O’aฎ@!จ&` ฺบ‘฿2`L๗ เr๖ HS#Z๒%cRdŽd!ะRมแD%"IแV„$ฦ'‰ƒ?ปษ?ฝiœœ๖W Vœิ ช๖Wคv�3nt6ซ4€ฤ๖^wˆ ธ ฉh\ถ@ L+7โj‚๒-”ก”ก๔ก2 ก� o-Joดo+๊ข๐Nฃ†๒kฏŠ0aqhฉุริ*o0@+8COjฆdสraสB17C5๗B-ดฆซ 5ŒJŽrdQE9ภงย2,ET#” จŽ*r“ฺ๊@4จhw#b—ฉœjtฟVฐ�6)}WK) พ b‡wV@๒ก RภธFyฐ|ฦfšr@z{0yฉ“ @๚ ฺM‰ะ%–V-r€|‹{W๐™Pฎ~เ~[OC ™๐Imvz&1 —ฐ ูQb฿'’€J B ฉ bJ22€ธ ๘I้}&เสA�Dญผ ?ขฦเ `๗ƒ�ษถ‚๋‹โg&€1m0๒„cx#f…9†x}’ Cพโpฅฦ[� J๗‡้ะ†‡S๗|’„Tก๒๘ะ5โj’•ืฆื @‘˜้T~pฯžิQaUโk#ฤ8ท˜�.ธ}:€@ฐภGOc$‘Ž๗! XดŽ๏˜SืX๐,™xเมสฦี๐ตtฑF‘๚�7‡ว'I์L3?BYฑMgมdg%4๙„#น้cjฟ ่jW~8QgษŸ 3€g@5`ื6ข’เHธ{z8l@ก฿้์ ฺๆ,:Ÿˆ<:a:ข‹G vMกลฎ 3:ไzฎoฆฐ8:ค‹F+อ๔4* Dภร\ฆ'ๅไ์3ฅY๎งg%>:ešVภŒก>$ ้ก tšขIๅ–จ7ฅำh!ŠบฬŽš`ทoฦๆซฝZญภšl๎ฆฦxph`zเาภyrขค-Z„&ษช5ซ}zง�มีก!:nYเฏ�Zj�Zนd๙;ฐ5bฐY๔ เ nq าa ข`ˆ@;ใ๚ฅ+zPืU๐ฺฎ5ฏณร#-ๅ๘Zค ฉ ชไ€วฺก๐aˆฏต�AตY๛\ถ‡XM่ม Hcฦ`ปŒข‡h”l ด=ปฺ�๙q~nด7ย€๛Z#`ฆ` ึ`ˆดVฆ †+งปบฏ;ปทปปฟ›d€โเ๒€Aช&ฎ‰[ณมfฎ…&น’O:"๔:ฆฃ[EJj  Dg๗œ๊j$๘^„ jไ๚”xiล5|ไนˆmพ‡ฌซeๅj๖`JBฎไŒ3eซั#“–ปล?;ศeˆั0๛S@œVฎๆ๚e_เyหSซ1›ฎYeh„�ๅผˆ$R’ผhf%IZช}:‘๛ส�,หcœ8บM{xยœงUฎfฬห่žฃฆžฟๆๆN—v�|ะ ฝะ ป�ะฃ’อฝ'฿ร$ฝัSนœฦ%�‡จ$๎IB9}ำ;‚ั/}=bZN&"=ิAฑe์่4๎ิQฝU=ึฎ=@�ีCqฅMŽhp=ƒ>ศีm] Iฦื�7zอ-}ุ�T*�ฟ‘}"�Qืจ™ฝูgฒึฅฝฺญ�ฺฑ=�ต}�Y0uสq็�น�ๆผรHžฬ}�ฎ�ซV,H�ีๆุฃ๓sณ�=�‹n�{็�ำ฿wVื๛]@๘����฿�a � พเษณ>@ž�~d~แ>�~โeN฿m'โ5^’�โ!ใE�ใ=ไ%��I>ๅM^�Q~฿Wพๅ๚ๅ;>ๆe~๐Jtr�ๆyพ็}�็>่…~่‰พ่�่‘>้•~้™พ้�้ก>๊ฅ~๊ฉพ๊ญ�๊ฑ>๋ต~๋นพ๋ˆtภ8ข\(& ฌแ๐jvllใMyโŒ@'ฝพใเ �๚Aศ>@Xธ๛pฒ‚`ซศ•'\ภธ1๋>ซ€I‚`�ฅeM ๊๘ Z€v!_$ฟ๊Š%+ขฎpข— ฃ๒ฒbGjๆJเหข./�`YโF‹_ซ`Kถ๊ฐ�`�AดžD5ำ„A–q_๗y๒c PกยJเlเ\"ITก‡ ขา�„แp?ภธค”aN^ป๊~กƒg�ฌบ๋ปยห;]"[ฯA ฒ ธรzเ \A๚uร�?�9ใOBG3F)DE�a€ย… +ŒPๅ*D…‚€๐4‡ย ๒0๛รๅ $*iFL$&2|� 3ฆฬ™4kฺผ‰3งฮ<{๚� 4จะกD‹=Š4ฉาฅL›:} 3A๒หTรL—ฆฝ`๐ก1!6<Œ่’ซWฐb=„@&฿)}X: iขฤTสธัรกZTR`ภ@‚‡ .š้ษAขƒdจ”+[พŒ9ณๆอœ;{� :tR  ูWฎˆ 1Xสศฑ ณ"6๒ขU่๖ูด'L˜„ญ”ญxฯ๎]่๐ฦท�i ำ‚DB๕h=ป๖ํ�ป{�>ผ๘˜zเiVฎ Zwๅ{vmใi฿ว๏�ฃถsGlฺ>Ž�_ื)”ภZhา1JL‚cŽ�a„NHa…^ˆ!M\aL=ฦ�AR[]’อ 0€หXŠy0 ,0˜W%ž˜"Y€b ห,bC .ลฤ‹ดฐ# Z‰9/$ฦฆฐฐมศ๘ใ=™%b],ำญ)งย oผ๒ฮKoฝ›5ฉWยˆ )`ซP @A *่หฏฟ)Lะ€"pะT*จ`“ภงฉ( €รl @;,‘ˆ4๐$ษ?ฺหrห.ฟ sฬตพGkฏ/ฉลCอาPsQ‡ษ tะBM4ผ8๗์^FHอtำN? u†�LM5ีดชAึZoญAิ^ vุ-WM๖ีฌrถุjฏอv�R“m5ึhoํv�v฿๗gpว}๖�Z็ xเ‚>ิ�S›ญSฺE๙ญโ6ั €฿Nyๅ–[n๘?ˆ็ไ๘PŒGฮ5NกKž๕ๅฆŸŽ๒:�™oฮ้tw บ๋ฟค๘� ษŽ{้ฉ๏ฮ{๏Bฏ.S๎บ ?y๐กว9๒ฤ�๎{๓ฮ?/๐1฿๘๒4ี~�๒ฒCฯ}๗�w)=Lิ+๛๕ูŸฏฝไ฿ฏฯ~๛฿ญ;ํยW?ฟ๙ษ#๛็ป๎~���ฏ๘Mฏ~ใณ_๑๐—?์ tl x”๐ษ/}ใจง=jpƒผ‰[Gม N}ๅ#�:ˆยr๐ƒ <ž ห็ย’†"Tก oศ>า/†!< ๑๖‡มโpˆD๔•7ฝฦฏqทSŠ๎jrย"JqŠจ;b grละD‘Š\์bเ!๙ ,#‹!๙ ,$y�Hฐ มƒtุใฤ!ฤม‡#J˜paร‰ก’cb k๒�Mใภ„ึ�แรีม!ฦ—0cสœIณฆอ›8s๊�ษณงฯŸ@ƒ Jดจัฃ; \้ง๋"า‡J™:ญ้ยš%#.ิ4'L ..Ÿช]หถญ�ทpใสKทฎ]ˆ(˜ ๑ฒA$dHaขB ฐดะA _ภ‚ – m‚*T46ธaๅ4ศ�8ฐ„) #Vฬุ๑_ำจ]C€ A„ —q\s#B„ๆะ!๘—Ffเa+Wฤ'Rh๐@eิ6(Hp=ลๅฬ g�AAป‚ูเˆž=<„่ฬ.ฬaโย�๛๘๓๋฿ฯฟฟ��yว&,Q~,ใO=สธQJ* ใมf*ศ ƒฤะ *,„Pย-ถค๐ะ#ƒส„X“‰ j“N=�ผ‘„๘HHก.ย(ฃ@R?�(ฃ„ ำฮ‘๕เ๓ห”K:�ะ๓‡. PษWmเ*ะฅQSคe>ˆคเwTรเ�bL Up=expย#ึ๘๓ฮ8E็$”RR๙‘)*pภม#ฺม€Œ6๊่ฃF*)~€โ-eˆ€š `ษ9jdัI?a„เAOธาฅ@ž‚**ฉ-้�ะ #)ˆอ&,@ร=�“ฦฟ๖๒Dถ0d.บ๊ฒ@$O๐A2ขx0้ศ$—l๒ษ(O* ไN&}B„�4/0๐A/ฤtHโผU&W๓อฤP๙œข๐uะฎฝ๘ ์1รฮ�+�“ศP0ฯฌ๚LตีxpH-Tค` $ฤ็B3zไ@h`๐€@์าJฝหC–[;�ซ๋พ€ƒ P Sส ,€ณWฬsฤ้,ล`L ร>qpš๒๋ฐว.๛์ดื”ืh์SN&,jง#y�3Eุฐณณภ๓ <๑6ˆ5 6ฅ๘เ๚@ป๖๚๋P›GฃHKวำ›|๗฿sฐย-�hF $ไ ภฺz์ ภ pˆ6๏ ƒM+๒๋อ7ืaภ๊ั‚ิ€m<0ิ๐b…|aุุรเq„ ๔เ้@…Zƒฮุ๚ใ฿Iภกฅ์C ›ฉ WศยบฐQ่šQŽFฐภ๖๙�๖–Wผ๐๕,x^๑�V lœใู•+T๑6์Em{8๗ˆใรฎ$ŠStฯด@0c I @ @ดิ‰cิ!@ฐ�Xu%๙ฑo๔:ศฐบ@�ส€ืˆ฿๒bะzฤแ‡ "ฒแƒผ(ภจุำฮTฦ3ฆQ„D4  ,ฝ๐’˜ฬค&7๙“\ม๕ศืถRจรlผ  ภEฮาๆIภ{!rL‰JUv่ 0–ฑภ้ Zr% R€€์อŒ{ศโZ๙สX“|ฤyภŒ‚cฌaไร ะลemจล€JHc›|้@ุ&A ฤาuุภ `ŠคภshK�โฐ�UŽเทE0จP Pุ‚  ฺhIt๓›$็8Xm`ข#œฬจF7สัM"เง‚ GJฺ#]€D?โ€Cัแˆpรdy‰“ฆtฅฤษร1สฆ…k�+ภไภฐaŠ/\Oผb2Aฌฝ4ฆ3}ๆR‰c‡=|แ ^h&ไ,s่a oˆ"+–๑.ิภn๒๛ื>1‡70†~yภ �0ฅคjพพะ…บ�+`#ี6ม?จ`ุ *ฐ@|แwุG"&0W๘ขfE+ซ@ฑ p€ฃ ญhGKZI…€ำ{r่1z€"Jะ‰w�ผร0Xmk_›‚,!upศ:1!ไ๐ ๐ร�ฃ, มhœบ‘‰ `ถตฝ- ž�้Vื{@†?ข”‰5ฆ]PP>L1!ฺพใEฺ๘Cl)1ญ—Jื-A*๔dŠุ๊* ผศำmS ฅ;XF2ิถQ„D!\ฐม๖๐#ผฌb ํ๊฿ุ �ะ“JKโ›๘ฤq1TL# t‡ @ TภbŸ&h@o8œ๔ค MdRภฯฬl ŸกPpf�ย!Yฆฺ{D@œฦT€:่ qb�bRZvIŒG‘้(6„ปภvบc(เ�6ˆนอ•…|cูะฆฮ ฃ=าZ๛๙ฯ€๔PŽ9=Nบ วํณRRูhz›‰�rŒsฯ„าi4Fp-ะโš‘ 2A›๚ิจNตI‚‘Cซ'"{H˜QM !Š „ภMร$ึฏถฐ‡M์@cA˜. 2P’ะม3i$(ษ V!‡”$ค–ษฑ“ €e๛�เทธg‡@ { ุม2€๎‡t€N`7ค0ŒXœJภ4Fส}nูช[�ใธภฮัฮd <ฎ]าVaํฌˆ q่7D^Šgม8”๘ฦ7คppR@C —ศย.‡G<ื�OนสWn2!ข ๐R์�]Œ„*Z ๏ƒผ ฯ <oƒp7(ปŠœ5ฟyฮw�s–[�๊๗�YKแFhเ๋`Ÿˆอย0„\Ÿ$%+)`†A"V ์a/ˆวส!‰ึ@๎{ุ๕๗šผG3yมึA d€eˆE9๎1Œ:ภาpƒ^€uธระ@ธMD๐„7<โฯxวฃ๋จO} =Wl,แปุ†‰ˆ…,fA‹@ถาKพ]๏™ ่~วปใ="ฒ๗:฿5p“์!X8๎พ‹xxC‡ฯภ†1Œ3ะๆ!ฆ � Zะ‚4โ๒๎ †b`๔ฺื่ฆพ๕ฑ?๊ํw�๛ชฯฟ�9ฺd๑ˆk pก‘€ท†เ9 ่x๑Dˆ๑€q. a0แqึqc ุฯA_g„v Pาf…ณp5หท€0fT (€g(๓_ื˜ั[มQrฦ�แะc*๐uเ๛`ษวwฝ๑ƒทT่ ”๐/ q``1xล ะเ ิเ –wy๊เ �ภ b๘ œpq…SX…W˜…[ุ…งทzธ‡ฐณนjr’Œp&~p ๕เใ0ํ•� 9�p ่…xˆ‰8!๒„'zย'$๒ ํ€๘๐๒ˆ1๐$[_ง%<w๏โpืh Q2ˆ!@"˜˜ �ะ0ธ|žฉ๐� ˆ[vเใเ๊๔ส@%_—"s`=๒#A2$BŠฺ[ขภc ลuฐ„Mธ|สv๐ฐ ฿@…Tจ๋ Ž@D•ก€ฒ็Ž@ ่‡~๑py่ะ่@ ะึviๆˆŽ๊ธ์่Ž๐(ัฦ‡๙ด#ž �3|’ฐ+ฌเ c0 ภ f�ž0_‡‘ษ‘Y:x1ํง2pkA ๑ไe�0Q �ฐL"/]ขŠmT’`๕\@Zต7ƒ@ร“ธุ1XIภฤภ/o๐0‚Pˆ0ƒp Aฐp#ะ*เฬpีr-ูฒ-ภ9น“=ฉ%š C`๐ ฉ„Lh<ƒ`B@ ุ้ŽV@์'^ธ|ฟาi0 ่๎ภ๑ภ์๐Ÿะ9A˜†‰˜T ‹)Yu™šช9)I‘ยGwJ4 €jyQ๑QN$ฉฑ9›ตษšร9๘D H7ฐk@i!p€ภรืTG๙uz๓u'ฑ v€6šc>8›6�p„1hณpNhฃkฒ …K„ดีp}.ฐS6ฐศะfƒhqภœฮYCZฒืa.< •เ๗ ำ)˜`wp?@ ๋เ ? เ\ ห&˜Œั/ ะP™๊ภ—ฉ™์@ ”p*kJกŠกสก!ทš6zฃŒ2Y‘5„‘ะQโฤิ9”บ้?*Q‹uAดAจ0ฦv €้P � ั™Š`WNŠ�€ใ5^ลUyิIžหw [๕u๑a๛ Jศ๖pkš ‰ ห0$็“๊ร>ธQUzฅ� _็ยย�ช8ไคI`Lภสw…)฿เ}ะ0PศFโ๘ข*ชœ`ขฆบ™� Mขš�—šฉณมฉžŠš8ZซถzญY‘p‘FJH:คน้ฃพค ˆคHBœQส|Pฉe€Z0ก€ณ(B™›ึu@B@A€–fช‘ฆRนฆK้ฆy†q:ง’๊วน๐ า@ํ„[ิECโฌเP80 E0GดดK<๘คฟภŠ•สwิ๐ กะp1(G ฑฐ "P�€(บ™ะ |ะฑ_ าwฑ€sKrท:ณ4;V�b/0w ฌ!๐ซฐ18‹6<‹ค ตP2ฐ&q8›$มฒ8�# pZญtฏ ๓ณๆ„N€�p๖$7ท‘8ก€Q5ฤcๅ9 ย๐Iรl๋9ธ$ >ฐ—ฮ0 0 ๆ๐ ง5MิdM<`ปตษคฅ0Q` p  ‚€/0! wœ๐" xิ๐ W(o#{๐บC๐ฅบ™š)ฒ์Pm}  XPฃ1น•{นgน›๛65{ปธ๛เX Y “;�ซ=คs*Y`5ดAJ๕X‘•ภฏ™ 9Sp ๓ภKๅ๐=ณH-rEWย๙ณx�ฐVm5@๘p s0vเไ0ผ�๚0ž็สwฐาภ/0g‚S`y |@N‘ภ…“ „ะUuUYตUศQฝื›ฝ-a3ฃ` ๖ะ9็zฉ€‰๛ ‘‹Ž Dฯ๐ ๎๐  |๐>pน?P (:†šษ •หย่๖ม!<ย%|ยึ•ป@ฤ?‘y `�0aใ ผs€ +ยƒ๔ฅ ฟh ,ฐJ<ตMผ"@aใ™อ๋9F ^ท%]1  y#ƒณต_ทฅ|๔e_ฆเฏธ �ภฝ^โ…ˆs‹|ท‹ฉPq^8ยว ‘w@C�ภQ\ z<^๔P^!ลPย\k|ZPฎ—@`ว†nช‘ v\๐ฉ/๐ �—�8Ž}Gi}๐ ิ@ ›ภ`` ิ€ลpqด๚งlฉผสญ,ฤฤ\ฬ7ฑ6˜ดadžั8*ะคC‡ฝั”Aฉะœ1F_€�qPฌgซกึƒมx_๗eaถ•!@`ฺqฮ"ภ1h เ€/{*@eHˆJ9่R&TVษHงล‰?pวฃ!{Wส0Ÿ*Vเ/‰ฟืw่†˜ฯะ//๐”ภ ลp ๙=ั}ัฦ�า.=ฝl*<๖�มฌฑw|ฑ—ำ?vฬjำ�ฬฌ%แำ:�w๊1mw6#ญฤ  ๘๕ำ!ิ่๖ฒกรืโH$uŽเ$0mf๐)เลPฎ๋ชRmT-8�าl�ึ01ำศ‡ำฅลศึข0ถห6 Žิษ7Y�>€6L@s๐ใ!&-{�}ํึู๛ื—‡1q|,ชฎ,ำ Cผก�)•Œw™]ิ’ฺชป8‹:-ำa๗นฝกฒ›�ญ}ฎซ�บ=2-;O(ื<อwR ‰pืวW�ป�สญxีฏ#Žฝ �ฅl�ฒ�ห}�ท��ม2��-Z๗ท^ทi‘n๊�๙�Qสฮอ�ศ-� ‹�๒�pMั�๐}ฌ็z0QWกู�๑�ฦนแ�๊]์=฿ žyฑ๑ญ~1…q G่uฅ๎C–a€๔|ีq€€ ๅBท‘ป1�ฦmเ๊ ใี}�฿=เ ๎�0Žใำฝเ<~uB ค,˜ฒ ๒ hg#=˜ DŽ!ย!")ฑ๑�ะˆ8ฯัH$F‚$Jฒฒ?-ใ=-ๆ7�ิ9฿gๆ๎�ใlพr•r)™ฒ)‚ู*ก2*ฅย,ชขธt�*a+ณR+ทb๚!้ แ9m� oษ-�.โB.๘ํสd>้W-เjพใh^ๆnเm�้ท2-๓2}2็>c38ฃ3ุชRc๊ACS4Gฃkƒ.G`พLcS6g“6?nSjN้ภ��๋๑mใšฎใ๊้ส>n้Lธฃ;ผCฉ|—<จํ๓u๐#คฬ‡�rMใมw๚ํ™.โศ~์›^ฉห~๏เ–ฮ2ิ โyCฤญwวTํ>D;”ํCิEtD-๏yทธ๙ฺ^$่F ๎ฤ๎ำ์�๎^ใ �ะรพ๐œ�โ้๏๘^๒€ๆI $JƒณtJฉดJ๋ิLh�ำ๒ต”3€KบฤKพฤม็-‘`a(าดท�#ฺู„N่ํํฟ๑ฟ�๑๓ฮ๐ ฟๆf^์&_๕ฆ๖Q!5R+ฯ๒5…R*ลR�T2ES6๕๕9ตS)ะS?ไNl‰0`( ŒU้ช]๕Uol๑พ๔2๕ฅm๏OOเS?๋V๘vZl๏๑ฯZฎeŠ#่^ฺฅ[‚ [พ\<8\�˜์!ลQลแๅ0ษๆแ้๗e์dฎ๔๒4!๘ฟฮ๚Pฏ๚$๘ถฯQ‹b๏ชx -ึญธd4ฦ๛๓<9ถc=&๛I/|Ÿ�แ€a&• แY&‘>๒~ฏ๛ฝ�๖o���o๘?๛ท�ค…ฺแj9mh�ร)‰Vเ7!ฺ–mไเQ�6q��฿๗เ๛๙฿B†ai@เ@‚ D˜PแB† >„QโDŠ-^ฤ˜QใFŽ= pi CŽ,๙QๅJ‡\พtฉฆ††3mยฌ8“ๅN=zฐธภS่PขEEšT้RฆM"ค๙’gฬ–4โ”z‘๊S–:๐๐q†%Rค(ฅม"ƒA6l"lเภU๎\บuํ�ล›W๏^[!ฺ�่—/ล,าฃFฮบo่ˆ…๒#C*ฤœY๓fฮ=/ผpฏE)4H‘"eวำฉ?๏Œชฐu žณ�W�๕ส„ปx๎zป„g˜ฃUœุ�AB ๆือ?‡�ใp์„QดDช?V€ศฤm้<่�๓ะอ.จ‚sF๔‹๕ๆ�๏?0ค๚QF Oผz๐๙ๅ‚สgo�๖จ$า6๐€*d&๊L๏xึ+ ๚ถแ[\Cg<3„1Z œ€eฐๆ4wดิข�๘ภว?ไ๑ ฐฮuๆใ^A๒ถŒำ)ร W๛>ยทตถ๐…�๋*X‚�ย;(๑xจƒ๎๘)ิั%~Iพ)ฦ๎6E*VQ/:‡ }€ก#D=1†A\ƒ<๐@Œp B€!;˜+|๑†1L`ศAAเ‰iภMพร@ม 3ไ xุว&ฒp…}4‚]ซป<๘€„N์ร ขAพ<`ใŒ‡ด!ž๐8ภภ_!ฑG"บ‰~ฤกAvx"ธ@†f`‚`C*—เ}4iฎค ™ƒะ=x‚+๘.Kdq‹]ฤ๒ภŒ?p”,� AะŒ6พ‘Žำ$'=ษƒ์๐o$iฑ#\๊’—˜@ย8ฦN in์dร9|ฐL ๗H‚ฐวs .+`d ‚%t $’คค%ลw,ช! ่GBpฬd.ณขอ(tะ Fค@ัุ า#๑ ?€้ษ๑า—~c‰ล eEž๖ิง;9ษ ฐ‚{$ย{�DHpPdรธ€ šก‡„€ …+< 0šป€9๗ไวธbภ@Wmc”โ๘ภ=ู.03B%j"ผ‚ธปh…!1)„:ตŠABrืLฤ8A� ถa‡ ˜ ƒ0‡pŒ@๐  …1FPึณฆีซO"ศ8@จs"มkQฃwˆZPA5   *Uช’ภ+เซ@�ุ‰• ำrญ@6�ูฯ†๖=ุR@‰ล@ฎ0&l!Kˆย<ˆ€ฐ๖oHก!ภ ๖ฑ†ั๐hCwjQKCQุ&ฉฤุ!r=Z฿�w C>Nก,(5!$hi<†ฑŠU ƒ>^R„#พAŽ%~ƒ ุ�i†5ผaŠ8 ฎัPฐ^|ฺใ.แํuษ๚ฆ  ญซX]โ&‘ภล0ฎA3๘ƒ   ’ $G๒aEp`8„6�� lภธ=ธE:Pก…lpxz M˜‚,๔�ณ?ๆ1„\ฃ ๓>สว7ูฐจ-ˆ~ืษฬ!ฤเภ nแM„ก$P)R,ธ YษL†1Hฬ{�ึ.ณฬgณšQ0โ4เGึลฎˆ้แ„E`cc€‡฿ต่`$ ˆฬฃแqˆYfFl ็�นึฒž”Kแƒ „ฅ^zUฐ…S๕pุุวF๖Ÿ๔4€Xฤ๛PC‰OฌLUล4ฦฮcl�ว,�/ๆA™ฉฺใ?ฒฎชnv‘Yะ‰cิ!@ฐŒ) /# โwC๐€ฒGk.๗ฃB ภ› ‚�ืม 'Fภื� กuž†œn์$`ZะD?Œแ>T[C ป� ๏Dบ „n๔œ ~p$�ูังฎ{  qxEhiสธฅV๏LตlŠ฿Sโฏพ€–e๑ํ;้ฑถ?ksˆ๚ภ”@}–(lญซใฆ>ภpฒล>๖žชฺฮŠ8ช rหTiศ ี�qฎ๊ํh�%ˆ„9^[lื�hฦ&�๚r4Bu71;ˆ™ฝ ATึลฃ@n {Hย่@ bŒ@ฒ‡Pj(2ถ~โฃษmeฆm…Pgฐ€ r›xE`ว`ฃม1�v๏�‹Wป€TBพgW๔&A ศžl ล้q{Uษ )y1;0‚ภมhB-`a b?๏ศมnภ4ด’ ดŸณb/มX<›ส‡ฝ๓๛‚ิฟ!`*cภ‚eX˜ „@›b`‡dต‰0Gp0u` dLxฒรภ ,ปe;ป ˜Aธ†1*ฃ3r‰ุsƒ1xƒัzฟƒฑปcƒ}H์;m€H U2„^€ศฺใ>VX†7เ i…ฃู@๘‚ธL,ลjฆ5่Œ ƒ˜:ภ‡S˜ƒ1ฐ'ฐ,6ะ[(ƒ1! 1lbฝ„P-:A�7x€ฟฐƒ=๘‚'๐‚Z4=PAfB"4ย!9ค}„9xv‹<ภ@xƒ9 \๊[ค2œ€ ƒvศ„/จ‚ภ›9ƒ๐@;๚่4Bฤœม7ภ3rK†@zร8ผฒTZฅVสเ6D7„รK€EVŠ์ศƒcฐ--ธ†P�) 8‚g`oHFj …€ฉPฦh10 ดฦk�•H…`h ๊†Lศทผœq˜ƒรk€.`ก|0˜q^๐Š@†LPค€ะPGุ€vิ€€GyิCH‡tศ…~h„L้›ะFn๔ฦLุœ่„w€wะ†?H�ธm‡L€vAˆผQŸzp!•๊€Xžำ้.0ฺณมแ``เา๘วyT €ศwxS€…์ฦ๘F์ูำižLุก9วt\วส‰HพฉศฉวTHuฤK+T๐SH์™0.Pส=ษn˜ *จ"ห"่ H–ฬŽzผว|์€ะ0ำ้ษส†œ(๙ฃ‡y8žไŒศž„โLไนศQฉƒ์X€Nณ[9›pqเnj่ƒ˜ฒLห|Rุ€หรฦา4อ่ i  ๐ ŽQ œkƒ– ุ€ ด8ะ  H—�ฺ�€`้Xศ฿T€ €Š฿บŠ™Hอ Xอึl˜ฯบ๑—ฉฉฌ๑ภ_a˜ ุlฑอŽˆ�ดฒ„เ˜P้\อR€0„กœฃ™อƒอเ˜š€y ฮ�ดrIจ*OŸ!ƒ๑Oƒ PฐPNฐOีj€˜—๗Ldกฮ`™/๙ ˜จˆ๖,!€šิ˜€€P–@P„@J‡}†gpNศัลฬg8ำา!uŽญฎ^Kศฌ(ˆ'LR)ฮ๔ผ‚2˜-{hา๔5_๐‹-ฅ/ˆ^“LŒX#S0-Š1%�4]$3ER— ข‚ำิL‡๘ƒญัฯ์ำb…"R&%าB5Tฉซ…HT…XCx‡vH‡Q0ƒ@9ิบฉำั7mˆ เM1 …>Uq่='ญิSEี*ยั ฐKUTล-ก3เ>(3P‚€?yี^๕ี_ึปq ์@–เxRฝ‰`eึfๅ wŠE %%;^• kฅ‹ ั€ัะVSuึoืkลV–ึพืc+ืŽHWน่ึv ืw…WŽhmRoฝj•W| z•ˆs%ˆeล“ชะW† ืD๕ื‡เืŒhWmื†uX มŠEMXŒ8X�‹Xื•˜X„ญXw X%T‚•XjญุšธุœXุŽ}ุ”eึySVช pนื„อ“๔• Ec“ =bมฮ์ิ˜ˆ v้ศํ pธะPuอdA—z๙ื“ํึ'5ˆ—NŽตฺฎURฎ�‰ืซ๕ZSตฺ'-[I�‘Uูถ-ิ๊x‚๑ศŠ (H…ศษFภŒ(xeภ—๘k˜@Ÿj๖q๏™zะ†ไ๑pMHˆฬo™‰Xๆั†Sจp8K j ษณ พœย;P\e˜๒ชืตU�e]—ฅ�บโีฐี †อฺ�ˆeุต�ฎ�Zโํแu�ไํี …2ิะ�›(€$เb€ƒ/xƒ/๐ภy€ไ:€…๋ช5™ฆjบ&ุา(€~XาX€W†ฑฒ‰@}E„ฬ5}PคŒซB0#22ฃ[ยฮM๒ม�Mื€ตเ�ล“�ี�ด=ฏฝŠ�uื6^>^ฺU�ฎT”กGศ‡Ž m5€Y8พ�๚&จพตk*ุ‚jˆ8ศ,p`Edh‚�ส”  ฅeฺ‚kเู9�ใ6a&ไ!u‰y;4ุ‡rTฐ ˜<[’Š—ƒA“ช ะ}H-X๗ัณt่ณ? ™xpH‡jเ‡Pp ุ!‰b%ฐอฑ‰@‚{ุโ.�b+ศฑฃE-ใ2Tkง#ุeƒย Yฃใๅาแ&d>ฎZ>�ใqฝZfNfฟ˜ๆ฿=[๖]AไB๖ๆk<ไภƒf(<ˆๅG~‰Hๆ_ d h'&ุ,8ศ…O:`พนธŒ�8(€T‡:ภ)ฐ‡ „ช[Zˆฝ ž H‚{`.๖b�07ค๓ๅ,€6iซlะƒcฆc…เ�@†fe~ย‘ๆ]?X‘ei”}f’6�แEแoฆik<€+0†z0†?ภฆ+ถ‰†0ฐ’ศแฅ…ถห_g˜†˜s๘„ๅx€ัxปcจˆหำ€ ˆk€ƒh„]m่+, ะด0nซ \€ว๒<คาแถ[cภ7f่ ้๎ใ—=fB]ุูีๆk^าฝ�ๆภF�š6lฒC€[8!=฿๊ึ iศ�2จ‘ Aๆ๓ใ‡B€IะBP‘9ฌร;ฤธ„yเƒ%ศ‚rP„I’Qฐ0ถŸ กe[v ณๅC2เ.@`�+œ'ฬv`–`‚>ๆป†ใf๖]ฝ.nlถ�ๅfnม^W >์้Nถๅ˜^;พ‰บM…zˆึิษ9™; ‡!`€(.ะ(J0ฃค น฿้†?ุจฺ่�‹ a€,ศ"pไ๒ฮ‡& ;ฐ’�›์ภH๕!Gpk€nSม6nป^ๆh�c—ฎf —n•�Zแ๎™ฆ๎฿0Z๔ฺ 8NcˆฝM™ืคœขํ?1‰ž๙ฌOญP—rY`ซCn€Qฃี€�œOศ‚Yสู๒\aษ๐ ฿ุใ†r”nn�อZ9�๋e~๎–ฮ� ืr๓ŸBgd๖ˆm%�™ ซ$ม- ]ธJ.�ฺแ SM]Sฌต๓% ŒŽ๓่D dถW8๏‹=d=i&M,2'์˜๓EOY�๎๊t…!€NF7Š’}rŠ�rJื๔w…ึก:฿๔่ศๆP'๕R7๕ฬ์SWu™ ‹๖rUŠx๕V“YQ็hu1Gtฃฐ \ฑอ ศ]๕า๑๕ƒ ๋ˆฟH๖๋ภา็h๖eoŽb/ถถe๏8“iAส~๖a?6 •‰่;Hฏ{เƒq_‰ะXnZOT9‘ั”YjA„(w>`ำ€กŽุ๗กิPwvwƒˆxwˆ'„v็Œิ œํv�•pˆ(P†~xeเ1)Y‘๏wง€ซr๋€ƒ_•H’%9voฯŒK•tG/€h˜ŸŽ `œwr'Cั”Daz_&ƒ—y&่ตFy�แฃ‡”p' ›วy—g็?๙ึszy แFZ|^`X_‰Sy@Zƒ)ˆƒ"hTแyPzคyฒ’—๚ˆ•Ra๙๒x�D™๚œว�ัภชQ๘ื4‡ั™ตZ๐„:qœjqs™Zi€G๖˜�ภž”^๘–˜]๑™ํOŸE™`1dม ‹aใh–g‰–iฉ–k๑๎a|วOˆ–ษิ�]๑d —ะo๑”Y�yI™ฮ๗�ฝZ‰Q}ญะŒใG�’}r��ฟY˜3ฝศ”๑I�์ ฺG’ฤฟฯ„ุฌVA`>ˆZึI๏'�%�๑g—ำx€…ัๅˆ),h0AืยdกขB ฐดะ  .Mใก`ƒ ˆ"„ม”4€A… ‹"*p้ฐรE•6PP€ย‰r Jคh‘เL‰6Uเ 9ฒไษ XบT!bC… ๚…ฉชbŸ$$H๕Aี ภไpมetาญk๗.�ผz๗๒ํ๋๗/เภ‚ nค7ฎ8�@”ศ•ฮฝ?Bl8๑ศšฟwใŠl1ฺ๊;‚sBtะ ก|qNšnํ๚5ˆk|x๐ ฺ฿ธั6xธSญžฒ9)*Lงา4ท€Cdใฃ้ 7๘ลษเ๖B”T๏�ๅct๒ƒกj�๚)Sbวญzฺ4Uฃ7งB k�๐ ๓sฒv๚๑ี๛Uม๖vน‘fPlณแ%@%ำ๘`Pw€ƒ›8I•�v�‘ึุc‘MF‚ ช$uึvXbรล7_}95เ‡6้ิำอ‹buืQศw!4๘เ8r๐ˆ2๐5sุžxไ™Ÿc๊)8ขpะAฬ3ฤ ฆฺIv9ฐ‹4�๐ฐภ‚ ดฐ@ยŽโd’ใ•gยน�๒ะ �!Wมฉ<–")lค>/ฦ˜“N๘้W™ภ'}๖ิแ‚ ก‘ dŒIWš=โิ#นT˜#]]ๆO.� ubฃ9q๊)Ž'50iฅ“นucw#Œ๐J;๘เ๓|ฒL6๔๐„+ฎt €Aq !`ฑCg 3Œฏพ;UP๐ม&HL H=ˆŒ1ˆl<l2ส*ใ4nน็ฮ†R‡X๔qศ^ยญทเ†P๔A-€%็ศD*๚€ANp3Aะุ'ง�…ึ\{ ๖ltภrยMจเ3zุเ2ฬ2Ÿน eL$�ฌม›&ฐ aรฏƒ5ฟ�์ๅ1p,ƒIะbซใ_‡M—Tœว<3 x๒๚(”# DF`cฬS ์็%๐๐D'๛XแจNEัL JA�zฃDุีimG=€pAF3˜ธRใ]๛>�2่#Š่c•o?๐!\แ^‰.ฎ๏}™`ๅืถ.5ธF)I* ‹<%˜๓ใ‹|๔cอ�ธ’†UB€ะื ถ-onl0ร@*8A€+�ร จsbC-๊Q“๚@Y๐[tbuฌY ๔ฤจะฬl•�“ฌwตพn ๐#ภVถ!X๏หหเ�@ .PI3๊เ!ะ€DˆKs๓ฐ‹ี•Fฟ�˜ต01c(�ฝคณฑ^2‹— ',๏EŒฝ^‚{ุ ฆ |9์a๛29ัแM'Ž๑๊X 8ม> %ดอํŸšมf75%| G%แ�ถมE�M๏๙๎;ฮจ].>! :˜ษล w?ศm‚คuภม์!‰“๐GX-``‚ฃโ%ศC.ร2PA }/ฟv ๗ธอR*/‚ แu๊ L\Cฃหปลผˆ5฿H่Gh@ํ XŽP!:ฟ๑ ่{ซ$ู#U�žwรu‹ฌ}ใ`{hะ„hผ nmtA�๎๐I3ฃ <…ˆ ขa€HxจิŽ<ไCฝุซ.€ฤ8ม �–!ณภl(€Hงหผi ์ตบธ‰&เไื|E M#oำ๒€ฎ”ข๏๑mƒX 6ŽdฑJรrยฐ 2€ Ž$# 8)๙ฮ‹P 6๐€& KyอAP‰�ำำž �,H6เ‚<์�m•&"ล)r‚Bhฦ rฉ|ชk‘๓ถ๏๛“EŸ(์œ�LๅY^-’๖q_KIX—Y] !แŸ�a€�ัŸ�ตŽ3LCฬ‚9|๓e„๗Aภ.ภั„อ�OE5ภA4ยะจ๋™]J4ิ.ฬ†ผ‚=8EำEูฯ…า4฿ 2QT�‚9ผ€ ลภ(ด@ H")ุ„I C…กpTr |ฤย*กภ6‘ ˜]lเ6ี>ะ^ธQ(ไภ6ย”xแ๖qž €Uš8‚ คq�ๆ_๒NH‡>„ิS ุ,ภั@ญMJุVt๎ฬEไ}"(†"ต๐อ9ฌAtWTภ ฐย2ผิภeึย�ภ\ู์ตยทi@์�‹๋™ฦฃ Rlฏัžu €NุAฐฟŒม‚„ภiฬWดžภ๔ƒ4 Lผหุม|มxมIฦธำ๕…\"ธม  �ภ.˜ƒŒม่๙\0Dสƒ-0KAจ อมLภึภใ�๐€_qฃ7&ไ*ถโ+ฦโ‡แมAึหZฃL‘ข)ขb aผc<ฮ#FเI0Gฮ6vใ|ฃB CBŽCF?BL‚>„€8’ฃ9สฯFฬ,A”C…MเŒ‚5tื์@/z"]๘€๐ƒ3PŽL>ฮ๏ฬ�d9NPxม>”BlะPŒภ<ฯ|AฬมrลกA„O5 Iส#=d ไL >œยŒ8๊ฤ_nๅ�ไ€•U�ฅฤร˜%ZZ^ยฃ^6ฆ๒อศะA`faš�UKF$[‘T2@ใ<Аฤp… ๐œ€ย6lเ–(ึฆm�ๆ]ฌˆx‡˜ภ””@'lว;hรจลhƒ<๘8d ๐†+๐‚„ภ ฒPใ–ฌJ๘โ๑L9'tJ'u~EoXpฤˆAะฅrื8pมEจหฐ5—iLH*w˜‚lภ ƒpัC&œษคƒyฤ: p|@'ิร;˜ |Ztม2ิงP/เฤt"หT€ pงA,า#\Cqฤ€žไƒ}vภŠhnฬ…aG*—‰^pพƒ‹็bŒh‰ฺgธ็hิJ๘มn*CoNฬ่‚ยภค�B0ด€*vC& ๚จ|ยจ)4‹ฌ(N$ภะร0@ไM~๎g†ภˆj‚ฑtรศ•[ิ6ผŽ^็ีฉD‘‰ จ"8รdฌฐ(˜Œ)d๔' DS`†f่Fd/ ช)ค@’.i“>้ŸคƒศF)ƒ^Žฆƒ‰ฺวซPส‹pมaฮTŠbih–j่oŒi=ไฦšip)ซฦAŽช?”ช\ธ(•Šไ~.h � yvCoฤ คƒ‘€š|๑ƒ.|nN+ตึๆYH@Y-U@ภXD4Ztk pภHPฃฉณh@0€Uฦฏฉb4�B\˜F4EsฅklภˆE”E]ธิฤKฤ„ฮย@ยV|jภด”ห4มBEHฤX์ฮaภZธD ์ท>ลUl@HJ่ซผา๋ฉ€THภ"EO(ลn๋ป4ฌM˜„Ž8ฌธยฌDP,!%,Hดฤ`ซ h๊ถ~,BภVŒNmึ\เ์ร๊,อš•4LดซU,’Oไ์I$มยฤ6ฑŒ•ƒธ+ึ2”4€ทVภXHาีศL]@Lฌฮ’ซฤบD ,Hlั’… ,€ป>�J€Lอิ� ฯ%ญ฿ฦ,ลj ๎ห๊ฤ�.b”„Tฺฟ๎�mมลJ8์วN@ๆŠภๆfอLฎธJE,ุ†@ณฌEท~Eฟ&@ฯภโชฯๅ=คมcV+๐/ฉC�๎_˜FJ€๐C”AฌVoJ\๒aอhฮฝU๏_ะ%`(/๓V*ไIล๕>‡NoAฐ]N|ˆ/ž‘oถ˜ฏJไVแ€ƒ.\Š`˜เ_ภ๏^HQ�ชoฯ1ิ^Ho^ฐ_ิ™๑�ฎGฏNเเ�€ะย/ค๐Vฐ_0ตภ ุaAoต4K#ญฮ‡šsฐm‚p-ˆ๐ต ฐ “š—ZC:ˆยะ#ืฐ OK0C=hยDํ �01ห่D6ถFถd� F�ๆO+c์้ใ]iŒpWฑ gD–๐ฏo1wฑ1‡ฑ1—๑ใน†` ถจ1ณ1ฃฑ_๐k˜ฑวฑ฿๑ อ1`ิ1ต๐1๛q ๋1Wซ 1gใ 'ฒ"งฑ†i€ุ'๔คด�Z‰Qติ1๒.ยฑJ`ฒต€rAXrJp๒'›2!๛šจyr^�b๕Šฒใ2*๏1+/ฒ-หq-๓ล#oฃ.P๒`rr%๒sฒ0ฤ,๏1ป^)3]$๓ด3bๅฒ]ธ2"Cณa�โ7๒-wณำ็mฒ]๋ฟฒ!MP„\ป-ถฆยฎ$-�-SŒ๒kฐ ลKค€€ˆ+นŠY่3[tlU@CPภ(<ฏ$€ร:ฤะดล[œซ\ณEลH-Š:ฌHƒBWฎJ˜ดํ&ด5s3Eทด_3,ซ1L๋qMcฒM;3=[๓๎ีLw2O๛ด+๗ด({๐3ณ4#๓Pห๔M7uNืด7G๕ y< (,SืณD(o" ชคˆiฌW ฉWw*Q �‚-hชภ‹cdฒฤษœจขx€ควŠฺ‡*ใ‰žpGŸ`ฐ‡>‚1ด5ช"=”XขnFF`dHF”€5˜‚ ฐฬ#< | � zจ{ธidJ„์Fo�†`Ÿ.•vฐTก*๕);๕KณSƒ๒lCตP�๔m๓ดmqK+ต๖55๏to๓vPืถq๗PKตr[ห€‚?ะBˆ€@`ตภขdH�อฯ˜iX7vs…\้@30B ˆB4์ภaŠ‘๗ก ฑP๑ˆ”ž%Œ/@#(0ƒ่‹5[Jฯคj’Pฐ - L@�Cจ ป<ฬภ€i น KMย ั๘%�Lฮ์ฬษ;peบเฤคfุ ]�ทjR#๔ๆฒ8wkทธqว8Œ�๖kwหxk฿83 wl๓M็ถ6#7Žyp/�ท‘๛…Eำภ#ไรrVว๖ไaี]Qืuw5I91LFA>œ‚>`A.๕ธiฌท^Yแ{3๓คั#ถRภผ†Iก”Jฑ]แ๎ีัฬรlA:,Aฬ8™น >€i(‘Vม>–`A•,›ฦนa’&)TƒE1•ํูีๅีลœczฝrฃ#ฒoguฏxl‹9‘y575qŸบuฉ 95ซ๚Q๋6Žร:‘9ฎ32ญ์C9€oŠ฿0VชIZŸu๗– žB�K)๘@2 TAนฦา�?ษ  žภ8�•ษ•ื่[q€cลภ—ม `รŒมฐigy�hE“ุeT dฺฆํƒดนภœ[wQ’Qvฦ‚X0Fณ๕tเ…๛ธซš x0+ใ๔pC๕ร72ซ›บ,฿Er๙q 7* ฒ&S|Ž7sL‡�ฌ#ตษฯ8ษป6ว‡|R็บหใ…iP@เA3 ุ๕Vwร๛รํ”•;รx-`ƒอ…z{_ฅตfkL™ต—1ํ‰ัŸ=แ|;žฑlฑว@ะCผย!ะ"๔ญ)€ญแ๖�ภษฺHiจโ1ž�ฺฃ;[%eม=(Yฺ›BZฑ|�aƒธปร;ผK๑งบ%Ÿ|จ[<ซป4ฬ>‹ผฌ+>จฏ:จผงงผสท�ผŽ#/ฤ?๕Ž'�ส็ธQฟ<้ใล\1ิ๔œ ฟฟF3ตk%*R๖ํแบ1@%†ภ.,ร"ุ€ag~ด7รฺ,=�ิ@แำkปk์ฦ H!š๙œOโภ �(ิ(R่ž ศ�อ_ถGฝ€{hƒa๘waœP’คS:๚€$R"1Œ€ฆฟP?ฉ8@h8€4˜aC‡ >”8‘"Bˆ ิธฑโล;†d๘‘cBAR$1ไส"ฒ4 “ๆห‘.qๆิน“gOŸ?:”hQnข"‰\ฒ—จ ค~qH„เภ�ฮ5Dn�ˆ:ต๊ี!๒ฃ’Bหต?60€Œˆ3B๘ี๗D๐`Dกƒx็ญ๗S’8ฆW`SO}ฏาV�‹จdU!๒่;œ,%ISMGu๕ๅ•%ญyี๚Zพ4gœูƒoฟi-ฺ่ฃ‘N๚ั๚ขš†‡ธH‚ZฃฆŸ�่ค�น Vbิ'LS่‚ซ?ย@j—ธ^งnะ‚–_TH'ณ Šซน]2๛'ฝ‡ry้—ๆ๓ูf‰–Yp�็ฉ๐ฏมnuงฦ/ต?ศ็(=˜ƒZ� กWฺ๓ฯA�?ชฯฦ‰tลMžี๏Xฃ`ฆM†ศกt™Ssอ‹Zi๏l๏�๗฿_ี๘แ‰/�xž•�”rXอพภlพ��;�๏ๅ'ว”๚�1’^๛ํฟ‰๛๏ม_|๑g฿�๓ัO_�๕ูo฿�๗แ_�๙้ฏ฿�๛๑ฯ_��๙๏฿��<ญ€4เ˜@ข์z tเ!A NP(ตƒ™ฃฌG3ฺญ”ย™เHตม‡edUกโQ˜BฎPVแy4๘B๓x ?,sก fA๎‡=๔ก ;—CๅฅLg๙)ข BD "1„?tโก(ม-RขขbฬศD็ˆZœbมF1๚๏‹ _˜ล&�N^๔�๕“ฒN‘Žuดc�ส(ฤ ฮ๑‚&‹กฯฺ่ฦ ~Pfิแ™HE/†d�Yฆ�จฤฐ ’doL#%-นHMn’“ปž#UIT�”‚ผ มƒษCv’•ญte๎pืลIF๊�SI&g‰ฤร�L”ฅ4!_La3zŒ{#งP™ห_ฦฒ=๔ฉb,ษฃว-๒‘˜ีดๆ5yฒ ๐๋W�„ผูอˆ€›แg8EN 3Z\[4ฒ8x6ฐซd‰ๅๆIO›`“Ÿ�๔งF60‚\�ƒ ๒๘‡<�qЁ”ก๕P(Azะ„.4ข๒p่@ *QS  ธ�็GAาW-O0้IŸฐƒ’ขิค*eiKWสR—พT ๅS฿'EšS๎ิ'c�๓โb7’�ํงฯ€P�?BT็จ �๔ษSฉN•ชUต๊UฑšUญn•ซ]๕๊WมVฑŽ•ฌe5ซŠ ผmไYลzภขฅV‚A ฎใ@วVนึuwูฑrB ˜เฉg5์aญz<ws nQmhข๔˜ƒฦpp๘cnธ[ ชQeฬม^ศ์f;๛Y%…vดฅu‰เ๐ุฑตตm5ซ€ิ�๘… ๛แtม๛เร!–ฑƒไผƒ9ฮŽt0ณฮŒ@9อ}NtดSณธไ ๐-ส ‚cั๖ถๅ5ฏ")ะƒGXฃ‚ ‚L๐ก•งY ‡€ภ %(กฝN=(ฒแ�ƒฃ/(W/ˆq‚lC�ฐ€ lเ ˜๒-�ณh๐ˆ||ฉไ=๏‡A F4ƒa€A :@^๚fม๘mฦฒฐ55lฐวjะ =๐`+m` bผ†‚ิ๘ฦ9�q™Q–@ร>สQ(…˜สUŽข.SŽLa'+พฏ šQ฿{Dฦ16Ž|YฦCถ๑™ำ์c%ถxhF9มอส}๖ณ Cเ !ํ๘EB`>oD8ฤแšp๖�^เฟา€pฐ\c ฤค+}้dzำvษฎ`Œz@†N๙๓ซa=A ฌK.ะ…0Zํ’F�วIฮršs]่R‡3#˜@uƒ��์rวยAŠR˜โ๊XO›ฺ ์ภOsง9ฤNxาŸ�(%YcPO’๋ž8๎D•{ถh3Pตแ๏FฬGฅ˜ล~…1…ฌ^zฝXฦ6&ฏฟ" 'Q’๗มพ@ะํAธ�D�‘ˆ2แท8U้จ‹oœใฯ8ษ:r‘฿ฯ>›ป]MHxŽœๅ-w๙หas™ฯœๆ5ท๙อqžsฟœ„ข$แ2งiœฺs็EฯนGq(I”้:๙Goซ`ง ข>u T�๊Y๏&ฑ๔ๅsข7์๚F\ŽŒโ;X‚ฺ3J!ค}�ํm;D *wบG”ํEฎ2W^vม'�„IeมKƒเรณ4&โxว#~ป๕tแเ1pnDฐ๐ผ๎ๆ |�๓Y๑H๚ั“ธF�็๏ฆ1TŽ=๓ต‡๕5rธๅˆม�!๚น๋>๘ฝ��๎ :|ไ๗žหอผํก_๑c…*้๓>S>ตฉœผ้œยi+•?อ3•(n# ฃ*jข*๊ ๊มK@!t!Rฎ'H๊ฅR*ฆPjR,B9T(|jฉ๔ฉ6bา:ดDyขจ�ุฒ Š๊AMดEรา R4,9พ Y\ิEฅcกฉ๎าก #.ขqซ1G็Sชเแd(๚๊aภ@l.`ปIแs์!tD ๚ ฏหด.Fิ๊D(@ Gม5 BษQKแ“๎เคภMภtตD‹ดฎๆด8หณ�ค( 2ซQ€แt@5@bS1bุคkจ ุœ‹QOdva5็ฑต4ยR1๕M4`ภ ์ภฬำ,ภ8` ข‚ฏm๛๑;•:ยvr@TŒว8`อ‚ฬอn์  œFlไ2!gต7?๕Vy@W#’�ฦฦW�ฌฬ�์ ภ€ม โ!#๒H—ี4u[1‚TGMำ8อำ@ ,ํCฦjกrต . ห1\K3ศ ล,lุบ :Œ ู&UE/ วฬ/4ย#A_MS5ทก5ฑr�”แ RเC2 �t‰*ฏ  $O’a#ณ&` ฺบ‘฿2`L๗ เr๖ HS#Z๒%cRd#ะRมแD%"IแV„$ฦ'‰ƒ?ปษ?ฝiœ”๖W˜Vœิ ข๖Wœv�3np–€ šŠ&ภe ดภฐr#ฎ&(฿AJAŠA*ฃ๊ก�ึขโ6A้ถข. ๏�4j(ท6—Š=<@ญ๒ด‚0๔คfJฆฆ,tq3ดq/ิBk๊oAฑไ(GU”|*,รRD5Bฉ€๊จ7ฉฎ Dƒชt7Bt™สฉ,wkหl“๒uต”ะเ *–vgu4!ส L€ktหglV !†ทu—: Dกธ �„]โhี"ชท›wŸ ๅ๊ว^แธ๕4” Ÿ”f' `’p ›ฐ{Q%6๑} จด ค ำท & $ใ 8`ซP~O”�gN มMิส ม๐#j ˜ t!๘^ ธฑ(~&`Xำ#1Dx#�J8ƒ9† T}’ Cพโpฅฦ[�  b˜QุUXsฯ'IXA*�P#ฎ&Yy-a๚p t‡™nƒแ7๑์I VE!พ6ขŠั‰้�ง ๔ |๔4F@)ฦxย€EัX9ี‹~ม๒‡7ฌl%ศŠo็–ขสท'’7]@Y$   ไ`'ฐั8เjWูั•G๒�)จB_ r7@5€—{Y>yn>—^ubF4€ศน’yHฑP4uD ๅภWJq�ภŽข*œ;‰0!ŸŸšศ}ัži›}่Ÿ‹ศŸa 3ฏ~g@5`ื6ข’เ*ธ{z8l€ก฿้์ฺๆ0บž๛$:a#zข‹G vกล๎ 7:ไzฎoฆฐ8zค‹F+อ๔4* Dภร`Zฆ'ๅไ์sฅY.จg%B:išVภŒก>$ ้ก xฺขIๅ|Xจ7Nฅำh!Žบฬ’š`ทoฦ&ฌมZญฤšl๎ฆฦxph`zเาภyrโค1Z„& ซ9Zซ�บงมข%mY ฐ[j[นJ๙{ฐ5ขฐY๔ เ nq าa ข`ˆ@;็:ฆ/z2ืU๔ฏ5๏ณร#-ๅ�šค`ฉ ชไ€วฺก๐ก†ฏFต�ต]๛`[ถkXM่ม Hcฦ`ปฬข‡h”lIดAปฺ๚q~ฎด7โ~�Z#`ฆ` ึ`ˆดVฆ †+ซ๛บณ{ปป๛ปร›d€โเ๒€Aชfฎ›ณมฆฎ…fนŸS:"๘zฆง[EJj  DIื›๊j๘^„ j"Y&๙๚ธ“(gˆZฟํฟH#บ๚ด‹ฆ๊�@ใZ€4๎ข>่๚”xฉล9|ไ๎ธ Aผพ‡์ซeๅj๖`JBฎD‹3ฅซั#“š๛ลC{ศeˆั4๛SDœVฎๆ๚e_xห“ซ5�ฎYehˆ�ๅผˆ$V‡m|Vf%Mšช:•;ห�lหญพsCบQ{xฦ�งUฎfะ\หhžฃ&žฟ๊ๆ0—vฝะ �ะ—<ัฅาอฝ'฿ร$�ัUฝ<ภ)]‡จ$๎IB=ฝำ;ยั3}=bZN&&}ิCฑe์่4.ีU�Y}ึฎ=D=ึW5Uๆำๅโพ๏I%`ื’d‚]ุฒฃ โึ!‘]?�Š}ู๏1s‘ำก}&•ฝฺฑ=�ต}�นฝ�ฝ�{=�รHžฤ��Ÿ8�ฏV,่ฬอ�ๆR‡�Oณณำฝ�๎�ห]2�้}ๆ์}�ฟ2฿๏}฿qฎ฿=:?ๅฝเพ่พwึ=เพๆ�v�฿>ฏ‰F฿a‰�)พโi.โEgโžใ��C~�^ไmฯใCไQพ๖TtXพๅ1๏ๅ?'ๆeพŽK�w6�ๆyพ็}�็>่…~่‰พ่�่‘>้•~้™พ้�้ก>๊ฅ~๊ฉพ๊ญ�๊ฑ>๋ต~๋นพ๋ˆtภ8ฆ\(& ฌแ๐fvllใMyโŒ@'ฝพใเ �๚Aศ>@:ธ๛pฒ‚`ซศ•'\ภธœ๋>ซbwŽI‚`�eeM ๊๘ Z t!_$ฟ๊Š%+ขฎpB– ฃ๒ฒbGjๆJเหข./�`Yโฆ‰_ซ`Kถ๊ฐ�`�AดžD5ำ„ก”q_๗y๒c PกยJเlเ\"ITแ… ขา�„มo?ภธค”aNbป๊~มg�ฌบ๋ปยห;]"[ฯA ฒ ธรzเ \A๚uร�?�9ใOBG3F)DE�a€ย… +ŒPๅ*D…‚€๐4‡ย ๒0๛รๅ $*iFL$&2|� 3ฆฬ™4kฺผ‰3งฮ<{๚� 4จะกD‹=Š4ฉาฅL›:} 3A๒หTรL—ฆฝ`๐ก1!6<Œ่’ซWฐb=„@&฿)}X: iขฤTสธัรกZTR`ภ@‚‡ .š้ษAขƒdจ”+[พŒ9ณๆอœ;{� :tR  ูWฎˆ 1Xสศฑ ณ"6๒ขU่๖ูด'L˜„ญ”ญxฯ๎]่๐ฦท�i ำ‚DB๕h=ป๖ํ�ป{�>ผ๘˜zเiVฎ Zwๅ{vmใi฿ว๏�ฃถsGlฺ>Ž�_ื)”ภZhา1JL‚cŽ�a„NHa…^ˆ!M\aL=ฦ�AR[]’อ 0€หXŠy0 ,0˜W%ž˜"Y€b ห,bC .ลฤ‹ดฐ# Z‰9/$ฦฆฐฐมศ๘ใ=™%b],ำญ)งย oผ๒ฮKoฝ›5ฉWยˆ )`ซP @A *่หฏฟ)Lะ€"pะT*จ`“ภงฉ( €รl @;,‘ˆ4๐$ษ?ฺหrห.ฟ sฬตพGkฏ/ฉลCอาPsQ‡ษ 4vTภQะF4S8๗์^FL' ๕—=๔ภยฮQ_uึZo�ชd<๘p%”B %i`!IฐมFŠr w�rฯMwvฐF1ิใ:฿ CL(~ศ :Pku/ฮ๘ผ6e4๐๏ำAM�๏ึ˜ผิๆ–&คฐ;ฅป3�ร8ฒ 'l�ฒ6N{ํฌ^เv๘Tp bXRภˆ Ÿ=ยs฿”๏ภ฿ู�คSz<ฅSรฦ%ษ C9DยŠ�N~๙`9(ไ+hi _5์Rสƒ:ม?�ธฝฦ�8ๅ_ซื๘เ2ะ=8ม•๑}๊ณI ฌ!8 ˆ5�\t@4มภŽฑ ๛�„#๓ซCภ†! *7"ไI˜@ค๐ คณ�้โA jุะ๎ ล(fพ Q<.ฐ†€๖ฝ/~เHฐŒ๖$ฮ�w*ึdi–!ภ๚ก‹�คˆG<�d€P�LCŠพกp‹–่ไƒ!ไ‰ก(E…0@Ya`ฮ„โE0Šั(L(† ้Ž๎!าzี†ษH&ลb่Wๆ`1L ฎ๑‡ฉภ,ฃล4Iฑˆ%ภ“Cศ4ภ‘(เb*ะ@ล1Zr’ŠLD˜%ฑตJ…ˆ@ูrฉI`&พTXภ*2H$– ๘%5u‰ช— cXh'6ฐJ:“ƒ€D`$ Ÿtƒ(%#•‘3‡แ"EFเพใP๓—เ$ุ˜ยl.Db@ZDฐNเ •Zฉภเ}„–อผคL() คภ�T@7ฟINTR�ฃญ@ฟZฒฮ‰ t!่:Lวร˜�๐tฆs„8(ษœ๊ิ' ๐ƒ6าQnผ[ ธ8�17Hfศh‡S๑Q_`ฃธฦ;:‡tเ�๘‡0rpXzภทจVOต�a\๕Hชdx‰›K่CAชˆJ‹t€pหN๐kXkYa"3/AqไjWCC.สลท e"lเฑ–๕ฌ+eŸ1(Œwaาง@*ถ>`ˆj๘ฃสXSฺŽzเใุj^หีืด�%Hซ^\‚;สๆ๊ฒZAํ6�q‹k4& 0„1|”ฤะฃ \๕*X“8‚Zด๘�๘‡<~q0นาีด ˜ุUeธฃŸลGhทิS๔ช—ฝ�@YJp [p๎% ฤ7โกRธใคPG้ �Cu˜ฎ�ูฉƒผืœCHH…>ภะ€ขˆร ฎม˜ไ)Y‰YXแ‹7Œ8`ศAE.’๘ตฯc(˜a†๛ุDฎฐF์R‹Vผ<๘€„N์ร งAพ<`#ฤ;ถ!ž ,๓f—pœไไ;ผ\ รงx0)7-ม๚h„C]Acใ8ล]žเŠ�Xัฎ๐…1ฐจ„d$% ยIJฬ’ฉ>9สSๆมD’๓ ‡�V!iNฤ�š�< lธรR!N'u,€่ม=า@‹`ไ:ฌY„ (ก\9r’—,Zf๒Y Y่D?ย�9ฯว‘0ฏ}† „ AศL: `Ÿแิ609ฆ=ํoธR0„ฟ ๎ฆฝ€+ธG"*ƒmย.7ส†S„€…+< 0`ํ‹€d<WŒยะ7จ"pŒRผ`ธ‡;ื=—ˆ�ๆ– ?์ข:ฤ<คP4#–ห3š@ŠNn;dภ„9|เ€c‚'…1F๐|฿-l"(@Ÿธz…โ‰า` s&1ฦ1‘L.�ฎs\Dfศ\๓™ื�'่ม6่ &€-ภๆม„-คc Q˜šX๕&^€7ุว ืLผ�‰ˆ�aื–ฑ”%่E|[�—นิ%R1!Aดใ1ŒUฌbึใก"๑ r๘L(oธ?ฏS2<Š ์Ni6ุใ๐ซู5 Dญ T‚คžOฬžJ5hr0”C€8‘q6zE| ‡ะฦ; *U ทH*ดะ‚๋สg๑AA๚r‘ Z5ธF=ฬ๊|”cศฝจจTœI๏9#ษ๏˜ใ่HG!ฎWˆข˜๏|ะV$ VGxๅw�~ไข~(pziภ3tDv1az๔เ‹€ {0๐ะ`qง0จ‡๏Q85๐ฑ๒qu๛ฃผaพย๑9ะฦC‡Dƒ5ศmฬz9่`ศwคgz๛ ฉทz๐ำ ึqฑปื?p{ฐ‡Ohก„ๅ๗ม7|CVEศ๗ค‡n๊–7" 7e%พ—@oO(J?Hข$‰„Jอฐ ๗้๑Vผฤ„"ุŒŠ0'ฐ ‚ฐrณ0 ๖ $Xx_ฑฤ@ #n‡&@้„pŸ๐IG'…€Hธำ"/#์Hz๑$Q2%Ur%Y๒Ž๑˜JHi• YยIA2 CR$ ‰๔–n‚)ะ‰0ั#pฐ มะต ถ@g’’$&pEp$ฬx‘ฒH"&‚"*‚’.R$2•57’#;า�#$ุRลภaษ/uyŽ@y๊ภhษ˜๐ส่–‘ฤ”ŠP wโa ถ„ขbrp)8—OIˆ�H)–๒แˆ ๔๐&†ะf๊ธŽ๔ืŽ8ษ ห๐\0?ญD{_pwP)#w3|ถ]€l#0t€ง0c`Nภrl ถPcB D$!’3q`'xข'q)šย)ž* ก—ฅ๒๏X™—™™ืฑc๛s๐œค(Œโ(2)�` 5v›0tะ™๐U@Žs †b„ƒy)๖G…ษPcมi‘ฑธ}]ึ'rB'พy'yฒ'๚๙e2�(…‚D1G๐ ์เ J ค๐ิ๖jกโ *€Œoษกtณฉ -P'ะ ™ †=uTใ0้จ.์โ-pผ € ™@L -�โ-เโ ผ !@ฃ6๚W‡้ �ะrคŽข#Zขa๕+๏๏  €,w  ๒เWwQ~ Z้ต^œิŽๅAล&\อT\เ, ผP|AJLฅยL*ข$jข .ไb.่‚I๋า.งฅ@Uฅุข-ฉ/:ิb-ฆ™EYไ2\.{ฆ�0๛ต‹กฉEะ@i:9ฺ.,ะaใ",- �งNjขๅU,‹0พ,xZซว’,หา,ฯ-2ั)เโภ � ิะ/ ิfฌฦ๚ คฐ�ุกี*7)5J&%’‘I* Mฦw2ัด๐˜ข`…ฃำ1"0ฎ�ขLฐฃ P๗0'[ยKุบฺสญ  ฟฎๆค0[ฦตrุ2—"eM€L3ฎๆD ฎ™d0�ชญม2##MZ•D(๓*“I�”-วิฎวัญ)ฐ D MโdQ/ั ้$K&`รฎ*ำ› –ค�/~วe๛‚ฒ�T0แฅด ร01 ฐI311” Y๛ฺ ฯเœะตY‹ฌฯpซaญeK;[๒tŸCd/!šk †•WPS ฃ`M@ญ •ถ ฑe?ใ~ซ–ƒฑ?ม4h ธ€[ƒห3ะc3DaธŸcO�ลeฤ;รธŽ้ฐตY๛ฌœ[ ฒ ใcถฃIn;ฆ+ `๏ะ้0 f€Šค�‘ห&4#ฎb@ œหปโะ>#ฒ+ผรห_9Jผ๔‚้Dfภ|Pf )ภ;ษkฝื‹ฝ/#๓</ผู+พใKพส{นๅ‹พญ!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,z q5Hมƒ "จpaB‡.@‚ x€๓ฤ†ƒ>€๒GซŒˆ)>Hะ€ฦฃ|เ2Q)ฐโ4๛ส1มกf<อส5baใย+ฦ๊๛ร‚D‡„nข"…ำˆ!8|ฬศก็Dˆ!!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,z *� 8มƒTธแฟ„ !*”ˆะแC‹ flฃG‡†|!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,z 5Hฐ มƒ*\ศฐกร‡#JL!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,z *� 8มƒTธแฟ„ !*”ˆะแC‹ flฃG‡†|!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,z 5Hฐ มƒ*\ศฐกร‡#JL!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,z *� 8มƒTธแฟ„ !*”ˆะแC‹ flฃG‡†|!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,#‹!๙ ,z 5Hฐ มƒ*\ศฐกร‡#JL!๙ ,#‹!๙ ,#‹!๙ ,x L1@Hฐ „ไษ๛ทฐ!ร �  ขลˆ%^ผจQใฦŠ!~ฤHrdศ‘$=~„่ฐaEŠ!^ (hcร‡/Fฬ(๒ขFŽ%rœ8า"K“6†LY1ไK’7ฦD้r%ร–ย”ษ&H•{&�IดcO›BqFีูิจฯฅHžœ๙4ซษ’wr +Xช<ษz•ŠUlัฎgƒถญ 7ง\ญnชปํX•eต๒อk5๐Wป[฿๎E<˜๎โฉw;pipx-1.15.0/pyproject.toml000066400000000000000000000071311521706623000154760ustar00rootroot00000000000000[build-system] build-backend = "hatchling.build" requires = [ "hatch-vcs>=0.4", "hatchling>=1.27", ] [project] name = "pipx" description = "Install and Run Python Applications in Isolated Environments" readme = "docs/README.md" keywords = [ "cli", "install", "pip", "Virtual Environment", "workflow", ] license = "MIT" license-files = [ "LICENSE" ] authors = [ { name = "Chad Smith", email = "chadsmith.software@gmail.com" }, ] requires-python = ">=3.10" classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", ] dynamic = [ "version", ] dependencies = [ "argcomplete>=1.9.4", "colorama>=0.4.4; sys_platform=='win32'", "packaging>=20", "platformdirs>=2.1", "tomli; python_version<'3.11'", "userpath>=1.6,!=1.9", ] optional-dependencies.uv = [ "uv>=0.4", ] urls."Issue Tracker" = "https://github.com/pypa/pipx/issues" urls."Release Notes" = "https://pipx.pypa.io/latest/changelog/" urls."Source Code" = "https://github.com/pypa/pipx" urls.Documentation = "https://pipx.pypa.io" urls.Homepage = "https://pipx.pypa.io" scripts.pipx = "pipx.main:cli" [dependency-groups] dev = [ { include-group = "docs" }, { include-group = "lint" }, { include-group = "man" }, { include-group = "test" }, ] test = [ "pypiserver[cache,passlib]>=2.3.2", "pytest>=8", "pytest-cov>=4", "pytest-mock>=3.11.1", "pytest-subprocess>=1.5.4", "pytest-xdist>=3.5", "setuptools>=68; python_version>='3.12'", "watchdog>=4", ] docs = [ "jinja2", "markdown-gfm-admonition", "mkdocs<2", "mkdocs-gen-files", "mkdocs-macros-plugin", "mkdocs-material", "towncrier>=23.6", ] lint = [ "pre-commit>=3", ] man = [ "argparse-manpage[setuptools]", ] zipapp = [ "shiv>=1.0.6", ] [tool.hatch] version.source = "vcs" build.hooks.vcs.version-file = "src/pipx/version.py" build.targets.sdist.include = [ "/*.md", "/logo.png", "/pipx_demo.gif", "/src", ] [tool.ruff] line-length = 121 lint.extend-select = [ "A", "B", "C4", "C9", "I", "ISC", "PERF", "PGH", "PIE", "PLC", "PLE", "PLW", "RSE", "RUF", "TC", "UP", "W", ] lint.ignore = [ "PERF203", "PLW1508", "RUF005", ] lint.per-file-ignores."src/pipx/venv.py" = [ "A005", ] lint.isort = { known-first-party = [ "helpers", "package_info", "pipx", ] } lint.mccabe.max-complexity = 15 [tool.codespell] # case sensitive etc ignore-regex = "\\b(UE|path/doesnt/exist)\\b" # Ref: https://github.com/codespell-project/codespell#using-a-config-file skip = ".git,*.pdf,*.svg,.nox,testdata,.mypy_cache" check-hidden = true [tool.mypy] warn_unreachable = true enable_error_code = [ "ignore-without-code", "redundant-expr", "truthy-bool" ] overrides = [ { module = [ "pycowsay.*", ], ignore_missing_imports = true } ] [tool.pytest] ini_options.minversion = "8" ini_options.testpaths = [ "tests" ] ini_options.addopts = [ "-ra", "--strict-config", "--strict-markers" ] ini_options.markers = [ "all_packages: test install with maximum number of packages", ] ini_options.log_level = "INFO" [tool.towncrier] package = "pipx" directory = "changelog.d" filename = "docs/changelog.md" start_string = "\n" title_format = "## [{version}](https://github.com/pypa/pipx/tree/{version}) - {project_date}" issue_format = "[#{issue}](https://github.com/pypa/pipx/issues/{issue})" underlines = [ "", "", "", ] pipx-1.15.0/scripts/000077500000000000000000000000001521706623000142475ustar00rootroot00000000000000pipx-1.15.0/scripts/gen_doc_pages.py000066400000000000000000000030711521706623000173770ustar00rootroot00000000000000import os import sys from pathlib import Path from subprocess import check_output import mkdocs_gen_files from jinja2 import Environment, FileSystemLoader def get_help(cmd: str | None) -> str: base = ["pipx"] args = base + ([cmd] if cmd else []) + ["--help"] env_patch = os.environ.copy() env_patch["PATH"] = os.pathsep.join([str(Path(sys.executable).parent)] + env_patch["PATH"].split(os.pathsep)) content = check_output(args, text=True, env=env_patch) content = content.replace(str(Path("~").expanduser()), "~") return f""" ``` {content} ``` """ params = { "install": get_help("install"), "installall": get_help("install-all"), "uninject": get_help("uninject"), "inject": get_help("inject"), "upgrade": get_help("upgrade"), "upgradeall": get_help("upgrade-all"), "upgradeshared": get_help("upgrade-shared"), "uninstall": get_help("uninstall"), "uninstallall": get_help("uninstall-all"), "reinstall": get_help("reinstall"), "reinstallall": get_help("reinstall-all"), "list": get_help("list"), "interpreter": get_help("interpreter"), "run": get_help("run"), "runpip": get_help("runpip"), "ensurepath": get_help("ensurepath"), "environment": get_help("environment"), "completions": get_help("completions"), "usage": get_help(None), } env = Environment(loader=FileSystemLoader(Path(__file__).parent / "templates")) with mkdocs_gen_files.open("reference/cli.md", "wt") as file_handler: file_handler.write(env.get_template("docs.md").render(**params)) file_handler.write("\n") pipx-1.15.0/scripts/generate_man.py000077500000000000000000000020151521706623000172470ustar00rootroot00000000000000#!/usr/bin/env python3 import os.path import sys import textwrap from typing import cast from build_manpages.manpage import Manpage # type: ignore[import-not-found] from pipx.main import get_command_parser def main(): sys.argv[0] = "pipx" parser, _ = get_command_parser() parser.man_short_description = cast("str", parser.description).splitlines()[1] # type: ignore[attr-defined] manpage = Manpage(parser) body = str(manpage) # Avoid hardcoding build paths in manpages (and improve readability) body = body.replace(os.path.expanduser("~").replace("-", "\\-"), "~") # Add a credit section body += textwrap.dedent( """ .SH AUTHORS .IR pipx (1) was written by Chad Smith and contributors. The project can be found online at .UR https://pipx.pypa.io .UE .SH SEE ALSO .IR pip (1), .IR virtualenv (1) """ ) with open("pipx.1", "w") as f: f.write(body) if __name__ == "__main__": main() pipx-1.15.0/scripts/list_test_packages.py000077500000000000000000000123341521706623000204770ustar00rootroot00000000000000#!/usr/bin/env python3 import argparse import re import subprocess import sys import tempfile from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path from typing import Any from test_packages_support import get_platform_list_path def process_command_line(argv: list[str]) -> argparse.Namespace: """Process command line invocation arguments and switches. Args: argv: list of arguments, or `None` from ``sys.argv[1:]``. Returns: argparse.Namespace: named attributes of arguments and switches """ # script_name = argv[0] argv = argv[1:] # initialize the parser object: parser = argparse.ArgumentParser( description="Create list of needed test packages for pipx tests and local pypiserver." ) # specifying nargs= puts outputs of parser in list (even if nargs=1) # required arguments parser.add_argument( "primary_package_list", help="Main packages to examine, getting list of matching distribution files and dependencies.", ) parser.add_argument("package_list_dir", help="Directory to output package distribution lists.") # switches/options: parser.add_argument("-v", "--verbose", action="store_true", help="Maximum verbosity, especially for pip operations.") return parser.parse_args(argv) def parse_package_list(package_list_file: Path) -> list[dict[str, Any]]: output_list: list[dict[str, Any]] = [] try: with package_list_file.open("r") as package_list_fh: for line in package_list_fh: line_parsed = re.sub(r"#.+$", "", line) if not re.search(r"\S", line_parsed): continue line_list = line_parsed.strip().split() if len(line_list) == 1: output_list.append({"spec": line_list[0]}) elif len(line_list) == 2: output_list.append({"spec": line_list[0], "no-deps": line_list[1].lower() == "true"}) else: print(f"ERROR: Unable to parse primary package list line:\n {line.strip()}") return [] except OSError: print("ERROR: File problem reading primary package list.") return [] return output_list def create_test_packages_list(package_list_dir_path: Path, primary_package_list_path: Path, verbose: bool) -> int: exit_code = 0 package_list_dir_path.mkdir(exist_ok=True) platform_package_list_path = get_platform_list_path(package_list_dir_path) primary_test_packages = parse_package_list(primary_package_list_path) if not primary_test_packages: print(f"ERROR: Problem reading {primary_package_list_path}. Exiting.", file=sys.stderr) return 1 with ThreadPoolExecutor(max_workers=12) as pool: futures = {pool.submit(download, pkg, verbose) for pkg in primary_test_packages} downloaded_list = set() for future in as_completed(futures): downloaded_list.update(future.result()) all_packages = [] for downloaded_path in downloaded_list: wheel_re = re.search(r"([^-]+)-([^-]+)-([^-]+)\-([^-]+)-([^-]+)(-[^-]+)?\.whl$", downloaded_path) src_re = re.search(r"(.+)-([^-]+)\.(?:tar.gz|zip)$", downloaded_path) if wheel_re: package_name = wheel_re.group(1) package_version = wheel_re.group(2) elif src_re: package_name = src_re.group(1) package_version = src_re.group(2) else: print(f"ERROR: cannot parse: {downloaded_path}", file=sys.stderr) continue all_packages.append(f"{package_name}=={package_version}") with platform_package_list_path.open("w") as package_list_fh: for package in sorted(all_packages): print(package, file=package_list_fh) return exit_code def download(test_package: dict[str, str], verbose: bool) -> set[str]: no_deps = test_package.get("no-deps", False) test_package_option_string = " (no-deps)" if no_deps else "" verbose_this_iteration = False with tempfile.TemporaryDirectory() as download_dir: cmd_list = ["pip", "download"] + (["--no-deps"] if no_deps else []) + [test_package["spec"], "-d", download_dir] if verbose: print(f"CMD: {' '.join(cmd_list)}") pip_download_process = subprocess.run(cmd_list, capture_output=True, text=True, check=False) if pip_download_process.returncode == 0: print(f"Examined {test_package['spec']}{test_package_option_string}") else: print(f"ERROR with {test_package['spec']}{test_package_option_string}", file=sys.stderr) verbose_this_iteration = True if verbose or verbose_this_iteration: print(pip_download_process.stdout) print(pip_download_process.stderr) return {i.name for i in Path(download_dir).iterdir()} def main(argv: list[str]) -> int: args = process_command_line(argv) return create_test_packages_list(Path(args.package_list_dir), Path(args.primary_package_list), args.verbose) if __name__ == "__main__": try: status = main(sys.argv) except KeyboardInterrupt: print("Stopped by Keyboard Interrupt", file=sys.stderr) status = 130 sys.exit(status) pipx-1.15.0/scripts/migrate_pipsi_to_pipx.py000077500000000000000000000035471521706623000212330ustar00rootroot00000000000000#!/usr/bin/env python3 """ Script to migrate from pipsi to pipx """ import os import subprocess import sys from pathlib import Path from shutil import which def main(): if not which("pipx"): sys.exit("pipx must be installed to migrate from pipsi to pipx") if not sys.stdout.isatty(): sys.exit("Must be run from a terminal, not a script") pipsi_home = os.environ.get("PIPSI_HOME", os.path.expanduser("~/.local/venvs/")) packages = [p.name for p in Path(pipsi_home).iterdir()] if not packages: print("No packages installed with pipsi") sys.exit(0) print("Attempting to migrate the following packages from pipsi to pipx:") for package in packages: print(f" - {package}") answer = None while answer not in ["y", "n"]: answer = input("Continue? [y/n] ") if answer == "n": sys.exit(0) error = False for package in packages: ret = subprocess.run(["pipsi", "uninstall", "--yes", package], check=False) if ret.returncode: error = True print(f"Failed to uninstall {package!r} with pipsi. Not attempting to install with pipx.") else: print(f"uninstalled {package!r} with pipsi. Now attempting to install with pipx.") ret = subprocess.run(["pipx", "install", package], check=False) if ret.returncode: error = True print(f"Failed to install {package!r} with pipx.") else: print(f"Successfully installed {package} with pipx") print(f"Done migrating {len(packages)} packages!") print("You may still need to run `pipsi uninstall pipsi` or `pip uninstall pipsi`. Refer to pipsi's documentation.") if error: print("Note: Finished with errors. Review output to manually complete migration.") if __name__ == "__main__": main() pipx-1.15.0/scripts/release.py000066400000000000000000000073761521706623000162560ustar00rootroot00000000000000"""Handles creating a release.""" from __future__ import annotations import os from pathlib import Path from subprocess import call, check_call from urllib.parse import urlsplit, urlunsplit from git import Commit, Remote, Repo, TagReference # type: ignore[import-not-found] from packaging.version import Version ROOT_DIR = Path(__file__).resolve().parents[1] CHANGELOG_DIR = ROOT_DIR / "changelog.d" def main(version_str: str, *, push: bool) -> None: repo = Repo(str(ROOT_DIR)) if repo.is_dirty(): msg = "Current repository is dirty. Please commit any changes and try again." raise RuntimeError(msg) remote = get_remote(repo) remote.fetch() version = resolve_version(version_str, repo) print(f"Releasing {version}") release_commit = release_changelog(repo, version) tag = tag_release_commit(release_commit, repo, version) if push: # The checkout uses persist-credentials: false, so no token lives in the git config; # authenticate the push via GH_RELEASE_TOKEN embedded into the remote URL. push_target = authenticated_url(remote) print("Pushing release commit") repo.git.push(push_target, "HEAD:main") print("Pushing release tag") repo.git.push(push_target, str(tag)) print("All done! โœจ ๐Ÿฐ โœจ") def authenticated_url(remote: Remote) -> str: if (token := os.environ.get("GH_RELEASE_TOKEN")) is None: return remote.name split = urlsplit(next(iter(remote.urls))) if split.scheme != "https": return remote.name return urlunsplit(split._replace(netloc=f"x-access-token:{token}@{split.hostname}")) def resolve_version(version_str: str, repo: Repo) -> Version: if version_str not in {"auto", "major", "minor", "patch"}: return Version(version_str) latest_tag = repo.git.describe("--tags", "--abbrev=0") parts = [int(x) for x in latest_tag.split(".")] if version_str == "major": parts = [parts[0] + 1, 0, 0] elif version_str == "minor": parts = [parts[0], parts[1] + 1, 0] elif version_str == "patch": parts[2] += 1 elif any(CHANGELOG_DIR.glob("*.feature.md")) or any(CHANGELOG_DIR.glob("*.removal.md")): parts = [parts[0], parts[1] + 1, 0] else: parts[2] += 1 return Version(".".join(str(p) for p in parts)) def get_remote(repo: Repo) -> Remote: upstream_remote = "pypa/pipx" urls = set() for remote in repo.remotes: for url in remote.urls: if url.rstrip(".git").endswith(upstream_remote): return remote urls.add(url) msg = f"Could not find {upstream_remote} remote, has {urls}" raise RuntimeError(msg) def release_changelog(repo: Repo, version: Version) -> Commit: print("Generating release commit") check_call(["towncrier", "build", "--yes", "--version", version.public], cwd=str(ROOT_DIR)) call(["pre-commit", "run", "--all-files"], cwd=str(ROOT_DIR)) repo.git.add(".") check_call(["pre-commit", "run", "--all-files"], cwd=str(ROOT_DIR)) return repo.index.commit(f"Release {version}") def tag_release_commit(release_commit: Commit, repo: Repo, version: Version) -> TagReference: print("Tagging release commit") existing_tags = [x.name for x in repo.tags] if str(version) in existing_tags: print(f"Deleting existing tag {version}") repo.delete_tag(str(version)) print(f"Creating tag {version}") return repo.create_tag(str(version), ref=release_commit, force=True) if __name__ == "__main__": import argparse parser = argparse.ArgumentParser(prog="release") parser.add_argument("--version", default="auto") parser.add_argument("--no-push", action="store_true") options = parser.parse_args() main(options.version, push=not options.no_push) pipx-1.15.0/scripts/templates/000077500000000000000000000000001521706623000162455ustar00rootroot00000000000000pipx-1.15.0/scripts/templates/docs.md000066400000000000000000000011771521706623000175250ustar00rootroot00000000000000{{usage}} ### pipx install {{install}} ### pipx install-all {{installall}} ### pipx uninject {{uninject}} ### pipx inject {{inject}} ### pipx upgrade {{upgrade}} ### pipx upgrade-all {{upgradeall}} ### pipx upgrade-shared {{upgradeshared}} ### pipx uninstall {{uninstall}} ### pipx uninstall-all {{uninstallall}} ### pipx reinstall {{reinstall}} ### pipx reinstall-all {{reinstallall}} ### pipx list {{list}} ### pipx interpreter {{interpreter}} ### pipx run {{run}} ### pipx runpip {{runpip}} ### pipx ensurepath {{ensurepath}} ### pipx environment {{environment}} ### pipx completions {{completions}} pipx-1.15.0/scripts/test_packages_support.py000066400000000000000000000011421521706623000212300ustar00rootroot00000000000000import platform import sys from pathlib import Path PYTHON_VERSION_STR = f"{sys.version_info[0]}.{sys.version_info[1]}" # Platform logic if sys.platform == "darwin": FULL_PLATFORM = "macos" + platform.release().split(".")[0] elif sys.platform == "win32": FULL_PLATFORM = "win" else: FULL_PLATFORM = "unix" def get_platform_list_path(package_list_dir_path: Path) -> Path: return package_list_dir_path / f"{FULL_PLATFORM}-python{PYTHON_VERSION_STR}.txt" def get_platform_packages_dir_path(pipx_package_cache_path: Path) -> Path: return pipx_package_cache_path / f"{PYTHON_VERSION_STR}" pipx-1.15.0/scripts/update_package_cache.py000077500000000000000000000146051521706623000207120ustar00rootroot00000000000000#!/usr/bin/env python3 import argparse import re import subprocess import sys from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path from list_test_packages import create_test_packages_list from test_packages_support import get_platform_list_path, get_platform_packages_dir_path def process_command_line(argv: list[str]) -> argparse.Namespace: """Process command line invocation arguments and switches. Args: argv: list of arguments, or `None` from ``sys.argv[1:]``. Returns: argparse.Namespace: named attributes of arguments and switches """ # script_name = argv[0] argv = argv[1:] # initialize the parser object: parser = argparse.ArgumentParser( description="Check and update as needed the pipx tests package cache " "for use with the pipx tests local pypiserver." ) # specifying nargs= puts outputs of parser in list (even if nargs=1) # required arguments parser.add_argument( "package_list_dir", help="Directory where platform- and python-specific package lists are found for pipx tests.", ) parser.add_argument( "pipx_package_cache_dir", help="Directory to store the packages distribution files.", ) # switches/options: parser.add_argument( "-c", "--check-only", action="store_true", help="Only check to see if needed packages are in PACKAGES_DIR, do not download or delete files.", ) return parser.parse_args(argv) def update_test_packages_cache(package_list_dir_path: Path, pipx_package_cache_path: Path, check_only: bool) -> int: exit_code = 0 platform_package_list_path = get_platform_list_path(package_list_dir_path) packages_dir_path = get_platform_packages_dir_path(pipx_package_cache_path) packages_dir_path.mkdir(exist_ok=True, parents=True) packages_dir_files = list(packages_dir_path.iterdir()) if not platform_package_list_path.exists(): print( f"WARNING. File {platform_package_list_path!s}\n does not exist. Creating now...", file=sys.stderr, ) create_list_returncode = create_test_packages_list( package_list_dir_path, package_list_dir_path / "primary_packages.txt", verbose=False, ) if create_list_returncode == 0: print( f"File {platform_package_list_path!s}\n" " successfully created. Please check this file in to the" " repository for future use.", file=sys.stderr, ) else: print( f"ERROR. Unable to create {platform_package_list_path!s}\n Cannot continue.\n", file=sys.stderr, ) return 1 try: platform_package_list_fh = platform_package_list_path.open("r") except OSError: print( f"ERROR. File {platform_package_list_path!s}\n is not readable. Cannot continue.\n", file=sys.stderr, ) return 1 else: platform_package_list_fh.close() print("Using the following file to specify needed package files:") print(f" {platform_package_list_path!s}") print("Ensuring the following directory contains necessary package files:") print(f" {packages_dir_path!s}") packages_dir_hits = [] packages_dir_missing = [] with platform_package_list_path.open("r") as platform_package_list_fh: for line in platform_package_list_fh: package_spec = line.strip() package_spec_re = re.search(r"^(.+)==(.+)$", package_spec) if not package_spec_re: print(f"ERROR: CANNOT PARSE {package_spec}", file=sys.stderr) exit_code = 1 continue package_name = package_spec_re.group(1) package_ver = package_spec_re.group(2) package_dist_patt = re.escape(package_name) + r"-" + re.escape(package_ver) + r"(.tar.gz|.zip|-)" matches = [ output_dir_file for output_dir_file in packages_dir_files if re.search(package_dist_patt, output_dir_file.name) ] if len(matches) == 1: packages_dir_files.remove(matches[0]) packages_dir_hits.append(matches[0]) continue elif len(matches) > 1: print(f"ERROR: more than one match for {package_spec}.", file=sys.stderr) print(f" {matches}", file=sys.stderr) exit_code = 1 continue packages_dir_missing.append(package_spec) print(f"MISSING FILES: {len(packages_dir_missing)}") print(f"EXISTING (found) FILES: {len(packages_dir_hits)}") print(f"LEFTOVER (unused) FILES: {len(packages_dir_files)}") if check_only: return 0 if len(packages_dir_missing) == 0 else 1 else: with ThreadPoolExecutor(max_workers=4) as pool: futures = {pool.submit(download, pkg, packages_dir_path) for pkg in packages_dir_missing} for future in as_completed(futures): exit_code = future.result() or exit_code for unused_file in packages_dir_files: print(f"Deleting {unused_file}...") unused_file.unlink() return exit_code def download(package_spec: str, packages_dir_path: Path) -> int: pip_download_process = subprocess.run( [ sys.executable, "-m", "pip", "download", "--no-deps", package_spec, "-d", str(packages_dir_path), ], capture_output=True, text=True, check=False, ) if pip_download_process.returncode == 0: print(f"Successfully downloaded {package_spec}") return 0 print(f"ERROR downloading {package_spec}", file=sys.stderr) print(pip_download_process.stdout, file=sys.stderr) print(pip_download_process.stderr, file=sys.stderr) return 1 def main(argv: list[str]) -> int: args = process_command_line(argv) return update_test_packages_cache(Path(args.package_list_dir), Path(args.pipx_package_cache_dir), args.check_only) if __name__ == "__main__": try: status = main(sys.argv) except KeyboardInterrupt: print("Stopped by Keyboard Interrupt", file=sys.stderr) status = 130 sys.exit(status) pipx-1.15.0/src/000077500000000000000000000000001521706623000133475ustar00rootroot00000000000000pipx-1.15.0/src/pipx/000077500000000000000000000000001521706623000143275ustar00rootroot00000000000000pipx-1.15.0/src/pipx/__init__.py000066400000000000000000000002571521706623000164440ustar00rootroot00000000000000import sys if sys.version_info < (3, 10, 0): # noqa: UP036 sys.exit("Python 3.10 or later is required. See https://github.com/pypa/pipx for installation instructions.") pipx-1.15.0/src/pipx/__main__.py000066400000000000000000000006231521706623000164220ustar00rootroot00000000000000import os import sys if not __spec__ or not __spec__.parent: # Running from source. Add pipx's source code to the system # path to allow direct invocation, such as: # python src/pipx --help pipx_package_source_path = os.path.dirname(os.path.dirname(__file__)) sys.path.insert(0, pipx_package_source_path) from pipx.main import cli if __name__ == "__main__": sys.exit(cli()) pipx-1.15.0/src/pipx/animate.py000066400000000000000000000076011521706623000163230ustar00rootroot00000000000000import logging import shutil import sys from collections.abc import Generator from contextlib import contextmanager from threading import Event, Thread from pipx.constants import WINDOWS from pipx.emojis import EMOJI_SUPPORT logger = logging.getLogger(__name__) stderr_is_tty = bool(sys.stderr and sys.stderr.isatty()) CLEAR_LINE = "\033[K" EMOJI_ANIMATION_FRAMES = ["โฃท", "โฃฏ", "โฃŸ", "โกฟ", "โขฟ", "โฃป", "โฃฝ", "โฃพ"] NONEMOJI_ANIMATION_FRAMES = ["", ".", "..", "..."] EMOJI_FRAME_PERIOD = 0.1 NONEMOJI_FRAME_PERIOD = 1 MINIMUM_COLS_ALLOW_ANIMATION = 16 if WINDOWS: import ctypes class _CursorInfo(ctypes.Structure): _fields_ = (("size", ctypes.c_int), ("visible", ctypes.c_byte)) def _env_supports_animation() -> bool: (term_cols, _) = shutil.get_terminal_size(fallback=(0, 0)) return stderr_is_tty and term_cols > MINIMUM_COLS_ALLOW_ANIMATION @contextmanager def animate(message: str, do_animation: bool, *, delay: float = 0) -> Generator[None, None, None]: pipx_logger = logging.getLogger("pipx") handler_level = pipx_logger.handlers[0].level if pipx_logger.handlers else 0 if pipx_logger.handlers and handler_level > logging.WARNING: yield return if not do_animation or not _env_supports_animation(): sys.stderr.write(f"{message}...\n") yield return event = Event() if EMOJI_SUPPORT: animate_at_beginning_of_line = True symbols = EMOJI_ANIMATION_FRAMES period = EMOJI_FRAME_PERIOD else: animate_at_beginning_of_line = False symbols = NONEMOJI_ANIMATION_FRAMES period = NONEMOJI_FRAME_PERIOD thread_kwargs = { "message": message, "event": event, "symbols": symbols, "delay": delay, "period": period, "animate_at_beginning_of_line": animate_at_beginning_of_line, } t = Thread(target=print_animation, kwargs=thread_kwargs) t.start() try: yield finally: event.set() clear_line() def print_animation( *, message: str, event: Event, symbols: list[str], delay: float, period: float, animate_at_beginning_of_line: bool, ) -> None: (term_cols, _) = shutil.get_terminal_size(fallback=(9999, 24)) event.wait(delay) while not event.wait(0): for s in symbols: if animate_at_beginning_of_line: max_message_len = term_cols - len(f"{s} ... ") cur_line = f"{s} {message:.{max_message_len}}" if len(message) > max_message_len: cur_line += "..." else: max_message_len = term_cols - len("... ") cur_line = f"{message:.{max_message_len}}{s}" clear_line() sys.stderr.write(cur_line) sys.stderr.flush() if event.wait(period): break # for Windows pre-ANSI-terminal-support (before Windows 10 TH2 (v1511)) # https://stackoverflow.com/a/10455937 def win_cursor(visible: bool) -> None: if sys.platform != "win32": # hello mypy return ci = _CursorInfo() # type: ignore[unreachable] handle = ctypes.windll.kernel32.GetStdHandle(-11) ctypes.windll.kernel32.GetConsoleCursorInfo(handle, ctypes.byref(ci)) ci.visible = visible ctypes.windll.kernel32.SetConsoleCursorInfo(handle, ctypes.byref(ci)) def hide_cursor() -> None: if stderr_is_tty: if WINDOWS: win_cursor(visible=False) else: sys.stderr.write("\033[?25l") sys.stderr.flush() def show_cursor() -> None: if stderr_is_tty: if WINDOWS: win_cursor(visible=True) else: sys.stderr.write("\033[?25h") sys.stderr.flush() def clear_line() -> None: """Clears current line and positions cursor at start of line""" sys.stderr.write(f"\r{CLEAR_LINE}") sys.stdout.write(f"\r{CLEAR_LINE}") pipx-1.15.0/src/pipx/backends/000077500000000000000000000000001521706623000161015ustar00rootroot00000000000000pipx-1.15.0/src/pipx/backends/__init__.py000066400000000000000000000050071521706623000202140ustar00rootroot00000000000000from __future__ import annotations import os from functools import cache from pipx.backends._base import KNOWN_BACKENDS, PIP, UV, Backend from pipx.backends.pip import PipBackend from pipx.backends.uv import UvBackend, find_uv_binary from pipx.util import PipxError def resolve_backend_name( *, cli_value: str | None = None, env_value: str | None = None, metadata_value: str | None = None, auto: bool = True, ) -> tuple[str, str]: """Return ``(name, source)`` per precedence cli > metadata > env > auto. ``metadata`` sits above ``env`` so ``PIPX_DEFAULT_BACKEND=uv`` cannot silently retarget pip-backed venvs already on disk. """ for candidate, source in ((cli_value, "cli"), (metadata_value, "metadata"), (env_value, "env")): if (validated := _validate(candidate)) is not None: return validated, source if auto and (binary_source := find_uv_binary()[1]) != "missing": return UV, f"auto-{binary_source}" return PIP, "auto-pip" @cache def get_backend(name: str) -> Backend: # Cached so ``UvBackend.__init__``'s version probe and log line fire once # per process even when validation + construction both ask for the backend. if name == PIP: return PipBackend() if name == UV: return UvBackend() raise PipxError(f"Unknown backend {name!r}. Valid backends: {', '.join(KNOWN_BACKENDS)}.") def assert_not_pip_under_uv(package_name: str, backend_name: str) -> None: # Named narrowly: a future backend with its own deny-list should grow its # own guard rather than overload this one. if package_name == "pip" and backend_name != PIP: raise PipxError( "The 'pip' package cannot be installed or exposed via the uv backend, since uv venvs " "don't ship pip and the resulting environment would be inconsistent.\n" "Use `--backend pip` (or set PIPX_DEFAULT_BACKEND=pip) to install 'pip' as a tool." ) def env_default_backend() -> str | None: raw = os.environ.get("PIPX_DEFAULT_BACKEND") return raw.strip() if raw else None def _validate(candidate: str | None) -> str | None: if not candidate: return None if candidate not in KNOWN_BACKENDS: raise PipxError(f"Unknown backend {candidate!r}. Valid backends: {', '.join(KNOWN_BACKENDS)}.") return candidate __all__ = [ "KNOWN_BACKENDS", "PIP", "UV", "Backend", "assert_not_pip_under_uv", "env_default_backend", "find_uv_binary", "get_backend", "resolve_backend_name", ] pipx-1.15.0/src/pipx/backends/_base.py000066400000000000000000000035571521706623000175360ustar00rootroot00000000000000from __future__ import annotations from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Final if TYPE_CHECKING: from pathlib import Path from subprocess import CompletedProcess PIP: Final[str] = "pip" UV: Final[str] = "uv" KNOWN_BACKENDS: Final[tuple[str, ...]] = (PIP, UV) class Backend(ABC): name: str @abstractmethod def create_venv( self, root: Path, *, python: str, venv_args: list[str], pip_args: list[str], include_pip: bool, verbose: bool, ) -> None: ... @abstractmethod def install( self, *, venv_root: Path, venv_python: Path, requirements: list[str], pip_args: list[str], no_deps: bool = False, upgrade: bool = False, log_pip_errors: bool = True, verbose: bool = False, ) -> CompletedProcess[str]: ... @abstractmethod def uninstall( self, *, venv_root: Path, venv_python: Path, package: str, verbose: bool, ) -> CompletedProcess[str]: ... @abstractmethod def list_installed( self, *, venv_root: Path, venv_python: Path, not_required: bool = False, ) -> set[str]: ... @abstractmethod def run_raw_pip( self, *, venv_root: Path, venv_python: Path, args: list[str], capture_stdout: bool = True, capture_stderr: bool = True, verbose: bool = False, ) -> CompletedProcess[str]: ... @abstractmethod def needs_shared_libs(self) -> bool: ... @abstractmethod def upgrade_packaging_libraries( self, venv_python: Path, pip_args: list[str], *, verbose: bool, ) -> None: ... __all__ = [ "KNOWN_BACKENDS", "PIP", "UV", "Backend", ] pipx-1.15.0/src/pipx/backends/pip.py000066400000000000000000000111151521706623000172420ustar00rootroot00000000000000from __future__ import annotations import json import logging from typing import TYPE_CHECKING, Final from pipx.animate import animate from pipx.backends._base import PIP, Backend from pipx.constants import PIPX_SHARED_PTH from pipx.shared_libs import shared_libs from pipx.util import ( PipxError, get_site_packages, get_venv_paths, run_subprocess, subprocess_post_check, subprocess_post_check_handle_pip_error, ) from pipx.venv_inspect import list_not_required_packages if TYPE_CHECKING: from pathlib import Path from subprocess import CompletedProcess _LOGGER: Final[logging.Logger] = logging.getLogger(__name__) class PipBackend(Backend): name = PIP def needs_shared_libs(self) -> bool: return True def upgrade_packaging_libraries( self, venv_python: Path, pip_args: list[str], *, verbose: bool, ) -> None: # Reached only for ``pipx install pip``-style venvs that ship pip # in-tree; shared-libs venvs upgrade through ``shared_libs.upgrade``. process = run_subprocess( [str(venv_python), "-m", "pip", "--no-input", "install", "--upgrade", *pip_args, "pip"], run_dir=str(venv_python.parent.parent), ) subprocess_post_check(process) def create_venv( self, root: Path, *, python: str, venv_args: list[str], pip_args: list[str], include_pip: bool, verbose: bool, ) -> None: cmd = [python, "-m", "venv"] if not include_pip: cmd.append("--without-pip") cmd += [*venv_args, str(root)] with animate("creating virtual environment", not verbose): venv_process = run_subprocess(cmd, run_dir=str(root)) subprocess_post_check(venv_process) shared_libs.create(verbose=verbose, pip_args=pip_args) if not include_pip: _, python_path, _ = get_venv_paths(root) pipx_pth = get_site_packages(python_path) / PIPX_SHARED_PTH pipx_pth.write_text(f"{shared_libs.site_packages}\n") def install( self, *, venv_root: Path, venv_python: Path, requirements: list[str], pip_args: list[str], no_deps: bool = False, upgrade: bool = False, log_pip_errors: bool = True, verbose: bool = False, ) -> CompletedProcess[str]: cmd: list[str] = [str(venv_python), "-m", "pip", "--no-input", "install"] if upgrade: cmd.append("--upgrade") if no_deps: cmd.append("--no-dependencies") cmd += [*pip_args, *requirements] process = run_subprocess( cmd, log_stdout=not log_pip_errors, log_stderr=not log_pip_errors, run_dir=str(venv_root), ) if log_pip_errors: subprocess_post_check_handle_pip_error(process) return process def uninstall( self, *, venv_root: Path, venv_python: Path, package: str, verbose: bool, ) -> CompletedProcess[str]: cmd = [str(venv_python), "-m", "pip", "uninstall", "-y", package] if not verbose: cmd.append("-q") process = run_subprocess(cmd, run_dir=str(venv_root)) subprocess_post_check(process) return process def list_installed( self, *, venv_root: Path, venv_python: Path, not_required: bool = False, ) -> set[str]: del venv_root if not_required: return list_not_required_packages(venv_python) cmd = [str(venv_python), "-m", "pip", "list", "--format=json"] process = run_subprocess(cmd) if process.returncode != 0: raise PipxError( f"Failed to execute {process.args}.\n" f"Process exited with return code {process.returncode}.\n" f"stderr: {process.stderr}" ) return {entry["name"] for entry in json.loads(process.stdout.strip())} def run_raw_pip( self, *, venv_root: Path, venv_python: Path, args: list[str], capture_stdout: bool = True, capture_stderr: bool = True, verbose: bool = False, ) -> CompletedProcess[str]: cmd = [str(venv_python), "-m", "pip", *args] if not verbose: cmd.append("-q") return run_subprocess( cmd, capture_stdout=capture_stdout, capture_stderr=capture_stderr, run_dir=str(venv_root), ) __all__ = [ "PipBackend", ] pipx-1.15.0/src/pipx/backends/uv.py000066400000000000000000000223571521706623000171160ustar00rootroot00000000000000from __future__ import annotations import json import logging import re import shutil import subprocess from functools import cache from pathlib import Path from typing import TYPE_CHECKING, Final from packaging.version import InvalidVersion, Version from pipx.animate import animate from pipx.backends._base import UV, Backend from pipx.util import ( PipxError, run_subprocess, subprocess_post_check, subprocess_post_check_handle_pip_error, ) from pipx.venv_inspect import list_not_required_packages if TYPE_CHECKING: from collections.abc import Callable from subprocess import CompletedProcess _LOGGER: Final[logging.Logger] = logging.getLogger(__name__) # Imported into a temporary, then assigned through Final and deleted so mypy # sees one Final binding (it rejects redefinition across try/except branches). try: from uv import find_uv_bin as _uv_bin_from_extra # type: ignore[import-not-found] except ImportError: _uv_bin_from_extra = None _FIND_UV_BIN_FROM_EXTRA: Final[Callable[[], str] | None] = _uv_bin_from_extra del _uv_bin_from_extra _MIN_UV_VERSION: Final[Version] = Version("0.4.0") _VERSION_RE: Final[re.Pattern[str]] = re.compile( r""" uv \s+ # the literal "uv " prefix from `uv --version` (\S+) # capture the version token; PEP 440 parser validates it """, re.VERBOSE, ) # Stripping VIRTUAL_ENV stops uv from auto-targeting an active venv when no # ``--python`` flag is passed. _UV_ENV_OVERRIDES: Final[dict[str, str | None]] = {"VIRTUAL_ENV": None, "UV_NO_PROGRESS": "1"} class UvBackend(Backend): name = UV def __init__(self) -> None: self._binary = resolve_uv_binary() _, self._source = find_uv_binary() version = _check_uv_version(self._binary) _LOGGER.info(f"using {self._source} uv {version} from {self._binary}") def needs_shared_libs(self) -> bool: return False def upgrade_packaging_libraries(self, venv_python: Path, pip_args: list[str], *, verbose: bool) -> None: del venv_python, pip_args, verbose # uv venvs ship no pip to upgrade. def create_venv( self, root: Path, *, python: str, venv_args: list[str], pip_args: list[str], include_pip: bool, verbose: bool, ) -> None: del pip_args # uv venv has no pip to seed. if include_pip: raise PipxError( "The uv backend cannot create a virtual environment with pip preinstalled.\n" "Reinstall the package with `--backend pip` (or unset PIPX_DEFAULT_BACKEND)." ) cmd: list[str | Path] = [self._binary, "venv", "--python", python, *venv_args] cmd.append("--verbose" if verbose else "--quiet") cmd.append(str(root)) with animate("creating virtual environment", not verbose): process = run_subprocess(cmd, run_dir=str(root), env_overrides=_UV_ENV_OVERRIDES) subprocess_post_check(process) def install( self, *, venv_root: Path, venv_python: Path, requirements: list[str], pip_args: list[str], no_deps: bool = False, upgrade: bool = False, log_pip_errors: bool = True, verbose: bool = False, ) -> CompletedProcess[str]: cmd = self._uv_pip_command("install", venv_python, verbose=verbose) if upgrade: cmd.append("--upgrade") if no_deps: cmd.append("--no-deps") cmd += [*_strip_pip_quiet_flags(pip_args), *requirements] process = run_subprocess( cmd, run_dir=str(venv_root), log_stdout=not log_pip_errors, log_stderr=not log_pip_errors, env_overrides=_UV_ENV_OVERRIDES, ) if log_pip_errors: subprocess_post_check_handle_pip_error(process, tool_name="uv") return process def uninstall( self, *, venv_root: Path, venv_python: Path, package: str, verbose: bool, ) -> CompletedProcess[str]: cmd = self._uv_pip_command("uninstall", venv_python, verbose=verbose) cmd.append(package) process = run_subprocess(cmd, run_dir=str(venv_root), env_overrides=_UV_ENV_OVERRIDES) subprocess_post_check(process) return process def list_installed( self, *, venv_root: Path, venv_python: Path, not_required: bool = False, ) -> set[str]: if not_required: return list_not_required_packages(venv_python) cmd = self._uv_pip_command("list", venv_python, verbose=False) cmd += ["--format", "json"] process = run_subprocess(cmd, run_dir=str(venv_root), env_overrides=_UV_ENV_OVERRIDES) if process.returncode != 0: raise PipxError( f"Failed to execute {process.args}.\n" f"Process exited with return code {process.returncode}.\n" f"stderr: {process.stderr}" ) return {entry["name"] for entry in json.loads(process.stdout.strip())} def run_raw_pip( self, *, venv_root: Path, venv_python: Path, args: list[str], capture_stdout: bool = True, capture_stderr: bool = True, verbose: bool = False, ) -> CompletedProcess[str]: # Mirror pip-backend ``pipx runpip TOOL`` (no args -> pip's help). cmd: list[str | Path] = [self._binary, "pip", args[0] if args else "--help"] cmd += ["--python", str(venv_python)] if verbose: cmd.append("--verbose") elif args: cmd.append("--quiet") cmd += _strip_pip_quiet_flags(args[1:]) return run_subprocess( cmd, run_dir=str(venv_root), capture_stdout=capture_stdout, capture_stderr=capture_stderr, env_overrides=_UV_ENV_OVERRIDES, ) def _uv_pip_command(self, subcommand: str, venv_python: Path, *, verbose: bool) -> list[str | Path]: cmd: list[str | Path] = [self._binary, "pip", subcommand, "--python", str(venv_python)] cmd.append("--verbose" if verbose else "--quiet") return cmd def resolve_uv_binary() -> Path: # The version check fires here so non-Backend callers (e.g. ``pipx run``'s # uv-tool-run shortcut) still get the "needs uv >= X" message rather than # an opaque uv-side error. binary, _ = find_uv_binary() if binary is None: raise PipxError( "The uv backend was requested but the 'uv' executable could not be found.\n" "Install pipx with the uv extra (`pipx install pipx[uv]`) or place 'uv' on your PATH.\n" "Alternatively, run with `--backend pip` (or set PIPX_DEFAULT_BACKEND=pip)." ) _check_uv_version(binary) return binary @cache def find_uv_binary() -> tuple[Path | None, str]: # Cached so ``upgrade-all`` and similar hot loops don't re-walk PATH per venv. if _FIND_UV_BIN_FROM_EXTRA is not None: try: bundled = Path(_FIND_UV_BIN_FROM_EXTRA()) except FileNotFoundError: pass else: # ``is_file`` rejects a stale path from a half-installed extra; # ``_binary_runs`` catches the exec-fails case (missing dylib, # ENOEXEC) so we fall through to PATH instead of erroring later. if bundled.is_file() and _binary_runs(bundled): return bundled, "bundled" if path := shutil.which("uv"): return Path(path), "path" return None, "missing" def _binary_runs(binary: Path) -> bool: # Liveness probe; full floor-version check stays in ``_check_uv_version``. try: process = subprocess.run([str(binary), "--version"], check=False, text=True, capture_output=True, timeout=10) except OSError as exc: _LOGGER.debug(f"uv launch probe failed for {binary}: {exc}") return False if process.returncode != 0 or not _VERSION_RE.search(process.stdout): _LOGGER.debug( f"uv launch probe rejected {binary}: " f"rc={process.returncode}, stdout={process.stdout!r}, stderr={process.stderr!r}" ) return False return True @cache def _check_uv_version(binary: Path) -> Version: # Cached so ``upgrade-all`` over many venvs doesn't fork uv repeatedly. process = subprocess.run([str(binary), "--version"], check=False, text=True, capture_output=True) if (match := _VERSION_RE.search(process.stdout)) is None: raise PipxError( f"Could not parse uv version from {binary} " f"(rc={process.returncode}, stdout={process.stdout!r}, stderr={process.stderr!r})." ) try: version = Version(match.group(1)) except InvalidVersion as exc: raise PipxError(f"Unrecognized uv version {match.group(1)!r}.") from exc if version < _MIN_UV_VERSION: raise PipxError( f"pipx needs uv>={_MIN_UV_VERSION}, but {binary} reports {version}.\n" "Upgrade uv (`uv self update` or reinstall pipx[uv]), or run with `--backend pip` to bypass." ) return version def _strip_pip_quiet_flags(pip_args: list[str]) -> list[str]: return [arg for arg in pip_args if arg not in ("-q", "-qq", "--quiet")] __all__ = [ "UvBackend", "find_uv_binary", "resolve_uv_binary", ] pipx-1.15.0/src/pipx/colors.py000066400000000000000000000015271521706623000162070ustar00rootroot00000000000000import sys from collections.abc import Callable try: import colorama # type: ignore[import-untyped] except ImportError: # Colorama is Windows only package colorama = None PRINT_COLOR = bool(sys.stdout and sys.stdout.isatty()) if PRINT_COLOR and colorama: colorama.init() class c: header = "\033[95m" blue = "\033[94m" green = "\033[92m" yellow = "\033[93m" red = "\033[91m" bold = "\033[1m" cyan = "\033[96m" underline = "\033[4m" end = "\033[0m" def mkcolorfunc(style: str) -> Callable[[str], str]: def stylize_text(x: str) -> str: if PRINT_COLOR: return f"{style}{x}{c.end}" else: return x return stylize_text bold = mkcolorfunc(c.bold) red = mkcolorfunc(c.red) blue = mkcolorfunc(c.cyan) cyan = mkcolorfunc(c.blue) green = mkcolorfunc(c.green) pipx-1.15.0/src/pipx/commands/000077500000000000000000000000001521706623000161305ustar00rootroot00000000000000pipx-1.15.0/src/pipx/commands/__init__.py000066400000000000000000000021301521706623000202350ustar00rootroot00000000000000from pipx.commands.ensure_path import ensure_pipx_paths from pipx.commands.environment import environment from pipx.commands.inject import inject from pipx.commands.install import install, install_all from pipx.commands.interpreter import list_interpreters, prune_interpreters, upgrade_interpreters from pipx.commands.list_packages import list_packages from pipx.commands.pin import pin, unpin from pipx.commands.reinstall import reinstall, reinstall_all from pipx.commands.run import run from pipx.commands.run_pip import run_pip from pipx.commands.uninject import uninject from pipx.commands.uninstall import uninstall, uninstall_all from pipx.commands.upgrade import upgrade, upgrade_all, upgrade_shared __all__ = [ "ensure_pipx_paths", "environment", "inject", "install", "install_all", "list_interpreters", "list_packages", "pin", "prune_interpreters", "reinstall", "reinstall_all", "run", "run_pip", "uninject", "uninstall", "uninstall_all", "unpin", "upgrade", "upgrade_all", "upgrade_interpreters", "upgrade_shared", ] pipx-1.15.0/src/pipx/commands/common.py000066400000000000000000000437411521706623000200030ustar00rootroot00000000000000import filecmp import logging import os import shlex import shutil import sys import tempfile import time from pathlib import Path from shutil import which from tempfile import TemporaryDirectory from typing import Final import userpath # type: ignore[import-not-found] from packaging.utils import canonicalize_name from pipx import paths from pipx.colors import bold, red from pipx.constants import MAN_SECTIONS, WINDOWS from pipx.emojis import hazard, stars from pipx.package_specifier import parse_specifier_for_install, valid_pypi_name from pipx.pipx_metadata_file import PackageInfo from pipx.util import PipxError, mkdir, pipx_wrap, rmdir, safe_unlink from pipx.venv import Venv _LOGGER: Final[logging.Logger] = logging.getLogger(__name__) class VenvProblems: def __init__( self, bad_venv_name: bool = False, invalid_interpreter: bool = False, missing_metadata: bool = False, not_installed: bool = False, ) -> None: self.bad_venv_name = bad_venv_name self.invalid_interpreter = invalid_interpreter self.missing_metadata = missing_metadata self.not_installed = not_installed def any_(self) -> bool: return any(self.__dict__.values()) def or_(self, venv_problems: "VenvProblems") -> None: for attribute in self.__dict__: setattr( self, attribute, getattr(self, attribute) or getattr(venv_problems, attribute), ) def expose_resources_globally( resource_type: str, local_resource_dir: Path, paths: list[Path], *, force: bool, suffix: str = "", ) -> None: for path in paths: if resource_type == "app": _add_ignore_environment_to_python_shebang(path) src = path.resolve() if resource_type == "man": dest_dir = local_resource_dir / src.parent.name else: dest_dir = local_resource_dir if not dest_dir.is_dir(): mkdir(dest_dir) if not can_symlink(dest_dir): _copy_package_resource(dest_dir, path, suffix=suffix) else: _symlink_package_resource( dest_dir, path, force=force, suffix=suffix, executable=(resource_type == "app"), ) _can_symlink_cache: dict[Path, bool] = {} def can_symlink(local_resource_dir: Path) -> bool: if not WINDOWS: # Technically, even on Unix this depends on the filesystem return True if local_resource_dir not in _can_symlink_cache: with TemporaryDirectory(dir=local_resource_dir) as d: p = Path(d) target = p / "a" target.touch() lnk = p / "b" try: lnk.symlink_to(target) _can_symlink_cache[local_resource_dir] = True except (OSError, NotImplementedError): _can_symlink_cache[local_resource_dir] = False return _can_symlink_cache[local_resource_dir] def _add_ignore_environment_to_python_shebang(path: Path) -> None: if WINDOWS or not path.is_file(): return try: data = path.read_bytes() except OSError: return first_line, separator, rest = data.partition(b"\n") if not first_line.startswith(b"#!"): return interpreter = first_line[2:] if interpreter.endswith(b" -E"): return if b"python" not in interpreter.lower() or b" " in interpreter or b"\t" in interpreter: return path.write_bytes(first_line + b" -E" + separator + rest) def _copy_package_resource(dest_dir: Path, path: Path, suffix: str = "") -> None: src = path.resolve() name = src.name dest = Path(dest_dir / add_suffix(name, suffix)) if not dest.parent.is_dir(): mkdir(dest.parent) if dest.exists(): if filecmp.cmp(dest, src, shallow=False): return _LOGGER.warning(f"{hazard} Overwriting file {dest!s} with {src!s}") safe_unlink(dest) if src.exists(): shutil.copy(src, dest) def _symlink_package_resource( dest_dir: Path, path: Path, *, force: bool, suffix: str = "", executable: bool = False, ) -> None: name_suffixed = add_suffix(path.name, suffix) symlink_path = Path(dest_dir / name_suffixed) if not symlink_path.parent.is_dir(): mkdir(symlink_path.parent) if force: _LOGGER.info(f"Force is true. Removing {symlink_path!s}.") try: symlink_path.unlink() except (FileNotFoundError, RuntimeError): pass except IsADirectoryError: rmdir(symlink_path) exists = symlink_path.exists() is_symlink = symlink_path.is_symlink() if exists: if symlink_path.samefile(path): _LOGGER.info(f"Same path {symlink_path!s} and {path!s}") else: _LOGGER.warning( pipx_wrap( f""" {hazard} File exists at {symlink_path!s} and points to {symlink_path.resolve()}, not {path!s}. Not modifying. """, subsequent_indent=" " * 4, ) ) return if is_symlink and not exists: _LOGGER.info(f"Removing existing symlink {symlink_path!s} since it pointed non-existent location") symlink_path.unlink() if executable: existing_executable_on_path = which(name_suffixed) else: existing_executable_on_path = None symlink_path.symlink_to(path) if executable and existing_executable_on_path: _LOGGER.warning( pipx_wrap( f""" {hazard} Note: {name_suffixed} was already on your PATH at {existing_executable_on_path} """, subsequent_indent=" " * 4, ) ) def venv_health_check(venv: Venv, package_name: str | None = None) -> tuple[VenvProblems, str]: venv_dir = venv.root python_path = venv.python_path.resolve() if package_name is None: package_name = venv.main_package_name if not python_path.is_file(): return ( VenvProblems(invalid_interpreter=True), f" package {red(bold(venv_dir.name))} has invalid interpreter {python_path!s}\r{hazard}", ) if not venv.package_metadata: return ( VenvProblems(missing_metadata=True), f" package {red(bold(venv_dir.name))} has missing internal pipx metadata.\r{hazard}", ) if venv_dir.name != canonicalize_name(venv_dir.name): return ( VenvProblems(bad_venv_name=True), f" package {red(bold(venv_dir.name))} needs its internal data updated.\r{hazard}", ) if venv.package_metadata[package_name].package_version == "": return ( VenvProblems(not_installed=True), f" package {red(bold(package_name))} {red('is not installed')} in the venv {venv_dir.name}\r{hazard}", ) return (VenvProblems(), "") def get_venv_summary( venv_dir: Path, *, package_name: str | None = None, new_install: bool = False, include_injected: bool = False, ) -> tuple[str, VenvProblems]: venv = Venv(venv_dir) if package_name is None: package_name = venv.main_package_name (venv_problems, warning_message) = venv_health_check(venv, package_name) if venv_problems.any_(): return (warning_message, venv_problems) package_metadata = venv.package_metadata[package_name] apps = package_metadata.apps man_pages = package_metadata.man_pages if package_metadata.include_dependencies: apps += package_metadata.apps_of_dependencies man_pages += package_metadata.man_pages_of_dependencies exposed_app_paths = get_exposed_paths_for_package( venv.bin_path, paths.ctx.bin_dir, [add_suffix(app, package_metadata.suffix) for app in apps], ) exposed_binary_names = sorted(p.name for p in exposed_app_paths) unavailable_binary_names = sorted( {add_suffix(name, package_metadata.suffix) for name in package_metadata.apps} - set(exposed_binary_names) ) exposed_man_paths = set() for man_section in MAN_SECTIONS: exposed_man_paths |= get_exposed_man_paths_for_package( venv.man_path / man_section, paths.ctx.man_dir / man_section, man_pages, ) exposed_man_pages = sorted(str(Path(p.parent.name) / p.name) for p in exposed_man_paths) unavailable_man_pages = sorted(set(package_metadata.man_pages) - set(exposed_man_pages)) # The following is to satisfy mypy that python_version is str and not # Optional[str] python_version = venv.pipx_metadata.python_version if venv.pipx_metadata.python_version is not None else "" source_interpreter = venv.pipx_metadata.source_interpreter is_standalone = ( str(source_interpreter).startswith(str(paths.ctx.standalone_python_cachedir.resolve())) if source_interpreter else False ) return ( _get_list_output( python_version, is_standalone, package_metadata.package_version, package_name, new_install, exposed_binary_names, unavailable_binary_names, exposed_man_pages, unavailable_man_pages, venv.pipx_metadata.injected_packages if include_injected else None, suffix=package_metadata.suffix, ), venv_problems, ) def get_exposed_paths_for_package( venv_resource_path: Path, local_resource_dir: Path, package_resource_names: list[str] | None = None, ) -> set[Path]: # package_binary_names is used only if local_bin_path cannot use symlinks. # It is necessary for non-symlink systems to return valid app_paths. if package_resource_names is None: package_resource_names = [] if not local_resource_dir.exists(): return set() symlinks = set() for b in local_resource_dir.iterdir(): try: # sometimes symlinks can resolve to a file of a different name # (in the case of ansible for example) so checking the resolved paths # is not a reliable way to determine if the symlink exists. # We always use the stricter check on non-Windows systems. On # Windows, we use a less strict check if we don't have a symlink. is_same_file = False if can_symlink(local_resource_dir) and b.is_symlink(): is_same_file = b.resolve().parent.samefile(venv_resource_path) elif not can_symlink(local_resource_dir): is_same_file = b.name in package_resource_names if is_same_file: symlinks.add(b) except (FileNotFoundError, RuntimeError): pass return symlinks def get_exposed_man_paths_for_package( venv_man_path: Path, local_man_dir: Path, package_man_pages: list[str] | None = None, ) -> set[Path]: man_section = venv_man_path.name prefix = man_section + os.sep return get_exposed_paths_for_package( venv_man_path, local_man_dir, [name.removeprefix(prefix) for name in package_man_pages or [] if name.startswith(prefix)], ) def _get_list_output( python_version: str, python_is_standalone: bool, package_version: str, package_name: str, new_install: bool, exposed_binary_names: list[str], unavailable_binary_names: list[str], exposed_man_pages: list[str], unavailable_man_pages: list[str], injected_packages: dict[str, PackageInfo] | None = None, suffix: str = "", ) -> str: output = [] suffix = f" ({bold(shlex.quote(package_name + suffix))})" if suffix else "" output.append( f" {'installed' if new_install else ''} package {bold(shlex.quote(package_name))}" f" {bold(package_version)}{suffix}, installed using {python_version}" + (" (standalone)" if python_is_standalone else "") ) if new_install and (exposed_binary_names or unavailable_binary_names): output.append(" These apps are now available") output.extend(f" - {name}" for name in exposed_binary_names) output.extend( f" - {red(name)} (symlink missing or pointing to unexpected location)" for name in unavailable_binary_names ) if new_install and (exposed_man_pages or unavailable_man_pages): output.append(" These manual pages are now available") output.extend(f" - {name}" for name in exposed_man_pages) output.extend( f" - {red(name)} (symlink missing or pointing to unexpected location)" for name in unavailable_man_pages ) if injected_packages: output.append(" Injected Packages:") output.extend(f" - {name} {injected_packages[name].package_version}" for name in injected_packages) return "\n".join(output) def package_name_from_spec( package_spec: str, python: str, *, pip_args: list[str], verbose: bool, backend: str | None = None, env_backend: str | None = None, ) -> str: start_time = time.time() # shortcut if valid PyPI name pypi_name = valid_pypi_name(package_spec) if pypi_name is not None: # NOTE: if pypi name and installed package name differ, this means pipx # will use the pypi name package_name = pypi_name _LOGGER.info(f"Determined package name: {package_name}") _LOGGER.info(f"Package name determined in {time.time() - start_time:.1f}s") return package_name # check syntax and clean up spec and pip_args (package_spec, pip_args) = parse_specifier_for_install(package_spec, pip_args) with tempfile.TemporaryDirectory() as temp_venv_dir: venv = Venv(Path(temp_venv_dir), python=python, verbose=verbose, backend=backend, env_backend=env_backend) venv.create_venv(venv_args=[], pip_args=[]) package_name = venv.install_package_no_deps(package_or_url=package_spec, pip_args=pip_args) _LOGGER.info(f"Package name determined in {time.time() - start_time:.1f}s") return package_name def run_post_install_actions( venv: Venv, package_name: str, local_bin_dir: Path, local_man_dir: Path, venv_dir: Path, include_dependencies: bool, *, force: bool, ) -> None: package_metadata = venv.package_metadata[package_name] display_name = f"{package_name}{package_metadata.suffix}" if ( not venv.main_package_name == package_name and venv.package_metadata[venv.main_package_name].suffix == package_metadata.suffix ): package_name = display_name if not package_metadata.apps: if not package_metadata.apps_of_dependencies: if venv.safe_to_remove(): venv.remove_venv() raise PipxError( f""" No apps associated with package {display_name} or its dependencies. If you are attempting to install a library, pipx should not be used. Consider using pip or a similar tool instead. """ ) if package_metadata.apps_of_dependencies and not include_dependencies: for ( dep, dependent_apps, ) in package_metadata.app_paths_of_dependencies.items(): print(f"Note: Dependent package '{dep}' contains {len(dependent_apps)} apps") for app in dependent_apps: print(f" - {app.name}") if venv.safe_to_remove(): venv.remove_venv() raise PipxError( f""" No apps associated with package {display_name}. Try again with '--include-deps' to include apps of dependent packages, which are listed above. If you are attempting to install a library, pipx should not be used. Consider using pip or a similar tool instead. """ ) expose_resources_globally( "app", local_bin_dir, package_metadata.app_paths, force=force, suffix=package_metadata.suffix, ) expose_resources_globally("man", local_man_dir, package_metadata.man_paths, force=force) if include_dependencies: for app_paths in package_metadata.app_paths_of_dependencies.values(): expose_resources_globally( "app", local_bin_dir, app_paths, force=force, suffix=package_metadata.suffix, ) for man_paths in package_metadata.man_paths_of_dependencies.values(): expose_resources_globally("man", local_man_dir, man_paths, force=force) package_summary, _ = get_venv_summary(venv_dir, package_name=package_name, new_install=True) pipx_logger = logging.getLogger("pipx") if not pipx_logger.handlers or pipx_logger.handlers[0].level <= logging.WARNING: print(package_summary) warn_if_not_on_path(local_bin_dir) print(f"done! {stars}", file=sys.stderr) def warn_if_not_on_path(local_bin_dir: Path) -> None: if not userpath.in_current_path(str(local_bin_dir)): _LOGGER.warning( pipx_wrap( f""" {hazard} Note: '{local_bin_dir}' is not on your PATH environment variable. These apps will not be globally accessible until your PATH is updated. Run `pipx ensurepath` to automatically add it, or manually modify your PATH in your shell's config file (e.g. ~/.bashrc). """, subsequent_indent=" " * 4, ) ) def add_suffix(name: str, suffix: str) -> str: """Add suffix to app.""" app = Path(name) return f"{app.stem}{suffix}{app.suffix}" __all__ = [ "VenvProblems", "add_suffix", "can_symlink", "expose_resources_globally", "get_exposed_man_paths_for_package", "get_exposed_paths_for_package", "get_venv_summary", "package_name_from_spec", "run_post_install_actions", "venv_health_check", ] pipx-1.15.0/src/pipx/commands/ensure_path.py000066400000000000000000000135661521706623000210320ustar00rootroot00000000000000import logging import site import sys from pathlib import Path import userpath # type: ignore[import-not-found] from pipx import paths from pipx.constants import EXIT_CODE_OK, ExitCode from pipx.emojis import hazard, stars from pipx.util import pipx_wrap logger = logging.getLogger(__name__) def get_pipx_user_bin_path() -> Path | None: """Returns None if pipx is not installed using `pip --user` Otherwise returns parent dir of pipx binary """ # NOTE: using this method to detect pip user-installed pipx will return # None if pipx was installed as editable using `pip install --user -e` # https://docs.python.org/3/install/index.html#inst-alt-install-user # Linux + Mac: # scripts in /bin # Windows: # scripts in /Python/Scripts # modules in /Python/site-packages pipx_bin_path = None script_path = Path(__file__).resolve() userbase_path = Path(site.getuserbase()).resolve() try: _ = script_path.relative_to(userbase_path) except ValueError: pip_user_installed = False else: pip_user_installed = True if pip_user_installed: test_paths = ( userbase_path / "bin" / "pipx", Path(site.getusersitepackages()).resolve().parent / "Scripts" / "pipx.exe", ) for test_path in test_paths: if test_path.exists(): pipx_bin_path = test_path.parent break return pipx_bin_path def ensure_path( location: Path, *, force: bool, prepend: bool = False, all_shells: bool = False, dry_run: bool = False ) -> tuple[bool, bool]: """Ensure location is in user's PATH or add it to PATH. If prepend is True, location will be prepended to PATH, else appended. If dry_run is True, report what would change without modifying PATH or any shell configuration file. Returns True if location was added to PATH """ location_str = str(location) path_added = False need_shell_restart = userpath.need_shell_restart(location_str) in_current_path = userpath.in_current_path(location_str) if force or (not in_current_path and not need_shell_restart): if dry_run: action = "prepend" if prepend else "append" print( pipx_wrap( f"Would {action} {location_str} to the PATH environment variable.", subsequent_indent=" " * 4, ) ) return (False, False) if prepend: path_added = userpath.prepend(location_str, "pipx", all_shells=all_shells) else: path_added = userpath.append(location_str, "pipx", all_shells=all_shells) if not path_added: print( pipx_wrap( f"{hazard} {location_str} is not added to the PATH environment variable successfully. " f"You may need to add it to PATH manually.", subsequent_indent=" " * 4, ) ) else: print( pipx_wrap( f"Success! Added {location_str} to the PATH environment variable.", subsequent_indent=" " * 4, ) ) need_shell_restart = userpath.need_shell_restart(location_str) elif not in_current_path and need_shell_restart: print( pipx_wrap( f""" {location_str} has been added to PATH, but you need to open a new terminal or re-login for this PATH change to take effect. Alternatively, you can source your shell's config file with e.g. 'source ~/.bashrc'. """, subsequent_indent=" " * 4, ) ) else: print(pipx_wrap(f"{location_str} is already in PATH.", subsequent_indent=" " * 4)) return (path_added, need_shell_restart) def ensure_pipx_paths(force: bool, prepend: bool = False, all_shells: bool = False, dry_run: bool = False) -> ExitCode: """Returns pipx exit code.""" bin_paths = {paths.ctx.bin_dir} pipx_user_bin_path = get_pipx_user_bin_path() if pipx_user_bin_path is not None: bin_paths.add(pipx_user_bin_path) path_added = False need_shell_restart = False path_action_str = "prepended to" if prepend else "appended to" for bin_path in bin_paths: (path_added_current, need_shell_restart_current) = ensure_path( bin_path, prepend=prepend, force=force, all_shells=all_shells, dry_run=dry_run ) path_added |= path_added_current need_shell_restart |= need_shell_restart_current print() if dry_run: print(pipx_wrap("This was a dry run; no changes were made to your PATH or shell configuration files.")) return EXIT_CODE_OK if path_added: print( pipx_wrap( """ Consider adding shell completions for pipx. Run 'pipx completions' for instructions. """ ) + "\n" ) elif not need_shell_restart: sys.stdout.flush() logger.warning( pipx_wrap( f""" {hazard} All pipx binary directories have been {path_action_str} PATH. If you are sure you want to proceed, try again with the '--force' flag. """ ) + "\n" ) if need_shell_restart: print( pipx_wrap( """ You will need to open a new terminal or re-login for the PATH changes to take effect. Alternatively, you can source your shell's config file with e.g. 'source ~/.bashrc'. """ ) + "\n" ) print(f"Otherwise pipx is ready to go! {stars}") return EXIT_CODE_OK pipx-1.15.0/src/pipx/commands/environment.py000066400000000000000000000056421521706623000210550ustar00rootroot00000000000000import os from pipx import paths from pipx.backends import env_default_backend, find_uv_binary, resolve_backend_name from pipx.constants import EXIT_CODE_OK, ExitCode from pipx.emojis import EMOJI_SUPPORT from pipx.interpreter import DEFAULT_PYTHON from pipx.shared_libs import ( DISABLE_SHARED_LIBS_AUTO_UPGRADE, shared_libs_auto_upgrade_disabled, ) from pipx.util import PipxError ENVIRONMENT_VARIABLES = [ "PIPX_HOME", "PIPX_GLOBAL_HOME", "PIPX_BIN_DIR", "PIPX_GLOBAL_BIN_DIR", "PIPX_MAN_DIR", "PIPX_GLOBAL_MAN_DIR", "PIPX_SHARED_LIBS", "PIPX_DEFAULT_PYTHON", "PIPX_DEFAULT_BACKEND", "PIPX_FETCH_MISSING_PYTHON", "PIPX_FETCH_PYTHON", DISABLE_SHARED_LIBS_AUTO_UPGRADE, "PIPX_USE_EMOJI", "PIPX_HOME_ALLOW_SPACE", ] DERIVED_ENVIRONMENT_VARIABLES = [ "PIPX_LOCAL_VENVS", "PIPX_LOG_DIR", "PIPX_TRASH_DIR", "PIPX_VENV_CACHEDIR", "PIPX_STANDALONE_PYTHON_CACHEDIR", "PIPX_RESOLVED_BACKEND", "PIPX_BACKEND_SOURCE", "PIPX_UV_BINARY", "UV_CACHE_DIR", ] ENVIRONMENT_VALUE_CHOICES = ENVIRONMENT_VARIABLES + DERIVED_ENVIRONMENT_VARIABLES def environment(value: str) -> ExitCode: """Print a list of environment variables and paths used by pipx""" resolved_backend, backend_source = resolve_backend_name(env_value=env_default_backend()) uv_binary, _uv_source = find_uv_binary() derived_values = { "PIPX_HOME": paths.ctx.home, "PIPX_BIN_DIR": paths.ctx.bin_dir, "PIPX_MAN_DIR": paths.ctx.man_dir, "PIPX_SHARED_LIBS": paths.ctx.shared_libs, "PIPX_LOCAL_VENVS": paths.ctx.venvs, "PIPX_LOG_DIR": paths.ctx.logs, "PIPX_TRASH_DIR": paths.ctx.trash, "PIPX_VENV_CACHEDIR": paths.ctx.venv_cache, "PIPX_STANDALONE_PYTHON_CACHEDIR": paths.ctx.standalone_python_cachedir, "PIPX_DEFAULT_PYTHON": DEFAULT_PYTHON, "PIPX_RESOLVED_BACKEND": resolved_backend, "PIPX_BACKEND_SOURCE": backend_source, "PIPX_UV_BINARY": str(uv_binary) if uv_binary else "", "UV_CACHE_DIR": os.environ.get("UV_CACHE_DIR", ""), DISABLE_SHARED_LIBS_AUTO_UPGRADE: str(shared_libs_auto_upgrade_disabled()).lower(), "PIPX_USE_EMOJI": str(EMOJI_SUPPORT).lower(), "PIPX_HOME_ALLOW_SPACE": str(paths.ctx.allow_spaces_in_home_path).lower(), } if value is None: print("Environment variables (set by user):") # type: ignore[unreachable] print("") for env_variable in ENVIRONMENT_VARIABLES: env_value = os.getenv(env_variable, "") print(f"{env_variable}={env_value}") print("") print("Derived values (computed by pipx):") print("") for env_variable, derived_value in derived_values.items(): print(f"{env_variable}={derived_value}") elif value in derived_values: print(derived_values[value]) else: raise PipxError("Variable not found.") return EXIT_CODE_OK pipx-1.15.0/src/pipx/commands/inject.py000066400000000000000000000136771521706623000177740ustar00rootroot00000000000000import logging import os import re import sys from collections.abc import Generator, Iterable from pathlib import Path from typing import Final from packaging.utils import canonicalize_name from pipx import paths from pipx.backends import assert_not_pip_under_uv from pipx.colors import bold from pipx.commands.common import package_name_from_spec, run_post_install_actions from pipx.constants import EXIT_CODE_INJECT_ERROR, EXIT_CODE_OK, ExitCode from pipx.emojis import hazard, stars from pipx.util import PipxError, pipx_wrap from pipx.venv import Venv _LOGGER: Final[logging.Logger] = logging.getLogger(__name__) _COMMENT_RE: Final[re.Pattern[str]] = re.compile(r"(^|\s+)#.*$") def inject_dep( venv_dir: Path, package_name: str | None, package_spec: str, pip_args: list[str], *, verbose: bool, include_apps: bool, include_dependencies: bool, force: bool, suffix: bool = False, backend: str | None = None, env_backend: str | None = None, ) -> bool: _LOGGER.debug("Injecting package %s", package_spec) if not venv_dir.exists() or next(venv_dir.iterdir(), None) is None: raise PipxError( f""" Can't inject {package_spec!r} into nonexistent Virtual Environment {venv_dir.name!r}. Be sure to install the package first with 'pipx install {venv_dir.name}' before injecting into it. """ ) venv = Venv(venv_dir, verbose=verbose, backend=backend, env_backend=env_backend) venv.check_upgrade_shared_libs(pip_args=pip_args, verbose=verbose) if not venv.package_metadata: raise PipxError( f""" Can't inject {package_spec!r} into Virtual Environment {venv.name!r}. {venv.name!r} has missing internal pipx metadata. It was likely installed using a pipx version before 0.15.0.0. Please uninstall and install {venv.name!r}, or reinstall-all to fix. """ ) # package_spec is anything pip-installable, including package_name, vcs spec, # zip file, or tar.gz file. if package_name is None: package_name = package_name_from_spec( package_spec, os.fspath(venv.python_path), pip_args=pip_args, verbose=verbose, backend=venv.backend_name, env_backend=env_backend, ) # Mirrors the install-side guard: dropping pip into a uv venv works for # ``pipx run`` but breaks anyone reaching for the venv's missing pip. assert_not_pip_under_uv(canonicalize_name(package_name), venv.backend_name) if not force and venv.has_package(package_name): _LOGGER.info("Package %s has already been injected", package_name) print( pipx_wrap( f""" {hazard} {package_name} already seems to be injected in {venv.name!r}. Not modifying existing installation in '{venv_dir}'. Pass '--force' to force installation. """ ) ) return True if suffix: venv_suffix = venv.package_metadata[venv.main_package_name].suffix else: venv_suffix = "" venv.install_package( package_name=package_name, package_or_url=package_spec, pip_args=pip_args, install_only_pip_args=["--force-reinstall"] if force else None, include_dependencies=include_dependencies, include_apps=include_apps, is_main_package=False, suffix=venv_suffix, ) if include_apps: run_post_install_actions( venv, package_name, paths.ctx.bin_dir, paths.ctx.man_dir, venv_dir, include_dependencies, force=force, ) print(f" injected package {bold(package_name)} into venv {bold(venv.name)}") print(f"done! {stars}", file=sys.stderr) # Any failure to install will raise PipxError, otherwise success return True def inject( venv_dir: Path, package_specs: Iterable[str], requirement_files: Iterable[str], pip_args: list[str], *, verbose: bool, include_apps: bool, include_dependencies: bool, force: bool, suffix: bool = False, backend: str | None = None, env_backend: str | None = None, ) -> ExitCode: """Returns pipx exit code.""" # Combined collection of package specifications packages = list(package_specs) for filename in requirement_files: packages.extend(parse_requirements(filename)) # Remove duplicates and order deterministically packages = sorted(set(packages)) if not packages: raise PipxError("No packages have been specified.") _LOGGER.info("Injecting packages: %r", packages) # Inject packages if not include_apps and include_dependencies: include_apps = True all_success = True for dependency in packages: all_success &= inject_dep( venv_dir, package_name=None, package_spec=dependency, pip_args=pip_args, verbose=verbose, include_apps=include_apps, include_dependencies=include_dependencies, force=force, suffix=suffix, backend=backend, env_backend=env_backend, ) # Any failure to install will raise PipxError, otherwise success return EXIT_CODE_OK if all_success else EXIT_CODE_INJECT_ERROR def parse_requirements(filename: str | os.PathLike) -> Generator[str, None, None]: """ Extract package specifications from requirements file. Return all of the non-empty lines with comments removed. """ # Based on https://github.com/pypa/pip/blob/main/src/pip/_internal/req/req_file.py with open(filename) as f: for line in f: # Strip comments and filter empty lines if pkgspec := _COMMENT_RE.sub("", line).strip(): yield pkgspec __all__ = [ "inject", "inject_dep", "parse_requirements", ] pipx-1.15.0/src/pipx/commands/install.py000066400000000000000000000232101521706623000201460ustar00rootroot00000000000000import json import sys from collections.abc import Iterator from pathlib import Path from packaging.utils import canonicalize_name from pipx import commands, paths from pipx.backends import PIP from pipx.commands.common import package_name_from_spec, run_post_install_actions from pipx.constants import ( EXIT_CODE_INSTALL_VENV_EXISTS, EXIT_CODE_OK, ExitCode, ) from pipx.emojis import sleep from pipx.interpreter import DEFAULT_PYTHON from pipx.pipx_metadata_file import PackageInfo, PipxMetadata, load_spec_file from pipx.util import PipxError, pipx_wrap from pipx.venv import Venv, VenvContainer def install( venv_dir: Path | None, package_names: list[str] | None, package_specs: list[str], local_bin_dir: Path, local_man_dir: Path, python: str | None, pip_args: list[str], venv_args: list[str], verbose: bool, *, force: bool, reinstall: bool, include_dependencies: bool, preinstall_packages: list[str] | None, suffix: str = "", python_flag_passed: bool = False, backend: str | None = None, env_backend: str | None = None, ) -> ExitCode: """Returns pipx exit code.""" # package_spec is anything pip-installable, including package_name, vcs spec, # zip file, or tar.gz file. python = python or DEFAULT_PYTHON package_names = package_names or [] if len(package_names) != len(package_specs): package_names = [ package_name_from_spec( package_spec, python, pip_args=pip_args, verbose=verbose, backend=backend, env_backend=env_backend, ) for package_spec in package_specs ] for package_name, package_spec in zip(package_names, package_specs, strict=False): if venv_dir is None: venv_container = VenvContainer(paths.ctx.venvs) venv_dir = venv_container.get_venv_dir(f"{package_name}{suffix}") try: exists = venv_dir.exists() and bool(next(venv_dir.iterdir())) except StopIteration: exists = False # ``pipx install pip`` always uses pip (uv venvs ship no pip). Override # only the implicit env path; ``--backend uv`` still falls through to # ``assert_not_pip_under_uv`` so an explicit conflict fails loudly. install_backend, install_env_backend = backend, env_backend if canonicalize_name(package_name) == "pip": install_backend = backend or PIP install_env_backend = None venv = Venv( venv_dir, python=python, verbose=verbose, backend=install_backend, env_backend=install_env_backend, ) venv.check_upgrade_shared_libs(pip_args=pip_args, verbose=verbose) if exists: if not reinstall and force and python_flag_passed: print( pipx_wrap( f""" --python is ignored when --force is passed. If you want to reinstall {package_name} with {python}, run `pipx reinstall {package_spec} --python {python}` instead. """ ) ) if force: print(f"Installing to existing venv {venv.name!r}") pip_args = ["--force-reinstall"] + pip_args else: installed_version = venv.pipx_metadata.main_package.package_version version_info = f" ({installed_version})" if installed_version else "" print( pipx_wrap( f""" {venv.name!r}{version_info} already seems to be installed. Not modifying existing installation in '{venv_dir}'. Pass '--force' to force installation, or use 'pipx upgrade {venv.name}' to upgrade. """ ) ) if len(package_specs) == 1: return EXIT_CODE_INSTALL_VENV_EXISTS # Reset venv_dir to None ready to install the next package in the list venv_dir = None continue try: override_shared = canonicalize_name(package_name) == "pip" venv.create_venv(venv_args, pip_args, override_shared) for dependency in preinstall_packages or []: venv.upgrade_package_no_metadata(dependency, []) venv.install_package( package_name=package_name, package_or_url=package_spec, pip_args=pip_args, include_dependencies=include_dependencies, include_apps=True, is_main_package=True, suffix=suffix, ) run_post_install_actions( venv, package_name, local_bin_dir, local_man_dir, venv_dir, include_dependencies, force=force, ) except (Exception, KeyboardInterrupt): print() venv.remove_venv() raise # Reset venv_dir to None ready to install the next package in the list venv_dir = None # Any failure to install will raise PipxError, otherwise success return EXIT_CODE_OK def extract_venv_metadata(spec_metadata_file: Path) -> Iterator[PipxMetadata]: try: spec = load_spec_file(spec_metadata_file) except json.decoder.JSONDecodeError as exc: raise PipxError("The spec metadata file is an invalid JSON file.") from exc venvs_metadata_dict = spec.get("venvs") if not venvs_metadata_dict: raise PipxError("No packages found in the spec metadata file.") if not isinstance(venvs_metadata_dict, dict): raise PipxError("The spec metadata file is invalid.") for package_path_name, entry in venvs_metadata_dict.items(): venv_dir = paths.ctx.venvs.joinpath(package_path_name) venv_metadata = PipxMetadata(venv_dir, read=False) venv_metadata.from_dict(entry["metadata"]) yield venv_metadata def generate_package_spec(package_info: PackageInfo) -> str: """Generate more precise package spec from package info.""" if not package_info.package_or_url: raise PipxError(f"A package spec is not available for {package_info.package}") if package_info.package == package_info.package_or_url: return f"{package_info.package}=={package_info.package_version}" return package_info.package_or_url def get_python_interpreter( source_interpreter: Path | None, ) -> str | None: """Get appropriate python interpreter.""" if source_interpreter is not None and source_interpreter.is_file(): return str(source_interpreter) print( pipx_wrap( f""" The exported python interpreter '{source_interpreter}' is ignored as not found. """ ) ) return None def install_all( spec_metadata_file: Path, local_bin_dir: Path, local_man_dir: Path, python: str | None, pip_args: list[str], venv_args: list[str], verbose: bool, *, force: bool, backend: str | None = None, env_backend: str | None = None, ) -> ExitCode: """Return pipx exit code.""" venv_container = VenvContainer(paths.ctx.venvs) failed: list[str] = [] installed: list[str] = [] for venv_metadata in extract_venv_metadata(spec_metadata_file): # Install the main package main_package = venv_metadata.main_package venv_dir = venv_container.get_venv_dir(f"{main_package.package}{main_package.suffix}") try: install( venv_dir, None, [generate_package_spec(main_package)], local_bin_dir, local_man_dir, python or get_python_interpreter(venv_metadata.source_interpreter), pip_args, venv_args, verbose, force=force, reinstall=False, include_dependencies=main_package.include_dependencies, preinstall_packages=[], suffix=main_package.suffix, backend=backend or venv_metadata.backend, env_backend=env_backend, ) # Install the injected packages for inject_package in venv_metadata.injected_packages.values(): commands.inject( venv_dir=venv_dir, package_specs=[generate_package_spec(inject_package)], requirement_files=[], pip_args=pip_args, verbose=verbose, include_apps=inject_package.include_apps, include_dependencies=inject_package.include_dependencies, force=force, suffix=inject_package.suffix == main_package.suffix, ) except PipxError as e: print(e, file=sys.stderr) failed.append(venv_dir.name) else: installed.append(venv_dir.name) if len(installed) == 0: print(f"No packages installed after running 'pipx install-all {spec_metadata_file}' {sleep}") if len(failed) > 0: raise PipxError(f"The following package(s) failed to install: {', '.join(failed)}") # Any failure to install will raise PipxError, otherwise success return EXIT_CODE_OK __all__ = [ "extract_venv_metadata", "generate_package_spec", "get_python_interpreter", "install", "install_all", ] pipx-1.15.0/src/pipx/commands/interpreter.py000066400000000000000000000132611521706623000210500ustar00rootroot00000000000000import logging import subprocess from pathlib import Path from packaging import version from pipx import commands, constants, paths, standalone_python from pipx.animate import animate from pipx.pipx_metadata_file import PipxMetadata from pipx.util import is_paths_relative, rmdir from pipx.venv import Venv, VenvContainer logger = logging.getLogger(__name__) def get_installed_standalone_interpreters() -> list[Path]: return [python_dir for python_dir in paths.ctx.standalone_python_cachedir.iterdir() if python_dir.is_dir()] def get_venvs_using_standalone_interpreter(venv_container: VenvContainer) -> list[Venv]: venvs: list[Venv] = [] for venv_dir in venv_container.iter_venv_dirs(): venv = Venv(venv_dir) if venv.pipx_metadata.source_interpreter: venvs.append(venv) return venvs def get_interpreter_users(interpreter: Path, venvs: list[Venv]) -> list[PipxMetadata]: return [ venv.pipx_metadata for venv in venvs if venv.pipx_metadata.source_interpreter and is_paths_relative(venv.pipx_metadata.source_interpreter, interpreter) ] def list_interpreters( venv_container: VenvContainer, ): interpreters = get_installed_standalone_interpreters() venvs = get_venvs_using_standalone_interpreter(venv_container) output: list[str] = [] output.append(f"Standalone interpreters are in {paths.ctx.standalone_python_cachedir}") for interpreter in interpreters: output.append(f"Python {interpreter.name}") used_in = get_interpreter_users(interpreter, venvs) if used_in: output.append(" Used in:") output.extend(f" - {p.main_package.package} {p.main_package.package_version}" for p in used_in) else: output.append(" Unused") print("\n".join(output)) return constants.EXIT_CODE_OK def prune_interpreters( venv_container: VenvContainer, ): interpreters = get_installed_standalone_interpreters() venvs = get_venvs_using_standalone_interpreter(venv_container) removed = [] for interpreter in interpreters: if get_interpreter_users(interpreter, venvs): continue rmdir(interpreter, safe_rm=True) removed.append(interpreter.name) if removed: print("Successfully removed:") for interpreter_name in removed: print(f" - Python {interpreter_name}") else: print("Nothing to remove") return constants.EXIT_CODE_OK def get_latest_micro_version( current_version: version.Version, latest_python_versions: list[version.Version] ) -> version.Version: for latest_python_version in latest_python_versions: if current_version.major == latest_python_version.major and current_version.minor == latest_python_version.minor: return latest_python_version return current_version def upgrade_interpreters(venv_container: VenvContainer, verbose: bool): with animate("Getting the index of the latest standalone python builds", not verbose): latest_pythons = standalone_python.list_pythons(use_cache=False) parsed_latest_python_versions = [] for latest_python_version in latest_pythons: try: parsed_latest_python_versions.append(version.parse(latest_python_version)) except version.InvalidVersion: logger.info(f"Invalid version found in latest pythons: {latest_python_version}. Skipping.") upgraded = [] for interpreter_dir in paths.ctx.standalone_python_cachedir.iterdir(): if not interpreter_dir.is_dir(): continue interpreter_python = interpreter_dir / "python.exe" if constants.WINDOWS else interpreter_dir / "bin" / "python3" interpreter_full_version = ( subprocess.run([str(interpreter_python), "--version"], stdout=subprocess.PIPE, check=True, text=True) .stdout.strip() .split()[1] ) try: parsed_interpreter_full_version = version.parse(interpreter_full_version) except version.InvalidVersion: logger.info(f"Invalid version found in interpreter at {interpreter_dir}. Skipping.") continue latest_micro_version = get_latest_micro_version(parsed_interpreter_full_version, parsed_latest_python_versions) if latest_micro_version > parsed_interpreter_full_version: standalone_python.download_python_build_standalone( f"{latest_micro_version.major}.{latest_micro_version.minor}", override=True, ) for venv_dir in venv_container.iter_venv_dirs(): venv = Venv(venv_dir) if venv.pipx_metadata.source_interpreter is not None and is_paths_relative( venv.pipx_metadata.source_interpreter, interpreter_dir ): print( f"Upgrade the interpreter of {venv.name} from {interpreter_full_version} to {latest_micro_version}" ) commands.reinstall( venv_dir=venv_dir, local_bin_dir=paths.ctx.bin_dir, local_man_dir=paths.ctx.man_dir, python=str(interpreter_python), verbose=verbose, ) upgraded.append((venv.name, interpreter_full_version, latest_micro_version)) if upgraded: print("Successfully upgraded the interpreter(s):") for venv_name, old_version, new_version in upgraded: print(f" - {venv_name}: {old_version} -> {new_version}") else: print("Nothing to upgrade") # Any failure to upgrade will raise PipxError, otherwise success return constants.EXIT_CODE_OK pipx-1.15.0/src/pipx/commands/list_packages.py000066400000000000000000000131141521706623000213130ustar00rootroot00000000000000import json import logging import sys from collections.abc import Collection from pathlib import Path from typing import Any from pipx import paths from pipx.colors import bold from pipx.commands.common import VenvProblems, get_venv_summary, venv_health_check from pipx.constants import EXIT_CODE_LIST_PROBLEM, EXIT_CODE_OK, ExitCode from pipx.emojis import sleep from pipx.pipx_metadata_file import JsonEncoderHandlesPath, PipxMetadata from pipx.venv import Venv, VenvContainer logger = logging.getLogger(__name__) PIPX_SPEC_VERSION = "0.1" def get_venv_metadata_summary(venv_dir: Path) -> tuple[PipxMetadata, VenvProblems, str]: venv = Venv(venv_dir) (venv_problems, warning_message) = venv_health_check(venv) if venv_problems.any_(): return (PipxMetadata(venv_dir, read=False), venv_problems, warning_message) return (venv.pipx_metadata, venv_problems, "") def list_short(venv_dirs: Collection[Path]) -> VenvProblems: all_venv_problems = VenvProblems() for venv_dir in venv_dirs: venv_metadata, venv_problems, warning_str = get_venv_metadata_summary(venv_dir) if venv_problems.any_(): logger.warning(warning_str) else: print( venv_metadata.main_package.package, venv_metadata.main_package.package_version, ) all_venv_problems.or_(venv_problems) return all_venv_problems def list_text(venv_dirs: Collection[Path], include_injected: bool, venv_root_dir: str) -> VenvProblems: print(f"venvs are in {bold(venv_root_dir)}") print(f"apps are exposed on your $PATH at {bold(str(paths.ctx.bin_dir))}") print(f"manual pages are exposed at {bold(str(paths.ctx.man_dir))}") all_venv_problems = VenvProblems() for venv_dir in venv_dirs: package_summary, venv_problems = get_venv_summary(venv_dir, include_injected=include_injected) if venv_problems.any_(): logger.warning(package_summary) else: print(package_summary) all_venv_problems.or_(venv_problems) return all_venv_problems def list_json(venv_dirs: Collection[Path]) -> VenvProblems: warning_messages = [] spec_metadata: dict[str, Any] = { "pipx_spec_version": PIPX_SPEC_VERSION, "venvs": {}, } all_venv_problems = VenvProblems() for venv_dir in venv_dirs: (venv_metadata, venv_problems, warning_str) = get_venv_metadata_summary(venv_dir) all_venv_problems.or_(venv_problems) if venv_problems.any_(): warning_messages.append(warning_str) continue spec_metadata["venvs"][venv_dir.name] = {} spec_metadata["venvs"][venv_dir.name]["metadata"] = venv_metadata.to_dict() print(json.dumps(spec_metadata, indent=4, sort_keys=True, cls=JsonEncoderHandlesPath)) for warning_message in warning_messages: logger.warning(warning_message) return all_venv_problems def list_pinned(venv_dirs: Collection[Path], include_injected: bool) -> VenvProblems: all_venv_problems = VenvProblems() for venv_dir in venv_dirs: venv_metadata, venv_problems, warning_str = get_venv_metadata_summary(venv_dir) if venv_problems.any_(): logger.warning(warning_str) else: if venv_metadata.main_package.pinned: print( venv_metadata.main_package.package, venv_metadata.main_package.package_version, ) if include_injected: for pkg, info in venv_metadata.injected_packages.items(): if info.pinned: print(pkg, info.package_version, f"(injected in venv {venv_dir.name})") all_venv_problems.or_(venv_problems) return all_venv_problems def list_packages( venv_container: VenvContainer, include_injected: bool, json_format: bool, short_format: bool, pinned_only: bool, ) -> ExitCode: """Returns pipx exit code.""" venv_dirs: Collection[Path] = sorted(venv_container.iter_venv_dirs()) if not venv_dirs: print(f"nothing has been installed with pipx {sleep}", file=sys.stderr) if json_format: all_venv_problems = list_json(venv_dirs) elif short_format: all_venv_problems = list_short(venv_dirs) elif pinned_only: all_venv_problems = list_pinned(venv_dirs, include_injected) else: if not venv_dirs: return EXIT_CODE_OK all_venv_problems = list_text(venv_dirs, include_injected, str(venv_container)) if all_venv_problems.bad_venv_name: logger.warning( "\nOne or more packages contain out-of-date internal data installed from a\n" "previous pipx version and need to be updated.\n" " To fix, execute: pipx reinstall-all" ) if all_venv_problems.invalid_interpreter: logger.warning( "\nOne or more packages have a missing python interpreter.\n To fix, execute: pipx reinstall-all" ) if all_venv_problems.missing_metadata: logger.warning( "\nOne or more packages have a missing internal pipx metadata.\n" " They were likely installed using a pipx version before 0.15.0.0.\n" " Please uninstall and install these package(s) to fix." ) if all_venv_problems.not_installed: logger.warning( "\nOne or more packages are not installed properly.\n" " Please uninstall and install these package(s) to fix." ) if all_venv_problems.any_(): print("", file=sys.stderr) return EXIT_CODE_LIST_PROBLEM return EXIT_CODE_OK pipx-1.15.0/src/pipx/commands/pin.py000066400000000000000000000071161521706623000172750ustar00rootroot00000000000000import logging from collections.abc import Sequence from pathlib import Path from pipx.colors import bold from pipx.constants import ExitCode from pipx.emojis import sleep from pipx.util import PipxError from pipx.venv import Venv logger = logging.getLogger(__name__) def _update_pin_info(venv: Venv, package_name: str, is_main_package: bool, unpin: bool) -> int: package_metadata = venv.package_metadata[package_name] venv.update_package_metadata( package_name=str(package_metadata.package), package_or_url=str(package_metadata.package_or_url), pip_args=package_metadata.pip_args, include_dependencies=package_metadata.include_dependencies, include_apps=package_metadata.include_apps, is_main_package=is_main_package, suffix=package_metadata.suffix, pinned=not unpin, ) return 1 def pin( venv_dir: Path, verbose: bool, skip: Sequence[str], injected_only: bool = False, ) -> ExitCode: venv = Venv(venv_dir, verbose=verbose) try: main_package_metadata = venv.package_metadata[venv.main_package_name] except KeyError as e: raise PipxError(f"Package {venv.name} is not installed") from e if injected_only or skip: pinned_packages_count = 0 pinned_packages_list = [] for package_name in venv.package_metadata: if package_name == venv.main_package_name or package_name in skip: continue if venv.package_metadata[package_name].pinned: print(f"{package_name} was pinned. Not modifying.") continue pinned_packages_count += _update_pin_info(venv, package_name, is_main_package=False, unpin=False) pinned_packages_list.append(f"{package_name} {venv.package_metadata[package_name].package_version}") if pinned_packages_count != 0: print(bold(f"Pinned {pinned_packages_count} packages in venv {venv.name}")) for package in pinned_packages_list: print(" -", package) elif main_package_metadata.pinned: logger.warning(f"Package {main_package_metadata.package} already pinned {sleep}") else: for package_name in venv.package_metadata: if package_name == venv.main_package_name: _update_pin_info(venv, venv.main_package_name, is_main_package=True, unpin=False) else: _update_pin_info(venv, package_name, is_main_package=False, unpin=False) return ExitCode(0) def unpin(venv_dir: Path, verbose: bool) -> ExitCode: venv = Venv(venv_dir, verbose=verbose) try: main_package_metadata = venv.package_metadata[venv.main_package_name] except KeyError as e: raise PipxError(f"Package {venv.name} is not installed") from e unpinned_packages_count = 0 unpinned_packages_list = [] for package_name in venv.package_metadata: if package_name == main_package_metadata.package and main_package_metadata.pinned: unpinned_packages_count += _update_pin_info(venv, package_name, is_main_package=True, unpin=True) elif venv.package_metadata[package_name].pinned: unpinned_packages_count += _update_pin_info(venv, package_name, is_main_package=False, unpin=True) unpinned_packages_list.append(package_name) if unpinned_packages_count != 0: print(bold(f"Unpinned {unpinned_packages_count} packages in venv {venv.name}")) for package in unpinned_packages_list: print(" -", package) else: logger.warning(f"No packages to unpin in venv {venv.name}") return ExitCode(0) pipx-1.15.0/src/pipx/commands/reinstall.py000066400000000000000000000203751521706623000205060ustar00rootroot00000000000000import sys from collections.abc import Sequence from pathlib import Path from tempfile import mkdtemp from packaging.utils import canonicalize_name from pipx.commands.common import add_suffix from pipx.commands.inject import inject_dep from pipx.commands.install import install from pipx.commands.uninstall import _get_venv_resource_paths from pipx.constants import ( EXIT_CODE_OK, EXIT_CODE_REINSTALL_INVALID_PYTHON, EXIT_CODE_REINSTALL_VENV_NONEXISTENT, MAN_SECTIONS, ExitCode, ) from pipx.emojis import error, sleep, stars from pipx.util import PipxError, rmdir, safe_unlink from pipx.venv import Venv, VenvContainer def _create_reinstall_backup(venv_dir: Path) -> Path: backup_dir = Path(mkdtemp(prefix=f".{venv_dir.name}-", suffix="-pipx-reinstall", dir=venv_dir.parent)) backup_dir.rmdir() venv_dir.rename(backup_dir) return backup_dir def _restore_reinstall_backup(venv_dir: Path, restore_venv_dir: Path, backup_dir: Path) -> None: rmdir(venv_dir) backup_dir.rename(restore_venv_dir) def _get_reinstall_resource_paths(venv: Venv, local_bin_dir: Path, local_man_dir: Path) -> set[Path]: resource_paths = _get_venv_resource_paths("app", venv, venv.bin_path, local_bin_dir) for man_section in MAN_SECTIONS: resource_paths |= _get_venv_resource_paths("man", venv, venv.man_path / man_section, local_man_dir / man_section) return resource_paths def _get_expected_reinstall_resource_paths(venv: Venv, local_bin_dir: Path, local_man_dir: Path) -> set[Path]: resource_paths: set[Path] = set() for package_info in venv.package_metadata.values(): if package_info.include_apps: for app_path in package_info.app_paths: resource_paths.add(local_bin_dir / add_suffix(app_path.name, package_info.suffix)) for man_path in package_info.man_paths: resource_paths.add(local_man_dir / man_path.parent.name / man_path.name) if package_info.include_dependencies: for app_paths in package_info.app_paths_of_dependencies.values(): for app_path in app_paths: resource_paths.add(local_bin_dir / add_suffix(app_path.name, package_info.suffix)) for man_paths in package_info.man_paths_of_dependencies.values(): for man_path in man_paths: resource_paths.add(local_man_dir / man_path.parent.name / man_path.name) return resource_paths def _remove_stale_reinstall_resources(resource_paths: set[Path]) -> None: for path in sorted(resource_paths): try: safe_unlink(path) if path.is_symlink(): path.unlink() except FileNotFoundError: pass def reinstall( *, venv_dir: Path, local_bin_dir: Path, local_man_dir: Path, python: str, verbose: bool, force_reinstall_shared_libs: bool = False, python_flag_passed: bool = False, backend: str | None = None, env_backend: str | None = None, ) -> ExitCode: """Returns pipx exit code.""" if not venv_dir.exists(): print(f"Nothing to reinstall for {venv_dir.name} {sleep}") return EXIT_CODE_REINSTALL_VENV_NONEXISTENT try: Path(python).relative_to(venv_dir) except ValueError: pass else: print( f"{error} Error, the python executable would be deleted!", "Change it using the --python option or PIPX_DEFAULT_PYTHON environment variable.", ) return EXIT_CODE_REINSTALL_INVALID_PYTHON venv = Venv(venv_dir, verbose=verbose, backend=backend, env_backend=env_backend) venv.check_upgrade_shared_libs( pip_args=venv.pipx_metadata.main_package.pip_args, verbose=verbose, force_upgrade=force_reinstall_shared_libs ) if venv.pipx_metadata.main_package.package_or_url is not None: package_or_url = venv.pipx_metadata.main_package.package_or_url else: package_or_url = venv.main_package_name if venv.pipx_metadata.main_package.pinned: raise PipxError(f"{error} Package {venv_dir} is pinned. Run `pipx unpin {venv_dir.name}` to unpin it first.") old_resource_paths = _get_reinstall_resource_paths(venv, local_bin_dir, local_man_dir) original_venv_dir = venv_dir reinstall_backup_dir = _create_reinstall_backup(venv_dir) print(f"uninstalled {venv.name}! {stars}") # in case legacy original dir name venv_dir = venv_dir.with_name(canonicalize_name(venv_dir.name)) try: # install main package first install( venv_dir, [venv.main_package_name], [package_or_url], local_bin_dir, local_man_dir, python, venv.pipx_metadata.main_package.pip_args, venv.pipx_metadata.venv_args, verbose, force=True, reinstall=True, include_dependencies=venv.pipx_metadata.main_package.include_dependencies, preinstall_packages=[], suffix=venv.pipx_metadata.main_package.suffix, python_flag_passed=python_flag_passed, backend=backend or venv.pipx_metadata.backend, env_backend=env_backend, ) # now install injected packages for injected_name, injected_package in venv.pipx_metadata.injected_packages.items(): if injected_package.package_or_url is None: # This should never happen, but package_or_url is type # Optional[str] so mypy thinks it could be None raise PipxError(f"Internal Error injecting package {injected_package} into {venv.name}") inject_dep( venv_dir, injected_name, injected_package.package_or_url, injected_package.pip_args, verbose=verbose, include_apps=injected_package.include_apps, include_dependencies=injected_package.include_dependencies, force=True, backend=backend or venv.pipx_metadata.backend, env_backend=env_backend, ) new_resource_paths = _get_expected_reinstall_resource_paths( Venv(venv_dir, verbose=verbose), local_bin_dir, local_man_dir ) _remove_stale_reinstall_resources(old_resource_paths - new_resource_paths) except (Exception, KeyboardInterrupt): _restore_reinstall_backup(venv_dir, original_venv_dir, reinstall_backup_dir) print(f"{error} Reinstall failed; restored {venv.name}.", file=sys.stderr) raise else: rmdir(reinstall_backup_dir) # Any failure to install will raise PipxError, otherwise success return EXIT_CODE_OK def reinstall_all( venv_container: VenvContainer, local_bin_dir: Path, local_man_dir: Path, python: str, verbose: bool, *, skip: Sequence[str], python_flag_passed: bool = False, backend: str | None = None, env_backend: str | None = None, ) -> ExitCode: """Returns pipx exit code.""" failed: list[str] = [] reinstalled: list[str] = [] # iterate on all packages and reinstall them # for the first one, we also trigger # a reinstall of shared libs beforehand first_reinstall = True for venv_dir in venv_container.iter_venv_dirs(): if venv_dir.name in skip: continue try: reinstall( venv_dir=venv_dir, local_bin_dir=local_bin_dir, local_man_dir=local_man_dir, python=python, verbose=verbose, force_reinstall_shared_libs=first_reinstall, python_flag_passed=python_flag_passed, backend=backend, env_backend=env_backend, ) except PipxError as e: print(e, file=sys.stderr) failed.append(venv_dir.name) else: first_reinstall = False reinstalled.append(venv_dir.name) if len(reinstalled) == 0: print(f"No packages reinstalled after running 'pipx reinstall-all' {sleep}") if len(failed) > 0: raise PipxError(f"The following package(s) failed to reinstall: {', '.join(failed)}") # Any failure to install will raise PipxError, otherwise success return EXIT_CODE_OK __all__ = [ "reinstall", "reinstall_all", ] pipx-1.15.0/src/pipx/commands/run.py000066400000000000000000000421561521706623000173160ustar00rootroot00000000000000import datetime import hashlib import logging import re import sys import time import urllib.parse import urllib.request from pathlib import Path from shutil import which from typing import Final, NoReturn from packaging.requirements import InvalidRequirement, Requirement from pipx import paths from pipx.backends import UV, resolve_backend_name from pipx.commands.common import package_name_from_spec from pipx.commands.inject import inject_dep from pipx.commands.run_uv import run_script_via_uv_run, run_via_uv_tool_run from pipx.constants import TEMP_VENV_EXPIRATION_THRESHOLD_DAYS, WINDOWS from pipx.emojis import hazard from pipx.util import ( PipxError, exec_app, get_pypackage_bin_path, pipx_wrap, rmdir, run_pypackage_bin, ) from pipx.venv import Venv if sys.version_info < (3, 11): import tomli as tomllib else: import tomllib _LOGGER: Final[logging.Logger] = logging.getLogger(__name__) _VENV_EXPIRED_FILENAME: Final[str] = "pipx_expired_venv" _APP_NOT_FOUND_ERROR_MESSAGE: Final[str] = """\ '{app}' executable script not found in package '{package_name}'. Available executable scripts: {app_lines}""" def maybe_script_content(app: str, is_path: bool) -> str | Path | None: """If the app is a script, return its content. Return None if it should be treated as a package name.""" # Look for a local file first. app_path = Path(app) if app_path.is_file(): return app_path # In case it's a named pipe, read it out to pass to the interpreter if app_path.is_fifo(): return app_path.read_text(encoding="utf-8") if is_path: raise PipxError(f"The specified path {app} does not exist") # Check for a URL if urllib.parse.urlparse(app).scheme: if not app.endswith(".py"): raise PipxError( """ pipx will only execute apps from the internet directly if they end with '.py'. To run from an SVN, try pipx --spec URL BINARY """ ) _LOGGER.info("Detected url. Downloading and executing as a Python file.") return _http_get_request(app) # Otherwise, it's a package return None def run_script( content: str | Path, app_args: list[str], python: str, pip_args: list[str], venv_args: list[str], verbose: bool, use_cache: bool, *, backend: str | None = None, env_backend: str | None = None, resolved_backend: str | None = None, script_source: Path | None = None, dependencies: list[str] | None = None, ) -> NoReturn: requirements = _get_requirements_from_script(content) if dependencies and not requirements: # Plain scripts have nowhere to record extra requirements; the pip path # silently dropped ``--with`` here, but a clear error is better. raise PipxError( "--with packages can only be applied to scripts with PEP 723 inline metadata " "(`# /// script` block). Add the dependencies to the script's metadata or run " "via `pipx run --spec`." ) if resolved_backend == UV and requirements is not None: if script_source is not None: run_script_via_uv_run( script_path=script_source, app_args=app_args, python=python, pip_args=pip_args, venv_args=venv_args, use_cache=use_cache, verbose=verbose, dependencies=dependencies, ) # URL / named-pipe content has no on-disk path for ``uv run --script``; # warn so users on ``--backend uv`` notice they lose uv's cache and # Python-managing semantics for this run. _LOGGER.warning( pipx_wrap( f""" {hazard} Script content is not a local file; building a pipx-managed venv via the uv backend instead of `uv run --script`. The uv cache and uv-managed Python features will not apply to this run. """, subsequent_indent=" " * 4, ) ) if not requirements: python_path = Path(python) else: # Note that the environment name is based on the identified # requirements, and *not* on the script name. This is deliberate, as # it ensures that two scripts with the same requirements can use the # same environment, which means fewer environments need to be # managed. The requirements are normalised (in # _get_requirements_from_script), so that irrelevant differences in # whitespace, and similar, don't prevent environment sharing. venv_dir = _get_temporary_venv_path(requirements, python, pip_args, venv_args, resolved_backend or "pip") venv = Venv(venv_dir, backend=backend, env_backend=env_backend) _prepare_venv_cache(venv, None, use_cache) if venv_dir.exists(): _LOGGER.info(f"Reusing cached venv {venv_dir}") else: venv = Venv(venv_dir, python=python, verbose=verbose, backend=backend, env_backend=env_backend) venv.check_upgrade_shared_libs(pip_args=pip_args, verbose=verbose) venv.create_venv(venv_args, pip_args) try: venv.install_unmanaged_packages(requirements, pip_args) except: # Package installation failed, so mark the cache as expired. # This ensures an attempt is made to re-install requirements # when `pipx run` is next executed, rather than just failing. (venv_dir / _VENV_EXPIRED_FILENAME).touch() raise python_path = venv.python_path if isinstance(content, Path): exec_app([python_path, content, *app_args]) else: exec_app([python_path, "-c", content, *app_args]) def run_package( app: str, package_or_url: str, dependencies: list[str], app_args: list[str], python: str, pip_args: list[str], venv_args: list[str], pypackages: bool, verbose: bool, use_cache: bool, *, backend: str | None = None, env_backend: str | None = None, resolved_backend: str | None = None, no_path_check: bool = False, ) -> NoReturn: if not no_path_check and which(app): _LOGGER.warning( pipx_wrap( f""" {hazard} {app} is already on your PATH and installed at {which(app)}. Downloading and running anyway. """, subsequent_indent=" " * 4, ) ) if WINDOWS: app_filename = f"{app}.exe" _LOGGER.info(f"Assuming app is {app_filename!r} (Windows only)") else: app_filename = app pypackage_bin_path = get_pypackage_bin_path(app) if pypackage_bin_path.exists(): _LOGGER.info(f"Using app in local __pypackages__ directory at '{pypackage_bin_path}'") run_pypackage_bin(pypackage_bin_path, app_args) if pypackages: raise PipxError( f""" '--pypackages' flag was passed, but '{pypackage_bin_path}' was not found. See https://github.com/cs01/pythonloc to learn how to install here, or omit the flag. """ ) venv_dir = _get_temporary_venv_path([package_or_url], python, pip_args, venv_args, resolved_backend or "pip") venv = Venv(venv_dir, backend=backend, env_backend=env_backend) bin_path = venv.bin_path / app_filename _prepare_venv_cache(venv, bin_path, use_cache) if venv.has_app(app, app_filename): _LOGGER.info(f"Reusing cached venv {venv_dir}") else: _LOGGER.info(f"venv location is {venv_dir}") venv, app, app_filename = _prepare_venv( Path(venv_dir), package_or_url, app, app_filename, python, pip_args, venv_args, use_cache, verbose, backend=backend, env_backend=env_backend, ) for dependency in dependencies: inject_dep( venv_dir=venv_dir, package_name=None, package_spec=dependency, pip_args=pip_args, verbose=verbose, include_apps=False, include_dependencies=False, force=False, backend=backend, env_backend=env_backend, ) venv.run_app(app, app_filename, app_args) def run( app: str, spec: str | None, dependencies: list[str], is_path: bool, app_args: list[str], python: str, pip_args: list[str], venv_args: list[str], pypackages: bool, verbose: bool, use_cache: bool, *, no_path_check: bool = False, backend: str | None = None, env_backend: str | None = None, ) -> NoReturn: """Installs venv to temporary dir (or reuses cache), then runs app from package """ # For any package, we need to just use the name try: package_name = Requirement(app).name except InvalidRequirement: # Raw URLs to scripts are supported, too, so continue if # we can't parse this as a package package_name = app # ``resolved_backend`` only decides ROUTING (uv tool run vs Venv); cli/env # stay separate when we hand off so the Venv's source-attribution stays right. resolved_backend, _ = resolve_backend_name(cli_value=backend, env_value=env_backend) use_uvx = resolved_backend == UV and not pypackages content = None if spec is not None else maybe_script_content(app, is_path) if content is not None: run_script( content, app_args, python, pip_args, venv_args, verbose, use_cache, backend=backend, env_backend=env_backend, resolved_backend=resolved_backend, script_source=Path(app) if isinstance(content, Path) else None, dependencies=dependencies, ) elif use_uvx: run_via_uv_tool_run( app=app, package_or_url=spec if spec is not None else app, dependencies=dependencies, app_args=app_args, python=python, pip_args=pip_args, venv_args=venv_args, use_cache=use_cache, verbose=verbose, no_path_check=no_path_check, ) else: package_or_url = spec if spec is not None else app run_package( package_name, package_or_url, dependencies, app_args, python, pip_args, venv_args, pypackages, verbose, use_cache, backend=backend, env_backend=env_backend, resolved_backend=resolved_backend, no_path_check=no_path_check, ) def _prepare_venv( venv_dir: Path, package_or_url: str, app: str, app_filename: str, python: str, pip_args: list[str], venv_args: list[str], use_cache: bool, verbose: bool, *, backend: str | None = None, env_backend: str | None = None, ) -> tuple[Venv, str, str]: venv = Venv(venv_dir, python=python, verbose=verbose, backend=backend, env_backend=env_backend) venv.check_upgrade_shared_libs(pip_args=pip_args, verbose=verbose) if venv.pipx_metadata.main_package.package is not None: package_name = venv.pipx_metadata.main_package.package else: package_name = package_name_from_spec(package_or_url, python, pip_args=pip_args, verbose=verbose) override_shared = package_name == "pip" venv.create_venv(venv_args, pip_args, override_shared) venv.install_package( package_name=package_name, package_or_url=package_or_url, pip_args=pip_args, include_dependencies=False, include_apps=True, is_main_package=True, ) if not venv.has_app(app, app_filename): apps = venv.pipx_metadata.main_package.apps # If there's a single app inside the package, run that by default if app == package_name and len(apps) == 1: app = apps[0] print(f"NOTE: running app {app!r} from {package_name!r}") if WINDOWS: app_filename = f"{app}.exe" _LOGGER.info(f"Assuming app is {app_filename!r} (Windows only)") else: app_filename = app else: all_apps = (f"{a} - usage: 'pipx run --spec {package_or_url} {a} [arguments?]'" for a in apps) raise PipxError( _APP_NOT_FOUND_ERROR_MESSAGE.format( app=app, package_name=package_name, app_lines="\n ".join(all_apps), ), wrap_message=False, ) if not use_cache: # Let future _remove_all_expired_venvs know to remove this (venv_dir / _VENV_EXPIRED_FILENAME).touch() return venv, app, app_filename def _get_temporary_venv_path( requirements: list[str], python: str, pip_args: list[str], venv_args: list[str], backend: str, ) -> Path: """Hash venv-affecting inputs to a deterministic cache path. ``backend`` is part of the key so pip- and uv-backed temp venvs for the same package coexist instead of stomping on each other. """ digest = hashlib.sha256() digest.update("".join(requirements).encode()) digest.update(python.encode()) digest.update("".join(pip_args).encode()) digest.update("".join(venv_args).encode()) digest.update(backend.encode()) venv_folder_name = digest.hexdigest()[:15] # 15 chosen arbitrarily return Path(paths.ctx.venv_cache) / venv_folder_name def _is_temporary_venv_expired(venv_dir: Path) -> bool: created_time_sec = venv_dir.stat().st_ctime current_time_sec = time.mktime(datetime.datetime.now().timetuple()) age = current_time_sec - created_time_sec expiration_threshold_sec = 60 * 60 * 24 * TEMP_VENV_EXPIRATION_THRESHOLD_DAYS return age > expiration_threshold_sec or (venv_dir / _VENV_EXPIRED_FILENAME).exists() def _prepare_venv_cache(venv: Venv, bin_path: Path | None, use_cache: bool) -> None: venv_dir = venv.root if not use_cache and (bin_path is None or bin_path.exists()): _LOGGER.info(f"Removing cached venv {venv_dir!s}") rmdir(venv_dir) _remove_all_expired_venvs() def _remove_all_expired_venvs() -> None: for venv_dir in Path(paths.ctx.venv_cache).iterdir(): if _is_temporary_venv_expired(venv_dir): _LOGGER.info(f"Removing expired venv {venv_dir!s}") rmdir(venv_dir) def _http_get_request(url: str) -> str: try: res = urllib.request.urlopen(url) charset = res.headers.get_content_charset() or "utf-8" return res.read().decode(charset) except Exception as e: _LOGGER.debug("Uncaught Exception:", exc_info=True) raise PipxError(str(e)) from e # Pattern from PEP 723 / inline script metadata spec. _INLINE_SCRIPT_METADATA: Final[re.Pattern[str]] = re.compile( r""" ^\#\ ///\ (?P[a-zA-Z0-9-]+)$ \s # opening fence: ``# /// `` (?P (^\#(|\ .*)$ \s)+ ) # body: lines starting with ``#`` or ``# `` ^\#\ ///$ # closing fence: ``# ///`` """, re.VERBOSE | re.MULTILINE, ) def _get_requirements_from_script(content: str | Path) -> list[str] | None: """ Supports inline script metadata. """ if isinstance(content, Path): content = content.read_text(encoding="utf-8") name = "script" # Windows is currently getting un-normalized line endings, so normalize content = content.replace("\r\n", "\n") matches = [m for m in _INLINE_SCRIPT_METADATA.finditer(content) if m.group("type") == name] if not matches: pyproject_matches = [m for m in _INLINE_SCRIPT_METADATA.finditer(content) if m.group("type") == "pyproject"] if pyproject_matches: _LOGGER.error( pipx_wrap( f""" {hazard} Using old form of requirements table. Use updated PEP 723 syntax by replacing `# /// pyproject` with `# /// script` and `run.dependencies` (or `run.requirements`) with `dependencies`. """, subsequent_indent=" " * 4, ) ) raise ValueError("Old 'pyproject' table found") return None if len(matches) > 1: raise ValueError(f"Multiple {name} blocks found") content = "".join( line[2:] if line.startswith("# ") else line[1:] for line in matches[0].group("content").splitlines(keepends=True) ) pyproject = tomllib.loads(content) requirements = [] for requirement in pyproject.get("dependencies", []): try: parsed_requirement = Requirement(requirement) except InvalidRequirement as exc: raise PipxError(f"Invalid requirement {requirement}: {exc}") from exc # Use the normalised form of the requirement requirements.append(str(parsed_requirement)) return requirements __all__ = [ "maybe_script_content", "run", "run_package", "run_script", ] pipx-1.15.0/src/pipx/commands/run_pip.py000066400000000000000000000007661521706623000201670ustar00rootroot00000000000000from pathlib import Path from pipx.constants import ExitCode from pipx.util import PipxError from pipx.venv import Venv def run_pip(package: str, venv_dir: Path, pip_args: list[str], verbose: bool) -> ExitCode: """Returns pipx exit code.""" venv = Venv(venv_dir, verbose=verbose) if not venv.python_path.exists(): raise PipxError(f"venv for {package!r} was not found. Was {package!r} installed with pipx?") venv.verbose = True return venv.run_pip_get_exit_code(pip_args) pipx-1.15.0/src/pipx/commands/run_uv.py000066400000000000000000000117001521706623000200170ustar00rootroot00000000000000"""``pipx run`` delegation under the uv backend, kept out of ``run.py`` so the flag-translation surface doesn't bury the pip flow.""" from __future__ import annotations import logging from shutil import which from typing import TYPE_CHECKING, Final, NoReturn from pipx.backends.uv import resolve_uv_binary from pipx.emojis import hazard from pipx.util import PipxError, exec_app, pipx_wrap if TYPE_CHECKING: from pathlib import Path _LOGGER: Final[logging.Logger] = logging.getLogger(__name__) # Pip flags translatable to ``uv tool run`` / ``uv run``; anything else errors. _UV_TRANSLATABLE_VALUE_FLAGS: Final[frozenset[str]] = frozenset( {"--index-url", "-i", "--extra-index-url", "--find-links", "-f", "--no-binary", "--only-binary", "--trusted-host"} ) _UV_TRANSLATABLE_BOOL_FLAGS: Final[dict[str, str]] = { "--pre": "--prerelease=allow", "--no-deps": "--no-deps", "--no-cache-dir": "--no-cache", "--upgrade": "--upgrade", "-U": "--upgrade", } def _reject_venv_args(venv_args: list[str]) -> None: # uv builds its own venv internally; silently dropping ``--venv-args`` # (e.g. ``--system-site-packages``) would diverge from the pip path. if venv_args: raise PipxError( f"--venv-args ({' '.join(venv_args)}) is not supported by `pipx run --backend uv`.\n" "Use `pipx run --backend pip` if those flags are required, or drop them." ) def run_via_uv_tool_run( *, app: str, package_or_url: str, dependencies: list[str], app_args: list[str], python: str, pip_args: list[str], venv_args: list[str], use_cache: bool, verbose: bool, no_path_check: bool = False, ) -> NoReturn: _reject_venv_args(venv_args) if not no_path_check and (existing_app_path := which(app)): _LOGGER.warning( pipx_wrap( f""" {hazard} {app} is already on your PATH and installed at {existing_app_path}. Downloading and running anyway. """, subsequent_indent=" " * 4, ) ) cmd: list[str] = [str(resolve_uv_binary()), "tool", "run"] if package_or_url and package_or_url != app: cmd += ["--from", package_or_url] if python: cmd += ["--python", python] for dependency in dependencies: cmd += ["--with", dependency] if not use_cache: cmd.append("--no-cache") if verbose: cmd.append("--verbose") cmd += translate_pip_args_for_uv(pip_args) cmd.append(app) cmd += app_args exec_app(cmd) def run_script_via_uv_run( *, script_path: Path, app_args: list[str], python: str, pip_args: list[str], venv_args: list[str], use_cache: bool, verbose: bool, dependencies: list[str] | None = None, ) -> NoReturn: _reject_venv_args(venv_args) cmd: list[str] = [str(resolve_uv_binary()), "run", "--script"] if python: cmd += ["--python", python] if not use_cache: cmd.append("--no-cache") if verbose: cmd.append("--verbose") for dependency in dependencies or []: cmd += ["--with", dependency] cmd += translate_pip_args_for_uv(pip_args) cmd += [str(script_path), *app_args] exec_app(cmd) def translate_pip_args_for_uv(pip_args: list[str]) -> list[str]: """Translate ``pip_args`` for ``uv tool run`` / ``uv run --script``. Strict on this boundary because ``uv tool run`` exposes a smaller flag surface than ``uv pip install``; the install path stays permissive (it forwards everything to uv) since over-rejecting valid uv pip flags would be more painful than the asymmetry. """ translated: list[str] = [] iterator = iter(pip_args) for arg in iterator: if arg in ("-q", "-qq", "--quiet"): continue if arg in ("--editable", "-e"): raise PipxError( "`--editable` is not supported by `pipx run --backend uv`.\n" "Use `pipx run --backend pip` for editable installs." ) if (translated_bool := _UV_TRANSLATABLE_BOOL_FLAGS.get(arg)) is not None: translated.append(translated_bool) continue if arg in _UV_TRANSLATABLE_VALUE_FLAGS: try: value = next(iterator) except StopIteration as exc: raise PipxError(f"Missing value for {arg!r} in --pip-args.") from exc translated.extend([arg, value]) continue # Bool flags must not accept ``=value`` (caught ``--pre=foo`` slipping through). if "=" in arg and arg.split("=", 1)[0] in _UV_TRANSLATABLE_VALUE_FLAGS: translated.append(arg) continue raise PipxError( f"--pip-args contains {arg!r}, which has no `uv tool run` equivalent.\n" "Use `--backend pip` if you need pip-only flags." ) return translated __all__ = [ "run_script_via_uv_run", "run_via_uv_tool_run", "translate_pip_args_for_uv", ] pipx-1.15.0/src/pipx/commands/uninject.py000066400000000000000000000146121521706623000203250ustar00rootroot00000000000000import logging import os from importlib import metadata from pathlib import Path from packaging.utils import canonicalize_name from pipx.colors import bold from pipx.commands.uninstall import ( _get_package_bin_dir_app_paths, _get_package_man_paths, ) from pipx.constants import ( EXIT_CODE_OK, EXIT_CODE_UNINJECT_ERROR, MAN_SECTIONS, ExitCode, ) from pipx.emojis import stars from pipx.util import PipxError, pipx_wrap from pipx.venv import Venv from pipx.venv_inspect import fetch_info_in_venv, get_dist, get_required_dependency_names logger = logging.getLogger(__name__) def uninject( venv_dir: Path, dependencies: list[str], *, local_bin_dir: Path, local_man_dir: Path, leave_deps: bool, verbose: bool, ) -> ExitCode: """Returns pipx exit code""" if not venv_dir.exists() or next(venv_dir.iterdir(), None) is None: raise PipxError(f"Virtual environment {venv_dir.name} does not exist.") venv = Venv(venv_dir, verbose=verbose) if not venv.package_metadata: raise PipxError( f""" Can't uninject from Virtual Environment {venv_dir.name!r}. {venv_dir.name!r} has missing internal pipx metadata. It was likely installed using a pipx version before 0.15.0.0. Please uninstall and install {venv_dir.name!r} manually to fix. """ ) all_success = True for dep in dependencies: all_success &= uninject_dep( venv, dep, local_bin_dir=local_bin_dir, local_man_dir=local_man_dir, leave_deps=leave_deps, ) if all_success: return EXIT_CODE_OK else: return EXIT_CODE_UNINJECT_ERROR def uninject_dep( venv: Venv, package_name: str, *, local_bin_dir: Path, local_man_dir: Path, leave_deps: bool = False, ) -> bool: package_name = canonicalize_name(package_name) if package_name == venv.pipx_metadata.main_package.package: logger.warning( pipx_wrap( f""" {package_name} is the main package of {venv.root.name} venv. Use `pipx uninstall {venv.root.name}` to uninstall instead of uninject. """, subsequent_indent=" " * 4, ) ) return False if package_name not in venv.pipx_metadata.injected_packages: logger.warning(f"{package_name} is not in the {venv.root.name} venv. Skipping.") return False need_app_uninstall = venv.package_metadata[package_name].include_apps new_resource_paths = get_include_resource_paths(package_name, venv, local_bin_dir, local_man_dir) if not leave_deps: orig_not_required_packages = venv.list_installed_packages(not_required=True) logger.info(f"Original not required packages: {orig_not_required_packages}") venv.uninstall_package(package=package_name, was_injected=True) if not leave_deps: new_not_required_packages = venv.list_installed_packages(not_required=True) logger.info(f"New not required packages: {new_not_required_packages}") deps_of_uninstalled = new_not_required_packages - orig_not_required_packages if deps_of_uninstalled: remaining_deps = _get_remaining_dependencies(venv, package_name) deps_of_uninstalled -= remaining_deps logger.info(f"Dependencies of uninstalled package: {deps_of_uninstalled}") for dep_package_name in deps_of_uninstalled: venv.uninstall_package(package=dep_package_name, was_injected=False) deps_string = " and its dependencies" else: deps_string = "" if need_app_uninstall: for path in new_resource_paths: try: os.unlink(path) except FileNotFoundError: logger.info(f"tried to remove but couldn't find {path}") else: logger.info(f"removed file {path}") print(f"Uninjected package {bold(package_name)}{deps_string} from venv {bold(venv.root.name)} {stars}") return True def get_include_resource_paths(package_name: str, venv: Venv, local_bin_dir: Path, local_man_dir: Path) -> set[Path]: bin_dir_app_paths = _get_package_bin_dir_app_paths( venv, venv.package_metadata[package_name], venv.bin_path, local_bin_dir ) man_paths = set() for man_section in MAN_SECTIONS: man_paths |= _get_package_man_paths( venv, venv.package_metadata[package_name], venv.man_path / man_section, local_man_dir / man_section, ) pkg_metadata = venv.package_metadata[package_name] all_apps = set(pkg_metadata.apps) all_man_pages = set(pkg_metadata.man_pages) if pkg_metadata.include_dependencies: all_apps.update(pkg_metadata.apps_of_dependencies) all_man_pages.update(pkg_metadata.man_pages_of_dependencies) need_to_remove = set() for bin_dir_app_path in bin_dir_app_paths: if bin_dir_app_path.name in all_apps: need_to_remove.add(bin_dir_app_path) for man_path in man_paths: path = Path(man_path.parent.name) / man_path.name if str(path) in all_man_pages: need_to_remove.add(path) return need_to_remove def _get_remaining_dependencies(venv: Venv, excluded_package: str) -> set[str]: venv_sys_path, venv_env, _ = fetch_info_in_venv(venv.python_path) distributions = tuple(metadata.distributions(path=venv_sys_path)) remaining_deps: set[str] = set() remaining_packages: list[str] = [ name for name in [venv.pipx_metadata.main_package.package] + list(venv.pipx_metadata.injected_packages) if name is not None and name != excluded_package ] for pkg_name in remaining_packages: remaining_deps |= _collect_transitive_deps(pkg_name, distributions, venv_env) return remaining_deps def _collect_transitive_deps( package_name: str, distributions: tuple[metadata.Distribution, ...], env: dict[str, str], visited: set[str] | None = None, ) -> set[str]: if visited is None: visited = set() canonical = canonicalize_name(package_name) if canonical in visited: return visited visited.add(canonical) if dist := get_dist(package_name, distributions): for dep_name in get_required_dependency_names(dist, env): _collect_transitive_deps(dep_name, distributions, env, visited) return visited pipx-1.15.0/src/pipx/commands/uninstall.py000066400000000000000000000143061521706623000205170ustar00rootroot00000000000000import logging from pathlib import Path from shutil import which from typing import TYPE_CHECKING if TYPE_CHECKING: from collections.abc import Callable from pipx.commands.common import ( add_suffix, can_symlink, get_exposed_man_paths_for_package, get_exposed_paths_for_package, ) from pipx.constants import ( EXIT_CODE_OK, EXIT_CODE_UNINSTALL_ERROR, EXIT_CODE_UNINSTALL_VENV_NONEXISTENT, MAN_SECTIONS, ExitCode, ) from pipx.emojis import hazard, sleep, stars from pipx.pipx_metadata_file import PackageInfo from pipx.util import rmdir, safe_unlink from pipx.venv import Venv, VenvContainer from pipx.venv_inspect import VenvMetadata logger = logging.getLogger(__name__) def _venv_metadata_to_package_info( venv_metadata: VenvMetadata, package_name: str, package_or_url: str = "", pip_args: list[str] | None = None, include_apps: bool = True, include_dependencies: bool = False, suffix: str = "", ) -> PackageInfo: if pip_args is None: pip_args = [] return PackageInfo( package=package_name, package_or_url=package_or_url, pip_args=pip_args, include_apps=include_apps, include_dependencies=include_dependencies, apps=venv_metadata.apps, app_paths=venv_metadata.app_paths, apps_of_dependencies=venv_metadata.apps_of_dependencies, app_paths_of_dependencies=venv_metadata.app_paths_of_dependencies, man_pages=venv_metadata.man_pages, man_paths=venv_metadata.man_paths, man_pages_of_dependencies=venv_metadata.man_pages_of_dependencies, man_paths_of_dependencies=venv_metadata.man_paths_of_dependencies, package_version=venv_metadata.package_version, suffix=suffix, ) def _get_package_bin_dir_app_paths( venv: Venv, package_info: PackageInfo, venv_bin_path: Path, local_bin_dir: Path ) -> set[Path]: suffix = package_info.suffix apps = [] if package_info.include_apps: apps += package_info.apps if package_info.include_dependencies: apps += package_info.apps_of_dependencies return get_exposed_paths_for_package(venv_bin_path, local_bin_dir, [add_suffix(app, suffix) for app in apps]) def _get_package_man_paths(venv: Venv, package_info: PackageInfo, venv_man_path: Path, local_man_dir: Path) -> set[Path]: man_pages = [] if package_info.include_apps: man_pages += package_info.man_pages if package_info.include_dependencies: man_pages += package_info.man_pages_of_dependencies return get_exposed_man_paths_for_package(venv_man_path, local_man_dir, man_pages) def _get_venv_resource_paths( resource_type: str, venv: Venv, venv_resource_path: Path, local_resource_dir: Path ) -> set[Path]: resource_paths = set() assert resource_type in ("app", "man"), "invalid resource type" get_package_resource_paths: Callable[[Venv, PackageInfo, Path, Path], set[Path]] get_package_resource_paths = { "app": _get_package_bin_dir_app_paths, "man": _get_package_man_paths, }[resource_type] if venv.pipx_metadata.main_package.package is not None: # Valid metadata for venv for package_info in venv.package_metadata.values(): resource_paths |= get_package_resource_paths(venv, package_info, venv_resource_path, local_resource_dir) elif venv.python_path.is_file(): # No metadata from pipx_metadata.json, but valid python interpreter. # In pre-metadata-pipx venv.root.name is name of main package # In pre-metadata-pipx there is no suffix # We make the conservative assumptions: no injected packages, # not include_dependencies. Other PackageInfo fields are irrelevant # here. venv_metadata = venv.get_venv_metadata_for_package(venv.root.name, set()) main_package_info = _venv_metadata_to_package_info(venv_metadata, venv.root.name) resource_paths = get_package_resource_paths(venv, main_package_info, venv_resource_path, local_resource_dir) else: # No metadata and no valid python interpreter. # We'll take our best guess on what to uninstall here based on symlink # location for symlink-capable systems. # The heuristic here is any symlink in ~/.local/bin pointing to # .local/share/pipx/venvs/VENV_NAME/{bin,Scripts} should be uninstalled. # For non-symlink systems we give up and return an empty set. if not local_resource_dir.is_dir() or not can_symlink(local_resource_dir): return set() resource_paths = get_exposed_paths_for_package(venv_resource_path, local_resource_dir) return resource_paths def uninstall(venv_dir: Path, local_bin_dir: Path, local_man_dir: Path, verbose: bool) -> ExitCode: """Uninstall entire venv_dir, including main package and all injected packages. Returns pipx exit code. """ if not venv_dir.exists(): print(f"Nothing to uninstall for {venv_dir.name} {sleep}") app = which(venv_dir.name) if app: print(f"{hazard} Note: '{app}' still exists on your system and is on your PATH") return EXIT_CODE_UNINSTALL_VENV_NONEXISTENT venv = Venv(venv_dir, verbose=verbose) bin_dir_app_paths = _get_venv_resource_paths("app", venv, venv.bin_path, local_bin_dir) man_dir_paths = set() for man_section in MAN_SECTIONS: man_dir_paths |= _get_venv_resource_paths("man", venv, venv.man_path / man_section, local_man_dir / man_section) for path in bin_dir_app_paths | man_dir_paths: try: safe_unlink(path) except FileNotFoundError: logger.info(f"tried to remove but couldn't find {path}") else: logger.info(f"removed file {path}") rmdir(venv_dir) print(f"uninstalled {venv.name}! {stars}") return EXIT_CODE_OK def uninstall_all( venv_container: VenvContainer, local_bin_dir: Path, local_man_dir: Path, verbose: bool, ) -> ExitCode: """Returns pipx exit code.""" all_success = True for venv_dir in venv_container.iter_venv_dirs(): return_val = uninstall(venv_dir, local_bin_dir, local_man_dir, verbose) all_success &= return_val == 0 return EXIT_CODE_OK if all_success else EXIT_CODE_UNINSTALL_ERROR pipx-1.15.0/src/pipx/commands/upgrade.py000066400000000000000000000245241521706623000201400ustar00rootroot00000000000000import logging import os import sys from collections.abc import Sequence from pathlib import Path from typing import Final from pipx import commands, paths from pipx.colors import bold, red from pipx.commands.common import expose_resources_globally from pipx.constants import EXIT_CODE_OK, ExitCode from pipx.emojis import sleep from pipx.package_specifier import parse_specifier_for_upgrade from pipx.shared_libs import shared_libs from pipx.util import PipxError, pipx_wrap from pipx.venv import Venv, VenvContainer _LOGGER: Final[logging.Logger] = logging.getLogger(__name__) def _upgrade_package( venv: Venv, package_name: str, pip_args: list[str], is_main_package: bool, force: bool, upgrading_all: bool, ) -> int: """Returns 1 if package version changed, 0 if same version""" package_metadata = venv.package_metadata[package_name] if package_metadata.package_or_url is None: raise PipxError(f"Internal Error: package {package_name} has corrupt pipx metadata.") elif package_metadata.pinned: if package_metadata.package != venv.main_package_name: _LOGGER.warning( f"Not upgrading pinned package {package_metadata.package} in venv {venv.name}. " f"Run `pipx unpin {venv.name}` to unpin it." ) else: _LOGGER.warning(f"Not upgrading pinned package {venv.name}. Run `pipx unpin {venv.name}` to unpin it.") return 0 package_or_url = parse_specifier_for_upgrade(package_metadata.package_or_url) old_version = package_metadata.package_version venv.upgrade_package( package_name, package_or_url, pip_args, include_dependencies=package_metadata.include_dependencies, include_apps=package_metadata.include_apps, is_main_package=is_main_package, suffix=package_metadata.suffix, ) package_metadata = venv.package_metadata[package_name] display_name = f"{package_metadata.package}{package_metadata.suffix}" new_version = package_metadata.package_version if package_metadata.include_apps: expose_resources_globally( "app", paths.ctx.bin_dir, package_metadata.app_paths, force=force, suffix=package_metadata.suffix, ) expose_resources_globally("man", paths.ctx.man_dir, package_metadata.man_paths, force=force) if package_metadata.include_dependencies: for app_paths in package_metadata.app_paths_of_dependencies.values(): expose_resources_globally( "app", paths.ctx.bin_dir, app_paths, force=force, suffix=package_metadata.suffix, ) for man_paths in package_metadata.man_paths_of_dependencies.values(): expose_resources_globally("man", paths.ctx.man_dir, man_paths, force=force) if old_version == new_version: if upgrading_all: pass else: print( pipx_wrap( f""" {display_name} is already at latest version {old_version} (location: {venv.root!s}) """ ) ) return 0 else: print( pipx_wrap( f""" upgraded package {display_name} from {old_version} to {new_version} (location: {venv.root!s}) """ ) ) return 1 def _upgrade_venv( venv_dir: Path, pip_args: list[str], verbose: bool, *, include_injected: bool, upgrading_all: bool, force: bool, install: bool = False, venv_args: list[str] | None = None, python: str | None = None, python_flag_passed: bool = False, backend: str | None = None, env_backend: str | None = None, venv: Venv | None = None, shared_libs_already_checked: bool = False, ) -> int: """Return number of packages whose versions changed. ``upgrade-all`` passes ``venv`` and ``shared_libs_already_checked=True`` after its own pre-checks to avoid re-running them per venv. """ if not venv_dir.is_dir(): if install: if venv_args is None: venv_args = [] commands.install( venv_dir=None, venv_args=venv_args, package_names=None, package_specs=[str(venv_dir).split(os.path.sep)[-1]], local_bin_dir=paths.ctx.bin_dir, local_man_dir=paths.ctx.man_dir, python=python, pip_args=pip_args, verbose=verbose, force=force, reinstall=False, include_dependencies=False, preinstall_packages=None, python_flag_passed=python_flag_passed, backend=backend, env_backend=env_backend, ) return 0 else: raise PipxError( f""" Package is not installed. Expected to find {venv_dir!s}, but it does not exist. """ ) if venv_args and not install: _LOGGER.info(f"Ignoring {', '.join(venv_args)} as not combined with --install") if python and not install: _LOGGER.info("Ignoring --python as not combined with --install") if venv is None: venv = Venv(venv_dir, verbose=verbose, backend=backend, env_backend=env_backend) if not pip_args: pip_args = venv.pipx_metadata.main_package.pip_args if not shared_libs_already_checked: venv.check_upgrade_shared_libs(pip_args=pip_args, verbose=verbose) if not venv.python_path.is_file(): raise PipxError( f"Not upgrading {red(bold(venv_dir.name))}. It has an invalid python interpreter {venv.python_path}.\n" f"This usually happens after a system Python upgrade.\n" f"To fix, execute: pipx reinstall-all", wrap_message=False, ) if not venv.package_metadata: raise PipxError( f"Not upgrading {red(bold(venv_dir.name))}. It has missing internal pipx metadata.\n" f"It was likely installed using a pipx version before 0.15.0.0.\n" f"Please uninstall and install this package to fix.", wrap_message=False, ) # Upgrade shared libraries (pip, setuptools and wheel) venv.upgrade_packaging_libraries(pip_args) versions_updated = 0 package_name = venv.main_package_name versions_updated += _upgrade_package( venv, package_name, pip_args, is_main_package=True, force=force, upgrading_all=upgrading_all, ) if include_injected: for package_name in venv.package_metadata: if package_name == venv.main_package_name: continue injected_pip_args = pip_args or venv.package_metadata[package_name].pip_args versions_updated += _upgrade_package( venv, package_name, injected_pip_args, is_main_package=False, force=force, upgrading_all=upgrading_all, ) return versions_updated def upgrade( venv_dirs: dict[str, Path], python: str | None, pip_args: list[str], venv_args: list[str], verbose: bool, *, include_injected: bool, force: bool, install: bool, python_flag_passed: bool = False, backend: str | None = None, env_backend: str | None = None, ) -> ExitCode: """Return pipx exit code.""" for venv_dir in venv_dirs.values(): _ = _upgrade_venv( venv_dir, pip_args, verbose, include_injected=include_injected, upgrading_all=False, force=force, install=install, venv_args=venv_args, python=python, python_flag_passed=python_flag_passed, backend=backend, env_backend=env_backend, ) # Any error in upgrade will raise PipxError (e.g. from venv.upgrade_package()) return EXIT_CODE_OK def upgrade_all( venv_container: VenvContainer, verbose: bool, *, pip_args: list[str], include_injected: bool, skip: Sequence[str], force: bool, python_flag_passed: bool = False, backend: str | None = None, env_backend: str | None = None, ) -> ExitCode: """Return pipx exit code.""" failed: list[str] = [] upgraded: list[str] = [] for venv_dir in venv_container.iter_venv_dirs(): # Cheap skip-list check first so we don't pay metadata read + # cross-backend warning + shared-libs health check on excluded venvs. if venv_dir.name in skip: continue venv = Venv(venv_dir, verbose=verbose, backend=backend, env_backend=env_backend) if "--editable" in venv.pipx_metadata.main_package.pip_args: continue venv.check_upgrade_shared_libs(pip_args=pip_args, verbose=verbose) try: versions_updated = _upgrade_venv( venv_dir, pip_args, verbose=verbose, include_injected=include_injected, upgrading_all=True, force=force, python_flag_passed=python_flag_passed, backend=backend, env_backend=env_backend, venv=venv, shared_libs_already_checked=True, ) if versions_updated > 0: upgraded.append(venv_dir.name) except PipxError as e: print(e, file=sys.stderr) failed.append(venv_dir.name) if len(upgraded) == 0: print(f"No packages upgraded after running 'pipx upgrade-all' {sleep}") if len(failed) > 0: raise PipxError(f"The following package(s) failed to upgrade: {','.join(failed)}") # Any failure to install will raise PipxError, otherwise success return EXIT_CODE_OK def upgrade_shared( verbose: bool, pip_args: list[str], ) -> ExitCode: # Always refreshes: the next pip-backed install needs a fresh shared-libs # venv even when all currently-installed venvs are uv-backed. shared_libs.upgrade(verbose=verbose, pip_args=pip_args, raises=True) return EXIT_CODE_OK __all__ = [ "upgrade", "upgrade_all", "upgrade_shared", ] pipx-1.15.0/src/pipx/constants.py000066400000000000000000000062111521706623000167150ustar00rootroot00000000000000import enum import os import platform import sysconfig from textwrap import dedent from typing import NewType # enum.StrEnum is Python 3.11+; pipx supports 3.10. class FetchPythonOptions(str, enum.Enum): ALWAYS = "always" MISSING = "missing" NEVER = "never" def __str__(self) -> str: return self.value PIPX_SHARED_PTH = "pipx_shared.pth" TEMP_VENV_EXPIRATION_THRESHOLD_DAYS = 14 MINIMUM_PYTHON_VERSION = "3.10" MAN_SECTIONS = [f"man{i}" for i in range(1, 10)] _FETCH_MISSING_PYTHON_RAW = os.environ.get("PIPX_FETCH_MISSING_PYTHON") _FETCH_PYTHON_RAW = os.environ.get("PIPX_FETCH_PYTHON") _FALSY = ("", "0", "false", "f", "no", "n", "off") def _compute_fetch_python(missing_raw: str | None, python_raw: str | None) -> tuple[FetchPythonOptions, bool]: fetch_missing = missing_raw is not None and missing_raw.strip().lower() not in _FALSY raw = python_raw or (FetchPythonOptions.MISSING.value if fetch_missing else FetchPythonOptions.NEVER.value) try: return FetchPythonOptions(raw), False except ValueError: return FetchPythonOptions.NEVER, True _FETCH_PYTHON, _FETCH_PYTHON_INVALID = _compute_fetch_python(_FETCH_MISSING_PYTHON_RAW, _FETCH_PYTHON_RAW) ExitCode = NewType("ExitCode", int) # pipx shell exit codes EXIT_CODE_OK = ExitCode(0) EXIT_CODE_INJECT_ERROR = ExitCode(1) EXIT_CODE_UNINJECT_ERROR = ExitCode(1) EXIT_CODE_INSTALL_VENV_EXISTS = ExitCode(0) EXIT_CODE_LIST_PROBLEM = ExitCode(1) EXIT_CODE_UNINSTALL_VENV_NONEXISTENT = ExitCode(1) EXIT_CODE_UNINSTALL_ERROR = ExitCode(1) EXIT_CODE_REINSTALL_VENV_NONEXISTENT = ExitCode(1) EXIT_CODE_REINSTALL_INVALID_PYTHON = ExitCode(1) EXIT_CODE_SPECIFIED_PYTHON_EXECUTABLE_NOT_FOUND = ExitCode(1) def is_windows() -> bool: return platform.system() == "Windows" def is_macos() -> bool: return platform.system() == "Darwin" def is_linux() -> bool: return platform.system() == "Linux" def is_mingw() -> bool: return sysconfig.get_platform().startswith("mingw") WINDOWS: bool = is_windows() MACOS: bool = is_macos() LINUX: bool = is_linux() MINGW: bool = is_mingw() completion_instructions = dedent( """ If you encountered register-python-argcomplete command not found error, or if you are using zipapp, run pipx install argcomplete before running any of the following commands. Add the appropriate command to your shell's config file so that it is run on startup. You will likely have to restart or re-login for the autocompletion to start working. bash: eval "$(register-python-argcomplete pipx)" zsh: To activate completions in zsh, first make sure compinit is marked for autoload and run autoload: autoload -U compinit && compinit Afterwards you can enable completions for pipx: eval "$(register-python-argcomplete pipx)" NOTE: If your version of argcomplete is earlier than v3, you may need to have bashcompinit enabled in zsh by running: autoload -U bashcompinit bashcompinit tcsh: eval `register-python-argcomplete --shell tcsh pipx` fish: # Not required to be in the config file, only run once register-python-argcomplete --shell fish pipx >~/.config/fish/completions/pipx.fish """ ) pipx-1.15.0/src/pipx/emojis.py000066400000000000000000000016551521706623000161760ustar00rootroot00000000000000import os import sys def strtobool(val: str) -> bool: val = val.lower() if val in ("y", "yes", "t", "true", "on", "1"): return True elif val in ("n", "no", "f", "false", "off", "0"): return False else: return False def use_emojis() -> bool: # All emojis that pipx might possibly use emoji_test_str = "โœจ๐ŸŒŸโš ๏ธ๐Ÿ˜ดโฃทโฃฏโฃŸโกฟโขฟโฃปโฃฝโฃพ" try: emoji_test_str.encode(sys.stderr.encoding) platform_emoji_support = True except UnicodeEncodeError: platform_emoji_support = False use_emoji = os.getenv("PIPX_USE_EMOJI") if use_emoji is None: use_emoji = str(os.getenv("USE_EMOJI", platform_emoji_support)) return strtobool(use_emoji) EMOJI_SUPPORT = use_emojis() if EMOJI_SUPPORT: stars = "โœจ ๐ŸŒŸ โœจ" hazard = "โš ๏ธ" error = "โ›”" sleep = "๐Ÿ˜ด" else: stars = "" hazard = "" error = "" sleep = "" pipx-1.15.0/src/pipx/interpreter.py000066400000000000000000000165171521706623000172560ustar00rootroot00000000000000import logging import os import shutil import subprocess import sys from pathlib import Path from packaging import version from pipx.constants import WINDOWS, FetchPythonOptions from pipx.standalone_python import download_python_build_standalone from pipx.util import PipxError logger = logging.getLogger(__name__) def has_venv() -> bool: try: import venv # noqa: F401, PLC0415 except ImportError: return False return True class InterpreterResolutionError(PipxError): def __init__(self, source: str, version: str, wrap_message: bool = True): self.source = source self.version = version potentially_path = "/" in version potentially_pylauncher = "python" not in version and not potentially_path message = ( f"No executable for the provided Python version '{version}' found in {source}." " Please make sure the provided version is " ) if source == "py launcher": message += "listed when running `py --list`." if source == "PATH": message += "on your PATH or the file path is valid. " if potentially_path: message += "The provided version looks like a path, but no executable was found there." if potentially_pylauncher: message += ( "The provided version looks like a version, " "but both the python command and the Python Launcher were not found on PATH." ) if source == "the python-build-standalone project": message += "listed in https://github.com/astral-sh/python-build-standalone/releases/latest." if source == "PIPX_DEFAULT_PYTHON": message += ( "on your PATH or the file path is valid. " "Try setting it to an executable name (e.g. python3.10) " "or a full path (e.g. /usr/bin/python3.10)." ) super().__init__(message, wrap_message) def find_unix_command_python(python_version: str) -> str | None: try: parsed_python_version = version.parse(python_version) except version.InvalidVersion: logger.info(f"Invalid Python version: {python_version}") return None if ( parsed_python_version.epoch != 0 or parsed_python_version.is_devrelease or parsed_python_version.is_postrelease or parsed_python_version.is_prerelease ): logger.info(f"Unsupported Python version: {python_version}") return None # Python command could be `python3` or `python3.x` without micro version component python_command = f"python{'.'.join(python_version.split('.')[:2])}" python_path = shutil.which(python_command) if not python_path: logger.info(f"Command `{python_command}` was not found on the system") return None if parsed_python_version.micro != 0: logger.warning( f"The command '{python_command}' located at '{python_path}' will be used. " f"It may not match the specified version {python_version} at the micro/patch level." ) return python_path def _fetch_standalone_interpreter(python_version: str) -> str: try: return download_python_build_standalone(python_version) except PipxError as e: raise InterpreterResolutionError(source="the python-build-standalone project", version=python_version) from e def find_python_interpreter(python_version: str, fetch_python: FetchPythonOptions = FetchPythonOptions.NEVER) -> str: if fetch_python == FetchPythonOptions.ALWAYS: if os.sep in python_version or "/" in python_version or Path(python_version).is_file(): raise PipxError( f"--fetch-python={FetchPythonOptions.ALWAYS} requires a Python version " f"(e.g. '3.13'), got '{python_version}'." ) return _fetch_standalone_interpreter(python_version) if Path(python_version).is_file() or shutil.which(python_version): return python_version if not WINDOWS: python_unix_command = find_unix_command_python(python_version) if python_unix_command: return python_unix_command try: py_executable = find_py_launcher_python(python_version) if py_executable: return py_executable except (subprocess.CalledProcessError, FileNotFoundError) as e: if fetch_python != FetchPythonOptions.MISSING: raise InterpreterResolutionError(source="py launcher", version=python_version) from e if fetch_python == FetchPythonOptions.MISSING: return _fetch_standalone_interpreter(python_version) raise InterpreterResolutionError(source="PATH", version=python_version) # The following code was copied from https://github.com/uranusjr/pipx-standalone # which uses the same technique to build a completely standalone pipx # distribution. # # If we are running under the Windows embeddable distribution, # venv isn't available (and we probably don't want to use the # embeddable distribution as our applications' base Python anyway) # so we try to locate the system Python and use that instead. def find_py_launcher_python(python_version: str | None = None) -> str | None: py = shutil.which("py") if py and python_version: python_semver = python_version if python_version.startswith("python"): logger.warning( "Removing `python` from the start of the version, as pylauncher just expects the semantic version" ) python_semver = python_semver.lstrip("python") py = subprocess.run( [py, f"-{python_semver}", "-c", "import sys; print(sys.executable)"], capture_output=True, text=True, check=True, ).stdout.strip() return py def _find_default_windows_python() -> str: if has_venv(): return sys.executable python = find_py_launcher_python() or shutil.which("python") if python is None: raise PipxError("No suitable Python found") # If the path contains "WindowsApps", it's the store python if "WindowsApps" not in python: return python # Special treatment to detect Windows Store stub. # https://twitter.com/zooba/status/1212454929379581952 proc = subprocess.run([python, "-V"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=False) if proc.returncode != 0: # Cover the 9009 return code preemptively. raise PipxError("No suitable Python found") if not proc.stdout.strip(): # A real Python should print version, Windows Store stub won't. raise PipxError("No suitable Python found") return python # This executable seems to work. def _get_sys_executable() -> str: if WINDOWS: return _find_default_windows_python() else: return str(Path(sys.executable).resolve()) def _resolve_python(python: str) -> str: if (path := Path(python)).is_file(): return str(path.resolve()) if found := shutil.which(python): return found if not WINDOWS and (found := find_unix_command_python(python)): return found raise InterpreterResolutionError(source="PIPX_DEFAULT_PYTHON", version=python) env_default_python = os.environ.get("PIPX_DEFAULT_PYTHON") if not env_default_python: DEFAULT_PYTHON = _get_sys_executable() else: DEFAULT_PYTHON = _resolve_python(env_default_python) pipx-1.15.0/src/pipx/main.py000066400000000000000000001466261521706623000156440ustar00rootroot00000000000000# PYTHON_ARGCOMPLETE_OK """The command line interface to pipx""" import argparse import logging import logging.config import os import re import shlex import sys import textwrap import time import urllib.parse from collections.abc import Callable from dataclasses import dataclass from pathlib import Path from typing import Any, NoReturn import argcomplete import platformdirs from packaging.utils import canonicalize_name from pipx import commands, constants, paths from pipx.animate import hide_cursor, show_cursor from pipx.backends import KNOWN_BACKENDS, UV, env_default_backend, get_backend, resolve_backend_name from pipx.colors import bold, green from pipx.commands.environment import ENVIRONMENT_VALUE_CHOICES, ENVIRONMENT_VARIABLES from pipx.constants import ( _FETCH_MISSING_PYTHON_RAW, _FETCH_PYTHON, _FETCH_PYTHON_INVALID, _FETCH_PYTHON_RAW, EXIT_CODE_OK, EXIT_CODE_SPECIFIED_PYTHON_EXECUTABLE_NOT_FOUND, MINIMUM_PYTHON_VERSION, WINDOWS, ExitCode, FetchPythonOptions, ) from pipx.emojis import hazard from pipx.interpreter import ( DEFAULT_PYTHON, InterpreterResolutionError, find_python_interpreter, ) from pipx.package_specifier import valid_pypi_name from pipx.util import PipxError, mkdir, pipx_wrap, rmdir from pipx.venv import VenvContainer from pipx.version import version as __version__ logger = logging.getLogger(__name__) VenvCompleter = Callable[[str], list[str]] def print_version() -> None: print(__version__) def prog_name() -> str: try: prog = os.path.basename(sys.argv[0]) if prog == "__main__.py": return f"{sys.executable} -m pipx" else: return prog except Exception: pass return "pipx" SPEC_HELP = textwrap.dedent( """\ The package name or specific installation source passed to pip. Runs `pip install -U SPEC`. For example `--spec mypackage==2.0.0` or `--spec git+https://github.com/user/repo.git@branch` """ ) PIPX_DESCRIPTION = textwrap.dedent( f""" Install and execute apps from Python packages. Binaries can either be installed globally into isolated Virtual Environments or run directly in a temporary Virtual Environment. Virtual Environment location is {paths.ctx.venvs!s}. Symlinks to apps are placed in {paths.ctx.bin_dir!s}. Symlinks to manual pages are placed in {paths.ctx.man_dir!s}. """ ) PIPX_DESCRIPTION += pipx_wrap( """ optional environment variables: PIPX_HOME Overrides default pipx location. Virtual Environments will be installed to $PIPX_HOME/venvs. PIPX_GLOBAL_HOME Used instead of PIPX_HOME when the `--global` option is given. PIPX_BIN_DIR Overrides location of app installations. Apps are symlinked or copied here. PIPX_GLOBAL_BIN_DIR Used instead of PIPX_BIN_DIR when the `--global` option is given. PIPX_MAN_DIR Overrides location of manual pages installations. Manual pages are symlinked or copied here. PIPX_GLOBAL_MAN_DIR Used instead of PIPX_MAN_DIR when the `--global` option is given. PIPX_DEFAULT_PYTHON Overrides default python used for commands. PIPX_DEFAULT_BACKEND Overrides which backend (`pip` or `uv`) is used for new venvs. PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE Skips automatic shared library upgrades. PIPX_USE_EMOJI Overrides emoji behavior. Default value varies based on platform. PIPX_HOME_ALLOW_SPACE Overrides default warning on spaces in the home path """, subsequent_indent=" " * 24, # match the indent of argparse options keep_newlines=True, ) DOC_DEFAULT_PYTHON = os.getenv("PIPX__DOC_DEFAULT_PYTHON", DEFAULT_PYTHON) INSTALL_DESCRIPTION = textwrap.dedent( f""" The install command is the preferred way to globally install apps from python packages on your system. It creates an isolated virtual environment for the package, then ensures the package's apps are accessible on your $PATH. The package's manual pages installed in share/man/man[1-9] can be viewed with man on an operating system where it is available and the path in the environment variable `PIPX_MAN_DIR` (default: {paths.DEFAULT_PIPX_MAN_DIR}) is in the man search path ($MANPATH). The result: apps you can run from anywhere, located in packages you can cleanly upgrade or uninstall. Guaranteed to not have dependency version conflicts or interfere with your OS's python packages. 'sudo' is not required to do this. pipx install PACKAGE_SPEC ... pipx install --python PYTHON PACKAGE_SPEC pipx install VCS_URL pipx install ./LOCAL_PATH pipx install ZIP_FILE pipx install TAR_GZ_FILE The PACKAGE_SPEC argument is passed directly to `pip install`. Virtual Environments will be installed to `$PIPX_HOME/venvs`. The default pipx home location is {paths.DEFAULT_PIPX_HOME} and can be overridden by setting the environment variable `PIPX_HOME`. If the `--global` option is used, the default pipx home location instead is {paths.DEFAULT_PIPX_GLOBAL_HOME} and can be overridden by setting the environment variable `PIPX_GLOBAL_HOME`. The default app location is {paths.DEFAULT_PIPX_BIN_DIR} and can be overridden by setting the environment variable `PIPX_BIN_DIR`. If the `--global` option is used, the default app location instead is {paths.DEFAULT_PIPX_GLOBAL_BIN_DIR} and can be overridden by setting the environment variable `PIPX_GLOBAL_BIN_DIR`. The default manual pages location is {paths.DEFAULT_PIPX_MAN_DIR} and can be overridden by setting the environment variable `PIPX_MAN_DIR`. If the `--global` option is used, the default manual pages location instead is {paths.DEFAULT_PIPX_GLOBAL_MAN_DIR} and can be overridden by setting the environment variable `PIPX_GLOBAL_MAN_DIR`. The default python executable used to install a package is {DOC_DEFAULT_PYTHON} and can be overridden by setting the environment variable `PIPX_DEFAULT_PYTHON`. """ ) class LineWrapRawTextHelpFormatter(argparse.RawDescriptionHelpFormatter): def _split_lines(self, text: str, width: int) -> list[str]: text = self._whitespace_matcher.sub(" ", text).strip() return textwrap.wrap(text, width) class InstalledVenvsCompleter: def __init__(self, venv_container: VenvContainer) -> None: self.packages = [str(p.name) for p in sorted(venv_container.iter_venv_dirs())] def use(self, prefix: str, **kwargs: Any) -> list[str]: return [f"{prefix}{x[len(prefix) :]}" for x in self.packages if x.startswith(canonicalize_name(prefix))] def get_pip_args(parsed_args: dict[str, str]) -> list[str]: pip_args: list[str] = [] if parsed_args.get("index_url"): pip_args += ["--index-url", parsed_args["index_url"]] if parsed_args.get("pip_args"): # Stripping the single quote that can be parsed from several shells pip_args_striped = parsed_args["pip_args"].strip("'") pip_args += shlex.split(pip_args_striped, posix=not WINDOWS) # make sure --editable is last because it needs to be right before # package specification if parsed_args.get("editable"): pip_args += ["--editable"] return pip_args def get_runpip_args(pip_args: list[str]) -> list[str]: if len(pip_args) != 1: return pip_args candidate = pip_args[0] # Allow a single quoted string like "install black". if not any(char.isspace() for char in candidate): return pip_args split_args = shlex.split(candidate, posix=not WINDOWS) return split_args or pip_args def get_venv_args(parsed_args: dict[str, str]) -> list[str]: venv_args: list[str] = [] if parsed_args.get("system_site_packages"): venv_args += ["--system-site-packages"] return venv_args def get_backend_arg(parsed_args: dict[str, str]) -> str | None: return parsed_args.get("backend") or None def package_is_url(package: str, raise_error: bool = True) -> bool: url_parse_package = urllib.parse.urlparse(package) if url_parse_package.scheme and url_parse_package.netloc: if not raise_error: return True raise PipxError("Package cannot be a URL. A valid package name should be passed instead.") return False def package_is_path(package: str): if os.path.sep in package: raise PipxError( pipx_wrap( f""" Error: '{package}' looks like a path. Expected the name of an installed package. """ ) ) def _validate_fetch_python() -> None: if _FETCH_PYTHON_INVALID: valid = ", ".join(str(option) for option in FetchPythonOptions) raise PipxError(f"PIPX_FETCH_PYTHON must be unset or one of: {valid}.") if _FETCH_MISSING_PYTHON_RAW is not None: if _FETCH_PYTHON_RAW is not None: raise PipxError("Setting both PIPX_FETCH_MISSING_PYTHON and PIPX_FETCH_PYTHON is invalid.") print( f"{hazard} PIPX_FETCH_MISSING_PYTHON is deprecated; " f'use PIPX_FETCH_PYTHON="{FetchPythonOptions.MISSING}" instead.', file=sys.stderr, ) def run_pipx_command(args: argparse.Namespace) -> ExitCode: if "package" in args: package_is_url(args.package) package_is_path(args.package) if "packages" in args: for package in args.packages: package_is_url(package) package_is_path(package) spec: str | None = getattr(args, "spec", None) if "package" in args and spec is not None: if package_is_url(spec, raise_error=False) and "#egg=" not in spec: spec = f"{spec}#egg={args.package}" python = DEFAULT_PYTHON python_flag_passed = False if "python" in args: python_flag_passed = bool(args.python) try: python = find_python_interpreter(args.python or DEFAULT_PYTHON, fetch_python=args.fetch_python) except InterpreterResolutionError as e: logger.debug("Failed to resolve interpreter:", exc_info=True) print(pipx_wrap(f"{hazard} {e}", subsequent_indent=" " * 4)) return EXIT_CODE_SPECIFIED_PYTHON_EXECUTABLE_NOT_FOUND cli_backend = get_backend_arg(vars(args)) env_backend = env_default_backend() _validate_backend_available(cli_backend, env_backend) ctx = DispatchContext( verbose=bool(args.verbose) if "verbose" in args else False, pip_args=get_pip_args(vars(args)), venv_args=get_venv_args(vars(args)), venv_container=VenvContainer(paths.ctx.venvs), skip_list=[canonicalize_name(x) for x in args.skip] if "skip" in args else [], python=python, python_flag_passed=python_flag_passed, spec=spec, backend=cli_backend, env_backend=env_backend, ) return args.func(args, ctx) def _validate_backend_available(cli_backend: str | None, env_backend: str | None) -> None: """Eagerly surface a missing uv when ``--backend uv`` was passed. Env-source and auto-detect stay silent so diagnostic commands (which never instantiate a backend) keep working, and so unrelated PATH ``uv`` doesn't pay a ``uv --version`` subprocess on every command. """ name, source = resolve_backend_name(cli_value=cli_backend, env_value=env_backend) logger.info(f"Backend resolved to {name} (source: {source})") if name == UV and source == "cli": get_backend(name) @dataclass(frozen=True) class DispatchContext: verbose: bool pip_args: list[str] venv_args: list[str] venv_container: VenvContainer skip_list: list[str] python: str python_flag_passed: bool spec: str | None backend: str | None = None env_backend: str | None = None @property def effective_backend(self) -> str | None: # Use ``backend`` and ``env_backend`` separately when building a # :class:`pipx.venv.Venv`; this only collapses them for code paths # without metadata to consult. return self.backend or self.env_backend def add_pip_venv_args(parser: argparse.ArgumentParser) -> None: parser.add_argument( "--system-site-packages", action="store_true", help="Give the virtual environment access to the system site-packages dir.", ) parser.add_argument("--index-url", "-i", help="Base URL of Python Package Index") parser.add_argument( "--editable", "-e", help="Install a project in editable mode", action="store_true", ) parser.add_argument( "--pip-args", help=( "Arbitrary pip arguments to pass directly to pip install/upgrade commands. " "Under the uv backend a small subset is translated (--index-url, --extra-index-url, " "--find-links, --pre); other flags raise an error so behaviour stays explicit." ), ) def add_backend_arg(parser: argparse.ArgumentParser) -> None: parser.add_argument( "--backend", choices=list(KNOWN_BACKENDS), default=None, help=( "Backend used for creating venvs and managing packages. Defaults to " "PIPX_DEFAULT_BACKEND when set, else 'uv' when uv is available " "(via the `pipx[uv]` extra or on PATH), else 'pip'. Existing venvs " "always honor their recorded backend unless this flag is given." ), ) def add_include_dependencies(parser: argparse.ArgumentParser) -> None: parser.add_argument("--include-deps", help="Include apps of dependent packages", action="store_true") class _DeprecatedFetchMissingPython(argparse.Action): def __init__(self, option_strings: list[str], dest: str, **kwargs: Any) -> None: super().__init__(option_strings, dest, nargs=0, **kwargs) def __call__( self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, values: Any, option_string: str | None = None, ) -> None: if not {"--help", "-h"}.intersection(sys.argv): print( f"{hazard} {option_string} is deprecated; use --fetch-python={FetchPythonOptions.MISSING} instead.", file=sys.stderr, ) setattr(namespace, self.dest, FetchPythonOptions.MISSING) def add_python_options(parser: argparse.ArgumentParser) -> None: parser.add_argument( "--python", help=( "Python to install with. Possible values can be the executable name (python3.11), " "the version of an available system Python or to pass to py launcher (3.11), " f"or the full path to the executable. Requires Python {MINIMUM_PYTHON_VERSION} or above." ), ) fetch_python_group = parser.add_mutually_exclusive_group() fetch_python_group.add_argument( "--fetch-python", type=FetchPythonOptions, choices=list(FetchPythonOptions), default=_FETCH_PYTHON, help=( f"When to fetch a standalone Python build from python-build-standalone. " f"'{FetchPythonOptions.ALWAYS}' always downloads (ignores any system Python); " f"'{FetchPythonOptions.MISSING}' downloads only when the requested version is not found locally; " f"'{FetchPythonOptions.NEVER}' never downloads. " "Defaults to PIPX_FETCH_PYTHON, or 'never'." ), ) fetch_python_group.add_argument( "--fetch-missing-python", dest="fetch_python", action=_DeprecatedFetchMissingPython, help="Deprecated: alias for --fetch-python=missing.", ) def _add_install(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "install", help="Install a package", formatter_class=LineWrapRawTextHelpFormatter, description=INSTALL_DESCRIPTION, parents=[shared_parser], ) p.add_argument("package_spec", help="package name(s) or pip installation spec(s)", nargs="+") add_include_dependencies(p) p.add_argument( "--force", "-f", action="store_true", help="Modify existing virtual environment and files in PIPX_BIN_DIR and PIPX_MAN_DIR", ) p.add_argument( "--suffix", default="", help="Optional suffix for virtual environment and executable names.", ) add_python_options(p) p.add_argument( "--preinstall", action="append", help=( "Optional package to be installed into the Virtual Environment before " "installing the main package. Use this flag multiple times if you want to preinstall multiple packages." ), ) add_pip_venv_args(p) add_backend_arg(p) p.set_defaults(func=_cmd_install) def _cmd_install(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.install( None, None, args.package_spec, paths.ctx.bin_dir, paths.ctx.man_dir, ctx.python, ctx.pip_args, ctx.venv_args, ctx.verbose, force=args.force, reinstall=False, include_dependencies=args.include_deps, preinstall_packages=args.preinstall, suffix=args.suffix, python_flag_passed=ctx.python_flag_passed, backend=ctx.backend, env_backend=ctx.env_backend, ) def _add_install_all(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "install-all", help="Install all packages", formatter_class=LineWrapRawTextHelpFormatter, description="Installs all the packages according to spec metadata file.", parents=[shared_parser], ) p.add_argument("spec_metadata_file", help="Spec metadata file generated from pipx list --json") p.add_argument( "--force", "-f", action="store_true", help="Modify existing virtual environment and files in PIPX_BIN_DIR and PIPX_MAN_DIR", ) add_python_options(p) add_pip_venv_args(p) add_backend_arg(p) p.set_defaults(func=_cmd_install_all) def _cmd_install_all(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.install_all( args.spec_metadata_file, paths.ctx.bin_dir, paths.ctx.man_dir, ctx.python, ctx.pip_args, ctx.venv_args, ctx.verbose, force=args.force, backend=ctx.backend, env_backend=ctx.env_backend, ) def _add_inject(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "inject", help="Install packages into an existing Virtual Environment", description="Installs packages to an existing pipx-managed virtual environment.", parents=[shared_parser], ) p.add_argument( "package", help="Name of the existing pipx-managed Virtual Environment to inject into", ).completer = venv_completer p.add_argument( "dependencies", nargs="*", help="the packages to inject into the Virtual Environment--either package name or pip package spec", ) p.add_argument( "-r", "--requirement", dest="requirements", action="append", default=[], metavar="file", help=( "file containing the packages to inject into the Virtual Environment--" "one package name or pip package spec per line. " "May be specified multiple times." ), ) p.add_argument( "--include-apps", action="store_true", help="Add apps from the injected packages onto your PATH and expose their manual pages", ) p.add_argument( "--include-deps", help="Include apps of dependent packages. Implies --include-apps", action="store_true", ) add_pip_venv_args(p) p.add_argument( "--force", "-f", action="store_true", help="Modify existing virtual environment and files in PIPX_BIN_DIR and PIPX_MAN_DIR", ) p.add_argument( "--with-suffix", action="store_true", help="Add the suffix (if given) of the Virtual Environment to the packages to inject", ) add_backend_arg(p) p.set_defaults(func=_cmd_inject) def _cmd_inject(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.inject( _venv_dir(args, ctx), args.dependencies, args.requirements, ctx.pip_args, verbose=ctx.verbose, include_apps=args.include_apps, include_dependencies=args.include_deps, force=args.force, suffix=args.with_suffix, backend=ctx.backend, env_backend=ctx.env_backend, ) def _add_uninject(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser): p = subparsers.add_parser( "uninject", help="Uninstall injected packages from an existing Virtual Environment", description="Uninstalls injected packages from an existing pipx-managed virtual environment.", parents=[shared_parser], ) p.add_argument( "package", help="Name of the existing pipx-managed Virtual Environment to inject into", ).completer = venv_completer p.add_argument( "dependencies", nargs="+", help="the package names to uninject from the Virtual Environment", ) p.add_argument( "--leave-deps", action="store_true", help="Only uninstall the main injected package but leave its dependencies installed.", ) p.set_defaults(func=_cmd_uninject) def _cmd_uninject(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.uninject( _venv_dir(args, ctx), args.dependencies, local_bin_dir=paths.ctx.bin_dir, local_man_dir=paths.ctx.man_dir, leave_deps=args.leave_deps, verbose=ctx.verbose, ) def _add_pin(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "pin", help="Pin the specified package to prevent it from being upgraded", description="Pin the specified package to prevent it from being upgraded", parents=[shared_parser], ) p.add_argument("package", help="Installed package to pin") p.add_argument( "--injected-only", action="store_true", help=( "Pin injected packages in venv only, so that they will not be upgraded during upgrade operations. " "Note that this should not be passed if you wish to pin both main package and injected packages." ), ) p.add_argument( "--skip", nargs="+", default=[], help="Skip these packages. Implies `--injected-only`.", ) p.set_defaults(func=_cmd_pin) def _cmd_pin(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.pin(_venv_dir(args, ctx), ctx.verbose, ctx.skip_list, args.injected_only) def _add_unpin(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "unpin", help="Unpin the specified package", description="Unpin the specified package and all injected packages in its venv to allow them to be upgraded", parents=[shared_parser], ) p.add_argument("package", help="Installed package to unpin") p.set_defaults(func=_cmd_unpin) def _cmd_unpin(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.unpin(_venv_dir(args, ctx), ctx.verbose) def _add_upgrade(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "upgrade", help="Upgrade a package", description="Upgrade package(s) in pipx-managed Virtual Environment(s) by running 'pip install --upgrade PACKAGE'", parents=[shared_parser], ) p.add_argument("packages", help="package names(s) to upgrade", nargs="+").completer = venv_completer p.add_argument( "--include-injected", action="store_true", help="Also upgrade packages injected into the main app's environment", ) p.add_argument( "--force", "-f", action="store_true", help="Modify existing virtual environment and files in PIPX_BIN_DIR and PIPX_MAN_DIR", ) add_pip_venv_args(p) p.add_argument( "--install", action="store_true", help="Install package spec if missing", ) add_python_options(p) add_backend_arg(p) p.set_defaults(func=_cmd_upgrade) def _cmd_upgrade(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.upgrade( _venv_dirs(args, ctx), ctx.python, ctx.pip_args, ctx.venv_args, ctx.verbose, include_injected=args.include_injected, force=args.force, install=args.install, python_flag_passed=ctx.python_flag_passed, backend=ctx.backend, env_backend=ctx.env_backend, ) def _add_upgrade_all(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "upgrade-all", help="Upgrade all packages. Runs `pip install -U ` for each package.", description="Upgrades all packages within their virtual environments by running 'pip install --upgrade PACKAGE'", parents=[shared_parser], ) p.add_argument( "--include-injected", action="store_true", help="Also upgrade packages injected into the main app's environment", ) p.add_argument("--skip", nargs="+", default=[], help="skip these packages") p.add_argument( "--force", "-f", action="store_true", help="Modify existing virtual environment and files in PIPX_BIN_DIR and PIPX_MAN_DIR", ) add_pip_venv_args(p) add_backend_arg(p) p.set_defaults(func=_cmd_upgrade_all) def _cmd_upgrade_all(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.upgrade_all( ctx.venv_container, ctx.verbose, include_injected=args.include_injected, skip=ctx.skip_list, force=args.force, pip_args=ctx.pip_args, python_flag_passed=ctx.python_flag_passed, backend=ctx.backend, env_backend=ctx.env_backend, ) def _add_upgrade_shared(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "upgrade-shared", help="Upgrade shared libraries.", description="Upgrade shared libraries.", parents=[shared_parser], ) p.add_argument( "--pip-args", help="Arbitrary pip arguments to pass directly to pip install/upgrade commands", ) p.set_defaults(func=_cmd_upgrade_shared) def _cmd_upgrade_shared(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.upgrade_shared(ctx.verbose, ctx.pip_args) def _add_uninstall(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "uninstall", help="Uninstall a package", description="Uninstalls a pipx-managed Virtual Environment by deleting it and any files that point to its apps.", parents=[shared_parser], ) p.add_argument("package").completer = venv_completer p.set_defaults(func=_cmd_uninstall) def _cmd_uninstall(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.uninstall(_venv_dir(args, ctx), paths.ctx.bin_dir, paths.ctx.man_dir, ctx.verbose) def _add_uninstall_all(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "uninstall-all", help="Uninstall all packages", description="Uninstall all pipx-managed packages", parents=[shared_parser], ) p.set_defaults(func=_cmd_uninstall_all) def _cmd_uninstall_all(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.uninstall_all(ctx.venv_container, paths.ctx.bin_dir, paths.ctx.man_dir, ctx.verbose) def _add_reinstall(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "reinstall", formatter_class=LineWrapRawTextHelpFormatter, help="Reinstall a package", description=textwrap.dedent( """ Reinstalls a package. Package is uninstalled, then installed with pipx install PACKAGE with the same options used in the original install of PACKAGE. """ ), parents=[shared_parser], ) p.add_argument("package").completer = venv_completer add_python_options(p) add_backend_arg(p) p.set_defaults(func=_cmd_reinstall) def _cmd_reinstall(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.reinstall( venv_dir=_venv_dir(args, ctx), local_bin_dir=paths.ctx.bin_dir, local_man_dir=paths.ctx.man_dir, python=ctx.python, verbose=ctx.verbose, python_flag_passed=ctx.python_flag_passed, backend=ctx.backend, env_backend=ctx.env_backend, ) def _add_reinstall_all(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "reinstall-all", formatter_class=LineWrapRawTextHelpFormatter, help="Reinstall all packages", description=textwrap.dedent( """ Reinstalls all packages. Packages are uninstalled, then installed with pipx install PACKAGE with the same options used in the original install of PACKAGE. This is useful if you upgraded to a new version of Python and want all your packages to use the latest as well. """ ), parents=[shared_parser], ) add_python_options(p) p.add_argument("--skip", nargs="+", default=[], help="skip these packages") add_backend_arg(p) p.set_defaults(func=_cmd_reinstall_all) def _cmd_reinstall_all(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.reinstall_all( ctx.venv_container, paths.ctx.bin_dir, paths.ctx.man_dir, ctx.python, ctx.verbose, skip=ctx.skip_list, python_flag_passed=ctx.python_flag_passed, backend=ctx.backend, env_backend=ctx.env_backend, ) def _add_list(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "list", help="List installed packages", description="List packages and apps installed with pipx", parents=[shared_parser], ) p.add_argument( "--include-injected", action="store_true", help="Show packages injected into the main app's environment", ) g = p.add_mutually_exclusive_group() g.add_argument("--json", action="store_true", help="Output rich data in json format.") g.add_argument("--short", action="store_true", help="List packages only.") g.add_argument( "--pinned", action="store_true", help="List pinned packages only. Pass --include-injected at the same time to list injected packages that were pinned.", ) g.add_argument("--skip-maintenance", action="store_true", help="(deprecated) No-op") p.set_defaults(func=_cmd_list) def _cmd_list(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.list_packages(ctx.venv_container, args.include_injected, args.json, args.short, args.pinned) def _add_interpreter( subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser ) -> argparse.ArgumentParser: p: argparse.ArgumentParser = subparsers.add_parser( "interpreter", help="Interact with interpreters managed by pipx", description="Interact with interpreters managed by pipx", parents=[shared_parser], ) s = p.add_subparsers( title="subcommands", description="Get help for commands with pipx interpreter COMMAND --help", dest="interpreter_command", ) list_p = s.add_parser("list", help="List available interpreters", description="List available interpreters") prune_p = s.add_parser("prune", help="Prune unused interpreters", description="Prune unused interpreters") upgrade_p = s.add_parser( "upgrade", help="Upgrade installed interpreters to the latest available micro/patch version", description="Upgrade installed interpreters to the latest available micro/patch version", ) list_p.set_defaults(func=_cmd_interpreter_list) prune_p.set_defaults(func=_cmd_interpreter_prune) upgrade_p.set_defaults(func=_cmd_interpreter_upgrade) p.set_defaults(func=_make_print_help(p)) return p def _cmd_interpreter_list(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.list_interpreters(ctx.venv_container) def _cmd_interpreter_prune(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.prune_interpreters(ctx.venv_container) def _cmd_interpreter_upgrade(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.upgrade_interpreters(ctx.venv_container, ctx.verbose) def _add_run(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "run", formatter_class=LineWrapRawTextHelpFormatter, help=( "Download the latest version of a package to a temporary virtual environment, " "then run an app from it. Also compatible with local `__pypackages__` " "directory (experimental)." ), description=textwrap.dedent( f""" Download the latest version of a package to a temporary virtual environment, then run an app from it. The environment will be cached and reused for up to {constants.TEMP_VENV_EXPIRATION_THRESHOLD_DAYS} days. This means subsequent calls to 'run' for the same package will be faster since they can reuse the cached Virtual Environment. In support of PEP 582 'run' will use apps found in a local __pypackages__ directory, if present. Please note that this behavior is experimental, and acts as a companion tool to pythonloc. It may be modified or removed in the future. See https://github.com/cs01/pythonloc. """ ), parents=[shared_parser], ) p.add_argument( "--no-cache", action="store_true", help="Do not reuse cached virtual environment if it exists", ) p.add_argument( "--no-path-check", action="store_true", help="Do not check whether the app is already on PATH", ) p.add_argument( "app_with_args", metavar="app ...", nargs=argparse.REMAINDER, help="app/package name and any arguments to be passed to it", default=[], ) p.add_argument("--path", action="store_true", help="Interpret app name as a local path") p.add_argument( "--pypackages", action="store_true", help="Require app to be run from local __pypackages__ directory", ) p.add_argument( "--with", dest="with_", action="append", default=[], help="Extra dependencies to add to the temporary environment", ) p.add_argument("--spec", help=SPEC_HELP) add_python_options(p) add_pip_venv_args(p) add_backend_arg(p) p.set_defaults(subparser=p, func=_cmd_run) # modify usage text to show required app argument p.usage = re.sub(r"^usage: ", "", p.format_usage()) # add a double-dash to usage text to show requirement before app p.usage = re.sub(r"\.\.\.", "app ...", p.usage) def _cmd_run(args: argparse.Namespace, ctx: DispatchContext) -> NoReturn: commands.run( args.app_with_args[0], ctx.spec, args.with_, args.path, args.app_with_args[1:], ctx.python, ctx.pip_args, ctx.venv_args, args.pypackages, ctx.verbose, not args.no_cache, no_path_check=args.no_path_check, backend=ctx.backend, env_backend=ctx.env_backend, ) def _add_runpip(subparsers, venv_completer: VenvCompleter, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "runpip", help="Run pip in an existing pipx-managed Virtual Environment", description="Run pip in an existing pipx-managed Virtual Environment", parents=[shared_parser], ) p.add_argument( "package", help="Name of the existing pipx-managed Virtual Environment to run pip in", ).completer = venv_completer p.add_argument( "pipargs", nargs=argparse.REMAINDER, default=[], help="Arguments to forward to pip command", ) p.set_defaults(func=_cmd_runpip) def _cmd_runpip(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.run_pip(args.package, _venv_dir(args, ctx), get_runpip_args(args.pipargs), ctx.verbose) def _add_ensurepath(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "ensurepath", help=("Ensure directories necessary for pipx operation are in your PATH environment variable."), description=( "Ensure directory where pipx stores apps is in your " "PATH environment variable. Also if pipx was installed via " "`pip install --user`, ensure pipx itself is in your PATH. " "Note that running this may modify " "your shell's configuration file(s) such as '~/.bashrc'." ), parents=[shared_parser], ) p.add_argument( "--prepend", action="store_true", help=( "Prepend directories to your PATH instead of appending. " "This is useful if you want to prioritize pipx apps over system apps." ), ) p.add_argument( "--force", "-f", action="store_true", help=( "Add text to your shell's config file even if it looks like your " "PATH already contains paths to pipx and pipx-install apps." ), ) p.add_argument( "--all-shells", action="store_true", help=("Add directories to PATH in all shells instead of just the current one."), ) p.add_argument( "--dry-run", action="store_true", help=("Report what would be added to PATH without modifying PATH or any shell configuration file."), ) p.set_defaults(func=_cmd_ensurepath) def _cmd_ensurepath(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: try: return commands.ensure_pipx_paths( prepend=args.prepend, force=args.force, all_shells=args.all_shells, dry_run=args.dry_run ) except Exception as e: logger.debug("Uncaught Exception:", exc_info=True) raise PipxError(str(e), wrap_message=False) from None def _add_environment(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None: p = subparsers.add_parser( "environment", formatter_class=LineWrapRawTextHelpFormatter, help="Print a list of environment variables and paths used by pipx.", description=textwrap.dedent( """ Prints the names and current values of environment variables used by pipx, followed by internal pipx variables which are derived from the environment variables and platform specific default values. Available variables: """ ) + textwrap.fill(", ".join(ENVIRONMENT_VARIABLES), break_long_words=False), parents=[shared_parser], ) p.add_argument( "--value", "-V", choices=ENVIRONMENT_VALUE_CHOICES, metavar="VARIABLE", help="Print the value of the variable.", ) p.set_defaults(func=_cmd_environment) def _cmd_environment(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: return commands.environment(value=args.value) def _venv_dir(args: argparse.Namespace, ctx: DispatchContext) -> Path: venv_dir = ctx.venv_container.get_venv_dir(valid_pypi_name(args.package) or args.package) logger.info(f"Virtual Environment location is {venv_dir}") return venv_dir def _venv_dirs(args: argparse.Namespace, ctx: DispatchContext) -> dict[str, Path]: venv_dirs = {pkg: ctx.venv_container.get_venv_dir(valid_pypi_name(pkg) or pkg) for pkg in args.packages} venv_dirs_msg = "\n".join(f"- {key} : {value}" for key, value in venv_dirs.items()) logger.info(f"Virtual Environment locations are:\n{venv_dirs_msg}") return venv_dirs def _make_print_help( target_parser: argparse.ArgumentParser, ) -> Callable[[argparse.Namespace, DispatchContext], ExitCode]: def _print_help(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: target_parser.print_help() return EXIT_CODE_OK return _print_help def get_command_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentParser]]: venv_container = VenvContainer(paths.ctx.venvs) completer_venvs = InstalledVenvsCompleter(venv_container) shared_parser = argparse.ArgumentParser(add_help=False) shared_parser.add_argument( "--quiet", "-q", action="count", default=0, help=( "Give less output. May be used multiple times corresponding to the" " ERROR and CRITICAL logging levels. The count maxes out at 2." ), ) shared_parser.add_argument( "--verbose", "-v", action="count", default=0, help=( "Give more output. May be used multiple times corresponding to the" " INFO, DEBUG and NOTSET logging levels. The count maxes out at 3." ), ) if not constants.WINDOWS: shared_parser.add_argument( "--global", action="store_true", dest="is_global", help="Perform action globally for all users.", ) parser = argparse.ArgumentParser( prog=prog_name(), formatter_class=LineWrapRawTextHelpFormatter, description=PIPX_DESCRIPTION, ) parser.man_short_description = PIPX_DESCRIPTION.splitlines()[1] # type: ignore[attr-defined] subparsers = parser.add_subparsers(dest="command", description="Get help for commands with pipx COMMAND --help") subparsers_with_subcommands = {} _add_install(subparsers, shared_parser) _add_install_all(subparsers, shared_parser) _add_uninject(subparsers, completer_venvs.use, shared_parser) _add_inject(subparsers, completer_venvs.use, shared_parser) _add_pin(subparsers, completer_venvs.use, shared_parser) _add_unpin(subparsers, completer_venvs.use, shared_parser) _add_upgrade(subparsers, completer_venvs.use, shared_parser) _add_upgrade_all(subparsers, shared_parser) _add_upgrade_shared(subparsers, shared_parser) _add_uninstall(subparsers, completer_venvs.use, shared_parser) _add_uninstall_all(subparsers, shared_parser) _add_reinstall(subparsers, completer_venvs.use, shared_parser) _add_reinstall_all(subparsers, shared_parser) _add_list(subparsers, shared_parser) subparsers_with_subcommands["interpreter"] = _add_interpreter(subparsers, shared_parser) _add_run(subparsers, shared_parser) _add_runpip(subparsers, completer_venvs.use, shared_parser) _add_ensurepath(subparsers, shared_parser) _add_environment(subparsers, shared_parser) parser.add_argument("--version", action="store_true", help="Print version and exit") completions_p = subparsers.add_parser( "completions", help="Print instructions on enabling shell completions for pipx", description="Print instructions on enabling shell completions for pipx", parents=[shared_parser], ) completions_p.set_defaults(func=_cmd_completions) help_p = subparsers.add_parser( "help", help="Show help for pipx or a command", description="Show help for pipx or a command", parents=[shared_parser], ) help_p.set_defaults(func=_make_print_help(parser)) return parser, subparsers_with_subcommands def _cmd_completions(args: argparse.Namespace, ctx: DispatchContext) -> ExitCode: print(constants.completion_instructions) return ExitCode(0) def delete_oldest_logs(file_list: list[Path], keep_number: int) -> None: file_list = sorted(file_list) if len(file_list) > keep_number: for existing_file in file_list[:-keep_number]: try: existing_file.unlink() except FileNotFoundError: pass def _setup_log_file(pipx_log_dir: Path | None = None) -> Path: max_logs = int(os.getenv("PIPX_MAX_LOGS", 10)) pipx_log_dir = pipx_log_dir or paths.ctx.logs # don't use utils.mkdir, to prevent emission of log message pipx_log_dir.mkdir(parents=True, exist_ok=True) delete_oldest_logs(list(pipx_log_dir.glob("cmd_*[0-9].log")), max_logs) delete_oldest_logs(list(pipx_log_dir.glob("cmd_*_pip_errors.log")), max_logs) datetime_str = time.strftime("%Y-%m-%d_%H.%M.%S") log_file = pipx_log_dir / f"cmd_{datetime_str}.log" counter = 1 while log_file.exists() and counter < 10: log_file = pipx_log_dir / f"cmd_{datetime_str}_{counter}.log" counter += 1 log_file.touch() return log_file def setup_log_file() -> Path: try: return _setup_log_file() except PermissionError: return _setup_log_file(platformdirs.user_log_path("pipx")) def setup_logging(verbose: int) -> None: pipx_str = (sys.stdout and sys.stdout.isatty() and bold(green("pipx >"))) or "pipx >" paths.ctx.log_file = setup_log_file() # Determine logging level, a value between 0 and 50 level_number = min(max(0, logging.WARNING - 10 * verbose), 50) level = logging.getLevelName(level_number) # "incremental" is False so previous pytest tests don't accumulate handlers logging_config = { "version": 1, "formatters": { "stream_nonverbose": { "class": "logging.Formatter", "format": "{message}", "style": "{", }, "stream_verbose": { "class": "logging.Formatter", "format": pipx_str + "({funcName}:{lineno}): {message}", "style": "{", }, "file": { "class": "logging.Formatter", "format": "{relativeCreated: >8.1f}ms ({funcName}:{lineno}): {message}", "style": "{", }, }, "handlers": { "stream": { "class": "logging.StreamHandler", "formatter": "stream_verbose" if verbose else "stream_nonverbose", "level": level, }, "file": { "class": "logging.FileHandler", "formatter": "file", "filename": str(paths.ctx.log_file), "encoding": "utf-8", "level": "DEBUG", }, }, "loggers": {"pipx": {"handlers": ["stream", "file"], "level": "DEBUG"}}, "incremental": False, } logging.config.dictConfig(logging_config) def setup(args: argparse.Namespace) -> None: if "version" in args and args.version: print_version() sys.exit(0) if not constants.WINDOWS and getattr(args, "is_global", False): paths.ctx.make_global() verbose = getattr(args, "verbose", 0) - getattr(args, "quiet", 0) setup_logging(verbose) logger.debug(f"{time.strftime('%Y-%m-%d %H:%M:%S')}") logger.debug(f"{' '.join(sys.argv)}") logger.info(f"pipx version is {__version__}") logger.info(f"Default python interpreter is '{DEFAULT_PYTHON}'") mkdir(paths.ctx.venvs) mkdir(paths.ctx.bin_dir) mkdir(paths.ctx.man_dir) mkdir(paths.ctx.venv_cache) mkdir(paths.ctx.standalone_python_cachedir) for cachedir in [ paths.ctx.venv_cache, paths.ctx.standalone_python_cachedir, ]: cachedir_tag = cachedir / "CACHEDIR.TAG" if not cachedir_tag.exists(): logger.debug("Adding CACHEDIR.TAG to cache directory") signature = ( "Signature: 8a477f597d28d172789f06886806bc55\n" "# This file is a cache directory tag created by pipx.\n" "# For information about cache directory tags, see:\n" "# https://bford.info/cachedir/\n" ) with open(cachedir_tag, "w") as file: file.write(signature) rmdir(paths.ctx.trash, False) old_pipx_venv_location = paths.ctx.venvs / "pipx-app" if old_pipx_venv_location.exists(): logger.warning( pipx_wrap( f""" {hazard} A virtual environment for pipx was detected at {old_pipx_venv_location!s}. The 'pipx-app' package has been renamed back to 'pipx' (https://github.com/pypa/pipx/issues/82). """, subsequent_indent=" " * 4, ) ) def check_args(parsed_pipx_args: argparse.Namespace) -> None: if parsed_pipx_args.command == "run": # we manually discard a first -- because using nargs=argparse.REMAINDER # will not do it automatically if parsed_pipx_args.app_with_args and parsed_pipx_args.app_with_args[0] == "--": parsed_pipx_args.app_with_args.pop(0) # since we would like app to be required but not in a separate argparse # add_argument, we implement our own missing required arg error if not parsed_pipx_args.app_with_args: parsed_pipx_args.subparser.error("the following arguments are required: app") def normalize_help_command(args: list[str]) -> list[str]: if args and args[0] == "help": if len(args) == 1: return ["--help"] return args[1:] + ["--help"] return args def _get_subparser(parser: argparse.ArgumentParser, command: str) -> argparse.ArgumentParser: subparsers_action = next(action for action in parser._actions if isinstance(action, argparse._SubParsersAction)) return subparsers_action.choices[command] def parse_pipx_args(parser: argparse.ArgumentParser, args: list[str]) -> argparse.Namespace: args = normalize_help_command(args) if args and args[0] == "inject": parsed_args = _get_subparser(parser, "inject").parse_intermixed_args(args[1:]) parsed_args.command = "inject" return parsed_args return parser.parse_args(args) def cli() -> ExitCode: """Entry point from command line""" try: hide_cursor() parser, _ = get_command_parser() argcomplete.autocomplete(parser, always_complete_options=False) parsed_pipx_args = parse_pipx_args(parser, sys.argv[1:]) _validate_fetch_python() setup(parsed_pipx_args) check_args(parsed_pipx_args) if not parsed_pipx_args.command: parser.print_help() return ExitCode(1) return run_pipx_command(parsed_pipx_args) except PipxError as e: print(str(e), file=sys.stderr) logger.debug(f"PipxError: {e}", exc_info=True) return ExitCode(1) except KeyboardInterrupt: return ExitCode(1) except Exception: logger.debug("Uncaught Exception:", exc_info=True) raise finally: logger.debug("pipx finished.") show_cursor() if __name__ == "__main__": sys.exit(cli()) pipx-1.15.0/src/pipx/package_specifier.py000066400000000000000000000220431521706623000203260ustar00rootroot00000000000000# Valid package specifiers for pipx: # PEP508-compliant # git+ # # # # import logging import re import urllib.parse from dataclasses import dataclass from pathlib import Path from packaging.requirements import InvalidRequirement, Requirement from packaging.specifiers import SpecifierSet from packaging.utils import canonicalize_name from pipx.emojis import hazard from pipx.util import PipxError, pipx_wrap logger = logging.getLogger(__name__) ARCHIVE_EXTENSIONS = (".whl", ".tar.gz", ".zip") @dataclass(frozen=True) class ParsedPackage: valid_pep508: Requirement | None valid_url: str | None valid_local_path: str | None def _split_path_extras(package_spec: str) -> tuple[str, str]: """Returns (path, extras_string)""" package_spec_extras_re = re.search(r"(.+)(\[.+\])", package_spec) if package_spec_extras_re: return (package_spec_extras_re.group(1), package_spec_extras_re.group(2)) else: return (package_spec, "") def _check_package_path(package_path: str) -> tuple[Path, bool]: pkg_path = Path(package_path) pkg_path_exists = pkg_path.exists() return (pkg_path, pkg_path_exists) def _parse_specifier(package_spec: str) -> ParsedPackage: """Parse package_spec as would be given to pipx""" # If package_spec is valid pypi name, pip will always treat it as a # pypi package, not checking for local path. # We replicate pypi precedence here (only non-valid-pypi names # initiate check for local path, e.g. './package-name') valid_pep508 = None valid_url = None valid_local_path = None try: package_req = Requirement(package_spec) except InvalidRequirement: # not a valid PEP508 package specification pass else: # valid PEP508 package specification valid_pep508 = package_req if valid_pep508 and package_req.name.endswith(ARCHIVE_EXTENSIONS): # It might be a local archive (package_path, package_path_exists) = _check_package_path(package_req.name) if package_path_exists: valid_local_path = str(package_path.resolve()) else: raise PipxError(f"{package_path} does not exist") # If this looks like a URL, treat it as such. if not valid_pep508: parsed_url = urllib.parse.urlsplit(package_spec) if parsed_url.scheme and parsed_url.netloc: valid_url = package_spec # Treat the input as a local path if it does not look like a PEP 508 # specifier nor a URL. In this case we want to split out the extra part. if not valid_pep508 and not valid_url: (package_path_str, package_extras_str) = _split_path_extras(package_spec) (package_path, package_path_exists) = _check_package_path(package_path_str) if package_path_exists: valid_local_path = str(package_path.resolve()) + package_extras_str if not valid_pep508 and not valid_url and not valid_local_path: raise PipxError(f"Unable to parse package spec: {package_spec}") if valid_pep508 and valid_local_path: # It is a valid local path without "./" # Use valid_local_path valid_pep508 = None return ParsedPackage( valid_pep508=valid_pep508, valid_url=valid_url, valid_local_path=valid_local_path, ) def package_or_url_from_pep508(requirement: Requirement, remove_version_specifiers: bool = False) -> str: requirement.marker = None requirement.name = canonicalize_name(requirement.name) if remove_version_specifiers: requirement.specifier = SpecifierSet("") return str(requirement) def _parsed_package_to_package_or_url(parsed_package: ParsedPackage, remove_version_specifiers: bool) -> str: if parsed_package.valid_pep508 is not None: if parsed_package.valid_pep508.marker is not None: logger.warning( pipx_wrap( f""" {hazard} Ignoring environment markers ({parsed_package.valid_pep508.marker}) in package specification. Use pipx options to specify this type of information. """, subsequent_indent=" " * 4, ) ) package_or_url = package_or_url_from_pep508( parsed_package.valid_pep508, remove_version_specifiers=remove_version_specifiers, ) elif parsed_package.valid_url is not None: package_or_url = parsed_package.valid_url elif parsed_package.valid_local_path is not None: package_or_url = parsed_package.valid_local_path logger.info(f"cleaned package spec: {package_or_url}") return package_or_url def parse_specifier_for_install(package_spec: str, pip_args: list[str]) -> tuple[str, list[str]]: """Return package_or_url and pip_args suitable for pip install Specifically: * Strip any markers (e.g. python_version > "3.4") * Ensure --editable is removed for any package_spec not a local path * Convert local paths to absolute paths """ parsed_package = _parse_specifier(package_spec) package_or_url = _parsed_package_to_package_or_url(parsed_package, remove_version_specifiers=False) if "--editable" in pip_args and not parsed_package.valid_local_path: logger.warning( pipx_wrap( f""" {hazard} Ignoring --editable install option. pipx disallows it for anything but a local path, to avoid having to create a new src/ directory. """, subsequent_indent=" " * 4, ) ) pip_args.remove("--editable") for index, option in enumerate(pip_args): if not option.startswith(("-c", "--constraint")): continue if option in ("-c", "--constraint"): argument_index = index + 1 if argument_index < len(pip_args) and not urllib.parse.urlsplit(pip_args[argument_index]).scheme: pip_args[argument_index] = str(Path(pip_args[argument_index]).expanduser().resolve()) elif (option_list := option.split("=", maxsplit=1)) and len(option_list) == 2: key, value = option_list if not urllib.parse.urlsplit(value).scheme: pip_args[index] = f"{key}={Path(value).expanduser().resolve()}" break return package_or_url, pip_args def parse_specifier_for_metadata(package_spec: str) -> str: """Return package_or_url suitable for pipx metadata Specifically: * Strip any markers (e.g. python_version > 3.4) * Convert local paths to absolute paths """ parsed_package = _parse_specifier(package_spec) return _parsed_package_to_package_or_url(parsed_package, remove_version_specifiers=False) def parse_specifier_for_upgrade(package_spec: str) -> str: """Return package_or_url suitable for pip upgrade Specifically: * Strip any version specifiers (e.g. package == 1.5.4) * Strip any markers (e.g. python_version > 3.4) * Convert local paths to absolute paths """ parsed_package = _parse_specifier(package_spec) return _parsed_package_to_package_or_url(parsed_package, remove_version_specifiers=True) def get_extras(package_spec: str) -> set[str]: parsed_package = _parse_specifier(package_spec) if parsed_package.valid_pep508 and parsed_package.valid_pep508.extras is not None: return parsed_package.valid_pep508.extras elif parsed_package.valid_local_path: (_, package_extras_str) = _split_path_extras(parsed_package.valid_local_path) return Requirement("notapackage" + package_extras_str).extras return set() def valid_pypi_name(package_spec: str) -> str | None: try: package_req = Requirement(package_spec) except InvalidRequirement: # not a valid PEP508 package specification return None if package_req.url or package_req.name.endswith(ARCHIVE_EXTENSIONS): # package name supplied by user might not match package found in URL, # also if package name ends with archive extension, it might be a local archive file, # so force package name determination the long way return None return canonicalize_name(package_req.name) def fix_package_name(package_or_url: str, package_name: str) -> str: try: package_req = Requirement(package_or_url) except InvalidRequirement: # not a valid PEP508 package specification return package_or_url if package_req.name.endswith(ARCHIVE_EXTENSIONS): return str(package_req) if canonicalize_name(package_req.name) != canonicalize_name(package_name): logger.warning( pipx_wrap( f""" {hazard} Name supplied in package specifier was {package_req.name!r} but package found has name {package_name!r}. Using {package_name!r}. """, subsequent_indent=" " * 4, ) ) package_req.name = package_name return str(package_req) pipx-1.15.0/src/pipx/paths.py000066400000000000000000000154351521706623000160300ustar00rootroot00000000000000import logging import os from pathlib import Path from platformdirs import user_cache_path, user_data_path, user_log_path from pipx.constants import LINUX, WINDOWS from pipx.emojis import hazard, strtobool from pipx.wrap import pipx_wrap if LINUX: DEFAULT_PIPX_HOME = Path(user_data_path("pipx")) FALLBACK_PIPX_HOMES = [Path.home() / ".local/pipx"] elif WINDOWS: DEFAULT_PIPX_HOME = Path.home() / "pipx" FALLBACK_PIPX_HOMES = [Path.home() / ".local/pipx", Path(user_data_path("pipx"))] else: DEFAULT_PIPX_HOME = Path.home() / ".local/pipx" FALLBACK_PIPX_HOMES = [Path(user_data_path("pipx"))] DEFAULT_PIPX_BIN_DIR = Path.home() / ".local/bin" DEFAULT_PIPX_MAN_DIR = Path.home() / ".local/share/man" DEFAULT_PIPX_GLOBAL_HOME = Path("/opt/pipx") DEFAULT_PIPX_GLOBAL_BIN_DIR = Path("/usr/local/bin") DEFAULT_PIPX_GLOBAL_MAN_DIR = Path("/usr/local/share/man") # Overrides for testing OVERRIDE_PIPX_HOME = None OVERRIDE_PIPX_BIN_DIR = None OVERRIDE_PIPX_MAN_DIR = None OVERRIDE_PIPX_SHARED_LIBS = None OVERRIDE_PIPX_GLOBAL_HOME = None OVERRIDE_PIPX_GLOBAL_BIN_DIR = None OVERRIDE_PIPX_GLOBAL_MAN_DIR = None logger = logging.getLogger(__name__) def get_expanded_environ(env_name: str) -> Path | None: val = os.environ.get(env_name) if val is not None: return Path(val).expanduser().resolve() return val class _PathContext: _base_home: Path | None _default_home: Path _base_bin: Path | None _default_bin: Path _base_man: Path | None _default_man: Path _default_log: Path _default_cache: Path _default_trash: Path _base_shared_libs: Path | None _fallback_home: Path | None _home_exists: bool log_file: Path | None = None def __init__(self): self.make_local() @property def venvs(self) -> Path: return self.home / "venvs" @property def logs(self) -> Path: if self._home_exists or not LINUX: return self.home / "logs" return self._default_log @property def trash(self) -> Path: if self._home_exists: return self.home / ".trash" return self._default_trash @property def venv_cache(self) -> Path: if self._home_exists or not LINUX: return self.home / ".cache" return self._default_cache @property def bin_dir(self) -> Path: return (self._base_bin or self._default_bin).resolve() @property def man_dir(self) -> Path: return (self._base_man or self._default_man).resolve() @property def home(self) -> Path: if self._base_home: home = Path(self._base_home) elif self._fallback_home: home = self._fallback_home else: home = self._default_home return home.resolve() @property def shared_libs(self) -> Path: return (self._base_shared_libs or self.home / "shared").resolve() def make_local(self) -> None: self._base_home = OVERRIDE_PIPX_HOME or get_expanded_environ("PIPX_HOME") # type: ignore[redundant-expr] self._default_home = DEFAULT_PIPX_HOME self._base_bin = OVERRIDE_PIPX_BIN_DIR or get_expanded_environ("PIPX_BIN_DIR") # type: ignore[redundant-expr] self._default_bin = DEFAULT_PIPX_BIN_DIR self._base_man = OVERRIDE_PIPX_MAN_DIR or get_expanded_environ("PIPX_MAN_DIR") # type: ignore[redundant-expr] self._default_man = DEFAULT_PIPX_MAN_DIR self._base_shared_libs = OVERRIDE_PIPX_SHARED_LIBS or get_expanded_environ("PIPX_SHARED_LIBS") # type: ignore[redundant-expr] self._default_log = Path(user_log_path("pipx")) self._default_cache = Path(user_cache_path("pipx")) self._default_trash = self._default_home / "trash" self._fallback_home = next(iter([fallback for fallback in FALLBACK_PIPX_HOMES if fallback.exists()]), None) self._home_exists = self._base_home is not None or any(fallback.exists() for fallback in FALLBACK_PIPX_HOMES) def make_global(self) -> None: self._base_home = OVERRIDE_PIPX_GLOBAL_HOME or get_expanded_environ("PIPX_GLOBAL_HOME") # type: ignore[redundant-expr] self._default_home = DEFAULT_PIPX_GLOBAL_HOME self._base_bin = OVERRIDE_PIPX_GLOBAL_BIN_DIR or get_expanded_environ("PIPX_GLOBAL_BIN_DIR") # type: ignore[redundant-expr] self._default_bin = DEFAULT_PIPX_GLOBAL_BIN_DIR self._base_man = OVERRIDE_PIPX_GLOBAL_MAN_DIR or get_expanded_environ("PIPX_GLOBAL_MAN_DIR") # type: ignore[redundant-expr] self._default_man = DEFAULT_PIPX_GLOBAL_MAN_DIR self._default_log = self._default_home / "logs" self._default_cache = self._default_home / ".cache" self._default_trash = self._default_home / "trash" self._base_shared_libs = None self._fallback_home = None self._home_exists = self._base_home is not None @property def standalone_python_cachedir(self) -> Path: return self.home / "py" @property def allow_spaces_in_home_path(self) -> bool: return strtobool(os.getenv("PIPX_HOME_ALLOW_SPACE", "0")) def log_warnings(self): if " " in str(self.home) and not self.allow_spaces_in_home_path: logger.warning( pipx_wrap( ( f"{hazard} Found a space in the pipx home path. We heavily discourage this, due to " "multiple incompatibilities. Please check our docs for more information on this, " "as well as some pointers on how to migrate to a different home path." ), subsequent_indent=" " * 4, ) ) logger.warning( pipx_wrap( (f"{hazard} To see your PIPX_HOME dir: pipx environment --value PIPX_HOME"), subsequent_indent=" " * 4, ) ) logger.warning( pipx_wrap( (f"{hazard} Most likely fix on macOS: mv ~/Library/Application\\ Support/pipx ~/.local/"), subsequent_indent=" " * 4, ) ) fallback_home_exists = self._fallback_home is not None and self._fallback_home.exists() specific_home_exists = self.home != self._fallback_home if fallback_home_exists and specific_home_exists: logger.info( pipx_wrap( ( f"Both a specific pipx home folder ({self.home}) and the fallback " f"pipx home folder ({self._fallback_home}) exist. If you are done migrating from the" "fallback to the new location, it is safe to delete the fallback location." ), subsequent_indent=" " * 4, ) ) ctx = _PathContext() ctx.log_warnings() pipx-1.15.0/src/pipx/pipx_metadata_file.py000066400000000000000000000237631521706623000205330ustar00rootroot00000000000000import json import logging from dataclasses import asdict, dataclass, field from pathlib import Path from typing import Any, Final, TypedDict from pipx.backends._base import KNOWN_BACKENDS from pipx.emojis import hazard from pipx.util import PipxError, pipx_wrap _LOGGER: Final[logging.Logger] = logging.getLogger(__name__) PIPX_INFO_FILENAME = "pipx_metadata.json" class _RawPackageInfo(TypedDict, total=False): """JSON-on-disk shape for :class:`PackageInfo`. ``total=False`` because the legacy-metadata migration runs against dumps from older pipx versions that lacked some keys. """ package: str | None package_or_url: str | None pip_args: list[str] include_dependencies: bool include_apps: bool apps: list[str] app_paths: list[Path] apps_of_dependencies: list[str] app_paths_of_dependencies: dict[str, list[Path]] package_version: str man_pages: list[str] man_paths: list[Path] man_pages_of_dependencies: list[str] man_paths_of_dependencies: dict[str, list[Path]] suffix: str pinned: bool class _RawMetadata(TypedDict, total=False): """JSON-on-disk shape for ``pipx_metadata.json``.""" main_package: _RawPackageInfo python_version: str | None source_interpreter: Path | None venv_args: list[str] injected_packages: dict[str, _RawPackageInfo] backend: str pipx_metadata_version: str pinned: bool class _RawSpecVenvEntry(TypedDict): metadata: _RawMetadata class _RawSpecFile(TypedDict, total=False): # Wire format produced by ``pipx list --json`` and consumed by ``install-all``. venvs: dict[str, _RawSpecVenvEntry] pipx_spec_version: str class JsonEncoderHandlesPath(json.JSONEncoder): def default(self, obj: Any) -> Any: # only handles what json.JSONEncoder doesn't understand by default if isinstance(obj, Path): return {"__type__": "Path", "__Path__": str(obj)} return super().default(obj) def _json_decoder_object_hook(json_dict: dict[str, Any]) -> dict[str, Any] | Path: if json_dict.get("__type__") == "Path" and "__Path__" in json_dict: return Path(json_dict["__Path__"]) return json_dict @dataclass(frozen=True) class PackageInfo: package: str | None package_or_url: str | None pip_args: list[str] include_dependencies: bool include_apps: bool apps: list[str] app_paths: list[Path] apps_of_dependencies: list[str] app_paths_of_dependencies: dict[str, list[Path]] package_version: str man_pages: list[str] = field(default_factory=list) man_paths: list[Path] = field(default_factory=list) man_pages_of_dependencies: list[str] = field(default_factory=list) man_paths_of_dependencies: dict[str, list[Path]] = field(default_factory=dict) suffix: str = "" pinned: bool = False class PipxMetadata: # Only change this if file format changes # V0.1 -> original version # V0.2 -> Improve handling of suffixes # V0.3 -> Add man pages fields # V0.4 -> Add source interpreter # V0.5 -> Add pinned # V0.6 -> Add backend (pip|uv) __METADATA_VERSION__: str = "0.6" def __init__(self, venv_dir: Path, read: bool = True): self.venv_dir = venv_dir # Reasonable defaults for everything except the fields the caller # populates from the install spec (package, package_or_url, python_version). self.main_package = PackageInfo( package=None, package_or_url=None, pip_args=[], include_dependencies=False, include_apps=True, # always True for main_package apps=[], app_paths=[], apps_of_dependencies=[], app_paths_of_dependencies={}, man_pages=[], man_paths=[], man_pages_of_dependencies=[], man_paths_of_dependencies={}, package_version="", ) self.python_version: str | None = None self.source_interpreter: Path | None = None self.venv_args: list[str] = [] self.injected_packages: dict[str, PackageInfo] = {} self.backend: str = "pip" # ``None`` until ``read()`` succeeds; lets callers tell a fresh # instance from one with authoritative on-disk values. self.read_metadata_version: str | None = None if read: self.read() def to_dict(self) -> dict[str, Any]: # Plain dict over _RawMetadata: asdict() returns dict[str, Any] and # consumers serialise straight to JSON. return { "main_package": asdict(self.main_package), "python_version": self.python_version, "source_interpreter": self.source_interpreter, "venv_args": self.venv_args, "injected_packages": {name: asdict(data) for (name, data) in self.injected_packages.items()}, "backend": self.backend, "pipx_metadata_version": self.__METADATA_VERSION__, } def _convert_legacy_metadata(self, metadata_dict: _RawMetadata) -> _RawMetadata: version = metadata_dict["pipx_metadata_version"] if version in (self.__METADATA_VERSION__, "0.5"): pass elif version == "0.4": metadata_dict["pinned"] = False elif version in ("0.2", "0.3"): metadata_dict["source_interpreter"] = None elif version == "0.1": main_package_data = metadata_dict["main_package"] package_name = main_package_data["package"] if package_name is not None and package_name != self.venv_dir.name: # handle older suffixed packages gracefully main_package_data["suffix"] = self.venv_dir.name.replace(package_name, "") metadata_dict["source_interpreter"] = None else: raise PipxError( f""" {self.venv_dir.name}: Unknown metadata version {version}. Perhaps it was installed with a later version of pipx. """ ) # ``backend`` is absent from any pre-0.6 dump; default once here. metadata_dict.setdefault("backend", "pip") return metadata_dict def from_dict(self, input_dict: _RawMetadata) -> None: input_dict = self._convert_legacy_metadata(input_dict) self.main_package = PackageInfo(**input_dict["main_package"]) self.python_version = input_dict.get("python_version") source_interpreter_raw = input_dict.get("source_interpreter") self.source_interpreter = Path(source_interpreter_raw) if source_interpreter_raw else None self.venv_args = input_dict.get("venv_args", []) self.injected_packages = { f"{name}{data.get('suffix', '')}": PackageInfo(**data) for (name, data) in input_dict.get("injected_packages", {}).items() } # Permissive: an unknown ``backend`` (manual edit, post-downgrade # dump from a newer pipx) falls back to pip with a warning so # ``pipx list`` / ``pipx uninstall`` keep working. Strict validation # still fires on write via :func:`pipx.backends.resolve_backend_name`. recorded_backend = input_dict.get("backend") or "pip" if recorded_backend not in KNOWN_BACKENDS: _LOGGER.warning( "%s: ignoring unknown recorded backend %r; treating as 'pip'.", self.venv_dir.name, recorded_backend, ) recorded_backend = "pip" self.backend = recorded_backend self.read_metadata_version = input_dict.get("pipx_metadata_version") def _validate_before_write(self) -> None: if ( self.main_package.package is None or self.main_package.package_or_url is None or not self.main_package.include_apps ): _LOGGER.debug(f"PipxMetadata corrupt:\n{self.to_dict()}") raise PipxError("Internal Error: PipxMetadata is corrupt, cannot write.") def write(self) -> None: self._validate_before_write() try: with open(self.venv_dir / PIPX_INFO_FILENAME, "w", encoding="utf-8") as pipx_metadata_fh: json.dump( self.to_dict(), pipx_metadata_fh, indent=4, sort_keys=True, cls=JsonEncoderHandlesPath, ) except OSError: _LOGGER.warning( pipx_wrap( f""" {hazard} Unable to write {PIPX_INFO_FILENAME} to {self.venv_dir}. This may cause future pipx operations involving {self.venv_dir.name} to fail or behave incorrectly. """, subsequent_indent=" " * 4, ) ) def read(self, verbose: bool = False) -> None: try: with open(self.venv_dir / PIPX_INFO_FILENAME, "rb") as pipx_metadata_fh: payload: _RawMetadata = json.load(pipx_metadata_fh, object_hook=_json_decoder_object_hook) self.from_dict(payload) except OSError: # Reset self if problem reading if verbose: _LOGGER.warning( pipx_wrap( f""" {hazard} Unable to read {PIPX_INFO_FILENAME} in {self.venv_dir}. This may cause this or future pipx operations involving {self.venv_dir.name} to fail or behave incorrectly. """, subsequent_indent=" " * 4, ) ) return def load_spec_file(path: Path) -> "_RawSpecFile": # Round-trips Path values through :class:`JsonEncoderHandlesPath`'s hook. with open(path, encoding="utf-8") as handle: payload: _RawSpecFile = json.load(handle, object_hook=_json_decoder_object_hook) return payload __all__ = [ "PIPX_INFO_FILENAME", "JsonEncoderHandlesPath", "PackageInfo", "PipxMetadata", "load_spec_file", ] pipx-1.15.0/src/pipx/shared_libs.py000066400000000000000000000167571521706623000172000ustar00rootroot00000000000000import datetime import logging import os import time from configparser import ConfigParser from contextlib import suppress from pathlib import Path from packaging.requirements import InvalidRequirement, Requirement from packaging.specifiers import SpecifierSet from pipx import paths from pipx.animate import animate from pipx.constants import WINDOWS from pipx.emojis import strtobool from pipx.interpreter import DEFAULT_PYTHON from pipx.util import ( get_site_packages, get_venv_paths, run_subprocess, subprocess_post_check, ) logger = logging.getLogger(__name__) SHARED_LIBS_MAX_AGE_SEC = datetime.timedelta(days=30).total_seconds() DISABLE_SHARED_LIBS_AUTO_UPGRADE = "PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE" def shared_libs_auto_upgrade_disabled() -> bool: return strtobool(os.getenv(DISABLE_SHARED_LIBS_AUTO_UPGRADE, "0")) def _venv_python_is_valid(python_path: Path) -> bool: """Check if a venv's Python is valid and its underlying interpreter exists. On Windows, a venv's python.exe is a wrapper that uses pyvenv.cfg to find the actual Python installation. If the original Python is uninstalled, the wrapper exists but cannot execute. This function checks that the underlying interpreter referenced in pyvenv.cfg still exists. """ if not WINDOWS: return True pyvenv_cfg = python_path.parent.parent / "pyvenv.cfg" if not pyvenv_cfg.is_file(): return True try: config = ConfigParser() with open(pyvenv_cfg, encoding="utf-8") as f: # ConfigParser needs a section header, pyvenv.cfg doesn't have one config.read_string("[DEFAULT]\n" + f.read()) home = config.get("DEFAULT", "home", fallback=None) if home: # The home path points to the directory containing the original python.exe original_python = Path(home) / "python.exe" if not original_python.is_file(): logger.info(f"Shared libs venv references a missing Python interpreter: {original_python}") return False except Exception: # If we can't read pyvenv.cfg, assume the venv is valid pass return True class _SharedLibs: def __init__(self) -> None: self._site_packages: dict[Path, Path] = {} self.has_been_updated_this_run = False self.has_been_logged_this_run = False @property def root(self) -> Path: return paths.ctx.shared_libs @property def bin_path(self) -> Path: bin_path, _, _ = get_venv_paths(self.root) return bin_path @property def python_path(self) -> Path: _, python_path, _ = get_venv_paths(self.root) return python_path @property def man_path(self) -> Path: _, _, man_path = get_venv_paths(self.root) return man_path @property def pip_path(self) -> Path: return self.bin_path / ("pip" if not WINDOWS else "pip.exe") @property def site_packages(self) -> Path: if self.python_path not in self._site_packages: self._site_packages[self.python_path] = get_site_packages(self.python_path) return self._site_packages[self.python_path] def create(self, pip_args: list[str], verbose: bool = False) -> None: if not self.is_valid: with animate("creating shared libraries", not verbose): create_process = run_subprocess( [DEFAULT_PYTHON, "-m", "venv", "--clear", self.root], run_dir=str(self.root) ) subprocess_post_check(create_process) # ignore installed packages to ensure no unexpected patches from the OS vendor # are used pip_args = pip_args or [] pip_args.append("--force-reinstall") self.upgrade(pip_args=pip_args, verbose=verbose, raises=True) # Remove setuptools from shared libs to prevent it from leaking into # app venvs via the .pth file. On Python < 3.12, venv bundles setuptools # via ensurepip, but a 3.10 setuptools breaks when imported under 3.12+ # because distutils was removed from the stdlib. run_subprocess( [self.python_path, "-m", "pip", "--no-input", "uninstall", "-y", "setuptools"], capture_stderr=False, ) @property def is_valid(self) -> bool: if self.python_path.is_file(): # On Windows, check that the venv's underlying Python still exists if not _venv_python_is_valid(self.python_path): return False check_pip = "import importlib.util; print(importlib.util.find_spec('pip'))" out = run_subprocess( [self.python_path, "-c", check_pip], capture_stderr=False, log_cmd_str="", ).stdout.strip() return self.pip_path.is_file() and out != "None" else: return False @property def needs_upgrade(self) -> bool: if self.has_been_updated_this_run: return False if not self.pip_path.is_file(): return True now = time.time() time_since_last_update_sec = now - self.pip_path.stat().st_mtime if not self.has_been_logged_this_run: logger.info( f"Time since last upgrade of shared libs, in seconds: {time_since_last_update_sec:.0f}. " f"Upgrade will be run by pipx if greater than {SHARED_LIBS_MAX_AGE_SEC:.0f}." ) self.has_been_logged_this_run = True return time_since_last_update_sec > SHARED_LIBS_MAX_AGE_SEC def upgrade(self, *, pip_args: list[str], verbose: bool = False, raises: bool = False) -> None: if not self.is_valid: self.create(verbose=verbose, pip_args=pip_args) return # Don't try to upgrade multiple times per run if self.has_been_updated_this_run: logger.info(f"Already upgraded libraries in {self.root}") return if pip_args is None: pip_args = [] # type: ignore[unreachable] logger.info(f"Upgrading shared libraries in {self.root}") ignored_args = ["--editable"] _pip_args = [arg for arg in pip_args if arg not in ignored_args] if not verbose: _pip_args.append("-q") user_pip_req = None for arg in _pip_args: with suppress(InvalidRequirement): if (req := Requirement(arg)).name == "pip": user_pip_req = req break add_default = not user_pip_req or not (user_pip_req.specifier & SpecifierSet(">=23.1")) install_args = [*_pip_args, "pip >= 23.1"] if add_default else _pip_args try: with animate("upgrading shared libraries", not verbose): upgrade_process = run_subprocess( [ self.python_path, "-m", "pip", "--no-input", "--disable-pip-version-check", "install", "--upgrade", *install_args, ] ) subprocess_post_check(upgrade_process) self.has_been_updated_this_run = True self.pip_path.touch() except Exception: logger.error("Failed to upgrade shared libraries", exc_info=not raises) if raises: raise shared_libs = _SharedLibs() pipx-1.15.0/src/pipx/standalone_python.py000066400000000000000000000207111521706623000204330ustar00rootroot00000000000000import datetime import hashlib import json import logging import platform import re import shutil import tarfile import tempfile import urllib.error from functools import partial from pathlib import Path from typing import Any from urllib.request import urlopen from pipx import constants, paths from pipx.animate import animate from pipx.util import PipxError logger = logging.getLogger(__name__) # Much of the code in this module is adapted with extreme gratitude from # https://github.com/tusharsadhwani/yen/blob/main/src/yen/github.py MACHINE_SUFFIX: dict[str, dict[str, Any]] = { "Darwin": { "arm64": ["aarch64-apple-darwin-install_only.tar.gz"], "x86_64": ["x86_64-apple-darwin-install_only.tar.gz"], }, "Linux": { "aarch64": { "glibc": ["aarch64-unknown-linux-gnu-install_only.tar.gz"], # musl doesn't exist }, "x86_64": { "glibc": [ "x86_64_v3-unknown-linux-gnu-install_only.tar.gz", "x86_64-unknown-linux-gnu-install_only.tar.gz", ], "musl": ["x86_64_v3-unknown-linux-musl-install_only.tar.gz"], }, }, "Windows": {"AMD64": ["x86_64-pc-windows-msvc-install_only.tar.gz"]}, } GITHUB_API_URL = "https://api.github.com/repos/astral-sh/python-build-standalone/releases/latest" PYTHON_VERSION_REGEX = re.compile(r"cpython-(\d+\.\d+\.\d+)") def download_python_build_standalone(python_version: str, override: bool = False): """When all other python executable resolutions have failed, attempt to download and use an appropriate python build from https://github.com/astral-sh/python-build-standalone and unpack it into the pipx shared directory.""" # python_version can be a bare version number like "3.10" or a "binary name" like python3.10 # we'll convert it to a bare version number python_version = re.sub(r"[c]?python", "", python_version) install_dir = paths.ctx.standalone_python_cachedir / python_version installed_python = install_dir / "python.exe" if constants.WINDOWS else install_dir / "bin" / "python3" if override: if install_dir.exists(): shutil.rmtree(install_dir) else: if installed_python.exists(): return str(installed_python) if install_dir.exists(): logger.warning(f"A previous attempt to install python {python_version} failed. Retrying.") shutil.rmtree(install_dir) full_version, (download_link, digest) = resolve_python_version(python_version) with tempfile.TemporaryDirectory() as tempdir: archive = Path(tempdir) / f"python-{full_version}.tar.gz" download_dir = Path(tempdir) / "download" # download the python build gz _download(full_version, download_link, archive) # unpack the python build _unpack(full_version, download_link, archive, download_dir, digest) # the python installation we want is nested in the tarball # under a directory named 'python'. We move it to the install # directory extracted_dir = download_dir / "python" shutil.move(extracted_dir, install_dir) return str(installed_python) def _download(full_version: str, download_link: str, archive: Path): with animate(f"Downloading python {full_version} build", True): try: # python standalone builds are typically ~32MB in size. to avoid # ballooning memory usage, we read the file in chunks with urlopen(download_link) as response, open(archive, "wb") as file_handle: for data in iter(partial(response.read, 32768), b""): file_handle.write(data) except urllib.error.URLError as e: raise PipxError(f"Unable to download python {full_version} build.") from e def _unpack(full_version, download_link, archive: Path, download_dir: Path, expected_checksum: str): with animate(f"Unpacking python {full_version} build", True): # Calculate checksum efficiently sha256_hash = hashlib.sha256() with open(archive, "rb") as python_zip: # Read in chunks to avoid loading the whole file into memory for chunk in iter(lambda: python_zip.read(32768), b""): sha256_hash.update(chunk) checksum = "sha256:" + sha256_hash.hexdigest() # Validate checksum if checksum != expected_checksum: raise PipxError( f"Checksum mismatch for python {full_version} build. Expected {expected_checksum}, got {checksum}." ) with tarfile.open(archive, mode="r:gz") as tar: tar.extractall(download_dir) def _is_valid_python_index(index: Any) -> bool: if not isinstance(index, dict): return False fetched = index.get("fetched") releases = index.get("releases") if not isinstance(fetched, int | float) or not isinstance(releases, list): return False return all( isinstance(release, (list, tuple)) and len(release) == 2 and all(isinstance(value, str) for value in release) for release in releases ) def get_or_update_index(use_cache: bool = True): """Get or update the index of available python builds from the python-build-standalone repository.""" index_file = paths.ctx.standalone_python_cachedir / "index.json" if use_cache and index_file.exists(): index = json.loads(index_file.read_text()) # Refresh legacy URL-only indexes, and update current indexes after 30 days. if _is_valid_python_index(index): fetched = datetime.datetime.fromtimestamp(index["fetched"]) if datetime.datetime.now() - fetched > datetime.timedelta(days=30): index = {} else: index = {} else: index = {} if not index: releases = get_latest_python_releases() index = {"fetched": datetime.datetime.now().timestamp(), "releases": releases} # update index index_file.write_text(json.dumps(index)) return index def get_latest_python_releases() -> list[tuple[str, str]]: """Returns the list of python download links from the latest github release.""" try: with urlopen(GITHUB_API_URL) as response: release_data = json.load(response) except urllib.error.URLError as e: # raise raise PipxError(f"Unable to fetch python-build-standalone release data (from {GITHUB_API_URL}).") from e return [(asset["browser_download_url"], asset["digest"]) for asset in release_data["assets"]] def list_pythons(use_cache: bool = True) -> dict[str, tuple[str, str]]: """Returns available python versions for your machine and their download links.""" system, machine = platform.system(), platform.machine() download_link_suffixes = MACHINE_SUFFIX[system][machine] # linux suffixes are nested under glibc or musl builds if system == "Linux": # fallback to musl if libc version is not found libc_version = platform.libc_ver()[0] or "musl" download_link_suffixes = download_link_suffixes[libc_version] python_releases = get_or_update_index(use_cache)["releases"] available_python_links = [ (link, digest) # Suffixes are in order of preference. for download_link_suffix in download_link_suffixes for link, digest in python_releases if link.endswith(download_link_suffix) ] python_versions: dict[str, tuple[str, str]] = {} for link, digest in available_python_links: match = PYTHON_VERSION_REGEX.search(link) assert match is not None python_version = match[1] # Don't override already found versions, they are in order of preference if python_version in python_versions: continue python_versions[python_version] = link, digest return { version: python_versions[version] for version in sorted( python_versions, # sort by semver key=lambda version: [int(k) for k in version.split(".")], reverse=True, ) } def resolve_python_version(requested_version: str): pythons = list_pythons() requested_release = requested_version.split(".") for full_version, download_link in pythons.items(): standalone_release = full_version.split(".") if requested_release == standalone_release[: len(requested_release)]: return full_version, download_link raise PipxError(f"Unable to acquire a standalone python build matching {requested_version}.") pipx-1.15.0/src/pipx/util.py000066400000000000000000000366361521706623000156740ustar00rootroot00000000000000import logging import os import random import re import shutil import string import subprocess import sys from collections.abc import Sequence from dataclasses import dataclass from pathlib import Path from re import Pattern from typing import ( Any, Final, NoReturn, ) from pipx import paths from pipx.animate import show_cursor from pipx.constants import MINGW, WINDOWS from pipx.wrap import pipx_wrap _LOGGER: Final[logging.Logger] = logging.getLogger(__name__) class PipxError(Exception): def __init__(self, message: str, wrap_message: bool = True): if wrap_message: super().__init__(pipx_wrap(message)) else: super().__init__(message) @dataclass(frozen=True) class RelevantSearch: pattern: Pattern[str] category: str def _get_trash_file(path: Path) -> Path: if not paths.ctx.trash.is_dir(): paths.ctx.trash.mkdir() prefix = "".join(random.choices(string.ascii_lowercase, k=8)) return paths.ctx.trash / f"{prefix}.{path.name}" def rmdir(path: Path, safe_rm: bool = True) -> None: if not path.is_dir(): return _LOGGER.info(f"removing directory {path}") # Windows doesn't let us delete or overwrite files that are being run # But it does let us rename/move it. To get around this issue, we can move # the file to a temporary folder (to be deleted at a later time) # So, if safe_rm is True, we ignore any errors and move the file to the trash with below code shutil.rmtree(path, ignore_errors=safe_rm) # move it to be deleted later if it still exists if path.is_dir(): if safe_rm: _LOGGER.warning(f"Failed to delete {path}. Will move it to a temp folder to delete later.") path.rename(_get_trash_file(path)) else: _LOGGER.warning(f"Failed to delete {path}. You may need to delete it manually.") def mkdir(path: Path) -> None: if path.is_dir(): return _LOGGER.info(f"creating directory {path}") path.mkdir(parents=True, exist_ok=True) def safe_unlink(file: Path) -> None: # Windows doesn't let us delete or overwrite files that are being run # But it does let us rename/move it. To get around this issue, we can move # the file to a temporary folder (to be deleted at a later time) if not file.is_file(): return try: file.unlink() except PermissionError: file.rename(_get_trash_file(file)) def get_pypackage_bin_path(binary_name: str) -> Path: return ( Path("__pypackages__") / (str(sys.version_info.major) + "." + str(sys.version_info.minor)) / "lib" / "bin" / binary_name ) def run_pypackage_bin(bin_path: Path, args: list[str]) -> NoReturn: exec_app( [str(bin_path.resolve())] + args, extra_python_paths=[".", str(bin_path.parent.parent)], ) if WINDOWS: def get_venv_paths(root: Path) -> tuple[Path, Path, Path]: # Make sure to use the real root path. This matters especially on Windows when using the packaged app # (Microsoft Store) version of Python, which uses path redirection for sandboxing. # See https://github.com/pypa/pipx/issues/1164 root = root.resolve() bin_path = root / "Scripts" if not MINGW else root / "bin" python_path = bin_path / "python.exe" man_path = root / "share" / "man" return bin_path, python_path, man_path else: def get_venv_paths(root: Path) -> tuple[Path, Path, Path]: bin_path = root / "bin" python_path = bin_path / "python" man_path = root / "share" / "man" return bin_path, python_path, man_path def get_site_packages(python: Path) -> Path: output = run_subprocess( [python, "-c", "import sysconfig; print(sysconfig.get_path('purelib'))"], capture_stderr=False, ).stdout path = Path(output.strip()) path.mkdir(parents=True, exist_ok=True) return path def _fix_subprocess_env(env: dict[str, str]) -> dict[str, str]: # Remove PYTHONPATH because some platforms (macOS with Homebrew) add pipx # directories to it, and can make it appear to venvs as though pipx # dependencies are in the venv path (#233) # Remove __PYVENV_LAUNCHER__ because it can cause the wrong python binary # to be used (#334) env_blocklist = ["PYTHONPATH", "__PYVENV_LAUNCHER__"] for env_to_remove in env_blocklist: env.pop(env_to_remove, None) env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1" # Make sure that Python writes output in UTF-8 env["PYTHONIOENCODING"] = "utf-8" env["PYTHONLEGACYWINDOWSSTDIO"] = "utf-8" # Make sure we install packages to venv, not to userbase or a custom target dir env["PIP_USER"] = "0" env.pop("PIP_TARGET", None) env.setdefault("PIP_KEYRING_PROVIDER", "subprocess") return env def run_subprocess( cmd: Sequence[str | Path], capture_stdout: bool = True, capture_stderr: bool = True, log_cmd_str: str | None = None, log_stdout: bool = True, log_stderr: bool = True, run_dir: str | None = None, env_overrides: dict[str, str | None] | None = None, ) -> "subprocess.CompletedProcess[str]": """Run a command as a subprocess, capturing stderr and stdout. ``env_overrides`` keys map to a string (set/replace) or ``None`` (delete). """ env = dict(os.environ) env = _fix_subprocess_env(env) for key, value in (env_overrides or {}).items(): if value is None: env.pop(key, None) else: env[key] = value if log_cmd_str is None: log_cmd_str = " ".join(str(c) for c in cmd) _LOGGER.info(f"running {log_cmd_str}") if run_dir: os.makedirs(run_dir, exist_ok=True) # windows cannot take Path objects, only strings cmd_str_list = [str(c) for c in cmd] # Set PYTHONSAFEPATH to prevent adding CWD to sys.path in subprocess Python commands # This prevents local files from shadowing standard library modules (security issue #1575) # Supported in Python 3.11+; for earlier versions, the environment variable is ignored # but those versions are less commonly used and the risk is lower if len(cmd_str_list) > 0 and "python" in Path(cmd_str_list[0]).name.lower(): env.setdefault("PYTHONSAFEPATH", "1") completed_process = subprocess.run( cmd_str_list, env=env, stdout=subprocess.PIPE if capture_stdout else None, stderr=subprocess.PIPE if capture_stderr else None, encoding="utf-8", text=True, check=False, cwd=run_dir, ) if capture_stdout and log_stdout: _LOGGER.debug(f"stdout: {completed_process.stdout}".rstrip()) if capture_stderr and log_stderr: _LOGGER.debug(f"stderr: {completed_process.stderr}".rstrip()) _LOGGER.debug(f"returncode: {completed_process.returncode}") return completed_process def subprocess_post_check(completed_process: "subprocess.CompletedProcess[str]", raise_error: bool = True) -> None: if completed_process.returncode: if completed_process.stdout is not None: print(completed_process.stdout, file=sys.stdout, end="") if completed_process.stderr is not None: print(completed_process.stderr, file=sys.stderr, end="") if raise_error: raise PipxError(f"{' '.join([str(x) for x in completed_process.args])!r} failed") else: _LOGGER.info(f"{' '.join(completed_process.args)!r} failed") def dedup_ordered(input_list: list[tuple[str, Any]]) -> list[tuple[str, Any]]: output_list = [] seen = set() for x in input_list: if x[0] not in seen: output_list.append(x) seen.add(x[0]) return output_list def analyze_pip_output(pip_stdout: str, pip_stderr: str) -> None: r"""Extract useful errors from pip output of failed install Print the module that failed to build Print some of the most relevant errors from the pip output Example pip stderr line for each "relevant" type: not_found Package cairo was not found in the pkg-config search path. src/common.h:34:10: fatal error: 'stdio.h' file not found The headers or library files could not be found for zlib, no_such unable to execute 'gcc': No such file or directory build\test1.c(2): fatal error C1083: Cannot open include file: 'cpuid.h': No such file ... exception_error Exception: Unable to find OpenSSL >= 1.0 headers. (Looked here: ... fatal_error LINK : fatal error LNK1104: cannot open file 'kernel32.lib' conflict_ ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/... error_ error: can't copy 'lib\ansible\module_utils\ansible_release.py': doesn't exist ... build\test1.c(4): error C2146: syntax error: missing ';' before identifier 'x' """ max_relevant_errors = 10 failed_build_stdout: list[str] = [] last_collecting_dep: str | None = None # for any useful information in stdout, `pip install` must be run without # the -q option for line in pip_stdout.split("\n"): failed_match = re.search(r"Failed to build\s+(\S.+)$", line) collecting_match = re.search(r"^\s*Collecting\s+(\S+)", line) if failed_match: failed_build_stdout = failed_match.group(1).strip().split() if collecting_match: last_collecting_dep = collecting_match.group(1) # In order of most useful to least useful relevant_searches = [ RelevantSearch(re.compile(r"not (?:be )?found", re.I), "not_found"), RelevantSearch(re.compile(r"no such", re.I), "no_such"), RelevantSearch(re.compile(r"(Exception|Error):\s*\S+"), "exception_error"), RelevantSearch(re.compile(r"fatal error", re.I), "fatal_error"), RelevantSearch(re.compile(r"conflict", re.I), "conflict_"), RelevantSearch( re.compile( r"error:" r"(?!.+Command errored out)" r"(?!.+failed building wheel for)" r"(?!.+could not build wheels? for)" r"(?!.+failed to build one or more wheels)" r".+[^:]$", re.I, ), "error_", ), ] failed_stderr_patt = re.compile(r"Failed to build\s+(?!one or more packages)(\S+)") relevants_saved = [] failed_build_stderr = set() for line in pip_stderr.split("\n"): failed_build_match = failed_stderr_patt.search(line) if failed_build_match: failed_build_stderr.add(failed_build_match.group(1)) for relevant_search in relevant_searches: if relevant_search.pattern.search(line): relevants_saved.append((line.strip(), relevant_search.category)) break if failed_build_stdout: failed_to_build_str = "\n ".join(failed_build_stdout) plural_str = "s" if len(failed_build_stdout) > 1 else "" print("", file=sys.stderr) _LOGGER.error(f"pip failed to build package{plural_str}:\n {failed_to_build_str}") elif failed_build_stderr: failed_to_build_str = "\n ".join(failed_build_stderr) plural_str = "s" if len(failed_build_stderr) > 1 else "" print("", file=sys.stderr) _LOGGER.error(f"pip seemed to fail to build package{plural_str}:\n {failed_to_build_str}") elif last_collecting_dep is not None: print("", file=sys.stderr) _LOGGER.error(f"pip seemed to fail to build package:\n {last_collecting_dep}") relevants_saved = dedup_ordered(relevants_saved) if relevants_saved: print("\nSome possibly relevant errors from pip install:", file=sys.stderr) print_categories = [x.category for x in relevant_searches] relevants_saved_filtered = relevants_saved.copy() while (len(print_categories) > 1) and (len(relevants_saved_filtered) > max_relevant_errors): print_categories.pop(-1) relevants_saved_filtered = [x for x in relevants_saved if x[1] in print_categories] for relevant_saved in relevants_saved_filtered: print(f" {relevant_saved[0]}", file=sys.stderr) def subprocess_post_check_handle_pip_error( completed_process: "subprocess.CompletedProcess[str]", tool_name: str = "pip", ) -> None: """Persist subprocess output; run pip-specific heuristics for the pip path. ``tool_name="pip"`` keeps the historical wording that log-scrapers / tests built around. uv writes a uv-labelled log and skips the pip output analyser, whose regexes don't match uv's error format. """ if not completed_process.returncode: return _LOGGER.info(f"{' '.join(completed_process.args)!r} failed") if paths.ctx.log_file is None: raise PipxError("Pipx internal error: No log_file present.") error_file = paths.ctx.log_file.parent / f"{paths.ctx.log_file.stem}_{tool_name}_errors.log" upper_label = tool_name.upper() with error_file.open("a", encoding="utf-8") as error_fh: print(f"{upper_label} STDOUT", file=error_fh) print("----------", file=error_fh) if completed_process.stdout is not None: print(completed_process.stdout, file=error_fh, end="") print(f"\n{upper_label} STDERR", file=error_fh) print("----------", file=error_fh) if completed_process.stderr is not None: print(completed_process.stderr, file=error_fh, end="") _LOGGER.error( f"Fatal error from {tool_name} prevented installation. Full {tool_name} output in file:\n {error_file}" ) if tool_name == "pip": analyze_pip_output(completed_process.stdout, completed_process.stderr) def exec_app( cmd: Sequence[str | Path], env: dict[str, str] | None = None, extra_python_paths: list[str] | None = None, ) -> NoReturn: """Run command, do not return POSIX: replace current process with command using os.exec*() Windows: Use subprocess and sys.exit() to run command """ if env is None: env = dict(os.environ) env = _fix_subprocess_env(env) if extra_python_paths is not None: env["PYTHONPATH"] = os.path.pathsep.join( extra_python_paths + (os.getenv("PYTHONPATH", "").split(os.path.pathsep) if os.getenv("PYTHONPATH") else []) ) # make sure we show cursor again before handing over control show_cursor() _LOGGER.info(f"exec_app: {' '.join(str(c) for c in cmd)}") if WINDOWS: sys.exit( subprocess.run( cmd, env=env, stdout=None, stderr=None, encoding="utf-8", text=True, check=False, ).returncode ) else: os.execvpe(str(cmd[0]), [str(x) for x in cmd], env) def full_package_description(package_name: str, package_spec: str) -> str: if package_name == package_spec: return package_name else: return f"{package_name} from spec {package_spec!r}" def is_paths_relative(path: Path, parent: Path): return path.is_relative_to(parent) __all__ = [ "PipxError", "RelevantSearch", "analyze_pip_output", "dedup_ordered", "exec_app", "full_package_description", "get_pypackage_bin_path", "get_site_packages", "get_venv_paths", "is_paths_relative", "mkdir", "pipx_wrap", "rmdir", "run_pypackage_bin", "run_subprocess", "safe_unlink", "subprocess_post_check", "subprocess_post_check_handle_pip_error", ] pipx-1.15.0/src/pipx/venv.py000066400000000000000000000547531521706623000156750ustar00rootroot00000000000000import logging import shutil import time from collections.abc import Generator from pathlib import Path from typing import TYPE_CHECKING, Final, NoReturn if TYPE_CHECKING: from subprocess import CompletedProcess try: from importlib.metadata import Distribution, EntryPoint except ImportError: from importlib_metadata import Distribution, EntryPoint # type: ignore[import-not-found,no-redef] from packaging.utils import canonicalize_name from pipx.animate import animate from pipx.backends import Backend, assert_not_pip_under_uv, env_default_backend, get_backend, resolve_backend_name from pipx.constants import PIPX_SHARED_PTH, ExitCode from pipx.emojis import hazard from pipx.interpreter import DEFAULT_PYTHON from pipx.package_specifier import ( fix_package_name, get_extras, parse_specifier_for_install, parse_specifier_for_metadata, ) from pipx.pipx_metadata_file import PackageInfo, PipxMetadata from pipx.shared_libs import ( DISABLE_SHARED_LIBS_AUTO_UPGRADE, shared_libs, shared_libs_auto_upgrade_disabled, ) from pipx.util import ( PipxError, exec_app, full_package_description, get_site_packages, get_venv_paths, pipx_wrap, rmdir, run_subprocess, subprocess_post_check, ) from pipx.venv_inspect import VenvMetadata, inspect_venv _LOGGER: Final[logging.Logger] = logging.getLogger(__name__) # Keyed on full path so global vs user-local venvs with the same name don't # collide; deduped per-session because ``upgrade-all --backend uv`` against # many pip-backed venvs would otherwise repeat the multi-line warning per venv. _BACKEND_OVERRIDE_WARNED: Final[set[str]] = set() def reset_backend_override_warnings() -> None: # Test-only: production callers never reset this. _BACKEND_OVERRIDE_WARNED.clear() def _resolve_backend_for_venv( *, root: Path, existing: bool, metadata_backend: str | None, cli_backend: str | None, env_backend: str | None, ) -> tuple[str, str]: """Apply cli > metadata > env > auto precedence, warning on conflict. Pulled out of ``Venv.__init__`` so the precedence rule is unit-testable without instantiating a real venv. """ # Existing venvs lock to their recorded backend: ``uv pip install`` against # a pip-backed venv leaves it in an inconsistent state. ``pipx reinstall # NAME --backend X`` is the supported flip. if existing and cli_backend is not None and cli_backend != metadata_backend: warning_key = str(root) if warning_key not in _BACKEND_OVERRIDE_WARNED: _BACKEND_OVERRIDE_WARNED.add(warning_key) _LOGGER.warning( pipx_wrap( f""" {hazard} Ignoring --backend={cli_backend} for existing venv {root.name!r} (recorded backend is {metadata_backend!r}). Run `pipx reinstall {root.name} --backend {cli_backend}` to flip the venv. """, subsequent_indent=" " * 4, ) ) cli_backend = None # Direct ``Venv(...)`` callers (including unit tests) won't have threaded # ``env_backend`` through; honor ``PIPX_DEFAULT_BACKEND`` so auto-detect # doesn't fire when the operator already opted in via env. if env_backend is None: env_backend = env_default_backend() return resolve_backend_name(cli_value=cli_backend, env_value=env_backend, metadata_value=metadata_backend) class VenvContainer: """A collection of venvs managed by pipx.""" def __init__(self, root: Path): self._root = root def __repr__(self) -> str: return f"VenvContainer({str(self._root)!r})" def __str__(self) -> str: return str(self._root) def iter_venv_dirs(self) -> Generator[Path, None, None]: """Iterate venv directories in this container.""" if not self._root.is_dir(): return for entry in self._root.iterdir(): if not entry.is_dir(): continue yield entry def get_venv_dir(self, package_name: str) -> Path: """Return the expected venv path for given `package_name`.""" return self._root.joinpath(canonicalize_name(package_name)) class Venv: """Abstraction for a virtual environment with various useful methods for pipx""" def __init__( self, path: Path, *, verbose: bool = False, python: str = DEFAULT_PYTHON, backend: str | None = None, env_backend: str | None = None, ) -> None: self.root = path self.python = python self.bin_path, self.python_path, self.man_path = get_venv_paths(self.root) self.pipx_metadata = PipxMetadata(venv_dir=path) self.verbose = verbose self.do_animation = not verbose try: self._existing = self.root.exists() and bool(next(self.root.iterdir())) except StopIteration: self._existing = False self._backend_name, self._backend_source = _resolve_backend_for_venv( root=self.root, existing=self._existing, metadata_backend=self.pipx_metadata.backend if self._existing else None, cli_backend=backend, env_backend=env_backend, ) self._backend: Backend | None = None self._uses_shared_libs_cache: bool | None = None @property def backend(self) -> Backend: if self._backend is None: self._backend = get_backend(self._backend_name) return self._backend @property def backend_name(self) -> str: return self._backend_name @property def backend_source(self) -> str: return self._backend_source def check_upgrade_shared_libs(self, verbose: bool, pip_args: list[str], force_upgrade: bool = False): """ If necessary, run maintenance tasks to keep the shared libs up-to-date. This can trigger `pip install`/`pip install --upgrade` operations, so we expect the caller to provide sensible `pip_args` ( provided by the user in the current CLI call or retrieved from the metadata of a previous installation) """ if self._existing and self.uses_shared_libs: if shared_libs.is_valid: if force_upgrade: shared_libs.upgrade(verbose=verbose, pip_args=pip_args) elif shared_libs_auto_upgrade_disabled(): _LOGGER.info(f"Skipping shared libs auto-upgrade because {DISABLE_SHARED_LIBS_AUTO_UPGRADE} is set.") elif shared_libs.needs_upgrade: shared_libs.upgrade(verbose=verbose, pip_args=pip_args) else: shared_libs.create(verbose=verbose, pip_args=pip_args) if not shared_libs.is_valid: raise PipxError( pipx_wrap( f""" Error: pipx's shared venv {shared_libs.root} is invalid and needs re-installation. To fix this, install or reinstall a package. For example: """ ) + f"\n pipx install {self.root.name} --force", wrap_message=False, ) @property def name(self) -> str: if self.pipx_metadata.main_package.package is not None: venv_name = f"{self.pipx_metadata.main_package.package}{self.pipx_metadata.main_package.suffix}" else: venv_name = self.root.name return venv_name @property def uses_shared_libs(self) -> bool: if not self._existing: return self.backend.needs_shared_libs() if self._uses_shared_libs_cache is not None: return self._uses_shared_libs_cache # Metadata 0.6+ records ``backend`` authoritatively; older recordings # (or missing file) fall back to the .pth probe so legacy venvs still # report correctly. recorded_version = self.pipx_metadata.read_metadata_version if recorded_version is not None and recorded_version >= "0.6": answer = self.pipx_metadata.backend == "pip" else: answer = next(self.root.glob(f"**/{PIPX_SHARED_PTH}"), None) is not None self._uses_shared_libs_cache = answer return answer @property def package_metadata(self) -> dict[str, PackageInfo]: return_dict = self.pipx_metadata.injected_packages.copy() if self.pipx_metadata.main_package.package is not None: return_dict[self.pipx_metadata.main_package.package] = self.pipx_metadata.main_package return return_dict @property def main_package_name(self) -> str: if self.pipx_metadata.main_package.package is None: # This is OK, because if no metadata, we are pipx < v0.15.0.0 and # venv_name==main_package_name return self.root.name else: return self.pipx_metadata.main_package.package def create_venv(self, venv_args: list[str], pip_args: list[str], override_shared: bool = False) -> None: """ override_shared -- Override installing shared libraries to the pipx shared directory (default False) """ _LOGGER.info("Creating virtual environment") if override_shared: assert_not_pip_under_uv("pip", self._backend_name) self.backend.create_venv( self.root, python=self.python, venv_args=venv_args, pip_args=pip_args, include_pip=override_shared, verbose=self.verbose, ) self.pipx_metadata.venv_args = venv_args # Persist the chosen backend on disk only when actually creating the venv. # Runtime overrides on existing venvs (e.g. `--backend uv pipx upgrade ...`) # must not silently flip the recorded backend. self.pipx_metadata.backend = self._backend_name self.pipx_metadata.python_version = self.get_python_version() source_interpreter = shutil.which(self.python) if source_interpreter: self.pipx_metadata.source_interpreter = Path(source_interpreter) def safe_to_remove(self) -> bool: return not self._existing def remove_venv(self) -> None: if self.safe_to_remove(): rmdir(self.root) else: _LOGGER.warning( pipx_wrap( f""" {hazard} Not removing existing venv {self.root} because it was not created in this session """, subsequent_indent=" " * 4, ) ) def upgrade_packaging_libraries(self, pip_args: list[str]) -> None: if self.uses_shared_libs: shared_libs.upgrade(pip_args=pip_args, verbose=self.verbose) return # Short-circuit before instantiating UvBackend so ``upgrade-all`` over # uv venvs still works after uv has been uninstalled (the no-op upgrade # would have happened anyway). if self._backend_name == "uv": return self.backend.upgrade_packaging_libraries(self.python_path, pip_args, verbose=self.verbose) def uninstall_package(self, package: str, was_injected: bool = False): try: _LOGGER.info("Uninstalling %s", package) with animate(f"uninstalling {package}", self.do_animation): self.backend.uninstall( venv_root=self.root, venv_python=self.python_path, package=package, verbose=self.verbose, ) except PipxError as e: _LOGGER.info(e) raise PipxError(f"Error uninstalling {package}.") from None if was_injected: self.pipx_metadata.injected_packages.pop(package) self.pipx_metadata.write() def install_package( self, package_name: str, package_or_url: str, pip_args: list[str], include_dependencies: bool, include_apps: bool, is_main_package: bool, suffix: str = "", install_only_pip_args: list[str] | None = None, ) -> None: # package_name in package specifier can mismatch URL due to user error package_or_url = fix_package_name(package_or_url, package_name) # check syntax and clean up spec and pip_args (package_or_url, pip_args) = parse_specifier_for_install(package_or_url, pip_args) install_pip_args = [*(install_only_pip_args or []), *pip_args] _LOGGER.info("Installing %s", package_descr := full_package_description(package_name, package_or_url)) with animate(f"installing {package_descr}", self.do_animation): process = self.backend.install( venv_root=self.root, venv_python=self.python_path, requirements=[package_or_url], pip_args=install_pip_args, verbose=self.verbose, ) if process.returncode: raise PipxError(f"Error installing {full_package_description(package_name, package_or_url)}.") self.update_package_metadata( package_name=package_name, package_or_url=package_or_url, pip_args=pip_args, include_dependencies=include_dependencies, include_apps=include_apps, is_main_package=is_main_package, suffix=suffix, ) # Verify package installed ok if self.package_metadata[package_name].package_version is None: raise PipxError( f"Unable to install " f"{full_package_description(package_name, package_or_url)}.\n" f"Check the name or spec for errors, and verify that it can " f"be installed with pip.", wrap_message=False, ) def install_unmanaged_packages(self, requirements: list[str], pip_args: list[str]) -> None: """Install packages in the venv, but do not record them in the metadata.""" _LOGGER.info("Installing %s", package_descr := ", ".join(requirements)) with animate(f"installing {package_descr}", self.do_animation): process = self.backend.install( venv_root=self.root, venv_python=self.python_path, requirements=list(requirements), pip_args=pip_args, verbose=self.verbose, ) if process.returncode: raise PipxError(f"Error installing {', '.join(requirements)}.") def install_package_no_deps(self, package_or_url: str, pip_args: list[str]) -> str: with animate(f"determining package name from {package_or_url!r}", self.do_animation): old_package_set = self.list_installed_packages() process = self.backend.install( venv_root=self.root, venv_python=self.python_path, requirements=[package_or_url], pip_args=pip_args, no_deps=True, log_pip_errors=False, verbose=self.verbose, ) if process.returncode: error_output = (process.stderr or process.stdout or "").strip() if error_output: raise PipxError(error_output, wrap_message=False) raise PipxError( f""" Error determining package name from spec {package_or_url!r}. See installer output for details. """ ) installed_packages = self.list_installed_packages() - old_package_set if len(installed_packages) == 1: package_name = installed_packages.pop() _LOGGER.info(f"Determined package name: {package_name}") else: _LOGGER.info(f"old_package_set = {old_package_set}") _LOGGER.info(f"install_packages = {installed_packages}") raise PipxError( f""" Cannot determine package name from spec {package_or_url!r}. Check package spec for errors. """ ) return package_name def get_venv_metadata_for_package(self, package_name: str, package_extras: set[str]) -> VenvMetadata: data_start = time.time() venv_metadata = inspect_venv(package_name, package_extras, self.bin_path, self.python_path, self.man_path) _LOGGER.info(f"get_venv_metadata_for_package: {1e3 * (time.time() - data_start):.0f}ms") return venv_metadata def update_package_metadata( self, package_name: str, package_or_url: str, pip_args: list[str], include_dependencies: bool, include_apps: bool, is_main_package: bool, suffix: str = "", pinned: bool = False, ) -> None: venv_package_metadata = self.get_venv_metadata_for_package(package_name, get_extras(package_or_url)) package_info = PackageInfo( package=package_name, package_or_url=parse_specifier_for_metadata(package_or_url), pip_args=pip_args, include_apps=include_apps, include_dependencies=include_dependencies, apps=venv_package_metadata.apps, app_paths=venv_package_metadata.app_paths, apps_of_dependencies=venv_package_metadata.apps_of_dependencies, app_paths_of_dependencies=venv_package_metadata.app_paths_of_dependencies, man_pages=venv_package_metadata.man_pages, man_paths=venv_package_metadata.man_paths, man_pages_of_dependencies=venv_package_metadata.man_pages_of_dependencies, man_paths_of_dependencies=venv_package_metadata.man_paths_of_dependencies, package_version=venv_package_metadata.package_version, suffix=suffix, pinned=pinned, ) if is_main_package: self.pipx_metadata.main_package = package_info else: self.pipx_metadata.injected_packages[package_name] = package_info self.pipx_metadata.write() def get_python_version(self) -> str: return run_subprocess([str(self.python_path), "--version"]).stdout.strip() def list_installed_packages(self, not_required: bool = False) -> set[str]: return self.backend.list_installed( venv_root=self.root, venv_python=self.python_path, not_required=not_required, ) def _find_entry_point(self, app: str) -> EntryPoint | None: if not self.python_path.exists(): return None dists = Distribution.discover(name=self.main_package_name, path=[str(get_site_packages(self.python_path))]) for dist in dists: for ep in dist.entry_points: if ep.group == "pipx.run": if ep.name == app: return ep # Try to infer app name from dist's metadata if given # local path if Path(app).exists() and dist.metadata["Name"] == ep.name: return ep return None def run_app(self, app: str, filename: str, app_args: list[str]) -> NoReturn: entry_point = self._find_entry_point(app) # No [pipx.run] entry point; default to run console script. if entry_point is None: exec_app([str(self.bin_path / filename)] + app_args) # Evaluate and execute the entry point. _LOGGER.info("Using discovered entry point for 'pipx run'") module, attr = entry_point.module, entry_point.attr code = f"import sys, {module}\nsys.argv[0] = {entry_point.name!r}\nsys.exit({module}.{attr}())\n" exec_app([str(self.python_path), "-c", code] + app_args) def has_app(self, app: str, filename: str) -> bool: if self._find_entry_point(app) is not None: return True return (self.bin_path / filename).is_file() def has_package(self, package_name: str) -> bool: return bool(list(Distribution.discover(name=package_name, path=[str(get_site_packages(self.python_path))]))) def upgrade_package_no_metadata(self, package_name: str, pip_args: list[str]) -> None: _LOGGER.info("Upgrading %s", package_descr := full_package_description(package_name, package_name)) with animate(f"upgrading {package_descr}", self.do_animation): process = self.backend.install( venv_root=self.root, venv_python=self.python_path, requirements=[package_name], pip_args=pip_args, upgrade=True, log_pip_errors=False, verbose=self.verbose, ) subprocess_post_check(process) def upgrade_package( self, package_name: str, package_or_url: str, pip_args: list[str], include_dependencies: bool, include_apps: bool, is_main_package: bool, suffix: str = "", ) -> None: _LOGGER.info("Upgrading %s", package_descr := full_package_description(package_name, package_or_url)) with animate(f"upgrading {package_descr}", self.do_animation): process = self.backend.install( venv_root=self.root, venv_python=self.python_path, requirements=[package_or_url], pip_args=pip_args, upgrade=True, log_pip_errors=False, verbose=self.verbose, ) subprocess_post_check(process) self.update_package_metadata( package_name=package_name, package_or_url=package_or_url, pip_args=pip_args, include_dependencies=include_dependencies, include_apps=include_apps, is_main_package=is_main_package, suffix=suffix, ) def _run_pip(self, cmd: list[str]) -> "CompletedProcess[str]": return self.backend.run_raw_pip( venv_root=self.root, venv_python=self.python_path, args=cmd, verbose=self.verbose, ) def run_pip_get_exit_code(self, cmd: list[str]) -> ExitCode: process = self.backend.run_raw_pip( venv_root=self.root, venv_python=self.python_path, args=cmd, capture_stdout=False, capture_stderr=False, verbose=self.verbose, ) if process.returncode: cmd_str = " ".join(str(c) for c in process.args) _LOGGER.error(f"{cmd_str!r} failed") return ExitCode(process.returncode) __all__ = [ "Venv", "VenvContainer", "reset_backend_override_warnings", ] pipx-1.15.0/src/pipx/venv_inspect.py000066400000000000000000000446531521706623000174200ustar00rootroot00000000000000import ast import configparser import json import logging import shutil import textwrap from collections.abc import Collection, Iterable, Iterator from pathlib import Path, PurePosixPath from typing import NamedTuple from urllib.parse import urlparse from urllib.request import url2pathname from packaging.requirements import Requirement from packaging.utils import canonicalize_name try: from importlib import metadata except ImportError: import importlib_metadata as metadata # type: ignore[import-not-found,no-redef] try: import tomllib except ImportError: import tomli as tomllib # type: ignore[import-not-found,no-redef] from pipx.constants import MAN_SECTIONS, WINDOWS from pipx.util import PipxError, run_subprocess logger = logging.getLogger(__name__) class VenvInspectInformation(NamedTuple): distributions: Collection[metadata.Distribution] env: dict[str, str] bin_path: Path man_path: Path class VenvMetadata(NamedTuple): apps: list[str] app_paths: list[Path] apps_of_dependencies: list[str] app_paths_of_dependencies: dict[str, list[Path]] man_pages: list[str] man_paths: list[Path] man_pages_of_dependencies: list[str] man_paths_of_dependencies: dict[str, list[Path]] package_version: str python_version: str def get_dist(package: str, distributions: Collection[metadata.Distribution]) -> metadata.Distribution | None: """Find matching distribution in the canonicalized sense.""" for dist in distributions: if canonicalize_name(dist.metadata["name"]) == canonicalize_name(package): return dist return None def get_package_dependencies(dist: metadata.Distribution, extras: set[str], env: dict[str, str]) -> list[Requirement]: eval_env = env.copy() # Add an empty extra to enable evaluation of non-extra markers if not extras: extras.add("") dependencies = [] for req in map(Requirement, dist.requires or []): if not req.marker: dependencies.append(req) else: for extra in extras: eval_env["extra"] = extra if req.marker.evaluate(eval_env): dependencies.append(req) break return dependencies def get_required_dependency_names(dist: metadata.Distribution, env: dict[str, str]) -> set[str]: """Canonical names ``dist`` depends on, including dependencies behind its declared extras. Considering every declared extra is deliberately conservative: a dependency that is only pulled in by an extra is still treated as required, so it is never reported as removable while the package declaring it stays installed. """ extras = set(dist.metadata.get_all("Provides-Extra") or []) return {canonicalize_name(req.name) for req in get_package_dependencies(dist, extras, env)} def list_not_required_packages(venv_python: Path) -> set[str]: """Canonical names of installed packages that no other installed package requires. Mirrors ``pip list --not-required`` from installed distribution metadata so every backend (including uv, which lacks the flag) computes the same result. """ venv_sys_path, venv_env, _ = fetch_info_in_venv(venv_python) distributions = tuple(metadata.distributions(path=venv_sys_path)) installed: set[str] = set() required: set[str] = set() for dist in distributions: if (name := dist.metadata["Name"]) is not None: installed.add(canonicalize_name(name)) required |= get_required_dependency_names(dist, venv_env) return installed - required def get_apps_from_entry_points(dist: metadata.Distribution, bin_path: Path): app_names = set() sections = {"console_scripts", "gui_scripts"} # "entry_points" entry in setup.py are found here for ep in dist.entry_points: if ep.group not in sections: continue if (bin_path / ep.name).exists(): app_names.add(ep.name) if WINDOWS and (bin_path / (ep.name + ".exe")).exists(): # WINDOWS adds .exe to entry_point name app_names.add(ep.name + ".exe") return app_names def get_resources_from_dist_files(dist: metadata.Distribution, bin_path: Path, man_path: Path): app_names = set() man_names = set() # search installed files # "scripts" entry in setup.py is found here (test w/ awscli) for path in dist.files or []: # vast speedup by ignoring all paths not above distribution root dir # (venv/bin or venv/Scripts is above distribution root) if Path(path).parts[0] != "..": continue dist_file_path = Path(str(dist.locate_file(path))) try: if dist_file_path.parent.samefile(bin_path): app_names.add(path.name) if dist_file_path.parent.name in MAN_SECTIONS and dist_file_path.parent.parent.samefile(man_path): man_names.add(str(Path(dist_file_path.parent.name) / path.name)) except FileNotFoundError: pass return app_names, man_names def get_resources_from_inst_files(dist: metadata.Distribution, bin_path: Path, man_path: Path): app_names = set() man_names = set() # not sure what is found here inst_files = dist.read_text("installed-files.txt") or "" for line in inst_files.splitlines(): entry = line.split(",")[0] inst_file_path = Path(str(dist.locate_file(entry))).resolve() try: if inst_file_path.parent.samefile(bin_path): app_names.add(inst_file_path.name) if inst_file_path.parent.name in MAN_SECTIONS and inst_file_path.parent.parent.samefile(man_path): man_names.add(str(Path(inst_file_path.parent.name) / inst_file_path.name)) except FileNotFoundError: pass return app_names, man_names def get_resources(dist: metadata.Distribution, bin_path: Path, man_path: Path) -> tuple[list[str], list[str]]: app_names = set() man_names = set() app_names_ep = get_apps_from_entry_points(dist, bin_path) app_names_df, man_names_df = get_resources_from_dist_files(dist, bin_path, man_path) app_names_if, man_names_if = get_resources_from_inst_files(dist, bin_path, man_path) app_names = app_names_ep | app_names_df | app_names_if man_names = man_names_df | man_names_if | _get_man_pages_from_editable_project(dist, man_path) return sorted(app_names), sorted(man_names) def _get_man_pages_from_editable_project(dist: metadata.Distribution, man_path: Path) -> set[str]: project_root = _get_editable_project_root(dist) if project_root is None: return set() man_names = set() for target_dir, source_paths in _iter_editable_project_data_files(project_root): man_section = _get_man_section_from_data_files_target(target_dir) if man_section is None: continue for source_path in source_paths: source = Path(source_path) if not source.is_absolute(): source = project_root / source if not source.is_file(): continue dest = man_path / man_section / source.name try: dest.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(source, dest) except OSError as exc: logger.warning("Unable to copy editable man page %s to %s: %s", source, dest, exc) continue man_names.add(str(Path(man_section) / source.name)) return man_names def _get_editable_project_root(dist: metadata.Distribution) -> Path | None: direct_url = dist.read_text("direct_url.json") if not direct_url: return None try: data = json.loads(direct_url) except json.JSONDecodeError: return None dir_info = data.get("dir_info", {}) if not isinstance(dir_info, dict) or not dir_info.get("editable"): return None url = data.get("url") if not isinstance(url, str): return None parsed_url = urlparse(url) if parsed_url.scheme != "file": return None url_path = f"//{parsed_url.netloc}{parsed_url.path}" if parsed_url.netloc else parsed_url.path return Path(url2pathname(url_path)) def _iter_editable_project_data_files(project_root: Path) -> Iterator[tuple[str, list[str]]]: yield from _iter_pyproject_data_files(project_root) yield from _iter_setup_cfg_data_files(project_root) yield from _iter_setup_py_data_files(project_root) def _iter_pyproject_data_files(project_root: Path) -> Iterator[tuple[str, list[str]]]: pyproject = project_root / "pyproject.toml" try: with pyproject.open("rb") as file: pyproject_data = tomllib.load(file) except (OSError, tomllib.TOMLDecodeError): return data_files = pyproject_data.get("tool", {}).get("setuptools", {}).get("data-files", {}) yield from _iter_normalized_data_files(data_files) def _iter_setup_cfg_data_files(project_root: Path) -> Iterator[tuple[str, list[str]]]: setup_cfg = project_root / "setup.cfg" config = configparser.ConfigParser(interpolation=None) try: config.read(setup_cfg) except configparser.Error: return if not config.has_section("options.data_files"): return yield from _iter_normalized_data_files(dict(config.items("options.data_files"))) def _iter_setup_py_data_files(project_root: Path) -> Iterator[tuple[str, list[str]]]: setup_py = project_root / "setup.py" try: tree = ast.parse(setup_py.read_text(encoding="utf-8")) except (OSError, SyntaxError, UnicodeDecodeError): return for node in ast.walk(tree): if not isinstance(node, ast.Call) or not _is_setup_call(node): continue for keyword in node.keywords: if keyword.arg != "data_files": continue try: data_files = ast.literal_eval(keyword.value) except (ValueError, SyntaxError): return yield from _iter_normalized_data_files(data_files) return def _is_setup_call(node: ast.Call) -> bool: return (isinstance(node.func, ast.Name) and node.func.id == "setup") or ( isinstance(node.func, ast.Attribute) and node.func.attr == "setup" ) def _iter_normalized_data_files(data_files: object) -> Iterator[tuple[str, list[str]]]: items: Iterable[object] if isinstance(data_files, dict): items = data_files.items() elif isinstance(data_files, (list, tuple)): items = data_files else: return for item in items: if not isinstance(item, (list, tuple)) or len(item) != 2: continue target_dir, source_paths = item if not isinstance(target_dir, str): continue if isinstance(source_paths, str): yield target_dir, [line.strip() for line in source_paths.splitlines() if line.strip()] elif isinstance(source_paths, (list, tuple)) and all(isinstance(path, str) for path in source_paths): yield target_dir, list(source_paths) def _get_man_section_from_data_files_target(target_dir: str) -> str | None: parts = PurePosixPath(target_dir.replace("\\", "/")).parts if len(parts) < 2 or parts[-2] != "man" or parts[-1] not in MAN_SECTIONS: return None return parts[-1] def _dfs_package_resources( dist: metadata.Distribution, package_req: Requirement, venv_inspect_info: VenvInspectInformation, app_paths_of_dependencies: dict[str, list[Path]], man_paths_of_dependencies: dict[str, list[Path]], dep_visited: dict[str, bool] | None = None, ) -> tuple[dict[str, list[Path]], dict[str, list[Path]]]: if dep_visited is None: # Initialize: we have already visited root dep_visited = {canonicalize_name(package_req.name): True} dependencies = get_package_dependencies(dist, package_req.extras, venv_inspect_info.env) for dep_req in dependencies: dep_name = canonicalize_name(dep_req.name) if dep_name in dep_visited: # avoid infinite recursion, avoid duplicates in info continue dep_dist = get_dist(dep_req.name, venv_inspect_info.distributions) if dep_dist is None: raise PipxError(f"Pipx Internal Error: cannot find package {dep_req.name!r} metadata.") app_names, man_names = get_resources(dep_dist, venv_inspect_info.bin_path, venv_inspect_info.man_path) if app_names: app_paths_of_dependencies[dep_name] = [venv_inspect_info.bin_path / name for name in app_names] if man_names: man_paths_of_dependencies[dep_name] = [venv_inspect_info.man_path / name for name in man_names] # recursively search for more dep_visited[dep_name] = True app_paths_of_dependencies, man_paths_of_dependencies = _dfs_package_resources( dep_dist, dep_req, venv_inspect_info, app_paths_of_dependencies, man_paths_of_dependencies, dep_visited, ) return app_paths_of_dependencies, man_paths_of_dependencies def _windows_extra_app_paths(app_paths: list[Path]) -> list[Path]: # In Windows, editable package have additional files starting with the # same name that are required to be in the same dir to run the app # Add "*-script.py", "*.exe.manifest" only to app_paths to make # execution work; do not add them to apps to ensure they are not listed app_paths_output = app_paths.copy() for app_path in app_paths: win_app_path = app_path.parent / (app_path.stem + "-script.py") if win_app_path.exists(): app_paths_output.append(win_app_path) win_app_path = app_path.parent / (app_path.stem + ".exe.manifest") if win_app_path.exists(): app_paths_output.append(win_app_path) return app_paths_output def fetch_info_in_venv(venv_python_path: Path) -> tuple[list[str], dict[str, str], str]: command_str = textwrap.dedent( """ import json import os import platform import sys impl_ver = sys.implementation.version implementation_version = "{0.major}.{0.minor}.{0.micro}".format(impl_ver) if impl_ver.releaselevel != "final": implementation_version = "{}{}{}".format( implementation_version, impl_ver.releaselevel[0], impl_ver.serial, ) sys_path = sys.path try: sys_path.remove("") except ValueError: pass print( json.dumps( { "sys_path": sys_path, "python_version": "{0.major}.{0.minor}.{0.micro}".format(sys.version_info), "environment": { "implementation_name": sys.implementation.name, "implementation_version": implementation_version, "os_name": os.name, "platform_machine": platform.machine(), "platform_release": platform.release(), "platform_system": platform.system(), "platform_version": platform.version(), "python_full_version": platform.python_version(), "platform_python_implementation": platform.python_implementation(), "python_version": ".".join(platform.python_version_tuple()[:2]), "sys_platform": sys.platform, }, } ) ) """ ) venv_info = json.loads( run_subprocess( [venv_python_path, "-c", command_str], capture_stderr=False, log_cmd_str="", ).stdout ) return ( venv_info["sys_path"], venv_info["environment"], f"Python {venv_info['python_version']}", ) def inspect_venv( root_package_name: str, root_package_extras: set[str], venv_bin_path: Path, venv_python_path: Path, venv_man_path: Path, ) -> VenvMetadata: app_paths_of_dependencies: dict[str, list[Path]] = {} apps_of_dependencies: list[str] = [] man_paths_of_dependencies: dict[str, list[Path]] = {} man_pages_of_dependencies: list[str] = [] root_req = Requirement(root_package_name) root_req.extras = root_package_extras (venv_sys_path, venv_env, venv_python_version) = fetch_info_in_venv(venv_python_path) # Collect the generator created from metadata.distributions() # (see `itertools.chain.from_iterable`) into a tuple because we # need to iterate over it multiple times in `_dfs_package_apps`. # Tuple is chosen over a list because the program only iterate over # the distributions and never modify it. distributions = tuple(metadata.distributions(path=venv_sys_path)) venv_inspect_info = VenvInspectInformation( bin_path=venv_bin_path, man_path=venv_man_path, env=venv_env, distributions=distributions, ) root_dist = get_dist(root_req.name, venv_inspect_info.distributions) if root_dist is None: raise PipxError(f"Pipx Internal Error: cannot find package {root_req.name!r} metadata.") app_paths_of_dependencies, man_paths_of_dependencies = _dfs_package_resources( root_dist, root_req, venv_inspect_info, app_paths_of_dependencies, man_paths_of_dependencies, ) apps, man_pages = get_resources(root_dist, venv_bin_path, venv_man_path) app_paths = [venv_bin_path / app for app in apps] man_paths = [venv_man_path / man_page for man_page in man_pages] if WINDOWS: app_paths = _windows_extra_app_paths(app_paths) for dep in app_paths_of_dependencies: apps_of_dependencies += [dep_path.name for dep_path in app_paths_of_dependencies[dep]] if WINDOWS: app_paths_of_dependencies[dep] = _windows_extra_app_paths(app_paths_of_dependencies[dep]) for dep in man_paths_of_dependencies: man_pages_of_dependencies += [ str(Path(dep_path.parent.name) / dep_path.name) for dep_path in man_paths_of_dependencies[dep] ] return VenvMetadata( apps=apps, app_paths=app_paths, apps_of_dependencies=apps_of_dependencies, app_paths_of_dependencies=app_paths_of_dependencies, man_pages=man_pages, man_paths=man_paths, man_pages_of_dependencies=man_pages_of_dependencies, man_paths_of_dependencies=man_paths_of_dependencies, package_version=root_dist.version, python_version=venv_python_version, ) pipx-1.15.0/src/pipx/version.pyi000066400000000000000000000003621521706623000165400ustar00rootroot00000000000000version: str version_tuple: tuple[int, int, int, str, str] | tuple[int, int, int] # Note that newer versions of setuptools_scm also add __version__, but we are # not forcing new versions of setuptools_scm, so only these imports are allowed. pipx-1.15.0/src/pipx/wrap.py000066400000000000000000000015441521706623000156560ustar00rootroot00000000000000import shutil import textwrap def pipx_wrap(text: str, subsequent_indent: str = "", keep_newlines: bool = False) -> str: """Dedent, strip, wrap to shell width. Don't break on hyphens, only spaces""" minimum_width = 40 width = max(shutil.get_terminal_size((80, 40)).columns, minimum_width) - 2 text = textwrap.dedent(text).strip() if keep_newlines: return "\n".join( [ textwrap.fill( line, width=width, subsequent_indent=subsequent_indent, break_on_hyphens=False, ) for line in text.splitlines() ] ) else: return textwrap.fill( text, width=width, subsequent_indent=subsequent_indent, break_on_hyphens=False, ) pipx-1.15.0/testdata/000077500000000000000000000000001521706623000143715ustar00rootroot00000000000000pipx-1.15.0/testdata/empty_project/000077500000000000000000000000001521706623000172555ustar00rootroot00000000000000pipx-1.15.0/testdata/empty_project/README.md000066400000000000000000000000441521706623000205320ustar00rootroot00000000000000Empty project used for testing only pipx-1.15.0/testdata/empty_project/empty_project/000077500000000000000000000000001521706623000221415ustar00rootroot00000000000000pipx-1.15.0/testdata/empty_project/empty_project/__init__.py000066400000000000000000000000001521706623000242400ustar00rootroot00000000000000pipx-1.15.0/testdata/empty_project/empty_project/main.py000066400000000000000000000000751521706623000234410ustar00rootroot00000000000000def main(): pass if __name__ == "__main__": main() pipx-1.15.0/testdata/empty_project/pyproject.toml000066400000000000000000000012331521706623000221700ustar00rootroot00000000000000[build-system] build-backend = "setuptools.build_meta" requires = [ "setuptools", ] [project] name = "empty-project" version = "0.1.0" description = "Empty Python Project" authors = [ { name = "My Name", email = "me@example.com" }, ] requires-python = ">=3.10" classifiers = [ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", ] scripts.empty-project = "empty_project.main:cli" entry-points."pipx.run".empty-project = "empty_project.main:cli" pipx-1.15.0/testdata/pipx_metadata_multiple_errors.json000066400000000000000000000042231521706623000234140ustar00rootroot00000000000000{ "pipx_spec_version": "0.1", "venvs": { "dotenv": { "metadata": { "injected_packages": {}, "main_package": { "app_paths": [ ], "app_paths_of_dependencies": {}, "apps": [ ], "apps_of_dependencies": [], "include_apps": true, "include_dependencies": false, "man_pages": [], "man_pages_of_dependencies": [], "man_paths": [], "man_paths_of_dependencies": {}, "package": "dotenv", "package_or_url": "dotenv", "package_version": "0.0.5", "pip_args": [], "suffix": "" }, "pipx_metadata_version": "0.4", "python_version": "Python 3.10.12", "source_interpreter": { }, "venv_args": [] } }, "weblate": { "metadata": { "injected_packages": {}, "main_package": { "app_paths": [ ], "app_paths_of_dependencies": {}, "apps": [ ], "apps_of_dependencies": [], "include_apps": true, "include_dependencies": false, "man_pages": [ ], "man_pages_of_dependencies": [], "man_paths": [ ], "man_paths_of_dependencies": {}, "package": "weblate", "package_or_url": "weblate", "package_version": "4.3.1", "pip_args": [], "suffix": "" }, "pipx_metadata_version": "0.4", "python_version": "Python 3.10.12", "source_interpreter": { }, "venv_args": [] } } } } pipx-1.15.0/testdata/standalone_python_index_20250818.json000066400000000000000000004537751521706623000232220ustar00rootroot00000000000000{"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"]]} pipx-1.15.0/testdata/standalone_python_index_20250828.json000066400000000000000000004537751521706623000232230ustar00rootroot00000000000000{"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"]]} pipx-1.15.0/testdata/test_package_specifier/000077500000000000000000000000001521706623000210545ustar00rootroot00000000000000pipx-1.15.0/testdata/test_package_specifier/local_extras/000077500000000000000000000000001521706623000235345ustar00rootroot00000000000000pipx-1.15.0/testdata/test_package_specifier/local_extras/repeatme/000077500000000000000000000000001521706623000253365ustar00rootroot00000000000000pipx-1.15.0/testdata/test_package_specifier/local_extras/repeatme/__init__.py000066400000000000000000000000001521706623000274350ustar00rootroot00000000000000pipx-1.15.0/testdata/test_package_specifier/local_extras/repeatme/main.py000066400000000000000000000004321521706623000266330ustar00rootroot00000000000000import sys try: import pycowsay.main has_pycowsay = True except ImportError: has_pycowsay = False def main(): print(f"You said:\n {' '.join(sys.argv[1:])}") if has_pycowsay: print() print("In cow, you said:") pycowsay.main.main() pipx-1.15.0/testdata/test_package_specifier/local_extras/setup.py000066400000000000000000000004071521706623000252470ustar00rootroot00000000000000from setuptools import setup setup( name="repeatme", version=0.1, description="Repeat arguments.", packages=["repeatme"], extras_require={"cow": ["pycowsay==0.0.0.2"]}, entry_points={"console_scripts": ["repeatme=repeatme.main:main"]}, ) pipx-1.15.0/testdata/test_package_specifier/local_manpage/000077500000000000000000000000001521706623000236365ustar00rootroot00000000000000pipx-1.15.0/testdata/test_package_specifier/local_manpage/local_manpage/000077500000000000000000000000001521706623000264205ustar00rootroot00000000000000pipx-1.15.0/testdata/test_package_specifier/local_manpage/local_manpage/__init__.py000066400000000000000000000000001521706623000305170ustar00rootroot00000000000000pipx-1.15.0/testdata/test_package_specifier/local_manpage/local_manpage/main.py000066400000000000000000000000471521706623000277170ustar00rootroot00000000000000def main(): print("local-manpage") pipx-1.15.0/testdata/test_package_specifier/local_manpage/man/000077500000000000000000000000001521706623000244115ustar00rootroot00000000000000pipx-1.15.0/testdata/test_package_specifier/local_manpage/man/local-manpage.1000066400000000000000000000000751521706623000271750ustar00rootroot00000000000000.TH local-manpage 1 .SH NAME local-manpage \- local test app pipx-1.15.0/testdata/test_package_specifier/local_manpage/setup.py000066400000000000000000000004031521706623000253450ustar00rootroot00000000000000from setuptools import setup setup( name="local-manpage", version="0.1", packages=["local_manpage"], entry_points={"console_scripts": ["local-manpage=local_manpage.main:main"]}, data_files=[("share/man/man1", ["man/local-manpage.1"])], ) pipx-1.15.0/testdata/tests_packages/000077500000000000000000000000001521706623000173715ustar00rootroot00000000000000pipx-1.15.0/testdata/tests_packages/README.md000066400000000000000000000024041521706623000206500ustar00rootroot00000000000000# Introduction `primary_packages.txt` is the master list, containing all packages installed or injected in the pipx tests `tests`. Platform-specific list files listing both these primary packages and their dependencies are generated from it. These platform-specific list files are used to populate the directory `.pipx_tests/package_cache`. # Generating the platform-specific lists from the master list Using the Github Workflow * 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. * Manually activate the Github workflow: Create tests package lists for offline tests * Download the artifact `lists` and put the files from it into this directory. Or to locally generate these lists, on the target platform execute: * `nox -s create_test_package_list` # Updating / Populating the directory `.pipx_tests/package_cache` before running the tests Pre-populating this directory allows the pipx `tests` to run completely offline. Nox instructions * execute `nox -s refresh_packages_cache` Or manually execute from the top-level pipx repo directory: * `mkdir -p .pipx_tests/package_cache` * `python3 scripts/update_package_cache.py testdata/tests_packages .pipx_tests/package_cache` pipx-1.15.0/testdata/tests_packages/macos24-python3.13.txt000066400000000000000000000215571521706623000232400ustar00rootroot00000000000000PyYAML==5.3.1 QtPy==2.4.3 Send2Trash==1.8.3 Weblate==4.3.1 ansible==6.7.0 ansible_core==2.13.13 anyio==4.11.0 appnope==0.1.4 argcomplete==1.12.3 argcomplete==3.6.2 argon2_cffi==25.1.0 argon2_cffi_bindings==25.1.0 arrow==1.3.0 astroid==3.0.3 astroid==3.3.11 asttokens==3.0.0 async_lru==2.0.5 attrs==25.3.0 awscli==1.18.168 babel==2.17.0 backcall==0.2.0 beautifulsoup4==4.14.2 black==22.10.0 black==22.8.0 bleach==6.2.0 botocore==1.19.8 build==1.3.0 cachetools==6.2.0 certifi==2025.8.3 cffi==2.0.0 chardet==5.2.0 charset_normalizer==3.4.3 click==8.3.0 cmarkgfm==2024.11.20 colorama==0.4.3 colorama==0.4.6 colorlog==6.9.0 comm==0.2.3 cryptography==46.0.1 cython==3.1.4 debugpy==1.8.17 decorator==5.2.1 defusedxml==0.7.1 dill==0.4.0 distlib==0.4.0 docutils==0.15.2 docutils==0.22.2 executing==2.2.1 fastjsonschema==2.21.2 filelock==3.19.1 fqdn==1.5.1 h11==0.16.0 httpcore==1.0.9 httpx==0.28.1 id==1.5.0 idna==3.10 iniconfig==2.1.0 ipykernel==6.30.1 ipython==7.16.1 ipython==9.6.0 ipython_pygments_lexers==1.1.1 ipywidgets==8.1.7 isoduration==20.11.0 isort==5.13.2 isort==5.6.4 isort==6.0.1 jaraco.classes==3.4.0 jaraco.clipboard==2.0.1 jaraco.context==6.0.1 jaraco_functools==4.3.0 jedi==0.19.2 jinja2==3.1.6 jmespath==0.10.0 json5==0.12.1 jsonpointer==3.0.0 jsonschema==4.25.1 jsonschema_specifications==2025.9.1 jupyter==1.0.0 jupyter_client==8.6.3 jupyter_console==6.6.3 jupyter_core==5.8.1 jupyter_events==0.12.0 jupyter_lsp==2.3.0 jupyter_server==2.17.0 jupyter_server_terminals==0.5.3 jupyterlab==4.4.9 jupyterlab_pygments==0.3.0 jupyterlab_server==2.27.3 jupyterlab_widgets==3.0.15 kaggle==1.6.11 keyring==25.6.0 lark==1.3.0 markdown_it_py==4.0.0 markupsafe==3.0.3 matplotlib_inline==0.1.7 mccabe==0.7.0 mdurl==0.1.2 mistune==3.1.4 more_itertools==10.8.0 mypy_extensions==1.1.0 nbclient==0.10.2 nbconvert==7.16.6 nbformat==5.10.4 nest_asyncio==1.6.0 nh3==0.3.0 notebook==7.4.7 notebook_shim==0.2.4 nox==2022.1.7 nox==2023.4.22 packaging==25.0 pandocfilters==1.5.1 parso==0.8.5 pathspec==0.12.1 pbr==5.6.0 pexpect==4.9.0 pickleshare==0.7.5 pip==23.3.2 pip==24.0 pip==25.2 platformdirs==4.4.0 pluggy==1.6.0 prometheus_client==0.23.1 prompt_toolkit==3.0.52 psutil==7.1.0 ptyprocess==0.7.0 pure_eval==0.2.3 py==1.11.0 pyasn1==0.6.1 pycowsay==0.0.0.2 pycparser==2.23 pygdbmi==0.10.0.0 pygments==2.19.2 pylint==3.0.4 pylint==3.3.8 pyobjc==11.1 pyobjc_core==11.1 pyobjc_framework_accessibility==11.1 pyobjc_framework_accounts==11.1 pyobjc_framework_addressbook==11.1 pyobjc_framework_adservices==11.1 pyobjc_framework_adsupport==11.1 pyobjc_framework_applescriptkit==11.1 pyobjc_framework_applescriptobjc==11.1 pyobjc_framework_applicationservices==11.1 pyobjc_framework_apptrackingtransparency==11.1 pyobjc_framework_audiovideobridging==11.1 pyobjc_framework_authenticationservices==11.1 pyobjc_framework_automaticassessmentconfiguration==11.1 pyobjc_framework_automator==11.1 pyobjc_framework_avfoundation==11.1 pyobjc_framework_avkit==11.1 pyobjc_framework_avrouting==11.1 pyobjc_framework_backgroundassets==11.1 pyobjc_framework_browserenginekit==11.1 pyobjc_framework_businesschat==11.1 pyobjc_framework_calendarstore==11.1 pyobjc_framework_callkit==11.1 pyobjc_framework_carbon==11.1 pyobjc_framework_cfnetwork==11.1 pyobjc_framework_cinematic==11.1 pyobjc_framework_classkit==11.1 pyobjc_framework_cloudkit==11.1 pyobjc_framework_cocoa==11.1 pyobjc_framework_collaboration==11.1 pyobjc_framework_colorsync==11.1 pyobjc_framework_contacts==11.1 pyobjc_framework_contactsui==11.1 pyobjc_framework_coreaudio==11.1 pyobjc_framework_coreaudiokit==11.1 pyobjc_framework_corebluetooth==11.1 pyobjc_framework_coredata==11.1 pyobjc_framework_corehaptics==11.1 pyobjc_framework_corelocation==11.1 pyobjc_framework_coremedia==11.1 pyobjc_framework_coremediaio==11.1 pyobjc_framework_coremidi==11.1 pyobjc_framework_coreml==11.1 pyobjc_framework_coremotion==11.1 pyobjc_framework_coreservices==11.1 pyobjc_framework_corespotlight==11.1 pyobjc_framework_coretext==11.1 pyobjc_framework_corewlan==11.1 pyobjc_framework_cryptotokenkit==11.1 pyobjc_framework_datadetection==11.1 pyobjc_framework_devicecheck==11.1 pyobjc_framework_devicediscoveryextension==11.1 pyobjc_framework_dictionaryservices==11.1 pyobjc_framework_discrecording==11.1 pyobjc_framework_discrecordingui==11.1 pyobjc_framework_diskarbitration==11.1 pyobjc_framework_dvdplayback==11.1 pyobjc_framework_eventkit==11.1 pyobjc_framework_exceptionhandling==11.1 pyobjc_framework_executionpolicy==11.1 pyobjc_framework_extensionkit==11.1 pyobjc_framework_externalaccessory==11.1 pyobjc_framework_fileprovider==11.1 pyobjc_framework_fileproviderui==11.1 pyobjc_framework_findersync==11.1 pyobjc_framework_fsevents==11.1 pyobjc_framework_fskit==11.1 pyobjc_framework_gamecenter==11.1 pyobjc_framework_gamecontroller==11.1 pyobjc_framework_gamekit==11.1 pyobjc_framework_gameplaykit==11.1 pyobjc_framework_healthkit==11.1 pyobjc_framework_imagecapturecore==11.1 pyobjc_framework_inputmethodkit==11.1 pyobjc_framework_installerplugins==11.1 pyobjc_framework_instantmessage==11.1 pyobjc_framework_intents==11.1 pyobjc_framework_intentsui==11.1 pyobjc_framework_iobluetooth==11.1 pyobjc_framework_iobluetoothui==11.1 pyobjc_framework_iosurface==11.1 pyobjc_framework_ituneslibrary==11.1 pyobjc_framework_kernelmanagement==11.1 pyobjc_framework_latentsemanticmapping==11.1 pyobjc_framework_launchservices==11.1 pyobjc_framework_libdispatch==11.1 pyobjc_framework_libxpc==11.1 pyobjc_framework_linkpresentation==11.1 pyobjc_framework_localauthentication==11.1 pyobjc_framework_localauthenticationembeddedui==11.1 pyobjc_framework_mailkit==11.1 pyobjc_framework_mapkit==11.1 pyobjc_framework_mediaaccessibility==11.1 pyobjc_framework_mediaextension==11.1 pyobjc_framework_medialibrary==11.1 pyobjc_framework_mediaplayer==11.1 pyobjc_framework_mediatoolbox==11.1 pyobjc_framework_metal==11.1 pyobjc_framework_metalfx==11.1 pyobjc_framework_metalkit==11.1 pyobjc_framework_metalperformanceshaders==11.1 pyobjc_framework_metalperformanceshadersgraph==11.1 pyobjc_framework_metrickit==11.1 pyobjc_framework_mlcompute==11.1 pyobjc_framework_modelio==11.1 pyobjc_framework_multipeerconnectivity==11.1 pyobjc_framework_naturallanguage==11.1 pyobjc_framework_netfs==11.1 pyobjc_framework_network==11.1 pyobjc_framework_networkextension==11.1 pyobjc_framework_notificationcenter==11.1 pyobjc_framework_opendirectory==11.1 pyobjc_framework_osakit==11.1 pyobjc_framework_oslog==11.1 pyobjc_framework_passkit==11.1 pyobjc_framework_pencilkit==11.1 pyobjc_framework_phase==11.1 pyobjc_framework_photos==11.1 pyobjc_framework_photosui==11.1 pyobjc_framework_preferencepanes==11.1 pyobjc_framework_pushkit==11.1 pyobjc_framework_quartz==11.1 pyobjc_framework_quicklookthumbnailing==11.1 pyobjc_framework_replaykit==11.1 pyobjc_framework_safariservices==11.1 pyobjc_framework_safetykit==11.1 pyobjc_framework_scenekit==11.1 pyobjc_framework_screencapturekit==11.1 pyobjc_framework_screensaver==11.1 pyobjc_framework_screentime==11.1 pyobjc_framework_scriptingbridge==11.1 pyobjc_framework_searchkit==11.1 pyobjc_framework_security==11.1 pyobjc_framework_securityfoundation==11.1 pyobjc_framework_securityinterface==11.1 pyobjc_framework_securityui==11.1 pyobjc_framework_sensitivecontentanalysis==11.1 pyobjc_framework_servicemanagement==11.1 pyobjc_framework_sharedwithyou==11.1 pyobjc_framework_sharedwithyoucore==11.1 pyobjc_framework_shazamkit==11.1 pyobjc_framework_social==11.1 pyobjc_framework_soundanalysis==11.1 pyobjc_framework_speech==11.1 pyobjc_framework_spritekit==11.1 pyobjc_framework_storekit==11.1 pyobjc_framework_symbols==11.1 pyobjc_framework_syncservices==11.1 pyobjc_framework_systemconfiguration==11.1 pyobjc_framework_systemextensions==11.1 pyobjc_framework_threadnetwork==11.1 pyobjc_framework_uniformtypeidentifiers==11.1 pyobjc_framework_usernotifications==11.1 pyobjc_framework_usernotificationsui==11.1 pyobjc_framework_videosubscriberaccount==11.1 pyobjc_framework_videotoolbox==11.1 pyobjc_framework_virtualization==11.1 pyobjc_framework_vision==11.1 pyobjc_framework_webkit==11.1 pyproject_api==1.9.1 pyproject_hooks==1.2.0 pytest==8.4.2 python_dateutil==2.9.0.post0 python_json_logger==3.3.0 python_slugify==8.0.4 pyyaml==6.0.3 pyzmq==27.1.0 qtconsole==5.7.0 readme_renderer==44.0 referencing==0.36.2 requests==2.31.0 requests==2.32.5 requests_toolbelt==1.0.0 resolvelib==0.8.1 rfc3339_validator==0.1.4 rfc3986==2.0.0 rfc3986_validator==0.1.1 rfc3987_syntax==1.1.0 rich==14.1.0 richxerox==1.0.1 rpds_py==0.27.1 rsa==4.5 s3transfer==0.3.7 setuptools==80.9.0 setuptools_scm==9.2.0 shell-functools==0.3.0 six==1.17.0 sniffio==1.3.1 soupsieve==2.8 stack_data==0.6.3 terminado==0.18.1 text_unidecode==1.3 tinycss2==1.4.0 tomlkit==0.13.3 tornado==6.5.2 tox==3.28.0 tox==4.30.2 tox_ini_fmt==0.5.0 tqdm==4.67.1 traitlets==5.14.3 twine==6.2.0 types_python_dateutil==2.9.0.20250822 typing_extensions==4.15.0 uri_template==1.3.0 urllib3==1.25.11 urllib3==2.5.0 virtualenv==20.34.0 wcwidth==0.2.14 webcolors==24.11.1 webencodings==0.5.1 websocket_client==1.8.0 wheel==0.45.1 widgetsnbextension==4.0.14 zest.releaser==9.1.2 pipx-1.15.0/testdata/tests_packages/primary_packages.txt000066400000000000000000000013121521706623000234500ustar00rootroot00000000000000# Modify this list if the packages pipx installs in 'tests' changes # Comments ignored after # # Space-separated values on same row. # spec no-deps chardet Cython # in 'setup_requires' of jupyter dep pywinpty on Win ansible==6.7.0 awscli==1.18.168 black==22.8.0 black==22.10.0 # cloudtoken==2.1.0 ipython==7.16.1 isort==5.6.4 jaraco-clipboard==2.0.1 zest-releaser==9.1.2 jupyter==1.0.0 kaggle==1.6.11 nox==2022.1.7 nox[tox_to_nox]==2023.4.22 pbr==5.6.0 pip==23.3.2 pip==24.0 pip pycowsay==0.0.0.2 pygdbmi==0.10.0.0 pylint pylint==3.0.4 requests==2.31.0 setuptools-scm setuptools>=41.0 shell-functools==0.3.0 tox tox-ini-fmt==0.5.0 weblate==4.3.1 True # expected fail in tests wheel pipx-1.15.0/testdata/tests_packages/unix-python3.10.txt000066400000000000000000000066531521706623000227500ustar00rootroot00000000000000PyYAML==5.3.1 QtPy==2.4.3 Send2Trash==1.8.3 Weblate==4.3.1 ansible==6.7.0 ansible_core==2.13.13 anyio==4.11.0 argcomplete==1.12.3 argcomplete==3.6.2 argon2_cffi==25.1.0 argon2_cffi_bindings==25.1.0 arrow==1.3.0 astroid==3.0.3 astroid==3.3.11 asttokens==3.0.0 async_lru==2.0.5 attrs==25.3.0 awscli==1.18.168 babel==2.17.0 backcall==0.2.0 backports.tarfile==1.2.0 beautifulsoup4==4.14.2 black==22.10.0 black==22.8.0 bleach==6.2.0 botocore==1.19.8 build==1.3.0 cachetools==6.2.0 certifi==2025.8.3 cffi==2.0.0 chardet==5.2.0 charset_normalizer==3.4.3 click==8.3.0 cmarkgfm==2024.11.20 colorama==0.4.3 colorama==0.4.6 colorlog==6.9.0 comm==0.2.3 cryptography==46.0.1 cython==3.1.4 debugpy==1.8.17 decorator==5.2.1 defusedxml==0.7.1 dill==0.4.0 distlib==0.4.0 docutils==0.15.2 docutils==0.22.2 exceptiongroup==1.3.0 executing==2.2.1 fastjsonschema==2.21.2 filelock==3.19.1 fqdn==1.5.1 h11==0.16.0 httpcore==1.0.9 httpx==0.28.1 id==1.5.0 idna==3.10 importlib_metadata==8.7.0 iniconfig==2.1.0 ipykernel==6.30.1 ipython==7.16.1 ipython==8.37.0 ipython_pygments_lexers==1.1.1 ipywidgets==8.1.7 isoduration==20.11.0 isort==5.13.2 isort==5.6.4 isort==6.0.1 jaraco.classes==3.4.0 jaraco.clipboard==2.0.1 jaraco.context==6.0.1 jaraco_functools==4.3.0 jedi==0.19.2 jeepney==0.9.0 jinja2==3.1.6 jmespath==0.10.0 json5==0.12.1 jsonpointer==3.0.0 jsonschema==4.25.1 jsonschema_specifications==2025.9.1 jupyter==1.0.0 jupyter_client==8.6.3 jupyter_console==6.6.3 jupyter_core==5.8.1 jupyter_events==0.12.0 jupyter_lsp==2.3.0 jupyter_server==2.17.0 jupyter_server_terminals==0.5.3 jupyterlab==4.4.9 jupyterlab_pygments==0.3.0 jupyterlab_server==2.27.3 jupyterlab_widgets==3.0.15 kaggle==1.6.11 keyring==25.6.0 lark==1.3.0 markdown_it_py==4.0.0 markupsafe==3.0.3 matplotlib_inline==0.1.7 mccabe==0.7.0 mdurl==0.1.2 mistune==3.1.4 more_itertools==10.8.0 mypy_extensions==1.1.0 nbclient==0.10.2 nbconvert==7.16.6 nbformat==5.10.4 nest_asyncio==1.6.0 nh3==0.3.0 notebook==7.4.7 notebook_shim==0.2.4 nox==2022.1.7 nox==2023.4.22 overrides==7.7.0 packaging==25.0 pandocfilters==1.5.1 parso==0.8.5 pathspec==0.12.1 pbr==5.6.0 pexpect==4.9.0 pickleshare==0.7.5 pip==23.3.2 pip==24.0 pip==25.2 platformdirs==4.4.0 pluggy==1.6.0 prometheus_client==0.23.1 prompt_toolkit==3.0.52 psutil==7.1.0 ptyprocess==0.7.0 pure_eval==0.2.3 py==1.11.0 pyasn1==0.6.1 pycowsay==0.0.0.2 pycparser==2.23 pygdbmi==0.10.0.0 pygments==2.19.2 pylint==3.0.4 pylint==3.3.8 pyperclip==1.11.0 pyproject_api==1.9.1 pyproject_hooks==1.2.0 pytest==8.4.2 python_dateutil==2.9.0.post0 python_json_logger==3.3.0 python_slugify==8.0.4 pyyaml==6.0.3 pyzmq==27.1.0 qtconsole==5.7.0 readme_renderer==44.0 referencing==0.36.2 requests==2.31.0 requests==2.32.5 requests_toolbelt==1.0.0 resolvelib==0.8.1 rfc3339_validator==0.1.4 rfc3986==2.0.0 rfc3986_validator==0.1.1 rfc3987_syntax==1.1.0 rich==14.1.0 rpds_py==0.27.1 rsa==4.5 s3transfer==0.3.7 secretstorage==3.4.0 setuptools==80.9.0 setuptools_scm==9.2.0 shell-functools==0.3.0 six==1.17.0 sniffio==1.3.1 soupsieve==2.8 stack_data==0.6.3 terminado==0.18.1 text_unidecode==1.3 tinycss2==1.4.0 tomli==2.2.1 tomlkit==0.13.3 tornado==6.5.2 tox==3.28.0 tox==4.30.2 tox_ini_fmt==0.5.0 tqdm==4.67.1 traitlets==5.14.3 twine==6.2.0 types_python_dateutil==2.9.0.20250822 typing_extensions==4.15.0 uri_template==1.3.0 urllib3==1.25.11 urllib3==2.5.0 virtualenv==20.34.0 wcwidth==0.2.14 webcolors==24.11.1 webencodings==0.5.1 websocket_client==1.8.0 wheel==0.45.1 widgetsnbextension==4.0.14 zest.releaser==9.1.2 zipp==3.23.0 pipx-1.15.0/testdata/tests_packages/unix-python3.11.txt000066400000000000000000000066071521706623000227500ustar00rootroot00000000000000PyYAML==5.3.1 QtPy==2.4.3 Send2Trash==1.8.3 Weblate==4.3.1 ansible==6.7.0 ansible_core==2.13.13 anyio==4.11.0 argcomplete==1.12.3 argcomplete==3.6.2 argon2_cffi==25.1.0 argon2_cffi_bindings==25.1.0 arrow==1.3.0 astroid==3.0.3 astroid==3.3.11 asttokens==3.0.0 async_lru==2.0.5 attrs==25.3.0 awscli==1.18.168 babel==2.17.0 backcall==0.2.0 backports.tarfile==1.2.0 beautifulsoup4==4.14.2 black==22.10.0 black==22.8.0 bleach==6.2.0 botocore==1.19.8 build==1.3.0 cachetools==6.2.0 certifi==2025.8.3 cffi==2.0.0 chardet==5.2.0 charset_normalizer==3.4.3 click==8.3.0 cmarkgfm==2024.11.20 colorama==0.4.3 colorama==0.4.6 colorlog==6.9.0 comm==0.2.3 cryptography==46.0.1 cython==3.1.4 debugpy==1.8.17 decorator==5.2.1 defusedxml==0.7.1 dill==0.4.0 distlib==0.4.0 docutils==0.15.2 docutils==0.22.2 executing==2.2.1 fastjsonschema==2.21.2 filelock==3.19.1 fqdn==1.5.1 h11==0.16.0 httpcore==1.0.9 httpx==0.28.1 id==1.5.0 idna==3.10 importlib_metadata==8.7.0 iniconfig==2.1.0 ipykernel==6.30.1 ipython==7.16.1 ipython==9.6.0 ipython_pygments_lexers==1.1.1 ipywidgets==8.1.7 isoduration==20.11.0 isort==5.13.2 isort==5.6.4 isort==6.0.1 jaraco.classes==3.4.0 jaraco.clipboard==2.0.1 jaraco.context==6.0.1 jaraco_functools==4.3.0 jedi==0.19.2 jeepney==0.9.0 jinja2==3.1.6 jmespath==0.10.0 json5==0.12.1 jsonpointer==3.0.0 jsonschema==4.25.1 jsonschema_specifications==2025.9.1 jupyter==1.0.0 jupyter_client==8.6.3 jupyter_console==6.6.3 jupyter_core==5.8.1 jupyter_events==0.12.0 jupyter_lsp==2.3.0 jupyter_server==2.17.0 jupyter_server_terminals==0.5.3 jupyterlab==4.4.9 jupyterlab_pygments==0.3.0 jupyterlab_server==2.27.3 jupyterlab_widgets==3.0.15 kaggle==1.6.11 keyring==25.6.0 lark==1.3.0 markdown_it_py==4.0.0 markupsafe==3.0.3 matplotlib_inline==0.1.7 mccabe==0.7.0 mdurl==0.1.2 mistune==3.1.4 more_itertools==10.8.0 mypy_extensions==1.1.0 nbclient==0.10.2 nbconvert==7.16.6 nbformat==5.10.4 nest_asyncio==1.6.0 nh3==0.3.0 notebook==7.4.7 notebook_shim==0.2.4 nox==2022.1.7 nox==2023.4.22 overrides==7.7.0 packaging==25.0 pandocfilters==1.5.1 parso==0.8.5 pathspec==0.12.1 pbr==5.6.0 pexpect==4.9.0 pickleshare==0.7.5 pip==23.3.2 pip==24.0 pip==25.2 platformdirs==4.4.0 pluggy==1.6.0 prometheus_client==0.23.1 prompt_toolkit==3.0.52 psutil==7.1.0 ptyprocess==0.7.0 pure_eval==0.2.3 py==1.11.0 pyasn1==0.6.1 pycowsay==0.0.0.2 pycparser==2.23 pygdbmi==0.10.0.0 pygments==2.19.2 pylint==3.0.4 pylint==3.3.8 pyperclip==1.11.0 pyproject_api==1.9.1 pyproject_hooks==1.2.0 pytest==8.4.2 python_dateutil==2.9.0.post0 python_json_logger==3.3.0 python_slugify==8.0.4 pyyaml==6.0.3 pyzmq==27.1.0 qtconsole==5.7.0 readme_renderer==44.0 referencing==0.36.2 requests==2.31.0 requests==2.32.5 requests_toolbelt==1.0.0 resolvelib==0.8.1 rfc3339_validator==0.1.4 rfc3986==2.0.0 rfc3986_validator==0.1.1 rfc3987_syntax==1.1.0 rich==14.1.0 rpds_py==0.27.1 rsa==4.5 s3transfer==0.3.7 secretstorage==3.4.0 setuptools==80.9.0 setuptools_scm==9.2.0 shell-functools==0.3.0 six==1.17.0 sniffio==1.3.1 soupsieve==2.8 stack_data==0.6.3 terminado==0.18.1 text_unidecode==1.3 tinycss2==1.4.0 tomlkit==0.13.3 tornado==6.5.2 tox==3.28.0 tox==4.30.2 tox_ini_fmt==0.5.0 tqdm==4.67.1 traitlets==5.14.3 twine==6.2.0 types_python_dateutil==2.9.0.20250822 typing_extensions==4.15.0 uri_template==1.3.0 urllib3==1.25.11 urllib3==2.5.0 virtualenv==20.34.0 wcwidth==0.2.14 webcolors==24.11.1 webencodings==0.5.1 websocket_client==1.8.0 wheel==0.45.1 widgetsnbextension==4.0.14 zest.releaser==9.1.2 zipp==3.23.0 pipx-1.15.0/testdata/tests_packages/unix-python3.12.txt000066400000000000000000000064661521706623000227540ustar00rootroot00000000000000PyYAML==5.3.1 QtPy==2.4.3 Send2Trash==1.8.3 Weblate==4.3.1 ansible==6.7.0 ansible_core==2.13.13 anyio==4.11.0 argcomplete==1.12.3 argcomplete==3.6.2 argon2_cffi==25.1.0 argon2_cffi_bindings==25.1.0 arrow==1.3.0 astroid==3.0.3 astroid==3.3.11 asttokens==3.0.0 async_lru==2.0.5 attrs==25.3.0 awscli==1.18.168 babel==2.17.0 backcall==0.2.0 beautifulsoup4==4.14.2 black==22.10.0 black==22.8.0 bleach==6.2.0 botocore==1.19.8 build==1.3.0 cachetools==6.2.0 certifi==2025.8.3 cffi==2.0.0 chardet==5.2.0 charset_normalizer==3.4.3 click==8.3.0 cmarkgfm==2024.11.20 colorama==0.4.3 colorama==0.4.6 colorlog==6.9.0 comm==0.2.3 cryptography==46.0.1 cython==3.1.4 debugpy==1.8.17 decorator==5.2.1 defusedxml==0.7.1 dill==0.4.0 distlib==0.4.0 docutils==0.15.2 docutils==0.22.2 executing==2.2.1 fastjsonschema==2.21.2 filelock==3.19.1 fqdn==1.5.1 h11==0.16.0 httpcore==1.0.9 httpx==0.28.1 id==1.5.0 idna==3.10 iniconfig==2.1.0 ipykernel==6.30.1 ipython==7.16.1 ipython==9.6.0 ipython_pygments_lexers==1.1.1 ipywidgets==8.1.7 isoduration==20.11.0 isort==5.13.2 isort==5.6.4 isort==6.0.1 jaraco.classes==3.4.0 jaraco.clipboard==2.0.1 jaraco.context==6.0.1 jaraco_functools==4.3.0 jedi==0.19.2 jeepney==0.9.0 jinja2==3.1.6 jmespath==0.10.0 json5==0.12.1 jsonpointer==3.0.0 jsonschema==4.25.1 jsonschema_specifications==2025.9.1 jupyter==1.0.0 jupyter_client==8.6.3 jupyter_console==6.6.3 jupyter_core==5.8.1 jupyter_events==0.12.0 jupyter_lsp==2.3.0 jupyter_server==2.17.0 jupyter_server_terminals==0.5.3 jupyterlab==4.4.9 jupyterlab_pygments==0.3.0 jupyterlab_server==2.27.3 jupyterlab_widgets==3.0.15 kaggle==1.6.11 keyring==25.6.0 lark==1.3.0 markdown_it_py==4.0.0 markupsafe==3.0.3 matplotlib_inline==0.1.7 mccabe==0.7.0 mdurl==0.1.2 mistune==3.1.4 more_itertools==10.8.0 mypy_extensions==1.1.0 nbclient==0.10.2 nbconvert==7.16.6 nbformat==5.10.4 nest_asyncio==1.6.0 nh3==0.3.0 notebook==7.4.7 notebook_shim==0.2.4 nox==2022.1.7 nox==2023.4.22 packaging==25.0 pandocfilters==1.5.1 parso==0.8.5 pathspec==0.12.1 pbr==5.6.0 pexpect==4.9.0 pickleshare==0.7.5 pip==23.3.2 pip==24.0 pip==25.2 platformdirs==4.4.0 pluggy==1.6.0 prometheus_client==0.23.1 prompt_toolkit==3.0.52 psutil==7.1.0 ptyprocess==0.7.0 pure_eval==0.2.3 py==1.11.0 pyasn1==0.6.1 pycowsay==0.0.0.2 pycparser==2.23 pygdbmi==0.10.0.0 pygments==2.19.2 pylint==3.0.4 pylint==3.3.8 pyperclip==1.11.0 pyproject_api==1.9.1 pyproject_hooks==1.2.0 pytest==8.4.2 python_dateutil==2.9.0.post0 python_json_logger==3.3.0 python_slugify==8.0.4 pyyaml==6.0.3 pyzmq==27.1.0 qtconsole==5.7.0 readme_renderer==44.0 referencing==0.36.2 requests==2.31.0 requests==2.32.5 requests_toolbelt==1.0.0 resolvelib==0.8.1 rfc3339_validator==0.1.4 rfc3986==2.0.0 rfc3986_validator==0.1.1 rfc3987_syntax==1.1.0 rich==14.1.0 rpds_py==0.27.1 rsa==4.5 s3transfer==0.3.7 secretstorage==3.4.0 setuptools==80.9.0 setuptools_scm==9.2.0 shell-functools==0.3.0 six==1.17.0 sniffio==1.3.1 soupsieve==2.8 stack_data==0.6.3 terminado==0.18.1 text_unidecode==1.3 tinycss2==1.4.0 tomlkit==0.13.3 tornado==6.5.2 tox==3.28.0 tox==4.30.2 tox_ini_fmt==0.5.0 tqdm==4.67.1 traitlets==5.14.3 twine==6.2.0 types_python_dateutil==2.9.0.20250822 typing_extensions==4.15.0 uri_template==1.3.0 urllib3==1.25.11 urllib3==2.5.0 virtualenv==20.34.0 wcwidth==0.2.14 webcolors==24.11.1 webencodings==0.5.1 websocket_client==1.8.0 wheel==0.45.1 widgetsnbextension==4.0.14 zest.releaser==9.1.2 pipx-1.15.0/testdata/tests_packages/unix-python3.13.txt000066400000000000000000000064661521706623000227550ustar00rootroot00000000000000PyYAML==5.3.1 QtPy==2.4.3 Send2Trash==1.8.3 Weblate==4.3.1 ansible==6.7.0 ansible_core==2.13.13 anyio==4.11.0 argcomplete==1.12.3 argcomplete==3.6.2 argon2_cffi==25.1.0 argon2_cffi_bindings==25.1.0 arrow==1.3.0 astroid==3.0.3 astroid==3.3.11 asttokens==3.0.0 async_lru==2.0.5 attrs==25.3.0 awscli==1.18.168 babel==2.17.0 backcall==0.2.0 beautifulsoup4==4.14.2 black==22.10.0 black==22.8.0 bleach==6.2.0 botocore==1.19.8 build==1.3.0 cachetools==6.2.0 certifi==2025.8.3 cffi==2.0.0 chardet==5.2.0 charset_normalizer==3.4.3 click==8.3.0 cmarkgfm==2024.11.20 colorama==0.4.3 colorama==0.4.6 colorlog==6.9.0 comm==0.2.3 cryptography==46.0.1 cython==3.1.4 debugpy==1.8.17 decorator==5.2.1 defusedxml==0.7.1 dill==0.4.0 distlib==0.4.0 docutils==0.15.2 docutils==0.22.2 executing==2.2.1 fastjsonschema==2.21.2 filelock==3.19.1 fqdn==1.5.1 h11==0.16.0 httpcore==1.0.9 httpx==0.28.1 id==1.5.0 idna==3.10 iniconfig==2.1.0 ipykernel==6.30.1 ipython==7.16.1 ipython==9.6.0 ipython_pygments_lexers==1.1.1 ipywidgets==8.1.7 isoduration==20.11.0 isort==5.13.2 isort==5.6.4 isort==6.0.1 jaraco.classes==3.4.0 jaraco.clipboard==2.0.1 jaraco.context==6.0.1 jaraco_functools==4.3.0 jedi==0.19.2 jeepney==0.9.0 jinja2==3.1.6 jmespath==0.10.0 json5==0.12.1 jsonpointer==3.0.0 jsonschema==4.25.1 jsonschema_specifications==2025.9.1 jupyter==1.0.0 jupyter_client==8.6.3 jupyter_console==6.6.3 jupyter_core==5.8.1 jupyter_events==0.12.0 jupyter_lsp==2.3.0 jupyter_server==2.17.0 jupyter_server_terminals==0.5.3 jupyterlab==4.4.9 jupyterlab_pygments==0.3.0 jupyterlab_server==2.27.3 jupyterlab_widgets==3.0.15 kaggle==1.6.11 keyring==25.6.0 lark==1.3.0 markdown_it_py==4.0.0 markupsafe==3.0.3 matplotlib_inline==0.1.7 mccabe==0.7.0 mdurl==0.1.2 mistune==3.1.4 more_itertools==10.8.0 mypy_extensions==1.1.0 nbclient==0.10.2 nbconvert==7.16.6 nbformat==5.10.4 nest_asyncio==1.6.0 nh3==0.3.0 notebook==7.4.7 notebook_shim==0.2.4 nox==2022.1.7 nox==2023.4.22 packaging==25.0 pandocfilters==1.5.1 parso==0.8.5 pathspec==0.12.1 pbr==5.6.0 pexpect==4.9.0 pickleshare==0.7.5 pip==23.3.2 pip==24.0 pip==25.2 platformdirs==4.4.0 pluggy==1.6.0 prometheus_client==0.23.1 prompt_toolkit==3.0.52 psutil==7.1.0 ptyprocess==0.7.0 pure_eval==0.2.3 py==1.11.0 pyasn1==0.6.1 pycowsay==0.0.0.2 pycparser==2.23 pygdbmi==0.10.0.0 pygments==2.19.2 pylint==3.0.4 pylint==3.3.8 pyperclip==1.11.0 pyproject_api==1.9.1 pyproject_hooks==1.2.0 pytest==8.4.2 python_dateutil==2.9.0.post0 python_json_logger==3.3.0 python_slugify==8.0.4 pyyaml==6.0.3 pyzmq==27.1.0 qtconsole==5.7.0 readme_renderer==44.0 referencing==0.36.2 requests==2.31.0 requests==2.32.5 requests_toolbelt==1.0.0 resolvelib==0.8.1 rfc3339_validator==0.1.4 rfc3986==2.0.0 rfc3986_validator==0.1.1 rfc3987_syntax==1.1.0 rich==14.1.0 rpds_py==0.27.1 rsa==4.5 s3transfer==0.3.7 secretstorage==3.4.0 setuptools==80.9.0 setuptools_scm==9.2.0 shell-functools==0.3.0 six==1.17.0 sniffio==1.3.1 soupsieve==2.8 stack_data==0.6.3 terminado==0.18.1 text_unidecode==1.3 tinycss2==1.4.0 tomlkit==0.13.3 tornado==6.5.2 tox==3.28.0 tox==4.30.2 tox_ini_fmt==0.5.0 tqdm==4.67.1 traitlets==5.14.3 twine==6.2.0 types_python_dateutil==2.9.0.20250822 typing_extensions==4.15.0 uri_template==1.3.0 urllib3==1.25.11 urllib3==2.5.0 virtualenv==20.34.0 wcwidth==0.2.14 webcolors==24.11.1 webencodings==0.5.1 websocket_client==1.8.0 wheel==0.45.1 widgetsnbextension==4.0.14 zest.releaser==9.1.2 pipx-1.15.0/testdata/tests_packages/unix-python3.9.txt000066400000000000000000000066531521706623000227000ustar00rootroot00000000000000PyYAML==5.3.1 QtPy==2.4.3 SecretStorage==3.3.3 Send2Trash==1.8.3 Weblate==4.3.1 ansible==6.7.0 ansible_core==2.13.13 anyio==4.11.0 argcomplete==1.12.3 argcomplete==3.6.2 argon2_cffi==25.1.0 argon2_cffi_bindings==25.1.0 arrow==1.3.0 astroid==3.0.3 astroid==3.3.11 asttokens==3.0.0 async_lru==2.0.5 attrs==25.3.0 awscli==1.18.168 babel==2.17.0 backcall==0.2.0 backports.tarfile==1.2.0 beautifulsoup4==4.14.2 black==22.10.0 black==22.8.0 bleach==6.2.0 botocore==1.19.8 build==1.3.0 cachetools==6.2.0 certifi==2025.8.3 cffi==2.0.0 chardet==5.2.0 charset_normalizer==3.4.3 click==8.1.8 cmarkgfm==2024.11.20 colorama==0.4.3 colorama==0.4.6 colorlog==6.9.0 comm==0.2.3 cryptography==46.0.1 cython==3.1.4 debugpy==1.8.17 decorator==5.2.1 defusedxml==0.7.1 dill==0.4.0 distlib==0.4.0 docutils==0.15.2 docutils==0.22.2 exceptiongroup==1.3.0 executing==2.2.1 fastjsonschema==2.21.2 filelock==3.19.1 fqdn==1.5.1 h11==0.16.0 httpcore==1.0.9 httpx==0.28.1 id==1.5.0 idna==3.10 importlib_metadata==8.7.0 iniconfig==2.1.0 ipykernel==6.30.1 ipython==7.16.1 ipython==8.18.1 ipython_pygments_lexers==1.1.1 ipywidgets==8.1.7 isoduration==20.11.0 isort==5.13.2 isort==5.6.4 isort==6.0.1 jaraco.classes==3.4.0 jaraco.clipboard==2.0.1 jaraco.context==6.0.1 jaraco_functools==4.3.0 jedi==0.19.2 jeepney==0.9.0 jinja2==3.1.6 jmespath==0.10.0 json5==0.12.1 jsonpointer==3.0.0 jsonschema==4.25.1 jsonschema_specifications==2025.9.1 jupyter==1.0.0 jupyter_client==8.6.3 jupyter_console==6.6.3 jupyter_core==5.8.1 jupyter_events==0.12.0 jupyter_lsp==2.3.0 jupyter_server==2.17.0 jupyter_server_terminals==0.5.3 jupyterlab==4.4.9 jupyterlab_pygments==0.3.0 jupyterlab_server==2.27.3 jupyterlab_widgets==3.0.15 kaggle==1.6.11 keyring==25.6.0 lark==1.3.0 markdown_it_py==3.0.0 markupsafe==3.0.3 matplotlib_inline==0.1.7 mccabe==0.7.0 mdurl==0.1.2 mistune==3.1.4 more_itertools==10.8.0 mypy_extensions==1.1.0 nbclient==0.10.2 nbconvert==7.16.6 nbformat==5.10.4 nest_asyncio==1.6.0 nh3==0.3.0 notebook==7.4.7 notebook_shim==0.2.4 nox==2022.1.7 nox==2023.4.22 overrides==7.7.0 packaging==25.0 pandocfilters==1.5.1 parso==0.8.5 pathspec==0.12.1 pbr==5.6.0 pexpect==4.9.0 pickleshare==0.7.5 pip==23.3.2 pip==24.0 pip==25.2 platformdirs==4.4.0 pluggy==1.6.0 prometheus_client==0.23.1 prompt_toolkit==3.0.52 psutil==7.1.0 ptyprocess==0.7.0 pure_eval==0.2.3 py==1.11.0 pyasn1==0.6.1 pycowsay==0.0.0.2 pycparser==2.23 pygdbmi==0.10.0.0 pygments==2.19.2 pylint==3.0.4 pylint==3.3.8 pyperclip==1.11.0 pyproject_api==1.9.1 pyproject_hooks==1.2.0 pytest==8.4.2 python_dateutil==2.9.0.post0 python_json_logger==3.3.0 python_slugify==8.0.4 pyyaml==6.0.3 pyzmq==27.1.0 qtconsole==5.7.0 readme_renderer==44.0 referencing==0.36.2 requests==2.31.0 requests==2.32.5 requests_toolbelt==1.0.0 resolvelib==0.8.1 rfc3339_validator==0.1.4 rfc3986==2.0.0 rfc3986_validator==0.1.1 rfc3987_syntax==1.1.0 rich==14.1.0 rpds_py==0.27.1 rsa==4.5 s3transfer==0.3.7 setuptools==80.9.0 setuptools_scm==9.2.0 shell-functools==0.3.0 six==1.17.0 sniffio==1.3.1 soupsieve==2.8 stack_data==0.6.3 terminado==0.18.1 text_unidecode==1.3 tinycss2==1.4.0 tomli==2.2.1 tomlkit==0.13.3 tornado==6.5.2 tox==3.28.0 tox==4.30.2 tox_ini_fmt==0.5.0 tqdm==4.67.1 traitlets==5.14.3 twine==6.2.0 types_python_dateutil==2.9.0.20250822 typing_extensions==4.15.0 uri_template==1.3.0 urllib3==1.25.11 urllib3==2.5.0 virtualenv==20.34.0 wcwidth==0.2.14 webcolors==24.11.1 webencodings==0.5.1 websocket_client==1.8.0 wheel==0.45.1 widgetsnbextension==4.0.14 zest.releaser==9.1.2 zipp==3.23.0 pipx-1.15.0/testdata/tests_packages/win-python3.13.txt000066400000000000000000000072441521706623000225620ustar00rootroot00000000000000PyYAML==5.3.1 QtPy==2.4.3 Send2Trash==1.8.3 Weblate==4.3.1 ansible==6.7.0 ansible_core==2.13.13 anyio==4.11.0 argcomplete==1.12.3 argcomplete==3.6.2 argon2_cffi==25.1.0 argon2_cffi_bindings==25.1.0 arrow==1.3.0 astroid==3.0.3 astroid==3.3.11 asttokens==3.0.0 async_lru==2.0.5 attrs==25.3.0 autocommand==2.2.2 awscli==1.18.168 babel==2.17.0 backcall==0.2.0 beautifulsoup4==4.14.2 black==22.10.0 black==22.8.0 bleach==6.2.0 botocore==1.19.8 build==1.3.0 cachetools==6.2.0 certifi==2025.8.3 cffi==2.0.0 chardet==5.2.0 charset_normalizer==3.4.3 click==8.3.0 cmarkgfm==2024.11.20 colorama==0.4.3 colorama==0.4.6 colorlog==6.9.0 comm==0.2.3 cryptography==46.0.1 cython==3.1.4 debugpy==1.8.17 decorator==5.2.1 defusedxml==0.7.1 dill==0.4.0 distlib==0.4.0 docutils==0.15.2 docutils==0.22.2 executing==2.2.1 fastjsonschema==2.21.2 filelock==3.19.1 fqdn==1.5.1 h11==0.16.0 httpcore==1.0.9 httpx==0.28.1 id==1.5.0 idna==3.10 iniconfig==2.1.0 ipykernel==6.30.1 ipython==7.16.1 ipython==9.6.0 ipython_pygments_lexers==1.1.1 ipywidgets==8.1.7 isoduration==20.11.0 isort==5.13.2 isort==5.6.4 isort==6.0.1 jaraco.classes==3.4.0 jaraco.clipboard==2.0.1 jaraco.context==6.0.1 jaraco.structures==2.2.0 jaraco.text==4.0.0 jaraco.ui==2.4.0 jaraco_collections==5.2.1 jaraco_functools==4.3.0 jaraco_windows==5.10.0 jedi==0.19.2 jinja2==3.1.6 jmespath==0.10.0 json5==0.12.1 jsonpointer==3.0.0 jsonschema==4.25.1 jsonschema_specifications==2025.9.1 jupyter==1.0.0 jupyter_client==8.6.3 jupyter_console==6.6.3 jupyter_core==5.8.1 jupyter_events==0.12.0 jupyter_lsp==2.3.0 jupyter_server==2.17.0 jupyter_server_terminals==0.5.3 jupyterlab==4.4.9 jupyterlab_pygments==0.3.0 jupyterlab_server==2.27.3 jupyterlab_widgets==3.0.15 kaggle==1.6.11 keyring==25.6.0 lark==1.3.0 markdown_it_py==4.0.0 markupsafe==3.0.3 matplotlib_inline==0.1.7 mccabe==0.7.0 mdurl==0.1.2 mistune==3.1.4 more_itertools==10.8.0 mypy_extensions==1.1.0 named==1.4.2 nbclient==0.10.2 nbconvert==7.16.6 nbformat==5.10.4 nest_asyncio==1.6.0 nh3==0.3.0 notebook==7.4.7 notebook_shim==0.2.4 nox==2022.1.7 nox==2023.4.22 packaging==25.0 pandocfilters==1.5.1 parso==0.8.5 path==17.1.1 pathspec==0.12.1 pbr==5.6.0 pickleshare==0.7.5 pip==23.3.2 pip==24.0 pip==25.2 platformdirs==4.4.0 pluggy==1.6.0 prometheus_client==0.23.1 prompt_toolkit==3.0.52 psutil==7.1.0 pure_eval==0.2.3 py==1.11.0 pyasn1==0.6.1 pycowsay==0.0.0.2 pycparser==2.23 pygdbmi==0.10.0.0 pygments==2.19.2 pylint==3.0.4 pylint==3.3.8 pyproject_api==1.9.1 pyproject_hooks==1.2.0 pytest==8.4.2 python_dateutil==2.9.0.post0 python_json_logger==3.3.0 python_slugify==8.0.4 pywin32==311 pywin32_ctypes==0.2.3 pywinpty==3.0.0 pyyaml==6.0.3 pyzmq==27.1.0 qtconsole==5.7.0 readme_renderer==44.0 referencing==0.36.2 requests==2.31.0 requests==2.32.5 requests_toolbelt==1.0.0 resolvelib==0.8.1 rfc3339_validator==0.1.4 rfc3986==2.0.0 rfc3986_validator==0.1.1 rfc3987_syntax==1.1.0 rich==14.1.0 rpds_py==0.27.1 rsa==4.5 s3transfer==0.3.7 setuptools==80.9.0 setuptools_scm==9.2.0 shell-functools==0.3.0 shellingham==1.5.4 six==1.17.0 sniffio==1.3.1 soupsieve==2.8 stack_data==0.6.3 tempora==5.8.1 terminado==0.18.1 text_unidecode==1.3 tinycss2==1.4.0 tomlkit==0.13.3 tornado==6.5.2 tox==3.28.0 tox==4.30.2 tox_ini_fmt==0.5.0 tqdm==4.67.1 traitlets==5.14.3 twine==6.2.0 typer==0.19.2 types_python_dateutil==2.9.0.20250822 typing_extensions==4.15.0 uri_template==1.3.0 urllib3==1.25.11 urllib3==2.5.0 virtualenv==20.34.0 wcwidth==0.2.14 webcolors==24.11.1 webencodings==0.5.1 websocket_client==1.8.0 wheel==0.45.1 widgetsnbextension==4.0.14 zest.releaser==9.1.2 pipx-1.15.0/tests/000077500000000000000000000000001521706623000137225ustar00rootroot00000000000000pipx-1.15.0/tests/conftest.py000066400000000000000000000247151521706623000161320ustar00rootroot00000000000000import importlib import json import os import shutil import socket import subprocess import sys from collections.abc import Iterator from contextlib import closing from http import HTTPStatus from pathlib import Path from urllib.error import HTTPError, URLError from urllib.request import urlopen import pytest from helpers import WIN from pipx import commands, interpreter, paths, shared_libs, standalone_python, venv from pipx.backends import get_backend from pipx.backends import pip as _pip_backend_module from pipx.backends import uv as _uv_backend_module from pipx.backends.uv import find_uv_binary from pipx.venv import reset_backend_override_warnings # ``pipx.commands.__init__`` re-exports ``upgrade`` (the function), which # shadows the submodule on the package. ``import_module`` returns the module # regardless. _upgrade_module = importlib.import_module("pipx.commands.upgrade") PIPX_TESTS_DIR = Path(".pipx_tests") PIPX_TESTS_PACKAGE_LIST_DIR = Path("testdata/tests_packages") @pytest.fixture(scope="session") def root() -> Path: return Path(__file__).parents[1] @pytest.fixture(autouse=True) def _backend_test_baseline(monkeypatch: pytest.MonkeyPatch) -> None: """Pin every test to pip with empty backend caches. Without the env pin, unit tests that build a ``Venv`` outside the ``pipx_temp_env`` fixture would auto-detect uv from CI's PATH and fork a real ``uv --version`` probe. Cache resets stop the previous test's monkeypatched ``shutil.which`` from poisoning this one. Uv-backend tests opt back in with ``--backend uv``. """ monkeypatch.setenv("PIPX_DEFAULT_BACKEND", "pip") find_uv_binary.cache_clear() _uv_backend_module._check_uv_version.cache_clear() get_backend.cache_clear() reset_backend_override_warnings() @pytest.fixture() def mocked_github_api(monkeypatch, root): """ Fixture to replace the github index with a local copy, to prevent unit tests from exceeding github's API request limit. """ with open(root / "testdata" / "standalone_python_index_20250818.json") as f: index = json.load(f) monkeypatch.setattr(standalone_python, "get_or_update_index", lambda _: index) def pytest_addoption(parser): parser.addoption( "--all-packages", action="store_true", dest="all_packages", default=False, help="Run only the long, slow tests installing the maximum list of packages.", ) parser.addoption( "--net-pypiserver", action="store_true", dest="net_pypiserver", default=False, help="Start local pypi server and use in tests.", ) def pytest_configure(config): markexpr = getattr(config.option, "markexpr", "") if config.option.all_packages: new_markexpr = (f"{markexpr} or " if markexpr else "") + "all_packages" else: new_markexpr = (f"{markexpr} and " if markexpr else "") + "not all_packages" config.option.markexpr = new_markexpr def pipx_temp_env_helper(pipx_shared_dir, tmp_path, monkeypatch, request, utils_temp_dir, pypi): home_dir = Path(tmp_path) / "subdir" / "pipxhome" bin_dir = Path(tmp_path) / "otherdir" / "pipxbindir" man_dir = Path(tmp_path) / "otherdir" / "pipxmandir" global_home_dir = Path(tmp_path) / "global" / "pipxhome" global_bin_dir = Path(tmp_path) / "global_otherdir" / "pipxbindir" global_man_dir = Path(tmp_path) / "global_otherdir" / "pipxmandir" # Patch in test specific paths monkeypatch.setattr(paths, "OVERRIDE_PIPX_HOME", home_dir) monkeypatch.setattr(paths, "OVERRIDE_PIPX_BIN_DIR", bin_dir) monkeypatch.setattr(paths, "OVERRIDE_PIPX_MAN_DIR", man_dir) monkeypatch.setattr(paths, "OVERRIDE_PIPX_SHARED_LIBS", pipx_shared_dir) monkeypatch.setattr(paths, "OVERRIDE_PIPX_GLOBAL_HOME", global_home_dir) monkeypatch.setattr(paths, "OVERRIDE_PIPX_GLOBAL_BIN_DIR", global_bin_dir) monkeypatch.setattr(paths, "OVERRIDE_PIPX_GLOBAL_MAN_DIR", global_man_dir) # Refresh paths.ctx to commit the overrides paths.ctx.make_local() # Each consumer holds its own ``shared_libs`` reference (via # ``from pipx.shared_libs import shared_libs``); patch every importer. monkeypatch.setattr(shared_libs, "shared_libs", shared_libs._SharedLibs()) monkeypatch.setattr(venv, "shared_libs", shared_libs.shared_libs) monkeypatch.setattr(_pip_backend_module, "shared_libs", shared_libs.shared_libs) monkeypatch.setattr(_upgrade_module, "shared_libs", shared_libs.shared_libs) monkeypatch.setattr(interpreter, "DEFAULT_PYTHON", sys.executable) if "PIPX_DEFAULT_PYTHON" in os.environ: monkeypatch.delenv("PIPX_DEFAULT_PYTHON") # CI runners ship uv on PATH; pin every legacy test to pip so the auto- # detect doesn't silently flip them. Uv-backend tests opt in with --backend uv. monkeypatch.setenv("PIPX_DEFAULT_BACKEND", "pip") # macOS needs /usr/bin in PATH to compile certain packages, but # applications in /usr/bin cause test_install.py tests to raise warnings # which make tests fail (e.g. on Github ansible apps exist in /usr/bin) monkeypatch.setenv("PATH_ORIG", str(paths.ctx.bin_dir) + os.pathsep + os.environ["PATH"]) monkeypatch.setenv("PATH_TEST", str(paths.ctx.bin_dir)) monkeypatch.setenv("PATH", str(paths.ctx.bin_dir) + os.pathsep + str(utils_temp_dir)) # On Windows, monkeypatch pipx.commands.common._can_symlink_cache to # indicate that paths.ctx.bin_dir and paths.ctx.man_dir # cannot use symlinks, even if we're running as administrator and # symlinks are actually possible. if WIN: monkeypatch.setitem(commands.common._can_symlink_cache, paths.ctx.bin_dir, False) monkeypatch.setitem(commands.common._can_symlink_cache, paths.ctx.man_dir, False) if not request.config.option.net_pypiserver: # IMPORTANT: use 127.0.0.1 not localhost # Using localhost on Windows creates enormous slowdowns # (for some reason--perhaps IPV6/IPV4 tries, timeouts?) monkeypatch.setenv("PIP_INDEX_URL", pypi) # uv ignores PIP_INDEX_URL; without UV_INDEX_URL the uv-backend tests # would hit real PyPI on CI. monkeypatch.setenv("UV_INDEX_URL", pypi) @pytest.fixture(scope="session", autouse=True) def pipx_local_pypiserver(request, root: Path, tmp_path_factory) -> Iterator[str]: """Starts local pypiserver once per session unless --net-pypiserver was passed to pytest""" if request.config.option.net_pypiserver: # need both yield and return because other codepath has both yield "" return pipx_cache_dir = root / PIPX_TESTS_DIR / "package_cache" check_test_packages_cmd = [ sys.executable, "scripts/update_package_cache.py", "--check-only", str(PIPX_TESTS_PACKAGE_LIST_DIR), str(pipx_cache_dir), ] update_test_packages_cmd = [ sys.executable, "scripts/update_package_cache.py", str(PIPX_TESTS_PACKAGE_LIST_DIR), str(pipx_cache_dir), ] check_test_packages_process = subprocess.run(check_test_packages_cmd, check=False, cwd=root) if check_test_packages_process.returncode != 0: subprocess.run(update_test_packages_cmd, check=True, cwd=root) def find_free_port(): with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: s.bind(("", 0)) return s.getsockname()[1] server_log = tmp_path_factory.mktemp("log") / "pypiserver.log" if server_log.exists(): server_log.unlink() port = find_free_port() os.environ["NO_PROXY"] = "127.0.0.1" cache = str(pipx_cache_dir / f"{sys.version_info[0]}.{sys.version_info[1]}") server = str(Path(sys.executable).parent / "pypi-server") cmd = [ server, "run", "--verbose", "--disable-fallback", "--backend", "cached-dir", "--cache-control=3600", "--host", "127.0.0.1", "--port", str(port), cache, ] cmd += ["--log-file", str(server_log)] pypiserver_process = subprocess.Popen(cmd, cwd=root) url = f"http://127.0.0.1:{port}/simple/" while True: try: with urlopen(url) as response: if response.code == HTTPStatus.OK: break except (URLError, HTTPError): continue yield url pypiserver_process.terminate() @pytest.fixture(scope="session") def pipx_session_shared_dir(tmp_path_factory): """Makes a temporary pipx shared libs directory only once per session""" return tmp_path_factory.mktemp("session_shareddir") @pytest.fixture(scope="session") def utils_temp_dir(tmp_path_factory): tmp_path = tmp_path_factory.mktemp("session_utilstempdir") required = ["git"] optional = ["uv"] # exposed only when present so the uv-backend smoke test can find it for util in required: at_path = shutil.which(util) assert at_path is not None util_path = Path(at_path) try: (tmp_path / util_path.name).symlink_to(util_path) except FileExistsError: pass for util in optional: at_path = shutil.which(util) if at_path is None: continue util_path = Path(at_path) try: (tmp_path / util_path.name).symlink_to(util_path) except FileExistsError: pass return tmp_path @pytest.fixture def pipx_temp_env(tmp_path, monkeypatch, pipx_session_shared_dir, request, utils_temp_dir, pipx_local_pypiserver): """Sets up temporary paths for pipx to install into. Shared libs are setup once per session, all other pipx dirs, constants are recreated for every test function. Also adds environment variables as necessary to make pip installations seamless. """ pipx_temp_env_helper(pipx_session_shared_dir, tmp_path, monkeypatch, request, utils_temp_dir, pipx_local_pypiserver) yield monkeypatch.undo() paths.ctx.make_local() @pytest.fixture def pipx_ultra_temp_env(tmp_path, monkeypatch, request, utils_temp_dir, pipx_local_pypiserver): """Sets up temporary paths for pipx to install into. Fully temporary environment, every test function starts as if pipx has never been run before, including empty shared libs directory. Also adds environment variables as necessary to make pip installations seamless. """ shared_dir = Path(tmp_path) / "shareddir" pipx_temp_env_helper(shared_dir, tmp_path, monkeypatch, request, utils_temp_dir, pipx_local_pypiserver) yield monkeypatch.undo() paths.ctx.make_local() pipx-1.15.0/tests/helpers.py000066400000000000000000000172621521706623000157460ustar00rootroot00000000000000import json import os import re import sys from dataclasses import replace from pathlib import Path from typing import Any from unittest import mock import pytest from packaging.utils import canonicalize_name from package_info import PKG from pipx import constants, main, paths, pipx_metadata_file, util WIN = sys.platform.startswith("win") PIPX_METADATA_LEGACY_VERSIONS = [None, "0.1", "0.2", "0.3"] MOCK_PIPXMETADATA_0_1: dict[str, Any] = { "main_package": None, "python_version": None, "venv_args": [], "injected_packages": {}, "pipx_metadata_version": "0.1", } MOCK_PIPXMETADATA_0_2: dict[str, Any] = { "main_package": None, "python_version": None, "venv_args": [], "injected_packages": {}, "pipx_metadata_version": "0.2", } MOCK_PIPXMETADATA_0_3: dict[str, Any] = { "main_package": None, "python_version": None, "venv_args": [], "injected_packages": {}, "pipx_metadata_version": "0.3", "man_pages": [], "man_paths": [], "man_pages_of_dependencies": [], "man_paths_of_dependencies": {}, } MOCK_PACKAGE_INFO_0_1: dict[str, Any] = { "package": None, "package_or_url": None, "pip_args": [], "include_dependencies": False, "include_apps": True, "apps": [], "app_paths": [], "apps_of_dependencies": [], "app_paths_of_dependencies": {}, "package_version": "", } MOCK_PACKAGE_INFO_0_2: dict[str, Any] = { "package": None, "package_or_url": None, "pip_args": [], "include_dependencies": False, "include_apps": True, "apps": [], "app_paths": [], "apps_of_dependencies": [], "app_paths_of_dependencies": {}, "package_version": "", "suffix": "", } def app_name(app: str) -> str: return f"{app}.exe" if WIN else app def run_pipx_cli(pipx_args: list[str]) -> int: with mock.patch.object(sys, "argv", ["pipx"] + pipx_args): return main.cli() def unwrap_log_text(log_text: str): """Remove line-break + indent space from log messages Captured log lines always start with the 'severity' so if a line starts with any spaces assume it is due to an indented pipx wrapped message. """ return re.sub(r"\n\s+", " ", log_text) def _mock_legacy_package_info(modern_package_info: dict[str, Any], metadata_version: str) -> dict[str, Any]: if metadata_version in ["0.2", "0.3"]: mock_package_info_template = MOCK_PACKAGE_INFO_0_2 elif metadata_version == "0.1": mock_package_info_template = MOCK_PACKAGE_INFO_0_1 else: raise Exception(f"Internal Test Error: Unknown metadata_version={metadata_version}") mock_package_info = {} for key in mock_package_info_template: mock_package_info[key] = modern_package_info[key] return mock_package_info def mock_legacy_venv(venv_name: str, metadata_version: str | None = None) -> None: """Convert a venv installed with the most recent pipx to look like one with a previous metadata version. metadata_version=None refers to no metadata file (pipx pre-0.15.0.0) """ venv_dir = Path(paths.ctx.venvs) / canonicalize_name(venv_name) if metadata_version == "0.4": # Current metadata version, do nothing return elif metadata_version == "0.3": mock_pipx_metadata_template = MOCK_PIPXMETADATA_0_3 elif metadata_version == "0.2": mock_pipx_metadata_template = MOCK_PIPXMETADATA_0_2 elif metadata_version == "0.1": mock_pipx_metadata_template = MOCK_PIPXMETADATA_0_1 elif metadata_version is None: # No metadata os.remove(venv_dir / "pipx_metadata.json") return else: raise Exception(f"Internal Test Error: Unknown metadata_version={metadata_version}") modern_metadata = pipx_metadata_file.PipxMetadata(venv_dir).to_dict() # Convert to mock old metadata mock_pipx_metadata: dict[str, Any] = {} for key in mock_pipx_metadata_template: if key == "main_package": mock_pipx_metadata[key] = _mock_legacy_package_info(modern_metadata[key], metadata_version=metadata_version) if key == "injected_packages": mock_pipx_metadata[key] = {} for injected in modern_metadata[key]: mock_pipx_metadata[key][injected] = _mock_legacy_package_info( modern_metadata[key][injected], metadata_version=metadata_version ) else: mock_pipx_metadata[key] = modern_metadata.get(key) mock_pipx_metadata["pipx_metadata_version"] = mock_pipx_metadata_template["pipx_metadata_version"] # replicate pipx_metadata_file.PipxMetadata.write() with open(venv_dir / "pipx_metadata.json", "w") as pipx_metadata_fh: json.dump( mock_pipx_metadata, pipx_metadata_fh, indent=4, sort_keys=True, cls=pipx_metadata_file.JsonEncoderHandlesPath, ) def create_package_info_ref(venv_name, package_name, pipx_venvs_dir, **field_overrides): """Create reference PackageInfo to check against Overridable fields to be used in field_overrides: pip_args (default: []) include_apps (default: True) include_dependencies (default: False) app_paths_of_dependencies (default: {}) """ venv_bin_dir = "Scripts" if constants.WINDOWS else "bin" return pipx_metadata_file.PackageInfo( package=package_name, package_or_url=PKG[package_name]["spec"], pip_args=field_overrides.get("pip_args", []), include_apps=field_overrides.get("include_apps", True), include_dependencies=field_overrides.get("include_dependencies", False), apps=PKG[package_name]["apps"], app_paths=[pipx_venvs_dir / venv_name / venv_bin_dir / app for app in PKG[package_name]["apps"]], apps_of_dependencies=PKG[package_name]["apps_of_dependencies"], app_paths_of_dependencies=field_overrides.get("app_paths_of_dependencies", {}), man_pages=PKG[package_name].get("man_pages", []), man_paths=[ pipx_venvs_dir / venv_name / "share" / "man" / man_page for man_page in PKG[package_name].get("man_pages", []) ], man_pages_of_dependencies=PKG[package_name].get("man_pages_of_dependencies", []), man_paths_of_dependencies=field_overrides.get("man_paths_of_dependencies", {}), package_version=PKG[package_name]["spec"].split("==")[-1], ) def assert_package_metadata(test_metadata, ref_metadata): # only compare sorted versions of apps, app_paths so order is not important assert test_metadata.package_version != "" assert isinstance(test_metadata.apps, list) assert isinstance(test_metadata.app_paths, list) test_metadata_replaced = replace( test_metadata, apps=sorted(test_metadata.apps), app_paths=sorted(test_metadata.app_paths), apps_of_dependencies=sorted(test_metadata.apps_of_dependencies), app_paths_of_dependencies={key: sorted(value) for key, value in test_metadata.app_paths_of_dependencies.items()}, ) ref_metadata_replaced = replace( ref_metadata, apps=sorted(ref_metadata.apps), app_paths=sorted(ref_metadata.app_paths), apps_of_dependencies=sorted(ref_metadata.apps_of_dependencies), app_paths_of_dependencies={key: sorted(value) for key, value in ref_metadata.app_paths_of_dependencies.items()}, ) assert test_metadata_replaced == ref_metadata_replaced def remove_venv_interpreter(venv_name): _, venv_python_path, _ = util.get_venv_paths(paths.ctx.venvs / venv_name) assert venv_python_path.is_file() venv_python_path.unlink() assert not venv_python_path.is_file() skip_if_windows = pytest.mark.skipif(sys.platform.startswith("win"), reason="This behavior is undefined on Windows") pipx-1.15.0/tests/package_info.py000066400000000000000000000675061521706623000167200ustar00rootroot00000000000000import sys from pathlib import Path from typing import Any WIN = sys.platform.startswith("win") def _exe_if_win(apps): return [f"{app}.exe" if WIN else app for app in apps] # Versions of all packages possibly used in our tests # Only apply _exe_if_win to entry_points, NOT scripts PKG: dict[str, dict[str, Any]] = { "ansible": { "spec": "ansible==6.7.0", "apps": [ "ansible", "ansible-config", "ansible-connection", "ansible-console", "ansible-doc", "ansible-galaxy", "ansible-inventory", "ansible-playbook", "ansible-pull", "ansible-test", "ansible-vault", ], "apps_of_dependencies": [], }, "awscli": { "spec": "awscli==1.18.168", "apps": [ "aws", "aws.cmd", "aws_bash_completer", "aws_completer", "aws_zsh_completer.sh", ], "apps_of_dependencies": _exe_if_win( [ "pyrsa-decrypt", # rsa EXE "pyrsa-encrypt", # rsa EXE "pyrsa-keygen", # rsa EXE "pyrsa-priv2pub", # rsa EXE "pyrsa-sign", # rsa EXE "pyrsa-verify", # rsa EXE ] ) + [ "jp.py", # jmespath.py NO_EXE "rst2html.py", # docutils NO_EXE "rst2html4.py", # docutils NO_EXE "rst2html5.py", # docutils NO_EXE "rst2latex.py", # docutils NO_EXE "rst2man.py", # docutils NO_EXE "rst2odt.py", # docutils NO_EXE "rst2odt_prepstyles.py", # docutils NO_EXE "rst2pseudoxml.py", # docutils NO_EXE "rst2s5.py", # docutils NO_EXE "rst2xetex.py", # docutils NO_EXE "rst2xml.py", # docutils NO_EXE "rstpep2html.py", # docutils NO_EXE ], }, "b2": { "spec": "b2==3.12.0", "apps": _exe_if_win(["b2"]), "apps_of_dependencies": _exe_if_win(["docutils", "rst2ansi", "tabulate", "tqdm", "normalizer"]) + [ "activate-global-python-argcomplete", "python-argcomplete-check-easy-install-script", "register-python-argcomplete", "rst2html4.py", "rst2html5.py", "rst2html.py", "rst2latex.py", "rst2man.py", "rst2odt_prepstyles.py", "rst2odt.py", "rst2pseudoxml.py", "rst2s5.py", "rst2xetex.py", "rst2xml.py", "rstpep2html.py", ], }, "beancount": { "spec": "beancount==2.3.6", "apps": _exe_if_win( [ "bean-bake", "bean-check", "bean-doctor", "bean-example", "bean-extract", "bean-file", "bean-format", "bean-identify", "bean-price", "bean-query", "bean-report", "bean-sql", "bean-web", "treeify", "upload-to-sheets", ] ), "apps_of_dependencies": _exe_if_win( [ "chardetect", # chardet EXE "py.test", # pytest EXE "pyrsa-decrypt", # rsa EXE "pyrsa-encrypt", # rsa EXE "pyrsa-keygen", # rsa EXE "pyrsa-priv2pub", # rsa EXE "pyrsa-sign", # rsa EXE "pyrsa-verify", # rsa EXE "pytest", # pytest EXE "normalizer", "py.test", ] ) + ["bottle.py", "dumppdf.py", "latin2ascii.py", "pdf2txt.py"], # bottle NO_EXE }, "beets": { "spec": "beets==1.4.9", "apps": _exe_if_win(["beet"]), "apps_of_dependencies": _exe_if_win( [ "mid3cp", "mid3iconv", "mid3v2", "moggsplit", "mutagen-inspect", "mutagen-pony", "unidecode", # unidecode EXE ] ), "man_pages": [], "man_pages_of_dependencies": [ str(Path("man1") / "mutagen-pony.1"), str(Path("man1") / "mutagen-inspect.1"), str(Path("man1") / "moggsplit.1"), str(Path("man1") / "mid3v2.1"), str(Path("man1") / "mid3iconv.1"), str(Path("man1") / "mid3cp.1"), ], }, "black": { "spec": "black==22.8.0", "apps": _exe_if_win(["black", "blackd"]), "apps_of_dependencies": [], }, "cactus": { "spec": "cactus==3.3.3", "apps": _exe_if_win(["cactus"]), "apps_of_dependencies": _exe_if_win(["keyring", "markdown2"]) + [ "asadmin", "bundle_image", "cfadmin", "cq", "cwutil", "django-admin.py", "dynamodb_dump", "dynamodb_load", "elbadmin", "fetch_file", "glacier", "instance_events", "keyring", "kill_instance", "launch_instance", "list_instances", "lss3", "mturk", "pyami_sendmail", "route53", "s3put", "sdbadmin", "taskadmin", ], }, "chert": { "spec": "chert==19.1.0", "apps": _exe_if_win(["chert"]), "apps_of_dependencies": _exe_if_win(["ashes", "markdown_py"]) + ["ashes.py"], }, # "cloudtoken": { # "spec": "cloudtoken==2.1.0", # "apps": ["awstoken", "cloudtoken", "cloudtoken.app", "cloudtoken_proxy.sh"], # "apps_of_dependencies": _exe_if_win(["flask", "keyring", "normalizer"]) + ["jp.py"], # }, "coala": { "spec": "coala==0.11.0", "apps": _exe_if_win(["coala", "coala-ci", "coala-delete-orig", "coala-format", "coala-json"]), "apps_of_dependencies": _exe_if_win(["normalizer", "pygmentize"]) + ["unidiff"], }, "cookiecutter": { "spec": "cookiecutter==2.4.0", "apps": _exe_if_win(["cookiecutter"]), "apps_of_dependencies": _exe_if_win(["chardetect", "normalizer", "markdown-it", "pygmentize", "slugify"]), }, "cython": { "spec": "cython==0.29.21", "apps": _exe_if_win(["cygdb", "cython", "cythonize"]), "apps_of_dependencies": [], }, "datasette": { "spec": "datasette==0.50.2", "apps": _exe_if_win(["datasette"]), "apps_of_dependencies": _exe_if_win(["httpx", "hupper", "uvicorn"]) + ["pint-convert"], }, "diffoscope": { "spec": "diffoscope==154", "apps": _exe_if_win(["diffoscope"]), "apps_of_dependencies": [], }, "doc2dash": { "spec": "doc2dash==3.0.0", "apps": _exe_if_win(["doc2dash"]), "apps_of_dependencies": _exe_if_win( ["markdown-it", "pygmentize"] # pygments EXE ), }, "doitlive": { "spec": "doitlive==4.3.0", "apps": _exe_if_win(["doitlive"]), "apps_of_dependencies": [], }, "gdbgui": { "spec": "gdbgui==0.14.0.1", "apps": _exe_if_win(["gdbgui"]), "apps_of_dependencies": _exe_if_win(["flask", "pygmentize"]), }, "gns3-gui": { "spec": "gns3-gui==2.2.15", "apps": _exe_if_win(["gns3"]), "apps_of_dependencies": _exe_if_win(["distro", "jsonschema"]), }, "grow": { "spec": "grow==1.0.0a10", "apps": ["grow"], "apps_of_dependencies": _exe_if_win( [ "chardetect", # chardet EXE "gen_protorpc", # EXE "html2text", # html2text EXE "markdown_py", # Markdown EXE "pybabel", # babel EXE "pygmentize", # pygments EXE "pyrsa-decrypt", # rsa EXE "pyrsa-encrypt", # rsa EXE "pyrsa-keygen", # rsa EXE "pyrsa-priv2pub", # rsa EXE "pyrsa-sign", # rsa EXE "pyrsa-verify", # rsa EXE "slugify", # python_slugify EXE "watchmedo", # watchdog EXE ] ), }, "guake": { "spec": "guake==3.7.0", "apps": _exe_if_win(["guake", "guake-toggle"]), "apps_of_dependencies": _exe_if_win(["pbr"]), }, "gunicorn": { "spec": "gunicorn==20.0.4", "apps": _exe_if_win(["gunicorn"]), "apps_of_dependencies": [], }, "howdoi": { "spec": "howdoi==2.0.20", "apps": _exe_if_win(["howdoi"]), "apps_of_dependencies": _exe_if_win(["markdown-it", "keep", "normalizer", "pygmentize"]), }, "httpie": { "spec": "httpie==3.2.2", "apps": _exe_if_win(["http", "httpie", "https"]), "apps_of_dependencies": _exe_if_win(["markdown-it", "normalizer", "pygmentize"]), "man_pages": [ str(Path("man1") / "http.1"), str(Path("man1") / "httpie.1"), str(Path("man1") / "https.1"), ], "man_pages_of_dependencies": [], }, "hyde": { "spec": "hyde==0.8.9", "apps": _exe_if_win(["hyde"]), "apps_of_dependencies": _exe_if_win(["markdown_py", "pygmentize"]) + ["smartypants"], }, "ipython": { "spec": "ipython==7.16.1", "apps": _exe_if_win(["iptest", "iptest3", "ipython", "ipython3"]), "apps_of_dependencies": _exe_if_win(["pygmentize"]), # pygments EXE "man_pages": [str(Path("man1") / "ipython.1.gz")], "man_pages_of_dependencies": [], }, "isort": { "spec": "isort==5.6.4", "apps": _exe_if_win(["isort"]), "apps_of_dependencies": [], }, "zest-releaser": { "spec": "zest.releaser==9.1.2", "apps": _exe_if_win( [ "addchangelogentry", "bumpversion", "fullrelease", "lasttagdiff", "lasttaglog", "longtest", "postrelease", "prerelease", "release", ] ), "apps_of_dependencies": _exe_if_win( [ "markdown-it", "normalizer", "twine", "pkginfo", "pyproject-build", "docutils", "pygmentize", "keyring", ] ) + [ "rst2html.py", "rst2html4.py", "rst2html5.py", "rst2latex.py", "rst2man.py", "rst2odt.py", "rst2odt_prepstyles.py", "rst2pseudoxml.py", "rst2s5.py", "rst2xetex.py", "rst2xml.py", "rstpep2html.py", ], }, "jupyter": { "spec": "jupyter==1.0.0", "apps": [], "apps_of_dependencies": _exe_if_win( [ "iptest", # EXE "iptest3", # EXE "ipython", # EXE "ipython3", # EXE "jsonschema", # jsonschema EXE "jupyter", # EXE "jupyter-bundlerextension", # EXE "jupyter-console", # EXE "jupyter-kernel", # EXE "jupyter-kernelspec", # EXE "jupyter-migrate", # EXE "jupyter-nbconvert", # EXE "jupyter-nbextension", # EXE "jupyter-notebook", # EXE "jupyter-qtconsole", # EXE "jupyter-run", # EXE "jupyter-serverextension", # EXE "jupyter-troubleshoot", # EXE "jupyter-trust", # EXE "pygmentize", # pygments EXE ] ), }, "kaggle": { "spec": "kaggle==1.6.11", "apps": _exe_if_win(["kaggle"]), "apps_of_dependencies": list(set(_exe_if_win(["slugify", "normalizer", "tqdm"]))), }, "kibitzr": { "spec": "kibitzr==7.0.5", "apps": _exe_if_win(["kibitzr"]), "apps_of_dependencies": _exe_if_win(["doesitcache", "httpx", "normalizer"]), }, "klaus": { "spec": "klaus==1.5.2", "apps": ["klaus"], "apps_of_dependencies": _exe_if_win(["dulwich", "flask", "pygmentize"]) + ["dul-receive-pack", "dul-upload-pack"], }, "kolibri": { "spec": "kolibri==0.14.3", "apps": _exe_if_win(["kolibri"]), "apps_of_dependencies": [], }, "lektor": { "spec": "Lektor==3.3.10", "apps": _exe_if_win(["lektor"]), "apps_of_dependencies": _exe_if_win(["filetype", "flask", "pybabel", "normalizer", "slugify", "watchmedo"]) + ["EXIF.py"], }, "localstack": { "spec": "localstack==0.12.1", "apps": ["localstack", "localstack.bat"], "apps_of_dependencies": _exe_if_win( [ "dotenv", "markdown-it", "pbr", "pygmentize", "pyrsa-decrypt", "pyrsa-encrypt", "pyrsa-keygen", "pyrsa-priv2pub", "pyrsa-sign", "pyrsa-verify", "pysemver", "pytail", "tabulate", "normalizer", ] ) + ["get_objgraph", "jp.py", "localstack-supervisor", "undill"], }, "mackup": { "spec": "mackup==0.8.29", "apps": _exe_if_win(["mackup"]), "apps_of_dependencies": [], }, # ONLY FOR mac, linux "magic-wormhole": { "spec": "magic-wormhole==0.13.0", "apps": _exe_if_win(["wormhole"]), "apps_of_dependencies": _exe_if_win( [ "automat-visualize", # EXE "cftp", # EXE "ckeygen", # EXE "conch", # EXE "mailmail", # EXE "pyhtmlizer", # EXE "tkconch", # EXE "tqdm", # tqdm EXE "trial", # EXE "twist", # EXE "twistd", # EXE "wamp", # EXE "xbrnetwork", # EXE "xbrnetwork-ui", # EXE ] ) + (["pywin32_postinstall.py", "pywin32_testall.py"] if WIN else []), }, "mayan-edms": { "spec": "mayan-edms==3.5.2", "apps": ["mayan-edms.py"], "apps_of_dependencies": _exe_if_win( [ "celery", # EXE "chardetect", # chardet EXE "django-admin", # EXE "gunicorn", # EXE "jsonschema", # jsonschema EXE "sqlformat", # sqlparse EXE "swagger-flex", # EXE "update-tld-names", # # EXE ] ) + ["django-admin.py", "jsonpointer"], }, "mkdocs": { "spec": "mkdocs==1.1.2", "apps": _exe_if_win(["mkdocs"]), "apps_of_dependencies": _exe_if_win( [ "livereload", # EXE "futurize", # future EXE "pasteurize", # future EXE "nltk", # EXE "tqdm", # tqdm EXE "markdown_py", # Markdown EXE ] ), }, "mycli": { "spec": "mycli==1.22.2", "apps": _exe_if_win(["mycli"]), "apps_of_dependencies": _exe_if_win(["pygmentize", "sqlformat", "tabulate"]), }, "nikola": { "spec": "nikola==8.2.4", "apps": _exe_if_win(["nikola"]), "apps_of_dependencies": _exe_if_win( [ "docutils", "doit", # EXE "mako-render", # mako EXE "markdown_py", # Markdown EXE "natsort", # EXE "pybabel", # babel EXE "pygmentize", # pygments EXE "unidecode", # unidecode EXE "normalizer", ] ) + [ "rst2html.py", # docutils NO_EXE "rst2html4.py", # docutils NO_EXE "rst2html5.py", # docutils NO_EXE "rst2latex.py", # docutils NO_EXE "rst2man.py", # docutils NO_EXE "rst2odt.py", # docutils NO_EXE "rst2odt_prepstyles.py", # docutils NO_EXE "rst2pseudoxml.py", # docutils NO_EXE "rst2s5.py", # docutils NO_EXE "rst2xetex.py", # docutils NO_EXE "rst2xml.py", # docutils NO_EXE "rstpep2html.py", # docutils NO_EXE ], "man_pages": [str(Path("man1") / "nikola.1.gz")], "man_pages_of_dependencies": [], }, "nox": { "spec": "nox==2023.4.22", "apps": _exe_if_win(["nox", "tox-to-nox"]), "apps_of_dependencies": _exe_if_win(["virtualenv"]) + [ "activate-global-python-argcomplete", "python-argcomplete-check-easy-install-script", "register-python-argcomplete", ], # from argcomplete }, "pbr": {"spec": "pbr==5.6.0", "apps": _exe_if_win(["pbr"])}, "pelican": { "spec": "pelican==4.8.0", "apps": _exe_if_win( [ "pelican", "pelican-import", "pelican-plugins", "pelican-quickstart", "pelican-themes", ] ), "apps_of_dependencies": _exe_if_win(["docutils", "markdown-it", "pygmentize", "unidecode"]) + [ "rst2html.py", # docutils NO_EXE "rst2html4.py", # docutils NO_EXE "rst2html5.py", # docutils NO_EXE "rst2latex.py", # docutils NO_EXE "rst2man.py", # docutils NO_EXE "rst2odt.py", # docutils NO_EXE "rst2odt_prepstyles.py", # docutils NO_EXE "rst2pseudoxml.py", # docutils NO_EXE "rst2s5.py", # docutils NO_EXE "rst2xetex.py", # docutils NO_EXE "rst2xml.py", # docutils NO_EXE "rstpep2html.py", # docutils NO_EXE ], }, "platformio": { "spec": "platformio==6.1.11", "apps": _exe_if_win(["pio", "piodebuggdb", "platformio"]), "apps_of_dependencies": _exe_if_win( [ "async-json-rpc-server", "pyserial-miniterm", "pyserial-ports", "tabulate", "uvicorn", "normalizer", ] ) + ["bottle.py", "readelf.py"], }, "ppci": { "spec": "ppci==0.5.8", "apps": _exe_if_win( [ "ppci-archive", "ppci-asm", "ppci-build", "ppci-c3c", "ppci-cc", "ppci-dbg", "ppci-disasm", "ppci-hexdump", "ppci-hexutil", "ppci-java", "ppci-ld", "ppci-llc", "ppci-mkuimage", "ppci-objcopy", "ppci-objdump", "ppci-ocaml", "ppci-opt", "ppci-pascal", "ppci-pedump", "ppci-pycompile", "ppci-readelf", "ppci-wabt", "ppci-wasm2wat", "ppci-wasmcompile", "ppci-wat2wasm", "ppci-yacc", ] ), "apps_of_dependencies": [], }, "prosopopee": { "spec": "prosopopee==1.1.3", "apps": _exe_if_win(["prosopopee"]), "apps_of_dependencies": _exe_if_win(["futurize", "pasteurize", "pybabel"]), }, "ptpython": { "spec": "ptpython==3.0.7", "apps": _exe_if_win( [ "ptipython", "ptipython3", "ptipython3.8", "ptpython", "ptpython3", "ptpython3.8", ] ), "apps_of_dependencies": _exe_if_win(["pygmentize"]), # pygments EXE }, "pycowsay": { "spec": "pycowsay==0.0.0.2", "apps": _exe_if_win(["pycowsay"]), "apps_of_dependencies": [], "man_pages": [str(Path("man6") / "pycowsay.6")], "man_pages_of_dependencies": [], }, "pygdbmi": {"spec": "pygdbmi==0.10.0.0", "apps": [], "apps_of_dependencies": []}, "pylint": { "spec": "pylint==3.0.4", "apps": _exe_if_win(["pylint", "pylint-config", "pyreverse", "symilar"]), "apps_of_dependencies": _exe_if_win(["isort", "isort-identify-imports"]) + ["get_gprof", "get_objgraph", "undill"], }, "retext": { "spec": "ReText==8.0.1", "apps": _exe_if_win(["retext"]), "apps_of_dependencies": _exe_if_win( [ "docutils", "markdown_py", # Markdown EXE "pygmentize", # pygments EXE "pylupdate6", # EXE "pyuic6", # EXE "chardetect", ] ) + [ "rst2html.py", # docutils NO_EXE "rst2html4.py", # docutils NO_EXE "rst2html5.py", # docutils NO_EXE "rst2latex.py", # docutils NO_EXE "rst2man.py", # docutils NO_EXE "rst2odt.py", # docutils NO_EXE "rst2odt_prepstyles.py", # docutils NO_EXE "rst2pseudoxml.py", # docutils NO_EXE "rst2s5.py", # docutils NO_EXE "rst2xetex.py", # docutils NO_EXE "rst2xml.py", # docutils NO_EXE "rstpep2html.py", # docutils NO_EXE ], }, "robotframework": { "spec": "robotframework==3.2.2", "apps": _exe_if_win(["rebot", "robot"]), "apps_of_dependencies": [], }, "shell-functools": { "spec": "shell-functools==0.3.0", "apps": [ "filter", "foldl", "foldl1", "ft-functions", "map", "sort_by", "take_while", ], "apps_of_dependencies": [], }, "speedtest-cli": { "spec": "speedtest-cli==2.1.2", "apps": _exe_if_win(["speedtest", "speedtest-cli"]), "apps_of_dependencies": [], }, "sphinx": { "spec": "Sphinx==7.2.6", "apps": _exe_if_win(["sphinx-apidoc", "sphinx-autogen", "sphinx-build", "sphinx-quickstart"]), "apps_of_dependencies": _exe_if_win(["docutils", "pybabel", "normalizer", "pygmentize"]) + [ "rst2html.py", # docutils NO_EXE "rst2html4.py", # docutils NO_EXE "rst2html5.py", # docutils NO_EXE "rst2latex.py", # docutils NO_EXE "rst2man.py", # docutils NO_EXE "rst2odt.py", # docutils NO_EXE "rst2odt_prepstyles.py", # docutils NO_EXE "rst2pseudoxml.py", # docutils NO_EXE "rst2s5.py", # docutils NO_EXE "rst2xetex.py", # docutils NO_EXE "rst2xml.py", # docutils NO_EXE "rstpep2html.py", # docutils NO_EXE ], }, "sqlmap": { "spec": "sqlmap==1.4.10", "apps": _exe_if_win(["sqlmap"]), "apps_of_dependencies": [], }, "streamlink": { "spec": "streamlink==6.3.1", "apps": _exe_if_win(["streamlink"] + (["streamlinkw"] if WIN else [])), "apps_of_dependencies": _exe_if_win(["normalizer", "wsdump"]), "man_pages": [str(Path("man1") / "streamlink.1")], "man_pages_of_dependencies": [], }, "taguette": { "spec": "taguette==0.9.2", "apps": _exe_if_win(["taguette"]), "apps_of_dependencies": _exe_if_win(["alembic", "mako-render"]) + ["vba_extract.py"], }, "term2048": { "spec": "term2048==0.2.7", "apps": _exe_if_win(["term2048"]), "apps_of_dependencies": [], }, "tox-ini-fmt": { "spec": "tox-ini-fmt==0.5.0", "apps": _exe_if_win(["tox-ini-fmt"]), "apps_of_dependencies": _exe_if_win(["py.test", "pytest"]), # pytest EXE }, "visidata": { "spec": "visidata==2.0.1", "apps": _exe_if_win(["visidata"]) + ["vd"], "apps_of_dependencies": [], "man_pages": [str(Path("man1") / "vd.1")], "man_pages_of_dependencies": [], }, "vulture": { "spec": "vulture==2.1", "apps": _exe_if_win(["vulture"]), "apps_of_dependencies": [], }, "weblate": { "spec": "Weblate==4.3.1", "apps": _exe_if_win(["weblate"]), "apps_of_dependencies": _exe_if_win( # TODO: check if _exe_if_win (can't install) [ "borg", "borgfs", "build_firefox.sh", "build_tmdb", "buildxpi.py", "celery", "chardetect", # chardet EXE "csv2po", "csv2tbx", "cygdb", "cython", "cythonize", "django-admin", "django-admin.py", # NO_EXE "flatxml2po", "get_moz_enUS.py", "html2po", "html2text", # html2text EXE "ical2po", "idml2po", "ini2po", "json2po", "jsonschema", # jsonschema EXE "junitmsgfmt", "misaka", "moz2po", "mozlang2po", "odf2xliff", "oo2po", "oo2xliff", "php2po", "phppo2pypo", "po2csv", "po2flatxml", "po2html", "po2ical", "po2idml", "po2ini", "po2json", "po2moz", "po2mozlang", "po2oo", "po2php", "po2prop", "po2rc", "po2resx", "po2sub", "po2symb", "po2tiki", "po2tmx", "po2ts", "po2txt", "po2web2py", "po2wordfast", "po2xliff", "po2yaml", "poclean", "pocommentclean", "pocompendium", "pocompile", "poconflicts", "pocount", "podebug", "pofilter", "pogrep", "pomerge", "pomigrate2", "popuretext", "poreencode", "porestructure", "posegment", "posplit", "poswap", "pot2po", "poterminology", "pretranslate", "prop2po", "pydiff", "pypo2phppo", "rc2po", "resx2po", "sqlformat", # sqlparse EXE "sub2po", "symb2po", "tbx2po", "tiki2po", "tmserver", "ts2po", "txt2po", "web2py2po", "weblate-discover", "xliff2odf", "xliff2oo", "xliff2po", "yaml2po", ] ), }, "youtube-dl": { "spec": "youtube-dl==2020.9.20", "apps": _exe_if_win(["youtube-dl"]), "apps_of_dependencies": [], "man_pages": [str(Path("man1") / "youtube-dl.1")], "man_pages_of_dependencies": [], }, "zeo": { "spec": "ZEO==5.2.2", "apps": _exe_if_win(["runzeo", "zeo-nagios", "zeoctl", "zeopack"]), "apps_of_dependencies": _exe_if_win( [ "fsdump", "fsoids", "fsrefs", "fstail", "repozo", "zconfig", "zconfig_schema2html", "zdaemon", ] ), }, } pipx-1.15.0/tests/test_animate.py000066400000000000000000000112531521706623000167530ustar00rootroot00000000000000import time from timeit import default_timer import pytest import pipx.animate from pipx.animate import ( CLEAR_LINE, EMOJI_ANIMATION_FRAMES, EMOJI_FRAME_PERIOD, NONEMOJI_ANIMATION_FRAMES, NONEMOJI_FRAME_PERIOD, ) # 40-char test_string counts columns e.g.: "0204060810 ... 363840" TEST_STRING_40_CHAR = "".join([f"{x:02}" for x in range(2, 41, 2)]) def check_animate_output( capsys, test_string, frame_strings, frame_period, frames_to_test, extra_animate_time=0.4, extra_after_thread_time=0.1, ): """ Refactored to use polling instead of rigid sleeps. """ expected_string = "".join(frame_strings) # FIX: Calculate exact length required. Removed the "+ 1" that caused flakes. chars_to_test = len("".join(frame_strings[:frames_to_test])) total_err = "" # Generous timeout for slow CI environments (e.g. Windows/Mac runners) timeout = 5.0 start_time = default_timer() with pipx.animate.animate(test_string, do_animation=True): # POLLING LOOP: Keep reading until we get the expected data while default_timer() - start_time < timeout: captured = capsys.readouterr() total_err += captured.err # If we have enough data, stop waiting immediately if len(total_err) >= chars_to_test: break # Tiny sleep to avoid 100% CPU usage loop time.sleep(0.01) # Capture any final output after loop or context manager exit captured = capsys.readouterr() total_err += captured.err print("check_animate_output() Test Debug Output:") if len(total_err) < chars_to_test: print("Not enough captured characters--Timed out waiting for output") print(f"captured characters: {len(total_err)}") print(f"chars_to_test: {chars_to_test}") for i in range(0, chars_to_test, 40): i_end = min(i + 40, chars_to_test) print(f"expected_string[{i}:{i_end}]: {expected_string[i:i_end]!r}") print(f"captured.err[{i}:{i_end}] : {total_err[i:i_end]!r}") assert total_err[:chars_to_test] == expected_string[:chars_to_test] def test_delay_suppresses_output(capsys, monkeypatch): monkeypatch.setattr(pipx.animate, "stderr_is_tty", True) monkeypatch.setenv("COLUMNS", "80") test_string = "asdf" # We keep sleep here because we are testing the ABSENCE of output during a delay. with pipx.animate.animate(test_string, do_animation=True, delay=0.9): time.sleep(0.5) captured = capsys.readouterr() assert test_string not in captured.err @pytest.mark.parametrize( "env_columns,expected_frame_message", [ (45, f"{TEST_STRING_40_CHAR:.{45 - 6}}..."), (46, f"{TEST_STRING_40_CHAR}"), (47, f"{TEST_STRING_40_CHAR}"), ], ) def test_line_lengths_emoji(capsys, monkeypatch, env_columns, expected_frame_message): monkeypatch.setattr(pipx.animate, "stderr_is_tty", True) monkeypatch.setattr(pipx.animate, "EMOJI_SUPPORT", True) monkeypatch.setenv("COLUMNS", str(env_columns)) frames_to_test = 4 frame_strings = [f"\r{CLEAR_LINE}{x} {expected_frame_message}" for x in EMOJI_ANIMATION_FRAMES] check_animate_output(capsys, TEST_STRING_40_CHAR, frame_strings, EMOJI_FRAME_PERIOD, frames_to_test) @pytest.mark.parametrize( "env_columns,expected_frame_message", [ (43, f"{TEST_STRING_40_CHAR:.{43 - 4}}"), (44, f"{TEST_STRING_40_CHAR}"), (45, f"{TEST_STRING_40_CHAR}"), ], ) def test_line_lengths_no_emoji(capsys, monkeypatch, env_columns, expected_frame_message): monkeypatch.setattr(pipx.animate, "stderr_is_tty", True) monkeypatch.setattr(pipx.animate, "EMOJI_SUPPORT", False) monkeypatch.setenv("COLUMNS", str(env_columns)) frames_to_test = 2 frame_strings = [f"\r{CLEAR_LINE}{expected_frame_message}{x}" for x in NONEMOJI_ANIMATION_FRAMES] check_animate_output( capsys, TEST_STRING_40_CHAR, frame_strings, NONEMOJI_FRAME_PERIOD, frames_to_test, ) @pytest.mark.parametrize("env_columns,stderr_is_tty", [(0, True), (8, True), (16, True), (17, False)]) def test_env_no_animate(capsys, monkeypatch, env_columns, stderr_is_tty): monkeypatch.setattr(pipx.animate, "stderr_is_tty", stderr_is_tty) monkeypatch.setenv("COLUMNS", str(env_columns)) expected_string = f"{TEST_STRING_40_CHAR}...\n" # Replaced complex sleep math with a simple short wait. # We just need the context manager to run and exit to verify the static output. with pipx.animate.animate(TEST_STRING_40_CHAR, do_animation=True): time.sleep(0.1) captured = capsys.readouterr() assert captured.out == "" assert captured.err == expected_string pipx-1.15.0/tests/test_backends.py000066400000000000000000000362031521706623000171110ustar00rootroot00000000000000from __future__ import annotations import json from pathlib import Path from typing import TYPE_CHECKING, TypedDict import pytest from pipx import backends, pipx_metadata_file from pipx.backends import ( KNOWN_BACKENDS, PIP, UV, assert_not_pip_under_uv, env_default_backend, find_uv_binary, resolve_backend_name, ) from pipx.backends.uv import UvBackend from pipx.commands.run_uv import translate_pip_args_for_uv from pipx.main import _validate_backend_available from pipx.util import PipxError from pipx.venv import Venv, reset_backend_override_warnings from pipx.venv_inspect import list_not_required_packages if TYPE_CHECKING: from pytest_mock import MockerFixture class _BackendResolveKwargs(TypedDict, total=False): cli_value: str env_value: str metadata_value: str auto: bool class _LegacyPackageInfo(TypedDict): package: str package_or_url: str pip_args: list[str] include_dependencies: bool include_apps: bool apps: list[str] app_paths: list[str] apps_of_dependencies: list[str] app_paths_of_dependencies: dict[str, list[str]] man_pages: list[str] man_paths: list[str] man_pages_of_dependencies: list[str] man_paths_of_dependencies: dict[str, list[str]] package_version: str suffix: str pinned: bool class _LegacyMetadata(TypedDict): main_package: _LegacyPackageInfo python_version: str source_interpreter: None venv_args: list[str] injected_packages: dict[str, _LegacyPackageInfo] pipx_metadata_version: str class _CurrentMetadata(_LegacyMetadata): backend: str def _write_dist_info( site_packages: Path, name: str, requires: list[str] | None = None, provides_extra: list[str] | None = None ) -> None: dist_info = site_packages / f"{name}-1.0.dist-info" dist_info.mkdir() metadata = [f"Name: {name}", "Version: 1.0"] metadata += [f"Provides-Extra: {extra}" for extra in provides_extra or []] metadata += [f"Requires-Dist: {requirement}" for requirement in requires or []] (dist_info / "METADATA").write_text("\n".join(metadata), encoding="utf-8") def test_known_backends() -> None: assert KNOWN_BACKENDS == ("pip", "uv") @pytest.mark.parametrize( ("env", "expected"), [ pytest.param(None, None, id="unset"), pytest.param("", None, id="empty"), pytest.param("uv", "uv", id="uv"), pytest.param(" pip ", "pip", id="trims-whitespace"), ], ) def test_env_default_backend(monkeypatch: pytest.MonkeyPatch, env: str | None, expected: str | None) -> None: if env is None: monkeypatch.delenv("PIPX_DEFAULT_BACKEND", raising=False) else: monkeypatch.setenv("PIPX_DEFAULT_BACKEND", env) assert env_default_backend() == expected @pytest.mark.parametrize( ("kwargs", "expected_name", "expected_source"), [ pytest.param({"cli_value": "pip"}, "pip", "cli", id="cli-pip"), pytest.param({"cli_value": "uv"}, "uv", "cli", id="cli-uv"), pytest.param({"env_value": "pip"}, "pip", "env", id="env-pip"), pytest.param({"metadata_value": "uv"}, "uv", "metadata", id="metadata-uv"), pytest.param( {"cli_value": "pip", "env_value": "uv", "metadata_value": "uv"}, "pip", "cli", id="cli-wins", ), # The headline bug fix: env must NOT override an existing venv's metadata. pytest.param( {"env_value": "uv", "metadata_value": "pip"}, "pip", "metadata", id="metadata-locks-env-out", ), pytest.param({"env_value": "uv"}, "uv", "env", id="env-when-no-metadata"), ], ) def test_resolve_backend_name_precedence( kwargs: _BackendResolveKwargs, expected_name: str, expected_source: str ) -> None: name, source = resolve_backend_name(**kwargs) assert (name, source) == (expected_name, expected_source) def test_resolve_backend_name_rejects_unknown() -> None: with pytest.raises(PipxError, match="Unknown backend"): resolve_backend_name(cli_value="poetry") def test_resolve_backend_name_auto_uv_available(mocker: MockerFixture) -> None: mocker.patch.object(backends, "find_uv_binary", return_value=(Path("/opt/uv"), "path")) assert resolve_backend_name() == (UV, "auto-path") def test_resolve_backend_name_auto_no_uv(mocker: MockerFixture) -> None: mocker.patch.object(backends, "find_uv_binary", return_value=(None, "missing")) assert resolve_backend_name() == (PIP, "auto-pip") def test_resolve_backend_name_auto_disabled(mocker: MockerFixture) -> None: mocker.patch.object(backends, "find_uv_binary", return_value=(Path("/opt/uv"), "path")) assert resolve_backend_name(auto=False) == (PIP, "auto-pip") def test_find_uv_binary_prefers_bundled(mocker: MockerFixture, tmp_path: Path) -> None: bundled = tmp_path / "bundled-uv" bundled.write_text("") mocker.patch("pipx.backends.uv._FIND_UV_BIN_FROM_EXTRA", lambda: str(bundled)) mocker.patch("shutil.which", return_value="/usr/local/bin/uv") # Stub bundled isn't a real uv, so bypass the launch probe. mocker.patch("pipx.backends.uv._binary_runs", return_value=True) binary, source = find_uv_binary() assert binary == bundled assert source == "bundled" def test_find_uv_binary_skips_broken_bundled(mocker: MockerFixture, tmp_path: Path) -> None: bundled = tmp_path / "bundled-uv" bundled.write_text("") mocker.patch("pipx.backends.uv._FIND_UV_BIN_FROM_EXTRA", lambda: str(bundled)) mocker.patch("pipx.backends.uv._binary_runs", return_value=False) mocker.patch("shutil.which", return_value="/usr/local/bin/uv") binary, source = find_uv_binary() assert binary == Path("/usr/local/bin/uv") assert source == "path" def test_find_uv_binary_falls_back_to_path(mocker: MockerFixture) -> None: mocker.patch("pipx.backends.uv._FIND_UV_BIN_FROM_EXTRA", None) mocker.patch("shutil.which", return_value="/usr/local/bin/uv") binary, source = find_uv_binary() assert binary == Path("/usr/local/bin/uv") assert source == "path" def test_find_uv_binary_missing(mocker: MockerFixture) -> None: mocker.patch("pipx.backends.uv._FIND_UV_BIN_FROM_EXTRA", None) mocker.patch("shutil.which", return_value=None) binary, source = find_uv_binary() assert binary is None assert source == "missing" def test_find_uv_binary_is_cached(mocker: MockerFixture) -> None: # Self-contained cache reset: this test asserts hot-loop performance and # must not rely on the autouse fixture's reset already having run. find_uv_binary.cache_clear() mocker.patch("pipx.backends.uv._FIND_UV_BIN_FROM_EXTRA", None) which_mock = mocker.patch("shutil.which", return_value="/usr/local/bin/uv") find_uv_binary() find_uv_binary() find_uv_binary() assert which_mock.call_count == 1 @pytest.mark.parametrize( ("pip_args", "expected"), [ pytest.param([], [], id="empty"), pytest.param(["-q"], [], id="strip-quiet-short"), pytest.param(["--quiet"], [], id="strip-quiet-long"), pytest.param( ["--index-url", "https://example.com/simple"], ["--index-url", "https://example.com/simple"], id="index-url", ), pytest.param( ["--extra-index-url", "https://other.example/simple"], ["--extra-index-url", "https://other.example/simple"], id="extra-index", ), pytest.param(["--pre"], ["--prerelease=allow"], id="pre-to-prerelease"), pytest.param(["--index-url=https://example.com"], ["--index-url=https://example.com"], id="equals-form"), ], ) def test_translate_pip_args_for_uv(pip_args: list[str], expected: list[str]) -> None: assert translate_pip_args_for_uv(pip_args) == expected @pytest.mark.parametrize( "pip_args", [ pytest.param(["--editable"], id="editable"), pytest.param(["-e"], id="editable-short"), pytest.param(["--no-build-isolation"], id="unknown-flag"), pytest.param(["--index-url"], id="missing-value"), ], ) def test_translate_pip_args_for_uv_errors(pip_args: list[str]) -> None: with pytest.raises(PipxError): translate_pip_args_for_uv(pip_args) def test_assert_not_pip_under_uv_blocks_pip_on_uv() -> None: with pytest.raises(PipxError, match="cannot be installed or exposed via the uv backend"): assert_not_pip_under_uv("pip", UV) def test_assert_not_pip_under_uv_allows_pip_on_pip() -> None: assert_not_pip_under_uv("pip", PIP) # does not raise def test_assert_not_pip_under_uv_allows_other_packages_on_uv() -> None: assert_not_pip_under_uv("ruff", UV) # does not raise def test_uv_list_installed_not_required_uses_distribution_metadata( monkeypatch: pytest.MonkeyPatch, tmp_path: Path ) -> None: site_packages = tmp_path / "site-packages" site_packages.mkdir() _write_dist_info(site_packages, "root-package") _write_dist_info(site_packages, "top-package", ["child-package>=1"]) _write_dist_info(site_packages, "child-package") def fail_run_subprocess(*args: object, **kwargs: object) -> None: pytest.fail("uv pip list should not be invoked with --not-required") monkeypatch.setattr("pipx.backends.uv.run_subprocess", fail_run_subprocess) monkeypatch.setattr( "pipx.venv_inspect.fetch_info_in_venv", lambda venv_python: ([str(site_packages)], {}, "Python 3.13.0"), raising=False, ) backend = UvBackend.__new__(UvBackend) backend._binary = tmp_path / "uv" assert backend.list_installed( venv_root=tmp_path, venv_python=tmp_path / "bin" / "python", not_required=True, ) == {"root-package", "top-package"} def test_list_not_required_packages_treats_extra_dependencies_as_required( monkeypatch: pytest.MonkeyPatch, tmp_path: Path ) -> None: # ``child-package`` is required only via ``top-package``'s ``extra == "cli"`` dependency; it must # still be reported as required (not removable) while top-package stays installed. site_packages = tmp_path / "site-packages" site_packages.mkdir() _write_dist_info(site_packages, "top-package", ['child-package>=1; extra == "cli"'], provides_extra=["cli"]) _write_dist_info(site_packages, "child-package") monkeypatch.setattr( "pipx.venv_inspect.fetch_info_in_venv", lambda venv_python: ([str(site_packages)], {}, "Python 3.13.0"), raising=False, ) assert list_not_required_packages(tmp_path / "bin" / "python") == {"top-package"} def test_metadata_round_trip_includes_backend(tmp_path: Path) -> None: venv_dir = tmp_path / "venv" venv_dir.mkdir() metadata = pipx_metadata_file.PipxMetadata(venv_dir, read=False) metadata.main_package = pipx_metadata_file.PackageInfo( package="demo", package_or_url="demo", pip_args=[], include_dependencies=False, include_apps=True, apps=["demo"], app_paths=[Path("demo")], apps_of_dependencies=[], app_paths_of_dependencies={}, package_version="1.0", ) metadata.python_version = "3.12" metadata.backend = "uv" metadata.write() raw_payload = json.loads((venv_dir / pipx_metadata_file.PIPX_INFO_FILENAME).read_text()) assert raw_payload["backend"] == "uv" reread = pipx_metadata_file.PipxMetadata(venv_dir) assert reread.backend == "uv" def test_legacy_metadata_defaults_to_pip(tmp_path: Path) -> None: venv_dir = tmp_path / "venv" venv_dir.mkdir() legacy_payload: _LegacyMetadata = { "main_package": { "package": "demo", "package_or_url": "demo", "pip_args": [], "include_dependencies": False, "include_apps": True, "apps": ["demo"], "app_paths": [], "apps_of_dependencies": [], "app_paths_of_dependencies": {}, "man_pages": [], "man_paths": [], "man_pages_of_dependencies": [], "man_paths_of_dependencies": {}, "package_version": "1.0", "suffix": "", "pinned": False, }, "python_version": "3.12", "source_interpreter": None, "venv_args": [], "injected_packages": {}, "pipx_metadata_version": "0.5", } (venv_dir / pipx_metadata_file.PIPX_INFO_FILENAME).write_text(json.dumps(legacy_payload)) metadata = pipx_metadata_file.PipxMetadata(venv_dir) assert metadata.backend == "pip" def test_validate_backend_silent_on_env_uv_missing( monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture, caplog: pytest.LogCaptureFixture, ) -> None: # Diagnostic commands (``pipx environment``/``list``) must keep working # without a startup warning that would later contradict the actual error # from ``UvBackend.__init__``. # Patch both the source and the package-level re-export โ€” either branch # poisons the test if only one is mocked. mocker.patch.object(backends, "find_uv_binary", return_value=(None, "missing")) mocker.patch("pipx.backends.uv.find_uv_binary", return_value=(None, "missing")) monkeypatch.setenv("PIPX_DEFAULT_BACKEND", "uv") caplog.set_level("WARNING", logger="pipx.main") _validate_backend_available(cli_backend=None, env_backend="uv") assert not any("uv" in rec.message.lower() for rec in caplog.records) def test_validate_backend_raises_on_cli_uv_missing(mocker: MockerFixture) -> None: mocker.patch.object(backends, "find_uv_binary", return_value=(None, "missing")) mocker.patch("pipx.backends.uv.find_uv_binary", return_value=(None, "missing")) with pytest.raises(PipxError, match="uv' executable could not be found"): _validate_backend_available(cli_backend="uv", env_backend=None) def test_backend_override_warning_dedups_per_venv( tmp_path: Path, caplog: pytest.LogCaptureFixture, ) -> None: # ``upgrade-all --backend uv`` against many pip-backed venvs would # otherwise emit a multi-line block per Venv construction. venv_dir = tmp_path / "demo" venv_dir.mkdir() metadata_payload: _CurrentMetadata = { "main_package": { "package": "demo", "package_or_url": "demo", "pip_args": [], "include_dependencies": False, "include_apps": True, "apps": ["demo"], "app_paths": [], "apps_of_dependencies": [], "app_paths_of_dependencies": {}, "man_pages": [], "man_paths": [], "man_pages_of_dependencies": [], "man_paths_of_dependencies": {}, "package_version": "1.0", "suffix": "", "pinned": False, }, "python_version": "3.12", "source_interpreter": None, "venv_args": [], "injected_packages": {}, "backend": "pip", "pipx_metadata_version": "0.6", } (venv_dir / pipx_metadata_file.PIPX_INFO_FILENAME).write_text(json.dumps(metadata_payload)) reset_backend_override_warnings() caplog.set_level("WARNING", logger="pipx.venv") Venv(venv_dir, backend="uv") Venv(venv_dir, backend="uv") Venv(venv_dir, backend="uv") warnings = [rec for rec in caplog.records if "Ignoring --backend=uv" in rec.message] assert len(warnings) == 1 pipx-1.15.0/tests/test_backends_install.py000066400000000000000000000026221521706623000206350ustar00rootroot00000000000000from __future__ import annotations import shutil import sys import pytest from helpers import run_pipx_cli from pipx import paths def test_install_pip_with_uv_backend_errors(capsys: pytest.CaptureFixture[str]) -> None: exit_code = run_pipx_cli(["install", "pip", "--backend", "uv"]) captured = capsys.readouterr() assert exit_code != 0 assert "'pip' package cannot be installed or exposed via the uv backend" in captured.err @pytest.mark.skipif(shutil.which("uv") is None, reason="uv binary not on PATH; skipping uv integration smoke") def test_uv_backend_install_uninstall_smoke(pipx_temp_env, capsys: pytest.CaptureFixture[str]) -> None: # Asserts that a uv-built venv records ``backend="uv"``, exposes apps the # same way pip venvs do, and uninstalls without re-invoking uv. install_rc = run_pipx_cli(["install", "pycowsay", "--backend", "uv", "--python", sys.executable]) captured = capsys.readouterr() assert install_rc == 0, captured.err metadata_file = paths.ctx.venvs / "pycowsay" / "pipx_metadata.json" assert metadata_file.is_file() assert '"backend": "uv"' in metadata_file.read_text() list_rc = run_pipx_cli(["list", "--short"]) list_out = capsys.readouterr().out assert list_rc == 0 assert "pycowsay" in list_out uninstall_rc = run_pipx_cli(["uninstall", "pycowsay"]) assert uninstall_rc == 0 assert not metadata_file.exists() pipx-1.15.0/tests/test_common.py000066400000000000000000000030011521706623000166150ustar00rootroot00000000000000import os import subprocess import sys from helpers import skip_if_windows from pipx.commands.common import expose_resources_globally, get_exposed_paths_for_package @skip_if_windows def test_get_exposed_paths_ignores_recursive_symlink(tmp_path): venv_resource_path = tmp_path / "venv_bin" venv_resource_path.mkdir() local_resource_dir = tmp_path / "bin" local_resource_dir.mkdir() loop = local_resource_dir / "recursiveexample" loop.symlink_to(loop.name) exposed = get_exposed_paths_for_package(venv_resource_path, local_resource_dir) assert loop not in exposed @skip_if_windows def test_expose_app_scripts_ignores_pythonpath(tmp_path): venv_resource_path = tmp_path / "venv_bin" venv_resource_path.mkdir() local_resource_dir = tmp_path / "bin" shadow_path = tmp_path / "shadow" shadow_path.mkdir() app_path = venv_resource_path / "demo" app_path.write_text( f"#!{sys.executable}\n" "import sys\n" f"if {str(shadow_path)!r} in sys.path:\n" " raise SystemExit('PYTHONPATH leaked into app script')\n" "print('ok')\n" ) app_path.chmod(0o755) expose_resources_globally("app", local_resource_dir, [app_path], force=False) assert app_path.read_text().splitlines()[0] == f"#!{sys.executable} -E" result = subprocess.run( [app_path], check=True, capture_output=True, env={**os.environ, "PYTHONPATH": str(shadow_path)}, text=True, ) assert result.stdout == "ok\n" pipx-1.15.0/tests/test_completions.py000066400000000000000000000003171521706623000176700ustar00rootroot00000000000000from helpers import run_pipx_cli def test_cli(monkeypatch, capsys): assert not run_pipx_cli(["completions"]) captured = capsys.readouterr() assert "Add the appropriate command" in captured.out pipx-1.15.0/tests/test_emojis.py000066400000000000000000000026521521706623000166260ustar00rootroot00000000000000import sys from io import BytesIO, TextIOWrapper from unittest import mock import pytest from pipx.emojis import use_emojis @pytest.mark.parametrize( "PIPX_USE_EMOJI, encoding, expected", [ # utf-8 (None, "utf-8", True), ("", "utf-8", False), ("0", "utf-8", False), ("1", "utf-8", True), ("true", "utf-8", True), ("tru", "utf-8", False), # codespell:ignore tru ("True", "utf-8", True), ("false", "utf-8", False), # latin_1 (alias: iso-8859-1) (None, "latin_1", False), ("", "latin_1", False), ("0", "latin_1", False), ("1", "latin_1", True), ("true", "latin_1", True), ("tru", "latin_1", False), # codespell:ignore tru ("True", "latin_1", True), ("false", "latin_1", False), # cp1252 (None, "cp1252", False), ("", "cp1252", False), ("0", "cp1252", False), ("1", "cp1252", True), ("true", "cp1252", True), ("tru", "cp1252", False), # codespell:ignore tru ("True", "cp1252", True), ("false", "cp1252", False), ], ) def test_use_emojis(monkeypatch, PIPX_USE_EMOJI, encoding, expected): with mock.patch.object(sys, "stderr", TextIOWrapper(BytesIO(), encoding=encoding)): if PIPX_USE_EMOJI is not None: monkeypatch.setenv("PIPX_USE_EMOJI", PIPX_USE_EMOJI) assert use_emojis() is expected pipx-1.15.0/tests/test_ensurepath.py000066400000000000000000000051441521706623000175150ustar00rootroot00000000000000from pathlib import Path import pytest from pipx.commands import ensure_path as ensure_path_module from pipx.constants import EXIT_CODE_OK class FakeUserpath: """Minimal stand-in for the ``userpath`` module used by ensure_path. Records append/prepend calls so tests can assert that a dry run never mutates PATH or any shell configuration file. """ def __init__(self) -> None: self.append_calls: list[str] = [] self.prepend_calls: list[str] = [] self.in_path = False self.restart = False def need_shell_restart(self, location: str) -> bool: return self.restart def in_current_path(self, location: str) -> bool: return self.in_path def append(self, location: str, app: str, all_shells: bool = False) -> bool: self.append_calls.append(location) return True def prepend(self, location: str, app: str, all_shells: bool = False) -> bool: self.prepend_calls.append(location) return True @pytest.fixture def mock_userpath(monkeypatch: pytest.MonkeyPatch) -> FakeUserpath: fake = FakeUserpath() monkeypatch.setattr(ensure_path_module, "userpath", fake) return fake def test_ensure_path_dry_run_does_not_append(mock_userpath: FakeUserpath, capsys: pytest.CaptureFixture[str]) -> None: location = Path("/some/bin") path_added, _ = ensure_path_module.ensure_path(location, force=True, dry_run=True) assert path_added is False assert mock_userpath.append_calls == [] assert mock_userpath.prepend_calls == [] assert "Would append" in capsys.readouterr().out def test_ensure_path_dry_run_reports_prepend(mock_userpath: FakeUserpath, capsys: pytest.CaptureFixture[str]) -> None: location = Path("/some/bin") ensure_path_module.ensure_path(location, force=True, prepend=True, dry_run=True) assert mock_userpath.prepend_calls == [] assert "Would prepend" in capsys.readouterr().out def test_ensure_path_non_dry_run_still_appends(mock_userpath: FakeUserpath, capsys: pytest.CaptureFixture[str]) -> None: location = Path("/some/bin") ensure_path_module.ensure_path(location, force=True) assert mock_userpath.append_calls == [str(location)] assert "Success!" in capsys.readouterr().out def test_ensure_pipx_paths_dry_run_footer(mock_userpath: FakeUserpath, capsys: pytest.CaptureFixture[str]) -> None: exit_code = ensure_path_module.ensure_pipx_paths(force=True, dry_run=True) assert exit_code == EXIT_CODE_OK assert mock_userpath.append_calls == [] out = capsys.readouterr().out.lower() assert "dry run" in out assert "no changes were made" in out pipx-1.15.0/tests/test_environment.py000066400000000000000000000102731521706623000177020ustar00rootroot00000000000000import fnmatch from pathlib import Path import pytest from helpers import run_pipx_cli, skip_if_windows from pipx import paths from pipx.commands.environment import ENVIRONMENT_VARIABLES from pipx.paths import get_expanded_environ def test_cli(pipx_temp_env, monkeypatch, capsys): assert not run_pipx_cli(["environment"]) captured = capsys.readouterr() assert fnmatch.fnmatch(captured.out, "*PIPX_HOME=*subdir/pipxhome*") assert fnmatch.fnmatch(captured.out, "*PIPX_BIN_DIR=*otherdir/pipxbindir*") assert fnmatch.fnmatch(captured.out, "*PIPX_MAN_DIR=*otherdir/pipxmandir*") assert "PIPX_SHARED_LIBS" in captured.out assert fnmatch.fnmatch(captured.out, "*PIPX_LOCAL_VENVS=*subdir/pipxhome/venvs*") assert fnmatch.fnmatch(captured.out, "*PIPX_LOG_DIR=*subdir/pipxhome/logs*") assert fnmatch.fnmatch(captured.out, "*PIPX_TRASH_DIR=*subdir/pipxhome/.trash*") assert fnmatch.fnmatch(captured.out, "*PIPX_VENV_CACHEDIR=*subdir/pipxhome/.cache*") # Checking just for the sake of completeness for env_var in ENVIRONMENT_VARIABLES: assert env_var in captured.out def test_cli_with_args(monkeypatch, capsys): assert not run_pipx_cli(["environment", "--value", "PIPX_HOME"]) assert not run_pipx_cli(["environment", "--value", "PIPX_BIN_DIR"]) assert not run_pipx_cli(["environment", "--value", "PIPX_MAN_DIR"]) assert not run_pipx_cli(["environment", "--value", "PIPX_SHARED_LIBS"]) assert not run_pipx_cli(["environment", "--value", "PIPX_LOCAL_VENVS"]) assert not run_pipx_cli(["environment", "--value", "PIPX_LOG_DIR"]) assert not run_pipx_cli(["environment", "--value", "PIPX_TRASH_DIR"]) assert not run_pipx_cli(["environment", "--value", "PIPX_VENV_CACHEDIR"]) assert not run_pipx_cli(["environment", "--value", "PIPX_DEFAULT_PYTHON"]) assert not run_pipx_cli(["environment", "--value", "PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE"]) assert not run_pipx_cli(["environment", "--value", "PIPX_USE_EMOJI"]) assert not run_pipx_cli(["environment", "--value", "PIPX_HOME_ALLOW_SPACE"]) with pytest.raises(SystemExit) as excinfo: run_pipx_cli(["environment", "--value", "SSS"]) assert excinfo.value.code == 2 captured = capsys.readouterr() assert "invalid choice" in captured.err def test_resolve_user_dir_in_env_paths(monkeypatch): monkeypatch.setenv("TEST_DIR", "~/test") home = Path.home() env_dir = get_expanded_environ("TEST_DIR") assert "~" not in str(env_dir) assert env_dir == home / "test" env_dir = get_expanded_environ("THIS_SHOULD_NOT_EXIST") assert env_dir is None def test_allow_space_in_pipx_home( monkeypatch, capsys, tmp_path, ): home_dir = Path(tmp_path) / "path with space" monkeypatch.setattr(paths.ctx, "_base_home", home_dir) assert not run_pipx_cli(["environment", "--value", "PIPX_HOME_ALLOW_SPACE"]) paths.ctx.log_warnings() captured = capsys.readouterr() assert "Found a space" in captured.err assert "false" in captured.out monkeypatch.setenv("PIPX_HOME_ALLOW_SPACE", "1") assert not run_pipx_cli(["environment", "--value", "PIPX_HOME_ALLOW_SPACE"]) paths.ctx.log_warnings() captured = capsys.readouterr() assert "Found a space" not in captured.err assert "true" in captured.out paths.ctx.make_local() @skip_if_windows def test_cli_global(pipx_temp_env, monkeypatch, capsys): assert not run_pipx_cli(["environment", "--global"]) captured = capsys.readouterr() assert fnmatch.fnmatch(captured.out, "*PIPX_HOME=*global/pipxhome*") assert fnmatch.fnmatch(captured.out, "*PIPX_BIN_DIR=*global_otherdir/pipxbindir*") assert fnmatch.fnmatch(captured.out, "*PIPX_MAN_DIR=*global_otherdir/pipxmandir*") assert "PIPX_SHARED_LIBS" in captured.out assert fnmatch.fnmatch(captured.out, "*PIPX_LOCAL_VENVS=*global/pipxhome/venvs*") assert fnmatch.fnmatch(captured.out, "*PIPX_LOG_DIR=*global/pipxhome/logs*") assert fnmatch.fnmatch(captured.out, "*PIPX_TRASH_DIR=*global/pipxhome/.trash*") assert fnmatch.fnmatch(captured.out, "*PIPX_VENV_CACHEDIR=*global/pipxhome/.cache*") # Checking just for the sake of completeness for env_var in ENVIRONMENT_VARIABLES: assert env_var in captured.out pipx-1.15.0/tests/test_imports.py000066400000000000000000000006771521706623000170420ustar00rootroot00000000000000import os import subprocess import sys def test_importing_util_first_succeeds(root): env = {**os.environ, "PYTHONPATH": str(root / "src")} result = subprocess.run( [sys.executable, "-c", "import pipx.util; print('Success!')"], check=False, capture_output=True, cwd=root, env=env, text=True, ) assert result.returncode == 0, result.stderr assert result.stdout == "Success!\n" pipx-1.15.0/tests/test_inject.py000066400000000000000000000113701521706623000166110ustar00rootroot00000000000000import logging import re import textwrap import pytest from helpers import PIPX_METADATA_LEGACY_VERSIONS, mock_legacy_venv, run_pipx_cli, skip_if_windows from package_info import PKG from pipx import paths from pipx.pipx_metadata_file import PipxMetadata # Note that this also checks that packages used in other tests can be injected individually @pytest.mark.parametrize( "pkg_spec", [ PKG["black"]["spec"], PKG["nox"]["spec"], PKG["pylint"]["spec"], PKG["ipython"]["spec"], "jaraco.clipboard==2.0.1", # tricky character ], ) def test_inject_single_package(pipx_temp_env, capsys, caplog, pkg_spec): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["inject", "pycowsay", pkg_spec]) # Check arguments have been parsed correctly assert f"Injecting packages: {[pkg_spec]!r}" in caplog.text # Check it's actually being installed and into correct venv captured = capsys.readouterr() injected = re.findall(r"injected package (.+?) into venv pycowsay", captured.out) pkg_name = pkg_spec.split("=", 1)[0].replace(".", "-") # assuming spec is always of the form == assert set(injected) == {pkg_name} @skip_if_windows def test_inject_simple_global(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "--global", "pycowsay"]) assert not run_pipx_cli(["inject", "--global", "pycowsay", PKG["black"]["spec"]]) @pytest.mark.parametrize("metadata_version", PIPX_METADATA_LEGACY_VERSIONS) def test_inject_simple_legacy_venv(pipx_temp_env, capsys, metadata_version): assert not run_pipx_cli(["install", "pycowsay"]) mock_legacy_venv("pycowsay", metadata_version=metadata_version) if metadata_version is not None: assert not run_pipx_cli(["inject", "pycowsay", PKG["black"]["spec"]]) else: # no metadata in venv should result in PipxError with message assert run_pipx_cli(["inject", "pycowsay", PKG["black"]["spec"]]) assert "Please uninstall and install" in capsys.readouterr().err @pytest.mark.parametrize("with_suffix", [False, True]) def test_inject_include_apps(pipx_temp_env, capsys, with_suffix): install_args = [] suffix = "" if with_suffix: suffix = "_x" install_args = [f"--suffix={suffix}"] assert not run_pipx_cli(["install", "pycowsay", *install_args]) assert not run_pipx_cli(["inject", f"pycowsay{suffix}", PKG["black"]["spec"], "--include-deps"]) if suffix: assert run_pipx_cli(["inject", "pycowsay", PKG["black"]["spec"], "--include-deps"]) assert not run_pipx_cli(["inject", f"pycowsay{suffix}", PKG["black"]["spec"], "--include-deps"]) @pytest.mark.parametrize( "with_packages", [ (), # no extra packages ("black",), # duplicate from requirements file ("ipython",), # additional package ], ) def test_inject_with_req_file(pipx_temp_env, capsys, caplog, tmp_path, with_packages): caplog.set_level(logging.INFO) req_file = tmp_path / "inject-requirements.txt" req_file.write_text( textwrap.dedent( f""" {PKG["black"]["spec"]} # a comment inline {PKG["nox"]["spec"]} {PKG["pylint"]["spec"]} # comment on separate line """ ).strip() ) assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli( ["inject", "pycowsay", *(PKG[pkg]["spec"] for pkg in with_packages), "--requirement", str(req_file)] ) packages = [ ("black", PKG["black"]["spec"]), ("nox", PKG["nox"]["spec"]), ("pylint", PKG["pylint"]["spec"]), ] packages.extend((pkg, PKG[pkg]["spec"]) for pkg in with_packages) packages = sorted(set(packages)) # Check arguments and files have been parsed correctly assert f"Injecting packages: {[p for _, p in packages]!r}" in caplog.text # Check they're actually being installed and into correct venv captured = capsys.readouterr() injected = re.findall(r"injected package (.+?) into venv pycowsay", captured.out) assert set(injected) == {pkg for pkg, _ in packages} def test_force_inject_reinstalls_without_storing_force(pipx_temp_env, caplog): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["inject", "pycowsay", PKG["black"]["spec"], "--pip-args=--no-cache-dir"]) caplog.clear() assert not run_pipx_cli( [ "inject", "pycowsay", PKG["black"]["spec"], "--force", "--verbose", "--pip-args=--no-cache-dir", ] ) assert "--force-reinstall" in caplog.text metadata = PipxMetadata(paths.ctx.venvs / "pycowsay") assert metadata.injected_packages["black"].pip_args == ["--no-cache-dir"] pipx-1.15.0/tests/test_install.py000066400000000000000000000535341521706623000170130ustar00rootroot00000000000000import os import re import subprocess import sys from pathlib import Path from unittest import mock import pytest from helpers import app_name, run_pipx_cli, skip_if_windows, unwrap_log_text from package_info import PKG from pipx import paths, shared_libs from pipx.util import PipxError from pipx.venv import Venv TEST_DATA_PATH = "./testdata/test_package_specifier" def test_help_text(monkeypatch, capsys): mock_exit = mock.Mock(side_effect=ValueError("raised in test to exit early")) with mock.patch.object(sys, "exit", mock_exit), pytest.raises(ValueError, match="raised in test to exit early"): run_pipx_cli(["install", "--help"]) captured = capsys.readouterr() assert "apps you can run from anywhere" in captured.out def install_packages(capsys, pipx_temp_env, caplog, packages, package_names=()): if len(package_names) != len(packages): package_names = packages run_pipx_cli(["install", *packages, "--verbose"]) captured = capsys.readouterr() for package_name in package_names: assert f"installed package {package_name}" in captured.out if not sys.platform.startswith("win"): # TODO assert on windows too # https://github.com/pypa/pipx/issues/217 assert "symlink missing or pointing to unexpected location" not in captured.out assert "not modifying" not in captured.out assert "is not on your PATH environment variable" not in captured.out assert "โš ๏ธ" not in caplog.text assert "WARNING" not in caplog.text @pytest.mark.parametrize( "package_name, package_spec", [("pycowsay", "pycowsay"), ("black", PKG["black"]["spec"])], ) def test_install_easy_packages(capsys, pipx_temp_env, caplog, package_name, package_spec): install_packages(capsys, pipx_temp_env, caplog, [package_spec], [package_name]) def test_install_easy_multiple_packages(capsys, pipx_temp_env, caplog): install_packages( capsys, pipx_temp_env, caplog, ["pycowsay", PKG["black"]["spec"]], ["pycowsay", "black"], ) @pytest.mark.parametrize( "package_name, package_spec", [("pycowsay", "pycowsay"), ("black", PKG["black"]["spec"])], ) @skip_if_windows def test_install_easy_packages_globally(capsys, pipx_temp_env, caplog, package_name, package_spec): install_packages(capsys, pipx_temp_env, caplog, [package_spec], [package_name]) @pytest.mark.parametrize( "package_name, package_spec", [ # ("cloudtoken", PKG["cloudtoken"]["spec"]), ("awscli", PKG["awscli"]["spec"]), ("ansible", PKG["ansible"]["spec"]), ("shell-functools", PKG["shell-functools"]["spec"]), ], ) def test_install_tricky_packages(capsys, pipx_temp_env, caplog, package_name, package_spec): if os.getenv("FAST"): pytest.skip("skipping slow tests") if sys.platform.startswith("win") and package_name == "ansible": pytest.skip("Ansible is not installable on Windows") install_packages(capsys, pipx_temp_env, caplog, [package_spec], [package_name]) def test_install_multiple_packages_when_some_already_installed(capsys, pipx_temp_env, caplog): run_pipx_cli(["install", "black", "pycowsay"]) captured = capsys.readouterr() assert "installed package black" in captured.out assert "installed package pycowsay" in captured.out run_pipx_cli(["install", "black", "pycowsay", "isort"]) captured = capsys.readouterr() assert "black" in captured.out and "already seems to be installed" in captured.out assert "pycowsay" in captured.out and "already seems to be installed" in captured.out assert "installed package isort" in captured.out def test_install_tricky_multiple_packages(capsys, pipx_temp_env, caplog): if os.getenv("FAST"): pytest.skip("skipping slow tests") packages = ["awscli", "shell-functools"] # cloudtoken is temporarily removed package_specs = [PKG[package]["spec"] for package in packages] install_packages(capsys, pipx_temp_env, caplog, package_specs, packages) @pytest.mark.parametrize( "package_name, package_spec", [ ("pycowsay", "git+https://github.com/cs01/pycowsay.git@master"), ("pylint", PKG["pylint"]["spec"]), ("nox", "https://github.com/wntrblm/nox/archive/2022.1.7.zip"), ], ) def test_install_package_specs(capsys, pipx_temp_env, caplog, package_name, package_spec): install_packages(capsys, pipx_temp_env, caplog, [package_spec], [package_name]) def test_force_install(pipx_temp_env, capsys): run_pipx_cli(["install", "pycowsay"]) captured = capsys.readouterr() # print(captured.out) assert "installed package" in captured.out run_pipx_cli(["install", "pycowsay"]) captured = capsys.readouterr() assert "installed package" not in captured.out assert "pycowsay" in captured.out and "already seems to be installed" in captured.out run_pipx_cli(["install", "pycowsay", "--force"]) captured = capsys.readouterr() assert "Installing to existing venv" in captured.out def test_install_no_packages_found(pipx_temp_env, capsys): run_pipx_cli(["install", PKG["pygdbmi"]["spec"]]) captured = capsys.readouterr() assert "No apps associated with package pygdbmi" in captured.err def test_install_same_package_twice_no_force(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["install", "pycowsay"]) captured = capsys.readouterr() assert "already seems to be installed" in captured.out assert "pipx upgrade" in captured.out assert "0.0.0.2" in captured.out def test_include_deps(pipx_temp_env, capsys): assert run_pipx_cli(["install", PKG["jupyter"]["spec"]]) == 1 assert not run_pipx_cli(["install", PKG["jupyter"]["spec"], "--include-deps"]) @pytest.mark.parametrize( "package_name, package_spec", [ ("zest-releaser", PKG["zest-releaser"]["spec"]), ("tox-ini-fmt", PKG["tox-ini-fmt"]["spec"]), ], ) def test_name_tricky_characters(caplog, capsys, pipx_temp_env, package_name, package_spec): if sys.platform == "darwin" and package_name == "zest-releaser": pytest.skip("Skipping zest-releaser due to missing Python 3.13 wheel for cmarkgfm on macOS") install_packages(capsys, pipx_temp_env, caplog, [package_spec], [package_name]) def test_extra(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "nox[tox_to_nox]==2023.4.22", "--include-deps"]) captured = capsys.readouterr() assert f"- {app_name('tox')}\n" in captured.out def test_install_local_extra(pipx_temp_env, capsys, monkeypatch, root): assert not run_pipx_cli(["install", str(root / f"{TEST_DATA_PATH}/local_extras[cow]"), "--include-deps"]) captured = capsys.readouterr() assert f"- {app_name('pycowsay')}\n" in captured.out assert f"- {Path('man6/pycowsay.6')}\n" in captured.out def test_path_warning(pipx_temp_env, capsys, monkeypatch, caplog): assert not run_pipx_cli(["install", "pycowsay"]) assert "is not on your PATH environment variable" not in unwrap_log_text(caplog.text) monkeypatch.setenv("PATH", "") assert not run_pipx_cli(["install", "pycowsay", "--force"]) assert "is not on your PATH environment variable" in unwrap_log_text(caplog.text) @skip_if_windows def test_existing_symlink_points_to_existing_wrong_location_warning(pipx_temp_env, caplog, capsys): paths.ctx.bin_dir.mkdir(exist_ok=True, parents=True) (paths.ctx.bin_dir / "pycowsay").symlink_to(os.devnull) assert not run_pipx_cli(["install", "pycowsay"]) captured = capsys.readouterr() assert "File exists at" in unwrap_log_text(caplog.text) assert "symlink missing or pointing to unexpected location" in captured.out # bin dir was on path, so the warning should NOT appear (even though the symlink # pointed to the wrong location) assert "is not on your PATH environment variable" not in captured.err @skip_if_windows def test_existing_man_page_symlink_points_to_existing_wrong_location_warning(pipx_temp_env, caplog, capsys): (paths.ctx.man_dir / "man6").mkdir(exist_ok=True, parents=True) (paths.ctx.man_dir / "man6" / "pycowsay.6").symlink_to(os.devnull) assert not run_pipx_cli(["install", "pycowsay"]) captured = capsys.readouterr() assert "File exists at" in unwrap_log_text(caplog.text) assert "symlink missing or pointing to unexpected location" in captured.out @skip_if_windows def test_existing_symlink_points_to_nothing(pipx_temp_env, capsys): paths.ctx.bin_dir.mkdir(exist_ok=True, parents=True) (paths.ctx.bin_dir / "pycowsay").symlink_to("/asdf/jkl") assert not run_pipx_cli(["install", "pycowsay"]) captured = capsys.readouterr() # pipx should realize the symlink points to nothing and replace it, # so no warning should be present assert "symlink missing or pointing to unexpected location" not in captured.out @skip_if_windows def test_existing_man_page_symlink_points_to_nothing(pipx_temp_env, capsys): (paths.ctx.man_dir / "man6").mkdir(exist_ok=True, parents=True) (paths.ctx.man_dir / "man6" / "pycowsay.6").symlink_to("/asdf/jkl") assert not run_pipx_cli(["install", "pycowsay"]) captured = capsys.readouterr() # pipx should realize the symlink points to nothing and replace it, # so no warning should be present assert "symlink missing or pointing to unexpected location" not in captured.out def test_pip_args_forwarded_to_shared_libs(pipx_ultra_temp_env, capsys, caplog): # strategy: # 1. start from an empty env to ensure the next command would trigger a shared lib update assert shared_libs.shared_libs.needs_upgrade # 2. install any package with --no-index # and check that the shared library update phase fails return_code = run_pipx_cli(["install", "--verbose", "--pip-args=--no-index", "pycowsay"]) assert "Upgrading shared libraries in" in caplog.text captured = capsys.readouterr() assert return_code != 0 assert "ERROR: Could not find a version that satisfies the requirement pip" in captured.err assert "Failed to upgrade shared libraries" in caplog.text def test_pip_args_forwarded_to_package_name_determination(pipx_temp_env, capsys): assert run_pipx_cli( [ "install", # use a valid spec and invalid pip args "https://github.com/psf/black/archive/22.8.0.zip", "--verbose", "--pip-args='--asdf'", ] ) captured = capsys.readouterr() assert "--asdf" in captured.err assert "Cannot determine package name from spec" not in captured.err def test_package_name_determination_preserves_install_error(monkeypatch): class FailingBackend: def install(self, **kwargs): return subprocess.CompletedProcess( ["pip", "install", "requires-newer-python"], 1, stdout="", stderr="ERROR: Package 'requires-newer-python' requires a different Python\n", ) def no_installed_packages(): return set() venv = Venv(Path("requires-newer-python-venv")) monkeypatch.setattr(venv, "_backend", FailingBackend()) monkeypatch.setattr(venv, "list_installed_packages", no_installed_packages) with pytest.raises(PipxError) as excinfo: venv.install_package_no_deps("requires-newer-python", []) error = str(excinfo.value) assert "requires a different Python" in error assert "Cannot determine package name from spec" not in error @pytest.mark.skipif(not sys.platform.startswith("win"), reason="Windows only") def test_pip_args_with_windows_path(pipx_temp_env, capsys): assert run_pipx_cli( [ "install", "pycowsay", "--verbose", "--pip-args='--no-index --find-links=D:\\TEST\\DIR'", ] ) captured = capsys.readouterr() assert r"D:\\TEST\\DIR" in captured.err @pytest.mark.parametrize("constraint_flag", ["-c ", "--constraint ", "--constraint="]) def test_pip_args_with_constraint_relative_path(constraint_flag, pipx_temp_env, tmp_path, caplog): constraint_file_name = "constraints.txt" package_name = "ipython" package_version = "8.23.0" os.chdir(tmp_path) constraints_file = tmp_path / constraint_file_name constraints_file.write_text(f"{package_name}!={package_version}") constraints_file.touch() assert not run_pipx_cli(["install", f"--pip-args='{constraint_flag}{constraint_file_name}'", package_name]) assert f"{constraint_flag}{constraints_file}" in caplog.text subprocess_package_version = subprocess.run([package_name, "--version"], capture_output=True, text=True, check=False) subprocess_package_version_output = subprocess_package_version.stdout.strip() assert subprocess_package_version_output != package_version @pytest.mark.parametrize("constraint_flag", ["-c ", "--constraint ", "--constraint="]) def test_pip_args_with_wrong_constraint_fail(constraint_flag, pipx_ultra_temp_env, tmp_path, capsys): constraint_file_name = "constraints.txt" os.chdir(tmp_path) assert run_pipx_cli(["install", f"--pip-args='{constraint_flag}{constraint_file_name}'", "pycowsay"]) # pip phrases this as "requirements file" (<26) or "constraint file" (>=26), so match the common part assert f"[Errno 2] No such file or directory: '{constraint_file_name}'" in capsys.readouterr().err def test_install_suffix(pipx_temp_env, capsys): name = "pbr" suffix = "_a" assert not run_pipx_cli(["install", PKG[name]["spec"], f"--suffix={suffix}"]) captured = capsys.readouterr() name_a = app_name(f"{name}{suffix}") assert f"- {name_a}" in captured.out suffix = "_b" assert not run_pipx_cli(["install", PKG[name]["spec"], f"--suffix={suffix}"]) captured = capsys.readouterr() name_b = app_name(f"{name}{suffix}") assert f"- {name_b}" in captured.out assert (paths.ctx.bin_dir / name_a).exists() assert (paths.ctx.bin_dir / name_b).exists() def test_man_page_install(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "pycowsay"]) captured = capsys.readouterr() assert f"- {Path('man6/pycowsay.6')}" in captured.out assert (paths.ctx.man_dir / "man6" / "pycowsay.6").exists() def test_editable_install_links_man_pages(pipx_temp_env, capsys, root): package = (root / TEST_DATA_PATH / "local_manpage").as_posix() assert not run_pipx_cli(["install", "--editable", package]) captured = capsys.readouterr() assert f"- {app_name('local-manpage')}\n" in captured.out assert f"- {Path('man1/local-manpage.1')}" in captured.out assert (paths.ctx.venvs / "local-manpage" / "share" / "man" / "man1" / "local-manpage.1").exists() assert (paths.ctx.man_dir / "man1" / "local-manpage.1").exists() assert not run_pipx_cli(["uninstall", "local-manpage"]) assert not (paths.ctx.man_dir / "man1" / "local-manpage.1").exists() def test_install_pip_failure(pipx_temp_env, capsys): assert run_pipx_cli(["install", "weblate==4.3.1", "--verbose"]) captured = capsys.readouterr() assert "Fatal error from pip" in captured.err pip_log_file_match = re.search(r"Full pip output in file:\s+(\S.+)$", captured.err, re.MULTILINE) assert pip_log_file_match assert Path(pip_log_file_match[1]).exists() assert re.search(r"pip (failed|seemed to fail) to build package", captured.err) def test_install_local_archive(pipx_temp_env, monkeypatch, capsys, root): monkeypatch.chdir(root / TEST_DATA_PATH / "local_extras") subprocess.run([sys.executable, "-m", "pip", "wheel", "."], check=True) assert not run_pipx_cli(["install", "repeatme-0.1-py3-none-any.whl"]) captured = capsys.readouterr() assert f"- {app_name('repeatme')}\n" in captured.out def test_force_install_changes(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "https://github.com/wntrblm/nox/archive/2022.1.7.zip"]) captured = capsys.readouterr() assert "2022.1.7" in captured.out assert not run_pipx_cli(["install", "nox", "--force"]) captured = capsys.readouterr() assert "2022.1.7" not in captured.out def test_force_install_changes_editable(pipx_temp_env, root, capsys): empty_project_path_as_string = (root / "testdata" / "empty_project").as_posix() assert not run_pipx_cli(["install", "--editable", empty_project_path_as_string]) captured = capsys.readouterr() assert "empty-project" in captured.out assert not run_pipx_cli(["install", "--editable", empty_project_path_as_string, "--force"]) captured = capsys.readouterr() assert "Installing to existing venv 'empty-project'" in captured.out def test_preinstall(pipx_temp_env, caplog): assert not run_pipx_cli(["install", "--preinstall", "black", "nox"]) assert "black" in caplog.text def test_preinstall_multiple(pipx_temp_env, caplog): assert not run_pipx_cli(["install", "--preinstall", "chardet", "--preinstall", "colorama", "nox"]) assert "chardet" in caplog.text assert "colorama" in caplog.text def test_preinstall_specific_version(pipx_temp_env, caplog): assert not run_pipx_cli(["install", "--preinstall", "black==22.8.0", "nox"]) assert "black==22.8.0" in caplog.text @pytest.mark.xfail def test_do_not_wait_for_input(pipx_temp_env, pipx_session_shared_dir, monkeypatch): monkeypatch.setenv("PIP_INDEX_URL", "http://127.0.0.1:8080/simple") run_pipx_cli(["install", "pycowsay"]) def test_passed_python_and_force_flag_warning(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "black"]) assert not run_pipx_cli(["install", "--python", sys.executable, "--force", "black"]) captured = capsys.readouterr() assert "--python is ignored when --force is passed." in captured.out assert not run_pipx_cli(["install", "black", "--force"]) captured = capsys.readouterr() assert "--python is ignored when --force is passed." not in captured.out, ( "Should only print warning if both flags present" ) assert not run_pipx_cli(["install", "pycowsay", "--force"]) captured = capsys.readouterr() assert "--python is ignored when --force is passed." not in captured.out, ( "Should not print warning if package does not exist yet" ) @pytest.mark.parametrize( ("python_version", "fetch_flag", "expect_deprecation"), [ pytest.param("3.0", "--fetch-python=missing", False, id="3.0-fetch-python-missing"), pytest.param("3.1", "--fetch-python=missing", False, id="3.1-fetch-python-missing"), pytest.param("3.0", "--fetch-missing-python", True, id="3.0-deprecated-flag"), ], ) def test_install_fetch_missing_python_invalid(capsys, python_version, fetch_flag, expect_deprecation): assert run_pipx_cli(["install", "--python", python_version, fetch_flag, "pycowsay"]) captured = capsys.readouterr() assert f"No executable for the provided Python version '{python_version}' found" in captured.out assert ("--fetch-missing-python is deprecated" in captured.err) is expect_deprecation def test_install_run_in_separate_directory(caplog, capsys, pipx_temp_env, monkeypatch, tmp_path): monkeypatch.chdir(tmp_path) f = Path("argparse.py") f.touch() install_packages(capsys, pipx_temp_env, caplog, ["pycowsay"], ["pycowsay"]) @skip_if_windows @pytest.mark.parametrize( "python_version", [ str(sys.version_info.major), f"{sys.version_info.major}.{sys.version_info.minor}", f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}", ], ) def test_install_python_command_version(pipx_temp_env, monkeypatch, capsys, python_version): monkeypatch.setenv("PATH", os.getenv("PATH_ORIG")) assert not run_pipx_cli(["install", "--python", python_version, "--verbose", "pycowsay"]) captured = capsys.readouterr() assert python_version in captured.out @skip_if_windows def test_install_python_command_version_invalid(pipx_temp_env, capsys): python_version = "3.x" assert run_pipx_cli(["install", "--python", python_version, "--verbose", "pycowsay"]) captured = capsys.readouterr() assert f"Invalid Python version: {python_version}" in captured.err @skip_if_windows def test_install_python_command_version_unsupported(pipx_temp_env, capsys): python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}.dev" assert run_pipx_cli(["install", "--python", python_version, "--verbose", "pycowsay"]) captured = capsys.readouterr() assert f"Unsupported Python version: {python_version}" in captured.err @skip_if_windows def test_install_python_command_version_missing(pipx_temp_env, monkeypatch, capsys): monkeypatch.setenv("PATH", os.getenv("PATH_ORIG")) python_version = f"{sys.version_info.major + 99}.{sys.version_info.minor}" assert run_pipx_cli(["install", "--python", python_version, "--verbose", "pycowsay"]) captured = capsys.readouterr() assert f"Command `python{python_version}` was not found" in captured.err @skip_if_windows def test_install_python_command_version_micro_mismatch(pipx_temp_env, monkeypatch, capsys): monkeypatch.setenv("PATH", os.getenv("PATH_ORIG")) python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro + 1}" assert not run_pipx_cli(["install", "--python", python_version, "--verbose", "pycowsay"]) captured = capsys.readouterr() assert f"It may not match the specified version {python_version} at the micro/patch level" in captured.err @skip_if_windows def test_global_flag_before_subcommand_rejected(pipx_temp_env, capsys): with pytest.raises(SystemExit) as exc_info: run_pipx_cli(["--global", "install", "pycowsay"]) assert exc_info.value.code == 2, "argparse error should exit with code 2" captured = capsys.readouterr() assert "unrecognized arguments: --global" in captured.err def test_install_quiet_flag(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "--quiet", "--quiet", "pycowsay"]) captured = capsys.readouterr() assert "installed package" not in captured.out assert "These apps are now" not in captured.out assert "done!" not in captured.out assert "done!" not in captured.err pipx-1.15.0/tests/test_install_all.py000066400000000000000000000025531521706623000176360ustar00rootroot00000000000000from pathlib import Path from helpers import run_pipx_cli from pipx import paths def test_install_all(pipx_temp_env, tmp_path, capsys): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["install", "black"]) _ = capsys.readouterr() assert not run_pipx_cli(["list", "--json"]) captured = capsys.readouterr() pipx_list_path = Path(tmp_path) / "pipx_list.json" with open(pipx_list_path, "w") as pipx_list_fh: pipx_list_fh.write(captured.out) assert not run_pipx_cli(["install-all", str(pipx_list_path)]) captured = capsys.readouterr() assert "black" in captured.out assert "pycowsay" in captured.out def test_install_all_multiple_errors(pipx_temp_env, root, capsys): pipx_metadata_path = root / "testdata" / "pipx_metadata_multiple_errors.json" assert run_pipx_cli(["install-all", str(pipx_metadata_path)]) captured = capsys.readouterr() assert "The following package(s) failed to install: dotenv, weblate" in captured.err assert f"No packages installed after running 'pipx install-all {pipx_metadata_path}'" in captured.out if paths.ctx.log_file: with open(paths.ctx.log_file.parent / (paths.ctx.log_file.stem + "_pip_errors.log")) as log_fh: log_contents = log_fh.read() assert "dotenv" in log_contents assert "weblate" in log_contents pipx-1.15.0/tests/test_install_all_packages.py000066400000000000000000000437421521706623000215010ustar00rootroot00000000000000""" This module uses the pytest infrastructure to produce reports on a large list of packages. It verifies installation with and without an intact system PATH. It also generates report summaries and error reports files. Test pytest outcomes: PASS - if no pip errors, and no pipx issues, and package apps verified all installed correctly XFAIL - if there is a pip error, i.e. an installation problem out of pipx's control FAIL - if there is no pip error, but there is a problem due to pipx, including a pipx error or warning, incorrect list of installed apps, etc. """ import io import os import re import subprocess import sys import textwrap import time from datetime import datetime, timedelta from pathlib import Path import pytest from helpers import run_pipx_cli from package_info import PKG REPORTS_DIR = "./reports" REPORT_FILENAME_ROOT = "all_packages" PACKAGE_NAME_LIST = [ "ansible", "awscli", "b2", "beancount", "beets", "black", "cactus", "chert", # "cloudtoken", "coala", "cookiecutter", "cython", "datasette", "diffoscope", "doc2dash", "doitlive", "gdbgui", "gns3-gui", "grow", "guake", "gunicorn", "howdoi", "httpie", "hyde", "ipython", "isort", "zest-releaser", "kaggle", "kibitzr", "klaus", "kolibri", "lektor", "localstack", "mackup", "magic-wormhole", "mayan-edms", "mkdocs", "mycli", "nikola", "nox", "pelican", "platformio", "ppci", "prosopopee", "ptpython", "pycowsay", "pylint", "retext", "robotframework", "shell-functools", "speedtest-cli", "sphinx", "sqlmap", "streamlink", "taguette", "term2048", "tox-ini-fmt", "visidata", "vulture", "weblate", "youtube-dl", "zeo", ] class PackageData: def __init__(self): self.package_name: str = "" self.package_spec: str = "" self.clear_elapsed_time: float | None = None self.clear_pip_pass: bool | None = None self.clear_pipx_pass: bool | None = None self.sys_elapsed_time: float | None = None self.sys_pip_pass: bool | None = None self.sys_pipx_pass: bool | None = None self.overall_pass: bool | None = None @property def clear_pip_pf_str(self) -> str: return self._get_pass_fail_str("clear_pip_pass") @property def clear_pipx_pf_str(self) -> str: return self._get_pass_fail_str("clear_pipx_pass") @property def sys_pip_pf_str(self) -> str: return self._get_pass_fail_str("sys_pip_pass") @property def sys_pipx_pf_str(self) -> str: return self._get_pass_fail_str("sys_pipx_pass") @property def overall_pf_str(self) -> str: return self._get_pass_fail_str("overall_pass") def _get_pass_fail_str(self, test_attr: str) -> str: if getattr(self, test_attr) is not None: return "PASS" if getattr(self, test_attr) else "FAIL" else: return "" class ModuleGlobalsData: def __init__(self): self.errors_path = Path(".") self.install_data: list[PackageData] = [] pyver = sys.version_info self.py_version_display = f"Python {pyver.major}.{pyver.minor}.{pyver.micro}" self.py_version_short = f"{pyver.major}.{pyver.minor}" self.report_path = Path(".") self.sys_platform = sys.platform self.test_class = "" self.test_start = datetime.now() self.test_end = datetime.now() # default, must be set later def reset(self, test_class: str = "") -> None: self.errors_path = Path(".") self.install_data = [] self.report_path = Path(".") self.test_class = test_class self.test_start = datetime.now() @pytest.fixture(scope="module") def module_globals() -> ModuleGlobalsData: return ModuleGlobalsData() def pip_cache_purge() -> None: subprocess.run([sys.executable, "-m", "pip", "cache", "purge"], check=True) def write_report_legend(report_legend_path: Path) -> None: with report_legend_path.open("w", encoding="utf-8") as report_legend_fh: print( textwrap.dedent( """ LEGEND =========== cleared_PATH = PATH used for pipx tests with only pipx bin dir and nothing else sys_PATH = Normal system PATH with all default directories included overall = PASS or FAIL for complete end-to-end pipx install, PASS if no errors or warnings and all the proper apps were installed and linked pip = PASS or FAIL sub-category based only if pip inside of pipx installs package with/without error pipx = PASS or FAIL sub-category based on the non-pip parts of pipx, including whether any errors or warnings are present, and if all the proper apps were installed and linked """ ).strip(), file=report_legend_fh, ) def format_report_table_header(module_globals: ModuleGlobalsData) -> str: header_string = "\n\n" header_string += "=" * 79 + "\n" header_string += f"{module_globals.sys_platform:16}" header_string += f"{module_globals.py_version_display:16}" header_string += f"{module_globals.test_start.strftime('%Y-%m-%d %H:%M:%S')}\n\n" header_string += f"{'package_spec':24}{'overall':12}{'cleared_PATH':24}" header_string += f"{'system_PATH':24}\n" header_string += f"{'':24}{'':12}{'pip':8}{'pipx':8}{'time':8}" header_string += f"{'pip':8}{'pipx':8}{'time':8}\n" header_string += "-" * 79 return header_string def format_report_table_row(package_data: PackageData) -> str: clear_install_time = f"{package_data.clear_elapsed_time:>3.0f}s" if package_data.sys_elapsed_time is not None: sys_install_time = f"{package_data.sys_elapsed_time:>3.0f}s" else: sys_install_time = "" return ( f"{package_data.package_spec:24}{package_data.overall_pf_str:12}" f"{package_data.clear_pip_pf_str:8}{package_data.clear_pipx_pf_str:8}" f"{clear_install_time:8}" f"{package_data.sys_pip_pf_str:8}{package_data.sys_pipx_pf_str:8}" f"{sys_install_time:8}" ) def format_report_table_footer(module_globals: ModuleGlobalsData) -> str: fail_list = [] prebuild_list = [] footer_string = "\nSummary\n" footer_string += "-" * 79 + "\n" for package_data in module_globals.install_data: clear_pip_pass = package_data.clear_pip_pass clear_pipx_pass = package_data.clear_pipx_pass sys_pip_pass = package_data.sys_pip_pass sys_pipx_pass = package_data.sys_pipx_pass if clear_pip_pass and clear_pipx_pass: continue elif not clear_pip_pass and sys_pip_pass and sys_pipx_pass: prebuild_list.append(package_data.package_spec) else: fail_list.append(package_data.package_spec) if fail_list: footer_string += "FAILS:\n" for failed_package_spec in sorted(fail_list, key=str.lower): footer_string += f" {failed_package_spec}\n" if prebuild_list: footer_string += "Needs prebuilt wheel:\n" for prebuild_package_spec in sorted(prebuild_list, key=str.lower): footer_string += f" {prebuild_package_spec}\n" dt_string = module_globals.test_end.strftime("%Y-%m-%d %H:%M:%S") el_datetime = module_globals.test_end - module_globals.test_start el_datetime = el_datetime - timedelta(microseconds=el_datetime.microseconds) footer_string += f"\nFinished {dt_string}\n" footer_string += f"Elapsed: {el_datetime}" return footer_string def verify_installed_resources( resource_type: str, captured_outerr, package_name: str, test_error_fh: io.StringIO, deps: bool = False, ) -> bool: resource_name = {"app": "apps", "man": "man_pages"}[resource_type] resource_name_long = {"app": "apps", "man": "manual pages"}[resource_type] package_resources = PKG[package_name][resource_name].copy() if deps: package_resources += PKG[package_name][f"{resource_name}_of_dependencies"] if len(package_resources) == 0: return True reported_resources_re = re.search( r"These " + resource_name_long + r" are now available\n((?: - [^\n]+\n)*)", captured_outerr.out, re.DOTALL, ) if reported_resources_re: reported_resources = [x.strip()[2:] for x in reported_resources_re.group(1).strip().split("\n")] if set(reported_resources) != set(package_resources): resource_success = False print("verify_install: REPORTED APPS DO NOT MATCH PACKAGE", file=test_error_fh) print( f"pipx reported %s: {reported_resources}" % resource_name, file=test_error_fh, ) print( f" true package %s: {package_resources}" % resource_name, file=test_error_fh, ) else: resource_success = True else: resource_success = False print("verify_install: APPS TESTING ERROR", file=test_error_fh) return resource_success def verify_post_install( pipx_exit_code: int, captured_outerr, caplog, package_name: str, test_error_fh: io.StringIO, using_clear_path: bool, deps: bool = False, ) -> tuple[bool, bool | None, Path | None]: pip_error_file = None caplog_problem = False install_success = f"installed package {package_name}" in captured_outerr.out for record in caplog.records: if "โš ๏ธ" in record.message or "WARNING" in record.message: if using_clear_path or "was already on your PATH" not in record.message: caplog_problem = True print("verify_install: WARNING IN CAPLOG:", file=test_error_fh) print(record.message, file=test_error_fh) if "Fatal error from pip prevented installation" in record.message: pip_error_file_re = re.search(r"pip output in file:\s+(\S.+)$", record.message) if pip_error_file_re: pip_error_file = Path(pip_error_file_re.group(1)) if install_success and PKG[package_name].get("apps", None) is not None: app_success = verify_installed_resources("app", captured_outerr, package_name, test_error_fh, deps=deps) else: app_success = True if install_success and ( PKG[package_name].get("man_pages", None) is not None or PKG[package_name].get("man_pages_of_dependencies", None) is not None ): man_success = verify_installed_resources("man", captured_outerr, package_name, test_error_fh, deps=deps) else: man_success = True pip_pass = not ((pipx_exit_code != 0) and f"Error installing {package_name}" in captured_outerr.err) pipx_pass: bool | None if pip_pass: pipx_pass = install_success and not caplog_problem and app_success and man_success else: pipx_pass = None return pip_pass, pipx_pass, pip_error_file def print_error_report( module_globals: ModuleGlobalsData, command_captured, test_error_fh: io.StringIO, package_spec: str, test_type: str, pip_error_file: Path | None, ) -> None: with module_globals.errors_path.open("a", encoding="utf-8") as errors_fh: print("\n\n", file=errors_fh) print("=" * 79, file=errors_fh) print( f"{package_spec:24}{test_type:16}{module_globals.sys_platform:16}{module_globals.py_version_display}", file=errors_fh, ) print("\nSTDOUT:", file=errors_fh) print("-" * 76, file=errors_fh) print(command_captured.out, end="", file=errors_fh) print("\nSTDERR:", file=errors_fh) print("-" * 76, file=errors_fh) print(command_captured.err, end="", file=errors_fh) if pip_error_file is not None: print("\nPIP ERROR LOG FILE:", file=errors_fh) print("-" * 76, file=errors_fh) with pip_error_file.open("r") as pip_error_fh: print(pip_error_fh.read(), end="", file=errors_fh) print("\n\nTEST WARNINGS / ERRORS:", file=errors_fh) print("-" * 76, file=errors_fh) print(test_error_fh.getvalue(), end="", file=errors_fh) def install_and_verify( capsys: pytest.CaptureFixture, caplog, monkeypatch, module_globals: ModuleGlobalsData, using_clear_path: bool, package_data: PackageData, deps: bool, ) -> tuple[bool, bool | None, float]: _ = capsys.readouterr() caplog.clear() test_error_fh = io.StringIO() monkeypatch.setenv("PATH", os.getenv("PATH_TEST" if using_clear_path else "PATH_ORIG")) start_time = time.time() pipx_exit_code = run_pipx_cli( ["install", package_data.package_spec, "--verbose"] + (["--include-deps"] if deps else []) ) elapsed_time = time.time() - start_time captured = capsys.readouterr() pip_pass, pipx_pass, pip_error_file = verify_post_install( pipx_exit_code, captured, caplog, package_data.package_name, test_error_fh, using_clear_path=using_clear_path, deps=deps, ) if not pip_pass or not pipx_pass: print_error_report( module_globals, captured, test_error_fh, package_data.package_spec, "clear PATH" if using_clear_path else "sys PATH", pip_error_file, ) return pip_pass, pipx_pass, elapsed_time def install_package_both_paths( monkeypatch, capsys: pytest.CaptureFixture, caplog, module_globals: ModuleGlobalsData, pipx_temp_env, package_name: str, deps: bool = False, ) -> bool: package_data = PackageData() module_globals.install_data.append(package_data) package_data.package_name = package_name package_data.package_spec = PKG[package_name]["spec"] ( package_data.clear_pip_pass, package_data.clear_pipx_pass, package_data.clear_elapsed_time, ) = install_and_verify( capsys, caplog, monkeypatch, module_globals, using_clear_path=True, package_data=package_data, deps=deps, ) if not package_data.clear_pip_pass: # if we fail to install due to pip install error, try again with # full system path ( package_data.sys_pip_pass, package_data.sys_pipx_pass, package_data.sys_elapsed_time, ) = install_and_verify( capsys, caplog, monkeypatch, module_globals, using_clear_path=False, package_data=package_data, deps=deps, ) package_data.overall_pass = bool( (package_data.clear_pip_pass and package_data.clear_pipx_pass) or (package_data.sys_pip_pass and package_data.sys_pipx_pass) ) with module_globals.report_path.open("a", encoding="utf-8") as report_fh: print(format_report_table_row(package_data), file=report_fh, flush=True) if not package_data.clear_pip_pass and not package_data.sys_pip_pass: # Use xfail to specify error that is from a pip installation problem pytest.xfail("pip installation error") return package_data.overall_pass # use class scope to start and finish at end of all parametrized tests @pytest.fixture(scope="class") def start_end_test_class(module_globals: ModuleGlobalsData, request): reports_path = Path(REPORTS_DIR) reports_path.mkdir(exist_ok=True, parents=True) module_globals.reset() module_globals.test_class = getattr(request.cls, "test_class", "unknown") report_filename = ( f"{REPORT_FILENAME_ROOT}_" f"{module_globals.test_class}_" f"report_" f"{module_globals.sys_platform}_" f"{module_globals.py_version_short}_" f"{module_globals.test_start.strftime('%Y%m%d')}.txt" ) errors_filename = ( f"{REPORT_FILENAME_ROOT}_" f"{module_globals.test_class}_" f"errors_" f"{module_globals.sys_platform}_" f"{module_globals.py_version_short}_" f"{module_globals.test_start.strftime('%Y%m%d')}.txt" ) module_globals.report_path = reports_path / report_filename module_globals.errors_path = reports_path / errors_filename write_report_legend(reports_path / f"{REPORT_FILENAME_ROOT}_report_legend.txt") with module_globals.report_path.open("a", encoding="utf-8") as report_fh: print(format_report_table_header(module_globals), file=report_fh) yield module_globals.test_end = datetime.now() with module_globals.report_path.open("a", encoding="utf-8") as report_fh: print(format_report_table_footer(module_globals), file=report_fh) class TestAllPackagesNoDeps: test_class = "nodeps" @pytest.mark.parametrize("package_name", PACKAGE_NAME_LIST) @pytest.mark.all_packages def test_all_packages( self, monkeypatch, capsys: pytest.CaptureFixture, caplog, module_globals: ModuleGlobalsData, start_end_test_class, pipx_temp_env, package_name: str, ): pip_cache_purge() assert install_package_both_paths( monkeypatch, capsys, caplog, module_globals, pipx_temp_env, package_name, deps=False, ) class TestAllPackagesDeps: test_class = "deps" @pytest.mark.parametrize("package_name", PACKAGE_NAME_LIST) @pytest.mark.all_packages def test_deps_all_packages( self, monkeypatch, capsys: pytest.CaptureFixture, caplog, module_globals: ModuleGlobalsData, start_end_test_class, pipx_temp_env, package_name: str, ): pip_cache_purge() assert install_package_both_paths( monkeypatch, capsys, caplog, module_globals, pipx_temp_env, package_name, deps=True, ) pipx-1.15.0/tests/test_interpreter.py000066400000000000000000000230711521706623000177010ustar00rootroot00000000000000import shutil import subprocess import sys from pathlib import Path from unittest.mock import Mock import pytest import pipx.interpreter import pipx.paths import pipx.standalone_python from pipx.constants import WINDOWS, FetchPythonOptions from pipx.interpreter import ( InterpreterResolutionError, _find_default_windows_python, _resolve_python, find_python_interpreter, ) from pipx.util import PipxError original_which = shutil.which @pytest.mark.skipif(not sys.platform.startswith("win"), reason="Looks for Python.exe") @pytest.mark.parametrize("venv", [True, False]) def test_windows_python_with_version(monkeypatch, venv): def which(name): if name == "py": return "py" return original_which(name) major = sys.version_info.major minor = sys.version_info.minor monkeypatch.setattr(pipx.interpreter, "has_venv", lambda: venv) monkeypatch.setattr(shutil, "which", which) python_path = find_python_interpreter(f"{major}.{minor}") assert python_path is not None assert f"{major}.{minor}" in python_path or f"{major}{minor}" in python_path assert python_path.endswith("python.exe") @pytest.mark.skipif(not sys.platform.startswith("win"), reason="Looks for Python.exe") @pytest.mark.parametrize("venv", [True, False]) def test_windows_python_with_python_and_version(monkeypatch, venv): def which(name): if name == "py": return "py" return original_which(name) major = sys.version_info.major minor = sys.version_info.minor monkeypatch.setattr(pipx.interpreter, "has_venv", lambda: venv) monkeypatch.setattr(shutil, "which", which) python_path = find_python_interpreter(f"python{major}.{minor}") assert python_path is not None assert f"{major}.{minor}" in python_path or f"{major}{minor}" in python_path assert python_path.endswith("python.exe") @pytest.mark.skipif(not sys.platform.startswith("win"), reason="Looks for Python.exe") @pytest.mark.parametrize("venv", [True, False]) def test_windows_python_with_python_and_unavailable_version(monkeypatch, venv): def which(name): if name == "py": return "py" return original_which(name) major = sys.version_info.major + 99 minor = sys.version_info.minor monkeypatch.setattr(pipx.interpreter, "has_venv", lambda: venv) monkeypatch.setattr(shutil, "which", which) with pytest.raises(InterpreterResolutionError) as e: find_python_interpreter(f"python{major}.{minor}") assert "py --list" in str(e) def test_windows_python_no_version_with_venv(monkeypatch): monkeypatch.setattr(pipx.interpreter, "has_venv", lambda: True) assert _find_default_windows_python() == sys.executable def test_windows_python_no_version_no_venv_with_py(monkeypatch): def which(name): return "py" monkeypatch.setattr(pipx.interpreter, "has_venv", lambda: False) monkeypatch.setattr(shutil, "which", which) assert _find_default_windows_python() == "py" def test_windows_python_no_version_no_venv_python_present(monkeypatch): def which(name): if name == "python": return "python" # Note: returns False for "py" monkeypatch.setattr(pipx.interpreter, "has_venv", lambda: False) monkeypatch.setattr(shutil, "which", which) assert _find_default_windows_python() == "python" def test_windows_python_no_version_no_venv_no_python(monkeypatch): def which(name): return None monkeypatch.setattr(pipx.interpreter, "has_venv", lambda: False) monkeypatch.setattr(shutil, "which", which) with pytest.raises(PipxError): _find_default_windows_python() # Test the checks for the store Python. def test_windows_python_no_venv_store_python(monkeypatch): def which(name): if name == "python": return "WindowsApps" class dummy_runner: def __init__(self, rc, out): self.rc = rc self.out = out def __call__(self, *args, **kw): ret = Mock() ret.returncode = self.rc ret.stdout = self.out return ret monkeypatch.setattr(pipx.interpreter, "has_venv", lambda: False) monkeypatch.setattr(shutil, "which", which) # Store version stub gives return code 9009 monkeypatch.setattr(subprocess, "run", dummy_runner(9009, "")) with pytest.raises(PipxError): _find_default_windows_python() # Even if it doesn't, it returns no output monkeypatch.setattr(subprocess, "run", dummy_runner(0, "")) with pytest.raises(PipxError): _find_default_windows_python() # If it *does* pass the tests, we use it as it's not the stub monkeypatch.setattr(subprocess, "run", dummy_runner(0, "3.8")) assert _find_default_windows_python() == "WindowsApps" def test_resolve_python_absolute_path() -> None: result = _resolve_python(sys.executable) assert Path(result).resolve() == Path(sys.executable).resolve() def test_resolve_python_executable_name() -> None: candidates = [ f"python{sys.version_info.major}.{sys.version_info.minor}", f"python{sys.version_info.major}", "python", ] name = next((c for c in candidates if shutil.which(c)), None) assert name is not None, "no python executable on PATH" result = _resolve_python(name) assert Path(result).is_absolute() assert Path(result).is_file() @pytest.mark.skipif(WINDOWS, reason="Unix command resolution") def test_resolve_python_bare_version() -> None: major = sys.version_info.major minor = sys.version_info.minor result = _resolve_python(f"{major}.{minor}") assert Path(result).is_absolute() assert Path(result).is_file() def test_resolve_python_relative_path_resolves_to_absolute(tmp_path: Path) -> None: fake_python = tmp_path / "mypython" fake_python.touch(mode=0o755) result = _resolve_python(str(fake_python)) assert Path(result).is_absolute() def test_resolve_python_invalid_raises() -> None: with pytest.raises(InterpreterResolutionError, match="PIPX_DEFAULT_PYTHON"): _resolve_python("no_such_python_99.99") def test_resolve_python_invalid_version_raises() -> None: with pytest.raises(InterpreterResolutionError, match="PIPX_DEFAULT_PYTHON"): _resolve_python("99.99") def test_find_python_interpreter_by_path(monkeypatch): interpreter_path = sys.executable assert interpreter_path == find_python_interpreter(interpreter_path) def test_find_python_interpreter_by_version(monkeypatch): major = sys.version_info.major minor = sys.version_info.minor python_path = find_python_interpreter(f"python{major}.{minor}") assert python_path == f"python{major}.{minor}" or f"Python\\{major}.{minor}" in python_path def test_find_python_interpreter_by_wrong_path_raises(monkeypatch): interpreter_path = sys.executable + "99" with pytest.raises(InterpreterResolutionError) as e: find_python_interpreter(interpreter_path) assert "like a path" in str(e) def test_find_python_interpreter_missing_on_path_raises(monkeypatch): interpreter = "1.1" with pytest.raises(InterpreterResolutionError) as e: find_python_interpreter(interpreter) assert "Python Launcher" in str(e) assert "on your PATH" in str(e) @pytest.mark.parametrize( "fetch_python", [ pytest.param(FetchPythonOptions.MISSING, id="missing"), pytest.param(FetchPythonOptions.ALWAYS, id="always"), ], ) def test_fetch_missing_python(monkeypatch, mocked_github_api, fetch_python): def which(name): return None monkeypatch.setattr(shutil, "which", which) major = sys.version_info.major minor = sys.version_info.minor target_python = f"{major}.{minor}" python_path = find_python_interpreter(target_python, fetch_python=fetch_python) assert python_path is not None assert target_python in python_path assert str(pipx.paths.ctx.standalone_python_cachedir) in python_path if WINDOWS: assert python_path.endswith("python.exe") else: assert python_path.endswith("python3") subprocess.run([python_path, "-c", "import sys; print(sys.executable)"], check=True) def test_fetch_python_always_invalid_version_raises(monkeypatch): with pytest.raises(InterpreterResolutionError, match="python-build-standalone"): find_python_interpreter("3.0", fetch_python=FetchPythonOptions.ALWAYS) @pytest.mark.parametrize( "python_version", [ pytest.param("/usr/bin/python3.13", id="absolute-path"), pytest.param("relative/python3.13", id="relative-path"), pytest.param(__file__, id="existing-file"), ], ) def test_fetch_python_always_rejects_paths(python_version): with pytest.raises(PipxError, match="requires a Python version"): find_python_interpreter(python_version, fetch_python=FetchPythonOptions.ALWAYS) def test_find_python_interpreter_py_launcher_failure_without_fetch_raises(monkeypatch): monkeypatch.setattr(shutil, "which", lambda name: None) def raise_called_process(*args, **kwargs): raise subprocess.CalledProcessError(1, ["py"]) monkeypatch.setattr(pipx.interpreter, "find_py_launcher_python", raise_called_process) with pytest.raises(InterpreterResolutionError, match="py launcher"): find_python_interpreter("3.13", fetch_python=FetchPythonOptions.NEVER) def test_find_python_interpreter_py_launcher_success(monkeypatch): monkeypatch.setattr(shutil, "which", lambda name: None) monkeypatch.setattr(pipx.interpreter, "find_py_launcher_python", lambda v: f"/fake/python{v}") assert find_python_interpreter("3.13", fetch_python=FetchPythonOptions.NEVER) == "/fake/python3.13" pipx-1.15.0/tests/test_list.py000066400000000000000000000217161521706623000163150ustar00rootroot00000000000000import json import os import re import shutil import sys import time import pytest from helpers import ( PIPX_METADATA_LEGACY_VERSIONS, app_name, assert_package_metadata, create_package_info_ref, mock_legacy_venv, remove_venv_interpreter, run_pipx_cli, skip_if_windows, ) from package_info import PKG from pipx import constants, paths, shared_libs, venv from pipx.pipx_metadata_file import PackageInfo, _json_decoder_object_hook from pipx.util import PipxError def test_cli(pipx_temp_env, monkeypatch, capsys): assert not run_pipx_cli(["list"]) captured = capsys.readouterr() assert "nothing has been installed with pipx" in captured.err @skip_if_windows def test_cli_global(pipx_temp_env, monkeypatch, capsys): assert not run_pipx_cli(["install", "pycowsay"]) captured = capsys.readouterr() assert "installed package" in captured.out assert not run_pipx_cli(["list", "--global"]) captured = capsys.readouterr() assert "nothing has been installed with pipx" in captured.err def test_missing_interpreter(pipx_temp_env, monkeypatch, capsys): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["list"]) captured = capsys.readouterr() assert "package pycowsay has invalid interpreter" not in captured.err remove_venv_interpreter("pycowsay") assert run_pipx_cli(["list"]) captured = capsys.readouterr() assert "package pycowsay has invalid interpreter" in captured.err def test_list_suffix(pipx_temp_env, monkeypatch, capsys): suffix = "_x" assert not run_pipx_cli(["install", "pycowsay", f"--suffix={suffix}"]) assert not run_pipx_cli(["list"]) captured = capsys.readouterr() assert f"package pycowsay 0.0.0.2 (pycowsay{suffix})," in captured.out @pytest.mark.parametrize("metadata_version", PIPX_METADATA_LEGACY_VERSIONS) def test_list_legacy_venv(pipx_temp_env, monkeypatch, capsys, metadata_version): assert not run_pipx_cli(["install", "pycowsay"]) mock_legacy_venv("pycowsay", metadata_version=metadata_version) if metadata_version is None: assert run_pipx_cli(["list"]) captured = capsys.readouterr() assert "package pycowsay has missing internal pipx metadata" in captured.err else: assert not run_pipx_cli(["list"]) captured = capsys.readouterr() assert "package pycowsay 0.0.0.2," in captured.out @pytest.mark.parametrize("metadata_version", ["0.1"]) def test_list_suffix_legacy_venv(pipx_temp_env, monkeypatch, capsys, metadata_version): suffix = "_x" assert not run_pipx_cli(["install", "pycowsay", f"--suffix={suffix}"]) mock_legacy_venv(f"pycowsay{suffix}", metadata_version=metadata_version) assert not run_pipx_cli(["list"]) captured = capsys.readouterr() assert f"package pycowsay 0.0.0.2 (pycowsay{suffix})," in captured.out def test_list_json(pipx_temp_env, capsys): pipx_venvs_dir = paths.ctx.home / "venvs" venv_bin_dir = "Scripts" if constants.WINDOWS else "bin" assert not run_pipx_cli(["install", PKG["pycowsay"]["spec"]]) assert not run_pipx_cli(["install", PKG["pylint"]["spec"]]) assert not run_pipx_cli(["inject", "pylint", PKG["black"]["spec"]]) captured = capsys.readouterr() assert not run_pipx_cli(["list", "--json"]) captured = capsys.readouterr() assert not re.search(r"\S", captured.err) json_parsed = json.loads(captured.out, object_hook=_json_decoder_object_hook) # raises error if not valid json assert sorted(json_parsed["venvs"].keys()) == ["pycowsay", "pylint"] # pycowsay venv pycowsay_package_ref = create_package_info_ref("pycowsay", "pycowsay", pipx_venvs_dir) assert_package_metadata( PackageInfo(**json_parsed["venvs"]["pycowsay"]["metadata"]["main_package"]), pycowsay_package_ref, ) assert json_parsed["venvs"]["pycowsay"]["metadata"]["injected_packages"] == {} # pylint venv pylint_package_ref = create_package_info_ref( "pylint", "pylint", pipx_venvs_dir, app_paths_of_dependencies={ "dill": [ pipx_venvs_dir / "pylint" / venv_bin_dir / "get_gprof", pipx_venvs_dir / "pylint" / venv_bin_dir / "get_objgraph", pipx_venvs_dir / "pylint" / venv_bin_dir / "undill", ], "isort": [ pipx_venvs_dir / "pylint" / venv_bin_dir / app_name("isort"), pipx_venvs_dir / "pylint" / venv_bin_dir / app_name("isort-identify-imports"), ], }, ) assert_package_metadata( PackageInfo(**json_parsed["venvs"]["pylint"]["metadata"]["main_package"]), pylint_package_ref, ) assert sorted(json_parsed["venvs"]["pylint"]["metadata"]["injected_packages"].keys()) == ["black"] black_package_ref = create_package_info_ref("pylint", "black", pipx_venvs_dir, include_apps=False) assert_package_metadata( PackageInfo(**json_parsed["venvs"]["pylint"]["metadata"]["injected_packages"]["black"]), black_package_ref, ) def test_list_short(pipx_temp_env, monkeypatch, capsys): assert not run_pipx_cli(["install", PKG["pycowsay"]["spec"]]) assert not run_pipx_cli(["install", PKG["pylint"]["spec"]]) captured = capsys.readouterr() assert not run_pipx_cli(["list", "--short"]) captured = capsys.readouterr() assert "pycowsay 0.0.0.2" in captured.out assert "pylint 3.0.4" in captured.out def test_list_standalone_interpreter(pipx_temp_env, monkeypatch, mocked_github_api, capsys): def which(name): return None monkeypatch.setattr(shutil, "which", which) major = sys.version_info.major minor = sys.version_info.minor target_python = f"{major}.{minor}" assert not run_pipx_cli( [ "install", "--fetch-python=missing", "--python", target_python, PKG["pycowsay"]["spec"], ] ) captured = capsys.readouterr() assert not run_pipx_cli(["list"]) captured = capsys.readouterr() assert "standalone" in captured.out def test_list_does_not_trigger_maintenance(pipx_temp_env, caplog): assert not run_pipx_cli(["install", PKG["pycowsay"]["spec"]]) assert not run_pipx_cli(["install", PKG["pylint"]["spec"]]) now = time.time() shared_libs.shared_libs.create(verbose=True, pip_args=[]) shared_libs.shared_libs.has_been_updated_this_run = False access_time = now # this can be anything os.utime( shared_libs.shared_libs.pip_path, (access_time, -shared_libs.SHARED_LIBS_MAX_AGE_SEC - 5 * 60 + now), ) assert shared_libs.shared_libs.needs_upgrade run_pipx_cli(["list"]) assert not shared_libs.shared_libs.has_been_updated_this_run assert shared_libs.shared_libs.needs_upgrade # same test with --skip-maintenance, which is a no-op # we expect the same result, along with a warning os.utime( shared_libs.shared_libs.pip_path, (access_time, -shared_libs.SHARED_LIBS_MAX_AGE_SEC - 5 * 60 + now), ) shared_libs.shared_libs.has_been_updated_this_run = False assert shared_libs.shared_libs.needs_upgrade run_pipx_cli(["list", "--skip-maintenance"]) assert not shared_libs.shared_libs.has_been_updated_this_run assert shared_libs.shared_libs.needs_upgrade def test_list_pinned_packages(pipx_temp_env, monkeypatch, capsys): assert not run_pipx_cli(["install", PKG["pycowsay"]["spec"]]) assert not run_pipx_cli(["install", PKG["black"]["spec"]]) captured = capsys.readouterr() assert not run_pipx_cli(["pin", "black"]) assert not run_pipx_cli(["list", "--pinned"]) captured = capsys.readouterr() assert "black 22.8.0" in captured.out assert "pycowsay 0.0.0.2" not in captured.out def test_list_pinned_packages_include_injected(pipx_temp_env, monkeypatch, capsys): assert not run_pipx_cli(["install", PKG["pylint"]["spec"], PKG["nox"]["spec"]]) assert not run_pipx_cli(["inject", "pylint", PKG["black"]["spec"]]) assert not run_pipx_cli(["pin", "pylint"]) assert not run_pipx_cli(["pin", "nox"]) captured = capsys.readouterr() assert not run_pipx_cli(["list", "--pinned", "--include-injected"]) captured = capsys.readouterr() assert "nox 2023.4.22" in captured.out assert "pylint 3.0.4" in captured.out assert "black 22.8.0 (injected in venv pylint)" in captured.out def test_list_installed_packages_error(monkeypatch, tmp_path, fake_process): fake_venv = venv.Venv(tmp_path / "fake_venv") pip_list_args = [str(fake_venv.python_path), "-m", "pip", "list", "--format=json"] fake_process.register(pip_list_args, returncode=1, stderr="unit test stderr") with pytest.raises(PipxError) as excinfo: fake_venv.list_installed_packages() # Collapse the wrapping pipx_wrap applies on render so we can assert on substrings. rendered = " ".join(str(excinfo.value).split()) assert "Failed to execute" in rendered assert "Process exited with return code 1" in rendered assert "unit test stderr" in rendered pipx-1.15.0/tests/test_main.py000066400000000000000000000146441521706623000162700ustar00rootroot00000000000000import argparse import sys from unittest import mock import pytest from helpers import run_pipx_cli from pipx import constants, main def test_help_text(monkeypatch, capsys): mock_exit = mock.Mock(side_effect=ValueError("raised in test to exit early")) with mock.patch.object(sys, "exit", mock_exit), pytest.raises(ValueError, match="raised in test to exit early"): assert not run_pipx_cli(["--help"]) captured = capsys.readouterr() assert "usage: pipx" in captured.out def test_help_command_text(monkeypatch, capsys): mock_exit = mock.Mock(side_effect=ValueError("raised in test to exit early")) with mock.patch.object(sys, "exit", mock_exit), pytest.raises(ValueError, match="raised in test to exit early"): assert not run_pipx_cli(["help"]) captured = capsys.readouterr() mock_exit.assert_called_with(0) assert "usage: pipx" in captured.out def test_help_command_for_subcommand(monkeypatch, capsys): mock_exit = mock.Mock(side_effect=ValueError("raised in test to exit early")) with mock.patch.object(sys, "exit", mock_exit), pytest.raises(ValueError, match="raised in test to exit early"): assert not run_pipx_cli(["help", "install"]) captured = capsys.readouterr() mock_exit.assert_called_with(0) assert "usage: pipx install" in captured.out def test_version(monkeypatch, capsys): mock_exit = mock.Mock(side_effect=ValueError("raised in test to exit early")) with mock.patch.object(sys, "exit", mock_exit), pytest.raises(ValueError, match="raised in test to exit early"): assert not run_pipx_cli(["--version"]) captured = capsys.readouterr() mock_exit.assert_called_with(0) assert main.__version__ in captured.out.strip() @pytest.mark.parametrize( ("argv", "executable", "expected"), [ ("/usr/bin/pipx", "", "pipx"), ("__main__.py", "/usr/bin/python", "/usr/bin/python -m pipx"), ], ) def test_prog_name(monkeypatch, argv, executable, expected): monkeypatch.setattr("pipx.main.sys.argv", [argv]) monkeypatch.setattr("pipx.main.sys.executable", executable) assert main.prog_name() == expected def test_limit_verbosity(): assert not run_pipx_cli(["list", "-qqq"]) assert not run_pipx_cli(["list", "-vvvv"]) def test_all_subcommands_have_func_registered(): parser, _ = main.get_command_parser() subparsers_action = next(a for a in parser._actions if isinstance(a, argparse._SubParsersAction)) for name, subparser in subparsers_action.choices.items(): assert callable(subparser._defaults.get("func")), f"{name!r} missing callable func default" for nested in (a for a in subparser._actions if isinstance(a, argparse._SubParsersAction)): for sub_name, sub_parser in nested.choices.items(): assert callable(sub_parser._defaults.get("func")), f"{sub_name!r} missing callable func default" @pytest.mark.parametrize( "argv", [ ["inject", "ansible-core", "--force", "ansible"], ["inject", "ansible-core", "ansible", "--force"], ], ) def test_inject_accepts_force_before_or_after_dependency(argv): parser, _ = main.get_command_parser() args = main.parse_pipx_args(parser, argv) assert args.package == "ansible-core" assert args.dependencies == ["ansible"] assert args.force is True def test_package_is_path_ignores_existing_directory(tmp_path, monkeypatch): # Regression for #1778: a directory in CWD with the same name as a # package should not be treated as a path. monkeypatch.chdir(tmp_path) (tmp_path / "commit-check").mkdir() # Should not raise even though a directory of that name exists in CWD. main.package_is_path("commit-check") @pytest.mark.parametrize( ("missing_raw", "python_raw", "expected_option", "expected_invalid"), [ pytest.param(None, None, constants.FetchPythonOptions.NEVER, False, id="unset"), pytest.param("1", None, constants.FetchPythonOptions.MISSING, False, id="legacy-truthy"), pytest.param("0", None, constants.FetchPythonOptions.NEVER, False, id="legacy-falsy"), pytest.param(" False ", None, constants.FetchPythonOptions.NEVER, False, id="legacy-whitespace-falsy"), pytest.param("n", None, constants.FetchPythonOptions.NEVER, False, id="legacy-falsy-n"), pytest.param(None, "always", constants.FetchPythonOptions.ALWAYS, False, id="explicit-always"), pytest.param(None, "garbage", constants.FetchPythonOptions.NEVER, True, id="invalid-value"), ], ) def test_compute_fetch_python(missing_raw, python_raw, expected_option, expected_invalid): option, invalid = constants._compute_fetch_python(missing_raw, python_raw) assert option is expected_option assert invalid is expected_invalid @pytest.mark.parametrize( ("invalid", "fetch_python_raw", "missing_raw", "expected"), [ pytest.param(True, "garbage", None, "PIPX_FETCH_PYTHON must be unset", id="invalid-value"), pytest.param(False, "missing", "1", "Setting both", id="both-env-vars"), ], ) def test_validate_fetch_python_raises(monkeypatch, invalid, fetch_python_raw, missing_raw, expected): monkeypatch.setattr(main, "_FETCH_PYTHON_INVALID", invalid) monkeypatch.setattr(main, "_FETCH_PYTHON_RAW", fetch_python_raw) monkeypatch.setattr(main, "_FETCH_MISSING_PYTHON_RAW", missing_raw) with pytest.raises(main.PipxError, match=expected): main._validate_fetch_python() def test_validate_fetch_python_deprecated_env_warning(monkeypatch, capsys): monkeypatch.setattr(main, "_FETCH_PYTHON_INVALID", False) monkeypatch.setattr(main, "_FETCH_PYTHON_RAW", None) monkeypatch.setattr(main, "_FETCH_MISSING_PYTHON_RAW", "1") main._validate_fetch_python() assert "PIPX_FETCH_MISSING_PYTHON is deprecated" in capsys.readouterr().err def test_validate_fetch_python_passes_when_unset(monkeypatch): monkeypatch.setattr(main, "_FETCH_PYTHON_INVALID", False) monkeypatch.setattr(main, "_FETCH_PYTHON_RAW", None) monkeypatch.setattr(main, "_FETCH_MISSING_PYTHON_RAW", None) main._validate_fetch_python() def test_deprecated_fetch_missing_python_silent_under_help(capsys): mock_exit = mock.Mock(side_effect=ValueError("raised in test to exit early")) with mock.patch.object(sys, "exit", mock_exit), pytest.raises(ValueError, match="raised in test to exit early"): run_pipx_cli(["install", "--fetch-missing-python", "--help"]) assert "--fetch-missing-python is deprecated" not in capsys.readouterr().err pipx-1.15.0/tests/test_max_logs.py000066400000000000000000000050531521706623000171470ustar00rootroot00000000000000"""Tests for PIPX_MAX_LOGS environment variable functionality.""" from pipx.main import _setup_log_file, delete_oldest_logs def test_delete_oldest_logs_keeps_specified_number(tmp_path): """Test that delete_oldest_logs keeps only the specified number of newest files.""" # Create 5 log files log_files = [] for i in range(5): log_file = tmp_path / f"cmd_2024-01-01_00.00.0{i}.log" log_file.touch() log_files.append(log_file) # Keep only 2 delete_oldest_logs(log_files, keep_number=2) # Check that only the 2 newest files remain remaining = list(tmp_path.glob("cmd_*.log")) assert len(remaining) == 2 def test_delete_oldest_logs_keeps_all_when_under_limit(tmp_path): """Test that delete_oldest_logs keeps all files when under the limit.""" log_files = [] for i in range(3): log_file = tmp_path / f"cmd_2024-01-01_00.00.0{i}.log" log_file.touch() log_files.append(log_file) delete_oldest_logs(log_files, keep_number=10) remaining = list(tmp_path.glob("cmd_*.log")) assert len(remaining) == 3 def test_max_logs_defaults_to_10(tmp_path, monkeypatch): """Test that max_logs defaults to 10 when PIPX_MAX_LOGS is not set.""" monkeypatch.delenv("PIPX_MAX_LOGS", raising=False) # Create 15 log files for i in range(15): log_file = tmp_path / f"cmd_2024-01-01_00.00.{i:02d}.log" log_file.touch() _setup_log_file(pipx_log_dir=tmp_path) # Should have 10 old logs + 1 new log = 11 total remaining = list(tmp_path.glob("cmd_*.log")) assert len(remaining) == 11 def test_max_logs_respects_env_var(tmp_path, monkeypatch): """Test that max_logs respects the PIPX_MAX_LOGS environment variable.""" monkeypatch.setenv("PIPX_MAX_LOGS", "5") # Create 15 log files for i in range(15): log_file = tmp_path / f"cmd_2024-01-01_00.00.{i:02d}.log" log_file.touch() _setup_log_file(pipx_log_dir=tmp_path) # Should have 5 old logs + 1 new log = 6 total remaining = list(tmp_path.glob("cmd_*.log")) assert len(remaining) == 6 def test_max_logs_with_large_value(tmp_path, monkeypatch): """Test that a large PIPX_MAX_LOGS value keeps all logs.""" monkeypatch.setenv("PIPX_MAX_LOGS", "100") # Create 5 log files for i in range(5): log_file = tmp_path / f"cmd_2024-01-01_00.00.0{i}.log" log_file.touch() _setup_log_file(pipx_log_dir=tmp_path) # Should have 5 old logs + 1 new log = 6 total remaining = list(tmp_path.glob("cmd_*.log")) assert len(remaining) == 6 pipx-1.15.0/tests/test_package_specifier.py000066400000000000000000000244161521706623000207660ustar00rootroot00000000000000from pathlib import Path import pytest from pipx.package_specifier import ( fix_package_name, parse_specifier_for_install, parse_specifier_for_metadata, parse_specifier_for_upgrade, valid_pypi_name, ) from pipx.util import PipxError TEST_DATA_PATH = "./testdata/test_package_specifier" @pytest.mark.parametrize( "package_spec_in,package_name_out", [ ("Black", "black"), ("https://github.com/ambv/black/archive/18.9b0.zip", None), ("black @ https://github.com/ambv/black/archive/18.9b0.zip", None), ("black-18.9b0-py36-none-any.whl", None), ("black-18.9b0.tar.gz", None), ], ) def test_valid_pypi_name(package_spec_in, package_name_out): assert valid_pypi_name(package_spec_in) == package_name_out @pytest.mark.parametrize( "package_spec_in,package_name,package_spec_out", [ ( "https://github.com/ambv/black/archive/18.9b0.zip", "black", "https://github.com/ambv/black/archive/18.9b0.zip", ), ( "nox@https://github.com/ambv/black/archive/18.9b0.zip", "black", "black @ https://github.com/ambv/black/archive/18.9b0.zip", ), ( "nox[extra]@https://github.com/ambv/black/archive/18.9b0.zip", "black", "black[extra] @ https://github.com/ambv/black/archive/18.9b0.zip", ), ], ) def test_fix_package_name(package_spec_in, package_name, package_spec_out): assert fix_package_name(package_spec_in, package_name) == package_spec_out _ROOT = Path(__file__).parents[1] @pytest.mark.parametrize( "package_spec_in,package_or_url_correct,valid_spec", [ ("pipx", "pipx", True), ("PiPx_stylized.name", "pipx-stylized-name", True), ("pipx==0.15.0", "pipx==0.15.0", True), ("pipx>=0.15.0", "pipx>=0.15.0", True), ("pipx<=0.15.0", "pipx<=0.15.0", True), ('pipx;python_version>="3.6"', "pipx", True), ('pipx==0.15.0;python_version>="3.6"', "pipx==0.15.0", True), ("pipx[extra1]", "pipx[extra1]", True), ("pipx[extra1, extra2]", "pipx[extra1,extra2]", True), ("src/pipx", str((_ROOT / "src" / "pipx").resolve()), True), ( "git+https://github.com/cs01/nox.git@5ea70723e9e6", "git+https://github.com/cs01/nox.git@5ea70723e9e6", True, ), ( "nox@git+https://github.com/cs01/nox.git@5ea70723e9e6", "nox @ git+https://github.com/cs01/nox.git@5ea70723e9e6", True, ), ( "https://github.com/ambv/black/archive/18.9b0.zip", "https://github.com/ambv/black/archive/18.9b0.zip", True, ), ( "black@https://github.com/ambv/black/archive/18.9b0.zip", "black @ https://github.com/ambv/black/archive/18.9b0.zip", True, ), ( "black @ https://github.com/ambv/black/archive/18.9b0.zip", "black @ https://github.com/ambv/black/archive/18.9b0.zip", True, ), ( "black[extra] @ https://github.com/ambv/black/archive/18.9b0.zip", "black[extra] @ https://github.com/ambv/black/archive/18.9b0.zip", True, ), ( 'my-project[cli] @ git+ssh://git@bitbucket.org/my-company/myproject.git ; python_version<"3.8"', "my-project[cli] @ git+ssh://git@bitbucket.org/my-company/myproject.git", True, ), ("path/doesnt/exist", "non-existent-path", False), ( "https:/github.com/ambv/black/archive/18.9b0.zip", "URL-syntax-error-slash", False, ), ], ) def test_parse_specifier_for_metadata(package_spec_in, package_or_url_correct, valid_spec, monkeypatch, root): monkeypatch.chdir(root) if valid_spec: package_or_url = parse_specifier_for_metadata(package_spec_in) assert package_or_url == package_or_url_correct else: # print package_spec_in for info in case no error is raised print(f"package_spec_in = {package_spec_in}") with pytest.raises(PipxError, match=r"^Unable to parse package spec"): package_or_url = parse_specifier_for_metadata(package_spec_in) @pytest.mark.parametrize( "package_spec_in,package_or_url_correct,valid_spec", [ ("pipx", "pipx", True), ("PiPx_stylized.name", "pipx-stylized-name", True), ("pipx==0.15.0", "pipx", True), ("pipx>=0.15.0", "pipx", True), ("pipx<=0.15.0", "pipx", True), ('pipx;python_version>="3.6"', "pipx", True), ('pipx==0.15.0;python_version>="3.6"', "pipx", True), ("pipx[extra1]", "pipx[extra1]", True), ("pipx[extra1, extra2]", "pipx[extra1,extra2]", True), ("src/pipx", str((_ROOT / "src" / "pipx").resolve()), True), ( "git+https://github.com/cs01/nox.git@5ea70723e9e6", "git+https://github.com/cs01/nox.git@5ea70723e9e6", True, ), ( "nox@git+https://github.com/cs01/nox.git@5ea70723e9e6", "nox @ git+https://github.com/cs01/nox.git@5ea70723e9e6", True, ), ( "https://github.com/ambv/black/archive/18.9b0.zip", "https://github.com/ambv/black/archive/18.9b0.zip", True, ), ( "black@https://github.com/ambv/black/archive/18.9b0.zip", "black @ https://github.com/ambv/black/archive/18.9b0.zip", True, ), ( "black @ https://github.com/ambv/black/archive/18.9b0.zip", "black @ https://github.com/ambv/black/archive/18.9b0.zip", True, ), ( "black[extra] @ https://github.com/ambv/black/archive/18.9b0.zip", "black[extra] @ https://github.com/ambv/black/archive/18.9b0.zip", True, ), ( 'my-project[cli] @ git+ssh://git@bitbucket.org/my-company/myproject.git ; python_version<"3.8"', "my-project[cli] @ git+ssh://git@bitbucket.org/my-company/myproject.git", True, ), ("path/doesnt/exist", "non-existent-path", False), ( "https:/github.com/ambv/black/archive/18.9b0.zip", "URL-syntax-error-slash", False, ), ], ) def test_parse_specifier_for_upgrade(package_spec_in, package_or_url_correct, valid_spec, monkeypatch, root): monkeypatch.chdir(root) if valid_spec: package_or_url = parse_specifier_for_upgrade(package_spec_in) assert package_or_url == package_or_url_correct else: # print package_spec_in for info in case no error is raised print(f"package_spec_in = {package_spec_in}") with pytest.raises(PipxError, match=r"^Unable to parse package spec"): package_or_url = parse_specifier_for_upgrade(package_spec_in) @pytest.mark.parametrize( "package_spec_in,pip_args_in,package_spec_expected,pip_args_expected,warning_str", [ ('pipx==0.15.0;python_version>="3.6"', [], "pipx==0.15.0", [], None), ("pipx==0.15.0", ["--editable"], "pipx==0.15.0", [], "Ignoring --editable"), ( 'pipx==0.15.0;python_version>="3.6"', [], "pipx==0.15.0", [], 'Ignoring environment markers (python_version >= "3.6") in package', ), ( "pipx==0.15.0", ["--no-cache-dir", "--editable"], "pipx==0.15.0", ["--no-cache-dir"], "Ignoring --editable", ), ( "git+https://github.com/cs01/nox.git@5ea70723e9e6", ["--editable"], "git+https://github.com/cs01/nox.git@5ea70723e9e6", [], "Ignoring --editable", ), ( "https://github.com/ambv/black/archive/18.9b0.zip", ["--editable"], "https://github.com/ambv/black/archive/18.9b0.zip", [], "Ignoring --editable", ), ( "src/pipx", ["--editable"], str(Path("src/pipx").resolve()), ["--editable"], None, ), ( TEST_DATA_PATH + "/local_extras", [], str(Path(TEST_DATA_PATH + "/local_extras").resolve), [], None, ), ( TEST_DATA_PATH + "/local_extras[cow]", [], str(Path(TEST_DATA_PATH + "/local_extras").resolve) + "[cow]", [], None, ), ( TEST_DATA_PATH + "/local_extras", ["--editable"], str(Path(TEST_DATA_PATH + "/local_extras").resolve), ["--editable"], None, ), ( TEST_DATA_PATH + "/local_extras[cow]", ["--editable"], str(Path(TEST_DATA_PATH + "/local_extras").resolve) + "[cow]", ["--editable"], None, ), ], ) def test_parse_specifier_for_install( caplog, package_spec_in, pip_args_in, package_spec_expected, pip_args_expected, warning_str, monkeypatch, root, ): monkeypatch.chdir(root) parse_specifier_for_install(package_spec_in, pip_args_in) if warning_str is not None: assert warning_str in caplog.text @pytest.mark.parametrize( "pip_args_in,pip_args_expected", [ ( ["-c", "https://example.com/constraints.txt"], ["-c", "https://example.com/constraints.txt"], ), ( ["--constraint", "https://example.com/constraints.txt"], ["--constraint", "https://example.com/constraints.txt"], ), ( ["--constraint=https://example.com/constraints.txt"], ["--constraint=https://example.com/constraints.txt"], ), ( ["-c", "constraints.txt"], ["-c", str(Path("constraints.txt").resolve())], ), ( ["--constraint=constraints.txt"], [f"--constraint={Path('constraints.txt').resolve()}"], ), ], ) def test_parse_specifier_for_install_constraint_args( pip_args_in: list[str], pip_args_expected: list[str], ) -> None: _, pip_args_out = parse_specifier_for_install("pipx", pip_args_in) assert pip_args_out == pip_args_expected pipx-1.15.0/tests/test_pin.py000066400000000000000000000061261521706623000161260ustar00rootroot00000000000000from helpers import run_pipx_cli from package_info import PKG def test_pin(capsys, pipx_temp_env, caplog): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["pin", "pycowsay"]) assert not run_pipx_cli(["upgrade", "pycowsay"]) assert "Not upgrading pinned package pycowsay" in caplog.text def test_pin_with_suffix(capsys, pipx_temp_env, caplog): assert not run_pipx_cli(["install", PKG["black"]["spec"], "--suffix", "@1"]) assert not run_pipx_cli(["pin", "black@1"]) assert not run_pipx_cli(["upgrade", "black@1"]) assert "Not upgrading pinned package black@1" in caplog.text def test_pin_warning(capsys, pipx_temp_env, caplog): assert not run_pipx_cli(["install", PKG["nox"]["spec"]]) assert not run_pipx_cli(["pin", "nox"]) assert not run_pipx_cli(["pin", "nox"]) assert "Package nox already pinned ๐Ÿ˜ด" in caplog.text def test_pin_not_installed_package(capsys, pipx_temp_env): assert run_pipx_cli(["pin", "abc"]) captured = capsys.readouterr() assert "Package abc is not installed" in captured.err def test_pin_injected_packages_only(capsys, pipx_temp_env, caplog): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["inject", "pycowsay", "black", PKG["pylint"]["spec"]]) assert not run_pipx_cli(["pin", "pycowsay", "--injected-only"]) captured = capsys.readouterr() assert "Pinned 2 packages in venv pycowsay" in captured.out assert "black" in captured.out assert "pylint" in captured.out assert not run_pipx_cli(["upgrade", "pycowsay", "--include-injected"]) assert "Not upgrading pinned package black in venv pycowsay" in caplog.text assert "Not upgrading pinned package pylint in venv pycowsay" in caplog.text def test_pin_injected_packages_only_when_main_package_pinned(capsys, pipx_temp_env, caplog): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["pin", "pycowsay"]) assert not run_pipx_cli(["inject", "pycowsay", "black", PKG["pylint"]["spec"]]) _ = capsys.readouterr() caplog.clear() assert not run_pipx_cli(["pin", "pycowsay", "--injected-only"]) captured = capsys.readouterr() assert "Pinned 2 packages in venv pycowsay" in captured.out assert "black" in captured.out assert "pylint" in captured.out assert "Package pycowsay already pinned" not in caplog.text assert not run_pipx_cli(["upgrade", "pycowsay", "--include-injected"]) assert "Not upgrading pinned package pycowsay" in caplog.text assert "Not upgrading pinned package black in venv pycowsay" in caplog.text assert "Not upgrading pinned package pylint in venv pycowsay" in caplog.text def test_pin_injected_packages_with_skip(capsys, pipx_temp_env): assert not run_pipx_cli(["install", "black"]) assert not run_pipx_cli(["inject", "black", PKG["pylint"]["spec"], PKG["isort"]["spec"]]) _ = capsys.readouterr() assert not run_pipx_cli(["pin", "black", "--injected-only", "--skip", "isort"]) captured = capsys.readouterr() assert "pylint" in captured.out assert "isort" not in captured.out pipx-1.15.0/tests/test_pipx_metadata_file.py000066400000000000000000000075141521706623000211610ustar00rootroot00000000000000import sys from dataclasses import replace from pathlib import Path import pytest from helpers import assert_package_metadata, create_package_info_ref, run_pipx_cli from package_info import PKG from pipx import paths from pipx.pipx_metadata_file import PackageInfo, PipxMetadata from pipx.util import PipxError TEST_PACKAGE1 = PackageInfo( package="test_package", package_or_url="test_package_url", pip_args=[], include_apps=True, include_dependencies=False, apps=["testapp"], app_paths=[Path("/usr/bin")], apps_of_dependencies=["dep1"], app_paths_of_dependencies={"dep1": [Path("bin")]}, man_pages=[str(Path("man1/testapp.1"))], man_pages_of_dependencies=[str(Path("man1/dep1.1"))], man_paths_of_dependencies={"dep1": [Path("man1/dep1.1")]}, package_version="0.1.2", ) TEST_PACKAGE2 = PackageInfo( package="inj_package", package_or_url="inj_package_url", pip_args=["-e"], include_apps=True, include_dependencies=False, apps=["injapp"], app_paths=[Path("/usr/bin")], apps_of_dependencies=["dep2"], app_paths_of_dependencies={"dep2": [Path("bin")]}, man_pages=[str(Path("man1/injapp.1"))], man_pages_of_dependencies=[str(Path("man1/dep2.1"))], man_paths_of_dependencies={"dep2": [Path("man1/dep2.1")]}, package_version="6.7.8", ) def test_pipx_metadata_file_create(tmp_path): venv_dir = tmp_path / TEST_PACKAGE1.package venv_dir.mkdir() pipx_metadata = PipxMetadata(venv_dir) pipx_metadata.main_package = TEST_PACKAGE1 pipx_metadata.python_version = "3.4.5" pipx_metadata.source_interpreter = Path(sys.executable) pipx_metadata.venv_args = ["--system-site-packages"] pipx_metadata.injected_packages = {"injected": TEST_PACKAGE2} pipx_metadata.write() pipx_metadata2 = PipxMetadata(venv_dir) for attribute in [ "venv_dir", "main_package", "python_version", "venv_args", "injected_packages", ]: assert getattr(pipx_metadata, attribute) == getattr(pipx_metadata2, attribute) @pytest.mark.parametrize( "test_package", [ replace(TEST_PACKAGE1, include_apps=False), replace(TEST_PACKAGE1, package=None), replace(TEST_PACKAGE1, package_or_url=None), ], ) def test_pipx_metadata_file_validation(tmp_path, test_package): venv_dir = tmp_path / "venv" venv_dir.mkdir() pipx_metadata = PipxMetadata(venv_dir) pipx_metadata.main_package = test_package pipx_metadata.python_version = "3.4.5" pipx_metadata.source_interpreter = Path(sys.executable) pipx_metadata.venv_args = ["--system-site-packages"] pipx_metadata.injected_packages = {} with pytest.raises(PipxError): pipx_metadata.write() def test_package_install(monkeypatch, tmp_path, pipx_temp_env): pipx_venvs_dir = paths.ctx.home / "venvs" run_pipx_cli(["install", PKG["pycowsay"]["spec"]]) assert (pipx_venvs_dir / "pycowsay" / "pipx_metadata.json").is_file() pipx_metadata = PipxMetadata(pipx_venvs_dir / "pycowsay") pycowsay_package_ref = create_package_info_ref("pycowsay", "pycowsay", pipx_venvs_dir) assert_package_metadata(pipx_metadata.main_package, pycowsay_package_ref) assert pipx_metadata.injected_packages == {} def test_package_inject(monkeypatch, tmp_path, pipx_temp_env): pipx_venvs_dir = paths.ctx.home / "venvs" run_pipx_cli(["install", PKG["pycowsay"]["spec"]]) run_pipx_cli(["inject", "pycowsay", PKG["black"]["spec"]]) assert (pipx_venvs_dir / "pycowsay" / "pipx_metadata.json").is_file() pipx_metadata = PipxMetadata(pipx_venvs_dir / "pycowsay") assert pipx_metadata.injected_packages.keys() == {"black"} black_package_ref = create_package_info_ref("pycowsay", "black", pipx_venvs_dir, include_apps=False) assert_package_metadata(pipx_metadata.injected_packages["black"], black_package_ref) pipx-1.15.0/tests/test_pth_encoding.py000066400000000000000000000011211521706623000177670ustar00rootroot00000000000000import site import sys from pathlib import Path def test_pth_file_readable_with_non_ascii_path(tmp_path: Path) -> None: non_ascii_name = "ร รฉรฎรถรผ" if sys.platform == "win32" else "็”จๆˆท" non_ascii_path = tmp_path / non_ascii_name / "pipx" / "shared" / "site-packages" non_ascii_path.mkdir(parents=True) pth_file = tmp_path / "pipx_shared.pth" pth_file.write_text(f"{non_ascii_path}\n") known_paths: set[str] = set() site.addpackage(str(tmp_path), pth_file.name, known_paths) assert str(non_ascii_path).casefold() in {p.casefold() for p in known_paths} pipx-1.15.0/tests/test_reinstall.py000066400000000000000000000100311521706623000173230ustar00rootroot00000000000000import sys from dataclasses import replace import pytest from helpers import PIPX_METADATA_LEGACY_VERSIONS, app_name, mock_legacy_venv, run_pipx_cli, skip_if_windows from pipx import paths, util from pipx.pipx_metadata_file import PipxMetadata def test_reinstall(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["reinstall", "--python", sys.executable, "pycowsay"]) @skip_if_windows def test_reinstall_global(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "--global", "pycowsay"]) assert not run_pipx_cli(["reinstall", "--global", "--python", sys.executable, "pycowsay"]) def test_reinstall_nonexistent(pipx_temp_env, capsys): assert run_pipx_cli(["reinstall", "--python", sys.executable, "nonexistent"]) assert "Nothing to reinstall for nonexistent" in capsys.readouterr().out @pytest.mark.parametrize("metadata_version", PIPX_METADATA_LEGACY_VERSIONS) def test_reinstall_legacy_venv(pipx_temp_env, capsys, metadata_version): assert not run_pipx_cli(["install", "pycowsay"]) mock_legacy_venv("pycowsay", metadata_version=metadata_version) assert not run_pipx_cli(["reinstall", "--python", sys.executable, "pycowsay"]) def test_reinstall_suffix(pipx_temp_env, capsys): suffix = "_x" assert not run_pipx_cli(["install", "pycowsay", f"--suffix={suffix}"]) assert not run_pipx_cli(["reinstall", "--python", sys.executable, f"pycowsay{suffix}"]) @pytest.mark.parametrize("metadata_version", ["0.1"]) def test_reinstall_suffix_legacy_venv(pipx_temp_env, capsys, metadata_version): suffix = "_x" assert not run_pipx_cli(["install", "pycowsay", f"--suffix={suffix}"]) mock_legacy_venv(f"pycowsay{suffix}", metadata_version=metadata_version) assert not run_pipx_cli(["reinstall", "--python", sys.executable, f"pycowsay{suffix}"]) def test_reinstall_specifier(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "pylint==3.0.4"]) # clear capsys before reinstall captured = capsys.readouterr() assert not run_pipx_cli(["reinstall", "--python", sys.executable, "pylint"]) captured = capsys.readouterr() assert "installed package pylint 3.0.4" in captured.out @skip_if_windows def test_reinstall_removes_stale_apps_after_success(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "pycowsay"]) capsys.readouterr() venv_dir = paths.ctx.venvs / "pycowsay" stale_app_name = app_name("removed-cowsay") stale_app_path = util.get_venv_paths(venv_dir)[0] / stale_app_name stale_app_path.write_text("#!/bin/sh\n") stale_app_path.chmod(0o755) stale_exposed_path = paths.ctx.bin_dir / stale_app_name stale_exposed_path.symlink_to(stale_app_path) metadata = PipxMetadata(venv_dir) metadata.main_package = replace( metadata.main_package, apps=[*metadata.main_package.apps, stale_app_name], app_paths=[*metadata.main_package.app_paths, stale_app_path], ) metadata.write() assert not run_pipx_cli(["reinstall", "--python", sys.executable, "pycowsay"]) assert not stale_exposed_path.exists() assert not stale_exposed_path.is_symlink() def test_reinstall_with_path(pipx_temp_env, capsys, tmp_path): path = tmp_path / "some" / "path" assert run_pipx_cli(["reinstall", str(path)]) captured = capsys.readouterr() assert "Expected the name of an installed package" in captured.err.replace("\n", " ") assert run_pipx_cli(["reinstall", str(path.resolve())]) captured = capsys.readouterr() assert "Expected the name of an installed package" in captured.err.replace("\n", " ") def test_reinstall_pinned_package(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "black"]) assert not run_pipx_cli(["pin", "black"]) assert run_pipx_cli(["reinstall", "black"]) captured = capsys.readouterr() assert "pinned" in captured.err assert not run_pipx_cli(["unpin", "black"]) assert not run_pipx_cli(["reinstall", "black"]) captured = capsys.readouterr() assert "installed package black" in captured.out pipx-1.15.0/tests/test_reinstall_all.py000066400000000000000000000056351521706623000201710ustar00rootroot00000000000000import importlib import sys import pytest from helpers import PIPX_METADATA_LEGACY_VERSIONS, mock_legacy_venv, run_pipx_cli from pipx import paths, shared_libs def test_reinstall_all(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["reinstall-all", "--python", sys.executable]) def test_reinstall_all_none(pipx_temp_env, capsys): assert not run_pipx_cli(["reinstall-all"]) captured = capsys.readouterr() assert "No packages reinstalled after running 'pipx reinstall-all'" in captured.out @pytest.mark.parametrize("metadata_version", PIPX_METADATA_LEGACY_VERSIONS) def test_reinstall_all_legacy_venv(pipx_temp_env, capsys, metadata_version): assert not run_pipx_cli(["install", "pycowsay"]) mock_legacy_venv("pycowsay", metadata_version=metadata_version) assert not run_pipx_cli(["reinstall-all", "--python", sys.executable]) def test_reinstall_all_suffix(pipx_temp_env, capsys): suffix = "_x" assert not run_pipx_cli(["install", "pycowsay", f"--suffix={suffix}"]) assert not run_pipx_cli(["reinstall-all", "--python", sys.executable]) @pytest.mark.parametrize("metadata_version", ["0.1"]) def test_reinstall_all_suffix_legacy_venv(pipx_temp_env, capsys, metadata_version): suffix = "_x" assert not run_pipx_cli(["install", "pycowsay", f"--suffix={suffix}"]) mock_legacy_venv(f"pycowsay{suffix}", metadata_version=metadata_version) assert not run_pipx_cli(["reinstall-all", "--python", sys.executable]) def test_reinstall_all_triggers_shared_libs_upgrade(pipx_temp_env, caplog, capsys): assert not run_pipx_cli(["install", "pycowsay"]) shared_libs.shared_libs.has_been_updated_this_run = False caplog.clear() assert not run_pipx_cli(["reinstall-all"]) assert "Upgrading shared libraries in" in caplog.text def test_reinstall_all_restores_package_after_keyboard_interrupt(pipx_temp_env, monkeypatch, capsys): reinstall_module = importlib.import_module("pipx.commands.reinstall") assert not run_pipx_cli(["install", "pycowsay"]) capsys.readouterr() venv_dir = paths.ctx.venvs / "pycowsay" metadata_before = (venv_dir / "pipx_metadata.json").read_text() def interrupting_install(replacement_venv_dir, *args, **kwargs): assert not venv_dir.exists() replacement_venv_dir.mkdir() (replacement_venv_dir / "partial-install").write_text("new") raise KeyboardInterrupt monkeypatch.setattr(reinstall_module, "install", interrupting_install) assert run_pipx_cli(["reinstall-all", "--python", sys.executable]) captured = capsys.readouterr() assert "Reinstall failed; restored pycowsay." in captured.err assert venv_dir.exists() assert (venv_dir / "pipx_metadata.json").read_text() == metadata_before assert not (venv_dir / "partial-install").exists() assert not any(path.name.endswith("-pipx-reinstall") for path in paths.ctx.venvs.iterdir()) pipx-1.15.0/tests/test_run.py000066400000000000000000000443631521706623000161510ustar00rootroot00000000000000import importlib import logging import os import subprocess import sys import textwrap from pathlib import Path from unittest import mock import pytest import pipx.main import pipx.util from helpers import run_pipx_cli from package_info import PKG from pipx import paths, shared_libs def test_help_text(pipx_temp_env, monkeypatch, capsys): mock_exit = mock.Mock(side_effect=ValueError("raised in test to exit early")) with mock.patch.object(sys, "exit", mock_exit), pytest.raises(ValueError, match="raised in test to exit early"): run_pipx_cli(["run", "--help"]) captured = capsys.readouterr() assert "Download the latest version of a package" in captured.out def execvpe_mock(cmd_path, cmd_args, env): return_code = subprocess.run( [str(x) for x in cmd_args], env=env, capture_output=False, encoding="utf-8", text=True, check=False, ).returncode sys.exit(return_code) def run_pipx_cli_exit(pipx_cmd_list, assert_exit=None): with pytest.raises(SystemExit) as sys_exit: run_pipx_cli(pipx_cmd_list) if assert_exit is not None: assert sys_exit.type is SystemExit assert sys_exit.value.code == assert_exit @pytest.mark.parametrize("package_name", ["pycowsay", "pycowsay==0.0.0.2", "pycowsay>=0.0.0.2"]) @mock.patch("os.execvpe", new=execvpe_mock) def test_simple_run(pipx_temp_env, monkeypatch, capsys, package_name): run_pipx_cli_exit(["run", package_name, "--help"]) captured = capsys.readouterr() assert "Download the latest version of a package" not in captured.out @mock.patch("os.execvpe", new=execvpe_mock) def test_cache(pipx_temp_env, monkeypatch, capsys, caplog): run_pipx_cli_exit(["run", "pycowsay", "cowsay", "args"]) caplog.set_level(logging.DEBUG) run_pipx_cli_exit(["run", "--verbose", "pycowsay", "cowsay", "args"], assert_exit=0) assert "Reusing cached venv" in caplog.text run_pipx_cli_exit(["run", "--no-cache", "pycowsay", "cowsay", "args"]) assert "Removing cached venv" in caplog.text @mock.patch("os.execvpe", new=execvpe_mock) def test_no_path_check(pipx_temp_env, monkeypatch, capsys, caplog): def fake_which(_app): return "/fake/bin/pycowsay" for module_name in ("pipx.commands.run", "pipx.commands.run_uv"): monkeypatch.setattr(importlib.import_module(module_name), "which", fake_which) run_pipx_cli_exit(["run", "pycowsay", "cowsay", "args"]) assert "is already on your PATH" in caplog.text caplog.clear() run_pipx_cli_exit(["run", "--no-path-check", "pycowsay", "cowsay", "args"]) assert "is already on your PATH" not in caplog.text @mock.patch("os.execvpe", new=execvpe_mock) def test_cachedir_tag(pipx_ultra_temp_env, monkeypatch, capsys, caplog): tag_path = paths.ctx.venv_cache / "CACHEDIR.TAG" assert not tag_path.exists() # Run pipx to create tag caplog.set_level(logging.DEBUG) run_pipx_cli_exit(["run", "pycowsay", "cowsay", "args"]) assert "Adding CACHEDIR.TAG to cache directory" in caplog.text assert tag_path.exists() caplog.clear() # Run pipx again to verify the tag file is not recreated run_pipx_cli_exit(["run", "pycowsay", "cowsay", "args"]) assert "Adding CACHEDIR.TAG to cache directory" not in caplog.text assert tag_path.exists() # Verify the tag file starts with the required signature. with tag_path.open("r") as tag_file: assert tag_file.read().startswith("Signature: 8a477f597d28d172789f06886806bc55") @mock.patch("os.execvpe", new=execvpe_mock) def test_run_script_from_internet(pipx_temp_env, capsys): run_pipx_cli_exit( [ "run", "https://gist.githubusercontent.com/cs01/" "fa721a17a326e551ede048c5088f9e0f/raw/" "6bdfbb6e9c1132b1c38fdd2f195d4a24c540c324/pipx-demo.py", ], assert_exit=0, ) @pytest.mark.parametrize( "input_run_args,expected_app_with_args", [ (["--", "pycowsay", "--", "hello"], ["pycowsay", "--", "hello"]), (["--", "pycowsay", "--", "--", "hello"], ["pycowsay", "--", "--", "hello"]), (["--", "pycowsay", "hello", "--"], ["pycowsay", "hello", "--"]), (["--", "pycowsay", "hello", "--", "--"], ["pycowsay", "hello", "--", "--"]), (["--", "pycowsay", "--"], ["pycowsay", "--"]), (["--", "pycowsay", "--", "--"], ["pycowsay", "--", "--"]), (["pycowsay", "--", "hello"], ["pycowsay", "--", "hello"]), (["pycowsay", "--", "--", "hello"], ["pycowsay", "--", "--", "hello"]), (["pycowsay", "hello", "--"], ["pycowsay", "hello", "--"]), (["pycowsay", "hello", "--", "--"], ["pycowsay", "hello", "--", "--"]), (["pycowsay", "--"], ["pycowsay", "--"]), (["pycowsay", "--", "--"], ["pycowsay", "--", "--"]), (["--", "--", "pycowsay", "--"], ["--", "pycowsay", "--"]), ], ) def test_appargs_doubledash(pipx_temp_env, capsys, monkeypatch, input_run_args, expected_app_with_args): parser, _ = pipx.main.get_command_parser() monkeypatch.setattr(sys, "argv", ["pipx", "run"] + input_run_args) parsed_pipx_args = parser.parse_args() pipx.main.check_args(parsed_pipx_args) assert parsed_pipx_args.app_with_args == expected_app_with_args def test_run_ensure_null_pythonpath(): env = os.environ.copy() env["PYTHONPATH"] = "test" assert ( "None" in subprocess.run( [ sys.executable, "-m", "pipx", "run", "ipython", "-c", "import os; print(os.environ.get('PYTHONPATH'))", ], env=env, capture_output=True, text=True, check=True, ).stdout ) # packages listed roughly in order of increasing test duration @pytest.mark.parametrize( "package, package_or_url, app_appargs, skip_win", [ ("pycowsay", "pycowsay", ["pycowsay", "hello"], False), ("shell-functools", PKG["shell-functools"]["spec"], ["filter", "--help"], True), ("black", PKG["black"]["spec"], ["black", "--help"], False), ("pylint", PKG["pylint"]["spec"], ["pylint", "--help"], False), ("kaggle", PKG["kaggle"]["spec"], ["kaggle", "--help"], False), ("ipython", PKG["ipython"]["spec"], ["ipython", "--version"], False), # ("cloudtoken", PKG["cloudtoken"]["spec"], ["cloudtoken", "--help"], True), ("awscli", PKG["awscli"]["spec"], ["aws", "--help"], True), # ("ansible", PKG["ansible"]["spec"], ["ansible", "--help"]), # takes too long ], ) @mock.patch("os.execvpe", new=execvpe_mock) def test_package_determination(caplog, pipx_temp_env, package, package_or_url, app_appargs, skip_win): if sys.platform.startswith("win") and skip_win: # Skip packages with 'scripts' in setup.py that don't work on Windows pytest.skip() caplog.set_level(logging.INFO) run_pipx_cli_exit(["run", "--verbose", "--spec", package_or_url, "--"] + app_appargs) assert "Cannot determine package name" not in caplog.text assert f"Determined package name: {package}" in caplog.text @mock.patch("os.execvpe", new=execvpe_mock) def test_run_without_requirements(caplog, pipx_temp_env, tmp_path): script = tmp_path / "test.py" out = tmp_path / "output.txt" test_str = "Hello, world!" script.write_text( textwrap.dedent( f""" from pathlib import Path Path({str(out)!r}).write_text({test_str!r}) """ ).strip() ) run_pipx_cli_exit(["run", script.as_uri()]) assert out.read_text() == test_str @mock.patch("os.execvpe", new=execvpe_mock) @pytest.mark.parametrize( "script_text, expected_output", [ pytest.param( """ # /// script # dependencies = [] # /// from pathlib import Path Path({out!r}).write_text("explicit-empty") """, "explicit-empty", id="explicit-empty-dependencies", ), pytest.param( """ # /// script # /// from pathlib import Path Path({out!r}).write_text("implicit-empty") """, "implicit-empty", id="implicit-empty-dependencies", ), pytest.param( """ # /// script # dependencies = ["requests==2.31.0"] # /// # Check requests can be imported import requests # Check dependencies of requests can be imported import certifi # Check the installed version from pathlib import Path Path({out!r}).write_text(requests.__version__) """, "2.31.0", id="non-empty-dependencies", ), ], ) def test_run_with_requirements(script_text, expected_output, caplog, pipx_temp_env, tmp_path): script = tmp_path / "test.py" out = tmp_path / "output.txt" script.write_text( textwrap.dedent(script_text.format(out=str(out))).strip(), encoding="utf-8", ) run_pipx_cli_exit(["run", script.as_uri()]) assert out.read_text() == expected_output @mock.patch("os.execvpe", new=execvpe_mock) def test_run_with_requirements_old(caplog, pipx_temp_env, tmp_path): script = tmp_path / "test.py" out = tmp_path / "output.txt" script.write_text( textwrap.dedent( f""" # /// pyproject # run.requirements = ["requests==2.31.0"] # /// # Check requests can be imported import requests # Check dependencies of requests can be imported import certifi # Check the installed version from pathlib import Path Path({str(out)!r}).write_text(requests.__version__) """ ).strip(), encoding="utf-8", ) with pytest.raises(ValueError): run_pipx_cli_exit(["run", script.as_uri()]) @mock.patch("os.execvpe", new=execvpe_mock) def test_run_correct_traceback(capfd, pipx_temp_env, tmp_path): script = tmp_path / "test.py" script.write_text( textwrap.dedent( """ raise RuntimeError("Should fail") """ ).strip() ) with pytest.raises(SystemExit): run_pipx_cli(["run", str(script)]) captured = capfd.readouterr() assert "test.py" in captured.err @mock.patch("os.execvpe", new=execvpe_mock) def test_run_with_args(caplog, pipx_temp_env, tmp_path): script = tmp_path / "test.py" out = tmp_path / "output.txt" script.write_text( textwrap.dedent( f""" import sys from pathlib import Path Path({str(out)!r}).write_text(str(int(sys.argv[1]) + 1)) """ ).strip() ) run_pipx_cli_exit(["run", script.as_uri(), "1"]) assert out.read_text() == "2" @mock.patch("os.execvpe", new=execvpe_mock) def test_run_with_requirements_and_args(caplog, pipx_temp_env, tmp_path): script = tmp_path / "test.py" out = tmp_path / "output.txt" script.write_text( textwrap.dedent( f""" # /// script # dependencies = ["packaging"] # /// import packaging import sys from pathlib import Path Path({str(out)!r}).write_text(str(int(sys.argv[1]) + 1)) """ ).strip() ) run_pipx_cli_exit(["run", script.as_uri(), "1"]) assert out.read_text() == "2" @mock.patch("os.execvpe", new=execvpe_mock) def test_run_with_failing_requirements(capfd, pipx_temp_env, tmp_path): script = tmp_path / "test.py" script.write_text( textwrap.dedent( """ # /// script # dependencies = ["will_fail @ git+https://0.0.0.0/will_fail.git"] # /// import will_fail """ ).strip() ) # Attempt first invocation of `pipx run`. # This should fail as the `will_fail` package will not be able to be installed. return_code = run_pipx_cli(["run", str(script)]) captured = capfd.readouterr() assert return_code != 0 assert "Error installing will_fail @ git+https://0.0.0.0/will_fail.git." in captured.err # Attempt second invocation of `pipx run`. # If above failure was detected and the temporary venv marked for deletion, # then this should fail in the same manner. # If the above failure was not detected, then a ModuleNotFoundError will be raised. return_code = run_pipx_cli(["run", str(script)]) captured = capfd.readouterr() assert return_code != 0 assert "ModuleNotFoundError: No module named 'will_fail'" not in captured.err assert "Error installing will_fail @ git+https://0.0.0.0/will_fail.git." in captured.err def test_pip_args_forwarded_to_shared_libs(pipx_ultra_temp_env, capsys, caplog): # strategy: # 1. start from an empty env to ensure the next command would trigger a shared lib update assert shared_libs.shared_libs.needs_upgrade # 2. install any package with --no-index # and check that the shared library update phase fails return_code = run_pipx_cli(["run", "--verbose", "--pip-args=--no-index", "pycowsay", "hello"]) assert "Upgrading shared libraries in" in caplog.text captured = capsys.readouterr() assert return_code != 0 assert "ERROR: Could not find a version that satisfies the requirement pip" in captured.err assert "Failed to upgrade shared libraries" in caplog.text @mock.patch("os.execvpe", new=execvpe_mock) def test_run_with_invalid_requirement(capsys, pipx_temp_env, tmp_path): script = tmp_path / "test.py" script.write_text( textwrap.dedent( """ # /// script # dependencies = ["this is an invalid requirement"] # /// print() """ ).strip() ) ret = run_pipx_cli(["run", script.as_uri()]) assert ret == 1 captured = capsys.readouterr() assert "Invalid requirement this is an invalid requirement" in captured.err @mock.patch("os.execvpe", new=execvpe_mock) def test_run_script_by_absolute_name(caplog, pipx_temp_env, tmp_path): script = tmp_path / "test.py" out = tmp_path / "output.txt" test_str = "Hello, world!" script.write_text( textwrap.dedent( f""" from pathlib import Path Path({str(out)!r}).write_text({test_str!r}) """ ).strip() ) run_pipx_cli_exit(["run", "--path", str(script)]) assert out.read_text() == test_str @mock.patch("os.execvpe", new=execvpe_mock) def test_run_script_by_relative_name(caplog, pipx_temp_env, monkeypatch, tmp_path): script = tmp_path / "test.py" out = tmp_path / "output.txt" test_str = "Hello, world!" script.write_text( textwrap.dedent( f""" from pathlib import Path Path({str(out)!r}).write_text({test_str!r}) """ ).strip() ) with monkeypatch.context() as m: m.chdir(tmp_path) run_pipx_cli_exit(["run", "test.py"]) assert out.read_text() == test_str @mock.patch("os.execvpe", new=execvpe_mock) @pytest.mark.skipif(sys.platform.startswith("win"), reason="uses file descriptor") def test_run_script_by_file_descriptor(caplog, pipx_temp_env, monkeypatch, tmp_path): read_fd, write_fd = os.pipe() out = tmp_path / "output.txt" test_str = "Hello, world!" os.write( write_fd, textwrap.dedent( f""" from pathlib import Path Path({str(out)!r}).write_text({test_str!r}) """ ) .strip() .encode("utf-8"), ) os.close(write_fd) with monkeypatch.context() as m: m.chdir(tmp_path) try: run_pipx_cli_exit(["run", f"/dev/fd/{read_fd}"]) finally: os.close(read_fd) assert out.read_text() == test_str @pytest.mark.skipif(not sys.platform.startswith("win"), reason="uses windows version format") @mock.patch("os.execvpe", new=execvpe_mock) def test_run_with_windows_python_version(caplog, pipx_temp_env, tmp_path): script = tmp_path / "test.py" out = tmp_path / "output.txt" script.write_text( textwrap.dedent( f""" import sys from pathlib import Path Path({str(out)!r}).write_text(sys.version) """ ).strip() ) run_pipx_cli_exit(["run", script.as_uri(), "--python", "3.13"]) assert "3.13" in out.read_text() @mock.patch("os.execvpe", new=execvpe_mock) def test_run_verify_script_name_provided(pipx_temp_env, capsys, tmpdir): tmpdir.mkdir("black") run_pipx_cli_exit(["run", "black"]) captured = capsys.readouterr() assert "black" in captured.err @mock.patch("os.execvpe", new=execvpe_mock) def test_run_shared_lib_as_app(pipx_temp_env, monkeypatch, capfd): run_pipx_cli_exit(["run", "pip", "--help"]) captured = capfd.readouterr() assert "pip [options]" in captured.out @mock.patch("os.execvpe", new=execvpe_mock) def test_run_local_path_entry_point(pipx_temp_env, caplog, root): empty_project_path = (Path("testdata") / "empty_project").as_posix() os.chdir(root) caplog.set_level(logging.INFO) run_pipx_cli_exit(["run", empty_project_path]) assert "Using discovered entry point for 'pipx run'" in caplog.text @mock.patch("os.execvpe", new=execvpe_mock) def test_run_with(capsys): run_pipx_cli_exit(["run", "--with", "black", "pycowsay", "--help"]) captured = capsys.readouterr() assert "injected package black into venv pycowsay" in captured.out @mock.patch("os.execvpe", new=execvpe_mock) def test_run_with_cache(capsys, caplog): # Maybe there's a better way to remove the previous venv cache? run_pipx_cli_exit(["run", "--no-cache", "pycowsay", "cowsay", "args"]) run_pipx_cli_exit(["run", "pycowsay", "cowsay", "args"], assert_exit=0) caplog.set_level(logging.DEBUG) caplog.clear() run_pipx_cli_exit(["run", "--verbose", "--with", "black", "pycowsay", "args"], assert_exit=0) captured = capsys.readouterr() assert "Reusing cached venv" in caplog.text assert "injected package black into venv pycowsay" in captured.out pipx-1.15.0/tests/test_run_uv.py000066400000000000000000000102471521706623000166550ustar00rootroot00000000000000from __future__ import annotations from pathlib import Path from typing import TYPE_CHECKING import pytest from pipx.commands.run_uv import run_script_via_uv_run, run_via_uv_tool_run from pipx.util import PipxError if TYPE_CHECKING: from pytest_mock import MockerFixture @pytest.fixture def fake_uv(mocker: MockerFixture) -> Path: binary = Path("/opt/uv/bin/uv") mocker.patch("pipx.commands.run_uv.resolve_uv_binary", return_value=binary) return binary def test_run_via_uv_tool_run_basic(mocker: MockerFixture, fake_uv: Path) -> None: exec_mock = mocker.patch("pipx.commands.run_uv.exec_app") mocker.patch("pipx.commands.run_uv.which", return_value=None) run_via_uv_tool_run( app="black", package_or_url="black", dependencies=[], app_args=["--check"], python="python3.12", pip_args=[], venv_args=[], use_cache=True, verbose=False, ) # ``mocker.patch`` keeps ``exec_app``'s ``NoReturn`` annotation, so pre-commit # mypy flags everything below as unreachable; ``unused-ignore`` covers the # local-checker case where the unreachable detection doesn't fire. (cmd,), _ = exec_mock.call_args # type: ignore[unreachable, unused-ignore] assert cmd == [str(fake_uv), "tool", "run", "--python", "python3.12", "black", "--check"] def test_run_via_uv_tool_run_threads_spec_and_with(mocker: MockerFixture, fake_uv: Path) -> None: exec_mock = mocker.patch("pipx.commands.run_uv.exec_app") mocker.patch("pipx.commands.run_uv.which", return_value=None) run_via_uv_tool_run( app="rope", package_or_url="git+https://example.com/rope.git", dependencies=["rich", "click"], app_args=["--version"], python="python3.12", pip_args=["--index-url", "https://example.com/simple", "--pre"], venv_args=[], use_cache=False, verbose=True, ) (cmd,), _ = exec_mock.call_args # type: ignore[unreachable, unused-ignore] assert cmd == [ str(fake_uv), "tool", "run", "--from", "git+https://example.com/rope.git", "--python", "python3.12", "--with", "rich", "--with", "click", "--no-cache", "--verbose", "--index-url", "https://example.com/simple", "--prerelease=allow", "rope", "--version", ] def test_run_via_uv_tool_run_omits_from_when_app_matches_spec(mocker: MockerFixture, fake_uv: Path) -> None: exec_mock = mocker.patch("pipx.commands.run_uv.exec_app") mocker.patch("pipx.commands.run_uv.which", return_value=None) run_via_uv_tool_run( app="black", package_or_url="black", dependencies=[], app_args=[], python="", pip_args=[], venv_args=[], use_cache=True, verbose=False, ) (cmd,), _ = exec_mock.call_args # type: ignore[unreachable, unused-ignore] assert "--from" not in cmd def test_run_script_via_uv_run_uses_uv_run_script(mocker: MockerFixture, fake_uv: Path, tmp_path: Path) -> None: exec_mock = mocker.patch("pipx.commands.run_uv.exec_app") script = tmp_path / "demo.py" script.write_text("print('hi')\n") run_script_via_uv_run( script_path=script, app_args=["--quiet"], python="python3.12", pip_args=[], venv_args=[], use_cache=True, verbose=False, ) (cmd,), _ = exec_mock.call_args # type: ignore[unreachable, unused-ignore] assert cmd == [str(fake_uv), "run", "--script", "--python", "python3.12", str(script), "--quiet"] def test_run_via_uv_tool_run_rejects_venv_args(mocker: MockerFixture, fake_uv: Path) -> None: mocker.patch("pipx.commands.run_uv.exec_app") mocker.patch("pipx.commands.run_uv.which", return_value=None) with pytest.raises(PipxError, match=r"--venv-args .* is not supported"): run_via_uv_tool_run( app="black", package_or_url="black", dependencies=[], app_args=[], python="", pip_args=[], venv_args=["--system-site-packages"], use_cache=True, verbose=False, ) pipx-1.15.0/tests/test_runpip.py000066400000000000000000000012011521706623000166420ustar00rootroot00000000000000from helpers import run_pipx_cli, skip_if_windows def test_runpip(pipx_temp_env, monkeypatch, capsys): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["runpip", "pycowsay", "list"]) def test_runpip_splits_single_argument(pipx_temp_env, monkeypatch, capsys): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["runpip", "pycowsay", "list --format=freeze"]) @skip_if_windows def test_runpip_global(pipx_temp_env, monkeypatch, capsys): assert not run_pipx_cli(["install", "--global", "pycowsay"]) assert not run_pipx_cli(["runpip", "--global", "pycowsay", "list"]) pipx-1.15.0/tests/test_shared_libs.py000066400000000000000000000107031521706623000176130ustar00rootroot00000000000000import json import os import subprocess import time from pathlib import Path from unittest.mock import patch import pytest from pipx import shared_libs from pipx.constants import PIPX_SHARED_PTH, WINDOWS from pipx.venv import Venv @pytest.mark.parametrize( "mtime_minus_now,needs_upgrade", [ (-shared_libs.SHARED_LIBS_MAX_AGE_SEC - 5 * 60, True), (-shared_libs.SHARED_LIBS_MAX_AGE_SEC + 5 * 60, False), ], ) def test_auto_update_shared_libs(capsys, pipx_ultra_temp_env, mtime_minus_now, needs_upgrade): now = time.time() shared_libs.shared_libs.create(verbose=True, pip_args=[]) shared_libs.shared_libs.has_been_updated_this_run = False access_time = now # this can be anything os.utime(shared_libs.shared_libs.pip_path, (access_time, mtime_minus_now + now)) assert shared_libs.shared_libs.needs_upgrade is needs_upgrade @pytest.mark.skipif(not WINDOWS, reason="Windows-specific test") def test_venv_python_is_valid_missing_interpreter(tmp_path: Path) -> None: """Test that _venv_python_is_valid returns False when the underlying Python is missing.""" # Create a fake venv structure venv_path = tmp_path / "test_venv" scripts_path = venv_path / "Scripts" scripts_path.mkdir(parents=True) python_exe = scripts_path / "python.exe" python_exe.touch() # Create a pyvenv.cfg pointing to a non-existent Python pyvenv_cfg = venv_path / "pyvenv.cfg" pyvenv_cfg.write_text("home = C:\\NonExistent\\Python\\Path\nversion = 3.14.0\n") assert shared_libs._venv_python_is_valid(python_exe) is False @pytest.mark.skipif(not WINDOWS, reason="Windows-specific test") def test_venv_python_is_valid_existing_interpreter(tmp_path: Path) -> None: """Test that _venv_python_is_valid returns True when the underlying Python exists.""" # Create a fake venv structure venv_path = tmp_path / "test_venv" scripts_path = venv_path / "Scripts" scripts_path.mkdir(parents=True) python_exe = scripts_path / "python.exe" python_exe.touch() # Create the "original" Python installation original_python_dir = tmp_path / "original_python" original_python_dir.mkdir() original_python_exe = original_python_dir / "python.exe" original_python_exe.touch() # Create a pyvenv.cfg pointing to the existing Python pyvenv_cfg = venv_path / "pyvenv.cfg" pyvenv_cfg.write_text(f"home = {original_python_dir}\nversion = 3.12.0\n") assert shared_libs._venv_python_is_valid(python_exe) is True def test_shared_libs_excludes_setuptools(pipx_ultra_temp_env: None) -> None: shared_libs.shared_libs.create(verbose=True, pip_args=[]) result = subprocess.run( [str(shared_libs.shared_libs.python_path), "-m", "pip", "list", "--format=json"], capture_output=True, text=True, check=True, ) installed = {pkg["name"].lower() for pkg in json.loads(result.stdout)} assert "pip" in installed assert "setuptools" not in installed def test_venv_python_is_valid_non_windows() -> None: """Test that _venv_python_is_valid always returns True on non-Windows platforms.""" with patch.object(shared_libs, "WINDOWS", False): # Should return True regardless of the path assert shared_libs._venv_python_is_valid(Path("/fake/path/python")) is True @pytest.mark.parametrize( ("env_value", "force_upgrade", "expected_calls"), [ ("1", False, 0), ("1", True, 1), ("0", False, 1), ], ) def test_disable_shared_libs_auto_upgrade( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, env_value: str, force_upgrade: bool, expected_calls: int, ) -> None: venv_path = tmp_path / "venv" site_packages = venv_path / "lib" / "python" / "site-packages" site_packages.mkdir(parents=True) (site_packages / PIPX_SHARED_PTH).write_text("") venv = Venv(venv_path) upgrade_calls = [] monkeypatch.setenv(shared_libs.DISABLE_SHARED_LIBS_AUTO_UPGRADE, env_value) monkeypatch.setattr( type(shared_libs.shared_libs), "is_valid", property(lambda self: True), ) monkeypatch.setattr( type(shared_libs.shared_libs), "needs_upgrade", property(lambda self: True), ) monkeypatch.setattr( shared_libs.shared_libs, "upgrade", lambda **kwargs: upgrade_calls.append(kwargs), ) venv.check_upgrade_shared_libs(verbose=True, pip_args=[], force_upgrade=force_upgrade) assert len(upgrade_calls) == expected_calls pipx-1.15.0/tests/test_standalone_interpreter.py000066400000000000000000000120321521706623000221040ustar00rootroot00000000000000import datetime import json import shutil import sys from helpers import ( run_pipx_cli, ) from package_info import PKG from pipx import standalone_python MAJOR_PYTHON_VERSION = sys.version_info.major MINOR_PYTHON_VERSION = sys.version_info.minor TARGET_PYTHON_VERSION = f"{MAJOR_PYTHON_VERSION}.{MINOR_PYTHON_VERSION}" original_which = shutil.which def mock_which(name): if name == TARGET_PYTHON_VERSION: return None return original_which(name) def test_legacy_standalone_python_index_is_refreshed(pipx_temp_env, monkeypatch): legacy_link = ( "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" ) digest = "sha256:" + "0" * 64 current_releases = [(legacy_link, digest)] cache_dir = standalone_python.paths.ctx.standalone_python_cachedir index_file = cache_dir / "index.json" cache_dir.mkdir(parents=True) index_file.write_text( json.dumps( { "fetched": datetime.datetime.now().timestamp(), "releases": [legacy_link], } ) ) monkeypatch.setattr(standalone_python, "get_latest_python_releases", lambda: current_releases) assert standalone_python.get_or_update_index()["releases"] == current_releases assert json.loads(index_file.read_text())["releases"] == [[legacy_link, digest]] def test_list_no_standalone_interpreters(pipx_temp_env, monkeypatch, capsys): assert not run_pipx_cli(["interpreter", "list"]) captured = capsys.readouterr() assert "Standalone interpreters" in captured.out assert len(captured.out.splitlines()) == 1 def test_list_used_standalone_interpreters(pipx_temp_env, monkeypatch, mocked_github_api, capsys): monkeypatch.setattr(shutil, "which", mock_which) assert not run_pipx_cli( [ "install", "--fetch-python=missing", "--python", TARGET_PYTHON_VERSION, PKG["pycowsay"]["spec"], ] ) capsys.readouterr() assert not run_pipx_cli(["interpreter", "list"]) captured = capsys.readouterr() assert TARGET_PYTHON_VERSION in captured.out assert "pycowsay" in captured.out def test_list_unused_standalone_interpreters(pipx_temp_env, monkeypatch, mocked_github_api, capsys): monkeypatch.setattr(shutil, "which", mock_which) assert not run_pipx_cli( [ "install", "--fetch-python=missing", "--python", TARGET_PYTHON_VERSION, PKG["pycowsay"]["spec"], ] ) assert not run_pipx_cli(["uninstall", "pycowsay"]) capsys.readouterr() assert not run_pipx_cli(["interpreter", "list"]) captured = capsys.readouterr() assert TARGET_PYTHON_VERSION in captured.out assert "pycowsay" not in captured.out assert "Unused" in captured.out def test_prune_unused_standalone_interpreters(pipx_temp_env, monkeypatch, mocked_github_api, capsys): monkeypatch.setattr(shutil, "which", mock_which) assert not run_pipx_cli( [ "install", "--fetch-python=missing", "--python", TARGET_PYTHON_VERSION, PKG["pycowsay"]["spec"], ] ) capsys.readouterr() assert not run_pipx_cli(["interpreter", "prune"]) captured = capsys.readouterr() assert "Nothing to remove" in captured.out assert not run_pipx_cli(["uninstall", "pycowsay"]) capsys.readouterr() assert not run_pipx_cli(["interpreter", "prune"]) captured = capsys.readouterr() assert "Successfully removed:" in captured.out assert f"- Python {TARGET_PYTHON_VERSION}" in captured.out assert not run_pipx_cli(["interpreter", "list"]) captured = capsys.readouterr() assert "Standalone interpreters" in captured.out assert len(captured.out.splitlines()) == 1 assert not run_pipx_cli(["interpreter", "prune"]) captured = capsys.readouterr() assert "Nothing to remove" in captured.out def test_upgrade_standalone_interpreter(pipx_temp_env, root, monkeypatch, capsys): monkeypatch.setattr(shutil, "which", mock_which) with open(root / "testdata" / "standalone_python_index_20250818.json") as f: new_index = json.load(f) monkeypatch.setattr(standalone_python, "get_or_update_index", lambda _: new_index) assert not run_pipx_cli( [ "install", "--fetch-python=missing", "--python", TARGET_PYTHON_VERSION, PKG["pycowsay"]["spec"], ] ) with open(root / "testdata" / "standalone_python_index_20250828.json") as f: new_index = json.load(f) monkeypatch.setattr(standalone_python, "get_or_update_index", lambda _: new_index) assert not run_pipx_cli(["interpreter", "upgrade"]) def test_upgrade_standalone_interpreter_nothing_to_upgrade(pipx_temp_env, capsys, mocked_github_api): assert not run_pipx_cli(["interpreter", "upgrade"]) captured = capsys.readouterr() assert "Nothing to upgrade" in captured.out pipx-1.15.0/tests/test_subprocess_env.py000066400000000000000000000006261521706623000203770ustar00rootroot00000000000000from pipx.util import _fix_subprocess_env def test_pip_target_removed_from_env() -> None: env = {"PATH": "/usr/bin", "PIP_TARGET": "/some/custom/target"} result = _fix_subprocess_env(env) assert "PIP_TARGET" not in result def test_pip_user_disabled_in_env() -> None: env = {"PATH": "/usr/bin", "PIP_USER": "1"} result = _fix_subprocess_env(env) assert result["PIP_USER"] == "0" pipx-1.15.0/tests/test_uninject.py000066400000000000000000000055031521706623000171550ustar00rootroot00000000000000from helpers import run_pipx_cli, skip_if_windows from package_info import PKG def test_uninject_simple(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["inject", "pycowsay", PKG["black"]["spec"]]) assert not run_pipx_cli(["uninject", "pycowsay", "black"]) captured = capsys.readouterr() assert "Uninjected package black" in captured.out assert not run_pipx_cli(["list", "--include-injected"]) captured = capsys.readouterr() assert "black" not in captured.out @skip_if_windows def test_uninject_simple_global(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "--global", "pycowsay"]) assert not run_pipx_cli(["inject", "--global", "pycowsay", PKG["black"]["spec"]]) assert not run_pipx_cli(["uninject", "--global", "pycowsay", "black"]) captured = capsys.readouterr() assert "Uninjected package black" in captured.out assert not run_pipx_cli(["list", "--global", "--include-injected"]) captured = capsys.readouterr() assert "black" not in captured.out def test_uninject_with_include_apps(pipx_temp_env, capsys, caplog): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["inject", "pycowsay", PKG["black"]["spec"], "--include-deps", "--include-apps"]) assert not run_pipx_cli(["uninject", "pycowsay", "black", "--verbose"]) assert "removed file" in caplog.text def test_uninject_removes_dependency_app_symlinks(pipx_temp_env, capsys, caplog): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["inject", "pycowsay", PKG["pylint"]["spec"], "--include-deps", "--include-apps"]) captured = capsys.readouterr() assert "isort" in captured.out assert not run_pipx_cli(["uninject", "pycowsay", "pylint", "--verbose"]) assert "removed file" in caplog.text assert "isort" in caplog.text def test_uninject_leave_deps(pipx_temp_env, capsys, caplog): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["inject", "pycowsay", PKG["black"]["spec"]]) assert not run_pipx_cli(["uninject", "pycowsay", "black", "--leave-deps", "--verbose"]) captured = capsys.readouterr() assert "Uninjected package black from venv pycowsay" in captured.out assert "Dependencies of uninstalled package:" not in caplog.text def test_uninject_preserves_shared_deps(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["inject", "pycowsay", PKG["black"]["spec"]]) assert not run_pipx_cli(["inject", "pycowsay", PKG["pylint"]["spec"]]) assert not run_pipx_cli(["uninject", "pycowsay", "black"]) capsys.readouterr() assert not run_pipx_cli(["list", "--include-injected"]) captured = capsys.readouterr() assert "pylint" in captured.out assert "black" not in captured.out pipx-1.15.0/tests/test_uninstall.py000066400000000000000000000162151521706623000173510ustar00rootroot00000000000000import sys import pytest from helpers import ( PIPX_METADATA_LEGACY_VERSIONS, app_name, mock_legacy_venv, remove_venv_interpreter, run_pipx_cli, skip_if_windows, ) from package_info import PKG from pipx import paths def file_or_symlink(filepath): # Returns True for file or broken symlink or non-broken symlink # Returns False for no file and no symlink # filepath.exists() returns True for regular file or non-broken symlink # filepath.exists() returns False for no regular file or broken symlink # filepath.is_symlink() returns True for broken or non-broken symlink return filepath.exists() or filepath.is_symlink() def test_uninstall(pipx_temp_env): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["uninstall", "pycowsay"]) @skip_if_windows def test_uninstall_global(pipx_temp_env): assert not run_pipx_cli(["install", "--global", "pycowsay"]) assert not run_pipx_cli(["uninstall", "--global", "pycowsay"]) # TODO: We can add this test back once a suitable substitute for cloudtoken is found # def test_uninstall_circular_deps(pipx_temp_env): # assert not run_pipx_cli(["install", PKG["cloudtoken"]["spec"]]) # assert not run_pipx_cli(["uninstall", "cloudtoken"]) @pytest.mark.parametrize("metadata_version", PIPX_METADATA_LEGACY_VERSIONS) def test_uninstall_legacy_venv(pipx_temp_env, metadata_version): executable_path = paths.ctx.bin_dir / app_name("pycowsay") assert not run_pipx_cli(["install", "pycowsay"]) assert executable_path.exists() mock_legacy_venv("pycowsay", metadata_version=metadata_version) assert not run_pipx_cli(["uninstall", "pycowsay"]) assert not file_or_symlink(executable_path) def test_uninstall_suffix(pipx_temp_env): name = "pbr" suffix = "_a" executable_path = paths.ctx.bin_dir / app_name(f"{name}{suffix}") assert not run_pipx_cli(["install", PKG[name]["spec"], f"--suffix={suffix}"]) assert executable_path.exists() assert not run_pipx_cli(["uninstall", f"{name}{suffix}"]) assert not file_or_symlink(executable_path) def test_uninstall_man_page(pipx_temp_env): man_page_path = paths.ctx.man_dir / "man6" / "pycowsay.6" assert not run_pipx_cli(["install", "pycowsay"]) assert man_page_path.exists() assert not run_pipx_cli(["uninstall", "pycowsay"]) assert not file_or_symlink(man_page_path) def test_uninstall_injected(pipx_temp_env): pycowsay_app_paths = [paths.ctx.bin_dir / app for app in PKG["pycowsay"]["apps"]] pycowsay_man_page_paths = [paths.ctx.man_dir / man_page for man_page in PKG["pycowsay"]["man_pages"]] pylint_app_paths = [paths.ctx.bin_dir / app for app in PKG["pylint"]["apps"]] app_paths = pycowsay_app_paths + pylint_app_paths man_page_paths = pycowsay_man_page_paths assert not run_pipx_cli(["install", PKG["pycowsay"]["spec"]]) assert not run_pipx_cli(["inject", "--include-apps", "pycowsay", PKG["pylint"]["spec"]]) for app_path in app_paths: assert app_path.exists() for man_page_path in man_page_paths: assert man_page_path.exists() assert not run_pipx_cli(["uninstall", "pycowsay"]) for app_path in app_paths: assert not file_or_symlink(app_path) for man_page_path in man_page_paths: assert not file_or_symlink(man_page_path) @pytest.mark.parametrize("metadata_version", ["0.1"]) def test_uninstall_suffix_legacy_venv(pipx_temp_env, metadata_version): name = "pbr" # legacy uninstall on Windows only works with "canonical name characters" # in suffix suffix = "-a" executable_path = paths.ctx.bin_dir / app_name(f"{name}{suffix}") assert not run_pipx_cli(["install", PKG[name]["spec"], f"--suffix={suffix}"]) mock_legacy_venv(f"{name}{suffix}", metadata_version=metadata_version) assert executable_path.exists() assert not run_pipx_cli(["uninstall", f"{name}{suffix}"]) assert not file_or_symlink(executable_path) @pytest.mark.parametrize("metadata_version", PIPX_METADATA_LEGACY_VERSIONS) def test_uninstall_with_missing_interpreter(pipx_temp_env, metadata_version): executable_path = paths.ctx.bin_dir / app_name("pycowsay") assert not run_pipx_cli(["install", "pycowsay"]) assert executable_path.exists() mock_legacy_venv("pycowsay", metadata_version=metadata_version) remove_venv_interpreter("pycowsay") assert not run_pipx_cli(["uninstall", "pycowsay"]) # On Windows we cannot remove app binaries if no metadata and no python if not (sys.platform.startswith("win") and metadata_version is None): assert not file_or_symlink(executable_path) @pytest.mark.parametrize("metadata_version", PIPX_METADATA_LEGACY_VERSIONS) def test_uninstall_proper_dep_behavior(pipx_temp_env, metadata_version): # isort is a dependency of pylint. Make sure that uninstalling pylint # does not also uninstall isort app in LOCAL_BIN_DIR isort_app_paths = [paths.ctx.bin_dir / app for app in PKG["isort"]["apps"]] pylint_app_paths = [paths.ctx.bin_dir / app for app in PKG["pylint"]["apps"]] assert not run_pipx_cli(["install", PKG["pylint"]["spec"]]) assert not run_pipx_cli(["install", PKG["isort"]["spec"]]) mock_legacy_venv("pylint", metadata_version=metadata_version) mock_legacy_venv("isort", metadata_version=metadata_version) for pylint_app_path in pylint_app_paths: assert pylint_app_path.exists() for isort_app_path in isort_app_paths: assert isort_app_path.exists() assert not run_pipx_cli(["uninstall", "pylint"]) for pylint_app_path in pylint_app_paths: assert not file_or_symlink(pylint_app_path) # THIS is what we're making sure is true: for isort_app_path in isort_app_paths: assert isort_app_path.exists() @pytest.mark.parametrize("metadata_version", PIPX_METADATA_LEGACY_VERSIONS) def test_uninstall_proper_dep_behavior_missing_interpreter(pipx_temp_env, metadata_version): # isort is a dependency of pylint. Make sure that uninstalling pylint # does not also uninstall isort app in LOCAL_BIN_DIR isort_app_paths = [paths.ctx.bin_dir / app for app in PKG["isort"]["apps"]] pylint_app_paths = [paths.ctx.bin_dir / app for app in PKG["pylint"]["apps"]] assert not run_pipx_cli(["install", PKG["pylint"]["spec"]]) assert not run_pipx_cli(["install", PKG["isort"]["spec"]]) mock_legacy_venv("pylint", metadata_version=metadata_version) mock_legacy_venv("isort", metadata_version=metadata_version) remove_venv_interpreter("pylint") remove_venv_interpreter("isort") for pylint_app_path in pylint_app_paths: assert pylint_app_path.exists() for isort_app_path in isort_app_paths: assert isort_app_path.exists() assert not run_pipx_cli(["uninstall", "pylint"]) # Do not check the following on Windows without metadata, we do not # remove bin dir links by design for missing interpreter in that case if not (sys.platform.startswith("win") and metadata_version is None): for pylint_app_path in pylint_app_paths: assert not file_or_symlink(pylint_app_path) # THIS is what we're making sure is true: for isort_app_path in isort_app_paths: assert isort_app_path.exists() pipx-1.15.0/tests/test_uninstall_all.py000066400000000000000000000010711521706623000201730ustar00rootroot00000000000000import pytest from helpers import PIPX_METADATA_LEGACY_VERSIONS, mock_legacy_venv, run_pipx_cli def test_uninstall_all(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["uninstall-all"]) @pytest.mark.parametrize("metadata_version", PIPX_METADATA_LEGACY_VERSIONS) def test_uninstall_all_legacy_venv(pipx_temp_env, capsys, metadata_version): assert not run_pipx_cli(["install", "pycowsay"]) mock_legacy_venv("pycowsay", metadata_version=metadata_version) assert not run_pipx_cli(["uninstall-all"]) pipx-1.15.0/tests/test_unpin.py000066400000000000000000000033771521706623000164760ustar00rootroot00000000000000from helpers import run_pipx_cli from package_info import PKG def test_unpin(capsys, pipx_temp_env, caplog): assert not run_pipx_cli(["install", PKG["nox"]["spec"]]) assert not run_pipx_cli(["pin", "nox"]) assert not run_pipx_cli(["unpin", "nox"]) assert not run_pipx_cli(["upgrade", "nox"]) captured = capsys.readouterr() assert "nox is already at latest version" in captured.out def test_unpin_with_suffix(capsys, pipx_temp_env): assert not run_pipx_cli(["install", PKG["black"]["spec"], "--suffix", "@1"]) assert not run_pipx_cli(["pin", "black@1"]) assert not run_pipx_cli(["unpin", "black@1"]) captured = capsys.readouterr() assert "Unpinned 1 packages in venv black@1" in captured.out assert not run_pipx_cli(["upgrade", "black@1"]) captured = capsys.readouterr() assert "upgraded package black@1 from 22.8.0 to 22.10.0" in captured.out def test_unpin_warning(capsys, pipx_temp_env, caplog): assert not run_pipx_cli(["install", PKG["nox"]["spec"]]) assert not run_pipx_cli(["pin", "nox"]) assert not run_pipx_cli(["unpin", "nox"]) assert not run_pipx_cli(["unpin", "nox"]) assert "No packages to unpin in venv nox" in caplog.text def test_unpin_not_installed_package(capsys, pipx_temp_env): assert run_pipx_cli(["unpin", "abc"]) captured = capsys.readouterr() assert "Package abc is not installed" in captured.err def test_unpin_injected_packages(capsys, pipx_temp_env): assert not run_pipx_cli(["install", "black"]) assert not run_pipx_cli(["inject", "black", "nox", "pylint"]) assert not run_pipx_cli(["pin", "black"]) assert not run_pipx_cli(["unpin", "black"]) captured = capsys.readouterr() assert "Unpinned 3 packages in venv black" in captured.out pipx-1.15.0/tests/test_upgrade.py000066400000000000000000000172631521706623000167730ustar00rootroot00000000000000import pytest from helpers import ( PIPX_METADATA_LEGACY_VERSIONS, mock_legacy_venv, remove_venv_interpreter, run_pipx_cli, skip_if_windows, ) from package_info import PKG from pipx import paths from pipx.pipx_metadata_file import PipxMetadata def test_upgrade(pipx_temp_env, capsys): assert run_pipx_cli(["upgrade", "pycowsay"]) captured = capsys.readouterr() assert "Package is not installed" in captured.err assert not run_pipx_cli(["install", "pycowsay"]) captured = capsys.readouterr() assert "installed package pycowsay" in captured.out assert not run_pipx_cli(["upgrade", "pycowsay"]) captured = capsys.readouterr() assert "pycowsay is already at latest version" in captured.out @skip_if_windows def test_upgrade_global(pipx_temp_env, capsys): assert run_pipx_cli(["upgrade", "--global", "pycowsay"]) captured = capsys.readouterr() assert "Package is not installed" in captured.err assert not run_pipx_cli(["install", "--global", "pycowsay"]) captured = capsys.readouterr() assert "installed package pycowsay" in captured.out assert not run_pipx_cli(["upgrade", "--global", "pycowsay"]) captured = capsys.readouterr() assert "pycowsay is already at latest version" in captured.out @pytest.mark.parametrize("metadata_version", PIPX_METADATA_LEGACY_VERSIONS) def test_upgrade_legacy_venv(pipx_temp_env, capsys, metadata_version): assert not run_pipx_cli(["install", "pycowsay"]) mock_legacy_venv("pycowsay", metadata_version=metadata_version) captured = capsys.readouterr() if metadata_version is None: assert run_pipx_cli(["upgrade", "pycowsay"]) captured = capsys.readouterr() assert "Not upgrading pycowsay. It has missing internal pipx metadata." in captured.err else: assert not run_pipx_cli(["upgrade", "pycowsay"]) captured = capsys.readouterr() def test_upgrade_suffix(pipx_temp_env, capsys): name = "pycowsay" suffix = "_a" assert not run_pipx_cli(["install", name, f"--suffix={suffix}"]) assert run_pipx_cli(["upgrade", f"{name}"]) assert not run_pipx_cli(["upgrade", f"{name}{suffix}"]) @pytest.mark.parametrize("metadata_version", ["0.1"]) def test_upgrade_suffix_legacy_venv(pipx_temp_env, capsys, metadata_version): name = "pycowsay" suffix = "_a" assert not run_pipx_cli(["install", name, f"--suffix={suffix}"]) mock_legacy_venv(f"{name}{suffix}", metadata_version=metadata_version) assert run_pipx_cli(["upgrade", f"{name}"]) assert not run_pipx_cli(["upgrade", f"{name}{suffix}"]) def test_upgrade_specifier(pipx_temp_env, capsys): name = "pylint" pkg_spec = PKG[name]["spec"] initial_version = pkg_spec.split("==")[-1] assert not run_pipx_cli(["install", f"{pkg_spec}"]) assert not run_pipx_cli(["upgrade", f"{name}"]) captured = capsys.readouterr() assert f"upgraded package {name} from {initial_version} to" in captured.out def test_upgrade_missing_interpreter(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "pycowsay"]) remove_venv_interpreter("pycowsay") result = run_pipx_cli(["upgrade", "pycowsay"]) assert result != 0, "upgrade should fail when Python interpreter is missing" captured = capsys.readouterr() assert "invalid python interpreter" in captured.err assert "pipx reinstall-all" in captured.err def test_upgrade_editable(pipx_temp_env, capsys, root): empty_project_path_as_string = (root / "testdata" / "empty_project").as_posix() assert not run_pipx_cli(["install", "--editable", empty_project_path_as_string, "--force"]) assert not run_pipx_cli(["upgrade", "--editable", "empty_project"]) captured = capsys.readouterr() assert "empty-project is already at latest version" in captured.out def test_upgrade_include_injected(pipx_temp_env, capsys): assert not run_pipx_cli(["install", PKG["pylint"]["spec"]]) assert not run_pipx_cli(["inject", "pylint", PKG["black"]["spec"]]) captured = capsys.readouterr() assert not run_pipx_cli(["upgrade", "--include-injected", "pylint"]) captured = capsys.readouterr() assert "upgraded package pylint" in captured.out assert "upgraded package black" in captured.out def test_upgrade_no_include_injected(pipx_temp_env, capsys): assert not run_pipx_cli(["install", PKG["pylint"]["spec"]]) assert not run_pipx_cli(["inject", "pylint", PKG["black"]["spec"]]) captured = capsys.readouterr() assert not run_pipx_cli(["upgrade", "pylint"]) captured = capsys.readouterr() assert "upgraded package pylint" in captured.out assert "upgraded package black" not in captured.out def test_upgrade_install_missing(pipx_temp_env, capsys): assert not run_pipx_cli(["upgrade", "pycowsay", "--install"]) captured = capsys.readouterr() assert "installed package pycowsay" in captured.out def test_upgrade_multiple(pipx_temp_env, capsys): name = "pylint" pkg_spec = PKG[name]["spec"] initial_version = pkg_spec.split("==")[-1] assert not run_pipx_cli(["install", pkg_spec]) assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["upgrade", name, "pycowsay"]) captured = capsys.readouterr() assert f"upgraded package {name} from {initial_version} to" in captured.out assert "pycowsay is already at latest version" in captured.out def test_upgrade_absolute_path(pipx_temp_env, capsys, root): assert run_pipx_cli(["upgrade", "--verbose", str((root / "testdata" / "empty_project").resolve())]) captured = capsys.readouterr() assert "Package cannot be a URL" not in captured.err def test_upgrade_with_extras(pipx_temp_env, capsys): """Test that upgrading a package with extras in the name works correctly. Regression test for https://github.com/pypa/pipx/issues/925 """ assert not run_pipx_cli(["install", "pycowsay"]) captured = capsys.readouterr() assert "installed package pycowsay" in captured.out assert not run_pipx_cli(["upgrade", "pycowsay[test_extra]"]) captured = capsys.readouterr() assert "pycowsay is already at latest version" in captured.out assert "Package is not installed" not in captured.err @pytest.mark.parametrize( ("upgrade_args", "expected_args", "unexpected_args"), [ pytest.param([], ["--no-cache-dir"], [], id="preserves_stored"), pytest.param(["--pip-args=--no-deps"], ["--no-deps"], ["--no-cache-dir"], id="cli_overrides_stored"), ], ) def test_upgrade_pip_args( pipx_temp_env: None, capsys: pytest.CaptureFixture[str], upgrade_args: list[str], expected_args: list[str], unexpected_args: list[str], ) -> None: assert not run_pipx_cli(["install", "pycowsay", "--pip-args=--no-cache-dir"]) assert not run_pipx_cli(["upgrade", "pycowsay", *upgrade_args]) pipx_venvs_dir = paths.ctx.home / "venvs" metadata = PipxMetadata(pipx_venvs_dir / "pycowsay") for arg in expected_args: assert arg in metadata.main_package.pip_args for arg in unexpected_args: assert arg not in metadata.main_package.pip_args def test_upgrade_injected_preserves_stored_pip_args(pipx_temp_env: None, capsys: pytest.CaptureFixture[str]) -> None: assert not run_pipx_cli(["install", PKG["pylint"]["spec"]]) assert not run_pipx_cli(["inject", "pylint", PKG["black"]["spec"], "--pip-args=--no-cache-dir"]) pipx_venvs_dir = paths.ctx.home / "venvs" metadata = PipxMetadata(pipx_venvs_dir / "pylint") assert "--no-cache-dir" in metadata.injected_packages["black"].pip_args assert not run_pipx_cli(["upgrade", "--include-injected", "pylint"]) capsys.readouterr() metadata = PipxMetadata(pipx_venvs_dir / "pylint") assert "--no-cache-dir" in metadata.injected_packages["black"].pip_args pipx-1.15.0/tests/test_upgrade_all.py000066400000000000000000000024641521706623000176200ustar00rootroot00000000000000import pytest from helpers import PIPX_METADATA_LEGACY_VERSIONS, mock_legacy_venv, run_pipx_cli def test_upgrade_all(pipx_temp_env, capsys): assert run_pipx_cli(["upgrade", "pycowsay"]) assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["upgrade-all"]) def test_upgrade_all_none(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["upgrade-all"]) captured = capsys.readouterr() assert "No packages upgraded after running 'pipx upgrade-all'" in captured.out def test_upgrade_all_with_pip_args(pipx_temp_env, capsys): assert not run_pipx_cli(["install", "pycowsay"]) assert not run_pipx_cli(["upgrade-all", "--pip-args=--no-cache-dir"]) @pytest.mark.parametrize("metadata_version", PIPX_METADATA_LEGACY_VERSIONS) def test_upgrade_all_legacy_venv(pipx_temp_env, capsys, metadata_version): assert run_pipx_cli(["upgrade", "pycowsay"]) assert not run_pipx_cli(["install", "pycowsay"]) mock_legacy_venv("pycowsay", metadata_version=metadata_version) if metadata_version is None: capsys.readouterr() assert run_pipx_cli(["upgrade-all"]) assert "The following package(s) failed to upgrade: pycowsay" in capsys.readouterr().err else: assert not run_pipx_cli(["upgrade-all"]) pipx-1.15.0/tests/test_upgrade_shared.py000066400000000000000000000054521521706623000203160ustar00rootroot00000000000000import subprocess import pytest from helpers import run_pipx_cli @pytest.fixture def shared_libs(pipx_ultra_temp_env): # local import to get the shared_libs object patched by fixtures from pipx.shared_libs import shared_libs as _shared_libs # noqa: PLC0415 yield _shared_libs def test_upgrade_shared(shared_libs, capsys, caplog): assert shared_libs.has_been_updated_this_run is False assert shared_libs.is_valid is False assert run_pipx_cli(["upgrade-shared", "-v"]) == 0 captured = capsys.readouterr() assert "creating shared libraries" in captured.err assert "upgrading shared libraries" in captured.err assert "Upgrading shared libraries in" in caplog.text assert "Already upgraded libraries in" not in caplog.text assert shared_libs.has_been_updated_this_run is True assert shared_libs.is_valid is True # type: ignore[unreachable] shared_libs.has_been_updated_this_run = False assert run_pipx_cli(["upgrade-shared", "-v"]) == 0 captured = capsys.readouterr() assert "creating shared libraries" not in captured.err assert "upgrading shared libraries" in captured.err assert "Upgrading shared libraries in" in caplog.text assert "Already upgraded libraries in" not in caplog.text assert shared_libs.has_been_updated_this_run is True assert run_pipx_cli(["upgrade-shared", "-v"]) == 0 assert "Already upgraded libraries in" in caplog.text def test_upgrade_shared_pip_args(shared_libs, capsys, caplog): assert shared_libs.has_been_updated_this_run is False assert shared_libs.is_valid is False assert run_pipx_cli(["upgrade-shared", "-v", "--pip-args='--no-index'"]) == 1 captured = capsys.readouterr() assert "creating shared libraries" in captured.err assert "upgrading shared libraries" in captured.err assert "Upgrading shared libraries in" in caplog.text assert "Already upgraded libraries in" not in caplog.text assert shared_libs.has_been_updated_this_run is False assert shared_libs.is_valid is True def test_upgrade_shared_pin_pip(shared_libs): def pip_version(): cmd = "from importlib.metadata import version; print(version('pip'))" ret = subprocess.run([shared_libs.python_path, "-c", cmd], check=True, capture_output=True, text=True) return ret.stdout.strip() assert shared_libs.has_been_updated_this_run is False assert shared_libs.is_valid is False assert run_pipx_cli(["upgrade-shared", "-v", "--pip-args=pip==24.0"]) == 0 assert shared_libs.is_valid is True assert pip_version() == "24.0" # type: ignore[unreachable] shared_libs.has_been_updated_this_run = False # reset for next run assert run_pipx_cli(["upgrade-shared", "-v", "--pip-args=pip==23.3.2"]) == 0 assert shared_libs.is_valid is True assert pip_version() == "23.3.2" pipx-1.15.0/tests/test_util.py000066400000000000000000000021231521706623000163060ustar00rootroot00000000000000from __future__ import annotations import sys import pytest from pipx.util import run_subprocess @pytest.mark.parametrize( ("env_value", "expected"), [ pytest.param(None, "subprocess", id="defaults_to_subprocess"), pytest.param("import", "import", id="preserves_explicit"), ], ) def test_subprocess_keyring_provider(monkeypatch: pytest.MonkeyPatch, env_value: str | None, expected: str) -> None: if env_value is not None: monkeypatch.setenv("PIP_KEYRING_PROVIDER", env_value) else: monkeypatch.delenv("PIP_KEYRING_PROVIDER", raising=False) result = run_subprocess([sys.executable, "-c", "import os; print(os.environ['PIP_KEYRING_PROVIDER'])"]) assert result.stdout.strip() == expected def test_subprocess_pythonsafepath_set_for_python_commands() -> None: """Test that PYTHONSAFEPATH is set for Python subprocess calls to prevent CWD shadowing (issue #1575).""" result = run_subprocess( [sys.executable, "-c", "import os, sys; sys.stdout.write(os.environ.get('PYTHONSAFEPATH', ''))"] ) assert result.stdout == "1" pipx-1.15.0/tox.toml000066400000000000000000000035551521706623000142770ustar00rootroot00000000000000requires = ["tox>=4.45"] env_list = [ "3.13", "3.12", "3.11", "3.10", "lint", "docs", "man", ] skip_missing_interpreters = true [env_run_base] description = "run tests with {env_name}" package = "wheel" dependency_groups = ["test"] set_env.COVERAGE_FILE = "{work_dir}{/}.coverage.{env_name}" commands = [ [ "pytest", {replace = "posargs", default = [ "--cov=pipx", "--cov-report=term-missing:skip-covered", "--cov-report=xml:{work_dir}{/}coverage.{env_name}.xml", "-n", "auto", "--dist", "loadfile", "tests", ], extend = true}, ], ] uv_seed = true [env.dev] description = "generate a DEV environment" package = "editable" dependency_groups = ["dev"] commands = [ ["uv", "pip", "tree"], ["python", "-c", "import sys; print(sys.executable)"], ] [env.lint] description = "run pre-commit on the codebase" skip_install = true dependency_groups = ["lint"] commands = [["pre-commit", "run", "--all-files"]] [env.docs] description = "build documentation" dependency_groups = ["docs"] set_env.NO_MKDOCS_2_WARNING = "true" commands = [ ["mkdocs", "build", "--strict", "--site-dir", "{env:READTHEDOCS_OUTPUT:site}{/}html"], ["python", "-c", "print('docs built to: file://{tox_root}{/}{env:READTHEDOCS_OUTPUT:site}{/}html{/}index.html')"], ] [env.docs-live] description = "serve documentation with live reload" dependency_groups = ["docs"] set_env.NO_MKDOCS_2_WARNING = "true" commands = [ ["mkdocs", "serve", "--dirty", "--open"], ] [env.man] description = "build man page" dependency_groups = ["man"] commands = [ ["python", "scripts/generate_man.py"], ] [env.zipapp] description = "build zipapp via shiv" skip_install = true dependency_groups = ["zipapp"] allowlist_externals = ["{tox_root}{/}pipx.pyz"] commands = [ ["shiv", "-c", "pipx", "-o", "{tox_root}{/}pipx.pyz", "."], ["{tox_root}{/}pipx.pyz", "--version"], ]