pax_global_header00006660000000000000000000000064147756062750014535gustar00rootroot0000000000000052 comment=8293f059f6bccef2d7eedea8dfa25a645651ef78 pdm-2.23.1/000077500000000000000000000000001477560627500124025ustar00rootroot00000000000000pdm-2.23.1/.github/000077500000000000000000000000001477560627500137425ustar00rootroot00000000000000pdm-2.23.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001477560627500161255ustar00rootroot00000000000000pdm-2.23.1/.github/ISSUE_TEMPLATE/bug_report.yml000066400000000000000000000040741477560627500210250ustar00rootroot00000000000000name: "Bug report" description: Create a report to help us improve labels: ['🐛 bug'] body: - type: markdown attributes: value: "Thank you for taking the time to report a bug. Please provide as much information as possible to help us understand and resolve the issue." - type: textarea id: describe-bug attributes: label: Describe the bug description: "A clear and concise description of what the bug is." placeholder: "Describe the bug..." validations: required: true - type: textarea id: reproduce-bug attributes: label: To reproduce description: "Steps to reproduce the behavior." placeholder: "Steps to reproduce the behavior..." validations: required: true - type: textarea id: expected-behavior attributes: label: Expected Behavior description: "A clear and concise description of what you expected to happen." placeholder: "Explain what you expected to happen..." validations: required: true - type: textarea id: "environment-info" attributes: label: Environment Information description: "Paste the output of `pdm info && pdm info --env`" placeholder: "Paste the output of `pdm info && pdm info --env`" validations: required: true - type: textarea id: "pdm-debug-output" attributes: label: "Verbose Command Output" description: "Please provide the command output with `-v`." placeholder: "Add the command output with `-v`..." validations: required: false - type: textarea id: additional-context attributes: label: Additional Context description: "Add any other context about the problem here." placeholder: "Additional details..." validations: required: false - type: checkboxes id: willing-to-submit-pr attributes: label: "Are you willing to submit a PR to fix this bug?" description: "Let us know if you are willing to contribute a fix by submitting a Pull Request." options: - label: "Yes, I would like to submit a PR." pdm-2.23.1/.github/ISSUE_TEMPLATE/feature_request.yml000066400000000000000000000030241477560627500220520ustar00rootroot00000000000000name: "Feature / Enhancement Proposal" description: Suggest an idea for this project labels: ['⭐ enhancement'] body: - type: markdown attributes: value: "Thank you for suggesting a new feature. Please fill out the details below to help us understand your idea better." - type: textarea id: feature-description attributes: label: Feature Description description: "A detailed description of the feature you would like to see." placeholder: "Describe the feature you'd like..." validations: required: true - type: textarea id: problem-solution attributes: label: Problem and Solution description: "Describe the problem that this feature would solve. Explain how you envision it working." placeholder: "What problem does this feature solve? How do you envision it working?" validations: required: true - type: textarea id: additional-context attributes: label: Additional Context description: "Add any other context or screenshots about the feature request here." placeholder: "Add any other context or screenshots about the feature request here." validations: required: false - type: checkboxes id: willing-to-contribute attributes: label: "Are you willing to contribute to the development of this feature?" description: "Let us know if you are willing to help by contributing code or other resources." options: - label: "Yes, I am willing to contribute to the development of this feature." pdm-2.23.1/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000002651477560627500175460ustar00rootroot00000000000000## Pull Request Checklist - [ ] A news fragment is added in `news/` describing what is new. - [ ] Test cases added for changed code. ## Describe what you have changed in this PR. pdm-2.23.1/.github/workflows/000077500000000000000000000000001477560627500157775ustar00rootroot00000000000000pdm-2.23.1/.github/workflows/ci.yml000066400000000000000000000062431477560627500171220ustar00rootroot00000000000000name: Tests on: pull_request: branches: - main - dev - "maintain/*" paths-ignore: - "docs/**" - "news/**" - "*.md" - Dockerfile - tasks/benchmarks/** push: branches: - main - dev - "maintain/*" paths-ignore: - "docs/**" - "news/**" - "*.md" - Dockerfile - tasks/benchmarks/** concurrency: group: ${{ github.event.number || github.run_id }} cancel-in-progress: true jobs: Testing: env: PYTHONDEVMODE: 1 runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] os: [ubuntu-latest, windows-latest, macos-latest] install-via: [pip] include: - python-version: "3.11" os: ubuntu-latest install-via: script steps: - uses: actions/checkout@v4 - name: Setup Python Versions uses: actions/setup-python@v5 with: python-version: | 3.9 3.10 3.11 3.12 3.13 allow-prereleases: true if: matrix.os != 'macos-latest' - name: Setup Python Versions uses: actions/setup-python@v5 with: python-version: | 3.10 3.11 3.12 3.13 allow-prereleases: true if: matrix.os == 'macos-latest' - name: Setup Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} cache: pip allow-prereleases: true - name: Cache venv uses: actions/cache@v3 with: path: .venv key: venv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('pdm.lock') }} - name: Install uv uses: astral-sh/setup-uv@v1 with: version: "latest" - name: Install current PDM via pip if: matrix.install-via == 'pip' run: python -m pip install -U . - name: Install current PDM via script if: matrix.install-via == 'script' run: | shasum -a256 --check install-pdm.py.sha256 python install-pdm.py --version head echo "$HOME/.local/bin" >> $GITHUB_PATH - name: Install Dev Dependencies run: | pdm install -v -dGtest pdm run pip install -U setuptools pdm info - name: Run Tests run: pdm run pytest -n auto --cov=pdm --cov-config=pyproject.toml --cov-report=xml tests - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./coverage.xml flags: unittests Pack: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-python@v5 with: python-version: 3.x - name: Install PDM run: | python -m pip install . pdm self add pdm-packer - name: Pack pdm run: pdm pack - name: Test zipapp run: python pdm.pyz --version pdm-2.23.1/.github/workflows/release.yml000066400000000000000000000103121477560627500201370ustar00rootroot00000000000000name: Release on: push: tags: - "*" defaults: run: # make sure to work on Windows shell: bash jobs: release-pypi: name: release-pypi runs-on: ubuntu-latest permissions: id-token: write contents: write steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.11" cache: pip - name: Check prerelease id: check_version run: | if [[ "${{ github.ref }}" =~ ^refs/tags/[0-9.]+$ ]]; then echo "PRERELEASE=false" >> $GITHUB_OUTPUT else echo "PRERELEASE=true" >> $GITHUB_OUTPUT fi - name: Build artifacts run: | pipx run build - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: pdm-wheel path: dist/*.whl if-no-files-found: error retention-days: 15 - name: Test Build run: | python -m pip install "pdm[locked] @ file://$(ls ${GITHUB_WORKSPACE}/dist/*.whl)" pdm --help - name: Publish package distributions to PyPI run: pdm publish --no-build - name: Get Changelog id: get-changelog run: | awk '/## Release/{if (flag==1)exit;else;flag=1;next} flag' CHANGELOG.md > .changelog.md - name: Create Release uses: actions/create-release@main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ github.ref }} release_name: v${{ github.ref }} body_path: .changelog.md draft: false prerelease: ${{ steps.check_version.outputs.PRERELEASE }} - name: Trigger Bucket Update uses: benc-uk/workflow-dispatch@v1 with: workflow: Excavator repo: frostming/scoop-frostming token: ${{ secrets.G_T }} ref: master binary: needs: release-pypi runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: ["ubuntu-22.04", "ubuntu-22.04-arm", "windows-2022", "macos-13", "macos-14"] env: PYAPP_REPO: pyapp PYAPP_VERSION: "0.27.0" PYAPP_PROJECT_NAME: pdm PYAPP_PROJECT_VERSION: ${{ github.ref_name }} PYAPP_SELF_COMMAND: app # since `self` has been taken in `pdm` PYAPP_DISTRIBUTION_EMBED: true PYAPP_PROJECT_FEATURES: locked SCCACHE_GHA_ENABLED: "true" RUSTC_WRAPPER: "sccache" SOURCE_FILE: ${{ matrix.os != 'windows-2022' && 'pyapp' || 'pyapp.exe' }} TARGET_FILE: ${{ matrix.os != 'windows-2022' && 'pdm' || 'pdm.exe' }} steps: - name: Checkout uses: actions/checkout@v4 - name: Fetch PyApp run: >- mkdir $PYAPP_REPO && curl -L https://github.com/ofek/pyapp/releases/download/v$PYAPP_VERSION/source.tar.gz | tar --strip-components=1 -xzf - -C $PYAPP_REPO - name: Setup Rust uses: dtolnay/rust-toolchain@stable - name: Run sccache-cache uses: mozilla-actions/sccache-action@v0.0.9 - name: Download artifacts uses: actions/download-artifact@v4 with: name: pdm-wheel path: dist - name: Configure embedded wheel run: | cd dist wheel="$(echo *.whl)" mv $wheel ../$PYAPP_REPO echo "PYAPP_PROJECT_PATH=$wheel" >> $GITHUB_ENV echo "TARGET_TRIPLE=$(rustc --version --verbose | grep "host" | awk '{print $2}')" >> $GITHUB_ENV - name: Build run: | cd $PYAPP_REPO cargo build --release mv target/release/$SOURCE_FILE ../$TARGET_FILE - name: Upload Assets uses: actions/upload-artifact@v4 with: name: pdm-${{ github.ref_name }}-${{ env.TARGET_TRIPLE }} path: ${{ env.TARGET_FILE }} if-no-files-found: error retention-days: 15 - name: Upload to GitHub Release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} UPLOAD_FILE: pdm-${{ github.ref_name }}-${{ env.TARGET_TRIPLE }}.tar.gz run: | tar -czf $UPLOAD_FILE $TARGET_FILE gh release upload ${{ github.ref_name }} $UPLOAD_FILE --clobber pdm-2.23.1/.gitignore000066400000000000000000000036371477560627500144030ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ pip-wheel-metadata/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ docs/site # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ /venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ .vscode/ caches/ .idea/ __pypackages__ .pdm.toml .pdm-python temp.py # Pyannotate generated stubs type_info.json .pdm-build/ src/pdm/VERSION pdm-2.23.1/.pre-commit-config.yaml000066400000000000000000000012371477560627500166660ustar00rootroot00000000000000ci: autoupdate_schedule: monthly repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: 'v0.11.4' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix, --show-fixes] - id: ruff-format - repo: https://github.com/codespell-project/codespell rev: v2.4.1 hooks: - id: codespell # See pyproject.toml for args additional_dependencies: - tomli - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.15.0 hooks: - id: mypy args: [src] pass_filenames: false additional_dependencies: - types-requests - types-certifi - pytest pdm-2.23.1/.pre-commit-hooks.yaml000066400000000000000000000013001477560627500165330ustar00rootroot00000000000000- id: pdm-lock-check name: pdm-lock-check description: run pdm lock --check to validate config entry: pdm lock --check language: python language_version: python3 pass_filenames: false files: ^pyproject.toml$ - id: pdm-export name: pdm-export-lock description: export locked packages to requirements.txt or setup.py entry: pdm export language: python language_version: python3 pass_filenames: false files: ^pdm.lock$ - id: pdm-sync name: pdm-sync description: sync current working set with pdm.lock entry: pdm sync language: python language_version: python3 pass_filenames: false stages: - post-checkout - post-merge - post-rewrite always_run: true pdm-2.23.1/.readthedocs.yaml000066400000000000000000000010271477560627500156310ustar00rootroot00000000000000# Read the Docs configuration file for MkDocs projects # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.12" jobs: post_create_environment: - python install-pdm.py --path ~/.local/pdm - ~/.local/pdm/bin/pdm --version post_install: - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH ~/.local/pdm/bin/pdm install -dG doc mkdocs: configuration: mkdocs.yml pdm-2.23.1/CHANGELOG.md000066400000000000000000005535371477560627500142350ustar00rootroot00000000000000## Release v2.23.1 (2025-04-09) ### Features & Improvements - Use `pyapp` to wrap `pdm` as a Python application that bootstrap itself at runtime. ([#3429](https://github.com/pdm-project/pdm/issues/3429)) - Support all providers `id` is supporting currently for OIDC trusted publishing ([#3441](https://github.com/pdm-project/pdm/issues/3441)) ### Bug Fixes - Installation error for local plugins specified with file URL without a name. ([#3407](https://github.com/pdm-project/pdm/issues/3407)) - Eliminate the warning about inherit_metadata when using uv mode. ([#3434](https://github.com/pdm-project/pdm/issues/3434)) - Fix an installation failure when installing editable local dependencies on Windows and Python 3.13. ([#3444](https://github.com/pdm-project/pdm/issues/3444)) - Fix a bug that overriden requirements in lock file get rewritten when adding a new requirement. ([#3446](https://github.com/pdm-project/pdm/issues/3446)) - Cyclic group inclusion is detected incorrectly. Also show the cyclic group names in the error message. ([#3447](https://github.com/pdm-project/pdm/issues/3447)) - Fix a bug that `pdm remove` doesn't handle dependency groups include correctly. ([#3452](https://github.com/pdm-project/pdm/issues/3452)) - Update `unearth` to address an issue downloading git repos with short commit hash. ([#3455](https://github.com/pdm-project/pdm/issues/3455)) ## Release v2.23.0 (2025-04-01) ### Features & Improvements - Add `pdm python find` command to search for a python interpreter. ([#3389](https://github.com/pdm-project/pdm/issues/3389)) - `pdm import` now converts `package-mode` from Poetry's settings table to `distribution`. ([#3427](https://github.com/pdm-project/pdm/issues/3427)) ### Bug Fixes - Excluding non-existing groups for `pdm remove`. ([#3404](https://github.com/pdm-project/pdm/issues/3404)) - Fix a bug that `pdm add` and `pdm update` remove dependency groups incorrectly. ([#3418](https://github.com/pdm-project/pdm/issues/3418)) - Fix a bug that using resolution overrides drops extra dependencies. ([#3426](https://github.com/pdm-project/pdm/issues/3426)) ## Release v2.22.4 (2025-03-07) ### Bug Fixes - Ensure dev-dependencies are added to the correct group when the `tool.pdm.dev-dependencies` table has groups. ([#3392](https://github.com/pdm-project/pdm/issues/3392)) ## Release v2.22.3 (2025-01-27) ### Bug Fixes - Don't validate local file requirements that are not used. ([#3376](https://github.com/pdm-project/pdm/issues/3376)) - Don't set "dependencies" as empty list for uv toml if there is no dependencies in the raw toml file. ([#3378](https://github.com/pdm-project/pdm/issues/3378)) - Add a dummy project name to the script environment pyproject.toml. ([#3382](https://github.com/pdm-project/pdm/issues/3382)) ## Release v2.22.2 (2025-01-11) ### Features & Improvements - Write installer metadata like `INSTALLER` and `REQUESTED` to dist-info directory when installing packages. ([#3359](https://github.com/pdm-project/pdm/issues/3359)) - Respect `.python-version` file in the project root directory when selecting the Python interpreter. By default, it will be written when running `pdm use` command. ([#3367](https://github.com/pdm-project/pdm/issues/3367)) ### Bug Fixes - Fix a problem of missing dependencies when adding to dev dependencies if both editable and non-editable dependencies exist. ([#3361](https://github.com/pdm-project/pdm/issues/3361)) - Use stdlib for URL <-> Path conversions. ([#3362](https://github.com/pdm-project/pdm/issues/3362)) - `shellingham.detect_shell()` returns `('tcsh', '/bin/tcsh')` for tcsh on FreeBSD, so the current code tries to use the Bash venv activation script and fails due to syntax error. This change fixes the issue. ([#3366](https://github.com/pdm-project/pdm/issues/3366)) - Fix a performance issue because pypi source credentials were being queried many times from keyring. ([#3368](https://github.com/pdm-project/pdm/issues/3368)) ## Release v2.22.1 (2024-12-19) ### Bug Fixes - Fix zsh hanging issue by removing PyPI package completion. ([#3329](https://github.com/pdm-project/pdm/issues/3329)) - Write dev dependencies to `dependency-groups` section when importing project from other package managers. ([#3354](https://github.com/pdm-project/pdm/issues/3354)) ### Miscellany - Show a warning when resolving against cross-platform targets under uv mode. ([#3341](https://github.com/pdm-project/pdm/issues/3341)) ## Release v2.22.0 (2024-12-09) ### Features & Improvements - Use minimal template if the project is an application. ([#3295](https://github.com/pdm-project/pdm/issues/3295)) - Add one `safe_compatible` version specifiers saving strategy. ([#3301](https://github.com/pdm-project/pdm/issues/3301)) - Allow customizing scripts display with `scripts.show_header` settings. ([#3313](https://github.com/pdm-project/pdm/issues/3313)) - Speed up the resolution by only resolving wheel candidates if possible. ([#3319](https://github.com/pdm-project/pdm/issues/3319)) - Drop version from the search result, following the change of warehouse. ([#3328](https://github.com/pdm-project/pdm/issues/3328)) - Support `overrides` settings under `[tool.pdm.resolution]` with use_uv ([#3330](https://github.com/pdm-project/pdm/issues/3330)) ### Bug Fixes - No longer requires `wheel` to build a setuptools-backed package. ([#3320](https://github.com/pdm-project/pdm/issues/3320)) - Fix an inconsistent behavior when running `pdm remove ` with uv enabled. ([#3323](https://github.com/pdm-project/pdm/issues/3323)) - Fix: uninstallation error when pdm is not installed before. ([#3325](https://github.com/pdm-project/pdm/issues/3325)) - Fix a bug in uv mode that direct URL dependencies can't be installed. ([#3332](https://github.com/pdm-project/pdm/issues/3332)) - Fix a crash issue when rewriting dependency groups with `include-group` items. ([#3333](https://github.com/pdm-project/pdm/issues/3333)) - Also read username from keyring if missing in source/repository config. ([#3334](https://github.com/pdm-project/pdm/issues/3334)) - Allow configuring repositories in project. ([#3335](https://github.com/pdm-project/pdm/issues/3335)) ### Miscellany - Mark tests that require uv and skip them if uv is not found. ([#3324](https://github.com/pdm-project/pdm/issues/3324)) ## Release v2.21.0 (2024-11-25) ### Features & Improvements - Pass original working directory as env variable to pdm scripts ([#3179](https://github.com/pdm-project/pdm/issues/3179)) - Output similar commands or script command when the input command is not correct ([#3270](https://github.com/pdm-project/pdm/issues/3270)) - improve readability of Python interpreter validation message ([#3276](https://github.com/pdm-project/pdm/issues/3276)) - Print task name by default when using `pdm run` ([#3277](https://github.com/pdm-project/pdm/issues/3277)) - Make `OrderedSet.__contains__` run in O(1) ([#3280](https://github.com/pdm-project/pdm/issues/3280)) - Emit `post_lock` after writing pyproject.toml and pdm.lock in add/update ([#3285](https://github.com/pdm-project/pdm/issues/3285)) - Drop support of Python 3.8 ([#3298](https://github.com/pdm-project/pdm/issues/3298)) ### Bug Fixes - Fix the name normalization issue for optional dependency groups. ([#3271](https://github.com/pdm-project/pdm/issues/3271)) - Don't use uv when installing plugins in project. ([#3283](https://github.com/pdm-project/pdm/issues/3283)) - Fix the bug that pdm plugins are invalid after installation on ubuntu system python. ([#3289](https://github.com/pdm-project/pdm/issues/3289)) ## Release v2.20.1 (2024-11-09) ### Features & Improvements - Add a fixer to remove the deprecated `cross_platform` strategy from lock file. ([#3259](https://github.com/pdm-project/pdm/issues/3259)) ### Bug Fixes - Fix the bug that `pdm build` would fail when `use_uv` is true. ([#3231](https://github.com/pdm-project/pdm/issues/3231)) - Fix group name normalization when comparing groups. ([#3247](https://github.com/pdm-project/pdm/issues/3247)) - Inherit file descriptors instead of closing when running child processes in `pdm run`. ([#3252](https://github.com/pdm-project/pdm/issues/3252)) - Fix using `no_proxy` when `all_proxy` is set. ([#3254](https://github.com/pdm-project/pdm/issues/3254)) - Preserve multiline arrays and don't add empty tool.pdm table header when updating the pyproject.toml. ([#3258](https://github.com/pdm-project/pdm/issues/3258)) - Fix compatibility of `ErrorArgumentParser` for Python 3.12 and above. ([#3264](https://github.com/pdm-project/pdm/issues/3264)) ## Release v2.20.0 (2024-10-31) ### Features & Improvements - Support dependency groups as standardized by [PEP 735](https://peps.python.org/pep-0735/). By default, dev dependencies will be written to `[dependency-groups]` table. ([#3230](https://github.com/pdm-project/pdm/issues/3230)) ### Bug Fixes - Fix a bug that `strategy.inherit_metadata` config is not honored when using `--lockfile` option. ([#3232](https://github.com/pdm-project/pdm/issues/3232)) - Always perform install-time resolution when `use_uv` is on. ([#3233](https://github.com/pdm-project/pdm/issues/3233)) ### Miscellany - Update `resolvelib` to 1.1.0. ([#3235](https://github.com/pdm-project/pdm/issues/3235)) ## Release v2.19.3 (2024-10-19) ### Features & Improvements - Allow linking existing Python interpreters to PDM's managed location. ([#3215](https://github.com/pdm-project/pdm/issues/3215)) ### Bug Fixes - Fix a bug that overrides provided by environment variables do not work. ([#3182](https://github.com/pdm-project/pdm/issues/3182)) - Allow prereleases when the requirement is pinned even if disabled by project ([#3202](https://github.com/pdm-project/pdm/issues/3202)) - Pass the python path to the uv venv command. ([#3204](https://github.com/pdm-project/pdm/issues/3204)) - Fix the infinite loop when running in uv mode if the current project has dynamic metadata. ([#3207](https://github.com/pdm-project/pdm/issues/3207)) - Add `--no-frozen-deps` option to `install-pdm.py` script to allow installing newer versions of dependencies. ([#3213](https://github.com/pdm-project/pdm/issues/3213)) - `pdm self update` now prefers the locked dependencies unless `--no-frozen-deps` is specified. ([#3216](https://github.com/pdm-project/pdm/issues/3216)) - By default, `pdm outdated` will only list direct dependencies. This can be changed by adding the `--include-sub` option. ([#3218](https://github.com/pdm-project/pdm/issues/3218)) ### Documentation - Show users the way to uninstall pdm in a more obvious way ([#2470](https://github.com/pdm-project/pdm/issues/2470)) ## Release v2.19.2 (2024-10-11) ### Features & Improvements - Support installing free-threaded Python interpreters with the `t` suffix. ([#3201](https://github.com/pdm-project/pdm/issues/3201)) ### Bug Fixes - `use_uv` fails to lock when there are non-ascii characters in pyproject.toml on Windows. ([#3181](https://github.com/pdm-project/pdm/issues/3181)) - Fix the `pre_install` and `post_install` signals receiving an exhausted generator, instead of a list of packages. ([#3190](https://github.com/pdm-project/pdm/issues/3190)) - Create backup file with random filename to avoid conflicts. ([#3193](https://github.com/pdm-project/pdm/issues/3193)) - Fix the logic error in the `uv` format marker matching. ([#3197](https://github.com/pdm-project/pdm/issues/3197)) - `pdm lock --check` on a lockfile generated with older PDM version has a 0 exit code when there's a change in `pyproject.toml`. ([#3199](https://github.com/pdm-project/pdm/issues/3199)) ### Documentation - Fixed *Bash Completion* suggestion so it doesn't require root privileges ([#3183](https://github.com/pdm-project/pdm/issues/3183)) ## Release v2.19.1 (2024-09-23) ### Bug Fixes - PDM libraries are not loaded correctly for in-process scripts when installed in the user site. ([#3178](https://github.com/pdm-project/pdm/issues/3178)) ## Release v2.19.0 (2024-09-23) ### Breaking Changes - The minimum supported Python version of projects using PDM has been bumped to 3.8. ([#3176](https://github.com/pdm-project/pdm/issues/3176)) ### Bug Fixes - Fallback version to 0.0.0 when the version is not specified or empty. This can avoid crash when building such project. ([#3163](https://github.com/pdm-project/pdm/issues/3163)) - Ensures that `/` is URL encoded in sources URL environment variables. ([#3169](https://github.com/pdm-project/pdm/issues/3169)) - Call functions from shared library in the in-process `env_spec.py` script. ([#3176](https://github.com/pdm-project/pdm/issues/3176)) ### Removals and Deprecations - PDM no longer falls back to `setuptools-pep660` when the build backend doesn't support PEP 660. ([#3159](https://github.com/pdm-project/pdm/issues/3159)) ### Miscellany - Change the project structure to a normal package from a namespace package. ([#3155](https://github.com/pdm-project/pdm/issues/3155)) ## Release v2.18.2 (2024-09-10) ### Bug Fixes - Respect the `excludes` and `overrides` settings when installing packages. ([#3113](https://github.com/pdm-project/pdm/issues/3113)) - Fix a bug of export command that packages with extras are included twice. ([#3123](https://github.com/pdm-project/pdm/issues/3123)) - Remove empty groups when removing packages with `pdm remove`. ([#3133](https://github.com/pdm-project/pdm/issues/3133)) - When running `pdm venv purge`, if the current project's python version had been referencing the removed venv then clear it out. ([#3137](https://github.com/pdm-project/pdm/issues/3137)) - Fix command `pdm config` to not show site configuration file path if it doesn't exist. ([#3149](https://github.com/pdm-project/pdm/issues/3149)) - Now when `--no-markers` is used, the exported requirements can only work on the current platform. ([#3152](https://github.com/pdm-project/pdm/issues/3152)) ### Miscellany - Skip tests related to python installation on non-standard platforms. ([#3053](https://github.com/pdm-project/pdm/issues/3053)) ## Release v2.19.0a0 (2024-09-05) ### Breaking Changes - `pre_install` and `post_install` signals now receive the list of packages to be installed, instead of a candidate mapping. ([#3144](https://github.com/pdm-project/pdm/issues/3144)) ### Features & Improvements - Deprecate `Core.synchronizer_class` attribute. To get the synchronizer class, use `Project.get_synchronizer` method instead. Deprecate `Core.resolver_class` attribute. To get the resolver class, use `Project.get_resolver` method instead. ([#3144](https://github.com/pdm-project/pdm/issues/3144)) - Add experimental support for `uv` as the resolver and installer. One can opt in by setting `use_uv` to `true` using `pdm config` command. ([#3144](https://github.com/pdm-project/pdm/issues/3144)) ## Release v2.18.1 (2024-08-16) ### Bug Fixes - Skip checking `project.name` if it is absent when running `pdm outdated`. ([#3095](https://github.com/pdm-project/pdm/issues/3095)) - Don't remove the `cross_platform` strategy from old lock files. ([#3105](https://github.com/pdm-project/pdm/issues/3105)) - Fix a bug that the VCS revision is lost if the candidate metadata is cached during resolution. ([#3107](https://github.com/pdm-project/pdm/issues/3107)) - Fix a bug that PDM can't delete source password when saved in keyring. ([#3108](https://github.com/pdm-project/pdm/issues/3108)) ## Release v2.18.0 (2024-08-14) ### Features & Improvements - Respect certificates in env vars `REQUESTS_CA_BUNDLE` and `CURL_CA_BUNDLE` when verifying SSL certificates. ([#3076](https://github.com/pdm-project/pdm/issues/3076)) - Allow pypi.verify_ssl to be configured via PDM_PYPI_VERIFY_SSL environmental variable. ([#3081](https://github.com/pdm-project/pdm/issues/3081)) - Clean logs older than 7 days. ([#3091](https://github.com/pdm-project/pdm/issues/3091)) - Polish the UI looking of locking packages to display the progress. ([#3100](https://github.com/pdm-project/pdm/issues/3100)) ### Bug Fixes - Fixed `pdm venv activate` to remove quotes such that `iex (pdm venv activate)` works correctly ([#2895](https://github.com/pdm-project/pdm/issues/2895)) - Don't crash if the version can't be resolved from the self project. ([#3077](https://github.com/pdm-project/pdm/issues/3077)) - Don't fail `install-pdm.py` if there is an invalid `pyproject.toml` file under the current directory. ([#3085](https://github.com/pdm-project/pdm/issues/3085)) - Make it able to expand env vars in the the dotenv file. Expose `PDM_PROJECT_ROOT` to the dotenv file for expansion. ([#3087](https://github.com/pdm-project/pdm/issues/3087)) - Fix a bug that Python markers from the existing locked packages are considered when locking with `--append` option. ([#3089](https://github.com/pdm-project/pdm/issues/3089)) - Backfill urls from configured indexed when exporting to requirements.txt. ([#3094](https://github.com/pdm-project/pdm/issues/3094)) - Consider the auto-selected Python range when installing from requirements.txt. ([#3095](https://github.com/pdm-project/pdm/issues/3095)) - Fix a bug that env vars do not override project config correctly. ([#3099](https://github.com/pdm-project/pdm/issues/3099)) ## Release v2.17.3 (2024-08-01) ### Bug Fixes - Fix a crash issue when `requires-python` is absent in the project metadata. ([#3062](https://github.com/pdm-project/pdm/issues/3062)) - Now correctly sets related config for PDM_IGNORE_SAVED_PYTHON when it is set to "false", "no", "0". ([#3064](https://github.com/pdm-project/pdm/issues/3064)) - Fix a bug that PDM plugins installed from project-root cannot be loaded, if they have dependencies. ([#3067](https://github.com/pdm-project/pdm/issues/3067)) ## Release v2.17.2 (2024-07-31) ### Features & Improvements - Improve the installation progress output to show the time elapsed. ([#3051](https://github.com/pdm-project/pdm/issues/3051)) - The effect of `pypi.ignore_stored_index` changes a bit. Now even if it is true, index configurations in the config will still be loaded if the index is listed in the `pyproject.toml`. ([#3052](https://github.com/pdm-project/pdm/issues/3052)) ### Bug Fixes - Ignore invalid requires-python values from index. ([#3038](https://github.com/pdm-project/pdm/issues/3038)) - Fix the group selection logic, to make `--without GROUP` work as expected. ([#3045](https://github.com/pdm-project/pdm/issues/3045)) - Suppress outputs for `pdm python install --quiet`. ([#3049](https://github.com/pdm-project/pdm/issues/3049)) ## Release v2.17.1 (2024-07-19) ### Bug Fixes - Raise dep-logic lower bound to 0.4.2 to fix issues with pdm lock after upgrading from older pdm versions ([#3033](https://github.com/pdm-project/pdm/issues/3033)) - Correct the current platform and architecture for win32 and macos systems. ([#3035](https://github.com/pdm-project/pdm/issues/3035)) ### Miscellany - Fix zsh completions ([#3031](https://github.com/pdm-project/pdm/issues/3031)) ## Release v2.17.0 (2024-07-18) ### Breaking Changes - `LockedRepository.all_candidates` now returns a `dict[str, list[Candidate]]` instead of `dict[str, Candidate]`. ([#2995](https://github.com/pdm-project/pdm/issues/2995)) - `post_lock` hook now receives a resolution result of type `dict[str, list[Candidate]]`, instead of `dict[str, Candidate]`. ([#2995](https://github.com/pdm-project/pdm/issues/2995)) ### Features & Improvements - Support reading requirement constraints from pip-style requirement files for "overriding" via `--override` option. ([#2896](https://github.com/pdm-project/pdm/issues/2896)) - Add a `--non-interactive` option for automation scenarios, also interactive prompts will not show up when not running in an interactive terminal. ([#2934](https://github.com/pdm-project/pdm/issues/2934)) - Refactored `pdm python install --list` to reuse the same implementation as other cli commands that work with Python interpreters from pbs_installer. ([#2977](https://github.com/pdm-project/pdm/issues/2977)) - Add `--license` and `--project-version` as CLI options to control and streamline them during `pdm init` - especially in automated scenarios with `--non-interactive` ([#2978](https://github.com/pdm-project/pdm/issues/2978)) - Run pdm sync in "post-rewrite" stage of pre-commit ([#2994](https://github.com/pdm-project/pdm/issues/2994)) - `Project.get_dependencies()` now returns a list of `Requirement` instead of a mapping. The first argument of `Project.add_dependencies()` now accepts a list of `Requirement` instead of a mapping. The old usage will be kept working for a short period of time and will be removed in the future. ([#2995](https://github.com/pdm-project/pdm/issues/2995)) - Support locking for specific target, which is a combination of (python, platform, implementation) triple. Bump lock file version to `4.5.0`. Example usage: `pdm lock --platform=linux --python="==3.8.*" --implementation=cpython`. See the [docs](https://pdm-project.org/en/latest/usage/lock-targets) for more details. ([#2995](https://github.com/pdm-project/pdm/issues/2995)) - Rename `--reuse-env` to `--recreate` for `run` command, and reverse the behavior. ([#2999](https://github.com/pdm-project/pdm/issues/2999)) - PDM is now published with optional pinned dependencies using the pdm plugin [pdm-build-locked](https://pdm-build-locked.readthedocs.io/). To install pdm with its dependencies pinned to the versions it was tested with, run: ```bash pipx install pdm[locked] ``` To install optional dependency group copier: ```bash pipx install pdm[locked,copier-locked] ``` This feature is entirely optional. Installing pdm without the extra will work the same way as before this change. ([#3001](https://github.com/pdm-project/pdm/issues/3001)) - Added `--clean-unselected` alias for `--only-keep` ([#3007](https://github.com/pdm-project/pdm/issues/3007)) - Group options for update strategy and save strategy. ([#3016](https://github.com/pdm-project/pdm/issues/3016)) ### Bug Fixes - When locking dependencies that references the self project, the referenced groups should also be recorded in the lockfile. ([#2976](https://github.com/pdm-project/pdm/issues/2976)) - Retry failed installation jobs if they are run sequentially, such as for editable dependencies. ([#3005](https://github.com/pdm-project/pdm/issues/3005)) - Fix the local path issue when `-p` is passed to change the project root. ([#3009](https://github.com/pdm-project/pdm/issues/3009)) - Fix a bug that PDM can't install editable self package with non-isolated build in one go. ([#3018](https://github.com/pdm-project/pdm/issues/3018)) - Add context when parsing version failed. ([#3020](https://github.com/pdm-project/pdm/issues/3020)) - Fix a mistake in build env setup that will cause the `PATH` env var length to grow. ([#3022](https://github.com/pdm-project/pdm/issues/3022)) ### Removals and Deprecations - Remove the deprecation warning of `BaseCommand.__init__()` method. Now it doesn't take any arguments. ([#2995](https://github.com/pdm-project/pdm/issues/2995)) - `Provider.get_reuse_candidate()` method is deprecated in favor of `Provider.iter_reuse_candidates()`, to return an iterable of reuse candidates. ([#2995](https://github.com/pdm-project/pdm/issues/2995)) - `--no-markers` option in `pdm export` command becomes a no-op and is marked as deprecated, because it doesn't make sense anymore. ([#2995](https://github.com/pdm-project/pdm/issues/2995)) - `ignore_compatibility` parameter of `Project.get_provider()`/`Project.get_repository()`/`Environment.get_finder()` is deprecated. Pass in a `EnvSpec` via `env_spec` parameter instead. `requires_python` parameter of `pdm.resolver.core.resolve()` function is deprecated and has no effect. `cross_platform` parameter of `pdm.cli.actions.resolve_candidates_from_lockfile()` function is deprecated and has no effect. ([#2995](https://github.com/pdm-project/pdm/issues/2995)) ## Release v2.16.1 (2024-06-26) ### Bug Fixes - Fix new interface from pbs_installer regarding `build_dir` and best match auto-install strategy for `pdm use` (same as for `pdm python install --list`) ([#2943](https://github.com/pdm-project/pdm/issues/2943)) - Fix crash when pdm is used with `importlib-metadata` version 8.0. ([#2974](https://github.com/pdm-project/pdm/issues/2974)) ## Release v2.16.0 (2024-06-25) ### Features & Improvements - Add `--no-extras` to `pdm export` to strip extras from the requirements. Now the default behavior is to keep extras. ([#2519](https://github.com/pdm-project/pdm/issues/2519)) - Support PEP 723: running scripts with inline metadata in standalone environment with dependencies. ([#2924](https://github.com/pdm-project/pdm/issues/2924)) - `pdm use` and `pdm python install` now take `requires-python` into account (incl. from pyproject.toml) if python version not specified and `pdm use` provides auto installation by that. ([#2943](https://github.com/pdm-project/pdm/issues/2943)) - `--no-isolation` no longer installs `build-requires` nor dynamic build dependencies, to be consistent with `pip`. ([#2944](https://github.com/pdm-project/pdm/issues/2944)) - Add notifiers in CLI output when global project is being used. ([#2952](https://github.com/pdm-project/pdm/issues/2952)) - Use `tool.pdm.resolution` table when calculating the content hash of project file, previously only `overrides` table was used. This will change the hash already stored in the lockfile, so bump the lockfile version to `4.4.2`. ([#2956](https://github.com/pdm-project/pdm/issues/2956)) ### Bug Fixes - Add max retries on read timeout or bad connection. ([#2914](https://github.com/pdm-project/pdm/issues/2914)) - Don't update local files if they don't change. ([#2966](https://github.com/pdm-project/pdm/issues/2966)) - Don't list python versions that don't have any installation link for the current platform. ([#2970](https://github.com/pdm-project/pdm/issues/2970)) ### Documentation - Clarify the purposes of `pdm outdated` and `--unconstrained` option. ([#2965](https://github.com/pdm-project/pdm/issues/2965)) - Some clarifications on the interpreter selection and central package cache. ([#2967](https://github.com/pdm-project/pdm/issues/2967)) ## Release v2.15.4 (2024-05-30) ### Bug Fixes - Build wheel from sdist if available, to make sure sdist is built properly. This behavior is consistent with [pypa/build](https://pypi.org/project/build). ([#2843](https://github.com/pdm-project/pdm/issues/2843)) - Fix the issue of self-referencing extra dependencies failing to be resolved for local packages. ([#2898](https://github.com/pdm-project/pdm/issues/2898)) - Fix an issue of max recursion depth error when parsing a poetry project with circular dependencies on local packages. ([#2900](https://github.com/pdm-project/pdm/issues/2900)) - Fix a bug that VCS dependencies and `--self` don't work in the exported requirements.txt with hashes. ([#2908](https://github.com/pdm-project/pdm/issues/2908)) - Fix a cache miss when there exist built wheels for a given link. ([#2912](https://github.com/pdm-project/pdm/issues/2912)) - Don't try to store caches when `--no-cache` is given. ([#2913](https://github.com/pdm-project/pdm/issues/2913)) ## Release v2.15.3 (2024-05-20) ### Bug Fixes - Fixed pdm venv activate, to also work for windows. And added documentation on how to authenticate to Azure Artifacts ([#2851](https://github.com/pdm-project/pdm/issues/2851)) - Don't show unsupported formats in `pdm export`. ([#2877](https://github.com/pdm-project/pdm/issues/2877)) - Proxy (`HTTP_PROXY` env vars) settings are ignored for custom indexes. ([#2880](https://github.com/pdm-project/pdm/issues/2880)) - Fix the quoting of venv activate command for powershell. ([#2881](https://github.com/pdm-project/pdm/issues/2881)) - Raise an error if the package given by `pdm update` does not exist in the select dependency group but in other groups. ([#2885](https://github.com/pdm-project/pdm/issues/2885)) ## Release v2.15.2 (2024-05-08) ### Features & Improvements - Use `get_runner()` method to build the task runner in `run` command. `runner_cls` attribute is deprecated. ([#2872](https://github.com/pdm-project/pdm/issues/2872)) ### Bug Fixes - Expand `${PROJECT_ROOT}` in source URLs. ([#2846](https://github.com/pdm-project/pdm/issues/2846)) - Fix env and other options being inherited in nested composite scripts. ([#2849](https://github.com/pdm-project/pdm/issues/2849)) - Keep the `${PROJECT_ROOT}` variable in dependencies after running `pdm lock --update-reuse`. ([#2852](https://github.com/pdm-project/pdm/issues/2852)) - Make `direct_minimal_versions` work on newly added dependencies. ([#2853](https://github.com/pdm-project/pdm/issues/2853)) - Fix a syntax error in the zsh completion script. ([#2868](https://github.com/pdm-project/pdm/issues/2868)) ## Release v2.15.1 (2024-04-25) ### Bug Fixes - Disable check update in `zsh` completion script. ([#2838](https://github.com/pdm-project/pdm/issues/2838)) - Fixes cached packages metadata files (`.referrers`) collisions on `sync` when using a `venv` with `symlink` cache method. ([#2839](https://github.com/pdm-project/pdm/issues/2839)) ### Documentation - Build docs with object inventory to support cross references from Sphinx documentation projects. ([#2841](https://github.com/pdm-project/pdm/issues/2841)) ## Release v2.15.0 (2024-04-19) ### Features & Improvements - Packages format preferences can now be defined in the project `pyproject.toml` using the `no-binary`, `only-binary` and `prefer-binary` keys of the `tool.pdm.resolution` section. ([#2656](https://github.com/pdm-project/pdm/issues/2656)) ### Bug Fixes - Don't create project and virtualenv when running `pdm python install`. ([#2809](https://github.com/pdm-project/pdm/issues/2809)) - Clean up the python installation directory if a previous download was unsuccessful. ([#2810](https://github.com/pdm-project/pdm/issues/2810)) - Don't cache editable installations. ([#2816](https://github.com/pdm-project/pdm/issues/2816)) - Fix a bug that installing in-project plugins with editable local paths doesn't work. ([#2820](https://github.com/pdm-project/pdm/issues/2820)) - Don't create log directory until it's needed, to fix a PermissionError in docker environment. ([#2825](https://github.com/pdm-project/pdm/issues/2825)) - Fix recursive script detection on multiple invocations. ([#2829](https://github.com/pdm-project/pdm/issues/2829)) ## Release v2.14.0 (2024-04-12) ### Features & Improvements - Revert the package cache introduced in 2.13. Don't cache the decompressed contents of wheels unless being told so. ([#2803](https://github.com/pdm-project/pdm/issues/2803)) ### Bug Fixes - Fix inconsistent logging when `pdm use` a different python interpreter ([#2776](https://github.com/pdm-project/pdm/issues/2776)) - Fix PDM unable to find Python interpreters when `PDM_IGNORE_ACTIVE_VENV` is set ([#2779](https://github.com/pdm-project/pdm/issues/2779)) - Check verify_ssl when trusting each source. ([#2784](https://github.com/pdm-project/pdm/issues/2784)) - Fix name check for project itself in `pdm outdated` ([#2785](https://github.com/pdm-project/pdm/issues/2785)) - Fix a regression that proxy env vars are not respected. ([#2788](https://github.com/pdm-project/pdm/issues/2788)) - Fix an issue that venv provider can't be found when providers are explicitly configured. ([#2792](https://github.com/pdm-project/pdm/issues/2792)) - Fix a bug that `[tool.pdm.options]` are ignored if `-c/--config CONFIG` is given. ([#2793](https://github.com/pdm-project/pdm/issues/2793)) - Make `--without` respect groups in `dev-dependencies` ([#2799](https://github.com/pdm-project/pdm/issues/2799)) ## Release v2.13.3 (2024-04-08) ### Bug Fixes - Per-source configuration for ca-certs and client-cert. [#2754](https://github.com/pdm-project/pdm/issues/2754) - Remove all caches by removing individual cache types one by one. [#2757](https://github.com/pdm-project/pdm/issues/2757) - Use the default HTTP client when downloading the pythons, to use the certificates settings. [#2759](https://github.com/pdm-project/pdm/issues/2759) - Fix a race condition where pth files take effect when multiple packages are installed in parallel. [#2762](https://github.com/pdm-project/pdm/issues/2762) - Refuse to run recursive composite scripts. [#2766](https://github.com/pdm-project/pdm/issues/2766) ## Release v2.13.2 (2024-03-30) ### Bug Fixes - Fix errors when parsing poetry format that contains special characters in author name. Poetry-specific `parse_name_email` and `NAME_EMAIL_RE` moved from `pdm.formats.base` to `pdm.formats.poetry`. [#2665](https://github.com/pdm-project/pdm/issues/2665) - Fix a race condition in cached packages. When a cached package is being created it shouldn't be used for installation. [#2739](https://github.com/pdm-project/pdm/issues/2739) - Add back `PreparedCandidate.build()` for backward-compatibility. [#2747](https://github.com/pdm-project/pdm/issues/2747) ### Documentation - Fixed a small non-code typo in docs and provided better wording. [#2740](https://github.com/pdm-project/pdm/issues/2740) ## Release v2.13.1 (2024-03-29) ### Bug Fixes - Fix a bug that PDM couldn't find interpreters for global project. [#2726](https://github.com/pdm-project/pdm/issues/2726) - Make the cache package path shorter to solve the Windows path problem. [#2730](https://github.com/pdm-project/pdm/issues/2730) ### Documentation - Extract "Lock file" doc from "Manage Dependencies" doc. [#2725](https://github.com/pdm-project/pdm/issues/2725) ## Release v2.13.0 (2024-03-27) ### Features & Improvements - Add option to exclude group(s) when running ```pdm sync/install -G:all``` by adding flag ```--without group1,group2,...``` [#2258](https://github.com/pdm-project/pdm/issues/2258) - Default to log to user home and make logs directory configurable. [#2398](https://github.com/pdm-project/pdm/issues/2398) - Add an option `keep_going` to continue on errors for composite scripts and return the last failing exit code. [#2582](https://github.com/pdm-project/pdm/issues/2582) - Add an option `working_dir` for PDM's scripts to set the current working directory. [#2620](https://github.com/pdm-project/pdm/issues/2620) - Allow updating specific sub-dependencies (i.e., transitive dependencies) in the lock file. [#2628](https://github.com/pdm-project/pdm/issues/2628) - Add `--config-setting` option to `add/install/sync/update/remove/export` commands, the config settings dictionary will be shared by all packages. [#2636](https://github.com/pdm-project/pdm/issues/2636) - Cache the decompressed contents of wheels for faster access. [#2660](https://github.com/pdm-project/pdm/issues/2660) - Add configuration for timeout for network requests. [#2680](https://github.com/pdm-project/pdm/issues/2680) - Reuse the request session within the environment. [#2697](https://github.com/pdm-project/pdm/issues/2697) - Caches can be disabled by using the `--no-cache` option or setting the `PDM_NO_CACHE` environment variable. [#2702](https://github.com/pdm-project/pdm/issues/2702) - Switch to `httpx.Client` for HTTP requests, drop `requests` dependency. [#2709](https://github.com/pdm-project/pdm/issues/2709) - We have timemachine now! You can exclude packages published newer than a certain date via `pdm lock --exclude-newer=`, allowing reproduction of resolutions regardless of new package releases. [#2712](https://github.com/pdm-project/pdm/issues/2712) - Add command `pdm outdated` to check the outdated packages and list the latest versions. [#2718](https://github.com/pdm-project/pdm/issues/2718) - When `python.use_venv` is on, always try to create a virtualenv when using `pdm use` to switch the Python interpreter. [#2720](https://github.com/pdm-project/pdm/issues/2720) - Support installing Pythons from [python-build-standalone](https://github.com/indygreg/python-build-standalone). Add command group `pdm python` to manage Python installations. And `pdm use` can automatically install the Python interpreter if it's not found. [#2721](https://github.com/pdm-project/pdm/issues/2721) - Supports custom distribution files path via `-d/--dest` option for `pdm publish`. [#2723](https://github.com/pdm-project/pdm/issues/2723) ### Bug Fixes - Don't modify TOML tables that are not related to PDM. [#2666](https://github.com/pdm-project/pdm/issues/2666) - Made `--without` imply `--with :all`. [#2670](https://github.com/pdm-project/pdm/issues/2670) - Expand user path for `venv.location` and other path-like config values. [#2672](https://github.com/pdm-project/pdm/issues/2672) - Give a default version when it's missing in `pyproject.toml` when parsing candidate's metadata. [#2677](https://github.com/pdm-project/pdm/issues/2677) - Fix the issue that ANSI codes are shown in the output of `pdm --help` on Windows. [#2678](https://github.com/pdm-project/pdm/issues/2678) - Don't show empty configuration sections in `pdm config`. [#2683](https://github.com/pdm-project/pdm/issues/2683) ### Documentation - Document the difference between `[tool.pdm.scripts]` and `[project.scripts]` [#2121](https://github.com/pdm-project/pdm/issues/2121) ### Removals and Deprecations - Remove the support of `pth` cache method. And `symlink` cache method now behaves the same as `symlink_individual` cache method. [#2660](https://github.com/pdm-project/pdm/issues/2660) - Remove `pdm.models.environment` module deprecated before. Also remove the renamed members from `pdm.environments`. [#2710](https://github.com/pdm-project/pdm/issues/2710) ### Miscellany - Delete `setup.cfg`, move tool configurations under it to `pyproject.toml` [#2703](https://github.com/pdm-project/pdm/issues/2703) Release v2.12.4 (2024-02-26) ---------------------------- ### Features & Improvements - Use env PDM_NO_EDITABLE as the default value for --no-editable option. [#2613](https://github.com/pdm-project/pdm/issues/2613) ### Bug Fixes - Reset project.environment when importing from setup.py, to fix resolution error. [#2608](https://github.com/pdm-project/pdm/issues/2608) - Do not fetch package hashes when `--frozen-lockfile` is passed. [#2630](https://github.com/pdm-project/pdm/issues/2630) - Make sure non-venv interpreters are used by venv creator. [#2631](https://github.com/pdm-project/pdm/issues/2631) - Don't cause a hard failure if the local directory doesn't exist. [#2650](https://github.com/pdm-project/pdm/issues/2650) ### Documentation - Fix the default value for negative CLI flags. [#2642](https://github.com/pdm-project/pdm/issues/2642) - Auto-gen configuration reference documentation. [#2645](https://github.com/pdm-project/pdm/issues/2645) Release v2.12.3 (2024-02-01) ---------------------------- ### Bug Fixes - fix the package-type fixer won't update toml properly for "Nested Section Ordering Issue in TOML". [#2578](https://github.com/pdm-project/pdm/issues/2578) - Unable to force override a package if the package is required with extras. [#2586](https://github.com/pdm-project/pdm/issues/2586) - Failed to clone template repository if the URL contains the rev part. [#2597](https://github.com/pdm-project/pdm/issues/2597) - Handle legacy specifiers when converting from poetry project. [#2599](https://github.com/pdm-project/pdm/issues/2599) ### Documentation - Fix typo in template docs [#2588](https://github.com/pdm-project/pdm/issues/2588) Release v2.12.2 (2024-01-21) ---------------------------- ### Bug Fixes - Fix the auto fixer for package-type. [#2564](https://github.com/pdm-project/pdm/issues/2564) - Fix the wrong installation destination for header files when installing build requirements. [#2573](https://github.com/pdm-project/pdm/issues/2573) - Install header files into package namespace under `include` directory. [#2574](https://github.com/pdm-project/pdm/issues/2574) Release v2.12.1 (2024-01-17) ---------------------------- ### Bug Fixes - Hotfix: missing `identifier` attribute for package type fixer. [#2564](https://github.com/pdm-project/pdm/issues/2564) Release v2.12.0 (2024-01-17) ---------------------------- ### Features & Improvements - Allow excluding packages from the lockfile via `tool.pdm.resolution.excludes` setting, the dependencies will also be skipped. [#1316](https://github.com/pdm-project/pdm/issues/1316) - Rename `--no-lock` option to `--frozen-lockfile`. [#2496](https://github.com/pdm-project/pdm/issues/2496) - Add `--no-hashes` as the recommended option name in favor of `--without-hashes` for `pdm export` command. [#2497](https://github.com/pdm-project/pdm/issues/2497) - Add `--no-markers` to `export` command to exclude markers from the output. [#2497](https://github.com/pdm-project/pdm/issues/2497) - Allow initializing a project without extra project files, with a new builtin template "minimal". Run it with `pdm init minimal`. [#2543](https://github.com/pdm-project/pdm/issues/2543) - Change the warning category emitted by `deprecated_warning()` to `PDMDeprecationWarning`. [#2547](https://github.com/pdm-project/pdm/issues/2547) - Prereleases will be allowed if a prerelease version is pinned in the lockfile. This can be disabled by passing `--stable` option. [#2552](https://github.com/pdm-project/pdm/issues/2552) - Change `tracked_names` argument to keyword-only. Move `allow_prereleases` setting to `tool.pdm.resolution` table. [#2552](https://github.com/pdm-project/pdm/issues/2552) - Rename the `preferred_pins` argument of provider classes to `locked_candidates`, and deprecate the old name. [#2552](https://github.com/pdm-project/pdm/issues/2552) - Rename the `package-type` field under `tool.pdm` settings table to `distribution` to make it more clear. [#2564](https://github.com/pdm-project/pdm/issues/2564) ### Bug Fixes - `tool.pdm.resolution` settings won't be honored when installing dependencies into the build environment. [#1316](https://github.com/pdm-project/pdm/issues/1316) - Fixed pdm list output containing full license text in some cases [#2538](https://github.com/pdm-project/pdm/issues/2538) - Fix the environment variable substitution for `cmd` scripts. [#2542](https://github.com/pdm-project/pdm/issues/2542) - Allow normal extension modules in wheel tags when the python is debug build. [#2548](https://github.com/pdm-project/pdm/issues/2548) - Don't use pypi.org when pypi.url is set. [#2560](https://github.com/pdm-project/pdm/issues/2560) ### Removals and Deprecations - Remove deprecated methods from `Project`. Remove deprecated helper functions from `actions.py`. [#2547](https://github.com/pdm-project/pdm/issues/2547) Release v2.11.2 (2024-01-02) ---------------------------- ### Bug Fixes - Fix a KeyError raised when resolving a URL dependency without package name given. [#2488](https://github.com/pdm-project/pdm/issues/2488) - `pdm update --update-eager` can hit InconsistentCandidate error when dependency is included both through default dependencies and extra. [#2495](https://github.com/pdm-project/pdm/issues/2495) - `pdm install` should not warn when overwriting its own symlinks on `install`/`update`. [#2502](https://github.com/pdm-project/pdm/issues/2502) - Fix a bug that candidates without local version are rejected when the local version is pinned. [#2507](https://github.com/pdm-project/pdm/issues/2507) ### Documentation - Add maturin as a compatible build backend in the docs. [#2510](https://github.com/pdm-project/pdm/issues/2510) Release v2.11.1 (2023-12-14) ---------------------------- ### Bug Fixes - Update candidate names before resolving markers, to fix a KeyError when the requirement is not named. [#2488](https://github.com/pdm-project/pdm/issues/2488) - Fix a KeyError when resolving packages that have parents that are no longer needed. [#2489](https://github.com/pdm-project/pdm/issues/2489) Release v2.11.0 (2023-12-14) ---------------------------- ### Features & Improvements - Officially drop the support for Python 3.7. - Allow exporting current project as editable dependency with `pdm export`. [#1910](https://github.com/pdm-project/pdm/issues/1910) - Improve the lockfile compatibility checking by using 3-digit version numbers. This can distinguish forward-compatibility and backward-compatibility. [#2164](https://github.com/pdm-project/pdm/issues/2164) - Add `--skip-existing` to `pdm publish` to ignore the uploading error if the package already exists. [#2362](https://github.com/pdm-project/pdm/issues/2362) - Use `==major.minor.*` as default requires python for application projects. [#2382](https://github.com/pdm-project/pdm/issues/2382) - We now use the `package-type` field in the `tool.pdm` table to differentiate between library and application projects. [#2394](https://github.com/pdm-project/pdm/issues/2394) - Add support for {pdm} placeholder in script definitions to call the same PDM entrypoint [#2408](https://github.com/pdm-project/pdm/issues/2408) - When exporting requirements, record the environment markers from all parents for each requirement. This allows the exported requirements to work on different platforms and Python versions. [#2418](https://github.com/pdm-project/pdm/issues/2418) - `pdm lock` now supports `--update-reuse` option to keep the pinned versions in the lockfile if possible. [#2419](https://github.com/pdm-project/pdm/issues/2419) - Introduce a new lock strategy `inherit_metadata` to inherit and merge markers from parent requirements. This is enabled by default when creating a new lockfile. [#2421](https://github.com/pdm-project/pdm/issues/2421) - New cache methods: `symlink_individual` for creating a symlink for each individual package file and `hardlink` for creating hardlinks. [#2425](https://github.com/pdm-project/pdm/issues/2425) - Add "pdm sync" pre-commit hook [#2474](https://github.com/pdm-project/pdm/issues/2474) - New update strategy: `reuse-installed`. When this strategy is enabled, PDM will try to reuse the versions already installed in the environment, even if the package names are given in the command line following `add` or `update`. This strategy is supported by `add`, `update` and `lock` commands. [#2479](https://github.com/pdm-project/pdm/issues/2479) - Show subcommand's help info when passing unrecognized arguments. [#2480](https://github.com/pdm-project/pdm/issues/2480) - add `PDM_CACHE_DIR` environment variable to configure cache directory location. [#2485](https://github.com/pdm-project/pdm/issues/2485) ### Bug Fixes - Use the same order of Python interpreters as interactive mode in `pdm init -n`. [#2436](https://github.com/pdm-project/pdm/issues/2436) - `pdm init` now implies `--lib` if `--backend` is passed. [#2437](https://github.com/pdm-project/pdm/issues/2437) - Fix a bug that link collection ignores package-index-binding. [#2442](https://github.com/pdm-project/pdm/issues/2442) - Fix the wrong installation candidates for different architectures on Windows. [#2464](https://github.com/pdm-project/pdm/issues/2464) - Fix installing PEP 561 stub-only packages with `install.cache_method = "symlink"`. [#2466](https://github.com/pdm-project/pdm/issues/2466) - Fix a `KeyError` raised by `pdm update --unconstrained` when the project itself is listed as a dependency. [#2483](https://github.com/pdm-project/pdm/issues/2483) Release v2.10.4 (2023-11-24) ---------------------------- ### Bug Fixes - Do not detect as requirements.txt if the file is a python script. [#2416](https://github.com/pdm-project/pdm/issues/2416) - Provide information of the original line when parsing requirement fails. [#2417](https://github.com/pdm-project/pdm/issues/2417) - Resolve `-r` requirements paths relative to the requirement file they are specified in [#2422](https://github.com/pdm-project/pdm/issues/2422) - Updating package now overwrites the old files instead of removing before installing. [#2423](https://github.com/pdm-project/pdm/issues/2423) Release v2.10.3 (2023-11-16) ---------------------------- ### Bug Fixes - Create virtualenv for conda base Python. [#2409](https://github.com/pdm-project/pdm/issues/2409) Release v2.10.2 (2023-11-16) ---------------------------- ### Features & Improvements - Log the response text when `pdm publish` fails with HTTP error. [#2400](https://github.com/pdm-project/pdm/issues/2400) ### Bug Fixes - Improve the error message when a specific package can't be found in the lockfile. [#2358](https://github.com/pdm-project/pdm/issues/2358) - prevent wrong project name (including space and illegal characters) [#2360](https://github.com/pdm-project/pdm/issues/2360) - Fix a bug that PDM cannot detect namespace packages correctly when creating symlinks. The package's `__init__.py` contains an unusual line. [#2378](https://github.com/pdm-project/pdm/issues/2378) - Fix template files created by `pdm init` being read-only when copied from a read-only PDM installation. [#2379](https://github.com/pdm-project/pdm/issues/2379) - Don't reset the build backend when asking for import. [#2388](https://github.com/pdm-project/pdm/issues/2388) - Never wrap the output of the `export` command. [#2390](https://github.com/pdm-project/pdm/issues/2390) - Forbid global project in conda base environment, since it may remove conda-managed packages. [#2409](https://github.com/pdm-project/pdm/issues/2409) Release v2.10.1 (2023-11-07) ---------------------------- ### Bug Fixes - Fix a bug preventing ctrl-c from interrupting program execution on 2nd invocation when using "pdm run" (Windows only). [#2292](https://github.com/pdm-project/pdm/issues/2292) - Fix list index out of range when build error message is empty. [#2337](https://github.com/pdm-project/pdm/issues/2337) - Fix find_link sources being exported as `--extra--index-url` [#2342](https://github.com/pdm-project/pdm/issues/2342) - Fix an installation failure when install.cache = true. [#2355](https://github.com/pdm-project/pdm/issues/2355) - Fix a resolution issue that extra dependencies are not resolved when the bare dependency has more specific version constraint. [#2369](https://github.com/pdm-project/pdm/issues/2369) ### Documentation - Set up a chatbot powered by LLM on the doc page. [#2365](https://github.com/pdm-project/pdm/issues/2365) Release v2.10.0 (2023-10-26) ---------------------------- ### Features & Improvements - Allow binding packages to specific sources with `include_packages` and `exclude_packages` config under `tool.pdm.source` table. [#1645](https://github.com/pdm-project/pdm/issues/1645) - Show warnings when a package is rejected by the resolve because of uncovered `requires-python` range. And provide a way to ignore them per-package. [#2304](https://github.com/pdm-project/pdm/issues/2304) - Add `-q/--quiet` option to suppress some warnings printed to the console. This option is mutually exclusive with `-v/--verbose`. [#2304](https://github.com/pdm-project/pdm/issues/2304) - Introduce a new `--strategy/-S` option for `lock` command, to specify one or more strategy flags for resolving dependencies. `--static-urls` and `--no-cross-platform` are deprecated at the same time. [#2310](https://github.com/pdm-project/pdm/issues/2310) - Add lock option to resolve direct dependencies to the minimal versions available. [#2310](https://github.com/pdm-project/pdm/issues/2310) - Report the progress of download and unpacking when installing packages. [#2328](https://github.com/pdm-project/pdm/issues/2328) ### Bug Fixes - Change the venv backend clean function `pdm.cli.commands.venv.backend.Backend._ensure_clean` to empty the `.venv` folder instead of deleting it. [#2282](https://github.com/pdm-project/pdm/issues/2282) - Fix a bug that dependency groups from Poetry 1.2+ do not migrate properly to PDM. [#2285](https://github.com/pdm-project/pdm/issues/2285) - Fix a bug that build requirements are installed into wrong location when using `--venv` option. [#2314](https://github.com/pdm-project/pdm/issues/2314) - Fix a bug that global repository setting results in TypeError . [#2330](https://github.com/pdm-project/pdm/issues/2330) - Fix a credentials error when working with two indices on the same host [#2333](https://github.com/pdm-project/pdm/issues/2333) ### Miscellany - Officially supports python3.12 now. [#2301](https://github.com/pdm-project/pdm/issues/2301) Release v2.9.3 (2023-09-25) --------------------------- ### Bug Fixes - Revert the changes to the behavior of installing self, introduced in #2162. Self package won't be installed when `--no-default` is requested. [#2230](https://github.com/pdm-project/pdm/issues/2230) - Reject the candidate if it contains invalid metadata, to avoid a crash in the process of resolution. [#2261](https://github.com/pdm-project/pdm/issues/2261) ### Documentation - Clarify what `--no-isolated` does. [#2071](https://github.com/pdm-project/pdm/issues/2071) Release v2.9.2 (2023-09-12) --------------------------- ### Features & Improvements - Fix an issue that `--no-lock` option doesn't work as expected. Also support `--no-lock` option for `add`, `remove` and `update` commands. [#2245](https://github.com/pdm-project/pdm/issues/2245) ### Bug Fixes - Use `findpython` to find pythons with the spec given by the user. [#2225](https://github.com/pdm-project/pdm/issues/2225) - Use UTF-8 to read pyvenv.cfg. [#2227](https://github.com/pdm-project/pdm/issues/2227) - On Windows, try looking for the `virtualenv` `python.exe` binary under `bin/` as well as `Scripts/` and the `virtualenv`/`conda` root. [#2236](https://github.com/pdm-project/pdm/issues/2236) - Write relocatable dependency URLs with `${PROJECT_ROOT}` variable in the lockfile. [#2240](https://github.com/pdm-project/pdm/issues/2240) Release v2.9.1 (2023-09-03) --------------------------- ### Features & Improvements - Support convert setup.cfg without existing setup.py. [#2222](https://github.com/pdm-project/pdm/issues/2222) ### Bug Fixes - `pdm run` should only find local file if the command starts with `./`. [#2221](https://github.com/pdm-project/pdm/issues/2221) Release v2.9.0 (2023-08-31) --------------------------- ### Features & Improvements - Add an `--overwrite` option to `pdm init` to overwrite existing files(default False). [#2163](https://github.com/pdm-project/pdm/issues/2163) - Support passing filter patterns as positional arguments to `pdm list` command. Add `--tree` as an alias and preferred name of `--graph` option. [#2165](https://github.com/pdm-project/pdm/issues/2165) - Switch to truststore by default. [#2195](https://github.com/pdm-project/pdm/issues/2195) - Consider packages as installed if the venv includes them from the system-site-packages. [#2216](https://github.com/pdm-project/pdm/issues/2216) - Allow `pdm run` to run a script with the relative or absolute path. [#2217](https://github.com/pdm-project/pdm/issues/2217) ### Bug Fixes - Fix a bug that removing dev dependency uninstalls the project as well. [#2150](https://github.com/pdm-project/pdm/issues/2150) - Fix a bug that `@ file://` dependencies can not be updated. [#2169](https://github.com/pdm-project/pdm/issues/2169) - Fix a bug that dependencies requested out of the range of `requires-python` cause PDM to crash. [#2175](https://github.com/pdm-project/pdm/issues/2175) - Fix the compatibility issue with copier 8.0+. [#2177](https://github.com/pdm-project/pdm/issues/2177) - Makes `comparable_version("1.2.3+local1") == Version("1.2.3")`. [#2182](https://github.com/pdm-project/pdm/issues/2182) - Default behavior for pdm venv activate when shell detection fails. [#2187](https://github.com/pdm-project/pdm/issues/2187) - Handle parsing errors when converting from poetry-style metadata. [#2203](https://github.com/pdm-project/pdm/issues/2203) - Don't copy .pyc files from the template directory. [#2213](https://github.com/pdm-project/pdm/issues/2213) ### Removals and Deprecations - Remove the legacy build backend `pdm-pep517`. [#2167](https://github.com/pdm-project/pdm/issues/2167) Release v2.8.2 (2023-07-31) --------------------------- ### Features & Improvements - Allow setting username and password in URL for publish command [#2140](https://github.com/pdm-project/pdm/issues/2140) ### Bug Fixes - Use UTF-8 encoding when writing `sitecustomize.py`. [#2139](https://github.com/pdm-project/pdm/issues/2139) Release v2.8.1 (2023-07-26) --------------------------- ### Features & Improvements - Add `keyring`, `copier`, `cookiecutter`, `template`, `truststore` dependency groups. [#2109](https://github.com/pdm-project/pdm/issues/2109) - Ignore wheels for python versions not in range. [#2113](https://github.com/pdm-project/pdm/issues/2113) - Read default value from env var `PDM_PROJECT` for `-p/--project` option. [#2126](https://github.com/pdm-project/pdm/issues/2126) ### Bug Fixes - Fix the comparison of the candidate keys in the lockfile. [#2120](https://github.com/pdm-project/pdm/issues/2120) - Don't update `pyproject.toml` if both `--unconstrained` and `--dry-run` are passed to `pdm update`. [#2125](https://github.com/pdm-project/pdm/issues/2125) - Overwrite the `build-system` table when importing from other package manager. [#2126](https://github.com/pdm-project/pdm/issues/2126) - Skip sources with empty URL when merging sources. [#2130](https://github.com/pdm-project/pdm/issues/2130) - Fix the invalid requirement converted from poetry metadata. [#2133](https://github.com/pdm-project/pdm/issues/2133) ### Dependencies - Update `unearth` to 0.10.0 [#2113](https://github.com/pdm-project/pdm/issues/2113) Release v2.8.0 (2023-07-15) --------------------------- ### Features & Improvements - Support target python with other architectures. [#2078](https://github.com/pdm-project/pdm/issues/2078) - Display the help information when running pdm directly. [#2081](https://github.com/pdm-project/pdm/issues/2081) - Allow to change the python providers from the config. Support finding pythons from Rye installation location with the new findpython. [#2099](https://github.com/pdm-project/pdm/issues/2099) - Option to save static URLs in the lockfile. By default only filenames are saved. [#2101](https://github.com/pdm-project/pdm/issues/2101) ### Bug Fixes - Fix a bug that egg-info directories are not removed completely, leading to incomplete distribution. [#2027](https://github.com/pdm-project/pdm/issues/2027) - Skip distributions with wrong package meta information and duplicate path. [#2075](https://github.com/pdm-project/pdm/issues/2075) - Avoid mistakenly passing command-line arguments while testing. [#2083](https://github.com/pdm-project/pdm/issues/2083) - Fix a bug that lockfile groups are overwritten when running locking in a preceding step of `pdm install`. [#2086](https://github.com/pdm-project/pdm/issues/2086) - Tolerate and actually ignore the local versions in version specifiers. [#2102](https://github.com/pdm-project/pdm/issues/2102) - Fix a bug that shared cache cannot support overlapping namespace packages. [#2105](https://github.com/pdm-project/pdm/issues/2105) ### Documentation - Add notes about using custom venv path. [#2096](https://github.com/pdm-project/pdm/issues/2096) Release v2.8.0a2 (2023-06-30) ----------------------------- ### Bug Fixes - Fix a bug that dependencies can't be updated when the table is separated by another table. [#2056](https://github.com/pdm-project/pdm/issues/2056) - Fix a bug that `*_lock` hooks are always emitted with dry_run=True in `pdm update`. [#2060](https://github.com/pdm-project/pdm/issues/2060) - Fix a bug that `pdm install --plugins` can't install self. [#2062](https://github.com/pdm-project/pdm/issues/2062) - Fix a cache collision between named requirements and url requirements. [#2064](https://github.com/pdm-project/pdm/issues/2064) Release v2.8.0a1 (2023-06-27) ----------------------------- ### Features & Improvements - Add support for `cookiecutter` and `copier` as project generator. [#2059](https://github.com/pdm-project/pdm/issues/2059) Release v2.8.0a0 (2023-06-27) ----------------------------- ### Features & Improvements - `pdm init` now accepts a template argument to initialize project from a built-in or Git template. [#2053](https://github.com/pdm-project/pdm/issues/2053) - Replace the `DeprecationWarning` with `FutureWarning` for better exposure. [#2012](https://github.com/pdm-project/pdm/issues/2012) - Serve `install-pdm.py` and its checksum file on the docs site. [#2026](https://github.com/pdm-project/pdm/issues/2026) - Add new option `--edit/-e` to `pdm config` to edit the config file in default editor. [#2028](https://github.com/pdm-project/pdm/issues/2028) - Add `--project` option to `pdm venv` to support another path as the project root. [#2042](https://github.com/pdm-project/pdm/issues/2042) - Add support for using `truststore` as the SSL backend. This only works on Python 3.10 or newer. [#2049](https://github.com/pdm-project/pdm/issues/2049) ### Bug Fixes - Fix the breaking change by adding the functions back to the old location with deprecation warnings. [#2013](https://github.com/pdm-project/pdm/issues/2013) - Fix the duplicate entries in the output of `pdm self list`. [#2018](https://github.com/pdm-project/pdm/issues/2018) - Disable hashes caching for local files. [#2019](https://github.com/pdm-project/pdm/issues/2019) - Populate the `url` field when converting requirements from a Pipfile-style file requirement. [#2032](https://github.com/pdm-project/pdm/issues/2032) - Fix a bug that empty source tables in configuration files causes errors when running pdm commands. [#2034](https://github.com/pdm-project/pdm/issues/2034) - Fix a resolution conflict caused by requested yanked version also in other transitive dependencies. [#2038](https://github.com/pdm-project/pdm/issues/2038) - Fix a bug that binary executables are corrupted when replacing shebangs. [#2045](https://github.com/pdm-project/pdm/issues/2045) - Do not normalize the package name when uploading to PyPI. [#2057](https://github.com/pdm-project/pdm/issues/2057) Release v2.7.4 (2023-06-13) --------------------------- No significant changes. Release v2.7.3 (2023-06-13) --------------------------- ### Bug Fixes - Fix the warning of extras not found due to extra names not normalized. [#2006](https://github.com/pdm-project/pdm/issues/2006) - Pop up a warning when the deprecated `parser` argument is passed to `BaseCommand.__init__()` method. [#2007](https://github.com/pdm-project/pdm/issues/2007) - Fix a bug that merging settings with AoTs causing a failure. [#2011](https://github.com/pdm-project/pdm/issues/2011) Release v2.7.2 (2023-06-12) --------------------------- ### Features & Improvements - Add option to expand environment variables when exporting requirements. [#1997](https://github.com/pdm-project/pdm/issues/1997) ### Bug Fixes - Case-insensitive sorting in `pdm list`. [#1973](https://github.com/pdm-project/pdm/issues/1973) - Make a compatible cache reader to read the old cache files. [#1981](https://github.com/pdm-project/pdm/issues/1981) - Fix a bug that `pdm init -n` doesn't respect the `--python` option. [#1984](https://github.com/pdm-project/pdm/issues/1984) - Do not use the deprecated nested argument groups. [#1988](https://github.com/pdm-project/pdm/issues/1988) - Fix an error parsing `setup.py` if it prints something to stdout. [#1995](https://github.com/pdm-project/pdm/issues/1995) - Exclude yanked versions when running `install-pdm.py`. [#1996](https://github.com/pdm-project/pdm/issues/1996) Release v2.7.1 (2023-06-06) --------------------------- ### Features & Improvements - Switch HTTP data cache to use a split body setup, where the actual body contents are not written to disk unless changed. Previously, any changed headers would write the whole body to disk again. [#1971](https://github.com/pdm-project/pdm/issues/1971) - Show the specific install commands for different installations when checking update. This was removed before. [#1972](https://github.com/pdm-project/pdm/issues/1972) ### Bug Fixes - PDM ignores env vars `PDM_PYPI_USERNAME` and `PDM_PYPI_PASSWORD` when there are no defaults in config. [#1961](https://github.com/pdm-project/pdm/issues/1961) - Guess the project name from VCS url if it is missing when importing from requirements.txt. [#1970](https://github.com/pdm-project/pdm/issues/1970) - Correctly read the config from environment variables. [#1977](https://github.com/pdm-project/pdm/issues/1977) Release v2.7.0 (2023-05-29) --------------------------- ### Features & Improvements - When keyring is available, either by importing or by CLI, the credentials of repositories and PyPI indexes will be saved into it. [#1908](https://github.com/pdm-project/pdm/issues/1908) - Add support for reading metadata from simple index directly. [#1919](https://github.com/pdm-project/pdm/issues/1919) - Add a configuration to specify constant command arguments for every pdm invocation. [#1923](https://github.com/pdm-project/pdm/issues/1923) - Add ability to skip SSL verification for publish repositories via `repository.custom.verify_ssl` config option as well as new command line argument of `publish` command. [#1928](https://github.com/pdm-project/pdm/issues/1928) - Use lazy import to reduce the startup time of the CLI. [#1929](https://github.com/pdm-project/pdm/issues/1929) - Add the local plugin scripts to `PATH` env var. [#1944](https://github.com/pdm-project/pdm/issues/1944) ### Bug Fixes - Don't use install cache when installing build requirements to avoid race condition. [#1869](https://github.com/pdm-project/pdm/issues/1869) - Fix a number of `ResourceWarning`s when running the test suite with warnings enabled. [#1915](https://github.com/pdm-project/pdm/issues/1915) - Fix a bug that dev-dependencies group gets updated with the optional dependencies, causing the hash mismatch. [#1916](https://github.com/pdm-project/pdm/issues/1916) - Fix format conversion error from Poetry when `tool.poetry.build` doesn't exist. [#1935](https://github.com/pdm-project/pdm/issues/1935) - Add timeout when fetching .gitignore from GitHub. [#1937](https://github.com/pdm-project/pdm/issues/1937) - Keep the variables in the URL credentials when exporting. [#1939](https://github.com/pdm-project/pdm/issues/1939) - Convert to boolean when setting verify_ssl for custom indexes. [#1945](https://github.com/pdm-project/pdm/issues/1945) - `pdm import` clobbers `build-system.requires` value in `pyproject.toml`. [#1948](https://github.com/pdm-project/pdm/issues/1948) ### Documentation - Update publish.md to use run instead of runs to match GitHub Actions steps documentation [#1936](https://github.com/pdm-project/pdm/issues/1936) - Update advanced.md to use `pdm sync` instead of `pdm install --no-lock`. [#1947](https://github.com/pdm-project/pdm/issues/1947) Release v2.6.1 (2023-05-10) --------------------------- ### Bug Fixes - Fix the error when publishing using trusted publisher. [#1868](https://github.com/pdm-project/pdm/issues/1868) - Fix a bug that `PATH` env var isn't set correctly when running under non-isolation mode. [#1904](https://github.com/pdm-project/pdm/issues/1904) Release v2.6.0 (2023-05-09) --------------------------- ### Features & Improvements - Install project-level plugins from project config, with `tool.pdm.plugins` setting. [#1461](https://github.com/pdm-project/pdm/issues/1461) - Added a `--json` flag to both `run` and `info` command allowing to dump scripts and infos as JSON. [#1854](https://github.com/pdm-project/pdm/issues/1854) - Consider tasks with a name starting by an underscore (`_`) as internal tasks and hide them from the listing. [#1855](https://github.com/pdm-project/pdm/issues/1855) - When running `pdm init -n`(non-interactive mode), a venv will be created by default. Previously, the selected Python will be used under PEP 582 mode. [#1862](https://github.com/pdm-project/pdm/issues/1862) - Support [Trusted Publisher](https://docs.pypi.org/trusted-publishers/). [#1868](https://github.com/pdm-project/pdm/issues/1868) - Add an ephemeral wheel cache in process for wheels built from non-static revision sources. [#1885](https://github.com/pdm-project/pdm/issues/1885) - Allow self-referencing groups in dev-dependencies. [#1890](https://github.com/pdm-project/pdm/issues/1890) - Add an option `--no-cross-platform` to `pdm lock` to create a non-cross-platform lockfile. [#1898](https://github.com/pdm-project/pdm/issues/1898) ### Bug Fixes - Fix brackets in `--venv` option descriptions in zsh completion script. [#1847](https://github.com/pdm-project/pdm/issues/1847) - The resolver doesn't take into account of the requirements for both bare `package` and `package[extra]`. [#1851](https://github.com/pdm-project/pdm/issues/1851) - Default pypi source does not use configured pypi.password, but "" instead. [#1856](https://github.com/pdm-project/pdm/issues/1856) - Detect Python interpreters under the root of virtual environments. [#1866](https://github.com/pdm-project/pdm/issues/1866) - Fix a race condition when the builder is creating a new build directory. [#1869](https://github.com/pdm-project/pdm/issues/1869) - Raise `FileNotFoundError` if the requirement path is not found. [#1875](https://github.com/pdm-project/pdm/issues/1875) - Fix a bug that the self package isn't uninstallable. [#1901](https://github.com/pdm-project/pdm/issues/1901) Release v2.5.6 (2023-05-07) --------------------------- ### Bug Fixes - Fix a double reading issue due to cachecontrol not compatible with urllib3 2.0. [#1894](https://github.com/pdm-project/pdm/issues/1894) Release v2.5.5 (2023-05-05) --------------------------- No significant changes. Release v2.5.4 (2023-05-05) --------------------------- ### Bug Fixes - Pin the urllib3 to `<2.0` to avoid incompatibility with `cachecontrol`. [#1886](https://github.com/pdm-project/pdm/issues/1886) Release v2.5.3 (2023-04-19) --------------------------- ### Bug Fixes - Fix the wrong argument validation for update command, where packages given with group option should be allowed. [#1836](https://github.com/pdm-project/pdm/issues/1836) ### Documentation - Update `markdown-exec` to `1.5.0` for rendering TOC in CLI reference page. [#1836](https://github.com/pdm-project/pdm/issues/1836) - Remove advertizing of PEP-582 from the feature highlights. Improve the anchor links for CLI reference. [#1840](https://github.com/pdm-project/pdm/issues/1840) Release v2.5.2 (2023-04-10) --------------------------- ### Bug Fixes - Regression(#1710): Don't crash when trying to update the shebang in a binary script [#1827](https://github.com/pdm-project/pdm/issues/1827) - Rename the env var `PDM_USE_VENV` as `PDM_IN_VENV` for `--venv` flag as it mistakenly override another existing env var. [#1829](https://github.com/pdm-project/pdm/issues/1829) Release v2.5.1 (2023-04-09) --------------------------- ### Bug Fixes - Fix a bug that `pdm --pep582` raises an argument error. [#1823](https://github.com/pdm-project/pdm/issues/1823) Release v2.5.0 (2023-04-09) --------------------------- ### Features & Improvements - When `resolution.respect-source-order` is enabled, sources are lazily evaluated. This means that if a match is found on the first source, the remaining sources will not be requested. [#1509](https://github.com/pdm-project/pdm/issues/1509) - New option `--venv ` to run a command in the virtual environment with the given name. [#1705](https://github.com/pdm-project/pdm/issues/1705) - Allow to prefer binary distributions when locking and installing packages, via `PDM_PREFER_BINARY` environment variable. [#1817](https://github.com/pdm-project/pdm/issues/1817) ### Bug Fixes - Do not validate selected groups against the locked grouped when running `pdm lock`. [#1796](https://github.com/pdm-project/pdm/issues/1796) - Avoid duplicate .pdm-python in .gitignore. [#1800](https://github.com/pdm-project/pdm/issues/1800) - Fix a backwards compatibility issue by adding back the `environment.is_global` property. [#1814](https://github.com/pdm-project/pdm/issues/1814) - Fix a resolution conflict when a relative path requirement resolves to the same path as another file requirement with absolute path. [#1822](https://github.com/pdm-project/pdm/issues/1822) - Fix an error when running `pdm init -p ` if the target directory is not created yet. [#1822](https://github.com/pdm-project/pdm/issues/1822) Release v2.5.0b0 (2023-03-29) ----------------------------- ### Breaking Changes - Switch the default build backend to `pdm-backend`. [#1684](https://github.com/pdm-project/pdm/issues/1684) - Only lock selected groups into the lockfile. Modify other commands to honor the groups included in the lockfile. [#1704](https://github.com/pdm-project/pdm/issues/1704) - Move the project python path to its own file, and rename the project config file as `pdm.toml` which can be committed to the VCS. [#1742](https://github.com/pdm-project/pdm/issues/1742) - Refactor the environment package. `Environment` is renamed to `PythonLocalEnvironment` and `GlobalEnvironment` is renamed to `PythonEnvironment`. Move `pdm.models.environment` module to `pdm.environments` package. [#1791](https://github.com/pdm-project/pdm/issues/1791) ### Features & Improvements - Add option to fail on the first install error. [#1614](https://github.com/pdm-project/pdm/issues/1614) - Upgrade `unearth` to 0.8 to allow calling keyring from CLI. [#1653](https://github.com/pdm-project/pdm/issues/1653) - Merge the index parameters from different configuration files. [#1667](https://github.com/pdm-project/pdm/issues/1667) - Add new options to `venv` command to show the path or the python interpreter for a managed venv. [#1680](https://github.com/pdm-project/pdm/issues/1680) - Write the groups of resolved dependencies to the metadata table in lockfile. [#1692](https://github.com/pdm-project/pdm/issues/1692) - Introduce `--lib` option to `init` command to create a library project without prompting. [#1708](https://github.com/pdm-project/pdm/issues/1708) - New command: `pdm fix` to migrate to the new PDM features. Add a hint when invoking PDM commands. [#1743](https://github.com/pdm-project/pdm/issues/1743) - Include `.pdm-python` in project root `.gitignore` when running `pdm init`. [#1749](https://github.com/pdm-project/pdm/issues/1749) - Allow to ignore the activated venv with `PDM_IGNORE_ACTIVE_VENV` env var. [#1782](https://github.com/pdm-project/pdm/issues/1782) - Add a signal `pre_invoke` to emit before any command is invoked. [#1792](https://github.com/pdm-project/pdm/issues/1792) ### Bug Fixes - Fix a bug that install warning prints to terminal under non-verbose mode. [#1635](https://github.com/pdm-project/pdm/issues/1635) - Fix the random failure of `pdm export` due to non-deterministic order of group iteration. [#1786](https://github.com/pdm-project/pdm/issues/1786) - Show the actual version when running `pdm show --version` [#1788](https://github.com/pdm-project/pdm/issues/1788) ### Documentation - Restructure the documentation. [#1687](https://github.com/pdm-project/pdm/issues/1687) ### Dependencies - Update `installer` to `0.7.0` and emit a warning if the RECORD validation fails. [#1784](https://github.com/pdm-project/pdm/issues/1784) Release v2.4.9 (2023-03-16) --------------------------- ### Bug Fixes - Fix a bug of synchronization of not considering the revision of VCS requirement in comparison. [#1762](https://github.com/pdm-project/pdm/issues/1762) - Improve the error message when parsing an invalid requirement string. [#1765](https://github.com/pdm-project/pdm/issues/1765) - Fix a bug that `pdm export` output doesn't include the extras of the dependencies. [#1767](https://github.com/pdm-project/pdm/issues/1767) Release v2.4.8 (2023-03-09) --------------------------- ### Bug Fixes - Fix the resolution order to prefer the packages causing the conflict. This can make the resolution reach a solution faster. [#1752](https://github.com/pdm-project/pdm/issues/1752) - Fix a bug that embedded credentials in URL are not respected for the default source. [#1757](https://github.com/pdm-project/pdm/issues/1757) Release v2.4.7 (2023-03-02) --------------------------- ### Bug Fixes - Abort if lockfile isn't generated when executing `pdm export`. [#1730](https://github.com/pdm-project/pdm/issues/1730) - Ignore `venv.prompt` configuration when using `conda` as the backend. [#1734](https://github.com/pdm-project/pdm/issues/1734) - Fix a bug of finding local packages in the parent folder when it exists in the current folder. [#1736](https://github.com/pdm-project/pdm/issues/1736) - Ensure UTF-8 encoding when generating README.md. [#1739](https://github.com/pdm-project/pdm/issues/1739) - Fix a bug of show command not showing metadata of the current project. [#1740](https://github.com/pdm-project/pdm/issues/1740) - Replace `.` with `-` when normalizing package name. [#1745](https://github.com/pdm-project/pdm/issues/1745) ### Documentation - Support using `pdm venv activate` without specifying `env_name` to activate in project venv created by conda [#1735](https://github.com/pdm-project/pdm/issues/1735) Release v2.4.6 (2023-02-20) --------------------------- ### Bug Fixes - Fix a resolution failure when the project has cascading relative path dependencies. [#1702](https://github.com/pdm-project/pdm/issues/1702) - Don't crash when trying to update the shebang in a binary script. [#1709](https://github.com/pdm-project/pdm/issues/1709) - Handle the legacy specifiers that is unable to parse with packaging>22.0. [#1719](https://github.com/pdm-project/pdm/issues/1719) - Fix the setup.py parser to ignore the expressions unable to parse as a string. This is safe for initializing a requirement. [#1720](https://github.com/pdm-project/pdm/issues/1720) - Fix a bug converting from flit metadata when the source file can't be found. [#1726](https://github.com/pdm-project/pdm/issues/1726) ### Documentation - Add config example for Emacs using eglot + pyright [#1721](https://github.com/pdm-project/pdm/issues/1721) ### Miscellany - Use `ruff` as the linter. [#1715](https://github.com/pdm-project/pdm/issues/1715) - Document installation via `asdf`. [#1725](https://github.com/pdm-project/pdm/issues/1725) Release v2.4.5 (2023-02-10) --------------------------- ### Bug Fixes - Fix a bug that built wheels are prioritized over source distributions with higher version number. [#1698](https://github.com/pdm-project/pdm/issues/1698) Release v2.4.4 (2023-02-10) --------------------------- ### Features & Improvements - Add more intuitive error message when the `requires-python` doesn't work for all dependencies. [#1690](https://github.com/pdm-project/pdm/issues/1690) ### Bug Fixes - Prefer built distributions when finding packages for metadata extraction. [#1535](https://github.com/pdm-project/pdm/issues/1535) Release v2.4.3 (2023-02-06) --------------------------- ### Features & Improvements - Allow creating venv in project forcibly if it already exists. [#1666](https://github.com/pdm-project/pdm/issues/1666) - Always ignore remembered selection in pdm init. [#1672](https://github.com/pdm-project/pdm/issues/1672) ### Bug Fixes - Fix the fallback build backend to `pdm-pep517` instead of `setuptools`. [#1658](https://github.com/pdm-project/pdm/issues/1658) - Eliminate the deprecation warnings from `importlib.resources`. [#1660](https://github.com/pdm-project/pdm/issues/1660) - Don't crash when failed to get the latest version of PDM for checking update. [#1663](https://github.com/pdm-project/pdm/issues/1663) - Fix the priorities of importable formats to make sure the correct format is used. [#1669](https://github.com/pdm-project/pdm/issues/1669) - Import editable requirements into dev dependencies. [#1674](https://github.com/pdm-project/pdm/issues/1674) Release v2.4.2 (2023-01-31) --------------------------- ### Bug Fixes - Skip some tests on packaging < 22. [#1649](https://github.com/pdm-project/pdm/issues/1649) - Fix a bug that sources from the project config are not loaded. [#1651](https://github.com/pdm-project/pdm/issues/1651) - Set VIRTUAL_ENV in `pdm run`. [#1652](https://github.com/pdm-project/pdm/issues/1652) Release v2.4.1 (2023-01-28) --------------------------- ### Features & Improvements - Add proper display for the extra pypi sources in `pdm config`. [#1622](https://github.com/pdm-project/pdm/issues/1622) - Support running python scripts without prefixing with `python`. [#1626](https://github.com/pdm-project/pdm/issues/1626) ### Bug Fixes - Ignore the python requirement for overridden packages. [#1575](https://github.com/pdm-project/pdm/issues/1575) - Fix the wildcards in requirement specifiers to make it pass the new parser of `packaging>=22`. [#1619](https://github.com/pdm-project/pdm/issues/1619) - Add the missing `subdirectory` attribute to the lockfile entry. [#1630](https://github.com/pdm-project/pdm/issues/1630) - Fix a bug that VCS locks don't update when the rev part changes. [#1640](https://github.com/pdm-project/pdm/issues/1640) - Redirect the spinner output to stderr. [#1646](https://github.com/pdm-project/pdm/issues/1646) - Ensure the destination directory exists before building the packages. [#1647](https://github.com/pdm-project/pdm/issues/1647) Release v2.4.0 (2023-01-12) --------------------------- ### Features & Improvements - Support multiple PyPI indexes in the configuration. They will be tried after the sources in `pyproject.toml`. [#1310](https://github.com/pdm-project/pdm/issues/1310) - Accept yanked versions when the requirement version is pinned. [#1575](https://github.com/pdm-project/pdm/issues/1575) - Expose PDM fixtures as a `pytest` plugin `pdm.pytest` for plugin developers. [#1594](https://github.com/pdm-project/pdm/issues/1594) - Show message in the status when fetching package hashes. Fetch hashes from the JSON API response as well. [#1609](https://github.com/pdm-project/pdm/issues/1609) - Mark `pdm.lock` with an `@generated` comment. [#1611](https://github.com/pdm-project/pdm/issues/1611) ### Bug Fixes - Exclude site-packages for symlinks of the python interpreter as well. [#1598](https://github.com/pdm-project/pdm/issues/1598) - Fix a bug that error output can't be decoded correctly on Windows. [#1602](https://github.com/pdm-project/pdm/issues/1602) Release v2.3.4 (2022-12-27) --------------------------- ### Features & Improvements - Detect PDM inside a zipapp and disable some functions. [#1578](https://github.com/pdm-project/pdm/issues/1578) ### Bug Fixes - Don't write `sitecustomize` to the home directory if it exists in the filesystem(not packed in a zipapp). [#1572](https://github.com/pdm-project/pdm/issues/1572) - Fix a bug that a directory is incorrectly marked as to be deleted when it contains symlinks. [#1580](https://github.com/pdm-project/pdm/issues/1580) Release v2.3.3 (2022-12-15) --------------------------- ### Bug Fixes - Allow relative paths in `build-system.requires`, since `build` and `hatch` both support it. Be aware it is not allowed in the standard. [#1560](https://github.com/pdm-project/pdm/issues/1560) - Strip the local part when building a specifier for comparison with the package version. This is not permitted by PEP 508 as implemented by `packaging 22.0`. [#1562](https://github.com/pdm-project/pdm/issues/1562) - Update the version for check_update after self update [#1563](https://github.com/pdm-project/pdm/issues/1563) - Replace the `__file__` usages with `importlib.resources`, to make PDM usable in a zipapp. [#1567](https://github.com/pdm-project/pdm/issues/1567) - Fix the matching problem of packages in the lockfile. [#1569](https://github.com/pdm-project/pdm/issues/1569) ### Dependencies - Exclude `package==22.0` from the dependencies to avoid some breakages to the end users. [#1568](https://github.com/pdm-project/pdm/issues/1568) Release v2.3.2 (2022-12-08) --------------------------- ### Bug Fixes - Fix an installation failure when the RECORD file contains commas in the file path. [#1010](https://github.com/pdm-project/pdm/issues/1010) - Fallback to `pdm.pep517` as the metadata transformer for unknown custom build backends. [#1546](https://github.com/pdm-project/pdm/issues/1546) - Fix a bug that Ctrl + C kills the python interactive session instead of clearing the current line. [#1547](https://github.com/pdm-project/pdm/issues/1547) - Fix a bug with egg segment for local dependency [#1552](https://github.com/pdm-project/pdm/issues/1552) ### Dependencies - Update `installer` to `0.6.0`. [#1550](https://github.com/pdm-project/pdm/issues/1550) - Update minimum version of `unearth` to `0.6.3` and test against `packaging==22.0`. [#1555](https://github.com/pdm-project/pdm/issues/1555) Release v2.3.1 (2022-12-05) --------------------------- ### Bug Fixes - Fix a resolution loop issue when the current project depends on itself and it uses the dynamic version from SCM. [#1541](https://github.com/pdm-project/pdm/issues/1541) - Don't give duplicate results when specifying a relative path for `pdm use`. [#1542](https://github.com/pdm-project/pdm/issues/1542) Release v2.3.0 (2022-12-02) --------------------------- ### Features & Improvements - Beautify the error message of build errors. Default to showing the last 10 lines of the build output. [#1491](https://github.com/pdm-project/pdm/issues/1491) - Rename the `tool.pdm.overrides` table to `tool.pdm.resolution.overrides`. The old name is deprecated at the same time. [#1503](https://github.com/pdm-project/pdm/issues/1503) - Add backend selection and `--backend` option to `pdm init` command, users can choose a favorite backend from `setuptools`, `flit`, `hatchling` and `pdm-pep517`(default), since they all support PEP 621 standards. [#1504](https://github.com/pdm-project/pdm/issues/1504) - Allows specifying the insertion position of user provided arguments in scripts with the `{args[:default]}` placeholder. [#1507](https://github.com/pdm-project/pdm/issues/1507) ### Bug Fixes - The local package is now treated specially during installation and locking. This means it will no longer be included in the lockfile, and should never be installed twice even when using nested extras. This will ensure the lockdown stays relevant when the version changes. [#1481](https://github.com/pdm-project/pdm/issues/1481) - Fix the version diff algorithm of installed packages to consider local versions as compatible. [#1497](https://github.com/pdm-project/pdm/issues/1497) - Fix the confusing message when detecting a Python interpreter under `python.use_venv=False` [#1508](https://github.com/pdm-project/pdm/issues/1508) - Fix the test failure with the latest `findpython` installed. [#1516](https://github.com/pdm-project/pdm/issues/1516) - Fix the module missing error of pywin32 in a virtualenv with `install.cache` set to `true` and caching method is `pth`. [#863](https://github.com/pdm-project/pdm/issues/863) ### Dependencies - Drop the dependency `pdm-pep517`. [#1504](https://github.com/pdm-project/pdm/issues/1504) - Replace `pep517` with `pyproject-hooks` because of the rename. [#1528](https://github.com/pdm-project/pdm/issues/1528) ### Removals and Deprecations - Remove the support for exporting the project file to a `setup.py` format, users are encouraged to migrate to the PEP 621 metadata. [#1504](https://github.com/pdm-project/pdm/issues/1504) Release v2.2.1 (2022-11-03) --------------------------- ### Features & Improvements - Make `sitecustomize.py` respect the `PDM_PROJECT_MAX_DEPTH` environment variable [#1471](https://github.com/pdm-project/pdm/issues/1471) ### Bug Fixes - Fix the comparison of `python_version` in the environment marker. When the version contains only one digit, the result was incorrect. [#1484](https://github.com/pdm-project/pdm/issues/1484) Release v2.2.0 (2022-10-31) --------------------------- ### Features & Improvements - Add `venv.prompt` configuration to allow customizing prompt when a virtualenv is activated [#1332](https://github.com/pdm-project/pdm/issues/1332) - Allow the use of custom CA certificates per publish repository using `ca_certs` or from the command line via `pdm publish --ca-certs ...`. [#1392](https://github.com/pdm-project/pdm/issues/1392) - Rename the `plugin` command to `self`, and it can not only manage plugins but also all dependencies. Add a subcommand `self update` to update PDM itself. [#1406](https://github.com/pdm-project/pdm/issues/1406) - Allow `pdm init` to receive a Python path or version via `--python` option. [#1412](https://github.com/pdm-project/pdm/issues/1412) - Add a default value for `requires-python` when importing from other formats. [#1426](https://github.com/pdm-project/pdm/issues/1426) - Use `pdm` instead of `pip` to resolve and install build requirements. So that PDM configurations can control the process. [#1429](https://github.com/pdm-project/pdm/issues/1429) - Customizable color theme via `pdm config` command. [#1450](https://github.com/pdm-project/pdm/issues/1450) - A new `pdm lock --check` flag to validate whether the lock is up to date. [#1459](https://github.com/pdm-project/pdm/issues/1459) - Add both option and config item to ship `pip` when creating a new venv. [#1463](https://github.com/pdm-project/pdm/issues/1463) - Issue warning and skip the requirement if it has the same name as the current project. [#1466](https://github.com/pdm-project/pdm/issues/1466) - Enhance the `pdm list` command with new formats: `--csv,--markdown` and add options `--fields,--sort` to control the output contents. Users can also include `licenses` in the `--fields` option to display the package licenses. [#1469](https://github.com/pdm-project/pdm/issues/1469) - A new pre-commit hook to run `pdm lock --check` in pre-commit. [#1471](https://github.com/pdm-project/pdm/issues/1471) ### Bug Fixes - Fix the issue that relative paths don't work well with `--project` argument. [#1220](https://github.com/pdm-project/pdm/issues/1220) - It is now possible to refer to a package from outside the project with relative paths in dependencies. [#1381](https://github.com/pdm-project/pdm/issues/1381) - Ensure `pypi.[ca,client]_cert[s]` config items are passed to distribution builder install steps to allow for custom PyPI index sources with self signed certificates. [#1396](https://github.com/pdm-project/pdm/issues/1396) - Fix a crash issue when depending on editable packages with extras. [#1401](https://github.com/pdm-project/pdm/issues/1401) - Do not save the python path when using non-interactive mode in `pdm init`. [#1410](https://github.com/pdm-project/pdm/issues/1410) - Fix the matching of `python*` command in `pdm run`. [#1414](https://github.com/pdm-project/pdm/issues/1414) - Show the Python path, instead of the real executable, in the Python selection menu. [#1418](https://github.com/pdm-project/pdm/issues/1418) - Fix the HTTP client of package publishment to prompt for password and read PDM configurations correctly. [#1430](https://github.com/pdm-project/pdm/issues/1430) - Ignore the unknown fields when constructing a requirement object. [#1445](https://github.com/pdm-project/pdm/issues/1445) - Fix a bug of unrelated candidates being fetched if the requirement is matching wildcard versions(e.g. `==1.*`). [#1465](https://github.com/pdm-project/pdm/issues/1465) - Use `importlib-metadata` from PyPI for Python < 3.10. [#1467](https://github.com/pdm-project/pdm/issues/1467) ### Documentation - Clarify the difference between a library and an application. Update the guide of multi-stage docker build. [#1371](https://github.com/pdm-project/pdm/issues/1371) ### Removals and Deprecations - Remove all top-level imports, users should import from the submodules instead. [#1404](https://github.com/pdm-project/pdm/issues/1404) - Remove the usages of old config names deprecated since 2.0. [#1422](https://github.com/pdm-project/pdm/issues/1422) - Remove the deprecated color functions, use [rich's console markup](https://rich.readthedocs.io/en/latest/markup.html) instead. [#1452](https://github.com/pdm-project/pdm/issues/1452) Release v2.1.5 (2022-10-05) --------------------------- ### Bug Fixes - Ensure `pypi.[ca,client]_cert[s]` config items are passed to distribution builder install steps to allow for custom PyPI index sources with self signed certificates. [#1396](https://github.com/pdm-project/pdm/issues/1396) - Fix a crash issue when depending on editable packages with extras. [#1401](https://github.com/pdm-project/pdm/issues/1401) - Do not save the python path when using non-interactive mode in `pdm init`. [#1410](https://github.com/pdm-project/pdm/issues/1410) - Restrict importlib-metadata (<5.0.0) for Python <3.8 [#1411](https://github.com/pdm-project/pdm/issues/1411) Release v2.1.4 (2022-09-17) --------------------------- ### Bug Fixes - Fix a lock failure when depending on self with URL requirements. [#1347](https://github.com/pdm-project/pdm/issues/1347) - Ensure list to concatenate args for composite scripts. [#1359](https://github.com/pdm-project/pdm/issues/1359) - Fix an error in `pdm lock --refresh` if some packages has URLs. [#1361](https://github.com/pdm-project/pdm/issues/1361) - Fix unnecessary package downloads and VCS clones for certain commands. [#1370](https://github.com/pdm-project/pdm/issues/1370) - Fix a conversion error when converting a list of conditional dependencies from a Poetry format. [#1383](https://github.com/pdm-project/pdm/issues/1383) ### Documentation - Adds a section to the docs on how to correctly work with PDM and version control systems. [#1364](https://github.com/pdm-project/pdm/issues/1364) Release v2.1.3 (2022-08-30) --------------------------- ### Features & Improvements - When adding a package to (or removing from) a group, enhance the formatting of the group name in the printed message. [#1329](https://github.com/pdm-project/pdm/issues/1329) ### Bug Fixes - Fix a bug of missing hashes for packages with `file://` links the first time they are added. [#1325](https://github.com/pdm-project/pdm/issues/1325) - Ignore invalid values of `data-requires-python` when parsing package links. [#1334](https://github.com/pdm-project/pdm/issues/1334) - Leave an incomplete project metadata if PDM fails to parse the project files, but emit a warning. [#1337](https://github.com/pdm-project/pdm/issues/1337) - Fix the bug that `editables` package isn't installed for self package. [#1344](https://github.com/pdm-project/pdm/issues/1344) - Fix a decoding error for non-ASCII characters in package description when publishing it. [#1345](https://github.com/pdm-project/pdm/issues/1345) ### Documentation - Clarify documentation explaining `setup-script`, `run-setuptools`, and `is-purelib`. [#1327](https://github.com/pdm-project/pdm/issues/1327) Release v2.1.2 (2022-08-15) --------------------------- ### Bug Fixes - Fix a bug that dependencies from different versions of the same package override each other. [#1307](https://github.com/pdm-project/pdm/issues/1307) - Forward SIGTERM to child processes in `pdm run`. [#1312](https://github.com/pdm-project/pdm/issues/1312) - Fix errors when running on FIPS 140-2 enabled systems using Python 3.9 and newer. [#1313](https://github.com/pdm-project/pdm/issues/1313) - Fix the build failure when the subprocess outputs with non-UTF8 characters. [#1319](https://github.com/pdm-project/pdm/issues/1319) - Delay the trigger of `post_lock` for `add` and `update` operations, to ensure the `pyproject.toml` is updated before the hook is run. [#1320](https://github.com/pdm-project/pdm/issues/1320) Release v2.1.1 (2022-08-05) --------------------------- ### Features & Improvements - Add a env_file.override option that allows the user to specify that the env_file should override any existing environment variables. This is not the default as the environment the code runs it should take precedence. [#1299](https://github.com/pdm-project/pdm/issues/1299) ### Bug Fixes - Fix a bug that unnamed requirements can't override the old ones in either `add` or `update` command. [#1287](https://github.com/pdm-project/pdm/issues/1287) - Support mutual TLS to private repositories via pypi.client_cert and pypi.client_key config options. [#1290](https://github.com/pdm-project/pdm/issues/1290) - Set a minimum version for the `packaging` dependency to ensure that `packaging.utils.parse_wheel_filename` is available. [#1293](https://github.com/pdm-project/pdm/issues/1293) - Fix a bug that checking for PDM update creates a venv. [#1301](https://github.com/pdm-project/pdm/issues/1301) - Prefer compatible packages when fetching metadata. [#1302](https://github.com/pdm-project/pdm/issues/1302) Release v2.1.0 (2022-07-29) --------------------------- ### Features & Improvements - Allow the use of custom CA certificates using the `pypi.ca_certs` config entry. [#1240](https://github.com/pdm-project/pdm/issues/1240) - Add `pdm export` to available pre-commit hooks. [#1279](https://github.com/pdm-project/pdm/issues/1279) ### Bug Fixes - Skip incompatible requirements when installing build dependencies. [#1264](https://github.com/pdm-project/pdm/issues/1264) - Fix a crash when pdm tries to publish a package with non-ASCII characters in the metadata. [#1270](https://github.com/pdm-project/pdm/issues/1270) - Try to read the lock file even if the lock version is incompatible. [#1273](https://github.com/pdm-project/pdm/issues/1273) - For packages that are only available as source distribution, the `summary` field in `pdm.lock` contains the `description` from the package's `pyproject.toml`. [#1274](https://github.com/pdm-project/pdm/issues/1274) - Do not crash when calling `pdm show` for a package that is only available as source distribution. [#1276](https://github.com/pdm-project/pdm/issues/1276) - Fix a bug that completion scripts are interpreted as rich markups. [#1283](https://github.com/pdm-project/pdm/issues/1283) ### Dependencies - Remove the dependency of `pip`. [#1268](https://github.com/pdm-project/pdm/issues/1268) ### Removals and Deprecations - Deprecate the top-level imports from `pdm` module, it will be removed in the future. [#1282](https://github.com/pdm-project/pdm/issues/1282) Release v2.0.3 (2022-07-22) --------------------------- ### Bug Fixes - Support Conda environments when detecting the project environment. [#1253](https://github.com/pdm-project/pdm/issues/1253) - Fix the interpreter resolution to first try `python` executable in the `PATH`. [#1255](https://github.com/pdm-project/pdm/issues/1255) - Stabilize sorting of URLs in `metadata.files` in `pdm.lock`. [#1256](https://github.com/pdm-project/pdm/issues/1256) - Don't expand credentials in the file URLs in the `[metadata.files]` table of the lock file. [#1259](https://github.com/pdm-project/pdm/issues/1259) Release v2.0.2 (2022-07-20) --------------------------- ### Features & Improvements - `env_file` variables no longer override existing environment variables. [#1235](https://github.com/pdm-project/pdm/issues/1235) - Support referencing other optional groups in optional-dependencies with `[group1, group2]` [#1241](https://github.com/pdm-project/pdm/issues/1241) ### Bug Fixes - Respect `requires-python` when creating the default venv. [#1237](https://github.com/pdm-project/pdm/issues/1237) Release v2.0.1 (2022-07-17) --------------------------- ### Bug Fixes - Write lockfile before calling 'post_lock' hook [#1224](https://github.com/pdm-project/pdm/issues/1224) - Suppress errors when cache dir isn't accessible. [#1226](https://github.com/pdm-project/pdm/issues/1226) - Don't save python path for venv commands. [#1230](https://github.com/pdm-project/pdm/issues/1230) Release v2.0.0 (2022-07-15) --------------------------- ### Bug Fixes - Fix a bug that the running env overrides the PEP 582 `PYTHONPATH`. [#1211](https://github.com/pdm-project/pdm/issues/1211) - Add [`pwsh`](https://github.com/PowerShell/PowerShell) as an alias of `powershell` for shell completion. [#1216](https://github.com/pdm-project/pdm/issues/1216) - Fixed a bug with `zsh` completion regarding `--pep582` flag. [#1218](https://github.com/pdm-project/pdm/issues/1218) - Fix a bug of requirement checking under non-isolated mode. [#1219](https://github.com/pdm-project/pdm/issues/1219) - Fix a bug when removing packages, TOML document might become invalid. [#1221](https://github.com/pdm-project/pdm/issues/1221) Release v2.0.0b2 (2022-07-08) ----------------------------- ### Breaking Changes - Store file URLs instead of filenames in the lock file, bump lock version to `4.0`. [#1203](https://github.com/pdm-project/pdm/issues/1203) ### Features & Improvements - Read site-wide configuration, which serves as the lowest-priority layer. This layer will be read-only in the CLI. [#1200](https://github.com/pdm-project/pdm/issues/1200) - Get package links from the urls stored in the lock file. [#1204](https://github.com/pdm-project/pdm/issues/1204) ### Bug Fixes - Fix a bug that the host pip(installed with pdm) may not be compatible with the project python. [#1196](https://github.com/pdm-project/pdm/issues/1196) - Update `unearth` to fix a bug that install links with weak hashes are skipped. This often happens on self-hosted PyPI servers. [#1202](https://github.com/pdm-project/pdm/issues/1202) Release v2.0.0b1 (2022-07-02) ----------------------------- ### Features & Improvements - Integrate `pdm venv` commands into the main program. Make PEP 582 an opt-in feature. [#1162](https://github.com/pdm-project/pdm/issues/1162) - Add config `global_project.fallback_verbose` defaulting to `True`. When set to `False` disables message `Project is not found, fallback to the global project` [#1188](https://github.com/pdm-project/pdm/issues/1188) - Add `--only-keep` option to `pdm sync` to keep only selected packages. Originally requested at #398. [#1191](https://github.com/pdm-project/pdm/issues/1191) ### Bug Fixes - Fix a bug that requirement extras and underlying are resolved to the different version [#1173](https://github.com/pdm-project/pdm/issues/1173) - Update `unearth` to `0.4.1` to skip the wheels with invalid version parts. [#1178](https://github.com/pdm-project/pdm/issues/1178) - Fix reading `PDM_RESOLVE_MAX_ROUNDS` environment variable (was spelled `…ROUDNS` before). [#1180](https://github.com/pdm-project/pdm/issues/1180) - Deduplicate the list of found Python versions. [#1182](https://github.com/pdm-project/pdm/issues/1182) - Use the normal stream handler for logging, to fix some display issues under non-tty environments. [#1184](https://github.com/pdm-project/pdm/issues/1184) ### Removals and Deprecations - Remove the useless `--no-clean` option from `pdm sync` command. [#1191](https://github.com/pdm-project/pdm/issues/1191) Release v2.0.0a1 (2022-06-29) ----------------------------- ### Breaking Changes - Editable dependencies in the `[project]` table is not allowed, according to PEP 621. They are however still allowed in the `[tool.pdm.dev-dependencies]` table. PDM will emit a warning when it finds editable dependencies in the `[project]` table, or will abort when you try to add them into the `[project]` table via CLI. [#1083](https://github.com/pdm-project/pdm/issues/1083) - Now the paths to the global configurations and global project are calculated according to platform standards. [#1161](https://github.com/pdm-project/pdm/issues/1161) ### Features & Improvements - Add support for importing from a `setup.py` project. [#1062](https://github.com/pdm-project/pdm/issues/1062) - Switch the UI backend to `rich`. [#1091](https://github.com/pdm-project/pdm/issues/1091) - Improved the terminal UI and logging. Disable live progress under verbose mode. The logger levels can be controlled by the `-v` option. [#1096](https://github.com/pdm-project/pdm/issues/1096) - Use `unearth` to replace `pip`'s `PackageFinder` and related data models. PDM no longer relies on `pip` internals, which are unstable across updates. [#1096](https://github.com/pdm-project/pdm/issues/1096) - Lazily load the candidates returned by `find_matches()` to speed up the resolution. [#1098](https://github.com/pdm-project/pdm/issues/1098) - Add a new command `publish` to PDM since it is required for so many people and it will make the workflow easier. [#1107](https://github.com/pdm-project/pdm/issues/1107) - Add a `composite` script kind allowing to run multiple defined scripts in a single command as well as reusing scripts but overriding `env` or `env_file`. [#1117](https://github.com/pdm-project/pdm/issues/1117) - Add a new execution option `--skip` to opt-out some scripts and hooks from any execution (both scripts and PDM commands). [#1127](https://github.com/pdm-project/pdm/issues/1127) - Add the `pre/post_publish`, `pre/post_run` and `pre/post_script` hooks as well as an extensive lifecycle and hooks documentation. [#1147](https://github.com/pdm-project/pdm/issues/1147) - Shorter scripts listing, especially for multilines and composite scripts. [#1151](https://github.com/pdm-project/pdm/issues/1151) - Build configurations have been moved to `[tool.pdm.build]`, according to `pdm-pep517 1.0.0`. At the same time, warnings will be shown against old usages. [#1153](https://github.com/pdm-project/pdm/issues/1153) - Improve the lock speed by parallelizing the hash fetching. [#1154](https://github.com/pdm-project/pdm/issues/1154) - Retrieve the candidate metadata by parsing the `pyproject.toml` rather than building it. [#1156](https://github.com/pdm-project/pdm/issues/1156) - Update the format converters to support the new `[tool.pdm.build]` table. [#1157](https://github.com/pdm-project/pdm/issues/1157) - Scripts are now available as root command if they don't conflict with any builtin or plugin-contributed command. [#1159](https://github.com/pdm-project/pdm/issues/1159) - Add a `post_use` hook triggered after successfully switching Python version. [#1163](https://github.com/pdm-project/pdm/issues/1163) - Add project configuration `respect-source-order` under `[tool.pdm.resolution]` to respect the source order in the `pyproject.toml` file. Packages will be returned by source earlier in the order or later ones if not found. [#593](https://github.com/pdm-project/pdm/issues/593) ### Bug Fixes - Fix a bug that candidates with local part in the version can't be found and installed correctly. [#1093](https://github.com/pdm-project/pdm/issues/1093) ### Dependencies - Prefer `tomllib` on Python 3.11 [#1072](https://github.com/pdm-project/pdm/issues/1072) - Drop the vendored libraries `click`, `halo`, `colorama` and `log_symbols`. PDM has no vendors now. [#1091](https://github.com/pdm-project/pdm/issues/1091) - Update dependency version `pdm-pep517` to `1.0.0`. [#1153](https://github.com/pdm-project/pdm/issues/1153) ### Removals and Deprecations - PDM legacy metadata format(from `pdm 0.x`) is no longer supported. [#1157](https://github.com/pdm-project/pdm/issues/1157) ### Miscellany - Provide a `tox.ini` file for easier local testing against all Python versions. [#1160](https://github.com/pdm-project/pdm/issues/1160) Release v1.15.4 (2022-06-28) ---------------------------- ### Bug Fixes - Revert #1106: Do not use `venv` scheme for `prefix` kind install scheme. [#1158](https://github.com/pdm-project/pdm/issues/1158) - Fix a bug when updating a package with extra requirements, the package version doesn't get updated correctly. [#1166](https://github.com/pdm-project/pdm/issues/1166) ### Miscellany - Add additional installation option via [asdf-pdm](https://github.com/1oglop1/asdf-pdm). Add `skip-add-to-path` option to installer in order to prevent changing `PATH`. Replace `bin` variable name with `bin_dir`. [#1145](https://github.com/pdm-project/pdm/issues/1145) Release v1.15.3 (2022-06-14) ---------------------------- ### Bug Fixes - Fix a defect in the resolution preferences that causes an infinite resolution loop. [#1119](https://github.com/pdm-project/pdm/issues/1119) - Update the poetry importer to support the new `[tool.poetry.build]` config table. [#1131](https://github.com/pdm-project/pdm/issues/1131) ### Improved Documentation - Add support for multiple versions of documentations. [#1126](https://github.com/pdm-project/pdm/issues/1126) Release v1.15.2 (2022-06-06) ---------------------------- ### Bug Fixes - Fix bug where SIGINT is sent to the main `pdm` process and not to the process actually being run. [#1095](https://github.com/pdm-project/pdm/issues/1095) - Fix a bug due to the build backend fallback, which causes different versions of the same requirement to exist in the build environment, making the building unstable depending on which version being used. [#1099](https://github.com/pdm-project/pdm/issues/1099) - Don't include the `version` in the cache key of the locked candidates if they are from a URL requirement. [#1099](https://github.com/pdm-project/pdm/issues/1099) - Fix a bug where dependencies with `requires-python` pre-release versions caused `pdm update` to fail with `InvalidPyVersion`. [#1111](https://github.com/pdm-project/pdm/issues/1111) Release v1.15.1 (2022-06-02) ---------------------------- ### Bug Fixes - Fix a bug that dependencies are missing from the dep graph when they are depended by a requirement with extras. [#1097](https://github.com/pdm-project/pdm/issues/1097) - Give a default version if the version is dynamic in `setup.cfg` or `setup.py`. [#1101](https://github.com/pdm-project/pdm/issues/1101) - Fix a bug that the hashes for file URLs are not included in the lock file. [#1103](https://github.com/pdm-project/pdm/issues/1103) - Fix a bug that package versions are updated even when they are excluded by `pdm update` command. [#1104](https://github.com/pdm-project/pdm/issues/1104) - Prefer `venv` install scheme when available. This scheme is more stable than `posix_prefix` scheme since the latter is often patched by distributions. [#1106](https://github.com/pdm-project/pdm/issues/1106) ### Miscellany - Move the test artifacts to a submodule. It will make it easier to package this project. [#1084](https://github.com/pdm-project/pdm/issues/1084) Release v1.15.0 (2022-05-16) ---------------------------- ### Features & Improvements - Allow specifying lockfile other than `pdm.lock` by `--lockfile` option or `PDM_LOCKFILE` env var. [#1038](https://github.com/pdm-project/pdm/issues/1038) ### Bug Fixes - Replace the editable entry in `pyproject.toml` when running `pdm add --no-editable `. [#1050](https://github.com/pdm-project/pdm/issues/1050) - Ensure the pip module inside venv in installation script. [#1053](https://github.com/pdm-project/pdm/issues/1053) - Fix the py2 compatibility issue in the in-process `get_sysconfig_path.py` script. [#1056](https://github.com/pdm-project/pdm/issues/1056) - Fix a bug that file paths in URLs are not correctly unquoted. [#1073](https://github.com/pdm-project/pdm/issues/1073) - Fix a bug on Python 3.11 that overriding an existing command from plugins raises an error. [#1075](https://github.com/pdm-project/pdm/issues/1075) - Replace the `${PROJECT_ROOT}` variable in the result of `export` command. [#1079](https://github.com/pdm-project/pdm/issues/1079) ### Removals and Deprecations - Show a warning if Python 2 interpreter is being used and remove the support on 2.0. [#1082](https://github.com/pdm-project/pdm/issues/1082) Release v1.14.1 (2022-04-21) ---------------------------- ### Features & Improvements - Ask for description when doing `pdm init` and create default README for libraries. [#1041](https://github.com/pdm-project/pdm/issues/1041) ### Bug Fixes - Fix a bug of missing subdirectory fragment when importing from a `requirements.txt`. [#1036](https://github.com/pdm-project/pdm/issues/1036) - Fix use_cache.json with corrupted python causes `pdm use` error. [#1039](https://github.com/pdm-project/pdm/issues/1039) - Ignore the `optional` key when converting from Poetry's dependency entries. [#1042](https://github.com/pdm-project/pdm/issues/1042) ### Improved Documentation - Clarify documentation on enabling PEP582 globally. [#1033](https://github.com/pdm-project/pdm/issues/1033) Release v1.14.0 (2022-04-08) ---------------------------- ### Features & Improvements - Editable installations won't be overridden unless `--no-editable` is passed. `pdm add --no-editable` will now override the `editable` mode of the given packages. [#1011](https://github.com/pdm-project/pdm/issues/1011) - Re-calculate the file hashes when running `pdm lock --refresh`. [#1019](https://github.com/pdm-project/pdm/issues/1019) ### Bug Fixes - Fix a bug that requirement with extras isn't resolved to the version as specified by the range. [#1001](https://github.com/pdm-project/pdm/issues/1001) - Replace the `${PROJECT_ROOT}` in the output of `pdm list`. [#1004](https://github.com/pdm-project/pdm/issues/1004) - Further fix the python path issue of macOS system installed Python. [#1023](https://github.com/pdm-project/pdm/issues/1023) - Fix the install path issue on Python 3.10 installed from homebrew. [#996](https://github.com/pdm-project/pdm/issues/996) ### Improved Documentation - Document how to install PDM inside a project with Pyprojectx. [#1004](https://github.com/pdm-project/pdm/issues/1004) ### Dependencies - Support `installer 0.5.x`. [#1002](https://github.com/pdm-project/pdm/issues/1002) Release v1.13.6 (2022-03-28) ---------------------------- ### Bug Fixes - Default the optional `license` field to "None". [#991](https://github.com/pdm-project/pdm/issues/991) - Don't create project files in `pdm search` command. [#993](https://github.com/pdm-project/pdm/issues/993) - Fix a bug that the env vars in source urls in exported result are not expanded. [#997](https://github.com/pdm-project/pdm/issues/997) Release v1.13.5 (2022-03-23) ---------------------------- ### Features & Improvements - Users can change the install destination of global project to the user site(`~/.local`) with `global_project.user_site` config. [#885](https://github.com/pdm-project/pdm/issues/885) - Make the path to the global project configurable. Rename the configuration `auto_global` to `global_project.fallback` and deprecate the old name. [#986](https://github.com/pdm-project/pdm/issues/986) ### Bug Fixes - Fix the compatibility when fetching license information in `show` command. [#966](https://github.com/pdm-project/pdm/issues/966) - Don't follow symlinks for the paths in the requirement strings. [#976](https://github.com/pdm-project/pdm/issues/976) - Use the default install scheme when installing build requirements. [#983](https://github.com/pdm-project/pdm/issues/983) - Fix a bug that `_.site_packages` is overridden by default option value. [#985](https://github.com/pdm-project/pdm/issues/985) Release v1.13.4 (2022-03-09) ---------------------------- ### Features & Improvements - Update the dependency `pdm-pep517` to support PEP 639. [#959](https://github.com/pdm-project/pdm/issues/959) ### Bug Fixes - Filter out the unmatched python versions when listing the available versions. [#941](https://github.com/pdm-project/pdm/issues/941) - Fix a bug displaying the available python versions. [#943](https://github.com/pdm-project/pdm/issues/943) - Fix a bug under non-UTF8 console encoding. [#960](https://github.com/pdm-project/pdm/issues/960) - Fix a bug that data files are not copied to the destination when using installation cache. [#961](https://github.com/pdm-project/pdm/issues/961) Release v1.13.3 (2022-02-24) ---------------------------- ### Bug Fixes - Fix a bug that VCS repo name are parsed as the package name. [#928](https://github.com/pdm-project/pdm/issues/928) - Support prerelease versions for global projects. [#932](https://github.com/pdm-project/pdm/issues/932) - Fix a bug that VCS revision in the lock file isn't respected when installing. [#933](https://github.com/pdm-project/pdm/issues/933) ### Dependencies - Switch from `pythonfinder` to `findpython` as the Python version finder. [#930](https://github.com/pdm-project/pdm/issues/930) Release v1.13.2 (2022-02-20) ---------------------------- ### Bug Fixes - Fix a regression issue that prereleases can't be installed if the version specifier of the requirement doesn't imply that. [#920](https://github.com/pdm-project/pdm/issues/920) Release v1.13.1 (2022-02-18) ---------------------------- ### Bug Fixes - Fix a bug that bad pip cache dir value breaks PDM's check update function. [#922](https://github.com/pdm-project/pdm/issues/922) - Fix a race condition in parallel installation by changing metadata to a lazy property. This fixes a bug that incompatible wheels are installed unexpectedly. [#924](https://github.com/pdm-project/pdm/issues/924) Release v1.13.0.post0 (2022-02-18) ---------------------------------- ### Bug Fixes - Fix a bug that incompatible platform-specific wheels are installed. [#921](https://github.com/pdm-project/pdm/issues/921) Release v1.13.0 (2022-02-18) ---------------------------- ### Features & Improvements - Support `pre_*` and `post_*` scripts for task composition. Pre- and Post- scripts for `init`, `build`, `install` and `lock` will be run if present. [#789](https://github.com/pdm-project/pdm/issues/789) - Support `--config/-c` option to specify another global configuration file. [#883](https://github.com/pdm-project/pdm/issues/883) - Packages with extras require no longer inherit the dependencies from the same package without extras. It is because the package without extras are returned as one of the dependencies. This change won't break the existing lock files nor dependency cache. [#892](https://github.com/pdm-project/pdm/issues/892) - Support version ranges in `[tool.pdm.overrides]` table. [#909](https://github.com/pdm-project/pdm/issues/909) - Rename config `use_venv` to `python.use_venv`; rename config `feature.install_cache` to `install.cache`; rename config `feature.install_cache_method` to `install.cache_method`; rename config `parallel_install` to `install.parallel`. [#914](https://github.com/pdm-project/pdm/issues/914) ### Bug Fixes - Fix a bug that file URLs or VCS URLs don't work in `[tool.pdm.overrides]` table. [#861](https://github.com/pdm-project/pdm/issues/861) - Fix a bug of identifier mismatch for URL requirements without an explicit name. [#901](https://github.com/pdm-project/pdm/issues/901) - No `requires-python` should be produced if ANY(`*`) is given. [#917](https://github.com/pdm-project/pdm/issues/917) - Fix a bug that `pdm.lock` gets created when `--dry-run` is passed to `pdm add`. [#918](https://github.com/pdm-project/pdm/issues/918) ### Improved Documentation - The default editable backend becomes `path`. [#904](https://github.com/pdm-project/pdm/issues/904) ### Removals and Deprecations - Stop auto-migrating projects from PDM 0.x format. [#912](https://github.com/pdm-project/pdm/issues/912) ### Refactor - Rename `ExtrasError` to `ExtrasWarning` for better understanding. Improve the warning message. [#892](https://github.com/pdm-project/pdm/issues/892) - Extract the environment related code from `Candidate` into a new class `PreparedCandidate`. `Candidate` no longer holds an `Environment` instance. [#920](https://github.com/pdm-project/pdm/issues/920) Release v1.12.8 (2022-02-06) ---------------------------- ### Features & Improvements - Print the error and continue if a plugin fails to load. [#878](https://github.com/pdm-project/pdm/issues/878) ### Bug Fixes - PDM now ignores configuration of uninstalled plugins. [#872](https://github.com/pdm-project/pdm/issues/872) - Fix the compatibility issue with `pip>=22.0`. [#875](https://github.com/pdm-project/pdm/issues/875) Release v1.12.7 (2022-01-31) ---------------------------- ### Features & Improvements - If no command is given to `pdm run`, it will run the Python REPL. [#856](https://github.com/pdm-project/pdm/issues/856) ### Bug Fixes - Fix the hash calculation when generating `direct_url.json` for a local pre-built wheel. [#861](https://github.com/pdm-project/pdm/issues/861) - PDM no longer migrates project meta silently. [#867](https://github.com/pdm-project/pdm/issues/867) ### Dependencies - Pin `pip<22.0`. [#874](https://github.com/pdm-project/pdm/issues/874) ### Miscellany - Reduce the number of tests that require network, and mark the rest with `network` marker. [#858](https://github.com/pdm-project/pdm/issues/858) Release v1.12.6 (2022-01-12) ---------------------------- ### Bug Fixes - Fix a bug that cache dir isn't created. [#843](https://github.com/pdm-project/pdm/issues/843) Release v1.12.5 (2022-01-11) ---------------------------- ### Bug Fixes - Fix a resolution error that dots in the package name are normalized to `-` unexpectedly. [#853](https://github.com/pdm-project/pdm/issues/853) Release v1.12.4 (2022-01-11) ---------------------------- ### Features & Improvements - Remember the last selection in `use` command to save the human effort. And introduce an `-i` option to ignored that remembered value. [#846](https://github.com/pdm-project/pdm/issues/846) ### Bug Fixes - Fix a bug of uninstall crash when the package has directories in `RECORD`. [#847](https://github.com/pdm-project/pdm/issues/847) - Fix the `ModuleNotFoundError` during uninstall when the modules required are removed. [#850](https://github.com/pdm-project/pdm/issues/850) Release v1.12.3 (2022-01-07) ---------------------------- ### Features & Improvements - Support setting Python path in global configuration. [#842](https://github.com/pdm-project/pdm/issues/842) ### Bug Fixes - Lowercase the package names in the lock file make it more stable. [#836](https://github.com/pdm-project/pdm/issues/836) - Show the packages to be updated in dry run mode of `pdm update` even if `--no-sync` is passed. [#837](https://github.com/pdm-project/pdm/issues/837) - Improve the robustness of update check code. [#841](https://github.com/pdm-project/pdm/issues/841) - Fix a bug that export result has environment markers that don't apply for all requirements. [#843](https://github.com/pdm-project/pdm/issues/843) Release v1.12.2 (2021-12-30) ---------------------------- ### Features & Improvements - Allow changing the installation linking method by `feature.install_cache_method` config. [#822](https://github.com/pdm-project/pdm/issues/822) ### Bug Fixes - Fix a bug that namespace packages can't be symlinked to the cache due to existing links. [#820](https://github.com/pdm-project/pdm/issues/820) - Make PDM generated pth files processed as early as possible. [#821](https://github.com/pdm-project/pdm/issues/821) - Fix a UnicodeDecodeError for subprocess logger under Windows/GBK. [#823](https://github.com/pdm-project/pdm/issues/823) Release v1.12.1 (2021-12-24) ---------------------------- ### Bug Fixes - Don't symlink pycaches to the target place. [#817](https://github.com/pdm-project/pdm/issues/817) Release v1.12.0 (2021-12-22) ---------------------------- ### Features & Improvements - Add `lock --refresh` to update the hash stored with the lock file without updating the pinned versions. [#642](https://github.com/pdm-project/pdm/issues/642) - Support resolution overriding in the `[tool.pdm.overrides]` table. [#790](https://github.com/pdm-project/pdm/issues/790) - Add support for signals for basic operations, now including `post_init`, `pre_lock`, `post_lock`, `pre_install` and `post_install`. [#798](https://github.com/pdm-project/pdm/issues/798) - Add `install --check` to check if the lock file is up to date. [#810](https://github.com/pdm-project/pdm/issues/810) - Use symlinks to cache installed packages when it is supported by the file system. [#814](https://github.com/pdm-project/pdm/issues/814) ### Bug Fixes - Fix a bug that candidates from urls are rejected by the `allow_prereleases` setting. Now non-named requirements are resolved earlier than pinned requirements. [#799](https://github.com/pdm-project/pdm/issues/799) ### Improved Documentation - Add a new doc page: **API reference**. [#802](https://github.com/pdm-project/pdm/issues/802) ### Dependencies - Switch back from `atoml` to `tomlkit` as the style-preserving TOML parser. The latter has supported TOML v1.0.0. [#809](https://github.com/pdm-project/pdm/issues/809) ### Miscellany - Cache the latest version of PDM for one week to reduce the request frequency. [#800](https://github.com/pdm-project/pdm/issues/800) Release v1.11.3 (2021-12-15) ---------------------------- ### Features & Improvements - Change the default version save strategy to `minimum`, without upper bounds. [#787](https://github.com/pdm-project/pdm/issues/787) ### Bug Fixes - Fix the patching of sysconfig in PEP 582 initialization script. [#796](https://github.com/pdm-project/pdm/issues/796) ### Miscellany - Fix an installation failure of the bootstrap script on macOS Catalina. [#793](https://github.com/pdm-project/pdm/issues/793) - Add a basic benchmarking script. [#794](https://github.com/pdm-project/pdm/issues/794) Release v1.11.2 (2021-12-10) ---------------------------- ### Bug Fixes - Fix the resolution order to reduce the loop number to find a conflict. [#781](https://github.com/pdm-project/pdm/issues/781) - Patch the functions in `sysconfig` to return the PEP 582 scheme in `pdm run`. [#784](https://github.com/pdm-project/pdm/issues/784) ### Dependencies - Remove the upper bound of version constraints for most dependencies, except for some zero-versioned ones. [#787](https://github.com/pdm-project/pdm/issues/787) Release v1.11.1 (2021-12-08) ---------------------------- ### Features & Improvements - Support `--pre/--prerelease` option for `pdm add` and `pdm update`. It will allow prereleases to be pinned. [#774](https://github.com/pdm-project/pdm/issues/774) - Improve the error message when python is found but not meeting the python requirement. [#777](https://github.com/pdm-project/pdm/issues/777) ### Bug Fixes - Fix a bug that `git+https` candidates cannot be resolved. [#771](https://github.com/pdm-project/pdm/issues/771) - Fix an infinite resolution loop by resolving the top-level packages first. Also deduplicate the lines from the same requirement in the error output. [#776](https://github.com/pdm-project/pdm/issues/776) ### Miscellany - Fix the install script to use a zipapp of virtualenv when it isn't installed. [#780](https://github.com/pdm-project/pdm/issues/780) Release v1.11.0 (2021-11-30) ---------------------------- ### Features & Improvements - Move `version` from `[project]` table to `[tool.pdm]` table, delete `classifiers` from `dynamic`, and warn usage about the deprecated usages. [#748](https://github.com/pdm-project/pdm/issues/748) - Add support for Conda environments in addition to Python virtual environments. [#749](https://github.com/pdm-project/pdm/issues/749) - Add support for saving only the lower bound `x >= VERSION` when adding dependencies. [#752](https://github.com/pdm-project/pdm/issues/752) - Improve the error message when resolution fails. [#754](https://github.com/pdm-project/pdm/issues/754) ### Bug Fixes - Switch to self-implemented `pdm list --freeze` to fix a bug due to Pip's API change. [#533](https://github.com/pdm-project/pdm/issues/533) - Fix an infinite loop issue when resolving candidates with incompatible `requires-python`. [#744](https://github.com/pdm-project/pdm/issues/744) - Fix the python finder to support pyenv-win. [#745](https://github.com/pdm-project/pdm/issues/745) - Fix the ANSI color output for Windows cmd and Powershell terminals. [#753](https://github.com/pdm-project/pdm/issues/753) ### Removals and Deprecations - Remove `-s/--section` option from all previously supported commands. Use `-G/--group` instead. [#756](https://github.com/pdm-project/pdm/issues/756) Release v1.10.3 (2021-11-18) ---------------------------- ### Bug Fixes - Use `importlib` to replace `imp` in the `sitecustomize` module for Python 3. [#574](https://github.com/pdm-project/pdm/issues/574) - Fix the lib paths under non-isolated build. [#740](https://github.com/pdm-project/pdm/issues/740) - Exclude the dependencies with extras in the result of `pdm export`. [#741](https://github.com/pdm-project/pdm/issues/741) Release v1.10.2 (2021-11-14) ---------------------------- ### Features & Improvements - Add a new option `-s/--site-packages` to `pdm run` as well as a script config item. When it is set to `True`, site-packages from the selected interpreter will be loaded into the running environment. [#733](https://github.com/pdm-project/pdm/issues/733) ### Bug Fixes - Now `NO_SITE_PACKAGES` isn't set in `pdm run` if the executable is out of local packages. [#733](https://github.com/pdm-project/pdm/issues/733) Release v1.10.1 (2021-11-09) ---------------------------- ### Features & Improvements - Isolate the project environment with system site packages in `pdm run`, but keep them seen when PEP 582 is enabled. [#708](https://github.com/pdm-project/pdm/issues/708) ### Bug Fixes - Run `pip` with `--isolated` when building wheels. In this way some env vars like `PIP_REQUIRE_VIRTUALENV` can be ignored. [#669](https://github.com/pdm-project/pdm/issues/669) - Fix the install script to ensure `pip` is not DEBUNDLED. [#685](https://github.com/pdm-project/pdm/issues/685) - Fix a bug that when `summary` is `None`, the lockfile can't be generated. [#719](https://github.com/pdm-project/pdm/issues/719) - `${PROJECT_ROOT}` should be written in the URL when relative path is given. [#721](https://github.com/pdm-project/pdm/issues/721) - Fix a bug that when project table already exists, `pdm import` can't merge the settings correctly. [#723](https://github.com/pdm-project/pdm/issues/723) Release v1.10.0 (2021-10-25) ---------------------------- ### Features & Improvements - Add `--no-sync` option to `update` command. [#684](https://github.com/pdm-project/pdm/issues/684) - Support `find_links` source type. It can be specified via `type` key of `[[tool.pdm.source]]` table. [#694](https://github.com/pdm-project/pdm/issues/694) - Add `--dry-run` option to `add`, `install` and `remove` commands. [#698](https://github.com/pdm-project/pdm/issues/698) ### Bug Fixes - Remove trailing whitespace with terminal output of tables (via `project.core.ui.display_columns`), fixing unnecessary wrapping due to / with empty lines full of spaces in case of long URLs in the last column. [#680](https://github.com/pdm-project/pdm/issues/680) - Include files should be installed under venv's base path. [#682](https://github.com/pdm-project/pdm/issues/682) - Ensure the value of `check_update` is boolean. [#689](https://github.com/pdm-project/pdm/issues/689) ### Improved Documentation - Update the contributing guide, remove the usage of `setup_dev.py` in favor of `pip install`. [#676](https://github.com/pdm-project/pdm/issues/676) Release v1.9.0 (2021-10-12) --------------------------- ### Bug Fixes - Fix a bug that `requires-python` is not recognized in candidates evaluation. [#657](https://github.com/pdm-project/pdm/issues/657) - Fix the path order when pdm run so that executables in local packages dir are found first. [#678](https://github.com/pdm-project/pdm/issues/678) ### Dependencies - Update `installer` to `0.3.0`, fixing a bug that broke installation of some packages with unusual wheel files. [#653](https://github.com/pdm-project/pdm/issues/653) - Change `packaging` and `typing-extensions` to direct dependencies. [#674](https://github.com/pdm-project/pdm/issues/674) ### Refactor - `requires-python` now participates in the resolution as a dummy requirement. [#658](https://github.com/pdm-project/pdm/issues/658) Release v1.8.5 (2021-09-16) --------------------------- ### Bug Fixes - Fix the error of regex to find the shebang line. [#656](https://github.com/pdm-project/pdm/issues/656) Release v1.8.4 (2021-09-15) --------------------------- ### Features & Improvements - Support `--no-isolation` option for `install`, `lock`, `update`, `remove`, `sync` commands. [#640](https://github.com/pdm-project/pdm/issues/640) - Make `project_max_depth` configurable and default to `5`. [#643](https://github.com/pdm-project/pdm/issues/643) ### Bug Fixes - Don't try `pdm-pep517` backend on Python 2.7 when installing self as editable. [#640](https://github.com/pdm-project/pdm/issues/640) - Fix a bug that existing shebang can't be replaced correctly. [#651](https://github.com/pdm-project/pdm/issues/651) - Fix the version range saving for prerelease versions. [#654](https://github.com/pdm-project/pdm/issues/654) Release v1.8.3 (2021-09-07) --------------------------- ### Features & Improvements - Allow to build in non-isolated environment, to enable optional speedups depending on the environment. [#635](https://github.com/pdm-project/pdm/issues/635) ### Bug Fixes - Don't copy `*-nspkg.pth` files in `install_cache` mode. It will still work without them. [#623](https://github.com/pdm-project/pdm/issues/623) Release v1.8.2 (2021-09-01) --------------------------- ### Bug Fixes - Fix the removal issue of standalone pyc files [#633](https://github.com/pdm-project/pdm/issues/633) Release v1.8.1 (2021-08-26) --------------------------- ### Features & Improvements - Add `-r/--reinstall` option to `sync` command to force re-install the existing dependencies. [#601](https://github.com/pdm-project/pdm/issues/601) - Show update hint after every pdm command. [#603](https://github.com/pdm-project/pdm/issues/603) - `pdm cache clear` can clear cached installations if not needed any more. [#604](https://github.com/pdm-project/pdm/issues/604) ### Bug Fixes - Fix the editable install script so that `setuptools` won't see the dependencies under local packages. [#601](https://github.com/pdm-project/pdm/issues/601) - Preserve the executable bit when installing wheels. [#606](https://github.com/pdm-project/pdm/issues/606) - Write PEP 610 metadata `direct_url.json` when installing wheels. [#607](https://github.com/pdm-project/pdm/issues/607) - Fix a bug that `*` fails to be converted as `SpecifierSet`. [#609](https://github.com/pdm-project/pdm/issues/609) ### Refactor - Build editable packages are into wheels via PEP 660 build backend. Now all installations are unified into wheels. [#612](https://github.com/pdm-project/pdm/issues/612) Release v1.8.0 (2021-08-16) --------------------------- ### Features & Improvements - Added a new mode `--json` to the list command which outputs the dependency graph as a JSON document. [#583](https://github.com/pdm-project/pdm/issues/583) - Add a new config `feature.install_cache`. When it is turned on, wheels will be installed into a centralized package repo and create `.pth` files under project packages directory to link to the cached package. [#589](https://github.com/pdm-project/pdm/issues/589) ### Bug Fixes - Fix env vars in source URLs not being expanded in all cases. [#570](https://github.com/pdm-project/pdm/issues/570) - Fix the weird output of `pdm show`. [#580](https://github.com/pdm-project/pdm/issues/580) - Prefer `~/.pyenv/shims/python3` as the pyenv interpreter. [#590](https://github.com/pdm-project/pdm/issues/590) - Fix a bug that installing will download candidates that do not match the locked hashes. [#596](https://github.com/pdm-project/pdm/issues/596) ### Improved Documentation - Added instructions to the Contributing section for creating news fragments [#573](https://github.com/pdm-project/pdm/issues/573) ### Removals and Deprecations - Deprecate `-s/--section` option in favor of `-G/--group`. [#591](https://github.com/pdm-project/pdm/issues/591) ### Refactor - Switch to a self-implemented version of uninstaller. [#586](https://github.com/pdm-project/pdm/issues/586) - `pdm/installers/installers.py` is renamed to `pdm/installers/manager.py` to be more accurate. The `Installer` class under that file is renamed to `InstallerManager` and is exposed in the `pdm.core.Core` object for overriding. The new `pdm/installers/installers.py` contains some installation implementations. [#589](https://github.com/pdm-project/pdm/issues/589) - Switch from `pkg_resources.Distribution` to the implementation of `importlib.metadata`. [#592](https://github.com/pdm-project/pdm/issues/592) Release v1.7.2 (2021-07-30) --------------------------- ### Bug Fixes - Remove the existing files before installing. [#565](https://github.com/pdm-project/pdm/issues/565) - Deduplicate the plugins list. [#566](https://github.com/pdm-project/pdm/issues/566) Release v1.7.1 (2021-07-29) --------------------------- ### Bug Fixes - Accept non-canonical distribution name in the wheel's dist-info directory name. [#529](https://github.com/pdm-project/pdm/issues/529) - Prefer requirements with narrower version constraints or allowing prereleases to find matches. [#551](https://github.com/pdm-project/pdm/issues/551) - Use the underlying real executable path for writing shebangs. [#553](https://github.com/pdm-project/pdm/issues/553) - Fix a bug that extra markers cannot be extracted when combined with other markers with "and". [#559](https://github.com/pdm-project/pdm/issues/559) - Fix a bug that redacted credentials in source urls get overwritten with the plain text after locking. [#561](https://github.com/pdm-project/pdm/issues/561) ### Refactor - Use installer as the wheel installer, replacing `distlib`. [#519](https://github.com/pdm-project/pdm/issues/519) Release v1.7.0 (2021-07-20) --------------------------- ### Features & Improvements - Support showing individual fields by `--` options in pdm show. When no package is given, show this project. [#527](https://github.com/pdm-project/pdm/issues/527) - Add `--freeze` option to `pdm list` command which shows the dependencies list as pip's requirements.txt format. [#531](https://github.com/pdm-project/pdm/issues/531) ### Bug Fixes - Fix the path manipulation on Windows, now the PEP 582 path is prepended to the `PYTHONPATH`. [#522](https://github.com/pdm-project/pdm/issues/522) - Fix the handling of auth prompting: will try keyring in non-verbose mode. [#523](https://github.com/pdm-project/pdm/issues/523) - Recognize old entry point name "pdm.plugin" for backward-compatibility. [#530](https://github.com/pdm-project/pdm/issues/530) - Match the VCS scheme in case-insensitive manner. [#537](https://github.com/pdm-project/pdm/issues/537) - Use the default permission bits when writing project files. [#542](https://github.com/pdm-project/pdm/issues/542) - Fix the VCS url to be consistent between lock and install. [#547](https://github.com/pdm-project/pdm/issues/547) ### Improved Documentation - Add installation instructions for Scoop. [#522](https://github.com/pdm-project/pdm/issues/522) ### Dependencies - Update `pdm-pep517` to `0.8.0`. [#524](https://github.com/pdm-project/pdm/issues/524) - Switch from `toml` to `tomli`. [#541](https://github.com/pdm-project/pdm/issues/541) ### Refactor - Separate the build env into two different levels for better caching. [#541](https://github.com/pdm-project/pdm/issues/541) - Refactor the build part into smaller functions. [#543](https://github.com/pdm-project/pdm/issues/543) Release v1.6.4 (2021-06-23) --------------------------- ### Features & Improvements - Extract package name from egg-info in filename when eligible. Remove the patching code of resolvelib's inner class. [#441](https://github.com/pdm-project/pdm/issues/441) - Support installing packages from subdirectories of VCS repository. [#507](https://github.com/pdm-project/pdm/issues/507) - Add an install script to bootstrap PDM quickly without help of other tools. Modify docs to recommend this installation method. [#508](https://github.com/pdm-project/pdm/issues/508) - Add a new subcommand `plugin` to manage pdm plugins, including `add`, `remove` and `list` commands. [#510](https://github.com/pdm-project/pdm/issues/510) ### Bug Fixes - Don't monkeypatch the internal class of `resolvelib` any more. This makes PDM more stable across updates of sub-dependencies. [#515](https://github.com/pdm-project/pdm/issues/515) ### Miscellany - Clear the type errors from mypy. [#261](https://github.com/pdm-project/pdm/issues/261) Release v1.6.3 (2021-06-17) --------------------------- ### Features & Improvements - Add an option `-u/--unconstrained` to support unconstraining version specifiers when adding packages. [#501](https://github.com/pdm-project/pdm/issues/501) ### Bug Fixes - Fix the format of dependency arrays when a new value is appended. [#487](https://github.com/pdm-project/pdm/issues/487) - Allow missing email attribute for authors and maintainers. [#492](https://github.com/pdm-project/pdm/issues/492) - Fix a bug that editable install shouldn't require pyproject.toml to be valid. [#497](https://github.com/pdm-project/pdm/issues/497) - Fix a bug on macOS that purelib and platlib paths of isolated build envs cannot be substituted correctly if the Python is a framework build. [#502](https://github.com/pdm-project/pdm/issues/502) - Fix the version sort of candidates. [#506](https://github.com/pdm-project/pdm/issues/506) Release v1.6.2 (2021-05-31) --------------------------- No significant changes. Release v1.6.1 (2021-05-31) --------------------------- No significant changes. Release v1.6.0 (2021-05-31) --------------------------- ### Features & Improvements - Use a new approach to determine the packages to be installed. This requires a quick resolution step before installation. [#456](https://github.com/pdm-project/pdm/issues/456) - `pdm export` no longer produces requirements file applicable for all platforms due to the new approach. [#456](https://github.com/pdm-project/pdm/issues/456) - Add structural typing for requirements module. Refactor the requirements module for that purpose. [#433](https://github.com/pdm-project/pdm/issues/433) - Introduce `--no-editable` option to install non-editable versions of all packages. [#443](https://github.com/pdm-project/pdm/issues/443) - Introduce `--no-self` option to prevent the project itself from being installed. [#444](https://github.com/pdm-project/pdm/issues/444) - Add a default `.gitignore` file in the `__pypackages__` directory. [#446](https://github.com/pdm-project/pdm/issues/446) - Check if the lock file version is compatible with PDM program before installation. [#463](https://github.com/pdm-project/pdm/issues/463) - Expose the project root path via `PDM_PROJECT_ROOT` env var. Change to the project root when executing scripts. [#470](https://github.com/pdm-project/pdm/issues/470) - Fix a bug that installation resolution doesn't respect the requirement markers from pyproject config. [#480](https://github.com/pdm-project/pdm/issues/480) ### Bug Fixes - Changing to multiline breaks the parsing of TOML document. [#462](https://github.com/pdm-project/pdm/issues/462) - Fix a bug that transient dependencies of conditional requirements can't be resolved. [#472](https://github.com/pdm-project/pdm/issues/472) - Fix a bug that invalid wheels are rejected while they are acceptable for resolution. [#473](https://github.com/pdm-project/pdm/issues/473) - Fix a bug that build environment is not fully isolated with the hosted environment. [#477](https://github.com/pdm-project/pdm/issues/477) - Ensure the lock file is compatible before looking for the locked candidates. [#484](https://github.com/pdm-project/pdm/issues/484) ### Improved Documentation - Fix 404 links in documentation. [#472](https://github.com/pdm-project/pdm/issues/472) ### Dependencies - Migrate from `tomlkit` to `atoml` as the style-preserving TOML parser and writer. [#465](https://github.com/pdm-project/pdm/issues/465) ### Removals and Deprecations - Remove the warning of `--dev` flag for older versions of PDM. [#444](https://github.com/pdm-project/pdm/issues/444) ### Miscellany - Add Python 3.10 beta CI job. [#457](https://github.com/pdm-project/pdm/issues/457) Release v1.5.3 (2021-05-10) --------------------------- ### Features & Improvements - Support passing options to the build backends via `--config-setting`. [#452](https://github.com/pdm-project/pdm/issues/452) ### Bug Fixes - Seek for other sitecustomize.py to import. [#422](https://github.com/pdm-project/pdm/issues/422) - Fix an unescaped single quote in fish completion script. [#423](https://github.com/pdm-project/pdm/issues/423) - The hashes of a remote file candidate should be calculated from the link itself. [#450](https://github.com/pdm-project/pdm/issues/450) ### Dependencies - Remove `keyring` as a dependency and guide users to install it when it is not available. [#442](https://github.com/pdm-project/pdm/issues/442) - Specify the minimum version of `distlib`. [#447](https://github.com/pdm-project/pdm/issues/447) ### Miscellany - Add log output about found candidates and their origin. [#421](https://github.com/pdm-project/pdm/issues/421) - Add [mypy](https://github.com/python/mypy) pre-commit hook [#427](https://github.com/pdm-project/pdm/issues/427) - Improve type safety of `pdm.cli.actions` [#428](https://github.com/pdm-project/pdm/issues/428) - Fix wrong mypy configuration. [#451](https://github.com/pdm-project/pdm/issues/451) Release v1.5.2 (2021-04-27) --------------------------- ### Features & Improvements - Allow `pdm use` with no argument given, which will list all available pythons for pick. [#409](https://github.com/pdm-project/pdm/issues/409) ### Bug Fixes - Inform user to enable PEP 582 for development script to work. [#404](https://github.com/pdm-project/pdm/issues/404) - Check the existence of pyenv shim Python interpreter before using it. [#406](https://github.com/pdm-project/pdm/issues/406) - Fix a bug that executing `setup.py` failed for NameError. [#407](https://github.com/pdm-project/pdm/issues/407) - Check before setting the PYTHONPATH environment variable for PEP582 [#410](https://github.com/pdm-project/pdm/issues/410) - Fix development setup error. [#415](https://github.com/pdm-project/pdm/issues/415) ### Dependencies - Update pip to 21.1 and fix compatibility issues. [#412](https://github.com/pdm-project/pdm/issues/412) Release v1.5.1 (2021-04-22) --------------------------- ### Bug Fixes - Make func translate_sections pure to avoid exporting requirements in random order. [#401](https://github.com/pdm-project/pdm/issues/401) - Expand the variables in install requirements' attributes for build. [#402](https://github.com/pdm-project/pdm/issues/402) Release v1.5.0 (2021-04-20) --------------------------- ### Features & Improvements - Include dev dependencies by default for `install` and `sync` commands. Add a new option `--prod/--production` to exclude them. Improve the dependency selection logic to be more convenient to use — the more common the usage is, the shorter the command is. [#391](https://github.com/pdm-project/pdm/issues/391) ### Bug Fixes - Enquote executable path to ensure generating valid scripts. [#387](https://github.com/pdm-project/pdm/issues/387) - Consider hashes when fetching artifact link for build. [#389](https://github.com/pdm-project/pdm/issues/389) - Consider the sources settings when building. [#399](https://github.com/pdm-project/pdm/issues/399) ### Improved Documentation - New pdm setting `source-includes` to mark files to be included only in sdist builds. [#390](https://github.com/pdm-project/pdm/issues/390) ### Dependencies - Update `pdm-pep517` to `0.7.0`; update `resolvelib` to` 0.7.0`. [#390](https://github.com/pdm-project/pdm/issues/390) ### Removals and Deprecations - Deprecate the usage of `-d/--dev` option in `install` and `sync` commands. [#391](https://github.com/pdm-project/pdm/issues/391) Release v1.5.0b1 (2021-04-12) ----------------------------- ### Features & Improvements - Improve the env builder to run in isolated mode. [#384](https://github.com/pdm-project/pdm/issues/384) ### Bug Fixes - Remove the incompatible code from the files that will be run in-process. [#375](https://github.com/pdm-project/pdm/issues/375) - Get the correct Python ABI tag of selected interpreter [#378](https://github.com/pdm-project/pdm/issues/378) - Error out when doing `pdm run` on a directory not initialized yet. - Give warning message when the project automatically fallbacks to the global project. ### Dependencies - Upgrade `resolvelib` to `0.6.0`. [#381](https://github.com/pdm-project/pdm/issues/381) ### Miscellany - refactor `pdm.models.readers` to improve typing support [#321](https://github.com/pdm-project/pdm/issues/321) - Add a basic integration test for cross-python check. [#377](https://github.com/pdm-project/pdm/issues/377) - Refactor the `project.python_executable` to `project.python` that contains all info of the interpreter. [#382](https://github.com/pdm-project/pdm/issues/382) - Continue refactoring Python info to extract to its own module. [#383](https://github.com/pdm-project/pdm/issues/383) - Refactor the creation of project. Release v1.5.0b0 (2021-04-03) ----------------------------- ### Features & Improvements - Add hand-written zsh completion script. [#188](https://github.com/pdm-project/pdm/issues/188) - Add a special value `:all` given to `-s/--section` to refer to all sections under the same species. Adjust `add`, `sync`, `install`, `remove` and `update` to support the new `dev-dependencies` groups. Old behavior will be kept the same. [#351](https://github.com/pdm-project/pdm/issues/351) - `dev-dependencies` is now a table of dependencies groups, where key is the group name and value is an array of dependencies. These dependencies won't appear in the distribution's metadata. `dev-dependencies` of the old format will turn into `dev` group under `dev-dependencies`. [#351](https://github.com/pdm-project/pdm/issues/351) - Move `dev-dependencies`, `includes`, `excludes` and `package-dir` out from `[project]` table to `[tool.pdm]` table. The migration will be done automatically if old format is detected. [#351](https://github.com/pdm-project/pdm/issues/351) - Throws an error with meaningful message when no candidate is found for one requirement. [#357](https://github.com/pdm-project/pdm/issues/357) - Support `--dry-run` option for `update` command to display packages that need update, install or removal. Add `--top` option to limit to top level packages only. [#358](https://github.com/pdm-project/pdm/issues/358) - Full-featured completion scripts for Zsh and Powershell - section selection, package name autocompletion and so on. Windows is a first-class citizen! [#367](https://github.com/pdm-project/pdm/issues/367) - Support non-interactive `init` command via `-n/--non-interactive` option. No question will be asked in this mode. [#368](https://github.com/pdm-project/pdm/issues/368) - Show project packages path(PEP 582) in the output of `pdm info`, also add an option `--packages` to show that value only. [#372](https://github.com/pdm-project/pdm/issues/372) ### Bug Fixes - Fix a bug that pure python libraries are not loaded to construct the WorkingSet. [#346](https://github.com/pdm-project/pdm/issues/346) - Don't write ` ## Feature highlights - Simple and fast dependency resolver, mainly for large binary distributions. - A [PEP 517] build backend. - [PEP 621] project metadata. - Flexible and powerful plug-in system. - Versatile user scripts. - Install Pythons using [indygreg's python-build-standalone](https://github.com/indygreg/python-build-standalone). - Opt-in centralized installation cache like [pnpm]. [pep 517]: https://www.python.org/dev/peps/pep-0517 [pep 621]: https://www.python.org/dev/peps/pep-0621 [pnpm]: https://pnpm.io/motivation#saving-disk-space-and-boosting-installation-speed ## Installation PDM requires Python 3.9+ to be installed. It works on multiple platforms including Windows, Linux and macOS. !!! note You can still have your project working on lower Python versions, read how to do it [here](usage/project.md#working-with-python-37). ### Recommended installation method PDM requires python version 3.9 or higher. Like Pip, PDM provides an installation script that will install PDM into an isolated environment. === "Linux/Mac" ```bash curl -sSL https://pdm-project.org/install-pdm.py | python3 - ``` === "Windows" ```powershell powershell -ExecutionPolicy ByPass -c "irm https://pdm-project.org/install-pdm.py | py -" ``` !!! note On Windows, if you do not have the optional `py` launcher installed (including if you installed Python through the Microsoft store), replace `py` with `python`. For security reasons, you should verify the checksum of `install-pdm.py`. It can be downloaded from [install-pdm.py.sha256](https://pdm-project.org/install-pdm.py.sha256). For example, on Linux/Mac: ```bash curl -sSLO https://pdm-project.org/install-pdm.py curl -sSL https://pdm-project.org/install-pdm.py.sha256 | shasum -a 256 -c - # Run the installer python3 install-pdm.py [options] ``` The installer will install PDM into the user site and the location depends on the system: - `$HOME/.local/bin` for Unix - `$HOME/Library/Python//bin` for MacOS - `%APPDATA%\Python\Scripts` on Windows You can pass additional options to the script to control how PDM is installed: ```bash usage: install-pdm.py [-h] [-v VERSION] [--prerelease] [--remove] [-p PATH] [-d DEP] optional arguments: -h, --help show this help message and exit -v VERSION, --version VERSION | envvar: PDM_VERSION Specify the version to be installed, or HEAD to install from the main branch --prerelease | envvar: PDM_PRERELEASE Allow prereleases to be installed --remove | envvar: PDM_REMOVE Remove the PDM installation -p PATH, --path PATH | envvar: PDM_HOME Specify the location to install PDM -d DEP, --dep DEP | envvar: PDM_DEPS Specify additional dependencies, can be given multiple times ``` You can either pass the options after the script or set the env var value. ### Other installation methods === "Homebrew" ```bash brew install pdm ``` === "Scoop" ``` scoop bucket add frostming https://github.com/frostming/scoop-frostming.git scoop install pdm ``` === "uv" ```bash uv tool install pdm ``` === "pipx" ```bash pipx install pdm ``` Install the head version of GitHub repository. Make sure you have installed [Git LFS](https://git-lfs.github.com/) on your system. ```bash pipx install git+https://github.com/pdm-project/pdm.git@main#egg=pdm ``` To install PDM with all features: ```bash pipx install pdm[all] ``` See also: === "pip" ```bash pip install --user pdm ``` === "asdf" Assuming you have [asdf](https://asdf-vm.com/) installed. ``` asdf plugin add pdm asdf install pdm latest asdf local pdm latest ``` === "inside project" By copying the [Pyprojectx](https://pyprojectx.github.io/) wrapper scripts to a project, you can install PDM as (npm-style) dev dependency inside that project. This allows different projects/branches to use different PDM versions. To [initialize a new or existing project](https://pyprojectx.github.io/usage/#initialize-a-new-or-existing-project), cd into the project folder and: === "Linux/Mac" ``` curl -LO https://github.com/pyprojectx/pyprojectx/releases/latest/download/wrappers.zip && unzip wrappers.zip && rm -f wrappers.zip ./pw --add pdm ``` === "Windows" ```powershell Invoke-WebRequest https://github.com/pyprojectx/pyprojectx/releases/latest/download/wrappers.zip -OutFile wrappers.zip; Expand-Archive -Path wrappers.zip -DestinationPath .; Remove-Item -Path wrappers.zip .\pw --add pdm ``` When installing pdm with this method, you need to run all `pdm` commands through the `pw` wrapper: === "Linux/Mac/Windows" ``` ./pw pdm install ``` ### Update the PDM version ```bash pdm self update ``` ### Uninstallation If you need to remove PDM from your system, you can use the following script: === "Linux/Mac" ```bash curl -sSL https://pdm-project.org/install-pdm.py | python3 - --remove ``` === "Windows" ```powershell powershell -ExecutionPolicy ByPass -c "irm https://pdm-project.org/install-pdm.py | py - --remove" ``` If you installed PDM using a third-party package management tool like Homebrew, you can also uninstall PDM using the tool's uninstall method, such as `brew uninstall pdm`. ## Packaging Status [![Packaging status](https://repology.org/badge/vertical-allrepos/pdm.svg)](https://repology.org/project/pdm/versions) ## Shell Completion PDM supports generating completion scripts for Bash, Zsh, Fish or Powershell. Here are some common locations for each shell: === "Bash" ```bash pdm completion bash > /etc/bash_completion.d/pdm.bash-completion # Requires root (sudo). For an alternative, see next pdm completion bash > ~/.bash_completion # Does not require root (sudo). Installed only for your user account ``` === "Zsh" ```bash # Make sure ~/.zfunc is added to fpath, before compinit. pdm completion zsh > ~/.zfunc/_pdm ``` Oh-My-Zsh: ```bash mkdir $ZSH_CUSTOM/plugins/pdm pdm completion zsh > $ZSH_CUSTOM/plugins/pdm/_pdm ``` Then make sure pdm plugin is enabled in ~/.zshrc === "Fish" ```bash pdm completion fish > ~/.config/fish/completions/pdm.fish ``` === "Powershell" ```ps1 # Create a directory to store completion scripts mkdir $PROFILE\..\Completions echo @' Get-ChildItem "$PROFILE\..\Completions\" | ForEach-Object { . $_.FullName } '@ | Out-File -Append -Encoding utf8 $PROFILE # Generate script Set-ExecutionPolicy Unrestricted -Scope CurrentUser pdm completion powershell | Out-File -Encoding utf8 $PROFILE\..\Completions\pdm_completion.ps1 ``` ## Virtualenv and PEP 582 PDM offers experimental support for [PEP 582](https://www.python.org/dev/peps/pep-0582/) as an opt-in feature, in addition to virtualenv management. Although [the Python Steering Council has rejected PEP 582][rejected], you can still test it out using PDM. To learn more about the two modes, refer to the relevant chapters on [Working with virtualenv](usage/venv.md) and [Working with PEP 582](usage/pep582.md). [rejected]: https://discuss.python.org/t/pep-582-python-local-packages-directory/963/430 ## PDM Eco-system [Awesome PDM](https://github.com/pdm-project/awesome-pdm) is a curated list of awesome PDM plugins and resources. ## Sponsors

pdm-2.23.1/docs/overrides/000077500000000000000000000000001477560627500153345ustar00rootroot00000000000000pdm-2.23.1/docs/overrides/main.html000066400000000000000000000033741477560627500171550ustar00rootroot00000000000000{% extends 'base.html' %} {% block announce %} {% include ".icons/octicons/heart-fill-24.svg" %} {% endblock %} {% block footer %}
{{ super() }} {% endblock %} pdm-2.23.1/docs/reference/000077500000000000000000000000001477560627500152705ustar00rootroot00000000000000pdm-2.23.1/docs/reference/api.md000066400000000000000000000004721477560627500163660ustar00rootroot00000000000000# API Reference ::: pdm.core.Core options: show_root_heading: yes show_source: false heading_level: 2 ::: pdm.core.Project options: show_root_heading: yes show_source: false heading_level: 2 ## Signals +++ 1.12.0 ::: pdm.signals options: heading_level: 3 pdm-2.23.1/docs/reference/build.md000066400000000000000000000041011477560627500167050ustar00rootroot00000000000000# Build Configuration `pdm` uses the [PEP 517](https://www.python.org/dev/peps/pep-0517/) to build the package. It acts as a build frontend that calls the build backend to build the package. A build backend is what drives the build system to build source distributions and wheels from arbitrary source trees. If you run [`pdm init`](../reference/cli.md#init), PDM will let you choose the build backend to use. Unlike other package managers, PDM does not force you to use a specific build backend. You can choose the one you like. Here is a list of build backends and corresponding configurations initially supported by PDM: === "pdm-backend" `pyproject.toml` configuration: ```toml [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" ``` [:book: Read the docs](https://backend.pdm-project.org/) === "setuptools" `pyproject.toml` configuration: ```toml [build-system] requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" ``` [:book: Read the docs](https://setuptools.pypa.io/) === "flit" `pyproject.toml` configuration: ```toml [build-system] requires = ["flit_core >=3.2,<4"] build-backend = "flit_core.buildapi" ``` [:book: Read the docs](https://flit.pypa.io/) === "hatchling" `pyproject.toml` configuration: ```toml [build-system] requires = ["hatchling"] build-backend = "hatchling.build" ``` [:book: Read the docs](https://hatch.pypa.io/) === "maturin" `pyproject.toml` configuration: ```toml [build-system] requires = ["maturin>=1.4,<2.0"] build-backend = "maturin" ``` [:book: Read the docs](https://www.maturin.rs/) Apart from the above mentioned backends, you can also use any other backend that supports PEP 621, however, [poetry-core](https://python-poetry.org/) is not supported because it does not support reading PEP 621 metadata. !!! info If you are using a custom build backend that is not in the above list, PDM will handle the relative paths as PDM-style(`${PROJECT_ROOT}` variable). pdm-2.23.1/docs/reference/cli.md000066400000000000000000000044361477560627500163700ustar00rootroot00000000000000# CLI Reference ```python exec="1" idprefix="" import argparse import re from pdm.core import Core parser = Core().parser MONOSPACED = ("pyproject.toml", "pdm.lock", ".pdm-python", ":pre", ":post", ":all") def clean_help(help: str) -> str: # Make dunders monospaced avoiding italic markdown rendering help = re.sub(r"__([\w\d\_]+)__", r"`__\1__`", help) # Make env vars monospaced help = re.sub(r"env var: ([A-Z_]+)", r"env var: `\1`", help) for monospaced in MONOSPACED: help = re.sub(rf"\s(['\"]?{monospaced}['\"]?)", f"`{monospaced}`", help) return help def render_parser( parser: argparse.ArgumentParser, title: str, heading_level: int = 2 ) -> str: """Render the parser help documents as a string.""" result = [f"{'#' * heading_level} {title}\n"] if parser.description and title != "pdm": result.append("> " + parser.description + "\n") for group in sorted( parser._action_groups, key=lambda g: g.title.lower(), reverse=True ): if not any( bool(action.option_strings or action.dest) or isinstance(action, argparse._SubParsersAction) for action in group._group_actions ): continue result.append(f"{group.title.title()}:\n") for action in group._group_actions: if isinstance(action, argparse._SubParsersAction): for name, subparser in action._name_parser_map.items(): result.append(render_parser(subparser, name, heading_level + 1)) continue opts = [f"`{opt}`" for opt in action.option_strings] if not opts: line = f"- `{action.dest}`" else: line = f"- {', '.join(opts)}" if action.metavar: line += f" `{action.metavar}`" line += f": {clean_help(action.help)}" if action.default and action.default != argparse.SUPPRESS: default = action.default if any(opt.startswith("--no-") for opt in action.option_strings) and default is True: default = not default line += f" (default: `{default}`)" result.append(line) result.append("") return "\n".join(result) print(render_parser(parser, "pdm")) ``` pdm-2.23.1/docs/reference/configuration.md000066400000000000000000000101661477560627500204650ustar00rootroot00000000000000# Configurations [pdm-config]: ../reference/cli.md#config ## Color Theme The default theme used by PDM is as follows: | Key | Default Style | | --------- | ------------------------------------------------------------ | | `primary` | cyan | | `success` | green | | `warning` | yellow | | `error` | red | | `info` | blue | | `req` | bold green | You can change the theme colors with [`pdm config`][pdm-config] command. For example, to change the `primary` color to `magenta`: ```bash pdm config theme.primary magenta ``` Or use a hex color code: ```bash pdm config theme.success '#51c7bd' ``` ## Available Configurations The following configuration items can be retrieved and modified by [`pdm config`][pdm-config] command. !!! note "Environment Variable Overrides" If the corresponding env var is set, the value will take precedence over what is saved in the config file. ```python exec="on" from pdm.project.config import Config print("| Config Item | Description | Default Value | Available in Project | Env var |") print("| --- | --- | --- | --- | --- |") for key, value in Config._config_map.items(): print(f"| `{key}` | {value.description} | {('`%s`' % value.default) if value.should_show() else ''} | {'No' if value.global_only else 'Yes'} | {('`%s`' % value.env_var) if value.env_var else ''} |") print("""\ | `pypi..url` | The URL of custom package source | `https://pypi.org/simple` | Yes | | | `pypi..username` | The username to access custom source | | Yes | | | `pypi..password` | The password to access custom source | | Yes | | | `pypi..type` | `index` or `find_links` | `index` | Yes | | | `pypi..verify_ssl` | Verify SSL certificate when query custom source | `True` | Yes | | | `repository..url` | The URL of custom package source | `https://pypi.org/simple` | Yes | | | `repository..username` | The username to access custom repository | | Yes | | | `repository..password` | The password to access custom repository | | Yes | | | `repository..ca_certs` | Path to a PEM-encoded CA cert bundle (used for server cert verification) | The CA certificates from [certifi](https://pypi.org/project/certifi/) | Yes | | | `repository..verify_ssl` | Verify SSL certificate when uploading to repository | `True` | Yes | | """) ``` pdm-2.23.1/docs/reference/pep621.md000066400000000000000000000146421477560627500166360ustar00rootroot00000000000000# PEP 621 Metadata The project metadata are stored in the `pyproject.toml`. The specifications are defined by [PEP 621], [PEP 631] and [PEP 639]. Read the detailed specifications in the PEPs. [PEP 621]: https://www.python.org/dev/peps/pep-0621/ [PEP 631]: https://www.python.org/dev/peps/pep-0631/ [PEP 639]: https://www.python.org/dev/peps/pep-0639/ _In the following part of this document, metadata should be written under `[project]` table if not given explicitly._ ## Multiline description You can split a long description onto multiple lines, thanks to TOML support for multiline strings. Just remember to escape new lines, so the final description appears [on one line only in your package metadata](https://packaging.python.org/specifications/core-metadata/#summary). Indentation will be removed as well when escaping new lines: ```toml description = """\ Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \ Ut enim ad minim veniam, quis nostrud exercitation ullamco \ laboris nisi ut aliquip ex ea commodo consequat.\ """ ``` See [TOML's specification on strings](https://toml.io/en/v1.0.0#string). ## Package version === "Static" ```toml [project] version = "1.0.0" ``` === "Dynamic" ```toml [project] ... dynamic = ["version"] [tool.pdm] version = { source = "file", path = "mypackage/__version__.py" } ``` The version will be read from the `mypackage/__version__.py` file searching for the pattern: `__version__ = "{version}"`. Read more information about other configurations in [dynamic project version](https://backend.pdm-project.org/metadata/#dynamic-project-version) from the `pdm-backend` documentation. ## Python version The required version of Python is specified as the string `requires-python`: ```toml requires-python = ">=3.9" classifiers = [ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", ... ] ``` Note: As per [PEP 621](https://peps.python.org/pep-0621/#allow-tools-to-add-extend-data), PDM is not permitted to dynamically update the `classifiers` section like some other non-compliant tools. Thus, you should also include the appropriate [trove classifiers](https://pypi.org/classifiers/) as shown above if you plan on publishing your package on [PyPI](https://pypi.org/). ## License The license is specified as the string `license`: ```toml license = {text = "BSD-2-Clause"} classifiers = [ "License :: OSI Approved :: BSD License", ... ] ``` Note: As per [PEP 621](https://peps.python.org/pep-0621/#allow-tools-to-add-extend-data), PDM is not permitted to dynamically update the `classifiers` section like some other non-compliant tools. Thus, you should also include the appropriate [trove classifiers](https://pypi.org/classifiers/) as shown above if you plan on publishing your package on [PyPI](https://pypi.org/). ## Dependency specification The `project.dependencies` is an array of dependency specification strings following the [PEP 440](https://www.python.org/dev/peps/pep-0440/) and [PEP 508](https://www.python.org/dev/peps/pep-0508/). Examples: ```toml [project] ... dependencies = [ # Named requirement "requests", # Named requirement with version specifier "flask >= 1.1.0", # Requirement with environment marker "pywin32; sys_platform == 'win32'", # URL requirement "pip @ git+https://github.com/pypa/pip.git@20.3.1" ] ``` ## Optional dependencies You can have some requirements optional, which is similar to `setuptools`' `extras_require` parameter. ```toml [project.optional-dependencies] socks = [ 'PySocks >= 1.5.6, != 1.5.7, < 2' ] tests = [ 'ddt >= 1.2.2, < 2', 'pytest < 6', 'mock >= 1.0.1, < 4; python_version < "3.4"', ] ``` To install a group of optional dependencies: ```bash pdm install -G socks ``` `-G` option can be given multiple times to include more than one group. ## Context variables expansion Depending on which build backend you are using, PDM will expand some variables in the dependency strings. ### Environment variables === "pdm-backend" ```toml [project] dependencies = ["flask @ https://${USERNAME}:${PASSWORD}/artifacts.io/Flask-1.1.2.tar.gz"] ``` === "hatchling" ```toml [project] dependencies = ["flask @ https://{env:USERNAME}:{env:PASSWORD}/artifacts.io/Flask-1.1.2.tar.gz"] ``` Find more usages [here](https://hatch.pypa.io/dev/config/context/#environment-variables) Don't worry about credential leakage, the environment variables will be expanded when needed and kept untouched in the lock file. ### Relative paths When you add a package from a relative path, PDM will automatically save it as a relative path for `pdm-backend` and `hatchling`. For example, if you run `pdm add ./my-package`, it will result in the following line in `pyproject.toml`. === "pdm-backend" ```toml [project] dependencies = ["my-package @ file:///${PROJECT_ROOT}/my-package"] ``` === "hatchling" ```toml [project] dependencies = ["my-package @ {root:uri}/my-package"] ``` By default, hatchling doesn't support [direct references](https://hatch.pypa.io/dev/config/dependency/#direct-references) in the dependency string, you need to turn it on in `pyproject.toml`: ```toml [tool.hatch.metadata] allow-direct-references = true ``` The relative path will be expanded based on the project root when installing or locking. ## Console scripts The following content: ```toml [project.scripts] mycli = "mycli.__main__:main" ``` will be translated to `setuptools` style: ```python entry_points = { 'console_scripts': [ 'mycli=mycli.__main__:main' ] } ``` Also, `[project.gui-scripts]` will be translated to `gui_scripts` entry points group in `setuptools` style. ## Entry points Other types of entry points are given by `[project.entry-points.]` section, with the same format of `[project.scripts]`: ```toml [project.entry-points.pytest11] myplugin = "mypackage.plugin:pytest_plugin" ``` If the entry point name contains dots or other special characters, wrap it in quotes: ```toml [project.entry-points."flake8.extension"] myplugin = "mypackage.plugin:flake8_plugin" ``` pdm-2.23.1/docs/usage/000077500000000000000000000000001477560627500144365ustar00rootroot00000000000000pdm-2.23.1/docs/usage/advanced.md000066400000000000000000000214311477560627500165260ustar00rootroot00000000000000# Advanced Usage ## Automatic Testing ### Use Tox as the runner [Tox](https://tox.readthedocs.io/en/latest/) is a great tool for testing against multiple Python versions or dependency sets. You can configure a `tox.ini` like the following to integrate your testing with PDM: ```ini [tox] env_list = py{36,37,38},lint [testenv] setenv = PDM_IGNORE_SAVED_PYTHON="1" deps = pdm commands = pdm install --dev pytest tests [testenv:lint] deps = pdm commands = pdm install -G lint flake8 src/ ``` To use the virtualenv created by Tox, you should make sure you have set `pdm config python.use_venv true`. PDM then will install dependencies from [`pdm lock`](../reference/cli.md#lock) into the virtualenv. In the dedicated venv you can directly run tools by `pytest tests/` instead of `pdm run pytest tests/`. You should also make sure you don't run `pdm add/pdm remove/pdm update/pdm lock` in the test commands, otherwise the [`pdm lock`](../reference/cli.md#lock) file will be modified unexpectedly. Additional dependencies can be supplied with the `deps` config. Besides, `isolated_build` and `passenv` config should be set as the above example to make PDM work properly. To get rid of these constraints, there is a Tox plugin [tox-pdm](https://github.com/pdm-project/tox-pdm) which can ease the usage. You can install it by ```bash pip install tox-pdm ``` Or, ```bash pdm add --dev tox-pdm ``` And you can make the `tox.ini` much tidier as following, : ```ini [tox] env_list = py{36,37,38},lint [testenv] groups = dev commands = pytest tests [testenv:lint] groups = lint commands = flake8 src/ ``` See the [project's README](https://github.com/pdm-project/tox-pdm) for a detailed guidance. ### Use Nox as the runner [Nox](https://nox.thea.codes/) is another great tool for automated testing. Unlike tox, Nox uses a standard Python file for configuration. It is much easier to use PDM in Nox, here is an example of `noxfile.py`: ```python hl_lines="4" import os import nox os.environ.update({"PDM_IGNORE_SAVED_PYTHON": "1"}) @nox.session def tests(session): session.run_always('pdm', 'install', '-G', 'test', external=True) session.run('pytest') @nox.session def lint(session): session.run_always('pdm', 'install', '-G', 'lint', external=True) session.run('flake8', '--import-order-style', 'google') ``` Note that `PDM_IGNORE_SAVED_PYTHON` should be set so that PDM can pick up the Python in the virtualenv correctly. Also make sure `pdm` is available in the `PATH`. Before running nox, you should also ensure configuration item `python.use_venv` is true to enable venv reusing. ### About PEP 582 `__pypackages__` directory By default, if you run tools by [`pdm run`](../reference/cli.md#run), `__pypackages__` will be seen by the program and all subprocesses created by it. This means virtual environments created by those tools are also aware of the packages inside `__pypackages__`, which result in unexpected behavior in some cases. For `nox`, you can avoid this by adding a line in `noxfile.py`: ```python os.environ.pop("PYTHONPATH", None) ``` For `tox`, `PYTHONPATH` will not be passed to the test sessions so this isn't going to be a problem. Moreover, it is recommended to make `nox` and `tox` live in their own pipx environments so you don't need to install for every project. In this case, PEP 582 packages will not be a problem either. ## Use PDM in Continuous Integration Only one thing to keep in mind -- PDM can't be installed on Python < 3.7, so if your project is to be tested on those Python versions, you have to make sure PDM is installed on the correct Python version, which can be different from the target Python version the particular job/task is run on. Fortunately, if you are using GitHub Action, there is [pdm-project/setup-pdm](https://github.com/marketplace/actions/setup-pdm) to make this process easier. Here is an example workflow of GitHub Actions, while you can adapt it for other CI platforms. ```yaml Testing: runs-on: ${{ matrix.os }} strategy: matrix: python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] os: [ubuntu-latest, macOS-latest, windows-latest] steps: - uses: actions/checkout@v4 - name: Set up PDM uses: pdm-project/setup-pdm@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | pdm sync -d -G testing - name: Run Tests run: | pdm run -v pytest tests ``` !!! important "TIPS" For GitHub Action users, there is a [known compatibility issue](https://github.com/actions/virtual-environments/issues/2803) on Ubuntu virtual environment. If PDM parallel install is failed on that machine you should either set `parallel_install` to `false` or set env `LD_PRELOAD=/lib/x86_64-linux-gnu/libgcc_s.so.1`. It is already handled by the `pdm-project/setup-pdm` action. !!! note If your CI scripts run without a proper user set, you might get permission errors when PDM tries to create its cache directory. To work around this, you can set the HOME environment variable yourself, to a writable directory, for example: ```bash export HOME=/tmp/home ``` ## Use PDM in a multi-stage Dockerfile It is possible to use PDM in a multi-stage Dockerfile to first install the project and dependencies into `__pypackages__` and then copy this folder into the final stage, adding it to `PYTHONPATH`. ```dockerfile ARG PYTHON_BASE=3.10-slim # build stage FROM python:$PYTHON_BASE AS builder # install PDM RUN pip install -U pdm # disable update check ENV PDM_CHECK_UPDATE=false # copy files COPY pyproject.toml pdm.lock README.md /project/ COPY src/ /project/src # install dependencies and project into the local packages directory WORKDIR /project RUN pdm install --check --prod --no-editable # run stage FROM python:$PYTHON_BASE # retrieve packages from build stage COPY --from=builder /project/.venv/ /project/.venv ENV PATH="/project/.venv/bin:$PATH" # set command/entrypoint, adapt to fit your needs COPY src /project/src CMD ["python", "src/__main__.py"] ``` ## Use PDM to manage a monorepo With PDM, you can have multiple sub-packages within a single project, each with its own `pyproject.toml` file. And you can create only one `pdm.lock` file to lock all dependencies. The sub-packages can have each other as their dependencies. To achieve this, follow these steps: `project/pyproject.toml`: ```toml [dependency-groups] dev = [ "-e file:///${PROJECT_ROOT}/packages/foo-core", "-e file:///${PROJECT_ROOT}/packages/foo-cli", "-e file:///${PROJECT_ROOT}/packages/foo-app", ] ``` `packages/foo-cli/pyproject.toml`: ```toml [project] dependencies = ["foo-core"] ``` `packages/foo-app/pyproject.toml`: ```toml [project] dependencies = ["foo-core"] ``` Now, run `pdm install` in the project root, and you will get a `pdm.lock` with all dependencies locked. All sub-packages will be installed in editable mode. Look at the [🚀 Example repository](https://github.com/pdm-project/pdm-example-monorepo) for more details. ## Hooks for `pre-commit` [`pre-commit`](https://pre-commit.com/) is a powerful framework for managing git hooks in a centralized fashion. PDM already uses `pre-commit` [hooks](https://github.com/pdm-project/pdm/blob/main/.pre-commit-config.yaml) for its internal QA checks. PDM exposes also several hooks that can be run locally or in CI pipelines. ### Export `requirements.txt` This hook wraps the command `pdm export` along with any valid argument. It can be used as a hook (e.g., for CI) to ensure that you are going to check in the codebase a `requirements.txt`, which reflects the actual content of [`pdm lock`](../reference/cli.md#lock). ```yaml # export python requirements - repo: https://github.com/pdm-project/pdm rev: 2.x.y # a PDM release exposing the hook hooks: - id: pdm-export # command arguments, e.g.: args: ['-o', 'requirements.txt', '--without-hashes'] files: ^pdm.lock$ ``` ### Check `pdm.lock` is up to date with pyproject.toml This hook wraps the command `pdm lock --check` along with any valid argument. It can be used as a hook (e.g., for CI) to ensure that whenever `pyproject.toml` has a dependency added/changed/removed, that `pdm.lock` is also up to date. ```yaml - repo: https://github.com/pdm-project/pdm rev: 2.x.y # a PDM release exposing the hook hooks: - id: pdm-lock-check ``` ### Sync current working set with `pdm.lock` This hook wraps the command `pdm sync` along with any valid argument. It can be used as a hook to ensure that your current working set is synced with `pdm.lock` whenever you checkout or merge a branch. Add _keyring_ to `additional_dependencies` if you want to use your systems credential store. ```yaml - repo: https://github.com/pdm-project/pdm rev: 2.x.y # a PDM release exposing the hook hooks: - id: pdm-sync additional_dependencies: - keyring ``` pdm-2.23.1/docs/usage/config.md000066400000000000000000000364661477560627500162440ustar00rootroot00000000000000# Configure the Project PDM's `config` command works just like `git config`, except that `--list` isn't needed to show configurations. Show the current configurations: ```bash pdm config ``` Get one single configuration: ```bash pdm config pypi.url ``` Change a configuration value and store in home configuration: ```bash pdm config pypi.url "https://test.pypi.org/simple" ``` By default, the configuration are changed globally, if you want to make the config seen by this project only, add a `--local` flag: ```bash pdm config --local pypi.url "https://test.pypi.org/simple" ``` Any local configurations will be stored in `pdm.toml` under the project root directory. ## Configuration files The configuration files are searched in the following order: 1. `/pdm.toml` - The project configuration 2. `/config.toml` - The home configuration 3. `/config.toml` - The site configuration where `` is: - `$XDG_CONFIG_HOME/pdm` (`~/.config/pdm` in most cases) on Linux as defined by [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) - `~/Library/Application Support/pdm` on macOS as defined by [Apple File System Basics](https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html) - `%USERPROFILE%\AppData\Local\pdm\pdm` on Windows as defined in [Known folders](https://docs.microsoft.com/en-us/windows/win32/shell/known-folders) and `` is: - `$XDG_CONFIG_DIRS/pdm` (`/etc/xdg/pdm` in most cases) on Linux as defined by [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) - `/Library/Application Support/pdm` on macOS as defined by [Apple File System Basics](https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html) - `%ProgramData%\pdm\pdm` on Windows as defined in [Known folders](https://docs.microsoft.com/en-us/windows/win32/shell/known-folders) If `-g/--global` option is used, the first item will be replaced by `/global-project/pdm.toml`. You can find all available configuration items in [Configuration Page](../reference/configuration.md). ## Configure the Python finder By default, PDM will try to find Python interpreters in the following sources: - `venv`: The PDM virtualenv location - `path`: The `PATH` environment variable - `pyenv`: The [pyenv](https://github.com/pyenv/pyenv) install root - `rye`: The [rye](https://rye-up.com/) toolchain install root - `asdf`: The [asdf](https://asdf-vm.com/) python install root - `winreg`: The Windows registry You can unselect some of them or change the order by setting `python.providers` config key: ```bash pdm config python.providers rye # Rye source only pdm config python.providers pyenv,asdf # pyenv and asdf ``` ## Allow prereleases in resolution result By default, `pdm`'s dependency resolver will ignore prereleases unless there are no stable versions for the given version range of a dependency. This behavior can be changed by setting `allow-prereleases` to `true` in `[tool.pdm.resolution]` table: ```toml [tool.pdm.resolution] allow-prereleases = true ``` ## Configure the package indexes You can tell PDM where to to find the packages by either specifying sources in the `pyproject.toml` or via `pypi.*` configurations. Add sources in `pyproject.toml`: ```toml [[tool.pdm.source]] name = "private" url = "https://private.pypi.org/simple" verify_ssl = true ``` Change the default index via `pdm config`: ```bash pdm config pypi.url "https://test.pypi.org/simple" ``` Add extra indexes via `pdm config`: ```bash pdm config pypi.extra.url "https://extra.pypi.org/simple" ``` The available configuration options are: - `url`: The URL of the index - `verify_ssl`: (Optional)Whether to verify SSL certificates, default to true - `username`: (Optional)The username for the index - `password`: (Optional)The password for the index - `type`: (Optional) index or find_links, default to index ??? note "About the source types" By default, all sources are [PEP 503](https://www.python.org/dev/peps/pep-0503/) style "indexes" like pip's `--index-url` and `--extra-index-url`, however, you can set the type to `find_links` which contains files or links to be looked for directly. See [this answer](https://stackoverflow.com/a/46651848) for the difference between the two types. For example, to use a local directory as a source: ```toml [[tool.pdm.source]] name = "local" url = "file:///${PROJECT_ROOT}/packages" type = "find_links" ``` These configurations are read in the following order to build the final source list: - `pypi.url`, if `pypi` doesn't appear in the `name` field of any source in `pyproject.toml` - Sources in `pyproject.toml` - `pypi..url` in PDM config. You can set `pypi.ignore_stored_index` to `true` to disable all additional indexes from the PDM config and only use those specified in `pyproject.toml`. !!! TIP "Disable the default PyPI index" If you want to omit the default PyPI index, just set the source name to `pypi` and that source will **replace** it. ```toml [[tool.pdm.source]] url = "https://private.pypi.org/simple" verify_ssl = true name = "pypi" ``` ??? note "Indexes in `pyproject.toml` or config" When you want to share the indexes with other people who are going to use the project, you should add them in `pyproject.toml`. For example, some packages only exist in a private index and can't be installed if someone doesn't configure the index. Otherwise, store them in the local config which won't be seen by others. ### Respect the order of the sources By default, all sources are considered equal, packages from them are sorted by the version and wheel tags, the most matching one with the highest version is selected. In some cases you may want to return packages from the preferred source, and search for others if they are missing from the former source. PDM supports this by reading the configuration `respect-source-order`. For example: ```toml [tool.pdm.resolution] respect-source-order = true [[tool.pdm.source]] name = "private" url = "https://private.pypi.org/simple" [[tool.pdm.source]] name = "pypi" url = "https://pypi.org/simple" ``` A package will be searched from the `private` index first, and only if no matching version is found there, it will be searched from the `pypi` index. ### Specify index for individual packages You can bind packages to specific sources with `include_packages` and `exclude_packages` config under `tool.pdm.source` table. ```toml [[tool.pdm.source]] name = "private" url = "https://private.pypi.org/simple" include_packages = ["foo", "foo-*"] exclude_packages = ["bar-*"] ``` With the above configuration, any package matching `foo` or `foo-*` will only be searched from the `private` index, and any package matching `bar-*` will be searched from all indexes except `private`. Both `include_packages` and `exclude_packages` are optional and accept a list of glob patterns, and `include_packages` takes effect exclusively when the pattern matches. ### Store credentials with the index You can specify credentials in the URL with `${ENV_VAR}` variable expansion and these variables will be read from the environment variables: ```toml [[tool.pdm.source]] name = "private" url = "https://${PRIVATE_PYPI_USERNAME}:${PRIVATE_PYPI_PASSWORD}@private.pypi.org/simple" ``` ### Configure HTTPS certificates You can use a custom CA bundle or client certificate for HTTPS requests. It can be configured for both indexes(for package download) and repositories(for upload): ```bash pdm config pypi.ca_certs /path/to/ca_bundle.pem pdm config repository.pypi.ca_certs /path/to/ca_bundle.pem ``` Besides, it is possible to use the system trust store, instead of the bundled certifi certificates for verifying HTTPS certificates. This approach will typically support corporate proxy certificates without additional configuration. To use `truststore`, you need Python 3.10 or newer and install `truststore` into the same environment as PDM: ```bash pdm self add truststore ``` In addition, CA certificates specified by env vars `REQUESTS_CA_BUNDLE` and `CURL_CA_BUNDLE` will be used if they are set. ### Index configuration merging Index configurations are merged with the `name` field of `[[tool.pdm.source]]` table or `pypi.` key in the config file. This enables you to store the url and credentials separately, to avoid secrets being exposed in the source control. For example, if you have the following configuration: ```toml [[tool.pdm.source]] name = "private" url = "https://private.pypi.org/simple" ``` You can store the credentials in the config file: ```bash pdm config pypi.private.username "foo" pdm config pypi.private.password "bar" ``` PDM can retrieve the configurations for `private` index from both places. If the index requires a username and password, but they can't be found from the environment variables nor config file, PDM will prompt you to enter them. Or, if `keyring` is installed, it will be used as the credential store. PDM can use the `keyring` from either the installed package or the CLI. ## Central installation caches If a package is required by many projects on the system, each project has to keep its own copy. This can be a waste of disk space, especially for data science and machine learning projects. PDM supports _caching_ installations of the same wheel by installing it in a centralized package repository and linking to that installation in different projects. To enable it, run: ```bash pdm config install.cache on ``` It can be enabled on a per-project basis by adding the `--local` option to the command. The caches are located in `$(pdm config cache_dir)/packages`. You can view the cache usage with `pdm cache info`. Note that the cached installations are managed automatically -- they will be deleted if they are not linked to any projects. Manually deleting the caches from disk may break some projects on the system. In addition, several different link methods are supported: - `symlink`(default), create symlinks to the package files. - `hardlink`, create hard links to the package files of the cache entry. You can switch between them by running `pdm config [--local] install.cache_method `. !!! note Only packages installed from one of the package sources can be cached. ## Configure the repositories for upload When using the [`pdm publish`](../reference/cli.md#publish) command, it reads the repository secrets from the **global** config file(`/config.toml`). The content of the config is as follows: ```toml [repository.pypi] username = "frostming" password = "" [repository.company] url = "https://pypi.company.org/legacy/" username = "frostming" password = "" ca_certs = "/path/to/custom-cacerts.pem" ``` Alternatively, these credentials can be provided with env vars: ```bash export PDM_PUBLISH_REPO=... export PDM_PUBLISH_USERNAME=... export PDM_PUBLISH_PASSWORD=... export PDM_PUBLISH_CA_CERTS=... ``` A PEM-encoded Certificate Authority bundle (`ca_certs`) can be used for local / custom PyPI repositories where the server certificate is not signed by the standard [certifi](https://github.com/certifi/python-certifi/blob/master/certifi/cacert.pem) CA bundle. !!! NOTE Repositories are different from indexes in the previous section. Repositories are for publishing while indexes are for locking and resolving. They don't share the configuration. !!! TIP You don't need to configure the `url` for `pypi` and `testpypi` repositories, they are filled by default values. The username, password, and certificate authority bundle can be passed in from the command line for `pdm publish` via `--username`, `--password`, and `--ca-certs`, respectively. To change the repository config from the command line, use the [`pdm config`](../reference/cli.md#config) command: ```bash pdm config repository.pypi.username "__token__" pdm config repository.pypi.password "my-pypi-token" pdm config repository.company.url "https://pypi.company.org/legacy/" pdm config repository.company.ca_certs "/path/to/custom-cacerts.pem" ``` ## Password management with keyring When keyring is available and supported, the passwords will be stored to and retrieved from the keyring instead of writing to the config file. This supports both indexes and upload repositories. The service name will be `pdm-pypi-` for an index and `pdm-repository-` for a repository. You can enable keyring by either installing `keyring` into the same environment as PDM or installing globally. To add keyring to the PDM environment: ```bash pdm self add keyring ``` Alternatively, if you have installed a copy of keyring globally, make sure the CLI is exposed in the `PATH` env var to make it discoverable by PDM: ```bash export PATH=$PATH:path/to/keyring/bin ``` ### Password management with keyring for Azure Artifacts When trying to authenticate towards azure artifacts, this can be achieved by either using AD groups to authenticate: `pdm self add keyring artifacts-keyring` ensuring that artifacts-keyring will be used for authentication. And then adding the artifacts url to `pyproject.toml` ```toml [[tool.pdm.source]] name = "NameOfFeed" url = "https://pkgs.dev.azure.com/[org name]/_packaging/[feed name]/pypi/simple/" ``` ## Exclude specific packages and their dependencies from the lock file +++ 2.12.0 Sometimes you don't even want to include certain packages in the locked file because you are sure they won't be used by any code. In this case, you can completely skip them and their dependencies during dependency resolution: ```toml [tool.pdm.resolution] excludes = ["requests"] ``` With this config, `requests` will not be locked in the lockfile, and its dependencies such as `urllib3` and `idna` will also not show up in the resolution result, if not depended on by other packages. The installer will not be able to pick them up either. ## Passing constant arguments to every pdm invocation +++ 2.7.0 You can add extra options passed to individual pdm commands by `tool.pdm.options` configuration: ```toml [tool.pdm.options] add = ["--no-isolation", "--no-self"] install = ["--no-self"] lock = ["--no-cross-platform"] ``` These options will be added right after the command name. For instance, based on the configuration above, `pdm add requests` is equivalent to `pdm add --no-isolation --no-self requests`. ## Ignore package warnings +++ 2.10.0 You may see some warnings when resolving dependencies like this: ```bash PackageWarning: Skipping scipy@1.10.0 because it requires Python <3.12,>=3.8 but the project claims to work with Python>=3.9. Narrow down the `requires-python` range to include this version. For example, ">=3.9,<3.12" should work. warnings.warn(record.message, PackageWarning, stacklevel=1) Use `-q/--quiet` to suppress these warnings, or ignore them per-package with `ignore_package_warnings` config in [tool.pdm] table. ``` This is because the supported range of Python versions of the package doesn't cover the `requires-python` value specified in the `pyproject.toml`. You can ignore these warnings in a per-package basis by adding the following config: ```toml [tool.pdm] ignore_package_warnings = ["scipy", "tensorflow-*"] ``` Where each item is a case-insensitive glob pattern to match the package name. pdm-2.23.1/docs/usage/dependency.md000066400000000000000000000431031477560627500170770ustar00rootroot00000000000000# Manage Dependencies PDM provides a bunch of useful commands to help manage your project and dependencies. The following examples are run on Ubuntu 18.04, a few changes must be done if you are using Windows. ## Add dependencies [`pdm add`](../reference/cli.md#add) can be followed by one or several dependencies, and the dependency specification is described in [PEP 508](https://www.python.org/dev/peps/pep-0508/). Examples: ```bash pdm add requests # add requests pdm add requests==2.25.1 # add requests with version constraint pdm add requests[socks] # add requests with extra dependency pdm add "flask>=1.0" flask-sqlalchemy # add multiple dependencies with different specifiers ``` PDM also allows extra dependency groups by providing `-G/--group ` option, and those dependencies will go to `[project.optional-dependencies.]` table in the project file, respectively. You can reference other optional groups in `optional-dependencies`, even before the package is uploaded: ```toml [project] name = "foo" version = "0.1.0" [project.optional-dependencies] socks = ["pysocks"] jwt = ["pyjwt"] all = ["foo[socks,jwt]"] ``` After that, dependencies and sub-dependencies will be resolved properly and installed for you, you can view `pdm.lock` to see the resolved result of all dependencies. ### Local dependencies Local packages can be added with their paths. The path can be a file or a directory: ```bash pdm add ./sub-package pdm add ./first-1.0.0-py2.py3-none-any.whl ``` The paths MUST start with a `.`, otherwise it will be recognized as a normal named requirement. The local dependencies will be written to the `pyproject.toml` file with the URL format: ```toml [project] dependencies = [ "sub-package @ file:///${PROJECT_ROOT}/sub-package", "first @ file:///${PROJECT_ROOT}/first-1.0.0-py2.py3-none-any.whl", ] ``` ??? note "Using other build backends" If you are using `hatchling` instead of the pdm backend, the URLs would be as follows: ``` sub-package @ {root:uri}/sub-package first @ {root:uri}/first-1.0.0-py2.py3-none-any.whl ``` Other backends doesn't support encoding relative paths in the URL and will write the absolute path instead. ### URL dependencies PDM also supports downloading and installing packages directly from a web address. Examples: ```bash # Install gzipped package from a plain URL pdm add "https://github.com/numpy/numpy/releases/download/v1.20.0/numpy-1.20.0.tar.gz" # Install wheel from a plain URL pdm add "https://github.com/explosion/spacy-models/releases/download/en_core_web_trf-3.5.0/en_core_web_trf-3.5.0-py3-none-any.whl" ``` ### VCS dependencies You can also install from a git repository url or other version control systems. The following are supported: - Git: `git` - Mercurial: `hg` - Subversion: `svn` - Bazaar: `bzr` The URL should be like: `{vcs}+{url}@{rev}` Examples: ```bash # Install pip repo on tag `22.0` pdm add "git+https://github.com/pypa/pip.git@22.0" # Provide credentials in the URL pdm add "git+https://username:password@github.com/username/private-repo.git@master" # Give a name to the dependency pdm add "pip @ git+https://github.com/pypa/pip.git@22.0" # Or use the #egg fragment pdm add "git+https://github.com/pypa/pip.git@22.0#egg=pip" # Install from a subdirectory pdm add "git+https://github.com/owner/repo.git@master#egg=pkg&subdirectory=subpackage" ``` To use ssh scheme for git, just replace `https://` to `ssh://git@` Example: ```bash pdm add "wheel @ git+ssh://git@github.com/pypa/wheel.git@main" ``` Or the short non-URI form, which uses a colon(`:`) to separate the host and path: ```bash pdm add "wheel @ git+git@github.com:pypa/wheel.git@main" ``` ### Hide credentials in the URL You can hide the credentials in the URL by using the `${ENV_VAR}` variable syntax: ```toml [project] dependencies = [ "mypackage @ git+http://${VCS_USER}:${VCS_PASSWD}@test.git.com/test/mypackage.git@master" ] ``` These variables will be read from the environment variables when installing the project. ### Add development only dependencies +++ 1.5.0 PDM also supports defining groups of dependencies that are useful for development, e.g. some for testing and others for linting. We usually don't want these dependencies to appear in the distribution's metadata so using `optional-dependencies` is probably not a good idea. We can define them as development dependencies: ```bash pdm add -dG test pytest ``` This will result in a `pyproject.toml` as following: ```toml [dependency-groups] test = ["pytest"] ``` You can have several groups of development only dependencies. Unlike `optional-dependencies`, they won't appear in the package distribution metadata such as `PKG-INFO` or `METADATA`, which means the package index won't be aware of these dependencies. The schema is similar to that of `optional-dependencies`. ```toml [dependency-groups] lint = [ "flake8", "black" ] test = ["pytest", "pytest-cov"] doc = ["mkdocs"] ``` For backward-compatibility, if only `-d` or `--dev` is specified, dependencies will go to `dev` group under `[dependency-groups]` by default. !!! NOTE The same group name MUST NOT appear in both `[dependency-groups]` and `[project.optional-dependencies]`. ### Editable dependencies **Local directories** and **VCS dependencies** can be installed in [editable mode](https://pip.pypa.io/en/stable/cli/pip_install/#editable-installs). If you are familiar with `pip`, it is just like `pip install -e `. **Editable packages are allowed only in development dependencies**: !!! NOTE Editable installs are only allowed in the `dev` dependency group. Other groups, including the default, will fail with a `[PdmUsageError]`. ```bash # A relative path to the directory pdm add -e ./sub-package --dev # A file URL to a local directory pdm add -e file:///path/to/sub-package --dev # A VCS URL pdm add -e git+https://github.com/pallets/click.git@main#egg=click --dev ``` ### Save version specifiers If the package is given without a version specifier like `pdm add requests`. PDM provides three different behaviors of what version specifier is saved for the dependency, which is given by `--save-`(Assume `2.21.0` is the latest version that can be found for the dependency): - `minimum`: Save the minimum version specifier: `>=2.21.0` (default). - `compatible`: Save the compatible version specifier: `>=2.21.0,<3.0.0`. - `exact`: Save the exact version specifier: `==2.21.0`. - `wildcard`: Don't constrain version and leave the specifier to be wildcard: `*`. ### Add prereleases One can give `--pre/--prerelease` option to [`pdm add`](../reference/cli.md#add) so that prereleases are allowed to be pinned for the given packages. ## Update existing dependencies To update all dependencies in the lock file: ```bash pdm update ``` To update the specified package(s): ```bash pdm update requests ``` To update multiple groups of dependencies: ```bash pdm update -G security -G http ``` Or using comma-separated list: ```bash pdm update -G "security,http" ``` To update a given package in the specified group: ```bash pdm update -G security cryptography ``` If the group is not given, PDM will search for the requirement in the default dependencies set and raises an error if none is found. To update packages in development dependencies: ```bash # Update all default + dev-dependencies pdm update -d # Update a package in the specified group of dev-dependencies pdm update -dG test pytest ``` ### About update strategy Similarly, PDM also provides 3 different behaviors of updating dependencies and sub-dependencies, which is given by `--update-` option: - `reuse`: Keep all locked dependencies except for those given in the command line (default). - `reuse-installed`: Try to reuse the versions installed in the working set. **This will also affect the packages requested in the command line**. - `eager`: Try to lock a newer version of the packages in command line and their recursive sub-dependencies and keep other dependencies as they are. - `all`: Update all dependencies and sub-dependencies. ### Update packages to the versions that break the version specifiers One can give `-u/--unconstrained` to tell PDM to ignore the version specifiers in the `pyproject.toml`. This works similarly to the `yarn upgrade -L/--latest` command. Besides, [`pdm update`](../reference/cli.md#update_2) also supports the `--pre/--prerelease` option. ## Remove existing dependencies To remove existing dependencies from project file and the library directory: ```bash # Remove requests from the default dependencies pdm remove requests # Remove h11 from the 'web' group of optional-dependencies pdm remove -G web h11 # Remove pytest-cov from the `test` group of dependency-groups pdm remove -dG test pytest-cov ``` ## List outdated packages and the latest versions +++ 2.13.0 To list outdated packages and the latest versions: ```bash pdm outdated ``` You can pass glob patterns to filter the packages to show: ```bash pdm outdated requests* flask* ``` ## Select a subset of dependency groups to install Say we have a project with following dependencies: ```toml [project] # This is production dependencies dependencies = ["requests"] [project.optional-dependencies] # This is optional dependencies extra1 = ["flask"] extra2 = ["django"] [dependency-groups] # This is dev dependencies dev1 = ["pytest"] dev2 = ["mkdocs"] ``` | Command | What it does | Comments | | ------------------------------- | -------------------------------------------------------------------- | ------------------------- | | `pdm install` | install all groups locked in the lockfile | | | `pdm install -G extra1` | install prod deps, dev deps, and "extra1" optional group | | | `pdm install -G dev1` | install prod deps and only "dev1" dev group | | | `pdm install -G:all` | install prod deps, dev deps and "extra1", "extra2" optional groups | | | `pdm install -G extra1 -G dev1` | install prod deps, "extra1" optional group and only "dev1" dev group | | | `pdm install --prod` | install prod only | | | `pdm install --prod -G extra1` | install prod deps and "extra1" optional | | | `pdm install --prod -G dev1` | Fail, `--prod` can't be given with dev dependencies | Leave the `--prod` option | **All** development dependencies are included as long as `--prod` is not passed and `-G` doesn't specify any dev groups. Besides, if you don't want the root project to be installed, add `--no-self` option, and `--no-editable` can be used when you want all packages to be installed in non-editable versions. You may also use the pdm lock command with these options to lock only the specified groups, which will be recorded in the `[metadata]` table of the lock file. If no `--group/--prod/--dev/--no-default` option is specified, `pdm sync` and `pdm update` will operate using the groups in the lockfile. However, if any groups that are not included in the lockfile are given as arguments to the commands, PDM will raise an error. ## Dependency Overrides If none of versions of a specific package doesn't meet all the constraints, the resolution will fail. In this case, you can tell the resolver to use a specific version of the package with dependency overrides. Overrides are a useful last resort for cases in which the user knows that a dependency is compatible with a newer version of a package than the package declares, but the package has not yet been updated to declare that compatibility. For example, if a transitive dependency declares `pydantic>=1.0,<2.0`, but the user knows that the package is compatible with `pydantic>=2.0`, the user can override the declared dependency with `pydantic>=2.0,<3` to allow the resolver to continue. In PDM, there are two ways to specify overrides: ### In the project file +++ 1.12.0 You can specify the overrides in the `pyproject.toml` file, under the `[tool.pdm.resolution.overrides]` table: ```toml [tool.pdm.resolution.overrides] asgiref = "3.2.10" # exact version urllib3 = ">=1.26.2" # version range pytz = "https://mypypi.org/packages/pytz-2020.9-py3-none-any.whl" # absolute URL ``` Each entry in the table is a package name and a version specifier. The version specifier can be a version range, an exact version, or an absolute URL. ### Via CLI option +++ 2.17.0 PDM also supports reading dependency overrides from a requirements file. The file works similarly to the constraint file in pip(`--constraint constraints.txt`), and the syntax is the same as the requirements file: ``` requests==2.20.0 django==1.11.8 certifi==2018.11.17 chardet==3.0.4 idna==2.7 pytz==2019.3 urllib3==1.23 ``` Override files serve as an easy way to store the dependencies in a centralized location that can be shared by multiple projects in your organization. You can pass the constraint file to various PDM commands that would perform a resolution, such as [`pdm install`](../reference/cli.md#install), [`pdm lock`](../reference/cli.md#lock), [`pdm add`](../reference/cli.md#add), etc. ```bash pdm lock --override constraints.txt ``` This option can be supplied multiple times. Override files can also be served via a URL, e.g. `--override http://example.com/constraints.txt`, so that your organization can store and serve them in a remote server. ## Show what packages are installed Similar to `pip list`, you can list all packages installed in the packages directory: ```bash pdm list ``` ### Include and exclude groups By default, all packages installed in the working set will be listed. You can specify which groups to be listed by `--include/--exclude` options, and `include` has a higher priority than `exclude`. ```bash pdm list --include dev pdm list --exclude test ``` There is a special group `:sub`, when included, all transitive dependencies will also be shown. It is included by default. You can also pass `--resolve` to `pdm list`, which will show the packages resolved in `pdm.lock`, rather than installed in the working set. ### Change the output fields and format By default, name, version and location will be shown in the list output, you can view more fields or specify the order of fields by `--fields` option: ```bash pdm list --fields name,licenses,version ``` For all supported fields, please refer to the [CLI reference](../reference/cli.md#list_1). Also, you can specify the output format other than the default table output. The supported formats and options are `--csv`, `--json`, `--markdown` and `--freeze`. ### Show the dependency tree Or show a dependency tree by: ```bash $ pdm list --tree tempenv 0.0.0 └── click 7.0 [ required: <7.0.0,>=6.7 ] black 19.10b0 ├── appdirs 1.4.3 [ required: Any ] ├── attrs 19.3.0 [ required: >=18.1.0 ] ├── click 7.0 [ required: >=6.5 ] ├── pathspec 0.7.0 [ required: <1,>=0.6 ] ├── regex 2020.2.20 [ required: Any ] ├── toml 0.10.0 [ required: >=0.9.4 ] └── typed-ast 1.4.1 [ required: >=1.4.0 ] bump2version 1.0.0 ``` Note that `--fields` option doesn't work with `--tree`. ### Filter packages by patterns You can also limit the packages to show by passing the patterns to `pdm list`: ```bash pdm list flask-* requests-* ``` ??? warning "Be careful with the shell expansion" In most shells, the wildcard `*` will be expanded if there are matching files under the current directory. To avoid getting unexpected results, you can wrap the patterns with single quotes: `pdm list 'flask-*' 'requests-*'`. In `--tree` mode, only the subtree of the matched packages will be displayed. This can be used to achieve the same purpose as `pnpm why`, which is to show why a specific package is required. ```bash $ pdm list --tree --reverse certifi certifi 2023.7.22 └── requests 2.31.0 [ requires: >=2017.4.17 ] └── cachecontrol[filecache] 0.13.1 [ requires: >=2.16.0 ] ``` ## Manage global project Sometimes users may want to keep track of the dependencies of global Python interpreter as well. It is easy to do so with PDM, via `-g/--global` option which is supported by most subcommands. If the option is passed, `/global-project` will be used as the project directory, which is almost the same as normal project except that `pyproject.toml` will be created automatically for you and it doesn't support build features. The idea is taken from Haskell's [stack](https://docs.haskellstack.org). However, unlike `stack`, by default, PDM won't use global project automatically if a local project is not found. Users should pass `-g/--global` explicitly to activate it, since it is not very pleasing if packages go to a wrong place. But PDM also leave the decision to users, just set the config `global_project.fallback` to `true`. By default, when `pdm` uses global project implicitly the following message is printed: `Project is not found, fallback to the global project`. To disable this message set the config `global_project.fallback_verbose` to `false`. If you want global project to track another project file other than `/global-project`, you can provide the project path via `-p/--project ` option. Especially if you pass `--global --project .`, PDM will install the dependencies of the current project into the global Python. !!! warning Be careful with `remove` and `sync --clean/--pure` commands when global project is used, because it may remove packages installed in your system Python. pdm-2.23.1/docs/usage/hooks.md000066400000000000000000000176501477560627500161140ustar00rootroot00000000000000# Lifecycle and Hooks As any Python deliverable, your project will go through the different phases of a Python project lifecycle and PDM provides commands to perform the expected tasks for those phases. It also provides hooks attached to these steps allowing for: - plugins to listen to the [signals][pdm.signals] of the same name. - developers to define custom scripts with the same name. Besides, `pre_invoke` signal is emitted before ANY command is invoked, allowing plugins to modify the project or options beforehand. The built-in commands are currently split into 3 groups: - the [initialization phase](#initialization) - the [dependencies management](#dependencies-management). - the [publication phase](#publication). You will most probably need to perform some recurrent tasks between the installation and publication phases (housekeeping, linting, testing, ...) this is why PDM lets you define your own tasks/phases using [user scripts](#user-scripts). To provides full flexibility, PDM allows to [skip some hooks and tasks](#skipping) on demand. ## Initialization The initialization phase should occur only once in a project lifetime by running the [`pdm init`](../reference/cli.md#init) command to initialize an existing project (prompt to fill the `pyproject.toml` file). They trigger the following hooks: - [`post_init`][pdm.signals.post_init] ```mermaid flowchart LR subgraph pdm-init [pdm init] direction LR post-init{{Emit post_init}} init --> post-init end ``` ## Dependencies management The dependencies management is required for the developer to be able to work and perform the following: - `lock`: compute a lock file from the `pyproject.toml` requirements. - `sync`: synchronize (add/remove/update) PEP582 packages from the lock file and install the current project as editable. - `add`: add a dependency - `remove`: remove a dependency All those steps are directly available with the following commands: - [`pdm lock`](../reference/cli.md#lock): execute the `lock` task - [`pdm sync`](../reference/cli.md#sync): execute the `sync` task - [`pdm install`](../reference/cli.md#install): execute the `sync` task, preceded from `lock` if required - [`pdm add`](../reference/cli.md#add): add a dependency requirement, re-lock and then sync - [`pdm remove`](../reference/cli.md#remove): remove a dependency requirement, re-lock and then sync - [`pdm update`](../reference/cli.md#update): re-lock dependencies from their latest versions and then sync They trigger the following hooks: - [`pre_install`][pdm.signals.pre_install] - [`post_install`][pdm.signals.post_install] - [`pre_lock`][pdm.signals.pre_lock] - [`post_lock`][pdm.signals.post_lock] ```mermaid flowchart LR subgraph pdm-install [pdm install] direction LR subgraph pdm-lock [pdm lock] direction TB pre-lock{{Emit pre_lock}} post-lock{{Emit post_lock}} pre-lock --> lock --> post-lock end subgraph pdm-sync [pdm sync] direction TB pre-install{{Emit pre_install}} post-install{{Emit post_install}} pre-install --> sync --> post-install end pdm-lock --> pdm-sync end ``` ### Switching Python version This is a special case in dependency management: you can switch the current Python version using [`pdm use`](../reference/cli.md#use) and it will emit the [`post_use`][pdm.signals.post_use] signal with the new Python interpreter. ```mermaid flowchart LR subgraph pdm-use [pdm use] direction LR post-use{{Emit post_use}} use --> post-use end ``` ## Publication As soon as you are ready to publish your package/library, you will require the publication tasks: - `build`: build/compile assets requiring it and package everything into a Python package (sdist, wheel) - `upload`: upload/publish the package to a remote PyPI index All those steps are available with the following commands: - [`pdm build`](../reference/cli.md#build) - [`pdm publish`](../reference/cli.md#publish) They trigger the following hooks: - [`pre_publish`][pdm.signals.pre_publish] - [`post_publish`][pdm.signals.post_publish] - [`pre_build`][pdm.signals.pre_build] - [`post_build`][pdm.signals.post_build] ```mermaid flowchart LR subgraph pdm-publish [pdm publish] direction LR pre-publish{{Emit pre_publish}} post-publish{{Emit post_publish}} subgraph pdm-build [pdm build] pre-build{{Emit pre_build}} post-build{{Emit post_build}} pre-build --> build --> post-build end %% subgraph pdm-upload [pdm upload] %% pre-upload{{Emit pre_upload}} %% post-upload{{Emit post_upload}} %% pre-upload --> upload --> post-upload %% end pre-publish --> pdm-build --> upload --> post-publish end ``` Execution will stop at first failure, hooks included. ## User scripts [User scripts are detailed in their own section](scripts.md) but you should know that: - each user script can define a `pre_*` and `post_*` script, including composite scripts. - each `run` execution will trigger the [`pre_run`][pdm.signals.pre_run] and [`post_run`][pdm.signals.post_run] hooks - each script execution will trigger the [`pre_script`][pdm.signals.pre_script] and [`post_script`][pdm.signals.post_script] hooks Given the following `scripts` definition: ```toml [tool.pdm.scripts] pre_script = "" post_script = "" pre_test = "" post_test = "" test = "" pre_composite = "" post_composite = "" composite = {composite = ["test"]} ``` a `pdm run test` will have the following lifecycle: ```mermaid flowchart LR subgraph pdm-run-test [pdm run test] direction LR pre-run{{Emit pre_run}} post-run{{Emit post_run}} subgraph run-test [test task] direction TB pre-script{{Emit pre_script}} post-script{{Emit post_script}} pre-test[Execute pre_test] post-test[Execute post_test] test[Execute test] pre-script --> pre-test --> test --> post-test --> post-script end pre-run --> run-test --> post-run end ``` while `pdm run composite` will have the following: ```mermaid flowchart LR subgraph pdm-run-composite [pdm run composite] direction LR pre-run{{Emit pre_run}} post-run{{Emit post_run}} subgraph run-composite [composite task] direction TB pre-script-composite{{Emit pre_script}} post-script-composite{{Emit post_script}} pre-composite[Execute pre_composite] post-composite[Execute post_composite] subgraph run-test [test task] direction TB pre-script-test{{Emit pre_script}} post-script-test{{Emit post_script}} pre-test[Execute pre_test] post-test[Execute post_test] pre-script-test --> pre-test --> test --> post-test --> post-script-test end pre-script-composite --> pre-composite --> run-test --> post-composite --> post-script-composite end pre-run --> run-composite --> post-run end ``` ## Skipping It is possible to control which task and hook runs for any built-in command as well as custom user scripts using the `--skip` option. It accepts a comma-separated list of hooks/task names to skip as well as the predefined `:all`, `:pre` and `:post` shortcuts respectively skipping all hooks, all `pre_*` hooks and all `post_*` hooks. You can also provide the skip list in `PDM_SKIP_HOOKS` environment variable but it will be overridden as soon as the `--skip` parameter is provided. Given the previous script block, running `pdm run --skip=:pre,post_test composite` will result in the following reduced lifecycle: ```mermaid flowchart LR subgraph pdm-run-composite [pdm run composite] direction LR post-run{{Emit post_run}} subgraph run-composite [composite task] direction TB post-script-composite{{Emit post_script}} post-composite[Execute post_composite] subgraph run-test [test task] direction TB post-script-test{{Emit post_script}} test --> post-script-test end run-test --> post-composite --> post-script-composite end run-composite --> post-run end ``` pdm-2.23.1/docs/usage/lock-targets.md000066400000000000000000000131721477560627500173630ustar00rootroot00000000000000# Lock for specific platforms or Python versions +++ 2.17.0 By default, PDM will try to make a lock file that works on all platforms within the Python versions specified by [`requires-python` in `pyproject.toml`](./project.md#specify-requires-python). This is very convenient during development. You can generate a lock file in your development environment and then use this lock file to replicate the same dependency versions in CI/CD or production environments. However, there are times when this approach may not work. For example, your project or dependency has some platform-specific dependencies, or conditional dependencies depending on the Python version, like the following: ```toml [project] name = "myproject" requires-python = ">=3.9" dependencies = [ "numpy<1.25; python_version < '3.9'", "numpy>=1.25; python_version >= '3.9'", "pywin32; sys_platform == 'win32'", ] ``` In this case, it's almost impossible to get a single resolution for each package on all platforms and Python versions(`>=3.9`). You should, instead, make lock files for specific platforms or Python versions. ## Specify lock target when generating lock file PDM supports specifying one or more environment criteria when generating a lock file. These criteria include: - `--python=`: A [PEP 440](https://www.python.org/dev/peps/pep-0440/) compatible Python version specifier. For example, `--python=">=3.9,<3.10"` will generate a lock file for Python versions `>=3.9` and `<3.10`. For convenience, `--python=3.10` is equivalent to `--python=">=3.10"`, meaning to resolve for Python 3.10 and above. - `--platform=`: A platform specifier. For example, `pdm lock --platform=linux` will generate a lock file for Linux x86_64 platform. Available options are: * `linux` * `windows` * `macos` * `alpine` * `windows_amd64` * `windows_x86` * `windows_arm64` * `macos_arm64` * `macos_x86_64` * `macos_X_Y_arm64` * `macos_X_Y_x86_64` * `manylinux_X_Y_x86_64` * `manylinux_X_Y_aarch64` * `musllinux_X_Y_x86_64` * `musllinux_X_Y_aarch64` - `--implementation=cpython|pypy|pyston`: A Python implementation specifier. Currently only `cpython`, `pypy`, and `pyston` are supported. You can ignore some of the criteria, for example, by specifying only `--platform=linux`, the generated lock file will be applicable to Linux platform and all implementations. !!! note "`python` criterion and `requires-python`" `--python` option, or `requires-python` criterion in the lock target is still limited by the `requires-python` in `pyproject.toml`. For example, if `requires-python` is `>=3.9` and you specified `--python="<3.11"`, the lock target will be `>=3.9,<3.11`. ## Separate lock files or merge into one If you need more than one lock targets, you can either create separate lock files for each target or combine them into a single lock file. PDM supports both ways. To create separate lock file with a specific target: ```bash # Generate a lock file for Linux platform and Python 3.9, write the result to py38-linux.lock pdm lock --platform=linux --python="==3.9.*" --lockfile=py38-linux.lock ``` When you install dependencies on Linux and Python 3.9, you can use this lock file: ```bash pdm install --lockfile=py38-linux.lock ``` Additionally, you can also select a subset of dependency groups for the lock file, see [here](./lockfile.md#specify-another-lock-file-to-use) for more details. If you would like to use the same lock file for multiple targets, add `--append` to the `pdm lock` command: ```bash # Generate a lock file for Linux platform and Python 3.9, append the result to pdm.lock pdm lock --platform=linux --python="==3.9.*" --append ``` The advantages of using a single lock file are you don't need to manage multiple lock files when updating dependencies. However, you can't specify different lock strategies for different targets in a single lock file. And the time cost of updating the locks is expected to be higher. What's more, each lock file can have one or more lock targets, making it rather flexible to use. You can choose to merge some targets in a lock file and lock specific groups and targets in separate lock files. We'll illustrate this with an example in the next section. ## Example Here is the `pyproject.toml` content: ```toml [project] name = "myproject" requires-python = ">=3.9" dependencies = [ "numpy<1.25; python_version < '3.10'", "numpy>=1.25; python_version >= '3.10'", "pandas" ] [project.optional-dependencies] windows = ["pywin32"] macos = ["pyobjc"] ``` In the above example, we have conditional dependency versions for `numpy` and platform-specific optional dependencies for Windows and MacOS. We want to generate lock files for Linux, Windows, and MacOS platforms, and Python 3.9 and 3.10. ```bash pdm lock --python=">=3.10" pdm lock --python="<3.10" --append pdm lock --platform=windows --python=">=3.10" --lockfile=py310-windows.lock --with windows pdm lock --platform=macos --python=">=3.10" --lockfile=py310-macos.lock --with macos ``` Run the above commands in order, and you will get 3 lockfiles: - `pdm.lock`: the default main lock file, which works on all platforms and Python versions in `>=3.9`. No platform specific dependencies are included. In this lock file, there are two versions of `numpy`, suitable for Python 3.10 and above and below respectively. The PDM installer will choose the correct version according to the Python version. - `py39-windows.lock`: lock file for Windows platform and Python 3.10 above, including the optional dependencies for Windows. - `py39-macos.lock`: lock file for MacOS platform and Python 3.10 above, including the optional dependencies for MacOS. pdm-2.23.1/docs/usage/lockfile.md000066400000000000000000000272671477560627500165660ustar00rootroot00000000000000# Lock file PDM installs packages exclusively from the existing lock file named `pdm.lock`. This file serves as the sole source of truth for installing dependencies. The lock file contains essential information such as: - All packages and their versions - The file names and hashes of the packages - Optionally, the origin URLs to download the packages (See also: [Static URLs](#static-urls)) - The dependencies and markers of each package (See also: [Inherit the metadata from parents](#inherit-the-metadata-from-parents)) To create or overwrite the lock file, run [`pdm lock`](../reference/cli.md#lock), and it supports the same [update strategies](./dependency.md#about-update-strategy) as [`pdm add`](../reference/cli.md#add). In addition, the [`pdm install`](../reference/cli.md#install) and [`pdm add`](../reference/cli.md#add) commands will also automatically create the `pdm.lock` file. ??? NOTE "Should I add `pdm.lock` to version control?" It depends. If your goal is to make CI use the same dependency versions as local development and avoid unexpected failures, you should add the `pdm.lock` file to version control. Otherwise, if your project is a library and you want CI to mimic the installation on user site to ensure that the current version on PyPI doesn't break anything, then do not submit the `pdm.lock` file. ## Install the packages pinned in lock file There are a few similar commands to do this job with slight differences: - [`pdm sync`](../reference/cli.md#sync) installs packages from the lock file. - [`pdm update`](../reference/cli.md#update) will update the lock file, then `pdm sync`. - [`pdm install`](../reference/cli.md#install) will check the project file for changes, update the lock file if needed, then `pdm sync`. `pdm sync` also has a few options to manage installed packages: - `--clean`: will remove packages no longer in the lockfile - `--clean-unselected` (or `--only-keep`): more thorough version of `--clean` that will also remove packages not in the groups specified by the `-G`, `-d`, and `--prod` options. Note: by default, `pdm sync` selects all groups from the lockfile, so `--clean-unselected` is identical to `--clean` unless `-G`, `-d`, and `--prod` are used. ## Hashes in the lock file By default, `pdm install` will check if the lock file matches the content of `pyproject.toml`, this is done by storing a content hash of `pyproject.toml` in the lock file. To check if the hash in the lock file is up-to-date: ```bash pdm lock --check ``` If you want to refresh the lock file without changing the dependencies, you can use the `--refresh` option: ```bash pdm lock --refresh ``` This command also refreshes _all_ file hashes recorded in the lock file. ## Specify another lock file to use By default, PDM uses `pdm.lock` in the current directory. You can specify another lock file with the `-L/--lockfile` option or the `PDM_LOCKFILE` environment variable: ```bash pdm install --lockfile my-lockfile.lock ``` This command installs packages from `my-lockfile.lock` instead of `pdm.lock`. Alternate lock files are helpful when there exist conflicting dependencies for different environments. In this case, if you lock them as a whole, PDM will raise an error. So you have to [select a subset of dependency groups](./dependency.md#select-a-subset-of-dependency-groups-to-install) and lock them separately. For a realistic example, your project depends on a release version of `werkzeug` and you may want to work with a local in-development copy of it when developing. You can add the following to your `pyproject.toml`: ```toml [project] requires-python = ">=3.7" dependencies = ["werkzeug"] [dependency-groups] dev = ["werkzeug @ file:///${PROJECT_ROOT}/dev/werkzeug"] ``` Then, run `pdm lock` with different options to generate lockfiles for different purposes: ```bash # Lock default + dev, write to pdm.lock # with the local copy of werkzeug pinned. pdm lock # Lock default, write to pdm.prod.lock # with the release version of werkzeug pinned. pdm lock --prod -L pdm.prod.lock ``` Check the `metadata.groups` field in the lockfile to see which groups are included. ## Option to not write lock file Sometimes you want to add or update dependencies without updating the lock file, or you don't want to generate `pdm.lock`, you can use the `--frozen-lockfile` option: ```bash pdm add --frozen-lockfile flask ``` In this case, the lock file, if existing, will become read-only, no write operation will be performed on it. However, dependency resolution step will still be performed if needed. ## Lock strategies Currently, we support three flags to control the locking behavior: `cross_platform`, `static_urls` and `direct_minimal_versions`, with the meanings as follows. You can pass one or more flags to `pdm lock` by `--strategy/-S` option, either by giving a comma-separated list or by passing the option multiple times. Both of these commands function in the same way: ```bash pdm lock -S cross_platform,static_urls pdm lock -S cross_platform -S static_urls ``` The flags will be encoded in the lockfile and get read when you run `pdm lock` next time. But you can disable flags by prefixing the flag name with `no_`: ```bash pdm lock -S no_cross_platform ``` This command makes the lockfile not cross-platform. ### Cross platform +++ 2.6.0 !!! warning "Deprecated in 2.17.0" See [Lock for specific platforms or Python versions](./lock-targets.md) for the new behavior. By default, the generated lockfile is **cross-platform**, which means the current platform isn't taken into account when resolving the dependencies. The result lockfile will contain wheels and dependencies for all possible platforms and Python versions. However, sometimes this will result in a wrong lockfile when a release doesn't contain all wheels. To avoid this, you can tell PDM to create a lockfile that works for **this platform** only, trimming the wheels not relevant to the current platform. This can be done by passing the `--strategy no_cross_platform` option to `pdm lock`: ```bash pdm lock --strategy no_cross_platform ``` ### Static URLs +++ 2.8.0 By default, PDM only stores the filenames of the packages in the lockfile, which benefits the reusability across different package indexes. However, if you want to store the static URLs of the packages in the lockfile, you can pass the `--strategy static_urls` option to `pdm lock`: ```bash pdm lock --strategy static_urls ``` The settings will be saved and remembered for the same lockfile. You can also pass `--strategy no_static_urls` to disable it. ### Direct minimal versions +++ 2.10.0 When it is enabled by passing `--strategy direct_minimal_versions`, dependencies specified in the `pyproject.toml` will be resolved to the minimal versions available, rather than the latest versions. This is useful when you want to test the compatibility of your project within a range of dependency versions. For example, if you specified `flask>=2.0` in the `pyproject.toml`, `flask` will be resolved to version `2.0.0` if there is no other compatibility issue. !!! NOTE Version constraints in package dependencies are not future-proof. If you resolve the dependencies to the minimal versions, there will likely be backwards-compatibility issues. For example, `flask==2.0.0` requires `werkzeug>=2.0`, but in fact, it can not work with `Werkzeug 3.0.0`, which is released 2 years after it. ### Inherit the metadata from parents +++ 2.11.0 Previously, the `pdm lock` command would record package metadata as it is. When installing, PDM would start from the top requirements and traverse down to the leaf node of the dependency tree. It would then evaluate any marker it encounters against the current environment. If a marker is not satisfied, the package would be discarded. In other words, we need an additional "resolution" step in installation. When the `inherit_metadata` strategy is enabled, PDM will inherit and merge environment markers from a package's ancestors. These markers are then encoded in the lockfile during locking, resulting in faster installations. This has been enabled by default from version `2.11.0`, to disable this strategy in the config, use `pdm config strategy.inherit_metadata false`. ### Exclude packages newer than specific date +++ 2.13.0 You can exclude packages that are newer than a specified date by passing the `--exclude-newer` option to `pdm lock`. This is useful when you want to lock the dependencies to a specific date, for example, to ensure reproducibility of the build. The date may be specified as a RFC 3339 timestamp (e.g., `2006-12-02T02:07:43Z`) or UTC date in the same format (e.g., `2006-12-02`). ```bash pdm lock --exclude-newer 2024-01-01 ``` !!! note The package index must support the `upload-time` field as specified in [PEP 700]. If the field is not present for a given distribution, the distribution will be treated as unavailable. [PEP 700]: https://peps.python.org/pep-0700/ ## Set acceptable format for locking or installing If you want to control the format(binary/sdist) of the packages, you can set the env vars `PDM_NO_BINARY`, `PDM_ONLY_BINARY` and `PDM_PREFER_BINARY`. Each env var is a comma-separated list of package name. You can set it to `:all:` to apply to all packages. For example: ```toml # No binary for werkzeug will be locked nor used for installation PDM_NO_BINARY=werkzeug pdm add flask # Only binaries will be locked in the lock file PDM_ONLY_BINARY=:all: pdm lock # No binaries will be used for installation PDM_NO_BINARY=:all: pdm install # Prefer binary distributions and even if sdist with higher version is available PDM_PREFER_BINARY=flask pdm install ``` You can also defined those values in your project `pyproject.toml` with the `no-binary`, `only-binary` and `prefer-binary` keys of the `tool.pdm.resolution` section. They accept the same format as the environment variables and also support lists. ```toml [tool.pdm.resolution] # No binary for werkzeug and flask will be locked nor used for installation no-binary = "werkzeug,flask" # equivalent to no-binary = ["werkzeug", "flask"] # Only binaries will be locked in the lock file only-binary = ":all:" # Prefer binary distributions and even if sdist with higher version is available prefer-binary = "flask" ``` !!! note Each environment variable takes precedence over its `pyproject.toml` alternative. ## Allow prerelease versions to be installed Include the following setting in `pyproject.toml` to enable: ```toml [tool.pdm.resolution] allow-prereleases = true ``` ## Solve the locking failure If PDM is not able to find a resolution to satisfy the requirements, it will raise an error. For example, ```bash pdm django==3.1.4 "asgiref<3" ... 🔒 Lock failed Unable to find a resolution for asgiref because of the following conflicts: asgiref<3 (from project) asgiref<4,>=3.2.10 (from ) To fix this, you could loosen the dependency version constraints in pyproject.toml. If that is not possible, you could also override the resolved version in `[tool.pdm.resolution.overrides]` table. ``` You can either change to a lower version of `django` or remove the upper bound of `asgiref`. But if it is not eligible for your project, you can try [overriding the resolved package versions](./config.md#override-the-resolved-package-versions) or even [don't lock that specific package](./config.md#exclude-specific-packages-and-their-dependencies-from-the-lock-file) in `pyproject.toml`. ## Export locked packages to alternative formats You can export the `pdm.lock` file to other formats, which will simplify the CI flow or image building process. At present, only the `requirements.txt` format is supported. ```bash pdm export -o requirements.txt ``` !!! TIP You can also run `pdm export` with a [`.pre-commit` hook](./advanced.md#hooks-for-pre-commit). pdm-2.23.1/docs/usage/pep582.md000066400000000000000000000223751477560627500160140ustar00rootroot00000000000000# Working with PEP 582 !!! warning "PEP 582 has been rejected" This is a rejected PEP. However, due to the fact that this feature is the reason for PDM's birth, PDM will retain the support. We recommend using [virtual environments](./venv.md) instead. With [PEP 582](https://www.python.org/dev/peps/pep-0582/), dependencies will be installed into `__pypackages__` directory under the project root. With [PEP 582 enabled globally](#enable-pep-582-globally), you can also use the project interpreter to run scripts directly. **When the project interpreter is a normal Python, this mode is enabled.** Besides, on a project you work with for the first time on your machine, if it contains an empty `__pypackages__` directory, PEP 582 is enabled automatically, and virtualenv won't be created. ## Enable PEP 582 in projects managed my pdm To make pdm use PEP 582 instead of virtual environment, set `python.use_venv` config variable to False: ```bash pdm config python.use_venv False ``` ## Enable PEP 582 globally To make the Python interpreters aware of PEP 582 packages, one needs to add the `pdm/pep582/sitecustomize.py` to the Python library search path. === "Windows" One just needs to execute `pdm --pep582`, then environment variable will be changed automatically. Don't forget to restart the terminal session to take effect. === "Mac and Linux" The command to change the environment variables can be printed by `pdm --pep582 []`. If `` isn't given, PDM will pick one based on some guesses. You can run `eval "$(pdm --pep582)"` to execute the command. You may want to write a line in your `.bash_profile`(or similar profiles) to make it effective when logging in. For example, in bash you can do this: ```bash pdm --pep582 >> ~/.bash_profile ``` Once again, Don't forget to restart the terminal session to take effect. ??? note "How is it done?" Thanks to the [site packages loading](https://docs.python.org/3/library/site.html) on Python startup. It is possible to patch the `sys.path` by executing the `sitecustomize.py` shipped with PDM. The interpreter can search the directories for the nearest `__pypackage__` folder and append it to the `sys.path` variable. ## Configure IDE to support PEP 582 Now there are no built-in support or plugins for PEP 582 in most IDEs, you have to configure your tools manually. ### PyCharm Mark `__pypackages__//lib` as [Sources Root](https://www.jetbrains.com/help/pycharm/configuring-project-structure.html#mark-dir-project-view). Then, select as [Python interpreter](https://www.jetbrains.com/help/pycharm/configuring-python-interpreter.html#interpreter) a Python installation with the same `` version. Additionally, if you want to use tools from the environment (e.g. `pytest`), you have to add the `__pypackages__//bin` directory to the `PATH` variable in the corresponding run/debug configuration. ### VSCode Add the following two entries to the top-level dict in `.vscode/settings.json`: ```json { "python.autoComplete.extraPaths": ["__pypackages__//lib"], "python.analysis.extraPaths": ["__pypackages__//lib"] } ``` This file can be auto-generated with plugin [`pdm-vscode`](https://github.com/frostming/pdm-vscode). [Enable PEP582 globally](#enable-pep-582-globally), and make sure VSCode runs using the same user and shell you enabled PEP582 for. ??? note "Cannot enable PEP582 globally?" If for some reason you cannot enable PEP582 globally, you can still configure each "launch" in each project: set the `PYTHONPATH` environment variable in your launch configuration, in `.vscode/launch.json`. For example, to debug your `pytest` run: ```json { "version": "0.2.0", "configurations": [ { "name": "pytest", "type": "python", "request": "launch", "module": "pytest", "args": ["tests"], "justMyCode": false, "env": {"PYTHONPATH": "__pypackages__//lib"} } ] } ``` If your package resides in a `src` directory, add it to `PYTHONPATH` as well: ```json "env": {"PYTHONPATH": "src:__pypackages__//lib"} ``` ??? note "Using Pylance/Pyright?" If you have configured `"python.analysis.diagnosticMode": "workspace"`, and you see a ton of errors/warnings as a result. you may need to create `pyrightconfig.json` in the workspace directory, and fill in the following fields: ```json { "exclude": ["__pypackages__"] } ``` Then restart the language server or VS Code and you're good to go. In the future ([microsoft/pylance-release#1150](https://github.com/microsoft/pylance-release/issues/1150)), maybe the problem will be solved. ??? note "Using Jupyter Notebook?" If you wish to use pdm to install jupyter notebook and use it in vscode in conjunction with the python extension: 1. Use `pdm add notebook` or so to install notebook 2. Add a `.env` file inside of your project directory with contents like the following: ``` PYTHONPATH=/your-workspace-path/__pypackages__/./lib ``` If the above still doesn't work, it's most likely because the environment variable is not properly loaded when the Notebook starts. There are two workarounds. 1. Run `code .` in Terminal. It will open a new VSCode window in the current directory with the path set correctly. Use the Jupyter Notebook in the new window 2. If you prefer not to open a new window, run the following at the beginning of your Jupyter Notebook to explicitly set the path: ``` import sys sys.path.append('/your-workspace-path/__pypackages__/./lib') ``` > [Reference Issue](https://github.com/pdm-project/pdm/issues/848) ??? note "PDM Task Provider" In addition, there is a [VSCode Task Provider extension][pdm task provider] available for download. This makes it possible for VSCode to automatically detect [pdm scripts][pdm scripts] so they can be run natively as [VSCode Tasks][vscode tasks]. [vscode tasks]: https://code.visualstudio.com/docs/editor/tasks [pdm task provider]: https://marketplace.visualstudio.com/items?itemName=knowsuchagency.pdm-task-provider [pdm scripts]: scripts.md ### Neovim If using [neovim-lsp](https://github.com/neovim/nvim-lspconfig) with [pyright](https://github.com/Microsoft/pyright) and want your `__pypackages__` directory to be added to the path, you can add this to your project's `pyproject.toml`. ```toml [tool.pyright] extraPaths = ["__pypackages__//lib/"] ``` ### Emacs You have a few options, but basically you'll want to tell an LSP client to add `__pypackages__` to the paths it looks at. Here are a few options that are available: #### Using `pyproject.toml` and pyright Add this to your project's `pyproject.toml`: ```toml [tool.pyright] extraPaths = ["__pypackages__//lib/"] ``` #### eglot + pyright Using [pyright](https://github.com/microsoft/pyright) and [eglot](https://github.com/joaotavora/eglot) (included in Emacs 29), add the following to your config: ```emacs-lisp (defun get-pdm-packages-path () "For the current PDM project, find the path to the packages." (let ((packages-path (string-trim (shell-command-to-string "pdm info --packages")))) (concat packages-path "/lib"))) (defun my/eglot-workspace-config (server) "For the current PDM project, dynamically generate a python lsp config." `(:python\.analysis (:extraPaths ,(vector (get-pdm-packages-path))))) (setq-default eglot-workspace-configuration #'my/eglot-workspace-config) ``` You'll want pyright installed either globally, or in your project (probably as a dev dependency). You can add this with, for example: ```bash pdm add --dev --group devel pyright ``` #### LSP-Mode + lsp-python-ms Below is a sample code snippet showing how to make PDM work with [lsp-python-ms](https://github.com/emacs-lsp/lsp-python-ms) in Emacs. Contributed by [@linw1995](https://github.com/pdm-project/pdm/discussions/372#discussion-3303501). ```emacs-lisp ;; TODO: Cache result (defun linw1995/pdm-get-python-executable (&optional dir) (let ((pdm-get-python-cmd "pdm info --python")) (string-trim (shell-command-to-string (if dir (concat "cd " dir " && " pdm-get-python-cmd) pdm-get-python-cmd))))) (defun linw1995/pdm-get-packages-path (&optional dir) (let ((pdm-get-packages-cmd "pdm info --packages")) (concat (string-trim (shell-command-to-string (if dir (concat "cd " dir " && " pdm-get-packages-cmd) pdm-get-packages-cmd))) "/lib"))) (use-package lsp-python-ms :ensure t :init (setq lsp-python-ms-auto-install-server t) :hook (python-mode . (lambda () (setq lsp-python-ms-python-executable (linw1995/pdm-get-python-executable)) (setq lsp-python-ms-extra-paths (vector (linw1995/pdm-get-packages-path))) (require 'lsp-python-ms) (lsp)))) ; or lsp-deferred ``` pdm-2.23.1/docs/usage/project.md000066400000000000000000000263031477560627500164320ustar00rootroot00000000000000# New Project To start with, create a new project with [`pdm init`](../reference/cli.md#init): ```bash mkdir my-project && cd my-project pdm init ``` You will need to answer a few questions, to help PDM to create a `pyproject.toml` file for you. For more usages of `pdm init`, please read [Create your project from a template](./template.md). ## Choose a Python interpreter At first, you need to choose a Python interpreter from a list of Python versions installed on your machine. The interpreter path will be stored in `.pdm-python` and used by subsequent commands. You can also change it later with [`pdm use`](../reference/cli.md#use). Alternatively, you can specify the Python interpreter path via `PDM_PYTHON` environment variable. When it is set, the path saved in `.pdm-python` will be ignored. +++ 2.23.0 If `.python-version` is present in the project root or `PDM_PYTHON_VERSION` env var is set, PDM will use the Python version specified in it. The file or env var should contain a valid Python version string, such as `3.11`. !!! warning "Using an existing environment" If you choose to use an existing environment, such as reusing an environment created by `conda`, please note that PDM will _remove_ dependencies not listed in `pyproject.toml` or `pdm.lock` when running `pdm sync --clean` or `pdm remove`. This may lead to destructive consequences. Therefore, try not to share environment among multiple projects. ### Install Python interpreters with PDM +++ 2.13.0 PDM supports installing additional Python interpreters from [@indygreg's python-build-standalone](https://github.com/indygreg/python-build-standalone) with the `pdm python install` command. For example, to install CPython 3.9.8: ```bash pdm python install 3.9.8 ``` You can view all available Python versions with `pdm python install --list`. This will install the Python interpreter into the location specified by `python.install_root` configuration. List the currently installed Python interpreters: ```bash pdm python list ``` Remove an installed Python interpreter: ```bash pdm python remove 3.9.8 ``` Install a free-threaded Python interpreter: ```bash pdm python install 3.13t ``` !!! TIP "Share installations with Rye" PDM installs Python interpreters using the same source as [Rye](https://rye-up.com). If you are using Rye at the same time, you can point the `python.install_root` to the same directory as Rye to share the Python interpreters: ```bash pdm config python.install_root ~/.rye/py ``` Afterwards you can manage the installations using either `rye toolchain` or `pdm python`. ### Installation strategy based on `requires-python` +++ 2.16.0 If Python `version` is not given, PDM will try to install the best match for the current platform/arch combination based on `requires-python` from `pyproject.toml` (if pyproject.toml or requires-python attribute is not available, all install-able Python interpreters are considered). Default strategy is `maximum`, i.e. the highest cPython interpreter version will be installed. If `minimum` is preferred, use the option `--min` and leave `version` empty. ```bash pdm python install --min ``` The same principles apply to [`pdm use`](../reference/cli.md#use) (incl. an automatic installation feature) which make it a good unattended set up command for CI/CD or 'fresh start with existing pyproject.toml' use-cases. ### Virtualenv or not After you select the Python interpreter, PDM will ask you whether you want to create a virtual environment for the project. If you choose **yes**, PDM will create a virtual environment in the project root directory, and use it as the Python interpreter for the project. If the selected Python interpreter is in a virtual environment, PDM will use it as the project environment and install dependencies into it. Otherwise, `__pypackages__` will be created in the project root and dependencies will be installed into it. For the difference between these two approaches, please refer to the corresponding sections in the docs: - [Virtualenv](./venv.md) - [`__pypackages__`(PEP 582)](./pep582.md) ## Library or Application A library and an application differ in many ways. In short, a library is a package that is intended to be installed and used by other projects. In most cases it also needs to be uploaded to PyPI. An application, on the other hand, is one that is directly facing end users and may need to be deployed into some production environments. In PDM, if you choose to create a library, PDM will add a `name`, `version` field to the `pyproject.toml` file, as well as a `[build-system]` table for the [build backend](../reference/build.md), which is only useful if your project needs to be built and distributed. So you need to manually add these fields to `pyproject.toml` if you want to change the project from an application to a library. Also, a library project will be installed into the environment when you run `pdm install` or `pdm sync`, unless `--no-self` is specified. In `pyproject.toml`, there is a field `distribution` under the `[tool.pdm]` table. If it is set to true, PDM will treat the project as a library. ## Specify `requires-python` You need to set an appropriate `requires-python` value for your project. This is an important property that affects how dependencies are resolved. Basically, each package's `requires-python` must _cover_ the project's `requires-python` range. For example, consider the following setup: - Project: `requires-python = ">=3.9"` - Package `foo`: `requires-python = ">=3.7,<3.11"` Resolving the dependencies will cause a `ResolutionImpossible`: ```bash Unable to find a resolution because the following dependencies don't work on all Python versions defined by the project's `requires-python` ``` Because the dependency's `requires-python` is `>=3.7,<3.11`, it _doesn't_ cover the project's `requires-python` range of `>=3.9`. In other words, the project promises to work on Python 3.9, 3.10, 3.11 (and so on), but the dependency doesn't support Python 3.11 (or any higher). Since PDM creates a cross-platform lockfile that should work on all Python versions within the `requires-python` range, it can't find a valid resolution. To fix this, you need add a maximum version to `requires-python`, like `>=3.9,<3.11`. The value of `requires-python` is a [version specifier as defined in PEP 440](https://peps.python.org/pep-0440/#version-specifiers). Here are some examples: | `requires-python` | Meaning | | ----------------------- | ---------------------------------------- | | `>=3.7` | Python 3.7 and above | | `>=3.7,<3.11` | Python 3.7, 3.8, 3.9 and 3.10 | | `>=3.6,!=3.8.*,!=3.9.*` | Python 3.6 and above, except 3.8 and 3.9 | ## Working with older Python versions --- 2.21.0 PDM now supports 3.9 and above as the python version of projects. Although PDM run on Python 3.9 and above, you can still have lower Python versions for your **working project**. But remember, if your project is a library, which needs to be built, published or installed, you make sure the PEP 517 build backend being used supports the lowest Python version you need. For instance, the default backend `pdm-backend` only works on Python 3.7+, so if you run [`pdm build`](../reference/cli.md#build) on a project with Python 3.6, you will get an error. Most modern build backends have dropped the support for Python 3.6 and lower, so it is highly recommended to upgrade the Python version to 3.7+. Here are the supported Python range for some commonly used build backends, we only list those that support PEP 621 since otherwise PDM can't work with them. | Backend | Supported Python | Support PEP 621 | | --------------------- | ---------------- | --------------- | | `pdm-backend` | `>=3.7` | Yes | | `setuptools>=60` | `>=3.7` | Experimental | | `hatchling` | `>=3.7` | Yes | | `flit-core>=3.4` | `>=3.6` | Yes | | `flit-core>=3.2,<3.4` | `>=3.4` | Yes | Note that if your project is an application (i.e. without the `name` metadata), the above limitation of backends does not apply. Therefore, if you don't need a build backend you can use any Python version `>=2.7`. ## Import the project from other package managers If you are already using other package manager tools like Pipenv or Poetry, it is easy to migrate to PDM. PDM provides `import` command so that you don't have to initialize the project manually, it now supports: 1. Pipenv's `Pipfile` 2. Poetry's section in `pyproject.toml` 3. Flit's section in `pyproject.toml` 4. `requirements.txt` format used by pip 5. setuptools `setup.py`(It requires `setuptools` to be installed in the project environment. You can do this by configuring `venv.with_pip` to `true` for venv and `pdm add setuptools` for `__pypackages__`) Also, when you are executing [`pdm init`](../reference/cli.md#init) or [`pdm install`](../reference/cli.md#install), PDM can auto-detect possible files to import if your PDM project has not been initialized yet. !!! info Converting a `setup.py` will execute the file with the project interpreter. Make sure `setuptools` is installed with the interpreter and the `setup.py` is trusted. ## Working with version control You **must** commit the `pyproject.toml` file. You **should** commit the `pdm.lock` and `pdm.toml` file. **Do not** commit the `.pdm-python` file. The `pyproject.toml` file must be committed as it contains the project's build metadata and dependencies needed for PDM. It is also commonly used by other python tools for configuration. Read more about the `pyproject.toml` file at [Pip documentation](https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml/). You should be committing the `pdm.lock` file, by doing so you ensure that all installers are using the same versions of dependencies. To learn how to update dependencies see [update existing dependencies](./dependency.md#update-existing-dependencies). `pdm.toml` contains some project-wide configuration and it may be useful to commit it for sharing. `.pdm-python` stores the **Python path** used by the **current** project and doesn't need to be shared. ## Show the current Python environment ```bash $ pdm info PDM version: 2.0.0 Python Interpreter: /opt/homebrew/opt/python@3.9/bin/python3.9 (3.9) Project Root: /Users/fming/wkspace/github/test-pdm Project Packages: /Users/fming/wkspace/github/test-pdm/__pypackages__/3.9 # Show environment info $ pdm info --env { "implementation_name": "cpython", "implementation_version": "3.9.0", "os_name": "nt", "platform_machine": "AMD64", "platform_release": "10", "platform_system": "Windows", "platform_version": "10.0.18362", "python_full_version": "3.9.0", "platform_python_implementation": "CPython", "python_version": "3.9", "sys_platform": "win32" } ``` [This command](../reference/cli.md#info) is useful for checking which mode is being used by the project: - If **Project Packages** is `None`, [virtualenv mode](./venv.md) is enabled. - Otherwise, [PEP 582 mode](./pep582.md) is enabled. Now, you have set up a new PDM project and get a `pyproject.toml` file. Refer to [metadata section](../reference/pep621.md) about how to write `pyproject.toml` properly. pdm-2.23.1/docs/usage/publish.md000066400000000000000000000043261477560627500164330ustar00rootroot00000000000000# Build and Publish If you are developing a library, after adding dependencies to your project, and finishing the coding, it's time to build and publish your package. It is as simple as one command: ```bash pdm publish ``` This will automatically build a wheel and a source distribution(sdist), and upload them to the PyPI index. PyPI requires API tokens to publish packages, you can use `__token__` as the username and API token as the password. To specify another repository other than PyPI, use the `--repository` option, the parameter can be either the upload URL or the name of the repository stored in the config file. ```bash pdm publish --repository testpypi pdm publish --repository https://test.pypi.org/legacy/ ``` ## Publish with trusted publishers You can configure trusted publishers for PyPI so that you don't need to expose the PyPI tokens in the release workflow. To do this, follow [the guide](https://docs.pypi.org/trusted-publishers/adding-a-publisher/) to add a publisher write a action as below: ### GitHub Actions ```yaml on: release: types: [published] jobs: pypi-publish: name: upload release to PyPI runs-on: ubuntu-latest permissions: # This permission is needed for private repositories. contents: read # IMPORTANT: this permission is mandatory for trusted publishing id-token: write steps: - uses: actions/checkout@v4 - uses: pdm-project/setup-pdm@v4 - name: Publish package distributions to PyPI run: pdm publish ``` ### GitLab CI ```yaml image: python:3.12-bookworm before_script: - pip install pdm publish-package: stage: release environment: production id_tokens: PYPI_ID_TOKEN: # for testpypi: TESTPYPI_ID_TOKEN aud: "pypi" # testpypi script: - pdm publish ``` ## Build and publish separately You can also build the package and upload it in two steps, to allow you to inspect the built artifacts before uploading. ```bash pdm build ``` There are many options to control the build process, depending on the backend used. Refer to the [build configuration](../reference/build.md) section for more details. The artifacts will be created at `dist/` and able to upload to PyPI. ```bash pdm publish --no-build ``` pdm-2.23.1/docs/usage/scripts.md000066400000000000000000000374361477560627500164640ustar00rootroot00000000000000# PDM Scripts Like `npm run`, with PDM, you can run arbitrary scripts or commands with local packages loaded. ## Arbitrary Scripts ```bash pdm run flask run -p 54321 ``` It will run `flask run -p 54321` in the environment that is aware of packages in your project environment. ## Single-file Scripts +++ 2.16.0 PDM can run single-file scripts with [inline script metadata](https://peps.python.org/pep-0723/). The following is an example of a script with embedded metadata: ```python # test_script.py # /// script # requires-python = ">=3.11" # dependencies = [ # "requests<3", # "rich", # ] # /// import requests from rich.pretty import pprint resp = requests.get("https://peps.python.org/api/peps.json") data = resp.json() pprint([(k, v["title"]) for k, v in data.items()][:10]) ``` When you run it with `pdm run test_script.py`, PDM will create a temporary environment with the specified dependencies installed and run the script: ```python [ │ ('1', 'PEP Purpose and Guidelines'), │ ('2', 'Procedure for Adding New Modules'), │ ('3', 'Guidelines for Handling Bug Reports'), │ ('4', 'Deprecation of Standard Modules'), │ ('5', 'Guidelines for Language Evolution'), │ ('6', 'Bug Fix Releases'), │ ('7', 'Style Guide for C Code'), │ ('8', 'Style Guide for Python Code'), │ ('9', 'Sample Plaintext PEP Template'), │ ('10', 'Voting Guidelines') ] ``` Add `--reuse-env` option if you want to reuse the environment created last time. You can also add `[tool.pdm]` section to the script metadata to configure PDM. For example: ```python # test_script.py # /// script # requires-python = ">=3.11" # dependencies = [ # "requests<3", # "rich", # ] # # [[tool.pdm.source]] # Use a custom index # url = "https://mypypi.org/simple" # name = "pypi" # /// ``` Read the [specification](https://packaging.python.org/en/latest/specifications/inline-script-metadata/#inline-script-metadata) for more details. ## User Scripts PDM also supports custom script shortcuts in the optional `[tool.pdm.scripts]` section of `pyproject.toml`. !!! NOTE "Confuse with `[project.scripts]`?" There is another field `[project.scripts]` in `pyproject.toml`, and the scripts can also be invoked with `pdm run`. It's used to define the console script entry points to be installed with the package. Therefore, the executables can only be run after the project itself is installed into the environment. That is to say, you must have `distribution = true`. In contrast, `[tool.pdm.scripts]` defines some tasks to be run in your project. It works for projects regardless of whether the `distribution` is `true` or `false`. The tasks are primarily for development and testing purposes and support more types and settings, as will be shown later., you can regard it as a replacement for `Makefile`. It doesn't require the project to be installed but requires the existence of a `pyproject.toml` file. See more explanations about `[project.scripts]` [here](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#creating-executable-scripts). You can then run `pdm run ` to invoke the script in the context of your PDM project. For example: ```toml [tool.pdm.scripts] start = "flask run -p 54321" ``` And then in your terminal: ```bash $ pdm run start Flask server started at http://127.0.0.1:54321 ``` Any following arguments will be appended to the command: ```bash $ pdm run start -h 0.0.0.0 Flask server started at http://0.0.0.0:54321 ``` !!! note "Yarn-like script shortcuts" There is a builtin shortcut making all scripts available as root commands as long as the script does not conflict with any builtin or plugin-contributed command. Said otherwise, if you have a `start` script, you can run both `pdm run start` and `pdm start`. But if you have an `install` script, only `pdm run install` will run it, `pdm install` will still run the builtin `install` command. PDM supports 4 types of scripts: ### `cmd` Plain text scripts are regarded as normal command, or you can explicitly specify it: ```toml [tool.pdm.scripts] start = {cmd = "flask run -p 54321"} ``` In some cases, such as when wanting to add comments between parameters, it might be more convenient. to specify the command as an array instead of a string: ```toml [tool.pdm.scripts] start = {cmd = [ "flask", "run", # Important comment here about always using port 54321 "-p", "54321" ]} ``` ### `shell` Shell scripts can be used to run more shell-specific tasks, such as pipeline and output redirecting. This is basically run via `subprocess.Popen()` with `shell=True`: ```toml [tool.pdm.scripts] filter_error = {shell = "cat error.log|grep CRITICAL > critical.log"} ``` ### `call` The script can be also defined as calling a python function in the form `:`: ```toml [tool.pdm.scripts] foobar = {call = "foo_package.bar_module:main"} ``` The function can be supplied with literal arguments: ```toml [tool.pdm.scripts] foobar = {call = "foo_package.bar_module:main('dev')"} ``` ### `composite` This script kind execute other defined scripts: ```toml [tool.pdm.scripts] lint = "flake8" test = "pytest" all = {composite = ["lint", "test"]} ``` Running `pdm run all` will run `lint` first and then `test` if `lint` succeeded. +++ 2.13.0 To override the default behavior and continue the execution of the remaining scripts after a failure, set the `keep_going` option to `true`: ```toml [tool.pdm.scripts] lint = "flake8" test = "pytest" all.composite = ["lint", "test"] all.keep_going = true ``` If `keep_going` set to `true` return code of composite script is either '0' if all succeeded or the code of last failed individual script. You can also provide arguments to the called scripts: ```toml [tool.pdm.scripts] lint = "flake8" test = "pytest" all = {composite = ["lint mypackage/", "test -v tests/"]} ``` !!! note Argument passed on the command line are given to each called task. You can also use the `composite` script to combine multiple commands: ```toml [tool.pdm.scripts] mytask.composite = [ "echo 'Hello'", "echo 'World'" ] ``` ## Script Options ### `env` All environment variables set in the current shell can be seen by `pdm run` and will be expanded when executed. Besides, you can also define some fixed environment variables in your `pyproject.toml`: ```toml [tool.pdm.scripts] start.cmd = "flask run -p 54321" start.env = {FOO = "bar", FLASK_DEBUG = "1"} ``` Note how we use [TOML's syntax](https://github.com/toml-lang/toml) to define a composite dictionary. !!! note "About environment variable substitution" Variables in script specifications can be substituted in all script types. In `cmd` scripts, only `${VAR}` syntax is supported on all platforms, however in `shell` scripts, the syntax is platform-dependent. For example, Windows cmd uses `%VAR%` while bash uses `$VAR`. !!! note Environment variables specified on a composite task level will override those defined by called tasks. ### `env_file` You can also store all environment variables in a dotenv file and let PDM read it: ```toml [tool.pdm.scripts] start.cmd = "flask run -p 54321" start.env_file = ".env" ``` The variables within the dotenv file will *not* override any existing environment variables. If you want the dotenv file to override existing environment variables use the following: ```toml [tool.pdm.scripts] start.cmd = "flask run -p 54321" start.env_file.override = ".env" ``` !!! note "Environment variable loading order" Env vars loaded from different sources are loaded in the following order: 1. OS environment variables 2. Project environments such as `PDM_PROJECT_ROOT`, `PATH`, `VIRTUAL_ENV`, etc 3. Dotenv file specified by `env_file` 4. Env vars mapping specified by `env` Env vars from the latter sources will override those from the former sources. A dotenv file specified on a composite task level will override those defined by called tasks. An env var can contain a reference to another env var from the sources loaded before, for example: ``` VAR=42 FOO=hello-${VAR} ``` will result in `FOO=hello-42`. The reference can also contain a default value with the syntax `${VAR:-default}`. ### `working_dir` +++ 2.13.0 You can set the current working directory for the script: ```toml [tool.pdm.scripts] start.cmd = "flask run -p 54321" start.working_dir = "subdir" ``` Relative paths are resolved against the project root. +++ 2.20.2 To identify the original calling working directory, each script gets the environment variable `PDM_RUN_CWD` injected. ### `site_packages` To make sure the running environment is properly isolated from the outer Python interpreter, site-packages from the selected interpreter WON'T be loaded into `sys.path`, unless any of the following conditions holds: 1. The executable is from `PATH` but not inside the `__pypackages__` folder. 2. `-s/--site-packages` flag is following `pdm run`. 3. `site_packages = true` is in either the script table or the global setting key `_`. Note that site-packages will always be loaded if running with PEP 582 enabled(without the `pdm run` prefix). ### Shared Options If you want the options to be shared by all tasks run by `pdm run`, you can write them under a special key `_` in `[tool.pdm.scripts]` table: ```toml [tool.pdm.scripts] _.env_file = ".env" start = "flask run -p 54321" migrate_db = "flask db upgrade" ``` Besides, inside the tasks, `PDM_PROJECT_ROOT` environment variable will be set to the project root. ### Arguments placeholder By default, all user provided extra arguments are simply appended to the command (or to all the commands for `composite` tasks). If you want more control over the user provided extra arguments, you can use the `{args}` placeholder. It is available for all script types and will be interpolated properly for each: ```toml [tool.pdm.scripts] cmd = "echo '--before {args} --after'" shell = {shell = "echo '--before {args} --after'"} composite = {composite = ["cmd --something", "shell {args}"]} ``` will produce the following interpolations (those are not real scripts, just here to illustrate the interpolation): ```shell $ pdm run cmd --user --provided --before --user --provided --after $ pdm run cmd --before --after $ pdm run shell --user --provided --before --user --provided --after $ pdm run shell --before --after $ pdm run composite --user --provided cmd --something shell --before --user --provided --after $ pdm run composite cmd --something shell --before --after ``` You may optionally provide default values that will be used if no user arguments are provided: ```toml [tool.pdm.scripts] test = "echo '--before {args:--default --value} --after'" ``` will produce the following: ```shell $ pdm run test --user --provided --before --user --provided --after $ pdm run test --before --default --value --after ``` !!! note As soon a placeholder is detected, arguments are not appended anymore. This is important for `composite` scripts because if a placeholder is detected on one of the subtasks, none for the subtasks will have the arguments appended, you need to explicitly pass the placeholder to every nested command requiring it. !!! note `call` scripts don't support the `{args}` placeholder as they have access to `sys.argv` directly to handle such complex cases and more. ### `{pdm}` placeholder Sometimes you may have multiple PDM installations, or `pdm` installed with a different name. This could for example occur in a CI/CD situation, or when working with different PDM versions in different repos. To make your scripts more robust you can use `{pdm}` to use the PDM entrypoint executing the script. This will expand to `{sys.executable} -m pdm`. ```toml [tool.pdm.scripts] whoami = { shell = "echo `{pdm} -V` was called as '{pdm} -V'" } ``` will produce the following output: ```shell $ pdm whoami PDM, version 0.1.dev2501+g73651b7.d20231115 was called as /usr/bin/python3 -m pdm -V $ pdm2.8 whoami PDM, version 2.8.0 was called as /venvs/pdm2-8/bin/python -m pdm -V ``` !!! note While the above example uses PDM 2.8, this functionality was introduced in the 2.10 series and only backported for the showcase. ## Show the List of Scripts Use `pdm run --list/-l` to show the list of available script shortcuts: ```bash $ pdm run --list ╭─────────────┬───────┬───────────────────────────╮ │ Name │ Type │ Description │ ├─────────────┼───────┼───────────────────────────┤ │ test_cmd │ cmd │ flask db upgrade │ │ test_script │ call │ call a python function │ │ test_shell │ shell │ shell command │ ╰─────────────┴───────┴───────────────────────────╯ ``` You can add an `help` option with the description of the script, and it will be displayed in the `Description` column in the above output. !!! note Tasks with a name starting with an underscore (`_`) are considered internal (helpers...) and are not shown in the listing. ## Pre & Post Scripts Like `npm`, PDM also supports tasks composition by pre and post scripts, pre script will be run before the given task and post script will be run after. ```toml [tool.pdm.scripts] pre_compress = "{{ Run BEFORE the `compress` script }}" compress = "tar czvf compressed.tar.gz data/" post_compress = "{{ Run AFTER the `compress` script }}" ``` In this example, `pdm run compress` will run all these 3 scripts sequentially. !!! note "The pipeline fails fast" In a pipeline of pre - self - post scripts, a failure will cancel the subsequent execution. ## Hook Scripts Under certain situations PDM will look for some special hook scripts for execution: - `post_init`: Run after `pdm init` - `pre_install`: Run before installing packages - `post_install`: Run after packages are installed - `pre_lock`: Run before dependency resolution - `post_lock`: Run after dependency resolution - `pre_build`: Run before building distributions - `post_build`: Run after distributions are built - `pre_publish`: Run before publishing distributions - `post_publish`: Run after distributions are published - `pre_script`: Run before any script - `post_script`: Run after any script - `pre_run`: Run once before run script invocation - `post_run`: Run once after run script invocation !!! note Pre & post scripts can't receive any arguments. !!! note "Avoid name conflicts" If there exists an `install` scripts under `[tool.pdm.scripts]` table, `pre_install` scripts can be triggered by both `pdm install` and `pdm run install`. So it is recommended to not use the preserved names. !!! note Composite tasks can also have pre and post scripts. Called tasks will run their own pre and post scripts. ## Skipping scripts Because, sometimes it is desirable to run a script but without its hooks or pre and post scripts, there is a `--skip=:all` which will disable all hooks, pre and post. There is also `--skip=:pre` and `--skip=:post` allowing to respectively skip all `pre_*` hooks and all `post_*` hooks. It is also possible to need a pre script but not the post one, or to need all tasks from a composite tasks except one. For those use cases, there is a finer grained `--skip` parameter accepting a list of tasks or hooks name to exclude. ```bash pdm run --skip pre_task1,task2 my-composite ``` This command will run the `my-composite` task and skip the `pre_task1` hook as well as the `task2` and its hooks. You can also provide you skip list in `PDM_SKIP_HOOKS` environment variable but it will be overridden as soon as the `--skip` parameter is provided. There is more details on hooks and pre/post scripts behavior on [the dedicated hooks page](hooks.md). pdm-2.23.1/docs/usage/template.md000066400000000000000000000062601477560627500165770ustar00rootroot00000000000000# Create Project From a Template Similar to `yarn create` and `npm create`, PDM also supports initializing or creating a project from a template. The template is given as a positional argument of `pdm init`, in one of the following forms: - `pdm init django` - Initialize the project from the template `https://github.com/pdm-project/template-django` - `pdm init https://github.com/frostming/pdm-template-django` - Initialize the project from a Git URL. Both HTTPS and SSH URL are acceptable. - `pdm init django@v2` - To check out the specific branch or tag. Full Git URL also supports it. - `pdm init /path/to/template` - Initialize the project from a template directory on local filesystem. - `pdm init minimal` - Initialize with the builtin "minimal" template, that only generates a `pyproject.toml`. And `pdm init` will use the default template built in. The project will be initialized at the current directory, existing files with the same name will be overwritten. You can also use the `-p ` option to create a project at a new path. ## Contribute a template According to the first form of the template argument, `pdm init ` will refer to the template repository located at `https://github.com/pdm-project/template-`. To contribute a template, you can create a template repository and establish a request to transfer the ownership to `pdm-project` organization(it can be found at the bottom of the repository settings page). The administrators of the organization will review the request and complete the subsequent steps. You will be added as the repository maintainer if the transfer is accepted. ## Requirements for a template A template repository must be a pyproject-based project, which contains a `pyproject.toml` file with PEP-621 compliant metadata. No other special config files are required. ## Project name replacement On initialization, the project name in the template will be replaced by the name of the new project. This is done by a recursive full-text search and replace. The import name, which is derived from the project name by replacing all non-alphanumeric characters with underscores and lowercasing, will also be replaced in the same way. For example, if the project name is `foo-project` in the template and you want to initialize a new project named `bar-project`, the following replacements will be made: - `foo-project` -> `bar-project` in all `.md` files and `.rst` files - `foo_project` -> `bar_project` in all `.py` files - `foo_project` -> `bar_project` in the directory name - `foo_project.py` -> `bar_project.py` in the file name Therefore, we don't support name replacement if the import name isn't derived from the project name. ## Use other project generators If you are seeking for a more powerful project generator, you can use [cookiecutter](https://github.com/cookiecutter/cookiecutter) via `--cookiecutter` option and [copier](https://github.com/copier-org/copier) via `--copier` option. You need to install `cookiecutter` and `copier` respectively to use them. You can do this by running `pdm self add `. To use them: ```bash pdm init --cookiecutter gh:cjolowicz/cookiecutter-hypermodern-python # or pdm init --copier gh:pawamoy/copier-pdm --UNSAFE ``` pdm-2.23.1/docs/usage/uv.md000066400000000000000000000025611477560627500154160ustar00rootroot00000000000000# Use uv (Experimental) +++ 2.19.0 PDM has experimental support for [uv](https://github.com/astral-sh/uv) as the resolver and installer. To enable it: ``` pdm config use_uv true ``` PDM will automatically detect the `uv` binary on your system. You need to install `uv` first. See [uv's installation guide](https://docs.astral.sh/uv/getting-started/installation/) for more details. ## Reuse the Python installations of uv uv also supports installing Python interpreters. To avoid overhead, you can configure PDM to reuse the Python installations of uv by: ``` pdm config python.install_root $(uv python dir) ``` ## Limitations Despite the significant performance improvements brought by uv, it is important to note the following limitations: - The cache files are stored in uv's own cache directory, and you have to use `uv` command to manage them. - PEP 582 local packages layout is not supported. - `inherit_metadata` lock strategy is not supported by uv. This will be ignored when writing to the lock file. - Update strategies other than `all` and `reuse` are not supported. - Editable requirement must be a local path. Requirements like `-e git+` are not supported. - `excludes` settings under `[tool.pdm.resolution]` are not supported. - Cross-platform lock targets are not supported by uv resolver, i.e., you can lock for platforms that are different from the current. pdm-2.23.1/docs/usage/venv.md000066400000000000000000000217321477560627500157430ustar00rootroot00000000000000# Working with Virtual Environments When you run [`pdm init`](../reference/cli.md#init) command, PDM will [ask for the Python interpreter to use](./project.md#choose-a-python-interpreter) in the project, which is the base interpreter to install dependencies and run tasks. Compared to [PEP 582](https://www.python.org/dev/peps/pep-0582/), virtual environments are considered more mature and have better support in the Python ecosystem as well as IDEs. Therefore, virtualenv is the default mode if not configured otherwise. !!! NOTE "Configure pdm to use virtual environment or PEP 582" By default pdm is configured to use virtual environment instead of PEP 582. But this behavior can be changed with `pdm config python.use_venv False` config variable. **Virtual environments will be used if the project interpreter (the interpreter stored in `.pdm-python`, which can be checked by `pdm info`) is from a virtualenv.** ## Virtualenv auto-creation By default, PDM prefers to use the virtualenv layout as other package managers do. When you run `pdm install` the first time on a new PDM-managed project, whose Python interpreter is not decided yet, PDM will create a virtualenv in `/.venv`, and install dependencies into it. In the interactive session of `pdm init`, PDM will also ask to create a virtualenv for you. You can choose the backend used by PDM to create a virtualenv. Currently it supports three backends: - [`virtualenv`](https://virtualenv.pypa.io/)(default) - `venv` - `conda` You can change it by `pdm config venv.backend [virtualenv|venv|conda]`. +++ 2.13.0 Moreover, when `python.use_venv` config is set to `true`, PDM will always try to create a virtualenv when using `pdm use` to switch the Python interpreter. ## Create a virtualenv yourself You can create more than one virtualenvs with whatever Python version you want. ```bash # Create a virtualenv based on 3.9 interpreter pdm venv create 3.9 # Assign a different name other than the version string pdm venv create --name for-test 3.9 # Use venv as the backend to create, support 3 backends: virtualenv(default), venv, conda pdm venv create --with venv 3.10 ``` ## The location of virtualenvs If no `--name` is given, PDM will create the venv in `/.venv`. Otherwise, virtualenvs go to the location specified by the `venv.location` configuration. They are named as `--` to avoid name collision. You can disable the in-project virtualenv creation by `pdm config venv.in_project false`. And all virtualenvs will be created under `venv.location`. ## Reuse the virtualenv you created elsewhere You can tell PDM to use a virtualenv you created in preceding steps, with [`pdm use`](../reference/cli.md#use): ```bash pdm use -f /path/to/venv ``` ## Virtualenv auto-detection When no interpreter is stored in the project config or `PDM_IGNORE_SAVED_PYTHON` env var is set, PDM will try to detect possible virtualenvs to use: - `venv`, `env`, `.venv` directories in the project root - The currently activated virtualenv, unless `PDM_IGNORE_ACTIVE_VENV` is set ## List all virtualenvs created with this project ```bash $ pdm venv list Virtualenvs created with this project: - 3.8.6: C:\Users\Frost Ming\AppData\Local\pdm\pdm\venvs\test-project-8Sgn_62n-3.8.6 - for-test: C:\Users\Frost Ming\AppData\Local\pdm\pdm\venvs\test-project-8Sgn_62n-for-test - 3.9.1: C:\Users\Frost Ming\AppData\Local\pdm\pdm\venvs\test-project-8Sgn_62n-3.9.1 ``` ## Show the path or python interpreter of a virtualenv ```bash pdm venv --path for-test pdm venv --python for-test ``` ## Remove a virtualenv ```bash $ pdm venv remove for-test Virtualenvs created with this project: Will remove: C:\Users\Frost Ming\AppData\Local\pdm\pdm\venvs\test-project-8Sgn_62n-for-test, continue? [y/N]:y Removed C:\Users\Frost Ming\AppData\Local\pdm\pdm\venvs\test-project-8Sgn_62n-for-test ``` ## Activate a virtualenv Instead of spawning a subshell like what `pipenv` and `poetry` do, `pdm venv` doesn't create the shell for you but print the activate command to the console. In this way you won't leave the current shell. You can then feed the output to `eval` to activate the virtualenv: === "bash/csh/zsh" ```bash $ eval $(pdm venv activate for-test) (test-project-for-test) $ # Virtualenv entered ``` === "Fish" ```bash $ eval (pdm venv activate for-test) ``` === "Powershell" ```ps1 PS1> Invoke-Expression (pdm venv activate for-test) ``` Additionally, if the project interpreter is a venv Python, you can omit the name argument following activate. !!! NOTE `venv activate` **does not** switch the Python interpreter used by the project. It only changes the shell by injecting the virtualenv paths to environment variables. For the aforementioned purpose, use the `pdm use` command. For more CLI usage, see the [`pdm venv`](../reference/cli.md#venv) documentation. !!! TIP "Looking for `pdm shell`?" PDM doesn't provide a `shell` command because many fancy shell functions may not work perfectly in a subshell, which brings a maintenance burden to support all the corner cases. However, you can still gain the ability via the following ways: - Use `pdm run $SHELL`, this will spawn a subshell with the environment variables set properly. **The subshell can be quit with `exit` or `Ctrl+D`.** - Add a shell function to activate the virtualenv, here is an example of BASH function that also works on ZSH: ```bash pdm() { local command=$1 if [[ "$command" == "shell" ]]; then eval $(pdm venv activate) else command pdm $@ fi } ``` Copy and paste this function to your `~/.bashrc` file and restart your shell. For `fish` shell you can put the following into your `~/fish/config.fish` or in `~/.config/fish/config.fish` ```fish function pdm set cmd $argv[1] if test "$cmd" = "shell" eval (pdm venv activate) else command pdm $argv end end ``` Now you can run `pdm shell` to activate the virtualenv. **The virtualenv can be deactivated with `deactivate` command as usual.** ## Prompt customization By default when you activate a virtualenv, the prompt will show: `{project_name}-{python_version}`. For example if your project is named `test-project`: ```bash $ eval $(pdm venv activate for-test) (test-project-3.10) $ # {project_name} == test-project and {python_version} == 3.10 ``` The format can be customized before virtualenv creation with the [`venv.prompt`](../reference/configuration.md) configuration or `PDM_VENV_PROMPT` environment variable (before a `pdm init` or `pdm venv create`). Available variables are: - `project_name`: name of your project - `python_version`: version of Python (used by the virtualenv) ```bash $ PDM_VENV_PROMPT='{project_name}-py{python_version}' pdm venv create --name test-prompt $ eval $(pdm venv activate test-prompt) (test-project-py3.10) $ ``` ## Run a command in a virtual environment without activating it ```bash # Run a script pdm run --venv test test # Install packages pdm sync --venv test # List the packages installed pdm list --venv test ``` There are other commands supporting `--venv` flag or `PDM_IN_VENV` environment variable, see the [CLI reference](../reference/cli.md). You should create the virtualenv with `pdm venv create --name ` before using this feature. ## Switch to a virtualenv as the project environment By default, if you use `pdm use` and select a non-venv Python, the project will be switched to [PEP 582 mode](./pep582.md). We also allow you to switch to a named virtual environment via the `--venv` flag: ```bash # Switch to a virtualenv named test $ pdm use --venv test # Switch to the in-project venv located at $PROJECT_ROOT/.venv $ pdm use --venv in-project ``` ## Disable virtualenv mode You can disable the auto-creation and auto-detection for virtualenv by `pdm config python.use_venv false`. **If venv is disabled, PEP 582 mode will always be used even if the selected interpreter is from a virtualenv.** ## Including pip in your virtual environment By default PDM will not include `pip` in virtual environments. This increases isolation by ensuring that _only your dependencies_ are installed in the virtual environment. To install `pip` once (if for example you want to install arbitrary dependencies in CI) you can run: ```bash # Install pip in the virtual environment pdm run python -m ensurepip # Install arbitrary dependencies # These dependencies are not checked for conflicts against lockfile dependencies! pdm run python -m pip install coverage ``` Or you can create the virtual environment with `--with-pip`: ```bash pdm venv create --with-pip 3.9 ``` See the [ensurepip docs](https://docs.python.org/3/library/ensurepip.html) for more details on `ensurepip`. If you want to permanently configure PDM to include `pip` in virtual environments you can use the [`venv.with_pip`](../reference/configuration.md) configuration. pdm-2.23.1/install-pdm.py000066400000000000000000000350521477560627500152050ustar00rootroot00000000000000from __future__ import annotations import argparse import dataclasses import io import json import os import platform import shutil import site import subprocess import sys import urllib.request from pathlib import Path from tempfile import TemporaryDirectory from typing import Sequence if sys.version_info < (3, 8): # noqa: UP036 sys.exit("Python 3.8 or above is required to install PDM.") _plat = platform.system() MACOS = _plat == "Darwin" WINDOWS = _plat == "Windows" REPO = "https://github.com/pdm-project/pdm" JSON_URL = "https://pypi.org/pypi/pdm/json" FOREGROUND_COLORS = { "black": 30, "red": 31, "green": 32, "yellow": 33, "blue": 34, "magenta": 35, "cyan": 36, "white": 37, } def _call_subprocess(args: list[str]) -> int: try: return subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True).returncode except subprocess.CalledProcessError as e: print(f"An error occurred when executing {args}:", file=sys.stderr) print(e.output.decode("utf-8"), file=sys.stderr) sys.exit(e.returncode) def _echo(text: str) -> None: sys.stdout.write(text + "\n") if WINDOWS: import winreg def _get_win_folder_with_ctypes(csidl_name: str) -> str: import ctypes csidl_const = { "CSIDL_APPDATA": 26, "CSIDL_COMMON_APPDATA": 35, "CSIDL_LOCAL_APPDATA": 28, }[csidl_name] buf = ctypes.create_unicode_buffer(1024) ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) # Downgrade to short path name if have highbit chars. See # . has_high_char = False for c in buf: if ord(c) > 255: has_high_char = True break if has_high_char: buf2 = ctypes.create_unicode_buffer(1024) if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): buf = buf2 return buf.value def _get_win_folder_from_registry(csidl_name: str) -> str: """This is a fallback technique at best. I'm not sure if using the registry for this guarantees us the correct answer for all CSIDL_* names. """ shell_folder_name = { "CSIDL_APPDATA": "AppData", "CSIDL_COMMON_APPDATA": "Common AppData", "CSIDL_LOCAL_APPDATA": "Local AppData", }[csidl_name] key = winreg.OpenKey( winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders", ) dir, _ = winreg.QueryValueEx(key, shell_folder_name) return dir try: from ctypes import windll # noqa: F401 _get_win_folder = _get_win_folder_with_ctypes except ImportError: _get_win_folder = _get_win_folder_from_registry def _remove_path_windows(target: Path) -> None: value = os.path.normcase(target) with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as root: with winreg.OpenKey(root, "Environment", 0, winreg.KEY_ALL_ACCESS) as env_key: try: old_value, type_ = winreg.QueryValueEx(env_key, "PATH") paths = [os.path.normcase(item) for item in old_value.split(os.pathsep)] if value not in paths: return new_value = os.pathsep.join(p for p in paths if p != value) winreg.SetValueEx(env_key, "PATH", 0, type_, new_value) except FileNotFoundError: return def _add_to_path(target: Path) -> None: value = os.path.normcase(target) if WINDOWS: with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as root: with winreg.OpenKey(root, "Environment", 0, winreg.KEY_ALL_ACCESS) as env_key: try: old_value, type_ = winreg.QueryValueEx(env_key, "PATH") if value in [os.path.normcase(item) for item in old_value.split(os.pathsep)]: return except FileNotFoundError: old_value, type_ = "", winreg.REG_EXPAND_SZ new_value = os.pathsep.join([old_value, value]) if old_value else value winreg.SetValueEx(env_key, "PATH", 0, type_, new_value) _echo( "Post-install: {} is added to PATH env, please restart your terminal to take effect".format( colored("green", value) ) ) else: paths = [os.path.normcase(p) for p in os.getenv("PATH", "").split(os.pathsep)] if value in paths: return _echo( "Post-install: Please add {} to PATH by executing:\n {}".format( colored("green", value), colored("cyan", f"export PATH={value}:$PATH"), ) ) def support_ansi() -> bool: if WINDOWS: return ( os.getenv("ANSICON") is not None or os.getenv("WT_SESSION") is not None or "ON" == os.getenv("ConEmuANSI") or "xterm" == os.getenv("Term") ) if not hasattr(sys.stdout, "fileno"): return False try: return os.isatty(sys.stdout.fileno()) except io.UnsupportedOperation: return False def colored(color: str, text: str, bold: bool = False) -> str: if not support_ansi(): return text codes = [FOREGROUND_COLORS[color]] if bold: codes.append(1) return "\x1b[{}m{}\x1b[0m".format(";".join(map(str, codes)), text) @dataclasses.dataclass class Installer: location: str | None = None version: str | None = None prerelease: bool = False additional_deps: Sequence[str] = () skip_add_to_path: bool = False output_path: str | None = None frozen_deps: bool = True def __post_init__(self): self._path = self._decide_path() self._path.mkdir(parents=True, exist_ok=True) def _decide_path(self) -> Path: if self.location is not None: return Path(self.location).expanduser().resolve() if WINDOWS: const = "CSIDL_APPDATA" path = os.path.normpath(_get_win_folder(const)) path = os.path.join(path, "pdm") elif MACOS: path = os.path.expanduser("~/Library/Application Support/pdm") else: path = os.getenv("XDG_DATA_HOME", os.path.expanduser("~/.local/share")) path = os.path.join(path, "pdm") return Path(path).resolve() def _make_env(self) -> Path: venv_path = self._path / "venv" _echo( "Installing {} ({}): {}".format( colored("green", "PDM", bold=True), colored("yellow", self.version or "latest"), colored("cyan", "Creating virtual environment"), ) ) try: import venv venv.create(venv_path, clear=False, with_pip=True) except (ModuleNotFoundError, subprocess.CalledProcessError): try: import virtualenv except ModuleNotFoundError: python_version = f"{sys.version_info.major}.{sys.version_info.minor}" url = f"https://bootstrap.pypa.io/virtualenv/{python_version}/virtualenv.pyz" with TemporaryDirectory(prefix="pdm-installer-") as tempdir: virtualenv_zip = Path(tempdir) / "virtualenv.pyz" urllib.request.urlretrieve(url, virtualenv_zip) _call_subprocess([sys.executable, str(virtualenv_zip), str(venv_path)]) else: virtualenv.cli_run([str(venv_path)]) return venv_path def _install(self, venv_path: Path) -> None: _echo( "Installing {} ({}): {}".format( colored("green", "PDM", bold=True), colored("yellow", self.version or "latest"), colored("cyan", "Installing PDM and dependencies"), ) ) if WINDOWS: venv_python = venv_path / "Scripts/python.exe" else: venv_python = venv_path / "bin/python" # Re-install the venv pip to ensure it's not DEBUNDLED # See issue/685 try: _call_subprocess([str(venv_python), "-m", "ensurepip"]) except SystemExit: pass _call_subprocess([str(venv_python), "-m", "pip", "install", "-IU", "pip"]) locked = "[locked]" if self.frozen_deps else "" if self.version: if self.version.upper() == "HEAD": req = f"pdm{locked} @ git+{REPO}.git@main" else: try: parsed = tuple(map(int, self.version.split("."))) except ValueError: extra = "" else: extra = locked if parsed >= (2, 17) else "" req = f"pdm{extra}=={self.version}" else: req = f"pdm{locked}" args = [req] + [d for d in self.additional_deps if d] pip_cmd = [str(venv_python), "-Im", "pip", "install", *args] _call_subprocess(pip_cmd) def _make_bin(self, venv_path: Path) -> Path: if self.location: bin_path = self._path / "bin" else: userbase = Path(site.getuserbase()) bin_path = userbase / ("Scripts" if WINDOWS else "bin") _echo( "Installing {} ({}): {} {}".format( colored("green", "PDM", bold=True), colored("yellow", self.version or "latest"), colored("cyan", "Making binary at"), colored("green", str(bin_path)), ) ) bin_path.mkdir(parents=True, exist_ok=True) if WINDOWS: script = bin_path / "pdm.exe" target = venv_path / "Scripts" / "pdm.exe" else: script = bin_path / "pdm" target = venv_path / "bin" / "pdm" if script.exists(): script.unlink() try: script.symlink_to(target) except OSError: shutil.copy(target, script) return bin_path def _post_install(self, venv_path: Path, bin_path: Path) -> None: if WINDOWS: script = bin_path / "pdm.exe" python = venv_path / "Scripts/python.exe" else: script = bin_path / "pdm" python = venv_path / "bin/python" subprocess.check_call([str(script), "--help"]) print() pdm_version = ( subprocess.check_output([python, "-c", 'from importlib.metadata import version; print(version("pdm"))']) .decode("utf-8") .strip() ) _echo( "Successfully installed: {} ({}) at {}".format( colored("green", "PDM", bold=True), colored("yellow", pdm_version), colored("cyan", str(script)), ) ) if not self.skip_add_to_path: _add_to_path(bin_path) self._write_output(venv_path, script, pdm_version) def _write_output(self, venv_path: Path, script: Path, pdm_version: str) -> None: if not self.output_path: return print("Writing output to", colored("green", self.output_path)) output = { "pdm_version": pdm_version, "pdm_bin": str(script), "install_python_version": platform.python_version(), "install_location": str(venv_path), } with open(self.output_path, "w") as f: json.dump(output, f, indent=2) def install(self) -> None: venv = self._make_env() self._install(venv) bin_dir = self._make_bin(venv) self._post_install(venv, bin_dir) def uninstall(self) -> None: venv_path = self._path / "venv" if not venv_path.exists(): _echo( "{} is not currently installed.".format( colored("green", "PDM", bold=True), ) ) return _echo( "Uninstalling {}: {}".format( colored("green", "PDM", bold=True), colored("cyan", "Removing venv and script"), ) ) if self.location: bin_path = self._path / "bin" else: userbase = Path(site.getuserbase()) bin_path = userbase / ("Scripts" if WINDOWS else "bin") if WINDOWS: script = bin_path / "pdm.exe" else: script = bin_path / "pdm" shutil.rmtree(venv_path) script.unlink() if WINDOWS: _remove_path_windows(bin_path) print() _echo("Successfully uninstalled") def main(): parser = argparse.ArgumentParser() parser.add_argument( "-v", "--version", help="Specify the version to be installed, or HEAD to install from the main branch", default=os.getenv("PDM_VERSION"), ) parser.add_argument( "--prerelease", action="store_true", help="Allow prereleases to be installed", default=os.getenv("PDM_PRERELEASE"), ) parser.add_argument( "--no-frozen-deps", action="store_false", dest="frozen_deps", default=not bool(os.getenv("PDM_NO_FROZEN_DEPS")), help="Do not install frozen dependency versions", ) parser.add_argument( "--remove", action="store_true", help="Remove the PDM installation", default=os.getenv("PDM_REMOVE"), ) parser.add_argument( "-p", "--path", help="Specify the location to install PDM", default=os.getenv("PDM_HOME"), ) parser.add_argument( "-d", "--dep", action="append", default=os.getenv("PDM_DEPS", "").split(","), help="Specify additional dependencies, can be given multiple times", ) parser.add_argument( "--skip-add-to-path", action="store_true", help="Do not add binary to the PATH.", default=os.getenv("PDM_SKIP_ADD_TO_PATH"), ) parser.add_argument("-o", "--output", help="Output file to write the installation info to") options = parser.parse_args() installer = Installer( location=options.path, version=options.version, prerelease=options.prerelease, additional_deps=options.dep, skip_add_to_path=options.skip_add_to_path, output_path=options.output, frozen_deps=options.frozen_deps, ) if options.remove: installer.uninstall() else: installer.install() if __name__ == "__main__": main() pdm-2.23.1/install-pdm.py.sha256000066400000000000000000000001211477560627500162010ustar00rootroot00000000000000e91cea16112b6e8e754944f4a29fe57fe5e206d42aee61fd35c77a9124388de3 install-pdm.py pdm-2.23.1/mkdocs.yml000066400000000000000000000065731477560627500144200ustar00rootroot00000000000000site_name: PDM repo_url: https://github.com/pdm-project/pdm edit_uri: edit/main/docs theme: name: material palette: - scheme: default media: "(prefers-color-scheme: light)" primary: deep purple accent: teal toggle: icon: material/weather-sunny name: Switch to dark mode - scheme: slate media: "(prefers-color-scheme: dark)" primary: deep purple accent: teal toggle: icon: material/weather-night name: Switch to light mode font: text: Open Sans code: Fira Code logo: assets/logo.svg favicon: assets/logo.svg features: - content.code.copy - navigation.tabs - navigation.tabs.sticky custom_dir: docs/overrides plugins: - search - markdown-exec - "mkdocs-version-annotations": version_added_admonition: "tip" - mkdocstrings: enable_inventory: true handlers: python: options: docstring_style: google - redirects: redirect_maps: 'plugin/fixtures.md': 'dev/fixtures.md' 'plugin/write.md': 'dev/write.md' 'pyproject/build.md': 'reference/build.md' 'plugin/reference.md': 'reference/api.md' 'usage/cli_reference.md': 'reference/cli.md' 'usage/configuration.md': 'reference/configuration.md' 'pyproject/pep621.md': 'reference/pep621.md' nav: - Usage: - Introduction: index.md - usage/project.md - usage/dependency.md - Lock Files: - usage/lockfile.md - usage/lock-targets.md - usage/uv.md - usage/publish.md - usage/config.md - usage/scripts.md - usage/hooks.md - usage/advanced.md - usage/venv.md - usage/pep582.md - usage/template.md - Reference: - reference/pep621.md - reference/configuration.md - reference/build.md - reference/cli.md - reference/api.md - Development: - dev/write.md - dev/fixtures.md - dev/contributing.md - dev/changelog.md - dev/benchmark.md - Sponsor: https://github.com/sponsors/pdm-project markdown_extensions: - pymdownx.highlight: linenums: true - pymdownx.tabbed: alternate_style: true - pymdownx.details - pymdownx.snippets: restrict_base_path: false - admonition - tables - toc: permalink: "#" - attr_list - pymdownx.emoji: emoji_index: !!python/name:material.extensions.emoji.twemoji emoji_generator: !!python/name:material.extensions.emoji.to_svg - pymdownx.superfences: custom_fences: - name: mermaid class: mermaid format: !!python/name:pymdownx.superfences.fence_code_format copyright: Copyright © 2019 Frost Ming extra: version: provider: mike analytics: provider: google property: G-RP4PM5PGLN social: - icon: fontawesome/brands/github link: https://github.com/pdm-project/pdm - icon: fontawesome/brands/twitter link: https://twitter.com/pdm_project - icon: fontawesome/brands/discord link: https://discord.gg/Phn8smztpv chatbot: url: https://2prxfnwkygf4vexczrbpcq.streamlit.app/?embed=true alternate: - name: '🇬🇧 English' link: /en/ lang: en - name: '🇨🇳 简体中文' link: /zh-cn/ lang: zh extra_css: - assets/extra.css extra_javascript: - assets/extra.js watch: - src pdm-2.23.1/news/000077500000000000000000000000001477560627500133565ustar00rootroot00000000000000pdm-2.23.1/news/.gitkeep000066400000000000000000000000001477560627500147750ustar00rootroot00000000000000pdm-2.23.1/pdm.lock000066400000000000000000004161471477560627500140510ustar00rootroot00000000000000# This file is @generated by PDM. # It is not intended for manual editing. [metadata] groups = ["default", "all", "cookiecutter", "copier", "doc", "keyring", "pytest", "template", "test", "tox", "workflow"] strategy = ["inherit_metadata"] lock_version = "4.5.0" content_hash = "sha256:5b9a6368d6eb1341b227a41c56d341325b743bbd81c1ae6269eba51dc6489474" [[metadata.targets]] requires_python = ">=3.9" [[package]] name = "annotated-types" version = "0.7.0" requires_python = ">=3.8" summary = "Reusable constraint types to use with typing.Annotated" groups = ["all", "copier", "template"] dependencies = [ "typing-extensions>=4.0.0; python_version < \"3.9\"", ] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] [[package]] name = "anyio" version = "4.3.0" requires_python = ">=3.8" summary = "High level compatibility layer for multiple asynchronous event loop implementations" groups = ["default", "test"] dependencies = [ "exceptiongroup>=1.0.2; python_version < \"3.11\"", "idna>=2.8", "sniffio>=1.1", "typing-extensions>=4.1; python_version < \"3.11\"", ] files = [ {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, ] [[package]] name = "arpeggio" version = "2.0.0" summary = "Packrat parser interpreter" groups = ["workflow"] files = [ {file = "Arpeggio-2.0.0-py2.py3-none-any.whl", hash = "sha256:448e332deb0e9ccd04046f1c6c14529d197f41bc2fdb3931e43fc209042fbdd3"}, {file = "Arpeggio-2.0.0.tar.gz", hash = "sha256:d6b03839019bb8a68785f9292ee6a36b1954eb84b925b84a6b8a5e1e26d3ed3d"}, ] [[package]] name = "arrow" version = "1.2.3" requires_python = ">=3.6" summary = "Better dates & times for Python" groups = ["all", "cookiecutter", "template"] dependencies = [ "python-dateutil>=2.7.0", "typing-extensions; python_version < \"3.8\"", ] files = [ {file = "arrow-1.2.3-py3-none-any.whl", hash = "sha256:5a49ab92e3b7b71d96cd6bfcc4df14efefc9dfa96ea19045815914a6ab6b1fe2"}, {file = "arrow-1.2.3.tar.gz", hash = "sha256:3934b30ca1b9f292376d9db15b19446088d12ec58629bc3f0da28fd55fb633a1"}, ] [[package]] name = "attrs" version = "23.1.0" requires_python = ">=3.7" summary = "Classes Without Boilerplate" groups = ["workflow"] dependencies = [ "importlib-metadata; python_version < \"3.8\"", ] files = [ {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, ] [[package]] name = "babel" version = "2.14.0" requires_python = ">=3.7" summary = "Internationalization utilities" groups = ["doc"] dependencies = [ "pytz>=2015.7; python_version < \"3.9\"", ] files = [ {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] [[package]] name = "backports-tarfile" version = "1.2.0" requires_python = ">=3.8" summary = "Backport of CPython tarfile module" groups = ["all", "keyring"] marker = "python_version < \"3.12\"" files = [ {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, ] [[package]] name = "binaryornot" version = "0.4.4" summary = "Ultra-lightweight pure Python package to check if a file is binary or text." groups = ["all", "cookiecutter", "template"] dependencies = [ "chardet>=3.0.2", ] files = [ {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4"}, {file = "binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061"}, ] [[package]] name = "blinker" version = "1.8.2" requires_python = ">=3.8" summary = "Fast, simple object-to-object and broadcast signaling" groups = ["default"] files = [ {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"}, {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, ] [[package]] name = "cachetools" version = "5.5.0" requires_python = ">=3.7" summary = "Extensible memoizing collections and decorators" groups = ["tox"] files = [ {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, ] [[package]] name = "certifi" version = "2024.8.30" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." groups = ["default", "all", "cookiecutter", "doc", "template", "test"] files = [ {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] [[package]] name = "cffi" version = "1.15.1" summary = "Foreign Function Interface for Python calling C code." groups = ["all", "keyring"] marker = "sys_platform == \"linux\"" dependencies = [ "pycparser", ] files = [ {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, ] [[package]] name = "chardet" version = "5.2.0" requires_python = ">=3.7" summary = "Universal encoding detector for Python 3" groups = ["all", "cookiecutter", "template", "tox"] files = [ {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, ] [[package]] name = "charset-normalizer" version = "3.3.2" requires_python = ">=3.7.0" summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." groups = ["default", "all", "cookiecutter", "doc", "template"] files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] [[package]] name = "click" version = "8.1.7" requires_python = ">=3.7" summary = "Composable command line interface toolkit" groups = ["all", "cookiecutter", "doc", "template", "workflow"] dependencies = [ "colorama; platform_system == \"Windows\"", "importlib-metadata; python_version < \"3.8\"", ] files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [[package]] name = "colorama" version = "0.4.6" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Cross-platform colored terminal text." groups = ["all", "cookiecutter", "copier", "doc", "pytest", "template", "test", "tox", "workflow"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "cookiecutter" version = "2.6.0" requires_python = ">=3.7" summary = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template." groups = ["all", "cookiecutter", "template"] dependencies = [ "Jinja2<4.0.0,>=2.7", "arrow", "binaryornot>=0.4.4", "click<9.0.0,>=7.0", "python-slugify>=4.0.0", "pyyaml>=5.3.1", "requests>=2.23.0", "rich", ] files = [ {file = "cookiecutter-2.6.0-py3-none-any.whl", hash = "sha256:a54a8e37995e4ed963b3e82831072d1ad4b005af736bb17b99c2cbd9d41b6e2d"}, {file = "cookiecutter-2.6.0.tar.gz", hash = "sha256:db21f8169ea4f4fdc2408d48ca44859349de2647fbe494a9d6c3edfc0542c21c"}, ] [[package]] name = "copier" version = "9.3.1" requires_python = ">=3.8" summary = "A library for rendering project templates." groups = ["all", "copier", "template"] dependencies = [ "colorama>=0.4.6", "dunamai>=1.7.0", "eval-type-backport<0.3.0,>=0.1.3; python_version < \"3.10\"", "funcy>=1.17", "jinja2-ansible-filters>=1.3.1", "jinja2>=3.1.4", "packaging>=23.0", "pathspec>=0.9.0", "plumbum>=1.6.9", "pydantic>=2.4.2", "pygments>=2.7.1", "pyyaml>=5.3.1", "questionary>=1.8.1", "typing-extensions<5.0.0,>=3.7.4; python_version < \"3.9\"", ] files = [ {file = "copier-9.3.1-py3-none-any.whl", hash = "sha256:20d60383a3fb99054ea4d24a8f71c6f9219b40ac50db1d97d412583d739bce99"}, {file = "copier-9.3.1.tar.gz", hash = "sha256:e8515c082149f771268e8bc7fe400891d96e765797f5dac9fbae191f6427a24c"}, ] [[package]] name = "coverage" version = "7.2.7" requires_python = ">=3.7" summary = "Code coverage measurement for Python" groups = ["test"] files = [ {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, ] [[package]] name = "coverage" version = "7.2.7" extras = ["toml"] requires_python = ">=3.7" summary = "Code coverage measurement for Python" groups = ["test"] dependencies = [ "coverage==7.2.7", "tomli; python_full_version <= \"3.11.0a6\"", ] files = [ {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, ] [[package]] name = "cryptography" version = "41.0.1" requires_python = ">=3.7" summary = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." groups = ["all", "keyring"] marker = "sys_platform == \"linux\"" dependencies = [ "cffi>=1.12", ] files = [ {file = "cryptography-41.0.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:f73bff05db2a3e5974a6fd248af2566134d8981fd7ab012e5dd4ddb1d9a70699"}, {file = "cryptography-41.0.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1a5472d40c8f8e91ff7a3d8ac6dfa363d8e3138b961529c996f3e2df0c7a411a"}, {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fa01527046ca5facdf973eef2535a27fec4cb651e4daec4d043ef63f6ecd4ca"}, {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b46e37db3cc267b4dea1f56da7346c9727e1209aa98487179ee8ebed09d21e43"}, {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d198820aba55660b4d74f7b5fd1f17db3aa5eb3e6893b0a41b75e84e4f9e0e4b"}, {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:948224d76c4b6457349d47c0c98657557f429b4e93057cf5a2f71d603e2fc3a3"}, {file = "cryptography-41.0.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:059e348f9a3c1950937e1b5d7ba1f8e968508ab181e75fc32b879452f08356db"}, {file = "cryptography-41.0.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b4ceb5324b998ce2003bc17d519080b4ec8d5b7b70794cbd2836101406a9be31"}, {file = "cryptography-41.0.1-cp37-abi3-win32.whl", hash = "sha256:8f4ab7021127a9b4323537300a2acfb450124b2def3756f64dc3a3d2160ee4b5"}, {file = "cryptography-41.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:1fee5aacc7367487b4e22484d3c7e547992ed726d14864ee33c0176ae43b0d7c"}, {file = "cryptography-41.0.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f0ff6e18d13a3de56f609dd1fd11470918f770c6bd5d00d632076c727d35485"}, {file = "cryptography-41.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7bfc55a5eae8b86a287747053140ba221afc65eb06207bedf6e019b8934b477c"}, {file = "cryptography-41.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eb8163f5e549a22888c18b0d53d6bb62a20510060a22fd5a995ec8a05268df8a"}, {file = "cryptography-41.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8dde71c4169ec5ccc1087bb7521d54251c016f126f922ab2dfe6649170a3b8c5"}, {file = "cryptography-41.0.1.tar.gz", hash = "sha256:d34579085401d3f49762d2f7d6634d6b6c2ae1242202e860f4d26b046e3a1006"}, ] [[package]] name = "dep-logic" version = "0.4.11" requires_python = ">=3.8" summary = "Python dependency specifications supporting logical operations" groups = ["default"] dependencies = [ "packaging>=22", ] files = [ {file = "dep_logic-0.4.11-py3-none-any.whl", hash = "sha256:44cf69b3e0e369315e7d4cafa4a5050aa70666753831bf0c27b7f3eadbcf70ce"}, {file = "dep_logic-0.4.11.tar.gz", hash = "sha256:6084c81ce683943a60394ca0f8531ff803dfd955b5d7f52fb0571b53b930a182"}, ] [[package]] name = "distlib" version = "0.3.8" summary = "Distribution utilities" groups = ["default", "tox"] files = [ {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] [[package]] name = "dunamai" version = "1.17.0" requires_python = ">=3.5,<4.0" summary = "Dynamic version generation" groups = ["all", "copier", "template"] dependencies = [ "importlib-metadata>=1.6.0; python_version < \"3.8\"", "packaging>=20.9", ] files = [ {file = "dunamai-1.17.0-py3-none-any.whl", hash = "sha256:5aa4ac1085de10691269af021b10497261a5dd644f277e2a21822212604d877b"}, {file = "dunamai-1.17.0.tar.gz", hash = "sha256:459381b585a1e78e4070f0d38a6afb4d67de2ee95064bf6b0438ec620dde0820"}, ] [[package]] name = "eval-type-backport" version = "0.2.0" requires_python = ">=3.8" summary = "Like `typing._eval_type`, but lets older Python versions use newer typing features." groups = ["all", "copier", "template"] marker = "python_version < \"3.10\"" files = [ {file = "eval_type_backport-0.2.0-py3-none-any.whl", hash = "sha256:ac2f73d30d40c5a30a80b8739a789d6bb5e49fdffa66d7912667e2015d9c9933"}, {file = "eval_type_backport-0.2.0.tar.gz", hash = "sha256:68796cfbc7371ebf923f03bdf7bef415f3ec098aeced24e054b253a0e78f7b37"}, ] [[package]] name = "exceptiongroup" version = "1.2.0" requires_python = ">=3.7" summary = "Backport of PEP 654 (exception groups)" groups = ["default", "pytest", "test"] marker = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, ] [[package]] name = "execnet" version = "2.1.1" requires_python = ">=3.8" summary = "execnet: rapid multi-Python deployment" groups = ["test"] files = [ {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, ] [[package]] name = "filelock" version = "3.16.1" requires_python = ">=3.8" summary = "A platform independent file lock." groups = ["default", "tox"] files = [ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [[package]] name = "findpython" version = "0.6.3" requires_python = ">=3.8" summary = "A utility to find python versions on your system" groups = ["default"] dependencies = [ "packaging>=20", ] files = [ {file = "findpython-0.6.3-py3-none-any.whl", hash = "sha256:a85bb589b559cdf1b87227cc233736eb7cad894b9e68021ee498850611939ebc"}, {file = "findpython-0.6.3.tar.gz", hash = "sha256:5863ea55556d8aadc693481a14ac4f3624952719efc1c5591abb0b4a9e965c94"}, ] [[package]] name = "funcy" version = "2.0" summary = "A fancy and practical functional tools" groups = ["all", "copier", "template"] files = [ {file = "funcy-2.0-py2.py3-none-any.whl", hash = "sha256:53df23c8bb1651b12f095df764bfb057935d49537a56de211b098f4c79614bb0"}, {file = "funcy-2.0.tar.gz", hash = "sha256:3963315d59d41c6f30c04bc910e10ab50a3ac4a225868bfa96feed133df075cb"}, ] [[package]] name = "ghp-import" version = "2.1.0" summary = "Copy your docs directly to the gh-pages branch." groups = ["doc"] dependencies = [ "python-dateutil>=2.8.1", ] files = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, ] [[package]] name = "griffe" version = "0.29.0" requires_python = ">=3.7" summary = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." groups = ["doc"] dependencies = [ "cached-property; python_version < \"3.8\"", "colorama>=0.4", ] files = [ {file = "griffe-0.29.0-py3-none-any.whl", hash = "sha256:e62ff34b04630c2382e2e277301cb2c29221fb09c04028e62ef35afccc64344b"}, {file = "griffe-0.29.0.tar.gz", hash = "sha256:6fc892aaa251b3761e3a8d2f5893758e1850ec5d81d4605c4557be0666202a0b"}, ] [[package]] name = "h11" version = "0.14.0" requires_python = ">=3.7" summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" groups = ["default", "test"] dependencies = [ "typing-extensions; python_version < \"3.8\"", ] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] [[package]] name = "hishel" version = "0.0.33" requires_python = ">=3.8" summary = "Persistent cache implementation for httpx and httpcore" groups = ["default"] dependencies = [ "httpx>=0.22.0", "typing-extensions>=4.8.0", ] files = [ {file = "hishel-0.0.33-py3-none-any.whl", hash = "sha256:6e6c6cdaf432ff4c4981e7792ef7d1fa4c8ede58b9dbbcefb9ab3fc9770f2a07"}, {file = "hishel-0.0.33.tar.gz", hash = "sha256:ab5b2661d5e2252f305fd0fb20e8c76bfab3ea73458f20f2591c53c37b270089"}, ] [[package]] name = "httpcore" version = "1.0.6" requires_python = ">=3.8" summary = "A minimal low-level HTTP client." groups = ["default", "test"] dependencies = [ "certifi", "h11<0.15,>=0.13", ] files = [ {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, ] [[package]] name = "httpx" version = "0.27.2" requires_python = ">=3.8" summary = "The next generation HTTP client." groups = ["default", "test"] dependencies = [ "anyio", "certifi", "httpcore==1.*", "idna", "sniffio", ] files = [ {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, ] [[package]] name = "httpx" version = "0.27.2" extras = ["socks"] requires_python = ">=3.8" summary = "The next generation HTTP client." groups = ["default"] dependencies = [ "httpx==0.27.2", "socksio==1.*", ] files = [ {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, ] [[package]] name = "id" version = "1.5.0" requires_python = ">=3.8" summary = "A tool for generating OIDC identities" groups = ["default"] dependencies = [ "requests", ] files = [ {file = "id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658"}, {file = "id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d"}, ] [[package]] name = "idna" version = "3.6" requires_python = ">=3.5" summary = "Internationalized Domain Names in Applications (IDNA)" groups = ["default", "all", "cookiecutter", "doc", "template", "test"] files = [ {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] [[package]] name = "importlib-metadata" version = "8.5.0" requires_python = ">=3.8" summary = "Read metadata from Python packages" groups = ["default", "all", "doc", "keyring", "workflow"] marker = "python_version < \"3.12\"" dependencies = [ "typing-extensions>=3.6.4; python_version < \"3.8\"", "zipp>=3.20", ] files = [ {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, ] [[package]] name = "importlib-resources" version = "6.4.5" requires_python = ">=3.8" summary = "Read resources from Python packages" groups = ["workflow"] marker = "python_version < \"3.10\"" dependencies = [ "zipp>=3.1.0; python_version < \"3.10\"", ] files = [ {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, ] [[package]] name = "iniconfig" version = "2.0.0" requires_python = ">=3.7" summary = "brain-dead simple config-ini parsing" groups = ["pytest", "test"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "installer" version = "0.7.0" requires_python = ">=3.7" summary = "A library for installing Python wheels." groups = ["default"] files = [ {file = "installer-0.7.0-py3-none-any.whl", hash = "sha256:05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53"}, {file = "installer-0.7.0.tar.gz", hash = "sha256:a26d3e3116289bb08216e0d0f7d925fcef0b0194eedfa0c944bcaaa106c4b631"}, ] [[package]] name = "jaraco-classes" version = "3.2.3" requires_python = ">=3.7" summary = "Utility functions for Python class constructs" groups = ["all", "keyring"] dependencies = [ "more-itertools", ] files = [ {file = "jaraco.classes-3.2.3-py3-none-any.whl", hash = "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158"}, {file = "jaraco.classes-3.2.3.tar.gz", hash = "sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a"}, ] [[package]] name = "jaraco-context" version = "5.3.0" requires_python = ">=3.8" summary = "Useful decorators and context managers" groups = ["all", "keyring"] dependencies = [ "backports-tarfile; python_version < \"3.12\"", ] files = [ {file = "jaraco.context-5.3.0-py3-none-any.whl", hash = "sha256:3e16388f7da43d384a1a7cd3452e72e14732ac9fe459678773a3608a812bf266"}, {file = "jaraco.context-5.3.0.tar.gz", hash = "sha256:c2f67165ce1f9be20f32f650f25d8edfc1646a8aeee48ae06fb35f90763576d2"}, ] [[package]] name = "jaraco-functools" version = "4.0.2" requires_python = ">=3.8" summary = "Functools like those found in stdlib" groups = ["all", "keyring"] dependencies = [ "more-itertools", ] files = [ {file = "jaraco.functools-4.0.2-py3-none-any.whl", hash = "sha256:c9d16a3ed4ccb5a889ad8e0b7a343401ee5b2a71cee6ed192d3f68bc351e94e3"}, {file = "jaraco_functools-4.0.2.tar.gz", hash = "sha256:3460c74cd0d32bf82b9576bbb3527c4364d5b27a21f5158a62aed6c4b42e23f5"}, ] [[package]] name = "jeepney" version = "0.8.0" requires_python = ">=3.7" summary = "Low-level, pure Python DBus protocol wrapper." groups = ["all", "keyring"] marker = "sys_platform == \"linux\"" files = [ {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, ] [[package]] name = "jinja2" version = "3.1.4" requires_python = ">=3.7" summary = "A very fast and expressive template engine." groups = ["all", "cookiecutter", "copier", "doc", "template", "workflow"] dependencies = [ "MarkupSafe>=2.0", ] files = [ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [[package]] name = "jinja2-ansible-filters" version = "1.3.2" summary = "A port of Ansible's jinja2 filters without requiring ansible core." groups = ["all", "copier", "template"] dependencies = [ "Jinja2", "PyYAML", ] files = [ {file = "jinja2-ansible-filters-1.3.2.tar.gz", hash = "sha256:07c10cf44d7073f4f01102ca12d9a2dc31b41d47e4c61ed92ef6a6d2669b356b"}, {file = "jinja2_ansible_filters-1.3.2-py3-none-any.whl", hash = "sha256:e1082f5564917649c76fed239117820610516ec10f87735d0338688800a55b34"}, ] [[package]] name = "keyring" version = "25.4.1" requires_python = ">=3.8" summary = "Store and access your passwords safely." groups = ["all", "keyring"] dependencies = [ "SecretStorage>=3.2; sys_platform == \"linux\"", "importlib-metadata>=4.11.4; python_version < \"3.12\"", "importlib-resources; python_version < \"3.9\"", "jaraco-classes", "jaraco-context", "jaraco-functools", "jeepney>=0.4.2; sys_platform == \"linux\"", "pywin32-ctypes>=0.2.0; sys_platform == \"win32\"", ] files = [ {file = "keyring-25.4.1-py3-none-any.whl", hash = "sha256:5426f817cf7f6f007ba5ec722b1bcad95a75b27d780343772ad76b17cb47b0bf"}, {file = "keyring-25.4.1.tar.gz", hash = "sha256:b07ebc55f3e8ed86ac81dd31ef14e81ace9dd9c3d4b5d77a6e9a2016d0d71a1b"}, ] [[package]] name = "markdown" version = "3.6" requires_python = ">=3.8" summary = "Python implementation of John Gruber's Markdown." groups = ["doc"] dependencies = [ "importlib-metadata>=4.4; python_version < \"3.10\"", ] files = [ {file = "Markdown-3.6-py3-none-any.whl", hash = "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f"}, {file = "Markdown-3.6.tar.gz", hash = "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224"}, ] [[package]] name = "markdown-exec" version = "1.9.3" requires_python = ">=3.8" summary = "Utilities to execute code blocks in Markdown files." groups = ["doc"] dependencies = [ "pymdown-extensions>=9", ] files = [ {file = "markdown_exec-1.9.3-py3-none-any.whl", hash = "sha256:6b3f5f35d9c74e3c7ba9011376ad1732aa3ed3806cadefb8522dee3afe02b1ec"}, {file = "markdown_exec-1.9.3.tar.gz", hash = "sha256:6ad206b75306020779144835f44de115235903755c46a7ccd3b754aad74661e1"}, ] [[package]] name = "markdown-it-py" version = "3.0.0" requires_python = ">=3.8" summary = "Python port of markdown-it. Markdown parsing, done right!" groups = ["default", "all", "cookiecutter", "template"] dependencies = [ "mdurl~=0.1", ] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, ] [[package]] name = "markupsafe" version = "2.1.5" requires_python = ">=3.7" summary = "Safely add untrusted strings to HTML/XML markup." groups = ["all", "cookiecutter", "copier", "doc", "template", "test", "workflow"] files = [ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] [[package]] name = "mdurl" version = "0.1.2" requires_python = ">=3.7" summary = "Markdown URL utilities" groups = ["default", "all", "cookiecutter", "template"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] [[package]] name = "mergedeep" version = "1.3.4" requires_python = ">=3.6" summary = "A deep merge function for 🐍." groups = ["doc"] files = [ {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, ] [[package]] name = "mkdocs" version = "1.6.1" requires_python = ">=3.8" summary = "Project documentation with Markdown." groups = ["doc"] dependencies = [ "click>=7.0", "colorama>=0.4; platform_system == \"Windows\"", "ghp-import>=1.0", "importlib-metadata>=4.4; python_version < \"3.10\"", "jinja2>=2.11.1", "markdown>=3.3.6", "markupsafe>=2.0.1", "mergedeep>=1.3.4", "mkdocs-get-deps>=0.2.0", "packaging>=20.5", "pathspec>=0.11.1", "pyyaml-env-tag>=0.1", "pyyaml>=5.1", "watchdog>=2.0", ] files = [ {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, ] [[package]] name = "mkdocs-autorefs" version = "1.2.0" requires_python = ">=3.8" summary = "Automatically link across pages in MkDocs." groups = ["doc"] dependencies = [ "Markdown>=3.3", "markupsafe>=2.0.1", "mkdocs>=1.1", ] files = [ {file = "mkdocs_autorefs-1.2.0-py3-none-any.whl", hash = "sha256:d588754ae89bd0ced0c70c06f58566a4ee43471eeeee5202427da7de9ef85a2f"}, {file = "mkdocs_autorefs-1.2.0.tar.gz", hash = "sha256:a86b93abff653521bda71cf3fc5596342b7a23982093915cb74273f67522190f"}, ] [[package]] name = "mkdocs-get-deps" version = "0.2.0" requires_python = ">=3.8" summary = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" groups = ["doc"] dependencies = [ "importlib-metadata>=4.3; python_version < \"3.10\"", "mergedeep>=1.3.4", "platformdirs>=2.2.0", "pyyaml>=5.1", ] files = [ {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, ] [[package]] name = "mkdocs-material" version = "9.5.41" requires_python = ">=3.8" summary = "Documentation that simply works" groups = ["doc"] dependencies = [ "babel~=2.10", "colorama~=0.4", "jinja2~=3.0", "markdown~=3.2", "mkdocs-material-extensions~=1.3", "mkdocs~=1.6", "paginate~=0.5", "pygments~=2.16", "pymdown-extensions~=10.2", "regex>=2022.4", "requests~=2.26", ] files = [ {file = "mkdocs_material-9.5.41-py3-none-any.whl", hash = "sha256:990bc138c33342b5b73e7545915ebc0136e501bfbd8e365735144f5120891d83"}, {file = "mkdocs_material-9.5.41.tar.gz", hash = "sha256:30fa5d459b4b8130848ecd8e1c908878345d9d8268f7ddbc31eebe88d462d97b"}, ] [[package]] name = "mkdocs-material-extensions" version = "1.3.1" requires_python = ">=3.8" summary = "Extension pack for Python Markdown and MkDocs Material." groups = ["doc"] files = [ {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, ] [[package]] name = "mkdocs-redirects" version = "1.2.1" requires_python = ">=3.6" summary = "A MkDocs plugin for dynamic page redirects to prevent broken links." groups = ["doc"] dependencies = [ "mkdocs>=1.1.1", ] files = [ {file = "mkdocs-redirects-1.2.1.tar.gz", hash = "sha256:9420066d70e2a6bb357adf86e67023dcdca1857f97f07c7fe450f8f1fb42f861"}, {file = "mkdocs_redirects-1.2.1-py3-none-any.whl", hash = "sha256:497089f9e0219e7389304cffefccdfa1cac5ff9509f2cb706f4c9b221726dffb"}, ] [[package]] name = "mkdocs-version-annotations" version = "1.0.0" requires_python = ">=3.7,<4.0" summary = "MkDocs plugin to add custom admonitions for documenting version differences" groups = ["doc"] files = [ {file = "mkdocs-version-annotations-1.0.0.tar.gz", hash = "sha256:6786024b37d27b330fda240b76ebec8e7ce48bd5a9d7a66e99804559d088dffa"}, {file = "mkdocs_version_annotations-1.0.0-py3-none-any.whl", hash = "sha256:385004eb4a7530dd87a227e08cd907ce7a8fe21fdf297720a4149c511bcf05f5"}, ] [[package]] name = "mkdocstrings" version = "0.26.1" requires_python = ">=3.8" summary = "Automatic documentation from sources, for MkDocs." groups = ["doc"] dependencies = [ "Jinja2>=2.11.1", "Markdown>=3.6", "MarkupSafe>=1.1", "click>=7.0", "importlib-metadata>=4.6; python_version < \"3.10\"", "mkdocs-autorefs>=1.2", "mkdocs>=1.4", "platformdirs>=2.2", "pymdown-extensions>=6.3", "typing-extensions>=4.1; python_version < \"3.10\"", ] files = [ {file = "mkdocstrings-0.26.1-py3-none-any.whl", hash = "sha256:29738bfb72b4608e8e55cc50fb8a54f325dc7ebd2014e4e3881a49892d5983cf"}, {file = "mkdocstrings-0.26.1.tar.gz", hash = "sha256:bb8b8854d6713d5348ad05b069a09f3b79edbc6a0f33a34c6821141adb03fe33"}, ] [[package]] name = "mkdocstrings-python" version = "1.1.0" requires_python = ">=3.7" summary = "A Python handler for mkdocstrings." groups = ["doc"] dependencies = [ "griffe>=0.24", "mkdocstrings>=0.20", ] files = [ {file = "mkdocstrings_python-1.1.0-py3-none-any.whl", hash = "sha256:4e9a9d728e6ba742ecbd7379f5091f3a32b16d723881963bb457b2149656e167"}, {file = "mkdocstrings_python-1.1.0.tar.gz", hash = "sha256:00cca5e47bf63eb2aece08b9887421b6828bdb939a13a481e4e8b495569e8101"}, ] [[package]] name = "mkdocstrings" version = "0.26.1" extras = ["python"] requires_python = ">=3.8" summary = "Automatic documentation from sources, for MkDocs." groups = ["doc"] dependencies = [ "mkdocstrings-python>=0.5.2", "mkdocstrings==0.26.1", ] files = [ {file = "mkdocstrings-0.26.1-py3-none-any.whl", hash = "sha256:29738bfb72b4608e8e55cc50fb8a54f325dc7ebd2014e4e3881a49892d5983cf"}, {file = "mkdocstrings-0.26.1.tar.gz", hash = "sha256:bb8b8854d6713d5348ad05b069a09f3b79edbc6a0f33a34c6821141adb03fe33"}, ] [[package]] name = "more-itertools" version = "9.1.0" requires_python = ">=3.7" summary = "More routines for operating on iterables, beyond itertools" groups = ["all", "keyring"] files = [ {file = "more-itertools-9.1.0.tar.gz", hash = "sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d"}, {file = "more_itertools-9.1.0-py3-none-any.whl", hash = "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3"}, ] [[package]] name = "msgpack" version = "1.1.0" requires_python = ">=3.8" summary = "MessagePack serializer" groups = ["default"] files = [ {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"}, {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"}, {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"}, {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"}, {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"}, {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"}, {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"}, {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"}, {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"}, {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"}, {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"}, {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"}, {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"}, {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"}, {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"}, {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"}, {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"}, {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"}, {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"}, {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"}, {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"}, {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"}, {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"}, {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"}, {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"}, {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"}, {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"}, {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"}, {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"}, {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"}, {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"}, {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"}, {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"}, {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"}, {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"}, {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"}, {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"}, {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"}, {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"}, {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"}, {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"}, {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"}, {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"}, {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"}, {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"}, {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"}, {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"}, {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"}, {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"}, {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"}, {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, ] [[package]] name = "packaging" version = "24.1" requires_python = ">=3.8" summary = "Core utilities for Python packages" groups = ["default", "all", "copier", "doc", "pytest", "template", "test", "tox"] files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] name = "paginate" version = "0.5.6" summary = "Divides large result sets into pages for easier browsing" groups = ["doc"] files = [ {file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"}, ] [[package]] name = "parver" version = "0.5" requires_python = ">=3.8" summary = "Parse and manipulate version numbers." groups = ["workflow"] dependencies = [ "arpeggio>=1.7", "attrs>=19.2", "typing-extensions; python_version < \"3.10\"", ] files = [ {file = "parver-0.5-py3-none-any.whl", hash = "sha256:2281b187276c8e8e3c15634f62287b2fb6fe0efe3010f739a6bd1e45fa2bf2b2"}, {file = "parver-0.5.tar.gz", hash = "sha256:b9fde1e6bb9ce9f07e08e9c4bea8d8825c5e78e18a0052d02e02bf9517eb4777"}, ] [[package]] name = "pathspec" version = "0.12.1" requires_python = ">=3.8" summary = "Utility library for gitignore style pattern matching of file paths." groups = ["all", "copier", "doc", "template"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] name = "pbs-installer" version = "2025.2.12" requires_python = ">=3.8" summary = "Installer for Python Build Standalone" groups = ["default"] files = [ {file = "pbs_installer-2025.2.12-py3-none-any.whl", hash = "sha256:3d9034047945b2d5f169cd9bb324f1f28c37d0ec120d6110ddb10aa07016fb79"}, {file = "pbs_installer-2025.2.12.tar.gz", hash = "sha256:c6815165babf312c90d27ccd16afe598de641d616860f88e1855f183b0253b39"}, ] [[package]] name = "platformdirs" version = "4.3.6" requires_python = ">=3.8" summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." groups = ["default", "doc", "tox"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [[package]] name = "pluggy" version = "1.5.0" requires_python = ">=3.8" summary = "plugin and hook calling mechanisms for python" groups = ["pytest", "test", "tox"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [[package]] name = "plumbum" version = "1.8.2" requires_python = ">=3.6" summary = "Plumbum: shell combinators library" groups = ["all", "copier", "template"] dependencies = [ "pywin32; platform_system == \"Windows\" and platform_python_implementation != \"PyPy\"", ] files = [ {file = "plumbum-1.8.2-py3-none-any.whl", hash = "sha256:3ad9e5f56c6ec98f6f7988f7ea8b52159662ea9e915868d369dbccbfca0e367e"}, {file = "plumbum-1.8.2.tar.gz", hash = "sha256:9e6dc032f4af952665f32f3206567bc23b7858b1413611afe603a3f8ad9bfd75"}, ] [[package]] name = "prompt-toolkit" version = "3.0.38" requires_python = ">=3.7.0" summary = "Library for building powerful interactive command lines in Python" groups = ["all", "copier", "template"] dependencies = [ "wcwidth", ] files = [ {file = "prompt_toolkit-3.0.38-py3-none-any.whl", hash = "sha256:45ea77a2f7c60418850331366c81cf6b5b9cf4c7fd34616f733c5427e6abbb1f"}, {file = "prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"}, ] [[package]] name = "pycomplete" version = "0.4.0" requires_python = ">=3.7" summary = "A Python library to generate static completion scripts for your CLI app" groups = ["workflow"] files = [ {file = "pycomplete-0.4.0-py3-none-any.whl", hash = "sha256:aedeccb742a9cc8f86cc22563b92dc7edd8a3ee200e2eefb5c7a66a90bbf5f34"}, {file = "pycomplete-0.4.0.tar.gz", hash = "sha256:8ae83fb5883c78da157d208aefdad4a989e0695e289ef738d05c9d14cb9a13b3"}, ] [[package]] name = "pycparser" version = "2.21" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" summary = "C parser in Python" groups = ["all", "keyring"] marker = "sys_platform == \"linux\"" files = [ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] [[package]] name = "pydantic" version = "2.9.2" requires_python = ">=3.8" summary = "Data validation using Python type hints" groups = ["all", "copier", "template"] dependencies = [ "annotated-types>=0.6.0", "pydantic-core==2.23.4", "typing-extensions>=4.12.2; python_version >= \"3.13\"", "typing-extensions>=4.6.1; python_version < \"3.13\"", ] files = [ {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, ] [[package]] name = "pydantic-core" version = "2.23.4" requires_python = ">=3.8" summary = "Core functionality for Pydantic validation and serialization" groups = ["all", "copier", "template"] dependencies = [ "typing-extensions!=4.7.0,>=4.6.0", ] files = [ {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, ] [[package]] name = "pygments" version = "2.17.2" requires_python = ">=3.7" summary = "Pygments is a syntax highlighting package written in Python." groups = ["default", "all", "cookiecutter", "copier", "doc", "template"] files = [ {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] [[package]] name = "pymdown-extensions" version = "10.7.1" requires_python = ">=3.8" summary = "Extension pack for Python Markdown." groups = ["doc"] dependencies = [ "markdown>=3.5", "pyyaml", ] files = [ {file = "pymdown_extensions-10.7.1-py3-none-any.whl", hash = "sha256:f5cc7000d7ff0d1ce9395d216017fa4df3dde800afb1fb72d1c7d3fd35e710f4"}, {file = "pymdown_extensions-10.7.1.tar.gz", hash = "sha256:c70e146bdd83c744ffc766b4671999796aba18842b268510a329f7f64700d584"}, ] [[package]] name = "pyproject-api" version = "1.8.0" requires_python = ">=3.8" summary = "API to interact with the python pyproject.toml based projects" groups = ["tox"] dependencies = [ "packaging>=24.1", "tomli>=2.0.1; python_version < \"3.11\"", ] files = [ {file = "pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228"}, {file = "pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496"}, ] [[package]] name = "pyproject-hooks" version = "1.2.0" requires_python = ">=3.7" summary = "Wrappers to call pyproject.toml-based build backend hooks." groups = ["default"] files = [ {file = "pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913"}, {file = "pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"}, ] [[package]] name = "pytest" version = "8.3.3" requires_python = ">=3.8" summary = "pytest: simple powerful testing with Python" groups = ["pytest", "test"] dependencies = [ "colorama; sys_platform == \"win32\"", "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", "iniconfig", "packaging", "pluggy<2,>=1.5", "tomli>=1; python_version < \"3.11\"", ] files = [ {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, ] [[package]] name = "pytest-cov" version = "5.0.0" requires_python = ">=3.8" summary = "Pytest plugin for measuring coverage." groups = ["test"] dependencies = [ "coverage[toml]>=5.2.1", "pytest>=4.6", ] files = [ {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, ] [[package]] name = "pytest-httpserver" version = "1.1.0" requires_python = ">=3.8" summary = "pytest-httpserver is a httpserver for pytest" groups = ["test"] dependencies = [ "Werkzeug>=2.0.0", ] files = [ {file = "pytest_httpserver-1.1.0-py3-none-any.whl", hash = "sha256:7ef88be8ed3354b6784daa3daa75a422370327c634053cefb124903fa8d73a41"}, {file = "pytest_httpserver-1.1.0.tar.gz", hash = "sha256:6b1cb0199e2ed551b1b94d43f096863bbf6ae5bcd7c75c2c06845e5ce2dc8701"}, ] [[package]] name = "pytest-httpx" version = "0.34.0" requires_python = ">=3.9" summary = "Send responses to httpx." groups = ["test"] dependencies = [ "httpx==0.27.*", "pytest==8.*", ] files = [ {file = "pytest_httpx-0.34.0-py3-none-any.whl", hash = "sha256:42cf0a66f7b71b9111db2897e8b38a903abd33a27b11c48aff4a3c7650313af2"}, {file = "pytest_httpx-0.34.0.tar.gz", hash = "sha256:3ca4b0975c0f93b985f17df19e76430c1086b5b0cce32b1af082d8901296a735"}, ] [[package]] name = "pytest-mock" version = "3.14.0" requires_python = ">=3.8" summary = "Thin-wrapper around the mock package for easier use with pytest" groups = ["pytest", "test"] dependencies = [ "pytest>=6.2.5", ] files = [ {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, ] [[package]] name = "pytest-rerunfailures" version = "14.0" requires_python = ">=3.8" summary = "pytest plugin to re-run tests to eliminate flaky failures" groups = ["test"] dependencies = [ "packaging>=17.1", "pytest>=7.2", ] files = [ {file = "pytest-rerunfailures-14.0.tar.gz", hash = "sha256:4a400bcbcd3c7a4ad151ab8afac123d90eca3abe27f98725dc4d9702887d2e92"}, {file = "pytest_rerunfailures-14.0-py3-none-any.whl", hash = "sha256:4197bdd2eaeffdbf50b5ea6e7236f47ff0e44d1def8dae08e409f536d84e7b32"}, ] [[package]] name = "pytest-xdist" version = "3.6.1" requires_python = ">=3.8" summary = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" groups = ["test"] dependencies = [ "execnet>=2.1", "pytest>=7.0.0", ] files = [ {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, ] [[package]] name = "python-dateutil" version = "2.9.0.post0" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" summary = "Extensions to the standard Python datetime module" groups = ["all", "cookiecutter", "doc", "template"] dependencies = [ "six>=1.5", ] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] [[package]] name = "python-dotenv" version = "1.0.1" requires_python = ">=3.8" summary = "Read key-value pairs from a .env file and set them as environment variables" groups = ["default"] files = [ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, ] [[package]] name = "python-slugify" version = "8.0.1" requires_python = ">=3.7" summary = "A Python slugify application that also handles Unicode" groups = ["all", "cookiecutter", "template"] dependencies = [ "text-unidecode>=1.3", ] files = [ {file = "python-slugify-8.0.1.tar.gz", hash = "sha256:ce0d46ddb668b3be82f4ed5e503dbc33dd815d83e2eb6824211310d3fb172a27"}, {file = "python_slugify-8.0.1-py2.py3-none-any.whl", hash = "sha256:70ca6ea68fe63ecc8fa4fcf00ae651fc8a5d02d93dcd12ae6d4fc7ca46c4d395"}, ] [[package]] name = "pywin32" version = "306" summary = "Python for Window Extensions" groups = ["all", "copier", "template"] marker = "platform_system == \"Windows\" and platform_python_implementation != \"PyPy\"" files = [ {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, ] [[package]] name = "pywin32-ctypes" version = "0.2.1" requires_python = ">=3.6" summary = "A (partial) reimplementation of pywin32 using ctypes/cffi" groups = ["all", "keyring"] marker = "sys_platform == \"win32\"" files = [ {file = "pywin32-ctypes-0.2.1.tar.gz", hash = "sha256:934a2def1e5cbc472b2b6bf80680c0f03cd87df65dfd58bfd1846969de095b03"}, {file = "pywin32_ctypes-0.2.1-py3-none-any.whl", hash = "sha256:b9a53ef754c894a525469933ab2a447c74ec1ea6b9d2ef446f40ec50d3dcec9f"}, ] [[package]] name = "pyyaml" version = "6.0.1" requires_python = ">=3.6" summary = "YAML parser and emitter for Python" groups = ["all", "cookiecutter", "copier", "doc", "template"] files = [ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] [[package]] name = "pyyaml-env-tag" version = "0.1" requires_python = ">=3.6" summary = "A custom YAML tag for referencing environment variables in YAML files. " groups = ["doc"] dependencies = [ "pyyaml", ] files = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, ] [[package]] name = "questionary" version = "1.10.0" requires_python = ">=3.6,<4.0" summary = "Python library to build pretty command line user prompts ⭐️" groups = ["all", "copier", "template"] dependencies = [ "prompt-toolkit<4.0,>=2.0", ] files = [ {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, ] [[package]] name = "regex" version = "2023.12.25" requires_python = ">=3.7" summary = "Alternative regular expression module, to replace re." groups = ["doc"] files = [ {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, ] [[package]] name = "requests" version = "2.31.0" requires_python = ">=3.7" summary = "Python HTTP for Humans." groups = ["default", "all", "cookiecutter", "doc", "template"] dependencies = [ "certifi>=2017.4.17", "charset-normalizer<4,>=2", "idna<4,>=2.5", "urllib3<3,>=1.21.1", ] files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [[package]] name = "resolvelib" version = "1.1.0" requires_python = ">=3.7" summary = "Resolve abstract dependencies into concrete ones" groups = ["default"] files = [ {file = "resolvelib-1.1.0-py2.py3-none-any.whl", hash = "sha256:f80de38ae744bcf4e918e27a681a5c6cb63a08d9a926c0989c0730bcdd089049"}, {file = "resolvelib-1.1.0.tar.gz", hash = "sha256:b68591ef748f58c1e2a2ac28d0961b3586ae8b25f60b0ba9a5e4f3d87c1d6a79"}, ] [[package]] name = "rich" version = "13.9.2" requires_python = ">=3.8.0" summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" groups = ["default", "all", "cookiecutter", "template"] dependencies = [ "markdown-it-py>=2.2.0", "pygments<3.0.0,>=2.13.0", "typing-extensions<5.0,>=4.0.0; python_version < \"3.11\"", ] files = [ {file = "rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1"}, {file = "rich-13.9.2.tar.gz", hash = "sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c"}, ] [[package]] name = "secretstorage" version = "3.3.3" requires_python = ">=3.6" summary = "Python bindings to FreeDesktop.org Secret Service API" groups = ["all", "keyring"] marker = "sys_platform == \"linux\"" dependencies = [ "cryptography>=2.0", "jeepney>=0.6", ] files = [ {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, ] [[package]] name = "setuptools" version = "75.2.0" requires_python = ">=3.8" summary = "Easily download, build, install, upgrade, and uninstall Python packages" groups = ["doc"] files = [ {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"}, {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"}, ] [[package]] name = "shellingham" version = "1.5.4" requires_python = ">=3.7" summary = "Tool to Detect Surrounding Shell" groups = ["default"] files = [ {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, ] [[package]] name = "six" version = "1.16.0" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" summary = "Python 2 and 3 compatibility utilities" groups = ["all", "cookiecutter", "doc", "template"] files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] [[package]] name = "sniffio" version = "1.3.1" requires_python = ">=3.7" summary = "Sniff out which async library your code is running under" groups = ["default", "test"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] [[package]] name = "socksio" version = "1.0.0" requires_python = ">=3.6" summary = "Sans-I/O implementation of SOCKS4, SOCKS4A, and SOCKS5." groups = ["default"] files = [ {file = "socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3"}, {file = "socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac"}, ] [[package]] name = "text-unidecode" version = "1.3" summary = "The most basic Text::Unidecode port" groups = ["all", "cookiecutter", "template"] files = [ {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, ] [[package]] name = "tomli" version = "2.0.2" requires_python = ">=3.8" summary = "A lil' TOML parser" groups = ["default", "pytest", "test", "tox", "workflow"] marker = "python_version < \"3.11\"" files = [ {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, ] [[package]] name = "tomlkit" version = "0.13.2" requires_python = ">=3.8" summary = "Style preserving TOML library" groups = ["default"] files = [ {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, ] [[package]] name = "towncrier" version = "24.8.0" requires_python = ">=3.8" summary = "Building newsfiles for your project." groups = ["workflow"] dependencies = [ "click", "importlib-metadata>=4.6; python_version < \"3.10\"", "importlib-resources>=5; python_version < \"3.10\"", "jinja2", "tomli; python_version < \"3.11\"", ] files = [ {file = "towncrier-24.8.0-py3-none-any.whl", hash = "sha256:9343209592b839209cdf28c339ba45792fbfe9775b5f9c177462fd693e127d8d"}, {file = "towncrier-24.8.0.tar.gz", hash = "sha256:013423ee7eed102b2f393c287d22d95f66f1a3ea10a4baa82d298001a7f18af3"}, ] [[package]] name = "tox" version = "4.23.0" requires_python = ">=3.8" summary = "tox is a generic virtualenv management and test command line tool" groups = ["tox"] dependencies = [ "cachetools>=5.5", "chardet>=5.2", "colorama>=0.4.6", "filelock>=3.16.1", "packaging>=24.1", "platformdirs>=4.3.6", "pluggy>=1.5", "pyproject-api>=1.8", "tomli>=2.0.1; python_version < \"3.11\"", "typing-extensions>=4.12.2; python_version < \"3.11\"", "virtualenv>=20.26.6", ] files = [ {file = "tox-4.23.0-py3-none-any.whl", hash = "sha256:46da40afb660e46238c251280eb910bdaf00b390c7557c8e4bb611f422e9db12"}, {file = "tox-4.23.0.tar.gz", hash = "sha256:a6bd7d54231d755348d3c3a7b450b5bf6563833716d1299a1619587a1b77a3bf"}, ] [[package]] name = "tox-pdm" version = "0.7.2" requires_python = ">=3.7" summary = "A plugin for tox that utilizes PDM as the package manager and installer" groups = ["tox"] dependencies = [ "tomli; python_version < \"3.11\"", "tox>=4.0", ] files = [ {file = "tox_pdm-0.7.2-py3-none-any.whl", hash = "sha256:12f6215416b7acd00a80a9e7128f3dc3e3c89308d60707f5d0a24abdf83ac104"}, {file = "tox_pdm-0.7.2.tar.gz", hash = "sha256:a841a7e1e942a71805624703b9a6d286663bd6af79bba6130ba756975c315308"}, ] [[package]] name = "truststore" version = "0.9.2" requires_python = ">=3.10" summary = "Verify certificates using native system trust stores" groups = ["default"] marker = "python_version >= \"3.10\"" files = [ {file = "truststore-0.9.2-py3-none-any.whl", hash = "sha256:04559916f8810cc1a5ecc41f215eddc988746067b754fc0995da7a2ceaf54735"}, {file = "truststore-0.9.2.tar.gz", hash = "sha256:a1dee0d0575ff22d2875476343783a5d64575419974e228f3248772613c3d993"}, ] [[package]] name = "typing-extensions" version = "4.12.2" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" groups = ["default", "all", "cookiecutter", "copier", "doc", "template", "test", "tox", "workflow"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "unearth" version = "0.17.5" requires_python = ">=3.8" summary = "A utility to fetch and download python packages" groups = ["default"] dependencies = [ "httpx<1,>=0.27.0", "packaging>=20", ] files = [ {file = "unearth-0.17.5-py3-none-any.whl", hash = "sha256:9963e66b14f0484644c9b45b517e530befb2de6a8da4b06a9a38bed2d086dfe6"}, {file = "unearth-0.17.5.tar.gz", hash = "sha256:a19e1c02e64b40518d088079c7416fc41b45a648b81a4128aac02597234ee6ba"}, ] [[package]] name = "urllib3" version = "2.2.1" requires_python = ">=3.8" summary = "HTTP library with thread-safe connection pooling, file post, and more." groups = ["default", "all", "cookiecutter", "doc", "template"] files = [ {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [[package]] name = "virtualenv" version = "20.27.0" requires_python = ">=3.8" summary = "Virtual Python Environment builder" groups = ["default", "tox"] dependencies = [ "distlib<1,>=0.3.7", "filelock<4,>=3.12.2", "importlib-metadata>=6.6; python_version < \"3.8\"", "platformdirs<5,>=3.9.1", ] files = [ {file = "virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655"}, {file = "virtualenv-20.27.0.tar.gz", hash = "sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2"}, ] [[package]] name = "watchdog" version = "4.0.0" requires_python = ">=3.8" summary = "Filesystem events monitoring" groups = ["doc"] files = [ {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"}, {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"}, {file = "watchdog-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c"}, {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b"}, {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935"}, {file = "watchdog-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b"}, {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257"}, {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19"}, {file = "watchdog-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b"}, {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101"}, {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca"}, {file = "watchdog-4.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8"}, {file = "watchdog-4.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b"}, {file = "watchdog-4.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269"}, {file = "watchdog-4.0.0-py3-none-win32.whl", hash = "sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c"}, {file = "watchdog-4.0.0-py3-none-win_amd64.whl", hash = "sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245"}, {file = "watchdog-4.0.0-py3-none-win_ia64.whl", hash = "sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7"}, {file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"}, ] [[package]] name = "wcwidth" version = "0.2.6" summary = "Measures the displayed width of unicode strings in a terminal" groups = ["all", "copier", "template"] dependencies = [ "backports-functools-lru-cache>=1.2.1; python_version < \"3.2\"", ] files = [ {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, ] [[package]] name = "werkzeug" version = "2.2.3" requires_python = ">=3.7" summary = "The comprehensive WSGI web application library." groups = ["test"] dependencies = [ "MarkupSafe>=2.1.1", ] files = [ {file = "Werkzeug-2.2.3-py3-none-any.whl", hash = "sha256:56433961bc1f12533306c624f3be5e744389ac61d722175d543e1751285da612"}, {file = "Werkzeug-2.2.3.tar.gz", hash = "sha256:2e1ccc9417d4da358b9de6f174e3ac094391ea1d4fbef2d667865d819dfd0afe"}, ] [[package]] name = "zipp" version = "3.20.2" requires_python = ">=3.8" summary = "Backport of pathlib-compatible object wrapper for zip files" groups = ["default", "all", "doc", "keyring", "workflow"] marker = "python_version < \"3.12\"" files = [ {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, ] pdm-2.23.1/pyproject.toml000066400000000000000000000144771477560627500153330ustar00rootroot00000000000000[build-system] requires = ["pdm-backend", "pdm-build-locked"] build-backend = "pdm.backend" [project] # PEP 621 project metadata # See https://www.python.org/dev/peps/pep-0621/ name = "pdm" description = "A modern Python package and dependency manager supporting the latest PEP standards" authors = [ {name = "Frost Ming", email = "mianghong@gmail.com"}, ] dynamic = ["version"] requires-python = ">=3.9" license = {text = "MIT"} dependencies = [ "blinker", "packaging>=20.9,!=22.0", "platformdirs", "rich>=12.3.0", "virtualenv>=20", "pyproject-hooks", "unearth>=0.17.5", "dep-logic>=0.4.4", "findpython>=0.6.0,<1.0.0a0", "tomlkit>=0.11.1,<1", "shellingham>=1.3.2", "python-dotenv>=0.15", "resolvelib>=1.1", "installer<0.8,>=0.7", "truststore>=0.9; python_version >= \"3.10\"", "tomli>=1.1.0; python_version < \"3.11\"", "importlib-metadata>=3.6; python_version < \"3.10\"", "hishel>=0.0.32", "msgpack>=1.0", "pbs-installer>=2024.4.18", "httpx[socks]<1,>0.20", "filelock>=3.13", "httpcore>=1.0.6", "certifi>=2024.8.30", "id>=1.5.0" ] readme = "README.md" keywords = ["packaging", "dependency", "workflow"] classifiers = [ "Topic :: Software Development :: Build Tools", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", ] [project.urls] Homepage = "https://pdm-project.org" Repository = "https://github.com/pdm-project/pdm" Documentation = "https://pdm-project.org" Changelog = "https://pdm-project.org/latest/dev/changelog/" [project.optional-dependencies] pytest = [ "pytest", "pytest-mock", ] copier = ["copier>=8.0.0"] cookiecutter = ["cookiecutter"] keyring = ["keyring"] template = [ "pdm[copier,cookiecutter]", ] all = [ "pdm[keyring,template]", ] [project.scripts] pdm = "pdm.core:main" [dependency-groups] test = [ "pdm[pytest]", "pytest-cov", "pytest-xdist>=1.31.0", "pytest-rerunfailures>=10.2", "pytest-httpserver>=1.0.6", "pytest-httpx>=0.34.0", ] tox = [ "tox", "tox-pdm>=0.5", ] doc = [ "mkdocs>=1.1", "mkdocs-material>=7.3", "mkdocstrings[python]>=0.18", "setuptools>=62.3.3", "markdown-exec>=0.7.0", "mkdocs-redirects>=1.2.0", "mkdocs-version-annotations>=1.0.0", ] workflow = [ "parver>=0.3.1", "towncrier>=20", "pycomplete~=0.3", ] [tool.ruff] line-length = 120 exclude = ["tests/fixtures"] target-version = "py38" src = ["src"] [tool.ruff.lint] extend-select = [ "I", # isort "B", # flake8-bugbear "C4", # flake8-comprehensions "FA", # flake8-future-annotations "PGH", # pygrep-hooks "RUF", # ruff "W", # pycodestyle "UP", # pyupgrade "YTT", # flake8-2020 ] extend-ignore = ["B018", "B019", "RUF018"] [tool.ruff.lint.mccabe] max-complexity = 10 [tool.ruff.lint.isort] known-first-party = ["pdm"] [tool.towncrier] package = "pdm" filename = "CHANGELOG.md" issue_format = "[#{issue}](https://github.com/pdm-project/pdm/issues/{issue})" directory = "news/" title_format = "## Release v{version} ({project_date})" underlines = ["", "", ""] [[tool.towncrier.type]] directory = "break" name = "Breaking Changes" showcontent = true [[tool.towncrier.type]] directory = "feature" name = "Features & Improvements" showcontent = true [[tool.towncrier.type]] directory = "bugfix" name = "Bug Fixes" showcontent = true [[tool.towncrier.type]] directory = "doc" name = "Documentation" showcontent = true [[tool.towncrier.type]] directory = "dep" name = "Dependencies" showcontent = true [[tool.towncrier.type]] directory = "removal" name = "Removals and Deprecations" showcontent = true [[tool.towncrier.type]] directory = "misc" name = "Miscellany" showcontent = true [tool.pytest.ini_options] filterwarnings = [ "ignore::DeprecationWarning" ] markers = [ "network: Tests that require network", "integration: Run with all Python versions", "path: Tests that compare with the system paths", "deprecated: Tests about deprecated features", "uv: Tests that require uv to be installed", ] addopts = "-r aR" testpaths = [ "tests/", ] [tool.codespell] ignore-words-list = "ba,overriden,te,instal" [tool.coverage.run] branch = true source = ["pdm"] omit = [ "*/pdm/__main__.py", "*/pdm/pep582/sitecustomize.py", "*/pdm/models/in_process/*.py", "*/pdm/models/setup.py", "*/pdm-test-*-env/*", ] [tool.coverage.report] # Regexes for lines to exclude from consideration exclude_lines = [ "pragma: no cover", # Don't complain about missing debug-only code: "def __repr__", "if self.debug", # Don't complain if tests don't hit defensive assertion code: "raise AssertionError", "raise NotImplementedError", # Don't complain if non-runnable code isn't run: "if __name__ == .__main__.:", "if TYPE_CHECKING:", ] ignore_errors = true [tool.mypy] follow_imports = "silent" ignore_missing_imports = true disallow_incomplete_defs = true disallow_untyped_defs = true disallow_untyped_decorators = true exclude = "pdm/(pep582/|models/in_process/.+\\.py)" namespace_packages = true mypy_path = "src" explicit_package_bases = true [tool.pdm.version] source = "scm" write_to = "pdm/VERSION" [tool.pdm.build] excludes = ["./**/.git"] package-dir = "src" includes = ["src/pdm"] source-includes = ["tests", "typings", "CHANGELOG.md", "LICENSE", "README.md", "tox.ini"] # editables backend doesn't work well with namespace packages editable-backend = "path" locked = true locked-groups = ["default", "all"] [tool.pdm.scripts] pre_release = "python tasks/max_versions.py" release = "python tasks/release.py" test = "pytest" coverage = {shell = """\ python -m pytest \ --verbosity=3 \ --cov=src/pdm \ --cov-branch \ --cov-report term-missing \ tests/ """} tox = "tox" doc = {cmd = "mkdocs serve", help = "Start the dev server for docs preview"} lint = "pre-commit run --all-files" complete = {call = "tasks.complete:main", help = "Create autocomplete files for bash and fish"} pdm-2.23.1/src/000077500000000000000000000000001477560627500131715ustar00rootroot00000000000000pdm-2.23.1/src/pdm/000077500000000000000000000000001477560627500137515ustar00rootroot00000000000000pdm-2.23.1/src/pdm/__init__.py000066400000000000000000000001031477560627500160540ustar00rootroot00000000000000import pkgutil __path__ = pkgutil.extend_path(__path__, __name__) pdm-2.23.1/src/pdm/__main__.py000066400000000000000000000001011477560627500160330ustar00rootroot00000000000000from pdm.core import main if __name__ == "__main__": main() pdm-2.23.1/src/pdm/__version__.py000066400000000000000000000004651477560627500166110ustar00rootroot00000000000000from pdm.compat import importlib_metadata, resources_read_text def read_version() -> str: try: return importlib_metadata.version(__package__ or "pdm") except importlib_metadata.PackageNotFoundError: return resources_read_text("pdm", "VERSION").strip() __version__ = read_version() pdm-2.23.1/src/pdm/_types.py000066400000000000000000000071231477560627500156310ustar00rootroot00000000000000from __future__ import annotations import dataclasses as dc import re from typing import TYPE_CHECKING, Any, NamedTuple, TypeVar, Union if TYPE_CHECKING: from typing import Protocol def _normalize_pattern(pattern: str) -> str: return re.sub(r"[^A-Za-z0-9?*\[\]-]+", "-", pattern).lower() @dc.dataclass class RepositoryConfig: """Private dataclass to be subclassed""" config_prefix: str name: str url: str | None = None username: str | None = None password: str | None = dc.field(default=None, repr=False) verify_ssl: bool | None = None type: str | None = None ca_certs: str | None = None client_cert: str | None = None client_key: str | None = None include_packages: list[str] = dc.field(default_factory=list) exclude_packages: list[str] = dc.field(default_factory=list) def __post_init__(self) -> None: self.include_packages = [_normalize_pattern(p) for p in self.include_packages] self.exclude_packages = [_normalize_pattern(p) for p in self.exclude_packages] def populate_keyring_auth(self) -> None: if self.username is None or self.password is None: from pdm.models.auth import keyring service = f"pdm-{self.config_prefix}-{self.name}" auth = keyring.get_auth_info(service, self.username) if auth is not None: self.username, self.password = auth def passive_update(self, other: RepositoryConfig | None = None, **kwargs: Any) -> None: """An update method that prefers the existing value over the new one.""" if other is not None: for k in other.__dataclass_fields__: v = getattr(other, k) if getattr(self, k) is None and v is not None: setattr(self, k, v) for k, v in kwargs.items(): if getattr(self, k) is None and v is not None: setattr(self, k, v) def __rich__(self) -> str: config_prefix = f"{self.config_prefix}.{self.name}." if self.name else f"{self.config_prefix}." lines: list[str] = [] self.populate_keyring_auth() if self.url: lines.append(f"[primary]{config_prefix}url[/] = {self.url}") if self.username: lines.append(f"[primary]{config_prefix}username[/] = {self.username}") if self.password: lines.append(f"[primary]{config_prefix}password[/] = [i][/]") if self.verify_ssl is not None: lines.append(f"[primary]{config_prefix}verify_ssl[/] = {self.verify_ssl}") if self.type: lines.append(f"[primary]{config_prefix}type[/] = {self.type}") if self.ca_certs: lines.append(f"[primary]{config_prefix}ca_certs[/] = {self.ca_certs}") return "\n".join(lines) RequirementDict = Union[str, dict[str, Union[str, bool]]] CandidateInfo = tuple[list[str], str, str] class SearchResult(NamedTuple): name: str version: str summary: str SearchResults = list[SearchResult] if TYPE_CHECKING: from typing import Required, TypedDict class Comparable(Protocol): def __lt__(self, __other: Any) -> bool: ... SpinnerT = TypeVar("SpinnerT", bound="Spinner") class Spinner(Protocol): def update(self, text: str) -> None: ... def __enter__(self: SpinnerT) -> SpinnerT: ... def __exit__(self, *args: Any) -> None: ... class RichProtocol(Protocol): def __rich__(self) -> str: ... class FileHash(TypedDict, total=False): url: str hash: Required[str] file: str class NotSetType: pass NotSet = NotSetType() pdm-2.23.1/src/pdm/builders/000077500000000000000000000000001477560627500155625ustar00rootroot00000000000000pdm-2.23.1/src/pdm/builders/__init__.py000066400000000000000000000003111477560627500176660ustar00rootroot00000000000000from pdm.builders.editable import EditableBuilder from pdm.builders.sdist import SdistBuilder from pdm.builders.wheel import WheelBuilder __all__ = ["EditableBuilder", "SdistBuilder", "WheelBuilder"] pdm-2.23.1/src/pdm/builders/base.py000066400000000000000000000306001477560627500170450ustar00rootroot00000000000000from __future__ import annotations import functools import logging import os import shutil import subprocess import textwrap import threading from logging import Logger from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar, Iterable, cast from pyproject_hooks import ( BackendInvalid, BackendUnavailable, BuildBackendHookCaller, HookMissing, UnsupportedOperation, ) from pdm.compat import tomllib from pdm.environments import PythonEnvironment from pdm.exceptions import BuildError from pdm.models.in_process import get_sys_config_paths from pdm.models.requirements import Requirement, parse_requirement from pdm.models.working_set import WorkingSet from pdm.termui import logger if TYPE_CHECKING: from typing import Callable, ParamSpec, TypeVar from pdm.environments import BaseEnvironment R = TypeVar("R") P = ParamSpec("P") class LoggerWrapper(threading.Thread): """ Read messages from a pipe and redirect them to a logger (see python's logging module). """ def __init__(self, logger: Logger, level: int) -> None: super().__init__() self.daemon = True self.logger = logger self.level = level # create the pipe and reader self.fd_read, self.fd_write = os.pipe() self.start() self._output_buffer: list[str] = [] def fileno(self) -> int: return self.fd_write @staticmethod def remove_newline(msg: str) -> str: return msg[:-1] if msg.endswith("\n") else msg def run(self) -> None: with os.fdopen(self.fd_read, encoding="utf-8", errors="replace") as reader: for line in reader: self._write(self.remove_newline(line)) def _write(self, message: str) -> None: self.logger.log(self.level, message) self._output_buffer.append(message) if len(self._output_buffer) > 10: self._output_buffer[:-10] = [] def stop(self) -> None: os.close(self.fd_write) self.join() def wrap_error(func: Callable[P, R]) -> Callable[P, R]: def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: try: return func(*args, **kwargs) except (HookMissing, BackendUnavailable, BackendInvalid, UnsupportedOperation) as e: raise BuildError(str(e)) from e return functools.update_wrapper(wrapper, func) def build_error(e: subprocess.CalledProcessError) -> BuildError: """Get a build error with meaningful error message from the subprocess output. """ output = cast("list[str]", e.output) errors: list[str] = [] if output and output[-1].strip().startswith("ModuleNotFoundError"): package = output[-1].strip().split()[-1] errors.append( f"Module {package} is missing, please make sure it is specified in the " "'build-system.requires' section. If it is not possible, " "add it to the project and use '--no-isolation' option." ) errors.extend(["Showing the last 10 lines of the build output:", *output]) error_message = "\n".join(errors) return BuildError(f"Build backend raised error: {error_message}") def log_subprocessor( cmd: list[str], cwd: str | Path | None = None, extra_environ: dict[str, str] | None = None, ) -> None: env = os.environ.copy() if extra_environ: env.update(extra_environ) outstream = LoggerWrapper(logger, logging.INFO) try: subprocess.check_call( cmd, cwd=cwd, env=env, stdout=outstream.fileno(), stderr=subprocess.STDOUT, ) except subprocess.CalledProcessError as e: e.output = outstream._output_buffer raise build_error(e) from None finally: outstream.stop() class _Prefix: def __init__(self, executable: str, shared: str, overlay: str) -> None: self.bin_dirs: list[str] = [] self.lib_dirs: list[str] = [] for path in (overlay, shared): paths = get_sys_config_paths(executable, vars={"base": path, "platbase": path}, kind="prefix") self.bin_dirs.append(paths["scripts"]) self.lib_dirs.extend({paths["platlib"], paths["purelib"]}) self.site_dir = os.path.join(overlay, "site") if os.path.isdir(self.site_dir): # Clear existing site dir as .pyc may be cached. shutil.rmtree(self.site_dir) os.makedirs(self.site_dir) with open(os.path.join(self.site_dir, "sitecustomize.py"), "w", encoding="utf-8") as fp: fp.write( textwrap.dedent( f""" import sys, os, site original_sys_path = sys.path[:] known_paths = set() site.addusersitepackages(known_paths) site.addsitepackages(known_paths) known_paths = {{os.path.normcase(p) for p in known_paths}} original_sys_path = [ p for p in original_sys_path if os.path.normcase(p) not in known_paths] sys.path[:] = original_sys_path for lib_path in {self.lib_dirs!r}: site.addsitedir(lib_path) """ ) ) self.shared = shared self.overlay = overlay class EnvBuilder: """A simple PEP 517 builder for an isolated environment""" DEFAULT_BACKEND: ClassVar[dict[str, Any]] = { "build-backend": "setuptools.build_meta:__legacy__", "requires": ["setuptools>=61"], } _shared_envs: ClassVar[dict[int, str]] = {} _overlay_envs: ClassVar[dict[str, str]] = {} if TYPE_CHECKING: _hook: BuildBackendHookCaller _requires: list[str] _prefix: _Prefix | None def get_shared_env(self, key: int) -> str: if key in self._shared_envs: logger.debug("Reusing shared build env: %s", self._shared_envs[key]) return self._shared_envs[key] # We don't save the cache here, instead it will be done after the installation # finished. return self._env.project.core.create_temp_dir("-shared", "pdm-build-env-") def get_overlay_env(self, key: str) -> str: if key not in self._overlay_envs: self._overlay_envs[key] = self._env.project.core.create_temp_dir("-overlay", "pdm-build-env-") return self._overlay_envs[key] def __init__(self, src_dir: str | Path, environment: BaseEnvironment) -> None: """If isolated is True(default), the builder will set up a *clean* environment. Otherwise, the environment of the host Python will be used. """ self._env = environment self.executable = self._env.interpreter.executable.as_posix() self.src_dir = src_dir self.isolated = environment.project.core.state.build_isolation self.config_settings = environment.project.core.state.config_settings mode = "Isolated" if self.isolated else "Non-isolated" logger.info("Preparing environment(%s mode) for PEP 517 build...", mode) try: with open(os.path.join(src_dir, "pyproject.toml"), "rb") as f: spec = tomllib.load(f) except FileNotFoundError: spec = {} except Exception as e: raise BuildError(e) from e build_system = spec.get("build-system", self.DEFAULT_BACKEND) if "build-backend" not in build_system: build_system["build-backend"] = self.DEFAULT_BACKEND["build-backend"] if "requires" not in build_system: raise BuildError("Missing 'build-system.requires' in pyproject.toml") self.init_build_system(build_system) def init_build_system(self, build_system: dict[str, Any]) -> None: """Initialize the build system and requires list from the PEP 517 spec""" self._hook = BuildBackendHookCaller( self.src_dir, build_system["build-backend"], backend_path=build_system.get("backend-path"), runner=self.subprocess_runner, python_executable=self.executable, ) self._requires = build_system["requires"] self._prefix = ( _Prefix( self.executable, # Build backends with the same requires list share the cached base env. shared=self.get_shared_env(hash(frozenset(self._requires))), # Overlay envs are unique for each source to be built. overlay=self.get_overlay_env(os.path.normcase(self.src_dir).rstrip("\\/")), ) if self.isolated else None ) @property def _env_vars(self) -> dict[str, str]: env: dict[str, str] = {} if self.isolated: assert self._prefix is not None paths = self._prefix.bin_dirs[:] env.update( { "PYTHONPATH": self._prefix.site_dir, "PYTHONNOUSERSITE": "1", } ) else: env_paths = self._env.get_paths() pythonpath = list({env_paths["purelib"], env_paths["platlib"]}) if "PYTHONPATH" in os.environ: pythonpath.append(os.getenv("PYTHONPATH", "")) env.update( PYTHONPATH=os.pathsep.join(pythonpath), ) paths = [env_paths["scripts"]] if "PATH" in os.environ: paths.append(os.getenv("PATH", "")) env["PATH"] = os.pathsep.join(paths) return env def subprocess_runner( self, cmd: list[str], cwd: str | Path | None = None, extra_environ: dict[str, str] | None = None ) -> None: env = self._env_vars.copy() if extra_environ: env.update(extra_environ) return log_subprocessor(cmd, cwd, extra_environ=env) def check_requirements(self, reqs: Iterable[str]) -> Iterable[Requirement]: missing = set() conflicting = set() env_paths = self._env.get_paths() libs = ( list({env_paths["purelib"], env_paths["platlib"]}) if not self.isolated else cast(_Prefix, self._prefix).lib_dirs ) if reqs: ws = WorkingSet(libs) for req in reqs: parsed_req = parse_requirement(req) parsed_req.groups = ["default"] if parsed_req.marker and not parsed_req.marker.matches(self._env.spec): logger.debug( "Skipping requirement %s: mismatching marker %s", req, parsed_req.marker, ) continue if parsed_req.identify() not in ws: missing.add(parsed_req) elif parsed_req.specifier and not parsed_req.specifier.contains( ws[parsed_req.identify()].version, prereleases=True ): conflicting.add(req) if conflicting: raise BuildError(f"Conflicting requirements: {', '.join(conflicting)}") return missing def install(self, requirements: Iterable[str], shared: bool = False) -> None: from pdm.installers.core import install_requirements missing = list(self.check_requirements(requirements)) if not missing: return assert self._prefix is not None path = self._prefix.shared if shared else self._prefix.overlay env = PythonEnvironment(self._env.project, python=str(self._env.interpreter.path), prefix=path) install_requirements(missing, env, allow_uv=False) if shared: # The shared env is prepared and is safe to be cached now. This is to make # sure no broken env is returned early when run in parallel mode. key = hash(frozenset(requirements)) if key not in self._shared_envs: self._shared_envs[key] = path def prepare_metadata(self, out_dir: str) -> str: """Prepare metadata and store in the out_dir. Some backends doesn't provide that API, in that case the metadata will be retrieved from the built result. """ raise NotImplementedError("Should be implemented in subclass") def build(self, out_dir: str, metadata_directory: str | None = None) -> str: """Build and store the artifact in out_dir, return the absolute path of the built result. """ raise NotImplementedError("Should be implemented in subclass") pdm-2.23.1/src/pdm/builders/editable.py000066400000000000000000000020441477560627500177050ustar00rootroot00000000000000from __future__ import annotations import os from pdm.builders.base import EnvBuilder, wrap_error class EditableBuilder(EnvBuilder): """Build egg-info in isolated env with managed Python.""" @wrap_error def prepare_metadata(self, out_dir: str) -> str: if self.isolated: self.install(self._requires, shared=True) requires = self._hook.get_requires_for_build_editable(self.config_settings) self.install(requires) filename = self._hook.prepare_metadata_for_build_editable(out_dir, self.config_settings) return os.path.join(out_dir, filename) @wrap_error def build(self, out_dir: str, metadata_directory: str | None = None) -> str: if self.isolated: self.install(self._requires, shared=True) requires = self._hook.get_requires_for_build_editable(self.config_settings) self.install(requires) filename = self._hook.build_editable(out_dir, self.config_settings, metadata_directory) return os.path.join(out_dir, filename) pdm-2.23.1/src/pdm/builders/sdist.py000066400000000000000000000011431477560627500172610ustar00rootroot00000000000000from __future__ import annotations import os from pdm.builders.base import EnvBuilder, wrap_error class SdistBuilder(EnvBuilder): """Build sdist in isolated env with managed Python.""" @wrap_error def build(self, out_dir: str, metadata_directory: str | None = None) -> str: if self.isolated: self.install(self._requires, shared=True) requires = self._hook.get_requires_for_build_sdist(self.config_settings) self.install(requires) filename = self._hook.build_sdist(out_dir, self.config_settings) return os.path.join(out_dir, filename) pdm-2.23.1/src/pdm/builders/wheel.py000066400000000000000000000020221477560627500172340ustar00rootroot00000000000000from __future__ import annotations import os from pdm.builders.base import EnvBuilder, wrap_error class WheelBuilder(EnvBuilder): """Build wheel in isolated env with managed Python.""" @wrap_error def prepare_metadata(self, out_dir: str) -> str: if self.isolated: self.install(self._requires, shared=True) requires = self._hook.get_requires_for_build_wheel(self.config_settings) self.install(requires) filename = self._hook.prepare_metadata_for_build_wheel(out_dir, self.config_settings) return os.path.join(out_dir, filename) @wrap_error def build(self, out_dir: str, metadata_directory: str | None = None) -> str: if self.isolated: self.install(self._requires, shared=True) requires = self._hook.get_requires_for_build_wheel(self.config_settings) self.install(requires) filename = self._hook.build_wheel(out_dir, self.config_settings, metadata_directory) return os.path.join(out_dir, filename) pdm-2.23.1/src/pdm/cli/000077500000000000000000000000001477560627500145205ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/__init__.py000066400000000000000000000000001477560627500166170ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/actions.py000066400000000000000000000460201477560627500165340ustar00rootroot00000000000000from __future__ import annotations import contextlib import datetime import hashlib import inspect import json import os import sys import textwrap from typing import TYPE_CHECKING, Collection, Iterable, cast from resolvelib.resolvers import ResolutionImpossible, ResolutionTooDeep from pdm import termui from pdm.cli.filters import GroupSelection from pdm.cli.hooks import HookManager from pdm.cli.utils import ( check_project_file, find_importable_files, format_resolution_impossible, get_pep582_path, set_env_in_reg, ) from pdm.environments import BareEnvironment from pdm.exceptions import PdmException, PdmUsageError, ProjectError, ResolutionError from pdm.models.candidates import Candidate from pdm.models.markers import EnvSpec from pdm.models.repositories import LockedRepository, Package from pdm.project import Project from pdm.project.lockfile import FLAG_CROSS_PLATFORM, FLAG_INHERIT_METADATA, FLAG_STATIC_URLS from pdm.resolver.reporters import RichLockReporter from pdm.termui import logger from pdm.utils import deprecation_warning if TYPE_CHECKING: from pdm.models.requirements import Requirement def do_lock( project: Project, strategy: str = "all", tracked_names: Iterable[str] | None = None, requirements: list[Requirement] | None = None, dry_run: bool = False, refresh: bool = False, groups: list[str] | None = None, strategy_change: list[str] | None = None, hooks: HookManager | None = None, env_spec: EnvSpec | None = None, append: bool = False, ) -> dict[str, list[Candidate]]: """Performs the locking process and update lockfile.""" hooks = hooks or HookManager(project) check_project_file(project) lock_strategy = project.lockfile.apply_strategy_change(strategy_change or []) if FLAG_CROSS_PLATFORM in lock_strategy: # pragma: no cover project.core.ui.deprecated( "`cross_platform` strategy is deprecated in favor of the new lock targets.\n" "See docs: http://pdm-project.org/en/latest/usage/lock-targets/" ) if strategy_change and append: raise PdmUsageError("Not allowed to change lock strategy when --append is used.") locked_repo = project.get_locked_repository() if refresh: if env_spec is not None: raise PdmUsageError("Cannot pass --python/--platform/--implementation with --refresh") repo = project.get_repository() with project.core.ui.open_spinner("Re-calculating hashes..."): with project.core.ui.logging("lock"): candidates = [entry.candidate for entry in locked_repo.packages.values()] for c in candidates: c.hashes.clear() repo.fetch_hashes(candidates) lockfile = locked_repo.format_lockfile(groups=project.lockfile.groups, strategy=lock_strategy) project.write_lockfile(lockfile) return locked_repo.all_candidates if groups is None: groups = list(project.iter_groups()) if not requirements: all_deps = project._resolve_dependencies(groups) requirements = [r for group in groups for r in project.get_dependencies(group, all_deps)] ui = project.core.ui supports_env_spec = "env_spec" in inspect.signature(project.get_provider).parameters # The repository to store the lock result if locked_repo.packages: result_repo = LockedRepository({}, sources=project.sources, environment=project.environment) else: # Use the same repository if the lock is empty. result_repo = locked_repo if not supports_env_spec: # pragma: no cover ui.warn("Lock targets are not supported by the current provider") if append: if env_spec is None: raise PdmUsageError("Cannot use `--append` without --python/--platform/--implementation") if env_spec in locked_repo.targets: ui.echo(f"{termui.Emoji.LOCK} Lock target {env_spec} already exists, skip locking.") return locked_repo.all_candidates targets = [env_spec] result_repo = locked_repo # Append to the same lockfile else: targets = [env_spec] if env_spec else (locked_repo.targets[:] or project.lock_targets) # Restrict the target python to within the project's requires-python global_requires_python = project.environment.python_requires for i, target in enumerate(targets): if (merged := global_requires_python & target.requires_python).is_empty(): raise PdmUsageError( f"The target requires Python {target.requires_python} which is not compatible with " f"the project's requires-python {global_requires_python}" ) targets[i] = target.replace(requires_python=merged) resolve_max_rounds = int(project.config["strategy.resolve_max_rounds"]) hooks.try_emit("pre_lock", requirements=requirements, dry_run=dry_run) with ui.logging("lock"): # The context managers are nested to ensure the spinner is stopped before # any message is thrown to the output. resolver_class = project.get_resolver() with RichLockReporter(requirements, ui) as reporter: try: for target in targets: resolver = resolver_class( environment=project.environment, requirements=[r for r in requirements if not r.marker or r.marker.matches(target)], update_strategy=strategy, strategies=lock_strategy, target=target, tracked_names=list(tracked_names or ()), locked_repository=locked_repo, reporter=reporter, ) reporter.update(f"Resolve for environment {target}") resolved, collected_groups = resolver.resolve() locked_repo.merge_result(target, resolved) if result_repo is not locked_repo: result_repo.merge_result(target, resolved) except ResolutionTooDeep: reporter.update(f"{termui.Emoji.LOCK} Lock failed.", info="", completed=1) ui.echo( "The dependency resolution exceeds the maximum loop depth of " f"{resolve_max_rounds}, there may be some circular dependencies " "in your project. Try to solve them or increase the " f"[success]`strategy.resolve_max_rounds`[/] config.", err=True, ) raise ResolutionError( f"The dependency resolution exceeds the maximum loop depth of {resolve_max_rounds}" ) from None except ResolutionImpossible as err: reporter.update(f"{termui.Emoji.LOCK} Lock failed.", info="", completed=1) ui.error(format_resolution_impossible(err)) raise ResolutionError("Unable to find a resolution") from None else: groups = list(set(groups) | collected_groups) data = result_repo.format_lockfile(groups=groups, strategy=lock_strategy) if project.enable_write_lockfile: reporter.update(f"{termui.Emoji.LOCK} Lock successful.", info="", completed=1) project.write_lockfile(data, write=not dry_run) hooks.try_emit("post_lock", resolution=result_repo.all_candidates, dry_run=dry_run) return result_repo.all_candidates def resolve_from_lockfile( project: Project, requirements: Iterable[Requirement], groups: Collection[str] | None = None, env_spec: EnvSpec | None = None, ) -> Iterable[Package]: from dep_logic.tags import EnvCompatibility from pdm.resolver.resolvelib import RLResolver ui = project.core.ui if env_spec is None: # Resolve for the current environment by default env_spec = project.environment.spec reqs = [req for req in requirements if not req.marker or req.marker.matches(env_spec)] with ui.open_spinner("Resolving packages from lockfile..."): locked_repo = project.get_locked_repository(env_spec) lock_targets = locked_repo.targets if env_spec not in lock_targets: compatibilities = [target.compare(env_spec) for target in lock_targets] if not any(compat == EnvCompatibility.LOWER_OR_EQUAL for compat in compatibilities): loose_compatible_target = next( ( target for (target, compat) in zip(lock_targets, compatibilities) if compat == EnvCompatibility.HIGHER ), None, ) if loose_compatible_target is not None: ui.warn(f"Found lock target {loose_compatible_target}, installing for env {env_spec}") else: errors = [f"None of the lock targets matches the current env {env_spec}:"] + [ f" - {target}" for target in lock_targets ] ui.error("\n".join(errors)) raise PdmException("No compatible lock target found") with ui.logging("install-resolve"): strategies = project.lockfile.strategy.copy() if FLAG_INHERIT_METADATA in strategies and groups is not None and not project.config["use_uv"]: return locked_repo.evaluate_candidates(groups) strategies.add(FLAG_STATIC_URLS) resolver = project.get_resolver()( environment=project.environment, requirements=reqs, update_strategy="reuse", strategies=strategies, target=env_spec, tracked_names=(), locked_repository=locked_repo, ) if isinstance(resolver, RLResolver): # resolve from lock file resolver.provider.repository = locked_repo try: return resolver.resolve().packages except ResolutionImpossible as e: logger.exception("Broken lockfile") raise PdmException( "Resolving from lockfile failed. You may fix the lockfile by `pdm lock --update-reuse` and retry." ) from e def resolve_candidates_from_lockfile( project: Project, requirements: Iterable[Requirement], cross_platform: bool | None = None, groups: Collection[str] | None = None, env_spec: EnvSpec | None = None, ) -> dict[str, Candidate]: if cross_platform is not None: # pragma: no cover deprecation_warning("cross_platform argument is deprecated", stacklevel=2) packages = resolve_from_lockfile(project, requirements, groups, env_spec) return {p.candidate.identify(): p.candidate for p in packages} def check_lockfile(project: Project, raise_not_exist: bool = True) -> str | None: """Check if the lock file exists and is up to date. Return the lock strategy.""" from pdm.project.lockfile import Compatibility if not project.lockfile.exists(): if raise_not_exist: raise ProjectError("Lockfile does not exist, nothing to install") project.core.ui.warn("Lockfile does not exist") return "all" compat = project.lockfile.compatibility() if compat == Compatibility.NONE: project.core.ui.warn("Lockfile is not compatible with PDM") return "reuse" elif compat == Compatibility.BACKWARD: project.core.ui.warn("Lockfile is generated on an older version of PDM") elif compat == Compatibility.FORWARD: project.core.ui.warn("Lockfile is generated on a newer version of PDM") if not project.is_lockfile_hash_match(): project.core.ui.warn("Lockfile hash doesn't match pyproject.toml, packages may be outdated") return "reuse" return None def do_sync( project: Project, *, selection: GroupSelection, dry_run: bool = False, clean: bool = False, requirements: list[Requirement] | None = None, tracked_names: Collection[str] | None = None, no_editable: bool | Collection[str] = False, no_self: bool = False, reinstall: bool = False, only_keep: bool = False, fail_fast: bool = False, hooks: HookManager | None = None, ) -> None: """Synchronize project""" hooks = hooks or HookManager(project) if requirements is None: requirements = [] selection.validate() all_deps = project._resolve_dependencies(list(selection)) for group in selection: requirements.extend(project.get_dependencies(group, all_deps)) packages = list(resolve_from_lockfile(project, requirements, groups=list(selection))) if tracked_names and dry_run: packages = [p for p in packages if p.candidate.identify() in tracked_names] synchronizer = project.get_synchronizer()( project.environment, clean=clean, dry_run=dry_run, no_editable=no_editable, install_self=not no_self and project.is_distribution, reinstall=reinstall, only_keep=only_keep, fail_fast=fail_fast, packages=packages, ) with project.core.ui.logging("install"): hooks.try_emit("pre_install", packages=packages, dry_run=dry_run) synchronizer.synchronize() hooks.try_emit("post_install", packages=packages, dry_run=dry_run) def ask_for_import(project: Project) -> None: """Show possible importable files and ask user to decide""" from pdm.cli.commands.import_cmd import Command as ImportCommand importable_files = list(find_importable_files(project)) if not importable_files: return project.core.ui.echo("Found following files from other formats that you may import:", style="primary") for i, (key, filepath) in enumerate(importable_files): project.core.ui.echo(f"{i}. [success]{filepath.as_posix()}[/] ({key})") project.core.ui.echo(f"{len(importable_files)}. [warning]don't do anything, I will import later.[/]") choice = termui.ask( "Please select", prompt_type=int, choices=[str(i) for i in range(len(importable_files) + 1)], show_choices=False, ) if int(choice) == len(importable_files): return key, filepath = importable_files[int(choice)] ImportCommand.do_import(project, str(filepath), key, reset_backend=False) def print_pep582_command(project: Project, shell: str = "AUTO") -> None: """Print the export PYTHONPATH line to be evaluated by the shell.""" import shellingham pep582_path = get_pep582_path(project) ui = project.core.ui if os.name == "nt": try: set_env_in_reg("PYTHONPATH", pep582_path) except PermissionError: ui.error("Permission denied, please run the terminal as administrator.") ui.info("The environment variable has been saved, please restart the session to take effect.") return lib_path = pep582_path.replace("'", "\\'") if shell == "AUTO": shell = shellingham.detect_shell()[0] shell = shell.lower() if shell in ("zsh", "bash", "sh", "dash"): result = textwrap.dedent( f""" if [ -n "$PYTHONPATH" ]; then export PYTHONPATH='{lib_path}':$PYTHONPATH else export PYTHONPATH='{lib_path}' fi """ ).strip() elif shell == "fish": result = f"set -x PYTHONPATH '{lib_path}' $PYTHONPATH" elif shell in ("tcsh", "csh"): result = textwrap.dedent( f""" if ( $?PYTHONPATH ) then if ( "$PYTHONPATH" != "" ) then setenv PYTHONPATH '{lib_path}':$PYTHONPATH else setenv PYTHONPATH '{lib_path}' endif else setenv PYTHONPATH '{lib_path}' endif """ ).strip() else: raise PdmUsageError(f"Unsupported shell: {shell}, please specify another shell via `--pep582 `") ui.echo(result) def get_latest_pdm_version_from_pypi(project: Project, prereleases: bool = False) -> str | None: """Get the latest version of PDM from PyPI.""" environment = BareEnvironment(project) with environment.get_finder([project.default_source]) as finder: candidate = finder.find_best_match("pdm", allow_prereleases=prereleases).best return cast(str, candidate.version) if candidate else None def get_latest_version(project: Project, expire_after: int = 7 * 24 * 3600) -> str | None: # pragma: no cover """Get the latest version of PDM from PyPI, cache for 7 days""" cache_key = hashlib.sha224(sys.executable.encode()).hexdigest() cache_file = project.cache("self-check") / cache_key state = {} with contextlib.suppress(OSError): state = json.loads(cache_file.read_text()) current_time = datetime.datetime.now(datetime.timezone.utc).timestamp() if (last_check := state.get("last-check")) and current_time - last_check < expire_after: return cast(str, state["latest-version"]) try: latest_version = get_latest_pdm_version_from_pypi(project) except Exception as e: project.core.ui.warn(f"Failed to get latest version: {e}", verbosity=termui.Verbosity.NORMAL) latest_version = None if latest_version is None: return None state.update({"latest-version": latest_version, "last-check": current_time}) with contextlib.suppress(OSError): cache_file.write_text(json.dumps(state)) return latest_version def check_update(project: Project) -> None: # pragma: no cover """Check if there is a new version of PDM available""" from pdm.cli.utils import is_homebrew_installation, is_pipx_installation, is_scoop_installation from pdm.utils import parse_version if project.core.ui.verbosity < termui.Verbosity.NORMAL: return this_version = project.core.version latest_version = get_latest_version(project) if latest_version is None or parse_version(this_version) >= parse_version(latest_version): return disable_command = "pdm config check_update false" is_prerelease = parse_version(latest_version).is_prerelease if is_pipx_installation(): install_command = f"pipx upgrade {'--pip-args=--pre ' if is_prerelease else ''}pdm" elif is_homebrew_installation(): install_command = "brew upgrade pdm" elif is_scoop_installation(): install_command = "scoop update pdm" else: install_command = "pdm self update" + (" --pre" if is_prerelease else "") if os.name == "nt": # On Windows, the executable can't replace itself, we add the python prefix to the command # A bit ugly but it works install_command = f"{sys.executable} -m {install_command}" message = [ f"PDM [primary]{this_version}[/]", f" is installed, while [primary]{latest_version}[/]", " is available.\n", f"Please run [req]`{install_command}`[/]", " to upgrade.\n", f"Run [req]`{disable_command}`[/]", " to disable the check.", ] project.core.ui.info("".join(message)) pdm-2.23.1/src/pdm/cli/commands/000077500000000000000000000000001477560627500163215ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/commands/__init__.py000066400000000000000000000000001477560627500204200ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/commands/add.py000066400000000000000000000157461477560627500174400ustar00rootroot00000000000000from __future__ import annotations import argparse from typing import TYPE_CHECKING from pdm.cli.commands.base import BaseCommand from pdm.cli.filters import GroupSelection from pdm.cli.hooks import HookManager from pdm.cli.options import ( dry_run_option, frozen_lockfile_option, install_group, lockfile_option, override_option, packages_group, prerelease_option, save_strategy_group, skip_option, unconstrained_option, update_strategy_group, venv_option, ) from pdm.exceptions import PdmUsageError if TYPE_CHECKING: from typing import Collection from pdm.models.requirements import Requirement from pdm.project import Project class Command(BaseCommand): """Add package(s) to pyproject.toml and install them""" arguments = ( *BaseCommand.arguments, lockfile_option, frozen_lockfile_option, save_strategy_group, override_option, update_strategy_group, prerelease_option, unconstrained_option, packages_group, install_group, dry_run_option, venv_option, skip_option, ) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "-d", "--dev", default=False, action="store_true", help="Add packages into dev dependencies", ) parser.add_argument("-G", "--group", help="Specify the target dependency group to add into") parser.add_argument( "--no-sync", dest="sync", default=True, action="store_false", help="Only write pyproject.toml and do not sync the working set", ) def handle(self, project: Project, options: argparse.Namespace) -> None: if options.editables and options.no_editable: raise PdmUsageError("`--no-editable` cannot be used with `-e/--editable`") self.do_add( project, selection=GroupSelection.from_options(project, options), sync=options.sync, save=options.save_strategy or project.config["strategy.save"], strategy=options.update_strategy or project.config["strategy.update"], editables=options.editables, packages=options.packages, unconstrained=options.unconstrained, no_editable=options.no_editable, no_self=options.no_self, dry_run=options.dry_run, prerelease=options.prerelease, fail_fast=options.fail_fast, hooks=HookManager(project, options.skip), ) @staticmethod def do_add( project: Project, *, selection: GroupSelection, sync: bool = True, save: str = "compatible", strategy: str = "reuse", editables: Collection[str] = (), packages: Collection[str] = (), unconstrained: bool = False, no_editable: bool = False, no_self: bool = False, dry_run: bool = False, prerelease: bool | None = None, fail_fast: bool = False, hooks: HookManager | None = None, ) -> None: """Add packages and install""" from pdm.cli.actions import do_lock, do_sync from pdm.cli.utils import check_project_file, save_version_specifiers from pdm.models.requirements import parse_requirement from pdm.models.specifiers import get_specifier from pdm.utils import normalize_name hooks = hooks or HookManager(project) check_project_file(project) if editables and no_editable: raise PdmUsageError("Cannot use --no-editable with editable packages given.") group = selection.one() tracked_names: set[str] = set() requirements: list[Requirement] = [] lock_groups = ["default"] if project.lockfile.empty() else project.lockfile.groups if lock_groups is not None and group not in lock_groups: if project.enable_write_lockfile: project.core.ui.info(f"Adding group [success]{group}[/] to lockfile") lock_groups.append(group) if group == "default" or ( not selection.dev and normalize_name(group) not in project.pyproject.dev_dependencies ): if editables: raise PdmUsageError("Cannot add editables to the default or optional dependency group") for r in [parse_requirement(line, True) for line in editables] + [parse_requirement(line) for line in packages]: if project.is_distribution and normalize_name(name := project.name) == r.key and not r.extras: project.core.ui.warn(f"Package [req]{name}[/] is the project itself.") continue if r.is_file_or_url: r.relocate(project.backend) # type: ignore[attr-defined] key = r.identify() tracked_names.add(key) requirements.append(r) if requirements: project.core.ui.echo( f"Adding {'[bold]global[/] ' if project.is_global else ''}packages to [primary]{group}[/] " f"{'dev-' if selection.dev else ''}dependencies: " + ", ".join(f"[req]{r.as_line()}[/]" for r in requirements) ) project.add_dependencies(requirements, group, selection.dev or False, write=False) all_dependencies = project.all_dependencies group_deps = all_dependencies[group] for req in group_deps: if req.identify() in tracked_names: req.prerelease = prerelease if unconstrained: if not requirements: raise PdmUsageError("--unconstrained requires at least one package") for req in group_deps: req.specifier = get_specifier("") reqs = [r for g, deps in all_dependencies.items() for r in deps if lock_groups is None or g in lock_groups] with hooks.skipping("post_lock"): resolved = do_lock( project, strategy, tracked_names, reqs, dry_run=True, hooks=hooks, groups=lock_groups, ) # Update dependency specifiers and lockfile hash. deps_to_update = group_deps if unconstrained else requirements save_version_specifiers(deps_to_update, resolved, save) if not dry_run: project.add_dependencies(deps_to_update, group, selection.dev or False) project.write_lockfile(project.lockfile._data, False) hooks.try_emit("post_lock", resolution=resolved, dry_run=dry_run) if sync: do_sync( project, selection=GroupSelection(project, groups=[group], default=False), no_editable=no_editable and tracked_names, no_self=no_self or group != "default", requirements=list(group_deps), dry_run=dry_run, fail_fast=fail_fast, hooks=hooks, ) pdm-2.23.1/src/pdm/cli/commands/base.py000066400000000000000000000044061477560627500176110ustar00rootroot00000000000000from __future__ import annotations import argparse from argparse import _SubParsersAction from typing import Any, Sequence, TypeVar from pdm.cli.options import Option, global_option, project_option, verbose_option from pdm.project import Project C = TypeVar("C", bound="BaseCommand") class BaseCommand: """A CLI subcommand""" # The subcommand's name name: str | None = None # The subcommand's help string, if not given, __doc__ will be used. description: str | None = None # A list of pre-defined options which will be loaded on initializing # Rewrite this if you don't want the default ones arguments: Sequence[Option] = (verbose_option, global_option, project_option) @classmethod def init_parser(cls: type[C], parser: argparse.ArgumentParser) -> C: cmd = cls() for arg in cmd.arguments: arg.add_to_parser(parser) cmd.add_arguments(parser) return cmd @classmethod def register_to(cls, subparsers: _SubParsersAction, name: str | None = None, **kwargs: Any) -> None: """Register a subcommand to the subparsers, with an optional name of the subcommand. """ help_text = cls.description or cls.__doc__ name = name or cls.name or "" # Remove the existing subparser as it will raise an error on Python 3.11+ subparsers._name_parser_map.pop(name, None) subactions = subparsers._get_subactions() subactions[:] = [action for action in subactions if action.dest != name] parser = subparsers.add_parser( name, description=help_text, help=help_text, **kwargs, ) command = cls.init_parser(parser) command.name = name # Store the command instance in the parsed args. See pdm/core.py for more details parser.set_defaults(command=command) def add_arguments(self, parser: argparse.ArgumentParser) -> None: """Manipulate the argument parser to add more arguments""" pass def handle(self, project: Project, options: argparse.Namespace) -> None: """The command handler function. :param project: the pdm project instance :param options: the parsed Namespace object """ raise NotImplementedError pdm-2.23.1/src/pdm/cli/commands/build.py000066400000000000000000000132051477560627500177730ustar00rootroot00000000000000from __future__ import annotations import argparse import os import shutil import tarfile import tempfile from pathlib import Path from typing import Mapping from pdm.cli.commands.base import BaseCommand from pdm.cli.hooks import HookManager from pdm.cli.options import ( config_setting_option, no_isolation_option, project_option, skip_option, verbose_option, ) from pdm.exceptions import ProjectError from pdm.project import Project class Command(BaseCommand): """Build artifacts for distribution""" arguments = (verbose_option, project_option, no_isolation_option, skip_option, config_setting_option) @staticmethod def do_build( project: Project, sdist: bool = True, wheel: bool = True, dest: str = "dist", clean: bool = True, verbose: int = 0, config_settings: Mapping[str, str] | None = None, hooks: HookManager | None = None, ) -> None: """Build artifacts for distribution.""" from pdm.builders import SdistBuilder, WheelBuilder hooks = hooks or HookManager(project) config_settings = project.core.state.config_settings if project.is_global: raise ProjectError("Not allowed to build based on the global project.") if not project.is_distribution: # pragma: no cover raise ProjectError("tool.pdm.distribution must be `true` to be built.") if not wheel and not sdist: project.core.ui.echo("All artifacts are disabled, nothing to do.", err=True) return if not os.path.isabs(dest): dest = project.root.joinpath(dest).as_posix() if clean: shutil.rmtree(dest, ignore_errors=True) if not os.path.exists(dest): os.makedirs(dest, exist_ok=True) hooks.try_emit("pre_build", dest=dest, config_settings=config_settings) artifacts: list[str] = [] with project.core.ui.logging("build"): if not project.config["use_uv"]: if sdist: project.core.ui.echo("[info]Building sdist...") sdist_file = SdistBuilder(project.root, project.environment).build(dest) project.core.ui.echo(f"[info]Built sdist at {sdist_file}") artifacts.append(sdist_file) if wheel: if sdist: project.core.ui.echo("[info]Building wheel from sdist...") sdist_out = tempfile.mkdtemp(prefix="pdm-build-via-sdist-") try: with tarfile.open(sdist_file, "r:gz") as tf: tf.extractall(sdist_out) sdist_name = os.path.basename(sdist_file)[: -len(".tar.gz")] whl = WheelBuilder(os.path.join(sdist_out, sdist_name), project.environment).build(dest) project.core.ui.echo(f"[info]Built wheel at {whl}") artifacts.append(whl) finally: shutil.rmtree(sdist_out, ignore_errors=True) else: project.core.ui.echo("[info]Building wheel...") whl = WheelBuilder(project.root, project.environment).build(dest) project.core.ui.echo(f"[info]Built wheel at {whl}") artifacts.append(whl) else: import subprocess dest_dir = Path(dest).absolute() uv_build_cmd = [*project.core.uv_cmd, "build", "--out-dir", str(dest_dir)] if verbose == -1: uv_build_cmd.append("-q") elif verbose > 0: uv_build_cmd.append(f"-{'v' * verbose}") subprocess.run(uv_build_cmd, check=True) # pdm build doesn't include .gitignore, and pdm publish would fail with .gitignore (dest_dir / ".gitignore").unlink(missing_ok=True) for sdist_fp in dest_dir.glob("*.tar.gz"): if sdist is False: sdist_fp.unlink(missing_ok=True) else: artifacts.append(str(sdist_fp)) for whl_file in dest_dir.glob("*.whl"): if wheel is False: whl_file.unlink(missing_ok=True) else: artifacts.append(str(whl_file)) hooks.try_emit("post_build", artifacts=artifacts, config_settings=config_settings) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "--no-sdist", dest="sdist", default=True, action="store_false", help="Don't build source tarballs", ) parser.add_argument( "--no-wheel", dest="wheel", default=True, action="store_false", help="Don't build wheels", ) parser.add_argument("-d", "--dest", default="dist", help="Target directory to put artifacts") parser.add_argument( "--no-clean", dest="clean", default=True, action="store_false", help="Do not clean the target directory", ) def handle(self, project: Project, options: argparse.Namespace) -> None: self.do_build( project, sdist=options.sdist, wheel=options.wheel, dest=options.dest, clean=options.clean, verbose=options.verbose, hooks=HookManager(project, options.skip), ) pdm-2.23.1/src/pdm/cli/commands/cache.py000066400000000000000000000136721477560627500177470ustar00rootroot00000000000000import argparse import os from pathlib import Path from typing import Iterable from pdm import termui from pdm.cli.commands.base import BaseCommand from pdm.cli.options import verbose_option from pdm.exceptions import PdmUsageError from pdm.project import Project class Command(BaseCommand): """Control the caches of PDM""" arguments = (verbose_option,) def add_arguments(self, parser: argparse.ArgumentParser) -> None: subparsers = parser.add_subparsers(title="commands", metavar="") ClearCommand.register_to(subparsers, "clear") RemoveCommand.register_to(subparsers, "remove") ListCommand.register_to(subparsers, "list") InfoCommand.register_to(subparsers, "info") parser.set_defaults(search_parent=False) self.parser = parser def handle(self, project: Project, options: argparse.Namespace) -> None: self.parser.print_help() def file_size(file: Path) -> int: if file.is_symlink(): return 0 return os.path.getsize(file) def find_files(parent: Path, pattern: str) -> Iterable[Path]: for file in parent.rglob(pattern): if file.is_file() or file.is_symlink(): yield file def directory_size(directory: Path) -> int: return sum(map(file_size, find_files(directory, "*"))) def format_size(size: float) -> str: if size > 1000 * 1000: return f"{size / 1000.0 / 1000:.1f} MB" elif size > 10 * 1000: return f"{int(size / 1000)} kB" elif size > 1000: return f"{size / 1000.0:.1f} kB" else: return f"{int(size)} bytes" def remove_cache_files(project: Project, pattern: str) -> None: if not pattern: raise PdmUsageError("Please provide a pattern") wheel_cache = project.cache("wheels") files = list(find_files(wheel_cache, pattern)) if not files: raise PdmUsageError("No matching files found") for file in files: os.unlink(file) project.core.ui.echo(f"Removed {file}", verbosity=termui.Verbosity.DETAIL) project.core.ui.echo(f"{len(files)} file{'s' if len(files) > 1 else ''} removed") class ClearCommand(BaseCommand): """Clean all the files under cache directory""" arguments = (verbose_option,) CACHE_TYPES = ("hashes", "http", "wheels", "metadata", "packages") def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "type", nargs="?", help="Clear the given type of caches", choices=self.CACHE_TYPES, ) @staticmethod def _clear_files(root: Path) -> int: files = list(find_files(root, "*")) for file in files: os.unlink(file) return len(files) def handle(self, project: Project, options: argparse.Namespace) -> None: if not options.type: types: Iterable[str] = self.CACHE_TYPES else: types = (str(options.type),) packages = files = 0 with project.core.ui.open_spinner(f"Clearing {options.type or 'all'} caches..."): for type_ in types: if type_ == "packages": packages += project.package_cache.cleanup() else: files += self._clear_files(project.cache(type_)) message = [] if packages: message.append(f"{packages} package{'s' if packages > 1 else ''}") if files: message.append(f"{files} file{'s' if files > 1 else ''}") if not message: # pragma: no cover text = "No files need to be removed" else: text = f"{' and '.join(message)} are removed" project.core.ui.echo(text) class RemoveCommand(BaseCommand): """Remove files matching the given pattern""" arguments = (verbose_option,) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument("pattern", help="The pattern to remove") def handle(self, project: Project, options: argparse.Namespace) -> None: return remove_cache_files(project, options.pattern) class ListCommand(BaseCommand): """List the built wheels stored in the cache""" arguments = (verbose_option,) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument("pattern", nargs="?", default="*", help="The pattern to list") def handle(self, project: Project, options: argparse.Namespace) -> None: rows = [ (format_size(file_size(file)), file.name) for file in find_files(project.cache("wheels"), options.pattern) ] project.core.ui.display_columns(rows, [">Size", "Filename"]) class InfoCommand(BaseCommand): """Show the info and current size of caches""" arguments = (verbose_option,) def handle(self, project: Project, options: argparse.Namespace) -> None: with project.core.ui.open_spinner("Calculating cache files"): output = [ f"[primary]Cache Root[/]: {project.cache_dir}, " f"Total size: {format_size(directory_size(project.cache_dir))}" ] for name, description in [ ("hashes", "File Hash Cache"), ("http", "HTTP Cache"), ("wheels", "Wheels Cache"), ("metadata", "Metadata Cache"), ("packages", "Package Cache"), ]: cache_location = project.cache(name) size = directory_size(cache_location) output.append(f" [primary]{description}[/]: {cache_location}") if name == "packages": packages = list(project.package_cache.iter_packages()) output.append(f" Packages: {len(packages)}, Size: {format_size(size)}") else: files = list(find_files(cache_location, "*")) output.append(f" Files: {len(files)}, Size: {format_size(size)}") project.core.ui.echo("\n".join(output)) pdm-2.23.1/src/pdm/cli/commands/completion.py000066400000000000000000000023541477560627500210500ustar00rootroot00000000000000from __future__ import annotations import argparse import sys from pdm.cli.commands.base import BaseCommand from pdm.compat import resources_read_text from pdm.exceptions import PdmUsageError from pdm.project import Project class Command(BaseCommand): """Generate completion scripts for the given shell""" arguments = () SUPPORTED_SHELLS = ("bash", "zsh", "fish", "powershell", "pwsh") def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "shell", nargs="?", help="The shell to generate the scripts for. If not given, PDM will properly guess from `SHELL` env var.", ) def handle(self, project: Project, options: argparse.Namespace) -> None: import shellingham shell = options.shell or shellingham.detect_shell()[0] if shell not in self.SUPPORTED_SHELLS: raise PdmUsageError(f"Unsupported shell: {shell}") suffix = "ps1" if shell in {"powershell", "pwsh"} else shell completion = resources_read_text("pdm.cli.completions", f"pdm.{suffix}") # Can't use rich print or otherwise the rich markups will be interpreted print(completion.replace("%{python_executable}", sys.executable)) pdm-2.23.1/src/pdm/cli/commands/config.py000066400000000000000000000166531477560627500201530ustar00rootroot00000000000000import argparse import os from pathlib import Path from typing import Any, Mapping from pdm import termui from pdm._types import RepositoryConfig from pdm.cli.commands.base import BaseCommand from pdm.exceptions import PdmUsageError from pdm.project import Project from pdm.project.config import DEFAULT_REPOSITORIES, REPOSITORY, SOURCE, Config class Command(BaseCommand): """Display the current configuration""" ui: termui.UI def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "-l", "--local", action="store_true", help="Set config in the project's local configuration file", ) parser.add_argument("-d", "--delete", action="store_true", help="Unset a configuration key") parser.add_argument( "-e", "--edit", action="store_true", help="Edit the configuration file in the default editor(defined by EDITOR env var)", ) parser.add_argument("key", help="Config key", nargs="?") parser.add_argument("value", help="Config value", nargs="?") @staticmethod def get_editor() -> str: for key in "VISUAL", "EDITOR": rv = os.getenv(key) if rv: return rv if os.name == "nt": return "notepad" for editor in "sensible-editor", "vim", "nano": if os.system(f"which {editor} >/dev/null 2>&1") == 0: return editor return "vi" def edit_file(self, path: Path) -> None: import subprocess editor = self.get_editor() proc = subprocess.Popen(f'{editor} "{path}"', shell=True) if proc.wait() != 0: raise PdmUsageError(f"Editor {editor} exited abnormally") def handle(self, project: Project, options: argparse.Namespace) -> None: self.ui = project.core.ui if options.edit: if options.key: raise PdmUsageError("Cannot specify an argument when `--edit` is given") if options.delete: raise PdmUsageError("`--delete` doesn't work when `--edit` is given") config = project.project_config if options.local else project.global_config return self.edit_file(config.config_file) if options.delete: self._delete_config(project, options) elif options.value: self._set_config(project, options) elif options.key: self._get_config(project, options) else: self._list_config(project, options) def _get_config(self, project: Project, options: argparse.Namespace) -> None: from findpython import ALL_PROVIDERS if options.key in project.project_config.deprecated: # pragma: no cover project.core.ui.warn( f"[warning]DEPRECATED:[/] the config has been renamed to {project.project_config.deprecated[options.key]}", ) options.key = project.project_config.deprecated[options.key] try: value = project.project_config[options.key] except KeyError: value = project.global_config[options.key] if options.key.endswith(".password"): value = "[i][/i]" elif options.key == "python.providers" and not value: value = ["venv", *ALL_PROVIDERS] project.core.ui.echo(value) def _set_config(self, project: Project, options: argparse.Namespace) -> None: config = project.project_config if options.local else project.global_config if options.key in config.deprecated: # pragma: no cover project.core.ui.warn( f"[warning]DEPRECATED:[/] the config has been renamed to {config.deprecated[options.key]}", ) config[options.key] = options.value def _show_config(self, config: Mapping[str, Any], supersedes: Mapping[str, Any]) -> None: from findpython import ALL_PROVIDERS assert Config.site is not None for key in sorted(config): deprecated = "" canonical_key = key superseded = key in supersedes if key in Config.site.deprecated: # pragma: no cover canonical_key = Config.site.deprecated[key] if canonical_key in supersedes: superseded = True deprecated = f"[error](deprecating: {key})[/]" elif key not in Config._config_map and not (key.startswith("pypi.") or key.startswith(REPOSITORY)): continue extra_style = "dim" if superseded else None if canonical_key not in Config._config_map: prefix, name = key.split(".", 1) if prefix in (SOURCE, REPOSITORY): title = "non-default PyPI index" if prefix == SOURCE else "custom repository" self.ui.echo( f"[warning]# Configuration of {title} `{name}`", style=extra_style, verbosity=termui.Verbosity.DETAIL, ) repository = RepositoryConfig(**config[key], config_prefix=prefix, name=name) if not repository.url and name in DEFAULT_REPOSITORIES: repository.url = DEFAULT_REPOSITORIES[name] self.ui.echo(repository) continue config_item = Config._config_map[canonical_key] self.ui.echo( f"[warning]# {config_item.description}", style=extra_style, verbosity=termui.Verbosity.DETAIL, ) if key.endswith("password"): value: Any = "[i][/i]" else: value = config[key] if key == "python.providers" and not value: value = ["venv", *ALL_PROVIDERS] self.ui.echo( f"[primary]{canonical_key}[/]{deprecated} = {value}", style=extra_style, ) def _list_config(self, project: Project, options: argparse.Namespace) -> None: assert Config.site is not None site_title = "Site/default configuration" if Config.site.config_file.exists(): site_title += f" ([success]{Config.site.config_file}[/])" self.ui.echo(site_title, style="bold") self._show_config( Config.get_defaults(), {**project.global_config.self_data, **project.project_config.self_data}, ) if project.global_config.self_data: self.ui.echo( f"\nHome configuration ([success]{project.global_config.config_file}[/]):", style="bold", ) self._show_config(project.global_config.self_data, project.project_config.self_data) if project.project_config.self_data: self.ui.echo( f"\nProject configuration ([success]{project.project_config.config_file}[/]):", style="bold", ) self._show_config(project.project_config.self_data, {}) def _delete_config(self, project: Project, options: argparse.Namespace) -> None: config = project.project_config if options.local else project.global_config if options.key in config.deprecated: # pragma: no cover project.core.ui.warn( f"[warning]DEPRECATED:[/] the config has been renamed to {config.deprecated[options.key]}", ) del config[options.key] pdm-2.23.1/src/pdm/cli/commands/export.py000066400000000000000000000112621477560627500202160ustar00rootroot00000000000000from __future__ import annotations import argparse from pathlib import Path from typing import Iterable from pdm.cli.commands.base import BaseCommand from pdm.cli.filters import GroupSelection from pdm.cli.options import groups_group, lockfile_option from pdm.exceptions import PdmUsageError from pdm.formats import FORMATS from pdm.models.candidates import Candidate from pdm.models.requirements import Requirement from pdm.project import Project from pdm.project.lockfile import FLAG_INHERIT_METADATA class Command(BaseCommand): """Export the locked packages set to other formats""" def add_arguments(self, parser: argparse.ArgumentParser) -> None: lockfile_option.add_to_parser(parser) parser.add_argument( "-f", "--format", choices=["requirements"], default="requirements", help="Only requirements.txt is supported for now.", ) groups_group.add_to_parser(parser) parser.add_argument( "--no-hashes", "--without-hashes", dest="hashes", action="store_false", default=True, help="Don't include artifact hashes", ) parser.add_argument( "--no-markers", action="store_false", default=True, dest="markers", help="(DEPRECATED)Don't include platform markers", ) parser.add_argument( "--no-extras", action="store_false", default=True, dest="extras", help="Strip extras from the requirements" ) parser.add_argument( "-o", "--output", help="Write output to the given file, or print to stdout if not given", ) parser.add_argument( "--pyproject", action="store_true", help="Read the list of packages from pyproject.toml", ) parser.add_argument("--expandvars", action="store_true", help="Expand environment variables in requirements") group = parser.add_mutually_exclusive_group() group.add_argument("--self", action="store_true", help="Include the project itself") group.add_argument( "--editable-self", action="store_true", help="Include the project itself as an editable dependency" ) def handle(self, project: Project, options: argparse.Namespace) -> None: if options.pyproject: options.hashes = False selection = GroupSelection.from_options(project, options) if options.markers is False: project.core.ui.warn( "The --no-markers option is on, the exported requirements can only work on the current platform" ) packages: Iterable[Requirement] | Iterable[Candidate] if options.pyproject: all_deps = project._resolve_dependencies(list(selection)) packages = [r for group in selection for r in project.get_dependencies(group, all_deps)] else: if not project.lockfile.exists(): raise PdmUsageError("No lockfile found, please run `pdm lock` first.") if FLAG_INHERIT_METADATA not in project.lockfile.strategy: raise PdmUsageError( "Can't export a lock file without environment markers, please re-generate the lock file with `inherit_metadata` strategy." ) candidates = sorted( (entry.candidate for entry in project.get_locked_repository().packages.values()), key=lambda c: not c.req.extras, ) groups = set(selection) packages = [] seen_extras: set[str] = set() this_spec = project.environment.spec for candidate in candidates: if groups.isdisjoint(candidate.req.groups): continue if options.extras: key = candidate.req.key or "" if candidate.req.extras: seen_extras.add(key) elif key in seen_extras: continue elif candidate.req.extras: continue if not options.markers and candidate.req.marker: if not candidate.req.marker.matches(this_spec): continue candidate.req.marker = None packages.append(candidate) # type: ignore[arg-type] content = FORMATS[options.format].export(project, packages, options) if options.output: Path(options.output).write_text(content, encoding="utf-8") else: # Use a regular print to avoid any formatting / wrapping. print(content) pdm-2.23.1/src/pdm/cli/commands/fix/000077500000000000000000000000001477560627500171075ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/commands/fix/__init__.py000066400000000000000000000061751477560627500212310ustar00rootroot00000000000000from __future__ import annotations import argparse from pdm.cli.commands.base import BaseCommand from pdm.cli.commands.fix.fixers import BaseFixer, LockStrategyFixer, PackageTypeFixer, ProjectConfigFixer from pdm.exceptions import PdmUsageError from pdm.project import Project from pdm.termui import Emoji class Command(BaseCommand): """Fix the project problems according to the latest version of PDM""" def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument("problem", nargs="?", help="Fix the specific problem, or all if not given") parser.add_argument("--dry-run", action="store_true", help="Only show the problems") @staticmethod def find_problems(project: Project) -> list[tuple[str, BaseFixer]]: """Get the problems in the project""" problems: list[tuple[str, BaseFixer]] = [] for fixer in Command.get_fixers(project): if fixer.check(): problems.append((fixer.identifier, fixer)) return problems @staticmethod def check_problems(project: Project, strict: bool = True) -> None: """Check the problems in the project""" problems = Command.find_problems(project) if not problems: return breaking = False project.core.ui.warn("The following problems are found in your project:") for name, fixer in problems: project.core.ui.echo(f" [b]{name}[/]: {fixer.get_message()}", err=True) if fixer.breaking: breaking = True extra_option = " -g" if project.is_global else "" project.core.ui.echo( f"Run [success]pdm fix{extra_option}[/] to fix all or [success]pdm fix{extra_option} [/]" " to fix individual problem.", err=True, ) if breaking and strict: raise SystemExit(1) @staticmethod def get_fixers(project: Project) -> list[BaseFixer]: """Return a list of fixers to check, the order matters""" return [ProjectConfigFixer(project), PackageTypeFixer(project), LockStrategyFixer(project)] def handle(self, project: Project, options: argparse.Namespace) -> None: if options.dry_run: return self.check_problems(project) problems = self.find_problems(project) if options.problem: fixer = next((fixer for name, fixer in problems if name == options.problem), None) if not fixer: raise PdmUsageError( f"The problem doesn't exist: [success]{options.problem}[/], " f"possible values are {[p[0] for p in problems]}", ) project.core.ui.echo(f"Fixing [success]{fixer.identifier}[/]...", end=" ") fixer.fix() project.core.ui.echo(f"[success]{Emoji.SUCC}[/]") return if not problems: project.core.ui.echo("No problem is found, nothing to fix.") return for name, fixer in problems: project.core.ui.echo(f"Fixing [success]{name}[/]...", end=" ") fixer.fix() project.core.ui.echo(f"[success]{Emoji.SUCC}[/]") pdm-2.23.1/src/pdm/cli/commands/fix/fixers.py000066400000000000000000000104201477560627500207560ustar00rootroot00000000000000import abc import re from pdm.project import Config, Project from pdm.project.lockfile import FLAG_CROSS_PLATFORM from pdm.termui import Verbosity from pdm.utils import parse_version class BaseFixer(abc.ABC): """Base class for fixers""" # A unique identifier for the fixer identifier: str # A boolean flag to indicate if the problem is breaking breaking: bool = False def __init__(self, project: Project) -> None: self.project = project def log(self, message: str, verbosity: Verbosity = Verbosity.DETAIL) -> None: self.project.core.ui.echo(message, verbosity=verbosity) @abc.abstractmethod def get_message(self) -> str: """Return a description of the problem""" @abc.abstractmethod def fix(self) -> None: """Perform the fix""" @abc.abstractmethod def check(self) -> bool: """Check if the problem exists""" class ProjectConfigFixer(BaseFixer): """Fix the project config""" identifier = "project-config" def get_message(self) -> str: return ( "[success]python.path[/] config needs to be moved to [info].pdm-python[/] and " "[info].pdm.toml[/] needs to be renamed to [info]pdm.toml[/]" ) def _fix_gitignore(self) -> None: gitignore = self.project.root.joinpath(".gitignore") if not gitignore.exists(): return content = gitignore.read_text("utf8") if ".pdm-python" not in content: content = re.sub(r"^\.pdm\.toml$", ".pdm-python", content, flags=re.M) gitignore.write_text(content, "utf8") def fix(self) -> None: old_file = self.project.root.joinpath(".pdm.toml") config = Config(old_file).self_data if not self.project.root.joinpath(".pdm-python").exists() and config.get("python.path"): self.log("Creating .pdm-python...", verbosity=Verbosity.DETAIL) self.project.root.joinpath(".pdm-python").write_text(config["python.path"]) self.project.project_config # access the project config to move the config items self.log("Moving .pdm.toml to pdm.toml...", verbosity=Verbosity.DETAIL) old_file.unlink() self.log("Fixing .gitignore...", verbosity=Verbosity.DETAIL) self._fix_gitignore() def check(self) -> bool: return self.project.root.joinpath(".pdm.toml").exists() class PackageTypeFixer(BaseFixer): # pragma: no cover identifier = "package-type" def get_message(self) -> str: package_type = self.project.pyproject.settings["package-type"] dist = str(package_type == "library").lower() return ( rf'[success]package-type = "{package_type}"[/] has been renamed to ' rf"[info]distribution = {dist}[/] under \[tool.pdm] table" ) def check(self) -> bool: return "package-type" in self.project.pyproject.settings def fix(self) -> None: # Copy the project settings settings = self.project.pyproject.settings.copy() # Pop the package type and convert it to a distribution type package_type = settings.pop("package-type") dist = package_type == "library" settings["distribution"] = dist # Update the project settings with the new distribution type self.project.pyproject._data["tool"].pop("pdm") self.project.pyproject.settings.update(settings) # Write the updated settings back to the project self.project.pyproject.write(False) class LockStrategyFixer(BaseFixer): identifier = "deprecated-cross-platform" def get_message(self) -> str: return "Lock strategy [success]`cross_platform`[/] has been deprecated in favor of lock targets." def check(self) -> bool: lockfile_version = self.project.lockfile.file_version if not lockfile_version or parse_version(lockfile_version) < parse_version("4.5.0"): return False return FLAG_CROSS_PLATFORM in self.project.lockfile.strategy def fix(self) -> None: strategies = self.project.lockfile.strategy - {FLAG_CROSS_PLATFORM} self.project.lockfile._data["metadata"]["strategy"] = sorted(strategies) self.project.lockfile.write(False) self.log("Lock strategy [success]`cross_platform` has been removed.", verbosity=Verbosity.DETAIL) pdm-2.23.1/src/pdm/cli/commands/import_cmd.py000066400000000000000000000103351477560627500210320ustar00rootroot00000000000000from __future__ import annotations import argparse from pdm.cli.commands.base import BaseCommand from pdm.exceptions import PdmUsageError from pdm.formats import FORMATS from pdm.project import Project class Command(BaseCommand): """Import project metadata from other formats""" name = "import" def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "-d", "--dev", default=False, action="store_true", help="import packages into dev dependencies", ) parser.add_argument("-G", "--group", help="Specify the target dependency group to import into") parser.add_argument( "-f", "--format", choices=FORMATS.keys(), help="Specify the file format explicitly", ) parser.add_argument("filename", help="The file name") parser.set_defaults(search_parent=False) def handle(self, project: Project, options: argparse.Namespace) -> None: self.do_import(project, options.filename, options.format, options) @staticmethod def do_import( project: Project, filename: str, format: str | None = None, options: argparse.Namespace | None = None, reset_backend: bool = True, ) -> None: """Import project metadata from given file. :param project: the project instance :param filename: the file name :param format: the file format, or guess if not given. :param options: other options parsed to the CLI. """ import tomlkit from pdm.cli.utils import merge_dictionary from pdm.formats import FORMATS from pdm.models.backends import DEFAULT_BACKEND if not format: for key in FORMATS: if FORMATS[key].check_fingerprint(project, filename): break else: raise PdmUsageError( "Can't derive the file format automatically, please specify it via '-f/--format' option." ) else: key = format if options is None: options = argparse.Namespace(dev=False, group=None) project_data, settings = FORMATS[key].convert(project, filename, options) dependency_groups = settings.pop("dev-dependencies", {}) # type: ignore[attr-defined] pyproject = project.pyproject._data if "tool" not in pyproject or "pdm" not in pyproject["tool"]: pyproject.setdefault("tool", {})["pdm"] = tomlkit.table() if "build" in pyproject["tool"]["pdm"] and isinstance(pyproject["tool"]["pdm"]["build"], str): pyproject["tool"]["pdm"]["build"] = { "setup-script": pyproject["tool"]["pdm"]["build"], "run-setuptools": True, } if "project" not in pyproject: pyproject.add("project", tomlkit.table()) pyproject["project"].add(tomlkit.comment("PEP 621 project metadata")) pyproject["project"].add(tomlkit.comment("See https://www.python.org/dev/peps/pep-0621/")) merge_dictionary(pyproject["project"], project_data) dynamic_fields = pyproject["project"].get("dynamic", []) if "dependencies" in project_data and "dependencies" in dynamic_fields: dynamic_fields.remove("dependencies") if "optional-dependencies" in project_data and "optional-dependencies" in dynamic_fields: dynamic_fields.remove("optional-dependencies") merge_dictionary(pyproject["tool"]["pdm"], settings) if dependency_groups: merge_dictionary(pyproject.setdefault("dependency-groups", {}), dependency_groups) if reset_backend: pyproject["build-system"] = DEFAULT_BACKEND.build_system() if "requires-python" not in pyproject["project"]: python_version = f"{project.python.major}.{project.python.minor}" pyproject["project"]["requires-python"] = f">={python_version}" project.core.ui.echo( "The project's [primary]requires-python[/] has been set to [primary]>=" f"{python_version}[/]. You can change it later if necessary." ) project.pyproject.write() pdm-2.23.1/src/pdm/cli/commands/info.py000066400000000000000000000061471477560627500176360ustar00rootroot00000000000000import argparse import json from rich import print_json from pdm.cli.commands.base import BaseCommand from pdm.cli.options import ArgumentGroup, venv_option from pdm.cli.utils import check_project_file from pdm.project import Project class Command(BaseCommand): """Show the project information""" def add_arguments(self, parser: argparse.ArgumentParser) -> None: venv_option.add_to_parser(parser) group = ArgumentGroup("fields", is_mutually_exclusive=True) group.add_argument("--python", action="store_true", help="Show the interpreter path") group.add_argument( "--where", dest="where", action="store_true", help="Show the project root path", ) group.add_argument("--packages", action="store_true", help="Show the local packages root") group.add_argument("--env", action="store_true", help="Show PEP 508 environment markers") group.add_argument("--json", action="store_true", help="Dump the information in JSON") group.add_to_parser(parser) def handle(self, project: Project, options: argparse.Namespace) -> None: check_project_file(project) interpreter = project.environment.interpreter packages_path = "" if project.environment.is_local: packages_path = project.environment.packages_path # type: ignore[attr-defined] if options.python: project.core.ui.echo(str(interpreter.executable)) elif options.where: project.core.ui.echo(str(project.root)) elif options.packages: project.core.ui.echo(str(packages_path)) elif options.env: project.core.ui.echo(json.dumps(project.environment.spec.markers_with_defaults(), indent=2)) elif options.json: print_json( data={ "pdm": {"version": project.core.version}, "python": { "interpreter": str(interpreter.executable), "version": interpreter.identifier, "markers": project.environment.spec.markers_with_defaults(), }, "project": { "root": str(project.root), "pypackages": str(packages_path), }, } ) else: for name, value in zip( [ f"[primary]{key}[/]:" for key in [ "PDM version", f"{'Global ' if project.is_global else ''}Python Interpreter", f"{'Global ' if project.is_global else ''}Project Root", f"{'Global ' if project.is_global else ''}Local Packages", ] ], [ project.core.version, f"{interpreter.executable} ({interpreter.identifier})", project.root.as_posix(), str(packages_path), ], ): project.core.ui.echo(f"{name}\n {value}") pdm-2.23.1/src/pdm/cli/commands/init.py000066400000000000000000000251231477560627500176410ustar00rootroot00000000000000from __future__ import annotations import argparse from typing import TYPE_CHECKING, Any, cast from pdm import termui from pdm.cli import actions from pdm.cli.commands.base import BaseCommand from pdm.cli.hooks import HookManager from pdm.cli.options import skip_option from pdm.cli.templates import ProjectTemplate from pdm.exceptions import PdmUsageError from pdm.models.backends import _BACKENDS, DEFAULT_BACKEND, BuildBackend, get_backend from pdm.models.specifiers import get_specifier from pdm.utils import ( get_user_email_from_git, package_installed, sanitize_project_name, validate_project_name, ) if TYPE_CHECKING: from pdm.project import Project class Command(BaseCommand): """Initialize a pyproject.toml for PDM. Built-in templates: - default: `pdm init`, A simple template with a basic structure. - minimal: `pdm init minimal`, A minimal template with only `pyproject.toml`. """ def __init__(self) -> None: self.interactive = True def do_init(self, project: Project, options: argparse.Namespace) -> None: """Bootstrap the project and create a pyproject.toml""" hooks = HookManager(project, options.skip) if options.generator == "copier": self._init_copier(project, options) elif options.generator == "cookiecutter": self._init_cookiecutter(project, options) else: self.set_python(project, options.python, hooks) self._init_builtin(project, options) hooks.try_emit("post_init") def _init_copier(self, project: Project, options: argparse.Namespace) -> None: if not package_installed("copier"): raise PdmUsageError( "--copier is passed but copier is not installed. Install it by `pdm self add copier`" ) from None from copier.cli import CopierApp if not options.template: raise PdmUsageError("template argument is required when --copier is passed") _, retval = CopierApp.run( ["copier", "copy", options.template, str(project.root), *options.generator_args], exit=False ) if retval != 0: raise RuntimeError("Copier exited with non-zero status code") def _init_cookiecutter(self, project: Project, options: argparse.Namespace) -> None: if not package_installed("cookiecutter"): raise PdmUsageError( "--cookiecutter is passed but cookiecutter is not installed. Install it by `pdm self add cookiecutter`" ) from None from cookiecutter.cli import main as cookiecutter if not options.template: raise PdmUsageError("template argument is required when --cookiecutter is passed") if options.project_path: project.core.ui.warn( "Cookiecutter generator does not respect --project option. " "It will always create a project dir under the current directory", ) try: cookiecutter.main([options.template, *options.generator_args], standalone_mode=False) except SystemExit as e: raise RuntimeError("Cookiecutter exited with an error") from e def _init_builtin(self, project: Project, options: argparse.Namespace) -> None: metadata = self.get_metadata_from_input(project, options) template = options.template if not template: template = "default" if options.dist else "minimal" with ProjectTemplate(template) as template: template.generate(project.root, metadata, options.overwrite) project.pyproject.reload() def set_interactive(self, value: bool) -> None: self.interactive = value def ask(self, question: str, default: str) -> str: if not self.interactive: return default return termui.ask(question, default=default) def ask_project(self, project: Project) -> str: default = sanitize_project_name(project.root.name) name = self.ask("Project name", default) if default == name or validate_project_name(name): return name project.core.ui.echo( "Project name is not valid, it should follow PEP 426", err=True, style="warning", ) return self.ask_project(project) def get_metadata_from_input(self, project: Project, options: argparse.Namespace) -> dict[str, Any]: from pdm.formats.base import array_of_inline_tables, make_array, make_inline_table name = self.ask_project(project) version = self.ask("Project version", options.project_version or "0.1.0") is_dist = options.dist or bool(options.backend) if not is_dist and self.interactive: is_dist = termui.confirm( "Do you want to build this project for distribution(such as wheel)?\n" "If yes, it will be installed by default when running `pdm install`." ) options.dist = is_dist build_backend: type[BuildBackend] | None = None python = project.python if is_dist: description = self.ask("Project description", "") if options.backend: build_backend = get_backend(options.backend) elif self.interactive: all_backends = list(_BACKENDS) project.core.ui.echo("Which build backend to use?") for i, backend in enumerate(all_backends): project.core.ui.echo(f"{i}. [success]{backend}[/]") selected_backend = termui.ask( "Please select", prompt_type=int, choices=[str(i) for i in range(len(all_backends))], show_choices=False, default=0, ) build_backend = get_backend(all_backends[int(selected_backend)]) else: build_backend = DEFAULT_BACKEND default_python_requires = f">={python.major}.{python.minor}" else: description = "" default_python_requires = f"=={python.major}.{python.minor}.*" license = self.ask("License(SPDX name)", options.license or "MIT") git_user, git_email = get_user_email_from_git() author = self.ask("Author name", git_user) email = self.ask("Author email", git_email) python_requires = self.ask("Python requires('*' to allow any)", default_python_requires) data = { "project": { "name": name, "version": version, "authors": array_of_inline_tables([{"name": author, "email": email}]), "license": make_inline_table({"text": license}), "dependencies": make_array([], True), }, "tool": {"pdm": {"distribution": is_dist}}, } if python_requires and python_requires != "*": get_specifier(python_requires) data["project"]["requires-python"] = python_requires # type: ignore[index] if description: data["project"]["description"] = description # type: ignore[index] if build_backend is not None: data["build-system"] = cast(dict, build_backend.build_system()) return data def add_arguments(self, parser: argparse.ArgumentParser) -> None: skip_option.add_to_parser(parser) status = { False: termui.style("\\[not installed]", style="error"), True: termui.style("\\[installed]", style="success"), } generator = parser.add_mutually_exclusive_group() generator.add_argument( "--copier", action="store_const", dest="generator", const="copier", help=f"Use Copier to generate project {status[package_installed('copier')]}", ) generator.add_argument( "--cookiecutter", action="store_const", dest="generator", const="cookiecutter", help=f"Use Cookiecutter to generate project {status[package_installed('cookiecutter')]}", ) group = parser.add_argument_group("builtin generator options") group.add_argument( "-n", "--non-interactive", action="store_true", help="Don't ask questions but use default values", ) group.add_argument("--python", help="Specify the Python version/path to use") group.add_argument( "--dist", "--lib", dest="dist", action="store_true", help="Create a package for distribution" ) group.add_argument("--backend", choices=list(_BACKENDS), help="Specify the build backend, which implies --dist") group.add_argument("--license", help="Specify the license (SPDX name)") group.add_argument("--project-version", help="Specify the project's version") parser.add_argument( "template", nargs="?", help="Specify the project template, which can be a local path or a Git URL" ) parser.add_argument("generator_args", nargs=argparse.REMAINDER, help="Arguments passed to the generator") parser.add_argument("-r", "--overwrite", action="store_true", help="Overwrite existing files") parser.set_defaults(search_parent=False, generator="builtin") def set_python(self, project: Project, python: str | None, hooks: HookManager) -> None: from pdm.cli.commands.use import Command as UseCommand python_info = UseCommand().do_use( project, python or "", first=bool(python) or not self.interactive, ignore_remembered=True, ignore_requires_python=True, save=False, hooks=hooks, ) if python_info.get_venv() is None: project.core.ui.info( "You are using the PEP 582 mode, no virtualenv is created.\n" "You can change configuration with `pdm config python.use_venv True`.\n" "For more info, please visit https://peps.python.org/pep-0582/" ) project.python = python_info def handle(self, project: Project, options: argparse.Namespace) -> None: if project.pyproject.exists(): project.core.ui.echo("pyproject.toml already exists, update it now.", style="primary") else: project.core.ui.echo("Creating a pyproject.toml for PDM...", style="primary") self.set_interactive(not options.non_interactive and termui.is_interactive()) self.do_init(project, options=options) project.core.ui.echo("Project is initialized successfully", style="primary") if self.interactive: actions.ask_for_import(project) pdm-2.23.1/src/pdm/cli/commands/install.py000066400000000000000000000077671477560627500203620ustar00rootroot00000000000000import argparse import sys import sysconfig from pdm import termui from pdm.cli import actions from pdm.cli.commands.base import BaseCommand from pdm.cli.filters import GroupSelection from pdm.cli.hooks import HookManager from pdm.cli.options import ( dry_run_option, frozen_lockfile_option, groups_group, install_group, lockfile_option, override_option, skip_option, venv_option, ) from pdm.project import Project class Command(BaseCommand): """Install dependencies from lock file""" arguments = ( *BaseCommand.arguments, groups_group, install_group, override_option, dry_run_option, lockfile_option, frozen_lockfile_option, skip_option, venv_option, ) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "--check", action="store_true", help="Check if the lock file is up to date and fail otherwise", ) parser.add_argument("--plugins", action="store_true", help="Install the plugins specified in pyproject.toml") def install_plugins(self, project: Project) -> None: from pdm.environments import PythonEnvironment from pdm.installers.core import install_requirements from pdm.models.requirements import parse_line plugins = [parse_line(r) for r in project.pyproject.plugins] if not plugins: return plugin_root = project.root / ".pdm-plugins" extra_paths = list({sysconfig.get_path("purelib"), sysconfig.get_path("platlib")}) environment = PythonEnvironment( project, python=sys.executable, prefix=str(plugin_root), extra_paths=extra_paths ) with project.core.ui.open_spinner("[success]Installing plugins...[/]"): with project.core.ui.logging("install-plugins"): install_requirements( plugins, environment, clean=True, use_install_cache=project.config["install.cache"], allow_uv=False ) if not plugin_root.joinpath(".gitignore").exists(): plugin_root.mkdir(exist_ok=True) plugin_root.joinpath(".gitignore").write_text("*\n") project.core.ui.echo("Plugins are installed successfully into [primary].pdm-plugins[/].") def handle(self, project: Project, options: argparse.Namespace) -> None: if not project.pyproject.is_valid and termui.is_interactive(): actions.ask_for_import(project) if options.plugins: return self.install_plugins(project) hooks = HookManager(project, options.skip) strategy = actions.check_lockfile(project, False) selection = GroupSelection.from_options(project, options) if strategy: if options.check: project.core.ui.echo( "Please run [success]`pdm lock`[/] to update the lock file", err=True, ) sys.exit(1) if project.enable_write_lockfile: project.core.ui.echo("Updating the lock file...", style="success", err=True) # We would like to keep the selected groups when the lockfile exists # but use the groups passed-in when creating a new lockfile or doing a non-lock install. if strategy == "all" or not project.enable_write_lockfile: lock_selection = selection else: lock_selection = GroupSelection(project) actions.do_lock( project, strategy=strategy, dry_run=options.dry_run, hooks=hooks, groups=lock_selection.all(), ) actions.do_sync( project, selection=selection, no_editable=options.no_editable, no_self=options.no_self or "default" not in selection, dry_run=options.dry_run, fail_fast=options.fail_fast, hooks=hooks, ) pdm-2.23.1/src/pdm/cli/commands/list.py000066400000000000000000000412041477560627500176470ustar00rootroot00000000000000from __future__ import annotations import argparse import csv import io import json from collections import defaultdict from fnmatch import fnmatch from typing import Iterable, Mapping, Sequence from pdm.cli import actions from pdm.cli.commands.base import BaseCommand from pdm.cli.options import venv_option from pdm.cli.utils import ( DirectedGraph, PackageNode, build_dependency_graph, check_project_file, get_dist_location, normalize_pattern, show_dependency_graph, ) from pdm.compat import importlib_metadata as im from pdm.exceptions import PdmUsageError from pdm.models.requirements import Requirement from pdm.project import Project # Group label for subdependencies SUBDEP_GROUP_LABEL = ":sub" class Command(BaseCommand): """List packages installed in the current working set""" DEFAULT_FIELDS = "name,version,location" def add_arguments(self, parser: argparse.ArgumentParser) -> None: venv_option.add_to_parser(parser) graph = parser.add_mutually_exclusive_group() parser.add_argument( "--freeze", action="store_true", help="Show the installed dependencies in pip's requirements.txt format", ) graph.add_argument("--tree", "--graph", action="store_true", help="Display a tree of dependencies", dest="tree") parser.add_argument("-r", "--reverse", action="store_true", help="Reverse the dependency tree") parser.add_argument( "--resolve", action="store_true", default=False, help="Resolve all requirements to output licenses (instead of just showing those currently installed)", ) parser.add_argument( "--fields", default=Command.DEFAULT_FIELDS, help="Select information to output as a comma separated string. " f"All fields: {','.join(sorted(Listable.KEYS))}.", ) parser.add_argument( "--sort", default=None, help="Sort the output using a given field name. If nothing is " "set, no sort is applied. Multiple fields can be combined with ','.", ) list_formats = parser.add_mutually_exclusive_group() list_formats.add_argument( "--csv", action="store_true", help="Output dependencies in CSV document format", ) list_formats.add_argument( "--json", action="store_true", help="Output dependencies in JSON document format", ) list_formats.add_argument( "--markdown", action="store_true", help="Output dependencies and legal notices in markdown document format - best effort basis", ) parser.add_argument( "--include", default="", help="Dependency groups to include in the output. By default all are included", ) parser.add_argument( "--exclude", default="", help="Exclude dependency groups from the output", ) parser.add_argument( "patterns", nargs="*", help="Filter packages by patterns. e.g. pdm list requests-* flask-*. " "In --tree mode, only show the subtree of the matched packages.", type=normalize_pattern, ) @staticmethod def filter_by_patterns( packages: Mapping[str, im.Distribution], patterns: Iterable[str] ) -> Mapping[str, im.Distribution]: """Filter packages by patterns""" if not patterns: return packages return {k: v for k, v in packages.items() if any(fnmatch(k, p) for p in patterns)} def handle(self, project: Project, options: argparse.Namespace) -> None: # Raise an error if the project is not defined. check_project_file(project) # Freeze. if options.freeze: self.handle_freeze(project, options) return # Map dependency groups to requirements. name_to_groups: Mapping[str, set[str]] = defaultdict(set) all_deps = project._resolve_dependencies() for g in project.iter_groups(): for r in project.get_dependencies(g, all_deps): k = r.key or "unknown" name_to_groups[k].add(g) # Set up `--include` and `--exclude` dep groups. # Include everything by default (*) then exclude after. # Check to make sure that only valid dep group names are given. valid_groups = {*list(project.iter_groups()), SUBDEP_GROUP_LABEL} include = set(parse_comma_separated_string(options.include, lowercase=False, asterisk_values=valid_groups)) if not all(g in valid_groups for g in include): raise PdmUsageError(f"--include groups must be selected from: {valid_groups}") exclude = set(parse_comma_separated_string(options.exclude, lowercase=False, asterisk_values=valid_groups)) if exclude and not all(g in valid_groups for g in exclude): raise PdmUsageError(f"--exclude groups must be selected from: {valid_groups}") # Include selects only certain groups when set, but always selects :sub # unless it is explicitly unset. selected_groups = include if include else valid_groups selected_groups = selected_groups | {SUBDEP_GROUP_LABEL} selected_groups = selected_groups - (exclude - include) # Requirements as importtools distributions (eg packages). # Resolve all the requirements. Map the candidates to distributions. requirements = [r for g in selected_groups if g != SUBDEP_GROUP_LABEL for r in project.get_dependencies(g)] if options.resolve: candidates = actions.resolve_candidates_from_lockfile( project, requirements, groups=selected_groups - {SUBDEP_GROUP_LABEL} ) packages: Mapping[str, im.Distribution] = { k: c.prepare(project.environment).metadata for k, c in candidates.items() } # Use requirements from the working set (currently installed). else: packages = project.environment.get_working_set() selected_keys = {r.identify() for r in requirements} dep_graph = build_dependency_graph( packages, project.environment.spec, None if not (include or exclude) else selected_keys, include_sub=SUBDEP_GROUP_LABEL in selected_groups, ) # Process as a graph or list. if options.tree: self.handle_graph(dep_graph, project, options) else: selected_packages = [k.name.split("[")[0] for k in dep_graph if k] packages = self.filter_by_patterns( {k: v for k, v in packages.items() if k in selected_packages}, options.patterns ) self.handle_list(packages, name_to_groups, project, options) def handle_freeze(self, project: Project, options: argparse.Namespace) -> None: if options.tree: raise PdmUsageError("--tree cannot be used with --freeze") if options.reverse: raise PdmUsageError("--reverse cannot be used without --tree") if options.fields != Command.DEFAULT_FIELDS: raise PdmUsageError("--fields cannot be used with --freeze") if options.resolve: raise PdmUsageError("--resolve cannot be used with --freeze") if options.sort: raise PdmUsageError("--sort cannot be used with --freeze") if options.csv: raise PdmUsageError("--csv cannot be used with --freeze") if options.json: raise PdmUsageError("--json cannot be used with --freeze") if options.markdown: raise PdmUsageError("--markdown cannot be used with --freeze") if options.include or options.exclude: raise PdmUsageError("--include/--exclude cannot be used with --freeze") working_set = self.filter_by_patterns(project.environment.get_working_set(), options.patterns) requirements = sorted( (Requirement.from_dist(dist).as_line() for dist in working_set.values()), key=lambda x: x.lower(), ) project.core.ui.echo("\n".join(requirements)) def handle_graph( self, dep_graph: DirectedGraph[PackageNode | None], project: Project, options: argparse.Namespace, ) -> None: if options.csv: raise PdmUsageError("--csv cannot be used with --tree") if options.markdown: raise PdmUsageError("--markdown cannot be used with --tree") if options.sort: raise PdmUsageError("--sort cannot be used with --tree") show_dependency_graph(project, dep_graph, reverse=options.reverse, json=options.json, patterns=options.patterns) def handle_list( self, packages: Mapping[str, im.Distribution], name_to_groups: Mapping[str, set[str]], project: Project, options: argparse.Namespace, ) -> None: if options.reverse: raise PdmUsageError("--reverse cannot be used without --tree") # Check the fields are specified OK. fields = parse_comma_separated_string(options.fields, asterisk_values=Listable.KEYS) if not all(field in Listable.KEYS for field in fields): raise PdmUsageError(f"--fields must specify one or more of: {Listable.KEYS}") # Wrap each distribution with a Listable (and a groups pairing) # to make it easier to filter on later. def _group_of(name: str) -> set[str]: return name_to_groups.get(name, {SUBDEP_GROUP_LABEL}) records = [Listable(d, _group_of(k)) for k, d in packages.items()] ui = project.core.ui # Order based on a field key. keys = parse_comma_separated_string(options.sort) if options.sort else ["name"] if not all(key in Listable.KEYS for key in keys): raise PdmUsageError(f"--sort key must be one of: {','.join(Listable.KEYS)}") records.sort(key=lambda d: tuple(d[key].casefold() for key in keys)) # Write CSV if options.csv: buffer = io.StringIO() writer = csv.DictWriter(buffer, fieldnames=fields) writer.writeheader() for row in records: writer.writerow(row.json(fields)) ui.echo(buffer.getvalue(), highlight=True, end="") # Write JSON elif options.json: json_row = [row.json(fields) for row in records] ui.echo(json.dumps(json_row, indent=4), highlight=True) # Write Markdown elif options.markdown: body = [f"# {project.name} licenses"] body.extend(row.markdown(fields) for row in records) text_body = "\n".join(body) try: ui.echo(text_body, highlight=True) except UnicodeEncodeError: ui.error( "Markdown output contains non-ASCII characters. " "Setting env var PYTHONIOENCODING to 'utf8' may fix this.", ) ui.echo(text_body.encode().decode("ascii", errors="ignore"), highlight=True) ui.echo("**Problem decoding file as UTF-8. Some characters may be omit.**") # Write nice table format. else: formatted = [row.rich(fields) for row in records] ui.display_columns(formatted, fields) def parse_comma_separated_string( comma_string: str, lowercase: bool = True, asterisk_values: Iterable[str] | None = None, ) -> list[str]: """Parse a CLI comma separated string. Apply optional lowercase transformation and if the value given is "*" then return a list of pre-defined values (`asterisk_values`). """ if asterisk_values is not None and comma_string.strip() == "*": return list(asterisk_values) items = f"{comma_string}".split(",") items = [el.strip() for el in items if el] if lowercase: items = [el.lower() for el in items] return items class Listable: """Wrapper makes sorting and exporting information about a Distribution easier. It also retrieves license information from dist-info metadata. https://packaging.python.org/en/latest/specifications/core-metadata """ # Fields that users are allowed to sort on. KEYS = frozenset(["name", "groups", "version", "homepage", "licenses", "location"]) def __init__(self, dist: im.Distribution, groups: set[str]): self.dist = dist self.name = dist.metadata.get("Name") self.groups = "|".join(groups) self.version = dist.metadata.get("Version") self.version = None if self.version == "UNKNOWN" else self.version self.homepage = dist.metadata.get("Home-Page") self.homepage = None if self.homepage == "UNKNOWN" else self.homepage # If the License metadata field is empty or UNKNOWN then try to # find the license in the Trove classifiers. There may be more than one # so generate a pipe separated list (to avoid complexity with CSV export). self.licenses = dist.metadata.get("License") self.licenses = None if self.licenses == "UNKNOWN" else self.licenses # Sometimes package metadata contains the full license text. # e.g. license = { file="LICENSE" } in pyproject.toml # To identify this, check for newlines or very long strings. # 50 chars is picked because the longest OSI license (WTFPL) full name is 43 characters. is_full_text = (self.licenses and "\n" in self.licenses) or len(self.licenses or "") > 50 # If that is the case, look at the classifiers instead. if not self.licenses or is_full_text: classifier_licenses = [v for v in dist.metadata.get_all("Classifier", []) if v.startswith("License")] alternatives = [parts.split("::") for parts in classifier_licenses] alternatives = [part[-1].strip() for part in alternatives if part] self.licenses = "|".join(alternatives) @property def location(self) -> str: return get_dist_location(self.dist) def license_files(self) -> list[im.PackagePath]: """Path to files inside the package that may contain license information or other legal notices. The implementation is a "best effort" and may contain errors, select incorrect information, or otherwise be error-prone. It is not a substitute for a lawyer. """ if not self.dist.files: return [] # Inconsistency between packages means that we check in several locations # for license files. There may be 0 or more of these. There may be false # positives & negatives. locations = ("**/LICENSE*", "**/LICENCE*", "**/COPYING*", "**/NOTICE*") # Compile a list of all file paths in the distribution that look like # they might contain a license file. paths = [] for path in self.dist.files: paths += [path for loc in locations if path.match(loc)] return paths def __getitem__(self, field: str) -> str: if field not in Listable.KEYS: raise PdmUsageError(f"list field `{field}` not in: {Listable.KEYS}") return getattr(self, field) def json(self, fields: Sequence[str]) -> dict: return {f: self[f] for f in fields} def rich(self, fields: Sequence[str]) -> Sequence[str]: output = [] for field in fields: data = f"{self[field]}" data = data if field != "name" else f"[req]{data}[/]" data = data if field != "version" else f"[warning]{data}[/]" data = data if field != "groups" else f"[error]{data}[/]" output.append(data) return output def markdown(self, fields: Sequence[str]) -> str: nl = "\n" section = "" # Heading section += f"## {self.name}{nl}" section += f"{nl}" # Table section += f"| Name | {self.name} |{nl}" section += f"|----|----|{nl}" for field in fields: if field == "name": continue section += f"| {field.capitalize()} | {self[field]} |{nl}" section += f"{nl}" # Files for path in self.license_files(): section += f"{path}{nl}" section += f"{nl}{nl}" section += f"````{nl}" try: section += path.read_text("utf-8") except UnicodeDecodeError: section += "Problem decoding file as UTF-8" except Exception as err: section += f"Problem finding license text: {err}" section += f"{nl}" section += f"````{nl}" section += f"{nl}" return section pdm-2.23.1/src/pdm/cli/commands/lock.py000066400000000000000000000114031477560627500176220ustar00rootroot00000000000000import argparse import re import sys from typing import cast from pdm import termui from pdm.cli import actions from pdm.cli.commands.base import BaseCommand from pdm.cli.filters import GroupSelection from pdm.cli.hooks import HookManager from pdm.cli.options import ( config_setting_option, groups_group, lock_strategy_group, lockfile_option, no_isolation_option, override_option, skip_option, ) from pdm.models.markers import EnvSpec from pdm.models.specifiers import PySpecSet from pdm.project import Project from pdm.utils import convert_to_datetime class Command(BaseCommand): """Resolve and lock dependencies""" arguments = ( *BaseCommand.arguments, lockfile_option, no_isolation_option, config_setting_option, override_option, skip_option, groups_group, lock_strategy_group, ) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "--refresh", action="store_true", help="Refresh the content hash and file hashes in the lock file", ) parser.add_argument( "--check", action="store_true", help="Check if the lock file is up to date and quit", ) parser.add_argument( "--update-reuse", action="store_const", dest="update_strategy", default="all", const="reuse", help="Reuse pinned versions already present in lock file if possible", ) parser.add_argument( "--update-reuse-installed", action="store_const", dest="update_strategy", const="reuse-installed", help="Reuse installed packages if possible", ) parser.add_argument( "--exclude-newer", help="Exclude packages newer than the given UTC date in format `YYYY-MM-DD[THH:MM:SSZ]`", type=convert_to_datetime, ) target_group = parser.add_argument_group("Lock Target") target_group.add_argument("--python", help="The Python range to lock for. E.g. `>=3.9`, `==3.12.*`") target_group.add_argument( "--platform", help="The platform to lock for. E.g. `windows`, `linux`, `macos`, `manylinux_2_17_x86_64`. " "See docs for available choices: http://pdm-project.org/en/latest/usage/lock-targets/", ) target_group.add_argument( "--implementation", help="The Python implementation to lock for. E.g. `cpython`, `pypy`, `pyston`", ) target_group.add_argument("--append", action="store_true", help="Append the result to the current lock file") def handle(self, project: Project, options: argparse.Namespace) -> None: if options.check: strategy = actions.check_lockfile(project, False) if strategy: project.core.ui.echo( f"[error]{termui.Emoji.FAIL}[/] Lockfile is [error]out of date[/].", err=True, verbosity=termui.Verbosity.DETAIL, ) sys.exit(1) else: project.core.ui.echo( f"[success]{termui.Emoji.SUCC}[/] Lockfile is [success]up to date[/].", err=True, verbosity=termui.Verbosity.DETAIL, ) sys.exit(0) selection = GroupSelection.from_options(project, options) strategy = options.update_strategy if options.exclude_newer: strategy = "all" if strategy != options.update_strategy: project.core.ui.info("--exclude-newer is set, forcing --update-all") project.core.state.exclude_newer = options.exclude_newer env_spec: EnvSpec | None = None if any([options.python, options.platform, options.implementation]): replace_dict = {} if options.python: if re.match(r"[\d.]+", options.python): options.python = f">={options.python}" replace_dict["requires_python"] = PySpecSet(options.python) if options.platform: replace_dict["platform"] = options.platform if options.implementation: replace_dict["implementation"] = options.implementation env_spec = project.environment.allow_all_spec.replace(**replace_dict) actions.do_lock( project, refresh=options.refresh, strategy=cast(str, strategy), groups=selection.all(), strategy_change=options.strategy_change, hooks=HookManager(project, options.skip), env_spec=env_spec, append=options.append, ) pdm-2.23.1/src/pdm/cli/commands/outdated.py000066400000000000000000000131111477560627500205010ustar00rootroot00000000000000from __future__ import annotations import functools import json from concurrent.futures import ThreadPoolExecutor from dataclasses import asdict, dataclass from fnmatch import fnmatch from itertools import zip_longest from typing import TYPE_CHECKING, cast from pdm.cli.commands.base import BaseCommand from pdm.cli.utils import normalize_pattern from pdm.models.requirements import strip_extras from pdm.utils import normalize_name if TYPE_CHECKING: from argparse import ArgumentParser, Namespace from unearth import PackageFinder from pdm.project.core import Project @dataclass class ListPackage: package: str groups: list[str] installed_version: str pinned_version: str latest_version: str = "" @functools.lru_cache def _find_first_diff(a: str, b: str) -> int: a_parts = a.split(".") b_parts = b.split(".") for i, (x, y) in enumerate(zip_longest(a_parts, b_parts)): if x != y: return (len(".".join(a_parts[:i])) + 1) if i > 0 else 0 return 0 class Command(BaseCommand): """Check for outdated packages and list the latest versions on indexes.""" def add_arguments(self, parser: ArgumentParser) -> None: parser.add_argument( "--json", action="store_const", const="json", dest="format", default="table", help="Output in JSON format" ) parser.add_argument("--include-sub", action="store_true", help="Include sub-dependencies") parser.add_argument("patterns", nargs="*", help="The packages to check", type=normalize_pattern) @staticmethod def _match_pattern(name: str, patterns: list[str]) -> bool: return not patterns or any(fnmatch(name, p) for p in patterns) @staticmethod def _populate_latest_version(finder: PackageFinder, package: ListPackage) -> None: best = finder.find_best_match(package.package).best if best: package.latest_version = best.version or "" @staticmethod def _format_json(packages: list[ListPackage]) -> str: return json.dumps([asdict(package) for package in packages], indent=2) @staticmethod def _render_version(version: str, base_version: str) -> str: from packaging.version import InvalidVersion from pdm.utils import parse_version if not version or version == base_version: return version if not base_version: return f"[bold red]{version}[/]" try: parsed_version = parse_version(version) parsed_base_version = parse_version(base_version) except InvalidVersion: return version first_diff = _find_first_diff(version, base_version) head, tail = version[:first_diff], version[first_diff:] if parsed_version.major != parsed_base_version.major: return f"{head}[bold red]{tail}[/]" if parsed_version.minor != parsed_base_version.minor: return f"{head}[bold yellow]{tail}[/]" return f"{head}[bold green]{tail}[/]" def handle(self, project: Project, options: Namespace) -> None: environment = project.environment installed = environment.get_working_set() resolved = {strip_extras(k)[0]: v for k, v in project.get_locked_repository().candidates.items()} collected: list[ListPackage] = [] project_dependencies: dict[str, list[str]] = {} for group, dependencies in project.all_dependencies.items(): for dep in dependencies: project_dependencies.setdefault(cast(str, dep.key), []).append(group) for name, distribution in installed.items(): if not self._match_pattern(name, options.patterns): continue if project.name and name == normalize_name(project.name): continue constrained_version = resolved.pop(name).version or "" if name in resolved else "" if not options.include_sub and name not in project_dependencies: continue groups = project_dependencies.get(name, []) collected.append(ListPackage(name, groups, distribution.version or "", constrained_version)) for name, candidate in resolved.items(): if not self._match_pattern(name, options.patterns): continue if candidate.req.marker and not candidate.req.marker.matches(environment.spec): continue if not options.include_sub and name not in project_dependencies: continue groups = project_dependencies.get(name, []) collected.append(ListPackage(name, groups, "", candidate.version or "")) with environment.get_finder() as finder, ThreadPoolExecutor() as executor: for package in collected: executor.submit(self._populate_latest_version, finder, package) collected = sorted( [p for p in collected if p.latest_version and p.latest_version != p.installed_version], key=lambda p: p.package, ) if options.format == "json": print(self._format_json(collected)) else: rows = [ ( f"[bold]{package.package}[/]", ", ".join(package.groups), package.installed_version, self._render_version(package.pinned_version, package.installed_version), self._render_version(package.latest_version, package.installed_version), ) for package in collected ] project.core.ui.display_columns(rows, header=["Package", "Groups", "Installed", "Pinned", "Latest"]) pdm-2.23.1/src/pdm/cli/commands/publish/000077500000000000000000000000001477560627500177675ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/commands/publish/__init__.py000066400000000000000000000176511477560627500221120ustar00rootroot00000000000000from __future__ import annotations import argparse import os from typing import TYPE_CHECKING from pdm.cli.commands import build from pdm.cli.commands.base import BaseCommand from pdm.cli.commands.publish.package import PackageFile from pdm.cli.commands.publish.repository import Repository from pdm.cli.hooks import HookManager from pdm.cli.options import project_option, skip_option, verbose_option from pdm.exceptions import PdmUsageError, PublishError from pdm.termui import logger if TYPE_CHECKING: from httpx import Response from pdm.project import Project class Command(BaseCommand): """Build and publish the project to PyPI""" arguments = (verbose_option, project_option, skip_option) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "-r", "--repository", help="The repository name or url to publish the package to [env var: PDM_PUBLISH_REPO]", ) parser.add_argument( "-u", "--username", help="The username to access the repository [env var: PDM_PUBLISH_USERNAME]", ) parser.add_argument( "-P", "--password", help="The password to access the repository [env var: PDM_PUBLISH_PASSWORD]", ) parser.add_argument( "-S", "--sign", action="store_true", help="Upload the package with PGP signature", ) parser.add_argument( "-i", "--identity", help="GPG identity used to sign files.", ) parser.add_argument( "-c", "--comment", help="The comment to include with the distribution file.", ) parser.add_argument( "--no-build", action="store_false", dest="build", help="Don't build the package before publishing", ) parser.add_argument( "-d", "--dest", help="The directory to upload the package from", default="dist", ) parser.add_argument( "--skip-existing", action="store_true", help="Skip uploading files that already exist. This may not work with some repository implementations.", ) group = parser.add_mutually_exclusive_group() group.add_argument( "--no-very-ssl", action="store_false", dest="verify_ssl", help="Disable SSL verification", default=None ) group.add_argument( "--ca-certs", dest="ca_certs", help="The path to a PEM-encoded Certificate Authority bundle to use" " for publish server validation [env var: PDM_PUBLISH_CA_CERTS]", ) @staticmethod def _make_package(filename: str, signatures: dict[str, str], options: argparse.Namespace) -> PackageFile: p = PackageFile.from_filename(filename, options.comment) if p.base_filename in signatures: p.add_gpg_signature(signatures[p.base_filename], p.base_filename + ".asc") elif options.sign: p.sign(options.identity) return p @staticmethod def _skip_upload(response: Response) -> bool: status = response.status_code reason = response.reason_phrase.lower() text = response.text.lower() # Borrowed from https://github.com/pypa/twine/blob/main/twine/commands/upload.py#L149 return ( # pypiserver (https://pypi.org/project/pypiserver) status == 409 # PyPI / TestPyPI / GCP Artifact Registry or (status == 400 and any("already exist" in x for x in [reason, text])) # Nexus Repository OSS (https://www.sonatype.com/nexus-repository-oss) or (status == 400 and any("updating asset" in x for x in [reason, text])) # Artifactory (https://jfrog.com/artifactory/) or (status == 403 and "overwrite artifact" in text) # Gitlab Enterprise Edition (https://about.gitlab.com) or (status == 400 and "already been taken" in text) ) @staticmethod def _check_response(response: Response) -> None: import httpx message = "" if response.status_code == 410 and "pypi.python.org" in str(response.url): message = ( "Uploading to these sites is deprecated. " "Try using https://upload.pypi.org/legacy/ " "(or https://test.pypi.org/legacy/) instead." ) elif response.status_code == 405 and "pypi.org" in str(response.url): message = "It appears you're trying to upload to pypi.org but have an invalid URL." else: try: response.raise_for_status() except httpx.HTTPStatusError as err: message = str(err) if response.text: logger.debug(response.text) if message: raise PublishError(message) @staticmethod def get_repository(project: Project, options: argparse.Namespace) -> Repository: repository = options.repository or os.getenv("PDM_PUBLISH_REPO", "pypi") username = options.username or os.getenv("PDM_PUBLISH_USERNAME") password = options.password or os.getenv("PDM_PUBLISH_PASSWORD") ca_certs = options.ca_certs or os.getenv("PDM_PUBLISH_CA_CERTS") config = project.project_config.get_repository_config(repository, "repository") if config is None: config = project.global_config.get_repository_config(repository, "repository") if config is None: raise PdmUsageError(f"Missing repository config of {repository}") else: global_config = project.global_config.get_repository_config(repository, "repository") if global_config is not None: config.passive_update(global_config) assert config.url is not None if username is not None: config.username = username if password is not None: config.password = password if ca_certs is not None: config.ca_certs = ca_certs if options.verify_ssl is False: config.verify_ssl = options.verify_ssl config.populate_keyring_auth() return Repository(project, config) def handle(self, project: Project, options: argparse.Namespace) -> None: hooks = HookManager(project, options.skip) hooks.try_emit("pre_publish") if options.build: build.Command.do_build(project, dest=options.dest, hooks=hooks) upload_dir = project.root.joinpath(options.dest) package_files = [str(p) for p in upload_dir.iterdir() if not p.name.endswith(".asc")] signatures = {p.stem: str(p) for p in upload_dir.iterdir() if p.name.endswith(".asc")} repository = self.get_repository(project, options) uploaded: list[PackageFile] = [] with project.core.ui.logging("publish"): packages = sorted( (self._make_package(p, signatures, options) for p in package_files), # Upload wheels first if they exist. key=lambda p: not p.base_filename.endswith(".whl"), ) for package in packages: resp = repository.upload(package) logger.debug("Response from %s:\n%s %s", resp.url, resp.status_code, resp.reason_phrase) if options.skip_existing and self._skip_upload(resp): project.core.ui.warn(f"Skipping {package.base_filename} because it appears to already exist") continue self._check_response(resp) uploaded.append(package) release_urls = repository.get_release_urls(uploaded) if release_urls: project.core.ui.echo("\n[success]View at:") for url in release_urls: project.core.ui.echo(url) hooks.try_emit("post_publish") pdm-2.23.1/src/pdm/cli/commands/publish/package.py000066400000000000000000000201131477560627500217310ustar00rootroot00000000000000from __future__ import annotations import email import email.message import email.policy import hashlib import io import os import re import subprocess from dataclasses import dataclass from typing import IO, Any, cast from pdm.exceptions import PdmUsageError, ProjectError from pdm.termui import logger DIST_EXTENSIONS = { ".whl": "bdist_wheel", ".tar.bz2": "sdist", ".tar.gz": "sdist", ".zip": "sdist", } wheel_file_re = re.compile( r"""^(?P(?P.+?)(-(?P\d.+?))?) ((-(?P\d.*?))?-(?P.+?)-(?P.+?)-(?P.+?) \.whl|\.dist-info)$""", re.VERBOSE, ) UTF8_POLICY = email.policy.EmailPolicy(utf8=True) def parse_metadata(fp: IO[bytes]) -> email.message.Message: """ Note that this function will close fp. See https://github.com/python/cpython/issues/65562. """ with io.TextIOWrapper(fp, encoding="utf-8", errors="surrogateescape") as file: return email.message_from_file(file, policy=UTF8_POLICY) # type: ignore[arg-type] @dataclass class PackageFile: """A distribution file for upload. XXX: currently only supports sdist and wheel. """ filename: str metadata: email.message.Message comment: str | None py_version: str | None filetype: str def __post_init__(self) -> None: self.base_filename = os.path.basename(self.filename) self.gpg_signature: tuple[str, bytes] | None = None def get_hashes(self) -> dict[str, str]: hashers = {"sha256_digest": hashlib.sha256()} try: hashers["md5_digest"] = hashlib.md5() except ValueError: pass try: hashers["blake2_256_digest"] = hashlib.blake2b(digest_size=256 // 8) # type: ignore[assignment] except (TypeError, ValueError): pass with open(self.filename, "rb") as f: for chunk in iter(lambda: f.read(8192), b""): for hasher in hashers.values(): hasher.update(chunk) return {k: v.hexdigest() for k, v in hashers.items()} @classmethod def from_filename(cls, filename: str, comment: str | None) -> PackageFile: filetype = "" for ext, dtype in DIST_EXTENSIONS.items(): if filename.endswith(ext): filetype = dtype break else: raise PdmUsageError(f"Unknown distribution file type: {filename}") if filetype == "bdist_wheel": metadata = cls.read_metadata_from_wheel(filename) match = wheel_file_re.match(os.path.basename(filename)) if match is None: py_ver = "any" else: py_ver = match.group("pyver") elif filename.endswith(".zip"): metadata = cls.read_metadata_from_zip(filename) py_ver = "source" else: metadata = cls.read_metadata_from_tar(filename) py_ver = "source" return cls(filename, metadata, comment, py_ver, filetype) @staticmethod def read_metadata_from_tar(filename: str) -> email.message.Message: import tarfile from unearth.preparer import has_leading_dir, split_leading_dir if filename.endswith(".gz"): mode = "r:gz" elif filename.endswith(".bz2"): mode = "r:bz2" else: logger.warning(f"Can't determine the compression mode for {filename}") mode = "r:*" with tarfile.open(filename, mode) as tar: # type: ignore[call-overload] members = tar.getmembers() has_leading = has_leading_dir(m.name for m in members) for m in members: fn = split_leading_dir(m.name)[1] if has_leading else m.name if fn == "PKG-INFO": return parse_metadata(cast(IO[bytes], tar.extractfile(m))) raise ProjectError(f"No PKG-INFO found in {filename}") @staticmethod def read_metadata_from_zip(filename: str) -> email.message.Message: import zipfile from unearth.preparer import has_leading_dir, split_leading_dir with zipfile.ZipFile(filename, allowZip64=True) as zip: filenames = zip.namelist() has_leading = has_leading_dir(filenames) for name in filenames: fn = split_leading_dir(name)[1] if has_leading else name if fn == "PKG-INFO": return parse_metadata(zip.open(name)) raise ProjectError(f"No PKG-INFO found in {filename}") @staticmethod def read_metadata_from_wheel(filename: str) -> email.message.Message: import zipfile with zipfile.ZipFile(filename, allowZip64=True) as zip: for fn in zip.namelist(): if fn.replace("\\", "/").endswith(".dist-info/METADATA"): return parse_metadata(zip.open(fn)) raise ProjectError(f"No egg-info is found in {filename}") def add_gpg_signature(self, filename: str, signature_name: str) -> None: if self.gpg_signature is not None: raise PdmUsageError("GPG signature already added") with open(filename, "rb") as f: self.gpg_signature = (signature_name, f.read()) def sign(self, identity: str | None) -> None: logger.info("Signing %s with gpg", self.base_filename) gpg_args = ["gpg", "--detach-sign"] if identity is not None: gpg_args.extend(["--local-user", identity]) gpg_args.extend(["-a", self.filename]) self._run_gpg(gpg_args) self.add_gpg_signature(self.filename + ".asc", self.base_filename + ".asc") @staticmethod def _run_gpg(gpg_args: list[str]) -> None: try: subprocess.run(gpg_args, check=True) return except FileNotFoundError: logger.warning("gpg executable not available. Attempting fallback to gpg2.") gpg_args[0] = "gpg2" try: subprocess.run(gpg_args, check=True) except FileNotFoundError: raise PdmUsageError( "'gpg' or 'gpg2' executables not available.\n" "Try installing one of these or specifying an executable " "with the --sign-with flag." ) from None @property def metadata_dict(self) -> dict[str, Any]: meta = self.metadata data = { # identify release "name": meta["Name"], "version": meta["Version"], # file content "filetype": self.filetype, "pyversion": self.py_version, # additional meta-data "metadata_version": meta["Metadata-Version"], "summary": meta["Summary"], "home_page": meta["Home-page"], "author": meta["Author"], "author_email": meta["Author-email"], "maintainer": meta["Maintainer"], "maintainer_email": meta["Maintainer-email"], "license": meta["License"], "description": meta.get_payload(), "keywords": meta["Keywords"], "platform": meta.get_all("Platform") or (), "classifiers": meta.get_all("Classifier") or [], "download_url": meta["Download-URL"], "supported_platform": meta.get_all("Supported-Platform") or (), "comment": self.comment, # Metadata 1.2 "project_urls": meta.get_all("Project-URL") or (), "provides_dist": meta.get_all("Provides-Dist") or (), "obsoletes_dist": meta.get_all("Obsoletes-Dist") or (), "requires_dist": meta.get_all("Requires-Dist") or (), "requires_external": meta.get_all("Requires-External") or (), "requires_python": meta.get_all("Requires-Python") or (), # Metadata 2.1 "provides_extras": meta.get_all("Provides-Extra") or (), "description_content_type": meta.get("Description-Content-Type"), # Metadata 2.2 "dynamic": meta.get_all("Dynamic") or (), # Hashes **self.get_hashes(), } if self.gpg_signature is not None: data["gpg_signature"] = self.gpg_signature return data pdm-2.23.1/src/pdm/cli/commands/publish/repository.py000066400000000000000000000163751477560627500225740ustar00rootroot00000000000000from __future__ import annotations import os from typing import TYPE_CHECKING, Any, Iterable, cast from urllib.parse import urlparse, urlunparse import httpx from id import AmbientCredentialError, detect_credential from rich.progress import ( BarColumn, DownloadColumn, TimeRemainingColumn, TransferSpeedColumn, ) from pdm import termui from pdm.cli.commands.publish.package import PackageFile from pdm.exceptions import PdmUsageError from pdm.project import Project from pdm.project.config import DEFAULT_REPOSITORIES if TYPE_CHECKING: from typing import Callable, Self from httpx import Response from httpx._multipart import MultipartStream from pdm._types import RepositoryConfig class CallbackWrapperStream(httpx.SyncByteStream): def __init__(self, stream: httpx.SyncByteStream, callback: Callable[[Self], Any]) -> None: self._stream = stream self._callback = callback self.bytes_read = 0 def __iter__(self) -> Iterable[bytes]: for chunk in self._stream: self.bytes_read += len(chunk) self._callback(self) yield chunk class Repository: def __init__(self, project: Project, config: RepositoryConfig) -> None: self.url = cast(str, config.url) self.session = project.environment._build_session([config]) self._credentials_to_save: tuple[str, str, str] | None = None self.ui = project.core.ui username, password = self._ensure_credentials(config.username, config.password) self.session.auth = (username, password) def _ensure_credentials(self, username: str | None, password: str | None) -> tuple[str, str]: from pdm.models.auth import keyring parsed_url = urlparse(self.url) netloc = parsed_url.netloc if username and password: return username, password if password: return "__token__", password if parsed_url.username is not None and parsed_url.password is not None: return parsed_url.username, parsed_url.password if keyring.enabled: auth = keyring.get_auth_info(self.url, username) if auth is not None: return auth token = self._get_pypi_token_via_oidc() if token is not None: return "__token__", token if not termui.is_interactive(): raise PdmUsageError("Username and password are required") username, password, save = self._prompt_for_credentials(netloc, username) if save and keyring.enabled and termui.confirm("Save credentials to keyring?"): self._credentials_to_save = (netloc, username, password) return username, password def _get_pypi_token_via_oidc(self) -> str | None: self.ui.echo("Getting PyPI token via OIDC...") try: parsed_url = urlparse(self.url) audience_url = urlunparse(parsed_url._replace(path="/_/oidc/audience")) resp = self.session.get(audience_url) resp.raise_for_status() audience = cast(str, resp.json()["audience"]) oidc_token = detect_credential(audience) if oidc_token is None: self.ui.echo( "This platform is not supported for trusted publishing via OIDC", err=True, ) return None mint_token_url = urlunparse(parsed_url._replace(path="/_/oidc/mint-token")) resp = self.session.post(mint_token_url, json={"token": oidc_token}) resp.raise_for_status() token = resp.json()["token"] except AmbientCredentialError as e: self.ui.echo(f"Unable to detect OIDC token for CI platform: {e}", err=True) return None except httpx.HTTPError: self.ui.echo("Failed to get PyPI token via OIDC", err=True) return None else: if os.getenv("GITHUB_ACTIONS"): # tell GitHub Actions to mask the token in any console logs print(f"::add-mask::{token}") return token def _prompt_for_credentials(self, service: str, username: str | None) -> tuple[str, str, bool]: from pdm.models.auth import keyring if keyring.enabled: cred = keyring.get_auth_info(service, username) if cred is not None: return cred[0], cred[1], False if username is None: username = termui.ask("[primary]Username") password = termui.ask("[primary]Password", password=True) return username, password, True def _save_credentials(self, service: str, username: str, password: str) -> None: from pdm.models.auth import keyring self.ui.echo("Saving credentials to keyring") keyring.save_auth_info(service, username, password) @staticmethod def _convert_to_list_of_tuples(data: dict[str, Any]) -> list[tuple[str, Any]]: result: list[tuple[str, Any]] = [] for key, value in data.items(): if isinstance(value, (list, tuple)) and key != "gpg_signature": for item in value: result.append((key, item)) else: result.append((key, value)) return result def get_release_urls(self, packages: list[PackageFile]) -> Iterable[str]: if self.url.startswith(DEFAULT_REPOSITORIES["pypi"].rstrip("/")): base = "https://pypi.org/" elif self.url.startswith(DEFAULT_REPOSITORIES["testpypi"].rstrip("/")): base = "https://test.pypi.org/" else: return set() return {f"{base}project/{package.metadata['name']}/{package.metadata['version']}/" for package in packages} def upload(self, package: PackageFile) -> Response: data_fields = package.metadata_dict data_fields.update( { ":action": "file_upload", "protocol_version": "1", } ) with self.ui.make_progress( " [progress.percentage]{task.percentage:>3.0f}%", BarColumn(), DownloadColumn(), "•", TimeRemainingColumn( compact=True, elapsed_when_finished=True, ), "•", TransferSpeedColumn(), ) as progress: progress.console.print(f"Uploading [success]{package.base_filename}") with open(package.filename, "rb") as fp: file_fields = [("content", (package.base_filename, fp, "application/octet-stream"))] def on_upload(monitor: CallbackWrapperStream) -> None: progress.update(job, completed=monitor.bytes_read) request = self.session.build_request("POST", self.url, data=data_fields, files=file_fields) stream = cast("MultipartStream", request.stream) request.stream = CallbackWrapperStream(stream, on_upload) job = progress.add_task("", total=stream.get_content_length()) resp = self.session.send(request, follow_redirects=False) if not resp.is_error and self._credentials_to_save is not None: self._save_credentials(*self._credentials_to_save) self._credentials_to_save = None return resp pdm-2.23.1/src/pdm/cli/commands/python.py000066400000000000000000000247701477560627500202260ustar00rootroot00000000000000from __future__ import annotations import os import shutil import sys import tempfile from argparse import ArgumentParser from pathlib import Path from typing import TYPE_CHECKING, cast from pdm.cli.commands.base import BaseCommand from pdm.cli.options import verbose_option from pdm.environments import BareEnvironment from pdm.exceptions import InstallationError, PdmArgumentError from pdm.models.python import PythonInfo from pdm.termui import Verbosity from pdm.utils import get_all_installable_python_versions if TYPE_CHECKING: from argparse import ArgumentParser, Namespace, _SubParsersAction from typing import Any from pdm.project.core import Project class Command(BaseCommand): """Manage installed Python interpreters""" arguments = () def add_arguments(self, parser: ArgumentParser) -> None: self.parser = parser subparsers = parser.add_subparsers(title="commands", metavar="") ListCommand.register_to(subparsers, name="list") RemoveCommand.register_to(subparsers, name="remove") InstallCommand.register_to(subparsers, name="install") LinkCommand.register_to(subparsers, name="link") FindCommand.register_to(subparsers, name="find") @classmethod def register_to(cls, subparsers: _SubParsersAction, name: str | None = None, **kwargs: Any) -> None: return super().register_to(subparsers, name, aliases=["py"], **kwargs) def handle(self, project: Project, options: Namespace) -> None: self.parser.print_help() class ListCommand(BaseCommand): """List all Python interpreters installed with PDM""" arguments = (verbose_option,) def handle(self, project: Project, options: Namespace) -> None: from findpython.providers.rye import RyeProvider ui = project.core.ui provider = RyeProvider(root=Path(project.config["python.install_root"]).expanduser()) for version in provider.find_pythons(): ui.echo(f"[success]{version.implementation.lower()}@{version.version}[/] ({version.executable})") class RemoveCommand(BaseCommand): """Remove a Python interpreter installed with PDM""" arguments = (verbose_option,) def add_arguments(self, parser: ArgumentParser) -> None: parser.add_argument("version", help="The Python version to remove. E.g. cpython@3.10.3") def handle(self, project: Project, options: Namespace) -> None: ui = project.core.ui root = Path(project.config["python.install_root"]).expanduser() if not root.exists(): ui.error(f"No Python interpreter found for {options.version!r}") sys.exit(1) version = str(options.version) if root.joinpath(version).exists(): version_dir = root.joinpath(version) else: version = options.version.lower() if "@" not in version: # pragma: no cover version = f"cpython@{version}" version_dir = root.joinpath(version) if not version_dir.exists(): ui.error(f"No Python interpreter found for {options.version!r}") ui.echo("Installed Pythons:", err=True) for child in root.iterdir(): ui.echo(f" {child.name}", err=True) sys.exit(1) if version_dir.is_symlink(): version_dir.unlink() else: shutil.rmtree(version_dir, ignore_errors=True) ui.echo(f"[success]Removed installed[/] {options.version}", verbosity=Verbosity.NORMAL) class InstallCommand(BaseCommand): """Install a Python interpreter with PDM""" arguments = (verbose_option,) def add_arguments(self, parser: ArgumentParser) -> None: parser.add_argument( "version", help="The Python version to install (e.g. cpython@3.10.3). If left empty, " "highest cPython version that matches this platform/arch is installed. " "If pyproject.toml with requires-python is available, this is considered as well.", nargs="?", ) parser.add_argument("--list", "-l", action="store_true", help="List all available Python versions") parser.add_argument( "--min", action="store_true", help="Use minimum instead of highest version for installation if `version` is left empty", ) def handle(self, project: Project, options: Namespace) -> None: if options.list: for version in get_all_installable_python_versions(build_dir=False): project.core.ui.echo(str(version)) return version = options.version if version is None: match = project.get_best_matching_cpython_version(options.min) if match is not None: version = str(match) if version is None: raise PdmArgumentError("Please specify a Python version to be installed. E.g. cpython@3.10.3") self.install_python(project, version) @staticmethod def install_python(project: Project, request: str) -> PythonInfo: from pbs_installer import download, get_download_link, install_file from pbs_installer._install import THIS_ARCH from pdm.termui import logger ui = project.core.ui root = Path(project.config["python.install_root"]).expanduser() implementation, _, version = request.rpartition("@") implementation = implementation.lower() or "cpython" version, _, arch = version.partition("-") arch = "x86" if arch == "32" else (arch or THIS_ARCH) ver, python_file = get_download_link(version, implementation=implementation, arch=arch, build_dir=False) ver_str = f"{ver}{'t' if request.endswith('t') else ''}" with ui.open_spinner(f"Downloading [success]{ver_str}[/]") as spinner: destination = root / ver_str logger.debug("Installing %s to %s", ver_str, destination) env = BareEnvironment(project) install_root = destination if install_root.joinpath("install").exists(): install_root = install_root.joinpath("install") interpreter = install_root / "bin" / "python3" if sys.platform != "win32" else destination / "python.exe" if not destination.exists() or not interpreter.exists(): shutil.rmtree(destination, ignore_errors=True) destination.mkdir(parents=True, exist_ok=True) with tempfile.NamedTemporaryFile() as tf: tf.close() original_filename = download(python_file, tf.name, env.session) spinner.update(f"Installing [success]{ver_str}[/]") try: install_file(tf.name, destination, original_filename) except ModuleNotFoundError as e: if "zstandard is required" in str(e): raise InstallationError( "zstandard is required to install this Python version. " "Please install it with `pdm self add zstandard`." ) from None if destination.joinpath("install").exists(): install_root = destination.joinpath("install") interpreter = install_root / "bin" / "python3" if sys.platform != "win32" else install_root / "python.exe" if not interpreter.exists(): raise InstallationError("Installation failed, please try again.") python_info = PythonInfo.from_path(interpreter) ui.echo( f"[success]Successfully installed[/] {python_info.implementation}@{python_info.version}", verbosity=Verbosity.NORMAL, ) ui.echo(f"[info]Version:[/] {python_info.version}", verbosity=Verbosity.NORMAL) ui.echo(f"[info]Executable:[/] {python_info.path}", verbosity=Verbosity.NORMAL) return python_info class LinkCommand(BaseCommand): """Link an external Python interpreter to PDM""" arguments = (verbose_option,) def add_arguments(self, parser: ArgumentParser) -> None: parser.add_argument("interpreter", help="The path to the Python interpreter to link") parser.add_argument("--name", help="The name of the link") def handle(self, project: Project, options: Namespace) -> None: python_info = PythonInfo.from_path(options.interpreter) if not python_info.valid: raise PdmArgumentError("Invalid Python interpreter") if options.name is None: link_name = f"{python_info.implementation}@{python_info.identifier}" else: link_name = cast(str, options.name) link_path = Path(project.config["python.install_root"]).expanduser() / link_name if link_path.exists(): raise PdmArgumentError(f"Link {link_name} already exists") exe_dir = python_info.path.parent if exe_dir.name in ("Scripts", "bin"): exe_dir = exe_dir.parent link_path.parent.mkdir(parents=True, exist_ok=True) link_path.symlink_to(exe_dir) project.core.ui.echo(f"[success]Successfully linked {link_name} to {exe_dir}[/]") class FindCommand(BaseCommand): """Search for a Python interpreter""" arguments = (verbose_option,) def add_arguments(self, parser: ArgumentParser) -> None: parser.add_argument("--managed", action="store_true", help="Only find interpreters managed by PDM") parser.add_argument("request", help="The Python version to find. E.g. 3.12, cpython@3.13") def handle(self, project: Project, options: Namespace) -> None: from findpython import Finder if options.managed: old_rye_root = os.getenv("RYE_PY_ROOT") os.environ["RYE_PY_ROOT"] = os.path.expanduser(project.config["python.install_root"]) try: finder = Finder(resolve_symlinks=True, selected_providers=["rye"]) finally: if old_rye_root: # pragma: no cover os.environ["RYE_PY_ROOT"] = old_rye_root else: del os.environ["RYE_PY_ROOT"] else: finder = project._get_python_finder() python_version = finder.find(options.request) location = ( "managed installations" if options.managed else "virtual environments, managed installations, or search paths" ) if python_version is None: project.core.ui.error(f"Python interpreter {options.request!r} not found in {location}") raise SystemExit(1) print(python_version.executable) pdm-2.23.1/src/pdm/cli/commands/remove.py000066400000000000000000000113101477560627500201640ustar00rootroot00000000000000from __future__ import annotations import argparse from typing import TYPE_CHECKING from pdm.cli.commands.base import BaseCommand from pdm.cli.filters import GroupSelection from pdm.cli.hooks import HookManager from pdm.cli.options import ( dry_run_option, frozen_lockfile_option, install_group, lockfile_option, override_option, skip_option, venv_option, ) from pdm.exceptions import PdmUsageError, ProjectError from pdm.utils import normalize_name if TYPE_CHECKING: from typing import Collection from pdm.project import Project class Command(BaseCommand): """Remove packages from pyproject.toml""" arguments = ( *BaseCommand.arguments, install_group, dry_run_option, lockfile_option, override_option, frozen_lockfile_option, skip_option, venv_option, ) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "-d", "--dev", default=False, action="store_true", help="Remove packages from dev dependencies", ) parser.add_argument("-G", "--group", help="Specify the target dependency group to remove from") parser.add_argument( "--no-sync", dest="sync", default=True, action="store_false", help="Only write pyproject.toml and do not uninstall packages", ) parser.add_argument("packages", nargs="+", help="Specify the packages to remove") def handle(self, project: Project, options: argparse.Namespace) -> None: self.do_remove( project, selection=GroupSelection.from_options(project, options), sync=options.sync, packages=options.packages, no_editable=options.no_editable, no_self=options.no_self, dry_run=options.dry_run, fail_fast=options.fail_fast, hooks=HookManager(project, options.skip), ) @staticmethod def do_remove( project: Project, selection: GroupSelection, sync: bool = True, packages: Collection[str] = (), no_editable: bool = False, no_self: bool = False, dry_run: bool = False, fail_fast: bool = False, hooks: HookManager | None = None, ) -> None: """Remove packages from working set and pyproject.toml""" from pdm.cli.actions import do_lock, do_sync from pdm.cli.utils import check_project_file from pdm.models.requirements import parse_requirement from pdm.utils import cd hooks = hooks or HookManager(project) check_project_file(project) if not packages: raise PdmUsageError("Must specify at least one package to remove.") group = selection.one() lock_groups = project.lockfile.groups deps, setter = project.use_pyproject_dependencies(group, selection.dev or False) project.core.ui.echo( f"Removing {'[bold]global[/] ' if project.is_global else ''}packages from [primary]{group}[/] " f"{'dev-' if selection.dev else ''}dependencies: " + ", ".join(f"[req]{name}[/]" for name in packages) ) tracked_names: set[str] = set() with cd(project.root): for name in packages: req = parse_requirement(name) matched_indexes = sorted((i for i, r in enumerate(deps) if req.matches(r)), reverse=True) if not matched_indexes: raise ProjectError(f"[req]{name}[/] does not exist in [primary]{group}[/] dependencies.") for i in matched_indexes: del deps[i] tracked_names.add(normalize_name(name)) setter(deps) if not dry_run: project.pyproject.write() if lock_groups and group not in lock_groups: project.core.ui.warn(f"Group [success]{group}[/] isn't in lockfile, skipping lock.") return # It may remove the whole group, exclude it from lock groups first project_groups = project.iter_groups() if lock_groups is not None: lock_groups = [g for g in lock_groups if g in project_groups] do_lock(project, "reuse", dry_run=dry_run, tracked_names=tracked_names, hooks=hooks, groups=lock_groups) if sync: do_sync( project, selection=GroupSelection(project, default=False, groups=[group], exclude_non_existing=True), clean=True, no_editable=no_editable, no_self=no_self, dry_run=dry_run, fail_fast=fail_fast, hooks=hooks, ) pdm-2.23.1/src/pdm/cli/commands/run.py000066400000000000000000000523171477560627500175070ustar00rootroot00000000000000from __future__ import annotations import argparse import itertools import os import re import shlex import signal import subprocess import sys from pathlib import Path from typing import TYPE_CHECKING, Mapping, NamedTuple, Sequence, cast from rich import print_json from pdm import termui from pdm.cli.commands.base import BaseCommand from pdm.cli.hooks import HookManager from pdm.cli.options import skip_option, venv_option from pdm.cli.utils import check_project_file from pdm.exceptions import PdmUsageError from pdm.signals import pdm_signals from pdm.utils import deprecation_warning, expand_env_vars, is_path_relative_to if TYPE_CHECKING: from types import FrameType from typing import Any, Callable, Iterator, TypedDict from pdm.environments import BaseEnvironment from pdm.project import Project class EnvFileOptions(TypedDict, total=True): override: str class TaskOptions(TypedDict, total=False): env: Mapping[str, str] env_file: EnvFileOptions | str | None help: str keep_going: bool site_packages: bool working_dir: str def merge_options(*options: TaskOptions | None) -> TaskOptions: """Merge multiple options dicts. For the same key, the last one wins.""" return cast( "TaskOptions", { "env": {k: v for opts in options if opts for k, v in opts.get("env", {}).items()}, **{k: v for opts in options if opts for k, v in opts.items() if k not in ("env", "help", "keep_going")}, }, ) exec_opts = merge_options # Alias for merge_options RE_ARGS_PLACEHOLDER = re.compile(r"\{args(?::(?P[^}]*))?\}") RE_PDM_PLACEHOLDER = re.compile(r"\{pdm\}") def _interpolate_args(script: str, args: Sequence[str]) -> tuple[str, bool]: """Interpolate the `{args:[defaults]} placeholder in a string""" import shlex def replace(m: re.Match[str]) -> str: default = m.group("default") or "" return shlex.join(args) if args else default interpolated, count = RE_ARGS_PLACEHOLDER.subn(replace, script) return interpolated, count > 0 def _interpolate_pdm(script: str) -> str: """Interpolate the `{pdm} placeholder in a string""" executable_path = Path(sys.executable) pdm_executable = shlex.join([executable_path.as_posix(), "-m", "pdm"]) interpolated = RE_PDM_PLACEHOLDER.sub(pdm_executable, script) return interpolated def interpolate(script: str, args: Sequence[str]) -> tuple[str, bool]: """Interpolate the `{args:[defaults]} placeholder in a string""" script, args_interpolated = _interpolate_args(script, args) script = _interpolate_pdm(script) return script, args_interpolated _METADATA_REGEX = r"(?m)^# /// (?P[a-zA-Z0-9-]+)$\s(?P(^#(| .*)$\s)+)^# ///$" def read_script_metadata(script: str, section: str) -> dict[str, Any] | None: # Adapted from https://packaging.python.org/en/latest/specifications/inline-script-metadata/ if sys.version_info >= (3, 11): import tomllib else: import tomli as tomllib matches = [m for m in re.finditer(_METADATA_REGEX, script) if m.group("type") == section] if len(matches) > 1: raise ValueError(f"Multiple {section} blocks found") elif len(matches) == 1: content = "".join( line[2:] if line.startswith("# ") else line[1:] for line in matches[0].group("content").splitlines(keepends=True) ) return tomllib.loads(content) else: return None class Task(NamedTuple): kind: str name: str args: str | Sequence[str] options: TaskOptions def __str__(self) -> str: return f"" @property def short_description(self) -> str: """ A short one line task description """ if self.kind == "composite": fallback = f" {termui.Emoji.ARROW_SEPARATOR} ".join(self.args) else: lines = [line.strip() for line in str(self.args).splitlines() if line.strip()] fallback = f"{lines[0]}{termui.Emoji.ELLIPSIS}" if len(lines) > 1 else lines[0] return self.options.get("help", fallback) class TaskRunner: """The task runner for pdm project""" TYPES = ("cmd", "shell", "call", "composite") OPTIONS = ("env", "env_file", "help", "keep_going", "working_dir", "site_packages") def __init__(self, project: Project, hooks: HookManager) -> None: self.project = project global_options = cast( "TaskOptions", self.project.scripts.get("_", {}) if self.project.scripts else {}, ) self.global_options = global_options.copy() self.recreate_env = False self.hooks = hooks def _get_script_env(self, script_file: str) -> BaseEnvironment: import hashlib from pdm.cli.commands.venv.backends import BACKENDS from pdm.environments import PythonEnvironment from pdm.installers.core import install_requirements from pdm.models.venv import get_venv_python with open(script_file, encoding="utf8") as f: metadata = read_script_metadata(f.read(), "script") if not metadata: return self.project.environment tool_config = metadata.pop("tool", {}) script_project = self.project.core.create_project() script_project.pyproject.set_data( {"project": {"name": "temp-project", **metadata, "version": "0.0.0"}, "tool": tool_config} ) md5_kwargs = {"usedforsecurity": False} venv_name = hashlib.md5(os.path.realpath(script_file).encode("utf-8"), **md5_kwargs).hexdigest() venv_backend = BACKENDS[script_project.config["venv.backend"]](script_project, None) venv = venv_backend.get_location(None, venv_name) if venv.exists() and not self.recreate_env: self.project.core.ui.info(f"Reusing existing script environment: {venv}", verbosity=termui.Verbosity.DETAIL) else: self.project.core.ui.info(f"Creating environment for script: {venv}", verbosity=termui.Verbosity.DETAIL) venv = venv_backend.create(venv_name=venv_name, force=True) env = PythonEnvironment(script_project, python=get_venv_python(venv).as_posix()) script_project._python = env.interpreter env.project = script_project # keep a strong reference to the project if reqs := script_project.get_dependencies(): install_requirements(reqs, env, clean=True) return env def get_task(self, script_name: str) -> Task | None: """Get the task with the given name. Return None if not found.""" if script_name not in self.project.scripts: return None script = cast("str | Sequence[str] | Mapping[str,Any]", self.project.scripts[script_name]) if not isinstance(script, Mapping): # Regard as the same as {cmd = ... } kind = "cmd" value = script options = {} else: script = dict(script) # to remove the effect of tomlkit's container type. for key in self.TYPES: if key in script: kind = key value = cast("str | Sequence[str]", script.pop(key)) break else: raise PdmUsageError(f"Script type must be one of ({', '.join(self.TYPES)})") options = script.copy() unknown_options = set(options) - set(self.OPTIONS) if unknown_options: raise PdmUsageError(f"Unknown options for task {script_name}: {', '.join(unknown_options)}") return Task(kind, script_name, value, cast("TaskOptions", options)) def expand_command(self, env: BaseEnvironment, command: str) -> str: expanded_command = os.path.expanduser(command) if expanded_command.replace(os.sep, "/").startswith(("./", "../")): abspath = os.path.abspath(expanded_command) if not os.path.isfile(abspath): raise PdmUsageError(f"Command [success]'{command}'[/] is not a valid executable.") return abspath result = env.which(command) if not result: raise PdmUsageError(f"Command [success]'{command}'[/] is not found in your PATH.") return result def _run_process( self, args: Sequence[str] | str, chdir: bool = False, shell: bool = False, site_packages: bool = False, env: Mapping[str, str] | None = None, env_file: EnvFileOptions | str | None = None, working_dir: str | None = None, ) -> int: """Run command in a subprocess and return the exit code.""" import dotenv from dotenv.main import resolve_variables project = self.project if not shell and args[0].endswith(".py"): project_env = self._get_script_env(os.path.expanduser(args[0])) else: check_project_file(project) project_env = project.environment this_path = project_env.get_paths()["scripts"] os.environ.update(project_env.process_env) if env_file is not None: if isinstance(env_file, str): path = env_file override = False else: path = env_file["override"] override = True project.core.ui.echo( f"Loading .env file: [success]{env_file}[/]", err=True, verbosity=termui.Verbosity.DETAIL, ) dotenv.load_dotenv(self.project.root / path, override=override) if env: os.environ.update(resolve_variables(env.items(), override=True)) if shell: assert isinstance(args, str) # environment variables will be expanded by shell process_cmd: str | Sequence[str] = args else: assert isinstance(args, Sequence) command, *args = (expand_env_vars(arg) for arg in args) if command.endswith(".py"): args = [command, *args] command = str(project_env.interpreter.executable) expanded_command = self.expand_command(project_env, command) real_command = os.path.realpath(expanded_command) process_cmd = [expanded_command, *args] if ( project_env.is_local and not site_packages and ( os.path.basename(real_command).startswith("python") or is_path_relative_to(expanded_command, this_path) ) ): # The executable belongs to the local packages directory. # Don't load system site-packages os.environ["NO_SITE_PACKAGES"] = "1" cwd = (project.root / working_dir) if working_dir else project.root if chdir else None def forward_signal(signum: int, frame: FrameType | None) -> None: if sys.platform == "win32" and signum == signal.SIGINT: signum = signal.SIGTERM process.send_signal(signum) process_env = os.environ.copy() process_env.update({"PDM_RUN_CWD": str(Path.cwd())}) handle_term = signal.signal(signal.SIGTERM, forward_signal) handle_int = signal.signal(signal.SIGINT, forward_signal) process = subprocess.Popen(process_cmd, cwd=cwd, shell=shell, bufsize=0, close_fds=False, env=process_env) retcode = process.wait() signal.signal(signal.SIGTERM, handle_term) signal.signal(signal.SIGINT, handle_int) return retcode def run_task( self, task: Task, args: Sequence[str] = (), opts: TaskOptions | None = None, seen: set[str] | None = None ) -> int: """Run the named task with the given arguments. Args: task: The task to run args: The extra arguments passed to the task opts: The options passed from parent if any seen: The set of seen tasks to prevent recursive calls """ kind, _, value, options = task shell = False if kind == "cmd": if isinstance(value, str): cmd, interpolated = interpolate(value, args) value = shlex.split(cmd) else: agg = [interpolate(part, args) for part in value] interpolated = any(row[1] for row in agg) # In case of multiple default, we need to split the resulting string. parts: Iterator[list[str]] = ( shlex.split(part) if interpolated else [part] for part, interpolated in agg ) # We flatten the nested list to obtain a list of arguments value = list(itertools.chain(*parts)) args = value if interpolated else [*value, *args] elif kind == "shell": assert isinstance(value, str) script, interpolated = interpolate(value, args) args = script if interpolated else " ".join([script, *args]) shell = True elif kind == "call": assert isinstance(value, str) module, _, func = value.partition(":") if not module or not func: raise PdmUsageError("Python callable must be in the form :") short_name = "_1" if re.search(r"\(.*?\)", func) is None: func += "()" args = ["python", "-c", f"import sys, {module} as {short_name};sys.exit({short_name}.{func})", *list(args)] elif kind == "composite": assert isinstance(value, list) self.display_task(task, args) if kind == "composite": args = list(args) should_interpolate = any(RE_ARGS_PLACEHOLDER.search(script) for script in value) should_interpolate = should_interpolate or any(RE_PDM_PLACEHOLDER.search(script) for script in value) composite_code = 0 keep_going = options.pop("keep_going", False) if options else False for script in value: if should_interpolate: script, _ = interpolate(script, args) split = shlex.split(script) cmd = split[0] subargs = split[1:] + ([] if should_interpolate else args) code = self.run(cmd, subargs, merge_options(options, opts), chdir=True, seen=seen) if code != 0: if not keep_going: return code composite_code = code return composite_code return self._run_process(args, chdir=True, shell=shell, **merge_options(self.global_options, options, opts)) # type: ignore[misc] def display_task(self, task: Task, args: Sequence[str]) -> None: """Display a task given current verbosity and settings""" is_verbose = self.project.core.ui.verbosity >= termui.Verbosity.DETAIL if not (is_verbose or self.project.config.get("scripts.show_header")): return args = task.args if task.kind == "composite" else args content = args if is_verbose else task.short_description self.project.core.ui.echo( f"Running {task}: [success]{content}[/]", err=True, verbosity=termui.Verbosity.NORMAL, ) def run( self, command: str, args: list[str], opts: TaskOptions | None = None, chdir: bool = False, seen: set[str] | None = None, ) -> int: """Run a command or script with the given arguments.""" if command in self.hooks.skip: return 0 if seen is None: seen = set() task = self.get_task(command) if task is not None: if task.kind == "composite": if command in seen: raise PdmUsageError(f"Script {command} is recursive.") seen = {command, *seen} self.hooks.try_emit("pre_script", script=command, args=args) pre_task = self.get_task(f"pre_{command}") if pre_task is not None and self.hooks.should_run(pre_task.name): code = self.run_task(pre_task, opts=opts) if code != 0: return code code = self.run_task(task, args, opts=opts, seen=seen) if code != 0: return code post_task = self.get_task(f"post_{command}") if post_task is not None and self.hooks.should_run(post_task.name): code = self.run_task(post_task, opts=opts) self.hooks.try_emit("post_script", script=command, args=args) return code else: return self._run_process([command, *args], chdir=chdir, **merge_options(self.global_options, opts)) # type: ignore[misc] def show_list(self) -> None: if not self.project.scripts: return columns = ["Name", "Type", "Description"] result = [] for name in sorted(self.project.scripts): if name.startswith("_"): continue task = self.get_task(name) assert task is not None result.append( ( f"[success]{name}[/]", task.kind, task.short_description, ) ) self.project.core.ui.display_columns(result, columns) def as_json(self) -> dict[str, Any]: out = {} for name in sorted(self.project.scripts): if name == "_": data = out["_"] = dict(name="_", kind="shared", help="Shared options", **self.global_options) _fix_env_file(data) continue task = self.get_task(name) assert task is not None data = out[name] = { "name": name, "kind": task.kind, "help": task.short_description, "args": task.args, # type: ignore[dict-item] } data.update(**task.options) _fix_env_file(data) return out def _fix_env_file(data: dict[str, Any]) -> dict[str, Any]: env_file = data.get("env_file") if isinstance(env_file, dict): del data["env_file"] data["env_file.override"] = env_file.get("override") return data class Command(BaseCommand): """Run commands or scripts with local packages loaded""" arguments = (*BaseCommand.arguments, skip_option, venv_option) def add_arguments(self, parser: argparse.ArgumentParser) -> None: action = parser.add_mutually_exclusive_group() action.add_argument( "-l", "--list", action="store_true", help="Show all available scripts defined in pyproject.toml", ) action.add_argument( "-j", "--json", action="store_true", help="Output all scripts infos in JSON", ) exec = parser.add_argument_group("Execution parameters") exec.add_argument( "-s", "--site-packages", action="store_true", help="Load site-packages from the selected interpreter", ) exec.add_argument( "--recreate", action="store_true", help="Recreate the script environment for self-contained scripts" ) exec.add_argument("script", nargs="?", help="The command to run") exec.add_argument( "args", nargs=argparse.REMAINDER, help="Arguments that will be passed to the command", ) def get_runner(self, project: Project, hooks: HookManager, options: argparse.Namespace) -> TaskRunner: if (runner_cls := getattr(self, "runner_cls", None)) is not None: # pragma: no cover deprecation_warning("runner_cls attribute is deprecated, use get_runner method instead.") runner = cast("type[TaskRunner]", runner_cls)(project, hooks) else: runner = TaskRunner(project, hooks) runner.recreate_env = options.recreate if options.site_packages: runner.global_options["site_packages"] = True return runner def handle(self, project: Project, options: argparse.Namespace) -> None: hooks = HookManager(project, options.skip) runner = self.get_runner(project, hooks, options) if options.list: return runner.show_list() if options.json: return print_json(data=runner.as_json()) if not options.script: project.core.ui.warn("No command is given, default to the Python REPL.") options.script = "python" hooks.try_emit("pre_run", script=options.script, args=options.args) exit_code = runner.run(options.script, options.args) hooks.try_emit("post_run", script=options.script, args=options.args) sys.exit(exit_code) def run_script_if_present(script_name: str) -> Callable: """Helper to create a signal handler to run specific script""" def handler(sender: Project, hooks: HookManager, **kwargs: Any) -> None: runner = TaskRunner(sender, hooks) task = runner.get_task(script_name) if task is None: return exit_code = runner.run_task(task) if exit_code != 0: sys.exit(exit_code) return handler for hook in pdm_signals: pdm_signals.signal(hook).connect(run_script_if_present(hook), weak=False) pdm-2.23.1/src/pdm/cli/commands/search.py000066400000000000000000000041711477560627500201430ustar00rootroot00000000000000from __future__ import annotations import argparse import sys import textwrap from shutil import get_terminal_size from pdm import termui from pdm._types import SearchResults from pdm.cli.commands.base import BaseCommand from pdm.cli.options import verbose_option from pdm.environments import BareEnvironment from pdm.models.working_set import WorkingSet from pdm.project import Project from pdm.utils import normalize_name def print_results( ui: termui.UI, hits: SearchResults, working_set: WorkingSet, terminal_width: int | None = None, ) -> None: if not hits: return name_column_width = max(len(hit.name) + len(hit.version or "") for hit in hits) + 4 for hit in hits: name = hit.name summary = hit.summary or "" if terminal_width is not None: target_width = terminal_width - name_column_width - 5 if target_width > 10: # wrap and indent summary to fit terminal summary = ("\n" + " " * (name_column_width + 2)).join(textwrap.wrap(summary, target_width)) current_width = len(name) + 1 spaces = " " * (name_column_width - current_width) line = f"[req]{name}[/]{spaces} - {summary}" try: ui.echo(line) if normalize_name(name) in working_set: dist = working_set[normalize_name(name)] ui.echo(f" INSTALLED: {dist.version}") except UnicodeEncodeError: pass class Command(BaseCommand): """Search for PyPI packages""" arguments = (verbose_option,) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument("query", help="Query string to search") def handle(self, project: Project, options: argparse.Namespace) -> None: project.environment = BareEnvironment(project) result = project.get_repository().search(options.query) terminal_width = None if sys.stdout.isatty(): terminal_width = get_terminal_size()[0] working_set = project.environment.get_working_set() print_results(project.core.ui, result, working_set, terminal_width) pdm-2.23.1/src/pdm/cli/commands/self_cmd.py000066400000000000000000000234641477560627500204600ustar00rootroot00000000000000from __future__ import annotations import argparse import shlex import subprocess import sys from typing import Any from pdm import termui from pdm.cli.actions import get_latest_pdm_version_from_pypi from pdm.cli.commands.base import BaseCommand from pdm.cli.options import verbose_option from pdm.cli.utils import PackageNode, build_dependency_graph from pdm.compat import Distribution from pdm.environments import BareEnvironment from pdm.models.markers import EnvSpec from pdm.models.working_set import WorkingSet from pdm.project import Project from pdm.utils import is_in_zipapp, normalize_name, parse_version PDM_REPO = "https://github.com/pdm-project/pdm" def list_distributions(plugin_only: bool = False) -> list[Distribution]: result: list[Distribution] = [] working_set = WorkingSet() for dist in working_set.values(): if not plugin_only or any(ep.group in ("pdm", "pdm.plugin") for ep in dist.entry_points): result.append(dist) return sorted(result, key=lambda d: d.metadata.get("Name", "UNKNOWN")) def run_pip(project: Project, args: list[str]) -> subprocess.CompletedProcess[str]: if project.config["use_uv"]: if "--upgrade-strategy" in args: # uv doesn't support this option args[(i := args.index("--upgrade-strategy")) : i + 2] = [] run_args = [*project.core.uv_cmd, "pip", *args, "--python", sys.executable] else: env = BareEnvironment(project) project.environment = env run_args = [*env.pip_command, *args] project.core.ui.echo(f"Running pip command: {run_args}", verbosity=termui.Verbosity.DETAIL) result = subprocess.run( run_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True, text=True, ) project.core.ui.echo( f"Run pip returns status {result.returncode}: {result.stdout}", verbosity=termui.Verbosity.DEBUG ) return result class Command(BaseCommand): """Manage the PDM program itself (previously known as plugin)""" arguments = (verbose_option,) name = "self" @classmethod def register_to( cls, subparsers: argparse._SubParsersAction, name: str | None = None, **kwargs: Any, ) -> None: return super().register_to(subparsers, name, aliases=["plugin"], **kwargs) def add_arguments(self, parser: argparse.ArgumentParser) -> None: subparsers = parser.add_subparsers(title="commands", metavar="") ListCommand.register_to(subparsers) if not is_in_zipapp(): AddCommand.register_to(subparsers) RemoveCommand.register_to(subparsers) UpdateCommand.register_to(subparsers) parser.set_defaults(search_parent=False) self.parser = parser def handle(self, project: Project, options: argparse.Namespace) -> None: self.parser.print_help() class ListCommand(BaseCommand): """List all packages installed with PDM""" arguments = (verbose_option,) name = "list" def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument("--plugins", action="store_true", help="List plugins only") def handle(self, project: Project, options: argparse.Namespace) -> None: distributions = list_distributions(plugin_only=options.plugins) echo = project.core.ui.echo if not distributions: # This should not happen when plugin_only is False echo("No plugin is installed with PDM", err=True) sys.exit(1) echo("Installed packages:", err=True) rows = [] for dist in distributions: rows.append( ( f"[success]{dist.metadata.get('Name')}[/]", f"[warning]{dist.metadata.get('Version')}[/]", dist.metadata.get("Summary", ""), ), ) project.core.ui.display_columns(rows) class AddCommand(BaseCommand): """Install packages to the PDM's environment""" arguments = (verbose_option,) name = "add" def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "--pip-args", help="Arguments that will be passed to pip install", default="", ) parser.add_argument( "packages", nargs="+", help="Specify one or many package names, each package can have a version specifier", ) def handle(self, project: Project, options: argparse.Namespace) -> None: pip_args = ["install", *shlex.split(options.pip_args), *options.packages] try: with project.core.ui.open_spinner(f"Installing packages: {options.packages}"): run_pip(project, pip_args) except subprocess.CalledProcessError as e: project.core.ui.echo("[error]Installation failed:[/]\n" + e.output, err=True) sys.exit(1) else: project.core.ui.echo("[success]Installation succeeds.[/]") class RemoveCommand(BaseCommand): """Remove packages from PDM's environment""" arguments = (verbose_option,) name = "remove" def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "--pip-args", help="Arguments that will be passed to pip uninstall", default="", ) parser.add_argument("-y", "--yes", action="store_true", help="Answer yes on the question") parser.add_argument("packages", nargs="+", help="Specify one or many package names") def _resolve_dependencies_to_remove(self, packages: list[str]) -> list[str]: """Perform a BFS to find all unneeded dependencies""" result: set[str] = set() to_resolve = list(packages) ws = WorkingSet() graph = build_dependency_graph(ws, env_spec=EnvSpec.current()) while to_resolve: temp: list[PackageNode] = [] for name in to_resolve: key = normalize_name(name) if key in ws: result.add(key) package = PackageNode(key, "0.0.0", {}) if package not in graph: continue for dep in graph.iter_children(package): temp.append(dep) graph.remove(package) to_resolve.clear() for dep in temp: if not any(graph.iter_parents(dep)) and dep.name != "pdm": to_resolve.append(dep.name) return sorted(result) def handle(self, project: Project, options: argparse.Namespace) -> None: packages_to_remove = self._resolve_dependencies_to_remove(options.packages) if not packages_to_remove: project.core.ui.echo("No package to remove.", err=True) sys.exit(1) if not (options.yes or termui.confirm(f"Will remove: {packages_to_remove}, continue?", default=True)): return pip_args = ["uninstall", "-y", *shlex.split(options.pip_args), *packages_to_remove] try: with project.core.ui.open_spinner(f"Uninstalling packages: [success]{', '.join(options.packages)}[/]"): run_pip(project, pip_args) except subprocess.CalledProcessError as e: project.core.ui.echo("[error]Uninstallation failed:[/]\n" + e.output, err=True) sys.exit(1) else: project.core.ui.echo("[success]Uninstallation succeeds.[/]") class UpdateCommand(BaseCommand): """Update PDM itself""" arguments = (verbose_option,) name = "update" def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "--head", action="store_true", help="Update to the latest commit on the main branch", ) parser.add_argument( "--pre", help="Update to the latest prerelease version", action="store_true", ) parser.add_argument( "--no-frozen-deps", action="store_false", dest="frozen_deps", default=True, help="Do not install frozen dependency versions", ) parser.add_argument( "--pip-args", help="Additional arguments that will be passed to pip install", default="", ) def handle(self, project: Project, options: argparse.Namespace) -> None: from pdm.__version__ import __version__, read_version locked = "[locked]" if options.frozen_deps else "" if options.head: package = f"pdm{locked} @ git+{PDM_REPO}@main" version: str | None = "HEAD" else: version = get_latest_pdm_version_from_pypi(project, options.pre) assert version is not None, "No version found" if parse_version(__version__) >= parse_version(version): project.core.ui.echo(f"Already up-to-date: [primary]{__version__}[/]") return package = f"pdm{locked}=={version}" pip_args = ["install", "--upgrade", "--upgrade-strategy", "eager", *shlex.split(options.pip_args), package] try: with project.core.ui.open_spinner(f"Updating pdm to version [primary]{version}[/]"): run_pip(project, pip_args) except subprocess.CalledProcessError as e: project.core.ui.echo( f"[error]Installing version [primary]{version}[/] failed:[/]\n" + e.output, err=True, ) sys.exit(1) else: project.core.ui.echo(f"[success]Successfully installed version [primary]{version}[/][/]") project.core.ui.echo(f"See what's new in this version: [link]{PDM_REPO}/releases/tag/{version}[/]") # Update the version value to avoid check update print wrong message project.core.version = read_version() pdm-2.23.1/src/pdm/cli/commands/show.py000066400000000000000000000055311477560627500176570ustar00rootroot00000000000000from __future__ import annotations import argparse from typing import TYPE_CHECKING from pdm.cli.commands.base import BaseCommand from pdm.cli.options import venv_option from pdm.exceptions import PdmUsageError from pdm.models.candidates import Candidate from pdm.models.project_info import ProjectInfo from pdm.models.requirements import parse_requirement from pdm.project import Project from pdm.utils import normalize_name, parse_version if TYPE_CHECKING: from unearth import Package def filter_stable(package: Package) -> bool: assert package.version return not parse_version(package.version).is_prerelease class Command(BaseCommand): """Show the package information""" metadata_keys = ("name", "version", "summary", "license", "platform", "keywords") def add_arguments(self, parser: argparse.ArgumentParser) -> None: venv_option.add_to_parser(parser) parser.add_argument( "package", type=normalize_name, nargs=argparse.OPTIONAL, help="Specify the package name, or show this package if not given", ) for option in self.metadata_keys: parser.add_argument(f"--{option}", action="store_true", help=f"Show {option}") def handle(self, project: Project, options: argparse.Namespace) -> None: package = options.package if package: with project.environment.get_finder() as finder: best_match = finder.find_best_match(package, allow_prereleases=True) if not best_match.applicable: project.core.ui.warn(f"No match found for the package {package!r}") return latest = Candidate.from_installation_candidate(best_match.best, parse_requirement(package)) latest_stable = next(filter(filter_stable, best_match.applicable), None) metadata = latest.prepare(project.environment).metadata else: if not project.is_distribution: raise PdmUsageError("This project is not a library") package = normalize_name(project.name) metadata = project.make_self_candidate(False).prepare(project.environment).prepare_metadata(True) latest_stable = None project_info = ProjectInfo.from_distribution(metadata) if any(getattr(options, key, None) for key in self.metadata_keys): for key in self.metadata_keys: if getattr(options, key, None): project.core.ui.echo(getattr(project_info, key)) return installed = project.environment.get_working_set().get(package) if latest_stable: project_info.latest_stable_version = str(latest_stable.version) if installed: project_info.installed_version = str(installed.version) project.core.ui.display_columns(list(project_info.generate_rows())) pdm-2.23.1/src/pdm/cli/commands/sync.py000066400000000000000000000027431477560627500176550ustar00rootroot00000000000000import argparse from pdm.cli import actions from pdm.cli.commands.base import BaseCommand from pdm.cli.filters import GroupSelection from pdm.cli.hooks import HookManager from pdm.cli.options import ( clean_group, dry_run_option, groups_group, install_group, lockfile_option, skip_option, venv_option, ) from pdm.project import Project class Command(BaseCommand): """Synchronize the current working set with lock file""" arguments = ( *BaseCommand.arguments, groups_group, dry_run_option, lockfile_option, skip_option, clean_group, install_group, venv_option, ) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "-r", "--reinstall", action="store_true", help="Force reinstall existing dependencies", ) def handle(self, project: Project, options: argparse.Namespace) -> None: actions.check_lockfile(project) selection = GroupSelection.from_options(project, options) actions.do_sync( project, selection=selection, dry_run=options.dry_run, clean=options.clean, no_editable=options.no_editable, no_self=options.no_self or "default" not in selection, reinstall=options.reinstall, only_keep=options.only_keep, hooks=HookManager(project, options.skip), ) pdm-2.23.1/src/pdm/cli/commands/update.py000066400000000000000000000203531477560627500201600ustar00rootroot00000000000000from __future__ import annotations import argparse from collections import defaultdict from typing import TYPE_CHECKING from pdm.cli.commands.base import BaseCommand from pdm.cli.filters import GroupSelection from pdm.cli.hooks import HookManager from pdm.cli.options import ( frozen_lockfile_option, groups_group, install_group, lockfile_option, override_option, prerelease_option, save_strategy_group, skip_option, unconstrained_option, update_strategy_group, venv_option, ) from pdm.exceptions import PdmUsageError, ProjectError if TYPE_CHECKING: from typing import Collection from pdm.models.requirements import Requirement from pdm.project import Project class Command(BaseCommand): """Update package(s) in pyproject.toml""" arguments = ( *BaseCommand.arguments, groups_group, install_group, lockfile_option, frozen_lockfile_option, save_strategy_group, override_option, update_strategy_group, prerelease_option, unconstrained_option, skip_option, venv_option, ) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "-t", "--top", action="store_true", help="Only update those listed in pyproject.toml", ) parser.add_argument( "--dry-run", "--outdated", action="store_true", dest="dry_run", help="Show the difference only without modifying the lockfile content", ) parser.add_argument( "--no-sync", dest="sync", default=True, action="store_false", help="Only update lock file but do not sync packages", ) parser.add_argument("packages", nargs="*", help="If packages are given, only update them") parser.set_defaults(dev=None) def handle(self, project: Project, options: argparse.Namespace) -> None: self.do_update( project, selection=GroupSelection.from_options(project, options), save=options.save_strategy or project.config["strategy.save"], strategy=options.update_strategy or project.config["strategy.update"], unconstrained=options.unconstrained, top=options.top, dry_run=options.dry_run, packages=options.packages, sync=options.sync, no_editable=options.no_editable, no_self=options.no_self, prerelease=options.prerelease, fail_fast=options.fail_fast, hooks=HookManager(project, options.skip), ) @staticmethod def do_update( project: Project, *, selection: GroupSelection, strategy: str = "reuse", save: str = "compatible", unconstrained: bool = False, top: bool = False, dry_run: bool = False, packages: Collection[str] = (), sync: bool = True, no_editable: bool = False, no_self: bool = False, prerelease: bool | None = None, fail_fast: bool = False, hooks: HookManager | None = None, ) -> None: """Update specified packages or all packages""" from itertools import chain from pdm.cli.actions import do_lock, do_sync from pdm.cli.utils import check_project_file, save_version_specifiers from pdm.models.specifiers import get_specifier from pdm.utils import normalize_name hooks = hooks or HookManager(project) check_project_file(project) if len(packages) > 0 and (top or len(selection.groups) > 1 or not selection.default): raise PdmUsageError("packages argument can't be used together with multiple -G or --no-default or --top.") all_dependencies = project.all_dependencies updated_deps: dict[str, list[Requirement]] = defaultdict(list) locked_groups = project.lockfile.groups tracked_names: set[str] = set() if not packages: if prerelease is not None: raise PdmUsageError("--prerelease/--stable must be used with packages given") selection.validate() for group in selection: updated_deps[group] = list(all_dependencies[group]) tracked_names.update(r.identify() for deps in updated_deps.values() for r in deps) else: group = selection.one() if locked_groups and group not in locked_groups: raise ProjectError(f"Requested group not in lockfile: {group}") dependencies = all_dependencies[group] for name in packages: normalized_name = normalize_name(name) matched_reqs = [d for d in dependencies if (d.key or "") == normalized_name] if not matched_reqs: candidates = project.get_locked_repository().all_candidates if normalized_name not in candidates: raise ProjectError( f"[req]{name}[/] does not exist in [primary]{group}[/] " f"{'dev-' if selection.dev else ''}dependencies nor is a transitive dependency." ) look_in_other_group = next( ( g for g, deps in all_dependencies.items() if any(normalized_name == d.key for d in deps) and g != group ), None, ) if look_in_other_group is not None: raise ProjectError( f"[req]{name}[/] does not exist in [primary]{group}[/], but exists in [primary]{look_in_other_group}[/]." " Please specify the correct group with `-G/--group`." ) tracked_names.add(normalized_name) else: for req in matched_reqs: req.prerelease = prerelease updated_deps[group].extend(matched_reqs) tracked_names.update(r.identify() for deps in updated_deps.values() for r in deps) project.core.ui.echo( f"Updating {'[bold]global[/] ' if project.is_global else ''}packages: {', '.join(f'[req]{v}[/]' for v in tracked_names)}." ) if unconstrained: for deps in updated_deps.values(): for dep in deps: dep.specifier = get_specifier("") reqs = [r for g, deps in all_dependencies.items() for r in deps if locked_groups is None or g in locked_groups] # Since dry run is always true in the locking, # we need to emit the hook manually with the real dry_run value hooks.try_emit("pre_lock", requirements=reqs, dry_run=dry_run) with hooks.skipping("pre_lock", "post_lock"): resolved = do_lock( project, strategy=strategy, tracked_names=tracked_names, requirements=reqs, dry_run=True, hooks=hooks, groups=locked_groups, ) if unconstrained: # Need to update version constraints save_version_specifiers(chain.from_iterable(updated_deps.values()), resolved, save) if not dry_run: if unconstrained: for group, deps in updated_deps.items(): project.add_dependencies(deps, group, selection.dev or False) project.write_lockfile(project.lockfile._data, False) hooks.try_emit("post_lock", resolution=resolved, dry_run=dry_run) if sync or dry_run: do_sync( project, selection=selection, clean=False, dry_run=dry_run, requirements=[r for deps in updated_deps.values() for r in deps], tracked_names=[r.identify() for deps in updated_deps.values() for r in deps] if top else None, no_editable=no_editable, no_self=no_self or "default" not in selection, fail_fast=fail_fast, hooks=hooks, ) pdm-2.23.1/src/pdm/cli/commands/use.py000066400000000000000000000220161477560627500174700ustar00rootroot00000000000000from __future__ import annotations import argparse from pdm import termui from pdm.cli.commands.base import BaseCommand from pdm.cli.hooks import HookManager from pdm.cli.options import skip_option from pdm.exceptions import NoPythonVersion from pdm.models.caches import JSONFileCache from pdm.models.python import PythonInfo from pdm.models.venv import get_venv_python from pdm.project import Project from pdm.utils import is_conda_base_python class Command(BaseCommand): """Use the given python version or path as base interpreter. If not found, PDM will try to install one.""" def add_arguments(self, parser: argparse.ArgumentParser) -> None: skip_option.add_to_parser(parser) unattended_use_group = parser.add_mutually_exclusive_group() unattended_use_group.add_argument( "-f", "--first", action="store_true", help="Select the first matched interpreter - no auto install", ) unattended_use_group.add_argument( "--auto-install-min", action="store_true", help="If `python` argument not given, auto install minimal best match - otherwise has no effect", ) unattended_use_group.add_argument( "--auto-install-max", action="store_true", help="If `python` argument not given, auto install maximum best match - otherwise has no effect", ) parser.add_argument( "-i", "--ignore-remembered", action="store_true", help="Ignore the remembered selection", ) parser.add_argument( "--no-version-file", dest="version_file", default=True, action="store_false", help="Do not write .python-version file", ) parser.add_argument("--venv", help="Use the interpreter in the virtual environment with the given name") parser.add_argument("python", nargs="?", help="Specify the Python version or path", default="") @staticmethod def select_python( project: Project, python: str, *, ignore_remembered: bool, ignore_requires_python: bool, venv: str | None, first: bool, auto_install_min: bool, auto_install_max: bool, ) -> PythonInfo: from pdm.cli.commands.python import InstallCommand from pdm.cli.commands.venv.utils import get_venv_with_name def version_matcher(py_version: PythonInfo) -> bool: return py_version.valid and ( ignore_requires_python or project.python_requires.contains(py_version.version, True) ) if venv: virtualenv = get_venv_with_name(project, venv) return PythonInfo.from_path(virtualenv.interpreter) if not project.cache_dir.exists(): project.cache_dir.mkdir(parents=True) use_cache: JSONFileCache[str, str] = JSONFileCache(project.cache_dir / "use_cache.json") python = python.strip() if python and not ignore_remembered and python in use_cache: path = use_cache.get(python) cached_python = PythonInfo.from_path(path) if not cached_python.valid: project.core.ui.error( f"The last selection is corrupted. {path!r}", ) elif version_matcher(cached_python): project.core.ui.info("Using the last selection, add '-i' to ignore it.") return cached_python if not python and not first and (auto_install_min or auto_install_max): match = project.get_best_matching_cpython_version(auto_install_min) if match is None: req = f'requires-python="{project.python_requires}"' raise NoPythonVersion( f"No Python interpreter matching [success]{req}[/] is found based on 'auto-install' strategy." ) try: installed_interpreter_to_use = InstallCommand.install_python(project, str(match)) except Exception as e: project.core.ui.error(f"Failed to install Python {python}: {e}") project.core.ui.info("Please select a Python interpreter manually") else: return installed_interpreter_to_use found_interpreters = list( dict.fromkeys(project.iter_interpreters(python, filter_func=version_matcher, respect_version_file=False)) ) if not found_interpreters: req = python if ignore_requires_python else f'requires-python="{project.python_requires}"' raise NoPythonVersion(f"No Python interpreter matching [success]{req}[/] is found.") if first or len(found_interpreters) == 1 or not termui.is_interactive(): project.core.ui.info("Using the first matched interpreter.") return found_interpreters[0] project.core.ui.echo( f"Please enter the {'[bold]Global[/] ' if project.is_global else ''}Python interpreter to use" ) for i, py_version in enumerate(found_interpreters): project.core.ui.echo( f"{i:>2}. [success]{py_version.implementation}@{py_version.identifier}[/] ({py_version.path!s})" ) selection = termui.ask( "Please select", default="0", prompt_type=int, choices=[str(i) for i in range(len(found_interpreters))], show_choices=False, ) return found_interpreters[int(selection)] def do_use( self, project: Project, python: str = "", first: bool = False, ignore_remembered: bool = False, ignore_requires_python: bool = False, save: bool = True, venv: str | None = None, auto_install_min: bool = False, auto_install_max: bool = False, version_file: bool = True, hooks: HookManager | None = None, ) -> PythonInfo: """Use the specified python version and save in project config. The python can be a version string or interpreter path. """ from pdm.environments import PythonLocalEnvironment selected_python = self.select_python( project, python, ignore_remembered=ignore_remembered, first=first, venv=venv, ignore_requires_python=ignore_requires_python, auto_install_min=auto_install_min, auto_install_max=auto_install_max, ) # NOTE: PythonInfo is cached with path as key. # This can lead to inconsistency when the same virtual environment is reused. # So the original python identifier is preserved here for logging purpose. selected_python_identifier = selected_python.identifier if python and selected_python.get_venv() is None: use_cache: JSONFileCache[str, str] = JSONFileCache(project.cache_dir / "use_cache.json") use_cache.set(python, selected_python.path.as_posix()) if project.config["python.use_venv"] and ( selected_python.get_venv() is None or is_conda_base_python(selected_python.path) ): venv_path = project._create_virtualenv(str(selected_python.path)) selected_python = PythonInfo.from_path(get_venv_python(venv_path)) if not save: return selected_python saved_python = project._saved_python old_python = PythonInfo.from_path(saved_python) if saved_python else None project.core.ui.echo( f"Using {'[bold]Global[/] ' if project.is_global else ''}Python interpreter: [success]{selected_python.path!s}[/] ({selected_python_identifier})" ) project.python = selected_python if version_file: with project.root.joinpath(".python-version").open("w") as f: f.write(f"{selected_python.major}.{selected_python.minor}\n") if project.environment.is_local: assert isinstance(project.environment, PythonLocalEnvironment) project.core.ui.echo( "Using __pypackages__ because non-venv Python is used.", style="primary", err=True, ) if old_python and old_python.executable != selected_python.executable: project.core.ui.echo("Updating executable scripts...", style="primary") project.environment.update_shebangs(selected_python.executable.as_posix()) hooks = hooks or HookManager(project) hooks.try_emit("post_use", python=selected_python) return selected_python def handle(self, project: Project, options: argparse.Namespace) -> None: self.do_use( project, python=options.python, first=options.first, ignore_remembered=options.ignore_remembered, venv=options.venv, auto_install_min=options.auto_install_min, auto_install_max=options.auto_install_max, version_file=options.version_file, hooks=HookManager(project, options.skip), ) pdm-2.23.1/src/pdm/cli/commands/venv/000077500000000000000000000000001477560627500172775ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/commands/venv/__init__.py000066400000000000000000000032701477560627500214120ustar00rootroot00000000000000from __future__ import annotations import argparse from pdm.cli.commands.base import BaseCommand from pdm.cli.commands.venv.activate import ActivateCommand from pdm.cli.commands.venv.create import CreateCommand from pdm.cli.commands.venv.list import ListCommand from pdm.cli.commands.venv.purge import PurgeCommand from pdm.cli.commands.venv.remove import RemoveCommand from pdm.cli.commands.venv.utils import get_venv_with_name from pdm.cli.options import project_option from pdm.project import Project class Command(BaseCommand): """Virtualenv management""" name = "venv" arguments = (project_option,) def add_arguments(self, parser: argparse.ArgumentParser) -> None: group = parser.add_mutually_exclusive_group() group.add_argument("--path", help="Show the path to the given virtualenv") group.add_argument("--python", help="Show the python interpreter path for the given virtualenv") subparser = parser.add_subparsers(title="commands", metavar="") CreateCommand.register_to(subparser, "create") ListCommand.register_to(subparser, "list") RemoveCommand.register_to(subparser, "remove") ActivateCommand.register_to(subparser, "activate") PurgeCommand.register_to(subparser, "purge") self.parser = parser def handle(self, project: Project, options: argparse.Namespace) -> None: if options.path: venv = get_venv_with_name(project, options.path) project.core.ui.echo(str(venv.root)) elif options.python: venv = get_venv_with_name(project, options.python) project.core.ui.echo(str(venv.interpreter)) else: self.parser.print_help() pdm-2.23.1/src/pdm/cli/commands/venv/activate.py000066400000000000000000000053541477560627500214600ustar00rootroot00000000000000import argparse import platform import shlex from pathlib import Path import shellingham from pdm.cli.commands.base import BaseCommand from pdm.cli.commands.venv.utils import get_venv_with_name from pdm.cli.options import verbose_option from pdm.models.venv import VirtualEnv from pdm.project import Project class ActivateCommand(BaseCommand): """Print the command to activate the virtualenv with the given name""" arguments = (verbose_option,) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument("env", nargs="?", help="The key of the virtualenv") def handle(self, project: Project, options: argparse.Namespace) -> None: if options.env: venv = get_venv_with_name(project, options.env) else: # Use what is saved in .pdm-python interpreter = project._saved_python if not interpreter: project.core.ui.warn( "The project doesn't have a saved python.path. Run [success]pdm use[/] to pick one." ) raise SystemExit(1) venv_like = VirtualEnv.from_interpreter(Path(interpreter)) if venv_like is None: project.core.ui.warn( f"Can't activate a non-venv Python [success]{interpreter}[/], " "you can specify one with [success]pdm venv activate [/]", ) raise SystemExit(1) venv = venv_like project.core.ui.echo(self.get_activate_command(venv)) def get_activate_command(self, venv: VirtualEnv) -> str: # pragma: no cover try: shell, _ = shellingham.detect_shell() except shellingham.ShellDetectionFailure: shell = "" if shell == "fish": command, filename = "source", "activate.fish" elif shell in ["csh", "tcsh"]: command, filename = "source", "activate.csh" elif shell in ["powershell", "pwsh"]: command, filename = ".", "Activate.ps1" else: command, filename = "source", "activate" activate_script = venv.interpreter.with_name(filename) if activate_script.exists(): if platform.system() == "Windows": return f"{self.quote(str(activate_script), shell)}" return f"{command} {self.quote(str(activate_script), shell)}" # Conda backed virtualenvs don't have activate scripts return f"conda activate {self.quote(str(venv.root), shell)}" @staticmethod def quote(command: str, shell: str) -> str: if shell in ["powershell", "pwsh"] or platform.system() == "Windows": return "{}".format(command.replace("'", "''")) return shlex.quote(command) pdm-2.23.1/src/pdm/cli/commands/venv/backends.py000066400000000000000000000166471477560627500214410ustar00rootroot00000000000000from __future__ import annotations import abc import os import shutil import subprocess import sys from functools import cached_property from pathlib import Path from typing import Any, Iterable, Mapping from pdm import termui from pdm.cli.commands.venv.utils import get_venv_prefix from pdm.exceptions import PdmUsageError, ProjectError from pdm.models.python import PythonInfo from pdm.project import Project class VirtualenvCreateError(ProjectError): pass class Backend(abc.ABC): """The base class for virtualenv backends""" def __init__(self, project: Project, python: str | None) -> None: self.project = project self.python = python @abc.abstractmethod def pip_args(self, with_pip: bool) -> Iterable[str]: pass @cached_property def _resolved_interpreter(self) -> PythonInfo: if not self.python: project_python = self.project._python if project_python: return project_python def match_func(py_version: PythonInfo) -> bool: return bool(self.python) or ( py_version.valid and self.project.python_requires.contains(py_version.version, True) ) for py_version in self.project.iter_interpreters(self.python, search_venv=False, filter_func=match_func): return py_version python = f" {self.python}" if self.python else "" raise VirtualenvCreateError(f"Can't resolve python interpreter{python}") @property def ident(self) -> str: """Get the identifier of this virtualenv. self.python can be one of: 3.8 /usr/bin/python 3.9.0a4 python3.8 """ return self._resolved_interpreter.identifier def subprocess_call(self, cmd: list[str], **kwargs: Any) -> None: self.project.core.ui.echo( f"Run command: [success]{cmd}[/]", verbosity=termui.Verbosity.DETAIL, err=True, ) try: subprocess.check_call( cmd, stdout=subprocess.DEVNULL if self.project.core.ui.verbosity < termui.Verbosity.DETAIL else None, ) except subprocess.CalledProcessError as e: # pragma: no cover raise VirtualenvCreateError(e) from None def _ensure_clean(self, location: Path, force: bool = False) -> None: if not location.exists(): return if location.is_dir() and not any(location.iterdir()): return if not force: raise VirtualenvCreateError(f"The location {location} is not empty, add --force to overwrite it.") if location.is_file(): self.project.core.ui.info(f"Removing existing file {location}", verbosity=termui.Verbosity.DETAIL) location.unlink() else: self.project.core.ui.info( f"Cleaning existing target directory {location}", verbosity=termui.Verbosity.DETAIL ) with os.scandir(location) as entries: for entry in entries: if entry.is_dir() and not entry.is_symlink(): shutil.rmtree(entry.path) else: os.remove(entry.path) def get_location(self, name: str | None = None, venv_name: str | None = None) -> Path: if name and venv_name: raise PdmUsageError("Cannot specify both name and venv_name") venv_parent = Path(self.project.config["venv.location"]).expanduser() if not venv_parent.is_dir(): venv_parent.mkdir(exist_ok=True, parents=True) if not venv_name: venv_name = f"{get_venv_prefix(self.project)}{name or self.ident}" return venv_parent / venv_name def create( self, name: str | None = None, args: tuple[str, ...] = (), force: bool = False, in_project: bool = False, prompt: str | None = None, with_pip: bool = False, venv_name: str | None = None, ) -> Path: if in_project: location = self.project.root / ".venv" else: location = self.get_location(name, venv_name) args = (*self.pip_args(with_pip), *args) if prompt is not None: prompt = prompt.format( project_name=self.project.root.name.lower() or "virtualenv", python_version=self.ident, ) self._ensure_clean(location, force) self.perform_create(location, args, prompt=prompt) return location @abc.abstractmethod def perform_create(self, location: Path, args: tuple[str, ...], prompt: str | None = None) -> None: pass class VirtualenvBackend(Backend): def pip_args(self, with_pip: bool) -> Iterable[str]: if with_pip: return () return ("--no-pip", "--no-setuptools", "--no-wheel") def perform_create(self, location: Path, args: tuple[str, ...], prompt: str | None = None) -> None: prompt_option = (f"--prompt={prompt}",) if prompt else () cmd = [ sys.executable, "-m", "virtualenv", str(location), "-p", str(self._resolved_interpreter.executable), *prompt_option, *args, ] self.subprocess_call(cmd) class VenvBackend(VirtualenvBackend): def pip_args(self, with_pip: bool) -> Iterable[str]: if with_pip: return () return ("--without-pip",) def perform_create(self, location: Path, args: tuple[str, ...], prompt: str | None = None) -> None: prompt_option = (f"--prompt={prompt}",) if prompt else () cmd = [str(self._resolved_interpreter.executable), "-m", "venv", str(location), *prompt_option, *args] self.subprocess_call(cmd) class UvBackend(VirtualenvBackend): def pip_args(self, with_pip: bool) -> Iterable[str]: if with_pip: return ("--seed", "pip") return () def perform_create(self, location: Path, args: tuple[str, ...], prompt: str | None = None) -> None: prompt_option = (f"--prompt={prompt}",) if prompt else () cmd = [ *self.project.core.uv_cmd, "venv", "-p", str(self._resolved_interpreter.executable), *prompt_option, *args, str(location), ] self.subprocess_call(cmd) class CondaBackend(Backend): @property def ident(self) -> str: # Conda supports specifying python that doesn't exist, # use the passed-in name directly if self.python: return self.python return super().ident def pip_args(self, with_pip: bool) -> Iterable[str]: if with_pip: return ("pip",) return () def perform_create(self, location: Path, args: tuple[str, ...], prompt: str | None = None) -> None: if self.python: python_ver = self.python else: python = self._resolved_interpreter python_ver = f"{python.major}.{python.minor}" if any(arg.startswith("python=") for arg in args): raise PdmUsageError("Cannot use python= in conda creation arguments") cmd = ["conda", "create", "--yes", "--prefix", str(location), f"python={python_ver}", *args] self.subprocess_call(cmd) BACKENDS: Mapping[str, type[Backend]] = { "virtualenv": VirtualenvBackend, "venv": VenvBackend, "conda": CondaBackend, "uv": UvBackend, } pdm-2.23.1/src/pdm/cli/commands/venv/create.py000066400000000000000000000041541477560627500211200ustar00rootroot00000000000000import argparse from pdm.cli.commands.base import BaseCommand from pdm.cli.commands.venv.backends import BACKENDS from pdm.cli.options import verbose_option from pdm.project import Project class CreateCommand(BaseCommand): """Create a virtualenv pdm venv create [-other args] """ description = "Create a virtualenv" arguments = (verbose_option,) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "-w", "--with", dest="backend", choices=BACKENDS.keys(), help="Specify the backend to create the virtualenv", ) parser.add_argument( "-f", "--force", action="store_true", help="Recreate if the virtualenv already exists", ) parser.add_argument("-n", "--name", help="Specify the name of the virtualenv") parser.add_argument("--with-pip", action="store_true", help="Install pip with the virtualenv") parser.add_argument( "python", nargs="?", help="Specify which python should be used to create the virtualenv", ) parser.add_argument( "venv_args", nargs=argparse.REMAINDER, help="Additional arguments that will be passed to the backend", ) def handle(self, project: Project, options: argparse.Namespace) -> None: in_project = project.config["venv.in_project"] and not options.name backend: str = options.backend or project.config["venv.backend"] venv_backend = BACKENDS[backend](project, options.python) with project.core.ui.open_spinner(f"Creating virtualenv using [success]{backend}[/]..."): path = venv_backend.create( options.name, options.venv_args, options.force, in_project, prompt=project.config["venv.prompt"], with_pip=options.with_pip or project.config["venv.with_pip"], ) project.core.ui.echo(f"Virtualenv [success]{path}[/] is created successfully") pdm-2.23.1/src/pdm/cli/commands/venv/list.py000066400000000000000000000014641477560627500206310ustar00rootroot00000000000000import argparse from pathlib import Path from pdm.cli.commands.base import BaseCommand from pdm.cli.commands.venv.utils import iter_venvs from pdm.cli.options import verbose_option from pdm.project import Project class ListCommand(BaseCommand): """List all virtualenvs associated with this project""" arguments = (verbose_option,) def handle(self, project: Project, options: argparse.Namespace) -> None: project.core.ui.echo("Virtualenvs created with this project:\n") for ident, venv in iter_venvs(project): saved_python = project._saved_python if saved_python and Path(saved_python).parent.parent == venv.root: mark = "*" else: mark = "-" project.core.ui.echo(f"{mark} [success]{ident}[/]: {venv.root}") pdm-2.23.1/src/pdm/cli/commands/venv/purge.py000066400000000000000000000045231477560627500207770ustar00rootroot00000000000000import argparse import shutil from pathlib import Path from pdm import termui from pdm.cli.commands.base import BaseCommand from pdm.cli.commands.venv.utils import iter_central_venvs from pdm.cli.options import verbose_option from pdm.project import Project class PurgeCommand(BaseCommand): """Purge selected/all created Virtualenvs""" arguments = (verbose_option,) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "-f", "--force", action="store_true", help="Force purging without prompting for confirmation", ) parser.add_argument( "-i", "--interactive", action="store_true", help="Interactively purge selected Virtualenvs", ) def handle(self, project: Project, options: argparse.Namespace) -> None: all_central_venvs = list(iter_central_venvs(project)) if not all_central_venvs: project.core.ui.echo("No virtualenvs to purge, quitting.", style="success") return if not options.force: project.core.ui.echo("The following Virtualenvs will be purged:", style="warning") for i, venv in enumerate(all_central_venvs): project.core.ui.echo(f"{i}. [success]{venv[0]}[/]") if not options.interactive: if options.force or termui.confirm("continue?", default=True): return self.del_all_venvs(project) selection = termui.ask( "Please select", choices=([str(i) for i in range(len(all_central_venvs))] + ["all", "none"]), default="none", show_choices=False, ) if selection == "all": self.del_all_venvs(project) elif selection != "none": for i, venv in enumerate(all_central_venvs): if i == int(selection): shutil.rmtree(venv[1]) project.core.ui.echo("Purged successfully!") def del_all_venvs(self, project: Project) -> None: saved_python = project._saved_python for _, venv in iter_central_venvs(project): shutil.rmtree(venv) if saved_python and Path(saved_python).parent.parent == venv: project._saved_python = None project.core.ui.echo("Purged successfully!") pdm-2.23.1/src/pdm/cli/commands/venv/remove.py000066400000000000000000000024001477560627500211420ustar00rootroot00000000000000import argparse import shutil from pathlib import Path from pdm import termui from pdm.cli.commands.base import BaseCommand from pdm.cli.commands.venv.utils import get_venv_with_name from pdm.cli.options import verbose_option from pdm.project import Project class RemoveCommand(BaseCommand): """Remove the virtualenv with the given name""" arguments = (verbose_option,) def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "-y", "--yes", action="store_true", help="Answer yes on the following question", ) parser.add_argument("env", help="The key of the virtualenv") def handle(self, project: Project, options: argparse.Namespace) -> None: project.core.ui.echo("Virtualenvs created with this project:") venv = get_venv_with_name(project, options.env) if options.yes or termui.confirm(f"[warning]Will remove: [success]{venv.root}[/], continue?", default=True): shutil.rmtree(venv.root) saved_python = project._saved_python if saved_python and Path(saved_python).parent.parent == venv.root: project._saved_python = None project.core.ui.echo("Removed successfully!") pdm-2.23.1/src/pdm/cli/commands/venv/utils.py000066400000000000000000000051341477560627500210140ustar00rootroot00000000000000from __future__ import annotations import base64 import hashlib import typing as t from pathlib import Path from findpython import BaseProvider, PythonVersion from pdm.exceptions import PdmUsageError from pdm.models.venv import VirtualEnv from pdm.project import Project def hash_path(path: str) -> str: """Generate a hash for the given path.""" return base64.urlsafe_b64encode(hashlib.new("md5", path.encode(), usedforsecurity=False).digest()).decode()[:8] def get_in_project_venv(root: Path) -> VirtualEnv | None: """Get the python interpreter path of venv-in-project""" for possible_dir in (".venv", "venv", "env"): venv = VirtualEnv.get(root / possible_dir) if venv is not None: return venv return None def get_venv_prefix(project: Project) -> str: """Get the venv prefix for the project""" path = project.root name_hash = hash_path(path.as_posix()) return f"{path.name}-{name_hash}-" def iter_venvs(project: Project) -> t.Iterable[tuple[str, VirtualEnv]]: """Return an iterable of venv paths associated with the project""" in_project_venv = get_in_project_venv(project.root) if in_project_venv is not None: yield "in-project", in_project_venv venv_prefix = get_venv_prefix(project) venv_parent = Path(project.config["venv.location"]) for path in venv_parent.glob(f"{venv_prefix}*"): ident = path.name[len(venv_prefix) :] venv = VirtualEnv.get(path) if venv is not None: yield ident, venv def iter_central_venvs(project: Project) -> t.Iterable[tuple[str, Path]]: """Return an iterable of all managed venvs and their paths.""" venv_parent = Path(project.config["venv.location"]) for venv in venv_parent.glob("*"): ident = venv.name yield ident, venv class VenvProvider(BaseProvider): """A Python provider for project venv pythons""" def __init__(self, project: Project) -> None: self.project = project @classmethod def create(cls) -> t.Self | None: return None def find_pythons(self) -> t.Iterable[PythonVersion]: for _, venv in iter_venvs(self.project): yield PythonVersion(venv.interpreter, _interpreter=venv.interpreter, keep_symlink=True) def get_venv_with_name(project: Project, name: str) -> VirtualEnv: all_venvs = dict(iter_venvs(project)) try: return all_venvs[name] except KeyError: raise PdmUsageError( f"No virtualenv with key '{name}' is found, must be one of {list(all_venvs)}.\n" "You can create one with 'pdm venv create'.", ) from None pdm-2.23.1/src/pdm/cli/completions/000077500000000000000000000000001477560627500170545ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/completions/__init__.py000066400000000000000000000000001477560627500211530ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/completions/pdm.bash000066400000000000000000000142671477560627500205050ustar00rootroot00000000000000# BASH completion script for pdm # Generated by pycomplete 0.4.0 _pdm_a919b69078acdf0a_complete() { local cur script coms opts com COMPREPLY=() _get_comp_words_by_ref -n : cur words # for an alias, get the real script behind it if [[ $(type -t ${words[0]}) == "alias" ]]; then script=$(alias ${words[0]} | sed -E "s/alias ${words[0]}='(.*)'/\\1/") else script=${words[0]} fi # lookup for command for word in ${words[@]:1}; do if [[ $word != -* ]]; then com=$word break fi done # completing for an option if [[ ${cur} == --* ]] ; then opts="--config --help --ignore-python --no-cache --non-interactive --pep582 --quiet --verbose --version" case "$com" in (add) opts="--config-setting --dev --dry-run --editable --fail-fast --frozen-lockfile --global --group --help --lockfile --no-editable --no-isolation --no-self --no-sync --override --prerelease --project --quiet --save-compatible --save-exact --save-minimum --save-safe-compatible --save-wildcard --skip --stable --unconstrained --update-all --update-eager --update-reuse --update-reuse-installed --venv --verbose" ;; (build) opts="--config-setting --dest --help --no-clean --no-isolation --no-sdist --no-wheel --project --quiet --skip --verbose" ;; (cache) opts="--help --quiet --verbose" ;; (completion) opts="--help" ;; (config) opts="--delete --edit --global --help --local --project --quiet --verbose" ;; (export) opts="--dev --editable-self --expandvars --format --global --group --help --lockfile --no-default --no-extras --no-markers --output --production --project --pyproject --quiet --self --verbose --without --without-hashes" ;; (fix) opts="--dry-run --global --help --project --quiet --verbose" ;; (import) opts="--dev --format --global --group --help --project --quiet --verbose" ;; (info) opts="--env --global --help --json --packages --project --python --quiet --venv --verbose --where" ;; (init) opts="--backend --cookiecutter --copier --dist --global --help --license --non-interactive --overwrite --project --project-version --python --quiet --skip --verbose" ;; (install) opts="--check --config-setting --dev --dry-run --fail-fast --frozen-lockfile --global --group --help --lockfile --no-default --no-editable --no-isolation --no-self --override --plugins --production --project --quiet --skip --venv --verbose --without" ;; (list) opts="--csv --exclude --fields --freeze --global --graph --help --include --json --markdown --project --quiet --resolve --reverse --sort --venv --verbose" ;; (lock) opts="--append --check --config-setting --dev --exclude-newer --global --group --help --implementation --lockfile --no-cross-platform --no-default --no-isolation --no-static-urls --override --platform --production --project --python --quiet --refresh --skip --static-urls --strategy --update-reuse --update-reuse-installed --verbose --without" ;; (outdated) opts="--global --help --include-sub --json --project --quiet --verbose" ;; (plugin) opts="--help --quiet --verbose" ;; (publish) opts="--ca-certs --comment --dest --help --identity --no-build --no-very-ssl --password --project --quiet --repository --sign --skip --skip-existing --username --verbose" ;; (py) opts="--help" ;; (python) opts="--help" ;; (remove) opts="--config-setting --dev --dry-run --fail-fast --frozen-lockfile --global --group --help --lockfile --no-editable --no-isolation --no-self --no-sync --override --project --quiet --skip --venv --verbose" ;; (run) opts="--global --help --json --list --project --quiet --recreate --site-packages --skip --venv --verbose" ;; (search) opts="--help --quiet --verbose" ;; (self) opts="--help --quiet --verbose" ;; (show) opts="--global --help --keywords --license --name --platform --project --quiet --summary --venv --verbose --version" ;; (sync) opts="--clean --clean-unselected --config-setting --dev --dry-run --fail-fast --global --group --help --lockfile --no-default --no-editable --no-isolation --no-self --production --project --quiet --reinstall --skip --venv --verbose --without" ;; (update) opts="--config-setting --dev --fail-fast --frozen-lockfile --global --group --help --lockfile --no-default --no-editable --no-isolation --no-self --no-sync --outdated --override --prerelease --production --project --quiet --save-compatible --save-exact --save-minimum --save-safe-compatible --save-wildcard --skip --stable --top --unconstrained --update-all --update-eager --update-reuse --update-reuse-installed --venv --verbose --without" ;; (use) opts="--auto-install-max --auto-install-min --first --global --help --ignore-remembered --no-version-file --project --quiet --skip --venv --verbose" ;; (venv) opts="--help --path --project --python" ;; esac COMPREPLY=($(compgen -W "${opts}" -- ${cur})) __ltrim_colon_completions "$cur" return 0; fi # completing for a command if [[ $cur == $com ]]; then coms="add build cache completion config export fix import info init install list lock outdated plugin publish py python remove run search self show sync update use venv" COMPREPLY=($(compgen -W "${coms}" -- ${cur})) __ltrim_colon_completions "$cur" return 0 fi } complete -o default -F _pdm_a919b69078acdf0a_complete pdm pdm-2.23.1/src/pdm/cli/completions/pdm.fish000066400000000000000000002207111477560627500205120ustar00rootroot00000000000000# FISH completion script for pdm # Generated by pycomplete 0.4.0 function __fish_pdm_a919b69078acdf0a_complete_no_subcommand for i in (commandline -opc) if contains -- $i add build cache completion config export fix import info init install list lock outdated plugin publish py python remove run search self show sync update use venv return 1 end end return 0 end # global options complete -c pdm -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -l config -d 'Specify another config file path [env var: PDM_CONFIG_FILE] ' complete -c pdm -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -l help -d 'Show this help message and exit.' complete -c pdm -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -l ignore-python -d 'Ignore the Python path saved in .pdm-python. [env var: PDM_IGNORE_SAVED_PYTHON]' complete -c pdm -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -l no-cache -d 'Disable the cache for the current command. [env var: PDM_NO_CACHE]' complete -c pdm -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -l non-interactive -d 'Don\'t show interactive prompts but use defaults. [env var: PDM_NON_INTERACTIVE]' complete -c pdm -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -l pep582 -d 'Print the command line to be eval\'d by the shell for PEP 582' complete -c pdm -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -l quiet -d 'Suppress output' complete -c pdm -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' complete -c pdm -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -l version -d 'Show the version and exit' # commands # add complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a add -d 'Add package(s) to pyproject.toml and install them' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l config-setting -d 'Pass options to the builder. options with a value must be specified after "=": `--config-setting=key(=value)` or `-Ckey(=value)`' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l dev -d 'Add packages into dev dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l dry-run -d 'Show the difference only and don\'t perform any action' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l editable -d 'Specify editable packages' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l fail-fast -d 'Abort on first installation error' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l frozen-lockfile -d 'Don\'t try to create or update the lockfile. [env var: PDM_FROZEN_LOCKFILE]' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l group -d 'Specify the target dependency group to add into' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l lockfile -d 'Specify another lockfile path. Default: pdm.lock. [env var: PDM_LOCKFILE]' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l no-editable -d 'Install non-editable versions for all packages. [env var: PDM_NO_EDITABLE]' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l no-isolation -d 'Disable isolation when building a source distribution that follows PEP 517, as in: build dependencies specified by PEP 518 must be already installed if this option is used.' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l no-self -d 'Don\'t install the project itself. [env var: PDM_NO_SELF]' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l no-sync -d 'Only write pyproject.toml and do not sync the working set' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l override -d 'Use the constraint file in pip-requirements format for overriding. [env var: PDM_OVERRIDE] This option can be used multiple times. See https://pip.pypa.io/en/stable/user_guide/#constraints-files' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l prerelease -d 'Allow prereleases to be pinned' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l save-compatible -d 'Save compatible version specifiers' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l save-exact -d 'Save exact version specifiers' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l save-minimum -d 'Save minimum version specifiers' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l save-safe-compatible -d 'Save safe compatible version specifiers' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l save-wildcard -d 'Save wildcard version specifiers' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l skip -d 'Skip some tasks and/or hooks by their comma-separated names. Can be supplied multiple times. Use ":all" to skip all hooks. Use ":pre" and ":post" to skip all pre or post hooks.' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l stable -d 'Only allow stable versions to be pinned' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l unconstrained -d 'Ignore the version constraints in pyproject.toml and overwrite with new ones from the resolution result' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l update-all -d 'Update all dependencies and sub-dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l update-eager -d 'Try to update the packages and their dependencies recursively' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l update-reuse -d 'Reuse pinned versions already present in lock file if possible' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l update-reuse-installed -d 'Reuse installed packages if possible' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l venv -d 'Run the command in the virtual environment with the given key. [env var: PDM_IN_VENV]' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # build complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a build -d 'Build artifacts for distribution' complete -c pdm -A -n '__fish_seen_subcommand_from build' -l config-setting -d 'Pass options to the builder. options with a value must be specified after "=": `--config-setting=key(=value)` or `-Ckey(=value)`' complete -c pdm -A -n '__fish_seen_subcommand_from build' -l dest -d 'Target directory to put artifacts' complete -c pdm -A -n '__fish_seen_subcommand_from build' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from build' -l no-clean -d 'Do not clean the target directory' complete -c pdm -A -n '__fish_seen_subcommand_from build' -l no-isolation -d 'Disable isolation when building a source distribution that follows PEP 517, as in: build dependencies specified by PEP 518 must be already installed if this option is used.' complete -c pdm -A -n '__fish_seen_subcommand_from build' -l no-sdist -d 'Don\'t build source tarballs' complete -c pdm -A -n '__fish_seen_subcommand_from build' -l no-wheel -d 'Don\'t build wheels' complete -c pdm -A -n '__fish_seen_subcommand_from build' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from build' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from build' -l skip -d 'Skip some tasks and/or hooks by their comma-separated names. Can be supplied multiple times. Use ":all" to skip all hooks. Use ":pre" and ":post" to skip all pre or post hooks.' complete -c pdm -A -n '__fish_seen_subcommand_from build' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # cache complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a cache -d 'Control the caches of PDM' complete -c pdm -A -n '__fish_seen_subcommand_from cache' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from cache' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from cache' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # cache subcommands set -l cache_subcommands clear info list remove # cache clear complete -c pdm -f -n '__fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from $cache_subcommands' -a clear -d 'Clean all the files under cache directory' complete -c pdm -A -n '__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from clear' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from clear' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from clear' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # cache info complete -c pdm -f -n '__fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from $cache_subcommands' -a info -d 'Show the info and current size of caches' complete -c pdm -A -n '__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from info' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from info' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from info' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # cache list complete -c pdm -f -n '__fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from $cache_subcommands' -a list -d 'List the built wheels stored in the cache' complete -c pdm -A -n '__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from list' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from list' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from list' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # cache remove complete -c pdm -f -n '__fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from $cache_subcommands' -a remove -d 'Remove files matching the given pattern' complete -c pdm -A -n '__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from remove' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from remove' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from remove' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # completion complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a completion -d 'Generate completion scripts for the given shell' complete -c pdm -A -n '__fish_seen_subcommand_from completion' -l help -d 'Show this help message and exit.' # config complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a config -d 'Display the current configuration' complete -c pdm -A -n '__fish_seen_subcommand_from config' -l delete -d 'Unset a configuration key' complete -c pdm -A -n '__fish_seen_subcommand_from config' -l edit -d 'Edit the configuration file in the default editor(defined by EDITOR env var)' complete -c pdm -A -n '__fish_seen_subcommand_from config' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from config' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from config' -l local -d 'Set config in the project\'s local configuration file' complete -c pdm -A -n '__fish_seen_subcommand_from config' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from config' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from config' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # export complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a export -d 'Export the locked packages set to other formats' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l dev -d 'Select dev dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l editable-self -d 'Include the project itself as an editable dependency' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l expandvars -d 'Expand environment variables in requirements' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l format -d 'Only requirements.txt is supported for now.' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l group -d 'Select group of optional-dependencies separated by comma or dependency-groups (with `-d`). Can be supplied multiple times, use ":all" to include all groups under the same species.' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l lockfile -d 'Specify another lockfile path. Default: pdm.lock. [env var: PDM_LOCKFILE]' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l no-default -d 'Don\'t include dependencies from the default group' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l no-extras -d 'Strip extras from the requirements' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l no-markers -d '(DEPRECATED)Don\'t include platform markers' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l output -d 'Write output to the given file, or print to stdout if not given' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l production -d 'Unselect dev dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l pyproject -d 'Read the list of packages from pyproject.toml' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l self -d 'Include the project itself' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l without -d 'Exclude groups of optional-dependencies or dependency-groups' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l without-hashes -d 'Don\'t include artifact hashes' # fix complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a fix -d 'Fix the project problems according to the latest version of PDM' complete -c pdm -A -n '__fish_seen_subcommand_from fix' -l dry-run -d 'Only show the problems' complete -c pdm -A -n '__fish_seen_subcommand_from fix' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from fix' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from fix' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from fix' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from fix' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # import complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a import -d 'Import project metadata from other formats' complete -c pdm -A -n '__fish_seen_subcommand_from import' -l dev -d 'import packages into dev dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from import' -l format -d 'Specify the file format explicitly' complete -c pdm -A -n '__fish_seen_subcommand_from import' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from import' -l group -d 'Specify the target dependency group to import into' complete -c pdm -A -n '__fish_seen_subcommand_from import' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from import' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from import' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from import' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # info complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a info -d 'Show the project information' complete -c pdm -A -n '__fish_seen_subcommand_from info' -l env -d 'Show PEP 508 environment markers' complete -c pdm -A -n '__fish_seen_subcommand_from info' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from info' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from info' -l json -d 'Dump the information in JSON' complete -c pdm -A -n '__fish_seen_subcommand_from info' -l packages -d 'Show the local packages root' complete -c pdm -A -n '__fish_seen_subcommand_from info' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from info' -l python -d 'Show the interpreter path' complete -c pdm -A -n '__fish_seen_subcommand_from info' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from info' -l venv -d 'Run the command in the virtual environment with the given key. [env var: PDM_IN_VENV]' complete -c pdm -A -n '__fish_seen_subcommand_from info' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' complete -c pdm -A -n '__fish_seen_subcommand_from info' -l where -d 'Show the project root path' # init complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a init -d 'Initialize a pyproject.toml for PDM. Built-in templates: - default: `pdm init`, A simple template with a basic structure. - minimal: `pdm init minimal`, A minimal template with only `pyproject.toml`. ' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l backend -d 'Specify the build backend, which implies --dist' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l cookiecutter -d 'Use Cookiecutter to generate project [installed]' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l copier -d 'Use Copier to generate project [installed]' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l dist -d 'Create a package for distribution' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l license -d 'Specify the license (SPDX name)' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l non-interactive -d 'Don\'t ask questions but use default values' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l overwrite -d 'Overwrite existing files' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l project-version -d 'Specify the project\'s version' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l python -d 'Specify the Python version/path to use' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l skip -d 'Skip some tasks and/or hooks by their comma-separated names. Can be supplied multiple times. Use ":all" to skip all hooks. Use ":pre" and ":post" to skip all pre or post hooks.' complete -c pdm -A -n '__fish_seen_subcommand_from init' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # install complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a install -d 'Install dependencies from lock file' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l check -d 'Check if the lock file is up to date and fail otherwise' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l config-setting -d 'Pass options to the builder. options with a value must be specified after "=": `--config-setting=key(=value)` or `-Ckey(=value)`' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l dev -d 'Select dev dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l dry-run -d 'Show the difference only and don\'t perform any action' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l fail-fast -d 'Abort on first installation error' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l frozen-lockfile -d 'Don\'t try to create or update the lockfile. [env var: PDM_FROZEN_LOCKFILE]' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l group -d 'Select group of optional-dependencies separated by comma or dependency-groups (with `-d`). Can be supplied multiple times, use ":all" to include all groups under the same species.' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l lockfile -d 'Specify another lockfile path. Default: pdm.lock. [env var: PDM_LOCKFILE]' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l no-default -d 'Don\'t include dependencies from the default group' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l no-editable -d 'Install non-editable versions for all packages. [env var: PDM_NO_EDITABLE]' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l no-isolation -d 'Disable isolation when building a source distribution that follows PEP 517, as in: build dependencies specified by PEP 518 must be already installed if this option is used.' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l no-self -d 'Don\'t install the project itself. [env var: PDM_NO_SELF]' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l override -d 'Use the constraint file in pip-requirements format for overriding. [env var: PDM_OVERRIDE] This option can be used multiple times. See https://pip.pypa.io/en/stable/user_guide/#constraints-files' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l plugins -d 'Install the plugins specified in pyproject.toml' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l production -d 'Unselect dev dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l skip -d 'Skip some tasks and/or hooks by their comma-separated names. Can be supplied multiple times. Use ":all" to skip all hooks. Use ":pre" and ":post" to skip all pre or post hooks.' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l venv -d 'Run the command in the virtual environment with the given key. [env var: PDM_IN_VENV]' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l without -d 'Exclude groups of optional-dependencies or dependency-groups' # list complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a list -d 'List packages installed in the current working set' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l csv -d 'Output dependencies in CSV document format' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l exclude -d 'Exclude dependency groups from the output' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l fields -d 'Select information to output as a comma separated string. All fields: groups,homepage,licenses,location,name,version.' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l freeze -d 'Show the installed dependencies in pip\'s requirements.txt format' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l graph -d 'Display a tree of dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l include -d 'Dependency groups to include in the output. By default all are included' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l json -d 'Output dependencies in JSON document format' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l markdown -d 'Output dependencies and legal notices in markdown document format - best effort basis' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l resolve -d 'Resolve all requirements to output licenses (instead of just showing those currently installed)' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l reverse -d 'Reverse the dependency tree' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l sort -d 'Sort the output using a given field name. If nothing is set, no sort is applied. Multiple fields can be combined with \',\'.' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l venv -d 'Run the command in the virtual environment with the given key. [env var: PDM_IN_VENV]' complete -c pdm -A -n '__fish_seen_subcommand_from list' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # lock complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a lock -d 'Resolve and lock dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l append -d 'Append the result to the current lock file' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l check -d 'Check if the lock file is up to date and quit' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l config-setting -d 'Pass options to the builder. options with a value must be specified after "=": `--config-setting=key(=value)` or `-Ckey(=value)`' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l dev -d 'Select dev dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l exclude-newer -d 'Exclude packages newer than the given UTC date in format `YYYY-MM-DD[THH:MM:SSZ]`' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l group -d 'Select group of optional-dependencies separated by comma or dependency-groups (with `-d`). Can be supplied multiple times, use ":all" to include all groups under the same species.' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l implementation -d 'The Python implementation to lock for. E.g. `cpython`, `pypy`, `pyston`' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l lockfile -d 'Specify another lockfile path. Default: pdm.lock. [env var: PDM_LOCKFILE]' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l no-cross-platform -d '[DEPRECATED] Only lock packages for the current platform' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l no-default -d 'Don\'t include dependencies from the default group' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l no-isolation -d 'Disable isolation when building a source distribution that follows PEP 517, as in: build dependencies specified by PEP 518 must be already installed if this option is used.' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l no-static-urls -d '[DEPRECATED] Do not store static file URLs in the lockfile' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l override -d 'Use the constraint file in pip-requirements format for overriding. [env var: PDM_OVERRIDE] This option can be used multiple times. See https://pip.pypa.io/en/stable/user_guide/#constraints-files' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l platform -d 'The platform to lock for. E.g. `windows`, `linux`, `macos`, `manylinux_2_17_x86_64`. See docs for available choices: http://pdm-project.org/en/latest/usage/lock-targets/' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l production -d 'Unselect dev dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l python -d 'The Python range to lock for. E.g. `>=3.9`, `==3.12.*`' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l refresh -d 'Refresh the content hash and file hashes in the lock file' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l skip -d 'Skip some tasks and/or hooks by their comma-separated names. Can be supplied multiple times. Use ":all" to skip all hooks. Use ":pre" and ":post" to skip all pre or post hooks.' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l static-urls -d '[DEPRECATED] Store static file URLs in the lockfile' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l strategy -d 'Specify lock strategy (cross_platform, static_urls, direct_minimal_versions, inherit_metadata). Add \'no_\' prefix to disable. Can be supplied multiple times or split by comma.' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l update-reuse -d 'Reuse pinned versions already present in lock file if possible' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l update-reuse-installed -d 'Reuse installed packages if possible' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l without -d 'Exclude groups of optional-dependencies or dependency-groups' # outdated complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a outdated -d 'Check for outdated packages and list the latest versions on indexes.' complete -c pdm -A -n '__fish_seen_subcommand_from outdated' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from outdated' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from outdated' -l include-sub -d 'Include sub-dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from outdated' -l json -d 'Output in JSON format' complete -c pdm -A -n '__fish_seen_subcommand_from outdated' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from outdated' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from outdated' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # plugin complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a plugin -d 'Manage the PDM program itself (previously known as plugin)' complete -c pdm -A -n '__fish_seen_subcommand_from plugin' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from plugin' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from plugin' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # plugin subcommands set -l plugin_subcommands add list remove update # plugin add complete -c pdm -f -n '__fish_seen_subcommand_from plugin; and not __fish_seen_subcommand_from $plugin_subcommands' -a add -d 'Install packages to the PDM\'s environment' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from add' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from add' -l pip-args -d 'Arguments that will be passed to pip install' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from add' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from add' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # plugin list complete -c pdm -f -n '__fish_seen_subcommand_from plugin; and not __fish_seen_subcommand_from $plugin_subcommands' -a list -d 'List all packages installed with PDM' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from list' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from list' -l plugins -d 'List plugins only' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from list' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from list' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # plugin remove complete -c pdm -f -n '__fish_seen_subcommand_from plugin; and not __fish_seen_subcommand_from $plugin_subcommands' -a remove -d 'Remove packages from PDM\'s environment' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from remove' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from remove' -l pip-args -d 'Arguments that will be passed to pip uninstall' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from remove' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from remove' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from remove' -l yes -d 'Answer yes on the question' # plugin update complete -c pdm -f -n '__fish_seen_subcommand_from plugin; and not __fish_seen_subcommand_from $plugin_subcommands' -a update -d 'Update PDM itself' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from update' -l head -d 'Update to the latest commit on the main branch' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from update' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from update' -l no-frozen-deps -d 'Do not install frozen dependency versions' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from update' -l pip-args -d 'Additional arguments that will be passed to pip install' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from update' -l pre -d 'Update to the latest prerelease version' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from update' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from update' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # publish complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a publish -d 'Build and publish the project to PyPI' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l ca-certs -d 'The path to a PEM-encoded Certificate Authority bundle to use for publish server validation [env var: PDM_PUBLISH_CA_CERTS]' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l comment -d 'The comment to include with the distribution file.' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l dest -d 'The directory to upload the package from' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l identity -d 'GPG identity used to sign files.' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l no-build -d 'Don\'t build the package before publishing' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l no-very-ssl -d 'Disable SSL verification' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l password -d 'The password to access the repository [env var: PDM_PUBLISH_PASSWORD]' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l repository -d 'The repository name or url to publish the package to [env var: PDM_PUBLISH_REPO]' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l sign -d 'Upload the package with PGP signature' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l skip -d 'Skip some tasks and/or hooks by their comma-separated names. Can be supplied multiple times. Use ":all" to skip all hooks. Use ":pre" and ":post" to skip all pre or post hooks.' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l skip-existing -d 'Skip uploading files that already exist. This may not work with some repository implementations.' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l username -d 'The username to access the repository [env var: PDM_PUBLISH_USERNAME]' complete -c pdm -A -n '__fish_seen_subcommand_from publish' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # py complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a py -d 'Manage installed Python interpreters' complete -c pdm -A -n '__fish_seen_subcommand_from py' -l help -d 'Show this help message and exit.' # py subcommands set -l py_subcommands find install link list remove # py find complete -c pdm -f -n '__fish_seen_subcommand_from py; and not __fish_seen_subcommand_from $py_subcommands' -a find -d 'Search for a Python interpreter' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from find' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from find' -l managed -d 'Only find interpreters managed by PDM' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from find' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from find' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # py install complete -c pdm -f -n '__fish_seen_subcommand_from py; and not __fish_seen_subcommand_from $py_subcommands' -a install -d 'Install a Python interpreter with PDM' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from install' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from install' -l list -d 'List all available Python versions' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from install' -l min -d 'Use minimum instead of highest version for installation if `version` is left empty' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from install' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from install' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # py link complete -c pdm -f -n '__fish_seen_subcommand_from py; and not __fish_seen_subcommand_from $py_subcommands' -a link -d 'Link an external Python interpreter to PDM' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from link' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from link' -l name -d 'The name of the link' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from link' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from link' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # py list complete -c pdm -f -n '__fish_seen_subcommand_from py; and not __fish_seen_subcommand_from $py_subcommands' -a list -d 'List all Python interpreters installed with PDM' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from list' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from list' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from list' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # py remove complete -c pdm -f -n '__fish_seen_subcommand_from py; and not __fish_seen_subcommand_from $py_subcommands' -a remove -d 'Remove a Python interpreter installed with PDM' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from remove' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from remove' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from py; and __fish_seen_subcommand_from remove' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # python complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a python -d 'Manage installed Python interpreters' complete -c pdm -A -n '__fish_seen_subcommand_from python' -l help -d 'Show this help message and exit.' # python subcommands set -l python_subcommands find install link list remove # python find complete -c pdm -f -n '__fish_seen_subcommand_from python; and not __fish_seen_subcommand_from $python_subcommands' -a find -d 'Search for a Python interpreter' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from find' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from find' -l managed -d 'Only find interpreters managed by PDM' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from find' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from find' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # python install complete -c pdm -f -n '__fish_seen_subcommand_from python; and not __fish_seen_subcommand_from $python_subcommands' -a install -d 'Install a Python interpreter with PDM' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from install' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from install' -l list -d 'List all available Python versions' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from install' -l min -d 'Use minimum instead of highest version for installation if `version` is left empty' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from install' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from install' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # python link complete -c pdm -f -n '__fish_seen_subcommand_from python; and not __fish_seen_subcommand_from $python_subcommands' -a link -d 'Link an external Python interpreter to PDM' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from link' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from link' -l name -d 'The name of the link' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from link' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from link' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # python list complete -c pdm -f -n '__fish_seen_subcommand_from python; and not __fish_seen_subcommand_from $python_subcommands' -a list -d 'List all Python interpreters installed with PDM' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from list' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from list' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from list' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # python remove complete -c pdm -f -n '__fish_seen_subcommand_from python; and not __fish_seen_subcommand_from $python_subcommands' -a remove -d 'Remove a Python interpreter installed with PDM' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from remove' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from remove' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from python; and __fish_seen_subcommand_from remove' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # remove complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a remove -d 'Remove packages from pyproject.toml' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l config-setting -d 'Pass options to the builder. options with a value must be specified after "=": `--config-setting=key(=value)` or `-Ckey(=value)`' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l dev -d 'Remove packages from dev dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l dry-run -d 'Show the difference only and don\'t perform any action' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l fail-fast -d 'Abort on first installation error' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l frozen-lockfile -d 'Don\'t try to create or update the lockfile. [env var: PDM_FROZEN_LOCKFILE]' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l group -d 'Specify the target dependency group to remove from' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l lockfile -d 'Specify another lockfile path. Default: pdm.lock. [env var: PDM_LOCKFILE]' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l no-editable -d 'Install non-editable versions for all packages. [env var: PDM_NO_EDITABLE]' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l no-isolation -d 'Disable isolation when building a source distribution that follows PEP 517, as in: build dependencies specified by PEP 518 must be already installed if this option is used.' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l no-self -d 'Don\'t install the project itself. [env var: PDM_NO_SELF]' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l no-sync -d 'Only write pyproject.toml and do not uninstall packages' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l override -d 'Use the constraint file in pip-requirements format for overriding. [env var: PDM_OVERRIDE] This option can be used multiple times. See https://pip.pypa.io/en/stable/user_guide/#constraints-files' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l skip -d 'Skip some tasks and/or hooks by their comma-separated names. Can be supplied multiple times. Use ":all" to skip all hooks. Use ":pre" and ":post" to skip all pre or post hooks.' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l venv -d 'Run the command in the virtual environment with the given key. [env var: PDM_IN_VENV]' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # run complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a run -d 'Run commands or scripts with local packages loaded' complete -c pdm -A -n '__fish_seen_subcommand_from run' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from run' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from run' -l json -d 'Output all scripts infos in JSON' complete -c pdm -A -n '__fish_seen_subcommand_from run' -l list -d 'Show all available scripts defined in pyproject.toml' complete -c pdm -A -n '__fish_seen_subcommand_from run' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from run' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from run' -l recreate -d 'Recreate the script environment for self-contained scripts' complete -c pdm -A -n '__fish_seen_subcommand_from run' -l site-packages -d 'Load site-packages from the selected interpreter' complete -c pdm -A -n '__fish_seen_subcommand_from run' -l skip -d 'Skip some tasks and/or hooks by their comma-separated names. Can be supplied multiple times. Use ":all" to skip all hooks. Use ":pre" and ":post" to skip all pre or post hooks.' complete -c pdm -A -n '__fish_seen_subcommand_from run' -l venv -d 'Run the command in the virtual environment with the given key. [env var: PDM_IN_VENV]' complete -c pdm -A -n '__fish_seen_subcommand_from run' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # search complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a search -d 'Search for PyPI packages' complete -c pdm -A -n '__fish_seen_subcommand_from search' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from search' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from search' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # self complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a self -d 'Manage the PDM program itself (previously known as plugin)' complete -c pdm -A -n '__fish_seen_subcommand_from self' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from self' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from self' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # self subcommands set -l self_subcommands add list remove update # self add complete -c pdm -f -n '__fish_seen_subcommand_from self; and not __fish_seen_subcommand_from $self_subcommands' -a add -d 'Install packages to the PDM\'s environment' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from add' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from add' -l pip-args -d 'Arguments that will be passed to pip install' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from add' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from add' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # self list complete -c pdm -f -n '__fish_seen_subcommand_from self; and not __fish_seen_subcommand_from $self_subcommands' -a list -d 'List all packages installed with PDM' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from list' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from list' -l plugins -d 'List plugins only' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from list' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from list' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # self remove complete -c pdm -f -n '__fish_seen_subcommand_from self; and not __fish_seen_subcommand_from $self_subcommands' -a remove -d 'Remove packages from PDM\'s environment' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from remove' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from remove' -l pip-args -d 'Arguments that will be passed to pip uninstall' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from remove' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from remove' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from remove' -l yes -d 'Answer yes on the question' # self update complete -c pdm -f -n '__fish_seen_subcommand_from self; and not __fish_seen_subcommand_from $self_subcommands' -a update -d 'Update PDM itself' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from update' -l head -d 'Update to the latest commit on the main branch' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from update' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from update' -l no-frozen-deps -d 'Do not install frozen dependency versions' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from update' -l pip-args -d 'Additional arguments that will be passed to pip install' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from update' -l pre -d 'Update to the latest prerelease version' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from update' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from self; and __fish_seen_subcommand_from update' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # show complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a show -d 'Show the package information' complete -c pdm -A -n '__fish_seen_subcommand_from show' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from show' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from show' -l keywords -d 'Show keywords' complete -c pdm -A -n '__fish_seen_subcommand_from show' -l license -d 'Show license' complete -c pdm -A -n '__fish_seen_subcommand_from show' -l name -d 'Show name' complete -c pdm -A -n '__fish_seen_subcommand_from show' -l platform -d 'Show platform' complete -c pdm -A -n '__fish_seen_subcommand_from show' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from show' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from show' -l summary -d 'Show summary' complete -c pdm -A -n '__fish_seen_subcommand_from show' -l venv -d 'Run the command in the virtual environment with the given key. [env var: PDM_IN_VENV]' complete -c pdm -A -n '__fish_seen_subcommand_from show' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' complete -c pdm -A -n '__fish_seen_subcommand_from show' -l version -d 'Show version' # sync complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a sync -d 'Synchronize the current working set with lock file' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l clean -d 'Clean packages not in the lockfile' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l clean-unselected -d 'Only keep the selected packages' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l config-setting -d 'Pass options to the builder. options with a value must be specified after "=": `--config-setting=key(=value)` or `-Ckey(=value)`' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l dev -d 'Select dev dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l dry-run -d 'Show the difference only and don\'t perform any action' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l fail-fast -d 'Abort on first installation error' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l group -d 'Select group of optional-dependencies separated by comma or dependency-groups (with `-d`). Can be supplied multiple times, use ":all" to include all groups under the same species.' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l lockfile -d 'Specify another lockfile path. Default: pdm.lock. [env var: PDM_LOCKFILE]' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l no-default -d 'Don\'t include dependencies from the default group' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l no-editable -d 'Install non-editable versions for all packages. [env var: PDM_NO_EDITABLE]' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l no-isolation -d 'Disable isolation when building a source distribution that follows PEP 517, as in: build dependencies specified by PEP 518 must be already installed if this option is used.' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l no-self -d 'Don\'t install the project itself. [env var: PDM_NO_SELF]' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l production -d 'Unselect dev dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l reinstall -d 'Force reinstall existing dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l skip -d 'Skip some tasks and/or hooks by their comma-separated names. Can be supplied multiple times. Use ":all" to skip all hooks. Use ":pre" and ":post" to skip all pre or post hooks.' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l venv -d 'Run the command in the virtual environment with the given key. [env var: PDM_IN_VENV]' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l without -d 'Exclude groups of optional-dependencies or dependency-groups' # update complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a update -d 'Update package(s) in pyproject.toml' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l config-setting -d 'Pass options to the builder. options with a value must be specified after "=": `--config-setting=key(=value)` or `-Ckey(=value)`' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l dev -d 'Select dev dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l fail-fast -d 'Abort on first installation error' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l frozen-lockfile -d 'Don\'t try to create or update the lockfile. [env var: PDM_FROZEN_LOCKFILE]' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l group -d 'Select group of optional-dependencies separated by comma or dependency-groups (with `-d`). Can be supplied multiple times, use ":all" to include all groups under the same species.' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l lockfile -d 'Specify another lockfile path. Default: pdm.lock. [env var: PDM_LOCKFILE]' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l no-default -d 'Don\'t include dependencies from the default group' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l no-editable -d 'Install non-editable versions for all packages. [env var: PDM_NO_EDITABLE]' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l no-isolation -d 'Disable isolation when building a source distribution that follows PEP 517, as in: build dependencies specified by PEP 518 must be already installed if this option is used.' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l no-self -d 'Don\'t install the project itself. [env var: PDM_NO_SELF]' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l no-sync -d 'Only update lock file but do not sync packages' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l outdated -d 'Show the difference only without modifying the lockfile content' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l override -d 'Use the constraint file in pip-requirements format for overriding. [env var: PDM_OVERRIDE] This option can be used multiple times. See https://pip.pypa.io/en/stable/user_guide/#constraints-files' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l prerelease -d 'Allow prereleases to be pinned' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l production -d 'Unselect dev dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l save-compatible -d 'Save compatible version specifiers' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l save-exact -d 'Save exact version specifiers' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l save-minimum -d 'Save minimum version specifiers' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l save-safe-compatible -d 'Save safe compatible version specifiers' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l save-wildcard -d 'Save wildcard version specifiers' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l skip -d 'Skip some tasks and/or hooks by their comma-separated names. Can be supplied multiple times. Use ":all" to skip all hooks. Use ":pre" and ":post" to skip all pre or post hooks.' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l stable -d 'Only allow stable versions to be pinned' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l top -d 'Only update those listed in pyproject.toml' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l unconstrained -d 'Ignore the version constraints in pyproject.toml and overwrite with new ones from the resolution result' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l update-all -d 'Update all dependencies and sub-dependencies' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l update-eager -d 'Try to update the packages and their dependencies recursively' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l update-reuse -d 'Reuse pinned versions already present in lock file if possible' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l update-reuse-installed -d 'Reuse installed packages if possible' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l venv -d 'Run the command in the virtual environment with the given key. [env var: PDM_IN_VENV]' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l without -d 'Exclude groups of optional-dependencies or dependency-groups' # use complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a use -d 'Use the given python version or path as base interpreter. If not found, PDM will try to install one.' complete -c pdm -A -n '__fish_seen_subcommand_from use' -l auto-install-max -d 'If `python` argument not given, auto install maximum best match - otherwise has no effect' complete -c pdm -A -n '__fish_seen_subcommand_from use' -l auto-install-min -d 'If `python` argument not given, auto install minimal best match - otherwise has no effect' complete -c pdm -A -n '__fish_seen_subcommand_from use' -l first -d 'Select the first matched interpreter - no auto install' complete -c pdm -A -n '__fish_seen_subcommand_from use' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from use' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from use' -l ignore-remembered -d 'Ignore the remembered selection' complete -c pdm -A -n '__fish_seen_subcommand_from use' -l no-version-file -d 'Do not write .python-version file' complete -c pdm -A -n '__fish_seen_subcommand_from use' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from use' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from use' -l skip -d 'Skip some tasks and/or hooks by their comma-separated names. Can be supplied multiple times. Use ":all" to skip all hooks. Use ":pre" and ":post" to skip all pre or post hooks.' complete -c pdm -A -n '__fish_seen_subcommand_from use' -l venv -d 'Use the interpreter in the virtual environment with the given name' complete -c pdm -A -n '__fish_seen_subcommand_from use' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # venv complete -c pdm -f -n '__fish_pdm_a919b69078acdf0a_complete_no_subcommand' -a venv -d 'Virtualenv management' complete -c pdm -A -n '__fish_seen_subcommand_from venv' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from venv' -l path -d 'Show the path to the given virtualenv' complete -c pdm -A -n '__fish_seen_subcommand_from venv' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__ [env var: PDM_PROJECT]' complete -c pdm -A -n '__fish_seen_subcommand_from venv' -l python -d 'Show the python interpreter path for the given virtualenv' # venv subcommands set -l venv_subcommands activate create list purge remove # venv activate complete -c pdm -f -n '__fish_seen_subcommand_from venv; and not __fish_seen_subcommand_from $venv_subcommands' -a activate -d 'Print the command to activate the virtualenv with the given name' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from activate' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from activate' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from activate' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # venv create complete -c pdm -f -n '__fish_seen_subcommand_from venv; and not __fish_seen_subcommand_from $venv_subcommands' -a create -d 'Create a virtualenv' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from create' -l force -d 'Recreate if the virtualenv already exists' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from create' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from create' -l name -d 'Specify the name of the virtualenv' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from create' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from create' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from create' -l with -d 'Specify the backend to create the virtualenv' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from create' -l with-pip -d 'Install pip with the virtualenv' # venv list complete -c pdm -f -n '__fish_seen_subcommand_from venv; and not __fish_seen_subcommand_from $venv_subcommands' -a list -d 'List all virtualenvs associated with this project' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from list' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from list' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from list' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # venv purge complete -c pdm -f -n '__fish_seen_subcommand_from venv; and not __fish_seen_subcommand_from $venv_subcommands' -a purge -d 'Purge selected/all created Virtualenvs' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from purge' -l force -d 'Force purging without prompting for confirmation' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from purge' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from purge' -l interactive -d 'Interactively purge selected Virtualenvs' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from purge' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from purge' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' # venv remove complete -c pdm -f -n '__fish_seen_subcommand_from venv; and not __fish_seen_subcommand_from $venv_subcommands' -a remove -d 'Remove the virtualenv with the given name' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from remove' -l help -d 'Show this help message and exit.' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from remove' -l quiet -d 'Suppress output' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from remove' -l verbose -d 'Use `-v` for detailed output and `-vv` for more detailed' complete -c pdm -A -n '__fish_seen_subcommand_from venv; and __fish_seen_subcommand_from remove' -l yes -d 'Answer yes on the following question' pdm-2.23.1/src/pdm/cli/completions/pdm.ps1000066400000000000000000000516541477560627500202740ustar00rootroot00000000000000# Powershell completion script for pdm if ((Test-Path Function:\TabExpansion) -and -not (Test-Path Function:\_pdm_completeBackup)) { Rename-Item Function:\TabExpansion _pdm_completeBackup } $PDM_PYTHON = "%{python_executable}" $PDM_PIP_INDEX = (& $PDM_PYTHON -m pdm config pypi.url).Trim() $CONFIG_DIR = "$env:LOCALAPPDATA\pdm" class Option { [string[]] $Opts [string[]] $Values Option([string[]] $opts) { $this.Opts = $opts } [Option] WithValues([string[]] $values) { $this.Values = $values return $this } [bool] Match([string] $word) { foreach ($opt in $this.Opts) { if ($word -eq $opt) { return $true } } return $false } [bool] TakesArg() { return $null -ne $this.Values } } class Completer { [string []] $params [bool] $multiple = $false [Option[]] $opts = @() Completer() { } [string[]] Complete([string[]] $words) { $expectArg = $null $lastWord = $words[-1] $paramUsed = $false if ($words.Length -gt 1) { foreach ($word in $words[0..($words.Length - 2)]) { if ($expectArg) { $expectArg = $null continue } if ($word.StartsWith("-")) { $opt = $this.opts.Where( { $_.Match($word) })[0] if ($null -ne $opt -and $opt.TakesArg()) { $expectArg = $opt } } elseif (-not $this.multiple) { $paramUsed = $true } } } $candidates = @() if ($lastWord.StartsWith("-")) { foreach ($opt in $this.opts) { $candidates += $opt.Opts } } elseif ($null -ne $expectArg) { $candidates = $expectArg.Values } elseif ($null -ne $this.params -and -not $paramUsed) { $candidates = $this.params } return $candidates.Where( { $_.StartsWith($lastWord) }) } [void] AddOpts([Option[]] $options) { $this.opts += $options } [void] AddParams([string[]] $params, [bool]$multiple = $false) { $this.params = $params $this.multiple = $multiple } } function getSections() { if (-not (Test-Path -Path "pyproject.toml")) { return @() } [string[]] $sections = @() [bool] $inSection = $false foreach ($line in (Get-Content "pyproject.toml")) { if (($line -match ' *\[project\.optional-dependencies\]') -or ($line -match ' *\[tool\.pdm.dev-dependencies\]')) { $inSection = $true } elseif ($inSection -and ($line -match '(\S+) *= *\[')) { $sections += $Matches[1] } elseif ($line -like '`[*`]') { $inSection = $false } } return $sections } function _fetchPackageListFromPyPI() { if (-not (Test-Path -Path $CONFIG_DIR)) { mkdir $CONFIG_DIR } (Invoke-WebRequest $PDM_PIP_INDEX).Links | ForEach-Object { $_.innerText } | Out-File -FilePath "$CONFIG_DIR\.pypiPackages" } function getPyPIPackages() { # $cacheFile = "$CONFIG_DIR\.pypiPackages" # if (-not (Test-Path -Path $cacheFile) -or (Get-Item $cacheFile).LastWriteTime -lt (Get-Date).AddDays(-28)) { # _fetchPackageListFromPyPI # } # Get-Content $cacheFile } function getPdmPackages() { & $PDM_PYTHON -c " import sys if sys.version_info >= (3, 11): import tomllib else: import tomli as tomllib import os, re PACKAGE_REGEX = re.compile(r'^[A-Za-z][A-Za-z0-9._-]*') def get_packages(lines): return [PACKAGE_REGEX.match(line).group() for line in lines] with open('pyproject.toml', 'rb') as f: data = tomllib.load(f) packages = get_packages(data.get('project', {}).get('dependencies', [])) for reqs in data.get('project', {}).get('optional-dependencies', {}).values(): packages.extend(get_packages(reqs)) for reqs in data.get('tool', {}).get('pdm', {}).get('dev-dependencies', {}).values(): packages.extend(get_packages(reqs)) print(*set(packages), sep='\n') " } $_cachedConfigKeys = $null function getConfigKeys() { if ($null -eq $_cachedConfigKeys) { [string[]] $keys = @() $config = @(& $PDM_PYTHON -m pdm config) foreach ($line in $config) { if ($line -match ' *(\S+) *=') { $keys += $Matches[1] } } $_cachedConfigKeys = $keys } return $_cachedConfigKeys } function getScripts() { [string[]] $scripts = @() $packagesDir = (& $PDM_PYTHON -m pdm info --packages) if (Test-Path -Path "pyproject.toml") { [bool] $inScripts = $false foreach ($line in (Get-Content "pyproject.toml")) { if ($line -match ' *\[tool\.pdm\.scripts\]') { $inScripts = $true } elseif ($inScripts -and ($line -match '(\S+) *= *')) { $scripts += $Matches[1] } elseif ($line -like '`[*`]') { $inScripts = $false } } } if ($packagesDir -ne "None") { $scripts += (Get-ChildItem "$packagesDir\Scripts" | ForEach-Object { $_.Basename }) } return $scripts } function TabExpansion($line, $lastWord) { $lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart() if ($lastBlock -match "^pdm ") { [string[]]$words = $lastBlock.Split()[1..$lastBlock.Length] [string[]]$AllCommands = ( "add", "build", "cache", "completion", "config", "export", "fix", "import", "info", "init", "install", "list", "lock", "outdated", "plugin", "publish", "remove", "run", "search", "show", "sync", "update", "use", "python", "py" ) [string[]]$commands = $words.Where( { $_ -notlike "-*" }) $command = $commands[0] $completer = [Completer]::new() $completer.AddOpts(([Option]::new(("-h", "--help", "-v", "--verbose", "-q", "--quiet")))) $sectionOption = [Option]::new(@("-G", "--group", "--with", "--without")).WithValues(@(getSections)) $projectOption = [Option]::new(@("-p", "--project")).WithValues(@()) $skipOption = [Option]::new(@("-k", "--skip")).WithValues(@()) $venvOption = [Option]::new(@("--venv")).WithValues(@()) $formatOption = [Option]::new(@("-f", "--format")).WithValues(@("setuppy", "requirements", "poetry", "flit")) Switch ($command) { "add" { $completer.AddOpts(@( [Option]::new(( "-d", "--dev", "--save-compatible", "--save-wildcard", "--dry-run", "--save-exact", "--override", "--save-minimum", "--save-safe-compatible", "--update-eager", "--update-reuse", "--update-all", "-g", "--global", "--no-sync", "--no-editable", "--no-self", "-u", "--unconstrained", "--no-isolation", "-C", "--config-setting", "--stable", "--pre", "--prerelease", "-L", "--lockfile", "--fail-fast", "-x", "--frozen-lockfile", "--update-reuse-installed" )), $sectionOption, $projectOption, $venvOption, $skipOption [Option]::new(@("-e", "--editable")).WithValues(@(getPyPIPackages)) )) $completer.AddParams(@(getPyPIPackages), $true) break } "build" { $completer.AddOpts(@([Option]::new(@("-d", "--dest", "--no-clean", "--no-sdist", "--no-wheel", "-C", "--config-setting", "--no-isolation")), $projectOption, $skipOption)) } "cache" { $subCommand = $commands[1] switch ($subCommand) { "clear" { $completer.AddParams(@("wheels", "http", "hashes", "metadata"), $false) $command = $subCommand } "remove" {$command = $subCommand} "info" {$command = $subCommand} "list" {$command = $subCommand} default { $completer.AddParams(@("clear", "remove", "info", "list"), $false) } } break } "completion" { $completer.AddParams(@("powershell", "bash", "zsh", "fish")); break } "config" { $completer.AddOpts(@([Option]::new(@("--delete", "--global", "--local", "--edit", "-e", "-d", "-l", "-g")), $projectOption)) $completer.AddParams(@(getConfigKeys), $false) break } "export" { $completer.AddOpts(@( [Option]::new(@( "--dev", "--output", "--global", "--no-default", "--expandvars", "--prod", "--production", "-g", "-d", "-o", "--no-hashes", "--no-markers", "-L", "--lockfile", "--self", "--editable-self", "--no-extras" )), $formatOption, $sectionOption, $projectOption )) break } "fix" { $completer.AddOpts(@( [Option]::new(@("--dry-run", "-g", "--global")), $projectOption )) break } "import" { $completer.AddOpts(@( [Option]::new(@("--dev", "--global", "--no-default", "-g", "-d")), $formatOption, $sectionOption, $projectOption )) break } "info" { $completer.AddOpts( @( [Option]::new(@("--env", "--global", "-g", "--python", "--where", "--packages", "--json")), $projectOption, $venvOption )) break } "init" { $completer.AddOpts( @( [Option]::new(@( "-g", "--global", "--non-interactive", "-n", "--python", "--dist", "--lib", "--copier", "--cookiecutter", "--overwrite", "--license", "--project-version" )), $projectOption, $skipOption, [Option]::new(@("--backend")).WithValues(@("pdm-backend", "setuptools", "flit", "hatching")) )) break } "install" { $completer.AddOpts(@( [Option]::new(( "-d", "--dev", "-g", "--global", "--dry-run", "--no-default", "--frozen-lockfile", "--prod", "--production", "--no-editable", "--no-self", "-C", "--config-setting", "--no-isolation", "--check", "-L", "--lockfile", "--fail-fast", "-x", "--plugins", "--override" )), $sectionOption, $skipOption, $venvOption, $projectOption )) break } "list" { $completer.AddOpts( @( [Option]::new(@( "--graph", "--tree", "--global", "-g", "--reverse", "-r", "--freeze","--json", "--csv", "--markdown", "--fields", "--sort", "--include", "--exclude", "--resolve" )), $venvOption, $projectOption )) break } "lock" { $completer.AddOpts( @( [Option]::new(@( "--global", "-g", "-C", "--config-setting", "--no-isolation", "--refresh", "-L", "--lockfile", "--check", "--dev", "--prod", "--production", "-d", "--no-default", "--no-cross-platform", "--static-urls", "--no-static-urls", "--override", "--strategy", "-S", "--update-reuse", "--update-reuse-installed", "--exclude-newer", "--append", "--platform", "--python", "--implementation" )), $skipOption, $sectionOption, $projectOption )) break } "outdated" { $completer.AddOpts( @( [Option]::new(@("--json")), $projectOption )) break } "self" { $subCommand = $commands[1] switch ($subCommand) { "add" { $completer.AddOpts(([Option]::new(("--pip-args")))) $completer.AddParams(@(getPyPIPackages), $true) $command = $subCommand break } "remove" { $completer.AddOpts(([Option]::new(("--pip-args", "-y", "--yes")))) $command = $subCommand break } "list" { $completer.AddOpts(([Option]::new(("--plugins")))) $command = $subCommand break } "update" { $completer.AddOpts(([Option]::new(("--pip-args", "--head", "--pre")))) $command = $subCommand break } Default { $completer.AddParams(@("add", "remove", "list", "update"), $false) } } break } "publish" { $completer.AddOpts( @( [Option]::new(@( "-r", "--repository", "-u", "--username", "-P", "--password", "-S", "--sign", "-i", "--identity", "-c", "--comment", "--no-build", "--ca-certs", "--no-verify-ssl", "--skip-existing", "-d", "--dest" )), $skipOption, $projectOption )) break } "py" {} "python" { $subCommand = $commands[1] switch ($subCommand) { "list" { $command = $subCommand break } "remove" { $command = $subCommand break } "install" { $completer.AddOpts(([Option]::new(("--list", "--min")))) $command = $subCommand break } "find" { $completer.AddOpts(([Option]::new(("--managed")))) $command = $subCommand break } Default { break } } break } "remove" { $completer.AddOpts( @( [Option]::new(@( "--global", "-g", "--dev", "-d", "--dry-run", "--no-sync", "--no-editable", "--no-self", "--override", "-C", "--config-setting", "--no-isolation", "-L", "--lockfile", "--fail-fast", "-x", "--frozen-lockfile" )), $projectOption, $skipOption, $venvOption, $sectionOption )) $completer.AddParams(@(getPdmPackages), $true) break } "run" { $completer.AddOpts( @( [Option]::new(@("--global", "-g", "-l", "--list", "-s", "--site-packages", "--json", "--recreate")), $skipOption, $venvOption, $projectOption )) $completer.AddParams(@(getScripts), $false) break } "search" { break } "show" { $completer.AddOpts( @( [Option]::new(@("--global", "-g", "--name", "--version", "--summary", "--license", "--platform", "--keywords")), $venvOption, $projectOption )) break } "sync" { $completer.AddOpts(@( [Option]::new(( "-d", "--dev", "-g", "--global", "--no-default", "--clean", "--clean-unselected", "--only-keep", "--dry-run", "-r", "--reinstall", "--prod", "--production", "--no-editable", "--no-self", "--no-isolation", "-C", "--config-setting", "-L", "--lockfile", "--fail-fast", "-x" )), $sectionOption, $venvOption, $skipOption, $projectOption )) break } "update" { $completer.AddOpts(@( [Option]::new(( "-d", "--dev", "--save-compatible", "--prod", "--production", "--save-wildcard", "--save-exact", "--save-minimum", "--update-eager", "--update-reuse", "--update-all", "-g", "--global", "--dry-run", "--outdated", "--top", "-u", "--unconstrained", "--no-editable", "--no-self", "--no-isolation", "--no-sync", "--pre", "--prerelease", "-L", "--lockfile", "--fail-fast", "-x", "--frozen-lockfile", "-C", "--config-setting", "--update-reuse-installed", "--stable", "--override", "--save-safe-compatible" )), $sectionOption, $skipOption, $venvOption, $projectOption )) $completer.AddParams(@(getPdmPackages), $true) break } "use" { $completer.AddOpts( @( [Option]::new(@("--global", "-g", "-f", "--first", "-i", "--ignore-remembered", "--skip", "--auto-install-min", "--auto-install-max", "--no-version-file")), $venvOption, $projectOption )) break } "venv" { $subCommand = $commands[1] switch ($subCommand) { "create" { $command = $subCommand $completer.AddOpts(( [Option]::new(("--with", "-w")).WithValues(@("venv", "virtualenv", "conda")), [Option]::new(("--name", "-n")).WithValues(@()), [Option]::new(("--with-pip", "-f", "--force")) )) } "list" {$command = $subCommand} "remove" { $command = $subCommand $completer.AddOpts(([Option]::new(("-y", "--yes")))) } "activate" {$command = $subCommand} "purge" { $command = $subCommand $completer.AddOpts(([Option]::new(("-i", "--interactive", "--force", "-f")))) } Default { $completer.AddOpts(([Option]::new(("--python", "--path")))) $completer.AddParams(@("create", "list", "remove", "activate", "purge"), $false) break } } break } default { # No command $command = $null $completer.AddOpts(([Option]::new(("--pep582", "-I", "--ignore-python", "-c", "--config", "--no-cache", "-n", "--non-interactive")))) $completer.AddParams($AllCommands, $false) } } $start = [array]::IndexOf($words, $command) + 1 $completer.Complete($words[$start..$words.Length]) } elseif (Test-Path Function:\_pdm_completeBackup) { # Fall back on existing tab expansion _pdm_completeBackup $line $lastWord } } pdm-2.23.1/src/pdm/cli/completions/pdm.zsh000066400000000000000000001002311477560627500203570ustar00rootroot00000000000000#compdef pdm PDM_PYTHON="%{python_executable}" _pdm() { emulate -L zsh -o extended_glob typeset -A opt_args local context state state_descr line local curcontext=$curcontext ret=1 local -a arguments=( {-h,--help}'[Show help message and exit]' {-v,--verbose}'[Use `-v` for detailed output and `-vv` for more detailed]' {-q,--quiet}'[Suppress output]' ) local sub_commands=( 'add:Add package(s) to pyproject.toml and install them' 'build:Build artifacts for distribution' 'cache:Control the caches of PDM' 'completion:Generate completion scripts for the given shell' 'config:Display the current configuration' 'export:Export the locked packages set to other formats' 'fix:Fix the project problems according to the latest version of PDM' 'import:Import project metadata from other formats' 'info:Show the project information' 'init:Initialize a pyproject.toml for PDM' 'install:Install dependencies from lock file' 'list:List packages installed in the current working set' 'lock:Resolve and lock dependencies' 'self:Manage the PDM program itself (previously known as plugin)' 'outdated:Check for outdated packages and list the latest versions on indexes' 'publish:Build and publish the project to PyPI' 'python:Manage installed Python interpreters' 'py:Manage installed Python interpreters' 'remove:Remove packages from pyproject.toml' 'run:Run commands or scripts with local packages loaded' 'search:Search for PyPI packages' 'show:Show the package information' 'sync:Synchronize the current working set with lock file' 'update:Update package(s) in pyproject.toml' 'use:Use the given python version or path as base interpreter' 'venv:Virtualenv management' ) _arguments -s -C -A '-*' \ $arguments \ {-c,--config}'[Specify another config file path\[env var: PDM_CONFIG_FILE\]]' \ {-V,--version}'[Show the version and exit]' \ {-I,--ignore-python}'[Ignore the Python path saved in .pdm-python]' \ '--no-cache:Disable the cache for the current command. [env var: PDM_NO_CACHE]' \ '--pep582:Print the command line to be eval by the shell for PEP 582:shell:(zsh bash fish tcsh csh)' \ {-n,--non-interactive}"[Don't show interactive prompts but use defaults. \[env var: PDM_NON_INTERACTIVE\]]" \ '*:: :->_subcmds' \ && return 0 if (( CURRENT == 1 )); then _describe -t commands 'pdm subcommand' sub_commands return fi curcontext=${curcontext%:*}:$words[1] case $words[1] in add) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-d,--dev}'[Add packages into dev dependencies]' {-G,--group}'[Specify the target dependency group to add into]:group:_pdm_groups' {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' "--override+[Use the constraint file in pip-requirements format for overriding. \[env var: PDM_CONSTRAINT\] This option can be used multiple times. See https://pip.pypa.io/en/stable/user_guide/#constraints-files]:override:_files" '--no-sync[Only write pyproject.toml and do not sync the working set]' '--save-compatible[Save compatible version specifiers]' '--save-wildcard[Save wildcard version specifiers]' '--save-exact[Save exact version specifiers]' '--save-safe-compatible[Save safe compatible version specifiers]' '--save-minimum[Save minimum version specifiers]' '--update-reuse[Reuse pinned versions already present in lock file if possible]' '--update-reuse-installed[Reuse installed packages if possible]' '--update-eager[Try to update the packages and their dependencies recursively]' '--update-all[Update all dependencies and sub-dependencies]' '--no-editable[Install non-editable versions for all packages]' "--no-self[Don't install the project itself]" "--frozen-lockfile[Don't try to create or update the lockfile. \[env var: PDM_FROZEN_LOCKFILE\]]" '--venv[Run the command in the virtual environment with the given key. \[env var: PDM_IN_VENV\]]:venv:' {-k,--skip}'[Skip some tasks and/or hooks by their comma-separated names]' {-u,--unconstrained}'[Ignore the version constraints in pyproject.toml and overwrite with new ones from the resolution result]' {--pre,--prerelease}'[Allow prereleases to be pinned]' "--stable[Only allow stable versions to be pinned]" {-e+,--editable+}'[Specify editable packages]:packages' {-x,--fail-fast}'[Abort on first installation error]' {-C,--config-setting}'[Pass options to the backend. options with a value must be specified after "=": "--config-setting=key(=value)" or "-Ckey(=value)"]:cs:' "--no-isolation[do not isolate the build in a clean environment]" "--dry-run[Show the difference only without modifying the lockfile content]" ) ;; build) arguments+=( "--no-sdist[Don't build source tarballs]" "--no-wheel[Don't build wheels]" {-d+,--dest+}'[Target directory to put artifacts]:directory:_files -/' {-k,--skip}'[Skip some tasks and/or hooks by their comma-separated names]' '--no-clean[Do not clean the target directory]' {-C,--config-setting}'[Pass options to the backend. options with a value must be specified after "=": "--config-setting=key(=value)" or "-Ckey(=value)"]:cs:' "--no-isolation[do not isolate the build in a clean environment]" ) ;; cache) _arguments -C \ $arguments \ ': :->command' \ '*:: :->args' && ret=0 case $state in command) local -a actions=( "clear:Clean all the files under cache directory" "remove:Remove files matching the given pattern" "list:List the built wheels stored in the cache" "info:Show the info and current size of caches" ) _describe -t command 'pdm cache actions' actions && ret=0 ;; args) case $words[1] in clear) compadd -X type 'hashes' 'http' 'wheels' 'metadata' 'packages' && ret=0 ;; *) _message "pattern" && ret=0 ;; esac ;; esac return $ret ;; config) _arguments -s \ {-g,--global}'[Use the global project, supply the project root with `-p` option]' \ {-l,--local}"[Set config in the project's local configuration file]" \ {-d,--delete}'[Unset a configuration key]' \ {-e,--edit}'[Edit the configuration file in the default editor(defined by EDITOR env var)]' \ '1:key:->keys' \ '2:value:_files' && return 0 if [[ $state == keys ]]; then local l mbegin mend match keys=() for l in ${(f)"$(PDM_CHECK_UPDATE=0 command ${PDM_PYTHON} -m pdm config)"}; do if [[ $l == (#b)" "#(*)" = "(*) ]]; then keys+=("$match[1]:$match[2]") fi done _describe -t key "key" keys && return 0 fi ;; export) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-f+,--format+}"[Only requirements.txt is supported for now.]:format:(requirements)" "--no-hashes[Don't include artifact hashes]" "--no-markers[Don't include platform markers]" "--no-extras[Strip extras from the requirements]" "--expandvars[Expand environment variables in requirements]" "--self[Include the project itself]" "--editable-self[Include the project itself as an editable dependency]" {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' {-o+,--output+}"[Write output to the given file, or print to stdout if not given]:output file:_files" {-G+,--group+,--with+}'[Select group of optional-dependencies or dev-dependencies(with -d). Can be supplied multiple times, use ":all" to include all groups under the same species]:group:_pdm_groups' "--without+[Exclude groups of optional-dependencies or dev-dependencies]:group:_pdm_groups" {-d,--dev}"[Select dev dependencies]" {--prod,--production}"[Unselect dev dependencies]" "--no-default[Don't include dependencies from the default group]" ) ;; fix) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' '--dry-run[Only show the problems]' '1:problem:' ) ;; import) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-f+,--format+}"[Specify the file format explicitly]:format:(pipfile poetry flit requirements)" '1:filename:_files' ) ;; info) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' '--python[Show the interpreter path]' '--where[Show the project root path]' '--env[Show PEP 508 environment markers]' '--packages[Show the packages root]' '--json[Dump the information in JSON]' '--venv[Run the command in the virtual environment with the given key. \[env var: PDM_IN_VENV\]]:venv:' ) ;; init) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-n,--non-interactive}"[Don't ask questions but use default values]" {-k,--skip}'[Skip some tasks and/or hooks by their comma-separated names]' {-r,--overwrite}'[Overwrite existing files]' '--backend[Specify the build backend, which implies --dist]:backend:(pdm-backend setuptools hatchling flit)' {--dist,--lib}'[Create a package for distribution]' '--python[Specify the Python version/path to use]:python:' '--copier[Use Copier to generate project]' '--cookiecutter[Use Cookiecutter to generate project]' '--license[Specify the license (SPDX name)]:license:' "--project-version[Specify the project's version]:project_version:" '1:template:' ) ;; install) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-G+,--group+,--with+}'[Select group of optional-dependencies or dev-dependencies(with -d). Can be supplied multiple times, use ":all" to include all groups under the same species]:group:_pdm_groups' "--without+[Exclude groups of optional-dependencies or dev-dependencies]:group:_pdm_groups" {-d,--dev}"[Select dev dependencies]" {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' "--override+[Use the constraint file in pip-requirements format for overriding. \[env var: PDM_CONSTRAINT\] This option can be used multiple times. See https://pip.pypa.io/en/stable/user_guide/#constraints-files]:override:_files" {--prod,--production}"[Unselect dev dependencies]" {-k,--skip}'[Skip some tasks and/or hooks by their comma-separated names]' "--frozen-lockfile[Don't try to create or update the lockfile. \[env var: PDM_FROZEN_LOCKFILE\]]" "--no-default[Don\'t include dependencies from the default group]" '--no-editable[Install non-editable versions for all packages]' "--no-self[Don't install the project itself]" {-x,--fail-fast}'[Abort on first installation error]' {-C,--config-setting}'[Pass options to the backend. options with a value must be specified after "=": "--config-setting=key(=value)" or "-Ckey(=value)"]:cs:' "--no-isolation[do not isolate the build in a clean environment]" "--dry-run[Show the difference only without modifying the lock file content]" "--check[Check if the lock file is up to date and fail otherwise]" "--plugins[Install the plugins specified in pyproject.toml]" '--venv[Run the command in the virtual environment with the given key. \[env var: PDM_IN_VENV\]]:venv:' ) ;; list) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-r,--reverse}'[Reverse the dependency tree]' '--fields[Select information to output as a comma separated string.]:fields:' "--sort[Sort the output using a given field name. If nothing is set, no sort is applied. Multiple fields can be combined with ',']:sort:" '--json[Output dependencies in JSON document format]' '--csv[Output dependencies in CSV document format]' '--markdown[Output dependencies and legal notices in markdown document format - best effort basis]' {--tree,--graph}'[Display a tree of dependencies]' "--freeze[Show the installed dependencies as pip's requirements.txt format]" "--include[Dependency groups to include in the output. By default all are included]:include:" "--exclude[Dependency groups to exclude from the output]:exclude:" "--resolve[Resolve all requirements to output licenses (instead of just showing those currently installed)]" '--venv[Run the command in the virtual environment with the given key. \[env var: PDM_IN_VENV\]]:venv:' '*:patterns:' ) ;; lock) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' {-C,--config-setting}'[Pass options to the backend. options with a value must be specified after "=": "--config-setting=key(=value)" or "-Ckey(=value)"]:cs:' "--no-isolation[Do not isolate the build in a clean environment]" {-k,--skip}'[Skip some tasks and/or hooks by their comma-separated names]' "--refresh[Refresh the content hash and file hashes in the lock file]" "--check[Check if the lock file is up to date and quit]" {-G+,--group+,--with+}'[Select group of optional-dependencies or dev-dependencies(with -d). Can be supplied multiple times, use ":all" to include all groups under the same species]:group:_pdm_groups' "--without+[Exclude groups of optional-dependencies or dev-dependencies]:group:_pdm_groups" "--override+[Use the constraint file in pip-requirements format for overriding. \[env var: PDM_CONSTRAINT\] This option can be used multiple times. See https://pip.pypa.io/en/stable/user_guide/#constraints-files]:override:_files" {-d,--dev}"[Select dev dependencies]" {--prod,--production}"[Unselect dev dependencies]" '--update-reuse[Reuse pinned versions already present in lock file if possible]' '--update-reuse-installed[Reuse installed packages if possible]' "--static-urls[(DEPRECATED) Store static file URLs in the lockfile]" "--no-static-urls[(DEPRECATED) Do not store static file URLs in the lockfile]" "--no-default[Don\'t include dependencies from the default group]" "--no-cross-platform[(DEPRECATED) Only lock packages for the current platform]" "--exclude-newer[Exclude packages newer than the given UTC date in format YYYY-MM-DD\[THH:MM:SSZ\]]:exclude-newer:" {-S,--strategy}'[Specify lock strategy(cross_platform,static_urls,direct_minimal_versions). Add no_ prefix to disable. Support given multiple times or split by comma.]:strategy:_pdm_lock_strategy' "--append[Append the result to the current lock file]" "--python[The Python range to lock for. E.g. >=3.9, ==3.12.*]:python:" "--implementation[The Python implementation to lock for. E.g. cpython, pypy]:implementation:" "--platform[The platform to lock for. E.g. linux, windows, macos, alpine, windows_amd64]:platform:_pdm_lock_platform" ) ;; outdated) arguments+=( '--json[Output in JSON format]' '*:patterns:' ) ;; self) _arguments -C \ $arguments \ ': :->command' \ '*:: :->args' && ret=0 case $state in command) local -a actions=( "add:Install packages to the PDM's environment" "remove:Remove packages from PDM's environment" "list:List all packages installed with PDM" "update:Update PDM itself" ) _describe -t command 'pdm self actions' actions && ret=0 ;; args) case $words[1] in add) arguments+=( '--pip-args[Arguments that will be passed to pip install]:pip args:' ) ;; remove) arguments+=( '--pip-args[Arguments that will be passed to pip uninstall]:pip args:' {-y,--yes}'[Answer yes on the question]' ) ;; list) arguments+=( '--plugins[List plugins only]' '*:patterns:' ) ;; update) arguments+=( '--head[Update to the latest commit on the main branch]' '--pre[Update to the latest prerelease version]' '--pip-args[Arguments that will be passed to pip uninstall]:pip args:' ) ;; *) ;; esac ;; esac return $ret ;; python|py) _arguments -C \ $arguments \ ': :->command' \ '*:: :->args' && ret=0 case $state in command) local -a actions=( "remove:Remove a Python interpreter installed with PDM" "list:List all Python interpreters installed with PDM" "install:Install a Python interpreter with PDM" "find:Search for a Python interpreter" ) _describe -t command 'pdm python actions' actions && ret=0 ;; args) case $words[1] in remove) arguments+=( ':python:' ) ;; install) arguments+=( '--list[List all available Python versions]' '--min[Use minimum instead of highest version for installation if `version` is left empty]' ':python:_files' ) ;; find) arguments+=( '--managed[Only find interpreters managed by PDM]' ':request:' ) ;; *) ;; esac ;; esac return $ret ;; publish) arguments+=( {-r,--repository}'[The repository name or url to publish the package to }\[env var: PDM_PUBLISH_REPO\]]:repository:' {-u,--username}'[The username to access the repository \[env var: PDM_PUBLISH_USERNAME\]]:username:' {-P,--password}'[The password to access the repository \[env var: PDM_PUBLISH_PASSWORD\]]:password:' {-S,--sign}'[Upload the package with PGP signature]' {-i,--identity}'[GPG identity used to sign files.]:gpg identity:' {-k,--skip}'[Skip some tasks and/or hooks by their comma-separated names]' {-c,--comment}'[The comment to include with the distribution file.]:comment:' {-d,--dest}'[The directory to upload the package from]:dest:_files' "--no-verify-ssl[Disable SSL verification]" "--ca-certs[The path to a PEM-encoded Certificate Authority bundle to use for publish server validation]:cacerts:_files" "--no-build[Don't build the package before publishing]" "--skip-existing[Skip uploading files that already exist. This may not work with some repository implementations.]" ) ;; remove) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-G,--group}'[Specify the target dependency group to remove from]:group:_pdm_groups' {-d,--dev}"[Remove packages from dev dependencies]" {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' "--override+[Use the constraint file in pip-requirements format for overriding. \[env var: PDM_CONSTRAINT\] This option can be used multiple times. See https://pip.pypa.io/en/stable/user_guide/#constraints-files]:override:_files" {-k,--skip}'[Skip some tasks and/or hooks by their comma-separated names]' "--no-sync[Only write pyproject.toml and do not uninstall packages]" '--no-editable[Install non-editable versions for all packages]' "--no-self[Don't install the project itself]" "--frozen-lockfile[Don't try to create or update the lockfile. \[env var: PDM_FROZEN_LOCKFILE\]]" {-x,--fail-fast}'[Abort on first installation error]' {-C,--config-setting}'[Pass options to the backend. options with a value must be specified after "=": "--config-setting=key(=value)" or "-Ckey(=value)"]:cs:' "--no-isolation[do not isolate the build in a clean environment]" "--dry-run[Show the difference only without modifying the lockfile content]" '--venv[Run the command in the virtual environment with the given key. \[env var: PDM_IN_VENV\]]:venv:' "*:packages:_pdm_packages" ) ;; run) _arguments -s \ {-g,--global}'[Use the global project, supply the project root with `-p` option]' \ {-l,--list}'[Show all available scripts defined in pyproject.toml]' \ '--json[Output all scripts infos in JSON]' \ '--recreate[Recreate the script environment for self-contained scripts]' \ {-k,--skip}'[Skip some tasks and/or hooks by their comma-separated names]' \ {-s,--site-packages}'[Load site-packages from the selected interpreter]' \ '--venv[Run the command in the virtual environment with the given key. \[env var: PDM_IN_VENV\]]:venv:' \ '(-)1:command:->command' \ '*:arguments: _normal ' && return 0 if [[ $state == command ]]; then _command_names -e local local_commands=($(_pdm_scripts)) _describe "local command" local_commands return 0 fi ;; search) arguments+=( '1:query string:' ) ;; show) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' '--name[Show name]' '--version[Show version]' '--summary[Show summary]' '--license[Show license]' '--platform[Show platform]' '--keywords[Show keywords]' '--venv[Run the command in the virtual environment with the given key. \[env var: PDM_IN_VENV\]]:venv:' '1:package:' ) ;; sync) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-G+,--group+,--with+}'[Select group of optional-dependencies or dev-dependencies(with -d). Can be supplied multiple times, use ":all" to include all groups under the same species]:group:_pdm_groups' "--without+[Exclude groups of optional-dependencies or dev-dependencies]:group:_pdm_groups" {-d,--dev}"[Select dev dependencies]" {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' {--prod,--production}"[Unselect dev dependencies]" {-k,--skip}'[Skip some tasks and/or hooks by their comma-separated names]' '--dry-run[Only prints actions without actually running them]' {-r,--reinstall}"[Force reinstall existing dependencies]" '--clean[Clean unused packages]' "--clean-unselected[Remove all but the selected packages]" "--only-keep[Remove all but the selected packages]" "--no-default[Don\'t include dependencies from the default group]" {-x,--fail-fast}'[Abort on first installation error]' '--no-editable[Install non-editable versions for all packages]' "--no-self[Don't install the project itself]" {-C,--config-setting}'[Pass options to the backend. options with a value must be specified after "=": "--config-setting=key(=value)" or "-Ckey(=value)"]:cs:' "--no-isolation[do not isolate the build in a clean environment]" '--venv[Run the command in the virtual environment with the given key. \[env var: PDM_IN_VENV\]]:venv:' ) ;; update) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-G+,--group+,--with+}'[Select group of optional-dependencies or dev-dependencies(with -d). Can be supplied multiple times, use ":all" to include all groups under the same species]:group:_pdm_groups' "--without+[Exclude groups of optional-dependencies or dev-dependencies]:group:_pdm_groups" {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' "--override+[Use the constraint file in pip-requirements format for overriding. \[env var: PDM_CONSTRAINT\] This option can be used multiple times. See https://pip.pypa.io/en/stable/user_guide/#constraints-files]:override:_files" '--save-compatible[Save compatible version specifiers]' '--save-wildcard[Save wildcard version specifiers]' '--save-exact[Save exact version specifiers]' '--save-minimum[Save minimum version specifiers]' '--save-safe-compatible[Save safe compatible version specifiers]' '--update-reuse[Reuse pinned versions already present in lock file if possible]' '--update-eager[Try to update the packages and their dependencies recursively]' '--update-all[Update all dependencies and sub-dependencies]' '--update-reuse-installed[Reuse installed packages if possible]' '--no-editable[Install non-editable versions for all packages]' "--no-self[Don't install the project itself]" "--no-sync[Only update lock file but do not sync packages]" "--frozen-lockfile[Don't try to create or update the lockfile. \[env var: PDM_FROZEN_LOCKFILE\]]" {-k,--skip}'[Skip some tasks and/or hooks by their comma-separated names]' {-u,--unconstrained}'[Ignore the version constraints in pyproject.toml and overwrite with new ones from the resolution result]' {--pre,--prerelease}'[Allow prereleases to be pinned]' "--stable[Only allow stable versions to be pinned]" {-d,--dev}'[Select dev dependencies]' {--prod,--production}"[Unselect dev dependencies]" "--no-default[Don\'t include dependencies from the default group]" {-t,--top}'[Only update those list in pyproject.toml]' "--dry-run[Show the difference only without modifying the lockfile content]" "--outdated[Show the difference only without modifying the lockfile content]" {-x,--fail-fast}'[Abort on first installation error]' {-C,--config-setting}'[Pass options to the backend. options with a value must be specified after "=": "--config-setting=key(=value)" or "-Ckey(=value)"]:cs:' "--no-isolation[do not isolate the build in a clean environment]" '--venv[Run the command in the virtual environment with the given key. \[env var: PDM_IN_VENV\]]:venv:' "*:packages:_pdm_packages" ) ;; use) arguments+=( {-f,--first}'[Select the first matched interpreter -- no auto install]' '--auto-install-min[If `python` argument not given, auto install minimum best match - otherwise has no effect]' '--auto-install-max[If `python` argument not given, auto install maximum best match - otherwise has no effect]' {-i,--ignore-remembered}'[Ignore the remembered selection]' '--venv[Use the interpreter in the virtual environment with the given name]:venv:' '--no-version-file[Do not write the version file]' '*:python:_files' ) ;; venv) _arguments -C \ $arguments \ ': :->command' \ '*:: :->args' && ret=0 case $state in command) local -a actions=( "create:Create a virtualenv" "list:List all virtualenvs associated with this project" "remove:Remove the virtualenv with the given name" "activate:Activate the virtualenv with the given name" "purge:Purge selected/all created Virtualenvs" ) arguments+=( '--path[Show the path to the given virtualenv]' '--python[Show the Python interpreter path of the given virtualenv]' ) _describe -t command 'pdm venv actions' actions && ret=0 ;; args) case $words[1] in create) arguments+=( {-w,--with}'[Specify the backend to create the virtualenv]:backend:(virtualenv venv conda)' '--with-pip[Install pip with the virtualenv]' {-n,--name}'[Specify the name of the virtualenv]:name:' {-f,--force}'[Recreate if the virtualenv already exists]' ) ;; remove) arguments+=( {-y,--yes}'[Answer yes on the following question]' ) ;; purge) arguments+=( {-f,--force}'[Force purging without prompting for confirmation]' {-i,--interactive}'[Interactively purge selected Virtualenvs]' ) ;; *) ;; esac ;; esac return $ret ;; esac _arguments -s $arguments && ret=0 return ret } _pdm_groups() { if [[ ! -f pyproject.toml ]]; then _message "not a pdm project" return 1 fi local l groups=() in_groups=0 while IFS= read -r l; do case $l in "["project.optional-dependencies"]") in_groups=1 ;; "["tool.pdm.dev-dependencies"]") in_groups=1 ;; "["*"]") in_groups=0 ;; *"= [") if (( in_groups )); then groups+=$l[(w)1] fi ;; esac done = (3, 11): import tomllib else: import tomli as tomllib import os, re PACKAGE_REGEX = re.compile(r'^[A-Za-z][A-Za-z0-9._-]*') def get_packages(lines): return [PACKAGE_REGEX.match(line).group() for line in lines] with open('pyproject.toml', 'rb') as f: data = tomllib.load(f) packages = get_packages(data.get('project', {}).get('dependencies', [])) for reqs in data.get('project', {}).get('optional-dependencies', {}).values(): packages.extend(get_packages(reqs)) for reqs in data.get('tool', {}).get('pdm', {}).get('dev-dependencies', {}).values(): packages.extend(get_packages(reqs)) print(*set(packages)) EOF } _pdm_scripts() { local scripts=() package_dir=$(PDM_CHECK_UPDATE=0 $PDM_PYTHON -m pdm info --packages) if [[ -f pyproject.toml ]]; then local l in_scripts=0 while IFS= read -r l; do case $l in "["tool.pdm.scripts"]") in_scripts=1 ;; "["*"]") in_scripts=0 ;; *"= "*) if (( in_scripts )); then scripts+=$l[(w)1] fi ;; esac done < pyproject.toml fi if [[ $package_dir != "None" ]]; then scripts+=($package_dir/bin/*(N:t)) fi echo $scripts } _pdm_packages() { if [[ ! -f pyproject.toml ]]; then _message "not a pdm project" return 1 fi local packages=(${=$(_get_packages_with_python)}) compadd -X packages -a packages } _pdm_lock_strategy() { local -a strategy=( 'cross_platform:(DEPRECATED)Lock packages for all platforms' 'inherit_metadata:Calculate and store the markers for the packages' 'static_urls:Store static file URLs in the lockfile' 'direct_minimal_versions:Store the minimal versions of the dependencies' 'no_cross_platform:Only lock packages for the current platform' 'no_static_urls:Do not store static file URLs in the lockfile' 'no_inherit_metadata:Do not calculate and store the markers for the packages' 'no_direct_minimal_versions:Do not store the minimal versions of the dependencies' ) _describe -t strategy "lock strategy" strategy } _pdm_lock_platform() { local -a platforms=( "linux" "windows" "macos" "alpine" "windows_amd64" "windows_x86" "windows_arm64" "macos_arm64" "macos_x86_64" ) _describe -t platform "platform" platforms } _pdm "$@" pdm-2.23.1/src/pdm/cli/filters.py000066400000000000000000000107131477560627500165440ustar00rootroot00000000000000from __future__ import annotations import argparse from functools import cached_property from typing import TYPE_CHECKING from pdm.exceptions import PdmUsageError from pdm.utils import normalize_name if TYPE_CHECKING: from typing import Iterator, Sequence from pdm.project import Project class GroupSelection: def __init__( self, project: Project, *, default: bool = True, dev: bool | None = None, groups: Sequence[str] = (), group: str | None = None, excluded_groups: Sequence[str] = (), exclude_non_existing: bool = False, ): self.project = project self.groups = groups self.group = group self.default = default self.dev = dev self.excluded_groups = excluded_groups self.exclude_non_existing = exclude_non_existing @classmethod def from_options(cls, project: Project, options: argparse.Namespace) -> GroupSelection: if getattr(options, "excluded_groups", None) and not options.groups and options.dev is None: options.groups = [":all"] if "group" in options: return cls(project, group=options.group, dev=options.dev) return cls( project, default=options.default, dev=options.dev, groups=options.groups, excluded_groups=options.excluded_groups, ) def one(self) -> str: if self.group: return self.group if len(self.groups) == 1: return self.groups[0] return "dev" if self.dev else "default" @property def is_unset(self) -> bool: return self.default and self.dev is None and not self.groups def all(self) -> list[str] | None: project_groups = list(self.project.iter_groups()) if self.is_unset: if self.project.lockfile.exists(): groups = self.project.lockfile.groups if groups: groups = [g for g in groups if g in project_groups] return groups return list(self) @cached_property def _translated_groups(self) -> list[str]: """Translate default, dev and groups containing ":all" into a list of groups""" locked_groups = self.project.lockfile.groups if self.is_unset: # Default case, return what is in the lock file project_groups = list(self.project.iter_groups()) if locked_groups: return [g for g in locked_groups if g in project_groups] default, dev, groups = self.default, self.dev, self.groups if dev is None: # --prod is not set, include dev-dependencies dev = True project = self.project optional_groups = {normalize_name(g) for g in project.pyproject.metadata.get("optional-dependencies", {})} dev_groups = set(project.pyproject.dev_dependencies) groups_set = {normalize_name(g) if g != ":all" else g for g in groups} if groups_set & dev_groups: if not dev: raise PdmUsageError("--prod is not allowed with dev groups and should be left") elif dev: groups_set.update(dev_groups) if self.exclude_non_existing and locked_groups: groups_set.intersection_update(locked_groups) if ":all" in groups: groups_set.discard(":all") groups_set.update(optional_groups) if default: groups_set.add("default") groups_set -= {normalize_name(g) for g in self.excluded_groups} invalid_groups = groups_set - {normalize_name(g) for g in project.iter_groups()} if invalid_groups: project.core.ui.echo( f"[d]Ignoring non-existing groups: [success]{', '.join(invalid_groups)}[/]", err=True, ) groups_set -= invalid_groups # Sorts the result in ascending order instead of in random order # to make this function pure result = sorted(groups_set, key=lambda x: (x != "default", x)) return result def validate(self) -> None: extra_groups = self.project.lockfile.compare_groups(self._translated_groups) if extra_groups: raise PdmUsageError(f"Requested groups not in lockfile: {','.join(extra_groups)}") def __iter__(self) -> Iterator[str]: return iter(self._translated_groups) def __contains__(self, group: str) -> bool: return group in self._translated_groups pdm-2.23.1/src/pdm/cli/hooks.py000066400000000000000000000026751477560627500162270ustar00rootroot00000000000000from __future__ import annotations import contextlib from typing import Any, Generator from pdm.project.core import Project from pdm.signals import pdm_signals class HookManager: def __init__(self, project: Project, skip: list[str] | None = None): self.project = project self.skip = skip or [] @contextlib.contextmanager def skipping(self, *names: str) -> Generator[None]: """ Temporarily skip some hooks. """ old_skip = self.skip[:] self.skip.extend(names) yield self.skip = old_skip @property def skip_all(self) -> bool: return ":all" in self.skip @property def skip_pre(self) -> bool: return ":pre" in self.skip @property def skip_post(self) -> bool: return ":post" in self.skip def should_run(self, name: str) -> bool: """ Tells whether a task given its name should run or not according to the current skipping rules. """ return ( not self.skip_all and name not in self.skip and not (self.skip_pre and name.startswith("pre_")) and not (self.skip_post and name.startswith("post_")) ) def try_emit(self, name: str, **kwargs: Any) -> None: """ Emit a hook signal if rules allow it. """ if self.should_run(name): pdm_signals.signal(name).send(self.project, hooks=self, **kwargs) pdm-2.23.1/src/pdm/cli/options.py000066400000000000000000000362061477560627500165740ustar00rootroot00000000000000from __future__ import annotations import argparse import os import sys from functools import partial from typing import TYPE_CHECKING, cast if TYPE_CHECKING: from typing import Any, Protocol, Sequence from pdm.project import Project class ActionCallback(Protocol): def __call__( self, project: Project, namespace: argparse.Namespace, values: Any, option_string: str | None = None, ) -> None: ... class Option: """A reusable option object which delegates all arguments to parser.add_argument(). """ def __init__(self, *args: Any, **kwargs: Any) -> None: self.args = args self.kwargs = kwargs def add_to_parser(self, parser: argparse._ActionsContainer) -> None: parser.add_argument(*self.args, **self.kwargs) def add_to_group(self, group: argparse._ArgumentGroup) -> None: group.add_argument(*self.args, **self.kwargs) def __call__(self, func: ActionCallback) -> Option: self.kwargs.update(action=CallbackAction, callback=func) return self class CallbackAction(argparse.Action): def __init__(self, *args: Any, callback: ActionCallback, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.callback = callback def __call__( self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, values: Any, option_string: str | None = None, ) -> None: if not hasattr(namespace, "callbacks"): namespace.callbacks = [] callback = partial(self.callback, values=values, option_string=option_string) namespace.callbacks.append(callback) class ExtendMapAction(argparse._AppendAction): def __call__( self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, values: str | Sequence[Any] | None, option_string: str | None = None, ) -> None: assert isinstance(values, str) k, _, v = values.partition("=") mapping = getattr(namespace, self.dest, None) or {} if k in mapping: if not isinstance(mapping[k], list): mapping[k] = [mapping[k]] mapping[k].append(v) else: mapping[k] = v setattr(namespace, self.dest, mapping) class ArgumentGroup(Option): """A reusable argument group object which can call `add_argument()` to add more arguments. And itself will be registered to the parser later. """ def __init__(self, name: str, is_mutually_exclusive: bool = False, required: bool = False) -> None: self.name = name self.options: list[Option] = [] self.required = required self.is_mutually_exclusive = is_mutually_exclusive def add_argument(self, *args: Any, **kwargs: Any) -> None: if args and isinstance(args[0], Option): self.options.append(args[0]) else: self.options.append(Option(*args, **kwargs)) def add_to_parser(self, parser: argparse._ActionsContainer) -> None: group: argparse._ArgumentGroup if self.is_mutually_exclusive: group = parser.add_mutually_exclusive_group(required=self.required) else: group = parser.add_argument_group(self.name) for option in self.options: option.add_to_group(group) def add_to_group(self, group: argparse._ArgumentGroup) -> None: self.add_to_parser(group) def split_lists(separator: str) -> type[argparse.Action]: """ Works the same as `append` except each argument is considered a `separator`-separated list. """ class SplitList(argparse.Action): def __call__( self, parser: argparse.ArgumentParser, args: argparse.Namespace, values: Any, option_string: str | None = None, ) -> None: if not isinstance(values, str): return split = getattr(args, self.dest) or [] split.extend(value.strip() for value in values.split(separator) if value.strip()) setattr(args, self.dest, split) return SplitList def from_splitted_env(name: str, separator: str) -> list[str] | None: """ Parse a `separator`-separated list from a `name` environment variable if present. """ value = os.getenv(name) if not value: return None return [v.strip() for v in value.split(separator) if v.strip()] or None verbose_option = ArgumentGroup("Verbosity options", is_mutually_exclusive=True) verbose_option.add_argument( "-v", "--verbose", action="count", default=0, help="Use `-v` for detailed output and `-vv` for more detailed", ) verbose_option.add_argument("-q", "--quiet", action="store_const", const=-1, dest="verbose", help="Suppress output") no_cache_option = Option( "--no-cache", action="store_true", default=os.getenv("PDM_NO_CACHE"), help="Disable the cache for the current command. [env var: PDM_NO_CACHE]", ) dry_run_option = Option( "--dry-run", action="store_true", default=False, help="Show the difference only and don't perform any action", ) lockfile_option = Option( "-L", "--lockfile", default=os.getenv("PDM_LOCKFILE"), help="Specify another lockfile path. Default: pdm.lock. [env var: PDM_LOCKFILE]", ) @Option( "--frozen-lockfile", "--no-lock", nargs=0, help="Don't try to create or update the lockfile. [env var: PDM_FROZEN_LOCKFILE]", ) def frozen_lockfile_option( project: Project, namespace: argparse.Namespace, values: str | Sequence[Any] | None, option_string: str | None = None, ) -> None: if option_string == "--no-lock": project.core.ui.warn("--no-lock is deprecated, use --frozen-lockfile instead.") project.enable_write_lockfile = False # type: ignore[has-type] @Option( "--pep582", const="AUTO", metavar="SHELL", nargs="?", help="Print the command line to be eval'd by the shell for PEP 582", ) def pep582_option( project: Project, namespace: argparse.Namespace, values: str | Sequence[Any] | None, option_string: str | None = None, ) -> None: from pdm.cli.actions import print_pep582_command print_pep582_command(project, cast(str, values)) sys.exit(0) install_group = ArgumentGroup("Install options") install_group.add_argument( "--no-editable", action="store_true", default=bool(os.getenv("PDM_NO_EDITABLE")), dest="no_editable", help="Install non-editable versions for all packages. [env var: PDM_NO_EDITABLE]", ) install_group.add_argument( "--no-self", action="store_true", default=bool(os.getenv("PDM_NO_SELF")), dest="no_self", help="Don't install the project itself. [env var: PDM_NO_SELF]", ) install_group.add_argument("--fail-fast", "-x", action="store_true", help="Abort on first installation error") @Option( "--no-isolation", dest="build_isolation", nargs=0, help="Disable isolation when building a source distribution that follows PEP 517, " "as in: build dependencies specified by PEP 518 must be already installed if this option is used.", ) def no_isolation_option( project: Project, namespace: argparse.Namespace, values: str | Sequence[Any] | None, option_string: str | None = None, ) -> None: project.core.state.build_isolation = False install_group.options.append(no_isolation_option) groups_group = ArgumentGroup("Dependencies Selection") groups_group.add_argument( "-G", "--group", "--with", dest="groups", metavar="GROUP", action=split_lists(","), help="Select group of optional-dependencies separated by comma " "or dependency-groups (with `-d`). Can be supplied multiple times, " 'use ":all" to include all groups under the same species.', default=[], ) groups_group.add_argument( "--without", dest="excluded_groups", metavar="", action=split_lists(","), help="Exclude groups of optional-dependencies or dependency-groups", default=[], ) groups_group.add_argument( "--no-default", dest="default", action="store_false", default=True, help="Don't include dependencies from the default group", ) dev_group = ArgumentGroup("dev", is_mutually_exclusive=True) dev_group.add_argument( "-d", "--dev", default=None, dest="dev", action="store_true", help="Select dev dependencies", ) dev_group.add_argument( "--prod", "--production", dest="dev", action="store_false", help="Unselect dev dependencies", ) groups_group.options.append(dev_group) save_strategy_group = ArgumentGroup("Save Strategy") _save_sub_group = ArgumentGroup("save_strategy", is_mutually_exclusive=True) _save_sub_group.add_argument( "--save-compatible", action="store_const", dest="save_strategy", const="compatible", help="Save compatible version specifiers", ) _save_sub_group.add_argument( "--save-safe-compatible", action="store_const", dest="save_strategy", const="safe_compatible", help="Save safe compatible version specifiers", ) _save_sub_group.add_argument( "--save-wildcard", action="store_const", dest="save_strategy", const="wildcard", help="Save wildcard version specifiers", ) _save_sub_group.add_argument( "--save-exact", action="store_const", dest="save_strategy", const="exact", help="Save exact version specifiers", ) _save_sub_group.add_argument( "--save-minimum", action="store_const", dest="save_strategy", const="minimum", help="Save minimum version specifiers", ) save_strategy_group.add_argument(_save_sub_group) skip_option = Option( "-k", "--skip", dest="skip", action=split_lists(","), help="Skip some tasks and/or hooks by their comma-separated names." " Can be supplied multiple times." ' Use ":all" to skip all hooks.' ' Use ":pre" and ":post" to skip all pre or post hooks.', default=from_splitted_env("PDM_SKIP_HOOKS", ","), ) update_strategy_group = ArgumentGroup("Update Strategy") _update_sub_group = ArgumentGroup("update_strategy", is_mutually_exclusive=True) _update_sub_group.add_argument( "--update-reuse", action="store_const", dest="update_strategy", const="reuse", help="Reuse pinned versions already present in lock file if possible", ) _update_sub_group.add_argument( "--update-eager", action="store_const", dest="update_strategy", const="eager", help="Try to update the packages and their dependencies recursively", ) _update_sub_group.add_argument( "--update-all", action="store_const", dest="update_strategy", const="all", help="Update all dependencies and sub-dependencies", ) _update_sub_group.add_argument( "--update-reuse-installed", action="store_const", dest="update_strategy", const="reuse-installed", help="Reuse installed packages if possible", ) update_strategy_group.add_argument(_update_sub_group) project_option = Option( "-p", "--project", dest="project_path", help="Specify another path as the project root, which changes the base of pyproject.toml " "and __pypackages__ [env var: PDM_PROJECT]", default=os.getenv("PDM_PROJECT"), ) global_option = Option( "-g", "--global", dest="global_project", action="store_true", help="Use the global project, supply the project root with `-p` option", ) clean_group = ArgumentGroup("clean", is_mutually_exclusive=True) clean_group.add_argument("--clean", action="store_true", help="Clean packages not in the lockfile") clean_group.add_argument( "--only-keep", "--clean-unselected", action="store_true", help="Only keep the selected packages" ) packages_group = ArgumentGroup("Package Arguments") packages_group.add_argument( "-e", "--editable", dest="editables", action="append", help="Specify editable packages", default=[], ) packages_group.add_argument("packages", nargs="*", help="Specify packages") @Option( "-I", "--ignore-python", nargs=0, help="Ignore the Python path saved in .pdm-python. [env var: PDM_IGNORE_SAVED_PYTHON]", ) def ignore_python_option( project: Project, namespace: argparse.Namespace, values: str | Sequence[Any] | None, option_string: str | None = None, ) -> None: os.environ.update({"PDM_IGNORE_SAVED_PYTHON": "1"}) @Option( "-n", "--non-interactive", nargs=0, dest="_non_interactive", help="Don't show interactive prompts but use defaults. [env var: PDM_NON_INTERACTIVE]", ) def non_interactive_option( project: Project, namespace: argparse.Namespace, values: str | Sequence[Any] | None, option_string: str | None = None, ) -> None: os.environ.update({"PDM_NON_INTERACTIVE": "1"}) prerelease_option = ArgumentGroup("prerelease", is_mutually_exclusive=True) prerelease_option.add_argument( "--pre", "--prerelease", action="store_true", dest="prerelease", default=None, help="Allow prereleases to be pinned", ) prerelease_option.add_argument( "--stable", action="store_false", dest="prerelease", help="Only allow stable versions to be pinned" ) unconstrained_option = Option( "-u", "--unconstrained", action="store_true", default=False, help="Ignore the version constraints in pyproject.toml and overwrite with new ones from the resolution result", ) venv_option = Option( "--venv", dest="use_venv", metavar="NAME", nargs="?", const="in-project", help="Run the command in the virtual environment with the given key. [env var: PDM_IN_VENV]", default=os.getenv("PDM_IN_VENV"), ) lock_strategy_group = ArgumentGroup("Lock Strategy") lock_strategy_group.add_argument( "--strategy", "-S", dest="strategy_change", metavar="STRATEGY", action=split_lists(","), help="Specify lock strategy (cross_platform, static_urls, direct_minimal_versions, inherit_metadata). " "Add 'no_' prefix to disable. Can be supplied multiple times or split by comma.", ) lock_strategy_group.add_argument( "--no-cross-platform", action="append_const", dest="strategy_change", const="no_cross_platform", help="[DEPRECATED] Only lock packages for the current platform", ) lock_strategy_group.add_argument( "--static-urls", action="append_const", dest="strategy_change", help="[DEPRECATED] Store static file URLs in the lockfile", const="static_urls", ) lock_strategy_group.add_argument( "--no-static-urls", action="append_const", dest="strategy_change", help="[DEPRECATED] Do not store static file URLs in the lockfile", const="no_static_urls", ) config_setting_option = Option( "--config-setting", "-C", action=ExtendMapAction, help="Pass options to the builder. options with a value must be " 'specified after "=": `--config-setting=key(=value)` ' "or `-Ckey(=value)`", ) install_group.options.append(config_setting_option) override_option = Option( "--override", default=[env] if (env := os.getenv("PDM_OVERRIDE")) else None, action="append", help="Use the constraint file in pip-requirements format for overriding. [env var: PDM_OVERRIDE] " "This option can be used multiple times. " "See https://pip.pypa.io/en/stable/user_guide/#constraints-files", ) pdm-2.23.1/src/pdm/cli/templates/000077500000000000000000000000001477560627500165165ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/templates/__init__.py000066400000000000000000000151031477560627500206270ustar00rootroot00000000000000from __future__ import annotations import importlib.resources import os import re import shutil import subprocess import tempfile from pathlib import Path from typing import TYPE_CHECKING, Any from pdm.exceptions import PdmException from pdm.utils import normalize_name if TYPE_CHECKING: from importlib.resources.abc import Traversable from typing import Callable, TypeVar ST = TypeVar("ST", Traversable, Path) TEMPLATE_PACKAGE = "pdm.cli.templates" BUILTIN_TEMPLATES = ["default", "minimal"] class ProjectTemplate: _path: Path def __init__(self, path_or_url: str | None) -> None: self.template = path_or_url or "default" def __enter__(self) -> ProjectTemplate: self._path = Path(tempfile.mkdtemp(suffix="-template", prefix="pdm-")) self.prepare_template() return self def __exit__(self, *args: Any) -> None: shutil.rmtree(self._path, ignore_errors=True) def generate(self, target_path: Path, metadata: dict[str, Any], overwrite: bool = False) -> None: from pdm.compat import tomllib def replace_all(path: str, old: str, new: str) -> None: with open(path, encoding=encoding) as fp: content = fp.read() content = re.sub(rf"\b{old}\b", new, content) with open(path, "w", encoding=encoding) as fp: fp.write(content) if metadata.get("project", {}).get("name"): try: with open(self._path / "pyproject.toml", "rb") as fp: pyproject = tomllib.load(fp) except FileNotFoundError: raise PdmException("Template pyproject.toml not found") from None new_name = metadata["project"]["name"] new_import_name = normalize_name(new_name).replace("-", "_") try: original_name = pyproject["project"]["name"] except KeyError: raise PdmException("Template pyproject.toml is not PEP-621 compliant") from None import_name = normalize_name(original_name).replace("-", "_") encoding = "utf-8" for root, dirs, filenames in os.walk(self._path): for i, d in enumerate(dirs): if d == import_name: os.rename(os.path.join(root, d), os.path.join(root, new_import_name)) dirs[i] = new_import_name for f in filenames: if f.endswith(".py"): replace_all(os.path.join(root, f), import_name, new_import_name) if f == import_name + ".py": os.rename(os.path.join(root, f), os.path.join(root, new_import_name + ".py")) elif f.endswith((".md", ".rst")): replace_all(os.path.join(root, f), original_name, new_name) elif Path(root) == self._path and f == "pyproject.toml": replace_all(os.path.join(root, f), import_name, new_import_name) target_path.mkdir(exist_ok=True, parents=True) self.mirror(self._path, target_path, [self._path / "pyproject.toml"], overwrite=overwrite) self._generate_pyproject(target_path / "pyproject.toml", metadata) def prepare_template(self) -> None: if self.template in BUILTIN_TEMPLATES: self._prepare_package_template(f"{TEMPLATE_PACKAGE}.{self.template}") elif "://" in self.template or self.template.startswith("git@"): self._prepare_git_template(self.template) elif os.path.exists(self.template): self._prepare_local_template(self.template) else: # template name template = f"https://github.com/pdm-project/template-{self.template}" self._prepare_git_template(template) @staticmethod def mirror( src: ST, dst: Path, skip: list[ST] | None = None, copyfunc: Callable[[ST, Path], Any] = shutil.copyfile, # type: ignore[assignment] *, overwrite: bool = False, ) -> None: if skip and src in skip: return if src.is_dir(): dst.mkdir(exist_ok=True) for child in src.iterdir(): ProjectTemplate.mirror(child, dst / child.name, skip, copyfunc) elif src.name.endswith(".pyc"): return elif overwrite or not dst.exists(): copyfunc(src, dst) @staticmethod def _copy_package_file(src: Traversable, dst: Path) -> Path: with importlib.resources.as_file(src) as f: return shutil.copyfile(f, dst) def _generate_pyproject(self, path: Path, metadata: dict[str, Any]) -> None: import tomlkit from pdm.cli.utils import merge_dictionary try: with open(path, encoding="utf-8") as fp: content = tomlkit.load(fp) except FileNotFoundError: content = tomlkit.document() try: with open(self._path / "pyproject.toml", encoding="utf-8") as fp: template_content = tomlkit.load(fp) except FileNotFoundError: template_content = tomlkit.document() merge_dictionary(content, template_content) if "version" in content.get("project", {}).get("dynamic", []): metadata["project"].pop("version", None) merge_dictionary(content, metadata) if "build-system" in metadata: content["build-system"] = metadata["build-system"] else: content.pop("build-system", None) with open(path, "w", encoding="utf-8") as fp: fp.write(tomlkit.dumps(content)) def _prepare_package_template(self, import_name: str) -> None: files = importlib.resources.files(import_name) self.mirror(files, self._path, skip=[files / "__init__.py"], copyfunc=self._copy_package_file) def _prepare_git_template(self, url: str) -> None: left, amp, right = url.rpartition("@") if left != "git" and amp: extra_args = [f"--branch={right}"] url = left else: extra_args = [] git_command = ["git", "clone", "--recursive", "--depth=1", *extra_args, url, self._path.as_posix()] result = subprocess.run(git_command, capture_output=True, text=True) if result.returncode != 0: raise PdmException(f"Failed to clone template from git repository {url}: {result.stderr}") shutil.rmtree(self._path / ".git", ignore_errors=True) def _prepare_local_template(self, path: str) -> None: src = Path(path) self.mirror(src, self._path, skip=[src / ".git", src / ".svn", src / ".hg"]) pdm-2.23.1/src/pdm/cli/templates/default/000077500000000000000000000000001477560627500201425ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/templates/default/.gitignore000066400000000000000000000060401477560627500221320ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control #poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. #pdm.lock # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # in version control. # https://pdm-project.org/#use-with-ide .pdm.toml .pdm-python .pdm-build/ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ # PyCharm # JetBrains specific template is maintained in a separate JetBrains.gitignore that can # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ pdm-2.23.1/src/pdm/cli/templates/default/README.md000066400000000000000000000000221477560627500214130ustar00rootroot00000000000000# example-package pdm-2.23.1/src/pdm/cli/templates/default/__init__.py000066400000000000000000000000001477560627500222410ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/templates/default/pyproject.toml000066400000000000000000000004261477560627500230600ustar00rootroot00000000000000[project] name = "example-package" version = "0.1.0" description = "Default template for PDM package" authors = [] dependencies = [] requires-python = ">=3.9" readme = "README.md" license = {text = "MIT"} [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" pdm-2.23.1/src/pdm/cli/templates/default/src/000077500000000000000000000000001477560627500207315ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/templates/default/src/example_package/000077500000000000000000000000001477560627500240375ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/templates/default/src/example_package/__init__.py000066400000000000000000000000001477560627500261360ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/templates/default/tests/000077500000000000000000000000001477560627500213045ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/templates/default/tests/__init__.py000066400000000000000000000000001477560627500234030ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/templates/minimal/000077500000000000000000000000001477560627500201445ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/templates/minimal/__init__.py000066400000000000000000000000001477560627500222430ustar00rootroot00000000000000pdm-2.23.1/src/pdm/cli/templates/minimal/pyproject.toml000066400000000000000000000004261477560627500230620ustar00rootroot00000000000000[project] name = "example-package" version = "0.1.0" description = "Default template for PDM package" authors = [] dependencies = [] requires-python = ">=3.9" readme = "README.md" license = {text = "MIT"} [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" pdm-2.23.1/src/pdm/cli/utils.py000066400000000000000000000661011477560627500162360ustar00rootroot00000000000000from __future__ import annotations import argparse import dataclasses as dc import json import os import re import sys from collections import OrderedDict from fnmatch import fnmatch from gettext import gettext as _ from json import dumps from pathlib import Path from typing import TYPE_CHECKING, Any, Callable, Iterable, Mapping, MutableMapping, cast, no_type_check from packaging.specifiers import SpecifierSet from resolvelib.structs import DirectedGraph from rich.tree import Tree from pdm import termui from pdm.exceptions import PdmArgumentError, ProjectError from pdm.models.markers import EnvSpec from pdm.models.requirements import Requirement, filter_requirements_with_extras, strip_extras from pdm.models.specifiers import PySpecSet, get_specifier from pdm.utils import comparable_version, is_path_relative_to, normalize_name, url_to_path if TYPE_CHECKING: from argparse import Action, _ArgumentGroup from packaging.version import Version from resolvelib.resolvers import RequirementInformation, ResolutionImpossible from pdm.compat import Distribution from pdm.compat import importlib_metadata as im from pdm.models.candidates import Candidate from pdm.project import Project class PdmFormatter(argparse.RawDescriptionHelpFormatter): def start_section(self, heading: str | None) -> None: return super().start_section(termui.style(heading.title() if heading else "", style="warning")) def _format_usage( self, usage: str | None, actions: Iterable[Action], groups: Iterable[_ArgumentGroup], prefix: str | None, ) -> str: if prefix is None: prefix = "Usage: " result = super()._format_usage(usage, actions, groups, prefix) # type: ignore[arg-type] # Remove continuous spaces result = re.sub(r" +", " ", result) if prefix: return result.replace(prefix, termui.style(prefix, style="warning")) return result def _format_action(self, action: Action) -> str: # determine the required width and the entry label help_position = min(self._action_max_length + 2, self._max_help_position) help_width = max(self._width - help_position, 11) action_width = help_position - self._current_indent - 2 action_header = self._format_action_invocation(action) # no help; start on same line and add a final newline if not action.help: tup = "", self._current_indent, action_header action_header = "{0:>{1}}{2}\n".format(*tup) # short action name; start on the same line and pad two spaces elif len(action_header) <= action_width: tup = "", self._current_indent, action_header, action_width # type: ignore[assignment] action_header = "{0:>{1}}{2:<{3}} ".format(*tup) indent_first = 0 # long action name; start on the next line else: tup = "", self._current_indent, action_header action_header = "{0:>{1}}{2}\n".format(*tup) indent_first = help_position # Special format for empty action_header # - No extra indent block # - Help info in the same indent level as subactions if not action_header.strip(): action_header = "" help_position = self._current_indent indent_first = self._current_indent # collect the pieces of the action help parts = [termui.style(action_header, style="primary")] # if there was help for the action, add lines of help text if action.help: help_text = self._expand_help(action) help_lines = self._split_lines(help_text, help_width) parts.append("{:>{}}{}\n".format("", indent_first, help_lines[0])) for line in help_lines[1:]: parts.append("{:>{}}{}\n".format("", help_position, line)) # or add a newline if the description doesn't end with one elif not action_header.endswith("\n"): if action_header: parts.append("\n") # cancel out extra indent when action_header is empty if not action_header: self._dedent() # if there are any sub-actions, add their help as well for subaction in self._iter_indented_subactions(action): parts.append(self._format_action(subaction)) # cancel out extra dedent when action_header is empty if not action_header: self._indent() # return a single string return self._join_parts(parts) class ArgumentParser(argparse.ArgumentParser): """A standard argument parser but with title-cased help.""" def __init__(self, *args: Any, **kwargs: Any) -> None: kwargs["formatter_class"] = PdmFormatter kwargs["add_help"] = False super().__init__(*args, **kwargs) self.add_argument( "-h", "--help", action="help", default=argparse.SUPPRESS, help="Show this help message and exit." ) self._optionals.title = "options" def parse_known_args(self, args: Any = None, namespace: Any = None) -> Any: args, argv = super().parse_known_args(args, namespace) if argv: msg = _("unrecognized arguments: %s") self.error(msg % " ".join(argv)) return args, argv def format_similar_command(root_command: str, commands: list[str], script_commands: list[str]) -> str: from difflib import get_close_matches similar_commands = get_close_matches(root_command, commands) similar_script_commands = get_close_matches(root_command, script_commands) commands_text = "\n".join([f" - {cmd}" for cmd in similar_commands]) script_commands_text = "\n".join([f" - {cmd}" for cmd in similar_script_commands]) message = f"[red]Command not found: {root_command}[/]" if commands_text: message += f""" [green]Did you mean one of these commands? {commands_text}[/]""" if script_commands_text: message += f""" [yellow]{"Or" if commands_text else "Did you mean"} one of these script commands? {script_commands_text}[/]""" return message class ErrorArgumentParser(ArgumentParser): """A subclass of argparse.ArgumentParser that raises parsing error rather than exiting. This does the same as passing exit_on_error=False on Python 3.9+ """ def _parse_known_args( self, arg_strings: list[str], namespace: argparse.Namespace, *args: Any, **kwargs: Any ) -> tuple[argparse.Namespace, list[str]]: try: return super()._parse_known_args(arg_strings, namespace, *args, **kwargs) except argparse.ArgumentError as e: # We raise a dedicated error to avoid being caught by the caller raise PdmArgumentError(e) from e @dc.dataclass(frozen=True) class PackageNode: """An internal class for the convenience of dependency graph building.""" name: str = dc.field(hash=True, compare=True) version: str | None = dc.field(compare=False) requirements: dict[str, Requirement] = dc.field(compare=False) def __repr__(self) -> str: return f"" def build_dependency_graph( working_set: Mapping[str, im.Distribution], env_spec: EnvSpec, selected: set[str] | None = None, include_sub: bool = True, ) -> DirectedGraph: """Build a dependency graph from locked result.""" graph: DirectedGraph[PackageNode | None] = DirectedGraph() graph.add(None) # sentinel parent of top nodes. node_with_extras: set[str] = set() def add_package(key: str, dist: Distribution | None) -> PackageNode: name, extras = strip_extras(key) extras = extras or () reqs: dict[str, Requirement] = {} if dist: requirements = filter_requirements_with_extras(dist.requires or [], extras, include_default=True) for req in requirements: if not req.marker or req.marker.matches(env_spec): reqs[req.identify()] = req version: str | None = dist.version else: version = None node = PackageNode(key, version, reqs) if node not in graph: if extras: node_with_extras.add(name) graph.add(node) if include_sub: for k in reqs: child = add_package(k, working_set.get(strip_extras(k)[0])) graph.connect(node, child) return node selected_map: dict[str, str] = {} for key in selected or (): name = key.split("[")[0] if len(key) >= len(selected_map.get(name, "")): # Ensure key with extras remains selected_map[name] = key for k, dist in working_set.items(): if selected is not None: name = k.split("[")[0] if name not in selected_map: continue k = selected_map[name] add_package(k, dist) for node in list(graph): if node is not None and not list(graph.iter_parents(node)): # Top requirements if node.name in node_with_extras: # Already included in package[extra], no need to keep the top level # non-extra package. graph.remove(node) else: graph.connect(None, node) return graph def specifier_from_requirement(requirement: Requirement) -> str: return str(requirement.specifier) or "Any" def add_package_to_tree( root: Tree, graph: DirectedGraph, package: PackageNode, required: list[str], visited: frozenset[str] = frozenset(), ) -> None: """Format one package. :param graph: the dependency graph :param package: the package instance :param required: the version required by its parent :param visited: the visited package collection """ version = ( "[error][ not installed ][/]" if not package.version else f"[error]{package.version}[/]" if required and not any(s in ("Any", "This project") or SpecifierSet(s).contains(package.version) for s in required) else f"[warning]{package.version}[/]" ) # escape deps with extras name = package.name.replace("[", r"\[") if "[" in package.name else package.name if package.name in visited: version = r"[error]\[circular][/]" req_str = f"[ required: {'&&'.join(required)} ]" if required else "[ Not required ]" node = root.add(f"[req]{name}[/] {version} {req_str}") if package.name in visited: return children = sorted(graph.iter_children(package), key=lambda p: p.name) for child in children: required = [specifier_from_requirement(package.requirements[child.name])] add_package_to_tree(node, graph, child, required, visited | {package.name}) def add_package_to_reverse_tree( root: Tree, graph: DirectedGraph, package: PackageNode, child: PackageNode | None = None, requires: str = "", visited: frozenset[str] = frozenset(), ) -> None: """Format one package for output reverse dependency graph.""" version = "[error][ not installed ][/]" if not package.version else f"[warning]{package.version}[/]" if package.name in visited: version = r"[error]\[circular][/]" requires = ( f"[ requires: [error]{requires}[/] ]" if requires not in ("Any", "") and child and child.version and not SpecifierSet(requires).contains(child.version) else "" if not requires else f"[ requires: {requires} ]" ) name = package.name.replace("[", r"\[") if "[" in package.name else package.name node = root.add(f"[req]{name}[/] {version} {requires}") if package.name in visited: return parents: list[PackageNode] = sorted(filter(None, graph.iter_parents(package)), key=lambda p: p.name) for parent in parents: requires = specifier_from_requirement(parent.requirements[package.name]) add_package_to_reverse_tree(node, graph, parent, package, requires, visited=visited | {package.name}) return def package_is_project(package: PackageNode, project: Project) -> bool: return project.is_distribution and package.name == normalize_name(project.name) def _format_forward_dependency_graph( project: Project, graph: DirectedGraph[PackageNode | None], patterns: list[str] ) -> Tree: """Format dependency graph for output.""" root = Tree("Dependencies", hide_root=True) def find_package_to_add(package: PackageNode) -> PackageNode | None: if not patterns: return package to_check = [package] while to_check: package = to_check.pop(0) if package and package_match_patterns(package, patterns): return package to_check.extend(graph.iter_children(package)) return None all_dependencies = { r.identify(): r for deps in project.all_dependencies.values() for r in deps if not r.marker or r.marker.matches(project.environment.spec) } top_level_dependencies = {find_package_to_add(node) for node in graph.iter_children(None) if node} for package in sorted((node for node in top_level_dependencies if node), key=lambda p: p.name): required: set[str] = set() for parent in graph.iter_parents(package): if parent: r = specifier_from_requirement(parent.requirements[package.name]) elif package.name in all_dependencies: r = specifier_from_requirement(all_dependencies[package.name]) elif package_is_project(package, project): r = "This project" else: continue required.add(r) add_package_to_tree(root, graph, package, sorted(required)) return root def _format_reverse_dependency_graph( project: Project, graph: DirectedGraph[PackageNode | None], patterns: list[str] ) -> Tree: """Format reverse dependency graph for output.""" def find_package_to_add(package: PackageNode) -> PackageNode | None: if not patterns: return package to_check = [package] while to_check: package = to_check.pop(0) if package and package_match_patterns(package, patterns): return package to_check.extend(graph.iter_parents(package)) return None root = Tree("Dependencies", hide_root=True) leaf_nodes = {find_package_to_add(node) for node in graph if not list(graph.iter_children(node)) and node} for package in sorted((node for node in leaf_nodes if node), key=lambda p: p.name): if not package: continue add_package_to_reverse_tree(root, graph, package) return root def build_forward_dependency_json_subtree( root: PackageNode, project: Project, graph: DirectedGraph[PackageNode | None], required_by: PackageNode | None = None, visited: frozenset[str] = frozenset(), ) -> dict: required: set[str] = set() all_dependencies = { r.identify(): r for deps in project.all_dependencies.values() for r in deps if not r.marker or r.marker.matches(project.environment.spec) } for parent in graph.iter_parents(root): if parent: r = specifier_from_requirement(parent.requirements[root.name]) elif not package_is_project(root, project): requirements = required_by.requirements if required_by else all_dependencies if root.name in requirements: r = specifier_from_requirement(requirements[root.name]) else: continue else: r = "This project" required.add(r) children = graph.iter_children(root) if root.name not in visited else [] return OrderedDict( package=root.name, version=root.version, required="&&".join(sorted(required)), dependencies=sorted( ( build_forward_dependency_json_subtree(p, project, graph, root, visited | {root.name}) for p in children if p ), key=lambda d: d["package"], ), ) def build_reverse_dependency_json_subtree( root: PackageNode, project: Project, graph: DirectedGraph[PackageNode | None], requires: PackageNode | None = None, visited: frozenset[str] = frozenset(), ) -> dict: parents = graph.iter_parents(root) if root.name not in visited else [] return OrderedDict( package=root.name, version=root.version, requires=specifier_from_requirement(root.requirements[requires.name]) if requires else None, dependents=sorted( ( build_reverse_dependency_json_subtree(p, project, graph, root, visited | {root.name}) for p in parents if p ), key=lambda d: d["package"], ), ) def package_match_patterns(package: PackageNode, patterns: list[str]) -> bool: return not patterns or any(fnmatch(package.name, pattern) for pattern in patterns) def build_dependency_json_tree( project: Project, graph: DirectedGraph[PackageNode | None], reverse: bool, patterns: list[str] ) -> list[dict]: def find_package_to_add(package: PackageNode) -> PackageNode | None: if not patterns: return package to_check = [package] while to_check: package = to_check.pop(0) if package and package_match_patterns(package, patterns): return package if reverse: to_check.extend(graph.iter_parents(package)) else: to_check.extend(graph.iter_children(package)) return None top_level_packages: Iterable[PackageNode | None] if reverse: top_level_packages = filter(lambda n: not list(graph.iter_children(n)), graph) # leaf nodes build_dependency_json_subtree: Callable = build_reverse_dependency_json_subtree else: top_level_packages = graph.iter_children(None) # root nodes build_dependency_json_subtree = build_forward_dependency_json_subtree top_level_packages = {find_package_to_add(node) for node in top_level_packages if node} return [ build_dependency_json_subtree(p, project, graph) for p in sorted((node for node in top_level_packages if node), key=lambda p: p.name) ] def show_dependency_graph( project: Project, graph: DirectedGraph[PackageNode | None], reverse: bool = False, json: bool = False, patterns: list[str] | None = None, ) -> None: echo = project.core.ui.echo if patterns is None: patterns = [] if json: echo( dumps( build_dependency_json_tree(project, graph, reverse, patterns), indent=2, ) ) return if reverse: tree = _format_reverse_dependency_graph(project, graph, patterns) else: tree = _format_forward_dependency_graph(project, graph, patterns) echo(tree) def save_version_specifiers( requirements: Iterable[Requirement], resolved: dict[str, list[Candidate]], save_strategy: str, ) -> None: """Rewrite the version specifiers according to the resolved result and save strategy :param requirements: the requirements to be updated :param resolved: the resolved mapping :param save_strategy: compatible/safe_compatible/wildcard/exact """ def candidate_version(candidates: list[Candidate]) -> Version | None: if len(candidates) > 1: return None c = candidates[0] assert c.version is not None return comparable_version(c.version) for r in requirements: name = r.identify() if r.is_named and not r.specifier and name in resolved: version = candidate_version(resolved[name]) if version is None: continue if save_strategy == "exact": r.specifier = get_specifier(f"=={version}") elif save_strategy in ["compatible", "safe_compatible"]: if version.is_prerelease or version.is_devrelease: r.specifier = get_specifier(f">={version},<{version.major + 1}") else: if save_strategy == "compatible": r.specifier = get_specifier(f"~={version.major}.{version.minor}") else: r.specifier = get_specifier(f"~={version}") elif save_strategy == "minimum": r.specifier = get_specifier(f">={version}") def check_project_file(project: Project) -> None: """Check the existence of the project file and throws an error on failure.""" if not project.pyproject.is_valid: raise ProjectError( "The pyproject.toml has not been initialized yet. You can do this by running [success]`pdm init`[/]." ) from None def find_importable_files(project: Project) -> Iterable[tuple[str, Path]]: """Find all possible files that can be imported""" from pdm.formats import FORMATS for filename in ("Pipfile", "pyproject.toml", "requirements.in", "requirements.txt", "setup.py", "setup.cfg"): project_file = project.root / filename if not project_file.exists(): continue for key, module in FORMATS.items(): if module.check_fingerprint(project, project_file.as_posix()): yield key, project_file @no_type_check def set_env_in_reg(env_name: str, value: str) -> None: """Manipulate the WinReg, and add value to the environment variable if exists or create new. """ import winreg value = os.path.normcase(value) with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as root: with winreg.OpenKey(root, "Environment", 0, winreg.KEY_ALL_ACCESS) as env_key: try: old_value, type_ = winreg.QueryValueEx(env_key, env_name) paths = [os.path.normcase(item) for item in old_value.split(os.pathsep)] if value in paths: return except FileNotFoundError: paths, type_ = [], winreg.REG_EXPAND_SZ new_value = os.pathsep.join([value, *paths]) winreg.SetValueEx(env_key, env_name, 0, type_, new_value) def format_resolution_impossible(err: ResolutionImpossible) -> str: from pdm.resolver.python import PythonRequirement causes: list[RequirementInformation] = err.causes info_lines: set[str] = set() if not causes: return "" if all(isinstance(cause.requirement, PythonRequirement) for cause in causes): project_requires: PythonRequirement = next(cause.requirement for cause in causes if cause.parent is None) pyspec = cast(PySpecSet, project_requires.specifier) conflicting = [ cause for cause in causes if cause.parent is not None and not cause.requirement.specifier.is_superset(pyspec) ] result = [ "Unable to find a resolution because the following dependencies don't work " "on all Python versions in the range of the project's `requires-python`: " f"[success]{pyspec}[/]." ] for req, parent in conflicting: pyspec &= req.specifier info_lines.add(f" {req.as_line()} (from {parent!r})") result.extend(sorted(info_lines)) if pyspec.is_empty(): result.append("Consider changing the version specifiers of the dependencies to be compatible") else: result.append( "A possible solution is to change the value of `requires-python` " f"in pyproject.toml to [success]{pyspec}[/]." ) return "\n".join(result) if len(causes) == 1: return ( "Unable to find a resolution for " f"[success]{causes[0].requirement.identify()}[/]\n" "Please make sure the package name is correct." ) result = [ "Unable to find a resolution for " f"[success]{causes[0].requirement.identify()}[/]\n" "because of the following conflicts:" ] for req, parent in causes: info_lines.add(f" {req.as_line()} (from {parent if parent else 'project'})") result.extend(sorted(info_lines)) result.append( "To fix this, you could loosen the dependency version constraints in " "pyproject.toml. See https://pdm-project.org/en/latest/usage/lockfile/" "#solve-the-locking-failure for more details." ) return "\n".join(result) def merge_dictionary(target: MutableMapping[Any, Any], input: Mapping[Any, Any], append_array: bool = True) -> None: """Merge the input dict with the target while preserving the existing values properly. This will update the target dictionary in place. List values will be extended, but only if the value is not already in the list. """ for key, value in input.items(): if key not in target: target[key] = value elif isinstance(target[key], dict): merge_dictionary(target[key], value, append_array=append_array) elif isinstance(target[key], list) and append_array: target[key].extend(x for x in value if x not in target[key]) if hasattr(target[key], "multiline"): target[key].multiline(True) # type: ignore[attr-defined] else: target[key] = value def is_pipx_installation() -> bool: return sys.prefix.split(os.sep)[-3:-1] == ["pipx", "venvs"] def is_homebrew_installation() -> bool: return "/libexec" in sys.prefix.replace("\\", "/") def is_scoop_installation() -> bool: return os.name == "nt" and is_path_relative_to(sys.prefix, Path.home() / "scoop/apps/pdm") def get_dist_location(dist: Distribution) -> str: direct_url = dist.read_text("direct_url.json") if not direct_url: return "" direct_url_data = json.loads(direct_url) url = cast(str, direct_url_data["url"]) if url.startswith("file:"): path = url_to_path(url) editable = direct_url_data.get("dir_info", {}).get("editable", False) return f"{'-e ' if editable else ''}{path}" return "" def get_pep582_path(project: Project) -> str: from pdm.compat import resources_open_binary script_dir = Path(__file__).parent.parent / "pep582" if script_dir.exists(): return str(script_dir) script_dir = project.global_config.config_file.parent / "pep582" if script_dir.joinpath("sitecustomize.py").exists(): return str(script_dir) script_dir.mkdir(parents=True, exist_ok=True) with resources_open_binary("pdm.pep582", "sitecustomize.py") as f: script_dir.joinpath("sitecustomize.py").write_bytes(f.read()) return str(script_dir) def use_venv(project: Project, name: str) -> None: from pdm.cli.commands.venv.utils import get_venv_with_name from pdm.environments import PythonEnvironment venv = get_venv_with_name(project, cast(str, name)) project.core.ui.info(f"In virtual environment: [success]{venv.root}[/]", verbosity=termui.Verbosity.DETAIL) project.environment = PythonEnvironment(project, python=str(venv.interpreter)) def normalize_pattern(pattern: str) -> str: """Normalize a pattern to a valid name for a package.""" return re.sub(r"[^A-Za-z0-9*?]+", "-", pattern).lower() pdm-2.23.1/src/pdm/compat.py000066400000000000000000000065371477560627500156210ustar00rootroot00000000000000from __future__ import annotations import importlib.resources import sys from collections.abc import Iterator from pathlib import Path from typing import TYPE_CHECKING, BinaryIO, ContextManager, Sequence, TypeVar if TYPE_CHECKING: from typing import Protocol class SupportsIdentify(Protocol): def identify(self) -> str: ... if sys.version_info >= (3, 11): import tomllib else: import tomli as tomllib T = TypeVar("T", bound="SupportsIdentify") if ( not (sys.version_info[:2] == (3, 9) and sys.platform == "win32") # a bug on windows+py39 that zipfile path is not normalized ): def resources_open_binary(package: str, resource: str) -> BinaryIO: return (importlib.resources.files(package) / resource).open("rb") def resources_read_text(package: str, resource: str, encoding: str = "utf-8", errors: str = "strict") -> str: with (importlib.resources.files(package) / resource).open("r", encoding=encoding, errors=errors) as f: return f.read() def resources_path(package: str, resource: str) -> ContextManager[Path]: return importlib.resources.as_file(importlib.resources.files(package) / resource) else: resources_open_binary = importlib.resources.open_binary resources_read_text = importlib.resources.read_text resources_path = importlib.resources.path if sys.version_info >= (3, 10): import importlib.metadata as importlib_metadata else: import importlib_metadata Distribution = importlib_metadata.Distribution class CompatibleSequence(Sequence[T]): # pragma: no cover """A compatibility class for Sequence that also exposes `items()`, `keys()` and `values()` methods""" def __init__(self, data: Sequence[T]) -> None: self._data = data def __getitem__(self, index: str | slice | int) -> T | Sequence[T]: # type: ignore[override] if isinstance(index, str): from pdm.utils import deprecation_warning deprecation_warning( "__getitem__ with a string key is deprecated on the requirements collection. It's not a mapping but a list", stacklevel=2, ) for r in self._data: if r.identify() == index: return r raise KeyError(index) return self._data[index] def __len__(self) -> int: return len(self._data) def __iter__(self) -> Iterator[T]: return iter(self._data) def keys(self) -> Sequence[str]: from pdm.utils import deprecation_warning deprecation_warning( ".keys() is deprecated on the requirements collection, it's not a mapping but a list.", stacklevel=2 ) return [r.identify() for r in self._data] def values(self) -> Sequence[T]: from pdm.utils import deprecation_warning deprecation_warning( ".values() is deprecated on the requirements collection, it's not a mapping but a list.", stacklevel=2 ) return self._data def items(self) -> Iterator[tuple[str, T]]: from pdm.utils import deprecation_warning deprecation_warning( ".items() is deprecated on the requirements collection, it's not a mapping anymore.", stacklevel=2 ) for r in self._data: yield r.identify(), r __all__ = ["CompatibleSequence", "Distribution", "importlib_metadata", "tomllib"] pdm-2.23.1/src/pdm/core.py000066400000000000000000000344351477560627500152640ustar00rootroot00000000000000r""" ____ ____ __ ___ / __ \/ __ \/ |/ / / /_/ / / / / /|_/ / / ____/ /_/ / / / / /_/ /_____/_/ /_/ """ from __future__ import annotations import argparse import contextlib import dataclasses as dc import importlib import itertools import os import pkgutil import sys from datetime import datetime from functools import cached_property from pathlib import Path from tempfile import TemporaryDirectory from typing import TYPE_CHECKING, ClassVar, cast import tomlkit.exceptions from pdm import termui from pdm.__version__ import __version__ from pdm.cli.options import ignore_python_option, no_cache_option, non_interactive_option, pep582_option, verbose_option from pdm.cli.utils import ArgumentParser, ErrorArgumentParser, format_similar_command from pdm.compat import importlib_metadata from pdm.exceptions import PdmArgumentError, PdmUsageError from pdm.installers import InstallManager from pdm.models.repositories import BaseRepository, PyPIRepository from pdm.project import Project from pdm.project.config import Config from pdm.utils import is_in_zipapp if TYPE_CHECKING: from typing import Any, Iterable from pdm.cli.commands.base import BaseCommand from pdm.project.config import ConfigItem COMMANDS_MODULE_PATH = importlib.import_module("pdm.cli.commands").__path__ @dc.dataclass class State: """State of the core object.""" config_settings: dict[str, Any] | None = None """The config settings map shared by all packages""" exclude_newer: datetime | None = None """The exclude newer than datetime for the lockfile""" build_isolation: bool = True """Whether to make an isolated environment and install requirements for build""" enable_cache: bool = True """Whether to enable the cache""" overrides: list[str] = dc.field(default_factory=list) """The requirement overrides for the resolver""" class Core: """A high level object that manages all classes and configurations""" parser: argparse.ArgumentParser subparsers: argparse._SubParsersAction project_class = Project repository_class: type[BaseRepository] = PyPIRepository install_manager_class = InstallManager commands: ClassVar[list[str]] = [] def __init__(self) -> None: self.version = __version__ self.exit_stack = contextlib.ExitStack() self.ui = termui.UI(exit_stack=self.exit_stack) self.state = State() self.exit_stack.callback(setattr, self, "config_settings", None) self.init_parser() self.load_plugins() def create_temp_dir(self, *args: Any, **kwargs: Any) -> str: return self.exit_stack.enter_context(TemporaryDirectory(*args, **kwargs)) def init_parser(self) -> None: self.parser = ErrorArgumentParser( prog="pdm", description=__doc__, ) self.parser.add_argument( "-V", "--version", action="version", version="{}, version {}".format( termui.style("PDM", style="bold"), termui.style(self.version, style="success"), ), help="Show the version and exit", ) self.parser.add_argument( "-c", "--config", help="Specify another config file path [env var: PDM_CONFIG_FILE] ", ) verbose_option.add_to_parser(self.parser) no_cache_option.add_to_parser(self.parser) ignore_python_option.add_to_parser(self.parser) pep582_option.add_to_parser(self.parser) non_interactive_option.add_to_parser(self.parser) self.subparsers = self.parser.add_subparsers(parser_class=ArgumentParser, title="commands", metavar="") for _, name, _ in pkgutil.iter_modules(COMMANDS_MODULE_PATH): module = importlib.import_module(f"pdm.cli.commands.{name}", __name__) try: klass = module.Command except AttributeError: continue self.register_command(klass, klass.name or name) def __call__(self, *args: Any, **kwargs: Any) -> None: return self.main(*args, **kwargs) def ensure_project(self, options: argparse.Namespace, obj: Project | None) -> Project: if obj is not None: project = obj else: global_project = bool(getattr(options, "global_project", None)) default_root = None if global_project or getattr(options, "search_parent", True) else "." project = self.create_project( getattr(options, "project_path", None) or default_root, is_global=global_project, global_config=options.config or os.getenv("PDM_CONFIG_FILE"), ) self.state.build_isolation = project.config["build_isolation"] return project def create_project( self, root_path: str | Path | None = None, is_global: bool = False, global_config: str | None = None, ) -> Project: """Create a new project object Args: root_path (PathLike): The path to the project root directory is_global (bool): Whether the project is a global project global_config (str): The path to the global config file Returns: The project object """ return self.project_class(self, root_path, is_global, global_config) def handle(self, project: Project, options: argparse.Namespace) -> None: """Called before command invocation""" from pdm.cli.commands.fix import Command as FixCommand from pdm.cli.hooks import HookManager from pdm.cli.utils import use_venv self.ui.set_verbosity(options.verbose) self.ui.set_theme(project.global_config.load_theme()) self.ui.log_dir = os.path.expanduser(cast(str, project.config["log_dir"])) command = cast("BaseCommand | None", getattr(options, "command", None)) self.state.config_settings = getattr(options, "config_setting", None) if options.no_cache: self.state.enable_cache = False hooks = HookManager(project, getattr(options, "skip", None)) hooks.try_emit("pre_invoke", command=command.name if command else None, options=options) if not isinstance(command, FixCommand): FixCommand.check_problems(project) for callback in getattr(options, "callbacks", []): callback(project, options) if lockfile := getattr(options, "lockfile", None): project.set_lockfile(cast(str, lockfile)) if getattr(options, "use_venv", None): use_venv(project, cast(str, options.use_venv)) if overrides := getattr(options, "override", None): self.state.overrides = overrides if command is None: self.parser.print_help() sys.exit(0) command.handle(project, options) @staticmethod def get_command(args: list[str]) -> tuple[int, str]: """Get the command name from the arguments""" options_with_values = ("-c", "--config") need_value = False for i, arg in enumerate(args): if arg.startswith("-"): if not arg.startswith(options_with_values): continue if (arg.startswith("-c") and arg != "-c") or arg.startswith("--config="): continue need_value = True elif need_value: need_value = False continue else: return i, arg return -1, "" def _get_cli_args(self, args: list[str], obj: Project | None) -> list[str]: project = self.create_project(is_global=False) if obj is None else obj if project.is_global: return args try: config = project.pyproject.settings.get("options", {}) except tomlkit.exceptions.TOMLKitError as e: # pragma: no cover self.ui.error(f"Failed to parse pyproject.toml: {e}") config = {} (pos, command) = self.get_command(args) if command and command in config: # add args after the command args[pos + 1 : pos + 1] = list(config[command]) return args def main( self, args: list[str] | None = None, prog_name: str | None = None, obj: Project | None = None, **extra: Any, ) -> None: """The main entry function""" if args is None: args = [] args = self._get_cli_args(args, obj) # Keep it for after project parsing to check if its a defined script root_script = None try: options = self.parser.parse_args(args) except PdmArgumentError as e: # Failed to parse, try to give all to `run` command as shortcut # and keep to root script (first non-dashed param) to check existence # as soon as the project is parsed _, root_script = self.get_command(args) if not root_script: self.parser.error(str(e.__cause__)) try: options = self.parser.parse_args(["run", *args]) except PdmArgumentError as e: self.parser.error(str(e.__cause__)) project = self.ensure_project(options, obj) if root_script and root_script not in project.scripts: message = format_similar_command(root_script, self.commands, list(project.scripts.keys())) message = termui.style(message) self.parser.error(message) try: self.handle(project, options) except Exception: etype, err, traceback = sys.exc_info() should_show_tb = not isinstance(err, PdmUsageError) if self.ui.verbosity > termui.Verbosity.NORMAL and should_show_tb: raise cast(Exception, err).with_traceback(traceback) from None self.ui.echo( rf"[error]\[{etype.__name__}][/]: {err}", # type: ignore[union-attr] err=True, ) if should_show_tb: self.ui.warn("Add '-v' to see the detailed traceback", verbosity=termui.Verbosity.NORMAL) sys.exit(1) else: if project.config["check_update"] and not is_in_zipapp(): from pdm.cli.actions import check_update check_update(project) def register_command(self, command: type[BaseCommand], name: str | None = None) -> None: """Register a subcommand to the subparsers, with an optional name of the subcommand. Args: command (Type[pdm.cli.commands.base.BaseCommand]): The command class to register name (str): The name of the subcommand, if not given, `command.name` is used """ assert self.subparsers if name: self.commands.append(name) command.register_to(self.subparsers, name) @staticmethod def add_config(name: str, config_item: ConfigItem) -> None: """Add a config item to the configuration class. Args: name (str): The name of the config item config_item (pdm.project.config.ConfigItem): The config item to add """ Config.add_config(name, config_item) def _add_project_plugins_library(self) -> None: project = self.create_project(is_global=False) if project.is_global or not project.root.joinpath(".pdm-plugins").exists(): return import site import sysconfig base = str(project.root / ".pdm-plugins") replace_vars = {"base": base, "platbase": base} scheme_names = sysconfig.get_scheme_names() if (sys.platform == "darwin" and "osx_framework_library" in scheme_names) or sys.platform == "linux": scheme = "posix_prefix" # sysconfig._get_default_scheme is a private function in 3.8 & 3.9 elif sys.version_info < (3, 10): scheme = "nt" if os.name == "nt" else "posix_prefix" else: scheme = sysconfig.get_default_scheme() purelib = sysconfig.get_path("purelib", scheme, replace_vars) scripts = sysconfig.get_path("scripts", scheme, replace_vars) site.addsitedir(purelib) if os.path.exists(scripts): os.environ["PATH"] = os.pathsep.join([scripts, os.getenv("PATH", "")]) def load_plugins(self) -> None: """Import and load plugins under `pdm.plugin` namespace A plugin is a callable that accepts the core object as the only argument. Example: ```python def my_plugin(core: pdm.core.Core) -> None: ... ``` """ self._add_project_plugins_library() entry_points: Iterable[importlib_metadata.EntryPoint] = itertools.chain( importlib_metadata.entry_points(group="pdm"), importlib_metadata.entry_points(group="pdm.plugin"), ) for plugin in entry_points: try: plugin.load()(self) except Exception as e: self.ui.error( f"Failed to load plugin {plugin.name}={plugin.value}: {e}", ) @cached_property def uv_cmd(self) -> list[str]: from pdm.compat import importlib_metadata self.ui.info("Using uv is experimental and might break due to uv updates.") # First, try to find uv in Python modules try: importlib_metadata.distribution("uv") except ModuleNotFoundError: pass else: return [sys.executable, "-m", "uv"] # Try to find it in the typical place: for bin_dir in [".local/bin", ".cargo/bin"]: if (uv_path := Path.home() / bin_dir / "uv").exists(): return [str(uv_path)] # If not found, try to find it in PATH import shutil path = shutil.which("uv") if path: return [path] # If not found, try to find in the bin dir: if (uv_path := Path(sys.argv[0]).with_name("uv")).exists(): return [str(uv_path)] raise PdmUsageError( "use_uv is enabled but can't find uv, please install it first: " "https://docs.astral.sh/uv/getting-started/installation/" ) def main(args: list[str] | None = None) -> None: """The CLI entry function""" core = Core() with core.exit_stack: return core.main(args or sys.argv[1:]) pdm-2.23.1/src/pdm/environments/000077500000000000000000000000001477560627500165005ustar00rootroot00000000000000pdm-2.23.1/src/pdm/environments/__init__.py000066400000000000000000000004471477560627500206160ustar00rootroot00000000000000from pdm.environments.base import BareEnvironment, BaseEnvironment from pdm.environments.local import PythonLocalEnvironment from pdm.environments.python import PythonEnvironment __all__ = [ "BareEnvironment", "BaseEnvironment", "PythonEnvironment", "PythonLocalEnvironment", ] pdm-2.23.1/src/pdm/environments/base.py000066400000000000000000000247661477560627500200030ustar00rootroot00000000000000from __future__ import annotations import abc import os import re import shutil import subprocess import sys import tempfile import weakref from contextlib import contextmanager from functools import cached_property from pathlib import Path from typing import TYPE_CHECKING, Generator from pdm._types import NotSet, NotSetType from pdm.exceptions import BuildError, PdmUsageError from pdm.models.in_process import get_env_spec from pdm.models.markers import EnvSpec from pdm.models.python import PythonInfo from pdm.models.working_set import WorkingSet from pdm.utils import deprecation_warning, is_pip_compatible_with_python if TYPE_CHECKING: import unearth from httpx import BaseTransport from pdm._types import RepositoryConfig from pdm.models.session import PDMPyPIClient from pdm.project import Project class BaseEnvironment(abc.ABC): """Environment dependent stuff related to the selected Python interpreter.""" project: Project is_local = False def __init__(self, project: Project, *, python: str | None = None) -> None: """ :param project: the project instance """ from pdm.models.auth import PdmBasicAuth if isinstance(project, weakref.ProxyTypes): self.project = project else: self.project = weakref.proxy(project) self.python_requires = project.python_requires self.auth = PdmBasicAuth(project.core.ui, self.project.sources) if python is None: self._interpreter = project.python else: self._interpreter = PythonInfo.from_path(python) @property def is_global(self) -> bool: """For backward compatibility, it is opposite to ``is_local``.""" return not self.is_local @property def interpreter(self) -> PythonInfo: return self._interpreter @abc.abstractmethod def get_paths(self, dist_name: str | None = None) -> dict[str, str]: """Get paths like ``sysconfig.get_paths()`` for installation. :param dist_name: The package name to be installed, if any. """ ... @property def process_env(self) -> dict[str, str]: """Get the process env var dict for the environment.""" project = self.project this_path = self.get_paths()["scripts"] python_root = os.path.dirname(project.python.executable) new_path = os.pathsep.join([this_path, os.getenv("PATH", ""), python_root]) return {"PATH": new_path, "PDM_PROJECT_ROOT": str(project.root)} def _build_session( self, sources: list[RepositoryConfig] | None = None, mounts: dict[str, BaseTransport | None] | None = None ) -> PDMPyPIClient: from pdm.models.session import PDMPyPIClient if sources is None: sources = self.project.sources session = PDMPyPIClient( sources=sources, cache_dir=self.project.cache("http") if self.project.core.state.enable_cache else None, timeout=self.project.config["request_timeout"], auth=self.auth, mounts=mounts, ) self.project.core.exit_stack.callback(session.close) return session @cached_property def session(self) -> PDMPyPIClient: """Build the session and cache it.""" return self._build_session() @contextmanager def get_finder( self, sources: list[RepositoryConfig] | None = None, ignore_compatibility: bool | NotSetType = NotSet, minimal_version: bool = False, env_spec: EnvSpec | None = None, ) -> Generator[unearth.PackageFinder]: """Return the package finder of given index sources. :param sources: a list of sources the finder should search in. :param ignore_compatibility: (DEPRECATED)whether to ignore the python version and wheel tags. :param minimal_version: whether to find the minimal version of the package. :param env_spec: the environment spec to filter the packages. """ from pdm.models.finder import PDMPackageFinder if sources is None: sources = self.project.sources if not sources: raise PdmUsageError( "You must specify at least one index in pyproject.toml or config.\n" "The 'pypi.ignore_stored_index' config value is " f"{self.project.config['pypi.ignore_stored_index']}" ) if ignore_compatibility is not NotSet: # pragma: no cover deprecation_warning( "`ignore_compatibility` argument is deprecated, pass in `env_spec` instead.\n", stacklevel=2, ) else: ignore_compatibility = False if env_spec is None: if ignore_compatibility: # pragma: no cover env_spec = self.allow_all_spec else: env_spec = self.spec finder = PDMPackageFinder( session=self.session, env_spec=env_spec, no_binary=self._setting_list("PDM_NO_BINARY", "resolution.no-binary"), only_binary=self._setting_list("PDM_ONLY_BINARY", "resolution.only-binary"), prefer_binary=self._setting_list("PDM_PREFER_BINARY", "resolution.prefer-binary"), respect_source_order=self.project.pyproject.settings.get("resolution", {}).get( "respect-source-order", False ), verbosity=self.project.core.ui.verbosity, minimal_version=minimal_version, exclude_newer_than=self.project.core.state.exclude_newer, ) finder.sources.clear() for source in sources: assert source.url if source.type == "find_links": finder.add_find_links(source.url) else: finder.add_index_url(source.url) yield finder def _setting_list(self, var: str, key: str) -> list[str]: """ Get a list value, either comma separated or structured. Returns `None` if both the environment variable and the key does not exists. """ if value := self.project.env_or_setting(var, key): if isinstance(value, str): value = [stripped for v in value.split(",") if (stripped := v.strip())] return [stripped for v in value if (stripped := v.strip())] return [] def get_working_set(self) -> WorkingSet: """Get the working set based on local packages directory.""" paths = self.get_paths() return WorkingSet([paths["platlib"], paths["purelib"]]) @cached_property def spec(self) -> EnvSpec: return get_env_spec(self.interpreter.executable.as_posix()) @property def allow_all_spec(self) -> EnvSpec: return EnvSpec(self.python_requires._logic) def which(self, command: str) -> str | None: """Get the full path of the given executable against this environment.""" if not os.path.isabs(command) and command.startswith("python"): match = re.match(r"python(\d(?:\.\d{1,2})?)", command) this_version = self.interpreter.version if not match or str(this_version).startswith(match.group(1)): return str(self.interpreter.executable) # Fallback to use shutil.which to find the executable this_path = self.get_paths()["scripts"] python_root = os.path.dirname(self.interpreter.executable) new_path = os.pathsep.join([this_path, os.getenv("PATH", ""), python_root]) return shutil.which(command, path=new_path) def _download_pip_wheel(self, path: str | Path) -> None: # pragma: no cover from unearth import UnpackError download_error = BuildError("Can't get a working copy of pip for the project") with self.get_finder([self.project.default_source]) as finder: finder.only_binary = {"pip"} best_match = finder.find_best_match("pip").best if not best_match: raise download_error with tempfile.TemporaryDirectory(prefix="pip-download-") as dirname: try: downloaded = finder.download_and_unpack(best_match.link, dirname, dirname) except UnpackError as e: raise download_error from e shutil.move(str(downloaded), path) @cached_property def pip_command(self) -> list[str]: """Get a pip command for this environment, and download one if not available. Return a list of args like ['python', '-m', 'pip'] """ try: from pip import __file__ as pip_location except ImportError: pip_location = None # type: ignore[assignment] python_version = self.interpreter.version executable = str(self.interpreter.executable) proc = subprocess.run([executable, "-Esm", "pip", "--version"], capture_output=True) if proc.returncode == 0: # The pip has already been installed with the executable, just use it command = [executable, "-Esm", "pip"] elif pip_location and is_pip_compatible_with_python(python_version): # Use the host pip package if available command = [executable, "-Es", os.path.dirname(pip_location)] else: # Otherwise, download a pip wheel from the Internet. pip_wheel = self.project.cache_dir / "pip.whl" if not pip_wheel.is_file(): self._download_pip_wheel(pip_wheel) command = [executable, str(pip_wheel / "pip")] verbosity = self.project.core.ui.verbosity if verbosity > 0: command.append("-" + "v" * verbosity) return command @property def script_kind(self) -> str: from dep_logic.tags.platform import Arch if os.name != "nt": return "posix" if (arch := self.spec.platform.arch) == Arch.X86: # pragma: no cover return "win-ia32" elif arch == Arch.Aarch64: # pragma: no cover return "win-arm64" else: return "win-amd64" class BareEnvironment(BaseEnvironment): """Bare environment that does not depend on project files.""" def __init__(self, project: Project) -> None: super().__init__(project, python=sys.executable) def get_paths(self, dist_name: str | None = None) -> dict[str, str]: return {} def get_working_set(self) -> WorkingSet: if self.project.project_config.config_file.exists(): return self.project.get_environment().get_working_set() else: return WorkingSet([]) pdm-2.23.1/src/pdm/environments/local.py000066400000000000000000000104741477560627500201520ustar00rootroot00000000000000from __future__ import annotations import os import re import shlex from functools import cached_property from pathlib import Path from pdm.environments.base import BaseEnvironment from pdm.utils import pdm_scheme def _get_shebang_path(executable: str, is_launcher: bool) -> bytes: """Get the interpreter path in the shebang line The launcher can just use the command as-is. Otherwise if the path contains whitespace or is too long, both distlib and installer use a clever hack to make the shebang after ``/bin/sh``, where the interpreter path is quoted. """ if is_launcher or (" " not in executable and (len(executable) + 3) <= 127): return executable.encode("utf-8") return shlex.quote(executable).encode("utf-8") def _is_console_script(content: bytes) -> bool: import io import zipfile if os.name == "nt": # Windows .exe should be a zip file. try: with zipfile.ZipFile(io.BytesIO(content)) as zf: return zf.namelist() == ["__main__.py"] except zipfile.BadZipFile: return False try: text = content.decode("utf-8") return text.startswith("#!") except UnicodeDecodeError: return False def _replace_shebang(path: Path, new_executable: bytes) -> None: """Replace the python executable from the shebang line, which can be in two forms: 1. #!python_executable 2. #!/bin/sh '''exec' '/path to/python' "$0" "$@" ' ''' """ _complex_shebang_re = rb"^(#!/bin/sh\n'''exec' )('.+?')( \"\$0\")" _simple_shebang_re = rb"^(#!)(.+?)\s*(?=\n)" contents = path.read_bytes() if not _is_console_script(contents): return if os.name == "nt": new_content, count = re.subn(_simple_shebang_re, rb"\1" + new_executable, contents, count=1, flags=re.M) if count > 0: path.write_bytes(new_content) return new_content, count = re.subn(_complex_shebang_re, rb"\1" + new_executable + rb"\3", contents, count=1) if count > 0: path.write_bytes(new_content) return new_content, count = re.subn(_simple_shebang_re, rb"\1" + new_executable, contents, count=1) if count > 0: path.write_bytes(new_content) class PythonLocalEnvironment(BaseEnvironment): """A project environment that installs packages into the local `__pypackages__` directory(PEP 582). """ is_local = True @property def process_env(self) -> dict[str, str]: from pdm.cli.utils import get_pep582_path env = super().process_env pythonpath = os.getenv("PYTHONPATH", "").split(os.pathsep) pythonpath = [get_pep582_path(self.project)] + [p for p in pythonpath if "/pep582" not in p.replace("\\", "/")] env["PYTHONPATH"] = os.pathsep.join(pythonpath) env["PEP582_PACKAGES"] = str(self.packages_path) return env @cached_property def packages_path(self) -> Path: """The local packages path.""" pypackages = self.project.root / "__pypackages__" / self.interpreter.identifier if not pypackages.exists() and "-32" in pypackages.name: compatible_packages = pypackages.with_name(pypackages.name[:-3]) if compatible_packages.exists(): pypackages = compatible_packages scripts = "Scripts" if os.name == "nt" else "bin" if not pypackages.parent.exists(): pypackages.parent.mkdir(parents=True) pypackages.parent.joinpath(".gitignore").write_text("*\n!.gitignore\n") for subdir in [scripts, "include", "lib"]: pypackages.joinpath(subdir).mkdir(exist_ok=True, parents=True) return pypackages def get_paths(self, dist_name: str | None = None) -> dict[str, str]: scheme = pdm_scheme(self.packages_path.as_posix()) scheme["headers"] = os.path.join(scheme["headers"], dist_name or "UNKNOWN") return scheme def update_shebangs(self, new_path: str) -> None: """Update the shebang lines""" scripts = self.get_paths()["scripts"] for child in Path(scripts).iterdir(): if not child.is_file() or child.suffix not in (".exe", ".py", ""): continue is_launcher = child.suffix == ".exe" new_shebang = _get_shebang_path(new_path, is_launcher) _replace_shebang(child, new_shebang) pdm-2.23.1/src/pdm/environments/python.py000066400000000000000000000044751477560627500204050ustar00rootroot00000000000000from __future__ import annotations import os from typing import TYPE_CHECKING from pdm.environments.base import BaseEnvironment from pdm.models.in_process import get_sys_config_paths from pdm.models.working_set import WorkingSet if TYPE_CHECKING: from pdm.project import Project class PythonEnvironment(BaseEnvironment): """A project environment that is directly derived from a Python interpreter""" def __init__( self, project: Project, *, python: str | None = None, prefix: str | None = None, extra_paths: list[str] | None = None, ) -> None: super().__init__(project, python=python) self.prefix = prefix self.extra_paths = extra_paths or [] def get_paths(self, dist_name: str | None = None) -> dict[str, str]: is_venv = self.interpreter.get_venv() is not None if self.prefix is not None: replace_vars = {"base": self.prefix, "platbase": self.prefix} kind = "prefix" else: replace_vars = None kind = "user" if not is_venv and self.project.global_config["global_project.user_site"] else "default" paths = get_sys_config_paths(str(self.interpreter.executable), replace_vars, kind=kind) if is_venv: python_xy = f"python{self.interpreter.identifier}" paths["include"] = os.path.join(paths["data"], "include", "site", python_xy) elif not dist_name: dist_name = "UNKNOWN" if dist_name: paths["include"] = os.path.join(paths["include"], dist_name) paths["prefix"] = paths["data"] paths["headers"] = paths["include"] return paths @property def process_env(self) -> dict[str, str]: env = super().process_env venv = self.interpreter.get_venv() if venv is not None and self.prefix is None: env.update(venv.env_vars()) return env def get_working_set(self) -> WorkingSet: scheme = self.get_paths() paths = [scheme["platlib"], scheme["purelib"]] venv = self.interpreter.get_venv() shared_paths = self.extra_paths[:] if venv is not None and venv.include_system_site_packages: shared_paths.extend(venv.base_paths) return WorkingSet(paths, shared_paths=list(dict.fromkeys(shared_paths))) pdm-2.23.1/src/pdm/exceptions.py000066400000000000000000000031561477560627500165110ustar00rootroot00000000000000from __future__ import annotations import warnings from typing import TYPE_CHECKING if TYPE_CHECKING: from pdm.models.candidates import Candidate class PdmException(Exception): pass class ResolutionError(PdmException): pass class PdmArgumentError(PdmException): pass class PdmUsageError(PdmException): pass class RequirementError(PdmUsageError, ValueError): pass class PublishError(PdmUsageError): pass class InvalidPyVersion(PdmUsageError, ValueError): pass class CandidateNotFound(PdmException): pass class CandidateInfoNotFound(PdmException): def __init__(self, candidate: Candidate) -> None: message = f"No metadata information is available for [success]{candidate!s}[/]." self.candidate = candidate super().__init__(message) class PDMWarning(Warning): pass class PackageWarning(PDMWarning): pass class PDMDeprecationWarning(PDMWarning, DeprecationWarning): pass warnings.simplefilter("default", category=PDMDeprecationWarning) class ExtrasWarning(PDMWarning): def __init__(self, project_name: str, extras: list[str]) -> None: super().__init__(f"Extras not found for {project_name}: [{','.join(extras)}]") self.extras = tuple(extras) class ProjectError(PdmUsageError): pass class InstallationError(PdmException): pass class UninstallError(PdmException): pass class NoConfigError(PdmUsageError, KeyError): def __str__(self) -> str: return f"No such config key: {self.args[0]!r}" class NoPythonVersion(PdmUsageError): pass class BuildError(PdmException, RuntimeError): pass pdm-2.23.1/src/pdm/formats/000077500000000000000000000000001477560627500154245ustar00rootroot00000000000000pdm-2.23.1/src/pdm/formats/__init__.py000066400000000000000000000023201477560627500175320ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING, cast from pdm.formats import flit, pipfile, poetry, requirements, setup_py from pdm.formats.base import MetaConvertError as MetaConvertError if TYPE_CHECKING: from argparse import Namespace from pathlib import Path from typing import Iterable, Mapping, Protocol, Union from pdm.models.candidates import Candidate from pdm.models.requirements import Requirement from pdm.project import Project ExportItems = Union[Iterable[Candidate], Iterable[Requirement]] class _Format(Protocol): def check_fingerprint(self, project: Project | None, filename: str | Path) -> bool: ... def convert( self, project: Project | None, filename: str | Path, options: Namespace | None, ) -> tuple[Mapping, Mapping]: ... def export(self, project: Project, candidates: ExportItems, options: Namespace | None) -> str: ... FORMATS: Mapping[str, _Format] = { "pipfile": cast("_Format", pipfile), "poetry": cast("_Format", poetry), "flit": cast("_Format", flit), "setuppy": cast("_Format", setup_py), "requirements": cast("_Format", requirements), } pdm-2.23.1/src/pdm/formats/base.py000066400000000000000000000065061477560627500167170ustar00rootroot00000000000000from __future__ import annotations from typing import Any, Callable, Mapping, TypeVar, cast import tomlkit from pdm import termui _T = TypeVar("_T", bound=Callable) def convert_from(field: str | None = None, name: str | None = None) -> Callable[[_T], _T]: def wrapper(func: _T) -> _T: func._convert_from = field # type: ignore[attr-defined] func._convert_to = name # type: ignore[attr-defined] return func return wrapper class Unset(Exception): pass class MetaConvertError(Exception): """A special exception that preserves the partial metadata that are already resolved.""" def __init__(self, errors: list[str], *, data: dict[str, Any], settings: dict[str, Any]) -> None: self.errors = errors self.data = data self.settings = settings def __str__(self) -> str: return "\n" + "\n".join(self.errors) class _MetaConverterMeta(type): def __init__(cls, name: str, bases: tuple[type, ...], ns: dict[str, Any]) -> None: super().__init__(name, bases, ns) cls._converters = {} _default = object() for key, value in ns.items(): if getattr(value, "_convert_from", _default) is not _default: name = value._convert_to or key cls._converters[name] = value class MetaConverter(metaclass=_MetaConverterMeta): """Convert a metadata dictionary to PDM's format""" _converters: dict[str, Callable] def __init__(self, source: dict, ui: termui.UI | None = None) -> None: self.source = source self.settings: dict[str, Any] = {} self._data: dict[str, Any] = {} self._ui = ui def convert(self) -> tuple[Mapping[str, Any], Mapping[str, Any]]: source = self.source errors: list[str] = [] for key, func in self._converters.items(): if func._convert_from and func._convert_from not in source: # type: ignore[attr-defined] continue value = source if func._convert_from is None else source[func._convert_from] # type: ignore[attr-defined] try: self._data[key] = func(self, value) except Unset: pass except Exception as e: errors.append(f"{key}: {e}") # Delete all used fields for func in self._converters.values(): if func._convert_from is None: # type: ignore[attr-defined] continue try: del source[func._convert_from] # type: ignore[attr-defined] except KeyError: pass # Add remaining items to the data self._data.update(source) if errors: raise MetaConvertError(errors, data=self._data, settings=self.settings) return self._data, self.settings def make_inline_table(data: Mapping) -> dict: """Create an inline table from the given data.""" table = cast(dict, tomlkit.inline_table()) table.update(data) return table def make_array(data: list, multiline: bool = False) -> list: if not data: return [] array = cast(list, tomlkit.array().multiline(multiline)) array.extend(data) return array def array_of_inline_tables(value: list[Mapping], multiline: bool = True) -> list[str]: return make_array([make_inline_table(item) for item in value], multiline) pdm-2.23.1/src/pdm/formats/flit.py000066400000000000000000000131731477560627500167410ustar00rootroot00000000000000from __future__ import annotations import ast import os from pathlib import Path from typing import TYPE_CHECKING, Any, Mapping, cast from pdm.compat import tomllib from pdm.formats.base import ( MetaConverter, Unset, array_of_inline_tables, convert_from, make_array, make_inline_table, ) from pdm.utils import cd if TYPE_CHECKING: from argparse import Namespace from os import PathLike from pdm.project import Project def check_fingerprint(project: Project | None, filename: PathLike) -> bool: with open(filename, "rb") as fp: try: data = tomllib.load(fp) except tomllib.TOMLDecodeError: return False return "tool" in data and "flit" in data["tool"] def _get_author(metadata: dict[str, Any], type_: str = "author") -> list[str]: name = metadata.pop(type_) email = metadata.pop(f"{type_}-email", None) return cast(list[str], array_of_inline_tables([{"name": name, "email": email}])) def get_docstring_and_version_via_ast( target: Path, ) -> tuple[str | None, str | None]: """ This function is borrowed from flit's implementation, but does not attempt to import that file. If docstring or version can't be retrieved by this function, they are just left empty. """ # read as bytes to enable custom encodings if not target.exists(): return None, None node = ast.parse(target.read_bytes()) for child in node.body: # Only use the version from the given module if it's a simple # string assignment to __version__ is_version_str = ( isinstance(child, ast.Assign) and len(child.targets) == 1 and isinstance(child.targets[0], ast.Name) and child.targets[0].id == "__version__" and isinstance(child.value, ast.Str) ) if is_version_str: version: str | None = cast(ast.Str, cast(ast.Assign, child).value).s break else: version = None return ast.get_docstring(node), version class FlitMetaConverter(MetaConverter): def warn_against_dynamic_version_or_docstring(self, source: Path, version: str, description: str) -> None: if not self._ui: return dynamic_fields = [] if not version: dynamic_fields.append("version") if not description: dynamic_fields.append("description") if not dynamic_fields: return fields = " and ".join(dynamic_fields) message = ( f"Can't retrieve {fields} from pyproject.toml or parsing {source}. " "They are probably imported from other files which is not supported by PDM." " You may need to supply their values in pyproject.toml manually." ) self._ui.warn(message) @convert_from("metadata") def name(self, metadata: dict[str, Any]) -> str: # name module = metadata.pop("module") self._data["name"] = metadata.pop("dist-name", module) # version and description if (Path(module) / "__init__.py").exists(): source = Path(module) / "__init__.py" else: source = Path(f"{module}.py") version = self._data.get("version") description = self._data.get("description") description_in_ast, version_in_ast = get_docstring_and_version_via_ast(source) self._data["version"] = version or version_in_ast or "" self._data["description"] = description or description_in_ast or "" self.warn_against_dynamic_version_or_docstring(source, self._data["version"], self._data["description"]) # author and maintainer if "author" in metadata: self._data["authors"] = _get_author(metadata) if "maintainer" in metadata: self._data["maintainers"] = _get_author(metadata, "maintainer") if "license" in metadata: self._data["license"] = make_inline_table({"text": metadata.pop("license")}) self._data["dynamic"] = ["classifiers"] if "urls" in metadata: self._data["urls"] = metadata.pop("urls") if "home-page" in metadata: self._data.setdefault("urls", {})["homepage"] = metadata.pop("home-page") if "description-file" in metadata: self._data["readme"] = metadata.pop("description-file") if "requires-python" in metadata: self._data["requires-python"] = metadata.pop("requires-python") self._data["dynamic"] = ["classifiers"] # requirements self._data["dependencies"] = make_array(metadata.pop("requires", []), True) self._data["optional-dependencies"] = metadata.pop("requires-extra", {}) # Add remaining metadata as the same key self._data.update(metadata) return self._data["name"] @convert_from("entrypoints", name="entry-points") def entry_points(self, value: dict[str, dict[str, str]]) -> dict[str, dict[str, str]]: return value @convert_from("sdist") def includes(self, value: dict[str, list[str]]) -> None: self.settings.setdefault("build", {}).update( {"excludes": value.get("exclude"), "includes": value.get("include")} ) raise Unset() def convert(project: Project | None, filename: PathLike, options: Namespace | None) -> tuple[Mapping, Mapping]: with open(filename, "rb") as fp, cd(os.path.dirname(os.path.abspath(filename))): converter = FlitMetaConverter(tomllib.load(fp)["tool"]["flit"], project.core.ui if project else None) return converter.convert() def export(project: Project, candidates: list, options: Namespace | None) -> None: raise NotImplementedError() pdm-2.23.1/src/pdm/formats/pipfile.py000066400000000000000000000052261477560627500174330ustar00rootroot00000000000000from __future__ import annotations import functools import operator import os from typing import TYPE_CHECKING, Any from packaging.markers import default_environment from pdm.compat import tomllib from pdm.formats.base import make_array from pdm.models.markers import Marker, get_marker from pdm.models.requirements import FileRequirement, Requirement if TYPE_CHECKING: from argparse import Namespace from os import PathLike from pdm._types import RequirementDict from pdm.models.backends import BuildBackend from pdm.project import Project MARKER_KEYS = list(default_environment().keys()) def convert_pipfile_requirement(name: str, req: RequirementDict, backend: BuildBackend) -> str: if isinstance(req, dict): markers: list[Marker] = [] if "markers" in req: markers.append(get_marker(req["markers"])) # type: ignore[arg-type] for key in MARKER_KEYS: if key in req: marker = get_marker(f"{key}{req[key]}") markers.append(marker) del req[key] if markers: marker = functools.reduce(operator.and_, markers) req["marker"] = str(marker).replace('"', "'") r = Requirement.from_req_dict(name, req) if isinstance(r, FileRequirement): r.relocate(backend) return r.as_line() def check_fingerprint(project: Project, filename: PathLike) -> bool: return os.path.basename(filename) == "Pipfile" def convert(project: Project, filename: PathLike, options: Namespace | None) -> tuple[dict[str, Any], dict[str, Any]]: with open(filename, "rb") as fp: data = tomllib.load(fp) result = {} settings: dict[str, Any] = {} backend = project.backend if "pipenv" in data and "allow_prereleases" in data["pipenv"]: settings.setdefault("resolution", {})["allow-prereleases"] = data["pipenv"]["allow_prereleases"] if "requires" in data: python_version = data["requires"].get("python_full_version") or data["requires"].get("python_version") result["requires-python"] = f">={python_version}" if "source" in data: settings["source"] = data["source"] result["dependencies"] = make_array( # type: ignore[assignment] [convert_pipfile_requirement(k, req, backend) for k, req in data.get("packages", {}).items()], True, ) settings["dev-dependencies"] = { "dev": make_array( [convert_pipfile_requirement(k, req, backend) for k, req in data.get("dev-packages", {}).items()], True, ) } return result, settings def export(project: Project, candidates: list, options: Any) -> None: raise NotImplementedError() pdm-2.23.1/src/pdm/formats/poetry.py000066400000000000000000000215211477560627500173210ustar00rootroot00000000000000from __future__ import annotations import functools import operator import os import re from pathlib import Path from typing import TYPE_CHECKING, Any, Iterable, Mapping, cast from pdm.compat import tomllib from pdm.formats.base import ( MetaConverter, Unset, array_of_inline_tables, convert_from, make_array, make_inline_table, ) from pdm.models.markers import Marker, get_marker from pdm.models.requirements import FileRequirement, Requirement from pdm.models.specifiers import PySpecSet from pdm.utils import cd if TYPE_CHECKING: from argparse import Namespace from pdm._types import RequirementDict from pdm.project import Project def check_fingerprint(project: Project | None, filename: Path | str) -> bool: if Path(filename).name != "pyproject.toml": return False with open(filename, "rb") as fp: try: data = tomllib.load(fp) except tomllib.TOMLDecodeError: return False return "tool" in data and "poetry" in data["tool"] VERSION_RE = re.compile(r"([^\d\s]*)\s*(\d.*?)\s*(?=,|$)") def _convert_specifier(version: str) -> str: parts = [] for op, ver in VERSION_RE.findall(str(version)): if op == "~": op += "=" elif op == "^": major, *vparts = ver.split(".") next_major = ".".join([str(int(major) + 1)] + ["0"] * len(vparts)) parts.append(f">={ver},<{next_major}") continue elif not op: op = "==" parts.append(f"{op}{ver}") return ",".join(parts) def _convert_python(python: str) -> PySpecSet: if not python: return PySpecSet() parts = [PySpecSet(_convert_specifier(s)) for s in python.split("||")] return functools.reduce(operator.or_, parts) def _convert_req(name: str, req_dict: RequirementDict | list[RequirementDict]) -> Iterable[str]: from pdm.models.backends import DEFAULT_BACKEND backend = DEFAULT_BACKEND(Path.cwd()) def fix_req_path(req: Requirement) -> Requirement: if isinstance(req, FileRequirement): req.relocate(backend) return req if isinstance(req_dict, list): for req in req_dict: yield from _convert_req(name, req) elif isinstance(req_dict, str): pdm_req = fix_req_path(Requirement.from_req_dict(name, _convert_specifier(req_dict))) yield pdm_req.as_line() else: assert isinstance(req_dict, dict) req_dict = dict(req_dict) req_dict.pop("optional", None) # Ignore the 'optional' key if "version" in req_dict: req_dict["version"] = _convert_specifier(str(req_dict["version"])) markers: list[Marker] = [] if "markers" in req_dict: markers.append(get_marker(req_dict.pop("markers"))) # type: ignore[arg-type] if "python" in req_dict: markers.append(get_marker(_convert_python(str(req_dict.pop("python"))).as_marker_string())) if markers: req_dict["marker"] = str(functools.reduce(operator.and_, markers)).replace('"', "'") if "rev" in req_dict or "branch" in req_dict or "tag" in req_dict: req_dict["ref"] = req_dict.pop( "rev", req_dict.pop("tag", req_dict.pop("branch", None)), # type: ignore[arg-type] ) pdm_req = fix_req_path(Requirement.from_req_dict(name, req_dict)) yield pdm_req.as_line() # See https://github.com/python-poetry/poetry-core/pull/521#issuecomment-1327689551 # for reasoning why email.utils.parseaddr is not used here. NAME_EMAIL_RE = re.compile( r"^(?P[- .,\w'’\"():&]+)(?: <(?P.+?)>)?$", # noqa: RUF001 re.UNICODE, ) def parse_name_email(name_email: list[str]) -> list[str]: return array_of_inline_tables( [ { k: v for k, v in NAME_EMAIL_RE.match(item).groupdict().items() # type: ignore[union-attr] if v is not None } for item in name_email ] ) class PoetryMetaConverter(MetaConverter): @convert_from("authors") def authors(self, value: list[str]) -> list[str]: return parse_name_email(value) @convert_from("maintainers") def maintainers(self, value: list[str]) -> list[str]: return parse_name_email(value) @convert_from("license") def license(self, value: str) -> dict[str, str]: return make_inline_table({"text": value}) @convert_from(name="requires-python") def requires_python(self, source: dict[str, Any]) -> str: python = source.get("dependencies", {}).pop("python", None) return str(_convert_python(python)) @convert_from() def urls(self, source: dict[str, Any]) -> dict[str, str]: rv = source.pop("urls", {}) if "homepage" in source: rv["homepage"] = source.pop("homepage") if "repository" in source: rv["repository"] = source.pop("repository") if "documentation" in source: rv["documentation"] = source.pop("documentation") return rv @convert_from("plugins", name="entry-points") def entry_points(self, value: dict[str, dict[str, str]]) -> dict[str, dict[str, str]]: return value @convert_from() def dependencies(self, source: dict[str, Any]) -> list[str]: rv = [] value, extras = dict(source["dependencies"]), source.pop("extras", {}) for key, req_dict in value.items(): optional = getattr(req_dict, "items", None) and req_dict.pop("optional", False) for req in _convert_req(key, req_dict): if optional: extra = next((k for k, v in extras.items() if key in v), None) if extra: self._data.setdefault("optional-dependencies", {}).setdefault(extra, []).append(req) else: rv.append(req) del source["dependencies"] return make_array(rv, True) @convert_from("dev-dependencies") def dev_dependencies(self, value: dict) -> None: self.settings.setdefault("dev-dependencies", {})["dev"] = make_array( [r for key, req in value.items() for r in _convert_req(key, req)], True ) raise Unset() @convert_from("group") def group_dependencies(self, value: dict[str, dict[str, Any]]) -> None: for name, group in value.items(): self.settings.setdefault("dev-dependencies", {})[name] = make_array( [r for key, req in group.get("dependencies", {}).items() for r in _convert_req(key, req)], True ) raise Unset() @convert_from("package-mode") def package_mode(self, value: bool) -> None: self.settings["distribution"] = value raise Unset() @convert_from() def includes(self, source: dict[str, list[str] | str]) -> list[str]: includes: list[str] = [] source_includes: list[str] = [] for item in source.pop("packages", []): assert isinstance(item, dict) include = item["include"] if item.get("from"): include = Path(str(item.get("from")), include).as_posix() includes.append(include) for item in source.pop("include", []): if not isinstance(item, dict): includes.append(item) else: dest = source_includes if "sdist" in item.get("format", "") else includes dest.append(item["path"]) if includes: self.settings.setdefault("build", {})["includes"] = includes raise Unset() @convert_from("exclude") def excludes(self, value: list[str]) -> None: self.settings.setdefault("build", {})["excludes"] = value raise Unset() @convert_from("build") def build(self, value: str | dict) -> None: result = {} if isinstance(value, dict) and "generate-setup-file" in value: result["run-setuptools"] = cast(bool, value["generate-setup-file"]) self.settings.setdefault("build", {}).update(result) raise Unset() @convert_from("source") def sources(self, value: list[dict[str, Any]]) -> None: self.settings["source"] = [ { "name": item.get("name", ""), "url": item.get("url", ""), "verify_ssl": item.get("url", "").startswith("https"), } for item in value ] raise Unset() def convert( project: Project | None, filename: str | Path, options: Namespace | None, ) -> tuple[Mapping[str, Any], Mapping[str, Any]]: with open(filename, "rb") as fp, cd(os.path.dirname(os.path.abspath(filename))): converter = PoetryMetaConverter(tomllib.load(fp)["tool"]["poetry"], project.core.ui if project else None) return converter.convert() def export(project: Project, candidates: list, options: Any) -> None: raise NotImplementedError() pdm-2.23.1/src/pdm/formats/requirements.py000066400000000000000000000225151477560627500205260ustar00rootroot00000000000000from __future__ import annotations import argparse import hashlib import shlex import urllib.parse from pathlib import Path from typing import TYPE_CHECKING, Any, Iterable, Mapping, cast from pdm.environments import BareEnvironment from pdm.exceptions import PdmException, PdmUsageError from pdm.formats.base import make_array from pdm.models.requirements import FileRequirement, Requirement, parse_requirement if TYPE_CHECKING: from argparse import Namespace from os import PathLike from pdm.models.candidates import Candidate from pdm.models.session import PDMPyPIClient from pdm.project import Project class RequirementParser: """Reference: https://pip.pypa.io/en/stable/reference/requirements-file-format/ """ # TODO: support no_binary, only_binary, prefer_binary, pre and no_index def __init__(self, session: PDMPyPIClient) -> None: self.requirements: list[Requirement] = [] self.index_url: str | None = None self.extra_index_urls: list[str] = [] self.no_index: bool = False self.find_links: list[str] = [] self.trusted_hosts: list[str] = [] parser = argparse.ArgumentParser() parser.add_argument("--index-url", "-i") parser.add_argument("--no-index", action="store_true") parser.add_argument("--extra-index-url") parser.add_argument("--find-links", "-f") parser.add_argument("--trusted-host") parser.add_argument("-e", "--editable", nargs="+") parser.add_argument("-r", "--requirement") self._parser = parser self._session = session def _clean_line(self, line: str) -> str: """Strip the surrounding whitespaces and comment from the line""" line = line.strip() if line.startswith("#"): return "" return line.split(" #", 1)[0].strip() def _parse_line(self, filename: str, line: str) -> None: if not line.startswith("-"): # Starts with a requirement, just ignore all per-requirement options req_string = line.split(" -", 1)[0].strip() req = parse_requirement(req_string) if not req.name: assert isinstance(req, FileRequirement) req.name = req.guess_name() self.requirements.append(req) return args, _ = self._parser.parse_known_args(shlex.split(line)) if args.index_url: self.index_url = args.index_url if args.no_index: self.no_index = args.no_index if args.extra_index_url: self.extra_index_urls.append(args.extra_index_url) if args.find_links: self.find_links.append(args.find_links) if args.trusted_host: self.trusted_hosts.append(args.trusted_host) if args.editable: self.requirements.append(parse_requirement(" ".join(args.editable), True)) if args.requirement: referenced_requirements = Path(filename).parent.joinpath(args.requirement).as_posix() self.parse_file(referenced_requirements) def parse_lines(self, lines: Iterable[str], filename: str = "") -> None: this_line = "" for line in filter(None, map(self._clean_line, lines)): if line.endswith("\\"): this_line += line[:-1].rstrip() + " " continue this_line += line self._parse_line(filename, this_line) this_line = "" if this_line: self._parse_line(filename, this_line) def parse_file(self, filename_or_url: str) -> None: parsed = urllib.parse.urlparse(filename_or_url) if parsed.scheme in ("http", "https", "file"): resp = self._session.get(filename_or_url) if resp.is_error: # pragma: no cover raise PdmException( f"Failed to fetch {filename_or_url}: ({resp.status_code} - {resp.reason_phrase}) {resp.text}" ) return self.parse_lines(resp.text.splitlines(), filename_or_url) with open(filename_or_url, encoding="utf-8") as f: self.parse_lines(f, filename_or_url) def check_fingerprint(project: Project, filename: PathLike) -> bool: from pdm.compat import tomllib with open(filename, "rb") as fp: try: tomllib.load(fp) except ValueError: # the file should be a requirements.txt # if it's not a TOML document nor py script. return Path(filename).suffix not in (".py", ".cfg") else: return False def _is_url_trusted(url: str, trusted_hosts: list[str]) -> bool: parsed = urllib.parse.urlparse(url) netloc, host = parsed.netloc, parsed.hostname for trusted in trusted_hosts: if trusted in (host, netloc): return True return False def convert_url_to_source(url: str, name: str | None, trusted_hosts: list[str], type: str = "index") -> dict[str, Any]: if not name: name = hashlib.sha1(url.encode("utf-8")).hexdigest()[:6] source = { "name": name, "url": url, "verify_ssl": not _is_url_trusted(url, trusted_hosts), } if type != "index": source["type"] = type return source def convert(project: Project, filename: PathLike, options: Namespace) -> tuple[Mapping[str, Any], Mapping[str, Any]]: env = BareEnvironment(project) parser = RequirementParser(env.session) parser.parse_file(str(filename)) backend = project.backend deps = make_array([], True) dev_deps = make_array([], True) for req in parser.requirements: if req.is_file_or_url: req.relocate(backend) # type: ignore[attr-defined] if req.editable or options.dev: dev_deps.append(req.as_line()) else: deps.append(req.as_line()) data: dict[str, Any] = {} settings: dict[str, Any] = {} if dev_deps: dev_group = options.group if options.group and options.dev else "dev" settings["dev-dependencies"] = {dev_group: dev_deps} if options.group and deps: data["optional-dependencies"] = {options.group: deps} else: data["dependencies"] = deps sources: list[dict[str, Any]] = [] if parser.index_url and not parser.no_index: sources.append(convert_url_to_source(parser.index_url, "pypi", parser.trusted_hosts)) if not parser.no_index: for url in parser.extra_index_urls: sources.append(convert_url_to_source(url, None, parser.trusted_hosts)) if parser.find_links: first, *find_links = parser.find_links sources.append( convert_url_to_source( first, "pypi" if parser.no_index else None, parser.trusted_hosts, "find_links", ) ) for url in find_links: sources.append(convert_url_to_source(url, None, parser.trusted_hosts, "find_links")) if sources: settings["source"] = sources return data, settings def export( project: Project, candidates: list[Candidate] | list[Requirement], options: Namespace, ) -> str: from pdm.models.candidates import Candidate lines = ["# This file is @generated by PDM.\n# Please do not edit it manually.\n\n"] collected_req: set[str] = set() for candidate in sorted(candidates, key=lambda x: x.identify()): # type: ignore[attr-defined] if isinstance(candidate, Candidate): req = candidate.req.as_pinned_version(candidate.version) if options.hashes and not candidate.hashes: raise PdmUsageError(f"Hash is not available for '{req}', please export with `--no-hashes` option.") else: assert isinstance(candidate, Requirement) req = candidate line = project.backend.expand_line(req.as_line(), options.expandvars) if line in collected_req: continue lines.append(project.backend.expand_line(req.as_line(), options.expandvars)) collected_req.add(line) if options.hashes and getattr(candidate, "hashes", None): for item in sorted({row["hash"] for row in candidate.hashes}): # type: ignore[attr-defined] lines.append(f" \\\n --hash={item}") lines.append("\n") if (options.self or options.editable_self) and not project.is_distribution: raise PdmUsageError("Cannot export the project itself in a non-library project.") if options.hashes and (options.self or options.editable_self): raise PdmUsageError("Hash is not available for `--self/--editable-self`. Please export with `--no-hashes`.") if options.self: lines.append(". # this package\n") elif options.editable_self: lines.append("-e . # this package\n") for source in project.get_sources(expand_env=options.expandvars, include_stored=False): url = cast(str, source.url) source_type = source.type or "index" if source_type == "index": prefix = "--index-url" if source.name == "pypi" else "--extra-index-url" elif source_type == "find_links": prefix = "--find-links" else: raise ValueError(f"Unknown source type: {source_type}") lines.append(f"{prefix} {url}\n") if source.verify_ssl is False: host = urllib.parse.urlparse(url).hostname lines.append(f"--trusted-host {host}\n") return "".join(lines) pdm-2.23.1/src/pdm/formats/setup_py.py000066400000000000000000000046261477560627500176560ustar00rootroot00000000000000from __future__ import annotations import os from pathlib import Path from typing import TYPE_CHECKING, Any, Mapping from pdm.formats.base import array_of_inline_tables, make_array, make_inline_table if TYPE_CHECKING: from pdm.project import Project def check_fingerprint(project: Project, filename: Path) -> bool: return os.path.basename(filename) in ("setup.py", "setup.cfg") def convert(project: Project, filename: Path, options: Any | None) -> tuple[Mapping[str, Any], Mapping[str, Any]]: from pdm.models.in_process import parse_setup_py parsed = parse_setup_py(str(project.environment.interpreter.executable), os.path.dirname(filename)) metadata: dict[str, Any] = {} settings: dict[str, Any] = {} for name in [ "name", "version", "description", "keywords", "urls", "readme", ]: if name in parsed: metadata[name] = parsed[name] if "authors" in parsed: metadata["authors"] = array_of_inline_tables(parsed["authors"]) if "maintainers" in parsed: metadata["maintainers"] = array_of_inline_tables(parsed["maintainers"]) if "classifiers" in parsed: metadata["classifiers"] = make_array(sorted(parsed["classifiers"]), True) if "python_requires" in parsed: metadata["requires-python"] = parsed["python_requires"] if "install_requires" in parsed: metadata["dependencies"] = make_array(sorted(parsed["install_requires"]), True) if "extras_require" in parsed: metadata["optional-dependencies"] = { k: make_array(sorted(v), True) for k, v in parsed["extras_require"].items() } if "license" in parsed: metadata["license"] = make_inline_table({"text": parsed["license"]}) if "package_dir" in parsed: settings["package-dir"] = parsed["package_dir"] entry_points = parsed.get("entry_points", {}) if "console_scripts" in entry_points: metadata["scripts"] = entry_points.pop("console_scripts") if "gui_scripts" in entry_points: metadata["gui-scripts"] = entry_points.pop("gui_scripts") if entry_points: metadata["entry-points"] = entry_points # reset the environment as `requires-python` may change project.environment = None # type: ignore[assignment] return metadata, settings def export(project: Project, candidates: list, options: Any | None) -> str: raise NotImplementedError() pdm-2.23.1/src/pdm/formats/uv.py000066400000000000000000000241071477560627500164340ustar00rootroot00000000000000from __future__ import annotations import tempfile from collections.abc import Iterator from contextlib import ExitStack, contextmanager from dataclasses import dataclass, field from functools import cached_property from pathlib import Path from typing import Any, cast import tomlkit from pdm.models.candidates import Candidate from pdm.models.markers import Marker, get_marker from pdm.models.repositories import LockedRepository, Package from pdm.models.requirements import FileRequirement, Requirement, VcsRequirement, parse_requirement, strip_extras from pdm.project.core import Project from pdm.utils import get_requirement_from_override, normalize_name @dataclass class _UvFileBuilder: project: Project requires_python: str requirements: list[Requirement] locked_repository: LockedRepository stack: ExitStack = field(default_factory=ExitStack, init=False) @cached_property def default_source(self) -> str: return cast(str, self.project.sources[0].url) def __post_init__(self) -> None: self._enter_path(self.project.root / "uv.lock") def build_pyproject_toml(self) -> Path: data = self.project.pyproject._data.unwrap() uv_overrides = [] for override_key, override_value in ( data.get("tool", {}).get("pdm", {}).get("resolution", {}).get("overrides", {}).items() ): uv_overrides.append(f"{get_requirement_from_override(override_key, override_value)}") if uv_overrides: data.setdefault("tool", {}).setdefault("uv", {}).setdefault("override-dependencies", []).extend( uv_overrides ) data.setdefault("project", {})["requires-python"] = self.requires_python data.pop("dependency-groups", None) data.setdefault("project", {}).pop("optional-dependencies", None) sources = {} collected_deps: dict[str, list[str]] = {} for dep in self.requirements: if isinstance(dep, FileRequirement): entry = self._get_name(dep) sources[entry] = self._build_source(dep) else: entry = dep.as_line() for group in dep.groups: collected = collected_deps.setdefault(group, []) if entry not in collected: collected.append(entry) for group, deps in collected_deps.items(): if group == "default": data.setdefault("project", {})["dependencies"] = deps else: data.setdefault("project", {}).setdefault("optional-dependencies", {})[group] = deps if sources: data.setdefault("tool", {}).setdefault("uv", {}).setdefault("sources", {}).update(sources) path = self._enter_path(self.project.root / "pyproject.toml") with path.open("w", newline="", encoding="utf-8") as f: tomlkit.dump(data, f) return path def _enter_path(self, path: Path) -> Path: if path.exists(): name = tempfile.mktemp(dir=path.parent, prefix=f"{path.name}.") backup = path.rename(name) @self.stack.callback def restore() -> None: path.unlink(True) backup.rename(path) else: self.stack.callback(path.unlink, True) return path def build_uv_lock(self, include_self: bool = False) -> Path: locked_repo = self.locked_repository packages: list[dict[str, Any]] = [] for key in locked_repo.packages: if "[" in key[0]: # skip entries with extras continue # Merge related entries with the same name and version related_packages = [ p for k, p in locked_repo.packages.items() if strip_extras(k[0])[0] == key[0] and k[1:] == key[1:] ] packages.append(self._build_lock_entry(related_packages)) if name := self.project.name: version = self.project.pyproject.metadata.get("version", "0.0.0") this_package = { "name": normalize_name(name), "version": version, "source": {"editable" if include_self else "virtual": "."}, } dependencies: list[dict[str, Any]] = [] optional_dependencies: dict[str, list[dict[str, Any]]] = {} for req in self.requirements: if (dep := self._make_dependency(None, req)) is None: continue for group in req.groups: if group == "default": target_group = dependencies else: target_group = optional_dependencies.setdefault(group, []) if dep not in target_group: target_group.append(dep) if dependencies: this_package["dependencies"] = dependencies # type: ignore[assignment] if optional_dependencies: this_package["optional-dependencies"] = optional_dependencies packages.append(this_package) data = {"version": 1, "requires-python": self.requires_python} if packages: data["package"] = packages path = self.project.root / "uv.lock" with path.open("w", newline="", encoding="utf-8") as f: tomlkit.dump(data, f) return path def _get_name(self, req: FileRequirement) -> str: if req.key: return req.key can = Candidate(req).prepare(self.project.environment) return normalize_name(can.metadata.name) def _build_source(self, req: FileRequirement) -> dict[str, Any]: result: dict[str, Any] if isinstance(req, VcsRequirement): result = {req.vcs: req.repo} if req.ref: result["rev"] = req.ref elif req.path: result = {"path": req.str_path} else: result = {"url": req.url} if req.editable: result["editable"] = True return result def _build_lock_source(self, req: Requirement) -> dict[str, Any]: if isinstance(req, VcsRequirement): return {req.vcs: f"{req.repo}?rev={req.ref}#{req.revision}"} elif isinstance(req, FileRequirement): if req.editable: return {"editable": req.str_path} else: return {"url": req.url} else: return {"registry": self.default_source} def _build_lock_entry(self, packages: list[Package]) -> dict[str, Any]: packages.sort(key=lambda x: len(x.candidate.req.extras or [])) candidate = packages[0].candidate req = candidate.req result: dict[str, Any] = { "name": candidate.name, "version": candidate.version, "source": self._build_lock_source(req), } for file_hash in candidate.hashes: filename = file_hash.get("url", file_hash.get("file", "")) is_wheel = filename.endswith(".whl") item = {"url": file_hash.get("url", filename), "hash": file_hash["hash"]} if is_wheel: result.setdefault("wheels", []).append(item) else: result["sdist"] = item optional_dependencies: dict[str, list[dict[str, Any]]] = {} for package in packages: if not package.candidate.req.extras: deps = [ self._make_dependency(package.candidate, parse_requirement(dep)) for dep in package.dependencies ] result["dependencies"] = [dep for dep in deps if dep is not None] else: deps = [ self._make_dependency(package.candidate, parse_requirement(dep)) for dep in package.dependencies if parse_requirement(dep).key != candidate.req.key ] deps = [dep for dep in deps if dep is not None] for extra in package.candidate.req.extras: # XXX: when depending on a package with extras, the extra dependencies are encoded in # the corresponding group under optional-dependencies. But in case multiple extras are requested, # the same dependencies get duplicated in those groups, but it's okay if each single extra is # never requested alone. if extra not in optional_dependencies: optional_dependencies[extra] = deps # type: ignore[assignment] if optional_dependencies: result["optional-dependencies"] = optional_dependencies return result def _make_dependency(self, parent: Candidate | None, req: Requirement) -> dict[str, Any] | None: locked_repo = self.locked_repository parent_marker = req.marker or get_marker("") if parent is not None: parent_marker &= parent.req.marker or get_marker("") matching_entries = [e for k, e in locked_repo.packages.items() if k[0] == req.key] def marker_match(marker: Marker | None) -> bool: return not (parent_marker & (marker or get_marker(""))).is_empty() if not matching_entries: return None result: dict[str, Any] = {} if len(matching_entries) == 1: candidate = matching_entries[0].candidate multiple = False else: candidate = next(e.candidate for e in matching_entries if marker_match(e.candidate.req.marker)) multiple = True result.update({"name": candidate.name}) if multiple: result.update(version=candidate.version, source=self._build_lock_source(candidate.req)) if req.extras: result["extra"] = list(req.extras) if req.marker is not None: result["marker"] = str(req.marker) return result @contextmanager def uv_file_builder( project: Project, requires_python: str, requirements: list[Requirement], locked_repository: LockedRepository ) -> Iterator[_UvFileBuilder]: builder = _UvFileBuilder(project, requires_python, requirements, locked_repository) with builder.stack: yield builder pdm-2.23.1/src/pdm/installers/000077500000000000000000000000001477560627500161315ustar00rootroot00000000000000pdm-2.23.1/src/pdm/installers/__init__.py000066400000000000000000000004321477560627500202410ustar00rootroot00000000000000from pdm.installers.base import BaseSynchronizer from pdm.installers.manager import InstallManager from pdm.installers.synchronizers import Synchronizer from pdm.installers.uv import UvSynchronizer __all__ = ["BaseSynchronizer", "InstallManager", "Synchronizer", "UvSynchronizer"] pdm-2.23.1/src/pdm/installers/base.py000066400000000000000000000225731477560627500174260ustar00rootroot00000000000000from __future__ import annotations import dataclasses from functools import cached_property from itertools import chain from typing import Collection, Iterable from pdm import termui from pdm.compat import Distribution from pdm.environments import BaseEnvironment from pdm.exceptions import BuildError from pdm.installers.manager import InstallManager from pdm.models.candidates import Candidate from pdm.models.repositories import Package from pdm.models.requirements import FileRequirement, Requirement, parse_requirement, strip_extras from pdm.utils import is_editable, normalize_name def editables_candidate(environment: BaseEnvironment) -> Candidate | None: """Return a candidate for `editables` package""" with environment.get_finder() as finder: best = finder.find_best_match("editables").best return None if best is None else Candidate.from_installation_candidate(best, parse_requirement("editables")) class BaseSynchronizer: """Synchronize the working set with given installation candidates :param candidates: a dict of candidates to be installed :param environment: the environment associated with the project :param clean: clean unneeded packages :param dry_run: only prints summary but do not install or uninstall :param retry_times: retry times when installation failed :param install_self: whether to install self project :param no_editable: if True, override all editable installations, if a list, override editables with the given names :param use_install_cache: whether to use install cache :param reinstall: whether to reinstall all packages :param only_keep: If true, only keep the selected candidates :param fail_fast: If true, stop the installation on first error """ SEQUENTIAL_PACKAGES = ("pip", "setuptools", "wheel") def __init__( self, environment: BaseEnvironment, candidates: dict[str, Candidate] | None = None, clean: bool = False, dry_run: bool = False, retry_times: int = 1, install_self: bool = False, no_editable: bool | Collection[str] = False, reinstall: bool = False, only_keep: bool = False, fail_fast: bool = False, use_install_cache: bool | None = None, packages: Iterable[Package] = (), requirements: Iterable[Requirement] | None = None, ) -> None: if candidates: # pragma: no cover self.requested_candidates = candidates else: self.requested_candidates = {entry.candidate.identify(): entry.candidate for entry in packages} self.environment = environment self.clean = clean self.dry_run = dry_run self.retry_times = retry_times self.no_editable = no_editable self.install_self = install_self if use_install_cache is None: use_install_cache = bool(environment.project.config["install.cache"]) self.use_install_cache = use_install_cache self.reinstall = reinstall self.only_keep = only_keep self.parallel = environment.project.config["install.parallel"] self.fail_fast = fail_fast self.working_set = environment.get_working_set() self.ui = environment.project.core.ui self._manager: InstallManager | None = None self.packages = packages self.requirements = requirements @cached_property def self_candidate(self) -> Candidate: """Return the candidate for self project""" return self.environment.project.make_self_candidate(not self.no_editable) @cached_property def candidates(self) -> dict[str, Candidate]: """Return the candidates to be installed""" candidates = self.requested_candidates.copy() requested = { req.identify() for req in (self.requirements or chain.from_iterable(self.environment.project.all_dependencies.values())) } if isinstance(self.no_editable, Collection): keys = self.no_editable elif self.no_editable: keys = candidates.keys() else: keys = [] if self.should_install_editables(): # Install `editables` as well as required by self project editables = editables_candidate(self.environment) if editables is not None: candidates["editables"] = editables for key in keys: if key in candidates and candidates[key].req.editable: candidate = candidates[key] # Create a new candidate with editable=False req = dataclasses.replace(candidate.req, editable=False) candidates[key] = candidate.copy_with(req) for key in requested: if key in candidates: candidates[key].requested = True return candidates def should_install_editables(self) -> bool: """Return whether to add editables""" if not self.install_self or "editables" in self.requested_candidates: return False # As editables may be added by the backend, we need to check the metadata try: metadata = self.self_candidate.prepare(self.environment).metadata except BuildError: return False return any(req.startswith("editables") for req in metadata.requires or []) @property def manager(self) -> InstallManager: if not self._manager: self._manager = self.get_manager(rename_pth=True) return self._manager def get_manager(self, rename_pth: bool = False) -> InstallManager: return self.environment.project.core.install_manager_class( self.environment, use_install_cache=self.use_install_cache, rename_pth=rename_pth ) @property def self_key(self) -> str | None: if not self.install_self: return None name = self.environment.project.name if name: return normalize_name(name) return name def _should_update(self, dist: Distribution, can: Candidate) -> bool: """Check if the candidate should be updated""" backend = self.environment.project.backend if self.reinstall or can.req.editable: # Always update if incoming is editable return True if is_editable(dist): # only update editable if no_editable is True return bool(self.no_editable) if not can.req.is_named: dreq = Requirement.from_dist(dist) if not isinstance(dreq, FileRequirement): return True url = dreq.get_full_url() if dreq.is_local_dir: # We don't know whether a local dir has been changed, always update return True assert can.link is not None return url != backend.expand_line(can.link.url_without_fragment) specifier = can.req.as_pinned_version(can.version).specifier return not specifier.contains(dist.version, prereleases=True) def compare_with_working_set(self) -> tuple[list[str], list[str], list[str]]: """Compares the candidates and return (to_add, to_update, to_remove)""" working_set = self.working_set candidates = self.candidates.copy() to_update: set[str] = set() to_remove: set[str] = set() to_add: set[str] = set() locked_repository = self.environment.project.get_locked_repository() all_candidate_keys = list(locked_repository.all_candidates) for key, dist in working_set.items(): if key == self.self_key and self.install_self: continue if key in candidates: can = candidates.pop(key) if self._should_update(dist, can): if working_set.is_owned(key): to_update.add(key) else: to_add.add(key) elif ( (self.only_keep or (self.clean and key not in all_candidate_keys)) and key not in self.SEQUENTIAL_PACKAGES and working_set.is_owned(key) ): # Remove package only if it is not required by any group # Packages for packaging will never be removed to_remove.add(key) to_add.update( strip_extras(name)[0] for name, _ in candidates.items() if name != self.self_key and strip_extras(name)[0] not in working_set ) return (sorted(to_add), sorted(to_update), sorted(to_remove)) def synchronize(self) -> None: """Synchronize the working set with pinned candidates.""" to_add, to_update, to_remove = self.compare_with_working_set() manager = self.get_manager() for key in to_add: can = self.candidates[key] termui.logger.info("Installing %s@%s...", key, can.version) manager.install(can) for key in to_update: can = self.candidates[key] dist = self.working_set[strip_extras(key)[0]] dist_version = dist.version termui.logger.info("Updating %s@%s -> %s...", key, dist_version, can.version) manager.uninstall(dist) manager.install(can) for key in to_remove: dist = self.working_set[key] termui.logger.info("Removing %s@%s...", key, dist.version) manager.uninstall(dist) termui.logger.info("Synchronization complete.") pdm-2.23.1/src/pdm/installers/core.py000066400000000000000000000026341477560627500174400ustar00rootroot00000000000000from __future__ import annotations from typing import Iterable from pdm.environments import BaseEnvironment from pdm.models.requirements import Requirement from pdm.resolver.reporters import LockReporter def install_requirements( reqs: Iterable[Requirement], environment: BaseEnvironment, clean: bool = False, use_install_cache: bool = False, allow_uv: bool = True, ) -> None: # pragma: no cover """Resolve and install the given requirements into the environment.""" reqs = [req for req in reqs if not req.marker or req.marker.matches(environment.spec)] reporter = LockReporter() project = environment.project backend = project.backend for req in reqs: if req.is_file_or_url: req.relocate(backend) # type: ignore[attr-defined] resolver = project.get_resolver(allow_uv=allow_uv)( environment=environment, requirements=reqs, update_strategy="all", strategies=project.lockfile.default_strategies, target=environment.spec, tracked_names=(), keep_self=True, reporter=reporter, ) resolved = resolver.resolve().packages syncer = environment.project.get_synchronizer(quiet=True, allow_uv=allow_uv)( environment, clean=clean, retry_times=0, use_install_cache=use_install_cache, packages=resolved, requirements=reqs, ) syncer.synchronize() pdm-2.23.1/src/pdm/installers/installers.py000066400000000000000000000173001477560627500206640ustar00rootroot00000000000000from __future__ import annotations import json import os import stat from functools import cached_property from pathlib import Path from typing import TYPE_CHECKING, Iterator from installer import install as _install from installer._core import _process_WHEEL_file from installer.destinations import SchemeDictionaryDestination, WheelDestination from installer.exceptions import InvalidWheelSource from installer.records import RecordEntry from installer.sources import WheelContentElement, WheelSource from installer.sources import WheelFile as _WheelFile from pdm.models.cached_package import CachedPackage if TYPE_CHECKING: from typing import Any, BinaryIO, Iterable, Literal from installer.destinations import Scheme from installer.sources import WheelContentElement from pdm.environments import BaseEnvironment LinkMethod = Literal["symlink", "hardlink", "copy"] def _get_dist_name(wheel_path: str) -> str: from packaging.utils import parse_wheel_filename return parse_wheel_filename(os.path.basename(wheel_path))[0] class WheelFile(_WheelFile): @cached_property def dist_info_dir(self) -> str: namelist = self._zipfile.namelist() try: return next(name.split("/")[0] for name in namelist if name.split("/")[0].endswith(".dist-info")) except StopIteration: # pragma: no cover canonical_name = super().dist_info_dir raise InvalidWheelSource(f"The wheel doesn't contain metadata {canonical_name!r}") from None class PackageWheelSource(WheelSource): def __init__(self, package: CachedPackage) -> None: self.package = package distribution, version = package.path.name.split("-")[:2] super().__init__(distribution, version) @cached_property def dist_info_dir(self) -> str: return self.package.dist_info.name @property def dist_info_filenames(self) -> list[str]: return os.listdir(self.package.dist_info) def read_dist_info(self, filename: str) -> str: return self.package.dist_info.joinpath(filename).read_text("utf-8") def iter_files(self) -> Iterable[Path]: for root, _, files in os.walk(self.package.path): for file in files: if Path(root) == self.package.path and file in CachedPackage.cache_files: continue yield Path(root, file) def get_contents(self) -> Iterator[WheelContentElement]: from installer.records import parse_record_file record_lines = self.read_dist_info("RECORD").splitlines() records = parse_record_file(record_lines) record_mapping = {record[0]: record for record in records} for item in self.iter_files(): fn = item.relative_to(self.package.path).as_posix() # Pop record with empty default, because validation is handled by `validate_record` record = record_mapping.pop(fn, (fn, "", "")) # Borrowed from: # https://github.com/pypa/pip/blob/0f21fb92/src/pip/_internal/utils/unpacking.py#L96-L100 mode = item.stat().st_mode is_executable = bool(mode and stat.S_ISREG(mode) and mode & 0o111) with item.open("rb") as stream: yield record, stream, is_executable class InstallDestination(SchemeDictionaryDestination): def __init__( self, *args: Any, link_method: LinkMethod = "copy", rename_pth: bool = False, **kwargs: Any, ) -> None: super().__init__(*args, **kwargs) self.link_method = link_method self.rename_pth = rename_pth def write_to_fs(self, scheme: Scheme, path: str, stream: BinaryIO, is_executable: bool) -> RecordEntry: from installer.records import Hash from installer.utils import copyfileobj_with_hashing, make_file_executable target_path = os.path.join(self.scheme_dict[scheme], path) if os.path.exists(target_path): os.unlink(target_path) os.makedirs(os.path.dirname(target_path), exist_ok=True) if self.rename_pth and target_path.endswith(".pth") and "/" not in path: # Postpone the creation of pth files since it may cause race condition # when multiple packages are installed at the same time. target_path += ".pdmtmp" if self.link_method == "copy" or not hasattr(stream, "name"): with open(target_path, "wb") as f: hash_, size = copyfileobj_with_hashing(stream, f, self.hash_algorithm) else: src_path = stream.name # create links, we don't need the stream anymore stream.close() if self.link_method == "symlink": os.symlink(src_path, target_path) else: # hardlink os.link(src_path, target_path) hash_ = "" size = os.path.getsize(target_path) if is_executable: make_file_executable(target_path) return RecordEntry(path, Hash(self.hash_algorithm, hash_), size) def _get_link_method(cache_method: str) -> LinkMethod: from pdm import utils if "symlink" in cache_method and utils.fs_supports_link_method("symlink"): return "symlink" if "link" in cache_method and utils.fs_supports_link_method("link"): return "hardlink" return "copy" def install_wheel( wheel: Path, environment: BaseEnvironment, direct_url: dict[str, Any] | None = None, install_links: bool = False, rename_pth: bool = False, requested: bool = False, ) -> str: """Only create .pth files referring to the cached package. If the cache doesn't exist, create one. """ interpreter = str(environment.interpreter.executable) script_kind = environment.script_kind # the cache_method can be any of "symlink", "hardlink", "copy" and "pth" cache_method: str = environment.project.config["install.cache_method"] dist_name = wheel.name.split("-")[0] link_method: LinkMethod | None if not install_links or dist_name == "editables": link_method = "copy" else: link_method = _get_link_method(cache_method) additional_metadata: dict[str, bytes] = {"INSTALLER": b"pdm"} if direct_url is not None: additional_metadata["direct_url.json"] = json.dumps(direct_url, indent=2).encode() if requested: additional_metadata["REQUESTED"] = b"" destination = InstallDestination( scheme_dict=environment.get_paths(dist_name), interpreter=interpreter, script_kind=script_kind, link_method=link_method, rename_pth=rename_pth, ) if install_links: package = environment.project.package_cache.cache_wheel(wheel) source = PackageWheelSource(package) if link_method == "symlink": # Track usage when symlink is used additional_metadata["REFER_TO"] = package.path.as_posix().encode() dist_info_dir = install(source, destination=destination, additional_metadata=additional_metadata) if link_method == "symlink": package.add_referrer(dist_info_dir) else: with WheelFile.open(wheel) as source: dist_info_dir = install(source, destination=destination, additional_metadata=additional_metadata) return dist_info_dir def install( source: WheelSource, destination: WheelDestination, additional_metadata: dict[str, bytes] | None = None ) -> str: """A lower level installation method that is copied from installer but is controlled by extra parameters. Return the .dist-info path """ _install(source, destination, additional_metadata=additional_metadata or {}) root_scheme = _process_WHEEL_file(source) return os.path.join(destination.scheme_dict[root_scheme], source.dist_info_dir) pdm-2.23.1/src/pdm/installers/manager.py000066400000000000000000000060011477560627500201120ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING from pdm import termui from pdm.compat import Distribution from pdm.exceptions import UninstallError from pdm.installers.installers import install_wheel from pdm.installers.uninstallers import BaseRemovePaths, StashedRemovePaths if TYPE_CHECKING: from pdm.environments import BaseEnvironment from pdm.models.candidates import Candidate class InstallManager: """The manager that performs the installation and uninstallation actions.""" # The packages below are needed to load paths and thus should not be cached. NO_CACHE_PACKAGES = ("editables",) def __init__( self, environment: BaseEnvironment, *, use_install_cache: bool = False, rename_pth: bool = False ) -> None: self.environment = environment self.use_install_cache = use_install_cache self.rename_pth = rename_pth def install(self, candidate: Candidate) -> Distribution: """Install a candidate into the environment, return the distribution""" prepared = candidate.prepare(self.environment) dist_info = install_wheel( prepared.build(), self.environment, direct_url=prepared.direct_url(), install_links=self.use_install_cache and not candidate.req.editable, rename_pth=self.rename_pth, requested=candidate.requested, ) return Distribution.at(dist_info) def get_paths_to_remove(self, dist: Distribution) -> BaseRemovePaths: """Get the path collection to be removed from the disk""" return StashedRemovePaths.from_dist(dist, environment=self.environment) def uninstall(self, dist: Distribution) -> None: """Perform the uninstallation for a given distribution""" remove_path = self.get_paths_to_remove(dist) dist_name = dist.metadata.get("Name") termui.logger.info("Removing distribution %s", dist_name) try: remove_path.remove() remove_path.commit() except OSError as e: termui.logger.warn("Error occurred during uninstallation, roll back the changes now.") remove_path.rollback() raise UninstallError(e) from e def overwrite(self, dist: Distribution, candidate: Candidate) -> None: """An in-place update to overwrite the distribution with a new candidate""" paths_to_remove = self.get_paths_to_remove(dist) termui.logger.info("Overwriting distribution %s", dist.metadata.get("Name")) installed = self.install(candidate) installed_paths = self.get_paths_to_remove(installed) # Remove the paths that are in the new distribution paths_to_remove.difference_update(installed_paths) try: paths_to_remove.remove() paths_to_remove.commit() except OSError as e: termui.logger.warn("Error occurred during overwriting, roll back the changes now.") paths_to_remove.rollback() raise UninstallError(e) from e pdm-2.23.1/src/pdm/installers/synchronizers.py000066400000000000000000000247471477560627500214410ustar00rootroot00000000000000from __future__ import annotations import functools import traceback from concurrent.futures import Future, ThreadPoolExecutor from types import SimpleNamespace from typing import TYPE_CHECKING from pdm import termui from pdm.exceptions import InstallationError from pdm.installers.base import BaseSynchronizer from pdm.models.candidates import Candidate from pdm.models.reporter import CandidateReporter, InstallationStatus, RichProgressReporter from pdm.models.requirements import strip_extras if TYPE_CHECKING: from rich.progress import Progress from pdm.compat import Distribution class Synchronizer(BaseSynchronizer): def install_candidate(self, key: str, progress: Progress) -> Candidate: """Install candidate""" can = self.candidates[key] job = progress.add_task(f"Installing {can.format()}...", text="", total=None) can.prepare(self.environment, RichProgressReporter(progress, job)) try: self.manager.install(can) except Exception: progress.print(f" [error]{termui.Emoji.FAIL}[/] Install {can.format()} failed") raise else: progress.print(f" [success]{termui.Emoji.SUCC}[/] Install {can.format()} successful") finally: progress.remove_task(job) can.prepare(self.environment, CandidateReporter()) return can def update_candidate(self, key: str, progress: Progress) -> tuple[Distribution, Candidate]: """Update candidate""" can = self.candidates[key] dist = self.working_set[strip_extras(key)[0]] dist_version = dist.version job = progress.add_task( f"Updating [req]{key}[/] [warning]{dist_version}[/] -> [warning]{can.version}[/]...", text="", total=None ) can.prepare(self.environment, RichProgressReporter(progress, job)) try: self.manager.overwrite(dist, can) except Exception: progress.print( f" [error]{termui.Emoji.FAIL}[/] Update [req]{key}[/] " f"[warning]{dist_version}[/] " f"-> [warning]{can.version}[/] failed", ) raise else: progress.print( f" [success]{termui.Emoji.SUCC}[/] Update [req]{key}[/] " f"[warning]{dist_version}[/] " f"-> [warning]{can.version}[/] successful", ) finally: progress.remove_task(job) can.prepare(self.environment, CandidateReporter()) return dist, can def remove_distribution(self, key: str, progress: Progress) -> Distribution: """Remove distributions with given names.""" dist = self.working_set[key] dist_version = dist.version job = progress.add_task(f"Removing [req]{key}[/] [warning]{dist_version}[/]...", text="", total=None) try: self.manager.uninstall(dist) except Exception: progress.print( f" [error]{termui.Emoji.FAIL}[/] Remove [req]{key}[/] [warning]{dist_version}[/] failed", ) raise else: progress.print( f" [success]{termui.Emoji.SUCC}[/] Remove [req]{key}[/] [warning]{dist_version}[/] successful" ) finally: progress.remove_task(job) return dist def _show_headline(self, packages: dict[str, list[str]]) -> None: add, update, remove = packages["add"], packages["update"], packages["remove"] if not any((add, update, remove)): self.ui.echo("All packages are synced to date, nothing to do.") return results = ["[bold]Synchronizing working set with resolved packages[/]:"] results.extend( [ f"[success]{len(add)}[/] to add,", f"[warning]{len(update)}[/] to update,", f"[error]{len(remove)}[/] to remove", ] ) self.ui.echo(" ".join(results) + "\n") def _show_summary(self, packages: dict[str, list[str]]) -> None: to_add = [self.candidates[key] for key in packages["add"]] to_update = [(self.working_set[key], self.candidates[key]) for key in packages["update"]] to_remove = [self.working_set[key] for key in packages["remove"]] lines = [] if to_add: lines.append("[bold]Packages to add[/]:") for can in to_add: lines.append(f" - {can.format()}") if to_update: lines.append("[bold]Packages to update[/]:") for prev, cur in to_update: lines.append(f" - [req]{cur.name}[/] [warning]{prev.version}[/] -> [warning]{cur.version}[/]") if to_remove: lines.append("[bold]Packages to remove[/]:") for dist in to_remove: lines.append(f" - [req]{dist.metadata['Name']}[/] [warning]{dist.version}[/]") if lines: self.ui.echo("\n".join(lines)) else: self.ui.echo("All packages are synced to date, nothing to do.") def _fix_pth_files(self) -> None: """Remove the .pdmtmp suffix from the installed packages""" from pathlib import Path lib_paths = self.environment.get_paths() for scheme in ["purelib", "platlib"]: for path in list(Path(lib_paths[scheme]).iterdir()): if path.suffix == ".pdmtmp": target_path = path.with_suffix("") if target_path.exists(): target_path.unlink() path.rename(target_path) def synchronize(self) -> None: to_add, to_update, to_remove = self.compare_with_working_set() to_do = {"remove": to_remove, "update": to_update, "add": to_add} if self.dry_run: self._show_summary(to_do) return self._show_headline(to_do) handlers = { "add": self.install_candidate, "update": self.update_candidate, "remove": self.remove_distribution, } sequential_jobs = [] parallel_jobs = [] for kind in to_do: for key in to_do[kind]: if key in self.SEQUENTIAL_PACKAGES or not self.parallel: sequential_jobs.append((kind, key)) elif key in self.candidates and self.candidates[key].req.editable: # Editable packages are installed sequentially. sequential_jobs.append((kind, key)) else: parallel_jobs.append((kind, key)) state = SimpleNamespace(errors=[], parallel_failed=[], sequential_failed=[], jobs=[], mark_failed=False) def update_progress(future: Future, kind: str, key: str) -> None: error = future.exception() status.update_spinner(advance=1) # type: ignore[has-type] if error: exc_info = (type(error), error, error.__traceback__) termui.logger.exception("Error occurs %sing %s: ", kind.rstrip("e"), key, exc_info=exc_info) state.parallel_failed.append((kind, key)) state.errors.extend([f"{kind} [success]{key}[/] failed:\n", *traceback.format_exception(*exc_info)]) if self.fail_fast: for future in state.jobs: future.cancel() state.mark_failed = True # get rich progress and live handler to deal with multiple spinners with InstallationStatus(self.ui, "Synchronizing") as status: for i in range(self.retry_times + 1): status.update_spinner(completed=0, total=len(sequential_jobs) + len(parallel_jobs)) for kind, key in sequential_jobs: try: handlers[kind](key, status.progress) except Exception: termui.logger.exception("Error occurs: ") state.sequential_failed.append((kind, key)) state.errors.extend([f"{kind} [success]{key}[/] failed:\n", traceback.format_exc()]) if self.fail_fast: state.mark_failed = True break finally: status.update_spinner(advance=1) if state.mark_failed: break state.jobs.clear() if parallel_jobs: with ThreadPoolExecutor() as executor: for kind, key in parallel_jobs: future = executor.submit(handlers[kind], key, status.progress) future.add_done_callback(functools.partial(update_progress, kind=kind, key=key)) state.jobs.append(future) if ( state.mark_failed or i == self.retry_times or (not state.sequential_failed and not state.parallel_failed) ): break sequential_jobs, state.sequential_failed = state.sequential_failed, [] parallel_jobs, state.parallel_failed = state.parallel_failed, [] state.errors.clear() status.update_spinner(description=f"Retry failed jobs({i + 2}/{self.retry_times + 1})") try: if state.errors: if self.ui.verbosity < termui.Verbosity.DETAIL: status.console.print("\n[error]ERRORS[/]:") status.console.print("".join(state.errors), end="") status.update_spinner(description=f"[error]{termui.Emoji.FAIL}[/] Some package operations failed.") raise InstallationError("Some package operations failed.") if self.install_self: self_key = self.self_key assert self_key self.candidates[self_key] = self.self_candidate word = "a" if self.no_editable else "an editable" status.update_spinner(description=f"Installing the project as {word} package...") if self_key in self.working_set: self.update_candidate(self_key, status.progress) else: self.install_candidate(self_key, status.progress) status.update_spinner(description=f"{termui.Emoji.POPPER} All complete!") finally: # Now we remove the .pdmtmp suffix from the installed packages self._fix_pth_files() pdm-2.23.1/src/pdm/installers/uninstallers.py000066400000000000000000000275661477560627500212460ustar00rootroot00000000000000from __future__ import annotations import abc import glob import os import shutil from pathlib import Path from tempfile import TemporaryDirectory from typing import TYPE_CHECKING, Iterable, NewType, TypeVar, cast from pdm import termui from pdm.exceptions import UninstallError from pdm.models.cached_package import CachedPackage from pdm.utils import is_egg_link, is_path_relative_to if TYPE_CHECKING: from pdm.compat import Distribution from pdm.environments import BaseEnvironment _T = TypeVar("_T", bound="BaseRemovePaths") NormalizedPath = NewType("NormalizedPath", str) def renames(old: str, new: str) -> None: """Like os.renames(), but handles renaming across devices.""" # Implementation borrowed from os.renames(). head, tail = os.path.split(new) if head and tail and not os.path.exists(head): os.makedirs(head) shutil.move(old, new) head, tail = os.path.split(old) if head and tail: try: os.removedirs(head) except OSError: pass def compress_for_rename(paths: Iterable[NormalizedPath]) -> set[NormalizedPath]: """Returns a set containing the paths that need to be renamed. This set may include directories when the original sequence of paths included every file on disk. """ case_map = {NormalizedPath(os.path.normcase(p)): p for p in paths if os.path.exists(p)} remaining = set(case_map) unchecked = sorted({NormalizedPath(os.path.split(p)[0]) for p in case_map.values()}, key=len) wildcards: set[NormalizedPath] = set() def norm_join(*a: str) -> NormalizedPath: return NormalizedPath(os.path.normcase(os.path.join(*a))) for root in unchecked: if any(os.path.normcase(root).startswith(w) for w in wildcards): # This directory has already been handled. continue all_files: set[NormalizedPath] = set() for dirname, subdirs, files in os.walk(root): all_files.update(norm_join(root, dirname, f) for f in files) for d in subdirs: norm_path = norm_join(root, dirname, d) if os.path.islink(norm_path): all_files.add(norm_path) # If all the files we found are in our remaining set of files to # remove, then remove them from the latter set and add a wildcard # for the directory. if not (all_files - remaining): remaining.difference_update(all_files) wildcards.add(NormalizedPath(root + os.sep)) collected = set(map(case_map.__getitem__, remaining)) | wildcards shortened: set[NormalizedPath] = set() # Filter out any paths that are sub paths of another path in the path collection. for path in sorted(collected, key=len): if not any(is_path_relative_to(path, p) for p in shortened): shortened.add(path) return shortened def _script_names(script_name: str, is_gui: bool) -> Iterable[str]: yield script_name if os.name == "nt": yield script_name + ".exe" yield script_name + ".exe.manifest" if is_gui: yield script_name + "-script.pyw" else: yield script_name + "-script.py" def _cache_file_from_source(py_file: NormalizedPath) -> Iterable[NormalizedPath]: py2_cache = py_file[:-3] + ".pyc" if os.path.isfile(py2_cache): yield NormalizedPath(py2_cache) parent, base = os.path.split(py_file) cache_dir = os.path.join(parent, "__pycache__") yield from map(NormalizedPath, glob.glob(os.path.join(cache_dir, base[:-3] + ".*.pyc"))) def _get_file_root(path: str, base: str) -> str | None: try: rel_path = Path(path).relative_to(base) except ValueError: return None else: root = rel_path.parts[0] if len(rel_path.parts) > 1 else "" return os.path.normcase(os.path.join(base, root)) def _get_all_parents(path: NormalizedPath) -> Iterable[NormalizedPath]: while True: yield path parent = NormalizedPath(os.path.split(path)[0]) if parent == path: break path = parent class BaseRemovePaths(abc.ABC): """A collection of paths and/or pth entries to remove""" def __init__(self, dist: Distribution, environment: BaseEnvironment) -> None: self.dist = dist self.environment = environment self._paths: set[NormalizedPath] = set() self._pth_entries: set[str] = set() self.refer_to: str | None = None def difference_update(self, other: BaseRemovePaths) -> None: self._pth_entries.difference_update(other._pth_entries) for p in other._paths: # if other_p is a file, remove all parent dirs of it self._paths.difference_update(_get_all_parents(p)) # other_p is a symlink dir, remove all files under it self._paths.difference_update({p2 for p2 in self._paths if p2.startswith(p + os.sep)}) @abc.abstractmethod def remove(self) -> None: """Remove the files""" @abc.abstractmethod def commit(self) -> None: """Commit the removal""" @abc.abstractmethod def rollback(self) -> None: """Roll back the removal operations""" @classmethod def from_dist(cls: type[_T], dist: Distribution, environment: BaseEnvironment) -> _T: """Create an instance from the distribution""" scheme = environment.get_paths() instance = cls(dist, environment) meta_location = os.path.normcase(dist._path.absolute()) # type: ignore[attr-defined] dist_location = os.path.dirname(meta_location) if is_egg_link(dist): # pragma: no cover egg_link_path = cast("Path | None", getattr(dist, "link_file", None)) dist_name = dist.metadata.get("Name") if not egg_link_path: termui.logger.warn( "No egg link is found for editable distribution %s, do nothing.", dist_name, ) else: with egg_link_path.open("rb") as f: link_pointer = os.path.normcase(f.readline().decode().strip()) if link_pointer != dist_location: raise UninstallError( f"The link pointer in {egg_link_path} doesn't match " f"the location of {dist_name} (at {dist_location}" ) instance.add_path(str(egg_link_path)) instance.add_pth(link_pointer) elif dist.files: for file in dist.files: location = dist.locate_file(file) instance.add_path(str(location)) bare_name, ext = os.path.splitext(cast(Path, location)) if ext == ".py": # .pyc files are added by add_path() instance.add_path(bare_name + ".pyo") # installed-files.txt isn't recorded in SOURCES.txt for egg-info instance.add_path(os.path.join(meta_location, "installed-files.txt")) bin_dir = scheme["scripts"] if os.path.isdir(os.path.join(meta_location, "scripts")): # pragma: no cover for script in os.listdir(os.path.join(meta_location, "scripts")): instance.add_path(os.path.join(bin_dir, script)) if os.name == "nt": instance.add_path(os.path.join(bin_dir, script) + ".bat") # find console_scripts _scripts_to_remove: list[str] = [] for ep in dist.entry_points: if ep.group == "console_scripts": _scripts_to_remove.extend(_script_names(ep.name, False)) elif ep.group == "gui_scripts": _scripts_to_remove.extend(_script_names(ep.name, True)) for s in _scripts_to_remove: instance.add_path(os.path.join(bin_dir, s)) return instance def add_pth(self, line: str) -> None: self._pth_entries.add(line) def add_path(self, path: str) -> None: normalized_path = NormalizedPath(os.path.normcase(os.path.expanduser(os.path.abspath(path)))) self._paths.add(normalized_path) if path.endswith(".py"): self._paths.update(_cache_file_from_source(normalized_path)) elif path.replace("\\", "/").endswith(".dist-info/REFER_TO"): with open(path, "rb") as f: line = f.readline().decode().strip() if line: self.refer_to = line class StashedRemovePaths(BaseRemovePaths): """Stash the paths to temporarily location and remove them after commit""" PTH_REGISTRY = "easy-install.pth" def __init__(self, dist: Distribution, environment: BaseEnvironment) -> None: super().__init__(dist, environment) self._pth_file = os.path.join(self.environment.get_paths()["purelib"], self.PTH_REGISTRY) self._saved_pth: bytes | None = None self._stashed: list[tuple[str, str]] = [] self._tempdirs: dict[str, TemporaryDirectory] = {} def remove(self) -> None: self._remove_pth() self._stash_files() def _remove_pth(self) -> None: if not self._pth_entries: return with open(self._pth_file, "rb") as f: self._saved_pth = f.read() endline = "\r\n" if b"\r\n" in self._saved_pth else "\n" lines = self._saved_pth.decode().splitlines() for item in self._pth_entries: termui.logger.debug("Removing pth entry: %s", item) lines.remove(item) with open(self._pth_file, "wb") as f: f.write((endline.join(lines) + endline).encode("utf8")) def _stash_files(self) -> None: paths_to_rename = sorted(compress_for_rename(self._paths)) prefix = os.path.abspath(self.environment.get_paths()["prefix"]) for old_path in paths_to_rename: if not os.path.exists(old_path): continue is_dir = os.path.isdir(old_path) and not os.path.islink(old_path) termui.logger.debug("Removing %s %s", "directory" if is_dir else "file", old_path) if old_path.endswith(".pyc"): # Don't stash cache files, remove them directly os.unlink(old_path) continue root = _get_file_root(old_path, prefix) if root is None: termui.logger.debug("File path %s is not under packages root %s, skip", old_path, prefix) continue if root not in self._tempdirs: self._tempdirs[root] = TemporaryDirectory("-uninstall", "pdm-") new_root = self._tempdirs[root].name relpath = os.path.relpath(old_path, root) new_path = os.path.join(new_root, relpath) if is_dir and os.path.isdir(new_path): os.rmdir(new_path) renames(old_path, new_path) self._stashed.append((old_path, new_path)) def commit(self) -> None: for tempdir in self._tempdirs.values(): try: tempdir.cleanup() except FileNotFoundError: pass self._tempdirs.clear() self._stashed.clear() self._saved_pth = None if self.refer_to: termui.logger.info("Unlink from cached package %s", self.refer_to) CachedPackage(self.refer_to).remove_referrer(os.path.dirname(self.refer_to)) self.refer_to = None def rollback(self) -> None: if not self._stashed: termui.logger.error("Can't rollback, not uninstalled yet") return if self._saved_pth is not None: with open(self._pth_file, "wb") as f: f.write(self._saved_pth) for old_path, new_path in self._stashed: termui.logger.debug("Rollback %s\n from %s", old_path, new_path) if os.path.isfile(old_path) or os.path.islink(old_path): os.unlink(old_path) elif os.path.isdir(old_path): shutil.rmtree(old_path) renames(new_path, old_path) self.commit() pdm-2.23.1/src/pdm/installers/uv.py000066400000000000000000000066561477560627500171520ustar00rootroot00000000000000from __future__ import annotations import subprocess from typing import Any from pdm.environments.local import PythonLocalEnvironment from pdm.exceptions import PdmUsageError from pdm.installers.base import BaseSynchronizer from pdm.models.repositories import LockedRepository from pdm.termui import Verbosity class UvSynchronizer(BaseSynchronizer): def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) locked_repo = LockedRepository({}, self.environment.project.sources, self.environment) for package in self.packages: if self.no_editable and package.candidate.req.editable: package.candidate.req.editable = False locked_repo.add_package(package) self.locked_repo = locked_repo def synchronize(self) -> None: from itertools import chain from pdm.formats.uv import uv_file_builder if isinstance(self.environment, PythonLocalEnvironment): raise PdmUsageError( "uv doesn't support PEP 582 local packages, this error occurs because you set use_uv = true." ) if self.dry_run: self.environment.project.core.ui.echo("[warning]uv doesn't support dry run mode, skipping installation") return if self.requirements is not None: requirements = list(self.requirements) else: requirements = list(chain.from_iterable(self.environment.project.all_dependencies.values())) with uv_file_builder( self.environment.project, str(self.environment.python_requires), requirements, self.locked_repo ) as builder: builder.build_pyproject_toml() builder.build_uv_lock(include_self=self.install_self) cmd = self._get_sync_command() self.environment.project.core.ui.echo(f"Running uv sync command: {cmd}", verbosity=Verbosity.DETAIL) subprocess.run(cmd, check=True, cwd=self.environment.project.root) def _get_sync_command(self) -> list[str]: core = self.environment.project.core cmd = [ *core.uv_cmd, "sync", "--all-extras", "--frozen", "-p", str(self.environment.interpreter.executable), ] if core.ui.verbosity > 0: cmd.append("--verbose") if not core.state.enable_cache: cmd.append("--no-cache") if not self.clean and not self.only_keep: cmd.append("--inexact") if self.reinstall: cmd.append("--reinstall") if not self.install_self: cmd.append("--no-install-project") first_index = True for source in self.environment.project.sources: assert source.url is not None if source.type == "find_links": cmd.extend(["--find-links", source.url]) elif first_index: cmd.extend(["--index-url", source.url]) first_index = False else: cmd.extend(["--extra-index-url", source.url]) if self.use_install_cache: cmd.extend(["--link-mode", self.environment.project.config["install.cache_method"]]) return cmd class QuietUvSynchronizer(UvSynchronizer): def get_sync_command(self) -> list[str]: cmd = super()._get_sync_command() if "--verbose" in cmd: cmd.remove("--verbose") return [*cmd, "--quiet"] pdm-2.23.1/src/pdm/models/000077500000000000000000000000001477560627500152345ustar00rootroot00000000000000pdm-2.23.1/src/pdm/models/__init__.py000066400000000000000000000000001477560627500173330ustar00rootroot00000000000000pdm-2.23.1/src/pdm/models/auth.py000066400000000000000000000114371477560627500165550ustar00rootroot00000000000000from __future__ import annotations import functools import urllib.parse from unearth.auth import MaybeAuth, MultiDomainBasicAuth, get_keyring_provider from unearth.utils import commonprefix, split_auth_from_url from pdm._types import RepositoryConfig from pdm.exceptions import PdmException from pdm.termui import UI, Verbosity, is_interactive class PdmBasicAuth(MultiDomainBasicAuth): """A custom auth class that differs from Pip's implementation in the following ways: - It shows an error message when credentials are not provided or correct. """ def __init__(self, ui: UI, sources: list[RepositoryConfig]) -> None: super().__init__(prompting=is_interactive()) self.sources = sources self.ui = ui self._selected_source: RepositoryConfig | None = None def _get_new_credentials( self, original_url: str, *, allow_netrc: bool = True, allow_keyring: bool = False ) -> tuple[str | None, str | None]: user, password = super()._get_new_credentials( original_url, allow_netrc=allow_netrc, allow_keyring=allow_keyring ) if (user is None or password is None) and allow_keyring and self._selected_source: self._selected_source.populate_keyring_auth() user = user or self._selected_source.username password = password or self._selected_source.password self._selected_source = None return user, password def _get_auth_from_index_url(self, url: str) -> tuple[MaybeAuth, str | None]: if not self.sources: return None, None target = urllib.parse.urlsplit(url.rstrip("/") + "/") candidates: list[tuple[MaybeAuth, str, urllib.parse.SplitResult, RepositoryConfig]] = [] for source in self.sources: assert source.url index = source.url.rstrip("/") + "/" auth, url_no_auth = split_auth_from_url(index) parsed = urllib.parse.urlsplit(url_no_auth) if source.username: auth = (source.username, source.password) if parsed == target: return auth, index if parsed.netloc == target.netloc: candidates.append((auth, index, parsed, source)) if not candidates: return None, None auth, index, _, source = max(candidates, key=lambda x: commonprefix(x[2].path, target.path).rfind("/")) self._selected_source = source return auth, index def _prompt_for_password(self, netloc: str, username: str | None = None) -> tuple[str | None, str | None, bool]: if self.ui.verbosity < Verbosity.DETAIL: raise PdmException( f"The credentials for {netloc} are not provided. To give them via interactive shell, " "please rerun the command with `-v` option." ) return super()._prompt_for_password(netloc, username) def _should_save_password_to_keyring(self) -> bool: if get_keyring_provider() is None: self.ui.info( "The provided credentials will not be saved into the keyring.\n" "You can enable this by installing keyring:\n" " [success]pdm self add keyring[/]" ) return super()._should_save_password_to_keyring() class Keyring: def __init__(self) -> None: self.provider = get_keyring_provider() self.enabled = self.provider is not None @functools.lru_cache(maxsize=128) def get_auth_info(self, url: str, username: str | None) -> tuple[str, str] | None: """Return the password for the given url and username. The username can be None. """ if self.provider is None or not self.enabled: return None try: return self.provider.get_auth_info(url, username) except Exception: self.enabled = False return None def save_auth_info(self, url: str, username: str, password: str) -> bool: """Set the password for the given url and username. Returns whether the operation is successful. """ if self.provider is None or not self.enabled: return False try: self.provider.save_auth_info(url, username, password) return True except Exception: self.enabled = False return False def delete_auth_info(self, url: str, username: str) -> bool: """Delete the password for the given url and username. Returns whether the operation is successful. """ if self.provider is None or not self.enabled: return False try: self.provider.delete_auth_info(url, username) return True except Exception: self.enabled = False return False keyring = Keyring() pdm-2.23.1/src/pdm/models/backends.py000066400000000000000000000107771477560627500173740ustar00rootroot00000000000000from __future__ import annotations import abc import os import urllib.parse from pathlib import Path from typing import TYPE_CHECKING from pdm.utils import expand_env_vars if TYPE_CHECKING: from typing import TypedDict BuildSystem = TypedDict("BuildSystem", {"requires": list[str], "build-backend": str}) class BuildBackend(metaclass=abc.ABCMeta): """A build backend that does not support dynamic values in dependencies""" def __init__(self, root: Path) -> None: self.root = root def expand_line(self, line: str, expand_env: bool = True) -> str: return line def relative_path_to_url(self, path: str) -> str: return self.root.joinpath(path).as_uri() @classmethod @abc.abstractmethod def build_system(cls) -> BuildSystem: pass class FlitBackend(BuildBackend): @classmethod def build_system(cls) -> BuildSystem: return { "requires": ["flit_core>=3.2,<4"], "build-backend": "flit_core.buildapi", } class SetuptoolsBackend(BuildBackend): @classmethod def build_system(cls) -> BuildSystem: return { "requires": ["setuptools>=61"], "build-backend": "setuptools.build_meta", } class PDMBackend(BuildBackend): def expand_line(self, req: str, expand_env: bool = True) -> str: line = req.replace("file:///${PROJECT_ROOT}", self.root.as_uri()) if expand_env: line = expand_env_vars(line) return line def relative_path_to_url(self, path: str) -> str: if os.path.isabs(path): return Path(path).as_uri() return f"file:///${{PROJECT_ROOT}}/{urllib.parse.quote(path)}" @classmethod def build_system(cls) -> BuildSystem: return { "requires": ["pdm-backend"], "build-backend": "pdm.backend", } # Context formatting helpers for hatch class PathContext: def __init__(self, path: Path) -> None: self.__path = path def __format__(self, __format_spec: str) -> str: if not __format_spec: return self.__path.as_posix() elif __format_spec == "uri": return self.__path.as_uri() elif __format_spec == "real": return self.__path.resolve().as_posix() raise ValueError(f"Unknown format specifier: {__format_spec}") class EnvContext: def __init__(self, expand: bool = True) -> None: self.expand = expand def __format__(self, __format_spec: str) -> str: name, sep, default = __format_spec.partition(":") if not self.expand: return f"${{{name}}}" if name in os.environ: return os.environ[name] if not sep: raise ValueError(f"Nonexistent environment variable must set a default: {name}") return default class HatchBackend(BuildBackend): def expand_line(self, line: str, expand_env: bool = True) -> str: return line.format( env=EnvContext(expand=expand_env), root=PathContext(self.root), home=PathContext(Path.home()), ) def relative_path_to_url(self, path: str) -> str: if os.path.isabs(path): return Path(path).as_uri() return f"{{root:uri}}/{urllib.parse.quote(path)}" @classmethod def build_system(cls) -> BuildSystem: return { "requires": ["hatchling"], "build-backend": "hatchling.build", } _BACKENDS: dict[str, type[BuildBackend]] = { "pdm-backend": PDMBackend, "setuptools": SetuptoolsBackend, "flit-core": FlitBackend, "hatchling": HatchBackend, } # Fallback to the first backend DEFAULT_BACKEND = next(iter(_BACKENDS.values())) def get_backend(name: str) -> type[BuildBackend]: """Get the build backend class by name""" return _BACKENDS[name] def get_backend_by_spec(spec: dict) -> type[BuildBackend]: """Get the build backend class by specification. The parameter passed in is the 'build-system' section in pyproject.toml. """ if "build-backend" not in spec: return DEFAULT_BACKEND for backend_cls in _BACKENDS.values(): if backend_cls.build_system()["build-backend"] == spec["build-backend"]: return backend_cls return DEFAULT_BACKEND def get_relative_path(url: str) -> str | None: if url.startswith("file:///${PROJECT_ROOT}"): return urllib.parse.unquote(url[len("file:///${PROJECT_ROOT}/") :]) if url.startswith("{root:uri}"): return urllib.parse.unquote(url[len("{root:uri}/") :]) return None pdm-2.23.1/src/pdm/models/cached_package.py000066400000000000000000000057171477560627500205020ustar00rootroot00000000000000from __future__ import annotations import os import shutil from functools import cached_property from pathlib import Path from typing import Any, ClassVar, ContextManager from pdm.termui import logger class CachedPackage: """A package cached in the central package store. The directory name is similar to wheel's filename: $PACKAGE_ROOT//----/ The checksum is stored in a file named `.checksum` under the directory. Under the directory there could be a text file named `.referrers`. Each line of the file is a distribution path that refers to this package. *Only wheel installations will be cached* """ cache_files: ClassVar[tuple[str, ...]] = (".lock", ".checksum", ".referrers") """List of files storing cache metadata and not being part of the package""" def __init__(self, path: str | Path, original_wheel: Path | None = None) -> None: self.path = Path(os.path.normcase(os.path.expanduser(path))).resolve() self.original_wheel = original_wheel self._referrers: set[str] | None = None def lock(self) -> ContextManager[Any]: import filelock return filelock.FileLock(self.path / ".lock") @cached_property def checksum(self) -> str: """The checksum of the path""" return self.path.joinpath(".checksum").read_text().strip() @cached_property def dist_info(self) -> Path: """The dist-info directory of the wheel""" from installer.exceptions import InvalidWheelSource try: return next(self.path.glob("*.dist-info")) except StopIteration: raise InvalidWheelSource(f"The wheel doesn't contain metadata {self.path!r}") from None @property def referrers(self) -> set[str]: """A set of entries in referrers file""" if self._referrers is None: filepath = self.path / ".referrers" if not filepath.is_file(): return set() self._referrers = { line.strip() for line in filepath.read_text("utf8").splitlines() if line.strip() and os.path.exists(line.strip()) } return self._referrers def add_referrer(self, path: str) -> None: """Add a new referrer""" path = os.path.normcase(os.path.expanduser(os.path.abspath(path))) referrers = self.referrers | {path} (self.path / ".referrers").write_text("\n".join(sorted(referrers)) + "\n", "utf8") self._referrers = None def remove_referrer(self, path: str) -> None: """Remove a referrer""" path = os.path.normcase(os.path.expanduser(os.path.abspath(path))) referrers = self.referrers - {path} (self.path / ".referrers").write_text("\n".join(referrers) + "\n", "utf8") self._referrers = None def cleanup(self) -> None: logger.info("Clean up cached package %s", self.path) shutil.rmtree(self.path) pdm-2.23.1/src/pdm/models/caches.py000066400000000000000000000270741477560627500170460ustar00rootroot00000000000000from __future__ import annotations import contextlib import hashlib import json import os import stat from functools import lru_cache from pathlib import Path from typing import TYPE_CHECKING, Generic, Iterable, TypeVar import httpx from packaging.utils import canonicalize_name, parse_wheel_filename from pdm._types import CandidateInfo from pdm.exceptions import PdmException from pdm.models.cached_package import CachedPackage from pdm.models.candidates import Candidate from pdm.models.markers import EnvSpec from pdm.termui import logger from pdm.utils import atomic_open_for_write, create_tracked_tempdir if TYPE_CHECKING: from unearth import Link KT = TypeVar("KT") VT = TypeVar("VT") class JSONFileCache(Generic[KT, VT]): """A file cache that stores key-value pairs in a json file.""" def __init__(self, cache_file: Path | str) -> None: self.cache_file = Path(cache_file) self._cache: dict[str, VT] = {} self._read_cache() def _read_cache(self) -> None: if not self.cache_file.exists(): self._cache = {} return with self.cache_file.open() as fp: try: self._cache = json.load(fp) except json.JSONDecodeError: return def _write_cache(self) -> None: with self.cache_file.open("w") as fp: json.dump(self._cache, fp) def __contains__(self, obj: KT) -> bool: return self._get_key(obj) in self._cache @classmethod def _get_key(cls, obj: KT) -> str: return str(obj) def get(self, obj: KT) -> VT: key = self._get_key(obj) return self._cache[key] def set(self, obj: KT, value: VT) -> None: key = self._get_key(obj) self._cache[key] = value self._write_cache() class CandidateInfoCache(JSONFileCache[Candidate, CandidateInfo]): """A cache manager that stores the candidate -> (dependencies, requires_python, summary) mapping. """ @staticmethod def get_url_part(link: Link) -> str: import base64 from pdm.utils import url_without_fragments url = url_without_fragments(link.split_auth()[1]) return base64.urlsafe_b64encode(url.encode()).decode() @classmethod def _get_key(cls, obj: Candidate) -> str: # Name and version are set when dependencies are resolved, # so use them for cache key. Local directories won't be cached. if not obj.name or not obj.version: raise KeyError("The package is missing a name or version") extras = "[{}]".format(",".join(sorted(obj.req.extras))) if obj.req.extras else "" version = obj.version if obj.link is not None: version = cls.get_url_part(obj.link) return f"{obj.name}{extras}-{version}" class HashCache: """Caches hashes of PyPI artifacts so we do not need to re-download them. Hashes are only cached when the URL appears to contain a hash in it and the cache key includes the hash value returned from the server). This ought to avoid issues where the location on the server changes. """ FAVORITE_HASH = "sha256" STRONG_HASHES = ("sha256", "sha384", "sha512") def __init__(self, directory: Path | str) -> None: self.directory = Path(directory) def _read_from_link(self, link: Link, session: httpx.Client) -> Iterable[bytes]: if link.is_file: with open(link.file_path, "rb") as f: yield from f else: import httpx with session.stream("GET", link.normalized) as resp: try: resp.raise_for_status() except httpx.HTTPStatusError as e: raise PdmException(f"Failed to read from {link.redacted}: {e}") from e yield from resp.iter_bytes(chunk_size=8192) def _get_file_hash(self, link: Link, session: httpx.Client) -> str: h = hashlib.new(self.FAVORITE_HASH) logger.debug("Downloading link %s for calculating hash", link.redacted) for chunk in self._read_from_link(link, session): h.update(chunk) return ":".join([h.name, h.hexdigest()]) def _should_cache(self, link: Link) -> bool: # For now, we only disable caching for local files. # We may add more when we know better about it. return not link.is_file def get_hash(self, link: Link, session: httpx.Client) -> str: # If there is no link hash (i.e., md5, sha256, etc.), we don't want # to store it. hash_value = self.get(link.url_without_fragment) if not hash_value: if link.hashes and link.hashes.keys() & self.STRONG_HASHES: logger.debug("Using hash in link for %s", link.redacted) hash_name = next(k for k in self.STRONG_HASHES if k in link.hashes) hash_value = f"{hash_name}:{link.hashes[hash_name]}" elif link.hash and link.hash_name in self.STRONG_HASHES: logger.debug("Using hash in link for %s", link.redacted) hash_value = f"{link.hash_name}:{link.hash}" else: hash_value = self._get_file_hash(link, session) if self._should_cache(link): self.set(link.url_without_fragment, hash_value) return hash_value def _get_path_for_key(self, key: str) -> Path: hashed = hashlib.sha224(key.encode("utf-8")).hexdigest() parts = (hashed[:2], hashed[2:4], hashed[4:6], hashed[6:8], hashed[8:]) return self.directory.joinpath(*parts) def get(self, url: str) -> str | None: path = self._get_path_for_key(url) with contextlib.suppress(OSError, UnicodeError): return path.read_text("utf-8").strip() return None def set(self, url: str, hash: str) -> None: path = self._get_path_for_key(url) with contextlib.suppress(OSError, UnicodeError): path.parent.mkdir(parents=True, exist_ok=True) with atomic_open_for_write(path, encoding="utf-8") as fp: fp.write(hash) class EmptyCandidateInfoCache(CandidateInfoCache): def get(self, obj: Candidate) -> CandidateInfo: raise KeyError def set(self, obj: Candidate, value: CandidateInfo) -> None: pass class EmptyHashCache(HashCache): def get(self, url: str) -> str | None: return None def set(self, url: str, hash: str) -> None: pass class WheelCache: """Caches wheels so we do not need to rebuild them. Wheels are only cached when the URL contains egg-info or is a VCS repository with an *immutable* revision. There might be more than one wheels built for one sdist, the one with most preferred tag will be returned. """ def __init__(self, directory: Path | str) -> None: self.directory = Path(directory) self.ephemeral_directory = Path(create_tracked_tempdir(prefix="pdm-wheel-cache-")) def _get_candidates(self, path: Path) -> Iterable[Path]: if not path.exists(): return for candidate in path.iterdir(): if candidate.name.endswith(".whl"): yield candidate def _get_path_parts(self, link: Link, env_spec: EnvSpec) -> tuple[str, ...]: hash_key = { "url": link.url_without_fragment, # target env participates in the hash key to handle the some cases # where the sdist produces different wheels on different Pythons, and # the differences are not encoded in compatibility tags. "env_spec": env_spec.as_dict(), } if link.subdirectory: hash_key["subdirectory"] = link.subdirectory if link.hash and link.hash_name: hash_key[link.hash_name] = link.hash hashed = hashlib.sha224( json.dumps(hash_key, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode("utf-8") ).hexdigest() return (hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]) def get_path_for_link(self, link: Link, env_spec: EnvSpec) -> Path: parts = self._get_path_parts(link, env_spec) return self.directory.joinpath(*parts) def get_ephemeral_path_for_link(self, link: Link, env_spec: EnvSpec) -> Path: parts = self._get_path_parts(link, env_spec) return self.ephemeral_directory.joinpath(*parts) def get(self, link: Link, project_name: str | None, env_spec: EnvSpec) -> Path | None: if not project_name: return None canonical_name = canonicalize_name(project_name) candidate = self._get_from_path(self.get_path_for_link(link, env_spec), canonical_name, env_spec) if candidate is not None: return candidate return self._get_from_path(self.get_ephemeral_path_for_link(link, env_spec), canonical_name, env_spec) def _get_from_path(self, path: Path, canonical_name: str, env_spec: EnvSpec) -> Path | None: max_compatible_candidate: tuple[tuple[int, ...], Path | None] = ((-1, -1, -1, -1), None) for candidate in self._get_candidates(path): try: name, *_ = parse_wheel_filename(candidate.name) except ValueError: logger.debug("Ignoring invalid cached wheel %s", candidate.name) continue if canonical_name != canonicalize_name(name): logger.debug( "Ignoring cached wheel %s with invalid project name %s, expected: %s", candidate.name, name, canonical_name, ) continue compat = env_spec.wheel_compatibility(candidate.name) if compat is None: continue if compat > max_compatible_candidate[0]: max_compatible_candidate = (compat, candidate) return max_compatible_candidate[1] @lru_cache(maxsize=None) def get_wheel_cache(directory: Path | str) -> WheelCache: return WheelCache(directory) class PackageCache: def __init__(self, root: Path) -> None: self.root = root def cache_wheel(self, wheel: Path) -> CachedPackage: """Create a CachedPackage instance from a wheel file""" import zipfile from installer.utils import make_file_executable dest = self.root.joinpath(f"{wheel.name}.cache") pkg = CachedPackage(dest, original_wheel=wheel) if dest.exists(): return pkg dest.mkdir(parents=True, exist_ok=True) with pkg.lock(): logger.info("Unpacking wheel into cached location %s", dest) with zipfile.ZipFile(wheel) as zf: try: for item in zf.infolist(): target_path = zf.extract(item, dest) mode = item.external_attr >> 16 is_executable = bool(mode and stat.S_ISREG(mode) and mode & 0o111) if is_executable: make_file_executable(target_path) except Exception: # pragma: no cover pkg.cleanup() # cleanup on any error raise return pkg def iter_packages(self) -> Iterable[CachedPackage]: for path in self.root.rglob("*.whl.cache"): p = CachedPackage(path) with p.lock(): # ensure the package is not being created pass yield p def cleanup(self) -> int: """Remove unused cached packages""" count = 0 for pkg in self.iter_packages(): if not any(os.path.exists(fn) for fn in pkg.referrers): pkg.cleanup() count += 1 return count pdm-2.23.1/src/pdm/models/candidates.py000066400000000000000000000700601477560627500177100ustar00rootroot00000000000000from __future__ import annotations import dataclasses import hashlib import os import re import warnings from functools import cached_property from pathlib import Path from tempfile import TemporaryDirectory from typing import TYPE_CHECKING, Any, ClassVar, cast, no_type_check from zipfile import ZipFile from packaging.version import InvalidVersion from pdm import termui from pdm.builders import EditableBuilder, WheelBuilder from pdm.compat import importlib_metadata as im from pdm.exceptions import BuildError, CandidateNotFound, InvalidPyVersion, PDMWarning, RequirementError from pdm.models.backends import get_backend, get_backend_by_spec from pdm.models.reporter import CandidateReporter from pdm.models.requirements import ( FileRequirement, Requirement, VcsRequirement, _egg_info_re, filter_requirements_with_extras, ) from pdm.models.setup import Setup from pdm.models.specifiers import PySpecSet from pdm.utils import ( comparable_version, convert_hashes, filtered_sources, get_rev_from_url, normalize_name, url_without_fragments, ) if TYPE_CHECKING: from importlib.metadata import _SimplePath from unearth import Link, Package, PackageFinder from pdm._types import FileHash from pdm.environments import BaseEnvironment def _dist_info_files(whl_zip: ZipFile) -> list[str]: """Identify the .dist-info folder inside a wheel ZipFile.""" res = [] for path in whl_zip.namelist(): m = re.match(r"[^/\\]+-[^/\\]+\.dist-info/", path) if m: res.append(path) if res: return res raise Exception("No .dist-info folder found in wheel") def _get_wheel_metadata_from_wheel(whl_file: Path, metadata_directory: str) -> str: """Extract the metadata from a wheel. Fallback for when the build backend does not define the 'get_wheel_metadata' hook. """ with ZipFile(whl_file) as zipf: dist_info = _dist_info_files(zipf) zipf.extractall(path=metadata_directory, members=dist_info) return os.path.join(metadata_directory, dist_info[0].split("/")[0]) def _filter_none(data: dict[str, Any]) -> dict[str, Any]: """Return a new dict without None values""" return {k: v for k, v in data.items() if v is not None} def _find_best_match_link(finder: PackageFinder, req: Requirement, files: list[FileHash]) -> Link | None: """Get the best matching link for a requirement""" # This function is called when a lock file candidate is given or incompatible wheel # In this case, the requirement must be pinned, so no need to pass allow_prereleases # If links are not empty, find the best match from the links, otherwise find from # the package sources. from unearth import Link links = [Link(f["url"]) for f in files if "url" in f] hashes = convert_hashes(files) if not links: best = finder.find_best_match(req.as_line(), hashes=hashes).best else: # this branch won't be executed twice if ignore_compatibility is True evaluator = finder.build_evaluator(req.name) packages = finder._evaluate_links(links, evaluator) best = max(packages, key=finder._sort_key, default=None) return best.link if best is not None else None class MetadataDistribution(im.Distribution): """A wrapper around a single METADATA file to provide the Distribution interface""" def __init__(self, text: str) -> None: self.text = text def locate_file(self, path: str | os.PathLike[str]) -> _SimplePath: return Path() def read_text(self, filename: str) -> str | None: if filename != "": return None return self.text class Candidate: """A concrete candidate that can be downloaded and installed. A candidate comes from the PyPI index of a package, or from the requirement itself (for file or VCS requirements). Each candidate has a name, version and several dependencies together with package metadata. """ __slots__ = ( "_preferred", "_prepared", "_requires_python", "_revision", "hashes", "link", "name", "req", "requested", "summary", "version", ) def __init__( self, req: Requirement, name: str | None = None, version: str | None = None, link: Link | None = None, ): """ :param req: the requirement that produces this candidate. :param name: the name of the candidate. :param version: the version of the candidate. :param link: the file link of the candidate. """ self.req = req self.name = name or self.req.project_name self.version = version if link is None and not req.is_named: link = cast("Link", req.as_file_link()) # type: ignore[attr-defined] self.link = link self.summary = "" self.hashes: list[FileHash] = [] self.requested = False self._requires_python: str | None = None self._prepared: PreparedCandidate | None = None self._revision = getattr(req, "revision", None) def identify(self) -> str: return self.req.identify() def copy_with(self, requirement: Requirement) -> Candidate: can = Candidate(requirement, name=self.name, version=self.version, link=self.link) can.summary = self.summary can.hashes = self.hashes can._requires_python = self._requires_python can._prepared = self._prepared can._revision = self._revision if can._prepared: can._prepared.req = requirement return can @property def dep_key(self) -> tuple[str, str | None]: """Key for retrieving and storing dependencies from the provider. Return a tuple of (name, version). For URL candidates, the version is None but there will be only one for the same name so it is also unique. """ return (self.identify(), self.version) @property def prepared(self) -> PreparedCandidate | None: return self._prepared def __eq__(self, other: Any) -> bool: if not isinstance(other, Candidate): return False if self.req.is_named: return self.name == other.name and self.version == other.version return self.name == other.name and self.link == other.link def get_revision(self) -> str: if not self.req.is_vcs: raise AttributeError("Non-VCS candidate doesn't have revision attribute") if self._revision: return self._revision if self.req.revision: # type: ignore[attr-defined] return self.req.revision # type: ignore[attr-defined] return self._prepared.revision if self._prepared else "unknown" def __repr__(self) -> str: source = getattr(self.link, "comes_from", None) from_source = f" from {source}" if source else "" return f"" def __str__(self) -> str: if self.req.is_named: return f"{self.name}@{self.version}" assert self.link is not None return f"{self.name}@{self.link.url_without_fragment}" @classmethod def from_installation_candidate(cls, candidate: Package, req: Requirement) -> Candidate: """Build a candidate from unearth's find result.""" return cls( req, name=candidate.name, version=str(candidate.version), link=candidate.link, ) @property def requires_python(self) -> str: """The Python version constraint of the candidate.""" if self._requires_python is not None: return self._requires_python if self.link: requires_python = self.link.requires_python if requires_python is not None: if requires_python.isdigit(): requires_python = f">={requires_python},<{int(requires_python) + 1}" try: # ensure the specifier is valid PySpecSet(requires_python) except InvalidPyVersion: pass else: self._requires_python = requires_python return self._requires_python or "" @requires_python.setter def requires_python(self, value: str) -> None: try: # ensure the specifier is valid PySpecSet(value) except InvalidPyVersion: return self._requires_python = value @no_type_check def as_lockfile_entry(self, project_root: Path) -> dict[str, Any]: """Build a lockfile entry dictionary for the candidate.""" version = str(self.version) if not self.req.is_pinned: try: version = str(comparable_version(version)) except InvalidVersion as e: raise RequirementError(f"Invalid version for {self.req.as_line()}: {e}") from None result = { "name": normalize_name(self.name), "version": version, "extras": sorted(self.req.extras or ()), "requires_python": str(self.requires_python), "editable": self.req.editable, "subdirectory": getattr(self.req, "subdirectory", None), } if self.req.is_vcs: result.update( { self.req.vcs: self.req.repo, "ref": self.req.ref, } ) if not self.req.editable: result.update(revision=self.get_revision()) elif not self.req.is_named: if self.req.is_file_or_url and self.req.is_local: self.req._root = project_root result.update(path=self.req.str_path) else: result.update(url=self.req.url) return {k: v for k, v in result.items() if v} def format(self) -> str: """Format for output.""" return f"[req]{self.name}[/] [warning]{self.version}[/]" def prepare(self, environment: BaseEnvironment, reporter: CandidateReporter | None = None) -> PreparedCandidate: """Prepare the candidate for installation.""" if self._prepared is None: if isinstance(self.req, FileRequirement): self.req.check_installable() self._prepared = PreparedCandidate(self, environment, reporter=reporter or CandidateReporter()) else: self._prepared.environment = environment if reporter is not None: self._prepared.reporter = reporter return self._prepared @dataclasses.dataclass class PreparedCandidate: """A candidate that has been prepared for installation. The metadata and built wheel are available. """ _build_dir_cache: ClassVar[dict[Link, str]] = {} candidate: Candidate environment: BaseEnvironment reporter: CandidateReporter = dataclasses.field(default_factory=CandidateReporter) def __post_init__(self) -> None: self.req = self.candidate.req self.link = self._replace_url_vars(self.candidate.link) self._cached: Path | None = None self._source_dir: Path | None = None self._unpacked_dir: Path | None = None self._metadata_dir: str | None = None self._metadata: im.Distribution | None = None if self.link is not None and self.link.is_file and self.link.file_path.is_dir(): self._source_dir = self.link.file_path self._unpacked_dir = self._source_dir / (self.link.subdirectory or "") def _replace_url_vars(self, link: Link | None) -> Link | None: if link is None: return None url = self.environment.project.backend.expand_line(link.normalized) return dataclasses.replace(link, url=url) @cached_property def revision(self) -> str: from unearth import vcs_support if not (self._source_dir and os.path.exists(self._source_dir)): # It happens because the cached wheel is hit and the source code isn't # pulled to local. In this case the link url must contain the full commit # hash which can be taken as the revision safely. # See more info at https://github.com/pdm-project/pdm/issues/349 rev = get_rev_from_url(self.candidate.link.url) # type: ignore[union-attr] if rev: return rev assert isinstance(self.req, VcsRequirement) return vcs_support.get_backend(self.req.vcs, self.environment.project.core.ui.verbosity).get_revision( cast(Path, self._source_dir) ) def direct_url(self) -> dict[str, Any] | None: """PEP 610 direct_url.json data""" req = self.req if isinstance(req, VcsRequirement): if req.editable: assert self._source_dir return _filter_none( { "url": self._source_dir.as_uri(), "dir_info": {"editable": True}, "subdirectory": req.subdirectory, } ) return _filter_none( { "url": url_without_fragments(req.repo), "vcs_info": _filter_none( { "vcs": req.vcs, "requested_revision": req.ref, "commit_id": self.revision, } ), "subdirectory": req.subdirectory, } ) elif isinstance(req, FileRequirement): assert self.link is not None if self.link.is_file and self.link.file_path.is_dir(): return _filter_none( { "url": self.link.url_without_fragment, "dir_info": _filter_none({"editable": req.editable or None}), "subdirectory": req.subdirectory, } ) hash_cache = self.environment.project.make_hash_cache() return _filter_none( { "url": self.link.url_without_fragment, "archive_info": { "hash": hash_cache.get_hash(self.link, self.environment.session).replace(":", "=") }, "subdirectory": req.subdirectory, } ) else: return None def build(self) -> Path: """Call PEP 517 build hook to build the candidate into a wheel""" self._obtain(allow_all=False) if self._cached: return self._cached if not self.req.editable: cached = self._get_build_cache() if cached: return cached assert self._source_dir, "Source directory isn't ready yet" builder_cls = EditableBuilder if self.req.editable else WheelBuilder builder = builder_cls(str(self._unpacked_dir), self.environment) build_dir = self._get_wheel_dir() os.makedirs(build_dir, exist_ok=True) termui.logger.info("Running PEP 517 backend to build a wheel for %s", self.link) self.reporter.report_build_start(self.link.filename) # type: ignore[union-attr] self._cached = Path(builder.build(build_dir, metadata_directory=self._metadata_dir)) self.reporter.report_build_end(self.link.filename) # type: ignore[union-attr] return self._cached def _obtain(self, allow_all: bool = False, unpack: bool = True) -> None: """Fetch the link of the candidate and unpack to local if necessary. :param allow_all: If true, don't validate the wheel tag nor hashes :param unpack: Whether to download and unpack the link if it's not local """ if self._cached and self._wheel_compatible(self._cached.name, allow_all): return if self._source_dir and self._source_dir.exists(): return sources = filtered_sources(self.environment.project.sources, self.req.key) env_spec = self.environment.allow_all_spec if allow_all else self.environment.spec with self.environment.get_finder(sources, env_spec=env_spec) as finder: if not self.link or (self.link.is_wheel and not self._wheel_compatible(self.link.filename, allow_all)): if self.req.is_file_or_url: raise CandidateNotFound(f"The URL requirement {self.req.as_line()} is a wheel but incompatible") self.link = self._cached = None # reset the incompatible wheel self.link = _find_best_match_link( finder, self.req.as_pinned_version(self.candidate.version), self.candidate.hashes ) if not self.link: raise CandidateNotFound( f"No candidate is found for `{self.req.project_name}` that matches the environment or hashes" ) if not self.candidate.link: self.candidate.link = self.link # find if there is any build cache for the candidate if not self.req.editable: cached = self._get_build_cache() if cached and self._wheel_compatible(cached.name, allow_all): self._cached = cached return # If not, download and unpack the link if unpack: self._unpack(validate_hashes=not allow_all) def _unpack(self, validate_hashes: bool = False) -> None: hash_options = None if validate_hashes and self.candidate.hashes: hash_options = convert_hashes(self.candidate.hashes) assert self.link is not None with self.environment.get_finder() as finder: with TemporaryDirectory(prefix="pdm-download-") as tmpdir: build_dir = self._get_build_dir() if self.link.is_wheel: download_dir = build_dir else: download_dir = tmpdir result = finder.download_and_unpack( self.link, build_dir, download_dir, hash_options, download_reporter=self.reporter.report_download, unpack_reporter=self.reporter.report_unpack, ) if self.link.is_wheel: self._cached = result else: self._source_dir = Path(build_dir) self._unpacked_dir = result def prepare_metadata(self, force_build: bool = False) -> im.Distribution: self._obtain(allow_all=True, unpack=False) if self._metadata_dir: return im.PathDistribution(Path(self._metadata_dir)) if self._cached: return self._get_metadata_from_wheel(self._cached) assert self.link is not None if self.link.dist_info_metadata: assert self.link.dist_info_link dist = self._get_metadata_from_metadata_link(self.link.dist_info_link, self.link.dist_info_metadata) if dist is not None: return dist self._unpack(validate_hashes=False) if self._cached: # check again if the wheel is downloaded to local return self._get_metadata_from_wheel(self._cached) assert self._unpacked_dir, "Source directory isn't ready yet" pyproject_toml = self._unpacked_dir / "pyproject.toml" if not force_build and pyproject_toml.exists(): dist = self._get_metadata_from_project(pyproject_toml) if dist is not None: return dist # If all fail, try building the source to get the metadata metadata_parent = self.environment.project.core.create_temp_dir(prefix="pdm-meta-") return self._get_metadata_from_build(self._unpacked_dir, metadata_parent) def _get_metadata_from_metadata_link( self, link: Link, medata_hash: bool | dict[str, str] | None ) -> im.Distribution | None: resp = self.environment.session.get(link.normalized) if isinstance(medata_hash, dict): hash_name, hash_value = next(iter(medata_hash.items())) if hashlib.new(hash_name, resp.content).hexdigest() != hash_value: termui.logger.warning("Metadata hash mismatch for %s, ignoring the metadata", link) return None return MetadataDistribution(resp.text) def _get_metadata_from_wheel(self, wheel: Path) -> im.Distribution: # Get metadata from METADATA inside the wheel metadata_parent = self.environment.project.core.create_temp_dir(prefix="pdm-meta-") dist_info = self._metadata_dir = _get_wheel_metadata_from_wheel(wheel, metadata_parent) return im.PathDistribution(Path(dist_info)) def _get_metadata_from_project(self, pyproject_toml: Path) -> im.Distribution | None: # Try getting from PEP 621 metadata from pdm.formats import MetaConvertError from pdm.project.project_file import PyProject try: pyproject = PyProject(pyproject_toml, ui=self.environment.project.core.ui) except MetaConvertError as e: termui.logger.warning("Failed to parse pyproject.toml: %s", e) return None metadata = pyproject.metadata.unwrap() if not metadata: termui.logger.warning("Failed to parse pyproject.toml") return None dynamic_fields = metadata.get("dynamic", []) # Use the parse result only when all are static if not set(dynamic_fields).isdisjoint( { "name", "version", "dependencies", "optional-dependencies", "requires-python", } ): return None try: backend_cls = get_backend_by_spec(pyproject.build_system) except Exception: # no variable expansion backend_cls = get_backend("setuptools") backend = backend_cls(pyproject_toml.parent) if "name" not in metadata: termui.logger.warning("Failed to parse pyproject.toml, name is required") return None setup = Setup( name=metadata.get("name"), summary=metadata.get("description"), version=metadata.get("version", "0.0.0"), install_requires=list( map( backend.expand_line, metadata.get("dependencies", []), ) ), extras_require={ k: list(map(backend.expand_line, v)) for k, v in metadata.get("optional-dependencies", {}).items() }, python_requires=metadata.get("requires-python"), ) return setup.as_dist() def _get_metadata_from_build(self, source_dir: Path, metadata_parent: str) -> im.Distribution: builder = EditableBuilder if self.req.editable else WheelBuilder try: termui.logger.info("Running PEP 517 backend to get metadata for %s", self.link) self.reporter.report_build_start(self.link.filename) # type: ignore[union-attr] self._metadata_dir = builder(source_dir, self.environment).prepare_metadata(metadata_parent) self.reporter.report_build_end(self.link.filename) # type: ignore[union-attr] except BuildError: termui.logger.warning("Failed to build package, try parsing project files.") try: setup = Setup.from_directory(source_dir) except Exception: message = "Failed to parse the project files, dependencies may be missing" termui.logger.warning(message) warnings.warn(message, PDMWarning, stacklevel=1) setup = Setup() return setup.as_dist() else: return im.PathDistribution(Path(cast(str, self._metadata_dir))) @property def metadata(self) -> im.Distribution: if self._metadata is None: result = self.prepare_metadata() if not self.candidate.name: self.req.name = self.candidate.name = cast(str, result.metadata.get("Name")) if not self.candidate.version and result.metadata.get("Version"): self.candidate.version = result.version if not self.candidate.requires_python: self.candidate.requires_python = result.metadata.get("Requires-Python", "") self._metadata = result return self._metadata def get_dependencies_from_metadata(self) -> list[Requirement]: """Get the dependencies of a candidate from metadata.""" extras = self.req.extras or () return filter_requirements_with_extras(self.metadata.requires or [], extras) def should_cache(self) -> bool: """Determine whether to cache the dependencies and built wheel.""" from unearth import vcs_support if not self.environment.project.core.state.enable_cache: return False link, source_dir = self.candidate.link, self._source_dir if self.req.editable: return False if self.req.is_named: return True if self.req.is_vcs: if not source_dir: # If the candidate isn't prepared, we can't cache it return False assert link vcs_backend = vcs_support.get_backend(link.vcs, self.environment.project.core.ui.verbosity) return vcs_backend.is_immutable_revision(source_dir, link) if link and not (link.is_file and link.file_path.is_dir()): # Cache if the link contains egg-info like 'foo-1.0' return _egg_info_re.search(link.filename) is not None return False def _get_build_cache(self) -> Path | None: if not self.environment.project.core.state.enable_cache: return None wheel_cache = self.environment.project.make_wheel_cache() assert self.candidate.link cache_entry = wheel_cache.get(self.candidate.link, self.candidate.name, self.environment.spec) if cache_entry is not None: termui.logger.info("Using cached wheel: %s", cache_entry) return cache_entry def _get_build_dir(self) -> str: assert self.link is not None if self.link.is_file and self.link.file_path.is_dir(): # Local directories are built in tree return str(self.link.file_path) if self.req.editable: # In this branch the requirement must be an editable VCS requirement. # The repository will be unpacked into a *persistent* src directory. prefix: Path | None = None if self.environment.is_local: prefix = self.environment.packages_path # type: ignore[attr-defined] else: venv = self.environment.interpreter.get_venv() if venv is not None: prefix = venv.root if prefix is not None: src_dir = prefix / "src" else: src_dir = Path("src") src_dir.mkdir(exist_ok=True, parents=True) dirname = self.candidate.name or self.req.name if not dirname: dirname, _ = os.path.splitext(self.link.filename) return str(src_dir / str(dirname)) # Otherwise, for source dists, they will be unpacked into a *temp* directory. if (build_dir := self._build_dir_cache.get(self.link)) is None: build_dir = self._build_dir_cache[self.link] = self.environment.project.core.create_temp_dir( prefix="pdm-build-" ) return build_dir def _wheel_compatible(self, wheel_file: str, allow_all: bool = False) -> bool: env_spec = self.environment.allow_all_spec if allow_all else self.environment.spec return env_spec.wheel_compatibility(wheel_file) is not None def _get_wheel_dir(self) -> str: assert self.candidate.link wheel_cache = self.environment.project.make_wheel_cache() if self.should_cache(): termui.logger.info("Saving wheel to cache: %s", self.candidate.link) return wheel_cache.get_path_for_link(self.candidate.link, self.environment.spec).as_posix() else: return wheel_cache.get_ephemeral_path_for_link(self.candidate.link, self.environment.spec).as_posix() pdm-2.23.1/src/pdm/models/finder.py000066400000000000000000000076101477560627500170610ustar00rootroot00000000000000from __future__ import annotations import logging from typing import TYPE_CHECKING, Any, Callable import unearth from dep_logic.specifiers import InvalidSpecifier, parse_version_specifier from packaging.version import Version from unearth.evaluator import Evaluator, FormatControl, LinkMismatchError, Package from pdm.models.markers import EnvSpec from pdm.utils import parse_version logger = logging.getLogger("unearth") if TYPE_CHECKING: from pdm.models.session import PDMPyPIClient class ReverseVersion(Version): """A subclass of version that reverse the order of comparison.""" def __lt__(self, other: Any) -> bool: return super().__gt__(other) def __le__(self, other: Any) -> bool: return super().__ge__(other) def __gt__(self, other: Any) -> bool: return super().__lt__(other) def __ge__(self, other: Any) -> bool: return super().__le__(other) class PDMEvaluator(Evaluator): def __init__(self, *args: Any, env_spec: EnvSpec, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.env_spec = env_spec def check_requires_python(self, link: unearth.Link) -> None: if link.requires_python: try: requires_python = parse_version_specifier(link.requires_python) except InvalidSpecifier as e: logger.debug( "Invalid requires-python specifier for link(%s) %s: %s", link.redacted, link.requires_python, e ) return if (requires_python & self.env_spec.requires_python).is_empty(): raise LinkMismatchError( f"The package requires-python {link.requires_python} is not compatible with the target {self.env_spec.requires_python}." ) def check_wheel_tags(self, filename: str) -> None: if self.env_spec.wheel_compatibility(filename) is None: raise LinkMismatchError( f"The wheel file {filename} is not compatible with the target environment {self.env_spec}." ) class PDMPackageFinder(unearth.PackageFinder): def __init__( self, session: PDMPyPIClient | None = None, *, env_spec: EnvSpec, minimal_version: bool = False, **kwargs: Any, ) -> None: super().__init__(session, **kwargs) self.minimal_version = minimal_version self.env_spec = env_spec def build_evaluator(self, package_name: str, allow_yanked: bool = False) -> Evaluator: format_control = FormatControl(no_binary=self.no_binary, only_binary=self.only_binary) return PDMEvaluator( package_name=package_name, target_python=self.target_python, allow_yanked=allow_yanked, format_control=format_control, exclude_newer_than=self.exclude_newer_than, env_spec=self.env_spec, ) def _sort_key(self, package: Package) -> tuple: from packaging.utils import BuildTag, canonicalize_name if self.minimal_version: version_cls: Callable[[str], Version] = ReverseVersion else: version_cls = parse_version link = package.link compatibility = (0, 0, 0, 0) # default value for sdists build_tag: BuildTag = () prefer_binary = False if link.is_wheel: compat = self.env_spec.wheel_compatibility(link.filename) if compat is None: compatibility = (-1, -1, -1, -1) else: compatibility = compat if canonicalize_name(package.name) in self.prefer_binary or ":all:" in self.prefer_binary: prefer_binary = True return ( -int(link.is_yanked), int(prefer_binary), version_cls(package.version) if package.version is not None else version_cls("0"), compatibility, build_tag, ) pdm-2.23.1/src/pdm/models/in_process/000077500000000000000000000000001477560627500174005ustar00rootroot00000000000000pdm-2.23.1/src/pdm/models/in_process/__init__.py000066400000000000000000000035461477560627500215210ustar00rootroot00000000000000""" A collection of functions that need to be called via a subprocess call. """ from __future__ import annotations import contextlib import functools import json import os import subprocess import tempfile from typing import Any, Generator from pdm.compat import resources_path from pdm.models.markers import EnvSpec @contextlib.contextmanager def _in_process_script(name: str) -> Generator[str, None, None]: with resources_path(__name__, name) as script: yield str(script) def get_sys_config_paths(executable: str, vars: dict[str, str] | None = None, kind: str = "default") -> dict[str, str]: """Return the sys_config.get_paths() result for the python interpreter""" env = os.environ.copy() env.pop("__PYVENV_LAUNCHER__", None) if vars is not None: env["_SYSCONFIG_VARS"] = json.dumps(vars) with _in_process_script("sysconfig_get_paths.py") as script: cmd = [executable, "-Es", script, kind] return json.loads(subprocess.check_output(cmd, env=env)) def parse_setup_py(executable: str, path: str) -> dict[str, Any]: """Parse setup.py and return the kwargs""" with _in_process_script("parse_setup.py") as script: _, outfile = tempfile.mkstemp(suffix=".json") cmd = [executable, script, path, outfile] subprocess.check_call(cmd) with open(outfile, "rb") as fp: return json.load(fp) @functools.lru_cache def get_env_spec(executable: str) -> EnvSpec: """Get the environment spec of the python interpreter""" from pdm.core import importlib_metadata required_libs = ["dep_logic", "packaging"] shared_libs = {str(importlib_metadata.distribution(lib).locate_file("")) for lib in required_libs} with _in_process_script("env_spec.py") as script: return EnvSpec.from_spec(**json.loads(subprocess.check_output([executable, "-EsS", script, *shared_libs]))) pdm-2.23.1/src/pdm/models/in_process/env_spec.py000066400000000000000000000013031477560627500215510ustar00rootroot00000000000000from __future__ import annotations import json import platform import site import sys import sysconfig def get_current_env_spec() -> dict[str, str | bool]: from dep_logic.tags import Platform python_version = f"{sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]}" return { "requires_python": f"=={python_version}", "platform": str(Platform.current()), "implementation": platform.python_implementation().lower(), "gil_disabled": sysconfig.get_config_var("Py_GIL_DISABLED") or False, } if __name__ == "__main__": for shared_lib in sys.argv[1:]: site.addsitedir(shared_lib) print(json.dumps(get_current_env_spec(), indent=2)) pdm-2.23.1/src/pdm/models/in_process/parse_setup.py000066400000000000000000000147611477560627500223150ustar00rootroot00000000000000from __future__ import annotations import os import sys from typing import Any def _parse_setup_cfg(path: str) -> dict[str, Any]: import configparser setup_cfg = configparser.ConfigParser() setup_cfg.read(path, encoding="utf-8") result: dict[str, Any] = {} if not setup_cfg.has_section("metadata"): return result metadata = setup_cfg["metadata"] if "name" in metadata: result["name"] = metadata["name"] if "description" in metadata: result["description"] = metadata["description"] if "license" in metadata: result["license"] = metadata["license"] if "author" in metadata: result["author"] = metadata["author"] if "author_email" in metadata: result["author_email"] = metadata["author_email"] if "maintainer" in metadata: result["maintainer"] = metadata["maintainer"] if "maintainer_email" in metadata: result["maintainer_email"] = metadata["maintainer_email"] if "keywords" in metadata: keywords = metadata["keywords"].strip().splitlines() result["keywords"] = keywords if len(keywords) > 1 else keywords[0] if "classifiers" in metadata: result["classifiers"] = metadata["classifiers"].strip().splitlines() if "url" in metadata: result["url"] = metadata["url"] if "download_url" in metadata: result["download_url"] = metadata["download_url"] if "project_urls" in metadata: result["project_urls"] = dict( [u.strip() for u in url.split("=", 1)] for url in metadata["project_urls"].strip().splitlines() ) if "long_description" in metadata: long_description = metadata["long_description"].strip() if long_description.startswith("file:"): result["readme"] = long_description[5:].strip() if setup_cfg.has_section("options"): options = setup_cfg["options"] if "python_requires" in options: result["python_requires"] = options["python_requires"] if "install_requires" in options: result["install_requires"] = options["install_requires"].strip().splitlines() if "package_dir" in options: result["package_dir"] = dict( [p.strip() for p in d.split("=", 1)] for d in options["package_dir"].strip().splitlines() ) if setup_cfg.has_section("options.extras_require"): result["extras_require"] = { feature: dependencies.strip().splitlines() for feature, dependencies in setup_cfg["options.extras_require"].items() } if setup_cfg.has_section("options.entry_points"): result["entry_points"] = { entry_point: definitions.strip().splitlines() for entry_point, definitions in setup_cfg["options.entry_points"].items() } return result setup_kwargs = {} SUPPORTED_ARGS = ( "name", "version", "description", "license", "author", "author_email", "maintainer", "maintainer_email", "keywords", "classifiers", "url", "download_url", "project_urls", "python_requires", "install_requires", "extras_require", "entry_points", "package_dir", ) def fake_setup(**kwargs): setup_kwargs.update((k, v) for k, v in kwargs.items() if k in SUPPORTED_ARGS) def clean_metadata(metadata: dict[str, Any]) -> None: author = {} if "author" in metadata: author["name"] = metadata.pop("author") if "author_email" in metadata: author["email"] = metadata.pop("author_email") if author: metadata["authors"] = [author] maintainer = {} if "maintainer" in metadata: maintainer["name"] = metadata.pop("maintainer") if "maintainer_email" in metadata: maintainer["email"] = metadata.pop("maintainer_email") if maintainer: metadata["maintainers"] = [maintainer] urls = {} if "url" in metadata: urls["Homepage"] = metadata.pop("url") if "download_url" in metadata: urls["Downloads"] = metadata.pop("download_url") if "project_urls" in metadata: urls.update(metadata.pop("project_urls")) if urls: metadata["urls"] = urls if "" in metadata.get("package_dir", {}): metadata["package_dir"] = metadata["package_dir"][""] if "keywords" in metadata: keywords = metadata["keywords"] if isinstance(keywords, str): keywords = [k.strip() for k in keywords.split(",")] metadata["keywords"] = keywords if "entry_points" in metadata and isinstance(metadata["entry_points"], dict): entry_points = {} for entry_point, definitions in metadata["entry_points"].items(): if isinstance(definitions, str): definitions = [definitions] definitions = dict(sorted(d.replace(" ", "").split("=", 1) for d in definitions)) entry_points[entry_point] = definitions if entry_points: metadata["entry_points"] = dict(sorted(entry_points.items())) def parse_setup(path: str) -> dict[str, Any]: import tokenize parsed: dict[str, Any] = {} path = os.path.abspath(path) os.chdir(path) if os.path.exists("setup.cfg"): parsed.update(_parse_setup_cfg("setup.cfg")) setup_path = os.path.join(path, "setup.py") if os.path.exists(setup_path): try: import setuptools except ModuleNotFoundError: raise RuntimeError( "setuptools is required to convert setup.py, install it by `pdm add setuptools`" ) from None setuptools.setup = fake_setup # Execute setup.py and get the kwargs __file__ = sys.argv[0] = setup_path sys.path.insert(0, path) setup_kwargs.clear() with tokenize.open(setup_path) as f: code = f.read() exec( compile(code, __file__, "exec"), {"__name__": "__main__", "__file__": __file__, "setup_kwargs": setup_kwargs}, ) parsed.update(setup_kwargs) if "readme" not in parsed: for readme_file in ("README.md", "README.rst", "README.txt"): readme_path = os.path.join(path, readme_file) if os.path.exists(readme_path): parsed["readme"] = readme_file break clean_metadata(parsed) return parsed def json_default(o): return "" if __name__ == "__main__": import json outfile = sys.argv[2] with open(outfile, "w") as f: json.dump(parse_setup(sys.argv[1]), f, default=json_default) pdm-2.23.1/src/pdm/models/in_process/sysconfig_get_paths.py000066400000000000000000000032421477560627500240150ustar00rootroot00000000000000import json import os import sys import sysconfig def _running_under_venv(): """This handles PEP 405 compliant virtual environments.""" return sys.prefix != getattr(sys, "base_prefix", sys.prefix) def _running_under_regular_virtualenv(): """This handles virtual environments created with pypa's virtualenv.""" # pypa/virtualenv case return hasattr(sys, "real_prefix") def running_under_virtualenv(): """Return True if we're running inside a virtualenv, False otherwise.""" return _running_under_venv() or _running_under_regular_virtualenv() def _get_user_scheme(): if os.name == "nt": return "nt_user" if sys.platform == "darwin" and sys._framework: return "osx_framework_user" return "posix_user" def get_paths(kind="default", vars=None): scheme_names = sysconfig.get_scheme_names() if kind == "user" and not running_under_virtualenv(): scheme = _get_user_scheme() if scheme not in scheme_names: raise ValueError(f"{scheme} is not a valid scheme on the system, or user site may be disabled.") return sysconfig.get_paths(scheme, vars=vars) else: if ( (sys.platform == "darwin" and "osx_framework_library" in scheme_names) or sys.platform == "linux" ) and kind == "prefix": return sysconfig.get_paths("posix_prefix", vars=vars) return sysconfig.get_paths(vars=vars) def main(): vars = None if "_SYSCONFIG_VARS" in os.environ: vars = json.loads(os.environ["_SYSCONFIG_VARS"]) kind = sys.argv[1] if len(sys.argv) > 1 else "default" print(json.dumps(get_paths(kind, vars))) if __name__ == "__main__": main() pdm-2.23.1/src/pdm/models/markers.py000066400000000000000000000141221477560627500172520ustar00rootroot00000000000000from __future__ import annotations import operator from dataclasses import dataclass, replace from functools import lru_cache, reduce from typing import TYPE_CHECKING, Any, cast, overload from dep_logic.markers import ( BaseMarker, InvalidMarker, MarkerExpression, MarkerUnion, MultiMarker, from_pkg_marker, parse_marker, ) from dep_logic.tags import EnvSpec as _EnvSpec from packaging.markers import Marker as PackageMarker from pdm.exceptions import RequirementError from pdm.models.specifiers import PySpecSet if TYPE_CHECKING: from typing import Self PLATFORM_MARKERS = frozenset( {"sys_platform", "platform_release", "platform_system", "platform_version", "os_name", "platform_machine"} ) IMPLEMENTATION_MARKERS = frozenset({"implementation_name", "implementation_version", "platform_python_implementation"}) PYTHON_MARKERS = frozenset({"python_version", "python_full_version"}) def _exclude_multi(marker: Marker, *names: str) -> Marker: inner = marker.inner for name in names: inner = inner.exclude(name) return type(marker)(inner) @dataclass(frozen=True, unsafe_hash=True, repr=False) class Marker: inner: BaseMarker def __and__(self, other: Any) -> Marker: if not isinstance(other, Marker): return NotImplemented return type(self)(self.inner & other.inner) def __or__(self, other: Any) -> Marker: if not isinstance(other, Marker): return NotImplemented return type(self)(self.inner | other.inner) def is_any(self) -> bool: return self.inner.is_any() def is_empty(self) -> bool: return self.inner.is_empty() def __str__(self) -> str: return str(self.inner) def __repr__(self) -> str: return f"" def evaluate(self, environment: dict[str, Any] | None = None) -> bool: return self.inner.evaluate(environment) def matches(self, spec: EnvSpec) -> bool: non_python_marker, python_spec = self.split_pyspec() if spec.platform is None: non_python_marker = _exclude_multi(non_python_marker, *PLATFORM_MARKERS) if spec.implementation is None: non_python_marker = _exclude_multi(non_python_marker, *IMPLEMENTATION_MARKERS) return not (python_spec & spec.requires_python).is_empty() and non_python_marker.evaluate(spec.markers()) @lru_cache(maxsize=1024) def split_pyspec(self) -> tuple[Marker, PySpecSet]: """Split `python_version` and `python_full_version` from marker string""" python_marker = self.inner.only(*PYTHON_MARKERS) if python_marker.is_any(): return self, PySpecSet() new_marker = _exclude_multi(self, *PYTHON_MARKERS) return new_marker, _build_pyspec_from_marker(python_marker) def split_extras(self) -> tuple[Marker, Marker]: """An element can be stripped from the marker only if all parts are connected with `and` operator. The rest part are returned as a string or `None` if all are stripped. """ return type(self)(self.inner.without_extras()), type(self)(self.inner.only("extra")) @overload def get_marker(marker: None) -> None: ... @overload def get_marker(marker: PackageMarker | Marker | str) -> Marker: ... def get_marker(marker: PackageMarker | Marker | str | None) -> Marker | None: if marker is None: return None if isinstance(marker, Marker): return marker elif isinstance(marker, PackageMarker): return Marker(from_pkg_marker(marker)) try: return Marker(parse_marker(marker)) except InvalidMarker as e: raise RequirementError(f"Invalid marker {marker}: {e}") from e def _build_pyspec_from_marker(marker: BaseMarker) -> PySpecSet: def split_version(version: str) -> list[str]: if "," in version: return [v.strip() for v in version.split(",")] return version.split() if isinstance(marker, MarkerExpression): name = marker.name op = marker.op version = marker.value if name == "python_version": if op == ">": int_versions = [int(ver) for ver in version.split(".")] if len(int_versions) < 2: int_versions.append(0) int_versions[-1] += 1 version = ".".join(str(v) for v in int_versions) op = ">=" elif op in ("==", "!="): if len(version.split(".")) < 3: version += ".*" elif op in ("in", "not in"): version = " ".join(v + ".*" for v in split_version(version)) if op == "in": pyspec = reduce(operator.or_, (PySpecSet(f"=={v}") for v in split_version(version))) elif op == "not in": pyspec = reduce(operator.and_, (PySpecSet(f"!={v}") for v in split_version(version))) else: pyspec = PySpecSet(f"{op}{version}") return pyspec elif isinstance(marker, MultiMarker): return reduce(operator.and_, (_build_pyspec_from_marker(m) for m in marker.markers)) elif isinstance(marker, MarkerUnion): return reduce(operator.or_, (_build_pyspec_from_marker(m) for m in marker.markers)) else: # pragma: no cover raise TypeError(f"Unsupported marker type: {type(marker)}") class EnvSpec(_EnvSpec): def replace(self, **kwargs: Any) -> Self: from dep_logic.tags import Implementation, Platform if "requires_python" in kwargs: kwargs["requires_python"] = cast(PySpecSet, kwargs["requires_python"])._logic if "platform" in kwargs: kwargs["platform"] = Platform.parse(kwargs["platform"]) if "implementation" in kwargs: kwargs["implementation"] = Implementation.parse(kwargs["implementation"]) return replace(self, **kwargs) def markers_with_defaults(self) -> dict[str, str]: from packaging.markers import default_environment return {**default_environment(), **self.markers()} # type: ignore[dict-item] def is_allow_all(self) -> bool: return self.platform is None and self.implementation is None pdm-2.23.1/src/pdm/models/project_info.py000066400000000000000000000067031477560627500202750ustar00rootroot00000000000000from __future__ import annotations import itertools from dataclasses import dataclass, field from email.message import Message from typing import TYPE_CHECKING, Any, Iterator, cast if TYPE_CHECKING: from pdm.compat import Distribution DYNAMIC = "DYNAMIC" @dataclass class ProjectInfo: name: str version: str summary: str = "" author: str = "" email: str = "" license: str = "" requires_python: str = "" platform: str = "" keywords: str = "" homepage: str = "" project_urls: list[str] = field(default_factory=list) latest_stable_version: str = "" installed_version: str = "" @classmethod def from_distribution(cls, data: Distribution) -> ProjectInfo: metadata = cast(Message, data.metadata) keywords = metadata.get("Keywords", "").replace(",", ", ") platform = metadata.get("Platform", "").replace(",", ", ") if "Project-URL" in metadata: project_urls = { k.strip(): v.strip() for k, v in (row.split(",") for row in metadata.get_all("Project-URL", [])) } else: project_urls = {} return cls( name=metadata.get("Name", ""), version=metadata.get("Version", ""), summary=metadata.get("Summary", ""), author=metadata.get("Author", ""), email=metadata.get("Author-email", ""), license=metadata.get("License", ""), requires_python=metadata.get("Requires-Python", ""), platform=platform, keywords=keywords, homepage=metadata.get("Home-page", ""), project_urls=[": ".join(parts) for parts in project_urls.items()], ) @classmethod def from_metadata(cls, metadata: dict[str, Any]) -> ProjectInfo: def get_str(key: str) -> str: if key in metadata.get("dynamic", []): return DYNAMIC return metadata.get(key, "") authors = metadata.get("authors", []) author = authors[0]["name"] if authors else "" email = authors[0]["email"] if authors else "" return cls( name=metadata["name"], version=get_str("version"), summary=get_str("description"), author=author, email=email, license=metadata.get("license", {}).get("text", ""), requires_python=get_str("requires-python"), keywords=",".join(get_str("keywords")), project_urls=[": ".join(parts) for parts in metadata.get("urls", {}).items()], ) def generate_rows(self) -> Iterator[tuple[str, str]]: yield "[primary]Name[/]:", self.name yield "[primary]Latest version[/]:", self.version if self.latest_stable_version: yield ("[primary]Latest stable version[/]:", self.latest_stable_version) if self.installed_version: yield ("[primary]Installed version[/]:", self.installed_version) yield "[primary]Summary[/]:", self.summary yield "[primary]Requires Python:", self.requires_python yield "[primary]Author[/]:", self.author yield "[primary]Author email[/]:", self.email yield "[primary]License[/]:", self.license yield "[primary]Homepage[/]:", self.homepage yield from itertools.zip_longest(("[primary]Project URLs[/]:",), self.project_urls, fillvalue="") yield "[primary]Platform[/]:", self.platform yield "[primary]Keywords[/]:", self.keywords pdm-2.23.1/src/pdm/models/python.py000066400000000000000000000044061477560627500171330ustar00rootroot00000000000000from __future__ import annotations import os from functools import cached_property from pathlib import Path from typing import TYPE_CHECKING, Any from packaging.version import InvalidVersion, Version from pdm.models.venv import VirtualEnv if TYPE_CHECKING: from findpython import PythonVersion class PythonInfo: """ A convenient helper class that holds all information of a Python interpreter. """ def __init__(self, py_version: PythonVersion) -> None: self._py_ver = py_version @classmethod def from_path(cls, path: str | Path) -> PythonInfo: from findpython import PythonVersion py_ver = PythonVersion(Path(path)) return cls(py_ver) @cached_property def valid(self) -> bool: return self._py_ver.executable.exists() and self._py_ver.is_valid() def __hash__(self) -> int: return hash(self._py_ver) def __eq__(self, o: Any) -> bool: if not isinstance(o, PythonInfo): return False return self.path == o.path @property def path(self) -> Path: return self._py_ver.executable @property def executable(self) -> Path: return self._py_ver.interpreter @cached_property def version(self) -> Version: return self._py_ver.version @cached_property def implementation(self) -> str: return self._py_ver.implementation.lower() @property def major(self) -> int: return self.version.major @property def minor(self) -> int: return self.version.minor @property def micro(self) -> int: return self.version.micro @property def version_tuple(self) -> tuple[int, ...]: return (self.major, self.minor, self.micro) @property def is_32bit(self) -> bool: return "32bit" in self._py_ver.architecture def for_tag(self) -> str: return f"{self.major}{self.minor}" @property def identifier(self) -> str: try: if os.name == "nt" and self.is_32bit: return f"{self.major}.{self.minor}-32" return f"{self.major}.{self.minor}" except InvalidVersion: return "unknown" def get_venv(self) -> VirtualEnv | None: return VirtualEnv.from_interpreter(self.executable) pdm-2.23.1/src/pdm/models/python_max_versions.json000066400000000000000000000005361477560627500222510ustar00rootroot00000000000000{ "2": 7, "2.0": 1, "2.1": 3, "2.2": 3, "2.3": 7, "2.4": 6, "2.5": 6, "2.6": 9, "2.7": 18, "3": 13, "3.0": 1, "3.1": 5, "3.10": 17, "3.11": 12, "3.12": 10, "3.13": 3, "3.2": 6, "3.3": 7, "3.4": 10, "3.5": 10, "3.6": 15, "3.7": 17, "3.8": 20, "3.9": 22 } pdm-2.23.1/src/pdm/models/reporter.py000066400000000000000000000070411477560627500174520ustar00rootroot00000000000000from __future__ import annotations from dataclasses import dataclass from pathlib import Path from typing import TYPE_CHECKING, Any from rich import get_console from rich.live import Live from rich.progress import MofNCompleteColumn, Progress, SpinnerColumn, TaskProgressColumn, TimeElapsedColumn from pdm import termui if TYPE_CHECKING: from rich.console import Console, ConsoleOptions, RenderResult from rich.progress import Progress, TaskID class CandidateReporter: def report_download(self, link: Any, completed: int, total: int | None) -> None: pass def report_build_start(self, filename: str) -> None: pass def report_build_end(self, filename: str) -> None: pass def report_unpack(self, filename: Path, completed: int, total: int | None) -> None: pass @dataclass class RichProgressReporter(CandidateReporter): progress: Progress task_id: TaskID def report_download(self, link: Any, completed: int, total: int | None) -> None: self.progress.update(self.task_id, completed=completed, total=total, text="Downloading...") def report_unpack(self, filename: Path, completed: int, total: int | None) -> None: self.progress.update(self.task_id, completed=completed, total=total, text="Unpacking...") def report_build_start(self, filename: str) -> None: task = self.progress._tasks[self.task_id] task.total = None task.finished_time = None self.progress.update(self.task_id, text="Building...") def report_build_end(self, filename: str) -> None: self.progress.update(self.task_id, text="") class InstallationStatus: def __init__(self, ui: termui.UI, text: str) -> None: self.ui = ui self.console = get_console() self._spinner = Progress( SpinnerColumn(termui.SPINNER), TimeElapsedColumn(), "{task.description}", MofNCompleteColumn(), console=self.console, ) self._spinner_task = self._spinner.add_task(text, total=None) self.progress = Progress( " ", SpinnerColumn(termui.SPINNER, style="primary"), "{task.description}", "[info]{task.fields[text]}", TaskProgressColumn("[info]{task.percentage:>3.0f}%[/]"), console=self.console, ) self.live = Live(self, console=self.console) def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult: yield self.progress yield "" yield self._spinner def update_spinner( self, *, total: float | None = None, completed: float | None = None, advance: float | None = None, description: str | None = None, ) -> None: if self.ui.verbosity >= termui.Verbosity.DETAIL and description is not None: self.console.print(f" {description}") self._spinner.update( self._spinner_task, total=total, completed=completed, advance=advance, description=description ) self.live.refresh() def start(self) -> None: """Start the progress display.""" if self.ui.verbosity < termui.Verbosity.DETAIL: self.live.start(refresh=True) def stop(self) -> None: """Stop the progress display.""" self.live.stop() if not self.console.is_interactive: self.console.print() def __enter__(self) -> InstallationStatus: self.start() return self def __exit__(self, *args: Any) -> None: self.stop() pdm-2.23.1/src/pdm/models/repositories/000077500000000000000000000000001477560627500177635ustar00rootroot00000000000000pdm-2.23.1/src/pdm/models/repositories/__init__.py000066400000000000000000000005561477560627500221020ustar00rootroot00000000000000from pdm.models.repositories.base import BaseRepository as BaseRepository from pdm.models.repositories.base import CandidateMetadata as CandidateMetadata from pdm.models.repositories.lock import LockedRepository as LockedRepository from pdm.models.repositories.lock import Package as Package from pdm.models.repositories.pypi import PyPIRepository as PyPIRepository pdm-2.23.1/src/pdm/models/repositories/base.py000066400000000000000000000361451477560627500212600ustar00rootroot00000000000000from __future__ import annotations import dataclasses import fnmatch import re import sys import warnings from functools import wraps from typing import TYPE_CHECKING, Generator, NamedTuple, TypeVar, cast from pdm import termui from pdm._types import NotSet, NotSetType from pdm.exceptions import CandidateInfoNotFound, PackageWarning from pdm.models.candidates import Candidate from pdm.models.markers import EnvSpec from pdm.models.requirements import Requirement, parse_line from pdm.models.specifiers import PySpecSet from pdm.utils import deprecation_warning, filtered_sources, normalize_name if TYPE_CHECKING: from typing import Callable, Iterable from unearth import Link from pdm._types import FileHash, RepositoryConfig, SearchResults from pdm.environments import BaseEnvironment T = TypeVar("T", bound="BaseRepository") class CandidateMetadata(NamedTuple): dependencies: list[Requirement] requires_python: str summary: str def cache_result(func: Callable[[T, Candidate], CandidateMetadata]) -> Callable[[T, Candidate], CandidateMetadata]: @wraps(func) def wrapper(self: T, candidate: Candidate) -> CandidateMetadata: result = func(self, candidate) prepared = candidate.prepared info = ([r.as_line() for r in result.dependencies], result.requires_python, result.summary) if prepared and prepared.should_cache(): self._candidate_info_cache.set(candidate, info) return result return wrapper class BaseRepository: """A Repository acts as the source of packages and metadata.""" def __init__( self, sources: list[RepositoryConfig], environment: BaseEnvironment, ignore_compatibility: bool | NotSetType = NotSet, env_spec: EnvSpec | None = None, ) -> None: """ :param sources: a list of sources to download packages from. :param environment: the bound environment instance. :param ignore_compatibility: (DEPRECATED)if True, don't evaluate candidate against the current environment. :param env_spec: the environment specifier to filter the candidates. """ from pdm.resolver.reporters import LockReporter self.sources = sources self.environment = environment self._candidate_info_cache = environment.project.make_candidate_info_cache() self._hash_cache = environment.project.make_hash_cache() self.has_warnings = False if ignore_compatibility is not NotSet: # pragma: no cover deprecation_warning( "The ignore_compatibility argument is deprecated and will be removed in the future. " "Pass in env_set instead. This repository doesn't support lock targets.", stacklevel=2, ) else: ignore_compatibility = True if env_spec is None: # pragma: no cover if ignore_compatibility: env_spec = environment.allow_all_spec else: env_spec = environment.spec self.env_spec = env_spec self.reporter = LockReporter() def get_filtered_sources(self, req: Requirement) -> list[RepositoryConfig]: """Get matching sources based on the index attribute.""" return filtered_sources(self.sources, req.key) def get_dependencies(self, candidate: Candidate) -> tuple[list[Requirement], PySpecSet, str]: """Get (dependencies, python_specifier, summary) of the candidate.""" requires_python, summary = "", "" requirements: list[Requirement] = [] last_ext_info = None for getter in self.dependency_generators(): try: requirements, requires_python, summary = getter(candidate) except CandidateInfoNotFound: last_ext_info = sys.exc_info() continue break else: if last_ext_info is not None: raise last_ext_info[1].with_traceback(last_ext_info[2]) # type: ignore[union-attr] if candidate.req.extras: # XXX: If the requirement has extras, add the original candidate # (without extras) as its dependency. This ensures the same package with # different extras resolve to the same version. self_req = dataclasses.replace( candidate.req.as_pinned_version(candidate.version), extras=None, marker=None, ) requirements.insert(0, self_req) # Store the metadata on the candidate for caching candidate.requires_python = requires_python candidate.summary = summary return requirements, PySpecSet(requires_python), summary def _find_candidates(self, requirement: Requirement, minimal_version: bool) -> Iterable[Candidate]: raise NotImplementedError def is_this_package(self, requirement: Requirement) -> bool: """Whether the requirement is the same as this package""" project = self.environment.project return requirement.is_named and project.is_distribution and requirement.key == normalize_name(project.name) def make_this_candidate(self, requirement: Requirement) -> Candidate: """Make a candidate for this package. In this case the finder will look for a candidate from the package sources """ from unearth import Link project = self.environment.project link = Link.from_path(project.root) candidate = Candidate(requirement, project.name, link=link) with self.reporter.make_candidate_reporter(candidate) as reporter: candidate.prepare(self.environment, reporter=reporter).metadata return candidate def _should_ignore_package_warning(self, requirement: Requirement) -> bool: ignore_settings: list[str] = self.environment.project.pyproject.settings.get("ignore_package_warnings", []) package_name = requirement.key assert package_name is not None for pat in ignore_settings: pat = re.sub(r"[^A-Za-z0-9?*\[\]]+", "-", pat).lower() if fnmatch.fnmatch(package_name, pat): return True return False def find_candidates( self, requirement: Requirement, allow_prereleases: bool | None = None, ignore_requires_python: bool = False, minimal_version: bool = False, ) -> Iterable[Candidate]: """Find candidates of the given NamedRequirement. Let it to be implemented in subclasses. :param requirement: the requirement to find :param allow_prereleases: whether to include pre-releases :param ignore_requires_python: whether to ignore the requires-python marker :param minimal_version: whether to prefer the minimal versions of the package """ # `allow_prereleases` is None means to let the specifier decide whether to # include prereleases from unearth.utils import LazySequence if self.is_this_package(requirement): return [self.make_this_candidate(requirement)] requires_python = requirement.requires_python & self.env_spec.requires_python cans = LazySequence(self._find_candidates(requirement, minimal_version=minimal_version)) applicable_cans = LazySequence( c for c in cans if requirement.specifier.contains(c.version, allow_prereleases) # type: ignore[arg-type, union-attr] ) def filter_candidates_with_requires_python(candidates: Iterable[Candidate]) -> Generator[Candidate]: env_requires_python = PySpecSet(self.env_spec.requires_python) if ignore_requires_python: yield from candidates return def python_specifier(spec: str | PySpecSet) -> str: if isinstance(spec, PySpecSet): spec = str(spec) return "all Python versions" if not spec else f"Python{spec}" for candidate in candidates: if not requires_python.is_subset(candidate.requires_python): if self._should_ignore_package_warning(requirement): continue working_requires_python = env_requires_python & PySpecSet(candidate.requires_python) if working_requires_python.is_empty(): # pragma: no cover continue warnings.warn( f"Skipping {candidate.name}@{candidate.version} because it requires " f"{python_specifier(candidate.requires_python)} but the lock targets to work with " f"{python_specifier(env_requires_python)}. Instead, another version of " f"{candidate.name} that supports {python_specifier(env_requires_python)} will " f"be used.\nIf you want to install {candidate.name}@{candidate.version}, " "narrow down the `requires-python` range to " f'include this version. For example, "{working_requires_python}" should work.', PackageWarning, stacklevel=4, ) self.has_warnings = True else: yield candidate applicable_cans_python_compatible = LazySequence(filter_candidates_with_requires_python(applicable_cans)) # Evaluate data-requires-python attr and discard incompatible candidates # to reduce the number of candidates to resolve. if applicable_cans_python_compatible: applicable_cans = applicable_cans_python_compatible if not applicable_cans: termui.logger.debug("\tCould not find any matching candidates.") if not applicable_cans and allow_prereleases is None: # No non-pre-releases is found, force pre-releases now applicable_cans = LazySequence( c for c in cans if requirement.specifier.contains(c.version, True) # type: ignore[arg-type, union-attr] ) applicable_cans_python_compatible = LazySequence(filter_candidates_with_requires_python(applicable_cans)) if applicable_cans_python_compatible: applicable_cans = applicable_cans_python_compatible if not applicable_cans: termui.logger.debug( "\tCould not find any matching candidates even when considering pre-releases.", ) def log_candidates(title: str, candidates: Iterable[Candidate], max_lines: int = 10) -> None: termui.logger.debug("\t" + title) logged_lines = set() for can in candidates: new_line = f"\t {can!r}" if new_line not in logged_lines: logged_lines.add(new_line) if len(logged_lines) > max_lines: termui.logger.debug("\t ... [more]") break else: termui.logger.debug(new_line) if self.environment.project.core.ui.verbosity >= termui.Verbosity.DEBUG: if applicable_cans: log_candidates("Found matching candidates:", applicable_cans) elif cans: log_candidates("Found but non-matching candidates:", cans) return applicable_cans def _get_dependencies_from_cache(self, candidate: Candidate) -> CandidateMetadata: try: info = self._candidate_info_cache.get(candidate) except KeyError: raise CandidateInfoNotFound(candidate) from None deps: list[Requirement] = [] for line in info[0]: deps.append(parse_line(line)) termui.logger.debug("Using cached metadata for %s", candidate) return CandidateMetadata(deps, info[1], info[2]) @cache_result def _get_dependencies_from_metadata(self, candidate: Candidate) -> CandidateMetadata: with self.reporter.make_candidate_reporter(candidate) as reporter: prepared = candidate.prepare(self.environment, reporter=reporter) deps = prepared.get_dependencies_from_metadata() requires_python = candidate.requires_python summary = prepared.metadata.metadata.get("Summary", "") return CandidateMetadata(deps, requires_python, summary) def get_hashes(self, candidate: Candidate) -> list[FileHash]: """Get hashes of all possible installable candidates of a given package version. """ if ( candidate.req.is_vcs or (candidate.req.is_file_or_url and candidate.req.is_local_dir) # type: ignore[attr-defined] ): return [] if candidate.hashes: return candidate.hashes req = candidate.req.as_pinned_version(candidate.version) comes_from = candidate.link.comes_from if candidate.link else None result: list[FileHash] = [] logged = False respect_source_order = self.environment.project.pyproject.settings.get("resolution", {}).get( "respect-source-order", False ) sources = self.get_filtered_sources(candidate.req) if req.is_named and respect_source_order and comes_from: sources = [s for s in sources if comes_from.startswith(cast(str, s.url))] if req.is_file_or_url: this_link = cast("Link", candidate.prepare(self.environment).link) links: list[Link] = [this_link] else: # the req must be a named requirement with self.environment.get_finder(sources, env_spec=self.env_spec) as finder: links = [package.link for package in finder.find_matches(req.as_line())] for link in links: if not link or link.is_vcs or (link.is_file and link.file_path.is_dir()): # The links found can still be a local directory or vcs, skipping it. continue if not logged: termui.logger.info("Fetching hashes for %s", candidate) logged = True result.append( { "url": link.url_without_fragment, "file": link.filename, "hash": self._hash_cache.get_hash(link, self.environment.session), } ) return result def dependency_generators(self) -> Iterable[Callable[[Candidate], CandidateMetadata]]: """Return an iterable of getter functions to get dependencies, which will be called one by one. """ raise NotImplementedError def search(self, query: str) -> SearchResults: """Search package by name or summary. :param query: query string :returns: search result, a dictionary of name: package metadata """ raise NotImplementedError def fetch_hashes(self, candidates: Iterable[Candidate]) -> None: """Fetch hashes for candidates in parallel""" from concurrent.futures import ThreadPoolExecutor def do_fetch(candidate: Candidate) -> None: candidate.hashes = self.get_hashes(candidate) with ThreadPoolExecutor() as executor: executor.map(do_fetch, candidates) pdm-2.23.1/src/pdm/models/repositories/lock.py000066400000000000000000000274411477560627500212750ustar00rootroot00000000000000from __future__ import annotations import dataclasses import posixpath from functools import cached_property from pathlib import Path from typing import TYPE_CHECKING, Collection, NamedTuple, cast from pdm.exceptions import CandidateNotFound, PdmException from pdm.models.candidates import Candidate from pdm.models.markers import EnvSpec from pdm.models.repositories.base import BaseRepository, CandidateMetadata from pdm.models.requirements import FileRequirement, Requirement, parse_line from pdm.utils import cd, url_to_path, url_without_fragments if TYPE_CHECKING: from typing import Any, Callable, Iterable, Mapping from tomlkit.toml_document import TOMLDocument from pdm._types import FileHash, RepositoryConfig from pdm.environments import BaseEnvironment CandidateKey = tuple[str, str | None, str | None, bool] class Package(NamedTuple): candidate: Candidate dependencies: list[str] summary: str class LockedRepository(BaseRepository): def __init__( self, lockfile: Mapping[str, Any], sources: list[RepositoryConfig], environment: BaseEnvironment, env_spec: EnvSpec | None = None, ) -> None: super().__init__(sources, environment, env_spec=env_spec or environment.spec) self.packages: dict[CandidateKey, Package] = {} self.targets: list[EnvSpec] = [] self._read_lockfile(lockfile) def add_package(self, package: Package) -> None: self.packages[self._identify_candidate(package.candidate)] = package @cached_property def all_candidates(self) -> dict[str, list[Candidate]]: """Return a dict of all candidates grouped by the package name.""" result: dict[str, list[Candidate]] = {} for entry in self.packages.values(): result.setdefault(entry.candidate.identify(), []).append(entry.candidate) return result @property def candidates(self) -> dict[str, Candidate]: """Return a dict of candidates for the current environment.""" result: dict[str, Candidate] = {} for candidates in self.all_candidates.values(): for can in candidates: if can.req.marker and not can.req.marker.matches(self.env_spec): continue result[can.identify()] = can return result def _read_lockfile(self, lockfile: Mapping[str, Any]) -> None: from pdm.project.lockfile import FLAG_CROSS_PLATFORM, FLAG_STATIC_URLS root = self.environment.project.root static_urls = FLAG_STATIC_URLS in self.environment.project.lockfile.strategy self.targets = [EnvSpec.from_spec(**t) for t in lockfile.get("metadata", {}).get("targets", [])] if not self.targets and lockfile: # pragma: no cover # XXX: for reading old lockfiles, to be removed in the future if FLAG_CROSS_PLATFORM in self.environment.project.lockfile.strategy: self.targets.append(self.environment.allow_all_spec) else: self.targets.append(self.environment.spec) with cd(root): for package in lockfile.get("package", []): version = package.get("version") if version: package["version"] = f"=={version}" package_name = package.pop("name") req_dict = { k: v for k, v in package.items() if k not in ("dependencies", "requires_python", "summary", "files", "targets") } req = Requirement.from_req_dict(package_name, req_dict) if req.is_file_or_url and req.path and not req.url: # type: ignore[attr-defined] req.url = root.joinpath(req.path).as_uri() # type: ignore[attr-defined] can = Candidate(req, name=package_name, version=version) can.hashes = package.get("files", []) if not static_urls and any("url" in f for f in can.hashes): raise PdmException( "Static URLs are not allowed in lockfile unless enabled by `pdm lock --static-urls`." ) can_id = self._identify_candidate(can) can.requires_python = package.get("requires_python", "") entry = Package( can, package.get("dependencies", []), package.get("summary", ""), ) self.packages[can_id] = entry def _identify_candidate(self, candidate: Candidate) -> CandidateKey: url: str | None = None if not candidate.req.is_named and candidate.link is not None: url = candidate.link.url_without_fragment url = self.environment.project.backend.expand_line(cast(str, url)) if url.startswith("file://"): path = posixpath.normpath(url_to_path(url)) url = Path(path).as_uri() return ( candidate.identify(), candidate.version if not url else None, url, candidate.req.editable, ) def _get_dependencies_from_lockfile(self, candidate: Candidate) -> CandidateMetadata: err = ( f"Missing package {candidate.identify()} from the lockfile, " "the lockfile may be broken. Run `pdm lock --update-reuse` to fix it." ) try: entry = self.packages[self._identify_candidate(candidate)] except KeyError as e: # pragma: no cover raise CandidateNotFound(err) from e deps: list[Requirement] = [] for line in entry.dependencies: deps.append(parse_line(line)) return CandidateMetadata(deps, candidate.requires_python, entry.summary) def dependency_generators(self) -> Iterable[Callable[[Candidate], CandidateMetadata]]: return (self._get_dependencies_from_lockfile,) def _matching_entries(self, requirement: Requirement) -> Iterable[Package]: for key, entry in self.packages.items(): can_req = entry.candidate.req if requirement.name: if key[0] != requirement.identify(): continue else: assert isinstance(requirement, FileRequirement) if not isinstance(can_req, FileRequirement): continue if requirement.path and can_req.path: if requirement.path != can_req.path: continue elif key[2] is not None and key[2] != url_without_fragments(requirement.url): continue yield entry def find_candidates( self, requirement: Requirement, allow_prereleases: bool | None = None, ignore_requires_python: bool = False, minimal_version: bool = False, ) -> Iterable[Candidate]: if self.is_this_package(requirement): candidate = self.make_this_candidate(requirement) if candidate is not None: yield candidate return for entry in self._matching_entries(requirement): can = entry.candidate.copy_with(requirement) if not requirement.name: # make sure can.identify() won't return a randomly-generated name requirement.name = can.name yield can def get_hashes(self, candidate: Candidate) -> list[FileHash]: return candidate.hashes def evaluate_candidates(self, groups: Collection[str]) -> Iterable[Package]: for package in self.packages.values(): can = package.candidate if can.req.marker and not can.req.marker.matches(self.env_spec): continue if not any(g in can.req.groups for g in groups): continue yield package def merge_result(self, env_spec: EnvSpec, result: Iterable[Package]) -> None: if env_spec not in self.targets: self.targets.append(env_spec) for entry in result: key = self._identify_candidate(entry.candidate) existing = self.packages.get(key) if existing is None: self.packages[key] = entry else: # merge markers old_marker = existing.candidate.req.marker if old_marker is None or entry.candidate.req.marker is None: new_marker = None else: new_marker = old_marker | entry.candidate.req.marker bare_marker, py_spec = new_marker.split_pyspec() if py_spec.is_superset(self.environment.python_requires): new_marker = bare_marker if new_marker.is_any(): new_marker = None # merge groups new_groups = list(set(existing.candidate.req.groups) | set(entry.candidate.req.groups)) existing.candidate.req = dataclasses.replace( existing.candidate.req, marker=new_marker, groups=new_groups ) # merge file hashes for file in entry.candidate.hashes: if file not in existing.candidate.hashes: existing.candidate.hashes.append(file) # clear caches if "all_candidates" in self.__dict__: del self.__dict__["all_candidates"] def format_lockfile(self, groups: Iterable[str] | None, strategy: set[str]) -> TOMLDocument: """Format lock file from a dict of resolved candidates, a mapping of dependencies and a collection of package summaries. """ import tomlkit from pdm.formats.base import make_array, make_inline_table from pdm.project.lockfile import FLAG_CROSS_PLATFORM, FLAG_INHERIT_METADATA, FLAG_STATIC_URLS def _group_sort_key(group: str) -> tuple[bool, str]: return group != "default", group project = self.environment.project packages = tomlkit.aot() for entry in sorted(self.packages.values(), key=lambda x: x.candidate.identify()): base = tomlkit.table() base.update(entry.candidate.as_lockfile_entry(project.root)) base.add("summary", entry.summary or "") if FLAG_INHERIT_METADATA in strategy: base.add("groups", sorted(entry.candidate.req.groups, key=_group_sort_key)) if entry.candidate.req.marker is not None: base.add("marker", str(entry.candidate.req.marker)) if len(entry.dependencies) > 0: base.add("dependencies", make_array(sorted(entry.dependencies), True)) if hashes := entry.candidate.hashes: collected = {} for item in hashes: if FLAG_STATIC_URLS in strategy: row = {"url": item["url"], "hash": item["hash"]} else: row = {"file": item["file"], "hash": item["hash"]} inline = make_inline_table(row) # deduplicate and sort collected[tuple(row.values())] = inline if collected: base.add("files", make_array([collected[k] for k in sorted(collected)], True)) packages.append(base) doc = tomlkit.document() metadata = tomlkit.table() if groups is None: groups = list(project.iter_groups()) metadata.update( { "groups": sorted(groups, key=_group_sort_key), "strategy": sorted(strategy), "targets": [t.as_dict() for t in self.targets], } ) metadata.pop(FLAG_STATIC_URLS, None) metadata.pop(FLAG_CROSS_PLATFORM, None) doc.add("metadata", metadata) doc.add("package", packages) return doc pdm-2.23.1/src/pdm/models/repositories/pypi.py000066400000000000000000000100741477560627500213200ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING, cast from pdm.exceptions import CandidateInfoNotFound, CandidateNotFound from pdm.models.candidates import Candidate from pdm.models.repositories.base import BaseRepository, CandidateMetadata, cache_result from pdm.models.requirements import Requirement, filter_requirements_with_extras from pdm.models.search import SearchResultParser if TYPE_CHECKING: from typing import Callable, Iterable from pdm._types import SearchResults class PyPIRepository(BaseRepository): """Get package and metadata from PyPI source.""" DEFAULT_INDEX_URL = "https://pypi.org" @cache_result def _get_dependencies_from_json(self, candidate: Candidate) -> CandidateMetadata: # pragma: no cover if not candidate.name or not candidate.version: # Only look for json api for named requirements. raise CandidateInfoNotFound(candidate) sources = self.get_filtered_sources(candidate.req) url_prefixes = [ proc_url[:-7] # Strip "/simple". for proc_url in (raw_url.rstrip("/") for raw_url in (source.url for source in sources) if raw_url) if proc_url.endswith("/simple") ] session = self.environment.session for prefix in url_prefixes: json_url = f"{prefix}/pypi/{candidate.name}/{candidate.version}/json" resp = session.get(json_url) if resp.is_error: continue info = resp.json()["info"] requires_python = info["requires_python"] or "" summary = info["summary"] or "" try: requirement_lines = info["requires_dist"] or [] except KeyError: requirement_lines = info["requires"] or [] requirements = filter_requirements_with_extras(requirement_lines, candidate.req.extras or ()) return CandidateMetadata(requirements, requires_python, summary) raise CandidateInfoNotFound(candidate) def dependency_generators(self) -> Iterable[Callable[[Candidate], CandidateMetadata]]: yield self._get_dependencies_from_cache if self.environment.project.config["pypi.json_api"]: yield self._get_dependencies_from_json yield self._get_dependencies_from_metadata def _find_candidates(self, requirement: Requirement, minimal_version: bool) -> Iterable[Candidate]: from unearth.utils import LazySequence sources = self.get_filtered_sources(requirement) req_name = cast(str, requirement.project_name) with self.environment.get_finder(sources, env_spec=self.env_spec, minimal_version=minimal_version) as finder: cans = LazySequence( Candidate.from_installation_candidate(c, requirement) for c in finder.find_all_packages(req_name, allow_yanked=requirement.is_pinned) ) if not cans: raise CandidateNotFound( f"Unable to find candidates for {req_name}. There may " "exist some issues with the package name or network condition." ) return cans def search(self, query: str) -> SearchResults: pypi_simple = self.sources[0].url.rstrip("/") # type: ignore[union-attr] if pypi_simple.endswith("/simple"): search_url = pypi_simple[:-6] + "search" else: search_url = pypi_simple + "/search" session = self.environment.session resp = session.get(search_url, params={"q": query}) if resp.status_code == 404: # pragma: no cover self.environment.project.core.ui.warn( f"{pypi_simple!r} doesn't support '/search' endpoint, fallback " f"to {self.DEFAULT_INDEX_URL!r} now.\n" "This may take longer depending on your network condition.", ) resp = session.get(f"{self.DEFAULT_INDEX_URL}/search/", params={"q": query}, follow_redirects=True) parser = SearchResultParser() resp.raise_for_status() parser.feed(resp.text) return parser.results pdm-2.23.1/src/pdm/models/requirements.py000066400000000000000000000464301477560627500203400ustar00rootroot00000000000000from __future__ import annotations import dataclasses import functools import inspect import json import os import posixpath import re import secrets import urllib.parse as urlparse from pathlib import Path from typing import TYPE_CHECKING, Any, Sequence, TypeVar, cast from packaging.requirements import InvalidRequirement from packaging.requirements import Requirement as PackageRequirement from packaging.specifiers import InvalidSpecifier, SpecifierSet from packaging.utils import parse_sdist_filename, parse_wheel_filename from pdm.compat import Distribution from pdm.exceptions import RequirementError from pdm.models.backends import BuildBackend, get_relative_path from pdm.models.markers import Marker, get_marker from pdm.models.setup import Setup from pdm.models.specifiers import PySpecSet, fix_legacy_specifier, get_specifier from pdm.utils import ( PACKAGING_22, add_ssh_scheme_to_git_uri, comparable_version, normalize_name, split_path_fragments, url_to_path, url_without_fragments, ) if TYPE_CHECKING: from unearth import Link from pdm._types import RequirementDict VCS_SCHEMA = ("git", "hg", "svn", "bzr") _vcs_req_re = re.compile( rf"(?P(?P{'|'.join(VCS_SCHEMA)})\+[^\s;]+)(?P[\t ]*;[^\n]+)?", flags=re.IGNORECASE, ) _file_req_re = re.compile( r"(?:(?P\S+://[^\s\[\];]+)|" r"(?P(?:[^\s;\[\]]|\\ )*" r"|'(?:[^']|\\')*'" r"|\"(?:[^\"]|\\\")*\"))" r"(?P\[[^\[\]]+\])?(?P[\t ]*;[^\n]+)?" ) _egg_info_re = re.compile(r"([a-z0-9_.]+)-([a-z0-9_.!+-]+)", re.IGNORECASE) T = TypeVar("T", bound="Requirement") ALLOW_ANY = SpecifierSet() def strip_extras(line: str) -> tuple[str, tuple[str, ...] | None]: match = re.match(r"^(.+?)(?:\[([^\]]+)\])?$", line) assert match is not None name, extras_str = match.groups() extras = tuple({e.strip() for e in extras_str.split(",")}) if extras_str else None return name, extras @functools.lru_cache(maxsize=None) def _get_random_key(req: Requirement) -> str: return f":empty:{secrets.token_urlsafe(8)}" @dataclasses.dataclass(eq=False) class Requirement: """Base class of a package requirement. A requirement is a (virtual) specification of a package which contains some constraints of version, python version, or other marker. """ name: str | None = None marker: Marker | None = None extras: Sequence[str] | None = None specifier: SpecifierSet = ALLOW_ANY editable: bool = False prerelease: bool | None = None groups: list[str] = dataclasses.field(default_factory=list) def __post_init__(self) -> None: self.requires_python = self.marker.split_pyspec()[1] if self.marker else PySpecSet() @property def project_name(self) -> str | None: return normalize_name(self.name, lowercase=False) if self.name else None @property def key(self) -> str | None: return self.project_name.lower() if self.project_name else None @property def is_pinned(self) -> bool: if len(self.specifier) != 1: return False sp = next(iter(self.specifier)) return sp.operator == "===" or (sp.operator == "==" and "*" not in sp.version) def as_pinned_version(self: T, other_version: str | None) -> T: """Return a new requirement with the given pinned version.""" if self.is_pinned or not other_version: return self normalized = comparable_version(other_version) return dataclasses.replace(self, specifier=get_specifier(f"=={normalized}")) def _hash_key(self) -> tuple: return ( self.key, frozenset(self.extras) if self.extras else None, str(self.marker) if self.marker else None, ) def __hash__(self) -> int: return hash(self._hash_key()) def __eq__(self, o: object) -> bool: return isinstance(o, Requirement) and self._hash_key() == o._hash_key() def identify(self) -> str: if not self.key: return _get_random_key(self) extras = "[{}]".format(",".join(sorted(self.extras))) if self.extras else "" return self.key + extras def __repr__(self) -> str: return f"<{self.__class__.__name__} {self.as_line()}>" def __str__(self) -> str: return self.as_line() @classmethod def create(cls: type[T], **kwargs: Any) -> T: if "marker" in kwargs: kwargs["marker"] = get_marker(kwargs["marker"]) if "extras" in kwargs and isinstance(kwargs["extras"], str): kwargs["extras"] = tuple(e.strip() for e in kwargs["extras"][1:-1].split(",")) version = kwargs.pop("version", "") try: kwargs["specifier"] = get_specifier(version) except InvalidSpecifier as e: raise RequirementError(f"Invalid specifier for {kwargs.get('name')}: {version}: {e}") from None return cls(**{k: v for k, v in kwargs.items() if k in inspect.signature(cls).parameters}) @classmethod def from_dist(cls, dist: Distribution) -> Requirement: direct_url_json = dist.read_text("direct_url.json") if direct_url_json is not None: direct_url = json.loads(direct_url_json) data = { "name": dist.metadata.get("Name"), "url": direct_url.get("url"), "editable": direct_url.get("dir_info", {}).get("editable"), "subdirectory": direct_url.get("subdirectory"), } if "vcs_info" in direct_url: vcs_info = direct_url["vcs_info"] data.update( url=f"{vcs_info['vcs']}+{direct_url['url']}", ref=vcs_info.get("requested_revision"), revision=vcs_info.get("commit_id"), ) return VcsRequirement.create(**data) return FileRequirement.create(**data) return NamedRequirement.create(name=dist.metadata["Name"], version=f"=={dist.version}") @classmethod def from_req_dict(cls, name: str, req_dict: RequirementDict) -> Requirement: if isinstance(req_dict, str): # Version specifier only. return NamedRequirement.create(name=name, version=req_dict) for vcs in VCS_SCHEMA: if vcs in req_dict: repo = cast(str, req_dict.pop(vcs, None)) url = f"{vcs}+{repo}" return VcsRequirement.create(name=name, vcs=vcs, url=url, **req_dict) if "path" in req_dict or "url" in req_dict: return FileRequirement.create(name=name, **req_dict) return NamedRequirement.create(name=name, **req_dict) @property def is_named(self) -> bool: return isinstance(self, NamedRequirement) @property def is_vcs(self) -> bool: return isinstance(self, VcsRequirement) @property def is_file_or_url(self) -> bool: return type(self) is FileRequirement def as_line(self) -> str: raise NotImplementedError def matches(self, line: str) -> bool: """Return whether the passed in PEP 508 string is the same requirement as this one. """ if not isinstance(line, str): return False req = parse_line(line) return self.key == req.key or ( isinstance(self, FileRequirement) and isinstance(req, FileRequirement) and self.url == req.url ) @classmethod def from_pkg_requirement(cls, req: PackageRequirement) -> Requirement: from unearth import Link kwargs = { "name": req.name, "extras": req.extras, "specifier": req.specifier, "marker": get_marker(req.marker), } if getattr(req, "url", None): link = Link(cast(str, req.url)) klass = VcsRequirement if link.is_vcs else FileRequirement return klass(url=cast(str, req.url), **kwargs) # type: ignore[arg-type] else: return NamedRequirement(**kwargs) # type: ignore[arg-type] def _format_marker(self) -> str: if self.marker: return f"; {self.marker!s}" return "" @dataclasses.dataclass(eq=False) class NamedRequirement(Requirement): def as_line(self) -> str: extras = f"[{','.join(sorted(self.extras))}]" if self.extras else "" return f"{self.project_name}{extras}{self.specifier or ''}{self._format_marker()}" @dataclasses.dataclass(eq=False) class FileRequirement(Requirement): url: str = "" path: Path | None = None subdirectory: str | None = None _root: Path = dataclasses.field(default_factory=Path.cwd, repr=False) def __post_init__(self) -> None: super().__post_init__() self._parse_url() def _hash_key(self) -> tuple: return (*super()._hash_key(), self.get_full_url(), self.editable) def guess_name(self) -> str | None: filename = os.path.basename(urlparse.unquote(url_without_fragments(self.url))).rsplit("@", 1)[0] if self.is_vcs: if self.vcs == "git": # type: ignore[attr-defined] name = filename if name.endswith(".git"): name = name[:-4] return name elif self.vcs == "hg": # type: ignore[attr-defined] return filename else: # svn and bzr name, in_branch, _ = filename.rpartition("/branches/") if not in_branch and name.endswith("/trunk"): return name[:-6] return name elif filename.endswith(".whl"): return parse_wheel_filename(filename)[0] else: try: return parse_sdist_filename(filename)[0] except ValueError: match = _egg_info_re.match(filename) # Filename is like `-.tar.gz`, where name will be # extracted and version will be left to be determined from # the metadata. if match: return match.group(1) return None @classmethod def create(cls: type[T], **kwargs: Any) -> T: if kwargs.get("path"): kwargs["path"] = Path(kwargs["path"]) return super().create(**kwargs) @property def str_path(self) -> str | None: if not self.path: return None if self.path.is_absolute(): try: result = self.path.relative_to(self._root).as_posix() except ValueError: return self.path.as_posix() else: result = self.path.as_posix() result = posixpath.normpath(result) if not result.startswith(("./", "../")): result = "./" + result if result.startswith("./../"): result = result[2:] return result def _parse_url(self) -> None: if self.path: path, fragments = split_path_fragments(self.path) if not self.url and path.is_absolute(): self.url = path.as_uri() + fragments self.path = path else: url = url_without_fragments(self.url) relpath = get_relative_path(url) if relpath is None: try: self.path = Path(url_to_path(url)) except ValueError: pass else: self.path = Path(relpath) if self.path is not None: # For relative path, we don't resolve URL now, so the path may still contain fragments, # it will be handled in `relocate()` method. result = Setup.from_directory(self.absolute_path) # type: ignore[arg-type] if result.name: self.name = result.name if self.url: self._parse_name_from_url() def relocate(self, backend: BuildBackend) -> None: """Change the project root to the given path""" if self.path is None or self.path.is_absolute(): return # self.path is relative path, fragments = split_path_fragments(self.path) self.path = Path(os.path.relpath(path, backend.root)) relpath = self.path.as_posix() if relpath == ".": relpath = "" self.url = backend.relative_path_to_url(relpath) + fragments self._root = backend.root @property def absolute_path(self) -> Path | None: return self._root.joinpath(self.path) if self.path else None @property def is_local(self) -> bool: return (path := self.absolute_path) is not None and path.exists() @property def is_local_dir(self) -> bool: return self.is_local and cast(Path, self.absolute_path).is_dir() def as_file_link(self) -> Link: from unearth import Link url = self.get_full_url() # only subdirectory is useful in a file link if self.subdirectory: url += f"#subdirectory={self.subdirectory}" return Link(url) def get_full_url(self) -> str: return url_without_fragments(self.url) def as_line(self) -> str: project_name = f"{self.project_name}" if self.project_name else "" extras = f"[{','.join(sorted(self.extras))}]" if self.extras and self.project_name else "" marker = self._format_marker() if marker: marker = f" {marker}" url = self.get_full_url() fragments = [] if self.subdirectory: fragments.append(f"subdirectory={self.subdirectory}") if self.editable: if project_name: fragments.insert(0, f"egg={project_name}{extras}") fragment_str = ("#" + "&".join(fragments)) if fragments else "" return f"-e {url}{fragment_str}{marker}" delimiter = " @ " if project_name else "" fragment_str = ("#" + "&".join(fragments)) if fragments else "" return f"{project_name}{extras}{delimiter}{url}{fragment_str}{marker}" def _parse_name_from_url(self) -> None: parsed = urlparse.urlparse(self.url) fragments = dict(urlparse.parse_qsl(parsed.fragment)) if "egg" in fragments: egg_info = urlparse.unquote(fragments["egg"]) name, extras = strip_extras(egg_info) self.name = name if not self.extras: self.extras = extras if not self.name and not self.is_vcs: self.name = self.guess_name() def check_installable(self) -> None: if path := self.absolute_path: if not path.exists(): raise RequirementError(f"The local path '{self.path}' does not exist.") if path.is_dir(): if not path.joinpath("setup.py").exists() and not path.joinpath("pyproject.toml").exists(): raise RequirementError(f"The local path '{self.path}' is not installable.") elif self.editable: raise RequirementError("Local file requirement must not be editable.") @dataclasses.dataclass(eq=False) class VcsRequirement(FileRequirement): vcs: str = "" ref: str | None = None revision: str | None = None def __post_init__(self) -> None: super().__post_init__() if not self.vcs: self.vcs = self.url.split("+", 1)[0] def get_full_url(self) -> str: url = super().get_full_url() if self.revision and not self.editable: url += f"@{self.revision}" elif self.ref: url += f"@{self.ref}" return url def _parse_url(self) -> None: vcs, url_no_vcs = self.url.split("+", 1) if url_no_vcs.startswith("git@"): url_no_vcs = add_ssh_scheme_to_git_uri(url_no_vcs) if not self.name: self._parse_name_from_url() ref = self.ref parsed = urlparse.urlparse(url_no_vcs) path = parsed.path fragments = dict(urlparse.parse_qsl(parsed.fragment)) if "subdirectory" in fragments: self.subdirectory = fragments["subdirectory"] if "@" in parsed.path: path, ref = parsed.path.split("@", 1) repo = urlparse.urlunparse(parsed._replace(path=path, fragment="")) self.url = f"{vcs}+{repo}" self.repo, self.ref = repo, ref def filter_requirements_with_extras( requirement_lines: list[str], extras: Sequence[str], include_default: bool = False ) -> list[Requirement]: """Filter the requirements with extras. If extras are given, return those with matching extra markers. Otherwise, return those without extra markers. """ result: list[Requirement] = [] for req in requirement_lines: _r = parse_requirement(req) req_extras = get_marker("") if _r.marker: rest, req_extras = _r.marker.split_extras() _r.marker = rest if not rest.is_any() else None if not req_extras.evaluate({"extra": extras or ""}): continue # Add to the requirements if: # The requirement has no extras while requested extras are empty or include_default is True, or # The requirement has extras, in which case the `evaluate()` test must have been passed. if not req_extras.is_any() or include_default or not extras: result.append(_r) return result def parse_as_pkg_requirement(line: str) -> PackageRequirement: """Parse a requirement line as packaging.requirement.Requirement""" try: return PackageRequirement(line) except InvalidRequirement: if not PACKAGING_22: # We can't do anything, reraise the error. raise new_line = fix_legacy_specifier(line) return PackageRequirement(new_line) def parse_line(line: str) -> Requirement: if line.startswith("-e "): return parse_requirement(line[3:].strip(), editable=True) return parse_requirement(line) def parse_requirement(line: str, editable: bool = False) -> Requirement: m = _vcs_req_re.match(line) r: Requirement if m is not None: r = VcsRequirement.create(**m.groupdict()) else: # Special handling for hatch local references: # https://hatch.pypa.io/latest/config/dependency/#local # We replace the {root.uri} temporarily with a dummy URL header # to make it pass through the packaging.requirement parser # and then revert it. root_url = Path().absolute().as_uri() replaced = "{root:uri}" in line if replaced: line = line.replace("{root:uri}", root_url) try: pkg_req = parse_as_pkg_requirement(line) except InvalidRequirement as e: m = _file_req_re.match(line) if m is None: raise RequirementError(f"{line}: {e}") from None args = m.groupdict() if not line.startswith(".") and not args["url"] and args["path"] and not os.path.exists(args["path"]): raise RequirementError(f"{line}: {e}") from None r = FileRequirement.create(**args) else: r = Requirement.from_pkg_requirement(pkg_req) if replaced: assert isinstance(r, FileRequirement) r.url = r.url.replace(root_url, "{root:uri}") r.path = Path(get_relative_path(r.url) or "") if editable: if isinstance(r, FileRequirement) and (r.is_vcs or not r.url or r.url.startswith("file://")): r.editable = True else: raise RequirementError(f"{line}: Editable requirement is only supported for VCS link or local directory.") return r pdm-2.23.1/src/pdm/models/search.py000066400000000000000000000044011477560627500170520ustar00rootroot00000000000000from __future__ import annotations import functools from dataclasses import dataclass from html.parser import HTMLParser from typing import Callable from pdm._types import SearchResult @dataclass class Result: name: str = "" version: str = "" description: str = "" def as_frozen(self) -> SearchResult: return SearchResult(self.name, self.version, self.description) class SearchResultParser(HTMLParser): """A simple HTML parser for pypi.org search results.""" def __init__(self) -> None: super().__init__() self.results: list[SearchResult] = [] self._current: Result | None = None self._nest_anchors = 0 self._data_callback: Callable[[str], None] | None = None @staticmethod def _match_class(attrs: list[tuple[str, str | None]], name: str) -> bool: attrs_map = dict(attrs) return name in (attrs_map.get("class") or "").split() def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None: if not self._current: if tag == "a" and self._match_class(attrs, "package-snippet"): self._current = Result() self._nest_anchors = 1 else: if tag == "span" and self._match_class(attrs, "package-snippet__name"): self._data_callback = functools.partial(setattr, self._current, "name") elif tag == "span" and self._match_class(attrs, "package-snippet__version"): self._data_callback = functools.partial(setattr, self._current, "version") elif tag == "p" and self._match_class(attrs, "package-snippet__description"): self._data_callback = functools.partial(setattr, self._current, "description") elif tag == "a": self._nest_anchors += 1 def handle_data(self, data: str) -> None: if self._data_callback is not None: self._data_callback(data) self._data_callback = None def handle_endtag(self, tag: str) -> None: if tag != "a" or self._current is None: return self._nest_anchors -= 1 if self._nest_anchors == 0: if self._current.name: self.results.append(self._current.as_frozen()) self._current = None pdm-2.23.1/src/pdm/models/session.py000066400000000000000000000156431477560627500173020ustar00rootroot00000000000000from __future__ import annotations import os import sys from functools import lru_cache from pathlib import Path from typing import TYPE_CHECKING, Any, cast import hishel import httpx import msgpack from hishel._serializers import Metadata from httpcore import Request, Response from unearth.fetchers import PyPIClient from pdm.__version__ import __version__ from pdm.termui import logger if TYPE_CHECKING: from ssl import SSLContext from pdm._types import RepositoryConfig def _create_truststore_ssl_context() -> SSLContext | None: if sys.version_info < (3, 10): return None try: import ssl except ImportError: return None try: import truststore except ImportError: return None import certifi ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ctx.load_verify_locations(certifi.where()) return ctx _ssl_context = _create_truststore_ssl_context() CACHES_TTL = 7 * 24 * 60 * 60 # 7 days MAX_RETRIES = 4 @lru_cache(maxsize=None) def _get_transport( verify: bool | SSLContext | str = True, cert: tuple[str, str | None] | None = None, proxy: httpx.Proxy | None = None, ) -> httpx.BaseTransport: return httpx.HTTPTransport(verify=verify, cert=cert, trust_env=True, proxy=proxy, retries=MAX_RETRIES) class MsgPackSerializer(hishel.BaseSerializer): KNOWN_REQUEST_EXTENSIONS = ("timeout", "sni_hostname") KNOWN_RESPONSE_EXTENSIONS = ("http_version", "reason_phrase") DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" def dumps(self, response: Response, request: Request, metadata: Metadata) -> bytes: from hishel._utils import normalized_url response_dict = { "status": response.status, "headers": response.headers, "content": response.content, "extensions": { key: value for key, value in response.extensions.items() if key in self.KNOWN_RESPONSE_EXTENSIONS }, } request_dict = { "method": request.method.decode("ascii"), "url": normalized_url(request.url), "headers": request.headers, "extensions": { key: value for key, value in request.extensions.items() if key in self.KNOWN_REQUEST_EXTENSIONS }, } metadata_dict = { "cache_key": metadata["cache_key"], "number_of_uses": metadata["number_of_uses"], "created_at": metadata["created_at"].strftime(self.DATETIME_FORMAT), } full_dict = { "response": response_dict, "request": request_dict, "metadata": metadata_dict, } return cast(bytes, msgpack.packb(full_dict, use_bin_type=True)) def loads(self, data: bytes) -> tuple[Response, Request, Metadata]: from datetime import datetime full_dict = cast("dict[str, Any]", msgpack.loads(data, raw=False)) response_dict = full_dict["response"] request_dict = full_dict["request"] metadata_dict = full_dict["metadata"] metadata_dict["created_at"] = datetime.strptime(metadata_dict["created_at"], self.DATETIME_FORMAT) response = Response( status=response_dict["status"], headers=response_dict["headers"], content=response_dict["content"], extensions=response_dict["extensions"], ) request = Request( method=request_dict["method"], url=request_dict["url"], headers=request_dict["headers"], extensions=request_dict["extensions"], ) metadata = Metadata( cache_key=metadata_dict["cache_key"], created_at=metadata_dict["created_at"], number_of_uses=metadata_dict["number_of_uses"], ) return response, request, metadata @property def is_binary(self) -> bool: return True class PDMPyPIClient(PyPIClient): def __init__(self, *, sources: list[RepositoryConfig], cache_dir: Path | None = None, **kwargs: Any) -> None: from httpx._utils import URLPattern from unearth.fetchers.sync import LocalFSTransport if cache_dir is None: def cache_transport(transport: httpx.BaseTransport) -> httpx.BaseTransport: return transport else: storage = hishel.FileStorage(serializer=MsgPackSerializer(), base_path=cache_dir, ttl=CACHES_TTL) controller = hishel.Controller() def cache_transport(transport: httpx.BaseTransport) -> httpx.BaseTransport: return hishel.CacheTransport(transport, storage, controller) mounts: dict[str, httpx.BaseTransport] = {"file://": LocalFSTransport()} self._trusted_host_ports: set[tuple[str, int | None]] = set() self._proxy_map = { URLPattern(key): proxy for key, proxy in self._get_proxy_map(None, allow_env_proxies=True).items() } self._proxy_map = dict(sorted(self._proxy_map.items())) for s in sources: assert s.url is not None url = httpx.URL(s.url) if s.verify_ssl is False: self._trusted_host_ports.add((url.host, url.port)) if s.name == "pypi": kwargs["transport"] = self._transport_for(s) continue mounts[f"{url.scheme}://{url.netloc.decode('ascii')}/"] = cache_transport(self._transport_for(s)) mounts.update(kwargs.pop("mounts", None) or {}) kwargs.update(follow_redirects=True) httpx.Client.__init__(self, mounts=mounts, **kwargs) self.headers["User-Agent"] = self._make_user_agent() self.event_hooks["response"].append(self.on_response) self._transport = cache_transport(self._transport) # type: ignore[has-type] def _transport_for(self, source: RepositoryConfig) -> httpx.BaseTransport: if source.verify_ssl is False: verify: str | bool | SSLContext = False elif source.ca_certs: verify = source.ca_certs else: verify = os.getenv("REQUESTS_CA_BUNDLE") or os.getenv("CURL_CA_BUNDLE") or _ssl_context or True if source.client_cert: cert = (source.client_cert, source.client_key) else: cert = None source_url = httpx.URL(cast(str, source.url)) proxy = next((proxy for pattern, proxy in self._proxy_map.items() if pattern.matches(source_url)), None) return _get_transport(verify=verify, cert=cert, proxy=proxy) def _make_user_agent(self) -> str: import platform return f"pdm/{__version__} {platform.python_implementation()}/{platform.python_version()} {platform.system()}/{platform.release()}" def on_response(self, response: httpx.Response) -> None: from unearth.utils import ARCHIVE_EXTENSIONS if response.extensions.get("from_cache") and response.url.path.endswith(ARCHIVE_EXTENSIONS): logger.info("Using cached response for %s", response.url) pdm-2.23.1/src/pdm/models/setup.py000066400000000000000000000350611477560627500167530ustar00rootroot00000000000000from __future__ import annotations import ast import os from configparser import ConfigParser from dataclasses import asdict, dataclass, field, fields from pathlib import Path from typing import TYPE_CHECKING, Any, Iterable, no_type_check from pdm.compat import Distribution if TYPE_CHECKING: from importlib.metadata import _SimplePath @dataclass class Setup: """ Abstraction of a Python project setup file. """ name: str | None = None version: str | None = None install_requires: list[str] = field(default_factory=list) extras_require: dict[str, list[str]] = field(default_factory=dict) python_requires: str | None = None summary: str | None = None def update(self, other: Setup) -> None: for f in fields(self): other_field = getattr(other, f.name) if other_field: setattr(self, f.name, other_field) def as_dict(self) -> dict[str, Any]: return asdict(self) @classmethod def from_directory(cls, dir: Path) -> Setup: return _SetupReader.read_from_directory(dir) def as_dist(self) -> Distribution: return SetupDistribution(self) class _SetupReader: """ Class that reads a setup.py file without executing it. """ @classmethod def read_from_directory(cls, directory: Path) -> Setup: result = Setup() for filename, file_reader in [ ("pyproject.toml", cls.read_pyproject_toml), ("setup.cfg", cls.read_setup_cfg), ("setup.py", cls.read_setup_py), ]: filepath = directory / filename if not filepath.exists(): continue new_result = file_reader(filepath) result.update(new_result) return result @staticmethod def read_pyproject_toml(file: Path) -> Setup: from pdm import termui from pdm.exceptions import ProjectError from pdm.formats import MetaConvertError from pdm.project.project_file import PyProject try: metadata = PyProject(file, ui=termui.UI()).metadata.unwrap() except ProjectError: return Setup() except MetaConvertError as e: termui.logger.warning("Error parsing pyproject.toml, metadata may be incomplete. %s", e) metadata = e.data return Setup( name=metadata.get("name"), summary=metadata.get("description"), version=metadata.get("version"), install_requires=metadata.get("dependencies", []), extras_require=metadata.get("optional-dependencies", {}), python_requires=metadata.get("requires-python"), ) @no_type_check @classmethod def read_setup_py(cls, file: Path) -> Setup: with file.open(encoding="utf-8") as f: content = f.read() body = ast.parse(content).body setup_call, body = cls._find_setup_call(body) if not setup_call: return Setup() return Setup( name=cls._find_single_string(setup_call, body, "name"), version=cls._find_single_string(setup_call, body, "version") or "0.0.0", install_requires=cls._find_install_requires(setup_call, body), extras_require=cls._find_extras_require(setup_call, body), python_requires=cls._find_single_string(setup_call, body, "python_requires"), ) @staticmethod def read_setup_cfg(file: Path) -> Setup: parser = ConfigParser() parser.read(str(file)) name = None version = "0.0.0" if parser.has_option("metadata", "name"): name = parser.get("metadata", "name") if parser.has_option("metadata", "version"): meta_version = parser.get("metadata", "version") if not meta_version.startswith("attr:"): version = meta_version install_requires = [] extras_require: dict[str, list[str]] = {} python_requires = None if parser.has_section("options"): if parser.has_option("options", "install_requires"): for dep in parser.get("options", "install_requires").split("\n"): dep = dep.strip() if not dep: continue install_requires.append(dep) if parser.has_option("options", "python_requires"): python_requires = parser.get("options", "python_requires") if parser.has_section("options.extras_require"): for group in parser.options("options.extras_require"): extras_require[group] = [] deps = parser.get("options.extras_require", group) for dep in deps.split("\n"): dep = dep.strip() if not dep: continue extras_require[group].append(dep) return Setup( name=name, version=version, install_requires=install_requires, extras_require=extras_require, python_requires=python_requires, ) @classmethod def _find_setup_call(cls, elements: list[Any]) -> tuple[ast.Call | None, list[Any | None]]: funcdefs = [] for i, element in enumerate(elements): if isinstance(element, ast.If) and i == len(elements) - 1: # Checking if the last element is an if statement # and if it is 'if __name__ == "__main__"' which # could contain the call to setup() test = element.test if not isinstance(test, ast.Compare): continue left = test.left if not isinstance(left, ast.Name): continue if left.id != "__name__": continue setup_call, body = cls._find_sub_setup_call([element]) if not setup_call: continue return setup_call, body + elements if not isinstance(element, ast.Expr): if isinstance(element, ast.FunctionDef): funcdefs.append(element) continue value = element.value if not isinstance(value, ast.Call): continue func = value.func if not (isinstance(func, ast.Name) and func.id == "setup") and not ( isinstance(func, ast.Attribute) and isinstance(func.value, ast.Name) and func.value.id == "setuptools" and func.attr == "setup" ): continue return value, elements # Nothing, we inspect the function definitions return cls._find_sub_setup_call(funcdefs) @no_type_check @classmethod def _find_sub_setup_call(cls, elements: list[Any]) -> tuple[ast.Call | None, list[Any | None]]: for element in elements: if not isinstance(element, (ast.FunctionDef, ast.If)): continue setup_call = cls._find_setup_call(element.body) if setup_call != (None, None): setup_call, body = setup_call body = elements + body return setup_call, body return None, None @no_type_check @classmethod def _find_install_requires(cls, call: ast.Call, body: Iterable[Any]) -> list[str]: install_requires: list[str] = [] value = cls._find_in_call(call, "install_requires") if value is None: # Trying to find in kwargs kwargs = cls._find_call_kwargs(call) if kwargs is None or not isinstance(kwargs, ast.Name): return install_requires variable = cls._find_variable_in_body(body, kwargs.id) if not isinstance(variable, (ast.Dict, ast.Call)): return install_requires if isinstance(variable, ast.Call): if not isinstance(variable.func, ast.Name): return install_requires if variable.func.id != "dict": return install_requires value = cls._find_in_call(variable, "install_requires") else: value = cls._find_in_dict(variable, "install_requires") if value is None: return install_requires if isinstance(value, ast.List): install_requires.extend([el.s for el in value.elts if isinstance(el, ast.Str)]) elif isinstance(value, ast.Name): variable = cls._find_variable_in_body(body, value.id) if variable is not None and isinstance(variable, ast.List): install_requires.extend([el.s for el in variable.elts if isinstance(el, ast.Str)]) return install_requires @no_type_check @classmethod def _find_extras_require(cls, call: ast.Call, body: Iterable[Any]) -> dict[str, list[str]]: extras_require: dict[str, list[str]] = {} value = cls._find_in_call(call, "extras_require") if value is None: # Trying to find in kwargs kwargs = cls._find_call_kwargs(call) if kwargs is None or not isinstance(kwargs, ast.Name): return extras_require variable = cls._find_variable_in_body(body, kwargs.id) if not isinstance(variable, (ast.Dict, ast.Call)): return extras_require if isinstance(variable, ast.Call): if not isinstance(variable.func, ast.Name): return extras_require if variable.func.id != "dict": return extras_require value = cls._find_in_call(variable, "extras_require") else: value = cls._find_in_dict(variable, "extras_require") if value is None: return extras_require if isinstance(value, ast.Dict): for key, val in zip(value.keys, value.values): if isinstance(val, ast.Name): val = cls._find_variable_in_body(body, val.id) if isinstance(val, ast.List): extras_require[key.s] = [e.s for e in val.elts if isinstance(e, ast.Str)] elif isinstance(value, ast.Name): variable = cls._find_variable_in_body(body, value.id) if variable is None or not isinstance(variable, ast.Dict): return extras_require for key, val in zip(variable.keys, variable.values): if isinstance(val, ast.Name): val = cls._find_variable_in_body(body, val.id) if isinstance(val, ast.List): extras_require[key.s] = [e.s for e in val.elts if isinstance(e, ast.Str)] return extras_require @classmethod def _find_single_string(cls, call: ast.Call, body: list[Any], name: str) -> str | None: value = cls._find_in_call(call, name) if value is None: # Trying to find in kwargs kwargs = cls._find_call_kwargs(call) if kwargs is None or not isinstance(kwargs, ast.Name): return None variable = cls._find_variable_in_body(body, kwargs.id) if not isinstance(variable, (ast.Dict, ast.Call)): return None if isinstance(variable, ast.Call): if not isinstance(variable.func, ast.Name): return None if variable.func.id != "dict": return None value = cls._find_in_call(variable, name) else: value = cls._find_in_dict(variable, name) if value is None: return None if isinstance(value, ast.Str): return value.s elif isinstance(value, ast.Name): variable = cls._find_variable_in_body(body, value.id) if variable is not None and isinstance(variable, ast.Str): return variable.s return None @staticmethod def _find_in_call(call: ast.Call, name: str) -> Any | None: for keyword in call.keywords: if keyword.arg == name: return keyword.value return None @staticmethod def _find_call_kwargs(call: ast.Call) -> Any | None: kwargs = None for keyword in call.keywords: if keyword.arg is None: kwargs = keyword.value return kwargs @staticmethod def _find_variable_in_body(body: Iterable[Any], name: str) -> Any | None: for elem in body: if not isinstance(elem, ast.Assign): continue for target in elem.targets: if not isinstance(target, ast.Name): continue if target.id == name: return elem.value return None @staticmethod def _find_in_dict(dict_: ast.Dict, name: str) -> Any | None: for key, val in zip(dict_.keys, dict_.values): if isinstance(key, ast.Str) and key.s == name: return val return None class SetupDistribution(Distribution): def __init__(self, data: Setup) -> None: self._data = data def read_text(self, filename: str) -> str | None: return None def locate_file(self, path: str | os.PathLike[str]) -> _SimplePath: return Path() @property def metadata(self) -> dict[str, Any]: # type: ignore[override] return { k: v for k, v in { "Name": self._data.name, "Version": self._data.version, "Summary": self._data.summary, "Requires-Python": self._data.python_requires, }.items() if v is not None } @property def requires(self) -> list[str] | None: from pdm.models.markers import get_marker from pdm.models.requirements import parse_requirement result = self._data.install_requires[:] for extra, reqs in self._data.extras_require.items(): extra_marker = f"extra == '{extra}'" for req in reqs: parsed = parse_requirement(req) old_marker = str(parsed.marker) if parsed.marker else None if old_marker: if " or " in old_marker: new_marker = f"({old_marker}) and {extra_marker}" else: new_marker = f"{old_marker} and {extra_marker}" else: new_marker = extra_marker parsed.marker = get_marker(new_marker) result.append(parsed.as_line()) return result pdm-2.23.1/src/pdm/models/specifiers.py000066400000000000000000000231541477560627500177470ustar00rootroot00000000000000from __future__ import annotations import dataclasses import itertools import json import re import warnings from functools import lru_cache from operator import attrgetter from typing import Any, Iterable, Match, cast from dep_logic.specifiers import ( BaseSpecifier, EmptySpecifier, RangeSpecifier, UnionSpecifier, VersionSpecifier, from_specifierset, ) from packaging.specifiers import SpecifierSet from pdm.exceptions import InvalidPyVersion from pdm.models.versions import Version from pdm.utils import parse_version def _read_max_versions() -> dict[Version, int]: from pdm.compat import resources_open_binary with resources_open_binary("pdm.models", "python_max_versions.json") as fp: return {Version(k): v for k, v in json.load(fp).items()} @lru_cache def get_specifier(version_str: str | None) -> SpecifierSet: if not version_str or version_str == "*": return SpecifierSet() return SpecifierSet(version_str) _legacy_specifier_re = re.compile(r"(==|!=|<=|>=|<|>)(\s*)([^,;\s)]*)") @lru_cache def fix_legacy_specifier(specifier: str) -> str: """Since packaging 22.0, legacy specifiers like '>=4.*' are no longer supported. We try to normalize them to the new format. """ def fix_wildcard(match: Match[str]) -> str: operator, _, version = match.groups() if operator in ("==", "!="): return match.group(0) if ".*" in version: warnings.warn(".* suffix can only be used with `==` or `!=` operators", FutureWarning, stacklevel=4) version = version.replace(".*", ".0") if operator in ("<", "<="): # <4.* and <=4.* are equivalent to <4.0 operator = "<" elif operator in (">", ">="): # >4.* and >=4.* are equivalent to >=4.0 operator = ">=" elif "+" in version: # Drop the local version warnings.warn( "Local version label can only be used with `==` or `!=` operators", FutureWarning, stacklevel=4 ) version = version.split("+")[0] return f"{operator}{version}" return _legacy_specifier_re.sub(fix_wildcard, specifier) class PySpecSet(SpecifierSet): """A custom SpecifierSet that supports merging with logic operators (&, |).""" PY_MAX_MINOR_VERSION = _read_max_versions() MAX_MAJOR_VERSION = max(PY_MAX_MINOR_VERSION)[:1].bump() __slots__ = ("_logic", "_prereleases", "_specs") def __init__(self, spec: str | VersionSpecifier = "") -> None: if spec == "": spec = EmptySpecifier() if isinstance(spec, BaseSpecifier): super().__init__(self._normalize(spec)) self._logic = spec return try: if spec == "*": # pragma: no cover spec = "" super().__init__(fix_legacy_specifier(spec)) self._logic = from_specifierset(self) except ValueError: raise InvalidPyVersion(f"Invalid specifier: {spec}") from None def __hash__(self) -> int: return hash(self._logic) def __str__(self) -> str: if self.is_empty(): return "" return super().__str__() def __eq__(self, other: Any) -> bool: if not isinstance(other, PySpecSet): return NotImplemented return self._logic == other._logic def is_empty(self) -> bool: """Check whether the specifierset contains any valid versions.""" return self._logic.is_empty() def is_any(self) -> bool: """Return True if the specifierset accepts all versions.""" return self._logic.is_any() @classmethod def _normalize(cls, spec: VersionSpecifier) -> str: if spec.is_empty(): return "" if not isinstance(spec, UnionSpecifier): return str(spec) ranges, next_ranges = itertools.tee(sorted(spec.ranges)) next(next_ranges, None) whole_range = RangeSpecifier( min=spec.ranges[0].min, max=spec.ranges[-1].max, include_min=spec.ranges[0].include_min, include_max=spec.ranges[-1].include_max, ) parts = [] if whole_range.is_any() else [str(whole_range)] for left, right in zip(ranges, next_ranges): assert left.max is not None and right.min is not None start = Version(left.max.release).complete() end = Version(right.min.release).complete() if left.include_max: start = start.bump() if not right.include_min: end = end.bump() parts.extend(f"!={v}" for v in cls._populate_version_range(start, end)) return ",".join(parts) def __repr__(self) -> str: return f"" def __and__(self, other: Any) -> PySpecSet: if isinstance(other, PySpecSet): return type(self)(self._logic & other._logic) elif isinstance(other, VersionSpecifier): return type(self)(self._logic & other) return NotImplemented def __or__(self, other: Any) -> PySpecSet: if isinstance(other, PySpecSet): return type(self)(self._logic | other._logic) elif isinstance(other, VersionSpecifier): return type(self)(self._logic | other) return NotImplemented @classmethod def _populate_version_range(cls, lower: Version, upper: Version) -> Iterable[Version]: """Expand the version range to a collection of versions to exclude, taking the released python versions into consideration. """ assert lower < upper prev = lower while prev < upper: if prev[-2:] == Version((0, 0)): # X.0.0 cur = prev.bump(0) # X+1.0.0 if cur <= upper: # It is still within the range yield Version((prev[0], "*")) # Exclude the whole major series: X.* prev = cur continue if prev[-1] == 0: # X.Y.0 cur = prev.bump(1) # X.Y+1.0 if cur <= upper: # It is still within the range yield prev[:2].complete("*") # Exclude X.Y.* prev = ( prev.bump(0) if cur.is_py2 and cast(int, cur[1]) > cls.PY_MAX_MINOR_VERSION[cur[:1]] else cur ) # If prev is 2.7, next is 3.0, otherwise next is X.Y+1.0 continue while prev < upper: # Iterate each version from X.Y.0(prev) to X.Y.Z(upper) yield prev prev = prev.bump() break # Can't produce any wildcard versions cur = prev.bump(1) if cur <= upper: # X.Y+1.0 is still within the range current_max = cls.PY_MAX_MINOR_VERSION[prev[:2]] for z in range(cast(int, prev[2]), current_max + 1): yield prev[:2].complete(z) prev = prev.bump(0) if cur.is_py2 and cast(int, cur[1]) > cls.PY_MAX_MINOR_VERSION[cur[:1]] else cur else: # Produce each version from X.Y.Z to X.Y.W while prev < upper: yield prev prev = prev.bump() break @lru_cache def is_superset(self, other: str | PySpecSet) -> bool: if self.is_empty(): return False this = _fix_py4k(self._logic) if this.is_any(): return True if isinstance(other, str): other = type(self)(other) return this & other._logic == other._logic @lru_cache def is_subset(self, other: str | PySpecSet) -> bool: if self.is_empty(): return False if isinstance(other, str): other = type(self)(other) that = _fix_py4k(other._logic) if that.is_any(): return True return self._logic & that == self._logic def as_marker_string(self) -> str: spec = self._logic if spec.is_empty(): raise InvalidPyVersion("Impossible specifier") if spec.is_any(): return "" return _convert_spec(cast(VersionSpecifier, spec)) def _convert_spec(specifier: VersionSpecifier) -> str: if isinstance(specifier, UnionSpecifier): return " or ".join(_convert_spec(s) for s in specifier.ranges) result: list[str] = [] excludes: list[str] = [] full_excludes: list[str] = [] for spec in sorted(specifier.to_specifierset(), key=attrgetter("version")): op, version = spec.operator, spec.version if len(version.split(".")) < 3: key = "python_version" else: key = "python_full_version" if version[-2:] == ".*": version = version[:-2] key = "python_version" if op == "!=": if key == "python_version": excludes.append(version) else: full_excludes.append(version) else: result.append(f"{key}{op}{version!r}") if excludes: result.append("python_version not in {!r}".format(", ".join(sorted(excludes)))) if full_excludes: result.append("python_full_version not in {!r}".format(", ".join(sorted(full_excludes)))) return " and ".join(result) def _fix_py4k(spec: VersionSpecifier) -> VersionSpecifier: """If the upper bound is 4.0, replace it with unlimited.""" if isinstance(spec, UnionSpecifier): *pre, last = spec.ranges return UnionSpecifier([*pre, _fix_py4k(last)]) if isinstance(spec, RangeSpecifier) and spec.max == parse_version("4.0"): return dataclasses.replace(spec, max=None, include_max=False) return spec pdm-2.23.1/src/pdm/models/venv.py000066400000000000000000000051221477560627500165640ustar00rootroot00000000000000from __future__ import annotations import dataclasses as dc import sys from functools import cached_property from pathlib import Path from pdm.models.in_process import get_sys_config_paths from pdm.utils import find_python_in_path, get_venv_like_prefix IS_WIN = sys.platform == "win32" BIN_DIR = "Scripts" if IS_WIN else "bin" def get_venv_python(venv: Path) -> Path: """Get the interpreter path inside the given venv.""" suffix = ".exe" if IS_WIN else "" result = venv / BIN_DIR / f"python{suffix}" if IS_WIN and not result.exists(): result = venv / "bin" / f"python{suffix}" # for mingw64/msys2 if result.exists(): return result else: return venv / "python.exe" # for conda return result def is_conda_venv(root: Path) -> bool: return (root / "conda-meta").exists() @dc.dataclass(frozen=True) class VirtualEnv: root: Path is_conda: bool interpreter: Path @classmethod def get(cls, root: Path) -> VirtualEnv | None: path = get_venv_python(root) if not path.exists(): return None return cls(root, is_conda_venv(root), path) @classmethod def from_interpreter(cls, interpreter: Path) -> VirtualEnv | None: root, is_conda = get_venv_like_prefix(interpreter) if root is not None: return cls(root, is_conda, interpreter) return None def env_vars(self) -> dict[str, str]: key = "CONDA_PREFIX" if self.is_conda else "VIRTUAL_ENV" return {key: str(self.root)} @cached_property def venv_config(self) -> dict[str, str]: venv_cfg = self.root / "pyvenv.cfg" if not venv_cfg.exists(): return {} parsed: dict[str, str] = {} with venv_cfg.open(encoding="utf-8") as fp: for line in fp: if "=" in line: k, v = line.split("=", 1) k = k.strip().lower() v = v.strip() if k == "include-system-site-packages": v = v.lower() parsed[k] = v return parsed @property def include_system_site_packages(self) -> bool: return self.venv_config.get("include-system-site-packages") == "true" @cached_property def base_paths(self) -> list[str]: home = Path(self.venv_config["home"]) base_executable = find_python_in_path(home) or find_python_in_path(home.parent) assert base_executable is not None paths = get_sys_config_paths(str(base_executable)) return [paths["purelib"], paths["platlib"]] pdm-2.23.1/src/pdm/models/versions.py000066400000000000000000000134241477560627500174620ustar00rootroot00000000000000from __future__ import annotations import re from typing import TYPE_CHECKING, overload from pdm.exceptions import InvalidPyVersion if TYPE_CHECKING: from typing import Any, Literal, Union VersionBit = Union[int, Literal["*"]] PRE_RELEASE_SEGMENT_RE = re.compile( r"(?P\d+)(?Pa|b|rc)(?P\d*)", flags=re.IGNORECASE, ) class Version: """A loosely semantic version implementation that allows '*' in version part. This class is designed for Python specifier set merging only, hence up to 3 version parts are kept, plus optional prerelease suffix. This is a slightly different purpose than packaging.version.Version which is focused on supporting PEP 440 version identifiers, not specifiers. """ MIN: Version MAX: Version # Pre-release may follow version with {a|b|rc}N # https://docs.python.org/3/faq/general.html#how-does-the-python-version-numbering-scheme-work pre: tuple[str, int] | None = None def __init__(self, version: tuple[VersionBit, ...] | str) -> None: if isinstance(version, str): version_str = re.sub(r"(? Version: """ Complete the version with the given bit if the version has less than max parts """ assert len(self._version) <= max_bits, self new_tuple = self._version + (max_bits - len(self._version)) * (complete_with,) ret = type(self)(new_tuple) ret.pre = self.pre return ret def bump(self, idx: int = -1) -> Version: """Bump version by incrementing 1 on the given index of version part. If index is not provided: increment the last version bit unless version is a pre-release, in which case, increment the pre-release number. """ version = self._version if idx == -1 and self.pre: ret = type(self)(version).complete() ret.pre = (self.pre[0], self.pre[1] + 1) else: head, value = version[:idx], int(version[idx]) ret = type(self)((*head, value + 1)).complete() ret.pre = None return ret def startswith(self, other: Version) -> bool: """Check if the version begins with another version.""" return self._version[: len(other._version)] == other._version @property def is_wildcard(self) -> bool: """Check if the version ends with a '*'""" return self._version[-1] == "*" @property def is_prerelease(self) -> bool: """Check if the version is a prerelease.""" return self.pre is not None def __str__(self) -> str: parts = [] parts.append(".".join(map(str, self._version))) if self.pre: parts.append("".join(str(x) for x in self.pre)) return "".join(parts) def __repr__(self) -> str: return f"" def __eq__(self, other: Any) -> bool: if not isinstance(other, Version): return NotImplemented return self._version == other._version and self.pre == other.pre def __lt__(self, other: Any) -> bool: if not isinstance(other, Version): return NotImplemented def comp_key(version: Version) -> list[float]: ret: list[float] = [-1 if v == "*" else v for v in version._version] if version.pre: # Get the ascii value of first character, a < b < r[c] ret += [ord(version.pre[0][0]), version.pre[1]] else: ret += [float("inf")] return ret return comp_key(self) < comp_key(other) def __gt__(self, other: Any) -> bool: return not (self.__lt__(other) or self.__eq__(other)) def __le__(self, other: Any) -> bool: return self.__lt__(other) or self.__eq__(other) def __ge__(self, other: Any) -> bool: return self.__gt__(other) or self.__eq__(other) @overload def __getitem__(self, idx: int) -> VersionBit: ... @overload def __getitem__(self, idx: slice) -> Version: ... def __getitem__(self, idx: int | slice) -> VersionBit | Version: if isinstance(idx, slice): return type(self)(self._version[idx]) else: return self._version[idx] def __setitem__(self, idx: int, value: VersionBit) -> None: if not isinstance(idx, int): raise TypeError("Slice assignment is not supported") version = list(self._version) version[idx] = value self._version = tuple(version) def __hash__(self) -> int: return hash((self._version, self.pre)) @property def is_py2(self) -> bool: return self._version[0] == 2 Version.MIN = Version((-1, -1, -1)) Version.MAX = Version((99, 99, 99)) pdm-2.23.1/src/pdm/models/working_set.py000066400000000000000000000064471477560627500201540ustar00rootroot00000000000000from __future__ import annotations import itertools import sys from collections import ChainMap from pathlib import Path from typing import Iterable, Iterator, Mapping from pdm.compat import importlib_metadata as im from pdm.utils import normalize_name default_context = im.DistributionFinder.Context() class EgglinkFinder(im.DistributionFinder): @classmethod def find_distributions(cls, context: im.DistributionFinder.Context = default_context) -> Iterable[im.Distribution]: found_links = cls._search_paths(context.name, context.path) # For Py3.7 compatibility, handle both classmethod and instance method meta_finder = im.MetadataPathFinder() for link in found_links: name = link.stem with link.open("rb") as file_link: link_pointer = Path(file_link.readline().decode().strip()) dist = next( iter( meta_finder.find_distributions(im.DistributionFinder.Context(name=name, path=[str(link_pointer)])) ), None, ) if not dist: continue dist.link_file = link.absolute() # type: ignore[attr-defined] yield dist @classmethod def _search_paths(cls, name: str | None, paths: list[str]) -> Iterable[Path]: for path in paths: if name: if Path(path).joinpath(f"{name}.egg-link").is_file(): yield Path(path).joinpath(f"{name}.egg-link") else: yield from Path(path).glob("*.egg-link") def distributions(path: list[str]) -> Iterable[im.Distribution]: """Find distributions in the paths. Similar to `importlib.metadata`'s implementation but with the ability to discover egg-links. """ context = im.DistributionFinder.Context(path=path) resolvers = itertools.chain( filter( None, (getattr(finder, "find_distributions", None) for finder in sys.meta_path), ), (EgglinkFinder.find_distributions,), ) return itertools.chain.from_iterable(resolver(context) for resolver in resolvers) class WorkingSet(Mapping[str, im.Distribution]): """A dictionary of currently installed distributions""" def __init__(self, paths: list[str] | None = None, shared_paths: list[str] | None = None) -> None: if paths is None: paths = sys.path if shared_paths is None: shared_paths = [] self._dist_map = { normalize_name(dist.metadata["Name"]): dist for dist in distributions(path=list(dict.fromkeys(paths))) if dist.metadata.get("Name") } self._shared_map = { normalize_name(dist.metadata["Name"]): dist for dist in distributions(path=list(dict.fromkeys(shared_paths))) if dist.metadata.get("Name") } self._iter_map = ChainMap(self._dist_map, self._shared_map) def __getitem__(self, key: str) -> im.Distribution: return self._iter_map[key] def is_owned(self, key: str) -> bool: return key in self._dist_map def __len__(self) -> int: return len(self._iter_map) def __iter__(self) -> Iterator[str]: return iter(self._iter_map) def __repr__(self) -> str: return repr(self._iter_map) pdm-2.23.1/src/pdm/pep582/000077500000000000000000000000001477560627500147745ustar00rootroot00000000000000pdm-2.23.1/src/pdm/pep582/__init__.py000066400000000000000000000000001477560627500170730ustar00rootroot00000000000000pdm-2.23.1/src/pdm/pep582/sitecustomize.py000066400000000000000000000104251477560627500202570ustar00rootroot00000000000000import os import site import sys from pathlib import Path def get_pypackages_path(): def find_pypackage(path, version): if not path.exists(): return None packages_name = f"__pypackages__/{version}/lib" files = list(path.glob(packages_name)) if files: return str(files[0]) if path == path.parent: return None return find_pypackage(path.parent, version) if "PEP582_PACKAGES" in os.environ: return os.path.join(os.getenv("PEP582_PACKAGES"), "lib") find_paths = [os.getcwd()] version = bare_version = ".".join(map(str, sys.version_info[:2])) if os.name == "nt" and sys.maxsize <= 2**32: version += "-32" if getattr(sys, "argv", None) and sys.argv[0]: script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) find_paths.insert(0, script_dir) for path in find_paths: result = find_pypackage(Path(path), version) if result: return result if bare_version != version: for path in find_paths: result = find_pypackage(Path(path), bare_version) if result: return result def load_next_sitecustomize_py2(): import imp try: f, pathname, desc = imp.find_module("sitecustomize", sys.path) try: imp.load_module("another_sitecustomize", f, pathname, desc) finally: f.close() except ImportError: pass def load_next_sitecustomize_py3(): import importlib.util old_module = sys.modules.pop("sitecustomize", None) spec = importlib.util.find_spec("sitecustomize") if spec is not None: module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) if old_module is not None: sys.modules["sitecustomize"] = old_module def patch_sysconfig(libpath): """This is a hack to make sure that the sysconfig.get_paths() returns PEP 582 scheme. """ import functools import sysconfig bin_prefix = "Scripts" if os.name == "nt" else "bin" pep582_base = os.path.dirname(libpath) pep582_scheme = { "stdlib": "{pep582_base}/lib", "platstdlib": "{pep582_base}/lib", "purelib": "{pep582_base}/lib", "platlib": "{pep582_base}/lib", "include": "{pep582_base}/include", "scripts": f"{{pep582_base}}/{bin_prefix}", "data": "{pep582_base}", "prefix": "{pep582_base}", "headers": "{pep582_base}/include", } def patch_pep582(get_paths): @functools.wraps(get_paths) def wrapper(scheme=None, vars=None, expand=True): default_scheme = get_paths.__defaults__[0] if not vars and scheme is None: scheme = "pep582" else: scheme = scheme or default_scheme return get_paths(scheme, vars, expand) return wrapper # This returns a global variable, just update it in place. sysconfig.get_config_vars()["pep582_base"] = pep582_base sysconfig.get_paths = patch_pep582(sysconfig.get_paths) sysconfig._INSTALL_SCHEMES["pep582"] = pep582_scheme def main(): self_path = os.path.normcase(os.path.dirname(os.path.abspath(__file__))) sys.path[:] = [path for path in sys.path if os.path.normcase(path) != self_path] if sys.version_info[0] == 2: # noqa: UP036 load_next_sitecustomize_py2() else: load_next_sitecustomize_py3() libpath = get_pypackages_path() if not libpath: return # First, drop site related paths. original_sys_path = sys.path[:] known_paths = set() site.addusersitepackages(known_paths) site.addsitepackages(known_paths) known_paths = {os.path.normcase(path) for path in known_paths} original_sys_path = [path for path in original_sys_path if os.path.normcase(path) not in known_paths] sys.path[:] = original_sys_path # Second, add lib directories, ensuring .pth file are processed. site.addsitedir(libpath) if not os.environ.pop("NO_SITE_PACKAGES", None): # Then add the removed path to the tail of the paths known_paths.clear() site.addusersitepackages(known_paths) site.addsitepackages(known_paths) if "PEP582_PACKAGES" in os.environ: patch_sysconfig(libpath) main() del main pdm-2.23.1/src/pdm/project/000077500000000000000000000000001477560627500154175ustar00rootroot00000000000000pdm-2.23.1/src/pdm/project/__init__.py000066400000000000000000000002061477560627500175260ustar00rootroot00000000000000from pdm.project.config import Config, ConfigItem from pdm.project.core import Project __all__ = ["Config", "ConfigItem", "Project"] pdm-2.23.1/src/pdm/project/config.py000066400000000000000000000442521477560627500172450ustar00rootroot00000000000000from __future__ import annotations import collections import dataclasses import os from functools import cached_property from pathlib import Path from typing import Any, Callable, ClassVar, Iterator, Mapping, MutableMapping, cast import platformdirs import rich.theme import tomlkit from pdm import termui from pdm._types import RepositoryConfig from pdm.exceptions import NoConfigError, PdmUsageError REPOSITORY = "repository" SOURCE = "pypi" DEFAULT_REPOSITORIES = { "pypi": "https://upload.pypi.org/legacy/", "testpypi": "https://test.pypi.org/legacy/", } ui = termui.UI() def load_config(file_path: Path) -> dict[str, Any]: """Load a nested TOML document into key-value pairs E.g. ["python"]["use_venv"] will be loaded as "python.use_venv" key. """ def get_item(sub_data: Mapping[str, Any]) -> dict[str, Any]: result: dict[str, Any] = {} for k, v in sub_data.items(): if k in (REPOSITORY, SOURCE): result.update((f"{k}.{sub_k}", sub_v) for sub_k, sub_v in v.items()) elif isinstance(v, Mapping): result.update({f"{k}.{sub_k}": sub_v for sub_k, sub_v in get_item(v).items()}) else: result.update({k: v}) return result if not file_path.is_file(): return {} return get_item(dict(tomlkit.parse(file_path.read_text("utf-8")))) def ensure_boolean(val: Any) -> bool: """Coerce a string value to a boolean value""" if not isinstance(val, str): return val return bool(val) and val.lower() not in ("false", "no", "0") def split_by_comma(val: list[str] | str) -> list[str]: """Split a string value by comma""" if isinstance(val, str): return [v.strip() for v in val.split(",")] return val DEFAULT_PYPI_INDEX = "https://pypi.org/simple" @dataclasses.dataclass class ConfigItem: """An item of configuration, with following attributes: Args: description (str): the config description default (Any): the default value, if given, will show in `pdm config` global_only (bool): not allowed to save in project config env_var (str|None): the env var name to take value from coerce (Callable): a function to coerce the value replace: (str|None): the deprecated name to replace """ _NOT_SET = object() description: str default: Any = _NOT_SET global_only: bool = False env_var: str | None = None coerce: Callable = str replace: str | None = None def should_show(self) -> bool: return self.default is not self._NOT_SET class Config(MutableMapping[str, str]): """A dict-like object for configuration key and values""" _config_map: ClassVar[dict[str, ConfigItem]] = { "cache_dir": ConfigItem( "The root directory of cached files", platformdirs.user_cache_dir("pdm"), True, env_var="PDM_CACHE_DIR", ), "log_dir": ConfigItem( "The root directory of log files", platformdirs.user_log_dir("pdm"), True, env_var="PDM_LOG_DIR", ), "check_update": ConfigItem( "Check if there is any newer version available", True, True, env_var="PDM_CHECK_UPDATE", coerce=ensure_boolean, ), "build_isolation": ConfigItem( "Isolate build environment from the project environment", True, False, "PDM_BUILD_ISOLATION", ensure_boolean, ), "request_timeout": ConfigItem( "The timeout for network requests in seconds", 15, True, "PDM_REQUEST_TIMEOUT", coerce=int ), "use_uv": ConfigItem( "Use uv for faster resolution and installation", False, False, "PDM_USE_UV", ensure_boolean, ), "global_project.fallback": ConfigItem( "Use the global project implicitly if no local project is found", False, True, coerce=ensure_boolean, ), "global_project.fallback_verbose": ConfigItem( "If True show message when global project is used implicitly", True, True, coerce=ensure_boolean, ), "global_project.path": ConfigItem( "The path to the global project", platformdirs.user_config_path("pdm") / "global-project", True, ), "global_project.user_site": ConfigItem("Whether to install to user site", False, True, coerce=ensure_boolean), "strategy.update": ConfigItem("The default strategy for updating packages", "reuse", False), "strategy.save": ConfigItem("Specify how to save versions when a package is added", "minimum", False), "strategy.resolve_max_rounds": ConfigItem( "Specify the max rounds of resolution process", 10000, env_var="PDM_RESOLVE_MAX_ROUNDS", coerce=int, ), "strategy.inherit_metadata": ConfigItem( "Inherit the groups and markers from parents for each package", True, coerce=ensure_boolean ), "install.parallel": ConfigItem( "Whether to perform installation and uninstallation in parallel", True, env_var="PDM_INSTALL_PARALLEL", coerce=ensure_boolean, ), "install.cache": ConfigItem( "Cache wheel installation and only put symlinks in the library root", False, coerce=ensure_boolean, ), "install.cache_method": ConfigItem( "Specify how to create links to the caches(`symlink/hardlink`)", "symlink", ), "python.providers": ConfigItem( "List of python provider names for findpython", default=[], coerce=split_by_comma ), "python.use_pyenv": ConfigItem("Use the pyenv interpreter", True, coerce=ensure_boolean), "python.use_venv": ConfigItem( "Use virtual environments when available", True, env_var="PDM_USE_VENV", coerce=ensure_boolean ), "python.install_root": ConfigItem( "The root directory to install python interpreters", global_only=True, default=os.path.join(platformdirs.user_data_dir("pdm"), "python"), ), "pypi.url": ConfigItem( "The URL of PyPI mirror, defaults to https://pypi.org/simple", DEFAULT_PYPI_INDEX, env_var="PDM_PYPI_URL", ), "pypi.verify_ssl": ConfigItem( "Verify SSL certificate when query PyPI", True, env_var="PDM_PYPI_VERIFY_SSL", coerce=ensure_boolean, ), "pypi.username": ConfigItem("The username to access PyPI", env_var="PDM_PYPI_USERNAME"), "pypi.password": ConfigItem("The password to access PyPI", env_var="PDM_PYPI_PASSWORD"), "pypi.ca_certs": ConfigItem( "Path to a CA certificate bundle used for verifying the identity of the PyPI server", global_only=True ), "pypi.ignore_stored_index": ConfigItem( "Don't add the indexes from the config that is not listed in project", False, env_var="PDM_IGNORE_STORED_INDEX", coerce=ensure_boolean, ), "pypi.client_cert": ConfigItem("Path to client certificate file, or combined cert/key file", global_only=True), "pypi.client_key": ConfigItem("Path to client cert keyfile, if not in pypi.client_cert", global_only=True), "pypi.json_api": ConfigItem( "Consult PyPI's JSON API for package metadata", False, env_var="PDM_PYPI_JSON_API", coerce=ensure_boolean, ), "scripts.show_header": ConfigItem( "Display script name and help before running", default=False, env_var="PDM_SCRIPTS_SHOW_HEADER", coerce=ensure_boolean, ), "venv.location": ConfigItem( "Parent directory for virtualenvs", os.path.join(platformdirs.user_data_dir("pdm"), "venvs"), global_only=True, ), "venv.backend": ConfigItem( "Default backend to create virtualenv", default="virtualenv", env_var="PDM_VENV_BACKEND", ), "venv.in_project": ConfigItem( "Create virtualenv in `.venv` under project root", default=True, env_var="PDM_VENV_IN_PROJECT", coerce=ensure_boolean, ), "venv.prompt": ConfigItem( "Define a custom template to be displayed in the prompt when virtualenv is" "active. Variables `project_name` and `python_version` are available for" "formatting", default="{project_name}-{python_version}", env_var="PDM_VENV_PROMPT", ), "venv.with_pip": ConfigItem( "Install pip when creating a new venv", default=False, env_var="PDM_VENV_WITH_PIP", coerce=ensure_boolean, ), } _config_map.update( (f"theme.{k}", ConfigItem(f"Theme color for {k}", default=v, global_only=True)) for k, v in termui.DEFAULT_THEME.items() ) site: Config | None = None @classmethod def get_defaults(cls) -> dict[str, Any]: defaults = {k: v.default for k, v in cls._config_map.items() if v.should_show()} if cls.site is None: cls.site = Config(platformdirs.site_config_path("pdm") / "config.toml") defaults.update(cls.site.self_data) return defaults @cached_property def env_map(self) -> Mapping[str, Any]: return EnvMap(self._config_map) @classmethod def add_config(cls, name: str, item: ConfigItem) -> None: """Add or modify a config item""" cls._config_map[name] = item def __init__(self, config_file: Path, is_global: bool = False): self.is_global = is_global self.config_file = config_file.resolve() self.deprecated = {v.replace: k for k, v in self._config_map.items() if v.replace} self._file_data = load_config(self.config_file) self._data = collections.ChainMap( cast(MutableMapping[str, Any], self.env_map) if not is_global else {}, self._file_data, self.get_defaults() if is_global else {}, ) def load_theme(self) -> rich.theme.Theme: if not self.is_global: # pragma: no cover raise PdmUsageError("Theme can only be loaded from global config") return rich.theme.Theme({k[6:]: v for k, v in self.items() if k.startswith("theme.")}) @property def self_data(self) -> dict[str, Any]: return dict(self._file_data) def iter_sources(self) -> Iterator[RepositoryConfig]: for name, data in self._data.items(): if name.startswith(f"{SOURCE}.") and name not in self._config_map and data: yield RepositoryConfig(**data, name=name[len(SOURCE) + 1 :], config_prefix=SOURCE) def _save_config(self) -> None: """Save the changed to config file.""" self.config_file.parent.mkdir(parents=True, exist_ok=True) toml_data: dict[str, Any] = {} for key, value in self._file_data.items(): *parts, last = key.split(".") temp = toml_data for part in parts: if part not in temp: temp[part] = {} temp = temp[part] temp[last] = value with self.config_file.open("w", encoding="utf-8") as fp: tomlkit.dump(toml_data, fp) def __getitem__(self, key: str) -> Any: parts = key.split(".") if parts[0] in (REPOSITORY, SOURCE) and key not in self._config_map: if len(parts) < 2: raise PdmUsageError(f"Must specify a {parts[0]} name") repo = self.get_repository_config(parts[1], parts[0]) if repo is None: raise KeyError(f"No {parts[0]} named {parts[1]}") if len(parts) >= 3: repo.populate_keyring_auth() return getattr(repo, parts[2]) return repo if key not in self._config_map and key not in self.deprecated: raise NoConfigError(key) config_key = self.deprecated.get(key, key) config = self._config_map[config_key] if config_key in self._data: result = self._data[config_key] elif config.replace: result = self._data[config.replace] else: raise NoConfigError(key) from None return config.coerce(result) def __setitem__(self, key: str, value: Any) -> None: from pdm.models.auth import keyring parts = key.split(".") if parts[0] in (REPOSITORY, SOURCE) and key not in self._config_map: if len(parts) < 3: raise PdmUsageError(f"Set {parts[0]} config with [success]{parts[0]}.{{name}}.{{attr}}") index_key = ".".join(parts[:2]) username = self._data.get(index_key, {}).get("username") # type: ignore[call-overload] service = f"pdm-{index_key.replace('.', '-')}" if ( parts[2] == "password" and self.is_global and username and keyring.save_auth_info(service, username, value) ): return if parts[2] == "verify_ssl": value = ensure_boolean(value) self._file_data.setdefault(index_key, {})[parts[2]] = value self._save_config() return if key not in self._config_map and key not in self.deprecated: raise NoConfigError(key) config_key = self.deprecated.get(key, key) config = self._config_map[config_key] if not self.is_global and config.global_only: raise ValueError(f"Config item '{key}' is not allowed to set in project config.") value = config.coerce(value) if key in self.env_map: ui.warn(f"the config is shadowed by env var '{config.env_var}', the value set won't take effect.") self._file_data[config_key] = value if config.replace: self._file_data.pop(config.replace, None) self._save_config() def __len__(self) -> int: return len(self._data) def __iter__(self) -> Iterator[str]: keys: set[str] = set() for key in self._data: if key in self.deprecated: key = self.deprecated[key] keys.add(key) return iter(keys) def __delitem__(self, key: str) -> None: from pdm.models.auth import keyring parts = key.split(".") if parts[0] in (REPOSITORY, SOURCE) and key not in self._config_map: if len(parts) < 2: raise PdmUsageError(f"Should specify the name of {parts[0]}") index_key = ".".join(parts[:2]) username = self._data.get(index_key, {}).get("username") # type: ignore[call-overload] service = f"pdm-{index_key.replace('.', '-')}" if len(parts) >= 3: index_key, attr = key.rsplit(".", 1) if attr == "password" and username: keyring.delete_auth_info(service, username) self._file_data.get(index_key, {}).pop(attr, None) else: del self._file_data[key] if username: keyring.delete_auth_info(service, username) self._save_config() return config_key = self.deprecated.get(key, key) config = self._config_map[config_key] self._file_data.pop(config_key, None) if config.replace: self._file_data.pop(config.replace, None) env_var = config.env_var if env_var is not None and env_var in os.environ: ui.warn(f"The config is shadowed by env var '{env_var}', set value won't take effect.") self._save_config() def get_repository_config(self, name_or_url: str, prefix: str) -> RepositoryConfig | None: """Get a repository or source by name or url.""" repositories: dict[str, RepositoryConfig] = {} for k, v in self._data.items(): if not k.startswith(f"{prefix}.") or k in self._config_map: continue key = k[len(prefix) + 1 :] repositories[key] = RepositoryConfig(**v, name=key, config_prefix=prefix) config: RepositoryConfig | None = None if "://" in name_or_url: config = next( (v for v in repositories.values() if v.url == name_or_url), RepositoryConfig(url=name_or_url, name="__unknown__", config_prefix=prefix), ) else: config = repositories.get(name_or_url) if prefix == SOURCE or not self.is_global: return config if name_or_url in DEFAULT_REPOSITORIES: if config is None: return RepositoryConfig(url=DEFAULT_REPOSITORIES[name_or_url], name=name_or_url, config_prefix=prefix) config.passive_update(url=DEFAULT_REPOSITORIES[name_or_url]) if name_or_url in DEFAULT_REPOSITORIES.values(): name = next(k for k, v in DEFAULT_REPOSITORIES.items() if v == name_or_url) if config is None: return RepositoryConfig( name=name, config_prefix=prefix, url=name_or_url, ) config.passive_update(url=name_or_url) if config.name == "__unknown__": config.name = name return config class EnvMap(Mapping[str, Any]): def __init__(self, config_items: Mapping[str, ConfigItem]) -> None: self._config_map = config_items def __repr__(self) -> str: return repr(dict(self)) def __getitem__(self, k: str) -> Any: try: item = self._config_map[k] if item.env_var: return item.coerce(os.environ[item.env_var]) except KeyError: pass raise KeyError(k) def __iter__(self) -> Iterator[str]: for key, item in self._config_map.items(): if item.env_var and item.env_var in os.environ: yield key def __len__(self) -> int: return sum(1 for _ in self) pdm-2.23.1/src/pdm/project/core.py000066400000000000000000001236441477560627500167330ustar00rootroot00000000000000from __future__ import annotations import contextlib import hashlib import itertools import operator import os import re import shutil import sys from copy import deepcopy from functools import cached_property, reduce from pathlib import Path from typing import TYPE_CHECKING, Any, Callable, Iterable, Mapping, Sequence, cast import tomlkit from pbs_installer import PythonVersion from pdm._types import NotSet, NotSetType, RepositoryConfig from pdm.compat import CompatibleSequence from pdm.exceptions import NoPythonVersion, PdmUsageError, ProjectError from pdm.models.backends import DEFAULT_BACKEND, BuildBackend, get_backend_by_spec from pdm.models.caches import PackageCache from pdm.models.markers import EnvSpec from pdm.models.python import PythonInfo from pdm.models.repositories import BaseRepository, LockedRepository from pdm.models.requirements import Requirement, parse_line, parse_requirement, strip_extras from pdm.models.specifiers import PySpecSet from pdm.project.config import Config, ensure_boolean from pdm.project.lockfile import FLAG_INHERIT_METADATA, Lockfile from pdm.project.project_file import PyProject from pdm.utils import ( cd, deprecation_warning, expand_env_vars_in_auth, find_project_root, find_python_in_path, get_all_installable_python_versions, get_class_init_params, is_conda_base, is_conda_base_python, is_path_relative_to, normalize_name, ) if TYPE_CHECKING: from findpython import Finder from pdm.core import Core from pdm.environments import BaseEnvironment from pdm.installers.base import BaseSynchronizer from pdm.models.caches import CandidateInfoCache, HashCache, WheelCache from pdm.models.candidates import Candidate from pdm.resolver.base import Resolver from pdm.resolver.providers import BaseProvider from pdm.resolver.reporters import RichLockReporter PYENV_ROOT = os.path.expanduser(os.getenv("PYENV_ROOT", "~/.pyenv")) class Project: """Core project class. Args: core: The core instance. root_path: The root path of the project. is_global: Whether the project is global. global_config: The path to the global config file. """ PYPROJECT_FILENAME = "pyproject.toml" LOCKFILE_FILENAME = "pdm.lock" DEPENDENCIES_RE = re.compile(r"(?:(.+?)-)?dependencies") def __init__( self, core: Core, root_path: str | Path | None, is_global: bool = False, global_config: str | Path | None = None, ) -> None: import platformdirs self._lockfile: Lockfile | None = None self._environment: BaseEnvironment | None = None self._python: PythonInfo | None = None self._cache_dir: Path | None = None self.core = core if global_config is None: global_config = platformdirs.user_config_path("pdm") / "config.toml" self.global_config = Config(Path(global_config), is_global=True) global_project = Path(self.global_config["global_project.path"]).expanduser() if root_path is None: root_path = find_project_root() if not is_global else global_project if ( not is_global and root_path is None and self.global_config["global_project.fallback"] and not is_conda_base() ): root_path = global_project is_global = True if self.global_config["global_project.fallback_verbose"]: self.core.ui.info("Project is not found, fallback to the global project") self.root: Path = Path(root_path or "").absolute() self.is_global = is_global self.enable_write_lockfile = os.getenv("PDM_FROZEN_LOCKFILE", os.getenv("PDM_NO_LOCK", "0")).lower() not in ( "1", "true", ) self.init_global_project() def __repr__(self) -> str: return f"" @cached_property def cache_dir(self) -> Path: return Path(self.config.get("cache_dir", "")).expanduser() @cached_property def pyproject(self) -> PyProject: return PyProject(self.root / self.PYPROJECT_FILENAME, ui=self.core.ui) @property def lockfile(self) -> Lockfile: if self._lockfile is None: self.set_lockfile(self.root / self.LOCKFILE_FILENAME) assert self._lockfile is not None return self._lockfile def set_lockfile(self, path: str | Path) -> None: self._lockfile = Lockfile(path, ui=self.core.ui) if self.config.get("use_uv"): self._lockfile.default_strategies.discard(FLAG_INHERIT_METADATA) if not self.config["strategy.inherit_metadata"]: self._lockfile.default_strategies.discard(FLAG_INHERIT_METADATA) @cached_property def config(self) -> Mapping[str, Any]: """A read-only dict configuration""" import collections return collections.ChainMap(self.project_config, self.global_config) @property def scripts(self) -> dict[str, str | dict[str, str]]: return self.pyproject.settings.get("scripts", {}) @cached_property def project_config(self) -> Config: """Read-and-writable configuration dict for project settings""" config = Config(self.root / "pdm.toml") # TODO: for backward compatibility, remove this in the future if self.root.joinpath(".pdm.toml").exists(): legacy_config = Config(self.root / ".pdm.toml").self_data config.update((k, v) for k, v in legacy_config.items() if k != "python.path") return config @property def name(self) -> str: return self.pyproject.metadata.get("name") @property def python(self) -> PythonInfo: if not self._python: python = self.resolve_interpreter() if python.major < 3: raise PdmUsageError( "Python 2.7 has reached EOL and PDM no longer supports it. " "Please upgrade your Python to 3.6 or later.", ) if self.is_global and is_conda_base_python(python.path): # pragma: no cover raise PdmUsageError("Can't use global project in conda base environment since it is managed by conda") self._python = python return self._python @python.setter def python(self, value: PythonInfo) -> None: self._python = value self._saved_python = value.path.as_posix() @property def _saved_python(self) -> str | None: if os.getenv("PDM_PYTHON"): return os.getenv("PDM_PYTHON") with contextlib.suppress(FileNotFoundError): return self.root.joinpath(".pdm-python").read_text("utf-8").strip() with contextlib.suppress(FileNotFoundError): # TODO: remove this in the future with self.root.joinpath(".pdm.toml").open("rb") as fp: data = tomlkit.load(fp) if data.get("python", {}).get("path"): return data["python"]["path"] return None @_saved_python.setter def _saved_python(self, value: str | None) -> None: self.root.mkdir(parents=True, exist_ok=True) python_file = self.root.joinpath(".pdm-python") if value is None: with contextlib.suppress(FileNotFoundError): python_file.unlink() return python_file.write_text(value, "utf-8") def resolve_interpreter(self) -> PythonInfo: """Get the Python interpreter path.""" from pdm.cli.commands.venv.utils import iter_venvs from pdm.models.venv import get_venv_python def match_version(python: PythonInfo) -> bool: return python.valid and self.python_requires.contains(python.version, True) def note(message: str) -> None: if not self.is_global: self.core.ui.info(message) def is_active_venv(python: PythonInfo) -> bool: if not (venv := os.getenv("VIRTUAL_ENV", os.getenv("CONDA_PREFIX"))): return False return is_path_relative_to(python.executable, venv) config = self.config saved_path = self._saved_python if saved_path and not ensure_boolean(os.getenv("PDM_IGNORE_SAVED_PYTHON")): python = PythonInfo.from_path(saved_path) if match_version(python): return python elif not python.valid: note("The saved Python interpreter does not exist or broken. Trying to find another one.") else: note( "The saved Python interpreter doesn't match the project's requirement. Trying to find another one." ) self._saved_python = None # Clear the saved path if it doesn't match if config.get("python.use_venv") and not self.is_global: # Resolve virtual environments from env-vars ignore_active_venv = ensure_boolean(os.getenv("PDM_IGNORE_ACTIVE_VENV")) venv_in_env = os.getenv("VIRTUAL_ENV", os.getenv("CONDA_PREFIX")) # We don't auto reuse conda's base env since it may cause breakage when removing packages. if not ignore_active_venv and venv_in_env and not is_conda_base(): python = PythonInfo.from_path(get_venv_python(Path(venv_in_env))) if match_version(python): note( f"Inside an active virtualenv [success]{venv_in_env}[/], reusing it.\n" "Set env var [success]PDM_IGNORE_ACTIVE_VENV[/] to ignore it." ) return python # otherwise, get a venv associated with the project for _, venv in iter_venvs(self): python = PythonInfo.from_path(venv.interpreter) if match_version(python) and not (ignore_active_venv and is_active_venv(python)): note(f"Virtualenv [success]{venv.root}[/] is reused.") self.python = python return python if not self.root.joinpath("__pypackages__").exists(): self.core.ui.warn( f"Project requires a python version of {self.python_requires}, " f"The virtualenv is being created for you as it cannot be matched to the right version." ) note("python.use_venv is on, creating a virtualenv for this project...") venv_path = self._create_virtualenv() self.python = PythonInfo.from_path(get_venv_python(venv_path)) return self.python if self.root.joinpath("__pypackages__").exists() or not config["python.use_venv"] or self.is_global: for py_version in self.iter_interpreters(filter_func=match_version): note("[success]__pypackages__[/] is detected, using the PEP 582 mode") self.python = py_version return py_version raise NoPythonVersion(f"No Python that satisfies {self.python_requires} is found on the system.") def get_environment(self) -> BaseEnvironment: from pdm.environments import PythonEnvironment, PythonLocalEnvironment """Get the environment selected by this project""" if self.is_global: env = PythonEnvironment(self) # Rewrite global project's python requires to be # compatible with the exact version env.python_requires = PySpecSet(f"=={self.python.version}") return env return ( PythonEnvironment(self) if self.config["python.use_venv"] and self.python.get_venv() is not None else PythonLocalEnvironment(self) ) def _create_virtualenv(self, python: str | None = None) -> Path: from pdm.cli.commands.venv.backends import BACKENDS backend: str = self.config["venv.backend"] if backend == "virtualenv" and self.config["use_uv"]: backend = "uv" venv_backend = BACKENDS[backend](self, python) path = venv_backend.create( force=True, in_project=self.config["venv.in_project"], prompt=self.config["venv.prompt"], with_pip=self.config["venv.with_pip"], ) self.core.ui.echo(f"Virtualenv is created successfully at [success]{path}[/]", err=True) return path @property def environment(self) -> BaseEnvironment: if not self._environment: self._environment = self.get_environment() return self._environment @environment.setter def environment(self, value: BaseEnvironment | None) -> None: self._environment = value @property def python_requires(self) -> PySpecSet: return PySpecSet(self.pyproject.metadata.get("requires-python", "")) def get_dependencies( self, group: str | None = None, all_dependencies: dict[str, list[Requirement]] | None = None ) -> Sequence[Requirement]: group = normalize_name(group or "default") if all_dependencies is None: all_dependencies = self._resolve_dependencies([group]) if group not in all_dependencies: raise ProjectError(f"Dependency group {group} does not exist") return CompatibleSequence(all_dependencies[group]) def iter_groups(self) -> Iterable[str]: groups = {"default"} if self.pyproject.metadata.get("optional-dependencies"): groups.update(self.pyproject.metadata["optional-dependencies"].keys()) groups.update(self.pyproject._data.get("dependency-groups", {}).keys()) groups.update(self.pyproject.settings.get("dev-dependencies", {}).keys()) return {normalize_name(g) for g in groups} def _resolve_dependencies( self, requested_groups: list[str] | None = None, include_referred: bool = True ) -> dict[str, list[Requirement]]: """Resolve dependencies for the given groups, and return a list of requirements for each group. The .groups attribute will be set to all that refers this requirement directly or indirectly. If `include_referred` is True, all self-references and `include-group` will be expanded to corresponding requirements. Otherwise, each group only contains explicitly defined requirements. """ def _get_dependencies(group: str) -> tuple[list[Requirement], set[str]]: in_metadata = group in metadata_dependencies collected_deps: list[str] = [] referred: set[str] = set() deps = metadata_dependencies.get(group, []) if in_metadata else dev_dependencies[group] for item in deps: if isinstance(item, str): try: name, extras = strip_extras(item) except AssertionError: pass else: if normalize_name(name) == project_name: if extras: allowed = ( set(metadata_dependencies) if in_metadata else {*metadata_dependencies, *dev_dependencies} ) extras = tuple(normalize_name(extra) for extra in extras) not_allowed = set(extras) - allowed if not_allowed: raise ProjectError( f"Optional dependency group '{group}' cannot " f"include non-existing extras: [{','.join(not_allowed)}]" ) referred.update(extras) continue collected_deps.append(item) elif not in_metadata and isinstance(item, dict): if tuple(item.keys()) != ("include-group",): raise ProjectError(f"Invalid dependency group item: {item}") include_group = normalize_name(item["include-group"]) if include_group not in dev_dependencies: raise ProjectError(f"Missing group '{include_group}' in `include-group`") referred.add(include_group) else: raise ProjectError(f"Invalid dependency in group {group}: {item}") result: list[Requirement] = [] with cd(self.root): for line in collected_deps: if line.startswith("-e ") and in_metadata: self.core.ui.warn( f"Skipping editable dependency [b]{line}[/] in the" r" [success]\[project][/] table. Please move it to the " r"[success]\[tool.pdm.dev-dependencies][/] table" ) continue req = parse_line(line) req.groups = [group] # make editable packages behind normal ones to override correctly. result.append(req) return result, referred if requested_groups is None: requested_groups = list(self.iter_groups()) requested_groups = [normalize_name(g) for g in requested_groups] referred_groups: dict[str, set[str]] = {} metadata_dependencies = { normalize_name(k): v for k, v in self.pyproject.metadata.get("optional-dependencies", {}).items() } metadata_dependencies["default"] = self.pyproject.metadata.get("dependencies", []) dev_dependencies = self.pyproject.dev_dependencies group_deps: dict[str, list[Requirement]] = {} project_name = normalize_name(self.name) if self.name else None for group in requested_groups: deps, referred = _get_dependencies(group) group_deps[group] = deps if referred: referred_groups[group] = referred extra_deps: dict[str, list[Requirement]] = {} while referred_groups: updated = False ref_iter = list(referred_groups.items()) for group, referred in ref_iter: for ref in list(referred): if ref not in requested_groups: deps, r = _get_dependencies(ref) group_deps[ref] = deps if r: referred_groups[ref] = r # append to the ref_iter to process later ref_iter.append((ref, r)) requested_groups.append(ref) if ref in referred_groups: # not resolved yet continue extra_deps.setdefault(group, []).extend(group_deps[ref]) for req in itertools.chain(group_deps[ref], extra_deps.get(ref, [])): if group not in req.groups: req.groups.append(group) referred.remove(ref) updated = True if not referred: referred_groups.pop(group) if not updated: raise ProjectError(f"Cyclic dependency group include detected: {set(referred_groups)}") if include_referred: for group, deps in extra_deps.items(): group_deps[group].extend(deps) return group_deps @property def all_dependencies(self) -> dict[str, Sequence[Requirement]]: return {k: CompatibleSequence(v) for k, v in self._resolve_dependencies(include_referred=False).items()} @property def default_source(self) -> RepositoryConfig: """Get the default source from the pypi setting""" config = RepositoryConfig( config_prefix="pypi", name="pypi", url=self.config["pypi.url"], verify_ssl=self.config["pypi.verify_ssl"], username=self.config.get("pypi.username"), password=self.config.get("pypi.password"), ca_certs=self.config.get("pypi.ca_certs"), client_cert=self.config.get("pypi.client_cert"), client_key=self.config.get("pypi.client_key"), ) return config @property def sources(self) -> list[RepositoryConfig]: return self.get_sources(include_stored=not self.config.get("pypi.ignore_stored_index", False)) def get_sources(self, expand_env: bool = True, include_stored: bool = False) -> list[RepositoryConfig]: result: dict[str, RepositoryConfig] = {} for source in self.pyproject.settings.get("source", []): result[source["name"]] = RepositoryConfig(**source, config_prefix="pypi") def merge_sources(other_sources: Iterable[RepositoryConfig]) -> None: for source in other_sources: name = source.name if name in result: result[name].passive_update(source) elif include_stored: result[name] = source merge_sources(self.project_config.iter_sources()) merge_sources(self.global_config.iter_sources()) if "pypi" in result: result["pypi"].passive_update(self.default_source) elif include_stored: # put pypi source at the beginning result = {"pypi": self.default_source, **result} sources: list[RepositoryConfig] = [] for source in result.values(): if not source.url: continue if expand_env: source.url = DEFAULT_BACKEND(self.root).expand_line(expand_env_vars_in_auth(source.url)) sources.append(source) return sources def get_repository( self, cls: type[BaseRepository] | None = None, ignore_compatibility: bool | NotSetType = NotSet, env_spec: EnvSpec | None = None, ) -> BaseRepository: """Get the repository object""" if cls is None: cls = self.core.repository_class sources = self.sources or [] params = get_class_init_params(cls) if "env_spec" in params: return cls(sources, self.environment, env_spec=env_spec) else: return cls(sources, self.environment, ignore_compatibility=ignore_compatibility) def get_locked_repository(self, env_spec: EnvSpec | None = None) -> LockedRepository: try: lockfile = self.lockfile._data.unwrap() except ProjectError: lockfile = {} return LockedRepository(lockfile, self.sources, self.environment, env_spec=env_spec) @property def locked_repository(self) -> LockedRepository: deprecation_warning("Project.locked_repository is deprecated, use Project.get_locked_repository() instead", 2) return self.get_locked_repository() def get_provider( self, strategy: str = "all", tracked_names: Iterable[str] | None = None, for_install: bool = False, ignore_compatibility: bool | NotSetType = NotSet, direct_minimal_versions: bool = False, env_spec: EnvSpec | None = None, locked_repository: LockedRepository | None = None, ) -> BaseProvider: """Build a provider class for resolver. :param strategy: the resolve strategy :param tracked_names: the names of packages that needs to update :param for_install: if the provider is for install :param ignore_compatibility: if the provider should ignore the compatibility when evaluating candidates :param direct_minimal_versions: if the provider should prefer minimal versions instead of latest :returns: The provider object """ import inspect from pdm.resolver.providers import get_provider if env_spec is None: env_spec = ( self.environment.allow_all_spec if ignore_compatibility in (True, NotSet) else self.environment.spec ) repo_params = inspect.signature(self.get_repository).parameters if "env_spec" in repo_params: repository = self.get_repository(env_spec=env_spec) else: # pragma: no cover repository = self.get_repository(ignore_compatibility=ignore_compatibility) if locked_repository is None: try: locked_repository = self.get_locked_repository(env_spec) except Exception: # pragma: no cover if strategy != "all": self.core.ui.warn("Unable to reuse the lock file as it is not compatible with PDM") provider_class = get_provider(strategy) params: dict[str, Any] = {} if strategy != "all": params["tracked_names"] = [strip_extras(name)[0] for name in tracked_names or ()] locked_candidates: dict[str, list[Candidate]] = ( {} if locked_repository is None else locked_repository.all_candidates ) params["locked_candidates"] = locked_candidates return provider_class(repository=repository, direct_minimal_versions=direct_minimal_versions, **params) def get_reporter( self, requirements: list[Requirement], tracked_names: Iterable[str] | None = None ) -> RichLockReporter: # pragma: no cover """Return the reporter object to construct a resolver. :param requirements: requirements to resolve :param tracked_names: the names of packages that needs to update :param spinner: optional spinner object :returns: a reporter """ from pdm.resolver.reporters import RichLockReporter return RichLockReporter(requirements, self.core.ui) def get_lock_metadata(self) -> dict[str, Any]: content_hash = "sha256:" + self.pyproject.content_hash("sha256") return {"lock_version": str(self.lockfile.spec_version), "content_hash": content_hash} def write_lockfile(self, toml_data: dict, show_message: bool = True, write: bool = True, **_kwds: Any) -> None: """Write the lock file to disk.""" if _kwds: deprecation_warning("Extra arguments have been moved to `format_lockfile` function", stacklevel=2) toml_data["metadata"].update(self.get_lock_metadata()) self.lockfile.set_data(toml_data) if write and self.enable_write_lockfile: self.lockfile.write(show_message) def make_self_candidate(self, editable: bool = True) -> Candidate: from unearth import Link from pdm.models.candidates import Candidate req = parse_requirement(self.root.as_uri(), editable) assert self.name req.name = self.name can = Candidate(req, name=self.name, link=Link.from_path(self.root)) can.prepare(self.environment).metadata return can def is_lockfile_hash_match(self) -> bool: hash_in_lockfile = str(self.lockfile.hash) if not hash_in_lockfile: return False algo, hash_value = hash_in_lockfile.split(":") content_hash = self.pyproject.content_hash(algo) return content_hash == hash_value def use_pyproject_dependencies( self, group: str, dev: bool = False ) -> tuple[list[str], Callable[[list[str]], None]]: """Get the dependencies array and setter in the pyproject.toml Return a tuple of two elements, the first is the dependencies array, and the second value is a callable to set the dependencies array back. """ from pdm.formats.base import make_array def update_dev_dependencies(deps: list[str]) -> None: from tomlkit.container import OutOfOrderTableProxy dependency_groups: list[str | dict[str, str]] = tomlkit.array().multiline(True) dev_dependencies: list[str] = tomlkit.array().multiline(True) for dep in deps: if isinstance(dep, str) and dep.startswith("-e"): dev_dependencies.append(dep) else: dependency_groups.append(dep) if dependency_groups: self.pyproject.dependency_groups[group] = dependency_groups else: self.pyproject.dependency_groups.pop(group, None) if dev_dependencies: settings.setdefault("dev-dependencies", {})[group] = dev_dependencies else: settings.setdefault("dev-dependencies", {}).pop(group, None) if isinstance(self.pyproject._data["tool"], OutOfOrderTableProxy): # In case of a separate table, we have to remove and re-add it to make the write correct. # This may change the order of tables in the TOML file, but it's the best we can do. # see bug pdm-project/pdm#2056 for details del self.pyproject._data["tool"]["pdm"] self.pyproject._data["tool"]["pdm"] = settings metadata, settings = self.pyproject.metadata, self.pyproject.settings if group == "default": return metadata.get("dependencies", tomlkit.array()), lambda x: metadata.__setitem__("dependencies", x) dev_dependencies = deepcopy(self.pyproject._data.get("dependency-groups", {})) for dev_group, items in self.pyproject.settings.get("dev-dependencies", {}).items(): dev_dependencies.setdefault(dev_group, []).extend(items) deps_setter = [ ( metadata.get("optional-dependencies", {}), lambda x: metadata.setdefault("optional-dependencies", {}).__setitem__(group, x) if x else metadata.setdefault("optional-dependencies", {}).pop(group, None), ), (dev_dependencies, update_dev_dependencies), ] normalized_group = normalize_name(group) for deps, setter in deps_setter: normalized_groups = {normalize_name(g) for g in deps} if group in deps: return make_array(deps[group], True), setter if normalized_group in normalized_groups: raise PdmUsageError(f"Group {group} already exists in another non-normalized form") # If not found, return an empty list and a setter to add the group return tomlkit.array().multiline(True), deps_setter[int(dev)][1] def add_dependencies( self, requirements: Iterable[str | Requirement], to_group: str = "default", dev: bool = False, show_message: bool = True, write: bool = True, ) -> list[Requirement]: """Add requirements to the given group, and return the requirements of that group.""" if isinstance(requirements, Mapping): # pragma: no cover deprecation_warning( "Passing a requirements map to add_dependencies is deprecated, please pass an iterable", stacklevel=2 ) requirements = requirements.values() deps, setter = self.use_pyproject_dependencies(to_group, dev) updated_indices: set[int] = set() with cd(self.root): parsed_deps = [(parse_line(dep) if isinstance(dep, str) else None) for dep in deps] for req in requirements: if isinstance(req, str): req = parse_line(req) matched_index = next( ( i for i, r in enumerate(deps) if isinstance(r, str) and req.matches(r) and i not in updated_indices ), None, ) dep = req.as_line() if matched_index is None: updated_indices.add(len(deps)) deps.append(dep) parsed_deps.append(req) else: deps[matched_index] = dep parsed_deps[matched_index] = req updated_indices.add(matched_index) setter(deps) if write: self.pyproject.write(show_message) for r in parsed_deps: if r is not None: r.groups = [to_group] return [r for r in parsed_deps if r is not None] def init_global_project(self) -> None: if not self.is_global or not self.pyproject.empty(): return self.root.mkdir(parents=True, exist_ok=True) self.pyproject.set_data({"project": {"dependencies": ["pip", "setuptools", "wheel"]}}) self.pyproject.write() @property def backend(self) -> BuildBackend: return get_backend_by_spec(self.pyproject.build_system)(self.root) def cache(self, name: str) -> Path: path = self.cache_dir / name try: path.mkdir(parents=True, exist_ok=True) except OSError: # The path could be not accessible pass return path def make_wheel_cache(self) -> WheelCache: from pdm.models.caches import get_wheel_cache return get_wheel_cache(self.cache("wheels")) @property def package_cache(self) -> PackageCache: return PackageCache(self.cache("packages")) def make_candidate_info_cache(self) -> CandidateInfoCache: from pdm.models.caches import CandidateInfoCache, EmptyCandidateInfoCache python_hash = hashlib.sha1(str(self.environment.python_requires).encode()).hexdigest() file_name = f"package_meta_{python_hash}.json" return ( CandidateInfoCache(self.cache("metadata") / file_name) if self.core.state.enable_cache else EmptyCandidateInfoCache(self.cache("metadata") / file_name) ) def make_hash_cache(self) -> HashCache: from pdm.models.caches import EmptyHashCache, HashCache return HashCache(self.cache("hashes")) if self.core.state.enable_cache else EmptyHashCache(self.cache("hashes")) def iter_interpreters( self, python_spec: str | None = None, search_venv: bool | None = None, filter_func: Callable[[PythonInfo], bool] | None = None, respect_version_file: bool = True, ) -> Iterable[PythonInfo]: """Iterate over all interpreters that matches the given specifier. And optionally install the interpreter if not found. """ from packaging.version import InvalidVersion from pdm.cli.commands.python import InstallCommand found = False if ( respect_version_file and not python_spec and (os.getenv("PDM_PYTHON_VERSION") or self.root.joinpath(".python-version").exists()) ): requested = os.getenv("PDM_PYTHON_VERSION") or self.root.joinpath(".python-version").read_text().strip() if requested not in self.python_requires: self.core.ui.warn(".python-version is found but the version is not in requires-python, ignored.") else: python_spec = requested for interpreter in self.find_interpreters(python_spec, search_venv): if filter_func is None or filter_func(interpreter): found = True yield interpreter if found or self.is_global: return if not python_spec: # handle both empty string and None # Get the best match meeting the requires-python best_match = self.get_best_matching_cpython_version() if best_match is None: return python_spec = str(best_match) else: try: if python_spec not in self.python_requires: return except InvalidVersion: return try: # otherwise if no interpreter is found, try to install it installed = InstallCommand.install_python(self, python_spec) except Exception as e: self.core.ui.error(f"Failed to install Python {python_spec}: {e}") return else: if filter_func is None or filter_func(installed): yield installed def find_interpreters( self, python_spec: str | None = None, search_venv: bool | None = None ) -> Iterable[PythonInfo]: """Return an iterable of interpreter paths that matches the given specifier, which can be: 1. a version specifier like 3.7 2. an absolute path 3. a short name like python3 4. None that returns all possible interpreters """ config = self.config python: str | Path | None = None finder_arg: str | None = None if not python_spec: if config.get("python.use_pyenv", True) and os.path.exists(PYENV_ROOT): pyenv_shim = os.path.join(PYENV_ROOT, "shims", "python3") if os.name == "nt": pyenv_shim += ".bat" if os.path.exists(pyenv_shim): yield PythonInfo.from_path(pyenv_shim) elif os.path.exists(pyenv_shim.replace("python3", "python")): yield PythonInfo.from_path(pyenv_shim.replace("python3", "python")) python = shutil.which("python") or shutil.which("python3") if python: yield PythonInfo.from_path(python) else: if not all(c.isdigit() for c in python_spec.split(".")): path = Path(python_spec) if path.exists(): python = find_python_in_path(python_spec) if python: yield PythonInfo.from_path(python) return if len(path.parts) == 1: # only check for spec with only one part python = shutil.which(python_spec) if python: yield PythonInfo.from_path(python) return finder_arg = python_spec if search_venv is None: search_venv = cast(bool, config["python.use_venv"]) finder = self._get_python_finder(search_venv) for entry in finder.find_all(finder_arg, allow_prereleases=True): yield PythonInfo(entry) if not python_spec: # Lastly, return the host Python as well this_python = getattr(sys, "_base_executable", sys.executable) yield PythonInfo.from_path(this_python) def _get_python_finder(self, search_venv: bool = True) -> Finder: from findpython import Finder from pdm.cli.commands.venv.utils import VenvProvider providers: list[str] = self.config["python.providers"] venv_pos = -1 if not providers: venv_pos = 0 elif "venv" in providers: venv_pos = providers.index("venv") providers.remove("venv") old_rye_root = os.getenv("RYE_PY_ROOT") os.environ["RYE_PY_ROOT"] = os.path.expanduser(self.config["python.install_root"]) try: finder = Finder(resolve_symlinks=True, selected_providers=providers or None) finally: if old_rye_root: # pragma: no cover os.environ["RYE_PY_ROOT"] = old_rye_root else: del os.environ["RYE_PY_ROOT"] if search_venv and venv_pos >= 0: finder.add_provider(VenvProvider(self), venv_pos) return finder @property def is_distribution(self) -> bool: if not self.name: return False settings = self.pyproject.settings if "package-type" in settings: return settings["package-type"] == "library" elif "distribution" in settings: return cast(bool, settings["distribution"]) else: return True def get_setting(self, key: str) -> Any: """ Get a setting from its dotted key (without the `tool.pdm` prefix). Returns `None` if the key does not exists. """ try: return reduce(operator.getitem, key.split("."), self.pyproject.settings) except KeyError: return None def env_or_setting(self, var: str, key: str) -> Any: """ Get a value from environment variable and fallback on a given setting. Returns `None` if both the environment variable and the key does not exists. """ return os.getenv(var.upper()) or self.get_setting(key) def get_best_matching_cpython_version(self, use_minimum: bool | None = False) -> PythonVersion | None: """ Returns the best matching cPython version that fits requires-python, this platform and arch. If no best match could be found, return None. Default for best match strategy is "highest" possible interpreter version. If "minimum" shall be used, set `use_minimum` to True. """ def get_version(version: PythonVersion) -> str: return f"{version.major}.{version.minor}.{version.micro}" all_matches = get_all_installable_python_versions(build_dir=False) filtered_matches = [ v for v in all_matches if get_version(v) in self.python_requires and v.implementation.lower() == "cpython" ] if filtered_matches: if use_minimum: return min(filtered_matches, key=lambda v: (v.major, v.minor, v.micro)) return max(filtered_matches, key=lambda v: (v.major, v.minor, v.micro)) return None @property def lock_targets(self) -> list[EnvSpec]: return [self.environment.allow_all_spec] def get_resolver(self, allow_uv: bool = True) -> type[Resolver]: """Get the resolver class to use for the project.""" from pdm.resolver.resolvelib import RLResolver from pdm.resolver.uv import UvResolver if allow_uv and self.config.get("use_uv"): return UvResolver else: return RLResolver def get_synchronizer(self, quiet: bool = False, allow_uv: bool = True) -> type[BaseSynchronizer]: """Get the synchronizer class to use for the project.""" from pdm.installers import BaseSynchronizer, Synchronizer, UvSynchronizer from pdm.installers.uv import QuietUvSynchronizer if allow_uv and self.config.get("use_uv"): return QuietUvSynchronizer if quiet else UvSynchronizer if quiet: return BaseSynchronizer return getattr(self.core, "synchronizer_class", Synchronizer) pdm-2.23.1/src/pdm/project/lockfile.py000066400000000000000000000104761477560627500175710ustar00rootroot00000000000000from __future__ import annotations import enum from functools import cached_property from typing import Any, Iterable, Mapping import tomlkit from pdm import termui from pdm.exceptions import PdmUsageError from pdm.project.toml_file import TOMLBase from pdm.utils import parse_version GENERATED_COMMENTS = [ "This file is @generated by PDM.", "It is not intended for manual editing.", ] FLAG_STATIC_URLS = "static_urls" FLAG_CROSS_PLATFORM = "cross_platform" FLAG_DIRECT_MINIMAL_VERSIONS = "direct_minimal_versions" FLAG_INHERIT_METADATA = "inherit_metadata" SUPPORTED_FLAGS = frozenset( (FLAG_STATIC_URLS, FLAG_CROSS_PLATFORM, FLAG_DIRECT_MINIMAL_VERSIONS, FLAG_INHERIT_METADATA) ) class Compatibility(enum.IntEnum): NONE = 0 # The lockfile can't be read by the current version of PDM. SAME = 1 # The lockfile version is the same as the current version of PDM. BACKWARD = 2 # The current version of PDM is newer than the lockfile version. FORWARD = 3 # The current version of PDM is older than the lockfile version. class Lockfile(TOMLBase): spec_version = parse_version("4.5.0") @cached_property def default_strategies(self) -> set[str]: return {FLAG_INHERIT_METADATA} @property def hash(self) -> str: return self._data.get("metadata", {}).get("content_hash", "") @property def file_version(self) -> str: return self._data.get("metadata", {}).get("lock_version", "") @property def groups(self) -> list[str] | None: return self._data.get("metadata", {}).get("groups") @property def strategy(self) -> set[str]: metadata = self._data.get("metadata", {}) if not metadata: return self.default_strategies.copy() result: set[str] = set(metadata.get("strategy", {FLAG_CROSS_PLATFORM})) # Compatibility with old lockfiles if not metadata.get(FLAG_CROSS_PLATFORM, True): result.discard(FLAG_CROSS_PLATFORM) if metadata.get(FLAG_STATIC_URLS, False): result.add(FLAG_STATIC_URLS) return result & SUPPORTED_FLAGS def apply_strategy_change(self, changes: Iterable[str]) -> set[str]: original = self.strategy for change in changes: change = change.replace("-", "_").lower() if change.startswith("no_"): if change[3:] not in SUPPORTED_FLAGS: raise PdmUsageError(f"Invalid strategy flag: {change[3:]}, supported: {', '.join(SUPPORTED_FLAGS)}") original.discard(change[3:]) else: if change not in SUPPORTED_FLAGS: raise PdmUsageError(f"Invalid strategy flag: {change}, supported: {', '.join(SUPPORTED_FLAGS)}") original.add(change) return original def compare_groups(self, groups: Iterable[str]) -> list[str]: if not self.groups: return [] return list(set(groups).difference(self.groups)) def set_data(self, data: Mapping[str, Any]) -> None: self._data = tomlkit.document() for line in GENERATED_COMMENTS: self._data.append(None, tomlkit.comment(line)) self._data.update(data) def write(self, show_message: bool = True) -> None: super().write() if show_message: self.ui.echo(f"Changes are written to [success]{self._path.name}[/].", verbosity=termui.Verbosity.NORMAL) def __getitem__(self, key: str) -> dict: return self._data[key] def compatibility(self) -> Compatibility: """We use a three-part versioning scheme for lockfiles: The first digit represents backward compatibility and the second digit represents forward compatibility. """ if not self.exists(): return Compatibility.SAME if not self.file_version: return Compatibility.NONE lockfile_version = parse_version(self.file_version) if lockfile_version == self.spec_version: return Compatibility.SAME if lockfile_version.major != self.spec_version.major or lockfile_version.minor > self.spec_version.minor: return Compatibility.NONE if lockfile_version.minor < self.spec_version.minor: return Compatibility.BACKWARD return Compatibility.BACKWARD if lockfile_version.micro < self.spec_version.micro else Compatibility.FORWARD pdm-2.23.1/src/pdm/project/project_file.py000066400000000000000000000106021477560627500204350ustar00rootroot00000000000000from __future__ import annotations import hashlib import json from typing import Any, Mapping, cast from tomlkit import TOMLDocument, items from pdm import termui from pdm.exceptions import ProjectError from pdm.project.toml_file import TOMLBase from pdm.utils import normalize_name def _remove_empty_tables(doc: dict) -> None: for k, v in list(doc.items()): if isinstance(v, dict): _remove_empty_tables(v) if not v: del doc[k] class PyProject(TOMLBase): """The data object representing th pyproject.toml file""" def read(self) -> TOMLDocument: from pdm.formats import flit, poetry data = super().read() if "project" not in data and self._path.exists(): # Try converting from flit and poetry for converter in (flit, poetry): if converter.check_fingerprint(None, self._path): metadata, settings = converter.convert(None, self._path, None) data["project"] = metadata if settings: data.setdefault("tool", {}).setdefault("pdm", {}).update(settings) break return data def write(self, show_message: bool = True) -> None: """Write the TOMLDocument to the file.""" _remove_empty_tables(self._data.get("project", {})) if "tool" in self._data: tool_table = cast(dict, self._data["tool"]) _remove_empty_tables(tool_table.get("pdm", {})) if "pdm" in tool_table and not tool_table["pdm"]: del tool_table["pdm"] if not tool_table: del self._data["tool"] if "dependency-groups" in self._data and not self.dependency_groups: del self._data["dependency-groups"] super().write() if show_message: self.ui.echo("Changes are written to [success]pyproject.toml[/].", verbosity=termui.Verbosity.NORMAL) @property def is_valid(self) -> bool: return bool(self._data.get("project")) @property def metadata(self) -> items.Table: return self._data.setdefault("project", {}) @property def dependency_groups(self) -> items.Table: return self._data.setdefault("dependency-groups", {}) @property def dev_dependencies(self) -> dict[str, list[Any]]: groups: dict[str, list[Any]] = {} for group, deps in self._data.get("dependency-groups", {}).items(): group = normalize_name(group) if group in groups: raise ProjectError(f"The group {group} is duplicated in dependency-groups") groups[group] = deps.unwrap() if hasattr(deps, "unwrap") else deps for group, deps in self.settings.get("dev-dependencies", {}).items(): group = normalize_name(group) groups.setdefault(group, []).extend(deps.unwrap() if hasattr(deps, "unwrap") else deps) return groups @property def settings(self) -> items.Table: return self._data.setdefault("tool", {}).setdefault("pdm", {}) @property def build_system(self) -> dict: return self._data.get("build-system", {}) @property def resolution(self) -> Mapping[str, Any]: """A compatible getter method for the resolution overrides in the pyproject.toml file. """ return self.settings.get("resolution", {}) @property def allow_prereleases(self) -> bool | None: return self.resolution.get("allow-prereleases") def content_hash(self, algo: str = "sha256") -> str: """Generate a hash of the sensible content of the pyproject.toml file. When the hash changes, it means the project needs to be relocked. """ dump_data = { "sources": self.settings.get("source", []), "dependencies": self.metadata.get("dependencies", []), "dev-dependencies": self.dev_dependencies, "optional-dependencies": self.metadata.get("optional-dependencies", {}), "requires-python": self.metadata.get("requires-python", ""), "resolution": self.resolution, } pyproject_content = json.dumps(dump_data, sort_keys=True) hasher = hashlib.new(algo) hasher.update(pyproject_content.encode("utf-8")) return hasher.hexdigest() @property def plugins(self) -> list[str]: return self.settings.get("plugins", []) pdm-2.23.1/src/pdm/project/toml_file.py000066400000000000000000000020561477560627500177460ustar00rootroot00000000000000from __future__ import annotations from pathlib import Path from typing import Any, Mapping import tomlkit from tomlkit.toml_document import TOMLDocument from tomlkit.toml_file import TOMLFile from pdm import termui class TOMLBase(TOMLFile): def __init__(self, path: str | Path, *, ui: termui.UI) -> None: super().__init__(path) self._path = Path(path) self.ui = ui self._data = self.read() def read(self) -> TOMLDocument: if not self._path.exists(): return tomlkit.document() return super().read() def set_data(self, data: Mapping[str, Any]) -> None: """Set the data of the TOML file.""" self._data = tomlkit.document() self._data.update(data) def reload(self) -> None: self._data = self.read() def write(self) -> None: self._path.parent.mkdir(parents=True, exist_ok=True) return super().write(self._data) def exists(self) -> bool: return self._path.exists() def empty(self) -> bool: return not self._data pdm-2.23.1/src/pdm/py.typed000066400000000000000000000000001477560627500154360ustar00rootroot00000000000000pdm-2.23.1/src/pdm/pytest.py000066400000000000000000000513101477560627500156530ustar00rootroot00000000000000""" Some reusable fixtures for `pytest`. +++ 2.4.0 To enable them in your test, add `pdm.pytest` as a plugin. You can do so in your root `conftest.py`: ```python title="conftest.py" # single plugin pytest_plugins = "pytest.plugin" # many plugins pytest_plugins = [ ... "pdm.pytest", ... ] ``` """ from __future__ import annotations import collections.abc import json import os import shutil import sys from dataclasses import dataclass from io import StringIO from pathlib import Path from typing import ( TYPE_CHECKING, Any, Callable, Generator, Iterable, Iterator, Mapping, MutableMapping, Union, cast, ) import httpx import pytest from httpx._content import IteratorByteStream from pytest_mock import MockerFixture from unearth import Link from pdm.core import Core from pdm.environments import BaseEnvironment, PythonEnvironment from pdm.exceptions import CandidateInfoNotFound from pdm.installers.installers import install_wheel from pdm.models.backends import DEFAULT_BACKEND from pdm.models.candidates import Candidate from pdm.models.repositories import BaseRepository, CandidateMetadata from pdm.models.requirements import ( Requirement, filter_requirements_with_extras, parse_requirement, ) from pdm.models.session import PDMPyPIClient from pdm.project.config import Config from pdm.project.core import Project from pdm.utils import find_python_in_path, normalize_name, parse_version if TYPE_CHECKING: from typing import Protocol from _pytest.fixtures import SubRequest from pdm._types import FileHash class FileByteStream(IteratorByteStream): def close(self) -> None: self._stream.close() # type: ignore[attr-defined] class LocalIndexTransport(httpx.BaseTransport): """ A local file transport for HTTPX. Allows to mock some HTTP requests with some local files """ def __init__( self, aliases: dict[str, Path], overrides: IndexOverrides | None = None, strip_suffix: bool = False, ): super().__init__() self.aliases = sorted(aliases.items(), key=lambda item: len(item[0]), reverse=True) self.overrides = overrides if overrides is not None else {} self.strip_suffix = strip_suffix def get_file_path(self, path: str) -> Path | None: for prefix, base_path in self.aliases: if path.startswith(prefix): file_path = base_path / path[len(prefix) :].lstrip("/") if not self.strip_suffix: return file_path return next( (p for p in file_path.parent.iterdir() if p.stem == file_path.name), None, ) return None def handle_request(self, request: httpx.Request) -> httpx.Response: request_path = request.url.path file_path = self.get_file_path(request_path) headers: dict[str, str] = {} stream: httpx.SyncByteStream | None = None content: bytes | None = None if request_path in self.overrides: status_code = 200 content = self.overrides[request_path] headers["Content-Type"] = "text/html" elif file_path is None or not file_path.exists(): status_code = 404 else: status_code = 200 stream = FileByteStream(file_path.open("rb")) if file_path.suffix == ".html": headers["Content-Type"] = "text/html" elif file_path.suffix == ".json": headers["Content-Type"] = "application/vnd.pypi.simple.v1+json" return httpx.Response(status_code, headers=headers, content=content, stream=stream) class RepositoryData: def __init__(self, pypi_json: Path) -> None: self.pypi_data = self.load_fixtures(pypi_json) @staticmethod def load_fixtures(pypi_json: Path) -> dict[str, Any]: return json.loads(pypi_json.read_text()) def add_candidate(self, name: str, version: str, requires_python: str = "") -> None: pypi_data = self.pypi_data.setdefault(normalize_name(name), {}).setdefault(version, {}) pypi_data["requires_python"] = requires_python def add_dependencies(self, name: str, version: str, requirements: list[str]) -> None: pypi_data = self.pypi_data[normalize_name(name)][version] pypi_data.setdefault("dependencies", []).extend(requirements) def get_raw_dependencies(self, candidate: Candidate) -> tuple[str, list[str]]: try: pypi_data = self.pypi_data[cast(str, candidate.req.key)] for version, data in sorted(pypi_data.items(), key=lambda item: len(item[0])): base, *_ = version.partition("+") if candidate.version in (version, base): return version, data.get("dependencies", []) except KeyError: pass assert candidate.prepared is not None meta = candidate.prepared.metadata return meta.version, meta.requires or [] class TestRepository(BaseRepository): """ A mock repository to ease testing dependencies """ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self._pypi_data = self.get_data() def get_data(self) -> dict[str, Any]: raise NotImplementedError("To be injected by the fixture.") def _get_dependencies_from_fixture(self, candidate: Candidate) -> CandidateMetadata: try: pypi_data = self._pypi_data[cast(str, candidate.req.key)][cast(str, candidate.version)] except KeyError: raise CandidateInfoNotFound(candidate) from None deps = pypi_data.get("dependencies", []) deps = filter_requirements_with_extras(deps, candidate.req.extras or ()) return CandidateMetadata(deps, pypi_data.get("requires_python", ""), "") def dependency_generators(self) -> Iterable[Callable[[Candidate], CandidateMetadata]]: return ( self._get_dependencies_from_cache, self._get_dependencies_from_fixture, self._get_dependencies_from_metadata, ) def get_hashes(self, candidate: Candidate) -> list[FileHash]: return [] def _find_candidates(self, requirement: Requirement, minimal_version: bool) -> Iterable[Candidate]: for version, candidate in sorted( self._pypi_data.get(cast(str, requirement.key), {}).items(), key=lambda item: parse_version(item[0]), reverse=not minimal_version, ): c = Candidate( requirement, name=requirement.project_name, version=version, ) c.requires_python = candidate.get("requires_python", "") c.link = Link(f"https://mypypi.org/packages/{c.name}-{c.version}.tar.gz") yield c class Metadata(dict): def get_all(self, name: str, fallback: list[str] | None = None) -> list[str] | None: return [self[name]] if name in self else fallback def __getitem__(self, __key: str) -> str: return cast(str, dict.get(self, __key)) class Distribution: """A mock Distribution""" def __init__( self, key: str, version: str, editable: bool = False, metadata: Metadata | None = None, ): self.version = version self.link_file = "editable" if editable else None self.dependencies: list[str] = [] self._metadata = {"Name": key, "Version": version} if metadata: self._metadata.update(metadata) self.name = key @property def metadata(self) -> Metadata: return Metadata(self._metadata) def as_req(self) -> Requirement: return parse_requirement(f"{self.name}=={self.version}") @property def requires(self) -> list[str]: return self.dependencies def read_text(self, path: Path | str) -> None: return None class MockWorkingSet(collections.abc.MutableMapping): """A mock working set""" _data: dict[str, Distribution] def __init__(self, *args: Any, **kwargs: Any) -> None: self._data = {} def add_distribution(self, dist: Distribution) -> None: self._data[dist.name] = dist def is_owned(self, key: str) -> bool: return key in self._data def __getitem__(self, key: str) -> Distribution: return self._data[key] def __len__(self) -> int: return len(self._data) def __iter__(self) -> Iterator[str]: return iter(self._data) def __setitem__(self, key: str, value: Distribution) -> None: self._data[key] = value def __delitem__(self, key: str) -> None: del self._data[key] # Note: # When going through pytest assertions rewrite, the future annotations is ignored. # As a consequence, type definition must comply with Python 3.7 syntax IndexMap = dict[str, Path] """Path some root-relative http paths to some local paths""" IndexOverrides = dict[str, bytes] """PyPI indexes overrides fixture format""" IndexesDefinition = dict[str, Union[tuple[IndexMap, IndexOverrides, bool], IndexMap]] """Mock PyPI indexes format""" @pytest.fixture(scope="session") def build_env_wheels() -> Iterable[Path]: """ Expose some wheels to be installed in the build environment. Override to provide your owns. Returns: a list of wheels paths to install """ return [] @pytest.fixture(autouse=True) def temp_env(monkeypatch: pytest.MonkeyPatch) -> Generator[MutableMapping[str, str]]: old_env = os.environ.copy() monkeypatch.setattr("pdm.models.candidates.PreparedCandidate._build_dir_cache", {}) try: yield os.environ finally: os.environ.clear() os.environ.update(old_env) @pytest.fixture(scope="session") def build_env(build_env_wheels: Iterable[Path], tmp_path_factory: pytest.TempPathFactory) -> Path: """ A fixture build environment Args: build_env_wheels: a list of wheel to install in the environment Returns: The build environment temporary path """ d = tmp_path_factory.mktemp("pdm-test-env") p = Core().create_project(d) env = PythonEnvironment(p, prefix=str(d), python=sys.executable) for wheel in build_env_wheels: install_wheel(wheel, env, requested=True) return d @pytest.fixture def pypi_indexes() -> IndexesDefinition: """ Provides some mocked PyPI entries Returns: a definition of the mocked indexes """ return {} _build_session = BaseEnvironment._build_session @pytest.fixture def build_test_session(pypi_indexes: IndexesDefinition) -> Callable[..., PDMPyPIClient]: def get_pypi_session(*args: Any, **kwargs: Any) -> PDMPyPIClient: mounts: dict[str, httpx.BaseTransport] = {} for root, specs in pypi_indexes.items(): index, overrides, strip = specs if isinstance(specs, tuple) else (specs, None, False) mounts[root] = LocalIndexTransport(index, overrides=overrides, strip_suffix=strip) kwargs["mounts"] = mounts return _build_session(*args, **kwargs) return get_pypi_session def remove_pep582_path_from_pythonpath(pythonpath: str) -> str: """Remove all pep582 paths of PDM from PYTHONPATH""" paths = pythonpath.split(os.pathsep) paths = [path for path in paths if "pdm/pep582" not in path] return os.pathsep.join(paths) @pytest.fixture def core() -> Iterator[Core]: old_config_map = Config._config_map.copy() # Turn off use_venv by default, for testing Config._config_map["python.use_venv"].default = False main = Core() with main.exit_stack: yield main # Restore the config items Config._config_map = old_config_map @pytest.fixture def project_no_init( tmp_path: Path, mocker: MockerFixture, core: Core, build_test_session: Callable[..., PDMPyPIClient], monkeypatch: pytest.MonkeyPatch, build_env: Path, ) -> Project: """ A fixture creating a non-initialized test project for the current test. Returns: The non-initialized project """ test_home = tmp_path / ".pdm-home" test_home.mkdir(parents=True) test_home.joinpath("config.toml").write_text( '[global_project]\npath = "{}"\n'.format(test_home.joinpath("global-project").as_posix()) ) p = core.create_project(tmp_path, global_config=test_home.joinpath("config.toml").as_posix()) p.global_config["python.install_root"] = str(tmp_path / "pythons") p.global_config["venv.location"] = str(tmp_path / "venvs") mocker.patch.object(BaseEnvironment, "_build_session", build_test_session) mocker.patch("pdm.builders.base.EnvBuilder.get_shared_env", return_value=str(build_env)) tmp_path.joinpath("caches").mkdir(parents=True) p.global_config["cache_dir"] = tmp_path.joinpath("caches").as_posix() p.global_config["log_dir"] = tmp_path.joinpath("logs").as_posix() python_path = find_python_in_path(sys.base_prefix) if python_path is None: raise ValueError("Unable to find a Python path") p._saved_python = python_path.as_posix() monkeypatch.delenv("VIRTUAL_ENV", raising=False) monkeypatch.delenv("CONDA_PREFIX", raising=False) monkeypatch.delenv("PEP582_PACKAGES", raising=False) monkeypatch.delenv("NO_SITE_PACKAGES", raising=False) pythonpath = os.getenv("PYTHONPATH", "") pythonpath = remove_pep582_path_from_pythonpath(pythonpath) if pythonpath: monkeypatch.setenv("PYTHONPATH", pythonpath) return p @pytest.fixture def project(project_no_init: Project) -> Project: """ A fixture creating an initialized test project for the current test. Returns: The initialized project """ from pdm.cli.utils import merge_dictionary data = { "project": { "name": "test-project", "version": "0.0.0", "description": "", "authors": [], "license": {"text": "MIT"}, "dependencies": [], "requires-python": ">=3.7", }, "build-system": DEFAULT_BACKEND.build_system(), } merge_dictionary(project_no_init.pyproject._data, data) project_no_init.pyproject.write() # Clean the cached property project_no_init._environment = None return project_no_init @pytest.fixture def working_set(mocker: MockerFixture, repository: RepositoryData) -> MockWorkingSet: """ a mock working set as a fixture Returns: a mock working set """ from pdm.installers import InstallManager rv = MockWorkingSet() mocker.patch.object(BaseEnvironment, "get_working_set", return_value=rv) class MockInstallManager(InstallManager): def install(self, candidate: Candidate) -> Distribution: # type: ignore[override] key = normalize_name(candidate.name or "") candidate.prepare(self.environment) version, dependencies = repository.get_raw_dependencies(candidate) dist = Distribution(key, version, candidate.req.editable) dist.dependencies = dependencies rv.add_distribution(dist) return dist def uninstall(self, dist: Distribution) -> None: # type: ignore[override] del rv[dist.name] def overwrite(self, dist: Distribution, candidate: Candidate) -> None: # type: ignore[override] self.uninstall(dist) self.install(candidate) mocker.patch.object(Core, "install_manager_class", MockInstallManager) return rv @pytest.fixture def local_finder_artifacts() -> Path: """ The local finder search path as a fixture Override to provides your own artifacts. Returns: The path to the artifacts root """ return Path() @pytest.fixture def local_finder(project_no_init: Project, local_finder_artifacts: Path) -> None: project_no_init.pyproject.settings["source"] = [ { "type": "find_links", "verify_ssl": False, "url": local_finder_artifacts.as_uri(), "name": "pypi", } ] project_no_init.pyproject.write() @pytest.fixture def repository_pypi_json() -> Path: """ The test repository fake PyPI definition path as a fixture Override to provides your own definition path. Returns: The path to a fake PyPI repository JSON definition """ return Path() @pytest.fixture() def repository( core: Core, mocker: MockerFixture, repository_pypi_json: Path, local_finder: type[None], ) -> RepositoryData: """ A fixture providing a mock PyPI repository Returns: A mock repository """ repo = RepositoryData(repository_pypi_json) core.repository_class = TestRepository mocker.patch.object(TestRepository, "get_data", return_value=repo.pypi_data) return repo @dataclass class RunResult: """ Store a command execution result. """ exit_code: int """The execution exit code""" stdout: str """The execution `stdout` output""" stderr: str """The execution `stderr` output""" exception: Exception | None = None """If set, the exception raised on execution""" @property def output(self) -> str: """The execution `stdout` output (`stdout` alias)""" return self.stdout @property def outputs(self) -> str: """The execution `stdout` and `stderr` outputs concatenated""" return self.stdout + self.stderr def print(self) -> None: """A debugging facility""" print("# exit code:", self.exit_code) print("# stdout:", self.stdout, sep="\n") print("# stderr:", self.stderr, sep="\n") if TYPE_CHECKING: class PDMCallable(Protocol): """The PDM fixture callable signature""" def __call__( self, args: str | list[str], strict: bool = False, input: str | None = None, obj: Project | None = None, env: Mapping[str, str] | None = None, cleanup: bool = True, **kwargs: Any, ) -> RunResult: """ Args: args: the command arguments as a single lexable string or a strings array strict: raise an exception on failure instead of returning if enabled input: an optional string to be submitted too `stdin` obj: an optional existing `Project`. env: override the environment variables with those Returns: The command result """ ... @pytest.fixture def pdm(core: Core, monkeypatch: pytest.MonkeyPatch) -> PDMCallable: """ A fixture allowing to execute PDM commands Returns: A `pdm` fixture command. """ # Hide the spinner text from testing output to not break existing tests monkeypatch.setattr("pdm.termui.DummySpinner._show", lambda self: None) def caller( args: str | list[str], strict: bool = False, input: str | None = None, obj: Project | None = None, env: Mapping[str, str] | None = None, cleanup: bool = True, **kwargs: Any, ) -> RunResult: __tracebackhide__ = True stdin = StringIO(input) stdout = StringIO() stderr = StringIO() exit_code: int = 0 exception: Exception | None = None args = args.split() if isinstance(args, str) else args with monkeypatch.context() as m: old_env = os.environ.copy() m.setattr("sys.stdin", stdin) m.setattr("sys.stdout", stdout) m.setattr("sys.stderr", stderr) for key, value in (env or {}).items(): m.setenv(key, value) try: core.main(args, "pdm", obj=obj, **kwargs) except SystemExit as e: exit_code = cast(int, e.code) except Exception as e: exit_code = 1 exception = e finally: os.environ.clear() os.environ.update(old_env) if cleanup: core.exit_stack.close() result = RunResult(exit_code, stdout.getvalue(), stderr.getvalue(), exception) if strict and result.exit_code != 0: if result.exception: raise result.exception.with_traceback(result.exception.__traceback__) raise RuntimeError(f"Call command {args} failed({result.exit_code}): {result.stderr}") return result return caller VENV_BACKENDS = ["virtualenv", "venv"] @pytest.fixture(params=VENV_BACKENDS) def venv_backends(project: Project, request: SubRequest) -> None: """A fixture iterating over `venv` backends""" project.project_config["venv.backend"] = request.param project.project_config["venv.prompt"] = "{project_name}-{python_version}" project.project_config["python.use_venv"] = True shutil.rmtree(project.root / "__pypackages__", ignore_errors=True) pdm-2.23.1/src/pdm/resolver/000077500000000000000000000000001477560627500156125ustar00rootroot00000000000000pdm-2.23.1/src/pdm/resolver/__init__.py000066400000000000000000000002151477560627500177210ustar00rootroot00000000000000from .base import Resolver from .resolvelib import RLResolver from .uv import UvResolver __all__ = ["RLResolver", "Resolver", "UvResolver"] pdm-2.23.1/src/pdm/resolver/base.py000066400000000000000000000041521477560627500171000ustar00rootroot00000000000000from __future__ import annotations import abc import typing as t from dataclasses import dataclass, field from resolvelib import BaseReporter from pdm.models.candidates import Candidate from pdm.models.repositories import LockedRepository if t.TYPE_CHECKING: from pdm.environments import BaseEnvironment from pdm.models.markers import EnvSpec from pdm.models.repositories import Package from pdm.models.requirements import Requirement from pdm.project import Project class Resolution(t.NamedTuple): """The resolution result.""" packages: t.Iterable[Package] """The list of pinned packages with dependencies.""" collected_groups: set[str] """The list of collected groups.""" @property def candidates(self) -> dict[str, Candidate]: return {entry.candidate.identify(): entry.candidate for entry in self.packages} @dataclass class Resolver(abc.ABC): """The resolver class.""" environment: BaseEnvironment """The environment instance.""" requirements: list[Requirement] """The list of requirements to resolve.""" update_strategy: str """The update strategy to use [all|reuse|eager|reuse-installed].""" strategies: set[str] """The list of strategies to use.""" target: EnvSpec """The target environment specification.""" tracked_names: t.Collection[str] = () """The list of tracked names.""" keep_self: bool = False """Whether to keep self dependencies.""" locked_repository: LockedRepository | None = None """The repository with all locked dependencies.""" reporter: BaseReporter = field(default_factory=BaseReporter) """The reporter to use.""" requested_groups: set[str] = field(default_factory=set, init=False) """The list of requested groups.""" def __post_init__(self) -> None: self.requested_groups = {g for r in self.requirements for g in r.groups} @abc.abstractmethod def resolve(self) -> Resolution: """Resolve the requirements.""" pass @property def project(self) -> Project: """The project instance.""" return self.environment.project pdm-2.23.1/src/pdm/resolver/graph.py000066400000000000000000000111351477560627500172660ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING, AbstractSet, Iterable, Iterator, TypeVar, overload from pdm.models.markers import Marker, get_marker if TYPE_CHECKING: from resolvelib.resolvers import Criterion, Result from pdm.models.candidates import Candidate from pdm.models.requirements import Requirement T = TypeVar("T") class OrderedSet(AbstractSet[T]): """Set with deterministic ordering.""" __slots__ = "_data" def __init__(self, iterable: Iterable[T] = ()) -> None: self._data = dict.fromkeys(iterable) def __hash__(self) -> int: return self._hash() def __repr__(self) -> str: return f"{self.__class__.__name__}({self})" def __str__(self) -> str: return f"{{{', '.join(map(repr, self._data))}}}" def __contains__(self, obj: object) -> bool: return obj in self._data def __iter__(self) -> Iterator[T]: return iter(self._data) def __len__(self) -> int: return len(self._data) @overload def _identify_parent(parent: None) -> None: ... @overload def _identify_parent(parent: Candidate) -> str: ... def _identify_parent(parent: Candidate | None) -> str | None: return parent.identify() if parent else None def merge_markers(result: Result[Requirement, Candidate, str]) -> dict[str, Marker]: """Traverse through the parent dependencies till the top and merge any requirement markers on the path. Return a map of Metaset for each candidate. """ all_markers: dict[str, Marker] = {} unresolved = OrderedSet(result.mapping) circular: dict[str, OrderedSet[str]] = {} while unresolved: new_markers: dict[str, Marker] = {} for k in unresolved: crit = result.criteria[k] keep_unresolved = circular.get(k) or OrderedSet() # All parents must be resolved first if any(p and _identify_parent(p) in (unresolved - keep_unresolved) for p in crit.iter_parent()): continue new_markers[k] = _build_marker(crit, all_markers, keep_unresolved) if new_markers: all_markers.update(new_markers) unresolved -= new_markers # type: ignore[assignment,operator] else: # No progress, there are likely circular dependencies. # Pick one package and keep its parents unresolved now, we will get into it # after all others are resolved. package = next((p for p in unresolved if p not in circular), None) if not package: break crit = result.criteria[package] unresolved_parents = OrderedSet( filter( lambda p: p in unresolved and p != package, (_identify_parent(p) for p in crit.iter_parent() if p), ) ) circular[package] = unresolved_parents for key in circular: crit = result.criteria[key] all_markers[key] = _build_marker(crit, all_markers, set()) return all_markers def _build_marker( crit: Criterion[Requirement, Candidate, str], resolved: dict[str, Marker], keep_unresolved: AbstractSet[str] ) -> Marker: marker = None for r, parent in crit.information: if parent and ((k := _identify_parent(parent)) in keep_unresolved or k not in resolved): continue this_marker = r.marker if r.marker is not None else get_marker("") # Use 'and' to connect markers inherited from parent. if not parent: parent_marker = get_marker("") else: parent_marker = resolved[_identify_parent(parent)] merged = this_marker & parent_marker # Use 'or' to connect metasets inherited from different parents. marker = (marker | merged) if marker is not None else merged # type: ignore[operator] return marker if marker is not None else get_marker("") def populate_groups(result: Result[Requirement, Candidate, str]) -> None: """Find where the candidates come from by traversing the dependency tree back to the top. """ resolved: dict[str, set[str]] = {} def get_candidate_groups(key: str) -> set[str]: if key in resolved: return resolved[key] res = resolved[key] = set() crit = result.criteria[key] for req, parent in crit.information: res.update(req.groups) if parent is not None: pkey = _identify_parent(parent) res.update(get_candidate_groups(pkey)) return res for k, can in result.mapping.items(): can.req.groups = sorted(get_candidate_groups(k)) pdm-2.23.1/src/pdm/resolver/providers.py000066400000000000000000000500451477560627500202050ustar00rootroot00000000000000from __future__ import annotations import dataclasses import os from functools import cached_property from typing import TYPE_CHECKING, Callable from packaging.specifiers import InvalidSpecifier from packaging.version import InvalidVersion from resolvelib import AbstractProvider, RequirementsConflicted from resolvelib.resolvers import Criterion from pdm.exceptions import InvalidPyVersion, RequirementError from pdm.models.candidates import Candidate from pdm.models.repositories import LockedRepository from pdm.models.requirements import FileRequirement, Requirement, parse_requirement, strip_extras from pdm.models.specifiers import PySpecSet from pdm.resolver.python import PythonCandidate, PythonRequirement, find_python_matches, is_python_satisfied_by from pdm.termui import logger from pdm.utils import ( deprecation_warning, get_requirement_from_override, normalize_name, parse_version, url_without_fragments, ) if TYPE_CHECKING: from typing import Any, Iterable, Iterator, Mapping, Sequence, TypeVar from resolvelib.resolvers import RequirementInformation from pdm._types import Comparable from pdm.models.repositories import BaseRepository from pdm.models.requirements import Requirement ProviderT = TypeVar("ProviderT", bound="type[BaseProvider]") _PROVIDER_REGISTRY: dict[str, type[BaseProvider]] = {} def get_provider(strategy: str) -> type[BaseProvider]: return _PROVIDER_REGISTRY[strategy] def register_provider(strategy: str) -> Callable[[ProviderT], ProviderT]: def wrapper(cls: ProviderT) -> ProviderT: _PROVIDER_REGISTRY[strategy] = cls return cls return wrapper @register_provider("all") class BaseProvider(AbstractProvider[Requirement, Candidate, str]): def __init__( self, repository: BaseRepository, allow_prereleases: bool | None = None, overrides: dict[str, str] | None = None, direct_minimal_versions: bool = False, *, locked_candidates: dict[str, list[Candidate]], ) -> None: if overrides is not None: # pragma: no cover deprecation_warning( "The `overrides` argument is deprecated and will be removed in the future.", stacklevel=2 ) if allow_prereleases is not None: # pragma: no cover deprecation_warning( "The `allow_prereleases` argument is deprecated and will be removed in the future.", stacklevel=2 ) project = repository.environment.project self.repository = repository self.allow_prereleases = project.pyproject.allow_prereleases # Root allow_prereleases value self.fetched_dependencies: dict[tuple[str, str | None], list[Requirement]] = {} self.excludes = {normalize_name(k) for k in project.pyproject.resolution.get("excludes", [])} self.direct_minimal_versions = direct_minimal_versions self.locked_candidates = locked_candidates def requirement_preference(self, requirement: Requirement) -> Comparable: """Return the preference of a requirement to find candidates. - Editable requirements are preferred. - File links are preferred. - The one with narrower specifierset is preferred. """ editable = requirement.editable is_named = requirement.is_named is_pinned = requirement.is_pinned is_prerelease = bool(requirement.prerelease) and bool(requirement.specifier.prereleases) specifier_parts = len(requirement.specifier) return (not editable, is_named, not is_pinned, not is_prerelease, -specifier_parts) def identify(self, requirement_or_candidate: Requirement | Candidate) -> str: return requirement_or_candidate.identify() def get_preference( self, identifier: str, resolutions: dict[str, Candidate], candidates: dict[str, Iterator[Candidate]], information: dict[str, Iterator[RequirementInformation]], backtrack_causes: Sequence[RequirementInformation], ) -> tuple[Comparable, ...]: is_top = any(parent is None for _, parent in information[identifier]) backtrack_identifiers = {req.identify() for req, _ in backtrack_causes} | { parent.identify() for _, parent in backtrack_causes if parent is not None } # Use the REAL identifier as it may be updated after candidate preparation. deps: list[Requirement] = [] for candidate in candidates[identifier]: try: deps = self.get_dependencies(candidate) except RequirementsConflicted: continue break is_backtrack_cause = any(dep.identify() in backtrack_identifiers for dep in deps) is_file_or_url = any(not requirement.is_named for requirement, _ in information[identifier]) operators = [spec.operator for req, _ in information[identifier] for spec in req.specifier] is_python = identifier == "python" is_pinned = any(op[:2] == "==" for op in operators) constraints = len(operators) return ( not is_python, not is_top, not is_file_or_url, not is_pinned, not is_backtrack_cause, -constraints, identifier, ) @cached_property def overrides(self) -> dict[str, Requirement]: """A mapping of package name to the requirement for overriding.""" from pdm.formats.requirements import RequirementParser project_overrides: dict[str, str] = { normalize_name(k): v for k, v in self.repository.environment.project.pyproject.resolution.get("overrides", {}).items() } requirements: dict[str, Requirement] = {} for name, value in project_overrides.items(): req = get_requirement_from_override(name, value) r = parse_requirement(req) requirements[r.identify()] = r # Read from --override files parser = RequirementParser(self.repository.environment.session) for override_file in self.repository.environment.project.core.state.overrides: parser.parse_file(override_file) for r in parser.requirements: # There might be duplicates, we only keep the last one requirements[r.identify()] = r return requirements def _is_direct_requirement(self, requirement: Requirement) -> bool: from itertools import chain project = self.repository.environment.project all_dependencies = chain.from_iterable(project.all_dependencies.values()) return any(r.is_named and requirement.identify() == r.identify() for r in all_dependencies) def _find_candidates(self, requirement: Requirement) -> Iterable[Candidate]: if not requirement.is_named and not isinstance(self.repository, LockedRepository): can = Candidate(requirement) if not can.name: can.prepare(self.repository.environment).metadata yield can else: prerelease = requirement.prerelease if prerelease is None and requirement.is_pinned and requirement.specifier.prereleases: prerelease = True if prerelease is None and (key := requirement.identify()) in self.locked_candidates: # keep the prerelease if it is locked candidates = self.locked_candidates[key] for candidate in candidates: if candidate.version is not None: try: parsed_version = parse_version(candidate.version) except InvalidVersion: # pragma: no cover pass else: if parsed_version.is_prerelease: prerelease = True break found = self.repository.find_candidates( requirement, self.allow_prereleases if prerelease is None else prerelease, minimal_version=self.direct_minimal_versions and self._is_direct_requirement(requirement), ) current_version: str | None = None collected_wheels: list[Candidate] = [] collected_others: list[Candidate] = [] for candidate in found: assert candidate.version is not None if current_version is None: current_version = candidate.version if candidate.version != current_version: # If there are wheels for the given version, we should only return wheels # to avoid build steps. if collected_wheels: yield from collected_wheels else: yield from collected_others current_version = candidate.version collected_wheels.clear() collected_others.clear() if candidate.link and candidate.link.is_wheel: collected_wheels.append(candidate) else: collected_others.append(candidate) if collected_wheels: yield from collected_wheels else: yield from collected_others def find_matches( self, identifier: str, requirements: Mapping[str, Iterator[Requirement]], incompatibilities: Mapping[str, Iterator[Candidate]], ) -> Callable[[], Iterator[Candidate]]: def matches_gen() -> Iterator[Candidate]: incompat = list(incompatibilities[identifier]) if identifier == "python": candidates = find_python_matches(identifier, requirements) return (c for c in candidates if c not in incompat) elif identifier in self.overrides: return iter(self._find_candidates(self.overrides[identifier])) else: name, extras = strip_extras(identifier) if name in self.overrides: req = dataclasses.replace(self.overrides[name], extras=extras) return iter(self._find_candidates(req)) reqs = list(requirements[identifier]) if not reqs: return iter(()) original_req = min(reqs, key=self.requirement_preference) bare_name, extras = strip_extras(identifier) if extras and bare_name in requirements: # We should consider the requirements for both foo and foo[extra] reqs.extend(requirements[bare_name]) reqs.sort(key=self.requirement_preference) candidates = self._find_candidates(reqs[0]) return ( # In some cases we will use candidates from the bare requirement, # this will miss the extra dependencies if any. So we associate the original # requirement back with the candidate since it is used by `get_dependencies()`. can.copy_with(original_req) if extras else can for can in candidates if can not in incompat and all(self.is_satisfied_by(r, can) for r in reqs) ) return matches_gen def _compare_file_reqs(self, req1: FileRequirement, req2: FileRequirement) -> bool: backend = self.repository.environment.project.backend if req1.path and req2.path: return os.path.normpath(req1.path.absolute()) == os.path.normpath(req2.path.absolute()) left = backend.expand_line(url_without_fragments(req1.get_full_url())) right = backend.expand_line(url_without_fragments(req2.get_full_url())) return left == right def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> bool: if isinstance(requirement, PythonRequirement): return is_python_satisfied_by(requirement, candidate) elif (name := candidate.identify()) in self.overrides or strip_extras(name)[0] in self.overrides: return True if not requirement.is_named: if candidate.req.is_named: return False can_req = candidate.req if requirement.is_vcs and can_req.is_vcs: return can_req.vcs == requirement.vcs and can_req.repo == requirement.repo # type: ignore[attr-defined] return self._compare_file_reqs(requirement, can_req) # type: ignore[arg-type] version = candidate.version this_name = self.repository.environment.project.name if version is None or candidate.name == this_name: # This should be a URL candidate or self package, consider it to be matching return True # Allow prereleases if: 1) it is not specified in the tool settings or # 2) the candidate doesn't come from PyPI index or 3) the requirement is pinned allow_prereleases = ( self.allow_prereleases in (True, None) or not candidate.req.is_named or requirement.is_pinned ) return requirement.specifier.contains(version, allow_prereleases) def get_dependencies(self, candidate: Candidate) -> list[Requirement]: if isinstance(candidate, PythonCandidate): return [] try: deps, requires_python, _ = self.repository.get_dependencies(candidate) except (RequirementError, InvalidPyVersion, InvalidSpecifier) as e: # When the metadata is invalid, skip this candidate by marking it as conflicting. # Here we pass an empty criterion so it doesn't provide any info to the resolution. logger.error("Invalid metadata in %s: %s", candidate, e) raise RequirementsConflicted(Criterion([], [], [])) from None self.fetched_dependencies[candidate.dep_key] = deps[:] # Filter out incompatible dependencies(e.g. functools32) early so that # we don't get errors when building wheels. valid_deps: list[Requirement] = [] for dep in deps: if ( dep.requires_python & requires_python & candidate.req.requires_python & PySpecSet(self.repository.env_spec.requires_python) ).is_empty(): continue if dep.marker and not dep.marker.matches(self.repository.env_spec): continue if dep.identify() in self.excludes: continue dep.requires_python &= candidate.req.requires_python valid_deps.append(dep) # A candidate contributes to the Python requirements only when: # It isn't an optional dependency, or the requires-python doesn't cover # the req's requires-python. # For example, A v1 requires python>=3.6, it not eligible on a project with # requires-python=">=2.7". But it is eligible if A has environment marker # A1; python_version>='3.8' new_requires_python = candidate.req.requires_python & self.repository.environment.python_requires if not ( candidate.identify() in self.overrides or new_requires_python.is_empty() or requires_python.is_superset(new_requires_python) ): valid_deps.append(PythonRequirement.from_pyspec_set(requires_python)) return valid_deps @register_provider("reuse") class ReusePinProvider(BaseProvider): """A provider that reuses preferred pins if possible. This is used to implement "add", "remove", and "reuse upgrade", where already-pinned candidates in lockfile should be preferred. """ def __init__(self, *args: Any, tracked_names: Iterable[str], **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.tracked_names = set(tracked_names) def iter_reuse_candidates(self, identifier: str, requirement: Requirement | None) -> Iterable[Candidate]: bare_name = strip_extras(identifier)[0] if bare_name in self.tracked_names or identifier not in self.locked_candidates: return [] return sorted(self.locked_candidates[identifier], key=lambda c: c.version or "", reverse=True) def get_reuse_candidate(self, identifier: str, requirement: Requirement | None) -> Candidate | None: deprecation_warning( "The get_reuse_candidate method is deprecated, use iter_reuse_candidates instead.", stacklevel=2 ) return next(iter(self.iter_reuse_candidates(identifier, requirement)), None) def find_matches( self, identifier: str, requirements: Mapping[str, Iterator[Requirement]], incompatibilities: Mapping[str, Iterator[Candidate]], ) -> Callable[[], Iterator[Candidate]]: super_find = super().find_matches(identifier, requirements, incompatibilities) def matches_gen() -> Iterator[Candidate]: requested_req = next(filter(lambda r: r.is_named, requirements[identifier]), None) for pin in self.iter_reuse_candidates(identifier, requested_req): if identifier not in self.overrides: pin = pin.copy_with(min(requirements[identifier], key=self.requirement_preference)) incompat = list(incompatibilities[identifier]) pin._preferred = True # type: ignore[attr-defined] if pin not in incompat and all(self.is_satisfied_by(r, pin) for r in requirements[identifier]): yield pin yield from super_find() return matches_gen @register_provider("eager") class EagerUpdateProvider(ReusePinProvider): """A specialized provider to handle an "eager" upgrade strategy. An eager upgrade tries to upgrade not only packages specified, but also their dependencies (recursively). This contrasts to the "only-if-needed" default, which only promises to upgrade the specified package, and prevents touching anything else if at all possible. The provider is implemented as to keep track of all dependencies of the specified packages to upgrade, and free their pins when it has a chance. """ def iter_reuse_candidates(self, identifier: str, requirement: Requirement | None) -> Iterable[Candidate]: if identifier in self.tracked_names: # If this is a tracked package, don't reuse its pinned version, so it can be upgraded. return [] return super().iter_reuse_candidates(identifier, requirement) def get_dependencies(self, candidate: Candidate) -> list[Requirement]: # If this package is being tracked for upgrade, remove pins of its # dependencies, and start tracking these new packages. dependencies = super().get_dependencies(candidate) if self.identify(candidate) in self.tracked_names: for dependency in dependencies: if dependency.key: self.tracked_names.add(dependency.key) return dependencies def get_preference( self, identifier: str, resolutions: dict[str, Candidate], candidates: dict[str, Iterator[Candidate]], information: dict[str, Iterator[RequirementInformation]], backtrack_causes: Sequence[RequirementInformation], ) -> tuple[Comparable, ...]: # Resolve tracking packages so we have a chance to unpin them first. (python, *others) = super().get_preference(identifier, resolutions, candidates, information, backtrack_causes) return (python, identifier not in self.tracked_names, *others) @register_provider("reuse-installed") class ReuseInstalledProvider(ReusePinProvider): """A provider that reuses installed packages if possible.""" def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.installed = self.repository.environment.get_working_set() def iter_reuse_candidates(self, identifier: str, requirement: Requirement | None) -> Iterable[Candidate]: key = strip_extras(identifier)[0] if key not in self.installed or requirement is None: return super().iter_reuse_candidates(identifier, requirement) else: dist = self.installed[key] return [Candidate(requirement, name=dist.metadata["Name"], version=dist.metadata["Version"])] pdm-2.23.1/src/pdm/resolver/python.py000066400000000000000000000030521477560627500175050ustar00rootroot00000000000000""" Special requirement and candidate classes to describe a requires-python constraint """ from __future__ import annotations from typing import Iterable, Iterator, Mapping, cast from pdm.models.candidates import Candidate from pdm.models.requirements import NamedRequirement, Requirement from pdm.models.specifiers import PySpecSet class PythonCandidate(Candidate): def format(self) -> str: return f"[req]{self.name}[/][warning]{self.req.specifier!s}[/]" class PythonRequirement(NamedRequirement): @classmethod def from_pyspec_set(cls, spec: PySpecSet) -> PythonRequirement: return cls(name="python", specifier=spec) def as_candidate(self) -> PythonCandidate: return PythonCandidate(self) def find_python_matches( identifier: str, requirements: Mapping[str, Iterator[Requirement]], ) -> Iterable[Candidate]: """All requires-python except for the first one(must come from the project) must be superset of the first one. """ python_reqs = cast(Iterator[PythonRequirement], iter(requirements[identifier])) project_req = next(python_reqs) python_specs = cast(Iterator[PySpecSet], (req.specifier for req in python_reqs)) if all(spec.is_superset(project_req.specifier or "") for spec in python_specs): return [project_req.as_candidate()] else: # There is a conflict, no match is found. return [] def is_python_satisfied_by(requirement: Requirement, candidate: Candidate) -> bool: return cast(PySpecSet, requirement.specifier).is_superset(candidate.req.specifier) pdm-2.23.1/src/pdm/resolver/reporters.py000066400000000000000000000136311477560627500202150ustar00rootroot00000000000000from __future__ import annotations from contextlib import contextmanager from typing import TYPE_CHECKING, Any, Generator from resolvelib import BaseReporter from rich import get_console from rich.live import Live from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TimeElapsedColumn from pdm.models.reporter import CandidateReporter, RichProgressReporter from pdm.termui import SPINNER, UI, Verbosity, logger if TYPE_CHECKING: from resolvelib.resolvers import Criterion, RequirementInformation, State from rich.console import Console, ConsoleOptions, RenderResult from pdm.models.candidates import Candidate from pdm.models.requirements import Requirement def log_title(title: str) -> None: logger.info("=" * 8 + " " + title + " " + "=" * 8) class LockReporter(BaseReporter): @contextmanager def make_candidate_reporter(self, candidate: Candidate) -> Generator[CandidateReporter]: yield CandidateReporter() def starting(self) -> Any: log_title("Start resolving requirements") def adding_requirement(self, requirement: Requirement, parent: Candidate) -> None: parent_line = f"(from {parent.name} {parent.version})" if parent else "" logger.info(" Adding requirement %s%s", requirement.as_line(), parent_line) def ending(self, state: State) -> Any: log_title("Resolution Result") if state.mapping: column_width = max(map(len, state.mapping.keys())) for k, can in state.mapping.items(): if not can.req.is_named: can_info = can.req.url if can.req.is_vcs: can_info = f"{can_info}@{can.get_revision()}" else: can_info = can.version logger.info(f" {k.rjust(column_width)} {can_info}") class RichLockReporter(LockReporter): def __init__(self, requirements: list[Requirement], ui: UI) -> None: self.ui = ui self.console = get_console() self.requirements = requirements self.progress = Progress( "[progress.description]{task.description}", "[info]{task.fields[text]}", BarColumn(), TaskProgressColumn(), console=self.console, ) self._spinner = Progress( SpinnerColumn(SPINNER, style="primary"), TimeElapsedColumn(), "[bold]{task.description}", "{task.fields[info]}", console=self.console, ) self._spinner_task = self._spinner.add_task("Resolving dependencies", info="", total=1) self.live = Live(self) @contextmanager def make_candidate_reporter(self, candidate: Candidate) -> Generator[CandidateReporter]: task_id = self.progress.add_task(f"Resolving {candidate.format()}", text="", total=None) try: yield RichProgressReporter(self.progress, task_id) finally: self.progress.update(task_id, visible=False) if candidate._prepared: candidate._prepared.reporter = CandidateReporter() def update(self, description: str | None = None, info: str | None = None, completed: float | None = None) -> None: self._spinner.update(self._spinner_task, description=description, info=info, completed=completed) self.live.refresh() def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult: # pragma: no cover yield self._spinner yield self.progress def start(self) -> None: """Start the progress display.""" if self.ui.verbosity < Verbosity.DETAIL: self.live.start(refresh=True) def stop(self) -> None: """Stop the progress display.""" self.live.stop() if not self.console.is_interactive: # pragma: no cover self.console.print() def __enter__(self) -> RichLockReporter: self.start() return self def __exit__(self, *args: Any) -> None: self.stop() def starting_round(self, index: int) -> None: log_title(f"Starting round {index}") def starting(self) -> None: """Called before the resolution actually starts.""" log_title("Start resolving requirements") for req in self.requirements: logger.info(" " + req.as_line()) def ending_round(self, index: int, state: State) -> None: """Called before each round of resolution ends. This is NOT called if the resolution ends at this round. Use `ending` if you want to report finalization. The index is zero-based. """ resolved = len(state.mapping) to_resolve = len(state.criteria) - resolved self.update(info=f"[info]{resolved}[/] resolved, [info]{to_resolve}[/] to resolve") def rejecting_candidate(self, criterion: Criterion, candidate: Candidate) -> None: if not criterion.information: logger.info("Candidate rejected because it contains invalid metadata: %s", candidate) return *others, last = criterion.information logger.info( "Candidate rejected: %s because it introduces a new requirement %s" " that conflicts with other requirements:\n %s", candidate, last.requirement.as_line(), # type: ignore[attr-defined] " \n".join( sorted({f" {req.as_line()} (from {parent if parent else 'project'})" for req, parent in others}) ), ) def pinning(self, candidate: Candidate) -> None: """Called when adding a candidate to the potential solution.""" logger.info("Adding new pin: %s %s", candidate.name, candidate.version) def resolving_conflicts(self, causes: list[RequirementInformation]) -> None: conflicts = sorted({f" {req.as_line()} (from {parent if parent else 'project'})" for req, parent in causes}) logger.info("Conflicts detected: \n%s", "\n".join(conflicts)) pdm-2.23.1/src/pdm/resolver/resolvelib.py000066400000000000000000000133471477560627500203420ustar00rootroot00000000000000from __future__ import annotations import inspect import os from dataclasses import dataclass, replace from pathlib import Path from typing import cast from pdm import termui from pdm.models.candidates import Candidate from pdm.models.markers import get_marker from pdm.models.requirements import FileRequirement, Requirement, strip_extras from pdm.models.specifiers import PySpecSet from pdm.project.lockfile import FLAG_DIRECT_MINIMAL_VERSIONS, FLAG_INHERIT_METADATA from pdm.resolver.base import Resolution, Resolver from pdm.resolver.graph import merge_markers, populate_groups from pdm.resolver.python import PythonRequirement from pdm.resolver.reporters import LockReporter, RichLockReporter from pdm.utils import normalize_name @dataclass class RLResolver(Resolver): def __post_init__(self) -> None: super().__post_init__() if self.locked_repository is None: self.locked_repository = self.project.get_locked_repository() supports_env_spec = "env_spec" in inspect.signature(self.project.get_provider).parameters if supports_env_spec: provider = self.project.get_provider( self.update_strategy, self.tracked_names, direct_minimal_versions=FLAG_DIRECT_MINIMAL_VERSIONS in self.strategies, env_spec=self.target, locked_repository=self.locked_repository, ) else: # pragma: no cover provider = self.project.get_provider( self.update_strategy, self.tracked_names, direct_minimal_versions=FLAG_DIRECT_MINIMAL_VERSIONS in self.strategies, ignore_compatibility=self.target.is_allow_all(), ) if isinstance(self.reporter, LockReporter): provider.repository.reporter = self.reporter self.provider = provider def resolve(self) -> Resolution: from pdm.models.repositories import Package mapping = self._do_resolve() if self.project.enable_write_lockfile: # type: ignore[has-type] if isinstance(self.reporter, RichLockReporter): self.reporter.update(info="Fetching hashes for resolved packages") self.provider.repository.fetch_hashes(mapping.values()) if not (env_python := PySpecSet(self.target.requires_python)).is_superset(self.environment.python_requires): python_marker = get_marker(env_python.as_marker_string()) for candidate in mapping.values(): marker = candidate.req.marker or get_marker("") candidate.req = replace(candidate.req, marker=marker & python_marker) backend = self.project.backend packages: list[Package] = [] for candidate in mapping.values(): deps: list[str] = [] for r in self.provider.fetched_dependencies[candidate.dep_key]: if isinstance(r, FileRequirement) and r.path: try: if r.path.is_absolute(): r.path = Path(os.path.normpath(r.path)).relative_to(os.path.normpath(self.project.root)) except ValueError: pass else: r.url = backend.relative_path_to_url(r.path.as_posix()) deps.append(r.as_line()) packages.append(Package(candidate, deps, candidate.summary)) return Resolution(packages, self.requested_groups) def _do_resolve(self) -> dict[str, Candidate]: from resolvelib import Resolver as _Resolver resolver_class = cast("type[_Resolver]", getattr(self.project.core, "resolver_class", _Resolver)) resolver = resolver_class(self.provider, self.reporter) provider = self.provider repository = self.provider.repository target = self.target python_req = PythonRequirement.from_pyspec_set(PySpecSet(target.requires_python)) requirements: list[Requirement] = [python_req, *self.requirements] max_rounds = self.project.config["strategy.resolve_max_rounds"] result = resolver.resolve(requirements, max_rounds) if repository.has_warnings: self.project.core.ui.info( "Use `-q/--quiet` to suppress these warnings, or ignore them per-package with " r"`ignore_package_warnings` config in \[tool.pdm] table.", verbosity=termui.Verbosity.NORMAL, ) mapping = cast(dict[str, Candidate], result.mapping) mapping.pop("python", None) local_name = normalize_name(self.project.name) if self.project.is_distribution else None for key, candidate in list(mapping.items()): if key is None: continue # For source distribution whose name can only be determined after it is built, # the key in the resolution map and criteria should be updated. if key.startswith(":empty:"): new_key = provider.identify(candidate) mapping[new_key] = mapping.pop(key) result.criteria[new_key] = result.criteria.pop(key) # type: ignore[attr-defined] if FLAG_INHERIT_METADATA in self.strategies: all_markers = merge_markers(result) populate_groups(result) else: all_markers = {} for key, candidate in list(mapping.items()): if key in all_markers: marker = all_markers[key] if marker.is_empty(): del mapping[key] continue candidate.req = replace(candidate.req, marker=None if marker.is_any() else marker) if not self.keep_self and strip_extras(key)[0] == local_name: del mapping[key] return mapping pdm-2.23.1/src/pdm/resolver/uv.py000066400000000000000000000210661477560627500166230ustar00rootroot00000000000000from __future__ import annotations import logging import re import subprocess from dataclasses import dataclass, replace from itertools import chain from pathlib import Path from typing import TYPE_CHECKING, Any from pdm.models.candidates import Candidate from pdm.models.markers import get_marker from pdm.models.repositories import Package from pdm.models.requirements import FileRequirement, NamedRequirement, Requirement, VcsRequirement from pdm.models.specifiers import get_specifier from pdm.project.lockfile import FLAG_DIRECT_MINIMAL_VERSIONS, FLAG_INHERIT_METADATA, FLAG_STATIC_URLS from pdm.resolver.base import Resolution, Resolver from pdm.resolver.reporters import RichLockReporter from pdm.termui import Verbosity from pdm.utils import normalize_name if TYPE_CHECKING: from pdm._types import FileHash logger = logging.getLogger(__name__) GIT_URL = re.compile(r"(?P[^:/]+://[^\?#]+)(?:\?rev=(?P[^#]+?))?(?:#(?P[a-f0-9]+))$") @dataclass class UvResolver(Resolver): def __post_init__(self) -> None: super().__post_init__() self.default_source = self.project.sources[0].url if self.locked_repository is None: self.locked_repository = self.project.get_locked_repository() if self.update_strategy not in {"reuse", "all"}: self.project.core.ui.warn( f"{self.update_strategy} update strategy is not supported by uv, using 'reuse' instead" ) self.update_strategy = "reuse" if FLAG_INHERIT_METADATA in self.strategies: self.project.core.ui.warn("inherit_metadata strategy is not supported by uv resolver, it will be ignored") self.strategies.discard(FLAG_INHERIT_METADATA) this_spec = self.environment.spec assert this_spec.platform is not None if self.target.platform and ( self.target.platform.sys_platform != this_spec.platform.sys_platform or self.target.platform.arch != this_spec.platform.arch ): self.project.core.ui.warn( f"Resolving against target {self.target.platform} on {this_spec.platform} is not supported by uv mode, " "the resolution may be inaccurate." ) def _build_lock_command(self) -> list[str]: cmd = [*self.project.core.uv_cmd, "lock", "-p", str(self.environment.interpreter.executable)] if self.project.core.ui.verbosity > 0: cmd.append("--verbose") if not self.project.core.state.enable_cache: cmd.append("--no-cache") first_index = True for source in self.project.sources: assert source.url is not None if source.type == "find_links": cmd.extend(["--find-links", source.url]) elif first_index: cmd.extend(["--index-url", source.url]) first_index = False else: cmd.extend(["--extra-index-url", source.url]) if self.project.pyproject.settings.get("resolution", {}).get("respect-source-order", False): cmd.append("--index-strategy=unsafe-first-match") else: cmd.append("--index-strategy=unsafe-best-match") if self.update_strategy != "all": for name in self.tracked_names: cmd.extend(["-P", name]) if self.project.pyproject.allow_prereleases: cmd.append("--prerelease=allow") no_binary = self.environment._setting_list("PDM_NO_BINARY", "resolution.no-binary") only_binary = self.environment._setting_list("PDM_ONLY_BINARY", "resolution.only-binary") if ":all:" in no_binary: cmd.append("--no-binary") else: for pkg in no_binary: cmd.extend(["--no-binary-package", pkg]) if ":all:" in only_binary: cmd.append("--no-build") else: for pkg in only_binary: cmd.extend(["--no-build-package", pkg]) if not self.project.core.state.build_isolation: cmd.append("--no-build-isolation") if cs := self.project.core.state.config_settings: for k, v in cs.items(): cmd.extend(["--config-setting", f"{k}={v}"]) if FLAG_DIRECT_MINIMAL_VERSIONS in self.strategies: cmd.append("--resolution=lowest-direct") if dt := self.project.core.state.exclude_newer: cmd.extend(["--exclude-newer", dt.isoformat()]) return cmd def _parse_uv_lock(self, path: Path) -> Resolution: from unearth import Link from pdm.compat import tomllib with path.open("rb") as f: data = tomllib.load(f) packages: list[Package] = [] def make_requirement(dep: dict[str, Any]) -> str: req = NamedRequirement(name=dep["name"]) if version := dep.get("version"): req.specifier = get_specifier(f"=={version}") if marker := dep.get("marker"): req.marker = get_marker(marker) return req.as_line() for package in data["package"]: if ( self.project.name and package["name"] == normalize_name(self.project.name) and (not self.keep_self or package["source"].get("virtual")) ): continue req: Requirement if url := package["source"].get("url"): req = FileRequirement.create(url=url, name=package["name"]) elif git := package["source"].get("git"): matches = GIT_URL.match(git) if not matches: raise ValueError(f"Invalid git URL: {git}") url = f"git+{matches.group('repo')}" if ref := matches.group("ref"): url += f"@{ref}" req = VcsRequirement.create(url=url, name=package["name"]) req.revision = matches.group("revision") elif editable := package["source"].get("editable"): req = FileRequirement.create(path=editable, name=package["name"], editable=True) elif filepath := package["source"].get("path"): req = FileRequirement.create(path=filepath, name=package["name"]) else: req = NamedRequirement.create(name=package["name"], specifier=f"=={package['version']}") candidate = Candidate(req, name=package["name"], version=package["version"]) if FLAG_STATIC_URLS in self.strategies: def hash_maker(item: dict[str, Any]) -> FileHash: return {"url": item["url"], "hash": item["hash"]} else: def hash_maker(item: dict[str, Any]) -> FileHash: return {"file": Link(item["url"]).filename, "hash": item["hash"]} for wheel in chain(package.get("wheels", []), [sdist] if (sdist := package.get("sdist")) else []): candidate.hashes.append(hash_maker(wheel)) entry = Package(candidate, [make_requirement(dep) for dep in package.get("dependencies", [])], "") packages.append(entry) if optional_dependencies := package.get("optional-dependencies"): for group, deps in optional_dependencies.items(): extra_entry = Package( candidate.copy_with(replace(req, extras=(group,))), [f"{req.key}=={candidate.version}", *(make_requirement(dep) for dep in deps)], "", ) packages.append(extra_entry) return Resolution(packages, self.requested_groups) def resolve(self) -> Resolution: from pdm.formats.uv import uv_file_builder locked_repo = self.locked_repository or self.project.get_locked_repository() with uv_file_builder(self.project, str(self.target.requires_python), self.requirements, locked_repo) as builder: builder.build_pyproject_toml() uv_lock_path = self.project.root / "uv.lock" if self.update_strategy != "all": builder.build_uv_lock() try: if isinstance(self.reporter, RichLockReporter): self.reporter.stop() uv_lock_command = self._build_lock_command() self.project.core.ui.echo(f"Running uv lock command: {uv_lock_command}", verbosity=Verbosity.DETAIL) subprocess.run(uv_lock_command, cwd=self.project.root, check=True) finally: if isinstance(self.reporter, RichLockReporter): self.reporter.start() return self._parse_uv_lock(uv_lock_path) pdm-2.23.1/src/pdm/signals.py000066400000000000000000000076541477560627500157770ustar00rootroot00000000000000""" The signal definition for PDM. Example: ```python from pdm.signals import post_init, post_install def on_post_init(project): project.core.ui.echo("Project initialized") # Connect to the signal post_init.connect(on_post_init) # Or use as a decorator @post_install.connect def on_post_install(project, candidates, dry_run): project.core.ui.echo("Project install succeeded") ``` """ from blinker import NamedSignal, Namespace pdm_signals = Namespace() post_init: NamedSignal = pdm_signals.signal("post_init") """Called after a project is initialized. Args: project (Project): The project object """ pre_lock: NamedSignal = pdm_signals.signal("pre_lock") """Called before a project is locked. Args: project (Project): The project object requirements (list[Requirement]): The requirements to lock dry_run (bool): If true, won't perform any actions """ post_lock: NamedSignal = pdm_signals.signal("post_lock") """Called after a project is locked. Args: project (Project): The project object resolution (dict[str, list[Candidate]]): The resolved candidates dry_run (bool): If true, won't perform any actions """ pre_install: NamedSignal = pdm_signals.signal("pre_install") """Called before a project is installed. Args: project (Project): The project object packages (list[Package]): The packages to install dry_run (bool): If true, won't perform any actions """ post_install: NamedSignal = pdm_signals.signal("post_install") """Called after a project is installed. Args: project (Project): The project object packages (list[Package]): The packages installed dry_run (bool): If true, won't perform any actions """ pre_build: NamedSignal = pdm_signals.signal("pre_build") """Called before a project is built. Args: project (Project): The project object dest (str): The destination location config_settings (dict[str, str]|None): Additional config settings passed via args """ post_build: NamedSignal = pdm_signals.signal("post_build") """Called after a project is built. Args: project (Project): The project object artifacts (Sequence[str]): The locations of built artifacts config_settings (dict[str, str]|None): Additional config settings passed via args """ pre_publish: NamedSignal = pdm_signals.signal("pre_publish") """Called before a project is published. Args: project (Project): The project object """ post_publish: NamedSignal = pdm_signals.signal("post_publish") """Called after a project is published. Args: project (Project): The project object """ pre_run: NamedSignal = pdm_signals.signal("pre_run") """Called before any run. Args: project (Project): The project object script (str): the script name args (Sequence[str]): the command line provided arguments """ post_run: NamedSignal = pdm_signals.signal("post_run") """Called after any run. Args: project (Project): The project object script (str): the script name args (Sequence[str]): the command line provided arguments """ pre_script: NamedSignal = pdm_signals.signal("pre_script") """Called before any script. Args: project (Project): The project object script (str): the script name args (Sequence[str]): the command line provided arguments """ post_script: NamedSignal = pdm_signals.signal("post_script") """Called after any script. Args: project (Project): The project object script (str): the script name args (Sequence[str]): the command line provided arguments """ post_use: NamedSignal = pdm_signals.signal("post_use") """Called after use switched to a new Python version. Args: project (Project): The project object python (PythonInfo): Information about the new Python interpreter """ pre_invoke: NamedSignal = pdm_signals.signal("pre_invoke") """Called before any command is invoked. Args: project (Project): The project object command (str | None): the command name options (Namespace): the parsed arguments """ pdm-2.23.1/src/pdm/termui.py000066400000000000000000000235671477560627500156450ustar00rootroot00000000000000from __future__ import annotations import contextlib import enum import logging import os import tempfile import warnings from typing import TYPE_CHECKING import rich from rich.box import ROUNDED from rich.console import Console from rich.progress import Progress, ProgressColumn from rich.prompt import Confirm, IntPrompt, Prompt from rich.table import Table from rich.theme import Theme from pdm.exceptions import PDMWarning if TYPE_CHECKING: from typing import Any, Iterator, Sequence from pdm._types import RichProtocol, Spinner, SpinnerT logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) logger.addHandler(logging.NullHandler()) unearth_logger = logging.getLogger("unearth") unearth_logger.setLevel(logging.DEBUG) DEFAULT_THEME = { "primary": "cyan", "success": "green", "warning": "yellow", "error": "red", "info": "blue", "req": "bold green", } rich.reconfigure(highlight=False, theme=Theme(DEFAULT_THEME)) _err_console = Console(stderr=True, theme=Theme(DEFAULT_THEME)) def is_interactive(console: Console | None = None) -> bool: """Check if the terminal is run under interactive mode""" if console is None: console = rich.get_console() return "PDM_NON_INTERACTIVE" not in os.environ and console.is_interactive def is_legacy_windows(console: Console | None = None) -> bool: """Legacy Windows renderer may have problem rendering emojis""" if console is None: console = rich.get_console() return console.legacy_windows def style(text: str, *args: str, style: str | None = None, **kwargs: Any) -> str: """return text with ansi codes using rich console :param text: message with rich markup, defaults to "". :param style: rich style to apply to whole string :return: string containing ansi codes """ _console = rich.get_console() if _console.legacy_windows or not _console.is_terminal: # pragma: no cover return text with _console.capture() as capture: _console.print(text, *args, end="", style=style, **kwargs) return capture.get() def confirm(*args: str, **kwargs: Any) -> bool: default = kwargs.setdefault("default", False) if not is_interactive(): return default return Confirm.ask(*args, **kwargs) def ask(*args: str, prompt_type: type[str] | type[int] | None = None, **kwargs: Any) -> str: """prompt user and return response :prompt_type: which rich prompt to use, defaults to str. :raises ValueError: unsupported prompt type :return: str of user's selection """ if not prompt_type or prompt_type is str: return Prompt.ask(*args, **kwargs) elif prompt_type is int: return str(IntPrompt.ask(*args, **kwargs)) else: raise ValueError(f"unsupported {prompt_type}") class Verbosity(enum.IntEnum): QUIET = -1 NORMAL = 0 DETAIL = 1 DEBUG = 2 LOG_LEVELS = { Verbosity.NORMAL: logging.WARN, Verbosity.DETAIL: logging.INFO, Verbosity.DEBUG: logging.DEBUG, } class Emoji: if is_legacy_windows(): SUCC = "v" FAIL = "x" LOCK = " " CONGRAT = " " POPPER = " " ELLIPSIS = "..." ARROW_SEPARATOR = ">" else: SUCC = ":heavy_check_mark:" FAIL = ":heavy_multiplication_x:" LOCK = ":lock:" POPPER = ":party_popper:" ELLIPSIS = "…" ARROW_SEPARATOR = "➤" if is_legacy_windows(): SPINNER = "line" else: SPINNER = "dots" class DummySpinner: """A dummy spinner class implementing needed interfaces. But only display text onto screen. """ def __init__(self, text: str) -> None: self.text = text def _show(self) -> None: _err_console.print(f"[primary]STATUS:[/] {self.text}") def update(self, text: str) -> None: self.text = text self._show() def __enter__(self: SpinnerT) -> SpinnerT: self._show() # type: ignore[attr-defined] return self def __exit__(self, *args: Any) -> None: pass class SilentSpinner(DummySpinner): def _show(self) -> None: pass class UI: """Terminal UI object""" def __init__( self, verbosity: Verbosity = Verbosity.NORMAL, *, exit_stack: contextlib.ExitStack | None = None ) -> None: self.verbosity = verbosity self.exit_stack = exit_stack or contextlib.ExitStack() self.log_dir: str | None = None def set_verbosity(self, verbosity: int) -> None: self.verbosity = Verbosity(verbosity) if self.verbosity == Verbosity.QUIET: self.exit_stack.enter_context(warnings.catch_warnings()) warnings.simplefilter("ignore", PDMWarning, append=True) warnings.simplefilter("ignore", FutureWarning, append=True) def set_theme(self, theme: Theme) -> None: """set theme for rich console :param theme: dict of theme """ rich.get_console().push_theme(theme) _err_console.push_theme(theme) def echo( self, message: str | RichProtocol = "", err: bool = False, verbosity: Verbosity = Verbosity.QUIET, **kwargs: Any, ) -> None: """print message using rich console :param message: message with rich markup, defaults to "". :param err: if true print to stderr, defaults to False. :param verbosity: verbosity level, defaults to QUIET. """ if self.verbosity >= verbosity: console = _err_console if err else rich.get_console() if not console.is_interactive: kwargs.setdefault("crop", False) kwargs.setdefault("overflow", "ignore") console.print(message, **kwargs) def display_columns(self, rows: Sequence[Sequence[str]], header: list[str] | None = None) -> None: """Print rows in aligned columns. :param rows: a rows of data to be displayed. :param header: a list of header strings. """ if header: table = Table(box=ROUNDED) for title in header: if title[0] == "^": title, justify = title[1:], "center" elif title[0] == ">": title, justify = title[1:], "right" else: title, justify = title, "left" table.add_column(title, justify=justify) else: table = Table.grid(padding=(0, 1)) for _ in rows[0]: table.add_column() for row in rows: table.add_row(*row) rich.print(table) @contextlib.contextmanager def logging(self, type_: str = "install") -> Iterator[logging.Logger]: """A context manager that opens a file for logging when verbosity is NORMAL or print to the stdout otherwise. """ log_file: str | None = None if self.verbosity >= Verbosity.DETAIL: handler: logging.Handler = logging.StreamHandler() handler.setLevel(LOG_LEVELS[self.verbosity]) else: if self.log_dir and not os.path.exists(self.log_dir): os.makedirs(self.log_dir, exist_ok=True) self._clean_logs() log_file = tempfile.mktemp(".log", f"pdm-{type_}-", self.log_dir) handler = logging.FileHandler(log_file, encoding="utf-8") handler.setLevel(logging.DEBUG) handler.setFormatter(logging.Formatter("%(name)s: %(message)s")) logger.addHandler(handler) unearth_logger.addHandler(handler) def cleanup() -> None: if not log_file: return with contextlib.suppress(OSError): os.unlink(log_file) try: yield logger except Exception: if self.verbosity < Verbosity.DETAIL: logger.exception("Error occurs") self.echo( f"See [warning]{log_file}[/] for detailed debug log.", style="error", err=True, ) raise else: self.exit_stack.callback(cleanup) finally: logger.removeHandler(handler) unearth_logger.removeHandler(handler) handler.close() def open_spinner(self, title: str) -> Spinner: """Open a spinner as a context manager.""" if self.verbosity >= Verbosity.DETAIL or not is_interactive(): return DummySpinner(title) else: return _err_console.status(title, spinner=SPINNER, spinner_style="primary") def make_progress(self, *columns: str | ProgressColumn, **kwargs: Any) -> Progress: """create a progress instance for indented spinners""" return Progress(*columns, disable=self.verbosity >= Verbosity.DETAIL, **kwargs) def info(self, message: str, verbosity: Verbosity = Verbosity.NORMAL) -> None: """Print a message to stdout.""" self.echo(f"[info]INFO:[/] [dim]{message}[/]", err=True, verbosity=verbosity) def deprecated(self, message: str, verbosity: Verbosity = Verbosity.NORMAL) -> None: """Print a message to stdout.""" self.echo(f"[warning]DEPRECATED:[/] [dim]{message}[/]", err=True, verbosity=verbosity) def warn(self, message: str, verbosity: Verbosity = Verbosity.NORMAL) -> None: """Print a message to stdout.""" self.echo(f"[warning]WARNING:[/] {message}", err=True, verbosity=verbosity) def error(self, message: str, verbosity: Verbosity = Verbosity.QUIET) -> None: """Print a message to stdout.""" self.echo(f"[error]ERROR:[/] {message}", err=True, verbosity=verbosity) def _clean_logs(self) -> None: import time from pathlib import Path if self.log_dir is None: return for file in Path(self.log_dir).iterdir(): if not file.is_file(): continue if file.stat().st_ctime < time.time() - 7 * 24 * 60 * 60: # 7 days file.unlink() pdm-2.23.1/src/pdm/utils.py000066400000000000000000000433501477560627500154700ustar00rootroot00000000000000""" Utility functions """ from __future__ import annotations import atexit import contextlib import functools import inspect import json import os import re import shutil import subprocess import sys import sysconfig import tempfile import urllib.parse as parse import warnings from datetime import datetime, timezone from os import name as os_name from pathlib import Path from typing import TYPE_CHECKING, Mapping from packaging.specifiers import InvalidSpecifier, SpecifierSet from packaging.version import Version, _cmpkey from pbs_installer import PythonVersion from pdm.compat import importlib_metadata from pdm.exceptions import PDMDeprecationWarning, PdmException if TYPE_CHECKING: from re import Match from typing import IO, Any, Iterator from pdm._types import FileHash, RepositoryConfig from pdm.compat import Distribution try: _packaging_version = importlib_metadata.version("packaging") except Exception: from packaging import __version__ as _packaging_version @functools.lru_cache(maxsize=1024) def parse_version(version: str) -> Version: return Version(version) PACKAGING_22 = parse_version(_packaging_version) >= parse_version("22") def create_tracked_tempdir(suffix: str | None = None, prefix: str | None = None, dir: str | None = None) -> str: name = tempfile.mkdtemp(suffix=suffix, prefix=prefix, dir=dir) os.makedirs(name, mode=0o777, exist_ok=True) def clean_up() -> None: shutil.rmtree(name, ignore_errors=True) atexit.register(clean_up) return name def get_trusted_hosts(sources: list[RepositoryConfig]) -> list[str]: """Parse the project sources and return the trusted hosts""" trusted_hosts = [] for source in sources: assert source.url url = source.url netloc = parse.urlparse(url).netloc host = netloc.rsplit("@", 1)[-1] if host not in trusted_hosts and source.verify_ssl is False: trusted_hosts.append(host) return trusted_hosts def url_without_fragments(url: str) -> str: return parse.urlunparse(parse.urlparse(url)._replace(fragment="")) def join_list_with(items: list[Any], sep: Any) -> list[Any]: new_items = [] for item in items: new_items.extend([item, sep]) return new_items[:-1] def find_project_root(cwd: str = ".") -> str | None: """Recursively find a `pyproject.toml` at given path or current working directory.""" path = Path(cwd).absolute() if list(path.glob("pyproject.toml")): return path.as_posix() if path == path.parent: return None return find_project_root(str(path.parent)) def convert_hashes(files: list[FileHash]) -> dict[str, list[str]]: """Convert Pipfile.lock hash lines into InstallRequirement option format. The option format uses a str-list mapping. Keys are hash algorithms, and the list contains all values of that algorithm. """ result: dict[str, list[str]] = {} for f in files: hash_value = f.get("hash", "") name, has_name, hash_value = hash_value.partition(":") if not has_name: name, hash_value = "sha256", name result.setdefault(name, []).append(hash_value) return result def get_user_email_from_git() -> tuple[str, str]: """Get username and email from git config. Return empty if not configured or git is not found. """ git = shutil.which("git") if not git: return "", "" try: username = subprocess.check_output([git, "config", "user.name"], text=True, encoding="utf-8").strip() except subprocess.CalledProcessError: username = "" try: email = subprocess.check_output([git, "config", "user.email"], text=True, encoding="utf-8").strip() except subprocess.CalledProcessError: email = "" return username, email def add_ssh_scheme_to_git_uri(uri: str) -> str: """Cleans VCS uris from pip format""" # Add scheme for parsing purposes, this is also what pip does if "://" not in uri: uri = "ssh://" + uri parsed = parse.urlparse(uri) if ":" in parsed.netloc: netloc, _, path_start = parsed.netloc.rpartition(":") path = f"/{path_start}{parsed.path}" uri = parse.urlunparse(parsed._replace(netloc=netloc, path=path)) return uri @contextlib.contextmanager def atomic_open_for_write(filename: str | Path, *, mode: str = "w", encoding: str = "utf-8") -> Iterator[IO]: dirname = os.path.dirname(filename) if not os.path.exists(dirname): os.makedirs(dirname) fd, name = tempfile.mkstemp(prefix="atomic-write-", dir=dirname) fp = open(fd, mode, encoding=encoding if "b" not in mode else None) try: yield fp except Exception: fp.close() raise else: fp.close() with contextlib.suppress(OSError): os.unlink(filename) # The tempfile is created with mode 600, we need to restore the default mode # with copyfile() instead of move(). # See: https://github.com/pdm-project/pdm/issues/542 shutil.copyfile(name, str(filename)) finally: os.unlink(name) @contextlib.contextmanager def cd(path: str | Path) -> Iterator: _old_cwd = os.getcwd() os.chdir(path) try: yield finally: os.chdir(_old_cwd) def url_to_path(url: str) -> str: """ Convert a file: URL to a path. """ from urllib.request import url2pathname WINDOWS = sys.platform == "win32" if not url.startswith("file:"): raise ValueError(f"You can only turn file: urls into filenames (not {url!r})") _, netloc, path, _, _ = parse.urlsplit(url) if not netloc or netloc == "localhost": # According to RFC 8089, same as empty authority. netloc = "" elif WINDOWS: # If we have a UNC path, prepend UNC share notation. netloc = "\\\\" + netloc else: raise ValueError(f"non-local file URIs are not supported on this platform: {url!r}") path = url2pathname(netloc + path) # On Windows, urlsplit parses the path as something like "/C:/Users/foo". # This creates issues for path-related functions like io.open(), so we try # to detect and strip the leading slash. if ( WINDOWS and not netloc # Not UNC. and len(path) >= 3 and path[0] == "/" # Leading slash to strip. and path[1].isalpha() # Drive letter. and path[2:4] in (":", ":/") # Colon + end of string, or colon + absolute path. ): path = path[1:] return path def split_path_fragments(path: Path) -> tuple[Path, str]: """Split a path into fragments""" left, sep, right = path.as_posix().partition("#egg=") return Path(left), sep + right def expand_env_vars(credential: str, quote: bool = False, env: Mapping[str, str] | None = None) -> str: """A safe implementation of env var substitution. It only supports the following forms: ${ENV_VAR} Neither $ENV_VAR and %ENV_VAR is supported. """ if env is None: env = os.environ def replace_func(match: Match) -> str: rv = env.get(match.group(1), match.group(0)) return parse.quote(rv, "") if quote else rv return re.sub(r"\$\{(.+?)\}", replace_func, credential) def expand_env_vars_in_auth(url: str) -> str: """In-place expand the auth in url""" scheme, netloc, path, params, query, fragment = parse.urlparse(url) if "@" in netloc: auth, rest = netloc.split("@", 1) auth = expand_env_vars(auth, True) netloc = "@".join([auth, rest]) return parse.urlunparse((scheme, netloc, path, params, query, fragment)) @functools.lru_cache def path_replace(pattern: str, replace_with: str, dest: str) -> str: """Safely replace the pattern in a path with given string. :param pattern: the pattern to match :param replace_with: the string to replace with :param dest: the path to replace :return the replaced path """ sub_flags = re.IGNORECASE if os_name == "nt" else 0 return re.sub( pattern.replace("\\", "/"), replace_with, dest.replace("\\", "/"), flags=sub_flags, ) def is_path_relative_to(path: str | Path, other: str | Path) -> bool: try: Path(path).relative_to(other) except ValueError: return False return True def get_venv_like_prefix(interpreter: str | Path) -> tuple[Path | None, bool]: """Check if the given interpreter path is from a virtualenv, and return two values: the root path and whether it's a conda env. """ interpreter = Path(interpreter) prefix = interpreter.parent if prefix.joinpath("conda-meta").exists(): return prefix, True prefix = prefix.parent if prefix.joinpath("pyvenv.cfg").exists(): return prefix, False if prefix.joinpath("conda-meta").exists(): return prefix, True virtual_env = os.getenv("VIRTUAL_ENV") if virtual_env and is_path_relative_to(interpreter, virtual_env): return Path(virtual_env), False virtual_env = os.getenv("CONDA_PREFIX") if virtual_env and is_path_relative_to(interpreter, virtual_env): return Path(virtual_env), True return None, False def find_python_in_path(path: str | Path) -> Path | None: """Find a python interpreter from the given path, the input argument could be: - A valid path to the interpreter - A Python root directory that contains the interpreter """ pathlib_path = Path(path).absolute() if pathlib_path.is_file(): return pathlib_path if os.name == "nt": for root_dir in (pathlib_path, pathlib_path / "Scripts"): if root_dir.joinpath("python.exe").exists(): return root_dir.joinpath("python.exe") else: executable_pattern = re.compile(r"python(?:\d(?:\.\d+m?)?)?$") for python in pathlib_path.joinpath("bin").glob("python*"): if executable_pattern.match(python.name): return python return None def get_rev_from_url(url: str) -> str: """Get the rev part from the VCS URL.""" path = parse.urlparse(url).path if "@" in path: _, rev = path.rsplit("@", 1) return rev return "" @functools.lru_cache def normalize_name(name: str, lowercase: bool = True) -> str: name = re.sub(r"[^A-Za-z0-9]+", "-", name) return name.lower() if lowercase else name def comparable_version(version: str) -> Version: """Normalize a version to make it valid in a specifier.""" parsed = parse_version(version or "0.0.0") if parsed.local is not None: # strip the local part parsed._version = parsed._version._replace(local=None) # To make comparable_version("1.2.3+local1") == Version("1.2.3") parsed._key = _cmpkey( parsed._version.epoch, parsed._version.release, parsed._version.pre, parsed._version.post, parsed._version.dev, parsed._version.local, ) return parsed def is_egg_link(dist: Distribution) -> bool: """Check if the distribution is an egg-link install""" return getattr(dist, "link_file", None) is not None def is_editable(dist: Distribution) -> bool: """Check if the distribution is installed in editable mode""" if is_egg_link(dist): return True direct_url = dist.read_text("direct_url.json") if not direct_url: return False direct_url_data = json.loads(direct_url) return direct_url_data.get("dir_info", {}).get("editable", False) def pdm_scheme(base: str) -> dict[str, str]: """Return a PEP 582 style install scheme""" if "pep582" not in sysconfig.get_scheme_names(): bin_prefix = "Scripts" if os.name == "nt" else "bin" sysconfig._INSTALL_SCHEMES["pep582"] = { # type: ignore[attr-defined] "stdlib": "{pep582_base}/lib", "platstdlib": "{pep582_base}/lib", "purelib": "{pep582_base}/lib", "platlib": "{pep582_base}/lib", "include": "{pep582_base}/include", "scripts": f"{{pep582_base}}/{bin_prefix}", "data": "{pep582_base}", "prefix": "{pep582_base}", "headers": "{pep582_base}/include", } return sysconfig.get_paths("pep582", vars={"pep582_base": base}, expand=True) def is_url(url: str) -> bool: """Check if the given string is a URL""" return bool(parse.urlparse(url).scheme) @functools.lru_cache def fs_supports_link_method(method: str) -> bool: if not hasattr(os, method): return False if sys.platform == "win32": with tempfile.TemporaryDirectory(prefix="TmP") as temp_dir: with open(src := os.path.join(temp_dir, "a"), "w") as tmp_file: tmp_file.write("foo") dest = f"{src}-link" try: getattr(os, method)(src, dest) return True except (OSError, NotImplementedError): return False else: return True def deprecation_warning(message: str, stacklevel: int = 1, raise_since: str | None = None) -> None: # pragma: no cover """Show a deprecation warning with the given message and raise an error after a specified version. """ from pdm.__version__ import __version__ if raise_since is not None: if parse_version(__version__) >= parse_version(raise_since): raise PDMDeprecationWarning(message) warnings.warn(message, PDMDeprecationWarning, stacklevel=stacklevel + 1) def is_pip_compatible_with_python(python_version: Version | str) -> bool: """Check the given python version is compatible with the pip installed""" from pdm.compat import importlib_metadata from pdm.models.specifiers import get_specifier pip = importlib_metadata.distribution("pip") requires_python = get_specifier(pip.metadata.get("Requires-Python")) return requires_python.contains(python_version, True) def is_in_zipapp() -> bool: """Check if the current process is running in a zipapp""" return not os.path.exists(__file__) @functools.lru_cache(None) def package_installed(package_name: str) -> bool: try: importlib_metadata.distribution(package_name) except importlib_metadata.PackageNotFoundError: return False else: return True def validate_project_name(name: str) -> bool: """Check if the project name is valid or not""" pattern = r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$" return re.fullmatch(pattern, name, flags=re.IGNORECASE) is not None def sanitize_project_name(name: str) -> str: """Sanitize the project name and remove all illegal characters""" pattern = r"[^a-zA-Z0-9\-_\.]+" result = re.sub(pattern, "-", name) result = re.sub(r"^[\._-]|[\._-]$", "", result) if not result: raise PdmException(f"Invalid project name: {name}") return result def is_conda_base() -> bool: return os.getenv("CONDA_DEFAULT_ENV", "") == "base" def is_conda_base_python(python: Path) -> bool: if not is_conda_base(): return False prefix = os.environ["CONDA_PREFIX"] try: python.relative_to(prefix) except ValueError: return False return True def filtered_sources(sources: list[RepositoryConfig], package: str | None) -> list[RepositoryConfig]: """Get matching sources based on the index attribute.""" source_preferences = [(s, _source_preference(package, s)) for s in sources] included_by = [s for s, p in source_preferences if p is True] if included_by: return included_by return [s for s, p in source_preferences if p is None] def _source_preference(package: str | None, source: RepositoryConfig) -> bool | None: import fnmatch if package is None: return None key = normalize_name(package) if any(fnmatch.fnmatch(key, pat) for pat in source.include_packages): return True if any(fnmatch.fnmatch(key, pat) for pat in source.exclude_packages): return False return None def get_file_hash(filename: str | Path, algorithm: str = "sha256") -> str: """Calculate the hash of a file with the given algorithm""" import hashlib h = hashlib.new(algorithm) with open(filename, "rb") as f: for chunk in iter(lambda: f.read(8192), b""): h.update(chunk) return h.hexdigest() def convert_to_datetime(value: str) -> datetime: if "T" in value: return datetime.fromisoformat(value.replace("Z", "+00:00")) return datetime.strptime(value, "%Y-%m-%d").replace(tzinfo=timezone.utc) def get_all_installable_python_versions(build_dir: bool = False) -> list[PythonVersion]: """Returns all installable standalone Python interpreter versions from @indygreg Installable means: Fitting current platform and arch Parameters: build_dir: Whether to include the `build/` directory from indygreg builds (aka 'Full Archive') """ from pbs_installer._install import THIS_ARCH, THIS_PLATFORM from pbs_installer._versions import PYTHON_VERSIONS arch = "x86" if THIS_ARCH == "32" else THIS_ARCH matches = [v for v, u in PYTHON_VERSIONS.items() if any(k[:2] == (THIS_PLATFORM, arch) for k in u)] return matches def get_class_init_params(klass: type) -> set[str]: arguments: set[str] = set() for cls in klass.__mro__: if "__init__" not in cls.__dict__: continue params = inspect.signature(cls).parameters arguments.update({k for k, v in params.items() if v.kind not in (v.VAR_POSITIONAL, v.VAR_KEYWORD)}) if not any(p.kind in (p.VAR_POSITIONAL, p.VAR_KEYWORD) for p in params.values()): break return arguments def get_requirement_from_override(name: str, value: str) -> str: if is_url(value): req = f"{name} @ {value}" else: try: SpecifierSet(value) except InvalidSpecifier: req = f"{name}=={value}" else: req = f"{name}{value}" return req pdm-2.23.1/tasks/000077500000000000000000000000001477560627500135275ustar00rootroot00000000000000pdm-2.23.1/tasks/complete.py000066400000000000000000000006361477560627500157160ustar00rootroot00000000000000from pathlib import Path import pycomplete from pdm.core import Core COMPLETIONS = Path(__file__).parent.parent / "src/pdm/cli/completions" def main(): core = Core() core.init_parser() completer = pycomplete.Completer(core.parser, ["pdm"]) for shell in ("bash", "fish"): COMPLETIONS.joinpath(f"pdm.{shell}").write_text(completer.render(shell)) if __name__ == "__main__": main() pdm-2.23.1/tasks/max_versions.py000066400000000000000000000042361477560627500166230ustar00rootroot00000000000000from __future__ import annotations import json from html.parser import HTMLParser from pathlib import Path import httpx PROJECT_DIR = Path(__file__).parent.parent class PythonVersionParser(HTMLParser): def __init__(self, *, convert_charrefs: bool = True) -> None: super().__init__(convert_charrefs=convert_charrefs) self._parsing_release_number_span = False self._parsing_release_number_a = False self.parsed_python_versions: list[str] = [] def handle_starttag(self, tag: str, attrs: list[tuple[str, str]]) -> None: if tag == "span" and any("release-number" in value for key, value in attrs if key == "class"): self._parsing_release_number_span = True return if self._parsing_release_number_span and tag == "a": self._parsing_release_number_a = True def handle_endtag(self, tag: str) -> None: if self._parsing_release_number_span and tag == "span": self._parsing_release_number_span = False if self._parsing_release_number_a and tag == "a": self._parsing_release_number_a = False def handle_data(self, data: str) -> None: if self._parsing_release_number_a: self.parsed_python_versions.append(data[7:]) def dump_python_version_module(dest_file) -> None: resp = httpx.get("https://python.org/downloads/", follow_redirects=True) resp_text = resp.text parser = PythonVersionParser() parser.feed(resp_text) python_versions = sorted(parser.parsed_python_versions) max_versions: dict[str, int] = {} for version in python_versions: major, minor, patch = version.split(".") major_minor = f"{major}.{minor}" if major not in max_versions or max_versions[major] < int(minor): max_versions[major] = int(minor) if major_minor not in max_versions or max_versions[major_minor] < int(patch): max_versions[major_minor] = int(patch) with open(dest_file, "w") as f: json.dump(max_versions, f, sort_keys=True, indent=4) f.write("\n") if __name__ == "__main__": dump_python_version_module(PROJECT_DIR / "src/pdm/models/python_max_versions.json") pdm-2.23.1/tasks/release.py000066400000000000000000000062701477560627500155260ustar00rootroot00000000000000from __future__ import annotations import argparse import subprocess import sys from pathlib import Path from typing import TYPE_CHECKING, Any, cast import parver from rich.console import Console if TYPE_CHECKING: from parver._typing import PreTag _console = Console(highlight=False) _err_console = Console(stderr=True, highlight=False) def echo(*args: str, err: bool = False, **kwargs: Any): if err: _err_console.print(*args, **kwargs) else: _console.print(*args, **kwargs) PROJECT_DIR = Path(__file__).parent.parent def get_current_version() -> str: return subprocess.check_output(["git", "describe", "--abbrev=0", "--tags"], cwd=PROJECT_DIR).decode().strip() def bump_version(pre: str | None = None, major: bool = False, minor: bool = False) -> str: if major and minor: echo("Only one option should be provided among (--major, --minor)", style="red", err=True) sys.exit(1) current_version = parver.Version.parse(get_current_version()) if major or minor: version_idx = [major, minor].index(True) version = current_version.bump_release(index=version_idx) elif pre is not None and current_version.is_prerelease: version = current_version else: version = current_version.bump_release(index=2) if pre is not None: if version.pre_tag != pre: version = version.replace(pre_tag=cast("PreTag", pre), pre=0) else: version = version.bump_pre() else: version = version.replace(pre=None, post=None) version = version.replace(local=None, dev=None) return str(version) def release( dry_run: bool = False, commit: bool = True, pre: str | None = None, major: bool = False, minor: bool = False ) -> None: new_version = bump_version(pre, major, minor) echo(f"Bump version to: {new_version}", style="yellow") if dry_run: subprocess.check_call(["towncrier", "build", "--version", new_version, "--draft"]) else: subprocess.check_call(["towncrier", "build", "--yes", "--version", new_version]) subprocess.check_call(["git", "add", "."]) if commit: subprocess.check_call(["git", "commit", "-m", f"chore: Release {new_version}"]) subprocess.check_call(["git", "tag", "-a", new_version, "-m", f"v{new_version}"]) subprocess.check_call(["git", "push"]) subprocess.check_call(["git", "push", "--tags"]) def parse_args(argv=None): parser = argparse.ArgumentParser("release.py") parser.add_argument("--dry-run", action="store_true", help="Dry run mode") parser.add_argument( "--no-commit", action="store_false", dest="commit", default=True, help="Do not commit to Git", ) group = parser.add_argument_group(title="version part") group.add_argument("--pre", help="Bump with the pre tag", choices=["a", "b", "rc"]) group.add_argument("--major", action="store_true", help="Bump major version") group.add_argument("--minor", action="store_true", help="Bump minor version") return parser.parse_args(argv) if __name__ == "__main__": args = parse_args() release(args.dry_run, args.commit, args.pre, args.major, args.minor) pdm-2.23.1/tests/000077500000000000000000000000001477560627500135445ustar00rootroot00000000000000pdm-2.23.1/tests/__init__.py000066400000000000000000000001101477560627500156450ustar00rootroot00000000000000from pathlib import Path FIXTURES = Path(__file__).parent / "fixtures" pdm-2.23.1/tests/cli/000077500000000000000000000000001477560627500143135ustar00rootroot00000000000000pdm-2.23.1/tests/cli/__init__.py000066400000000000000000000000001477560627500164120ustar00rootroot00000000000000pdm-2.23.1/tests/cli/conftest.py000066400000000000000000000072511477560627500165170ustar00rootroot00000000000000from __future__ import annotations import shutil import textwrap from dataclasses import dataclass from pathlib import Path from typing import Any from unittest.mock import MagicMock import httpx import pytest from pytest_mock import MockerFixture from pdm.cli.commands.publish.package import PackageFile from pdm.cli.commands.publish.repository import Repository from pdm.models.auth import Keyring, keyring from tests import FIXTURES @pytest.fixture def mock_run_gpg(mocker: MockerFixture): def mock_run_gpg(args): signature_file = args[-1] + ".asc" with open(signature_file, "wb") as f: f.write(b"fake signature") mocker.patch.object(PackageFile, "_run_gpg", side_effect=mock_run_gpg) @pytest.fixture def prepare_packages(tmp_path: Path): dist_path = tmp_path / "dist" dist_path.mkdir() for filename in [ "demo-0.0.1-py2.py3-none-any.whl", "demo-0.0.1.tar.gz", "demo-0.0.1.zip", ]: shutil.copy2(FIXTURES / "artifacts" / filename, dist_path) @pytest.fixture def mock_pypi(mocker: MockerFixture): def send(request, **kwargs): # consume the data body to make the progress complete request.read() return httpx.Response(status_code=200, request=request) return mocker.patch("pdm.models.session.PDMPyPIClient.send", side_effect=send) @pytest.fixture def uploaded(mocker: MockerFixture): packages = [] def fake_upload(package): packages.append(package) return httpx.Response(status_code=200, request=httpx.Request("POST", "https://upload.pypi.org/legacy/")) mocker.patch.object(Repository, "upload", side_effect=fake_upload) return packages @dataclass class PublishMock: mock_pypi: MagicMock uploaded: list[Any] @pytest.fixture # @pytest.mark.usefixtures("mock_run_gpg", "prepare_packages") def mock_publish(mock_pypi, uploaded) -> PublishMock: return PublishMock( mock_pypi=mock_pypi, uploaded=uploaded, ) @pytest.fixture def _echo(project): """ Provides an echo.py script producing cross-platform expectable outputs """ (project.root / "echo.py").write_text( textwrap.dedent( """\ import os, sys, io sys.stdout = io.TextIOWrapper(sys.stdout.buffer, newline='\\n') name = sys.argv[1] vars = " ".join([f"{v}={os.getenv(v)}" for v in sys.argv[2:]]) print(f"{name} CALLED with {vars}" if vars else f"{name} CALLED") """ ) ) @pytest.fixture(name="keyring") def keyring_fixture(mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch) -> Keyring: from unearth.auth import AuthInfo, KeyringBaseProvider class MockKeyringProvider(KeyringBaseProvider): def __init__(self) -> None: self._store: dict[str, dict[str, str]] = {} def save_auth_info(self, url: str, username: str, password: str) -> None: self._store.setdefault(url, {})[username] = password def get_auth_info(self, url: str, username: str | None) -> AuthInfo | None: d = self._store.get(url, {}) if username is not None and username in d: return username, d[username] if username is None and d: return next(iter(d.items())) return None def delete_auth_info(self, url: str, username: str) -> None: self._store.get(url, {}).pop(username, None) provider = MockKeyringProvider() mocker.patch("unearth.auth.get_keyring_provider", return_value=provider) monkeypatch.setattr(keyring, "provider", provider) monkeypatch.setattr(keyring, "enabled", True) keyring.get_auth_info.cache_clear() return keyring pdm-2.23.1/tests/cli/test_add.py000066400000000000000000000414331477560627500164610ustar00rootroot00000000000000import shutil import pytest from unearth import Link from pdm.models.markers import EnvSpec from pdm.models.specifiers import PySpecSet from pdm.pytest import Distribution from tests import FIXTURES def test_add_package(project, working_set, dev_option, pdm): pdm(["add", *dev_option, "requests"], obj=project, strict=True) group = project.pyproject.dependency_groups["dev"] if dev_option else project.pyproject.metadata["dependencies"] assert group[0] == "requests>=2.19.1" locked_candidates = project.get_locked_repository().candidates assert locked_candidates["idna"].version == "2.7" for package in ("requests", "idna", "chardet", "urllib3", "certifi"): assert package in working_set def test_add_package_no_lock(project, working_set, dev_option, pdm): pdm(["add", *dev_option, "--frozen-lockfile", "-v", "requests"], obj=project, strict=True) group = project.pyproject.dependency_groups["dev"] if dev_option else project.pyproject.metadata["dependencies"] assert group[0] == "requests>=2.19.1" assert not project.lockfile.exists() for package in ("requests", "idna", "chardet", "urllib3", "certifi"): assert package in working_set def test_add_command(project, pdm, mocker): do_add = mocker.patch("pdm.cli.commands.add.Command.do_add") pdm(["add", "requests"], obj=project) do_add.assert_called_once() def test_add_package_to_custom_group(project, working_set, pdm): pdm(["add", "requests", "--group", "test"], obj=project, strict=True) assert "requests" in project.pyproject.metadata["optional-dependencies"]["test"][0] locked_candidates = project.get_locked_repository().candidates assert locked_candidates["idna"].version == "2.7" for package in ("requests", "idna", "chardet", "urllib3", "certifi"): assert package in working_set def test_add_package_to_custom_dev_group(project, working_set, pdm): pdm(["add", "requests", "--group", "test", "--dev"], obj=project, strict=True) dependencies = project.pyproject.dependency_groups["test"] assert "requests" in dependencies[0] locked_candidates = project.get_locked_repository().candidates assert locked_candidates["idna"].version == "2.7" for package in ("requests", "idna", "chardet", "urllib3", "certifi"): assert package in working_set @pytest.mark.usefixtures("vcs") def test_add_editable_package(project, working_set, pdm): # Ensure that correct python version is used. project.environment.python_requires = PySpecSet(">=3.6") pdm(["add", "--dev", "demo"], obj=project, strict=True) pdm(["add", "-de", "git+https://github.com/test-root/demo.git#egg=demo"], obj=project, strict=True) group = project.pyproject.dev_dependencies["dev"] assert group == ["-e git+https://github.com/test-root/demo.git#egg=demo"] assert not project.pyproject.dependency_groups locked_candidates = project.get_locked_repository().candidates assert locked_candidates["demo"].prepare(project.environment).revision == "1234567890abcdef" assert working_set["demo"].link_file assert locked_candidates["idna"].version == "2.7" assert "idna" in working_set pdm(["sync", "--no-editable"], obj=project, strict=True) assert not working_set["demo"].link_file @pytest.mark.usefixtures("vcs", "working_set") def test_add_editable_package_to_metadata_forbidden(project, pdm): project.environment.python_requires = PySpecSet(">=3.6") result = pdm(["add", "-v", "-e", "git+https://github.com/test-root/demo.git#egg=demo"], obj=project) assert "PdmUsageError" in result.stderr result = pdm(["add", "-v", "-Gtest", "-e", "git+https://github.com/test-root/demo.git#egg=demo"], obj=project) assert "PdmUsageError" in result.stderr @pytest.mark.usefixtures("working_set", "vcs") def test_non_editable_override_editable(project, pdm): project.environment.python_requires = PySpecSet(">=3.6") url = "git+https://github.com/test-root/demo.git#egg=demo" pdm(["add", "--dev", "-e", url], obj=project, strict=True) pdm(["add", "--dev", url], obj=project, strict=True) assert not project.get_dependencies("dev")[0].editable @pytest.mark.usefixtures("working_set", "vcs") def test_add_editable_normal_dev_dependency(project, pdm): project.environment.python_requires = PySpecSet(">=3.6") url = "git+https://github.com/test-root/demo.git#egg=demo" pdm(["add", "--dev", "-e", url], obj=project, strict=True) pdm(["add", "-d", "urllib3"], obj=project, strict=True) pdm(["add", "-d", "idna"], obj=project, strict=True) dev_group = project.pyproject.settings["dev-dependencies"]["dev"] pep735_group = project.pyproject.dependency_groups["dev"] assert dev_group == ["-e git+https://github.com/test-root/demo.git#egg=demo"] assert pep735_group == ["urllib3>=1.22", "idna>=2.7"] @pytest.mark.usefixtures("working_set", "vcs") def test_add_dev_dependency_with_existing_editables_group(project, pdm): project.environment.python_requires = PySpecSet(">=3.6") url = "git+https://github.com/test-root/demo.git#egg=demo" pdm(["add", "-dG", "editables", "-e", url], obj=project, strict=True) pdm(["add", "-d", "urllib3"], obj=project, strict=True) pdm(["add", "-dG", "named", "idna"], obj=project, strict=True) assert "editables" in project.pyproject.settings["dev-dependencies"] assert "dev" in project.pyproject.dependency_groups assert "named" in project.pyproject.dependency_groups editables_group = project.pyproject.settings["dev-dependencies"]["editables"] pep735_group = project.pyproject.dependency_groups["dev"] pep735_named_group = project.pyproject.dependency_groups["named"] assert editables_group == ["-e git+https://github.com/test-root/demo.git#egg=demo"] assert "editables" not in project.pyproject.dependency_groups assert pep735_group == ["urllib3>=1.22"] assert pep735_named_group == ["idna>=2.7"] @pytest.mark.usefixtures("working_set") def test_add_remote_package_url(project, dev_option, pdm): project.environment.python_requires = PySpecSet(">=3.6") url = "http://fixtures.test/artifacts/demo-0.0.1-py2.py3-none-any.whl" pdm(["add", *dev_option, url], obj=project, strict=True) group = project.pyproject.dependency_groups["dev"] if dev_option else project.pyproject.metadata["dependencies"] assert group[0] == f"demo @ {url}" def test_add_no_install(project, working_set, pdm): pdm(["add", "--no-sync", "requests"], obj=project, strict=True) for package in ("requests", "idna", "chardet", "urllib3", "certifi"): assert package not in working_set @pytest.mark.usefixtures("repository") def test_add_package_save_exact(project, pdm): pdm(["add", "--save-exact", "--no-sync", "requests"], obj=project, strict=True) assert project.pyproject.metadata["dependencies"][0] == "requests==2.19.1" @pytest.mark.usefixtures("repository") def test_add_package_save_wildcard(project, pdm): pdm(["add", "--save-wildcard", "--no-sync", "requests"], obj=project, strict=True) assert project.pyproject.metadata["dependencies"][0] == "requests" @pytest.mark.usefixtures("repository") def test_add_package_save_minimum(project, pdm): pdm(["add", "--save-minimum", "--no-sync", "requests"], obj=project, strict=True) assert project.pyproject.metadata["dependencies"][0] == "requests>=2.19.1" def test_add_package_update_reuse(project, repository, pdm): pdm(["add", "--no-sync", "--save-wildcard", "requests", "pytz"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert locked_candidates["requests"].version == "2.19.1" assert locked_candidates["chardet"].version == "3.0.4" assert locked_candidates["pytz"].version == "2019.3" repository.add_candidate("pytz", "2019.6") repository.add_candidate("chardet", "3.0.5") repository.add_candidate("requests", "2.20.0") repository.add_dependencies( "requests", "2.20.0", [ "certifi>=2017.4.17", "chardet<3.1.0,>=3.0.2", "idna<2.8,>=2.5", "urllib3<1.24,>=1.21.1", ], ) pdm(["add", "--no-sync", "--save-wildcard", "requests"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert locked_candidates["requests"].version == "2.20.0" assert locked_candidates["chardet"].version == "3.0.4" assert locked_candidates["pytz"].version == "2019.3" def test_add_package_update_eager(project, repository, pdm): pdm(["add", "--no-sync", "--save-wildcard", "requests", "pytz"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert locked_candidates["requests"].version == "2.19.1" assert locked_candidates["chardet"].version == "3.0.4" assert locked_candidates["pytz"].version == "2019.3" repository.add_candidate("pytz", "2019.6") repository.add_candidate("chardet", "3.0.5") repository.add_candidate("requests", "2.20.0") repository.add_dependencies( "requests", "2.20.0", [ "certifi>=2017.4.17", "chardet<3.1.0,>=3.0.2", "idna<2.8,>=2.5", "urllib3<1.24,>=1.21.1", ], ) pdm(["add", "--no-sync", "--save-wildcard", "--update-eager", "requests"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert locked_candidates["requests"].version == "2.20.0" assert locked_candidates["chardet"].version == "3.0.5" assert locked_candidates["pytz"].version == "2019.3" def test_add_package_with_mismatch_marker(project, working_set, pdm): env = project.environment env.__dict__["spec"] = EnvSpec.from_spec("==3.11", "macos", "cpython") pdm(["add", "requests", "pytz; platform_system!='Darwin'"], obj=project, strict=True) assert "pytz" not in working_set def test_add_dependency_from_multiple_parents(project, working_set, pdm): env = project.environment env.__dict__["spec"] = EnvSpec.from_spec("==3.11", "macos", "cpython") pdm(["add", "requests", "chardet; platform_system!='Darwin'"], obj=project, strict=True) assert "chardet" in working_set def test_add_packages_without_self(project, working_set, pdm): project.environment.python_requires = PySpecSet(">=3.6") pdm(["add", "--no-self", "requests"], obj=project, strict=True) assert project.name not in working_set @pytest.mark.usefixtures("working_set") def test_add_package_unconstrained_rewrite_specifier(project, pdm): project.environment.python_requires = PySpecSet(">=3.6") pdm(["add", "--no-self", "django"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert locked_candidates["django"].version == "2.2.9" assert project.pyproject.metadata["dependencies"][0] == "django>=2.2.9" pdm(["add", "--no-self", "--unconstrained", "django-toolbar"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert locked_candidates["django"].version == "1.11.8" assert project.pyproject.metadata["dependencies"][0] == "django>=1.11.8" @pytest.mark.usefixtures("working_set", "vcs") def test_add_cached_vcs_requirement(project, mocker, pdm): project.environment.python_requires = PySpecSet(">=3.6") url = "git+https://github.com/test-root/demo.git@1234567890abcdef#egg=demo" built_path = FIXTURES / "artifacts/demo-0.0.1-py2.py3-none-any.whl" wheel_cache = project.make_wheel_cache() cache_path = wheel_cache.get_path_for_link(Link(url), project.environment.spec) if not cache_path.exists(): cache_path.mkdir(parents=True) shutil.copy2(built_path, cache_path) downloader = mocker.patch("unearth.finder.unpack_link") builder = mocker.patch("pdm.builders.WheelBuilder.build") pdm(["add", "--no-self", url], obj=project, strict=True) lockfile_entry = next(p for p in project.lockfile["package"] if p["name"] == "demo") assert lockfile_entry["revision"] == "1234567890abcdef" downloader.assert_not_called() builder.assert_not_called() @pytest.mark.usefixtures("repository") def test_add_with_dry_run(project, pdm): result = pdm(["add", "--dry-run", "requests"], obj=project, strict=True) project.pyproject.reload() assert not project.get_dependencies() assert "requests 2.19.1" in result.stdout assert "urllib3 1.22" in result.stdout def test_add_with_prerelease(project, working_set, pdm): pdm(["add", "--prerelease", "--save-compatible", "urllib3"], obj=project, strict=True) assert working_set["urllib3"].version == "1.23b0" assert project.pyproject.metadata["dependencies"][0] == "urllib3<2,>=1.23b0" def test_add_editable_package_with_extras(project, working_set, pdm): project.environment.python_requires = PySpecSet(">=3.6") dep_path = FIXTURES.joinpath("projects/demo") pdm(["add", "-dGdev", "-e", f"{dep_path.as_posix()}[security]"], obj=project, strict=True) assert f"-e {dep_path.as_uri()}#egg=demo[security]" in project.use_pyproject_dependencies("dev", True)[0] assert "demo" in working_set assert "requests" in working_set assert "urllib3" in working_set def test_add_package_with_local_version(project, repository, working_set, pdm): repository.add_candidate("foo", "1.0a0+local") pdm(["add", "-v", "foo"], obj=project, strict=True) assert working_set["foo"].version == "1.0a0+local" dependencies, _ = project.use_pyproject_dependencies("default") assert dependencies[0] == "foo>=1.0a0" def test_add_group_to_lockfile(project, working_set, pdm): pdm(["add", "requests"], obj=project, strict=True) assert project.lockfile.groups == ["default"] pdm(["add", "--group", "tz", "pytz"], obj=project, strict=True) assert project.lockfile.groups == ["default", "tz"] assert "pytz" in working_set def test_add_group_to_lockfile_without_package(project, working_set, pdm): project.add_dependencies(["requests"]) project.add_dependencies(["pytz"], to_group="tz") pdm(["install"], obj=project, strict=True) assert "pytz" not in working_set assert project.lockfile.groups == ["default"] pdm(["add", "--group", "tz"], obj=project, strict=True) assert project.lockfile.groups == ["default", "tz"] assert "pytz" in working_set def test_add_update_reuse_installed(project, working_set, repository, pdm): working_set["foo"] = Distribution("foo", "1.0.0") repository.add_candidate("foo", "1.0.0") repository.add_candidate("foo", "1.1.0") pdm(["add", "--update-reuse-installed", "foo"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert locked_candidates["foo"].version == "1.0.0" def test_add_update_reuse_installed_config(project, working_set, repository, pdm): working_set["foo"] = Distribution("foo", "1.0.0") repository.add_candidate("foo", "1.0.0") repository.add_candidate("foo", "1.1.0") project.project_config["strategy.update"] = "reuse-installed" pdm(["add", "foo"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert locked_candidates["foo"].version == "1.0.0" def test_add_disable_cache(project, pdm, working_set): cache_dir = project.cache_dir pdm(["--no-cache", "add", "requests"], obj=project, strict=True) assert "requests" in working_set files = [file for file in cache_dir.rglob("*") if file.is_file()] assert not files @pytest.mark.usefixtures("working_set") def test_add_dependency_with_direct_minimal_versions(project, pdm, repository): pdm(["lock", "-S", "direct_minimal_versions"], obj=project, strict=True) repository.add_candidate("pytz", "2019.6") pdm(["add", "django"], obj=project, strict=True) all_candidates = project.get_locked_repository().candidates assert "django>=1.11.8" in project.pyproject.metadata["dependencies"] assert all_candidates["django"].version == "1.11.8" assert all_candidates["pytz"].version == "2019.6" def test_add_group_with_normalized_name(project, pdm, working_set): project.pyproject.dependency_groups.update({"foo_bar": ["requests"]}) project.pyproject.write() pdm(["lock"], obj=project, strict=True) assert "foo-bar" in project.lockfile.groups pdm(["sync", "-G", "foo.bar"], obj=project, strict=True) assert "requests" in working_set result = pdm(["add", "-G", "foo-bar", "pytz"], obj=project) assert result.exit_code != 0 assert "Group foo-bar already exists in another non-normalized form" in result.stderr @pytest.mark.usefixtures("working_set") def test_add_to_dependency_group_with_include(project, pdm): from pdm.formats.base import make_array project.pyproject.dependency_groups.update({"tz": ["pytz"], "web": make_array([{"include-group": "tz"}])}) project.pyproject.write() pdm(["add", "-Gweb", "requests"], obj=project, strict=True) assert project.pyproject.dependency_groups["web"] == [{"include-group": "tz"}, "requests>=2.19.1"] pdm-2.23.1/tests/cli/test_build.py000066400000000000000000000141021477560627500170210ustar00rootroot00000000000000import tarfile import zipfile from unittest import mock import pytest from pdm.cli.commands.build import Command pytestmark = pytest.mark.usefixtures("local_finder") def get_tarball_names(path): with tarfile.open(path, "r:gz") as tar: return tar.getnames() def get_wheel_names(path): with zipfile.ZipFile(path) as zf: return zf.namelist() def test_build_command(project, pdm, mocker): do_build = mocker.patch.object(Command, "do_build") # prevent the context_settings from being reset project.core.exit_stack.pop_all() pdm(["build", "--no-sdist", "-Ca=1", "--config-setting", "b=2"], obj=project) do_build.assert_called_with( project, sdist=False, wheel=True, dest=mock.ANY, clean=True, verbose=0, hooks=mock.ANY, ) assert project.core.state.config_settings == {"a": "1", "b": "2"} def test_build_global_project_forbidden(pdm): result = pdm(["build", "-g"]) assert result.exit_code != 0 def test_build_single_module(fixture_project): project = fixture_project("demo-module") Command.do_build(project) tar_names = get_tarball_names(project.root / "dist/demo_module-0.1.0.tar.gz") for name in [ "foo_module.py", "bar_module.py", "LICENSE", "pyproject.toml", "PKG-INFO", ]: assert f"demo_module-0.1.0/{name}" in tar_names for i in range(2): if i == 1: Command.do_build(project, sdist=False) zip_names = get_wheel_names(project.root / "dist/demo_module-0.1.0-py3-none-any.whl") for name in ["foo_module.py", "bar_module.py"]: assert name in zip_names for name in ("pyproject.toml", "LICENSE"): assert name not in zip_names def test_build_single_module_with_readme(fixture_project): project = fixture_project("demo-module") project.pyproject.metadata["readme"] = "README.md" project.pyproject.write() Command.do_build(project) assert "demo_module-0.1.0/README.md" in get_tarball_names(project.root / "dist/demo_module-0.1.0.tar.gz") def test_build_package(fixture_project): project = fixture_project("demo-package") Command.do_build(project) tar_names = get_tarball_names(project.root / "dist/my_package-0.1.0.tar.gz") assert "my_package-0.1.0/my_package/__init__.py" in tar_names assert "my_package-0.1.0/my_package/data.json" in tar_names assert "my_package-0.1.0/single_module.py" not in tar_names assert "my_package-0.1.0/data_out.json" not in tar_names zip_names = get_wheel_names(project.root / "dist/my_package-0.1.0-py3-none-any.whl") assert "my_package/__init__.py" in zip_names assert "my_package/data.json" in zip_names assert "single_module.py" not in zip_names assert "data_out.json" not in zip_names def test_build_src_package(fixture_project): project = fixture_project("demo-src-package") Command.do_build(project) tar_names = get_tarball_names(project.root / "dist/demo_package-0.1.0.tar.gz") assert "demo_package-0.1.0/src/my_package/__init__.py" in tar_names assert "demo_package-0.1.0/src/my_package/data.json" in tar_names zip_names = get_wheel_names(project.root / "dist/demo_package-0.1.0-py3-none-any.whl") assert "my_package/__init__.py" in zip_names assert "my_package/data.json" in zip_names def test_build_package_include(fixture_project): project = fixture_project("demo-package") build_config = project.pyproject.settings.setdefault("build", {}) build_config["includes"] = [ "my_package/", "single_module.py", "data_out.json", ] build_config["excludes"] = ["my_package/*.json"] project.pyproject.write() Command.do_build(project) tar_names = get_tarball_names(project.root / "dist/my_package-0.1.0.tar.gz") assert "my_package-0.1.0/my_package/__init__.py" in tar_names assert "my_package-0.1.0/my_package/data.json" not in tar_names assert "my_package-0.1.0/single_module.py" in tar_names assert "my_package-0.1.0/data_out.json" in tar_names zip_names = get_wheel_names(project.root / "dist/my_package-0.1.0-py3-none-any.whl") assert "my_package/__init__.py" in zip_names assert "my_package/data.json" not in zip_names assert "single_module.py" in zip_names assert "data_out.json" in zip_names def test_build_src_package_by_include(fixture_project): project = fixture_project("demo-src-package") project.pyproject.settings.setdefault("build", {})["includes"] = ["src/my_package"] project.pyproject.write() Command.do_build(project) tar_names = get_tarball_names(project.root / "dist/demo_package-0.1.0.tar.gz") assert "demo_package-0.1.0/src/my_package/__init__.py" in tar_names assert "demo_package-0.1.0/src/my_package/data.json" in tar_names zip_names = get_wheel_names(project.root / "dist/demo_package-0.1.0-py3-none-any.whl") assert "my_package/__init__.py" in zip_names assert "my_package/data.json" in zip_names def test_build_with_config_settings(fixture_project): project = fixture_project("demo-src-package") project.core.state.config_settings = {"--plat-name": "win_amd64"} Command.do_build(project) assert (project.root / "dist/demo_package-0.1.0-py3-none-win_amd64.whl").exists() def test_cli_build_with_config_settings(fixture_project, pdm): project = fixture_project("demo-src-package") result = pdm(["build", "-C--plat-name=win_amd64"], obj=project) assert result.exit_code == 0 assert (project.root / "dist/demo_package-0.1.0-py3-none-win_amd64.whl").exists() @pytest.mark.usefixtures("local_finder") def test_build_with_no_isolation(pdm, project): result = pdm(["build", "--no-isolation"], obj=project) assert result.exit_code == 1 pdm(["add", "pdm-backend", "--no-self"], obj=project, strict=True) result = pdm(["build", "--no-isolation"], obj=project) assert result.exit_code == 0 def test_build_ignoring_pip_environment(fixture_project, monkeypatch): project = fixture_project("demo-module") monkeypatch.setenv("PIP_REQUIRE_VIRTUALENV", "1") Command.do_build(project) pdm-2.23.1/tests/cli/test_cache.py000066400000000000000000000120541477560627500167710ustar00rootroot00000000000000import pytest from unearth import Link from pdm.models.cached_package import CachedPackage from tests import FIXTURES @pytest.fixture def prepare_wheel_cache(project): cache_dir = project.cache("wheels") (cache_dir / "arbitrary/path").mkdir(parents=True) for name in ( "foo-0.1.0.whl", "bar-0.2.0.whl", "baz-0.3.0.whl", "foo_bar-0.4.0.whl", ): (cache_dir / "arbitrary/path" / name).touch() @pytest.fixture def prepare_http_cache(project): cache_dir = project.cache("http") (cache_dir / "arbitrary/path").mkdir(parents=True) for name in ( "foo-0.1.0.tar.gz", "bar-0.2.0.zip", "baz-0.3.0.tar.gz", "foobar-0.4.0.tar.gz", ): (cache_dir / "arbitrary/path" / name).touch() @pytest.mark.usefixtures("prepare_wheel_cache") def test_cache_list(project, pdm): result = pdm(["cache", "list"], obj=project) assert result.exit_code == 0 for name in ( "foo-0.1.0.whl", "bar-0.2.0.whl", "baz-0.3.0.whl", "foo_bar-0.4.0.whl", ): assert name in result.output @pytest.mark.usefixtures("prepare_wheel_cache") def test_cache_list_pattern(project, pdm): result = pdm(["cache", "list", "ba*"], obj=project) assert result.exit_code == 0 for name in ( "bar-0.2.0.whl", "baz-0.3.0.whl", ): assert name in result.output for name in ( "foo-0.1.0.whl", "foo_bar-0.4.0.whl", ): assert name not in result.output @pytest.mark.usefixtures("prepare_wheel_cache", "prepare_http_cache") def test_cache_remove_pattern(project, pdm): result = pdm(["cache", "remove", "ba*"], obj=project) assert result.exit_code == 0 for name in ( "bar-0.2.0.whl", "baz-0.3.0.whl", ): assert not (project.cache("wheels") / "arbitrary/path" / name).exists() for name in ( "foo-0.1.0.whl", "foo_bar-0.4.0.whl", ): assert (project.cache("wheels") / "arbitrary/path" / name).exists() assert (project.cache("http") / "arbitrary/path/foo-0.1.0.tar.gz").exists() @pytest.mark.usefixtures("prepare_wheel_cache", "prepare_http_cache") def test_cache_remove_wildcard(project, pdm): result = pdm(["cache", "remove", "*"], obj=project) assert result.exit_code == 0 for name in ( "bar-0.2.0.whl", "baz-0.3.0.whl", "foo-0.1.0.whl", "foo_bar-0.4.0.whl", ): assert not (project.cache("wheels") / "arbitrary/path" / name).exists() assert (project.cache("http") / "arbitrary/path/foo-0.1.0.tar.gz").exists() @pytest.mark.usefixtures("prepare_wheel_cache", "prepare_http_cache") def test_cache_clear(project, pdm): result = pdm(["cache", "clear"], obj=project) assert result.exit_code == 0 for name in ( "bar-0.2.0.whl", "baz-0.3.0.whl", "foo-0.1.0.whl", "foo_bar-0.4.0.whl", ): assert not (project.cache("wheels") / "arbitrary/path" / name).exists() assert not (project.cache("http") / "arbitrary/path/foo-0.1.0.tar.gz").exists() @pytest.mark.usefixtures("prepare_wheel_cache", "prepare_http_cache") def test_cache_remove_no_pattern(project, pdm): result = pdm(["cache", "remove"], obj=project) assert result.exit_code != 0 @pytest.mark.usefixtures("prepare_wheel_cache", "prepare_http_cache") def test_cache_info(project, pdm): result = pdm(["cache", "info"], obj=project) assert result.exit_code == 0 lines = result.output.splitlines() assert "Files: 4" in lines[4] assert "Files: 4" in lines[6] @pytest.mark.parametrize( "url,hash", [ ( "http://fixtures.test/artifacts/demo-0.0.1.tar.gz", "sha256:d57bf5e3b8723e4fc68275159dcc4ca983d86d4c84220a4d715d491401f27db2", ), ( f"file://{(FIXTURES / 'artifacts/demo-0.0.1.tar.gz').as_posix()}", "sha256:d57bf5e3b8723e4fc68275159dcc4ca983d86d4c84220a4d715d491401f27db2", ), ( "http://fixtures.test/artifacts/demo-0.0.1.tar.gz#sha384=9130e5e4912bc78b" "1ffabbf406d56bc74b9165b0adc8c627168b7b563b80d5ff6c30e269398d01144ee52aa3" "3292682d", "sha384:9130e5e4912bc78b1ffabbf406d56bc74b9165b0adc8c627168b7b563b80d5ff6c30e269398d01144ee52aa33292682d", ), ( "http://fixtures.test/artifacts/demo-0.0.1.tar.gz#md5=5218509812c9fcb4646adde8fd3307e1", "sha256:d57bf5e3b8723e4fc68275159dcc4ca983d86d4c84220a4d715d491401f27db2", ), ], ) def test_hash_cache(project, url, hash): hash_cache = project.make_hash_cache() assert hash_cache.get_hash(Link(url), project.environment.session) == hash def test_clear_package_cache(project, pdm): pkg = CachedPackage(project.cache("packages") / "test_package.whl.cache") pkg.path.mkdir(parents=True) refer_pkg = project.root / "refer_pkg" refer_pkg.mkdir() pkg.add_referrer(str(refer_pkg)) assert len(pkg.referrers) == 1 refer_pkg.rmdir() pdm(["cache", "clear", "packages"], obj=project, strict=True) assert not pkg.path.exists() pdm-2.23.1/tests/cli/test_config.py000066400000000000000000000225131477560627500171740ustar00rootroot00000000000000import pytest from pdm.exceptions import PdmUsageError from pdm.utils import cd def test_config_command(project, pdm): result = pdm(["config"], obj=project) assert result.exit_code == 0 assert "python.use_pyenv = True" in result.output result = pdm(["config", "-v"], obj=project) assert result.exit_code == 0 assert "Use the pyenv interpreter" in result.output def test_config_get_command(project, pdm): result = pdm(["config", "python.use_pyenv"], obj=project) assert result.exit_code == 0 assert result.output.strip() == "True" result = pdm(["config", "foo.bar"], obj=project) assert result.exit_code != 0 def test_config_set_command(project, pdm): result = pdm(["config", "python.use_pyenv", "false"], obj=project) assert result.exit_code == 0 result = pdm(["config", "python.use_pyenv"], obj=project) assert result.output.strip() == "False" result = pdm(["config", "foo.bar"], obj=project) assert result.exit_code != 0 result = pdm(["config", "-l", "cache_dir", "/path/to/bar"], obj=project) assert result.exit_code != 0 def test_config_del_command(project, pdm): result = pdm(["config", "-l", "python.use_pyenv", "false"], obj=project) assert result.exit_code == 0 result = pdm(["config", "python.use_pyenv"], obj=project) assert result.output.strip() == "False" result = pdm(["config", "-ld", "python.use_pyenv"], obj=project) assert result.exit_code == 0 result = pdm(["config", "python.use_pyenv"], obj=project) assert result.output.strip() == "True" def test_config_env_var_shadowing(project, pdm, monkeypatch): monkeypatch.setenv("PDM_PYPI_URL", "https://example.org/simple") result = pdm(["config", "pypi.url"], obj=project) assert result.output.strip() == "https://example.org/simple" result = pdm(["config", "pypi.url", "https://test.pypi.org/pypi"], obj=project) assert "config is shadowed by env var 'PDM_PYPI_URL'" in result.stderr result = pdm(["config", "pypi.url"], obj=project) assert result.output.strip() == "https://example.org/simple" monkeypatch.delenv("PDM_PYPI_URL") result = pdm(["config", "pypi.url"], obj=project) assert result.output.strip() == "https://test.pypi.org/pypi" def test_config_project_global_precedence(project, pdm): pdm(["config", "python.use_pyenv", "true"], obj=project) pdm(["config", "-l", "python.use_pyenv", "false"], obj=project) result = pdm(["config", "python.use_pyenv"], obj=project) assert result.output.strip() == "False" def test_specify_config_file(tmp_path, pdm, monkeypatch): tmp_path.joinpath("global_config.toml").write_text("strategy.resolve_max_rounds = 1000\n") with cd(tmp_path): result = pdm(["-c", "global_config.toml", "config", "strategy.resolve_max_rounds"]) assert result.exit_code == 0 assert result.output.strip() == "1000" monkeypatch.setenv("PDM_CONFIG_FILE", "global_config.toml") result = pdm(["config", "strategy.resolve_max_rounds"]) assert result.exit_code == 0 assert result.output.strip() == "1000" def test_default_repository_setting(project): repository = project.global_config.get_repository_config("pypi", "repository") assert repository.url == "https://upload.pypi.org/legacy/" assert repository.username is None assert repository.password is None repository = project.global_config.get_repository_config("testpypi", "repository") assert repository.url == "https://test.pypi.org/legacy/" repository = project.global_config.get_repository_config("nonexist", "repository") assert repository is None def test_repository_config_key_short(project): with pytest.raises(PdmUsageError): project.global_config["repository.test"] = {"url": "https://example.org/simple"} with pytest.raises(PdmUsageError): project.global_config["repository"] = "123" with pytest.raises(PdmUsageError): del project.global_config["repository"] def test_repository_overwrite_default(project): project.global_config["repository.pypi.username"] = "foo" project.global_config["repository.pypi.password"] = "bar" repository = project.global_config.get_repository_config("pypi", "repository") assert repository.url == "https://upload.pypi.org/legacy/" assert repository.username == "foo" assert repository.password == "bar" project.global_config["repository.pypi.url"] = "https://example.pypi.org/legacy/" repository = project.global_config.get_repository_config("pypi", "repository") assert repository.url == "https://example.pypi.org/legacy/" def test_hide_password_in_output_repository(project, pdm): assert project.global_config["repository.pypi.password"] is None project.global_config["repository.pypi.username"] = "testuser" project.global_config["repository.pypi.password"] = "secret" result = pdm(["config", "repository.pypi"], obj=project, strict=True) assert "password = " in result.output result = pdm(["config", "repository.pypi.password"], obj=project, strict=True) assert "" == result.output.strip() def test_hide_password_in_output_pypi(project, pdm): with pytest.raises(KeyError): assert project.global_config["pypi.extra.password"] is None project.global_config["pypi.extra.username"] = "testuser" project.global_config["pypi.extra.password"] = "secret" project.global_config["pypi.extra.url"] = "https://test/simple" result = pdm(["config", "pypi.extra"], obj=project, strict=True) assert "password = " in result.output result = pdm(["config", "pypi.extra.password"], obj=project, strict=True) assert "" == result.output.strip() result = pdm(["config"], obj=project) assert "pypi.extra.password" in result.output assert "" in result.output def test_config_get_repository(project, pdm): config = project.global_config["repository.pypi"] assert config == project.global_config.get_repository_config("pypi", "repository") assert project.global_config["repository.pypi.url"] == "https://upload.pypi.org/legacy/" result = pdm(["config", "repository.pypi"], obj=project, strict=True) assert result.stdout.strip() == "repository.pypi.url = https://upload.pypi.org/legacy/" assert ( project.global_config.get_repository_config("https://example.pypi.org/legacy/", "repository").url == "https://example.pypi.org/legacy/" ) result = pdm(["config", "repository.pypi.url"], obj=project, strict=True) assert result.stdout.strip() == "https://upload.pypi.org/legacy/" def test_config_set_repository(project): project.global_config["repository.pypi.url"] = "https://example.pypi.org/legacy/" project.global_config["repository.pypi.username"] = "foo" assert project.global_config["repository.pypi.url"] == "https://example.pypi.org/legacy/" assert project.global_config["repository.pypi.username"] == "foo" del project.global_config["repository.pypi.username"] assert project.global_config["repository.pypi.username"] is None def test_config_del_repository(project): project.global_config["repository.test.url"] = "https://example.org/simple" assert project.global_config.get_repository_config("test", "repository") is not None del project.global_config["repository.test"] assert project.global_config.get_repository_config("test", "repository") is None def test_config_password_save_into_keyring(project, keyring): project.global_config.update( { "pypi.extra.url": "https://extra.pypi.org/simple", "pypi.extra.username": "foo", "pypi.extra.password": "barbaz", "repository.pypi.username": "frost", "repository.pypi.password": "password", } ) assert project.global_config["pypi.extra.password"] == "barbaz" assert project.global_config["repository.pypi.password"] == "password" for key in ("pypi.extra", "repository.pypi"): assert "password" not in project.global_config._file_data[key] assert keyring.enabled assert keyring.get_auth_info("pdm-pypi-extra", "foo") == ("foo", "barbaz") assert keyring.get_auth_info("pdm-repository-pypi", None) == ("frost", "password") del project.global_config["pypi.extra"] del project.global_config["repository.pypi.password"] keyring.get_auth_info.cache_clear() assert keyring.get_auth_info("pdm-pypi-extra", "foo") is None assert keyring.get_auth_info("pdm-repository-pypi", None) is None def test_keyring_operation_error_disables_itself(project, keyring, mocker): saver = mocker.patch.object(keyring.provider, "save_auth_info", side_effect=RuntimeError()) getter = mocker.patch.object(keyring.provider, "get_auth_info") project.global_config.update( { "pypi.extra.url": "https://extra.pypi.org/simple", "pypi.extra.username": "foo", "pypi.extra.password": "barbaz", "repository.pypi.username": "frost", "repository.pypi.password": "password", } ) assert project.global_config["pypi.extra.password"] == "barbaz" assert project.global_config["repository.pypi.password"] == "password" saver.assert_called_once() getter.assert_not_called() assert not keyring.enabled assert keyring.get_auth_info("pdm-pypi-extra", "foo") is None assert keyring.get_auth_info("pdm-repository-pypi", None) is None pdm-2.23.1/tests/cli/test_fix.py000066400000000000000000000037551477560627500165240ustar00rootroot00000000000000import sys from pathlib import Path def test_fix_non_existing_problem(project, pdm): result = pdm(["fix", "non-existing"], obj=project) assert result.exit_code == 1 def test_fix_individual_problem(project, pdm): project._saved_python = None old_config = project.root / ".pdm.toml" old_config.write_text(f'[python]\nuse_pyenv = false\npath = "{Path(sys.executable).as_posix()}"\n') pdm(["fix", "project-config"], obj=project, strict=True) assert not old_config.exists() def test_show_fix_command(project, pdm): old_config = project.root / ".pdm.toml" old_config.write_text(f'[python]\nuse_pyenv = false\npath = "{Path(sys.executable).as_posix()}"\n') result = pdm(["info"], obj=project) assert "Run pdm fix to fix all" in result.stderr result = pdm(["fix", "-h"], obj=project) assert "Run pdm fix to fix all" not in result.stderr def test_show_fix_command_global_project(core, pdm, project_no_init): project = core.create_project(None, True, project_no_init.global_config.config_file) old_config = project.root / ".pdm.toml" old_config.write_text(f'[python]\nuse_pyenv = false\npath = "{Path(sys.executable).as_posix()}"\n') result = pdm(["info"], obj=project) assert "Run pdm fix -g to fix all" in result.stderr result = pdm(["fix", "-h"], obj=project) assert "Run pdm fix -g to fix all" not in result.stderr def test_fix_project_config(project, pdm): project._saved_python = None old_config = project.root / ".pdm.toml" old_config.write_text(f'[python]\nuse_pyenv = false\npath = "{Path(sys.executable).as_posix()}"\n') assert project.project_config["python.use_pyenv"] is False assert project._saved_python == Path(sys.executable).as_posix() pdm(["fix"], obj=project, strict=True) assert not old_config.exists() assert project.root.joinpath("pdm.toml").read_text() == "[python]\nuse_pyenv = false\n" assert project.root.joinpath(".pdm-python").read_text().strip() == Path(sys.executable).as_posix() pdm-2.23.1/tests/cli/test_hooks.py000066400000000000000000000240621477560627500170530ustar00rootroot00000000000000import shlex import sys from collections import namedtuple from textwrap import dedent import pytest from pdm.cli import actions from pdm.cli.options import from_splitted_env from pdm.signals import pdm_signals pytestmark = pytest.mark.usefixtures("repository", "working_set", "local_finder") def test_pre_script_fail_fast(project, pdm, capfd, mocker): project.pyproject.settings["scripts"] = { "pre_install": "python -c \"print('PRE INSTALL CALLED'); exit(1)\"", "post_install": "python -c \"print('POST INSTALL CALLED')\"", } project.pyproject.write() synchronize = mocker.patch("pdm.installers.synchronizers.Synchronizer.synchronize") result = pdm(["install"], obj=project) assert result.exit_code == 1 out, _ = capfd.readouterr() assert "PRE INSTALL CALLED" in out assert "POST INSTALL CALLED" not in out synchronize.assert_not_called() def test_pre_and_post_scripts(project, pdm, capfd, _echo): project.pyproject.settings["scripts"] = { "pre_script": "python echo.py pre_script", "post_script": "python echo.py post_script", "pre_test": "python echo.py pre_test", "test": "python echo.py test", "post_test": "python echo.py post_test", "pre_run": "python echo.py pre_run", "post_run": "python echo.py post_run", } project.pyproject.write() capfd.readouterr() pdm(["run", "test"], strict=True, obj=project) out, _ = capfd.readouterr() expected = dedent( """ pre_run CALLED pre_script CALLED pre_test CALLED test CALLED post_test CALLED post_script CALLED post_run CALLED """ ).strip() assert out.strip() == expected def test_composite_runs_all_hooks(project, pdm, capfd, _echo): project.pyproject.settings["scripts"] = { "test": {"composite": ["first", "second"]}, "pre_test": "python echo.py Pre-Test", "post_test": "python echo.py Post-Test", "first": "python echo.py First", "pre_first": "python echo.py Pre-First", "second": "python echo.py Second", "post_second": "python echo.py Post-Second", "pre_script": "python echo.py Pre-Script", "post_script": "python echo.py Post-Script", "pre_run": "python echo.py Pre-Run", "post_run": "python echo.py Post-Run", } project.pyproject.write() capfd.readouterr() pdm(["run", "test"], strict=True, obj=project) out, _ = capfd.readouterr() expected = dedent( """ Pre-Run CALLED Pre-Script CALLED Pre-Test CALLED Pre-Script CALLED Pre-First CALLED First CALLED Post-Script CALLED Pre-Script CALLED Second CALLED Post-Second CALLED Post-Script CALLED Post-Test CALLED Post-Script CALLED Post-Run CALLED """ ).strip() assert out.strip() == expected @pytest.mark.parametrize("option", [":all", ":pre,:post"]) def test_skip_all_hooks_option(project, pdm, capfd, option: str, _echo): project.pyproject.settings["scripts"] = { "test": {"composite": ["first", "second"]}, "pre_test": "python echo.py Pre-Test", "post_test": "python echo.py Post-Test", "first": "python echo.py First", "pre_first": "python echo.py Pre-First", "post_first": "python echo.py Post-First", "second": "python echo.py Second", "pre_second": "python echo.py Pre-Second", "post_second": "python echo.py Post-Second", "pre_script": "python echo.py Pre-Script", "post_script": "python echo.py Post-Script", "pre_run": "python echo.py Pre-Run", "post_run": "python echo.py Post-Run", } project.pyproject.write() capfd.readouterr() pdm(["run", f"--skip={option}", "first"], strict=True, obj=project) out, _ = capfd.readouterr() assert "Pre-First CALLED" not in out assert "First CALLED" in out assert "Post-First CALLED" not in out assert "Pre-Script CALLED" not in out assert "Post-Script CALLED" not in out capfd.readouterr() pdm(["run", f"--skip={option}", "test"], strict=True, obj=project) out, _ = capfd.readouterr() assert "Pre-Test CALLED" not in out assert "Pre-First CALLED" not in out assert "First CALLED" in out assert "Post-First CALLED" not in out assert "Pre-Second CALLED" not in out assert "Second CALLED" in out assert "Post-Second CALLED" not in out assert "Post-Test CALLED" not in out assert "Pre-Script CALLED" not in out assert "Post-Script CALLED" not in out assert "Pre-Run CALLED" not in out assert "Post-Run CALLED" not in out @pytest.mark.parametrize( "args", [ "--skip pre_test,post_first,second", "-k pre_test,post_first,second", "--skip pre_test --skip post_first --skip second", "-k pre_test -k post_first -k second", "--skip pre_test --skip post_first,second", "-k pre_test -k post_first,second", ], ) def test_skip_option(project, pdm, capfd, args, _echo): project.pyproject.settings["scripts"] = { "test": {"composite": ["first", "second"]}, "pre_test": "python echo.py Pre-Test", "post_test": "python echo.py Post-Test", "first": "python echo.py First", "pre_first": "python echo.py Pre-First", "post_first": "python echo.py Post-First", "second": "python echo.py Second", "pre_second": "python echo.py Pre-Second", "post_second": "python echo.py Post-Second", } project.pyproject.write() capfd.readouterr() pdm(["run", *shlex.split(args), "test"], strict=True, obj=project) out, _ = capfd.readouterr() assert "Pre-Test CALLED" not in out assert "Pre-First CALLED" in out assert "First CALLED" in out assert "Post-First CALLED" not in out assert "Pre-Second CALLED" not in out assert "Second CALLED" not in out assert "Post-Second CALLED" not in out assert "Post-Test CALLED" in out @pytest.mark.parametrize( "env, expected", [ ("pre_test", ["pre_test"]), ("pre_test,post_test", ["pre_test", "post_test"]), ("pre_test , post_test", ["pre_test", "post_test"]), (None, None), (" ", None), (" , ", None), ], ) def test_skip_option_default_from_env(env, expected, monkeypatch): if env is not None: monkeypatch.setenv("PDM_SKIP_HOOKS", env) # Default value is set once and not easily testable # so we test the function generating this default value assert from_splitted_env("PDM_SKIP_HOOKS", ",") == expected HookSpecs = namedtuple("HookSpecs", ["command", "hooks", "fixtures"]) this_python_version = f"{sys.version_info[0]}.{sys.version_info[1]}" KNOWN_COMMAND_HOOKS = ( ("add", "add requests", ("pre_lock", "post_lock"), ["working_set"]), ("build", "build", ("pre_build", "post_build"), []), ("init", "init --non-interactive", ("post_init",), []), ( "install", "install", ("pre_install", "post_install", "pre_lock", "post_lock"), ["repository"], ), ("lock", "lock", ("pre_lock", "post_lock"), []), ( "publish", "publish --username abc --password 123", ("pre_publish", "pre_build", "post_build", "post_publish"), ["mock_publish"], ), ("remove", "remove requests", ("pre_lock", "post_lock"), ["lock"]), ("sync", "sync", ("pre_install", "post_install"), ["lock"]), ("update", "update", ("pre_install", "post_install", "pre_lock", "post_lock"), []), ("use", f"use -f {this_python_version}", ("post_use",), []), ) parametrize_with_commands = pytest.mark.parametrize( "specs", [pytest.param(HookSpecs(command, hooks, fixtures), id=id) for id, command, hooks, fixtures in KNOWN_COMMAND_HOOKS], ) parametrize_with_hooks = pytest.mark.parametrize( "specs,hook", [ pytest.param(HookSpecs(command, hooks, fixtures), hook, id=f"{id}-{hook}") for id, command, hooks, fixtures in KNOWN_COMMAND_HOOKS for hook in hooks ], ) @pytest.fixture def hooked_project(project, capfd, specs, request): project.pyproject.settings["scripts"] = {hook: f"python -c \"print('{hook} CALLED')\"" for hook in pdm_signals} project.pyproject.write() for fixture in specs.fixtures: request.getfixturevalue(fixture) capfd.readouterr() return project @pytest.fixture def lock(project, capfd): project.add_dependencies(["requests"]) actions.do_lock(project) capfd.readouterr() @parametrize_with_commands def test_hooks(hooked_project, pdm, capfd, specs: HookSpecs): pdm(shlex.split(specs.command), strict=True, obj=hooked_project) out, _ = capfd.readouterr() for hook in specs.hooks: assert f"{hook} CALLED" in out @parametrize_with_hooks # Iterate over hooks as we need a clean slate for each run def test_skip_option_from_signal(hooked_project, pdm, capfd, specs: HookSpecs, hook: str): pdm([*shlex.split(specs.command), f"--skip={hook}"], strict=True, obj=hooked_project) out, _ = capfd.readouterr() assert f"{hook} CALLED" not in out for known_hook in specs.hooks: if known_hook != hook: assert f"{known_hook} CALLED" in out @parametrize_with_commands @pytest.mark.parametrize("option", [":all", ":pre,:post"]) def test_skip_all_option_from_signal(hooked_project, pdm, capfd, specs: HookSpecs, option: str): pdm( [*shlex.split(specs.command), f"--skip={option}"], strict=True, obj=hooked_project, ) out, _ = capfd.readouterr() for hook in pdm_signals: assert f"{hook} CALLED" not in out @parametrize_with_commands @pytest.mark.parametrize("prefix", ["pre", "post"]) def test_skip_pre_post_option_from_signal(hooked_project, pdm, capfd, specs: HookSpecs, prefix: str): pdm( [*shlex.split(specs.command), f"--skip=:{prefix}"], strict=True, obj=hooked_project, ) out, _ = capfd.readouterr() for hook in specs.hooks: if hook.startswith(prefix): assert f"{hook} CALLED" not in out else: assert f"{hook} CALLED" in out pdm-2.23.1/tests/cli/test_init.py000066400000000000000000000200121477560627500166620ustar00rootroot00000000000000import sys from unittest.mock import ANY import pytest from pdm.compat import tomllib from pdm.models.python import PythonInfo PYTHON_VERSION = f"{sys.version_info[0]}.{sys.version_info[1]}" @pytest.fixture(autouse=True) def enable_interactive_mode(mocker): from rich import get_console console = get_console() mocker.patch.object(console, "is_interactive", True) def test_init_validate_python_requires(project_no_init, pdm): result = pdm(["init"], input="\n\n\n\n\n\n\n3.7\n", obj=project_no_init) assert result.exit_code != 0 assert "InvalidSpecifier" in result.stderr def test_init_command(project_no_init, pdm, mocker): mocker.patch( "pdm.cli.commands.init.get_user_email_from_git", return_value=("Testing", "me@example.org"), ) pdm(["init"], input="\ntest-project\n\n\n\n\n\n\n", strict=True, obj=project_no_init) python_version = f"{project_no_init.python.major}.{project_no_init.python.minor}" data = { "project": { "authors": [{"email": "me@example.org", "name": "Testing"}], "dependencies": [], "description": "Default template for PDM package", "license": {"text": "MIT"}, "name": "test-project", "requires-python": f"=={python_version}.*", "readme": "README.md", "version": "0.1.0", }, "tool": {"pdm": {"distribution": False}}, } with open(project_no_init.root.joinpath("pyproject.toml"), "rb") as fp: assert tomllib.load(fp) == data def test_init_command_library(project_no_init, pdm, mocker): mocker.patch( "pdm.cli.commands.init.get_user_email_from_git", return_value=("Testing", "me@example.org"), ) result = pdm( ["init"], input="\ntest-project\n\ny\nTest Project\n1\n\n\n\n\n", obj=project_no_init, ) assert result.exit_code == 0 python_version = f"{project_no_init.python.major}.{project_no_init.python.minor}" data = { "project": { "authors": [{"email": "me@example.org", "name": "Testing"}], "dependencies": [], "description": "Test Project", "license": {"text": "MIT"}, "name": "test-project", "requires-python": f">={python_version}", "readme": "README.md", "version": "0.1.0", }, "build-system": {"build-backend": "setuptools.build_meta", "requires": ["setuptools>=61"]}, "tool": {"pdm": {"distribution": True}}, } with open(project_no_init.root.joinpath("pyproject.toml"), "rb") as fp: assert tomllib.load(fp) == data def test_init_non_interactive(project_no_init, pdm, mocker): mocker.patch( "pdm.cli.commands.init.get_user_email_from_git", return_value=("Testing", "me@example.org"), ) do_use = mocker.patch("pdm.cli.commands.use.Command.do_use", return_value=PythonInfo.from_path(sys.executable)) result = pdm(["init", "-n"], obj=project_no_init) assert result.exit_code == 0 python_version = f"{project_no_init.python.major}.{project_no_init.python.minor}" do_use.assert_called_once_with( project_no_init, ANY, first=True, ignore_remembered=True, ignore_requires_python=True, save=False, hooks=ANY, ) data = { "project": { "authors": [{"email": "me@example.org", "name": "Testing"}], "dependencies": [], "description": "Default template for PDM package", "license": {"text": "MIT"}, "name": project_no_init.root.name, "requires-python": f"=={python_version}.*", "readme": "README.md", "version": "0.1.0", }, "tool": {"pdm": {"distribution": False}}, } with open(project_no_init.root.joinpath("pyproject.toml"), "rb") as fp: assert tomllib.load(fp) == data def test_init_auto_create_venv(project_no_init, pdm, mocker): mocker.patch("pdm.models.python.PythonInfo.get_venv", return_value=None) project_no_init.project_config["python.use_venv"] = True result = pdm(["init"], input="\ntest-project\n\ny\nTest Project\n1\n\n\n\n\n", obj=project_no_init) assert result.exit_code == 0 assert project_no_init.python.executable.parent.parent == project_no_init.root / ".venv" assert ".pdm-python" in (project_no_init.root / ".gitignore").read_text() def test_init_auto_create_venv_specify_python(project_no_init, pdm, mocker): mocker.patch("pdm.models.python.PythonInfo.get_venv", return_value=None) project_no_init.project_config["python.use_venv"] = True result = pdm( ["init", f"--python={PYTHON_VERSION}"], input="\n\n\n\n\n\n\n", obj=project_no_init, ) assert result.exit_code == 0 assert project_no_init.python.executable.parent.parent == project_no_init.root / ".venv" def test_init_with_backend_default_library(project_no_init, pdm): pdm(["init", "--backend", "flit-core"], input="\n\n\n\n\n\n\n\n\n", obj=project_no_init) assert project_no_init.backend.__class__.__name__ == "FlitBackend" def test_init_with_backend_default_library_non_interactive(project_no_init, pdm): pdm(["init", "-n", "--backend", "flit-core"], obj=project_no_init) assert project_no_init.backend.__class__.__name__ == "FlitBackend" def test_init_with_license_non_interactive(project_no_init, pdm, mocker): mocker.patch( "pdm.cli.commands.init.get_user_email_from_git", return_value=("Testing", "me@example.org"), ) do_use = mocker.patch("pdm.cli.commands.use.Command.do_use", return_value=PythonInfo.from_path(sys.executable)) expected_license = "Proprietary" result = pdm(["init", "-n", "--license", expected_license], obj=project_no_init) assert result.exit_code == 0 python_version = f"{project_no_init.python.major}.{project_no_init.python.minor}" do_use.assert_called_once_with( project_no_init, ANY, first=True, ignore_remembered=True, ignore_requires_python=True, save=False, hooks=ANY, ) data = { "project": { "authors": [{"email": "me@example.org", "name": "Testing"}], "dependencies": [], "description": "Default template for PDM package", "license": {"text": f"{expected_license}"}, "name": project_no_init.root.name, "requires-python": f"=={python_version}.*", "readme": "README.md", "version": "0.1.0", }, "tool": {"pdm": {"distribution": False}}, } with open(project_no_init.root.joinpath("pyproject.toml"), "rb") as fp: assert tomllib.load(fp) == data def test_init_with_project_version_non_interactive(project_no_init, pdm, mocker): mocker.patch( "pdm.cli.commands.init.get_user_email_from_git", return_value=("Testing", "me@example.org"), ) do_use = mocker.patch("pdm.cli.commands.use.Command.do_use", return_value=PythonInfo.from_path(sys.executable)) expected_project_version = "2.0.42" result = pdm(["init", "-n", "--project-version", expected_project_version], obj=project_no_init) assert result.exit_code == 0 python_version = f"{project_no_init.python.major}.{project_no_init.python.minor}" do_use.assert_called_once_with( project_no_init, ANY, first=True, ignore_remembered=True, ignore_requires_python=True, save=False, hooks=ANY, ) data = { "project": { "authors": [{"email": "me@example.org", "name": "Testing"}], "dependencies": [], "description": "Default template for PDM package", "license": {"text": "MIT"}, "name": project_no_init.root.name, "requires-python": f"=={python_version}.*", "readme": "README.md", "version": f"{expected_project_version}", }, "tool": {"pdm": {"distribution": False}}, } with open(project_no_init.root.joinpath("pyproject.toml"), "rb") as fp: assert tomllib.load(fp) == data pdm-2.23.1/tests/cli/test_install.py000066400000000000000000000421021477560627500173710ustar00rootroot00000000000000import pytest from pdm.cli import actions from pdm.models.markers import EnvSpec from pdm.pytest import Distribution from pdm.utils import cd def test_sync_packages_with_group_all(project, working_set, pdm): project.add_dependencies(["requests"]) project.add_dependencies(["pytz"], "date") project.add_dependencies(["pyopenssl"], "ssl") pdm(["install", "-G:all"], obj=project, strict=True) assert "pytz" in working_set assert "requests" in working_set assert "idna" in working_set assert "pyopenssl" in working_set def test_sync_packages_with_all_dev(project, working_set, pdm): project.add_dependencies(["requests"]) project.add_dependencies(["pytz"], "date", True) project.add_dependencies(["pyopenssl"], "ssl", True) pdm(["install", "-d", "--no-default"], obj=project, strict=True) assert "requests" not in working_set assert "idna" not in working_set assert "pytz" in working_set assert "pyopenssl" in working_set def test_sync_no_lockfile(project, pdm): project.add_dependencies(["requests"]) result = pdm(["sync"], obj=project) assert result.exit_code == 1 def test_sync_clean_packages(project, working_set, pdm): for candidate in [ Distribution("foo", "0.1.0"), Distribution("chardet", "3.0.1"), Distribution("idna", "2.7"), ]: working_set.add_distribution(candidate) pdm(["add", "--no-sync", "requests"], obj=project, strict=True) pdm(["sync", "--clean"], obj=project, strict=True) assert "foo" not in working_set def test_sync_dry_run(project, working_set, pdm): for candidate in [ Distribution("foo", "0.1.0"), Distribution("chardet", "3.0.1"), Distribution("idna", "2.7"), ]: working_set.add_distribution(candidate) pdm(["add", "--no-sync", "requests"], obj=project, strict=True) pdm(["sync", "--clean", "--dry-run"], obj=project, strict=True) assert "foo" in working_set assert "requests" not in working_set assert working_set["chardet"].version == "3.0.1" def test_sync_only_different(project, working_set, pdm): working_set.add_distribution(Distribution("foo", "0.1.0")) working_set.add_distribution(Distribution("chardet", "3.0.1")) working_set.add_distribution(Distribution("idna", "2.7")) result = pdm(["add", "requests"], obj=project, strict=True) out = result.stdout assert "3 to add" in out, out assert "1 to update" in out assert "foo" in working_set assert "test-project" in working_set, list(working_set) assert working_set["chardet"].version == "3.0.4" def test_sync_in_sequential_mode(project, working_set, pdm): project.project_config["install.parallel"] = False result = pdm(["add", "requests"], obj=project, strict=True) assert "5 to add" in result.stdout assert "test-project" in working_set assert working_set["chardet"].version == "3.0.4" def test_sync_packages_with_groups(project, working_set, pdm): project.add_dependencies(["requests"]) project.add_dependencies(["pytz"], "date") pdm(["install", "-Gdate"], obj=project, strict=True) assert "pytz" in working_set assert "requests" in working_set assert "idna" in working_set @pytest.mark.parametrize("prod_option", [("--prod",), ()]) def test_sync_production_packages(project, working_set, prod_option, pdm): project.add_dependencies(["requests"]) project.add_dependencies(["pytz"], "dev", dev=True) pdm(["install", *prod_option], obj=project, strict=True) assert "requests" in working_set assert ("pytz" in working_set) == (not prod_option) def test_sync_without_self(project, working_set, pdm): project.add_dependencies(["requests"]) pdm(["install", "--no-self"], obj=project, strict=True) assert project.name not in working_set, list(working_set) def test_sync_with_index_change(project, index, pdm): project.project_config["pypi.url"] = "https://my.pypi.org/simple" project.pyproject.metadata["requires-python"] = ">=3.6" project.pyproject.metadata["dependencies"] = ["future-fstrings"] project.pyproject.write() index["/simple/future-fstrings/"] = b"""

future-fstrings

future_fstrings-1.2.0.tar.gz """ pdm(["lock"], obj=project, strict=True, cleanup=False) file_hashes = project.lockfile["package"][0]["files"] assert [e["hash"] for e in file_hashes] == [ "sha256:90e49598b553d8746c4dc7d9442e0359d038c3039d802c91c0a55505da318c63" ] # Mimic the CDN inconsistencies of PyPI simple index. See issues/596. del index["/simple/future-fstrings/"] pdm(["sync", "--no-self"], obj=project, strict=True) def test_install_command(project, pdm, mocker): do_lock = mocker.patch.object(actions, "do_lock") do_sync = mocker.patch.object(actions, "do_sync") pdm(["install"], obj=project) do_lock.assert_called_once() do_sync.assert_called_once() def test_sync_command(project, pdm, mocker): pdm(["lock"], obj=project) do_sync = mocker.patch.object(actions, "do_sync") pdm(["sync"], obj=project) do_sync.assert_called_once() @pytest.mark.usefixtures("working_set") def test_install_with_lockfile(project, pdm): result = pdm(["lock", "-v"], obj=project) assert result.exit_code == 0 result = pdm(["install"], obj=project) assert "Lockfile" not in result.stderr project.add_dependencies(["pytz"], "default") result = pdm(["install"], obj=project) assert "Lockfile hash doesn't match" in result.stderr assert "pytz" in project.get_locked_repository().candidates assert project.is_lockfile_hash_match() def test_install_with_dry_run(project, pdm, repository): project.add_dependencies(["pytz"], "default") result = pdm(["install", "--dry-run"], obj=project) project.lockfile.reload() assert "pytz" not in project.get_locked_repository().candidates assert "pytz 2019.3" in result.output def test_install_frozen_lockfile(project, pdm, working_set): project.add_dependencies(["requests"], "default") result = pdm(["install", "--frozen-lockfile"], obj=project) assert result.exit_code == 0 assert not project.lockfile.exists() assert "urllib3" in working_set assert "requests" in working_set def test_install_no_lock_deprecated(project, pdm, working_set): project.add_dependencies(["requests"], "default") result = pdm(["install", "--no-lock"], obj=project) assert result.exit_code == 0 assert not project.lockfile.exists() assert "urllib3" in working_set assert "requests" in working_set assert "WARNING: --no-lock is deprecated" in result.stderr def test_install_check(pdm, project, repository): result = pdm(["install", "--check"], obj=project) assert result.exit_code == 1 result = pdm(["add", "requests", "--no-sync"], obj=project) project.add_dependencies(["requests>=2.0"]) result = pdm(["install", "--check"], obj=project) assert result.exit_code == 1 def test_sync_with_clean_unselected_option(project, working_set, pdm): project.add_dependencies(["requests>=2.0"]) project.add_dependencies(["django"], "web", True) pdm(["install"], obj=project, strict=True) assert all(p in working_set for p in ("requests", "urllib3", "django", "pytz")), list(working_set) pdm(["sync", "--prod", "--clean-unselected"], obj=project, strict=True) assert "requests" in working_set assert "urllib3" in working_set assert "django" not in working_set def test_install_referencing_self_package(project, working_set, pdm): project.add_dependencies(["pytz"], to_group="tz") project.add_dependencies(["urllib3"], to_group="web") project.add_dependencies(["test-project[tz,web]"], to_group="all") pdm(["install", "-Gall"], obj=project, strict=True) assert "pytz" in working_set assert "urllib3" in working_set def test_install_monorepo_with_rel_paths(fixture_project, pdm, working_set): project = fixture_project("test-monorepo") with cd(project.root): pdm(["install"], obj=project, strict=True) for package in ("package-a", "package-b", "core"): assert package in working_set @pytest.mark.usefixtures("repository") def test_install_retry(project, pdm, mocker): pdm(["add", "certifi", "chardet", "--no-sync"], obj=project) handler = mocker.patch( "pdm.installers.synchronizers.Synchronizer.install_candidate", side_effect=RuntimeError, ) result = pdm(["install"], obj=project) assert result.exit_code == 1 handler.assert_has_calls( [ mocker.call("certifi", mocker.ANY), mocker.call("chardet", mocker.ANY), mocker.call("certifi", mocker.ANY), mocker.call("chardet", mocker.ANY), ], any_order=True, ) @pytest.mark.usefixtures("repository") def test_install_fail_fast(project, pdm, mocker): project.project_config["install.parallel"] = False pdm(["add", "certifi", "chardet", "pytz", "--no-sync"], obj=project) handler = mocker.patch( "pdm.installers.synchronizers.Synchronizer.install_candidate", side_effect=RuntimeError, ) result = pdm(["install", "--fail-fast"], obj=project) assert result.exit_code == 1 assert handler.call_count == 1 @pytest.mark.usefixtures("working_set") def test_install_groups_not_in_lockfile(project, pdm): project.add_dependencies(["pytz"], to_group="tz") project.add_dependencies(["urllib3"], to_group="web") pdm(["install", "-vv"], obj=project, strict=True) assert project.lockfile.groups == ["default"] all_locked_packages = project.get_locked_repository().candidates for package in ["pytz", "urllib3"]: assert package not in all_locked_packages with pytest.raises(RuntimeError, match="Requested groups not in lockfile"): pdm(["install", "-Gtz"], obj=project, strict=True) def test_install_locked_groups(project, pdm, working_set): project.add_dependencies(["urllib3"]) project.add_dependencies(["pytz"], to_group="tz") pdm(["lock", "-Gtz", "--no-default"], obj=project, strict=True) pdm(["sync"], obj=project, strict=True) assert "pytz" in working_set assert "urllib3" not in working_set def test_install_groups_and_lock(project, pdm, working_set): project.add_dependencies(["urllib3"]) project.add_dependencies(["pytz"], to_group="tz") pdm(["install", "-Gtz", "--no-default"], obj=project, strict=True) assert "pytz" in working_set assert "urllib3" not in working_set assert project.lockfile.groups == ["tz"] assert "pytz" in project.get_locked_repository().candidates assert "urllib3" not in project.get_locked_repository().candidates def test_install_requirement_with_extras(project, pdm, working_set): project.add_dependencies(["requests==2.19.1"]) project.add_dependencies(["requests[socks]"], to_group="socks") pdm(["lock", "-Gsocks"], obj=project, strict=True) pdm(["sync", "-Gsocks"], obj=project, strict=True) assert "pysocks" in working_set def test_fix_package_type_and_update(fixture_project, pdm, working_set): project = fixture_project("test-package-type-fixer") pdm(["fix", "package-type"], obj=project, strict=True) pdm(["update"], obj=project, strict=True) assert "test-package-type-fixer" not in working_set exclusion_cases = [ pytest.param(("-G", ":all", "--without", "tz,ssl"), id="-G :all --without tz,ssl"), pytest.param(("-G", ":all", "--without", "tz", "--without", "ssl"), id="-G :all --without tz --without ssl"), pytest.param(("--with", ":all", "--without", "tz,ssl"), id="--with all --without tz,ssl"), pytest.param(("--with", ":all", "--without", "tz", "--without", "ssl"), id="--with all --without tz --without ssl"), pytest.param(("--without", "tz", "--without", "ssl"), id="--without tz --without ssl"), pytest.param(("--without", "tz,ssl"), id="--without tz,ssl"), ] @pytest.mark.parametrize("args", exclusion_cases) def test_install_all_with_excluded_groups(project, working_set, pdm, args): project.add_dependencies(["urllib3"], "url") project.add_dependencies(["pytz"], "tz", True) project.add_dependencies(["pyopenssl"], "ssl") pdm(["install", *args], obj=project, strict=True) assert "urllib3" in working_set assert "pytz" not in working_set assert "pyopenssl" not in working_set @pytest.mark.parametrize("args", exclusion_cases) def test_sync_all_with_excluded_groups(project, working_set, pdm, args): project.add_dependencies(["urllib3"], "url") project.add_dependencies(["pytz"], "tz", True) project.add_dependencies(["pyopenssl"], "ssl") pdm(["lock", "-G:all"], obj=project, strict=True) pdm(["sync", *args], obj=project, strict=True) assert "urllib3" in working_set assert "pytz" not in working_set assert "pyopenssl" not in working_set def test_excluded_groups_ignored_if_prod_passed(project, working_set, pdm): project.add_dependencies(["urllib3"], "url") project.add_dependencies(["pytz"], "tz") project.add_dependencies(["pyopenssl"], "ssl") pdm(["install", "--prod", "--without", "ssl"], obj=project, strict=True) assert "urllib3" not in working_set assert "pytz" not in working_set assert "pyopenssl" not in working_set def test_excluded_groups_ignored_if_dev_passed(project, working_set, pdm): project.add_dependencies(["urllib3"], "url") project.add_dependencies(["pytz"], "tz") project.add_dependencies(["pyopenssl"], "ssl") pdm(["install", "--dev", "--without", "ssl"], obj=project, strict=True) assert "urllib3" not in working_set assert "pytz" not in working_set assert "pyopenssl" not in working_set @pytest.mark.parametrize("nested", [False, True]) @pytest.mark.parametrize("groups", [("default",), None]) def test_install_from_multi_target_lock(project, pdm, repository, nested, groups): from pdm.cli.actions import resolve_candidates_from_lockfile deps = [ 'django<2; sys_platform == "win32"', 'django>=2; sys_platform != "win32"', ] if nested: repository.add_candidate("foo", "0.1.0") repository.add_dependencies("foo", "0.1.0", deps) project.add_dependencies(["foo"]) else: project.add_dependencies(deps) pdm(["lock", "--platform", "windows"], obj=project, strict=True) pdm(["lock", "--platform", "linux", "--append"], obj=project, strict=True) candidates = resolve_candidates_from_lockfile( project, project.get_dependencies(), env_spec=EnvSpec.from_spec("==3.11", "windows"), groups=groups ) assert candidates["django"].version == "1.11.8" assert "sqlparse" not in candidates candidates = resolve_candidates_from_lockfile( project, project.get_dependencies(), env_spec=EnvSpec.from_spec("==3.11", "linux"), groups=groups ) assert candidates["django"].version == "2.2.9" assert "sqlparse" in candidates def test_install_from_lock_with_higher_version(project, pdm, working_set): project.add_dependencies(["django"]) pdm(["lock", "--platform", "manylinux_2_20_x86_64"], obj=project, strict=True) # linux is an alias for manylinux_2_17_x86_64 which is lower than the target project.environment.__dict__["spec"] = EnvSpec.from_spec("==3.11", "linux") result = pdm(["install"], obj=project) assert result.exit_code == 0 assert "WARNING: Found lock target" in result.stderr def test_install_from_lock_with_lower_version(project, pdm, working_set): project.add_dependencies(["django"]) pdm(["lock", "--platform", "linux"], obj=project, strict=True) project.environment.__dict__["spec"] = EnvSpec.from_spec("==3.11", "manylinux_2_20_x86_64") result = pdm(["install"], obj=project) assert result.exit_code == 0 @pytest.mark.parametrize( "python,platform", [("==3.11", "macos"), ("==3.10", "manylinux_2_17_x86_64"), ("==3.11", "manylinux_2_17_aarch64")] ) @pytest.mark.parametrize("python_option", ["3.11", ">=3.11"]) def test_install_from_lock_with_incompatible_targets(project, pdm, python, platform, python_option): pdm(["lock", "--platform", "linux", "--python", python_option], obj=project, strict=True) project.environment.__dict__["spec"] = EnvSpec.from_spec(python, platform) result = pdm(["install"], obj=project) assert result.exit_code == 1 assert "No compatible lock target found" in result.stderr @pytest.mark.network @pytest.mark.uv def test_uv_install(project, pdm): project.project_config.update({"use_uv": True, "python.use_venv": True}) project._saved_python = None pdm(["add", "requests"], obj=project, strict=True) working_set = project.environment.get_working_set() assert "requests" in working_set assert "urllib3" in working_set assert "idna" in working_set @pytest.mark.network @pytest.mark.uv def test_uv_install_pep582_not_allowed(project, pdm): project.project_config.update({"use_uv": True}) pdm(["add", "requests", "--no-sync"], obj=project, strict=True) result = pdm(["sync"], obj=project) assert result.exit_code == 1 assert "doesn't support PEP 582 local packages" in result.stderr pdm-2.23.1/tests/cli/test_list.py000066400000000000000000000747051477560627500167140ustar00rootroot00000000000000import json import os import pathlib from unittest import mock import pytest from rich.box import ASCII from pdm.cli.commands.list import Command from pdm.models.specifiers import PySpecSet from pdm.pytest import Distribution from tests import FIXTURES def test_list_command(project, pdm, mocker): # Calls the correct handler within the Command m = mocker.patch.object(Command, "handle_list") pdm(["list"], obj=project) m.assert_called_once() @pytest.mark.usefixtures("working_set") def test_list_graph_command(project, pdm, mocker): # Calls the correct handler within the list command m = mocker.patch.object(Command, "handle_graph") pdm(["list", "--tree"], obj=project) m.assert_called_once() @mock.patch("rich.console.ConsoleOptions.ascii_only", lambda: True) @pytest.mark.usefixtures("working_set") def test_list_dependency_graph(project, pdm): # Shows a line that contains a sub requirement (any order). pdm(["add", "requests"], obj=project, strict=True) result = pdm(["list", "--tree"], obj=project) expected = "-- urllib3 1.22 [ required: <1.24,>=1.21.1 ]" in result.outputs assert expected, result.outputs @mock.patch("rich.console.ConsoleOptions.ascii_only", lambda: True) @pytest.mark.usefixtures("working_set") def test_list_dependency_graph_include_exclude(project, pdm): # Just include dev packages in the graph project.environment.python_requires = PySpecSet(">=3.6") dep_path = FIXTURES.joinpath("projects/demo").as_posix() pdm(["add", "-de", f"{dep_path}[security]"], obj=project, strict=True) # Output full graph result = pdm(["list", "--tree"], obj=project) expects = ( "demo 0.0.1 [ Not required ]\n", "+-- chardet 3.0.4 [ required: Any ]\n" if os.name == "nt" else "", "`-- idna 2.7 [ required: Any ]\n", "requests 2.19.1 [ Not required ]\n", "+-- certifi 2018.11.17 [ required: >=2017.4.17 ]\n", "+-- chardet 3.0.4 [ required: <3.1.0,>=3.0.2 ]\n", "+-- idna 2.7 [ required: <2.8,>=2.5 ]\n", "`-- urllib3 1.22 [ required: <1.24,>=1.21.1 ]\n", ) expects = "".join(expects) assert expects == result.outputs # Now exclude the dev dep. result = pdm(["list", "--tree", "--exclude", "dev"], obj=project) expects = "" assert expects == result.outputs # Only include the dev dep result = pdm(["list", "--tree", "--include", "dev", "--exclude", "*"], obj=project) expects = "demo[security] 0.0.1 [ required: Any ]\n" expects = "".join(expects) assert expects == result.outputs @pytest.mark.usefixtures("working_set") def test_list_dependency_graph_with_circular_forward(project, pdm, repository): # shows a circular dependency repository.add_candidate("foo", "0.1.0") repository.add_candidate("foo-bar", "0.1.0") repository.add_dependencies("foo", "0.1.0", ["foo-bar"]) repository.add_dependencies("foo-bar", "0.1.0", ["foo"]) pdm(["add", "foo"], obj=project, strict=True) result = pdm(["list", "--tree"], obj=project) circular_found = "foo [circular]" in result.outputs assert circular_found @mock.patch("rich.console.ConsoleOptions.ascii_only", lambda: True) @pytest.mark.usefixtures("working_set") def test_list_dependency_graph_with_circular_reverse(project, pdm, repository): repository.add_candidate("foo", "0.1.0") repository.add_candidate("foo-bar", "0.1.0") repository.add_candidate("baz", "0.1.0") repository.add_dependencies("foo", "0.1.0", ["foo-bar"]) repository.add_dependencies("foo-bar", "0.1.0", ["foo", "baz"]) repository.add_dependencies("baz", "0.1.0", []) pdm(["add", "foo"], obj=project, strict=True) # --reverse flag shows packages reversed and with [circular] result = pdm(["list", "--tree", "--reverse"], obj=project) expected = ( "baz 0.1.0 \n" "`-- foo-bar 0.1.0 [ requires: Any ]\n" " `-- foo 0.1.0 [ requires: Any ]\n" " +-- foo-bar [circular] [ requires: Any ]\n" " `-- test-project 0.0.0 [ requires: >=0.1.0 ]\n" ) assert expected in result.outputs # -r flag shows packages reversed and with [circular] result = pdm(["list", "--tree", "-r"], obj=project) assert expected in result.outputs def test_list_reverse_without_graph_flag(project, pdm): # results in PDMUsageError since --reverse needs --tree result = pdm(["list", "--reverse"], obj=project) assert "[PdmUsageError]" in result.stderr assert "--reverse cannot be used without --tree" in result.stderr result = pdm(["list", "-r"], obj=project) assert "[PdmUsageError]" in result.stderr assert "--reverse cannot be used without --tree" in result.stderr @mock.patch("rich.console.ConsoleOptions.ascii_only", lambda: True) @pytest.mark.usefixtures("working_set") def test_list_reverse_dependency_graph(project, pdm): # requests visible on leaf node pdm(["add", "requests"], obj=project, strict=True) result = pdm(["list", "--tree", "--reverse"], obj=project) assert "`-- requests 2.19.1 [ requires: <1.24,>=1.21.1 ]" in result.outputs @pytest.mark.usefixtures("working_set") def test_list_json(project, pdm): # check json output matches graph output pdm(["add", "requests", "--no-self"], obj=project, strict=True) result = pdm(["list", "--tree", "--json"], obj=project) expected = [ { "package": "requests", "version": "2.19.1", "required": ">=2.19.1", "dependencies": [ { "package": "certifi", "version": "2018.11.17", "required": ">=2017.4.17", "dependencies": [], }, { "package": "chardet", "version": "3.0.4", "required": "<3.1.0,>=3.0.2", "dependencies": [], }, { "package": "idna", "version": "2.7", "required": "<2.8,>=2.5", "dependencies": [], }, { "package": "urllib3", "version": "1.22", "required": "<1.24,>=1.21.1", "dependencies": [], }, ], } ] assert expected == json.loads(result.outputs) @pytest.mark.usefixtures("working_set") def test_list_json_with_pattern(project, pdm): pdm(["add", "requests", "--no-self"], obj=project, strict=True) result = pdm(["list", "--tree", "--json", "chardet"], obj=project) expected = [ { "package": "chardet", "version": "3.0.4", "required": "<3.1.0,>=3.0.2", "dependencies": [], }, ] assert expected == json.loads(result.outputs) @pytest.mark.usefixtures("working_set") def test_list_json_reverse(project, pdm): # check json output matches reversed graph pdm(["add", "requests", "--no-self"], obj=project, strict=True) result = pdm(["list", "--tree", "--reverse", "--json"], obj=project) expected = [ { "package": "certifi", "version": "2018.11.17", "requires": None, "dependents": [ { "package": "requests", "version": "2.19.1", "requires": ">=2017.4.17", "dependents": [], } ], }, { "package": "chardet", "version": "3.0.4", "requires": None, "dependents": [ { "package": "requests", "version": "2.19.1", "requires": "<3.1.0,>=3.0.2", "dependents": [], } ], }, { "package": "idna", "version": "2.7", "requires": None, "dependents": [ { "package": "requests", "version": "2.19.1", "requires": "<2.8,>=2.5", "dependents": [], } ], }, { "package": "urllib3", "version": "1.22", "requires": None, "dependents": [ { "package": "requests", "version": "2.19.1", "requires": "<1.24,>=1.21.1", "dependents": [], } ], }, ] assert expected == json.loads(result.outputs) @pytest.mark.usefixtures("working_set") def test_list_reverse_json_with_pattern(project, pdm): # check json output matches reversed graph pdm(["add", "requests", "--no-self"], obj=project, strict=True) result = pdm(["list", "--tree", "--reverse", "--json", "certifi"], obj=project) expected = [ { "package": "certifi", "version": "2018.11.17", "requires": None, "dependents": [ { "package": "requests", "version": "2.19.1", "requires": ">=2017.4.17", "dependents": [], } ], }, ] assert expected == json.loads(result.outputs) @pytest.mark.usefixtures("working_set") def test_list_json_with_circular_forward(project, pdm, repository): # circulars are handled in json exports repository.add_candidate("foo", "0.1.0") repository.add_candidate("foo-bar", "0.1.0") repository.add_candidate("baz", "0.1.0") repository.add_dependencies("baz", "0.1.0", ["foo"]) repository.add_dependencies("foo", "0.1.0", ["foo-bar"]) repository.add_dependencies("foo-bar", "0.1.0", ["foo"]) pdm(["add", "baz", "--no-self"], obj=project, strict=True) result = pdm(["list", "--tree", "--json"], obj=project) expected = [ { "package": "baz", "version": "0.1.0", "required": ">=0.1.0", "dependencies": [ { "package": "foo", "version": "0.1.0", "required": "Any", "dependencies": [ { "package": "foo-bar", "version": "0.1.0", "required": "Any", "dependencies": [ { "package": "foo", "version": "0.1.0", "required": "Any", "dependencies": [], } ], } ], } ], }, ] assert expected == json.loads(result.outputs) @pytest.mark.usefixtures("working_set") def test_list_json_with_circular_reverse(project, pdm, repository): # circulars are handled in reversed json exports repository.add_candidate("foo", "0.1.0") repository.add_candidate("foo-bar", "0.1.0") repository.add_candidate("baz", "0.1.0") repository.add_dependencies("foo", "0.1.0", ["foo-bar"]) repository.add_dependencies("foo-bar", "0.1.0", ["foo", "baz"]) repository.add_dependencies("baz", "0.1.0", []) pdm(["add", "foo", "--no-self"], obj=project, strict=True) result = pdm(["list", "--tree", "--reverse", "--json"], obj=project) expected = [ { "package": "baz", "version": "0.1.0", "requires": None, "dependents": [ { "package": "foo-bar", "version": "0.1.0", "requires": "Any", "dependents": [ { "package": "foo", "version": "0.1.0", "requires": "Any", "dependents": [ { "package": "foo-bar", "version": "0.1.0", "requires": "Any", "dependents": [], } ], } ], } ], }, ] assert expected == json.loads(result.outputs) def test_list_field_unknown(project, pdm): # unknown list fields flagged to user result = pdm(["list", "--fields", "notvalid"], obj=project) assert "[PdmUsageError]" in result.stderr assert "--fields must specify one or more of:" in result.stderr def test_list_sort_unknown(project, pdm): # unknown sort fields flagged to user result = pdm(["list", "--sort", "notvalid"], obj=project) assert "[PdmUsageError]" in result.stderr assert "--sort key must be one of:" in result.stderr def test_list_freeze_banned_options(project, pdm): # other flags cannot be used with --freeze result = pdm(["list", "--freeze", "--tree"], obj=project) expected = "--tree cannot be used with --freeze" assert expected in result.outputs result = pdm(["list", "--freeze", "--reverse"], obj=project) expected = "--reverse cannot be used without --tree" assert expected in result.outputs result = pdm(["list", "--freeze", "-r"], obj=project) expected = "--reverse cannot be used without --tree" assert expected in result.outputs result = pdm(["list", "--freeze", "--fields", "name"], obj=project) expected = "--fields cannot be used with --freeze" assert expected in result.outputs result = pdm(["list", "--freeze", "--resolve"], obj=project) expected = "--resolve cannot be used with --freeze" assert expected in result.outputs result = pdm(["list", "--freeze", "--sort", "version"], obj=project) expected = "--sort cannot be used with --freeze" assert expected in result.outputs result = pdm(["list", "--freeze", "--csv"], obj=project) expected = "--csv cannot be used with --freeze" assert expected in result.outputs result = pdm(["list", "--freeze", "--json"], obj=project) expected = "--json cannot be used with --freeze" assert expected in result.outputs result = pdm(["list", "--freeze", "--markdown"], obj=project) expected = "--markdown cannot be used with --freeze" assert expected in result.outputs result = pdm(["list", "--freeze", "--include", "dev"], obj=project) expected = "--include/--exclude cannot be used with --freeze" assert expected in result.outputs result = pdm(["list", "--freeze", "--exclude", "dev"], obj=project) expected = "--include/--exclude cannot be used with --freeze" assert expected in result.outputs def test_list_multiple_export_formats(project, pdm): # export formats cannot be used with each other result = pdm(["list", "--csv", "--markdown"], obj=project) expected = "--markdown: not allowed with argument --csv" assert expected in result.outputs result = pdm(["list", "--csv", "--json"], obj=project) expected = "--json: not allowed with argument --csv" assert expected in result.outputs result = pdm(["list", "--markdown", "--csv"], obj=project) expected = "--csv: not allowed with argument --markdown" assert expected in result.outputs result = pdm(["list", "--markdown", "--json"], obj=project) expected = "--json: not allowed with argument --markdown" assert expected in result.outputs result = pdm(["list", "--json", "--markdown"], obj=project) expected = "--markdown: not allowed with argument --json" assert expected in result.outputs result = pdm(["list", "--json", "--csv"], obj=project) expected = "--csv: not allowed with argument --json" assert expected in result.outputs @mock.patch("pdm.termui.ROUNDED", ASCII) @pytest.mark.usefixtures("working_set") def test_list_bare(project, pdm): pdm(["add", "requests"], obj=project, strict=True) result = pdm(["list"], obj=project) # Ordering can be different on different platforms # and python versions. assert "| name | version | location |\n" in result.output assert "| certifi | 2018.11.17 | |\n" in result.output assert "| chardet | 3.0.4 | |\n" in result.output assert "| idna | 2.7 | |\n" in result.output assert "| requests | 2.19.1 | |\n" in result.output assert "| urllib3 | 1.22 | |\n" in result.output assert "| test-project | 0.0.0 | |\n" in result.output @mock.patch("pdm.termui.ROUNDED", ASCII) @pytest.mark.usefixtures("working_set") def test_list_bare_sorted_name(project, pdm): pdm(["add", "requests"], obj=project, strict=True) result = pdm(["list", "--sort", "name"], obj=project) expected = ( "+--------------------------------------+\n" "| name | version | location |\n" "|--------------+------------+----------|\n" "| certifi | 2018.11.17 | |\n" "| chardet | 3.0.4 | |\n" "| idna | 2.7 | |\n" "| requests | 2.19.1 | |\n" "| test-project | 0.0.0 | |\n" "| urllib3 | 1.22 | |\n" "+--------------------------------------+\n" ) assert expected == result.output @mock.patch("pdm.termui.ROUNDED", ASCII) @pytest.mark.usefixtures("working_set") def test_list_with_pattern(project, pdm): pdm(["add", "requests"], obj=project, strict=True) result = pdm(["list", "--sort", "name", "c*"], obj=project) expected = ( "+---------------------------------+\n" "| name | version | location |\n" "|---------+------------+----------|\n" "| certifi | 2018.11.17 | |\n" "| chardet | 3.0.4 | |\n" "+---------------------------------+\n" ) assert expected == result.output @pytest.fixture() def fake_working_set(working_set): """Create fake packages with license data for testing. """ class _MockPackagePath(pathlib.PurePosixPath): def read_text(self, *args, **kwargs): return self.license_text # Foo package (adapted to contain newlines in License field) # e.g. via license = { file="LICENSE" } foo = Distribution( "foo", "0.1.0", metadata={ "License": "A License\n\nextra\ntext", "Classifier": "License :: A License", }, ) foo_l = _MockPackagePath("foo-0.1.0.dist-info", "LICENSE") foo_l.license_text = "license text for foo here" foo.files = [foo_l] # Bar package. bar = Distribution("bar", "3.0.1", metadata={"License": "B License"}) bar_l = _MockPackagePath("bar-3.0.1.dist-info", "LICENSE") bar_l.license_text = "license text for bar here" bar.files = [bar_l] # Baz package. baz = Distribution("baz", "2.7", metadata={"License": "C License"}) baz_l = _MockPackagePath("bar-2.7.dist-info", "LICENSE") baz_l.license_text = "license text for baz here" baz.files = [baz_l] # missing package- License is set to UNKNOWN, text saved in COPYING unknown = Distribution( "unknown", "1.0", metadata={ "License": "UNKNOWN", "Classifier": "License :: OSI Approved :: Apache Software License", }, ) unknown_l = _MockPackagePath("unknown-1.0.dist-info", "COPYING") unknown_l.license_text = "license text for UNKNOWN here" unknown.files = [unknown_l] # missing package- License is set to UNKNOWN, text saved in LICENCE # using UK spelling classifier = Distribution("classifier", "1.0", metadata={"Classifier": "License :: PDM TEST D"}) classifier_l = _MockPackagePath("classifier-1.0.dist-info", "LICENCE") classifier_l.license_text = "license text for CLASSIFIER here" classifier_l.read_text = lambda *a, **kw: 1 / 0 # make it throw an error classifier.files = [classifier_l] # Place our fake packages in the working set. for candidate in [foo, bar, baz, unknown, classifier]: working_set.add_distribution(candidate) return working_set @pytest.fixture() def fake_metadata(mocker, repository): def prepare_metadata(self): can = self.candidate version, dependencies = repository.get_raw_dependencies(can) dist = Distribution(can.name, version, can.req.editable) dist.dependencies = dependencies return dist return mocker.patch( "pdm.models.candidates.PreparedCandidate.prepare_metadata", prepare_metadata, ) @mock.patch("pdm.termui.ROUNDED", ASCII) @pytest.mark.usefixtures("working_set") def test_list_freeze(project, pdm): pdm(["add", "requests"], obj=project, strict=True) result = pdm(["list", "--freeze"], obj=project) expected = "certifi==2018.11.17\nchardet==3.0.4\nidna==2.7\nrequests==2.19.1\ntest-project==0.0.0\nurllib3==1.22\n" assert expected == result.output @mock.patch("pdm.termui.ROUNDED", ASCII) @pytest.mark.usefixtures("working_set") def test_list_bare_sorted_version(project, pdm): pdm(["add", "requests"], obj=project, strict=True) result = pdm(["list", "--sort", "version"], obj=project) expected = ( "+--------------------------------------+\n" "| name | version | location |\n" "|--------------+------------+----------|\n" "| test-project | 0.0.0 | |\n" "| urllib3 | 1.22 | |\n" "| requests | 2.19.1 | |\n" "| idna | 2.7 | |\n" "| certifi | 2018.11.17 | |\n" "| chardet | 3.0.4 | |\n" "+--------------------------------------+\n" ) assert expected == result.output @mock.patch("pdm.termui.ROUNDED", ASCII) @pytest.mark.usefixtures("fake_metadata") def test_list_bare_sorted_version_resolve(project, pdm, working_set): project.environment.python_requires = PySpecSet(">=3.6") pdm(["add", "requests", "--no-sync"], obj=project, strict=True) result = pdm(["list", "--sort", "version", "--resolve"], obj=project, strict=True) assert "requests" not in working_set expected = ( "+----------------------------------+\n" "| name | version | location |\n" "|----------+------------+----------|\n" "| urllib3 | 1.22 | |\n" "| requests | 2.19.1 | |\n" "| idna | 2.7 | |\n" "| certifi | 2018.11.17 | |\n" "| chardet | 3.0.4 | |\n" "+----------------------------------+\n" ) assert expected == result.outputs, result.outputs @mock.patch("pdm.termui.ROUNDED", ASCII) @pytest.mark.usefixtures("fake_working_set") def test_list_bare_fields_licences(project, pdm): result = pdm(["list", "--fields", "name,version,groups,licenses"], obj=project) expected = ( "+---------------------------------------------------------+\n" "| name | version | groups | licenses |\n" "|------------+---------+--------+-------------------------|\n" "| bar | 3.0.1 | :sub | B License |\n" "| baz | 2.7 | :sub | C License |\n" "| classifier | 1.0 | :sub | PDM TEST D |\n" "| foo | 0.1.0 | :sub | A License |\n" "| unknown | 1.0 | :sub | Apache Software License |\n" "+---------------------------------------------------------+\n" ) assert expected == result.output @pytest.mark.usefixtures("fake_working_set") def test_list_csv_fields_licences(project, pdm): result = pdm(["list", "--csv", "--fields", "name,version,licenses"], obj=project) expected = ( "name,version,licenses\n" "bar,3.0.1,B License\n" "baz,2.7,C License\n" "classifier,1.0,PDM TEST D\n" "foo,0.1.0,A License\n" "unknown,1.0,Apache Software License\n" ) assert expected == result.output @pytest.mark.usefixtures("fake_working_set") def test_list_json_fields_licences(project, pdm): result = pdm(["list", "--json", "--fields", "name,version,licenses"], obj=project) expected = [ {"name": "bar", "version": "3.0.1", "licenses": "B License"}, {"name": "baz", "version": "2.7", "licenses": "C License"}, {"name": "classifier", "version": "1.0", "licenses": "PDM TEST D"}, {"name": "foo", "version": "0.1.0", "licenses": "A License"}, {"name": "unknown", "version": "1.0", "licenses": "Apache Software License"}, ] assert expected == json.loads(result.outputs) @pytest.mark.usefixtures("fake_working_set") def test_list_markdown_fields_licences(project, pdm): # Note that in "foo" the "License" metadata field ("License": "A License\n\nextra\ntext") # is ignored, in favour of the classifier and the LICENSE file. # This behaviour could be improved. result = pdm(["list", "--markdown", "--fields", "name,version,licenses"], obj=project) expected = ( "# test-project licenses\n" "## bar\n\n" "| Name | bar |\n" "|----|----|\n" "| Version | 3.0.1 |\n" "| Licenses | B License |\n\n" "bar-3.0.1.dist-info/LICENSE\n\n\n" "````\n" "license text for bar here\n" "````\n\n\n" "## baz\n\n" "| Name | baz |\n" "|----|----|\n" "| Version | 2.7 |\n" "| Licenses | C License |\n\n" "bar-2.7.dist-info/LICENSE\n\n\n" "````\n" "license text for baz here\n" "````\n\n\n" "## classifier\n\n" "| Name | classifier |\n" "|----|----|\n" "| Version | 1.0 |\n" "| Licenses | PDM TEST D |\n\n" "classifier-1.0.dist-info/LICENCE\n\n\n" "````\n" "Problem finding license text: division by zero\n" "````\n\n\n" "## foo\n\n" "| Name | foo |\n" "|----|----|\n" "| Version | 0.1.0 |\n" "| Licenses | A License |\n\n" "foo-0.1.0.dist-info/LICENSE\n\n\n" "````\n" "license text for foo here\n" "````\n\n\n" "## unknown\n\n" "| Name | unknown |\n" "|----|----|\n" "| Version | 1.0 |\n" "| Licenses | Apache Software License |\n\n" "unknown-1.0.dist-info/COPYING\n\n\n" "````\n" "license text for UNKNOWN here\n" "````\n\n\n" ) assert expected == result.output @pytest.mark.usefixtures("working_set", "repository") def test_list_csv_include_exclude_valid(project, pdm): project.environment.python_requires = PySpecSet(">=3.6") dep_path = FIXTURES.joinpath("projects/demo").as_posix() pdm(["add", "-de", f"{dep_path}[security]"], obj=project, strict=True) result = pdm( [ "list", "--csv", "--fields", "name,version,groups", "--sort", "name", "--include", "notexisting", ], obj=project, ) assert "[PdmUsageError]" in result.outputs assert "--include groups must be selected from" in result.outputs assert "dev" in result.outputs assert "default" in result.outputs assert ":sub" in result.outputs @pytest.mark.usefixtures("local_finder") def test_list_packages_in_given_venv(project, pdm): project.pyproject.metadata["requires-python"] = ">=3.7" project.pyproject.write() project.global_config["python.use_venv"] = True pdm(["venv", "create"], obj=project, strict=True) pdm(["venv", "create", "--name", "second"], obj=project, strict=True) project._saved_python = None pdm(["add", "first", "--no-self"], obj=project, strict=True) second_lockfile = str(project.root / "pdm.2.lock") pdm( ["add", "-G", "second", "--no-self", "-L", second_lockfile, "--venv", "second", "editables"], obj=project, strict=True, ) project.environment = None result1 = pdm(["list", "--freeze"], obj=project, strict=True) result2 = pdm(["list", "--freeze", "--venv", "second"], obj=project, strict=True) assert result1.output.strip() == "first==2.0.2" assert result2.output.strip() == "editables==0.2" @pytest.mark.usefixtures("working_set", "repository") def test_list_csv_include_exclude(project, pdm): project.environment.python_requires = PySpecSet(">=3.6") dep_path = FIXTURES.joinpath("projects/demo").as_posix() pdm(["add", "-de", f"{dep_path}[security]"], obj=project, strict=True) # Show all groups. result = pdm( ["list", "--csv", "--fields", "name,version,groups", "--sort", "name"], obj=project, ) expected = ( "name,version,groups\n" "certifi,2018.11.17,:sub\n" "chardet,3.0.4,:sub\n" "demo,0.0.1,dev\n" "idna,2.7,:sub\n" "requests,2.19.1,:sub\n" "urllib3,1.22,:sub\n" ) assert expected == result.output # Sub always included. result = pdm( [ "list", "--csv", "--fields", "name,groups", "--sort", "name", "--include", "dev", ], obj=project, ) expected = "name,groups\ncertifi,:sub\nchardet,:sub\ndemo,dev\nidna,:sub\nrequests,:sub\nurllib3,:sub\n" assert expected == result.output # Include all (default) except sub result = pdm( [ "list", "--csv", "--fields", "name,groups", "--sort", "name", "--exclude", ":sub", ], obj=project, ) expected = "name,groups\ndemo,dev\n" assert expected == result.output # Show just the dev group result = pdm( [ "list", "--csv", "--fields", "name,version,groups", "--sort", "name", "--include", "dev", "--exclude", "*", ], obj=project, ) expected = "name,version,groups\ndemo,0.0.1,dev\n" assert expected == result.output # Exclude the dev group. result = pdm( [ "list", "--csv", "--fields", "name,version,groups", "--sort", "name", "--exclude", "dev", ], obj=project, ) expected = "name,version,groups\n" assert expected == result.output pdm-2.23.1/tests/cli/test_lock.py000066400000000000000000000424361477560627500166650ustar00rootroot00000000000000from pathlib import Path from unittest.mock import ANY import pytest from unearth import Link from pdm.cli import actions from pdm.exceptions import PdmUsageError from pdm.models.requirements import parse_requirement from pdm.models.specifiers import PySpecSet from pdm.project.lockfile import FLAG_CROSS_PLATFORM, Compatibility from pdm.utils import parse_version from tests import FIXTURES def test_lock_command(project, pdm, mocker): m = mocker.patch.object(actions, "do_lock") pdm(["lock"], obj=project) m.assert_called_with( project, refresh=False, groups=["default"], hooks=ANY, strategy_change=None, strategy="all", append=False, env_spec=None, ) @pytest.mark.usefixtures("repository") def test_lock_dependencies(project): project.add_dependencies(["requests"]) actions.do_lock(project) assert project.lockfile.exists locked = project.get_locked_repository().candidates for package in ("requests", "idna", "chardet", "certifi"): assert package in locked @pytest.mark.parametrize("args", [("-S", "static_urls"), ("--static-urls",)]) def test_lock_refresh(pdm, project, repository, args, core, mocker): project.add_dependencies(["requests"]) result = pdm(["lock"], obj=project) assert result.exit_code == 0 assert project.is_lockfile_hash_match() package = next(p for p in project.lockfile["package"] if p["name"] == "requests") assert not package.get("files") project.add_dependencies(["requests>=2.0"]) url_hashes = { "http://example.com/requests-2.19.1-py3-none-any.whl": "sha256:abcdef123456", "http://example2.com/requests-2.19.1-py3-none-AMD64.whl": "sha256:abcdef123456", "http://example1.com/requests-2.19.1-py3-none-any.whl": "sha256:abcdef123456", } mocker.patch.object( core.repository_class, "get_hashes", side_effect=( lambda c: [{"url": url, "file": Link(url).filename, "hash": hash} for url, hash in url_hashes.items()] if c.identify() == "requests" else [] ), ) assert not project.is_lockfile_hash_match() result = pdm(["lock", "--refresh", "-v"], obj=project) assert result.exit_code == 0 package = next(p for p in project.lockfile["package"] if p["name"] == "requests") assert package["files"] == [ {"file": "requests-2.19.1-py3-none-AMD64.whl", "hash": "sha256:abcdef123456"}, {"file": "requests-2.19.1-py3-none-any.whl", "hash": "sha256:abcdef123456"}, ] assert project.is_lockfile_hash_match() result = pdm(["lock", "--refresh", *args, "-v"], obj=project) assert result.exit_code == 0 package = next(p for p in project.lockfile["package"] if p["name"] == "requests") assert package["files"] == [{"url": url, "hash": hash} for url, hash in sorted(url_hashes.items())] def test_lock_refresh_keep_consistent(pdm, project, repository): project.add_dependencies(["requests"]) result = pdm(["lock"], obj=project) assert result.exit_code == 0 assert project.is_lockfile_hash_match() previous = project.lockfile._path.read_text() result = pdm(["lock", "--refresh"], obj=project) assert result.exit_code == 0 assert project.lockfile._path.read_text() == previous def test_lock_check_no_change_success(pdm, project, repository): project.add_dependencies(["requests"]) result = pdm(["lock"], obj=project) assert result.exit_code == 0 assert project.is_lockfile_hash_match() result = pdm(["lock", "--check"], obj=project) assert result.exit_code == 0 def test_lock_check_change_fails(pdm, project, repository): project.add_dependencies(["requests"]) result = pdm(["lock"], obj=project) assert result.exit_code == 0 assert project.is_lockfile_hash_match() project.add_dependencies(["pyyaml"]) result = pdm(["lock", "--check"], obj=project) assert result.exit_code == 1 @pytest.mark.usefixtures("repository") def test_innovations_with_specified_lockfile(pdm, project, working_set): project.add_dependencies(["requests"]) lockfile = str(project.root / "mylock.lock") pdm(["lock", "--lockfile", lockfile], strict=True, obj=project) assert project.lockfile._path == project.root / "mylock.lock" assert project.is_lockfile_hash_match() locked = project.get_locked_repository().candidates assert "requests" in locked pdm(["sync", "--lockfile", lockfile], strict=True, obj=project) assert "requests" in working_set @pytest.mark.usefixtures("repository", "vcs") def test_skip_editable_dependencies_in_metadata(project, capsys): project.pyproject.metadata["dependencies"] = [ "-e git+https://github.com/test-root/demo.git@1234567890abcdef#egg=demo" ] actions.do_lock(project) _, err = capsys.readouterr() assert "WARNING: Skipping editable dependency" in err assert not project.get_locked_repository().candidates @pytest.mark.usefixtures("repository") def test_lock_selected_groups(project, pdm): project.add_dependencies(["requests"], to_group="http") project.add_dependencies(["pytz"]) pdm(["lock", "-G", "http", "--no-default"], obj=project, strict=True) assert project.lockfile.groups == ["http"] assert "requests" in project.get_locked_repository().candidates assert "pytz" not in project.get_locked_repository().candidates @pytest.mark.usefixtures("repository") @pytest.mark.parametrize("to_dev", [True, False]) def test_lock_self_referencing_dev_groups(project, pdm, to_dev): name = project.name project.add_dependencies(["requests"], to_group="http", dev=to_dev) project.add_dependencies( {"pytz": parse_requirement("pytz"), f"{name}[http]": parse_requirement(f"{name}[http]")}, to_group="dev", dev=True, ) pdm(["lock", "-G", "dev"], obj=project, strict=True) assert project.lockfile.groups == ["default", "dev", "http"] packages = project.lockfile["package"] pytz = next(p for p in packages if p["name"] == "pytz") assert pytz["groups"] == ["dev"] requests = next(p for p in packages if p["name"] == "requests") assert requests["groups"] == ["dev", "http"] idna = next(p for p in packages if p["name"] == "idna") assert idna["groups"] == ["dev", "http"] @pytest.mark.usefixtures("repository") def test_lock_self_referencing_optional_groups(project, pdm): name = project.name project.add_dependencies(["requests"], to_group="http") project.add_dependencies( {"pytz": parse_requirement("pytz"), f"{name}[http]": parse_requirement(f"{name}[http]")}, to_group="all", ) pdm(["lock", "-G", "all"], obj=project, strict=True) assert project.lockfile.groups == ["default", "all", "http"] packages = project.lockfile["package"] pytz = next(p for p in packages if p["name"] == "pytz") assert pytz["groups"] == ["all"] requests = next(p for p in packages if p["name"] == "requests") assert requests["groups"] == ["all", "http"] idna = next(p for p in packages if p["name"] == "idna") assert idna["groups"] == ["all", "http"] @pytest.mark.usefixtures("repository") def test_lock_include_groups_not_allowed(project, pdm): project.pyproject.metadata["optional-dependencies"] = {"http": ["requests"]} project.pyproject.dependency_groups.update({"dev": ["pytest", {"include-group": "http"}]}) project.pyproject.write() result = pdm(["lock", "-G", "all"], obj=project) assert result.exit_code != 0 assert "Missing group 'http' in `include-group`" in result.stderr @pytest.mark.usefixtures("repository") def test_lock_optional_referencing_dev_group_not_allowed(project, pdm): name = project.name project.pyproject.metadata["optional-dependencies"] = {"http": ["requests", f"{name}[dev]"]} project.pyproject.dependency_groups.update({"dev": ["pytest"]}) project.pyproject.write() result = pdm(["lock", "-G", "http"], obj=project) assert result.exit_code != 0 assert "Optional dependency group 'http' cannot include non-existing extras" in result.stderr @pytest.mark.usefixtures("local_finder") def test_lock_multiple_platform_wheels(project, pdm): project.environment.python_requires = PySpecSet(">=3.7") project.add_dependencies(["pdm-hello"]) pdm(["lock"], obj=project, strict=True) package = next(p for p in project.lockfile["package"] if p["name"] == "pdm-hello") file_hashes = package["files"] assert len(file_hashes) == 2 @pytest.mark.usefixtures("local_finder") @pytest.mark.parametrize("platform", ["linux", "macos", "windows"]) def test_lock_specific_platform_wheels(project, pdm, platform): project.environment.python_requires = PySpecSet(">=3.7") project.add_dependencies(["pdm-hello"]) pdm(["lock", "--platform", platform], obj=project, strict=True) assert FLAG_CROSS_PLATFORM not in project.lockfile.strategy package = next(p for p in project.lockfile["package"] if p["name"] == "pdm-hello") file_hashes = package["files"] wheels_num = 2 if platform == "windows" else 1 assert len(file_hashes) == wheels_num def test_parse_lock_strategy_group_options(core): core.init_parser() parser = core.parser ns = parser.parse_args(["lock", "-S", "no_cross_platform"]) assert ns.strategy_change == ["no_cross_platform"] ns = parser.parse_args(["lock", "-S", "no_cross_platform", "--static-urls"]) assert ns.strategy_change == ["no_cross_platform", "static_urls"] ns = parser.parse_args(["lock", "-S", "no_cross_platform,direct_minimal_versions"]) assert ns.strategy_change == ["no_cross_platform", "direct_minimal_versions"] def test_apply_lock_strategy_changes(project): assert project.lockfile.apply_strategy_change(["no_cross_platform", "static_urls"]) == { "inherit_metadata", "static_urls", } assert project.lockfile.apply_strategy_change(["no_static_urls"]) == {"inherit_metadata"} assert project.lockfile.apply_strategy_change(["no_inherit_metadata"]) == set() @pytest.mark.parametrize("strategy", [["abc"], ["no_abc", "static_urls"]]) def test_apply_lock_strategy_changes_invalid(project, strategy): with pytest.raises(PdmUsageError): project.lockfile.apply_strategy_change(strategy) def test_lock_direct_minimal_versions(project, repository, pdm): project.add_dependencies(["django"]) repository.add_candidate("pytz", "2019.6") pdm(["lock", "-S", "direct_minimal_versions"], obj=project, strict=True) assert project.lockfile.strategy == {"direct_minimal_versions", "inherit_metadata"} locked_repository = project.get_locked_repository() assert locked_repository.candidates["django"].version == "1.11.8" assert locked_repository.candidates["pytz"].version == "2019.6" @pytest.mark.usefixtures("local_finder") @pytest.mark.parametrize("args", [(), ("-S", "direct_minimal_versions")]) def test_lock_direct_minimal_versions_real(project, pdm, args): project.add_dependencies(["zipp"]) pdm(["lock", *args], obj=project, strict=True) locked_candidate = project.get_locked_repository().candidates["zipp"] if args: assert locked_candidate.version == "3.6.0" else: assert locked_candidate.version == "3.7.0" @pytest.mark.parametrize( "lock_version,expected", [ ("4.1.0", Compatibility.BACKWARD), ("4.1.1", Compatibility.SAME), ("4.1.2", Compatibility.FORWARD), ("4.2", Compatibility.NONE), ("3.0", Compatibility.NONE), ("4.0.1", Compatibility.BACKWARD), ], ) def test_lockfile_compatibility(project, monkeypatch, lock_version, expected, pdm): pdm(["lock"], obj=project, strict=True) monkeypatch.setattr("pdm.project.lockfile.Lockfile.spec_version", parse_version("4.1.1")) project.lockfile._data["metadata"]["lock_version"] = lock_version assert project.lockfile.compatibility() == expected result = pdm(["lock", "--check"], obj=project) assert result.exit_code == (1 if expected == Compatibility.NONE else 0) def test_lock_default_inherit_metadata(project, pdm, mocker, working_set): project.add_dependencies(["requests"]) pdm(["lock"], obj=project, strict=True) assert "inherit_metadata" in project.lockfile.strategy packages = project.lockfile["package"] assert all(package["groups"] == ["default"] for package in packages) resolver = mocker.patch.object(project, "get_resolver") pdm(["sync"], obj=project, strict=True) resolver.assert_not_called() for key in ("requests", "idna", "chardet", "urllib3"): assert key in working_set def test_lock_inherit_metadata_strategy(project, pdm, mocker, working_set): project.add_dependencies(["requests"]) pdm(["lock", "-S", "inherit_metadata"], obj=project, strict=True) assert "inherit_metadata" in project.lockfile.strategy packages = project.lockfile["package"] assert all(package["groups"] == ["default"] for package in packages) resolver = mocker.patch.object(project, "get_resolver") pdm(["sync"], obj=project, strict=True) resolver.assert_not_called() for key in ("requests", "idna", "chardet", "urllib3"): assert key in working_set def test_lock_exclude_newer(project, pdm): project.pyproject.metadata["requires-python"] = ">=3.9" project.project_config["pypi.url"] = "https://my.pypi.org/json" project.add_dependencies(["zipp"]) pdm(["lock", "--exclude-newer", "2024-01-01"], obj=project, strict=True, cleanup=False) assert project.get_locked_repository().candidates["zipp"].version == "3.6.0" pdm(["lock"], obj=project, strict=True, cleanup=False) assert project.get_locked_repository().candidates["zipp"].version == "3.7.0" exclusion_cases = [ pytest.param(("-G", ":all", "--without", "tz,ssl"), id="-G :all --without tz,ssl"), pytest.param(("-G", ":all", "--without", "tz", "--without", "ssl"), id="-G :all --without tz --without ssl"), pytest.param(("--with", ":all", "--without", "tz,ssl"), id="--with all --without tz,ssl"), pytest.param(("--with", ":all", "--without", "tz", "--without", "ssl"), id="--with all --without tz --without ssl"), pytest.param(("--without", "tz", "--without", "ssl"), id="--without tz --without ssl"), pytest.param(("--without", "tz,ssl"), id="--without tz,ssl"), ] @pytest.mark.parametrize("args", exclusion_cases) @pytest.mark.usefixtures("repository") def test_lock_all_with_excluded_groups(project, pdm, args): project.add_dependencies(["urllib3"], "url") project.add_dependencies(["pytz"], "tz", True) project.add_dependencies(["pyopenssl"], "ssl") pdm(["lock", *args], obj=project, strict=True) assert "urllib3" in project.get_locked_repository().candidates assert "pytz" not in project.get_locked_repository().candidates assert "pyopenssl" not in project.get_locked_repository().candidates @pytest.mark.parametrize( "args", [ ("--append",), ("--python", "<3.6"), ("-S", "cross_platform", "--append", "--python", "3.10"), ("--platform", "linux", "--refresh"), ], ) def test_forbidden_lock_target_options(project, pdm, args): result = pdm(["lock", *args], obj=project) assert result.exit_code != 0 assert "PdmUsageError" in result.stderr @pytest.mark.parametrize("nested", [False, True]) def test_lock_for_multiple_targets(project, pdm, repository, nested): deps = [ 'django<2; sys_platform == "win32"', 'django>=2; sys_platform != "win32"', ] if nested: repository.add_candidate("foo", "0.1.0") repository.add_dependencies("foo", "0.1.0", deps) project.add_dependencies(["foo"]) else: project.add_dependencies(deps) pdm(["lock", "--platform", "windows"], obj=project, strict=True) locked = project.get_locked_repository() candidates = locked.all_candidates assert len(candidates["django"]) == 1 assert candidates["django"][0].version == "1.11.8" assert len(locked.targets) == 1 pytz = candidates["pytz"][0] assert str(pytz.req.marker) == 'sys_platform == "win32"' result = pdm(["lock", "--platform", "windows", "--append"], obj=project, strict=True) assert "already exists, skip locking." in result.stdout pdm(["lock", "--platform", "linux", "--append"], obj=project, strict=True) locked = project.get_locked_repository() candidates = locked.all_candidates assert len(locked.targets) == 2 assert sorted(c.version for c in candidates["django"]) == ["1.11.8", "2.2.9"] pytz = candidates["pytz"][0] assert not pytz.req.marker or pytz.req.marker.is_any() # not append but overwrite pdm(["lock", "--platform", "windows"], obj=project, strict=True) locked = project.get_locked_repository() candidates = locked.all_candidates assert len(candidates["django"]) == 1 assert candidates["django"][0].version == "1.11.8" assert len(locked.targets) == 1 pytz = candidates["pytz"][0] assert str(pytz.req.marker) == 'sys_platform == "win32"' CONSTRAINT_FILE = str(FIXTURES / "constraints.txt") @pytest.mark.usefixtures("repository") @pytest.mark.parametrize("constraint", [CONSTRAINT_FILE, Path(CONSTRAINT_FILE).as_uri()]) def test_lock_with_override_file(project, pdm, constraint): project.add_dependencies(["requests"]) pdm(["lock", "--override", constraint], obj=project, strict=True) candidates = project.get_locked_repository().candidates assert candidates["requests"].version == "2.20.0b1" assert candidates["urllib3"].version == "1.23b0" assert "django" not in candidates pdm-2.23.1/tests/cli/test_others.py000066400000000000000000000225271477560627500172400ustar00rootroot00000000000000import json from pathlib import Path import pytest from pdm.cli import actions from pdm.utils import cd from tests import FIXTURES def test_build_distributions(project): from pdm.cli.commands.build import Command Command.do_build(project) dist = project.root / "dist" assert dist.exists() wheel = next(dist.glob("*.whl")) assert wheel.name.startswith("test_project-") tarball = next(dist.glob("*.tar.gz")) assert tarball.name.startswith("test_project-") def test_project_no_init_error(project_no_init, pdm): for command in ("add", "lock", "update"): result = pdm([command], obj=project_no_init) assert result.exit_code != 0 assert "The pyproject.toml has not been initialized yet" in result.stderr def test_help_option(pdm): result = pdm(["--help"]) assert "Usage: pdm [-h]" in result.output def test_pep582_option(pdm): result = pdm(["--pep582", "bash"]) assert result.exit_code == 0 def test_info_command(project, pdm): result = pdm(["info"], obj=project) assert "Project Root:" in result.output assert project.root.as_posix() in result.output result = pdm(["info", "--python"], obj=project) assert result.output.strip() == str(project.python.executable) result = pdm(["info", "--where"], obj=project) assert result.output.strip() == str(project.root) result = pdm(["info", "--env"], obj=project) assert result.exit_code == 0 def test_info_command_json(project, pdm): result = pdm(["info", "--json"], obj=project, strict=True) data = json.loads(result.outputs) assert data["pdm"]["version"] == project.core.version assert data["python"]["version"] == project.environment.interpreter.identifier assert data["python"]["interpreter"] == str(project.environment.interpreter.executable) assert isinstance(data["python"]["markers"], dict) assert data["project"]["root"] == str(project.root) assert isinstance(data["project"]["pypackages"], str) def test_info_global_project(pdm, tmp_path): with cd(tmp_path): result = pdm(["info", "-g", "--where"]) assert "global-project" in result.output.strip() def test_info_with_multiple_venvs(pdm, project): project.global_config["python.use_venv"] = True pdm(["venv", "create"], obj=project, strict=True) pdm(["venv", "create", "--name", "test"], obj=project, strict=True) project._saved_python = None result = pdm(["info", "--python"], obj=project, strict=True) assert Path(result.output.strip()).parent.parent == project.root / ".venv" venv_location = project.config["venv.location"] result = pdm(["info", "--python", "--venv", "test"], obj=project, strict=True) assert Path(result.output.strip()).parent.parent.parent == project.root / venv_location result = pdm(["info", "--python", "--venv", "test"], obj=project, strict=True, env={"PDM_IN_VENV": "test"}) assert Path(result.output.strip()).parent.parent.parent == project.root / venv_location result = pdm(["info", "--python", "--venv", "default"], obj=project) assert "No virtualenv with key 'default' is found" in result.stderr def test_global_project_other_location(pdm, project): result = pdm(["info", "-g", "-p", project.root.as_posix(), "--where"]) assert result.stdout.strip() == str(project.root) def test_uncaught_error(pdm, mocker): mocker.patch.object(actions, "do_lock", side_effect=RuntimeError("test error")) result = pdm(["lock"]) assert "[RuntimeError]: test error" in result.stderr result = pdm(["lock", "-v"]) assert isinstance(result.exception, RuntimeError) @pytest.mark.parametrize( "filename", [ "requirements.txt", "Pipfile", "pyproject.toml", "projects/flit-demo/pyproject.toml", ], ) def test_import_other_format_file(project, pdm, filename): requirements_file = FIXTURES / filename result = pdm(["import", str(requirements_file)], obj=project) assert result.exit_code == 0 def test_import_requirement_no_overwrite(project, pdm, tmp_path): project.add_dependencies(["requests"]) tmp_path.joinpath("reqs.txt").write_text("flask\nflask-login\n") result = pdm(["import", "-dGweb", str(tmp_path.joinpath("reqs.txt"))], obj=project) assert result.exit_code == 0, result.stderr assert [r.key for r in project.get_dependencies()] == ["requests"] assert [r.key for r in project.get_dependencies("web")] == ["flask", "flask-login"] @pytest.mark.network @pytest.mark.xfail(reason="HTTP search risk controlled") def test_search_package(pdm, tmp_path): with cd(tmp_path): result = pdm(["search", "requests"], strict=True) assert len(result.output.splitlines()) > 0 assert not tmp_path.joinpath("__pypackages__").exists() assert not tmp_path.joinpath(".pdm-python").exists() @pytest.mark.network def test_show_package_on_pypi(pdm): result = pdm(["show", "ipython"]) assert result.exit_code == 0 assert "ipython" in result.output.splitlines()[0] result = pdm(["show", "requests"]) assert result.exit_code == 0 assert "requests" in result.output.splitlines()[0] result = pdm(["show", "--name", "requests"]) assert result.exit_code == 0 assert "requests" in result.output.splitlines()[0] result = pdm(["show", "--name", "sphinx-data-viewer"]) assert result.exit_code == 0 assert "sphinx-data-viewer" in result.output.splitlines()[0] def test_show_self_package(project, pdm): result = pdm(["show"], obj=project) assert result.exit_code == 0, result.stderr result = pdm(["show", "--name", "--version"], obj=project) assert result.exit_code == 0 assert "test-project\n0.0.0\n" == result.output def test_export_to_requirements_txt(pdm, fixture_project): project = fixture_project("demo-package") requirements_txt = project.root / "requirements.txt" requirements_no_hashes = project.root / "requirements_simple.txt" requirements_pyproject = project.root / "requirements.ini" result = pdm(["export"], obj=project) assert result.exit_code == 0 assert result.output.strip() == requirements_txt.read_text().strip() result = pdm(["export", "--self"], obj=project) assert result.exit_code == 1 result = pdm(["export", "--editable-self"], obj=project) assert result.exit_code == 1 result = pdm(["export", "--no-hashes", "--self"], obj=project) assert result.exit_code == 0 assert ". # this package\n" in result.output.strip() result = pdm(["export", "--no-hashes", "--editable-self"], obj=project) assert result.exit_code == 0 assert "-e . # this package\n" in result.output.strip() result = pdm(["export", "--no-hashes"], obj=project) assert result.exit_code == 0 assert result.output.strip() == requirements_no_hashes.read_text().strip() result = pdm(["export", "--pyproject"], obj=project) assert result.exit_code == 0 assert result.output.strip() == requirements_pyproject.read_text().strip() result = pdm(["export", "-o", str(project.root / "requirements_output.txt")], obj=project) assert result.exit_code == 0 assert (project.root / "requirements_output.txt").read_text() == requirements_txt.read_text() @pytest.mark.parametrize("extra_opt", [[], ["--no-extras"]]) def test_export_doesnt_include_dep_with_extras(pdm, fixture_project, extra_opt): project = fixture_project("demo-package-has-dep-with-extras") result = pdm(["export", "--without-hashes", *extra_opt], obj=project) assert result.exit_code == 0 if extra_opt: assert "requests==2.26.0" in result.output.splitlines() else: assert "requests[security]==2.26.0" in result.output.splitlines() def test_completion_command(pdm): result = pdm(["completion", "bash"]) assert result.exit_code == 0 assert "(completion)" in result.output @pytest.mark.network def test_show_update_hint(pdm, project, monkeypatch): monkeypatch.delenv("PDM_CHECK_UPDATE", raising=False) prev_version = project.core.version try: project.core.version = "0.0.0" r = pdm(["config"], obj=project) finally: project.core.version = prev_version assert "to upgrade." in r.stderr assert "Run `pdm config check_update false` to disable the check." in r.stderr @pytest.mark.usefixtures("repository") def test_export_with_platform_markers(pdm, project): pdm( ["add", "--no-sync", 'urllib3; sys_platform == "fake"', 'idna; python_version >= "3.7"'], obj=project, strict=True, ) result = pdm(["export", "--no-hashes"], obj=project, strict=True) result_lines = result.output.splitlines() assert 'urllib3==1.22; sys_platform == "fake"' in result_lines assert 'idna==2.7; python_version >= "3.7"' in result_lines result = pdm(["export", "--no-hashes", "--no-markers"], obj=project, strict=True) result_lines = result.output.splitlines() assert not any(line.startswith("urllib3") for line in result_lines) assert "idna==2.7" in result_lines @pytest.mark.usefixtures("repository", "vcs") def test_export_with_vcs_deps(pdm, project): pdm(["add", "--no-sync", "git+https://github.com/test-root/demo.git"], obj=project, strict=True) result = pdm(["export"], obj=project) assert result.exit_code != 0 result = pdm(["export", "--no-hashes"], obj=project) assert result.exit_code == 0 assert "demo @ git+https://github.com/test-root/demo.git@1234567890abcdef" in result.output.splitlines() pdm-2.23.1/tests/cli/test_outdated.py000066400000000000000000000025701477560627500175410ustar00rootroot00000000000000import json from unittest import mock import pytest from rich.box import ASCII @mock.patch("pdm.termui.ROUNDED", ASCII) @pytest.mark.usefixtures("working_set") def test_outdated(project, pdm, index): pdm(["add", "requests"], obj=project, strict=True, cleanup=False) project.project_config["pypi.url"] = "https://my.pypi.org/simple" del project.pyproject.settings["source"] project.pyproject.write() index["/simple/requests/"] = b"""\

requests

requests-2.20.0-py3-none-any.whl """ result = pdm(["outdated"], obj=project, strict=True, cleanup=False) assert "| requests | default | 2.19.1 | 2.19.1 | 2.20.0 |" in result.stdout result = pdm(["outdated", "re*"], obj=project, strict=True, cleanup=False) assert "| requests | default | 2.19.1 | 2.19.1 | 2.20.0 |" in result.stdout result = pdm(["outdated", "--json"], obj=project, strict=True, cleanup=False) json_output = json.loads(result.stdout) assert json_output == [ { "package": "requests", "groups": ["default"], "installed_version": "2.19.1", "pinned_version": "2.19.1", "latest_version": "2.20.0", } ] pdm-2.23.1/tests/cli/test_publish.py000066400000000000000000000240261477560627500173760ustar00rootroot00000000000000import base64 import os from argparse import Namespace import pytest from pdm._types import RepositoryConfig from pdm.cli.commands.publish import Command as PublishCommand from pdm.cli.commands.publish.package import PackageFile from pdm.cli.commands.publish.repository import Repository from pdm.exceptions import PdmUsageError from tests import FIXTURES pytestmark = pytest.mark.usefixtures("mock_run_gpg") @pytest.mark.parametrize( "filename", ["demo-0.0.1-py2.py3-none-any.whl", "demo-0.0.1.tar.gz", "demo-0.0.1.zip"], ) def test_package_parse_metadata(filename): fullpath = FIXTURES / "artifacts" / filename package = PackageFile.from_filename(str(fullpath), None) assert package.base_filename == filename meta = package.metadata_dict assert meta["name"] == "demo" assert meta["version"] == "0.0.1" assert all(f"{hash_name}_digest" in meta for hash_name in ["md5", "sha256", "blake2_256"]) if filename.endswith(".whl"): assert meta["pyversion"] == "py2.py3" assert meta["filetype"] == "bdist_wheel" else: assert meta["pyversion"] == "source" assert meta["filetype"] == "sdist" def test_parse_metadata_with_non_ascii_chars(): fullpath = FIXTURES / "artifacts" / "caj2pdf-restructured-0.1.0a6.tar.gz" package = PackageFile.from_filename(str(fullpath), None) meta = package.metadata_dict assert meta["summary"] == "caj2pdf 重新组织,方便打包与安装" # noqa: RUF001 assert meta["author_email"] == "张三 " assert meta["description"].strip() == "# caj2pdf\n\n测试中文项目" def test_package_add_signature(tmp_path): package = PackageFile.from_filename(str(FIXTURES / "artifacts/demo-0.0.1-py2.py3-none-any.whl"), None) tmp_path.joinpath("signature.asc").write_bytes(b"test gpg signature") package.add_gpg_signature(str(tmp_path / "signature.asc"), "signature.asc") assert package.gpg_signature == ("signature.asc", b"test gpg signature") def test_package_call_gpg_sign(): package = PackageFile.from_filename(str(FIXTURES / "artifacts/demo-0.0.1-py2.py3-none-any.whl"), None) try: package.sign(None) finally: try: os.unlink(package.filename + ".asc") except OSError: pass assert package.gpg_signature == (package.base_filename + ".asc", b"fake signature") def test_repository_get_release_urls(project): package_files = [ PackageFile.from_filename(str(FIXTURES / "artifacts" / fn), None) for fn in [ "demo-0.0.1-py2.py3-none-any.whl", "demo-0.0.1.tar.gz", "demo-0.0.1.zip", ] ] config = RepositoryConfig( config_prefix="repository", name="test", url="https://upload.pypi.org/legacy/", username="abc", password="123", ) repository = Repository(project, config) assert repository.get_release_urls(package_files) == {"https://pypi.org/project/demo/0.0.1/"} repository.url = "https://example.pypi.org/legacy/" assert not repository.get_release_urls(package_files) @pytest.mark.usefixtures("prepare_packages") def test_publish_pick_up_asc_files(project, uploaded, pdm): for p in list(project.root.joinpath("dist").iterdir()): with open(str(p) + ".asc", "w") as f: f.write("fake signature") pdm( ["publish", "--no-build", "--username=abc", "--password=123"], obj=project, strict=True, ) # Test wheels are uploaded first assert uploaded[0].base_filename.endswith(".whl") for package in uploaded: assert package.gpg_signature == ( package.base_filename + ".asc", b"fake signature", ) @pytest.mark.usefixtures("prepare_packages") def test_publish_package_with_signature(project, uploaded, pdm): pdm( ["publish", "--no-build", "-S", "--username=abc", "--password=123"], obj=project, strict=True, ) for package in uploaded: assert package.gpg_signature == ( package.base_filename + ".asc", b"fake signature", ) @pytest.mark.usefixtures("local_finder") def test_publish_and_build_in_one_run(fixture_project, pdm, mock_pypi): project = fixture_project("demo-module") result = pdm(["publish", "--username=abc", "--password=123"], obj=project, strict=True).output mock_pypi.assert_called() assert "Uploading demo_module-0.1.0-py3-none-any.whl" in result assert "Uploading demo_module-0.1.0.tar.gz" in result, result assert "https://pypi.org/project/demo-module/0.1.0/" in result def test_publish_cli_args_and_env_var_precedence(project, monkeypatch, mocker): repository = mocker.patch.object(Repository, "__init__", return_value=None) PublishCommand.get_repository( project, Namespace( repository=None, username="foo", password="bar", ca_certs="custom.pem", verify_ssl=True, ), ) repository.assert_called_with( project, RepositoryConfig( config_prefix="repository", name="pypi", url="https://upload.pypi.org/legacy/", username="foo", password="bar", ca_certs="custom.pem", verify_ssl=None, ), ) with monkeypatch.context() as m: m.setenv("PDM_PUBLISH_USERNAME", "bar") m.setenv("PDM_PUBLISH_PASSWORD", "secret") m.setenv("PDM_PUBLISH_REPO", "testpypi") m.setenv("PDM_PUBLISH_CA_CERTS", "override.pem") PublishCommand.get_repository( project, Namespace( repository=None, username=None, password=None, ca_certs=None, verify_ssl=True, ), ) repository.assert_called_with( project, RepositoryConfig( config_prefix="repository", name="testpypi", url="https://test.pypi.org/legacy/", username="bar", password="secret", ca_certs="override.pem", verify_ssl=None, ), ) PublishCommand.get_repository( project, Namespace( repository="pypi", username="foo", password=None, ca_certs="custom.pem", verify_ssl=True, ), ) repository.assert_called_with( project, RepositoryConfig( config_prefix="repository", name="pypi", url="https://upload.pypi.org/legacy/", username="foo", password="secret", ca_certs="custom.pem", verify_ssl=None, ), ) def test_repository_get_credentials_from_keyring(project, keyring, mocker): keyring.save_auth_info("https://test.org/upload", "foo", "barbaz") config = RepositoryConfig(config_prefix="repository", name="test", url="https://test.org/upload") basic_auth = mocker.patch("httpx.BasicAuth.__init__", return_value=None) Repository(project, config) basic_auth.assert_called_with(username="foo", password="barbaz") def test_repository_get_token_from_oidc(project, mocker, httpx_mock): minted_token = "minted_oidc_token" test_pypi_url = "https://test.org/upload" httpx_mock.add_response( url="https://test.org/_/oidc/audience", method="GET", json={"audience": "testpypi"}, ) httpx_mock.add_response( url="https://test.org/_/oidc/mint-token", method="POST", json={"token": minted_token}, ) config = RepositoryConfig(config_prefix="repository", name="test", url=test_pypi_url) detect_credential_mock = mocker.patch( "pdm.cli.commands.publish.repository.detect_credential", return_value="A_OIDC_TOKEN", ) repository = Repository(project, config=config) detect_credential_mock.assert_called_once() assert base64.b64decode(repository.session.auth._auth_header[6:]).decode("utf-8") == f"__token__:{minted_token}" def test_repository_get_token_from_oidc_request_error(project, mocker, httpx_mock, capsys): test_pypi_url = "https://test.org/upload" httpx_mock.add_response( url="https://test.org/_/oidc/audience", method="GET", status_code=502, ) config = RepositoryConfig(config_prefix="repository", name="test", url=test_pypi_url) with pytest.raises(PdmUsageError): Repository(project, config=config) captured = capsys.readouterr() assert "Failed to get PyPI token via OIDC" in captured.err def test_repository_get_token_from_oidc_unsupported_platform(project, mocker, httpx_mock, capsys): test_pypi_url = "https://test.org/upload" httpx_mock.add_response( url="https://test.org/_/oidc/audience", method="GET", json={"audience": "testpypi"}, ) config = RepositoryConfig(config_prefix="repository", name="test", url=test_pypi_url) detect_credential_mock = mocker.patch( "pdm.cli.commands.publish.repository.detect_credential", return_value=None, ) with pytest.raises(PdmUsageError): Repository(project, config=config) captured = capsys.readouterr() assert "This platform is not supported for trusted publishing via OIDC" in captured.err detect_credential_mock.assert_called_once() def test_repository_get_token_misconfigured_github(project, monkeypatch, capsys, httpx_mock): test_pypi_url = "https://test.org/upload" httpx_mock.add_response( url="https://test.org/_/oidc/audience", method="GET", json={"audience": "testpypi"}, ) config = RepositoryConfig(config_prefix="repository", name="test", url=test_pypi_url) # set env variable to get `id`` into detect_github function monkeypatch.setenv("GITHUB_ACTIONS", "true") with pytest.raises(PdmUsageError): Repository(project, config=config) captured = capsys.readouterr() assert "Unable to detect OIDC token for CI platform:" in captured.err pdm-2.23.1/tests/cli/test_python.py000066400000000000000000000174571477560627500172630ustar00rootroot00000000000000import platform import sys from pathlib import Path import pytest from pbs_installer import PythonVersion from pdm.models.python import PythonInfo from pdm.utils import parse_version @pytest.fixture def mock_install(mocker): if (arch := platform.machine().lower()) not in ("arm64", "aarch64", "amd64", "x86_64"): pytest.skip(f"Skipped on non-standard platform: {arch}") def install_file( filename, destination, original_filename=None, build_dir=False, ) -> None: if sys.platform == "win32": Path(destination, "python.exe").touch() else: Path(destination, "bin").mkdir(parents=True, exist_ok=True) Path(destination, "bin", "python3").touch() def get_version(self): name = self.executable.parent.name if sys.platform == "win32" else self.executable.parent.parent.name if "@" not in name: return parse_version(platform.python_version()) return parse_version(name.split("@", 1)[1]) @property def interpreter(self): return self.executable @property def implementation(self): name = self.executable.parent.name if sys.platform == "win32" else self.executable.parent.parent.name if "@" not in name: return "cpython" return name.split("@", 1)[0] mocker.patch("pbs_installer.download", return_value="python-3.10.8.tar.gz") installer = mocker.patch("pbs_installer.install_file", side_effect=install_file) mocker.patch("findpython.python.PythonVersion.implementation", implementation) mocker.patch("findpython.python.PythonVersion._get_version", get_version) mocker.patch("findpython.python.PythonVersion.interpreter", interpreter) mocker.patch("findpython.python.PythonVersion.architecture", mocker.PropertyMock(return_value="64bit")) return installer def test_install_python(project, pdm, mock_install): root = Path(project.config["python.install_root"]) pdm(["py", "install", "cpython@3.10.8", "-v"], obj=project, strict=True) mock_install.assert_called_once() assert (root / "cpython@3.10.8").exists() result = pdm(["py", "list"], obj=project, strict=True) assert result.stdout.splitlines()[0].startswith("cpython@3.10.8") result = pdm(["py", "remove", "3.11.1"], obj=project) assert result.exit_code != 0 pdm(["py", "remove", "cpython@3.10.8"], obj=project, strict=True) assert not (root / "cpython@3.10.8").exists() result = pdm(["py", "install", "--list"], obj=project, strict=True) assert len(result.stdout.splitlines()) > 0 def test_install_python_best_match(project, pdm, mock_install, mocker): root = Path(project.config["python.install_root"]) mock_best_match = mocker.patch( "pdm.project.core.Project.get_best_matching_cpython_version", return_value=PythonVersion("cpython", 3, 10, 8) ) pdm(["py", "install"], obj=project, strict=True) mock_best_match.assert_called_once() mock_install.assert_called_once() assert (root / "cpython@3.10.8").exists() def test_install_python_min_match(project, pdm, mock_install, mocker): root = Path(project.config["python.install_root"]) mock_best_match = mocker.patch( "pdm.project.core.Project.get_best_matching_cpython_version", return_value=PythonVersion("cpython", 3, 10, 7) ) pdm(["py", "install", "--min"], obj=project, strict=True) mock_best_match.assert_called_once_with(True) mock_install.assert_called_once() assert (root / "cpython@3.10.7").exists() def test_use_auto_install_missing(project, pdm, mock_install, mocker): root = Path(project.config["python.install_root"]) mock_find_interpreters = mocker.patch("pdm.project.Project.find_interpreters", return_value=[]) mock_best_match = mocker.patch("pdm.project.core.Project.get_best_matching_cpython_version") pdm(["use", "3.10.8"], obj=project, strict=True) mock_install.assert_called_once() mock_find_interpreters.assert_called_once() mock_best_match.assert_not_called() assert (root / "cpython@3.10.8").exists() def test_use_auto_install_pick_latest(project, pdm, mock_install, mocker): root = Path(project.config["python.install_root"]) mock_find_interpreters = mocker.patch("pdm.project.Project.find_interpreters", return_value=[]) mock_best_match = mocker.patch( "pdm.project.core.Project.get_best_matching_cpython_version", return_value=PythonVersion("cpython", 3, 10, 8) ) pdm(["use", "-v"], obj=project, strict=True) mock_install.assert_called_once() mock_find_interpreters.assert_called_once() mock_best_match.assert_called_once() assert len(list(root.iterdir())) == 1 def test_use_no_auto_install(project, pdm, mocker): installer = mocker.patch("pbs_installer.install_file") mock_best_match = mocker.patch("pdm.project.core.Project.get_best_matching_cpython_version") pdm(["use", "-f"], obj=project, strict=True) installer.assert_not_called() mock_best_match.assert_not_called() def test_use_auto_install_strategy_max(project, pdm, mock_install, mocker): root = Path(project.config["python.install_root"]) mock_find_interpreters = mocker.patch("pdm.project.Project.find_interpreters") mock_best_match = mocker.patch( "pdm.project.core.Project.get_best_matching_cpython_version", return_value=PythonVersion("cpython", 3, 10, 8) ) pdm(["use", "--auto-install-max"], obj=project, strict=True) mock_install.assert_called_once() mock_find_interpreters.assert_not_called() mock_best_match.assert_called_once() assert len(list(root.iterdir())) == 1 def test_use_auto_install_strategy_min(project, pdm, mock_install, mocker): root = Path(project.config["python.install_root"]) mock_find_interpreters = mocker.patch("pdm.project.Project.find_interpreters") mock_best_match = mocker.patch( "pdm.project.core.Project.get_best_matching_cpython_version", return_value=PythonVersion("cpython", 3, 10, 7) ) pdm(["use", "--auto-install-min"], obj=project, strict=True) mock_install.assert_called_once() mock_find_interpreters.assert_not_called() mock_best_match.assert_called_once_with(True) assert len(list(root.iterdir())) == 1 def test_link_python(project, pdm): root = Path(project.config["python.install_root"]) pdm(["python", "link", sys.executable], obj=project, strict=True) python_info = PythonInfo.from_path(sys.executable) link_name = f"{python_info.implementation}@{python_info.identifier}" assert (root / link_name).resolve() == Path(sys.prefix).resolve() pdm(["python", "remove", link_name], obj=project, strict=True) assert not (root / link_name).exists() pdm(["python", "link", sys.executable, "--name", "foo"], obj=project, strict=True) assert (root / "foo").resolve() == Path(sys.prefix).resolve() pdm(["python", "remove", "foo"], obj=project, strict=True) assert not (root / "foo").exists() def test_link_python_invalid_interpreter(project, pdm): result = pdm(["python", "link", "/path/to/invalid/python"], obj=project) assert result.exit_code != 0 assert "Invalid Python interpreter" in result.stderr root = Path(project.config["python.install_root"]) root.mkdir(parents=True, exist_ok=True) root.joinpath("foo").touch() result = pdm(["python", "link", sys.executable, "--name", "foo"], obj=project) assert result.exit_code != 0 assert "Link foo already exists" in result.stderr def test_find_python(project, pdm, mock_install): pdm(["py", "install", "3.10.8"], obj=project, strict=True) result = pdm(["py", "find", "3.10.8"], obj=project, strict=True) assert "3.10.8" in result.stdout result = pdm(["py", "find", "3.10.8", "--managed"], obj=project, strict=True) assert "3.10.8" in result.stdout result = pdm(["py", "find", "3.10.9"], obj=project) assert result.exit_code != 0 pdm-2.23.1/tests/cli/test_remove.py000066400000000000000000000116211477560627500172220ustar00rootroot00000000000000import pytest from pdm.cli import actions from pdm.models.specifiers import PySpecSet def test_remove_command(project, pdm, mocker): do_remove = mocker.patch("pdm.cli.commands.remove.Command.do_remove") pdm(["remove", "demo"], obj=project) do_remove.assert_called_once() @pytest.mark.usefixtures("working_set", "vcs") def test_remove_editable_packages_while_keeping_normal(project, pdm): project.environment.python_requires = PySpecSet(">=3.6") pdm(["add", "demo"], obj=project, strict=True) pdm(["add", "-d", "-e", "git+https://github.com/test-root/demo.git#egg=demo"], obj=project, strict=True) pdm(["remove", "-d", "demo"], obj=project, strict=True) default_group = project.pyproject.metadata["dependencies"] dev_group = project.pyproject.dev_dependencies.get("dev") assert not dev_group assert len(default_group) == 1 assert not project.get_locked_repository().candidates["demo"].req.editable def test_remove_package(project, working_set, dev_option, pdm): pdm(["add", *dev_option, "requests", "pytz"], obj=project, strict=True) pdm(["remove", *dev_option, "pytz"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert "pytz" not in locked_candidates assert "pytz" not in working_set def test_remove_package_no_lock(project, working_set, dev_option, pdm): pdm(["add", *dev_option, "requests", "pytz"], obj=project, strict=True) pdm(["remove", *dev_option, "--frozen-lockfile", "pytz"], obj=project, strict=True) assert "pytz" not in working_set project.lockfile.reload() locked_candidates = project.get_locked_repository().candidates assert "pytz" in locked_candidates def test_remove_package_with_dry_run(project, working_set, pdm): pdm(["add", "requests"], obj=project, strict=True) result = pdm(["remove", "requests", "--dry-run"], obj=project, strict=True) project._lockfile = None locked_candidates = project.get_locked_repository().candidates assert "urllib3" in locked_candidates assert "urllib3" in working_set assert "- urllib3 1.22" in result.output def test_remove_package_no_sync(project, working_set, pdm): pdm(["add", "requests", "pytz"], obj=project, strict=True) pdm(["remove", "pytz", "--no-sync"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert "pytz" not in locked_candidates assert "pytz" in working_set @pytest.mark.usefixtures("working_set") def test_remove_package_not_exist(project, pdm): pdm(["add", "requests", "pytz"], obj=project, strict=True) result = pdm(["remove", "django"], obj=project) assert result.exit_code == 1 def test_remove_package_exist_in_multi_groups(project, working_set, pdm): pdm(["add", "requests"], obj=project, strict=True) pdm(["add", "--dev", "urllib3"], obj=project, strict=True) pdm(["remove", "--dev", "urllib3"], obj=project, strict=True) assert "dependency-groups" not in project.pyproject._data assert "urllib3" in working_set assert "requests" in working_set @pytest.mark.usefixtures("repository") def test_remove_no_package(project, pdm): result = pdm(["remove"], obj=project) assert result.exit_code != 0 @pytest.mark.usefixtures("working_set") def test_remove_package_wont_break_toml(project_no_init, pdm): project_no_init.pyproject._path.write_text( """ [project] dependencies = [ "requests", # this is a comment ] """ ) project_no_init.pyproject.reload() pdm(["remove", "requests"], obj=project_no_init, strict=True) assert project_no_init.pyproject.metadata["dependencies"] == [] @pytest.mark.usefixtures("working_set") def test_remove_group_not_in_lockfile(project, pdm, mocker): pdm(["add", "requests"], obj=project, strict=True) project.add_dependencies(["pytz"], to_group="tz") assert project.lockfile.groups == ["default"] locker = mocker.patch.object(actions, "do_lock") pdm(["remove", "--group", "tz", "pytz"], obj=project, strict=True) assert "optional-dependencies" not in project.pyproject.metadata locker.assert_not_called() @pytest.mark.usefixtures("working_set") def test_remove_exclude_non_existing_dev_group_in_lockfile(project, pdm): pdm(["add", "requests"], obj=project, strict=True) project.add_dependencies(["pytz"], to_group="tz", dev=True) assert project.lockfile.groups == ["default"] result = pdm(["remove", "requests"], obj=project) assert result.exit_code == 0 @pytest.mark.usefixtures("working_set") def test_remove_package_with_group_include(project, pdm): project.pyproject._data["dependency-groups"] = { "web": ["requests"], "serve": [{"include-group": "web"}, "django"], } project.pyproject.write() pdm(["lock"], obj=project, strict=True) pdm(["remove", "--no-sync", "-Gserve", "django"], obj=project, strict=True) assert "django" not in project.pyproject.dependency_groups["serve"] pdm-2.23.1/tests/cli/test_run.py000066400000000000000000001174201477560627500165350ustar00rootroot00000000000000import json import os import subprocess import textwrap from pathlib import Path from tempfile import TemporaryDirectory import pytest from pdm import termui from pdm.cli import actions from pdm.cli.utils import get_pep582_path from pdm.utils import cd @pytest.fixture def _args(project): (project.root / "args.py").write_text( textwrap.dedent( """ import os import sys name = sys.argv[1] args = ", ".join(sys.argv[2:]) print(f"{name} CALLED with {args}" if args else f"{name} CALLED") """ ) ) def test_pep582_launcher_for_python_interpreter(project, local_finder, pdm): project.root.joinpath("main.py").write_text("import first;print(first.first([0, False, 1, 2]))\n") result = pdm(["add", "first"], obj=project) assert result.exit_code == 0, result.stderr env = os.environ.copy() env.update({"PYTHONPATH": get_pep582_path(project)}) output = subprocess.check_output( [str(project.python.executable), str(project.root.joinpath("main.py"))], env=env, ) assert output.decode().strip() == "1" def test_auto_isolate_site_packages(project, pdm): env = os.environ.copy() env.update({"PYTHONPATH": get_pep582_path(project)}) proc = subprocess.run( [str(project.python.executable), "-c", "import sys;print(sys.path, sep='\\n')"], env=env, capture_output=True, text=True, cwd=str(project.root), check=True, ) assert any("site-packages" in path for path in proc.stdout.splitlines()) result = pdm( ["run", "python", "-c", "import sys;print(sys.path, sep='\\n')"], obj=project, strict=True, ) assert not any("site-packages" in path for path in result.stdout.splitlines()) def test_run_with_site_packages(project, pdm): project.pyproject.settings["scripts"] = { "foo": { "cmd": ["python", "-c", "import sys;print(sys.path, sep='\\n')"], "site_packages": True, } } project.pyproject.write() result = pdm( [ "run", "--site-packages", "python", "-c", "import sys;print(sys.path, sep='\\n')", ], obj=project, ) assert result.exit_code == 0 result = pdm(["run", "foo"], obj=project) assert result.exit_code == 0 def test_run_command_not_found(pdm): result = pdm(["run", "foobar"]) assert "Command 'foobar' is not found in your PATH." in result.stderr assert result.exit_code == 1 def test_run_pass_exit_code(pdm): result = pdm(["run", "python", "-c", "1/0"]) assert result.exit_code == 1 def test_run_cmd_script(project, pdm): project.pyproject.settings["scripts"] = {"test_script": "python -V"} project.pyproject.write() result = pdm(["run", "test_script"], obj=project) assert result.exit_code == 0 def test_run_cmd_script_with_array(project, pdm): project.pyproject.settings["scripts"] = {"test_script": ["python", "-c", "import sys; sys.exit(22)"]} project.pyproject.write() result = pdm(["run", "test_script"], obj=project) assert result.exit_code == 22 def test_run_script_pass_project_root(project, pdm, capfd): project.pyproject.settings["scripts"] = { "test_script": [ "python", "-c", "import os;print(os.getenv('PDM_PROJECT_ROOT'))", ] } project.pyproject.write() capfd.readouterr() result = pdm(["run", "test_script"], obj=project) assert result.exit_code == 0 out, _ = capfd.readouterr() assert Path(out.strip()) == project.root def test_run_shell_script(project, pdm): project.pyproject.settings["scripts"] = { "test_script": { "shell": "echo hello > output.txt", "help": "test it won't fail", } } project.pyproject.write() with cd(project.root): result = pdm(["run", "test_script"], obj=project) assert result.exit_code == 0 assert (project.root / "output.txt").read_text().strip() == "hello" def test_run_script_with_relative_path(project, pdm, capfd): if os.name == "nt": (project.root / "test_script.bat").write_text("@echo Hello\n") else: (project.root / "test_script.sh").write_text("#!/bin/bash\necho Hello\n") (project.root / "test_script.sh").chmod(0o755) with cd(project.root): pdm(["run", "./test_script.bat" if os.name == "nt" else "./test_script.sh"], obj=project, strict=True) out, _ = capfd.readouterr() assert out.strip() == "Hello" def test_run_non_existing_local_script(project, pdm): with cd(project.root): result = pdm(["run", "./test_script.sh"], obj=project) assert result.exit_code != 0 assert "not a valid executable" in result.stderr @pytest.mark.parametrize( "args,expected", ( pytest.param(["hello"], "ok hello", id="with-args"), pytest.param([], "ok", id="without-args"), ), ) def test_run_shell_script_with_args_placeholder(project, pdm, args, expected): project.pyproject.settings["scripts"] = { "test_script": { "shell": "echo ok {args} > output.txt", "help": "test it won't fail", } } project.pyproject.write() with cd(project.root): result = pdm(["run", "test_script", *args], obj=project) assert result.exit_code == 0 assert (project.root / "output.txt").read_text().strip() == expected @pytest.mark.parametrize( "args,expected", ( pytest.param(["hello"], "hello", id="with-args"), pytest.param([], "default", id="with-default"), ), ) def test_run_shell_script_with_args_placeholder_with_default(project, pdm, args, expected): project.pyproject.settings["scripts"] = { "test_script": { "shell": "echo {args:default} > output.txt", "help": "test it won't fail", } } project.pyproject.write() with cd(project.root): result = pdm(["run", "test_script", *args], obj=project) assert result.exit_code == 0 assert (project.root / "output.txt").read_text().strip() == expected def test_run_call_script(project, pdm): (project.root / "test_script.py").write_text( textwrap.dedent( """ import argparse import sys def main(argv=None): parser = argparse.ArgumentParser() parser.add_argument("-c", "--code", type=int) args = parser.parse_args(argv) sys.exit(args.code) """ ) ) project.pyproject.settings["scripts"] = { "test_script": {"call": "test_script:main"}, "test_script_with_args": {"call": "test_script:main(['-c', '9'])"}, } project.pyproject.write() with cd(project.root): result = pdm(["run", "test_script", "-c", "8"], obj=project) assert result.exit_code == 8 result = pdm(["run", "test_script_with_args"], obj=project) assert result.exit_code == 9 def test_run_script_with_extra_args(project, pdm, capfd): (project.root / "test_script.py").write_text( textwrap.dedent( """ import sys print(*sys.argv[1:], sep='\\n') """ ) ) project.pyproject.settings["scripts"] = {"test_script": "python test_script.py"} project.pyproject.write() with cd(project.root): pdm(["run", "test_script", "-a", "-b", "-c"], obj=project) out, _ = capfd.readouterr() assert out.splitlines()[-3:] == ["-a", "-b", "-c"] @pytest.mark.parametrize( "args,expected", ( pytest.param(["-a", "-b", "-c"], ["-a", "-b", "-c", "-x"], id="with-args"), pytest.param([], ["-x"], id="without-args"), ), ) @pytest.mark.parametrize( "script", ( pytest.param("python test_script.py {args} -x", id="as-str"), pytest.param(["python", "test_script.py", "{args}", "-x"], id="as-list"), ), ) def test_run_script_with_args_placeholder(project, pdm, capfd, script, args, expected): (project.root / "test_script.py").write_text( textwrap.dedent( """ import sys print(*sys.argv[1:], sep='\\n') """ ) ) project.pyproject.settings["scripts"] = {"test_script": script} project.pyproject.write() with cd(project.root): pdm(["run", "-v", "test_script", *args], obj=project) out, _ = capfd.readouterr() assert out.strip().splitlines()[1:] == expected @pytest.mark.parametrize( "args,expected", ( pytest.param(["-a", "-b", "-c"], ["-a", "-b", "-c", "-x"], id="with-args"), pytest.param([], ["--default", "--value", "-x"], id="default"), ), ) @pytest.mark.parametrize( "script", ( pytest.param("python test_script.py {args:--default --value} -x", id="as-str"), pytest.param(["python", "test_script.py", "{args:--default --value}", "-x"], id="as-list"), ), ) def test_run_script_with_args_placeholder_with_default(project, pdm, capfd, script, args, expected): (project.root / "test_script.py").write_text( textwrap.dedent( """ import sys print(*sys.argv[1:], sep='\\n') """ ) ) project.pyproject.settings["scripts"] = {"test_script": script} project.pyproject.write() with cd(project.root): pdm(["run", "-v", "test_script", *args], obj=project) out, _ = capfd.readouterr() assert out.strip().splitlines()[1:] == expected def test_run_shell_script_with_pdm_placeholder(project, pdm): project.pyproject.settings["scripts"] = { "test_script": { "shell": "{pdm} -V > output.txt", "help": "test it won't fail", } } project.pyproject.write() with cd(project.root): result = pdm(["run", "test_script"], obj=project) assert result.exit_code == 0 assert (project.root / "output.txt").read_text().strip().startswith("PDM, version") def test_run_expand_env_vars(project, pdm, capfd, monkeypatch): (project.root / "test_script.py").write_text("import os; print(os.getenv('FOO'))") project.pyproject.settings["scripts"] = { "test_cmd": 'python -c "foo, bar = 0, 1;print(${FOO})"', "test_cmd_no_expand": "python -c 'print(${FOO})'", "test_script": "python test_script.py", "test_cmd_array": ["python", "test_script.py"], "test_shell": {"shell": "echo %FOO%" if os.name == "nt" else "echo $FOO"}, } project.pyproject.write() capfd.readouterr() with cd(project.root): monkeypatch.setenv("FOO", "bar") pdm(["run", "test_cmd"], obj=project) assert capfd.readouterr()[0].strip() == "1" result = pdm(["run", "test_cmd_no_expand"], obj=project) assert result.exit_code == 1 pdm(["run", "test_script"], obj=project) assert capfd.readouterr()[0].strip() == "bar" pdm(["run", "test_cmd_array"], obj=project) assert capfd.readouterr()[0].strip() == "bar" pdm(["run", "test_shell"], obj=project) assert capfd.readouterr()[0].strip() == "bar" def test_run_expand_env_vars_from_config(project, pdm, capfd): (project.root / "test_script.py").write_text("import os; print(os.getenv('FOO'))") project.pyproject.settings["scripts"] = { "test_cmd": 'python -c "foo, bar = 0, 1;print(${FOO})"', "test_cmd_no_expand": "python -c 'print(${FOO})'", "test_script": "python test_script.py", "test_cmd_array": ["python", "test_script.py"], "test_shell": {"shell": "echo %FOO%" if os.name == "nt" else "echo $FOO"}, "_": {"env": {"FOO": "bar"}}, } project.pyproject.write() capfd.readouterr() with cd(project.root): pdm(["run", "test_cmd"], obj=project) assert capfd.readouterr()[0].strip() == "1" result = pdm(["run", "test_cmd_no_expand"], obj=project) assert result.exit_code == 1 pdm(["run", "test_script"], obj=project) assert capfd.readouterr()[0].strip() == "bar" pdm(["run", "test_cmd_array"], obj=project) assert capfd.readouterr()[0].strip() == "bar" pdm(["run", "test_shell"], obj=project) assert capfd.readouterr()[0].strip() == "bar" def test_run_script_with_env_defined(project, pdm, capfd): (project.root / "test_script.py").write_text("import os; print(os.getenv('FOO'))") project.pyproject.settings["scripts"] = {"test_script": {"cmd": "python test_script.py", "env": {"FOO": "bar"}}} project.pyproject.write() capfd.readouterr() with cd(project.root): pdm(["run", "test_script"], obj=project) assert capfd.readouterr()[0].strip() == "bar" def test_run_script_with_dotenv_file(project, pdm, capfd, monkeypatch): (project.root / "test_script.py").write_text("import os; print(os.getenv('FOO'), os.getenv('BAR'))") project.pyproject.settings["scripts"] = { "test_override": { "cmd": "python test_script.py", "env_file": {"override": ".env"}, }, "test_default": {"cmd": "python test_script.py", "env_file": ".env"}, } project.pyproject.write() monkeypatch.setenv("BAR", "foo") (project.root / ".env").write_text("FOO=bar\nBAR=override") capfd.readouterr() with cd(project.root): pdm(["run", "test_default"], obj=project) assert capfd.readouterr()[0].strip() == "bar foo" pdm(["run", "test_override"], obj=project) assert capfd.readouterr()[0].strip() == "bar override" def test_run_script_override_global_env(project, pdm, capfd): (project.root / "test_script.py").write_text("import os; print(os.getenv('FOO'))") project.pyproject.settings["scripts"] = { "_": {"env": {"FOO": "bar"}}, "test_env": {"cmd": "python test_script.py"}, "test_env_override": {"cmd": "python test_script.py", "env": {"FOO": "foobar"}}, } project.pyproject.write() capfd.readouterr() with cd(project.root): pdm(["run", "test_env"], obj=project) assert capfd.readouterr()[0].strip() == "bar" pdm(["run", "test_env_override"], obj=project) assert capfd.readouterr()[0].strip() == "foobar" def test_run_show_list_of_scripts(project, pdm): project.pyproject.settings["scripts"] = { "test_composite": {"composite": ["test_cmd", "test_script", "test_shell"]}, "test_cmd": "flask db upgrade", "test_multi": """\ I am a multilines command """, "test_script": {"call": "test_script:main", "help": "call a python function"}, "test_shell": {"shell": "echo $FOO", "help": "shell command"}, } project.pyproject.write() result = pdm(["run", "--list"], obj=project) result_lines = result.output.splitlines()[3:] assert result_lines[0][1:-1].strip() == "test_cmd │ cmd │ flask db upgrade" sep = termui.Emoji.ARROW_SEPARATOR assert result_lines[1][1:-1].strip() == f"test_composite │ composite │ test_cmd {sep} test_script {sep} test_shell" assert result_lines[2][1:-1].strip() == f"test_multi │ cmd │ I am a multilines{termui.Emoji.ELLIPSIS}" assert result_lines[3][1:-1].strip() == "test_script │ call │ call a python function" assert result_lines[4][1:-1].strip() == "test_shell │ shell │ shell command" def test_run_show_list_of_scripts_hide_internals(project, pdm): project.pyproject.settings["scripts"] = { "public": "true", "_internal": "true", } project.pyproject.write() result = pdm(["run", "--list"], obj=project) assert "public" in result.output assert "_internal" not in result.output def test_run_json_list_of_scripts(project, pdm): project.pyproject.settings["scripts"] = { "_": {"env_file": ".env"}, "test_composite": {"composite": ["test_cmd", "test_script", "test_shell"]}, "test_cmd": "flask db upgrade", "test_multi": """\ I am a multilines command """, "test_script": {"call": "test_script:main", "help": "call a python function"}, "test_shell": {"shell": "echo $FOO", "help": "shell command"}, "test_env": {"cmd": "true", "env": {"TEST": "value"}}, "test_env_file": {"cmd": "true", "env_file": ".env"}, "test_override": {"cmd": "true", "env_file": {"override": ".env"}}, "test_site_packages": {"cmd": "true", "site_packages": True}, "_private": "true", } project.pyproject.write() result = pdm(["run", "--json"], obj=project, strict=True) sep = termui.Emoji.ARROW_SEPARATOR assert json.loads(result.outputs) == { "_": {"name": "_", "help": "Shared options", "kind": "shared", "env_file": ".env"}, "test_cmd": {"name": "test_cmd", "help": "flask db upgrade", "kind": "cmd", "args": "flask db upgrade"}, "test_composite": { "name": "test_composite", "help": f"test_cmd {sep} test_script {sep} test_shell", "kind": "composite", "args": ["test_cmd", "test_script", "test_shell"], }, "test_multi": { "name": "test_multi", "help": f"I am a multilines{termui.Emoji.ELLIPSIS}", "kind": "cmd", "args": " I am a multilines\n command\n ", }, "test_script": { "name": "test_script", "help": "call a python function", "kind": "call", "args": "test_script:main", }, "test_shell": {"name": "test_shell", "help": "shell command", "kind": "shell", "args": "echo $FOO"}, "test_env": {"name": "test_env", "help": "true", "kind": "cmd", "args": "true", "env": {"TEST": "value"}}, "test_env_file": {"name": "test_env_file", "help": "true", "kind": "cmd", "args": "true", "env_file": ".env"}, "test_override": { "name": "test_override", "help": "true", "kind": "cmd", "args": "true", "env_file.override": ".env", }, "test_site_packages": { "name": "test_site_packages", "help": "true", "kind": "cmd", "args": "true", "site_packages": True, }, "_private": { "name": "_private", "help": "true", "kind": "cmd", "args": "true", }, } @pytest.mark.usefixtures("local_finder") @pytest.mark.parametrize("explicit_python", [True, False]) def test_run_with_another_project_root(project, pdm, capfd, explicit_python): project.pyproject.metadata["requires-python"] = ">=3.6" project.pyproject.write() pdm(["add", "first"], obj=project) with TemporaryDirectory(prefix="pytest-run-") as tmp_dir: Path(tmp_dir).joinpath("main.py").write_text("import first;print(first.first([0, False, 1, 2]))\n") capfd.readouterr() with cd(tmp_dir): args = ["run", "-p", str(project.root), "main.py"] if explicit_python: args.insert(len(args) - 1, "python") ret = pdm(args) out, err = capfd.readouterr() assert ret.exit_code == 0, err assert out.strip() == "1" def test_import_another_sitecustomize(project, pdm, capfd): project.pyproject.metadata["requires-python"] = ">=2.7" project.pyproject.write() # a script for checking another sitecustomize is imported project.root.joinpath("foo.py").write_text("import os;print(os.getenv('FOO'))") # ensure there have at least one sitecustomize can be imported # there may have more than one sitecustomize.py in sys.path project.root.joinpath("sitecustomize.py").write_text("import os;os.environ['FOO'] = 'foo'") env = os.environ.copy() paths = env.get("PYTHONPATH") this_path = str(project.root) new_paths = [this_path] if not paths else [this_path, paths] env["PYTHONPATH"] = os.pathsep.join(new_paths) project._environment = None capfd.readouterr() with cd(project.root): result = pdm(["run", "python", "foo.py"], env=env) assert result.exit_code == 0, result.stderr out, _ = capfd.readouterr() assert out.strip() == "foo" def test_run_with_patched_sysconfig(project, pdm, capfd): project.root.joinpath("script.py").write_text( """\ import sysconfig import json print(json.dumps(sysconfig.get_paths())) """ ) capfd.readouterr() with cd(project.root): result = pdm(["run", "python", "script.py"], obj=project) assert result.exit_code == 0 out = json.loads(capfd.readouterr()[0]) assert "__pypackages__" in out["purelib"] def test_run_composite(project, pdm, capfd, _echo): project.pyproject.settings["scripts"] = { "first": "python echo.py First", "second": "python echo.py Second", "test": {"composite": ["first", "second"]}, } project.pyproject.write() capfd.readouterr() pdm(["run", "test"], strict=True, obj=project) out, _ = capfd.readouterr() assert "First CALLED" in out assert "Second CALLED" in out def test_composite_stops_on_first_failure(project, pdm, capfd): project.pyproject.settings["scripts"] = { "first": {"cmd": ["python", "-c", "print('First CALLED')"]}, "fail": "python -c 'raise Exception'", "second": "echo 'Second CALLED'", "test": {"composite": ["first", "fail", "second"]}, } project.pyproject.write() capfd.readouterr() result = pdm(["run", "test"], obj=project) assert result.exit_code == 1 out, _ = capfd.readouterr() assert "First CALLED" in out assert "Second CALLED" not in out def test_composite_keep_going_on_failure(project, pdm, capfd): project.pyproject.settings["scripts"] = { "first": {"cmd": ["python", "-c", "print('First CALLED')"]}, "fail": "python -c 'raise Exception'", "second": "echo 'Second CALLED'", "test": {"composite": ["first", "fail", "second"], "keep_going": True}, } project.pyproject.write() capfd.readouterr() result = pdm(["run", "test"], obj=project) assert result.exit_code == 1 out, err = capfd.readouterr() assert "First CALLED" in out assert "Second CALLED" in out def test_composite_inherit_env(project, pdm, capfd, _echo): project.pyproject.settings["scripts"] = { "first": { "cmd": "python echo.py First VAR", "env": {"VAR": "42"}, }, "second": { "cmd": "python echo.py Second VAR", "env": {"VAR": "42"}, }, "nested": { "composite": ["third"], "env": {"VAR": "42"}, }, "third": { "cmd": "python echo.py Third VAR", "env": {"VAR": "42"}, }, "test": {"composite": ["first", "second", "nested"], "env": {"VAR": "overridden"}}, } project.pyproject.write() capfd.readouterr() pdm(["run", "test"], strict=True, obj=project) out, _ = capfd.readouterr() assert "First CALLED with VAR=overridden" in out assert "Second CALLED with VAR=overridden" in out assert "Third CALLED with VAR=overridden" in out def test_composite_fail_on_first_missing_task(project, pdm, capfd, _echo): project.pyproject.settings["scripts"] = { "first": "python echo.py First", "second": "python echo.py Second", "test": {"composite": ["first", "fail", "second"]}, } project.pyproject.write() capfd.readouterr() result = pdm(["run", "test"], obj=project) assert result.exit_code == 1 out, _ = capfd.readouterr() assert "First CALLED" in out assert "Second CALLED" not in out def test_composite_fails_on_recursive_script(project, pdm): project.pyproject.settings["scripts"] = { "first": {"composite": ["first"]}, "second": {"composite": ["third"]}, "third": {"composite": ["second"]}, "fourth": {"composite": ["python -V", "python -V"]}, "fifth": {"composite": ["fourth", "fourth"]}, } project.pyproject.write() result = pdm(["run", "first"], obj=project) assert result.exit_code == 1 assert "Script first is recursive" in result.stderr result = pdm(["run", "second"], obj=project) assert result.exit_code == 1 assert "Script second is recursive" in result.stderr result = pdm(["run", "fourth"], obj=project) assert result.exit_code == 0 result = pdm(["run", "fifth"], obj=project) assert result.exit_code == 0 def test_composite_runs_all_hooks(project, pdm, capfd, _echo): project.pyproject.settings["scripts"] = { "test": {"composite": ["first", "second"]}, "pre_test": "python echo.py Pre-Test", "post_test": "python echo.py Post-Test", "first": "python echo.py First", "pre_first": "python echo.py Pre-First", "second": "python echo.py Second", "post_second": "python echo.py Post-Second", } project.pyproject.write() capfd.readouterr() pdm(["run", "test"], strict=True, obj=project) out, _ = capfd.readouterr() assert "Pre-Test CALLED" in out assert "Pre-First CALLED" in out assert "First CALLED" in out assert "Second CALLED" in out assert "Post-Second CALLED" in out assert "Post-Test CALLED" in out def test_composite_pass_parameters_to_subtasks(project, pdm, capfd, _args): project.pyproject.settings["scripts"] = { "test": {"composite": ["first", "second"]}, "pre_test": "python args.py Pre-Test", "post_test": "python args.py Post-Test", "first": "python args.py First", "pre_first": "python args.py Pre-First", "second": "python args.py Second", "post_second": "python args.py Post-Second", } project.pyproject.write() capfd.readouterr() pdm(["run", "test", "param=value"], strict=True, obj=project) out, _ = capfd.readouterr() assert "Pre-Test CALLED" in out assert "Pre-First CALLED" in out assert "First CALLED with param=value" in out assert "Second CALLED with param=value" in out assert "Post-Second CALLED" in out assert "Post-Test CALLED" in out def test_composite_can_pass_parameters(project, pdm, capfd, _args): project.pyproject.settings["scripts"] = { "test": {"composite": ["first param=first", "second param=second"]}, "pre_test": "python args.py Pre-Test", "post_test": "python args.py Post-Test", "first": "python args.py First", "pre_first": "python args.py Pre-First", "second": "python args.py Second", "post_second": "python args.py Post-Second", } project.pyproject.write() capfd.readouterr() pdm(["run", "test"], strict=True, obj=project) out, _ = capfd.readouterr() assert "Pre-Test CALLED" in out assert "Pre-First CALLED" in out assert "First CALLED with param=first" in out assert "Second CALLED with param=second" in out assert "Post-Second CALLED" in out assert "Post-Test CALLED" in out @pytest.mark.parametrize( "args,expected", ( pytest.param(["-a"], "-a, ", id="with-args"), pytest.param([], "", id="without-args"), ), ) def test_composite_only_pass_parameters_to_subtasks_with_args(project, pdm, capfd, _args, args, expected): project.pyproject.settings["scripts"] = { "test": {"composite": ["first", "second {args} key=value"]}, "first": "python args.py First", "second": "python args.py Second", } project.pyproject.write() capfd.readouterr() pdm(["run", "-v", "test", *args], strict=True, obj=project) out, _ = capfd.readouterr() assert "First CALLED" in out assert f"Second CALLED with {expected}key=value" in out @pytest.mark.parametrize( "args,expected", ( pytest.param(["-a"], "-a", id="with-args"), pytest.param([], "--default", id="default"), ), ) def test_composite_only_pass_parameters_to_subtasks_with_args_with_default(project, pdm, capfd, _args, args, expected): project.pyproject.settings["scripts"] = { "test": {"composite": ["first", "second {args:--default} key=value"]}, "first": "python args.py First", "second": "python args.py Second", } project.pyproject.write() capfd.readouterr() pdm(["run", "-v", "test", *args], strict=True, obj=project) out, _ = capfd.readouterr() assert "First CALLED" in out assert f"Second CALLED with {expected}, key=value" in out def test_composite_hooks_inherit_env(project, pdm, capfd, _echo): project.pyproject.settings["scripts"] = { "pre_task": {"cmd": "python echo.py Pre-Task VAR", "env": {"VAR": "42"}}, "task": "python echo.py Task", "post_task": {"cmd": "python echo.py Post-Task VAR", "env": {"VAR": "42"}}, "test": {"composite": ["task"], "env": {"VAR": "overridden"}}, } project.pyproject.write() capfd.readouterr() pdm(["run", "test"], strict=True, obj=project) out, _ = capfd.readouterr() assert "Pre-Task CALLED with VAR=overridden" in out assert "Task CALLED" in out assert "Post-Task CALLED with VAR=overridden" in out def test_composite_inherit_env_in_cascade(project, pdm, capfd, _echo): project.pyproject.settings["scripts"] = { "_": {"env": {"FOO": "BAR", "TIK": "TOK"}}, "pre_task": { "cmd": "python echo.py Pre-Task VAR FOO TIK", "env": {"VAR": "42", "FOO": "foobar"}, }, "task": { "cmd": "python echo.py Task VAR FOO TIK", "env": {"VAR": "42", "FOO": "foobar"}, }, "post_task": { "cmd": "python echo.py Post-Task VAR FOO TIK", "env": {"VAR": "42", "FOO": "foobar"}, }, "test": {"composite": ["task"], "env": {"VAR": "overridden"}}, } project.pyproject.write() capfd.readouterr() pdm(["run", "test"], strict=True, obj=project) out, _ = capfd.readouterr() assert "Pre-Task CALLED with VAR=overridden FOO=foobar TIK=TOK" in out assert "Task CALLED with VAR=overridden FOO=foobar TIK=TOK" in out assert "Post-Task CALLED with VAR=overridden FOO=foobar TIK=TOK" in out def test_composite_inherit_dotfile(project, pdm, capfd, _echo): (project.root / ".env").write_text("VAR=42") (project.root / "override.env").write_text("VAR=overridden") project.pyproject.settings["scripts"] = { "pre_task": {"cmd": "python echo.py Pre-Task VAR", "env_file": ".env"}, "task": {"cmd": "python echo.py Task VAR", "env_file": ".env"}, "post_task": {"cmd": "python echo.py Post-Task VAR", "env_file": ".env"}, "test": {"composite": ["task"], "env_file": "override.env"}, } project.pyproject.write() capfd.readouterr() pdm(["run", "test"], strict=True, obj=project) out, _ = capfd.readouterr() assert "Pre-Task CALLED with VAR=overridden" in out assert "Task CALLED with VAR=overridden" in out assert "Post-Task CALLED with VAR=overridden" in out def test_resolve_env_vars_in_dotfile(project, pdm, capfd, _echo): (project.root / ".env").write_text("VAR=42\nFOO=${OUT}/${VAR}") project.pyproject.settings["scripts"] = { "_": {"env_file": ".env"}, "test": {"cmd": "python echo.py Task FOO BAR", "env": {"BAR": "${FOO}/bar"}}, } project.pyproject.write() capfd.readouterr() pdm(["run", "test"], strict=True, obj=project, env={"OUT": "hello"}) out, _ = capfd.readouterr() assert "Task CALLED with FOO=hello/42 BAR=hello/42/bar" in out def test_composite_can_have_commands(project, pdm, capfd): project.pyproject.settings["scripts"] = { "task": {"cmd": ["python", "-c", 'print("Task CALLED")']}, "test": {"composite": ["task", "python -c 'print(\"Command CALLED\")'"]}, } project.pyproject.write() capfd.readouterr() pdm(["run", "-v", "test"], strict=True, obj=project) out, _ = capfd.readouterr() assert "Task CALLED" in out assert "Command CALLED" in out def test_run_shortcut(project, pdm, capfd): project.pyproject.settings["scripts"] = { "test": "echo 'Everything is fine'", } project.pyproject.write() capfd.readouterr() result = pdm(["test"], obj=project, strict=True) assert result.exit_code == 0 out, _ = capfd.readouterr() assert "Everything is fine" in out def test_run_shortcuts_dont_override_commands(project, pdm, capfd, mocker): do_lock = mocker.patch.object(actions, "do_lock") do_sync = mocker.patch.object(actions, "do_sync") project.pyproject.settings["scripts"] = { "install": "echo 'Should not run'", } project.pyproject.write() capfd.readouterr() result = pdm(["install"], obj=project, strict=True) assert result.exit_code == 0 out, _ = capfd.readouterr() assert "Should not run" not in out do_lock.assert_called_once() do_sync.assert_called_once() def test_run_shortcut_fail_with_usage_if_script_not_found(project, pdm): result = pdm(["whatever"], obj=project) assert result.exit_code != 0 assert "Command not found: whatever" in result.stderr assert "Usage" in result.stderr @pytest.mark.parametrize( "args", [ pytest.param(["-ko"], id="unknown param"), pytest.param(["pip", "--version"], id="not an user script"), ], ) def test_empty_positional_args_still_display_usage(project, pdm, args): result = pdm(args, obj=project) assert result.exit_code != 0 assert "Usage" in result.stderr def test_empty_positional_args_display_help(project, pdm): result = pdm([], obj=project) assert result.exit_code == 0 assert "Usage:" in result.output assert "Commands:" in result.output assert "Options:" in result.output def test_run_script_changing_working_dir(project, pdm, capfd): project.root.joinpath("subdir").mkdir() project.root.joinpath("subdir", "file.text").write_text("Hello world\n") project.pyproject.settings["scripts"] = { "test_script": {"working_dir": "subdir", "cmd": "cat file.text"}, } project.pyproject.write() capfd.readouterr() pdm(["run", "test_script"], obj=project, strict=True) assert capfd.readouterr()[0].strip() == "Hello world" def test_run_script_with_inline_metadata(project, pdm, local_finder, local_finder_artifacts): with cd(project.root): project.root.joinpath("test_script.py").write_text( textwrap.dedent("""\ from first import first assert first([0, False, 1, 2]) == 1 """) ) result = pdm(["run", "test_script.py"], obj=project) assert result.exit_code != 0 local_artifacts_url = local_finder_artifacts.as_uri() project.root.joinpath("test_script.py").write_text( textwrap.dedent(f"""\ # /// script # requires-python = ">=3.9" # dependencies = [ # "first", # ] # # [[tool.pdm.source]] # name = "pypi" # url = "{local_artifacts_url}" # type = "find_links" # /// from first import first assert first([0, False, 1, 2]) == 1 """) ) with cd(project.root): result = pdm(["run", "test_script.py"], obj=project) assert result.exit_code == 0 def test_run_script_pass_run_cwd(project, pdm, capfd): project.pyproject.settings["scripts"] = { "test_script": [ "python", "-c", "import os;print(os.getenv('PDM_RUN_CWD'))", ] } project.pyproject.write() capfd.readouterr() result = pdm(["run", "test_script"], obj=project) assert result.exit_code == 0 out, _ = capfd.readouterr() assert Path(out.strip()) == Path.cwd() def test_run_script_pass_run_cwd_to_original_working_dir_when_working_dir_of_script_is_changed(project, pdm, capfd): project.root.joinpath("subdir").mkdir() project.root.joinpath("subdir", "test_script.py").write_text( textwrap.dedent("""\ import os print(os.getenv('PDM_RUN_CWD', '')) """) ) project.pyproject.settings["scripts"] = { "test_script": {"working_dir": "subdir", "cmd": "python test_script.py"}, } project.pyproject.write() capfd.readouterr() pdm(["run", "test_script"], obj=project, strict=True) assert capfd.readouterr()[0].strip() == str(Path.cwd()) def test_run_script_default_verbosity(project, pdm): project.pyproject.settings["scripts"] = {"test": {"cmd": "python -V", "help": "help"}} project.pyproject.write() result = pdm(["run", "test"], strict=True, obj=project) assert "task test" not in result.stderr assert "['python', '-V']" not in result.stderr assert "help" not in result.stderr def test_run_script_default_verbosity_with_show_header(project, pdm): project.project_config.update({"scripts.show_header": True}) project.pyproject.settings["scripts"] = {"test": {"cmd": "python -V", "help": "help"}} project.pyproject.write() result = pdm(["run", "test"], strict=True, obj=project) assert "task test" in result.stderr assert "['python', '-V']" not in result.stderr assert "python -V" not in result.stderr assert "help" in result.stderr def test_run_script_default_verbosity_with_show_header_no_help(project, pdm): project.project_config.update({"scripts.show_header": True}) project.pyproject.settings["scripts"] = {"test": {"cmd": "python -V"}} project.pyproject.write() result = pdm(["run", "test"], strict=True, obj=project) assert "task test" in result.stderr assert "python -V" in result.stderr assert "['python', '-V']" not in result.stderr def test_run_script_verbose(project, pdm): project.pyproject.settings["scripts"] = {"test": {"cmd": "python -V", "help": "help"}} project.pyproject.write() result = pdm(["run", "-v", "test"], strict=True, obj=project) assert "task test" in result.stderr assert "['python', '-V']" in result.stderr assert "python -V" not in result.stderr assert "help" not in result.stderr def test_run_composite_script_default_verbosity_with_show_header(project, pdm): project.project_config.update({"scripts.show_header": True}) project.pyproject.settings["scripts"] = { "test": {"cmd": "python -V", "help": "help"}, "parent": {"composite": ["test", "test"]}, } project.pyproject.write() result = pdm(["run", "parent"], strict=True, obj=project) assert "task parent" in result.stderr assert f"test {termui.Emoji.ARROW_SEPARATOR} test" in result.stderr assert "['test', 'test']" not in result.stderr assert "task test" in result.stderr assert "python -V" not in result.stderr assert "['python', '-V']" not in result.stderr assert "help" in result.stderr def test_run_composite_script_default_verbosity_with_show_header_and_help(project, pdm): project.project_config.update({"scripts.show_header": True}) project.pyproject.settings["scripts"] = { "test": {"cmd": "python -V", "help": "help"}, "parent": {"composite": ["test", "test"], "help": "composite"}, } project.pyproject.write() result = pdm(["run", "parent"], strict=True, obj=project) assert "task parent" in result.stderr assert "composite" in result.stderr assert f"test {termui.Emoji.ARROW_SEPARATOR} test" not in result.stderr assert "['test', 'test']" not in result.stderr assert "task test" in result.stderr assert "['python', '-V']" not in result.stderr assert "python -V" not in result.stderr assert "help" in result.stderr def test_run_composite_script_verbose(project, pdm): project.pyproject.settings["scripts"] = { "test": {"cmd": "python -V", "help": "help"}, "parent": {"composite": ["test", "test"], "help": "composite"}, } project.pyproject.write() result = pdm(["run", "-v", "parent"], strict=True, obj=project) assert "task parent" in result.stderr assert "composite" not in result.stderr assert f"test {termui.Emoji.ARROW_SEPARATOR} test" not in result.stderr assert "['test', 'test']" in result.stderr assert "task test" in result.stderr assert "['python', '-V']" in result.stderr assert "python -V" not in result.stderr assert "help" not in result.stderr pdm-2.23.1/tests/cli/test_self_command.py000066400000000000000000000102641477560627500203560ustar00rootroot00000000000000from types import SimpleNamespace from unittest.mock import ANY import pytest from pdm.cli.commands import self_cmd def mock_distribution(metadata, entry_points=()): entry_points = (SimpleNamespace(group=ep) for ep in entry_points) return SimpleNamespace(metadata=metadata, entry_points=entry_points) DISTRIBUTIONS = { "foo": mock_distribution({"Name": "foo", "Version": "1.0.0", "Summary": "Foo package"}, ["pdm.plugin"]), "bar": mock_distribution({"Name": "bar", "Version": "2.0.0", "Summary": "Bar package"}, ["pdm"]), "baz": mock_distribution({"Name": "baz", "Version": "3.0.0", "Summary": "Baz package"}), } @pytest.fixture() def mock_pip(mocker): mocked = mocker.patch("pdm.cli.commands.self_cmd.run_pip") return mocked @pytest.fixture() def mock_all_distributions(mocker): mocker.patch("pdm.cli.commands.self_cmd.WorkingSet", return_value=DISTRIBUTIONS) @pytest.fixture() def mock_latest_pdm_version(mocker): return mocker.patch( "pdm.cli.commands.self_cmd.get_latest_pdm_version_from_pypi", ) @pytest.mark.usefixtures("mock_all_distributions") def test_self_list(pdm): result = pdm(["self", "list"]) assert result.exit_code == 0, result.stderr packages = [line.split()[0] for line in result.stdout.splitlines()] assert packages == ["bar", "baz", "foo"] @pytest.mark.usefixtures("mock_all_distributions") def test_self_list_plugins(pdm): result = pdm(["self", "list", "--plugins"]) assert result.exit_code == 0, result.stderr packages = [line.split()[0] for line in result.stdout.splitlines()] assert packages == ["bar", "foo"] def test_self_add(pdm, mock_pip): result = pdm(["self", "add", "foo"]) assert result.exit_code == 0, result.stderr mock_pip.assert_called_with(ANY, ["install", "foo"]) result = pdm(["self", "add", "--pip-args", "--force-reinstall --upgrade", "foo"]) assert result.exit_code == 0, result.stderr mock_pip.assert_called_with(ANY, ["install", "--force-reinstall", "--upgrade", "foo"]) def test_self_remove(pdm, mock_pip, mocker, monkeypatch): from rich import get_console console = get_console() def _mock_resolve(packages): return ["demo", "pytz"] if "demo" in packages else packages mocker.patch.object( self_cmd.RemoveCommand, "_resolve_dependencies_to_remove", side_effect=_mock_resolve, ) mocker.patch.object(console, "is_interactive", True) result = pdm(["self", "remove", "foo"]) assert result.exit_code != 0 result = pdm(["self", "remove", "-y", "demo"]) assert result.exit_code == 0, result.stderr mock_pip.assert_called_with(ANY, ["uninstall", "-y", "demo", "pytz"]) with monkeypatch.context() as m: m.setenv("PDM_NON_INTERACTIVE", "1") result = pdm(["self", "remove", "demo"]) assert result.exit_code == 0, result.stderr mock_pip.assert_called_with(ANY, ["uninstall", "-y", "demo", "pytz"]) result = pdm(["-n", "self", "remove", "demo"]) assert result.exit_code == 0, result.stderr mock_pip.assert_called_with(ANY, ["uninstall", "-y", "demo", "pytz"]) @pytest.mark.parametrize( "args,expected", [ (["self", "update"], ["install", "--upgrade", "--upgrade-strategy", "eager", "pdm[locked]==99.0.0"]), (["self", "update", "--pre"], ["install", "--upgrade", "--upgrade-strategy", "eager", "pdm[locked]==99.0.1b1"]), ( ["self", "update", "--head"], ["install", "--upgrade", "--upgrade-strategy", "eager", f"pdm[locked] @ git+{self_cmd.PDM_REPO}@main"], ), ], ) def test_self_update(pdm, mock_pip, mock_latest_pdm_version, args, expected): def mocked_latest_version(project, pre): return "99.0.1b1" if pre else "99.0.0" mock_latest_pdm_version.side_effect = mocked_latest_version result = pdm(args) assert result.exit_code == 0, result.stderr mock_pip.assert_called_with(ANY, expected) def test_self_update_already_latest(pdm, mock_pip, mock_latest_pdm_version): mock_latest_pdm_version.return_value = "0.0.0" result = pdm(["self", "update"]) assert result.exit_code == 0, result.stderr assert "Already up-to-date" in result.stdout mock_pip.assert_not_called() pdm-2.23.1/tests/cli/test_template.py000066400000000000000000000054661477560627500175520ustar00rootroot00000000000000import os import pytest from pdm.cli.templates import ProjectTemplate from pdm.exceptions import PdmException def test_non_pyproject_template_disallowed(project_no_init): with ProjectTemplate("tests/fixtures/projects/demo_extras") as template: with pytest.raises(PdmException, match="Template pyproject.toml not found"): template.generate(project_no_init.root, {"project": {"name": "foo"}}) def test_module_project_template(project_no_init): metadata = { "project": {"name": "foo", "version": "0.1.0", "requires-python": ">=3.10"}, "build-system": {"requires": ["pdm-backend"], "build-backend": "pdm.backend"}, } with ProjectTemplate("tests/fixtures/projects/demo") as template: template.generate(project_no_init.root, metadata) project_no_init.pyproject.reload() assert project_no_init.pyproject.metadata["name"] == "foo" assert project_no_init.pyproject.metadata["requires-python"] == ">=3.10" assert project_no_init.pyproject._data["build-system"] == metadata["build-system"] assert project_no_init.pyproject.metadata["dependencies"] == ["idna", "chardet; os_name=='nt'"] assert project_no_init.pyproject.metadata["optional-dependencies"]["tests"] == ["pytest"] assert (project_no_init.root / "foo.py").exists() assert os.access(project_no_init.root / "foo.py", os.W_OK) def test_module_project_template_generate_application(project_no_init): metadata = { "project": {"name": "", "version": "", "requires-python": ">=3.10"}, } with ProjectTemplate("tests/fixtures/projects/demo") as template: template.generate(project_no_init.root, metadata) project_no_init.pyproject.reload() assert project_no_init.pyproject.metadata["name"] == "" assert "build-system" not in project_no_init.pyproject._data assert project_no_init.pyproject.metadata["dependencies"] == ["idna", "chardet; os_name=='nt'"] assert (project_no_init.root / "demo.py").exists() def test_package_project_template(project_no_init): metadata = { "project": {"name": "foo", "version": "0.1.0", "requires-python": ">=3.10"}, "build-system": {"requires": ["pdm-backend"], "build-backend": "pdm.backend"}, } with ProjectTemplate("tests/fixtures/projects/demo-package") as template: template.generate(project_no_init.root, metadata) project_no_init.pyproject.reload() assert project_no_init.pyproject.metadata["name"] == "foo" assert project_no_init.pyproject.metadata["requires-python"] == ">=3.10" assert project_no_init.pyproject._data["build-system"] == metadata["build-system"] assert (project_no_init.root / "foo").is_dir() assert (project_no_init.root / "foo/__init__.py").exists() assert project_no_init.pyproject.settings["version"] == {"path": "foo/__init__.py", "source": "file"} pdm-2.23.1/tests/cli/test_update.py000066400000000000000000000326421477560627500172150ustar00rootroot00000000000000import pytest @pytest.mark.usefixtures("working_set") def test_update_packages_with_top(project, pdm): pdm(["add", "requests"], obj=project, strict=True) result = pdm(["update", "--top", "requests"], obj=project) assert "PdmUsageError" in result.stderr def test_update_command(project, pdm, mocker): do_update = mocker.patch("pdm.cli.commands.update.Command.do_update") pdm(["update"], obj=project) do_update.assert_called_once() @pytest.mark.usefixtures("working_set") def test_update_ignore_constraints(project, repository, pdm): project.project_config["strategy.save"] = "compatible" pdm(["add", "pytz"], obj=project, strict=True) assert project.pyproject.metadata["dependencies"] == ["pytz~=2019.3"] repository.add_candidate("pytz", "2020.2") pdm(["update", "pytz"], obj=project, strict=True) assert project.pyproject.metadata["dependencies"] == ["pytz~=2019.3"] assert project.get_locked_repository().candidates["pytz"].version == "2019.3" pdm(["update", "pytz", "--unconstrained"], obj=project, strict=True) assert project.pyproject.metadata["dependencies"] == ["pytz~=2020.2"] assert project.get_locked_repository().candidates["pytz"].version == "2020.2" pdm(["add", "chardet"], obj=project, strict=True) assert "chardet~=3.0" in project.pyproject.metadata["dependencies"] assert project.get_locked_repository().candidates["chardet"].version == "3.0.4" repository.add_candidate("chardet", "3.0.6") pdm(["update", "chardet", "--unconstrained", "--save-safe-compatible"], obj=project, strict=True) assert "chardet~=3.0.6" in project.pyproject.metadata["dependencies"] @pytest.mark.usefixtures("working_set") @pytest.mark.parametrize("strategy", ["reuse", "all"]) def test_update_all_packages(project, repository, pdm, strategy): pdm(["add", "requests", "pytz"], obj=project, strict=True) repository.add_candidate("pytz", "2019.6") repository.add_candidate("chardet", "3.0.5") repository.add_candidate("requests", "2.20.0") repository.add_dependencies( "requests", "2.20.0", [ "certifi>=2017.4.17", "chardet<3.1.0,>=3.0.2", "idna<2.8,>=2.5", "urllib3<1.24,>=1.21.1", ], ) result = pdm(["update", f"--update-{strategy}"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert locked_candidates["requests"].version == "2.20.0" assert locked_candidates["chardet"].version == ("3.0.5" if strategy == "all" else "3.0.4") assert locked_candidates["pytz"].version == "2019.6" update_num = 3 if strategy == "all" else 2 assert f"{update_num} to update" in result.stdout, result.stdout result = pdm(["sync"], obj=project, strict=True) assert "All packages are synced to date" in result.stdout def test_update_no_lock(project, working_set, repository, pdm): pdm(["add", "pytz"], obj=project, strict=True) repository.add_candidate("pytz", "2019.6") pdm(["update", "--frozen-lockfile"], obj=project, strict=True) assert working_set["pytz"].version == "2019.6" project.lockfile.reload() assert project.get_locked_repository().candidates["pytz"].version == "2019.3" @pytest.mark.usefixtures("working_set") def test_update_dry_run(project, repository, pdm): pdm(["add", "requests", "pytz"], obj=project, strict=True) repository.add_candidate("pytz", "2019.6") repository.add_candidate("chardet", "3.0.5") repository.add_candidate("requests", "2.20.0") repository.add_dependencies( "requests", "2.20.0", [ "certifi>=2017.4.17", "chardet<3.1.0,>=3.0.2", "idna<2.8,>=2.5", "urllib3<1.24,>=1.21.1", ], ) result = pdm(["update", "--dry-run"], obj=project, strict=True) project.lockfile.reload() locked_candidates = project.get_locked_repository().candidates assert locked_candidates["requests"].version == "2.19.1" assert locked_candidates["chardet"].version == "3.0.4" assert locked_candidates["pytz"].version == "2019.3" assert "requests 2.19.1 -> 2.20.0" in result.stdout @pytest.mark.usefixtures("working_set") def test_update_top_packages_dry_run(project, repository, pdm): pdm(["add", "requests", "pytz"], obj=project, strict=True) repository.add_candidate("pytz", "2019.6") repository.add_candidate("chardet", "3.0.5") repository.add_candidate("requests", "2.20.0") repository.add_dependencies( "requests", "2.20.0", [ "certifi>=2017.4.17", "chardet<3.1.0,>=3.0.2", "idna<2.8,>=2.5", "urllib3<1.24,>=1.21.1", ], ) result = pdm(["update", "--dry-run", "--top"], obj=project, strict=True) assert "requests 2.19.1 -> 2.20.0" in result.stdout assert "- chardet 3.0.4 -> 3.0.5" not in result.stdout @pytest.mark.usefixtures("working_set") def test_update_specified_packages(project, repository, pdm): pdm(["add", "requests", "pytz", "--no-sync"], obj=project, strict=True) repository.add_candidate("pytz", "2019.6") repository.add_candidate("chardet", "3.0.5") repository.add_candidate("requests", "2.20.0") repository.add_dependencies( "requests", "2.20.0", [ "certifi>=2017.4.17", "chardet<3.1.0,>=3.0.2", "idna<2.8,>=2.5", "urllib3<1.24,>=1.21.1", ], ) pdm(["update", "requests"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert locked_candidates["requests"].version == "2.20.0" assert locked_candidates["chardet"].version == "3.0.4" @pytest.mark.usefixtures("working_set") def test_update_specified_packages_eager_mode(project, repository, pdm): pdm(["add", "requests", "pytz", "--no-sync"], obj=project, strict=True) repository.add_candidate("pytz", "2019.6") repository.add_candidate("chardet", "3.0.5") repository.add_candidate("requests", "2.20.0") repository.add_dependencies( "requests", "2.20.0", [ "certifi>=2017.4.17", "chardet<3.1.0,>=3.0.2", "idna<2.8,>=2.5", "urllib3<1.24,>=1.21.1", ], ) pdm(["update", "requests", "--update-eager"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert locked_candidates["requests"].version == "2.20.0" assert locked_candidates["chardet"].version == "3.0.5" assert locked_candidates["pytz"].version == "2019.3" @pytest.mark.usefixtures("working_set") def test_update_transitive(project, repository, pdm): pdm(["add", "requests", "--no-sync"], obj=project, strict=True) repository.add_candidate("chardet", "3.0.5") repository.add_candidate("requests", "2.20.0") repository.add_dependencies( "requests", "2.20.0", [ "certifi>=2017.4.17", "chardet<3.1.0,>=3.0.2", "idna<2.8,>=2.5", "urllib3<1.24,>=1.21.1", ], ) pdm(["update", "chardet"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert not any("chardet" in dependency for dependency in project.pyproject.metadata["dependencies"]) assert locked_candidates["chardet"].version == "3.0.5" assert locked_candidates["requests"].version == "2.19.1" @pytest.mark.usefixtures("working_set") def test_update_transitive_nonexistent_dependencies(project, pdm): pdm(["add", "requests", "--no-sync"], obj=project, strict=True) result = pdm(["update", "numpy"], obj=project) assert "ProjectError" in result.stderr assert "numpy does not exist in" in result.stderr @pytest.mark.usefixtures("working_set") def test_update_package_wrong_group(project, pdm): pdm(["add", "-d", "requests"], obj=project, strict=True) result = pdm(["update", "requests"], obj=project) assert "ProjectError" in result.stderr assert "requests does not exist in default, but exists in dev" in result.stderr @pytest.mark.usefixtures("working_set") def test_update_transitive_non_transitive_dependencies(project, repository, pdm): pdm(["add", "requests", "pytz", "--no-sync"], obj=project, strict=True) repository.add_candidate("pytz", "2019.6") repository.add_candidate("chardet", "3.0.5") repository.add_candidate("requests", "2.20.0") repository.add_dependencies( "requests", "2.20.0", [ "certifi>=2017.4.17", "chardet<3.1.0,>=3.0.2", "idna<2.8,>=2.5", "urllib3<1.24,>=1.21.1", ], ) pdm(["update", "requests", "chardet", "pytz"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert not any("chardet" in dependency for dependency in project.pyproject.metadata["dependencies"]) assert locked_candidates["requests"].version == "2.20.0" assert locked_candidates["chardet"].version == "3.0.5" assert locked_candidates["pytz"].version == "2019.6" @pytest.mark.usefixtures("working_set") def test_update_specified_packages_eager_mode_config(project, repository, pdm): pdm(["add", "requests", "pytz", "--no-sync"], obj=project, strict=True) repository.add_candidate("pytz", "2019.6") repository.add_candidate("chardet", "3.0.5") repository.add_candidate("requests", "2.20.0") repository.add_dependencies( "requests", "2.20.0", [ "certifi>=2017.4.17", "chardet<3.1.0,>=3.0.2", "idna<2.8,>=2.5", "urllib3<1.24,>=1.21.1", ], ) pdm(["config", "strategy.update", "eager"], obj=project, strict=True) pdm(["update", "requests"], obj=project, strict=True) locked_candidates = project.get_locked_repository().candidates assert locked_candidates["requests"].version == "2.20.0" assert locked_candidates["chardet"].version == "3.0.5" assert locked_candidates["pytz"].version == "2019.3" @pytest.mark.usefixtures("working_set") def test_update_with_package_and_groups_argument(project, pdm): pdm(["add", "-G", "web", "requests"], obj=project, strict=True) pdm(["add", "-Gextra", "pytz"], obj=project, strict=True) result = pdm(["update", "requests", "--group", "web", "-G", "extra"], obj=project) assert "PdmUsageError" in result.stderr result = pdm(["update", "requests", "--no-default"], obj=project) assert "PdmUsageError" in result.stderr @pytest.mark.usefixtures("working_set") def test_update_with_prerelease_without_package_argument(project, pdm): pdm(["add", "requests"], obj=project, strict=True) result = pdm(["update", "--prerelease"], obj=project) assert "--prerelease/--stable must be used with packages given" in result.stderr def test_update_existing_package_with_prerelease(project, working_set, pdm): project.project_config["strategy.save"] = "compatible" pdm(["add", "urllib3"], obj=project, strict=True) assert project.pyproject.metadata["dependencies"][0] == "urllib3~=1.22" assert working_set["urllib3"].version == "1.22" pdm(["update", "urllib3", "--prerelease"], obj=project, strict=True) assert project.pyproject.metadata["dependencies"][0] == "urllib3~=1.22" assert working_set["urllib3"].version == "1.23b0" pdm(["update", "urllib3"], obj=project, strict=True) # prereleases should be kept assert working_set["urllib3"].version == "1.23b0" pdm(["update", "urllib3", "--stable"], obj=project, strict=True) assert working_set["urllib3"].version == "1.22" pdm(["update", "urllib3", "--prerelease", "--unconstrained"], obj=project, strict=True) assert project.pyproject.metadata["dependencies"][0] == "urllib3<2,>=1.23b0" assert working_set["urllib3"].version == "1.23b0" def test_update_package_with_extras(project, repository, working_set, pdm): repository.add_candidate("foo", "0.1") foo_deps = ["urllib3; extra == 'req'"] repository.add_dependencies("foo", "0.1", foo_deps) pdm(["add", "foo[req]"], obj=project, strict=True) assert working_set["foo"].version == "0.1" repository.add_candidate("foo", "0.2") repository.add_dependencies("foo", "0.2", foo_deps) pdm(["update"], obj=project, strict=True) assert working_set["foo"].version == "0.2" assert project.get_locked_repository().candidates["foo"].version == "0.2" def test_update_groups_in_lockfile(project, working_set, pdm, repository): pdm(["add", "requests"], obj=project, strict=True) repository.add_candidate("foo", "0.1") pdm(["add", "foo", "--group", "extra"], obj=project, strict=True) assert project.lockfile.groups == ["default", "extra"] repository.add_candidate("foo", "0.2") pdm(["update"], obj=project, strict=True) assert project.get_locked_repository().candidates["foo"].version == "0.2" assert working_set["foo"].version == "0.2" def test_update_group_not_in_lockfile(project, working_set, pdm): pdm(["add", "requests"], obj=project, strict=True) project.add_dependencies(["pytz"], to_group="extra") result = pdm(["update", "--group", "extra"], obj=project) assert result.exit_code != 0 assert "Requested groups not in lockfile: extra" in result.stderr @pytest.mark.usefixtures("working_set") def test_update_dependency_group_with_include(project, pdm): from pdm.formats.base import make_array project.pyproject.dependency_groups.update({"tz": ["pytz"], "web": make_array([{"include-group": "tz"}])}) project.pyproject.write() pdm(["update", "-u"], obj=project, strict=True) pdm-2.23.1/tests/cli/test_use.py000066400000000000000000000075271477560627500165330ustar00rootroot00000000000000import os import shutil import sys from pathlib import Path import pytest from pdm.cli.commands.use import Command as UseCommand from pdm.exceptions import NoPythonVersion from pdm.models.caches import JSONFileCache def test_use_command(project, pdm): python = "python" if os.name == "nt" else "python3" python_path = shutil.which(python) result = pdm(["use", "-f", python], obj=project) assert result.exit_code == 0 config_content = project.root.joinpath(".pdm-python").read_text() assert Path(python_path).as_posix() in config_content result = pdm(["use", "-f", python_path], obj=project) assert result.exit_code == 0 project.pyproject.metadata["requires-python"] = ">=3.6" result = pdm(["use", "2.7"], obj=project) assert result.exit_code == 1 def test_use_python_by_version(project, pdm): python_version = ".".join(map(str, sys.version_info[:2])) result = pdm(["use", "-f", python_version], obj=project) assert result.exit_code == 0 @pytest.mark.skipif(os.name != "posix", reason="Run on POSIX platforms only") def test_use_wrapper_python(project): wrapper_script = f"""#!/bin/bash exec "{sys.executable}" "$@" """ shim_path = project.root.joinpath("python_shim.sh") shim_path.write_text(wrapper_script) shim_path.chmod(0o755) UseCommand().do_use(project, shim_path.as_posix()) assert project.python.executable == Path(sys.executable) @pytest.mark.skipif(os.name != "posix", reason="Run on POSIX platforms only") def test_use_invalid_wrapper_python(project): wrapper_script = """#!/bin/bash echo hello """ shim_path = project.root.joinpath("python_shim.sh") shim_path.write_text(wrapper_script) shim_path.chmod(0o755) with pytest.raises(NoPythonVersion): UseCommand().do_use(project, shim_path.as_posix()) def test_use_remember_last_selection(project, mocker): (project.cache_dir / "use_cache.json").unlink(missing_ok=True) cache = JSONFileCache(project.cache_dir / "use_cache.json") do_use = UseCommand().do_use do_use(project, first=True) cache._read_cache() assert not cache._cache do_use(project, "3", first=True) cache._read_cache() assert "3" in cache mocker.patch.object(project, "find_interpreters") do_use(project, "3") project.find_interpreters.assert_not_called() def test_use_venv_python(project, pdm): pdm(["venv", "create"], obj=project, strict=True) pdm(["venv", "create", "--name", "test"], obj=project, strict=True) project.global_config["python.use_venv"] = True venv_location = project.config["venv.location"] do_use = UseCommand().do_use do_use(project, venv="in-project") assert project.python.executable.parent.parent == project.root.joinpath(".venv") do_use(project, venv="test") assert project.python.executable.parent.parent.parent == Path(venv_location) with pytest.raises(Exception, match="No virtualenv with key 'non-exists' is found"): do_use(project, venv="non-exists") def test_use_auto_install_and_no_auto_install_are_mutual_exclusive(project, pdm): command = ["use", "--auto-install-min", "-f"] with pytest.raises(RuntimeError) as error: result = pdm(command, obj=project, strict=True) assert str(error.value).startswith(f"Call command {command} failed") assert result.exit_code != 0 command = ["use", "--auto-install-max", "-f"] with pytest.raises(RuntimeError) as error: result = pdm(command, obj=project, strict=True) assert str(error.value).startswith(f"Call command {command} failed") assert result.exit_code != 0 command = ["use", "--auto-install-max", "--auto-install-min"] with pytest.raises(RuntimeError) as error: result = pdm(command, obj=project, strict=True) assert str(error.value).startswith(f"Call command {command} failed") assert result.exit_code != 0 pdm-2.23.1/tests/cli/test_utils.py000066400000000000000000000004741477560627500170710ustar00rootroot00000000000000def test_help_with_unknown_arguments(pdm): result = pdm(["add", "--unknown-args"]) assert "Usage: pdm add " in result.stderr assert result.exit_code == 2 def test_output_similar_command_when_typo(pdm): result = pdm(["instal"]) assert "install" in result.stderr assert result.exit_code == 2 pdm-2.23.1/tests/cli/test_venv.py000066400000000000000000000310201477560627500166760ustar00rootroot00000000000000import os import platform import re import shutil import sys from unittest.mock import ANY import pytest import shellingham from pdm.cli.commands.venv import backends from pdm.cli.commands.venv.utils import get_venv_prefix @pytest.fixture(params=[True, False]) def with_pip(request): return request.param @pytest.fixture() def fake_create(monkeypatch): def fake_create(self, location, *args, prompt=None): bin_dir = "Scripts" if sys.platform == "win32" else "bin" suffix = ".exe" if sys.platform == "win32" else "" (location / bin_dir).mkdir(parents=True) (location / bin_dir / f"python{suffix}").touch() monkeypatch.setattr(backends.VirtualenvBackend, "perform_create", fake_create) monkeypatch.setattr(backends.VenvBackend, "perform_create", fake_create) monkeypatch.setattr(backends.CondaBackend, "perform_create", fake_create) @pytest.mark.usefixtures("fake_create") def test_venv_create(pdm, project): project._saved_python = None project.project_config["venv.in_project"] = False result = pdm(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) assert os.path.exists(venv_path) assert not project._saved_python @pytest.mark.usefixtures("fake_create") def test_venv_create_in_project(pdm, project): project.project_config["venv.in_project"] = True pdm(["venv", "create"], obj=project, strict=True) venv_path = project.root / ".venv" assert venv_path.exists() result = pdm(["venv", "create"], obj=project) assert result.exit_code == 1 assert "is not empty" in result.stderr @pytest.mark.usefixtures("fake_create") def test_venv_create_other_location(pdm, project): pdm(["venv", "-p", project.root.as_posix(), "create"], strict=True) venv_path = project.root / ".venv" assert venv_path.exists() result = pdm(["venv", "-p", project.root.as_posix(), "create"]) assert result.exit_code == 1 assert "is not empty" in result.stderr @pytest.mark.usefixtures("fake_create") def test_venv_show_path(pdm, project): project.project_config["venv.in_project"] = True pdm(["venv", "create"], obj=project, strict=True) pdm(["venv", "create", "--name", "test"], obj=project, strict=True) result = pdm(["venv", "--path", "in-project"], obj=project, strict=True) assert result.output.strip() == str(project.root / ".venv") result = pdm(["venv", "--path", "test"], obj=project) assert result.exit_code == 0 result = pdm(["venv", "--path", "foo"], obj=project) assert result.exit_code == 1 @pytest.mark.usefixtures("fake_create") def test_venv_list(pdm, project): project.project_config["venv.in_project"] = False result = pdm(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) result = pdm(["venv", "list"], obj=project) assert result.exit_code == 0, result.stderr assert venv_path in result.output @pytest.mark.usefixtures("fake_create") def test_venv_remove(pdm, project): project.project_config["venv.in_project"] = False result = pdm(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) key = os.path.basename(venv_path)[len(get_venv_prefix(project)) :] result = pdm(["venv", "remove", "non-exist"], obj=project) assert result.exit_code != 0 result = pdm(["venv", "remove", "-y", key], obj=project) assert result.exit_code == 0, result.stderr assert not os.path.exists(venv_path) @pytest.mark.usefixtures("fake_create") def test_venv_recreate(pdm, project): project.project_config["venv.in_project"] = False result = pdm(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr result = pdm(["venv", "create"], obj=project) assert result.exit_code != 0 result = pdm(["venv", "create", "-f"], obj=project) assert result.exit_code == 0, result.stderr @pytest.mark.usefixtures("venv_backends") def test_venv_activate(pdm, mocker, project): project.project_config["venv.in_project"] = False result = pdm(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) key = os.path.basename(venv_path)[len(get_venv_prefix(project)) :] mocker.patch("shellingham.detect_shell", return_value=("bash", None)) result = pdm(["venv", "activate", key], obj=project) assert result.exit_code == 0, result.stderr backend = project.config["venv.backend"] if backend == "conda": assert result.output.startswith("conda activate") else: assert result.output.strip("'\"\n").endswith("activate") if platform.system() == "Windows": assert not result.output.startswith("source") assert not result.output.startswith("'") else: assert result.output.startswith("source") @pytest.mark.usefixtures("venv_backends") @pytest.mark.skipif(platform.system() == "Windows", reason="UNIX only") def test_venv_activate_tcsh(pdm, mocker, project): project.project_config["venv.in_project"] = False result = pdm(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) key = os.path.basename(venv_path)[len(get_venv_prefix(project)) :] mocker.patch("shellingham.detect_shell", return_value=("tcsh", None)) result = pdm(["venv", "activate", key], obj=project) assert result.output.startswith("source") and result.output.strip("'\"\n").endswith("activate.csh") @pytest.mark.usefixtures("venv_backends") def test_venv_activate_custom_prompt(pdm, mocker, project): project.project_config["venv.in_project"] = False creator = mocker.patch("pdm.cli.commands.venv.backends.Backend.create") result = pdm(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr creator.assert_called_once_with( None, [], False, False, prompt=project.project_config["venv.prompt"], with_pip=False ) def test_venv_activate_project_without_python(pdm, project): project._saved_python = None result = pdm(["venv", "activate"], obj=project) assert result.exit_code != 0 assert "The project doesn't have a saved python.path" in result.stderr @pytest.mark.usefixtures("fake_create") def test_venv_activate_error(pdm, project): project.project_config["venv.in_project"] = False result = pdm(["venv", "create"], obj=project, strict=True) result = pdm(["venv", "activate", "foo"], obj=project) assert result.exit_code != 0 assert "No virtualenv with key" in result.stderr project._saved_python = os.path.abspath("fake/bin/python") result = pdm(["venv", "activate"], obj=project) assert result.exit_code != 0, result.output + result.stderr assert "Can't activate a non-venv Python" in result.stderr @pytest.mark.usefixtures("venv_backends") def test_venv_activate_no_shell(pdm, mocker, project): project.project_config["venv.in_project"] = False result = pdm(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) key = os.path.basename(venv_path)[len(get_venv_prefix(project)) :] mocker.patch("shellingham.detect_shell", side_effect=shellingham.ShellDetectionFailure()) result = pdm(["venv", "activate", key], obj=project) assert result.exit_code == 0, result.stderr backend = project.config["venv.backend"] if backend == "conda": assert result.output.startswith("conda activate") else: assert result.output.strip("'\"\n").endswith("activate") if platform.system() == "Windows": assert not result.output.startswith("source") assert not result.output.startswith("'") else: assert result.output.startswith("source") @pytest.mark.usefixtures("fake_create") @pytest.mark.parametrize("keep_pypackages", [True, False]) def test_venv_auto_create(pdm, mocker, project, keep_pypackages): creator = mocker.patch("pdm.cli.commands.venv.backends.Backend.create") project._saved_python = None if keep_pypackages: project.root.joinpath("__pypackages__").mkdir(exist_ok=True) else: shutil.rmtree(project.root / "__pypackages__", ignore_errors=True) project.project_config["python.use_venv"] = True pdm(["install", "--no-self"], obj=project) if keep_pypackages: creator.assert_not_called() else: creator.assert_called_once() @pytest.mark.usefixtures("fake_create") def test_venv_purge(pdm, project): project.project_config["venv.in_project"] = False result = pdm(["venv", "purge"], obj=project) assert result.exit_code == 0, result.stderr result = pdm(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) result = pdm(["venv", "purge"], input="y", obj=project) assert result.exit_code == 0, result.stderr assert not os.path.exists(venv_path) @pytest.mark.usefixtures("fake_create") def test_venv_purge_force(pdm, project): project.project_config["venv.in_project"] = False result = pdm(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) result = pdm(["venv", "purge", "-f"], obj=project) assert result.exit_code == 0, result.stderr assert not os.path.exists(venv_path) user_options = [("none", True), ("0", False), ("all", False)] @pytest.mark.usefixtures("venv_backends") @pytest.mark.parametrize("user_choices, is_path_exists", user_options) def test_venv_purge_interactive(pdm, user_choices, is_path_exists, project): project.project_config["venv.in_project"] = False result = pdm(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) result = pdm(["venv", "purge", "-i"], input=user_choices, obj=project) assert result.exit_code == 0, result.stderr assert os.path.exists(venv_path) == is_path_exists def test_virtualenv_backend_create(project, mocker, with_pip): backend = backends.VirtualenvBackend(project, None) assert backend.ident mock_call = mocker.patch("subprocess.check_call") location = backend.create(with_pip=with_pip) pip_args = [] if with_pip else ["--no-pip", "--no-setuptools", "--no-wheel"] mock_call.assert_called_once_with( [ sys.executable, "-m", "virtualenv", str(location), "-p", str(backend._resolved_interpreter.executable), *pip_args, ], stdout=ANY, ) def test_venv_backend_create(project, mocker, with_pip): backend = backends.VenvBackend(project, None) assert backend.ident mock_call = mocker.patch("subprocess.check_call") location = backend.create(with_pip=with_pip) pip_args = [] if with_pip else ["--without-pip"] mock_call.assert_called_once_with( [ str(backend._resolved_interpreter.executable), "-m", "venv", str(location), *pip_args, ], stdout=ANY, ) def test_conda_backend_create(project, mocker, with_pip): assert project.python backend = backends.CondaBackend(project, "3.9") assert backend.ident == "3.9" mock_call = mocker.patch("subprocess.check_call") location = backend.create(with_pip=with_pip) pip_args = ["pip"] if with_pip else [] mock_call.assert_called_once_with( [ "conda", "create", "--yes", "--prefix", str(location), "python=3.9", *pip_args, ], stdout=ANY, ) backend = backends.CondaBackend(project, None) python_version = f"{sys.version_info.major}.{sys.version_info.minor}" assert backend.ident.startswith(python_version) location = backend.create() mock_call.assert_called_with( [ "conda", "create", "--yes", "--prefix", str(location), f"python={python_version}", ], stdout=ANY, ) pdm-2.23.1/tests/conftest.py000066400000000000000000000074561477560627500157570ustar00rootroot00000000000000from __future__ import annotations import os import shutil from pathlib import Path from typing import TYPE_CHECKING, Iterable from urllib.parse import unquote, urlparse import pytest from unearth.vcs import Git, vcs_support from pdm.models.auth import keyring from pdm.project import Project from tests import FIXTURES if TYPE_CHECKING: from pdm.pytest import IndexesDefinition os.environ.update(CI="1", PDM_CHECK_UPDATE="0") pytest_plugins = [ "pdm.pytest", ] @pytest.fixture def index() -> dict[str, bytes]: return {} @pytest.fixture(scope="session", autouse=True) def disable_keyring(): keyring.enabled = False @pytest.fixture def pypi_indexes(index) -> IndexesDefinition: return { "http://fixtures.test/": { "/": FIXTURES, }, "https://my.pypi.org/": ( { "/simple": FIXTURES / "index", "/json": FIXTURES / "json", }, index, True, ), } def pytest_runtest_setup(item): if "uv" in item.keywords and not shutil.which("uv"): pytest.skip("uv command not found") class MockGit(Git): def fetch_new(self, location, url, rev, args): path = os.path.splitext(os.path.basename(unquote(urlparse(str(url)).path)))[0] mocked_path = FIXTURES / "projects" / path shutil.copytree(mocked_path, location) def get_revision(self, location: Path) -> str: return "1234567890abcdef" def is_immutable_revision(self, location, link) -> bool: rev = self.get_url_and_rev_options(link)[1] return rev == "1234567890abcdef" @pytest.fixture def repository_pypi_json() -> Path: return FIXTURES / "pypi.json" @pytest.fixture(scope="session") def build_env_wheels() -> Iterable[Path]: return [ FIXTURES / "artifacts" / wheel_name for wheel_name in ( "pdm_pep517-1.0.0-py3-none-any.whl", "poetry_core-1.3.2-py3-none-any.whl", "setuptools-68.0.0-py3-none-any.whl", "wheel-0.37.1-py2.py3-none-any.whl", "flit_core-3.6.0-py3-none-any.whl", "pdm_backend-2.1.4-py3-none-any.whl", "importlib_metadata-4.8.3-py3-none-any.whl", "zipp-3.7.0-py3-none-any.whl", "typing_extensions-4.4.0-py3-none-any.whl", ) ] @pytest.fixture def local_finder_artifacts() -> Path: return FIXTURES / "artifacts" def copytree(src: Path, dst: Path) -> None: if not dst.exists(): dst.mkdir(parents=True) for subpath in src.iterdir(): if subpath.is_dir(): copytree(subpath, dst / subpath.name) else: shutil.copy2(subpath, dst) @pytest.fixture() def fixture_project(project_no_init: Project, request: pytest.FixtureRequest, local_finder_artifacts: Path): """Initialize a project from a fixture project""" def func(project_name): source = FIXTURES / "projects" / project_name copytree(source, project_no_init.root) project_no_init.pyproject.reload() if "local_finder" in request.fixturenames: project_no_init.pyproject.settings["source"] = [ { "type": "find_links", "verify_ssl": False, "url": local_finder_artifacts.as_uri(), "name": "pypi", } ] project_no_init.pyproject.write() project_no_init.environment = None return project_no_init return func @pytest.fixture() def vcs(monkeypatch): monkeypatch.setattr(vcs_support, "_registry", {"git": MockGit}) return @pytest.fixture(params=[False, True]) def is_editable(request): return request.param @pytest.fixture(params=[False, True]) def dev_option(request) -> Iterable[str]: return ("--dev",) if request.param else () pdm-2.23.1/tests/fixtures/000077500000000000000000000000001477560627500154155ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/Pipfile000066400000000000000000000003531477560627500167310ustar00rootroot00000000000000[[source]] url = "https://pypi.python.org/simple" verify_ssl = true name = "pypi" [packages] requests = "*" pywinusb = {version = "*", sys_platform = "== 'win32'"} [pipenv] allow_prereleases = true [requires] python_version = "3.6" pdm-2.23.1/tests/fixtures/__init__.py000066400000000000000000000000001477560627500175140ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/artifacts/000077500000000000000000000000001477560627500173755ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/artifacts/PyFunctional-1.4.3-py3-none-any.whl000066400000000000000000001405311477560627500254240ustar00rootroot00000000000000PK!bfunctional/__init__.pyuOK@ aupW੠ڴ vIM+x~//os w $ rGEȜiH:ӎ06ar2 Q0 ЄDKE5' P. }T ¯8$0[ y3WmNIrW&?#ؘXh?5oIRʮԀ34} y#EQrY/ϺPK!1݊ functional/execution.pyVM0W%EhFa,UtR lQۉ$MEKRϼ|<e -S@*"5%lR+֔yB¹u&!?p2(XՒh3]֘P慿unw֯# Q;=f5TQymk觔I']z9MJ$ua~,I{aWH/%~PK!]cU%functional/io.pyZmo6_Ah_l1l]f^Wlm$X΃@K'@Q˰;ꕒlC2X!y'sw$đTl@dm]M`~{MV9>Ovi> |F?QDz,y+$Ly*~}_K&E!Z>Rb.q=>iqɄ]y !T٨+>h%0(J,[Q..|$$*фݭDM4/IoFkZ!Ϧf*b G1{ V"F%0`@NvR_x@K4KrbʨN UG=o&i)DZ6j4i64luQ 9 #AST @"Hllɘ\9{w|*H&I_6=I5<曗 O'cl@1KYTʂafb]8y4OӰV:/w6%v:?Cv׿BD4zs^R;m ָk]yQ<=Gr?FW^6aYwGXLҞ ξW/>v}u1|6s/_7/0=󧴗17D\Jy%K()0Gah374k{d6yƴWC !,rHB/aYyQjˮ$ɿH奆e-i>{#\܈Z2}Q52p&,$F`UnonNOQi wΘ/o.83 @o!`^,{`24go\YZ1{ɱIHaNDcYQV>}-q舺As%F 1#VQ,c`#5a4@iuOTb9R(: )V-3eil0B*ĿyiUkHF<Yc;zbݏZR&:fNW'f{79Գ6Xc?fv n3:{?L B㎢=4jr{Zş8ب ьчYv:I2?86~nxPV_PK!l'&-functional/pipeline.py}kFw '.)wazȳ,8z:h؄4o>vSI*++_U5 ZhnD"Z6Ѣ8ͫuղ(q,Γ(^?emN.bQuQFzSuf(NG2EVH 1nxSM\)RVQ__4G Y\ ]~dN];wUz<:5*  [( 2Uf(e|z)uZo̓<^nByb":wGFiZϿO66*% 1~~UM+WKZTT=ZdqUE/ů[/İ`4z bb=Q/En#P*Uv.VѦ,b! [ADե@4f QeB#I_4O|Xl9M ~ cK)J ڬQV*1wLUÿWk&8ҪLPh&*3C67i5gLt/d*=Y)djUSM iZ5ZH&+4jL+`D ʦKh >< `!.:50`.+D}SOX:`oa-k0~s?zF@W0JQoꜢhONh&QDpkJ1whD\5cn`Z@ _8:\wvfUuj*[*E` ^ʎ۶'yp,Q`l rB{e-(H9[wt Rmȇp}9 铬e)—`W㳫h0&ɕEVu?:C9qyOZsNaˡKpG.a*z%լJjz+"a[Z#ԹS 9V#z#jm3G uEnhPNz5!Fͬl)8HڝEX}Сc/gQp)V 01TDK~[ (x D醙 aRvQN~&ۚCVYfʚv՛2 P˅zSW_T6?Ҙ'IQC 9]ņiCp\3S99Qq4FF&C"2Q h&2I/Vc鵐_Z"Z$zӾ1a`* jk:y-"Bb9pjT2X('BD "5 We 90AJdQ[Tw#n^m>ݵP-F# Ȧ0U|Ԭhh(59[ol'u^f"QQE ޤqd<0i%h )V j8FtSY/qno|g|a-euY QHCq(beЮD|\ֽh@(Pda6nV5` +'}+(:˴XE^w +|<<Gj4f *gO*8- M :^ոje+b01? Svynj }}QskRǯltyj[G` C` Dʓ#bz1兹JPW>A,L^4S(bmX6ߙ~g*DzS j @C,0{\9P9Pݰ rgGoh#oR4K}o)'5+w UGW7c7n8aw,_716BC7+fX~W!GAs=bQUdq0iavZH>/zECe&HE\֢J\{?y}7mQ0[4;򛸢)jn"]bAEzB wۢL"kKl\7m!b8jykb@{&&C'4zGP=X;<Gqgy&o:ν*Wj~EƔ&.SZBq@Z!Fΰ˔ reI #2p*|%eHٟT熂s=GR{'իˀrfnAVWO>FL =a#92Y=Xdvgl3ju6"yqC]Lǂ] bԛc R0v).wڋ#d $]et(Y.D.CIEM0,IqD|p|b.#+r(^  Xm`QiۛK;eyO[ mhLH؈, '7#o|bgXc\ j 厗͠):FKӗjy iM;t`Xb\ĉ4`$JI$,,6]J˅{CYt6RMk'hk u ?-N>F[a_٤v~8އQ{2sTfk$ssnr!rm}Oa"Cy3P^^=ZY7eL]v*Rvt1V LC|8Ny͈.@l,p}B="&EV([l:AV L%;eթF?OI7E$ݥB97U֔"&g#ѱs- _NOi1 :ڱ]RÆM55UL? l0`9-bu27Wۗf8D87q'-Z]印zݞ G6M&8Rl(4i@+Hn2H"TFwN dnE*ʠ&Vz 7oyn-rP{VsMZ﬑f8>G'w_/;+S" AnLdv"LsRr%x1_RQ;⋎[Pƴo>'%UۀgV>iOG>\v{uSKvjSӴhBp l-XQiL ju/X ;$OTVc0бa,WpS8~IV6KjZn,Jv &@mu1:m?p@l- ko$2mFDFZm؜HKq cGJm.c-&iE¶/ewܔvˏ3$A*?n#؈G; SWwUC]c}bKd~[ޢ N9% ;̇}&A}'q|\U"9"c{h3Ch&fC$áQVC.@ B}dtmS|tu#4`6]BoӁ} L܇톼%Z>eY4H +M[VL`l-Oن>SHM)n_IGμlQqgSj0Lc8\<@j3yjm1ezU a $nQROF4~55"1NʵHPv9\l˒*8|y;QYxjEdIڊbTTIy/Ԝ[kt#5JrS9ЦfSw>U=_f;8pFt%p#u E\Ő!w&xU/, 4u0J͵LZZ]!\IXT6GpC12:707J o3?.vd.-LtIMfCt]OO|g] / 2D*s~e&GRr`q}t0Qg0PwG}y g<qSK2GSR::<ƗdZ?hC`  'Xk$6_7~^CGh<h'c QjIu@T_T>.ѕI8jlbXW7;G73 #ԱqdoY߰A/ } BAc(`ҏj'+#Ym[yD=g/ `&/~f/ծ&oks jev[x d 6jżgu^]Bϱ9]h0;Kr;} P:f4zf/ʆkc]TG-Μu=rx( HrMoZ 7 wUov w4g"q2 ݩa1#G[Zg @yPI"`}uͧ ܨo0%pW-jAYjy|?Sfk a&`eM3^';4wkޘ'~hPWMpY|tPi7"(pȁlWȴW2eHԘ㚩۸/[u=a<8wT)m+^-3 b RT),:J`TNo%*Ѓ>j^Cf?$IP-I6|ؓ2ֻM,m!JBk闸\41=8-=#~⇛܇J}^c;EL?Kˣň/cGqU_nĈ%Ǘ#5hA"{'H* %Y%_CU-k#na)&݈ ft #nr a;zM`ѣC% ;k744XРu$@|4$ᣋSl(Agt(H[_f`#)Os4,j<;0xJ#e s8HS9h`cnB#}p C hʴ*6/%j"bz K1D JW 9]\Jh@MRvۊۍܼM7]4{øf*ӫԣ֐)lN7Y Y}lH}^!2G}i {.[P2`AҮNZ7i^p-f[7\9 kLY[[sa)N~\MF̜3f=M: $5 R֖T$JJ";LkB\k˓Em.o@COU-FiC9B2|0>sB-m|WUY<.x7+S?w?QMJ9uW3O0;KBp,y ASio) 6:r4x:V&Pl*KFPעs>G~,jI<±Ǵ6auaQ7Uš8 Bd}~41u`&FU!snщ+wVLbɘC y+IiQ/6_XkOq\0zD<^Z.~z͖ˎϱ-LΪ6%)nR&WϋC+Kg5 "Z`k і$c\6X[bYՇޑ627Ey.u=D4Uѐp(8^o~Ptk"kLfne ua =~,eL\v¦ 4pHhGXm-iκ0#3g*!X :Lc!LǙV@dfqEUx*wc Xaҭ5 B#:͚@zѻQ]bj?C1=viTi"aK4K3O@}97x;) YRВ ΓDG~ }3xByUa> 65E.xj߽f0*H]|j4errCж8fȞG3*S#Oe!FZWTZ`()T/EY)l"&_OvdJ{#kNXd'^V,l$|Lh&,"F88: H$MzVq$ߜ[orGąJ4Morj8Y%ٞ_EO[&k>yx-@D)nRL!r0V10e,fPşϞ|N!Re ȺB`}:QߙXVea~Fjth[ 2S{G_^4+ /؀ΜWS(1VeB#%Ҧ-TUp3&|eĕ6vW~)zSce 7LD?Ǔc>˩BE 7LjشkgV0m=姽lsޖP>;;5V"FOd ٠t3axYzoqAs(n8Fpu!PEֵNvwk$IRzSM?:עg1*w'C=ywSqzs5m5Ch_5IZOj@>eOUSVqOY-k_2]jdP5\+ϼ8%}W}Ԕ/be}Y}7PֲF*#fxUl7v`נ{V3ߞ 2Sk ~ZJKXIx?Ë>)wԗq^G͍qPXFX_7wl;v9Y/V`hex+Lyz?6ՔK3&:4ʷ'}~^4ɚq9ȭ0L\&8rFti/L/;rUۜ l Qlϟ#bVw'\vw 4!/u<.9XQ(C}h\vFt'jX鸯8W3eTHRY 9)l}So$' ~#[0C%ZڼȠg}-sNaGd.8mEZ[ऌ1tT>'ej*>0qxZSqH0{/Q#%9/Og3}>QFe;gJ.%10:z'_9y] K1qi4vJ4gM~ޢ5rCLq @Ο/xP>I+)U[sꚎ+/U*,++xsaAws>#ۀQj0ZUZm/O.H-k8L:,V5C;m%C _ Ԧ̄"%k*2g{?PK!}^\ Q+functional/streams.pyێ۸_A,-4L ]lŢMa6m3%f =7QOI@L"?PD7o!T⃶ M +gk0xx-hdՅⰼ+:|'xQxRw*hUj41U0b;VS@@R9 .VWf c*],EE$f*Y-EQ/Y+d%$R#{^d-0$j@mj7azf=FУ߂k(B* nx$(D=,=d[*cɪmJJ.by {9 WV9ҮRS+T#2B[fl9 f^ŌBEd=pِ7X1Udb'ͻ[|;!@ +ޡaiuBÖ r-nhM0~ВjmN~<Ф{ Zw ڶH,G!fkפM[iA'D[EKվU?6##neubeH/2ŒlS:K "ω)t P 0 ~wfVPa=ULJS$3H5:cr+3Ts pQbN ˱ (d@l Ino|l Ϗ0Á49:iCmD3$Vcb]*9e20+W_wK9駟IJFZR3뜖Vmz%ݡ>2.FVA[X+|fr2rco+'+@;5΀wUM) 0)kp )ļẊ<8`[T_z*}Q9J'9S&D/Nכ@ހ/djVQkVR>`0@}oCh@N0h䠀3tٴ3R ,։W/L2G0 -͉mũvNw2;`2upȡ4-Jq̄)3})y#yߥpd:"MHzTH1hyf9]֠2KN4v@yE]+hxQ5t 0Wۃ5wT1I4L`Aim}iI;S:csO8L N2A/ۖMƆZޡ_N==<~=c (>_yۉ`ȷ0Vh(˫eJt%TYk4%>ptjHN,܆lx`Z+*dig.(^+_LդIG(Qo~`&`V~2N}*IvhF%=h*%]"4H̤h`W_'Iyq q=儣Dysч P'YA=1#Aw~-: S^(S~YUb#?n ~ǎ3F0'xVWs?2`U/GF5d+T:qeI0Ni0aG`¯'MX2F`4u2;JSk=HdKGaanfiHMz5݀ A&"43÷\@ C,b S?ni$A ":!OLhMY~^+o#C(19)WI`D Ywa(~-̪L| %[y\%@p&9pft83~|-%\Ӻtn?i|v[heW0vE֗7/p#?=q藡Η,e<.INAKiNRbu=g1Lez^em# *v]܀};Bh:14m+3?MXj9pqD׷#ݎ t34 m a^+8V3#(>sOߓN]R~H>U1P/ɕbޱf9o^X췚? $>i &N] pXcވ^aȰǷЧ?gXY~'`ZrMC#ĝ݄%Hyz"w%2]"iڞou%S]x/giE/ BT7%`O^ :u륩|ׁ[уZ˙R_qѺ"?vxer4Zc43XΉܮ~ʋ(]%/_o*n*`AG~ܜ &F62@1 !סN\}{ĿMʉzUUi!k}0rE6^'d_PK!functional/test/__init__.pyPK!B2Tfunctional/test/data/test.csv3111JIIIPK!Aȩ74!functional/test/data/test.csv.bz2sʰ4tT 4)& 3kjU[93W PK![/, functional/test/data/test.csv.gz<\Z\\\`|5Ç^WZ_Oϵ@ ,QPK!OFM #functional/test/data/test.jsonl6Q0Q0VJTRr4PL)HrPK!r'HC#functional/test/data/test.jsonl.bz2CBZh91AY&SY2[P8 8 1L( 0$ SUKH RPK!n#@="functional/test/data/test.jsonl.gze\Z\Umv%RAUa^!Nj0 є^/PK!H85a`"functional/test/data/test.jsonl.xzk^7&EF10(1(286 ͔23DFPK!Hɱ$functional/test/data/test_sqlite3.db ,IUH/M,Q0f`a`ddpPP````b&`b4>!ʠ(Q0 ,EEKrRKS@91U!U$WPZQ ʛ,0c~dQ0 :faff .I-H`ffeJK`9CsPK!!functional/test/data/tmp/.gitkeepPK!ݶD1"functional/test/test_functional.py=ko8+xX\{1,9igI.P؝;'N%WۤE9$%>ptbv!E4/H6ݟnӌ}Q䐧uMZerw-YYFuZwdG7a$iYEbsX'#~2|Mh3V1I3GL3Boko,"uն(w% +U݌_:!.3ܒ`zrr[OMdbU0l&ӋnUi~\P@]}M^", oIVQ&:KgaÉl9\KRQ!_vڒ?L}4fYݞ"ʌT&'ia6fM NBL%H]/L;ԸL, j7Cf8uݗDT&uZ]׵]]M\[@1Y31 ud=(;&&A_,]oޓ"}*}x췺rjn]D@%77rBaOi0܌,ƲZ% o$r{An3T[89_U$/jCDym,r^}9 DFې:v !f̶Ѕ[ m1VqOWu!Y< v|fxTo "RfM01JG#_+Zͫ#{ .ZN懲ٜS<# ~9GĊLqoh͂Ü匜׬V֞(EDt0 PT$৺> >'p+*Ge+WN&c֠ c0%aa=Gu6Q,zܹ׸؝!A%$'iNVv,qHMVuGC,ޅ>)W;KR"2c~H7e8N Iۦe3g>Nx#>L bKIk SkߋAגqSB.8_I{~ԥzJMz0K|'4$(O-Oj ˤ3G"JbMXn jJ&%} J@Pq5HX.δ3U0u;0OZL;<{9\}.8Nh[MY؅VgZdpav *pEfpӐfl:5a".`"4#"DgIaoRޜf3tFWfzٌH C6+mșH1[>){{|_}NL6m"mm]CH )Oc8\6h HZI3| }Yk391k+5zRRjnt-:SdxΆz,=ݎe>J#减0Nj;BBR3.z~11`IӞ3rǕӕ@d2Y}.ŏ3XI)AL\lf5 Şn_:׽H\7|k,J#y?a_@c.ǣ1l-w~O'R?7}1U8 0r&)`'3K>ցaͳֵR/Bfs4 E1hI$; BR7ZR\C&G4]~g@%SsΪ{]9NbMՌg,e:fΎvUqYU wد@6oق,3g ?SYĂe |p=F㨺=P" F3(8+N*53b~ZCmnF^`ߤ՚^ыMMJKK(t^1>ki=Iv~{Р:a,ٿ4 %V0hyMƜ6͝657\^esSߕ( sZHԵ礆<8"j)9<P)jg vN1N]/.Zo2ov ޔaN..)akS٧GΝxkd2tTVl!޾g\1{wW(n-Wv& O՞O b>]xyGT6B5"a.B04 m[7Zяk{ҷn=JM/9Ooarf\F~Q>*Gb~\/3YZVO1Ӭ0ei[Te&oFKSŒtHaèRG8p$Wgz|m4ͥכn_ۋmت1̊|,Qkќ?ҲZl160Y`b@;Nmt10 h#~], s*"E҅9t~39VfwG1 N|ޑ M뾦3*.[Q > tHG3qw*Vhnt#7TVds5Viy7@\^E=DHsd x>5LDxݡŮ1̏ޔ'\E.ϙ$.|HKۊԻ͵-^]S0u?SG`Yc}҅69H َD.01NF?Fl UB397X Aw}GO̰'{iptuDVuT! YٴGXTCH/:?:'νӛ?FeLuц_)e;1ku'ΏJB`yrFyO?+D{u(i<-; O^D|plO 6ZW;6J`}*ےRi+o!4\?ՇXQpr§9!Kڌ @3 (0$gl 3<֞\oȢڲ%ʡ䫜ľH7\z"Ya7`a> gp?.I,6^t|B~W9 Q?y U"?B#kr;B-jX&+& muUG >UK;W~ :p> 7<.=|Ϝ/sό.ف-VCĮCߍdr@PwZh~}+˘Þ[|[$،&1OMk@ U%,`X>N#VIJN7nW}8Qwb35);2LJ4BA*ׄ X R{ ey8z@?s4:"Bi_1|x߫\V C[!z5L lpK `|^?rϦpApm{d+}^BAEwx1Dm wN Bs5YV0v)WW9 p &d+"0KЌTpۯGjw_f| G97 z6qF^D,9'ﹸh d PJ;} )?ڿrܜ꜌WgE~okCO錤xΑS}4iN5 8%\bK R*\hx䗡g9S~Z}'錠2gI+b%➎natPɲA-߱p ]BB'CдDۊ}^@܎"Ofmc>;?@Xy3 |_|{b7ó'IãŁwf//$6'(Y|^wz-;%ᢌI^S'f!kUJ M KN%aa.vign;gU/_z*~?=E`?'%|K~h ^aX?Zd7#5 ݸf* DaM̭&NNMR^0H3+awrC2j>%#㾮e͜`4 R #}M$:Bl]$ey]9L -(!: oQl-ʂ )AJ Pm@%dW3e0 S<؏\ՄFl:XHKoMe*R{tz0Tߟ+V##9쬰\. 1+ZٺMOnn9sb3LS^St,s'xQd^s/A -'PK!+4functional/test/test_io.pyU]k0}yjӧ1e@>Qzi%S?u( s=mCBHJN=/"GYSM',Q3R5e_;n3HEXe>s I2쮂")YN/~mA[x@brSUOW/ щ#n߉c~.]N/8: <V"B129ƳD|N|r> oU`qD-eWO~" dZX 1=,F{l7ΨN?5 cse.ABOUe @OfQB{'yY BW x6}u55MȺz؂BjOޓ4_:,~L,W/9vCQ z/b M3 k,Z_Q55w\F]qgJ^xk FQd56h22ՠM\40gACm~B"S Bw7M~Փ}P#amn6GOux[ڼ>+DKeVʽG^RNb*C˦D;(wT߫xg}2at%dpp~AJb_Eܤ }ƼJPC}EkX9-s%Y[[(&faz r;IH%GdJNҞ"8ZJ@*kw\; "5 B qn'Ö+.=j!=ȑHH~Ҫ}+{a%GP ʁQryRbΜe+Pu}7twėXjgeppuy! c \2Ƈ1}CUAZ^*}~;s?-E 㠂1 jRg=ݜdӵYI~\ѵY9~B U߆$έ)n9V>i3O-Br)I$Ppb1iLWO:u%ļG)Y\V,YU|:ڂ$ t ] zr<3{'K9nI~T#`HFvCloIEF7 :l~G[函SaM5V{IDb3k>䛀z\ @a4 }Lhtͻș^Z%r'=Ö%ssfDǎ~37>7htLChE 0Mguxcg9s 7e/{16m|e Pi0Cu ?|Y U$v<$d税3ludJLrOs{$/O/ɯ7/nLLy=m[5%pVdy))gmz9c/ʢ1 PSm N#.Uۛ8`oGHƋeTxw/2OrkŠ=:6#b9 4ZK@zNz tFݏZ0&c]J ȟZpGo O!M WD\jrF +Q5ikZ's)NpwVn$m4k]B|j}>IyUW3TB hq2XxtX|TaByԏ6/8c-wxeչ4W]omzQJmoYٕ[uwQ]zv/\~4}DyG[u,,Tn̠=bӊ"zr rQ5wZpJC՝jtǁ86-oݤ+nRb;Nu7U)QmۆſH4.U6M$׊#*+o|g=V3|Ӯ@vQ*ZW,WCqe;ðoMΖjTn:7nU:>B)dū\z6i#o|3">ySsvu|5 {Ja[__ZUܲgtCWILu/yq5JW{ =>MaYO9ҒBfUu]zv5ݓVwKI@mQ/Z+ vɥNb\Ef"%ADus"gfc\8c92:If/ak(FV9Pbn~+ŢC.+9]{@{8۱Cf'9#mG؛WڢBgkyK"ɘcDsIU{I*AYlg|#rO+&G#gGT~h d3&ciD/5UH.<7M-cد oLcd$v? K k7 0 󚷨C[vD[y5_u|U&RXh jFt SL(fQza+o*]0(9^%=(6voFӁF#V#!=z1*5̤MRLjUVHn.*EmxX '+-p\e/5:&K `MqƧ $KLIu@1}:=uK(;yt"/n\u4Qij3Ǝozeaz~GA^r{$> j8RoPK!x0>tifunctional/test/test_util.pyUM0W .&N- BoMO!ŖbeKHv;dӲ{4ͳ*)[3c6kuR r9 n&-)s#-lm I1BHQ2%|b (\Lg6qd(8{Zrf2 H?O><碒PWoibNK& 1%9(/՚gi] Zg#28N:Ge#NMA`fUƕ_n"n9E"~65`'SJ0 ϸZM=e {B8r:3T_@Wd\s_y$X {Ӟ{g?(Iݚ6~MEԝI>uΤ'ɵZQεnsǃu-fqPK!Qi Dfunctional/transformations.py[_o^FW!*>=$)`M)R%Wq|A'V5`ܝ͎ܶmu!V}JW[Aovl㽨ne[UEsܷMy(MqZ]v2ow^lECn =jT/h4ɧ=.,I̫%ʋ^ _O%uӊoE;m^w&A$2gu +zdVszkKRþ YH=[̌ױf7N}^s&I߽3 $hjoI ;_f#_8@oOD@.0"8M찲WL6K *rwh-DϨ 72g}G C/7N{ xFRkf>+Cɨݳ?Yτv3}{}>[̚p>_X23\V4[dE,}݇W+:FёP|CHT(?@F1R4qQ1ӣgNED {:`oj%7Ëy-O؆P9#91?Pv2V*y `-mK+*Bffp4%d7ڼQ1gyc YR*&9*J&{IJS,+'D]TCw,z:E`-ǂ}$P޹@nvOJgؙiXF)E'A?!$ K:1U86tQ;}-@n:ǟ :teAUi$_a??  mߴt\l>@~}B ^$ѝۚ]7nܨsl}Ip;zcnWvÑt(O=A_49؊,od'Q'@KȞgɟ]=RgC F2seQ9UL;ޡձoSZ ~]y*,Ԉݾ" K{~ J*&;v*0꺳BSۊ/kZ,Vl7|I FU1CjʑX'PktSؑt mi3I Ow 8NIgH;O~?LH:wy]&̘'lۍo9)~.:H^ /#MT .t1zeK=Ƨء沸G~UR5lw+Ɍk_eFb^`}Ik} OYNzfKul62tұỹ!V(D k2SE]6A?LjEJ(ZYEuTn/'[޲ jIjlex%JP>8 ᦛpzpD#52`H|K{+jSQ`A 6 7i;  ;T{E(O+êiDed酴K-7V-ێ]em)QlV;.,)ԍD wqZ9Z^!PƒޗZ6 i$o?:Z_{ǻ7W]]|*?}`9@-_9(J#Evϛ|` l';ږ`Z F~)|2{#'|S<)[O y2Zjx!ԀVŒ[c1 &8whUiiû"QLU ĶjF< ՙ< e[QDz~pZg#K}K[`@k:f4[)Z3HjWIz~(֑d18d`Mvp-4zeYRY6nDRξ[S~npβ(Gqf ,?`'};o,J`Ȅܢ!wfCkiWERs浍[P\b^H.ÁXM-vT/ d"<:ucWNPC# Ѽ?%yk#5M,;[و8ϵމe9ӻ~lj4 N5i~9'ĬtLR`lrpHDO+UG">3|36+m[G u|Hv9w#n롑B3A7K%C>yH 4(( K~J1>v]J]۬+5$y_Md(#?VۍP_;}da1eortnQA)v [",ѠeB Wwy才1"f EEdI9Lsnz\2Zy426g;&$C=m:gM*ًɓW (\V-id\h(0ip.04< ^#]M̊I"yT\MFH:d+v$峄1?ƅ9v: <3`ǀk˔iqה @¤/EjW}t6H4t0UZ@A`p#,t9L0%%} v4ev^o: /"x,_Ln 8M}+ӁS5LFϺSس] +g@%.4M5ivuW otiyƒvIy\F,J⒠Lwr1k٩ԫft|GHeb!dp#BXճH|-]St黔-2;GgFoD@ai@3˳L1 %3>Q*Fߟ{X^o<q&dC>O\ڏV!rᄍr*]h3fQ)~[VB6>]# >GJ 7Ji^Oa:n͗an4-5E27ct?ۛ&đٰoD@Z=Iſ23/<ݏfCGh!_ PK!7zi;(pyfunctional-1.4.3.dist-info/LICENSE.txt]RKo0 W9=Nn4ːf9:kpt~){Ѧr!X➱ԟ_Fw"5oPv}aɅ.@gGXѶ Fkhv vPC\ 'c0-PW#Nvu$mmcq?^b qt a$Zܻ1@`:t@:8ζӾwKu"65<ɏl3Dp{n!g 4" Թtщ 0Ry6:4~}/dCQΘz(*p~)tuC^H!]ُ36%RKZYf"^$f6pB@-;!,Ԣ@i&e.dLO{OY7Fޠl-t’?\]–T8\nrRU3-d"֢0Ȋ=X@yNToP&}rJ@e1W*4\?yK!f4vUەqFl0]j󶺕HkYQ K 8qC W+ E .#To*9bUL_PK!HlPS"pyfunctional-1.4.3.dist-info/WHEEL HM K-*ϳR03rOK-J,/R(O-)ŃKt=uJRs2JJSBӁ*uRu*PK!H i] _%pyfunctional-1.4.3.dist-info/METADATA\{۶o$W}q[7u;q\&{}%J$d)!m VZ9Ɋ0 慙!eGet.ґ: 7JTW( qxWUTlFm4ZͳB &](rĤڪ)jL*/EV|A8#,܎TMY:|&2:|y`tj1۳ozΊ؎Z3 =C;x^ˬ:.2. @"J%ado1zǨmw8ggOãEYk~u+꼌ʪH=Qm DwƝNcUlt:4^u:RQ7w&vu.*`}.c~a2-Jv tlz~~jOG|##Vy "qzB1,73j9:*t G/"*-߂m`pmbp˛F*Jۿ2$Qx>lQGڏãt9P={a*JGBG=+ͪzZ̆mMfPZvyfMW IJ#QӍ,5JnȌUkVP}!$Q0kHJDRnU6WeT*SY8#Cu2Tp$ZMH݌ԍzNaamt~6LMZs R)KV`R | h}]Lj2GoB?O ѯ!*:0QsC| XFEAJ Vi"mܪABs$ Q$"8* `}$RrCu|t{y1s$8lI ĠȦ-6b%ACFPT"[sv<aRs,J++S~kfEfgo2]k@t&?YjKPyhZ^:&BAFz\tbQ## {T> : bbzp  jbl!ki89я1+#r M [6+EI6@,AYA 7t RS8R(4'4G^c" Y`24Q@PAY .l<`E&p0MNv =\|,-gP\Q])мYF;oy /Ѻl ժ?D@fQ.VtdzyYx|7vH0Q[szzM}zM8tJ;@':ȩx`Y7,lls%CvMC>Ȳ+<$ekt. .;#Kha~sf^hs='J¤7# כf|7{% ;#_eVd|ªnqr{@SYkQiF 3\,ij_;hA~m>V4.{nD6( CڿS;lyu7m~y~VChwu8zN3c 6k"rوɠn#I s2IT$aXجvf*hc.p|T΍0yP3L% R۠ӉqbEl;ɒjML<:KKG=K]tE/pܸ5Wc\^F3G+kA@bj(~ӻ3 ~5N!Η{o^;;\BFn+WGӜmK-4y3 h8+^{⥺xB>N3go.!*w=dvggxU*]l H T9~yy>h;{s]~~xP}1؅˔NŁIƁE)^!kXLAFGl ka=Ap .zB"vAђ{fgO0y6HZ>0l=p3XZ~qtM ΖN9$RT7I"+( p}"\dsY~P}@Μw0SOP:~ ).9-xPԅZϢ[njEө@]D%6vWT*U&zWA؟6Ԙ!擫M_ʡ1 ?Aۆ#)QUf1f9<]Dn|ɃS1D.Pr.Ӓ]2MtSlRlZhbEζL@V%)vvP}IۂϼIVp^(mu護d9')p5cns)=RBv4<9Zp&ǣK.ShHMGI!4;soQܭ%yZh\&}2kq fRD }D}8OQlvw64 .U` +Ɨ*R[D:x>h,k]&hLӄOئN.mؿ*; eT? [KҽYѝb! *- w\2H'|5{Fvm+5軝XUd\y8wQXHl ,yռjhLv2u֢FkzZ &+A<2$ )lmsg&-K0 0#Ĉ\] dvma7KXn sB۠9SB٬*X#J&;;܆WTa}Si<I]t0Tml݂|z&hozK}mO@<ʂ' /gkg7+XƿtJ5Sع^V%uz߲2R]OkLlk") ?[I:逦;zD?܏e` }(x1LNZN5ΞmMal{A!Oŗ~'&5[ q*&߉IE־ !cpfp88sM | "L-qr SIc}'|vt,zÝwSX&o#ޅ""W.Q[GgtFqvܥJMY|w1C]4솇ˍRL^bqGso89~ܼ/e&o,c<^:܁($K'LjP__NNW[f:'7;:pƃ*%'${dw0B|h N;NCQXĘt48kn9 |듋|Wv{f*sͮ޳<<-e^0["e>"60, vÉdJ1>QeI& e~]%ՊY0fL(4 pkQ[y=<ۓ(C'"=w)ݳ-j)qޫ /*J>"d+%\ؽ0)ۧe]a mZtp0ǀq1!倷6 3k4ԅ3huSi"_wLi| 6Vx E %`[W v޵Y#e0o9x 8+KPIHZ3u>ڞcl#'XI@LZr^87cK\Ft>w,L>5Em&--CtP=Zyt_5;Ь+Z'4JpO-j:k%> wHXt+J}`eU8:.t޸'+ljPrlt8KuneVo0*t@6eWln >d!qӕsr8î|Y, Ģ-TmHj>V2D*ulN@ͅfH,#"! onMaɼ*8^C,U04;?r3lcIԒN_o%&k]ɋ Цc Y0sr)E>cp@GWZ's%awF4_wG!w2-|A_v|ҿqoJ͠1znc/қ"is~T8v{5jϪ = Hƭkx=OZO[?{DيQ+WPٕ rB>͕oNKi`TN쥔C/QUuR:aDE7a0iե,q$6i <ɝ\Pl'TGECuV96t"}lHgcVF8ܟ) bQ6,vi"? SȡҎŰ6AT&0KDI+e>^ CaU5U+Wލjz&Nz+I'S=$er|>tmCiR9Ɣv G;Cs=}2 o2J-RH,>e7H-do{aB﷟܅e{.EuҾL.Q! }s+WoO?6 ص.,_ܟ>MnI;-vrwɺˤ[NqX*{G^/ߥ!C; ?6 {ҥgZԲȫdž8Of)-TM1/OχMQ~S-Gt'w"%o$HT'R_Mn{x9i_X߹=42<k_=PRhSj J:or>m]،U Jp#)n-ؙh@7ELNO}P6QcJE汩e&|RYWx+M7ӺR1-h61LE6nmQ݀+ ®igO3r-I LP^gCH_R60;LHE^Fqa~{&ĠSՇ}e:R^,MvvGW PK!HXl #pyfunctional-1.4.3.dist-info/RECORDǖX}= da.B,f ap =dUTNuz1;{D\tmP-f7hDž!{EزQy"7\/Rgn-Ԓ{-?8 #i j.q`5q&yPAǟ{jعȎ\6yn)`@e}/Ǔδ=w "/6 IOD]p/;ɻ t$jG+AE8J=AN%64{3Eٚ{zDPF<{<&A3=qi 6 %y"Ijz&"{ꙛi㍾3}Kj\d1s꩗ݫ"s-_R8.jp;9Ruu6'9P>7l=E-VgP_ d#:sD`p;N):jOh N6$p5G K)|-h3&0vU|m:N[N][0Nq…RC4f л4UDY O95^OP5~&5{QKg O\f` +{7]XZdIbY ǐzoaHC;g-~d|HgSfbtFݡz<o-ŸD^}0F\|UB/qw34rL^&R!U sQ43;ٲr+)U>=vQU,_SW3ghbP}7醻Xx26ufBs9>.`9\rhWMv#ʠi1nx"Y?.!ƨp+I"ԭێ {mpeJ6a/S~Nw4j kD"MBF '3k~+3kՓ&K;DB?6xH/uJ}x [n6ada_ᱨvMuqx)-Ȟ2/*bN _g}/PK!bfunctional/__init__.pyPK!1݊ "functional/execution.pyPK!]cU%functional/io.pyPK!? r functional/lineage.pyPK!l'&-o functional/pipeline.pyPK!}^\ Q+:functional/streams.pyPK!XFfunctional/test/__init__.pyPK!B2TFfunctional/test/data/test.csvPK!Aȩ74!Ffunctional/test/data/test.csv.bz2PK![/, UGfunctional/test/data/test.csv.gzPK!BJH Gfunctional/test/data/test.csv.xzPK!OFM #JHfunctional/test/data/test.jsonlPK!r'HC#Hfunctional/test/data/test.jsonl.bz2PK!n#@="0Ifunctional/test/data/test.jsonl.gzPK!H85a`"Ifunctional/test/data/test.jsonl.xzPK!QJfunctional/test/data/test.txtPK!8 3Q41!Jfunctional/test/data/test.txt.bz2PK!WNf,) Kfunctional/test/data/test.txt.gzPK!XiOL wKfunctional/test/data/test.txt.xzPK! 撼#Lfunctional/test/data/test_dict.jsonPK!$J<;'\Lfunctional/test/data/test_dict.json.bz2PK!_1;8&Lfunctional/test/data/test_dict.json.gzPK!c\PT&\Mfunctional/test/data/test_dict.json.xzPK!A$Mfunctional/test/data/test_header.csvPK!#FNfunctional/test/data/test_list.jsonPK!!?74'Nfunctional/test/data/test_list.json.bz2PK!63&Ofunctional/test/data/test_list.json.gzPK!KH&Ofunctional/test/data/test_list.json.xzPK!Hɱ$Pfunctional/test/data/test_sqlite3.dbPK!!Qfunctional/test/data/tmp/.gitkeepPK!ݶD1"RQfunctional/test/test_functional.pyPK!+4gfunctional/test/test_io.pyPK!5-b Oifunctional/test/test_streams.pyPK!x0>titfunctional/test/test_util.pyPK!Qi DWwfunctional/transformations.pyPK![礲=Efunctional/util.pyPK!7zi;('pyfunctional-1.4.3.dist-info/LICENSE.txtPK!HlPS"pyfunctional-1.4.3.dist-info/WHEELPK!H i] _%|pyfunctional-1.4.3.dist-info/METADATAPK!HXl #pyfunctional-1.4.3.dist-info/RECORDPK(( ,pdm-2.23.1/tests/fixtures/artifacts/caj2pdf-restructured-0.1.0a6.tar.gz000066400000000000000000000010421477560627500254440ustar00rootroot00000000000000 ccaj2pdf-restructured-0.1.0a6.tarOkA[)J9--X hE/XX@4^!5дE5TLpm||@ ݯOqR,r"}= ɗh2~9=^?BUWZugQE R ﱜ3Xw!xˊ.' QƵp튈u_Ǐ?FUNH\tZSX}IC>^\>,IHf4s]R(qiWԨ7UԐbm?ܪXB$B&9:RvƻsCU]tT YM%{;Lkgrn󋏯'hptY7m pdm-2.23.1/tests/fixtures/artifacts/celery-4.4.2-py2.py3-none-any.whl000066400000000000000000014716501477560627500250210ustar00rootroot00000000000000PKJqPO Icelery/__init__.pyXms۸_?PK;qtw> 1EPߥv'lݘr^T kb ^xFƈ9H0I.*+3aQ>bO4ƿ?sPR˒Y"͔Bm<9߰u1$v ec+՜ռвr Ic=VYb7X=(y(fZbzu3e Z*I,\,[4Q"X2ϴ,acV2 rS*fMUHDVF(^ R-%ږjP.RX ]k17M]|mY)9cIZR;a,badF( o71+`gz sܸlzIri Vr-jbeL=y*PZP%jIsúktjr2܈/HQv_g|k5_ *$Ì\`1J(Y8kJWguqR뚦nKۙt8qxQYzsMT W4Q_|;ݱg=g3:n$a z棣KO?-P҂pX%忘QPU53ڨcsƔ`ڗ¼Z j!Y mBoN0H'pt^SqQ`$b17Ym Q*LTeϫe?^QHcRV˾KeE^,1+# %` S J>Y8=Ң /Qrlcb;UJҐqI]wRWĴg` UF kU}h6{2V9)Ć(L[w!= _ae6Nj(zN}zn#Y5!2GKg;`rw(CuJtJgGEp:q2Q4e'7|[)M.wj4p:o\ngp `#rw2< K:=SXS;w6p(Z𰷏 Kثu؃{ z}=jW׵%/*ax0DѤ$5rC*5$$u*!j7H{8`2hu$ШŬp#,][vf}o|Ngx;XfuЩteL6l~`j[`i)lu '>3<쯶=()r%.abvM:;KP=hC}RyvEFPmoί;A-}ea?FOR -TV'.T>S-dpO7]w0mJ+ky.Nأ,k6[CgXη⎖(vǣgQK:Wm=CY" |ݷ(Ǹ},Bt?qLxӿ8}.r<|aޕlZ پ|y3/p&} Z&40!O@ h5r.s;b;OH} hLӾI<݋i:h,J RsJ\dؕr:vY[t`aa$ޅt5[9"{C ZkJ " [dlR"ٙx]B~W꽹%G,бX(&X6R}9:Cr ͮpVeRPB_u.}rpL47ܟM/Kи8[U$i<:w(yk ~7h*լPKolXO~celery/_state.pyX[o6~ׯ ɛ-di8(ahNȤKRqËLr6 [P81/=0#xlg?Jݻa!P$Y?sMۆa( gh.% aHYVRLe9!uh~)ˠ ϲmjaլ&5W21G;RִỽTЍMkXO^pLTK1%0+NF')uK??YX806q+0uYA4{%{ۭΝ(C$) 4Âd T3SlKƆ, (0ݎR0I:0v) ΅xr4$IɧFn[o3.=g&č<`VG#YY{%_y k~Rְ+WRP+Un;sX/zy\}-֫equ_"Fh6W`Ԉub{k*T]0,S %*LW\ƠI{}5lK u"*Ia'Sk=FQ@8zy蠎x 5b~+SMBzk *yx pЮJ7-ǒ F`Tc6GBk +Hj,X+~\@GE"\`L@dgz ïTϝEAI=@FT Qf=u߼}8ϕ9[z?)NL,J KE3[p1zBFlT3!IjMCѩLw>plk~N'Նj p2GǩW<7AC7- XśuhWVf|nQ ʦA g4u0B^fv"ێ +}VOMzLY[ %+曮BV|aloUli9+X ([_ekܱɽS f ΃q o;f>~"@֨\MLw4yx.4P(8+-ũmGs^6PC3RT#z"Ɂ7Jo5n Ė90f Mn(6.{\&,hqYژEe#ecYNbp`~FUbxBطi;0{bžL?3gH&]ts]=$(v|4[k+WP{Q31 ׺7wS3^yE#Uus"bHeD9W60q pQde_>#7ehh3[q̰3j~\}"1,glXJC)_&y!莑ł{Aִ ,xxZxqo Uwޥ]8Cz8XpbE vԌn3?~&;* PKQbPQLY|_celery/beat.py4NF(vDjd˴,:de{rZA_˦j}w}SZy;k\e+oʳFXM/w]]ruUyKM6}4 ʴnr`Luɍm#3dnZV@kz~VEYY6jm}Semw].m 跬:M_1DntUV}wE&DP.;d*:(V֫߀] !AK3ѯZ6- ؗY] dS\K#@rՠ5m£T+pduŪ$Lhe|"?$Gs5VbE~3t),O 8$j]fm Mݪ.Ձ8Pڨ%Hn/e )0ROM:PQum˪=)AyeW2nF) fRDďL(ճYlgU;B@cgv./V2enON$*|f)fHv߃=iF3(%^$XVm E +q@.OT+uGCaT[R)ȀwϾwg?`υ8_]Q{vB q{=,\nUѥir bq?O%*a$V?x&۵#CX@w8/|5B¨+>evC'~p!!}LTNyԿ1HiՏ0sO- H^t HDHݐXM=0إM_fjl`uW+oXշG+׋DμD[J|g5]?xRlJs)^֕tZ [-.xV2vWu[ *ڄHg}y`bIz^|;R'uO]{h%Gъz9Z̯%e0D V,UT=D B!Z @$f)'-(JoS]ZHQ5&b,VI{[~Oo|ρ4}yA<[ ǔ + nA;tЗ»q/v0#4sE>l2}Rpy/rngIHhHK3qDӓ x|ž0`TZ믅!Jܢܥ[yN!bT%'it>]h8aă 1C`j,/"z 'uK[qwi$?d44Gmga KLI"NvQ$0/K&/K 魳*0u[v v5Ty)F*N WJ3 n!}[,[;]\`=@gmW3 F1avj#0]Ppʱ[ _[…‹Xٻb8CݖR f׍<~+ڎsH_P یt(#Px"J[,@UFqJ^,s.GP+ܪsk](KJuS)5LCV^*T̢%knQTUScVҘj.$&SәR]ᖒX-.`&Wy*. O;"nƤ?LuIh%u# <41sv{/ V .sSh`ƹѮKy0IPeܐ_0v,MmBKȨ/ji_ >a(ldK ï#jGp]Vy{Z*M()k@."Do8RvLfigxqNGq4;ǰD!0S뇏4~Wh8ńRHjI $׃*]coY;6u4,.jirM.~يv>ST)+p[ pP#+}4M[Ҳ0R%@\erY~=iE+efRЂ `٦0WP0wAK c*[bc-']9nyv?(cC,i7ík9-QQz'uR&@cкȁ\C1Kz/?6N qXqGD|hQ1>rр`꟩ۺG*1w !Dd,U d,Gs^9^aܑ2\daMmف-`żƷj7 t63lC҈B NZG{qF+ЏR}7Rd뮧E;AeUNE[cm;ζ]Q ^]湈2->鉕9N;Ӎվ Se\v>STzs[]P⡯,dV\^2szRh?~Sh"G*δ@/5Re%^1d.CI!0q pgmj)n$,)~L&˽!ZkZ˺ 3ׂ,ϵǂw\ohڂw w+5CK`jIʪR[E8j .L-iC1mE|Q'TsYuyFъ,~L;LDnqv (;E@JIOGl0ֲ+f>dBZKGNo3> {Zu's~RU ;;m' pUh?.ӦŮ_VY+-mA*'⪪o7G3\rN:ʮhfʴ0۵76UֈtVA}23p{+2|HB!+_ ~1Ƶ,e>/O xq<{^Mh"6jK v$4UPڳ m߃+dKKM6XZ|f-O!Ivb' Q9c34avSPwv*n%]+ܓHX>gM$cK'L|t~Yw}SA L|C+c Sr$6\{ !鱼 @;:G檟.``Evy؋$SV[.G}?oH}"yoȓc $4U$dY~6wfa01D|=kŇ _ u 7d+{2$Kf|;3|L=g Jc`Im]y#\'JQZvb̀!!}pX#<-a_ Z XAY~G9}zlN|08ӁWNDkk-)BOV33V^؏.|hcޛ|`[@R1a,yGpW^?7R<:_Zǿw2l}i*~v<6"_=uB*(=ʨ<^qymVtdyZpdUP:(Dl\&c(Fp[䯚#8sb4{OO')LN?_b5 5-7?*w־bN(8V|g;Çj.>jmST;0ٿɕKBSƟS8_LEhDu Q 8j2MF GѦ7с#]6]߃XԳ F'H{wšiOzIcwKRvj!ܙY>gODVE]ݜ̾"/*2;fem#;QoD:-ץTPfދ4tmר46k]]R~^*`OEYcfuYŽE]iv(ڵ[ؠu;Ŷ 4u}-JmV鴭S6chr|~]Vr ɦU!b,ĭ,;hw^2+6LX꠪X< AP7{قkE[YlB|dIeUm [ 9kr&GFT|a/>L}ԡ/i84rKQՠ[^xEz7oJ*ZN/,YʲS{)6Zmlڷm}/|j(J+*>,(ِ0e! %Qʵ*c7J}爣Nl~y fBڅH._bIN䵄d#?bVWy~)K:۩{Z_w򠢥|T FE~_7K/o|k@*7 AJFt᭐U."Б!?}>lL-BlFBU5sQqD-U4 :[f;.V:ٿ[Cc>qʔ hWs+"O?wr ܹ&(U@z׵y}l Q6ri2*LXCZ0K Eјby""rxtߍeիxQ@oҨC)3gS F̮yoVW'Mt6X*~"ENbɋ"cN6<F$0Oh#h1IAl jp/Tc RMuV릖c4wkdᥫJe7S/ad8A_M4^ >FQ8KB ).۫8#Zy_g;x!PЙVQ=J]Q*C%dq^˛zr!ol kK\ b|;Yr/|(Y FSo*1 ' @Ƭ3pT 611F^-q5v ✻PZ^8O+elt1#Vcu;DMGq $C]c'Se)DfƁQWTP踔ıx<< 5*"3/lvnLKi1y&1'![j]JHauN'M; ("ghN靗*u!+5XK[}w%{!H+q6MBoaMB7$o{K"z ,3M:MWSX# fсb+mlZtWH"{Sv)*c0G@/K( hy}!cMtӽ߾'vy͏Гs;=44g.<-`<xXˍ?TOAV /ІKppj  tf?ry Y ?|fsYܕ"VCr0Q&L2:1ۻk;H&A6$S!MI]?4qjVE)Y7Ŷ|S27ucX-~z͋OZ:ًp˨ulKp7p(/B7;*,uf4O;',MI@)XRpbp> |4l/!j-T T 5X9KV64nkkBj$(@m.//q~Sjw_hMTP4ṿiv$s'pៀg`f]rB;Y?N65?LE><[wɌn@qr,;/8p9)}oቑG:3=nOx>8!8S2_=gPKQbP$ύ4celery/canvas.py}ksGw^36@(uײΌ woa DWeWeeedzQ'٦;{rO>yZ_~Ϻ}]g߻7gmY˶ܻzf*tYwQevԗ^LfrY6YV٪UͲQlmr:U@i[/7]9GfUpĿ_z az,]UZ()zk.7?9۬VeVTlf./dvec ^ח񦫖 H-)V`y\Ɲӿ\L 7,~Wժ_OyӶ+:dirMkuV]6([oz5}4]u,zng5[YwMSe7QՔ[yieuM1O'hEQTՂw/6-ݫW^Ze=-;uD;O[ `5/) {ʛn̬KXO`7=X M ܳt }WʛyY 8^}z5r]F2{42k7kԻ{o:z^uq:_l0G/7Św^i~i]HOdmaʝ)iY@\1r ?^~bٳ^N|=/aO-{6;/\%l(τ64!#,_못r +ly jK [+~IsYWu,{O^TtkS8}ȾhoJX@ml՛\2QvH]h>$e `W^/l6]%)(z f}2?Q;ݖ 286!sL[)@@HhZ~z 09I#c>?CwFrSؚ[u(pl]vU3 ,aZX@<0?`x `ѱ2t2~2d*/󖏐eϐ> ` Jȶc6xewč?[L]M'IA $fm=?UMۙw#9XlD͈!Ӗ^W )2 rՂ_  p@BLb2wQ "^s`kYά60g7^,=VL㫟 E7;הv yUro^<ܦ| vs1Rժlr8P A1iɴ!Ȃ$MOcJv3Bq߼cF,FH`0:.p, /8.S 4݉ڦ ՇL˶B\.c!id04U(kKB?XEHK]Suy*@I5e7JBhpoˎho@) :Fp86  kQC" UV[ BsaeȀQTVHcjB%IIT81nWnÈf.\ !quxd c CZs)eҪm.M-oq#GGdmw_̖SV ;`RDy|.ǔuSKFg $—%p? {t ױ˘dY6|4º \@6/{u9QgEJK-.OQ 2_(5%Y(vՒB@\@d:ڶ8wJ<`/&C#q?ٳPb@#l/%H'Pgc4`A3Ѕà1{h\-}5@ u`ܳlӿ‚^<JM\M#[RT"}ԫzz: eX @)kGƂ HDxZR)_ `Ӆ-|`Π^Ib֔}\'=_LĞD5#;Dڌ1|q |d8tT6@7,Mq~YLg_9O~f+8ɳ 6nBF qeDVߓ/ŒN#}o}"To:aKQb}eߒ,]7Ы:_w=b7t$("WA[6FCCM*E5)DJjEZ$GCv!D_na28bj]$;y/bB/g|&{?'1V&>}nU24Ğu5p׀M0^TgUowcwۆYۑ1( ~&~bC]L,U }uas]`xhD30&cұH1^kn}F3-f'G:a7qpp}簾"M>b~5g)?p1GN̮Q֢6HU2vn9 6T1H G9K_,3TY Jwuw,JT0}n)LD|+n;c/KV7 P$;43͆f#YM]do@F/&![Yf8Azqe~,@S7ҶHY2vzV$tF>*Nuu e)8\p@G!Z!0c_=#/isqpAbl YQw=MobcE GL7c s-rnjr ݋yRh5juǯ1F\r}QO61'p; "d GմDH/U|lxHc ,Z,xeq6| tae寳t0gƶPZ.缛Rǐu*ȍ3.[D׼C>e/ͤy"tt p\=b .Cf hˢ1%c -N(Qz71vqc[*b~.j*&pt>=TŁkL($ó9'2MWP9aӃY$z N83K:=M"yB&|Ub2RqNaJGAO5Ug bYG0$JzqâeT :md&<&^${ ͽ1cQxGXZ {"D^ݗZ̀!#A)hx}8=u0'`݈0]><>9~pxf.YO+gK=N`C?fHjIygc~cC?RkLH7?"SnRqtA2i\`<0=M*יs}E׭ɧߜ v)?ӳ_O~eRx=GM|PBNE)r|Vc:E3 n%))JypQul,?2>(gFvvsVo* ?#?MIĮb-?/:̮ Vħ@x4&dKG{X"{t1ZOlok% נ86'cc˿-Jg0+4/pԋm.0jfXS/ćfn:O0%Un (XW R`$?J+84-o!5/}Mvo\4k);Jm <;7#ntӅB^XJIV4% MZgjz&@N<x-G8T`uIMf-;?&TY+h۩*@S qmb qU.0|w{\68{*[ }%naX!$Rt[+6IR"1l09-g%+WiUa-vɏb MlYhks,qp pxݜ)?e_X>;U>ʛ5ra3`ڟ8в=C[f6*Xpfq_/wM݀mB܎, J›D<=>q(rUs@c&((ܚD%lVK(]4:ZWN٣6G0i\‡.؋7)R8'>=Rt9& 3XZ8~T?HIRSjfNȃn7_7SK7!SA t>_Ǐgfu $5Id-\qslvJMaL$ȱԙHy'J7v˻4 rsjgB;^| Q =m PzEs۬\[WM_W[*4AqG:%j+cw8c[~XVf:D[).;"SI"1Ia]RzU1S*s ;w&w|{&]ƍIb!Xwm{iHRi@]ka9$1pЈLTIX 곽_FE3{Au߹[U\0by]liYK}u#ݩ.s,=~]'fuJצ~Und|e Ԅ7MVM5L9+JSG*^S?]6`\ zd8yCߧ`ȥiޒmňqY~~j 2ri}׭-cY x\$Jұ{!yʏؓAFbz3jH=w2=NaC HY;YU ?&Pe>hʋTbөHYAD$HX`Gaͭsp6L)y +vBz ddb d:sq }DoY)I5ֽV1KZ]/b_j1sw2.Mٛ@~}%9S͛ |Jd۲˰Xi;wVTKN Gp)7f!6$_x!TN;੘iAIVYdG*顳/l$իjf.)rfzؔZyUgbC}ɇlO)i: 8թ ⣌:qӂEji!̺ghN7"cHaO^ UTGf/Fꃼɚ|-pk' ե 4WDѮ,bx#ڳo ǐsD,؛zaDѢoi1ڣ$5qy 4aFV+n5b9Vlx6+]6SF=AGv Њ"B);)VusYq1\a=OaH n*QcV5c_ej,BF'Θh ^!^?H(_`/>/GϿ߿hYQ4<ج/G&E⸻K'^{@nAďHכuvoI|o$+/ 5.q?I1/m i k"yM? NqytNwX IK\ 7jLԭİ8 ڜn%P b@%;E6rdF5zVV(j"vSƩX5\ ƬNg&j8be d@gw/SFI?3U:Iz+ )ʷN4ib֓=:4Cx0OnlZg=X$o]lyR~܉@~0q-/?^gǁX*Y2x0w 777V-vDyPac2xBCk+8td#FᆄM'iJ&Vk=x? eҒd UB)і3:BZ#V $(Ѕ8re *II:e|ɜe?9 Wӈ#֏= 8{-60!^d?( E4E]bQ4昝XGlČfNSw1v|g6?F6 0DN6WɝadisMfU?%u0wAS߆y ߺOٛ me̹#^w}XEH1lB:!]$3kmJadvbk CEA{F+b3t*"&Wub->EOɅq#+MoVY:;s ܮ޼3 AoX9|{yiUBNP(;|pV/oǪcZ6X1<~tf󪯞D5/ 8xt~#Ri1\6q0Ed4 3sK0x{b9}K 3TT>~sl%^Eeh>DJ3x3^La\Ө),7'bR8]@%=1  b^K@e)ê'aE}>> 󘾜x`;s_ ?!ʆO8Vɭ34;I(Uiq-J 7cqT6MY{?4-ﱤYdI=.p"ךƵHB3U$0Vza?H4++UrfDcȣXIARhV-oT22{cZZB;^|t M;9՞ 9_1II}~׌eBͻg, __\a󺼖Z0>vP/хP:ٽIm f-Qf8 v?]i!Hw5Xh)'_'pNa{k̢7ȅ{'BȎK.97$ywH/HKke:l-_iz^-;B-5)=l8k: N@ssf1h&V~hK)ӱЎq1|#:cG ȝIKNefGgrӥك%y@Ņj̟H9<1~J7 8meb!5n:Is&L-&PxWuNJ͘2IOrys),[NŻ`. w e9)#pgrN'*xJo s ,8}0n:hR}]Y.;,BʜeoƬl1L:_h{a-䭔ݜ/`QאeI)8sdbSϚN79DձkӱI%nJk-mq9afj*Y ى7.dg $/HY? ;HB'@CɉSYIJp&^& k+=M TМvR8WyIIa=ԏ+U*+!(&׉AV ^FGw]` ^@s};gƻ%Ӂ!a"܏.KS{ӾETFbOLYPGj8qTv=[J(Qݠjr*FQ`?lVx˖5{LY ڥcAck./4+hQZ]EmO亣<W_Xk׸Xv%=# Ů]8f܋aPz;1 pi~ 2QQXG2N"Ӳ^mWy+16$ î'C934m6~sx Ei΍L۳j귙aF )o~ƓQԀ1,R"4Z5޽ə[M%YAn?!Ms8GkS'C#@DD;)QdpSX@UC,7,HVńjCҚoz12~kK⎂aroN[3gMh k.+Ě2(0R=L_*a܋#avesO0p8oT2ԩOHs(pr<<?@dÄg:h_8,5{sfTy\o= KS~ZެX!Vl$s5s= B۬8Y|!K Wy/CdJE*ӫ2Ҳ*[ NR1%d^ ^Z_ ku1)au_xέ'+{b']y7{|<7 5< SbrkjYc5]+}IZk:cHwJqcXrGK#X;I|`\b6ӑgLe|7m gE4./%+4Y?JxLcѠoN3~DDcG唸ӉWOMc vEXR}Ê H.>UG*%p Ѱ+4q;|eo9i1"w\.9A=w@~dd0L657[ q;V/t_954 cU B4Nc_1rlߐiT۟(NZD- s_Wŕҍ`B_%OSYMnLؾw.Aօ"[1ɞ=~9{(FE(\iݝPƎYd$W񎄯O9O"P=´#$\Oh5u-NOzA1`&#]Qe`#WCٲ8'}aPKolXOPcelery/exceptions.pyYmoܸ_A$Vk 48mp4\^!Ҭ$Hp!j%{7Zq^_ӓS\Twk֘_$z9v ] : Wv(:ekx7OF&bi3+4W85V#oCY+Y*vڈFA~dU\F"z :9ܨ֏?h4eS߷dY`(6*wy-J9'\) eP=BG|+Pfz}s/fH#^ tɅ=nb+]Ke] S< +Q#( " Bw[`8Ê)(h,oY*wbIhN >[Yw/KDDDQc[Q+o>9aq`#=,!JڟD)L~7qցIq< T)*GTg g* 8 g=пx;}92k'CK8N{DZ [U.yCh+/9"n]$6&~Dyt4M(m[-@꾯XS  -0/tyʪ)oAw+-Vd<+gj> ^:CƸZ13ow;U}^2bCu(JS^3[_3#~])p[bs r 3B"y`/ڊHd0*"m`fԾ-!n)oT0bjzr{™g$MAAiZ5K +z4^"1p8=3Á%壶N}/C?񅷜ѡ*0< !{bY5϶ iBv{ `SǭNgYbߔʬGVwdokE_>]^ŻkT_"Lr};Q]W Sdb/;6z6_%lZn1D@nhÞ9{>׌{M4Jʉ  AlN &@ߜ-°[4VOX)opCؑזQuCbKx(+x yVEȨT>ӱ^qxqs9qVrFZbkw }73%n)4Ó";W-ƮNPI nqu KVYu =},@S"T79i^`rAAI'{ IT¤iج+ъ,OON1MJcD[v|B$XH];?u?#V6 F;yeDV#O_BNwKd D8"_OCDlsՉ_&#O[=3hm/XCtW|G>tjT.a~׋kiƤ-r:ap'-1R Ff1+<s^90Mw^#Mm{ X#kC7Da8pO}9 u9{/S2ǡ,T9\4r`뫌5CYPp+ z\$glGe>{ b(џaS{ثU2oXN(;o3ݷ^7z,竡>te [HyBлiZlK*mۛ2 ك5';K T8st ]5g&P2^ 8RoV6vo4r`]{,qZ|4c[E-t}hdɖ hh(4&'4Q~>/Y9<9غu (ZՊ &G=aPC j {5u-m}b+VT o Y%Pl+@ʋJ."%+Jx5ްJ^%[ VJq5SRVvRbU*j˒U!p(CyQlfoUkVLo/n_WWQ_.+v.6A gkmZ)'eۈTǬ PHAQRfR໑V{*k,Ÿ́Ee'Ռkf8Mō<b ܪ~i3)$=cќt{#:~m~%Rq˖7b@"S+{Z} ֒\)~6bR۲IRiQc5b+b14 Q1I-LGԋbq,X2wtjKCCފ `rOv`Y&ʍc𑜯^O8)n~+Đx,NV'{?αV6M#X̮DVitAPI&sHIiy`R "YHDQqٿd%< :F"^bHD GP݊EQ"x1{܈<_Z]XFR0ٙ}-&f`}qo{8- tOTLڏ\ -+TX \ Ü>q]þ#Wu-Hid Rqk&d˘Kp`'/tkVPADQ]}Db_~Pꌯwng" :BѶ/ Vw BGRoď~4KL,- ;l$\ Q$Q |rgg芶b)<4 <~!í_3M/#&Qz!=&UYd/b,o/i̾8z!6SMurwc8(Ǔ2t}G~Mj%%# !Mfs@G'?@y[^:;!K<Q◃T-jH;/q4F#@}a ;\C;NGvwpyQn;>5Rjċy%N{+FXM |>hkEle2Dwp‰ۭ6A]NZK|ComQߺ<^xn7EIuD`yИ*za[2,fWxXQ| ~]uo]fW#m6$\CQo6.z5֣ոOIu&.]+e:>O|϶\]3)ɊJb=FO9r [3I.3abѬIXIh7[w@ErI񙴘ωϠ l z.:s -;:Uw =b(U/P $#3H62lt?&}+*̘K+ŔrUj`5t %}O8d oe[LeuIILzom0ɐd*[XƉOlVlo6 g(,˕IڊmXA׿|*^|qk%]~ǔ C|+2d)ǐZlrəN]f`h2)7/~<}q~@=X((ww57{-^vh!z7 -_$ ݡ4 pbkL NfUb؅EZ^<* M*ש&g3FuQQw;:NCu3n6W-IoD7WwwPoRp{_[Qo<;fuڠ+Nڞ6wsxή.d^d\drw AF@g$:?~qqAJs|;Pi#BP౩> {;ՐC@#>Ky5l& 7ޒ/zzXA$ ǞP Bmp4Uz퇈W<;0r}Z\3'eI QY_-!U \UcB+wNe-p3+ m&s#Eо.xlaaAphTzWfY>d7܂p\_Ee sS#hŬ݄ ^zZ6>{ål]-Qy}d«#j1>[rG쥏HCŚ> S՞z=5֞gSN#vۡ8t1*m6gY3qY=s3 wwrHAv՜uMpk=tpDgDu!/^s,EO]lPS{/[[Su(&ͳBmՏrѫg@î^ΘYQоڛ uzm+IrJBra?9fRs)1ĺ@1IeÍ!LZ0׮쐹<;};2v N(z25#ʁrVpBt K'g+nji.8}kai_) ,Zw|[t0!Q`/q\Yqu`iՒ7)̮+6Ju#k c^+0y2Oʢ.jӝOW$7t%Ɵj WLh@(f)NF&I #`fp19&zǀS$d/uD;$W9 7mݤ 3Q1ӹA; a^Z/ͅF)^(u.Fn.Uq:7qUU97gg4S{Ts l0 LkVwGbKW#<2_ !WY͸66sM]Id@~SFYݠy]ǖe\^j* c[\_;dP=N"ieñ4:q$@w2@ 'i{0fpI֮r)!?KܚVҞi/y0P;=&^[>h C. Vv5ɶNdv gxC[`ȥ7LQvuvUzx1q pB5#wА |,  hҏ6123)CP5#j̏L85PKolXOUfacelery/platforms.pyy{~$oo_ ?ؒ|7Gcq|-~wǯOWge z|z"J $+,MPqZh*M`ēw4*" !.I:/s~!b-eYjK(yN$EaHιʬ $~p #<K.W#,ƤB|%&zK_˸6zGFDmYhaT(7c;4i EH>*'vTsԴ:9- " WSmXpЈ]%GN_GN9<>Z'Pzv|(zqex1/GN'^"Px,ji~Q0E#"C|d:0% pYР3Z"ߊ:C*3Q~DEQ5&(r!p4V4{*D/N7ݧr`~v/@5b?L:/tEYt.jZjTz 5)j[0ObSpO~I:i(퍺 `p78$75:.L݁ B rеN2Bd#Iiz<9#|'كӷ0,k(2DxTympJ$62E2@h]h a[f0,XH(V$Jrjd;O,;@~>4ls!KKe1t,82#6'إXR薩i +\^iPW+7HJ f& \൴ eg]Vǃ=%Jg~jDC]EL[HD?Ne0Iu&R;s Г%,$fU1،wT9"13 $rd."d6쀧=,Ԗẙ> "y((b6:ca(ST$cOp@>!r>΢[4"-*t.ǡo+Ơ߱+8h4)4sA:,`$uA&`㴌K>+`FD\7װk MYQ\6fn4E,=tNxo[t ծRr?ܕK0;. {RD'Cj ]&PDTj;piNp،qC[)u)t.)ȯ ]T۵ *=3]evTi0`Ycn0_+9|U 6갬/L]}aee (7>_0wb+YW؀+ҭ&hp};;:՝0 ׀J>̒m̕^Pc}ݠKic!/ԢQ tQ(Pђҵh&p‹40YB0`tу<*d tQE]&GusPЏqR״H/^5{hN׋[c14/3\.ⲁ3VŌFo+Bl9oHt˖qz-׷@(a lihFPK2!PUf`=_$,K>p5 <=zz=0Y 3p耾`e-GI8X"^R dgU8|=FjZ2%&Z/ .u. =Å4{.&V=ZWq])RWW1fj̓5]ú=K"Unbn0U]F!p^^N(eGB HJU3\Q  ƎMhnE|Fs=@8I|{m gS2KU p+"_5RQN\ƿ6U<_F#.~\1 J&X"P080-bձ1YIZ[;~N챊g6MdF[+= =[2)FȦeSMzCk ia# &@ǔ "}G Am*HًGVO5ߘW<! 'd&p8歯Vw*Yu1 iƑ>x>p" apȂK}`zqڲcylM/!II4cNM9iŀ-p8Zey]X10!3|,.Z`ܐ"8z:% [̍(,X#tj'хAJ9ver$- ͂w.k4Ӏ|Xhx=%, ~00V^W8<"&g\/*d(h5nj$wn;"(&,C#.Q*TdtA!RbfYF8G jG6 ;H!9;%0dP rZR(S"&jTvd n+ b.L&avrؘ N p {cj!A0a ؗH4vV6 @" WkF;\c& +1RL&_qcS-wx c?sg0 p_8X3eo ˼XQrP8/1԰$f>4.sg\"{#>gAC=k[)k s  t }i*jx7`?rXa}`>&۽i`JZq/xLz@"U]"1x`.'W,X[TO(:llXg̉P'$R$=E@m PK7^ӫODZt^tir:9ǯ?G":p2DvYdE?Dv)K3^M ko.WތiYHnZǞI[a?sgHF7 MC\a>$Ƒi$Ld? }N౥W 5vxWɠ\!j-?^y!EZX' 7; T/*f}J&d[3}_Ǖۆn3kc-6`۾nnξO#^"Hx|yL9(u"ILRY@30on4IYvit2.6E3aL.r!u=6lLSu|Wc0WyѱfQu+08f UR`H SKQSf,ň$U{TLj73jܙ~(s ̦8]lY 'x:K|p$Mfw00Ͽ!̸̍ ,hfεlcu⳿G8;ɛ#Bӎ Ѷ K^1FʙNe ^9wOpC cL ;TҾ~v΍ gq0{Ț4+,#`q  6c%Lqe.^ĥ2:_>c̋vlLȡmg870:zfs?w9.6p7J< ;%HLLZfy1XV_]2Ә-!M0G\_& c8%w_5kBxju8Ɯ/Vhm-7Bom&r3*Mr9<~fKuCˢ6^ke`Sۣ][%_@tu}h Z ^=4laJJ=U050ޞ9T4xօHW//㗯 n__XoOxUF7S,a.Qe۟w˖/d0Y8vxyt~4nӃn! #!mt3&Opo!k\ C :<'nא-Kme$Dsl|4 }|=F7u}M>u.&3?*PGy}sMj~3m~V|vysyi.g`q^i01X:進Yr!+Lqa.jlVf/tW3SGAђb 9[ڱfFOJ^mx(qrLYe0͞\AY`W֏J5LSv&fc.G5^l7=LCoNPO5{rb=nEsIzis[(OAGxPc#D-FaSfqGZMg귳4ߥb*Hgpb/ ݞv P+3X't!^*tȽ|ٹD[{`ʞ]/?PKSBqP4 !5celery/result.py=ksF+p>v|=*gVr>Y$UA1R)o~{$%gjHLLOOOwOwɼ-zᓃO>'?bIєIhk׫>i Z,vb=;I\ݐW}[in*Vu5Yj)oNPm]PMJщe5i6y+US>YMq-:Uk1?~.z>k~.ub~#|յ+ [5BU8MqUirUt]%iU/$LZwhX ᷗ5rƶbmwU]Wu;?MbO% QZmbQr\TZ͋YTװ|4yx7tXN3Xv=t]+ј-[=sOe۴C  7m?՚#bM]\A~̇DuSU'怦.ͮbu+єoӄ|vb ȆUO?SMWEÃh&8xFJT%V= X~TzxJ;ˢ4s~?;?׸L Rɻ1Ǚ MN|/y1?kk1n&)0fO槬It:< tq PS,Sm:]DZ+`3%a= ?xmv궮;W0lۢ^ @Uw8ӯ )?J%ZT *fgl.@pdκ_k.?12wc E5tPOg,n:T$jx+6sŘN\W=n[9S3l3^ 43xǰ7}opTI m2Kp̀u˪tbU~_ܪTA6(D[wIIXaf &u'~%7x=jj1 63)pWM5yڋz1SU+F"X `}jAClDA?,$sziϼOHi6< PfQʊ]B2h&HBՉe{+b egɧ$Mnar)B4dq9(dBF%HWup}d (ۚKVA&54ZNܶ"iX`LU}uՠg0wNUBó%ػ{/^9n6) k/"t\g(tmPJ~x$+, ><ҩt^m gqݷciBj|5&mLMl&)K) j5,%4*NNV5L+8HyFReRH%J̫Ȩq[d'WDn%E.AMR5>hgw./bJ][g־ am 5:7n8N+j{{,GjGgś[jt+ɬC%h`KGPa"  uW\]9 WR"'o^y;;>;ywĆz{k?wmT;kޠ@'tM] R6㻢kf!;" g*ɲ$5G./SF{ Ž1$N,fHG߮ qCW4VQu'lPM1X &vƦ]'cq F50[fv?-Mã|`AOtrC2#Ek{%oB.0b1Hǘy=8 |N_]Rx 1 Zl @ڇ\ }P]fϧ1*ڮG_%r~ԎX◩D  m0h) KO ,n*HUD,hȡ~t+$YJYygDАW-lac,'%TTjN py.o+)f$\hNS=B$] OvmHWMp z,[(4K.r<'L%/E kH$g05 QQڜ8[og 2ygc;E*0U١5UVHF \͘|lyZYIKE8\S Q"B㒎'SQq[X-ђ,ƣR4G_JtVĽQ$tX j||X5[&z@CB, j@8j2,.7h k nP_/T4@j ՗qymT?n7B`J5_Mq#] &(UA|UJ;(dd yM[B0zݫ-N& 9bmT+3(nE*Х=zH4 I./Wv ewx蛽+vZI L u^kTvZRf@eiiԑEc}wm V=0eSRE`W(8+y jN i2<\]72t fYQnsXݡ@X 6܏[tmsԾq)(%UaH) K Q%De֧ՄUV;izNsU{,+E]lC#mTgVi/_|a/TȅW±H([%ȣXN---s-$Ԯw+g0)MNYF4s316<1Z dS鎱}|@^5wHFݍ2v7#Gl;_XLO_쎌֩>FjaVc |Nk2Cl/M.`%@ !4d߫)un9p )/+_:F)bb p ugH .(􎭂Յ=Ϙ@S'A; ź~",0&>#ŋw`xH׾=uoOO{T}y#>B^.KOl60m!VwrnXtV>7  ;mx@+tw~T=`vS;e.pЇG5D{ƒ%Gڜ-#5~BU\H7%R=GtY+'A1ņ,+Hr!oiAVi\v*e/:j 8'- ByTޘG#KgLR7J8_@ŔE7=-4M^ ȌQ:;ez[$P@Q'+yM4԰*2z<0K~[Urri,~>c-2JNms#_#ա&(L<0M* eB[#FNe3OD֧#(Plw%;%T Y4mHK`'VS$doV`\9uVxB@Y9&<5^ J3yr@y0nB4Ǖo1Z=ct {A} (`~B;T0t T;nlO0bD<pP&ȭ\O<̳H%FD_e(wG1)ДS4IH=s;!H E.2=E-.1]!_$HY֌u0*k &ΣraO-+T,쨧.JsS@GNjHaҬ9 9c!aF>ζYʾSOkWB4ZKݰ4*mȵztNTSXUSzO%F:`@j_t~_K?P]cXweؾC tQf )7V_*:a y=Hڀu)eZ3;vuMkyX)&+JTD{Ṳ̃b>F#l5+?Ll5Cvpp8\IKn" *gMc; 5tTLgK\5uZM@%r,l8bf{h߱zSN;Ųo.&yݝ^x_s .p{U#NVZr22t]>3y:f}nzHDB?Aqq2B&h_CYU&ۗhJYy 5TN`aMx)] oP**s1'𕘳*T(j99@GhTDcUIC=RiUX5-[*(: Vu7:bogԞ(}%G Vo$3XpeϋnnG=Ⴉ:Ļ PznE]/ps*ICg]X &mk#IZe":-q.;(B)&&;RlY]zbƛոvPBCVL4 9=v{qa}1^KNעix cLT=P>rk=b\ٱzq*OVzmeUW?TTt_vQ2L>GPclwkj6%f`*AvMjMYk/cl_#zx7rPfA>l ,sm#-{ J9V4?8`RqF/e~N ZZwxh`gXĢ;7tU7v%Os#(Gf3YR@Wkq۬Фs &{I>?=,v@wL 8i)E' &RmXi4G Su4p΂"7gv:xF}gs鄒 jCO=޿ܙwk.~{;gT?rd%s +ݹ+<`hNB`F5UG,UJbv,H)b#s|pGqdh$!/n}1g'[Ra3|IE3@-WWu5l,fx,+N70 Kd ғUDi ꦹsmm&Ę7?'i@ѷ;3 >n.G,txk4"dpJ~8)-]h Mu0lt̉-t9)zMduQTx)Ddya[jt:˹ ;uP%F0T0fbdmuzvxInʪX_!;#w?̾YSb'lJzMtFdPy1aѻ^Q~[ DZ`NR ;L?+ I%d T mFh6UR7 УKWRX.aNP|p p%qYQZHQ!׭;fl\u3Wwʪ[K.C ˹9n讪+ ف.URNiCc(.,\"J[uaBT60QBր sp7,HX\|,6-,ct!7%wAm@8ycQobg͙ABN4PE3`O6;Sָ+|SXL!2f7Vxj=PQ&a JIc-@"="ɤhl w1+$srě 9^m5Dn!2|6HjdUΰ})=)uh81o(h1ZháNz{2lAvuPRTJTŒ[&pI^f5[P2ۉ:Ӳby!Q g.*b5xƛOݾ쟤Z i<('/.P#JRPp93!b_ٲzS򢑋_┌{_c"aDlW:= k(2h($8A2~yq:|T{d%xzlWmԫlu?cVֿX#V 7xsֻ 9 G=`\:39RC59jb29q ?+l!^'pEu"~A?2(|4(MC*L"n; y:4QK%ƾRV6hV1M~ק +6P$(֘p>aXω7yG޳c3:|vϧ:lk٠I0%lNƹOxX&-M" MJ@D魷paAd  ^S%iIt͸zTm9~4YmjF#CEa>?f>I74MJo'Ț"h%=zJF2 խ#0e̿5,j[DK9m/I<$q ؜zzA 9Cཷæ9F l7v|.+q`˂&opܷ SFٝύ7'!iΜ|"o0+Fo?0`gq"ǀTE]˳M?MD՘8Gk-f/(. +3Zt CaP߄Xɗ5='ͦBqq'W&+-čFImB%pxtV0b;y@m$TqDi)J\'sE㗢ʋR,'"߈0\]!P$m^T"Zydȿ.K  3m(dJ߆gUݖy“$J762vTAJVFvPL{^.UIZWk.#dS-rCWӼ+ UaݪU`< OC̈́eFUH" XPMK1t\gKng{june',."͗Q Ob2D2.BOD߇nr%oW vQ)V._P(t Mk\OnMʭ\LB|%l?Pu%Ȇ[YN%h *#}9~kMc5۩<¼H!vU<֯ a Q: ;ذ7σ'7~x0ꇧo_wmfū],gvKەn!+ ,oX|3qz=Hl5X&_3ٍO_m/?ϫ/;q @ W U"/ &#O$+Q"ԗE`NiTݚXX,bJCswYm]PgMqT2?G&g#N# AUa2] E_v%)@PMQp'mDMh8 G-@Y 6 c e *t&Y`è# hT˘T Ӵ< b\&IWoM=9lveo\&eoQ }sٛ+mӇPOE^hW-Cf*! C%AѬ`a$`հ&]qeo1`9%!) fCg:f#}$^ pߔЖr6𴵦-z؃/j50`*ޒק ZpEO++4o{I\&i@oT)t#ɡū } V%~ S%ƪF8Y$$k]^O[jg2F6BzNN^n8aGjzgM'7mƘB񵴱&rE`P/*0|gږ! \1^L`+.ԣofCP: n灰kӪˋ|%~@)#;v bH8!8xd 5iyS@;jEf]OLJ+g].!Z*@=Whh 4 *ުyޕm^fX=b_W<rDb"h ULJ,Vm sJ K?;bE"6EM8~Q U PlX2smT)nQk^PM"Iڇ/䈸5cN Mfk ##I-1Hf{@GN4h2x Xq=.YCgb#efQfD^#C-(PFzWsz%{t`fʉ6:cLRBMlwK850vA> Z1$H:ű=i\ߕFpuBm]J:,)5"lo=szFxkG){.&*@ϧeЉ\"@?+Ц#~C$" SbIH1#T wF =/@/Ud &Jx 60bBEnSF]76fܖgf'-l4tf4mFUiNSoIjFwѧ)g1#yk)k dz,߼&-2L25x|lLegJWAMU 4?Q8f[i[45crxjD9_ߠVK<[4ufVȵ8871IZIl(A69Мc!c&`뷉oOXۛuyZU~Q*t;cwNʌ1qac>C.(;L𲌬8?M穭ja0w[`#d0p)VH8u[etE*8b& q}#n{w4*_!׊zMCB7R;]r~LX{kr]\hoa!~42欽d([31ؗ)+*b~B_* X=Q-yEq)v`lֽmqVte<:L\d .5ƽ$-- ZJhXNgsaV1XHNGhMb>3|f!/1:c]a?Gira2E4j !&2tBվc y.9=Xs^ȏr aAk!2@9ѧ>B;3Ms/*17ESۑXEI̺D`=cNDyܘXbX<ҡ[}D`(>c{X4¾X(deW,{eEd: j3$ЍGᤋ1yv^9g]|HDɛQ(:=@ķcïGG;ô7I%6\1 !|JC7˱k:0 8s9":w,@HUrz4ȞVX*3hU$fՔʳt??K<43 ^Oz|%08pBdAݽ/֖tq;MU: f6\Ě9bpg李jԛ )-(`-Zkt@&GɃ!xĝY'xE̘,eecu;?tq zf) WmCӆǭEQElfح )k9+t~Qjf@'L^ZqK›jc\Nj" &ƒs)`^e4\$}ay@ffոs6$-%HO#5/_@`h 85f <'I9{ƥ u_L%^K0⻮̱vh(ofx5Uy8Yѝ;g*TKѠ79v_|ٳ}"٧+@3NnA&w+ >2` [G`1e[`P<wȪA=Ӱ{A iUiR`H<[I,QFQ l4_şɒ^jPЫ6+=~#H(2}rO )؍ ZXX1c?AA6J>%DSVJ04G&|}Z7 lc30`(R.RF54څҰ˱~sH[d|h "(*jt`~e~>sMm)Ձ5Cd*S6aݹb|'O?8ب\oai!MgLpVH=َaHb nFJU O,6`>i h&NF8l8sCS) ҬHYl\t鹹ZCT*+{K}@MX㌽Vd]Lă ]T+sǭw)uN ک@j]^`*LN2Y8Ak$hԨ$Sp zcΐs]}7>P YΘr)޲.*MC_NT>%MAAĈUhCYڤـ#A3qr.wpυ6t? %f25KVЅEe[>aGL\ RiI*>/ЁI:Ba2,MvvkcmtzDk4 NnWa+=\F̺q%eApf=ꚃ?k`iIs&= ?nFn L{f. 0[ DB,)qgZi7&{($EH z:m֮hEGZF890;ytǀ!\~glq(gyrs۩qfB%0tf`ZN@;αxJr܉w]'>R:i'(dtqr}߯@ŠǽBƥ>SeE} C[Iox wBQi▅)(rb[O"_ɯ Y#0mCr!|7#hƊZ4bئyPh;٭dW-\  B54,Fx_o@VSw)sU@qZi˚Y~;zSK,1iiI:â1QTWW1_4so4Mm޶q\7=ԫsFf?+T4CmZvL4 藃pqdZLse(udeIk R7qeNaa }oT硸ô8g5e3 ^ifӽxHW/RFbB&1n0~n"afqw6΅ |8דoz;ԝnfu{hxЛfY*o.tUe!])$jޝDC%OwrkéUL0}+/+}w0iZ)\LEb[dI2{7~&7)=]'YPG߆u(0wSC~kJZ񈨠4lf-3w`$5?3lgOPk_bwo-gBz!^RsщnQ;9nzYf|ya_㎾Z+beitZ$xP\SmwȬj@fudǺ8a{-~ז۸\ռVrrM֍ˢ ,d: ~Rф()4u=_/+X9[Qj;}hWj_&rI)Zp&aYRV]vE/2j:jV T`#H]i'F.?PH>a5S o4~3ql|5}lOj3kŕ67X}d>h]uIO'}=6r(P`eQng^uYqWD?YO sYϬV󗽝5; t4x;ѫ7AHaYr{NqI @處dsf mJ $Ժ/VݸL?JY:tu6z( Lq0 SVEàq{9r1= Md뿇) 3@~oOR S^RD>J @jF 5l *em;}60&?q^7o¦Z~c y5}54[&;4"/LnrΫnxz1fFJBLwdӆӈb`qJN /u0TG@Q 0nڠ? IoVng8I=oHi~Sh! RgܴiN:An@37iZ 5abzf6f ZNE׬Ag 'YT ق,);EDXbU* eN{$ @guUecoj tuU7GY_:pV}m^El WfQٷ4&7.PIe8Mw'4U$S2%/Z3#}LKUqy?  %fG׆ڪ@+Cm׃}M;f-h 9(7O eHrZeiL3RBiV9fD:GQQ Bo HpN(F4ۡO~O^C^i4ƈĿ-sw.֪EQPҖQP%|B!8u -4V`vf%(EB?x6@PXfNE7nXZgA iнtᨌMϦ/K0f"Q3.0NPs*v0 ؾsԑsKg[s}U>c-V)I˓!92S1~b= J/nbI¥8vyDY\?˯,pѤyVG=} +5Wnݬ|:2O}o8=2{|d߿5}l$@"@d|SSfKP1MȘ "q; F5C"-H  Uw"ώ%w#XvW%7=EO,#-Y R$}$wp{ƣlm|c#%.Cp9B;k?Ǹzw3PKolXOnT$ celery/states.pyVo6_qhbAXxelqE!9,I%uoC2<0ir~B׃_ y2/ jpۄ؏J\K#3pl˖j+G?dȞ!̣v1ZDvOϊX{LÄ|tX%А+Z%zD ~Ԙ-wB7 3"q4[\M'5gWcѥAb̦׹mt6$gUV&Ѡ6,2KO|d#a eX0Rc^ԧ%/[K8^?1份>vJSc\eR( ;1lM.&gͣqt!0k8_xޚ<: <0vLh1hW鼢ۉW)馁ޑ%!OldHE!J#'#QrYl9Ehţa+v#QB3b3N&~fbuMEs۶cetŪHW%T?qsy쳍@!Ķ()3K tz}7#<սw< @&{?P oc$[1xP ׍lO|IY߹եXt-^kypV: gI)3ZZ$J9 45{Yez?z'A6w,/[U Q);ʰ=r8 ͒ ^dϨ >BvmխpJUSє{KZ&6KRh2M1vg`͵NX|Qj u|}[̌#v*9?܀0㻁ps)3@䶱NWN݈YPc`(3Rjg_Sz߼xwmUFِt"L>i\'t0maVKOFPKolXO celery/app/__init__.pyUM6W v]TA-ZmTlR%[Aa^/PZc ?F;}*Yq/.p79XcwV0iΨ 9Vjf":-1`JzarIT,C-win"Iε[KxW:H6z~.0hhbQ:cW޴J +7<*=p}( ή9rq4~~~ߢ#loY]ʛomMYuA4cm:l;qcPAE}u,ܚOꛜɖ1;i7쳔f{ؙ͝(NJΕ%r(0)Y,hlL=8?(UI?E!蚎L-7?5]H 3sJ-+Ic& gﻠ:\v!R>Zw  Youݚ]4+SX 0h[H}ϰѫ7G|Ts_Vi M˜:CL_r>1ΐ;gnp3#w%tm:8a˯_g AVGM^Oޯ?PKmjO_celery/app/amqp.pyKIQݝxoax$X," [UY^^Y׮Ob/x/J,XK4R4,,;yW4u]mXE|eS]+R>c]C"-VԼh&YvhUX!Fc*Fdm-x+|#t=>ghk]WxO^tyNhyHk9 ]U>7F3vquK1co*VXlh,DɪlRxMktm^4ɺ+%%?]+Y[=(D0M~YrMJa؛q`6͌5m pQzzЁԕ08?>^VeK ֆVmV%` Rjbd_Z(V%@:D:0J{ɷ[`D|\m^Pi]Wl}x x^ %&`؛]{$k-16ԧ$MyQ[8zh"Z\Xd`\4-ݗrGk&\ӟɋWҗ/^'OOA.mU} Y -[p-vM2_NOO|ONHFϽdQ6d_@Y&V'^>Q%>=JP~=j.T?S}lO`^Q+dXőTqtMӥp.'oYe;ۀXIz Sdv͘(^DZ-٧k/pSjyf2Q40asvj08 lztכFj>(bT*$=AIp >/Am37=Ҝay&y:g/ʼA.̀$j$ ,&o4Ut2 vtyW:ځ.Rww[ g<`]6@;(bZd-u/@IfP V]]]d@%p⎃.d5ρsbGOUv,~/@\ /Ϙ93Ck`eEۣ`0[jOL~dHL|&8(% RRnnxW^G:_K5`DCVNN”傽$IT4IӸz.f%"5*ՕŇ %" ~i&PgSSo /r!&AC+(jcͼҽ /@i7 Sr̮q9 `̌̍RNX*KJOXO7P {+h&Uӽ\&_(T)v em,;yfj/KpK^xČ-g@)H`s%mZzDQcXD} I2*49P@6$=~w7z(ŝ&K'1fFcPCFC,;ŅBk\yӔL5e0$sr72 "`Z{M +&A=]Ug&C ZSm'䠂 D)}ucP4Z =FXlPxO>'N#YRˁ" l+V\2$ieNdj}#̐lꩈ;ҕv95yt bHAbKB:_ؽKk\mLB)r8Ű˪-H Bl$ǔ#k!MΜ(l8s .b $=U![,X}P2$[Yc!u1?l4^祧R`T:6!VQ2͸xR[($t}突Q-e ήTzdY6mk8nFѭJ$dpQd ɱDf:$0UaHCYXF#ܑMA$m b٣s-(s ;X8bNyo#JE*UIO &d$B.(aw9;KC6P|Lg=y{,fCc4]>Cس^ xhVEy =A89":k %z,ktȗV=3sO݉I)$ V ~H@Wk^9{ƂʶY NƐ_St8QOGXz:ʳdZ&aa3Lswba @l7T̍~bZ۪ UVSkV!a%j)HKWrs`y<2xq ag¡`b[=ܰ6 wC7CՌhx :j"u ݮT-WzȥIpu0`Ip]Q~cۋ //\2㬜 :(\$z3$]ZlmQ{UK_յn)~)v#ӂor-MnS4صx{: 7Wئ5KSOH@]ڋufXN)emN= Z }:Χ-{dכi>9]pMt(;>O#s` wQBr +(GG$%W,H*Iz.s8JGAS#"F; ˞`Xv]w@QD_c8?CG[2e2` ǐ_$>|(wvJC_ >@aá4;.mȿ\v=@ǟ[.awΕ܈t|WX*(#tLCmagW;dFhLFDD7B:`-xbf~hPeUG/ky:b_BᆣET䀷3e)C ܒ+ 2h螋]GxDw=o6%SI2Д#2G*q%4W5}ퟯ[<5^.CZ}k9i榌cO(ݼ 7tjvo3='rYՕs.F eۺ]E ) MI^jmsP/ DUlD]mT6*ojjDkߊcw{ y:Baqۇǵ>lazorg,s[7R|Vzl嘆{r4}WW imFnY܏8|pYA 8T#iȿnsQG2eUǴ`u$rssj)16xAjm>uV'.ArܣBJAu351nkЗm:p9tE4Jp/0@~3ׇBY.HΒ`c#kr-lD}iʳ]V4ϲ4ic*ǥGf{4 {ʡ[4W|92d$EPY[3k֒}6/d LLFfĐ ~Xޙ$[k/62n޻"ɭ=^D]y &vk3sT<+/P}' GH~/=;ʛX0K(+ToIJ0 ?]}pIe s0y.lѾ{!ؖ[aJݪ_ h̷"r"T0~z?2g{}@Zo3chNpWR}c\;^o1 YQ?rh=%樂ޮx{c Du1̣LvoǩcHA'T.@$duG"\sbM哑)z(>S͌!6*cs5w/q nG]ڜL/s wzWqHhNpqR((nt#K #S~*j׮A()FُpH?`ɞd:EmSTQ i,QrkDDbǪ<:do /%P a1aY"<ПT4@/4AOPKolXO^Gcelery/app/annotations.pyT;o0+ )5\ClA0)&BIV}(9\h4I ln7к/[cͽ ZUΆF tV};fP=DVh veI$$4xGR{ГX\IAl>O5AnG)Aspf(FQM(lѠN'uW1jn- ib5Ffl8q Q٨UD!dR=v^Y,YcQ`T#(\Hr,~ӢbR?yCF3MFŠQuzհMVo"nN챃<2=~x^Z`ۄ}(n?p5wM2u SLh8 ^\f{.q[?ε<儉2,9'̓!kEgǺ{s];ݍ2,LݭD_VB)JB_4Ph'q0Xetsrά<\oB$؏Ld-V#IM=ӆs98{LGv+ p;X+.PKolXO>6 celery/app/backends.pyVo6 ~_u;ݭ/bȏn8\u{CF,$v^}e;CGRB[tq~ Y2Qk&8;;[♊iia)(JeY՚V,Cn2Z̽' Ҍ3C:(`]Eui 1tZJQa24G(MIudCVRT +(l([@Nn1z"f%dtC9GfG`W$Jax^, q}l!KSbKbP0qx>8Ӈ \bG}ހW9OK?ا9fmČXK$Lh~FI ɉx=s֠?\ ҰBS)DK94˶؝ZdīQ7 MR-V^/oGg=~ _1]Cs3?OLwo*=h-<3A:qG^z9\z8F}]WxsoK6|,}W{k%yw&}ʽNϾ]!CThЩQ~&@Xd\Έ礅I @S/u%+L4 :?wT~J1c!?mh+4< _^K:<)x,uH7(ʽaDGx}tTٰMQⴒ&fc;̵TdLT2/'LD4&Βɲ7ボmܾQSn!ӈ@u1ABeaU.ebo?WtTגƝ0P//CtVxzjԐK_0(m2YN/PKQbPF26celery/app/base.py}k۶\Dy5:ITWؓW;Or8"GØ"ޠ4D n4Gɺ-fH'gr]zH_m>^x=X뛲vk8P;o4/*r^.;h6MťtKi7Mb.|GY̒(wY2ɜI֗CwP<=~кme6Nwfƃx ]f :(:x)?˝C͏_䛲{ilZuSz퀻n ea(vof0Pjdꫮoz_n6]Gl?, `#R%p%pUf%qj9dGcXOՇ+Q^F ^"x37hMkXjiaam-סWm]eVn\o^"<.}^W۵P}E*3f)7^_g@`f ]&l?gg@. t*WP5 bDHgږw}{.^7h* F@͙+Ft)ϤC)ח= ~X.{_2V[#kx2hna_0B9 (jh0WY}'D%5P$>(R?Ԋ$yrW&N׾i7mPPdHCҔyh| n]m ,ZƠRޖ]Wrߔ:4W><}1ր ni}0'4@õI,ox7q"6v%/,3=So0\a(zH-Dcyr'i )/TCăY]!:@ - sKN#]ewT5t!jI@LںqMx&VNMRCj0\C4L:7%J ePB]$%B^^uϹ,o^5m:!H%]^ḴHg7o'*˯_DwKa0'6/tPx\ؐ(KKBaWo52P4eR ma PH}Y"y<.@$ OHJ?6,bZ\=yIXR<$XuY +;<, n-Zг vzp"f,zFSCl>C+Eט Wޣ>ڱ`/c3խ9޿aQ WhGjxݴΣG,.恐~YF"R$XaMtx AAe}Lp қS:m΁%+] Y dZ- 4S9Y3o@7(Y"S*;z3Jr`ueP[ꠀa@C2ٷG6YLȩ5;QM[@c%?$Ysu8 ȁU,[[PfW|?簹C'PIHҷ(;E aPrH/\>WMd0ߊG/w﬩Q0E= K3OG=i,I  2Q>OeXμXq2R, Azն55k-nq{(W@¶nFIJfKp̸MpHBd% KAm;PSWym+dBլ=(s.\\T=&\Ynq! PxiMB}RK$‘L|@3u47__ q#h9(Z@(ͮT̟_,ӑ@v]?cץ-A9MIodW7h-IfG^@3Ul:DmolQH®RH[\X=Qrh0 VЊ_C!\M܊v7Tc9 DŸƐ%JO+Z=[m9>Vl?"9Mi6.06[Vd~]_M̙t7c/bOB>{Rl /~y5gRTۖF2*cE\a;64ey;u $p)P8}/p-9jH$-Z,QoI*<BFF 4W`g{^Y2.Z_~xv.5|)F0G֡YGPD-avb/̈́Y, DžPbYjk.|@I0aP,1T f œGӅ0z,<:RT,csqܲxk~" fƘ%⹠(kPM!|K?:k<$mƬ7>(l}b.=J踧ixZe; LS%bJQŇNБ,LP~]Ф@J2Ek&7U=!UAu4y!p sٛdgLC-Y .  h&>Ona}a%RWil=wK;|HSUüs8::w~ B5UU"TRUzV*n o-q?1 xĜ{UMK9CnAMPJ[Gg> G̟;3W^++cS% ʮxa)G>ۂ>IF̟G֝j~{7| bUR9 .Ԁl8H?YxZ")`} MV-#2_ZFw3E$@-F-%)Ϭv9/Ziҩ]q:_Oo=I/lߦj˻3{FtOZlWRCtCU):߇N}UoS8GGѭ@pZryU&>Y, '⽰T0BcMP`Kmx:ΊL:֦%vz',Eaul$a{r%8Br'wyr_^\NWC X2o5Y'TKMt&{Mω5 A&ۿkY4H]&1 Hxr(-'RAhfy0q2'X:aV ѲIhj4s#셍OQC`Gx+@c:EsBLJUBND:8wWόA)yzvjإq-쮫m`]|LUƨsz7xNzNq`dpr$ 18to[b{|O]+00aNJ^Ux-xR0럐/')l.FʈfD<Z/ nE}n.ZЬxve${vC)X,p+-0oM;xM75lSgMC·R˻jMvi]]tڄ.ξli7 vT?1,erѹh惘á"7 36^&:89rcS H*HGyX^\vx,j,ir:/Kv+ 5Jؕ!؃&!^p^vg2y-UasD9p9 R|ZHHأA6Ml؊-++LՅ/ yíV|Ul\y46y'{&8pyv]5qޡݭ&.<Zn>>v.GQkrScb`$/W^8}UE(lBwĦ>Ks+ :q5~LU`%H/g s:#~r2I}hYd99igģڅs8B!_\&_=J2o VV@81w{/h3%1%Ķ-K8bqp㉂=-?l0c:l· ~6}q8#t҈M-}Pdmĺ*,w\wX*jm#[ss܄T?bȴ, dsCY,Ⱥ4tkm)npXd3k,E\K๔C d5cc0<4fZS-%d#t16dA [ BW%y ^$#0((`h9p]T=ưX e, e1p<iƜ@Kxl",P&2*9$jHNt~ޅNJGN% @.BU?F;9 (7ɅXELttXMǕ5u =YC>'Sgr\j02&"*l$j"P“߲O+0'ph]r`k%~3Tu5鎪F4a"<#UEԋ:1t2͢{}rBNYnS \{KDFpI"!O#\PM@dzhi֣p'KLO]N/u8ܞ3qp?w+ f֥Fx'YN )Z/+?fl&׎890 yԛŢ~ TlJi|TTZj|F{v5H~;61~(Ld]#͛=7me^awpCfx%ȣ_NBk*arKDC>}7 A/ae]qǵM$`,lQk}ėeJ֔P~.j/ C4?:aQQWӋU~O^^<}ڱy&FQv 1ʚsc0$[`$ź^y SSu6|wxRДg~i34{ܕ:-L熳*OzJ)l9[ rMtKH\u~@OiĦ70a=nۿ|0tVJ瀠Λ;m8?‹T{l VOy,#v@wʵH.7eUyV+fp|0 %^UjDzj2foɏi8䑤h惂EҺ]͋v %p\S:lDE1ctV0^;߆!,t:fVD%1_G$F RhV`N uB>$S{nt֛(K0y94yptX85Q9:@nXL|'ҼqTΖk(:+B !Gvfne^ SԺ#+4hۢ-;geQcCq>PYH}$'w/Pa;Ṛi^&{\!ˮ6(Míz7ڕ=EFurM .L'ԁҤK S|5WFޘyo}Ȱ/69~I˝"A+\lϣ™<7)n󶬗XLr:$]K'V^j7Scc8/WL.5e鲐&+uvb*[_Tߩ.lRàD湧|Mg˩zFy%^mS͗V97:aÔuu@|#:)Ra#{;"M_j]/"#hNIv[wIjoEO&r T*rZr)E/<&- A#K<DN?w4ܴ`EbVk;wvamա.C(RMX?DxOЀUET6YzSpxպTM.*2,.C9ܓ(r,P'<eV:%/Gh6%瀯 (ԢN` CEruᯊbe.|LxW5̒ÿ={y$  b"Sήʺs3ecrXD=kE@= hhPZěJygsB8S"`p XU2Q%]@lƈXzXOxq‚&Nu,ΨϜmQQ&^d%/%ԗ2\?D](/μ߶dfz<6%#&'(DMTDė_CZ @;U0B +z=S/#F^KqKK"A>q7WY7I zv?VN98^vِ{i AQFQ(FT푶gHh-.&3X >dNț0Ke+b"ށ>?Ʒseh"'X؋`-?3Äp2n(݌֏i [-VKkvƣ hSkXVTQX}_,WT8W+ v\4rm`w'/xui-Hʿ)#GjACrvGrqb9gFiUJX=b+C!?,M]7$)kO;)`RiYȼo`a],JAYb}h ׌/w_7mcQ>DsӸczeW#.FFl$HE3$,{;Akn9DإnYcyoGvz9u^#toE\Rt_HI+i9[Ra^w~"-w:6%U1HhҏĠ Us\#Otub':;p,r4QO*8}TTEdzyױJc V<|椌O R_?~R~>"3zK}ѳfyUON6F[#T9C;qSFOr1³[n}KIn-u}+IpЬ[BPr9&dsۑ%l^4-ʇL U]F-Dx. & ff+S*2|Ux'K܍`:87r~"0>SgXm{uxc:;"=s.䖏 ٛArCzNJ'\KlzZ1ۖp^#ނPARUOm/9G-~vBik{hX|ƥ>trpn3Hsx媺ZEj*O`Q-l(qc]*tQ;ɪ-9TF?sJ܁!D۬Ѹ(#1%aAu-&_hs5wxsaԂ~J;6VnpQPɝ#hu@݊JE. b+D$.:A)a(voR%j]+lթ*.t #KJ@`Ce G'6NC@)w _XN3D2/ן0!Qi+.z&zdMޅG,(ԡ"0i)DS#^+b!KtyaU[G 勶9ҝo@) U9a M65GK*M5 a2CG><єLXoۤ9{(Znr9|Ld:&U)cYz:7a;ꁹ+n_&u_FœDPO Hݘnm"0iR֪lUUtm= k-m\ĸ)BB!M>ǑrnikOA>>~ Bb9FEh8vN+ Q D0/F9 PoH {/u, (u,Ûg0OO>^EA0iFi Ѐ<9`{txݾ^iŸWl/U&#} 6:>7&jn" VT{vhЁ&\Jyosd $ޣ}єJrWe0kR9>|R_aѿ<=`x.[1y-WO֍QBN!%ˊ:cFL>qқ (#glR\hǵRmAh[<>8y~ҿHr hpˌ56m*&u3g_ 3f-D|9b\YeTWH^ۇx} 螝(Idw7!ḛv[z,{x8-a} $Q捱l`ږ!!imo j0{BOB=ͮ+nJ0xMcTFrԪ)J+FCلBs!SizQ˗O+a+85+L(n1eH6eE>Ș=4J= RUlczR`\ RKK6GPBb0`bU죣qBa$j;Fˆ#jhz2Ƣ˞d[ ;éA>toժ۵F/,t&,)8Y$ؑPKolXO%}?celery/app/builtins.pyY_o6 GX= t뺡/} !XZm2TwIIe*("QwǻzEnoH !+Ro2{(퍐3{fvEiNXxgdʒGc9FtSZsJ8TJ[FԿ/I-i),׬43ϙ& 9WRR%)H7BR|ڊҤڶL[n)n(mA$_"TfmW aEA,sYмLE\f~Ij bqd5M]ZnaӍ=j`0Zrva{j̊mb%)XC%IjaCJُ֜ȼ/!~ik N_68h$faTd|IJ '=I.$|NewYׇ {}ƥFyUĿVJǸ,OB'O /7Kr>EhH!ȓ[/:ܺowDl'DTSJ8^)@ײT;KPhi"(2IA AL!p=;(1W*K3HgQRP %ח{fr`͚S#؈?BkJm,K@$?,.yASCp#ZpS­ 1 9Z5%CsH>N >dcLJuIBE$X YBgHy罧@~7,FI#DtaR׌ > g1(#eC͒084QX2pJ@:$+Q'[0I;W#!_## x Ov7ddb'P5J]fPLXwyXc 0=plŻ4yӦ6rkG;QLK T-md0Y!6J`QLm\vF{lE#9*w^`:pٌMOqb[8|]\ΪŮH! vZ+Vx Ѻ@#%?n-6bqf@\ K*'uBA{P\ؕ6t2;L#? a01h9 GqlT~F{3a3ށv+6˝TE\N1n6LofDNj0AvHlT9q[}Ԁ{rw1:T@&N ~^/CS~:H`&{NsFIXy)E`–k V1dSPy$=:ܩv\s=8pk(m 5wq̣SkU{Q+jŴYEaK#':IIݠƼiui?T+ ?I zv \k7[Gy7>:W,zcYLtx  RQ\S8bX&F30}5#u 򤀜k koA=؂Um ݤ6:b8d/Y}:1X7ϖ5kbU,)G)}ч1M$鹊\7?F-TQ_(47LqU?q.C| . Y0|/OM8[=NDęyE"=~Ї016@v1~8|𖤡Nn xHZ#G>W#v`qY5GS ӱyֺ>bJ6bWIKu-t{+O7LI oPKSBqP%;Acelery/app/control.pyko8Ar> ^n6~2-ѶYҊTo~3CR"H3Fg)=`>bIfn}|2駟ޗ[q(`BeΞ(T4 -kv!k Ȥ<xhNԟa<;T8WYYL`C.׉E}4PXGٶ.,jjHY_Ȁ5lf /y]{Hmj2Y)- 6b7 & 4.$eaDYJ0#knjTq:qJ^4Ul\nhT#[39fCSa/oa\%cXuA],$bK>-imQVl0 qsm֒A=# ,ɹP{ΎyE`#2EAPe!= TR[G!UVPZ! FDg yɅ0J S<;d\Whu1E$wۃ+4?pk `11WeeAq!mT^Yq8{VS0{Te۾]:dIB3)NbxނA|P1lAPϕb'Zn։ؕquƗk Fp1YY,4X2ЕL:vVq`\ [ n gӏuۡ!PLLN8]fj3%\乼{=zw\{祙 RThjL}4jF֍\=9;"y7ձ୦3 EW JO֒D*\LuņL 07k (- ~?3Plm~C1Ů|| %9 d'Y^Pi@Xe¦U&v?YE`%g+m| JX/ӏUw23~)QA3l-c}de-)ބzS29,<.@Zsv1`:A5qDNbŗ ~nmlM\6UEpv!Z˸q]Gg |]:tSFF32@{݃B}э8fәiKg#~WKs|)pÿO q&`~8QSkHHcF}&8LD#W3ޝ"Wvӱ!;Tߠ7Ln]1?3gkܥ:ؗRс=[!|C;{jP[Rz3mУ϶9W5llLmSPN gll#o1.ǔ@/%QӽZ+N8ͷoiYߩ,#VkQukJw,1:#&3aoccH!=j0BPD>u?"4ľnjPb4Պ MbQ ȩ0 рyMS_qʃJ ̼0S0+F x:W v!Af&+0SnTJ&+]]*1u) Io-HLR|lW) E'ٮi?v[Z0=Nl3,. - 0F hqAamZO/U`P#kb|nEFeb M3c@[6} uۜ'xPҺ`920HPC[|G vq]gYzMӬ֥wZkd-Ce׫ROTSוb rʩQ[Ϟt/_Aeo^8e6/9II"KONµbb؝.U\Q&=U~KGچEbܚȝ#X|F6& ׯ:N-0g^Q4}Ix8HX="Dʺs \{Z})/Frŏ}9.zc1a2LZw$1cxٮ'F`I=^g"VoTZ^_=N]v_-:ҷ;FٱTW ~?R ȠRsbg SeVjL$1ODxM}8>Xׁ$]YիY{N2;'m;"q)_/!T)5|)Qo?jf;諱dKx1 ܲݤʃk/kҶ΅A"͸doJKɉK|9}G˜/iyP[|;1R+Uxlvx{X>K{C+7O~?N4QۖO[ }Z8":^kR75IxY#~[A`p2a{R(!zopƹyu3p嗖t/ezbm >QqJl6_ͱOA 𧿥v17G4PhZR\Vt v{.up20SBHYf..5C+ ǝ@F>7*P0n$0o*{N8-̼u8Ѓώ_ٟ'v?PKQbP t{tz8celery/app/defaults.pyko6b!M[Mql(v,6)"DȢ7Ç$ʔ`[8GwQ"RO |"y%iEB,w4OhjˈYUVe!dѩYU2bQs`HK&i ID.QI犍.YZVE dJKV%s`;eYI˘\Of|UץGl ź\&ND, 1gj2r-%[ 54_ KY`@2i4 Q|yke,i5)d&rJ*K0DWXk\PPcT|&𛋷g!F\HtE5ӓAWWHn^ew{xr| яpdI뇫qv~~ᆜ_]\\mI~=_W#Z1R}5*A[0c+?_ )ԥ%9ܠBH^ G5w>gհ[{SbG`@lSIFL '0ɧLE4:׋ѵԍh:kWoS@%u o(Fq9ЖnҐK7|0'(o24ZDqwbZZ療  yIPl6v suZ ӱa-`9pc8ZѬb]n6 J-Q3Rc&ʌ7tղ7w.9B$&gcq'z~ujo/\69W{ 0D4mƊ%xI< c=8 # t5uY$LNEb9$U8YՐ|-) 1@j@% VY- z sBd3Ci8 q'ҳ\o#tZ 譒Y`IFS7􃄐?x)i؂f$6q^pܔMT5zAQqtpZ5<% |P}jPv#t><<})l-K|O}WBᄂ#,GYZv xb.Ҡw(˽|)b5/Rt>-%4хiPat+n|3LױX Wws5vX 6n^ WuY- UЭ~-q'06٪;u׷M] tzd)WБs, = }E4\W[[@L݃@վeu-5x יtv^icvuW/ J,*~jPabC=L@MǁI'y=tNFiL\wm;ryQBfݔ/z;v/ه/'v|.gT<1m6g&l6j $KN?lx`P< %@ JT)N=6!)3+RA ;zBr>sAA2tsq(pMFw`I,#} B+;x fl3f4ݥjloj#UAKuƶ.>![Wpz)`l%|QM3~ޛiUbmR'aqOc,ԏS)a/P-X&|p9a;yq$̶tA;jn u7fx9J `x԰ ZJeiw2UL7xi"zMJ}AOZ%ff_=-NPH_97dM¤Ri>#&B7N2f3is8U{6q B%4p'g5hl?{6}j [t&+cցm})]`6,@ǘ1 +\% %HBB@.s[@7r{C0s8pT[Oڊ[Ix4 % uK-BoL}^O52 LB(VdAP4"Л@JK-vYhHqv1odH7 u|k*@oh}-p NXa> Q3̝>%5@>Cۼ/=B3yn*KؗQ5*-j;ziYY q4fpv7ARFJ]cԛv6?J-0^07Yk-},6Qc:iݎk'9mE<[(=gz@R W3/fߓL<`7`o^KQ춆mܵ 4wl}F 79eZ>dZMoSj~<':=~C0Idz7*wqE~^/|_5cZI_÷G>WTgxxٕPKolXO~< pcelery/app/events.pyTn0Y{%? R7][ I6ϛ3g9c~PЃKIk,> ^pEy6`1" QK-i)r岔)+Y/J5;=4x5oc碃 `eP9-=KNiJb,ʑ(~bf#=rGqx?5n)lEӥ0KAq2-j"vVXԅ:^ش|^cע{ Oiֳwѧ:-]K>b)־:R9^ ><(ϚWOEA@ZAS=ŁͨCۢCTL*Wv&EP?=jEx¹VF<ŢC;2X— (Vޘ+mb ]}t7I_^bfơB74ѠhX`3k@:(x׶(F4F !MPKolXOFE{ #celery/app/log.pyZmo_A$,oB0`ll KNР8hŢl]dIGR3|I]/Xl233Qɦz ,>Ln˺(ddcUHY &He قך&75=0)H:ʢD׆0N:' sf˳O8KeI'Tm'f+U&Y{xU>U)(b!͞PZtRRۆK=$~NK1 *1JmtKY]I__M4KNHYd4gEUwwccmzR RlAަՒLE q)T&,Ŧʄ–qyZp8}^LF4zWC\ h_yv+ѴfBN  Vna8ɾbX= 2f#!Tl~-VFaw{gE4'J\dqԘb:L~Jo d F˷ChÎ~ ('C$yPpt&Obsܣʨ2O13'[M9Xo܈Ż^2seP6^1!-(84/7Ҵksh/}ƴ̿dminb`@LN:ӧOُ3NKǬmB%i<|ѶUQAND>=_~G=IQ+J Tj4TI%!<*lk ޢjaeUE Ts^HҲ.2-/Y  Ănk1R]p6.Ӭ‰Ժ7򮩙"blRc]*㨴)f!UJQ+ºhvGB( TRܶ<셧D"}xj1uL `Y jDuz{%f3)BNon,;ry@ }l 8fSS, ˧qZҨlL BjoA{1[yufuu)-шOt2ƑP~G~j<7-jqN>|xyVx( (gY~=Æb]bgXP22n2+u;@(ދtxk.,e1Lh@Mz F16 zk8 ,mBy>0CZ4`6aƭ~N(-iTیar@_ E1@CėiI~e@AYT5=|>E9lA҆r"kF+m};1;6QM;1Ul]V!۾MNIm0<ʥr؆~(IM'QGrMTRfKC7VhD2PwDoeaBd~\_RM6s|$⼁3 M]…Z8ߜ8$zaopˠټCoSO_f3$jƹ;=3 c9V.ߘUTep/N^f/;Pȗտ0iT;BTb{H_tHMGWLSH\ F4C;q;-%96bBfw`2q{@F6:#Ed.2j qm!̩pgggq$7Y} e(Y>@@+NWs&ՈyZN@/G(leBKIa'J`F8RHgci)l&e-@X4S 5é뀬{C;Zk,c?W&Mu|흈h{FZOWXl0M+Y&թ/zr<:H %&ݠM7 5Hm"ʴƀtA$PsE߰=Tl&R#gnY&W$B0I֊ꌳ"R"#(<p=0wBOݝاinpKu޼ 7̔4{BK%͆[B=q|Y՗lzl#l!TkWht.~)Fuݞ1|>%kxj>vVgcwR'Nv#RU'wb8EKɻؔ*>mH A"nc뿚#H?" dKob~caj*GIIU !ڴxB'45XĄ]HfbI =eڼ?L_y)~_P?A`1Dމl7w uU%v fbe]fB) |ZE :Be ƧK7]}=p|<f-TX ]cV]' [RFnAAG v`B:1Ea\h}o s nW7!wPKolXO>,$celery/app/registry.pyU=o0+`PСCQ4CNAA!BI9ޑ3Dlxwk~Bn Uvr$WWWwxRL ,G{rNJk*l}kQPUcytFEVV.IHUsgZ<&*SFeyq<yk-^ȦYDLYjUS?5vVb8]XW]55t9Vu$yZܯw E iz3>C UY !D{#̈́HrOeR9~-26p9, K c4AhJûg5dM%ʉ+@P(d=fa9fp++us.թe^o %de@՜J9"ƩʼnH1|!JrɾFdaUv+H:WYi,~/53!j< Qɔ Q,']A&ij)1$vBLV4WnX4%׻,=ڂTjo`9 Ì)>{#9.pc`!4{L,1MJq+z'9#]p{{gRC5l +p -v3Mu0̝ʜyj2:b-VxAJS}u?ih}BU]@6O~'GvtU>=וDJZhnہ,X9 s/>_؏Y6F@MB:8+KyM<,l) w$,pJFp@ׇUӾK4Xd0k^Zh7[*2@= l>a^YlqxTL*˖ +x#F:á'8y+]QT@X9.f@e\-߲[3O>}i6޿yfn,[5=ئܓ:z潕^:< wG^QüUۃ\@I hOhFk/s5a6B%%҄l16XKIF6qR`o!GFsb,eir$Yz.җ$'4!@*,-ҳħ+c~TL`";YZhzSU>7HyeA*s.>>澀BU Z,7EO #o<e8Yut4o#dtq{ynxҫ^YGÍw8䯡DÞ δ]ÌKFnSP2HL}Mɵx8Z C +}l#]^o]^Wd8x)'ƪ F^5%Ș_T8A}M>ƚ,`nE[2)i,D]K̉Ȟ`5;#{߽8Y]W!8@G*oe]Dćs n,v.P2Cϓ:LqPiHxِ-IlW8` gC><8yKtm YBDWTJ uEl/~ek8 i -ļ餐F5IZ:$Hzbaeg%"YĄKMzPUY`;E!֓74@)[WS^ȦtOYQS2M[/W.BC!+X!SVy5Jaݭf;NV[y9:Ny7EPFvY6jKkkVFlڨb$Šj!-w-!xxU1ڟ׺Ncz̹!dsͷ}4J?^;d,}ezv8 >KSO2wжH-0')4/g(41mDkZ-d=cPl{BYg<#YF o?h[wf,0~Oþm/rM{8~׵ARNΝN^z0^\kz5϶eSjr.դsHoMb76-|(. 9ZGsq<̦ܯ< #...3x3m.SF8{EYz,]#..F^/7y+Nה|j,-Okf_4UmW Xv&H&Ï۲2N_ ECǡ: }owC&Pn..D-׷%#ڱOY>/2_gWLH +dHey1kwvuqj5vk^zT)bT H>d p>SP?!">kڬnalRk C9Y\ a9Kh_r')bW[LfKjѧ] &Óz4iZ gaPمSXj85)7vU4O/MN? ge #G[i-tr Ufa}!Ȅ#$5Bqt# b>e(*RObfd15m r;SqfT{yW҆:}6 y?#j U]+k2n S\g$zM^=g pp%>/Q»?$])my "kX׶CX2ܴql{Z 2dj<=Q\-DToh_Tb6w6 \2{Ӎ2eE=wv_@[72ܱeeJ})42i_ֻy ~=bRՈB9 KYpR.`D fX |SEG% {S g-u NTcyw??bGeF{@+zRF\BD \F)Iclܝ샧fOHu%5|"KOcP,F-f^ie9x◡Ŧ!*@]5ow]n2y-k0(ǠPO- &Jќcj sg37b~jKsP'[#SКC%;rKT(-H*IW9ˋ)yu8(sxP'N9٣쟟XG ;"m%0 4Kvʸlͳ'O>'Pb2E)s(B4s}Z1#jY}!B)dEwE +TxDJM=\d JH#Q۲Eu{bUKUWŵ7i82Pb&wd m[ t @[G6\$Gdq,`Ub2,^P6^wE3QCg(W^Hiw(J.߱Yԃ܍5͡"9 CDCmr צE-!F'|@\)v3DIgtPH2pQB8c@9Q` ^CG݀ژ%B%rb'ĢwZf<:tYC/Ue(]QCM)K‚m@Sff'HAHr2m^T.UF@NBd;0y4ॏJ水T 2IeDӸ[ȕ s6!yۑL<&+Y0$*5y%)7ZJv [ Xw:{-)qODZpP]}|f5a-+ݏ `S/ƺ,\WV ]d5lQ/ҭ5oEP M 6zunZB7ֈtxB7m[CH pcaJ=6#A=Q2&g*NDȶj1B *ժ:i{ 5 ]\ePex] $ iJ8Xp."-X/zSYӾCJnۑLV|"5Yz>% 4 HAz$^Xۃia{% B>C%dJR|Q@ZX/b f$? hҐ[T\*`3kXH'6jCo\hyVj9^8 Vy o bs bT~Q%9ov}Nr|Ӊ5wfW 8*^qO?3u^#NLa&.X~t )gz<#*ܶ3v[* "wg]?WFޤ.~p) ͍( XD` 3Y#" 2X8>t f;%tS.\W?m} W /XpBpslQU1 ']Nwn3,[PwPX\PM>&>3-r;?jN~َ>ڴKJ$nGQ$31~D9u!D3qzh+3 arzζՆiobEBYc].<*@"vx~ u8Z mw& pP=yČx+v[cx| D̉/B2ܕ P4lՖ>c[W@7 KABpUwZqr7X+%{t6ކ#OStyE7]q5xo*]β+wH},:W4;:Av{^nġ),n1˾h#+@ϒp?[h𫘎Utf*O{]/4Kfj"jH.xWidC\9j`R?왮+o]q}(xaӺp9+ 3}D-:< i*&xʚSq;K=&t15 i q Px@7t<> 1b h>2! лaq w_"Н"v3kR>T'8ϛ(#$QN rR)&HDd;1 ljS7>^ʠ@(ls4k>#ls5SOQ[u[lϴ}5dbw.5M8#~/`j,[gfc]tqbۭCp G<{(7uI-;Q N|KzIU+/ssrcEzA-[Gd5@|?;&G,vvvѠL )[T5c6ADÓgq[Öœ/]q9a$y2ޥ>Eơ *cW֊EJwqIiU7)Qc)II/@#ѕ\HP)Xo0Dῦ-VFh!H2 >J~$@ $7||Q;.Y-m_I(!n `?Jbu:b/{ѪD,MdͳŰc:KcAͻdue2RHO IH9qnlnX⸨[ɐm}uFC`=&Apos(cio30IT&M%!LGrwUːqFxAIlvAq`D=Cti(=hꊂW+(x#Y;A7A*#<@l NVJ9= L 16I7Q ~ n&qeGKU1ɐfHlU=gO2 ; sX_\s Qc f$B#.4THQ&VN6H.J4IA5IEOcg(u:t|0^Nb~ڨ]49N5Ka uʽaxϴ@ʎJ#y2Vs}2,mz(w-bǟ;1Hԍ4I 7}֣Rq wǗ>W cbG/GcI3A,G޴4 ,xP&M 4Ѱ΄5EsWDY.XF2Oa:A䛜* 4C"Sxw[#to1ܔs p==0a_S6%!܃)틕XbdA|~w#vC;Y"#3`ˎ/XD_0E B͇p={e B{de|T,H5ʔRHgWL/se9mIKt3W7x (Wm喊G#&o/2MHdQJp2OeSu nYLq.b9a3v[W[N%7fI|6!?ȣDn4{!ɋlR.:1(ϭB4O5-$OU`bg_2;KFM;>=y.綨%Fh<@r+nδrVqz@2<:PX[N7 C(6J|1>7brqj_{;3_PIӺ B#YsUoLROw;m%\:iFJU*lvtbP OEW D9wgbnYr:)2^d67!/A)PC5fnR{zCou+]X`W5k $:5<%M i2Y\(Nܿ0hB?,˃.d-pRn\\}+E7#\~ uŃvMR>\k'B m0 ݩd@{p1>#ZUĖH6yl7bD@1i}ƒrU+2RQuS8~N',䒸r+Sq䲽W^-Tdlo*޵[v^#d0)G'׭u>o=hBj觏 39hx >GN>W8-}u[\p"P΅UyR ?$VDη.O1u+ Gfw&}rt?GO{έVX+{Z=꿡R3ۡ{or:˜w'0fwrކ)9R($Ν,ҜTizaGx:V՟?"!iCG:Byg=P _xQWr #?q1ʭ^TO"]9vA(IkCEE)Au+isiM7rF߯}eCٴ&?s T;KMWH^gƒAvuSIz[bOut \%FX)x:U^6@ 07DX,W6E=T7=0; =e)^; P MC)&=3`F7naмBrɝLFM}.HxG~}/eZ7DM8UId3z4^ ^KcE1A+/7LbV켸7]|N~Umz!}R)75RHGu}:eaM]0?s&;89w,/.<@u1S89)?~vlFN~9^5-}۰W%}eⴛqƁ@|5 U)2qk4q0ꗋyueK鏰ނn8A,JKkL'k&Ǽ m_1wI<~,P<^Fz -Z7̤ GMݟ==Jޫ5Clwk~ |x%*<8pDYUl13骁_0k ʤʙÕ\=qˉC>$`$vsݾx]}kL, YRFDAoxerDdDvq82'O.iG',OAoû}M + }[@ѓ= 1*4FwVVPF b+*)D'suI8.,Չ2e ɶ*v*z_(R,ZkSdZG{y Qֺ33Hٟͬo`2A;2\.r|N,$i-l]"rbއ~h`]H2ȯhtYk̬`hJ( hSk XfB$v&Vs~5u%hW` *Ǥ9q,̶Plhs۪B( ZsV>WyQZ_i K [Wm!}WE]c׺Pu5P/)@3^X6PX#M]Uv_aU /mu#]J=nk; !qgə:;SBv -ءo]QjQ|O\W8~y 4H-`,H>Y/MG`}P3My'P ĀxrˋT< m6UʚCaF\W`My7ߛՉ`w('c[Tqa\MEpuǡS.VTY9D54w>(t#שi08Dcr6YͱИ?"Md&qŸ^^;NF{Vo__8bI_f-PTaxuZzIF&xג7G98`:6;V FXC1Tb^^?JP &NL:j}P>,>^'0mAiq 5v?TXv}>7IUk h{J݁az L>TݥU)LdnJ, DOf=(bn|]T9+"5C$/0E͍{͚r-T/Q 4aٮ'kWO^?Y5 &8gnazmrCv{v1ciem1hݿ^6&`|8Xa)"%Ba/.=0CU{#f0ҼRp?q؅$:z|0h֟Z:]oUx@qs9}>=$ D)6mؠ ȅib14c,/M /rACu~ Q&CpAu^?`'mHr.7 yR3M[8,.$^+TuD,)uE:B $b:ORSa=:GL[p+0x9ҡ\}&s]35Egף) ?Y+N^a߲Gz[o, d qi\3-Rl+GK~i8CZ8(o5j\eB~>f7ͪ|^UQۂ,#]hv>vM\!*dF?Ը)HM?[03߳ǾL0:+J&i+ªs<ks-N.6voYk/W.|&bEFP9KEB{ ??id?B DӦ6dx5Ȏa&- bMdSD F0pf!fRh 6ȲwNZxNiPW~݊hMj(-,L84_VH-BwE8Q.K$$`\<cԶ2‚yKPtw88NL\l&̗֒{mxS3%CQ;&ݕ ]}.MJoLJg(@H=sB (D78.Vk‚ďߊʱV %;-~ֆkE՛W`p$496\TViA;?Kbx[^YIŴn&M_uK|_*Ć~c>Hܧx .E$q\Gإ<էC$A&2DЮPbg nz~/mAN)jaBx{VH)(eUVQW*J,Ol.#"ь&aMϼ W.j6~fV:e/V{]}#dLp݆wئqX"Lh\ tXB@!,i,&CoV89E+h /Y'Z-<&h tvX5%+ LۣGLUZ@In6ۥq$"rrUݙrn68cG3=6b5ҒX (,Z:-gaW$ RIpџ,1 /yg-R6q.Q|W$6=B5;%+"9N|1q5 =ZMwzE ZXxp3Oa>f .kSc2*X1e_R]08aB@D5yyHf:J_4v^_8t0ִ>jDOoO6=O3Ի۞_m?m V!I`6]o<9lv/1P m cQQjRKOh 5}=o0#E*`c̏l,} G={Xb@ :ңcۛ 䆣lxp(+|rp+zb;8,0oӒB%cDV{mg*kw,1|jxKf 1ÛMqbk8 ;\*,=dfOm$!l'DxR;D^96y7LGs޼GX㻎J{K}Vv"YQ*/8R:B(RUlclMV^'cJwTv\D7{ p(^35G}Kס0~77is8,YLYG5>-L]) >!B n7`DT9#^Ϋp txq*HIvx>'Scq<ҘVg/VG|^ɇ͇Ѧ05]jf:?P3sL~Ì'9kY鵗JiŴ`{OO3Gp0`el$iuc>>01aw2hϙljM+S~js_MpZ LJ!t-,¨-diF)BzXcH{YNwNxޫo.,{={Վaa oήqTqqBqnRb"qRy>E\)hC7og 8>@ IKkt>7EO[U$ku6{gyNl.,y+(𱛞z#5M!9ǧ<>#er ~ЇuW Kվp0]|M?4<ftНqdy]uLB=[-p.;;vPvI]#Zq۸9{n vl[(C(gzm"jNYjQv܇`1\qwfv#۽U8[F!*T82YSPa*ׂrl>(ZQyHpdPr0U(rcӔZVc]$}"[zřfٺ[h-<ϞQZ[|nCΑRtƽ+9.vO֔ (}p[^A.wLݞ895F?쇶c>P//f8Ѧ3PޝՃRKL m2 -s+ag2i}~zѫ΂W'xA+b]3)XU' VjG5eS @'j,Bu H hSeګFDhwM7{vr!z^N ;-J/TvqFJ1Ch4ݫZ`F=!%ό7˽K +KiSrWid"3TaSj$)Gg"wb6l #GMJ-en=R-TO3}L-E @'y ={(z7=ToKӃ?_PKQbPg1celery/app/utils.pyks8 )Ѭ3&^m}Lm%I )QӞ?$E@6O0^z,'?Hŋ40 P;K֩_\}6+'H`ED}1?Ms0Yf/ʢ,\?˓,}V! 8彞LrF~H5s IjDaVEPiT-"wؤ6 HLa'%Y>r" 73s?--8|QslǹH ],IEmΒx.A3Ac"U߉ ͋ A_~T1Kn!k>2ЀrtKDS~ C4 ZM8Cg~j7Yن{B<F /ͯߜݛu2N/~G_~Ƿ~ug" 5-8pz"xm[?Wғ ,K29Mϯ=6ֵX9{X=!MJb/)٦qG8Հ4Ú(״B,2|7^jIX0/0Dsca&@G/s= a~/xt{:\8 ';9Um~~m5/QYmn |%C*<|x[lNd`ȁ{޹zߡƉp׋?[i`q R6mîۮʵ_}J4 puFBl<~N&nëh\N&^ ƣOp2RkHxtc#+>jzg~'GK@ ``BTˁ/EڊjSP#DMK|Ci6{U-|jX7_`QtvʏdA]j(.N9ÛH,ezֹ~;2(,izL BلBt0-pd0i)/x.+W~~Pr%H]0ɘ9>r <қG~3Z|N2wׅUNebMEE ¼@7 VR3>[}jq%Ϡ'?]EPz!]߇(llf4ۍa)债ѢzTAaǝgx F>>SqE:m<Ϫ|D| R jxiTH;r( r,4X:X'*EiB|YRb_ŢqI#L0܍AI\'4\l|Bpo4ycyU4#&jF4j  KejC)]%.Pj*ѡv@J <LR*7ݽԭf= |v E ߀*3֮ 񨰷 RֆiBcF&"KNl 1XG|bԀWWic ,v3z&/0l qxHXG4YRQ +%ZM׺u5ooUImb~U FQ\ot*Ӫ4p$@is_VdK`ixJ}<\#yWgHɮ\8j"㠠IgJ1(9)3ɢ ]s{Ʈgb&&wNw9;ʜy@YO֥>WJc=LQ^ݼ4n%1<0=p}1L 2[YgD\˩{Yٿ:lV%,ϖf ~b Q%h'; Y 8VbB)+ F)c=E2ܜ虜bL(m Ree!>D x|nS-m(OHd7Z~;b8yIO,S(e8W4C$,."34ZȲ)-1k-}UR쑵P, X^K CJ} .]F Z~Zs(OG[w:vlzȾB+SXmƂtMC˜ #:Oh*$cnE A\?EEMNNKwZ\Y 8 4\dm:T~7-v~6V7}֬k~u1fis/1<dʄS;n"@yḌB%h`ƕX'JbB.?R%\*m.$,虶ZtdjX2j[ZRr)@Gj2B͢DsLZZ)Pm cp- v nuUԈfiWa%V0B7&q@ ޑWan"=z0h?8Mi7pi ݩT70]9`[V2}$yk+, Il< b{=&55hu4Uxh<⯆WWAT@ȋ\S c7 ^`3N_=r tlUּM@W|}B>ڠ^5 X O%@Lsuu;4 k}xкNjeS1(T!Y(1n)Ҏd֬,D,Hу˦D9 .}K-@bdݮv<7YiaCERƁm5dRǦB5nSF|FC 4zՒ0}ro6ID`dAhm˭dE8S\*UY%a}Mփ{Ryyz<ڲm6iڜ5%wpVuUmw{!ڵ;Í|GoYz-52$Txz}7;Ck{*𬒾̈́xUt']=ft'w>:@/RhSqG;nG{d),NQ\6dCԆucKķ17<[Q)m_Ɨ\8*+&!fqܴ|AXۑk0~Bd#Sj`CX:ù8YtɠJwxmFQ3|*A )kU~kYWw5!8R_&3(Nqz}(qrttt #7Woggߐ_bvvB}J'ԙwf=!ij{:gbXox:q=)f?˓PKolXOuYy=celery/apps/multi.pyko~ <; K[r\{- G+g$Hʏ}?()m #Γ:99<0wۢ),zjlշ(Wq׳݂+(7,ö(:.>dpbɒ8[ DkmCj$i6 ky^jQ/FQF_e]uF MуYMMFXΫ<XGt`01=v1-L6 an7}[ol#Kb{_fCNSU5D1)Z=WOp$4=V?0kh I8e@iSZK=+QVn~4&y6kM:s`mX@\.V(pir=4+' `]H%;ˮ엃YAU"{Dk+}u- {{Sй ͧE5!K™.`p$O ek@'ђǸ^M";P$1ѭB.=}9j 2e&9_:mda#Ҏy~Hd#6Nգ/&c^4;wbL MEUt(ᑧ>_?z8]UJ'yƙLMu(W#XwܐǧMKuV8VU߂g{@( H$y۵l\b%܅ "@ѕG_}o[XiPd'_Ai\;Q͕sNNHl'òV(VDt p]*&g?%C4#tҐLcmϨ臡+}_!l_T&o{/ G˵ą(̘&0Wsn!p< ꕕpT- 0T9؇qz#M[pS,Ag@(5OJ5+Ъ]HѨe45xiW.޹PyNtоQmVLo,9{ PIjKIfKY.  xpBH+H[.$~&[!'%r,μ[ELvά s;}ܙ$4=?UbE/c !F Slyc-H%.72 &'($bMH+=~LPDW쓿NT0n<:KzOl(+t|=RAdYL1H4df}8.k膝\4&0ja] ?CQl\6Msv gpAX6+wWE_4kxX\mSo(bOrV4IҨ~ ʁU^]Eı$N I欋T\FiֵZ/Jz+%9%")QY'g3#NR*vd|\PVv9]^qz☌4''$OR_aETtLGr:¹sj9H#(} y)3N4=fוX)j^j4 \HNr7E )N#nIH]cD 'jI3~i8AJnn]i||,[Y\FTQ=BcMBe2=E`#)hCuug `j Eɷ%M٥U@:H$n{Atv^Fe\2AԮ8@lͣ(r߿oӆ,@o ~9u$T#v\6$2^2=uڦ"|uV eƌ4`v&΀H@ێc^N +2)F`ݎiOHݚ[4a&JI]O> tS$&,d떷UQ(@H9oKq$> h0U(>B<$Ӭ>%F4M~ $ *ap yLq}i@+u,8H?D,c5(ѰkJ.%!9}D9*@2WGtDi9,+hs߸ˆ'෫5F?r}VX5U@Js lG/P} ,&t`cn,ۧ&ovcC8-RfHɨ)+{ ' G(_H,Rn÷^!%0/ -.^] V˹(D;W0 o_Næ&1]VhxRkh\j L;d-u2P/V)A7'dA;>@l|&K^8tE~ ^3rrjlYЂ= 5M5 ئ" %eƻNk1һCSz'<@C #.`=u= y}y竿tჁA)-mďVHr!џ}Kݻ;|߽1zeS8'q@e:JH?R* Xuٸ/"T:x(QI5AE Mz1Hr@WnF{ɭ>k ƕ1Y9" \Io ᵛ\kr_vn {=;[mAaW+cͬ/mE9ByX]z$ÌU#AMYU=<>{tk1bPnҨSdo&{?jna|%r5ߵ7Z \4mxU Ű7bh,b_ʋPKNvOn21celery/apps/worker.pyko6~ m*+uqWi괾Kv=8˕kzlIv~3|HVk;A+Cr899xq@&/՜t<{F\s}U,jN֢Y V%Ap~UHR5yWro'=BMfI3-%VM ?nIpI8n+X9:苀 c >dԬ[M{LL Qf21_<  1^5Eܓ!}l-K-0ZCEbu$ߢl[Ak}[9KpٕԃK6_f/=%Fj/︼ ^W;s@Pk /ခ,Ym4+)-uW7+0OhS=DO$uV2A{}WUp63i6Q%W#堢c>L?U1YJQ(tx-КkW~DTla9puFS ]2fR|*Q@w)}Tڀ?˗j4䱞 ,x N:s-`fҭV0j8)rd!ڽ%jǀjG+C/QJq{+4-v 5c?y3$,C b[3cl eغ+nV70lDz<Ky EZwi JY>8>OZz3F\]fUuv6]r61QOa.O]yk_}/4h5ơky%S4m=rw x‹bKeǵSt:65݁&itsTl7Ygw`*pk#4_UV&17FS;H1I^(MTaZgh]<հl lvʊX[CEX@G9dQf[l ]0Yd#? Q֙e 4FIHp "3e1J**#x=Om@Ze0L D015&-0) zFL }'$ШRQ0H^"LGWkUlpFaNp9ۉ *&Rq7QuѵF{gZc!:K~$c=t-E Xfwۅ>F^Y/tнdc|?*ҪgUߣ}%PN9͑/0-EA(0U` #7Hȴ 6ë(N$skVѡ71H0\M̌J@l.4}e㡼+҇AYH4uVv9([08rwJdX:$fW7eg:@*ZJ]whl\qY2ށzSrؕ Ygke*|$ݸiI?xt7ڲΎYqlb į$5[[zC;ݞ{f, F M(/!B@)Ršcl'( "mhә&ݯz]tFHnJ~I6_U\}~9(B"ɬ1+kr$IU=,8wn0"ecwJ4EXiiV C{ 3)NJq0 <[_؆{wNFp tH0jHhTY8D:9tr1GtC%@p ނ_h$RWjL¢ e(8NPâeow:>MAYI_Ƒfe.4$UmU@pXW%. !MYcOaRuj⊢5'-bBr( F|%ؔ4@{<\T{>"ǰ#!U r rhzhT̎oP[Uyٱ1a5ϊ%qzq ?R>bz|'{w6Qv̛64等AI}[_L6ώ{Ut=%V|C Tn,Nhˮ Ѧ>, KV6stA]ͭ:"9Q+Eyz"ՁX\}UWŝ1LLp!o۵|o-VU/˶?|3? *.PGF}CvawSv>T;Yb?h?M+۫2A?qzJ%B45淦>m5F " ` 5ǥ1K-?W5jݏm%QdPV!9tbl U Ǘ{B4WFW#4*w*!߅@D#`Z]%T]L0S8ABbt2~?5>6? &} =Peig{'>Y9ZgS,Ũ'(Tt8d ?mfuE 7MLo. Aq[LZ栤WMW栅zO)sgXk/Ab+$GQ#MZS82pe Ũi4+(~%}J3{G"CSẐ>i3w[紏91v`sbajkQ쵧@Kb'mQd<9#/90Wu$c\O#/J5?JsV_&lu^ .Nt|t9y |>;'rvCOn0(2ycͤPpP < 2͚tK\?*UL [j2F,ju VLH=,#ʧ(8S3ئ rdoŹwpz/E8q:mt}dvܐmQL_}fyjW~pa#e燳C[NFZFǙ(|vK u:05FuĤ)>\4^\{ˮfN }j `/>RuQf a|ۣ?8z_Ix>g)ʣ8]AQ=SDʹBŊEXIUlQRUAЮ"wѸ-"pqqxa+sģ׃8}Io1HͬL;)߫CF2X `SSv8uWz矔 r @8dql4P!I8-aÚvcז6_6$>PKolXOG celery/backends/__init__.py͑Kj0:q0vc0RzelX2zetMb4i%@gʕBY3p)E)y?z3d8 P=!cFZ&`)˙Ž'^ْ-h,8Phg?=ZF_WѽUt[40qyK+bWܠ'}[|wn;ZξPKolXO!U /celery/backends/amqp.pykoܸ A@F{Ľ M}HWzUKFC"%jNS!J$g8􂜾<%)nAf} Gf'''FxLECV4gUmk҆eVLo jOpƬl]$ɺmښ% -BWmʁyjZL5 OʋM+aes,dk3;ZpUkfduD. XDj)2@weN4pva1e[dm.u̢WpԸm:xM\;N]wxEEpl$(@K8`˼f0,H|6S&L WCŌJ9)tJ6сq9]zGkCY֨i4ͳPh#Qt=Bf 4r\  c0gn帱[7j2"߰h^Z+-ZHhpk杮,7+3oT'[^&A>&83,YOxZ$` ul s9@O}U%%I^M6ZUʵ. |!<&~Ǻ瞆izY-?y X(Y SUlL(hЇ&b]  %‚Kn%bawξ46ۡ*q?te_p} 4V9:bn3͢@qHŭd!px8;{tY*Y&p'PC!We 2WNLs{[7VRij%@JR|DPG~ 4A`sz 'lh>I{8AWKWk0T9k 4TóJHvy:^4܂W?PA̢xۮ\lqAc8&de-۽f>[ ЀE.JA#cXUc-.f͡$/+x"`8ɫ`B 0("~ū۫xBVEH3xRb{rWp 4YRS|=嬴Iǖk1[69MN'jR#F< eKϺyZimݡ eMڧXk"MmvACu\JR`Key~vvfE .% [Q p[Lt:ta^5,vhplLy>_ Tp,i^UŜeklDZn0h@<#YwH !ޫ yi, 5%Q'%令dB;&>L%I!>/,QQu!6&ڏisA3s=c[h#^g j6,ltހQ@*fڧ7>OV ` R#v˿h4fM=95yϢ61W|ehB69o*;уm}NS ߓ-S6|h/* Y*G )[֟>}|*Aqj>W/&JW' z1GuMq_{]Ɠ4Ia<6Q:` 8bٛael7 1ϽJ![UW恇vA ȹK rZH@xJ*a FT!F”1XnJNmJ$tA2Gy'l1IQsL/ًR159恖twPe[ \w|vSW:XIJ#4~ߴz W~g vKjaZSetI{04sSqp _v5қJ6@vlo_{ԭ5+D- 7kV*e=}$?ooۊnΧ\ֈFI 4$qTjg> xޞCb׊g{y$j4{ =Ʌ,GgAbRK2 dan]#V^ha+;+._T]w G`َꓱ@#BxA-yKU-u=2~Zp\!2j, >^ƍ.UaL+t"O/Kk:^)?z06yw CͰW-:=oޕВa>:Φ S,=*Z Iy}r9˻WP GӁ&y)p},c 7yTo <:/?ƃq#aFP@}'Ǘ4u[wf|O tW*><˱o 2ucp۾Nle#-KT$m"JrL`wF ?7Ë fPKolXOZcelery/backends/arangodb.pyXmo6_Adv06̀`mŐ*mѶYRI*a/()麦@1,w{y#Kfyd1)|x"5%2DȜ32k!x#R$c'NS6y_`B=h@؇}Aɜpu?X~ &)V*.<< ﱯPg>MTo;b=np7r\ j+`1@ p)eT F0P'|rC=B.ErjZ0LʥMgs(I( Tf녀[|QMNs(W%z˧[1 Ƈ1 +jxMpV"ZP닃#{+С$c% u4*i2唯q4~b:-hjU6gAAtr`(,O"1[ Tz:V* 'l3@e䬃'zD{o7GPp{~#3/6 -vf S? .$B4k$6 zƂ̪QUl[9a@~hBGlrP=qr.͵JdKXcK;o$X-d+Vz=Ӓ(cK{u10U{KR0@6pp` r;9_Qt#+ۤBq r9WUE\'\W&Tmz;: >a雐{`~} 3<d8}=鄢Էq8QnB䲎wy5xMu^]$7p8k饓W Lpj]]070frs8=<8=&N.:5{BIŵ k;jI+"wT+`>)L-!p`0ܿjRss0-!U!ZR {4b!Аjb( []4tw.$gpKMrbO?!MhvO @0QTEv>dWW^9\W1*I# r;~?9U7u87cŶ7"{pr|]g:۷\L*'|&?tnK .s^?:QQSfrɥ3Gi s7VyCnԟiv+̚P]ړV{7K4쐔eV۳K}e)졎oNbnǸ7&pwbK>_v!{ s<G?nQ(Zba^`BWZv G^v@F+";0xERMʮp?§uPFjrvSlݐyplcv01٣L><ץw"0Ւҕ~c4Lz@z_W()tl FR gloHRg_Xzp P?mDT۝֋ cp~…w䮓a@ى1"@G@C[ &~kׅ(b&fUFtߩM}7z-IV!<e")]Oo2T_`:}@:Ia lTþ );4Yc^vưVvqo= kͯ/z҂Y!H5@mecAwO/F)q]8(IS+nPte[8ʪSK! \"$U ?\617POK!ϟ^ƣ]4c,L9CjRΧ6%eUȘPǶU!\1])WjiÛV:V ^9Db.dD{P;`@P IaBrMj15J۟|P.TVbmT]i1\ Jq,@Ȅ2 E~(F1J:0A?bHCV4Xf8Ԅ.W@F {;螈X1MqSѽW$F#@j.Q"e*V6jɆhQ55A|U?\`F6(oˊ`>wEQ5)@Heg"P7٬ g3؝4$R`4)i^ÏNl~d]rh c!W͹9⬞JX=Bc&%"ŅdVc[9sWDDsI%oT(-] g6kcMU9[ꇮg&$_~ɯ3 5+:JAݽ'="91RٰUzJsXPIF|HO=={-n0i*U%]К\-kѪW(ҒA4ȥsMS*1+g/{=iS +8"NJes)0 v( f?xҭ%78Njm"wGݵYC]y~K+ushg|*A%4_=){e²l z`A0y zWj 0t.𚞜H&hħw KjUGt!Ϝf.cXYLG^-^TP@Nc72մ4Tal(Xxu+٩X5(^` &/<_twC+.δ}`%/B ΁q~G|PR(o͗HRޚ}y*v:|VgA_.F<"xgGc-r͹`3z#ͷDl4G?ʩ ŮX͝3`?%'˲LNxB|"Y ,$w^lklmv۱~IIn煦Y f`{hSPKolXON[.!celery/backends/azureblockblob.pyWmo6_Ah(&݀Y6  Y !"IvH8iIQaV%K$L-;B׬"U$ya]WVQMyYrڞ'r1H$Q؝-ĪX]M}pJ`-WJbAgP=؎w@g8ɋ `Η(>oTENYb*K%U/Lt ?Y{Zjm1`!!QwwkAIV_P"v!lB7L*#9$<ǣ*}.&*I‹eQ4[w5(zC(I$/:H n Տ^y>m^9&IdmŖRy*i]KRP˜P8*s IeK~JPY|̝~HRTؠ, kFeZmռJZ8l!h.B4 5KULd*&8fqd:75~ B&pjk]Ò?#S)ߘ1=woqgRp4h$ Ɂk 'Ռ^94G9>tI(6#" 'ޗ%_ȢL_ g 9BWk&] Qe(HTG;޼y:v}_ߙR٩ҋ[3G^q2pphhN K 7h/E_gqm8n4s m54-;i"X')|2k9BXEaYo-;ep{E;^ѤZۅҟMu n)pilfК\ :sYlӼv:dc!sWuX+NXWW&\h}^V0 >3#RJvGgFj7`- M2o)1OUf eN׋eWt[BlHu>k_!neF _.2בPzMQ*4p#bht׮M7t43]lN8;(2lRR{:(p31pswPT2$/Cmo <_=`WacӀF>jGPKg$Outcelery/backends/base.py=koF+x6Ɏi{ڒ3? ۻ -Cʬ~f?Ido`X]]]U]y=Y\F}wɓ''.:עg+uhgђXYm]:\dka5AՍWQhl6uetoZɪXQY\(]jDࢩ7Q^]߈4Ͷny[}'R{UsiYYȖy։wkկXe)]QW6"m)\պrh͚J~{+F\w?&GA͚7,u <7PP?\}G-t.?@7^ ,~(I|o_J bZ6z_\m 4ŅdO>f+@4kn^6xJ2. 5M>}GJwFlO5r}~p7P(|f!eϏ#rOo޿K_8=|t|`i ]|7gМ 54E5Ǐ?N___ϟ0 ()aSV{Dh;`'p g9{~u#]-%w`êM>LBurQ7(n6 uO?vh!@aQc0p`ѹt;#SǪ d`!*2e[Gr e< C Y(j}ԕ\0{G7ѫgϟ,v 6YsF\bZQtg6B'c{Z5'b@$-#XCHa:t|4a8x9̡#.ZiL"sS9J 60av=,ޝ+pcPa??&|{BC`0UiN`8v,Grǃ$ł^֠+AX-?nWo~|rw[L$N=1DRHTm^ X_=|]:! T?0Q[ LlLʺY }Z#pQQ.l8 Z` hSԝ}_PD*:Uf]()J(f``aLnu=d)9HL;/rԜ F6ۢ"AXo c @cn/ѳ[(R,ǀYqX/#A8FaP5IRD  $ ַ*T2\4L=/f HKr=!Ie)ԃSoDJd' X+mJ@P)MдxVKvO)EGLj۬%%85(h$3 V])qA)2{Zj0--M]wv8%{[.1"aHnVf6%g+()fړKųB6-"7B.lS/hN SE.x%^>%(tW ,.$UgTÃˉTX"$q}}&~lbOFhIv^E[t\4!IiT/fMqu_vqZWQ#(c%*-΅)F_d$-*G܏Yɵ" # x2 8>cjT%AGij(jtL ~XWv{p?Po %sh{?HRƔ}=Σ^RaPxzg( j.1Z3K%I(3Rя`P"YLs҈ÄE̐S=/c)'̲ .pq R1qfv*bI- d6װ'; Q~ݎ ^AWB5! Lnh2!Ȟ٩|~JY a. vJETNm3UGɾ#]L6Q A'ijxægw|pI 2Cx!% }vl!{7M 3?bw1 [sߒ'$ņ#,ZM:"c*,ϹjiBKr X z9ʩR8r tIA4a9LZ7`hD'}d%)>_O3]ӧ\ڊ*$oLlHvZͲ Qu0_f dq4{Ȉc~5/TUSxE+,j#@͈* B㾓̄yEF=@dWI,svu kXs/}s) }I'E[Koe*cUAZQjJ*K rc6h6mWE5;Jo;l ~m13ǮA"f_(. Vҍ̑֜lE/!;AـS([Ψce2/Ҡh lȝ"TklY`_z1ݗK1UMkAwu/h1Yg #`*[fZ~p2b)5|υG괢*it8Xc@ `(Dc}a?YDOx'P@G#2)u{& r?a07g4 VYztrl1t"|H+ȸv(^ Z*\mYgW鈥 f57}! ([&iNP QGFL`ZBb3"P0ϕ\/dh3%@):nнɳӠn~'<߿:с ;4 BxYko!_hQDz6C$Zy+N܇σS&>(S1XUGz6H|Bd<Δ,L E:'HB/gRMR̾GTF4 Bli _/4I~#>e/njc#: 8N &t-vqTuHvb{'4v3W{6M?7$0@)f q&_+8jJ>jؽf!oHHSxǺF$Z꡷ůEe_B4pq:iElԕ_xɝeDF !ʃn>{/|`/z6xu ^ _^QVav G}gpGFC?of# }f!F#blڌSܖ? _ouwCfrYы6+:ِ(/䇅v*=1] G,}lr\CysLѵjBE usQ<û39!6;EF/_gHgj5,Xf9"ƺo12/=<8vxݑ2/+s Bo_ړ1 ]P]ݻin| Z/vۈ/3| ٌrd%1G9vc|jka oJ@ flޓNTxk C7JXscݬMy¬O<fӜ& .4b!qjp6j6 y;ZȌ=|N;\ $xh|[{ -dԨ2O3Dys동Ԇjff%脁Oӧ˂v~28Z+Ȑ-RwW w 9ةtcUbeHUWXC|r?&N@G=okRTC't*)ʎY.p  VpPOqy j6 HQk2 ~8#6HVp:Tqd3i. T̔6›[MƷU]C͍{KH* 9k -ys3|xJ0+ietZDoY*kK|˷cb, 5h\ uRċ9e=dT8Ksu~./3%Ս1 I- TXx`Y=irU:Y, 2O2v &aoośSY `(N#56h 9g!OܓUMyCJ H}Kk'A ;ɭ ,f)O4 &Љ=ێY#-32^MbXQ|8,2J䡉Ee]ombLO襋`]m4=CrX_z{8y5j;ltIrTT*0Naސy|>}L8N U4jwI7n~Q6&¦puqCәg5-ђ;da|8~w X!{8B*qI|jV5x&UGK\|5|c]@/,~MW<è݊1ީ9[y?m*8U. *qM:^ZPrN{r<,-ϑ+.+T84l$WեaEآj|_"&v\D6%U.$O \l㯉]1| b9-(F̻2- 7{ u΄Zmݽ~ǤYV"\O'0&dn86:ɯgk4*]qN!ϐ7D9V g cBQ\zoDI[IX)b=+knn ̉U; ϐS%6\@J]F%I[(c2tyxMW_A\rշ D,~E(ܗۦ&IR)|,n jO^.FC"2)w|}Q^ ib}Myw/V)o5TLTBԐd7J.J&UP>Dq9ԹͤT 4R}SUzژ0P]5ԵfvEyc2ZAGeƊ3K|Ny_rj9F^NDxS  qגL3 p.bj`b`՛B9jg2 X&Ԟk 5qTL 6pPVvcHu.Έ:;|OM5G%dYE״4`y pP'Ї5\*XA^4CN9X0k6b Qytq9cxQ1h<G0[VrVTDJީ i`cHZ`BG2,Tfjl6KGLONa:G\ BxF]Jn jg`tJmt܂jFe,A)}A a-/8"xE/OF]ZTOCXGC6)Oȸ uH, &$͢@y'z;%tcUd"\إEEb)nca! ӂ}(I}unֱIgF.w~k= wrO8Dub5Z~IA;Kfli>m Մ ؂G {s h;>ggN,1G&<gÝ , OD(Kq*\@kg5@<-IK]o,QT4Q}o2/ìmxdr\щhg͡dD%{yFg+xt',ؼPx +"սYU3nvĚ^Dh]3i(i=zWȋx_: ꤧ'pSk.J45NPm=Z`鱊骣Iy*gN^#`YMӓx;քOwSЁ)% _c?ζP`Eɴz)kK1vj0xiq@ d߮r_O!i;NH ƜkkL'6i|UWm&MYAQi~a0 FeVF'PKNvOF celery/backends/cassandra.pyYmo8_1hT8Jo>za\&]^q8(DDdQ); }/zW' Lp8:%#8w^z5II0%B$dT!yFaC;D \  IVC|dl3 2hۧ<@6ǹ=d<|f&spNJeϞ޳\ c Eq q;(jU =1J|e7G, jrn9A2/<$GX bNr'Gk,|2[&w"B\!$w5 5BЧd5k%j>`^g{Z*fW}OsT6^<2UG LzK?o 1}d[@ިt~F1'̟x5Tz *vpkiuTeSEp%aJNZyEIS_U7"tT _^>0OWs W &|?#Db?BO-ƽ,,2yx캯aV+Wjqуnш))AM {С%JIPZeqm[&rk =ebM!bS9uA3J |<&,Edv̰~[Em L I̚ƨiE+-߸įKjGb"5ej֒6>4h:1gM|nF06E%ul$ ܼ 8rus殽NϢ!^UHhR^ۧf䤁{?=k.OwT!čQJ<Շ|Vuc[Zu U.?&XpPC vurEn;Mkja))%&_ Fʁ6cnoLwy@1;R~̞.TY{pnwv~M1V!jq7$+^z8k3Lh~t^hZ jv$!^M1?bq#)]nYHW^D 8FVE^SU(ֈ9_su[Y}ӕN)tu]ԺAVХwU aܪQK}>긱:M`HVYK5RVY8iںl0-]m.~ eJH5J:1EQ2Ej~2fhUT}NqޏnMwM`Z#kl-%z/(f%5e@K]t]ϫ*f FTDxEƊFqu]\ Z}waFMϿRO">W_NLYnbnEDYY;,%=Gx 򌊾:`d8f00^jNZc،*GCCȁ eoSʨЛ- ,HKҧ%L\cւ-UE{xCZvUc8:WE o!m3-j?OXQL;j67t:5; ^蘗PRPKolXO؊d celery/backends/consul.pyVMo6W)ɁmoE]6- c)и[BKK#-$h2vD<μy7du"hY&VߛFjDj@-lEN޺O"`@w-<VDôz0Dtġ&@(QBjBJQCdf5g$*sQ3J]3vҠj-j(l8P9/ fs'^A3LqvY)|;DS!?w LK^1ظB:+M*r+z2GȢ{z@YVהsd"E<_/w^ݭoB#,1VJ#-0W>ζt-#13/,Hs/\ڡK&Ȼ7RL In>a3j8BUMG-"9w `waJcMO}Yh~g=u](ݒ\QSѼ|8X%1N2`<΂7m3eʆqD7 rEuǛ6q~AWWalbf(^(m:rIܨ?0Gknr+@k3 ;3:I>`'(g]2;^c K5F;Wdhg1#^ad f"6^P|?PKolXO\{.Scelery/backends/cosmosdbsql.pyXo6SL\ea3`m#htI:l (," =RuiQl3rX#Xd)ug\|߿Zsr,F燗%yD䘧ؒiBBnI˂SJ&&ldZjN󜔙Bʳr,RTլi˫K睝zZfl9)Ez1,j! S! m"@47nj>G胑id,"dZ!kbHS Q /5ZepB-?fJ?ŭʷQ%T`9AƟVf+kDąPe GT %8Dd @J4GM*~4%}(>Y XQJxҐyYKy|ͷ5K[9m԰*Dfϐg#5c 7L_Mpq[\R#0霬s< Vy5lP~2 ?mivRgP6Z2?u\#:W)z֩ ׁZŸ`= uL cp}YdM& v3ͱ~V!lOyxAD iJ<#"QΪ ?HQW6)`# 0@P|c}ɖkHfHi8t [MB4Gț}ܓPLMe|ݤZ8{C_ΐ}6ի_ s+3 m̀v"DZqNg]bΠVslT{_čfyН3=S*Dw!LET#ҝ]rsb,}(D.C%.U6MPL#S hs'U=n =Ȯ;a<#G"f2Z!H8힎ks _3B@/|ӳ}mˉb{Udrm=;4 U;]' bܼ]i4 )u=ӌBa߮ 5ng/W[5' r`66?ZKR2JIkܹS N)|5fRanLڬ !ZOX|NG?1]K``v6U|Y_%MYqEkt֗ת;aaʆ6.V]ԾfV,ĎWQՇ AibʹE N ,u<74b3ӪY xM F1:Z#3i&bUHQ1w7zlڤFS(QFPm? i*^>Ms-71`P}lOk>UgOn$2鈁3XO|PKolXO~b celery/backends/couchbase.pyVi6_!fm!ɒd6Br12=59 M[RիUY4eiOn_ٝ0Ñ* (-$#mo+:)Τ;&< =*گKbz&L\eYtB˙Gz磩f\U{"Ҳk1#y*Ը /|l`L*Jx@kwηAr;~2-/!פiZS(Bz*5 P1yLՈƕΖ: }'L:޿y1Ĕ \,ud>k9@݈+,nӵ<˷ Sx_fč\ѯ)P^>+~T[H9p 21[S2N74`w-tjr\4ۅv?W_|[gYAv0g(3X0US| ҀK3cu[-f[P5#\\Njq;#Luw%}+RrF@;﫪e]IXQ/QkFK{I870&MZ E#$g0hw`SΎʋgM]9*R&0 @FDI"LcKVzcH%-obL Æ|p9 gWI]z-l0U'@R#}4~4RuaQ0 7{~UÄ*/Q@nHBE}ܳ1a9#/?c/SO-{Z-^TBYߍ~l4=<243}C,# c4a],Y7]49wSprm'bO j˕g|~ϒ*S' Z\=Lh3TC|4MX+k+<55y6Zp4xImD^,%/p"݄G^[?Qedd-xW$JZn1~' t /AްՓwo{&޼Aԛ8̉SK%њL!L!T'gutH1>98~_ "i+KPKolXOr celery/backends/couchdb.pyVmk6_!qppz-m>$P(wOkk"ZK$۲wsZîѼ>sruyEr&ޠ$;;;{\hfXɶlՁYY]~蔶nβ:IX-e 'uغY.LdHa-3UXi1(Ն ^&+w:˕4ӪcZGȹ6Ֆ6?p f:#pEovHV:(dCdYVTkC܃*>׿Կ><JOd%V.YE𭦺G⢃wY5CSB9 !d~rLP)'}T#X z\Э`ՐN(i)LRyv*iP 2%B5TKnȏo@-7@ B%u]&vv-6PK .j8H/ Zx=?Y`8@(k9([Mn่9Di+Fbj<  L'e~qR uLZLS3Gt\y@E|EиEpPŗEwO1Z'&҆ "ϡcBqaJ a%kpGdOBBwwEܝҝ 2{4'dCe;OlN$ 0f}} )f6pٖZk1gnRd8>hq{ͨ=v-ޅ#Q_@'wDJNxb}؆b cw;o&X%Bב-y:8嚗 :!'xcNIw`>,0 7Oנ^BHwl4b~"z- .S _dqD [&U}IPKOuMDcelery/backends/dynamodb.py\sοj}RL~h 3+&'ctT G5 xhww{Ɲ &H>w ;;=c2͊9ٷfɓg.]lʗY͛.oYӖ5g7/trxյ]e[4e޵<ߧ+2y:ɛ,/۬,5fiU9]lU[s^M$tfչWI$ _Ԭ-Գ~I s!ѨwG)/(%"xѾV\Ы9c'M2gE .x,8)_(</8Rf!Yht~9Ȏ-iXNZfYg7aIoKI WޫGGS{(M$nw&02E>cȞ|DH$kx#Y,dR}Rmʴ9W*$u|dYWKxW yl?֏NM$Q$4մ>b}P5BdIJIXt TYѲwo_ k04 0ԖyLr-7Bę"`5@,`2:)f#LC uA]_֝!w*y =m, ܙiZD?C?98\xM"2hdS${ڔhJm~F.%hzABx|ɫajx|qԷ 9O޵A3eHq2Gx8Vzm9Ӄ0@5`v‡t]\S X&4v@"6tL#cK2Q@!GpW\!RCY ٠g)Z PfY,̔I0;x-TZƔA\iZ1kwh5jchUlORIџXt1;؟$&6?,@pX5'y¦D锕+Cd A@c4>*=3q.3ZEQ5ߵnRbh5^x۰G٦k@tdEw2͓{ɀ%;VY ݇[{5@'ciܡfO.w!Pn] aj0",BA`FtIa'#a)0BTe!y%69A>%Gِw1YW&}+O_qi]HxPOB[Udek]d1h2pp1P"".k߿0ؗX >bL/)Fg6cpA,/1tK}[A҄e[ҐisGNPy)|gCpA^S>zEXB74);NH?@׳ CSxBE0 /sa$4>hжIʈ4`u(Ot掰/#lK-&ecPW em]v*P.8I'L\fx7S7YkCA "wvgK`Ia袱z+#\#[*^ ٝgs3MEKo)Xxٱ G̵|8lk7 H)V1IX湐c{b `3GM~r2k}EEyb_O ?0bERD"H$9T Ɋ2/ GV1OFU7MUI v `U{,'G<>yH!J5!×ǝI)G c]x%T^ O.E=QyPռJjN xn~;:F ;'ajvUr,3./~ wYAvO_4g{𷇀. Ct@, |6ޅGH4>b>1#>~N#AcGK}:T 6~%tw=|6X ޿4ݷ_T; ҝymso4&n0jOr: xw/q @-LT$st!DL/<Ώ e{p` TIL>k=hWwvei27f)2.)Ae(cu G"q6shŞS$Y=֙Z 4|o"ɘƠI{X:l|_U! SJyĿ=ؘ'߷PKolXOtVk celery/backends/elasticsearch.pyWm6ίEKR*Nm!Uj/+‚:6wAQ{g%EZ- ό̌Լaݎx3.gϞi+X-i%T"RqAɡi䀊w,-Kº Ehi32 eU+#T*Q{|T9퍟zTbEP IKX5mrby Tӯ?;R*ls_Nv7&VQĴ\EFt޽ P xO yNzoEeY- IeW4ޔ\.~@|$= Q^*Cԉ.kATbBC@#.%>bDQTL-fH~_1I1"?7cKnab P:4wXجkaRME y!`d'.JZ^W->ΉՕ~xȹ@Lh|[%K,>Pa+'ZQ fs =B)L$mP-p\TN#ޥ"lqѥ -pY\Q[!dь>ÜuYIPg2;i9yT-݄}Ns&l6ws'K΍`OȲ 6q~sByvp JV)ˆ$!NW0?p;#`a0\?4zV[wPcޫqhb-!D>|N{Nvom$) SbJ&gsmAZbY˶u:/a7CKT(XPRT[,g=>:Oz dҊsd+"55^hG>ɷ_f`B |5}cx *}n5$)j?!-X%鳔YE\AF`:f*lEfg^j+,ߺ񗫯w&I3ɭRz|TM.|}*U- 1[ǎ0V֘T06wʕ  p =t,M3 h#4pF7"| ~?S$kr)k"_g][+~o$$U`5 OSvF ڛۀ) :a,?[j`VtUfڠ-FTp6k =~?}Tp/QSNf'mĆ vZ` \R5#+Jd^ڡ_Sy\n Wh@* *P^U2Zi`2y5m,;1`JA 9eM&$k;jJ;[]x<͞&[U sԖ@A>BHPgngOorsof*{J.N0`aw狏77䃸)x&>{d]E=@Gm7 YD/X۾a&ޡ7QɍqJ>y `%1疕9 j7׹DiMc*- 2V3R=t;V{3"  Jܜ8҆ıJvъ΂4஋1 fA|V\ĩN:1hw "%U)- ?.ͲȠ(&BIߤM2U&i&.\J?>8*= \16sݻSS8W>cAYE8f.^"mN#pYK/_2[P?\zͫOtEaINj(&^/9GSAQ"xACȺ=|/+P 6p2r/Uo͠GI#+qx wϿb]HZ]LQ RP.`jg8T]XOJ6 /)ݬY{ɡ=x6llJΟai1 TI^wHO@^6blx2U儶ɓ[][ ^iI^:Ih OEt?r p4߳0^a %ɓ@3= Ѡ+}{ ) &Ήm8lBP\,Jc FIͫ>[j~P4 0$c,M14笀"1ݑȎ_A-ӬLtDE&8ՊkFh DqZYs65"~:I!>D#TyBZ?8!r=&yLlƨ#k#Nq[d*0Hp|zT!67⋀AVQ9@=(yekǓ.&L|:֥NƕK;ԏ#8&33|>oNӾ=ԯ]?{,y};-Cy j.0֮QفF Tuv#2>rm3\8c_ju`@* 2LbeYwt͙*/(@[.ؚ+miXd,x,bxA8#p0wT#/CM(wBFK0(CziT9iXۂ;P)hCVMX*Y%`;aR`y2b5Ѻ6gFNbŪaO;lŘ|b,Z,b'iF #\[OQV 1DBTU8yqo%g{W U{*Ŕ+E˒˸x)>?q 18-H+Nw ʍD۴SDv @='tB&Cqv:c`[q+ư뎆."Ԋ{iHUT^7n0Si6]6FRc{fcGf^b_L@6WP15"qRV<>džҴX#hhn-jZ1'͛{Y>+h}I!;V hdf2r#%f"QOgdO}Q6 (B j֝4 Q6{g<{s~S70kS3^_Ɠ’% -.S̶0e~{S;Hx=1~vVԱ㢎b@,Sia!ח&*>~2/k%Yr_FwᨉeJ @lccV՞7uDtS>, ޫ<޼{yV.u =zrsz]]ÌSY kì+ab%Z }%m .NCT|€Fj#gQ8">|U;Ce4==Z[/QTMvDIrԗ1. L0$j _cz#֊?a)/ Gv9xgK%~Gc\]\*vܠZA.'^]( B-G-(c:rsMCn) yCK*oxad; _[7Q LJ:fv${Ѹ V}-6-Z)8<FhŪaTX?zy}j^zri~_ GYs䤸\2p'_qlamӷf%bngװEYb7Y,=ǗZ9OZ*e^ax<[7?Vcu"P~m,>5]9{2M dA'.@qWnE"ݰb*w8NZ˾ #+ZBL^/&~rqm2lJoP;"U Gvl-^pX#r{Q0dHړU}Ќ iKQal=/Xb=bD77$ͭa?yz$@{Иf~}fSU--O¾̊$Jdk TL(E o4Va+;$qm0Y$O̯;/oӍg|)lާ]NgEzfWPKSBqP:~Qcelery/backends/redis.py~8RF7Yme [8|B`فPjJ3^;G7]kذ{7& ;|*9H]t#dHYg`Ⲋn6 X㰪hp9AyKDC-YqiβA)AFB|Rer5:8Cy}ߞ.o Ǣr.4ee^f6PuJ=XeQ (x/wS.痧o>oJR[IdEQp :0̑k{a,/00>#N&7ͺ(K/"i\[dczY.(݁YmApwMCZf g@D4IfT6b Ө}~:@+y%N?|3sopq9˓*g^K0TG{k[2 x˦iytgH5AvM&ޱDkXM+ `l|]ߎN_K.;haZ02d׋rXv(h*Xt`̤Eynq 'Iw|^?]^| Nߞ^OsŐMZ{~T `%y"] yRHՐ1.DpaB C=3;N<,%o{ [A~igj=7 LQ&0q=MZ$6x &!rr˶ "f@ =P"[ ׯ{GR/{MQ<`Pp:۠ zJH~6`-:7CL7Q@OTu 0I!) 3@јbVi.M{ANabaþ.۱:]"Xjeݦ,K4]Lᬤ12+~*#G=*JMՐQ%G. D7+5ڜO.ǯ?ar0@~צ황 % 3٘L ɀ﨣h/iFY(pډB0}#K cI'+&!~x3phֲG0skKx"MEi/3fX@%\ OU'>v8QwXeG&:DU}1룒n"Qv,s{['ݞjbn m(9܌0\,5ݥrJxIE=z'"SӤ*KDW?؞\H_I4m1~w }OEɀE!>Iqbm\4hSu%j% WWi>w5nwyzp9rV1.of24n%+_l(쇿(ӱ04.=U!"tDXl0}5SKIxڍˠE.`jx({]q}5h1;+;[ω(=Mg@kƠ̭k 1E㒵swGhp*-Θmϰl.oq;t,ZޢuGԎvzC)%_|eRڳt?2y/~_'/G-X%?@'4Iu!Xx(7ݾ`Ƈu='3/4h?0f{t#غnh]X%߆zZ3m(n()GG/'wx??6o2KZ'iQ gWZyab36e۝B% aD4keՋl5#")&4)q;%]ph՝fjT؎l|PXxY ~G". /UEsݔ+p zyPhϵQaik/Ю ea[L rrh2'bR2퓖ᦰ(k9^1p<b LťyHEՖ_QAcR^>lS0H6(m)QMWA\(;b ~mg{vs̺6Z>){ր[)F_Ɵx5C]BH]O嬺K!ODt3 ]YŦPJD-hPKolXO{ Acelery/backends/riak.pyXmo6_qk>X.T>0mmmA$K-hZ5T2#IZ) ,{?R9_@VyAe/^%zEoA SI *-`ɳ[QR܌Zmue+-|[*m/̯KELzuqϡުJ+KVZЬ ).d*Ly6>zNVrwuAW"]r#jbJ\oQdn>KcXЪޡ\bfXvskEA"ϱJ|3(۩w4b駪Ty1ƥ|!xsL蘽>z|Nu/+e\ hj \@v50T!w 3xx~B%E;vϳSx7r%d;7;-z_kK3;<,we*9Ĕ=H9Z-1]1X0,/*oJ'O 1*Q+s}/ 硧^^  XUljZ`+()0R qMAH,~Oi֨5il$~>'dy>Rn##Ѵ+=#-wg;;oh8D!\J!vN#cS$68U7ұ9BܪU%̨f 6";K" p0{X64eU_.5fQNԌ(MjiT l͍ j`s,epH&HDCx>~={MFʸu@WiGrC=~+/s ct?f`8+rXl\'N8^;՛!\~٫8s1:#US8Јi4mEspq̨eGx'UA!|%ȫ>a9dWKɴ B8}aGIS01l^zcSXwR0͹s$&]4vY=ޞC:ߝ\{WJ6Dz;rs xp1>^:mG/{),;R]^fL12Gw4U_%sGG[OvƿH,XGxUPsO n^q0fePl1{ jo~ ]Jt zx`[a'DCs{"$pGbH q;^{5s۳)8J ­.hŨ]C1DPKolXO5/celery/backends/rpc.pyZmo6?(jO.o+nhĵUkEUlMERp8 už=}JUݳpdɓ7]]]bc3(oe[ً_;g^^{C#95ІѮ9<^9QNljj8ߍKYT?0ժo M=^4z3z/[ߎ&}-J߫Q݋VJe8mFN nr/k qA%j,֔z+n u4扌}з9n}4|ۨ6ZR^Vh{BC[Ug4:T9{ +.HTǽW+pنekݨzx󩔲:gk0 Kh}ZoWzwwa1IߕORkԬU 3Q}[*pJWcDʊ XXbcw n%<%:Jh@٠sR%ZhgmҖ3v!Z _ޘBNW4Zi͖D7>=[1O*cyc׃SQދ* JWm`lhǎd*٩1V}6KטR-8 fP2Q:z<ӲAO7)~@WSpiቤ˧0e/A|[O<:2բ!5Uo\Dh6v0!tz1;$nzX+`eޅ&_8k "ec6sVf:5)Tulؗrג4<=\QU䣆0co7,WGQb/TA x uL 3~LL<),y rq 9)I'g'h@XFq#[tE{`{sJ,~ril IN#mO@6Hp1J~sL:̝$鞯ީ.z/>q|ZK>c?>n!LA'W<X5Pf''.n/Lb%0Ylx#օfʛX׫Q/-N* - =_sah]AX.u"a9i&gAY?`)"t:O\9t!fB4g $9 AC\†D'v5T4>F ̪L jx66^ԽRK (7P%7}oy]vc nVLFK;a`"߁J1P(2M@j8d.䌤XJlgabeC]+Hz9ێѷugFy $mTɖlK x۪Sj0Twt/#2 6a+5x 9 &XKa~٩?]MAglUF `>(1}/#Ur(kUuծ8ho=lP5QcOdrC-: [3ry}8a^ g`1!Jz1;Qgm}3R#Ͽ뿬 SzFb0k̺J1 \j" W CUO䪲Mo˜QJ4 ^ +A6X@|2=Q'gķ] G|CblJzS26R0hpH݉Eh>oLX)BL8Kcw߯^syDz8(%0_ ԭvprV@cg!1 ͥ"L+\T}RQQ72Gpb]bX` f/s 2#alBa&?Ӄ3+x. UZi\:a.z-  ? uIm}]y3?ghwk/>$;hnryx"gLsȇHD!9(}3t,U su㎆e $P)C~`| 'ʑ\7,AYHM mA\X["hd[YtY6)iY,]'·/V!ٓʮ^"Ut7yϟ?f/},:M!Ty#82 EGt(\5 H> ]g24!]zMBWhf; )2XnI0L9sgsЈ/ GUirSp㜚\zq7;lsC+t8#1ZYM|JҁPbS/lƮOg, u_ n߿sPy6~[:\\?x!{;{5 7d>|Mse67) 8 Zdo0R{֛ irQ.!5ӼUܼ3XLynxrޔYhts?N/@^ަ|K'qH?SKy0д[m ^=Gi'cNeΦ݉}-1;vb~y}F].j$"D_PKSBqPa celery/backends/s3.pyVKFW4AD#b!3B2 TvV~~z^U=>wtm6eWY (PW9^EG/wL^mww|-7p%lg}9;lu8\X4t%(0)qR׶݀Qg] :L~zA(/TS#">vz<(1Fl`BgQĹP *XlqUl8Lne@52'y֖kLZVklg!8(Ȗ( !#EY W.=fZ( bHnK&"#Qn@MG<{x8dӑ둛dhF2VN00gPwn5?j.:&kP+_<;<:=8UqPyTJ.&1(4%.OUt:GS P`Kl8PkL"K~v~Vw "⵸EZ5}]ZKc/UZ8x#ܧURŝzx)G5$tK}7dMu+gdB,A!x5x~xSO͖'w $!ց93aMйҢJy˻1}tur+ &mѽQVd30QMs ²8@b;FU'@+XsόS8q"%=e%kh  {q#ZȨr6ъ~] f۲*֪DmzuIQb[$8S>gfzJ ͚ݲoZ^.; `Xk<}\H ȭ@TQ'G{ "{vryd E%FL7c)o!޻6EEZ3h!֛0ՍIJn5#g×pBX CGaHMs3h?.#7cz㺰air"%}Awd8 .QS|9yk߹UY$eX+ТX,ŠVq?-EO^֝`۳Xt$+g^WK'u\v}mn+ k8rLH/f vq]eD.+=: LR' $i"kU+Wq&Jiɗ$͉+vm.9̇j/A~sbu;~=χ"AJW j ѯK|)W.Q@X#3 }`h-D덀]Z6՛J۝UHH*p%O.t0YF)pI|&/1hlȲS6k!^ -m7X2x$=l:,(7r /eU\:6_1  ~Sd Ic"w(o|Qzj1lKg${lQ՘ khmB 9Rh2dHWv){̳^=ŴQ&`=L _htdu1f5B{{^C%D[htDy5B Oh?H򰷽ZS1%L $f!$KA0Ӱ1`7U'a2g1t1mcJݯpwDwπ_um`=7m})5gyyBx%6ݗހ>?LEаP}'g[pJ-bEȋC^z_1 R]Ӳϖ)$@ ! fwl?~:&Lb6m_OpR#};UPwBySrٟpAmcwqv_q=w-.be_oPKolXOD1bh#celery/backends/database/session.pyTM0WXaӪHpErCrIԱ?Iⴱ.9$gͼy3~E=-u%TsGrssY>B{ZxJj[X7mZzlQҒ1 iRwFXeߵ61<\KSj)gWPJnO0͎X2Nv [~봖'/g'+X/=Ro !q)k>I I55 eỹ#IYD`^ki'.D9]l&panY1EB& nW d@l tܬXE6#TX4*\ofē7ր ] ٪77 R\3`[D\B83F%AqaYZ]a!IPTo_apRʹ55>&=y6=>S*˗dm+6cyl R~b~ePavѴ[#We8w?$k͆h{D;2o;ynBpy9w$$#vuLI>鈵!PKolXOjVjcelery/bin/__init__.py5 "?[n:B6hܿ-;K] i3Ҵ8T1}q|$ٸ-,k "k}nPKolXOI"Af.celery/bin/amqp.py]o8ݿ pm` Z\k{B-:F$!)&1&Ù|ɓE\.Yg'ő٣G>[J^*^/WQ g]|]f,cdD0*Y}tK.nxVb9c9yD'`f$͛iSґۖ8ȴ)RlSqbcv@R4@rCGkQX4_굺jϻA|Ud!md}媔f $*)[py\S f(7F-W8zP%ƀn%{z6bWjo+C"=I 晨GC|]Y^<$"@YcQLN;EhyD شT|6ح"$l p9~Ds@߉%F,>qiNJ/XÏzO]59HѸ$*=F6`ߕ@d]&1l!sM#p3mgȶ_9 މij3͉FaQ~xԢnL &ߜRE̪^Y/Pa~A'ů_6B){ e֢eɇ1>y]Y`;jSu ~"-)K0%htR\ܔ&4?SB)jn&e;Q^Q0L8qAF{RXO؏&`.On+N*?`% ۰V-PKID&nd=6ŰG}oY<8,-8$@L {2盫Ar4Pj{QjQ]r:_#f'KӇ` %m4֛w]pCZŲ4;Y;%ǼoEF=ɦ6UAm4WH46:x?%ҹJ-RNƞ(m6_Jgd&@Q#˔cm8/tX)&/8xDP@hQ%6#3-mHR^ܠX m-V"\"lDH|=O1~|?= Lv9Pow75PBЩ0;O0+heQb:UЕlщcÍy + ªJߵbD=Ix^:q7"$Q@9AftMP8!SQױZco YB2N^3U{OЍud0gQ 6.7@*Q`U54 Qkt+\Zй>q;Uah)'v`i8-) ^Io7O6- Iu ӟ 6lXwőB7g_)s0pQčtF͹ HkT\@R+ {R3LDž8uT-QhB.սFbq_B5(Z3r PJӷGX qlv%z8,(NJĮ07 ƛUw`{ TSYo cG2bב#+kKRCUM JF~pw zZ#eOSza;n8kmUR27`1h=*U] $4Ķ4ki NT[Tx_#>eI#CQ7"Myf(q͆ue~+zע[nh"zvUywӉWoUU_j3`JU!`"]CXp:q]haYItS>5~spOҿO~Mnkty;?zJGB3p2/H\&kGYU-051Z2ҷ OF^M&ϽSZgp\#<]% poz9 Jz*oHHUb.=Γ܈h59UuG¥簲 &Lf*b<Sϣ9ulu2>@6L]x'h hUV_IL^֛.g:iA[qRo^@>M~;D٢$k>2%b L]Iwɶ`P€ʫ6iEK^+,XG\F`^D LJE-W!/ӹwsE,w'jԴ{c;ѷ|Z3"`9zJ4~&87զ̈@w&~?*qkT¬2]dzgeν?^9|9DQ{dOwϧcI?OWJ2Au9S4|\ yGPHLs<Ç㓟?Ά.2I[!e9/DW? LO KW\M3nܾ}d\W惮bkrCK`lMr\BC|2VUB>'z遌"*$E*\>]"EX a^2mkCKW"iIq} jZ!IY/`rGڮk6(쒴(jћO>3¤֪o$o 2B@^ (i=qMEժL*߄!/f=Z"0q=J%=x"!3lSeDe@:;P$P|tjKe5=;̰|sڠ ѩ]&l 85g6Mw)BJUAa`(/daR 0paacaOLvHy%utUy {Uo"F [Э7 5qЊb9e AvK>!x&󐴙ѾiSjFA5s'Akʽ FkEmIF>i3fQzC4`W`il]Uh\W5zoRkKwq׋ݗw~1R]N?K i\"-cb K"@J*،Fy_cMטB=aGgc^]X 'rl#6 5ù,nG[TX&-y mtE98!kw+Q`ꦺ3Ej2ǒ^vvTD5 g8 ww}jY|vF<^z˄,'4l`=aTuPt<ҳjcG0!p^Ro1] _pȍPⷘ! $iH **%22NC2BkpMְ!^N'D2-Xh_|{#ҡR]|#hSdKMr_li >ymblzmm7 ;#g3ʃLѿ3\MvVw%@ ez2GRi+AsisK-B+V`&񷛫aB$K,_`Z/d ZK׫.HJ:AFn8;9b=IrW/$РJݹ?-R1Hܲ2Y^Dȗ/\t+Pq oPGemaf*V}5=@XcA?F,0z^VZf7ej>\_|[͠6jjal z 7)X"C8ǡ9Z `G (蹳ٔ1_G^7;}!g5/qb08Q_*,ӀыZI1@:!ED_F|xq=@LC{" ?ceR+Л 16zb]٪GL4_Pf <Z >%cb sɗ[2Su`^'emV1vwG7̱~n @4A]A ^/IQ^V[ 6E~<-_!-?Dlr^Ϗ//8V}&-0\Ъen0 >I2|ʰpzp.4sA3 NM"r,jLAA_?8QGu[.BNi'ᖋ7uaĽHĚIЦTCs}"^-+{WOzwнs.WHa$P&Kܽedbl ;njAu7&.f{6oVF;{ %C5N l7Z;(.>'mf)aPݓƲ3@-Jh3TBch{ǥ::/xwS@n% GмJp=\Ƶ`cA,oTR~+ɼgS@[QGi?Pit8ȨW ﻖo^RYGiRki s 6snWF m*i)poI6(VeʲS!&)'*5vdN9?\{ wZ_*'$|-*>Qh6JIȌ~Ƥ\RX9uÖϥq(pp%K _ k~.;VuBĠPI:1c`A 9p4KF@FXF6xT?l|2$ڧ vx~W睊nD3 Oc<3#r퉝m' e (DOHӐ02<}*F){tܗȧrZ$ xORq_czlfw[e2CRBlJ/"H$J2Zv{،j:Qh(  "+V1 7#H x@brƦ6+c噜1Ϩ)[ SZ2تn_^YQLMW'͍mL0N^M x'W܉qEē+>KZ9\d2*C~F`묺MFc?:z17c2757;6z}_n_ٳDʙ p*׽9cF$7ʞкF&iӶU]4#o&B&'`^"S)m܏/l=4輏x w6N.2YKvZB&IpoKIF_ѱP`_^ gqS%1ٯޜ^p=ed: =eޅdXvNk\z64(7QN QuZq!A}Ɵ@ C0 s23(n,hvT,=nڕq[ldk|;yE`*mhO7#lFu*Ph0jaF􍲣0蠕\,W| @8m]h1}\>0#;6ͨ/ ri6j'fMZKS,Vt\[X.2s +{>vfCq90Zc材;npIbkpI,/ a1Is nq`t SiĴ)DP3Cd C:-$+c9$ReK&ǿ'*pP@QA(*`h18Z]/vDih+8iY^N;K؝48>AWu~NX (^ddtTԚ,t< ( dm߃˂QF#e&xwGgëj k1]ӓh=*Dɍh=9L)1Hc[ǩvsGg&C4AW`zZ.; wYM{" O52QeŁCQIQ dzNZïa *]ʤXB&|彚;L\- W1x5[Zex\4F[ݞF?u¿2},Rt;يlؓs46xo_Kq{0}MYݕ{@3`A *i*o'j<ßԠA(hn_׷:  ;J.j"عf%' QW1G|5`}}"Z9Y';sZGbN]93˕V#.H.h`qP_oo}sKx*u@><.~*ic;08g}_ |#iӺn|?,S_ƒV&roqb'`8ڃ_LN̊-gL殄PRz3 ;FwA}Ð#Z nLqucݣz!u#DoǮ`P_ Ltu| p`>"0cWN؃Ģvܐm(PE /DO/wE@}6mgfEZ4>O)MyQc `S~*?mRj!MΤ"돃ۿ`aӷtԹh04.g <{@d M0dP)U}gV9-m`߾,/lx WV'.=SڟI&fFeP:"A<$-_#UOھ⽷;3V Y%lW(˝xZށy*.H+*q**ԆVt _6&l$+c6$#6T_ub3/Zj 7 E^{s~sn..?}wmߏ7Az֜6<%[, \$)bGml e@Ϙz:6=Y{2T 8(`Q>e$"; *u,G9GDO+RgoY>޲S< xsc) Ꚓcژ ]ʼ=arV@+S4"'cr9SXDE aB4 B 5# _[T׺ TU/" 2/K7y{Ia{h 7<&2ݥtdDW,LKЪg=W`,RCzQ{TtLT[LKM3=ȱ8ܖwhv*]F\0kM$HK \q:^bc>m"fݙy.Kg}OcLugYH酚z(,'+CxSIƕ6 1V"ƘmH)-d_ktR1q+~bJZ l 42'H$zX\jv򭎺OSnؚGoC_hH_ 3'9}OV|G|.1 pc"Q X2!B⒲P. ^j|t,/WWMPKOzZ88celery/bin/celery.pyZmȑ_g r#c 2Allv}k9`nDEr({&/{ᾜefuuuuOy+z"2d" Z3_iiߠ%;c|q8=VG?8!)PB\Oe3Rg^`_8gsLF*7v) 1lpў y)Lf$=`!c4jvbL0A4 hY57x[$ dI7%팉8WX3*$W!LCcأ+EM }RhuN>M#"WIe)ݧ.)(ӻfoT)dTMCLN?1V>ȾFј"lEߧij߲Evy-C)Z:s݅{q\j*VjI&{& BbS=}B.أxٙ>*h"x;F( l+N g?:>ƠRPR`q ??L̙ęLyjo ]aR T$-M)xPtp\Oeb $eO<6 唷^~_ϻ8PŽ^ɈGÖL^+C}x8B3WFI'v:),Sw9=4`x[{7kRFND֒<{;޴r!Xb| \ǜKE sN 48}Ppo`DQ[z>"X8.e;},jҎp_Ͳ W,DYyp40"a0n){<(t-<XdM8fH"6$h I,sD ɨPS=Rm7/ _/~xݛ\v\Hq ^`ijN͆92֟_蔡KwKn(>;2béjR$K[ͺ9f5UڞmaS N$Ku1vr 1mFnIYS0P_˾[Xv 3wI:fqHG8'f*<3QJY(19K/_T]^7{ =ˇKPޒL׳ Ɂ(41uCcSP:)pYj\.浆07UNf#6q³igV 7$q芭->UoK/{kM50){ds@|V`InUh=$])n8A91I4ڪS*#fۥ1 QVDԐw3>H+y0~~M/JvuLNksÒ7MU|s8(R. +ױm{`)@3![ituު )Âz$W֤ Vb'e ڂXFZ5LbBlAnӷVw~#J(䒖\Wxb p莏>\)Q' uW(p+&U$驡g 6t/\KkbEE͗JJ.wRy bZ v]}r[m銖Qxs/DNݦWfV8ۇ]62ݻ+3?}jt '$>54d͖,04Gzpz)5g{_uV.z6= %]ߩs0MXH.AV^s>p%@hkaSPIel+4UvsLM/h&"ö40RWI23͊CP x,X[2,f%w++cŁ?Zke(poiF6'yIgxm!x>+ B|C]7ʘ*"-jkň7")~qDoA¨-w"a"Wy\5cIaFRyF{ozFRP<G8j{g!ydbs~5|YFY9{ eND#4LP5pM%mC|Eq\rbTcȷb8M$??-&!d=-F1Bh&x2*SuA>p5 z]w|x:g~o:0Q8hlgj/[+H& ³0 M_%9p/?m[ NhPJ)SvS,eRc#MCJ'.tNogccNtm^DpXwW;j]J??>DγK+v[٢k2Ͻ&8Í}:Ex)֟6>od I8b39~z/2GXi\!HYO ><'ܙ#-$Nݳmہo?|욎׭C3 էom\An& @0:79_mg_#tiķ![< ocX~I.9#GJ+bݓ)+aQwN )Ÿ,!JN6jX Ljs7˻ -s}O-%6QzhO4/Ge4mQރN`W |7#^ҝJ`Q>i\ռc]ъ}D3`{Mb![ٿ-G1 ׏c_ uG=gl5}1VV D mnޡ!RAdxxJ79My(=ApogOrL (#GvFW( [T!eQd6{)?"SA=g纄m:t2Lra`b/Z׌&D5 r?ڲWIu/eaLn:/Ik,b=CӘjghXun:G2`'~X`T[ '}lZ;C7\^v8ws5J0D 4̩1//MVpOPKolXO|j8zcelery/bin/celeryd_detach.pyWIo8W) ɩ-4: E` `hH@RYf0}"H[YH!Kפ7}UrXCzKb$)od+8yꎫo?~ )Ṕ8KCIլcDJ|s)[{JۇoϘPbw| Ϭ뎅`JU ze_kK L:_8'kuc!I`gla)@8D濎aFΉY "pޞY"[H!ԣA$@Dh=;cMC;=n3i1q#3ClLph$(BCYtn@ APkB^YRh\9ҹ6hi '*⭡wD6R;z;[cw o#qU:${?WyMVgP$If1>7*Jn~Pt8fZy/̤ڢyucIc+1Bkzoȵ@—ԲC+uϟ۫o\?]^n,M5dl{t:Ksrxݒz)fMEta@|@P4s&dqR/kK) 촆Н_V14S:&mP1v ]<:?t6Gn/cj}ݑBooF Vb@RG7ˍieE<1f:̛v+qcύpBaz3M8b;?CtG *c&sg^7߿ 뿾mQibj2)DA(ѹoE'OF,ȭȘ~O_@mf*J= Xg\E/Y +Zl\^ 6!P1Z|̊0U^@>% ,OD3,[&ICeJ崐hɩLM.3+!W\dқyHjB#z:lIUAk鎥hVSix$^<#GZ$JFϢJ!I4@PU,!) iP TcP_t`glI% 0( i|_urPUaK/kKv"@e*;6S@izxm, <7I<e$]˧ǫ'S=]X{@+ | CU NC$l3{yO! LM!hSHWu >UP{Mq&ՠI_͖^E8B+yTl@jy;օ` \d Q N 9(8c4 ?OGॊS{K~FZ<04;;3q̺N pGsEyc5p3";T}^BoLLMYkKsp&8.9nxդ03@1mtVECS֨N˫`쀋BQ]x=+OMCO`P=]U6X}< ޭU@x` ʳV;|F[Bh9@a}+q?ۉF^٩~Z +K*ߤ7?S'аǰWǞ:sr!Y(B(Ӭ'=-8Mx<*H{fClV=N$Вw*ӇJx7ѳ0Y /#5k"4 ˀ5~({0&@bo*}g|ٗU ڲ2伕N3B{F~WgiR+M6CaSKl ֦iǐ^#|u7Bnڼh;bϊ^ijoI *Q]Q')XCT0a]z_u\j45"-f֎^9zOn4_UeOۖ3X,9k CeNsutoU-Û%~<>wMu+!#L뎳sY~#W2q/j*^gq1DK62ߔTr GVuӊRl!]s2.*Y_,9[,j,P%S:`K-heS5oVWSpB1K?Ȅ"6TKcڮfE nyzie]g K0o7D>E|vZcx@+5!8T֙|ſ\1{CK"aeQE"sv~MZM JNn6H5gVJo Do.v=g5 t;e֗>a=6 &ʝRyQ:K?p,&]\6N`iyF6]vD6M{0b9)[arՒ bvj=:Ԡ-m ^gBm%A,2.tŽR+2+BX̸uSـ9Mtr wq"1/C䊣6-7VF8*.\f2"Trey𢡊P[΁`xok2lJeEu@9-C-G΄n#VT=7engO ] l閷 H^0iŢvp> /}^d2M Ғ+zx-7 _T:TK \^K-%ENZܖ2K=g є*j) Eԝ  ]8JD7 {,%?]6>xxY|yvGc?bK'է2wO לRY̵n1ex|7 Wgmd۹icuB!(̫_ A>\h>#%JL:׹Zx ̙W|G*r849jpdr8Fq31aզ_N8!9Ge}-z<0E̎{~ .&e9 4'h6<RZ7z_cS}b| {t4.)#S}),. 3G 165c"Cm@iӐTUxJ]pbqzZ+ۨw^ RՊ6 {=]A,qNp, wt?" \d} +:POrj~yXo>d0(d#Pu==[(粁B_=̒i2I/RfHp aLg-qO ņ\"cZ&-G LYF#YQwCѩXxȓD1dSgD 2)t&Y.m R#=GW -)-RSPDuB]Z۽G'~l*/%;y8Zx7WOYZj?QS Dd'GӕsM<_aH1W RY5\H{7pvtP%:r"aNl/"֝ҕE-0)1R($!$ c KH* PKolXO8E5kcelery/bin/graph.pyɎ6^_Axlҋ'K!e ̒X]U@R)Wd3UGR..og$(6۳wqeg;N׊/2^pu$0v/~<^,40KB{Ԋ)J*CFˢ6<͸#::َmH,40i}"B(M4< ?%P3MztFy@hTl8˃S5K(2YHUzg#+ө_- Hs`zǠh5\Dnvy_l ῷ;ԔV[cPeqKЖN=QFųf <ۨ,+M0FEM<͂aϳZ:iMN=Vd ڴ܊ͨ8زٴۛ P 6l∼&ԲM@w5rkr:N(XM|[+nZ_I:x*!}G_u#s/1 nEA7jft`񾾙51:!hSYGĀ?55MN 7`}B+VN(X j+DŽa߸3Я_@Uئ0pxR~W\g^F pM|ZGuf~w~fͩAp_15Pip;b%tDp SP%faYhcPuu`#ࣔ%;i3JQͮsW`vV5G O,Sq=7wHDcyg:ŷhMO9v } 8?zMtU}BCOi&$XGH7n nH`~hE.C{PKolXOGscelery/bin/list.pyTQk0~⚶ctcZQ#D-94Ďe;i2<-fk Y 97i,c `iZ,,:G&$j ˪v D*mF՝̿*1EkH !$7^AE͍+( eU \VKI߽W=W繰w`:96(-u7Nh, /iV2MEI z[ὢ{,sS7JFh M:J} "! ]Kue\:(Z+=&\P[d_ӔLV\Jev UqƆ0GΆl{~jǒJ GXKr~$6?p^'y:A4]nG? eV(ɦ1C)𢽈lI"dCk n$_gΖV[vR_ќ5#_o%dys4ih|G؝0UFs!DJa{1${Tetr)A?B)\^ ޓveMK aܸk@uPެ䉛 ]T':pHךƓ84?: PKolXOl9s}celery/bin/logtool.pyWmo6_Ad($9`,ۂ&uYUv A;H$Ȁ)@,{Mfd]Y~d@|Ǫ-Kݚ=U߁JQ$e8NfgêFdŮb5Iy75xJ2@hg5eIG!6UuU%FUM h)d9]Sk5L Dppک8N'yqUSqWb BFXLz W>j-/nW}u N]EdA4FSKq2_ 7#@/\=_ UZo?},JmJgb, Tý\PLs.H]I>'5 B1RB"92ެהB1)M#iMSh'! \(O;ʣ`S"x:O8'152{yk8߀;'1JHu| F%qW|s!zGe$BܣE;+7x.2Z@: A"clY緋hҬ/!uduا*E?V%s,Ynf+j@b|[P&Eޞg7y=.Iҧqc"Ds W(Fr@omL[!WP:|Hx;FŸGugM%DNE[>uoAw"!5M7IfikJZZ$M=mօ~DN J#!NO?TBtbT 6mkKʪƝw[o49.Rh?Hܖ5 uAk4KZga0>4 :9P -e[?D'jTuyz/oc[Ɲf,NWumyUz?Xɩ<01,DצlەvE,wx&#b6C[T+2.w;Kwi!B岵9_HsMA!Ñ?XS''cLHBDѨ+:^5Z9͜<>$j+'GLA"ah^$TM B䮥 6cvYѰn(1oPe%\2'l8".Dj8ЌN M-Jc/KZ%p/V Z*_ TK G Q/Rjb>CkuDey$ ^v>Kb8:zgIR䨥%ٙqjX;9 ijeͷIԐcJwIA =\2EPSÞ-/Oj.iL,''9dAyq.@G5PKolXO°g5Q9celery/bin/multi.pykoF~^s)bBU$m Hjq! J\I).Ç%/InZyfHV5$^%K g-O˂4'<͜K^$1gqA("z9GTs¢8  26>co ?$Q9(@tUmF)b+nh(s2)Ir `[)ؑLr0W ~81IRӸ܇Qm\FP*fSA /N2qQ0Zޯ7}Z)#}qSB d*/hKc&b0E߉T LP !41d[K)'hg!/Njѩ@DZ㸐Dp+"bVPMqYBz񰅾&a?!ώXd&ĐXO>V>+3 ) 鰚tذt~#j`YU΃=\8֬*y =qZX@XI\,8)LQ"^A4oB2#B$u+* b+^;T0z~y| YVr g_E8yӝK YRiO"tXs5Xƣћ'aQU@ؕǷY GO4߱CTdya4#ߦ53檌9e~Y?wG qqW\L]VGX8YeRv .>5O(";qdOt ,o?T1/R OVUUe \p#NjCbȻB0'F#p1 DtVp0pOJmcNb~)Hov!RQδn%{S4ƽ~O稾_ÑtgfDk^F7t)/1Emk:L"w I+'ˁ*: ŇޚZ֏Mf9I&-#6Vx`虽+П. ܹVT]X 8h!O{x 2g\]yvJ%-, ބb& R205nZ n 3濚qS˦􈒊 R`b#(sR~ck_xfA>NJ7h͛ױJ-z0\C`<0u;Jt'' c-va\Z2k cUwO3K &KԅI/U(i{;C.J8 L-*Gu⌽5 N] :sk!|h~Ѕ .%siH p\%,ڟ?PGn)d >Al }%\3ؕ%,rXۇ來ߞNYɭo.AV\фؿ`NxDunXAߪw.Yc0rXg%{whC6sua::A^S)X[<<W{<ʁ  #tiAcsL#LXZyzb TmaT9߱+};'xN5as%ݺGw\"%w|fj@ l[,%te,9wdJGjD k'a9S7'pGh$k#C SoB#ur/"o>K "=42O_Ս]h4.g6ᩛaRq@'%T*xǩ(Ɲ<0ٌ=53ZkBHw^Aړ/D*uwN%t@΍P{OSʴ<@-L=oype. wO2u(o.Nfuv}cO%6M,hI`dh~ ͭK#[HF> &"^u މc&P1jy>hΈ4"p<@엠}3]\.la=BB0xfҁ/zVuP7|a> ǠHGȄq͝xEg3ٵN"hFx *hHzy)2hA>.:^ x]XsuC`SsPqE[5N)%x?mڪB\u ޲5gEʅE\ zfl1UPN,Y:tyb޳P'fwBh\pv,aXu$aҸ%NuHbND_TBW p5\m) wZOFPKolXO=  celery/bin/purge.pyUn8 }Wb}.%m t6pQǖ\]2Rߒ H6uCl6nnjP`j770(S߂؀А 5ۡVYfj x?HejYUx'ײt▋iod3ѬnWFkxG&IcZȲ0Lq< 0LHĔb|E)Tjk$ȁb3\ hpp1<S5(lPB6yLᐇKN#'B {y:V} G;(/"=%DnXa3R^QQq%A-*12(5zG&̢hҐ7q(jLFNΐ+9OXzG-љƮFb-QKE% jLV;eF3:qe3Q]9c7Rf~ofo >.H|> )KP; 6tuEu)׮t&yT,ʊX"^(FtS^IA/`zf5.+Q#E+:?Z!4۝7b}X(<̙89Pdȁ[S➾6}}&rmqf1ZgJc\qUˎZ)/K?꯫?|{9:K7 jCJWWɩBfCr`,RЇUPB/hܹO3Btf̵(Qm=&S i03M 1Q++b#Ӽ =Ζ@iKXzJi2Isv:St8ӪzH.4etM~Hc#BgxPKolXO]hu)5celery/bin/result.pyT[k0~8bkJco8}Ȗѥ],1Jǖt=v[@u  N.#aڦYAdEXc ^j)PVB:𕞘mJTIv *i:@迶O$'.,B#pb5 ƚ?y7 J!fC=I70Kj\-3mV]wm-rqX2fZf7NC{GV1n;썎5&+*DSuM1w/`Z7J浳xf5"$+Jsbȣw TVj'gA 2SB+5:(4Ip2tV|37y=sW~tJ>wssxvoW!.*U1> >s %vhhEoۮ5h}ڛ?=*Z7ˆIӜ_ $s/ ix4l?sDzaPKolXO|I celery/bin/shell.pyXKo6WA&ҫX@T2%6TIʉKe%"p3r?QWSuAD9QQf:Mkd$҆(ӗr\lPQ:)Z5RKZ505-83T:>2rE#ӅyIq.Q6 u9s~&pDkhV[~ɱ![$$䙪թZ3)#3'@3 P*Ҕkp\s1XRD!L#%johi-rw-g1 N'~ G%?V'!>vAsO`!dV]i~'itU=2 ^т^QǮu)?QKj9a.1MUZg4G!m6urL*pi#!(v 8b ۠WRd%c!& 7JgNvf=D]֠aCf!R S."78uoH%Zu"A߇G4i;ۄ" x[R&GI)R VXZR Eڀ(}a]?XIQhS_UXy{5AP#Mb͙ 8K-3[bN &[CGj:VNh7My^+ۈ:\7Ά$54pq WC~ F5ӤtGQaddLzѬ&IIM[= BI}vI8 JՔ-'-@Gsv1Ʈ' ]Maf60vΏ#`㨴dY#g⮭­(i`rݺ%g?gżAwMqO,%JcRG|TZF.7τȄ=ds5BB;{n)GUϨ\A4.dP⩢-;#)`+}Ǽ52SSOD?gxuO%5T^z3Zx{AȖCo-WAW=zl=lT V8Vf6<3FQb[#mer{7k& yY#zeJm&@a) ⏵P!}\4 SX,>fp`m$x/LnT" QhmZhQ࿴mƢv3;"*:2)q04ꖞm쁉 +3DTզQwڔݮpnv>Dwo|зar4$-"ןQn`tnj_cKЉ:"KU$mmvf༐FPKolXO,-celery/bin/upgrade.pyVm6 _^1.rFmC{m0 pIs$O/#%Y~u:wN"|!G`m ڃ5lRujfUZXAΰF(i򛛛[*}g; eĩU23,w iLVb)*UC 7f; :b۶w\Þw5^D&6{m[n̷ŢjCqٕ5z,w)c2huc(sR#4m{ T6LT^Wxu6{;|-,nc~5JZ}0A3~ jAz4`I|baX4&` c t' X}YMr QPd~XAkϲ;n\`WReLev> cH}wK +fS*e?.T\Њ_)Tg9$6|rkt^>%+(9{*o|mDN} & 0_Te8%l&sTmow@P}v7!3Xؽw 0"eyGbX_2$A< FԞٮ]!֢.#RZQZvK8}IA}^VdydRtIGܿ%z^q (ʎGk&05FJ\_}+zd4[0$cܰƃrSY\[O>MAI/ӽR}EvtRPϒ'j ! nT cQE j;0^ W\, =@#k?^ތ1L,{X$9h\AEE$LslD9qOg޳y?WfXh3=1#v>>2  a3=R٨*E*(x^|sW^w9`>s-<%iu/T\w!Wxւ.#_s2\mhCNUSH7sSB^eS49{\1˲ijWmxip?*Ip+,O]Y1;J׻?ߗw?(\hՑEω~Q=jִiZCMtrhXHYq@s 6W*PKO{k7-celery/bin/worker.pyioF~ـdt?,8qRĹ6#5rHj6}1Cb$]Thcrf17| S_DE䫙hO\WŪhE]]˪R\;S] j3µ+x0. ÇLBɚ9x ZU,\JF#QK}+tVqu8D"Z[E kPV&!E(`X|_LɼF7\ \JeSL ND7IUAX;תUk f_$%F*' DniH^7;g@.vֲLSɗɪ`)A4f;:s$*$+5?WWEi#|lj u$'Ι'HMj 8A(@+3UG׿ڴC[7BkċbgD,k@oHG9m ѡd6=cshr- tV0qċIMB!R1[1X&B+e Y6tjoU'2U V%e`r cvֈL'Jm1RIxV+h݌3(ZU@eIޒk4ňqP*"@U_TQL|2|%2fSnViR.Y?Ȓr$˚4+.* ^r˦sܳ ipt]4* G"wj؀}km݇.DZfh ;CG Q¦)My޵5]3QēhGkg!Ր)X1 Cuc=]V6ࣦ8ͰjQdHT"LuTOO?<~&LM^cUae" ƹM (1iY /B0WCabS(F}PiAzybYC,LޑijP&oydM+,YdR`hT@`0 JjX3(JRe*#I W}-MNIR'<3Krq@)Y7!Nvǻ5vuDɳ.*dI%CXETj5Dg-t-n ΠdBcrM]Hæ$(:HBE/TN-ծ=JμxƕVU?uGO,Zb{t5DpNV6bUhmU%`![@FD+ILMd(8[ubnbE52hT+ONN&np`"B)d=IT*L`IP zτTsfjM$pw3UEhZ80M6,Th`K4f]zby ɴ]j¸*ʰ xC\I ȋW|{ơp}Cd!Lj.Pp't bϗ/a* " dG^NgdATqzgP=k/"ȿe0ǔ9oiƲc_+b?ٙٱ.3 ^`k`2!͗P6-MpO"E!x7]pmlCY4L< N`v;*X:oEXIk(DMB¯GL-pZ`λ[Pbو.?Dv`u!\6V׈ڎBtȄ +f⋀m]@'V cY8X|vQ[&eUEpeS#^e9&r+ì ڹ0#es2170W=1Hf>ƐNƮvgΌڙa rVY/(r?,fܡEXC M@:ʴ[-42Mce{D-~!בs)X`@_+oN]&۳;5wTDb%P֠qDs_3}rqѹd: 0_Uy"ΐ|oK1=DBnݽo"  L8 op,tX"oe&=F:_qDx_nDG%ʎaxs p !o=j]3(J<.6>~%+(wObӥcmw،vM5/2[+ <:b vѽ`ŷ߃ KVh~LO>rmv= 8W ϴq]סV, ÎVo@1@mg`g MVSer_+DG|n1Z! guQ}@yD/ţ-Ŷ / &ď\EԨ Cùp7~lʚ>v07]kg,f'0W$J?ThNvf]Ȝ) ꪿M6G*NPKg$OТcelery/concurrency/__init__.pySM0W IqL =@{؅=nR,cYrG㶦w%,`f{ͻ ظ|ŗ=~c`.0ub2_0ޙ .9-UW-nG "LȨ18G#Z}=Rb_8B~Qr\$6K!!XHS5 |4Mh;XaJh#6=2 m'Xg@z:z]O:x/~UG/Wknoo%PS6;aSuntRu. 폑+Ⱦ(ZL ӛ{Xn2'O)i{}Xl~QzSA 0vvr+q4oeeun,Kn$mź+2>JUSeW40#)Y4MU/wSnէX7wamu\\e]^\]XTv?o)b].iy2̏-e73U|I"i\*ygU ]tWg0Jӿ߿%Wo.=vfP7˪yMlׅW>,C]s܎M]k]%4}y~.M/ʿ=};>5]hcQufM?ͫ|]1矚vM̲l Y) dG]U$i1Q:WR} 7NVu\w]ehrZH]P猾IVEYu@vQLbVqװ縕E2ͧ@V-3$*y5-Ku^LP%&!a*u5SLI|o|F,qW$jcI^a @elbUa~5T#! j^_|L&K*L~__ Cd /ab$~X8K[5ZqW"JCOb4iꆺX52?<\x8bt>!倵*ZƓ0*R13@J~1INNBM{//U VQ+k.LG:V1"582&I:@a`4<:m=fn,*#D|GG߿?7P fע2Hsg@˟S Fl 9'sueds_TĝE  pq7A/~ESm@d_#bڎIiS;>*;oo\|=>9:ݛهK`5KU8|_Wi7׃yc @t.Mu?⫋ٷopxCJ?"!0&ޑ:::2 a04+gg0WvQ2r3Yyvm@`ʠ5gTXK!xg0V s9~>ikL/" `tR@ Ydt4v~B.m$HV`'Ch 7:ϣ介o9~o]r gٿס"!]v P%ˑMe.EVE7𰇄"Th7$0LnIj9- ZͬP(Y#~(01J}E\WݒY~~(2 )Cqi)Uslp j&RiATz44Ȱkkv _aš?R>ej#νi 82o!mZԍQ; G)ܩvZ.V0W1 ](2v  0;@:qżFѬ-5w륋2$PtogAOh'7l(N鏵 Lku[Djpq5+QRSZ,p"L@A,Fk~FȬhU"K V8앍rAA+fUhDe֚dJX4+cٌTa1Z+0$Dssi ۹Qon)bwy?@pz#WO;6BmA#@.jjcxAm҆7&7mLehT\Jףe_*{uvByE룲T?op`J dԫmw_o҆>iLAhnGM'v˒Am[{}Ty,/FQ,L3'f,@noz\v=}%0sq@v(2]jtcOb5&G+a xVӼ  --Z_$a)N5,l':mKeSÕ} {DhTvZ62I/=. %lVӦ\f-'`mV鵭+Hs͖P !>gdjŢ E$\O,:D97`r?K$f S;. >" LVuۖ0zKFU4VwE1H1߾n rоS!x`^ky ,H2C_ V3q01Yr?E>G:G.Zn ˣ\'ôE`bZ拤V] @ߠ4x6=`WI-[f8HmI`X7 *0@CEe[\[.ڡ,BVpHch(flv%tn5l>r3th5Ѷnb,hܤN s|`a u)ؕE͉W$O+ㅌ*uՠ[PHslrQv[*M2ܮ0ze`*n(ȶBqLFuF62+DN\Ez3gk Z($@IjM5ehC*s "MI^zƨV(*AG/z dv9j#Aay= 2Q6ZL(01D 04SSv%5AIN՘xcJfɪ^ #B)É', @J2mGfh&xSrya!LMZźub{8JQ*gYDb7ϐDJmEOCJՒC͞]!R^CaT:.١C ~ _ƫuz$CKRV)9c(*@``] D㢓4p@!}9z2BXXxv zDmz%g3B %w1U{W# YXIM:ceaDb*JqRۉ0󱯾83' D=}51~/[x0 P}ygw XL.Pk=&Ke'8psB17}qky${ Ș3=[nlJX_N,m=f0q-8ßP%/wP?QzKAQ|A$#PTq T!|Ezf>`PC~ۡPѼm@kHjP; Zk[FQ-BGDхA3cŷ# %ac󃥻8s~J@n| )oKʳ@_׏bkǎ{jCЦrVړSCr EY#PO]ZvQBWVձ!^TLDkjGGTS%{i`7J5dhbjA OtSEc[l[b]ӑ]͖Mʏe r6+lM;#rV?Հ(BPu͞4-li0t@Tr"H|ۢBs6!RI^m)Z?F? X(}1 :Ĺ2ί-uYeŧڬGyudAgX }㺐~Ũ@]ͲN`uy]s2x r/&&@=.AncÞ2S\bEC{ ;/ScP.]q72!1F#hUDG4bd۞60G`֪'z]lN `Xr7N@BD?%Wʠ +tY9MғWhJKBBQ#eBkfkb|&=iz%8 ؘS`W fQvmU7q[&B~!&Eq7#J$6 n:[*yH(CX1'ɘDS1=D&jcdJOƿ 1n00FCKYqR)8\'QqߒŧD`VP ٰ'ИZ%,J0Wo! zFfcmHmxs0/nAyfc-IH"t'4#imFUzEX}mfuKdsFRj{'KM`MKB*/v"BM #{NLKq?zTGMkeS2Qm`ݙOԩ#$)$kN+< HP՘&ߚHNs؟V~(/P,\^p;瑷Egj$sx(FU_8@慵M*PW9ftGHA#dxe?-RVL&/3xJ̶f Jl,Xj1S!mK[ڳo 4p@$][ùPخ\2 U!-uN#:hqIZ|*kje2qTN[QFAdyI f]aIwY @?<WoQBPظ *G"Y'#GRQ?6VMh@[-Ơ&!=-:֤6`=A4hjn |FI: Ѹ(ctZ͗|Azg5\ ?`WzY$2E]DڗB1;J^P;zGz3pM#r7Kn>v޶/ _=(充8Q6DBÙDaZ wQ481ȭ-TD$!0ٞ_e7R{Vq8+xEf2+XAUS>T&@ c u&+Obz(8 gA(Z)kP~|P01DFg ztb(>|#$b림C5Kq.yk]n#9X!QuS`ɋɫᄃc dS^rI5VEl$5Sw;츜ϲ݌uQx;lR7G?ZV8t#Bx@6XB1^}0ˉT\''N@ɠcb['٤ʻFeDHo" ^Ue/D9ٖuPӨ([gsئ&~mAUƢkazdqˮĻ #F>c˫r 4E|n1_$Y+X}ɡ:XƻFr9"g!MELt/~­F"3RQ~FnsrO(g.E9M "8cmOfRM(40nz&o>U%@Hm }QP:eƐ/lwOTQx@39$.%WTueNQhXIp M'-pVq4,N4AŒHhzm02#NlUUHX?kD O1h`uG7"Sc-B yV@U Rv0zW{OVǵ|CЂ401K M]ʸeeƇ.]1P+8[`JM6/R%vq>YE:P{dwu 3gR.}0;?O+A莆yObiu+MX?@y>6qK{+r?j@C֏gpy9{J'0Ja|o*H#[2)Kܼ.Z?TZG#H7y-WoZ :UJ`i;~Z9N,)r smPHZD3ԛ!'h?OɢOFI{_KL*G`GX)'a`JW B.C0mAiц* /JHoraF@S(=vd(2[+|ت |R)){dFTS'- nul6PU8tf}BI[-3Ԭ)\<xL몭%7l,;6Ta nUeTHI[nn$߷<Ps%&eKG±cuUT^U:* G5U'CRǃ1zD"'Ȋ{bTU>7:wWu4LAϳ*p֕ɭkj91H*#CpWwB:3fZE0?G>ؼʕʮK4:P^W=ή8Y Y]@f}4gTf"H/#7XIn1rqe6.Ӓfƽ(r^Pb2ߔqWW:ꃲ?t30UL6Ð yUkiohEW'O+p̳+)JS7g,hnKMUK ILzrE!{1j!WWŧJr\$hG`v֍+Y7(8;Z/9~(4=;E ܄֮݋%>5ܿ߱/'VRzHj78ϙdqPf%bj7BQN2ټo{5z(sSwbWnNwm"E 2nEo1WDOE"Jztzj;پz1wg #bG,1Ċι@Gz@04:V)oA}30Rax,Qad%9yW%?#*̗0y@n\uqa{JQ,ҵjj-mk ƀUk+aa`%O1WW3[R劣dqķX)E>!&6jp,s=@[vϾ3-w^ЍO#,`], ( B/]{UOJ#1/ \vˤuSr9P5In+6hF7qF1O1y?sUSĪP*,M{N1`/$(:n_#tܺb{?wX4t;tFMHh&~xrZ1=AZgW9y)ȹAu[!bV !7Վeȫ&ߎzb:c\50`+ժX<^0nfГ$Xrg5#߱x+aRRWyO 11 _U/8nd|by\Qoh&V`z?&cz]9=kc!Ga;[kNMcG(#Y:PveU, ``T}u| sJgtېpڎ8궣b; W/Ym' vhp,?.>;][^>*-DOX@8d6OPXWE79WTʎPf,Gߣ{ ջ<:.I`X!(VhA[Sm0NTLTl)p9ˎb$ʉW3 R 6<3)0[ʪ|!#+"!/x@{BaŠu{Wέ[SPZ`jl=SILcգ2W8V}W!Qo(GU85EK*OEy ɅO=MY=DD7YrѡD|0`0[(w &%/=ej |˞9NmKDe:O7n~%R`hx)-gvjnTuEE3j:+rLF<%KQ`q)u:O(<``QAZ߻;ċBr\Y_'"KD"f*Ol0I#Wvb2VEq6B brK1'jK)hAuZjqz'j>e &0HLݺ }̫ ksةJD/̬JFA j6*tS)̮zÛY?|`<4/j8Lz8GQĹ(pUrU10=;)@O5~|`@\N=%6ƩWiہV: Ud1W _}Kޅ~犞CmT IXt8G:eP-zerC*Ph znw^@[ht1n_9]+NZ[}Yc5 !S>̀هRڨd+5g̔'x~0~v Qk(YqKxF!nkܺ H^o bhԼ1>9\L홋 xnjz.#=Cۭ{ay}+2%nҧ 5bYvJ9$J ̌66Rv-+5Q.Sz^k] j6l0/=g=ZRRrHko+m4qΈ^?i@HR v(Իtp@I|֫!sÃeZS-.gNbK'}vPOfMU/΅8>wiF=@5!!| vk. *<\it52nrT"ji n9cO=q<ژeǵX=] eN>*g/*WE큎uEnѩX%@s925yh y-a"09/_;8ϸ~,kP^.붫_>gm *qKTDZ0Ʒ.X#eHq<tQ{4~MZ\3tOӐLoj b][xzh8ug[? L 7c+6$~ϱHٗ1(!=`;c?.;!VL5®~E:SszW+1G;Nx7 CuO_O~:PKolXOcelery/concurrency/base.pyW[o6~ Z h= ܡ@ m=KIZ5c;Itd$sw.zJ./.I. lV3寸3{K9Y3B6o)%lJ%kBiٙNqJ[ aUg8u 5rZ l)+ـ*uXY*T)ePyƼcp?Rz+Y+%:Qq~JNo7"Wo;S}\բaGܥは4² B"tLj F\]=<_B͆j{ S]e3JYU]d:_9k@ S sfN, :" ^1c~~u.no7 ;:#14&#( DK*ٲ xjE₦+ `~֓c1f+BN4B yv@D~#$ /l ŭ-%17T޵$M-Q]qh_FtvC Fp#>$`==Ëuq rY Q.9M@VN (#KG~d޲#D&\^@`FӚJdf-;.* ۿޯ}]͋[cҀ0%bi_р#19l3kHƉIlpxhiXE) ӗQ,etkQ77ډ `X BrM ucݲe*#9 :ʱh ռfV*7p CY%]Ђ, 8ͺ1c2a(M4D-L[A.H (oXVqOƕ {q!]3e*_Z`)p1 ~ǶUO1xdl"m!B1%dep:=/jy"7BhC1( vJX9qzJ~̈́4kEf80HGDcϦzһy^lX MVP5(_}#X3l{0A\3T эN*R=7iWOOJ=~z:%'6*nn\ ux !賾BL+CXhsic𺙗C{F+;UaXۉ;V!J+EW$De.a" rֳ z–6ĥZ k7|T=u ȷ~ `IVɋؕ~s2>@;PFtpz xjX'-VM?i,J}O:'?vˉ Ix鋢M7pzTOgg@ѕ4Qoj85@}4&Wǧ׸>N)F?W g zmg9 ֤D0ȶU qQz(jgSωtPKolXOVzrcelery/concurrency/eventlet.pyWK6W K[[moڦ4@.Zl4Tn(Y]aE̓ٗds!ߒ5x#WNrG:'"ֲ1D(m:N)VGj9N~M:%@7R8nY9^Qv]EUl͉7YBÊ$J`X#>N⒛Kj^AI)|<|>vkr<%X gǪcN *::eCZQoOR(Om(%kX{N"*_@{#ǥZog.i7D<T((-͚l77eMUkT h(r(k\R'^K=7pڮ `)T6Jc<-GF@%C;" CpA6]`, r!Gb~S {oq3GXYR u"qb_zߔ(_B~VLa?g|8Wb?a!ݴ\3.]Ƶ7ϔ\ytSGL ~WqXgpv>x<[ŲK1.rglZm %!(is7O7Pb!<81?-5G`$p};7C-kڅϥ@#K E0ZНu"Dq >7ՑQ=7Out'mi׹9M%fNf x֖𵛈.f"5y# F\+ǩy*b:'H#kx>^*h,nUa4:x0"=7=zEйZIX?8aE/M6sE= utZ onUe2h鐸 Ӂs0q>:VI4IFc$0ZU6_y*|~FmP6<0Rf)x=[4vb"Pvm?M"qSi:`uY&Y57ڞ/'0jNMex }agPKolXO%p celery/concurrency/gevent.pyV݋6_!&}+Q;Bه> ř8jd+ɛMK>lˎ~Hli|h>ӆTje%=l~u+Wxi CY$indB顳J oZ-a;Dg5$Gd[L,!v:OacNH-y>"e_.E&OO]%K3ޖp8DH@RM;CA)v|5ZG"g̓~Kff$9M> W7o+KDWֆKA*)8eU CVA2.Jɔi\ W$IצV,IPTНEmXާaI SAPJxBv(8UY+s&`C8e 5aiN ~rW5F"rJ6WW& :(cKXԀʥMDKYKhfCS"y̰ZU!J0lJJ1YjKG). Hs:AX}KuT㤘ޝ Y(܂/p5X7/iHNp 0[#bF|`Px@q? d|XnA <7Oj==69MI**<"xn$m!Zo!ƿ2(b}$ܝT dlWQzn ъSrڠS K` Jҭb%GΖʄr$`>5Э|P!I3p~{6-ty7Q-آw#@v]^Ѓ*e5, ]eaEg+-rbo\dY[Hd6D&%aƢMbBҠ!JY]'Esj"Eql8U-^)N븍 A=YB •1 8 7ݧzu '^K,$VˊC0]sQOgEK^Qs <$|XZS.N3hB1R@ |v'DXuuyZdOm q^Y~\mWXQ@4ʞqǩɇ?Ǜ~sw|xX?PٚN2_ahltnnq%+KnNYDt4p.i5\*8}lрj^7@R 5)5\eO+֑nS7Vf"ˉB{4bE彿Oxw}cX+ԚHw/b힛ȴyXyl?؋,/XU&"Qų)s_y=S>r &=~6w?ч@--3ogє⋁EmN CZԎ9qQWKo7$k| {q =6Uu  EAwxpOF%G4]zʗ9`8uC ج }L0C/ :`\K0~G|^xxqt]p;+4˔T^&L/^\J\o[/CZ.e]zC/0x4}Z&x/. y·$:jz$%3i/wK`)>%xJT% &w {в ;K+# '~{Bj>ssHpג(88`q)j ڰRRcc@rT ]`+f%O!VV -=s]30vj$<טesi!?KvvTW}k`B(г&G~"デM^d.)ܝ{6{NXү37pNεJ@*έx<3dp%}7ppEmf*f㬛MZqÂm E+ROEyݝy 5Ty5Nruu5+V ZHX7c^Z-CsJ22).ml#HUA劈fZ~ ąKbaQrC{eV)1.%Bq{rVy$I%KV.$@Ɔ'peCǻm arS$&tHF=Gm?k5b1k">&NNS2iX>+V"+}Ӡ!;#u+iM iqCZ9sQ|&A/ Xoa;cHcIuO_X(HosxUƒ[e>2઎;'b| wrػkGNv&u ^h.޻ZTGm 0Vt!H+K^BxUa2f|INpIe`񗎗kh_r1H7O-q3Q`K +z|j33ĒXJҥfk=~xO֗RTX pcc =B] GWp!y)!3(HV@PKNcelery/contrib/__init__.pyPKolXOoR45celery/contrib/abortable.pyXێ#}`v4 2c e1]&=csn.8@FRźsÃ]c~{)޽{u>mKIP(BkzU?8Zge62Qڪ-ɾS?q\P/T8[)i{hW8m7m"[Sɚ*6;S=!a* H JOm5ej2m:ۓ:z5yNIu}qeG΍Qpg }ƆmMH/R_=rwΆ3Wf>8|$oɍT?RFuws=HԌ"]9ccyRY80()sERW2FJVОo;j(gɰ F_M޲(C˥:-R;:^{Z>:}%$}f[qy76Tל5ۓ\mWcZlO!Ӓ?`qŖ˭ cu\7 Xg @n݀SK&axmT~dg|j+zpj֗[,XBiM:0ϠNYחTuD9]@PI|H!ӌQm,zuM^$ꞙTU{Nk9g[<ыn{B~9 HRinzU٦Wş^Fߣ[ Trt+N~/3BwekJh[N_W l{L~^ln{#DW'@<_{Ҟ݅SrCLxJDO#ƫAF5ԩaFc8Hpl݊bᤝ 63`> ̋Á+UaPI(e/P\t\TQ@ x?~$7N"Fb`R6#mwt!W%ais*ͽ0(i3L SH{19fJ'ŴS' Po6Y&gKXK&AA`ÍpJrK8m>bW}@frs:yp!x{Sz}5<&kH}j&MH.]sVݱOO#Ԛ/oa=RdjH8R4dڏ/U5(d/p(򼚦sz>}iL@֙,pRN t[Llld+%|--jw "N/l,2eVs՗8`xsLRؑÉ/\|c .\gaB֋[n:FB3$S154o.n|GӺFÄx+%##jB+ A\ÏϤJ&v!@]QwV{sW󑋡) 38E¾˶4xƒf57ʹD`\_Wծ}է0aXڢ!ܰب#TXӨualeKpO 'Thu01^0S2+?P_`w0-l&r av"m9X YXsq"uSӜT?!FY .-o;Ɋ;=/.a @6/V!APKolXO$I8celery/contrib/migrate.pyْ_1e=PHzYQXbG\Dc)z,`pW,ǜ8VC*LwL݃}"O")Ӭ-š_/x+&Ivufe!ڲ.e-3\d[{EC{el_u+MSVF<U- C2ʳVqL& )k2]dm̂jAiX 1:$L?~Oݻ&o[:<7&/p-8_&7ez@ ~& WH% }6`,%ġ)h]炷+2kNozk] @.%h'OoQ\}HU`GzEA#Z?V% . !;Z)Ig-"ÉY4@G(XpQ+b[eRݍL*qbա4ঌz&M}Hb'W?<%+CGZ<.0}v\m~vYt/x_FF}0Z|lX[:cA^Y"Jv$mŁjՖ2>c>R#j^%QF'ABK*[W N@gZ`z1 nx XHG¡V 6]8X/./ s:* b2H(T[e } NyA8pt ;\mR Uَt@kY5Kx8v 3ďwiȚoԫ8ݏfq {u,)b8| rtt w鸎Olr D]먵 SؼJ#x7\< 5 -3."o㟾]0g*1qw_ҏICXE&0еc=a9jKO4lh.om/@k :HY|@ 0,  ^2 #柎 y̅p9{|8(:3Qbw8VmHb];k \ګߔtL.6ݭ}MfCxqL^/>8G`8i|kUt?A7>0f9G4nN` Eoc5q\aFUd,k1}%-zg!*ҩW,eH[*b:bW)g8?{B7y:?=/}uIK}ҽ3d4~t9yǛlIoO7/6 smN?M Y/Bc$;4*=ҟc2 Dk \!Qu/ ]$HT'~L<V PKolXOoRpcelery/contrib/pytest.pyXM6W WbAMAZZmbeR%u >lٻ6 T][$7 =߈/Xnk(v4̫}%w>qwx<Bje ҨzQ;^V*tt-Űup6u<*)%+kbt,WWk- nsqv` #5F›3,jFoP |x RF!!⭒z˜+>pM%vg,d#/ J $&:}asd^7+v_s·)d:8Lp@z<{c{fG4ˑV;bi0#1 xf7Z ZF:!MoƛUESC8=-+N}˙Yf3o,Zn8ySCgѾ,,.UxPFzd|-L;ףy/!;V "p\aam!jHw CW*_7pg`ˑiqYw*FꀃV7L/öa>H\CezcJuF@6/cd{ ߯.u[xKe c̠ǃhLhɅj3 _(g17yCs3t`Tֶ@B]{01eI&]7jrx- ۅ0ㅯw@K„1-$jZZodMwσQ9AMS@9&ڕFRdKXVCjn B<޽>>}7?k1J_4<^9b\sugFuO |>ܱ jwW ?NMb4?wޞҫ{ 0^2ɮ=qq+li˰M;PKolXO*TOw/celery/contrib/rdb.pyX[o8~ׯ ISGHawƀulinPs#r9$u`"x97~9ޓH!K.6'xݒmaSI\ dYK'6\OrJ"Yԁ5QpAueNɂi ]NʊC]f&3KpM_sJ-|Ӽbz^S&{ˏ_2vu{*#IE + jx%"$lGa !b ڀ8yպϷ˱uuT3vO?%vݦ'^U $Y:*S^aXDm2Ffìј8P]c -Σ6AVXk\e heU ). LQR *n)%d"uqfڷvwe kי՜2tkfRwٞ$kg-CYF |JIdcpN]u54rseP_n9s A0RR'1'y`&:2)&zF؈ 1l+oX~ Jm1cia0te;3?- u}Hr.%"@Av$luv}37\'/@0YW+0:~ZXȠԘrp^4\KFKK6ȭ3%Uafxma={ˁƔ݉^֠vWaa/CgxVvf˻.S_7o+Z$tꔜ ~%+AL#`/]ima1pTyC; eLFt)˸&"mB0t2N4@br^O|#{J?|mJh gCJT͆C0 \`PQB^8,XL\g6eYBh"939eRȌ.NΨ6L ZHYLx sɖDa! ᨦȑ)?q<#: kF;|+"=0"Ґey~ʓQH}um[ּbQNp8Q Nihh`5u}c&tG=g:0(sQ# zn[",3NUJI5ԷYcf-=G|>SvCjۏ,_c)QV&Ere5]]-'b(6tF6ܾvT1125'Ĉf[zL^||?OS~z*͎8%ܺ 5::-E=v$s_(Ly:q~~q4d;H#R̟3HoN_@a&/JHc9y?OKsHyMiB)Ió'P~9ǽ7aVʬUs !2\8ULںdߘjnC׍ͩFԻ0{Kxh$tYi>E !SOMI:#̸O&؇Q@m ]º\-_Ppʠ5L$诳}3&#;PKmjO|Lcelery/contrib/sphinx.pyV]4}vV"FZe>X\OILk;mҎ@H䥍s8%Yƶj>k:a/^ek[یe >N>z`OyW7vlH~r =}Bz xBюםU]2 XU殷}]q<'doyYU&/Qr GȜ彾Wd\fуS~uoH9e !$H3nՂDB^: EI輸ުN}`7aHV-}3|=N& `>&x^jWGrtQ!(g$ Ҍea.D7)!> A~ J ^vV3PN ? Y#wUN$n4f+WdzaQH>5dprw57 }>Q+B2UAzBnЍ}ߙzj(sE_'(c?KXK9pê"Yo睊*BMLF)9ECBE<<ÝrºD^%рBxD"b~A_#jõ!lh!JSxciOu!Ӫ:dU ^,&0Ra_ nO֠']ݝԵGZB^/씷ۋDYsbDϯt.['L)bU_VKDwer<ʩHB#]O e✆ b\loz95bgf{ze"_"p:U,kJS)m&̈́8ڡ~wN]W8XJq8 T=3,i Zu6#*L8o0 ^> fp$<1o#*V7煖Ϣr 8q%98?,[&BEW~7Mr1"Ν.mM.GTG 4^3q|݋Htv⇤w zC7 eSE Ѐ׌0Q+nrIG6QK]F.: 1:c^7 {oN7gwu & 6mNT qPqY-ͩH f(_{\BSuS.U ?711i9iyPJ.d ~HLGVF̵8B1aǐ=B>ܰ {@cUp /fBHO`A1Wf}~Öorol^2O~2哨2;1awHf8(s-u &Q?9$-*5ʶyyFM+kZo p4| 0~^ˠԢoIB\eYJ80~y`1&R(<,`8 nXLPU!ٸ(rJ(̂^s5ճT2 DՍڐ ӁUg֑CRA`25kRQ["<8y-cy`+[ ]1#=W̨ DDpʣ:=X"MR?~TT4n D*[YΥ@sز d}:_ "RI7Ѕ GKGC1o"6VX;7Md"Y 9|}<߄lcc+FGE(Jp6g{%]WP|kbpBZKcϢVd  \O:yxzpj66VZ\EGiN(a;8*'mA0Yb;clI FްL#7legb:hV6ja4U$b&T0@ 9:Fx,b+FK$W raP)5NyNQ0R4 O+"/zbruR0B̀qbHS04&0ҰM[GD8hoe$XR4RiL8bCdjo,ܣstk @1 }[X&mCtl6M³ռ\I8&fCфYGOn2ǂKF[ KI΃O6LS0q~O6Gd+P;X"#/&D>Q+&GIR>6$1o4FYj~yr5c0CFwjes3o ;3|blPKolXOzZ)!celery/contrib/testing/manager.pyYݏ_l$ y0ࠋ˶X <ƶ2;HIK[zEjf8g{&aϩF$Y=9˪d"xs$y;3<'pIV4YBn)9I$c3EL,h"ESנy[=ײ ih[岢y ͙ɉyLmMdNIZ;J_x.#XjG#$E O2x.B,8ox@Puգ%Ȁ.ṥ7Jp>RV rEJA yr2˗_7 eV:.v '/׌ #/O`sF`sTRF?Vf=2(t5eD z[?#4'&+Vhc.φ׶ƠסnFY6K kߛ-׸,/ T s=Pן`N]P-AJV,w GZ.rf0ݠw'4  Q] \ȎngEE9'L!+Z]WGC F"63\m00a^޽F&ϑJyB$%g[X !PV>yVV\XapcuirqY*uF-A3hiH=-Efe8SRBV;3E .AHeb  kZˌ"9W 9xyHQ,{̡f#rZ^e q# Cl1>fU,oeS|R~3+%-'Sf53;PIo~~Ck## 9`&- v'eQ^:@5w%tBEYJrڴƙM/*ҩo;UQ{oў}שE<[$$.b@'zN<(b8%[VDH፻Z %CMuB u mn;I?{NkIT9kB*έi}:Vᰟ(l;A*0WJY9͟o ߂Yo 1S}cMURl#^@A!J)XlԳ'@G&F#á4?R4`kv儥fG뙍VB`c]E-͹.Kpe`M: :DH8*O.O\U(w{N/_7ߩgj`͍4f:^z*@±{K&׍P=MSvGU#uPlȖ_Ww?čr9N, Tܗj_Yo,:?qT!^oRZxD lа1wmzi(G8mꌝi_kV7óaq򶒣WBe %AiT?^ۺa\OMXj|H9&3??׳"ѰMu$oDʱV40WN=)+ut|\NBCkS}bm$F6 Ugk%}TJ*'բ*߅ yNTJgLkg;o_U{]fQkڶ[ vqcJMؤ>P.Zh#`jRs'bN=ېLJľL |O9|{A_sKPnlѳ&obPgb qs#\^I#kْ[^G< !M S(0fe -A1k|C;+ RuNsZ83ެ/ Oyn`)&hH#n8g[h't)IJj1؏YօL-aT Yles/mX 푻X]By*? ҃ZI1E+VV1* ؗZͿ|w~DOՃ׽͢nq,7_jI|s ; WyR4gi:߻Y Ѥrd=L0W^9U,6uK|s,oEbu?>Үo8Hx!4nsrhC-ռar}Ⱥ* =9Qr{Uud2UL!݊bMA!ӗe-G0zNbxɹP *p$DƘBf(TB>dkfe]F5.&sW>y;JRІ)w(< ?+=Wl1Uuטoy1N]/햢[UԾ7بy?s?.O6?,1?_qDWn;W.'mV_ahȘ'(zd9#f  PKolXOb celery/contrib/testing/tasks.pyMOAn0 wH}@ =nېC~?'Y$$%Zk/ 1˯`ʌ5%Q9ZkysS\2+MrJnǟ)H.%QS$^^.yzѭ717'?Wˏ%G7&k? |@B'm_^[~ ?UL$Jnq[UPKolXOjwm celery/contrib/testing/worker.pyXmo6_A$| [ úhCQtФFRa}7ْ-D;~l~mRi¥f+I,kʫR tNoZ-a Dg9$UTp 3EKeқ]k` V8_VEr[6LY4zL‚{J 6PZfKBЅPӜ0!6417” 7-:W ClGD*ISlOw?-3J+y6$ΊO^L[D ]3ʦ 7hznT;ǎ,XnSh*owf*~63R"#ɲKC6a-X Qkր²Fը; OTb@2AUM[']y_mQIޫ=^-(}ѵk I ^\s ڵ ѝgG]`X"qeK"e\bLnSyPD5љ3O.ތ9Qu5zWLJ*Ģ^V+pN.X溼0Eϊ;8s\1D-G>aÜ,bP5^]n]HJaN`#~ 'Q a<@A&nrG(k(w;`BxňB1X = ݨ #i00|zt@BTCJ5bU~̓F^-&D;)lT6 &Taiy5vyTEnc7w((7t 0;ʼ5Nw1u*];( 4^lUe~2og(o9A 2mPVܣh(. H7e~s]lKdW5s.=_**H;-3}'<'{42 U![p)7;<1N-~A1 3A%"9ݐ߹n|ΊOA0x*zU_|j"Mʝ<:w@&0%~4p5*~lukv*q3xFl}ð=PKN'.celery/events/__init__.pyUQAr MzH:9w2[z  I}~;ÍA+j%^`[$vQ+'r>oѢ!FiT18,f̦G..X$1.b`&**rAtE -X&"I _uk%Kʍ1v@blki~L4FFޗTGq$PkpI9$8+;A2ܷԺJnQ@4ڋ =6"F]س^?A)y(g]f~<=;wLRQ2gPKolXOa)u7Gcelery/events/cursesmon.py-A%*u7$A2Fw[1$.y%E'{ [>Gȵ9J^K(-b-,)iNKnljh-CyPo|cP! y*#_}8\p쳢~ 2Vrfy8h5yX>%u'E^/ $ LυR9[p1>ãѯw6x'Kh~8zr| M./Uz'Ch;l¯S~ɩn{l PXZ3+2JY:(0lft˃1eN'̷>dO eU?,94߃ AOON^7M`5o|Zz}^G˨Lc@Ci&*`Xrf!e5hJ,)|e)4<J Jp_ig!4ՋUV8eKQL>sew5̥~3V|8ik`V"iͭ=?9 \[ɢRg^ YqRԃקRs7Wz uXz%J-zN_t_mB`! ֯vW #(Ap N㪔Anp'm8V̂ g:1[^6z$bV0>V#-̳{i7u<n@˕4+Èy5>^ vghqS[Hʼn$i?ӥS0 !E.^7@MtH@mݣIh{]hc:QP] q1pk} .:BS3z ꮰ͑$5ZTOeM&w6@?*5Ѣ_z%"-aY٤*{ǹ> [`c'{bWҫf(L4Wx!A_76؀%[aḏړV"Jg j\cKVؘzzːD!㌏n>ɳh'@) ]aF+X1܀l6JABl@k `f|/NDQd*PF0 n=N7I.Ċ 87]!h6MCns>-(ݐK2N<[P/ 1/bn2l=>fj1g6;[-<5 IQpr[yfj#n ^6{cQ\CԴk2x,_b](' J{\JoDa91&N }j֔*^Z8, DYs :cP/ .5T HK>B*7 yY<4ӠMU3AB#YTUPtFyggO4K<\Eq޻4Z;fFX1\ )ÅSȊP ieU6SMu}]ak)tWpJZګFó-ON 1s`2(+ L>Ձ'Gv=L@r%yPeDޣ3gLS)KȖXLp fƘ7\dkDtmj\=րN03] "n 2`{T, Rn;t*oOH" jf@/APE5t'I2@gd5ݱm`X_C#K`9[QY>{2Bn DVHG+8^ BtjDmU@%%}O$z:]? .ad. M=1AXXգjKA@Lfܞ@߄ض9f¬ma{ Ӹ% XDC F5͐Бew‹w<%ϒ(<#s8X۩f7[4t$@.vqIyruFTS3DŇh۔)VFз_O#ޚ*z5dOUŞSAv_wzf^=Gk/:~}rd^^2,$6+3u,ֶ5Q{'Z:bi?n(bOD^M0s!En: ,ʻݸ=A0m͎S[ Lm"se=ZsyuRYShha;택FD>et|R/c [I OUܖI 5q?<8q48۸bRcTj&y= _,V*z1vʠ3J6,}rQXSk79lАȷIM!;Ҵ_!ɣ_!ᨎՇ%Gܗ{#myH-m K0-Q>gC>DF r|r 2?7tH5HUX4f'\.ڲߴq*Brr>ĿdNwҽ7zM=gr|0@0}ҭ#!Xx/[֧ BX9nb(j,UQroo,-CۧxśXXC ⡣V ^Ub 댬lMT_V %9oYv4x<#cݫe4ZmC^Vm|A_ze+`[ aC])Z3ʣ]3˴2mJI+vq0$^qndf[~"$A*EѕB͋GggpOoom^<G,IZZ^:huEe'w5+WZ-XLjT0 rk2y\*KXXҽ> T[1ﭖU^V+VjwfvK{uO~碸MPcw wE5')mOV&A34gjJJ݂Tw9sm[҇ɈX%و!9M>{;Mx#E9* So%0 !pշŧ)5Kaѵ)%"aq-:DҌɹǍhfq1 @ @cu9٠y ;յѭT+.ٍ;):iRx۠+495:]$AEyɊg_%~1<;R{],ܣ|9:N&})gH4/N64vL4Ɨv4ァcMnXEqE_YA':T㻵?/_N$ARb'tLVӨu B*8w|K&Tv\2͚7ل\L'2EAN2|wP]ls~5Ԣb},V./Sۍ{ryb^6%OT],%]90R}MP#gmthLI0'y hb=ӭ+1q٥rIve)4J&Ot!FaIXHc,sT3L?yR܋fu>| KiIP_2ml-Zj^H汈~B4/Pkw%[ oḥO *cŹ(|uˤ<: ƦPfBg,_ 16%w @Yb ;W ai:6$PKolXOs V y#celery/events/dispatcher.py]oܸ}`-EP4N40\u${sDRi>U88Kjf8]\\zEVTUG&d}- m_\\O,Q%iOЃݨXi[2mkVvbvr\_(nhOPcjy/ 5kةz~atPNJ b0\Zմ_O2x;y2&h&P&;7drkZ[rT#W]*/HWj:*%^er[x+kWHzd xB{ |*$L;Dq0+#9rPC$JZPj3ᣐ QP9(=1 |g5@HfZӒdZ}n[}+iŘ!AR:'mב,ĥd,⎭$1oXA~K>"BHOCYm_uc~k\>p O#Cjwko;jVjd 0nɉCȇz@=&w3K5ڣ5#d~vQ͒X.p!Gk _6͜Ʀa|8+!|gJFZlɬK+ھNzd2]BOS>cH]jHԣiW:G .{JlvKY[ҴcwN9̝b_NzCf%60 ӚiHb0G;V v\MJωFoTFjSqƆt$'7E}xBUSO1N;,{ﭙ`nӸapU>镞47~Qjhۍ%h H6 /ngNP|6K^3Z[} C,_W_+w_i7rb^pb{@Dn`({ZEgO0V\J{e:6=؛M#q N{(&cIo/bxq77Q0ܪK4_ ̤i& v~e5T•ŭa1`;:"'5ܔe4 ݺq//`zҳFQtSqrvS.?FXjm<׈sctgPKolXO©?2celery/events/event.pyuTێ6}WL[m bk}(4W+*I9gަa23pfZ=:TAgנLրEg[mt֔*k=R>POvC@km|A1EmyW{|#ˤT]G +31N 0<㛽C/9XgE??=?5v.xv 9 ۀ[#luhh^¥G-{~]'.DtOX kcd e1a%u-쌴H[7kƮFAʊxkWڱ`%ݻPl!L '<=xNB5QX5tA;;aÁi Ibqzp1 ;{ 6T@O誙|$b0vxZGw3"䅄7W>[W#y1?EEY޸|-T^KԉcQG>29L4^Qv/OH$L+EPu &}{/3]iZ{&PK\|TU-XDoZ.2GpBg]1\}pakRu+jBiNqWĞ)W'642 s8 CBeyj3:)*e *PׯY/8D$d*%Ua7PKQbP"{celery/events/receiver.pyXo6_A3WX=!ˆ_[`C%a,I%qDJmzH$x;z>_=FJ$KTsd|@bN@!5%N~/IRTfj6s"ZBz@y1 3KMwXǂMJT̓qÊFo'LUwTj&xgm{K۶ؖv:k=qu)]к2f=;6kplNĄ㣋F\fo__..~|S|zsн|\zu}}?IK*%Iz54+kH#,V3DnbV̬$w憲d!I/Ŋ D Hg2*p";3ov7 mvz ;VPHUcHCp|x(ϊ`Dv~1%DԵsHTIPFh$gރeF9y/f!xuQ%qٻȗ|IjvŞEN3O+GGpV-KS^Zu͞Z.0k!uf+#E(nOYUm_!GWcu|d@@{DtLP>ҋ& 8F1gt[.G .c o{"av~V؟t9tN'9'ќ >/ɒ^&o~=wO* sn1%ꨙf젲N"@Rtg˰X;[SqբGnŅQvSC=RY"w[aX9z}*IdIa Hmn_lm(Z,-|%scs$W-;wҽ+2\m%b"{{O5k&߰X&4x$ʃ*X)mh  K6l?/5 D` 3_gA/>s#ݳuuq۩d5VQ< G׺sJ0o7NW4(Ѭ0!/ tz@hYC&&_'>1UX1܁‹I_#9axPZVTk5ϮԠԝ@[ Z0Q!{).%d}ÎpȮշXqvQ8|mJ#ZC'T SQfE3s*3iURXqǨFo Ufoc,"%pٴP 8>󖳺Ry9=WㅳܓKT>Y bn1&dvE$qB e͕Uı)pׄJl}5sCEoH5^nkX0_-wSx(6StėGJ''lPWR 91g:QciNc.fa|V~elK$s3EL_7%W?GS:۩9bMWFYG| <5ԱEJB4TxZ4%ˇ׸멃;y E=RPBYoک|H?^DegAcLc^m[&q=O?7X p; k~QPKolXO ;VI celery/events/snapshot.pyWmo6_A$n0>؀aP4fCI%ݑD9vg/=wcwwѭP-]w d777JaUUo.#tQeA} `$>&Q&n39&+DmPΖ`]rrռh&%Ny {/G!H})KV6Nk=}?/6cnG\W$. 'sVWĪhrZs\L(y^,*O])yIf-48;}n1 |qut$C1, M3B|p,w$oND-ݕr}g1ZnS5QR7E䌢0a#SЅj}X?)r.ݬ~ ۲es|Ⱦݿq3jWw|L .+r0BbQ5,޺d|y{Ojamd2>#$T#hSro5.1Μ&T7jfB_pkn?835\X`޿cxb'׳MK) ARYPKolXOSob%fcelery/events/state.py=ksF+pq]t F>nutVv})YYoJ@r("%sS~0^̣_DժQeyUF:ZUu@Fj_( MEN/}}VQ[EJFm-oZQ _2^V\Q6knQF0`/0o@@뛪7IrF@`Vɨ 3_Q2VTYh025 @3z fNDˬ>6U|U;(/hF@5NCOio78/e%Yk;Z6JHS\nlTžU)'Ѿ́9TZ03QV5F?ZeN<Ʋ* h|AVjv6H~n R6+t+~:ĪΛ"_Jj(L]7ŅzwH;ܽnkJ@و[V~,mo@xɾ͋fZ-~lQtW#zA`2c6̍[3]wʝHDCT/Ҳ`.p'z_rX~wKD*p]4FJzԄ|"u|-ˬ(2m>,ծ^SY]W5w~]}vw~JXh䊾 G_O%j;7h l>{)$_4QY.T֦.7FOY7p^I{>)3`'*MQ-`ڶ,*vu1#dY]~F>fDIV4%[SmQLe Є;"rQcnU¤1idG:%d0rmgC LҼ\WDYtUu lO5!V+(D7ZW0k+SW5K&BQFF&ؿũY8(|jyy $~"=߼z&)M/`|l#\W- F liYfes(*a\^"ˋB~?o^~m1M 42@/usA/Ujs ftWt`xvi0qT8z4:O]8.rzx % u}zDGkBV4X&Ez֐Vc)3޼MNs??{yg֭[/L: @/ԐK\^<{n}Vxqp{ͨ12bGI 7tO5v|ki$1R '}F P73W(A9zDIפCv@v3_n W:ʚ8G aUѡړzf3.Q04&0Gr b'.Bw| TH %? pl"o80blzLSE <ޫD/yquDOԂ?O72$kpfIdjFvanDqmQ iLwp SV?g7n*f;vzzsqE](KteW3%k1N@¤R-&{$Mu\/#9 6"e8^Rјˠq>IEǐШS< R|HNf$8~#eMa>puCc.a9 qg@}ytR4fP3sƚ,fmM5' G}9n#k$)qaI/,TO"U'a:-e5%=]ؑl/#K͢᏷vms$x tF gb Qp ȎDma+3hm.T G?~= ϟ`O? u~2ɳ.gG JbWpӃD#^v_8"InEV]=xif̏&ayno{liV2A~ U % |mۢ?z hFBGWn>*Gsx[ :ݧzøA돳5߳kk,{;#Srߝ[jكbA@x2 $'lEK1Љ)s#RKr Ai-=HbGnzV)+eQ# &0J+= +&5DH$Nn 'DbC+#R2]0Xò=ޛ.[ ](f->eD}jcr!.2x;@6yB}-}2\cbꕪ% 7 _ vlyG,Y/EfD3?F܉fk mo1 cߏqh\4vEm@Q$`X*,-iFՐ(ˁ:65 fvuBaTN!yC`]`0`͊ F)94t0aAR3Dh)eTm%G0rf;"GX#0|1TStiluÃGCc*C/ ް;lR6dK|D!s0+d'.(an#RЅaDO?fQ*,Y + ߪ!R/\d_o'Hyɟ`3*^śv }t ``# S| NA=` jCy3 VNFNL$OӢun4ӗ/{OX8Ob= Iw5<]Qf9#r% KFt3iHϢsΓTN11,3 KEw^:K)0-kxܭPqqǧǶQ; LZuϠ31alA&/nGpftx6z46RUy,kL]DŽBXp<IGYĀAQ^V2OqDpCǥ!&vC 2Yu  _vxJ$Ʉh>Αo)=ѴOKd)hVZ%*u CPXypBс6'ƞDɧ{njj>jXohf2]TLc#BQZ ūY[z'@4lgO b_mPj]T*~?gvȲ -~ܑ;Z F5?7[{\xYɗsc X{*#лMFI6E-x!'t E;:ٜDnqD0dW濣MX[tLKfTvEt:HxeX0C,tAC_f<88p !):CšĴ DE]s\QLA/H~lc*k!1Ӓ;4؂Rjy\ފ0Jw\aUkSdw>X`Ma<:lD\_9uX7r]K6൷`.Y5$g-|E]! =+r 1䕡6lvleI:bpv%$lta"`a>-ܸ GՖb0 AKZo`,tl\};00Td>>OQ/-_&#C4y%a4JU_3p? }:iMV)4p6ͦ8Dծͷxs<;xyY=@[V*(_w=3`vZv4iu s vGA8:iޠJ`V3%Sضn]6(nO{4@A֬DXWNoF*kQVO Tlɇ1j1TmU?90(8z9KBsr\<'ՒɬDBץdZ~j]FڨxشsM莯Ewk`_tzbŹeu> s=gwUw?D@/FFPAnv>Y١ݱ/ꕏ<$d{8$"CE# "}op˚oSb߹~v M ⯧~9y-uGu0;7u%nŜPl}|klsδrܑ1>CL~`z^)qxzfb. %!htp4IG:w;Is}?A" դrӄeI'^ğ²bhBL*'O_;n'`DX`ǰͳ3!5Qs CSјXOvI JlǏUzR}_vw#]wnkUxT>b6wʑ6ABG 07B EXҖsZ*5W=16ymV[QZ  ԅ`Hh0.mq +f~r@H~t|qDS6a7>HP" d}*V1m-i_$*Nmhl :t3<їXo+,~B(NϨL&'b]^>ʭRXc`>Lep$M1|秿'P;r1KcPjGI"#+ɂnM63Fk`1(ft82ϴ>+2L &gh2< 5Ь:BJL̯ҷ!_}`f_ ;^`cZW@Mj !usYb3(2õWVڞN\=Zq"IOCui o},7+Idv}3٠sQB|oV:bzI,!G ;jYDM+)n& J5@UߢWPŜ&yЭ*(Y?>/9oh \U ;QR>MB)Ht<l@0!ݨOq愲?H)kL:K{yAdAA] ߠJ顬^fK 3AJ0_׆gMN. _DZ]P/5o(ԺsL^ͭ$ʅݣ]sdh_-ОN{tc#f6ϡP/ ({a?3632Q?(l|b{}DՉdG> G @oyk&)ٱ`@/c7Kl~j9KȻ2f/[e] ]$M5: ~q Ze+shR|P/PKN`celery/fixups/__init__.pySRRrˬ(-(SRRPKg$OV dicelery/fixups/django.pyXms_>hҹ$7uw|gwk`@B_]Rrzө> X,v}WV/XҲ3-dMV j-MXdjNei)4oX&G)U7R~aM-ꥲ wvN"?Zɢ-=*kVRv]*%ͶfO\f|G| n7zͶJSIJƻz$)s\Vu=B,AdB)+KP#k+had27|`Mhd]Z'~p/?79|!j84k5glbkWY"%^ێyӮ7dl64/tQċU~B৛OPr%e$f*nM#)s%;vv/FV~x'\3:Ax#[Z bӟw7/$>S 1-H>֊Nw))KM5Xƶ1hyJxt7Z_Zy&i(ZCȭ7#!">>]r82$fuA"1 IK  7=X0|;=k)&. r)b߹⑇8!16_MOlpJkKMp-;):8 <]- 4kHk  kW+p9`!L"WjPl _sxFY?53 t\hׯ!,U#'$㐜8L=v%J4@ +N<iԋX3kɠhqCNf~ǗZB ՟UXp<ɴ):M% 먝mۡdXupu[/~Gh}@Pg fplsZ0L"W_#?=t 8Ho]dèFJn!mp25r, Oƚ\c` (%3YƔ+3[dÿ+l$Ogر4/U珽75mM?|^}.diʫc=' p:Ίe[1ws{[;Q*}x]<(.Ĕ+bi 2RJ6CjRA߃Xd[r'_D]MPnWX[( <0z?iWu!Ɉ.nn_燫˿ޜ׈=Á\hE1"Ny7%`P]tI tV ruԓc@QxY.MPWJB}!A X$ ԛa%"vto'l+ȸXo^Ih^.[A5\LXwgCsQ.c SSS ڼcØ{!Io!ۢ][k BkٌΞ@%Lm( ȩmоw{,ER;z3)&{s]žmtΉ}A]} x΄Ml93%P3Túq=Ocmc_;Jz|ldṟko&:z?p WSݿ{ro]qUu'4 e z2ݝ_lO 1.09nBbȶDϏ3zh{Xe2{"n(,` Q2"EF֢v';sJOğyТs.GG.r$|N ENvlK/Q=&BJ8ITC*H6SHFe}v5)T h Y LPKolXO H<celery/loaders/__init__.pymAj0E:Ő.CR=Edhd\SzJQRH7ތ=q m90nZȃؒzBCK2Cqr5aܣǑ 'uX@<}Q3yjM l"N!:gHJPh˴a2* 'ElH[+Eb#-2r̼ ղ^d+uّLqFsoBSV;[@`:+!\jYcJ0rII!<}F#P+"FIG!Os,[gyL.PKolXO5celery/loaders/app.pymjC1 Fw?r3@Ŗ{ m,~\.$8N_bi\H<7ta \-7PYc9Ʉ6kol-⭖ԕ:sW٦(Y㕄yXK)}onk]3ƸD"xퟶ;] p5@*"{PKolXO,bAr "celery/loaders/base.pyYmoܸ_?HJd%-vZcĹ!s%b(7{{g" %gg^u@_gyYoW^s,`$-15ߐ$YM[$!uCBmX#Җ9peI7M.?}|zмkxף),xu U$'g¾#k7 %{Z]5/DPqI?_޿?7RRxK6hW+&G'n|#I7!%Τqaϝ3œBRؓj kZf]kQtQѴY|QJִU5+^ wZ 8ɹ+lÚ얗 %y7pl֌hM0u |o$F ڸ1\aågHh=6/L9TdxH`AA_{V9x+3(,߭r=AƙZb/nL@b0XD^z%4Фj ~'Rg|yM:\b/̹A.( (E@vmi9SX^! 4BK!fqww7 s[I ,RK8Z Yt3NnC58DuO1 NGI΋-iƂb0D ͘fYɆXăOȍHW.n{,Sʕ R\̜wAt̥2dA|_ ma؋!8j&*H?*UHa?X=7rY!7kкy@[~X*ߊ]DJ& wV~@z5[NJhKXf\:vr8]ʔAx;9e a:_,_.,'آ]'Iiz ݨ'| D("FХWg1umcvM(ڠ6fp70H1|)G2y^`)B{n8yx&SJʽuWh!qh >8Af ݛ^?k@+ N!IY}y:!.~$d 6oցW;*3|`#_>kY>]rpbPBߒ3 ^,m: ɂR9ElQUn"/KEks)Ԁoln8grtLDGƁwWw._\ݸ(r)G3u) ah(mDs EBAR@R,|??0W 3_]P ]I _1DtHАH:6n4ƫ`n-Д_h} el p6evho5}͸&[7p%7qÓJ§IWeD(κii(t'#+;(mxɍՏ37\,B>a 1ZqMvj(ǡ}έ'|z~?.9>|<lSx TPpyk#B_l7QVT' -f|?- ruuucmJT[m)zFAZyK^[Dx4ڊ"IPg:d I!$}XI{2YHxQe/0/| 6մYnfN9:O~aNjjA&1F^>LxWy/ ZGJe8pa|J f'n `ȇjΣ fxwVkYLQǂ ;:E\q6]V}KNCiӓ*\3! {`wߍ->]dקSbP%` alRcKB9Ҥj@돨N,~0>,Z+YCʏȈ>psi^pjXdPKolXO$ѸBcelery/loaders/default.pyTMO0W!ɪ伊--* uIkڕ$HVC83y%\LB-R(]q˯OK ^JR sx_!++5,9ҲP . C+ 2b[-K~@q@&Cå R;7Hҫ 4CfGk';v] Vm鄴IĬqΈ9q:bT5Ygk-Bɜ[ܽQY0ƥ$Z؁Mf,*Ie~}]u33[{{eCc_ƈJ*:j)JNY8/hVڂU6yxASIr( u6>\|f@\-Oe0s!L߀0~CNۘM%63t֪F]*c\4vp G̡B!mmNzB!?o֟?{c?q~3ۨ&[Te vJB>=(m3 Բn [=\oa^>=WJvBK7tyda.-lϷ2V2g pcFֹDj1`0N]h,Pc!JÑb'<"u 05f0Q*E0\hݛCy۽dM& ; (2ZPUsxlB3It3$O,gt OMP8ZR6)1Oc~!2-EaɄ.ywٔ1jPhlAa%]AknR{&o) r,4T/QU$Ci\9ť_XT~$zlr1jT5!i׵?Bq~D>L *8bMF-'|sIwH7+f6pu^"-B=ܷz啺eW[ȧG +B9k>9e0=^Fs̃\q !x\‡}12qɺ1r o$\ ?5ȨrAf0ѓqL6$ƟPELO|ϔ .?HS+#$>TV7D MI)/z)PKolXO/A?k celery/security/certificate.pyV[o6~IåfI6y+ AIG6kFR }ԍdz0$\sw-|uI*4:...>~$ H-2p *UAH`&j\)hU]>Kb{e~Ќ lmz]|zk8i<^}#0Nc~0@XѨ1OmE-[h_6'}Fє+ZHHmN8{ps3jW@:pqRV_ÈGYpvYhB&q@PM$gɌw@ )b ~l :Q҉QB '?T޼VY% J$Y%D;AB,u> ixJ^#xz"a!ZԁwXIҡ[y_-/% t4;7rOv>. ^}뙤89;8&7ٻ%Z ř@͜,>|D&g-7X-W8)V22 Vv8uBd& rՏR̠t+tu+ GwFh1nˍ 6sײD}rWv.> ݒ&AO,v]˥5 'zpknXޕ)igh~Z;_ }o>n?cul^joS߉{UXy>{;8.4F i:2<1`s r^^ѵ\eY2f)M]ψJ^MOjK+F4ygY)=WvNٶ?PKolXOlcelery/security/key.pyM0ԂeArIjؑ=*Ѧ[!r<3y662uXmކhZ=8ugu <S)8NڨraBTv`iC&sou uFQ0Z!8}4: ׷hk'}sY~JvtwcTPd#o@V>&xJ{f7 F,pT 9H"Ԛe,^O?AN( -g?d1ZD <Lܲ+QgQ(DAWiRA?ZWftaX rOZ4GY.mٯ/uFGBEB6\jVFL12cR$T弹L=8I(j𸨇xH< Ca JN`/oF!`R(i"nwlݾO _%լԸWw?ۯ'7E/BI:dxBPKolXO7 celery/security/serialization.pyXmo6_%$ipMb0 !3Zl&(SJcD=^)9$"jJ*ŕɜ%dD1ic2I&ҰDŽ?BjBJf}IUpk&i&(U褚QVO I.h ?$[qօU*b[TE !aBbf1$;fmD2JYF\FӇqo}|Omg94`y}24:_ MV%LjjֈBqs-$lH%~g7,fwtS:JHO?Ib1RH=5#g#;oijf2^{d2IrbL'L*X:L Tq+g!f,F8Bv`79}=(Qx5?|(Fw: cǎeUۗeۅ`{֮^KE8k y^%XZ+ DX3"[xf36/߻+2goMɷw.kJ0p'fLߚJ)4nK\;)8%& Tv]japmt5I0ئdM719N5#yf!+j"J4R`J>h0'4TvY׎uk}I:#J<)6Z6k1µZ[Y1PcFX<+.̰L=TTNqU7%4<*LtY=aZǽxC`yx݁/Ed (7YZUuޚ˄&Î{I7 ٭qXlU2mt:\+gz~.<7;2T(~ =Q0hѣ %RwXcXOHwu^ j p(#gQeHIY3!1.O[`sؗS<5Y'HZklۜBΊ^; RQ>#|s>5*43ϴ߲Z?[l{G^;נ֥̙ )=sѰƊ?1v:7͠: 9]D9j{u|ki']L!NW"#߶b1$w}[@o0w/f}|l@"! q0K/]oL@3)^@匔Y} f4W8ne`0ؓ8K {[ ,_E-e5LJ6nLBϻ( q<`0.,L(b_/)Qez=ȡ$2/Dk|^Y?PKolXO ?@zcelery/task/__init__.pyTk6~_1$з㎶4} AXY2HMz3J]5H=laI...2  p`/ysۀn2V:)<6Lzl>mhUچBk*`;Q;JEy48"jge-{|\|bNlosM 'CIRt)"_ LVS bY"@)j,'))NqB+wP8)j>Y:}ݷ#K\Cef@_Sj]k^Xֱ"Xt+Ϋ SW677v'd:QNR*v{C;vKgyp]mp߾ !W7߅;SqJyQ!tAJ K; VOLR''tXZA)'fn4˦331y/UCr?aS͚6^+03+b! dAVb,B}N?NheCVM:|䩄͏1jVp, д4XHTwƵ:o˪>ƴ]Ξ>'rl"LMEI dOTr*$yG "2a T͎f1|]p2$/ϸs77re $8Y;*"G[٠X-@~LDk zNM{$|a2jS!P*oYz ZڦcYa7ڗPba^9`(cV~䩚&H.;J_hUYSns=|EΞ}1/vf񥼯ZgϹq;%$\uwuBOo쐌޵Ũ*X*8m՟tG^ O|*[t8s7Q@CGG5MgPd+׽95х-裉2TᕵײN%EtlP7F:euW Epbdm7BP~%r{`L1ƳqO} h)g} e Lg?u c. F4]M:;_,uH Q^+2'*x%ARZ4. +gj XF 7VA%8*o +xԦ̝ X8q{s:]5 nb1秷q*6z^̭(7.JVOZuSbPN w^,6^% w^&Yö(Wu4&9Ji'tQ;hY˓IHf,1s߱p8xs>Gj(Y8W\CGD{DBm#ۃQtjgNWۙqNRutƧXW ("FOh+-c@ƗcH56e#%鯾 oY /&#M\sD;>n2ibm30ąɄ2xpH,:GW!_p%m3?UA=dGF7Ӡ^NCi'BS-R C'#l\xqT܍a)<ۖum >kI@n# Mz)?ѹ`Kqb_eN;[OyM2l8|J}e΃c/7 <-L"+|sNjYԐS҂.߂jmp铷rSF}I/6ÉVUީ4:{btϔenBu shI~Jasס:N(ROMxɽ-p&"Iʍ?qHa5f:~Fw8'=!C4+uzs$}PKolXOjE celery/utils/abstract.pyU;o0+`p4tia @C&E۝Ś"U=RڱBj.fLj_zeᩃTrk&rK`,]m1eZY+ϡVHIpiWiY~B ++4)J4MSl+x0i:Mv.|%1_)V>ci`ɖ+m rT-غF Fct]B<߸L0_Zqɔ ns&^Rr=m4Hp^ BMX&RG&rm떤+f],02|իKg1?ja0cޜXQm@ed_ lm="DaUZoXmT9TܠYvvb6P-wd*'TS ĭvK23A\ KF]f's2Cƈ ᾿|NH'TI"TMLcg-hmMw,/|8]xo+%_v PJ ya%~M2ΫJ6F8 CGlQsqf֟v_uώ v ٟ֖T>>2MVk k+A*B_ >/)Ԧ3+KiS 2&<*^b%o$ϻ$$v>:>ulk b;]$K~B;3}l$>?aJfvt,t>KB"tX /PKolXOw7kcelery/utils/collections.py=ksF+f+9J⳽w䲥U 1c+rEbfzzz==ȗUl/F\]R\IZHOoEYए~up_^'"kDZKQ13\Ȳd  x aomc;4鹜DQ9zIJ2Y1Sab9Ba7||*GX$|w]+y]7J?^f"up}yoT\ h5nDD/X%RxY3ZXȢT!&Do'C V})źrي5tBr2ToMЈ\7JE&0ҏf}dj=b=%{f%%N+IT.q~CdjDO8 } av8r"`CVp7` aլДvNW fܔ퇟F,xC|iU+`JVKQe1UUXtl V/I&ClTZWyQp=*eJ-NVM{p8E.BHzc\hd{ ~D#X4Y^?wE8YEjAE=jn$==>;}zf8 w"mjeAǐU"l4{+sȉv]N\GZ6Yn_Q+6r|.8gU[qnE[ @;_H Qb.C*>Dbq<3psAxP!A 4o\FM@Jf[D# C[h܃[q ;qꑘwAS -R32N88$ ӣ>(8Шѐa`0R ^ y`8R(A. z".׹⧤KC x `u#66w=wH!2"H)M,㉘QE.ψigcɹlLhQrHM=pzS?&z1O~['81cHt}F{`XX\C(rMB<8@KK*: l")V}! OIWURs,h:`h2=&{^{K Ø5/` l6.ŗ3)-ǴϙϥbayfKh%Aˈa0P;rDg+` Ly/w>xnvN.sr(EV`P(d$}<\Y!CAaP ҡCJR5 W-*I nS3oK$tǺLA 37*\NA!_9TdB?Zޝe_?0m"wY|xw B T\rЃΜ3ރJL6J)YVMŞtsgt J؉s!m2*>ÄAk DwZ%'3q;u8݁,{XO믏oG#ux@Ŋ.7k lSs9&q)=C(駱7l:˫>oBwhRAӨ& w3kI:4{HbhP+"Q Ȥߨŭ'N?!0JtOE,D 7]Q_{!s.5:). p6htŊz:q˸h@-MIdFW.k7Bi۽ΎNaS'T2摛 xHa‘Ks>C8Y}W;?K'wD&R:ԧ y3Ye z`;,E+6G;fQȴ] /ux? ĂIRr۝maaiq) Z)mXԳS(Ғ#ӬX)xC#T(9ؒ$eU!5hȃx( =~e5Z ]؞F_E1O61q;ZFM[w@K ^ND7pU*/{pÞZԾ'+^TOIE31j{B㟉%/֎AXOu} `Jul?4M*"o ]]]{'6+1Lh_[Ih鼯+tuBLW]/m¹v?%'7/:jKiJ2i#Ve̱.4{/OJ\T"L H?5?`)xd0ыg"?'t5RFL=${Љ8h0:tU!惈 *̒R LG XTfОQWI)Z^:V(FZ>{!>AgΗ+E% -V**2YMAT'-HQ1FL+9g->)%=MlUO\pk]f U4Z q#z36R ?x(\CA' {96jrk&\}C?b %ʳЇ~6OPy"S6`WZ3n2&#jS?1:_Gd4_ rc)՛ym+kYc*M:]M=4eF %<_J/7mZ wۜP@zhMU xTgpRFCHMI/:7%{9ܫ_c4W*lQGKΧawpMO&b4h]s]FW-՛KS%dc 0俆٭,YFL8= Z&X= iJ4s{^|:b',CqzHv0l1eRj͐ .O.kZljE\J6ƤW4K}KoR7GO9)ʃ,Q@I*]'ŰނaoW)h1#2[Xngos7x&̧s]4sڦL{/?vSX?rK7pMD: 0|8Sxv_Ucm֪iejnE,eigĒH| U 9*Wf|gFCdILjrM6_5IBb̕ 5 iYOPa_ W] ?*-0Mg +^;b=wf\ވ;L̷zۇWP(7 Z`/2*VU-U *whV+ < $u˨cDa;|iKDˢJ0~iϑVwc(D).iacq4<9G~/To3vlW倎?1_1)dw RCZsXIYl ʑ6o Ɂ$o$ +:/a]Z.3E(Sp|r8ͮf%Ғ/Sm '"ZN?\ZAy+V!Kώc~.~McrhNiL?pcb410LhC[K(xvti['.1cht|42pGgjz@| Q*(Ѷ!iwAO|.%o1'f%.& 2PvrrD*Ir. N]#:vlfuF15(E[o=Gi 8㎑sS@n4hmnu"ƱnE:{>H?BxXm RPry J-X=yGuۋHwS<ʼn6rc p5nGʜ\PKs%0n%WmUAc E= ƥ E*K7ͬ97SDU.$NHdIyO^QJx{dK@'4 xkGg6";T]*_0uTY̋&7%y'tZPے辈\ca_SRBDWd9g!%n H֖ܺcQp8tP:H! ٤N.0vWfEvÆ兄Eg;82-)G\~ٌ$R''$ |DrS#l (,pp{ck:D"i+w) 7̶#OBA/ k\0X(w1I֣{=9.Jw_Bû}zV;IJo.Nܢ4t]]zqbFn>K|I0-'t ltC7iDE "wȔ6t;x.l\^[M9JW/9Jp&쩛]wƞy1 fTcrEqE9ﱗ TiG֗Q8M!|׍\7b(LRʇMb _5W(cri}|j;׫hbD_F&'|#' ykq91!_q~j@_Na">Uy,Tnl8GG:3ǀ 뷠y['nwk^ܸ=86LL{ dvRZc\>iy%9|OEeJu911pow ~kE$tXoL~8Lt?OF!w6 e-:[*CƘ _F4YQbܪH{hd-6 T:cŃ ?A K1ޅ<a땚z&Y}< ] ] yMʏ:`N<2s]vGn'+) Y50pݑqL~>]2ݯ'~G{ؒCwCOO+^'E:@[>߯<%m}Lϒ#Ni0Wی S`8$΃>wwIz.u;G:|=Hkh)Ե6֕,_ߛ4s9f!ּ=VmZ`>wpU!ЧwSc moYnu ?&WJ/tΤtfEa%|H8db rTPNn/Lc `ۯ`9p*ZXo/0okbw+0 ƽyp*)Bfz/yt¼p(e!b0f>c곩FWDDW3rV3yV&j= R^i$ǡiY{ /5֬#m:u`{ƝWg Pv;Ʉ^}sFVd5 $"N\>3W$%"JcP ߉q Q>$t'6b$k/\B~5&PpIQRR;Fg~6rJ\w6$qz=!3۲D'*tNOv3M4B|؏O^Mo&HX'ZL7[j׬}z'{K\n1RBݎۣ4 G A(\05D{Y_>Z:ba9c.AwK˞۳4>3q8Α yA``Ԙ6᷋^]D7Y3˰*nF (8B\}dnz^.Sء7)aIq+kqte5fҒ-HGT; kNk ~ͬ ]jms[8öj74R2gkKM׭gQgB59vN޳,~y@ɬj?b]&'Na4A8Ԯ@hcF"jk oG$rMgXLf?(wrX.wW|b̾32wTutVywlQwZw*=="'[wx}Wߜ=>%ݜ>LtGZN.ΡhՇN">0 H<DfX_1˟A77]?E.(zKS Ï @߆^­NDt6abp/Ⱦ\_aٚ MI~kT67 T54uPO.kCk }& qXkTTJAf;|3%;>)@1w:pyƲV"80bNfs;lW?KkG 5mE鳮k.!qUw .L:w4T9oP : &POwlvKktXPynѰUs#g#2ec0>o+;4uh8Ti=^Dvi-< kcB GsCLMY~B!p̗4l|VptB({ɱH5(OvO" q42 ~6\>OI7ȬWanș쇲[>`D3A G=UpDsRB1MD 2#MFGÞNI۩/yoV2*}jJO̿=kĕ?Ԩ&Ȇ2Y7n9#t9hhcK%R35Jf:Ix4 ׁIu\o2f2>+zQb9䮬*;U-r2=|ZuUe ۦoCjZ,..(6A%H\2d[3$5|Đm'cw`'^#*%%E5e}t%di *N~ǂ#-=l~t9̼<@Ass,qD9cz!Jͣv'"8?nƸBZ/ZNfḯ f:̂4LÐ% _4,`#QqI]'vךW+AI0}]&cPKolXO(celery/utils/encoding.pyMO0 Vw(y ѴRE$=I $$Cb[~z+/ֿ"0trFXyɎkxX99Y<0W@49@`+zr"r"#t6Q.M0&XcLAY62yI d͂j& 6JknUGjoGPKolXO*/P)celery/utils/functional.pyZmo~ba}8ҡXI3j8Nqio'14t6gfHI6T-r93;o;oqTl꼨.oieѣjiעԅR|mz'lUY&]SZuWVyOD*BVlf1kFnߙ=mV4_JئbfޭҭaDBnT"bRDptST˃snT}-n.{_= ?V|$Jԧ S`­WxPg٦]'wNGwx((g§6ˢN[Kr@VGwx6YQm^Ml'cRXSͻ+%ÅE] Hu_u@>/-qA+&u,NBbN/V^B!j%d'JU C)p#(^5FRAuxb+ZSOp1:͓Fk6ONjC!$1[0qJG(zL. ?65L^Nj/`Oj[yX\5!-9ΓM68ĺ" mio8B@,Ou"IğD5&ov{hЁ9H9D~)M7]̦ ~Kq=RAs$ $wN.Wf7ITewd5+J„DDen"{zԁIhBStqP5*~*_S?Hz~ۋH}6bm<jRV8ua??jH|> 3nc 2;˹B<-_JU ` 4v<"9b~qY-E(w3k%,+SmJ.l(8G@U M9=T:^b5N1RֹgR%>N'jZHE|z2)UhoautGmQkC*Ӻ¨`TʅEŔ&mI ) 6a\CQE$E@#kM;嬺Z!T'ƄiKjCՠV1&\>u (SU39*(՘M.<-#0n8(vFf{`ćzG׫oR=K'~4:a~ +ɀ^*Nj0 4}dA5i~})aN|S'PPHͥrn /d4_'\jojne<@!e~fEB,WD{6m)/ңW7щ14lCdDK}]p 3NG8?0Tt}Co"*a #N {Cs.()٢;F7OMóyOB]'(Ho!cyP|1 3:4Q'hCĜz|!P`DcAG~DS=\:-&_nftS_"u_KOs/˰ 0+8;q?iSN 'CZLWPGMzK岹})[p@3bjnCm]tBl{}iԆK8m p2xנ^FZ {D6 q);K<_-KLu/UVKEN$"T;sfg) (Rex=\guًKGżnfEmnp3`@qbqL{O:|$68VRzzΓAhr\ToY?'O݂V^zۇK!AMξK;5ĈͮApNaW}O3=S~'k4F8l.C{b`E-ھRDQ%G{6]фmb1ͺoY@CA'b!2~FGz]dX)jyHЮZ *n$grWGp d.E_i _FpP`/ŒTåߜ3g>[4_֟g3dBs+|g ,D0nm:^OBCTu ܆G6y^Na]gynR$i_/o`,Da|^?pЉvoLrn:GoLɎƲSCSZqLV"~[}rSO&q9WW"@$r4A'Mp9K}TrHqQyi3 B`Q̔uk4y-5~ڲ .8[gmQV%%*k zzEZbAE#re; ,2m@S@HKmY;8M%TQǡr ͯʂOMRu2oB!Jg00ɒsBRa[XX&(R6Ui u'Y'uj4W:Zbpd{$W:ir'g+n$MQCd?<=< s93AXO黳YR!vQ2-dޡ*&5,޷TضoH&i2}@UYbnB?q Rk^̞ţx&YY^ێ_`V)nH`$tCQB+, Z~w"N}bđd[NbuR p%B < 2ʱf*ewgr8"5RlVs(Np Dj0M-,U ׳\Qm؞>h!yNƖi2;qh8k-$}|_{[Dj;B tF\a0\ M? N'@|1N3Ji=&DΘAwr=-eya,%05G{z~E>#3/,@7nh $bD[y8+aPxbS:ig 嚅SviÆ{!=;۶gH5 c?Cst#7 tl] /H~h (eu {e?Cx: s5@Ou䓋e 'FUeSUt@fy9Dԯ(Px1kdDx# 8=VF#WǖZܡ$ 5& rAit\0s&Ak*y9Øѽh \RvxG80G8C ;"+p5^+ruf0tP-Z u Ex4:7= s}9ּ:?E0\ c0᢯«ḠCj6S>?ߨy;އ}8FU MƀbCcwGi*t9i]$鍅$#H-Ѫ.зFW,;H¿v͈'LV9=\uҌKeYC[s#˃| OgPƅc3#} j @,>ѽv:\\kP&jL*D ДowuRq(X_i=D.LmN4r%>ޅ W?A!J+Xz}^ǴIaoY#mԏ'xSբ-r`) ޹BdJʷ*qW?~~knQ3}87 ŗejc`@.K{WQda8q k5?yPb/ A p=F࣡|ꄃ8CVe+$ZS~8w\YEik[rTt_n[5hZw0ZOiȦ&wo+nG!eq]meߌfO~tnt;vьzxcl )Lj+0o!C&)K[Ut:Yy,Ytv뫶-Xz]do~(\]_~skXQ ThUt{6ϲuM]A=ﵻATfw~:2ig_=lK88e׋q/9%QFdfGUQU׏Nj5_zT? i}.Ǻl(s,(u_T;.%N×\r&X3)y[A \7{kEHIcƣ)H%[r#p0;_zr6GxGwGE6}1 wC1m&I yjլyˤE{-U}}G1&~amqD8J߉_z5|sQ.979o-0{K?omByG4b;ux3|^y) [?*14>Y)I (œ$V7'ZNpWJ$^!l@=Y!i8/bʏ n.0&Jۦ?X LMOVȹzp\ zG韁6d h:0#LlgB#yؑuS"OvVV'9$xNPm8c!"pEgߟhg2\Pm-A*ƸqCyS'}.vaL}`Qy` W!oBD`z"ȝś,;^ʧ ,op|) u; ?b}394C{a%v|;oXl݄ tqy_#\u:b7y8rs80X&4%uq.;9 %]|YS5oi JbxSEH T#O49ok Y?[xb&J{/ Zݙ LbgeK8v?:Bݲ39}b$?aǶNϖ1x4 }=ξ}r&lS2- <)?Um=LLW97O"ʉG1HDSﱂ_QcJ(ߖ> Oç1-g3+}:Lc`[5f-fPKolXO> celery/utils/iso8601.pyVmo6_qPX^d;n3$lɐiزD\$Q^~d9"sϽN3 AYx?>} q(l˲0I )}F|W+bh8\3.*S=G`#dca4! e# VA)Y2bӇ"dtD?Eջ0)]X˄a\t*Ū,2Bݣ$b[ő,c=t=KA#˧DcoT vL,x]1f"1(I0:=f,څ4@9-R&т"mf&R[jrZtke[ITǰhrcyT4T6, N:ڣAbyLը/%SȂE D()c@uRVGP:@&(&j_u$1SRP~RT q@MNm0 UEe]#q,GcVs;BQ$Q7YTJb]sߌKZQP۶*˄^WSBb?煎yx5+Epc6[;gbOc-1 · [q'&羽Xg6:6w<8%0ufN*` #3_:wNpk… jg|=|_soac º{c{f8.~X\Yөe]#{0淾sy7(P-O-gfĚYf5+[0\U>Mx~ppq ,TA.|U9 *bMAnLlkX |ܾt:DrS⚢%TugL²:Pf,1]&L"L!eA+f5͙Tj#dAoꭻWvĥ XqQ}ӄJ:Y> q0}0'=(SU=83SHImN8)W 5:lW; SHq*e}o@eWcLIU)~ځ~ɏ#h;Q*NKmt,\'Tlu& /sm\ t?c|Jut2J-!qt &ھ5T9?Z|~o[՚0Z/lX& -ִȨ eqCxjcϯ؞Ԁ$P4-/$]}A#ԗYI*v^_xl骋ͭn6PKO] "celery/utils/log.py]s۸?l|mZM}4%q>N.dhJ$@ۚLۻ s)b].98:/\f}W\**`u5mU)RG,{R*^w?V,VrnYU]`bij+6RE v[m.{?]VR |2\>&EiãIu"+CZ8VjyQXLU-k%(JӬ,C"STBLFr] F/'0 @ |^\x/Wg׀eAYP(:h6n o- jOYRT:\4JcxO1g4h!TO"UËi ֈrLWXFHU=o "" $)%,LhgOlĒ֍:ױJKY8OiA4ІUбVr"aL{"=7u%պ(@[ߨ< N9DH싞⺧Xw$ T~. lYt;@֑(0O<-t)V1♁"PV7xɻA˼Tf)Ţ(A " qVvA,2<<"h824GIJokH$xj;ӧV-}9B,EUVmd xӞ+DbsфR*~%PA P] BKqVرC@I_~ #Z"N5Ll'0 E-֗$P# .jquxhHk"zE>U mʘ1I]V!?Lx^TXk0Vf!p^!/ut^^wvʃCNpiU _ZѦT!rF𪛧 2 a`> ]E=蕎 .]] he[(+id]$]`Qkz 40 epF;2)"@mj%]) {xmOLm{UB5a35JGHFuMqs6n{R`Obo'<2yb/'sigϠϮ\A,hquϟ]z ;W֐¤ieΜc F89U0%O4oCFdۢy5a]3ܗP"vkY󥪳1%R2Adv |d&fbF(HK\|9??uU0^Hj3  "si={1[ οggjB!Z'/}r,ϛx6: 73iwx9lEuJm~Y[N|M\<׶QINoë́ҭKM(&z\`֣1帡["FpǺtݤ!{ӣw!5xgxM32?[SSfjkl;nb (@О,!CnKr2|bv+=d-sH.WYnX^gA5Z`1wH̤bC7XxS8$PyTrDW7mBݝ>z~F9F'}IFE||xG|p~,,U+G<dihM #/'bnCF[{.>2Zal-% *_YjyjBkBMy>]}nBC3+䠏Rn樻 }J8KMd!3|P@bs}IF7BKܺSU 0Nq0?*d_ML9%P2_dxM m,&(RۈVB 6;s_۽O )޻D~:M {ȥ +FVcuc'vS-R$zoH:Z2[m-@Mm|+"a *n֮P#s\ JnHF/7Ow!wjVjp<0vH|cr"($:(]kCZ c_!/A@}w)}s\Ip,=_4AjB˿} ;gM=7GciQGڦ iH#7ML?(ö|Fv3Ƿ7Z@qfg.D,6 E`TR#y{dmAhr_PKolXOq celery/utils/nodenames.py}Vn6}WYEMT0 Qڠݤ$HF&$qm̜3C~@'' u{I> L%dAauL3JHZ)׽#X yR5@=gL1,t*IPMjBt*.Ԍt~\3,U='|F =Eb..BtÌVIaAIZkjn& ,,oElw@?=V|xm.6SH;r X/xq쪺xnTӎWQVø`vVKRa3G`91&],A5N ͧٽ;{Ag $Ж̷۫'Ҙ gn%mEG=jQFS<)zP*#-`!E~bU<1Ҳ@ `z ۾^Ac-̀JFPFYY윮rYtݴPBJX7.ƕ&iº+6l)iʐ7ZT@ՙS@cF5͢(qq8:iC  ʜkzl[1o?G26,xEpa! KI[CtpB)X"jʸQ=ꃱ_obQY !ٌ&Q"+2mË@ L(%뀣N ZD} dތ aJRPP:DRGA:$@gyuGyfs4!{^ŗӯÜ1vqևɌ5d&3MLZ3UxXd?v%A5*q9q'V.D:[YoBc]xCWY58Pnv폼x"sdJ!J:)dX9m=5 @. O$Ej#QZN$5ե"aaReYeVיX,$U (ZrM_O[]:xQ8X<%ѕ0{'bZF"#]m_;Dt59M'0U\(u=B>^Ee)]DVbM77sjmc,$Anh-6{e takNWwt)ps}y-k4tr{A5WD͈4ҿֳxNO͢cvqCTw`'Kݨ_Z{yf 0^U(Sڊ+,.R|`+5jOJq9Œ5u/Xj{q!sL,݁2g]q}AR\MjkQfpMN+tW]i A!<\k*L}3(X^SZ i+.' LN a=b#&m$&a(ǯ{ïR>'ܩУśa8NwF>J<^B_ snvfL٣ѽt{O8f8]+?vIvkN+Ns}:i kt=L+h\!"bр8)\!jCdF z (^axLn ~헀3G0TGNr]id;oZzy l9ƴM{q$P!w~lN/ h+ C^+K/`3Ixjo#yJh Ú<7W'=C|!: [)W0  æ}|lvꄟg_ӝmk塝PKolXOCD N$celery/utils/saferepr.pyZn:Spڴ¨ Y8ݢ8IX"[tZ\J!kI]_ؒ8gb׉Xʮ4?2y LQfugB.B)n$~L+1]hzYmqFoj% -"߈B^i\t覣DIE\HA0R|ܙk~)nT\^ޟKqq/||'S)) I܈tRw7g:/fkv*/6!Yj9,$Jq̝ ؋1z[)2#{r ϲji~+nC2:V[k;fB2͈EҔ"6ۼ0"^<-} J(UFqG#Gix)rmY-4KKW"[72162Kӊꭽc汼UfDzrV|k[T#Wl2|ˀV@OE]AN#LdܔDU(NShk&|ODŽm£_hƣT| hS /i)Bi6mFPMX0z VES|gb!kYGTΣo-[05tJpPm2 Zdx ..}a폘%șw+p}#RXJQٲzd0 idr`>`gg.χ@ׄ$U,41P{lBGea%$oỿW9[s<J*!g1}_0DBа`=1!3_UR zJ}l7@-TrH]9Wqp !H>zjX>/PHc n䠫] {؜'g;S!y_{pw6L8?ı'`'#LO8-iOFgF\*py 6]*|O׳dv|<QP8fL&!ud}=c1yMtT5DȨ`U1_ur%WxUI+Q5MmoQoqdd+igp5tVS!*5(2y\e~T[_9-OW&8S7vKec͹fA MߺofPCbKiJ񨲃B[f =‚,}:jqi"(qiw{KWg&Ž˫~Gߩ{xX3trǞ~R-8zEAH%x US3\(ޝ:Ns$r"" ̣:5DHqlpifV'hXz*4U[U׾+8.}3T4Kd>=mwL%=s(X ۝R"h8_K! ڷT`J&,,l o&/v7JzS<4^cX1#QhwoqԠ%``ٖ&Q EZðxN]QeYPN{w?95X* V `Kqt p>mZYr8t *SUƅU59dEu5&sMlz8< 4gU׸ݴӄ!zJ4Ĭ(8j6x4_vŸzd+LG,x.MAF#z?.>ζffǟo(BJ!P|`{:42?>CQU*;Ș7u7YڴwA[;zòu ]]L{0VÐ;<5tޛw2Auf!_,bYmC-$}Ene1V 郞_}jug^ϚbΕUVf]Qt`:"\͏?eLVؽFTK[-#gg,5U$(D> ʂD탼{[XkFџl05,Z&,RG{`e Y灍lqۯ<'e@ݞs PKolXOp, #celery/utils/serialization.pyYm8_k?z\|۷/~|u_>:]Aӌr^NH74E0m_NA`CVno{!Vl%JD o-q!B|Fcq>eWZk V54Jy*oUr/#xJX%ygf>&_Ѯ7ę X VT<.Gy:!Oesx@.%:fB{CwAfa߿IuOs+f=R^I$!ZBl!*y2z,*SNLߕ= 0VHl@Nڹ)W_RsXmʖ[VkVf7'S ^XS>U7 ΁̢@1ka c\Us@~1si{|jk ل4kWyX([\x_Vl EMZS5_ 5i)B:6ࣂ ѶĢ&Binc'93%t#Y'>z䔊RZDΉ[@@;z, ].5fP Ȑ" jZCemqFڐS\U|(8g,-PH؁mI}.Х}nzr UOPׂv֛ry,PPwB'DkA3ul @!g7P8֬UWQ\m !Z1 }BX ;Eq|1:F/1}Ök;o2<;Xل=!pGSSx mTzQ{u'\!gՈ< @tJ \jpZ)Z.կڂ a#p) k@[ <,yp@,2{x;D V/| 佾O폠GcF?pORi,D%d* $iNyʛUô;-adJpUqDwh=l. rw&#-Ui'5Kpo7፝(fei`eHZ C*PÒ1a +3ŽJp6~#c= lNj|Nm ï̵^t+2Xna:.n~jQ(3]Df ZQ~' Mm4H; v:_kqWs<;Wt ĸ醁4rpqm)b|!K|1@pJiae}@ 61qCPs*(ҎtMʑWE^w>]ŗƌɃK ۭg#6 5byZqL{XΣ$J'9 }\cY$K~lM[j{=#N3n ] AC_w%<8hn.%t}=H"MS0ݚ7N'#/NC T ?xm%P{Ab`oųO1Q]Xn=MZji5+3Ћ+gucTW5 g>J5EӟNNi<*1 ciʦ_oz#jg)\gFе'-sESZ^CsĖb4 52w7sKkw _K`_f 1矗crkrّd"1[Ҏhd~!Џ<$l \GJ,[8f9t65`#hFCx#bH.)^9Dcrg!y_$nd(knjwYpP`xCE"⌝lh>'8J{}ep*(4;OϠ0)݆ʴ/I=ᨓx!S 8'`T&'/(OX&9K9쪂6[[ !)͗8c;[AQnQ؀uݽGnf B@툯Nzf֍ݣ'&8l?Vh"# T ^|tPKolXO[ celery/utils/sysinfo.pyT]0 }ﯰv+Ф+/#u4);4~=N/ʝC:99Vָ֫Rfԁqڇx'6k9/t()}`mv`R{ 3B@JB)Eq1cݐE6|kN ǫ6{jU|OOEZ+9@UZ£p\BrQ=Fdb'O<Ŧ(@VK}x%'p^N;p|^{ZYQ$w\4-v+x\ _.^_(OIqC9+5=ۮ{`XCy8 WF}Bb.-1'1c$6﯊r"puA-gH1$CA%zu㏁y+O kGW(X.}HM (n| ;3Xd[㘔RhSkaѼ˕oo@xfk_ԗb#L6˕&z°gVBRY~ ԝ Y}[1B sv}%4t=7$ɝ-mp^fƪ fHf+^w*Aa9_#PGYE;@`Rvk|7_5)$ʪHh_-2vH9o[^ZTNZy6Aá" iAe:d{r}"SF_@İ ^u~kZdVhVжw`Ή[4pmu3|unX7zP΁.7[ܺw̷xntz$PA#p~j#7>C,Ԃ$hnȝ~\DR{m+mST (puWc80뮇{fIvg.9I靺|{ }jW,j|*u7J q޶$ܹt:kwg/b7wܣP?5]{Xz~O=D{$[Gr輯b4uRA9r$I[L~K>ǒyŐ(cUa &;g=.2l.-~ȷ ;d;b/^p/{t{dGnWwtGG*oOytHtXM 6Aɶxc4P17jePKolXO9celery/utils/text.pyXmo8_1"*B p'(vCm4[,\CP$:E _$V^qncKp^%A^\܏dŋ[a^e5 /L%88j i:ot#Y_֕Ԑݩl4K} hdVIJfm*7QgR󬴣u-uio-yYPJ&7ɜU ݧz4r3~hU,׼*r/KV]F1gw񥔕/-+E/ X3(dUF/*ɿ@55iN7hx)@hTNՙzL*Sڂ00K."AH QRwvy\!.V\xlx/FdZo kXJ"-FQ4~Wͻ׿]o?~6}'FJ]u9HpX4 Ci[ocw59^ΨVlKKu@9u1 ɛ?oA q ._6KIgQn7ؤc'g@#DDcuhk O(bcڋ9鵷frDAȽ  pd4pLܦag0O^#Dv7V1>\۔,Stغ#%9۪d_^<={:$ ھW>뢊']yI7M:H tm!891j'}[ZQ樥Br%{.w/EЃᓖp3ܻb_^\-r=c]] ⋻.-0^.!UH|f J-˲Ue+-۝ҖQ"'+s[rª"*4ALdT~/lXhZ@\,x~Ui+E4'<򒯅1!>NW,}߳o.{_Fٓ,Zb!EKT9QOZ}8Vg'BR(6%f[V,e >bgK'u3ƮI+R\P(%x ~nzϚ:\&Y:g};~n=9_[?= KspFNp'i8aW[{ss8j$+^Ib+Tx0mۻՏϟzr_hUNe4H 1iMD`ֿ(?SBu!k +FD=0ߡii~, q d8㤎yKn 11d4}]YkU"!ǝNeEY,!WVAb¾ڌt2NQ\|hxZ7f̦Ku(=WڪN,?/bH靮D{@5X9yaAY*)0@,ɂDhلm1[mmo\AfRz$*AMlB-S>J:AQRs', {5ϨX):l`5¶TƧӋuѸdSO[ * yP=JbƖV7 ].uÏ?1E,gc|NQb F?^2JVGdEaGHе8bgbm/ ;rho k}.4(>Iy꒣[3;yIee:vTj"pҙg[(q٢*.JUJwg+;ZUzͺ"}mObsE:^$*7*)ǣ pR#SAIW!='n`L@93'﹬R}[QǪ&pFvB"8g[~$%3SRߵ7oL&m 3Ŷa,V,ءx%N^,/Ei,FA/-{h eJx 0l">{; m;r_?9yP? x䫋|u2 Gw|af8:AM Jceu:9uY_Pg!y qMŧ"w}4|ƛ7q=g vF TG%BJtRV_ Za7lDw(7lRl7~uu4*-6@;tlFl:_U̴~hzP  | a_[c:O*;3:^C ƒ mSg&=6>3=qǍI]}!tGaEo*7UIWEXxVa "ؗl!?Pܦ(?aH84ӸRȿ:{h 'p2TKR/;XH-w+Brlo.K@.↨퓿la.Ĝ w9 ؎dJ!cH#]u%ضWBԼ.n-! =r*|ss۽Ga|Cr:ݤ`Cw=RuL}euWw]4~>M 2\TҭlݑJjwތ1I f܃k]Ezإhh4DžF׆fsQ?]Yq%C>=W=$ӞƸqr\4x;u$S{(O|eH][h[wО+Mٔ|;]q\³S5{+ˣ?(897/pMKAe'FxW @U k }Q$h2煤Zz#2CL{|A_PKolXO[Uڛ72celery/utils/time.pyks۸~̍(Ge.TixzInb7LI"UۻxSk& ,}a?c{{,JX'{?b`gg,Be(953Y,QTR4_y ʩD,MXF2~e'El2Y՘uU&  Ȫ[^5}k`zi [⋢"㥨ru%0J#W4Lض*vsQJE5jܗzy 4Xj`rMd5"OWMQykUWwr4u3fŵ筐JX9&X'3I&JϿzQWajV%+.JXTj[D%,)ӿM 5@q+4QMY4`Tp/4o1J}o.Ejm8Vc#m PXFk!ƌ[tK^i+[768 Z/"kDiR9XMkdXM]0:NN7ǿ;~{rö#a-dDDEySr8o0D*2ƌL/z9$/Nҷߜ|H C_0a%_^U XV^LY~08{{~|wvqnU)R UhѼn\F|8E5n7ZUGj#'Hq=0g?@cJ hC0d%o[Z\(!S/3KPC]-"I]7l oh&*q̘ndE *b{)TFJ^blZiԊr>r?c |"e)* AN\ '>|%8TB04>wF7OOO.edGc3zdscϲZHLJEiԼ!fA @a{ SJ/Fjd1P}x}7IOWXA8($. ТF^L&d/!d@nRVZ &䭌`/;=jtPV|)>~Vx`& jWK^s !ܢ0XfLKDgʕ8_2΀p%}FQR =mgrXEgm hbzΆ9Dp0 f7tTil DvRXtKPr> >G X랎G`lnBR㭱}R:+&n(EȠK*#ҨHe [ąlHrz7l PzU3h4f2=YSS+cX~G$=F0K1hԨoPRrXdT,{֍Z$Zh(_Ąi}^T"߫fM؏ !d۪[} 2ًu@v7&Ǭn>@I ]!BD1 q (5~ koZ){2yKp9kW"$6mڋ*Gj.ʲf{еd]ӀS ZϟmYCPlzd8R1I搜'߆~SJza;mb{tT'DݢEV ݒΚv0sݴ?*g/8̨GO`2q,!`L_#lDlqD]:Z*1Nʺ7WmQgSz,yik>hxҌL}\psdz{"/2d*zikeX 까]S8?D0"eb&T]z3v RLhF jDy{?W[AZt8 ̊O^E&UԄ:Lpo/&Qz)ZsTⱣ6/'^w" UaZkx@J$ ib<*]atOve' iUpJQ:V — UVv:ÀK: OيMۺF5/Nx55Bg\eA5Bw{vwtokjH<=nto10Ȗ6I%QQsM߽Qze&hĀ"/_]V _+/TdQ]9A4>ov]ҧ>8F5/[+E ~loΛYN\(Rd*r 5sN»j2LjSPKZSSc|XB,G!zܱY c[͗[+boJBA% ,o.HCƅ5"ŵ~rQq(T/ɛԹK@ZDaSYIv]= >WR5 hBj|\MZ>H5޹a2cnCū9ͳ*V{JnPT*;ZuqzDDFn :&Iu%pqFIƏnGFO VxTUwDž&1!g@rx`4P{*c5p'uc]8~( ?u|$wwѴ@fʑ3Y,()"unwnQ(oș>,ZOƁ y!`ܿJ<^=r}^nBeMͷR]q-euP=D! ꖧ\6-# A&7h nmr8:ػ} JnmoqL{:VL[Q=a#2@mwl_8 6W5H&Y5fXZ+4At +NSc]!wbSo88`nf3YsD@B%.R}̻L+TCIތLּP|Z6_ʭhj\#EfuWɼ^cQɐ:N;<$ϲUqJw/\zM($dL,}Mt(Ǿ: 1>uM ?j}ԁ[Fh^+uPk ]y5i{rtoPKQbPcelery/utils/timer2.pyW[o6~ׯ  [i@uXۡu1tlsHho:ImD .Ŋd2&ٯ~;y]"{9JA BIDHuB=rM_!w_W;f7:4)fXQRs}omő Ⱥ$]2݈쨤N /AD}7+YJPJxYIeiY_JqahdIj7X$pJWi%s͖d-k] IV0tV y{?̴ljU?;mRT1墮+  ݻiTe:  6@BXAL)p{Rs >H蜻ZC'=$ׄ\bCh/ށj=aO6e`toW '9$bXvV!TѧjyS_PHCD,kzyr P--TݫEԿ 4SK±`XckĄqg1֘8c³QТ苭슫hpxou0\S}M.x7wYrȪ {Į0 ]RQaeR(+ (";Q#ƜA)[UH&+mh}YYdf.WPگE?A- <^]ss=, , >)I b $R8SLL&\L)d904eUU4 G/l}m*@"멃-Ha!!mkڵq\[l1C}Xz0E2|@_Z ò:+IZk{= `FCM3Kr8hJ{fkB=hIJ. v] x OSGL޸;o}?R5pfi58`kɣ;xI((>Qj\CӴ#`3k)_t@5n9)#;=ݕ᮰3g^b:JM\?~ܭB{wQ\tG0}=IMKh#Rpf?s#l:r}V*oηh$8gI7(F(ԫRL*G(PKolXOkx!celery/utils/dispatch/__init__.py5A 0,oT7%Mf30s@wv̯+V 50 })oVܽkyCP*a~)Y1uئL[@/=~@EDZq:PKolXOW4A6celery/utils/dispatch/signal.py[f d"qZPE{,8Hl jD$f)ᐻV }>Xߙgx7mؒ83g6'OXZnbbm{'z3裏U.hx+w9F^ԬM#"ٮ.,Ivm"IXvʺa|#˼mD/Y[dJ$y<)O~m}?݄j]㦍&ekpŠM}JJ4Q5E}TT4__"mb ;Տ/Yujep 9\&܃iV:c;IvL۔m=*)$DbBw |u6exUeUglkZL#?`M%)_xdV6BV"x$Rl"NZVPzBK<ΎG%r\jJVZf+h% 5-f#3PLdؿf4R֬DtO o0$5mM(?w8%_fr[PG^J7#z9\n3DAlz${LW$/˻J)BPŖ>OImyہIFsSBۧȥts-"o_^%/˲ In_]uۚٷ~}crus &@?͞ml>TH?bf`wɘ >!hAx?Q.v&VΔ3#cHn#I)ăAɘ?顬Yo)w$gX$+Y5,}4%KE硾$2AATk8VIk ** 5ఢU>  h%AT!4N!Ex(鄢̄y@+ 2i.8a.X V-!zb` *qڜSGӮ"Y? en33RwJ$[K⫯RuR 5IED.2AF j/˗ԥOZ\7 –G# Û>#FH_Nψ0ˣPʓ"'lᐥ$4 f&M8HTZ[66 ihf# 1SXdw& h6zSzEffVCxrnɍQ AFYgJ4KP**Pƃ!L\U(Tb$ԉ#9!/ TˋApjgNus'5 bʼn쏷y%t,-C£2 {-`Uq(*x` ?v:v+FRzcL!&d3}Y+cadT.Ö*!RC8l D,ET;w lIiDgT5RP(>Gj<99_䲟<%$OZQ "cf&&"g1b횁Q-XۡLImR.kAv Gk,Q`TTҪ EISȘLQE#hF xKP!hcB~7x@yD\[(ltrsl:Ʈ I*7ajrG us(Ѥb"QjJ~fѯ4^Q>o:ktp3ԵѰxRPm-`g}&5 :=VQb¾y@^|;$|Oz 6![N!2V 1x1.LvfNqB2v U^q"z1w./4^GY[ m#GHEx3 /AowKɂг^Nof@ݻvtۣJF]:Y]e9Uq:gBv}({?$/#(UH Tkb#C%$>wo: ~y 8%D(1-F΅:r*o/ڣ8 ckܨZJ> >={2:gNq-puäD{WubNmW9֓O7W:?B2Cb> :UYCC&Gk%N/m_S0NӼy;X!9T'/~{m\ҭan}qjpRΖu&:Y阀P衳.B䅭o='[RVϿެݫ;d5%CD3i/$shqWNMOsawP駩/[\' B <+$xK}W9Gbإqs:5OXxBL$|t1Ɋ]-nݡZYć[bf]/h7BtmqU4 'ݩ$&uR"dD VH,$5,6R͡w;Be~O-jZ0>R\,e}<=]髒;w`ƃ_ꄕuam:TAk2Ty Y|zhE0upkYwF'+b&guz$MvYF=ΣC/À{oXV$RsQ[c?t DhFOZ­~M x_[#h`*hPNV]!+. ܦ#CƒޡIFU~k0͋:7._8/c}0ݨg<1};}O@M^ݡ HUT2ry0WNe>+/X1{}; O:d(h(q"|g̔hl-.Վ6dځ/-r҅mB*+1t]YJ}Z@AAd0Aޥѣjt6wmCUXi5: |}uyZa;on\D,9&V PKolXO5 2h*celery/utils/dispatch/weakref_backports.pyU6a`ŢM60JU)G1E;Ҋ2@mZիWn5(۞dХ6:$d]=H8I40[Ch&8 BmqWt^C >f>Npb0H]E.Sqq pNC tw"VV*-m!q L;f~>_قԆz+CcP8+ń m&KͦF1wj8>y q̃UL<[2~fdK-S|06>"cY\l:Ƥ|wq0\]-kJ!]2]ϽQt?z;^qr1:+.Q dm}қ9WR$ưo*y$eF ڢ+ŭ䜀`$Y.|uKfofzgYO:)ҽU g^d4MQkϹOۻKwP~[8?|C!0PKolXOTecelery/utils/static/__init__.pyen ;_aCIENSX41@dC9l\e~+a"^k " "i,`%E< 6^H< nsɲ/EA=u SjlI\Zu 1UYkOC2Ϥ*cmA35c>ƺB H!o=_9cnPKNmjU "celery/utils/static/celery_128.pngV{4[K"ǧ1.if/i̼\J|')bʽ踥t5VD.!I3*}uZﻟY߳~כc DD$xf⣬({7:SMkxl:/!8;HL~b#@'Dނ'cl5ecgr ɅϡP@ gЉz(a~h2! bÇFuA]'>F (Ί/y12{1 _ 0,kQdOFE"5퀲A[6$p#0D`lƥ1ok<,4`5ttt#l660Q0VRI28<,`v .mMiL{(po0ťnH`\cs}.zy6t S b LƐX< X,b`0xMwstHW-84wsu@.q ljd񖸴rq?. +2Alq-?+Z~ȅˊq/dZ4W|?Ȓi+v'J"|]cyFFcs'`Y.ſ|۱,Lehyf'y7 >d>/>7CWjlOգe!2betG0~e\Kxm(l=%==MQ%{'^ff+D0N|bo^^5:q>"2m*i}Mt2~XpPe4ER9pbGç;զ2euzJ)x&z̀zIwI[s1ٕe*-ծM⅕6^-bGKS2\dF<]^/ i2q暈b ʋhWM?}TIy7w岵,qbS'^a5T; "G4 VQd =.3KDKsy"[v{'i=L[_yV6a!􀋜ϟca5TzqPKolXOD+celery/worker/autoscale.pyXo6~_Ad(,ua/Q˺< +bɺGFζ`TI*1)Q?v 1ɻwIVW,Y.kVG >HYH&.Ap5+eV*a43 J$$S+)t- IJKDAH#Ҭh$M씤 B,i-4;kRg"Mq ZV&bBNRAxdN,vJ]mjSR?[-w%E/r )t4Rߜ3jZs7嶎}AI!kk( .;&fu+*_w/Ѯ@6yB[==9DQC3f.8',YUbWpaᢥŒ-iz- pl]ȹHJLOdKN.(%IIZ3r"J߿}wp׏w+dbBcO9$Ɲѩ,zu>ihFއ>1I7a~n?7ȕB,U #(.;6DXkHP}4B*EZ~P5ik2@)YFN;$.ry-i^>׍- ;a/қGHMR@Ep+?R!&'U1ދ^V>qmUbj<ƞV +rqLlh^d<ΛFtTWlҖQ{aA(76Ⴕ+Tj'H|g ߇"nx]g \6 _EchJAvb)72;[*+ z0_,},~_s_Q8񕒩n& ˈZ?r6q+خHY:$bM3Hpu79̅|h~j=#1="t]?^DŽRB*k~Ȉ 2NOfFGy.%wPKolXOZˉ celery/worker/components.pyYOo:Sp[Uu"CLk/=Y>FT\yv_5s^ru4Wx1ڀ< K?R(ʒf%O9ouUdUybF+-[[P4%3[=]kDR<:|s*pV+JHOIJ:&k+~IY5gQ '\@HY~vssK~rl\[nͷoZZ&jz$Ga3y8dWǪtꂕ$وdH~`V߭0Þ{- /tD &z/۲3NyAŒ3de$[rQ^0ZH0﹆NXӔ,V HEV`LkQ=\|̹_ .|Krzcc?bK cpX[k RKw!2'Њ=*:r,*5GV'7==/5 ߲!,4d'pHJ UMc\Yi7lBrO,JcɌHR1AN8H\RY "AahxvęOWϠQ<$}5YST F&F  ^]J@ooeZdڝ?mBn!7oQN֞A dلj4%e6DVxt 7R18@EeJoJn!uMҳ3Nu^v!?(XW$/ZXMyüMA{.aBsYh|&^HB.I!ʈWǁd7s!ڭfIg=dA{ ژ >$/Ȟ*Q?c9s$G|J"1bp ԴĒ$'0AF. @A9zW֚|jx5fw`#e?^NXL`" E_`%8QlW+(9'g0T?o K/' A5!0=.q8#}:W"u F`Ow@ zOٸ{6C0wYZ5<ݡ v"#B5R䣹@&t1+cLhT=IF=Z0D_,7 :цJzqvlv?;Sj|FshY}Ur9 EOEKyV+N 5utdܭڬSoSsDas4*lO;@ Q7a*w0x);to;fKJ3^cuh5 RM4(m $ -,~G5X\XiƼzIqΚpaaF֭~/Y7Tq(EY.9`=T77"Z(Z" ):%Zotɴޛ- {8E6=q2DZAHxDBBhXx2(w&49p)͉`M:駇=ļ2ZⳖNS_ƽG]PJrvR^Ɩ8_xǺ kSH;{^['R9<[$j%8h I.< ԷܬN8LGVn魛]hQ%4a8W7Z[oԣ'ջVhKN)ܩh( TL-G7&Ezc#%ފc7E^kG3뽼{^>NES#/s`eXQiU-3_llX]4/@e.Z{eerT'MrgMMŃ+(dhȇ_F}O t<&emf9gI1r| "g|@C0#w/< _dN/#,OQT:@ nh h4j:2cd9`% H9S1EL`)kdYⶉ.,^tܿP ݯ"^q0Ife̞.:R`?PKolXO-1Bcelery/worker/control.py;oFKQ d=*8qZIڋݻzM+5Em]^73Ý]~N^ufvfs'|2y׬R4u>)Ry$MV"M]Yoڦy#LY7,Y2ok fq5Nr1(ȬW W,s4"iYey%u!e?}//x\ޖUMu - k^ >Da+oz)]ۤ}o;~~g`u/fL45LÃ?4q㍬ٔ"ma`H&6mAIr=nV(^ aw˛nҳ$A #~emHS2d{#:i" GI1+ѠdIM-X<f__Wg?_\$(P.)<.2-?-gM0H+0ѾPY) A=L0 D|͡"22rL&is+|MF7›4fk&]|:'5Aћ/ u].:O`$P[n}^j܌hX4 FV"f qD3we*4^{R/0|b8(6M'O>BcFX|05B[2fqU1tl$Ɩ@E 63ϊ2姈%Qʚ`ǃ<?Jȴ\C}L# U+X`< !-/={EZiޗ28Rz#*qy1}I})o׬+_ ;GN;+'Y!*XR5G ^~Ϊ {:~@H+ A.raJV?H`|0ln2!(T5c m(6 _}VJF ,b(swk0Ϛ vz=Lv7%^(߃6h\mkPW jmwQ+?w7 : ZH}R4<hr%VCM>-+Ӣkl5eeAI(0' sJpdPZf7lE^}_L}@ /H,s}Fv5 tBTFϧ38 Vr^OoL dwgJ>0&`]q -؍fSۈ,%*iXB1]Fjh+Z~%M35) ]H|oࣝ53T`B0ѩlWhiVwTF2t.#$Tj ݶC/w$mHﴈ`?$5^aPd+WJîׂ_i0s)h4Ye(*pcXQҤ!,H}sk lCX2:Wu?~ ""XGGThƂY/wa',29h!I\I* 44ʡgGhJeY oX$WcޏW6>;Yѣˆ,{Lq)iiV 3-c2߰oJn.1͜sB15e_ӲCSb YZ /9z:/צԷkl U >~R@/c!$2{D=tpOk\egTŨP>IIȠ0C-fԀZ I:0[Ysj̩׍N?`,*RꮨבOO01U !d^x?X ^d|'5ϩ#af][ͭ}m벭5S)Xi^QQ(h_0Rr1)0H`~@IN,xT!j~F@Ӥ,ٚQ:xҁIU=iZpGհIݬx Žb~ F~W6 ~01( Iꉡ 3"pG ^wW;tN O ,zZhy^g.L1]#Sٷgs&w*AhX7])$b$Gh;Mf5lc2rn{|YsE.܊ /CC(i􍴑 tɨb|! h7mrPAxQJqrHVkq;[HL ß{NQDpeTD;6h:+9S*IG##*r;莮166az4JWjGg%ς32&I959e'hv] Qk|-]gZ ywuN}TR`V+|.L;{&!"|-Ƹ۪O)(3,dCӎҐTEh1_3ws,!Y.=*YYŧBю#kK(6tm%u#CBgoYG[gι֙w5oVC=啸kE4܃˵} CO O{q𨝈@iM .B$ysV HcLp  <0%:}wAǂY+b4vH^ Qй æI}X _6;$K٥ 6%2%r1_Π%j#^O=割H8Eu[`caitoNNh_ZȻ}Գ~yY~É霢7,9=eKa -0j['N?c.>ޫX'vAݩ;(}$">@D'9OnY7DxRw`ޮ .7#D ÁxU'@&'[{|ls(96G;n92tAk_2"۪ NN2 y>[y ο Ws HM:*CvԟD(㼀< JFA/Mgv|-, *LHѭas($YČc)|g%ƢĠak 9D~kfkz;KczYTvUcUs zD-[5E=/%7n`m €ۥXU_*ےcPS)fWǬGƝj%oR#fKJw͒EYAG,&0c{|i*<Z: ]4z~X,ʲ6: G9$Ob;1*zs;5;`Si>j1uLխ^ؖC:K^9:[+W Cӈ$V%>5%z*s> GRbO:JDLw`T-T =֧RYoc̛L ~=?PKolXOo7u{celery/worker/heartbeat.pyUMo0 W!vEOC vX lvAD"e"~8M3$GJSIA`I>~ƒC%$^, ,N.Bm,E Kem1B9HbxX  'Yu\o Ψ#osh+I%*;R]_?lT62bKK쀙cyB? :Ƞ KPƄ`P@:O,IR Rf CROԶ`:ߢGͬ n˕54d3hy!c7C|'W]_bx@5 iMR.;рwDݗ?BKa^hK؊RaM%U}u@x1xՉ:G, :.;JdiC2\W-@hfCB-^VdѼd",egăYj:mE5HHź#~1rыl{d(gv0 !XA lD hpW"-5=7/9Դ@\'v uz 7֔Vy(vY.$s77}{8'K wcNVR$]6`eↆ+K?1nAҟXQfsߣմ!|quIPKolXOݒPcelery/worker/loops.pyWo6~_,1!lJ:ۜ%R%_H46=$ywxDir^n_mtJhcȉ6Zlm.ti B] ע3ZrE/I;|x[(,ՒjrbamZ?sIp r;pu']еkw$>w3ahR5ڈlp˵Xh6FTګPum6!}@(S C5@,^<(gErL~gRy) AVNi Z l^߯nOp$G]Id**$퀾PnDpVUIc< 2| QȰd* ~k FycCN;lR>巯z}vDi@ ;'UiOʅT[9ji!C?|dhU(9N*ܽjhgC03廖3O)1ʼvd:YX 5Af?O8c-!wZ|~}p8Hl!&, jH^Qߒ+F)`C2e$ӥL*a]Qer"{wld!*EAcKlQ=A{lz}7kI&@K`ʋ("NYI g݈5L΋4'ñEn .',8#xkM68#s/7⃧tZg ;.?@N yypg)nQk +T1{/Oz&# 3{ٗE' ڂwf8ex0S=Q;éd8-y3 \\9;g19@my@:ǀJQcD lM JC,Lp͍~|bE:s/wN:slNnbx }dC\yy_yĠfG^z t_I~-$էzӡ?`B^ӬI<>skEQ?٩5=d?+i^Vs?Sx<2EV?!{9=%_PKolXO{pcelery/worker/pidbox.pyWKo6W[+ ¾աH}*`P w1E $}/I\ɱ{7(6ܠ/;꟨6׎#3Z6xU'{Dit 8DVqhT)7TڪJVgN;U㈙{oqVhcjb"-=qbIܼ&] lV9#_ ӨbohJ=p(p$0*%آz~6UA>; Q؛|@A3j$KoI%yͪ$Rk}4g7ѠB8= v;!'x< pԖ?3`E[sTQ Àn8z69:m/Tq;hhu-Q V|;Vhl-s6  h^rRqi96%͕0 xƊDPN s9"=y*Qw -4bHZ! BF;_Q-.#^]r. wJSt,]f՞2)slO每O|pT T4#F~J蓅ǯ~.cpjmcES~@_ŀ p gb>o(مqU36Hfkt V.+m:Y-!}ǜ JW(F^h \7*X8{4,;Ы"mz"1W&بW* E}⧗Ua+8Ӛk4W/:d,h^+.ķsrh{vli"ݷ%.g3ۈO_5,ڬtj]l790 J@vl>5y' cjhD4+/1%1V׋)f KBLeq:~(8m0[ZrϪ g_gPN\w^GLNor~9 ?nV!qP PKg$O /5Xcelery/worker/request.pydm|~V ɫnRifZ|oל31c wV6w9~S;!V7n;nc_Fak;ee*?ց`[M"#-bP3Ҏtvw>+oxd?6C]I^éaWҝͲ,/KPkY̗ RUAF]נ!k5+7IEܕFډm DA9z PWLAͽ!H[와6Yt&a?~s껯 D5s+qW7ui#+6ugv)kJ՜"l-nŅz3N>j1V'tLb[)!²RwKYg6  ̷U\S35/5`rؿkjl5q,vn-Q1l_,ͼTuj:3'"`MدKeTcw7 W*X5lp-%u9:u/oSM]w?)@BQ@zwR_VzF}U%noP~ԭc|!Hٮv4,m]MiTcܵϨ'r ^80Nz%6NeCe3lA%VTMZYȶ`hU#U|MO i67 wqy=FD0641j{m׷MZ4acLM[V%Asv  ޻@)O ڪm+$LM7ȐQ[ V Chvf5o=M+DF8j ?H9dq\@E>U"ިf6>TXxJm0>ܪ#= |fi^XG[ 4C ^b2׶!䫱}-Ѳ*:],2/#Ͷ 7+ \315\a<2|K:{+H pP6Ѓ m0r逾}cwۙyeUxyEA!al uUldf30DR|Bnx aU5@}JmR*:e( 9(01T&nn_GG|_O+7)&)]XEDS֘$/vq8&O8%} qϔ,z LH-+93g0܁…#]mQv?>(,@mmͶ?G F՝|"ⷋyoj%A=])谧%~5P Ut!;1o| ¸#W!yۺid$hć=W ༉`N.N'yo`R)c us2*iC3L*{HfFCeQWQy#^z#=hQ'J:qN#A;pFPOqǐ>DK#HW&b5Ս[&qp"x Yʦ(`Z3DH ԨxŞ1M!9`3m2$," $&p1s88!4s:[25SęC$LСpr."Lnoq?:}].Q^u4|+y Flv NvmCL"H9szmƷ J;xd6FXՍ{a 9 P' nھ3Pϲډ{Y}^=lKy "|^ݏ:?Ba[2o0R\2~#\U-TǺ.sdZ={# 3Atv%E*-68}^on/|f z7JMdҞ8~x܈&[^c" cP%+"$rL.wgZ_;Qc7Z2dH GZ L,V/5 z`'Y#>UҼ=U[?GW$.ubإC,mCJnAFiy% ת݇FV}_ߢ6bd XP\|֞#ƺad`@WhaS7gA1 Jy%յ5++r| W_3ݘ-D.VX4zˠ$Ůwu&B$Eձ&2mîHHڮ=UxGWS nZߩ FHխOy~ **_EU٤+}};_ڔ1骁,|7G\47ۉMJAUY.rck;{G/^ YXMCq x-:h3DS65[!0bv"0mR_s*3C}+L4МP]Ձ}BWxQ9|bI&_,[?b@\ S|q[_{^*w~SQWM\]Ҕꭄ|\=o;JuzR̍|,yaaqr: 1O"ުH?@;] ;y޶ U׉TV=C JiZw2Bb.#L `רYD17"C5kVtiO@q]hFn9xӃ'<g^;Ee߾Bc؉Iƽʦy_d:B'dh Xτ:ۜR$?Nncf?iSB}bT33A-1RAջG@ 3"#! /΀k믑/B[U>rBhX#Dyxqm&zjVm1`Z|90>/$׼ ݍpE]ZT@LhȓVTQcxy̿m/-\<1 ޜ'F3:YJ:"Z~yҥqG)!8Xfg=fa^F6ߣ evA165u#FT% ]uۡu 1O\ȋe4_[|Z3΄`]@$QKfw;>~L =I6>,FBtEv%*IQR=<}@PE\\o%(i m8Γ>(5v~u>5оH޿D({'׃:pu&|\+L-ub#.c{urSx,:~#"ltHA!џ2E5.' OF$J{TxJ J`:jI|'QI~tt\p?}R2Jp<88.26u0a z;] P{.|è[B% ^U 7 i[B h`K^c&5s f@?Y$0b7}zkn9)[sA;o1<.p5Z‹$xcmT$"ԅ`r΀u%i  z {k\o}ϵJTT F_nrj1к4&3E.m5Ӹu3V\9BC:("9.IQlT@| 4?'ȮAsY5u?(4&^% e<^V"Vk7DRx ]8_j R٥=&Ow?S'&;VcN󹧞-1PKolXO*!; ncelery/worker/state.pyYm۸_&(,^4 rEp)^Vu"e ~{g"v-pf8 G~H.rArQ͚j{W{}٫Œ,hT H%Y *1f~EP' jv8dc9%NJ#>F7JQ|Dõb9q~1䁖J[E{0s$/|# $-G8YFwڬ}*&M_g`dl#5*zsO~h IBeUd8qKơ-;HÎ7:RaɎI`e/xӫ_^?s,\=`m^1]㍉~9c%W5[r}ŧ)NݜZi '6' &X>JrGJIv.6έ[F^NhSFZ2ULȘ9ܒk=787K, 1;WJ h[Rz(*ڎ8Yr!H3Y`!N<{B_~X4' ذZ9{R^VFB鄄 CbODV zp#nA?FO;Cu͹( r3~X1r3čRv5vnO)2m Ϳhn= "ӶY3ΙO\F:t!7s; kos0iDԔ7Y  oQʜu%vvWݲ~} 9P 4ޛ?PsV ALy/;@YX"!;%^t?IW?*Ȃ1">#h3@jDk+wMC`Mup?4зӎJ\㒾gp]+?) 9`5㆙+~- I'\_y]u V0s4K^xr z\ u3pY{y6$p/LۂִZ:ւ1# "Wa&bΓ͆,^ymcD7x!5>wѴzH7^\w%)[}kuken%E·*I{A YuG)!k3j,|9{Ht476"FYTWhǖ8$흁<6A:h? _ FȐqr*k@9Y.@V4^ xN|oql.0~6Tov2qFX7#~}t vkp1k~͖ g!sΉ b&5MΡC%\6CJyGYj'"2P]zn^&G XD sV×zVFؼ%Č=0HknjѼTlǡ.& 6nzZwak/.ܬbyò _Z-OyW4G3ٗflDAyWHso=̜JI zҲ)vi\8Bv$y%[= xm ySv! T?$ռ~9GuJT(mYu$澌1u7x6&Fh1TT;MO'M> )]Cz' %OZ ^zD)<0$ϟؚ5 6u T3,Y#'ЗsjF) xAMRhyٽdN6L`)N$;'l>EV{MC2|UƊ_{ =|zpY"…5m3(Gg #?^uBy' *9РgSՅuÛq|$aP#zƓL0nuη"`LAGrA߻fSkV•\{bͱNf$QJ/'U`[9$2o_~{wiNͬ+,ك^9(+ &@ 5 p ³kd_XLHǹg0l8콿RnMtRl [֓PKQbP tcelery/worker/strategy.pyX_6O!f m61Br,Zvm9<ސ~U%ɖ잙A c~*UժiHܼϲmzfV&@r^rw8?vԒs^-JkԢ3Gvt vӽRZj4%(ukDZ2+ xpbpP0@[:-u~y"O6J+b7 nj )QQl.#x"r_YFjoԾ,I|YGNDm0JYZMȅcOZCss@#Y41xRV$'?xZb Z2OUv 1 x%:dL17؈n$HOߝA6ՐO5_-Bgb)GA2b)Y'YIQIfQs')yRoHK2ت(NܟI\$;ӧ wfFOA?3 XЀYbf*4!䄑.@-ӮB [vOEsG(i?A/Ƕ fbj h{g=*װ[EV(eLB/Uh@˙ BOV~-Kl5aIP;}I+)+^-'G`; F44/ o&GeXmFƝ8$-68`ٙ7=-$*x'rw5N}ԇ KO¤8Glaxyvp y/mS9m FGヱHj[".7Jp`7"ϣ+ Ws/a gha)9Gb ,Kj`&a;kS)Ϸz_MWOl\e e]. zc߸kȿD7;p:RnSc{ś߲4v$ҺovTF v[ѝf(?DÔd+ 媼XGJaХ֡R6s3g.LY~%3:#K}bAYȈAJvdM$L;v/!Ե#-I{vŠ~eMrsֵdn%S0H3̼y_?PKolXO]8celery/worker/worker.pykܶ F mVAEcoCw0 V*ǚq;ÇDRޥh{I yϐOɳϟ-f$>gfO1GqZR%|A֯~9;~v`KjeLk[,+zbX3reuـB̝m5Mej wYQx*uOM󳟯_$g6KeQNIL2ZY"\`V@s0JS4FP{XY|PmU"5YQ2Ы A?KE ΃{`L1ڊHEYSja2^fR=x۱δ8R+<7lIL]5Ȋ#\ӾfYH=ZgKRQKDSKE{$^^>O//^J~Y_\_֯/fWObG08p,}G;pr+_}ZMg>}'M$fG `%7Dfpg[rl;r:֠9+n8ڃf [zu9`g\?_YR1q]7g\7&,P,zNBA@ZNmw{ж={m6?"}ytk u F<í醀} (]$ .l܊s'r}5p{FetX8>&Z<WꣁU~4={4%:=twnnRA`-̆ԤlJ1vB/Ⱦ~{+/af1 6aM0y$ylfhO%p>4t:q (*N6K@*uXr3UXMεH%M* >7Ԁ bB U݀4 6ȊK-PFMl[ 7e 侾:ʊٕD&PVE@sG^Z.X7~]`~UtM?pgQfU;(g3 i^nv -ZeHP#w#G&]fx}ۙ`SF;׃0L r4ɫFxÁ&9^LnH0ꨝ #7XR.0E_>Aq˱y [V:'$S$Jq$ڔ;CvO)eD5fо/ylY#<4[V4[:KK[&;N d`zgQ@,QPp G?ѣAXc dY ^01qBH YOscF.h2al8]G ,͊"vN>M:d8CNX);*HǢmU}&=PQ+k}IQEtfCD;l)ET? ,֜Dg?vTH"SY+z.Ӕ6X9x$O'i*4_[Ne; Oci⤈+s/.Kkc20-80~:]fM@RC,m-}צ [³-R>At=!BaҒ}&JEܒ$ .On'v :EK΁Fq搹,XAUs/ʾES$Ӭv d%|9Կ}U}T{k -U7N"3~2C@i=!kW%a}/69ՠ+x"A' J9oWLfPrAKaCCTvFUF: 8~ݨ3,$=M측.ڸ^b¯bAјH7YUc56E]{Q #ݷΤΝO:^xV[XP-揇/#~tFͥonontY#ZHK~"G>(+mhIcՙ6T A3:;?hb`F3qFX2S;GU|:hm]>NfU*zy$'~ SY0Ӫ[x 0=>0{I'g=tc@u:(^CWBHA#ZbJ6F <*VU7 Rr7o୽:yԜq fI6B2C  Фh{3)p:dgy_V"dtKEOk*IQrl%iT{d.!CJkZ %Pz{o1 u:e5w'7@?}ҿ7aEI<"]z]&z^9wҮdUk/쟥D Q{@f[6:9G#M!Գ.Ƨ㥆a q+gʌGO (v^,zΔ64w&{?b-бS^Z ?N,Y#$>{ x䞾w~Ħ?=m }9 eC>r([bp#?FfQrUqQpU/PKolXOA|4Rcelery/worker/consumer/agent.pyUM eӤqH=ĩP)PcTV`}v4 ,!.pnx'arJ<Wm"GJ=|TW)%+wt1[JNk1)󛷇8:cL[#v(Τ9?|ǒ'U<:d-,)f_LPdb HɎ0gH6z{~<_ oQN U`Y SW c~jB(PM`[髶q`ڡƳVY[rdF/խO~PKolXOL$celery/worker/consumer/connection.pyun0E{~@A @ؽ-R\j%Lq>{F걈0ǝ{@EJ>zM(ń,BtP)T 0RHϑ\N쭡 vQ,444 zm{[ܭ=9'иo<0"('0ꠇȫo~wsG~?)#U9Rhg=Qlw- K.oPKQbP"U"celery/worker/consumer/consumer.pyiaq2% h9ZlIᷬURd1j-cj]/.ߞvɫggWuO<³~$K(nڶm5{,+>Ƣ<30=; >m+FHvzGڝ\Z1IV l//:{ !!Qd >51,vbq~``fP2Kn J]\]"yxuz =I T!;ٷHz#].~4r/a?լB|_<"{X=a~xxÁX3cɢݔf>!7ġ.eȖͪDÛavߺL߁2C nBCov1}ײbƋƂ9&r#lKMBRCP]]1PLr2F+M|,&`G7ZiӘ`-VOp-MGZ6``HE}A ѻ EB vE'nxzT_ >{KoQa>4y59m=P ֢@2#{0;v5 ԑQ@y%kx?&jx1Eɲ ?(F$ htIBқ(tTCAрRs}Q@qB5[>cs"T`j ӰC$e!cv @u~LdaL\%ءToP-;ļ*r=yrѦvv r i;P+hL+&n <9M 0=x,2qhIGmz7F>~IshHz*u@(4#i|!` a/v]']DBz~:yIԊut,J H#7ޢ%&x7Qd尵ߣw /~$烧 q(\LOwb"UMQ Ojg5snF'oYrD>n3m &ZϐPQYtK 34OH۶: 7ɦ(nCAQDCﲙøMSGRhA_@cX=НVhF Dy$"m]L%hbG'Bl0. D/`gZρ18NkdgNs04- sH@. "h,2@]4[o&SiL"=&H MÁ*i:ʩ)odB*Zd䙂%}#\ Es7pu0NInURYYaZ1zuhٮkH1%+;I>8ǣrhrL2,Q^; F72 e Y3db7ov{ŔXa?]fߝjk4`OL 9xqmLهOӮ <9^檬/&/}b7Cy2\H^qjK|ڳ;ҩ ٓUfIi@#by,yAr=bM_JoE-C'OAu.5Ghb/LO;q˓V$HZ*!Yrx#шGl $ V6ZJ;v]w|+! 'mi: {(=n{jTp -ErF> zW^.t}^@4xVd %^+nY~tᜰ|W(>;Ti|pIo)ÓdwJaropX yZ C@^ЪOٚq,a4)pdPߪs55c;WhûCA^댗⊬ۄ %RỌЗxOG] \.$"\ӣ&5bdus?&4  0 _r18Ij'F5q"4+^?mΣrocL3w`hvXNiAoX #Īp"D6@iLfmo -)1p1(]LV#@&̇4[F>1}dmS2j@ LFCVeO -!秩ѹs05Ya20 Π\Z*)oj H4*^[LVKV5*j ?@];*1̲rgXƱt7ݎJ'\Kd[s"Kkþb `QI3% #l8BPed5곰Uy*Afk YL&4ݺLE;a2JoMsCv;R6P!; 3tM, i$ȗLw|I'ɭίcl}׺αq`w+aC"q>էޡn 3i2+u/O+FlrHcD)!`Gc5^ޛ1S_݀-ogZ7*萐`*gt̀?{dskv/coC] ax[ ,o Rb1XqP8x=w|s;'["L} ܃xt>ϒmݻqOfMW9z<~{Y"KD+c*"񪖃PG'BuJ_mE)'K,jy'`Jj,kkeDrx s+v~G`!tompx7 cn2pDHωgLx$:#x3ݮ)&E=+t?N2Uq{fgu NxkvKUEa1P_Pݔk%#4C^$toI('@AQ?ZU+wGsH=۵mΎI_p2׷?ᓡNaz5n n7J_4}U&YܫC+Ƣω|>%Vʋ_E)p u!ym{|:ա7DcЅM i|}m wp *}Yuq: yG°s_ mZ" :=PvZ!ZD*c~ -K=\t~P+ rR)3(N'ra 9(*QmGK&s<c;B&W`^y&\>||Oao嘱{ ca+߉^ ǽT/o^0BYVTWxbCoޠ|X@O/-}#y`6֨d"]r|c7@!p(Br0;Vt6z"j%;w (+캖W`_PKolXOawn!celery/worker/consumer/control.pymRˎ0 +_b//fE!գJ r8 4rgt' ?G~p%x8wM /~Y#}ޥ4c 'T!JIރtz NhUiY4lp4C ѡ\ydb@Vԣ0:ƳrUI1h㹡F "=GtU_-mxk=?B$vSl;VirJKij ],*,UluC, qHkG3lAm|nQ>9ǵCFRGR7i˓gY-7b1_1]G*Hdw J소sRvvٽ[7KU&C5Ji-y+?hѠpTEjZ[uVہxҗ7 T'iGkv3HK1lPKNvOrL celery/worker/consumer/events.pyUK0+!d{$zKVJCPMXv}6LҔC<߼ƬVRAo 0ƃRm˒pV}8O_ȎB݁7e1dZ%-*4m'!QKn %V4L@yc@\',ۣ͘l[)F$$qq*@`|$ X[Nus$I 3ۛB_7A5 PJ*A8,1RUmhz%w*AoJS 1>9Rn@Z~?{ȓԺ {|<:}sHS͋#˺z[ƞlD)ALBE/M\q~UQ ))@A7[h]ȶyӘ8V׃p@f MJً)x{K ):x]7n8l(* 6:6h!g7 \KcTPT>SWl =aBWA*lg ASCx'\ʎ CEgT;;^uh/Ic̙Ee/k' >8!l<  w#MF.JSLyR\aH!٭4w /uwU\Kb^ Zheu!N8.4~GG!J'/)p,2PiH@;۾L8G?bRTz^]G~,l5v1PKolXO ' celery/worker/consumer/gossip.pyYK61Bl @0!q0qC%v2(ԌAV!z KK|Uꫪ컻J(-e]]rSˎ|/FdK; fP1RT`sP;7M#J\V81U]74tG\7"x)G?=2R^ݞG07u\_d'k"3 m{,SRц`a#:ּi䳨2N1~TJUJu SzޙQownts{ey{`QKݹE~vROtsuGx pHw8IםF}ݮl-LG-+}xw  x#k]9/ǁj=V#C$9#, 9mMf4?`-Ȯ7 H|"m at": (>]{qzc$:=]E D3xR:e6s r<6r[kKS+IoF*fE4ՄI"6p\G= $1扻KkM2^ Ztȧ33R]aXe! P l@H+CxmxR\R|sM^(Ep!g.Jܫ+GͰU=[KVs_}xy?c'Ic۾UpByQw#xYpH1!8%/-xïy/{5ϗg8! By.Lt ubwM$<0BFέ+iۇHf%u}o(tǵCDщDM/TU(H3@M%sV7j35tIGpM72ht\{u! Bq PKolXO$Rcelery/worker/consumer/heart.py}R͎0)Flz ąբ"qtgXi 3,>_E*UOG8,.3Hٍq (%CՐcD5hߢ&bPܩbt5335ۘ1cfR2ZdV,UM">V -AеƝ`(&ch epF"uDĩg#fCn$ljb}!起`YgʀFhRŎfRmWWK~rUaI2;.^ޤ; !Ȃ Ɔ[&M%V6\}5zEfb\΅M!MieE&m 4D/qN8mY1fjw:3'µa3#,#^bM\-EM2HMXTL*&r팼^HL+Π`㨷.&1:"-mR? Bޮ1UWTHeVQ(ZU+W_b#A'-uI5W*SC_֐{=G<ߗAu}i 7?Kgz< ȅS.ZdE;['Ob'Dpdݕ7C}fniݒ`3>'s7zg#޷c.뺳X |̄(xCPYw}vκ!բ֬@dl'u#N `}vwjfe!Z zr!նڬLu]&,{Z ]pU |>4.t2頾:"Ta+冋[2]U + x,(=(rRTS0m8zΦn+B )M:.\yv&rB{*fl~鬠YLd)J'XM\EHDFޓ:ϥ֔n(-^i,&*f%F8Vd#NiF5xwK7_\ *}x 1\Gy6K&*ksyGǃܕZlܻux<ߦ|Fk8bHa`5? =۝#=ݻ{g5^0ɡ˾cC x{C PK"KqP Mcelery-4.4.2.dist-info/METADATA\o$q->ٝ}S2IdLngH`8ALj!q@` d%},<¢,hdE\so*Y1v2-C*:S;sz,P ? 'O,A $n( H8(.G*䅿( fE5% &gEi#u%u{*T:_rYc: (<+"=%y6/"j"Zs@AXfwϲ"UӥwZCy&,'cb<wP #tLcٚgA?:>9H|ݚ,UH =5 K&e<(d cZ}Jd E$DA: 4h IK1v;޿;޿{=ڻ߹$;ġu8Gˣ" )kHG*aTT!wʁmcn]p= Xz7gݮc]@`7fɴQ1F6LRl2@3Eu(m'B'ni7dw%*_,`0˪8 Of;0bmqlh`~KpnwZdeք[ o[5'9jK7{˪[۰Q nm9_:7W_h"6,5pèVVwR]=*l $TY"cwו1t.N]Gm]cQ3 g3uX>+`;q9Y.W֭6 ͌H4Hz4+Է[@IRZEfZ-g z,5hk׿q]]x4մSU&/P]w1dMpq(O,F$Lx -9&4z}D&a.4&_$l@6h.> 3p%zc] ôfuG-xi} 5e;EAw27v}=pTFj#(0бB~vNf}F.A,e1\?8݁ XS|5~1ؖ,{p>`kF/Ac^1e;\N{]Hr$ɽb*1Jäɉ `ؕnh1J,r'nfV<;?83 yyg)N<(JD *q5ar"O" aľᓉYt2aV[p_XSԆY9%2CVˀhDqJbL4:cIYB*\J!$ WBJh%CgXHŠa. b  ʃ%__LMDAķKKfnP{YCUerb6h&5'P*+LEs[$yފ܎gNN8ԝ dYKeF3Z(|Qrق-O$Sdд$ ܃XghIs9]Wg:P7&eLBⓧ`a5XJ@cýsŒѠDsILMͼdloz1Ƿ8h|Wn{)y:,2Z.sC[w [{~o;A"ME<(.-D͊, )O!Snږ]bMacxQ LlxR8Phdt,)^ƸA]޸ >8! )l>榱w 3|ksxzǎC@(K";Bee!lɐƔP6ׇNSv|<O9X$՜tO\FLa)l ŹȕgD ,G+ qs^f++"gǏUi8IR5M CP hfLjwg2O!tWgz{->$ 5S2'ea=:/f=:iEAXG50/䙢uHI4<՘E o.KMQVjV# V9F a!-J|nY5ӈjyhZ>; ,ϔ<>i=] 1jkI3KC:$~}pc_o-f) 6{a(vvٝ1t[X:2X4+4S0y<X|bx i8YIdY0XDTvA{ᜑbçCH! mF`ڑ)T2PȢ s` PB]&_p2!{?8{dce(LM)b;Fjaucz،^< 1re=JZSnȗ Nwkx~0Ji<}Q7\^GZ=Kxi4B&$b^cePCkȫz|??1_?}◿wL42 S#~ӿ˿?џ_|z~o՞]E\E qh{xڔ KřMĦŎ u<:( 0Х[VC3Nd|ڣ؎- 4Æambh ye0!JL9|;ǬGO^Wf˿EO965hseť&ll3ЎWMg7Q S$x2SwYLqR)M.djb&ho]qxSM:4j!×E {*~3&ZfiKɄ:'R2(5& W?Wa2yfRW{Z#ٴ!woܔQBxZ ? AJͦ,/׬ <0o[`m~^9LI-0܂_A]@`pdy?!)qEJy$44xв'uy^Ϟ\oj9]]U]rNS-r>vޕ8z .)n.d[p$Oz#Zj=o pιA]}}C>Ka{B>riRתodj2ùg?u}WU2OC?;63&P)xu6lkyՁGuiT\`a)'1&75aqՋ;^-#,h\.֔nem͒`uv ay.}Kj UbMҽ'[> ͅu1U=0]V:4N*\OGb5zb s/o dEk*2=2(쥽8ޤ|vH:Wh-*GkjѸ>q :"™txp;r"*ss$fs|Fҙ**ez濑9Ojh<,DN;xeMY4R]֫nڐ̛Lt%/r7dBq&@4ȡ?Qˆ%#Bo.})dx[{:Fk̗C"c^;\ط*~tO>ϡ0"Q䔦ْ 6 \#\o XS5&A=UA4DxkޭnH QEۢتNᤧDeL H6\y7$"ͧz +cn7>Y{/,M!' b++HR :1𑞫*伊F/R oLZ֣..YKbZE'&_ غQU_zkZ9gUT.oҽc1X+|—f=j$8)#t{ 򋟉& *v[|niLGЊԳSڄeH@操&MmZOU!Db U^Yppp)Gri{7,kTJߞYKu5(mnnn[7{_eҸ#ٵK(tpSyoR[d2y|xO&Տ9Wf %^iY2B(=߆^\Q]"Me0E2EPfy/\ d<NKޙЄQ-妯ޛ" {R>1?1r] t'd,O긙=vQ^#WM%sqDUbGZV gۛ]S0qx{xp :3« CQ8qihn ƗWAqb3]珤ovjPtFvBPQz7 EZH{tNo3'(+MYçH: px>'nLl|릏drZے,f]zk DߠG/9?Ÿ@N\jjlU\-o+0 Aub!?!jXWpE21,kfyYHD[0ny!Ww(y8-o1Q>r^t+cEK5CVW4z# 5ĥO!D=pq "W7%nuu&F/ّ;ˮ_i< jq7,H)E \g\GOo*4Vl&F j!985?uW^_@?[d[FGNΡܱ EXF PӬU}A?;m0?4 "z=_h"ި+dbš1(yky?#XT~JzN, ȢN=1}4Sj6?0Şϙv:IFQn'S($,7jՄ54ۥ!@(A]6Lnyeۼ&/V 2:$޻~ Qv\= l~cUU_#'馫M ٧x1v>'XAU ćQ26lfROsr3e P:f #¤8_V$tFGxAy#$愮5J\;ĩngQp=tt ruik!49Oj),Hb!V^(o{ 8wz!=62e2Fxv8ʟsLb:S_Ev@rhu.h$^OUKa[o)z AF>49%eex5.(Ϗ3eE8]DŽ–C?]}Yo_ISrE.07eM8Bg,5wM_J(0DIBYmXYES$;H#=}2Y>2o4jvzj[KN錘;OkdhqGߐ3zh##K81># ~?dA]\u=~Sҿ3H'4T5 k}ZEV}^weUfuA WuV2T3Q3}m4kY@g} lew~)$`ʊ3Dfc{%g 쵔4Ѡ9yDnAztvPMI! Bm8203]/-WBkcN G~Uc$\1\0TֹԄ{7X\3\v=<{H,ęƔ.&?@8ӡ?_drFZ,FnM !;+t;d:ztPD~:|m`+yQ'`ɺ4S c` >vV z Ć[[kN5yXؼYx1ѓQ,Q *suCJ%reE}&h'pQuF=F(Jn=\Jl⾴-D~+_Yk `'%Ow\% 'r<"r7/^빜/]-Ayf<+"G]ї/ԯ /n2Ǭ}k6x㳧~v1}\^]}ڃb-(H^ [qtXZ#юNo e=F]'S{!4tӑDըHStnJS4 {`+퇲JapuJTQ%E <0c0͛o \f!kCknz;kcgDQ:BEQ'"/_bWV/qZxn( o$/"mMxl Pݛɗޜ ~Xw +RD/C͹SM9_;X|@N=X1ynԦ<.N;4OpE 7A~{~ K(]#9S~L>~'`O0_ve"[Lr[r4#`ͧ܂63[`XrVt^=ˍ.XFVt+X;Pd]r۬OF a0{mZ{Q.I*B Aq1t& W_sY}c.7RyݤGVNz}M8IƦ#[Dz]*v']ig{^cEU9px{0:rv%5$dKZ8`ҁ+K4޵[gӕ&Y2 ={2=2t^p( Q֫UokzBM|zLrQ̇H᪸gkOn+WY)qmJG\Rd,Ke^\;K{`ׁ!9LXQZ }qzgGj,'u쎞XM4'?Yl Im[B.(xbFH|Bb7$vŽr21Nj'?ڞ(+ʈ) d[i̽>HOd[oA[Rㅵ; ܵ- X0[Tvoz6T:o󵖍0׻;VM`- I DŽ7OZߤY ` W3"nmN? lW-GRx!b1N~6sw뀹=8p*)0GICXYsTh.1SK| Qd㪊G< (Zמ^U4F$.0WroO!bqR{&9[RPiлT ke%MH%>ITC87S@kB8@n H򼛄x'[V!Qу{/5aL?g[ldsr:uxNf2w.(lzd$>{P@N,:O ^4܉j㰵x>neO ]P7o=PȈMvQMeL-D)JS=758?7NENZg= 7_3ه{=T nAL/~)I\"tEMЭl7YX,'JcK; Lǚu (YѭósfuCg{,\FiGrKjx*\8K5q 6@ 6^JYD9$;@a-$B#iĄLϛQEz~͙ܔ[̱G# )?@$q .(`UF-[IdVxV65uxIyup%kc|}v Ʌl9bA4dE>!A+U^N%%:(nr#wWheFLˎ$NYFpM^'lPc|i2-0^bx1 yxy/}3y: L eJG Ua~ )=%/[Ѭ\ B _3.6B>hYzG\$j/l.@ηFiN(~5}#o i5F?O5f<-!]¹c΢wV9;9?VaTۇQ.2 tmO6N ^~ShPV%G֏$'uBbkkI1=jqJ+oZÕWQO? 0z?=_wH26aJ|>AyĦi|w/a+՘?"xM^caݔ1nƲTF ,99 I]_;ҧ*֍D'/,t>86?!KJ>G) y P|ѻH(hߞtt3ObN` 3'^074)65ڋ:Bدī[X%=•jQيMȀ 0-yث冬|[H=sE.!m#qDz5EqTh~!{bfDFcE^ tC'ϩQGIY-@^@ϑwQ}`}4 PlF]-mW:no7CQbnK H/a3L\ksek] 9brJ8HN+_}J|Wq+݅]e%uf:z[l~}w~쟀\"[GCE4b mHL&lY]ǚUbL)vȢ>F{ډ} ?=0曃nyʍf0 & N-8m<.[ jr1_^Q&ջv|!'# ]W m!/o?PW|zU/ ӔVƓ ifgvz ͹C M.we0{ðUiJ\x!?g:Dzx B' $qv se>)'Ab ӎ82=R _OF 8Yл,ө\|$\?];ĪG\xWm퉐bk|E_i[ƳOѦpܱ CB 9_%\' h;e,(Z}cnD3>e<#Sh 4xƞSѽ)5kGT|TЙ5 N^7&uҢԿ+r~b l3#hҼt?DBI(Zb,De;eq=|-w] C ՀFkXn2}#CٿZsQx8eXyVWTtF\uj/8f k:߫@7s\%^}lSHCFfvST{U=(]!(D:pR\v@K 7ҲPKJqPO Icelery/__init__.pyPKolXO| celery/__main__.pyPKolXO~ celery/_state.pyPKQbPQLY|_'celery/beat.pyPKolXO~U0,celery/bootsteps.pyPKQbP$ύ42<celery/canvas.pyPKolXOPpcelery/exceptions.pyPKolXOyycelery/five.pyPKolXOK_=Elzcelery/local.pyPKolXOUfacelery/platforms.pyPKSBqP4 !5Gcelery/result.pyPKolXO9kǓ5tcelery/schedules.pyPKmjO"-LMcelery/signals.pyPKolXOnT$ celery/states.pyPKolXO celery/app/__init__.pyPKmjO_celery/app/amqp.pyPKolXO^Gcelery/app/annotations.pyPKolXO>6 -celery/app/backends.pyPKQbPF26Pcelery/app/base.pyPKolXO%}?(Hcelery/app/builtins.pyPKSBqP%;APcelery/app/control.pyPKQbP t{tz8n`celery/app/defaults.pyPKolXO~< pocelery/app/events.pyPKolXOFE{ #Kqcelery/app/log.pyPKolXO>,$y{celery/app/registry.pyPKolXOL@c~celery/app/routes.pyPKQbPyC)Ccelery/app/task.pyPKolXOrhg_^celery/app/trace.pyPKQbPg1celery/app/utils.pyPKN%celery/apps/__init__.pyPKolXO]i\celery/apps/beat.pyPKolXOuYy=qcelery/apps/multi.pyPKNvOn21=celery/apps/worker.pyPKolXOG celery/backends/__init__.pyPKolXO!U /gcelery/backends/amqp.pyPKolXOZkcelery/backends/arangodb.pyPKQbPB/N ^%celery/backends/asynchronous.pyPKolXON[.!)celery/backends/azureblockblob.pyPKg$Outb$celery/backends/base.pyPKolXO57UnDcelery/backends/cache.pyPKNvOF Jcelery/backends/cassandra.pyPKolXO؊d !Ucelery/backends/consul.pyPKolXO\{.SYcelery/backends/cosmosdbsql.pyPKolXO~b &acelery/backends/couchbase.pyPKolXOr ecelery/backends/couchdb.pyPKOuMD%jcelery/backends/dynamodb.pyPKolXOtVk Kzcelery/backends/elasticsearch.pyPKolXO,͢Fz celery/backends/filesystem.pyPKQbP㷯 ,celery/backends/mongodb.pyPKSBqP:~Qcelery/backends/redis.pyPKolXO{ Acelery/backends/riak.pyPKolXO5/ɭcelery/backends/rpc.pyPKSBqPa 2celery/backends/s3.pyPKQbPI8 $celery/backends/database/__init__.pyPKQbPnfy  "celery/backends/database/models.pyPKolXOD1bh#celery/backends/database/session.pyPKolXOjVjhcelery/bin/__init__.pyPKolXOI"Af.celery/bin/amqp.pyPKmjOs%xYccelery/bin/base.pyPKolXO[hO{celery/bin/beat.pyPKolXO: AC :celery/bin/call.pyPKOzZ882celery/bin/celery.pyPKolXO|j8z6celery/bin/celeryd_detach.pyPKolXO: celery/bin/control.pyPKolXOP4%celery/bin/events.pyPKolXO8E5k,celery/bin/graph.pyPKolXOGsA5celery/bin/list.pyPKolXOl9s}8celery/bin/logtool.pyPKolXOgEe=celery/bin/migrate.pyPKolXO°g5Q9-Acelery/bin/multi.pyPKolXO=  Pcelery/bin/purge.pyPKolXO]hu)5Tcelery/bin/result.pyPKolXO|I Vcelery/bin/shell.pyPKolXO,-\celery/bin/upgrade.pyPKO{k7-bcelery/bin/worker.pyPKg$OТpcelery/concurrency/__init__.pyPKolXO13rcelery/concurrency/asynpool.pyPKolXO'celery/concurrency/base.pyPKolXOVzr!celery/concurrency/eventlet.pyPKolXO%p ϲcelery/concurrency/gevent.pyPKolXOycelery/concurrency/prefork.pyPKolXOL}celery/concurrency/solo.pyPKOtcelery/concurrency/thread.pyPKN;celery/contrib/__init__.pyPKolXOoR45ucelery/contrib/abortable.pyPKolXO$I8fcelery/contrib/migrate.pyPKolXOoRpcelery/contrib/pytest.pyPKolXO*TOw/ celery/contrib/rdb.pyPKmjO|Lcelery/contrib/sphinx.pyPKN"celery/contrib/testing/__init__.pyPKmjO&[g celery/contrib/testing/app.pyPKolXOzZ)!celery/contrib/testing/manager.pyPKolXO]/ celery/contrib/testing/mocks.pyPKolXOb celery/contrib/testing/tasks.pyPKolXOjwm celery/contrib/testing/worker.pyPKN'.5 celery/events/__init__.pyPKolXOa)u7G celery/events/cursesmon.pyPKolXOs V y#celery/events/dispatcher.pyPKolXO5 t# ,(celery/events/dumper.pyPKolXO©?2-celery/events/event.pyPKQbP"{0celery/events/receiver.pyPKolXO ;VI 7celery/events/snapshot.pyPKolXOSob%f<celery/events/state.pyPKN`Xcelery/fixups/__init__.pyPKg$OV diBYcelery/fixups/django.pyPKolXO H<acelery/loaders/__init__.pyPKolXO5`ccelery/loaders/app.pyPKolXO,bAr "Kdcelery/loaders/base.pyPKolXO$ѸBocelery/loaders/default.pyPKolXO╦B yrcelery/security/__init__.pyPKolXO/A?k Xvcelery/security/certificate.pyPKolXOlzcelery/security/key.pyPKolXO7 }celery/security/serialization.pyPKolXOẹScelery/security/utils.pyPKolXO ?@zcelery/task/__init__.pyPKolXOG%@ $celery/task/base.pyPKO~ 0celery/utils/__init__.pyPKolXOjE celery/utils/abstract.pyPKolXOw7kcelery/utils/collections.pyPKolXOvWqncelery/utils/debug.pyPKolXO23Da_celery/utils/deprecated.pyPKolXO(celery/utils/encoding.pyPKolXO*/P)celery/utils/functional.pyPKolXOǼ7 !$celery/utils/graph.pyPKolXO̘celery/utils/imports.pyPKolXO> Fcelery/utils/iso8601.pyPKO] "celery/utils/log.pyPKolXOq celery/utils/nodenames.pyPKolXOq"IO>celery/utils/objects.pyPKolXOCD N$qcelery/utils/saferepr.pyPKolXOp, # celery/utils/serialization.pyPKolXO[ Rcelery/utils/sysinfo.pyPKolXO7scelery/utils/term.pyPKolXO9celery/utils/text.pyPKolXO &&celery/utils/threads.pyPKolXO[Uڛ72'3celery/utils/time.pyPKQbPCcelery/utils/timer2.pyPKolXOkx!Icelery/utils/dispatch/__init__.pyPKolXOW4A6Jcelery/utils/dispatch/signal.pyPKolXO5 2h* [celery/utils/dispatch/weakref_backports.pyPKolXOTe^celery/utils/static/__init__.pyPKNmjU "_celery/utils/static/celery_128.pngPKolXODSopwhcelery/worker/__init__.pyPKolXOD+icelery/worker/autoscale.pyPKolXOZˉ ocelery/worker/components.pyPKolXO-1BCycelery/worker/control.pyPKolXOo7u{celery/worker/heartbeat.pyPKolXOݒPcelery/worker/loops.pyPKolXO{p֕celery/worker/pidbox.pyPKg$O /5Xcelery/worker/request.pyPKolXO*!; ncelery/worker/state.pyPKQbP tcelery/worker/strategy.pyPKolXO]8acelery/worker/worker.pyPKolXO"$celery/worker/consumer/__init__.pyPKolXOA|4R9celery/worker/consumer/agent.pyPKolXOL$celery/worker/consumer/connection.pyPKQbP"U"celery/worker/consumer/consumer.pyPKolXOawn!$celery/worker/consumer/control.pyPKNvOrL Bcelery/worker/consumer/events.pyPKolXO ' celery/worker/consumer/gossip.pyPKolXO$R_celery/worker/consumer/heart.pyPKolXOv29 Scelery/worker/consumer/mingle.pyPKolXOr6celery/worker/consumer/tasks.pyPK"KqPhlG celery-4.4.2.dist-info/LICENSEPK"KqP M celery-4.4.2.dist-info/METADATAPK"KqPY_n%celery-4.4.2.dist-info/WHEELPK!KqPB\'&celery-4.4.2.dist-info/entry_points.txtPK!KqP lf $'celery-4.4.2.dist-info/top_level.txtPK"KqPZŨV5b'celery-4.4.2.dist-info/RECORDPK.Dpdm-2.23.1/tests/fixtures/artifacts/demo-0.0.1-cp36-cp36m-win_amd64.whl000066400000000000000000000023461477560627500247610ustar00rootroot00000000000000PKn&P&)demo.py-/*Q/ʄ3RRK 2J4sS5PKp'P‚|demo-0.0.1.dist-info/METADATAeN0DU.CB$ $$ D XZv7{֭Jpxf ~yy^Ɔy6Hf&BΟ{ sOoYe8&nB+k1Izs"VI̸|[wc e}]4bA<:yN17; Sqd[gQ.r \ 1P(ECxdl~PKn&P&)demo.pyPKp'P‚|Kdemo-0.0.1.dist-info/METADATAPKp'PF_nedemo-0.0.1.dist-info/WHEELPKp'P"demo-0.0.1.dist-info/top_level.txtPKp'PӯΣ[Cdemo-0.0.1.dist-info/RECORDPKaopdm-2.23.1/tests/fixtures/artifacts/demo-0.0.1-py2.py3-none-any.whl000066400000000000000000000023461477560627500244400ustar00rootroot00000000000000PKn&P&)demo.py-/*Q/ʄ3RRK 2J4sS5PKp'P‚|demo-0.0.1.dist-info/METADATAeN0DU.CB$ $$ D XZv7{֭Jpxf ~yy^Ɔy6Hf&BΟ{ sOoYe8&nB+k1Izs"VI̸|[wc e}]4bA<:yN17; Sqd[gQ.r \ 1P(ECxdl~PKn&P&)demo.pyPKp'P‚|Kdemo-0.0.1.dist-info/METADATAPKp'PF_nedemo-0.0.1.dist-info/WHEELPKp'P"demo-0.0.1.dist-info/top_level.txtPKp'PӯΣ[Cdemo-0.0.1.dist-info/RECORDPKaopdm-2.23.1/tests/fixtures/artifacts/demo-0.0.1.tar.gz000066400000000000000000000020161477560627500221010ustar00rootroot00000000000000^dist/demo-0.0.1.tar[oHH{.<AOdE|B 7$!(݄ Ǜ$#>NM/߃o*^|(TBO,χ$jY\~QZdބ| T'g8YTf3 u2:\ Fw4߰oj>M@߯3??]^|ե*Eof]Ayj#9T'G)}v0?{diI 1;Mڿ{CvG|؋8u]BY'/(ښl$-˫juL.ySfɼ/Hѵ(@kS?Y+k?M}lAZ]7]gnɔ?JL$'u&O O\yp ?{4:2֛j[Lӡ}0R&|Dx_ K'xQ<;cǬ$~Lo[8,hE9e !ձ'4ڿӧo1-߆XA.({|􎐳*SʼTk/_B`4cݓ XPK,TګXZj%demo-0.0.1/demo.egg-info/requires.txtMA Es m]$DDtXx]Wc)h5 ~"xJƥ5[UwGVYuPK,TbǬ[$demo-0.0.1/demo.egg-info/SOURCES.txtmK@0ύ!:FՇMwKg֩0Js&YN}wC3Fy׹¾6A vq&Nh܎iJq<>PK ,T&demo-0.0.1/demo.egg-info/top_level.txtdemo PK ,T))demo-0.0.1/demo.pyimport os import chardet print(os.name) PK,TYdemo-0.0.1/PKG-INFO]A 0}N T JAPcQЎ0MNHBk4( n"/RrF UupMGUWc?@u>XA.({|􎐳*SʼTk/_B`4cݓ XPK ,TB&&demo-0.0.1/setup.cfg[egg_info] tag_build = tag_date = 0 PK,T?hLdemo-0.0.1/setup.pyMj0 ~ K6#Ê"ekۙw8?Y4,4aN$[*ҹ haHЖ}ݡCR ~1[&ds:}@.)Ld,v*-wBaXr(Gx(&Jw!˻gn9ܪ ϐld^O-˘T1T*3E9fš ~܅( ?ROO1Eָ.ޭrĘsvbיc]_ڈ!ݐc]U Vb:{ \dZ:2BڰUfV.6ЍhRPVUhC&{0۟y290Mz3Wў}z}Le4\ЎJE80b'ʉ]vJW4hPK7VRE3editables/redirector.pyRMk0 DO-A`2aQȭcف>iBrH$=EwQ_F>jSʂ[MDmOڶHOe)]gF3xvxӢNzѹ7mL؃x]9T-?99~c3P1S%0üV6?krƞ,0obi|iIKosݠ G*)I~o" EqݤsgUh41Y9G o5ArUAX!GB53Ä Jz͍PKkRqx0#editables-0.2.dist-info/LICENSE.txtURMo0#F9V=dfs$٨~gHmOho xhzah(J3 `=tf4w8Lq4O&bFj;54(8 xw z4n5FBh]3@G3Y5uv :opsS0چHD5Ԓޞ]Yh&pv=.ӡK}6=53&F[O3_h&Rk- n8jyu$Pit}ﮔqCk)IX_3xp\.ǽ?FsJGFJOpq,I?WO5XSD*D3X E;V"z /!RBőؔ2ͷAYhFhdK6\k,RBX -4V%SZۜ)(,*2B e ,g-E0-ʽk "8vea\i&m+SP aw5&8,բ$-VX&T;Qd 䧕B$cR 1To+&,GBQPKkRL- editables-0.2.dist-info/METADATAW]o6}篸pt64`M4Ɇ=ED\$Q#w.)r D=\ByK/(봩zGk|h#5ΫT0[,o;qQM3>2KPf@lZϫx]j?-5FXF_^w Ы~N; gGʬBHq6wJn&NQصMcww6.s2E/:-Kxe)ԇq9I*M&KʑS  u.iߥ,r2%4aRe; A i*JDPkQvFO(&4y"wG=^|6T #Uds.^Vw^cZr灃F?@-pSi  7[UK"Gi׬{6~@\.hy*h_dvipgX/xse&0r|&"et2?<ܲcvfp{%x.C)ÒlPq7{9eH 2ڲ <`r<-LNܘʌj[MdaB+,uȎ_]hδ3%-Jo*XXˡ<(>D 8ҰSfke@4ö`=FNl #57|DG`W~1 68=9JǦz0nB ȤFH"Np@&!Ə|}1^ි^wK PKkRܻ\\editables-0.2.dist-info/WHEEL HM K-*ϳR03rOK-J,/RHJ,./Q0363 /, (-JLR()*M ILR(4KM̫PKkRB9 %editables-0.2.dist-info/top_level.txtKM,ILI-PKkRK_#editables-0.2.dist-info/RECORDuл0gAE"(A.&#0 Ч_wmN͙c1 QY}RJv t$Nn$`3YT)ܜ<@}MV0M!S۟{13y* %#е)#a qU㕵Xt Ql "(K8vxw/ЯvNӵkpSJb{=<֪!=ODn:į`V[p5xtfӸ5 X[!dU{SeU jLqF~\3kź`g`)L n";Q{j`: ~PKVR^veditables/__init__.pyPK7VRE3editables/redirector.pyPKkRqx0#'editables-0.2.dist-info/LICENSE.txtPKkRL- editables-0.2.dist-info/METADATAPKkRܻ\\K editables-0.2.dist-info/WHEELPKkRB9 % editables-0.2.dist-info/top_level.txtPKkRK_#1editables-0.2.dist-info/RECORDPKpdm-2.23.1/tests/fixtures/artifacts/first-2.0.2-py2.py3-none-any.whl000066400000000000000000000123571477560627500246510ustar00rootroot00000000000000PKIgN'(first.pyTmk0_q 1i0_J[7~+%Qs""[Fr~=S0jV9t}e'''Ry1#(~PuMi`k:Yn9,A5kצɄ5Su 0l3{!55Ȇ,ʥ\ [c}:n,)I D\Li{ [1a"g f0s\rC+!@ ֨[pN6{D-}f%Ԇl $;3O^L`:1A(D^vkjs& ,mϲd"תa?f)}vu}{w Tepb>ka Q_\D'lMX*;b wY=$z*eʃjzDS*0boI.w{hM],hTORwң %SvpТm0-Ի }1!%`[^|^Q}<TQ#-%5M&D _R䲈;i-uqvDC}leMCb iiyP.h~ê U)jfWKUРO_K AvqiJiq,m_^|L=Cjrx"?~#oIE3 7pwK9">cQ&RbWPKPgNq#first-2.0.2.dist-info/LICENSE]Q_o08婕Pqo8Ȑfy$G,ʷIu| ]G)!$gU"??PKPgND_A gfirst-2.0.2.dist-info/METADATAYr}߯(" |AYJ(*!h&1ww3I${fq!%Yp%(ݹ e.}Ӧ4';,nRP]VU(mXf*d%WVN &Mzaӹߟi?o&if|]>O?7v(Kq M|S6q64[%LU۾L ƖC~\9= ;>U UT^ƉP<=qeMdJ1֍*\ɵ2Ef0Klke!լ4jVh7ߛŦoG⼮Y!+-I^;ʰ&*7:ͬ,KZ+vJ~v\q++-5*B%A\&j}䥩uF#fҪ'VZ/}rS2kڼwa(|Fp{U(aID;MQ+֦Xʵv$I ]Ur +oB^Scg|OШJb襝) y,0bI)IcvD`^!omVAKШi ~.L =0VǗRtV}«4Mʕ Hmve}dpM]k3ߨ557&$d ATID[j*fAQz_zA2}K%`ʖ_[dr :387S6֝c'޽''J%АB*#=mxuܛs~QI7qz:C+H`UwŃ74Ya ĴPV.kr)7ygլ)UGL0i_E!b{6$D*~bсz;Nw6߸T'.) ͟3U7&De&{xjdwWdBH!h̙b,CLr|Eaa7rJ osWm R: kH?@&"ڱ&qQ&I@Ƃ3ثpmsT/tkr3?TUy;vG-EЌ ȵPC͍t?%fn{LHBاgEj4Dav$ DdMEO1{Q9^\o) BIUL/tMDm);| pxI3\Z.'pƗVАjC_(G--OϮDV51$/5v03lX;*ᐧtPKPgNxMB_nfirst-2.0.2.dist-info/WHEEL HM K-*ϳR03rOK-J,/RHJ,./Q0363 /, (-JLR()*M ILR(4KM̫#DPKPgN*J#first-2.0.2.dist-info/top_level.txtK,*.PKPgNy"first-2.0.2.dist-info/RECORDuлv0g!- a 7A%}#?}_]/=3-8͔I峲rMǵ|x_n!᠌UH_ޞvv<l47i*([#Sg/Zxx/lm< GUW\K2MN^GĸCKM3ciߧ{ؚM̏ $aBBY];9B-Hlt@,=iG9LN<(uRw`8c$H+ʐM! sKc}|VPKIgN'(first.pyPKPgNq#first-2.0.2.dist-info/LICENSEPKPgND_A gfirst-2.0.2.dist-info/METADATAPKPgNxMB_nfirst-2.0.2.dist-info/WHEELPKPgN*J#first-2.0.2.dist-info/top_level.txtPKPgNy"first-2.0.2.dist-info/RECORDPK(pdm-2.23.1/tests/fixtures/artifacts/flit_core-3.6.0-py3-none-any.whl000066400000000000000000001566131477560627500247700ustar00rootroot00000000000000PKlS硪flit_core/__init__.py= 0<ő[PD:(up/&%IR;7˩| LQxυa7`K1PFL k# L*Q[4Һ29&`(TtO:-aXvWQUBJ)Df k[\PI*PKOtnS)&,@ flit_core/buildapi.pyWkF~_1ҁbC714²'ΛH^.G_.RzFZ|3}bq{/P~[5fo,psfXdЩFMz*=)3=U =YUnk[5 _*+N5Kgd=ZpKiaz%5%4X&d=|56VTeþ6BIEpƍ-]bk k;aYXv]1 [ B+:6K6_]QQgV}K S`le@M3`-Z#װ]5eG AO̪PEְ[)J?Ba>S*0T]I\5&NCZzjrDcCqlr\q͋xoZ!\: ?@b- G(\ZtɁjP$jV ([ pj5 (5rȤ&xzFw(Q=魴NhqŎ̶9_A>>jV͞H$Q5e*Y'Y*[MG52ti\ SgXwPPdI%52IQҞ~4d2sz+Ny >$3xb9ǡKDɠt I®h,C"h\|7$.q2( 9Hש?J]^Uו+Хk43;{n7 љR:^mϲO`&hBU&G1wmN3.i(7Wֶ/him7 ׫|l4w7on4 &:zl; 0yyQٯl-8f570o%b:a';Ss =pO̝v_L[񚦁H: rta8U} 1f~8xKeSHCHՃϟS(#g)-J\s"qy>#u!PK YwS6ep4flit_core/common.py[rܸ75 e{?Sve{y*q*%A!#iU^#'ɯ rF^*]ht7HmiEɲ6"V޶Ze&-ӕl&vt5עZTr_+=1]{4r22q+پ?e&IndDM|-j6iLd*2YL~黵 Y7R˲բ]Ka:ͮ5 "mXU`HȕnضaOU2keBYț $K$TmZfޫfwJ # !bI7s$^bխ~+lbE*z'@~&/H zqXNHwL L ڍnA}Jjq<@Ӄfdu%) RsRֳN!6QKXi=t:³J{f(T,N<m (g0h( T 2E9ԣW :  d$O6)ܦx[i&mѪ'kr˪h E.y?C/AhS>5; QÍ o.( {#o`f[7lxm3fImx<5 H["d`ǘ7ch99[ Hh8!Ft>;|U;L6Tt^9gr]VӖg")"@q΅'tҔ庀3a,qlvAYz^g|D3}(tφ ")f;5(!XQa`dCwo&G`$z3}h/;g5c:z[qG^H |ԩF#[ d;"2 (b]cCkCn? kr2N%g32rpSXc|aa}N0f-= r\l͞/LC.R66zjVbքFZFIDN-QBQ1Y+x^H)XRr@TR[::iK;NYXz[pa؋}uYZo:!=hP1E"6sD>^msz73.\-w}3;}QEWS6]?jm66 2çOLL(d8%~ǝ* X {'b.翦KﴹG3w&`Od/^mq*J{%oGHv6A87~׻!oeR:UQFު6[n]2^ \'i.+l6uGZnluB|+f,&8{B\T"C8K!%*At">k{)( puj䀓 98v `2o{wj+56iDanֺ2mkyklQtH} PTsDՁtý+!.۷xԢC_M2\5ն6A^ϲp\_~(/xE?$-%GF)5!GwwKaǑ(TӀOU4W_;3P-4xF}}%.M!z-5kx-os*Fi:-o77#t*T)x QKl/}RW] e9Ia3AфXWfred̖GE]΍߻6p.5c}EAԜxq&c<{ãsxΓ=z'BSjCٙ> (zf_C4lM^{ÑxOTUaSAu-K&z@JaN02ţQnDnIW7KIu!2ү?bpFB^y}B/I^x 1J'?"?;B(0% hҼr˰>$"{6;|فGso\B95<`vCi'%ަyNm{8Φ if?7oo/tT>WhTjtlf`Z%ŻLsZ2 Y`›DdxoL9w7{G:/-R®WDq= d^u*=Zgi>Ot- :ltaஉv`??[55- V*K)m[c{Imecgqլ:}P%2yG<>AJk,|[J3uS,m׳'cXJo^=K . vi#w2ۤ8O9䮻2P ,ۉ/>2l94fUCo׌T"k%09z/c &PKhS5[CVflit_core/config.py,,H %D `@I,vEZV"13===zVmU϶]c|Uzw*EX);(:.ę F?am_\VN`F ;~&.N]UNM]7n]^g a?WYV;e٦ʅ`kmy.s\1l<9l[#uL O;壎/Wwoك1#W\uT|{ǻu62rka~]#Z I(6]ŶrxBuƜho~=OS%$-ySs`7_Lq6 82%Vվ\ vm`&GZ8ޔV".)7g2>g1W!fٯ`ka5ghm7} ݕq=Yn=l ^։6xZ5y!hAhho% uxjvpF^ r18,W;]g,3Sx.8-ߔ ESLe`r tRXY#v#_DygI AES`0/ԫ*!޻]S}G_HrAb͚7MKU>#LJDF3^nYg6,U5M;91F]/TX$ӴX;V2( Lz48ނ,ՐLT֛j_*{W'h9#,V3 ԉ;[2PO@2ECH&2MY>~>3X${^=u;H2xke;w4T2"8>-} kŠkp߿'o/ƒ8 긐μiW Gw__c 5-ou X$/"Ah%.z]96lϼ[veu|m[WTCGu] $j(.$0qa(Gt|[;^gcݑ9%m=߉xंF(5D=ѠԜLAV#2J79`n!x}|{gYެ[Dp1so1]nzh: a ,dyx?P&Wd"+ˤCK$7&s~eC 綌x4Hb:2eleT2'X!.gg2 DCmj?yh s R0 CeioGl ' 2HWtL1ofwAXsvBd|mԒrJ1F=hYBlB#Ι<2!G@qARޠ?yS혫]c1AX7%q ĐtP./a旷o~y O(|_piwTgh,f B4 -:`y<Əҗa^³3q&һCC#':%Ӡ0ijEa ̓~9-֧iK)~aϕV77g wJ&f˾b= E^)a  n_UL])N.QYAwVsG1]FzHZfnA5uNߦN8MEK6ypWxr$S# ChqX'Y/ _ t&TYh`>du\ Pj<3_a$"c&4<$މqf4Y1@ci/msww- 7oG#/K?༉vDN߃,Ss=$ss(=U՜A3W퐶M9f.x<2N7e0a=dPzô@?h!+zχ`(`Πcjd_e`FO3 hiS_;oWw%PqE4F0㋰_p1 پO _ZǪ91׀88Zex|M2jrFq DFiԣ7DfHV 3JCg9'e:/;ʡDR.-OAΐqQeO iA"9?wqN_-I7 cx"EBULs), l4%# Ch$HᲛ' ?>2a>)\HSWBo:gX $&!#xa=\%v;~ʩ+Xnp*UKl@1xpboJ+iPSId;ss˙[ p!N׿ntj-Hdd| TTP JC26q"{==%`7pm/eA 74Hcҍ<.Y3$5, %gr-l:P#-w+r-dP᪟T ' q%pj!|1GX'~En-DdZ()}kbw~ӹyh8 N/ͥ$Xi5`$XN+-~jyM)ʕ&]3behgc; 3qȫFd4BiƟG!ġIMk~\RsϓSȀ"9٬L"Md=WnWZGmdß5f9e P3;cr-VƻQ'5Ew*zruI% ^HVlb/!h*'9?=cv9QՆJ J>xH5Sœ`gL>x(.\ngQS 1 dQKj<gkbֆ'Wn j< :فّO'CG Ԯpi$,/~`BեK@b*/ ݕ9H'8.74,&$xҖKqrf ;: 9#|W('siϣm)^J.nax=7*1yxd}eGhzFtYdp)u4ϨP ^":qzұ-ªʱ>䛘ZN>W䜺vM]=1sp9:R ˰x(=Qam!~lJIU=T²BJHJhG8*ǒa$ŴH cskus * ^2X,8`d׎oT.ӚA{uDz?)Ti3g} -o˨~utZ޴Wѐrc2Ix䟎;)S}!? QXAhʹBUiD϶9;.lm4P ,³VCHDg-0< G ߩ0 Z h^M.xmXc*֚M]o&논x8̼qy7H;3:'!}w*192iс~G!_2,>+6χkUg0еzit~fPsԩtXD9):V w]eh_"{KZNV 8ᙸn1IyMu5O@;U~wt X礅x'@]zDdu5 gt?F3TVF#8u 3jnN4ONJpy'{}3*o3!0OӇ=Qxйd#rR VOjS,3|acu$ q$^2'O{V}#sF PJGAB/cp3P y ܜS<Ėv\6e'Y+h3'z#4#H[Z1DxBBGz>~F͏ثa-w L;yGQoޤqe1t2=o”W4:焝WJ6|O}^J' z/ t-/qrf/"6oU[-s6{uRJV#9>rԊ-Uo@{W zi9^2K=(gS x 9=ܟڻ褮(K_Qթx*SaW\ PK8vS"3flit_core/sdist.pymoܶ * =дÖzNg(cITI^sDJ4vl|ZmY &/06]\'>ݟal徹V4 )Դw*X$n|u @(h{DpIj*k"]' D!/P0 ,L+lZEH2@H5_Nmފeg+s"ӴÒ[*/5тtI'$# RTo[ Sr4|C (P6B4f''mbd`JO$?|P2J^^hxM6rP8s;9JqgZ6$IFN'*f SH:XtҪQZkxO֜n,9LegIJJG dF=mDEDMPݣ߱GP4@4R*.0X\EX[G&"w [sQpY|NGY v\AQmC991R'̨@e1k#\&UG N 9/IV/ Lأ{:˝6 8.f~gsv+lۤ!4rCȷX2jd.D3!1WHXy$N~ P<`dzeMDbrJbuLP!B(+AQ;o+ v}qM^c {Ԭn 0S[z߶:ֆÎ;$5=-lcj_/P-[}cMʺ1'o X6< ZaoLXC1bOMbs^,XEbvf5rcm70R`4Pؤ1M_1„?hM=欎ӘtF -pl۩&8,Fpy%id-Y@֒Uqu FyT+:l=„yHx>9s#~@rl qrYl9XD6 RGu72}C@HOAO*zYhF $h7$Tk v ƊFGygʷڠvMX4p+cu]A}~AND3!XQ1.%I#A'+*+PqmK]aPm'"D#zc')lʡ_3Dy&߀ۃO!Cko}|s7UYg.ppJz!K)i1ת%h6.Pp@hX Q:>*w0G)iy8눡Qȿol{sq8es-$x]߱nAw[Z5npTFKWٗL,-«hվ%wcƃߪ -n#iK!+6ӑlxׯSzm606ZW044͊%$ j1I@9 ˒jZuN̶SYbm̫v_O Y3y>Ҟ .4ϹO:w%rd^?A!iȽ#oxnеWX?Kazfp&o4Jc~0ShX}]{f%$nj ).Mp _?^}}dsOXZa P{HٜD 5WyV4JЧ}':}q۫PKOtnSv"Yflit_core/versionno.pyWQsF~ׯ^ xeBJ ;.K`$sj@mt .Savߟ 82E v025f ~SśL^y=C!|U5/&8R[,` ._’'VRl €]>Mzc!Os~F"&sZXAWlW48]mB2^n !sZIji0s.wІ3{p7dlX1N} 羃}Õ}PMuڞ v6ahgg&1^&`0aG51jks;Ok&P`?%oW΂l4X.l@jhh̼bD;{wcM&ӡ@)ou\{f4"Ɖ1ؿHrʪg2G%6ֺF_wg4w[Cgܷqp4޿`x*js6Їo)hCbScSvgTG26pXٙ*)}  8R`U, -5>1~)L #ٹg1XBsEv qnjW]5cRLX5*t*fz{1[jw6钛dq5Iݥi罺q]~/?D_GP- %OxjV~Y!k: :2/wPkKO_ͧ\e\h5|kdG+o]nciF*- ) u6焑= .k]oticC*<>$ӏ(⻿0>Ƕ >oWIu}=!DArV'iv?5/.E,P*+yG>v]UyƓwq ar*eKZ2(E2O|5D'zG^U۹k[C/3&\!USZJH !"MSGڔ q\=/Κ1tK[esQdˋ}æ15\aJe?MEO4UI)cYq]8лumBW~[@@=v^o"WϤ2( -7l2~=ϠlCP͏]ʠ7 ZiT&qjQ-VPTOj."h$fe<5Wؘk)6PJ6'@\r&nKYBSj'0P-&qד*!'e̳$.H[ɩ;PTW"mp8KqjD̑)/PSPVIfPޘF9HwæQ|~~nq+$؃,NfHWXG1-UW^-UD\Ij0FP7HAhS2{?Ӱe[ІE_%EM YqR)%ɩQ%ІP\)BaD&߸z+[v-VS&J'QQ^?Hϟ\~+2 \[6ΩeNvXAB,2ߒs6紌@ inO2,De!6V.PgR`=omXS7-X m/jb#-Z#߼+УpbaAQz/Uurն.u~ulxQx`h[@\Ttr29!sd mSv#Ye_O; pOC\̙/#1IyjBӉ[WPbV&P{ /2yJWA>@fgoHYQmyZ% q]LOnC[TZ^dѦ2MC goiUUeETVBND/đ 40S 6QQnO=OEFU|m.@ݍttdHQ`5+{f[1U讁3 3:1 6A 9Ag!fύߪ̅2#E8. t{Ղ̡ Zb $Vfh].nR*yWb`z%,.9f 儖K~tX^?h C b>t<6y*^5e}ZV+k'a'/SuSլ[Rjv=#S,%\~&@\FNZ_t~*Kߣ,)0d4>*SuADGYIO qBI$nҸA\eAꢛEa P~ؼ]'Yyt$0}l!{:#!Î8Vn.kr"f$8z5yx-4'Z*9k W@H؃i/^>=4P캏T~ ļt%X UO sӉ'3yDh%#8X93xP3N//_e`B 8דk]vkhnJSiǃs7|"VӰs@M,` 9TE` QO]&d s@H'WqCnE8 B ycNQxܻA_3]a ;|s-t% OZB& ʧzP<`t/,[4VF:\i 0>9nd ٴ,r!H\ʋ-gs#Z l!Ā?6rv@Sǡ*wPŻ:/TR8TNr0inm r! I[TrXU]H z6.bxoPBgQg+[s%̜89=a( ZO{xḅGwUFE+"4g^;0E|I"-zj<;n>F&RDbҏtP I^Yjf~jTx5xPCW|N%C{qs w,YcuW>'?VdPa7Y*CPWt}Cʑ@7 Zt bc%#By'}& -  ~ z˗A0/^.``UۀdYTP hWY\hC' 8ǐ. k gp/.ƾL7OΟ<٫N~v"[W_kX֝~E:~zWyw(56;H`-z9ʴRب{x_q x{Z,j}_ ,ڶmTACXEёwyi#p!:0JHlX#D[7FOQx T۞DF|:H)Jɋv8J:NWǩ/SGyhi%A._:rlH>$ AоSHR۹< Ry"caV@7,4B&vm=r UY#9)kgǕF F҃0t$]'<"6'[ / Nbvq5=W+pdiYV. wkX|.6VRxn%6v,e>N_J|t<d3E=/-v o{?߯foR;ϋvGK#/a8©=N*dV KOPKOtnS@#2^ flit_core/tests/test_buildapi.pyWk0~_!2c2XOE(9V%M4M9q~,I۵BdNvLrRjk3b g_2+dж(\*)&>ރC|0or 4 Tr4nƻWB-p2SVz^Զσq81J€aypy:dI4O^ @=Fup۸Φ m>/ɶc̣[,qZ(dsW(-h)>) os?cW6rV˅fbD{KAAa&蔞[p^Mkgsat1)2o@ iI%\\pC0`?$i^r/ )߸y P R6T(&<O |HN+<4m-WFy`G bE5H >wb KU(G*g5cva!:ETc޲}%JMSP Ȇ_FU &W]Z_j`_ǣlt}_u)*YpҖjszLkb-FkvO r$#ԐQNdi7(t{E-$#m}fdJ"DxHEh\-C26)O>RrG轰R,^{K@#s2R2Re1Eh2b$s^yfE$Ixf51*HeBȷl)گC%-IiH.IA]0b|w) $KS8j~.GX-݁DF l`&:hf-oE) Lpޥ7{;9i$pmkv֦x]OCrPF~?$KƮѤǑ.{hwYVu~ᰙ^FןK&O4X-޾C`2W}RZ!!wb:\*Uv:Fo~]WV@d=M[p\4К"ݗUr_"Z]~vϷcD=Oe]\a J!\.ChhU|e7Av_c4PAYMy q uƇ`|GlA]YS~*M_?w{!]GhI%8UA*犗L犕E }c}Nfxr$8{cHr#<ܮݚ䀨K5a.=)!~kn/PgϦe:Eg*%G/.ZM)ȝ!8~,V\{K 1J\8B̨ \>[|Zehɛ`lˊQk_Z Mjmi^u[p{4z4@*<6#0Ə }GkAv.\` !Jnb#6}0o`vrwFRh{\Xl'ɍ 'י[Sosޤ8գTE)]#~ere "v Fm# $PKUrSflit_core/tests/test_config.pyXmo6_A>PbyuzѢ C}p h$H*ߺ?pfHPoج" 33ʐLn6،Jdf_~.wk3rrL(7,b3iQ*YAEƣhLR&3f /"PR`2A(A8$2:'_hl'tI(/_o | 'ieAFYxBzdy/ug|krVwAhOI j#nX [ҭyT +bkL96l8Ld>B)bւ ,k m-\J%y.X?ծ߾:,VՊ'*XX|lS%J#dzB^G3{*m =',V'Jpb3ZTB04ђAY fgdO9%_B>iVa7֣Oۅ`#S>n^'Gw6u9zRIk<ԅ&u/ QE+@{L$4O'Sd("ǹiTd$iM}6 1f̸q5&+Vgsȥz|Kp/vi>NyZnhWoV6L\ h=%1jܱ0Jé(;ʁ:FVZ%Yڝa<Mḵ^?qT>KC Y p'pa^ dVI&0 y:'DGX(22;~zNM,qw~vG}|5u|}{0a'\\Cm U!Uhbl!H87Qd/n^n/zp:-1X7hv[ިO*84d H*;$͵F)3waސX{~OB0нׅMm{%jrѫrhKgU/"LeSf9xC ^CI}8[{Zx`azjp?}Fe4.H[n?)ghú1,KnrhOS7;A|~0[U<°=(Y1SIJDӎcj pmZvVyVX {ښw9U\xz $NsetH28{+q$? hWA)g^>ew0a\|Ttw2;L& %~E޵t4* ^*aֽAªBۭ&hڢ_Vdjpo ¦N\g@7NP8oswhz];Pg@7|vϦ>cA}Fkb1}Ybv4}ŭ,]=x_PKOtnSm|:flit_core/tests/test_sdist.pyTn0+@ H zKn=@>6_ Hܯ/IIMD=3#JCpch OjztQxEWC۹wz€F\"<UHb,3h$ٹ3(2^C=֖O&) `}c|a(Q&eU,_؃q(0hwLeixn{ȳ˚xqQd-z)5I;rFf&YV]r-^ĦM|Vcs\>^nO>=ttT4o06![tf\ԗVWZן2}yc5kaM{4(+SՓGLӗ(['vh#bymM?m)&4)^O)ПFx vs&LWk)Uʅ7o7lv؃r?ogξ%rq'4늳X:є\LoZM_whk޻.H=7 8u<=eux&/ϝPKOtnSIf;j!flit_core/tests/test_versionno.py=o w~Q;tҭSJ BL=%R=^@Җ?%ժm'lU+ V}$#.D %R]ptyUL@KCO.]?31d]<ɚv4Dβ}q$UHcrF p}-NBylr ӕQlZ2X+yhXp[.`H tjAZ3߀9jn?O >Gi,C 8-39+ؖ=M|Ny7W gP7Hj;帯s#qU'a}`KiCox{Ӥ\%=/PKVtSE3qsflit_core/tests/test_wheel.pyRIk0W~m8m٬VqLy*򉸏 nH+M;d٘;e$D!V(aXwnܸ~cߐ0ܳ# I<ӝ5P4THx:L@$\ ЭpmϤ~fə1OB/Dۡ{z~d 0]Vo3u k@UPKOtnS i#flit_core/tests/samples/extras.tomlmAK0%gxW,R4tb2ߛ"x̼~&."g5<:䃥^bsƞaףWBLt6a3OSvP3t+K1i]63=!yԖ|0F #nOto}X-])_}5*Xj1$^CR9+SPKOtnSgߪ[1ڲ u }PMsi(]IPKOtnSܤ (flit_core/tests/samples/module1-pkg.tomlm1k@ vZ($өK2Sd[EOAsHl{z^7dS!N7b>Ο#L$F z2刷;޼lhNQZb EލBnzl6[ߛB6!M"2͓3aV#Y3Kvj&/;1 cf\ JL7&7uPKOtnS¶^,*"flit_core/tests/samples/module1.pySRRrH-IUO)IURR⊏/K-*ϋWUP73TPKVtS+㓚"flit_core/tests/samples/module2.py= 0~K#TB$1m$dnN^7J)&R(88e"n@ 8 ^":f$tkϾPܺ;KE)sB)$lH2}b;;3$=W(\ kkO^PKOtnSyKP-flit_core/tests/samples/moduleunimportable.pyRRRrT(N-IU(-/*ILrrSJsR@ 3S=Y>We#)Ai (vbG<-k.Y95R`7+5ـŔM%B+ᦟd(X9UVxd-qL?pT\y󩮪PKOtnSyO7)flit_core/tests/samples/requires-dev.tomlUPN0 +pN*HH 1q2Ur%q{MCfg=+碔:rCd1Du>b@$ 5:gս5XuAa/=7QB7iDQף5$r c4#T{?Hj N e݁&v$5iJyy,T~4tZeN_p\}PKOtnS5dL-flit_core/tests/samples/requires-envmark.tomlM 0 }.C/28t.`6|{ۉbNɟnH:"EGQSWz!:&r(hӘdtk%LbKy/te >\G] Kܖcy5 ٦A_7òr2*"Gٰu8lr~&ٶmxPKOtnSFO+3flit_core/tests/samples/requires-extra-envmark.tomlmPMO0 WD' *Dʼn8vՔb-i I;7~ǂ~  d^<9Fo`;[q.ꞺUJ. }"}qEh_&G6墄t19#2)&#m7͟ y"Lq'ޏ3Yia2oAeq=tn7PmAA?PKOtnS.flit_core/tests/samples/requires-requests.tomlU? 0|= `qrc)W{jr6- n~ݫۄW|#še#DDNg=-Fx꓃eNnN x CQQ*]nuzJ15zld<ꎼ!vgFԀ^!ϑDKPKOtnS6flit_core/tests/samples/conflicting_modules/module1.pyPKOtnS8}:flit_core/tests/samples/conflicting_modules/pyproject.toml=0 D|E=Xnk{Hlwξaj__UGS٨`';[&u1]`TX@a4,K鹯:8MTE&zG=*g`̢ݝI7XgIu;Ƹ6Y8V*MPKOtnS:flit_core/tests/samples/conflicting_modules/src/module1.pyPKOtnSÛcs6flit_core/tests/samples/constructed_version/module1.pyM1 0{bʆ`HRBL,%oK3Kֆ[SCBXZb0v5i^\7xS;'!G38qZ+/;c8PKOtnS4Ⱥ :flit_core/tests/samples/constructed_version/pyproject.tomlMn@ D՞Ɇ CU~9K,v{Ad͛5u|Bn#`jYϝD2߇kox[4V[ZERQ }3ϭIY:q4Giy|dQOKW *&,/Cj]'9+01Ԫ\v <PKOtnSͧ;'%,flit_core/tests/samples/inclusion/module1.pySRRr/R(I-.)VRR⊏/K-*ϋWUP73TPKOtnSr0flit_core/tests/samples/inclusion/pyproject.tomlUA н. ]N1:BzFC ?J-ѪJp&dmpg\t7]MZIRb5@b TyAy'GG@ܢ!"'1u>/XE o#q=ddmPKOtnSδYd1flit_core/tests/samples/inclusion/LICENSES/README%1 0 SK .zFlx{7E4# paCMsWc~ޛ:@14~,-ɩ^PKOtnSx5!"20.flit_core/tests/samples/inclusion/doc/test.rst+N,.)V(/IQK)MIU(,V(NMU(,(JM.+PKOtnSɟ 20.flit_core/tests/samples/inclusion/doc/test.txt+N,.)V(/IQHH)MIU(,V(NMU(,(JM.+PKOtnSx5!"205flit_core/tests/samples/inclusion/doc/subdir/test.txt+N,.)V(/IQK)MIU(,V(NMU(,(JM.+PKUrS}-flit_core/tests/samples/ns1-pkg/EG_README.rst=1 {^q/>򉸏 nH+M;d٘;e$D!V(aXwnܸ~cߐ0ܳ#C&PKOtnSh/'flit_core/tests/samples/package1/foo.pyKTU0PKOtnS޷%#0flit_core/tests/samples/package1/data_dir/foo.shSVO/JMWPrH-IUHI,ITHIUPKOtnS3flit_core/tests/samples/package1/subpkg/__init__.pyPKOtnS>=flit_core/tests/samples/package1/subpkg/sp_data_dir/test.jsonVJH-IUR()*MPKOtnS4flit_core/tests/samples/package1/subpkg2/__init__.pyPKOtnS&$&flit_core/tests/samples/pep517/LICENSE ,VHIU(/IQHJUHLIIMQ(W(HM)PKOtnS '%)flit_core/tests/samples/pep517/README.rst ,VH+I+VHT;-O7893S!9#(1$ PKOtnS¶^,*)flit_core/tests/samples/pep517/module1.pySRRrH-IUO)IURR⊏/K-*ϋWUP73TPKOtnSla?|-flit_core/tests/samples/pep517/pyproject.tomlUn E|bc%ꢊe7Ҳ,lOQCa/#;iv-2VL ʃl"wχAbmf<_G/hilFæd̬&D D=?gЖ(2X9,M~_)z WG Lqiθ~t|}{?YY *"l.wz6+iH6s{ 3'9.^Ő-flit_core/tests/samples/pep621/pyproject.tomluRN1+Fp XEHWeC2B?, QH>LwuuWu/VzYߝ j֤پ؛ Tp`#J3J5s kQwv9]j0qoBu2O*h9a,&m3%]_V6Rp\8F96o/s*za{dg< yCL qKF?-@JyJ2V$t\U0> zX` J;<\2%S&-MNw XlˇbP?LjP΢ eARiUQ+3,kFty"T/V<6t}NJ:b+6>[<c"v.1H)a3F yqQ@9zRߑ Qb[SY|<;݇*&.f㆏a*2s{0VTǚ W>>gt^ۏW(-{ %1tp櫁cT" u{lVEW-=:t2ܴtڍѦAaٶRc(Nw?<~eI͹}f6EPKhSflit_core/vendor/READMEe͎0 z-~[ @=D* 5'۟Dr ]:1*;8m4crZ)2:q՜8R6Φ=Or,|E>?=|{ՂV}/Q0JPޘUf,kKdnNT0sPoz- R[*MR4S ۬uR IC(vK\Y!tpd]-_AY k4بYq(Jb)(IaFG^5)p#_,yLBz_ؠvMmܛ5m}*kqNkqS$ik4ӜKuf6x.| (1bg }3 kugRF^[4ő/PKhS*'0T!flit_core/vendor/tomli/_parser.pyks۸~ Bbi{3:Nvzmm gԑu߻A/7Lb\ hHE%7iY9čbw>gP p9L=>JqcQXxC' {4r?-(Mct#Y3^u|ΆbqN>WN||?<?A"'Ahk4 bd9%+fAAqS`h4.Nr^Y֋\6M3(aY=eu;WGŌg,2s@= 儳_iqqbYq.'EfA29`Z(A%H-PMI~gtr2|t_G;<;49w4qհ_`RNFí\%B8] >- U[}qxzpscFʐctr Q`K<"?U^KOΎxtt1B9$nӅQxE;`*= Ϗ?ã!* ף'?7oxfBC GUc~q)hա2u, ˢ,Q+P#Ai3DN'91 fYK8 x"H3GSI.,Y. ?SAjқ.Z>O1 h] 0Fk AF1`c̦QY:G?jq`ťE8P?!EH(,4xYT5_z2y #/AWxqp!pܥmb}e_@Oz4ME:azl?"ex*aw=۱dbOB~^{צ6r pn4`W 1ܘ M 򔤿vv,^2Y}4Ysӣ1jz e"#fqg'|,OqLkxMI$*l4 Q`OOHQo{D?-Ų﹐MsŘ;/<dN( M8oALD0тV91<]f!ghkْWmb``-F3W.Vuv/%Ȗ1+>,лA@tYj:7a< F-d~+L稤[FJ(߅QN`fp(<!p;WpL -%e  M #]cS,؉aB!1F" /Aꞌdj?cV]A9 5Aq_]lk-yZ#h;i&| ~U<Fq-HVREwYQ䰠$B=PJא<^#輆0J-~;բLSo<ЎyzUrXpO|H\4\(y `ʸUw{=qK!5齃lw6(iZ,$&J)w50 rG+QI)e*_a s}iXe%$ #$+䇳OWaU@4'ffPԎ9X6zW<Ի'S(3e)K%f9DacR??(:_07L|?J{9`RZ܇hJ#T1Ɲ ,{J)k`PY!Wp!_vvhtK5@ fU99sy{}$8?E!Qn%J!Ͻ>J11F_(IJPȷTg!Pa b%dwܯߑz>o@p)~u^JF =2a <ƒP3pmPdlq5U%qM SP1w`f.2 ku9vRVp fbl G5>fPeQ@m\>HE-YuH}4Cg9u `jn͒tl c9ژ4 r 7vjxVL/Ϣ s"WK1t\>Gm5 N۽F~ }=;@S#`p) |忤h5d$kue(UmUK?DcVX?H<$]W풚e˕uF[o];[$[r)U9KX7k=L]GRI+O]  LZ9,G̠$[.cZ5_A k\i&lt CZRK!_棥st6[mbj1u FM+}, njx7z6.&ygX+jUM9{`rP%Gzdz,*8 [#"sH"YϹvtlH/.Le3-?T T?m%i;!T n]J|: IE4*-,o9vtDdi|(E15ۮ_1Pr A"{ Y4i+eN/jQ"[khOƝD][ԒG큳}>p֫c)*,̀IVj[Ѷ-EY/&ቧsVRY[C&{SFmfTJRHMֽ Pſ[<qTsB+kYT\icD%s.^tSA[+U תúlÈUV* Jƍm5%U0rz[q;q1%+}m<*Ep1ݦnsM{^X֐5V~Qm]Fug?D@ <%Kw2Zwk#h/x3jMpy*W;Z*m?5j LjK}6VU{"NMsa=cH b;=$|fSwmtl˘P]!3~r=ǁvX 1Ti:V$ UtQqS<׭b_Yonp&?'͎yxgLPvGTⱞn6mSbL=Cqlgi6,_'KCu㗗Q~5v E1cCi*U8Ͼ~myY12~RX'b*t1`k]hvAk;xĽvA6XjjRТ(Z.ܫT@fb8ªVeP"Tw}1/8.L^'e#Txʮ@qmh@IZj<=WtrMߤq##~'F=D),3Eh6@8E>e]$,rNt&<=_PLaf $Z{ [,_L0Ve̺͉߳jsipxibB&Vibo/1||%nu{M&l2MS1\XKZ\8][Ady9b7jrA`Z4VGhR0FkAJyZ|靌}'jKUO "ڰtڛF<2G'Q7r0ԛmH N6"KnCKe0=Jڇ_,`Xt!$t5)Nz !qiv B_> LO{{X5y1+Vԍ׌LL~nuŷYgʺ )`TR*^>!PBޮ֦ڨFT_RoWTv:uO=:I̓NC@z o2'9$ Gz3NU٨EM"A-1 cNӓtȃޣRa+ V{t ѵOoNPaj;*e[XM/lkFX.DԆw"g82<9ì>[!%Sl&8= ўK[fF$ZA*^m NC7Ԡx(!ǻw{?0zvPKhSt/ flit_core/vendor/tomli/_re.pyVo6~_A(/b* R50K;4Ea(2ekEqIQt<d/SAEljtRKZT}Q MT`7IfkZZSE2jW7[Q*-Ҋ^s%5XT tʃ?~x~w %w\/E0w/$'<˳)p<5<%s !pJmEIme!>J+c1Ȓ`NNAR=chwj`EL (zsQt /itE9ަ\@6_~%rNmQ#7-8i>X=}BUB[;I^z|_ܾp`t$ϰWwc5YP; P8| +NX1=`}LKNRH!9ޤ"['%m% rhG:~Pޱ`Xzfp=ަE =-5m-,VLqNk("?ҲXtk(LcdtGSԂ;d*n,XfjUTcYMRY=훲IHӗŪ ǻ B[qlk 4@ 6PvC;oA..j3T4RӽuZSiBi-ډFʿZol!2~Ӳ79LS"}ŰҖ//1ݪ׹Jaj\Y]P}2y +f4C\3oݽc]LBA҃cJtx˩:\L:s "g3l4n0Lt1O2zNmiKqAXISW̰T̹1v?(\8`1lYPKhS+)flit_core/vendor/tomli/py.typedSVM,N-RHIUH/Rp P053PKhS]Xuw0.flit_core/vendor/tomli-1.2.3.dist-info/LICENSE]Rn0+9%9FKtDT(:D[leN0 ps!a,v<}}`!t[7Л_85c0]phf:f|<.}hh#4"#wpi&4޻6kC`>Bҙf`v꽵bCa-aD`v;h} 3=: \g5#,AEO%Ɉ||qx3 ,^~[fH OC EDxƞͷoK` 2b^NHO]) Nq)&gI`@X.2Yj-;4g;=?fyi:^C9͹ R<5atz~;rZgA}+}9Y~ Lə ӔNlV3sc.%˻Awػ318wBr$yfl2'_DfM>Q\7Hqu G?|:fөccB3IqSg/_ FLyJF_+Esonir .:\3 h"C+]vJaDz߼+Z+w G#(>+K=oܑy$!k^^g6:CR|#·? 9fW6H"z.xkBaΕêh;jɭ*=x1M{"$)siS?|(!.c6JTiXHMC{&E :)gCK4|wi!2c|6: cٶ;$&A4?i)Jl{anB]( .'vwT$:49aC" @ͮv 6W4L#9ld&2,< wC1Hc|Ɋp1_-xiP:% < .B@a5_c_a9Wq\oBTl31 j 7>گ+g$YT!jv\.XZmMxfPkT|ƕ,2~Qڛg{'!R [Ƨ[OtBheSXɖBdȺW$.C;ѧe,ɒ9B"ƱU/uY1jH[oVBI^.H581L4hxC`7g'R*ԣeCjŇ%TJin ^sY@-뭊oC}N8dܷY˰—oU*4UB')_<n1[ȷMiA5iY oִm%s#KK] 7Q/D&"#Ml˰F11@ ' ]f!/#?WJ*K7Gd[|W 9d ,J‚Ԝpkk$&(y9Io3NG,_T(q$ dN' c90z.Kt$ӄC5-)2@YKΧꨴ3˝`^}ouձܔ6P-P 4`Ak?toM*7VkEoEaF&=61aPq1I2tzHNjɀa&d*4@o+jEsi݄gI}lE%i"ٝ 2}.8I l ()c$>Psx̠\%&e{m}.C]&D9JHe+1+k Z{WڌPӼ1$e<\ iAo"XlJ(M1}/EqN{ w-e cYޡcM|v34R@n֚84NnwӼ8 NE*MZgT-%d&5һNE$)t;[8+ԢjiL+ѤHD{i;5d^j1ma_vFJܧ(UF1dֆ&IvR7ltD *^ -u&P<[3ZVKฌ߶a(&K|TNŶ?ͮ㪊6ʂ UW)n)Kt6&-:mJkf#So}mhff)3\5GCTJ҈koS3|eX¾d^(!y8(۔R +#valuMg*hQsM'u'i)ǚt5|lRf/BhYɿnn'NG2*MZz-a)#B}]w/_rN5?p:?PǛ@M/Fsl:u^$;4ZMN썯~95!"(=i 06CjkD9F>ȌDR[3 NWmw9[4g9d'q:T*\ݏ:&g`qYC{Ka&s͵*1++Z"tvn5!B6yT{RWS3c\ lFg~§bx,蠞.I.-30!HŽD2I37IP7N a Q"p/mus]r8HU-B8=wF4 P>`gxqϏ{=x{#ѣDg2[^&/߽xWv}Av*ڑq-$L &$ \QY''7?'0VEU#r\<+:CC;F/˫LZţԤa$2- [cC6(F~RDS^³e[2 !RI+coP|MI(xpKTfa8Y$*Lsw7E!;Ď՚䶆 8&_L`Q dڀ_? ҇Ef{*p( `r Qr~a S3?{U[@ɳ$;g:B_)Gf>"z܆'*V!xPr <~2֍L0 ~ㇻcd㴐B\gbXb]z*RR)ׯemk_/zvߺ $x#Uȵ€Uzeۄjҕ OLٻAmV.5K <%Egtĝ}(`i|"jMGgľtFؼ⠺:걡" @lf~;#m { fXe=[x7;+"KsOXHBuLy sA"s@OuVn xKu$:LJSv;Hbh{ŨKV 6ZmS/hGº)tagP 1M!nnobӔw$6^6'wTM4Ӽ ˴*(-f+ 6Ȋ=FlibMXaA"d<\taв:.Q_` \ɢzܲ"&_:!axc +XyAQ c="!  ۲N#0.(h`zxtT&0"lUYaD׸\=zZCST 8f3X }oD%r];-X (!?*ȯ}#yҲV$O/|sܓiurn[A:o T~?t9]*`.ŘvXEa?c#pg#ez\ K"-ࢠ TkmAe{8n: k?s7^SL#~h%#) ~M]s (II[ Bԅv@ 6te@QUCۜRc[顨ܫQjMW |6Q.HJI k&8~[@yE:zU7aK[Ve4,--7h<-ywOﴃB}㼟ϼz4AvRx. x`-AL/=Bמo<]`V>~`$M<өW&znEh}O4.GQ4&]sۋQCA?m[}eP1Ȓe;`vK~qoX/Av~]oV~_^3# |l]w%+OF Q7g _'77>8U,[ᆵ5~R-')hMpxJ^RpF!78(w >[StJ\1I"3lҶ",ƹQ`w.ɤ AA_qbz;nChIځuN|gԎmGlz}LOɍaؤg5d1sC-TV4WЧO(=C᳣] +4xsEϢ$gVR`*.$<K T7[lq{Y.vtuosDƟqaI5[{F?*b- dIzyJyy}!UT.#{ٚ]4^wVͨCd7Ȼv[-H*#&MVhw:q qىԾi's/yUn(|)/̱],s 坄J:&hk}e< `˗73rNvF$j ]47(YgEm6PKlS硪flit_core/__init__.pyPKOtnS)&,@ flit_core/buildapi.pyPK YwS6ep4Kflit_core/common.pyPKhS5[CVflit_core/config.pyPK8vS"3,flit_core/sdist.pyPKOtnSv"Y&6flit_core/versionno.pyPKOtnSQ- ==flit_core/wheel.pyPKOtnSHflit_core/tests/__init__.pyPKhS %Iflit_core/tests/test_build_thyself.pyPKOtnS@#2^ CKflit_core/tests/test_buildapi.pyPKUrS~UNflit_core/tests/test_common.pyPKUrSTflit_core/tests/test_config.pyPKOtnSm|:]flit_core/tests/test_sdist.pyPKOtnSIf;j!T_flit_core/tests/test_versionno.pyPKVtSE3qs`flit_core/tests/test_wheel.pyPKOtnS}%bflit_core/tests/samples/EG_README.rstPKOtnSK0cflit_core/tests/samples/bad-description-ext.tomlPKOtnS@10dflit_core/tests/samples/extras-dev-conflict.tomlPKOtnS i#eflit_core/tests/samples/extras.tomlPKOtnSg=~flit_core/tests/samples/package1/subpkg/sp_data_dir/test.jsonPKOtnS4Gflit_core/tests/samples/package1/subpkg2/__init__.pyPKOtnS&$&flit_core/tests/samples/pep517/LICENSEPKOtnS '%)flit_core/tests/samples/pep517/README.rstPKOtnS¶^,*)sflit_core/tests/samples/pep517/module1.pyPKOtnSla?|-flit_core/tests/samples/pep517/pyproject.tomlPKOtnS&$&$flit_core/tests/samples/pep621/LICENSEPKOtnS '%)flit_core/tests/samples/pep621/README.rstPKOtnS¶^,**flit_core/tests/samples/pep621/module1a.pyPKOtnS|>^Ő-pflit_core/tests/samples/pep621/pyproject.tomlPKOtnS '%3Kflit_core/tests/samples/pep621_nodynamic/README.rstPKOtnS3Åflit_core/tests/samples/pep621_nodynamic/module1.pyPKOtnSA97flit_core/tests/samples/pep621_nodynamic/pyproject.tomlPKhSflit_core/vendor/READMEPKhS։flit_core/vendor/__init__.pyPKhS3߆&"flit_core/vendor/tomli/__init__.pyPKhS*'0T!!flit_core/vendor/tomli/_parser.pyPKhSt/  flit_core/vendor/tomli/_re.pyPKhSSQk~ yflit_core/vendor/tomli/_types.pyPKhS+)"flit_core/vendor/tomli/py.typedPKhS]Xuw0.{flit_core/vendor/tomli-1.2.3.dist-info/LICENSEPKhS;υt #/>flit_core/vendor/tomli-1.2.3.dist-info/METADATAPK!HlRQflit_core-3.6.0.dist-info/WHEELPK!H1˜"flit_core-3.6.0.dist-info/METADATAPK!H  flit_core-3.6.0.dist-info/RECORDPKNN7>pdm-2.23.1/tests/fixtures/artifacts/future_fstrings-1.2.0-py2.py3-none-any.whl000066400000000000000000000137721477560627500267540ustar00rootroot00000000000000PKN!,^~aaaaa_future_fstrings.pth-/*Q(,VHHMP/)SL\ZiIiQj|ZqIQf^zqL^jErjA'Xڵ( $S @ӪWY\ZPKN ql F future_fstrings.pyY[s۸~ׯ@fӗ4{I'өO˅(Pše= )FDՖdYԍdYFvWɚХʦfydybYk&i&^wT*枑*W xbR{`2i"Hay+G@1J6/R䪺er/j|Bb,V,fH -~Tc2\zt+`&e(m89ðA+gXۚzFJv0wRYEլ7垜M%LUB,0Zî~ҎȐ' _ɆMÆ H Xy&7L!^($#hbAއSvwv7B \o.Tg?6JfXitA%?ʼn(ń+"*j%Q&3Jn*Ves~cK&z:A/px8 jVlWc׃wM"Pti%'߮d\. o3 &)~PAmkt `bQ%F)UFYe!+ʽcss?`_F/i=I E$9\eb%oM4 $(;mlo$G>9nÇcp`n/ֱhTqY^M\ҵw w o^> Ihs w5t~ JNC0::7(˓Ko_LXFxb(2@7(Pl8nH{K>a #oPvxRE~[@D=-9U{&Q WAؘ44yԕFrA1`d,=+–-c0mDV@xZhntwzb~D9]<3`/? /Ƀ"sB*܂4Iپoߚ_-:$3[8``,q* }۪6՝V? 2fǬ3UfsEwAc'b:7]V*0cqBv_a3I)&4jO#*ֲdav0|KLUVy֞vqF2w:5Ŗ&H̃m"BN){dxKoSЬW/H$CfA뛮|6 =L4[f6؂n˫/0qh(qQ0-Ur?쀇Odcz_GXwAٷT, O1h) ){G1.("֫Fl=d; 5|znH(yv`&&A,QD)DHcPw64Vv62˒Il(Xgt PnkceS{&/^BTb EFT; *rYsxb_]K !l7XZ,Kѱj ?Hk XuIi/C1&"ZcL=%&^[fV&\!֯bߞ v_g/f? [fWgr`ȡ ܸI8fb9ճXCĢ1ΥjۯWO>FܭīlUU!rLi;O(zB"ZLOAfGXڥ+KnY)c!) &jHXjh pkjWof--.dta_&hQ mlꟍĂUp`U4Q!09U M5&zjFeJ5SUPwWG05Y H6X@"cܯ*BZ}9W?v*Y>Fiۍ<PL ֶF Zs834*rX,(ZVnv4*> `# Bqp "gWgU.(s,:K~~GjQQM1L疚wS+;3]PK=yk^X  ‚74ӟ(Zb70nH0_ݰY!E"kd·PD4'?d[Ȫ7gCj_ CFdkj}8-itjwRTXlzݽN?u693\9}T(xX5pIwE)]2j[VlIǴl VKH^@ LJdqG0N=^*9is$;P-'Yd"Y2 $#cmkPKN+m#'future_fstrings-1.2.0.dist-info/LICENSE]QKo0Wrj%}\VڛNc-`dfs$QΐUWBB5}{?BcteɅ.@o'Ԍv &khDq@Z 'c4ⵙ,wЄ[ tHz4!xE: ̍@\zDlk#7å#o]CK3܁vu:GKbbkGBa/~`!Csw Y?B}E*ޟ>'q.ӈvtW6+m 0+Ek9J~2f_;gw,WB }aFF8ɇwg?z|BZ̖kJ "4k1fj/Yf ߕu J3YTXeo2Y>q2B$5 HN%EMd|)siv [ISJiPqmdɹj+U ϐJ(DiPk ^9I1AAkkgK27) \ dbF)dьn`T"=_j*)FJ`Jmޡ[Y5-dU0Z'"LRXh"8BM- ![^Ct|ㆈM6Fmx1bӸO*]JԎ;߼[|7D=6{hx>Iwa;tt63R{uKU;NF :-7LR݈Z~ hG?s=-G1R!́3|@FhA!mm.]2 $W;]kv Υmxi]KuS? ;Z^g*Ê['Lo3S XX"SYK|}\3m)C?Ϊf-(sJ^:\iXFj糕 L{l}2ƶF2𜱱d|>"|NlrA4Uen}7:S?dJHAwdCSL; %τe^q'0/ss<c l݄n!]/cŝE.PS vߥkOGY@ЀU̷ƕۙn^ŠӃT/XEQɾ(b^ў[ "*0^e cSVJ_s %hHY.e>Tbm ڈ$Tm U7iȾ/P) &NBm'/ıؿPKN &A_n%future_fstrings-1.2.0.dist-info/WHEEL HM K-*ϳR03rOK-J,/RHJ,./Q0363 /, (-JLR()*M ILR(4KM̫#DPKN6?0future_fstrings-1.2.0.dist-info/entry_points.txtN+I/N.,()J+-)-JM+.)K/-/WUÄr3PKN!}-future_fstrings-1.2.0.dist-info/top_level.txtK+-)-JO+.)K/PKN> *&future_fstrings-1.2.0.dist-info/RECORD͒0}_ @t1 (6?bI! `@USsϻN:wkstf%`bñpڟ3/ `tiE֒`]9֚@bzAVj|J^e]lZ.aHmkv찠LN)SYTDIB,6O_7Շ,qyxunnV~~o6dI704,#4BoL$W-}\vs-q;k#MX@֤؆}%{z@?lNDi#^HpX vDMM(g"-[$JaK#nn Aq [wM#Ao3nP߆ 3,:H5ppq=zc/?-A PKN!,^~aaaaa_future_fstrings.pthPKN ql F future_fstrings.pyPKN+m#'1 future_fstrings-1.2.0.dist-info/LICENSEPKNoX ( future_fstrings-1.2.0.dist-info/METADATAPKN &A_n%future_fstrings-1.2.0.dist-info/WHEELPKN6?0future_fstrings-1.2.0.dist-info/entry_points.txtPKN!}-future_fstrings-1.2.0.dist-info/top_level.txtPKN> *&|future_fstrings-1.2.0.dist-info/RECORDPKRpdm-2.23.1/tests/fixtures/artifacts/future_fstrings-1.2.0.tar.gz000066400000000000000000000132321477560627500244120ustar00rootroot00000000000000]dist/future_fstrings-1.2.0.tar=kwƶ{ .Niqê^ЉT~^T4Ǟ=O0 9ea`3V7O ^W{{'Pnh9~ )bb^]Wj OK]Cpppx?4%Mb/|/5bښbk 5;0MSow,5fꍺ2lO=%4E4V08=Co*;cd轣`ևvxA%\mh҄NakYaԙ+XӠoFBՈjRf!N*  kġS#2?`Β\-ùZ,4 ),cCa ab= "9p3:w+,طZeO1vIE׈IOHIg|CވN̼Jj-1oSf`~(ֻ/ca4}(Isδ. Beb3zNU[^ˌg_S a>u'vIְoEĮljXKHa*tɥ,sBdgy,s I\A hw?NԇDs:~gS9gS I42l=0QC 12LD,U9)oC^aV]AHƖic'Pb5~@eZj9%o#V$^@. q6iTFXϭEWU ŰlNwt0wL//AmHnH{1rewf7hoѩNFq˴cTM U76Y_iRW֊)-9afRbd4_!ȍuWJVb՛6+BF^5kށ*pur羧K[5̌mYÜV[U \k!wL]`z[Vk|(9 aڜQp3Hn8 H%.)4LW_qLܛAGŖN $Ug{_|ݯZ{ 6ղDb 8k*dݘy7znyNİ(s Ex4=cbs , r+( l"{NW2}!"G[XCe5`c4٨? Ac*M~PD5 Gf洒95ϲIMEsjTS4) 3S "Z2pTb/wqHg@{Snn)^c.nCBlZPaQUDzN"}b22E,W-:iYf +FX+@A5KAy^̎1r1a$1L ?<ۭZ՜TLټwoBCq@ȝɏjŝO +9 exW>~܉ f $qL[È#QZUde B8WJyҔ H^e\Ě:287z@#;!ĺ ->2tg&GdVG"pHS0$ <54*V&C$Jh]ѐk%S[FVhÄ`iJxAVb%7է;s{04s[׊ƕʪJ&RTWWp`^2@ N HRi,?v-^bUvi"?k%(X$߇J!43S/ &;6E%(m$;5Q#h T~($\EYR<=fC"KSDesjnchO:YȧkCb<.SƘP>V0{D%ϯjahqrſT&|S21E%ɼ*dbZ Ԁ2M,*&xa)'hP03T ` BMJTdV:+ӌ~TX!lA6Gf8jpі i"4ɵi"fMS:V82IvlVA~G:8<<9,?8/m4njC0zt_zTR?BY{o-GG9?>ϥ?C융o߶D[7t& k-\cӽi(C4=t7 JoŸ1dwR4|Aywd8ٹT075-Y})_FI4''''ǥ?G{.jrQ:{sʲDC?Z)1,A?NJ)Хo5{T~~ttR| oc_Μ'E8hhe") ;Oho ڝ3;G#c-vvw}"h!>\liI̯SpCQ-ܠHeP;OVv&dZan+ Պf0^+ыc?LO:6ZH9}X?'ۋ6u&̰] K']U:$~?},5i6yPlZ k⇯"q ']⇻ {67|{nVtpN8< ٔρxF1s-Ai[U效̭$#J`'DO [nM M:(e~\P4^=-D7^L .WG^,p$)Oq|'J76xs0|de:fƈEƎ7T`WÖ#+TPv jA  aqW4; 9qǏqwg;b3'"有_j_]l@y<4þz8PIVipp !x7] `{oz?mXc0+x s88:gztX>K:#gTUr_ hWxܐ9 hI@OjdP#^0K[7 ajM%OP{3̛V@7FᤖSLhx O|C4h0~..,Nٱs0-NqYC؞/cy-q)ېc DZg`ڢA 6ntpm= /QģsoʹiХo<#3LkUOC|,0cPw OlN#0K 'ѳm!8P4M/~ts>';/@~;.H+=_ۃ۫N:ݳk_mvxH+BoZֺؕ9]fH.ې ^_EW0Vm޶~lZ=ha1M;vz]Y;k FU 5w~mMCtBouۢD5P7HۭK~͐(_ǻiieǿbȟ$G/vh-nhZ~Cۼh+ I#_Вd i]TxTHI*t^^7/g!\Tl ֫A>מ. kdTz`:GZ6H`8ښ `P[{ Z?U:V֞$\zf_yсOpIr$yaLN|# ЩsM!<xN!ʹl_ 73xW7x#Wl:d+6詛rwtx)qI:^A+v2'Xg$v [VEFU2MmVr?:!gagںm#YLe*@k,6?~kWrX,=zF:|||Bt(c" !jr;i<L].ʙ,"K|VV/QU{T['uJ\Wx_ Xf Ǜ=wX[g\MϏhL[yVyͦyyF_goynqKsM"\s^e xNQ近aQjMf*n&NPZd` u n|R;:{zr">Ⳟ7#9έ6r?*uCg3[-f"ލ} hXP3:tQyY*_Wy"zUT鴅aUN?/wujJ/jk~>~ x*8z `vٖX_pmj)u="vM~'rq3Qqƪϫb/UY"zj mk 0w~i}m[jɀT;a:Ɇl؂xB}`%^\Dtn@o rΝ<yNW6w{|R hxQT[oԎG ^U (5N;yAKOKqAiA]PRl<<DoVCQ"p Gk[IQC{= ~H"&E[ha"(#ZV~S] :=a ?ۢY FÇ81^eb@,îq{)4e8 ՛!l(ʷ;;wxշWU)Y k*X"`˵&V,>-[̅7@O$|k)AHjQ"<^[E8QuPo15I4D[6^3iyV|,1ӋF$6p-e+E[]a^ّ 8<k~W\/m;yr6e`YpQ9 s\މ}b7czY 115Q 6%Nxf 5Wm@]oGma]Qeq솓{t۬7tb]m*pzs{s̾֌l"R.gr"o"Cy0E'~OξNvxPڤ[AГ}1<4X.-]]@ @Y^!zþ4¡es[]\"iwMށG1/gv&꟠u[&)"a?4{TP0g^⨀ykDλu- }Z *jw#dCB[d#lnq)bh::'ګ;u3Y[5ը6 ,%]`jY=ߌU{3yڂ,Z) J|rY >hE IT/Qx-A,|eq>e$&h.*KT،^qeӰqŖnY;lMM$l ʛ ز; A_KQt΋{kd=70; ˄3tS 'czupR[+T)(,Uhud If7IÆЂ x(zl ڥ:,G@T P#Ʀ2P1g&ģeKteAԴ׈R rKa(+-]A&8u⿙x/xHavͪpɛO&%N\iKmvu 8|g40!C =R>l v^3wgΛ{~q E"k|H5ɪf+f}c}ɚ|zhi6hq!D%|\ "f)6ba<&J[B`f Ck J[6`fSin؉!Lr7=4rmwy[,3j9 ͩ6#}F}n l]@`<$7l;$Fr$+^367aE`@xKᚲ'ȱ?c=BPUCURw}~ 62{)XUCI'%f've~0 ; >E7Foƶ{,ȿ=ێ:g~ǡjPaNՕ>(\>h2p2Ĩ=P݄X$Yl{G%,2̺8CG}m)u`M=ؤ8G@6%Ose;FOw}Fv-lg޿@6hC@0B^M{yD,:C^0ByY,njt"qMc@S k,LxK7xOdR™b^;F^&5n~˚J&n"0 a#$^J\W"hw|$=R }_:ٴ2^퀕 Eo1te ;=Rff \RL#.YY @>DsO&OQYo[$ƶWfwYLXJsA9@қc^ua;JbG_/3# ) J\ (אֹc`AEn`w lbݲ١=#AHxwpPiy[W;$K Tp XKɴGjK(y] gMYeB}m@QZUXxyl1Cb[l`M+i,%+lf-o< ֗ # s i SSfҗ^3-pJJgXdIxk@z>Pfv8 _CU>8+0^a7*'LT *5G.-TV)W,xVЯ-aZƻqsؚ1uVItKYsgQlDO\z`$7憎UI@SN+T\ynD/ ky_AֈRXoo۶5S:(В46x>P`XȪc\=|z*=NVٺj+hR@͊H=`wv ]Dͥg-K8;p6N=jr@ 'U#9kXc ӐrO +o$5 bT7 A U ]-5tdm6*;|0ms\^X Dѯ/qy."ޢaw+ff\/nR&8zDli.=pbwq43wG@0Als6@W/qdz#{\MmgRTnmUO d%bCMԃגv{d_E@f\эD*Lq`(+B*syT\8FLf|SaŮ#t .Ruj0F#t!y~H5C '1>V|pP iVnV=]_9%æs6纪ާ l6!Mp%!'41\e+Lw5Eo AYRQWܮnz>в T' }!+I+ etC9xi!k2CspڗNO4`` TȢO :׏ArpRS u YQDㅵca@a?AM| G SH-Iםs!X}FA+ Mӝ,CVn|Nq{H`<T{K _Z<̭ A~43i4D0ή7$ a{b$k HznI5QP|yϴ0ڕ}!d; l_5C,S\2-t`csh08řIZ#)2@|Q)9ݧn'ѐ)U#vQ?Q-:w;x?i1ё=r$x&<aLp`a+LrOkIcO<"',ɀ"pxe7(3ו ?T4` wSx `Y2W;X/]Ƿ\;WK6(m.܋ՄzWu܂.hm-?hla%5^P@Q$|ć@-}YќMTUqNiE- ([Elch;D\NW ~ݷc^9QI W3O~{'na蒓1^xbKr('ϫG؅Iz̙&&\"ԿQӆAA:^l82ح>ق?4؎8UOR"Z/ qbJeQKNx׸9EזW!?htw%k!7ޮᅳSP_}|?hV^+jYQB h,*ҫa<ҹsiIQ"fy&h3g'~\WoT+ C$߶X"MẽqSASa Ed9Aᚸ!\c:҈VY{H1O-2Sa,vLij@.L-U)1Q|Pfҗ],g(Uԉ)S,_8Uӣ{L _aD%*պon#䶼 /ѝJUS`c<6SU?eY#If-te :pten` ޡU62;Yy"d4D@ Xɻ,tfjnZ0Ƞ ̲ ? "ro&gfNKo&8f4Fӽ4lFԞ>AiꮏZz7 Ca,%&x{Y32xșȄs !\n/u<-ȓH3`䜚f_WaNY=dT_Fk@M_g_`,ٴ1"-RvAr1в7R5J%(ֆTx2e"  υn{qCs^LD8QrW %*$ LE܏Mt~ڥDdF[v/v4uCdҮC p@`ʹB-C i]SLAhqK=qX|H|p5 go $vX&w:v X:OaI2k)D|6V7Yp[1LZ,&T * nEi7fD‡ >%4ªBv8R)_?v*!=aW…xƻ|JJVVh:Idx@l7 ~@<ٟfi؁Էqzt1{Q"m?lح8ξF|$iLF%y|pYR5$Xߔ-ՏFhfalY$"Ӏ n+ ϖGΏbʍL*.x\aR\dZߵ)s+}!27UԚ 8 صAR/9 /wH4L&t{dBYVebrk{rnT>SLOs#ߍCsd0|p.xX&^avŹr M%F]at|胆Ѯ(8Wze=/?Zp2r<,kz~n+uْ~bD5BfCm%j>%b~]pttN}A*ֶb([аe+xoz}7qGn/E έ3*rKOZ->2oj ]aEPz(П[}u@! &>@wB |cV;'d̽LrkᡆG{IsVQI'Ĵpca ]nEý/6a@ÆW$:[a)TrP8i337?nwS'[ sb 4%@ϕ`>:L6~~roܑHxEe.2N Nn ߮-ExqiZ:ML!(#7pe}TDI B>9OWupDgQ.0fIq IXM`ue,1@b7H&CX܉ b0Q5dխhPejx\_^zM ΝPe,P*TRp"͚w:I^ȀvikWYDp?8$BHm9f-ϷNx}p̣ݢ͌о,lX{.ѵQ|c Ewmv[U_U~LyYx@ǩ98~xw]h.ߠmq ܼPK9uS"importlib_metadata/_collections.pymMK0C=4%x\'x8sYMvz7$`EDwiBR#Fo^EStVT}[`߶mPK9uSȉ>"importlib_metadata/_compat.pyuUK0W&K+Ċ8+qP4Mc)_xv*5}*#?qVr\Ё2LSNa1D~iS\T5eT-3T35-NO'5+h~@/ipǯAy EPx`y0<2pn8a#:%~!ҞW=$hn#@AAV~bdb\[,=[^ţ:+BӦ:NZ8(Índ>*~!J?+Y\vetQ|aX70W/AAe+'ʧ 3 YUzq-<9FPK9uS:oikO importlib_metadata/_functools.pyVMo8 W!lv(6s6Sc[^InKRGl1=4(|$%]zLŮkroLf%쬩EYt:] A46WyIX,AV۽Umv4]\\$vA׵ycu P^ndd/{Z9]:s3 wAԵi%ȅ~a_E|ҹzE3]Suj- h2|Q4d{󄯪H {W^,.sO  sCF4`qA-UU]@"iC A}DRě,4\  f˪7H,rajS6 F {TMRJ!RjD@D(OlVg;s_q%h?%MR9g(2Cl ds61~j1hIRbwm v<59jŻDMNH%|/pҳGE~Xh{TLgZKmH?aٹe$r߅ų\*oo rKO6xvt%ۋ39;#|<ˡ̻UލpjNY&PK9uS: importlib_metadata/_itertools.pyUMo@+FȒ-Ijs"X,1Ndy%ӪiQ[rɜe"7y^lK{1>6e)bcXϿÙtM>Kcp ,hP?'(7Z*֨Ay\tΎ*t-˛[wCX5,nO&QBr}G`j8CaN7X{H{.3niLPqJ+di8Np0,Hj-1O"n $aGjA\:s9L=`3HT%1V(nW+.Dmlֿ"X +ghf]x۰F%uKr(.0ד^,@Fp>k9y9!~ ꐔc.I)x8\8S!ZeWRXTXe8';h6)X|<Է FZI9'4ϔ-қ̉C1<&F&LY+7a5bzKN554/\}cK|9ccpAHeb}ãm- j-ÑcȽFa2m}l{vwZ;'%SL9l 9;9j7}FIg.:~F _5_ӽK+}pp\u:l(6JxNpOVuPK9uS.importlib_metadata/_meta.pyˎ0 E ]L8-`6-:i7A`6hF]N뿯83Ε( d{ 9Kpȉ[`%{Dw P\X[UU3M0%AILsfϔ|l6Ӑ[T VJ"J:nS.7z/Ωcwf9&J,pG?L-n}]@-0>Umekq(\/^&="g$Tr8cMҞ jԾȫs/N=;I ;##ݽhwF.!˄ͲؠXNJ=4FKpT8ϭmHH&$j9{$ TL+irijH;bawEـ52;}{E-\ w D*Ur&4})_;#s˻=ɰ=!ú4׹&7 [;M6HnmQO#~_ߘ k$M\`LK49io_&}Z4̺͓^PK9uSimportlib_metadata/py.typedPKLuSr;*importlib_metadata-4.8.3.dist-info/LICENSEeAo@+F>i.Uq6’qj8GR¿8R^@yzg+|} *$p"3QN-eng.H 8@=# g5t>rHUU:UјSz=MSgI_aoERJclO!ПEU3W+%q'['ȶK 6N(dQrވ4{փVY y۬lϊC-aͮ~]z+ /QֽO5\k"ۍt/ IEkLG8PKLuSŇ+importlib_metadata-4.8.3.dist-info/METADATAWr6}Wl4 AKrJ2u:;imE /XiT/]nY, UF0#r (*qPtRd:gtBn`z)ȴ*઱*b=sC~U*Anme0 1MTV^:\.k|#4wMaT<2OLD~9Hxi/uI$d&1"Ϳ .UUe6E*a |CLy u*xpdmv 8*1O9%ӼPG s͊Bsxyt]Py k ڇN>[!1.fo.oެ_ c#[T<~9=O٦BYr1N-c []p4gG/D6|El6U./h;N'ö\Lj.R\;}Kg*ymHanh;ږ >>p(nb@odTrp:q3Aq^b{BG{.r/i^==Ugf/L֙]{[*Ϸ9 Xb>sveSrkVXh0'xjpfG/xuRjbNM.L  gJ%ŎBbN L9B@.ޖj`٥JC xbw]RL ]Mt<$cѼ\sL{&cu&m>DFdY="?E&csM u8_u4b`;=T0f^~ʛ.s% 'Q JWR )jNWҠONXZVKL еNi`[%C.LL]URp)t{`Ly{Y]&->TF,׋SD/P(ÇZ4|u&DZܮڗ a:6ؤׅӗ4Cf0ptC%_BqOfnc"Z {3ogj5/$Edֺ%FI"گh.c,sFY맳Mq$ m2<5ϕba:#5:~&Grhl; '@)r0x~|h6K *Ar֫AU~ ]kgR[L*/ 4KxΓSH2d$$L(k|$Rԗ(1ChY ݌Ճ_&3@2а-G7v -dӻޤ`*6xO!nW*%q3H#1 PT7YtIrMZvh'U t\H^&Wšo}M)'+ _e1^?PKLuSP2\\(importlib_metadata-4.8.3.dist-info/WHEEL HM K-*ϳR03rOK-J,/RHJ,./Q0363 /, (-JLR()*M ILR(4KM̫PKLuSZ0importlib_metadata-4.8.3.dist-info/top_level.txt-/*LM-ILI,IPKLuSy])importlib_metadata-4.8.3.dist-info/RECORDӻ:|<[ L ? 0ؼ_ߠ\3D>Uڦ'eq*L4&(*DѪ!9QuQgf~~D^5m6qY8m, j 7X2X4P3MVڴlΌٜ(Hܢ4eR4; - 2o9/L)Cy%;U'[⟨ay⤩ژ;BCGcO1 -_@Κ u׿TIb)T&7AB_S8ƾ))O=P4%P#8dl!/*ǧnǮy%];FDŶI }eG\4,S~ =ﭫ; s['Mx"%importlib_metadata/_compat.pyPK9uS:oikO )importlib_metadata/_functools.pyPK9uS: -importlib_metadata/_itertools.pyPK9uS.0importlib_metadata/_meta.pyPK9uSxv2importlib_metadata/_text.pyPK9uS6importlib_metadata/py.typedPKLuSr;*?6importlib_metadata-4.8.3.dist-info/LICENSEPKLuSŇ+7importlib_metadata-4.8.3.dist-info/METADATAPKLuSP2\\(=importlib_metadata-4.8.3.dist-info/WHEELPKLuSZ0k>importlib_metadata-4.8.3.dist-info/top_level.txtPKLuSy])>importlib_metadata-4.8.3.dist-info/RECORDPKgApdm-2.23.1/tests/fixtures/artifacts/jmespath-0.10.0-py2.py3-none-any.whl000066400000000000000000000576511477560627500254200ustar00rootroot00000000000000PK8PoX"jmespath-0.10.0.data/scripts/jp.pyU[k0~8k)!6)>X[8QKBH%˪}OkD6F[s#.0n+Agjm :#Fk-xK!O Љ /AQ\TU)=g3UV,h͢7y0ZR+O|Z'TP /rv6Rط>t|C'Ȣ VJ2΄Xym"E;zA#s.Q+0 hG-G{I4u/&-T&(8pR7O0ɍf=[+=a޸_8pPZQ&cl%mP#MRQohJ-*^ܸ@# OOSǃ{']1Af-h3@Z=R_W~el wjjCFV.ss/ߵ?sg =y|V=*^@)%{h2 ƗPCY.K( `ev~Y ǝPKP 1jmespath/functions.py[ݏ߿"hn`t^Mqms9m!pm֝B{kwfHJ(޻g8|p8ԥyUʚ_U[YC.TC ǵ,?>_9diKc%Wݻo(NI.}Q1{WUG+N7v-a]0L2._ig1M7e!`z)Yft]n^_mVZO iEìr#|׫dV ,+}Pʌ 'ԇAuAxwExf$jp},Yf6K4Cc戵Zf) b zl`tsBIFlJwRD_s;䢨lA8H.&iɶbm?(ҐHCQ^8A# =P"*EZ'I s%kbTeux-8}&YCQWny#GEԐǫD{j.٫tD8{u)Y3U:VmB:$*@97 ^Mb[Q0Z:kOeEY\?iLVuZ7zD#-CB`8ͺTd6Kyjdgj9 Č6tVz?I^}[P/K?4IR A/5ϲơ#%mܪ=J=MZJANYқrp}[o,?=X jb'YƂ}ل}/l,KXVo5uS&wp &&UF"C:99ձ׾-#SB/6jAML9l{ݰL)J Xg3oJqg[@S7)|eUগb1:&cu}YbF؉WngHW)=ul lz 0qПk>gei)!Hb]/Cvɰtc4x}R 2.v1p$ZCYJ LY5hŠ٨m{dn>XOuoFnL1̑ByPRb eXdU*VwFYC {5uz'y\_] ;E(t@TQ;P 4(O<0A[52b'<7q Ooa.颳Z@A:ĂJDegƇ][޲Od΍mijM[b}VԤPӑዠ&ϡ{Jh=9۝J}~?lCO7pe7e=۫Z02"cn;~{( YбUٸ!3UG.jPV7N&Ћ PKPEjmespath/compat.pyUM0W 8r꩷RJ=NʒMޑc@x#=-]2'Se*a$ID$ -K.t趟uO TN] Ip> a,DZh5f@'D(A"u =& zг ^&t?[9^Hx3%V %8%k_ښ#tL3CyOtD7,ӓm$IpD*uI~ߏ9Cp;yqp'2^[(ϣ-bG=P>A0W%.lC]?`6G馣vQv8F<Zb&BH|??RזU)w^/usX?<" EбBBPIjgGa`XY~-VS2MGPwT÷#Ub,FFCt =þ#C 7V:&yBqMWX =]!G^%e/j^=f̅\G ᆏ52MBs#L>0ionjZ,U! rCkB )۩b_u}atF1Z-CȸY ~3/PK&P[;ojmespath/__init__.pyKk0{F81[mn`Tg+ؒ<(]9N@{.z2ҽ>Ay60}S`=z N&)kr_[D zvPK%PQm$Jjmespath/parser.pys۶w|9ɩkw[%nx4 I)B%(tAJNn\px!,wk^ƕ(ٺ OypK`p$?q:+^Tq 6gǛ޼sQ~`_5a wɳgi>TgIOώ;>='b,.9bi3✕\Mpɪe\%׌߯8+ ,x,Ky̮2+R+5M'SDIbȊtYIX&eI)(i"VϪT//؜߱BT@ |vݔ~gx'mЌ]+T> w9{xJ&EL L!l^Q9|?%6eo|U,/ˢy`6m9/ִ}bV%UGDnjSf\"47.Fjz\A )ha0@%d 3wIރ1J^E  81jf몴̓1MfƋ؅m Zqya :ҕPXl[`TQSDDR.LN퀃hI BVgUs8s\>UY x e?Zr9KɼZW.@KV*+KЎf<Q 0-O{lN; viz3enś v~p-u\=w]05T/9u ҂iDe=6n3_eU=f%/9掏t) ȃAldYڦҜ(G4SZj{'90?:2:z]n_>#y^O-*V, ZKŶ">PPP!짗zi&SFg_CJʘ(w]V5a(HV垪CQRy+gY(F: l -Fydֵpk]/=Ez"+̟O P`8J픭Ԛ$hEx105( s跼y+ ٧`ǯ5S[ʩ`|2cGWsG.5zTQ67Y;=vV\5úG޷ͽ٨h$6z ILwhz,*>%d{iAgБݣJyCLoF3"$a!Y"u 秶d@S7hSm*L농 8i^{4a=Ŧc|ieWLpk9?t߻D ΰ oQf0zV:D]e9x5kVb }jp8Ly=Nݶt~5}\#_!;Ї%2ENN:89KAcxtY4%J:8Vm5ug#{z6(-MDDNz_-鎰KR/;BǤOA;eQtJcc3aEWdj;.:R.NB,pBmEmAԺXvqt`IFncfLy cUNt֊ܵ>eM58Bj=sj5DDBݒj dq !nżnTQMش5!::u\leE{~jٴmZݿh{uI!5H{$CAj:Z֖_=K@-Zx_kn{Re (7GT+ͣwέͬ@-&ȟ6j=c(v xy= ȥ"_:hnyXoܙM IKι>/6^e@$|rȺj):Ӿ3&m)Ǫ%LpƆeBCgAkkEMEs8Վɮu&dж9aSvj@_Lj9^6(+s ?_#ƣC*Q'sQ*hRJ_s ݆LItn,kE[ q2 g ߘ R2P<>1!>P|@7v XNxj<+(JӴxl~x,F6 Vkw.{ .V?ùNf&Kܨ[/ሙYUUs +-bۏ8&o; ڨ)o;4*gZ.rR,+\ixJaI-Deԏ5֙QPM2en$|SUy#/)8<#7Be#c`RoyFmyVQoxw^lhw1&[(w9 ;>xῙ}tIaSz;f(,r=&^5 Лd@.UKȮyo[1\I&7T`\,JF%5Wz1􅡪˥ >[5}ST3Kxxa9K5~yu~$}@(.~e%7[՚8jmO߫[~H6VYȴ|_ PKPu%H \*jmespath/visitor.pyZmo8_Myt?p\/a[@%f#KH;3$_Ң%<I&/47:/NNEfBm^1iekg>k* -#]m\K'''XH|*ycV.NIx+U6"R@<)V"S1:q Ua>(bmCszzjI%SBY6 @wsOFi+|T zKSԊ"oc-kXAL.c&T] zͮZC h(.v'*3B2Zc/.l 'U@=8ms=vQ@Էd}~r1h ,OROyTnߺkC(zbds717C7@~3ǩ2 >!0z`v;[k@bhyk] 1љ3϶ENB[lF;U{,BB6ׯ;?y籍L'6oMUi30#I|e;osrQU`@.^l`ڜ"BT9#KŢ;C.WXw |{ʆЬ"y?F4y4x'_@L6FZwAJNjT;C~*scN7Kv꒭4OEa!.WRyڨ ~0J'|Y22:7X&x<5tKGBTD"hk{C3ǃGO dY*M$[,z6D~˭CM;Ot3'C.f Hi-ג&Vo{B-#L:LCA@ ?;O$1ď(Q'AȬE#akI{M_c{]Cnw+lH,:{J n7=V&9aj2L]6.HFDXB~\2{-,< =h2nC+DXr.P%xB9t#]9ݙ/womcZތ(IqLkrh>,\ w|Y\ǫ6Z:nd6C+*)م=b`iH_AuUnyAޝoGɔ {8 uu"sl` yLrwIsJ*2:/Q``}_~}ݶ#nE}B)tV ~L]byRt 4dk+nd+kPN+V*<֣+ظJ.;则73RkCD.ܝ ½J6Ԡ-h=b dyzkaA#5T$G~̚= zq/fu/[_q{oj)?x\|8/ Ͽב^#p%Z.߰σ֒vJ ΔLƮIο%hϞ7e V"2^FbofbA~~j=> ^7О PKPMs~!jmespath/lexer.pyr8=_u٦cg [ ̆ҡQm%ulc),#ɱenY$>>:dJ#S-T=4G6 }cq7a6+̗cr듄8bw䖤4~7MRH`0C̘Btouzv̛ML'᎒pOI={(}>z7}{[2ǟ޿4I(\$ñЌϢYj'&ه?'ǧCBŲ!ۣ1Sx!e+R~Zaza%t$*pΓ/I0v0,%A8qCxonmu|7|bnjMn\hXtq.6IK~3p.+ -{U[ԹF+H~"r1y/aTV}8NIb__w AGL&Qޡz{(@ HRCO~jqWGW0TYgu=eyLHDF Cl=C20Cr5cO W@|ůt3pĞ QBRȯSж`!ʔPHau|?ab+PEZ]Sl{]Eq0v8(if0wՒz2>iJp+P7Um8}unCϲsz 3՚C)m f%VfhSD,FF53_L;7`s._,t4 copPZZ0{znz "VU%adt~Gh٨-s0LF~*0ư:`՛n+O;;F1W=.[mg x^!>[xqNM: )g%⸘v)"Y]Ĩ@Bk;yT`' 䤗{`N!n0b;z=ek[a(juCQ{G*PF{ag_Zl@~\FY(ۄ]dJ-ٳ5N+G]eQWjq^4NS/`г瓜 ~4_eka-dFgC_ fTa{W2K)<$f43Aw񮾭ԯ-/w؝?i5@㼰 FoHjZUjNG/ՅznWPKP̿ jmespath/exceptions.pyW[6 ~ϯ R[#y `- -ڭ8]'jmٓsNK7ɷ0'H,")':e_ Tӗ}ZJKL ԗlҜ)ޜ޽%񓔥 ?F{6@WE2$xy-==t{mBO=G^{s$c%Q)^.9cII*̳r|LRqMB1+ cڻ uw=h2+ Xm_e5꽻v}AG991c2}]Gi$E4ޣ Ѵ9pae5) dRɘF0'Ofv,MZh cXi&5wp.9u ^[-(s.L0[=i+5oJmGg*6A/Iap╕@5ҙq2 Blɕ$nH?[B߷:aZClƎN{ /a,>K}d'Gt%>K GBf/sЗ1^d}e,_ºE(G`woY"D5h yg{Lry<`{z(:?Xw[AX!u5 ntkXlUaOвS̥Q/2В-gI$=O//R;P\k {I%Ssnz[[HIݍ ,) zK[TVE +sy)v EF V|)k{&.=ġZN Wt);5lNl ZXfLLፑ)BGH 0P&{qJc8HD_6) ʑՁT8UƴŇf(zϽ [LX, T>`|i K*)G8\k^zv/{`ziF+AmLY0yV9ASX,7]Ceb mR?peG^^Aֲ-XVhaIz>U7N<ϓ D& $D,4z(]*5n(>[C绞k)8Jd^g/sQۣgaB_8u^D2Bу#r@M[nuDFa>p#KOއ-9n[IÏKCi&nV߭,$]Gd)0 |OA>I蛑lÈpu)xG\;2X9g힅dw[~r pxa3˖K)?9Ǭ)Y># cg쑙QuJPvknĿ.TCZ Ub!DCZ&^f"A"~ 'ڍF~ W9+A3o_D'P4c$aVe4t|up9Fn [.n{,_>E=OL霧/pu0,{'G%N ;?H709w{Y/wwRb-T5&aKy7'QkL]Ze*i|GTn 3EiGY S/1D IlADm bNlY?ľ ߽ۜ38͡3 MJMkָv1FxҢ6 @=uS ;MKG8HHvVS۾p=nDY@{d:/~n1edZXۻ^|nvjVPg]0Ф[n4fvJХ`tA?5?b :y&޻{ $ҸAo Hx3ld9q?M𩋘LjP~u)KwrÒ+CP]^O=2w+7$%ocIW ]TFRI5X0CFY4ӻq }ͤ?%q*Y%؟r$s7MW2X&LҌPK8P25~<%jmespath-0.10.0.dist-info/LICENSE.txt]R[o0~8⩕Rv{ۛIL rLYMOIl3"jR{zqk/߀ꏝS`T~ @Dyk[lN`< n`;hz:`AM/p#2Hf.\Ӹ܂6C mΣ QiwdQYj3!+s}p .5ùEz0)D&FҳgmMzu:Z>=E&z%"As7N9J4+_z;~̂%ug71f_ q;;<^bōZSHT[y4{5++tзb.|7ɺYK0SװX ZyM5~2K Vפmx滌B\YIy% :L< ,HKaK.;j2$.y V% Hy>b肮rv%iNy@F p_k<>~2SWeVxL0{^u UX"q%ZCϻy͑&~$PK8P'jmespath-0.10.0.dist-info/metadata.jsonoo0ƿ+lHJdڊB+ ӭݹK3T ;wO{r%X2:ϧo m"7"y/1yVK(c^)[K!l ./?7~{Ю6`#ˢM`)fڻ#0@婍` (]S(9k$"KRe#ˎRPQzrյw{c4@14Ź+gMi<7C@L ? g 95$I[C|ׇbx;yՈTy}ɀK]NP&#ZM٢XTʥ"`Ƚ3Kߞ3@(& u2VKUFGOŹԘ3eJi}4z/,jL/?iaJ#~(2uX9,-hFq5Q]BK(PRVѡʤNayu4aa'@ч_E*1>S fZAuҢTnJ[:H#c:9FGx|x FBDACLDHgG 녴 "[Y͕]@F m%o9uTT@z1d/_ì?݄HT\FdyMQ]dh5+Pb>4-VEM IeRzECؒychVI "HwEENr~hQ9م6-x,1?fE1}*+,Tۂ -4!dBKT) nqej 2uY5 ZpJEd௃"W |j, wӦѱ̽UKqzmU3j6`2f > Glٕ~Dl|)b Os5w +u!}#*6f|'9E}ߝ3V;}?sV穂#aZH *8ѥxӗ_vc(Yn,ළNMhOBmb+Wl;bpMq..[vL8A=xT+B+,xؑVE S#4%xy@B-2.'Xw{'>!e\auh_XF.{r[)\!ΠneZsfC)(`{.o[g5LKXW%J -k+ GRQoyB3_Js,xx߅Ui?1MuÑ vE|{|85qʣ^ mfGso6] xVX r$vUFLN!Zvsik~ZԱkhf铆e=\]vp,Ŝdt+9-7R ,DDȪΓ=m2X,rmSU*}ppĕ4˙ Oga#f`42jUZ1 8Byi gЎ):8hQ2T1A!*QW0l‘6V.>d1oM$3f ()'&HbΫo]g',f#{rʹUy\hm=zO–nKÏ+CĻfNݾoJ_?cUr`l=H5i ,W[ #;_c)wJ,qݼ=و8-EK4٭yPS8L?|]fCtkfˈv'Uw! 90z4DA%w#”(Wz:߽~֨riegΨj&읅Y=L9HMFcl]M͏XoFt$ HλkvR$/ʸ%V+P+;ڍ2#΢ʭ<:#1̳E%wr#kÛP]ꪻ^'~c2|u<~&\rcb2 kMc+-fh5oA30k܏K(tmf)xUV~bbˑ݄V e0_qn0I ?PK8PA  jmespath-0.10.0.dist-info/RECORD}K8}W?(B@'\8Su螩&ɗ7Y:Ϗ H iBAFfLyĹE)Z檺$pzINOnyv6fi @h$ԊZ!?PԦ5FYmyOZ3x~kiq64.7jn ?v0CV\mOV-_c8ʜ4=DFK+ &Nlqz@a꒲scOz0 (0 ' BSv_wx,zMv`8j怜I#WhugSvi_S+\mCcsZfe$T8?h,ӣ\rW"?Xe* nʹg{e3AjiA]x>OTWݵb -$']s3Mӑd [PK8PoX"jmespath-0.10.0.data/scripts/jp.pyPKP 1jmespath/functions.pyPKPEjmespath/compat.pyPK&P[;ojmespath/__init__.pyPK%PQm$Jjmespath/parser.pyPKPu%H \*I$jmespath/visitor.pyPKPMs~!.jmespath/lexer.pyPKP?R6jmespath/ast.pyPKP̿ =8jmespath/exceptions.pyPK8P )M<jmespath-0.10.0.dist-info/DESCRIPTION.rstPK8P25~<%yFjmespath-0.10.0.dist-info/LICENSE.txtPK8P'AaD(6So?#x@"E>,35 ^3!1=Yλ_F74048})PK!Hpdm/backend/_vendor/__init__.pyPK!HҼ%pdm/backend/_vendor/packaging/LICENSEmN 0+CւP f!7襷y13 h_ ~!C&le K*$#nv;ލ/*Jꢴ4vB$(aC,@Z=(Nu=֢swJ|gr]ΩW//e6vn(}g퇡͛0ݛro^X݈wwnas[;|\[7z>!ōuP_ymfD3iDd'8l*QU6VNªޚj,qEổv D[JlbJ ȷfA{[ z{Xiջ ̡SVJPG!ao\Z1 `ӝKj'qKϔ;< i,IJz1^ jxk0`MSiUА]JӶ⠇= ⽱G?@$Fͼʼn+}KAgKB Fy)'Np_7{X!{EӾd9h&rAr%պkʖ( pU.H[ՁJ HL.Ì3qk7;ν&Qy|x [<',6[ )՞FZoUך,hMh*8Xwe3) EgVGghCpJG~_hFeq7¹;3pP~ִr/;:$DEM4c-`bz@/䘐6Ƅ2?"' NPn*-pc2(!iL8R@w1tXHn RhXJJ2B@70-/k2-dqPOagXa E+ 1de#XQ 4Fq^ (Q*VF?DcnE\8u5@ ̴Q+14>O ><0rA%lfJ Xw]}~I|zw$W Zc~^Z UV5Gȃ3n тqV]k"[˒Dh3:}) .Ҩ bL$H2lZN"KQMm7n;h#3ZLjDe}5yu^f^˰H5腭l(uD>[_`FWPhd!R+%u -+Y Tr;*,!%H+ȵrL 6 8n9:cKxi'BTS0!(hFJ&v(rzC(Ȱ#j{K|v':;d)On@dSe ("$}R:ඥ{sF›ѷs *]<~`Vb3rqz,GeURd!38@z5eB5A#L<5e3_V' єga`Xq|t q k}#!ЙK'X}[N#Y>B9'la5sG+X Z!P$PqCt-z>k= l/ѦAP /؁qe f죰MӁʈ]^f+ܺ7;^Ք ҡ审 6IdtA8wڂL-PbYc/0ScN |~ V8 (͈?hz6jE,O_8 vS&ñ?Zb 4e ͨϔh%/*+V|Ѻ Ċ໹X|24[yLvB:p %Ha@ Ċ 3 % sunDC>!R e-Bd6ˆ*bGd&%-5P8"?S8ި*Uch$bpyid03\L&VA<džy⢉RWALN_+P?G24 ˽h•L}A"MMV$T"yu6K VMpd8K&*q4N8{jvMx蠊:rzJ/I.H|x"aV6zvt>x:aͧKki `ZG^2.7T_2LwXFH57B9pR|io`? L=2xhviPCXxte9ٍ_1} ŹYd48Rg#c_A4T=‚'XɥPMӨ XxG MNj!|5PRuKZ| v9$nR-V"5/i V$wI+-j6/'νƹ@lL,7=܋Oz_n:{/??;op:I4JISќT:BKȞC,~y^/W/߮ ݯB }`e|\akansՖo Y{TӭpW8 5H5DB79Dx;S&3{V,?9}r<_b@`a'v6j 7Y@C>2ԮJu]b2ʍ/7zK<"[-9)8 .k4m'ZtWҗ\n=}bW H`xr^h@h8|gUmtɚcĘ;3|bpx ؝1A73e FT-W#c  [ r8HOq^FQ%is0kZ! /H:KO{t=,|-ro OAi9l+Zԑ+1G#ŝj;jIY0OAAW-p_造V2 NotMv9!@`%nQIQ~&=196uMjhWx0\ږ(hŔΣO+fx{d#H6dEcF.PK!HP G@)pdm/backend/_vendor/packaging/LICENSE.BSDROo0Sʚzbh'0k{Y M W-贝Q'r2.K^Z̧nuk_q!F8վmOǍ[#hdB+֥[{0&˽@&+ H0J,+# 8%\Z?k Rؔ@0DW0D*:BFl321>rXt)raQJ"pBIʩRIf9.oya@g4?u(.sN"L;P_.9PO. /IJ7t UofAWKm ihqrcZ H^cpYܣƢ8tebBRh .xB1)"И37m0@s Ś52+C8(rVr"bI+Vٗfܽ$F28ǽ PK!Himc*pdm/backend/_vendor/packaging/__about__.pyUj0 y [te e :v#9vB~n^1_$e-YI"rB pyE4J6+ aϧ5&b/0TA3Z%fjm]/*dAfIRBʢL?iQ0ĢHWWf%g(^&X[mb;_3RPrF~Xߢ!դID5~i$1 ; @A;Z2>֝3?zGѲ 굱Qk%d'vua[*P}:;P}Uh/#c_o;)EL?:@S`ܮ6%A&?PK!H)pdm/backend/_vendor/packaging/__init__.pyMj0 E 1i!2.Ea&C77QY.:7WG6@oB]4mq A;dA}xLe9XNCfs fxy{97u.`(X!_O -ɡ t(ƺP+3y6Ek~"S^QbU4l)}*rM\@ +J.Y#|a|6= b(`QMPK!HČ )pdm/backend/_vendor/packaging/_elffile.pyVn8}WꋴUv\&X,5ZD%J "n!%R$]>ؤ4sh87ux$R feǠ%QB"(yD.  *4] x*.4HF# :Cy^bR$kD"d'y'>' &ֹw:KtzU,(K;Z8;l{O0~ߏGwt8 ("cWhLS9D*L(XVj0$ >xӉVJpq{Rͤ& tf,"0Ds.z3 en$ 7~8D iRNp{x8+th^MO>3j}MF+.`ޤ8B=\͞=})rU>Ȟכ)W%( {3$c v#9\k ۧ5#rʎYF(3ÜC-; 0'FRڄvG\+w䛻\^BR~n!l`ؤ*p#5.yGxVl:vU2Pv{J>"ӈ5r(f&'L$L±4Ҍx (M Q -u¨9, 󖼂S<@12WƸ171@橩gR ʛG7cNO'fq?L2Pޢp/^5^;ߋ׫󻺺񻲫Ca7<ïWW;za5b7hF?{ҶEa"K?cmsŻo G} ~S&ڴAEO˂n}x5YgB}%{`B<_a"XKf?."k@&ߚm*$/Rz,5n *H;5lxVHXvJ`@"Du}lj25;-l#Ga81!6\ ݫ,&*H[A%c)l)blnVfH+brL`F;@~O.n~]2un $K7/c ׈pVo`պôFԺBSű¬(ùAY3lfFsփ pI ;5W/8B6UPfxsPK!H |W$" m"+pdm/backend/_vendor/packaging/_manylinux.pyYks6_afR~$zؙmp PUboIl.܋kgi^ "2MԎ^%x,"9,$(4S.'_>֟֘ه=8><]?|xcgSvvը˳1OR;X0?$ ꈩ" VGAi"ϻO}dbi& 2';C[4s賖" + C=(lpu}u&<*A2($ N$ɤ|y(H4[ yR^0nth|6VAs`AxGUym_ aj>G)i@"O4Xlꌉ, OX c| *F]ZdCDw%j4$ ec_W$܃T{H{\`iYXDђyUp6k}۸~,.:4XA5Gd"+y$*B P >Z5N{ %cHZ$RxB`$%%qBg[kNfLP{4+k"X$h 4|%Ir{:-)#5 jx, B0ʠ Eń?h@=O*#!˵΋J$"DD&I\doTB Ob9*HXJ$8+̀+z?ځjDvI6g`mmv ob>{3Z]X0Xnja|^i] 8͂?pPx * I#g<=U,)10u'vw r`4‹`%"ԙ IRԳf5q O!1NǓ}o2qjwիn@13ȝoǻ9_WaVʶ=z]杳>GSYp8zz{遰eZO-Y>;ng·2)d,~34c> N\o;7k{<-pCkj ]S/K?.'_v.)|!+'S9mQ:nojߟg=W?uiw (uoZ+#>즞6󾫁'E1L}0@ZwA:Wmsk*OuM^JWՠ[:tm=)m[SYv|Wkvrgx8İ+Zul%' ܍i.0{ZyV/h/6Ebd_ 9/O ۶ggoONߝ\>xzTꚽd#4@S:XOrv@2탌 m1k0:lK|zxk۶Uok5:}ƨ9QH-I#S2#ADjDR\-F)hݮqȟ۵U0<sM t0lje7"᪛ØMNv[0 ˘?|n;Pw.q۩qSUUŝ{@a#4_ZZ>yR a}n^c e\:HRWQB"+ {Oַ҄W髎C֠#J E(UJѤ8NN=Ag7 2h]Tj]&úm ut8FkźG[Gz\՘y[lV?^דRg.[n![֗jY1B[خ~ :8 XnA>bmgRQy̐ە';}MsGgسOMk/k$uP>)/yW)7jۺ}kPK!H} +pdm/backend/_vendor/packaging/_musllinux.pyUmo6_q>Llb `0a6@~q ( Ej$(wG[H {4Moo+p]YT VFh@,Y? (;k^vZK}ۓ? %JQ?psj\Wt:Ή%i& XtQnX1nZS 7޹Kjk7^X]ވÄ蕫$B1aҺ~$)w?01l2_4vRxz8&I%j(Zn((O83o;m_ƈ6sWh(a6Z2PUY>ɢMZ%} )pp N՛-|L_Y;"&qXcf>P{颷z`_ڟ*f ;XӵE/buėyc”튒G/EyWa6~r|2lh._R@:D׊R2E~{ҼA{XAMƚ hH^ K @tXCPFs]|'z'ө i?7%j} +/!{骸zW2.%on9X~(qxz{$L+[@jiA=}R!:{fu%¶ޅx.E!Z wV{:NB$DHo$^WqM!d gw7 贤qUh88p!+,* Cq['3r Mbλg ](M"}_adq@i3EEמP׻]Wvc9|^`.mcHw"]C_fNbbD⧼SCjnG ͊s^6vq\Vc&d:[|;,[_?7]e@f8R@%Z"ܰ _g8I1ˤC3k%zz'ָeЊ" Y4=Y^]CFwd<=䭵7Y(Lsp( bTȠVV0>}(TC^;UĊM%nyv90I7TCf؛D:]"#]NP٠}];RjĒvB˦Ɂ@Vz2-E@ҕdq 2,SaY𸑦(,06_5i "]R(+ XYipr ܉Nm!@j-t8 p48u+*ZG~PQF"J}lb{~ |;Z v^[nݧ6aI@n_S\(PbvHh(nnuF[[Ԉm4Qu]2sٝk?NNO!չI t&֖ԐJ- {KBlu#Y( tQdP_Ϸ((m5Z9MZO 5l7}vT|lΌPjxr)߄w'INe=cQzL۸7`IFV(DT#''U4č q7ה|.9&6GVVYB{A}AIUQ𶅤b)>w  &CoL{uX,)Ě}J,TkwyX)p٠>Ǻq+~aunؿOM,[[YufP3> Q%^ZpN_8B;2LN!hNsu3=2KO0Hb}?Q~?؆aN#4wZaLnf`g Thֱl&b(F/cW_#4ԊPK!H0`,pdm/backend/_vendor/packaging/_structures.py͓=k0wYpLh)BB϶"ҹ_YN?dt HQY̠V-ܿT!=yFh ){Zr&ܶ_[nZ 9HRqt_Ǩ7}w@@ɁEF+'dzg1wM㼦-V [$].q0ykQ0= CTirrgN\I$q/x0V5z4em?~& 5Zd OOU?.8OFψbFJH. 폕OΎ6^_CvsvҫƥתnEz;zL~# $qRmB+lFZA%X}l@TуWM%㋗Q?XAq`\n`kiqqvT5NX WýW09&يVS[Vf _M ׏) @^I{JBw,g1doA*րU?Lqf7*rhl'] *ze~vqcӦ[sc:.ۻPէtc1)ܳ)[GPK!H˫"^ (pdm/backend/_vendor/packaging/markers.pyY_6Ox־LEb KS @0Z[Gr$yt}#ǖ!\oMO[ ҋ)bȝ}yȖ_Li.|[>[*KϳMIyk/P1%qA:"go^/K|h$(R^>eaa Jw 5ɬR(^YtR ldT>3Rvc5Gv^ ınJ(x-{^9x ln\ǘFv\iycP=[X²PaA%[ ơ:( 䄜3pȞ^1O k xfSvKc H( ̮L n4k)$kaהg.kw͓Lyr׋bn%,j$3.&y@`p!1<ʍgnC ?[}Lqpܢ2qa?4&FV\#2γbˊV~#1@8/Ki$ȭ8`5_gtڦv5͡6.lűV: gCƨs(E"-=7E9DBDhrd:'us  du2>Gɰ/a8_(1@0X3neK7G, 8{u; 2PsY8$ŧdmuǥȵQfYK u\M<G 8B|G#<ն1DZoPjڍ #tG;e̫5Qe.x2@w8e?~O׿ɨ!ˈ C{ EԜiroLWOvewkRR]g 멧{A~3a; F$d`ޞ n9w?ߞw JYύh(#{+[LT&[D(mU%iWV5}##tO>E M᪱h!R4Į<8˝nk\f7.ŏynҲs50=/L!J׬B?['VtBQ@q63.$Jf4/J4N^>Q:8a&5֐>UmEҬ~k6&`L }<'.']~$*PȭI-qXNC@Rͥ); /Drj>qߑy|:Z'&aGn0V[8 "f\H ڜzґDYتXn=3Y0 N{DV|S3azЙa7m>;؃j=MW'K萤o[<R< 4t7\SLx>+}u8~\ګ>YA?oxr>ՠ09c$!)#i@q,>ʆӈO<&с3p|  h2TA< tۣMmZ!qVLJSJm<V2g'NPz123\!PĆ hYY∧&֗[k:xzRm_kPd)d5@ps ?%hR>EO;~['e<1# FNnr̝.kw#7*3P:GX]bE(W KTn C&7l;wQ4L:zh%="z;T%ܸxz&PK!H{" +pdm/backend/_vendor/packaging/specifiers.py=ksF+jD4N>Tq{٩7+E+ĈB\XoW`zw8WY]ftQDQ4ZJDuT^ϖ NF?0z Hi7oc_zBMXг, 0IJneYErE#T4IA;cnjѬA?U"&kQ b:gPϒLgk4Ish$ϓi~ /߀QCr ^60Q!G%x5YRE6" k5?ˤPFuShd_/%0L?:s)~~ppbVjfpG7 zxqѲdQ2YQ7I1]#9n ? hH"Uz6˓nC$_UUV 60$ͳE4XJ6e4DD#9hh5WI^J8/C,Ԋl:izU2(QQ(UqsI0vqL6IS=|#97I-, ؿ| ?d &^)='Z䗴E_s8OKWRAK*Q b JZD'1kbRT}U4 ȍ:B;tWh6DJtbGq-"#PF%Pk*fEcDRXf@`ΑulX%"W8и@Lֺ zV*X4l.f*tyJnsզ1.-By{^2H6h>n=;ˏ Ώ&4[cUװ~*[8[HXߙX62¾W>{8+s_\$5zě<~ 3:b38ྑ'cJ!H%enч UJT%6 h(- FqN5_ľ&1⠕ZH, %wb.0OPyLᆒ$7_&<_ 擊eN8#;?1VtC:)G."f)p%%(ӔsiJͫr'L 8R%h13v;{} {Ԛqs| O OF=1>o*]/80wސiToJtl`=Tw+w@]5P$a2T/`8$܄&ngßv̓;CjSL [}d*TG! yl5RhNPd|a3Mq=pO0ݝD miconرMe~ #}Onؔog`*/9' l)+@>9]fIZ7{~94Ŷmi)%$%7eh[#Lhupc%<|{!v Hȗ#$`#atP׫N۝eq(k'/& @{ї7}^xe~7&!WÃg/|`/^<35`b˸7ze{c=Ut)}LpJK: ڗ Qs7YB13 EN<(,~v[d$qV3lSC}b?F&K՝xAx:0X/Ue<~??q.uFĵ0 odЦv97Zٔ,SP('šJ"*Xy9'ɑ=a>=cI|7 ,sƟ!ڲzWǞ6V`G AK \9/.({qB\\|h| ?N{\$L=6bJ`C4\N .B.cn}c/D37R&Yy}LJW=Zd^gQ㾣t;G°%Yh QX,pH%5󦄙7m{ و0s~" i87'Bhb/݅p2_ndKÈs\XO;_]R0/JZNSB$i쮶ԛ@vwɕck)T*HE ~ZlQC F}%5ҩH:q"z,?۠M~E/ 2\Wi,h]mKYB}2YNqdv0KљbMV6z¨X^i(^~#xOeYκ>K]8B6ڽ} (Qկ@*hqE4L;Vn xm]2ʹaD3@5)=N 3_mu+,ۓB$O[AVDSoC GۧnAMPɣ_M2 ]iJ/V:M!k-7kGEI k:iJE7]PDOXκAg 'vL֔ v!MBP* X66^߈]}ڼ;&+}'t$;S^pɥ]wY mA3ǖm3K7ي㐦~")4߸Ււ S-爯0̡lRBS/NTv+33^Ovz6kUVprA cֱ%H=ݤqLe~3QMlSd/T`D85}2YjA3U^t^> f]%y-8R{ѣ#bY[[Z(!AkD=hMpy6 ڊ DRU*Z:QW|1ltq7 < ==p'׋2>rL&jKXelOat`21b`pl%yVT6ZhDWt$3ڛyᆬs\,"O-3>::70L'? V* #o=c'Gc݇&Σg _2cXӣf@{1O3ep]SCEC,caU}v촅WI]Rǚ-Ns_%<<,;TdU#m%J^{3M u6sqWbU?aUXlU-) ?ha欵ɷ~zfS3[nQ04楒EfW`p-.jp LPfoDs~Éexi 5e_s' e29>>Lodi7o ?wK)t5ux ~1ձc `.2 a^2GCgWsevߙB84zP=TK;]G!KH#`H8et:{{-{CY (߶KMهm.xtf*8jWNf1>+faNO0v5_hrUm%;Zw)||N~X8ۜ$gdЕ2]KN\:N>"3&wM͈k.vd5r_H>433elˇ[si?goPi!ڦve%P(S%E`ṥm zoRYZY0>%a/xCpduatt]QG;՗3H fI;* 'k씥2OzB^:= H,FuY#F;wz,mߪ!Χ5g\J;6hyU6R 1.b»ח+u]{Qu wjp[ 'ڣswa2yr'.MN4ș39RφۛAmo =fjk,LwE=P'p/\ Kp6_dbߓMsh Pwq39XA7k Cp&%^ߢJH[iTOP]>xܣn)0eg~NZ&,tn(\ :4 {;Y hqYQo@c& !?d-G7uu߳n<ѻ ʙ$+Iu*Kt?l6} h`uxDwgO&Y;ع_V7-8d-Sugr>k*DW\7ߺ(6}+fb w>}e`&8&Ϻ˶|kad&MwT;Ar2F0?3@Gi!Arɯ 8iɌ}KvlS_̙3UO@yAPȗ\:Qgɵ߮fvvYºO~2^~؟ݯ1,kNѺJ*r5sԉ4R+:$#8SBG^}%l Gl6 $VkIdo`-&]n1kek#?lH)QcXb8WWk[$^(ږvX[jjȷ`mu]Lmӛba{[da’`V*Rea\W"Io%Ř גʶPXP".8(R7]쩧rw>w]E\?I0ӯVrSuRHu.;…{ 6kV&܎\UPK!H-tU@%pdm/backend/_vendor/packaging/tags.py;ks8+PtmEJm5U<*7ÿ.ǎ$P,tu% c љatoiAWej!Tc5*?fpp ?}> >}<kNeY|[Zd.^yc68_Dz5Q !Qdi){n0I` Bo3t$ i%OefL /XY` nȮ/9O@eI,CdaAS)rYv*qK볃.Eb`wѯ%Ldy4E$+eN >^ 6vC?pNLpTA0e)W|?TY/?ɮAG-xp湙er +~.t%Q^صHvO¤ k`Xr qy0Jl1I$d_Xo,n8ʖT0* W%Xt6RCLFa Y^plWI~W DݓDuAY(Ͳ|v\ ԪT`A@8̸B " 9P5(t%r෤pQq#qKE9/UxHt4Ϫ":0c0: ǠÔec2VvG%:ϲ%,p)@w7҈1QfM)+1xЦjr)j%8I$%ۛ+EwC-[K{~tJ=ºی٬W>X#8G}_x&kÈ;ϞcS&wV f!ogqV`È/GZ)HWa1OHwgCzI_(TyadjǬɨZ D d4c\1?.\JPʕI>CLoT^8+(+ p !DTFȶ~hᠰCOm=s5 0F͝cM0qs9Tikp]߭`~ u MD^#1)Dj <>8fW/XjTDcoE눽a[*g:tƆ1ۮ׭y\?{͚j!VJXChkk 1ה@6HFg`сARDb0C>S\c4@!⬴Pn5sy""401 TU헿yj 'p( ),KHuV<18އG\mk @{C/քҳuBG(a]ۯ_>;8PqKb|'H9`,tu#͛{ƞ*n Cy%(fQuq1;:[~0(,}ٟOhtʂ: pޜpEɝNz 6=vwkfEy>tYͮߤ3DŘ9Ï& *2hjm6fCthezV  \340r.d =ꆋidʢK5:} =O m . 0˕7װd4y)Uu>(-H*1ЯzU6 [XlaA͊%v+*I]4@*&UZ{kx1C3 7>Cih섙i)MAhBXՈV(Rэx& ̟QSDo.-*մX&]"aЮ>C'Qra@PI'*5AunNQm|zIJxm2-8%9~In$J{^hU8 @xzfbBeI2C% ȊV`_?pYZsî"*Jsg'}esF+,&)- kfD5NVԆ0hW[g ozllL nYvͳѨ{gteT\aT[}m͏K{# U[') MԱ]_L*{z2,jf@]Mc6B7i݉[l VZԚw}f+\O32[3_1\_k0&u-k"8B 62b +Z>4%%δR@$a%W"d3=mBViКH//_uvՂ67]@ R(ҡC #JURViOOpR1 TOn֧9CS Dvpzr]}W +&Za&E/gw>A+y{RytsCËh.X<=Vtzb!ė"2n zΒ[vXaz-/˰(%.=;6lE@%rX;ΘEyԋkX3C7g,BH:cL) _5]_ύ}:U[j:ڮa!eqj.\]k"DEL|o3HjLJUR " ն&=^6M%O-;hNxe9tJ&͡MmV.|H[JLG>$jۢE0 f}-~ ngk06 &n>Z4.Ә_TisrKHx9==iOH^WgzuoѮ줨z֛2?9t2>yM;F1Ź.\팂_'"岇;,np+;q5>ɗ6Gx^LuDž@_a&PvxX@Z q^-sNժƣ2̫J9ʵ;#ج p%ѸbTkEUY2A总ɠay+Etq6#bgRbTU n5w·3.+K9 ̜_tMP3>mc2JTE6F%JMUP;0/Lk@ĈUy ZMdZ UJl1:Sk xk1h2I9p}h guRvD]Bh&a*>k* OɫckėM\W\T,DP0TĪXҤ9faYMH.q[;c6{ſd<'}G#?eH.k #k غg=?M%YQVxH X:WkNuwbYzBqpMu^Ɯ}i{:>XDy%Ncu)7מwEVԢ9쟩5}ڬnRZ4Eoko}@m{vru۩y՜.Qevq.-6~U^9-GB6"Q 2Rafؔ |GAkJNye&l~Ӳ"N:SXFnXTlK\9GbZ=gnϩ2=@Kh3n??洆Kt-3u=PK!H*+$&pdm/backend/_vendor/packaging/utils.pyWmo6_qU XjdR 0}I[,iE:leQ#8N#E9Nb3"\;rb!1PZ!(k |aޖIJ_X_QH IIw.ũuX5mSK.bK\@e !C\%DkPےKU3\nK"|)(D*V Ue LĘu\#sV\'9l]λY{~dYA!I%șq&3͒5s;QIDb,H ?u>ua@*I@%)+^!3v"ȘTn 곟RVbCӤK `%C3|ѡS3ٜW\PT+2(4$%S1 {߳C _Q Dd0: SO,4:k 09ߓp(+ShG~ 7jU% qPx\?LmS}8W+c%hR[| W1i԰3$[<1e8&8P V,@TF$XhLv$[`Zr5`$M\P XA'Su/5Ķ{CegCmV oR,wbhq_KP ^!m*Vla.ֈ8vUIZGmӒZ)D/N`2d%p/tbl;Ϭv^7~okjM` fu9*Q8II-à0r8_)r<いA5Q/?4Hd|qsF[p]O뾫Ɯn2~tz>9NӰF9~*{rlR|uoǸjk w5.A71MJ:pHAKM^nV#jf:P{o',ܳ7/ ܈VFȟ]#u,RIs>Gf3K eo.ªPiOiRz_Ng^|Q795F-˜ʴ@Z|N ̱4KeL-Tc .n Y'*]5q꥽ɍ>~8)s {u[xXOPL]ov- Ogc]_4nÖOW^p8rVaRS:N.Jp=|cNx{iE7Ug@;+;"p3`s8pyۧ9-Es|cՓ, emh^l[9-+LiM1 w?S?'PK!Hީ?(pdm/backend/_vendor/packaging/version.pyks;ȘBI9P%ױՌ䃬RG("X{E+$SMbݽ}9;_$͓T0!C0?bXa7N9+.Ug&s:rOw4:q[>tj1HBJތz B2)9-bEϓͫD%*x+dtcpV{:Sl!]րT| ;$죜 A<:9hb!x)X7d9L,'+ Hf\åɜl^J'̡Ԋ+z'K/"T^inGc{w| ߉.>z=Gf nW+$D̄ؔntDϢE.!RALTpYPM&0D%<R0%)c IJX D1% LH ݱn_Gu)"|>}Eh//Yp LaO͠]B=Z}mA =pOk?Ma=聧D ;ˬWmxw@$m7e[ll'y}x PŃ|=zvT W[e&ݬVESz|tίɺ'܂zܦQm"&761 \,:咉{FEZEh 1_^j̥6(#F#} O#3 H`kȝ=_RML pfk W0q}5 |"Z4f3AC Am8;"?|<~)Vu d K-Xbj>UX[ F5*<* 9SJ QqSVhe27lB+C]fq)HgVjN6brZY_@:1tcFfsTn+)h6Z7+3e%vCiZxLڑTyCG#H3K(BL}&`/X}E=pey5f%xF>DO` 7yLX&?p9}S]BhW"p@-8B&w @쑝 XJX^L{x's*İ$\P .1@oaFZ$OGOGNDVXj9 Y˫QP)\9[%PSyh9P .EY[XMJ-gD..~{ȞhXL4B|e1D%g~Qz IGuGt3<-4lSq?;hNm^ SDXh6< wV (tX<X~P +t|/>Htf5Y+$+![SNT7 R[C 8e2j?k)MEx%1y)zvL&Y2%Xqx^lm pF_2-IR7(duEʙ3&ni Fz>O mm&u4,_>>[Z޸_>Ps'$ cspҷ8 k`{|o]FYgH2<[w KU)[+. i7R,sT$BcHݫ҅o@0B`z #^kZU򣚡-E7vMڶ5?I=Dig"%+R[:fx EUuWpEHF[(I@/ѩdqrurBNz /Eh0 ~{ntht+;:rUN1MV{uhr{q;ͩQR7.>`P Hލ.] 0 >\ h qX܉Vw uV'@=9Oi0W^ڰX~؅n݂%*NF6wP|DcCVढ़:5v cbRqƷKf=< ڣ05(% $/(L+nȝOot7FB nyuimy2qr k@:Őq9|wY[0vA@CX q?;B\E 4%61#lxM,>Jw(yʗ G>ٽ'͐&;ዧ׹sQ3 yhdNz:bh8!rpPI,+ ǒPk EJ;xic*{*Hqr$]7բmRh5)6x+:Dren7Bx +N jo֘hp 5'Syb_3ʄoGalWWId+OOZ"6VC}c麛=<, { )t)9FHJ*/$D|Eڞ_ɧTs[ՐP!9pPK!H7VY.pdm/backend/_vendor/pyproject_metadata/LICENSEUR͎0)FvZUUMb!AY$ q}u&VBf< {h߾s@<|ڏն_Gmi原he޺ fAw1cjp01{ .  dhƻ}8p {WYxPt4]ЁPp1#$nv@oOpqaa8T4iz7r)8 D +Y<0a%֓6B-,W[(-|y,X2'$["兂L,BPU^/ leD&6B9/$0X1DΘZ#}Yj HMXA`Y6ck/I $j+BRGil J2&1lU h"6 N-cK(rXhSՍ(y LFsY <{9PIpuoZR2*i4PK!HAsM2pdm/backend/_vendor/pyproject_metadata/__init__.pykoF|Hlp.Z+ZF-lTlffR w^;3;3;?KV6lc6_嬞EG?~<8X:Wv[<כn,6mlijeUlɟGYڦ"mUƷd'~/sf[{ !$IϗRtC@qty,dX^M6b.~íۼfkP"`>Fٰ%i_OQ .~w0aH9'zSb[IuU,g|8(/EBhfly;V&ѺEM[O'_4GpOݰz4N@Q́ ¿4U1G n(zmb΢(57/5 ߼~5MzFZ#ǷeUNWEYpR}  Œ:"1I@{,n3Z };;t@WY| k󖭕ẗ́?Ŗ +p+@Sg Ht^̑SDU #blMMOͫe7-X=Y#IƬsָ&f4F4DEpiCK x!$ߌBEB>?VJv?鋳{@Q@Q8ٙ/I:^G9(6 ٴ'δm*`϶ +U R-Է޳vyd0^dKY"#<.+Xy(ڀ o1 id#TơOp_௅ўZIkZHAzP}Ss)be(G?[?[#\ NytAH@s;PF֭!, ثҷ1`lLyB7"WY|ZrCa_l0!@M0et.~1'ADtN C8l4BQMDFrli{1JâT^5l&1Jc־\YٯL#6?7 -J[*/`/n1zvxr8 8F{0QX*YZgG$A/Ӛͮ_f5M8%LbIrc2}[Pn-5= B {*ۺ4 qMОr@yd.i(e&dto6UiRkuUӨO9)@IQ]cm7  q' 9SIďM]9S'e Zs~ŤILȱIn֚Y 7FƐP~pH,jw9,"<*#Τ֐5gaSKTCBJRĥ[Sl7xòhK'^ZxraGb biȩRj2OJxGx1@' ϷBؚŞ11L`N4m)]ꁁ:wdaX-VLhH)0%yta= ?#{cԘ33V0:E5't-1EËPAdb.^Q A驤FaӼ;1%Y!I,v!Qi Vk>CvBZڎF2I)Åp@H"`fHSx4N Xub uVt7q2j012~NHCG?Td ; ))] rcyq#zKgqᴙ׫Wt1hkpн`W~ur$+ntϵ$.C!İjwKWXpSbWɫXJQXO _7#|$4(TUn؛ˣ<]Nk/lMi :FNd5a:n5%vN[ŗ'HQ@?l:tЮ2,?xJJ>T<-ˢJ?lMot216S\KaAB dC(u%xBW$8:-*k07 8hs7} *X9AnZVCdybXE /9J/.$tiB$RshaJn~ˎ 5%(t4;a;$@tY%`[ܬ0%v _%ۼ ;#jcި5-[Jo8#R"%gRV곏[\L0d#5hxV*Q\p{p^[·K#sQ~ ^r.𷩆zA} xs"']wMiIM6qJtCscS٫Dtq}otGp'2Vrf0s[a^n/nc;nz W)~9S9?s}TI ;>8 ږi"g݅^:e_*t?yX#Stl' ǔIVwԈ:<rP~fQ$Sor'{bi[ix400*/J礂o 矰yW6|PK!H/pdm/backend/_vendor/pyproject_metadata/py.typedPK!H]Xuw0!pdm/backend/_vendor/tomli/LICENSE]Rn0+9%9FKtDT(:D[leN0 ps!a,v<}}`!t[7Л_85c0]phf:f|<.}hh#4"#wpi&4޻6kC`>Bҙf`v꽵bCa-aD`v;h} 3=: \g5#,AEO%Ɉ||qx3 ,^~[fH gpf ֲ߫1Þ |#^uԺWu(oSER6kzҨPK!HiiX$pdm/backend/_vendor/tomli/_parser.py]}`y4 ieI0˳8aۄ Z$^1]Ypej'(μ,T0>={3_5ggOg֓翌.ϕK7ݹ,X0yf<鵺pC{|Apë=\ġ:2]XċnyqUjvtnzl #/M9=|-7cxwi>,#P6z.k3/ {'b& Z Wc~)Kc쓂? #HC4) :XzLX9lq{#lg f|Xt{CݦH /jb;ނ=$'!7P`!M!1 d+]q/J9Z<炒%*$yQ]k-P4㶡I/HO{_H C3z͈@3l&59`y&]};$NF6|Gx[}h|*%i/aq$ m$ ֭lL+ V[.`BSgLn/kDĉӼEF)z!Q bג!Y 3@T룖ܤpg+o&-D6"C"2|jgh! :AD@0="O7 S.OA`%d3f?01:)H8DB)· l5!:,}2A0ՄiaAdIy -}Kb`D]m K- Q= AΰnqMu ʦ nA7k`z8QJ@@H:;ճk`عPWÙyz~W~CC1t!*ɣ\t$R=P6'tMmazQb-JR;^;Ԣ(k#z (inelat"09B@J$zTLWjR68=3Yso 08RXk$LtOh!^W#]yy&QNi,"H-ua`rbc/ o@E{$Cj+Bkĵ [YmUFoi LIbN SuL3np_zOEV rE~<ٿ +jL)UcoRBC x b+ʹc"X||GRSaL #+jHRi[S_`62TfW Ƌ|.=J~؃ \ %U(90긂Bƀ°_@Zg [5o,ӰZ 0Qeb Ԡ֙{G5ɨ^Jz6R( qj&iIBH2ql&/yz6rBhL]?=OW1H&Id``Xz(SJd Ne!R](j}' DV%j]*!e 93sulGY )oIzs/㵴/T2whP,gw[ ayz |C^62enkZ<蚺nS.AQOgǬ[ɠtǁq08|j6&%h̀Z ?)tć@KCI7es)jMc&fW$e5V%,}T^Jvgȉpu7L")RY[^u%mh8xX"d2s]\ )BNDDž k `<%!^3n]jvn|u(6ri , 1PMƼoU9M66co ?83W`eU2ȇb#ʓ$8U!Zr ,eǙ:b0vE o{%$e%2)SڲgVUEzl(Y[QR$i+!]4{Qm 2 -٣@5V~EMJ+fyC< >GS0!<,@5;fP]?^0(-؜E0;zH=sq2'ďˢw,uL.6$gK`G 9Hc7+K߉8L>Ȃ='5Fz)Csږ襢^!!Eg/xz6>*n~mrS(8SMzݽخfklR-P e7آrbϦ+oy^+,[+q !{ ĎX̰AU)֠vXԮKIimwg3/ (e F&;cY,}ةrу"pΑLJAf L:@d'zڪXB=;J*G&ywQcS=bpk1'6ڿ2^>G~z'AslΊmU0z;`3oΥ%Kj4V,~S͢ZvYD&GS#筊lT6rk9FnI^ڪS HdcJj4s3j|H 9'ApsکB3t L6UO+HRm)z$^/EQW@0 JitnGziׇZ\-;hI)&=EYشItob vbsB-< 0v-$j͢V.~n@^<(漢xmz]؅dcjIDsZJ5vvҤR>;bѹ`XvЙ':eڔWgaENl #= Sђx2M4#%!֚3Ib5}83l.GYmX#;np&Q)mFfpZTl -biUը}2l-k.Θ\r*ZM4e ɪ* Ul@.izqv]%h*2V+@u.YəK-#թgcz۾ҿƽ[j0V eDDP5>duXK-?} /&|ڵ`9'?&He_\I*{X;VjP yУ\=}Erp&4`DI|*>x&Bt mGL8 ۡcS!ݫ\7%zT(UA?5.7`xas/%8=f.]UϸOϪMv T7gm*:6JY87}K r,M>`u{C_f L$RP+k:d:Q]^=1{^FCx#ݹWd}@@P#{x_Oi3t6p b E܆gJPܱ$>}Evˊˎ٦~n\KBbp$ v7bo-yVe,7  E{ޖPS3| Qu L)&C"֕(c3*J :^ 2= ޿pyUit1oKqRqUm&n׍xbߊCW)Q΋#_~G,_ Lj%ZCHW>HĝVF›fv,B?'ٱ)7p`Y'[sWJSЖo'ה-ǻw?,wym[o㫲J^Ԛ:D+'^ &a// 1t6,Qg)O =0ʉ;Pyk7l^ɦC"(wlmm DξH` CZ-%C/[" QQRV}%'=LH9h4 ND@JKp5O왮wᢖ4\><a9޺r4zn0]Mwz6]7 c\@1߬KVk:9o/PK!Hu1~ pdm/backend/_vendor/tomli/_re.pyVmo6_A(_T$gka i-yHU *b;نaǻuo?~svR\<1 8Ǘy/fK hoyKPWRDubbtAJFǚΗ5\FFEQV(BzC(CqYIcYZFo 厔\ˌH7Y]&~ ZGIKMR[ nocZ˂̀҃.dw&~=ͮn4ݡ15w;Y!ܟ&%ρc{bTx^B`2io>qs2vȢIgoZ'yJ3*zˎC#DfޘqiAyjjSqV#{n&k7e4NRSs<]^uӱOIk$+e5 N>|:p\8փx>I3Σ۵|>hHՉL@(C;7Ӧ(3b܍Ec-hP%YVajHF3Yt[BrHnFv.Ra|+NӋé/~y=5.j< VR0PB(1y*-Ax=չ1*FFѹxH=˚ᾬP)vuQGb6"I[T˲oW l(hTm'[Ђ.4hzAС@y>A[M4R ]Ҝ: 3B>uM>JN$ʲ++fu零Cm.*|6 ;γkijFlVkB^&ozA7U^]zAht˕f XAĀ<7N ^s2epo 1< \'PK!H+)"pdm/backend/_vendor/tomli/py.typedSVM,N-RHIUH/Rp P053PK!H]Xuw0#pdm/backend/_vendor/tomli_w/LICENSE]Rn0+9%9FKtDT(:D[leN0 ps!a,v<}}`!t[7Л_85c0]phf:f|<.}hh#4"#wpi&4޻6kC`>Bҙf`v꽵bCa-aD`v;h} 3=: \g5#,AEO%Ɉ||qx3 ,^~[fH ߳ JtŃ!ObYMߣf5"YQi^3_TT 5]%(w3hH`kζynruOfdD+$SAIV / ~ Cٛwu}=}?ߎ&66s@ ,־P~Nߧ{T&9S I^W+ǀ3g?M$ȹTn޻y7Oއ뫛i|=y? v\\xO,M#ן/ޏQnhZTcCU,c,mM Qf ,kCP CP!8}`D0H¥ڳ?ӴlKRJ&ϩ\iz!Iyn {6WMMܗe%Br+) 6zIY(sDPb&m9XzIZ"R~8lF5_W,V$eʂ6Fi`;M2#?R"[F J6Gy5nU&N¬)R&X D JM]g@6CS~cY5GʅS F/+'wzeX;/f"zT)!ybZ*72ɷ|L?4.q`]CQU=֛.7\jbtW2l<| Ҏ&aY#@ZVp  "Wk! D Bk(KԒe&t=(^޸F\k4U4XÉ$=K(-A%T@ШSK9vdL&{,TŐ( <@+fhl)j2V1h'`y"+ݜr7?ᚤ%Fџ۽ {"mkqSF?\U[4d˾Cm2?߉@ptDAa[CmnoQձb3CU<-(TRǶF"`>5xW`$f/~z@԰x40gmJ ͔w׿8J@9,s6\Aȟuj F:/N{{Z{pǮW5eNё`Uęw.cK9 uGE/>xtŞR"r/[ /|zIiG`]uo%T2:AqtH4lE`J\]@JSP ި gz`b"Qk؝K.u BxF480'VM<~j8\]A%d#%ei:l*y´ʟXVqt齕g79NY CˈojYoPK!H+)$pdm/backend/_vendor/tomli_w/py.typedSVM,N-RHIUH/Rp P053PK!H@Fpdm/backend/_vendor/vendor.txt+HLNLK523* !r[[C ׀(?+5D77$1%$@PK!H CV .pdm/backend/base.pyk۶~J0uv^^oMuXK%dzS.mx1 ]rfwʓ6CP> zO1-s!e-˷u}V~e-\V@&̀\W{udeI[ U+7^*eJE)@6cPT]w⨓+ZԶ;Ko&TroB]w ^Vu`1^T}b{raP)H9E7uQv&VL'WͮZH]E vSwUHBFl rަ;ksf RU*oZ2l~.5QQ݊ih%}i~y"E IpUǸbWJvƴ颍vs9:FA^Gnc,r3dykY/=N3R\EK3\⩥*(۶{rIB?mERO Ly&.ha.`S[YԲP\ߛ,=>=-y+a igWKXJ!DF"a`Uf)rA5m 6@hBH^f~Ԓxx=?Ж "EqUd6GDoZOZ8m%ej_Rvdq kD1J۳'4Y*sC*~ t%۴z$= Hbp %ՊEgg`?#8QN'(_9gdzjȊ ֛wC v>X{:z=%N wmqVh CA\/BCsU0.ƩA8:ºKzUbs0vAT*+J ^5: Xo*ƲU.6. +X%(M:/$C+_/DgQR׌T\OY-3옦8!,dq0Cⷐ&.?߾((JFk ee:j;.ֈHU}PIf::6fZB!@Ru v K>`Xz%Y~.M^- -Q6yf*)TF#_ 1ɥ1e@<?<r](N/z).\܃I0ԩ:2+,0kNl"$IU@q`.N" Űd.y|{ ɞu%lO3eA~_93ư%)UiYW=7.V]8PJ*[ F~fh;XP2N!, P>(YE! fY+XJxLuH 6Th0tz20A ѽXhJnXJ˃k81![ 2 qD͓-c-셷s@C b,jM왗y>DU󳳻<GbI@$~Kq H@1+ %ߚQaZFԆнg,rB\TРZUˑVL)r1jnlЫSN4AJ$ȤMJHoF=5>=+Qrn>_I֣~:#r~-1o#(oW B֦h|d%")])t)^ l 5J POAdx rdw;GRfGW!&XOuts@4rK2X>MNI!*E&,#>K4>}fBv{.W2::,LJ#yRV:$C$4z @MY&F<.S%^4 GA RMIAe7XJ(xX OB~MN21V7=zY(N0"U9]`|͢fЏTDo= H>p C50+Q;z;+Z@U9M$ʁa8V},k'\);o_Q}*$dSux !/9ю+?X7HDUF灘|(Mzs.@E*<0FB{@Ν& f&hP]X}{kuex!wjBG+tHi 겯% T7*L&~N%h௿pYʞl槍i8gc칞0 sGL&EK&k0d55F J{Ih8;4Hh͂r#&Ioں wetR-`@L[8dށBӼ{e.D?/-+.Ѣ*NnLFW4T Q֩}i]n~nYlB!9|L]oUtݼb 1i6$/;.ؘU'!|hsi4CXE`; Q,N^GJ@Z,"<ÊE-#!IWAۛTp>s> ¦+m8a5][sGN6=A4DQvB-xD,gBPK!Ht &pdm/backend/config.pyZmo6_AEZJr{i.ohr=-:QWJַ~Hd^>}}GyJBix_̀:Us_2◗o~ZqA-'VQyȶk,\8auc+lSEY‰^f_tRES|m;" ෢^X^*4^l _hMr:)N:(-q7!ّ*ךtLjWiB 'gs?3|#8GTcctMaj:,^KdZ7r5ʲ옚Mm`#g` z@o|2Ӻ#f$1^x^ 3nCJ*S>hD-{5% mITfKhHhqVpɾMy6nׯ~`;^9L;/ SQ9'Z̶'} ս%Wf~]GmiYz@J"R sϡmǬm4=ǹo9`9G1}m0eۮDŽ9lXeLeeP*1Nxsa:u@57CI lfO)7`dyF9q zˮ=' ޚ>0=?m6ZXu%\EZrR -OB1B+4 rMTnŘdǧ!BƂ;Q>dcݠw v>4ql %ju-ky ȵ {v+.Em@հٳϊ,D0E۞d)drU5ccKTOO(XL[LB(g{a p $_*g</</Vy3Eќ*"[9-P6JfUu`a[`OJ4ș=a =՞*M=揾(,:^@<:7gs4ʅDGA0bo?|*C5^I[h}0R ׻=i Pn&M&k囫+h{fjs9;_ivkP2;?jj )ka8'C-:*mFgx_mz X_"sjmy kN2(MMm-w?QE#|QrbQ]>ҹ{{ppw?`7׹襛L81رK0"/2f Mc Sp_^tZ#5~spˀBdH/0uL]=5i忺}UdH_}5p]X"8'AaP)v; G~1[s֮'Å[dJm[>K[(~E^L7K< ?Y#J.D[h3EĜteez4^tB@U'R.^jCU}'GE ̋2`nݑd-d[<[װܓ rk,+2V G0ۚNKCHZ4PK!HZl@pdm/backend/editable.pyW͏&@ދIz4U՜('t5{lc춪 3J`x]qq)sB+5mǷgfPv!wE[Yr; F䍋d}{ghb+wsL&dXog?Gt+Pd/s~bwn& `gJ8k\v{҄^V4 E z8+Q94unP+WM j:{PJ̒._G D-!2(0I7뜻//@*4MY{Mμq xVRd^D/3TULhQf^[R縆Ɂ&0#C @S ';BȄ`}&4ȕf @d+ aߓbJU7|^>riy4|Z$9ɘ5m}xe}eɢ*Uk++dqK&M汿Nc)Wlo@n%K{Õۢޒ Go C8NY2 Ű#woQ@Ia[6>g.&%B @4\,xG~x!_F8?,ȧ鷏ud RϜ=oZP=-1"%z|ŗ(lAG'&$Luy-K3&|q)ڶaӯKn}HN[W#nnZ&$mp"a9e WKrGٝNPK!H0pdm/backend/exceptions.pyuJ0ye={TdA=`'eI$E6u!&!mNQk 7N`$ >*#\g=s}"sӴ dЛ-}2C|{6cD^HP7_xf,xVBun f oKp<|4pq5r\1u]mӬlX;2Z~ S ]8D:o1RSuPK!H9nUlpdm/backend/hooks/__init__.pyK+U(HKJLNK.S2s JJ3sR<žy%Eiɩ: @vE W||bNN|BTPIGA SR,PK!HQ[Mpdm/backend/hooks/base.pyWn6}WF WM HkEP@I#5M $ I&E=$p̙+7,˪5Mc\)Z$Jx!`jVR->nW lq!/svvsp`x.!H&yUV脯Pq.mF;]h9}YI@Zx0d: $ﴻ td2[+»*m9c+szBYXɄ{sݸ^hF`m AZVpVbg.LW;gD8z2^K1p(.ldg^u[]Y)(#Ji$h!+!y CO5uǮ,u=dFt"Ɖv-7KD13z\WkJc*0$ ^ 1m0ӽj,E3,mzo15 PP'S G&b wc #Q"Ƙ(Fi4'1c֮7h}PV—1= ޣΙV@%qf)2gsv]HR8Kg߲+`Mf9Lqۛ\7gb!`ՙ"C'8<fökɡ͚,.OsJ_P^ޙAWq6Bl.c2KF8Ȩ`7I~g4B ~]PYvOĴpЧ)φnNHY'%|gN>MCl \ypDJ1]Sd^S긮l,Gø$9TÉ\QʱWgK7)1|y'Վ55@|TZJqmҵpI8S vmsZ@]Xg~;yXopP!_|1 C㥿_;"z+k |0Z*){r:ҁLk؍pB)C K^S02uЙo)݆{'($عOռ1l/,3^ah.c% e6B!π!=EY)0dO甕WA^f?yncŌ7[otƪFO׍v0h.D!Y.Sb3Bl벓^AɹaN^f w"0$p.DwE[*ណ9z@筣B}L˖Zca& :0~X*rxM꠨yw`VB.UOwz37Hѱr=$ěiZ˒恠ef1uK`TmEwSI9XWEwt4%f&3q],"ju( %U]^`V {ҜIܟOQ+'ӓt9Ϙz(&ə2`U^,o,_hy xdrxN17i&k4:q$Ad=g4k#FWؐ.ZGɟG(5N V4['p@˹K i~WJt|O,kM+ m/nR7oK[u?2'>:Crbh,ª_Ӏ7`s9{4L[6lӬ93-W7UDrC]jbPrJ$^3n$1? #+B"*,&F"I|pa.|O(gGYwT붙߮wq맫|iJ=* l5ULC@"lK:=)? JzG)wZdx9+0}ЕyдRXBguP^zPK!H53m%pdm/backend/hooks/version/__init__.pyX[s4~_! 3k3Ki:iK:-oؖK]|߆$+K\LDQ֨F("yYsH\U\*\,ܞTnJ/NT⼐n|(x{$[}y*vrEu.י́@!ULĊMKz'WJ)Z_ñ*wO"$/?{kM\䩾>ʺJ“d=Y!k&$2)ǎnGbJH@MxYonAp&V{ͻ`,N`XyeWE24ϊH%?ހj"6sbqF V3]hF*D65F ?O9HވINKȞ^H<`įnNA ~ZFhiW!fdE\ZhҎ@;@\$:vQ_{+c AS4Äkn VXJV%S1 #%3cl(UB`hW@r9Bblڦ%P)xAMS,ޖ Vk>2P%folWTA : lǫCc@pd)q5hT ^Kri5-QK-{(ʟd= T;73ě z^\CTbvX2i='ƞAWMNPmu|j䂥C: sNJPkeSdV(|߅-g3f2$K?oSp7^; 0Xkr{"$ ax<+VJ?@ܮ[w2\,@[35^#kGS-س`?PC6T] /m8!&(bkqN Љy*V%</FeRK8-q"0dH.*wQAS~7gr'^,r'_Aj5N DMZm}9;)jpx%C8R3 JDA$k1:]Y9=XLНMs_]JS4+R mB=pR :O3 ['tR" YcKHs<=gJ3!ۇޥ%Js1Z:kni>2l.`#D@aa呂4tHepO_ ꚁawM;AH*vS[{]?P=aDQP\ s8}z튉 7©%gLC~FG7A1 MVhp~(@{Q1zWQFjV9N7A4!շuNGoU ,?`zHv$ К6'v2J&J O@D,Cg:8P=)/GH 3tPK!HQʑ ' pdm/backend/hooks/version/scm.pyZ{s6_e1i^М994ne)XU P]MbyިDdAYQ"sVD$ RɻѦ湨}4:̒FLT낇<-#r2F+Qܓ_m I,ȃ]%m[2$/岤jFzQėп"/vְ:#KXW 6E9fn[AE^RkZ&" xU⾁]Oћɩ,tLΓfmSБf,IoiE ~,j=YD}^A9swɈR+HB, &>$G2Fi]6yA}ɂy?&Ww^_]Sͫl#<_<^|w^_V}wZM/YK"Gu5Jhp0Aa@Rp9{siRJƴ& F E<ތy]Q8#Q`ILՄdy*)f8 7DJ5jSQ0\˺&eRݓY$IqTyꪄ(!\!n -~9o):_#Ekcv6e9VKZ_H@1X E`q&f["FWa0NI&2kp4ܣ[D?>`29<l]QAAC %є d0l+H.PpUOZK~MA`|~9˫ɗ7O31l,XZEn }Glac#e!2¬-+w_.]o9"hqK z_r!ɃKϿ _ϲ_JۉFF]frTht`j~ H!sIjdz[(S1-bz!XcI  QW 4xAtCyl)c.K8ajU:sؔh*rit jC;HL?Kl'nbNAn齩H+`/׎bXi}y#+HZKX`8\MĊr͓+\K&NiєkB􁽦2;r"-. eX 2KljIj{[Co~_G [|H]{Q tm̸?%]^h#0B Gpr(U'UXB>1SQW $S/>FMQUMϹU{ggb#%mb 9xc+Q;k$k T;4>I 4ZF3 Q{#٣g*obAU,݆%3j ʤL$ f@ tl/KB|hG].wd.БSxKאoԸ@F : Pв٣XY#jj\.y"(5+$4h-xU[P<ʫpZ.d,X,c3xDu=bqynǦ-! MV }s@͝8RFmO/[ύw:dpz MD ؾrcbmOdȼ \%xMe.j]FJ% MyNj8EH' )5 <ͩ,ٰ6F%?k]hhL&diH9]J|*DZa:+hǗn>S%e d bD7 趓Z^B4$DH-;1>,)xS 6v t?y06DvӃ]̓x uLBK S|P?*T>SSeڲAW>ZYE?*"^ţo*b9qQ}q -(6ԨlŇ̀ڴYK{Ny:F@pGs!#Xl9qF`M/%~Zg 6j4PyKH^cr7k) `J_ҟinyn8aT߆0Ϗv_>~RƒPAB-t$w#3,3B2yi1͝2O 'yFœ3 6}>4ۅEY55͸~ \7:M?%X5©1.oFzE}4.Uæ>~kat=lyWSu]r]t=w8>q1uGBRRKq >[?R Ykآ G! oq¯O *C"CmdB % #~-Ulkq}AfVUY6) t#k>X>b9uZФ<-3yӳYm8f{YS-69)yo*)%&Y %N5 ;OwVU}1̞ 8iˑPUݱ]UQS~rO7xWoJ5k_ RVcVexZE*0Y' }KLo'5dwsj $9Vu$U-6C5/)OΫ ;6E亲ꖤIJ;kս<bq?aN;%RKPK!HtYpdm/backend/intree.pyj0\)sB]="!6&Y[=NGa /up#4]H uI&ldl]sd:i/蓝+x>}^u'7 d m,Q1Dj;(*l-zɍ`:C_?tkq uMvbL=&}2.n2P,EĔ({^Z41*j^E-V# &L`'<ڂ}Z" xxi]Ir[NBr!Ej*ۚ\!)HM[R?FEAn Iֻy7WF%A/Y"oBL)QX9f,n+/ %PK!HҺ;ln:pdm/backend/macosx_platform.pykoFs+X :8[8ݴ=ĕĆ/-]r$}92tA΢_MA"A"a<*_< &, Vr"㓂嫼 iE t:EW, >9yZM$̋"͏vvfA1_Iͅwa29Ý<'$A(tq`UHytQ,2y,$+`-qR`yMU*r+)b$s i L߿uU1hϮYx˛c國Lb.X, QJEXi¤.35vNWvvn_\Sy6T၉pV!'g l8d0ĻKXMD%3y3> __.|A}w{.^NuIMy@~D|Lrw-xtOL0&ܛxyI$-@Ӑڟߐ+1 UyRŋ'Ov&(FY/c_,]nD)wbҀUz ,EL Kd HR\ ÄExQ@+Ƀ-DaH7 K>蠜B+ L04ig-+=Ťa\douVo!Xz{̅{ʋf0x &m{^ްUl5%7x&aWXH#m2G7.>(/PO "q nm7e|P"qͷHPoLn%ojx^_^^}9'׃׏AXPz_W/@Z1J)IO' NU`<fK(f-gB.Cm' yMZ'gkK>$ q[WwQᢌ.tzZk'4hu\G4[—K8 ,l9pFye>8"J Ep3qtBxl}%~& Ƭ[nPd~z8?!nU<e!aZ 4L3kbcӡKEی\Aq#&UL2"㒱[0JE%btCBB5v1{t*2ptmfR{ÅЏk!, 2_dz``ң>?7}S-]dA1s (BY6x{i0~aIVEC$Jڶ(1mr uf|$з$꠱\AC׹hwA|^p:(^m7] &n7YTOGQHu#^, ]8m*1c-y71Q&t}ywW[("rQe&8 BJY-PN3Tb'|J@1dJ5P1+<uo xF,{{1l ^O<,wuˉH Ka%Y)DN=ܢ[z%mPy'xK\k&hkPBnMʀ~l?O"0aSln[WVŬ]zFKBo4(KyO]0JJyCH ~}Omܝ# #x bE%l%R+M]̒hAz][Z$!:ŇپVYo)2뗉JNp0w+seBH)B2mSW+kAiCQ iNYU$AU\zsǐ#M52^j-Uq.7 nPtZVy9Zϛ[k2ZuJZ(eduD5c6)qǦ)i>^43UQON {b4t͸LoHKXQ65I!@0l;kfv>['%愇E p=3 >Rw+,I !K }fU9@bRMf&d0Q<1PK,*bawtb,aRHn,,V y1'U-T]/Q3?ǎ+Ku_!ŖӃ-lڡtSp%TNxa1l{`u,u K)z} W2@<idDԞk\{y|Xn+HJNDy$_c(+,oro%hR6S)fӤ"utSqTFf\h'v+F#/kGza7Peo]kfyowH@]#Z"L W&BL5!O9hwvruA8qN<be#qg^=O] 34y# cŞn9LEB*$&x9ab/HSB _,.ʐOi3q:a9:*Dg1K}0k3 3zv6j?~_:u9olgGs#@iӿ{msTfkUGDݵ)+=R3㯒E֖#͢!XB׫KhXRY =l@f+aPK!Hpdm/backend/py.typedPK!H aJ_ pdm/backend/sdist.pyVMo6 t(BfzHb-:Nk 0 +Q2cH*& Ec%J<ݑV K 4wJz r}gD@P. v(c]m;rrzWB,$1z }J̃qpԗP/bZX0D(3l:DxwOB3H.Â޸~d9\zJNhwzcF&7@<Um #-qvMT[_ξ_&r"{CBV&O> Z.,%x-(]pܟ}60ϷգyrHI!Ɋv|H(x! V/e7LRa4;,Euw e6|f bZ8 )g'xFŴUiznsTG-~ m/,էQB  A!W?{ν>8ޟ&0y6PK!Hܞepdm/backend/structures.pyՒ?O0wSD eT$$"R,;ܴq10!q~_՚8:׵9Ⱥ1qI-c"u#ܫܶz™6YJL4$b^h2=^[ ޿ )5J-EUP ' (Ej'q碇I `bQ*-%ib}TDꫨ :^ xP xjhi.TaϬ=%^|>2 P&a8!;Ц)}'4NKbF#JVt b -6J1%Um^4ejGcۏO Pߧ!Σ]ʿPK!HNIJ> pdm/backend/utils.pyks; %KMQTmklǙFq1G(^;@{$3ݽ}?Kj˾s&VQjlW%Yʯ*㟴OfmFj/tV^WH[\YNᩞ5R_ mMͻbAk*[H\n,~ʫ/*D#4NyOŲM7_iW#zb< \_ mɸ^NsF(Ttgޔ\b7'D:T5`JxD?LLwh75|'0qĢEYlEfE]|_ ;v*_J41ۭ7j( Vu` P{(kFd^5k6ߛb5/:tR+z5sVv+6DN:>%K#Mޮjr"^U!;ȵP%N4!9뷼$=)e]Q0ٰ8ʁ+H.: ,9(fe ^t/j eȦ18bi7[3s+^/Jl:5)Ss _|A{ډҩ5E'0T"I7u]?z13- u\ã^l_>`de2 6JA1w- Qw/lc@hAuR@%owo^}}ΐI#.L{01Ʒ7\Ry[as@/A0'uj-LۅK8KӔ]@PBpJ6[|e\#~-ط=ԸZd6]ښ1nA; _HrsѓE2̋3%DXV4)>O.I0QcF2dݢmS&V4:v@ zTJܶuRc 7PXFo=}r:#H:^i,`2 F_@Ls0ոhWŇ){i%?{~ׯ)5A@`P +7ߜ>f\t6@ !}}2d~krA~^p_>Ck2=MʒdoE 㴕(`펽F9ڞ2P0i,R(u*&=,gZ+ȵN~Y~k&:(\Y h!0H&AQэPYD錭)W6GOmzwC$icgoxůڟ_}7o? f$F7~ÝCΗ}S4k2"ľ 7bdWZm|0/ٮ#ō1cq:3/rg_5ːNM+*J伳~o0|6erh!A䕂0m&H0+&4:S(~]p5G$3רxQ R!%/8'[F#{s[,;Tb_d*K,&5yałl `I)Ć$LK0;-WiG'I;y~tŗ_  94N{miuoWӛ(oQ]}fwNJ^EW!e]` 9ѐ*ox[[TA ѵWFQ8MR^ Œ\pfqEQ0{fuy5)"n.REKE3Bôb!:FEk S;[d~uxU§k.* @E2)Iqu),yM@VInA0FdASěL.޼_-'g g<+n s}E1^s3jJK)q]!4A4!a'þo!")vpe J':cg|~X;a;tvr ,p] '5oGI %9Ւt؃M>GŴPsqr]Hq8&/@=98 ZeU8uPDV{ѐ!͟ӊz4|M"_?~zߞ\.7ݕ-gW~(wӳ c_1, ږG+?9RWE)XiIh7`wxTe*/QN \ M d@@Q<*ǜs]z0R NZU&sT5a1N 6ͫ[0lW@gF(>I7Qj)"+؁)|k.̲ɍ|2S\ -쁊۳Iqnde!y{l}9i DGGpWjw(rAK)ai*\pheC=bFWj spo/p`*hDa4aMa~<6go,a4lhl<#QZK kdJ vJKI~I`_Z֟pXQK31K,il\7pZs>TֶXBߕc39;3pcU-o`7(&kjo݋+îZRv&wsZbTcI!qŎ 3o&~ .prxK bX*LL=}(0R|͔kY@O1P-B֋ űbl /K!遝<+``c :2WuJ`&`KWMD75@̕76eSVL(wcf=+|,nm>Ik֘6=[n%zߧ(&9>|06̛.^Ij+p0ܟ`[f];=fx+j: |5!rZȶRCsTŐDk^l8$pVږ!"5f*b:D1V}wzk'9ʨ5ҭ_~Z9}ȯ$hxi|;=xhO S_A"vLB X*qUӝ#+JsJ *`hRW(ް> ;z:F?\7T%+3Pꔔ3sfUQd# $kօ2Z7,+WgźB FtP(^`|ͬr$)urY6`aoD`Vc sӆ IsseΆf9IzNR A$><-vuUԺ9k24]>W1ЕF-y@Ъ6>q[ӶshF޹uha. u0͛$~B~iKQ[nװ.S}8? nT2 nQeq,rўН+tj> oNO.P/ fH,\_(IQ zZ#}oH9iW'5]cTIڵh&PK!H=.| $pdm_backend-2.1.4.dist-info/METADATAVms4_\:gzp/6eC[2mK&)U$'g~=+ibh:,=Ͼh] =Ϲ/h*WQt+LU<=NnIaZ",j!sP;&gWKhHy0P8UmYkZ; >zۙ}m|^q!Sjt`~ =%ѥP9bJX ) 6*Akabu^gRdt8MY_sK d %WE IC٨r_|voƻ_8z?("'|e tp/޸B^ Fӡٮ0nh'Ϳq:1le+W V .Ja| t}&OATXUn0-U\p "3t&Dj)xRϞQzYh}Gx77r?&Q4b6i& URci`kVʓmih[.~L)so1wK׎e"i*l+SufQhwBָZ߭]^m> z-G:lYKلD TXS Ckȸ.i&х'O;dYU3uړ!nS.7 vPK!H*s+,pdm_backend-2.1.4.dist-info/licenses/LICENSE]RK0W8Jqko&1$cH qb n[ )| i we,#|&"n<2VBp~ݿqjht D g;}l܈4@2 'c4ⵙ,wЄ[ tHz7,f`n꽵b/&ZHp[{p'wW =0$L@>8kX~pOsDD,*KL('?A9뇻yi@kO&q.ӈvtW6+m 0+Ek9J3fo;gv,WB }am3|xx pӬ'_ l kz`k|/JV8yivVeYiQנ4EK5Y&3,W*B$5 HN%EMd|)siv [ISJiPqmdɹj+U ϐJ(DiPk ^9I1AAkkgK27) \ dbF)dьn`T"=HURTF3ڼC p-kZJ"aNDqЪ់7x'LjSķ'PK!HF"pdm_backend-2.1.4.dist-info/RECORDDz,w PI(D%/.]p}z=u6MX}Aïݳ4+Ч>;̊[7*w.JxI'M (SPBjyq1&ĝ郞%U?QU9\#o[h'=*K{N5u 2&PEDͲJx Lo\4_ًkg}dNRNQČ0?TٙOa 'Q᦬*|[5EL*(Urٙ`L[=&ag1 aRw|&݋Y !T,c+Ŀŝwz|"KYBаޔ%h">n 'ju3!aej5rx渻Fajɶ\oc,g?@iɔe-Z0s_KaW~WD%֝uDWWiex&xzMv6\K)FO˰6Q_4.jǬ><ingNw,l`U${T?+7Qk< ![(lQqfAA R4Ym~R鼙þ Am]k 1OSZ0'q~h;e:)*EölBEY!kC\4=z؀̀e|sEDxrDr~<.Χky ^ ~aNUv?^JD=d%&|qӯ2L!g^.3:sw! dQI֘osB~_>jdKxdk/׌'D0&mקGwwR{>Lg%#(%̙jbm/!KNhS}F6օR i*|5/z{#˼VDƾh  -<3XY^n> Per Ss 4Z4M ScZu[ KI,A1SoZ#V* ǚsFnFh4:gK&gt46y 7fFlo/E *%1` JGx`s^͛x6^_u]91O}YQcv4Fцx+7iQ96ntN8K0{I[3w;F70~Cal~UN˛I-|-JҋI@vC]B-ť!Κv]/H*wIyCOwG>'Fɝ dIWoC=GvZ\23wj)7Ɩc#rԣ[#$ )!CTOzp,\-NQhȽJ3O6yfתQz"F{xhְxZٸ#yBػE,P*1j+\$L<]wü8a4PqRPBS9{tcEXmum˙q(*`h{vIc)0jEO&D; ڨ0G?cx?h }>0W%;a|s!1Od'e`z!e$)qOrG)Q+?LX% [I;%.} -̂G-; kG.Knxz*>nc?G'PK!Hb pdm/backend/__init__.pyPK!Hpdm/backend/_vendor/__init__.pyPK!HҼ%(pdm/backend/_vendor/packaging/LICENSEPK!H ',pdm/backend/_vendor/packaging/LICENSE.APACHEPK!HP G@)pdm/backend/_vendor/packaging/LICENSE.BSDPK!Himc*pdm/backend/_vendor/packaging/__about__.pyPK!H)pdm/backend/_vendor/packaging/__init__.pyPK!HČ )pdm/backend/_vendor/packaging/_elffile.pyPK!H |W$" m"+)pdm/backend/_vendor/packaging/_manylinux.pyPK!H} +)pdm/backend/_vendor/packaging/_musllinux.pyPK!HTܨM( %(Z.pdm/backend/_vendor/packaging/_parser.pyPK!H0`,7pdm/backend/_vendor/packaging/_structures.pyPK!H-+r9pdm/backend/_vendor/packaging/_tokenizer.pyPK!H˫"^ (@pdm/backend/_vendor/packaging/markers.pyPK!H&6Kpdm/backend/_vendor/packaging/py.typedPK!Hc} -|Kpdm/backend/_vendor/packaging/requirements.pyPK!H{" +DPpdm/backend/_vendor/packaging/specifiers.pyPK!H-tU@%Oqpdm/backend/_vendor/packaging/tags.pyPK!H*+$&pdm/backend/_vendor/packaging/utils.pyPK!Hީ?(Fpdm/backend/_vendor/packaging/version.pyPK!H7VY.pdm/backend/_vendor/pyproject_metadata/LICENSEPK!HAsM2pdm/backend/_vendor/pyproject_metadata/__init__.pyPK!H/pdm/backend/_vendor/pyproject_metadata/py.typedPK!H]Xuw0!ڮpdm/backend/_vendor/tomli/LICENSEPK!H%%pdm/backend/_vendor/tomli/__init__.pyPK!HiiX$pdm/backend/_vendor/tomli/_parser.pyPK!Hu1~ pdm/backend/_vendor/tomli/_re.pyPK!Hg#tpdm/backend/_vendor/tomli/_types.pyPK!H+)"zpdm/backend/_vendor/tomli/py.typedPK!H]Xuw0#pdm/backend/_vendor/tomli_w/LICENSEPK!H(~'pdm/backend/_vendor/tomli_w/__init__.pyPK!H)P&gpdm/backend/_vendor/tomli_w/_writer.pyPK!H+)$pdm/backend/_vendor/tomli_w/py.typedPK!H@Fpdm/backend/_vendor/vendor.txtPK!H CV .pdm/backend/base.pyPK!Ht &pdm/backend/config.pyPK!HZl@pdm/backend/editable.pyPK!H0@pdm/backend/exceptions.pyPK!H9nUlfpdm/backend/hooks/__init__.pyPK!HQ[Mpdm/backend/hooks/base.pyPK!HM%pdm/backend/hooks/setuptools.pyPK!H53m% pdm/backend/hooks/version/__init__.pyPK!HQʑ ' Spdm/backend/hooks/version/scm.pyPK!HtY"pdm/backend/intree.pyPK!HҺ;ln:pdm/backend/macosx_platform.pyPK!H.pdm/backend/py.typedPK!H aJ_ .pdm/backend/sdist.pyPK!Hܞe~3pdm/backend/structures.pyPK!HNIJ> 45pdm/backend/utils.pyPK!H'/@pdm/backend/wheel.pyPK!H=.| $xOpdm_backend-2.1.4.dist-info/METADATAPK!HCx^`!Spdm_backend-2.1.4.dist-info/WHEELPK!H*s+,fTpdm_backend-2.1.4.dist-info/licenses/LICENSEPK!HF"#Wpdm_backend-2.1.4.dist-info/RECORDPK66T`pdm-2.23.1/tests/fixtures/artifacts/pdm_hello-0.1.0-py3-none-any.whl000066400000000000000000000025711477560627500247460ustar00rootroot00000000000000PK!H"pdm_hello-0.1.0.dist-info/METADATAMM 0D+[EK]md/1̛)uY+`Lp҄-v_T%u^H.{ػX/R$U;hj!C|c1/_jozr,eY},1yJ?OPK!H><[Zpdm_hello-0.1.0.dist-info/WHEEL HM K-*ϳR03rOK-J,/R(HMJLNKQ033 /, (-JLR()*M IL*4KM̫PK!H:?*pdm_hello-0.1.0.dist-info/entry_points.txt.HɍHWUrl<..PK!H8:W) pdm_hello.pyM=0 dJ*`[T!qڠ2!NB '/r!XYA B _Rg'7̹KstgFp! 'Ἂ̱ű VFώPwO*8~HTQ?ζާR D.Z8&Y=9fAOmPK!HH%x pdm_hello-0.1.0.dist-info/RECORD}r0(&r!BPYUM%d L/gE˺I\XT}x$&0)()đ# 8a2hf$En֚b?{B܅Uƿ"8n]3(nN;_eotZ%Z>|KPEFbrN^pLG! k! ngt3m2QO#n <?~PK!H"pdm_hello-0.1.0.dist-info/METADATAPK!H><[Zpdm_hello-0.1.0.dist-info/WHEELPK!H:?*spdm_hello-0.1.0.dist-info/entry_points.txtPK!H8:W) pdm_hello.pyPK!HH%x pdm_hello-0.1.0.dist-info/RECORDPK}pdm-2.23.1/tests/fixtures/artifacts/pdm_hello-0.1.0-py3-none-win_amd64.whl000066400000000000000000000025751477560627500257530ustar00rootroot00000000000000PK!H"pdm_hello-0.1.0.dist-info/METADATAMM 0D+[EK]md/1̛)uY+`Lp҄-v_T%u^H.{ػX/R$U;hj!C|c1/_jozr,eY},1yJ?OPK!HP@bapdm_hello-0.1.0.dist-info/WHEEL HM K-*ϳR03rOK-J,/R(HMJLNKQ033 /, (-JLRHK)N IL4K-̋OM13PK!H:?*pdm_hello-0.1.0.dist-info/entry_points.txt.HɍHWUrl<..PK!H8:W) pdm_hello.pyM=0 dJ*`[T!qڠ2!NB '/r!XYA B _Rg'7̹KstgFp! 'Ἂ̱ű VFώPwO*8~HTQ?ζާR D.Z8&Y=9fAOmPK!HFx pdm_hello-0.1.0.dist-info/RECORD}rD@Įf͇%\h2,$VNCם˜ØŦ؞*p3"$QRs0n@H-f$&+94Ez讫-Jh%+뮆){`NoCH!bWƪ;8IM 8 zR1or@F{!Hb1t/,=ifC>{/NgZ.W2̥udr虿*tTPՏJIdO*88QIQ!&?l,3=*ıq ؁-ơ'k*DCھ'n]벊j|MI Aݞ7|؃a Fkhmy$]XIŘx4NXi1FEEtCEw]f~nD>I\wȝߜidڑoPKET.%pdm/pep517/_vendor/boolean/boolean.py}w6+i$dﺩ{ov^;}wdJm&%);$|$;iwNH`0 <,YZ$Uu^u..i^^u,E̳kbHΪʒ:k/~>&?x~)ɛd*O 1Kl.@s/u1kӳa%s^.TZdQ^Y\Ժ,^Vedqܔ^fvqyho:]d,KqVMˬhF 0e%r/n\ p,j$mYǏ&rձ.gnm_^5I6Hçy\?Vi̪aB/F\b#H{?}t';x>yVο?xzll̠=bݬl2IK tZuMغg<,F{Qw[8733u^+ } J8I҄oeE[noSvh1'&>>ϲUHIUU-B0{0a7cD;c P8ZgoVJ*i{yvd  Ĺ4+rXn3i5wsz2^dx"OgT@Gh<.<<ſ ^| Y\I<=jzoEx^ˁх{? G?D$^J/zh?8hA<~ɳOϟOԓ4 \s'' ㋓PNOh/q3]_Hw$? +ۅj[:H"O8D_^vߡ|h!IBDVgW(i8 ޿D!$TkQ!jXi!wZVp,׍kH#0W(ܸ7[uk5I 'Y׸2_Ggg?r + hyeR x jj:/ H),?5LYUYېNh4%Lg:fMѣ@t$ +,`MFML̑%KX"o&~-. e 9. F#+dW'4B!_Kڎ>Jyϖ52kA+C' >@OZѣE F -~9} S05es͞y2mWx`~YhTfK)TF]}5 $Cʔ|ʚ *-slje s:gk!D3n,|BH,{ѯ(^3M6&D~o7?@M*3LLikt%shO5^sH08FYR[yT'JHa%Zu>'Vy>*8֓Pƥrj@o7MEIOn1"E֔JԾ.*FFUH9^=hhlΪ"H+RTl#6# zIah8Rhz3 ~6>l2!L@x4$N"q :i]55i=X^v)xw_Ƹ4iz:!koZ[aMszbs v=Di۟4[V|4iEJ5QܺCzAMqlOqJysmW}XVF=`AXqW﯅ -YBlU)i6EdB+ROBu$ak U&ꍡ~]^m 6")Bilܫ?]x4@@Y_>1齚#%.I*Xrp㺳Wvtv͖;.E^%"U} (){N-@Xj֟ϚX ͓ˁEP3>4Eo/)ӼO>:4Z:dXjko5v,ys+T QT@ch68KcmGԋ;p$`NU3CT wSb#=@:}ܱ߳;g̗n'Ϯ;״@At7qܳ14q`PΠ{)䜉`X>هo;Ytn%Gg {u5_=Lc8W*BOƧdVc{ 6^Mj<9ܱi:i &CM~71(8PNJM"2 dNlm.K\(6 ,~TDܲeO^W;ڟT5^pS>ف)2O7[)__UxV@gU3da#oQvOW>N,ݻtn -2Rzw࡮eSs7q5} g]y)2U TqBuNP*;Q:x;po*+~EhJ"_ SwY; 22eݻWy.^7yUShe$igmmޣ,F2 /S7+' &c΍k K=πڸeÕ+ ,xu-J;zbݯ﶐H#7wj,[B@f:'֌ WGb} ʽ!'qFjkUOwCçP颶Q'!"Ոs7_ظ^fmwqʠxٛ_!{?7gIEx Pd77o*0ϙhK\%"7Ԋ/-7hRwɛgwF0 pI[zkDzVS * /댪 ϏSRA6+nJ4M-&ؓ6rڕo#cLPLλB@֘$JY嗤x /)v.jbISE{Ev{:cNvtG·n$$IH7,UpTfc:Ŭ >ygXgb,0ɉ *-rR$]Q n/h\q\5œQ<(3]PA S#N"~ʥP۸Co[> 7wgW8n$d ۧ 1^FؠE|65t0KpMkC+[(GٽItСqc Tt̹7WƣA\:x5k>v:w:\MDg tbͪ|cEλHZ5{"tc#0$NtO8ᓜW)QHNjZjW7G d?ή]#mۑ0[ET5PYB]}3& l|id\gCjRi1RI9LώtLaL&߀ysU]8 yaP`>&&K28UDrL-5*~]2pE00l#{Uuuqԟ?1{Yg=s pu+Y[rӯGVZkNS2kl.ȝz-;@uNl|,&y0KTsWi ˼֕N&/x!M96/Fx2}bHնA;kFGq;aN+S:])pr!&VIjbCx}Yws8 t[SZanƭOjWU*Ck"?LhExkOz֗:k]eq;{v3rT ĥ¯fytPbhդv6ynX׺dRSdқ&iU,Cy#]vY ʬYT=ʒ&'d0#取NkXs֩xչSPU싲gnҋ\) 2|אN0aW8љ6@&㌞~sژGKT4W%<ҦOAL &44ij8 j­ ރ0p%O]xY+q>[(ֵݐ`):i::T#wDL0U,4=e;OXh&L5ш|G͢]`&XWiqp堕E:3/\_^)8YHvxrzV?s/e#k(FfNUFA^FhG}ٲd+J 2]2nUtC @V~auݹ^ؽлt>7"'h (FS&~1!-2^I4/6j Lpb;Y\%Θʷa'VIRM]AJ :'a8<t 3K4%ØrBib"KP-Ԝ+]A>OgF\y6u3=:UgiȨp˒l {O 9n16Yȵ8kظ/)iqh."%5#[k_ӓA7ԏ$,8Ov⫡v굦!Ug.I< kkf-ktdNTn>JJ"ALkBZi@!Nf#ۜ*l  D+.0b*T^.YJV(%¦cL+DPPy6+mh\C)i+n8L7O]P7_ɽRr~9ɨh3< d~P/*)MaRQ;*+O&h"_sYl0+t:F9UEYݸ{ՆGBۺ: <!0z)B|w^ve'" ԰*GYŠ`mhQëöEJ1d^L&+^5/êbYY#y#A' ~H 0%쟓 :ٖA{V:*􄇯t7v >; G݀q ng pg4 d/GoVn,)!&'zT8q/C Dvp4J-H8n"|ډŊWūGGx^m aI۹L>?q;& ٥0GV$j( E Os"#즶7`p_Q̓k߾,]:!3s r b뒾Pp "K>9F#K\}=@HS&{#q}ӗ/ 4@"BrMU苘?b\chCr$uZ`<򦆽upm՛@ҹ1lhu\'_Uĝt{nU>JTtnKPf=QnB͹<KzFy}*~& ~;_u {Bd L_hG8fޖ9S찌e!`_HrJ*ԨMcG9 M|*`1؈]JZ/JF#gݣ&rbSg>mDpOgУb0K{A;BvhhxmnkY/~ Uh_[Q-p^DG-Dw/=Tp5;7=ښcO"8ة]wQU6hW Pķ]Q;# 6flaWW"P_LE"⸺p_[tֳu5xEӔm |^4Y V}p?}郁c鯅4x=J63{ ;xE6=j.zŕDMC ,rqCKE/)V|/WʮsQ|(5\[hF7>q20HmQ F1ʀ ׬N3 N4WA&1MJ^=D޻9ʁ,ոDA~ڇ?2o3+L1ypZ#7Q]oQl{j vBW]-$QЛXi\RL8(P0OQ`H /vVzr5Bcpc3*hVzGץP<Z;"İ-C(h]+MC |Mf֕Ke팋K*8TL JHEɬpdXsi丌Šnk]Wfz% `˱qCjd5u3vCM] sC%<8ʳkzeI #Gϒ?Z"AZ&)ᴑds)W]l ͳ@^v@r/]'ƕ`_˾Ea10/>yz͑t)+=b7qsoF7yP( LCB͉֦|6rۣ^3=LK4z(9LG,܂ im\ Qg>0EM$0+ ֦l4`q #+LY %d+wNxJ0YF9}ڵ` D}116,oR̴骽bQ0kpFOf@-}c>m l@$8/9d:Čo9/p; t$+,lȈe?c֩9Ӵ1W;ﮟLLqjH7G0fm<; ]OoW녤}BZi`J 9RlqlhQx 3w9pn5M_Y饈֟N ) *|ѝ"Mz{[甂ՌHϺjexZk]:]O@?ԭBϛ_ymDS:): (J?5!'GmxQ;tJf14*90o56/O[KnFp3ユL'd1 =x\VXMnF8/ǎkObDnOj%۹w&?K BraWV=eIȶyb0yqϋ icVcKBj "عLu4Dmo:n.v.vjE'?͗yD($O'cηN VYnUޛc `ʗ$z3Hզ[|8Q_RD$s y#4SpSjw@\zs`>޹MqS6ܔmgΉ4i)YC{yoZ!,\Ix$-t]]yD+i@{-(ݱۇqG$H|p0TDX6n\]X:eY¬r)}X+ _j]F#iqo-ɳƢ,=Ǐ`DC׾ ~C|QFkCQw -iNhؕiKT'qFkC;8 mLY7Y%n/kB6;TY|n[z,VƤV{xqKx3"D!a];Iin}ؿ34^z$/&2ؑk9~NeUӬY1l#0c'u& 0m{I/%%(6jI̻3!FCU&[EpV~;@SYs'뀫l`vE'KXt"ƷQDo(I%a瘡2QT~y7ho+[0œ}N~G[95nRl#@Bv*Mzu23v:0,occ+G-5M6][5ixa͍ӚY]AB!Nf,, M,͜mhmumi?7BP`<!{йOsqivNA۲E0EN[ΨXyT_u4BzQM=p8Vabwϖgx~Jٺnq.ERzpwњ 1#< E(NgזÅb~5vO (KGT enojv,+3@r%<Do_AlM:Si|8޴;D 24hXwLZ̈7(!W;NGT_E_-E!"vk(x0X0&}"IHS[4i3>/s)':Mx]Fbi2h#3%9‹FѪZ HfO't309 ?I(+{zV7wJz0?Ey=u 14!2gڼ!Z"I#B t t qdi.J$9^Ɲ~=y Tzyɏ>T3P]P=Uا=U.yCNo<{SaJlaT Owdӏ(6 isܛIO6?Dx}6F/¢m>jbϣPKET4c43)pdm/pep517/_vendor/boolean.py.LICENSE.txtRr0)vz3&ޠ[I489:hӷg%i {-~>8𮽆57_]{RCߘä=4Cr8@Q'MDΌޙ; 0ڣku<ٙq/po]?lo{ܛ i8 {O }6vL! Nx+i{GA٧pui޴:;3&D \Ck 1 Egh;hHO p7|.ڙ0.d$ _0ۂZRxlTENRxZ*fxpE$N^[wRVU )2VfEr@l)FX:|+*%~+F!s5G2euATɬ lE ##5-%)? xQIf LeTZe+RX(E3DljlKɊ,L#IV 1YϤbV1hIŚeT&1ZQPjbhTTR|FyL*&6 b)lE4&EBea#tQ-3ny@0IqWL uv~L&?66Y}jKvObd$PKET|!#pdm/pep517/_vendor/cerberus/LICENSEUˎ0~YMYtKNm3, 8KQ޾dń |:Sow.@>;oٹ/#eУ70#8 3 ~?\x ,_vLy WO.Onv+<ݐ4?Ӻ^?48}&G݂G5J7;jp* H"Zi(*KSi93&UT^>e.ڂjuf'8V|qGTLJn%3f &-:|a2n'*XΩWf˷7@Ea[B UBY}MQبA3 K"Y[ðZxok21DӉ[dRc4HV ^134(l͛!Iɭ@̐얘{H1; ]SCPKET] Ja\a0|2<9 YYW?0k&AN_s/-^& ȐX7EU(&6d2[OC!@]itJ:bGƜIP0p/,/|Xc>{R%"/b|X(.5<| dk6ǽ@hp ߐe-yާ,HX5² "̀[DLa?bW<@+`7addEZ7O%ȊGbDc2/MO<XB<}tfIw߂pg;;g&lֆAM?xwtzzjoxFӣWn6qE荀؛x(R7]yzvA5u2]znpzX Jd #n8uF:_"(NCvc_[Y$KG8R](?kʥ繁zur ӵ U4Z’;FzIyE.!:Q_|z7G'-^bb:;:C޺0Ddh!D<#̣野!AG胗:9z:\S:*S BΒxۋ/LΎ޽E;<!N;1te||hv̄Ϙ!Yo J=缇Oɜjl(.Tf"WbNt?\EȍF B'NSϦo@(CV`Gm`( ?`Z`H5%@( U"Bz/0ԓ :^ vEi'Et y0deP CDBU`RqrH(E$j_ $WJwcG,  ,(d2cW~.3:*H6 9VCJ出2&xnHKH r 2SH];U&K`QWHß9d1d $\9ms1ܿΗJ_ >h=E"nd8AV~񠢸Cgo$+ZAF<߱uuRVAw?5/@G1@9@eQ9$ڼMf(|MKD|Kג#m 1$r)؅V0IyV@@m +AP_xt!=oid]_#ͳhQ*p(4cCUElWQAڈ. }ߘ ƒ;dVl_O~]P ؎ hbro|:5&ڇ lr:U 0r=jbQW\?% %h)ZWei%qIO*tGܙڶu`ŦC:^@@Iڣ$T [ ;:&ee#vA_L%MpnHJQr.Ŷ12f tyJK֔%f!PJԼcfd1|| ?Pq0 *X^t Rh͡)n:?d܌N6>{mM5]jڷc<+Yrܕ诒$g5zwrZ5Ɖ:K2ߔ坑{W) NJui QQřlA<o.ZD--.^UR$VoԅvV< $sq5ư&ZLJ eZb**=:T%"F0Car2?%?Ƈhx7ɎfMjЏPDU88|z^%/!="^\ _XBTXw,Ink/Uk «ڤ`8nb5cbum+=-C_Zz;RPU:;mCB"ъ]22@ v35ޙ I[7!x.Tw{0N'[vq #@ۗvqAOա,OmzF^BTVXz8U-N+Y* zS7ۧЪ0z#dLkYaջ45":oV̓`Rit g͇ ,|l/ra%vQh0#'|Ŏ0NpI#OTPf*g;K,A+-,Zx/ ?kײ2V՝8 83t TdG>Đ#ch+9hL_mt*o:xšb$qmN`a{Y^ZRצ=^ 4 W^]T4F(/h|b~0~RhLJ ym)_ŽLܤ|̎b&\Wz^*45gQ#Y5M}vkrN).q3K e<Ў{:d( `%_x<{xoSPC@*QQ2jNzgMXl!#!G3\Z?m 7#v(^4 m]yoН#?ns!Kz_">3L` (a )8ͽ)=M9ƨ7w6-u?Pv84:z]~{TOJd0O".=stQB}zD+kjFi2~H2-+2`UW4"R] Y\ˬ|bzAj*xӧ)FzR̀/xC1'/X_ޱww/Bra" RKl4UV8"0d&fEk2Q/R`V>tQI8ՒR'D=.<Ζsy9;" IFLuڻL5-Gfpe% S}4!%39 3,P9gB\ݮtBOѠ7ؿgbc7r RYAڗn1*a'_1n:,) D&cݞT3\08"8&^ ߰_wc;QU> 3x^Ɵc>BM j&sd_Ÿ \}BLbcb4C46׾w{$AoIxĄD[GrzETj0eMhdb!뤌0$36^MyCAdiKo=Dy 9rm#Вi@$;Wy&9V*D"ȩDy0g/ؖ܏[# w+<^k%"P0ntׁ\xy?&!1MI)`Z'EsAz KZ: J'"n*9o9r2tJg>#抝iZ4v7l#۷9Ffbb2 dXFlAanF}m=r7WK*ʊxMyJ(2>~ᚘR޾}iu_=/E.6\i4&5ŋ%sVSq,15(yfHO%fw.N^q5 t]|#]C04ԙyPUiDdg&~:Q+A:4f![]vr7ZUTFGh,m',ؓeNftC-۟f[{ŝ7sTX'u7{#D-Hչ7Yyc<̹G4}-ni/ CPx. EqA={sI2PS)_Sc$bD5xlys\JT@2$fb ;` {y}ټvRQD~2俵oRLj٨cE}na(o7bUπH_oww A9IYL sd*Oҟ\{)'D}3|ShSәd\9'%\s~5t,jy{ͫu26)9kP<:V=av\j)@{Kio/]6:qc )lehm,\ڢ@'HhFN= *^#=>ہ=dkq9걶#!@˼Jծ QQk |[ =*lPKET85VX'pdm/pep517/_vendor/cerberus/platform.pyRn0 +@'AӤi)MJ2_J( >`~~r޳HrʴXZ"7d1y"/!;⬺L uA"cR gC4 BU A0Akv֪eЊ $nQB/dyx6Gd6dRs+[4c2 Zx{g-[21 gnѥBn@ S[=:6HV.$&/<̌!R8o?vjr)y"}D".#4v}JSE(b\ ܔRl"HoYiϹ'PKETzG%pdm/pep517/_vendor/cerberus/schema.pyio@T&Ef$x- àii$1K*5|sm ,,3o޼w,}7Zat([ ﲿeʻɖe޶yΗI>ų-)\F>uQ]QWɉò;Xhz{|7[ {xg|^n.@z'{-_-X5Z1-mTaՆ! [UU+@JBݍnbwVŲ;m;9q- bZ K4z+QCMe;g|5*,x>%;VTlSwyNpTy!;v2|,;R<rfJL7#uާz4{sdt)Yxr9ljTn7<lT1̼Fc8Ofc|H}9'fjȁ{L}y3YJkF㖗\ )%x-y;@.np:}8I趠|}WF,/x'bRz[Tu6֩Y^1wOCOUM y"*^]UW4j@:ێq%,ѫRzS6AU2`GY7LzJa"顮}\0H z : gOaHbS~Yg#k38Y_},;Cl|r*ys`)|N@A%X5l̪G))&P{X5-pjV,:3+"& + d PRǔDވPٺ%DZ1u #hxln-7A6-7At7zڧ85?/duJ^ Nw^|Cg6 {OT]/oo$a1d^]d#`MAJNmd&,S_qBiP3&k@w)E~ ˤS4jL1{1cǻm2(J774 LzS@,[H.}FGt*&*挻wӇO@]}E`n+?UXWA7|IBxf XAa4S?b*7ȖJ-RF_E ֮Ďyr_r|i%2 nhz-(7}k SiPp)j=: ?ɫ'~_a>(RR-Txz(=Xcܨ架V w=NF\@mKaQJ Pݎp:,1ob(w0*sd=}ayPJ$#8h$n94#.fhk< |gn KB/j)&ݚ3,Ta?|*3d92| 0NT^b|,Ch $^dRD-tA6%yLal爼"tL{Eb+Ĝ9&3zt~y1L/bF0;ܚH\q,LZ~͐W7ާ`2bZ@w@}L)g`O@(Z@ * $@'ÀPnzInJfNG,W{ŒI*rEAqD(',hhC} AN',gZV@R@p\{\oLT?1=9^@@k +LxT}[(".!=h0OvH$2]İǔ)P8T7yL_.knVسX o &/6 7!xX,DZ߁؏[6 Ls ljfG#k2lҳ̙IۦkPQ?@M+pO$Öh;sf]I A?ma6I6|@=nh1v t1e/~&2f Ss8_*cٔmO "u~~qU`#fxPt)V`V_=- iV5oU)"rG5Jƈs)p|u1@IL4!.,040(Av&K|{T:UN^87A g<`&+j$//LWޓ98(&gT$0)t6kIn9m5K ?1e#8 ,:rWǵnTbmfPTEkntil{Eǽ̫x㭃ΐsN=%atȾJlȯE-m$*K\CyXpS]<5J/1Dq"Mݙy≭*f:Nńt4Lذ/|5;$kj,2PݰxxQ.t15G ϊKBLߡ%d,HQ#J,!m(ͮD03 ؾg^#[R/4z" c8}Wq_֩dgY+bǀ?/?^ ": ,"sҜ QG8DdS`Vz,nFBˁ@kBwp@*=a! sqD)-9=n (- +eVY7U3ןı%}- wx̛V ϭٝInݛp{;QH|]Oux'_GU:-q/0t5#n6mqYlǻ-J/SGd_-eMz9#e4̱k4B [ |3H$a1K'򠰎lEo-GX:LTƂa𺠄CgzحOA)KD!:Xǀ #XFYf8ؑTGS46>pNJU4HE@_b${8lp^=pHlѨpA2m-y9AD ;btDGowa2DLR^(&&p!vǫl ޶yc"S[C gXgy A+޷ >ֺӡfjjBZl ֢+MA,({xM!?--"aviud NכN;4>(6C7hJqꈭXGF+qZFeЎ&< ܚ#Iuiqnf'irS^-kX<݃ ޒZYyet7։'L<)R3di_QxV6pX u8~0i;KYHʸWǿnMg?]s uO,pƁ$1ޠN<-4\!IjB5 /i{.Rc/+ݞh3|> MZt PVt]$W@w <#?P֝j^$?7L(X `Oa!皎 $!+se^_4 @ПB 4ymVjl$^Tvh ;al}'n?wgZ UҒC8ivnػeXG[yLCݓl7յ^Cfh,lĐ,)Y]Ue9Jvn!/hs9Mpu.'BO` Ûr ObF;'U{Dey&;PF'NDJfE' nٔ sx;;jPa贖 [|~K %9LC+k.bx 3M[ QMsOǦq ,ԔKቋ)}\{C|ޟ4o4)xM_DiW&@?_Y.;wѴI/PKETΟ3 (pdm/pep517/_vendor/cerberus/validator.py}msw}yHz)\XQ*Z[UŖ}w7)шlIffhIPf+u?F<}IV|!ŢmYߵWu^.E3.&[V )XUdD}.M$٢h'i:wuyyNo_v]v\ΪEu5r颜Vb}|5!7G?f˪\E"W "HIs+mnMڛ)l'U})E]-<شZ9N۬8oŦ[+V7XE.~!NU?W h˥OMQe[j|9Y^IYU=ѳѭE]Wud(ZR1rZ4cwU$.WP4WHcXaG#'H6+,|oy-.K@N#mkqQJdM!@Rشnfj/s엷cXb^w91ڈgv'O.%ʯ|!l_-SuG(  GOo^W\[eWbcӴ#`|iKxY ~ G`$Pf)94ͻd"`sxeȐ=5⧈rd\U^-KQ3^Me0V7u?*Z.mMP KX"w$ǧ]$ p,>r6l O zRWmlָFs\smkir"}bFbCw@aL?i< jA`togy2dr%@K,LIAѣ x)d+L N) U]y&nlCs(8)Ú @|'eE,U,TOW~Huլa!"S" lZm==clghgaNK?fѢ8n$ޚ2~.мQC; [DZ\,p"jOhb ?IXv$R{TBrAdՅ:PKIܭN\\H.>Vuh{j=)-j6sSv[Yy-Z vkkF8ZNԖү@)'˜ѷI7Lb>ҫZze鎾3|W~Sv[asZ䒎 9L|C- o@H G>(;5UyIo?MʈTTx5?bd&%#-!Wr)JS q7~2cPPNX(s ~GdԾkąq8 VM<$@mfilT"<Dt]]19Eu稝MVG65 lg5&$xD̠T)$6kԨ`|A !y xφ؉fۨeI3w%lTZw8X%g;1VltT<AI(/D ,<T>\1?0A?tu2֜AoI/Kи`J*89Bဩٵ޺o-o8VB+c[nYG}4&la;ӻӬF̨?7x<^PbX>Q^W{$ޮz׎,}+0•bA-"`N/=Qꏔ;#°1~+.%O #g>&1\ŘKъև1\ݓR$C p&DrI0ZxIxxA+a@=9?vuXf0J*AG"0]14<5"q;: ؂I"ڋէZ*dU-7\ˈ45il=>HN֧ BTYe,zEqLZug}uQkj-OtikggM3 Dl!:}p8=@/;8ti`magx@ {#d`D%8<9ɾ,q(4yϞKJsmuo:i-/7 |;^m@ԩZ;j,)WRtv2lAZIQ} )fڗ4!/iīF|&tv!nd%uk;Cy5pU8LhsY-h6ƻV :FDFXiY=3 8]VYgI8E}^< `dbZW֎#kqQyp>o}-RKsfKsPsj{tnTJL`&0{>'ck_c`[I}Iyy:$4݌vǏ޽ I'؇JDk/Ngj.bqgg'\TSCW _&Kw}ސPYx~<񻟏߼9JɆf}u A >Xa+zqꟉ95nPg'Cv3 F'4ӨsZ^ƨFS4<v (?twN  ĉJ4~g<̊[N[YdhDIh~`<sdǴ$ݘ/)և' gWbnЛ՛幺>RH?m2 {7v{CtT$Ǎ|+H]&A@8۬(PhFvmZT>y2BzI~Ǿg)'ߞY?2ӌQ@Rg\Ua[̲} ꩅ3Shu9GsA-5ɱU8FN opv--$q;^ 8JT]D>ufߗ.n3@ȧz#:^TFt}թ6@p~_oۙqez*ݩ-= 𱽬Q,0|܅7lNF9O=Zu8O8O*(qA#ՇQʕSC#w4<>^nt qaY(yuz6@6ڍUYR tX ////i^֋h30/FZxbA- Iه]miOyJVT@;X|*5y)4nmweg$)qEe|-phj.neJf-jPFx:_Ezvf&pEzۼ&$BSx0u)HzqS$'9\%x*UCcqXd8;ߴq^Gɥ>taXv97@(\ /Ġij?6boFHrY&+!nBqW= RLd?1v;cQUכuN2d(ouTG Y'*S7D &X(t.r <b 0Bp'xE@YD@tHkRTxaA1!<76w@Jy 3&7V &僐lAk(#5)*k%CE. mk蓇HÒjR>~k/Aiѐ΁uKa!NFo%}ĥD㉆bAI4nPL&OYmС&F{ÈߤYJ / JCj_SIS1LuH?MXoB( =t$R@L./|vǝ+Xr5ƥ)1 @z,9YxL͘gA]B1=g{7t3{'#ts Lƅi{|xℕ]؜xƤjOߘAAS LmQ?[Me-.gdH?mkhH4w 7+P.p";,.L١t8}-QRQa5`&S}Ө}+SڧqM3Rߛtn yY: /O?ILvщ5QZJp=OmzNsQ-4ZK`?2ѽGug- #hD{*4 ѭB ;P~}&[6bYZtlPԉ[`s:ŕwŔ3^r{shi+00/|*H̻46^jO= -`f:# ~UeLBJżl */ڀk4V@@'QL> /&z:De J- Z cZwk㤱3+Eڶ3wq[G+e B|xEy"RdntIUᘹ(Lp+J8z+=Dʹ د5}s];.GvRaa&u`'n]-bFh4D͇>Nvs%c6U_Lڗpn MYp6P~qV貵% Br}Gv V٭myŗ(a'9ƺ|Sy):Ljo{7hpq⍯%tb/%|lIͺ ͳ=Tp@uxΪ֖J4AE)nGIafT%NV3*N8+V." `]T. SlY ]MFI,(@wbywV5-Yjx!jZD^ <<iSl= %fM f>]yE5` +>Dx\+PU(iL-, -[wMQDHNvTsrh?÷>o>~<:Gkו+)k&yǷǟXHeWa'P&/?)oqZt)֯އnF0%+a2ۀ% \T`k YUN0JLuE(Vaҽ 8ZHfW>-9"cφ2Q'B-7*T> 勭ud3Sړ‘XwM.cZʂUɐ; 𽌎*fzP4 )%,6I׵a `t`"daɧ-Yfzcؽ=[nL*> !`IXLp>|Uq ;P#'S(jF;|oVµݑ&e3zOJ<С)œʃF^yI8L:~Z%;䂮% xF%HNNLM\[1 5-S5&+|7p< d\D7?7JcH9JvpK!aHhŝvP砦3:mIϲWWbvD]wT}]\̊SryF͖z]^@[Έng&5=cu"whɷ0)uВԾV m?"Iu1f'}4wD ljЬ/u;vS%aO&7s+:W?:zwp5/Q#+Q$r9g%~bS X?Þi# 7t9岥>$2ǏG@x=Mب_ I{ ȕ'?xFWX#}8CbIR-LQr oa3~XªR䅶jMq 8h=5iLx_p,dZy0]ѭ|r!ٕRUȏ8ȭe~ᐿt\7s ?c@UMZXgRZLQZjMBPFNDӌ p~\iUm% x}zT&HG+RuGd :op-"W=6qs%Ċuj+ݠ&?΃g$Wi N^j$=[RlRB{^yIC?(FQ"ʢ:9!$Aq Dr.G x ;<Q[׎0>K *ʂ3.TQ3Jh::ndA 1ocD%\ٔF몆 *ۑg' Ng=]q.;h=T#=Vg'׌gvmt:fsÀcȃqV8poB-@:ʲq;s[[CȵW`\ Qչ\|k0H_󏙳dwb\4 g.L=yE-'/b\qVx؄`a%nRh+x!mТnrM_2lŠ4kq$/_|,P2ߩ/!ros(rt|`Ɂ%$)R ܧة@ Ձ\W.d[[(ݶ{T0!m|#bu)X%#FE & |kZ'ٖ[]+oRG6cDr~~ȳҦ;TsܫԐ*C[9SIeiuV6$/.O8ZOn[L'Gc8 < NLoxw1lQk_>%`$-\?旸x-閹^Q։MzM>K tݾ:3.| c_Hvxҋ]@%Y\rOl/zWpx|/ᕮok@eK1|I8D_#RzYw ix ;6D3F+C8  m1?dɵt}^S}m=?ÅB!5(ђzq/cƖ&(MUn&c3+w'm+o L?%K_]JM\CցibGOP>7%ܞ1=ƨ &Gc 1Bf[vQBGѿnWe=,:3eKEDZw͔xcLWzUb)Fne.ʯ} {o韄Zj%Mu0-8V*i<tGa@CUg- WW\lZZ=`WʬA0,.VWOz)kb%&ny_Il&5t=JS ºJB %tCxH"Ld'= <\Ng%EfB̛S&ܚxL+mg0aYrP[Wwȭ+l-)K}xed*Tl*:.f9v=R>ݯ C{o)!DthhmV19mXKM~>ٛ+3z!oyO5jnQS?I|mo\ \8{WtXG\`S%R6p2|'ҚC=$r&ks̠ӖLɂN-vjjr*̰䛠lhgWqx @Vw.H~b]` |~(ӾQ;."l:BE]4,/64U3WЎXvRcɬ4[nx_!]!I0x܌ q]6E3/孔Wg<\Ãq: L:Z)$.lr+PpPkw%Pƒ80/N9p=W@3Ϙ{J{0))~Nyzn\:{]Hu5}atos2Y '}bY!gXߝj#f"sYZ5j/ -JfVrW%97DL []@4f";vN&Z=-|VώNvLW`DMuLz5cuTJ0 7]Ĭڬ ZDruQ%vo+ǝ>wwCLdG 9 %L%HeEJrSZ}02e7ċK̻eq>ŮM~ 02(n **}{x}ÏS ywɉ>ԢN^t2ow۱#)AZ{h6?4M H҄e2qKXr4%\vh0Z\[k/uz)߈+_^uncSkT{XuDZ㌃PAY'G>ՠN\\^P1b~-cjāne70﹥49 U& bQ_ޫHhL3cmD4xY{8x*^I-<|;c>YSy ^ᘴA>(x9 C:~`H$`!͏0dEY[B(j{T b UGbG}5ƃuƏaz@ˋfAwՋlXyZ8=N}Nۏ|a:o%3,gǼTi9:\<0j`{;dru+Y#P9R&a8< ub6AC.仃918UEuWhj`"otBwUNpu{05*|Z/6򔀎$vEMU{?} sMGLFk /{ṚpF,1fx?MkHV~X. WH ?ఃvssOÃ.4R-t`HJxǍ85zuW]$chThqI9?<@Cwsw&{{AMqXI^UR!zLp))H: Sw$ޟxGwLJ'צ Z{S>V$<뢓~ܪFB`ߤMUEOmJDKm·_r4ѣ4QtUop]> vq/,k0JP0 ";cϵa1U "8MP&h_ggM쭝g^Go|z=@~|[iXH\)!t% -/gmF`X+?Bo >/궲TqT"W$ڭ|r>J37i:o5u0l1%- mͅ 1َ;s3[e:\;wI" d]%V=Xc[`+1u!SP ࿅~>=?0?ELfr l2TMvBIpo>!R"n")s&"!DND#aFE,e[bZ%H_SٻĹ@O}; R4tci*/oOm7̵6l(5['ytgLEG_ZΆT˳5tP\C[XwW6^UܤKѿ; @I1+iJ0Rk/?`3 h3J§7w0k 4<7MTr`ɻ SLGr+8sKDfͼl0"B 2ti/TXQU6tSo76#lʥ,uUZL }Jx`JESMwZbt3?w" awGA0/3-xVŖ{3 [L/W)!r2پv|W>k7!;0߿ Б߱+0ByJQoQ._E<Ά:mx Zk$Cj |gî}emAzPZ3w8KEqa>f̷2H+;'٘G*ߣ@>~6.8_Tdr^WHy{z]m6M :-a`X]m0zc̥JN9aRc]i@Q=>)W*Ћ}>Q>eT)SK~' 8LTdVR>+Gqf5n Fwvz֋NiГU0`onYpnh,|R} }Ftn|/qRa-_+&hotY͉-1&6蝹Ubk 7 _~ʙmRpx:_uQULhU24UF]:a)óZVZs[YVtq MЀ0`q}T''$\VRV7س€Aۿ .A b|+D5dnTRS@VO OPKET%IIP751pdm/pep517/_vendor/license_expression/__init__.py}s7+fWE2u^**{H>9fMqe/(K*@_h4vv./ڬ?db24ګnF|Q&T={X4ѴX,`OݯGRE]r﫯nnnF9UWs|!VfUA};R`sYWѤ ;lпY-UfuZ,U> !ժTSU"+:oaĜe˺g1ilt5/i1+Eu(y\0*[uS w-|:N?n[ lw^wA#nMj%4uQU"_d겜d0~ *x6OaIX 7UO&lTw8צy]Z 2őŤ\ȈC_x&;.&t\ nyN q .A$WO "Hۀ"h϶͖Uh3@(R&S<@RlAu$-FҘl^[akVW׀5PRrҮ)| UL[-5{tZKfZLhu[|\jF˼2E&M|O@YNGbl #< HC Fp,mpo'dM}\ <U%~El@M/Z" q_88=;==<:9yqxzt|v??=8pg~GۃP3^@z,O~:<𸓪;a8~hMjO0WqWkvV1vc4^Wդ󦜼@p0}(9h5Yx_o8`_d~L][y*PH[dr sH ^Rvq jm1o'We8㓗<;n=Y2a=~z7zմsy0Qi{8x ЩO\uT;|P ^_Tsb֊50U@wlsaĠ5hg𿎇*D$D(YQ3q 1&hG7b]-:#蜋]usk2ϛF)8$.f % ;w_!4Nc#vW 0k{ J<ǠPk__La(R>~Q%C4uxvsUN11!x#mȯX1bSoe(WTs|v۪f[HxWׇ!;6"A-rj% [^QFU' BFbK܀ulK~:9}vDǭ3M|jÁ6>{y zVo SfDG30nzEp](c6m>2c7s֘Lb) K5ˋ?-To^//_oRW`Oo~i7s o~Gkz>\O=9;GϏhĚ=Z*^hi_OW*EӮWVC mPP){<#a[`WT9 k;z5=ӵAj{>mx`,NHxnFU|Hۨ]ߒN8Aw7f*(a-k[{0,.|K=yӞ0Wy$ ХVYu]CBȁ@?ugؠqpX*DC]+d=ꂶmvoVS }SGp˓g}y1x{!ғG@>þA+#-Ucѡ?@{%3S`A_˚fGx`hmrsUF@ FI^-H@#\6?wЃռ-AӴv}w6|z@[ܝ"$vffDw 'j+ܢ!$iI>DSM2T!Coפa/,- pI;28yoC$:v'X7$19 (䢟Cp0 n#C~*o99vm9Bw W 랩oubTVܧG<~)}47J5F#g37 eLNuݟ[O,>Huצ_) oϞtL* &s 'L L;@k'Kb 4L2vxZO;_3uߏY䠑֖II6.*zU(%h9BQ(J Z  JjT_WުY`\ hm!ފW7 & 6D'^~JHfu^I4e/\k!~Qgj15Y^ $((fo5$5|00Ƥ {xzI!T(W'Խu컣8c\VOQP /)n`%z}SŸ}~ȿ\s')BΕ*g7W%vë^Cb!d̨ږW ڧ4h} tQ=~HTMlŚ66JL=FF)RP-el%YD0>ED6>c#*| ݧf4_uV݋eُ܆\{^tN8P6cr"x|=̾-e!NGƇwQ8?W1"g_b@EV爽e0+6+ذ ^߷u]ax0)X* 87ݰi![CXƑ'K96qn}o&,U7uxk B DH F{G#P VwⲴDPVǣ'`|^ܢܐpo&R BbHNgPBm?S"Etֱ/G{ˍ1Tv"z iᾼ]L8TL@Dew?)jncc,nBM>jQ*M9ijVfΚ1@⽅d_ T4)1ݪlVym-(Ԋ˟u-ogt`y֏11.$2S갪żM[l-g^`eݠ27Gp=!{%a'ah؇]^.2kq|xxm2opBP Aw0խ +-,wsQlosӁK]& M@0>pL ]֌,$#1$-j \ o ;`)]؟}Omb^W@%4!Dഎ>CC پ^# 1]^ZJg昦3Buq}QԣynOavFlbbvi::z!,^M7)幙1ݢhqłR"I G>Z1BOMn\AvO,OW0SeaOCĭc|mj6ѢQCwf)L60YXJ+0(nr CF*\D|k/҅D)QXo7j/>Z&_+}5HJȞK5ZСO4L|gn܀@>/fp)E>*/5KX.7T9n@J_7#ߛ[#v%$Ś>|T2nrOEfh¶%X/Kl~ /-CoxNDv#5`oNmmL/(؟4cjX&PHպu{/K]+ 0(U1ykU98܅l߉j;2ʉ #m_/[?t㼝w~bX jPf}?Sfm>oHa]806£.`U&u'M4? 4;x[%@+K9#Fn] M./ (!c6BfJ\4 㼢D$p#$ MfH͎n5SYr|aVLc5"<1,Reц<ٟ=;"ɶeD!kiO`bA[E*SAC}F"{gPKK9Ct\0rC_z*+8[['䬋D(@# Oج.W>*3cj饣s:*M =鼌 ϵ!|~iVׅ桯(?z:HՕw kxD hDžk"lhrt@ѾF~ tGҮJjØRK8Jowg?v/;Tm[4cͫN DĦJHsQ1$v u'FC DE fÁ| (XrjOe?I5s&bG&Z6 CKpXK]$^׃@5%q0A~ :wG 450F2/FG<:~Daϸ0m:Gk\fmK`OnsH/zb0j`,^ơT)-=*,{LEf6] o֚I]^ ; ;ܭЊ-/>3X[x.S.ٰ=N n2^/CPF5].,3 +8;NF!$il1sT{qqh& u!-fWRN?Xejs>R<02 0UEWQ@7tȣMFrZ:ܻWzgXbr+J_Əē„Ia7n= ݇cIƸKQت u+j0ړc 21Nּ-T]Yhr"ZH)SQDVd] IEΒDI1%< /e׵$$IւDg\p@9Jљ6/COOQ7#1֡y!ծ'1Wt0ECUɋ> .Y]Z7_]mzb'.E-\i_ӏtNXAJ%ssI*逑4+v%'7ly=č)@9ǖ͕@2y9#.]ٍ Cœ8F +egOWfU^ >tY,R u #@k]@N=FcRWb(})9)?}1o, jyiΥ:GɗV'@Q6RBjsxDpha]Ԙ;2eQ(M!0T;Di#m[ˮ0ɫ0{ƯD|K*(9ڎi`RXv !T/Wi;8 3>Ũ(4y+u:[~%ܷc~'3~hhpAQpǩLO$>d\S 65Im{E|}Q*tׯLJ̽Ιc,2Mka ~B0|\'|z#smAX:J7`[d/Q~n#Ub\肣ٌV}' sSDE`k7{⽡*cXb--8pYW%pj)*5=zY5w#r!i2 Fa&>ݼI,mb֩FdMq@cдec_o_1us~btoElv&D2b)1bs CO*2LaJwqB4X?\i6@J顒ȿ"v*cղA|&J0,92Rs,yMy䴱ܼ*#Vsd+qHg><Rl.EQ5`j*MI@TqRW8Kva ܻYUp3 ]qsˢ zh VDp1ݝWU2U8z)9ʶGZ/[ ;Zk # t!(2uE)8 ~݂9.Ƹ΋ ­ D%iXj$îs8OP_1"ɭ.~eo'A벓DfU7mT3Di"rL*x©F1TTIGy8t`|b&`5[k_4E<@9Nu17 9Cx 0x6E7;46uxѴoEttrn48s<8LnEt?)qm]_)#%gvzMYrN[1*>(iKɰjNj\ "%b1VRAr5=v]z\͉s L9M#PDGd:߳K\Mac"(lXpaK",f3(ا 09:m͢)8Ek ':]Sw6Ο@t!)6 5Ĵ!• ן~*IrN9[nކVcaW);2x9Z04 -Ҋ^`g Te 0 ݃=vV?ކ&T=lp]5x !UDulx!dndގ-j Yi꺕ϾܙQk ܃ٕ5ZLN~NapRe3Xߣ$KfM"JtԜX`oyk ^W Eg'ȃw{L㋽Fզ_kA z9aӨ^}Ffb9}? w: 5 ~< @l<u<=3nP3@g뗴fl|)ª{Gd.*6BU &%,C `4ٟY_n9hʹo"Tdx^5X@pXat^,fyy]@nUsȤmfY cJ$d0iK[A7z{ٜ=Q- k+>tL=+nAL TKP)ۈ xov>@INCΆ2%&ӏ: #sÈdѯN ݔa%l-r4úIJ+]w:7DxئOfR9?~ x)ÁH=M[j՘ʋ[;^\csd>R8S<)8yl?r'ݷX\QudkYeKD:Jk94/F1 Tc]\7W2O^>yц'<2FʏɁpScym u>8 WcD+.9xFg>yݹ)Ahy/G^n+ -8 $O`5msTOb^;H8y n7{BPMfc(.*i!#C\"KVu"|:nJe. ,EzjV#2 Y9؂矋 `qs FܚjtHIjcCS];D%OhzZwKf̗+.J`fmZ b>ѲdFCBsS֯l.*P /{?6 I\ HB5’Mgu0ifa Ct]WCRWߗ#y?u%}v[o KTiShx&|wqiI&;C~aGj׏*CX!Q[M®7: i1(h/ش0+_c3#E[{v2᜹27HͧAu^nt .LCnUjfLxh4  -S ߒyh) ?%=sW[ ٹR,dwXFcN>4b(F:@:8y@]ρ?puчp'Ar 'YkcRdbGazQEe*a2 oe.ch.dtq7>8ۀ玤 Zmn&.̍,L)CKXL&q$929}Tm8mm}ǧ.Yq`lKV0kvPBI>)БVr}A7>0CD(V.Jh%=7]9?hT[ 4?b5urjM L^s7sɘzNwrrU=voxUԪiA6.ԙkq-$?<ҡpJWZ&cb!ޒe _mjbZBW.W8BMY E7dkw qZ| LǷg(iHϮ9N\޲vG".9Xc.$"J!%Ok2 +}Y6@]dfnfy՗+ΎT׏}s[*ڔp"= wPlb n\g<)f]96 qvteјMMj*ITLL]&Mj!UP:V @REDvI_[dNHݎ$czaz[FFXZ/74~͗'6 G@-HJh6(8stU^0x(g/SӬ+ٕ(ЁIUmU;A9SXw$b &- inM0S|!^tӣ㿞}BExtQg7Բ0mۨl')6Zi2gNY㚆]vV~Fasew ס DKӥ`aҀ7fOyAY DkO/1!6Kmh:@xޓV# 7gS$J*opa0F`v*F /7TÄWc/T3 0(8-3c.H̠' pLaHLhEqτ_FBW4I*uR[% xUm'ubn%]og}%LR8HcW1Zu__L#GARj[CLS9aXtђkM\5YnD 6]1 ݠf w ؆c | IDg;<[Nron/o;kQ6ä7f.XZ@DvvpM.Zhb6mlS$-,Rݶ; OKY+ș|<R=j'kܾQ\ʪFWsI'}#g;@TRz9;N{N "u4LI>6%_SL˴rw:{.s^FM5&3'3"gk,MӁ;r)=Si -@d[Uh͚R2eDC/bv~mlZbQ5>ߖ1!X+IF'E>VQRR+$m$䬸lK*lmɆ, {{?~B};C:?Yώ8 T|dh\m" we|iGR!jb|.G+ܿ ;ƏNrÈ$&}'{2yRpd)Rd t76&;nw4].niD n>;^Qd^ΫAgKܳF%Jᯪ$Lۅ[N;mʙ!̐Z}#&4)S'E6Zg2/u}?BuzdPqWW/"qvm:JQLe\=Bt&̿#>Fp/悵t\+LJWT``D\as(5'$0[rAGP+0;__LiZXݽ{R!'4]ŏ5chjG|z l=<2#aX,~,&IF]~{;7O& ]iHSmQׂ^gKa$B ""-޺48z\„;Qޙܽ(-eF3Un%> Nzo]}tfeWo*ݍlmd :r;\ɕi$Lhcp;^8{^8Ͱ~SXld(Q zԀ4u>c@V68tQݙHl(ί …[T҆3%tW*Rɟ`)ebeFW$0wj:ڏem#\ n *%7{-qi&ߋ2O쪰9<`]<%m{=nblE("W5/xɵa`G$mrGb 7:r)olcP33c`Fa sbn'ARkW ӭߗ̦; ,RGUF߽'YMMH*OZ̍`O7x3#z9_209hxGas>fÁ=+EM\M w%`_2iG B*)8W8̽]MWp ҚiJ+˕|PAfe# hQg *`O{l4賻|{0]7?֓j>vJ!%sb&U Qav_~7(λuAQ \FI8 " LțuZ S^D;7"H%\ܸ1yM`C~q {U߄o8%DNO7X^BrUo*A7Z4/=JOʝ?";|: >8aR1Ut],[UGMA;.dA1)ORoPm-PկUĒz9咾&[6 Air\|s="4~h~|R!Id21xC97"N}NCH-Љpɂ`䕻^3yE!) ErOsiaԁqg5}cX?,J5@ݒ{4dە;!݁9W@Ѯ.V!a6;JZ_rS䬩j~,i(oڝ#hew y]R-[AjxHM04ˆ|A}2> //cOO0Әi jDwp;V-Fi%pb$m+ꡛz)_EHy-9PGt )r+q4V*#i\v 0$#9BM(V#iOu+YyU?NcJ-'Z\f(Csa5{&E( R= 9i[d67)m2r<~֭9ʽô˴>^ 4.\]MNȉ5`@!9?`xQ*-E=3tsߑż99Zo2 E]m{zO/s)S:fotK"([nLzsxaiC,KȠ̌P }\CE'SjfUkj(ܸnIyqYMĠ \rj> ިJ}i1ֹLB\.0 |+q77`uB$Kz@w! e墳As87vcGQ+vGsxi]=lwGUABd'( ()5ЈQ?),'EѾbh.(e)NɝntA{?.B&af_0/x:׻wܧsT J ndd~~Ѕ3PX*XU|23LK (lH:tlNSP)xct0AܺY"cH0D&D!(JN(ZtŤect~6A.F9)JFпt7jw&4ݒaL-}`+DK$CϷ*Z'&|IcJ?fOPKET;]cP7pdm/pep517/_vendor/license_expression/_pyahocorasick.pylɩ}Ɯ|GO1"@6'{IfҬ#;yYc]ъoq>ѧA_EB'~PϪ  ;2靰= 7Cqc@&:jK+%34!>;9`~.@H?WI+Qi9H&XZDTeM 0ޓ¨D"0dd8!G{f]-:g\$Mv;mMƂK;)Z5$"҆%Ad $inNWAh'2$ ) LaB c1ǁġVM< KrsKToG?C)[G0%zͩtFv<=z4D/@A .e~,Z3Ѳ> ^&Zsđk/ ihOۂ;ܡܭ3ViyN>x5A8ȊT'F&=3VGhM}yavr12k"Z'J|x8AYP`X+RaV 5-2%餽V9`ʮ>a᝼J0H[H b["SȊ%6B]ܿL<ጫaDAeYsGrYS c*N9ʣBV|+ü;o;C顭lqFPb1PbZ%s%"@ ?fK؉*4&"M)Eb6Ӭ1=I6's& TǓ}iB۵/dHO`ܽ8e!aݨ7`86Q TJ&-9 $w:&3d>H=(A1 Yk@ofpA] `= < o~%3)E۸MdQCf!h\`'/qb ap^ q/jv|iϔmM׭_D ZUB|0{32Mʥ{4y>إոGUn[[KI`D~0pO#3{3! 778w#^ t|yxz5oclYnu6vȩx%A3O# Έ9vidNNo;@ 7nY,w56nWs',h4!8|Zb]z6IP~n= @ _hӮ O =0`󭕙I;OOe yZ{iͤ/2DqwXJ _\lqCXX~BSA u|?T<<_<ڈCzDk4́;Œ33p'FqQ>tJۥ:yA!}͵BJs`A8@`ݘDV-:Ax^-d/q1u3!?&4߳Tn[[cGUasf#~X93* 16X4q}-jE7 Z+{&jrv,g'4!7tbr&>س|+!,'0뉟W^P^]ӞP1p!YN98I[Lm>+#`^U9&^/ĭrڣBӦH -MiIj` pwsj)PqSϻf],x`1sKY6 y|khܶ&:6-jg8UhT]UBsݢr_@ mO:E710 Vt/K2`›%p"7? c5DVy3uIB%OQ sydM: ۆ{gX>gY,W˲ޖEʁEI!JǗu޴&hTPvcP[]Ac7NXTJ^T +p\DzTDCRv]1#8 fS7# Hm:inޱH`y^P  5M)\ c,1LB8Bh)1sDnŽNq5=Cn -afo74BiuB1G75# >t iw#/:jnS 9w S;6mgMݹE2u/9#g QBj }pЯ{g̙=XLN]DW*u{=6Ə(eu|/\ ER8(͢@u?p`aF@paIuWx0E꭫1gUۤӒmm[W(b1 厪&m|òڮQ-sВk>O @0Z/F 1Z%k4[~Ҿe$1nģXNjnC-NA`I: @8IJ]1AwgU=ݶ>x5;ݾFlVH@.F-L&O$bUMOq2=orAڊ|И/Rɞ&_K6&0> ~4mu`2!WX~A~usPMX4TtQ=JVgq9HLONeeL/_w = ͦzq3Q4#Cok7ݻcG}K|_ ܂ef;n_~#VΖm8jpOfX|;8f==͠㦉{34;1(=hܰզmQk=fZ޽Cu-Ǎ| w]ǻ-!zܞmw0#q([tP+O m4JFs sRt,WQ5U?=Q'U:9Ҭ_ug9> c&^~&?__^>鳑ê%omմՊ͵r3U+È:uP%1(K<:qGNGPKET㉑,8pdm/pep517/_vendor/license_expression/apache-2.0.LICENSEZm_P$Mi (ss ȇ&.y:w^*3RO|,wJեzēPiӉ_o=G톡~KfnE[pkX]W7fonu!n\?r}w=B+UN?fdӈVN pA U4]ūDm*U5XυV VoF]H'*RUbskU/A5v'/3تn8JaTz89;ci?/܊a'nݖvP[وk}I{%dIR`x֋1WP+[AkBH—.4U4mk:/?(zرp.^Kz DLjtxKQЗ-}J]R9/D*tr+~}W2{R.4hBq;ݣZ`^E_|o/i;aA:d AFu`R+'3=2L\Z]^&Qy|x[<'$ְ[ )GZoUך,hMh*8Xwe3) EgVGgahCpJG~_hFeq©;oiNvuH2CcBʐr[8<9p^p{For8w[Ui)CO@a?ƄCi)tM ^Fn.$#.t3R&aB zp v\ LVRV߃@qmO% yb{`C0VV.̅N YL5LD(nγ *D Y_fez;xp!筇}G}հ:ꥥHA1ZeUs<>6-'lep ,HYF=Q L ?΁~р>B-zO(+D$öUǔ/ lvÃG]9S6"?T9QAT17 Y)'/W,ieq T h q^Ȇhoq]GcfAnt v\J+,E=࿤ npqe%+R!wpj]PsG%`ccVn"IdF-GGUvl /=@JzF5# y[i?"ĎRNo;~El$"XV`o)\NS_c $ m*vFA<eTtOJB~!~ܶ4`o.Hxc j.~@Z۾JG.>V63Y娬J@!t&G!^L?k鞓>c8l*ݛ7 1O.y?n`-Xo$ztRO,-,x~9D?,S -((!=5uX^PhS؁qe f죰MӁʈ]^f+ܺ7;^Ք ҡ. 6IdxA8wڂL-PbYc/0SeV |~ V8 (͈hލz6jE,O8 vS&±?Zb 4e ͨϔh%/*+V!uACsqМn!!1 m&x# mF9#d4&Vi%$+R+DI*^M=]/C|"mzo[5A+;A%ՇN;*aM'n> wX !htqݣ˶G!2uߒ1˙\f;[5@7g-ǐ= R2Dh1ќqvHOrZF.}㇮ng|;'Ϯ dʉe_zJ𡬨LAΚAI7ABG(>Ǯ>kW 6(鰝ʧ~IV Fˠ :&[' M=w9O,J: m-N,˦\78c(S@@zLp :Rnxo GsR>p(\hOȵ WWWx>CB$0< 9/4 4@oW[3*k57d1bȿ;3|bpx ح1^7#e FT-W#c 3o-onX9C$ǃ8/#eu钴o@x#x΀obQbM@+ŝPgIa}ǗO^Z)(M:'4sV+:PvC<wSm ix d||7f`dLp:5mHZg/1t=M03at Nm*UC+WgFҶD\G+tMe~r ]96鷏Lb:b\@*Y͇0(R{_kTN@JK:YDCWxFP[+.D%nKA-<8oZ]bCN[+~OӳLzHc#ه [7*BצA gMgX@סTQKzopg6i!mklt>IozI5R)D>_ļE&сkn;ߛ^ƈGQMNx!SBZ_?i dsמ&!1Z?rlk&* '1G#~~H;=_ Z3Gըt/UPKET&&%H7pdm/pep517/_vendor/license_expression/cc-by-4.0.LICENSEL\>&вzR)<*+Mcqt{rGls O R|S2'YݤE ;z/+Qɜ{uCZJf#UO'\ Po}7bh@?qTUR >Up*SceN 0UQיh NP,2EGԠ+i=A8fLk?dU'G" :[cA9ăSi89DZD:E~%ЃO$TDcីE,aE?H@dJH /X'˒ r~ z&iv1*$KT' ` d]-3UɆⵎʉ%)!eRhhb:Q<Ώ2 NAid0QL7 6 l۴fP_RydPU9p,%Y۳Ʃ1?)[qY=~_a_ݺj qV|i,CIJ#!Skh[<||1oI0L␷ɷ="\˦ȉhmU&Yh]!`<%ރթ\I6j5Dg_>!C 6G!GNվR[FLﯶWo˖hv;ni#}Q#i'% #f+2qPMS."7X+JU6'IΆErqҭO*%` M0q,7"ɵ]5.6cF8p'3_!w9Ԍf#L);늋l&gDc'C* Kj6boҮ r4P {9 i reȄxJZ(kE*yYz`p袂<#U@fG1X4fj+Ν-Û"]=sBcE)eFfUsT>}DγU,AeA.^WMP'Tƃ1|]偁LH :yG9gffɵk@ xŏ`,~[odrUG.+ġ(cAǵC@1$5亩TA~4)z64z,l}$m ޤjA-8W&WV~!*ȵ\#WqCAoxf .T6{B@ VOO"1?Es3,QLD!/B_LZw%ju`ϸ,K %D7E`"0-}RLW}ĕ 9g,!Zec~Y #L1KhaSPrQQw4{K}eGN̛F)!!K&ӝzrNSH! X^S^C/G:sdby利x)WAĶ[Cu(~t*c'vQ,8a[Śv[EOڌ@m`EqR%A2PH=L/;:nPt `TaB1iHޅs ڰ]mz#LeMSq0%(p[ȹ[0:8A;|._ Q>%1N&)3E~UǛm0 KkC+nOy#M} C5>Pkt6O6+>l偹 \DC4$H%~xcUX.PFYg&8݅J_@e.0  Ξ)RUzڵhhTJAZ ̂ۀfav@f S_+!(.,te+Vc Rx6M_"9ހ_{9㾼,nC]4Pa`9fB;W֟h^D2JXLJs{'l͇c~lfOvL~-Ox:[Hlbl:~;Ebl݊;`Ob eŏ&|B`;~-Kݯfkg x%>NVb w'k tZċ;GGkS!L lY>E2Zċvab@ 0xǹxox@⿃ &ȾɊa?n\ċw+8y |B(ٻtcO3NV)4ĥ+lZ-Mba.m.9c|u59 psO੃1ϗB{'-'\(& gR8"2 kFA傤e>{?y <~vt'!>x<MI@ +#ci| @N׳|O"Ŀf(N|s8v\Q|QnQ@$[xjqD僽sӉf {6w sB6EP]HHq499%/D+`URX A%.4M!w6#p -EA.Gsv( %6 o^^Kߦrn:|:z}|jSbC|n)*J3|Ȼ 5wEɚ@S %y1ԏ 1V'πO'J-Fu;A+|׼nj<5tcZ&oϷUa}ƃޢLXIJ%v>4{-A4L`o J4j4t@9cw 1h1쵞~R 5Gn{p &{Abkc #:JVxš*h*ZЙ^8m! #`ʊ찡ˬ~gtc`X%}ˋo ;:(oHK( PKET&&%H<pdm/pep517/_vendor/license_expression/data/cc-by-4.0.LICENSEL\>&вzR)<*+Mcqt{rGls O R|S2'YݤE ;z/+Qɜ{uCZJf#UO'\ Po}7bh@?qTUR >Up*SceN 0UQיh NP,2EGԠ+i=A8fLk?dU'G" :[cA9ăSi89DZD:E~%ЃO$TDcីE,aE?H@dJH /X'˒ r~ z&iv1*$KT' ` d]-3UɆⵎʉ%)!eRhhb:Q<Ώ2 NAid0QL7 6 l۴fP_RydPU9p,%Y۳Ʃ1?)[qY=~_a_ݺj qV|i,CIJ#!Skh[<||1oI0L␷ɷ="\˦ȉhmU&Yh]!`<%ރթ\I6j5Dg_>!C 6G!GNվR[FLﯶWo˖hv;ni#}Q#i'% #f+2qPMS."7X+JU6'IΆErqҭO*%` M0q,7"ɵ]5.6cF8p'3_!w9Ԍf#L);늋l&gDc'C* Kj6boҮ r4P {9 i reȄxJZ(kE*yYz`p袂<#U@fG1X4fj+Ν-Û"]=sBcE)eFfUsT>}DγU,AeA.^WMP'Tƃ1|]偁LH :yG9gffɵk@ xŏ`,~[odrUG.+ġ(cAǵC@1$5亩TA~4)z64z,l}$m ޤjA-8W&WV~!*ȵ\#WqCAoxf .T6{B@ VOO"1?Es3,QLD!/B_LZw%ju`ϸ,K %D7E`"0-}RLW}ĕ 9g,!Zec~Y #L1KhaSPrQQw4{K}eGN̛F)!!K&ӝzrNSH! X^S^C/G:sdby利x)WAĶ[Cu(~t*c'vQ,8a[Śv[EOڌ@m`EqR%A2PH=L/;:nPt `TaB1iHޅs ڰ]mz#LeMSq0%(p[ȹ[0:8A;|._ Q>%1N&)3E~UǛm0 KkC+nOy#M} C5>Pkt6O6+>l偹 \DC4$H%~xcUX.PFYg&8݅J_@e.0  Ξ)RUzڵhhTJAZ ̂ۀfav@f S_+!(.,te+Vc Rx6M_"9ހ_{9㾼,nC]4Pa`9fB;W֟h^D2JXLJs{'l͇c~lfOvL~-Ox:[Hlbl:~;Ebl݊;`Ob eŏ&|B`;~-Kݯfkg x%>NVb w'k tZċ;GGkS!L lY>E2Zċvab@ 0xǹxox@⿃ &ȾɊa?n\ċw+8y |B(ٻtcO3NV)4ĥ+lZ-Mba.m.9c|u59 psO੃1ϗB{'-'\(& gR8"2 kFA傤e>{?y <~vt'!>x<MI@ +#ci| @N׳|O"Ŀf(N|s8v\Q|QnQ@$[xjqD僽sӉf {6w sB6EP]HHq499%/D+`URX A%.4M!w6#p -EA.Gsv( %6 o^^Kߦrn:|:z}|jSbC|n)*J3|Ȼ 5wEɚ@S %y1ԏ 1V'πO'J-Fu;A+|׼nj<5tcZ&oϷUa}ƃޢLXIJ%v>4{-A4L`o J4j4t@9cw 1h1쵞~R 5Gn{p &{Abkc #:JVxš*h*ZЙ^8m! #`ʊ찡ˬ~gtc`X%}ˋo ;:(oHK( PKETVShGpdm/pep517/_vendor/license_expression/data/license_key_index.json.ABOUT0 D|G20ejZ;S{t}l6$Qٹ Rp=UpXv>pZ= ڸ۫2p30m7x0:@zdYƵrU0pq0'm PKETZͺ( Hpdm/pep517/_vendor/license_expression/data/scancode-licensedb-index.jsonԽ]Fv{~C6&%;$;N8PUjQ@ugw̅V=gez^ ׿TU!zWw߾ (,FΪyIYW/Uq(/}XEz&>GTM[|NGLPUE%_5o}v!~ho1(aJ~xӷ0nF;B`1)M^n Ϻ!Y*9KF2eD& B0@}\l'A;S?8 # S18#Tʃ,E,vnvgS[E CTXXL9h `.'*DE]}uzEh`MzabI:dJrʾxC?ʹa-wm{tC8_3q XՋ$,< _V]j7)s5dRҌ>׭slZ 1 SX$`ξ{SFEUvl_k@tB'EY$={AVM0 ن8CTƧtͲuZ$3tTHU/\vޔc4N,4LB4PBO4^Zr,mWV>*Ciyoc(ٌbͰɒ_aKc%WR ^Eepsmp'4{v96j2!P8z>&6!|m7aq;ݖW}0*"O-)wwv-wVMQhQ0N[qâd/vu_7v< Xppq(;@ pPշշշշVOQZ}sUCg]j?j8[-"Bb^v#$npK]>[pu7wyrI9Ȋ ogׯKk !(_p:% dvE=gκh5F"9wڏSVX/]/I/IZ!sglx"plaN #jPB. \w3cאD)8=ǺAa덒(QrgQrgQrgQrbFɝ [?g]K47{uOO>&>kvBwR]"*(XD#m>~oq5VcM9?J['ĪQT:հs\ecEOa!Ƿe7ع XvM.,ٸMp3]i1qנTd{OѵmiOjP~[ܷߡ lxDG9^΋P da gl Ģ,L, $"oq\ahԑpu{P(۲V= O0gy~㇏mUImfP،y H,uԢ|!P}S#6+5mQW_epJv0Tei`=6=Y|H11gӳN99IApXb-S ;R:)tCk0L]ݟG-QPzVKI+;d1v 4-6M \S"A]go?|X;Y89SXyսeN.Ulno,¯/zb~HY/`lQaGay4hp$ő&GDh5Hndߠ6M6ټ®#f&|eR/cu*-Ȯ 1Voa_@kowCТDbG'ZIxY sǐ;vA}ӯ>=+خaǠetN/Wf+-\&-$%8ˑ (Z~[)X7a)q-7=[ҏiR|`kpONr~1|y*9v n|䮤ЏScvA yusjfہv<7ʖ0,Ĕe"ŽcT]s~Cٍ4f`4%c@a05SvX4s;ݶ>>+IgU0oZذA:m딻su!(Ocޠ޿ J=@x]= &[k !h\rru^A*8G}vr:wvp&fbq6&5*.:a}-DSvqgܸ0mcư]Y]~nd,y@fIX8bu5x$9#QC`oHy8Kc0REvg_ux{g }YLdOy6^u(Y!j TdfuCRj #:AW{gQf}BēBAhL=uu -O  L@5ΧX~MֳŮhG6qh\&aYa!$G>L;h  `2G*\;GIpip)}h&cB&+C^Mv}y¼Na:E1W~ݖwX\kqE͵x/UңvZh[L#f&aWbyjP̴}(f\95\ۄ5kksy6y02>wWۯ  XĦizH_49FDW=xs獺 *!2zϸ{|=/Dz*MDbt cPqV>** ~#ۂU(H J u} *96?WpA:<ؾip]l^Ú7"Zp X!D˃p 6|uk)W='ۨXe3 6X^3j!;p`ѓaI|UAh}kEԓpIK,>q\6IvrM`  q 1(.0z^nYngR+*.xw;A Y<*lQo|[m;Bq;`|:ħX)l*odvځpv%Sp%H*ݲZo"똫,ئb߾{ ge5{w۶@nivf^% #X8%61%G"cB#ϖ1TeeCN6w`=ʊ"Jn>~7ލj]Zu )BvO,o=2RVb 'V<`bҋ1)6sO]1xsR]p6ka][ ^z7t@HnDZvPVT=.Xk;@6*8=qjMomXb7vXa"fmX{P',;T:=FJZ76 EVk ɋ,/ Uy6JGMGgttMGUTQq`OQFӆؘf8 gÚ¼.i>:ev)܎Ul-f0pJT 5d_aMPKqC 'Qx a\,$0LaJRILI2)>y;54&YL9Ĕ#r)\,kU%MۿCмa:<7Y`w/9g^`K$aKT{ҧj֧m\X+1[p n`4)j4w,k޴7K)]K^+QYoޡ6;JyǮ0'2QQZS)V_R[(JrQ"8zb$'7y5d5@_ _jIlIZ*uO'8Dq*KM5C2GT(˜] cDNPM B+R ſGp sPXр6I4U "z|++L2cH3V!qOY̵mZ}Gy eboP.O¢ӊMOmz^d/]Mll[Y&MrQ.ѪzK|mВmcecmcŊYo0Ymet([geW'fgwFʍysƺamwp{qcw9`F,#)'a{c ,<芌P28T2r:!XdY-^Tobn 7"gKxƿY39J0G8\.{u_ӛ0hz& צ{,tk)ebu~|t Qj{Kmp":,(k%W`} \w?k`IQ*9+%?lʞiqQw}q;uqEVE{Ժ? L}GaWD"kpѪ4_ 2&dlp_9k[km`:S")?Up?&I,:bBYt>KjY>=[{|l <P@H<2)vT Zrǐ;VABvK=^YM^#w=KF/O ;([J6ێiC:Zk ZkP&R{|y|y|z|-7~yw [MmǴЎ!v fkWv̚e;5+oJ-ַ[e~;¬w.^oT1k֬.ɪ^V1k_#W;*0r;NZ4v)ёYtM*:5ھ-[սʵG>ھ\ fZ%RWޓV%:wxQ =jgWej[ztn{kMul%}۸V0+K[Pzןe7뺤NaRt5V}R퓒sY>)EWjNOװ}m>0;퓺< 'delΣLH,ΩLU,ױm)Ҹ]Vjױmfkl\V\n]Ǒ-Z6Ǵh8r:29^budő$qC#+_޸ͮ~N=pol_Ǟn>vgBA?ͥ#Y@s]D_e6~Zn-\ 6*T8JC|{66V;+]vcffjW{P}Rjާg N`1H,-}e~*﹘"18##N$e׫?+. 9|x;H8] ԜW,Ԇ lE'(gngb-׌Zo> Fpr+W׵vYuVد,0߾_ъ_V_1"_6lE`nR)T )ݓaG#vB82؏C= 2?v5#Nq8c1q$ 8e?FvķEX(daauo`_Tsw#렣h'v޿{a-;H@#"ɯ{[nYxm<M5r[hs큵[&u(m$g}gnYܲI>]vDD6w.c,8;Aa-v2qƠ#" {ߛ~ ~qƎMF5[ [Wa쭅[Rzֿz׬ߜP\[[,y)YfHN1''u%,?YY~$|ey\uV!j zTâ:uUVoZI<{%/@d|B 5x! ^ SL(Ba|9CXNIU,>¶V熠%7p嵓1J?y˦&2L`cFIX>xd1V{NcF0H@#"0Z>qLyvIҹ0d^SV$vM/$mweCHlRF-[|POD,K`kQXtңf0 aF G@pA|.x. THg]yBG &Xk{!Zorv^掛?x5> k_AvQo'?q){1 c,fb:pOљ}4QԳFa(wY)smAʚQlvvQ/z\2`gx4LHd{R縒F3+E%_ 0W,s6=Pb;pq7\Eua9M-]P0=0r}aOOi\M]Y1vmh70S1Ѯ%#-: $~tw"9ڕDB4.(4DqK9sT0Yu{*^P$dNQq"YdS^-Tw o-Dw,#zj4}h?By!8]3 z]*4h!uLxFY}W al&6>4Jp(ݜy |(Ŝu߇eTF|W>)*1XCDk akR4aʭcNl k„ eN:gq 4I僚=!GmKkg.i\=X75Daq*FȻQ6G, ,QsZ<ޜtsMp  qk}5Cp"=nXi=Qo`J5CY9Jk3`3 ~uOu͠ȳzm~]ꝙ]?;S:vN w##j`vD (z4Ï/,nUi];/zšLdYO|YT 1,tK)1e0&/:2Ȍh^I,Yi.ITE,=AϳU2c7|4f|8N= w[?ZǑ~WU$ԷӦFa,wi@n<2#4r lY̚2}z{:l'\=j/$ N%q*͓ƷE3,Qƒu̥ulT9h~myv،B( ;Afyhn~xcJdRCNquc%1-aa5þjTֻevGzU7wv'`H kF(Ӭw9>Ơߜ8R] 9TO61h N6AGSQPeѢ,mj솑n]}>.}6F5>Kt$_>K4>1KhiÓ Գ϶F>aw|zS/2 nbQ&aWcqmU8/rKC]pmbm!/6+iޫPEk=㶁MڲnZe:i[<(Uz?6z( 0rg^0mX}VQv4Xld;Ld;j2":% |{zuZ!,[\?ej}n;qj;.)h>>a97qv@8ُC=j?+)ƷE3X ,Qƒ*gΘr46ofu<Ӫoa*]'lL=esl =BqIz,xC]*OcP)p" Neb]ydyZ89SRvazsGBx vM >a}4?Ð8Sl[K g(6I6uL&I&;,m n}fAaq>(yJ!S *b,c똕/J}nahZqWp6?&痸R.nkSPԨO.1Κvzkv$waӳ][7u\K2x.K JG=Rs =a6(܏;,?48NJ t-0Za Vg]K~m t}bbsX!R]\oDw4oZذA:mۛ$s~YV>qCsuКCMUE$ivQٟP`3 6Pa3&<8`ӟsMhNg@uUg{I#IX'>?xf/ZbmPh7LvPa7:c{shExv O yAs}BN  aAgQQ'N?I+TSb RYLIYu\k0ͱM}'\0p\ BWĈ~kM :3G|";0bnD*(w/؞5GÄ;<8/W O:!TO9|XaX,Sp}o̬R,SL6u)~^_M -Sxmk^KA4jMSL[pv)-5)7[pCmM9nM)*FmmmmuH7 b>ͦYf,hPD2]Kr=auRRjR2o,S6oS8z \(=G;y/8 X8*H$͂죎pA0RT#E12g@JLAo`R)L@2 . 1ɶYMs2 jiNBxd,}s2 xߜt"+OˆQ` _uoۇdpꑲJy$ \򓰦j߫gGV;r!Boo,2v$~#w;g#}6Ȝ>ff#}6H>2g{DkICo%8Xr d)Z"OQn efAgZа :Mжr+pJ-:9%^ruZyȮ4ܵ%4XoO8#}%)}<M,HLc:)_e3]` (,Psh=Ж`5Al$գ}:khf1tX8Lte;YM\<)O)}>Nܷ ,|DGs(=[\I߃U\[>l5vCЮa:j8e(sη Qc_JP,CM-WnD"GgQQRvt\!ȳ@9޳O[z{ t55dǭO&ٻ>y3rѩdǭO&>3's< 2iےmZ|'t= hHdֿIR.3H@:䪵eպQs=3kqP@"Vy2{^&v A5wRۚR.IPh#t7H@#h*<1{`@z9 }j!ШƸ*+f\=lbͷ.̶ՅٮpTT[ƞ nyҺ1OPAї"+g}4^;>g:hFֳѮGNq(\"#VYIv"߾ `&&aq,&-c4²E;pL@YL!B"Z3[Ly`ƙ`">R >5dPfc(f0B.ey̵Q*t<ҜgӚS+c_a*6jr([-*\zKW{+K N;I#8)3b8)oRI#9Jfk^F ž=v-`=n)*֝b'z oa&b]#*%Nj[4fp:KfDq2K=?V*eZE0WOQ_1Fy^?0 X@c@*Gz_ Πt!Gq67NEv=,/z$JaI,k"EYOYPa>pVY';%΋58C1$M~S町`"8ߏC=nI\יִogn{ }Nbsb&R=hOINvWy [qf`qf1l 8dzAr?P 9Y8=ETbL,ԂdI&zwfˈ}faQ׻m.m@@r{Icw<<a"-syOy"*s8̂ׯ߾ dͳj,"|b" WaQ_PVQd.p3,^Y!;e*j 6%}H'E~]䞡< mC˄޿ =@2ۇFCï,PRa??aPB,#9ef_L͢ߴn!w q[y|OݮVWn~)#D!H=Ńj(AxJr.*U~ۡhB0Tò_7@WÄm5LUy5!=k[H':奣]QN%Y^&a="xV=Š)O9giG z.# !|m ŧ̦IEQv.k׌qJ^KGXl#Aw×4q9;¢J7Da. 1Pa+Vdgە$aeʸeWA yg_bƠF5j/ElʲԷæ&a,wy }x9Rs5<^pCs A BTa~d!lj~۲P{ټ-Xp Wp{cOL:DS?%ݵ'q'T Gb0q XKu41YAjўzX\d/]M@j12ϫBNw2>` r!Hr0YQ5?I\lâ%>yռl^!ˮD'A6Mp(@!u=᡹bm$9[a 91S'@ g0A ^\ Wl6wD pD TF%JSkqv f_E38% 3Z8%Kc zӱ0 <pSDbGk qc Nâޒ KO?xaJ*^_lLP{Iu]9׸$8 K3׋.(eSll.5C e2sב9fhu,mLg׿c#7?&ˊ {8{/9pֵLdM1<ȲrYak )sKb?%"ar8tܜ%\jqƚ wq1ly8X'a;EnlllZmݮ؎c=_wE=gκiАu1Ɨ%䳅9ۘXY3 5XZ3lM4ywE6P {g~S3.fkFfZz s'U~/J3%i$L̸s(6oM0`B4~P)xW) }q=,m솑n]}f$ 4Lx_{-ԻEo̷sN\PN ULvo^27ח޸f97/Kv]咓SDtg`pXv턛en  "&JP2{6$yHc.o׌"nj"׌ڪ%azzrkֵ]8ոDOrYg$UOn96N)6n6%/`Qޞf;ߕXw圣qMR8g^ s )1du{;7$2`tƤ0pv?~huI3ڧ1 @g`:$o-^|sF7c,a^^JLy2mcoYzYWC32ht1X=9~g9Ξ_oXujc, 7ep+P<9AFrGr/D^+vZJ, ˲ޙwgrhKfHFb$!p6mΊ}v@ۙzV-@NX+26S!{ߵ(WZn@ he!WhV3笊Я𑶱>VG|/bSڽ~脵.#m( ghwn@+ Fe0=|1/zv+z] zŚ8f/$[:ک//xݩm٘Ve sWFWg~ízÿ-_n}<6 ٤c4";]‚{5,]՞keY>9Ÿ(lɼ>~!>9x(k uŗrx.kys'0|B_v+P޴ɩn!Fq qj[Jzc3gcS /~W *LGoRQIjЫ#0?+F/*2ɹe2BU_T𨋱ON* ⴊu-pVg, c}lZY>ry1s"G8ò窲; i^7YIpL.eźR#yZTnQg6E,PunwG>4:qQyrϻ۞1^g)lWE18*TlҰJAfZc-wU*wA;ASFE!V1Lz=^e9b!"5Tn9q^ČI+@`@GE߫"M~{ y!PΣSPdQ]`}k ֭A9*ۗp0Z#v#%_Yk;ٻ՞=@#\<"B܃:} r#yU#$T5"6" s4mN^v*"~pfbqBf1.oL^`Dc@a45jDmnLymS!_y:}MmpRyX6c`t(EũV w"z8 SPs3+] -+n>2܍(2 ; YYqφ34{ٷ:P@-$ ( ҿ-`B N(wJyx9E1.OaQo\ўT06C103c2"6O!,EghԟQ$u%g.?﬌ 8Lˌ̼c^kQ8:IjWcȱBՠׄ {_Svl-mP}Qv4%q)ކ1{v:6j!PƸ{nj?o>ôx">ìGgܿD?1v3s?ufέϮ[Zڭkn-5uMYeE E۾? O0Riz ӳY9$б ;'4v5au-z̪ ϪDcgCH # <**rdr@K%$aK94`0T{O0xO{p^dZQYq'ffnƉh"Tdېjt=)6q7uWswB9p G)\>7/u7T{7@8 l'="2\pO7[ˏmO W2[?B2(S9"/晴OҦ8)&k}z~ fYRަz>UbBN՟>AQWU~=6 `0tcS.e=~ORCgl*5-͛&U^o,54JiIaZ~H #m<âVz.(C9ޝ bq4cg} P C%YOYPjO4BO3aۣ⋉,b*gZ Eڦ ; RTjTw":+nR j} J$ÄOZ?3}*'#ZFUfpڌSRjMTPq`3NlwexW}Yl#Ag.Ņ?~bSBd`@hWS:Ff z$N7jeW"lSZdWgerς#'{n vo%SXxUdnbߋ|vu`=PiV(klw w$q4UǠ֖SOjφ@GwRZ|R&I.y?jY_Ӡ%XvHw^ٝ^35`3)/4!ȣ@#p*;e.>UَRNS$ޱ_P5Z35ÎҺ D 4.ݕhFdS;*sdsM,%bZ8J`W9~ae8?ih6yu[)gYggV}sx7?鄐?)GD0)ܾzu=1cNzKzPmJjRPؤp)4M (O.x%2cT'6)4E gBSx).ז)Lm ץ slזk-H_ۮ^F&xu6ۥ:I,Rh8PFϱ] o-Zn#Fz+Fb)a,g5)0QdHTnex`C W۱zxk|,򝂹#a:IҺ4*AhhmPt&CXN9;ς2.mw={{ Br.Sy>mc`a(f'd]*0Iʸ6J M,F|d\=WQ{woi s qYC~h *$5&^:II&&}V YZ7kk>m?f<Ĭ@ǪU6ڻd g¶ }B'$%C5g&.dH. $ҿUi-S(29,h O(Pp< EDŽP"eZOjfa>:ɥ :Ld~Q}¼aMw}0{6SES}݆>J2 n^~Fz+mr+Vcd rȎj[VPqn3NmwuYq YS}!, ezmWY+M]MBz. 0So] p@=zSN5flǑ~jW@$yo2 4F5PhpS=e[*&pQ^J\׎b)u_;f0(WM.+l>2ٍ(2 ow[]AoCX@#sPVa.:c,kݞpv+O$ᐲMvoл;|b;7%6s,a}M`#ݎ6<2Ξս'^fCw]{P#"']#,?.MlS5I=WayvY0oZXA:yQXo:0r 뭐' $sY"Ϋt'b2TZvYY:=e`#="l_eVjסO5L1P :ڋKEs1xvl6ȼAX\]iYOQ\8,򝐹#aVǁijU%sQ_-aʭ246Gi.c5󭞃> +J4>t gSD:&0/gR/iT}pҨڌglu;)O1yMӆiQWzrpoPfُ#8tV}Jv aQhvlGv9~+VFA*u!ҳR 귰}}}}. +x M۷Cиa:RqcoC@#M^9Eϖ4{vܷ ;@8-."R^n9톡ngAPg #0ٍ<>Jdjվ=`; tjmhx ^wsX*TI/ZQHYΦ7^eod,m6xqB`uj/UeCB.XTz}(EQv3&I<,DF ~N@g0=jH>JHt BaDH(GE W!RϞn톡nA*˴<۝%O)zJAS|.mlJ@a`FZqWu-sv4ݵ];ݵ*ڋ.4vڵlO8ݥVo?]ye_>lkǑ~G2$j.\Qsx<ڞsc 0C@[r9[A,@Q,H,zkYb{; oָf{pn~úFV_c6nCGq;nOy.<4z7t@mDZv2;kx֌c]3n:L&ԣ8q5!1Vj!zRػ׶Bl`@GQvΚͷW72f @&`ba$&5xϯMo] Xt@=S IL/IZ"sy6<(ۄ'R9 %T'28˷Ak*v9*ٵO?4pV} Ԁ,B`VXa0{ib{ދz:'u-|Sn~2ʸ@Z2v5R9z[A.{s'0}BW޸2xvPO0+Aڭ[W-l$q&A.{s'0}BQI7~Y!,cYf̲.e݊,+o^dvvvv͊y>4yk:fEyg V'EAus Pd%7X %TZQh>uNe%OifOq_ӕGinrp%ᄂ;;m;-w{OO1wC沇Fi#FJ1Z vٌZ^ZbYŲ`ZjMehz ]6 ̅0Yzz;/lCٜ:u<3*?%macvL½jI;Lsr;r':}mËY.)w :ͽr-/Ǘ㋀/w0,5e Ԏ"U(; f혥i2HP@ptP_dAegz'D=>ʪx~O ՞C weC c`FQ5JT&Wd7;횠aG(=9߉XnX5QBv7Ygxm雥(-!/sj >#Z%qz,|1ױ BaD(wcs%GM]P=0rޚYі{u 7DzeW|wުrk5?E2?DU(7o"##2DPF9?c8JgV'|5]QBv5'QPDǨ=mB4lp#B 20zEL>"<"N2lS!0#e2 LNmf Za V`-$ p92w { pC۫4Ͷq!w=0dY%ktQ*E8QݺϔYjy{?S"MQώ9}˄ps&YGAtIdà |ojmhNC4;aBRI*u=1*<ޗE7K_T X"Z|1mcHC ^Pziz'˷rЧK,%t4ARXcHPS|mwic='Vyg &IuFBz I M RRw{gk8'8ikPPj'.J$Yn"1iaiLjS-gXy2# }$q4牕ߏ?llzǑ~:Atkkp˳]A|g :imkZ[^kuAkڲ;enc};|Lrw":AӃHcЋaKE$%*߾՛ SfB2dR~E\CpaUvW8Msę dx̄:ϊ.5FVXe0l)BsFghz>Ƹy>_V{&/?ƟKϒ+)>®8WoPgHryH;.<=u 0 ,szβXv|s W} %\=EGu1f z<#͠XD!7,%Svllk70 31yU]9e'K9|'4G3`&31SxT9)¤")SrD S5 NaZWP"]96N30-Xs% GOS(T? *!~(.pue(iz58EYWms)z[l|G=8`V`, : ʷɨn E!L +>*LbЦ'GnƱpUWUFSV<=U(\6|/p  \SS=qZq=~&߉yiYq礊L=_K|M= " )h!_CruuC"4/)PX\}%.,d'@$.`: CS'a|y~ %T s9"krآ"/y~FOIgFF$5yWc\=QuYX<"`#Օei֙O~̼cB~!y "^omơnweW?w~)E8(ʬSm'kTx=@`"3Y)Ϧ9kǘ tL!N7"Jƛ=n; q]j7Yw@TQ5N:UjYx X; qʪ'*j6Іl>665QWaVQ?o0?:o'u?o632~tGlD}nQ?9ԝwI*V=ö(QI*Q~Im cuuWag砼ye 8l897_+a|8^B]zX|j-S{ hRaդ.Iɪ_ 2 TlWff,P>d`ŹTYA"JPx1|%"e ݲ"e8ͧhdsg{qűϑzX&,_ܞED18Ql=QjST/aMSrizzv˒ rJprIrrJt,sOK?UN0Oq ݂%[TN 9},P=O`=Awòb\A&qj35(gY۝BI0C9DQQQXkS5{u ^:l$*)[[տ,W0waҜEDKx-̴E~4݂#[IǮ>Alkz͚D:sf:Ry<^O=SCDFFdĉdF&>+d#\e#힑zS־8nmpS '2TZ&={f277_ j*rJQ9ReDT+Uoasa[3[#Rf qL!4Sur k{b"w1.]1I&}&XT0K9?}p(b|]9tkRaTVyO vEkp~KDm A74o^,7/݄mwF|Po|}{nVf NhH,ޞS9uɗvnr{^|{D8Yb"n@m0ɞW÷nǦSJlJjD9om.긑ldf$Y?LpslN3qI_8̴uRoˢLuRoLʤn_޾HR6N:Mj5IOZK. R@ur L{RD3Jc'r9Ձ7ZnGz:<2R[p)pYVQ]`Y>Ut";$-g UӢ 2dZIg9CHxM[!"8H3#ݳ#zm|y+y  yJ{f?)a:Lg3K2;XYq^qMױҰQ5WwnMrI~YUGu,EUtϘ!3QY`!@օ~H\@RDX=;ΙFm5zmy5ExS_0|.IJt "?ŚXkd- Y}ReS:!8u\G ]GTe{e9pʜ@Yf8ǻnNn3 ԑq7MogZ-LIM>~Fy>i$U޶c΋f(K))Xj}isVҽ04A%(!xgi}]T&.BVNaI=1 {LrS唹R8~lNij4N97Q%CoGK3oKԑӴ۳.wmʅho:um۷y杓kN~9uG x,#ՎG֣NlQvHM5E5nP ew|Wc&ijGn8fA,b0 }Yg}4eYjJ;jo@W?H5taЛ(weZRrvk}ł;di`禖`i!n^E;3jܛdc"¿EgqP͢ڋp~ 2dʡw*ML)TsPN-C %B!kJw/F#f2LFX0ӹ̒32.:WySʨ/}nVAU1MueXJǩaGRSJ)M)ڶCK+TЕct״mgKfllc7ˡf96ǬwyYgwx7}YȐ ag0JS7:Z6mZ:^n˾۲,쫳lu -;Z6mZu' uO>x>obB†u s Mey vaL +;iǯǐ5?8O43IscD8 :SA3=9|M{ymU1DT Gt>;ylZ bŋ{;UyA[Ld -S%dE]?L:]5%fhM4SִJhn6Iffuӽ?)~|'gs)+ՃQEX~f94,vjU@)!>Wm#nؔLC}ST+wzbr|v)^/}KWKCG87 nYtxo?wZ&A45 i&U!V˜$< Q WMTYVw/!):8d1^L0.nH{{~GWNz |J‡ّONUVQ X>|%, E(E S+e'y#qEs۠N"$`O*g:; 飘kE$,:j}܈֐SY%pĘAL6]?DU  ِ{[[d>0A6K7mZ7Enc@wwV !f,^ThN}b*J@Uaguk;)`SQh+}Kxg7B0?ӹܜMF :Xw0h'-6 SY|1<!d4A9_}[Tz wy*[d^Wb^J5yc%~_ dLF0$KB#dvW4yݑ۬s*X]X)+%vc%uunr_w/0/zc a %| M+Y GQN!! qp}!#n2!n2u&{]N؇> aqWwi15sۀ^#HjHبrgE7K5D({P`:H\A}ZdjwjD ߬g`s>as3Oώj x!|cwcIO$r]5IZ&ĜF=I IAOWv|:YAO3!CfҞQ ʂޗR++++¦J-W97١vT@% f<Ƨ bh*ۓ0_0pߝ>;鲸۫ehv>N"h,W`^0_Kk-UIuHZBVFUȃ ˜ɳC^VaVqaD"3!:CT SAWzeğ~BU BZ*q3,*tt<&%Q#j !ޕ4Ի]toLju?(~eQ :!Z6K1>q8OwZg7 W šB4"qeo ~ќ$@$'+ǧoEl(7 Y@ΚD:lEzRwEBځFZcTK<'=H+pimJMRFNmCe0>{Zr?':6Q9,*ZF%H8Yn*򼃙ƙ1[eR{TI>qwET- Zi VZʞݝ[T90)#CCM&9INeһL߷|gx>--^І; _!wC~ICl0yMͶ6ȲMF!>U~CNIĺ c$"Czͼ]s}Sj*U(pz̎ufL:j(z.2 ~Û3/*a_xJe^ɤϣy] 6VG>n^7t 9qDqCͥXѳ}}c.vk6kd}~^)ZRslT%R{WP{Ii6lov;HDyU7_> ܬkqB<*v.Qv@"Z kUԒgp G!Oi|!{^i_.__ dmZ Β0[=eo13{~uuqK/[uq\uwt"7Y+ ?R̕^M&RӇ>~2t.|:WVpG:*0Q"i^˴*H{!]OtRƩ[&11`hOX݅|hwvwC;vj\.I#JRoRߙUq,Whw=O IAB>I&-Z$^B%$^sYжK;L JKXRXM~אSc}RCĐ0&{rfm}EXCdQ~i2Y[[M :`Y b-14]')Բ433FATɧ?} EkJ1uR ;䈭6pmZcz׶W׶W׶W6׶Wk+k۫qm{eqm{eqm{esm{uε-5GZXǀS*7pG+1PE`}kַ6XʱE`}gw6X bEm^&Ȫ0U%p$L3*xs_&(/ P;rS#Άt&(΃kKppM+Z%هcp>:-\6MjGN-y>dEީm%ݦ?g.f왋9{j=^PLJ2KjU*ħeCS+9Jθ :jYxsnXlJ#Hi O.o":Og8K %MC!qА!.ɛɒn7ѥlnZA[uӾmk*4J~KOTϖ 1c+9cJ/MREJ:*įeC_+9Jz̢ap~GS}q64NيD=ջsf31sٴ\6p ɇ'<9JAʋ~8gjɅsn!9v"7H3|u4hkb;&&8ĝY7;s-\˸3YE 'ǁ|; 3?9ۛ+~}x6_W b4L]zlc(x]/5LO`}k/pOLT7_ YAҺṶPvzƚ,Yc%kfd-)eY>^z'͎8fZK}ܝןBpҚMZ3IkX(s g[v涚H wM$TAhB:kMi,#;\>$B}˼=VU7E+R/؂ EM/FfمYЂ LBLCZ1 Q##tՅ\=ČviDWgT>,ZF,Љ[T%AP.[Zzqf"]Ƌ,[z" /6ܭ h'q=E(ՠ%xmA~(pN,YMj #e7q+-JKٴ;˿j7zv+՛XNuLC}STm\N\TO`}Rj/pOq IV'KyfnnZ _L()ٳ'C ЅQHzTزo;(!߁ -pl{Qu}RiDy":|[@l'`; IʶȊu).Do^0_)+#U|&"fva453y~lh NLvd'[+mo-Ⱦ ֆ[IU"~ EWM~ RBVJJHܥ6Ӄo?`;BRX*]8fm&. L!ߞcR=JˢNy4c&=ijGx{TU\Grb Qxlh%dx"(x ;,b_ŇCDAd>º Uq]J~g5vǏ:Lw819$4M;⦨2 ( |)BlRx !7wffnnmI%jL"P+hcSoo]=J|7 {c;:uskYIWLW씱Y2SfSM-=FLH`ƙ>:;S ^npd{f 0lt IN xNRMO,Y?y/p`XH%Ԇ|}^hp"N-y ɜ۵lШ^ }.,ASo}  t;(ZL_I$ =YB,B8z0eW<4_$U QT1HP]MIgYM`$qj%8VA"dW@k%`V62 @C pZdmR>FEѩ)/::D}BgwMjw!F-zެ',0'$j]gż"XV sֺUл4o*ok=v.ȿuSCC !Gc^~VF~isQ@"6S)5Δ$tLн<#jrqRϹԳ-շi2TkuB*iL+=$s{&f>LkbS%A/Ddc3]m93|%'v HQD;Q{}YA+x&; ˜}%;j'x}n0aF,K!njK\<.uQG^FvܤAmgF ^ NnB(U#c'`1&LBG>^}I'5G=,RTΐgy C0y;lG42dό@+!BVJe?-^FY{iq<ˠ0R { 3IgL*2ا, `rrXdW*qcsnm۬@xbkZ‘وv.S)nm!(eM˖Ƴvlv~.8g(5#0A-4 jgԃ?V؞pg-v4g:3JkBB]k}]"!X E!Jb~j8򽌅\ۻ&=ȞU}3[<0A6W_'hHwh Gtu}M#zn) "zF8X2 whG8=ܩ6͠H"wڻ Ɣ럲" ]" ܩv) PHU P|-:444 "OѯCW}DG\qwt+MbmR]\ޜdIoMYkG qx:ħbA`*N*鶮JFq^C17  j\TV;ZtpzqWZ8+m\饮4Ë<LX نxgiɢG~ u GF9zEKE){) x6#3a&g*7u!}oKzAGhG+U~4,z~* UT̙[-'X;˩ &|5AZS2ϣۢ[OlJ }X)+#$ ~1zg waL h3d"+*3uė+2C7ʱF9aQjW}+f{ïo161 kg& !Γ qzlb~2|J#} %E!.uagjeܰ;Z杛jׅ1.L6imD{*3!S\i'SH.1>)qLSߦFh ɾcpe[dK:h׹ɞ:`81^JJ7,=l0%ڷHv`mDqRmP'zQ'yݦ P`*b2gGm2;#I삘ꑸE좮 r}'IjnZ6 ^lګZ&^ALy !KXHWM{cMq&;$Kcw6lm>͐>Oq `[pM6\~&;Tn=B)"0_ {3øTӎ-Ռ[KN.޿,޻ťN&?ףgyc틵'~ȼ%xjb85Zb\2ev2L)qE~z>Gb;a&5[Sڢ Oʱd1:2j+1Ey BK!`t&}<< kV@>N:/Hyjr$&5;*I66d]aɚKQDIDTRs]f/ϖrh.ٺO+jEنF۷K4ACrDXaאuz zиrRًeoc*y & o,&=պ`u k f]J;mLAe}' $=I0I9.I7i)Ͳs|:ΗV.*!'ۤQWC\j 9gD&9 #:h bJ7-@q쨐٧×gګHأHܣ‘c$SMB Q8qTry-.qPGD8*0Q"XXx!>uA}}s45#5pE'cgC (@G;*VM7fT=NU !U^B`_*10b)uu_\c--$} S|!?l;T R1J]I|WD}zGuCRUq2TCTk/jÞ83_8<6(k&$ TBg~Y,cm}nd\=@^A%˸ ٞ =@“$ @.MG)xy>J2IEuWD2H{.:gYmo흺y'p`< ., Wm;G;3&yHyBt'&ڣ%QQ8< H GgzYYT* 19ujHW`ʚHF5uЏ&8ԯ4 z!VE%ۦ*t[GmL /l=#dUWD ;~-tС{:P!ϜyuƬŮv/-Lx35. 4&r}ܨiպsj_B]bҐqLswWЍ:gjTV{yf]ccf*Ldh͢G. 段"H]aJ\6)6*9{&`NLx=SəˆpA3sbL%cJdO*aOphJMəIrp]HJPG^kDv֋\Kt9/hymG^#G5x[8zs ]n2,iPv[W 䐱//s9Ҝ&w6s\ƯNgP ]HiTdOs\;a]t ua ]n]䕆4,q%ҮE{vK[i8JV"T`u1üo>򃌃?w7xdY{V,`S %2 sX$:&69&܋3o %`qC=,<tr?ǗG;?ђbF~dPEx0¹0 &!ĤxQ\%pQG}TɘEY|1z}cD)- }S81@Sr.hRw+(cn(.e;rǠ&2) rwo5f ʗpd p +iqs4 \țɒCūR6+e;)+1JLڳRѷX6*]&?m3(7JjҺ⺾Ik*_/6(&Q';4-/7I(])jx />ꕫ基#pŨ\2&]O r.x'_}W $grLg*s\-|q0..8: L,bYioVrƥ^W&M\=Ek>R.ZwUT{@\ *LUqwj0D_բ Pg:LG .um5>T (ȏ"3ިGWfsUr`͎R-9sYaN*Xmek6z Q瓂|:lhI{RQ'I}9o/ IOVQLVE *LR 'DLR5@˜f&pvqWzRپ=`(q$|8ug >a;|HeT/qWȻ?!{ =8?z5{]))JP }Sl5 $i0gMJ;/⦩Q<*S7u7abW b;T/v4}a+3mUE cmfO3Gr~gX=lL!|9n66]\DF Șww[@L ړ™)({'D5gN _K YRwQrd@oS5 &\i&K,.|OrJyd0AIxP8=ͳo~]MIVYo^0Rb/VR#i}O6_W'+S7/uFhbWwҲ=o龽LH~`ƕseMo-}E{¶hNQ]b#"Rm$nQخ ?1&Rm$!96@JO`Z]E]ȼ΢w"܆\Ϥ !qvR-ocNͶ`GhHhGUpNJaB}Ð\wv6Iyte)FQҜә|ن}4rem#۝,jqjgJs\]գʤ:u--/C;,:ecLjG}ϱB)8)F~ojɒX8, w."{rd>L(mJzhr(|G#ۤMzq~E7D Ϝ@ bA 3NKQSw]S3> '#ۛvN413סv lCҴǛ$*ES%  ̑|MSt_gѽyg@# RA -1h]}~od)9x xy=Z=XDRCӢa't;//KVX3Y\XU\JjD]Q#0F(Qt)4Vȿ.FWE[.Y3U/ZدІ͑Wj<˶.߿B4YįVk!pc-DfB/...^>"ZabT8"AZaЄBS{1PֻW_>ZЯ`>CT)Z9 V85}+ d`e, '(<)r⪊I-D [M) E RAKTE7Iʸ0|{chcCLbIQR7\Sg' !xqx8RM&.2zfzj"Èh@#ϴ%*LV5u+4j+i, [kE.u+swhӓ8 ϓT`K!`)>,^Eo.z?g&oWL`kjQM2ʝUj"h[ m*ݥU7Gk-vh%YB; xĄ :3]/**L{ZF1} ҽ LvGsd7Q[Og-YYⓈB>PH*NTqlMiLϵk{dO I!`-򺨚>jeiZ$MMF1d$YMw{Yf nLYZ36Eō |6IPD&"Hj*G7UQ:{6& mMXkHZH91JC1&aM4QM=h5{<59kY@Κƕs4Z囤} B~]Ži.{^LQ<.!vQWn#>7}[<d:0Ab֏㑘㑘㑚}J.Q]/DY!.huA:zl><{o<'G3S|[Ѧ**c-ً䗮@lrSiO3}uB4h%ZhibGw%u 4g{vb'#"+e\t !quZM^JX0)|d%OOŌOr= ^u!{\]om%N6RzW:%tt!aE' xQ&C { ?oZ0>q ßپ=fgR*u%@cPv eZw7IYeHK-`/d?0An]4G<U\es?PKETҼ$pdm/pep517/_vendor/packaging/LICENSEmN 0+CւP f!7襷y13 h_ ~!C&le K*$#nv;ލ/*Jꢴ4vB$(aC,@Z=(Nu=֢swJ|gr]ΩW//e6vn(}g퇡͛0ݛro^X݈wwnas[;|\[7z>!ōuP_ymfD3iDd'8l*QU6VNªޚj,qEổv D[JlbJ ȷfA{[ z{Xiջ ̡SVJPG!ao\Z1 `ӝKj'qKϔ;< i,IJz1^ jxk0`MSiUА]JӶ⠇= ⽱G?@$Fͼʼn+}KAgKB Fy)'Np_7{X!{EӾd9h&rAr%պkʖ( pU.H[ՁJ HL.Ì3qk7;ν&Qy|x [<',6[ )՞FZoUך,hMh*8Xwe3) EgVGghCpJG~_hFeq7¹;3pP~ִr/;:$DEM4c-`bz@/䘐6Ƅ2?"' NPn*-pc2(!iL8R@w1tXHn RhXJJ2B@70-/k2-dqPOagXa E+ 1de#XQ 4Fq^ (Q*VF?DcnE\8u5@ ̴Q+14>O ><0rA%lfJ Xw]}~I|zw$W Zc~^Z UV5Gȃ3n тqV]k"[˒Dh3:}) .Ҩ bL$H2lZN"KQMm7n;h#3ZLjDe}5yu^f^˰H5腭l(uD>[_`FWPhd!R+%u -+Y Tr;*,!%H+ȵrL 6 8n9:cKxi'BTS0!(hFJ&v(rzC(Ȱ#j{K|v':;d)On@dSe ("$}R:ඥ{sF›ѷs *]<~`Vb3rqz,GeURd!38@z5eB5A#L<5e3_V' єga`Xq|t q k}#!ЙK'X}[N#Y>B9'la5sG+X Z!P$PqCt-z>k= l/ѦAP /؁qe f죰MӁʈ]^f+ܺ7;^Ք ҡ审 6IdtA8wڂL-PbYc/0ScN |~ V8 (͈?hz6jE,O_8 vS&ñ?Zb 4e ͨϔh%/*+V|Ѻ Ċ໹X|24[yLvB:p %Ha@ Ċ 3 % sunDC>!R e-Bd6ˆ*bGd&%-5P8"?S8ި*Uch$bpyid03\L&VA<džy⢉RWALN_+P?G24 ˽h•L}A"MMV$T"yu6K VMpd8K&*q4N8{jvMx蠊:rzJ/I.H|x"aV6zvt>x:aͧKki `ZG^2.7T_2LwXFH57B9pR|io`? L=2xhviPCXxte9ٍ_1} ŹYd48Rg#c_A4T=‚'XɥPMӨ XxG MNj!|5PRuKZ| v9$nR-V"5/i V$wI+-j6/'νƹ@lL,7=܋Oz_n:{/??;op:I4JISќT:BKȞC,~y^/W/߮ ݯB }`e|\akansՖo Y{TӭpW8 5H5DB79Dx;S&3{V,?9}r<_b@`a'v6j 7Y@C>2ԮJu]b2ʍ/7zK<"[-9)8 .k4m'ZtWҗ\n=}bW H`xr^h@h8|gUmtɚcĘ;3|bpx ؝1A73e FT-W#c  [ r8HOq^FQ%is0kZ! /H:KO{t=,|-ro OAi9l+Zԑ+1G#ŝj;jIY0OAAW-p_造V2 NotMv9!@`%nQIQ~&=196uMjhWx0\ږ(hŔΣO+fx{d#H6dEcF.PKETP G@(pdm/pep517/_vendor/packaging/LICENSE.BSDROo0Sʚzbh'0k{Y M W-贝Q'r2.K^Z̧nuk_q!F8վmOǍ[#hdB+֥[{0&˽@&+ H0J,+# 8%\Z?k Rؔ@0DW0D*:BFl321>rXt)raQJ"pBIʩRIf9.oya@g4?u(.sN"L;P_.9PO. /IJ7t UofAWKm ihqrcZ H^cpYܣƢ8tebBRh .xB1)"И37m0@s Ś52+C8(rVr"bI+Vٗfܽ$F28ǽ PKET& e)pdm/pep517/_vendor/packaging/__about__.pyUMk0 "6` e :v#9vAlݼbDJ :АDgI*58UMoA78)lh,i`[ TÞNkL'ϻp)"3Z;08hK5@A"#Ȃ-,e dbe/fL3^');mRpRTa4vgw~X_%Y*|i$1;ހI6H1nj;w.\V8UR K XGzm,VBpb4EobCg_:lcqvY,Ŀ1EoҿnansßPKET(pdm/pep517/_vendor/packaging/__init__.pyMj0 E 1i!2.Ea&C77QY.:7WG6@oB]4mq A;dA}xLe9XNCfs fxy{97u.`(X!_O -ɡ t(ƺP+3y6Ek~"S^QbU4l)}*rM\@ +J.Y#|a|6= b(`QMPKET*:c,*pdm/pep517/_vendor/packaging/_manylinux.pyZs` A^5qRYlMeɵU9G*%&Ҳr2ɳba"d#o4,nodB4ʪêZ$wHe(G"KXq̳K$cg(xy,iOZ*/K+9R0^KYu!X6W:%ᙸa]Y,Y"XKQzXE'Hd E'2qb#Bitۀ N,YX `2=v_>9?e? c^,SX#<'oe."XlI{ĀQnũKO3y,#k?g(b8zP X4oKi[+h7GΎo^c/ACq|~tugSn4~:zs~OQ ݛ`.w˫z>?z{Ӂ!9%ٛ)OO'3x|n_L7_^Igd.s2UR!bzDy3f[?,MhQyo|A^.Ze)aovV{!zr3jN}$|!CHlDu~8s:>rO/ݡW[D{d(x&tS%Dv|R9[r2%OeU;+)KW4CFI 6}GNiue)jfTbYVx7J|RylQ#u,b)' C(D dr CW!2 yXj9JsxP[ERxxUD-aG|}qݡ e덋|gk7,j:nc 2!1֗}g2b' M:(ոcs3vx[S,Dd7lY]߬ bů^94s ܬet~ϰI 豼p"RYSM ̶XNu D(P&fYfdQnaP\0v=S55W;ΜQA݋g@_:-N+L=8;c6;#\?\!T<,p~>FL#B*Q ,O|$\PrJX"tZ!;ʿKrCx. 8&)( i"]VR˅>וG*6h2wof?Kjn*c"H4kQnJGlGݑ} 'RBȅ.Q*U{ƪ1#(ng"&.{ʣQ?˪ɎQDٯ03|E5hN ZISU4)__H6/<,ZG XLy,P/.JU#uU`0jJC]1NUX"8]d&"j KADWz@ KzX?P͡R" lpj̲XJ\[x1qh~AeIg ܎঵>9EjATdBad/QV-WT8!P=N{G: DP} PygqA.zGelҜEm4}P#8=\(M{A =[b`ZCFbb!sFAEV 1DHE,CJH3<[Ÿi()1>רT -5n *\hCҴ0mg.էȀnsGW!3ڙLJ@?˭gl@=$Ąq [z/x}/=? it }Y fiG<0mH_96ȪCMoxջ:d"e鞍ihkw_Ym m_qQ!y{>ۍğhVjۓl/I;.k`QJ-F3AZlȺ>e)JRV2ʑuxtF!V }cطXL &taFS1UQqK#Hjys+(+jO!RMʲ #9u`^55)mz!Y@Y=-zۂfm TB}3`]͌w-Ȏ6[r8+߅HF_uov\B(;{ּग़]v` eR'6)TR2Ih]ueZ4KiZ`M5[oF ]JֲAh#k5IkjI=pFn1 5z^p1;{_  8G^}IngG Ͷ};bXCjo$-EFFk͎0$I/†2:8zZwP;uF otf⃄J?SYK3=[YI^!э;9|fy1ob]Uw=VCmz@mkuu</jc9PKETo0*pdm/pep517/_vendor/packaging/_musllinux.pyW[s6~ׯ8<,pNYv5&Ƴ/HPšYLf{/mg9p߹ k86M-Ln@Um)@VM)* l " [Y5{ЭRRmzoi&T"R Uk)p×-Yh#ih$Ţ.ʊ-[)Zٺ.MP7Bs[aGXfծ]gN2 ,/>Laa=)oZt>4uh[L&( Ղiq1C BQ)=R*;j6|VAYH8a'ef䯂$7\yJnMɜTH '}dt>.$ q:=k ;WDZ.x1zPlmx#eieuu\8?9=o_go/3.(w.z[4Z螡iDfªur"m=<{ymϘlkqJܗ{zd88N!:!bZ׺BV­a8 2KqMF ,g^* 6U[2=5"JpOBb #϶R ~!BFNx"uX}W 5`l4B@lAl:+QCKz. #מSk%N=3>='3>WWWm|_gpPקGkiو?~StO>y8S[0VC[/ 4mҍ v5I+*\,N|^;r#+zߓ$UQ;[-:~JV#j'E DAsq'%"' @-dr89Ȼ!:4ccZU`{]rlIo.s=+xhsw0 α$ 5Mݾ^[A<<:d%7ҟQ}uZ0B6uPTcaHeUHүy4&NZuU*v; N ժ+ޟσRF e1: B|Ln⹞N0ŧzkyu^+u'TwG˧~Zabn ̍>BЏ pVVA r#'h(MRvߍe!q++^DJC&TkE>FeM=:+ Sʱ"Ԩ E1Mhm+a$b6UEzqfvN?CɉM2_pLr''KCcܠ:{JPw J4p Î}PDžqDtt24Ƈ!elm,F{׋)JRDy*ťF"50ϴj\g>;xP,\ D&zx:91C0^֧m2Zgھsһ~Yg3ݝXۊ.o:d/48yJk=73;4xAF !QM Fr9f|oO[I~0> 6{)0Et/P)~uK ꟦HScI UKYh @~ebfr=O14V \읅ڇfđ&]1"MD$3lk 7:h(D%T> 3uqJbNdG+Ӡoqthyv /ʯPKETqt i]+pdm/pep517/_vendor/packaging/_structures.pyk0{`eX`?:(^Zv >5#M\_v;^I8}),B"1)RT3UD 4G :o*aӕ 'w1peϻKM;vr~Ymw󂪝3ZSv;0Xi+H@DBȐ6 ZX\(Ae00Y`t FAx A/Yr[B$D]ټtNǠSjaƥoF/BX,qb&bh cmRyPDǽ=(79&(,ΈA6bI|a߾pBW9i/007} -⏘=czs=AbԺR5qPKET .!'pdm/pep517/_vendor/packaging/markers.pyY_oܸO(`EH .Xdlr{Qvm(Xĵuъ::g HuҠ~Jpf8f\ %#𛷴$ea9i "oL·eY ~5YLh ^͞_1U_fir<>:X+5&8Z6h X͛Brq\IFr&iQ6lVj.feاxwof[wDEuM𲺏Zs vVd2ZK01lkTZJկ_ʹHz!!Isrկ3sqGE iCcSj8ciK\B XW ~*>r{VU^H*d<5ISVꖖE~a'b=fi KS k%40DW >Al`(^Vm!xc<;f}A@K0N"vk0F371y dMC<%+Zl%\/wYAT*JSve(ĤmG ouyMɱKNq ֦R] L`[GJsTmvO0 KڟKCtK¾q͛"g&?()n:9Ӌr$Mi69͈xU $vBS9FbמS0ي ^HrC.6xTJ@M=D^.LoO0-V#Ls/+ܖ[W:!-dA :yA 5I1 u ժ{&7> .@'Ho[HmO#Xh& prn'8gL2ěDMI_&It4Ygd{ji?-dd@ɶRk"W)h/25|Ag'9\iQsyjNa1riؐv.4L ,3 KS@i6f2p(S~:8:=I?|3Ҝ6[jy @c31Eޟ+{@g@V}.Ws4fo]:\}2=Cn;Y#{JKDʝgՉV: Q`MFsg*z=1S451,ةDX ~g{b:`iC6\h TdX0;bzU}l6^YlH%i1+(*htˮ'("U]XAʆd k *S1(~Z#@I$ Ϸhԩ҅ZuPC%PCۮIXrM" 8"鞓 cPc2@@f`" #5j!w7Ev/Pbi 72AF u `J$PmhVh8KQqeM_nJ*p L9Gti;bmHa`as[;v[7-T.P/Yef#XWH#-mt׋*auYN!Vu0!n0 A/P-32D98ViG< Z/DOCVdb2țx뛼]@7O* ܘ&Y-PZ Rх7l:x;O3,;oF%vDmxYs fO)fxv$^mL3է5Mz5k&z39~u C"LQ~ ΩĶ̐^?j@#|Ą sPhc$ mȢ'_o  A/fv;Hw ܦ ĤWh29)h+-/l\=9j V|\NoxɎ;xkRYS3ol*GX/?q]Ru\xIh]*ye"3Xd`$+رǂAfS%pHTTS&cE\/E~*-+mR@0$!Z:5З.QlscD^^1rPS*6!tWmsS;sXfpUt7[7hCgA8tș5nd0Ao7=B\LS ;S>Da+,=ݹi%t$Սh=e :ss64cMg l1k<Ĉy0sNܺS125gaEIk\g΃~ a@L5KJ9ѿ$ fPy2_`,40{A]9 ( Hl_Z%QP?ZW]k18z_vE/I[MV=pB|w)?s>uė@4Xс$k3u2I-xƚ&q=Z"N^/.h?{d'mvo6 `b0 PKET%pdm/pep517/_vendor/packaging/py.typedPKETpK,pdm/pep517/_vendor/packaging/requirements.pyWr6}Wɱq:4qi5ŕ4"d3H⸉I?<)xP>(P #_! MRfGfQGʼY37cb ަYmͤa {8˞WYƏ/,_zd*AQUN-y=|jUF dڦmX.w4۲}m? Zgȓ{P.=qWl3%VaY|w2h']NL3"*Vi#9 0)_,\^DP C_ -BAP-e`s^rbîI8m90_֒\͆їr} W~s < ײp|b*sza4WҳДC;n8/NKb&bWA@ Lg G~4~ p tikuHɴX\_31R"˳4C{1J' 07/47{%kp zއB)LdMBs'2h@D,T/|R|~ mV=Z|(3Y"}X]m&{b k%YhP`V`p1U5ZԞԉ]9v\"غgF@u rϢ_h\YU.^w8Ib }'ꊞD~,ѵpF"-ʖ<)Tv" rł0zxIƀ e (ӻ幟*fjUԦ Ԯh.+)ZVu5>V*%BIlWwgQik4k;2`=)rhI ]܆JգX[}ɀ3;6 > h s9pS53Z]q\a΀2z T!uc+b-4k3@=t&?>pBoڮCFE鎕zxnxIؚı 8=pW+2)rfC[vVUu}`ti^Aơ&%~cL5Ĺ_dAV yy t 6;Pq("59zHk=.킦$1]Bo5x*PΈIhxYNT]DgglXb?9r/:~SͲ*jJwPjU4 X%W**PiϕZD,vbNI})'I%vqHlˮȨjTQ 5 6԰JX(IV EkrPxDVHγ(5 lLU [Y۹TjUb sx (.a%Hr*|M3V({q3ui"/B㾁yVAuQq3˾Uh9dL"c2qRhoa2 R܉Q [>ALl)6,,(Kq5J2 yTl"_6,po!!Qkb]ghQ]Y50mb T@MfávNB*+Ɗ)ĕ [sI^kRC<<ɤJ/dᆢmwTIVp18G`W|Ƴ!ΡgU\ Q p4oMP[~vIE#IvbMQ_(ЧC w'i0Ni"3©Mi>*;OP" 4|4:`N<m3HSi!yvH}mfAZfYm3kS\Z'@ C36h. *X٬$EԚ&7rhSv뛅rѫ>s}Y6x䜃)ʵ$ M! 5.mɄ꿐4!XJ{?C)/"Hm0`a1Ƭ\9\ eO')載uJKUX[F)蜵nVLJa[kPAMtGl:+JReH{!tqwZf32 'dP9Q}:f).7`$"/Q  #{[ 5B5LV5!7?1x?ZɣA} P0b$\) /c jZp p) :jfU,/gY'I:=x,7+"Y-؟`mY.I䵒4z6prcڴc-t`k+`s\kܹBh|]W =iUkWsڶ3Oa?O2ij؁h08t#op9=DNx&s{Xr!먼)@1ObU Z'H6(.qtppuۢ$;RczD$J8 (+J@Ԛ]!!Rv,֎Jb'p$p'#AD|>aG_89N UL&S;,}s%|Z$*] }]bYV`DK<2G#L*X/ fdšH X{!wu7csU,6Y|PS"h ס?uV7-udvz.LOF!h{mЁ-Ҭִ%``;:Mhˣy'Pu`ӒHƘl<(2@ԗt!hS,IJfe :3&:!fFsbXӹ)3S8'3qi va#TM\jug5|x P^aSK s6%;m8luX mPv" BݹG-9qX$ǎSXML#Zӟ=3Ts*%r5i 8h\Pv-8MՊ_? zx޻3z"UR˜7>cst@M?s}|Ŗ`&77M$&uV]mi*]j ϧ5n7H?b\sd| [{cx]Ou83dNLyrqU!;)BL['lo{' 2(d{7~nOHZI>oϓYdW5m/MDh( tͲƸЌoV3 OPzI_Msf `t?z`([3ɡ7ݽz ȴ IT/)B3si;#t_hj_]KM_dh[RjIupca^no2hT!=8V @+0qb eE`5iY5&7Z~_NxڮjP͸5;γJSjrfmᖱ8GZĆu@uyVXG]'<'2%)Q xspLΣMVS6xV %nG6]:&cѬ0ϡq-S9$;-ۃ>9'_v''2 L ̌*k9 ă2b&yV7C{4n@.qe5y8 W/@cr4:9THTYT ,HL`=D`Г~ N>bP wt,{۶{Fs45tbh\1r1>0ishc++xq7S+W$^,,y8AfsH5"lHVtw;N1)1tܬ0ܗ4}2w!&ש!tJ(V$ˑtpn흧qn1mKϘoܥiTrG1QSgS bnhib(GK?TvIBf} Hd)GyNa;SQ,IǐΦfWϏ Y-CLuc]br1uη5bbƘ!AMrTjؾnMi0z!:(e^z-Pl5MdeC6IZ w]8gx3)i0 v}t>i?ߘh 57 =\0 Hb'WeuEJOcߡ65RWVj$70: Bƨ"(OT|%:ʧ%/LD`4V;% χu[ݭ8Y2I13ʛ8XSaX`+k/YwK5at.v8 U<?IxXgwWըC^:us޺ =z Yc=+KZNwBڅh: `]`-K)ߎӂ2Jv9 ?[LiJV$W ʺRT ^*Q+Y ơ &qվBG\렠kAye⺄srAOsb[;sFC\o7Aq/:bv;b\ ib N;j]ֻ>hǚsJ5.s5z1Ĥ 7"/AsWzQW.7 w=dk:3޵ amsnYbڽko;˙Z.DBg~LѶ1saF-Xa.:x^xg6MԌ.a%/+wUp^ZZdkiu*6ĂU[  /wnߞPч'&3`a`]Gc$VA=ikU^ao`+&gbΗť |1nxaV֪"ۼpEv$ڐ 2$ȅ.)NNwOxD{pG8 sT).`WKzsu!WbUR>D+^Rë!Zر^EqdǏڃi͓4>F`Q"p4&#G F&n#'So=xri/F帨 &)Ȩ!4:]a` ߏ87FE$+-.jLP)oTөchĩK9X[`kϕŰP-F9XWJTwg)`+ILڟXOCd* ˪R<𕶂`֦ ߂@"30FdryL+bUd=<;z/2 =G]dr XD$t/锌*նMVPC-ʡ1la{-Bbh'_–^*BV!]Tx\ϣNuʼ;Ocj406L3f>,4`x1Su7/MnʟWrj \O;ֆd_||ϒp_\*t% [VPvۭ`+RG&M&7ߍF(q$mu踻 %F߀(t`0  hp[L3yaՂGBEeem*̈ݗ]!_EYTw6xwNuFKM SN?UpMS.WG[]76:{gm+]>ьTۮ3S:6hU#[7XWNī* UkZjA2m%͐re2aW~f5pnD^!oPox'Gs 0`BF%ꣳax*j|[e lfT0|޲,ʾۊޭgiR$ĈSr[ pM.5# 9Q]խ-vz%$[3N$cX\5y+V_qVj%Y2Y'6aE>b%yJ`N>pN??|r'YU T,j EŪxYDd2Ī,e/m%5 X{ȗrSO3q`%"Z?c?>'_>~<RK@#ĢǪ'O=v%w`AꨬkINo ziS_rRǎGP|u#3? aTtO,awZ@Pd^ƶ_9_,"~,`b0ODU-Ͽgn4ųi Bo'gol=f)ir0}^V\NPjՕ& T:NJT8 JVM mQW§; Pkvuu$,lJ;O#K#]ı̊Z1,,ADXɅoc'tsj|ʗC䢎PlIb68+nl}.r!r(+n@xpὙer  +~6.tO^ٍ2voIs`Xqq}0J1$d_To,.e+JN@_%kĊ \*hR)!Qur&IIX\rWI~WxBݓFvI9(Ͳ"v\)ЦV`A@YYi*e.foYr6%LP'+f~!H90)#X_Wj30 #LL 1hlXrlf-m&TEĒ&!`δC&8OG]BoLmQcR+p4ӅRD^-_"A":k1d6Bz~Ŏp#.Oh#6!ݑ bwėuwvm0.pgBq" BdIMCJ˹HBˆt7$y6!Gc"w)\CK){ ?̵qd)@d8)A\*UǼ#@ѯAk$&i3ml%0+JXQq̢]ߢ23whlGeX+Y8]nqּQ :Ck^XSΎ! E2:tZ&9Rg`HA}{>֎s Ye/KS@u.8^}kVpB а\ $g\%u VN2E䐱AI_p?`-Z=Wq2BjllXAƘA4* \pPJH^u)^6`z%\ !el-t^Ǯ]2LlӨ\@qUXLܺ%8 z]h40zCځiD?~'Wvߙ0X.Nhr(RpeXχ>'uУX㣙^\Y \G ۷;1=5j<Zg[-p(ƋFdPlAU*HR(wu:rajpɔVjSv8{rn(@ʹAqFE?ʰg })nֲyfB,6 D  I~sL ϐght{efZJS2?>E 05DŔ3j3#荰֥ԂS]wZM5h/ LFzk̽ JY.fRe)/6e'9N7sGu=?3D%}-xm2-@#A~ BԠC)v@-ѺpugDu桋Y킗5UUT >; h+ymT~~I8\ӓⲇ ; UR;׌-{U3ߏdh|O\ b*/%9ĝ C?Eo,ރ#pqC]dzCB@x 6덣9^r7@`+â sIAd!j1P9Մ2 "Mzh6칰iwߚwO3gIpq3Oe>`r}Źsw~Gq])x_;i t4) X*v** 6g"N)s4JI\I^JEvz~8{ IbjP|{ h Ν1ۭ KmnLųP@{B@v:co|OOpRi AvqiSެO&XX1j]r|CQ;wYqE_|qaI ںܓ=Y_O/ 3ڦDIc ﱢ{(sh'J DU?NlB6 8o7a=a6&uRu= rQfO f_."Ou\ 'hO Mآlbf=7 n__ qlOp]:EKó\I l78=:  0NJ-vzh}Yzhj6۝Np$xh.vCKy1s 3Y}f:Gw^0AR̵nm\B *vM4V$b&1۵ Nڞ*pk6X2eCqw͉N0|Dȯxy3a}SL9nhf;ᣔr Wי?B L| ,&3&aWUjX .ƒe.M%OO;hϱxI6zNLh2mS i{ޙ9ORر6L9[_dRh#Yn %14Scpq%Ft\o͓ jg !kX >Ne~)!>~K0a!oc]7 b.K{ ̽XP|A_[N0AC9*"ZuEjR*le7/n"r֔)A웵1dZ!UJl1:c #M_ Lގ>p-b'dL-dT̏ }V7L u_%\Shf"(bՉeDq6'5zsJŷS],-& fF7ܝW6{ſx O6 !4<3`6dMKZ[=#$aB$Ef^odb@gN/Nž))Mm"PXJ-Tdo!r]h24uyb]JeH[(s[b wX\7%ExY>fiW&YF2YFP&J㜎u\#sZ\'\]JY̹BFf޹Tk2syFRqoB<Ѻe  >'y'JIN< E]+5,J.T2U .'𷸵E&4kDgߓ%זe`Y !ӤH-`sK!,3)Ptm\s;=;Lp~⃑Ryz?xýʷ&>_P?oR nB7<\haQ؝ne\*;sGvHFz69G}O=^c.Kvy^H8tT[ٿ﫴n\'7Z^jgмjYgGމ6l^x~5d:_&=E]{!QV] $ttst>ʂ~.FΌY~2+Q,Vi.njD&>oR|aN}o9Q6|;v;iZ=7Q5A#sHc.Gr: e )"gk>] xD_Sñ:gD:ET'ZkF_j؎*: ++60֌\ixwhQ(Ḳؙ"Mʋt ,ks-XMfS6 zc3׬7HBMKbSy{rb%Lr~(oL^ofmi ic}{hxm[:j.<Ts~" ="G>]`Pr!0eQ 57,ɥu WdJ1M4;*lT|^5sL۾ 5jX)pa UFXZ2z=^f^Kui~ij%)E$ewH1I|F)K_/N: 0NnϩMZC+V(]1^ XEqwadƫDdi֯y2QL"[$v~l 9Tg>u_ʌ.Rн)>>y[2 rs/ )e[,P0EBRos`Fm\=?{3f|okYMC4?7~me;[oC0z3a_Nn'O"I@_Ϧm<ք﷫yW}kD0ЇZd7ʠvtmSރPTlHsuE༣iO`"W'o|Y*Q^aG+%1ll4 WEEX*H!Vx YrE((Q}2J!#Li֚ǟϏ=Yʄ眎.Sd*U{Xf9]XyyHiM]T=Q`@#J 2IL:y\U};5z@&m0 4[%=vi1smL>+dGКBԊ(=tIulG~V ~YԾҙm'-Ih[4~Tw$wJ:`dy0a8muφV-3r]ۚ#'y2˺Vxt \d3#üݐ8`oiyGu 9MӞxۼ Ә۾yRJT7cOgWf4#yBg'՘u=vΦ#}{iLgJ4V 7n|6&6&HYF 8T{y5[lĎӒ!ԬԈu 8fCbd0whWupzvZO>Gl~y7 V+P}`5NV.m?4$VZŪ }Sl*J4橞9U橾PtMAWUIk,GGϘ]_+s3mmsj>G!=2$Yo6V5bId0SdȢ2F/؁ݚWlGbpeSVb 8XTAmD.Nia+BTLY.ɴFSA$RP[9 ˜qǭV]DjmětaetShNFJoF;-ǭYZ[|䤅2Ƅܭn.4JV,ѳÚn61oYPz;S7y }Xp6s-Dž΢?;wGpHNӜV9=^Z;y eg]B0kӹ]yE&{J<߽,1݉>2^?c b͑  TgԱ 25IU#;E\oe뙁.3@mB8zʹ Hֈ+õ2od7{B U@'i5N}@ BAԵ?Sr&kCLIņ[zaHP7A0TQZ "Kp!O2X`-jtNN G4è_0}ZuIG@ÿ~,7C$p{5NR.EYa.gίpՋC=f>'-t64.{hizۭ=L7?d<QfF6 oWp3~E;.pMk<]Ob ] ]&>S czӤ7z0P]i }Ǭ$PTuo|M 2c\E?%v ܛLW1Khn!a#}_>+9S\aP~e &䍨rr>8&kApnK^w%9װm$m m 9a# p MtnPxr[/Aoc  z";q@@(%wv zZO%^ao4:xzdhRd(Qb|\= Rd{!#( %{3 ۏz.9 踎yɽxT!0K0%O`10nF2N"h%'n{;TA(,9x;Oi~kst&!޾=<.3OYAK}?{{4ăC _PI :-==mLj:*zdԲì0 2򵏲+C9_|:KX,/JQx[ ģR^yqbZYGߎ-H=X d~`nb@xQeD+O,#DDvQvQm Q@)ΈTW`B ib)SO6P}w85y D&r̡,Iv?DȞbOaD*S'q9 VG}w&Go2[DE=yhl\\y qlcL1_3Iɳ[K# +q-R2*Ox XKtZ3:Gk\T<<Md2QB!eE-5STN>AYV|_5 k w=Ne:2DĎP`8hCfX[ ŮY(ڄ`:m9]s&۔ĒЎaPkX4TFяuztҖ &H@σb;SnRb"<%H) jA87P8 9qA;qrP7?Nc~fF)iF`d뵊n#'H4]epBt$ <]巁 GZ7PLn7do_ɣ1=m4i>~d9:7'{ޮMJ9ox+m<}4jvȆ _l"Ƕsaּz_D͟xonSRT  8>I/]9K|X6f qK e _wnTO|닖$D=?gQILulp2dţob+d9RjeBj0_JBkhؐUiT|0 5~x_&NNDwiں/v__}*4ۥocW#301Q]~_ EǦ :hsJJS`IAyA76IݱD ˰ gM $ j`kGOf#37K~56j+=Y5QWʚC(=(Q:d:# |::tto:\ -w\SU\ݍ94&ik $coKm\5*zMCs=hRx]˒Us%4jQJfF['ju\!dOibwZ-ЪV jgI8jCfu;Pm.@𬽺nSGY[׭]C]f{:ܪ3I ;1F6.xZrI2lĀz*GcGׁpб}OX҄.&v!9l@ftO6\UР`נ`TGUڗ);L:?⯊hPKET aiJ'pdm/pep517/_vendor/pyparsing/actions.pyX[o8~(D:nam;nA;-A 2es[IʉJ8mЇV@b<<<\!c܎`"cu8^Λᚬr:m" q*t.d A,KeDRQfpPn6B4U j◅JD\( JȔ[ xI,kI#gtIfВ1-Ti(2~Mo2+ʹAJv҉ e([΂sO#TjLްlpA߯5q(5NȰZ0әYQ9b\0jvGiqCn@Jk \u]/2xou7 HLx""3̇}@SG^%ROH+I//H\uc*˅Y?SnvdbYg0{׶sD7?ME0 XdU>Cu>#kleMjTXE7ڭB7Y1ED"v1S픑X˴Rc!ٺpFTV%U*nVP Ar [҆^Uy2LñVB5lW00v8#V#j ^.Wm<\EEYSϴpj˼F!aok+\3FD2{^0xx򕠍 Ł8>s~yF@_u&qUXKU8e~AD/ VKΤ>e30:u  .L5zZxfwyxԥ-GgZ(*#jak'_u6lHۗ_7s˓I0 $p1 YfGG4-t`(vz]hL;KsK[ kfiD$3Q^ٕkO6uh/J6M I@ʦ,D'EnH\OɷbǷ3 u$Ar'd-&IG\R s,\7T[Gkۥv^>jstX]Vi`BkO+t٨11\2ձvPᥟ͎Hi BŒ|m+Ϡҙ"žxAI5ABmE9: $xSYWd(,!ƼY#^k B=@"f-HԋgoyyQҠMQ8jSV! #4z8qZt"V?CGҪR'/fT' ; Y8 /u9mUC *_[XԽo#leV06 vߡmktZم?ѧyYObyPȣ:: >|K%|@'ܦT`i"8=sq"~EX+P^;\cxZa̋&.&2 @q5LIK̇?:2>=_@}p߶^2[C^ˆ-hW?QTtpU's)g'9_y/'z.U㴽KQNFt\CCTm՝N:((e$^IVmP%0#x@G07MWm .~le)\~[謹?W|t6bܡ}JhkDգ׳5AG =8Rr>t3?mLt@Cj#\md"3@uY/EKAϵbCN&p3H9vPKETe~2&pdm/pep517/_vendor/pyparsing/common.pyiw6d,R|$:v&y{Ưqo+)2DB7JB;^Iv=X\ K(ЊW;$ D #^G 'Os؜1K2 <܉良ChD1 '^gw o3.{A%i,R6[ KS/ Sb ݰthʈ4%!e00rF}s/%iL-W1Mp$}C4-K2Dl>[2+>tE` -L9M8r@.DqB>L.xEI} \y!gנUc(`zhH9:`f18)&l#' U,:Azn^.]2~% u]X~Ӏ:|ۻ/)1W>~x~ܳ˩h6\!twgW`nn\a]L3ݫs#(j 6 T'%PXc"='H}(K]M7~DWA>!|%Neg6&G[>"U1 }+Y,婾WNk'Q"d+:pI[@@1Xmx ᫘)h^O7:vlbfW Eٟ# wU9qj0--*ɛ7Rp6Ŝݮ*y=| b&&ǒ}Ɋl38jR0st4 FDø&0_CaO f\aWih[k)uw ~@P܎+/L[嵶Jl/dn cciB`HAC=KYTd\ۖZJ:=نC"IhƄGE$/$B r~˩f2GvIrsd+DdsےVTҭd9J\֤ kL dDJz"+':(XBS:tRxXɗtozK3δYe;>O{YFF/Lg#@nD󑻯U'$Pw9bDh=F Ȧoɖ'ZWl ZBZD3Y%ӕyOeʟOK Ƥߦ=~%RX*w]}!:i&"B6O,̦q Xh:l5EgjrP\r`p5rshw&)x'uodE0u(af3Y|ȻCz\ c>~%d{M/jG1B'u~p@h{a gn$0w?>d>gv܍5ɿTwŠ6K`Z*N*@f(xM*Vm<4F|>Սo7U2Q6׊|B@9Pd]{%(U 9@Չ\&#֋XCxN ģYcUxs4f |Q+LۥM {x݇1|bww>d|9;E˱Z ~K2r&K ^85g/7V˔&j1-|"7* %D|U0<%$4DFYJ'$C-ߠU#~zC % ,hd U6r/1ZTC k-Rt|݇̚3+1,97 rJˏК@\BD٨eE`᭔Do1u+'6Ha59Je! ;DT&/H=6G96j@8md|9l/s+@Ot|B4"sddQ+DA̅6 Ya척\8}qړO?Jo/,ev~o+ RoY[O",QP %f !~@ 0nJ4@ t.|\]UAzOdE'(+h_\*60)$tCE#:}zLP'''7l͐ ^CE:B!v($p~bd xdK#h:FƆ*-uX?`r%k!@=03b(rdFr~2Y 6|vGɬ_ʋ${D\,9"?]6_0bڝou[zuHCN?z+0Y?TO${+{ۚ_MF.yXzsUz%͟z#(`2t*ɭs7$c̣EAb.8Ean' (Q0og Xt{ܙ ˲_ @~꿬^oa}ѩF{Vψ(de7_h)#˽+L9'±x~=dՊE/壸*g"Ǣˑakih۩!g4`Q.c hSPDQ B) ʻ{9pv>DGhhgO,"x9d mykSAүﳧ]zF&8 1)wA[h>؋)^еW8K܅g/ q9a4lfZr p(/ZOFK*ym~i[a$y'DΚXG6ڹQ!ddZouSqOۉI#8xD,kC-vAupC#&CB#y3һ""Z o 7Lvoma‰s ΄{|2Gk?ee3k6xZE@7ȁ/2axJ;yǷm.f%g/Hyz2M"O Wi> I =:- mP=_ۘb7 2 V9.z}QVꦠ!hnP/^1AϢ` NX샎g1Te{d24'>fmؑu`2j6$VCjY)SP+GVmѾ^ ENu^۹;WGbI$_stg"Ljq pP j]2~ZG%ӎc$A1lipԑzo@9gXfa gR='\Xx>/cyS*:MVR:UBs*]'*Mf.BInRBvvv&ӅC),M8e(V.EoOks4*ZbS NoF<t&td5bgxPKETw&=$pdm/pep517/_vendor/pyparsing/core.pymCG(_19l!db%'d}0+iK3Șu|~~I`'wF5Lwuwuuuuu[ "nY^TQ^|U74ig-ѬJ,Dq̒ݯ"$V}7o_dt2MT篻dNv_O2Re%wgN_ӁWɯ$¯.0|ۃeUjTW$| w%( Q9`P_EJHt" ү0L![=ST屇E<|D>c|iL/ <Vy>)U"*xdAлY\]M %\7yN\JT J#>phɧ?ߕ鿓5@a)~E~ vm/^ D[W0qnhcGtv>Ny 䋅_&4-KjUR$7%LBzѸH(G8fn" *U81,[]2W`ed# F|8&Y#rq:IʨS]A_+RCQ$Rk@j>ŇDSԃB|P&4VDƁ}E|oB/&)ڽh"yK|.vQ:FODtZVL53pkQ_6$wX2ˁ2`CSdimfԷŒ ~;"ZG2*!^ `Zy6Hf)S&fGwD%骽]wvyM{3tYzR/;" ^t$~ieH*Oh˝} ʹ8fzLIv6.*FXɖE@cWfQ H&IhazZzDjz02/nq>O)A jmՕ3#̩{ř%j?ɯu$%jSM_k`i+SkQX?@oWnޱ65eNTER͋LK 5Dfx"xˤ^1j^mঢ়ju׺61Vgj{ /`3*ʵfߔ fJ|/W_֮wVvKJPѵYZӋi9 W eH{yy}VI9p<JA`EA\me4q"!!@qtQ8 ɤWRև+ +%d 0&Mʨ5xz1# -ܩk@nMx˭z:ޑ3.:EknoXظп]>̱Ysy)zLl`x 'U 7 'z`>M "\Eݸ6݃ I.W gm$`3;'?xut*X<7x96}=z{zte??:xF^A"M/~>x'p.]E|xmtq!6^tx?}QW%pqax˄0MMfN7di.yC' jF-J5U fӕߪ} ԋdˎ:%@[gZIw9⦷+8.WUR=^)Х.KlX7p&y!Jjn[Nc7 pH 4 f'o{Sub B~XX]!CcAj0~4S7{K ЎCkeCndejil^I9XVɦ'a\mx Ck. IYs e\Ulƭk +I^u觲>ך}쒂" hA\Ƌw&qRx/i@YE㸊'[$}I}R'5 uu烡Phc' ,Hי%&A'.Y ڧE_Dq xJFꭔTt>/刪^xG Z='!% x~=`Ѻߢsz,8<t)'t u}EW#]LGX dyiuņ0u Z*ʛDBoYtY߄t@g#A4)\K.>nZM LSסYjZ'w|^d`|= ~FCGa! H. >n?ׁ]&"d)<]]K1XFsDGtw( ;;vjx-Qyg{w~<8Nǯӊ^gdA'X( oB&tviOs>gZCuZ?kRE|nm ,]#2L!K{LJҕ3EONv{JRT\džq%Ga^$@n ʋBhuvyF: ڽ o?P}wz AphI`?>Fc93Zgo hGl .7l5q!Aؾ9LMlo} V,N;ͳwKTg]J]QڙGV[WhE,.lDJb1Ɉh"Նs(X4AdJ@v;"BKd8CHN& QaϧpJ 7ZG_GoZtP?Ǜ+a \xVU\L0h!rs$pNdՊ#n|.;ƹ:*;wμֱoZ(V*yBgF;9Ye220w״ƃA ?SK&rnz]}שV[@cl|)q, ^7'ß=)ȥ{ ?̉ ߐG+z B7tC)Ge$$(oa;ms6uOny(0K"4/u>DF5r~AP=IUd2O&Q`SCLVhQ"y@ ":x%ý(~#hb"0xrhK?;N, 'E36Y6UH_Sۥy4 yJh( rɌ\#v4Hӭ!c 8o(1o03Gяe2OhIoFxC !čFWg]a-75"u~3RPHSsw )ognߌF[RKf-mn<E8:m~\}1Ӝ~3_C&6/?[/&ȥ?_l>a^_<>_Z'_x:6'N\Q[T8 - Y7vo Nd=::sJ[*m=K‘?F>`m֒k8GRÎE$'Es0Tv+3g|NJXy u};X9%9kHb%Xɠ-Bv/a2gK̛|.{ ?{cF2ɻ4p?G)J О5@aIu"x""s ?tu|~O1Tyl4m8|_^sndk P}ɶh4" f 46a APB8ΌoE$f?aޱytu odvqQ$o9?" uZ ~⒵5Kk0m?`k9Ppfܩ`c7eK@mEHM>ZhLT+5 1q}JG Hi=s7y!1.Eu̴↷}"LKy\o4,F}~1R?[c΁L"dx .ݭzFIɏEo|ع[_&-lL^C ݂#|+t73Tn'e9EسB%X8 FLj2,~1D`QdЋF DNzԭ+U\Q`KU,C|nUTQ,bE ^FF(zKP^wnb(D݈f*.̪`[ospNj8Sw;r{ki;.FRV3yrcO4+UtN#V#bV2!rDa/?rQuՁC #$ Ixf ͒ދAxrlK1uEVZiOg(q }!)Ź 4& ^Q 4C;4oH(ͤ8h]|#&h^bEKG–y</:d_]ynf!@8DۡC wy?-S6% G,2ƶSQOSi^rJAs 3i4މ =k; , tK^_F[p.8ݿ? ϋ@%pBQI xMF,jJ'mVi몾{6Ʀܳy*PcLR=:d k@xW0ʪ.:ʝ+K:)&:11r[ p׉rw)䬊0P/=Wg)F-o;o-c5eɅ{n*vF#G6%rK}tJ-_J۽ 5a^m2> $(vSX?ArA?S"cE̐qBs7᷾0'<ܒòx6 0PoyJ~ ߈?@}H]ZO lmFzU(*q`á-uj1Ǯ:s5%YDG>(Yv~M`ѿbB>ڠQ'!Dkȫ9F%9jy߳빖!. %ܯ~?|:N$rÁGܔ }+ {–?+кerd6`#xe;޸k0]du' A#P$:"~ ݴEO h\ );C`1(;v6pVb+)hzbq 2ZKx#Y 91nf(dBm%3'*Kcj~tHr= JE7v5N_i?6Y<7^ZFKkjx<*S;L:jua wb[\I.#BT0:Cc"@!Ijcd0SnˇO>|rs')y*|v<[goءS2jeZڲIՊ8+#ԺxxFVIV_&n FK\4zK (tCP 6WVC1 DҐpfwrqH?Yb5 %%Dmi8P >:n{2]ZR䗽rDa n]oH皗dqdXQ`#C4ZƖ4aX/9[L6 nF%AsPi݄\l9 y\H`7l[+5N9j]40|;9ttz#'\^ܨs~ѭ2)iӅUzB@wʪы6tۨ2JKPL ku{s#sC؅Hv96 P7 z]bňk6)p'p+{F/Mv ¾sۯAtA-s!rQB)(Yor7TB{uiFa Foqe cRe%J:A=c(O'ry;4&-Dձ&I /̒x$*Ee U mGe #δV.a]h95J ܍A>,Xxhdj  9 S=:$gf/5^fJ0k`:A#B wwM>44 O;N .}y L8X'Oڅ(\ r 'ttú@QPW@cZ311/YI/-z*@0 ,]S,ԡF [jt,w+rApJK%oԡH ޷d'O+RJ6{fx̷vFfOXcpb'S7fTwhfZ8}PˋStMwɮojgC+RjJlwf^bP.px TX/$rស[th).vvvcb flY3pjO.aL_XQ&"uB#AW~Ӄ]e w N  ODSWZ};’B%nR Ac4:Af)wSVaac ӊ2+G rgĴ[HAn UhhӠPW BW̠='!KaV&dG[SRX i[XX4-]WZ-wk+Ҟ~Z[-sZ%PEM:h6fp&[P4h ]sJQ Ўpkf-`wi1ƵT$}YB>{ 9}%*%ޥlĵs)󏕞 ,@I-.37_~yτ2VP ;*Hp9=#*uiKAQ`ņ/2!dG'C*ih}8RzioCX \+zC|8>)= uiu -F8sKYWnP}D̳%jN\M*uޅ^ؚƞa.E5=#/1x&mX9ER6fI) 椊-ǻ{?OHR)n[yzA>zٷ-:9-j}&zt';yGYPc N>[*8_ Za~X#TzE`8A4x+Χ86 QGsUNfްPV DEB_7d3>ߞ?ЦuPN6:cJJз$cGRALm Ld-_ET*%zz-{z?> ^qGMS~pAzڦGH=l~!smK=|rLj{7$#HIm#-Wsi۾+];ۭaiQY2.hƳ*@ɠX6߶NvI+4$sj I蹧+-e|,yh; `{8Ŕi/قױ KAܚ0j).rXZkULFP cCA} ֓VpI.MbS?umY|2IagQSN]މvd+䟔)(ci_ܙ-w`1VƘlO1Ljb@|@=('U찎O5uIn9w:>xkKo)+1afv涝&xYE UXG GyRV]ѣu\T g{P0ݲST-3a:t?~>$-`ز&jr&vPvʭrb9Zva>ae2^w0]w0DA~7jpYiQz.x4G|pD z{ +BDBGr+Xrw] T"5캅pDϸHcՋ}KoiA)6ʶSEq(9(jIc8:RN'd=4<, D0*j-xNꜸ :_ҧr8p!w):&9$`J!h#Qwp&T| $ќ0ma[+ǼĿD>M7n{Em, V&io&[~/js7I G=tA(t[F`tvex\c?~ SteЦ8D<[킳RK&wmrtB[ +|ĩrsRy%_V){.NS`#؏gT/I8ėIZz{<{-oY`lꣃ8k6Kr$`( LzgSQ8ƳilJ[ywR4(gn3,:j1'^6=@Fp-' rtĸ!6q:-f7 CK3ӫg3Wd/Ԕ46qrdVW͕IqaxWF4SySS|ԩ#8@PGp8}.1g"5X8'ܨFCcW߁J02 StYhBp̑gh$lъ/ѻG_}Z~T0)l6eEV@~4qdHy<ҙDLm;s?w՛ 4J#6MΣ Txl~M1Mr 8 耽_ge2A`޲$Sb8xdTR6Zx%&ڔ .Zٍ`\rM9֦#J܉'Wi#qiK7 MM (fI\: Cxu rل[^ NL[LdҍJ&}*Wo졸 곲(^;ŗmYzjR"=_9'^u% ܴ JAʾKD{R3<9ե*;t.i)܀9Ctt!U2F~SFؘi#Jn%3VImbXhHJY+V|# ]l6nSKFu֕RsoXBuRUKג}tw4 Tf^ʰh`9V/D%Ѵt,{|@sJD>QNx,_W._#J'GhA[j?ܡ<NWǁŴ2W0 djߚZa؏:~R5( 0d=ؿQv  =VmR{."?;ls\SE,#R:"T$SlG\ωZ>k} ҍ aզiI:Iӱk^R)rn7?o~uk=WwdNd c;g>$[#[otOGkoEuR>GT" ud#Uy-%حN H`3xM~bE.y}X|獦q5#5dx1X|Va`GM>0Sѿh9rch1mЎ X"6!-V%H\k+-jiosc6>"ֹS9EGWqqW2Բ}Nx-͍:D[}ŒNck)Iݼ(۷|.Zs 3'i W dVe/G͍cM"sezN+R![}]g$~jkYrqӂD=/DlEr<'%#`(A>>ـ#a[" ZH8ꂪV%{J֫^FeВ?ʖ^ׂɈ]kn8Jx^{1M`m9/ũ&-k3p8B8#M; kV6SB.9L1@SYUc-aRz2dZӦeVDI|C(.F:'*/c*v4ߠsu DP1QǣHFV4FzVmE@W*@Nڌ֚+ՉNF~ UW+LxTN.xBJq+uɍՖePwQrJZ<뫄<;(d\)L{b\cfZ[P$7Èʐ3cQhgLI V}PCT^@Aix$ ݧ:dG)94_DQ#- dn&sv8g"L^z&x|u&̛ lƀV(BV_Ӛ]%JBێĨI]VH4a۠mL9urZ~3.Z Ϧ9PJ;kc }{gLԄxU~fPPQT CEN&ٺtiKPę,L"3O" E VW  oob%(NS"˜jfu0gߙ;ƈz+ڢU#{u0f0IƂϫl5q%/95M!wd D0l3e \8(昑.ypfn"+;P0v$I5Io N zk|e!Mџ(3iy>Ef3xVGmه-/FZŦ5K2nC_CS>y 6wқ[m;V:v(#Ov]Pߴ._j 5t3V : tE3B/X.P  +\Z} u'&Mlrd ~@ŚzGǪΓm =xVX{ޚڴEI5@4N'g0E@Bbw:-F{N^-!!bG V==R?ւa-0\f3!*KD3ZSC6 .} R/t";BTO"9p0]R G]NE*;2b^&wQքǮ pvOt%"aEG#bG;Rv9#(Aa\4iwP;: 'isvG'SOMKSpdcXk,e њ+drJt R GSiSBU|EZG?"B,U|/ENK:#\|:l =^MiKZ,Ed nx,E:x4l&8p!q6.q&*ϙvmX;acQBk 9 s.(b8Ca~-s@s:OY76=/-HO%ux -n{ "^9QAK: ր]"-bӁ8J #ܘ)$^*_7уU}c 6G?Bw66=ᐂ9 4}nV^,'Öf L#x5hٳwvόrX٨CTۭ^Z&#fʝdY醕yslQ* GUʅcu۷R=,uͥ'a(k_4_)葚`)ʂ3vO"p5;A= )C cg[u8D]?/IւsCoCm؊ȳV[]MCؚ0!)W%] HXqr !]XwA( Qs/iV-z)_iK!WGO Wc RǓhKCЛKAZ]`?p(] \!V_쭦I3|/2*e3w~8YsKZ17{wT]r\[V@ICH'w&O6_-cYo>MA;tJk?h΋"%n=ERR~z=?|X!jEpXmY>*Z_a" ͓jp,`O&L[(H̎r;/Dt̆ż߿mG+`UZ$Lҩof \6fac1n?Vڝ' X )aNK1P^_F?݋`Xo>0,|,(M8*\7?߽Ѕg(U* Ýne(kp;v'o|aۼ (_3o~˯ ɮx\+QNGБcoM˄$U]=ݺi3ٓbat\U_IץO䂮rSnwFmj [_4'tzE_p6w2J?ħ]C9JLƄB;>A!7ۑoܾ״쓢Wd@ uE>u,P^E>Wcxyǽǀoۛsɢ߄ʔ Z R,qJ울̷ 2N*|cnxCJ==Ӊm4{qŀOxZk෡cpr6ǩŧٶnEM9\+~L9+UTC^-5 S(0/3wq#V!Iztt$h+UUʭGϨ@?/.>~5ynjO~-UnM)~0ħJ2 o_>OAUPŹH8] 8'Vkd%!Xh)lZ^);8qR="Jٸ!t&C1#K S)s$489>X\ WbSRǢu`ףx+R3MYI1׸":ܯO>|_ëz}P։l1.:]٭ӝ\8x]r\n}{ߞ} v~Y~?~Ƿ۽g!p2mB&=6 cGEyף)kw`; A tK(Z"h7L[O%Eeǣ rMxw=GZ]ۅЭibw(@&V`'ФyX|ޡm=WMn"UeӼ^ÿOD {TStd`лQ:\5@[^Q?i Ƞ oPy۠SaY]6*a٘Yr~!85N:. D`r٘pZU1zq N0D2g| %GaXQYxު%[ȷ it-~zScVVK*ZO^o>}C%"KsuZW[SVЃgx!QSSCifž{/4(s?Tq<]BC[#V% P8gx"mdҽS˱Yڋ@Yܡ8*?8}Ŕ^w""ZV )3 5'%k @L 'ӴbܯCMm&BsHWX^FRStԊ kY8F FNFO@QF+2.H&L!0ñ'TiuF3 SUگ3F~jyaX́8kHe[m+ mO}'k=)։3NoEd˸mRx^$޿ocd{>7 xH{C/寓P?Z ۭ:X`~j%jг|kggMe JjjcF&1s\O39cs[5 S|Q=y+9)ؤ.kjӡەf4r&{mF</CMVFY׈r~ZݳOm\J9{G Ž(z6dYXF0j.wsm;Mnu [Gy7hvq,h~.{֪ l!s|ܣ3+/4h]0aZCuVOPjCï1f|Fa$dXCь悦S?qk5X i+{fCeI10WKMDTz%?tݲRr5Qjԟj Viuy9Rj'0j:46hh!@Gh֬:Z觞FEEt]NRuIRJ)9ڈ[[Qpg΍>~5ް@C'O[tIRu+piƍS)\wJH)_~vXe}XٶI€2P~aYuauv }|ǁ<ǃ-ݠA>wo6FiY d[r̳83ɧ% J=$vHޯ}`oq1)f1bx(o^T***^S!d4BJdƑVum+Iz5Id;ٖOۖG'=-)$D$HJ^'9uWl,3ZH`/jW}ISAC ZFEHc¬lp)fo(8JoUxjj$j"Վ]޸]x+{{Zo{^.s7Ό ?"0% {t5m@uzj902JfT5VzN:asacs,4Zd-2Rtô*,5. a%_&5 CeSƾ _4@*hD-[(EڜU= \wjG W_ &bgFsgo0l1%,Zy >`ddЗ 4WIA0E6qX;_5[@ 2a'¶Ţ!mu#IqITPJBѻwww-DYCiNQJ,Cr\#M?C=}]lnmқJ[_~~pO>{q^xMYlonr/vI~Qz[6S+pFu/^SSw{ح0W`d[0.1+D3B+ŖjCC{,SfkpRI>WWH$Y.n)cL3M#`$y&3C"n#,kv38%Y*Lڞ+pm$Z(mV+^!cIdb28-5hS;z eHQgbɊBS¼/y d|N 0-#a c PZ^g뙳cJyKEmӽq3*|5I\ 61_? "#C@ 7YjD߈̧y+uNl$(S4ZiF4 |`2UݡrOjR(euCC] e V{"~b0KQG='j]C O߱Kn%6Rϓ)=%}!kT)WY*tITviRc3ɨ8k-W!xuY_Tm:J/J]aHscc_$9\s[L2ăkVjG)PCJ}U5PKq~AJv>/5ۊei ]VΝHO0",OɈ/2 ՌF:XkGRd#B:ONޝB"~f& IeUz3,:!ErޜT ( <\J%*d4{"lTyf㩤w}`dz:b˾ơwx.-(xl琒 ҢY v[4I1, .Xv܀n0 )Om{eٟB=xW,N9#Lɩ23ߘ+ЛI MBFL~N}RFciUɫYDP2Ud)S\hK?\rqP{ $,; qb S-WpM<5CK~+*J!5)p y޺ B飲5[üwy6tmj5Fo.K&&x&s攼^hZoJ:3p'<+ =,棍Zv4- e뭟 X:<` $%5sT/lt{Hup&" MSHBɫ&{;_܋N1$uo xΏ0YQ'd=Z\(3HvaJ.89zBJ oJn֤.ܵcDFwV؊7;%g6F)95V Qr8ozgt0y'gQU>kY;M t|.tӝ鬺/";ea`C} 9790I.|B NZ\4]kmUn%TËdxyJ6[IM' ÜrU+d#2(Y2xHTEkWJq09Wq>,䃙 X3H䗔7n'P4+鰸a)6SV7P0I$?Q`jbӔT> $uR1:2PK!qC1aH'iI1 0b"蕝|uIاt"CbN3RuA ZYIk(8ywUґz D$7s$QBYtȠz:i 0"%/Dt Nh2Dʵ,[41GppQ&GAN\pr/R!T䤽F|a|vV;**BmWrGe%KJt#w_ /iNt7:& (!ݜ9%̑i#DFtZ95*O"*Gg|Wqx֢}ċg3) \3܍s{y|}'/BvnKȿlm4\ ^xhշkr4>"=+ț8 ̖@i(9\Ԉ웪/|J.GDt-WiLg>*~:(%+n|uZ)_C oi cYA𥠹?nC n` fWV.A]LWR襮JwhB.x>6ءJkDuRm[`j>͋PSLEҵ[ڝBXVҵ=λprWY64lj,9SN(bp0CzNR,I9HKB*Dar|:]şJq^6S7v 4~*?VIlZϘOY1*2Ӥ"j#[6z4Ά ;6r*}-Ұ9?3kQnk Opcv d& KG,CYJ&.C6)KyN@7Ǥ\Gs ~dq:RFyWYvb└pUq<9Lbq0(,>(hu8\dL<`+؞ӊXJճl[PΎk`7&`&WHC%|:TeԲm@D9^L~[%@e RaFvy챌#0  yDP9̳+j GeEaY=O.[`d*:6 >(@FR,_&Hnm8C+0t!.3hƣ*+:Fd8< D6jy[։*NwZAᥫ 6?0-D?悢sZj3ֶis "f?yE^rE\\&!w^ߵ$#cz`iQa\ < 'm4XVhDzR?\LO|RSܚv3<|.͎$(:xs# 'tORϿX@& (h3ٱn(KU39ߖ#hC1zyY9v(Ŝ/VӂUUfbA0w |7t& vQxu4 bA.@:I 2੧Z=&oQ#GP<^֗W_S|ԆmȎڜC.;_֭oQW5*ka&{\[J[?e` \̌锱˗61Ti+c.|"ʄإ=nsn94}:5Z/4)X`LMq"axuG=-)q971̻(V<)suS);G{u"`Hͪ0z%짭[Y: yrP1n4*m9.$(Wc%5:$ Bz~Aqs \j #X6s*_' #@S HFI10 Dƕh1żYIz~1G/Y"&\x=/rTO HmpBL \ FP3!@SМLlSӀ׵(+1Jڵ3ekVut)k Rw A[8# Yi@u?%ո;D|Iq3k4Q-u|="1ԵVY [^voC٧,^f VAJ T$h>U c8_{az1K*b~ׯ+ ~:E]u ﺂr Ixx`Ua I,"`')o.٘9:'ѣoWo_=߇{7vdxYc?G>oA}w2:xZEպos=]vk0Ut~j8ˊ΂?(m@^v!CYhty o~`L`Ӳ'-ǫqFX5m 2SXUYSVI 1yz%^xc<?F KW^&~а`+gӴF|xeE[hk{3KVt`d̹lvr^q{SӋno9Wkާ60[ qNF oks?(m_m8uZ@^_jzi;:1v9/~?ox`:uX q:0 dtGw"kEzEҋܵwd;cM0çrgt~x|y9. FHߺ2So^Ҫ`Ѡ#b~+VͿ(ުNJu<&8j&1z̍a1GVFeJP. u/Ȥl"R䀑md'#VʷmE7}EM{i(jQW&6,߈ ӗ( d\۵9R:U I*^vjtW'xAL4Fdy郧{uShDi1e`aa|Aȗ  QInBGz.OЋ̞Ϊyu)_+JhRrEv@'kIIM8?+\9axMO/hW Uk'z;w}뼥X O7TF>q 폸T^PnVWYLuV*XO njS^ ĘC-7J~׹x=ǕU*wV,VHEU+A cB(&p.+!`da1J*Eǐ°@mZ8نdKY4JJ6kra>AU_E\^. ƒ z[P%I>%s[fPgJnyʲ0KJzYMQDnfNizGe5lQ]a:[Fbt?yZߡ `~}"͈;EgEg-H>-Im94:6q2,%: آEh^;eܟ:Lt0ZF2).B.#i;<0r )vj|o=y=B!m:Maj Qjf[.?ۿeq6RaZL"rMwzNnxD}m@G~ fXl VYf.`xԦ՗/.c:&3sWjc&Ku[~ЁQ!׏tVҍ3Dkm9?ukCYm$0.3|KnJ#*M,Y[C2FԍDl<ή ϳ2HN@h6''v]]͘]E7=v#F'.YKC|4ΎeUko.WrItl4@(d"`gH4ʱE.DHAȠEiBD1RH澱:1}0* gi2!9CpJ~⨽c;F<0R>I)R'vZLZtQ8 qu.ȸh6M: .ew脫iGma@MAr,otO!2'g3wuTBP%T]gX<>2,fr{t &^PE's廇d#D{ۆvUUҞ#OMH(1;O:kCmTe@( ^e囆}漒=C4m.Yl6=2MR-d]Sˋ!J< s@y2Aq8>nwInws)2|6:@ZB޵(:Z9ɖ稨:K5Fa _tg2 Zw J4`GPqgy75?5dZDՋ&c2!Z E!!N,x 8qߋ^s?B@0yy\/~ *L<0ղK*GM 孨iZ#0x 3 " r5)J^S^ũJvޠ"F$˪ Ȱ''Pt@aH6DwX0 7?4uܐ%ʈm깈5X8'# V,wT@= 4j/"t]GWwt_A!8#y|ZT2WJLR jk[e1Gv[ۭZynUMԢPPy`7\ ۲_n[m ioN'$cW t-mT jKu} ~KM>;jBҥ4吠ni)=e$47VD- *~D b=]S^KbD'YHr*nu4D岏 Ͳ")=g}e@b h?9ӳb#f 'zB"~cd Bo3>^axMJ(kP?Pb@_mAK ǎdYSRG,f9F/wLaWQR*#>kۨw:/8}_̭~juJ b`by&5Kj;TM4OV>p-EۣZG/@B.92PD:]DR嶊 FWz䱐zgh%ZCt7nF S91P>&Eb6 wԚk?ӏu8d:nuw+O ]~`[dGp¬t oŴWO˽@nadB [VX2qbF]WU <1хuTGF8s'ptp9CKBTw4e}dRluܳg=P*45_L.WP 8Iod⃯\`5'nYy`J_8#.E0X|eO'oRXKv0ө$H_rwrSJ389hւgirqq*Vۜ2`]a2K7v|ClɈ]<$JS,miU$"J=\1`C $e."&<lpÔf*VQ~;vK b]+k@14$7ƃȻq6 -PmzbUP8hq!7 FYA*n(W W"x%jlYتۙ8ovbrZػӹ H!Iap4~$i? i< ocu[ T >>1p ]d T}},4+`CY2FEhz qvq ݺ7E ;hxmsp|[~gZ:Vi6pذh:t=.tCRYʊL~[i [.moq"y2Y$7<{Ϻ]yU$WˀcјCj[dڲu#rI7tVwv 5Q PF6)_)m$v(0+9+?j9ߖήF,>||Nߐ_]`"1 8tRqMN));u6^)6ôG3A{b5#HE\q:L29P~#aYlfe.؁V)a1(DgFv9ofpp ŘcI1):WeѺJs Z䄌Y ctz^6hXV6$R2Y^UMM9Bݚpҗ<Vf >-xO85QrܑlMgF2ZK״4eB/fq5+v.Xa4pVWAa0*y 'ܐ3q}nի@Dfh4 dʟ lh Z+-rl2_f8۔xt?KBz<-zMkg.R Ю1ZVVҸf5#gs `OȑlIM'N;Ӯ솀P͖O );5ʋF{-Z[R.z(g9xm5 k`$Bt;!}[Va夂sU, c@ĮM}'gi >\bسvH8l$3D݉~xV8tQh"k~O J3\LH! v%,+Ꭶ\'V n; X٢P-1`xn djzofĩ}C_$hE/Q%ZQU ^ 1Cw;Mhݥj]}l܍ާ=YL h72*B zeil/C(=r*툧?ENGPˉ˱(kbJg^3f$ԐNMZ!{XSB#;6q)BFZѤbcbݢǥ"\MUL/+V[YJ 63ZnX\\-& *H:V8zp28]w&N(JwB"p֚Y'pdaL+Z%^` &i{jUWKWà[agpn&?~QWCEĕp`k /CYǥ)ea„K䊧 4rS+?H-ְJ)%?_E/&.T [3P+ Z`1hn-OyGmutMGUkuoiYƱE*ܿVƛd<Hښ^9ͩ\_x S%1*#34h5qR.xPMpL-(U~C6׃iG4(oFHAچɺ2,szD}T?ߠJk.En}陛N6Iv5F'tzp&v HCE~pt"fxE7h.XaZH a#&,H*:XV jDDoz[(pP %MQ&9 [,գS.5=\=iVcZ#:?a0аQٷC欺\3t-轧& 7?4=|[a tft4xa.%z Fkq [km ZcRE[VLO=M%vN p܆#Z1Y5t)]8":JFax#!dh,H͆G(P0Rv@@Qd;\fh# őM1@ыM8"s|Gig5QT!+ c|JGtrҎG?º@j:T}>z fl,Bgo}"{QMS[ךXP:a088O:L2FM-᱄:|K=U&lJvf@߽=iGx ";ͭ{ԁ;fI,+|D&ĺ|T:QXk)DZH\3ʅQ0eB8_ۈ'kyk PB-HmP?GNdL9-hYrT^0.0(@e^HubD^uO1m 1mZ$Pr  K=Iв(>5m#h\dgϹ88jhJ:A\&EeZ6lC_d',bTqU@2zGlLa_d6MPWg}T*Mΰ0怄1#ӡ~drs=C^yAϦQ İtڦtNg9i^;xT斚sgPg B^1Bnu א?)$T 銩ݹ{M3a^wIԠZ k$WMF"}Yє5CG)))dwnFMF(cvvF,XPοg;`E?45 &ƃxf q,O9%;|bf|_KӢbZҟ`+ :f !KBYGTIIU"6F6_m}|$>>+XGz(Մx[EbQ1-auȳW hZ8Lp`D6ǣQu.i[SmXsrѸ`\܅JkZ-l--%FVeE;톜;\F]MƙH8ljz 3v'stq~ 'QA 8}K$}~r[: &  8Cq:0 |o{C$Ӑv[ %BrJi`>|e9N 0wGwXۿuZ*v:x(V{s'ö::}|@h'09O>twv/Gg{O[V0pXpYEn~h`8/W ~uvd~uy#`}e66ߢ:s®ֺjmr# 379uZG"0=~o2$"&@uJi7xM ?:B1Jac9)px94+d<$.0ܣP590O #j$GYgWp^O֛Bfh*ytvBl-%M GW_*7~2)!|~9f|O`]$EO$hfв{K uւ ׸c)TE[@EԮĺI82 ]z1dX- -)T 01: ݆vFcY*"P]0,wںa{ 3 Ɓq xt)]JamMTP"^V.E c`̆zn;77S=-5bVZ(2o8Rc2M?jty5Iŵ+lʁ^ PNdnį82!!ͨ2C?$]1R-UYQ5XpS9߰ \{n'WP}a$YG09#nLqlȬؕN\c Ho,j@Ú '鳣g⑎- T/rP<8N9 /Ս@,4ac2 ǓAKHİ^xDpuaRT!.-lÚr[bTU.ҕ>5FuH\ӕy.R5G77h{;h36GV1@CwÜ.,:bb_`ߩb_s5 YmEgqG$.d}c*baM's4frR1H7h8^ؑUwlzWVNsbgt @h1 )mfn<Y13WTZg4@0hx(%g#<#%Ac*硔j# xMAQʧ't~D't0\_LSt]gkIgDƮL+O0Ʊ;|Z}}t0y?3zPPx Ӝaί0W AN |q-$-Y3VN&Y|B _p1C ѝoN d>&`bXJ'x{pg{RCb=t1?( nJ1w$R/M}) Eui._2Ҩ4>iRt*W!;`PKETGZ$V0pdm/pep517/_vendor/pyparsing/diagram/__init__.pyQ-eݪ(K2mYtz֨׈<ӱ*a?EQVϺ( ꨺ OEi̚'Le<-OfQ5xK#\s ,٭hpQ+ -nBM& AnYtV4U#Ej7 E%LDumssImaa0#tiH_D4W]ZTW+EF^TRTA3"JidCkEiXD;@avVvC Hx",߿_ɋ\$$[Ugsn+$0M K9o ylꕝngJ%Iv@K$rAUO>eFϯJwpYoS9br']V`!GJPg$Ny$Z;,$scޞ'yѡqOXfuUw3*.yXwb@RR]iM9hjz]1mi*T W@KER9+ XA,ݷdݞ< B7(VMZH o-+J"k,5JE*NnI!qYYtEii`LEa +|S _04x0f%>hܢ6+YٞF9_ ѿDĀLFZȀ=S$6(ZR鑅Dz]g ϡ]f4'g`W4 h5cC{HI *4Z,"0gkjpp Y@ND>ST74r\'%Е/@b8g^ߧ}aJYNje7:] <1vDrakA %8eޗ;4 } Nt1O ¾9kX7sRu #H/gȎg^ զ}KpI@_\Fu]slOTa)'sP絮|T>qݸ.A#K:l][ zY. ДglO/*mт CЯU"2[HP9i Mh|A>'+Ds" s ߇M\h 튮D? ST"/C|`$;RyZCt&4pr(& 8~r xU<]dŜ^A4 Cۺ@4RޫD@,F5U #~UB{U4Q >05@Hz [mV'Z?!e1'5'YujYLaB'C4Tb_$_Qa=AEx5؊@w`9 FTeH jbCǕV5|"96M0n眇htqՇ SK3@?FPszpQقw(N4& $Dt;2[8 o]RBVCw?8PX{5ʉ`fӷin@`3 0~Ve5,hdc%;}g<٠/ p[x[ʙr@@\X.K"13AV?̯&) TVۂ4;AKc~: D(w~mO˴nKY7LN507m/,aVNP0\L8nɀVTbI~[| ljﮨuTp&Ybd?6А. 1!IX~EYI~P5Ɛ T(^{YrqB-b_È#˓bT>WG̖ȋF1#lg(j[ltIK46f:EAeIa@wVU/P$M}L-tU*=U9pGY@o{Dy 4||BNS:RMH4s5̫1ӂMɜb=:s,=y4y> /TJMHFQ.|UڏGin_$a?аӺ!ϺQ#u3j +]ކzdkVab'o%p]uhP6] Nٿ(0Dc%1xW`'趥 _31LS5)3JS(3PeSI<Ο0[_{/KfM-焌{H[+o#I| Y/?ƒ +l8؟/siN8`j7LRot`9YQgSZCc ,tCm[]봣pȴ.v#5j^a^ :ߴ'v:" g64mHlJjɓ{"2L yo1E:bֺ!YQ!Smú[cWkJ!1wE*1D;m24I($ssWt]pij;6+] *C/1 ځnE |w U,hi+ݛG(yR7qHtM<3¡ =>g74rCkj=xAj_c ^2+IHG9B'1D׺<a3Ãk_'bǛ=Og/Fc#~' B[ahwǺB3 Uezu[5 ߿7,ͱ,:GF?_U8%sXj.6 Mۦ)3tᅺ?Ƭ_7sr>~3{z9slK2tzW¸tR ecQSKuۆ[eAlCa"*m^*k|zW##&d(&MW`GC`w{zAD,;eLcDwioIݢ3WJ*mrm;p).fj"V^GE䐰8`#h& c7AQ3vvp/ڏlZmNS|yٌ0`E˴w>* 9Ҡβ}GLʪǶ]fV:a幻3;3_(|< 5j"L{y6/#wtq`"ַɝxt4`BK;2cD`0DfUN]F 3iM̕ P-WMW-CmoәLOo}ޚk~]&PKET*w M4pdm/pep517/_vendor/pyparsing/diagram/template.jinja2R=o +2&V֖xi:7C` őz '{wW2c!(3vj0C8݆`{-<^~BXqp\ig؇ne65~_VZk,vO\ZLq8QS C %cAvĘ̂!$Ik@(8ނ2k戧~Z 8I I2EGшj.P -;*'R&ā  e .dtB*%+S٠1–h-h_jr< $*M` oD0%O jL5"J%H^p0 `$zӝN$+FsوS1"8%FZ5Ycڕ&A咇G{yR.p'pnuO.HWVYSg[Nyܢ@ZAɑx_ rNPGS5b[*gV{=s;ْ"bbZ ~= e]FEA`*5+6ԪY6 jR( /2g8p%Ȍ*4@z+9 tf fMzTVp|V@E,:NY/i ZhQiv%ølA5XzB`pDqP_V)e;VnW)zQhu>Æ0"%.y+}e տS0_ <"Zqq`|GYNTEYw`a; IJTƠJAv$@8jKL+3LF)g3=oU19#dtXxWw?tJ+J@ ޿Tl2<:|PWPESG/>~rxQnLS{}H/6jgÙk/v߁dBe 1=V.n-M.= Nhp7;ѶmnM Zn6zZ9awCr]0KIQ&k8;:`{GI--} b8CH]ϣΑݢ˅jh#u-l@.:tnSZC[oeםyn?ty[,U7u_BOC`Q̂mۆy5IBհ{UEw6*&CB_Cq*yϞD];{~eo\bۼēueĂ|++nzG.Ӣɜ1 z EAIAO;FrDow!U J C H9HdME1 6*_- pomOP|j_t>XU:x1Mi ]uk jl -^ZZWl+L3m jDǔ AJvȏoZWZ E%{ZUSb<%OþCL# Z0O,}`Ыe0[ /lA!Vs` DK:y@( բ-N::B*2="˼ڱOv+7c7#;QQXb5#kK[ES\UG,@mZ=ÁSFclAt=)[7;EW BXn+l߂Ϟ,NŊg3:Foa1A;)$j?4`OȮ6yMZFtƯHa?17[( \F?Pk="@$X)2*T'$h@Hw8\#H qvW;=\z5#=%=xx]{{ѵ'M5@qPKET*L+'pdm/pep517/_vendor/pyparsing/helpers.py}s8wXfEڲ;Ww-̤.b)> 1EjIʶ&? )ٞ7{W-@ht7BEE⦜'}qBә\* i]*dԏUٲS}\$Qq,RMh!\^ˇ0kNnt:[-qd(BTT&<.4L;>aħ4QQ=,se*Q Px=N8N@,?FI!)[W=A\b~*X:PIYG+!a2f#!/9Or%,Q /3C&EQosXR A|Yz}@h%) Li.K%_}|gY<;  t>fXAsiG`=顪w!> GYid9 \nD6Ķ(١دhEjĿERyV/aR\Jr7 .^xivb=lzF I?lC'\ȿ.AI@6v`z8|!>4mkOLc&YS.[ 2L}fm KLKf)ILbbcnCG6wCBó'{UX%F/P墚_B2-hQ^CCDW#YhE7(e@w=0Nj?ː 3x MSdWdABixғ4BNY,=ɖhVXnXt9AJfwN.6b2r ^ !0Z^wH0l4W= NwELIT2y͂o$I[mIbxrk,HrD q`Nm(Ev%/}}"{MeQ8++>'YO HGqEI<8Jtk~A{":86h X q hLHsv3&rAPD;I$1#TVL%9M=t9*PW'RChIwuP}_repo&LRZd"ֹ >!=[(ư)YcV@+ÈC}$XM R @RGA" "Hf"2 T-UbT!kt7΄#OsQM:^gU]c} C&<ӖlY. (dP=E:j#t AX쓕/c[3xNQYMc!CE5K(ՠ8'h@Kqr8<>GV4즱I1uO2?]xDf^rU:"xj: ݃43݀SEa@GYQo'+;m'm09 ,]z G6,aM [xZųRZ"V=kkX,2Rzd3tY5\PtS@.b=Mqx Z!K"cFT"&tBv_T&8 WsL(QzY/=uJm}þ\ -zAo l: XV(;S2/X~"f^Ͻx4aqx]f'7?{̖,^TW m3ϖíc{ #q cĉJH_xciF\Ј}S80}-X|  -E`xgFj(3ep̽cσ߃cCC|VC D&=A 5Ԝe?IW֪«U > M%r 9g(0l:S"$KF %G5 Iv@֜-~޲PZwV?#G^.g?DYH@}6]%*= #" TxsL>fr747p|WAP7gb@4(ذ7c 6M=< F31;Uaw3y1:"DI2mv:F-]ѥaeh^A3]۠!AGܑ g/h-⤲o ar VmEF<гnj.ErM[zJCʛ0Ԁ4ˀ4DE7zB2wY*&rT|P*?P KIJ7&[z<̓Zϯ >`$@`J(fRZkֲ!Qx'j'8!?щz6>:o>yG_O#˔[+J:a3g hN=?rL,ST_ rF(>0c}S`%L``W$ 0qoϳc0DVywcqJ3r*K,b cV9.F쀼+CrPQ{Bh,3qGw*ylx,咞_/$#ᬐ`j"%>אޞ]DZ7iQ,td*#^|$4E5|UvbC{/%JT)NVB‡?Wm !r/.2!W\0X0(oF뮥Z ݺ+MYM~!ꩨф&ENmw`飽!VǶjTшfHzJ\"W>NTe jQٯ).{EMh^0YdbwG4Kj(՜)VkcC:`ĕj#\w2F@Ni:U ayi`+AͱTtUB=A`qtԒO>g|5P!)4KQKJ Tbq)gϏμ8{ f q*˂ßxRHL};l3rJ+lhIX DF_]^@U4fij5M62_`B.qqVqOםl:6+B9RLg/b e࠮AY ۰Bu p7l}OkxvvMcȞPE 8҇w/OH`ϊ` {Nj9ї#blu?P+ %@ HIKؐx$sיd[V{I =%v$ "ŮoΆ*iS}*Q939oX磥wd*;tbVTu5-3HNݞ^9yj*`gwT:@Tz&DKׂAOuz X& "dy/ej/jpv Q4;@,*QB3H:&rAi)T#lnTL9Ʋ'%h: Ļ *\W9SQ hiS_K`Tvd-]4KZLy>,H(̺RjR6\Ng V(6),bd0%lY 9IB)7e;ΙWYltCP&DYB`Pz Ǟiӿ𵗾5,7;8b_eXbMLB pgEq#Zj0T.b=1 0{ﺠ3c-(CÿSlv~}Ԗc'[|0EvN1I0N1O<k!AyTPC6H1X+GL.ggϿjΡ&AةC;nieS%*'kj^jRZ{-݊[1? 'AkMCgJhԅ_j굑$XhSOH)1ö_+4*NZaR婫>(ktռZ͜at?Vzx\-l˃f֯Iݙ&:ck րE CrBX .Ze?z;Ce囖Ȑxuxv??󛋳GW+l8E[vf{8~ibnH@sdkTYZYݚYZSns$[U{rxK'i=as',[lI5~LVl; -7q`'{X}'C +Ty~(~Oݣz2g܍!p^s>̓G ^ ޡX~otoRJww#[SY'#z@[ֻ4- J*>d*1Y{J@t[Pp\ZX_w l¿08 \ X_nʖ 3{IuwԠ`];c+=jc!A"nB=}" e T?Eц&)n%Uo\I-ء$E^s >{XN(8NgPlݔ]C/1|om߇{ё2Xp{[4m{UEAYNvKdS'ɃH9:I0xd$بԺMg *PSWRDwٲVII|+}*0 $0<5Rb>s!Dw{\QgT1CZZ1Tz9V:0-jז&Q]f3ID otH1B!6a0llAG _Ͱn9W^0wD|8or?wȌ:+C.<"]S8]>WO@639OIc W:L4@Zr9gγ:O3,B*<.l(ޞx!3:oY.+)8P^MԪfh5q˞Xs7Zs'Fl¸rBn(c+uݓ;w P/*L,1ञ2Sᰖr 8psU7=?Q=7ɈPy4GyQKU*}hC>Ňm1|dKZuoW߲j0 I`BHu)Z WW} },V],zJ{!u.iZq t⫶ruD ݂Z6̲ ij8ضgL ]B֛YaH HY:CeGVi~7CUǀ^|*􀡯$ln wMX{D=];E/.h 4T5L @-( "ɭxDMok&?_~wzYnWm z(MnXN{׋w(C!&\Txymiв;0 In2*tp-αQUP)״SEl{+e;2 2FiX;§D  ݫǣ15fXԆB$ݻhiDr>Oi7~Dfe&tӞ9T,^NkP[GY=65um; _rV`\25gg7(V[6Fk3 6O\bI^ 4WZ ֓Nb"=W[U-^[ܖM|lİ=+D-/ .GſỤz4eP7|%zpz|>TU!̢8Wz8M ZqD ^מ/buˬn[?^˒_jVeqq9)D)9vR@AQPM}o)o.;22tjXyRÜ{{?F} a/k, ~ީƐm+Ql]]y1WH;\|>M˺ SOxx7UO}_/hIÆ㇆\$smK$hoq"@U`X5'M+Ch&V:N^p,_i )0^-ɱwA6њ+^jSI5Ui3b79Ec\\'l>}d W~!)z:!ux(Eyt*PخoA\΍&g9WIDuъ 'FC*R4^TBJӼܬSo/b.m\%$ՉuX#JJi$wXy3i!E`g:cFQNNtF:߈LΑOV[lzjKfgoYtڒ9.aSwX(j2BZ/PKET|\ b'pdm/pep517/_vendor/pyparsing/results.py=]s8XD&byUׂW3(YQ(03w*=<<[l7_#~x }CUivUZm?5˼0KNOQW])d/V5yC#Dy^p:)4d ?|]|1+V|DE%ps+e9pP͈(8Ŝ*5l"A\ı6zz/d@>E."0eœ#Rg6B"B\ugr|J챏Wn=ΟnSՙ?Ѣ3l83 fuwQBM(CI"5$hY;\vw|_W@+z.kokH(qqb t[$qfT-Kҕ]) T?tC V hZ 6שӛ JWLu%CP=O gCuW7E%GM*=/+ t!HaƟcx3S់!nկJIA 7 P/Y U%jb9\ę+202, 6%[C3aO2vT"T9?ͱ.PϠT7YDRYYys#)5G;ͮ[l22 b),-D%pe3>4~IY50wZ) do<;}Sq!{=o"ygCRhjKYԻ{9[ரL(ث PiΓT붞a.M@I\y@uoZQS)/?eQ!,[() A>'$e^o0x㈶(R 45]t[I;ɭAfLe_m=7r1/6(iPQ - R!Zg(X)iaXM4gh95rk][7(SP33*D!wyj*gyW7@{]`}ISRgfg:-V16[-Kbx̂){0\/jcVpPPEš8O:LԵH!,܏@,i]2vnگuHSk2!16p p?܀@cV}zK .pv#~ya5 qмB7 T+KBWo#ȵZͥ )g'Y?f=|#tAȺte?]1/ǴeϨ*'NEhW|W1@;~n!]g#; ??~U)ihc~T[ҐSwtՑ8n i\r;:\p(@B92aZqyڣFTZe9c\|PKg'OEl"H9RD&4KyjGD:#E#)\\Z,7v9VfGTj S62' ]Q^EOaQ{=Y\#ee{8:^Z'"`e;vqr&uӯaUv5Q#6`M0t)ޞݰt Oh,p}&8@+-@iR H~瘋ǰ`aE7V߁nRC'8U)LDj\or)3gD<3C_6&Ё5kb=P% 6YTƼ- 6{YaX%mtۓ]t#R&A~Of6QW 4{{eI߰cO?/'ՀftB6<öt8d_M˯m q}1ʗ~# dV!=طآ3JVDزN*͇+]M|JA鐸~msӪo9]ZVmI|t)hnj-Ҹ#8T[%űUP2o!R/{(<Ž(c1f?{^SӇ zeg3 Iyʼ 9)?IdIqdƤq5M{o-dO@NRJi }A5>s%|`|H㩃@QrC>uSSVb}lHуY G^hi^vrq2/MCs[=MQ-nОaxTv[*T  ScDE>MI^srB흊c$D#?E1@Жrt>½J0c2§Y0AEIzFAǷ zpLua Coͱnd2;|hx$2ꖎ 66=weSvu(.;|߫@{w&%tyɓtTx ]rX@m8|3X:N9>1T-w\ b٩Rr#hli薃vB\ʰ}6# p_)o` ATi\T M0(ǭMȫ|T}s5_eu£Ȣ,ˮwȔ?m^P:$ώB΂co&lcv6sdrxcNm7Ҟ=ZA%gT@P< ]I!r!}J֣<'9;YMի=sSht/ݫr@zE@lkը\6"V~1΃ٖ p0p?u o~_Sc2^sItp^ ?u@sẠjK~O|Հ4b7z'ěrwQAGإBePPNŸ@E[g3H1PKET:\ L4'pdm/pep517/_vendor/pyparsing/testing.pyZܶY8)iSoi 1±WCǕ髢^@>c3$%"KOط3o(=!55ϷrrE^:k³IFse%j)+k^4,qQv,8!pVU)X^Gw1ճouE (j"+iIR!H/ Br6ɿ9͘(ïi4)98495DDPs+&XҴM@S(^ #uA E&v|"I@_$|ׄ-@ӆ^UT0C#T"$lCexZhG+׬  JIES" uM]/gJhq ^ѬLr?׻ ¥EiSeRPևyhׄmEVZQ>Z[EERWq~R/"mƓH.PWrdkZOBnڛ "Ұ Ocf 8ק6n _='8*)"l% #X)-r{dhr0@kfmዦL `z6ҙa Vf5_z5t&uE|Lmv-<{  ~I2~u'%;Mh`]v1cH$Y^T$쒱[WT_XҬ= eouca T^a dveHK](OY^^b#V/IXܒFgҲ8K^L "BhS-^nC`mw(}*ÿpT(*K\S([BǠd$a :[T=v Rɳc\C@FW^ǟq,0z IkIvk+L|)Xx r ?a(t_Nx \LB'V2_?OPU,/"tQ0[lp6ېy'` %OKFbtY[ԌV.zIwP|ۀgLг/cY_ >%jAAá8Hn {R |cu,y*#|Mf"ļ#W]vI]4=%˓Jt 2 JՍTFg?AK*aHBW$=t6$g(,-c+eC [7ۭA™ODr P3mh{mQO!5L00a6hV^KlMp56f˾Q_2S,(@[]C=:0t$۔7.oQ69Lpy*'X`-ˑaar`H 1,7rI i ]'Fj(RRT@wgݕ(T#Lc(^杇8J/3M@n~/E]&cuuuFfM"a3 "F4o)2"ٺHa# %e{&~d#{-Gj|l|6Rut3 "]r\g*i-m4O3u\:p4@+M`BՀtfgf|CP8j>k mcVfШט;`0|2QG{A- s({ݳOთgKI¥qK6|`6/4drDy.ۯY ~HaRh/xAw/u2)Ē9;I/_<pM>q˫iڡn*l١Oh\XN^7"*95d<9fFK4f.t:G#Te-HAxe>#dى\ܮ̧*2K4+C孜@Lyo.ѩ{00߽#U^Dh`ųŹD؈!G >[] 4"=~y<,-xnn%;bPJ~:[FcPKET( ('pdm/pep517/_vendor/pyparsing/unicode.pyZ[x-'hRk2xw;vgfMU  Mi 4-M[$@KRhC~_/|[UU?5gc;vMMy'A*a^Kn;jz (A *5e&{ԟ-0T_I5VY6w$ֺ|zm^' ^fz¼s\1ЈȭNf}`lq;B$Z=#6OoP`C'"/^LDIJ)+"ȪTav7/ٻ+/k_oVۆ-.{~=v|{Wݺw'')*=MH_g,1}IAIE-MO@H/?5zCߥW{*}^_ ޤ:Mл#1~hT+N o;(ң[L88 3 :bAjT>I.(jTTk$`֌%3Tm[]l JKə9;=Z[\y67} Iحe~:.uDu886182X8JFm16IUTƽО m`33SnmtvngMة|w#ə bIVBo@0Q *J$Ĩ4ژM!)U+ jX1G1 1G1MŨIJUt !՚UX:`UʖPٲ-#ɂ:Ye_6UԀ4@/aT]lԆEjDTo辦l"=QUT,hjU3!"J]A)@gWP4lē⠨jL6NX^ϵgm6v7ŒleKUCQ@jTkX&ץX7*"ZbjIP눬Tykbc@f``MNY#juA5+* .[TUd"KMAUH2jjFL*RplPP8$EϨHT"ҋ`$5peq:&v<².7 ڄ Ffnğbky֢Ya^+'wF/L?yF O.'Z(}$GQb xZ.,J0>!G.8i,9 (IY腂HS o+<1ćI%7X)듏4j~#"6"oC* CX!*XW&?}̝soHBِaML)rWnM]ۦ^D*VkX\ EIE)U$51sE˟,FPPg-&nN v#H;|8أqvhJdS~1|>Zở~x|~{cE4D?Ȯݿs߅^W  6m܅o|q{5T?E\o>zp}|}mT,VPzѥ]VJzѩOW>I@˖N~N$Zm;w?MT8Kۧ7/l_B yڭ͵+kl]\ycERPKET-Gl$pdm/pep517/_vendor/pyparsing/util.pyXmo6_sH#lz_z&˵@qmޗlf+*I%]r.:e3gC~$j:qNR{۾cKRt5)i`gt!vuhsS'LukD6 H6 .Dz0> HŊ$IuSd˽?uK /FdS4S[+w̰k[(0P"<)evÖ]Ygшk߽S=%@J2)fپAg܀VĂ ܮEFkq+"?9 xdH'EvbSãe雜ʲNlA[0Fe=XH7G'zQrej'>{DQ\W0kE76ig>R3څ 0V2RY'&\Z:=a;NNJO06!7{ejBP |#qFdDya!z |WLa zkH]RS|޷"3H]N.$E$QHpV\JbiccF|uSvX.`yv89hs'Vaz@%WNn-4TYծ1Kf8i@j*S6'ް77?s(p1Oy٩@HW'-ݦK'}_^\>bg<5H ݋yVл~:wMkQx 8QxoW*5#X$!q$cQSP eh#?+j:V1)We·`RBj_kj#~a\7 ej> =}hГ&>`Is99Yl;MЕ _;KRbXRIW-*:FlZkCxpݗu-N)+릯KBD~qܔ`@wK1:ʓWk۰ك[ $LW8F6Dsl@knWM5%  % F 9]¸ûk{W5 DeqѢ<[Jߨ~jQ'223L??j.9c߈BP..Yc'V@$ߘd߽|=z-BV/ekM*+m^>t;;gdmZ=RfWFȔv]mƭ3!$ѐe>)F]~4ٻ8)Ua|knlO`Voq5%6P2tǢ)4Xvۭ%"ڹQyKq>rT >.5\[ _,5]6hI켧rG7G.n]y".s: ^_5G,u\F g Z*E+Lu`LSnnpR +?l޽u=cbIo.҃!InVvU!|k'XH= d<0N18Os$mkuB@KARx L/O̦)Sf}Y=Bҙf`v꽵bCa-aD`v;h} 3=: \g5#,AEO%Ɉ||qx3 ,^~[fH gpf ֲ߫1Þ |#^uԺWu(oSER6kzҨPKETiiX#pdm/pep517/_vendor/tomli/_parser.py]}`y4 ieI0˳8aۄ Z$^1]Ypej'(μ,T0>={3_5ggOg֓翌.ϕK7ݹ,X0yf<鵺pC{|Apë=\ġ:2]XċnyqUjvtnzl #/M9=|-7cxwi>,#P6z.k3/ {'b& Z Wc~)Kc쓂? #HC4) :XzLX9lq{#lg f|Xt{CݦH /jb;ނ=$'!7P`!M!1 d+]q/J9Z<炒%*$yQ]k-P4㶡I/HO{_H C3z͈@3l&59`y&]};$NF6|Gx[}h|*%i/aq$ m$ ֭lL+ V[.`BSgLn/kDĉӼEF)z!Q bג!Y 3@T룖ܤpg+o&-D6"C"2|jgh! :AD@0="O7 S.OA`%d3f?01:)H8DB)· l5!:,}2A0ՄiaAdIy -}Kb`D]m K- Q= AΰnqMu ʦ nA7k`z8QJ@@H:;ճk`عPWÙyz~W~CC1t!*ɣ\t$R=P6'tMmazQb-JR;^;Ԣ(k#z (inelat"09B@J$zTLWjR68=3Yso 08RXk$LtOh!^W#]yy&QNi,"H-ua`rbc/ o@E{$Cj+Bkĵ [YmUFoi LIbN SuL3np_zOEV rE~<ٿ +jL)UcoRBC x b+ʹc"X||GRSaL #+jHRi[S_`62TfW Ƌ|.=J~؃ \ %U(90긂Bƀ°_@Zg [5o,ӰZ 0Qeb Ԡ֙{G5ɨ^Jz6R( qj&iIBH2ql&/yz6rBhL]?=OW1H&Id``Xz(SJd Ne!R](j}' DV%j]*!e 93sulGY )oIzs/㵴/T2whP,gw[ ayz |C^62enkZ<蚺nS.AQOgǬ[ɠtǁq08|j6&%h̀Z ?)tć@KCI7es)jMc&fW$e5V%,}T^Jvgȉpu7L")RY[^u%mh8xX"d2s]\ )BNDDž k `<%!^3n]jvn|u(6ri , 1PMƼoU9M66co ?83W`eU2ȇb#ʓ$8U!Zr ,eǙ:b0vE o{%$e%2)SڲgVUEzl(Y[QR$i+!]4{Qm 2 -٣@5V~EMJ+fyC< >GS0!<,@5;fP]?^0(-؜E0;zH=sq2'ďˢw,uL.6$gK`G 9Hc7+K߉8L>Ȃ='5Fz)Csږ襢^!!Eg/xz6>*n~mrS(8SMzݽخfklR-P e7آrbϦ+oy^+,[+q !{ ĎX̰AU)֠vXԮKIimwg3/ (e F&;cY,}ةrу"pΑLJAf L:@d'zڪXB=;J*G&ywQcS=bpk1'6ڿ2^>G~z'AslΊmU0z;`3oΥ%Kj4V,~S͢ZvYD&GS#筊lT6rk9FnI^ڪS HdcJj4s3j|H 9'ApsکB3t L6UO+HRm)z$^/EQW@0 JitnGziׇZ\-;hI)&=EYشItob vbsB-< 0v-$j͢V.~n@^<(漢xmz]؅dcjIDsZJ5vvҤR>;bѹ`XvЙ':eڔWgaENl #= Sђx2M4#%!֚3Ib5}83l.GYmX#;np&Q)mFfpZTl -biUը}2l-k.Θ\r*ZM4e ɪ* Ul@.izqv]%h*2V+@u.YəK-#թgcz۾ҿƽ[j0V eDDP5>duXK-?} /&|ڵ`9'?&He_\I*{X;VjP yУ\=}Erp&4`DI|*>x&Bt mGL8 ۡcS!ݫ\7%zT(UA?5.7`xas/%8=f.]UϸOϪMv T7gm*:6JY87}K r,M>`u{C_f L$RP+k:d:Q]^=1{^FCx#ݹWd}@@P#{x_Oi3t6p b E܆gJPܱ$>}Evˊˎ٦~n\KBbp$ v7bo-yVe,7  E{ޖPS3| Qu L)&C"֕(c3*J :^ 2= ޿pyUit1oKqRqUm&n׍xbߊCW)Q΋#_~G,_ Lj%ZCHW>HĝVF›fv,B?'ٱ)7p`Y'[sWJSЖo'ה-ǻw?,wym[o㫲J^Ԛ:D+'^ &a// 1t6,Qg)O =0ʉ;Pyk7l^ɦC"(wlmm DξH` CZ-%C/[" QQRV}%'=LH9h4 ND@JKp5O왮wᢖ4\><a9޺r4zn0]Mwz6]7 c\@1߬KVk:9o/PKETu1~ pdm/pep517/_vendor/tomli/_re.pyVmo6_A(_T$gka i-yHU *b;نaǻuo?~svR\<1 8Ǘy/fK hoyKPWRDubbtAJFǚΗ5\FFEQV(BzC(CqYIcYZFo 厔\ˌH7Y]&~ ZGIKMR[ nocZ˂̀҃.dw&~=ͮn4ݡ15w;Y!ܟ&%ρc{bTx^B`2io>qs2vȢIgoZ'yJ3*zˎC#DfޘqiAyjjSqV#{n&k7e4NRSs<]^uӱOIk$+e5 N>|:p\8փx>I3Σ۵|>hHՉL@(C;7Ӧ(3b܍Ec-hP%YVajHF3Yt[BrHnFv.Ra|+NӋé/~y=5.j< VR0PB(1y*-Ax=չ1*FFѹxH=˚ᾬP)vuQGb6"I[T˲oW l(hTm'[Ђ.4hzAС@y>A[M4R ]Ҝ: 3B>uM>JN$ʲ++fu零Cm.*|6 ;γkijFlVkB^&ozA7U^]zAht˕f XAĀ<7N ^s2epo 1< \'PKET+)!pdm/pep517/_vendor/tomli/py.typedSVM,N-RHIUH/Rp P053PKET]Xuw0"pdm/pep517/_vendor/tomli_w/LICENSE]Rn0+9%9FKtDT(:D[leN0 ps!a,v<}}`!t[7Л_85c0]phf:f|<.}hh#4"#wpi&4޻6kC`>Bҙf`v꽵bCa-aD`v;h} 3=: \g5#,AEO%Ɉ||qx3 ,^~[fH ɻxv" >NX7PKET)P%pdm/pep517/_vendor/tomli_w/_writer.pyXmo8_Ah?Dj#zuM" ,yI:RIIn/y3Cf\8ζj+XޔBZ i2YW> ߳ JtŃ!ObYMߣf5"YQi^3_TT 5]%(w3hH`kζynruOfdD+$SAIV / ~ Cٛwu}=}?ߎ&66s@ ,־P~Nߧ{T&9S I^W+ǀ3g?M$ȹTn޻y7Oއ뫛i|=y? v\\xO,M#ן/ޏQnhZTcCU,c,mM Qf ,kCP CP!8}`D0H¥ڳ?ӴlKRJ&ϩ\iz!Iyn {6WMMܗe%Br+) 6zIY(sDPb&m9XzIZ"R~8lF5_W,V$eʂ6Fi`;M2#?R"[F J6Gy5nU&N¬)R&X D JM]g@6CS~cY5GʅS F/+'wzeX;/f"zT)!ybZ*72ɷ|L?4.q`]CQU=֛.7\jbtW2l<| Ҏ&aY#@ZVp  "Wk! D Bk(KԒe&t=(^޸F\k4U4XÉ$=K(-A%T@ШSK9vdL&{,TŐ( <@+fhl)j2V1h'`y"+ݜr7?ᚤ%Fџ۽ {"mkqSF?\U[4d˾Cm2?߉@ptDAa[CmnoQձb3CU<-(TRǶF"`>5xW`$f/~z@԰x40gmJ ͔w׿8J@9,s6\Aȟuj F:/N{{Z{pǮW5eNё`Uęw.cK9 uGE/>xtŞR"r/[ /|zIiG`]uo%T2:AqtH4lE`J\]@JSP ި gz`b"Qk؝K.u BxF480'VM<~j8\]A%d#%ei:l*y´ʟXVqt齕g79NY CˈojYoPKET+)#pdm/pep517/_vendor/tomli_w/py.typedSVM,N-RHIUH/Rp P053PKETVBaypdm/pep517/_vendor/vendor.txt%Q KT?d tqi}>fa7ZkvE>Gca.kk=)Xr3) )H{QxPKET1zw pdm/pep517/api.pyVێ6}W R ЪA 8H10ZH$KR3.xm],r8gΙ a^^xUřmxUXa%ɪԲ[൒š^ *.hxύMSn'r)X:U7ł[p8vQ>7ys/,;j^ (dԙ'cQ˥(CfZbanG={cuo6_ מz?NA }AXj% P^^9u$?VA0 s^r, ZBP 6@xsp:LbEDQVʀɦrXC-ɿsݗhґZK':M2ZJ}\رvva-Ѳ;Fdu ^/~Nop̭6OW/ËWI`kKbRf)-%C4*1sR̲hs J޺ҳg\ȸE19q&XAZ#>uqd^@gyU_G!!<6L T*Q@K'"llyu*U2f{-f23߭V6c'ṡnOtM8i 71v6Q*J.l)Q=H+ qEm-V#+zxYq@W}̛w™9[} EbS{@J@U,G:uOX9;07"^wGfrV^{fBNhxrN ʈd& x"%a9s'G^}+??0H2/8eD|vPKET#=pdm/pep517/base.py;ks6+p=dr9cʵ$sUqi HT$fG$B_M5k5xzJG=7˸Pq%S5XqNG&Q ldQe\e o!<Ӏ͚"϶1$܄/ ϖyh&Mz@J:^u<-K7!|/l$m%UTluEE?\U{YWg醴f&+*4Ϟ%eB"Cǻ73A|GL`3Šos-+ v||?3XSw; E=R!8 1o75LW@Rɂg j,qkU&NR=.54͟?j<)oB6lR$4}*<f?DUc¶ȳH~ $$:"%s1xԔ\{ Wl̓uMf&Q* vnsm(^J|S`5N`Vl-C14WvE.ڛ{Vo^ϰq!kJVu\E)Yh$cp)N{}S'm.)7-+2={jȊ"x:8"Жب36`gNr!TCCDXBv%|b"Lw;Tʩᨬ-_/ISLDu9P)߁6ܮԴX #$eKڅJaބoKU%AUII8 [{d""o.X/ \\ &#R13\?HcDރ-gp ,yc9{ ه^C3rYعg =hߑq TG?^.ΐ6E`V-`.}>0^&"hZ/i\ULXms\!T#c4@& |2J۟ >lc׳hR/a0,H*h|ה6R&\ÀZ%O$@{u|\B qlQ<40ɹ6bPEScM͇=.L05)L[v.OR3u8HN/P-wk!BLϕuA(ra{ Y ۜ٨ D6n+RT}t]oRT̃dP ߂XH q5=I8F ^vp:wlXŌ~fU $@|DU7\'k.~Lezxg޸g!u6"C]24Bc6g!b=TnZ6-_+3dX~q:<8k^6/aCFrȒ }xZJ\vbզ_.H$2*۫ǐUfTBEY/+bRt X H7HIʹi̡ TP>>o,6: c۔هxZ)RHz,F4I[5& =BvUTH&г=0`"p*h rW&t;]U,!2xSgbk0M]wtW%ִP?.XTIihZ^oWUo\DTw'wHs x/|bG'̿KɥR%&ͮn\"EȌNP51g=4E .lk/G!a{6G5E|*: $:1uc +/|" 8(2&7, 0gY3hE&<]ZD6]:1aPa;7/ T\H;?sc,[<94)QWv׾}?E`C J,7um ,Xh,~*@,4l풊t[B~|Eq stvkL'^!ںs@a H>+c,{ |%eEKj7c{扬LR,l'^4ø1ZN"WϜ]_~L"{ZP.g5JU` qj'N |DĞoL~g֥a*ٸ\Ζ7@3hֈiiC-nGer} _\ faesqWa]R:]'9 ҶpwMEtox@G#L䲿(ys!;%&D{ݘWm4{W&kp>ԌN(w QG:07׏6?gj.^n""[oOOџPx]E 0$j/R[NyKb,FqdK"™Ÿ&f f"ecT s\<;;)Uaxj'6..8 Yۂ1ER:I]̷y"(H1jNuHUi0R|n4lt/V5l}[&>oI"I2He4*MRRrs2mmivAA/ nraf^^UJ5a^"ͧӦ$oH s!:h-ԄĞW3~:4̈&ͳ,O@# ȋGdƯp i0G2)]_&{5B/;g/WRRr3㴽>k|*5a wqwZ~~YnK(?LVN`y4M"dvВ+Yj' 8'δL?r{ɏ%Y!=ݩImI[D0H>L<䛺bȳɻ`=ڀo]s"m" 8㙼\,YeXK4G[1|T='։ܝa7CC˚B-cCA1 $ ZMH[x>wTyU/Ǭ *FiLօ7sfMR覜ss{ 25m_lVkղMFSG)|;F-0~dGfEI9hgu+, Ų]eʬ2wvɸ }1S sf4mt  Wt|`6+ln+!e])(ߜGϤQ'SDnvRg atNlZAIfC_nRe+I}4~7節3(PFQ}1 C,H>symBWҽ R1CUoħ$9J_:GY[[=B'޴ L5.g s\As?'B( l9zpN*Ps^<{Օc5_,>̃Wh737cߔ l͝smLYA $zW<.Ti#`^lt76+&(p\:wUYRel A3O\[:ۑX{Td Gnny8wz1!W,|20u'vrܜ 1s`TI@߂(+B/Ysw7~Ŝ*GUUxAQ%(ٶ(yObF_ge-N>ewr#.7E0[8|6C2hsМ}إ֣Np44e(Cjl;p<)TKFozaD7n{Hon ~IOOOLt2v661֥K@3e^ȖfazȒ~(i\GXA>Ci %˒j v(".1Ί—cO`zãoo_2Ue\7Pݝ,_ٜAf|y i,'`"z+@oF.axo3rE^7LK> D{ǽɐī\3`sԭ̑WIzͳWKdFՠf9`6\P}p2C+1}$e goFѷnӻ *oVyo_5[L-DVT*nDk !n2[z(- u&S,Y C>\=_rdxZnl# âf_uKl[pPKETpdm/pep517/exceptions.pyJ1y =x(XJԃ2MLPJll9M' x큺އPqBf@]C2HTR m_~v`ԭ?ݢ!Gi*Fkj0U1Ԁ#q%\ówXrcuȬw`ǜ:)8i1S<*byTa/7Iy<$y׬%$@zjY.vi7PKET,wCpdm/pep517/license.pySQk0~6$ʠ=ee}FVĖIɖ-)ia~OdisRR,IЉu\aEecGh ?,#G뷯h+Ӊc]=/ѝyQ2:ltO~e;<=;jt8IE&gauوaN_oX}wkp kk.эV.oU9T%0%ĥx6E> YF>7,3S&UI1"}nfnV:q|Z%s~l)8'g{s]lViNfPKETek:pdm/pep517/macosx_platform.pykoFs+@ :8ٕqnڞ+VJbHʖ%KR>PNs%vvXջ^ co6QfhqL0oП<]{"DOs\̏fqr$.TDyfq`]OI.<6K㰷$;ٙb9qqs"ؙdP8/wtVB> R@8ɺgz~iΦ:Y+.=d/\D?O|~9`o,wǃ^&#O,].S%[T|!X${g9cY.A6x3bhWgkblt2< z|F =<*bL\Fߟ OG::Q [Qg%xLaޞG/߹)Xo/@}??.~N{=ԕ+5|x|l+sj 8SwG@O%/gIM}#^ֱE<4 }}LGS'T OH/<f2}Cxui YiVI/ f~$Xd xFF߀$U=(29Qe J<>BDi^e ].`zM^ 4]1-&-Td"^l$C (?yxތTA tva#=4\}iXr3CjFi,8ڱmd6H0pK*cCʱ`' 7P(C_Cg$żItzG` UTh{ ?.eb τ!(oݚ )QKBr]੢zV`}1]pml Tn-l̷@l!U&#?Z20ߡH ghpM|048?^KH˄vjb FZ=4Xz@H 7m 7C{Dᘐ {‡ .TU%%8]3| tZyB5s28R#<Ŏ'G@ٌV+?\gJ ŽA;NUni :uTټy%Y-1Ael 5-DarnJDMB@x%T|@p+ O;fV|[OmEz[u,N S;1^y GrC}v1eQJivH r~vE4q  4[YϟIjL~Յ|$qBUQ93`\r58x@mEهh+>Ke_>%N MK)Mr?P5jNb=C >`ˁ 6:5(wG7@t?^PvǓQHԈY}?賿l? +a^U`#I{.+Me. \&)tO\LQGeM&ħ2 #ѣK]͍r++(/RsR;H"Qzz\vr^XB $ TsX;Yf"Iv SQw{MKѣOP#$4ssRO~ o;ӯquT:sq` <ud )k?JC'|y>ŒxnIXcQ?m;>5ڛ@_I``Hnp-om Bj4Υ4 9WRzRv[!h6Mu~yBkcW]bڗ7E,v;4$c*)Sa` N>ή7kP?m5`Z0;MМ}`]s&ul: Y9pРGbg<Cwv?Ec i\TˇcJ6 F 2 n?>Sk ?Y9o*?ˑ145Q;⑧< RS٘Joeg(= 7 ;Ctݮ۲2miwjIAR0:[УU 27Hav6Stl!<4ǞHxMhSl"nlouvD%#(6p<+#rⴅ<36BDDG  m_o1I fʱ6.,E/)(vʗCTs KvwOZҽ]gx*xAXE{{1t[ ~*?ZO:<,ECHrQi D^=ܢmPy'xK܀k&hkPb&ve@Dš`aLB6 mkVi+d>!PRy>MbbTa%Ke<ơhx[O<# CMT@}q VWT,_tI Y7UL+|3[mUF%,s]]-^)B0$ 86Ui:ߛ6.=՟V/]š^Q}WFv498=GW\;4.a:UjV//TN6+E5v`VCJdFjfC.0~FA?]Dհ'mCWˤ/;VAڊd] jfk8+;$uȍ`>Wvg&5`Ĝ` x.9eho1 zyz<`:u23'"9V#ZDŽ E1c'+FY|)66* FZ]3TGF&i$K/SeBl K*9fTHkkBXrϛ.pMaR$N]beq4`=6O3]e =4F|1b 8SGPӃ .8pؼ[T? wl|!ˡ˫2SbjaܺY:iӀr u_7/ްB }/INf#~GQ?ȯ/Qoޘ,Fѐdw wG uLrz>7zb2^*XeXzPKETU?v*Bpdm/pep517/metadata.pykoFo @bڴ׻ F(Tŕ͚"y$e[g샻RX{gɺڻID|ϫU"oړEݨo혾Ei]\z,k5x,ŏ75/I%E?.:ɳ(fۂ7߾Jc0ё9^/_gq^EnkPoyVWͳă~(,r]|}TfȱwU?eM#LiY,/Q(yUΜM-e, 6%/? aY*P,Wa2׼:*ԓ*Z0Rm>ȕГ:WIȨ=prKş|@<}^"-`N߉GG3Tg3DDN(<*b/#$AS@2.ro.2Vɍ!uF6ch9X؀wF_|,C < ?F7ӐLJΰ0w sƫ(8Lۖ׊k^$I+xYo5a"'7}kk.[I֛w@C$-xc83h}x,Du Ux?m(fMxoFHfZT4  [H" HlTv ãDx"@\V#m %أmuڋjwdƗF(6q`rHղLMN&[$ @+a,y9ADCD8 nBTX?c 4 ϳeSRa;[١քLX#~cÄ@ظoD,&7i^UeBdodc wA(VaU'b*Wdy. ,u[TBQYq{ta*Tu*U}i\$+4? `U=$? 1kAklAOb\6+=nJ:12;)a0؉I`d6]XG+6x842Lv}Kݘ®i' ?ͳ|D&r\nQWɖSP;t%6)s#Vpᒯ$'Sb|C_>¨ڱo6fOybN{ºD%Q'&. /;PTk'yx"dYP?{F=U ?P#vJ492& e,-D`<*P e산SI8ڽ[; /TZJw'bNhFl5v lB/=+Uc=RQ)*LW% '9(DiuA0~P:F[3.<=}W`lêx3`x+өF<ܨѨE|,Na5VE%3*J0Utwu~} 1Ndxt10$LpɪuKXʫ w|q5a JUrnԚt(84۟Xܕ(3HlWU ]&ȳsO JAzv?Fk쩚wd [1LX)kVORǯ\iD/{#qKԋsW&Znz(6"O[XN*HGxJ2ROl9Z: _$w@~h RaY)NMB=徑vp}mUvuV4z pKL1"-@=]Z@lCq Q7 <1NTl6FY,v.|bxL7gg>k~q9Aٿ.x öI8VZw5[ 4Iq"tHDH o&So )bdECqv@;& nNj/HUkC{Fe}ua8r.pBx͔qIDeʣ=y .CnR}92@ByY/:)[X;f1hZ]WDdH3NFL]%Z%D XK MAWRҺ,:p0C%xZwBWl p@9??{~:+7Cm HA dԸñw[:/XY voo!PKETpdm/pep517/py.typedPKETvǻ8 'pdm/pep517/scm.pyZ{s6_ei^VsIfӸ8*"!5_@9>Az%z6.}yڂQ%dM,X],I Ff,KA3EéhQyZFeV'ux45d TY-楜1jF|UOC+{j Sʻew jR,T%%yVo}Yu?"/TAY2/\%%ͮg~n#)FDOZ@ql2jh|"״j5Iz,aȨRwIIzAMu>M8% WX ^^tu|}*od JҺl??| gap^Sfav1;#<=xvqt~ `{ciRj&JE7\0Pf#¥zsmhSU;G}?9-O=9QGp9Ɵ YwY Q.h#H(l 'd^\vq_]FDNo߾={R4Fm?x7!9<{ pٛw8e'ű{<iu`K]N&I,WbѼc~BP#sZtr~΀jJ6%R"$'jtH,'XOIr: wf%K gto,Svsɶa9RIGZ.aCjgu- T|5qIɝCM#1=,CU0֯/^_]c`M9T -H Wa[AZJoi.ziU;kpo+#_6|3y1;İ< ?|/U7ݖ- -= Q9 eM b<@:\0X;$E]rBeD 8 $< 8ɋ!S^Gco_߇=`c(普ԊԳ_NYKIޘ)[R^dR ١-甩,-bz!Xc+I4rWh_(o qAt٦SE@e0bYU:ؕp'2 VnH;@ E8$39-Y62qO?ޔ:X_}w5 ׂ9qZoOSҫGp}IK&u:bJPm: (QZFSr"/V e\ 25p?A,d#bŊv1*P%%Ij\vB=ѲI=bCe cc;MS[4}?_GXX3;p*Z쭺|fu~wyQ% m@5!uq8w:Ip{ I+=Ii?! ZNTNo>ف,e)Scllw_s͢/5HSnxݭrȟ<߻zwJwQ{ ,CI !_wi604GjHOUm 9u^i&^g}W f}zу"E4t2'iBf|ЕokY4MFE]-HfgEGެ״ҶT`-G0o+uk<|e v/܊t@h'Jjʦ;N'leFh/.k-^߂!G\o!{ L[<zl@u"Y1s0!eB-G@0+!hx\;B *j1d5.(!| g](h!Y,3 57Zfoײ@[@N3D[Iëڢ`!)op֖[-e`,YҬbfxZ>CFۦ"mm:S [W'ѳUezgARG@UIT}*jՍj 6jL0"g;J*oA*iԉ$T@&*0rT4_T:ΚS=&|5Cn؜Z D4b 鄋_adIF5щdΗmJ53mVK<}!&6_R~ ID ӯ T :fBr;X(4/AsJBDXkh"0Nm x/'8>vjfl0]ˤm ˠ;@"I,`>U<ȋ^W. P_ Lff,gj mk~~6 v Ic?qQ}݄By[6P]:Q.';UYMDjy#t >B#SK3[nʸc#n$ehh'P&%k$+)]/5X(j+!-͜^AF4o@0jd1BwoqV{ŸdyB|/p: btw {i" XN9 $%nF+p8KDrs8хˡoi\V䍇.gӯ & CR8uhY/ݭp[xg]U8Ig?ܿLB7'9WSuD#2~n>[{'|G̰J tFrơn[v;z +3u¨ٗ ') aoG X@Sit+yU4!V"6hoq,r,r[Aew͝w[y)c+y1N Tgyx8{/LI${v f!SDX/J9)ƿw+eŷY %&5 ;OWVU}1̱j f.8i]5&֫o㫋_޿~Wt6u*`bWhT~OdoU<7qX &*Gp@7P,n86N (oLm` m!PKET$CRpdm/pep517/sdist.pyWm6~bp!ܑ) [r$GrBFk˻ږۼwFe{v -ˣgftn ՙoVhTe,ץDuKjU{C=YW!tgD[Ԭ,ۉPzd?1dU4yVk^"#xY+t-1Y  E{ ؍QVwRT8hr ??{gڭjk{`5Ϸ4Ai@܋|_FYwH:Sc@q}@`3a`/ YBBWs5l8483=BT}&QC<]ðEU,n?{a *wĀ\y] U57kQ8K2_B?S')GP%m]^ñ$>$I &yɐ)Uό^C;ophmΨWdjay-Z}#E@(wcT8lx-7x @1cEς#hGRaYToxVbC`OV<~P^(JLb^qc}!!$$n#=lTK6׀pY ftpO,A@MPZglDU6S<5pdcǑz +C6qا[_!`NhL4'T2wg{VH)<~s6 j:.=!a-&ѽd1bDpܘȤ2]nH"vBCՓQ F~.O #6^F_:ÏdοxkZaQ256VhS De_Krr;~Vq"Alf}ƭxQ P߈BZT$aS9cj?p?͛ϬE{L2UuZ,$) q%?s^-f7k|6& 3w1gͭ; 5RrY$^?'N ^nhGJܒ'' sI$.".TP 8 d.,6ު>Տӑ#Mo>ȴd5B#GdjkQ̦W=̾F KFD+IRx11`HI%9 ުP9}U1sŌbNN)_PKET" Epdm/pep517/utils.pyYs۸|٣8qnsrēMr p&@Zyw/>:LLX,"njɪE+eg~M1zZHWÆ*ŕ ̵춭y^1eVm$nXP|C9 Dm`/V]V,%`R䬅7cr)h/_ gnU0QJ5W-Z=DT]3fuqj&ڰSn`[P/>775%eQM oJ:8VEX>!٬d+,?GD*!hFEѩ XK T-y"Z;%U%)9~^v& glj M)jTt5SxbM-Z49U ,XI6]#wاbdg^"T픀Ge]d]lcvChPd3q56ǫf'GLZ\W5OF5¼b&!bkqr2$^[ |+p!HpZLclF)'Nd 5kul۵=,^uUH q ] )2K‚hjןiEKCc<.S2M=pA( ZYqa塍܇^Bo쇿K.¾1$p"- q,%)Xp{a»:j$©o7뜡O -Lv9!:@z/sRx>O@o__ZwU HWO0H8gd!L{G_l== j3^ }X[a.|~p9mzL ?yq]݆뗓6 [Ub pdJ 8 @+c*L lҬ׾j!|gd0kW<ǓJ=GrADgܔ2`51 AR` Qj_"*×$&lqSWAR8#!hMLR!6Gm]~b?鵽3aa w=&̍k׸vp[ zDy(>uz6;&Z52\\/2IF5 C.9BKٌ:?+LCaN1mwbTC}b >/dɆ1&A9\xg2:ol9HZJUJ%,zM#D9݆@o$~V12(˿?嗏 WaDc1xPg߼?q|.}v{gv|hAkFbWz]yWukQNm$=*rxHJ$:ei=e4p,0;k|^/ 1o ,@<*#AoGYhMF#WUȀ tFWne[+#SDbk6*!,pjelVHa{T;Wf{o*T?RJu]p*&Ry-7Tqʹep #.o\xq\\WPWGoX5ޣe,Wh.o$4oY *y%~PKET$ս pdm/pep517/validator.pyVmk0_!86P:/_}NIa,[v] fHH=N\ؕbbFBImɘgdJCt "Os / (ˤ0z~pH9Kik-uL/x:y)|~ (#,ռ.Ćv$sXP\in>cĿbPïiHq^P j-G\__zsU.^=zo](/o6PQ;7לW6Nmq@OxM|ؕ ԽG-P$b߮XAPU<>oUQp `Yٮ`\Yz w)@f8ZR-0Zۄ% Lq2m"xQHhJMGjGK9gl'mݴG]9όg,v|X=1-ܘIZfx^?[_}sQ I䤷v7/ϒEfU̲JM7\4.ZdR6%ۉ '?BH s9 t:qكZ _1t 5*GHK" '-,/"ׯ9baspVBHa"1>j nnT`.w<{YI~ΆDw~"9qUت]rۥGV'GAQW2r3=5=C 0i62VWoXXVG̾iLu`3/Dw);j`!Yj]G~2bk'7ʺ,`I+}k& {5QzCZi⾚?gRQ4W6`J?(!33Fio=3e|Dm I-6-y5+#结:7^Gh{fMzM9ﰛ*Z>cJ+e0pp03 -??m]γ+ w;\Y4'}V\& ,U`F:[0Qm>JjͩLqMxS773['UBM w z~n)*I3~6 u1xHcƧ!kkyɩZ11KX0[5x1t|O(C>rW B7Zumqk* PKETE / ,pdm/pep517/wheel.pykoTVC^m}-ڬ!2"KHq!Q䀢]3y|S$IKv+3qF؏k*J!짊Ob]K5$zYVEDK욏mʔg Y\5^NbCT7YLД7X+ EV<:ߗT9| rW}BrUTUHs/>AC򩔼i '.3~f6QZq|*q`)_UT^9 Iz%~:%Kxs| (J ךg+V `mAEP$'*ڃh ͞+&c1#T_ʌʴ6!LOOޟ>}p|A?#xmuX6U <f1\r~&u@䞋_f ?3(M03~$>>?Ϗ/.N?*/)_~[y,ɨDeDCuRs.X4leEB:K!Q`|3W`Rac".0XXLo/aK. j }2A(XRT+.PH E35$@{``Cе~ kCOQG]% I3ؿk.6Vrr0Js{/cJR?@@M)E_l!fu֥Tj`\W$+tK$Y jx*M$o tZ\&?W%\ m)}88>98]bVCzh_OX3.6|+JcHV5!q;xti^c-gpcnm,i; i J#f#sA(B)cM@Wu(21[Ehl_V^8Zt KjIFaTF5>FJWC 񎡽=5Kc$~+tV%b.hQر[@ 4W2p-gސ!ciH+*+d toZkFw}p,ZS=Ud Kg>f @I8",]8|n`k_wBkhn(7,e+{Q'd"Um%ŪD,-Xsh$WLeLF6*APT"t]}xoo?.c,#,+y^Yge4^& 9fD:N\ԗLe3`fdfg`߰!VlfԱrGˑ(,–5X}=MA2|P.uI8`PXou;'ȬʳzV7rM[DQWPx"x}p6=M`#(oqwK : tX^gA p7=ز$(@-lHܧ51;T:i~{TNfةݛ'VvZ՝"{]p8qS;|Z hsHLH|L2. QUD9Μa;;hz+}6fjN낕*{ݭWFh;() ߟZQ?js ՒLc?6K<3#ހ Ÿ.=hzBpr[X]7%͸}m`|Un>tȵT,\#L%>rr7=S'"J "RV?G~YT;f19,yR,Q% 5QosI1UxEO;,_V섥.qmګ1`?zSCM[]V )W*4c@eRv#4gI;]Wa,׉HQ&jR|{8%^ᴿjOaDgc^w_' w4ͧUfɤ-ɒka9WkC6$3VQeMaMr ?vhɍ+E~=2GS]IˢRSׇ(n60j8od=łf̶Y֪eb05F0 $ItaQ<tt#:=Ӊ%',S(&b3zɜP|UeJiiJOӏM @HnNl`y.&mk9 ?b5]a-h02erNCLV]8X) 90$D٦df0PlDF5)JV8rHg&t7Okdݓ5^;g[@Thhh>e!o5Υ p]sʊ&%FMC 3쐈{ɴR6K!X J[ R `!X- !֕YA"($ȫYAj|jU7gO Oo(3P#32qmAy {gYcX0US9NעN\1qt:WJ, "w65Sg? g1 4'`=J\ g &얃x,=wL\ק#A]N(1b NDλ#e0#ʁھ˃7*!o#>f[`Yrʗ] ؤvy8(1MG7ۊ Ʒ~F+{$᎓:봠()kFt! K_2 _c<h8 &3O.WhQ$QsI9J^ P̥r^HJDaZ:LI.`"$rTcUl[-" /C-9v T<__dpe}-BV.T7|LࢯOw 0)`wh/@q Q\s,{O%{Ø<渊rj3sY?HgXz܅bv(I7 dOiGVdˇZйxD =X =- -HFI/|I/wj‚1D{ /gq ,qگK׎\q7b>v;9"r3'Oh)nߣ:˻]~zVj7)w}/B SpVS7 ghey5C#:KvMZH*xnch>{:/P"Dqh1q}Wբ*'d5>{g#|Fn2'e'JZZ1|/PZ'r^g +:۴E/ъ=LY'x;;(y6vXfo9} ;f-~q(x髖يʖPq= j?'@ָ[05vGoj?XemD,τS7WO:VZ6•\-zE',#Z45͇gG0_PKET*s+0pdm_pep517-1.0.0.dist-info/license_files/LICENSE]RK0W8Jqko&1$cH qb n[ )| i we,#|&"n<2VBp~ݿqjht D g;}l܈4@2 'c4ⵙ,wЄ[ tHz7,f`n꽵b/&ZHp[{p'wW =0$L@>8kX~pOsDD,*KL('?A9뇻yi@kO&q.ӈvtW6+m 0+Ek9J3fo;gv,WB }am3|xx pӬ'_ l kz`k|/JV8yivVeYiQנ4EK5Y&3,W*B$5 HN%EMd|)siv [ISJiPqmdɹj+U ϐJ(DiPk ^9I1AAkkgK27) \ dbF)dьn`T"=HURTF3ڼC p-kZJ"aNDqЪ់7x'LjSķ'PK!HZNF !pdm_pep517-1.0.0.dist-info/RECORDYǒڲo}fpH@'@Gi#uTyDTTwf "rݬ֬>` ȁ̆ <*Em ;dSe$`?UaݽE UPZv:DiERsnG$G܁0G^;D \[W@=X1 Ka0~0'&F˴'EBGK,ɧ쮐; ( (XYg Tf5騤QD, w~;΂/ ^XZAcΏ;l>xr]|1 p?eQ#1S>H{WFZFާ9Cp]Fq;=D"_gslOVUU(_ךS9[IYǥgHPw!O=  H*ZyϾʄuwN;ѹgAT-M}V}!M9!27iQ%Dw8oK> o^1a<.<)VMƹ]EE_ۛ2#ɷ5+%î4k09Mq6cʼnZX@B$v0oE x[7o,rZrsȁ:*Z{u9iA y蟄ܗ \}YGfPPULx 5oX֭jhuZH.-D%Z@$撋Oq+">ͷBo?Wu _31MEΝa W qJ#Aj˼qwGʨJ~O%u1 "a`IdQo_ }R0WudE"|fwQ^Sg.zB]'IX@v)yT^qf%C$qKl? ϿK:|yEO&N,jZC@=:Vg _l7!*?|R-Ϫ܃X%u}+`CS5_^rB;")DyQJZɛڒ>L Ӏ: uk̙/FUnXX`ڹkjpd|l ؼi S_a9EWQM1 ԚŻx欠!C$}iMJEp^=JWM{!^.Tu>РU1[>ln=S #i`\,K`G n=FPt|?"؄%+G@B j|֜KPZ. &p|+{C]޳%oyLnf:! @)o qqVQ 9{ԃbxŎAo.dW9)+}6p?f"m,>ʫrPjy;.-502D݊C^yZm4P9Nc,ܭ8G/00YulviI=֌N&`iWYTD|zpwB*yԘbjم#XnJGyFMB>=?{̐@L .7 ߼9Ȁg.]qm͋/5o5.s\fY(}tϴpdm/pep517/_vendor/packaging/tags.pyPKETA3h%Ppdm/pep517/_vendor/packaging/utils.pyPKET 2 I9'Vpdm/pep517/_vendor/packaging/version.pyPKET?vhJQ$fpdm/pep517/_vendor/pyparsing/LICENSEPKETßHi #(qipdm/pep517/_vendor/pyparsing/__init__.pyPKET aiJ' wpdm/pep517/_vendor/pyparsing/actions.pyPKETe~2&pdm/pep517/_vendor/pyparsing/common.pyPKETw&=$ڎpdm/pep517/_vendor/pyparsing/core.pyPKETGZ$V0Kpdm/pep517/_vendor/pyparsing/diagram/__init__.pyPKET*w M4dpdm/pep517/_vendor/pyparsing/diagram/template.jinja2PKETtB ) F#*fpdm/pep517/_vendor/pyparsing/exceptions.pyPKET*L+'qpdm/pep517/_vendor/pyparsing/helpers.pyPKET|\ b'pdm/pep517/_vendor/pyparsing/results.pyPKET:\ L4'pdm/pep517/_vendor/pyparsing/testing.pyPKET( ('pdm/pep517/_vendor/pyparsing/unicode.pyPKET-Gl$pdm/pep517/_vendor/pyparsing/util.pyPKET]Xuw0 pdm/pep517/_vendor/tomli/LICENSEPKET%$pdm/pep517/_vendor/tomli/__init__.pyPKETiiX#pdm/pep517/_vendor/tomli/_parser.pyPKETu1~ pdm/pep517/_vendor/tomli/_re.pyPKETg"rpdm/pep517/_vendor/tomli/_types.pyPKET+)!wpdm/pep517/_vendor/tomli/py.typedPKET]Xuw0"pdm/pep517/_vendor/tomli_w/LICENSEPKETԝ&pdm/pep517/_vendor/tomli_w/__init__.pyPKET)P%bpdm/pep517/_vendor/tomli_w/_writer.pyPKET+)#pdm/pep517/_vendor/tomli_w/py.typedPKETVBaypdm/pep517/_vendor/vendor.txtPKET1zw pdm/pep517/api.pyPKET#=Apdm/pep517/base.pyPKEThBU<pdm/pep517/editable.pyPKET !pdm/pep517/exceptions.pyPKET,wC*"pdm/pep517/license.pyPKETek:4$pdm/pep517/macosx_platform.pyPKETU?v*B<3pdm/pep517/metadata.pyPKETCpdm/pep517/py.typedPKETvǻ8 'Dpdm/pep517/scm.pyPKET$CRSQpdm/pep517/sdist.pyPKET" EWpdm/pep517/utils.pyPKET$ս `pdm/pep517/validator.pyPKETlUH9 cpdm/pep517/version.pyPKETE / ,gpdm/pep517/wheel.pyPK!HytTW  updm_pep517-1.0.0.dist-info/WHEELPK!Hr #updm_pep517-1.0.0.dist-info/METADATAPKET*s+0pdm_pep517-1.0.0.dist-info/license_files/LICENSEPK!HZNF !pdm_pep517-1.0.0.dist-info/RECORDPKMMBpdm-2.23.1/tests/fixtures/artifacts/poetry_core-1.3.2-py3-none-any.whl000066400000000000000000020155061477560627500253460ustar00rootroot00000000000000PK!poetry/core/__init__.py]PIn0 {Tn@c& KH'K%:pfyMJ!%WL)+*$=nu<>tν.,pٱP* %w *ƺ0#+)NTn %7_#e J&K&4qp(h:V~3x4Q߬Gk `5ݱ %, $.N-,bv`QygPK!6RoV*poetry/core/_vendor/_pyrsistent_version.py/K-*ϋWUP733TPK!a KB$poetry/core/_vendor/attr/__init__.pyuTn0 }WKnu{b׮n@m@QB'Bɓ\IZ/9(W@8@ZrcmrW_vJ[b(Z%J Cr*N4Srڂ6 r%n|F&J4leey)yr6N ?Z{Λg¿Ql dWJ𿠨8mYfI! ͗ʃj>-xE6};o\' ]p<SFxU) 䘆\r4rvKoo`Oj-?Sa7.0 [Aqxkvf|r^kV+=? !܇`b9)iK;]o%<[tM٣<;/=2m5/l<_ddtz<۠( T O>0oTA(PM3w!!GX]%A7aeS,C2E`.+ͧȝu 7w&HLƌ ILC'MF`'sw]%`"$rgW>S?az;ǣryA/+z8"m SϠ0K*dES\CBZj9 QVRePK!10U poetry/core/_vendor/attr/_cmp.pyWmF_1(g_~2q$-iHB[8i-e$owuwfV9!,3~߿oE֮hlVT;ml:uZ;p; R7퍼5swh+tתB +m` BJϯQs9~-rz< 7VEg@]5R ^M>1xcKƌj4]Z*kUFW;eԚ gA o *<r;: iHs&*ɹ$$ [ ?:iIBɛA5u*>#Pt R1fi6#ߴ싲c6 =[t7 *Bՙb#2%.I.HfYk=1Bh]aڱd/UǎîʫT%{B{Ma2Se@p"?dk0u6}]~K` Fikᄅx}OK=⯡[t\Kk5$\TG]IzoԆA|e+ :IqHJJ;֙QX;f f9-nըS[qɧ}m'!TlЩ-]:3~|^mI:ۃؖ8zp0E]8}_1E~L,e^8AN3OM|?gnNU^7U,ex_QӋCM~w_?5z2m<8G5N8j/zcJ1a/94:3*|*< YOU>B?U~ UΨtLTD5c)y/o9PK!׉#poetry/core/_vendor/attr/_compat.pyXmo7_1?x({N !H5꠽"ĚZ$גzޙJC gy%/^Xy|y[`t]D7Е`׍QnݾoCUj-7}*W7?ή!ƐXm+E {.+R׻_ Vq\%3n?%&1}B5SWx' $_ٻO?șY_]}Ռq*A4N8'@u:,B灤 !?FoI%V1 ~/^N^N?ЌL,K I#[bmj`CvTdެ T-kMLW6T9@]PvXRU=}(?ieVԧ=cB)86j~:HvduL) rI9ҙשEFo KG2}M-]T-a,p4 lukDG[Nx‘ϲamۖ4MwӹL![oGqtɨ$ZK؊VB! L5^ blc1fBCvXf6Habpbkc3xMÃ):vz-MnhR*HٷԳcj>RJZIrF-J?ᬄ )i~cmt^Vf8MA[,w<1{,c&Y%d>8j&M'=aMTy|5;rrF&+33:7~XwIæ-`7H2>ٍNECf;ˀ7UfFGx|:h_]]7'j=5J;9{Dj3"<;UNZHɄkSVKc;*M3Bp*ϥJfӔyeN6g U1XϒxAҫjϻnV4IH¹_2UL ^>ǻ#+-Ҡw<t|ƻ]἖!wV9;p0(̓\j5tnLhWxr`VABG ^pXjE [25~8DJ,orД1? Y)GQ It<˽0O&q+(M9 DZwW鄭㛿n5(PK!KXI:#poetry/core/_vendor/attr/_config.py͑Mk1+ q[襴R-JnF DY(VK{\Bfy gӠ8htdC/Yee-KOкrъ|bb~qygEjCuXd!H K%'8ᩀ=aTxw,e #%Eno䝼 Í [W~zuK`TP]$[*EbUS+kՠ˟ndm-|XyEBf5*`.2lƧ|ŀUX1ԩ&ZYXz*IoHmp_a?PK!0 59"poetry/core/_vendor/attr/_funcs.py[[۸~_N1ema`M .iS`095f,^R]﹐Y=l.m%\xy$OӉ*z*UE3Tٙ^-EbV̚\(_^e"bs.vei'HDJm FN?ݺTMY韭5LU&KuR4]rB߬J֩kV$ykS8,dRad)u'&Unշ2w~FkKZŔޞrm QΕpn62,8a21Fǰ!Oy FhkOR+K$)OW%d*^,bxtzgLzL`u*14[V%r2/G۹I, /1>K]t۹ˢBj T$:uf3Dlv=RkV+~JǑ\E[]Yw /2m~MS"o@ >tF-W֤ *i߂-Y{\jcl,,5adx(]\+禖kD@04[g/l^S&E_Ql9:p̓*0wо.%Hu=kX5 7h\"UJ@&[BgHk o"n,47fv h$#͓`e dl`өx22vq\ X {x@[QO9@УV!+JG(IPq|Am2sVei=h72*R]T=&A[ _Dl.j?yCVgMJ]A6i#cށCE Ny){hU~ 63nLz!e/Q ?O`Kq]]p`D~`?a>Y^?Pٵ~X*iA8aU&YozbY, LBj`͔r> \B؄v1F'rp$tzf/yD0-'hط ʾ z7n vaklApv!s9qLg|>?zĵfY: Ubz> gDEX񤥓 g֚k@T!~uԨn.Dr>5U[/oOX'OOxQ侚NSPq4x"gS$67E6N֍M_e B@z6T tB?(U:p(1' Dd?vdЙu:3(jb*++:J.? Bퟗ8Pn3G-qjPR ݉BLO BI"zX (ǫysS7쟰{R†؆k؎:wk(I!lC",6onﺾfNp~_U>8X*c; Ózbb @[ɒ;VeKny-Q)~*wm}FnR.^xՈ6CBzْ}3WɢCTDSZgCEI$ J1%-5ns*y =VrTL[{9 (V6$>,՞PjWAqZA:n& 1S4<@>P[tXɅO{!dCxE C#c;yZ!U+Jpuk#sU[T1a3kEuuU:ω;q//Ѵi0:]apX#yͪYfan [lt2VZ9^[ڭlQB nU$_>gy_qƷvb&&%s4NX!J ŀolSpx\- uN|"9%ST^uؕY׫`Ksh^҉|$KX{G.ū>t}c cpt>s IYK1^^cf:۱Ty`pq1.o QPl^_<$HKc/u~<|Vجaߋ ,-/!R%ܿcp;cDUtl )T٩G8?Mٌl .,we>iw*Ɵ]oTȫ ku+~ kMX ɵ ͼ@Y`z!WґW|rC=%?|kںp AS5Jǃ:TB@;PP4*Z߇^4%ąG ,mn. jEߪɤx"9tǒl܏jIqPpc GzycިL]^/ՉK-:F&SDU4+cʏktO/8(K9OĨ>ʙ/ALv"F14iꛚz[<ī`sV Cޭ90[#nGz5-Hg:xfc*xJcNtPK!<`!}!poetry/core/_vendor/attr/_make.pyƑ0 *HHvι'tblj?I)*Kbwq ڨo ]%K`>{zz{axjS]yl[}u^i_Ыo7VO om`m tЖM-dM6pa?ooVmZ/Mi>|O}v튮3_.xWg^+}wYHve_.ԚPjYXWZo@B8/jE='D%P?wj"Z6Q*jsŧc]Uo˷g Vy|9E;YA *'qm̶MGg}8ە'Wmx0S,_¤a/.f8쪨/vK/K X5vMÎ-$a;kJS=ˢ\Z-nUy}]Î^2ȿ,8owf 11zCh:˶:@ Y]MUʬ |_ACQM`%?-|s #>啛Lϵo.D`EXͫ9C0-zUS5nꬺ84^k?=sa·MJ1UbU/<׈JKXr{z}$?Ϛf7~kuHش:-&_vp VuyZM7n:wMekrx ,t]mi֖{ .S5$50Rirc֝`Hn& ~Sjݗdgw 3KpWuSf܅U ;z.WmHElb(9kxUnf0JEpȳI\d xÖvx,Q#e H2)ˣ4/iesU  -ay^o=T (G=lJ#%^]\Z"\gh/t6ijA=) ht$O_RP- înZ>ڛ'RmSa~ټؼyuBBmį;1B#2B @>+BaO뢭X>p3c4+ɀegy/O-%}Ycy|/J4{*G;R?07 ٛh[إ 6\V;M lg!xRu9`"w-T< ф.iiU9+jdCg >B6Q, h.5F@CZ֍[Îw١9 tD.ݠì:8˶r: _TO'9s`lSsR)f㛎 !=T1C.>J)a{{ HJHQxQ#g( "N\x7P)l+adX Z/\{ިyK>;:9W?>x҂NgʜN9 Z&̟t_Q0n<#/2 \MIM}7-meUù) iZ䬠Mg7:[U|{*FY텰Fl-l4BU#W<:dq[=9y'&"1`GyR4:=(%h,P? v pcm6-Kx4,%0[- WLQ֧S\ԎGY>ΘpDRFB@/%U ̙C| æ"HpD}q5+Ύ7g}y$KFOwړ R^zVOЍGw~0_#"+nN|g ex50w_ٻ\atǴ hWobqU}hLpUpfEU9XTZhuy=êNdJ]eI9 D疾<_VݗhfjIlˁ jLK*cxs]"qԆ@iЙv Zl \cPRm2(9 @nX{}hzպ=ANht:y4Yd&fq̃φHO NA'C'诚zPB0)sދhhͳ)GAQT0(}-Tu&&Yr侌O pm>'_f4 7 PȞL:"7eAK&ƪ]A[5BB_ctV V 0#( yt-AK8kC8Ԯi{ޖ'w^l[Q卲HMC\@XySFCjryu&'6 'ehyCK2%E&~ -Pח~M4dcAi@Nf!T`!S'RxLrBacr\1šljBj7coݛro [^9b. Kh]`/?y߫M* 7, CGLPxq@Jyz HQҚ*d]::)He&[SR_,l$\ 8UgX8c@ 1a2WgmwWWwv㽳~%(i@cmA\v q -*K} ,J92TxX؇iHրRFP?6l4d95Eѝ?^Ơ&_ x*m\{kdw8u^w{ʚ@{" , jnѝ)3zmw=RbQWo;nn?91~P18'l0 /ymAwbruMlctW%٦XtnS,/xWd?H/VCĺG;Mz̑>bU;`J0tp_mj@BQ̎P%ҌLZׄ k05cSxe/b8 IKX; ^he՜,uBE@(X0ZG3>kG\77hE·Y$^vG3i 9%!`@H^Ш C=Sj 7ov;\:cdAဧ~޶i_O@-"c‡EpOF:L+D(:3[ hT襹ʶ_Q~O8d S3ѐC g*( Ծ} ѱvzeM]闉yܥ_ZFkf>(K%0;ȁʽ-!(UI̍VLV6buE]w&?A5dd"dvfʚ]D)'7X W*:[|n 溸墥$.LoF^b60;jL 7^U=6WP'zJҶ7dyC퓾jpE, A8%MB2Sut0 APv||N\,bJ偰tu/ {r4rDٜ8GCݍ (Dn7Ѝ;,w|⋘Zrb2z{6ZաQ1SA'(=c@Sr✂;)M''!42{AAmolDfʓÜ;57w**GVP0|t>EmЏ "|i P1>#ۖxdq["-P4N x[azۯޔ@va+=Q ne'` m׻.MuUjg.hߖt?4]?gvfsk[,^9} ? 9TC8>lP yZ _2o;j(()ЊFƶk +MgD#KE|`2sO&`t&0IU` % c?2Ѓ`sl6|L&mG G"Gw;0~{Q5 D׮y:I&a,yZ7KE(c0uM4ނ+}Rg&:V@niMi.9cqqޭPGm Ň/U)ZF%i">9cB s|#Tv'{PY9ک9;|HKl>#C(bccq-1>M+LsdR96RA~\wjNK^Qa䫦[텥/YnJ'+r>cf?Ĉ+@i8\oAgiFP?ě2bsOv%v8ʂ)av}5}hDz sՁC=KD{ЎQ>(>z$>0,{5iqh#$6 xAH4x-?gZM$z Ɗx_T5[W6b9\IKJ}h #$&  ͓OR~ 1&[epg#&=lB}HVYדa67o$>LQx7ѵ.) /klE8> պHu(iբ@%1vSLsb`CY#aJ@Vm|t†p&IG%SRnmB)v`4{3 ae-+%U,tot Ϋ QM6Xʓp5tTМ[5-Mˊ V-4(Di'&@ Ӝb[.l\2: ġ1lb*4TJZK**,ΚCO0m첵M#O=夈FxR9GLQQZAEm$IuXpR}+JǺ&ݯ ÝIMD#/F˟J%D[au9-4#l$k"ˠ Up|[uX4P=mdJBQ:5$=*5@4 \3q@jC8GpK.ӄ98'f9Ӥ!l#fˣ'Э|4lv<Ǝ?\f&BBBR%++^9--H窼ra ;cio R¤| 2ǃwZ\Y͡m1K?tfLQN%c$r-jP.Csf;3oJjɂrd*ܩ6`-.E.!uͺnꎋj>G+kvUZe} 2js4E3 q_{߈F\C0hOZ ZFCѝfBqQꄃTZ KZ#H6DץƸ3m2i 4TEwJ?cjPi_LAqy HNY @4,& ڦp]V:"is <0.e)[ |]vú6]g\{q>vbe{COxh俇(Ps V-֘m7 MLt62V|:C]!Z̻v99- su',qjRwIٽ8NxeU@"ɐS*$LwC`tJiu_ >q{!wL)[ߺ>RP{ s"2znȜ؆Zi[W<|R= =gP2,3w# !Fꜱ}wDzDsb!oÌܱT,oj3Wko~cDRsKlu?9au75akIO&1i^fpINɌwF4!9~*8Bf7tzwo䉔:Uq(R,4]DD7J뢥14`kaLD56KU]8׬>u@y =,uJ~V.J"v#Nݖ$Ə? ϳ>;==ʌ$V8vl"7?)l1kB$T&CVMmɻ^)9$@x4](uE`׃,q;&7} 4WgˊgP5ll|L+J`u,yFHRu+-Xm!QWy3bVsHi7TAIaVF\pCqjء:4nnoc8Ђ'Ȩk-ʑD/單"<>I~W q$1Ht Kg er}uH p4ni0bp efJr>A)&+_]lijhXEn ~n˕f"@ϲ,S5p/,ɪO7^ٽҨy.6@$:M(4qR90!*? بw"Ka 5zD#by70nHefM]q!?Φ_OG#iPHaRo\h͙LeNM̱﹦v$a0O摬@P(K%iiJMxRy#k *Gx,kP X?Y {JP%saN?Ph3}C T8O_PoBq }sx[ۣ!CPU+ \6}> La,Mb42B7gW.PO6 *u1oqSv}kKوF3NH] f)փX,6替ڷ.֐s4-x̚х9cUa޴?QS>]8 44t\$O^J҄hqx:MHZ].i=1PΧY,.NƶV6&g pZ9nK|hɭ$H_9E]5[8ggu 6oqy#Q0y"sNgh7cs̴u0mGUV+?k)?Z8_sϹfS)lam(ġ /':J4զ}5ݜ'p#nPmRr $c6vgUi=8ިˮ {64Vu&#rFLc]/{-wզw7 q'8z@}wy?KO3TV[$}0Fѐ-F>ˬ!WɬG5E|~)?:`|+[t])5"N\|$Z6$?~37P:&J曳چGRFT:pѡ$Y#՚F\g0vN/> ^LA0SءF_V/$ >Blϑ/kNa0烈,UC:пciwOO3{RN촢xMmnf9E r e䁸U%n?lKs\D3]xP T9V*qdQuVv2ҨkFJ[Z{.FJ^L<~IefeJ6mrv޽iV^fN>3k9T՛q{ bKEѲ˹wMnK:NO}đpUM-v(22jKϖYS12: 3b^P2q,Εw19Z%ᝑztІղxԹiqDsfX%) Qvcs*AѠrB fdB,w fx*X1 U 5 CWϲt-A^ NsQ=9)7g} ^)q;$i_4/#p03DksYo*afdY/dݙ\at,̾D!3Vf3:8cn@8A1xO\U14֖ WHJ(#$"H Eg e$7#coz F;!$lVVٍ$_× Jl,Ld&p5ZLL8crfgkh b x^@rYrȚ=K❌y[n91I+k#3:/z YKunA*@xDOIܦj&m U%ĝyd6Q7, (X+A"/o"zl`xB HHhj2K&j5ˎ&uiER39ݗ}%3KIS'b!:9-N\#F^h>M>kXR׈L4K-Y&uzcƷNB0LC1.| CFsͭ-//CQ/8LFbc<񘔛qmq'i!W5@e-tiJ&]!뷧x-}#R"\s\]T_HǸ =k4?akUm~ьtA+qBj)V}ք~*|WhdvTE,Y /ٻ(=<,C OyX1 0D.aAc:cCI0VyIgթFZsUY@tYM 96SqltK"UStވn8j-B)򍂞nV L⒔l% _=2vmQ<%De<Qٚ)T0 dlZ11t\i'M&^qƨ=:Vh8D ) &;3EwH;`O*IM6;B\C"jgmtHcn1LΕ,LkgOyrh߃ͷkF@ĸ7tUm h98܏ɬ4o*'[` 9_m36\p##|Ď قNRŽTD~*%PXJMjHM{u N5̎)4G暬7(5ꦀ7SObb^x١ (CLn+^c7Ecқ q+3<"dܐlWMxB (^fi<ᴉ,a04alrFJ+<%蜎!'c=y1R) 2 CoT$tazn"ZY1.b–f>D/mm,Dgo1fjP> =UAmky (mEXf.x [ɘZ^=;E6y;Yd$j@ψꍐ;Vk#?.ip-"Cg~V7:X.sK@y-Z7J'DCnE^ WTVӺH<-mn ɡz[{_KվuE^k0 zZ;O:MWC]OJz]04&]D&̦2\-{c$)m:KT=}di1*Dƥ[L#*s!\Lx ^qE\r>X$\#R"RXB akg| hy6oNbnDP0q^&.c*giqZ(1%iRpPXr{z=$? UBO[)uI%7F?=4eb%=ON?4ጜX)c3,afo)J2·T,ÑDI'89%'Dtl.% nioZFUbؤʳUWJ+1-ʤsġ$T4 " &a6)w >3w8rSq9OtQ80˓u3?az]]݇=Jg" 3K!G.Y>p34NfK` $fz- B{3I ? w":;9 >GcaN]?=Ы U>.X^t#](qCE|m`/LYs\|}=!.@#4(a Kʓ`eBpAǚILarp.i!mdNc\Swރ2cN>J};5k!S8SƑ-'w7sj5w/,^3rwLgYs7*/9<٢]MOFsLW+VެV:F:j hI DQ\Iv86T/&}U<Ҏ< ?FrnJ9x#4>7kȤְ+#<ԃTK06\24:iZq@CF"ǖ0?}!5?Oop/zqrWA;=fmjx΅?Nw :4 we):AO-kBtvQxd:( LCkۥ>@ uCQ>^o 躮6Jjo0̩yNJqX$DvdAzs5 \Ya2hا r:@u\!u1Л/J3JR,$M@!&Z~uξ"_ѹkpQkWj{_L sQԿʇm'4gkVi g@O1 k_ \m;_Kf @Pj#e'أ!$+jO3!4(>:R:,\D[l bל1_,~`qdf_P~`sX9 SņMf3BtىSu!?$HOPd'C J8|m!':vLa 3;{nAGwq D9I,zS҄K.r-ϫȍ{Hߪ/ݥ4;ZlzV5`B\Q^;I.V6[22G!G33 ļ|c`_ .P vAQ]Dq<]  3$-x_)!_}a!/f _h5/Ȍ 'V|] |;18C y:]F4\z4F$Km2ER-yi<:&TwP13A,8Pe+&3I ܝsߙ).|x<Fɤ!cE|fNTw v>ԍ${(Dd} wdLRΉdyTPV%w߬X 7ߑd V6d5 52sQ8 \G`0d',tHPfFיewv؝r;$NL&QWc'^_#Sg0叞?˲~ϲeu7n؅`BVz!`ZS@VzG)1wUqftqDԘ܉-W;L'\9֦\ʌ>“O3i(np2v|ouaAH݅ !!jihB[L6] S(C] N#f$f.0E*6C$@9 hjMa=n$ǯ.C603Q@0MKYS\<֕m U?쨧DdZ |* DuH|U[;(W4H>v=HZ&p9% Y&Ʊ]p=%xexwf1Yt줁_ui%7saN皼m-8.( Ð-gyFO<4 ?c)y ͨ(.r]PxS$G: w{:> ̿ݶ,='M^fKT)8>|hzϱ%r Sj,0E0v{j"'ܚA܂dΕ)P:.U{W&VXh8/ zCf.]z~3/N v&UGP1SY}DܣzKSpC\UOzT qs TP(K" vV\ehRA>zʵ-?5 W6yl' $-8:wtѳ*,(O9*A6uLql(/L΅;TU. nʹ<);5v=gӛ׍ )6!ҘbHOY4 &<= Jj'nD/xבZp!QX^c>?t Y@V/靻U -DI0i$iV~tdm.;$Z I aR"J>GVp_֙vnMY2$iٱbXgNP)35GP{:M/t'ZP=])`ud+WvdF1aOi/1Sc<|:ӎ%4D{nIr*d8v^ի](~&$++5sهGSM8`ЃA9QnI ?oMFidg5\(P<=b=~g?ҠV~$8#s4b2)(F@oTG9Ge{ cBP^jNyBL6̖©Y Nc$;T]I5=MsʁwLPRцu"ØQ\D2(}Dl2<8⊧~<0KDTn|pu\A;{Np)R]yTׇ>HqetLR" )#_5Ɔւ7Y-\CӦY)riC}S $~n;ix0hn/9~ 3>PJi(O[ggfuYa$?(Ot%ڡB? Kr<1ldZb·U1zCui*ssY riev(1ە=wf,>J l7J ;5Tyx'(6u>UUF Ǡ1:!SDz9~Qu0߭khڋp|AtJq:J(inaC 4}9ƴjEVѺnaI#q}x0[>"W*x]r53~TŨ~㤰-n|fG:=>!z >8p1yUxZd!ϸ}* !<)CZɀ| Jq3ye)!:ɝx}'JXkD<}a9kߝ 9tˣCa4Zw0b*:KAawF0(rᎹ1;|H cƠ*D;mCv :F#Αڒ>G !'R9I^`2o.?/M9R܉! ь~O0w"R_CӰfxqCv;68FBGS6YoXB.h>ex9k7y[jjF.x;f޻={UE{ήhtX]S"D r58#ֻ*`୏wH K03ZZ4R8ݠDBڡBC^FǶ(E1VkC/G̓ӵ\og^n׳>ͬ}2ibq[{LNe31/~ՀNRcNR`RW`7(w]Y^IfŇ$l8%.ݛ;oi b{ ;?Msubpݿ܍%7DaX.:"]y$ ބVhN&P#AOS(Κ7)f>/m1K;"Î&\ɫc˰(-Z'xLfn|A_5YܠoEzcw_iُ>N'% fKBC}T&8i h#q.Ĉ2rƵ|HBЃhX1@fW vU1#Ψɂ\9o LLfIhwX܍#ȽǼ,|!rz?c8$rlx`l czm#iLJu,7bǝjZBͿLs-; tη^|1xѬneGh^Dk༼"o˝@+@dh&Kr$Cyh~@=]Niȴr'wU/T`%7=;WyYOZYAu4Ú`?ץvq5[1.L/gGIyԊ庢ڔchS6 e4 I(ZHG阻Df*!2Rnxh~kmNfi%7W{I=/؀ӵ +BC__ӵuwCArsC ,=(=Z( ZR8r!#Yc;ZȊ]1]rܙ1\_'e$=BmzraQT1ޖlQrP"|0ta@yszYOR;ر>AÑWI.)L|NE[cFl_6Hf.1k4zcMndB!*hnђ}2ZiO =%XiraG0@QC&+J) BP~_dxMۼSrFfw%E>y0㥔|#`roVZhٮ:(nQyʸ@ؑupx`l`͇HzžܫU81RE /.ݔXd^Cȍ`8 |ܬc!B|k ݣ).GnWnr9!Hπfs,+[=Zi{.@G -@9M1.( Y]a8љױg'I5CpsRN,9,U7MÕrjjwϕ$֬Cj9Bjͮ f=uu(8~g#eϖXCNiƺz@4\$+>L OhOv4ƊoMVjfau %9X#%^MCEl5pwۇxi<ǎsKz%=X.L3@D ԣ$T+hDԨ@Õ5\)QSsk_ox{M.VNjkדjilUaɯl'Xqs,KANVFHUτCa{XUnI8M{?L؁|^3xK@5ZC^V_F^Zr'GԜRyKOw{;=k&ϲo5Jڬ'ăs@-此cg qGhưOH:lM{7-jV M?nT*&Uw~/KhX|1$u3F~I |l)aagDπ˄~ $sF*eZ;|'PI`4N`50Mob|һrd~.nX5xq5OXu߄YvC ?Lsp czxGzP?в[^%VGlKBҥ[}̞̺hW?7L:C;R*BDF| K"Gf ';$d?H<0 |PDU)UZGjWTdFk]}"gD\nl5!^Fr,Uz{T1.Fo<4D'o5jP@;+~rq!FY"d =d#9tQkO9&a#gBfdW*o85 a N~y 15XS1 */N%f^l+J4O"ydPgj뺜PK[U5hLX[D>v-;=56/g&TKmA?` 4}Uc윞sla/d8dȾ)ĺ!#ra>_ f2K|Q xo,[I 51IV"73ׂNk\eʚ WdԖbfqkԻ#Q o) I(j Πw;&[*GSqE3C'*:gӔjmžpbCnAтJ@J]Sf-唭3:3d(G 9u [ c\ca jic(va+Vw6[!?K{s" VtJ0_yf CaKIbKs>8Hm6_++I,_936v%IBJ.3XMLg#kC}l+`]2D=i]4sɸY%׈PF$Z[[Xǀ`5N, U8 ^Ywũx'x/ML vO7[2.*,S4|콽SqNTPgƇ93a6ӦXEj ?]SnwiLښLOK}Xdl ^c н1# h+b)ܡ>l@91O<:XA͞yiL="mYڦf_-av͸]?M/si!ަ*I{Z󞸠<[g3 3A?` {>#t-K)e &+˜/Եâ"LK]R(BÎsA7$߃M;@-eXWxC[.a (3Ec! >=j,*ZSdtZVQռ$0*6qC8h dv3zw7O4W誁RQiƷ(}ҝRVA4  C7^H d2L*88 O 눍 S7Sc4ӠO-: nHљgUY}:cnoQݖNP%)ӫ!,Q[JAH64^>}jl Sq.}ԾڗӹJ{J?f}+ xÞ,2l[%HIFҐ7cH+, i$cp+MXaML$1{sB 'X\"C`!(-,>$ ~ʬ RH\a'E>MWEVpі1H㗷8>4'J__n򩡷w<7w{ρ>1aPK!$q%poetry/core/_vendor/attr/_next_gen.pyXmo6_Aj{v>k v -1tI*ww$EJv"{yzoʊF('R5{{a6{NXvZ�Ъ=2v'm?2ռmYŝ3p_bvۭ0pkĖc-lktǶ֭ek؞'y8ms0[a J?|V2G߉H61ݯ+z)_J5aVݖAR~ GrưekwRA+ah^Ѿމ_l^7[{N+XC/fFMvJ%]j[Ӈwk0sΈmu&ӥ>P"zrʚ;QT`)c86‰:V8E g1*hVNkw6hFgr(lUC BGSPHIz D-n.+uGff@l[Nm6U(wu-BEڲGI*ybW^GJֲN+H(VpfcLercq~mOڻalŁK+7^;2H{l1} w f~˚ zN0 ]6b%[Q=dPiӭ+T[jzh6C>νGuH%ĈVsȼCN(!;?u,UMc/jiJw;߃ՈNߋ&Lt7T, -G݃C7;n\taڧiw뚀ȮI-(0t5(6H6zG||@.:Va!wB /hWDŽh5 U"8MPP&-z$B[``4&|ީuO$ ^xM 8C"G#n(ad[9$0ɦz^>/^'x~ "NU]PVT)u)j)g k!k˚ 1!VC}ZG2ݧs  <0l#Vs)6%pXevL'x52#V:E8+cяb},, %E9Rf& ̣䂾@Vx ׂm)ʎ2qCiIt^ S62#<[c=@H $SY;xc<) B;R3?KE zY}3js{?yxEOA/4h 嗸Q q/ B5v>{ˡ ſ6x(r #q`W/J dN=XT"yX9 ʠ Ag=ή&,fpǞx_!~~k1U r<ׄ4uVgÅ,B۫[#7Pb0@+`8nY ѽ 4٠}~xkGg}ܤ °>V>Nڸ3-ܙmDak3-P+c9nH1y;{KR|R[)cJlj&MtIs۱ҫ 5BH!wOEH0n gV^)i'^F' zDåLf-"ndqWeK2tGc~;|EQѵp0"?TUIh+a<}{KEV{`Vkn!WR*mVUE#w_P@#iʫ*Ma 'މ*y*olOoҍRt"}_u-d p Z]nVU-+\isQn "a !n(X$@+M NjMbB='y`qnԈ*G > 1%TO T)8+!=W\ Ѽ4F 8Dz> o5]kqƥeL=?[4CӇnj@? :*vOǎ#߅`<먁'd2`f(cq,CX캣Wmf6-FGU us/ςx_&ͬh/\h;::^CmS%stpl,|N ý4 R(E'Cg b 5T=BM]{I\p|40-E'lY=!(4/#6J%[I:?r9܊-mc0DBjYp#6 S**-;lxcG4=wYoWt/ zK5  ۿy0a87J|}тYuũR؜ *8Z0b:,qY T6'ߠD#qRu+jۻV[7%BHpǦ-_B衪4\tڽϤ{-a%%f=/PK!5Tԩ{&poetry/core/_vendor/attr/exceptions.pyn1y(H6iEJ ?QCJUf= Vwmjѧ^`iݙ7g nW9j#ThtR87FZc{C"f0.!1*ObV"(HX\X{ϰZJ Ob@!N\*vs@ي˙S-*%f+Ny t 0$BΉy.g:m4w2GBJ<γM  ҶǤs9ǎZkCn\W]pB 5HHm>1^DU~4;"@/^An|)| ţMlZ^ٟ'sXFUX?E -hQp+PWk,/iXZru>{>BrTh%Lz} IGc I܃ؑ}cm\bV;8Հ(pw'1_߶n%Z@^Wh'NB}8DnRm?RLV4IO5o8F''b&,]~CEj$-Xu~PK!qd}#poetry/core/_vendor/attr/filters.pySQK0~ϯ8K;> D}Y{`䪛&mƜ0Msk/uyJ4 )fpzfl6릮w94u  \JTΤmjy-T6R1VܵZQ&(4c/_Gk5B#Q}7cQGܞvWGt)!*9:GIPTǒfPK!!poetry/core/_vendor/attr/py.typedPK!Lkx#poetry/core/_vendor/attr/setters.pyTMo0 Waݎv(n"эVG4$G˲bKkL|OWdZFLi-[M&vd_x԰%zP Cp"˲]M.@Ȗ1Y:~ؿs Ms8jS4-3ৃ_ `AZ C,-VV VI0,Zve6Rk%ouAZs,Hvka#f=g06mtws-ҵ)MZQi1"řk-gUL@ E"x|6Ybĺ-%z'=o!!S`+K}1ի(\cÝ<.ϵ?qؖ OAN|%e|zESTp.i%bK$WFA/VjzW 0U6mT>{ʏb|iz '@H >PX')MʳCp`|f"r_ܯ*p#PK!B&b A&poetry/core/_vendor/attr/validators.py[6u?D^8JPMh%Wn@<~!梐BuD5a.pm^Ec[)ۜyuYɘl)Qay`5E-n<1]Kּ+QfqleDT"qlk0=gbxHkzYYHuYr]UdM ~o 6eHD}[^>govUY__[n#Q,\o\Dh' $W0 y!&ɀ2f+[Il0+U[Hܣy1IE2ki"Yz~%dR Wr䢰0ZNxn7U.[ieo X282#;T;YY1QU |Œoz+cֺz_ \4}[mE8: @PYYvk^aDU&r[QL *%_lw(Nس@!5+>-[JL,,+ٜ8+۪qJX`T#ЊNYeln)Im؉Pj` O ߪLp+Qag:2d- 4w{K1Z e"_2+З\U ɏpmSM2y^r ˫j;Ϲ,pBeu(I")!^)cL\| 1A]c<ίx`IH4;I.2i03"pʗ1Ng[# *}[oeg-V0ԞW2 aʞ-ϡ(˲hn?m‹i9)uQN4MH5m7H uGiSsot@&F^tK6c#yb7"L՜Z!D ͊Π_ аrYDs]@5Xͷ9oA4"X)t̅p^xCW#RqAnp]r 5r;yWJՌYBth&5-&C$XBJQZ䭊"Mb ґ"n6b^`0b27%5ת6.SFЍ% F-vROlZb (> \%'rLGM.H?cqJ@!+qZP "RQ=: Lw=QА|h t}yA.0эbs/N_{uPI @IR6 b"/*@?eF7ʥ:[X+R|ѧ|BYQNuB PIl0i//F)B 8H1QCPJФW/yNsߥ "b6|)1M-b=Q@xPldqO~w5_la]}!t*VȦ L7">'I b̬葪j ),|RĽ#lHWhp8f2+M|u|abbyuko@\^DxR%W;5"^HA6om_wi/3UaJNr루6G$ =S1Qǝw DvttJ<ݦF HI9[hovOM EGӒO'i8чe#VrH'^A+f2E$fCB$>cHn²t'*5 K0ƛ W Y<덜E ;=>mH? 9܌6w89IWcмo^0]ǹ/#83)H^grSkQ@oI\ng-I0qg;prd7=)ZeI \ 8FRٟ)#~z SxV ;-gz_Vc6WEIg,~靵uk/׫cP8 Uz>d)v|S|QYirݧq'n0i:S_,*<=èM|vM0B뎏:RE3;hI&fo#=a5s=MC4^yy-f{ XY*:Kǀeq8`btq\bo7}UAP~V3%t7;v%3\ԸVg9hD^yVIF 5Xkʌ%҉hjḧ́(8j~A?25;[j;-ok1kpڳ]Cj5yZnN|== /]mSnM<|SGJǾ1FL 0nbVDᔅM=p0Bl.4XD宇MޚHWG;A}0U{莞M:xj=km팶Co*%O}"TY@RU ^1mA0bxmSíd6d{n |iA?6O,>.\9cc%?h mӎiޜ*SZ;?pJo*1R "1xP}db#M%Q\)ernvHY~ o ?"X]Z,Yf>4W; N?w;q׈4/wCCрԌl+P,`{"҅]8 ͦxkV᢮~AK5GWv!A\m0@|s}Tr8Dn]00s{楾[FF>+7DRnj>~>Uj}oersDv4tnF\>;W9@ɛ@;yЊ/ssd֖kA "rџ[Ei¾[jwB9hAOHnsc\V&dU ^OQ^i\J@}e!Xs8p#=E LJ#\}K%J}UD *>uԛ셉 '}JVSʸ?9:p҃4oY n`,HGsW@ BU$vE"$r:鲢w=NgH͊3._P8ާөAZN{Nf:*{N^ƺ׹׵zPK!>(U!poetry/core/_vendor/attrs/LICENSE]R]0|Xt'E/}3\8rQMb{!F)wlTo`5c0#!;]=G7(y}+豃hк1z;G!Gu#fwc4]{o =AtHvnx+#MpxL:Z|й|4c1`~Ko/fzBڂXW Z! _%gU^z9*_9I$xId+&K:%Wی,RHPSx.z-k0_ mūDbzBU{e"t˴J\((D`؜3܌KvBSyI*]g6Hn`(rElR ]Je@%oR K)VIq"BL$؍%E \GRn;!,-IdmPK!9U%poetry/core/_vendor/attrs/__init__.pyeJ0Ao= @.[0MJrZ6ެIZM'4{?J\`P5 {Yoqe1v!Xx6ۧq1]XV!⃐h)ǃuK۝H5xTʚhehu"B=M6di,{+c)ێ^}e`zHu'ּVXDՊoR{;Nvm8:HЪ _KV #74$V, wpqnEg*x<-?梘z ebkgN䳵E)\R8BsIB')1,D1.II ͥBz9ƌ(İp>S0)؎;_PK!0!3HF'poetry/core/_vendor/attrs/converters.pySVpLN+NLI+LL-R J+UH,))K+K-*I-*V-/*QRPPV/LPK! HF'poetry/core/_vendor/attrs/exceptions.pySVpLN+NLI+LL-R J+UH,))KHN-(+V-/*QRPPV/LPK!8/EC$poetry/core/_vendor/attrs/filters.pySVpLN+NLI+LL-R J+UH,))K)I-*V-/*QRPPV/LPK!"poetry/core/_vendor/attrs/py.typedPK!(tήEC$poetry/core/_vendor/attrs/setters.pySVpLN+NLI+LL-R J+UH,))+N-)I-*V-/*QRPPV/LPK!WZHF'poetry/core/_vendor/attrs/validators.pySVpLN+NLI+LL-R J+UH,))+KLI,/*V-/*QRPPV/LPK!Ml!&poetry/core/_vendor/jsonschema/COPYING]QK0Wrڕug`dMs$WG4j+!!Iut.C_??,xJhp!6h-#4]=lC=Ŏk754p2vH1^p uq5AlXG;xEuG,g=sPW;?Emk#74Ԓv@9~`H:L@>8oXл%:>L9>{ }Yg+ Tv1 8Jz\٬6*4~}C(QΘV}vGz@` C]70*I>D<{q?Tjev\ ZLd Yм0{P+~"K@**PMK5Y63,W(HF ީl#t'_\}VĹR8\nsRU3-dҨ"60O5/jߢ{M U^絁3ť@g|Js.7 d|ßŌRȢn-DzHUPTF3ڼCw p-+ZJMhP3 qcUÇx'L<Ň=PK!, {*poetry/core/_vendor/jsonschema/__init__.py}TN0}WEZE"H 5B!AjP3Z":CKfc-A TQ9Lit[S,&0JqV1[SGFz=)K̊\@oC'oCoCGh?حзM/pE}a;L+ d+N4vk̯љ[n3 ݐ&nHS)ݡ#U跜'H#a-0aC݌eH- -J<8DC H8/C3,t^9<ɥ-dfXY"xKkdd4 QAba }/:-M@H5Ң3Ѷ>޹ϑ&˙U"Aj8C GB^mgqƔ2E~knR],R}djBrV(%ZcE& gRp y!PK!_mD'(*poetry/core/_vendor/jsonschema/__main__.pyK+U*+NHMKT-/*QM\PK! 7._ 7)poetry/core/_vendor/jsonschema/_format.pyko~ŖơUؕb:\8p5N@ȕgq8nz|bC ?ݙyrɒM}wiqӜ'6$q>_QiƄPEC3ūҜ|3OiJ4=S}/lG=9`,pd;β$|r˂ۗ4:bdyf*Y‚|5!$VVOG|.r"Tb`jD"x k"V<[MFhDTbq2W?^uǑ_&i,IPr)uI„ %;iH-C \^RD#Jz$\ l;ec8E , Jƒښlr (<C-'K[*셕\5\ AhtG8t r RmYL(84mQA;T\1D)P"g "FUz Ӹ|%!!9jyrA2f{A$| ɤ 5/b\,Գ- "&]E2xBĕ,4кhY%xtǣ h[8}[,Hȃ$.lR>2κBxB^)m  m&ͅ b+XrEE$1&T"gڳ$;.X$@Ge>qu#3%XiI-u8Y߾sf 4>gy D~v\qWrVaypQfZT![bSVĥ䋒 -R˦v!"Ԥ&K}2X-t9cؕM)~{A 0Wk:[ɱ;77l00?{xKpLpsٓu-⧏ ~ ~#g?A|qeG˨jVuNmtG-Vp𑬔>΁5 MiU6P }"{"Md=݄q#LҌ1r3<"9y#W'1[…Bk%JZ}g]u׈*ƫLP @j?͎H}#|0Gθb—nk{%Y]jLfzvwq7XET;bNZh CdtˢaQD^iT0CaӺC:$V\TT6~9bB;Pz}H85f-lZ7,W#M#$e>AƸ@ELu/ObMJþ5ImG1̵#Ki&XKe/P75uZ'@=Nui&makb̀O$܇n'=Am=:=n4eF''gc7{ZNMU֓jwIMYЬ%9\\MO hzt R}ˌݰUնri6ȰvRYmkcqt2l+KZ=Iqe.8wHXٓ0H 3R.UXc$$?nZ[Bo~X|Kg<8ݱuDxEP繗ώ7/~z}١>7:J IKkq. k4<$A$csC\fMYpqXRƢr(rmn>߂%lsr! &ON!&TgiD"hv 02Q0HG=]P~Wl7cfzE !<9 |%6ER]ĴS@hn<=;Icvƕn. $t# [ IX\Xfw ։G=n9SBQ1>N#ЌA&߰ kp9Qrɔd$G5-$-.x4tK-4 Z]4{}:Ʃd\jfr֒8t]~p㘳] #6(=i{?Sm0-rb/왍)E&_'Q}18}7/Wϰohݗ] may=Wqk怜})z{ 7&BSGu3)}>!'fPK!Ϛ.4poetry/core/_vendor/jsonschema/_legacy_validators.pyY[o6~haCZ݀vCYRI*^Db'mnQԑynΕFV;AU/`ǙՕlhQɦ:j- +)+9Lְab[V6K%V(*" O$!dUv~-3r4z61, BIЍ,/ v J#zWM%Y] }s \B- n rmvF]l(&deEԲתKhl\82ze(ԐЩv*k\C P˵}=,O(et G#ɺm&RkH}R>@oeUH5vL)3#k DɫRI:495>{ŚkP92J m[\_ۍwx -UZb-ˣgJ-`4n,̦[o7'Y^NC73Bx~q~h,͐ݻΈ}J׏'&!c'&1O6f^{Ԧ^pXĂf#+ScOpeM ,`Eu9b k!+ cPFHXS+{c: b oku'&*.̈́]]-tg< \;̠T P:X ;7o~rA]FaaXPC춬{d2pރ*hzӻcM~-Hnĉ9$0.nЖfȒAsU omuh`Breja/;Q];܎Q @u|p(qof<ϽQo. l&)>pd4j|lxt!?z̤݁:,jUnQ̝+|3;Aloq%7\h5?ȅ/>[s ?Ӌ/"ю*#ߟH7_@/vЩAsF|6Є;X:24OuhU7؆7FPtG/E0ɆfKjhqG黁yEA{`=|a9alt)`S1C3;ۦHAtOJ 0NL2Ylydit?i[(;V1`o g.`fXttit8<>uL͎*1vA+NˑK#dJER``˳Wz"1M{21ʆ1=هJFzN2yQX]h8*]E)r6[@ x7 >8z`x.L"{ǿLH8KIC;ƿD˦^6{@H" e>Y{6 ȩ`ȳR/O"bJ彆Ii׻}/*ٯxHYTw6-քļu-{O4/(85(g+L)uw= IoPK!S(poetry/core/_vendor/jsonschema/_types.pyXݏD_JpPjRQ5!NU!TtJ6vICUvf^;{wKlog֩UcUݙNjn*øFRg'&jB؟ڀ4aʍQFr ;^VEMVP ^o|eӂ-r`mm0 HP\54TkK APB A 4zeud5n-" `ww% y6JčfU0[|5cGDjT J"d"jE~zw%0o>5ONҥZ5oijGlt`m,oJK 1 kO =̣x= <tϱ)BR$/0pY|}n.Ȃ`#lonzb+K" -nHo94 uSG&B>*Øe 1vFχݯË쇸ǠZ&\CSlCEe- `l9y,V%?CSٲ$ }0)|b[9u- i͖2>Q-T ZJyY? C,䱯8?ﭗgƋ?ulU)EݷC*UFXb\ҽՙ~Z5P\☚ۚq ŭ^5s-'ea(pK ڪ2p!3lݙV@3˰ȦQ˶i )Oa_n|iNq,sz Ͳ314M޶]SS]vO{^3R[g-LL0]:[գ$+OJs,au[tU];\T<\ Gph#ġ}r7;_e>M_Z7 6ml񰰙r<*/Ilx~޺pF9ԕ2=G{§da"_5 ܹu垪pc#o?ĐϖbkOۈD!`GeQ}D/OG#rDw܅4 dpxor/r萍w64|xdܺ󯃞 K6aX7-7s[xti|W^%_&Qy= PK!DO ((poetry/core/_vendor/jsonschema/_utils.pyYmo_ >造)z>"]ֹr";/uҢ`2;3̳B;e u|1R~/h)}28+hv#Rl{.ئg]hKޫu*,+R쟿t_:8cf?@<ȶQXU/ŸbpTvyȡFF "A[~t#֔zWXFq'$ME%t:py77ȣҵ`9O͛}5DC9Ⴈ:t׮9w'ri;ci*veOČPnzb(IrRg y<;5Mzٟq[3ziWB.ΏBZM>q cm#ξ7K3hxY޽7KA[ɧ1,f.d)ҋ_\sDe1sNq>'Iv+n4Ȅa@ gLZ6&ߡB%0|ңEMނ;pw{C#8k5`:a˗ Tl`ؾ|Z~Z6+땼jvPiuqֺqcfZLהG[ T@k 4>s%a(]g# YP&~ɭ~yl|f!f"#m7~C0EL1phN"bX%=u:1Jb1Սg-Lmٖ1 9QT7~4S>+ !nWJ!(TO&)G祸K `%T Yׁ{wo+[5қ4εgNU`wу ؼ5We|sʊS} Zxg9,6v*D^4+R _(30,h +'-e})4B`$@|Dj!@j_s5 vWBdݿJG.F^t }!#ok Vl˫+NcĪ0ydi:WY,L12+\^}BvMbs3tffNʓ618'S֦wݯn?qaa-丕ڼ,ܖJ­ؿ&m,8zGq>C&2.w{>& 8p;`?9>mw-R=di6o߰K#z# >VoHiNY#-:50G0RpP >48 8%cˎ $שpr1\DXi*r^(%B%Âؾi?pDtN54 \EG vkzY@ED%\6+Hr3Zo >&kÁ: cMsN Z Q5Tӡ@C-@B}ulVȀQFґ Acpnnݜ9d 0mIR73g#1%?Ƿ55FaN2C<2[zAtY}(:V74&̙6)t UN  !*eDSs`(*q1ڜ39 P[Zf(9rW.N{ "I2°ҹJL:`U״](}HBmޡV{[$oa1h}Mj9b,x5`N. 1afado[%JbqыTwqwr>2dD U]Lvٵ41eߨmjGo^Ƙv·(&Kz'*}Φ&=/BF+F%ӓ62r ,~;Y Hp>!FqW| Ϧ5HaQ)inB;_MO'؝q"oMB>|i/>w q=Tis2rI۽Ic>׳LaPg&Y K7O,Q'G}'z(}zJBX oN|NO\N7\O駁ӜOOn#щ<7PK!NFL <-poetry/core/_vendor/jsonschema/_validators.pymo۸ s}ׯ9utw}Wh[, <I)vP4_w>/$ՎEjYWa[QrOEh8LS#DNTՖ٢mO<[E^7SM z߈^^eH,Gb՞&oӢM-yXP鰸ol[.$X+wW],D%ig)Sd2}4\:6[9xSu+>ekh׬͝9ylG_5M+ʉF%4>.%xS1Cֱ'L-QPg5Ojwnz9 ٰz,FskNoze*쎗VeKӌ>04sͮYHaL? ri9y# h |Hq/L%b⎒ͼr!DS(XZf荼!=-o%썿DnXHN//j>6>8y] ~łE6ƒ74]EqW;p<"KY\$u"s8T$/}$$iy`՚5[4l0"e(`=&#df,K )W3ז~˳h*wVh4Zz^kSór*DzZ{=&u$;.;Xյ2j@0rƑhRVKKIPx tI?+] F?jmgi<,g]n[hU労XbJZ@:޹BuG-%^/9(:Kz9f,cͦr+[yxL,GNh!mƷoyq93Ȅ(r HMHпmwMN7zKZ ΪU1ƴ! 䚤NJ{5LubT ƶLjxʓQ`!MJ)T 4xdwh#&=z+/˅Ɗɓ%NI5ޱĤ:BmU<ɘ_ ?`N5IDT@<@Z 0!\'b, N^G1bMcNNFsWQj-BuK6K?:v㨰ːx0'~Rnid`*dJ־P#BOQ?`7?:Fߍ+QpSаO6k,h $G%,d1ux6 i8U?2ߵ;Odӳ%C~ S758r#=K 9[uT0P#CRzII6}%)]5gJi#8$'(OJYB)%Â}s+me2&F}ڢmm "{2Q*sm5uـ:i}DuqpC: &j2qro\; O?[5dȊcc|ڃб~ [^b_eV(Tbo$ҼR&37-HqKQeAz&_M60Q՚c Q) !]e",5\ 5Ưֹe%!n[ޥj)-d&oj*0CkU e] PWak#jv7\Wd2 g`UywA ..47= cjy|C//}DMs1T)v.K1M82 Խ.O4V,u8*7xr{EOڼ ~$owLqmsMђג\GXw\ma(Wu#rg-\Fu]$WHܐ-ٞ D|20cB$KD@Gg*snk>ny4[g5?{5j]/?gxy ;YsO897ǵDIp 2x82 '!)JIMwR'ijN}k)2\sbX1h ҭ{T]5V^"Lo }ՍLqi K##!s^@# )-g5.6@3"M)vG>z6Ȼ2z?Q :Օĭi79tXpNnܾ qy^.p(S-:._ݴ1n~w,6S F P$ɩcuc//p\aMZrxd:ݝ$/џd6<bnF_a]zʒ/, Rd9")E2*v ʶnfQ[0%*O^W={ dƽw eBs͹M8r .S\ikݥAk Uf&XES誊xF26hVV+^loFpnN{2Y;i8]E pa{6̕ ^]Q0H+P tWҏ07t')oʀSL]gAL8#yuY0%J(37ud殒aPId ZIj4оxC^a@gWA92ƊU~w!ӈ-FTFna}ݧAovmt{LN$o<=OڹځJomQWvfvHG<۪-&; <ا;kIMI[|u׊G] v+҂$ʣ"p_f둒# \~<[>T~,[飴6P @e .ݍgUae@EoK QGdhgz J>Vr"rnڑguXM"HDGU?$ՠnAmh&kl5V/WhcwPK!xp@Cpoetry/core/_vendor/jsonschema/benchmarks/json_schema_test_suite.pyEr E{TH."3/4[{RJBц7/fr:&!XˆL)#8qz~RL.OvL`_ލ5n8d\y@LH2'r_rs18,"vKcrsUVl׎0@*4{N`jMjVPK!( / %poetry/core/_vendor/jsonschema/cli.pyYmoܸFU\Ekrsg(2J[wf"RE.G"pf8eY6Otk#w ke'YD \ozeؿ߿FQW/Q'jD#:3so\o¿#;?kqϳQۋq>_j1- >v +qV3'fq}+1|?zfJ}UxaeW1*OaeJx̮i"ڇU++]-Xx6[5^=o.e+7^ܚ" ,_~0 5^s`%TȻ|ni*HlݪohKBm:[`u x:=#M֓-+KmZ.l$M̨~AK?G|9=&O cG(qd ex) CcPlas-׬"B<)@~#hG#m@׼e?tMR dND@#^%Y Jv˾P|>O(.VgY"1v ץuܷˊ\'4Ew!㏟tQI#r;'ͭvV\_ҋ|n/r]-*5N}؁ ت:jdJd釬.vT}%Y&%[v o ۽! #/⒃v 3=-EBY[vϰDTA%6jJܼi^D|:wp1͐Aro`qĭ"mÉ#]n:,˱AD[cW:B33TJRz+3,]g~ I~8i]2ew&s7MyJFWݔxQsTŀ:e! J`j̑6LoD-|{ F5`%Fj 93+pZP-2_j 4\5.&&{ K߹ŬӠ7&xպ3εp@k["i]khW.b SqA|S.$lp6j7z E`kqہW Qt\@ pF.hK .l*eR,a/pTSJdB3 OW+omy>VH~J,7 vFX̭E3@ e'׼K|1‡[eĸ$F3Y.Y 38Y kޝ+3(bSa.*,c4!ȏYW6{L ,XwbE_VE>)7k<^×MFjilL;Zam!a-xu7ijI}XKW|)}D^P]qd,5FVD5Nu%Vrڕ!;ŧ| My;s~߃kߨ3TT]O+Q"VX6b'{?.T1d[(dU@P侌ڗ4px`y ǑV^Zgµ;&ƕtԝNCuO]g9͇] O){Qe?PK!0 ,,poetry/core/_vendor/jsonschema/exceptions.pyZob(S>QI\{$Aĕgn;3.)NqA 1;I,~,y/ۆ Z7%m-`dhJܲ:tH`N5+Juת66mO¬ٶU%4bLJ/__avMVuVռ7Sxw#R4[6Ž~q?d/T߶0{eY?۽X ĵ?ymWW% [u›dɒp->\?k{X>9;M,ךoP雇re#HivKZ[1%jeNg:Wi6l;*kTAΓ,i3²=o bOxPDw"ɂDԠ$|I"a/!&C3 `C+5+|Ʊ,2 fisDN|/ ;HN ̀xΐ&,bO"DTʨ ^цi|fQHЌ˝`{<GS|Q;0i"gX\M]F&@QH{ %:O0nTxvɟ?:`1<5?] F}K6 k ɫb iI{ŚX2V2dcZ3 QDYĮFNr0ZDBG-vmcIVt l R|*tKJ6)8 "8b勗7'*{byMNq)? d1McwP;l"{e%JX|- lc*Sq:ICCdYBҊxl R ܣFx+;Oߐע߷VJ鶂zB#̥4^#ݩptF" Z$HCyhkdm|f haU4e%v}-0GBdGl,4Ng,l"?.`%(NbL2פ >t8 9Iբ9ǽ姬>q5K.;puFALntlJ翘Ϭ96Y3oE-.;w Z d2>LlY؈`#|!>a*$[ v?Sd?9\ނ7M|2^.>IQS(zu~ِVautf,ʨdh+X@}aO%#ETdk D 2#?sOSDi2P <axQb"D /EW/؁kAHjrƖLO N6p_/" R^C]k@mY,AuzR(H^ ^kQ^O;T%Սz/ʩL S&͞l/X Ѵ~\wPo茯ѢLmw`P}kȌ1~,{~B!LBiLHM LDMx展6n"[5A=%JPhxϏØGq̳ԟǒ'cE}sVCDMl%7/͘3?6~J ?lGVZuv}0ÌuWi h RBgHf58%|;v{u|up"+ShڼXAs:Y3>~'|J}cWdY\)9C66]nIPsi;3㟯GZ9 ]Ng>PMWJNڪX[kt^CXYioBƖY̽HB1Ѓ2*XBܐ@qGh4j{"+*=V-uv՛ fܚށ]A F `O{t*{o7[mz}FU3xѭ@nD$,LT:1ț[Sd 1WeAauN9ɘy0D/-6$V`Y6mB4pP3֢XיQOisTVy۞Wxg .U^]#k6~bDނwۨBk PoQ,A 묲V5LZh( P(R1wX,ۙUf O썂o4:߭*~=Iʡ;qd&m @AE7VJPdH_sZG%rL/x*a '5PKҔQHt :㨷Cl*hie| [ i4t$8^|bu=K1g=d.k al}&bm2/V2Z2l! / H/x:";$AC]'FdHikԵɈ%kحrWif*)Q?@֓K8L!MM|#zfSDURJ$H@&K2b*;(d}Bg֪3>\\]j PSg4j^ #Rfމr"riFkk/cWLaWxEџViaW7ۜ M4dN-&+؄A9[K+Q| D$vrW`AbP;qSF՘+ --ЉQ9l'APIm!ӷC }0x 98p` 8A A$3$5*.8S֡ïw``8ВU"a%Ol ni*æ,74č\ªd:{nB#:V`!Rb>24!O_` _3t]2ei`q*Xqx푴ofDk;yF,.=_g#,Š$͡.ώ,::DРyA6`u +T;QB!/$)m=H3 C]KѬ4dوRCA:٤8w2@fRŁpM> i;TQld=Fkm ͵A# HjPckq_܍Ԅc+ e=OsO2'mTR5,hmxBPK!#R +poetry/core/_vendor/jsonschema/protocols.pyXko7_U X*QBim@ jXKC-ɑ{ gFsdFw[kJd+rbařeKB^م̔Kz} ʪ"Sb-q@˕0pJK7n:vdKS$Mrl_\Lz5k1-J_Z5 , p:\b?)Ҏ5^uŐʩJ;"v/$S6^\/56rřhfM)'ess AWxvQ zi:E<9ĪfW9`WB:ŏSX%sđ%LT1q^B`b뵚ykJ풥_VDžڍekFu%ًfK9O$d Osr= `L #]w#2We$j_LVN =:.LڛR73jh̲LQ}6/ rG* Q5$W^ eSH[1VP#P_ڳq$y/P-F"6+WUknHNK/g-)'=k:[+T%YiG$h:レu']h`v˕(.#m(N-ޅ,XJH/2ñ BQČV s8.\=4E>z+> ]pDdA2Ж!rs6zE؊B` 8Vzdcy%)-Z**AIs^r|W+8N"98&^ff E'ln483m=-FM/TkzzAPϢya^qet.PTv@ (zU49Ukcw 4,Ƞvsc濨̳(U<^\$ӼR0bۭaTOԆ)+t}.\-tK2Q:%ެnZB[瓃Ы^ilh嵪9 p&]Nw(Kܼyx@l(>*Vj"=_شci1 2Ҫ@27b/849zkWPA $W*]Fְ`SR0rNW"kJs>@KQ)mrX 6x\EaC(!F$SJR7ʪ0%Mpn0A$V9h*>(<e)}pP˵;Ѯ:W}զSc4rؔ7 &5J{:LAM,jδ\KTam咣u$Iƶ!tHV\$jMi?^ݾyw0Nm:[TUUO& ԴrWhŻG;pniA/h=}ݫۯq` F.PvpךӁt=(O7Yݘ Nl6j1j:nl:k9|lOPӨW5_;Ǡv@vG-gLߋ?[np+ !=DVN)l)#"!h+l!ZiEu ~ٳ[s-?]{v}1_:~r?~rެAt>QM&^ٙ"Ӱ$plؽVؖy]Ԛifwb֦ C?ŪRa%r%V;8A1GOebf/rM}x2_8Itnڷk%d.*lv~h^R[I9lTI#1x"k1beZ4N?*ބYSMqzo7h!0}+U']NmiWUS֏OdcڅڵnN;< ~~5l);zejo6W?dR0MAnO]R]E3b؜|+fvZsF#p)w8#̦FmU e s%32M(^{mTx߭?'^6G:},ӓ#Anden5PK!b/~ 8poetry/core/_vendor/jsonschema/schemas/draft2020-12.jsonUr0#)2vN/!ڒ*ɴ޵ 8 ڷOzvϰEj.j WwU/vR|u9$e"mX Bw̵E\r^S*@^GqH^juODѤW8ʣ;Qw!*g!@Җyu2g;ŜA.&GX>7o)?FsiO;Jb'.9)rt$UmHgJ"z|1ԁn _pVPK! ؓ 2poetry/core/_vendor/jsonschema/schemas/draft3.jsonVn0=G_!09*| z >ȞBR C-Hd,A{xޛq|nȝNw)W1SW-}g!v)㢳ݒav1 hAr mPet1~͇CQ($ U:i~)طZ7~U.SUBC9-i1THBN3XiA)({ctr$cħƀ/ߛ1Mc ~A5u<ύ9o%*< N9e,E D'Øg4H!&rjdh͕#@w8 %4G|QOY~Rϥ )rHї%V"m휌(h9rB %**詜U0|*c`Ʊ=Yt7{r$n#\pj(JO:EȮS3%ˬ`:=,IpWLwM[-Wb 2Π%"VQO+ǩ.]풔LIB-ݿZέ7kצ.E;\PK!'9aU2poetry/core/_vendor/jsonschema/schemas/draft6.jsonX;0+<\spe3<.dXl "c=O+ 8Ji߾W\KyxOKTo?$Rg8{KNRT` r@ !**x/a~Q}[s 'cQQO+Z_Y"#[^kkCq@ҍ}$eLSGb"qbo[泍-z?,7̷C%1֧}s@p{!b ][/cq@a71sʾT qAJf!h"`.,mmu]IHTƲ;"!1xLҕGP6h k@YCHISsTAp"RxcZb_VYΏSy?,en#iwAGwu )1GH=oZI#epkIhHE[ghgQuku;U9653dlGHl8@pE(vkkᙹpHxϨpH=u{8𶂁)uV*p~&馄.@jg;4͖%u2_gH_n\-|Q+Hf͎:SP^5 <~$Enï7LZ9om2@9\(O-*7PK!2poetry/core/_vendor/jsonschema/schemas/draft7.jsonX;o0+ c\SlE!@hl3I F{I)(rj佾{djSnvloBY}Y*ҳ*vLg`KӚ8堉n(`IQ}uXjoJ]PkrRDĝn} H̝[ VVU2}?0!fNhX`&~"Yh;39G<{6kzѰ^0gl/HAT.wK)3 #ᡭWf~Η\B1Jdsfô$`o%4eU 1c/̉rcɕAiosh%'OR*63 D c7o# z*9OA8BB*۫=VAS2zJ(r裏qEP%W }b]PQfFcOF`YYx=su~+h9N \DsQc0[r5ӷ z͕ѽŽ%VhHv ZL(CoCᄎ^![̻s3:BJ 4Ayj "3Vo%S@t[IpLy+"u {|,)O~-, wwBe@hCɦ2}Gv:/?aHO덓5ZjA݋h㕖{}u<[V e%bOJj'XzJD̜!7 .+%Bkc}PK!H5-28poetry/core/_vendor/jsonschema/schemas/vocabularies.jsonZMS6Wxl ^aNawHfaIe,L{%C8RXS8X0ї<%<\@S:Et|:`haqpXώ*`p#w6C`OCtWĈ.nR$f?hIP .R*Y w%SfaLPNwi"[9iaȥ00* Q񕰬"]t:O70ңJ{yAb=u=cnc,!b)[hv]2Hqf)g?ў4AlIb8%uzFGpၒPȋiPi-n$}i{F#\/ь ?hIF0˫-eu,5d ", NžnyJMEsZLc@ɇk_^] sxA$jF:fFV<~@) aKrS>"tL 8R5vdr>IAN`RW׉CxwDYbRݳ>xëmoݰmB#Cfse%( B,5Km2C. (zO7˓Pŭm2I k}}{ݝ(-K[TA1@0)MXIR^7H2 fŷyY[i o훃eު0YUOTWO,>uNlDw jPΓ8XN:k'QoM,(,yr1 |;óϷoQG`NYsc:5_*q.|£=}l7%k^ "@\clmP_ muȵ HF#w zE HcɈ׸V[7]H"['9[X<0~ht{1R.8w&q+XgC} 'vWwh`_@ʰ;Rs3ѹ|fUȲxk0@;<%@RޝLUђK3q\ۊ:hd}Yhx0u'gY"iQ,l|!¯dA= 7/N&^T/vໃ\mW U`ë;jfv{1l*wzvv; n`V.u=UBp7o7^[kl;cm/VoVv{}n_vk]>ݰ[s3[5 [w( \Nf@zoç;p+ 7(ۃ6>}1+Cm9蚄(c:]GQMɎKC2'cJo)EQ着B,x&/*?xG cg]*=#o:_#~PK!Y~I,poetry/core/_vendor/jsonschema/validators.py=io8+FQNzrmfpY, C-ضjGF")3>$nX,Uū8L2mi eJH<?$#~{t ES@o_TIہ "Ɠӽ=%s"$*ӵJ/1ɋZ IorHC7%M-i+XL hnY#Hs>LJ&&536G4E{!i˪F,[* U|@{F.Uέl6)k8нt8~%:IKXۤ>EOR2ftc|Lxaqn=+c7]sdDXz;Dr&htCQX]YMZ2AZg,IB+yВS\ND9 c29nV_m)lK82:^/8T~ҮS{0^6?DJ\j~pq\M>w~dIۋ]Y!WBgXu=s42VkQ3lzUbdjRpdjw]?i7g4ly${/aMvo: ׫z'׍<,~jf3"oh3a *sX#Plmߖ@T^̬b,DX#To9uιUtӛ<[}݁6a+ܯŭ%xNmKƒŹ(6GfpjĨ~#GE\IW56o PUKMo >F ޞtv/]Z=Lz:*+&Y퓪-]1}SnFO(Z0h݋M,SůwYul-XZ̪qO(@*P)v0V6"mzR ͢&/ T('DUa И=T\ŷ Z!3itc] deHo&ŵȞĶo y>L^W9fhL\(jf%p  ݔ3\l\^!3O -W!֡IϼDҙ.lٹkromebR~6n1})'.UUyu^b)ǂKt=~9h7#<Kim~YA䍆Ͻ( ?*W[֎b+t9D -Lɲ2SByVX=HUS "D N3o Rźohc iUts6T{o9Zji M-J9:ejZATHF`?Gnn>} ?kt<6I3w1\W 6|><6lR]pa'yIֱZYst}i[ևw cKp"]"=߭5ڧlr;Oo>J^h?9y_RoFs{{ N)WuP[xG-@pr.xױO?<%xߴ~%%y4MrD5Q[nYtM~%앱Ż _F'Nԣ~Рݙ 'J.y_#޷0{NwwvΎv|e;3wgCf4s}EϲuoxˮFwĦoH 1sJJVd,GA1U{cEU3w|SBfG6S1j۠`] X.MwFK׿u8&o%?Xލ${l0f2F1l #M^,i1QG?F*ju&Ձ"/Sh$ea|(LF+_#_qou7pCkmh;zaTHZ8@^Vutm{S*(>M6Q! I-3MûrY o෰¡p:rgXkF.s0^Lp>A[L:łLXM:{kG/Fr$V5<.eʗ( |pPkȓe$ٽZYdbNxּ[TJղ:?ǘ)"]f<2PmN^7[}gRw,9 KP YAcTCh;ji?&ݎ0Hi^pJLE'Gs)0PMdmtUe/N0"a,.hKJ'YF| %[G']M|*Sv| N?:M 7;Sf0bOȠ Ӊ.Hzs3:xD jaޑ՟Lϕ5ݱ'A:aQl!VBf]́VDHn殷.۞vKMt+FƧd֍&h<@OP4(0?]6սSGeg}.XPxu8uX*- Q("oFy6~ړ@>jAA!45s'R\>r~wwA)H3`Vh'EuץZs&4kfeǧ'PBޯb%?<|}_m_PS&›?W?_=g0PF"֙^[-B:!Oة =[LwU7Y((V`mq~b}wPh[ Gx?nhUy_ 1RW/mk&Lp td[,P.gq #^Pr "喇TZ./3r: RM'mОQ)#RP7~6{/CgUROI$jdrL`3":@$:BGYsGȯvh<ĤL̽,/xxEM3N[١@ay0بP-9ƀ#64n7Y5 V4j*gUU]vqnjdGhazS!}^KG8t`\feThS.b8\%3iG%=o.Ӗm|# fc_JP_G?{5uL {cfC[Mww}&E~V1l4BVOL⪰<~'qܤ T/\H]q*bps,GsU~aU*&SlJmbeVW{{)(e2UҨg^:8o2fd:n[)É  =0̬)˯WLۋ| _XkBL;(E"/րHҽ555 un]!k:v[nFa)%t`V{*"{IyzPlԇ*<6uxI\ PvҘxP-&}Utϴ **zua gVi^;x> nZduT O JOPO T=1\2z+r܂}Z㹦Zl4wCs_Uj[;իvkx{l)P݊sbdj._X, d=+26@a/k?1C B B+*5Ↄq.ggKmW4%Ggw߅O S t*~r4At3#fft*kH#$?i6s2L@UMM PӞdʼ1*0F}7r3~TT,$FlF]Z! yi[6d~A۱(J3֍`=f2g8NL@7I 8g8;ڎv٘077@ ALK 52\{w4gZ[K5&PwtW:~9ƣ hp~=5?Pڎ8z4QjxfI -9TJ *(U$2UX/ .{(V %W*ĦǦḯ|%BC.6B#.\WĶ*]cɖ"zJhq*0("LAUeQq4!rPoTkbu^ǵugK20a4gb@6 *(FͰݚS #KjQHR+,LٝxLRNq!B1)1To+&,GBW PK!6vY$poetry/core/_vendor/lark/__init__.pyuOj0 +DO@{uc01]I4Óse:=='U.F6"Q뚡I`^cƚ>-G8+z'.EMeTZ4=9)-Nz-31|qA\Pr\ W|F7f}Z.A+nvix@sεLQw cBd22ϖ%cI6bW7PK!ˆ2poetry/core/_vendor/lark/__pyinstaller/__init__.py5˱ 0=Oq&{@pEaMh +ڷ w!c_4mVQ̙;D&smO8 W F QuD]>6jƤҸ+hfRa uҭWx؆a ) PK!o.W3poetry/core/_vendor/lark/__pyinstaller/hook-lark.pyQj0+| q})zi!=^"H<>=F'igvvF[ '-CTŐw"Kt 0ha{whV($}\9j300E`jG| Z6ZUP:E2/E1 {>G9lG k~ךAkWm@(0{)?e𑧿j8[OwAZq5*VBQ^.D {zCzUX%?]|ޮ'PG*^ג,hiw8PK!f<=-%poetry/core/_vendor/lark/ast_utils.pyUm6_1͗ihplj˶+KFo.3^>j-͛fɀ{ VuNO/2g{NJP--tG;d!< &نbgjC2'%g'G#*k. Y4q.Lk>:-m88ዎxUZC+ZSyM@YKhw tH0'*^$u^F~AxBє ^Ro4HZK5I?JOU)muA#Dq'-4M$if$tjX89C?="ח⡄J+sb޳&K.&)75^\ur Bk'V&)P%9/’9 9]lYihZP m(W3a 6z\!'cm`X~9D Ũ ils9_y/o:Ast;zKIQw΃(19k(k£sĕӥ0&H_PK!hw "poetry/core/_vendor/lark/common.pyVk0_!҇$`B^F v F0BqADfNXnҥy%~w'jKJU J[>kfoʁӢNE!V:gB̕PZdNyisv;&rο̿}eJO>LoZ[xJt+u0 }b~? ("gd>'!n̈́0pLтt1W|ݽJpfA3Tn\DOМ )kJ-]C ,x2JB{ +vd Ɍ `Z~`Bg߸Y5(VP!\rKȀV?Eq~SFUrfw8-y9S_R5̏35 Jpܹ\P\5t}K*9qCQ qfGa QڬRqd5Sh u  N!fG̝wck ѓ~c;~"V8`wZ(`W`k'ؔ;)IMr63>8C#6 8k_J F^_ںE4SWbx9N4S +^pnlo57FyuY^8$?\PK! *&poetry/core/_vendor/lark/exceptions.pyZmo8_sX*Oy6+oئRMKtVTJ[!)l7)΋m$<i--T:Ke!5ˊk!#W.N8G4vs^o#R W3eCy.wȁeU.bR#^?oK xK,6b~{ILOI&t]_E,TBI3Hsdv 3lF@By>θR_/,dpq *4|0|=+uz]Ize>tMĚzgFV]/ ΀+Hʊ5;ScKJTXSӼE~̑fj+!NLlϔ0I:"NГgeZhE"DE"Wq"6Zdx,Fa[LN qǷ7^rv@#scs\Ysuـ7oπ9$%F5"O٪(ٯcLʮؿbWs/=ܭ(!\ cne0o J$cG%v4OR c9MlBedoDҤ~H"$|.6b)k ɺ(rnheT?J+#P(I uN59@ ZԒza\yiPP5"Op.!n8+,`[:/ zI[6TAa"g&8T~Z8 \&ZB\1c S5S##"[B*_(AUPb-mRH2U5x ̨]fZ%<8:ƍCup-$†d &Ռ#Jf4bysH"5/h'GaBaEt;hLĎ/Q+vl?-wmw jaoQ+=LSik٬evIp0|Y=?1Slf9V,ppafCݶ & ;'vmUri| Broǀr= R9R̶])?AfvnjQ#SfkD[d(dԢpRoЂsdqݥ?I6`w`g<>BLjfO"IKOBdvG~"j6t!wY?s,Z ZAosoZFB[Aŷ&R!gU^ sq8 ʿ'u+ENpW'.!+@Fs,)ڻ;UoWVS=bO|tM=[;p}{7Emɺ[5>Scפs]+dDt9CȪ j5j{h8U/P\T}. {4=K|'MZT`g`'r@ BwGq0eL"^/|,~bpó_12á=aks*Nw5iJ*=dmR- DߜT#^h6Dͺ:MȠ2T:2`|ax`jaIA5}Ib_5O3p#|,,TvxtM]857p ymIo ķeo`c|A5Uѽ>Hh%X{zC4 ",>ly86\z?ɜ#?*@q@V5c|)q Rzm VAӈTNAO>O@,@m%Jhmөɸ+w-u4rO1llh?JY/],V^ n arJKg5Z^U3@p9P.u0F71Aay? >'kQ5ÂwR p# dOYڀ }:Ui>FnSsՇ81SCHNY̩j,οkYĠb^a9WOPqӇQjc~i1|5=P Q%ҋe36rGpz0:ALRu;Uq^vYɌqxv۝q^N:UX ?r}wzzPK!Hc #poetry/core/_vendor/lark/grammar.pyWmo6_A$0$}5fHC}JҢF@}Q,%V)sϽi<sx{Gʐpւ@OZeYcGÅ?A]^^Mk![ȶ?޾{_כ뛫"/,YLt,E1MFØhP9-3'rO\3갉?}RܢiCм冱BhWШ~* JA!=r0C"yᚷ6;( "+0Gzo!RU^)|zV V4"H>@ZaYtj._bʜH^mt1JQ0U`m!ǃI[^5wbhD7\`Q<<{ 6hZh(`0DݽI}HM1x:@ݴqƗO.ιK_"aW5ri,G3gh8~_tyt-BQ%%F=MB63cd}Om}+i"0r*fS$q+ SezCt,?8ݷ42u6j6ƧI-BϿt|7{~$zsܷq:tr24[PK! $H$-poetry/core/_vendor/lark/grammars/common.larkeS0#SAB,qH@R`U)i#R{Y۰ 71C^NЕMuk [[UjxmK]RqCZMl][ s|"M>]{d#VLz͚!XA~iYrKr41; axЏ[% [WK ntCɎkytjss %`Ąí:" k 龫&B%/72k5<͒ ޠXШpg(Ksp|48^F{;Ԡ" rI~EU`|/Kr-[SyBKY,Ԑߐt˙Gl 'ҵ6Y9,K؋WNzsC&sq'yҕT*hDžA,)i z3ox}bݡ1ąpmZcI)>68'\ f85& {^YP[-qw?PK!}f#N+poetry/core/_vendor/lark/grammars/lark.larkT]o0}G?X {UZ%6 (ZԡRo߽6$d\{}J&-8$iST_r?lN8V׾$[!EQVf̚c>oEYgUY`xZyLؙp ;cN*cMO2no.﷈ Dz"8񛜃v>i]l+0XY˚3(ExI0w˛,9Q=RfrTFLߋ< (/"\ Vsƣj1napٌ}DoJH A*'9M7$>VQ Ep/ L7#n*w+<^:!RO")UFʿ?Rh~P *@o~ţHP[H=G6l_,=pñ!-> [lAt\01Waד7J gO~Md )aD.&\ۊm]ƶ#7̊:=+,V$yT㨦9 ʩ-._Bok\R].nn`C5cr`Э]G^jPK!-s #-poetry/core/_vendor/lark/grammars/python.larkZ{s_3:H"3\l1B'Ȓ'rP͉JW{DRC瑔-Uʰ&q/&s.unݱM.8"|(k=}ә@ 0(+4 ٥Y[riSR3d)p]`0} EYxR=k8Yhyb= !hpp3lbxt(daΟl!CAP! 2_d蔮kΡXc 2: !7lzPwⰑ@,Ӏ1j)9ôfbÙu[Q4K"*fBD5/dzz .E ^edL$$lUBf2mfTnd*\q9#tՃQIu8 2ԚG!lANFXV 6`G0xj2ŜDgIea# DB':,]qӱb2Ҩ2.t9J:}tL4@tfycپ̌"Qƚ1x@ #"F'$I6'A7#rOʡ|P=G4^8$>&6cFkC~Eߡo$EY3,ʥзqNa;/Wcu]Z"x"RqlNfzVe78WH7/E쎮HCY":\„ ƃׯA^Q JnfY_[1I悁*I3)ti8xra$0 MG\ZFq`%a?A m7䦥9{:ÿU mv4Z͠;&MSmvt#dn#REF՝9:L?.N[F#c W*4 R8H@y|!n52lTIb6o==" +ũ.bVuAzA;ފ^uhjkwG4q|_ݛ/W9=< w'?BH\}'P//~5d]vSa& 躘\7bz0wJá'WXǧT%ZN#v-+m6in"tzp01~MڛtTV=IzW{5~#i i?vwn,r'z2ޏONZ I_=ӭ:zֻ6wpx9ψ0rw>Gcb1&Rgg PK!Z7[Tg.poetry/core/_vendor/lark/grammars/unicode.larkWwRq q QrQH-Ia̒TPxp~BLILEA6HI$&-(&,PK!, $poetry/core/_vendor/lark/indenter.pyVoFbz(2t|PI/"QR5k.T쮍_ɩ7w3)^*rt"8(/ILĵ2cOO\MR&A Fwb 1d GJK-cBl(݃Fɴ>b̌+ucy,%8 r\fB) P{:J3byqʔ;4!Yݓ` 9"Qwȣ_0R%\TXK2W(Jx0]py!r1~4CYuAŇOBb= +?yP"R©uߣk$ ],:,q%642] #Q$Nc9)9~*Boy}4] {0%tJ/Qe6I4ԏŎ>?C}-/^5U;cKϲ hAM%q= *} e/? }D bJ[*o ?yȺAK5w]3X&[J(C5GC(nBa^(. yAPiL *5wElpR@9w U péT;Jf;`ٞ?_Fç8fskoJG0h9ov+_6Scy3*f9{ m+U}RUl'Z+"WM`'VVst~f-[Aw,ه_AoPOB"]9>1)EfOGrQXgG,qMljOrTuS|TNط3#zRܚŽr㫜4[f9,s?VϷh|NF'=jOϾG^yQQ8}: Ǿe4kt?%g_owںq2UʼR/};[v%8{O/PK!N>g poetry/core/_vendor/lark/lark.py=oƕbOԆ-(SZǻċ]IE$#)JE{8!%r͛7{̮j'U*ݾ[qENv'mWͱ E]!CMmV뼐gky`3^=CyӆM_N ]$EOe^ڷ7)BIA a' yy6. VE!Sr6;9"}R7n")8/0t1Гo> D[Kkx༿ϛ6uR6)(94_@b]o"S(DP?E1Dx*O5.V8Wbqe? ԡ 哬UI4CQT Uś:3 >$]Nzc|hd#eSTq~t8v4$Fr{? wL3=| ŷIw9p Io;Y9#i[~vc[ff9{X,iy'odq jL6zH*tOx 㐶 ~< gm}d56QȾr.MYA+MqIm^xM,)AZUaPuJQx,kT6Md77v&^}fe.U3zɱ'17L#>X5d2sوv+Et u< СP@jf.VUZe4Swou;@}AWp9F!vq$ qKt+G:Z:m71=E3n7"MR0H*"VwdkG ҕ7VPLjŽį<[ĒM;+p2o1fAo ]R෼eɞ[KzR[ta:ҥ6njPV?zJeo Q ?DQU)[f<z܌Fr0%W/~BI^- !%Xk^9b$\ 0z yHAj J1VTkqw(fl0uԈ˹Г 7:.-@ u"!#Sʠ,a+KA7\ՕglR<ۤ.$8Sy+6D,`"=߂u/7L 4aRweQWxAj sڈ"K,prWl}]"Y PPX' kyOh0`RѠ0OB c;GSdIfeyM=$Nݒ)gÝ $hY:Gp/* 58U}Pt"⼯0Hϸ%ECZ=j+aJ2M fe7 tNNH dBˑ6@"X1[lwK&/.(sGq&CRPզ/HxA4C7Nv`QH9B %{|* WuyAa3̱c{pQFQa,@H>vdtD~`H<\:fQZ!OODy$H$qf%XV'Fv}%+wsq*EVX !L]+ &s_8BGZj!٠ U}װ%Lî:d2Ѩ?9AC4G@kl%LWlAį -r}W" \mkV0B<W%[!M:ꭨv)v%bnФr;x( ,PGxRY!-ހeYC`n\>*CfiD )6A&,`UF9oAOMj i& 0Sf:[8U?y"Ōđ Y Kt=0z` }qZX^RƾxclMyGj_z@"PqRvcT^R14~.aUr@?;q:˶uv^3c5Ws[V|+|f嚥U\uUC{o]?vm?-, MLD&pF?sX*WzmP:"ccJBwaqqu{)|OZ1E}@m$Nvp7gv^(Z8M}?-Eb%yu` Bx Og3oJNO0Fp߹`zeA[,܅|Lv8 >#1\y~(@K y pr4!T"Y^SvXU`Zk6iU=ʻz(f"^#U٢6d̨jlY{M7y$*ʡYk/g =CS# apSX%:!$U"Vm tت5m !EdͶBu'wլ@%tvqO'Kd &4 TK;Ocҍ"i8pg͞9ʯm!mn& 7: :{ thCDط];Rص FzJc5V?&?L)ǦcSt*g_u^sr_CUL)ZC-&toyhLPy+]e;mipң6vIytaW*jC+/[N6쪘an 1Z Gy@]e6k2-W%@G+F%n<@(7fҪp-総 ,| ӸNj~v\jEDPxvRTİ2Sr'sFvStݍQЎʛ+ڬ{JaBA|EN֖^V(L.DӲPal9G#[S$(_O#p?jjӥIp(Su]nׅNor߬# @ |S#D{򚓜ek3gP9ǟ]TnZ-h!ԅT]_~q|6NO;֕2ҶXe(>#ոgt7FGyIdvva.mBkXKaȆԻ]s(ȅG4*uu<7~2 ~3Bo iT8$ŷ?X}u`N|~!zrܛo¡#4,*Ю<͢| b!Q wG ~Tꌥ2LrYMD՟+#P=k`317}QO!h ̚2CS$jzprDQD0}l44& ld'04[L8 5ZAXL S#O& ?4ؒTPtz|bzAXA#05W, fQ>XMY(2CsȜrg#Z=XdqXX ׃'֧ǘ0gu? ͔VC ,/+GaP0V5XB^M_t%C0yg4p$soq묂75n&N |BmU1ym.l[vWܓa+R*Ēky4؞WZy\F:;p^Kg}`yrNdy_/j 9 cROZ$vN=%]S'gFg8ƅ0M^;.!bjK5,N2վG?%h&hWҩig_S@ HLTa ’N!T-aq*2 NSmn< FPq=oH mzhGNX3氢̦nKy!~q?eO/X+ K1 P*']o"(EГ.j 8-}!=i`tԐ~A„b'8q1WrUL4j˕֨Jɟfc[(1jHW4=Y ! ]g*(tMFg@V!Ȇs7 7 J9sA@E5N :"43?ɗN٘(.CΥL 1!گ\b:#!(w3_CϿ񝭱600M2]O(LTMyJ=2sބi+ԫ4 1p+d8nnu vl./44w TU(%q !I夶0$`!C~#_ H%WxCC4GۀX`z}'ɨTE',NиU'ڰ`Ě:1=$ŝ'nO Q(mpp^ :` v P:dyFΔbxW9]_o侖)Q'_[?ݩՅސ:۬:w{UE΍7ԓFntK%n%m= |t't?%n>2@OesR)P{  Fs_f>porz$y1*fb>Q>gr(9[W\YǨ.s%_m̢0 =Ȣaס#}yldBVY RLtp&Ͳ`Oxu/09F:G]RoCBoQqS& Hk%Z"gt(zNΆ-Gm Ӱ8ctZTu۠C?o<kf{eF#z`of8[E Hm(HdԒRe<ހSO#tP=so8 E/'I;spċnie6֎;'baR|bdzX-XkPVtѭ#W:S=oȪNɌ Vg)Մ8LkTh*}uىb,EV 7>KG0W5SU|cD'OL\zҽ vLa920W=r˫i)!: GpPv:-ol  6~3tww:xi#ZBE8 =vqJ*xg\lA٬l|Ư 0aѻ4 uN(tB޵yn} #SGkY3 {s9i:Q-I >7fͽbw,@թVz%QFϨ=UJϚE>ڣQ}c`[(բ ׀p^jic+Tt{ּ:2 ޠeU$wլ;@=ދ/pL¤&Nޏ{m{wѺbD# W{΅V(]p99ft7 ՋEG1|v8'aLo͙0aZbQG瞹^^=w'RBlI!:mF5]E oy&3ҷ*ʬW0 |i!$R}q]] |Ó6\\~P_f$B%:lbٻ+x t2Dr?-FNI83`qu^)uƁj⯱SհSf噾Or*Ђ?ɻ[ 77~Oui~#W_y}uJ:DemW% Uk35{hOF,!oi.I1mҠ'(a<rh%j챵[u˹;"5g*ҍX@v=7~[a.ss55o윚'&6vJw 51[bzWsj~O\6uڶ0?tpAs;#y /c2~HK* V]D"/p v%fNi7Sɹآ_:z403ORr7A!uוh_;Vh4ʌ+W $(c:ޮ^B|-xJ̆NI:vUMތJPDvB/9m&מxuԊ3}57^3"Q7~v.+5 8-H1#^CF2k_0FĆjN{kӝ`W8R 5EV]\yzLSx@x7Gu [u9w hU+zw=X?%I9$}CK U19^;|{2|ugaW ?Z4e+R6/yrɺK/̪Gp9Ǫ2\PK!6ZI!poetry/core/_vendor/lark/lexer.py>]Ze]YtxFL0/r/ oFiV3ajei`%QU,\'d,$=faX`Sz8DH`hMJ'$`'F>q8HCjē /?\nk[U8 p$xEGb huS71H1J'$C4) Vt:lo<3 >cՎjQze&K#Q<+:J۟vM:N12 {/~6\kkjv#G B}we0Lx4ơz qp#3L^WȖ?¬q{lm/4+ϓ''_]O"\(b|gʷ ;VzcY._ Zut](aO,Lq~J:PĬ?q3!)dQa@4b ;R=zBq\?>:@Ұu5H Y8ꒀaϙ 6'֜,[% Pq'SE~E8]".8jb(8?c3/  uQ숢ޣ=4+V1|E`Ӣ9Pd87yFT F"wؗ -7VFn0gXE V8f%V u;R5B`B4A-* %9!y{ _Q,  mM n-wusMJw\^ /,`?޴"bX.auffҩ3:x;F@yq/مӚwA_gQCv5W$VA ip~xMOe]/ڐhA L|s5| ĤK8>Ǩ1 ^ jΗ'~2͹B@2Ӏn2g/rY,!9jkFR8D&>LQ7.;xznͮ!_&s8'~du<=^gA#3eqx.wALL#߄Џ [kg@2Oe;u$H!(E2#N p y'4D'fF *A}6 {NbSۙ,'VAuC+Vbcb8!!CzJ: Y( %ű\3a~]  /k-Aյeńa8@+p]u𐘥No +`w@'+YFL8/v"h<靉G&f2y|ax aF[uU;yd i4a^,7DV/}r: jtzndpڄX6Kk/YG\7ΨY80^É4$FikO3?y`tt+ưT֕g=՚Wf^2+cQvPL]5AMs[,r@!ZK(Z[9R >{ zG b'&g@V`w'57ڐ𡅜Q|.Q@q`U Cp}kT t,񷚦b`рr|ⴗ(BOU)Ć"_N1U%:GXF( -Xf,xO&L2=u?n7wRo{<ÈZ7lgbdgyS҂5#b:58c_Ja- XM8bRg[d7p\-18K֤ҶzMSw1H-$ۓŦn@ʅ[$K-p5>eZ*blekSL.SdMg,i!IoAƼTlb+8G #O$<$_{rfgQa4‹]Uftr\+h Z: Nҥ11p;٬d) 09!sFeDld5~.fzO\6uN; 3w&ṫɋUTE HWwTukYBkG5V9"Em3o>SzéMX9 VwSv@q"էll/} ,,V #]_u-l ̲m$ A޶PQ7/'O&7"ҵu# UmUEwXΗR{qV "G4'R'fݳ @,> 8FTz %lG%`HKYKGB=<549خlݦ(csh \wf?8g]T~ى޴>rsS#Nuu:GJc80a=!Pt Ba;Cꮩ?+^i4CSа?@k}w6pq.1$49=-BdiWݚSA&B՞d c+8 "I؈ɸm 9oH-ԶlrDqm A *u{K N.um{`%6L9NxF|bea8+&4:=?4nRlp58=RIou LYj{h9&? ^z1ch)+tV٦;,D6XVD FltƼKmh֋%:Fr](ÖPDc@㶈T =dX[*R c>r`*8TgIфWWDkˍfJ%d9z؇nŖ{)*CR +ٮ[AApb>ҁ4(m G% 4-cix;|[%:3 M&K]J4C.4,I5o_ T! ϔaKȣQvps{{>k](ZޯƀXIg$#G*}䉭u嚒I +WQ}:,3v܊Y_@] Qr[3vә_s{ۅEjF 8_CIYwMtxD2,LksY8Liޅjb]͙[[a~XBT" :+V@G{Ŀ~ח%L4mRN:k;#KŊ:k]t#Ѧ3$ta 3zf+< DnRȣmJ)4+''FH&Td~M}ʐ,{(f&N 9&1A; W̵I$@zcA]YvP>qt zt`M D&*6i|a [uO{*Fŷ0ӅiRlQsIb>!͵{D,WںGoFsk6,gˮ4rk;Mv)L# JL3WHUIZ{:}&K"LɡSiZQ=*.:P4BC^݁kqQ-{ 3iocjC˹])9Bܛ,CC?UtnJCVOՕ|zB̯.v f|%yr]ƌ/~8'PQF"z<``٬U5]V~L8;B^PUn]Ӯ Yl!VQCѳ/N߼<}yOAoJ?}r3[/uW7z u؉;n0CiD/47n[q@o={'7ItG7BR@-ۺL|CHݬajgkF A~ MZL/{$T j+u2]tcL e;oӗ,_b:)p|IztR!6/еͅ0]B Yzfq8U4%6'!)?&^$=sϹ:ݱDBP( ^}V7yru:[,ZUӶz[bYխ̚y1?fKmj|5l+E>kWy,o;>T<_7 mbs.5.=/ڼi~5oZ@LiVe7N{, uѴCu |Mb o-l>Tro1d:59oj^]\PMY餪bѤ,EԊrM*d*fγi[Ղhn'y(PZDC5mhOŏܭPY8PHɫ)pmlΫz'~9TWiV_.|?ϦwOW0ZWVQ΋2j_`$p^僭4Ź^hV8O4=/y޼w T?8e8OY4}x|z}foޟx&46M񛣷ӷo-T廓 woG{ѣoǏ?'v~򄟼>~=/O?}'u^>?;k zsxL߼z}g߸7PۗqJ?} _;>O_~p!L.(>ݧxZ?1?w'>}vf==>9zxa}R޳1S.zkPǯ_-{`*+淭Gf=eٸY: бy60ϰll˙g^9@~JwAxiqS$O=n=;Ays}a}<{ͻGIUwqaU`zAoӊ_ _(z>E {ɧOu߽O`yxn;?O @VGMb~<n>CDfɓYTXGO!Iv0~zb oeUvtx||R^VWy]3 VvòԅE T "-6"ۏo~4{̓ ۙG^AqMq3?M9EjMhT\6FMMTAux6'{:]T5Ca 2HF-뢪VSkRO 50-(ޙ5\0 -&M%Ե:se;*v'ftM6k)RD]Ru~;ey5kZv +iKy![lNU13 ;雅ECm#vpDZ} ֎ (,dQXWá"ìˣ7-uikN1lW-D*t_ʮ4|E}í&p1- .fC`f%*lQ! i᝞ <&>Q0Ry4) ;XiY60Zx_ eC HԻt_#|;8- !rAE'"umBNyZi/k@^uYӨ:/+T MD2`ZagQ*4ϑei (;c)Įi;}/M,)Z-=iSw`~7oCn4CAf~7TS$ŰNsݞ|$vX 4 gF*WI^\҆VǢl^+6FZחY@<lZU4eUղGC5YjrVhGCn}xsuɻ?{hΛj>Sח9_:_U(jGZZy-ʹ{@IM/R5E9M=Wdos Dmc9b VMr4b(; oW9AZe>Y]oَ Ch(xqيW5yg+uZb6#h.q7:>|xpx|xOaTܦ^ xcs0|m&M>?e٢Qkhd.)'q-43iԁ\% OaE.* M6mA'ulQ*QTĸzEFdrL%J6U$#rYW[^wq~⦇(ceRXvf0Oqtgl BhL@^~^໶@Cjڿr`JdZ!?hDߏF 7rpv6A rw12f* B1V+F^y h4:u\.&/+ȱ|cUiCO?B.J;phy%x4WiUûeUАiaQ" oĀP{!;3M)qa}(\S2>VŁlarN3BfHx?R]"ݖ wh/R}C/Q9 LUcaa|/jK.FROfw6}z?D#^;=vwG=& 3 kl 翢]C=adZ>;pOd3YZrv:28rD Oy@ sVn8UD5#b{0(jy4( )hxC/p ;|=Y.D|g;BC0{4o) E{خyɦV,4%ٻQv}eQ#rDXrZ@\W߇Ј YRRNz?f3uj m۳h{&Gbo=T=pҫ#HZ)M~Μv&rxZQvHPBMw2{4j'^l/5˅du6P!F֨DܢNR|8!"9q,]A Gnq+g3| }I;0C%#fӞy9sE0|@F2u> Ăb嗌q y_L`!CDA('4ubc-C:;K />CxSyqU,{%`c9gm^+*Np1QmԿuLZp!AyfP$Fh[JR.qAoQ>41}pxʯF([<ǠOdoq?B)&mg ~R7X7z9sk~1C]b0 P禫p B~;vA6eՀ*Y.*;C[!{SJ{l87(1(Z* ]TFifZtlpEt0Lh c_5.ˋߝ=,6Έ$Z >ou~.՗(-ury.h:HsB}ӕh^z3r9q {\cs Uf!Q.s 5]@F#QiY, Βt#5toPpn@ThAo+O ~Ě`VPkMq;ahhSc鰨*w).1qH߉ Ћ󆼼1n `--;afvʃ>ZXρ]_! .JtE %"/ېwqVc.)PלTrwom%Wuq8 ˰7lQA?Pr޶0 5-e10ԉz(%ݍqA=6q{Ɲka`m4NAN-Bk9%ޡ a?X.'Ь_O:OT-Էd":e)c y?'zlvɚv 8f9HJc3"eZs=|:A!CٮrǨOhUjvV&J4h?jPakQ09ƃ$9fJwxζ3 óx'LHՄ2W7y[ogJ>3p91=%Y0z"ڊdY}ئع뭀8Ae2hߖmvCDE GɌP!c˃fUHN$ !'a,>wWAzzAvͅؾq-@yKq0mFjuTqE&/Ri(TH_z!-AU ִ5.JoXBM_3i:U0pSyV+Ҩ |ƗL%OvɞoLdIBޗq' x Gٗv7r B6RwHGeHFXpN5kllܹzH!Xvg -73d7pE`!5v7FT^euw| l71P1F~뺜q8a€/TK {_g5E Ǣ:q)J_D$74|~Fe2+8c)uB"];}w)u,K!GarTJ&vklfqUY)hβMpm ݯ# l f땬e?^ L8kmX:@[,y icߗc#+-b)LvA_ѭ~(i?Lӭ(z-6|X!fdI4-|C3 }dbQ6̈́AEO<_s5$FYnviB2xetRCelSܛ',ߙ |6Bn%pw55Q Ll6VJg +} ܱGtc#A7h!bWlE1#$\Y>\=tT <)c@R\)6pF4nyx#c|}Lz6Ur^>!vuiu"9 ')RwM?"M$n<#-(s玝=rLvLK ;S͗9jߤ':>?sNdpeLЄY`R!0e*Bk5[Lj̞;b}/7P*0nGҥM<(khP,ch+㺇`E;;|w1j ;]P[pNqΝɦ=Z3΍ ŠVhu~w׎~;]*,\p ݖb^/]|>rH N#uL@)2%\4l8MŵxDtUr[k-Fy SxM:UʚgotbiA&ET*!NXg~GJ),ۗ;rjgjaS!FHkgQ;*sg2? l*zLH9~.㼩V4#_$;odC)]e Z}_WWŌ$"W-.2wj0ߖ`/tlKnggrOL!_Tt謂NP1AS&wˉDxKM2${Dx$8s,#ʫ%ȫ95&:>7a{Cp5]4KFJG-jMĒ RNEavd/dY=6^2\rZR93SgB{42AiH.&IX}߾pAb^aLvl5̢ i}tanZ< 1&2{|?#<hЛrC}"O: ikMT[MK wRԋ|dthC8Yó3< dQW6(5-KElCeYpܪs.HoU^UN;YA,KK\QbpO6e?DPBo[spUA|PN'&;$] +`A*`/$$iY7b=bv=sxr :)OJO$'ib:f =:~{5#I&7-,j'֔nZ6h!ᙖ J݂eigb‡;ȡr\j*O%RP|:bH9^SdCc\Ak^R rHb>kU%K!3&bD.a1)>sO ݌{[;`nP;@`-]bwk/ȸ;n-۴#IiL7ᦪʅ@mm 1ġf\ qPud1L@Z1kYb  RH`,K7M~dh@aBJ+>$gLA4-n| )ZV[ev͂Yd@ Ō,X-cشYMȇ!$AY2 n;pdwDz9PvYry5D8Evo/OBӌFBбɱ]rkZk^J] ab[k ztQ}65Cq&aMqIfsf։֑v\ō< ؟Q;T̼n$7T%==1om儦"=ͳ#Cp"ޝ;0\-$e.hn<8>=  G'6j,F3G'5€a^H٪"ٱF|]9oB&,N#Ҏ/fG]:ɪ(Pvh:.rk4J/yu*[TFNT'ŴkD_wKbz[0r ,]k"y7c\TJG%%@t ZQtʾ{IXh3%/a.DJsԸT 4vH18ꅍt N,ayU{ wK֎Bm;pu%[qh,]ÖlTrIzzZ3د?گ*DMѐ{*:ljs .y>௏5ˎug`HI#Df"A%pˁ ۙ OV>Վ^n6үm I},ԗ àa6Y麋Fg:rJ),W+7 8[#͡=; BtL@}I}1@< MǬB=䏔F~dGFlg% i%~=cgڢm:I@(p]G @Hr~iċ{QװSbۘB1Dj qX %OӂQ"5BonO7&*w'D~˻d RHGſ:S& nz$fqN@.B0+M$|9|sjX'e5s%#n1, _xf8Adp&\`HMD4F~.)ysI >}D`Re@ZVXBh@' yNqjd$%: ľu EDyTqm64 ԋRAw#Qr,s\9axJn& KM.^'ㅷs4?v"{)w2 Qpo.6m`ٗ{EwӿlBl-kaLfp YUbFᑵZfAL=N o =it$Ւls_%-a&熙ZZY;}Ýĸ@kt_%{ v3>fyQ,:J^^H^@ĊNGNkJZނIJ܅Bs|l;δ2:tS_D 2Z} ˘J唱9X}"x|Ȼ}b =~eP9Wᙽ>eY汃?]JԲžޝ{ C )LDe4 ӎk8ϱ-W=u}__c'ke"^XV%b^MyWBVlDݐRv"1t@_{%l} ꕰf8)@RNyMؗY@ɔʧ8@*p1j}Uii{bo0F`s-n6ٖ#4[dm#4LLtD;{: -c^qBȏ2|eDP[xB=]e|&fD.F#LNH= >/ { v1xmȺo ˃wf;,Hz`v!Ev|' /8M>~0!?GoI׿Eܞhdbg%ʿ #cR/77"K*[$ݔk5ɍJWvQ[A O:ęғB<;,boDa/7zsD9iѢ/М5AδqHƭG(*ŁQwQloZs\-14̧9yr<9m-B]T4lP±#c&>CA6{0:c$ 4!A~ܽ wѠ.ob=wj"hUv:g# {wh쪗߰J}Z< B.mLDD˹+~eg 7FxO&W[x9znzR jm?]V_0luOѣ`gB3yg;^?~^rwLJx˶Rwdbr%.ή% ɻ_ߦ/_||}?>zw|tw)o#M*EkcL}2+ظ!Mr@>㇓֩~ˬr=Xm9EcL4l/ao85ͽKe^kxprXIQp3sNזE)Ul~k.0>zM ͕{maV]PK(m7g֙8h:2!⋅T|}޳{I借~VRQ"clŤOuĞA<qag(%3ԝ@S0=k@0޴.-kJԹ A{zsp[@]!6n 4!4͈ٚ1HEV=9T!M#]y5&6U< I U/mwxLC,(>,FmpN7LsbS÷דсFYUN9( WB::zކsG_Gu N:hiFF mSh8vĞ2œ\}:M!;Z-1#h"?.w\vۈqP)w4GIh - z7iO1$Ż:GbY՝} +1`9qg6'GAM1 :'_[{D7dל!^Y ( 2!QMs 4IWc*:bwRBV#ݔD~j9 ƷH_ cD`s{ ?3]LuD{YXqYY,nK=KVpIE!e8EBpK99=VMys/+oƧyMKN5p3 f {EPIk+~Kgz:fC7~x\0~՞ͱ}Plu0y50wuPdDfV]:aK:ÖUIJk C1[э&U/DwQ$(=&]v1 Q=:=ugSEZ\^GSN_g| ov n do{hqb9j{3/6\UdgiHNR}}|H:I8hc Yb Pӷz/@(ۈIy2E1Ͳ*)4U-|m%H Y\k%["u+}555~D {]?Z!fֵS4| =ʉur|^Lu ZR'GJtQ˵4nx8?R/u"dAnT%ei(r5ح)c *xʒ[.:kX,,T":fc|W0j]PʨE*Uaӽhj$+^\1OxH̘A %EypSRKGÜ.AM^~7m7W]b[ JD1,qzPJuKL2]X??c']J"psAd~SQq8.vV7sKx/hY-ھ\bWN.i%1wzt;9*[Yt:]͵_cnųǵtytO6eſ6L!s߽*S¡C+0FӉju@E;މxi&:iҹr""!Wyέk 'ۘ9E¡3,JULW`_ (׹N=Ҳh}M>6?|V_w_y0cڮӑA-, +bș޶1pS}1AA?nk/KkIe%)DWVn<;܊Gn _m:atM2>緔a+8M7YǍBZgZxh/e=x UۭPK!Ti 6.poetry/core/_vendor/lark/parse_tree_builder.py[oba#ua-{Ms@. raVچ" >lwf}VҤ@ "qggfz۷{6:0~do0.[(}%Q`F?}ulsgzb-Eo~{!sAm?Cm}yټyuPsG)Z6b8??myuh.۩ƶ-uz Hz >TU͇pxg#5X mΚv#$_)*`Ꮁ tx][~JE[V&#₽Ӌq; đ5 as%KbP12tקL~pŎ!Jd01^6nVèVX6#cL?Z&"\m!CQcCdFqF㷂hJ k Unwk_}Z!!]Wq; |740 }skRc}rD՗x q`<9n>+> 6DqS__VI)Fgaդ6P$sg4U[Oqg="y\%ǏHn<7J"uZ5!KXדi{<zq *6sZDy13̴*R>;4z+[ُ"#c :)rT BF-,tf# nL%YŌ c=NSs!QV%&IP%qCAU1;X(ڠ;"XB4d@SMq*YK=#%Y}/~ߘ_j*D4¦myɖ-¨I%{/f7ZQ;𻪧u ({pd TίOʁM.3I+En(ل:F9hh@➂<5ƒl . ݐB1s;>)Qw%O2 ߾z!s~kbw/y>2`4@ZV\eyΆɰ'VN\=@K4AJO 7P13j.ߵt2k~HWSW__CI'}9_7#cBֺ0{A'7YOCU6@, VO%%!@ Fs ge;d^ɷ tMdM[D뎭-7ƳQ2*;M6b |4\O#S;%dM̛IVa c^%|:Yk |vs=WfQꍋe^3ԊÒvPzٝXb"zc4G}IPGĀL[9l7ރڜ8{et%8yܜvUbkF[穗ٯ˾QXj,"vpkǻJ0H#,;k@Sڬ}]̣|'eEF NHg~49@ T ЈoD6s<_# ^p)I]~IG ^]/EQ6$yTtus>SJKqݷ>;ov%d,,d"x%KƥՈ sGQ |t|@TqA Qں VgR|>[y[ BJ7SյӦpE K/28a2̸f=^[Js_ȱ{>|eѦC0 Y0wy3Ӧ7፜H!b7|BbB?=W (J9wz!`llWwZ;&]F83aF Z0J1zZO'I&,2@[8ulh22j *1cr^9fլژI0`ěw]BW/jItd1m[]I}c.Gђx-ft/>}Ez^>;?7oGG^ 'DA;/2QELmS[<,~/^I)_ʜcoWV2Ĩwo>;д30]LYr_6~†jv|tY8BUb a[\BcB&6 HP7i֦#d YB9S_y7vOFWM pn==q$O6J&LJD˕ނt3`W` =`@ji7x,00KC}x6DXN[>"=4=a(YW8| l#xnio~_ PK!Ad< $,poetry/core/_vendor/lark/parser_frontends.pyko6 CRC&!Z@kkaXPT~k(ɏHp8}TwV^*;o~ӈTW*gW^'U|}}%׫e)yri0 -QՍGq O]S95oo‚Cq0K<=^&Ո{!gru+G>%;$FCu>NHlx#K~ .Y`%p_.W}]Dofl1 T"}]޼y0(xw{VI~*Q)-W ~QR5Ay%Fk+,))hY&9ěAdϰ3O 1? p}շ@ Ɨ,Gժ,A4 $5? bbCHɮ- U vbpe6C. gʪp'h^%X ۥ;xZT`7:z㶖TKQ*1ubȮ7,;(Ox )M'YzyD 'Ip/nC6{^1$IGzJErXDGd-qgNG@eJVyeLa_f7%`HkZ8/0/+cs"o1X8# Ɲ@bPoǓ=C1+k IҊW"JBA[kS~Th3dT&!wubZ9xxt x_X q;qQ{r΢ -3D4 قfp_64‹~86O<c &sۺm{ Պ$$[NU]+a.+9 &|(y^aF!A⫁RŽqu  88cu(z&lAy^hyŁ~hLn%3FD9 @;QקґaBޟuʆY|P ם1N(ҟ |;~6 ]JeBA,Tg.LfvV`w{,5$6KQ_|aIT6[ބ͙eBKұF\;v]Gi,7٥`̈3lV[hq e 2´ +ʔ,}$i'Kw"l0@+̫~qgAY ;,4sCC!*ǐS?jh"|g! s]U"uy p QQuR 7@,鎶2h_sD7[#P,Ē ;N8Ad^Da]^ >^ @"fTקJn5 A; H12~8S'nf1 H\O!Q5=ii`}7)56j9%G47&mۣ tmH(N6yQЎx)LMAMa E+%qZ_ )-Օ@PM=TGqRأ}ҵ؈kڽR1Ea-0ś;~J^aMun{*isKfDo\ƔW aaEaĆ^/j#*XG[IX1_n ac:E74!DRM.g!6~خ y+ ہ~W.YpoBrt l:c[x3&I:^C?#Ug(:}{hLEwoػ$i=L]}kZ@bj.v߽93- fC!߉4򙋜$E;nP }iZmkn'7Q "8eyUiK [ԯ]GU !tAF칶rJʣPVR83gC+0{)U|y!Hgynfe{UXrZim(fGCϨ`|yLta{% D .ӗ}UVg[_2SiH),BxK.84voq&4!)-{ ФH6q_tAa4}}- 4b?cEml ˜fsQ"Xګ\ndrc{5L/i}ŻNԌXqv[[%I&0hXRXl>@<^o9CuJ bMe;xWOHZeڝɄq9ȣ^-arTϋx4"d:uO8=ԙܠ qPrSMFipp')w:3:M ($8n(׫(O/MzPK!,poetry/core/_vendor/lark/parsers/__init__.pyPK!Qe /'poetry/core/_vendor/lark/parsers/cyk.pyn_&,ulOE`dAtɶ(\P,f#KH',{]d!EFH]RIY#y39;; O;+QW?j{|K/qoՊ4`0xl-[UEWJTDlUX]rRH(ި*95ѱ/HMTS=o}K|Mf o Iv?bU>G__U1jor]S25)0͘ Vcw4 4t]5Cw!˥(Z.ɋ+6rzBd8]͛Qp&8ާ/+$ػjX==%Ҙ0 fAEqC;5ċ8P`R5F^Xn? 9d,% ߕ(>`Ҙ!ׇYщM&7Ob1N<3JmxCLoY+(xO/+HIQC?jE /%WB1r>?c*x- c;)r2y2\ݭ`m'H0 x *=@; ,(6 > ] (,گǝRZ9$= HI5&꣣n)CvMzf&kN DIՈ-b RU&[m*18*BMjʚ'pr(P Cz%GUڢ֓,N()1^.}H$!~{#]I^xdGVy1I Ѓ 4DoԣxFWbCg|p+PdP̕N0"HB*V=jO]㺽O4E*ޥ>;Osv'wrZq|:Z΃J(T"yK5ecv.& 5m2\Lu4_̉E'XЕ+ ji9KLц Ah#7ź;Ҧ DJ+22b =+Ağ(xZHcy=#@K7! 2Ob1jFбpR?MKz? 'EC\I3m$#Y(Q*6(6eXzK N0=K46:WtA%qF(3$Ө 0j-Pf")ȐYi%.Ԣ;B#ٵ_9h]5E "@ ^2S~tcuAE3A@A R 8t+: Rlx 8Tjy[|:OsChN L8-JRLJOinH'~])AXCG*vMh\K5 9-oVCDa%ܼ؇:L[`t'"tʞyt1\WM{sb݀:"":3{R] =8z6]5 }.\79}VVAVv{[*-]=2I:ɔ͢Uhs+ON u+^Av2hg`[Ʃ}*)H&**+5׿g_p]ׅfQE91P5Mɱ?ZWNMr,B@uP,No|H%2I-) Aa1K}~wP@/AMd+8{lϰ5_41,cAHݟͶfTZ¦|?.#~z2 VNCwOTvt g`B|HjŰ96-d{")>5CXiŐVMu c'=ZON;cZy{q ږ~n/ctfoji؊yFN}q^q !,=l6c_D(,?.'wu6'k-e따_~=>t#0|([wxg\Ə_>nw~Hn*stWDQU|P,C!7l N8tkx,)'.Y֣4@-V=ݕ)'&v/1,0M.9 @ۼP< e0cHf(!i=$o'"8CQQdޞl}1)<,W" 6Y,HqY z8*Ÿki*A$nJm3oA Yca!{TUP@UU)@"-n*R_}7#{Q㯅f{b9Sm @0 E(/{ yzZ_zy}ghٗ 6H'/rSQG= 00Ƶ_t\坳H'~Y{AȇU"<-J< &*ֹB4.G-i`=-" ]# hjaItk<:X0lT:u@W섣-~3 ]b8oGJUWo{chP 9>7*Wߺ.6ɑj8vzfnxzdo].0W\|`BDӈ=SO|[vd({r^7Ugu\0=0Ā pVAvEm}V<7k Kv/w`5X  D])@.yUO+ɔ ̆4":MЮl*0q&w=]J͜_SF;:ii^x׃zEkA16[>`" ۶Gm"k;pp zy}@8G_b u"{a'Qw\-kaT+ <;n>}lg}ݦU7.'t*י|Fz o)l:xcC\IYl'ehB@7pWh="ݤ]m˗K+6-EOv΁z-R( ?ȡ8N0Zoſ_PK!s5M6*poetry/core/_vendor/lark/parsers/earley.pyks6~Ξ;IBۙ\zFqS4umÁHH™"UowIiқi:E.}a:88YJVEZe:+kxy-[R2 n%E) -RUJl)qVLgB/uRh=T ~eOhgZm6H%RHe)%>9⥖I&R]/O>>>yW ,f|mgBiV9l^SQ*T jDA2xKH8- e.FF0@q:3R8d*őbZKz=EK,V5Y ŒXT¾L<Mq/rt/o\<&b`9!Hx_|Y$+ QWVyγ53|k Ի"JG1 ,΂b=@3/uTR]oW"{Wbb4c47% )fd\)FJc8q{B&VĭwP)/j.ڨ1Z;1i0U h4Jzz^-Er Hx,rҼj j00gMZ?MِVI' :WHbu]MKhADqz k66Mp#ry-ݻub -wF_9qK'xV]p{Cxt.D4VL]ǶvGbˤ״(F#=#~> rθ$<Qz~>p"]3!˜˜+{WE9v )nYL5؅oJ.b:/_!qKN[Gb {1p3*7}|*gUSqY*Vxi5sBKx=2zX6+ o!LQsYw{esLi uzR%h//wfw;w=~:!{޻h07Wi ie&wGؤm}c9\#&G&=M,V}7`'7BSJ< ); 1X#t^ #WHw-fж)mkq0R inY4m`c>i}mf-STn7}Vn>*(aʈ gy?akZOء 6sY*!OHc;;21̟uvi;/gKu;H6*[uҎb ߟM񝷬wρ|Tw7iiP*9Ic~3jt&DkyX$]Yn<1I0BiR6؅0}ʉt{GN1uK)p)w_N.x걝+w#?>P\7F'=q*L՝I{a4طK/F]ž&8Z2kv}guydNy[m۵R;[4I!]~;q~#lYk/3c {G!X&X$  rJL-JȁrYޡp0*q2eV>:{X蛄ݵ @&c|<dՉWo|r IbA 5v2{>I/? j^J i_/rj6O}]G~IN~>(kE}GTk~|ҾKb4hq7fưe3;;5OSu ܛqH4e|W'P0}#[K03q5m%aOϰZ [.:=GA v'Ad]2N,)U((z4n؉# aEaa p5]|Yܲ٬l7jPK!InTCz1poetry/core/_vendor/lark/parsers/earley_forest.py=kqWL@qlBêbYta|>ӏy.;R*3====[U]jWfr'I.y{4/.9e*Z4[)J )FzމMQQ*Q&U- J%*DGynn[(Y..޴Y&*0JOh&Qawۦ)?Ujo>ˢn<zT2*O M.؉ z/}Q5M ZY&HڼOE)&e^%MSʦy{~if(2 lTif3zTR.Vʐ+`osr'S4򫇒Hb߫ZkU*5a%ŝ͛Qu0>_\""uSCnUdnM.~[ |PB;fPQ1۟?`o7xNoWbKO|(ԇbWȂ|\K쟈F+X(Jcn!Dԇ *ߺD//+DHI;F lM#1j0[`C>1$F%ljD=zUU2Yow<@? ~eY+uO/+`t=,3 cA0x9yb[w~~W5s -׵e)NH6zv|5 σ/=_ڕ^]Ҭ[]*_gmqM h=`˴PtPŸ Y0At90Aێ V`.uV4b!b^O4#Lś$m3DU !e2G5b KN62;\&(fpۀX&1R݅ `Oh智ÝK'@%F)i[Qž%!,ptLG,5Ơr/1,Ϩ-*_1/[].}MSA/~V]̄Gv`qp,H;+WVk| [uJ%wzPKlMp=(dfE=c<-I|ˇfѨFɄ8"?ޞ4"Z3K^$[y3hilpfD 4) %a#wIK4aBl%딃eBVlrϲi+ՌUFCOE]`+/0LZMk$@OYI fKm%A,A㦰2#a^K:G>ijQǡU:wJVkޜ5h|:F,@qW=!O(S'!31Y_s1p *qs(1 NiB?:o}ٌbś2Az9-wA#ԧ"=H|qӽ{[I?H=6+ص$#$쓏F'Oc`؜~sÏ3ؓU4Xcb4k1j=!&n1dn~ obg$LC{eb\POqA}g'tA;NzlG:*H<6ub!Ėg k?mInF."{PAS\_.L _>tȺhOv̗&Cɑ 'K/s8&0lrεuO,_ Z9aŘ3fO{,loO wes8&\ uXUr0f!Fw/">NՎ _yqbfR#'o0 qb*좑ێJ19؄߂ozz 8qV1&ܬ>͊LUS`3'̺wp{y)7mY긬9O4C|dcзhSؗ~*Sw_3;mڟjQQc"6 FSMY8g78~ @%lAC~@}Ԏ$]7$L|7ڢɺ(=PQ:#&Ug90cn G0ȲdEHl6$ 'sg4`9Cv:b}Xg}oNOC4]S%ߴ+Hg(Il].|A[-R=/6W$`TX(eUTgɨrIW`D &(z!du^ ~$@p=lq+IVB @vh4\> bb[ /kX֋<Ao70 -S# f4jg7GӶ$T*}]~Y.?$P-*vA7vDuOEu/p#CDX.Rl&X: } 11b~ir"SԶDCSz`kF}%-$M9 eW6\LɿT QVZUW!>WY]u`9XGؙUj{pl/W͟u!PC$F3#њ;`I*{zAfaLňl CCXJ"Qd[@:y]v=E"Xo37)~$p[Z py  uH[zLI"TmyN l jR5U+ͮnu\9;& [v]'-UME:YԨ|!M`w&$P#5{zI-?hjYfW @S[H'(˥m 5L/hQuOaM-(P~1mJiȦ _La ]7kRYc3MD z4*[W@=%(s'8]% 0\îi$zX2 NT0G]YC:miǐl7'¡CMп3ଉ.rlrB ڡC%0kãD֛ܚ5Bd pvͻ! \=:pziO㊶˳I 2iJØTJeQt9` {KSJLv:g )qrLRhoȜOdajs])`i a2 WjB{ab氪F7-M$:O-\C7P[U{‚sV5({nHqq2鞞ez-C`v{h/*1-/+QS 6;G}qO.aun\k>Ћ)|풇1-{I_eY iנNpE;{D339[ +jjCHpB<' ND#xWm׋ׯ>fd6>kas$wpYQٻ^gTP*?rB8Wq*c~?]o;](wt勾i-&*+{96qdh@޽@ %0- [khB\Cuh6 pxSe {bSnD  7Y!2YLAU 6Ysb9kO  T cmdc0}JTbc_&ܫ.y(ShV BKҫH"/(]8j㰲tѨ[9rԭ#:SAw;gLX+D܎9ҶA헰Eao>NH|^SsƼV/^@S96:FOuFsflMBP3(vAmo vc=vﻚrގ/@^ՠt$uZ#ή|ǨVމmhr=p."׍哽@ nCGA[hg;:xxC,LsI'>9pOָWͣ(k5虓r_*E+4pdSђi;ň1x(al4|61;b-}bl]b_R?f8>3udX}? Şk/"ĽV#Q86ֳ_h<Ф=O4TctdחESG1͕eb9PjCy$SIPvzpBᮽЮ|tJ×Q9V%"cB>WoԜD;M%H7plPH$\Q듓8D>?q!5vfSؘ ]3N$3u|K`A} lo<{Nt^*oS/ˌu/pONMGx5~ZBHä: C +;#TjL֎c.ZQtBKߝI/6õrl0s-Yd:2XvymݶE䬃Æʎ>k&a<8s]jx_GS, j@:'M*UWhϸؐ vx:x OGy ÞU޲=ȯ %E<#%%U3;L-vK|8'@,nosNc&Gp!><6Ph^1$^]~9@Ό;Of_VtW]JQu w1=ȹ\./Bp=u_C@܄ n!Ӂ3Wr(+xC7~4NL/jf5n}iR-#Cmw˿OL  )(ױ]M~Js|PH a`no0*kJcޙnh~_aK@xLՐl; S>?ԟZԝ=sU󓭮&^!F%?ׅ1Nؾ .ӷA jtOwGa4>}wt-]uc 0 MX 4wvRVE*Ye\PhgfX((3d׬m2S_S8d_by/V@ޥ_~r8QP iѧyG8>s {jkb#%|*6rGj,fHLTs{F̴(۱iU pEJ7]t=?P7 %ڹS-=˘أU$ ?~wi_%YD@Zr^⽠}w9.߾] 2N+$L|Mj|Wi_(mo3Q11a=|/e0Jc䦂H8iO'US?y0?jS ϊQ?v Qo@,vAEGn!~ӉۿI7\KLMs!LϭQ*G+([}7WAv0O=K^>TǮ}LƗQY#n`k<2;voBq\17L[:/_?_ۋOg,*n {fa|pf^"]!|YOOKmTQ߅q GS}Bf*k u7\_ vW߳9Yv~^ S5UgPPK!6z4poetry/core/_vendor/lark/parsers/grammar_analysis.pyXmo6_+,-یX׵C`,1YHH,'A7@<=#\siQ.Bs_U"ӃGJ/UĖ_T)ք6e^򍔥.\Mr^fcYw; 3]f++T<?B,coߝ\48aC=0- X' SpPqZ#*-.!!Bm6yp&zYN i.ig1&mGp(5PX#\(ʐdYLfFptNfVp@tOIJ;?ӻOJp$CR+j}=h{)ڴFZ,(j\4nF.u4. 7w@$1׻p敿ݽ _.O!w:+JܹЁr>7F,W[|5zWlf=5\0>HFbԣpCzyB@|+M")۰C -wi[!n iTԋ0H(Gl^bD6pp8!urrrwU(}jiE(xuD ʫLf%/ 6 9Eb"Փ%)"}׌KɳreAey^2d)FdrJ>})`>㔓`vv&?4ɜ0<uYL&Qh5[ؕBm>)bx/X紪M5oɪ,6UD6T""y2aiÚZ+~($D)[=PNʺq;& 4,usd-v)Cހ@d/ڒKWA# #Ga'eJ:$Asq>ҲSora/QūqiQ$rKTJjo69Ŋ }Yo|ApI3$BNɝ4||@V6d'2ȉߡC 7'*:#+ aWgs"ubꚁ4n1ۚ> ) BA/I-V썒QԻ7yuӼq.<[/7W1-Us<~Nmİ^#rWz >(;d/p,JBsh͎IW]^mB2}89M'UW1p;i8t?=B]|:A=!?_ y,޼4+װ8|BwՊb>mA3(o\U7Ö:(,=K4בd4:=HUScPGb1=MSDk;6{SJǘo;` ot? J]&ì} ~k6Ӻ;oڹl6 j͡ϰ3^8l}uw?TJw0D`t0c~yNPK!d9A;poetry/core/_vendor/lark/parsers/lalr_interactive_parser.pyXmo6_A+l} +P .A?LKE&UJ)E^{즖u5FJXTN^:ykXa6졖e`OgEڰJAbxW^-{̝Z\VޢVAٵK={~?p` nrXIlVVNT7NHb⻛^-ֲr;ƫ{JC3a1T 5?HWS$9`l6{^ B0W jUqsB94,\u]=+ +~3dqP GÔh A?q ?Fخq N+c;`Ɲv:ů =r^F!%MvqTVf_;V؜Z wܰע5Џl85u1*5~_zFyQEH B_'Bq,Cq uk ;` *@1+  rE0#꧿޽^ec$Iq?$Od×$f8тFwܬ~/ f,8@]CL7lwb,WĞhЍFI>}rYeM6~pX b7# rv~D>AZF+_5d)A;$jJBAlڐ s z9IT^I>sz:ǂ!uU<ߘd &h'nF, }T%+j rHaYi-xH~Xṗ7|J!<9+9SCy0_WTGA^%m/Zm^V 0'W+=_7N؃V+8PL`xYDfH&Y@Og)8PDl`ܸZ| h`.m5Ilspb6qNbg&^v1s[s|׈9hI>ˉ\{ىG@q~[(~g.=fs\fs&Ik@;vN%Dl !)PL<ӧe嗤mG܎f4m֫H/͊dkTe9YGytt &)X}V_-TlFs0]945xXvÎܕ56#j7FŐsCD0L&~Xx/(;6 UݶbmLW=p 4#D'DeEF?Et $N"rf~ҍay#رp8Mn%tSps`{ (o~KD2b/r2U]fۥNMyS@!9QXSQYq#ᵿ7x {Kbݧ:^.8kFȫm-+Ӊc"I*5 Wx9r/۱p;N .1Lc}vi/])-{ cafIBgS344=E?[dg"y44^*OdndžQPK!fL/poetry/core/_vendor/lark/parsers/lalr_parser.pykk8 k.r9mCiS(`T{6k}ɶߌdip&ZD'z& d @#L+q/ٕCFXeHʳK =idQ&0~ne`$`Z{}YHi֊NȢ) C ym.Vr$!*ojEe^"k"&h|G23Zņ)dkPNq Lo~ )g^{`j*JAݣe0֞7Ur ;힟ܤ΁[V=9 jWNUMY22 UMl V9RR˷ЈTmK+qzV죄:aPAִmwƗQJP`=rOmLbbjZN]gl=fzZ4(LD5-2fyžXܒ9QҘmu,n#0ׂELTzP"HDQC`2x$}yGߜmᦒ41-cys6Y˶gKĪĸv=ct9":7}ɶS1ɑ6IOj.< q]n xtRIt[yMbupsL_ٜ i02hwq.\f)%=>yoµxםBڎR%3B`+W{itQEOfs&spfoD @:ԟ8 43I><ܧ6KCClP::¢9vFvBʼn6$T|kvn0T}B}|sjCCD?Rx z~ W@ja~H_~z޴$/zf"*l{4LM#tB!E [M`{\ d-Coa rU1gp\ 0>{vBTbVfxI Y;dy&MPxG}AVaܧЍllc5&QF11 -L۔n m-rw i1sx(|ּcn ,^yܱALXc='t S"61J3^39^rL`tSmBX0GN0XsTM܍l6's-g5kWdLػf33fTOtT }&sw< >9= G{{κұbWN/Z (@Xp3h@u[nsHa'c3?&)%DXH)Y.LC>_:g{/fO|lbC xB|Ϫt͞!,g>i穫bPK!kj Q+poetry/core/_vendor/lark/parsers/xearley.pyXmo_AX@-5]}H\ ҳp ڥ,wIaɵ ̐*\{BKÙ33 yvvvN6r%tUwBS,s[Y:U!E7Щճ*Gۍ-U-,[BS(_ֈwETݤs: fKG[w=vĥZTeTOR[㓹,Ns5j=˗/./AɃmiWr+(Bm)*yi2mX 1VO~:;O3VDb,#BFyZ0b*e$G0[OmdLt>7+EYC[!p6: /SދJ/V + BSFUd9 R3!ӍVOy6 w4,FYQ; dok2:;;֥-n#342ʖ^eL> % {\=j[C" ow/pLa*dN62UHS!TG\7be6S`F4EmV4P%ĩ|= q_{c=Ke!Oj)~mY62zB(sZU`^_ B9BֿU45:m̑D?b1S.zOYvZWdAHUa\KJCo@WKǑ\|ifE9w Rhz:GJYx(΅Ł,#Qx+8 sHȵc]U:TC-5kPalOV\VUɮiS @a-)O)l Aψr쎨 x#Ϣ3\tbP)|`} l"ǀlQn2Pq#AXOÿ,3[HFAjc4l7G0^-ؘjҠt36j3R܆9<** 5d*{ŊvSgSTЯ$u\mv)0t| Q}ɨ uNOv&$P4+Zz㡋ٽ+p [m'8̵QkCc~&L7ک*x|qJI1g¸AM9m _ϟ׶VlOe=u&↦"*%Ư^pX K܊쪔d]԰, `j* fr5$~Hie}T|,x ʽ~"xe2k5sr0ItC}$ cC#s>F5zQZg VO5xҜdST?}k˖^/y$p%oqpA[ܯ1ch\-f;}3[y&h < -NFMl_ȣ`T#opM`Vv 8s'-b{%q0f̈́aVqCya!'>_sJS, 8[fpp3j[4ޕ r9LZΛ#.:>ufYZG0 %t>aƬ2CU}_Joz=hw9|S0[gKJY8[['}ϣ/rY2I_?ΰmA>NIRjd+I3:vėuv44'>/W _F'.~!oQ6ȤR4n/ jpUٽl$ڡ9Ju.w~Iai5݄88=$CSk?;c v~z?xoOVi91(Z[8 VaZf6yaLi6D{㐒VeDgmuG;; 8kۘoK8t.MVWdGg(N'0h@y%x''qKɣC:ݻ6ȃD̃[* a?9?6Y^,,ì;,zCp+ c$3.츲F.6x &p"*^>GBPK!!poetry/core/_vendor/lark/py.typedPK!!'poetry/core/_vendor/lark/reconstruct.pyWM6W \QEh\hZ`Ѵ -z0 +l$R )B6i}r|>뿰:^A3)<%hg#ZzZy15RA֍6^KRx% [IRETxHӝCӯ4D Z% ]b)j Zi٧aV iNRBٓ65ND1HVxE3X#/;gRGSKx'V?AWxV 楴0-rd5FB̩VNWO~X7Kv8 EeqϠkDeo;_&a? (BS;e8 cuip( $6|1QF%ZЧ>`hwOwEh#LjVNpK%]'S C?1tp4[BZړ_m5̯*QVav(O3:g>Ma3v7i v5fh sf_yL2GFI.lW26!l̀X|B )xۈ?raiȔu w~5FenT9\ՠFY5}OB\iPһč3zmЛ] pQ)fjt+4fiPe>$Dp[ۤrٹP<\z[Em>SCĈkF[;錨S&$F m+T?B?i>?% eE,yT4eڪI<խ)x2z Qf ^4xh~ hn g1l@ZjmH)++x%oߏ}=U,B!и"ji8|fG#րEN"Nc< n)# uЭk0Ѕ4~+L3{o# 9uVd4?hO?U3;ǁ|Oʧqϓe&bB߼zQ=gڌ--TJ1 =/{Xs蕰័'XD78xM?ES~ysIP~к!Jס`š>͂I5n>5/6Ś_o+Ҡ(ux#ͥ za6x<}p(gR%vPK!&| *poetry/core/_vendor/lark/tools/__init__.pyVMo6W J[b{(@MAR-z0 2a$H*Qw!Mw"烏of#Fw A$Õ-ln?l7٤?框k~||x̉xE^?s슜zGu< mQH.m;_B ~ݢ4 k@#(awZ{[]lQ["Em4Fc|ptڲ+Yj`=Uuc;Dr)[wHQy/Xm=F;gkږí=ڃerخ+VEA,yn}@"-[g?'k ID=n$}sQC$Vu95]A&^UѝU p |>?\IL@ =;m5%[s'rbU{jCG.+bM~s ?C+@1 Xb>hdϴ5hxp=械؛J5UǑ6t:Ujk;=꒣eHmu)PYH[3቙߼E;5WOFϠR.>r߇#V]`F!.ٱfq,Ah͐jVx(th_c w.c;lg&Z2szfW+X PK!2.fy)poetry/core/_vendor/lark/tools/nearley.pyXmo8_ծ!)qM? -ݠ.h"Ʀ2#ĹfDjrh-3y}fm%5;\⑭_ҬAP7Y oݫ~q^-ļyj"֬)1R\eB} :Q Sa tUXmK˧Eļ.ErA~!J]󹪈]gOKrtiTrf fXO̎7vw2Iת'%V$eU$ Ӎ($cƉfhZBr1z㯣۱bʏsz1l8׵es^OA1je,7,,B{¨gD&LҮdZ 1)1s1Id֣1+o.Qz,_:`H1dFy(?n);q!JH;TM"~2}%iY=:Ҍ YRLKRdI^ȢXri)5.vdFc}w7W~׹i*$CJ@JVc.O7Ȉ-"6pNgHm`Ƌ yZӯX +CZB!)>͂=wn-B@04n۳6z%YyN#.7zz_LmEL׊FrǬR1x+mBh91Z~:%3ʞj=~cvZx`7͎Nfc?t:-ʅ߮fd➦W +1 (ȆD{ndQzW2% \|$Ĉ%>5?@0#9V\K˺,@`N\qmr՘l9Pʱs-r!*. L@(ejѦV1:SWQ,! `w eK.x̄Q7P'UPL!ްqd݁%3{+ t(|o $m,`${lp"qZv!WnCԟV&B$_[Dp-ax6&Jx>=ؽC܎eaw܆ጴJA iha;fE|X}և(=m68ߏӻOt`z!ˑX/' bnx9s@xr as"٪9cpE`oW _z=hB$p'Z~=H`/w0 (Ngmx7A$y sRvi+}M-2:AH;'{hu Wbӡ84$-?ѽ0Q2u~I>^8%-(p^Iib])׹!8ڈ8 G_i: c NNĔ~J;:n=uԾ ;\<A4 p]rq(JRȿc} 8gWs+-R JYpf'~昭36GOM7Ƣ -^K>A$9`+zNW[*mQ"2j_$⁙527P3I$`d' _ں|֋8q;utm`)u:/u0Q١ep3=U-φƇ*+M O3brxk8f[d➗8>ِto0@(73~u[. j+YC!H&=w~W4}ixwá0¸g`5VYlÔ!Vi4ϡ6tܹGG3o0 U /k7 (kPE:l.C`g8ІE)8 %OPI[Mb^8QPFȉY/zp+J-Lp#i ڜEZ2KPK!BC+poetry/core/_vendor/lark/tools/serialize.pyS]k0|ׯXR1>)A}Z߻|.M!`{vͮ`DM8S2$E6L8{n>A fm~7?r*Xz $ZXTB{)SfRkcLvuөjh=w`f5 Ӏp M|3KR 16|GQ ͭlT/?@ja:+YDw(L3>>74p6ހօ7p֎w}>6| d Vw>J#]NSe༝9Ca.4GiS\v\Lv\"-RdfRȑ4@8&&,w]H'!P%hsx+I*m u0@vM&,j:̷Gw *z"2[t?}Gt0b_wb.;⍸>1 IЫkakoTfG,|Ar"Bk&'Ҋ„K t2z@GpR &B{pX* 80n~FdQU2cfÿǛ7&8;jEQ+GV!½UZ<4v=<98^9fMRxtʳ2H uöH1 30yqi %%qjjȆ%$=l}y3Ƽuu} 99ow,1=kO)zw@a÷քWmBt% ͷ|*KڣUP?r/,,hK~ӫl d$/i&z>VЫ,޽&fQԨOyt)|AF]/AG\ <B^K{r!aCw5CsXlqhʧ)Au˳s/4VԮ$q &h d*$ZQD_fn8LWUt?OVdu{G΂Zj9۵|\Xa@̶,G5 ^&*d~b20FCK*v2okx6hDMn;FIRdܭ {0Z80eÂѨBq׃&?\-L^l-j26A1ܖj^Hb#ZWwh¦*l&z_ =ݻZRwäswfPt\gHAiJC RiJAiuPK!fy  poetry/core/_vendor/lark/tree.pyr۸]_K2ah{ь2śiͤng:HHBM YV ArҴQ&2 \qbȣE#e 3-O̖:o;&UJ^u]j_jDJYS[oQ-T2%7ǖ#/:?dlB)g5}Qb$$=振bN? EAk}֒<E9Cg ^uéSd]R&**hj2)BJgP޵8#1Ԍa\7@qF%ʫ<•! ``(\[hf$›]]mIZ5ALy,i_3-$Gu~ ]&W -FJ^rԇ5nr0Nof DdׂJRNz;%pF*T1+rJP>B)V{mD~Y(iȑ$eɊm!hfpBB3<#:_kxY!C<HMVk;8@^k |[+$mEP |{KAWC2ӆZM=LA;fu!4v]8i[L=HR{p<{Aa`܈l@6$ DS,g| ɛP`xrBFt:N}A^ޑm؛-z.Dʞp9 Z?C3wY{ لrÝ&:j>ɕv(K[\.SI>{b90Z DJ HC E+OB#]C@2!sXs*sfK_(:'`&{ڋ!\@`г0 !&AGs kW1g^+o6 0kqNw֒$O0p5p1ӌD> wNjځUjXG4hAx% qIxPzpuMT qוedtL87:M>xS+|1ZM4FK`ԏF}Àao! m5[YQU:ZZ~ y>a)*%8LG/G z}ǥSs ( <|JO^R K0Qz%my*k!qPs(>1x9|.s}Yx&'M~.#+=u\:^ blT]؋  8AYRK 5IQ=BBzqZ6zU S*:=m/vxSyqu bX6%*Yrs㵗eb)o+ZֆfٱVCqvA805Ò]&#Tnde Vۥ8G2"g`z@iv"QY cK0[vyE.ŋ[ȹ+:v2gY*PS'N(hXR.h"ZV́n+hw0 LIڎ;:T Z-I7Af(.5f'k6Nw5hJ}X]"\ME~c> Ѣm)L D7Y5j syz!0Cv@&CcI0r.sV7\D{ԍR}V(\†s1>$jUFNO(Ur= [XFϑԠ8J*&ͫiJ>nfɓC!6r(ZL- Ñ.0LN/'}7qE IԍXk͋MllH!<~5OWM)]01HZNnIm;Ʊm+Mb8IROMYtR;ukmʮ!@30!b-2uOk75Zu+=;s#@zÑpuVлί$*5{a=OPK!"%y(poetry/core/_vendor/lark/tree_matcher.pyXKo8Wp܇Hh>Ǣ' 4 Zn$JCRNw8nRB %ؓ(ɔ(.^]N.Y,gvۏ1R!AB' Θ5r`Hq 1滀 .VTr- /q\BUI2W䎡p!( Ur80Ț !C)OTOQN[j Y",5o=t0Ls6 @G TB1-*7 kL& dUgls'80e7Z6?[RR{$,ݫtye.Hsjgaӷхwb؞1SX|l}@6G}ޑP88`hG=_t=@5a@^dg2<&9|,"xYVv Ty\k6F -m!S"7S3tYdBeWU"P0p|zMFff3"lgIRlfd|5gorҎBE$Jv2Np Iw6I9MͭLd9n\W*Duwvf8So'LKn$)%9J6'F}|aK4k %QKM1%Q 3%t0b/aEq.e `v<7¸yl= 8L|z.rOn۷t@it?AݗѰ 4fn=$0"<ɺHT ܠhσ-4ضq`L`/ [{) ?o.Js稴hI*pm-4.ģ}}`ˮUXW ! u˭Bne$I)?@=ϳ/!pf,~fFy ;~y\lphm~7W8G crnKZ [ҺK̄~ N.?m [~D'\U#xC+3ȵq5ð)01j|),ukҁd@\R%s"쥦oP'7n%/y-Id$4Z? KI&Q䅵=)4DWPK!V*poetry/core/_vendor/lark/tree_templates.pyXn8}Wbр `QtYw_a-&*K^J^uqmHp83CfNjoˮlf4V=7۝l>0ޔ(6,3bw,|bo;nضm fM[(}*N`\I&e,*9py?ʰw `ohp|[`P{iUNGGa z텊"Y{+5Ǚv*Eo I7Fu`7۬/u\]\ϲ,\kvNhj1#ïC( _uٶӦ6RVP1ֶ Z6H̵;puۈڀ?*646j6'%W@<,43CD0%L(AjЍ" Xd kKJɲ6pPƉo?:Z?d_CfZN96ي9(#l|6.NȳTټ7q{X0TpZFhPA_=gOGRՂSq/U6A1i 2[DSـ*r R1)\)Nv:mPogϖC~$YqBy{ n7 {2H$nl| CLΘחD`Ϟ@%?amOЂU3/ >Q.β(zG7~h;XEZ6%RAle%!ֻV'كk[fIp^u "v @7Mkex%*R_8m O$7P !>Y+HK,2A5?_W6DRm0pgjvw3"v&jeZyN2dӺ k*.hkvMq:8jgL;?=%dp=F4DQ+,o@P Abtze~w1BCQN%<~18ޓ]}IC]mZ>"q?JQ~EgY}]%mO TDbwV?B)yaj+tR*i4UUb+,3#B]bRfJj]r~ݩ䣩eʬ,TCǬ\uѷfsYbj.g%iw0ɰ^֕y6 G'Rբ.F7ʀUT]$J)J2ǃ 0B%Q4"co+)!MRQPծj!= ,re\+h 0שQTlw8 JZBhvn-BD' ϴʌbHC/!DH;X"ۭ*6l ~]zWSL**-3AdSQH(}+'Wy140i S̗t~x"j&ĉxçO$`-9; E nCYVO$h-ız ; =5<#:G֜Xf*t翙ka)F/8#fYKRوJV`o@2ńTX~ uZN8nŎWZec&禮L<"7RȖBH!EkO$Qoej',+TJ\Z/] REoO:RNg5WFVeqtvݮu*^{&Ao& Up3""uhxoNPFvϩ W8Q7Yh'jJIb|(`W,i81&njjp>fSk*}fEb }駕1WF-@ | h4VNQX9WLCB:xzd4"<1iw>KXu 􎮗2~gp`F$cbdp}uxqe0;W@Dmk* P*`LT4 V6x*X_СHۉG)`Ej7\ܪPp-aqj0CAֹbTq&&MLǺ5ђmNμf3!ր(vd"d/عl$RQ sPiǑǏ%"LuY{h3E^9 aӟxT؞")m6| KJEIo5ι'9Dym;֛Nhvn%uTLz#~`qJ᯸ׁ*>]sSmfk+=^MT+EKE[F'EJ>b @M G+D~VږhwLXכV$ޠ35Xl@V,J;|65e99Е~D!d3DޏekZe^R 2cRt؇g<1Z^Fhpl !'RQc*Fz%u "GI'L;tHb$E]X*GJ8'Ҩ/1iX%b*EpA$R@z/ Ţ2g Ti?&lq$\Ѿ T$$D3xuwo/ί# 8 Iٯ0J+lԦRbK_m%TL64yrtFt:%A ZYz|͉P?ƗWﮠEM^gI Lm2ago^_x̓#L^\@c7dhÙ8=5Rd#A[-d跽AQDu&)Y6le~Qɍ-PGeH^tm9l\6 dGxUzL*]ЩhQ)/0gh] f KAoaa$.D$=>NZTaS*(%^ߞ~{:LQkS=M>|êϕTgzcX4Niӷ !+Z4i"kύ)hl Kk5';*]%@87k zVs9C~cC.j6ˢ#>g z8e֤-?hތ\4^=8aͯkEh S<%t8ҹ9P#X[d[Vogы7ҟ= Ry/ubQED2XnIޡLyf+ɑ骫V^Ž;Oj^bo Ϳ~9w{>uϧ>~'=I~bW@׾{/C"B$>o'j7'休mPBؼ'n'R7~<-ϟ,zh̗r~4o4I~.t2c&D;d$KætNWCn3Wj7 uPT&M탴[nd-OQg 7wZ~@IJ26`GCO>CAή*G|p\;qGM&OTGmQ&-go?=jXe|]6`FL$<9Fˡ9~TlMcyQKg?zRt04_ r} ۀp0)^l˲&H_2řqn]rE"/[8;e{7 xQ`ёn˕ (":"KŞ֜3TF{t.ޮmd: ,J3:ږBڡ=_To\:f\(ӊp. ]%$(N+y 2"{nh`yf't/MwC/f6 T DUWT!Gbi L3bhiA[W7zF Ӌ;B<=O7T|Ӗ}N$2i$dfG@з(E"UP;Q $ܑja뷳? OK_12u17PK!ϖ$!P$poetry/core/_vendor/lark/visitors.pyL|w(Bf޼~0v(+b>%9*U /S>1ǿmD`%yX)`5le$T< L5+dJR,&5CsHҎ} Fp n3obŲu!KW כ,Oa (4o}NCQ@#8.Ůc9/{ YV\yLAÙkȒ(/z-}U͎DN4 VVl>Ҙ$j@K j~ AqnuUR/9oT(>־̳Mo'MlN%>n5:Zx@-`ۑ R䒝DQbQ"TLދrk\#0_e]!A'ShD ЌC k!k8NKd؈ޒ0;Ѧ# OM.!ˌ:ϐnP|1yjmv"[nUu]*\ʗiWP]%AF`TW[5Ƿv|j e&xQ6V5WQvh*ief(`~"E%D8 6ÿhvPly (`>Qл ,B؋EDo&JȧMS09O:&B1h/;u-͎dvrU= G CB40ׂ\JQlI\I  $mtr5GU^`"J1X юPY!\YS@Y2޶%h2Gƿ cs)Х[AKAIcĩbᐵX]ĥ rd}eSxEV ]Z$ygMYGZeIi/=a`􈋅:qeOTΆ(ڨ)H6=\"i bZ@m@L=d؞#qů(ߴ;Y\fb-5KRUkRHܗB1թ,)>a߸ +`]7*JQł%!:ϘǍl'ʺY1nv&.7$p54KIF]V{2ct_ ݩ>#gUt9f73n@)ck",fӌvksrq6gkms6k 7GSPA@Uau&dj&NئIMS 3Uݰ?S~n*adV a Zx592ƅ;q90Va6 ؔSt \@]vQL,:[5/y8xPQ Z*G(,RƍQNtrV^LIe`ʼ5na,:" Aj :ͺcf<Ȱ?Nj֊ A7̪SR ³+WOC; ܶ ۗH ZA|Ulk|B3M 6*WLnc*+3ȗr$mA[mՐ-r-dJ+*Bv$V줭(x'{Y2 5 a1 E#z iN$* KjrrNVQEeInW$kyh[SW)\c("w-a!wm8@5a~+Ph xxiM0U3ʓ=%)mUd_2^@2xi;@!"+HCDRh|R#O;kz8oN(D;Mُ#{f5tf[g.a|Dl V9~9KtxCY%U 9 %8MPMuŶ!wzk Q=E=_+iw=cz[Y!\,ÊZ8_asWl㙓^OUq,P*I|n{Z?-L-h Y90'^$:֭GlSܹ=ut v'7gߔdˇ9 p:(i F}&C=Ӈ|*M 6ZA"buQSRV^`,XTYj)1F^AGgԺ`:ՀͰgKJ_Ptzsv'|6r2[SM䟐+ c.hlܾ2[.iW(\=0]vrL;Ȯa S$([G_s5; :Ln=C͓;EO,E #QhsއhGÑéu8])o/i|p` z*Z=ҍ9OBNziן6<|g |XyC(ȿ SvY K[-8HpTKYYaT`;t-uP쮐{U:5 lNAdH#wQy2J8'H@w-Ay:3 utcexG<Lg?[}tV^G}*iK%.ףrh>N-^&HU ЅCқn#FxO1 Md~Cb~i{OlJi/R/CpzIÁS͗V7s̞W |7Nw5-S%:H F`FMPHY]'8x"N0t;=U t_^ˑ.-$J<  rKڕ8#ǺGtn"@}Qu٫S$ U2&rI9) dXfb6&g>W% *r_л_:s)wCsLp?|*ϢB%؜ C^~Ld9%P#j%h2ZQWǹ9szk S1:%1O/ƾm2':'={ܔ @f5tZí=!/r!td,/^ūf?WGC#@U1Rqɯg |IW R:dS7h^ ٕ tr(MUKZ`(-˿ -@ GqȿA8/f*-t .`s^lDmFXExanف{H>,L畴p’PJ!?1gumnm3di[ dmL#&JTx#r)!`{^ǽѹR ÍGΆ=mT5;9}j8տ 8}˕,gx5(px=/z?[P6B?>7nUkFs+'v}^EI9wFHpXPx1av)Tۥ" Oj xܙetP=j3DӼ3l{:'h].Re6ǽöNnsEtTP8Pp)PCQxO4bӽnj2"hT#T?6|Ś0hӷ'W}MD&x' $AGh >ApU9wO1 u@hrhGƶuǪ$/خ1 dxw6!s$KJ1p1]P#km:ۄru4.ffnɒ@^B$(aC,@Z=(Nu=֢swJ|gr]ΩW//e6vn(}g퇡͛0ݛro^X݈wwnas[;|\[7z>!ōuP_ymfD3iDd'8l*QU6VNªޚj,qEổv D[JlbJ ȷfA{[ z{Xiջ ̡SVJPG!ao\Z1 `ӝKj'qKϔ;< i,IJz1^ jxk0`MSiUА]JӶ⠇= ⽱G?@$Fͼʼn+}KAgKB Fy)'Np_7{X!{EӾd9h&rAr%պkʖ( pU.H[ՁJ HL.Ì3qk7;ν&Qy|x [<',6[ )՞FZoUך,hMh*8Xwe3) EgVGghCpJG~_hFeq7¹;3pP~ִr/;:$DEM4c-`bz@/䘐6Ƅ2?"' NPn*-pc2(!iL8R@w1tXHn RhXJJ2B@70-/k2-dqPOagXa E+ 1de#XQ 4Fq^ (Q*VF?DcnE\8u5@ ̴Q+14>O ><0rA%lfJ Xw]}~I|zw$W Zc~^Z UV5Gȃ3n тqV]k"[˒Dh3:}) .Ҩ bL$H2lZN"KQMm7n;h#3ZLjDe}5yu^f^˰H5腭l(uD>[_`FWPhd!R+%u -+Y Tr;*,!%H+ȵrL 6 8n9:cKxi'BTS0!(hFJ&v(rzC(Ȱ#j{K|v':;d)On@dSe ("$}R:ඥ{sF›ѷs *]<~`Vb3rqz,GeURd!38@z5eB5A#L<5e3_V' єga`Xq|t q k}#!ЙK'X}[N#Y>B9'la5sG+X Z!P$PqCt-z>k= l/ѦAP /؁qe f죰MӁʈ]^f+ܺ7;^Ք ҡ审 6IdtA8wڂL-PbYc/0ScN |~ V8 (͈?hz6jE,O_8 vS&ñ?Zb 4e ͨϔh%/*+V|Ѻ Ċ໹X|24[yLvB:p %Ha@ Ċ 3 % sunDC>!R e-Bd6ˆ*bGd&%-5P8"?S8ި*Uch$bpyid03\L&VA<džy⢉RWALN_+P?G24 ˽h•L}A"MMV$T"yu6K VMpd8K&*q4N8{jvMx蠊:rzJ/I.H|x"aV6zvt>x:aͧKki `ZG^2.7T_2LwXFH57B9pR|io`? L=2xhviPCXxte9ٍ_1} ŹYd48Rg#c_A4T=‚'XɥPMӨ XxG MNj!|5PRuKZ| v9$nR-V"5/i V$wI+-j6/'νƹ@lL,7=܋Oz_n:{/??;op:I4JISќT:BKȞC,~y^/W/߮ ݯB }`e|\akansՖo Y{TӭpW8 5H5DB79Dx;S&3{V,?9}r<_b@`a'v6j 7Y@C>2ԮJu]b2ʍ/7zK<"[-9)8 .k4m'ZtWҗ\n=}bW H`xr^h@h8|gUmtɚcĘ;3|bpx ؝1A73e FT-W#c  [ r8HOq^FQ%is0kZ! /H:KO{t=,|-ro OAi9l+Zԑ+1G#ŝj;jIY0OAAW-p_造V2 NotMv9!@`%nQIQ~&=196uMjhWx0\ږ(hŔΣO+fx{d#H6dEcF.PK!P G@)poetry/core/_vendor/packaging/LICENSE.BSDROo0Sʚzbh'0k{Y M W-贝Q'r2.K^Z̧nuk_q!F8վmOǍ[#hdB+֥[{0&˽@&+ H0J,+# 8%\Z?k Rؔ@0DW0D*:BFl321>rXt)raQJ"pBIʩRIf9.oya@g4?u(.sN"L;P_.9PO. /IJ7t UofAWKm ihqrcZ H^cpYܣƢ8tebBRh .xB1)"И37m0@s Ś52+C8(rVr"bI+Vٗfܽ$F28ǽ PK!Fcf*poetry/core/_vendor/packaging/__about__.pyMj0 y [. = t2Fps`˃lݜC~I{GZl Fe4-6^Q0-%ljbqw)e=/UdFkr{m}Vu?HdYEU>3O^UL,XŒ)rd;EqMJ5SpNj=G]xַ8:Y*|j$1;ހI6H1,ĝ;~.0L dIWAY7^ p ٵ-mjkGuo!o|Ynpw3(KWTɘv_w09d PK!)poetry/core/_vendor/packaging/__init__.pyMj0 E 1i!2.Ea&C77QY.:7WG6@oB]4mq A;dA}xLe9XNCfs fxy{97u.`(X!_O -ɡ t(ƺP+3y6Ek~"S^QbU4l)}*rM\@ +J.Y#|a|6= b(`QMPK!*:c,+poetry/core/_vendor/packaging/_manylinux.pyZs` A^5qRYlMeɵU9G*%&Ҳr2ɳba"d#o4,nodB4ʪêZ$wHe(G"KXq̳K$cg(xy,iOZ*/K+9R0^KYu!X6W:%ᙸa]Y,Y"XKQzXE'Hd E'2qb#Bitۀ N,YX `2=v_>9?e? c^,SX#<'oe."XlI{ĀQnũKO3y,#k?g(b8zP X4oKi[+h7GΎo^c/ACq|~tugSn4~:zs~OQ ݛ`.w˫z>?z{Ӂ!9%ٛ)OO'3x|n_L7_^Igd.s2UR!bzDy3f[?,MhQyo|A^.Ze)aovV{!zr3jN}$|!CHlDu~8s:>rO/ݡW[D{d(x&tS%Dv|R9[r2%OeU;+)KW4CFI 6}GNiue)jfTbYVx7J|RylQ#u,b)' C(D dr CW!2 yXj9JsxP[ERxxUD-aG|}qݡ e덋|gk7,j:nc 2!1֗}g2b' M:(ոcs3vx[S,Dd7lY]߬ bů^94s ܬet~ϰI 豼p"RYSM ̶XNu D(P&fYfdQnaP\0v=S55W;ΜQA݋g@_:-N+L=8;c6;#\?\!T<,p~>FL#B*Q ,O|$\PrJX"tZ!;ʿKrCx. 8&)( i"]VR˅>וG*6h2wof?Kjn*c"H4kQnJGlGݑ} 'RBȅ.Q*U{ƪ1#(ng"&.{ʣQ?˪ɎQDٯ03|E5hN ZISU4)__H6/<,ZG XLy,P/.JU#uU`0jJC]1NUX"8]d&"j KADWz@ KzX?P͡R" lpj̲XJ\[x1qh~AeIg ܎঵>9EjATdBad/QV-WT8!P=N{G: DP} PygqA.zGelҜEm4}P#8=\(M{A =[b`ZCFbb!sFAEV 1DHE,CJH3<[Ÿi()1>רT -5n *\hCҴ0mg.էȀnsGW!3ڙLJ@?˭gl@=$Ąq [z/x}/=? it }Y fiG<0mH_96ȪCMoxջ:d"e鞍ihkw_Ym m_qQ!y{>ۍğhVjۓl/I;.k`QJ-F3AZlȺ>e)JRV2ʑuxtF!V }cطXL &taFS1UQqK#Hjys+(+jO!RMʲ #9u`^55)mz!Y@Y=-zۂfm TB}3`]͌w-Ȏ6[r8+߅HF_uov\B(;{ּग़]v` eR'6)TR2Ih]ueZ4KiZ`M5[oF ]JֲAh#k5IkjI=pFn1 5z^p1;{_  8G^}IngG Ͷ};bXCjo$-EFFk͎0$I/†2:8zZwP;uF otf⃄J?SYK3=[YI^!э;9|fy1ob]Uw=VCmz@mkuu</jc9PK!t10+poetry/core/_vendor/packaging/_musllinux.pyW[s6~ׯ8<,pNYv5&Ƴ/HPšYLf{/mg9p߹ k86M-Ln@Um)@VM)* l " [Y5{ЭRRmzoi&T"R Uk)p×-Yh#ih$Ţ.ʊ-[)Zٺ.MP7Bs[aGXfծ]gN2 ,/>Laa=)oZt>4uh[L&( Ղiq1C BQ)=R*;j6|VAYH8a'ef䯂$7\yJnMɜTH '}dt>.$ q:=k ;WDZ.x1zPlmx#eieuu\8?9=o_go/3.(w.z[4Z螡iDfªur"m=<{ymϘlkqJܗ{zd88N!:!bZ׺BV­a8 2KqMF ,g^* 6U[2=5"JpOBb #϶R ~!BFNx"uX}W 5`l4B@lAl:+QCKz. #מSk%N=3>='3>WWWm|_gpPקGkiو?~StO>y8S[0VC[/ 4mҍ v5I+*\,N|^;r#+zߓ$UQ;[-:~JV#j'E DAsq'%"' @-dr89Ȼ!:4ccZU`{]rlIo.s=+xhsw0 α$ 5Mݾ^[A<<:d%7ҟQ}uZ0B6uPTcaHeUHүy4&NZuU*v; N ժ+ޟσRF e1: B|Ln⹞N0ŧzkyu^+u'TwG˧~Zabn ̍>BЏ pVVA r#'h(MRvߍe!q++^DJC&TkE>FeM=:+ Sʱ"Ԩ E1Mhm+a$b6UEzqfvN?CɉM2_pLr''KCcܠ:{JPw J4p Î}PDžç?W5yNwHe=]//*Iej݊dJO#c9o+됍dr)ɮ4|3҈G 3vA/l.bG% 7Y4hup!rK>qo}8&WgiN0@kOC[/^'~"Ma'M+T-M#gE349n]ȍ<ňҨ[e.pwF^hNRG>tň\gjj4̰o2 h5P<08ԑ);hzOQDW6A4$d*~PK!0`,poetry/core/_vendor/packaging/_structures.py͓=k0wYpLh)BB϶"ҹ_YN?dt HQY̠V-ܿT3zIyM6yZ"XY5i5DnLj7e^ ~N4YLns^N^1Z$15xraqv0 jLp.l@׹m UyQ'I {mJlP-1!&hQOkTy%$Z亩pc&j)CENPHKR蔜}˄o\S?o*x3˴&'zŗ5KV7 l}%G?\0^Ev_$2DFXor2qyG|}e'b>N&i IS2#K%40ԧT|f"+!P6y`k^].xc<;f}A@Ku0L^"vÖK0F3W1yduM<%Z4l!T/w^B*JSve!cz˛b QmʱKA ֦R]L`;J. JZ.ѡg!%3?1- - g453@Iqvk_3((G4/sa͊M Aȫw@[Z@h4đn(Vx8(q,D.7ooGŞʔ4'W>j ztr!dr}i^3RcNC^ᶴ޺m%G@0mmep;/ONa[ӐK%WY=H|' 7 `fs~c8:WmFmjq^Lu&$otFItk aWpS4xe[BӆkO>brqX(ApyLv&he PڪEr,˵T׋S S3t8IÎ2rDX*}r1\i JVa4t"ʮcs1>G\sŝJ!ыE#;tpshi =Xp)Pq&2bm@z뇥UZ[}wg eƬث -"T)ce4r+j6a(Y2L5BX?[bi!&I,͠iI E\38`.ۜɏnvP[dOaKh]p&6]gp%Xb/}\5ѩ={bRqjNq~tM uQTOLYozl-BZ!b:+ MM"Kq,e&=n _C'4Oy>e?^D*q 1ݱ*5Cc]ph}E67FF[;xQ~0ɝjcB}&17հ3e֐ g\u[KG{1^u8tC7AfJ #Лqz#t8ugK3ӏ4= 9Ҿ1-Yb@MRhӟV 37%nNS=D `C3X;3m!S8iP#I~W$ x9oc?̵< 1Qz_!_{Hޮ@az(c|0Bjѵ X&UxU#Ex孚ce[ԫHp`ބ[/t·pp.CH~B'z9<9d8}N&X]'gC^$ł=-0p줩ָ. pGPK!&poetry/core/_vendor/packaging/py.typedPK!-48-poetry/core/_vendor/packaging/requirements.pyWr6}W ɱI:LF02j+ɹTp`R@]@Jz,,vdv L3FlhFtrƉfD2XA X|d\E_gBDV22'^4F\p.D* ~eɢX3&Ii&IWKV_B4Fee\0gɋwkr} !㵄p 2*i}}!$/S+Vi:z0HQΔ-Y37aW|~*xbdaKW)`fV+4Fybrq`E_8a8M٤CzkH%;`Wtq7AʞOMȨYLMيH 8/Vq<u!l&S2K9ȯ^5㄃k=CD@"McČT8G%?A&?!asz͙LY8FfkRo<f\F~o<^5W҈7i|ԛާ=wƁ;h4뫃bf'Y>ll.#ܫA|5 m<7+ϳIXrI3xܝ%Gﹸp>(gP 9LA81p k GfT8WQ}%= M_9q*`1LAO{YmiAD Hȿ#Txi@}R#Ev̯/֑Kd#%;[S}7. Ŝ*<=z\h m1y. -@0iPx^FW %CeȠ6ybR>;KYv2kdihڊa9< zPfcyj42N?"vʱ2=3677GNgxqޟDC ꬲsyսZN gK@*'q&z F'WrNXܦ, 9\ޮ*ٜI:{ b?BkŠ4OlשGZ!JZZnWtmrsF5ͪ.5dzV:HzQz~\fZ! W'n)5h ¤S TΚʚrY)F' Տy}.F<5Uŀ%'7P'dg wF0߷tl۱%q45=Hmr_ɵñ(j'H.Ǿ-^39J![olٗoU<׮PK!5u+poetry/core/_vendor/packaging/specifiers.py=sƱ@Θi<ۯ<=S͸v&vy#,(DhrwÁ"Ih&vVћˬ.\E*ɣ<]&)Gߩ0z?GI1a_ڌJϞ|\OXг, TjYYSVwQVQZ^/sըh$ ^U%z*Ҧ,<cAo*bQ\Tul! "yy2՘>} v @ O/ZzlIΟIWJyZ0o6K]WF.^5b\eI7>@(oc LURvkZz>v~@#'|$m`e9suMx:*ENpDUͪ*j%TZM|hˢ}rJ߂k)ȍ2D,vaMI})E%ޑ:ز}QԨVWk@2]-P/iulR'h߻VJfcSW ШU3x (e/a!_0>|c^  a/cc,-eY0K~ܷOZpdA 1X Pb;8k6+` 9Ja1 RP|BtÖ f=d뎭;s5. %>(a Ip j \~ML*:KDoe^Z`w ug7Iw>vzsZjAX GSzl|3BAeEX\ uҤ5)od2!فw(P4}$U׺^ N}z>r hxQp`:08[G! FlhZ6ޚ79N)ڊ^thpM&|Q}8ouҬPn ԩ@Ox锄`:ߊZM^ &8:nb?|`a G-lԺ41TgCP`!z(<:ɵl^ 8Fpt znf5ؙ&)R5Ѵ+Mqno{F_F./'e Weٜ\5TGoo a-tiM&V AjN6_B%Ǎ5!ǝ@bZreL &fچryQ bAie[q䟧pΩ0onj>IGCԼY9=‘>P*@z i/ܱzCZecԀ[_>zAddi^02\(jpXrg\K:ߨJD^aB8LA=!U/%%PTMj@ udxЊOa0O = 6+@µzHXJDkP󒔆o!yQU*k\d)n`:#WG9DZ? в\Ⱥ9k%i4müt`撂k&`ɳE?ܹAX|V =ikh'd*p?FPD5ҀpbPW*52։9лBB(jF{Gd% ΛD|>a^89NKU̻.S}s-|Z4*+}΃\[HxHZE!:rͱe$m΢ UFܙW5na z/n^&)*7AZeA_^'K4/k4pWEmU( 9BBCdI7N444lS+wvl77\d%TN%K? 8~?o?{un'L (1h)5L;_=rUSt"NhRh zY9 P͟d պZM@jc[Ë>2 z8&~^ɭ#PЁ XMlֵ%`;:Mˣ{'0u`󒆿OƸc26 @ԗt!萋ȪNeeE3!:!VF*AʇLSm^VpO ~a#Lj \juW57 :m[  |J ?qk.頌?E>6(kuַ;r$ǎs]L#Zן#3s*xr5  8h7^UP6t6ZP!Ɵw#Rxhh;tUMFeSƀh d U%γE֘0vj^U*Q! `i[HlP巡w [H;i -9l^(F3,9}~üR^ߘlj_Q] RǦ/h$TltR/حMI HB`aŐ7F^Ud6;=aީww/JޭQ0Ɩ`',kڸ!?ňᬖ`aauMlJP $9x?̲^9:a=-YA'IwHec uҬp/ⱙɳZ.! tϭl>F19|f{'x-u lʵ&%1je)8wrM!/cZ€zu^LAO}S8v|~y F{^o?{\Fs<5 +1иc MSf1*!)/|`<^VoLVH\,y8FfyXz{.QEov$+EN'Jǘ:n֘K>{P?>ු+klUH:8SRWwX%',ۃh*ˣQgXߨޥN \bahi bF+?Tƒ)Bf{ WHd%GyΎa;sߠYBU͡@룳 -޳o'?[ -Ƴ#k&? Ia6京sL;N\<5] !Jm FðIM @Dރ N=޴2tb\xcuKv)0^&Q kJց=Deߎw)&aNe aPŋ8z#]-[|C>nCj!/uRo]`NɬwE;zj eCtIۉNBPz]Gvvwu <yZp]F?@ՔĿ:hnvTUF}tZZzTaeۢmW r Y:zMK(tBg9?qŶv匆߃b/]_ %nS! ϩ.xP[ ýwltQׁ5jl-jjW bEAn]G<,b]hz@UG6t "YgνkZseJjTiF`/g:f-Etq1eƔSoIs?j`Py;6S3spĻ7vnIajėhg<пQ\4j妃eMK* =(-]t2M6,S'*P(Q)}{2CF<6#[-YCR*o|Barz1 LazO9؛Sܺ@lPfEz(ͻWhΤ76dq҂C )rK p\L &>گ{GҾQ9-jsrāIp 21|1x88@*QV 4Ƚ[Hzd4M  ;j::w)kwms`'z]u{wF jh掍dͤ 4OFb࿬)o/EȃI_i/6]jQx0"͌=C`ZdU!x1U$H;ꢒP8ƞg{ITT7h"gA( S | [F  wQ?Mr1:%cF;&Ҏ: \~&:ܘ#ŜNN?]>Kım1]=Z {#ysߎ AN;r\oW{14- rB6^U$hI5%Au"Bt踲n+oAc;0 uԾ߭#%sT`BDԤ2[&*SBE;KD U"Ek_00$:( 'awZ浶2E oy?jVk$T20EKݭ͚CKߩn2#Az_.1S;CE(,ZKXE.m']?+ l.3nc>M%3PK! msS=%poetry/core/_vendor/packaging/tags.py;oܸѿ_A( vۛR#N$N@.6N C%͜V҉={gDG{1=C;-E|'uT<crrdǣ3<Ǔ;ĚSY3g04Wތ Wq-@\x33qHDyB7@?L&b;|wpZ9 )~.fϟ6i$%;.vFϣ=˒KUJ͘B]Ո]_r3YEIɢgR=]ժPA 24`jQXGʜɺ@$hh0i^0z!@4|0:mnpNLOpLTaK.I#y}e߲y16@u5h>GޛY\ 7qX .Wh8 L)_ D)]4e*J88VWQ$T2QI5HW⠄ TAUF (/+U'c2 =MĊ#`?KR/?C%z "Biч֕ Ȋ2OjP).20;x˂.hN e_Bo=sSpc@ѯ F%|\k^3psH2N_98%!D7 p2R\R_j/Xh\Jq.(ؑo5QJzwA䕚*8:Xd}o!ӶBAd,=+FcCnhbDIs}LJl0N vT+E_**}tl\p#NFv6*z9u0]KbAflr 뙼0rgS1w:Ί,\)d7H,6U9_9Z8(ZiиzcfR޴}2O E n.}W*aNމF?WP OD5JT#x])+LX@E8kQz):N[rVowf:G}:|զT\oM<'tԙ\V*(hvKb|/H9`,t}#͛͏{ƞjn -Cy-R(fQuqG ;:X~0(,}y<6) p;=xs%ҋ[-;m!(ZmΚK{-xw  Q\ڽ2~!z\̈цAE ^N׍Vkuplg ,H#WB6~M(`V@QYtz)u潎v_BEH}KpLzr ,>rގCb~ CʸPpuIoH$ZJˁp 9fhvgH34: 03-)ME 0Dɔ3j"#荰֥9`qS]ewkTФ "["L{%62J.L(*;Ex1T6gN] 6@ $@w@Cy:U(4:&̈.r' U~}e ~:]EUpN"W4XLRHiC_*ֶ!V|?>ѣ=q) jl ЕQvѭ4Nn|v?p @ܱ.g3wAi]ɈeV\0N>Z{LJॽ兺*֯ 6opvخwSħ o,, &E 6dyWu6Cswb葵}hg>2vS%Z:l{&9@sk+u| Cg 8HNݬMa7ˤe`0 or |FE39)IKXɕkNO[9yfOU4~M:kzAj>)JtRJ4lgU޿Y=C?>J}ʀSm>YL57cԺvH%fÁm(WM˵pø߾`O^'熆h,X<[JŮ"/e|2\l N[vaz-&UTVU" =O쎍'_y6"uV7=3cqQz4~j`/ϻy*|sS/9c/7uQiP.O=jrYF`WmLW t 6T@8+6ebtjy^Wl=`GGHp|e^_\`CKy) 39}f:G{^0[/*WJZvFPgmo F2*23bRY$fUiCk| a+2ġ;ydF挅UIS*WMW<#8sc_`ՖksXHak~9|2 Wי?B b`& w9$ &G%:Wj[W uYQ/ &⒧g'PW2zJ2mSiKݚ:@nSŭ2P5EͬlvYOF1}N#A:@Q ASI4y*8U]+WP(v;XGu;Ej3\Vfp9n5,b2|;bKu&MxKo$thn]g3]n5<זcLzPʠȶd]QlZԄJ H9 َ qb 8`JYҟGg-ar=I5x J.M!Z05l6mȚȑ͡iР>f{WdEG-:2n)΅>Khm )P- tA2-wOw뀢m}ώYĺ.B (=!X:!7c ʩiVi"ϰWC :BAkJNv*atҿ3G(7YXFnXaG\9GZ;g16gg;Z. ZxL?PK!A3h&poetry/core/_vendor/packaging/utils.pyWnF}WL"cp\@.`pIUɑ ew/wf)KZ\=rb1PY!Tk ra)j>B$Ef^odb@gN/Nž))Mm"PXJ-Tdo!r]h24uyb]JeH[(s[b wX\7%ExY>fiW&YF2YFP&J㜎u\#sZ\'\]JY̹BFf޹Tk2syFRqoB<Ѻe  >'y'JIN< E]+5,J.T2U .'𷸵E&4kDgߓ%זe`Y !ӤH-`sK!,3)Ptm\s;=;Lp~⃑Ryz?xýʷ&>_P?oR nB7<\haQ؝ne\*;sGvHFz69G}O=^c.Kvy^H8tT[ٿ﫴n\'7Z^jgмjYgGމ6l^x~5d:_&=E]{!QV] $ttst>ʂ~.FΌY~2+Q,Vi.njD&>oR|aN}o9Q6|;v;iZ=7Q5A#sHc.Gr: e )"gk>] xD_Sñ:gD:ET'ZkF_j؎*: ++60֌\ixwhQ(Ḳؙ"Mʋt ,ks-XMfS6 zc3׬7HBMKbSy{rb%Lr~(oL^ofmi ic}{hxm[:j.<Ts~" ="G>]`Pr!0eQ 57,ɥu WdJ1M4;*lT|^5sL۾ 5jX)pa UFXZ2z=^f^Kui~ij%)E$ewH1I|F)K_/N: 0NnϩMZC+V(]1^ XEqwadƫDdi֯y2QL"[$v~l 9Tg>u_ʌ.Rн)>>y[2 rs/ )e[,P0EBRos`Fm\=?{3f|okYMC4?7~me;[oC0z3a_Nn'O"I@_Ϧm<ք﷫yW}kD0ЇZd7ʠvtmSރPTlHsuE༣iO`"W'o|Y*Q^aG+%1ll4 WEEX*H!Vx YrE((Q}2J!#Li֚ǟϏ=Yʄ眎.Sd*U{Xf9]XyyHiM]T=Q`@#J 2IL:y\U};5z@&m0 4[%=vi1smL>+dGКBԊ(=tIulG~V ~YԾҙm'-Ih[4~Tw$wJ:`dy0a8muφV-3r]ۚ#'y2˺Vxt \d3#üݐ8`oiyGu 9MӞxۼ Ә۾yRJT7cOgWf4#yBg'՘u=vΦ#}{iLgJ4V 7n|6&6&HYF 8T{y5[lĎӒ!ԬԈu 8fCbd0whWupzvZO>Gl~y7 V+P}`5NV.m?4$VZŪ }Sl*J4橞9U橾PtMAWUIk,GGϘ]_+s3mmsj>G!=2$Yo6V5bId0SdȢ2F/؁ݚWlGbpeSVb 8XTAmD.Nia+BTLY.ɴFSA$RP[9 ˜qǭV]DjmětaetShNFJoF;-ǭYZ[|䤅2Ƅܭn.4JV,ѳÚn61oYPz;S7y }Xp6s-Dž΢?;wGpHNӜV9=^Z;y eg]B0kӹ]yE&{J<߽,1݉>2^?c b͑  TgԱ 25IU#;E\oe뙁.3@mB8zʹ Hֈ+õ2od7{B U@'i5N}@ BAԵ?Sr&kCLIņ[zaHP7A0TQZ "Kp!O2X`-jtNN G4è_0}ZuIG@ÿ~,7C$p{5NR.EYa.gίpՋC=f>'-t64.{hizۭ=L7?d<QfF6 oWp3~E;.pMk<]Ob ] ]&>S czӤ7z0P]i }Ǭ$PTuo|M 2c\E?%v ܛLW1Khn!t@@c>R&NǓ[]o׎7^Xhph${gdll/Vұx1Yas b\ΙcIs𨼃EtSs錧ඤMWs-EҺ.;gЦv"1n@&M!XE?Ep`@!3$ OTroGZze {KEmF:CN=MYChDI6y',?paZMe @(x[g@@j,ڴx,| |rqTõWx/LBa'Ks!`LKn_#˺8 6TdS٢hCpEanpqߧ3 uaaZ3#UG%uLꏲ3׳ǣ7GH1#ׅh}$g֡":jۖ[+jG=<-DvlY&EC%+(0qeֽ.Ӭt+%zfa{WwGnoO%8]v3@w-c]ӛH".Ppgnb'٧#M:ّMxukByFڊ"J 2al,>_Ѧۨ8!19zoR<(Vt= DF-eU 3f8h\cr!Tf LO bbj-W{b<*=R1FТ(d8#ZRa<.Q?M[<+zR|) p=LC)C ~މ=Ld@08Z=I bIl"hG0dl4]L*§&|DQxK4%"gYk, ۔nRlz!(b dj}NVPN<a𥶻/5OS؁`bmo`mXU&faDŽ5: 2MfW90ĸALS?iAz)2Iӓ45ͬ҄E^%0-p Or;2u#jxw63ߤi 5-o: _`^X1|t;GW Q{9{mx YZC?UkVpoa5ly[XH,5-A$8_ΨuaPq}A| 翼Js|? u($&M B_ Va<"ʦolvjۥ1Z]P+{WvL-_] KiUWZUɴ+KHVQ<5'Eɥ p&hKJZӀ`EYKߢ~O aV[ct,Q*"ESE}9 BMW\y!f\:SaYkCbo]ӓq%m\?҃d5 Do7R'aC$ĥե.'omzGm#TJV5sM7#$ⶭV%v*ʒL-\zFtqը9o5 }IM)KNíZWhB`ЪEv[:=[ܢy]zm 5CȞ9wMc{tiVj(נxK ŋ.!oVSPcK / Ϻ>M4WEqE8';l.aw!Lí:8cc]t 殀 0Ĵ^\[1A1w{,m>8tl1OېRmaBw4>xX᪆?M$y[}cգ<%POPK!/HF(poetry/core/_vendor/pyparsing/actions.pyX[o8~(T:jam3"E;-A 2es[I*J8mЇV@b<<<\!Ff4JMH&A4Nƪv!ZFQIkEHt$A*eDZQvpPn6B,SK'*xR"u]VeDʭeLk~gzI 3:n3ȈEDK"Sb' .|3̬KF3d0=t*l1֙<,l9 |4jS,)#5\0k?h(_rty^֎*ӶE=YVސR&Z:ÁP}׋ދܾIKcQ,] xxTFxeXb UTRktE [D:BE 9u`ZnX, f/aB~⧙(vב= *xҵT 8AVƤFULT|:|ӨIpZD$o3Nk*⌭ oDmUZgf %$/rޙΝ%mH0 [Ŝe*WvO"G^[ atVQ,CqhǬF3/6]Ho/.-$x4 Ο`iuTȢF!aok48(.#+^/_+~8G']o6okRT\*RNpv0p(ՒsiLd})Nqgņ"K"MMp$0.DYck>;GҖC3| TMJ Yg'_6THW_7؃sq0do Yf4/u`(TvzSe4;K+۬o+YL$sQAٵk#'vχ:R~$ e[m[3"Jȭ\숅iMw's M$Ef'd#&iOBROF {=њTgnݮmA&ĕ "VW5kx#XZ|>iD4t TxCR4_8@3jSt'l+oP_R `+seXc^msj W#{ { x"hԳTc:?ԍ*f0Lsn"嵷>CG׽gL#ឆi4 ;Fj0gJWː[4`Q.7v1\ԎNjB=î2G~`) ŀ7b-MV,D89w(b^]F|,zfem=ɭa[Ovu߬uoˌcix_PK!e~2'poetry/core/_vendor/pyparsing/common.pyiw6d,R|$:v&y{Ưqo+)2DB7JB;^Iv=X\ K(ЊW;$ D #^G 'Os؜1K2 <܉良ChD1 '^gw o3.{A%i,R6[ KS/ Sb ݰthʈ4%!e00rF}s/%iL-W1Mp$}C4-K2Dl>[2+>tE` -L9M8r@.DqB>L.xEI} \y!gנUc(`zhH9:`f18)&l#' U,:Azn^.]2~% u]X~Ӏ:|ۻ/)1W>~x~ܳ˩h6\!twgW`nn\a]L3ݫs#(j 6 T'%PXc"='H}(K]M7~DWA>!|%Neg6&G[>"U1 }+Y,婾WNk'Q"d+:pI[@@1Xmx ᫘)h^O7:vlbfW Eٟ# wU9qj0--*ɛ7Rp6Ŝݮ*y=| b&&ǒ}Ɋl38jR0st4 FDø&0_CaO f\aWih[k)uw ~@P܎+/L[嵶Jl/dn cciB`HAC=KYTd\ۖZJ:=نC"IhƄGE$/$B r~˩f2GvIrsd+DdsےVTҭd9J\֤ kL dDJz"+':(XBS:tRxXɗtozK3δYe;>O{YFF/Lg#@nD󑻯U'$Pw9bDh=F Ȧoɖ'ZWl ZBZD3Y%ӕyOeʟOK Ƥߦ=~%RX*w]}!:i&"B6O,̦q Xh:l5EgjrP\r`p5rshw&)x'uodE0u(af3Y|ȻCz\ c>~%d{M/jG1B'u~p@h{a gn$0w?>d>gv܍5ɿTwŠ6K`Z*N*@f(xM*Vm<4F|>Սo7U2Q6׊|B@9Pd]{%(U 9@Չ\&#֋XCxN ģYcUxs4f |Q+LۥM {x݇1|bww>d|9;E˱Z ~K2r&K ^85g/7V˔&j1-|"7* %D|U0<%$4DFYJ'$C-ߠU#~zC % ,hd U6r/1ZTC k-Rt|݇̚3+1,97 rJˏК@\BD٨eE`᭔Do1u+'6Ha59Je! ;DT&/H=6G96j@8md|9l/s+@Ot|B4"sddQ+DA̅6 Ya척\8}qړO?Jo/,ev~o+ RoY[O",QP %f !~@ 0nJ4@ t.|\]UAzOdE'(+h_\*60)$tCE#:}zLP'''7l͐ ^CE:B!v($p~bd xdK#h:FƆ*-uX?`r%k!@=03b(rdFr~2Y 6|vGɬ_ʋ${D\,9"?]6_0bڝou[zuHCN?z+0Y?TO${+{ۚ_MF.yXzsUz%͟z#(`2t*ɭs7$c̣EAb.8Ean' (Q0og Xt{ܙ ˲_ @~꿬^oa}ѩF{Vψ(de7_h)#˽+L9'±x~=dՊE/壸*g"Ǣˑakih۩!g4`Q.c hSPDQ B) ʻ{9pv>DGhhgO,"x9d mykSAүﳧ]zF&8 1)wA[h>؋)^еW8K܅g/ q9a4lfZr p(/ZOFK*ym~i[a$y'DΚXG6ڹQ!ddZouSqOۉI#8xD,kC-vAupC#&CB#y3һ""Z o 7Lvoma‰s ΄{|2Gk?ee3k6xZE@7ȁ/2axJ;yǷm.f%g/Hyz2M"O Wi> I =:- mP=_ۘb7 2 V9.z}QVꦠ!hnP/^1AϢ` NX샎g1Te{d24'>fmؑu`2j6$VCjY)SP+GVmѾ^ ENu^۹;WGbI$_stg"Ljq pP j]2~ZG%ӎc$A1lipԑzo@9gXfa gR='\Xx>/cyS*:MVR:UBs*]'*Mf.BInRBvvv&ӅC),M8e(V.EoOks4*ZbS NoF<t&td5bgxPK!<ǀD>A%poetry/core/_vendor/pyparsing/core.pykCG(_ё9l!db2I6B{] J0@:7%I2`e~kAgxBf" #AMdzTߏٰI \LVWE8:ȡz7Iz޼k?҉;y8ߍW2|p0̳qz9OR!?ɀ8Agzx 2yoëP/I\^ Espa2ͣ]-,~<ы&35&I9TAYQ&G ?L*,DU̠4C^ w~8q2whj{Q?xۋ.n,+a7zxƣGtv>Ny + JiZ0(-H.nK*q$Q>p:. DK_TqĴ-lu|\H_4(ΧIVňhN2TWЗu,uZ]jhİ3DUt Hp-p2q?TI:M@(켄q`_{4cfI ^4Jż%>&փ;ѫݟvW?zy]x_x~8A498v~1Qw_?D?x.ϯf-UI/ ?d G Жj0nzהA3RO3\D5/,IT`0 3vY<`<:]mX8cҕV P4,\G\+H'iuqy!T>LqC9wgĥlt~ x2D{z:)r-0Π1`vfE FJ;Q63<", Џ>Ozt~.<`'9DaQ8RQ+e9O{_?fTg!"@szN,z!MYyƣ!7ٹ.Y̯<^WV|WQ^?  3!F[rkuz3/Ptf U WSs"Yϻ}tٮWٲ7,b|Ա3Æj`w4Ky=Ez=QLz=Ur78'œ^eEGݏa3|1 "Qb "5+T"@f}S+\䣛*kp擴oc//|ZJcGC8C[<,)!C|@EfN/&"AYtȂT2^]! UZ%,&g@p t(q:ʶFKkxh&T{S)!( qp+*,!ɾK5mj^+Ŭ1_`AaUšFΔI<, P-ܩkBZQncԠs8w Nu6.DfCvjR#o#9un=:=83%*&j<:h|y燨$M;/N@{'?r/'e}h磝WzVg×s{`M"C~{!G]d^d6>~yDDk.kd<#9e*pUq>e*zJnmuk%ݍpޚGݢY"Xp J}5׋8˒HV \FD09i^Y\ "zil¡ 2K&OB..W]Z}b-p Ris49M̦3a8eV4$FEtIVn^5dOk=K xCM, i#R6}Nɸ 9[JSS UW@?J|߫k]M vq)2>4xe{\cHxLG/U<"&P$'B]ERx8U岳"xE6&*ٌ E^P?MQoh6[ڨAZFsR"eRt90MjdDA7_ jQNLqn`YO%i$7y12-c Djcо2G}5GK`Ǔp0v09"%KS\ɍ;ȡ!55֜U4-/aAגN \Dȣ.t06d֕ECqQO/.Pa39Ft~Oz#̭udL AR Puv,VQH$Rf{{ J>]Mz$uEX&qN!szҸH)t u}EWn*sL~OCX2Ywcz!Lݲ2ji>J5x 7oԳa䳐 ׉^"tyKCڛr˕9]6X7j^%z)33M}Y¹N[Ó&}aje-K\𡗁s+-M: %fذ9 |`-|Y1֋\\Z4#9'8|kZQ ;KG~8k5泽;? ~qdo٣c촢u("'MXVjO&ǴtvLLgZV?sPVWBu ۨ%HGdh30b..Flk5V%5EONvJT\<Ȇ300uO7{ό(oN SUfb06j6?@SV,ϯ5'D:|Y2܋k]E߈XUyFgJ< [XRTދ2֔`[l-뉌 8V,N=3JTg]ZŠ".iFal%r6[6"6O#$'2AMYpEE H̀?uU nRBIp.xMDmW?*bߴkAjon>zxZCgxaF<J`gUU F?ANܷvI - ߣˎW.|䢄vz3ٚ'"r <ҙjze G2pci f|,O-йZW蠟ӧ//XS΂bwǾSp ""oφV{bW*R#KOA7u  4I!["ə…a^W_=>/W_[W޽x:|g ,.MRƙQ( %j %(,"}4 ͣ{oGjE#{}* BZXEPI]ZYl3d i,A;tX~&[ul ?jr6ѲGJr'$N1{>0 p7se4ǤmOT".lR=:ao|^N2erF <1$˘R Q禃BAXgc `;f3Y̫ɰTdzD`QZNE\l!qB'=օ*(`gvOw*!A3t':`t=Z7Q҃Q{/FF(zKP^wb(Df*.6̪&b[okpǛ8SJ޿T1mGxuŵt}& pcӹ5r%# 7;F9c"bǏhѠ:N:,Bpm 6*Bj%CH;cleW+ y,1) CAY7Tr5(- ؐ)H hq]RNsd`^AUO34<󎟋d I*9b&%F<` ~i#cHS`K͜5scN.oS'rdXa\TX*C| =K`b OwZdÇϞ5EBUW L *PO-4VnzY8SGK=rtէo+ =Qcyՠ*g6ύ2Ҙ ƨ5T%_fٝU?5dU2wGoy1eTj+jw`&#P.!'f|Uv^턀õlʪPh&Y1#",#tꪛNͶ^IL1t5 ==ЏbDZ~{N%wU9C|Oի|P޶ճPŘ%n vF#G6rK}tJ#ߧ6>J۟ %a^m2> C٨:KM池M뼤's?Ime*cXDGd( pKl*(2@a ]*~#ZnR8~vK!qvH>_kQڠQ(+ZwzK|;v1 }v=?E|կ25IA"G*Mx\^ dYͤ(%lG,Cy]:xx7K;Ipy=H I◜-zb@UO. f,F-uءn} V1ǟe8W=V __19x2{:ӈ5tpZzAÓÝ\?f8G޶[P<&`Rϵc/?ú9LBZvGc?]OH#T#&"$x,nA5}2P$kM$52qo%5) T/` *X;ГUשNI!>vTC@lOM m0{%m6Llg`,O4F,ǔ('M6)*Zf策hax!BzD}{DΚBKCI!(  /f޸?_q{ʭGIk&Ļ fTum֡^׸t='noiZl46\sAvf3nԤHvBl+Duq2`]jfm%1'*Kbj~tXH= RE7v4Nj?6Y<o#Kziڒ_Z#%WQ)vԖb ņb#\'Gx+px"N<~GPKz!p[:} ӝ;ќOIoHkV31w/ܚ8} QKmӖTzA39L{ gi'g5=mݐ,ˮhPE銇j3ik qI6Ekdq5 Om1<ݰLT, +1gv'\%FZn[#oHYZA/POԖ gQzeDV'3hq@X$*ve[{X$GW4&ttX14FzYW\bҖ0(?(50rC&e!\QB!a"_^auBQQՀۑѡ}!9D{q D.G+eU %@ ')1`- umL]eR5&X8!Cu{#SE؅H96%a(G%"%^W,1f<.q6q4pi+(s ;~}1x! d.gmGlt $? >F[LMZ!4:j\$1[QPpDq,Ռme\΋7)KRrUMd6$}"pc>&#k񳣾i sv%OewPiCePk zv4nOF;M[ fcl{ĚZhE҉ZnS ŐQI~~99cvLw虓s+y\G_TqCfgtcjGsmf^m[Sγ*@l_dNt^<~Q{yd}@e֩o(oуN)CoJ ErԠ9b2%GR+^!*WHCޤ3X :RG/kf<9=sX9"%Ruw#Sݭ9`_WA.8ty Ԛ d*dwx (Qrvغ9Wk {!!Q0,eTL)fH@DJwOUpWaZʚ 4R--ܲ#o³ݧ'% P#$OSxP k%3huE i UB>LNCxh~"ГV7>Lɚ"HF<QWؙlEFtWiB1gw@ -MѨ=̑@@2-,b!t=P$T5A۹"Óc ܉#/$e䥖վ<)# w$4ysQY C+Pȫ#}UtJ #hB$@`jl3fp:hG"#:EX5%x,N=G&\ʉ<ƓNMn4V}r峊㽹p4՗b1H5c6^ enW'q(\jmEGbW/="!HN *&[v' z4b3y_qD{̡,cCw]pK}!݌;t-n95'LqMs R>`!K*2pNY 3gY۸cl]hSP bX'~  4$kM1q{iMiXX|ȚI<>깋ȩH{F%D6߭O>ExvDc\SXŖEbOOTMo\a|/6zEϓ:WdTXO((.! b`gB״)uh˝l6:$1&_fƖB{L{ Ke$!w S;}юL\=7p 3@Ơ Ͻ(KRrͱIj4o+FHµU:{!)`)W5ύ fj'Ja@rYfP5m.hևU=и w#um/!+~zGM9ё7l q6fhR]h88FJ\WDc[dxI97_~O:/NZG~SY> m2aN|޷7 6c+} j}AT^; [ N)]+>`aI!B? 1T4:A&)wSfVafcÖ ӎ<G rgĴ[㪬?n U}J$aA }N^A{N LnJw ڍ:=%g=1Spk2[{߯[V}o؈-"P[fJ@ɡu>,,l|AMY=IPq9]Z q&ᒤ/?"+5LTHsЕ7y`RXȆ];'XIP \#hrJJ8s#|NU=XCa:4츪FSO@ dF+Ur6}L2,..#.OY?ՅxaLiTD:C"Pvqeˆlg";QGwJ"/H ㄍ._,UZMO3RDi ED (.'yBIZ2&/Wȩ/I3rT{7vQAP'i) Hù@ݕ:ց3ˢS Grs> ؾCHP.qΧ.KnGexD@&$ZT@AsHKY6>Emla a[c5>sE KP9@Db(74E-X6#eّTQB՚1(9A XM*:VYOFj3]^UQ3>!설hG`8'ԄLc|" 9Hʱl1i;Txbo6I*ck6P1b[w[׿kS02X ]ړzܣܦ1zlbj%_DMiA/OO-P0=-TzE`8A6x+ʧ(ZmEEe;:r!;&'8Փh?,}=?mZxǝltTp]["$AW9Yiɮ[1.$;mbۈ%h /ڶkz7,:-J1KF[x&\i*ˮ'lvd|.,+jB%:,G*#Fs19v]> S@~ Zm0] r2Rbb.CXA ~eWԺK ?"YЯϠ[S`D sz%x"K3疯$[Z^t6@SXm*]0fes!YA0$hیYZ0ҥ(n^[t';n-pI Wk:O-@p^p꾸FCbQ}]ulSvGOQb\-@6e]*=9<khBm[gn=:[W5A^C1/`wpD5F&suu,g.f (6&RN nmsA#vO`n7%#畵`7ZCSf(@X,`pe< G,[ʬo^FOK"ly3]h  1;@ס퀏v,Juj'|̭4;qp6鲦0zo?PH$?x,d-?ճ:3ѕr,^9 GܱJ<<46j*zD1 Iԡ@x{.;3Ǹ+RZz t={kܢBݶPw˾P-`VojnQ A \8b-/fN ŧXGC縻?RxXEQ؟O!00HDԟR끑{M.-)/?Nbv~/9wO:шZ8RKj{>%)Y7w~W;Mx\ľ\0 HGhoL~c8xb I&*@imT "CQ(!Na#KC=&bg3Rwh"BT^,LsQ*iv@T(^Rg)~L[l,w-zSݲ}Z֩)ܨWΖ_SnNu[긝j6 WIܤrUPlm/N %{B*yz*[2f-<++u#5WGu9}[lRMB- I PfʿCč!td4dn33JbY:1R+XNHP2Tmr&ӐYk\n(:;{C?CRSlB&7bW=}a'K,wi>f& ]G +=_%IfVY *@dp1Kg[ nr@kt3"Q% AYbd`eSKNTs 9ԏn*Bv U@vB8gvygE%7PH 6Ʋp[eFq( z$1PTh 3^N'+=4<|, nD0*k-hG>.‚:_qOEα8P!w)&:^&%9J%_$wMD&TA"ib=;ta4):j=vO/+347 , YEm.-8i5cՔrYM%TOZg@*޴[| η- ɹO m*v[$JC[u[`N0MbJAc$:_&eh[<-+ jJ/q9Y)R,,fadD'L šMc[{'#A93uaQ9/n>QQ}2bƀl9q1_bac:?jv;˂| 2EЖؑ0yf=s%MMqM~a78ܪ򪹲?)Nu2 J&Œ`~%2pn/@O_J+u홀V;*$ Km 6*_w ScC{+52 M(λy9L2 yٮaq7n:rDL1W7:n$JtYeU@>_ M,$t6S[OϟUf~Hgds#l>^i4_a_Lӵ<4N;19Gڌ)h>d84GeZěx9!V*ѱEvޏ^rvX'R5JO#(b>*>ɞv njv[_%-TVAt>N'ǤUUߓƪϛݘ_%%Om̱1U1 =OlDIb,,46U7tWh'qr(LYTȅg/ bXpg^se^ t^^z34Yx8[T2ge>-w/;ʤ%q^Fti:2AʾC]P/b.9V̼ooNub9!4ܝH>I V1yTT ]%7e;Ph):3 VLƨX%)܊%V,2&3g9.[n+P3؂v)Q;dM-Jm[WJ. |`:ŎZ;STr0PYy+ÞƂD]eﱊ4sJƋu`8k%lJjQ<98y=7r( Z%+~ Jݦ_Hf+bhaWg%Y./5A G ɶq ?3S#gӀ ٹdQr~wQV§ ;GGu &Zw֬)xO,0Iф{T`jy>䴥/](ubaV~El+ӳ1܁/"ɫE'ɻj%\̆HIVBͫ7-S@2|߷ ˼F/i-[Y2qZyƘZ;Y" es*#^VRdehU2b5B]1r8K=SxjVFرrq"bMõ`)(= c:5:`%!CE b[}͸.uMS ^Z2 $`טmZ& ͅs4hP ٳ)[^^܆ګs+oQ964@R-Jߵs.Kb*)=t.X)AN j,ćawrt54BrlexPV dXagh+څAT1\!a]>Raj0L)Rv,fʯ$EHHHV !0W'aK\DB v[0&*nILoEJ]up+h4ԡ3D{(ȷݐZ3{mn;kl) Ij̚bBwO2m6ҁVN4Y| 4v:;Z31I2NBh)`)i_cX-FM&kTbXL!{%Ø;Imv#('1DC;e۱:iA+uoN1jԹ|Xj}QSd{JTqt2˓$8Q6R7eg0t$Ͷ_,L8̧#F`#SU!1bkk}Yby&t!ljYE'$h>ojZ<H&y׎{pPl@ ny:;#̮f5PMj7d,)JLP{*|xZbV5x0yVO~T-3Pm$$1]FeZ)R_oi*GUvveoCX빼wi;&"K֩?SxpCXdkt=7tOGkoKyP~S\hy|gR%وywQ=`_֒j>/8 %lB#>,VyiE;H !-na:l!UI!Q}#j0O#Z utĬBAMHVbĢU+W|J[ܘM,:vbuYѣUk<ڃ @~X$Cپq9F]QbI} ާ)9`ۼym?Ļ~vw;h>9bNؗC.E>=sVSSYk͙XOa~IʬUaU״mO掁v=ۡЛp̞I2}ZmqH'tqXLfQXyycOIez}L"땳gKprPWmp/Qz>Nck+I_m= 蹖I0U9UQ+@sl~ZөͿYM i6Oq7:P\ 㴠7`Tq `9zYh;y ?56~L,f5D\lh=QK1YQWRAZEsW SaYZ.U_ԅ㸊iut[%"}5nc +O,)<{k'0P`9 (~'jG% WJ1. Misͳ8KBX?/{N_؊ tJ@&s"X j- [ na4ƚP QlA-w-$y]uAU]O?~} C[4ǫnN;[ZX{9\ &#va;*o+yA zA$ziYiCim"Y6]ZtZ :@<{[(}ܰrO*ʖYRm.W73$BԞ?tuT )X0>"1Hs~>\cNg;l -UzY:x|u=mmcsu~ԂV_ۧs+m-v̓B=ʿPSr5ᖕWjA7U/HI#["шK`rBH-8s{(Ez %S2 ՏZtef M"Te(Y>3hLcc.˅; WY*ETߝ*0ii˷ӠTg($s (Dlzu^`7`-ðbͯYaX{ ,[*2γ]"Nu4E C .\Hv!%(Ћ9ف1j+u8MN`"W wDHJDL &iEœ!~̰o3"eos*ƋR#`%>"K0AA8ESOzim( ^4ʅb)ђ6{Ϋlu!401&lɍ/-7G$j{PGg;^Z2EQm:VUtG늼B *i\GJ J[C])s&xc2RN9n OPm7c{m:ެlH[pm/+qdĘk\JU)*QxJ7'5!\dS{ ڊkA[szM6և^q-cZù w:W /H@Q &EO$Hy`bd:S65PЭcD=pܳ~~n[0O;fWHel4ɐ1듊 6f:&;}|b fћEf I/9oCMp+*jfG6tӓl2㫾w8,AFVlmobGݶ 03.:x#l^ACOCT(lC4IGPi[lV~f[j{`za)aĵϮ YC1h! 0} p9C,NѫZ$$yy3m1gTohJw8g{w~:8}?QEgE=t DK IQϔDU%P^d pmu@,_:&ƃ"t>U!8̩bn yV9l>٭xLdCw5gx̆S{2vLN_g EN:yAiꝴrVrt !`Q.Ss.SԼT83 AC$ ה.=4QaIi.?X29^mފNO=m*|vm}X/~!+lԡ>\lZS$aunpsҀ&ܬ?D5է9?Rl?T@YE+;WДs3af]qm WnD Á|CAR`&Q?[*[`/]Om@AbVj[:^o$<Xw5iZ^g/GpTwq< j sO bS{-zIX4NǞg0E@B}L*R1;u#̽*/ЖŐj`GӐwn(]Oԏ b}A> נY9{ QZ|QFqnS]zy:ҡs'm5ő)O[ "T1Jue[qeK ^~RLE !,Jn9:Q3uMf|\-_U6uj wǿ좫j5+oi {ntw1Fj-EԜ1ǠĪ f'{H qNKt1֤ ZpFwl}qaL%ťݏ'Gl݊$3TvyPVڴ S\ a@ `0'}{qD BulOkwLR)12Ҽ"SAV?rLLq  ދ1&9g!JRDtl() A&ήsǞλټ$aFYeBH8H"WP8qM <˞xLO1Of9.Dy).^hM]W/ )񈔶vF#l%Q2Mh}py8E7Zz~':q=҉WA 2] Hco񌖌G 2"鰖v{e#6Gkk~t1%ִ5WO6zR_;]2J&/$ontD({v3mP迸J5/Hs_$ g*vwen>fW_ɦZ-a` w>szUkq6φ Ҧ%-SRH 7Ӌ|rL "a`QW _Q6tCS8!6*q&s*y۰) *B SQB{r2\hi=E$mv[l(m'Cyol>zW_o[2Ih[U0E;)r8ud-'R "-lӁ8#+$~^._7aU}c 6FǔBO62n=9psVi=֭4_No㗃g-Ͱ0 մD˞}"{f3G͘Ju  e2lZH=c5?LgVG$ρ3jdv~{yUmJw{SzywʚxXYwmw&#{`ļ"0fVgͬv/mCBʐboᕆU  9Me3P@`9=+tw^= Ssy׉p [6WHI כS?Pl*̉qSbvڝ' )a.K1P^_F?]Y0\CHI% DL*[b S* ӝng(ka;cvm3ScL7LCnŪͯ]~^$P6`b4բnJtz ]1 8V`^NqEL1[?ouC~lz.kgttUW2)VTgf~)?:п0zR]-H⍼ V="*kzA"VCi\^v#=X5`::޳i %Dn_x!2]ZKtL=Յӭ殯0*Ҩ*vXF Nwř~Z]I_Gp]O. 7ۼe΃шpVm,@FNϸIUp1ڀ]]a{6I ʘD !':nRSR}R𮕔hU#N"w,P^E>Wxz2fb7A3EII)_ёScW4JA8<ϮdJ?^XXAĶs=;g5P M9Gvt;|Ll"l, ذGAJMxE?z&kSc)0yKREqŔV ˌ]܈}h^Dn+(cI>}rUUrQ>,3*ϋˇB".n1}ˏUMSG8(M<['X)0@e%*Q^9 +!9}R)vR R|c=?Wiy,vhI=)Ep1vC 0L dQ;#Lrc,I F';RSH[xaqi*\Iv8Oaz,ZzuqoEj)\6)ƓwNˇo}x_u:-uӶNںuҞ2\ǁ(͢4uޗ:>_/r_}y]+8q(7Y&=0SWMO__(y.#D|(vmreCyk9N?l֗⿚Ӎo;XHh'1O1_QdVCd֟;Evo:3/q? Y-dΉ%h0Ek!2 G2\o}ja8gCQ8I,ߕĪJc؁$/02S9]#)a2B;q''fiZdqY@-lS:_1KF}~KpGz(@@0jDKa${Bh`ۍ0="o/ 9le3i7ȳi2C||.\ofy&YhzTxmNIa)C)F)FY9niQԄMXgn@y.Cn^r,7=4jumλy%HD8TX1Yv^M'5o"=\):20(VMwEFM/zlVɴyЅ7Țm#UY]6 9.f#_p<ˆ9-'S+}KNs Hyv&p\r؉uk2K(VYނ PHޡ \]Ho;t:nԡ׏50J,.(~\I=1=.s/(F$j[uFȯE 9zU hV:~+NBN{R:Gǝߊq:7I -D,n?|o@ݑÇz_&~jWo[u pUR[Ւ-fgM/8=mΚX;#Qp;uE1gF?3sL?gA? ҷLlf=ŦJ\=)$gH.;jYh$ ی[?n1A2|ma15b%T7ԙrW)gZoс ?1g,Hn,Hv*0f.:i6'bXm֣<ÄH4o4G?HYgu/Ck >l\ :`oSV}.0:h4ڐeLe)IAIv:Y4O}okϻumʞ뛑fYR Ҧr"q*=@:ݲŵRQjΏԟkbsEt55>53yĂW-hrV ڎ/P@E#GWWo{ߺݶOPMHʌ(OۖG'=-)%D$HJ^'9uWl,3ZH`/jW}ņ&TvXŇiU{4$KxK2g; 0 zPbϰlh%݂♉-3[*_B *^)H7:l*xE-[(ǫڜUT㫍 \j"Wv_or'bgFsg0l1`',Z96>Pfdd@ <6WIA0E6uX;vmX[@f2[(¶Ţ!uIqITPJfC۷oog-#iNQJ,#O8IN?<'o«mƟnzFc?}'G1=Kߢ7/.?~O9˭JwϞ^>^ 7yT~_ex͗G)9jH9Oӿ4 |ݛ4_Ro^eȺHY;mZp*bǓJD5ҞU늙\^V8QG'OOgϩ,bG.g>G:g Bz7p5 r"qapnBiS-uvFK"Mרfc/L `T=DIƓm&JY]g`gF6枬lTѬFʡZPB)w[kdJڹjwIuE|mJ&%m .]qU~T1dtobnƬȿىw*Wv%GVg%Ү0 !/T?.b&u5+5QՏ;r ѣb!vw-W>*Pu8 %|ZmŪB*AP~N'Fg'dėLkFCFF{#絃^v)xj2]NPקo YrK3n\ *ZoNOoteqs@aIݫYRe_P;<wt6sHIi_,Hb-ФURʄl-I;n@7h6RAϠ+gI㫴lz&E,6y6 f$m]?谪O񻤌[ժ!W>e,]٧4.~*M4v6H@y!HZ$4KcyjvV~V_֕lR@xϸFc5DZLTyJ53Z=]튉}2Cm *% x HߚF+k(_|IO&/EВM( K%нW2ޞ R}ǻ'x>G&"1AB Guw\֘FT+DG]lL h c R]EYIg2 fΜE[=rМU=vϺ.3PGet0 d ]ۺکdѧR,:0^\u5*up9%EsIJ,ܱ ZCbKǯxsrkMKCiYǀ_:.'|Cގi oxš9v6:^\=VE]޺v8rf 9)|vDV•=Ň/Eg I>'PoJ%ٕl9>%bN$9+|WLdP$#e`푨?*y׮v`0Js8|6X 31=g/)o8>@X↥LY! B@'J|O GjXMS*S}xԍJ訏@-B "%`68 lW%aQRd9dTc-rFwHm 7jmgAT'AJhx]2ٻ_VJG ;F IfuRVI#ꩥ=K˔A&'x)q${i+cMB:!@#5uIǥO&i^9kMmzX] d/=(ErN^u:LeH:QyMWfEx} FKV:Bd]H_NoK`$.R0rtps~ENp*gn-zqLȫE63cѱ58?kWW~$`ֿkmf3v+Рܗh;Gy5:"xdɞ^NRfҎ꠴gs! HjDfN>P#fE"Hn<^EtK}8%nuZ_A i #- ?C#䂲c fB\nku;/Dt%^ꢬt-$!I.0j)C=fT'*6v ݼN5Ŕg2|vg4V15m\@54 -]/y;Ƶt\5KN&TJN؝>!o =PР̤nhRU(g u [ΖaaR fU9[SgLy姬UAhiRꑓUhEꃀO=dBifn9:x&j؜ҹzvw%1;2da짣I!,!z%wP/~ű ʡ&3` cQ^uy.|{$9a Lj2Y)݋-Hs;]@qJʁ*W$ }\8%;mҋ # \!/Q rY~A[f>3AuM 6;X3%_AyQuu8 Fotm'ʬNB s`АT ?~ L2ܢ0~ӧ=nCdXb8B"Ҕ ~s}#p/IPZ7iFj:\W4҃d>2Uc`\lG{#(<-#GZI;@MH*`AyRMsQӹ`-5<kr5`Ϲ ="eKv9Ŋ"..zg۳$#c`Qa=\+l<+'2m(4aV+h:: R?\O|[RSܚ‘z3<|Jx/ ^S9hIw;OF(-$0A3KQ2VkT.?9frmG(-@b{Q뗖\ՊkH1 XN V[Wm =67(TT>$CLvkә\ 0%Fub&bT31lI&)Hgz:[Lȓ^#y sQCk{~t`/C>nC6qm!J&J-*f7Pr4zkZKs=MP& L~!D˱}Ā?9qYSVCjv7O˂-jbv)7^*c6 [b||d( ڷc"VBgLˈ=@03tعR!5 3,$zΏJnR7؞Uekq Z7Vzc +,뭾?[Q`Vr1 Hxz5` `jӔY5ަ n3$J!+jm:t'ՑC#ot[8)iǭ!vMe 7sSа *'WP/oŗf'A%ͮR_n_{VݯYQ=b]O[TX- >.]]~~W]] RËrHsMy\M0& A6_|wg?D?{s|s|G?Ä\Wf z d}ʇ/^Ee*A Τ_΢SyVtA9hctk{nڶ5bGCˣ_T }{dҟÏݖ]>i9I 0ĪɌ`n oHªJȚJHU>Хi+xԓ%/ R'C!`֨1fXD5l ECf9;5#,/E;[\h[&;,dp;[R^ug7hs>9ls7ʌ_(][[AQm Ikөz̵ZڌSG׻Ns7.)u}݉ye/xsCӡ/A`(@<,×a&ϥw<*Y /+/r^䮽c>U;>;C%NiH54DS3=moO%Z9 y :p)&b⭪tUgmv{(xstejԧes\:x V./-KM6w2bIk};Ж^t^$Yx&('&Ά'q Q' E ;u BęEN, wER#)Vt8Kd6SߓE]2۰z#D-y,g*xDΑթmLMHP)ʵP4Pt8tKei&?+_}hyCp2okaSYvf@ 0' :7l *BfjUJv]j&7*Vkmy8m#U* @M鑓 Rs~7QuRԙ4A{Q. y ԵeRM\vEP?ٹ#Kp2ȁ>xWG7F3fF#<:Ȝ_`x+tܫ)ssY`n2EKXzEM[Jt&|-驩'Gݜq3':s clFof0⮼Oם]12T]";Ρʻĭ\*XbT Mu +{ f_;ᡸ2Tj:aX#u9,Uu7C?)ֳbb$.ܺq,oOQSX^M@֩N=J IC-XYĭY6P`p-Z)?{'Z}|Bc R|nSf>lBh 6%ME #cKCRQnlh5\ѺI,Pؘrk$qΝ훆׺0>Z HZ^cBA_%BJ2ЊF7E2^N,oƐrGk$*e#0^\"[C4>%]OR8U)LSl4%*J7G]NO/-Pi䇎 k\QM ); H9nu)ƒbǗ,b{ 7Bpߨ`6jБ"[@KF$KvZζ DY]jr|f^.o{Umn[HMc~k}ma)a_=,C)'xe-lq^g*ܥs]׈Z&Yv$mPn;^ݾ!ޜ^/qg+qM\Ѯp˨iu[[6g;^_$Zx-Y\_x篧F?RaGg{ txGq`ضmefih ^LmZ}٪+0#8!bkR:Sk`v,}f"T)7}(p.~Xg%(p1pJ4?C!S6F8㋮箏]xwv_TSmzgYΚN۲'!7n$Z& z,.$4 @B[N(v!N E.J%ᐂJ2ׁ3Q9ϒ.ЅSG]1Q =+={-AY&-(^ݐ8=.ȸh6M:ewDiGm@u*bw7cM1]B9hZ k|WLkh恉`Q:&B&ы6`-fSSo;J̶S[ f"={ν t AR&QV#g u)@Q\<@VNՍ?޲ R]Lq1 P]q/E6quNUxɟZrBPa:  ڛlY߰ضYVJ#%"qElF؃{S }]K˩a h1!%ڢ%R t"ju.šV󮬜?'HO{M?Ƴd*ZG*TK~f21UsƯ*EŪd3{.2"uì9GH#]D=hVsZbq=IwqGp4H\ S3N[CXH~6țw~-;{0̦S[) q3@r?49j5Φ 9V:$%$_aSZ&mW2JEM,g*5b5Q< zy)2dhjZqGQld2*$Ls^>mC]y,f:cxd<$q}$Jd0Bel=r*µk Pe!*rQQu.zÜ+()da uQ;(Cŝ^hpzo8xe1ZG 4uLF5D.ob;mHy9;cα{RL^䋟uuB꽄ʑ*L)DR+Siy+zgF&L:Isuڀb%/Q)bV %ٲRQ[xLP~G#ƓeU}d8S(z6zA4wl;,nbnHiWeĶ\Pqx eKja+A<*G ~]gK+ ŇA+N+ԫJ%8S+`b4޹?,F?.q{ggT+OͷJݲ*u*-wmy U㻝!mMdd"1=ΣaS to'|-Zsӷ`BZ- V?VM/:<vu'pXO`DE-Hi0I_́tE|䛯2g\DAՖT/V`XOWl-ƒiF[c} (Qcjn-,'dH 8`o_yqY<ڏ4Gwl@]xDLdVߋ:peuv/@LAsyǓ,^8ZTV;)7`G*a#XVRI)>ϳy|SXU6ȸҍrmT]OKvCX!̹̀%lֿ\:tvܯֱQ]hFfγ š=ZYV6Δq-業ܣI/@C+/:a1PD*:]DR巊 ]GWzz'h%PZwDt7nG SA1P>&E6 Ԝk?Տur8d:nuw+O a~`[ZdptpŶWbS˽@vaLdB [WXq2qbF]U <1AuTGF *$4x'VD'5?7<ֹV@P#eZiG-l4$`e'fipk(luVinԵ F[ hUd ~il8ûmRhw~m :P_Z4mnn:˜ 'DY!^] l6P5?'> L q(d SN}r8?Oi~lOAZ jTd9b@HYNO%P:ʦJ!1 nOu)3Cf,v6[J"P`G!"NߴnK!Ւ +:gX"BtH_fx1-L]1cKO~:WcJ SgËi;j}diB3ON6g8+ rD)S)W"15iه*I(ލ70;b$hG蛤 @C}Zsgc?6Ӻ- |_H+Z8E2y\\Z>{!< t Fcx4p=G$;0kwn? +x< _q B6 qb6DV^ %0ڹml@m!72Cl/%p yl9H&i%TM ތ#X)#GPq27U^v><FdLfNc Ie-Hi?3-Y,YXVlO4zO:˞tm[:a|eu&U?@+BHƭ-X`6719<,7=]yU$ˀcјCj[dֲ%rItVwjs㼕Շ }C \+\a--T^9jjyeUwVW`S-AD:Օ@PXZB==A8vhHx{A|ꃺ ͵@-QVyct܎g0+he˅xrjzCOK9[S0a:/E6CİML-#"ڻ7d/UeJs4Jpҳx"(BM2 .U<%%S2qJ-8!p0hgfWа%<91dH<Z|.gٕ>1:G9 8=zd{wF3ϼ]JC("E0M/WHz;aʹ`7 FO몥Z`tKA37$k-:C2:N!]d\k OpFa{@@š'0hF.)QTX*>^(jnl1iN$$gT|Vu{/I6YYȤ-9 =JNjl=sgЯ$q ҩě`*2ੋNcDƩZs:֖n5LUH%UٌS!+.:ۭ+ g)}uaJobG9ȻB@&b;ݱ8X6;~[k ֔ܣL,fe0BTk+I@$>x a2 5+'k6F؞,Fup 4xPJ¡_RoSɟ"a$XNO~iAC1D3N3\jH'Ƃr& ڽa Jl(F^aO`ѸAD n3-_xSM+AZnR*i},Tr Z-,NIGRY${n$G=8IrHP;t u% T;%Bjk,S80B5A~op0NLp4=5ժ'[Zaw=L?بۡ3"S" x7Ѕ!,҈0a%sYrS_dtOZxǖh[Y@"h1Hw*hQ-7y4JY7vg䯼6mS&գ倪7ʹ,OX$I,U2w) /iJE(Dms&^-n%5W& W*ה  &뉊•N/Y}=i]# NC$WmMT/c+1#sNZ4%S .xNxx>-6Raˮ Ҹ*0jcIBIaF\,uD|1ƢLQm6/&1,Ӹz1*N~$8;5x*E&/GϱbY' Yyc, J j8LgDl_1rF}Pdpl^p,"p[]1![[INekn9~{V 1P\w5V`>.x I 񣨚" N},MH%ɐ:P7E:Fxu'D a:&NߤZ)րG#+z2b(%}_4@K%=рLЙǎEw*ޭ:K(#Z,=b(T_ r:S9bQ1-[AWH#mu'tFЕqI|dP -h{>A(JڌTc͙i<0ʼi1Ig%TfN"dSH#jQr)iduiV,E t-} Vz`w$ -MXydś-svkWIEq1!F@{6JӍ֢%۪^*Gd٬ʬT7z*;N,돣)0vb$O@k)Q3ZYdѱV+~Q , oǛwlg5kʖ['}4TM$v.q奖5}?"Svr8=}klT=c@.w jM0g!4RILϿDؔ\jHu%0־ ZGՃ}Οl 6ZF&Pat yB٘%2RD[P{pE+$5ITz>Q9- [rv2k<~& 3Ha K Dgl ͻ?ۿuZ*@N{OFڛɝ=>A]Gyu99*o~{rj %oPla٘rP7x{H\I?j؇^d3l݉mQ<;Gы/8jˢ&[o!w<&WЅ@s#1Yu,2&C"r,Yr_x״S{,tIo+]c lʈLr8KO&stMEJe֏y[oLIQ@"zyvGlgZ+dV'h9i-ZVop[OZy-c_~*72)!|~9ebO`|$NEO(hNвK u !׻`)E&[@EJծĺi(2 ]z1dX- ) 71: %yF Z*"y_]0,ۺ~g5 ! gxt)]J~cCTqP"FV.H `̆[n;4S=-5FjV: v(2o(RcM?g%]nty5I嵕+lʁ^wPn4BRd^eZB*SetTȱL\7 s8( «qr .a.4R>OIB}2&nLqlȬ4_cZo,B*') -( @rP=8N9 N/G%Ս@,@aS26 A ð^xDvrudRԛ!.-*t[٫T XB5 [5Au ];Y-:[*&LVDq}mG[h;vҘNSDg5NVuAFpsH,!FP?/9~: D?p_@O b~b!)9*^A忠v|twD(L*iM|&="y c-%0Qz#q9Z~籜+hEix.~ .F5KxDS1-Mq713$bey*grEAcW<-%.ABpLIGFUC)=W_F?:;-8d3(Vvz;{}{~Z]-y-Gwݻtan>ʖB(P7P֪Vn[*P[VS7}qŧ|x(Ɍ]dWcw,%0afe1%íM9%~1yg 34gcSQ5ȧv7Zl:NŁĠer&($g %KP(!.J03BS͋M; zLj 9> @;'\L Qϕ wx`FtlH_ ½-'o^J'Ri`k`:͊DKS"3w&p_o%IW4JNz|@GJk,7;tXPK!-2U t\1poetry/core/_vendor/pyparsing/diagram/__init__.py*MJu-ϲmUZ{򗟪1(㷝lWj~Ѯ|)W:$}kkYt'''<ޙs6m!LF>y?]x^e_ȼ|I}QgO_)~^nWK$:zҽR)F~g"_ULT:/?GUer2FR|6%lFwF'0&jEYmTn.EQZFnڬX>1TU iWu5 x=f(.jUA L:BxbLktD!8I Hɉ40+YZ''䙨J a'eVmRH2rDl:OZ)f2a-U#~ӟV^ybP;ٝrU>ţU6jh"t׎'" bB^_YN 8kS5 3_yF G 7E 5arT.pq%m[1/XHyBtb(dQ,gYKg;v3DD@}yySAqUCx*.y`8ǥ@ӨB9+t~GDi0e ʪ`rt˼ bHXXms ݻk 1v|iY/&;ܮ6$OAΉ 3G)o e s7VvR @8"Y,l_mcu̫ޠoT4G4UiWS$Ӎ9aj<1nЩDG^/65'BmjyK`_7Zdrz;e)#u #H/WOO&ZA.[znnK|@ySgq3QNj yfs o߼Ss$3nQuzh׽c ,?W)*4o-7xq}AV P~F\NvߠDr-IK(EF&]]ӷ=Y"띻qFƬA'j6OT )An[0{oJ~(G&37n 1tQqR,@ RwyNsPz8 vu϶$M4f;_zxVિ1(À{4`+:DKLɁ12Ki9hFduTfP0sFS+~aùUr!)fARo~zs6lz݀ #^`Q$C] U$]ٍ%kV{̦" rD߳ݭfFRyL^R3_;LkOL:c" fϛ\Fk@V&4\*U톄t^Ƴ2S?"- W6eU,EYVc^iPC9b98Ƞ}|` f.U[qx1XvblfB33>LR5E)[ʈ,iFIJ]d0gAk(4BZlZPNF}$2b]@ g&фZ;gvl?b@Ų0 -_"Dž5"9"C?P7ðWJ?!kgGLY7(2Jt"=NǑrA0 p˝g5rk0lB{<&r'Fnb.ˉ"K7ѵB#Y`tfu)s}FzssXMrL)_Ր3lH VĮZݠy$Xl8HA8s\O3` y[J9r[-a"4d]B3@HPD$/4]I(q9ܝc|`{T,H!TבIP~: 9=WR]Qt!C W{ e9 TeDnt9n 5,2N˪.;9T.**l}9h-R``]p !>8Mj\xq<f#ygݶ*0$pѨVrxg y{>ea5޺y3c;wT9LDְDyDC<']xҼ۲\$13 % 򸁣|پ%z~~qDc %C(z^ CvN7pH3`B 73]b ֿpޖ9Y94:4`|$+7Vݗ ]؝X=]km/U>4;@a&&:BO+&&Bp [Urep"DGx발YH8&"I9mOw>0T=/d: ݡ鶨iZ`cbH(Q{"cRtN)\k꫋ʱi֦ piq`)}F||}.O[-!^| +-Dy!f=};iϼ=燁}&^&,q nEԶ)yZ'e\"7AR <7(T&Ʊ&m: $`e 9{9G.Fn,F0OXO}hpRe:U͠)?C%P?MU{HjS]ؘ.xD i kg)ZE"@١jwe)Fߍ 7 4FdXUl:LW_ F,>D<Y|0HvC a߬shwV&GJmTc8 6`b5ĵk6LX6uK)riNEY kjKeJP3VĶA%̶\*f)Xߵ/\b:5)GAF6Զt ȨaO.rq+,Yty-Ave#ߟHQm nUإ)QgJLK DΕV|LW%PE`nP12Nz4vmBD<ͫ1k}]^RBݒjddNm*Щcݱf.pmh&i~KүPt> |Ѵ4ᆑ' .nj-b ̦RlfAk_=Y1,767;jUcsh19qa/E XKMfK_2uGt:ƽ}֡r^LZQA~;E j'|2zt ʛ\v-f8Sa%0 x bo]?X jHBhؓX=;";rVTLc?{z-3-~(v}ǵ҃)bN 1f&Qs6U+#0yߛCGgY9ڡOi O pAi)LJ5f\A)l6v9,*غC6r15?`r?SA 6zӔmkQFm۴)(bܸa^TvYțK9 F,P>$ӽE qwWV.Qc?sr@'# K!9qňoQi+-iЯJII}la|O|hJ2TBx[~gmȍט.J8pպQRI8>}B{@e/x*+d}!Y!7 &&|Z2a0;=q/ 0W+/ &sN3?DPZI? }ó~qǘЅaJp1ۿud0Bxц" h DZm{ǿ2`q GSIvQ+@߀{՝7_q?rыV)˿~]\W?} __y[I x#;υI~ĎԏMo;?e((om6vX/#f$HO$giف;z% 7Q9^nLNѡ|)ݘth#:XM 7CA̞cՃ>5m:>,M;vMW :ަ2HٻzL>.#"hyW0}&nQ=ޓ YNې׺8kuAE5%ю Lr|K`:?} K1 q";sɻaNɟe)^27\n#Jթجaf78uh-Y%BԉݝݙVSd+W~^)`^ S~ˏW ӝ r۬hqʒ[AhPYk$cUMް5^;_@I\MAC)XM%tc4*w}惗:>߉`/PK!6 t ?#+poetry/core/_vendor/pyparsing/exceptions.pyYm_iKiX 7H:F jE$%wf\.%|N zt33L|HEmJO&yYW0%'ݣ׹N&U,nL^v&)+r)쯬,Z$(ؖ*Q\n<2ё5Wv\n&IZp٫NП+dׄ \Χ=?_o^Eb#IxQlJ͖w!&v{#.HnaEGR@V8:/D{F?btS8ހ =RkXЛ٭΋f&/uvNF(X_r f$r`,-AMjgCJGlGk'Ù{rkƶ0KI&>X{bZb(6 MVen) > ׵nQѩ CF 6G~ b ?-OAL9DŽ$W' Z!I(Q;JB#zGRBFÐ$W80A␨ $%WHBR_dE|6t!&WزՁK\_M'dWeIyA  ѾQ-бFDiwZ^TŤ 4ߕu^W/y dh.qc2FH <žbtU$Z\vb;On *k lv+Ib2[h]R"hc 9 dTpS)Xzzʳ^L|PP tq/׽ǹݡ}c`5[X],^\+'>rB~c{ĿV a+֨?L8^!qFsֆ:=+]Ѧ!]CY Q_"WeP 1~Sf;~zHo%cQ rYݬPB*o PA)V;MaZ -*.dW-0KOH(*Cz;U/*gfV$%oP6bwj W-5CG=>n|l/ԖHQS52k۩}(N 6VcQA/I)4@B)2ÎX ziE;h1Sl]⭪:XU:x1Mi ]uk jl -^ZZWl+L3m jDǔ AJvȏoZWZ E%{ZUSb<%OþCL# Z0/W,}`Ыe0[ /lA!Vs` DK:y@( բ-N::B*2="˼ڱOv+7c7#;QQXb5#kK[ES\U,@mZ=ÁSFclAt=)[7;EW BXn+l߂Ϟ,NŊg3:Foa1A;)$j?4`OȮ6yiZFtƯHa?17[( \T?Pk="@$X)2*T'$h@Hw8\#H qvW;=\z5[=%=xx]{{ѵ'M5@qPK!*,٘(poetry/core/_vendor/pyparsing/helpers.py}w8+0irU,ϳwwe\ٷ$fLN>IJzvޝ~%( B |"nde5\{bYPuZ҅2ͯ{yY,P8uiQJhG43'UT7ϳed5M2.I~-MRV^IΊIi|h&b&trgiU=eY/yZU];"˳L.`TD c ;-4#1) dG4) .kd)SVv_e]\IAX!wc g %@"bN$UTbO7)u*+tTII>*1Mrr|&ȡa,*Td*KbƺIHj^|efJbJM\ɫ+0Xr5r(^ *Qtu-y$ ຸ@KYh@3u?.o|U4[9w)L٦ ]T%͠ {T:qJngqnmG ٗdh? E9 lyTQ4\"gL0Hd2NH-V9VL2Y[l̍Tb~Ԫ--ү1Mk(4؊QP/ ɌEͤ܁,e2C 2ˠ'_ۦZ dL(_i(Ɗ@1fG@P:3ݤT*=t@nU6ZG}˲H 3- (΄#atkY҂|)!pg 7iOf4`ArSC؛ (&[(ڏϫL,YLfGZX_<(zf]6iwě$Ye+Ai2V*Bq"s K1( C- 6\,s `P י1Hg8#X1QB, ]/ ЬpTtF C.4 ܁#N' o/И?p`,K4K<ܙSpG88?tL=+##Ӿo@gj7Hl☵( δꉉC "v ȳb ´ϐȰLXdʼn͕uX`9C(aUvt+RGiVv>t?6&c(FE|ui΍f4'`VȺ`yDQufZ00<׶uPFH >Ū~a<>B=HΗrZgkUo&cԪVfGИ9 ά(nQ \G|(pi@AKSӲ=f1* ,:gEM]k`# \ `tnTtE&JآCT ,HBvr)ujZ]fz)FW "Y3/ v:oD-^Ty24rE&\7橕e1P[(oKyt J9Vk0/I|ht׊jY5bc۵R`5;.EE0/I7ҕ"8 1!-M%%D692Y%%Cx9D6G< x}E@; &I0,\Ac= a0ACkP#\΋ 0"3 n93@̓ TZ1x%O^?];#hy}JrZʄ^6,E|V9] \4ѡ@@n1 9VF9d^cKª3iR OA <fUŷr}"~cAotzk1%H %:|`{Tm@(w%]'_8ίeUܫ+S$:yN H =Li9)F\=Jk|#tE:x qq> pM(ar $v7GR4vIG^55 |:ktzUBВ*2 0G*`/_u8kCtDs%'@{C CPˍa2wƦO8WW wLSIM%E%]3DÄO-*kgW 47.2.p 1Ac8>ωP+7u Yv=xp^IVEm KE GMH=P3qz4>9ֿqIqq?EA0㉚):|ET鈊SX7tN;Àxj+YaUssg4F/ c3vPDBlc|`#TQk$I' (h+TtMv_* |bzSܶGeK2`൅e6>ORX(Oa$j Үq8]u[5ۘjC@+4GC.I NO <2?qk(0}(oGu35t֋I&lh{qiuYQov>+ %;mg]0) Lby~G9;_WIgujYX1h6|S"Ի'PN:"8C5XCl %tFHEY,nO JQ+9Xdl7@Ј'<|PaFԤ™E Y@@^ S+.nR# Pm~ ot"_=H}w iLS mhr8˨!^Q?VvA~BU'`ܶk* ygX^ujĬ۝1fS0,++rr7oߟ8pJTOu"tmg#yqk7##H}ňt247FX:ѱ>4bT@nxs7'L, >Nf`3#\ esT2{M8Ilj!>k"j[ kr*$TkTvg%xBS\Aޡ;J> &Xtȟuy#ɒՂ{ ¯aVܡѼ{Cn*V~:u/mڳ+%…,Qdb'0 bGdf`f`@*Pa^袴@\- [6Ƶb9 r=ܦAN9[\ڰ KASH(~s5bUך\ ɍ[ŧ $ "9R|PVYX]n_B?] +RCX-c] ,Kڜ 3DST`Q%hh{U>W+O :L D~CAZ~FD#'%k1w\158>wCn@ K?QԊ=qw ި"({$`e@*D ;t1%]/ m)E)"PMޡT1#`ϬɎ O,Ħ2*?PJį=n3)3Z8=Y!KnU 0gRؾzGi.r3ST/F=E^y9%#;ߟ/vч(52IȥCA6'O6m+ `JW6U RӁ\|ׂ !,X%wT@5Xsrϰh+d2Y.yMT 05Fb@e3C!g4P<%V3m2\4ʍٻr1p*?&ó=$Мi)!LpLH-B,ŇB#S},2047qgY1 YPOl 8M]1mW|0wͯ:Q,a9 D*9p݊PnF8]1lڶ6ѧ _:Y5{S,ňϲXv k5+6Ve@NW5rFԙm<'^7[Ya{C;d%)|}Ry=kCSȋ"G%.UF0RƊɵ=>/ޡ#%JN ˩D 5#H!1iNM<+IRH`Q%>S Ke0!z:ΉE9s>gTVюHXP}`kiےF ĭ5wT6>L( DF_]@۫U4sj5M729_aBΓIuQq6i:8+fByse'vaS&#[cOqo@a'g uqg~ ]óghC "e.j̓>{1}*@~Nk0 U;Wch59X6 % .=`CDP'mqOdSn£RX mU t=B~`iL::;:'Z$TP ;DT mˆ:BY"{3k[m'a4{p=ghO=G=O)r ޳(GGTnܐZ1ik;3HKOZݞ^9q{磂^*5aYәE]7 L|z&KUu8q& "od}'e.6(39inhY?/T҆f:M.VMRu%loT9r (; Dū,|o>:Њrow`!\>\ hŜ״P|hC2JFIr1Rʼn @yV̊ !r. nv)}Yw.lc&kvY5`Puz]}@_a?-xR05e1}v&1\Ɗܳi<3#\csx -pj*u0ﺢ4c#(C0yl6asԎoXS|0C^)I߸0+P|B:ms^ Po䗐vAPDz}?re K }B8xyw3nI7^/(pvP99p:pq?z[vpvrN{x@#1NUջ՝516'OR#Z]u*0Wh[=a k%>`b3Lumcqb6H,t#(4ƑԜ h98lޞ 5S= 3l*rcXJ N٢3:CU>*{VcPzUmP&$mdG(@S21&E1/~Q;(AƝqoT|pw5F6~UJ' ATPܐaxǍL>-aąwj7ĒE|ՉS+N~lhԄzi}nTjBjC`WL͊ }Yn CN0dJv mIWW4pcpGlG&| ֫\}D6G1*(7^Vke5\.Mch?9XkZCֽ1&`fZ4I3 dpI6ut]c 8R}6 H9iV7ӦbڼuƐL':NoS_C'fg7=(=hYPr uPųʵ~uxŪiԁV2s9 3ZwMY._a<$T߇;Q6n+^-cG5,WU ؈D;Qwwp P+zb> ? D_t^D¶xvi[ >M!V/J0!*UHi[rvÀY! ':ex)6OC qᎍr<C7|j-4ufzS'r0&?Ec8iAg=FNo=6h[v-mIB꫇ROPpQE}.7CWk9K6+V []^ZؒJX@6"!KTBWcjCy"d^.߹TOm%_+5hvvQ+Bʂq`K!}Fn?&)>kEhҥ_m4B2d\EѰH;]-?iðklبT2^d߼Zw3~JmM 9B#y^Z9HDx#"<ϼgS}[*N}^j$mdN>%\1hݻіѤ-5o ]Y~W #}S3^cPFFUE~aNgVn.8NZd;pP1Na[t } X/jr+#|'?F-7(٨Q0MifPݮ׿aHuY|ܲ2+oCVz.|z| 0O,*s2iD[܎Y&Mu.3(z7+_ ӱ=`cO~AK|=)pn ;x2FY^px12Oť=}$xLmpNd2Jo&.3&|qiўjWg/TCj{ޭj)q[Pjz,k %N:2YKijufyLUdMPX$yr-= nCtQx!@sX᧯IS9Nɮnp[Qc(4 O-ݸScK.RW%H9~wF!XŜ}tdy@ӥ)DUB:]=nZ_c{$o%RzsF=E)]Yiiɯa!شv@[" !SAq+eY5Z66K ƃfa5Hឡ95p?ZMKݧ*L܄^la#Ǖ?it*ڼyiz ۓ\;IUM5,sHyx -w6wsVF35wggݛX.v+\pIyBv;<"h2y̫bBKz6H" pש=bFMiݕtH=Q[C~&1ޜQC !pz| >YΐGI5R o#__q6DʒoAkKrew(zO`s֯'䢽5\Q7K6n ]K:9_2b\RHB$akܙr} }ou J3tbZ+"[ř6f}4\BPs# 'w00o7x7 IY&kgyߕsZ;9zzT:V]*r+o晞o".mݥ+.T'΅{=(h0'-EO0/l,=鴧v.TOy:Rz}nZI+7]uSWh\};R()?ື ۂs@YѨJ4uIPK!&poetry/core/_vendor/pyparsing/py.typedPK!^b(poetry/core/_vendor/pyparsing/results.py=ђ6XR"m<%L]nN*>hU%B3Q 5M߯j8Wu{TF4F`M^x[yuVb,˪fmd&}VF" >6XP*B﫬%l+@k"#B4 +b(3v#6LPձ"oy!QPAMYe 0X,)YY}f#x=`ıZ1f?Z\PA qVdu盈u(⃯'1Tf[^g5ߵ3i*ZluRstH؍"2n],_-Z$ FǺjO5/E.پ*Yd0alǓBf|. N}e딥I$Ɨ 3Q`y(r*)T@<Ҹ̧2b^OfI]W٪jɎ\BU9fK"A}w'@جT3dqoyT{YAD8!"bv0:Mn+7N(}@гC3R6{DMQƇ%*C?C] *_OA 7 P/y)&c zOш#Wd`dY mJv&Yi}頻Dvt,>;&.PKE*(b(;|s#)[yR5EzRq^]ͯ7E=b0_İ0$MVi€@SdC\of&Fu(j|-x.yL7T (*U:6fdw`D`\BaC{A$+xz*alӕL0g (Ņ ( ؃m9R?Us}UU5 rrYHH2')=[VĴ\ıѰ-s3JIn 2[;+j鹑|}p6@@Iedi0 UILI;Ҭ21CE)]v, A!g& @W!fE䇳Ӑ<5W٢Π=i>pAZU 3P_h>n9x~1< Mf=R h5W+m9L((mBC`YRlԵV9;#,O@"k]1nnuHS48uvG?܀@cV>HCB vGE/^_A3tPw'll` ] ,Սĺ{%0AqWhlx/tٍv&-^fU7~C-M*6 *jJ̊NxU57 &$ C=3`&/b6i!fNL:O ;-?P4ZkBbvƨ"g. NDtϛr)a,]K цx}WoM#> dqB\),p,GmqDLdLLA˽cL?c%+|m21 $ (rN:5{L.^}2@pKD~:[K*,,y~شp~cQb!0Ջ6ޫ_Q mFZ(@OR =bѱ Y(ZP `S\`W<udX3tђC?z1@9} Oq+Gad`L&J-SF;nNt'N7;s YdVuWlz{!#{W2ysZ4MVI̚gSӉq@L/:ahȓ5͒|*T/)noy* ޻wu j蚅VGX'4%a48|+N7t#&AʠBlYI6U{$ϧu?^| z\oָdČQimjzõ& AG,{e{ҶW~kn )3ύ'F ")goqXk V, i?T]d #26@-Ҹ … DZNqSZdm~8KkS`be8iWӌHqTLp̚%{oepc+Iq- iEXȾC퀫mvAe䯯f}6Y5q 2*5Nn.nN]\}c,v]'j= "ZO&U<.o`V/5 }FP*2_Tu+jy.5A;`VQd;~y3܀stʽh3p?h^i E6 uBK]4?8^ټ}A"多`i=31(E-4n^=dqHY9^/X~/?~W߉H4@N=/R-0} 79o"k.팳e+u7dsXlwFDOtA"I$q l@UL#j)]o_OsIoߞ#j KOg& 6f=$r4ʷ,:`I^y8al%dx&X#ʟEQ G)hpp7Oˬ]sux@[|+^m ~{L,_'̀FtFV<vaW7D_ۚ;_?l5M#F/Ħ9H{0nomCGrߠSUuV )WD )%GCJj_r{+T/6z| 3ǧuQ 13YV?˼M;~_LW rqe_t*~R ] xf튼U.n8&a,:?!YX\HI&0"[)5ld^zch<W9]7{Pؿ&-Sy;_6CkE)-IaDFuH_fjZgDŽyg׽`])1 Ĩ$W(VW9#WM _e#-,V4Xx|y`=MD;ӊ'56CM=)Vʅ8=XǚAs,'c=Źs1G֠l@EٳJr5INnE6ԆDE:s)MNgx \a* &Hfkr߿Oy_ө^KEZ_2UJJI}2=f;؜[Q2hW 0|JIG׾4NQ] ATҧS  ]-(ˎ(|{ڵXM`̬_Ȕ?emQ :$Af̂=o٥n%qdrxc]%6҂K˰POj T˷Z%6*w:-z|94l|a_W)2,~J8W'#?Ȥ+_0gª0gAi}α`}`rvf#*øm]oz/f_Jƫ&eu<[UIu|-kN]2[hw~ִOLY:5A!i:@Egza5FϰS3"x3C4J`SٯH}5l±*]çaGw {@X/hS$,VQ)VlUMF>C|{uƆ:;B3^j Pj32L g?ї5}۪lͲ.e 7VⰊ"1ջZ81{e[eBe,wϖqm90@4BHWe<{:`$TL' ~>imgsINl]@iV 2#smʆ}"G#];-l<*OtjoU;;hN:O} E\TF+˔e,˻5/r[-DDQ6ڻJZ}xrrTRK #})gf37%Qs7E~1A6MJxNn+*&_tDbQM[A14J m#Rd͐eBnwǒ; oNh ߒ4myꦂz1.<F+rϜmh [ hEUbxŘeLJHES" eM]b/gLowA Y-wcKk.^OMIpBY6ցM\J2Z hE3Z鞈,#q[H^<9{CтY 2"?1;*B͛ V l)a_opbxOS'Q^i;FN>r^q ^pW¡+XY( /Վ7)ų爞p6'WtNNǧB;A~QpZm=,F- pr( PX 1M[|5>6{$m}ywF?|/|4au~{{{3mHyx$=zfMs5W2T vu] ՕVÄuXN)Ke50rcG$1͈4HSגHҨ9STn\d85q0DecP٦ZfC CVGVk:cQRQ_$6F*BMx2P% B7 vV*ᦽy,2+ ;/^ TD1f~pa }}jq!-؀|{+?e`}|"`Q GƸ( :jpfhg#(ֱ`eV YCWCg]WǴ _9lCo!^ʞ@$w\wX$Q\l3VDNˋ*]26t d 6 ѣh /ـ_~s- Kr K YY&+@XTwmoh)Q@6d)Y^^b#V/I!OIזs͓tGk@N:Kd*z} "%BhS)/!ֶ;3}*ÿp) KZ)[LǠd$a :[T=v g ܿqI0( L'`1jެH]@3eGP~ݤȰMX*6-8R ǂ|,Q7e )k`8JF\L?qlHfRg"DqS82#mV˄4Blx>=Vo`&H4q̠ҝq8ҵ/4sv} EɕSV Z"'|A@>= A[K  u-zlwV' 0"d j+_qU~ͯ5 _a0TbA]20 S%^ObW4i/<̧Rɳc\Ba֍{#OƫBW-j&^$:2x#nm!:KOUnr< :{׃=^3h La;iϓ+T? #H$T=<ܪ6$tމ'@ph `4<EǪn2ݺ-Ee5v6p^ҝ"Tå:66.PCs1Q4(h8<|8 -Arw~Y~x%OZav/@|W${$s+w[,O5xMtk(Ghu#ϳ~"J aP%!9φfib)JHغnUΔ7 x2ViDE23d3wqwJ>uS"v2VrIqs 1`55%1EAB KE8$C! CGMxZveӗBBxUkܿ'ּBi͎$9Fv ob&%J^?~7Yh6P2{"(xwP_1A,V>tS-|aX4r['|/G32kr oH$4)~{NM#b6WfϿg׿Y<<C|ƇwRl#%Q:L\-,޵!exryƍ/HFSȳGnPČ f,TIlvƷ 5ƫEƻժl3 GxIq~4߲|N?X=_x>xL/}]s2 l GfLj*3^gaH+!NJ)aܽԱˬK432ﰞ ~{5W˴C زC. .HgCntETs /xs2̌kѻbn<ȷˍbK "Z\eǦ?s-S+,Ѭ |92罩SS~}`Yy¥6~]uf6 T5A uqe X+1 cXL>_/l T>jnlHM^`%S7 \sW%'9Pq׻O@傧[ 8zk Sͺ_L sX&cZ$L.=/%Fx )job fo`ai-wSw.Qp9rUۤH2"PK!zx #*(poetry/core/_vendor/pyparsing/unicode.pyZj[x-#Zx<ݙYMU  Mi 4-M[$@KRhC~_/|[UU?5ǻ6^ig{=9ܱw%ϱ-;[I$N$X m!NhaDRڎ Cm l1#\9BDa^v񜮗H$, pWWm@ʮOnFHN4aCGv`ZD3N?7Mgz~H?7F{>~B?MTd4Û Zω-&z`}yhsl]w[1 5HI*j._(ʕAY=1,K'S87 lq{+=X:ܒC[sje͵ܶ87;ձN뷾{|Mvv'}P;z]PӇnmFI8f:GrvfXX:lY ,[qdc`Ȱe 1#g #J7CSUϞUǀj߭F:ְkڳYmTJ?Yzzi')<ʵ`"W6J%IQyA-k(:~u)U jRYQGEF+R+uZ1vLRVe*F˒Zè7$o V0SaC,QB(KӔb^ƨ8Ds FQ&Q {+N[ll^S;h)Q59TeZ(v*IZU,X+i&[+ZE@@1 MHC jk&Xf$o IڰLT6OL lbK[{X3 l!M/6lvuz;sԸ{o6?mCQM Mo&Xgeg-M~)nɩ&*"L,<ձ "m ?zI{-K*RP`B.a".$U'_ԉ'`^0gql{QV+mγežDU%3c/䝛M9>ϑTmq"70uG$#/>׾8}tyQz|z#<õO=\{x>-\|HX~{7P~a~tuU!饫O/wP(؛aWuҦ yr'g>c_;(Mə7~i1#wۨ#lwn]XMcZ}w깍;6V?Xzk)TƠ~%PK!-Gl%poetry/core/_vendor/pyparsing/util.pyXmo6_sH#lz_z&˵@qmޗlf+*I%]r.:e3gC~$j:qNR{۾cKRt5)i`gt!vuhsS'LukD6 H6 .Dz0> HŊ$IuSd˽?uK /FdS4S[+w̰k[(0P"<)evÖ]Ygшk߽S=%@J2)fپAg܀VĂ ܮEFkq+"?9 xdH'EvbSãe雜ʲNlA[0Fe=XH7G'zQrej'>{DQ\W0kE76ig>R3څ 0V2RY'&\Z:=a;NNJO06!7{ejBP |#qFdDya!z |WLa zkH]RS|޷"3H]N.$E$QHpV\JbiccF|uSvX.`yv89hs'Vaz@%WNn-4TYծ1Kf8i@j*S6'ް77?s(p1Oy٩@HW'-ݦK'}_^\>bg<5H ݋yVл~:wMkQx 8QxoW*5#X$!q$cQSP eh#?+j:V1)We·`RBj_kj#~a\7 ej> =}hГ&>`Is99Yl;MЕ _;KRbXRIW-*:FlZkCxpݗu-N)+릯KBD~qܔ`@wK1:ʓWk۰ك[ $LW8F6Dsl@knWM5%  % F 9]¸ûk{W5 DeqѢ<[Jߨ~jQ'223L??j.9c߈BP..Yc'V@$ߘd߽|=z-BV/ekM*+m^>t;;gdmZ=RfWFȔv]mƭ3!$ѐe>)F]~4ٻ8)Ua|knlO`Voq5%6P2tǢ)4Xvۭ%"ڹQyKq>rT >.5\[ _,5]6hI켧rG7G.n]y".s: ^_5G,u\F g Z*E+Lu`LSnnpR +?l޽u=cbIo.҃!InVvU!|k'XH= d<0N18Os$mkuB@KARx L/O̦)Sf}Y= &HgEq*N0㮟bOBQ{{ c:δ#_;; !rlM}#DU}u|yzXp|Ǖa&K`xC})1zyJlԑJRĉ"\5"·*$0wPK!Y *poetry/core/_vendor/pyrsistent/__init__.pymSn0+{. ǡhu@SM8_ݥH*uvvgHjwm>ovoWTUe- z/MoEϴ} c,gWїW% gWP).bT,q☹sv8LF D%z8kϠx¸ '%^ b<g1wA ߤ.~oX9P͗ sQ)<-"}ĝVmֻ偩:[ьy|X,%}h ҈!wnWEADq0yR9Q UtAwP<`cbn|IЄ8s{>BjaLӰf֜_h:Mw|Xe?% ƃՁ(;:,`Y+5CBK]g<% ͜]:~掟~0plAfՄ ͝^\Im\QW;h-ye4; yqPK!2΢G0poetry/core/_vendor/pyrsistent/_checked_types.pyk{^65!w к]ueɐho΃H=K.II֢ÙIgSW[V]Ukq ' UfGѵVj=/ҒᲪ(Tlo+j*;MhU$m΂}-w3#p#pX,@0-d2 4nqjox_L|uj05.lO@U[%jΝ^;%<+ofNzRVVmW Κ5yD]K\X`Bg7LDHZT+,QX" b GZb2iie0nz579b@ q>MM&dPo*F&QF^{_Q*Y J/9' Ώ_5F9JQ@HEjDٗEsSɭ[E!v̀3]JX{P [8@GC ])kFIbH %k( T-l,3֌(-Sa0  ip! ;;ds(};D|B/iH K? FrAPZr|*WپFm8hyQ$n]V3ݴiLcC_ ~kW M\Il o rݒ pHB<!$_Һ fuCql1"7MuFʷ6(g\ƚ8)ܫo"k,([!,&)O&퉇4e1udw;08EKHb $XGZ#\ǐabߩ#GRjEk.iAdFkaܫ^SO|6Z'fwTՐz~E fҰTwPm3iR~F45 $~5UJ_tUԯE0{ 60?nOm}B:z68m ]x Gaa6DFJK'2 ?f/6]zm7oIۻ{ v`~j_Z[#AC !aU ˂܃6 %Om1 Ip Hi8mV?ʻs'}}?݁oXE5dlUGqSr TSN5+kyH hMecJ^ۈϩ^P8EQTw~" _gYLeEH /ferz&P]w#J/P& VA&u+r /co@x>*l`1_HzMPCfj.;CnL`ܴ[z Ph [8K+ZInM^ e5zZJc m$?CLT܃6سzB{왈, H{.m{oᶴ"s/hdu9(,!) ch{8O "]ہT~l۝Qרft:c9FkQ 6xg` JJ"Q"z38DtS2 ylr>{X}aP F - VWK=772/%s^ryA+nE5(6x| a c(/f{>;@>L8Skxb~Bh|\xw9m[:@RARMEXAh@PE<oz PVՠ \=3KYa*XUD4ۂc,#4nzH@>rMKb'c0H倕6e1ұ!-&uz:|.do#GGKZcKBo[>] `cENx_r,Y?#M==7JyS;lBcҹ'pRѦ5hc웴^|r<dR'$(sPp*l,v83|9ChBl]nc?xMGI~'b`3z\FagC+k{vqf;tKu1);ӻ=Hw^O$hAGOV{%8ժ+fS_PK!kU ./poetry/core/_vendor/pyrsistent/_field_common.pyZ[o~ׯ*& Q]ͺwۭkP#jdqMZ{e$%(Z?$g\U]mnUeZe7j[Ո|VD#y;U>QemU.?8-oe˲=ԶͫjJyvSת%QF>,`]֍Jw,md֛(HϊđL;]-inyl hVV*MډXDܨxJKz%fbgm6eE(ML&)NVm(ŪX2ݫťUAfJ 2SDg'(% UAnd`QkT[ 꺪ӬZygm gLޤ(=@ / S6}f@;UL⑶'3G-F '/pOџӕ =?|ӿP.IaڇG6 e c#ۼh4+=h-s$=b9+jp!j㞳ʬ0FGCZVcF^/ȗI)y|4w 'vW;O+M~sw ]o, (NF>zVC@3\;EL z' B"g6zNd(߇0+dD6+#sͯK꾭%f[/ O+&x( [ȍXÕ+ZRhVK JĶZS c/}ɑ󈑌N=Β'jU݀Gt飊iSTmpJt@f-~ܻ~vqo5Ml+t/G niC!FH(=\p;sl[%mO66Пugl?g}64Do):Y+ភZ1O[?/Ah!ްtXAR9?s{ . W `?;'v!q/4`g "uԍ@)r1z{=;ș.`Rt*"ȉTl,aO}@/s\nXT94&RHhB `jiѺFެ!Mc`ƸkFκ!t0>oʠ:Od*L. %vy(5,'g4W2#Xj6"5XW!a`q5EZbCZdkܧhod*UU(yȁ $>%m63Y, f$كc Y[H/Zu,S?N_[JGm H"V؉!1ژvsD,T&:,jXhPNov[l5nyN ̡ \CX TNjrw-|e즉wPBi%Z@R: l YK#*[ P9d5Q@kxJfI57 Kj4A^⮪oYVXl(R ߣ -@Kõ@* CVӉ {=k|RNLj ff&r ?bv ə-y}_FK@5/6DtK.O~㸃+rw3 N,NS[qHn48,a:VuT 7s-_RFNNNv|S1֟ƽ>4z~3hC[R,wmZA AkIbOdؕ4 +m/#O:#7n ]_8Bƒ.~|\;BֿC|qo4ۑrW~юSIҶ`gVU 3UqXP0r_@ ;Ho]R) k*/ZlI&yC^'Kfcձk]Ge-@&a{84z)W*NػFlexGC<2]Yfwct݁'^7D7FoInA=(?;6-{-XV3c 8hǛG@lb`^UW=tu``T9Ms{z̸=GMCts4y[L)1`ܔ6ӻCM I8liJ6cLXQZg&ˌTLm'q QqT|ܽMKsz*3/ m]imDBQ(z~Q5 C?FO8Z9#I谾ZX@"~ĮVg~0Y}..._u5=R4GQ+_Zë^i-hv'lw8ya'P ݐs@},}!s_3o0Pu:t:zxƒKcr18[:[v L/$ļ*O<ǐa$* Hec51蘵ȃgRRmgR [0قCBr^,4dD$Qw\y;\| RgeH-ǵd\Qj؟a 5|O@/ |zO6#ޅdxn?Ɨa-9 Ot!ԠPICD1 \qӢZ{HxOki n%23Ǚ&'ɵ(-T ::*p8k<&zcZki.MCzd4ͣh bB F02D8dB\r~/!pCV)"~k7lwmE8rno3 ,2Hc wki.Glc=Dm?5s9}Nl ntz0q LS :?f6 #=?Abx; 'YrHЌdU S;[)p+kv@$XN1u (H V[W'z]'5w8\v ,vJJ`3h;m(?S4*8-N8A'c#`g ~Q=#IqОlWwp~-nOrsmoa@a#> Gt9BHZҼrSILGdᩏĉN: |f6SwzVџENMOf;:NS @ V _uS+o#sh2n15k[>5j' n0)lm+}P'P8!"$Z]!{n/BdIm򷊶Zhﺅ*tqg&Z6[d) 3Cv6}n`@Hp#ǫFW|ϋѩdȱr{ưmVgyyyBtO{]G{7uPK!-GJ'poetry/core/_vendor/pyrsistent/_pbag.pyYo6_y,}2mm-hӇTJm-TR;~H,wGݏIxDe3'$+*.楢YDH^)&hdiH~r 5 Xe8ϥc,Y̤bEUA+GGQ$i)%.$/!a9+ujQ=L"lW7ѲKԥ@/Q$sdmdE j\FY( $`Ң?4`+RźbV{;fgilۗܧYx4m}KL]W)К+怽iZ4y_[a?|3{|_蝭 b]ZUe<1ݚ>sFa$}=3'}nfw01`8^ \4%#WWdVȝ%;b׸ }`<i3a ʺ |MTV@jBQH.Lw3sޤhR礽S+b G윕6`Vh7?2{({/޻.:o/5k`㬫S  86=ڿnrW6`+A -7̔=2xoĔI۰[c$2u(<ފu/;>1U@#?]~g`ǂ@;OSwhlAaqJꑣQLu0X-/9h╿0%/1)gCA143r.4yyke`,41w(I.k/`Rc &{[F^g =Wֿ+ھXgTQk:5ym[ v9#pYN-T<6X ?^І%_ZCYϸ>2֋A:=#dtC#R)\~:$ƯKS,t+!g7P.sω&| vh΁c!i`_($΂+h4"bb? l}!ߜ@ȿvpL qYq?h85=5VRH:D^z.?抌35f_?w༛zrw7qv3AC@hFu=/~݌[gNJ <Zޛq;ObS7t/;px"V,39{fT>V➒x5N§4nhu қ_qBH但0z}%ہ *F$x%ZPK!R %)poetry/core/_vendor/pyrsistent/_pclass.pyZo8_>Xv{C&l{wE 0ms#K:J-̐?$%iq~c3Pݶ5VIE,ߋN>6B1yhV䢺>{Ee]3l\J׭ߕ=Z-Q[)"á >Jh֌"qL*K$wȡ)]_}Gv[vqu#"@R)zV'bB6[m8*;bxX, eH%W*J;k+V>f {xU0 >, _Bu 鬬YrJ1["v_hMHYZiQE h2ħwy , @WOQ-+3]SNؔᷬ,u(j1p}ދVC4n| 0Ѓwf'M|ԼK6RhŚN3F <%#Aa?N:PީccEuh5?ҧXd>]\3?DU&!2 u%a{ r+Ϋme `n͈ MƉ}˪Z*gX7q5ԢT"}R$-L$ƹ}rrBߧ$eB!T l+E2zT#r jFwZr@U[ ma WY\&C 4Uwma {~LKyu ^p8H / -@rb@a^%P6[ț(-]L, 5]`G ͬ;w|eoK\֍zݦu}^;c&WwA85߼؁SG#<#LT#Ұv"P %. } 'XmZEl~3!4<7 D0"z3D ݊NC]Svc(ljfMKA#1A>ynepF|=~ = 0 שi6nic$_ 8a|7oW/SthamAk2:c;ȶqYFqu- Do0f.aVj~lnR,f0Զ˥G+x"2YD-&awb_C筀35"E96熚R 9Ǧm˻Ŕd @%q^kx`R=eEE @xlU2Cxa㢭SA7MfRam4ƴxOń# ͮԙw"n"Ⱦ.,g\z >rRy8? TIV Xd)?#>XD`[o@(h$v*.nJ0n򢓇K jDCxċ %'N}ܗePi6:sXUjTZߕK+kB%m1,6a ·X%f-fi)`ޭ|2(bp,a&:=IQHq b_?'jenVV \`h"ѥ; ar0B:v=zcD|whdƣ-DBhBNѸSߋdX[Y!s\)zi';޸doOv 8ۙ,#wWt7o=4E&}r@h8^Ywar1Ja FcL>*hd5 4uy/X &a )^&gA֞@6m\ c;ym)([]C]f`N(}`PJY6FަޫJQ+Mk$ qo" [ /]_,=0BHw A>+V4";_+?& <Pf!$TEHc ҝ0n,x |L&EAe}O6bHu 'mC)z '#e^C>gVHHHO52=ỦaT<ւMQx*V=&o@'.˦`լ).4m>qwy+wtǻHyi?PK!q /)poetry/core/_vendor/pyrsistent/_pdeque.py]s۸]Q Nd/F^`*l&< +]5(5b_F-L51A.8ȰMx>DDt%qg&~l EiFmbsKH ?, ?߀v&n lMi*`rC-%uj_ CV.V&9^=fJH,K'A8'$bK1s+ZX&KlxŲ2_]&Y}$ui@AC' I͙͢DCUKϴ)KӓHZP= /NT["eB=ym/!]#_<4凕m +MV&\ pjM0Ʒ=msbkd,|Bɻ]'BThn%MER8J^oM]ytVE{m*M;3(lA;$b\ fײ'K]1i 'J|cb@U QH{4)N$2ym dqV`ڀaE55 8f:I c+} k{?iTJ׺?smߐ;v2ïg~ϱLJ߰}QD;>;k_:+iqtIXҽ='SMsJ kycz#DL{6žgo>3F=v-F[QC7otP (}R\wuEާ`9diɱieZWM:AT;^ΥT* ]@yxK5K8 XSӽ̑>=( 0xYuO"VkO{|T0M`^:L5u$F<"]Sg d {X@ }>nbC"jm?d`+=ט"FT,x҈g+IH3" jR5k+^[uٌΥێf=ZȢYR)-ߝi=nRJ\l#X.|?rF8hmþ( CJ]LRw ;3$%evxErf8/ϼpkZ)Q:k曒CcėVԥX-nFⶭKꖍR,RqkYҺWT0 gggja~ӌ+@:ӒLog ;utpoe#F܂Id^0QWJ;[eb/x\eT|A{*}(2+6HsMǀ@qwp=ځ;zi@2bBê6 ;ev ~i"MB!C V#s^t;pS :xH9QM٠C=3q2Ty>Ҡ6/LձPL%6c'0 eq'ۢXKq ^VmsFSgȐ +a0nStkveQJ#Mn!ഹ Td]{iCm^-}H4wQmZZ&CX3_ XzJ6tPUw3д (pDFu{ gJZP۬9OX>,>[1!]!)\o0:t%Ty#8\< \e!nDz#yT$tfL@޷;pmuTY}C &&p\50%|hb H!':(]cdíb@yQ9/-矒KDpY늛*6w7gc;U A3%4 $Sn.?iْ I ~`AXP: E2ȓjUʢ[Xlg$ RJچAZN0_g #3\K3Q:J<~z}AFUHA-ÓxUɾw:ڬ"?4F|{?21E1= >ɃyD <c^{wb$lF.ޕn!w&ئmбWLӲZG)VPҫrv$+}X*6H6.j%Ğ]ܱJ*|t7ڽ&ZWp.A$]ZUMC1(7Ɓ%>6mwv|ۏ=; `"4,!:QW Wk&V}L$Ec#f-Z]H-uPGiP_>P~K7f;I ׃Yd?#0m4A γoO~'@#cJjXƙ 9RM.cRuDn%8lhX11U_VUbb$xK5o( gv!ǎjrJnMpO-ԏ-BtbR:o$PiKzK%cZ1[m³E(j8D^NSC\$AtDI긊[CA"R8v]e]|V1?aMipӒO{}s,2y5ia~0l$`'ҵxT,+.kӊ~Oc7":|"$.k(|T(BQ@ S2z>M$W`m=c?>p (ȟJ;/@dG2]$>߻T01ul0z VٟBrk*NeAg(82P@PK!zYen9'poetry/core/_vendor/pyrsistent/_pmap.pyk~F!QQ#uRHjF`0+r%1HeN1wf]>t8`LgvfՎUQΫR||d;}^\mk!*nx^I\բdc^-yVqw'IZp[@hV-8ɓ'؎; ꊭ`+=e5Kq#`+Uo++jy-`Ӝ F H򉭚R]Otgiπ.Jyi6J?yɋĎ2lğҢjbՊ~ikxLZ6@)`ɤ`Y^^R|l; OҼnrvdc՞KH}#Dd٤[Qp2'm vۀ Hh3Qo8:THٟ3Ӑm6 :qOl/ZXo7jRP.9DDFeu*=ä@S vtt|@K8VBN(2 b0Qj!V<1xnAH!u1n%BemsIDcrfrP.yj:hSrtG8v:?yo#`NQFB0[VC# 79fvАF-ldlt`ϛ=m$bg`4 sq"]GJ%hd44G\dVSكέˋAPo>.},0,E=B?eIZ  )*.3admWAY͗8TNQu_a{(g|X.US0"W9[N,f?)6WVcM,}d/{g} 0Dz\\f;:,ꭄP1k:w;~Ob13-Lys|2JxmV>àLȳaiF[؍ٛU[gDol0,s].-઎L.cC(,u_㼸<Uǡ,iY -3Ί9LZwj7ӅQQ%\ k𑎢Xh$܎h.%eCȚ{vNs>aAa|yH[b\jb3H).[dYmUڳJSX.{.w}UQꏔ$ h4'<{) Qŀ.W:4EEr>o4`Vle YbN7nM`KWnlPqt*k# L voQdCn  m=J mg=ߓA9h?B(0[xj 鿸$xJ^1!OL~]{kp<  Ԣ*׀#GP``)71/1M_TNBaC[-V[o[v!{=JYz{}=Mש-O{ɮ^h3Oí_clq.!Gl[ 82X%>cȂ*ƽ3-䇓1 cNX<~ fxYX}y! ѩVvgiř$M| Ӭ4$jNG`2Ӻ(s,73`y`~ 7a hgge[Zm9Lx}fq?C6!y%f {1 0բ 7oڡS ͤ=u5xGl gÕ [k44E-c::$~g|s$l}lë=/?d߰`Y8{ R|dɞ]hCDBJu!yn1ĥ1HExRNwc#͸kuK. yKxEâܟrEgR xT"eXi!Ğ™SqM1^ +lW8^O'6Z$6Npļ&J>gǏ iWb]}0fg Jnvvz76clkGh4j;oJ  бlB Gv@8S'}J}l>4Pr?ݾO6ً )pr9<&Xe(C>(ԑ}?v.oM?\ܻ<2w?-|c_S}'˞ֲo 5,]ݨ\kQB\(-P. ']\a0y@b#Ql DpPtc#B&XQ]D;3;u|E6(8]M`Oh®r-tO( }1{U.eX֗mmڗS`]Q1L]̠yp*TE–*jݕ݉~oJ^YWʎMZF&KC׾}M&kdEM-Ʉ4[VN7;~7ޕ]} >Lwz-3Ğv^M4}xʾkᅲӚ5SlOM̾o=TbDÝwi@m:e`Fvs]91n$3g!^"A3_k~FI[l]~.R@kR7L"|07 Dp }'3P~}cO`jUdo05N ;릏Pn.Sz /!jN/wh?HpZlԅT5l[VGfJF8PBGvF݇N%~^ -sĞy-.yTy[n^vt "ڱYv<*Mm+<9 ܰk/m:!^ioe0YdҊ$)4ļE3/[N6IN^xSP3@ZyAӉ)5&)ha[]36B'aR"ͮǐb~ce_T9Mާ}ρl.E#~Q$3ӈB &c6l Aܠ>$2Gs%EfoL@<^o˒㊋ȶpGHiN4LB}:(fڭ)l,g]D>*B- pz?Seɷ`[ۘw]6w#\D~!(?]>cʼnYZ== 8^T$"Vmkx@Jڰ`G VLU(1E)kZ \jG>5tWcu[ 8SZ _P7cU늊w\ݶH\HpUrT.>2n煅^sP{^vejU0U-F tk4"ٰ9t$S3^;(+PH9E 4bx3lTQ{=9`{_`>6+*? "޷u`k'{He-_;uLg^jp@LXNg;k8H2R(n@Rɑv`pjP/k`df]Ё-|y"= Sp"= EL?`[!UK1vd(.S=ɲpkaSoC~+QNG:vͱ!)Imy"uK<8 4=QsO_ی ?Bx @`8Eclu ziRF5ى}f8.#~9 n?d,GG jdtp-jB[@j< #S1yizipT](j' ~H7:hh:Ĭu=pKyP\o,G'}Lh*X7Uoņ}9eά z4zj'fL=*qqB;NYOJvÃ㻎:?->UK7{e8SE eqnNFRN#%r*| Ql4w6*[ Ը vܿy/ C:!k@H>ȅJ[!„:<'5SZ toa[~󬷶omHPpi0\'4^ a%?2O{]` e`-xS_"[fͲCi:inir/{ #ã~8.͚կ^ԟ\SZ/D']MO7nW39 Ӊ<+})]}!Zud\2)"=9UӢzθ[d1lpVO p{BtvUƢz& -NpsfsGjN9[$5Qx_ZJ׺ykr&a w2?:$"tLھ{2TxSс;] 'F^{Ѧ7bx(̛*F#p޲,*L|>Yx C"05U;uet.Tw ϒMK=3 tBXH KzVyle)v_Jzzu݆֯PK!4V='poetry/core/_vendor/pyrsistent/_pset.py]o6ݿ␢y’abݖa0,h2BKFU #) / J YU0B6:cDJe7 [V|zg+bjJ mxcYs٬p2ӫ=;hn5w lAl8# aī4k`U mՈƲ*i$ )Alf)H4ц5F0á 嬄Ns+o #V]WNW -`U $5^QѫVaTYQp5㉖Vr}y4)6\qhFVhJwET_9ׯA5-.p鈾@<Y&%A/2kEE#i ).cHJhC2 GWy>O{%_!qwy^@~W+rHnmR#M iD&SE ׄBr8P9=qJ+/嘙\$Np)Nh+[y;/A8f/5"OJ!ms~%^`tùmOeWcp+J4뾧 %T؆ ,GM(Rq)U<ڽl1&(L<.Nu<%= z"ΠNvI;KN>&g7^>zj\J >K`0z1=wJEê>QތyOFa6Gi5@y>T~Hn#TudsyZ'|zhX?#Sizl0_"u w&@BL蝺ٔvNXfQM>,4BGQ|lt@~MOK"fo~#ڰ<k{,a;Bq!rZz AgQo 4Lz–)!; uG Igi<$<ÅGеj˗ypnvMU'm@2Zj~dP$ذf3yk,i|J_C mR(0m-Ӂc/QΌOJ?}wlt Mgnf;C^U%ᷣKKLĆSMnT0vlM9^8J-dbj:eO-Ve0|%QjXuU?`|-awnoqgy!mDA>vBQkǬ5bƐO5g~Ӯ60M7|<ބty`c t?taOؓS#4ixMP**k+k -jNjqQTH"ðI(ޛ=f3:+*u_41>o~=}ws풆̎[~N҉ )^FFfأe@yi2e@ eIO5BghC|:?u~XrMwj hNo7⭾gj'!9ѣzo\𰻨0aizѓT-44QO X#\ PK!2-X*poetry/core/_vendor/pyrsistent/_pvector.pyUe>s']rtuffQoYd^+F\򜭫2mr%@܈N4&%)Sb *MzC>e ia#/BjjPImU|;F`l5į0 /5;}m"C.*u|8kXay[86B`s}9[c>U[uS=dF|wq00A8&љWkg,$`/6Ks(Gj%`9A/}Usr(^axA*A:@%-Ewq;gc؀k8DMAd&3ܵU(4pz: 1*b␾=њm] 4O1Saĉ5%]/&_<+Kp .w!]j9~d]#'^d,#!RM*Ϣ~WAAO,0<{]UfBbL`b# ȣ8 mLlJ+?g[188YS|=f3>>:LEӨlS$n[ e1LӟUNR҃n;\AYuJۿL=:iȦBb0qBZ0Kx!g'5 DY"Џ@ k̘@1q=쉓Lu76jo)É t#JɈ)2lL"bj 1&iQ"ta>6ރpG`c9kL1_mGJ #!(Ǐ]hч3\q#Yzh%=?nVbo:ʎuƍxFʼn|5>Y`#dT8egli_?arWW΍M  {\_bG.RK!:YeHmI3Rzh2]A nDnlty*T(1`SX0Չ] w(ba4;BcR88uY̵~ Bn`ԋ/P' h QD"_=!7 p=>y>vt{S%Y0nUm8(uP0Hmr/\')K]zaދgAAU6yk`&bZ}*Png~άP2g7EYn{RA6vǶ :E}Y:XG.C Y3P#H_?\\ޢ^olRX.[WيW[y@S1JX*m"`)> V5\U-^:wF_*ʏF#; mԾ/b' plTkiczi0uaJ;<{n"Wn~@D37 {p;sKܳ+yYW2*?presg/:uZhlڟtNT3'SEd;"!]x%{*^=^DnĂo׼w)B]rVV׍# XnF-xDߚboJ}1vfLJLD* uLDJaj0&@@A4Z% R/P~ۦSӂ %NĎo$Q$ K'E^nq֌(СQ5IWjld&Iꋨ߰fC/yOV07;[hgB k)-N39M|P;sM<&+co񰗅14˯z*C*ꏣ>3wi', PӐLW)ĄOUxZ^ &XYOHvFL?͖xq/u{.uzTasZ$*$Mт A N*擹To Dq2o2R/e+ȯЋGj2&HΈV̻~{/>ݠ1V[AZP-aQYT~bIWR~Q~$ܜݔc Ep#.Fmx&iloHg9;t gi8J `kbPT("EFtKrc𰠠B5Lũq A=`"²eVI]%@#YSe7j;Ov?9jYfz:%in8bJ?0GQ`/rl ޮSxq01i?\2ԇ5c]S=gezabkna!4^xHэ'i !{}UYB2I4?m1ꨠ tvn!:@ = X eJܢ r3;v_w~ֱNe_AWfm5:NQݤ=~cAioG%*Zb$^ .݊퇭}؃Ge{@̷J>E*^}>ܼ~{3njջ}GM*Pw:Bח + ͧ%*cX60؎*& E$r 0,޽PK!iR9d (poetry/core/_vendor/pyrsistent/_toolz.pyWOH~_1%  '*9BRP 47N :t<7ZU+Q*\/,hV$u"" U{(K!A\ʪUz^*%͍Y6'Ww\/k+pqɫjYWPQS/Pr(]R?F_n (Cbǹأ5Z\VsC=:~G#%:R${jd XNdSSN$UQW5<;UzM3]/A+-tf*'IKY/1pxYU`aXgoBPcҜxSP  EUc@*ŝ~VA C>fPZvMV/x`3/ZȺ ѻ0`n X8ϑ8-BBEj!+#6:D4kZ#k%f˸ u=˻4ʊq%v/4qPbMwSPa8F(`pyDȝ6Yʜ \.5yEReWA$ )"x8,z,^Br`tMq) EaeО, SQ8DsFqF2p-}6=.äA/Fٵre#uc$ax4حAAt~ GphLslXO\!عAخ >"‡ [ip .Bџ$%C餗fQ6B:0OƩ%i> , $,W( d2΢xt^XR㑍#kVX}0)HX?vv|Qx>@s?Ә\Eix@A|!rfk/X94@;RtFcİya)_tw=BXE P|nx>Xѐ1& 3泪5ZugbUӑІ*yq!Nl9;PUxzsS.LRnLcH_w3KuWsu =KSmd`A5*]Vd.kg:m#1xbq^O M9ʂ"LO밮yjM.VN@*dR!&\.UμYU;5|lʽj%t>81 7kuVKM a5i{sBV7A(,<v,:1kj5bEGqbQP.=xϛ~u~-؛ <%I^=`9{Y²66|QUa>'I=]?^I'"wg>r\oܮ7Ȯ;OZ޼km%N5㕱^Jf;>mq BiozPK!ȿ\o2poetry/core/_vendor/pyrsistent/_transformations.pyWO6~߿b<\.сzE:UTP h+Pd]w󫶳q  曙oƃ,ZP83j6z.AV̀%T rU e[.UwW+Oe[Bl6K/=>7W7t~D1-$,ŝ|ydL fJae`+yݨT8yh_Č,pRgBnbA>@-&X2rA6lޤY׭{,)Ilc3Y~ .;pFhM0.(EkT@W-%3j- ǻvJ!3k$*U ˶ *ʥ$FY Y53tbDaD N+k/j=0,NjFm =ofX@lw I@0%9L'^ 5P8\~^ŰhdJ:5us4ʃr\Ά,.F;8sy{&_I=fd̛RuJ My2r!m\LS'g/A`pY{Lݹ1J'7(M$S"=<Պڂϫ#@RRY:eTHm"8ὣ`bE -!O)Sk2IOŦFSt| Utqg÷=,~>pGjoA^q2R/4"wkV?,4^e0 'HPi6]]%s@YGY jMCZPOZ~sY63-w0/өɏKKY7u52A)oIGŠ !䬎*?s/*G9PA8?%CN+#{9IXZ[=$a|\x:c(düG# mz~u I74&V7>ՖT;B`cN\ 98~Nl~$Ѱ:~Z_?t#Nu;7{^!%iCݣwׯI%qS C0X6,ujsH.4[rR1g΋~j'p!eSk~hdN"'6;5V Yn]4PI.j'_~Nޡ yFٸ͡fi3;E,Y+[xDbZƤ^D_PK!'poetry/core/_vendor/pyrsistent/py.typedPK!t(poetry/core/_vendor/pyrsistent/typing.pyTMo0 W`e]ـr CXlyYܴ=|#DR?PiȤZ#&T,aryer,*@"֨r LkAˁV 3X5Jsm4<|Ժk3Xbj& BPoˑXQ <˔,2/* T'bJu-dNl.rm#]hѴEQ{󅻀K|\8jS+>5;h)jqk{Q⯤{YKTSL p ̓)g9WVYsʽƟ5$}c:lYwq̄KtE*_xܳzώ#=BU!6$;Ul#tPî&">z>[ z*"w`Yʥr0BN)2Tv+:k~,PM[2c"8fjwS˘XKyO3܌,kx=i[zf'TgUo%Xte)9<(E78PK!5t&#poetry/core/_vendor/tomlkit/LICENSE]Rn )zJVs5ؤAsI\L ^Gsv Nd8/e>}?~_{5#Bj3֍`=f28Nz 0m 8 AюGEz.z2܁޵V#tOf :DrwI3z;"+л9d|l92\j^{7OH:{L}fpr=įI~Hg#~xakƈ Cd;e}svPcVQҽ> Vt%M:,)0m,q]0JWB^iR.իW]^!Pę<)`1QŠKo;|( I+ZdMB_%gEM'X  J I(x qKf2_Hj*r.BMꍬEP x֬RRqfE2I _.O++Q .JvPyI:K(,vQ劋`'1ÔRny27HDH$ؕ%V ^Wi؛wPK!n|'poetry/core/_vendor/tomlkit/__init__.py1o w~\YԵ[V]U"1P`\2}HwpOcp^OVZ7-gg<9CŨXz ,?@9Rٽ^٦?gؖdj_ W׵m2#YDPK!q%poetry/core/_vendor/tomlkit/_utils.pyWs8~БD$NN;^fu+rJ MxXviJy0RPM$4c]ƈjh>J$ H)wk$d*+&{Z;~aRQh ggy$*I%@qx< ƟNOOHr)l xoq]GɲFO-N2` B}NDK8=ox/$ _BQ/K/T֏}!xYuE >WaxH0'~CbFMԅC?&3ʬC>A6WL{1`EbÖ/#!)mɘ C%F еi*l+ogF 9HēRv4E3=C>$02gsYVeLLZ~MD-d6~ -Á%-B̳5* mݜ#qj.K-{L+CJxp9B!2MP@YLLsi+V[ 7䮌4͉=(v"N/-j:5ACE%w&fz47i+Æe-֕Ź2& 'bYu:d)ٺbdyۊbfvP 5).O0:"ۈjr}Q ^؇_ԇCAjvyDs ryƱDSHЈ4w(B!湿'lۻqq|g<\Bif̮veGz{keIGs"_94m- %RdJd*1-ED^/ h#ºbZggyCg+SpmFCZ˘z!/5&Ux*Qy-̡k~_0|)642ʰox<]b˄ ùµ>P5Ȉen0}"b]ґt'x @we~!8Y@($S| 7hJ8Ɩ,A 1LH!9CGR1QFqaVNB}OHv[@i~(Gc#.P+V ߗe~a}۩](VQ@aua,2E'm YO='ۈ ̡cYkˀb)bQ`X^)v-Gah~Xr,SI.dipR]P9% JDIc &3en='n.,U^>o&H5R-5|"lƯA?"jqJm}b<j xS] I,"QRlt^zD7 ɛ*5 vu8f<//eNLl?~bt%w>ǀO#c(NPX Ԋb |BtUrF[CQBk $p_H鎶Lh-qh"*5CM9J}j<9+(FFcp.%mz5]=[7=A(VIMDn@a_p' z h@$ `z(Y0]q N{D fҲOb2 ]}cbsBO/#8]ô lhQLVStYĔ"~N0J&Wsg@%XI^jJR\z&nFx] mV+cBwj:'XQ+hGKf&g*Rb\ewwXS]ީ>.oy /gmP̊f2uOvVZU8h<OMЃL1.?5}7!o.,WVf %x 硬Z G$c[L@ 5uŸTfk#YSJtSt{/'m )fR$aqP&!H>!d^+@;R҈!i)~ c<x>HRf8X"!CY&6zO_=<Q@yΉ#Zk:5?דW `<Ԗx:rHu:4-jlTe&-x{߾}fx7ko,X]+pQ.U{uںc@z\ b;&{GyԳ+^QCXKg],?K )VL5́H/κ:waaS*`kWV҄۴|ߎמzo$uAe e.Wg1"jOc_= X :-|:d"L# EwlU(7?t  |VU9-=% ̷ͲpzN?c+Ԅ?K1 ΁7Ykpc$N&q;u_eh e7!vDhŃ6+嶡l{'twOg£/<:^t }~Nz$!ä 6XnoΓq¯1bසISV1S5YgHHf઄m??1>0bWRo5]2|gn;L6j׊kiO9?ڸZlWwLf,o0bQ6s*3 Lotl'IIBD/P PK!f:s(poetry/core/_vendor/tomlkit/container.py=]sFXdڹ7u${={+>hYD-@X)동|䒫*1Ettt ݾd]Oۺ%i_+OmC7>m^Yzw}"%QW!oV5وuNCj'n`.L<aE6բe;CU~3w_ִmVtwuoj~8E;q}?fxahdM⇋ﯓon3v]dM-,/E=5`ϖ <Ʉ}HJU`y{IFZC q}7 8_5I{/[$/a9ma7BCZWBcA6bi^mNQl>Y&wUU%ޢ >x r(Ofw洤%}CkFb춚mQBqj/w^I &i-T i=g%ʎMXp킾7 &Y/, E$D)B(ugq㤞66WL-r%1iqQP,ctCZ T%(D㉖IFl$+ҵYtذ)C;zmr؃_oM!q%@nN; ͥ9O 9ma㊚y@S |jgvCbʄx\0˓%'߄"DF~7&l@PW`=tCGi&&젅a% `3GOgA 2b3p?AW~{JצjZFW_{>^rf;-KnR+kԊtx4VawTSP4-݁e;bɞ\- hdҴxa-Jr a\9Y^j{s~wy-w_ނtZp{܎T܉S~qB~A? NA\ PCH0 vw(݆w~ lnxSְS9(OɃ/xPuk{=e?0 fV}0`ѩjR'J-gs UҳfQK|:v;N$ә 퐝Sk(ci?탤B J&*'- !D)尣(:O9]x1'}cot tXySjt~!0 Aͫ7+{İ ; 'MU͛U_ 'd[G!@PF7^.Q&^$MUBUL>GEZv —^JENg% e {Q ˟VKC3E^ TٗL&wݬ74Aww3 K<2(=$3/~[>ݱ`n|ler2YټX]K%8kb#670?yA֨eVSҸ Gc5ٙ!I5T/14A, ,U.ﮌǏ ;\O *i| -w#ΈE^| _b P -% 9\\L7`\heF}% )f]t cZ$hX5Cfrg6,/5gik3Ԕ"jpF "7ajSeO=9kw"kg`I!Etu Gv),A2yz lЍ|)Q9p5LqpZE, 4S llpH3 e?UYR@ 5c8bCxb6@JҬ"h]g'|Ĭ+ D 4hϧD5Dqo2y#ðٚfԂ7RY7;xvǔKNNdڐ  ip S* 0n2pvs.([V2 k g]ίci <#QHPٌy5x'DGÝi\t|3ӹyNt}qR6,.[iF25076_PoIU)UGad:0RKjDG0m'HjLѰ5vgt.Ac:E3 8hX fi z7-G`Hj?T/Y# /v)6}&F"?]fK\ٖp8:2ndF짭Ք8k lьI\Lۑ7N{;XjMwWƐ h ˶[ 3, [~)v.{Rj\qcD[j\ !Y2ho0ZXJYk:Z(V~;9{sJǔʦ?x6y- G" l~cIK1 l>YLjګ>qo]7y:,2N^R]Dj_5 ~ gچ90*2x^ 8zhZW#؍Ճ~:ڎV=V|#Bnlov=lz^cr:yTZTuDh.Yk{\9İwm'"ݓ'/$c驖 '%*[HFԛ 7;%tEOM_&ĦVnj[^wCiVd8q^2`qX9FYUidLV8̒:qf0 d@3:3ƍAz\2 eeh'k$.짜!<R#܂~e! ώMH>}JE89FWa9E:IzxptcW>)E\lMH"@Ѷ>PQ ~6:4ս#zmpHra x9i=OO=&yd"6QaǘBmNJVlK3G)-\9ILn&F5&#[6s4o_wYAcv36E7=CQ eԽDV=,SβW+OG30jbqƼ% !vMk-ὼN!Xý$K -Y}J,wiq>pĒ˅ʒ*˸DVFTBNʔ@l=wsWFjRY (P<}Ni.@+sdS v)FM$VEQ=wK$ UTk^CˌA'rfXB[u>(A%Wk4UftJ.@7 ]q:)ϋN NʛmbeK VOK>%P=ksV#43pvaV3ovZs8x<>Yj%7frK24ivvl憪TlSOD诙P/;Q7~&z >}o]mP[E^D7quUUkn5cZ^% ۩)+9q#^KPUaήc +ZоP/0^e{5-㙊]Di.$}]b'ЊG#s~Y%d%&:L A{-Wڢd5v in$|#O/5з5ہZϿD̾m]ܓNox=ξ)F\ wrx3=X}azDƹF;,v2?3sds`G"cHLDɌRluSf&<| &5c<('6S*a M&cn*dNLIw96|+ ZF!e @e9~|k}(s ;F; aFx@W p׳ FlLX <_|ג@dj@ x蒡uSe8f(7'w5~L>A1Ma.q3UJt'''F S?,-paZf5E@fd)@B)-^֮ha8xL20AEfP;ٔ &bʈY8͢2 7t oPA f\p3 5YfLQZ9=YF9\I!"3й5CiOU4g?95ǗVtT*509Z#WLɊ;1P9e2WQQegB)Vޔ9jmnefe]sL' FG 3ԯJ' [gcq+49v^ Ky|UdPiH‘s8ͼn'r:@~OfgxvLwAȏB{B{tm[ZzCS!{_n*c'Fndc#D޾[K`qwA}qc O8{H'|JaA~+;,7',VŲ_2gĴ^7!+vhUA?tvO%X`{7Z .o&1^^ocˠz1>h~@,)HBp?A d"UBMa C߄iW5#Y®]q`5 -e m{\1SVՈgl&%tU۞\Ի''Hn* ںahmiHIŖ˳ľZIlŨNci*5+Ě%Ki˶`q+s^vOo히Pr½ot5 6")ϸw_jFY &'gσ>9Hf;BgΓkv`]$n@PK!5bU '\$poetry/core/_vendor/tomlkit/items.py=isǕ+fG[%@ Rv/КVDevYtRYgh.c0 +HL}~*nd௳ju-׹ִuQ=ztZWdy[,DdxvTz)ӿ^!~oz_BPjgmQ?6dGڪ) oVA\,*[l2ҧYơErqQZV}Y`ϪyYŢQ$yuݻy3VTpU00ˉf2[wTx8c?I':;[fIYC% &9?-_^MΛ{{f0UҞ,:9PޜslE&yn%0@:MClf0z<5qd Զ`~Ld%>6hXtFh{'bgG4ztӑe0gD=tV 2Ђ㕫"ty)~'&N ~ DFudeņNB YҬW0Z9!Pə<@OfJ@\,ZA{fvlpc+m(L%baeb#!&iCxhڥ`\hkLGkyjl c3|qjóQ~pVOD!9A 68[/i[")(bs?Uĥ:;+rͣk 1i] ө47ÒAHL_Y= /C:|^pr F YVufu氱JH5̔%W h#OjjMSw`"A4c>{xu6؀>¤8} oXTD{9u, tx`xeC| d`)f0yODE[ފB*f{fP޼A)$ފbOy֟SzMl`(jO%Kx<Ƣ@ 41JHp^t EnsFǂ.VDt32HcME_e\TxwX3Ȣ|QNOpa[U#(,PC-(U06C4 z;zj@ Q0pѠ{5f BN$1OXDКF 4 z ͊ ̱zv BLJõZ2@&"K|͆cUDf<4b dSͪ%Np&a~134ρ| l$aJ 1#Yƃ€H0#^Hp[h6еx'fU{-6ݠ_K:Սz-")L_0eР[a`XHPqJhfяiUgE-$^h$Ej>A)P̀d'qc9 yc(@G/VDS qi[ kBpN3\'טѤ2=&Ų ڡ D˘`,kE $E bѕV^Qu% + W@%&n?m0gjBuзv:h<]Ȑ5D5qOJ 2ɞn9k PTr[iC1:rʘC 0͑Kc˛꟦K- _ ?-@^69=7 U 9:reV#tV^c4çY3+ 0}kq<Z>K4% :ÚPT렑~:wU-Y@00Tk'rX-@)[dŐfl&7-7Jq05*gKvur=[*>5.ؐ%\1QbDtq0BX%ڄ=unN5,`KVW蠃:D%=eCZ,<; ~x)^)%8d -( Qg4.셯՘08*7xDkl( ܞP1OheL2ń8[3FBJcH3b'QPqË=B4fk7cKӏ# Ą!j1 X -_}BϪY=⸧8Ŭn,E3AږYFև׸ P4|p3OSVn|G&wpOoeJU5ܪ_1=C!o@mٹkΠ K!pLqz>Nٮ)*wnQ$,CA%`(W/ȈP5ϊ˼L D!@ܔEJB"'@Y  5̳rC$yLN~nm oϰon=ЬjuPZ,o! jV-&ف9.#ݛgy4e|O96"Q`tSGPbqЁ)S))< ""Y55biASpҨ>oLg4=iMy@ߵ&~s`a%x|dgx&Lz?x1Y\5`}{hfmr\o8si!))V QdϸoF2RLt [ o5XbGN(xNB/pXS JwT>Y~ej )Upw.^>aTO4HaՌu] D.J;|Nyó;.AHې]+;ƿdO=ՅUQSJDQ!s;F7@I%2~جhqjM@b0`pTHcɭg9ݒ mp. lRn fDEӶO$43=s2IyFSg_N] Rqn+$Y eH#˧ap7D Ҙ7Bq4i)oKA/u]}D+z*gv'²i8%Md+k YѠ!6hqC a20z qOL( %EY[q5GE[GFʟNݵj pNCثMI [JԷMWӈ8Xչ46=+սa28ZQ≪x6Ϯkd6e#GI";%p;6 3 -cRLBg$ c}V< u*|@6RRǦc$_\}|;-6]JhOۙ>.*Q_,p<~CO0oz#t0-ldh"`'L p qQ3{D =/OfkvB?Xe2`&GO)?j*7+%@ ܮ5맪?Kk!iQG; ]FXYR!_Ol"2DxDSD Lb~U+29D~hjX޺]) z1a-Q)$*KS"mVƴiDkY/jݓӴ6шEǷ4;Ba\?-߬73ڜj>>嬉_ϒx+bsLBֹ}&6F3: +ۈmN+E:f[O`W:Trۆy0NoW4?ԍ?k\dip>׃H˨ooЌ|C;zJߟ03Q+$mhmk(Iy%~Ýi[UF ?lۏjs&t(kCs蓋fe9ؙ8Y0 Z/|ܸ[0I UapڃMk)f7oX7AdT$!EFluׇ׶!/  ~&yV+ ``>)e'3ex<ߠѫ+ >WyF׀>ʹ<6bƅ4hkpR.8V/U;x^/)Seڧ(rP\u ˩-L֘Weskg儂s+>t1 ([08d b"9:F({}1b`_N\\ Q*/{ /Wjt× wv 6{-8:K,aJ0C.9se\^GzL`5f[e;ky v菲PAh>249U6䈡o0[4*зsgih\o83άSHSrg7}iIiS-pX/ FԷ ]Cr5@B̑7|pV@}GQF>L6#4l!+5#UXЃs<}Wy^kAeu?O d`{rlMð[8!í+}n.6! B#M8T0X4wTGm݃C7;"`>4 qQp8jw }OPYC`rǀzbrjQ#bQEՈBwkTx^fZ}^+1qg$epЏ\_ *KcIlZޙiҡs9sOY{p T^cWQi\#9>0ȹ,XC֚뱨|h36X6-aZb4ojnmF[~Ԙ#@l$$ʯnʀg%V# r O"-?:iɷu~OV M2?r#]¦r.mOɨg+>] !Ì#ŭPO6;f+IB6Yt "?>C @|14 &m1o{趓mEP1[Ðt[DB؃"= ѓ>pkYHD 㽋8FoUqxp0^b6,5Mo\2R 0SMas02Y>jL ^V={A" IlՊ%"eZNi{1; 2[O:hg=p[2|/v)bb2:$w6v0`DsmEH }-w4!lXVF3"6DŽ.7Ă:} Ze^K%^Bà23@xs붑 O` Oݏquz-ytp%5r>I^tKjnoo-xz{#z>v]՚.e?@f=qL2.uwsSaz7:ir! ˜vCO]p;rerwp0z1& x9 o16;z( nsz?3\G\zK78v9Wa g 6ED6E%$Ղ> =DP;kaEJqpVD1=l }rc` ϲ9CՖ괶(D|_|s$`( I%SGPsn 6پ/Yl.٨nY+"0O6yO0bKƖ %8RޘXhz9`Uw2}P}3d?f&+_7 _L)!Ӧa:|#B$OZ<0H8UȳPZ YN=S5Bsj`#tQ\Qr3?+Mw@ Vt),Ǵ8س BtI=gxv$z'vuI8N΀1y 6*;Y͏ M3h%٧]2ߴPln*` 01K)Ķib% qi#T`H*[Lm2Əv))aP!P eߞ稪A}?o(*c8XcS>P&2lLDxio0 )MC -h;K4vg"naRTtͪAzUQ=UO~dWSw,j[Yd"}|~=}d^xvqumW?gI{]ח|XQ&F耵 3T/9ރCHX4!8'ٱz (%Cd`P(܆_H쟧·?Dv|O9O}ZhQ ۃRv࢞.ƝfocqMxJl%`XBSӾq!;^l-;!BaڶPq$+~8wx jF,QƞZv5'>t7Woegp{-:+E]߭= !.l/i*0EV v\w w~ڤO֧MFqn /󥤈:g&R?>8 r ;s<>#(5hlN1?Ьs)$N2 ΢e/izsiϏ[qͅumW-҇o7*q0NJ.R"S!y'o :8uʉ!.dڭ~@>BL7Z1ݠ WYpp13θ] T y(4Ƭ&NN|P:-y3  ;. c{zI\6&~>ؓgHa7N,ۂ,=5\sH̻%Yd-),^a캘kӴr_gvngڑfò>#9J^mx,O-wӉwo>%y3Vb-F.`EP+[e6 }V?s09vP<]^f=5W w{y9J]q4fw3>jgJ4&jINL6B-̀04Yװ5n;{&H'zX$ĸ`{: Se.A1$  Be⇩HT㕱7q1-kc3팈1X9@A !c?>fWkVȀ#QJvlLQjه>1~W@=M7yn@eJȼp˴>@N3hiiy~_)7e_E6{nOxյXM5̳&|'% :N#\,iDoWu~6S'M]RN1\\$0FD %{)@#0;Lo@uwu&$b%5&IiD+[3Ү׊HxID[<{fjxcƘ%wBG.nQm;5ۗ}KD}dYNtr0TB/vK\b-z>V䳾aPqq"ΒPK! rF a%poetry/core/_vendor/tomlkit/parser.py=ksƵ+j)FJF ڊD.%\ =]`(;mn碍mgϞ={^{v7]oJ򿪺L󫽽UY~?"EZվ/7uZIzɄMT:I|Q7I,ŢX ȶNJxd_~3iWɹF,lr@fEIR=Q/rBM}wqZŰ¯LŠ*Z0(?#*I.Oy~F|S=zYdX}`zhg:?mחcdahH|w ʎUNFZuSIkY&ߟEY"'z-yg"ϲ"jy|YW6K0ջmE3ntoD(36M: r ?MTŶ\Ue (M[/|#*,[d=W/ϣYt(^˿'0N=ӏte\ Ь̹_xT__8}A? ^dIU("xFUQA5m?biy\l5:GE?`so5Ї5]+OU1ޘ{V٤U,nX8~\/؈oҵ 4-FL) Hz!@`Ŷ,0d uRވ kKd!s%-RwB|RwPQM[l|!-Ǎw_8M%|?E)Y r_p.)1uREyQCߠcHb9m`NTݵ*e utP8!''$_0:`Κd0@4(G_>˵ny orG&:gWnvp4w+rh>JQZEUR*KAv@Bw!1JZ;#u L\ YR;GD4p"Zs&$)-ìTERHEJq0Lac@޵eI􇤼:7wИ@GIt%'JшQ>n<: .jVZ:Xä?CsFDIE7m]KA|2lGUۓ$N ~(C} APp *( ߙtY4:7rj% λh; l6`f[`A=90b1,փMQ-Z %ga i5_o:c%( Fr"&Pv[:L)l [rgCA Dgl}ҁa 5¤'Ug3>Lg >Dw*ժLF(2Rhv<adXd7KJߥM1u&Y_Ewi} 5u )&M?dG 9a哔XF rf*y c#BcEtނܘP0`X'mlu j'@> bS/x'&u2`:NS|a2)9`D=6-Rh6BJ+Ѝ2Ceݰ/84Ag"'hj=E8?8B46t^L"-qq[eH3!AP,@@-%C7"X=NS@I>.a`(<,j& fRE܇V%L< !Cc .`͢1F!Ze`,L6_(1z6@oAϛOH\\GI 7h@|P61Pr4S4plYztݨ\]@M0Iߋf2Ȁ]\i6fGи&",9KM m7lC78k>#93ǚɨC GmMѐc)Eg'@G`wHL$1P|ewդ %5qN GnzdM@d 't4^ Ysq P_~U%5x!0if ufLU`P e`lÚ!V)`\20PrLF-J41I Z2K@ӽNM,ӨN41J$A5I$ҥjTmȊdp)GkuI˟ oz/Z$Fjoi4N+]qo;G]# -Y)8F2% @B-ϝAn}pIt,$q zUب嗢H&0r LPz-Vg FFQND6{wM[3̚>g7vJhf{()tn$PI~MRU~t`[@ɉ^} tp ~s (R忣Zu"YQFhx,/DT RCÆE!w@rfk^ %}.` Q_nnti3AAbI ΐg0 ^PՉ˰v<#j~ô8'X2d4f(;;z(HLkпJzai+ `z ș}l Ӿx)=fKc >6o@SDhqM_^ÀP1V)2.@@vr|bG̭wSOL &ѣׄy,ix=5/i@w? NcQteY][\ })=?\ɋoBtڔ'vJN"Hz|0jF1~xo=N\Mxy^=Γܩyg`bR^m?0z@LzbPDN'7F.w(OPGҩPwoyL }1b,&#cB5#a 5Rޡ' NbXM {%P`>KUg.F`A>I9+.2-| @bp-S)Qd:au-sK"[Ǖ33{<VEލ`5Y&,-4?㦉9{ֿU =!])z yr cag褐>a;|k<JOܑvtFCϹW EoVSj|Mm *pxSNJeEsj0WQ߄']ӫvz)!8=G`Z! /<\/< ܋}tzӣC+ZmqP<"?m7vSi֬NYe WqLh2?:hZ2.y$A~w>Ẽ?ww@IV~G_o-E{ʼn At`^=kTZv$j{F8{.aq)}ܕ=̻yJoԺBw&^cF29o=2#{Q?㿾Lf_3BTg0\V.W,-#u}}n5kҕS軵޲RƮu@d} ٬34q^]sc(AByl7IxyE(%b%;|x|f.EfF'=t0RFSH[v@ylǷބ&ȗT`"c0lD!| %tys)8ߝg_OIaф{1$G!AE 3tk>xȧ/]|45EGG&p_9a8@ד{Hrdg(naAb^qQ{2}FGT.P ~na0v;LË)ìLM0|s{c0 Dv@vs:nnW/&DaPN~p:kƩ{+0'8Opo?,fDߑ`#wO&7 $Hа~u,-`s>{Re 6X+XVz"~x( UeڡmΠ /s\Z1hsb_#`ǨܑJВ|m]_~ǯdUqjkgO'}Mk_xݒRV+ ;Iq_PK!$poetry/core/_vendor/tomlkit/py.typedPK!=T!5g%poetry/core/_vendor/tomlkit/source.pyXMo6Wq7W^mS4ۓDDdR EEEQbg޼pJI% V*c{\5~'\~h 6ϧaɍKnCnV\1JBSٽRR䟂[VVJﲂ_AbQ5TkR>jzAS=)K.)ܮGfo*&٣}Ȃ#U/Lt;)'!iVLu| IY =Ļoc9gE)@%YTMܣFِ#W#FVbT4\CVތa-zR lHHڃ4vh2'%%YYJ%1V`Gk*Q 7 WCCz#cCCmg?SQ7,f4&DԖdE5H4>WUiQ>\q tquh2Q=e)ly۾螁7]L9-"z(O&p=Lj/Aɦ63ő6KoUUưV.#S=3ධp+}aD6Qڇh{(U LE 3@hPJu;PٝJIZ/c$\cQ6Br;>ڸ03:hpz-}fT̨ ž'AѾ羓ԶB ̼1&6\<qs &?*O8QazZpJIT΋ֶlUshO&Bp|TVV]X4y;08307 ]M]ZR-iQT ,A#@:Q 0}\0{*5|(qAxEfA x0d=p"b.DDxςuI$ Pw6Jg"0Mjh ˊ%9ry_-u#$ op)zY/|Ɖo\`{pb-@u@S."7r;^"h^0P~ [ĞM5fCӑ@ag"v}nrD%;Xu}9 hrlJ* 3x=P#s 7%;4J͑IdCDkBg~;o?` <3?+Y9#XXb8Qj%.R8h?e~lm?OIx4(L'ЕnnpOE8{SP[Ȯ.sIt% BMQ7cv6s PK!QP (poetry/core/_vendor/tomlkit/toml_char.pyn0yUN J*V2aCV' o_Фz(J}Hb$SY(((R|sձݧc$XG UT-ݨQu I#8TBuR S Ngm`Pxaǣ7딈 4V7,ڊ!lM|,emT+z}`hZ&ӕֺnVƕo}=[HWȎR u$qWϛMno/? .\#XGdQ jМ)P'.4Bf/ڭJ[7^ٺAGҰLܴ/(%Гygu;j`_u@9%OۀmV ()ZBI *O6S 3H_/PK!!Nn,poetry/core/_vendor/tomlkit/toml_document.pyK+U(,K+IK-R-/*Qp pqq%$+'h5@II L;)@%PK!Un(poetry/core/_vendor/tomlkit/toml_file.pySMo0+cJrzU%qaZ1e,8Kv <<|8P˛QpGrBl{M#J}CVE'G..!$>PgE C53lܹ^7+8@r]ۖpJg3%~mm"UFpiT?AGȼ=xXB bu,LtXKg|kB;Ve(;sܐ $uCHWJw,CZn#:z d36 zDN;5IWGI ImnnelQFsŹk2G^(L354ؓ;d䲑eM lô=s`|}g.bוkm~e[b?G7`]]^rҟ&tmA-X'+ ӏҐGS%څ>k#3ҤoPK!O 1-poetry/core/_vendor/typing_extensions.LICENSEZ[sF~_ص2c- Y%s# C$f6SƤLśMt,c^u۲&Giuxc:LXF<_J,̕(Y !t$Y)bB }|Sl}ܦg Hn,"jrfbB@XVk<}'Ap4Iۙ2yƜ X){Hn!%)@,T^VP¤|eʪtyl΢RPRWAh%W䩸RõJ3)OI hjx &!BqWwy9R߷ϡA%W=A1~~WM4ƾH~˒v96IyE`hM9P|<=zu <8-X7G()i6Ùojdhi,=KI%c/V0Kcx 1#cOBMMSe79GDU%kPGPRF4 odXϐ9qa?+G*tSUL%B#"J$2ĉ>c lB$$xiS A5 ɩZ:>1c\eY(+^c0  urNbUes @ /RL,ڔj UR-SSپ)XLTCvPi''hi|;<u_f:vɠw~/+~BtȒ{xޝ7\LU:ybuTOA G2xշZ2bTMn{1 ~\Uh Kbn#lgQ!gqOtoq^} 2#7I hx3uǼr2뢦ȏ~O pxLb|=&yq_Z"<^=9y a,kvHG`pu}PCsn(\'laϿ 'K# =/?%G\IHG#Q9VB뇶 ȭya([+TZ*Z)Dn׼t~>IzP>%ՠEfnӴSM|Jy QYic |5#yEX|%$Z4 ҔM%43@aV-v9S Iږ":evbILc[^BdYZ;68MQ'.yJK`A;;(LE[(V.pt#|Jy!kt'-{Cx ؂/.T .& `NB8E$1;痝 {&s O<fףnu( Zs2 5"g)9:߆ ~5r?BPDs+5XxDP!0MPp}D{*;x'.O9N h!-4|HA{)K -!aN9'Hx,rrB4 ȯB:bʯ;1)=wR#GJlv_槵d[2NSuQ.፡?n/U O==EYT%a wRMlojdY8S^W&O>';;K+hL}qsyWWe(6e:-7#r_w\h_:qD:9 ?yܶ2ۦyZeժc9GE%b:DC^Tz*kMk럼ju]g}M_9Ip3OS%파S,Vq^uĖ1+NY3P}X6v5VI^S{ܤu*MNRf(5;JPiM@Fj':[g>eL<+҃zopvy1PSNmPDt@R=й*8B> &F%l#jOnFSNiէ?Xgx@F Mlܚbf%J0GS;n륑9ҖF#ٍvYnc֑f_`Z{f,^5Waf-:HM= ִ_KvAxu*kyrʊM/6NnnޫN~]V<g9 dKgM.j.H*+lx\-P*m[~տe.߶dev;`^fEMqFy2NL,cT[xl~ݏe]_<6[ nՋ<0yMu3NI6 l xۼ([weZ.D SiYV- ߸)ɳV6B=Ъ]YimͲEv/5b {T..R?QLa -l)*X(pʾK_i\6[Ai~lM%/)-@OWEf|>}Ͼ~_`J|[}X|VnZ IA2} zdm1+VE{}Iho6G8]r3͛UuduE,rlsS٬`]|TPZje/:8SȲBoE44~h098u^a#ˋ|0=zrf[+,bJ/Ȭ-~ŤJio4m@ ~!L VLqa 6F5_t_q97[5,W $@.VQlFƟ8pyˢ`iE"E5?4 0L @N[DH2b = 3A:?(. @ٲ. KvF2kIL@9}??H>8NqW#F2%č?x^YU'27ϿDF{ |sWYJG.# nݦ{bhMY%"s$IDAlYIx5ؗ>E4-1;Hl&W9$#| d40EIq%k<ͦSU@ h<S'mi>ph&*'U}a4?|`R;&[ 9٨íSoUr46Pjvfc"(m01_Lv9/&"M1ag?$ݫ,.#ؽOg`k~͸Q[Ul;M6B#q OQak*VѪ+;"eu' RF}Wu~3xR&S3: ^.}]lhnBMu G-!Y> N mhfUȈ# ZlwYk5hcmjI97z #iռ)S0( k ˣ(gҐtX0wwew7oPlY:eh#/,5ĂX]ͦF"!4+$$jߠpE͊zᠱ됈|@92;9wAgb)H>(/ L7%d]ݐ&@AI>99#z'Cgh7%9YJ jH_H tWÊz(;b^_+&x~X Ҋg4(Ƶnk+=\ެ1AT@_Irs{{KݴA- V闬}+K{e9HL)6s_Zp)mgM2(D}AbTʁ`9l 52.ʂVmg`lw>Ͱ&}ނio')hY|ݕg-j6}ouX氲ʲ*Yph='b~z1խ,Qq.HmF7a!YxSʛ'`ZY8"2km#:4۔Gґm&>`6H0R(!h)6H82ԅSt.:E tzF ߝ# ̫̫^I fB޲tʒ P=oTeU[dj6Ao({l䥢6쪵.g {,~[N,siD]Ɠ-xpBKurG֍ḲK֏9j:==ztxFbd7C`1VB~蒡(T"YơTNݛ[w s"2$ί~'KMU` CȺmpp tp*VCOΛ4b0筈24\U%ihzRm|2vt+*%ִ(6 :nmSXTs;lC\Vk E>̯vdX *͖/Ra3ynlBXР:_f%U[l]jɅ Yy޷ŭ[:lmj 9!%GF'H|t*櫞$T'p k`H#uz9G$$Q''j. ‹%e,cYÂG6e j+lW骩X/xpsoVHXUQei |]JfzGd4+'>z]Ӣ_C~6Ï}v| k@J5Ϟ:0 Px{WE:xƪۢ> ^RUgӀYc>-4`ڝi KV4d \t OiǦJTF'UQ #SRnjZH . [[KlZxoGHnW|2GLo ߞ`oς6t.Sk8eFu淣zeiY!!欇8j5]kR[Y6 7ќnv;FDݤ% -eh/׳xTe<:`F?%w-Mƪ%S;;<ܺo+&ÆOq&宇FC|GFrIɨx!!-ٴZ(xsYw<n wЉ'_ofk)cf[KF WA%1ylF'۝xȱR>d^D-xB̿;v"ΌANN_>j_Poެw=:C|cxQ/ ^뵷~@'ʨ uDã{Y܆mO;E #&#qCǕ{{b-/_SL#}&E&| 17iF(О=L13U7cၐt*=v[G5D&hi:.}mQ@H3uowzLM3e-ȽS&Cܧ^g}^(ǝ~0 oAwzO~U~tdx\P.*?Hx`j\ݩ01A&TR٠}_j%7jU#JMS-:'kq:K/w],F݆&>=pg9t8-46jlAdn]7m _8>MyN dkS3Cz]'MŪ4:պ"p|/mfU2>vh ͏n,m`RSJS DvӘ_" +b\e4Y\y +4Mw64!2wa3/ =ٶ5~Ɂ.V~LF^ΖSü1L^v TL4iFMt!~%48 oD٪ '#3vᖳis1$q%j6Cԫ>X^$jO>xag@1]UPjxԴ׫5Tf OɁ8{*) P דaKHTd FiI͍/LNhm!gsAɩByqwѾ>ԠSQ;OÈj[if\{}p"uM1\b^ *{<-2f*H;[eFSt#/̊U"4"̯ҝNFdRwv5:H@A..-%zVq"d>=<:#bH2j,͙4=%o8, La?c _EZppqjq`h7$QJ T<~:Ctٳ ]]Qɡ-J z[w_O6&U%?%ޙif 9y vQ/:?q_c!׫M:~Ȇ}~ ߥ! BN Б`tg~x緻֞ Qk $\>Anr9TPBTݠ-6oAvƆWEz3z3,r>D١:!KK͘yngHMTuto)""hw)O&jRt4G|`TF,YZH߬."/|+a&D$^C9$$» G]ߕQ rMݡIiOXI?ӻQKs.( (öسy`uԳ`LzٔU;sc/guP]r2u<}b/0$q) QQyASY/ׄp]5n8Ne js][rDyPfQJJ6sӍ1חQqxEpO"qd@ܨ<$XgŠ7rcm)rGdaz/oݣgO ~Q@J̏6z@ *%N r9aoݘp [׿`-7j-?Ҡ_C 8nl2Ld n]TUAfߧ^@F.{ޅ qў6ޙ{wFL~QI@UozjI/yd{7i ^޻_5p{IbŮsR DKJ^B>r-Rt?9nZMwZPAsw2 M^$y¢E.(mJ@S[;f{-VN9 3tAߡuȒ9p؝U{my"噐ӺipRCay0^F`e:ߢġ4^]Anf^,V7B-oe1?(qrRDJa[W14SMm`(\4-))2`C]\c͈z@dO% .:#~ $>]wDcU{l;W)$3̡dS7Egd\Ko%HIUwR֏z6eUկ\&~^$sA8`r ;kR(_=#+RsUbL%:%s C_g;I3d&N87wp\[9sMJ :8,,; ηuT ݶ2#2;=9D'g$O1ݖ0&pGm8s 3t] U cf,bc6$:׌ݍ5p*b=# rs{,P>HNMD ha~ ttW(iBr UYT'ܡ?=yp_fo @8xאY@ [Že.ՌU~6WFa/%s,Tf(&H#ȶ[B1jJBM"(*O9K'q1;ݨGM.8l\a@#mJ|\iMX Fs9Y7JΕ3(w܇I|hldNο#cIP._lw- 3#c1 #|qdžsW!9Y;/Nݼ>/+K-ݡpP.'RGIj?^>P1#60 ^oXgf;[|b;Zea2cmP#iƫ@`P 'dc~ɾ:E^DYRi ODzɌ9~!DNiK1m:r;YY/,t0urkd#zU_ / %Z]˝)(ak58:êqn:P3d =g+w 1p%43 ?m=# ze}sxQbK,NP* pzeУDXD&07'޸;_ם);s󠠫ҩo=> KUp;`3DG"+I8f T Σ`TnsD}7{S}aGQȄ0z[Edf`Pp\˃#FF<&PKXp{!f@v5xJ&!fml»F oW~ms+ZBlf|zlKVB>[-6ݔ5jJ򮨖8eZEVN(PLϟ'{)spuB 7Q ELU@{Q0i9l} 1C+~Q*2{# @k 3YŸǘxzrB]|h{8A/WW ‡Gg~#nXwS\Xy6Λ5Y)A5DOH!K-iDtbCLyf{zdrqE<$£P2la3^$.sx?57쓯j>}[u ,Ɂ\St9=zr|qrXQCqlcFqʖ|4էCqjT6<-)z*,w^P:aBNC,&~TgD VCaeeiUHc}d'wmlrm YrB'^qb{#9Ɋ8<9w85Y7i{\'+cN\L%ayA;b=J3 9\pz dpUٙLtrwO1f*9ˋWUFuwƁ8ar0S2mj[dwZ:-{x0糢 o[>K6ygfY1+B!ZN bf׌N$RP T٠=µiT[1"6â\j~Uh/{1$xjT?k |vm ~&oп3cj{C¢';mHH[uH֌8X26){ ܨ?_MSDh7TX3nvbNz:|&Иl?3 s{ȳvM_ɞaW-'Lͨ .~d/R_D%Q:/kN,"*kG`kn^m thxܵMXiU*rI.u3 ou ;I^N†Ml3BAHbsCmf)क़rUR{Q^__Qۺb(nY%CPe5>L ,4~os&A䯵z ,ͽk b5x-јCaPwoߍ _ǔw$v,2ruN>V{W<)+CՔQNJf"3}2mHMR.s?{yMjĮ龄/uӻn8'Jh|f / Afk&,$'5*~dzQ7e6!q( y m#FE(Zf'r v`"7骺=|.It[ԡ")MQ-s7LBzS cVr-0H#gV;Ɏ!.7KG`dXIFkz\3ŒUekFӡoQѨ\n"'e }oR+zQ5Syo&aԹkW6n7mIta,Kd6$hY`_Ή ZvlfV̬$r>%g % >Žt渌.g ©n@q04d7Za⨍ ?$'-J6D-99Z/7RZMO_Qè䇨倥;kn_RawOA9 ? E²]sRi]bҪQhHB0 d>, "atoaT{>XErzMʊ V;Z^DGhWĩAZt+v!>潭紮4>K<VinBDҵ5H/.  +圣ωI kUn]Sт咫ڜ&)gTx!+ Eلz/3)`81 ,~!wu5@"n mrAFmƫGg X!zY..du5AƵ?luִPm[|j;52JVpՒ1mtj{*@/Xⵠa@-+[V0i͛,|l|T3kkBM7%uOU@)/|Fx  ꉑTV~?ʀ:Q$Wdwe_n5B(Q"}aP!-8W5T JJ4InP- cjžNe G$ "ؖ& Ϙgxe@KJH!F/PqB&+ݪ ԙ$]VV0:mѫhp6;<%ߣUlSZwTjڨ&B:*d̈I/S/ͨ'80;.ó9`6Up5!;]u{W?<] 0^g :lsz'8f#]]]]]]7?dďyl`!Se$AGs( 6TD범PY-yΩ[^ˉ@ʕ\9z#W{툂 6[~pXn8z0Z(5D evDv`շ8Wn|P#,~~Kq3/3}r9\3onlG.?^5;g+rE\+uۤj7`wvesڮQ!yȱM\"!M^QT{{>ﱒm?TΎ+ֳ1DOLbd[ykRrN3sj8Pa8-9s?+H{$y|w$rk /7EٶkPdz(͚wNjWPŠ~wX\|,c!۱4 #"r›~]`5 pEq01mbu &tFzgN6kD?";WJX&fU2K|(Hˢ&XoMj\e[K9pYJN#jQ|s=;;2\Z!qDZ\YR_הAsNY` t`"A6 7ڷ1,sR3 'MC7ͮb6+f oA7mD&cR>:KL4?~hPH'Pw :q{/TZerN<7ToܥAs"l:(=w dGD)7{iRg>m%IKQg/|kk9Eb#eLyQ@'ɤNyt1^1Oy),v?ߎ}JPl/zՆswG4,IPM>PX0+RzmsT!o%t,@_,h!aAy"6<7||6x~.as$}mYE@xR/n]hC?Fp<918+vh/hl^7as.ǩ9m#r1+r֝+4a9.CTP# % ܏$( h2,̜P3[5ޏgÏ45/..OOŢLR=Q355F50^3֜WX&X_>I>NF WVPcJ09kL8h\n NDxTR?WXGw:RR@Aa#[;vI8r2Bȴ=!;K](eGR#4[e\#C l՛t:$$,FnQCRE"tIcy9%1A%Ad1tdih#h/}h|˞ah[oKιzQeY_o%䇁YE4M]!"&նl[ qC:RmjA4Ϯnl@ǀ]VYu&,t!O)4s^6#a oFzpedIeʏ6-uWezP{OKƣ9dOl֯R y7_[jV#\p۲ 1G}~WooyЙEI&7]YC&0B1YO0LrETj7X{^t-dBџ1U!M{ zccwO+"/M)BgU{DPQosvu ^_MQ[wW1JN=pYx[ӎЊ95ߟgcƢJ:HxFA{練iÞ(! XFyh'>/Ƨ?Hl2]G yTŸ/6^= F!Nn@<c|g Tu]s<%"C?6mo}bv9l}ޯN caVƣ::Ԗww7RxQ|u@054Due *y#"P󜠄=P9C:5Cw9KxDv0||*e b53xNcrpp3tǀ?~Hy =(-&iՈ)!"GYLvF}*.n$&vɨf#G$3*l$a"{/=cf 0A}>#76pjpغv!>~G 5':!@Q't$a1V(vZ8ӥ՟r0*E=w6(vI;,^`RkIFXoH|;'4'vfvY60n a]䈘d㦃F/k yaYVN i`7Yu6zS5& $Mۑ$] \~Lhâʛrآڕ0l(X8d"|iI$eO7c>НE3k,^|#%X| YU=p"mГW>m3^] %md[9%8lVżh3X ˜h# 8&UD\f#%|L#_\r;N9!|$hAGn4}()8VY1.m!~Fpu>-ɗS[ V`8:i3SS64G:_ 9^dҤviɒ)$Uu=$r=m7(|48хZcbxQ?Fv`yQW+.,~ʇMUW䆌Pf Or`Id[4c~wFσNN:9V0P )W`4n겘@"nݦ Bb\M:˺%/؋px;`x;<P\`FEֆ#E'ehAaŞnJZ ?mD[J ݦD 1F6}2Dr XiRKB L4#\ণ_t@iP_qrsQ&l 2wYfaNt!rePIdYj{Wk[x$;=` $1Aq6q3lU^ެFg!/pnps vqz.֘=N< Y PK/1JXƩC.ew \dxHXXx ZPOb|ʕ{OA,0  C;[n&e+V5v4gϮ16n-{|=uXwnHXUIryR.;x: }P"@JeKOw.-|mLDҮEK[qqI`,;m~c ]2*i S6h 'vj/]3Gb \}qMq"Rn4`@@JT;f1J x|~!r? +(y .PK!#poetry/core/constraints/__init__.pyPK!_"@+poetry/core/constraints/generic/__init__.py w8GG'c6Ԑз4hK  _._g@kCJH\X-xƆT"ޢxB|RPIya:6SzU1"nU-e$*3a$]]o9^aV|^36Gp[ⳆBc~g<" e&2.qMȑ?PK!4WL1poetry/core/constraints/generic/any_constraint.pyRN0 +Z4; _\uXP'ncIMh9l?CJ!JIԈ>h=!i[7?"i1@:J_'?QAȄhA>b-|Z0R;Cqɫ\{`PbƤwK6ނ6pmJAݫR*+;FY$L]VD y:VUj3@"w; ;"O'S]Cvfz.d(R LqW:'6XRF!'ΌWE,PK!Zc2poetry/core/constraints/generic/base_constraint.pyJA (҃W 23Y,[kAg&Q8blB.,`NAң;LBWlY[CBJѹR7֒,/bp5s:Bs>Qb<Յ21zNt1POkw'Hj6P|&ɩ׊N>CRPm$( ~wC\D}MsWs6h{<]k^yGPK!OPs-poetry/core/constraints/generic/constraint.pyWK0W9%RTCE@rA(kYUwƎ?n 9xfy|;;T^kDw aƸŠr&xGV\dY:N87\aR LA7s_Qx[,I_:j垳,۴XJDEW.2O3ZA^rF2P95jp 2_n*/}Xc[9ͅ1zͅ1[@g) IڦB?j{:bڷZ@Hol8\tM@ Nhy~O>3WX>z}ԎL ߎ(ukbMm"6ǎs7k޻~!/nLUO; ^u"9]j,=Ȱltе E$y](۱iIJH>R!Ԡց,:G;)yPȢFa_FemZBLP!5CcΙ}Wo}GfȇP籺MȲU $dl֦k,켣YDd8Z>ՌC蔊}ƳmA"$|Q&0팵ڏD|cٿF 4P:9*ѳG%ݧ;)js:!hhJCi~~@xj?l%!u'F|.2)zG|!C SWS \D0{fھCnBx*)w{1Dcioö_z99wt=՘c!KL:Uڏ&,gG.52)n'^SiӀgiK插BKPK!Fij)poetry/core/constraints/generic/parser.pyUmo0_f`3}D І&ie&Q)#LEc}gț t4R"|s/Da[AyDc\F2嬴&e%]mj/8?Mg*S)vN+R&KgCiDliv~c[E!M;` C:\W%yJz&ǩgl\t@̋4X+|㹮GeËղ zַ65MP -"Xt72 :uݳ[:g!"BR r+vEZ`BjBE/\hRf$jEl}As(͊ݣ~@POݑE؃2DfežqM3ʰA 5bzT#Fw?܉5>@ hvH[Jxr"),QM6< 諘7gʟ  @MAXbI'^j)V=6uq*1pK=NsrQ$S$s#Ԫ[Ҽ9m$JK48Cv+o832hS{I(҉\6[#GkuӾPK!c3poetry/core/constraints/generic/union_constraint.pyW;o0+gZWhZ')С.A*(63IȏzeJѡ wݧ{R-rv9ۚ Ƹ*L&Iuԡ쩓 ZA͉l?&((S2{" IA!,a=*D@:D`]h޷dUR 9c7e&%2KRsr~.E7Hc(hI@*1kk"ԡwjYܻ.(˲ P;,tEUyjC]px _Y@iK}vFl%BF527Ahq\T$Q34nt'v lEϜ#ܯԃvk!a'#w̲%&8v|' MڲԱtmY l|Tmӏ"8JPEt]g'j{@V:7qY3-Zi%xCmھm&mrnI͡w`IVd[SHO!tkiu&K+K, t砷[/Whpj鞇@ _Bm dþ$THs*O~zqŕTof%V2XsP:ua9x1N*NR/g8M M U 2^8Jp%QW?;?QH+T\kd}ax50o"f)0s'9-y-Q(TaN4?ч!hc0a4$GQȭ\oᡶ%1fVs]O4 v5<'oS 8~CEni#BnZ ]+YNrƫ bE۽M lfA42ȰEhUbŶ΃?ɗRn)cC1*[)WJ#G% 1F |kRK>D + !5Aoc8M"~ބy܅ CI@?0  g-i_org%٪^2s5.fĆ6_/PK!lYsK3poetry/core/constraints/version/empty_constraint.pyJ0n@!!Nf$Kjr=f[nb.sHg7sYbs6uȄ1*k|2>{ytrpxmGu@ eЏ>y;L:C$JF@G jTSN?)%k=n  J7%ɔP| Ŕь?{q&)`J_zMCL0+.hCmat|fk+ӆ8w.sikWq 'Ƣ2 z"Ν߽ܽL {p.bYJۅq#0ic}H\drD"Kȥ4A(PL $dI0 "!Ai,P K_)$0FT $/W;Z lnⰒǝ} c|;@ w7=6Wd%YDFgdd8ffK1Ż{$MitU_d=p5nz;\`l`bu'**k&'Ŷ<.=jXM%UV *J3Yk |٦1V,zlk7ټ_; Nv狩?rvl~ů?t5qx7Vo7]by>W\q1nj]ђ"^/mS_z݊w yE7WNc?)*}DbaxccZӈA T6 |-S*  ʗTz5VAU#(&>-.(}{Ek)V s{=l. 67V1K:EE`UYZC840*)i +C6cmJdѧ X%=h:6eZQNJ\W'"@A$c*翖V6Ī6"o KtV} ᱔`UԠ1$s[ UwLY-F-+ͣΤZ9g,wAuQPz (vrM w#v ݏJF=iQV\PK!36+poetry/core/constraints/version/patterns.pyQk0)iǸ6y5$b-$%iϾ>c͠5uoFVʶ1δ dޗ+^} ,ܭk,2 !dNnXC >cg8<Ȏs~:G 3~\’wA^O= jzuG}$n}ϩ__olr 8@[ +gj =ȦjIRZ;iwLQpOp%m \)5X knXrd;&z~7bK-ǾVgo.й_PK!'5'poetry/core/constraints/version/util.pyT0+lꈤ[R^ҖRRX1"D2f;َI3o<2zEQqҎ;cS-զ?<~+?h<6o:år"Q[6%_},"Y *sYeF,=9u< G u4Vtu%--w;#VP"VFk>5_ VT}ppݰNħ}a6Ӊ2؄ƒ1 Bmq%dCHOFAj +d=kqζx ~wu1LۯGvRK.4u;쯉i^+A?A?ӫj5a_ ,(cD"CC$e;o~ :)5H@nUW0vqŚz&d!:͓j>ޑVnސ_i,Ɇ;k|`oo2o/ !> K1^B@llOe׽ y<~.g;oVͨ/PK!<堸*poetry/core/constraints/version/version.pyXm4_a-_Q"EEXj6Glx$NJuyE![RRlZ(ªVLu%VLdRrZN훢zne~?\-GSs%IV C%`Ed 4jOFӯ;i`Ihfh~8[d["[_5͇iE޼zhe3ɏ?[ۛ[N>t<S $a?+^}ϑNc`ڕqIԟJĖGZ}ÁsPGEv x#t~3"Q1uÅ f*$yGKfY"z2⤓=G XHjy~rlM4T$5€Bu]qKmlBxkE#vf_3;EgOoւ\ZegmNW\A(Eogk}[F?s!^<'mYOF)ܮ8Tץ(U#"1 MgKI2=A?VDcߘwXp 9u!cBk~jъ@YnB퉪-d,(V|Hr(C{t&,7%d#-Vvy:Tj]oUAҖZ ^_|:MM>Dz_bU "wmYqC xdQ]ZjF 62aR +88 mm$,7ՠ'eWG;\l7zŠu⣁pl j%ֿn_1иB/JW^Kh24 B_="r@V r-tz $0&ҹϮ ׺SV)l-<=ؔ D_?pB{pxцHL5^k^KqMRcج 7W} EC"1q8a8QPK!D55poetry/core/constraints/version/version_constraint.pyŔ]K0+r"d 9Tٿ7H_8rМڸuT<<2ɳ:I?NK BZq@>1?KRn rIjۀo٨uiDaQ{t(\ Z/y;`T\\MGӀ ȋPc(s"II޴9G'+kwؙ< `JVCI4 {2/@ 8 UvOG^{PK!`u 50poetry/core/constraints/version/version_range.pyZo6_zjw!{ bŶWPwm"iȒ*{wH?!oHi]I~Yl_5'(JNyVl ~Q?o;EQק()k?4+x")hw40;iŌKKAKX͖ 9S#+̟Žl1ns+(èYӦ,ͳle)FVd<[kaygō$'?#kˠFi"[Ḱ[7Ih~&6(f5mƴ$WM\p!쥧 =YԴJ{II+ EY7>lD%U mB`t!S:OMt}K/=c.o,xØO 7Y  >"š0 Btt$gUXVHv֢_\~8,ęzdXQ,}E&2Lh*vh89e,6H(Ђو%kt}* ifg]</<*QjB?FL3`=m<@>0 $P*åVП& pC|^y>VFtF 56AHOS$j!#-U=Aˉ5Di_ @=]׶WdTkF'ۂ`ʥtnŗ1Ht$JԌ셪4N*h=d.9͢v x^OXf́<[XKGo6`9HL߿fm >bЃ9@1+Wkq"ZCupPXa7/^N7l25"oWl!RU-V"1>#g]t MɲH)ö 1!A;Hnߎ6oWGr30ոa  \>*gDm%sZ%.Y̮m1z"z$:oweYHߑ x1{F8Zġ2)$F;WaGf1@[MnqFFYN/ 籗N:[y;S ]ؔdE7,ߟm& GA)u# 0ibBM,xV CӫeG1 d/]z\k?(ذ)`hF4q /o !2E)m9}%t1Ֆ>a3`bX!Kħ7:D#jDE xe'UŊ*w-ĥO/[{8fDZ}*~c o԰p\k:xd֬aO͔22㜻F h;d_ܒkK-8.dk`)|5}()7v4"Fq MtӱY:+Db_ )B&ai![_ '! CkǸGPb6<kw;7c}Re.}lQo/b~^*^f=|3G4~$n2w\=K܎+&⊧eR)l;؜5eͪ3O:->Uip;L8+ѾBpt38qP*CP"i9PK!AO ;poetry/core/constraints/version/version_range_constraint.pyJ0n˜^="iRTִ[7-959IltB3`$Qv ,!q&8%:*n 2_lr7B :2CɭޟZVrNNU&ALdЋq@L+(1X 'hj6gR*Y3t;z*. o` 9n cvC\4al#W* 7pI/så6-bg:*{*Eyx2EJX-r|)˓-ˬ|?avԀ{!h6DXNկ샦T&ennosZyՓfݛݽ )6 ߉{wmeYYi;u]b0aW_QA޵tմEe݉|yi#~F*<`FK0xۣ͋ .im_+k<#&~- PZA2u`৽%/Đ9!7}3EP9;#:1|ua^Jȼʽy}H4bred>f !m 6L4l1 -x((PiFRcK%_2Ztܢ. Wz\8gbr{ݶM'f_ut2g* mly{a;gH\| c_vJʦP ݁Ʃ&Bp`]ӯ6iLRh$2%_0_x|'Q$XP<ĭ"DT~/#H7.RY\"N֨xH)M4(bKf ٢\*B~wMSut`#Uh~UG♪%;yC''aEAg @ ~(!vWpit=%$b(>$IUJHVAm1dOK 5`>([ xM iAQZbSM ưJhEyqsn`!Y*6*"Z&-kjQɼQ* jkCZ(RRCu oP0_#g󤠪/CNS/|IR>EVuaPO*'s yHA`.aJCxu $g ц-no GQTP'1~2]>q+bq^?ŲYFդS{㍯n>affhNݾUjLI7 ỡW;29c]y]δ&ְǚC6re5.rP֊+R.&oPв`W2q0{ot(i!@C H ƺ z&q$4TIN^uz~ޤ^X0ҭx?>EAE]FZσ Aְhn9W<XE9tA|$l$dH 3Pr[u72B5b>HfRэDͅS=Sl椼|_oNa+-u5ȳ個 >HʆnLXZ}jncJt%L3 &0lkpωնhdX[Z~Fs 梪={򞲱'y1L7V gZe wؒH$=I%$<#mre*æ{?C&M>n\>"&3d!5Owj)7]QI#Lf054)sV v'k-[8`CBHFeMVjCMNxdM=lSk7-!AJy1o1D37;:ljˆ͹_b?[6ؾ0ffuKaI|VyQUjL!|:j"ݵ]8kNls*j(sw̵yD3zc?OLQ-i| `|-X g/'GF}J_DsOZ`NьfZ莥 rcIRFSf\2det-zkcxƗ0 c܊Y9?>iX"pQe. `2'cR/Q"ED H#+&~toN"k: $mJrnȫ_*)0k5Zb˸;UWqo9lV\RufKjR=B8R@~+Q8d$ _zh3=u}Ŝf7\`#=t[I6z}R!ֱN>q7x|røo)&V97ħYNR]iܵMtt= wQg\Kmp疇TZ.ɻqp&I[2W`_PY=F|bi7G츙*9Px} nˣV%2qakK裟XNJ>%DUa( 85@A"`Ts{ j< qک̓ `k ZێCV+RΑasA sˤw̏xHT[QN<@P"(62.ILlPQҎپ릊05  c?85CC͹WP}"d!tT ?۲INm*z Ҙ%ptk>/_6=mW_ˆM+`KrX]2/fQkJTpQtT\³R07BH] h& wmf_l |hf 'G)92:@u'UH4O9XѴζ1,HN:fT rcD!Hxm@!KQ3ahhl켑U‘%ڥ#R p<껈-:{ с ˨q"^즖V? J% ۋ#U%MoR*jϙ3Gi>,%΀&ӯ|U{[Z!+~GӐ5z\'WA}P.o*@4SʫM{$`m 5xɀc5Ey(̏C،&h'uO؇{VRW\y4*0=nLⶌuc_˵H*flRm \#] T>aHe*G&[ =傍(Wt`AUnC l;%~g:wXe AuH_D}ۧ'CB@@tO.zU 1gMf.N=VMJOϪc. Ĺ?,ƛmκ'Y ܦT-p_z^%]@T gZ3j'n+ebw j׍,#I ^D;%s!DI웺Q&1v7yh0^!HOU~LR=X0.G0ssqt)VތWHgۘTt^TX#+#GrK5U}NS|5R,x%p.`9-8>I?k p~v**$?tL Wa|pKm=n,~4Se .5[+ #[(WI~5g'T:ڦ&it&/)xb=W_}%%vMQ Gwlm_MBSK@=<(&rWT.r19f-+w>3xOZ4H:ׁ.^5w`9<+<8u /:?2>_o Z6U:|dqlZ|Ӿ<]PK!opoetry/core/json/__init__.pyeRM0W Eז@ Kw=҅^B0J4JڒmBvvFZCяT?kmRp8r1YF aELR?P?׀`)G/W_ &0/n:Åc2sL736'ls2&۴%zQ-:;bEfFyRM@a| S>*6ڬ,$]26lsAXM-x{c'Cr%H='D ,BۓJp>Em Qj TjN}&qR1A%ӿ?O@βzOJGkwx2 R;32f\M;8٨Q_dCKRčc{LaY$}܇w<|9)]^wX@5߿}7hC r#%ɉ|[s *$b=h3KӉ6ڜO A74FŸ$關=cƤ9 &ZfE9 Su8'oKc[='PܗT*np!X+6$y `XK?e_*tS gw #yit'49D4De-2]`v+hۗee )TY?Pg}/$iDJO3 ЪS\>Geي9)3w΁N#%d;K=QДteU?>ڄn=:\ZzEAvh鱒20SLސ,gN](1ERgdWx57q*,9r&Ղ./DQeRe GsvNeۀ|h2YC9rh@ ^-̦F9c4`]B/gW `ԍ8ҹoqG_C1 J`َdNۉSBy8y8 P}p~2QɳBRv6eX%_%_0oOgkʲ95rr6̇{q^5Dāt?{S!,nL"҂6[\ @q)Az?ڃL!NkOv.Wx_Wsg[G|k#vݫ= ~3`X:NϲϽV,mPc3?:E$U aېQp: njp%XMx!JJ@44oE&0 "vSۋ7kD7GQ İ.weEkA?ԋ R1i4}ivw-t׵b{M?}d3Oзeq@|6 W#} Y_?g5HK!]p`ezN\ТPK!Y} poetry/core/masonry/api.pyVn6}W  !h``lQ FcAp.cTI* Ii-/Ⱦh)ᙡ<..N;ԦfqUUkM,r 6^xi˲2ۭ,L~j~`ۑ"kEgo g4-"rF:? ?wj 7˨6X-mRM- -:ctCЯ6[{ N뼭1)YxTY '/AQ4"vt F*iaѡJ:^Yp@@A*)Z =8c-[ HM֞y)hB;*zc V#A=]XCw7 #||$B ڥFo Y DA(9j=jt#jk:gpN^ ΔPԵD$v% |(J=$Փҙ[a^4‹vm(TlS*%f\ڛz(YmQxi ^,/7=%qFt5CVB9,c(ڲ͊LXRbeUs v0.@uX~&.lVAӠP'ahE^1_+ک^uv%͟J{2 {ljS 3wj{O`-VzpHZIT@5 w#4al_t 8YMY!6DlCgʂiH1ua*|P<ҷA wsr_Hؐe$03s"PK!u$spoetry/core/masonry/builder.pyQK0)BZ:1P#dd$:澻Z%~%š2&j_[``\k㹗F;BDA~[a6}/ !R Bk %d51 y [ŝwT;-xMK-=c%&2zsKFCڮh 6-n'B5s"5Cߜu-;0ag—4&bo24P2lI?u7?Q:o'`[{Qlp DCa2_|ȠROP9!f5v:\Y҄+<_cHG\e1r逾ZcS5m1=as]ns8f4qZ:Lo>4#?PK!(poetry/core/masonry/builders/__init__.pyPK!x3 1'poetry/core/masonry/builders/builder.pykoܸ V))Y+~\%<Ĺp\Vvupl8 ot͡@.`X"=Ù! i Cut$*˪*BfP޵sԔH)<1##)'4K0uml-/Otw58^ *ՇV ?N]sUC}<go}wJ:˩82*ӫ/?~Deh{ `guuj]4Y}ҪTSs~C^d}f,=r3iNYCk4VDYu&mdugxFښYAFh~Fi0 (׍ ,f5ž*hVwgtN\ױuMHnA}, }YZNFh }?ѻU{H(A4qU)@A'l'}ڼ N##l6ݔ^"lRaN@\pH-,&dtkGǍ@qb_0 PwD1ۮ -$|ۅ<9GH0Lf s$eݨEM <*IDx}@G=!7T Ab>d(qDN8%iO%!3ϝYLRRx0hQK" @%Blº؎%?nr~N͕3 O!\v#VLɝ#™hA*b84 yG986⋫:KrE;a5v%d> o]zi(Rm)ŋn1C-C@Y ;U[P(] ?3PMG+>>؛RR]Fw.ÈlfAMN#7x=.j[(ymp fH#ұW,{,7VԹ$_`|]; qt7-j^0sUW\6ux/:;^Z$ DjK#e ȳ6M<G\ڞ].^l=f&8[M̢Il9b/Ϙ2P,s8,ܥ++XR,Tȕ?(?_xXV3߳NϕvG*#{xa;z=CtDay߂D<b('ڇe)mXُD|z c87GRUQ˹x_ ?!;Z~u$Bt*1rغ1n.dzQ@؋uazbb$ߑP,赡jei6 2rv<pLKs̕Hun _`Ãh~A*3AB9}SU\QxydN})AD;6&"9+ʱ_~PUu1rF%nWvqmae.DLF bN VJ{dǻ-Lɰ4ޔZrG Hh6`ai^Kr?E]q?.b0LR€Aw ~Kރ3 7>HJ'NǮ0V6cT$"F956Y˫N^=F|(WbDBD\lm@ ]ph{ 8va Dd"%F19_ovŞ[ѕ%G>_W2Fɿ?'t+My`a™Di0l*O͎#ٮg05LIo^1硉!f*:ػ%{ GQ톧m]wa䘏f MWJW#O@ Q hE;TǡyF(&nJUZ:2VM 򝻜0p ]f$[EY *[,pPE=/Zlր-e<ի *Wu?;^aS=S+a:1"iA4&{tO%m)Mǘ5n xO|@1B:.q,.fwi~hF-ێ}{m(OnGو-謭Tlpe<51R6`ƌ2Qv?տR#7g-e^bFz;ζ-gW2cz4ufax%#9IU$AB9Y-H}g*qD [R(TI VK@&vVN\CT$yeR@[)ỈjKD&6NK)D⮻z>(*eՆ IǙDBkj.&E4&/FS 8 )`=;2B@f rc @j?1ZihbM`)>>O~${! :M_:•x(%kk׍piia[! \@w^r ijsM$؉~p5LNO߉@ZI-j,3C񦄪< Ӗ_ 쵭첑Mϰ @⢺Qn/!C7/ Q&N"K4ݓN<rO~q./6.y^q)cEjnB$IYd c C ZWRV;)Mɼ(Ȗe͘q|&W_R:lj0K콛8 f7XGm,3BKfm~2'H&rd=6GɟSuc4]fn( ZCwUWm <lfnM%b- &,8]:Hp\dz&UĒ(: İVl8b~[wG]HYweTd3l<p=C𾝌rj,JL ۀu> Jne} lycs_+êdJwv^]巖t $jOAY~`g~t%Td $  lmM޶EN"ڡXq, +j)7WQ. /LRRyՅ- n{k(z@o1lBxi 0ѯ wցE@p!ċD'SxTƩ[1YgWإX^:;{gȱ&ka Կ#'rbY4Ւq7f%cռ-뷕*-"{> {IG9MHesN,G­_j( l!%0E呩U5_7FZ$5U! B?^R!YI&{PJC+<{$륾7F cƸ=<7]LbPqb;a\ٺd("/`Yι{KZ{4}Aw]LiH[V:+e68LhݏgOxL 8ƑFɨ3Zj4YC߁WGY7yvy9!IWK}8_4|Sl @Ba:=/ L l 'xi{gt% b%"4ޣ6WX VqM Ww\؇|C z;Tn-I]5aM/oJXH',]6U>}{$X8X;> @7L>E?Ցcခn4LaiV NǧG8#@$rC2ĦM`1X3k(NÛ_tCRo1C㯁"[#?NCIV)=*3gYX`ae0j掬a o"'OAQ|sGÅ?XWb)ѷ^(_QzSW!^S@5^d@.$y)֐ټ>dx"gCc5kGof%^Y)dEgx )+fyXUSJRSQzn'wE1/9Cn{mſyBIPK!j6 o4%poetry/core/masonry/builders/wheel.pys۶w{QL;n|ծYⴾI.\! 3EiYq=| ͗$Ě颩)RT5E!jZsQ|fugycWT\,XW!\557X}nfe%L5[ 3Q^7U.邥/X1@\X>%\;^@-~i;{}fE ̂k,IjDneH&sQ1 7 4BE-f畴,uk*E/L_#&ʌ˺3<8@&+Nř5viA Qi?$5RtXʋyd̩|]8 X([!#F?hb,[AKiȔDQ': ygV@ѝы'!3y Tܕhx?KxM4aj8YJ2i:vs*%Qpw_#j3׼u H/&Éa.*D, \ [blt2nfB0Ҍ_#IKr7uQgoIZ1I&OH~Y7e.A'D#.zStZK z2}.e G׬^^89s3eY Ǿ :lۑO>tZ1$N{i״LӖU zΕ)%ؗL 88BZ+S %:}[f!hFx+*h&p0wF ݮgLxF|<;{62 6y!ߖ!&%X&q״=5E6Q铲1`r)ftC5O% C~;<l лolf9|r'-* "%+Ԟ͗hL$L-4YaO^]+m4DHceivw.N8eBaȨ/ 0M-8+,`e_Ҿ'jKx\[i\ [qXG_xKC,Kz"YW ЛҌ:H~DY@M4E΋ѧkd-noQb7m;b5 PxEl0izoXJhjo736/ %X#*ZYl`&}LHPAr+KQoaB ?1Ȇ@. ADSɡˋV'@Zi]_O-o1 ?V O+]:owCHb/q,YbGP_Щ >[_8tKXX :Pu:E3:ՃJ{ͯ98f/uxIpڭg8v#s=̮9fLH'?<ũ8zT?#quQǗ{!j`4 y@jl BlJ K%Mv"y=`;X{=˜вdƩN IpOE.Σq'EqZb<(O2~(%VM:¾?S8KcʞlvS8oqoC_Q!86j<{(,|RD;QXYuG}c"_!Hg_-(p)*ŠW,H*0w\Om酱ʿy5%V[jᑩwIQz}4]0${{eʃ4z?Yv?x hcQc\_ li3 !K PxȦ[$~![űòjIskC $b#SZl{߽Eqqro?^ƀRLNze&?.mﰜ9-Yd}َDJ]!'ͅ/ԄK=cИقU4 mI YED*9as|?u rwZפjGuN<9;+]Q7L JE ;u(qxS(ddXͤ a]aNj6!z @K}ScM$Y3^5snDA yşލ /_p\Qp|o]QPRAh]cHnϘ|-yDG<=;s]+ЊMaG7ν"K& A67qx]{}Bݤp U>gT YFWsP Ϗ_gac4>ܼLҔY d:>hƎJkмK'αےwaPOwiY'-Cӌ/aY0ip򪁈9P\V8v.i-[Úvel^`^??j:` 2x8p iԖv[܏PMES&ɭ59|,-x%"PK!:i poetry/core/masonry/metadata.pyVk0_뗎ΖAVeQd]Blӝh$uݛ4iӴXvs_'hU;I1FiԈp.4Lp$C _|Cjy !iiV|OJ$%eCq!\cH$a4.A3+}>ZȆ'-I)F|AK?pQII46}Yj¡.iY !55D][plQm,FuiMCX蠤վWzTkkׂtz/q/ ajVPVZ1~똤 Yy">+QS}vx_:¸?J̈8N`rbpɔJANxq/ $B msw5C@$,湽@ {Q"%+juZlɳH\u]X+;]K TAP᭹%p)`T7mhH;! L=@A̽W{5o@!?|;|.Z3 QBiy`INZ/6I?g'_Y&>&`~(XD6ج 9ibZȃْi^ENV 9ޚhKa Z;p ƀv|ĬvbI_P.;2"sj/ ʲav vgn,l[C. *F{XJ BhkosFk6Ұe[hĠ$'tz)(kx}uVZC6 _@[)ǚP':'~!^ЂPʧ :9a]Xs$+AjkX6h8y=x;=Y 94+*Tf,}4+V}BU .K ub3gVBfh^a5pQ&*k%3GXb&jDtʴ*JRgcW}~ֳU_.'i0wsbw'~[8?>m㶁A}M8 fd&_յphHco'lȟ9sVd69I KJf GKB!1(K vvD>HhF6Q/&ݘ]I2 J3߇% J6|ؠpbx=;Mxa;ljX.EVδN ڵ[.Љp`7 jt®!'@6irz8(LML Vo:@=UsoPK!.1L$poetry/core/masonry/utils/include.py}SMK1W {څZ<*ADHN`6Y)㝤audޛ7D:r9 3&#&ze-y<1ƔC+%b/Nzd>ђ4z]EwO6PGLq%KM6k[mk-=A*YGV9l/;Si aZ X)1% F E!:ude*8MZeT༜\@-<ҩ-&3H: B Ӑ*JUQq8&iê: 4 5vX{;m9{X1Sul.NE6t͏ωfrNr8}9`RcׇìT[52wVz CI/i7dwOW%PK! wq#poetry/core/masonry/utils/module.pyW[0~ű/˪.( d:n$q礗Ύ`mr2;eEmjŲ]%!TiR (SQPMf+._﮳wWon>^(̨}KŒRGmx.޲Zr[죺7zny#kвfJI7j*@O-+\peYY/AwhԖ+v\0 jlG_kܬQ='>TkoH yѓ?ϔn&,%kHXY1; F>FO;@DO`\dZ|CKͦ:kw$%L\(4)ȭYajhѿ6)ˠ~r!_n"TJjO ^2$.?WdSKPC}>*NIP >KY"(g1$>xl0EfUtTrh 2LԳҳAׇ QhK<+2##Aq|L=Vl #8,P(7TǫLx(;Zf9zW^:/:M+ZM.p᭴y!-tFxz;^A EXhIQ3y=3QLϛ@Jæ5P]ZIiݾbܪZUmHc[[}ƥ~\62Y]IC'e%Ck^!~iBԻŗp3ms^bV&bK8p/ H존@G9L 4?G vsBl6RK=|D\cPK!r[ ,poetry/core/masonry/utils/package_include.pyV]6}އˌ' mB؇P(Y2ɐJƞze{trdCL(kZ BHC BGQmm̥e8xû[d ka\gLxWQ:D#?F͉ry%:8O=N?S]A1LQ$if\TC0w=ɜhT 5:'i[//^ {,d-SV>xPPaiȪ='DSu0M0W=v ݵ4vA͠۾!  Ѐ0:I=VID51}urFKX"Y7L@J) <zPP+Ч=l|!VXOCOU,[lz,M>~P|EeNXO<=<~~M,b ̼5y-_1ws"긫V?WhA=謺lHJ9%B81Wy>Lwu;H_ ڀ{^2PŁc j@(oͰ&3Otȼ-}yۼKqEF#-/Rv Hi3âpڐ±e-=C'iwx !׺SqI4{uBYW5{fp ' gwTa]`vYH%OIPb K`hWĞ_̙sIh7uHdIK#erO^\ ,1 ed+:.Q` nU E`{ )g^d5)猄*λ#8v T 9p% [}ĸJF̪VNu˱xq׭2P%,wqF < ji3ow PK! poetry/core/packages/__init__.pyPK!ta,poetry/core/packages/constraints/__init__.py?k0w} ᩅ{;%C9QYsC}/U"g(#;߻⠍F nHXC] YY;z3"2Z 21 9[.2DWbC~Jvd+BQ%\_Y)U2oϗ4 7hCLeDBfsfȤgo6Q4ꯩ\v]SoTQoyqyL [PK!G"poetry/core/packages/dependency.pyks~ 03I2qMWt G5 8@"w/ŌM-8=sRmH4P U۰ye[Oշof @+$T@3Ս"Ʈ)cyW %Ȩou2xC b}qS(}GPq6UPY4mSE]NRҡʶ_Æekо*zWLޓֳ݄oi@ʊ;uѬHB&eKfI>_)?{*?^@f-pOuiLf68ۢ@'i1z)( MotGKYKb,&2 *E+j kR,_qk5O2,˦iT=-ύ(ZߩgG{F}ј FD7dV~I"1 ƈ v&Ա+US y;x1Zs"51n;ρ߶9|_Ԍ|s5|6uޱmOk bSdGšdY;%R5]pc_ٔфַmƓ3\j6 ǎ, FYܹcva}`kDgߘź|%`Kx"Cى^Cqe`܇> dEꅵ@ 9%¬5zzmi3\, W%ZI{uej@\NFTܚ9cT1CI}'ChmS p4с#\PDtmI>M0YT h34[?=`owJKxs+Lb&^ -##b.57&DݵE|5t]4ɤbEQݻo3'E= 1`ȑ qf >: K"®çx 4qg%f45 ?LBڛIqUy PCLgǭrW´@'T%mB$  ukzϥzbhΉlD<2)7ܛ$tŚ$TTf-tV" %݆GPsbb5@Vtf4S|ftJ^CQc ʀDxR &u+g6V<ÿD}}HmJkQ IX@kgoTqwW˲yMYSfF~ͽ l|VV}9VuU`b/Z4ⶭ sl - <.7PkZRėJn |a `ədHugn?%9Js,0a{k6}^vŖ}u,.iz|_,,d99;:E4®q_9e3݀>ۭt9и-MFm2c{23*v ׸7;oz(SQc5ݕ:6[vJuTmآd'^)BG^wdOZ~ R468 c%%YIm I墱q v IDX&T]D4XUTQ0pJ.E1W^} /HCcM ,OL)jPkn,IJLiW(($P|\j؜VR3,=s{ވz/!ɉ?HMՕoȗS֟cI(.VIp@|M CiCjLY[vE$_෽ #VE 71,'" ",1>/{o ?TET<@癅v0" NL Wudo% 9? 9gmŵZW 31zK<yoH'*ΰo ,~kxf/yޕ.[񍙑C`BBtRw+iXnBSoW:%|#',߾;` (ߏK0/ x47h2 p"'שQPy EL݋_11:4о m(~&al] 48]rSDzՃ^mylZ=Ũ:BL WNCH`!_CD+ i6l"I쁡,)Hʁ\.%ׯr'1pA3\$ؕe$&m 9 n2Mo^P;uV J|xM8O~~>r:0#Rb&Rʇz5UTӒG>'Nx٫0;V` NKm?xW%|.0{͉?ٶJ):,0oRWEs[RA ی[  hHx}n%Sgwm v\Dq@m|dPxCw kPYzz@lu`8}G&$e>5FySyvlq'|;%S;==ZZjۃy,_W/ /JÜGϒ5D Ow!)/9(>.b CM H\Nu1֑zWSf4l I Yqx XMh]EHoOa$Jt}%l}(+V#6|+a q"ql;$+E)N4rϬeҟ˅[xc̭{|Wpj^ HNIb&IBY]We5d p`yպz|8G 3 0=EA!i cl1,tDbw;EJ_Y{Jԋz4DRoɒ{Dۀ7,Fkz[+j&EDVGlPZnKkM֚—b(ެ7OӚ)F)gŁ@[TE(9F uڎ]O(t6#=]'ЬFlO_\](GvE*"yƚpnt(]uQĭC}hy9ױ~zsWag9yP&kǖ6KOih^*y;?G|poNv:~{/c\m}yS5DyO[QLU55>nIlU@V7GۇLezMG3T홐UcṽesϹ4ZhUR[LZ&A6SL?~=ܑw_B5ӣazG%ꮨBꍾ)jP jU7"~ 'oLliũ1Nˣ"8L0KHf7[,h %6VoT^ %~;{ {|gAx0 sfcPAi@.HQ{=G$R*%ki5:L}VPBb]*ʤITM!LC+2^Cmڽ8ZآRrw hr: Sqᤉ_,)qW ~UIa8&dVy=!7Wfq3ڊ c =ϧ.=vv*- 5ZxRU-$:;^p J_/Ƕ>gCH?d?O9fsr5+vsN9ߠPK!Wui ,poetry/core/packages/directory_dependency.pyVMo0 W2o; x+P`k{(v) CFbiG̀٤GQc %$MT ҔZPDVVTef()fٺ3FajʻN~n@5JQu z:ABC՝i/yƬ2өFT2oLiúFz}b8՚2"jпF'sJf4thŲ*VDE6ȕ˷)QI}7%-~f)_5'(jh<\93MD~vԞwe-B(1ɾ|׳`ubւWȇb0-Dt([rr$ ^cAIU$XM9+ 8`ZU<-*AI~prk2uB]-' b$idhb6XnDlz`9V0Ɂ_42)sL8Ɲ9n# O*u84ɇY ^{)q-__ pFsg(O9wd79ii3aψr/a;mXDO}G}lk4d6ogp[(M72>-c@Gy ܨ 5p` Vupsxvep?< 7yg#'ɱ 6PK!('poetry/core/packages/file_dependency.pyUo0~_a)!!T)h$4!x`,7C[$Nc<ɝᄏJjlRWҖ0)e+i-Y ~Y:ygw 4[X !Y݃JA mguK0k,sAQhEE`Ɛ9%3F*,Rx{ jI)1V^6>[#ȕ@rͪvfbJJ Tϙ0sR˨KweOQ}Y.to_1ryE@7-$ Ow,8b~(.4 iA:P[WU#˙J"j1oL4w$U|5V-yHT`|~鉼3}ЅX8Xg !]DJV45pf&e2/`7%nȻ ֆ4.0njtiT׎oe&I'{ --A Kb=}N8 ȹrɧ/'a=S;`;sGV2Ҟ=qcdo+7eIX'i5G?4X&^KfHUB* #U&%^$ V&\eO|>B^~$YS 9;l %e'Pӳ|ƣh`\-XF\ Gؕ{L ^P&tLCؗ9ۇݾǻ8<-xxo s8sY,>8/PK!yRJpoetry/core/packages/package.pyks~ ii4Юw8[qQ",`P6ӿѿ_{ Grvv\͊dboZe\'y]7}ޗM{f[zp@ySm_'dcwVy/iˡyiT4oZ/횾ʽy׋ڷt޴˺sv XmG3"K uA6[f-I|}ɳыo}>ܭ\sf$XlƦ/+0=jVy{6P Ɯ9v~0-:= "MϚvWi,_)_+=e5/$ÝQM+J.ަU9uG%S~G꫼_ 3!!YGф4EŸyxs.+Q|gye V}tP|(N=wKn$;A*:鼱ω;w_}WOdϿqI.DF"\q~f^yiJd^~a^޺i]ח\]@-ϲxhPh)_T%]X|Ҿfس#@fii}Z-^4[59D+aulah(gtX8^sZ5)}SӇyiy}F ЅIȍیT=XHHNj5Y @@zw>6kI:tqGs 0S_ Eg /)f{&'lLr>Ěg6NAy[ٚ<$MڴU'g؂Z=mVt N &`KMW>G3߬hS0M&\e΋N~Yㄤic[r9?2 IH64Kj٦pQbev-rw ҷLJ<>+X>MaY"Z =uR;[DffȰb@%KH99fEwfEz%j, .ޥjeٕCtVR/#{Tjx]s|Xe7@2q6ƕ؅AfA-݄q9.zCwRR+QJһP ؠt81œt9/vNmaPSDOje!)³ޔ|zrKXD.45y"m 8MxMf-*eS+Qt?Jp7(5̔RK˲*N>`eEҞblU͚(y7XvD} 0w0ю̀/MG)Ƒ)yd-4톅o;KbࣚL VZ%.% XH'6}SZ ;fe5$& K9>H4oF!9^1#@-+Ҙx#b[A-ΫSsN`]^Se5yi9#&#e'33EfxMd:%NYmkثA+'[v9`5QfAUU7i@7I _PrXVj(-by] 9YN' :I'-Lª Qhմ 8d p˄`9p鯓({~?p[LMRpM6m]Bv Hzu/WpiƂ0Vvk&y3f$xɎ ikmϴ,l)#)Ѩ83 bnk~5KTϟ8ߌvh)YR:rYw1s1 rfFzE$[m5 0s_Q0"?2'ArHm7mG(kWίa+Yt,6 ұv=^ Ru7=@MQWvWj=o5S S, CAajFG1%fCrX^2g+tSCd%hvVJW2w3GdQ6m^ҕnOid)/XrgJ,MqًAqH&<:w ^S䄝;՞|jSjZ$N uՃ=p`"ٕ!-Z*}~hendj}kvUs1@E,p3Ob%[bUta[,bmo09k6˄iMWNXb>ߎ!gM_#q@,iGfoQJ23ynX\XS8EAx@keöxO2t!t*䷅}|{Tm1l\U HC=kz%$eUCۦ}I+Ȋd βc@kUaX'u 0=|-fRJj%괄^oP>2V;[;CH>E,@=+сCOD|y~rmr:?jmù@jƼe$u4qh"-#739 j3#09.oG` YhV)x5B'M;P >zOFlzm>; 0A6nlv1ީK( faL- mUnieYf6Z8euTfНlxZ?ՃDuWu//YP]Uc ~[Ҫ؉G]ޗݢtN-ZC\֐beZ MfNC6dEA)?]RY< AeWQZs8FY5TU'[lӮcz!ʮ5YyYΨנTP ^D+Rpƒv8jjc7lɘwBF eha ̲^ C0c;SY޹ +,AUMIsfIܓ WP6?G c::$ayihqic7(3w+KhtcEK.HguG.dB;؈`e:H[/ ]FdFHԵN C T^^<+j{u~tbT!~4.49?lO4OeƏ"xҟCr8 ݄D2 le/ PK!і!"# 'poetry/core/packages/project_package.pyV[o0~ϯ)HH:c4MbBBIkڑ/cĎ4-Їv9ؑ<6]#&sf$7oחŧˋ/>N {lh !\iI*{*Aَ{$IXL~Z x {4ߺ># ѬvO*$P 9 5U\K S=PBf>G<(jdI23M)ISn~G-U[\ ao` x:Iܠ(E6dd 5 tԳ vL7JEK5fk! /_lW]H3M0Wee˞8X 0v͓?PK!*p,%poetry/core/packages/specification.pyXێ6}WC-آ@6AE5R@K#L:$;CH%EΜ*:4-*[iHS&G-R*˭PfZ٬ {: k7{z՟o(nP>8Aݵ*,mU%;GDf%7Ŷkr(ПB BXvod%*AwؿԎULCdv/em.4dVY8YwO1Ž{۲*=jP Ђ$b/T0T=ĩ:0\>p/W{WbaOmEǧM Wb,a- Nj_ZASWnD5yWy&@)8/[zTL (| 1Sc av=0бq̗G%`q@ْ$k2_tߟ泡l s+v\*]%bY>/ԯō;XP(=zW܏a >Mkf0i-7UZ([Rvc 7 Qp e[zuE|yc)y YvW9 <<^N&ۉ/>%_6ѻs [+XUa.j \Nx8Rew4|9۞Ǥv.f,CK [IEp "20vuu4jT:j$'97c?c`EL/PoW{ZTLtbEIǘZ̥@E@5Ǣ2tMy.3T*hg @j`5᪨ ]{ u mgT-,ǂlOȻ:/K9.4űQZCGvo6ugP|/ʆ,Ĥy8tLkb!8GB^ w®;j%`0E(V 1Cp}pJ 2rO7iH9z`y&"5~bq>󐞗7?'NlE*!t} =?<7g؜PK!&poetry/core/packages/utils/__init__.pyPK!("poetry/core/packages/utils/link.pyYmo6_@+wҸMq۰mWݾdB˔E&jﻣ^,ɒؐ%w=<J~L1'|JeBjz0(ReJM\(Ve*IKҌP~0?̨H@&8^fxR|V7up.`05yt@`!7h M6j=y~2W|e5Kɮ8cD] ȪL+q_O7& R L,m1{yTўdЈz( X2SY€#+@^B_) [8:$,Y`bY~@|#y#cɒȜ| иtkܚg!":r F gw[W%9=]B6'^Vy&(aۢo X&\1W,B@ 8xs!6y֍\nZ(6`k|ۄ;xQx@" م&r%x5zxhS@N%xK.X MtL|Vt(iCĮ'|ի AypkyH{h1=[uW\sL!-o,k 8b"+2 Z>x3n KSpTPi3ԝ8ǹ{|z|C}CﶱQ-r}dʔYUXb`S9$2-i~؎mc^T",ne›S۲O*/Ξ3 ;m!qI\nF Q1 ӓ>oGEF[dN 71ɆJc. ͷݮ,^epG}̌*ZSS)u\j{EQ4l޶U32*=:y2t4/Nk1ɛ}O3#ܢYR&X+EJf_H-$t"Fk-oa+|M&쥽L^`u87t>:n ?`״llbFNKMjNM!bAA}9wbA[i&zxe}cݿJ,j oKYh2y k$sP]OHvepON6jI7Z5;1q{uםo;NԿnX^lf_/bbw:b/^e,d}@Zǻg 1f'HvɶA\m\k3Ty GWKTҠXm0WQA& )!˼#;s$uCW HϘ}obT䏷z(^ee˛C:F*"nG=RxѡIpNlm[[&oZy`k{S @նZ .(PK!Ԑ+-3#poetry/core/packages/utils/utils.pyiw~{":8|ZǑڲ,iZK`,JrܝX`^d yUV}%yViV2RUYsg Q7}|'^%qm\xMI%j0o&mM{ˆч BoҖ&'W,%2Kr͞\1r90v.]5 3'f$~en8\%M/6p ,|8`G\?S?wӶr |XO?n$P(` .^P5M~qO3vHG9cDz∔PCnmHgV}7:" Gv*`yI^ Z'ExN97&xMYQ ^Cpdm9|s?[!DI#5yz?%eY*p)G1o{ D{" OZŌd NfdNr,˜vwQf]Htwgq6= ;<!(xv7{Ãg|f{Qˋ@`Xx*bap%dl[k`,G /I#+&Ş[t2!Jh6 qB9..pТbEym<&2հM^C,{A@6Œɕ%y&)r՘0QJ`1-(%'g cZB,&(=C;Yi>ڪMFJxce,AZִjb&f}8- 2GTE@&fFu-呯h?X20Xyk%doNWIM on.Z/͟-j hvI]ÔJ( ]ŲU!f)Jk )]Xe2//S篖 CG: (HA%i,!r5\-}8Nۣ$bmWm@i턮xtȸ.%W=Z]w.-PkHkhT[E-Ev6xt]XɁ1%1[db BZ4Մ=V˚X*|@lu>= ܭǶyslɬЇtYD) mCO()Wku" &dZSP .RG(b!jhiw'E`F(gߤ$2\Y껒L  $q]2JRRܚ P:3- WIU^Z(ՂzPT OP0`u=zz45eQf#chOF DYxU\Fjtмĭ/ % (x{'SX0y+]Hۀ>aIj`:ikM&rKZDdv?Ȫ6Q6ah84$ױjuaW![u1=**:?!CѕK o`J"ab$5Ć{ j4yIa7VPZd͝d2نٜփD&ڧͱk;)3HXZ(-n f8mi7ڍۀYN?oMVA\GdcEw6oe="ǝy =$yC +e/sr؂IЍYC"!N3X"i MǐtUG ?b"{- @_u7?^t/>싀,}c|zXg/&ma_AV>tEXWy:+un Ӛjdr:S >)JJ jˇ4^]_t=n]npƽ [7<}sy-#x9 Wۘ0PY<$3n \"d[W1|MNV9-)c㾪kfyWĶYԇ~nʪ_|9aP!.ߩ􆟿`jy@Ŏum8vSKqoET .^DYJ03H0=zwD+چqhᡶ3LyL 8"B9#=ͳ8ήQ-ʆڝN]ykR_ڂU<{xP. ;GQnb珠8p^RM8L-vʁL*\"!*~%R @"JhTۣxoj`Թ>a^ҫ( 1rPu=U1[EA(h V4?2u9,(ezum.j4-ؒjҏ *eLg8ͣ0ѩ }Ogf$Y]հ ~9?7lRyUj{YM|7 5V iA"N}w ;ӨUvoE!ED*KP3y:|/wcյxD 44gMT#Kwt`\O^:P1 :$D3e=x!(jĒ;Q aA4!fp@ҊwL:s f4@XMO'K)ΰPK!}L &poetry/core/packages/vcs_dependency.pyVK0+,-K*VVV!C`jl#1b3x^v*qZJ@lWr+ x!=/:PfŶ?)lC8 ,Ͷ PlH+Hbۓ\|&(.'W%--)RG+i+,AC.1C1q∧.0Oҵ@x."' ոDjqԍj-ܹv]BC߱} Щ1gfqW1fqVtdjTL`tAHl&s ]=nE4}B}"pmۀИm(pD) ;Ɔ. 6и2~u^@^u'un&ѽ5;PK!o<ypoetry/core/poetry.pySN0+)"Pr!dǎ!?pB#>ٙ Uѣ>*dU-XfR4PmDٿ-l7wSK"3q7^{|n xD6-"i\&|'F]`HG8.9ղCfO'pbcMs>24BNFMr欎1. Q!eeύVmˈ,g3ósdc4`:EC \OPֽ{Ӏ«/|aDvh DhWb{)ƖR7s t2%S aF,0l%,y(M`~ IrRS$ZU?b<PK!poetry/core/py.typedPK!!poetry/core/pyproject/__init__.pyPK!ܤc#poetry/core/pyproject/exceptions.pym1 ὧ p5܀RPRJMtpBb&(Y+@¤rB@ V(GO>N}?PK!S#Opoetry/core/pyproject/tables.pyUmk0_q$_Ȝne 62>'IZ~Nv۵)[myݝΩ0>h+=B+7'㣗b[U}ksCՆ/PzfοϿ2D]ZBmbdx|w U\Ԉε5$Xr@~NppXm &S.dr]8Y%&2$M˕`e:mpf[RG༅?pª|\P qu)gCD| huv;;n(*ֆ!x{b%cw<.bf]}Hᕸmq0~#],k<eGCE1s&$O-K Z$ u%H< MAIEI@ثBgZn6@ZWp{%Զ  -@UテU~X/QG vrt6m1F9voNޞ6# XNyᒔ>Ggߪ/+bQ )|j佧y7>-D.Fdnqn-O=LNh wL.Us""VP"lo7ƺy_>z׿stJނC./0o̿Bôtq.XI!$Bm.V1q"hY,td+$$GtX):{v/5DGD%D9+߫`r3hiא׆r+t:N㝍Sf0gH2{!9DALEi? )ԗ &'KKAv8%9Μ\1 ;hܦo_ QmubzI 9.nQ ss\9qĞdw ޥqt/b7uH⑦Muk[-_zx97t4ENv{0a~swCcI |vm4^lT١/ik=뫀KEfakuKhxgl: B蜱rژ#Q0A'|(CALQG`oM@sslƉ>UT=شG _?`Ž=#5p°yZHɇ;:ypK^#)֪ =q1y([ h4~5h63J_b/f?jPK!xk poetry/core/semver/exceptions.pymͱ 0 ]_aL{R 2ɜ}B:u]mCSۺ!RQ(L~L7 b4<,s-ˏ^1uOsO7PK!v ppoetry/core/semver/helpers.pyM 1 9EZzO"J$3n%2E<罘O,6/ο};ɭyN~$PK!d.npoetry/core/semver/patterns.py 09 PK!(h[ppoetry/core/semver/version.py5A 0 yEE$i&%MoE,2I:4lhDt6Qs,F;@z& hݔ2[g>T|\pPK!7`(poetry/core/semver/version_constraint.pyK+UO+-)-JW-/*QH/I,+J)O-)K/Jy%Ey%zeE@e0a3\W||bNl[h% yX.PK!OHP`z#poetry/core/semver/version_range.pyMA 0 yEE$IרּeaM{@Lի1bDɳJHs(]ݢ-Q/V a*pPK!Zd.poetry/core/semver/version_range_constraint.pym1 @ >8|?6"!HN4\',M׀wߍCZ75$NT2@ͦv֓FI<[.OHfn?, C.PK!y!Ҳ^z#poetry/core/semver/version_union.pyMA 0 yEЋH(BMJ ވ^$\b:$"\@43u8h2ign7\߸C)v;mW7PK!poetry/core/spdx/__init__.pyPK!nu#poetry/core/spdx/data/licenses.json]۸rOlj7{h(kFBT d(e.N*79Or$%9hW Z׍Fэ'B^L72 i22R[L7Y|1L,K:ϸ MF%y3Wv; hH|Jjŷ:i|i)BH( Pe#VL[5X fKQdWvؙ{xa m?jy@byj=d u^<%T0d4iF\! 9g S)(sS.rONAans@ mn\N:3` )&fOfn/4Z.)&O݁˫$Y)$D:~OF^"2#F2bqdRSC͠"sQ1,O et8Ժ ~(0HQHd*tYdzܷݳ՞+XW5zz18U.ÜU2Vn ׺Z E~v"JUsń%toѐoq}}oАoE៤ $w0݅l/0Av*0=L#Bje 5hlm`>Nv61)ݤ[9qB~ZbPyv'1<;S'ޒggKRyژlF*O Nut9v.݉/<wHq{қT:snBG==e:$])p& UGG+#!kFUWG{r*A36Tk}: Т@rU jAKs8d3gNZE%TA+ h# d DOnJ}͈&HCfLHl"%JҌ~F;`KFꨀ#gk;kZ))k*PS&P%kh2햅d,|g%~x!..i3;E !dAM•yqUDR@<p'm3ɰ9nk%di4*Dh:Ste,M]:Ix$vp]ӮP `EHB5r]M@WQw\n`LߗLAN5n{WdO1>H23rc0>Gρ| u9{9sIyG"eHk]yN1NգF[Yšظ6 ef\k͸6q;iƵLLZ3};icEbNK9VK_,@[[[7~*m;gMjnQZ~Vfrv.C=,<2zx 2y~'#uB'r2Ag"Ѷv7ksY iSz7[eϓz(TC#q$2OV̹} z<>GUћ+g?4 1+vH=x$U| %Ed m.sr(,dӐ/C4)F8 y=cEwY7:"5B[ʭ%K*5;=bAqDXT*7#+[X'rX0U=;zyꠀJY֐ܝWjeKcdg^M-<ˆq>s=!~IAw.\< EȦRWpͳÁ?.`z n#'!Nܒ"dF> YX^_*amFB?O%95_lC2l˕wDb͍gXcZLG-A+O8 Y 5[_Dg}_|ɇ# mz6#͈Ks\ez {{kْr=0C[۟`kL9e2n37D/1*ԒUbT9*h^,ak/W}y! BY  9qεDrr5!{hH)!]Im:LMf3H.hN]6*p/\aT. [SG|9<3LvD5N(;vK5l5ܰ찥k8GU60C#J h jOBzV?K܉^Is*AfM;cw@ #J5fS_mmoh2\ΤTs=)0'bPx8Vh͔SEj|uQMJZpr:l1ɪ"_XM嫫;$~x2j1_Abg4+okA@1SC?RBW4~hžjqunK吺B@߉8v-#Go \̎+>;vap7q7^M6WCk};̕{9\:@9ޟY0U_Č!9H={9쯱mu:j@#Aoa64|9OЯetM@ I) >Ns+;be+՗J'ꮸ"u_;5(hX"E]7b*We6}}Wz+?tYPF=~ӡӭr$N-rl1Ǎf8LyIo2k`YIev@{@3ƱBl^xyJU b:,Vl8ãC~>$H&5={NasVg+Vbfax`]+N,j)_;&^Ѣ<G+VKqe(By/9eG芀lVY)qMrf,kSzAxF<(1Y#iRe+c;⡏I.!ܔ^vhhwPK! S(Hpoetry/core/spdx/helpers.pymT= +FNcK(IQHife!b9*x yy ʚ8W-rzZ/<bԨ[oLN&T z{e _YG-jIm[D!}$+=)BHA V N~nį[]g>F(˺gPӹPi }.Ҭ3/hB^s79?7!K/[^0D+ERϡH] u` =UHZXyH‹2|sj//`F[#I?ѫ㧲@-!b^R̢U=++c!rAZ =2؇,˧\>6^ҝRjlԏ/ypy1ޒ(^jMI=h;xf^k9 -Yo3rXlfշr%o^{W3ܢP >޲ J{A'jdDmo.)sCgɱPK!Gvpoetry/core/spdx/license.pyX[o8~WX ;M"hRRG#&fƻ!Uva9Ǘ%GrS|L e& aadA$Q(%R(byh0ˀCdhtMƥl| 8 =)%'vRBP4N?9 cT~ XRx%$zR.;< rxb>d}SnI[wFNƷ>>vD ok6[_C)˭FKY,Udu,DVRDTe-d*21媾V7H;,yB۝`>f^ s]m{QpOτ(R0NKJ ȣ-E7Znp!.H6\|پnptdgktonC~ǰ-E[!G+wKQl%El&Bij|B41i3'Gȩ(¡ܨXp⻰" G0>D7PUu3mB^(N6+0;GM0BG!' Iwj#aS qUuc4So' R@rXBjAӴZtb3Fk.p,0鱣p@G ʗ'#AUB'WY=1$"6)hLȶm#FZ}EDI*L"0硬C1^2 sV 19k3[ j(N*.Vu).VJ01J@1r G(Mͺk"$E02P] a jQ|4_Ykv! nVd1ۡwٗ]b`ۄs]b+ss t&>2z&ؾmߔ/b7D\?`%m$m癆Vg Sw514r]7v/aZ%]7G*.I3ƦMW8ˋחTl V@Ft01̩SHY/h@ݩfBpPM+xPO]c^Õ| w IЙjkXN0o'>iE?oe4_ e85y [aG#CD-ňAV!`DywLFo%wɲC_PK!򔲳Spoetry/core/spdx/updater.py}TM0WX9%u$NR SYnc>{3n`B1`f.D&uQF,gGfR7v6dCߛmMVE{%#U|~'?ݱ5+1z\5MG70F9F޹A~4",{qIP$`ZmbU& Q!zvAP3+1P\f샳p(ENxWp5P|)q3y$223/+hf%D`e^Ŀ hv9ܐzqyM<,ͬJըđ휢1嫲fDftR$I3(riDXE^\0__ bvI4eEvL9V3y&)kb; RU5W@2AU?vHiG)s{lپMᗰl[*}&),mF.5ྃ_[:4ݵ< @{PK!p*ppoetry/core/toml/__init__.pyK+UO+-)-JW-/*QH/I,+J)O-)K/J+KHN-i q-*/¡#-3'YV!Z nLR,PK!spoetry/core/toml/exceptions.pym1 1E9ŀT !L$Ʉ,7,#'3MjTQ+hGoGŽ]ΧcA'g&s,r6zpXіb>q:g1poetry/core/toml/file.py}Sj0+Dza0MMHP %x!dq%ͿWZD!ͼ7OFTit~6ƒ& ƜA x~@=uV뫛/Kh#پ{DZ`x^~g! afPNy1uHYK&jZXw= m BTkWܗZzr/7ok - =O4PD*ʔWs9\QVsW+,3H iU> ϯ1~EaLN&fГOkא(I3h(xs;Y'}xrI~[Zp?dF`BHN5zHnN?qN<};VvG%k@#ȢGaTصV\'I %DAG| w,UsF8m V=ONqHWt?J58s`6X6͏wSE@Y_*PZg0e*rn XzSFI?nHttKu$`_1C'jFpiFh~τ}dPSHK Aȧ\Ia,s,Df=xB)JeVIUX/GkGZaHP^ so~6j8=qEX,_vփƟ#Ќy$tMp VM fLGe|[I:G$!C']IQ`QZwA,8 eEg#z40{0UԴڄhn^.0_&nvHZu7Uq!E@g{(-ЯN`6HAt~A5j v0"`w1=L:81!k,U?/KWs6~(xr{*s<-D4/)dz̏PSDdT;dFNgz!? Vg~,-: 1ڔbv-܈,vͥj.;YҖy?O"2B` <|wq=._UNxitgr{'2C萊E Pe2?J֟=7LR6CHI|T9磊ڻ2F6ڀ @w(v3B IX~:eabs!\k1ε$qx0a1&8LR tsse|@Qk ~_- CجoL>e].lO9eU+P5v)LJ |KPK!!? [poetry/core/vcs/__init__.pyRn0 QOTMڝ `ڨ#VP|g$y5A(wXeA_C?|-xJXp{# 0"Qc}tp:Ͷ FxH' XzP dvchwh[@cc9`nl;#?"iWTڔ!ExyWG󦩌E/G3O` Ηgq5drw|_o/qHm϶ۿ#S(z4sr^HTxC) @ӂoCi/3(,APt֯aH`䁼3Z -9݋12Fwn40gwicS*Z#:( ~l h ~ Љ#Ic 6zͦ|IK*y\c\qI,g8AYFm- PD `=K$=?s+|H^KQ8dznKTNQVt>E ,Gny${YIڔ{ۈ^0ᔼBe(w.L@Gg?wzspъ2X .ZR(YՙwG!d4u:H,Gy=Nv7 9M[c.n~t:$%),-YȩU H%7V*nĄ}2D)ilP֣`{0sp)odW“Vt _4~$< v{]g!ŌlH|v z 3#L uV9^cNN;~IC:IWvU&h1(3ow`Q.~kH2XȾennK,cEy`˜y{Yj}Ra ϧz=OOPd9fT֙ssJ5İa^,iց Tu@u{^YwE)dZ<.pBmaV;ZׂǑ: ft 0@iEJy[R+ecK4uq@~%+q$#Dy`TڤO}'?4Xbc-oW |#MǡVfyݻĐ`6Pe/S7()>jڐ89aڃ/`\=qߏnu @BhK;69sdIYtشmkg#mR_XFIF!qL<IҹDr;xg A $%F^|}e >pNV9D.GX24Q̥pZ^C U-e IxñQ _g"u=@n\* z~dGKRW4JfIoYp%ńDTf%w3!pT`t}^qy."G! PĠZ9Lm5 9[= vփPzs,/)<WmuHVR*_jy i21ZnewSaVZ%[A^R261.J^ TՆ*`>̰A >;%sXiS~iΐlg({PK!poetry/core/version/__init__.pyPK!eLP!poetry/core/version/exceptions.py 1 @ѝS0+t jb`{Ǘ_M 1YD8<{{s1~d\fj=PK!W(poetry/core/version/grammars/__init__.pym? 0G|I"8-R5ݏ =EQ~{ϖ8q.SAcLѥ81fLx$MQ sA1).&z s?J8X*LLuU*ݧFL/PK!wKL)poetry/core/version/grammars/markers.lark}S]O0}ﯸ6Y3w4,26FfvHۥtFo &{=r[htZrp.'L6t28iFIԆ.80RVOid*.j0%7qD&5ݧ&6^* ,_3Τ L1^T/doe-A9YXbTp:_fE]no1Ud+h. w &VVze_ZQ0r8 U*A>_ BjD4q})l(l(oy 3]^>ir#0:ubz棥0K=(GI&ZޅT vPK!)- v(poetry/core/version/grammars/pep508.larkT[o0~p2M¤I:ZʼMQ H[cn%3$['}4ԧ> Ei`&M,xʳt7$aYr.l-Ki(~q5vvƌ-EXXXrQyVY7ql#YB:^6f^K5f,= Lѓ9 0kiݩm+ OECME&exc`H'_'[L. uB+8K|Mу9PR#4xᷠQч+azj|"i4M3)-D,CL%@'cC[ =b0Ԫ6K wI0YPW6HmVĪ&܅YE)E}֥&A<ՖX; ~< |]ti-W?XdQɩ_81Qq\MQ4@^HkJjA>*]qEGͷƧ^~ U +e3Ȕ1(vq+! !b#,lx[ ȄשDe .BmTAйi/ RXLW堩26<P1;T !#q|x7'A =E 0@?-avR< 3Σޱ2]$jIynI)~#zע>г"bqV)׭AA+%![QFX@^,խM<Ǵ?BteZsɋ+8*xHMdTf⣌ET{._}4Ǎ  ӲB *!C V…芖*$SA[( PK!< tpoetry/core/version/markers.py=ksF+&Ui=J%*ďr\m,J`вNV~u =$'ٽʲLtcfʜful|UV Kl&-zgG=K^5eAwv\Bþًۛ_B ס/,K3zw}wMuˊÏn$-:ȫhP?ɯ/L ZAUIUkп}7of?fwoF`^i7ni`G`ɒYalU.xtMjN3ϒf'$K/?F?%ٚWUYeQdK*ٲ\ [5/u^ۦd ܈ ł/ӂ/^9p"bP L4 W KGDˤa^Jb Ǵ*ͫ$=;]09v@sQ % oD=?)O:u&~7uYU|Wmw-pN?gn<_Gvu\ D+81-dsg۝7{ׯf?Jِui2p*;Rʈ0 LTu(:%YxI,R@@.|ތj-'ڦcȃhl٫9Ew uc2z@tIVs udK(I8E:oN|MpM8cꂇ0U rnfú%&DVZb]"ZGꏑ-&qǵ};YI}IA=`l3E uG_gňwy' +4I1#1a":tئb%Ghܮ_Fm;6 ~>;ߧu۪p\ڕgk?syt'POZByk~>]v`޽}~1"WiGU4&}WGo>¿Š7S?ŏλÊO;V&4ÏhxoN3QnE 9QFw&m;~61E;6)5"8D#Z$g:z8(2^Q'N֞M :s0”#9-Zjak?jc4F%Mʊ%B&IL1,5_&$G meNލv/Ɛ=ey|Qh"iCMۓL<gZt e,q6BvYN?(X@&JECZ? m yo4Ա^ei3N1$h0~Ԡq463co,`ՊQ-~NsK?E_M |MB4 |̀S2,RHqnwQϟYH6#!sX==anBcn|8+G?Io1.)^Y>!W~t!  DxTP&B| YKGu=к2)WFF@ C]:zp͵Дڅ͎a莎\Swmٺm.!q {/Qqd\^Ǟ/6NboKR3T.ى^p2$vE.q2i<e-$lFށnW\ḞlZs& ']x"Ӣ=ڃ!3<3+{5,2BؽzuM(D/#8mu6dkb0ʫ:{`K/ N >8w'։HF/qhq EPaF>뱨SkC+A7=OǸЀ< Цt!}n??a_G=W+?o#ծ$#A v4lj%wB9Ҳ5|~Ys/ Wɾ S +Ы4p߃`Wv߼M k "]S9XzCu9(MZHZ|:8B\E1YiIs fҴt M 2qiHq5򴘙刬,k(Wky4ɧp'cM;L>7ec|r O:th< q{Cz مPÀdy)7ۤ؉7A}CӸd`=x}asAJV)Mj']|&nlmхʁxrf3m, jj)ˁy:!gR25%9Ax Eu@Q.:RwUwoĦӎQӏ|{nȉRBMQ`C庴E'Z72/pԇ,Ƿe;tڒ(%=S! +,9._Y3АK]ˬ%ޓrwWi)jJ{wܽ4Z;Yz[(bƴI\5iACrO66t{14@(DP;J*cRzXh+ 0}ublPN+7pBnMxk'ƞ0dR MƄuWZTn}sFԧ陥 amHGDRmL_ʉ%Km{1XS|T~$`yœxPd~ \yr}sP#&PӤJF0ATyuQ]mMlvI;@% U. KRQ_o. 9dcv*{g zԈvJ$¢@EZ(˜WI`ӄ8MdvPtl%·Vk0%tS$\W<{biiLE!|-JaH(e͒šJչgm[DV_NY)t۪th!, leXlK,*Odkt؄z) \"\C`I3r+a6򸇴2[ﰑq7m։Q63R?OxOv?*3]fc/y,U#4ΦMVGp#ZOd*m,nyXF7dT߾~YX-%l0Բ \[%GMEcP]"w]>Gm:E6fUš)vUʦ_ܡ^&{b)}M;?U{s!&xU0摯ñ${oiԣf`(n[}& zN%|thhg/+.]X$QRNg3L9) ۇf"@;{qL(Is)ܰuv`S!pTPV}Q,!]5^+w)3-ZXnv'N~>Vm@dVN+5X>W`3\>mj97ڳKp] 3w~[]?:uݹћ:r7G`mw>}ٱh X3#>e&ճ+:OC|s=/7lS1c92T'L/:VC U:csuʽ ~-i?#@r+񇸜<"wNL<{"}ba|]-S8}&0Q B b "s2.*w#jsRt0>ckywϵ.ARWaᵌȶUj0]SH.x4S?ut=&r5)0׶0Pxr-}5=N*FxO %Ձe:K2PԵ"G> 0"#ӥ*䜡ӝ0^&<qgo+ʍuq@;f>1;m֤m'PK!#=  $poetry/core/version/pep440/parser.pyV]o0}ϯ"%DRcFtUMBcD&i#زݱ \ MҔ)=ަmH[$p&EU+g'pbUAO0x^L/J֝(r|+4w $f`|1Ƴ( @ a*wR<[^[aOlXy!*HxDW69> XmP>mf O+=7r+˱ۀ>VT}Fak`mx'/KoOg h}{Ocr9зsn\KTPńl FԹfKs+PX/lFV|7R[a ɋՍ􃷆N r->ɤӏv*7nK/2NP^e$+d $3幁MDODqr(7UԖIt~)LZgZ,1m}^Eх\1h>)RNs ):k;vĻٶ%"َ m 4v 潪vɍxGI֑!5v2JtPڥ3?GU؆b1"l-9[m` T*VܚqM.vguƂ֛ђ% C1iStVȽP:Щ1)vw1Rk!^M7@~"~=fZY~<ͼqH0ia;Khw R&*! }x"^ꚰT{poB?*'..gRE2 ѩ lU䴷fz.nmh(!?I^B[YBmٴvё4U|5~n3`Yř ;6麻Z&67Vk7G*_TT+8ºjMy!$b;k}o 'J$I>(^08t:\*nLq)V:9$ʰ 6nZ-EU#ipi 50Iӆ/E_#5 iCDFV#xљq+iAoԆUhu°AAoM!3H,niB: # RHj0B>! Kdzo;6-0Aٱ2yzHTx""~|#B}^]N'z!0 /Ч_2^^mGɰp(~Jxf+pFYl[$@~cb #+l(pܶ0Bh⢐-[I%PϺ Y>+>ov޴ Bf{9A়*w)Pf0 ,bfZWREj`P:oMo)Ч5xa  }j߲40\jc ⡥XQ/XNLvJ+MYm# }Qw8/VZKJsǽB#(wiRC [i^'|t )MDqt 6FM5iMCAot Ѯ[:d41)#VQ%/2^X̾3hkQwۙΖ~vEyq2[vO9O# rH PQ;hR,4d <*o;(ױ l* iQR4Ɔ@ITy&x`FR` :c(ƩÓ&=b<Y ^w=oҏ\ 45~/XQY&NۇYc<SMۇY 8B6fa=-SIS#8XM=L@T 3ɤh?&c't0ɟ)gGp_',ρ(>h $`[ 8a_^1L/Br hR25@wI 9S47-t x#۔iҦ4ɘKslCu_3'&8R@UVȜݝb"E|/TQ|>ОjQF%6OPY<CT붎yN$H> U'a&]vDgiK*uVAc]a&WW~4~cbXآäPXw  1P1{ $5:8Hʍs ́ޤ Ad:w |ʮԵ b d=>;x1㏶@;87Wrt鏲/lefd1NIe٣9\(&g_D~NfYB63 7@Odim=f^8ϵБ{Ŕjb>-٧[O`*¦%%7T@IK62KXprKȯ LWLf pSٰ ѸcU{E/on8>U)гng.bŴkifRhۙ.pZ/>+<=e܃9,7' 4"I"d{qq7ZD^HP5.S&~nq̘ǖ9cϻ8'Ud#aع+cVj]3UTrGVRm z8XPx=i~X9|.1oqV|W+s 8,KCX-J޹/f- jƃjT!PK!% #poetry/core/version/requirements.pyVmk6_S?]})h4% byeWӄe{Gd;!)]ٚy4/όPՎ0V4QRp)+Í[Rܥ5Wݭ uF=Y6 itJ#NT *39Υ9EOӿW|CN]^>Nr9><|<3Y(kM/E`Q>RaD FQ52_F[ jJ+S౦"&A7,~hXwdXkQ+%/MzF_zO`^< CGa%+lkY(Lʡ]o~_!sNɆ K&LNx^>}(* 0]P`) cXY[݄|L.* >qD)5%%<-W)KWH_UAF$o8>.9fͧgb<4|xɤf3 )̫-zt:7$f' do{=|$݃ |>(H2e'-f3.ك-Q|wsbĤrtD)M. t9ʑgd(,VtC^Ľ"E#Fn1hΜB@׳{{zFz#!&/}߿DAb tEk(яx x[F_%ʒvHl&;K ɒ`v˷ah+cC;)?-'E6݀2^lZD^[5_bm< ,Y i{U"3_C߂0+U _l+\a}Hj8K#S7 LeʲiGGd/]SPӱo 'm^a:Rǣ7Q_'@ܜnR_g-2*׾{^j; GIX5<.Kni2?s2`-+,.I}'wX2j)_Z/8x9d(j i\d{ !LPK!WJs&#poetry_core-1.3.2.dist-info/LICENSE]Rn )rJVWrw& c,L$frLd>Ҟc/IvR$~Iί={xz|zn !N{Fz3+'=epwdfph#Т\ =xw=\@{Z:'3] Y47>tF`Gd3vz7m#GK0w`OIg N5)yl}'4R\%ş %0 FCɱ1J+wLr}z+k7ZbC)b ?) R;*j) ΋ v\VnHZ+ |Ui@HȀWy-x KUBA7\!oT!nE6Lk钗\d⪊+$PSx-z+k0/H%*^$ 8@e/kkQ .KvPyI&n3K(,nQ劋`'1ÔRx27HDH$ؕ%V ^Wmػ@PK!HpTX!poetry_core-1.3.2.dist-info/WHEEL M н@Z[| 31"`Ϣ~nQ4n"DkQp9iN PK!He0 $poetry_core-1.3.2.dist-info/METADATAWRF}WtpV$0Il )ʕI yfĢOw=#-oXt3h4zO7 Zii,& {\vM#l?`//CDyJ~1 XPBeWdiKӘ%Lv&KԎO__[9CaBŴ–.iK; fgp߅p^(FH53i]z0Av8}M(ᜬ%Rf֚[[.,h gB/:jL={Օie'+a^-*6=rers<V@pnN d,f :>;m6<NBO߼>c Lh;Rҹlzqq /ЕV 'F{Bzշ|N{xSNG6W) սfDU9>%ne~qroˇl哘#4m?q;ߦ6|4Y!iJiJUn 3%*ڻBS!8+JW(gd^HC~˻9vW2$ǘ.T˯yQ|"G垿Gc!$Izȫ*!C*$aWGEx Uj:rO2ZJQ>Jލy%K+?S;zxBiPT?ᑚB~Rc\( mXʅLa)D5rm#xTCcRǦKGov-i)`[ʗv-4ԋ!znDő\?kK A..f0oe;srwtzg$$;8hzIJM?'%o0FXӋR#ȭ(&·h*!gNC fx$")X"7(!Xkع̽o0QD A7 :GLczrT鞓Ǝ<|?ӱ+Xw.j" ULRu$[#ֵR jׅ*Po7Fa?z,OӤ0~9#ʍ@ n+ө{> ձ?nΖ7>9@zb_eǯ;GWSpm@ *zR5|o) ΆlO'ܿ"7#˥`u5"=hYWyz0C8f6`2MЂ%;" sE;oJO}[/3H7-0$wޒ `cWj PK!Hs)\"poetry_core-1.3.2.dist-info/RECORDG-ڿE:AFA yo۪*{.֚k1â¾]Wm_I˴w˦K\//> J.$VF|%"1cSB$g1,S/mv}X?VT5j%wyPj;,M@KWn13Ӌ[ %E F1)3Ey& 7~Q?&q xL"/(^+Av'l%<JqR)%&/0 Y6(bQ?aZ ; A!'mZOF̓>g"¾/z;s*3M,nK^^fـ`n>A% n`BNKŎloHop8|? < h1#5Ht9 (c%o^ 1YoiD< G m63C`{cd5&r0D7U^yQ6GXlEZHB[N[醚vw3{6N Df"v8`a@5x3:st >YZzCŕpUal[u˽󘩬(ڝ(ܴ@Gw*U7plWHWwi ]hADNT1'Y\w#i1#0ܓ]4T'ztaK{&{gGb XEh53G9UNjg@'`kL0iL{F麓@9uJ &zHYN}u /)-r_ [GvUr MqN],`Vj?~RmeS@3KE}QD;GLNn<ر!y+!`K8TpmkEGlvdgaꍲ󪲀5_vWgQ#׫M;5|8hKl/Q1.,@t{yEh9v][t~.VU%]SUQïtq+P VpiFݙ/`[7.n6n<`u%\7[SI xk-׏AF= _=%EL$XZo}wneǸ]livHMFq⵰|O[@5D[~GB{AEd|;c*! :%tkd0~wσ<&ˡuexa*؞{QŗRw PC[maA`2b&輓|9(>k7HsQkl~?P)gYgvsQ̟%ZͻXps[?^]o6]#3nܻd\pfmˡvRu|iVk7 Kr'kj!L'q0f5 eȪ5W{H>{o}[ |J31'\=mU)s Q"Rl91&e_Q.A^ʘjE+6ԡ& 60څ]/in/K6E(I x<΢ QJNF>m@.yTU}bz΢A4d&G@ iܠo*w :DtHFI  C<)uOA7it*3^pQRr`O>?,0_5g\H5.@Z~lԑox;[u|ph1W ov;nb ONV٪A[)V ,iυ5ϙ? yWYa2kHc bl+rb!Kpf~0gUOȵscArwVĔ.~%lnD3^"'^ 9e&Q?4 .$ir} Hͪw닀hF @L^jI&@P2ɘs:۳\9~a޳ek2r},d$ň%uv(uTEQsrvN2 ojAWn|=쒃|ʄP--=k1^sD8 &`] C<&AmbtܘQڰjbwD:Q[;\.#nڔ%9Q\%nr o|QY3G{2ݯ)-&߅OLJB'ޱ7̢0xFଲfWB߅џT-|gCsL1]ˎun(sI>^n!o*r74EgOE )cr,|ɶ!I uu؛W]Od-u" /`K#~*7pk.M|`B?X_xN[߀%Z,?T=m6o \PrZW-/꺘 ~N>'/&զXj:q ՛I ֪K{9drU7$.ょn0Ǹ:)%c|F53מv̍2/8ZDwD1 ĺMNǭZ|_C)zՁ6%cӺܥ\/qiVkGp_nBCt uZڐN !lv횜7^5|CCL;\DaF.NlrYi@{xf<'?Ir}HMkȤ-~Vi[$m?nz%Oa~v^mF$0N .kMGeyʞI.LvPWMAh %鑑Z?ɂiu<Κ 0G~8 ³.SѨAד~} ѥ}d?mǣ6/q ;ᵼ[OJ%;t+@0!ltZf )&o6l 5|~ s }Q5ڨc4TkDlM7KW~W騩@wbMowNT&]fF6k7Iz7~ϋtO0(x'ȒYAy}Ušc?zooϞ;Bi}k%&<ߏU#^8) N uZˉl9Z*ba!9sgŴWģF[C8ޜdLRe8PW7fw"_TF:ZTeh _8%RE8ox.&3~V ~Tu*+J2W{9\MdM c:/ٕ8 [i9ƫY~SgGOw#${O(e G`x͌^3SXT&ye),`[w&#WPY5f$ g*'༦%`x_kNU:_@U*bPgP9qv #o/G v\jdS̤@*0^= 2o%3`>4$ *̈zض,xZA™D.ςڏFjfXsqHFm=zlO0Y':r铸+h"gEOx%jΥI} z] xv`N $ʴm/ eȰwoaaFp6qTRjʑcUwVbx, LcxmDŞw%Ն#O AcA0n3A:Vb~U XfYHw|W}(V$!7ULRQ}ӥ@JAއi$#3/VрMwJK[_< i} (qʄTGEi"Je4]0Q[ƄZ3GC؏HV< PVx^gS(+'+Nr syp3`\O\aA:^c9٬}\9#L$OdFm>oM`_v| ZӂQ(7w9B!e$Yv+R{Bzю,1aJŮx4!ߩ'->'h.W( &Q酭{$e( g;= ~#pP~')6)[0=t@;irUgiYcz.k@a!CVM{ǾuwFSﵥ~p $S=jpN9gqpK-I/Q-9K9U &1$!8 1}s~g*)d_ɱ;/SAZkyNE(Ozػ+28U+گMOҎHR2ÚJN* pdP^zX7)/DwsX}@Vī81CA]- +TlnWHoN7yI@;-}bdXo5iȬ*P8Yӂ:;MA'yyWަuS8toˉ5 t#4Q &dk*yW;zɹ1To;sR9)/C4OJ&tQ`:ɩje6S\tz7Ů:A_y΁8Wwq_B$vs9O Ο.("L־pG^wolSeGlډ93e!SWAL5Ovk2 6'Шr:JG^l^b% Kދ̝ӣz^HP hb %,lxjOCTPjU:nB!T$ےz竇I];,YZهVY*  yQݸPj4$o5j9535&=Cm6Lot nѾ(~a>t-nkƯNжއKT=FM2xV( s y;VnY0,'jC'7vݓpL"UB.(pvI;:sU}~J{laNl o}K+x?6&k]Pzs"'0!\ kN:@{fttI T<[dK *d9÷%6hSR*'*Ux>Q^ @$(Gi5)emw}J]od` *=UUx{mM"g-q;y\S2kw+uW.٫ Пo'Hźq/HK=1]μ-&v9?\󌁹AҢ3}ɘIuiAYçE=cm|zߧa[ 6NUHzڃz\3gRyB~'Tt P}rjCj])Dŝ-o=Z$ 6$]؏ԁ|οc pѤ'- ݔbϠKo/+#`dvͻNI~UG/BTrL%+xVb꣕{7p{92LTh[@P:$m3՚b7h:-ض>zSq7; &,/.0r*[(\q^n;S4<-|V;⽐X}CcLxfhw5fM{ hUQP'˽JtlCOhŧ᠏+PoPnK>Љ׻1uoU_F}1 lH^Wc\rr91A UiUeo872ȓ Aݹ i~ISi,ByIўd/FU['ڑ~=deiDhu=GZIi GNoXQ@m?nG,dVY6ꄒ)RH%ܺ/G5x4rk^L A@i 3u_Bz|o7`Y~4pr-PWكznۺtL1[33c8_~vw6ުѡ͑`{\5B+&!ä;h|]/ ~,`ᒸAd(ޭa>^MؾoL|g{H{9i;nEm6懺؝K5e>íg-Lj Aj)(OF*lkz n\1Ⅺnx?_b|}w98ӲHrn^dRșO5"<>>EB!QE.LSܬQdۆAɢsT=߈vr  Y kK؅L 咣Y>ʻq黁u\x9F4c"2yx}> 6{ڧ DMlZ~x(j*lOqoY>V,U2l#ߑk&t3DȠjX8OmY;7@ȷ֨e wc "z7>;Î!ʄl3;HЯ/"ZFzo?mEfTQe4bSm}q F_,CF~0s\Pkܱ4nbse|wqQyA^2U1sR"yKk8W[u}xtI3p oW̗.+_dRm(2^$dʰ0 &X1S<=>HhXU&QyOP;0Oˤ+iI>S #bqdu>-b.koErBG5@&4qj!!Gô.(_ɢquHrgsǙqR؉ UG[>&f{"=fܸjB9ΐPܤi#*?UWj T %6pU j*w˸{z@Ni =`?hXGGdzKIgDbcӭnJEfWZpiMALJd h^h8`59xF|M2GkS𤘥|w$V|"sTwUq (wC8zGruXK%1V .=30NP}J"{\'^|=smC"Jq;&EI*Nj;w? @*tᚳbaEU9^qKtNc vx$/?FdS\]$( a]2r3&}їa68z׵E$NѸCӶz$`oXK^pNLu{ G}ikH. PT2R~n4klK4Q(foݡe=$&]έ6D^8cZP;YdzA, cq2!T^ZH>}51n6PK!poetry/core/__init__.pyPK!6RoV*'poetry/core/_vendor/_pyrsistent_version.pyPK!a KB$poetry/core/_vendor/attr/__init__.pyPK!10U |poetry/core/_vendor/attr/_cmp.pyPK!׉# poetry/core/_vendor/attr/_compat.pyPK!KXI:#poetry/core/_vendor/attr/_config.pyPK!0 59"poetry/core/_vendor/attr/_funcs.pyPK!<`!}! poetry/core/_vendor/attr/_make.pyPK!$q%poetry/core/_vendor/attr/_next_gen.pyPK! I)"poetry/core/_vendor/attr/_version_info.pyPK!T &xpoetry/core/_vendor/attr/converters.pyPK!5Tԩ{&zpoetry/core/_vendor/attr/exceptions.pyPK!qd}#]poetry/core/_vendor/attr/filters.pyPK!!poetry/core/_vendor/attr/py.typedPK!Lkx#\poetry/core/_vendor/attr/setters.pyPK!B&b A&poetry/core/_vendor/attr/validators.pyPK!>(U!poetry/core/_vendor/attrs/LICENSEPK!9U%poetry/core/_vendor/attrs/__init__.pyPK!0!3HF'poetry/core/_vendor/attrs/converters.pyPK! HF'poetry/core/_vendor/attrs/exceptions.pyPK!8/EC$poetry/core/_vendor/attrs/filters.pyPK!"(poetry/core/_vendor/attrs/py.typedPK!(tήEC$jpoetry/core/_vendor/attrs/setters.pyPK!WZHF'poetry/core/_vendor/attrs/validators.pyPK!Ml!&~poetry/core/_vendor/jsonschema/COPYINGPK!, {*.poetry/core/_vendor/jsonschema/__init__.pyPK!_mD'(*poetry/core/_vendor/jsonschema/__main__.pyPK! 7._ 7)`poetry/core/_vendor/jsonschema/_format.pyPK!Ϛ.44poetry/core/_vendor/jsonschema/_legacy_validators.pyPK!S(poetry/core/_vendor/jsonschema/_types.pyPK!DO ((poetry/core/_vendor/jsonschema/_utils.pyPK!NFL <-"poetry/core/_vendor/jsonschema/_validators.pyPK!FF5poetry/core/_vendor/jsonschema/benchmarks/__init__.pyPK! 975Rpoetry/core/_vendor/jsonschema/benchmarks/issue232.pyPK!xp@Cpoetry/core/_vendor/jsonschema/benchmarks/json_schema_test_suite.pyPK!( / %poetry/core/_vendor/jsonschema/cli.pyPK!0 ,,Zpoetry/core/_vendor/jsonschema/exceptions.pyPK!#R +poetry/core/_vendor/jsonschema/protocols.pyPK!Iu28C poetry/core/_vendor/jsonschema/schemas/draft2019-09.jsonPK!b/~ 8 poetry/core/_vendor/jsonschema/schemas/draft2020-12.jsonPK! ؓ 2poetry/core/_vendor/jsonschema/schemas/draft3.jsonPK!d2poetry/core/_vendor/jsonschema/schemas/draft4.jsonPK!'9aU2poetry/core/_vendor/jsonschema/schemas/draft6.jsonPK!2Jpoetry/core/_vendor/jsonschema/schemas/draft7.jsonPK!H5-28poetry/core/_vendor/jsonschema/schemas/vocabularies.jsonPK!Y~I, poetry/core/_vendor/jsonschema/validators.pyPK!u13 I?poetry/core/_vendor/lark/LICENSEPK!6vY$Bpoetry/core/_vendor/lark/__init__.pyPK!ˆ2Cpoetry/core/_vendor/lark/__pyinstaller/__init__.pyPK!o.W3Cpoetry/core/_vendor/lark/__pyinstaller/hook-lark.pyPK!f<=-%~Epoetry/core/_vendor/lark/ast_utils.pyPK!hw "FIpoetry/core/_vendor/lark/common.pyPK! *&Lpoetry/core/_vendor/lark/exceptions.pyPK!Hc #Zpoetry/core/_vendor/lark/grammar.pyPK! $H$-^poetry/core/_vendor/lark/grammars/common.larkPK!}f#N+K`poetry/core/_vendor/lark/grammars/lark.larkPK!-s #-$cpoetry/core/_vendor/lark/grammars/python.larkPK!Z7[Tg.opoetry/core/_vendor/lark/grammars/unicode.larkPK!, $ppoetry/core/_vendor/lark/indenter.pyPK!N>g vtpoetry/core/_vendor/lark/lark.pyPK!6ZI!}poetry/core/_vendor/lark/lexer.pyPK!mAS85(poetry/core/_vendor/lark/load_grammar.pyPK!Ti 6.-poetry/core/_vendor/lark/parse_tree_builder.pyPK!Ad< $,poetry/core/_vendor/lark/parser_frontends.pyPK!,hpoetry/core/_vendor/lark/parsers/__init__.pyPK!Qe /'poetry/core/_vendor/lark/parsers/cyk.pyPK!s5M6*poetry/core/_vendor/lark/parsers/earley.pyPK!d-1Fpoetry/core/_vendor/lark/parsers/earley_common.pyPK!InTCz1poetry/core/_vendor/lark/parsers/earley_forest.pyPK!6z47poetry/core/_vendor/lark/parsers/grammar_analysis.pyPK!b] )1h?poetry/core/_vendor/lark/parsers/lalr_analysis.pyPK!d9A;Kpoetry/core/_vendor/lark/parsers/lalr_interactive_parser.pyPK!fL/Rpoetry/core/_vendor/lark/parsers/lalr_parser.pyPK!kj Q+jZpoetry/core/_vendor/lark/parsers/xearley.pyPK!!dpoetry/core/_vendor/lark/py.typedPK!!'dpoetry/core/_vendor/lark/reconstruct.pyPK!&| *Wjpoetry/core/_vendor/lark/tools/__init__.pyPK!2.fy)xnpoetry/core/_vendor/lark/tools/nearley.pyPK!BC+%wpoetry/core/_vendor/lark/tools/serialize.pyPK!![r,Fypoetry/core/_vendor/lark/tools/standalone.pyPK!fy  'poetry/core/_vendor/lark/tree.pyPK!"%y(ތpoetry/core/_vendor/lark/tree_matcher.pyPK!V*poetry/core/_vendor/lark/tree_templates.pyPK!YB %!poetry/core/_vendor/lark/utils.pyPK!ϖ$!P$poetry/core/_vendor/lark/visitors.pyPK!Ҽ%fpoetry/core/_vendor/packaging/LICENSEPK! ',(poetry/core/_vendor/packaging/LICENSE.APACHEPK!P G@)[poetry/core/_vendor/packaging/LICENSE.BSDPK!Fcf*]poetry/core/_vendor/packaging/__about__.pyPK!) poetry/core/_vendor/packaging/__init__.pyPK!*:c,+Fpoetry/core/_vendor/packaging/_manylinux.pyPK!t10+poetry/core/_vendor/packaging/_musllinux.pyPK!0`,poetry/core/_vendor/packaging/_structures.pyPK!Ȧr !(:poetry/core/_vendor/packaging/markers.pyPK!&poetry/core/_vendor/packaging/py.typedPK!-48-8poetry/core/_vendor/packaging/requirements.pyPK!5u+lpoetry/core/_vendor/packaging/specifiers.pyPK! msS=%poetry/core/_vendor/packaging/tags.pyPK!A3h&l+poetry/core/_vendor/packaging/utils.pyPK! 2 I9(~1poetry/core/_vendor/packaging/version.pyPK!?vhJQ%Apoetry/core/_vendor/pyparsing/LICENSEPK!J_ #)GDpoetry/core/_vendor/pyparsing/__init__.pyPK!/HF(Qpoetry/core/_vendor/pyparsing/actions.pyPK!e~2'yZpoetry/core/_vendor/pyparsing/common.pyPK!<ǀD>A%ipoetry/core/_vendor/pyparsing/core.pyPK!-2U t\1,'poetry/core/_vendor/pyparsing/diagram/__init__.pyPK!6 t ?#+Apoetry/core/_vendor/pyparsing/exceptions.pyPK!*,٘(Lpoetry/core/_vendor/pyparsing/helpers.pyPK!&xpoetry/core/_vendor/pyparsing/py.typedPK!^b(*ypoetry/core/_vendor/pyparsing/results.pyPK!5[ Z4(qpoetry/core/_vendor/pyparsing/testing.pyPK!zx #*(poetry/core/_vendor/pyparsing/unicode.pyPK!-Gl%vpoetry/core/_vendor/pyparsing/util.pyPK!DBN'r$*poetry/core/_vendor/pyrsistent/LICENSE.mitPK!Y *apoetry/core/_vendor/pyrsistent/__init__.pyPK!2΢G0poetry/core/_vendor/pyrsistent/_checked_types.pyPK!kU ./poetry/core/_vendor/pyrsistent/_field_common.pyPK!\k *kpoetry/core/_vendor/pyrsistent/_helpers.pyPK!) ,poetry/core/_vendor/pyrsistent/_immutable.pyPK!-GJ'1poetry/core/_vendor/pyrsistent/_pbag.pyPK!R %)2poetry/core/_vendor/pyrsistent/_pclass.pyPK!q /)poetry/core/_vendor/pyrsistent/_pdeque.pyPK!7gE e (poetry/core/_vendor/pyrsistent/_plist.pyPK!zYen9'poetry/core/_vendor/pyrsistent/_pmap.pyPK!|x*Tpoetry/core/_vendor/pyrsistent/_precord.pyPK!4V='"poetry/core/_vendor/pyrsistent/_pset.pyPK!2-X*^)poetry/core/_vendor/pyrsistent/_pvector.pyPK!iR9d ()Apoetry/core/_vendor/pyrsistent/_toolz.pyPK!ȿ\o2Gpoetry/core/_vendor/pyrsistent/_transformations.pyPK!'gMpoetry/core/_vendor/pyrsistent/py.typedPK!t(Mpoetry/core/_vendor/pyrsistent/typing.pyPK!5t&# Ppoetry/core/_vendor/tomlkit/LICENSEPK!n|'Rpoetry/core/_vendor/tomlkit/__init__.pyPK!b8;&Tpoetry/core/_vendor/tomlkit/_compat.pyPK!q%oUpoetry/core/_vendor/tomlkit/_utils.pyPK! "Zpoetry/core/_vendor/tomlkit/api.pyPK!f:s(cpoetry/core/_vendor/tomlkit/container.pyPK! n)zpoetry/core/_vendor/tomlkit/exceptions.pyPK!5bU '\$poetry/core/_vendor/tomlkit/items.pyPK! rF a%Upoetry/core/_vendor/tomlkit/parser.pyPK!$poetry/core/_vendor/tomlkit/py.typedPK!=T!5g%=poetry/core/_vendor/tomlkit/source.pyPK!QP (poetry/core/_vendor/tomlkit/toml_char.pyPK!!Nn,poetry/core/_vendor/tomlkit/toml_document.pyPK!Un(Opoetry/core/_vendor/tomlkit/toml_file.pyPK!O 1-poetry/core/_vendor/typing_extensions.LICENSEPK!"s3Fx&(poetry/core/_vendor/typing_extensions.pyPK!C; &poetry/core/_vendor/vendor.txtPK!# 'poetry/core/constraints/__init__.pyPK!_"@+L'poetry/core/constraints/generic/__init__.pyPK!4WL1Q(poetry/core/constraints/generic/any_constraint.pyPK!Zc2)poetry/core/constraints/generic/base_constraint.pyPK!OPs-/+poetry/core/constraints/generic/constraint.pyPK!w~%3.poetry/core/constraints/generic/empty_constraint.pyPK!r 3@0poetry/core/constraints/generic/multi_constraint.pyPK!Fij)c3poetry/core/constraints/generic/parser.pyPK!c36poetry/core/constraints/generic/union_constraint.pyPK!I @+':poetry/core/constraints/version/__init__.pyPK!lYsK3E;poetry/core/constraints/version/empty_constraint.pyPK!V QNV- =poetry/core/constraints/version/exceptions.pyPK!1s)=poetry/core/constraints/version/parser.pyPK!36+Bpoetry/core/constraints/version/patterns.pyPK!'5'rDpoetry/core/constraints/version/util.pyPK!<堸*Fpoetry/core/constraints/version/version.pyPK!D55Kpoetry/core/constraints/version/version_constraint.pyPK!`u 50tMpoetry/core/constraints/version/version_range.pyPK!AO ;IWpoetry/core/constraints/version/version_range_constraint.pyPK!2@ 90wYpoetry/core/constraints/version/version_union.pyPK!er^"dfpoetry/core/exceptions/__init__.pyPK!)NJTgpoetry/core/exceptions/base.pyPK!uS ?gpoetry/core/factory.pyPK!oOupoetry/core/json/__init__.pyPK!F_ F+lwpoetry/core/json/schemas/poetry-schema.jsonPK!\YCpoetry/core/masonry/__init__.pyPK!Y}  poetry/core/masonry/api.pyPK!u$sօpoetry/core/masonry/builder.pyPK!(poetry/core/masonry/builders/__init__.pyPK!x3 1'͇poetry/core/masonry/builders/builder.pyPK!-z9%poetry/core/masonry/builders/sdist.pyPK!j6 o4%poetry/core/masonry/builders/wheel.pyPK!:i 9poetry/core/masonry/metadata.pyPK!%poetry/core/masonry/utils/__init__.pyPK!=x $Cpoetry/core/masonry/utils/helpers.pyPK!.1L$\poetry/core/masonry/utils/include.pyPK! wq#Npoetry/core/masonry/utils/module.pyPK!r[ ,#poetry/core/masonry/utils/package_include.pyPK! poetry/core/packages/__init__.pyPK!ta,>poetry/core/packages/constraints/__init__.pyPK!G"poetry/core/packages/dependency.pyPK!HZi(Upoetry/core/packages/dependency_group.pyPK!Wui ,poetry/core/packages/directory_dependency.pyPK!(']poetry/core/packages/file_dependency.pyPK!yRJpoetry/core/packages/package.pyPK!і!"# 'poetry/core/packages/project_package.pyPK!*p,%poetry/core/packages/specification.pyPK!גK1&poetry/core/packages/url_dependency.pyPK!&gpoetry/core/packages/utils/__init__.pyPK!("poetry/core/packages/utils/link.pyPK!Ԑ+-3#|poetry/core/packages/utils/utils.pyPK!}L &poetry/core/packages/vcs_dependency.pyPK!o<yzpoetry/core/poetry.pyPK!,poetry/core/py.typedPK!!`poetry/core/pyproject/__init__.pyPK!ܤc#poetry/core/pyproject/exceptions.pyPK!S#OEpoetry/core/pyproject/tables.pyPK!9)Rm _poetry/core/pyproject/toml.pyPK!-I!poetry/core/semver/__init__.pyPK!69`&"poetry/core/semver/empty_constraint.pyPK!xk V#poetry/core/semver/exceptions.pyPK!v p#poetry/core/semver/helpers.pyPK!d.n$poetry/core/semver/patterns.pyPK!%]%poetry/core/semver/util.pyPK!(h[p4&poetry/core/semver/version.pyPK!7`(&poetry/core/semver/version_constraint.pyPK!OHP`z#p'poetry/core/semver/version_range.pyPK!Zd.(poetry/core/semver/version_range_constraint.pyPK!y!Ҳ^z#(poetry/core/semver/version_union.pyPK!`)poetry/core/spdx/__init__.pyPK!nu#)poetry/core/spdx/data/licenses.jsonPK! S(Ha<poetry/core/spdx/helpers.pyPK!Gv>poetry/core/spdx/license.pyPK!򔲳SCpoetry/core/spdx/updater.pyPK!p*pFpoetry/core/toml/__init__.pyPK!sFpoetry/core/toml/exceptions.pyPK!+ۇ>uGpoetry/core/toml/file.pyPK!iIpoetry/core/utils/__init__.pyPK!ALSIpoetry/core/utils/_compat.pyPK! ]_ ,Jpoetry/core/utils/helpers.pyPK!g=Npoetry/core/utils/patterns.pyPK!»CpNpoetry/core/utils/toml_file.pyPK!!? [{Ppoetry/core/vcs/__init__.pyPK!c +Rpoetry/core/vcs/git.pyPK!^poetry/core/version/__init__.pyPK!eLP!F^poetry/core/version/exceptions.pyPK!W(^poetry/core/version/grammars/__init__.pyPK!wKL)_poetry/core/version/grammars/markers.larkPK!)- v(kapoetry/core/version/grammars/pep508.larkPK!"u 7Fcpoetry/core/version/helpers.pyPK!< tDfpoetry/core/version/markers.pyPK!IZ5F{poetry/core/version/parser.pyPK!`&}poetry/core/version/pep440/__init__.pyPK!#=  $X~poetry/core/version/pep440/parser.pyPK!|E&poetry/core/version/pep440/segments.pyPK!FBx \*%poetry/core/version/pep440/version.pyPK!% #8poetry/core/version/requirements.pyPK!WJs&#poetry_core-1.3.2.dist-info/LICENSEPK!HpTX!Rpoetry_core-1.3.2.dist-info/WHEELPK!He0 $poetry_core-1.3.2.dist-info/METADATAPK!Hs)\"poetry_core-1.3.2.dist-info/RECORDPKRpdm-2.23.1/tests/fixtures/artifacts/setuptools-68.0.0-py3-none-any.whl000066400000000000000000030423051477560627500253230ustar00rootroot00000000000000PK~V}a~distutils-precedence.pthE ~۞Bĭa4RAmPpm/CiWAػIFm'Bvi&B>b-Y?NfdʘMS%zYТl^t1ՠ&_fi|缀HN oJaEmH%KB<臍!!چL^kgB{ZMv(yC!A!3~v0+@@(ILt`;!:…ϓ:#);D 5O++T&4C>Imma8=>"dQ|%Zũ`"qhk -7O-c8T:2y'f b jH!q꾫=ZqĔ9 #BÞ*AAż>WaO2$q6K%NʼIϴ?,$S)8cd[R>]Egֶ,~Y0@D$bzDd>"h0;6iWBt@wB ^oiU24W(ʖ>{3snk(5ɢu%3r ;Y׼Ə?G$""RS0aTeU|<=8d*,XFU&=+vZ,<\l`vE/$?p Qc..)ݨ8,a4EDz}i\.=a/_rg> \.x0d "iu wWyzc8:p8oR3ȁi"^q f}FΈņ MVWWg^Ԯ.XjCy6593zEfNjp8n#~9m8J_OE1Tȼ0`isFaC aFA%a٪DIE$MfOTF0a σ)74U tKhIȩ?]k գ㗰ARk\oU*#iC;`83;$;6eu|kWK/_U[xbi8uSmִxbD""%,f@uew2]G4/ʬ Ē GT?n3mb1^"=^M_E'jwcYmvهoн$F'cfp0S>BP}?o|˼'{9R}3IYɿ%Dp_o*<„+R/w{9D&1J̋^NBnǛrNZG5|[f@?qe{נ"0I[ͣ|>@ &H ^ڮĤ]7E4g"6+R@3btL2_AfHj&vCg΋y si8a]1/=)&@F+]Yg. S?pgm^2o"\Wp,yVG~z0 y&aZ?p90𳋣HU&w'>?=yFSdx ?Iyom~^|~sx?}ÿw:7u=G@ "QB^ ߛKZ'iUn f-D$/KR.7Ę"8@G,9J:T-+L4i?|Gp0_~k@Rwu#Cj P ͫy9[gHm lj@LI*=П<*~2# to׽iZtruNar?>M/1CkڠvTk&s,Բ; 꺕xa)|CqZ†, vpyMRO̳qgPeH>){ō.}u.uY\BHKpE<ɽh p;wyk!_rYZD1s@CI%]㠋 eS`-][˲"z]^,x Y{ NKZqofl %IYȫ5Se1Û ,K^q$sk6R{vA6^rhokZ*nb%,q{cm\Ηod+8/bߴtjHMen m7p;f$blm٬nxz|Lnྡ.k[.Sl̀[F!G&93;֣d0ϛ8zb #R>oayav:Wt7ǧq.zJ,4/6Z04?4TTpj3=4L`h'")o˙%Y} 禚\:M}]sThK|} y|/cu^#y^1qt?9˦Plan|P6z8nKo"yaǽM+b=dZټjJ(-Hz$ĒC5c'7yYU^;Y<:]֙il C|)di]]}QA7~nVǸvvE5'pV5 S( 8@mZ2cIcfhA#}ZT}e{Rj /3հB/ӺOX6O xA{UiH`dݽ"l GϘ\xسyܑ;-X0AQ@>%F]w.J]ءA' 掅Ku>EY@O BǍ?9k\%gy[6/VhU-=%dy xŅbq$+#,S+h$xGJNnp ?ݟnM(shX̙hD)eѮ]#afXuIb>s=:gЃJ}t hx'DNu~ p?z<DF e<<=~疛fxU=_;rpp}I A۩Nζ ny~1^1ZjA,PbynErY[V] } [m>UƣpzǔOI yA6&F."J8vwsaqw'Ci0+qM[Q#1|>|i֨ўa{)~fz#QxTNc=&4l3e~< @?,lnLSvFL̖ V ظ=v/axa#>\XE`#q2[iM~)=FNȨI Q+$)/)mf%vHW葏UԘx,y3L7=}oa2\fxғ >G4(ĀS'f8yz ڙWߒlXlP-{M;mM褉Z6(-V\%}Yme@zy8 R fǬA#0eH"AŗPW8{(l1cp'6g)=ߝ"ȁ8LW*cg>ohjmp4QA gV`_EclCH /}H1ag R7ۊ.=P$1n隲B' Y]Fٽ x:Og)P\itʩ`s pizya gͧΎ =Q0on6nnQi^xԥ3;F2|9򂈀#pH2团) AGxl#~R- !o"M?ř%Zrc;v{̸7-0F{jD]ƯEM( jnp2$r8aYr]~NrtI|.ndy>wZ\Ϙ%Y2 措#tMhRhwL5otxG`$Qh AP3'u{ݎXea=R'c2tk '-p !~Yٛ/Lj&g3$2t:Ł evA?Ȍ"P/5.z`W"+lu&IEy!FưF(ޗEz^v!.Vhu뽘bF\v=e1w' X7[i Ui(Ha/Ck<-u'ض`q1]~4.EIsx_OOϊ!yLCeoO(Oѫ{@)h1^F+Gp+b(djIaH#D|aWuWpПvWzQF=;SewdCZ{oQDW~B{5m-x2bg퐨#p!ϯWF1ByP-36EĂi8ZN.Roù̆-Zj1xzeQH hT:8JFJu229p"*5NHƀ δT,fUkf4]letSDb &DxQ>1Fp ]LpO'ґNŗvo!ӮgGzP.G:d+z2IT\yCOt&v4 z)U)(#<-4iI0h{ (l1eTi)ʭ81np0M_sVO:!Q^4,8 PѠ)ҾɕfbBKWi3>C 4rݪ.u 3kkŸQ\#BژeMfu]5ghG}6Ѯƭfm2CcW `fAL&#\#A&5t G:ow&rHԘ>zK%BR/|ɑ^V*}Q+`CoĸFͶ3*ÓF!Qsd'M F D\ n")>1ncy;6ˠ1xT[҆)x6U?vHⳌ2~i3Pp?gDb:y(/5y>%NR9M@t 0"s~-NMuuԅ;0H D=(Va_E!*s`Z~?! b|N `2_m@ Ɓ$ 咋:jpcDј"2>;AHGM^ p~%y4W`No+D뺜\H&;=rzC̸Ѷ qOalBG>pV :K7mp^zY^4vO]Gՠ.su>|K^ϻi9D3 NBqԪ-6puU)z V*P f&]SxXo{c(lC?#gW@ͺjDX Q2ڑbXEm5(J-sQUxEgڣV|u1pUMMƝ]SݓO"G1nN 8Ь1U]6s[ eo^tPL`%'I:lfcG&wC0 }_e>o?cHQfg@i;}ضxUw|SZs ,'$ ]W$n6'F|۫( tӼ[бH75J.tCr!e8CY3i($\G": %$kz{3 :6[UQzȢvًv#%ǿun4P< CwtT*{TF^0*\{U:!SFȡiCd#㞧\"MG|;!tp75!]m[;Y5NV5u=bGoӔa YRN7s6,7ŢΫp1Ff fvr'K9~Kȏ[SxC0Tp bOS #QmU2,$c^29'4FFȩ_31+䔉.LE_eu-yT(Ã׈Nf̰CGwdoT<n%a_NO"mD~vWvl'WI5ac 'V8I6SMoe=ey9*c鼛|q=$xciOucD)kH&TȤ&>J1sk I'Lh.ڕsTR7k>5kͬT0GBl6Ɖ5v쨪1Z!6dfAƙNt6bBC`a%sEWˮsΦQɢ#8>pzho}_jM%LJ#~?N+Kw$D)kĵul*0 Вt0;( 69 +Z, P;1j!,v;{C0Zb7{ױ[tN~t>KC}jIjM#9*TiqM-ƺ:7d "蔼9O|`hT2#VJ}R-g}@tQC9c/I99kc12tCxayI-d0YLd2K(Qm /=&Z'o.6tw#n#_O$CT%0P0=0JrduzR=̥ex\ڈвt yis<58? 8ݍ?8RBRcԕ]Zĺ+51Z]|'Ӝ.\`L?oYza(e(Ya8L cD"4Jy+\VМڠ8}hJc 8GNWO_ou,Rnn-@yޮy 빳L dtZ6E5J@a vcy"8&[taAMyQVbB ၉xI.f:Hde jܼvؠXe'c7jbj4!sIcՙ iNy{GWމOfwId%D yɋ{)'%*ڙ,92%N2`~i;7\0^.dߝ&h dv0] lqj&/.sGε G9Mnb"w%Wg8d1L 2bpںS/@g?߸pDѺf  zzjOro1&|^`n o)]e^]H"`g4ʵQ?|1O<NyS ;9hX›t"([P E5`S"uӽ2wm|˒282@ ?8pB'WW9JG <1ʄ@{D Jeul=.fLV60\iHSɷ$jӓ!~9QGA,6<ԥ_-jN g%e %&(tpYg# =])A  KY.酩)Yieh+q 6bj.lSOEx6Ր(9gt:_~.0[Ŝ,H>K*`ًB[MI-gSNVUrmɅa@R`jzH')  =r-MĞT8;IT 'ȗX/y5Tm~^L&~5f ՘`U4T@\/(5b@{d;PXt/.ˠ9b:Gqjc>>Zbe:􈺌ZQ^7ja0:{3y˷Nh߽~/ҲxѶwYϒ2?bD6 \c="<^Q7I?8b?]kwI#--bv%M7 #B/2Q.®ы+9 y'X>.YضWMOaM (^BL%/)P!hXa^WWaW|:tӬV8|9 yjwi6cS|ߥJaJO5nL2㘶 [pe( `6L֝H{څ5#io&o#C0b6{% Kd"ĐJ_p_=Y)CZC~ejY%l#-M M3b(a_t=?pv Srh 7Rh8s fەuW&?']]n $6э=p4YG}qO(@h|9ڼiHʦ(13?Jס%& `]Rs.~k`=|Gcmh(.OsҾ*`k+s8T?SAxrφ sޜW'D1&{  NELZKd_BcZ%T&L&orc ?֥3@ncvU/= iruk6$M_ کnL\rc.6 J8&PٰO{Uxda'|p)g~kBMz3&mzsL_-5$ H_Ç/6gjy45ټ~x1"@m?;T_wDKpY,Te1}Τ!+NrQ2yuw{8ӿdb+D#~z2 W@bĻjip==&cU0!do JF_>2s Hl80$ލP/|hB$NUG>qi7.tRv~ dȆ)SS)@\vmxd(F6,Wc];;zw.95YE;WZ-ݪ R0p&{xBf%_YOa 1'cW ɗd +өNwgHeR]mx#-mz/E1̪v؂"[\ss|#hybf"6Į%4'guI i$oAn!IS/IiaPm ށ̸rET&V-FU_ŮMt"ɠnZ0!E{e@ { '#vp- &n[ˠ4HI@Js ,]cJE Cs2ޡcYK~  M`>%7 C|-LHy.U/{ iYgڮd4x1iGˠm#~{}w?췢'1ehB0U9e:+| 2Dǫ֩5qz|ggÎŏUQ+wVގ@I&Z2%HSm7gk6AB6֫] OQ2A|(F6ƒ _.Z! EnYv'SjyHSsGWb_΃C}𝦩?;Rt&|{jv~QŠzp‡[`ןEiOHqQ 7@>k>R'6-rlQy1!(/m5*UP_Ї2 ? D3=~`/ICN;etXm-y 36ygSq81l8 p1KhwSJ2Sݼ9=: s)Zu!ciH#E W:%dmhWqUpi]O X2;fpyG/s#%J\zHdƤTaFV^j<~+tnrcA}TZT2N !n17.zy.<{Wd<vo=lg;_I-a4s+{l=S&z>xV^Զ<+EՅ3gbfQp9G9":/h>W4%o;b 8uAiD~p7:G^,,{n]wm2#I_݁. +ri})N,!*dFm@d..tm)vo0hmDy鲦3T]w"LeYxR^.\n4d>=xȫ+% tg4衺DV5zbe;KI!d?`(I'?LX Ǩ[@6mɒ.c&I؝|i`W:RIu{@ވ;3s' :ĘW7p֑=`vSLFڝ` W3*Wg/\GzM.lϊ,?ROnΫ$ bo.׹qf1u*;t4=<~):qpȠ,sFȆɹ<hhW[܁a3VL@JMF 3ԛy⼅l֛|L#f3NZ31T`Rދ hf,L8QfkƇD0b`EVH*jfH9}< TMP!a0'ԗh Xf(EPy_;_\``qZK y?19i o_#5um3/z ؖ Q_)Q[_-,buV4>UI ٜ t8ݖ`~j-f+b!!A ANg ]#I3;" M[p{6~)S{MwĮDzblQM9+05FtI'3.-X1$5'pB{eJ`VZۣ)O:a[gsARRd0Xps0l+um1FyT!Kj9f 'MQG耽x`VKNaA|czEr z?]ă'q<:?1.`Bx<2n戵3O@7MmQWl00=mag`f[MDN<.72'VK Zj5yKMrRv 6ITaRN LZf^ nv0wXY+@ϰ#XhCn1ˣοZ.Q P6P=0DzO}4:T6@v; 6,&pMZhVKv7Nne{fPU-!|O҇;x9gcD%-B,bLO};<uMLxZv"8JY9?L#ymr"ari^ [!3:~flfQ`~?:FCo2+uwulPϝ[YCG{!9Ǔ7?z9|0X3v {S-j`s"9j8LHrnd,b'iɸV"vRSu66֛5&䡬$!Y^~rDt%JlyQ:55 >djCg5NR)|f]gyi}+ 'Ckr+[Js@ųr1?.Y1Cd^JAbvE"`h!SK 3˦E L?f'g@-!/B$$唖)j윟 ZJo'bZ$gi|J׮Ul?VIt]ˎUw$WIp⢋\Rou'kV_ԻstSC|X/%hön2%j$OMT+Lʑ]rFpgW! |u2{p.#Jx4RcB{rFeҋw%_ 򄛞 6yDw7Dc ؒU::"9h v;p#{Gi;ޱ OXzם hvAr}\eK*WOOspX u&Mϻ^HV%PU23Y}C6=%\j/۫SQ*9<=X ׭յ)HfRl$c3ARS#ד;8I֬C$1)ٹJO.k_C=V >ܬgKx1'Q91__nGe#-naSJ'#3gf5I@옙h ARX`Ro %1_u Eem&%C+6*ϟ |zx(a US kII܃AY,PW07Ka']D:a9OŢ^QN hM7B{h!n]эnN1@h7Sdtqqy2qߏNz0s].uu#ۛC#wJ*Xw^1=XM!<{us_ X =-z۴$<7hD'3Tu Dİ/-?Lsl'Ƹ՜3B1_'ЍK\|02P9NldCmE4$`#oZџԷ1';nK!=%Z `r8N$uwk#0Rֈ/Hp|}PSb{*Ʀ?Mz=B)Τ3-7q{k}unlLKG*RkTYrv5a.c"v>0L#<,1 ߥ#ŢGp 2UDP:&+a&z#F؎g_sG:G2K} ̟j V{'tFʴAv{Ę ib8C `m.{!E_J煲)@~b YlthjK0Yw#ܲ5 %Vr c"›/yIp?O"Df֕AL,u}M_٪xgHѳ0J}M,~p_!XUV&}UY5Q& #/>{瓧Oؿ^<~6߹ A9~z%5T<5CO fs>*iʕ?`Mb %.4.]Ӟ4zp̊5ӷtb-y}8+(_A!+5J@ 9/WF5rKL5{L+/D3<}Fn"7V3\ZPAiSOvrR@ =*Ƞ|䢤:e JDnzE$9aY"~X\}K N].Ϥ"LJfߠUDmR~P8 jDKLqT HRɮبa>N.H7q|g0 /)U)2 @OEݟjq.uSELW}icRZ+I8^\Ckm i0t4S[{rd;js'b`H8p~#%D6672FQ,l\5GP7zvt_[[ =;M6 !cDfWcZG$ isoY#mƹȃphlr 1h[t[t e{~]bLLs Ok}bQ C=N!cЮT= Gt2+p$2@6Ųe2#ibбKcoIB:P4te |ꇜp>_sK1}+A U]̣eԟ^IDɼJFU4%Ցu'q*o`|(g-0N3ݩdԌ1@N鰬uLGSەVՄѲaXGJygfk7-e3Xg^6PhAwf G>㵎rU:^֡*n 0+wձ*G%ނ婽,@I4 I}rH@i:6zdǞgٝj:d#4%DIqXu}}P]~|MwCƁ qKL`qv#>QC,J G{6iF;%Oq@}vT)_3;XIt&]-СZ|l;L+H!vq"guȄ$ g9&kҩ#dv.cؚ}F֩2 >DF#"Քo>\t{&iw/)C./uZ":T p43VHDH6+Js  ^K cYK W5s"MbLm/tM|ĸ(0/b$qp6\8hy?z ?Jv}7ʠ*icY5fUdqŋӒ-Ћ޴yk#Yw'=|К.^ƭ]zp4.RJCc\ ~U%#6'`x*u,,MfzQ:?0^b C>d,7K4vqQlHԅl}ʯ#y(GH$&&(4.Իn;g$cL_ s t,MΗ8G&jmh9|5&A+i#Iܧ ُLYf~.`qrXɋ'?y+̞߼788?RHxvSXS~W6lzO/'b5Y LEH&Β%wX]UMuRt<|rAm W/]=zOMI&P};2K~*8F ]: &ϸ`m&Wh[{$FnYK*CA vKT ܔXs>I* a}lX/UJPPg GetL~-\#(B3akB#_ot|@CG3k ՟3 EqM]`+<N{Nſ DJHE%fg6C'"XEJ-)Cujlki}a7](!ONhdOOZL()cD-Nx5g/x:=$8zw:~Sx2 9 6ǩ;8X܄|s Ip2wOHpU2$ 4 ι1PD>mɕ5#T:q*B db!>Tɵ 'K&yn*:t?xZ;@ w eN3L#Y##{ ؗ &p"dvٽ'K]yGp"R; SRc_JiowYi&+t3 Xe& 9vm8I+ O jTV /oCh9tR Mmʉ ON6 iMnL`t֨IAD"ĉfP\VnR^ z* 6틿ITwYz~ρ念Fg YKiLlwHcUιq:au޴T#8upǤcړp|jek#V~C8lhXbЋ,(/Ply0h3'7“p3r8!GL YWQv~j"mFer/u_drqDB٥}&;Bi<M Kx l$%+~5KsK1t:]Ua]AN%=k0_zEz׻ ,k;17؝ݞ/jA#kbK70Bk8sD7%l0ƛ{vn~pqb.GG5'j=Nir/:s~fx / !-8S\ѱDA90"Pˈ' a?^׎yp aX2~ 'Q^kewڟv'3ˑ71f> G)M 0T,S{blޫBE}%.r\|F D%9yyUl#O$XiAJNWk/SWt,E"AOVoRiA]|ڠA?Z; L:ϭ3&J*DOю)U[0&%0tQF~-O!_UӲ6j+j`SaM`bkx,I2+1YGSaq2fb8 sЌ 6ÙLSi)5.}bo=#Yƀb4$plVQA[xgL`KLLF!IDzt1q|Q0|NɟpniR"Q8 &!K`yzl6>&1C=Ë <7;|C[dW nӞOh;6}wKY|gU{Տn ?3N8dBCΝدDpj,ga.SRaXCl3>N=hY)u]eI6+2pl:9P_wɛZSa%ɶ/98KG?5 tM. }p:szׅMO9貉1D{ЖżdN }H&NA!4P>Zqk S <;M@&`5t H̅H>Q%ELG/g P@Mɯ\ͤ.2De$tҴTWLѹ ZkXaGv((V2: H.=:7{#$vL}ɼRCJPҋD.s ֘(**.0Ε#c &QPWK@QV-ؓڄʿXRɽMn+*m0_$q?qY$YÕ.[eG 7608\2:nVxyKj+WBP(ntM~ LC>[k#i/tkP'JN?oGQ#=6!_㝓/9x6]XmdLyx:GGE-#NeUេgŢOƮ/*_m>!QDS*[s㙄92_BЪO72 =M4z$e] BT`3-(`97SŒ9\$PbO.x^ƟpѸYs5kp2t}Z駼y2*e:eB?w(” Q9tP3 ]xq ϱ JʱWî` J;TAҧS$P" UdQrZRݵ޳q]?AcEh)]XQM Fh B^@zePqd@!)Pb# d>]JA4%Ͳ:38™1^(pܟ[`:(KGwGd!Ng4 Nqө&X{iJ^O}޲n]dZ#;Gǭ;9\vW??>|/K=Z ğ>P+[jH&MiT~36FY8Re e(<x~7.hqy(J锆njʤ&r[*$))l/tt+] Y5eݠ)"jiK l0́pݵ/l ,|}`_Y\S5y:,@U sgZר2;ډDY%T<񇞏mΫ, N /Ly,r t\xv9O1?H>=v&_|pV "N[~G}c/O{Y)[c~hIgWVq6yh~$!1҈tEwVM4|ޝ&p:z# N/&;f?Y voԦmX\kUt u,K4w直uB &6S&GM+}j R̋ n:e҈C91=;ޣy"7ȣdrA UY%mt&asvE_fkaQbjTE-dэ%$GcՄUTH:{aJ[45ED]r5znhhV;%>Ga{jAJz<* qΫЭBᐨW=׫m7J&W"R ?'Ar/F߆$ڍaj72c{yh<'1of&)j3w/ ~NCeT@HicWۘ U9kzI+ٻqHqʆ%k:2_+k9l8%В @ v|>?6lhp(|<$2#%S^]ɼ`kiqŠܢ6:O- IZmbN8ڒ6+W[un9] qbSr )>* 3ta2|&s:  j+Q5"P˸ɣѣa6/?9؏뉼eWX&eibUD J[=< ؅US iלqndc,[)嵬y(#,R(;h)ZG}3-t+yp5#Ŵ>'5C_qnPSXpdB#Sa(b{Pth2P<o'5q)jëZ4[ \F*ɝ^+1+ iP y_ݾ"٣^+eO^>o%峯. Ȏ>?Qd,k$&1)7+mz~pbg6}ED5E=5&? *k-kN}xA2kb,6`$)eˆ'4S;4܉,*R7b&t}65VW9HO;R6ȱl٫l Ol aOqQХCBώƢA;y^~Gj!&RKp }o+h.k]Mt\ MJƙ7iMOM8GuIlϷ8ŗuU"g[Qy]O3IxdC[9 U\'.,chHaeSS]j^HrO Nr$=ÔRk3f0W$uϞhvyTMYm>5[# _-@5r 6@}FQ49< X>Z?5TR6/O֧2w[L%<(˿9'j@V5, W,^3I}e9-}g~QؑV!:ɛ_zZKfȆ@0}KME;3J:mY#ur{4םܲ?xvLQ Z-7N_O1Y6]LɎ͟N Ȃ,e2s3TNɩ$e-f@t `9Dcrȫ+yjrV Pc}9zMKbV[?ϊscGxԔ~;xt9j.R GXePQ 2[eT6I4u8ĺ g%DNS7KhN-Gs+kpz]ҵu(bĴdJ#.\|#=k DqyUDE/Lu7%k#BBQPKjbAs.[051Bݳ@u))'o, 'bqdLb $0ۆKi P/a`lG +:c-vZAY|, 4 `FVK*!H4 .iz."۳zt,__W0 CJ;H0 qCv@nM2~$mm˱ф㈡iWZ>Gb|z5 $V ih7liOV>sGhPv!3LA3^|Xwf7K'ָ(Z޻FsYia7OrE{/Y[MxՖX +P-w2}Bs;9@ey󢨗#J6)3Kh[?)ȏ:MUɹ.{HsFG5ue#YCtB #a3dzbpnS&x׶p**^%&m""k@(8#_k+N#.δ⣄͝#bԟGʻw3ƴvO2nQ??C#}B0-^eB-0cyapUL\(Y٣Ҵ6fMU@g9nez,O^pC.+4b%+EleRaQ5؊RtY#ݕ::5|WIƒd0P\JkACCWHj#: ďOA-4U5/guXse]q;q}3p.'xp?J"Wa\U)fÛ.PK~V!pkg_resources/_vendor/__init__.pyPK~VIB/OI8*pkg_resources/_vendor/typing_extensions.py}w+ǥC3Zq/b;$.t7,b% `0MUI6u^V--cl5߲y%o}UhWm ×Y_AO닦L=~ SW'dKFgM.jbdF~m6s 6ES}J>j_/y XlY]YQ~m\u檻<;18۟_?oscmWż5".5 -vnae>݌lzM.n!a6/EUe^8+kaZvZoܐYaʳzZAU-,,6# O:+eUͫ_jbF]\~Z[E*,*X(t2S_i6ҿba߿5&[y訁usJGU5_Ym;r^>>g_/jf~ȁ&,~+j7Uztvp 9IfNa/כ-fŪh^>9'S16o3x'9sM\,_UWIVɺZ"Gd4 \+@z嚪yJdjL f( uHj?t4O&sY:d0EL C:_z ˃]|R{tD ϰy]MW&i0>an'E\ e1*Λ`%gMuѬv~912J^ a 6@B(XHJ+YmLn`@_;P72WĽ㣷;/vレk7<4]>MliK73Z]O7>lhirqґk-MsxD%ǟ<FIEcUTUr3\g X+(;\W[1>g9H2` [XoAM(Aط\^`v piQ.+Z89|,\Vb*{ 5IiNN$e5 #=o}c Cyu^=a=kIWE;ky4D{ ^~s\dG"t@7Ue 2M!~PJE@uż% *<$_o5;HӓqMѴ`鞍F1L*<{5 7(~8.QA*bȅjX=rg\C4=>d!Tj-|VQQQ].t~@N[5 K#.9l'(ټy 5 ]yLpWρmXl!bm $lIޒ 3d83SyM0&_J}-žMŊM-[ms)x؏ clQ޹q6 COQ +)XJ {(C=Wz^d lJy82U^E:{Pot4v;z 7侃}:ҨZ)P> P&)EPC`J2'#"K96@Su0aF@orGwբ7^]ccܓ]%8]doF4x^pD\l*8/ 7E~e)`-ؑx<2K&4I+ & &eP~){ewt ؃ю9c n>Q掣L%cȹ?|:30Ѽ|ӣ'gUAG]_'ÄYKBV@J^;aBزEu JDtgsYo\n07rK8^yl%,*O(3- R5YTcX?O@|j ) ab#at@F%{;# A҂.TN_Ϗ,>{>5CܵiyCqG13?ʒ].|N^|OhϠˇ;;̍|5459Lm'הa\)Y - m"PhO(S\8MK!dfdm~$0,e"jHӯ|ipN?\\=tK8[TN筲*iٹS`@=ժslL%rabXsL_dM%6cG3L"ͽRl2B=}{M-{ܛS15Bὗi-9Q,o"sKܑq;h$5ю+\hՕR\@k@΂}Ѯ:QϛW[)ap.`.6,5 Uj wUUIo $%<7t<~U4Ń:JgGˌ\NsGVТ5OԾXĊΛM@#q4+$$Zޠz 3ۨ6MV̝~; ң33|0!coH(V1 *>=nR!懓2|ֳsFNnخ5d*-XG:)PGw(p6сk.:/w+B^1 P:e8 *2ca, ^xͺZl$ T䫢,T /@%ړc hiwgQ3S*2N_> @dM/~i¥ ,;k*vltj36W90U6sUᖴFa^BEYQ>6\VWW$j'=[ބao)hJXrݕg-j6}ou氲ʲ*YtzOv b[Y$!"i BjdC/H"b?4zTZP?.E7 r[O:Nàjfaï 3G{EfAy(Y0qiAr #:# `/)$K=~Ncj)~:rY o:=_^NTqE$ؕPEpr#!؁ ([ 6 ,yrL1wAN< xu&$5Vi?Glzj;QYW)!(k1aL]ʓ4:!qMؐԾ˩?Q-͓f@޴t'ҡX8h|_VE:mV>}#ߴpTTۆ];ź` Ha&Oeb kx%7nTTȹ:lс(ji*ȉӽ~~q9 *('Qݤ}G?L}TJ=rCU"b4[Un_ / =80ζ<9wT⴩L}*¯qRӏQ'O닓߼x3XO?C,wl@k\ _g?r_w>39o)RY8!*l9zY'WRY{P$jĀCT&CQ4 )RR өz=ܛ[ qb% ί'KUF"ѧ)X =oAghrGu/gIUM˨{_iVTzKiQ25pOس{,1YunmS`,!.yG>l(A[E7WU}1 ?~&b7M^Es44] j)VKM?ۘ2#u^g׳|-mZ1f Oh*k br<?*r ]=/bedU]L]8"`XzlSk2C:c|~s5[h׏ip7=H"48NN3\T{/& ‹%e,}YÄ lE`W5U HUS^ ^߬߱ʬA)@ڳ"]!P 6/H.kVV;F׿ giml[|»UXQ`A{vԁ]^:*3V% e%=_݃TYg ?fezRjw;BK,[c3OǦJTz;UQ #CRnv vavkv=nMm,r@]FY0ن/^iܪ8vw8eYQVxy^chsV#8tn%ݷ_l;݂J gmv4vO>V N! Vhx{uϳ-_?p_+eWDv]RI0;ɟLBmv+نI|'(S %&^Or'Wgn@2R'1Nrgϗaк2)YY{ c A1V>CK zvǴLZzN,a]cmWѺ(k^Zdq@icc X5x '^-ն2fX ྒ~rm: FdFo-؀O'g{ݎY['z1cv__xٻ/u!3*1[$ /i2],Z[ sed.ezHW|ؘ%Ȕ{.)=lH)t,wMCKG0ZP;2u)ؖǗB̿v_"΂ANN[9Yu;o;r>&66<c<< JGC(JŮ?HYC7?zC[ߞ ߘѼ-#eWWZ8㪾=P}$ u 䅻[L]BzةbɗjBTt99Mܢ8g8G4<kDYy4se}l$uל . ?A?@*8ŗ)yFz]վ0>Uy(- u z=Ab>MӋU5JC!vOl?e9EgqDQ{<{[| "C«jEK1yT/ vuץY_-!ڏ\a>MUo9؂vI֯n ߟr1 Hh g n}uVZJ je֢r~fvݨIW(5S2rN-,dz| Nb1u|v1Jw\wxDTZý6~wҨӣۀmQkx݀%|ΫNl;-d0|կLBqe4 7:]hV8I~hYϼh.?d{)K T9#w֔Ҕ‚6]4旈W:v b7~%A7p Mݴ 4M ]it0kϡ8s蚍^sr`KU{薅$T0j<ԾbGu %֨=oԙ&R#!ێh7[:e]Rr֗6-nw6%Df3tV,S\(AoOW,&a>ӏ>_?<V[6hAZ $骪Tţ^(r=b1=<:#fH4i,ϙ4juSnP!n Hx0cý?LB ]sO`FH50W:\>ArfA[-WE{3y3.q6E0CuBzHMynGHMTGuu/)(#"(hw)O!j'hBx⡌nE.ZyX]D^1ha$$Y^CG$+›|G]UP 7r_MݦIiwXlNӻQHгDEpih%4v6AՎ\XʃY7Pܬ s(/k {UCN\ڨ=,7ELpp_ߍDέ\m\/bnYT2|e܋28>JdyU&Y錿4u^"r݅kVk~ ]\R dM6q{j9fEPT ?{#ȨS45uoһX!i?ڒ)zHHz3ݻF6 ؃7MO`p%/yF=Eʭq\D2+vR-`^ʅU2RQVjQϔZy*I fwj gOԺ vþyUoM6uv† A ivkÂQ#^KUS΋B2]wdd]"$~d~j^y&*ŴnTyQ/Xηqx/iy7\ÄOL-"EU6Wk/Lc1x;]R:sOLx ŦC={$+F!G z,A`V')Ttz+μ M8i#rX P!ho bNq+_v h)7KMZNmLr(g!M׸?+94=+K-ݡpPf'RGIj?^>P#9l`Vdϒv*ӗ/xwBʎlڠF|GOiLPҏ)ANx݋83+\*9svf'KKN^0oЖ3lݶ:lz{)U!lWsiEs*CD!%ZkS^;sZ3fWeD$3d =g֐+w(cp%438~` t=z$erH;p:aG\pW^ȇ+%ƒ 2{ubꍻGu.¸<(j4jƅp;Ed5;G`<k_4&a#Pu&؏^? Eqm<<#B&y Wت*(; GsxLv@o[;>BV̀?xJ&!9x؄wQ/7ޮVąxؖ6,r|N^9{!Z4Am`y֞b1Jd I(FӬ*G@ rEeb&2:Oq~eKt;tykt#J$kS6y2\5b-9t?黜B;<Бk'P(;ÏC LO) ;2Kb-@ 3Y{ڎ_%}oOGc%l)k X21YdR?Y8o0LNO }5jJ򮨖8e\EVN(PBϟ'{)sxuP7Q ELU@{@s(@WŴ S6x>C-M+̅QF&3+EŸǘxzQǻ6жq`_ZYA.=mbOqib8ojԸf:G;J'gi:vKR:/-&ٖe0"{\L iD)`g|[Ƌą}CcFf}c ߇EXC$a`QM䊷ѓϰ>'5$1fWlgJc]}z@:Ge:Z@ *\'Ptx`1H>zF!a/y+:VVVe4ɋ;NLnqB1 &E('|R$6I~02"v8#u D +rFqw_qr\}eLI ҝ5=1$pGQ Giv9! @OB 99;!=}^N)S%={y8>]`d KwgC^&w1%ڦ6 }8HMvW\ͪ"fI K>+ :рd(Hxh9=l5 9FҢbS:X)8yh[{<'S:g7vSYUvtVE5&EkN)%_f3jKM1Q|kߊs+80kf% HP=wOz>;kki.L0cL %ۢd 8AKg5s"L]R[}O2Y/J KRF^jx(">"#dwfwe4,1 E1y=d|fFwO>~$dSi# =D]3!z=, m`/+ac2Xՙ[Me}+5:==bq\+mCR Lz2?0cG" <mbԸ=Gz.8!ERYӺ }r$rTnmQO蒏~A> G򋨆N8q%ތeObŘWB:dCMXiUrS#.u#[qgWN)'c Y-Npd3µXLE*)[nk"K:ң a;#bUHe4F>*$9X{z)ŷi;2to7co Q&Wyn]/(UI)[mJC}K(n gT:f{TKUc%K.##VMnw2Ğ]?;[*MXIuϰs^%L#nH*}B&~QMߛ{ ~:{ 4eg(x7xF9I%YL8YI" ?D_1fMp,rKkmRG tiUf>|Yе$fzr㼼Di+{ivEOҠ=Kqv P 7뵄'ҹ;,.0 /v lȕK~DAscPG:{~>2G?aL~^P GȤ6EMs LdIB:Y'7N(5Z@f'U"D,UuE}o}EZ9:w0pz,pNcZфr1m5!9qQc@g'^`IqO.&w(0|I 7V|mqE:~\FTA5䬛i077f^)?K(U kVJqe>gGv2#cWP "ת俬$|]892TIH6}ءCSWt̑^Ѯ AD$oTґm߫pܩp׍v[0a0G< Ĉ 7D嗼*Di+(OLwz .6@) |[4 vnhL4u.0̙qZBÐ櫪zM.`m=6ƈ-7*C `G"q62 }+ j#JRmp?g1Exf0N|TՇUB]&`;g|{yt53u_q'CeO 36p"y+QGDlsn)kH}~c9@nEr3Qu+\7|z.7QK悓kK"%c;^jR-4p6X-!̢<$0]Ů D]jVa-ل4,RP_+쾾};(,vIpHE6t {5V1$G1`ʮ+gʆ)Bt-*4-KvVjqjvG܋8O>S'Dws-=wn߈uOb_mV/9=57˩er#mբƽ0p 61Xr99zy!͵;Vr^^*6{IE<+]ȊuVK= d+C Zx>,xŊd0BNP mrNBm+Nf !X`̅jv-% o;Ii ++/Z}]f#.54tVHՒ4D5qƋLS^&Q@M+[fi̛,&y}R3sk)B|B&T]Gh4 q$+ XsPI xtX#勄(W+ _pvbLKkC`E`J>Xaa0 I+슴@$cZﲈċoiC']"#YD @tx-jzv8s( sEh]0 Che 4+ItAⲯMښRqvXg;x0q&L[uDoA4Go5tUD%%j5F ]dbݖddA8QpZaȢesRD8 nyWuT /$ynBM@ y. yvzieG3̊U^C} .ZhmˎNɗo/[#ֿGQrů"vzT8HGd_ %rVQOLtwr(^еqÿ;_S O6J}C^HOA=h W6ޝh9&]&]&]&]&]&]7?UdrQ< C9ޙ+JJ׼{pCCW׆NJ<甮-xYˉҕy*]9z#!Dl 6C~pXn8$z8Z85^ rwC/taշv\*7\KCRGg̋ƦJ {}_|\)W=e"rKj srs4Ij%ޡvÑ? C45_FΦlxTϞԤfB=Wհpgr!D(x"F"I0M$:K'^orm-נ 8*!5/=n@&N!9Xhǜ(fcD%VҀ3ofw!^8`/C7c[Gptlsc(4z-s~?SHr#&~9KTj447ږ1Lć'0n’I0Lh̿ s i7`tZwugG\rG&M_#.nxn#(ޑx_Ͱ"?Mͳ.M=;J&: XCpX»`E'99+'l7c723Ox n!6?;Eכ S{^C|u@>kQ[يoi<=w daS7{y"2`ѹV+ .taBG.'V&w&XP56 8 D j/+4oۇЄڶ`- J C jf )e .VЂ]k Br4@60⒓F+4 ߎ#4khzvk ƄMAI(VDHYg="ҿe~8X'?8XKKA5#U1ȬIߎ8hX1p^_>y`}"?;qX>󭠾wd?g4?\cl-3#xE 3 لrrP~knr |јi0lƒ9ɴ}w_(UÉ/q\NOV&!![!zsS'o86Tq슼r!2XN YyE:gS&QjỊ-QܜJa J/i{%ߚO볱 %T%|v=N.[ZaK'g~t/]s`6Yt[;b\@e&t/}aaGJCaw|@P#xT E[oG/qXdN瞣gXXlDIӯ:VwBo+x_y /yi&:1+m]_Dc(1u:4]Dmuܳ3nw˽ʩWྲ٬ }"%9.WxdW< `(6m/sN|[TC:G0UǥxsYXF٧4 ]AS 1Z]EeuU#fD&NZi?BWBH܆x[˒װA#Y?,`vcbEIsvA.Hڇi"&f[orlRlM"#[T<4yW兑bhޢʛrآYnBhI(̀/QBCid]ЯBWͨ~@tR;@̂=N]@+Lv[s^V A('p sX{Q4dfg0Tv01U7Cs ;@E3eK:v,c XOY)VY! +q Gȴ^ŕVK Wּ#5tmRB=ڰ*:Ahv,YNQG$8\O *dq t,RmV4X/_g*j^ ™pFC*fQU'9fAKdY4c9]ح/AwT#`lqz3>!Rd:e1+ELݼM-rŸuuK^61~1rKv9;Nt%A$-'ehdŹSF}!P 69YJ A"6u2Dq3Ӭ$54iD.W G4ćiH ؟qrsQv&j 6w֙f ۉN_481 e}t[ū-<2nu$];xHv5^i_7f8F5JTwW0ۈ ӌ[4oE|pZ6oN8^?κeZ)nQ:%<~vQ,5o+NOPQ|uȉ{AйXr#jm@έ#g9_"iiu(:/Gs'ܽZnA,1CE\C}垵}ϕ2VUz{pˎ:N7GyRDRzt:uKo*Ԝ\Xz:1+o6[A ftnn50ys_OLnc"YǬb(=P:pr#L= V }U+ rBmEy8_wOp~3MILQVw~X@B*'Ո~Q OU;*poa1a&ϣjѫiP]Ҥɽ&q/!n.PK~VBq8u pkg_resources/_vendor/zipp.pyY۸NXN-0IPŢ 4Xh(sDzfb>D~I(YvA;h76yn<в^3ٮԵJ>u\O+e%R^mmxҕ̆u$vVAJM*e`ѷ1zWm aU%r Ŝ۾(\DVَZ)4‡{h^B,x/"黺2E>qc*{DML q-  wN݋F>ZTdzѓa{ґcmfcv?3zn H@Je@K*bvIxϒ(BfLDJ"Jy t^컶!O$ro>qkERkZM_GrF6VemrêxU"%t)!'44;Kyŕboںoe"?dwggz:oMڋFzM+)d32(t#?;#^ׂlx1n6D(asD7$Z+|b@C!zq}=Vy|/1'v$-F|B?)&=a|^pͩi?PnKiSLFĀ^q%=rڜ#ζy~؊1e cZ[Hp={3!3\*|FwGẠۘiDJw QC-mG#y 0MZ j]EgʚMA 2B%O= 0% CjJ:X%|gjqJ(**0?c[D3_6'Y$7(*ySZuΚA YU ^dNQKFZ7i@ i% }=ʯPXUU`aj; M:QvwGowwvs󠹼ؠV]+06^ˆ|U_-W wwr xK:[j -H.&Mi@2_ݬsBٝm?̮s/#JSwzlܸ]]"2 |l 8af=}j/Loi_.h3C6oIrT8 }ynF痧9Dxl3:fJɉ1@ő==z"}|3Ld"n  "WR4 5oFmR$1mPxxa>=}1DPu驜\!D4n SgΞLouFiF SKj)7)ԢQԫB{6v& y;۰Gѥx8OGGxp =nYL-ǘj3=ozqy+|EKsg{7nɸv9'̯gv_lĉ u(KXS5v"BP :-_ jzN@_]Թu s;Y8'l5L Ðz ӫEs*sv/[eU3pAAg73XSïW۫͆0y0\ FG /a+1Pb.$>`h EYQg_jp8?rt$q0|-rsLNx\cB,}Û6 ww~у޻G\D' ߧg籿 ./8*w,pTT zY.&Qsn;{y)G}FP[.g(fVș'+?m iV`~4Op6RlZOg|ꁡg:՞{x|7%KtPK~Voq5pkg_resources/_vendor/importlib_resources/__init__.py]A0E=Ť41;4CZ֨RAV_/, kk h,23Fl,nn3G8> 1=fkqԢdbr#YU܁Fwܜ Ce0bT|]+iꅼB){^,yZ`ÒòbٝѦJpwhigj!0RWe}ژOӍ ##C]Q }D/ w.eV@M96EF߷9%xh:uۋ Zy!xP]bJ`+y0Ƣ& @T`eyxjm8"2\cw|@%C+Lb|qSRm7%T@a;]G`ǹzN6>cSyO%Hק9:1I$o(WyO{z9x/Jr0C`\skk3Nb!uE-vi 'Zg?73 {F'΍ŷ'x2Vw~nU>Waڗ>."հO;dVCW(!b!z_7[v(Ѹ ,~x&x&0Y<5P7<#7bv Ò71H8^6j=7n4ʕ_8aܸK7_wK0^2=af%c!O뤅D=zCkN"|nH/qMt{T\PK~VþQ4pkg_resources/_vendor/importlib_resources/_common.pyXM۶W7 K6Am"tiylWR{fHrwwwZWQzmN +2\*l%@Jo =S=Su^=[u IgOC{l+,syT *;Z)G5(kh5;3ZrGJ"A֛t"60Gu&Gjo ^-bh7Y}كBY\tY1}n` QwBzؾhNoD>N0/!{mF(@L%lo.!n5SZOsއ2m5=Q}tujح;TO"/SbjQK/g$^{#m(W߽~N2tvpI7ѹb+YȗѴO<#+J<0(5Sxb@ j˒ Z᧾⾩{0nJ#P\Ձ@ 9h/ 1evxY?Skv~ൂ#b~<6m`w[K t=QWSI@ d i&}&F( eLSeVGo$9keu!HRԈaua1"zX&^Tik<:[u0&"y![ 6@Vx#rhC P(rٷHQ(Mּ̔ؕnt?,˒\ˏ^׏aT`1+ ?كCJLt%6eX+XW(N5S [ƽˠ> 63O؉U V'UO[Qۓ0!n#JB`zWS1>LYTTp"&,~]&Cvtڄ{VQq 1"r\`_ǣnp\tFv;~\ń󟞯կD-0|I(E1ҚÛKwMԁ2ҵ,Lez+Y-b蟁 q HVfb64-D,,MmG@ȩj(L_Um Mt@kg=H}: %È+כy|p35,}&CaڒH= [n6Xwlfg = s-pɘh1b$:RM@kRn.1>Cw_bdlʭqq=Xi+0ʴّVxQ)@;x̤ Ϝx@t h<F 3h!=K5 t4:zbw b+ܩPJI2Il4P?&(Qff^;$#jL_j᠟Vo1*!al :ӢԤ -kTpiP|UoHr-EאfǣoK0*Yߊ+SCvCdЂ 5ǀ\x oR\Y xXD[xFx(:9U)6ǟu>X(i)Y7# (_Z*ywRZK)f:%JX\KiQ+H Vu05iĸ 4QP.]rjJWJ!bVT^FWl$ {$kޒzm*?PK~Va.;m 4pkg_resources/_vendor/importlib_resources/_compat.pyVo6~_q`!/M PFaC &LI%qIQd%M4ɻݏ9Tg882-M+{5w;)YeJ>8fSX Mg_*#KWX(427QWBb_iuX%3{Ҍ|\,sؓB7rErׄC?fmRڋh?1 pvYpNivz"yƔh?jVl6 WNd84F߉ N*6M-%B wd1`_-nH8p#*m`ut; ȍh 2P./h"#:,Tnޫ R WEY-Xd~{rF㎝*ACO}NFl:ޢ+L ޗin?-frS7tR"n~a jϋ(|R İzٚ|e\t>F۬zH,,R!,BzM>lO>@PqqɅ<,/ ?m'lr!}"իd.h}ჰ?ʟBv4񉛍7h<{ '7?^*C%}⡉~H(U.['m,>T;Դ9T\5/3ZA(̩~PST3{cɮMSguržoSNj3MI7"P4 ְYyɥױP1i[MC%qnt4V&^Xq ф ШYswCƊ(Oxzz|ۆƙU,/a⿠i6b~7/!u;A҈څ"!?=nh64;PK~Vht7pkg_resources/_vendor/importlib_resources/_itertools.pyuRMk0 W [K)maؙvOvYW_bIOɩAz@֍*(U 吱*StB)S:D.zm4Z.zG]Vm\TI@2E8 +3XI-c|=AU1֔bz9:DݩgŠ|WMqӁ|Ґep8 py瑀 kXthOaƖhFPC 4b( ]\Oftb߄a˛M԰-Se^"&,;i6qQ1I:- zeS/4s7 ,/̔V*!A)axɁ 'sMl[# PK~V.ev 4pkg_resources/_vendor/importlib_resources/_legacy.pyVM6WL݃,=\$mzC ݴE`hͮD $^' Iɟ[xqdiUnl" =>um}N%ՃMŏJjíC#6 V[~J9k}T1XYmJ|s } [] s֙O7%ITXCR8R4[$@WcΈΆ5ć]&psx?C?kԓ/lXhq QGPmT֡Xa\gy131M[:XRY7кyo)bV>5 =@\ D$K,&71wHt:RO,:dAvk)mZϸb6`d0J2L AxfloRHt>Vl 0q=V.,ciT"tM-9;YVtj6eřnLir1 U~lB@z-_942,<U +qrڻz4.2R:Z~Y9^af .< ?IoXՎhV!$Jc4no/ HcSs+?[,I-դ-f<Պ1Nn'IsnqqtW)=tEz/ 6uFZXGc;PDӌIxpa)kYҞ}PJ;-@A[Y!̏󅿖v5K54(>BǶ_n|k`@}DN1^MD_車qdӕG|8#RcLbR9n(#r'3%`'P8C7 Z.N6PVza~v\->ʊGt3z~:ImAggόD+Zr{0PiMhn[%< S'}ɶ }_;IFJIL}ERoc6wFOo;ӁLJ+:b撁+3&T@տˆoXh C,[??TC+4Sط9鳰أ0Ύu`M&4o kS"O7%k<,yD14%zNex"kcfXۺ(:#(;rCYB-Or9z7 ?3\9,RgY_uQUQn,hY0r|M\+– 7Zm~.ɜF"ʡ%͋ᮗ8bMG+* RxaZD#M aU&Ĺ.gJ ChQUx7ܺv6T3ĉ7ƙof,X*T^6rm ꗲ!*\) ހ@I^o ĞNm 9RRK_܁jvLRӇ J{sUP| rfF^O,n7cl2oXg1h"D8\ i,/- 1Re8k}ʸ|v=_B$']N0b. VdZS*M_?-Zo ³"Wu# BD<{.۱,gGt!luLKftSvC{) 0γ`ZcptAW_^A }~פҭ^ c% ֠͛eGD-Q>=L !Q'm'pn=PңS1!#5FhLsaGiDλ9ːW^v P'a_#Ǝt`%8f2hR*h)-AϤFAj E(1 WK,v םEYX+RBuX4j1q9B3=G=x:;wg/Օ 2ŧpƸƃ6U10(GN{!5@}TLOd_o> SZ\'/_J2vl[{@Cc$VE1%< c,ZV="SqWBxOF:#duTi o$hx5[cz8Ae~+6oia򜗽%,\Uk aF9_t()3{b+#5jYP帘80Vb-]tG? p|8flh Aq+]dGm?ƈvY&tVN2эӑn!Rltm/f=:xpy6Wq׼Aeb6Ó,RȋjK]~i~BuWaPK~V\ 3pkg_resources/_vendor/importlib_resources/simple.pyVMo0 Wv5@݊P+CQ-'Zmɓt$[ӏ\"#(sX, rqH!$)˪ZI!$XuZHM ͺ%~ZRǚi_^3[L$0(2|oS+tmXX]ΔܲJsF4lKQݤQ%QﲮnE[/u˼=& N֓63ܷ[l^4Hol8m\=S[TۙL<ȆL L[(a(u ݚ9y)N5F%rz`l*d;S^"a ӐpMa=ӛjl){z1נfJUԩjqNDFV.PF# 憈 jv )(Ԧ#̏ ʂTpX%;P7[[[rlAjdkaI{EJR{S˅_YMeڶ"FlEFU~U#fx.kfKb]7t)Ur~_z̶wlX¬J_N eSW0 /UioWmȎߜށzE䚕6jfb)%g q؍ٗjZ=J{U*mDT4ߐJw? ø)^&MΪ oւ 7QgnDC U-x_ѿ2QĦlD]{+ '`R sYV@\У"W -=apע[. %WIΒdr48OtOn]oD]T/,u+$nKс[˳nح {ې@ZsmaS (J ]nH&׸xmwBBK4s>lh=@C4mmwxpmBXL[nҧOi)V6dgv7v_$ B+/#MV]"KV6ijt,-ސR6+ eu&R>E 5<>]j߯qM'$Dt֥pcv3@KAT; 4dcLٳU>[+ucl}>TX/ -O  [|X9,]IUlAEgevt<$]4[V&],d~L<|oO ː@L(7[fzTA`LJضwRQBL? mCM`t9\K -k%p6N,sr#NY~,AoNM/ԔFq'$wJ'EAKۭεS҈9 Y \ܛv8[6z|wtbɡo:ߩzD@7nǓBڸA矽h-Qyeed_ŷRZ‥vj)uJ ]# w H\u5b/;Eш%W&*dlJcPD/{d8q[&f^%V$QzR5}]Չ&]2k8aNVblc6'79dVCu/ _H &g[V}d1D@(8O#M0+Ant#D;Y'qP\xM{h犂&8zp5R]Pʛt)nKX_s@^9' ?ojՃb-H!8{ 1-Ex/kcl#Άb47@& " :M!n[/0;7q gc U `| `aAD<բ^Qږi84ϢA(hO fq?VJ9Љޮ]4xI xșOPY#za \+O?2?n.#eFͱ-a'HGX6|~Lڔ)@&8>)Bq6|՛MgI]G"+?xa3GT35 jn ΢f@/? :\}( +2l 8 _wure'6AIR`Yps9|ف:x^Y1\J˜vW7#:1x=YJBǙ|vzyMoą7`>_% (O|s< !z߰hkyk{˽ Quˠ_'  sWIrp"afpgݪLx޻ T[\-zk ooA`l#c=h)YIƘM]/Ń|w`QV0+Gsc8F֟=}[&Z~6Ȑ/t0@fbhk&#дB6HθPΝ_X%7;QX5O7ZNQ Ej^4aPK~V/5:)pkg_resources/_vendor/jaraco/functools.py;]s6(A ;_)5<$<\4EBc-%~({RW7X"4ݍV6UۦI/r[1o ˦g?6XϙLJht&vwo~4 +UUtn`fĽQ 6:/OlX7?ǧ;/=%}nY]?o$6Qo<>k:V5$Xwnt: 6L FHs6<h5c筲 t*` Nt]f J|Sz)[1׳x8- ]?l<"2Ԉ XGMeֶFP/pm`GMSpdf<@vfavW AT(HVlh7 @SkkGږOZxgeAzZ4#X]7ZpQl_.@T,q̊^"ٵ*Eп_(S[Cbɞɷ`y3C[l͞tު ㈞_LϿDO 3$r9IĹL{&t{?1>z#&,ˢW|T9^O:' Q:`PdcZFZ6FΏ\Ǎut1&M'یMhM7>>I5Heq?zOq%b0;*~qwo])ͬoh)Nbu}ihF~Dv[ GP0B"k3tF6DmVca 4[vq6 d7yhbTe^gHh46>*w~-C^665``7APt%@d@ Ahv09d`j %.o`\䋖 ( feK>/FHD~$c3Оrd73hٗz=r|fU>|x(큱lg LʋV΍5 37{UٵIBhkP4@jtm~aiŊUD< j dˈ_N*L-A@p< '-=+B ʧ**:2c!T8w`rO f˙'g,]-˄7gI;9p9 A ճVu0) _24[vhMR4oڒo-Ӌ~kAF\\!ku ˂o4"3"̎L )XhK霡)'f%hUY, _%yFaa&2(C寚sJzXݻ)ĝ Nygt^n}wӇl7 cvq$a߱I0F3\vkgrU㈋ ;3% }Zo]>(>GMl,ׇJyh2=P@:h3FrYCKH@1>'f桜F!B+Sr4M 葬;UVJ!AwA %!h4A8؄ ǂy dg~6oc-πx7^9L+ fi*4%9 )0Bph!O*XHLexE4{1L(µЄ F/}>j V<ȕ'|@rضh ] C´Pi[3"m„ lx˹;7zF1-cUNs ܔ 1j’&)ҽ$}N rAproNI\?&VS4 .RSΝ $ 9iktNJUf>:Kqgn=DPrAal'|pѶ6 r*7A(洙,>oy|.EXUMF,&euIukx-GBDИ39:7_XHɡ< jJO8|΋/ ͏\(2DM'އvr2!0"hӞaO=A+&‘ҁ.t"L\iCK'cY,IxRH$E%A3o_EɶdfɌ$&}$)8@\27lۄȈ 'f=`pY4B8ϛ61%SIBhc[g"~F}?A Jn@R%:l␿:0grWE{阫rU WSxUqwYpHvpBx9?6Ev4aJ$+rF|MH e)[b/*8pV4ÿ(ש93 uW.IqS{ n/5SZhE|aDhnP,+( PZ%%jo(lp( )1X\$Ňp(Ål8Wm Xe2YiYv-MiFqο{uz[ЅR.QE08H/)B7B Z$Rҹ4lޢYXlx1$î JOƝ]$.q>a|"wzm!tYnx}94ɐչDbP?.7_MGA!q׽>ޑ;GXУǴU7iQ#oʸ c*)K MTt%rM hN oSy#t/єR<`ͱLg PL%7-c͂UDh@=\g %QjLcZ?o:s/2mM4ku'o +csI7&o=@6֜mt4ĎZ!gv֌ۙ^Է ,f0棷b1q;[׃z" '٦ZpA\-YSYrB'ۄ  8hِ:alLʪNm!^TLKlRm/wb'98ynn4[M \,}|~Ҹ)?pS3XOGpLK/U+4{_9oצ֊D'ɢHI31d:uSCB RceIb6\> S$7RCKil?,@Ԑ TJy#Dl-ҼP`UB5^=bbM&C$2xT҅@ֈ?C3TBb 4j$3cBʨhW;SJ^_Z]<[$87+U1uUnŵldNi'Er'*#ܒ*ZMY@S|Ǔ?SצUHW*$k~&7_zۀIzJg? ܬ$}FY\M8`kCMVzC d^,F6E mqaz ګ^b-*duXekؘ-4~A T֪N kҢymW+l'DXF0@bJx<#8%m{oY5'/T Nq\\fALF|6U58btZd/$̝;;䜵)*{n0l2<&BhU@U6&Fz"?GiYߨf2ܪ=8HCR~$*Q>J &'1VJh$|qi77H;EI[C}F|9 J60$(Df/&'mb84! ³,^ I[n]z%9bDL,G.E>#]9M|ZF҂3b/r >* " 1Z~{Ρ<.[=טemPOnr-@lJ7H)Tre*Od3J? qPV@N!& %JTXigϓb=]/FWIH+lM@ p]>a`M؞)#=2_)2Dȩad ]!{Č9HL"N7(ܓh)^dfݬڛ o!JCkPz2=.-H`ypY]"uڨ6˥ecx2X'*o!zS"d)MNƅd^2Q eu aO[$]j$\.u{n#)ۀ{v§(יޘvW4"x.)VHa^0@r,FC!2ݚu][*)H $:D#kJ YjT H!wYQӗk LBU H%oUk/̶D:-64WhV(18Ҧ%-RTݴѭ+/MPƠ6Ӕ܊l7k\(x1|q-%jQbA"hԻJrIJsrJ %ʴ mZPDl&oˍ$>@:װeTC+uIHb{uv7^׫>/F5Z -K`2oM*^zt PZv > jFk^05EsD H^Z*NjR:1͡-p 6^+ ښ2:nd\!7 %J͊ꕮAV0.j[ : ҙKϫ 纑z9 \{(Y/TCש/L$ͦo ANʴ"Q}Q0 f`|"*glk@9[9aμhaeA\b,.嬍IAqsԔ`ٯ>rUm{-$D$^J:; 9@VW[jyH^W _Р4izJĦ^J9cA8:K|ĹopT hWIKIyuHCXVhhjR.jf>!`LhRW ; N=;j /tsOwK#N;kv=niKR{7,OQ,x9%iD)uW cZ}[}sq+NܖNee˝!H&m KfI/G[ 3.D Zm6Tlah8wЪ])WzP#a ?gPj)Xu@Rr,BR(/F $gn}(h2UO|⍲ =:>:J/4ĐRziC%W|JQyOʘÞФ ҪcHK#CUPSg꓄GH4G; t;yvb0l<˛ڮVu[T\!ׯ:<@1xFP Jvm+;7!.v&ߓ tqtAzei!& K\۳o^ uYo  ph*JnX-w(|qFR޿ylNvRuw[qʽ~tnvQB>pv'L#j_? 7JY'j+nN;yf/!;|[A;rD.k .8QFb+$FT_^Yzow>4=dDt;N ;Ŀ$]u8%7Bv6_)X.Pp$6 e "]6u87$jFބ}:5|/+uVM354Yn=R;c|')%<tϡ47zc$!G7+ s&r|bN3,gr8Ў[Ԍ}6\/vfIdFhWW&)_^VEUWp53+v<)y6,3wPrn‡O㥩9 `iE" H :e4/#+%;DAǗ]by]"7cFw+?"-@ *0~?<B0O.Nx=3O÷!SQi{RHt.<S֥Xw B#fC|R\$O97Yp2Efžη6Vd@^]"ҦWX:Ҵa^⥇PR.5w'w9vqNŁ-p*:GFU$Oe!sk7;!)W|{sYՕc ss̼l>~@t_} /r㝜Rpre[;Mtx5:zGyo΅U42GHrL_7d{ }H.aˌYQn#̖5h@VsVï!H~EnN3~n~{3> БJ\,#_ѕ4njz| ҵ/N^u]O5ڨ~ol9'EvV ocPK~VtlOp0pkg_resources/_vendor/more_itertools/__init__.pyeA 09(]!WM^7Jw.gFD4b֊̈ôj=`73\8|Nb*=Ccܚ-.Z!O8PK~VCz@,pkg_resources/_vendor/more_itertools/more.pyksG(]G < B)dkvtw<֮4" w7DʪAٞːHYYYY7۲j۴*~dUdQ٢ˢNr.U+%l2_4]6JUq MXȸjW,\۶izTrnt[s,h |u?U]Xl-a^' ,Ӽru|y:˪^UU{|!/` Y*&6閿4Mh2S*րIk3n;JV)"e @_W\L7m_WiD {6 `+UYmtͪz2vez7%WkO}_5U5,9TJ');MYMY Aq-m,tV79AP-.MUYâm«u 3ަyuj[H-\#3Ut?xd::oӥnFo5 lj5uHSNY_[jWL`nnu0z kt 6!! b۴)᤺v䢆-LUzX]OnƩv}R=j?qO:j#ɹ[(]ϛ[]̇t %1 ׺ -mH } gM24% J\'*aJ U/JM?Qr2JNGɋQr|z _[%7>2 VHl ےzJn:Ҙ2`'TZ਒lm0I\qu4 _ϫNJx;?zZQ!TT&Lvu D룼HSu MϐK?U17KMXڙUl6#Xp0#f yZ&qP"У >'ˁY>.<*vOƗ{00k$HPeٸ7pʚofN/"`$:Ovh KW6<(7|i\/M~+*ABhw\qЕGQJ C<&8c8%fyJ΂hc 5*4b.isi%ͫǩZ9p}X=Nef{Ok,/[.lڦoKA.rKW,d$N2oJ_8R6p Cu*X3`Tr+D뵛0`  ݑ Ҁ2tv;F0҉r0}~ppT6:k.&䇲!½u\7Ͷ>{|,eu<]v˓bQ\u4Bo*W~`ڦA/HO??>q|L#nx3W7瓁l[lYWv->{,SwFޮMd0I&d:7',j]t8fLɺ,ok`i(Ί%~ KO6p=a3ZYՈ \s p61dKj x59qɻkmY^~@j`F[`8)NX3e뿧/DzM@Vf7B;`'܄GF𖀼m8p{#2>u1q0D뺄Ixg ~-"u{/0j꧌P z-ӈwQǬOpB`L/tU/GGB,`KJ%!9unx 8XL$Fz}+ndz]l_ֆ8 @+dW)J~5zݒuwtLoJLgESB6ij=hZb0uCw9v_*kv +sW/[C {#)7r7Z!ZK8a☇WHFmzc5f$ndUIBr=g6,u6p¶LaLl2ya 8tTm-%F)90ȦTHG̦XHrl˚w#d I!"YU٢y7n5ه{#݇q(E*]dN jq>Q(׾9p!'F%!B w- 86GvjRGJ<%2;|ȿ53G"}"q/Ib/lk cI|oKطx]*ݸHC#KȎ&nh#3#~[b(7~ʪR+1Y*TV Wrz_ &>՘܅,Qjc#,_]é 'XcF+0i@-y;Naط8C4- &Yb kOh,Y//<s" kvS' PCj^~ARl}y*I&&mA'R. !H@6oGqi'wvSZ=Uhs@>,̘1ªk'ژ0r{sANG/NS9=RD%]b5!!7Q$yhҨMZpf '`i:5~)f] {w|ŚFzK uiő&sID_?׸*[*17T(U\uWM`@emŋ*DOf5>_P58v%^?rp-~RKyEk 242(ó'OwN;z gc(N'vG;,H&_p :uNs@I,ß"[A yx#B)2 ZTY;}aU @'EX\ Fe-ވ`M£g XXum,fI#$[2oY{}7.0pJ~:\joe?[%{jԔ16q#EZ wG|V(!SSz{Y9o[N?rA|&wg1=L(Nۧ/N^“KcDi6SMKeMGH!NSz'*h!!|:.k@XoQ;D\nTE<~}UΞsK?Mpn%\ dÚUqHjx4;̗0qEheׇoY×P̫7%Ës;Zׇgym %otM.,g- 40N*[Ibnnؐ+xPC &,|E4FEa` !Y=(E݀ g:Oab@*=g0B}uT0uVr5c(2!{h`dp3 $m%C$r,Lo1ͻWoqB!vƺw]ǾdK aV-D&(|j 8FQbH 7jSpz tzcPF|-,4flmgZD8(?%[؇| p;y'55 EOQ6!$(knQf2aRWbz{yHgttP0lNAK]W7n {s6m5bbfz^3ۚW)7]8 Amp 8h]\Vc͍ڼn VpY&hC\5]-FgCvCtO)!"HM,v-{aKF ~XXk˚dW%J+EyGz$/i_Q0)@,p.o1"q0:3w=5' ?2Z׈lܭW|=<*NZy".(`a}u.:s#,|l,|O]U7 w0hhHtF6l I ;\3}6VGh_G6]!-عh d٭=G3ZW(No1ɑඎºЮ=Bj] )^$[EfmaVE!ا-=<L͝ըC)4ф fą[ MJ1F9 Pq:00 0:/Mqˑ-n>5'"3D3$ X#X}őc_cG@@)0L=:~:NNwvgHTfB SʑqݴPr͕A3>p-^8A)-l2&]hBALc U~ra[% ȼw1$BʗӅ?P6X9Q֕81Yen!0&$%|atyf(mo4&}7J8=P{*_ߗt2I \?l 2 5L1 {&T:셋]} aQ292QMZA'DK잉ER ^ ڽcq-G$LFD\dc^6@؂Ui%gZw@+vīM ,njfvQ3+;`$n=cHIDi<$Cpz~actǸԮWg `՟NFv|wtD)7K2MxH ]i?U@/zzJ~p\ 2U%=Iǫ̷̆dQGܑ5≍p0M̭Pɛ&Z$iXv4Q`~t|:Cpm3Eb@ˏJJXEo(XqDx#*d0HtZ!UHbuE蓤la1!J0W"+ Knǡ20p)͠?=f3ݼ'5% pJg/9yY@boKW:D^t|)5w wg D';!&=lpOZ>@r}ڢ'ns/[Dՠ |UΩq%]&B/QPaV?[-Hnȋ.[B?oVV2٭VsV}i PymΦKB d4A;ݎSGn^U7! cWw>aMד뜍]'ѡC3&Û~hx鄌~'~/i`v<Mbsvwwq|IrFKgk.̷I0ww vcJj^5 FqttI$|X_|Ժh, +5d~ɛ:4 \[;r2gC#$NaG 6?1c/W*^r|i!B?Ҁ䟯Rc-Hr.5l ^VGVAش="Wx9}l7KT\ #eNN 6㩽umn^R} yB%E6jBݪIpn?:#?BFcPFrsh$72o6V3a3F1q /2-vCd~X 0o^{JlRȀrmrKa VA<: ;>:߳8i)96,pDtbg -:ckvwr9uXs)"9ҰYCAAwvm˛<r6ywf(%99pdUV5l+ WZmc#P+񹱔ah"(?RskO4 S2^E4?rإI,zV^s糫oeU$n&C\[q$u13?^`[ҿ#!dӻɂA{D2SᅐYQ~1!F]Ao4*?9⫴)tf_21)yQ[) ұ =8 `;pOM@i C 1Ibb.#͝sФg9o(Z )BMPu\pOAhǞv%Kh;|}J[zAʮX]:#41@jd+ lt=BF߆3I&v"0,BLћۋa=Am>D3c\p؁}~rfF Eg [/a#P|1U9ϋɤB9%e1hPcOΥL[]$z0yÉy\ 3FL+oM@o_p"M|m|} +f,_TۣV::=qY$v G a~f)LF؆Xٴ|3Df. ѓƁKm&.*ʠ TϟK\CA[]h cQ㋢֟(@dk#rPuOھ5U͞-)CTxíOϠ홃MJ$eUu q,"ƥ7Jh'ꨑ5e['f4G^[B.ӵDڱ!9٢.Fr/1BObbLX籯(Ler0:S9[Q.l5ڃ=,*+=UdIn3]GZCdD) P&IֈSsoǗ#53׭8XPi!C,- I!#41)K`wLTDhub30cg%-Eʑi!knfCF# S0Pv\d,Бȵ'jtW%d)!(!&4H|dXPFh&O}t[86@r {}\cu/"QP{q5kEV##iMLtwA=X2d}Gs?ȅ5{2L@V2P|^iyOO'ӷz+C/ [ӤGV8ZVJrz zķ\]aZCC#݁j_ )̘Ȗ]2& `R"PkꜾa,9^e_w n[Gۧ1K46]ri:>˶;P Pŗ2 AQ93;"}ZMٲa_6Uni;86ZNM uEAkGe$Jãc+1VlHtjWh)t?M~[|(od- 7Qm,aBީ9#dVbB(6Z^H\\o&8 SEZ9!B9[Iu EV{dwb׸ї#0sL6\4 {1*gjK,.!߶** x9O}A]qOD?!;)?l,t&obmd~FWMݦK1ŗC~3lϿxlXZ1[l=uqф'¸ sy5vc^̙+oTg ӻ`Nl?Қʢk=B}U; !IMЏ0FV;/ OLoFL;A wSHҲLySzW_Ɔ׹ YTqWPBEIta%Ԓ]>E][iفOO Pp:d7Tso+Wb3|wqnl;mJU 0G&yE!90^.G5K?6R̈́"Cf 12! 3mG{~TU=^'Ţ'X ۅID4]gI99!W^<^.E)sex$+vh?a6EViX!Wa뵁꒻:K)']39Hi&4J[Hb-[F#I0Ca=ǚ> *;0j"R0F}>;,?m[9F}oWxL1rbh{ |`t]ɦ2';ڊ*L[?fo~l/,͠A嶷f.8y F'n8viB"{w[:Lϒzגo ?#~o/t:idmuB/Jo4?674ⴀM7)`°2G{tvǤ-OZBP*&LE^}%tNI ~=Gэ$H2.D륏v݆%nVm%UB7sz62&e#'מZp-yhVM|R,mp0+6tb{ra::9h]Tbv۪1Ol7!``>Y^-s+6 8bj#vYN( &/0/nkBe2XhpGUC]lt^9pF (^\𮨣@&4h@LV|Ϫ5Ha66 抋,]kS] F=?s[TEu=;:E"慁utX.E\1?!ᜁOu܍ֳNZcOq4]A{ƱȦ.Tm/h']#YLeȁ./Q98 / 4@/2" }C&5.́6y JXM贁ɞYk^E=6.1!E(a~p3q#8 S4%b'4n'|`䌬o\\ 3 Q=(_eq#s*+|F>h8Blŭ|]n $Lພzǽ'>JQӴۑo;uvו&ft[W ]g"q,ħBZQ65 !q$ږ ǟ|s)SZ*{yG6Lk^]yzr>p.8br-[ytHL ,5ήsGA[A E eZq!A18 !sQ>BX*O$lNq-~!oac *ik,##pA#Ԃ˴ш/yT nwkT q< [! INFRՅW`PhgKJ) C%)^䥓< aky7 M;+寃؈XZ=p\)UD*l֔5&\7KP&b%!tٛNlT ;17fص @Y?(rsJ}[LnvKdQn_f'ZB*ѷGL) `:b[B92?r&rmv7nfbVL}~{&0*\N7I^4,U-܎UHXRDZ͖d'Wa~rZ%uA4~Hh flIEfB3d3 F(Q7 օ|g{f8_vSsB\.(N1p#j EqZkkD+&n='v9x}VF_̵6u&%y@?# -j"!x搬LDN* ^"hYZ!)泥A$03MaD:z*׆-Jc7ؾ kC[ y`e.{hEJv *Q>7۪f|biN+ >Y`Olb,*O'9b?&בSx&(y @2>}qQS:&gjvǡlAyU kMD mZ0z$IBGO0~i5)M[`ol iy-omB>P"t8wg¢ъF+M`b xfLȠ?#݉-b~[?twq&lwI +Z'zyy7Ҧe%Cp# dʃL )GyUOΓ0&9OGMqWlEQ3l2P6)p:krh3S`67=\4 2Ѯ .bl͠[#^5 UqEL|F49Vz@qZ 9ު,#ij R`qFg0BdUŬq, -e`B|~Y2)QFʵ{u;ph`aK;dǻBo0RKT[11 R WW,h/Yj5 f/ar_l F=v~uHBDy64y>EK>YwlGLX=u#̜pצڛS[[Vm6K 9( .#p7&WHv Cem'X?Mb_ߗS iɟgjԷ#/g!}!MVƖW#%ގM YjZb0 7N󶶊;L{va,6\AAO gMOxuECfT^W_kSNݜ%7cgKYrΒcܘ]/ѯ\I'ڏY 4>و㘦{g\qp7)#j*Sށhz4&ٱQ mJ#|ͅ⪨5VD!)31$:,K#l-8XsBw`U)rm/ qܱa^-MK="m]G$˂d#f1߉H19Ej|wHݰyA0@>C:F_`%z݁#y`#*(!21huЦXvPփSPpmhKukkƍF|1es7 ׾R!<QϹ!L4zk{n0SK%tS)+g;O 9/[%"vGTWf= 2͑q?QQMˑ 7?lyk$pg % MDh^e7@'ObP5^ok6W3aE6~#~'=%EJSV0S<$ &!;bgHڛv[Pr@޴~̰պGpHMMb]ڥFL^ c( ׸ڌl &1j|ѬV:6ުQEmu1E'Ǔ/҄=N WL1W],S(L6R[wǿӆOE &ƈ"c+N1tՈ8.$./0 klRK)iuWuG 4-aP89ҍ j%[ŌkDgӃz}y# oXVcJڲJp"@^-O%4an9FwXFg2`3Ü8W5O?&,5lE040,!S Ӧ]meV=N)0Ϯ7_Z[v/uM50cj ^Z9UiקNMܹ{KŗB<(G < i&Ժ6z1n%%0(RTA?jj'T >toEOt[k^w3{<[%>PS ? TU=Q",d0 M3سAbcYBOO:٧a̟Y6Pb3Q,a aAP^_5VnȉHK յ@:pؔ6hCs)[^Smw r(J>aCo̰Ymuf^FbV׸:zqDdU4ĨO7FwrMw}>:fP:t{dC;ܭôrlVj2%D8}K36y_y-euW[j{pCw,(!`<@Q+eSMd OwEY6erG6g(W;vuB?8Vմp(`8~0/,}DnAvl݂&ԧ?25xjW#Vz>zg~p%߲sH>|pE7A5d&r[L (^9cu`\ûVG׾|d?Ce6O_Hw巟[Vd&ܖ0b7 pCIƀhPFIQnp˚UR¡1%qD:&ݪ44{^٤ՓV%~zmU "V$9Dac_$1Et#S?j]nBڊE6V^z ">5H|E#{&y=%趦h.x냂[9 yZ?p̻fkuY *keZZ _5\sv&AMЏ,]fr. VPD,˱FS6~>uC s)LApu#NCMFlcE>Pvz 1>=>IG6}YvLꁜ&^ޓVGªKCck2B;rBsg{)2f#C<V]OIM򤄶#3Ե3P_(z\{2cee6qx';ȒGloOJ34obQz#jh=+Polԓ3AhC u{VH`52[e(E͏LĿPgn˱d'=#-ac]9C/1#KYvmͬÆYgq !*Ć>{o[hI<56 {?-G5ͨu?F0}J6?0F?ONuxH]䅓cԹ1jE.-J8~U߻O[j;= 8)w~W|('r%[y#nQfx|W{ uiX?,8*U~D>frs> xaU[ń.83A?!h2$~sqq|Н%A!t"zM[ 5A (1 ]83c7Ak2;oM% (>=?{-z/8x[Gnl hW厃eY:sox?&8^BWr\>U% U!]0~dQwB# E8X=4PIw"E@SLWd*q {맕}MZS'w5Aw/Ht5iFҏ].lғ%luP,fqQxEc4= ^bzJ~lyW:i[`V a_eGxVF&{m=Gו =)J vx渵Hjm </߹L6yoCl\h˘^m/hS(S7>R"ѐ I%yr \@XSJ 2rP(;Y: V\ }BEZEoB'W~J?)1,}: ~h;D}/FnSuR3bƃʛMl_̖ZSy*G[Pl;X>(j΅x`=FFmcf7Ukl{ƩPd@EYϔKc{ˉZռ $ =Hx2zUBJLQNJ#if57? [ЩC&[gihF[8\ȭ菟c|p8 mYHf9ѡDTǃ6&W6*k߳$1c|vN Li}lH{trҚP4xio XK͏OJLC/bn1ܗ70Hxbg8) ˫Tmc&YF;$9G6P)^I+d}σ7B '5#1peސSoFފ%02&O.hAަT{V6fJ<^ukZeT)|m,%dOHIķ 1 AN\&**Zn$D1Qm0,0Lqqɷh%~GLfTGOAz;"J,Y(kJ0DJf?Ilƚhn(w椋n[Ü7pGK?9)RcBչ)QeDøAZZ7.u%Y݋KߝRzoEXkZ39_8ӛ٬|Ç cD9^|Ջj%_Ggg% ^g(|_ x\,`Wh7Sv6RLOPu^Ieߣ2QGRp?/P67-9nU@L9hNm8R*"k'#3~zhAj&fN#Y^|X#n 0h^ܬÄYLVftq v2ܯ+nK; :.;sHlsqQQg3݉HEL6!Q`~ [ZqO)K-;6%'J}n_jGדl|dia(f&-F_I @XLx0)2,u&&BW4hznNi㚄_=dծmv-b1)B3 M$3Tr/hz5,m4H$a)ED{(T}Қ#ώ! Y^D'/1mJ%ʏmMCQ9!FM5Dz0LR{䌁;Cw52 VE8~G7lF@j}Ƚí>pHC1sJ+',A(OZkqr8>=Y<1gXzKvtd¯fŝdD$~M8>vr <<^7w|hYseژ(GE4c`\C;2Y^Wb pHz I~Va ӄV&QT$RRdIpZƑtdNVk)U߆~  n^ 4v2aSGȬw 0G=@*X+A*sxBf[q?OV­`>Ti@ࢠ(#l-a*(m~Aֳeޛюp0.o蛕Y h6UVu~2㎂ͬ-әpGֵvMZ?eR|~o* Gk3;.CX%D;|9׻}nڙ#/Y,c0I@qI]gmԼ4j/N&`ы0s*lSBN8npӢh|宲KS>M+ ,FZb4+XWu`S~7 \]\-Y :M< G^7ҺS"7/-c[wC/{  hZߒXNca5q+GL:;7v%TQ:FHN&7#0 9{$V$# M"d_'bN* +PKGIqX\zYOxkt6'>qQ0I|°gjkŃ2r9yDQHYnWSrE [HC5=(/4viC|< ]aSKpϊ~RS%̑NـNkɢ b8*cc %5|2' yMU1NbFKۄaa̔L\8sAE{TKPD'8zVS 6?)Joo$uzRj!QxA});hAA9`n&u^lbdKR3 ⥁I7=Z.a K+ gI˴ć`Zac.5o/mP,'׈C̩mYG|~evW2 tڛcvcb{G;0 B$T'"M}4y&r( ٓ*[A1ӭSr襰 <4zSgMjTA+ƅ8 EabG+,1<@w&[CY px3~!01DbFO>A@ BXfh6px0"8+(7`v oQ_ZyCEn~) ]P ^jF~5ù@L&[ =i3FHEoE/j$f 7S̄ηXh9^>H1CmrY~ GegO`lQX޴΀XC2e%hHy9F2 ?ܡ 8RDӢVf3l`9-w pLt\\FX'd[g_N{; h IىH9TPrƱZm-|O&Nf+:(MCOy sM%uk ؐ-:GT| BWkQc Pop)^dlq"IaI ȷe쮠mZ)dfl+Ԏ۾̭ēU:KEQBzJzwyMY~P"R.ӿc`C=^tS)'y 5Z&\9y+&4o,kr/\5Κ#׍?'`Jð؆irGQ8!ugg3@֥ZQ\rkPÄڛϑnY.Ъ,Lo1ͻWoWo=Udy&tf UgA)L]d@b'5!ǭٱմf' z%i`D=+[T)u)=^n7O'ٙ4 s g%"5d΄11[L*O9%خMU B(_FF1H}MQLee_HB0g4E5mۯ|t'&4?[u݊9dmxnhiNudo|DQt`VJM1v$Ύ|JmolݴD)h!`b[ ](dyc/4R:n(Ί8`)?rS2PS9t0#+9%w`1IT/,ai2NG.W083:$D1m$Qv6i wWXLYLOשgqĿx@ct8A=]8aͿaڪ+_UXaAa"GT_|>J0a2ȇf'2g({s(~Z]Pjz:`i+Q*5{xCYsJtKJBB%ϹGM%$FGecEDNIkz02/S2_(FXhҍyo1Z#⦌$AHM";72mR!JMQ1-K]4N Xtx: ehQ |҂ii?r̠V;/n(9pfՋ{!CK} ,;JuF:Tg T#ĤHC|1]Όjh<@.H(!"lIiba~Y!,ԻD30rihҤXiW}ޏI[K*·FQ[^?[EjUp-ب09x. ?5S-ڨs[8+%"49iʦ_Yza=^t5I [ŵz$B|y7Jtv0scFW :gVrZh B6,Qp&VkK+3B~T\̼[jrԲ\]iOk|ƀL}?2C3|P ZQK}i$`۰N*Sf&D#o=1nmPg#{#>;mhܭ FA=2(Pv/ΕphPx`A)u L kYfb9k:KUی^GFNXb"MN^Zɚ)@0N|iR(AQ U<|~"E gmU)W@`8* Qo!5;֡8?E;]MP}H^Ǡ.ߨ^Hk;(-M#|2dG5=EĎz%*Q<WHǥ&82^!Fr#WN{Ҍxh#5z.Mwd'FIU7יj,ylڪc6aYR%/VԻh_ۣ`P=EdOy9W=˔TJ2zcfa~ZD1~wx|!WŶ8痏ظn -N6T5هNwmn0Xe&OUN&o& HB1º81Hb|l㲺zߞŧ85;P<:'ɨ;{Yn[!JԠcw6)g-+IS&k|A*8n 8LqJHo`a.U ''Q˧DAf??W==/cґWZ:`B2+3!ɰs1u95<2ʙу7+`P6х|XtivVmߔWGظ <'xrW9pFB,2STC~"oEn{S8mg 6$^hRiovͶ>:E-+̦6tӯdWE"h`CMݤFɔ,Y:x|)A/Qh0idC9_<ؗD'5/;Wl)4k-u r+%VRnNNVVpBhP+u?b9n@pz[a(u'.>/̃@l$RrK%(%g^.=);L1΅Wj(&*\iMΫxy:4墀RC$Cۧ``tjn|OQB;ۭMlJPc/V%R ™I6anFP)gO /Vf:8݉kWW%W50$% yfvﵺW=|ʭK!%|[!T t s_ lWq_. Yt֗oN59;̴ԉ'PQ|FbYuDJ8 Q\2zfL%y _\1XgLYGyܨA qV36O[6>!n ygwfg[E5u5XT_뎎x@n'vXoMkbaool-pAɁPH7S:|J5;LY>! YƭnbY/rH [ z\ ;<=;HzvSkLpUTbI \`n2+ Lho'\9_ACI}_P 0vr_#69gO2'ax7S a {gcĶ3jR=S~*}mhK1'#v,@D_eʼneMd]~NYq]VJ$wbi]-R=;?n ֥|-_O ""L\Zem##Gw^W)0œ搙230xͽ@ࣔ,~}yOoG0Ϟ_ESwDڻp4`1dVVJ< (?H ҦGx@3!S4y: |ݳus[8W3XLDO`=g4Av0T })Jg$#BP6GRVp,vD%e*7uH9( qbőM?}4 2g%u121F,05dKL uJz輒Nj*sϑ^8G"b5w.J&;<}ۂ8%\D\Vm/hⷱOфǺb^@D9(B_-*ձ98ڣ6fbks'rhh-Dƅ^o&$0c|RJyc]Po% u>$pn^v7y}o ll15c9q`,5 8@lhMѱ_.Z;ޏxw]f̰etꃟ U9#=ۤSD`إtg kR3΂\*Hb:|_1s=.ӍfQس?kN4<> }qx]#]f{uK8a=q !FJ5u$=6ߛbûs%v/ }{b]Q#3 *XLS4xQu/ƀHc%o:Mt~@5m˾.'ZLc& EzMΐVjsFLxu˘⠔2Fp R~^RQj2uk~wr$6[ȑ  27ي~Hb'pIϻzo5ݳpٷĥ}!j(3ʍݰ{4WS(! 62]Co!z 6!d&5kP2 "XIUZXx;%bNBcq1rXbLEt.6k4WMK(&^6LԱ5 5F{{iӘ$l77%aћOhk|J9c KKPqf_Nb\q []>c+DwK@f&y"!1td1JA%#y)%tԐ94aAcT|c듗 5-F)9*\HHG^! %9$mgh󐻺m`O8+1Z[E@fpīɡg5fLò h9m#yʻv'ƮYe0n)ժh!;> f*6vhE{/CI8N9>}e%(B&qM n෣V((tJdkPh<Ѹu_MSQDPȻ?t?H'jjCSywOLJQAk/J ܥH 7; VwJ\Ђ*\8(!)S"[L? jOxWѧ`}tđ .K_f3|6y}ù > wdX&aJO&b7^g=3eY%#,*}Z8VJ^\TwD>r (؟"4&&P1C 7SA)=%PZS~^uT=EGnju/\~~Sl/)9ơTWRbA$Iyj_t8wB#tL{Ge";]5 u?釾TM `iS44p'ŭVEPnbޮ =u"Z!iKyzŷmߎ77&elU9\*DڸK)+C7gVCx.WM_̧t2}bG O]< h996g; z$U%O|X<ŽeLMBAp~o+J^A8+m_. nfQ>A<>Fw%'8z HQ-0CcΤ]!slG˲U1&ž[*CþoQ!|KaHHun?$*3C8 c3"Խ yۈKUdX =_w[?R\&"zBXDskU%f}Rf}wd|vVRSlA/Eq5Xj*|L-o0(zV.f#^w.ӿkm*wWO)E2 "i']4.E)$X;sIOvekR҉Tˌe5!ԵS8}/|7Q=cI. V8+g6w5-=ѯo1j]v_ݪ;p 8J^QFE¸f n"aKU$Q`( ~sk uqnşUwNtz[z37JnIӤdBj歊S"ixQŹZp@닣Kc45sn)$i]?4J[z^>tDΚ]2x'2l`I = 1/=Eߚ1b }2(zd˓xÿ9fq\.i>2K+ZY7-ni;XC{=Lr=$UWލL P'(|j V,2B,^ #`6K zAD!wZ3qJ}oԶ ˂V&C*i(O]ēpG~ܒ/&}RYz4>mСsʷ kyKl3.kriP'F}z+B,(@Gm'"6SdAV%"=?Ɵ9]`d>vb+12HUAI U^O%ˆwtS FKnVnT76\q*hU>wBv &Sb\,z\^$7GʁOu?_W6 =nmA~& 6 (% ]Ka8=p_)ohGpM hl\Ed|<>Bk2EK-3 B[]+_e^9E}fX< E/4 АaLRl_v ݟ˘~'lfqeA`[w33\a)^_|,j?=u%/~ϊ㰜r>Z?3={ PK~Vi cHc/pkg_resources/_vendor/more_itertools/recipes.py=isȕ+z"HC4Eɞm*J6N͡RHh@:wd2[*Dݯ_~yɧUUnUѪlkUe*W8ktՔe^\h|=>:zj/uZ<[TIuP˓Kj,o*=^ ȵ"˳^e]UhDVkv0@Rٝ?ǻfؖ04`ztɓ\t4eerEVMvYHTםd4-dt-DyWt\2:R$Yr Z>UE#%l)/0tN'njKl7bkh6dHu |:Vu} vj}w&rt4%y> uI +P5<`4ˍNW*+=f f/,כ2Ӳ'YSmUcita!p^3}Ի&x3O{;_q-yKҢ,u~Ϫ۬WM/fI>%Ue- KfXV;6W,inzd71[⾱OӪIi)X@ ڧ1{,}r=]Y{2`l"0qGu缐CL)<e#|5R),XQeX-FZ۵p }QT*%Y%tSX}ER,޲ uܰ@ڕ$Iegj*O4QwNFj.gOݕ?moA yJMVǭ~?oz5qf/8h-nt7.&j (k-xT \ }hf%u@2Od$7Șa^j6Uy }UUVcuLAv7 f1 X"dɂ}sQ;}E &VPaEyA&pN~)sA-O=qZ#ՍZ-he$IPޯV2>nulodKjS7I0;'Árݎ`r Hr܃1M˳-)މ3H#ѝ (= }R=o-cup~wZ}e\⌗Px<ԇ 7ufī'riQ0l6+KSdѧU(iq2eYy^%7њ WЊxt[p/ikM\.C^^,y{sG(ŵFߏN32^ H޾fӘd[;(tXP cl;͑<\!? 'PvhY8Ax{hB:C5o῏꛳FՁopStg/N\6ϾܼZ~ʄZl4{c˜"<>LPafb( V*֔v;vnqĨKtO@F(/  |v62}.(N'R0A53tnY!.boP hݲYμvDZ6=ӭ?gbx}iiz 1 k00By̧l/` TƳ0/e>fg0%xYcAVԄt?hGAXll:_]g[u:9fcU (}L;5!W|?> ?[38ɠC5bp sUDZ*լ܊ltv $!L(v'8!IlV3*A_ah<\-n7{\gJuNu}(X45ʌK{AKYLAl+'W(m©`mPnQ> q&h9 Qfi1h*MWc(:@|Gر73(f3C nk&#qn=;4[ L*sĬ@| (,'Ht`xqߎf|ds J4ֹnw`a$#w V/JUvS4[AJϹgɼ_ob $eBNHYFj2v v]MYk;˻\y:h1IKq|ٚC,(W&"X#E ~.E.ːuQVZ eతD/ØM-<`F6K"Ÿ@֘`҃AZ)}?g}w?/⋉N+ZVЕI3CvB1CuDNٺvd? < }XC5Ģޭp4x%Ua(2umCWeq,)c1nnl ~_ 9y#y8cӝoV6T7[$I&j/fAa՝Sa> XChnX7V3*yYB#`,teo- '>Zk%h"r :"axNd*x܉vcf[J}d]®f^/Zs>=M'/+Gp>Qc  l]e7~"m0:ay+Y@9 .| H_vڭiڪO5E6LrJ;b]̌¾%Y\[t7&ΈCp\(/45%NC,!sNM x"< F{sƆ X(31g$ Lt7M^tQL7x3V:')?!#2?(t8ۘ]n $4l+tX4ϮyPw$qO4g pKɅP+rC2wEҚj꧲)'ywz9îqS!,3C1u9c`p0/X.FiC8Ϣ*BTհ2`$E]lJsUrVՙ[xWb a44(oZ%NPdgwg<c=W'` d9c';-_A?d2h=:>o$zFXیƔjm8='1W~A˟|+.փ^?82UvS#3!q'nUɭ 'q!cQvYkAX93@nu@-N4XrbׁXbtGl4PoDd ԡ(ݡmkB3 &<ϽjpG#<3;As$~e?Q .q{:6w3~ujoŹ$0ou\Sxj@ cKif$6۬͏CeTʙ _}Ȓ Up% U6ZkEG~βSlUu?e6˂큕aW[ 6KפD)AF_c0kUX\ev$%wװ#b4)'".Hw7Ex\0L5W [ Lᖝ"i.se8K~e)@5=õ6 ۉ[CιIe؆x|ulb}cR2ԜƠ= VKc~v{Sjq !zbj?|]+G;:$$xCF:}ɟ_6s)uE58 jڤqhj1iQA{f2S5Pc/ϏeFt —x^+`lWPFLds=pkׂGcbl-mI= m ^eM" gIHEqVÛLj~ _TWAiΕ5RtOޡZ{v4bEwF4ji#`ny⭿NKhn^ d"pAȋ%v_Sj ʓcev6PHYꎴkAL.lP.no@i͹N~!z j N|&bi2aY؜^(mrVsyAƊ:DcBq#fbIlpU8 {#e1i߭oM{prh?EBAZʒXͰ"ÊbPTTf# w~=tnSYQ5/hE+Wb@t>2$K"_7oL =!hW~}'s;NnjLQ/ g&~*}%F Ί"Hr?wlDhQrIA"bqɽEL ųN7ȭ_-%cw_:Y}rPՆ`DSPIK.@v+~ qf6-%ڗ)Fg_ 0NÎYjE@гCjƓm]Upnn6 $ b,R,`>;<0K`#ꭍNw(5*vǮrr(ڜƝOMr8` !djˆ,nz97e4itTUrECu= S3Y+JnK(*쀩wq;u9}vWY͒P#Q+mdlS{^)'ȶ~=_oL2r`X 9J%⽥UcKzx$=(Q)9^s*\4u5R*]`k^M fyl.@tv2y=9ǽn';c.Uk9ٵڣ H.T#.Hʟb 8IZ"u 6O Nya[,tY{D31#}o:9BMCigQHͬ3,5G~lm~1|1eEt`29_NW#{lĹN:S8&{uQ+/:-j r0nys>x:AK<[al4MGzK<Œޭ[RxOkA*LX@0z}-|\Hz.@Y|OqoˠfdR4^q!287&\>Frҕf`@J YEU|WSQ85%HHa6i}_iaOɨpۦͶLf޹ö} PW @ ]B(i;P/PK~V=64+pkg_resources/_vendor/packaging/__init__.pyMQMk0 Wޚv`aP cJ"Ɩ sb!KCO !VEjGF7y0}Xrʴ5|dt]Y2^/19uœ1cѯ7:3Y )_E2¶'Xf@zXD܇in/{"&chWIp#u=\V"͒]Me`P}i%](3C0g:t>*27Zʽ#iGqW0VdPK~Vb  +pkg_resources/_vendor/packaging/_elffile.pyVn8}WꋴUv\&X,5ZD%J "n!%R$]>ؤ4sh87ux$R feǠ%QB"(yD.  *4] x*.4HF# :Cy^bR$kD"d'y'>' &ֹw:KtzU,(K;Z8;l{O0~ߏGwt8 ("cWhLS9D*L(XVj0$ >xӉVJpq{Rͤ& tf,"0Ds.z3 en$ 7~8D iRNp{x8+th^MO>3j}MF+.`ޤ8B=\͞=})rU>Ȟכ)W%( {3$c v#9\k ۧ5#rʎYF(3ÜC-; 0'FRڄvG\+w䛻\^BR~n!l`ؤ*p#5.yGxVl:vU2Pv{J>"ӈ5r(f&'L$L±4Ҍx (M Q -u¨9z4|ޒW({JwI"S1>3#<5UlXAByY^7 ]L<ĬS7 IP@[>9Y&ыx{zu~WWW5~WvUx(7FgjxG/=LFgs/XB޶H3d Q'mx >0sAoJϤ@UPsS b ^el>V|N޴ÊVb VZw~ZWh8We883 v3=̌x4z{A.iwg&*22WB!sԆ LoPK~V"r&` "-pkg_resources/_vendor/packaging/_manylinux.pyYis6_afR$_I/Rtm5M,, Q$BڒիE$g՛yEF~EV'2YyƬXe.{+"]v^TO<MEe#[E4HTh'W^pI8}ǃLp?~??z~_/Ϧ7ߘ-ÖC+^,^|`IZ0ğ wʤZ4akB$Y,U+9EJmdz1OB[3?$=Em߈D&;b`|>g Dƀp`ɓPLTU~.{,E=,.(@ob4ۑє8I)h7p5,LqSxڷz\e,2XCc-QAEUeڥm,0Sw#FeDя CCf~<,3Xc u%Z054C"Ot S05/Ӗf8P3WdcH@$btՕ)5!p;xt:.EDayX}*1 $"{0~.e.XZa)e^е_S H:XBuD pRuh+(\A 2hfq{ !HZ$xdB~IJJBg[NfLP{$+mY"h$hLxy%ZMԑ5 IPEx(M5p`,A%= ?h@-O*C!ʵ̋$jK"cOVDFdt;bXem&" dA" o!md Zp")}s3pڵަ{oV,hRo 짒 ѭU5h+~#j{BDk՜Q(GL-}-GiV/3iqMTH6]VylUĽSA #R7=#K8\R"n> Tyoh*\9&*T=3u"Ύ+lUlS]@H x)IZ!(N`q * aXFAL,l@ACE6l5́1^K9(URg~6zl*9g+j]fӟ]bg3y3ص|ۻ^u =po?~EUmöǭCzk̃6Y0j=}@"-٧-=b!Yqþ28 g8t{,Xi(/C0p㟈%]1w\x{3SŰis(^^ɮzӌ;^CDhoVʐ]Si)iUeꓥ@UMQ/U(GWz i6έK?7y)*\AC\?d 8 = m:Siv|WKvrgX8Ȱ;:Zf ޤae-F ӴI}Hd篐9/"{5ۺoO~_>=xY~| d# @ׯ dXrt@v,I d2A-d@Lp-A_cefC`-ig#FD!$ fNʌ $T#zY?B,M4 Ma ;5 |sLG`)Z-Aæ=CZlrBg߷R9h^׳#vہkwj|SSUUŝڽ@^/Fr_|郏o^?hg`3>Iy=2.nUlTвvI36 Ep_M҇UATaIO,BICܮ(V&pr:yfq0GQ5u ޷-![Oknkm.f'Kne5[,Pt?SHo4ۺd0nyX[x׋ZiQB3[,~+ :88*>}?ǣˋ{GZ 0T.}=cm`.ќ;+bp=܌"`ŚN~#4):ؘ^6Flv`~`9PK~V} -pkg_resources/_vendor/packaging/_musllinux.pyUmo6_q>Llb `0a6@~q ( Ej$(wG[H {4Moo+p]YT VFh@,Y? (;k^vZK}ۓ? %JQ?psj\Wt:Ή%i& XtQnX1nZS 7޹Kjk7^X]ވÄ蕫$B1aҺ~$)w?01l2_4vRxz8&I%j(Zn((O83o;m_ƈ6sWh(a6Z2PUY>ɢMZ%} )pp N՛-|L_Y;"&qXcf>P{颷z`_ڟ*f ;XӵE/buėyc”튒G/EyWa6~r|2lh._R@:D׊R2E~{ҼA{XAMƚ hH^ K @tXCPFs]|'z'ө i?7%j} +/!{骸zW2.%on9X~(qxz{$L+[@jiA=}R!:{fu%¶ޅx.E!Z wV{:NB$DHo$^WqM!d gw7 贤qUh88p!+,* Cq['3r Mbλg ](M"}_adq@i3EEמP׻]Wvc9|^`.mcHw"]C_fNbbD⧼SCjnG ͊s^6vq\Vc&d:[|;,[_?7]e@f8R@%Z"ܰ _g8I1ˤC3k%zz'ָeЊ" Y4=Y^]C+f2b#HVSRoib00 \{a(CJLaooOG8͛P0"!6G csuzsVTN0n{RK]fh0#tк,Wj('#B tSd_zzw)^`r=B4Gl4d,'<7s9/mgw||2gms^c[Ž[*9ǍՌx1TVnA+Lmioȗb֓|ziXzFH+Rey^}0f] ' w԰7Kti X# *:0mLX^u%"4o Rrb-Np&nexKcn"3Uާi( XG$_g_b̓d27DA8G|c9kiHrmXUAeV {m, -THtش277AɡB(S9JmI Z" %C~ r2b6F5A 󶌤KD_'WԢl@UBML^Hhm-Y`un)+pđZ;1ofP2\)٬fhB]F]AȈ/u@IS(' (Qmsnns_NմݿC],|q)g~@j<Jiu3D.d'<̠VrгWX+ l=6]1=O߯Awo*+:݅~~_N2]26dRJj $ *3JA~ni `n^ Ԁ3lY,FdeA g?}vЖ##6g|luj]:ԯ(1y`g ⁲{\-4m7;CctI@K H^ekUZ iۧI+ $c- ˎʡQPf"R|0磹ծ<2i Z@1֪#{@~0 ۮq L2|.(>u/[-Î3?c{GS99l2. W /3ֽ/_ѧ|4p1Ez=Bl^G_'(MG(eL?oF(S*umgmcM0 ^m\f<=d83N8._SZgs:U6ln0&Կ$WUjd*gVۆ*6u`e!/B'Sgr&-tcڅrZAh 84MՕ-)->9X|]N.\ʐqغeYַ^ŔEjUkw ~.0;$1T⿢\1qopvK~%߇}ntN77F4cjަcKzMd]a8Z*4{.[8phښNHv#NWދd<b hs^)BGt i̽-Y2u% ^ jl;/M'ۿ:=.^}i߷)☇҃U󨿺(ZR= /fS(mdͳv-?yj5a<U;>ӗ5@`QtVɯT8cA.,,4s@f2]ȋ)3j:zk%4H?%ྯ3]@C;J >yB'Ӵ{"?u6\.:>߁/P$@JQ#PK~V0`.pkg_resources/_vendor/packaging/_structures.py͓=k0wYpLh)BB϶"ҹ_YN?dt HQY̠V-ܿT&)oogw(kM-a 9s?{=qv{woڻ_ڻ{_tYPl^{rM tkΨ9{M;=f1BvDBq:u!/Jfl 9'M.ν٭6*q ꤬Fdv#Z jg6o_zy=esq8Q*=` S~j1d/vPjԗ5eI 3()m~4z׏nzqo8.Gy`2;k N?wN~Uִ\K%55|g+svՈLǞ) ǥ)#@2!@I2!O66F[ܲih*(GKRf oG{o7M7i{`斴}eC*>j&?QuG^r *7B<>^`bV9M>}ɔ/f/`YI3ݬݎ6"6K=)ب0m`Y9b "Yy-#W*Ō.3sҁ' cw-2;QʤH0|タLrKSI*BNDEb2u$M)k7iiz/(9@wIoE`_HB2x8F1E砫FMnhj p{>L40TwR~7!4hJ680+H+~{^AU4b aiRYkd -JmLW0aϹq%VVFYYWrc }& (=Ŗ,W.h9<)C(YnV*)Gv^%RyA# ohD>LdRkѐ'8T\  c|*XDddhA0;]U Bmu׽Gq{JI.v)hT;pɋz֛'%ר%M?y{ L-x/GJ\`lDe',Bt #ˁW0y<$lժ]\&jV>~?'r0 t-(ƹOA*݃Æ,2( * _{=M1A~Z*_+Q]=xvp(h/36dѲVєJ[%[Gf;.0?V>~=y'j[,@zۈӔը<PK~VM  *pkg_resources/_vendor/packaging/markers.pyY_o6O.%mѢXr hNڇB%THjp>Dje8>9g~3C;ɖ-iÿ́f E1;F S{M־h ~qdK;SK8"ߖ/ƒ@S3/oߜ;;SR4PnV*R}2H . 車%S(wXV/J ~]7my f *k3%.ǾO踔UGx>W.zm~[v}N۞GŭlfV{PMx`(p#/?Xmn7JEXͷ'PWYXѯ 8THkXݳŢ^UEN*ynI AH&O-B!;)b=(+c1HdD}E!N2*=iそO5BykM ,p`2ނM~X\9`BC+B+' :u I-ipQ  It^(!٦N@_7 pSoQglJڡ!yqx_ߤ.9 &y0NYJn>ʗ`Yyw*~0/cɃ[ZݭZ& "Hv2\;y@J?E ?ȘӃ9%cr72O=e Z!9x]$yԖ3LR9-FhRXČ"UXlЃ«:W$\{Yn*&x9K#x7lE`V #ێ!Y M/}{lGPBivqs֗\Yk-bM}m ^C<`4Pa۹IoӆBsX$(e(l^%ȡO"5eO ۿ&]8|u5&(@?[_W seU#&%G?a[ٶUhtȩ2B}W`O0a9A<^xP5p)ko4S֓w/C.?~=1Q̎A㹉9ɏ~rXVf5JW ̰xGO"Aezbafo,b^ul:!W0E3Q0A+RY*xBW :Hc_ȵx)?vN*^֌̀%q\sH Y5ƃJobYF+zEZ a;bf YzfQ| ` &43$oZpj2 x),;.z;AB* @*Q&|mBo.1D^}p,N04<{ڸΖY;<;D_t4ñ#ngo,אH/^`ŕXHv-Ä kp02=h˳. 7Ntݺ;+Ujҕښ~U͞cy&ΥGR3:iyנE^Uvi;]4AO"nM|oNֺ;SB@2U59 {Y⎎ؐ'"H*p3̪uzFClOOO_/IXU>@KBN֚4!n;# ՝a"N%k+&W=LE4ϒ( O ],TVj Z&M_kAk{[XqKSBkl,^%jߤsP[RyCvyŠ'Lz;YMSqR~9d)S|:'.2΍&KH{܇qBI@umHXv&&Sگkm:'`ߩz3~er^w rIZ"5YCd~7濇.#SmJ.}9e2u Ģvn_Rq .M~p\dFNVMÖOQ&d sgSSw_ YYG92mL$Y_.U%le`om[qȂJE4Ċˤw35#G\#1=b{/ذZfwnsd-uc@@adzMVcimQ \P(Z%diQX:i &g fGf0ͬٯy? .?plmP=W[޽|"6)??3x? ~:$ aKNm!J@ik"]q~zquSfnAĊɔ=g-59No.1e^:fGzm;B[ vxoZ4ߒŁgn˓3)"R2fxa;{ pwHH)EERL.;#"\GfgdqS:P)9yͧDLL1 I&XgBR3#6ٟ$wY\z>;WT)QsHQ<3y\\ڥMKU` 2c)`,'PNiZJgQPi#0>R챲ZBLEDgOQB1\me[b\̬Qҏ!b9ǁ;YiqFUVLråPe{Ck6;P3НDZo7SϦ7y F`鍺P:n(QA-;1SC±<f_GՇ%,*MN(2 YtD YCJ9㼅OF.X,Xd>˔H&T6 t@ܻ|iOU3рeg{ "Wz4d@^Z?N^YFDSbG2@PIW ?)NeDo0dNYyzlG$HL:jq}=+gPˍc#@?8_͑il2tkuI魏F?RF9W.ܻx/ILMArpK+"ߝ]C9ۭeCfKo\X&6y¹qS-w,IND[^P[fm&2!7(8 E,VNÛrr@9aJ1'@:dl]yeFN ˫1-mƓ[ȬiSoo<%﹩#r3j0$N 3)CVp.mZ7>qMX9r+l'T-)%ْ b)A)Ac,aR> ]ȱm"4mEqecɞgĒkֈ.}|Ag^S0 }v} BxuE=h-勗Ɨ/ Lt[hP  xC 節Q8~FZK XJDX4*7:Q㑓&mB=B%2<\+_A"8"bl4'76T`DG5344xv'xӇD̦k`wsk^ڑɨ40`,'*r,TƂӝd÷/-?"DF",2L Qn*ZJ=omQsXU,r<0t: n$խfN Mθ)p=Iu4p2]Д*фQEt67_ 0޼?yB'#[HaiÆ^\".eF/>N1w ;t&WՅA8rtTP&g_{vA_F| uD %Kf{5c@ MXtD~$0ȗf1ϓjddqub &Fd`\JjyFZ_jPA!E, 2A@ykF)حNѮ'2*On{ 4p (aƝ9'd=_3u[zF JFZY_1Zf_h7uh6+fd.FeGAR"N4*h&1G6Cy/pq\o™e[¯j)9;(qj< k! QQMȅ*1mzVxvaW RpGOw$rd9GuTA8i- C#M ?İ \ naZGAɷ2o]dXjM_`{h{$R>{$îhVsd';C345HZ Ez ]'L[qMm#1)޸L8DY<5ٝ|[Fȹf3IӘ6>đ)fgU-9bJq7RN"˷^1rGl~mYfڬ+/%+4=O:%,4.|#Fkv2US{v؁q;c FGi.*ɤs2}7&"ˠ;OJ_΍lH껫@l#I#d\sSwH5BG{]MӁwq!lSr%3~<nvDQEd⨼[ G)ř*'gGw#TIe}/$~Yv)+/Ԩiw5m%>\&? RNو0 D|x1e9iŖ C0t)ؽ$U A$y =ko 7:7r_3bA${Kƹ ׊絒N UҔL2{3.PfqI m h}M3:ƒ "_-H5ϩ Tj-{/ WjD2/`ڮֲ:XH*'UQR0;o _Ƭ}G#3r+ư,G{H's_M W@z-lqKٝƺlτ[UC}㢪v1n5J:1UYpeI͈戆D=#p ?҃$|A8BɉjM@ .*ʗTvs&Kj L1ʇu7<=_/W$xbv >C![JvWȌe%EYOd4jd`ޞ n9w?ߞw JYύh(#{+[LT&[D(mU%iWV5}##tO>E M᪱h!R4Į<8˝nk\Z`^تo\cK˞ϵVCL2\*] rl5˟Xr oD)~yT(%Ѽ+8zDQާ:C/KN5lU[+>_> d@XF/ˉj5ʄ5kR0fK)":o.}ؙ n0|) $j}`ET8H>1 ;r,X Fm@1B"N?%VPĊv ȂQp#Z$ $6י ӎWiw TiRXj{Gɡcf6' LK+xq)b9 SK~, 3C_Cm%>Npz@N~';]1 lC&s&>yEyN%NuB$ȿg ŚNǃRAn* ]5}]{3!xq܉G0=9eP#|ZpdžugJPK~V7]3!&-pkg_resources/_vendor/packaging/specifiers.py=ksǑ+`P''JNU>)V] 䚋]dwA~Aɉ$r13/Go*L3EEY:y%"2ETrVE%tLxX?AUZ{ѣ%}[5&^ A{9=+fذR̋*r.2y&jMEYu:85Uu%|0ۋ,f&J󫸚Izt6/:zͯEݏ^IN[RRHbd)39ztN |ooo*.I!ʉƪ+D ѩn@K/2Ҽ|"Ի{<n5~p'ѐش(X$Kvl!eQJ]ry-(k1.hR#s0F2akz r EԈF2;ě21Cԝr462O*#jq!a}f耧>TP4&Y t=Oy@.Q2|I )='JdY:Q"EP??FD'1cRTu],i4[; 뤺Jz=RIDvlbRQ2 Z覥Xȶ ,tٴ,%k`<#+E$gds'37`ֵfO!e^m P @H_9xb iZ }RL-@#b"R7)b4B?ws8:[E"G#ijqZ{F/vqWdn'J g l TOZ ̵b'P5A ,# Ȩ  K,V#O kJ0f}pp7Ʒ4 HYe\irj<(`ɺZh0]ר/Y'm5m%qB”xܠ(z9C tRڳ*+r`oN'٢fks t@82XGdj%ZE,!Hgr6Ӡt%G@aMMܢ. "<קIhi_. i^JتyOԨ/ûݓ){w {{)Sq=`9T?O$U"MTd.j q2ͲiA(KnF/IBxLlW|eqpmHyt9_4^E?>zi)pwqT$_i;:F]3&Ţ$[La9H<&+:\j5~d[P^3ؼ lEa_w~64))HoO /,聪߀f#  /Nļ%|E.Z@@hRd|q7 b77v/b?sbzϟ=}C}:t8wR袔.cPp̤TR9N RxpZpRʨ1ql/$ȧ:kM 5ogG{ނǃ J)ܐ8ZAbWx(r-?m%N+HfxY|HO z# VgyP_mJz3ď/A6A0r8;>o[VZpLZJE˦]ɹmQ.i0iw/{(axC: Cy\(kS$<#WᒨgC-_6yh6LiMtS"gQ<:m@p^Qh=f6_xB:cL<@rꙏujZa1ʶVZ>N6V?Rp^ L!fnZ^A K+H^+Q_8MOt+>BQ:!{(S-db6b1iך0ۥ:B5ދTp`ŚS&"M 3 4[LlB61hJf˄ܽ&WO:;hB?h܉*R<ҪzNn}pCG0u]`\AOөBgƨ)RmW;!W|iqG`Z # x^+:?FJ^RJ'HdS:-샖;Kp!`/,L<8K'rLw?  B٢'d@E>6|x#B&$zX,_k Z0oZ`jAn8>DAkMGX'd4[H2$#Gw((z\jGevq ɴ}﫮HՕdIQbon]lv$(2Dͤ,O]{7tD_pTHȾ\[/6b4^Tz':8*q F%aGF4I"e6 _(i%`L3 ƀ N6ͲK2o2A}tLK櫑5QL+i10"ͬ~Ɔ \5pm'VSRpz!hrQA밹5W$iϢN?-dmPC[! kS=tTaE?[ `jWE.zl25޵|RM^gs]Bj`R\2Gޖ>g;śC,^Fk?]Էm5#Է{lKeevhvUw+7KNB8aWH'H#B%C<k@_*(ͱ^+IL{- FUOM4H0W ͓js;fDi#*:°yf5˱qPho;,fύ!pӷbv]6caL'Cv鑕Sﭠ5ϢǂK(5]gp4*+AᡞFӂ'w lxQֿKI2! pG\ twNz:S]u MM^Sǖ mhAM̙\tdAyΣ~dي|Fwdr q19{M.%HB9j(vjauSJ+eR!>#鵿BO |.Y-퉽*v ,.-r^GnKrxWgSYQ]iyҜ}I~3hۈdׅ;UZ$pn1њkDwph-y=&\SٳT#fZw Ɏ@k}}ZU`[Wi7J~:Ӻڬ%u)!/2GMYLflߵsyׂ"U63X^}h1wŕ3iРƸ B»2PkP7Q]Zzat{\GQvڕM5ݯ!|;5Fnjxns))EFF;Bƞh~؊W˴L;Lqָ0"39clsn6!18]_9>7˥'3ށeFlrW3f;봑&hfp"f[:9mOt'jx·RԬ:dvI fTr;[RE7`_>jħo'B[Z{r^p0C)Si{MwG61< L2?1`Kqc^U| ۂrsVD4<(od7\bmTs h>|\sȧeE-tlT$']@#W&0D3K0k*>_oMhۣs Znu&8 hSq;`C78l23nr 77 zON_WX 8U/B;JwŎ|/Si1j8]TuJDO2`prЮ4xepf5kuܣ'jɍW,ekZMk Y~:.ԼdСa(M .*5a My enj>kuCW]~UNn0yThN?t7gx] J91ˆ.882ٺ.+z+0s[xUa z~kQ ܗF+==^dsg;v{<J27vrKLVTvT{**ʜ`k+\MZn g<kȬru[Aoe]PVQ2eee[A^_Sk))XگQI':tbuP( uaD{יN&kܝ\X)d0vogCuˌ2ceYz68Z\]T(~_G(#弢+ Dѣ I=}m 18z5` i EOezV_XJc3fH>sנr% 隼4 . eq} E/䉝fXYjv(`Ě2/~چۨ†+qWaab%ؗeSv*)IUpIVdb1l,_2Mܲ(um1DMm_pPpUpO{᧚}jcrR;+rgsqћ Xx?NˇG8!j ~罘CPl#``f`h0wDg5{}Ei BolٳXJv_)|A@<+c12`e!)1<%gΤ;EUzh۩e,/n劁~EG|`-q*sP%J'.Hy)wD ^DTзsj|Cd霄lp1&`W9ϸ~CRd94=4?5ͬd.[8,Y+p /@bP }ήE)<*2be\(K4L} !尸+[pPd `"^!VbAеHa19 L̖G^*K= K_D,+d'OJ. TL*P).210;%I9I~ ]xA\-+ A2`Xx`to@ђj30#LL1hlrlf-,mo|Vi5.1gKIF G j0gyy!u'&U}oLߕ۾F]D1D&'Jy1|==|MKx׍!k-Wn=r^Zp0b "vvI|ܶڵkÈcS&woV$ fƅ>_{fSc @ѯ F%<j< :@~ x(ɌzJ~$r'C0J~ƿ`s)TP#j*;+5U+'7upx/)^:V!$ f@ Av "ŀcqA]10L!e0N>0P@h _RDE*⢏q|􂓾Pʩ<ȮՆ^7ʫLaǤZ@s@8̚\Pkiu; j_&bpCYvE_ I{DY܅$wzW_MY4^`i nĊ V0Zwc&َx ze_ѕʾ7B# (hhK%jX z"°%&~jp7=g Z V- ?nqkּV]bCh]SPώ!FN Z&{`IA}s>Se5/K3@538YE~{VpB<1hce 9 [ʼSYdAsm z0ܖ ԉ8tO46~S{A6, qL!l9&z6԰e{ϟ_RiC?_s!ex)hxg_vaJd[U:vTڭ8\{@=qF4AV&}T["O?n1U ?P8"v܈1U&fy nhr_-<>}ߊf+ ]af{|<SSuwbV" U@y@9ӡg^%=/&#ZmP7YҟɝNz5˶rKk53"Y_Io.}SFE^4Lss"隑+!m8g.= #ב"8*C1m`yj^pI}%_7!1@? .@Pe\QяSقSu 5o`cyg0s.ȹ9QĶF:>$g_M\&ǜtb`PRϺDoihli)MAiBV(Rx& ̟QDoABP'Oyt9Pvk5 ֨6ID61E5#J,\d\zQUvҊ `M:{P4/wTs0N$:AҿR>^ D H@;%hh1PG*Fg "]eD 3bqkX ${_Y͌/KfE ;[ؑդS%cw)N*"W48LRHiߠ]S!V|?>ѣ=q) j8hVg hхy; |L nXv̭󡿠}geYh瀞Si ovQ9VU!įB`~|B1Vw8 R3^$ʸ8YGfh5yEV2do S;xH$.`4H=(^$/q?/Yw jj\儂Ƣ(8E,,.74sU*z>0 GMW-S.cOnF55P,2Vٿ 5 d%K`Dv"Y۬z@3"4RxGTvc98|I&hHuCRtrGWޢHڢ#~s6p[]bO:T:PEݾ.6G{AsEb6: MR1kˋԧ0}u!ޣ"DѠFmOeCXɕ+YOOhz-eW%rV"txn;[wn*8F]zJ%Pm5Q d ^ nVe$u4 2Sg[bٙY1j]ٚͶ60 Q_|q߿3ZsSN?{YOЮ /:cӺUc X=9:.e-q!T68@oBlX?\P=77 nR>Y\"Հ}#C(.ףg`mNonL8}zi{k^tH93[H.I,@(y\vu1 uW_bßHL.b?cj9ЩӼ*xg:NN)CÑd[_F-ߩ39}fb ;/m %s[w{kPeӝ`6dUdhĤ.>1I̹mHA6B+2Į{dܲH:`L) _5]_\̜7My:ڮ Q䄏sj.\]c"D -3cZ$|\\恸|YhaYUxpe飡cqb=PCp9AE))gfC`e|`d}M`?jCtc rmڳP{EmD,mieN`.l`?^ϡ-A 40~3C~Fckė]W\W,l7с#$VЄ5pȤ9`YjMH.qۻC6<;!hDQӌf2ƀ{c݃;$k:vZʟ)' I 3: nu _vvM&GQ̤FbO؈3Sg,FS:zoT4tWgw ]s.9CF3ZRqV'"nwsmPK~V*+$(pkg_resources/_vendor/packaging/utils.pyWmo6_qU XjdR 0}I[,iE:leQ#8N#E9Nb3"\;rb!1PZ!(k |aޖIJ_X_QH IIw.ũuX5mSK.bK\@e !C\%DkPےKU3\nK"|)(D*V Ue LĘu\#sV\'9l]λY{~dYA!I%șq&3͒5s;QIDb,H ?u>ua@*I@%)+^!3v"ȘTn 곟RVbCӤK `%C3|ѡS3ٜW\PT+2(4$%S1 {߳C _Q Dd0: SO,4:k 09ߓp(+ShG~ 7jU% qPx\?LmS}8W+c%hR[| W1i԰3$[<1e8&8P V,@TF$XhLv$[`Zr5`$M\P XA'Su/5Ķ{CegCmV oR,wbhq_KP ^!m*Vla.ֈ8vUIZGmӒZ)D/N`2d%p/tbl;Ϭv^7~okjM` fu9*Q8II-à0r8_)r<いA5Q/?4Hd|qsF[p]O뾫Ɯn2~tz>9NӰF9~*{rlR|uoǸjk w5.A71MJ:pHAKM^nV#jf:P{o',ܳ7/ ܈VFȟ]#u,RIs>Gf3K eo.ªPiOiRz_Ng^|Q795F-˜ʴ@Z|N ̱4KeL-Tc .n Y'*]5q꥽ɍ>~8)s {u[xXOPL]ov- Ogc]_4nÖOW^p8rVaRS:N.Jp=|cNx{iE7Ug@;+;"p3`s8pyۧ9-Es|cՓ, emh^l[9-+LiM1 w?S?'PK~V x?*pkg_resources/_vendor/packaging/version.py]s>wFdL]I9y(ǒjFXj xX(߻hdC/,v|(6OR3^I$2%bb!YrX>oV<5ؐ$J!YL`ߟ0!;xٱ0g2 M8bsɢ|JE!X, *A CIJd1|ɀ~df%U. .HguVl$O,qM ^ ^3>%:eZRsZKAU +, 3ZoGb1`B>DT#fzúJ{fҽR'VNF7b313-:tj1HK*& B$7)y/bEa$*QI Eɩ䞶2[)h5`, &_;(gw|osPc4S*Y:GV#^Z,֢d\ }zu,(}^WcBJsC?\kau~/'>c?u53_:i2bSѡ=H1ReAɺ&׻a2HAKdq+6b`Ŕ7!;v}MוG\<f;N_dBCAjFz 92}z.3C!zOW % D ;ˬުa+A~L ޿2f-i6>Q]Ҹ~T _4U.AU6Vt A&nVEn")O>Zd'ͧt\ELpolXu%RɵeɋhђJbTYoڠ &`@j?acX΄΂ !wfWO@|I61Ud\QÐ9/^XjӘo94JZ"}ئG'?|ڛZA)@%,yHb9f6nNj4 UxT@ s8+.ʤo؄0'W*LR$ϬԜlDK3޵tb[Ȍ&P(UTY;SCAm7K4gvCiZxLڑTyCG#H3K(BL}&`/X}E=uqey5p]i EJ~g 'j0MXe? )<Ƀ%-B.ŻE!.*q&tLAA.#;FbNfaƹб]b:=>e~;³t(qjsIW$3WXrJn$x4\F&$ZOAl~ .g*Xׅa&vKHc+()f^_}b;z7D656̎|f2"Aszd TP |jಇS*Sܪ,>!k<͚$!9Hf|r5gil9|HHKcnQRvQ4ឋ"4~͊Q-̦r|z.+ 'v: f6J6LZjHsZ/iLm,/hABs)6 2C xďNDG{Sy~7Eސ̍8&15dcy6Ir)[+. i7z9yYv!z$vynw1,ÿ7 bCAvՏzǴ)VfhKl 6d4tmORQYA/,HUF Ɇ&*h{Ev,ukzIԝ$}K\-R#7<{\O³>rI Eqbt٥ݪAԟ;k  ̐) pQy]FNɤ!x K.oB{P}Uޟ(|I y!讅:.>IEe.zyx;"%>yy;ɘ: zqpvZVio3)8oHbjZM;i~ E%ߢyJbIwK?-O.,/^yv ؗ OWWW+Bd(N Oo朿S${LT%˲%AppRJ9xT)̷+ց-A^nӉ Nu ocshGM}NeS"Lm'ϕ2'$5JR[!I /dnK-xJT%qJMIQ>x@͓F3vh}䵼slKLX"9W=%$ס/e6R|(`DRtG}lvhjIȋbw@CjԎP|-Qs!pY O}VCPVk8ϊGĞNY;HvDn; .h %[e7:7L=N2N^h^/! &݉ ҳÚC{GGLʵv1 ~"8;'[O iN"q)_Z@nDu_`zOls~#0g1`irBlw"խ:ȭ* VzPh ĜK9v[gk=*7XPxiNM];TVw C~ެGtpG C{Xgb9F6,FwC7ZDt˫KkC;Cv=^+=R4M(.,3􀕸"Ί$5ҮQ> oźДؠo.j G)AA%GgkUʉpl+$ya 5ſ[2Orsv?hXO._]Vqt\iQҼ4Ox'= Kbh8!rpPI,+ DpynLC:La[thKBձs9KlMSܫZ c%.)S:nEkyQ [Ea&lM*VPyQA Ю y,HigرǙ6RwFt믕[$yu6I]\טy+HU}2hدT:k6V٭1@j1OL,ľ9gV [ߎBlWWId+OOZ"6VC}fdY;{yv0 {jm#Rp} $X%B= m&7_v"?ԝjV50ԫmH<4PK~V2.pkg_resources/_vendor/platformdirs/__init__.pyZo8~篰ˮnKKF6RTW)]m?` Id 0f|f|>JPI@+QL$)hF8aIY\$+rKBˍAң<̤J&, $|paYpUȂ0D41Y^x1aVTs,7 5>WJ!#\f+Nhr9qV>l`h9goفhC=h~q!/F FUjh8`X q{a{/}0{ B wg &`R!HI&E =Ph_ ɴ;."4-Jѣ_ z:z4`}WChVϵWP>5amPZF§is7rI#[V_R )#l>}p潳#9RŽùb5 +vfN璽݀:RPK~VZ7f.pkg_resources/_vendor/platformdirs/__main__.pyŔOK0C.VPAoW!mhʲwwvE{*oޛi*bc>7F5 ³G0O#qcT۹7;1S2tZfߙKP7,NJBH#$hLQBu ֈUsZ9%. V;nBҪatG*ZɎ}![w`]oa1%fR$ɤr!Yӫ! \:P@~\a+p6MZqk?+F| oDI;ESE`,!h߬PPK~VR"si-pkg_resources/_vendor/platformdirs/android.pyW[o6~ׯ8P^$̓(#12IvCWHDl$R#4.wHIǎE}Q!EIR44IUBW Տ$Gj˜(i!J[(ed$[v]jbFڅԬ).ΙTyD)8,S}^_%Er3 uRZM㘴"IvK% eI/fi[rK4(Q$%uI.1ID:Yh 1CRT}iaIN4IXhY -kD,+.XIU||ZW6ϴgZw`< Ia`j!h't$(s*0odDb]M@ntFR;/bwnm^6פh/E,v`LlvϵnFoFi[:NP/Vv'm[U+Xxy AƔs%"i&S\9zl=EMN5D0L9(,bM<ìDTk\2 VMi^A6:~+K6\UE NƵAEJOBϰquD7VQ0'Y+iM~8cLan0hZtعi3iX\Pݦ.0#zF9Uۡ)}7ZsBgkz2}#ENM<\Cp)t " Z؁y ۹tDhzy-A0A8&„=?'l= LSc7zm~[Ɂf0"E.i]͏KDa8Y؊Uut>~PKJnݬ٢cΠa{_oWhtk}c/ 1\ _;&7yw/PK~V Vp.)pkg_resources/_vendor/platformdirs/api.pyWo6~Wn4Xbb P2-Q;HHILqb0i==>=*V%qVZ8QVJ[`R*ˬPL&7e4fBNŠ87VĖ*m1y!wS Q;sBf .9-^B٦dK ֚$~\DTBnBr͊k'IR0c`6S$AS?[/^pW9ЮP &VA3TPHax~E6f$+0 }`Xԏe^Ób%JR>ue]XAt Pp?ѵeJI3ˁ} h!Ӭ9lC;]a4gDXgP'u #x~/D1[ ЮnS Y#R ,-uZ~ nV|JVҲD3b!R Q5A{N[\VB*l]pB3k?:xD!C^KK)h+ԓ–]S{[H6}8v"5M[¡%rJ &?3 [U83].JMW0JG9z4xYַ+lHZm V@pI:{R`mn)ٺ8D7w+mj#$L ]p[[- ˓3FH2*WY壙nI۷mz%zz1S_ ).G1V}[g!{"ɩ{*)X2hWKF𡡛Zmd;җ#vKۛ:ȑ +؆69)8G YQ-ߎmWL3~{a3xuI4Hݬ{N/tBpm<<cgNߔ"#ߔ-w6¬w,k۴oNA9j?wI[oQe\v&gm=Q2[3oi z d`Ir [`l~}##s*]mf`E9<v]lF|wQJCh =:fX>=:-^M׹R<ͨZG;QgÍCw&o7:;&{]܂l`PK~VK |_ +pkg_resources/_vendor/platformdirs/macos.pyV[k0~8>$ Z:-+d1d>dHrH&Y!~#}.8/:dj@4vI(4}D+Ц^(SZxٗdw:8{\̜6-P nkwZ)L\4B^Buج؇:Ws%IϨ,ufUɝT QoKn4Uk]'TYNFF5D0 ڿݾUV@A$EEFWmYdlR#)eg4ȏ#)[>>W'gx.4Ģ*n֙UAZH ̂Z^]r3MagU < M_dGMep-5{px:g`!3v,Fć{%3)z>m<, m1{_W<俤>#]9W Xd豧{㇜+UŖbOla!8,@uGB"2ڱgvz̑czj{Qg}wCmqF _F?PK~V -\*pkg_resources/_vendor/platformdirs/unix.pyYmOH_2Hu`Ztht@ER$KNpv /~3 uTj$gf;KDQ0 Ţy.!TiR^'uut/Eʼn)Ti*c{WH2~[ |I@s^ΨIp>ò).@~f{G?$dNtH$ d&w"ED$yƨf)gcpǺ#u̔Oz,lس K˻~.> _-svTgr%ⷌ,4/^7ɨR²y殺RUFRe#_'-ኌr6!fzOZb,a>_SX{hF{1o*Z8j(&hצa9 F{nX-E~f2w&a Ge>o*pa_BnlY4!p ^ TUcQb-Dx(s`"B4ҕ ^&hdۚ~;f?5]9s7 kBd+-[mȴpW=xig42[-9gR<$|L"l,ll|%*qSS/F*uv"GǟOw7;^l]F5rh|4]Qk?edrkˢqn*d)|DN2ovm_LryZҫ9 iKdzL!I0DF@Q*d_^_O/#$k )nlTwl8pLmXqM JXx8>ڶZR p 9X׋)tL2y:AzlU8 N:_vDP{&006{;(cՂq;NxX"Oژu;p:튻\nklSmAXͨ&b 9[bX""8.Ս^߮_ {-=߱'g'*pUY=1G3v_R#1Hb`}P[ r7.`iNq=+*08_H}aߢy{ ]$6M2*x&x:Of&v"´xc s#XC `$BS?!7baٱ6d*n֞vkԼJ2;j`7ޛg GRz=Bv"F1s|oˮ}zvL׽(YEx2TQ z7PK~V[є{p-pkg_resources/_vendor/platformdirs/version.pyMA 0E9Ň.ȢeXqFQ,{p-pK .tTkn d尓(7^TUiuD!3|1C^*W?)LPK~V?ߖ-pkg_resources/_vendor/platformdirs/windows.pyY{s۸_UzSFritg>i<'?j9v,SĚxh[w.Rj3xbvD")RQsɂi&&sW[ ,cx哚FbZDR! 'f& }LXn4H,^xQ~ l6G+"8y2ɈDi4ȘjJ8Y[gOP %""};fSY{ƿG}пttast90(K#""€#0Ӕ2@|Gy2j4S!w㵴%3 ,9)fn;,9d|7dC/zf %J`")D- $YZ8]׼k< *gDl耜m_σۛo,;a_4_>cF#Ar|BJCPMI7877gwMGS$P׽9i)*z |&L/1"b-Zyjɫo"}b6qi3LMg`YN6Ȣrp݅pNJ)VB㈎෪d[=j k-Z\Ka ck#Yv&iI* Sk[J[ŚP*ZM~zVzF 嶺ʪ%cb]_^^_մoaqPdQ<1xc{SxPEFZsjnjc7CY+LU[kXkkjsPq0LbKyR {/1|о-Zja}8tC\-F+bgL/t㦱 -Y)ah@7D؂ec+vblUP{4ꋉLb)+|+(6X@I}7Qus yXa2[\_uM-,R[jvp}u2̹ӽcKu^͡:u 60u~?C# U1b3FÃ;ka ?ƤP ٲ|mS la-p >m>x,| p^,LEos |7pP0jx.9rјH:S3GF"x2}>u]Mgc/lyx=': wÜC/p͸i#-`ۍjM,ДNqERzЬNxVz5p9j lXL CfM[m2jׄcT߆1Ubz[L\@0l GC?PK~Vzk pkg_resources/extern/__init__.pyVM6 ϯ b;qX=$^ z)Ak3jdɑ43ͩ lHY uT>Hgbn@N1Ս %⛔Vg|Y8XU~/-<|uFIv;\rO( I~mn J:!W=M9L[5p+z?KghZ' A;{ѝWXڗU^]Iy]w̥ډܚI`\,[`cb뽃4L&ݲ !R)&WA17#xL]-+fm:eH&el2H;_.wkE-T8Pe"Z6"&_:տjtVPK~V)S )$setuptools/__init__.pyZm6_A8Ys& ,rMh.&2-ѶjITIinz7IBgyfHe:oTes]Yhlem&/L4%p.Խ&_afM$/kmnѺF%d+]1yx$*o^<} Fǩjc*<_r ;OuśVH+჉數j-A5mfRQ~p:m7(@I >u2dj-%Y8tl$54ZdyՈ*4 @ٺ5ٗbEYnrcQ1OO ixWy)聹^q뗢trG_赐#bmMq "릯tNVm^dP"W2kn` 7IYUÈf.T"v&_f뼀*vN؆ȋX\o򿏤ZeyiG[Tي f aճ>b]&F 5>^x*P FF"z]"Tt pCYs0vS!JrYe\.!ۨB6El 0טx >j"ͪL3Td#N<LF>U5==UOij Y俪D)vW?'s 1yLp{mvt 0㑜g_|,Vݜ|O/r^X5kѴq$l|4z>^裥Z^nCJhCEŷ6)|q0Jf(ݮ "k{*; %dg,7q6ŋc[b6,:ÚT'Qd䀑F(c͖7N ^ Qڋ4-]UU zXXb0u 38`;4A="$jÑ6n+D^WTVaӷ;Tu&檗$MqJbVZʂH/Tf(() @\x!8LϟjL'U9eUOojړU>9dqNPZ0^A;^u>U]̱.UbCatpC< cUc$tm۹5HTڊ;'~4-Z ru$ek*՜Կ%*؂rN}+nY4 `StnXB}6 DFA]J EB-TwAklѢ_\|߾&[tR=$Ck&P'C̓_UW>` kgPׂL3쌈qyIEe`zDȁc r V٘.d 9L9W3q9԰,Rt޸sVN[n}ejω8B`m r@qiv,Q71B&_SE~z,3:FK6 d%R=`uaz>wlYC.o{ʞ"s%i_t xu/)}QqGG3Φn`KrԺXrԐ?{$kަ{F~؊Ե S"Ә-D '4SX [+pi~͗KKZM=WLE\#CI1 MNICu?losb RP *ْJgi'7/abVJ׭r&U|ȍ9sx01iD$]q]I]P>B(0\8ܤmI{T͝hPSq+TQ^S`D[!lW>o]YuO*WO=vy׃9C# {]Qܜff39Z>4b]hHQptsOAI{(>ś![Pf(wԈLۮgp/77B!O 69xn!O{O k^jE/oQˆOdk:أ]w,.mm@Hٴo _]5 klL6hvS:*M ؙ"rP4]ƟyYAi}MB{jS;t_4$4=b ha[^TsniQw+U@>oy-hwGM1 :L6엑d8=HY*v9>Xr`wLj ucz \G;Ɯ3Sr,ɂtP0ҟo0GI?xA[Y>#ZS9r^\RWr@>Zun >=0)lEӟBHh:~48Gi߿z6Tr4.̼я 溇޵|CFOI{#JDr䏃5*tHϽt‘!-.ơ|y9?23r? `3 w0Ώz $kΟ,GGpc;sp_:(\?6?ў~j5j"~LB |<=)-aYMQEQAe}爾W9tihn6:"jgݗ^#<ˍ]5=Ŏc]z_AM9Cޝd]F-ة?pmJ}羥app|֪(&zz|!]s>EAWO>rłxlL;xyu }S1 nmRU>rky:xYh 9|K'PK~V&lsetuptools/_entry_points.pyUM6WwH.zk-d&ɒtjPNRlސwGќMBӂuU~|2o{!{tQ[Bk=9^5tuiTT2w8ZU jG0<Y{*=dYVC3Usp!l6t% ۈxQ_u%'$ ^dTDbu_SzQ^^K._#;#0Bܘ]Њ'O4Wo\SkO_Q8 Jv'pAuU)FSjJw'k̾SBܓntH)aHsbz ]+ǖ e .[o.GrΰW0B 6!I-"GYn{g$ Tu"l^dMOXߣJBXptLŹɚߐ@.<3O~qS>YlO~9AsAYIr)ׄ\L]ZaVرl7#KRV16tꤴ-*y%ɠAI%kQb>'6 J+=)-r[z~~ƌ;q~$lev1koC/lxlfF(7mQ5xWx;ѩX+q9 7`SÏg,B8 ]7\Nϓ3_{4N.o0>_OuVgOfC%qh.A G9!)qNYI?J)$9M ͧ ڡUdG Έ Y<q;-#XT RƹCvO@V[#hbhSyNIFȨ$05z!qeu&ӂ綄uL)|Y-\H|$K?l8;뷶 i44-b![qhdZI3@mK*S E R "-ZQ{J'Κ[/x4z+W|Sf ZmkeZGC۵z%+Ex)=ՀM_'R;]c2\: aɣ2#tIT6t~3Loi>8s{V"aocBi͠Yӻd<;5t_ ϣw%f7ʢ.}0xIٯlRBh6Ge} O_M)X̑|P`xي'޻FL#PK~VλcDsetuptools/_importlib.pyTKk@W Ρ6)Ph9 -FiGY9ʒjM^}XӴ<$)°jLM4&K^L@l6k˝Cx!wZGS P-i`lNMaLyCUݑ{Ԏ:[@INN?=\=pz#$>zkM}n_|| g".4} =:=,&xݔ`;ձ~Kxz7ʑqoxPK~VTXsetuptools/_itertools.pymQN0 +Lwh+IC|BM])4M$Eӵ+XKlkt謣wX#oo6I+lPZ&b4Fc+MkhU5¡CQ&x8 ,8T@YaMDTddv +8aΰɁ8Cx{Pwp$ɰw5haZO1 %VE˸\36^^ Xct7̨'OBb_ȧ`Wtx,&+kJ¬9oQgPK~V].zsetuptools/_normalization.pyWmo6_qSXl:q5 A4+6U(lDzԋ%v`[>|xwv]yiRTHȅxu"r [nXʼnLe0M#*1DB"dy5w\2d$:S)2(IVzY$Y{aM̛G_d[.szSeH3,$F6#T>*-6kكօ:[Xș1kLʰGM=?{YpyvqN$Hd!Il{ff|s<}~ͺ͏k ׿ZbĘkg;Jۇѩ>v: %s FTtD+1zzzzNj{0aV`طv 8gNU\Ѱe*mhSd$dk@,Jdb5LKmIi~%yI!,46-Ll&SCq,0'tx)ĠNdX0F}dPo?y0!L~ Vi {L3Ϫ<_ if@6+A=Zn.y4 tLPu@\pi^xIG{ 77 䈱;J1^՜ rD `\Ca٢Lȅg~vY(ܴS8;3ԷXMh [EM`C56{3ڠs# guu2p!^55k D U=mPK~Vb) setuptools/_path.pyS0 =,HAJ3Zh qЖ~}(Ujg٘a$833g<{5d~ {w fYv>'7 m ^^vh=lpa‰\AX=䗤 b$2+J%zH25]܄R%Kb|}w˱4DVu;kA3=BH# <86IS R* > bapьg(,AKaHgD L+=U WU8$ BJЍЪz!~!,wSu-Yq 8K8wwȷ0a(Y [*Aܽ~FFbd >Lz2M]bGQBCD74yh˳Wz+Ӆ?T"3>oPwf uizP1e,2 PK~V+rsetuptools/_reqs.pyRj0 +DN . {SJOL:+;߯df2lEl===i 7B~񻉾 J|hޭz݁uGQDA`C* :d@ƻjU68TƲ٣dϽ 2KagF9VOz0Q,wUvНa@`i(P@^sj挈A"Gm <ٽAoVv񚔱}gV}`3@4Kk+3igБ?!15IVtQ.F9"ZwAw=܊j3U(My6V$B &-:I0A{` K4dU`Pe,gŸ`Q 58e%ޓMͽI=rKl9#-\u $+؜L##F|`"c&d@ u$L` cCffF*wfu{.ޞDh!dB#:ƄC&ke78 3%)2щHA™U YBMn*DTQM!iC%BOH*M<R}Hq|XPx8r&@fɭejwۜ1&wj[U PڌcBaDIk~6`L?02et%tj!TX>O/Ӷ*J &BVL|PRWlVEY¾ʧV%zÞ5{6vu<2u>iCt ^2Zl\Q7n>Qo."mI1.+S`*q'z\t@SU|jszoc sG #@xs n=@}M13()d#̷2|`=~Ϡ,}HhQ !N@N Po/S",sPS\ޯn)U5W=nAK|ovIs=ߔ3lWnJ/J՟@mDdPNmLߚ~i! =NA^WtCpY~pv&SG.o8tc(f9ci`q!૫4P &l}=&Jta{R?}V""+#奟{h0: ,hKc[IV6\S\-Z{,oH 7Pq`c]V~k,EFni\*69^܄0Q`;?YD;c!:nRe| [GiXY'7]2ϋ+**(a}Qܬ۴F#!+AT~qg0h+(ByA P<tMP7}F X16F1;ŕd#Eqc[ r2j",w4/'peGY-n!rQ,Ǥ47/k} 64BvK"áv_J=eܪNnf {HB-u ax^ ݕr.cvmLU/Z*ξ$27dht.rQx~ bIǭ!ۛ*$eϲkv:_Ҋ2?vHD( Zbg]x3(o3ӈ+b7x=KT8D\Ү_qH1G1~?,~/>D+)NW%S|)U#`(GGX/['W;}N+,=>[Jۋp޼y^/.vA k`BArf?uE%>*K1 ŖW |v}ϲr;PK~V 3BMsetuptools/build_meta.py[r/H]R Š\B K Nd:0v+Q*ꪂVbGϙd<z'l/T 9Xl gRD+@gQ#&j&L9Oٖ 0`xTpb+^,F K nͺHլEeK&xl߉( .D67 Pt3Z% 49Zd(Kb:OҤ܍h&l6tl6e H1 4*9"k,Ej! 6.phm, e X /Db0iq6|OA||B'K0"c%?.uY+{%d\3k?x7$J-|˥&W`2'wQUe_2M^7/20rf0tk~<1,B /Ycj阽cd.G ^QyaHi`4JH$E(ˢH`e`,_D6Xk.2C`(!;g7Cr3+>@I[~ԈER?A{zl T,y V-gG@QWo__z*x鋋oO߾ueycy [\ ʿ ^h zG΋d B4!`r,vSW}.颦4 jƎd4`59C7C_f~-pndfAԊpNXo;Akx((ҸMU lJ\D Ew’'j `Λ&kcr}5D 38jPze!%\2ё{Enz 8>gݠE̦/`NRog#A"-O2aLF[Cz1!*kR kcfjh"p!ϙ_k Xj"88_&QH2R-/O+AWfnIkUJ3L5c&혂n;$14RwG`8rבsʯ<u`z.O4aT8c6 5i 7 JKW_сoc2&T`]TtI6O~2h4c- BZɳB/|0!w ޠ3ρN:hl l:{i7 0Q 4j]CRQB(h-dR#IeP :fck~>i#p6U| DC?\ѨZk6ùq4L-Hhd6D T j%?bWo9r,^,+r6*# &ٓU9fcMՑD;uW=LpmNξlPี hmH+Z2bj< %0+n("Q1c y&%<~](8SkuD&ɞڐޣO0l=N0󡋈|.6R,^bZZB8cSP}B8ˢAJ$A@&Fg'xA->ȍH'ߎ'PM,Jӌ~TW$*"X |G9M*GmN|S$di-C0M!5{` S'זh^&$ڌ   s6#NBmnf-4K1El6/ m`SaT,Pgu`#7LzR7"ܞM쟌DbG|}I J2i{Mi.zS vp}r{ 'yk2! WS@=xaY>AYA&]`S VGℲ<23i3H1XGl}Zc簤(kp=0"CfӛlF)6)o< Mt?sRwd)qAB%Oճ3d0 |GEWo~c[cC:J3?_aFѺA,=+r8hPȓo 0ߴt1T)p !^ :s*3s<(5C7r\sG'\([Op.7*TdD=kk/mfh,`t0Xʇt݊> x$GYs ?-@)|mnnde1KgpjSdղ5?zrLȍ}-fIp?lum8GS0|V4B!T4u)WE_ yȉdZ4>;MJ O|ڔ8(.EI ~mH/IYbg[G{kpX_쨴"&}¨`FPk'B ئȷɢQ2xK\?_"rG*܉gcR c`iUR8T0KӞSl<*,_zs^嫗'/jsWHaEDX+Fp)!!2#/H;A,*>7*>;;$_dS%%d׌ND_tӢLIF#&C펵DԹ_1{hE"l68 '9V]> SwA!SHǍZe' lQ=2-7ӳqI ksgt{5 $(9^PM.rM1qƋIEuu'@ɫTu0r.;!TmtP< Ʉj5DqR`a5JwUGR*Pё4Ku[w*0 5O0̡e9 7㌐qga $dNJ?3|ɆIDc1Ks`l,N~P5a25mpm[yPo8M]]P^Ħ%l>U'v[7~*=O'iH5_:PkL;"5Zqe7i3:yA44,~OҺ@q-Ȱي^0k}+29X[Aia۷Įؘx#\Tt*WpBk駪Z@l̺9f\wK@~SDn8UsWvш}uN=爇u1݊\eG5'+X@N]N/r7ߜ:Vu-1{/;5cU*S.zJSU#t,W~ql+A W*'05L=,3{}n9RjvDKY Y{5.jěإjl4.}<"7ܡ^܄q oO>;w!Mmjj+AЄS? >&2r*V]:FSμ\o&IG9v&)\zL`ʘw:>zd ?%yT?O5?Pu'W:dBt%|/rs05%UU5.yokcAwjy[ˬ_=s?AB͌cJFk<͒؟:'3 fYu/{Ƭ6kCW.-mQo,v C-ݭZMy*I(/vꟓGt3c{Im@`Kg-_}[o~}hcOjà.>Ѱ)(KKw9y=>7#ww$j7'8ĝ-^hpqzd9*fpumn})[;fpW#w {#n45MC> b _)K!q*YUמft7Ws'h]>jlQ-phoq^&*;~=[ͮ+{T[_g:bvTU{.rND-(P4|ؼC+wC1C}>)p_0f>con:jNHJCŋ؛'d 5( ML~ +z%6Wj˒*PUZEX (VnV7mXVr:,azxjKZRJHDI(H@xhMM'ͅ7D| v.Q{NeJ%>a tIhsOYgc.y53)aYj)xF Xb2Տ6^$F[`2(:haa}5EV-bVEuTo؃zS5:H,[x׺Kxo BĜ;m*Q/i8d 6*yAɳxcpPCQ:GBpc[`O.HF8vy7{W@~ -`#@"ؗ4$PK~V5RX.setuptools/cli-32.exe:xSU7MRBiIVAXZ$m jBH_4Kذ!k3#8VtWg?gFf?Sq`|v֪=羗:wϹsϹsϽ/w$RB J2IH3_I^~zAdNW@*\^՚;T&$''PY5S%{/l ?bSo~gq0ߥccM)=#i{D`^"'-r6h$C2C2\ڃBQz!&m1>ʈxѧ`!D@d o'dg4{ Wj& Y0<)a,|qB_wQdPnz%9(hk| Z"߭S3CX;ڀ-ߪo^ߟotVIb`Q9f3nbMͤ ]5WYrn f' $?Z;9Kq@6˸%(];tqaPIѽ:B1P чQC:" Ŏ_:U-}- SUF.֞ZT|-G?6n\5(_T[StY$ˁ%W 9J|,϶x C_B }0AI42 `6WG#8NW1Pln{K#F_iIwL[QK2GW9=Ы8̈q[|-3}uS9TA)R J,9tyyPړ;ܑ })1/c|$φO^HĽ*2lQ5!Cg!y{(;:+8Ԕ+np[ЬGA?"&T%7it2ݸK& '2{܈Kֆs4r1§ -Ls"n$AʅE.Oj7KaUgܳM~bɏaRrb,!bUI+\^ƾ'{Y"r{o#V'!Ia~#N[ ý2ѵ൴[,GQq2ޓKz!+kNYAE+#&¸evHQ47_ve~W}~;>,Q7Tr*eݿf%֘<ܫ;L_:AzA$ޚ0I$>a3ਲ਼X]b:$濔ҽm QT.4+t`0o v #`G!+8wrv} u/Dz'P=/Ag@>8:sArΘ݌Psc` F1j %TǺhFS 0KX;ׂGp O8!W!rCz87-'΁wђdy;/r+[p/1ɳZ\5׉\ 5o/| Hj6_IwA9!c\ߓ z\0'H2o:&8? 3BOjeilvO?zqxj 7@G,.k 5PqNx2߬7# z,QetgrwӷSU5(a7!"4el~(eݤH.h1x r58 9VH1ۑ pۀUylz*8^y!b-totfB[+^> Ahi ^>rb7p5Fi?#={GH?3erنfU lC?;|rlcm|CxnV9f䬲! so(:B{DߖxA-z1#_q3A&53B#My$Q!V.|W$Lp/tLJg=6w Mv&>DAhz S-{Oޚp0IV!p;w B_[;ua ?1Cs}v[7 tow XA24 sظP7׍Dd'0 /2Ɲ@O<jОlx%,(dh߰ m;8C3[K6ϋ_:Ȣ8#Eaf7YFF_RJ2 L:42iB 74~ǟ/?ކ[C o:f5+gZBsgEKآ4% WkDБpn%JDŽ^rOpm-r"nw{~ʀ/g2xHt=]dzK]zzjoqK'  cwjD4Gx4ncOQ;LQHEO{(Б#GyTw_#|Ї&pF+/u}ij1g2JgG*Z3ܟWFB(P꡸CyDa({8lRE#!gI.r/hP)'GV X&P UPA9 mPB]e\J.{< #PG@{B9>z^>N.Ӥb}* ~ s0@IVz_ ê.V51f5oqjcKvZkcЕE- PvNɠnmRYi(aA־Q zTȆ]t <>)z&>VH=xZLx-oҡ5OIWK}Ûv-Y8#* jHfPg"(e@q@?f4)A ٙR+|g [&~+qᇳBz~ž\V;[),>ɕX_iu,DCⲉ utn ehnHhV*oD嫙@|'(h/D+va`H}Uzösѧ[4}J(HLMNES74# Kmi-*M|Cۓq  f!RP@{'%;o>oYh-$inBx@^6N_(ء*L]-af,ـ#,p*k|*J  _)衪P4Pg QpM ='u\ Yafe6*A k]m )A  0V22A]-A?c~>.u=ƻoRoJlpIqMR&Ȁ uC6zaCz gZ\\kG@/,<6^Vɠ4eQEKШwń5T(VN/'im]awd~_qѰX/d`uĆ<ڛ=h[ ۊ=ťb4N3]0kq^ʤLAw~ÖkVkqt ;ɒ4 99aY-$TzzQ^xzZ. 6|j]~&*d뀬_QiH@>'T0b쓮]5 GaFkƟW*>~~:5K%^SGBPB)ex =SrgGLE%"^H꫋ˊTi**+r@i٥ހ( 8L=Pr}_3[ )z=Z^ZmjflC6RY06Di iڅt @3A da0n }gʃTdeEʁTl"J s2._myn6.wSMQ]G}\`gK*i5iNhМ֜ȴjm֭Egt:O]B߭ŊWmmmKʬee칲e-(ו,,WԮYYrʝ+#+__yixsC]o ? .qq1X`\d\j ƵF.=F3ag b~n:az闦wLM E UŭT<^[~ŜʢJu2VWP;`" :[=[XzVRCߕtJl [5Um'4OEMPWOړoh?׎it:v]]G#'u׽{Itҝ}PnDnLWg7U}BF]}^xKJue2B^clҼ\ԏVYqM/?PK~V8setuptools/cli-64.exe; tՕo,ɑQb|DNKJ; v "q> &"c,($4e*Qڦ-KԄR>'8RecpRNI/PP̧-ҳ{23Ѹ]DC­(tv8B:0<\Qx26 )~S LmBi^^Yev {X*\1fu In^ n(~Ϟo|b-޴_J2ɛŭ)Mr2ƛITf|VZ $S퓂djL4i[S |>HK y BPP!4!쳘35֔/" #u@`[tr$ƓL&Qh'FxJ++`(!M*ݢ v**- K[uV<^|TIG_x7FHGSM|`0-5s#0VY ,t&^:!XQZk&K%bQnZ'aF7|gG|ldownfK;RCgA]xhnU҇i.+0< #!N0<}+I8"Yn~Zt O?][ưV<K׹p5$[* ߡa6If#!XR{ "$\1=6>&c+[LT)t{LLH%1t9xlhvV:7&/i5̶==hX^TUMګP/nb|~&X理||סܹΰ/ =xy착ҹGkn^:,_!׼f;i3}ЁW@˅.Ӻ5vIp46 ңMp:I5k |as9|)~ttӗc&H55nwFW[4FԘK"0pGb=&S C_^eqm3aV 8M7ݛ&GKEm"^~ ˈRZvʿ:.|[@cua(}`z>];Qn[Igg9͞uk5%~#:hgrqH/8bO-Ps&yx د"^u%dha~{9-"oۻ0ӑ_B|j3;ϓt3qPXG~9:^&*nO !^*?1웴8Na~4M~,*ASvh- ө*a:^ę-56Ph{@!b;fƅZcط]gM=r1]pj%fmW3cV̡>)o tHY@A<_-%:ݯ.)d  ޅU, xn}.f7d( sHI%3(y^Qk2N`T{lC߉kQ.^k^@]0k|LC9.^`e9r+m7!o=I GǣZ<𼁢vy6s髵۲ka_|5:@t*PcNsAx@c+?bQ+ S#}f}A^-= 8yB`Б`:f'Qo,Jـ1Uk[Vs3txqe0st[f7.dS:(tDز =Re (v];. w|;[v]'M>zjjF2JwWWxObcAJ4*T y@wT̈?K 9k7r/DH $Ԛul}ְiMnE˭M]|<3&FN0~܏}~cT\ԘyH4UaAQ%٘M;8k6츱fC䴢 ^+:@u#1%)HXQ#L3Ȥ jpK[EEb^b!HKqxYAv8{k-Kc/994FL>JR \z 3>O$jKr^zN:*1mU-7:>iuHGIvɦ8g;4Ga:$&æY/z5 b4с!a1}7Ԉei2Gp>ݝr>%,Ҷ sl<rˇ|\ͅubB_)w͡t;ކlq+3~3rɫ=啉P_gґeMѴ~%S(ODa,t`hPaNvn! lw9ΰ}I~ݿW<ٸ{ 6l:.'H} }}<l)'1ǐr"@姊aNHx "0h'#rF]ɳ!ΎT 9 ^N|(0e^ sX'OƜN~e@񯌢2.䆞߈Y2:.V ]KN?Us'ҜC1>)Z=% :2wqZa՝*?IhnX{9_Rt,`9Lw"chA[V[b{tk\j oN?a-1 /XHԚRHOqΡ&'4MO@, |X+h4p[qkrY,J FI1nJOTȳH:?GW$v^q51;A:2rg r'`[tMrPA9amYbpU}P|gga^e>| oviVJ [Oqye4 bhRWϺKj)^ 'S=v=I]7V\ UAVhV® $_}č3 ;dG6\WyT,u@n--k 83%Yǘw7 vA^d6yB~OkƜ8[tA8Rv󄨅T>WƠfԀmG} K6qrX|ϓ-5 5gR<{ㅉ3Ǖ^SlսX =Y}wdXB>aL%HTcC5)//`u3k 1wx+C߯ 8́uD }#ɟR"喤5&PC{Ҋ޳۶y;AVgNۏGf H߀aۻq҃2h<'2c9q"baUh$JrqYbi ,@#rb(H{׽48ڱ5#= j Kg,ISHH' w'ivB2i68W% C4Iu4CAvnA/=|Uתid1!)7c,1ܓZh4/?"6ҘC mP@xzi)́󾷢g36'qb}B+Ic \ϥױr^e`?[=(S13Ma'xr+9Kh1}52ȷ&}N8Z_ٷ Eg/H!k=^:޿FcZsmQࣕ tnf>W`]_!D&GĒeMD,Z@0M%ztLcIIv#{~q{ ]$ʡڠH *RpPvmN7 Zpn ۼkby7BpɆw O،Ŭ0C!`ݯ~<.0M0N.MJq@N##(O'Mbyԣ#Tuy#M͜5yj { Zmc<+|WcyMGOVeYJ|>O_s>o iDA鳙>O,|~&鳇>^USnWa G^cHbDGoPFM6׍gFg^=16ި^.~L?‡TU{Zp*~ *,SfUΫcOPU~^Q,*|x>Pdi>*b?;`S110uՌ?X81hfn(!2} ^ioL-tڹ!xyހXWɜ"S"KB]lr&x¢{O=U~a_ I%dr2w\z,SRWdXת€ 70[|2Wj?F_=#d\僋, ?!7lB}\7^{7bMqO未on#ʩpfem^ԋϛ~e#iH㫀 u42C佣q7!Q:18Dp5G%F᜔_hktG(vH#I͛}b: a:wѼLCUAcfC!p7Q9t\< "Z߉_LY>PqV:~1{0}nu=䏥B,5o4Rk7MTPcjqcMђ)+'&4[idE5ɴqdڢL2l73sLHf6ddfٴ) N-]II4MN $Eř7&EC"#=DL)`+zz.#832,YLbh"˰2'w a#aM\G6@]% ߒサP,<\ ӒE$oe3qCĶl-fe8#܋t01'u#ụ`|+Gp+`ONNgì!T. ti2zL~]гpgJu/e~ˮ/[1RM6m sm-ߌmk^qE{p׿'ܰV#uأj2,$}؅ta[[%@O'TA$kiHu+uXJ}>XMh "0.W+<ժ"A6{mú<~ꕫo]L%~7ܷ 0|^Vx\.;уkPE}% `BbM-/@6% A-@J7 =wjD!D}9bD;p[B΂ 6PY1;ax7oWun&lѹ(!.8f.Fȕ#e@V<ɂhڂ_2bF(1e6zC8:3ra.q,mp{nH)|Q_EqlQQcnSPa{Dڂ3Ii&HeZ HH&O= Ud%q7'y|!x緅.[w`oU3,mG EoN2:綃m<Eάy4ڕ# +T Z:hY aoBn^ADL9l.8UC*5fs8= u3(BpS:{`0Yu4luV@l `WYni0nb *#޴467n)M,E/I]b:򢢤~w4%,e'zq|C ;g|v Xx@.sJ;'!eЧGί`yMPX:~Ćr hwy{8CUU~n#V fT0r71ExLȵ9̚s|C- p W0E84CuM bSk Ŧ#B\ZJՂQ:U-mAnP"Sna+\t|soソdmgh=_PK~VN@6setuptools/cli-arm64.exe[ XՕ>L 3`ȏ2$\&LShSu 103 iH:f [51[`bJb냦h-il&t=|CHvp=s=ܙ/7ݶD4 h>]tj3ʍ4wڜmlv]z[I+3'%Wى[w[]P]ٓ?!o *WG<w: D~bU@ԗLs5p㴔RMR:W4iimQ28}Ҙx4XFnyF"b K\5U1ASuL/ j;RiM4'ͣQH'wG- [QytE>&ut3N6I3i-^n#+=–zU6žd5)2hjǝDzMsO#0/t6[>9'w)K&Uk\/m)Lj$NWLzwLظɤӕ3~қD]D׾igS|yJz8?b,G8Hn\(#e6]Gzb}9qw4oٳzc>4SFA$E:~œ7b7jF70Us4 Mc17 ܐz`٩htEI?FV{ ^Ua LMne>5J +J%ܜYorKw YF+009>c,a rp?&j_?z:A/O gbw' osS0B #52=oL>JؤLSv2/l3]F h3±ΊkDQ#J}iXoAY+<c|p`a306l(qsw!5Ls?F3n\أK1؃s]'yΗs5Qǰ9S X~E[z^ ;M苋z3r?lnSFQ#' '璥 3m.I䙺)>Z>֧з*cW,{XO#4p;>@mȃ rKѨ:S?o[ o_R4+1W<ʲ.>&S G:FTD9<oN7)t xyĶed> ٭c=O]xrgJE|=[s.mHUi qz uJ$ܰF^16,}rlcY?Gm|v݁uY̱_T#ݗOvσХe,w%D\hl)??'Ossr~}M T3nY> '8~O| vŶNB 'lȦfco8?F6QicLdܣ?ˑqq'xT8o?kZZ}aC> YFa]q.A_Blt4?Mģ5y6h 8`^!O4L{ oy?M|sļP%Kg\b]#tzoRzt¼ IQFcJՍ֍/IƧUз*|LNss9*qv#YB}C6fVcQU#[;WG S,~zF+ROr}yqj:55GV{领7{袼%ys~3ϜW_+g+P#$ϝOV}|,~~gt~k3Ryc&jnNk5bQBp1z̢({)޸j(矹,{"ƃi¨P],@;h 5e X <6s?7!>!NkO0&2jB迆j슉XJ6'hg߇n3ءyXϺ'uN\TQ9r\WpsIJkxz)З!X7cF79IQK r~kq/X/υ;FBg __!ߒʹaP0rR#(B48MlOubt11ıʇoWνaszkb.i-&*RՋL:䮣2{0ALl5s!x.3볢Frlt#rj}xfrSȣ/\c~+?}zz+8~.3c܂ʞ<0ı2=OM/#guq{){x$x;د?O㋐jQwqL6|c&bP=3qnJV#ɴ9[8}~^&~ N\ k/5NG8^IF0^(qfZX;0 iϩ,ȋ-@9enܪp>1!~-|JUr QBE'f13>gVr<oQs>2"Naܿ֩o$9~#"_COC^e=qD!CWM;ka[ϔ= ){(s.^~?) gXk=rv/UzLސm6'Vܽ fQH+XK,3:lKڿǔbO\.`Ƒ99|8]sTƳ.Si3,e:#r:}0li96b߃Z7m#|dە گ/; 7  Cnh?dh?lh͆/ 2 ?6OjhTP[Q_>QsM?ObDnH ZbpNX=yXq 0K" 7JxàJxOJ降||>,aMğX?x? ൉%OIj $('0Y?'J wd! pb֓H\ xrKۧcRgğ~ْ~ټXg]`=wd zmNjkS RoՂ6m;hsz4[j] OawE}~קy0Brt_Gi;4]d#XLs[Z41S4٘yEiZ]Mv/<ڙYXy]=6)wAosōrA-0a?C1{z]-جSQqy+HVzr7WU0M4#DmLWsky DJ ~Dۑkt+'cT?wΝM[򺟕Ag.2pq :]( PC_ߗ S qw<_>6&MֈX>j-}k7*[w3X_\HM{Ka't y:cvh[&KY_`胗eT5l3vup!\Cp\w'pih5q 30wq2œapKJ?g?8Uh#ʠ rׂGK#ѪUvT;Y 4+D~>Xfvf8~W̫nG{Ԑ{=dGdgmFWۨ. F'HGKXGv`.XPՎX&AK'Uwak+nO!4PFߴqxX t@֋Q=Awf$F![b/F!XFm6kZEzy%g*}RDk l b^Xfpgrqg5{㛜nyiC,'l󗞤@t g&ϫ_o~3}o22ukSވe z4 S\.\4# +nsqbGx>j," @A~&>+•}G倍2={~ }EQ}6 Po&ԳT_xC=ߘKV;F8OBeǾ_qoߜOf[q7&'3$wضkr󺼒ʈxѧ`!D@d o'dg4{ Wj& Y0<)a,|qB_wQdPnz%9(hk| Z"߭S3CX;ڀ-ߪo^ߟotVIb`Q9f3nbMͤ ]5WYrn f' $?Z;9Kq@6˸%(];tqaPIѽ:B1P чQC:" Ŏ_:U-}- SUF.֞ZT|-G?6n\5(_T[StY$ˁ%W 9J|,϶x C_B }0AI42 `6WG#8NW1Pln{K#F_iIwL[QK2GW9=Ы8̈q[|-3}uS9TA)R J,9tyyPړ;ܑ })1/c|$φO^HĽ*2lQ5!Cg!y{(;:+8Ԕ+np[ЬGA?"&T%7it2ݸK& '2{܈Kֆs4r1§ -Ls"n$AʅE.Oj7KaUgܳM~bɏaRrb,!bUI+\^ƾ'{Y"r{o#V'!Ia~#N[ ý2ѵ൴[,GQq2ޓKz!+kNYAE+#&¸evHQ47_ve~W}~;>,Q7Tr*eݿf%֘<ܫ;L_:AzA$ޚ0I$>a3ਲ਼X]b:$濔ҽm QT.4+t`0o v #`G!+8wrv} u/Dz'P=/Ag@>8:sArΘ݌Psc` F1j %TǺhFS 0KX;ׂGp O8!W!rCz87-'΁wђdy;/r+[p/1ɳZ\5׉\ 5o/| Hj6_IwA9!c\ߓ z\0'H2o:&8? 3BOjeilvO?zqxj 7@G,.k 5PqNx2߬7# z,QetgrwӷSU5(a7!"4el~(eݤH.h1x r58 9VH1ۑ pۀUylz*8^y!b-totfB[+^> Ahi ^>rb7p5Fi?#={GH?3erنfU lC?;|rlcm|CxnV9f䬲! so(:B{DߖxA-z1#_q3A&53B#My$Q!V.|W$Lp/tLJg=6w Mv&>DAhz S-{Oޚp0IV!p;w B_[;ua ?1Cs}v[7 tow XA24 sظP7׍Dd'0 /2Ɲ@O<jОlx%,(dh߰ m;8C3[K6ϋ_:Ȣ8#Eaf7YFF_RJ2 L:42iB 74~ǟ/?ކ[C o:f5+gZBsgEKآ4% WkDБpn%JDŽ^rOpm-r"nw{~ʀ/g2xHt=]dzK]zzjoqK'  cwjD4Gx4ncOQ;LQHEO{(Б#GyTw_#|Ї&pF+/u}ij1g2JgG*Z3ܟWFB(P꡸CyDa({8lRE#!gI.r/hP)'GV X&P UPA9 mPB]e\J.{< #PG@{B9>z^>N.Ӥb}* ~ s0@IVz_ ê.V51f5oqjcKvZkcЕE- PvNɠnmRYi(aA־Q zTȆ]t <>)z&>VH=xZLx-oҡ5OIWK}Ûv-Y8#* jHfPg"(e@q@?f4)A ٙR+|g [&~+qᇳBz~ž\V;[),>ɕX_iu,DCⲉ utn ehnHhV*oD嫙@|'(h/D+va`H}Uzösѧ[4}J(HLMNES74# Kmi-*M|Cۓq  f!RP@{'%;o>oYh-$inBx@^6N_(ء*L]-af,ـ#,p*k|*J  _)衪P4Pg QpM ='u\ Yafe6*A k]m )A  0V22A]-A?c~>.u=ƻoRoJlpIqMR&Ȁ uC6zaCz gZ\\kG@/,<6^Vɠ4eQEKШwń5T(VN/'im]awd~_qѰX/d`uĆ<ڛ=h[ ۊ=ťb4N3]0kq^ʤLAw~ÖkVkqt ;ɒ4 99aY-$TzzQ^xzZ. 6|j]~&*d뀬_QiH@>'T0b쓮]5 GaFkƟW*>~~:5K%^SGBPB)ex =SrgGLE%"^H꫋ˊTi**+r@i٥ހ( 8L=Pr}_3[ )z=Z^ZmjflC6RY06Di iڅt @3A da0n }gʃTdeEʁTl"J s2._myn6.wSMQ]G}\`gK*i5iNhМ֜ȴjm֭Egt:O]B߭ŊWmmmKʬee칲e-(ו,,WԮYYrʝ+#+__yixsC]o ? .qq1X`\d\j ƵF.=F3ag b~n:az闦wLM E UŭT<^[~ŜʢJu2VWP;`" :[=[XzVRCߕtJl [5Um'4OEMPWOړoh?׎it:v]]G#'u׽{Itҝ}PnDnLWg7U}BF]}^xKJue2B^clҼ\ԏVYqM/?PK~V+JΟsetuptools/dep_util.pyRn0 eyz =TU4klkl7& d+!a~lg1(۞1@rCg6BaR=ԳŤ/Ywanԅġo;ş(`46LĂ5d6DeM[48{#_1䑂o~U n{CNrgq)2jMZ2# #d/f0%2w X$[M!< y;P= yJJ2,G`3yh'j辮yə0͖ݡ՟cc{7;) NŨtЇ|&^lXW?qF0z;jn/(xAƬGYSOe#ѷSD"j,Mge5!`MxŒf@-{Qư[v0!T}?Pj;V";T_A/hބ qKHu=TT`l$2Bs gN8!Eȹ&-u0'!#ثί Q}HlZ@+!5lЗ{,Ϭlf>grJrjpT=9gZԆƪ}yēWQpR4}Zfހg(pPb,d.ؘ^e/ #j"}4 oY/]%po鶀̲I=t_?[*g6ܳ嬩vXx,jY8X0JyM am[&t3Q OI!#(l=z1*nhȩe{# k@h fܵ F6c =,t`G\O08K<عoSKO.j3%{By">͆PؾfgjFx{ٙaUe[ /ɷ+r[:~e:k܂t4w#[ǰ2͠܊Ϭ4sc֭Yƥl;^V-x$fw 8LX_{;/GJ`Zwv$:I zud\&l, HAP<'ǝ @߽<pk n c7Av}^&8.;<&l<-#wT9<NcWmgQͬs:"w!ͭ5W`AF@5jadȂ6Qx'j5{2[;!#1Ib=xy|d a-&}75BQ/lqhv~2N4wpCst4x p @ȇcy( =N-_%7@̹Ogǯ!ϯًY0C\z ay^~["3'(R#v1i7~;u?u#Yrv{~_1r: (cI V,PANTw7hȈM>@'W`USvAc^_:*, &l޴ ezAJȥ&Adv .IdwsL*j8 3 wm d|%v,PK~Ve g}_Rsetuptools/discovery.py\rFSLA ?Rvqq\R>[*- C `1dU!ryn,gU% tӣ(zuE.V;EoM]mڕRZ&o |u+*/w6E%9][,w, )ДRr?!F*oBb U ,*U;h2)MbvTA&ފuKy$ߔҼɺ/W~4<&.߾|)=zVz eK $^-4oNz`HP hd+)?x/Ls0_֛ޛ5A\7GQL&s8g *nmܠ"6LЦy*8Je9YJv@'ѧ= ZC Ns\Ur;z 1vLx`~Od{q4Ӓ5I&a`ԡk M?!ґ<{iFmZ5GHҋdqG@k#|y[ЛE CES>ϣ̔S#%arɵ6dg9k=&`I,qttlS#I >+o=MAYTB//Ÿ#qQ7{"DbbD_(P m:n16jv$x3ȳ1zv&`.=i <": ;0ܒrbJ6S1ɠx{[4Zi u\c׍ H>h vz؇0[5G|d(CA.e}== k8jfg_`Y ^iwX)< B9(YpqIIB. ^~сQ#LiU}_q*bfB{Y]d[Ad}0D* ">F蔷 v K0[g0y|0?ۄ ,LK"'nCe蝂oZ?\12E%~5!`-&Z^ma''''0\kWo7_tnh; 8:\Ɔ]ŽfH26سM- T?\}SVmM[al7!WD0+sC ):p`$흴q_1~A[e':\s[ |\)v3/###W_^W(RѩvKѴwRFl=GLlh5q1dv=h.z;T]PAz$1b̸{\gpuobxf`[[u}u_t.ƠUh%|/K \U~'Qyi!GwusZԽ=k ќ.2dm 0@$O)%i08 qGvmjdg4ȵ5 *xf.,O=`X\MA' o@zӉW`2g.V-+zo౓VDEC(lKI&l@N$lv{/Tznc-̱)I{Ya6+7/1-X]M0";`AG>W&{4*#sV瞒 $ndQbQZ,(1xl)Ɣ VxJGz Xt:6\8t |"`< hbL R a(m -YL1Nb3ꤶΥ+r̥ =A@y05PNc,OdTW<s"v{J$:@[`3n/o06VPV>+͉߁9ۦ)tX8yBvǹ7k! {V؟Cl(-4[ŌKؑ[Д;ӂ=>bhr=hpa `UVEbU5(]k5]'=ּo2KءRޡ+t>yzl;S˸x[xvSQܸ ;)x (qjm+|Fv(4~!u'n'MoIaԢSU’nz;U[f H 9tѰ/@7U HXqlT=h[喝 ǖJ]cǘpaPbAQI% âupQ#ҟj/G9f4sg\Sq b)eZ< 4"n;{笄`iUaKO)3> dTYL|0/kQ̻ (3Hu-7y“`:qq"I۵tniSgxqY"7D=%z/< 02&픜Saܨ~ gdTMwhB\t49c ꫔|M&/hcS`'?(bS#4ԧsf0LΛPY'n-"w@Xv~bcM1z^zg~H']~5 @Or!M\b"5eY1C2s22q?9Lf _,E6mȅ߾(T"Ң#O3Plˈ4G3bdsh[Sy 9z҅[țˋ<''6לw0DVuZ| :C̎ĎqoȗQjeY@*#[to-}*:zܻj !zW_^)| 괲?B^}4_ HY|e'su_?ӕt7 =KZ/h2ZKv0wT/]ӥ3%M:^?q*ax7ƴڎ?*2Y.´iJlaqw* vjZclPmب?[Y}#l?>Y$Wש ůcLsuЗW<\cSÇ8S^_#ۍ.@㥸Zbc`42ؓ%毋=veC.\N{RuNͱ~AŶى@I4*] qU|0sz.mW@{ uv Ua2qWLBJ6 2=띮lz+vC۹n|Ϭ7\`ӠI 8},Y~s'D,gշG1{smVE?70TlQk{w[.m35к)W_kSgZnYxףu3nwmXXo= <4v>DLD8B_>S-oCǿKz38= mg@3V~WQ| X!, =sP[<ֈ)T|9O<{;0a]tqVfANU~(i xjV\l:<1@{}=JB rh}I;0N8@u3#%rs#(}0"AYPBӴr]SHTX$%~0! uFOPK~V1׶setuptools/dist.py}{sF])Y~$hKUm8얢!r(a  Z֪|5O (UH`g(jHqmy˦NOͶil_[5Yۜ\8Uuy՜. yY ͦW8D#ܷ}s4UXhv'釰ĺWs7^ۢWW:3]rʐW˶reSリ4v۪ն ?jGfk?ţ>y҅m_8 _^U%ەZ_K|@8W |X:ڶi 9c_SxƬ]/Bub&Q2օ]qT\ӮTV (0R2?.̈銻u=0R Zl{g5|3}_8{ۦߩ+]qajץaz\W۶ |lA-e3<"0 uE$҅Uv3Ub۔u?xW7[oT_E[* uuEE`-\ FrϤÃ>vB24g`!-YDyUr]+ ALT~:U=q(T}KgIOgVS݆:eG#K;~:5e|jTn_L]:#W({L&W<ےWO͡VBuήy+UJպJh0ϻmU03TTѣirtܷ~л{y+V*:er3#aYA-ͅN#tR@ԪjRwӝg0Mf K <8uX4&C\}QYw~BoN59Qo_<P9%UtBYwIVW! ::F\Aaw0-:BަV}:p'*ܖ htuK.}Zt<9 (@0肠n;jaj) =MswEynz~I ;'G$'~ztBGdNAœ YCk$T2}pO稑s-OH]lx%RmmO%z+sƺtJI.UR IU.v Ysnӽ,u4C\b{ s*nM _45xtl.k"7V%P҅PKUwE.{Y(,Jjd"RQVݠH|W!8Yw5v]5%z?\djIμfY f]eeUtk{r;'/Qu&j>ň }€R?ؕڏ.jy_n TCmSJS]0!LnZ2c(%Xl:Z]ܕw3ʶŬ\ў(NJ^3ֱk(wݽS4Zar'y\*۶(+'I.YAǕtR&ӷb "Xߗ;׹ݬ"k B}K'9bwwo LqMKxϝg$ cک] ȭr\mC󢨒EQ"X_V$ެH`+h{ -fPTZ2?MGIRt@՗W>䲅S\^ͯa{t}l+!3Of Q~?"xBJSߌJV!nuE@YI0e Dg9ȩT6;L~'ɗ[Z๰gZ/3Aa:xj̢jV+@eMϴЗyPY+lwdbC:FtH#gҿ i3cOYSst[QL(`ngFrV!+eaʛ!d@i'4H(c< }+ ~uO2J|N`?GpÏ|j&!NH )B/_YMMGZzE5W|`ڥ0ҚAR"~4ߴ53%!W2 7'oJuW]3S\lEYefQ{"E4JSW*7}aT"QeNؕ4 Lzt/εYniZXodt缸idEb%\W۱ ?7P\k^|>m00^46  )RbuYLpeSΑSjKe+uX(NWc2)0A-Qe+p)R "B-߉8hEo,]ewa%ɐlͪҳaϧ7pS4f1R#gsGɗmL~ 5`YmVJ"=@ģB yv8NIrIe@NPE]]pu1: T譍ЍcmMzrXElM)ڭ(r^rMw@7<'DBtᨕP `LF;Gt:DD?c٠TL&OőcOZ#LSOt$ 戺[.n@( ea(F˚*fP1K0uuϠ,jQLx.6}D<'rH,+ng Ĭ{\3Zxޚ,SO>soWyyn??|gg+Lӄvκ?47[d g  ^В d6 =>Pf?\8| CPZܧVL/%8,3m`n̓{Vb,I Auw_psOwmb[1dS\IVivAV{nd*?A˼(89` L U'$ SCk* uWPoa-ٞ }pYr\Y5;VvhhAofHTVj IdN5yp'Y-Z%mٳ󅤷l,409zi_BY>i %': :3Ao:Oꐑ|5 %vs-H'.6Uĺ{T̹̄+##Idh{(R8̵2㍌0].gq!X;UlUk1ɮ /ڏ-Lc'#!c2"ԧ߭Ce϶P~ lHM]@0eS_IE7ONߴ>-F fY{h۾cEU)6XG9[fI);rtḍGAE;3J'ҷ}Dryɼlt6 %m{l 5='uf%0; >.`đ]J=4baM~һt0Ln!Yjr:»_@("5?#?\X]N1d Ey#P_3h%n|]m#< ᠟rOoB9uPf`y4')r[=HE˽`C y(;6-Ol#D(Ϙ g4pO nչ}vѺ)@FuLs0]`mŌtJwf4FR13^*43 :v]%):o@52et #iՕ( N]`Ēl,6cUp `eE%s(DTɦ,"޺]ši=þܨ\.YwpAҧD3>TcU8Q1%7 d fg2v*vN+wbqKtJ*LEKQ; #-{s>2#jA$'rCk9Pi9{X9\? 'X+v]FwgrmN9 6zdqxzɳSlH])w dbF'փ&񱠠R=,iФn6wb7Ֆ6n:ɞtR â `üdg/E^y2CNܷ{v_+qґ(HQĠ,is``gWeUuG x]\M6m&$tS'y*PM0wof-' qIA=C39 fc{_GcAIԑm'>\lp.oCpN,K8sq! 5 ώ BC~Y3O#:Ea(S3Te_beY: t x7%SMSVޘ. b7Ŕ G:m*[DVձuI_"jwW |h^LGʾ#8K} X9.YZ!}6.k:<[u4uо[ d&=*蒁yI!zUan" ?Rx§bu?#nygt83D~?su咂 !&(LFg&ꙃd?&Vڏ8t_9GGLǔN>-d-q8B5%\sV5_l 6mM,1ՊqcwYpσGyCz贡5icNdc=Qc?Tp?h =VzK\J@"^>d`@s~G:i`jۚuԳd[nJTv&}ݪm'QhyӞ=ڏ({p׳YiuOF?[~`$zpm] :/W~Cr rxLfB2pwYAoUk Ŭ5j(CEш"1̵<>hpVmS f >4؁Η"o|0x1~a̩2%i0(B>ۀZPRlWK"u7q9EֿXSsDn׊_V4ow쒀3MW6Ph9QDj8٘$[R>ڌ8 i>߾)縁SUnFeRrN;#Tz>7{&BnD탼G=j/J@T)1߲W09Y_w6~ӷ/k D\_&TuN 9g#ѩ1u bN=cSoiЅDž%h~W8$7ȖMZ=#ׂ$s:E5N#ˮWtF">Co ΢rYsKp/zU][|S怛&D$ONIhɜrfxM?ճȭAlL zUC]P񵂽2C= S殪v.-wP0xh}CpqğJ̭r #=y~tOx?/K7}WO\$l_/4N2H}i@\X|B9a¼~y*8O?u@Ǐ @v"jYb7}w\.dp&b (_)V\MQC>Enx>HWX)82#qVp-|?bWŶ9 GF}/nKLROJ^HY_]AV,$?Mx/陫SV>xIp8rcX+ ]0Q<9 ٢:+T71Hi]sҡּGOʛTM n* E8[|;-BˑLA-15v9,%<[(jukrÌͦL#o9:)Ϲo(!iy{ ئGAF9J Xt{.\̏])08khw A=Z7~ZW6Lj.K F >C_8hu:P*[hr{955dJVԔt}Ӭh`Aޚ]{ u}ƎN g8NA1a XIgdpMNwbAKA>/;|kϝN% 3%Yb2LycY@f@ƅZl||ZSTM'7_wA]{' r&oғsTʺv};wQ[:vhyTcb(zˬl3=k([njNY'۝_|1@+ ]©~`oMln,I Ӕfpa%B{T8}Fmx˞yNQGRBtH=Gl٬0. .Iw#P9vvl~}-ڤSP#{W:ZJFE<ɯ%Ư9Ήp>G"1(]\L!@\23.vPKOQ*.q BymOC &w9{! 7Gm(ڷu9kXZL2w߫<Ј0~`8<}\4ù/z(rpoIh[(VdsMr*^5Ãx$bm7Ee'57K?Ӣ#ȜOM5'&8fFN|m&Ir-7?6h禡@rctB&OL›' 8|^ۡI9Юn0DEjg%ξ)t"X4_ SljH4):E Si[ʦfH"3uf(tJN·)T@ jU:bn> -vbx|FŬZ214GAwR!Ά]8D’s w˦ujFĒvcA>xTg^w5Q({׮#9c= }<3$V2%sd}ȍcL%n%:62#{tސ}Urϰ}`a&aI8J2uvE5ݟYn[k|bwDY̰]+)]m캤RA>XU"׫9_|ф-;`@{ݖW hw#GB6F6!!C049]`ub#%|%j0%&X-ygݮ'^S㜈j(^/†2(tBw݈x=)Q !LϮaU(ϒ{S%:ڛ΁w&9uRHn@ƍ[jDq̴;!.1 ⲏ4-2h'AcO s J"*bOQ6OpRf@l +)L !LݥڊYQB8ϸv7hOGݫ[3 r4],fzᛆO^ vT4zN0aύWp;}HlMh・N|W,:h": H֦ Aw;[D\7ZHΣɇ43Gܣ%RvA_݂3*XGSy'۔C;]䔂 -4citqtMan=o v͕GWhtw'>n]o;EݬNu3Čdf^;[wt1N!_Bxܦ3ȓ;y2%Ǩ]4҆u%kPF/)oɀV#Ef.4L rkbݢ)pwIbͨ5ڜHİ, @GJ.V W7~gVjႱ ]_ߓ zb9sLzڐt.nFACZD06!w;NlH&1h/Ӂ?k}B1w&#jJܳWHK5|?E;=u\ouQSs&.&(btzvz#@CmM5ޣgm1ݥIܨyYoU]“:$(q^${swcQ"HԹl.7|1@VKzKs o+jVku~s(F}{E)9{EO `ubJ(wiL{YzxuKb>,I0ppտDd|3a?','i#93#bjD /{9x:?E?8gKWHff@cĩw`rV=$"rь: QJJU+I-:$֏-R|/V}JTo%!$!](7Q=(v ~\mF!#(NzJS}fnaZۓu}zbS'WMfLaރ yD $$"I!cJWUK&~д@gz1hՈzɫ ]y)#/ݰф(Nω,=:"Bb0ٵIu+UNί( OکAkش37#]ϝv!e52ԎPc1P|gD{<ѿ6zZB jAq.0vFRڞY5gbx4ma9r-fe8 ԅ}qi)tc\ Uᇻr/[s֪]ĸ<~XzHrOkJswCDkaéRh@gT̸vy6xïU*F}s2iS߸i;NjP;8^<}0Oaf%qeA?:4zѮ*goO@7й$M-ҳUSg=elNOL[fGJ϶*(_f:lH"m;56S E,*{!Hl+g}іK z! Xz!'hYJ=bN:c6I ܙ"WzL23+r5l-Zy*Hw7X&o|Mmρu犯(hLDlY};dwhGuU胇&]8vl#h%|8-qdnH]F˰𿵌%$⸡dv]%YWE+\3 x˫ݪBAG!a])5 .sY9&SCgЊs(vim0Y<٠rM<߭L7:{̓L@mH*z?'ral N1Ėb"VY0ʼnF/syf Ƣ 4{mm"e'x\a2/IMnBiDEGۢ{o z:x؋*M2EFyW#l4APK~Vu=̕setuptools/extension.pyXmo_A H=/P8vHs9ݑzܒ\* }lV"/3<RՍ^XkSTη^U.+0lh%k}e4={N=lEVHGn/TN&"'ʋ=_\M>4|DZ|- 7a(J6NDC9:+q-fqJpӪ, w"8=Xty|?|wwzH`0;kX*^Z 1ǚҡ3B;Y#(^\h/.I`"}K #OyWle!c3#שAkşWac^(O50Gctx8]90 I0@/bԙ0Lyc˛4)aArH$h+iӬ(8+iMAhE6tl{Tj Z wžsSBEȬ rLՏ«^0ZJC& $(ʋȝd}~sj#m ΒV0R u|L3VYX.:)~ yR8 BQN O+xJʽ-l; t&Y I1]&!LUnNKTJ21 EV*;MǹΒ/ n*eN|qWAlPvW9c\8>N)dso0wL.}#pM=E>–;h?6@Ձ:,Ri'ٵ\xb3MPhVaڪ G 6{Ta_ e A̮c|a 'Y̍N~ l삏}U+ht]g 0c6{Io(XoP{b3; .T"/~ kS p  M :N6"t+@ d'\JLV(N.ϭ,oCjƿBd+U7* wN9N#.D:S狿nI I8I_NLre]IkQ _R(CN@b }M+|!#p8kTwS6D$dԩewKVC9:rAn"̡=vb%jsR(\6sXm%wf,W8]xׄ֫bc×MEV*IJlWsJ[!vϠg T׸E ?³JYsGv)M)Kqb;}뚨d58a`%$7>TV݅`?哒") kx̧xxY ۆ|0#P{s46& @<A.\FB?N%.27hxsx饤K\z#e hmÛU-)2giKԝ=+᥁2>N0[>'ְfPKu<00~Hoǯ3vGgX9 !ū/F4`^{2t-1 ┡Alas1<#&sYG,evx'1ƞO%+k PK~V޷Y setuptools/glob.pyXMs6Wl%amgz8z$k$\)PH̯⋠$顺$vow ;^`+^,xMkz s#3W65K'TE^m I Ǫs`W0?U E&D?Zպj$X1I2` spPli$.m5ӥ7cO?3@ŕ6vG`-{"8ؖիB'_L:ij)9⌫Cm~ Qg\uCpJdJ!1ALuk_]/‚`+eLL]Vޘq1#=4o?0R=[$c*7ϥ#T UXQ,s_Y4B*$Ljk _Fr(ElM.(^=eTdV(D{*s+:c9rr4M25vrN4{F٧eM#AT?[Nr.*hj,SފՌL ahT@Y$=#o_>|HG/N]ײnZkN\} {)kys0vC4)avy@qxBqY\N[fC]Q[ඵ|6fdH=̲HipƬE<`5 :6{f.d:yq*7c t/PK~V_[.setuptools/gui-32.exe:xSU7MRBi!B(-IH)#@k 6iRB`Æ ό|.V[u\gGf?SUqt=wwKD8 ΜĀVE}CK?tKb\0]0/|Z&{$ cKњ݊-Aڡ[ Hr -SՌJ^[ҴIH7wl^G'YFmM ɶ{z `w^ף1΢F$gɌFއJ,2d҉- wgV㖺[ZHc1m B+Ar@RI0F-ۮ =Ն@[t@$N<J:@T)| pCR!hQ0s8 j9;-7Fʬe bk6-`S7' ߜ Ω6z9ٌ Vrx]Ic*PBW2. "{MʳtHGD؉+P)!D\7ܲ B$^AhEWcر!zchuߨ|JReMdxNr #r Ǖ;|5.2r1KYYm92P]=jaP%ƒhd,m<ү<Fpݯbء)##ٜZ/Gկ;f99rr\Õ{fWp(XQ>HjuMP9QF;Jmhz(;c]^mAhE1RgçsC`x(G5ǦY14vrH@= } ?65cp7)QcτXn M*L7 c b?7"Ҧ>zj%C:'9C7 oCM‚"_'΁ &n Ra#r10)e1YaJx^y~/ȈJc?$rsq'?6Iwj>]^KE9Rv]Ν(M(/mzy=9w % ]92 j..a]hFwMq4ï/rtqc!aSbkWI/0+a趆z!LP]scw^_C-K?3v](G8:T$#"~JϊLv.ݴpGp? <m9j59S=K6>#["8:<# y(JgoȮOɮ[Wi/0|%WwpNuH\Yݽ5a“>(I|aUmгX]r:$濒ҽm QT.4+yt`90o v #bG+87rv} u/2'u3P=/A@>8:uArΘ ݌Psc` F1j+ %TǺhFS 0CX;ׂq ?p.C亗rq0n!A[2x%ɲrv~=_&̋V4^ bg|*k&c"5 @T-l8ZrcǸ  鹚aN‘epLMp I0&<=QAf1/Odm۽=w[28;go^e0/k yPqΔx2Ϭw # z,Qytgrwn= QV>¼oEAhP^)EY(IA\FbBQjhqrbٱ%c1# 앷:2hUq VŚ[8 ̂6GVt|݃&b)@ }qpŮ窍>W ?Wz*,~n8j2 gpU>f6H .s. !YeC26GQ,B{Dߖx^-z1#_qA&53B#M{y%Q!Vz.|W$L-p/tVLJg6ۅ&TRC"f!W4k=V (D=r'ioMr̀[$+@|jsLF@ן%]>}K û X^2 sظP7׍Ddħ0 /2ƝDO<jОLx%,(dh߰ m;8C[K6Ϗ_:Ȣ8#U sEaf7YFF[J2 L:42iB 7v/?ށ[C g :5{-!݃9twmQqs H8 @%c‰?/9'8f9Ll)xw=?oVŠhCRlA 3Af$B٥vbzjoOzeixoU2:h!Ecۏh_Ǟ(v,EVPS'#G,9XyxAr8.^iqx`2yg1D-/$T:)y@pH,:g8y_AآSS,eG@$i4b"ݑ\\ś(N ߧg()|#e )a,/ >OaB1 8R]Rx&jM(^H#qv&TN@%X|n+Xx>x!8pV'I=Q m=*T9zRGs/-%d!r(uPPvCyOZ/teE7(-fv1[iryUVJG0tFaj]O/iRk|thUM{lMˋ,J kOLZh>$3(d 28܇3akńJ)on-^JY=?K`O.VM̈́Wo+شyL!qd=T$::V7)P7NQyI4+f2UOQi qEyh K$ˀ*a[йSHwȀ>%uK$g&ߟHES74# Kmi-*M|CSq  f!RPB{;o>oYh-$inBx@^6N_(M{fb2⪜R!f~ d<hBNe}mhN@h68 &+J-VW5:&zzsd5=ส$ik{vyY^m6b_R`wU>af,ـ#,p*k|&J  _1衪P4Pgk /=6Y]--0~ƻ[Wc)ѩM7CObkǴ|b z\,mfklvOdՆ\j-6|R[[Se4{~Hhi% v%@`* 5<,Ua RXƶ2%3l0[`ʌO<ir5k};9}\^{\w16m޸⚎Tj!eW6l"x/;z*u\BP8*4: !h^X xlv}u AiQEKШwŤ5T(V_AOH[,00>OS-2ag ^ oyd#ʵ75{|douvE [<%E%Eb4N3vW˵A/ja&LAw~kVkq,vh%hrsȷv/üZH.t]@lU`/XL#E!ۭ뀬EߔL"JZK}TN `']7p!1Œ0;͌?5fQ!U6LMlkkJ_C1&g{ cŮ` KՅ* 2LwU,T +ʳ2@iiWoT{WN(jq9-h)n+)T6t51~6RY06Di iڅt Ŷmi]m_xn }gʃThcҁTdBJIs2._myn..wSwFu^ K%J)$MhVjkU&y[,Ֆkt]VSiOXڊ巯߽}Its>[zқte:++Wܸ֬2rV6t5cxkàaba6󍋌KZh5m}ƿ1>l|xܨ0Lͦ禓L4k4l+ʳU巕?Rxyos+ +ڊXŏ*~R|o*])Lbg%K %J~S+%s47hnTji<9 {=J vN3t{t~;{Rw\݋G7#ǺOu#/uc4BߨWo/kruza,)ѕ4 zUIOsQ?]1wŵ]|PK~V(8setuptools/gui-64.exe; tՕOd˿(18`%b%vb;S0Ȋ_0nknZkl7%Y&ũÛt?$u?2k_̘S 7.9{)̼4k,yj% xܭ4ߝN-jmNI!Nd"P)d8x1ӨRN#$]铂dbLԤi{S |>H4: &BΨD@쳌ٟ15iW/v3GǑMI%f*IO)bFF84P[Qt#<ҕ^_0t&nx߭(t?EʖuqqU$(=S"Z tca"Z,S=Vcʷ&/rqĉ'ʲ\kbh{9eNɆ<햱 mTe0m FyMy m3m 6cv2d`)D*I ׀.~d]o-q|ٲ;d_QqrR6RD}> ԭgU싲!Qj:nFȽC'T-.*iIt'!Jr1}}tu4!v9X^'gۡtun}ܦ2cjHN;ԏT}gM: 'zM' &'n3mmNm ɽri\s>.ڣ /u9vq)U]+L=CWWwM/qˈ[vY˾uyڻbD ?OVl:$6}r`^J p}xm7lji%Yߠ>+-47E.{@[{ STK&3ԥl?F2N}B|I0uRܷ U$CtBVB\ uc#3BubO+ K}g}AN- hH` b)[?l퀪mRt \XƉ`<ٰ*4 3n89M ,v.Z[`=KUI,IIu"8~Tm:9m'9w?lx/?%T.'K;@:5 / <*goQ5wv/JU9F#cR $u~C Ò݊ېbx-d9VO*=p?A_m? kg"ԣZP͉bj鷃2|QT1u6t'`-φ7<!bTw>^Xӑ>Da82; L5%Cv"Ȣ0'lY8nY$o?[yۭ+_)iY4|gW;Ǹh?,Stz.9a>X/8:K&$ֵ_E:omg.DМ;nWjΝ\-NM:OttgkTj M}v 4dKdLV8&>V~><'x}XzN?BSFZ]x lc,q)'R !$] SbM'r K'wa ޏkV:嬕á:+s[3P_ӑ%'MѴ~-K)ODf,gLyt`Qc9Ԭg;`~6y\}c ^ol;YtYkO0?#؈^rvY`K)x*&Lh; Do K+w$BG8;bxuG 3:ȐZirHqJ]?bHn 7F"Ȏݕ:bE@!PKt^~f tϲ98`Js Х SCt9y0`ª;S :RgxB8K i˗,`9[hf[{ [pb{K\jqnOzgF6/^BSH бPʒh}' ҁ< Ƞƛsf i>a%1}tlqU ;79{`[tMrIK? LzNXS&eLKݙd+yWjkO7rkiVy2l QDxQFS2,-zv!}G.[N'w`NjJ &Zh M!'rU$9w 3^4$_~NWq9Q6r"x\l⨐/αCn%ԩ{ 8sCoXqǘw7vNF͞d6yBz_kƜq2}۰b]P0-!;OHs$ j`H x\jf|"!>Ǘ 2/3YeN|.ՙS;>+qv4tuxmڷK@JX/ \#,_ jvX6X&&%Xdx}>/zm> A#gvpezP{`]*wnK؋[D݌a*uT"mhSPl 2$7+*BB:9'IKˤY\Cxҳ$!m }P_D9J`V.ssOYѵjDŽhҖ~DojѼ P?И3 P`xvtq1wӶ_$\^#Fg.S'mde鵴 sbW[=(]6|Ɖ&<=ƙTwpݟїBy_GZmOྲྀz||]xXժ}m7pLc> |4ܑ! Esa N}z}ЗUtӈ0*`vKr%U{BjDm?Fh&}C 3#mWڠHxI?Vf(6~ˌw]uAQ4} aS=6 mPث7\& %D z_"V%٤Q({-$DK5TSNpn4Zcs[U1x$W8Wy$ƖB Vf y>L$wrN(~` 3>(@w }ҧ>'ee:^p?Nh*F)w+cJq>mO;%NRhW`9O~@T=G3+ P z{1E &ԵtBYk`&B7]NP$kbf^07Ɩm,<AO@( lf?ryyBW·cZHđ Bqݸy_LI;c`۰B]dɦ AM F1t&ET# _q\RMאk&7U3L!l֑ dt2t ٤94d:RP:s8pzBQ W]N $= FX FH!3>EtKB><]ѽ"ztT}lUYXSA^fԧ}s&у^5فf/Gm!oftXsFJ*SE27A]! ߜサPɹ)bsՐ\Hsפ;T-1,=v&ؖ!Y`ìR-ObJ&Q0U''g a3W.t}cpFq+7 qYcpgS_v]_ wc摫 mnUp 7[97<*pǷߴk[ G-Րe:X~I W> a/;KW A/$U7>>4n}!_]scRZ: m|[pq:xVxw8NGf_t}kPW_dəA./oqn`  x= 8NWp_1Զ N%s5`BBR@@Z ORZ JGW0CBG𸼞;GxOksQLpHQ]d9 kGN#x:qBӵb(1* =! 0T' 8q6ܷS`bG(ᤂTlQqc(Sz=*_iT`vd3A:x`:sY|\In?]ϢТ;!VZ{B]ƾ]c6WecK1NU g pBJ\tu|@0-~Xg>7[>ͽ\;@$K صt`m!X";u#.ȡ7jM͛`J{G. Ћ!bb?ypX+ !ܖDE/1m;Ys7Ka ;}lYh9 ΫO]p3,F('d|V5S Gpf/.+g33Te9})=ǔfԔ3rl"\]6; ڹR~2edx*™P*Vϗ\Icω| *)8<{e)ago^&8T^5n!3oh]êEeEƐh+*~EnV+6y/TU [6WhQ-"HDsJ,)2iCҀX)!WnBzxH{=V 0HAc6Ʊ͵.(b^UEPov>Xd {lnҪZ\_d\<*dT.S3f2]M]]H׮v=Xp{H{C=yX~qPK~V A6setuptools/gui-arm64.exe[ xՕ> 3h  q4Q/`RBm /H23 ['Jl*Hm}-vj(Uvm(2t=fuar=s=ܙoMf"O4J,JQ.}b:h}-Y]n;lN 6j6خz[Y+1#-_S{v[S}>)ֲ*= `Wi=s=;){ovp[U_CܟJc1ZH馋)t\ 4 ɶ(PniL&utSN6ISi^n#+?z5&6T5)2h籱jǜGsM3O!1/t6C~9'+}K%^g\;c!3Lj$2NULloLظɤӕ3vҗBD׼egSrByJf8?b,O؇HaT(j e;6zcN)rSH_sx}df×(<>H)t9<Ĝ/\0oQ9G}J'N$6 V'l*rCV륃'%(YiTjMQا+֥߫_N)/Q(ݖB3ѺC˭F /ݩSg8ì8D1Y>:~&da9Mq>u^Z˟W)<;a/Gz&dzk 9lOհIU/Mi!)ifsߙE9݌6C,(FM4 p57L&ɘkl}98 V4S cC"W1gp{J;Y;4h4{ԮgGLgL;/WY{ %c=nSԕjBQ3+vLFΡL2н@!; =ów+T`ݲn;H'g暾o>?/~ ?nk͠pB b B ,';nS)eǠ#˔ň{t՟{tK{<{pN \Dt9}-il9Qu "H\y9L19lF4Ml1*:-j1I.h5~JFrr.W@RDiɞÕhm}}b=BŲrA#Ӱ/Ԏ<؈Л<^}^+=Kfz޿Er@D=].xxΣ,c^*jD{JC3xBYpOZx(هl?]YN|KÐ =Vc7n3ȯ(qTEa!y^s5"F~YZՁ ?I^O᡿J |+"Æ"VN-,穝Ϯ۱.S9+jdY55< 5TZ>k3$?J?ZG_Xzh|`|F}7J =ojgnru;AQlf%< L52>en13 b9S^~aPλy5ԬIg8C}(-ɛn|\ZZ<[ey|cA]s8\~O,O.Q;{Tu]o~ ݎq֣EEy X/VCF18%1LF}4P7~gfڙD<- h}/ꈥC MiOHgP5u=-zyܤCW ? /gyOO<kO |}0q?ֿ |YT>DWݮ"u 7Ci2q sH?2MC_mYSFЏ_!bmU%S2rAMEFy0vr~~Mz3 -"XSJ&JeLr/t;nWh4: )j'Ӱ/V*eP`^cmh-퇠E~viQvYv]gؓJ)ƴު"Q#XQ-QV-9}˓ȱ{}^[h|qbܮb&|Vq}gƘ1;JrJ/#gu}q{I{xM%X;د?O+jQwsL6~{*bP=23ܼqnJU#Y!W:u~^"%8[:z jt_,kuOp.:986:!aX>HvY_e%LSZW^ Y7X5r\ݘU|RBwAKWD$o]2:,e,=N>b\1:=}d1+r)dy"Fsߢ|$"Nf!ܿ֩o$y~"_G'!d첞g؋&읕-g`Qlw_Iڈqy=EԀyk/-9yf$ZyhϤ< Ձ^Z#wo~l)r:Jdb59 R%1e*Xn듗 uparpNU|gY{)wdλΰ\G3[/B5-F{P_sc\fLz}C9CUCeC{}~~kC o~Cv=CqC_ ߟ j+/$#p.I̟o9XAZB)HQߓ:|AŸJ8&aJHX"Z oSuA ) ]5qmO‡%$_K}HII?*?kub?CJ W%c H?pY)WW$ǿ'IxIr_# %,9J. oKφ?~ł~0_gw=-wl zmNjkS JҒ7iA9͚m3.8}_b~קy0Ft|ASm t-nO^`1tllLv6ۘyEiz]uv/S_>pMmּqt"/wרӅp:N*%[u} ;⍀ q*%om$nzC gq+зvB~I;K%MI\[ S?Դpĉo]4N?66|,핝iP'r=FSIPJ'5}QG(}3vqSH5WڈR΂fwqw>\"R=aMQcMOw ,|gGߧp!*@3 s2)|D{!wb1s }Hƞ|b_ڮ^.ˀ[3p-w'phq  10wq2œ!p sJ?g?Ոh-ʠ*rӂkGk#ѦUw:<>-b&w `vxBU;b4vFs]/TPj̗WhwB%ah^|7zC1S+-}Š\Q~a*(h/peh1`BϞD71G&@7`}o+Tm][w #ߔOV9"a)'sqw{q*}(ޜ9eMn\KS$ڶhr󪂲qzqP*qmqE-Dv^UЩ V_3#m#6vXU\p6G{ޖ`1 -e6=-ŸXl+@nyPv_k*Zs߽g&-09h^#/ 7h[V[+]Uyx7k[G⪂Gk@+]_V^9ʘ4./D+߰COSϑў}O>ϟߟ?PK~V_[.setuptools/gui.exe:xSU7MRBi!B(-IH)#@k 6iRB`Æ ό|.V[u\gGf?SUqt=wwKD8 ΜĀVE}CK?tKb\0]0/|Z&{$ cKњ݊-Aڡ[ Hr -SՌJ^[ҴIH7wl^G'YFmM ɶ{z `w^ף1΢F$gɌFއJ,2d҉- wgV㖺[ZHc1m B+Ar@RI0F-ۮ =Ն@[t@$N<J:@T)| pCR!hQ0s8 j9;-7Fʬe bk6-`S7' ߜ Ω6z9ٌ Vrx]Ic*PBW2. "{MʳtHGD؉+P)!D\7ܲ B$^AhEWcر!zchuߨ|JReMdxNr #r Ǖ;|5.2r1KYYm92P]=jaP%ƒhd,m<ү<Fpݯbء)##ٜZ/Gկ;f99rr\Õ{fWp(XQ>HjuMP9QF;Jmhz(;c]^mAhE1RgçsC`x(G5ǦY14vrH@= } ?65cp7)QcτXn M*L7 c b?7"Ҧ>zj%C:'9C7 oCM‚"_'΁ &n Ra#r10)e1YaJx^y~/ȈJc?$rsq'?6Iwj>]^KE9Rv]Ν(M(/mzy=9w % ]92 j..a]hFwMq4ï/rtqc!aSbkWI/0+a趆z!LP]scw^_C-K?3v](G8:T$#"~JϊLv.ݴpGp? <m9j59S=K6>#["8:<# y(JgoȮOɮ[Wi/0|%WwpNuH\Yݽ5a“>(I|aUmгX]r:$濒ҽm QT.4+yt`90o v #bG+87rv} u/2'u3P=/A@>8:uArΘ ݌Psc` F1j+ %TǺhFS 0CX;ׂq ?p.C亗rq0n!A[2x%ɲrv~=_&̋V4^ bg|*k&c"5 @T-l8ZrcǸ  鹚aN‘epLMp I0&<=QAf1/Odm۽=w[28;go^e0/k yPqΔx2Ϭw # z,Qytgrwn= QV>¼oEAhP^)EY(IA\FbBQjhqrbٱ%c1# 앷:2hUq VŚ[8 ̂6GVt|݃&b)@ }qpŮ窍>W ?Wz*,~n8j2 gpU>f6H .s. !YeC26GQ,B{Dߖx^-z1#_qA&53B#M{y%Q!Vz.|W$L-p/tVLJg6ۅ&TRC"f!W4k=V (D=r'ioMr̀[$+@|jsLF@ן%]>}K û X^2 sظP7׍Ddħ0 /2ƝDO<jОLx%,(dh߰ m;8C[K6Ϗ_:Ȣ8#U sEaf7YFF[J2 L:42iB 7v/?ށ[C g :5{-!݃9twmQqs H8 @%c‰?/9'8f9Ll)xw=?oVŠhCRlA 3Af$B٥vbzjoOzeixoU2:h!Ecۏh_Ǟ(v,EVPS'#G,9XyxAr8.^iqx`2yg1D-/$T:)y@pH,:g8y_AآSS,eG@$i4b"ݑ\\ś(N ߧg()|#e )a,/ >OaB1 8R]Rx&jM(^H#qv&TN@%X|n+Xx>x!8pV'I=Q m=*T9zRGs/-%d!r(uPPvCyOZ/teE7(-fv1[iryUVJG0tFaj]O/iRk|thUM{lMˋ,J kOLZh>$3(d 28܇3akńJ)on-^JY=?K`O.VM̈́Wo+شyL!qd=T$::V7)P7NQyI4+f2UOQi qEyh K$ˀ*a[йSHwȀ>%uK$g&ߟHES74# Kmi-*M|CSq  f!RPB{;o>oYh-$inBx@^6N_(M{fb2⪜R!f~ d<hBNe}mhN@h68 &+J-VW5:&zzsd5=ส$ik{vyY^m6b_R`wU>af,ـ#,p*k|&J  _1衪P4Pgk /=6Y]--0~ƻ[Wc)ѩM7CObkǴ|b z\,mfklvOdՆ\j-6|R[[Se4{~Hhi% v%@`* 5<,Ua RXƶ2%3l0[`ʌO<ir5k};9}\^{\w16m޸⚎Tj!eW6l"x/;z*u\BP8*4: !h^X xlv}u AiQEKШwŤ5T(V_AOH[,00>OS-2ag ^ oyd#ʵ75{|douvE [<%E%Eb4N3vW˵A/ja&LAw~kVkq,vh%hrsȷv/üZH.t]@lU`/XL#E!ۭ뀬EߔL"JZK}TN `']7p!1Œ0;͌?5fQ!U6LMlkkJ_C1&g{ cŮ` KՅ* 2LwU,T +ʳ2@iiWoT{WN(jq9-h)n+)T6t51~6RY06Di iڅt Ŷmi]m_xn }gʃThcҁTdBJIs2._myn..wSwFu^ K%J)$MhVjkU&y[,Ֆkt]VSiOXڊ巯߽}Its>[zқte:++Wܸ֬2rV6t5cxkàaba6󍋌KZh5m}ƿ1>l|xܨ0Lͦ禓L4k4l+ʳU巕?Rxyos+ +ڊXŏ*~R|o*])Lbg%K %J~S+%s47hnTji<9 {=J vN3t{t~;{Rw\݋G7#ǺOu#/uc4BߨWo/kruza,)ѕ4 zUIOsQ?]1wŵ]|PK~V)K:>setuptools/installer.pyXmO#_b?8EI.9+Ka!{BC{lwhO=}W{|^BJS9f5Qᳱ'[dd}Ѯ\+Muev"WNi+km67 U:|ǁv]3R)':iӊls%7^ȪPŦ\]zgTVIL-M&"]"O*mL~+|5W”,KR UGV/BN ^1e TP*DOR+O=`bتiumSlb şDHXو(\M%<lK&ڒ(US3AO ǝ6 /JhٗrRϓƥQ*-W#@tSڞVAn~LPBg徽}Ѩ\EA\$ rzM?_{MOm~<3+Nj&OGHRK.iH.|$*Kqk@e8Q3! !̮D2qUeMod!kT' K-3J3S(Nf"aix+}][^(BȡW턭+̃<1S c4=S>;EţL;YSe?0غP>! qXb* =KO7-X,3uGj'r7p(HGY)Nw+b"]-~O/6z~nKHFcN`$riP\ M-2GfҭA#N,1drGPEyƹgk;\>PIz'B\ur՗M}qBF_"~< ſǣ1ɠszNJsS%W7d㛛m#Է0Eɭupb-l(}9FMD A7$bŮ`Dv[!%ٖ2n`=F'^eʕ8n "0}(sjH"e@mPvZsFp)J~'|+`#j`h:~S iXcb^_8 :m%WMnYڞ5^kMuRiz؎̥/~i.OFfcߢϝ `,aǦ ,޼=ު:2.[a|1u{{$Q0 E/nB8!5PmG~2[HJWH\qdU kj޻[,Be􇎝z#؞r߳0]esG˺q`"vH&׼MN>q[v`\>7&d, b폾}<_>WnPB:rIwL_]Q_˗A$lGtˠ7n!݃I߀_YTWG+'3B"V@Cz9M?¤w\Zp=3u7.wz}Zx_)ǞPK~V2S,setuptools/launch.pyURn `Gr-k$SUD`:_8 3;9⯚F ArQiR΀D ́ 0{JU;*)H8xOw3FfƢF \*B/g!~>o΂jI-Ok!;wʙ,գ&Tɡk{=0";+5ScQr!H' Q.024=YAދL)hC4mv0i !p.Ĕw5* 7e4)\bqZAYU*Gژ^_YiiCL<UKk&E9"G#,3Ƃ),$kH)i,:[7ɹaH!'\rZBA]ͬ|aeN)#Iʧm %x.;w2uλpO+S;ALVLHڰВNaPt&T$ž4Ff5vJ"(]rԣwi}Ou4Gס2z ͳ@lk 7gb|)l(= 0)4wdXGpLPߴˌ;BB@{1z.+eb{:HT䫳Wo\͝.#*-%y y|]<{rc 75?Y +Ls.FJVXUO'_&o8TteC !q>'o s0/U"xqX0qbZI(v.-<*@s `+ZNh@_Uy}Y}n?|/>YrNUXIŀ\ZfhC GǏlBGנa\@9 ]1%.["nTcVAXMҌrvCe1W>'}Stb'\M2r Jw5|M\ J$Ӫ՛™a1l{(Ey}FeVUl hW8S~%jB,ՙ$KT;bFuXqsضP)p5{+x-%zBͻў-yq[lTYo%t {9F1d/ioxΛ U:w^~GIngJ%Fv5p0"m0)7t\PqeмN'5#՟ZI-\X=^Np5!\Ճj<՘{./,JR㵯 Yӫ 4Lا]a3[ɵwD a"%^bvOmu/8Zafi' Tn'xM͓RWzp*Kmm_%wOåYQo{yAyre2oI$Uky$d98;H y0P)y"׶<=Ŭ[Ҿ@Qn3D3G`eNUH\F* PK~V0>%setuptools/msvc.py=w8:+{Ivt'ouqٻ'L-y%I"%R}>RMǛ%%MI%Q9+ ۿ?:}Y* 36Rkz_cpt:x1mm`LJ՟ݝgآ> ie YBk5oaASGZxTBDE{qR JI!'F~ .Ϫﭢ1UQ0YOْua( =6d,4B\*A(LJrTzuV Ų(>a]z.Eh2 ~PɒվDjĸ݇S&siӬqNPg!1>u뒋1{|a ʗ3~΀2NνU~u2Z3K`J ce2 t :|,9 yCHpS #ٍ]9-k]>O`׀}q^ t1 !0)'7d_Nn H#Q9-_Ff4V4nݴK>%qNmJi_3iEtY,EsMk<4|u$D)?45'`Ubؾ"36c_Kv0j`̊DGy#*G2*pJ/`J~B'~'/20@:YtTNazy0#l@{%gLZ]`z  O></̮+~3|`^ llo5'6WұПZxT큀g{'a{a].?k޺~zb齞]79!,%AcK]LP֗՚Ks8(['cg.C8z(c9y=k_ 1pN:22jlSR .C E( nU:[R*x酕f!0ivC:|-@…_$}D3?o}Q;Dc"ng%;5н!Le i԰%dȠGikh֕4yWJZT?0Kpf(y>M >WWW\Y`to|qtѴN{?[DH*]!x=ޓG"] ^(̓#Ʀc\J2 ,eXZO z/( :, lOEo@10;'LjA,ϳgB'm7GoqSM 0*_`3d h4Mz=Fs/bix*c<{YrLIު-4}]lo܆Ynw<C[ZW^hMt.Z.N*jdǙaFyӐޢۓhx21AS_XR=cU Gy(9Ki2hau&eXZæq8hD}a%X5lR7rhWc|A *L.`7TDbKh#H%I'QU6Tj$ S? IY&v1l>lTuHʖ5%i7#tQ_l-Pث/}R7h#$HZ;ru& nDa ց7w^{%G\@][H> I'={L^.8a hh(aۭvFc4J2BܕĬ'wbbF,U@MX5ɉJs{}UIZdf1pn5r83>~򞭭g)cg%IMkՓ(կr'54kȉ3in+ j/bhusNΨyP]ݱI z>+BzmA3}&`&n_j"]E4t:~oM4EcX~c[j=_Z훒luF7i1p2Hhַع+n[{*oOZ)DDzp&8^4#Q &UNe\cܯ52 ߷^:#q8h aЉ;n8N4shmmc|#<=R&Y^j#:ծoOn_ytvw/ETctZsjw׷J/K#5i&ߌ2Y DŽbthݞ0PKLm̚ :qR<`͖E5fiΌ|7iw8=bQ7 oZk! $$Cz}oVtUWŅcr-{!.Hnȏn~b%Nr!d iNʕiwbV lT0ҨD2(LX>V I̕)ز~}y$e:"**SiN=3_l']9xlNFM| SBǷWmP ڈ9w%(JW^cm *u-H3׶\3%q*VZ@pF*R.dUߵXg{.gsJӪ/]V: <֩ƝXVŬe5;H7|sC ?%dUr&ˁ8 )g^Y`qˁk%Q FfRҠQ.``|~KA|3+J)THfqMgw{#ai;>exHygmf_fCmaΝmrbZ;jrXҧw/骋0`1:m#]m[S#mzF39_XdzLzEeQɭ \z@7) 4OX,Y:do5'6Vs ȯ/Na@GR+ś!BK#B`VgQ^b~aa1T,SntpܜWڣ aN5`RЈr!V*"lM3&Ep_yh |Щm:!. tV(gKj !M2wxͱY p68>1[إyMM Q ˭zt.ymЅEph둪yKjPz^ˆL D;4JHZ{x*?7\_.X11ܹI "]Ɩ]'x4Zir#"z~i4EϟM;k󓐳+<5{HSj399m:sYwް2/t;dy^+m:KӴ'LiϜJC1NhY2+$[]/= GA %+JDPr[\tWۅ ]4UFm 4}4Eح9{&h(%F$mDS7AQy75N;m:k=uF6-Ghγ@(Z7QS*TKDTYYHhW{{/zudtFnwڍQ8cZ&o< ve ^$az l7 6 / Ju5oPk\Կ7y׋`ՅoUYU %f.Lf)^䇐=OYE+NJ QQ gN_y~ŋ‹>o9L e% t2#pBe&”T_:/܋{?An{P`0WХ [\{_&<"2ԃ0oBAY| 8cEyȏ!c7KTjNX^36 眵,2T.xd^#[c<DFσi vvR 7u;@B}@sL3h7]6t'o߽TY> - z PwqT|Fmahd'X~1𘝵&:c#]/Y-b iS0*Ij\wFCo&C1vvk&6DݶtMv|,$C.A-ZH)=@yGen49K<֦Ik]sGmٸf؄ވelڨR ;\N;bk(c'fHi6)y nSA0btW{4~[M-X4$9e_ERL3J.wi1]nqQVLvFn ,$0zgqbn0H|[Xs%r52E[yOzLDAm_һ0pm*GjZb^JM{"d}v  ۢcf%ZGQlC`w R;#Scz:=t+*elEKMլܪjrj'mLu0%瞆v!2,|3> pT$jӉW~r0S&&%Ż))<; ~,i 'L[lj&qԚ5]3̌.Wpr-YK ~`Vl#Փ˫.xDt]#ʡ+2Y«~c齞+M 6?˥SǑ7H IG8 uu'f_A,'}SvuަuJI]UDKl9s&ܶ1] OàXƺb8{};͞P:ɚQ$3~Uf+ohy kZ!45<^iҐ6%MeMT&%Y<)f_wƯ?PaI5Ra.jS ulŁzW8=ƕص%^4aQ12Pa^ /T2]%ߜx-@WQ2 4 8~03_'m敷Ea:qRfudHt6;p$Uˇt]ѯ_b) \,WcD) _ލzpu!%iϛ d Pi Zc%ۢUEU@V{$0g&{ύ rS=]ew'`~25nts]j˺ W76&\<哬L5eN`ߵ~Zu-.8Lupk-RDae]"~\kA<1l#-ˤv],EVN=DEj, zd'$5 1LZmX-Tel>a/@ŕfXNharwge-7, eQ< دl^!_B gw½]aRˢ"͙xsۆv_FTס%iŷ (Og+Z],,]9sn~GobΣܾcx>_7^Όb7f:ɮ|*2ştuk8j5PoPK~Vpm!N setuptools/namespaces.pyVK&W Wqnn5.V]Tx2=ṃf΃g}D6zoԩǨt@\h{M?U>Zn뵈Qt[O$aI OQK@fBZKyC1=|1OvGHp#8@=Ne8`tA@N/r+-l`N NC@/T[Qx"F7OK $11&#XU3ezK4ehUFtnqb9ݵ)f[ap?- EI3d[$7d} YT9bD@֙=κͿW̩fH5ZMr$BYhX;}1X|/"6FNȂour .UI>Þ@wТOt w+ʐ=F zhu7b_GM ! /Fo '#M|N ,IJ}{ : qRZQdFk_|fHA9:@0)q u˝hp7S7#K 5e@`-ͲeZ̯ͫWC6Ɋ{Wxi"-H/GDש#t$Pt>HȣB ^t횫^OND%PS⿧8[}"kmQJ]U ] 4iZF' &eri g}ft~ R]-eZeLzlw=}vήݼ6W2{ U)ڣB q^&0tl/^2].W* UiqLX-MjgdH]  YIsP Z! v5NUvWk6[.JBS,5 xqfuG+ڀei\N`*$I5E= O_ PQQ0_@mV^ 2b #欃a-Di}/̤4R'E!B?PK~V +͕setuptools/package_index.py}kwFw ^6 Y;J[N|Ʊ}d Ib  Jozhrf<'1EtWWWW׫ œ - k1mU:}EUeQY^Γ`/WUͦQ_+[^醋ujN˴S-fQD+궪 }Z|JFԺO,vL\zu]Ąt~okt[Y]-ٺlr]@GWZ4պ % Ӈ}ѓWӣiHSO=xwIY˴?*m۫o^><Wy]K30lYIgbRKay%FVY^%x~t/lL9@Ӥ'LuZrӅjii+Yz!D 5dYbLuY9D4(p_>g1x2+ UT8:=t/Ο{?<}ihQ; Eg'Cl${O&??NDi@:ǎ'>|-6wF(B 7׀ơPSq'Lo|[&3KG_}1 7/N^|bY$LGOq(Z٫'ϟMd!/?ї iVEFddh& UOLCU3UyN2/V=KU-Z>xx#z䦕'| F>~mQ7uⳉ ɧŦ]TfLY՟{kg@lLf[^pr~|A'jUb%d{Ym=IyԬ4>fPo K[ LUkA{5H@X Ҽ<  P fz a!mqYRK[gH*8f*PU4e!"@0PQ] L菈q"p9L"ʬE@alkP -Q:;=wJTBGc4ZgjvtMg$kGw͗Nw.o` n4mn*~t_UQW"-$b+@Nv L(&jvvGȖdg)~vW]LcڙHld\;QAPOI06,Q۷-YS qK 921pf$T["`Er82荍Xsl)=iZ6$EpdRx#fsHkX Lۮッf'U=?hr8֋iq!=M'2+{w0f4y&LGWdh-3`];`pvLLM 8HRd̈́3A!me6̄Dut Zk:aad/O8m5ADhBOH3\v^>ŏ!<Z4$Ox' ,q%,ɯ?'c|t۪ lqud|un͘Sÿ=) ID됓چCrRh+ri^zx;#۪;~;. !Ld%tMmΰ\yNL=&D~-h9z Qef|Ϧ&?m-\\hyю2aM XVah;NuZ\.n*rEUॠ$>67Y@(0dmPcAQ0C.j`Qn5"{ n#":e?c#+ r˝Kd 'TD, t_/(Iϓf :lhWܻ{Y5,n) 턦E=P7ɛ;7j7+0ޓiQl!Xԑ58ᵓ;ነ)ЌSE#kSz֦EyvAxt0)*|E0Gش*`,)u27ZվlN9p((A,P f *;/e;L&in/gE'ʏCJDÕ$( (h/:~PzjplW`Æ44 9ǓqȐ3b ܻ{k 3VSO30+ &`Sj>[xOT[.c#JtkYi]FGPYU :Fk- @:-:l؂O:!б 4F౭g; 5c$,l a鰋ǪY[Ck~ز"gi=.@t;viLHk͡5=aОƹvchm;oK>a@K]GʷnOPZ [I^]=]fB2("@xG #ek7##BfD{ 򢉓ZQ(c/ƄaD9/6ٰn[9 7-KۛR6tWRNj ci[|zz9Di)hO١U-8FNhFr&V]'#W໇/j}!gQ!%ռ?`.wdH$,m< Gի/$8|:6ϴ͜BG+e:o!OHV V/e_HHlCD>4yugtpie[+ Kb7Yu/'{ 5ϭ4WϜ ɩ|>~x3-]:ӥeRإAޡ׍s+N7"aWrn 0;0E Nh(: >~ϐ`=?Jкل R: =sj.`-SS lGWh^O7:tvG@4(lw0!Tͺ#BgL5TrϻXۭ/ 1(8(mn|KԽ3x.H)'2tt39 ic2}rvK]67 w%<5;`E[Vq-;F&~ \ZݑKk2Kq`j0ɭ/4W(kl>Z4tϵs0X;6f0`"?l"1KG* HLG ,9{yh}0ge^NNp_d(3ARLh<|1ym!WjQF$&0rئ0V"ƃس(ҡ%ZV;gޕ ؝@kOPMnø ceO,@!;c+DN/77ԏ"Ð΀ Ec IN،RW RFu) (ysSf*+& K5du ma }eQۉ٤PfRn9⼷Uka[)[4 J4&5δ#@CpHDO9Δ3}Ik3jKR|`7UbWJD` /2 C/@jڬ.Aew%vl uI^v4Qvim' }$-{]1jv+=ޝ@Oh[>ȲP;.ޘ(fJ4B[ƫTty^3@X<(þMˡnE\w|2x̫ s2biLOHWڏv)`9.V@zś. W qj(BIdr#e]Ԋ"CG&De_Rd9Xr_1ﲫD:Q!p?梣\6$b2­ !I$j˂DUmJ,"]G<:$)(̛f%@d[ٲ3蒙C58Č]), :ӰM+y{ZqBZjD[߽PF]ht=D l^-ګh#ڈB[rxZM:BְK#ٯUs@9h $sFgQ皿LnbW;hԤޔ:{0Ej F߱Z2À֍5IĿ72P+(q^יI']{]ʉ9 Y&̽@UE1Wx#Lvưѱb})o0,Ј-r HvCR+}0vY32~,oN=6"rX!&nʶ bEH,ΰ?z[kѧ/&zɤ=&zF]w?TwB~ڂXTƏlNvGQJwX-uۺwl"?-~xmm潯эG7쫩Roh 4Щ!e(lݿ|`A?CMFZ'aHtf ~ي&P;euzEil;>uߢv,-07_h?:}ק@HMX[)Z*+/Gdy%r(i&(]#1f؊+t..tf\,ϰ ֗ 1v],upC*O~< >$Zu:٭C;C4 j]Hrgw?Sf|kE+YUϫjZyᅿB ҥ@k#ez!y4\ɔ?-7A6 s9@(d||vI? 4ITp#Y"(_|U1l&k; =S5Q,¡%ZfZoΜXs`8_ymsI Ne @_UD6V*~t]&ѵWzgw|Vmc5Fp/T/e_f_'z +&ֵ\cԻF{V6K0 0o܎_Xnhsn3We)9t:}ϗC'DNd<w8A=m$Wm)z· 3}Kolէ%=R"Yt +c8QZϹJ+|Av`yO1HBM(M&b^..@X]$W]2g̻Ѷ_j8;%@8 uhNFC{~HWmڮ7D@G{)xYtS3 ^/5)eZ C|]dOlʗ1kFlc5rTDqc8`+O<ڼnAܐs_A$KjJ!P?E -$ c۷$HXD&'Q"x[*^w2yD{ϋ'S~,k7 rL "| Uz٢&;A5pǛ :ܟb~өP /~U#\h+*8|A>\M '1,Pj1HW |UHzp/dL%`zCd1T©Urd.~] $L\B^V5h;qN$wFB/ ESQpj5h]2h'uao+fAپ[{X;E \R4Y>T1 +:fgtz~@ i^3}nxqmrbFwa7+yRaE<ȍFk `2˶Q\no;ڝx{k޽{{S-jo|HvNjv󞉚̢F˴R ^~Yii!o!2kHf}yI~6Y:==>~:9o 7_Qe + z%}fyJ`1Yh-yxDOqss7ϯ|N2G%IBzoIrӁ09/o.ߵ;`z4q9ap?Fc7E,k̐hI/H;>:6!IJmM?U(9>3N^ 7kznko %D|q{C;ԗZ8/=e% 4OX$.zHX?wt#S: aT.^\7~]Mw,Vx o:)Xjy09Z;Bc  uh|n::Srw2>a]JѹT o,ksޱ0aLo%ܓ @>hè YU0L$rs :Z:(+V3m^S rqTIBjq@ui;լ!-ӮWn(xxjk&v  U{3 кOR6)pqs|+ғ0w4/ؼx\=kb:y^/o[ R)|Q4.멷c\vR;i31>VUEyF/F -IFQ&DчU]fFp Ftz]ǁ]N3r4%ȎjS9ߒM\2\vS2]aa3]N;\ϼ1kex|A?Ũ~n7˅KMj%`Je+AS%ƢBdH[ ?6Cy V(ퟨ"Zwf~c _HM|vC.*,y Ԇ<dz*}Ӎ2L+0Q9}j+⭮ˍO!AqX&4r6U|4mp&?ˋqB:r A rZbͩ هXbqόw(4L":XLԱ⤻GeG9ڀ#Ti=DcbPvCMhb0Fb.&(Rb mcNN5!m0cnJ*&` /5[W5K/~ML700q9᳣Xgz`K8IPdmXA6էU`݁&cZP}Wd #dGGr)RZm^U./1VGۆk؟+ۡ(xi[(ɟ5rnlcR v.m$pH%Jsb+\K~]fq?IN}Ȥ$؍0:Q t䮪p!>3ႿԑDz䦕'|8r;,?if:K{߯;)r/$UhX\y'/+(2+9@{hT:S#Nr W 43+/_9; >BcM)#/xݻW;ȓ&*l( kxPK~VJsetuptools/py312compat.py= 0 wBc&v+c^1$r޾qLC5{8y\^9:02F!*6 ݃!/A_!B2RΦIgze#Mm{ yclJm*; 5[zB][*RR13a=7Xn'j-TY#ISv]]Z.[u(My,jDQWv\v2k`Śߐ%-/Ւua|>7 ;QL,$7~T^o_@R,Aoyв\,Y*3~a#w !/YDG8~qň' `ߏE$WVH{ryhJ$7JciHIUjɰ#s:5x[h=~4h>.fq(\و<#狺ڊ#mLK^H|%$8p u>*&[kUùi3V?!sc2;iN'NmhRZa0;X ٿCAJ{[W$7 >$v2}R1a! Phe#$s+Y: Mi%L{D_~T踲wG(W{l 8[XT i;Y1Nh#=GϘW؉WT|J&JI5E-Hv A>` A P >Ry(HaBiOG74JI,zrހɁ.( mZcEY"ze[eoITq4B4k *dJkO 8Pf9+o-D Tix0%_H#I&IjM~R k4JubOC4zʞhĐ?s`G {ϑHk<y@̎7xZ\׸McI11+mc˗&n*&Pqƥ6ф\K^)Aճ!T=pĬI ko]a]٬ 9?sV7G1(ץC`vMTŞw^c*L*-mc)yn 7,M1;TvQ RFK"7B۷n5pZHL ,T:Hdobjap}Q!JHk]wsT/R bYS4C9cN\Frs^y_1JuV6.~8' /‰g1'kr{hF nV~[Opt9٩ /ar k|QbLj2zHј|sm_z7ϟ]\?BN"ðLS*-J7:`5JF t5A$R=19xڔmQ5?A%|E`qE=y~ne+k\8wܔEGId_/?I`%*b9PW:I=)ʞ]pӹsnqxY } <8ݻZCNV7E%zS,mH"'mU|-$&"4Ю<4e@WΡL# H%؊Ca1Aq!uN\{ha2:Hզ&ܐ[Ł-}&vIA\7xFۊ'zeꨶi3;RyhZ.U^ M|(Z>cG;cFghzCa_"/{Ù#RJ^3kAC9;o 4S, iD%߭r-={ ߋf%[wF u,/6NM`8J/*YW WjT9*P}C(KEI q6F)J,lN3EvC#j/"{@i뾐m9zHp[[!K%c*F,&xf=:zQ0z\Ժpg{cGFSaBG"XaAT;u} un_]>u!G݄L@#E;!]eP61 D8ʅWn}?h 4\ODwMs0R@6׼_,n“. !<`5TfK+01cb8uXfvKF|bɰOEg8~.hd󲨶;DZuϫ>}/SzrZȼiKG4 D}8q?$J0cQ5]k0ÿSOol7#|$3ɜSK9.Xozs*瘜@@@}f:GJk7g7XI.XS66CRCVuT!Q`H;ϰB1#_NӴQW+ؗqc—wa!]ۃ辠 ?*gdg$rI15e{Z>pI!Ձs{M K?\0I c'nl&FO0@"]Hxa2 M{HQ7P.QC5[H/Q:Bu7+u>M [i E[G&귘B:jba -2H.? J[G0Z@}Օx^^Vp_0 'ݸRyC^H"k=&R?iTr"5Jtyr[4dP7 z懅0Iab %.zn0Qc$2AbF1Y&PyúolL)uhex8i[:62@\h+JK{Lk . OFѷZ2B0.66 H.|˃ y&y\G-|Q<ƒ?kc_7nb-$> ~.q HM'&E$3%]4'1MdK+zoM 癞6DP=::_ijNEϡKS1qzr,ZusX/A9~N‹^r@Aqwݏ'e}xT(QTPRT}B=\%p_^*z >~}|yzu>޾|/^zrnb(c ]  a̮H95rIhpDWA|-Z?'rQ'l>nS]Srmrsš:֝d>cՀP0^K\Wi˒ΩrėϺ딉\vt͝zע`$zd3' F+R(/0 2=OÃEVF'R*R7OcL)3܈ۥzGi627 4ͮ(R^,(3,BWe=/>)Gʵ(L-++ N1 }qk3tqWyXRn7l <t;?(i^',q\pT2_FRejR> 7$/s8]knѵ,x+zQ1e#ׄ{[Xsɋ$WE1Ò/8Jb"qvse %d1 ,6# Wx4"[RWpg ,XVq}<10ƨ~<Zz$w'?>>;}e Wi݀꘣X!)EՇ'?:AN2Ɣ3e"!xq1:Fg=|ۜ&=64<VvZȎImPHaפjV8GkPddʖA.z.,y&<萖> t@Mw;q>/8Bz*6W;Cݮ/6,v!Eb6eZ)rj< )mФ9sVۉ%Zicd=덬a]_ 77D:9}=}:} a)+(8FGK e\p㠐R N@YAd2r ^I6Omny_~w ikb\hhES2Nto  #[HTnY f?rdyHtLӶրݶlhhɁgEn˛k*Szj cs#ڏIQ cf hů]E=1$=vN|=%ȇ!6`khkJ)t"[R"q0F P6c1B[ĖsHJ-NvZS՜2/PtB0X('juBM:nTdklM5tɥFZHR `L&}(jN>(8voQ )WzOJkvFa2~;5cF\hG7EΦOa26g2 m1H0Nuzrޕ/ll:W|/Opv_\Og՟1C5 ρoTI^=PK~Vpe !setuptools/wheel.pyYms6_Q.C1}IFeڸ4̴1 Z)@rvB$$=&ž>"mZj\. gLEh.u]/m uJ5L^4(׬4ӥ.$JZ_ȮN颖N=uX kNA$mXvͮDuEoT)0x> ͮ*Os<[ƪ+=O+YS~u=筄( jER$2g%1 OL 5K](zqXfSzqjC/ԡO iFyѯ&g~41ql>ٶz,5^ۊڬa{z+J:ݼϿlg:-/?lRM0X>M㨹'WQBsLX(p S a}봔ml=SE]d󂤘xnR#Nw_f-ljqSuUOWPq"N(zW@ u M 5P E4g@\8 {ܧԹR"ƚ (E+)h$8 ی\w4>8(zUp!0K1| &ӇHKN SC{4 m屏ZuL @ GNL۱4 )emAJ5ShCVޝЛ|̭psY>1;݀J3<^,lP(xY!84 PoxQF㎭OHg &doY򍔵#Q@-0<ycJ22^L$u"qBa(wAejt~@AodFqߥ}o\ۃI?l[F Cނ#~'R8Qҁ TJqP<#}v86:o:|G7D:DhAy5Y(AbJ.Ef=@(ȭkN:F`6sQ "f^~[n RLCm(BZlLo4)%؍&&PV,<$“N?W@cZU_ΥS?0-&w棋{j8aY&Xَډ SBsjec4tT3fd/FȨ:{Z2ז",bD3Vq@;Sǹ͋g//9It)Gn{ym=j|ƀPU P B1{![j 6֜!ZdHGO2$dLO(7#$1<ނҀKћ;5cڑ  THY$t!aC)DҲΌo5 Ý.AW>yO<2k.j #|l70k$j@+t@pQ-M-ѡ.zdY[[nV94H$-+; XVcO T  kqJGB^օSسU?]Ǐo3d G@XGFjDgKaAز wNK 칛O˞l|EWW7ߜK/~5cZ9uu9ce@ڜR^q=^^Y_CYL;=qM٧07yݎ?XAcNat=\.mٴ?yܥ^̕:xpTDH>ci#z}zppᾳ@h4_`P24qd;8 hM+y)| 7'fE::i;~)I0 ,s'?s!=vқ5?kikS@ߞvp;-awcanp(9丏aORQ#bhyB6: 5Fj6LxI+s Evq9{/IOZ3ͳ_d]vy}&{ ^8;jK/j/!Oiu/LԈ8eIEmUr%ggLo=%r}ח˔'e*$?.Ǽ7 KӍF71GV7nD'-)e%˥XySAۨVTaě0bK~mN[YpqS AvbL|Cb[x^j*s=@36_v f8Uy.:D]:|c+nu_葉27R%yL(9:(5u<hjbNYvojȟt9`.H7 H?J~IXklBf8T#Ǡd'% '0 %ے%WUwU;uu\f "5OQv'KX*/1p'F1 bR<]!~s>#LZ2ԧ~=F6%Z*VX]?Yfы B >uRi +u0 'uł?E!#`V)0k<ν;A/qP- ʄm3kڶÒMߚ 'rWp ֏ډՕX2%^v6×@B I4$ϭUqM"l4DȲr-^g@A YR w t.vš;Ht2IM24Mm(3sC'g SrJ{NB jDtsєˈ=Ad+o# q0HΑhBy(`T(847@QC.”շS&Mѣ8½PN}\o¯dB38Hk8PTxЄD,gJ"VPi[|Rr͓]D5ML1u /԰3qZxQ)iqDUdЩ?t,qTM 𳻞SF+Nt@g}Ҿ<_IL&m6+mi5ѼZ̙):8y:叇%ujvBqQaر_LT4 [PظNK!#dTGHq@+Q 42i7| EEwm3w7/"v'{f1sBg03C5_{NwAxƎS.~O9h׈[ьM00Ź ă&_E@llo" 5b|[S1 tL!vAMCeze0nI VXеM!L1흏q]kG[F'i*f))t ʵvW~eIkxU`?h' 9aD=Ӝm6Y;-tB05oPK~V O#setuptools/_distutils/_functools.pyuPn0 g^.j v G*ڒ@2H?l8RD41Oi%;aG4<ȖStbAiA6W6pB`.>K! 0 qD,GG[]gB>Q~Y=p+y S! 쨫.O8Zrom'Ѩ*uGɕbVK&m~PK~V=&-$+setuptools/_distutils/_log.py-/*QOOKla| 3HC PK~V];F&setuptools/_distutils/_macos_compat.pyuA E9Ev0El!UeۏP&OT2WS+ \bE̖S:Zh%xn hkȜPkTv50e(kOd9rj^AQϷPK~VAA8L&setuptools/_distutils/_msvccompiler.py5^q.AN* Oq2gqB6`p 0" *xȫŒZVl01d#"1^08Ɋ` 緜wͰ0Bl]uK4閻zD!D0kfCiڤ(b?60 8ɀ`^+s tG^MyDFv9Si$3[vҵ X,r|/Ԍ z0|Tn]2X+ CiMdWY9 b>g GpyO >7|5~ɽWz2-897o'#kX\v¾k]]ꪔ//?JʁZcFˇ/ >7G|]KbC0ЏZx2g6dyXХœ( {i :j%@<һ;c'5M7knjvA{dt"Uj9`/k'!#F&m4YK|My!6 YSmQ%$oмߊ%%sde(sxЍ;REjfx9P6ObG] $ݰhf \WYJdp$dL P MZ60ICp#-qS@i%<53%Nyb˳ۤ3lW ǗCgй?0LYd.-H?0-yU6W)z1O2WFsCt+Ӂ7u] Du-xAT{ *MO>f<|&w[6}J2(6=[ytI7e%wY#6q4*$ ?0HVI4nd޶6Z,'|2gŻ &7[p^٭r0|nY۰Yå@yau>mTWSszTî˾d`VM%3m|9y6Z =Ɵm ^̲$e Qqۡ'F[08d0m 9:\.'/NW?Ad姘Ҁ@\+Dr{%WG,0+&/~0|K.92|pٔ<hETO5_/]XrztnjlV|˔#m4݂w- "G]Qb~/M ZEIC$VUuBՠIEB-,45zJ'_FE< ZC*d Ql'm0C h=K𰈖5FYoA?6D<*s\_E~|~8}Ôb R$Oem9]^zL8yZ*F-aMeH&D|_t(M#B!Eb.ԵTD0 irJ|-fhgm[Gіpm;,$7 I* T }vO ~Prv}%ЧeQ"s2["3Hr<97÷whȽ<3"BԥY|wNmH:Me(Pi<-DɣL&) k:V! [f")8x4E@~{E5b;%ˎ[Fc$ul,@ցaHqdd1aTsT=jWX;" .co=capru;׈EڜX`1HF1}F;Hg_' W0"dmggnnQQ ^.)ڼ~J ZmR[ʹa]$*l+vƯ}H;0+H\ܔDs$d3pK.T} 04Uƨz}i-zdYE1/L{0 W_}cS1qgr.HRU*IqO4 u4qN\[K›%VdJgIY"[$aw0 X"$%*ʆقZU\}'b%Rk&_3z hMѺ2ؒa$`tUƥ"|C.b{pm JUw݊4ҎoEAX wGRB;2z>x&7ibY_5!53:C zy:{ @x :h]>C؎$KPB:vT*׮uW;Gtځw]4 O_0 (ؕ*?bڽcvAM0:y#;NTծ 7޸vT"?:lDN䫥`Q)%S&)Ggu|9 \)G`X1FOyr5ɶ>:SO=@ ֤Ob';xhح+Px4@(;{7wQ m%ؒPZ*믿;ij!&@/Kjs֝'aOr2`Iɾj*zr8Xzʟ9 v q{){"Ww|7:-kvKC)=FK6|DDJ't훾z:n @lV<[( JZ:&.i6N+mkY;2݄awW3 VnаN+=*ym]`y5Ϻ@*\LuzaU]2SF [͆oL:rQ7j?j#4!}ڂZŹ+got%.kvth$5BpYHiR:Y`tO ,J(nD浏0 ׵T-eJ%dH/. ; o ēp 6L992ٻ:٥Nq7vgn!o>Q WG-pI(3 K/Js5)#nV~F5K*>^ a_Jכtgt[2^RIG[>{O(OO_kV.kIWϯU- Q;Ъȶa/s?LT_3Ҍ"Ĺkj~kJZ7VBe86W+ƍ9*v tF7GoT޷4 =.j8|f҆ p.E7lj%7fan^[٬ýSvmRyZU#H~UeaW`=@Դ [,11ye H 0#Ve"s4`; ṯ(7OFlQMՁƹR>`T*7wyc 1R4AE#YdTV.K>;'Ǫ5*K;8>NK]|E 6H[)q_@C6ޙD~ێ/#QzyiJ{PBkAq#h?ZSėyj=> 04lcإBz@K昆Zg.*yB:6E ݢW'#9uy%SI.CrCy C]{=` gHEPK~VGJq |!%setuptools/_distutils/archive_util.pyYYF~aLhqv =x1vG  ْz"69y}OJx X=HduuUW8+q*x_nX[r8ؔlm^A6fbkϲӃy4lT]a ۲"(FUI85ꠢh"vE}zg ϶QW8.ofLT'عmLJ\Nc 4wn;+]䟒]@j-Ě-axU 3"+`ދa9D/};v 9=e45]SOOd5|wI5gih(7q4lr4A 3vA/~qe`[_˯ِl)k1wr%x]/LbGf5vxqf K$"nGCm[qPi.`zR7CGI;TF B4 S:h!6@`R+p49QOP"GoBTHΩq\U"niH*)fdB7Y7D?0DKk&TZn0@׬jj0.P,Yiڱ7Y%ù+6~A%nQ$FV f#\{~`Z &J( z^h΅=D39|pE8M] q^J lSCp(kSwAzov跓hYG B4Z8 %ߘ7/OtZpE/MNw>vn6ڧ1; Yoi(,]Rm/_$S;tk#=_<['Sz~;U )lGy$B¹xL"9]A5NPSl@ {j3b@DF][@0w\|Ui(Ů ?]]1uKtplx[L 4-LD@m @q/I5SSxCKsh]bZYZ ttic=t1@5[YD(J{,5 4&!Ŏ~{6X'%lYv8:Zu]Ǻ~[}<L;=)'Ǐڶ#ؔRm!z «zuDšyj6U\Liv*Q,M UKEoa]<4qaba yb"ଉrW*@=oټkvj6@s6 }s#{@79O*N$yn%v4E޿կ˷?ǫ?TX^4:x\lK)gܴEHDI?Α)y Lt$[nޖ pZ PAL`!"[==@Jlԛ FK]qDgjG%(Ftt[VRǁ鋉﹬ZLHt@)4x ݭ82hl._rho(}oi8r_[u#xN3[Y+wfjfi'7G_Kn. 3Y`.\Iԡn&zEm}y2 ѹċ6ek={cBܿ|Md e~5z9KL-zn<5|!X hb'n-YoNzv^;8Pwii?t[@6`~:PPK~VĠ 9%setuptools/_distutils/bcppcompiler.py[koF_1QJza,nn vS$a9Ҳޢ}sf^$ml9so^.MۨLIU%e^L֋MY4*HonHąPy\bIB;bd֋]Y K<{&Qr\,.Ļcۓx}ʤGy(tYDbk !pe3뇤W,/VbSU˄nkvU͔>,NS@LŹ^b>|YYk{IjhokUb+Ʋcin/2e!>m ǂ1܁fP6&nLqUWB\^~bʺvq]ze.ֲ˚JoÅweDVSq_ ﷬3ܹejYe"ޔ3na͆Psj ywܻ8j郹җsDE@:M ~Zn*f2rkk6GN-h%R¬'Z}TAT*~)I$y륡w9轹 Qy`޼}(v4^]K(qRFAqkfF֐?Is()Iz'|w0B{EtZ; TIq-A`]ZB@YnYAR"h`ZW[׸JvxOD$HxLp-8 PJmz pY(P|n!Id$AclIvlL&/[[PY!N2>$RS'50CDR HsB'0 HΠ9JC܉C7 id`ZBq#XLkH% ˭&if{z-nuWU5}0>>bG, )\S=Z̭!:D[Tn$05%LmG+Į8ĵLgVY6;kW,Z4<**TلZfH%mK-_D"O-#'xf cV0unME<598_Ȁ:F% 5Uvgb:k)sEj'+(xZ!61& X遁STd K;sd0B. %p9|AX5[Zy;|=umX#<#ޚy_߼? UnRm#.zc}WMÝٟUvYCf~׽Mά\~Ox!-_+q7@2꿱5m6LIHyԥ=TEW|wxƢ)Ҋx}W ӗdO={aЊ@"Ƈ!HB.a۪$ Ѫ7$Aԁ/uTsOOr A'uu\ ap=03=Ǧ9 gMfZ>H&W >B&ֻܹ*Ȫ' \;xaR#/(U`-p^Wp C<^ YǫGB4h^iǁpVprz ZZ_l݈8eOrBn)at=Y@.:إp$6=&8֋QR,L ({#ǁCms!K\ޡRD  bG6]"]nkH,/wKDr":a)b~:V.7)'7w$DDJ)ٖxR8fh5Gȫ# ˡfPH#.ߎ풹M 9NjTaMұwNF0G#9VY*!LI .BɯIHg4@!AD4TGD>$qU9Ч/ŭrlk~,9|MǥZtAg" m7 qU#y_Ы߬pn:Ćgv<Ÿ;$1y7T7LG;?bLK Xf>ioҶ.h'v Hugx[8 ..۩ǍY!.]z~eD5S<|y_ll(`lC:Qafq>J#]H#M d>U6Ȣ]~g~ͷ 0TT%Z.2(S w<ց(3>)JN/='[!Sr8qI='G*o)ߖ.{ȼ$3i<)3_ bؕYV7d"}q1/Eq(0%0X?fnTml`?"3E@P4u`zp[FSc,%K(.. /0./TE'oFB][Dв%56}HjOcbPt_}xuӻ}Ռ.M$/߼ YϜO7-^MËihc80&7J؍H2{$dJqkbmH ~mCɈ4_2 T|A]e '1ޛ 3LAeB:ET`wxҮw+"ZJaz`P/#Hi.qn@ᄭړ A2TQJD.[5àeU-jTӦyu,{vFҖlBqr[߭w nDnipđh#>#¤%t *[ͭ=[ βҏGaebE@\J:1c]V&1Yw=a1L4#[)F9.z_Ǫ͘`ajz=C\!i=V =!ϷALӻ.Jn<'5!VV(9c.muJ4ΌYItwO>>>nh!m Np3-#͚`l5CY ]7%3B{)F m3صzA%5tq˞W &SXJfo -ɛ`JJr✉iy'B'wu>wWBz۫wvHnrZG=ٛEÛjy_g-G/]#EKN4.$9ϞSc*c3FM#&8`<M)֧Æ:JcwY$҇J2b[␫-&v 2HXޥ^ާ_x 䃗B+R: D-J,+]/(<]`nUfƴ,V$K~COret*1#t 5X@O&IK.gR3(Lֿ'񕐊keGМ ia$ș_yD^nzgnY< !x̏>9/tۖPK~Va 3"setuptools/_distutils/ccompiler.py}msw dYJ˕U꤇%Ŷ\JQm,  }Rׯ3=/Jr{."ALOOOl6+ʮeխʵ''/ʺK^g$,sI^e]WYnSpIYd;4-=y]$ڃT6um WfI5mtNmon.d6dڶiDަ' ȸWKzMY_?usS7CzvݷM_-d.u @kN_nnOAQ]_K^Ƶ˶串}_ozW,^YWͥ_ONNx?Y:|>̍,rƼ}B˂`]`ղ*,;r*I>&ʺk.9!ٺ)d߹& {) 'G̓p⪬$&5K)T`Y Ɂa=RoVU4_JܗUdh3|.{>_!uvD4(Dz ^(Tb˃֜oY,y8mv@T#P@6`%Kv=dZЁK K.Kuss\Lwk QsͨU/;YduOD1k,OV?` 1Ua:\]uEFԜ< huze 2DZ7u:ڤ_jUAX/"v vO  V0EIL$pCnoA@t1O@J3,@-u _Y"TvM8D pп"|$w0e%~ iIR7=W]T:6]L$#a c#7EI&/Ks!$[]T-Ob%K]{혈 Y EgRz9.Fß_(Dh3e $ (C6p^E CxZIXġV0fQДm[tX v Y_]Qu.X$>^ W-EF;e higq HCYϯJf4!a3=fjYe?jN&H{ țx}{VђMM)"rf@ P'%I~J붼iA<3(`N;ZH {}PT~+ŞKXGqL4,11S 1ٸ9(#.Uj/ؔԈc+GkSh=B(ޣ% L+_?xk*"k Ru&֥b!(`&"]82_'(yq5M`\Λ 4yIU ? Eձ$|>fFʈXdcc܆0| 8WO!!C3y`[@Õ@w{ _Mr]Z5 >Zo^6bl56W:AaK @r5SھǦ-Ps$Ցu` ]P[ 3 T0ݯ`C "C@ hDi>%A2[=ۂL!}.Esxnj!`@̖?{8~ M=a(_q2'oCm#yw"i5Jl- R&ay6YJ&ɝuʳ&Y5f` A_rxx汅W$ů+xIƯx)`{[vdSHر4T4`XdHH1Ft,tY20v iP*?{%)εy@KĀ~–gg-IQBd-[ $);ɕdd"P`K@svUUÐp ܳG4bcG\c"AKvc)*Nab ldxQ+l0)@!$mdz,qh)/fj CGFxx6 WŊ] hhfO&%Lh=P&0Vx1p#J]`9H*nSǸ ;2;_q\ސ] xœ{ k]cSN!X;tLAȏ7@+8L}u_fbX 2y')뢹qlS(b(ƌ،А2eUx@{O-K@0=CwtZ֮/ۃ]Umrϒ8Y 9V%4;ş,0֩*q?y؋l4D*$[>{hæ|ޓ m;1%EhSh=&Scm~]m)!f 3`r\EtHYbCP - - w ,ڸx'Rxaդ`Y+-Z:$n?mVgh_+ ]' ')EkI7(5hk f^>ZOoWkը=dOHRĝ 6Ud`bM4CiDYW-!`K ~yd&x4`ƙ["$1pKXNdK0U x0jBwqze|rEaC4SBV~ǥpȏ3ro*Sc9tl$ȒRL̉`Z4yКF dcZ4 neHjL+?S|t1':?ppߪ #u€|(1K9`>7юQTNf/_I ]Yp-<<tJ xim5Ō0ZQ.- )腎E¦;t9֬3*%hvp–4Oenҩ-:w yClgu .jA>`Ыmm?6c =JIW1FD %8؉Tdn [2z|ZC$!Z&ϓv3E6Lyi٬>b'k˂Cc:ň@FYUd;DVg3f.f Mġ_ҲGl`4MvIt(h.BTxg CBl{"ne5('$ΒvwO~$HAaТعg \V$g\R_?к=.?J|@' N&{3=O93wKib:CUa˙9%5:qM$I0~ed&Aݮ`P|=z~G60 *P! 1w B9&Tp񌚫SNܻ¿nnv!|8fm ڬtr!8c~"mh/*G)\}7LQ7h>o; Tg:B>| 8mp!M?;`D/D#AXh훒gmH HQ&LOo1*P@`I g P"ןs|n:8uZ;Zs`,v%Q%'6It{Hy8ŠVd7}Rըzw-sI `+H<3 mH?k٦S8 `<&i1*тSmwDz4~4F;1i܄?\ mV۳kJ~Ǜ`GLkP%Q0; Sdgbah̲\&EkyC&+ #L- ; G{X}. /j:V_tBtlE[}bL'Hh"M5CaLwM6)p"s|3V5j%u4>7XYȲ/`0#0oZ?bse:_ᆳ =$j/榳E8X)`gcgkR P%t֓dޱgYNs&⾆-&bh46A !Dt4Se7Av7Y9˘[KקRaLJg11Kq>^0`t0@Mt p0zsij!!`Ҧ_O=(?ٕ 1䙰3M6mb BmTd 0>6/g:|n5R6Qc#K_s$@nڲ֣;2$hHi}B&:zէ5{a!6]L~)Q5NٹxT"8|5( ?!0wK]LN? iI\Uq+v#ZG[tI )(ZrI= fYAM>X%:oǸU{sHXbL9 Op@e [0D3>B :p=8f4/b"xjd :s e Kh 1GNl?! 澊ܐITYơTHiXZ$GAdb4myY+8^BޖT"E֮5iLv}f\8]<:m nQ75yI2H\*$`D嘸jBU> *edgLvtqoo$RiO#f<Wqnwƨ:|Rsᨮo@7G$ښ +Vj,2#h<(P*>*I;$1 =QoΓrDK`Ա?hϐR"'^ۨG<:9^G}?h`|!*PZ"~Pt9{7Q'v%/&JP}~IJπꈱ <αERΘQeGkhn.Yo |b::@FHTʗN J0}ע[7CNIϺc(>qO_\}Gm336߼OF˟|ϿKlPrX 9=0#[ +>3K,jlç(AguľZD/!5b0@`x)k;\oEc1h+rco6%D^YK16mvՄtN .gͽ.*.(AgIm]}9r| oL%yjI$H%RNNkWacT[uUhZ).`wv784]Ŧ:Ǭcz'ؓ1iw[IXFFX7%jH @㩴9/|1G7Xr7ɋ]G"MY.,QܬPxf<T{Q7i_۪k.t#ݿBC": ~$:#Bcط*%D~[v3)3~jt(z@EbQ 7 St > !lg)~[%^׹":VISf9S5&]F[)PG8 %U{s<xI#zc^;]p"tuk%YJ[b_Q>PL"Х9_{sAX$Urba QeG[TC=a)ڔEd<#W/ۦRNG:^ UxBJ8דr÷jO]PSLߜ| XYm>1zSaѐP])bG\ Yd_'vd\k1*fZwPDCoISϏ:@9yrӺmޥSszrޗ= 2, 3Sa1eʩ/!ɏNhG|?'1ċ)w G/3[/l%aS9$oNmIx]djj+1L,~CRԪ$533AC?I;$ &nk_z%Vc|Yf]ڱEHBhLC7ÎSVӻ`ՎOL"Ct7 iHGPü8HID{8Rd?ѓ'Xia+zSj+]ģiDZpmxja!BԳ{gv7kF\3Ƿ밅 jݔOB8+[&9[+Eut0]V6)&(,9b0#EHhJ u_'SϜ]c~#k+}n\?woN佸JqtoY7kz0Oz5O\Uy2\V|Ebʈ@}Oy'@cWޓ$J8Iq1 ߇Ěc3fka!UݨM@StVgE< =HtSQkۂq-ߊ&/_wb61m}V vn}CPo&t˂w 1f6sٲAI(Me91)roh6G݋2鲟nTXP8SUsH ӳ>0ߑNfǥc /|Aѓe :qzݪA7![?ϒO=ǐXj1 L-cdW˨NhmL=3fԆ s |bFppUӛ8BKi3ݍ~m,GY[ 6M.8 46;ʞG]ӕ3hq&'gvg`* /)FA<mnf;!Ƭ{P|34$'G3yYetz6Ra T^74Sxf/jh`lOew%`M9\=B4u(G ݶNq ٶ{3\< >0O9m:+̢(U꥜b(5?\S:Rui1'oQ+$tr+![PHίlm~I `=ٻ[:wcH]6^=@V,ϵKmz~ߜGC8caet9֘"g>vZ"L=(;4_hg<鏦wn"Kyi )O= e9 j<]Dd4BkTS&o%'y'!uhAsz0' T4U3"%:GQ9Y->)7 {CpߖXɟKJ^<~LC@~1烻uʀ%JwoI0Ff]qy=oCpc$bŋ￷i+'~aW >o{ABvVç!͖ٕvg~YW/hy?rt4:M~'&gY87̗\o J$S #n uE6G03q~:CXF6WkzIJZÅ،qE?#<q2x63V1绷{,h/6ⲙJ꿘jHlvb< E9tդ_\e&IxB)nEN{{2`sl0W5Η6eoJlإU4n<m*9(+0x_ 65|V(W`Tm3KH3 N :I㢝&͞ߘΈERjY/.P)ۆrbw'ͤvJέ EnZM\56]XArcx}j ÿ=68fg`C}fHRrw~XRh0S2Ǯ0{)*[d4P]9D\F='3)O[s^=bzX-7??Z=(IKz͒}_ toMc}[,ކ9X|%3Q/3`LXjB65Y,i㤹YYZ\2CMfL괽ldIDzt䛤%2l~D ͻ\Y߂#e!-n^j ߙ|ܴJK^.7W }GY,44j:.uJlPk"4G'rxKR-qq-U SxsRŬ% ˟^ )sj=],q& ٌ_ uѨl azM\@ۗ? (#+s׾T2 .HJ15h_挾 lP|WZlb L1ˆdѵxqM)r}lḆ0`;d*՚JqucOiԸdHl Dfy T#ͪLA+®t!#2:=c>{uFDSqp6B0^xفXs]H5&;㊺%qA%qRbϒqbe *D(ǎ%,Q2[33IJR pAP g!R˷FWxltq 2Ru3(U>hD/G5uG9rxA٥@2]_7+]6rgj'kLoBnT!񎐕Z* 6d hWL b ^V*3V=wߤe',9r|-1ͬ)* ߸4q-HK1~+xfW0Q ΐ> rFgV7YeݳdF~46IhF_vW/؏t~xPK~VÔ^ZEsetuptools/_distutils/cmd.py<ێF}h?`:g v@K-$Ȋ~7;ط`["N0Um6ũoR5zݞNyS΍.7}ӣ"M >?M$77թkA_[[UsImt߷Q[n;|)|.Z׼/ՃUꎾ-,<777LFx3CÔ1W >s19NkvO/E^X ST S`(sRQf-k`ع@G!|WCUR%-q${A~dK Llm d U^W譌\S8%D4[$UOp9-]Ȃ>W =(e&w5SޜUq$H/1g)Κb6[^ mLvȁB N8 pH2pN֥Zk@*;:ej٬3bP\,wQtSľa Ƚ{m8gqe$cʮo.:E ][^B'kjPe wߓԗ@E8P= e$0"备c[fLk^  - }yR@x+CLr"Zyݦ-PSU3vB&Pa1dTVyn'ۺ%{xy{dJY@F@`6S\ L> }8 AroX)Fȅ."n }Fc uR\/!pGQ{m)CcuP+U=-aaqW`z{6L@!|x ,TY+f?%c7i%ğeɲ„ьY4y_1Pa͛O`3UH0h#"q+EBTT'UѪW A~c턣Fݩ_~ f5,;¹ i!~[ݮY5{`&0gĴu1hf/T5+~.\Xy=!ĆX# l5Sߟ)Ĺ=1nСó6930"DxhFdF+E]OWK)4[Q\[MUllv "xG.:F6P#POK!*wĘx8gP yq`R6G j[ .n9;4DRB@V-w|[ @B7&ƋԌkhKBv+K\ +ЏZeA+_H =xysE.7FN5 JXP$TsLBД5Ak[4u{~gA\WB͍VBBk X@uh \@akٸ/ uWpQ7#bWc?C|awtX'1HB\f54Z{ޞa 3xrhr:lqyZ *ExѼl^ }Q]s 11N6ѠsDo7I x}""2هPZԃ P[:̱!A]~0"-pQI}q`lG&/Ս{J| `$NB̕vCp/Ʈ$7LӮyǪJP8Cx޺Z<;né#pqS@KctI F< f ?ƾE68[^D\~7سOFb6D/ խ~iݸ+,ܺDhmۗ d,w|Z#SQBٸcLWDD;STVf.A @|r+<b/n=,I l'B۵TqQiكKUHJf?0];n:U`AUV-}pWbo:ͦt4l`:zZ5魣 ֳE+N#i+Lk =4Awr(y |ܬ)omu?SOnIa 1INl`\YE4H|lE"$بeX#3X>C|~xNA>``TqQ/_ͳETNPi 5?]3|# v~A8?$-?UX > k)$y,0.,X'U8ѣtLy x6Ee_( T'ff`F~s[ eͧw5)ݟ>g?_0SY}*! RS!V#m au.UkM]&),j`II:hjZH] ḩ ׃$"ʐ(Z!zNU_%""8&$~9`b5nTt\msgҬ1?9&0{i8:J7;ji8CNq|i9DnFdb[Y?.z3/XLdz98q'f,<} QMiC1gJ&}Bm-Cf&xk4+VQnHܯ~JI+K9Tq՘Ɗ\O.CTQ~;E< fmoU>:1&TRsFzNN9wraZw4+DYLHi I Dˋm$0J* 3b*lWb%cɵ:Ҩ"Foq-,b}ox<^aJƯl]v-dbőeHSwseJy:Ǝ)2˻}gw9JLu[,z_1HIR~:!(i>\$au5'vQ8`M8yLQ&:-]}rj2u |]2jol׾& ^#~{ˀXV腳om<"Mfo&R]Uz\q3Flɥ[$Έ /jz,=,wQ-O Zo ^¾F/ @J8hۧ֕9Y Of{ rg?XvOߠ GّUwx×uՍ5ҿ!pK̉rkdK!.@tdxaYK0OةImлc]{LdEǶw__u ~gUddvKvLm j'uǜ qk[1V]B9* C*SS9ށy:ֶ?a}n]Qk` tpPoOPxzs7̏],𗴌a+;ߍ.Odd;iz=pO4 +M7rB!/Eβ(LG{ xL< ._{8apIKYdόE_36}D|e KXN寧n5yyT{J1hPUhEH6j w8\hON {L?8;Hr{j1'%ivvʡb/ Td/e4㑈 BqLfOL01w)EiLB|/w EUwRp zAI`&W v5NQ>4~j+϶kx&clH dWSs7;56Ų\:@-2\J=.W'f6=R|VVu7PK~V S/setuptools/_distutils/config.pyXo6~_$yr\/H4T3B^REgL ǜ9iAqNR˔EGˍ}R 7*@ک6@ 5K: u3b ڡX*#ld v9)bM &`jOus%y40yf.'JK^E}}|r>8l\ے} zlؓ])=U՚R-bzji֐206ۮZo>>Pl共4J RI&8d6L - UxGl' mt/%+58-df2t,\uӹVLn`$tCIwq<㴉9h!8u0qV5t;Gd쁔,nӻ!8 H1F!g l8B T<*ala jbK'm&QzE7dtCAa7F9$L2B1;ܰAXopM|ϛ?+MzپrSv`bc ;b{ kՃZNX#]0?w߃] qs>DaG4n "~0N7c3ϿQ7`60 i'~a㾞^hf:OR13isNsַVܴz@^fE.QOx;'u ~b~X[>Ѧ[шHUʹfi1G8ܫ[/3wkBu9}xo&l qL{kEԠeft#0pmѯs$PK~VTP $setuptools/_distutils/core.pyZm۶_#*q~x26mN "! =`d}BI7mBb`ee]?vyiZ,~<(a$S AUr7bn:c{U%^!QW-p|T?t+ڲצ˃AX)DiH׹/jgUeJVo,J4M.kr!`Y;Dvu+LlTb\.|la\xr7տ yJzOZc]x-{g36K8bke潰{?^,Rr&1bIʥ@˖ù@X`{"0Nwo^3ԇ^'Ţ(gE!w鶫Xy1ŁQ`$ޡa~<Rl[D{%UV"]-Ot%P.@7I2۲*rXҚqphal[+)Yc:8uɠ`(Au S(0lM=9VZn?"߆/}oF+?_~E z C`=焥܈> vlj(X[ )znue# 7`]}%fYz!R pa[Z>Sȥh UA$aa!K( \j p`iU^#EĐa*BBL %opn*Mn3䵮COqqKh3@5+ @A.S G{/12#R2pp"Y1̜`<Ձ ; TX)X8Ҹ։)4`Z9[?9h~_Ƅ"hYu9Wd?{G-^5 \i@T`-!ڇ9cq^DwfAL:HT&-Tb1 1`i B7 !?d=1\(/̗3$^k12Clh,bZ_o ޫ*wf}:c{2en %W۠G\Fk42*z=&ĈSHks\$9lCYCnɪv*N _]OR aBvz̼N.˻|"3J>Dn?^2-x2oVb  s4dZSh5]kXN/J&ϘcGx~Ҹ}F7J yAْ#8 P!%Tlz\Ogq 5SA=V]}ef .K8e)CJ) ]h1{(t`p&Z`c\J(A ޽ ږQo*e##oO54y6fdy7 Ǽ3-́E\w0qbk3up9 x)Q'!2BI$by=i$j@ 3^HJn>J}P%lDzjBjCDa&2ۤ0EӁo™@jg>ʝQ$P`o`zalICr JVtxFtS5Aj|.(setuptools/_distutils/cygwinccompiler.pyZ{6ߟUPH̤6M MȣE6j$z, KٴE/Ll&(*+}U˴_益(Ķjf?ucUr g+5ZYVԹ 39\Lۦm]Oi˷l "/USL{ֳMW=^=]X"-gi^5bi~lE,٬j0iu~{7Xv75?vy׀;% (8i7зC?Ӌs· k|]W^yžKOÓs>Vh;?,WzOէ_|h}"xD?9{}=:vzbNO|zp'xz <ώ:~>Ҁrh0uhJ^fzzgR 3Z!>kz(&ڮ{7cnϪ5io\{gWe^I~&y$z(c<ȿ,b_[F\2#8+Nb#R(W5'/yp&Zq`biG5eKvg>F:D-ZlǥKZ:iLHX|B=g>Ġ(I&tG*7#UHV/m{SaಉpU8L Ct356`{s)sXim+Hpl "p!Λtqsi">XuIx{"Wq{{lwj=vÄ=WhmovG x27/| "\nMd_b]ˤ@ni",w2$ 8J>Ba/B "tO  $5F*NK2~03Qq9{.a0[[d=I])*;saoԚ6A3 dJxWDtRL ͯQue!J6<$So[y5W)m~pvCck#)drΐVĚ) x99HduC{ϋI/$V#_ (/p~G^_-n%doQ]Fs/*6Qy+Л~ ѼWg&%M!F:IN pҔ`L$QE!`ԨZN+ȧXOK,PK%) XBjg_}U%iI(wKNGfeDK)a}&=,|2CZ:F%lg8ťܼ}^q/E9P(d㔗\}phK%^Ѻ Q&>:ԱaRJI";V+w~?$2LiOfBV*gX=SA["ï\5^PyʕEOO@Q{d,7{1@ǎz4H2eE~QmTU0er'th 3ZCT@M7:C ݝn^.*u*% V2sC!ʤO=Y} G:pB2R^إ6^.{,kl5 ٣M-S%BX$Bܳ&orx*Jt:HP91 [OpmBg۠] SszߊU1uPw6W!z$HpUtL<Q\d|L_y1?{)#GM¡*"KB<<so'3Ѯ zaw|p<׼+&-4*%?"(mWQ5fCI>uZ:: ԒiEqrA N%EO޼>H'һSFwK?Eɥ\~;?[dvXPcmZ$#ZB@`2/d1HgYʖպSܭ6^FĹr_$]ʑHR(8Y;/r)83%" F-;d1 RI2= $VK${#?>=y^ZT 64᲋ };6Jhot}[%)CR64tuhdכ$G3lذN|™cQBe_6 0"d4Q]-ޣS pwT4ȳtLQ_cWcj:엍r0*pSYU@.Ò.7=ɓQJb(+HۆYer~RN_ ºǖK!L.} ~[?^RtlbT\mkW`,e js9n"^o tZa,~/\[̉=YΝ}B| <:P R_FE(@JH~1Y S窹s_Qx+E:?\O?>f{ckN*J]Is7}БLz/^L^w9J$5V%32'1]i_Q7eIl,%b䊓\&Յw\o[?x;0~\ ?#:?.`f?{6]X,f] |jTy>Խ &MDE[)H 7*jd4HT6{:yW&^^ѣۗ'<~fQ}UӋZV@,E jt2rAe`F!UOrˡٱP! uIkQbg ,C2oRep˰avׂ`y Dzz8QN9h*QIbã)g5ǒV*(tۄn(TnRv"vTjs]>*I3p)jXRmzICsU^:tٓeflG^@+XVYOLDE ,NZ$c2X׎j,7m( a8*645V/#N6>RVQHp6A6u'f [<o<_ LUbWS)v$BTaɢIΉ2 ݭaqk=aK<(oE1K=A"z,tJv:S*P[15mMC]~}rL1'UC}߱THkw]N= 6J7!f=Y7tZ0QUC?s9XoPŸ".$FFɺq;#D=/P"NiĦGk8`AbʛZ?>C#H/Ф_~LMWuzXQ$Qk( ӛ D9ۑo S:kQ`m/D-=#PK~V_H%usetuptools/_distutils/debug.py] 0E|*HE*pk;K y)ko[8㜲"cNp_޽wOp(24RA8놢+~XjgD[=XsIa\PK~V5V !setuptools/_distutils/dep_util.pyWMo8W*(dc] @ `$&"Io!Kvk# g޼aʲlW[7OE7~+͠kv1VȫN:/Y8IH]lhZ Z3n|vKuf5R-WVث B[eYXOk:ZKkuOh?WEQ4rCZ]83ؚ!VMAx Im*H9ꌕdesgQ8 [Pcӕ_% R]6߅(N$Lt0J)P<,8nSޱ1n [s,^Y\}޹Sލ{]<[`P'AЏrϟ1*ʌyqeup'ԏivFT|OPvD%Ze:!{Fs۠Vl -S$E<+Aagm7-9AiC )<$9LDNkP\VgQ3~ Ss?!In@<.e=m.ЯfW?cz6o*.hc78ϓ3 '' F^8w1BG=sӼSAwU0N, l%%+kZ!pj4:WA`PD(S4)mDArn%F{`y} =$cCcr˹]&`H\9.ʇ;1U?˨V~'~J,MHG80{ f~INpjD]'%1-D+Alwy~|7#/Mw+c?z :}rs;SZ_ބ z *erX販[+j>ŶKsxWYVxC[](~旯zbBlfQ8WWBWB&Wd*rUyW岲X\&URW±r*ۺq-[R(UUsX+^rq'~[,*YEW^Qڨ4:Ⅸ?o7 +(Yom )2x3T`"PG FX""# K"m<YYd$6]*ĪDk$)#`5\{(XoRC4dLWu8{wSSj8Mf䑵j9)ӭ(~T;KnI+bMȧŢ퐸a+uU(K ; f("szڊ,` Ohc;]}jAAҐ @VNJ&❍Zhh%%p:ڊuQ@] 7RA* M(qӇ]Qe!&޼*MM]tT s{D%a#yjc Ծt6N3i"q/ƹ .;$bM-]-Yf%ckI >}Bx$E¸V(@Sp9蠙0cA {= ! ! kQP@*]:bqnUi{ׅS4GIF-G{Ec !0*vݗ>;)mUZ\|W;>6riQ4J+ellʵLIVΆI"d#={B)`BF%efT͚V 4$32IXe2jm|3{/V "R.+=Wq2>Y:b~MsW tOg25n)9 ;ꅯ%N X!yKdﵮz^(Z\{ɳ` G)@uթjԈT$E^e*N- @y 1UaP(I2R_6_a'z1^fCͧw>2=9E?6'“ܫG2Q;>9nt&2ـ;O9dqlaB6k.efb AS¢ѵ1EGa@h P;FA(>F PiICQ**P!rU4ׯ8*:8X`Brā o7*;͌&034P!|GG=hl䜀mƤn(@"Hl k8@0DC/[ tp8Eh%| ZgPQA>O܀PbR$=y3l, +AUF=(jwjn"?ݸ>%8Պfj9 ڽZs;zKH5Cf`fDɓLɋzHY#_[7򫞐*27m.LJ)A]1IC=UbH 24]a$C5F(`*3lA:/u&V~&CSAql9(/ PH4{zr =Ć:K|7GYt<`na: `G9DG_.eRjڑ {hz,#D{Mqe:Gn1&@(<hzڿUm8<Nk44Gs1ʤAç3ۧt P.y-j:@#펢n%.t3ή!y4vM9K i$iᖪ_4&@;P@o?/ p]bāI :1%}, z B-K ֕59A <\O+jʯt-Y`j6UYQx=ܒ5)(&qg~@ѲZcI:=OM݊@ti*>~rauCZܘ#~rNW:z4w15 j|F>ø 8:k`3 9/wswޙ)%<tt_{g\/E٨<x!ϵWNjSk*r*ŸfTEBUeg$@CN6wj;ovnIUuəSKٿftPḙN<+Yއfizmă#'FE諸eÑr}5< R>qdt._CWg/h(0 ၿ$Wۄ}UL}yX@@#of 2c5AzD1G}-ɷ e_)d<0zSoz'M׎J[>Ai YLbu'c(WZ 8P}go;Ps)H~H%{ ǽ:Nc #FJ"3Cwj*Y._C9׷7tIB5CԼ^&~p7fLPK~V?~94setuptools/_distutils/dist.py}kwȱw > gHڞU8gM8'3-M  0(WwW7.aO|G8w(-neFpBnԿAB1IOQNK!"`^vam\gW% sU -|e3M8opd SyCuc C܀Ư5~$ *\M+'8%]Sˢz6a_i7Rl@݀U; [0Ĺ6o6g!. %7q_\#y&Fɯfz_ZސkWoS82ݽFuU߂jf6 &@uk>< fo ӻ퐽 ҄K)0 ;+KS/Uwy𛳙aGQ٪YEPgw6wCСrЦ'2kaJt(27G{favmQyC֐L:BP t@m1Wڂٮ#ߐrHgi|B ynD KBb>-7{D(Y$PR~Dga߇ uCП QQnv! b `˛5h]״gSQ4{v 5%@MkɄ: 2ZJA(iLy6.`InG Oȩ1!NeZXXX:< [P drPYV1Y0[{=X 2@DvP_r*-Q6<(z0U_#W#  o.ʋ(O3.f|W4YO< \̉<X\/DϹU"β (ӟe$fd܃9q)auB٣s2/vjZFV`x$#J㱿5h4]Gyʘ啂RVlA1pvC,sp2$Rp$a_~mb8isxV _h$_obcX̬岄 hy2&xlEѲMn>_zo/ 4;'{:ʾs9Mrܷ6v%6i-W rꦀ6VJ?Vt]^mD"7ܐ }<Fw*E@%\!.}U2F 쓢M-ڷlW"yhd0H:ǧ )کUI0ne*s^t[6*Pw$Zg#.xFIM;Zǯ1o8-1t&8ȇr4jP '7@59XL!1pv. &J@Y2mjdY㉎>𐸿J 3Վ,V Ʉ{F+ &+gK% \Hu7Dkc@/] ѕ-Q#m <8@Ыb #~v #HD&JAwq+T7*ce1zg-n1O\kп ѧ^oʋIGl lg-;M/+qb/GB+ßԒ-9hzrc}uË6tۯ,8!edǚeRVU9V4MЧ6C-/K񩲳KjB\6Hj\}B4U!dL!nw8@pZ Y^k]S];A" . wrA(EtT:]_?j\xZ}RV |Lx .h":0YqEBòU)©.M{طPĊ_AŸI/9j;#MG!XZ)(LmQ̆%lD5PXs +Tr3/f`9Kvv}"/#ݓ^\~ͽ(.6!-V)G:4StS^c4~a]ӔRXE([+0k4HhZϸmKSML]O0b~ !C$,˓ 6HCBq'PPGAv/@)c*P-# ;Y" UC%ƇݙDJD[`4%YE2:5R:4m܉ƥh/8)9+ʗb[Vj$;)Q/_FU&Y:^"T!GV|t V{1$ Drʨ[ؖt;vpmѯ(Zf!Nbԑ Ou44jS ,tJ(wAkD( t0 ,pqm,~A1`?<6=;{|ЏA*r4P4{Ў~>ơI A,@s2fMXu!@Xx%1CGߙk {m|x8"߽5dŪ%֖Hh+9̹E%|Qv5,>iCHĸb'QT3qEQiha \.Yٛ_XU䩣3\=E!lԼ_<;"D5߉ |z_5f{|,CF?!J^c-6.9냼A(0>L7eR²0 tHnKSU'8ڦG k1!đJ6"Qyhx)`Gqvwcl1F,Hm,_w`^+)gs"@RuI &k}JA<#LS." _ev:~;#ސ1"u+b,&qOD01!, fC9qMMvvx)*nނɯ+` OupgOy g.,c^6Qmlan|6hA%BD`lmg!]"i1beǔ$,Y;Hcm_YS,IYW5Q:Z 0GPz7za*Eݬrn^.׻ (bѼt`L( qQ瑛mQGD[tOAOwq5m*O$gd0=Aw9jno<n94{I"ǒdAR h7T[mqi*]95/[ͽBO(=Nqe5ǚay*z/:ٜ6- :'}ڟC+IMQaE~>_AY,&k3(Q6’@_ND;ϳo1> Sw(\oT_w'F|!>w..,dbáɱVy=]1\/ddH ב10 zɝsI.?,^ų7xh$ q+מ@5V|dEyɹ!6=g[{WEM0;0iU$de3=jk|G k".cM(-1|2k_ϛ [xP_hnPPNJ4ϜIg$A:bMN%RNYpAGE8ZC,PQͱ8݂E$VAre%~U,˪[6ee< tC:[`%2I<.-FzF)# 씺`-G"~ $?3w,Qvzu%HPzmX'e`ESTt !9}Ž,-AaV*UV!o#] ‡ alȳJ(UlRyu`n$3 Jd791_|Iz>ҋIfL5~THZM ;_qO߽fGٷAZcp6^/>3 ZnfUQ# i- ӆ$f|C c^)lqZr(2%jR"*z&3T[aHj8<)#U;.O)#`L?n|6 Ƴl@J)ɻQPg3b?s,+((2z[v6H R"PѮfZqoW-DzZon'h~\q7ٛzG g,P$`+ȇvP8dp$6clN]dF߄P9HؾnNt,&+Y֗U!03)Ǝ>wS@EDV|{O @mr۩ZQE&>YffUF}kj umbͪ'GU1s J*w@OTcxNa4I _&϶)Fwwߓ,g6pW]9N8.Gz7-ny..(\ÉSZH _U~ Mŝ8UEJZ5q5(QOs*:@)DpEoN m7װ#Tne@B6/%)ُ-]ygݨoR0~j\F,Ou)g:7 Z4f}{TvD<BQ.qX@$"|2bHJdi]et;je[sd]kjOt\_vJT|}4])@qj-ѹ8#z[\_!Z'貭2(Q=M섭h1tn?Ջ<HϼM{,Ӧ+`hTS,\ ١+|};ԕbdz26 SL 1$M'[ʦKYmΨLޛ2|4]ڇt:QA?_@H"3J<ȡ%j4UU5+ cⳞyZ6h(cGjonw]l縖1[ߌަQLI?Sx ٓj g>))dgLfJh{q/V">lp y7̦UjRR$mZդp6Rk-/eho*`ƷXދԥwp&`-+u%Pڄ , :v`i_}+p1RAj-]rnac**(HZ|ECMP%1{;J%_~cc>QA r <+b\5^446[cIȾ]4MZtx O"n.[82’CaT@qpC$xE$c?؆uޅ'wB ĿC}ss .AҩdN5PD}!C_oPWW՝_/1uIN#ΗVUMZ[$cEUS]vWQ==dR:)~@ۨRVU2̀K!-@\ Dpu@±mM`*'zQ)@Q7(fY(O oAUPh=F"BKo*([\ ˠOz\ ^;$bR\F7!xܹ.`dE<.IhQ70z/dTC >kD!)j}­Ecfp` /sM<}55`*(jQTO BU5&{f3Y8;XfoOz:06Lg|q[(oJܟݭ>N7KM?}OCq+Aމq(wP4+ڧx %-tД@:y~xxoLBfԣ#[nᤇ4RGT CKUQzY>GD hĽbMNLZ̻J~,IɏRO֦K6Mx2]vx)Is4؅$jPs Jw'4pΝ~4:;ryt; N7j(VJ4Tם(GB@jUCLZ>c7&"-}T@Jq}2e4>={"^-,,cE֙(ٲaGHs$l=HKnߝ9ORqF1K )>^T>+?a]i@%IL[(^bHzdəIJ0S{x!Rt5H@~n'q('"K8IDcJ\z]03o\|[nTpX)[ .iRC(tK| edF۪;q?(H[סȼ M\ڷ'.d-e읖qH;< #a9gt ,(dƯ4wmrk\b,S_åsE Br>`x9ߚ9z3Vp<\(# SL#:+AxjT!SZ4K Hw.D!S$H &?arRA%̑|Zdw7VZ[8*pntA[R=lP!.QH|Qxc^GlPVu_"Vt㑴5kR$zEOXX:]8tW̲_8% v62 ~Q qZ9hG3yO91C̥$ӘbkpdkYby] o?'qu>,J̟'A(q}eaqLNa_"r3rUrY{ {ˢ 8h: 1}ݣj޽*!/nX6 S٣XPZuAS4JX|R$+prqeXnʕsyb%-ze]2quk@C_S_6#6IB+~MH( :ݍ;fIxUYb=s:Wى;02'4Sa-數;IKd%R }2$.=Uw"x=_:- >9 X-V LpUMLVt4xTADMlueKm Ecc2A #5$֖]~x4ld%B' V(ap1leWRU? ~ޗzMh |1B* {|c^mɒyM}<G!|8xqT/ЗMyMykv1M3~I8`y\'r 4 !DO_pV!\<] LrІ_M5B!ċ?fQJt6ΐϏI,=(0쮦rqQL $j$P=^ؓ]TŒ_~ł ¾eIP+qU B6l[ =lbRaGp>/Ջ~|_~Rܥ׏#' -ʍ~WxiO=MT֏TxXв.l~rUC,q/'GRU!׷,}}6Sm\3"ua{1-K];H|{IQY1HEwL:YQ"K[Ur=yyG;` |DvE6 =Ǭp5ը~z8b`ֶWxdd_cjjTVmSŶEn.'0 lOx e׍ƒ?\$yף<45 FN͘Z|pMӣS=vr"I5+<<ؠWУ[4[v~C:%# p @x )Egnl) ěf;@hA{[?Vp;WR$%S4>9 r3~ wsWT;NTu^:.6@Hu&SHy6.48IE!CNupe9ɓ"#z8.m7H0g袹2B;9ӝmu6egwmy]*?cS_o eq 6fc ts4 24IF fgMP,|܂ve*Pmޝ?}ao>r~v|[w΅G oW&ThJ5!&$_MNU/^S/3y*#[)uopQxƹ)gc8xU='nN;fe<ݥ)-fm;v*AԷףi z`o=ԏ\q=۟-aC;<mYJI}L^?fjUfQ` F֡nZΚgN з#Vhh,qz6p9`kLbf['R8c366cx_3ΗpP-ݹϸ~b>8ƨ86%toOfNS;2{2"*O!{ꓓFIA@$זs*[Ҿ#ܵ6_f*'a2l>ql"KHϝ}]*VCC6a{/w$_1jjQ@D "hoIQ5~R-&*q8VL/jBrl1q۸%#LOY_%>UUJ[86T 5 X F*/E8Ցi1x1aF hjS4T-qE6-F Мs,%&ϸWNA > 0o YpoMڕ9M+CM&V!_Wu&"=*|bޱhdMQL)ʨjW AG˫ !6T 5juL%|VWQo3$5Yh$W3۠ (fT݋D=܆Wq504PM=>>:\![oPK~VdXsetuptools/_distutils/errors.pyWr6+`yF''GRdbOɦXX,Aj":>z{u7<+b k0}T=ElP)P^_F պ*5J"Pl8U& QJOXiM' nR*iOƥV+$믂ujlA۵*tJ׶j%nSK6lv3c-Q9y]2m|T|yT".=0 }[89Zݢx ;5?@3WHSnz>f!aޣZr0:M^Cc#|h@mR*#h-qm7\ @ 3bF5 6DrwzHs!qxS$F7/wIMTPnH1S ZAjحxIu0Z-Bm,‘!$gje,bnl$_뒦e|XGNRjmY\_qۦ0o/+w4fin8Ody4JV|oPHs|1S=cH0ƩadB}>? BRQ,L)+Oa{RE8^pJ!JTM_Ɖvm%S @_ƉG!;eCM1ٺ1` $ <|8S_=&QpNH'#Dv#^"ZPz ],%cQt`aӏ]by?H7b"{yvOWyH+bh&_m#`9aHh@+^c{|X2?FGH٫\L=Ǩ)CO'YBV-L^*⹽larL'{vD&1 7rHj'I>5>Vg%~14L&/;? oy;m>m>~;p;܏\%wS߽' \k_tſ,}xwyˍ]o.q41[\?KgL"}Y1YDsCľPK~V~& ("setuptools/_distutils/extension.pyZ[oF~ׯ(L%2sy["6mE[4Ia#r$MEqTiY&nZETeUFT&L~*̝NFoH2iLTV4B/~vhLZesaUYۗ6 `2ѻ)Ja:"N&F[GP2ˎXG&)XV:Kv2OgKM @;U[E6APo}1j`EB|gN3` 9!RdbңXP֛m%`eKYc @@=`;ӫ#)&D+-+ck.{SFZTPEad-D.⠐IY=hrK2kf$32?4q0U#AV4dVWn<ɡS]̎$Ây@ 0HYsGДdd|"vʖib JȲV%[* {$5$Gf\3 l$ܘZU$CH V.3P|!Yah1f[T)UF aHor[W+)rCώ H75՗Ƒv@͠a"i#V*fa]`aL,R:Գ s}/V؉L6WVe!A"X:>%I@ȚV{L8^%z <&3*EĬso6*yM/SO=(ws0JɆaмANa-ɮvCyjsɤ0LBسΙY~&WS4BIP$>"ȳWLZqq.\u _QCQCin*(R'BYK~4UΤV?ڜk!K9`Q3rN[ [#q$u. Y}88KQL[Q0Ԕp!ʴQ;% ^FUNv  8;Ӣ%SV3j 0@ǃ$x.k$p(Af?OZ *U^Z7BH"8SWykXӷP&uvm/5gBSѢՉįZSYRv!-Z}Ƚk\Sϟ@Ҙ =NGɚ7Yw+Ϯp#DXu@NyGթO\KΘ.į2uA~ܖEEoFBD,} ApتmS9d('bAVX@Tj37힋ƳiLeV+{gM[P`}n%ѽnX=Єnl6|"v!W <He(Tr'VY9uv9+kUA)C\˦Ԑ{!› 7tO9 8޵1|СL=뎸wMm{aq-7)4Q#z=AoF=U{Vуux<^\H>{Wrcu٫mUYzF<>FI9$QlyqD>ō4*n5 f< 3Rrc]IёT|%}W)Ծwt{=s e9|wag8IoGsw׏#zn͈jd3b'| /?|>| ?|V|RSa~qUa+bAb'7:\,fEуtH*34h^NWl 7˹pif\QuwfG^7\QY{/Vz{YX \[E`g0,,?iй('cpK8'k]U?gԖ2.n^#5wa ZKnt_^ ; ІXιrQ>s= H*{m4˅ #b -oZ?%A񥳞DEK^b)كu󵎲172kS); ?"}PgC )RS7Z'ә80<炒٬i$mk4E:ݎ\0 뵜`ȳywAw^MN!0.0b ;=w)Tv,r {GE7aۋ,aN;e<0F@Bz>5H;;nVF4f؞ayq{z堩ory>yzĽ Oߞ⅐jfD<ʈcwӍ8 Bn EG={]銺kqg#z囕eg,Bi˽'hB(S8AqtQ;䚖2Z-}ǫ7#CY,\(1}Yq0n% * 95ˌM~:N]da ,&ع~kwS3TNmuOu\<܋DVۄAL]~vi=Z4*_? u (zH>14^]h?ÿB3XNt2XX_`zKrV}dᾤG|Pi.7* i}{UX4`~D L0vCl d",J#pĸ&w;QrMcǞQk9G%͋A-5RkS+ӺIE-NƓdRū2/PK~Vڍ E%setuptools/_distutils/fancy_getopt.pyn<x:@n4=ϳ\]rKVlc5oX҈X{LI%MV+8 %-qg\ĝ( q̫,ɲElǓk\{A4-x՞8%a7sO&d3zHn9;T/L RE˫6OK؜˺k'=+ˤ9yHx۵peu"%bb3O|>CȓZ[oFlɛF4顿MsfOf.="xhjN= S 𒥅@N$-[I OATo)yu-e_/_90,\QIqr <p[$k(@4y4%DP} qd\^ `Uʗ A֬ ח\__Q`@ɗ(y&=ah`b*U++OZ@%Eȹ%Uy~t˛-{>8rh|MÅy DF`mT@iF 8Yj] B؏x\\ %QOzY&7v . 7K ꟒i(]#PFAl@acT=m>ьwBOZؒIŶH+m3 1RbY`>rE[! {Ÿ!56Z ,qLRKhD+6kP (f zebK _gKwlɫlBɋBc&@QheQjf0xi4 ):`,tz䤈,DװۤS1s0lԆ %#rxxή5:r`GIJ@#P@CzPe<,c@4Ir`BT'Ɂ@Q\2e @^Y*CC1=^%qS"괗zK;b́(BRK*Y7&sRîFaȃ["VGӈц؇vX?B/ `"S; `@.@г)4T}vB+l&J0.qQKPh%89 E؃s )z 4d,"9`D] !C#ЖNxmf =g^)'Lx8 "k2aha^ݑj }$J|Bo~*pWi+sDso ?=92>.1Ct`)wg'+p²8=ЬȎ%񥬐 _cc%[8K ,vq? 5RT3eCTmhJNNyNaRS !r2-6aB-9 QMz 3cMkʎ*i0+LRڙڷK4D"D5q naK;1_Q鼟 y>Ibs$',D,xj4<dm'S|+:⼅BO^㦔ߓf$FҩRsGя-iY9Qg/ i V &u>N%Ə/ U(pQɎHr*\Q"m&ߨw["Vã-j*8ݩq1{!ɉWm",;!Iʞn w) *Z bkA`V})vTܩK:Tmm~2l>+V駟@Q2HG~7i2owbA>`!+=,ro6#ᝐ/CɺK,LcARDw +OyJ0:*?/aA/}Q[@@_Pmzá|95fI1[llpxm UvdBnD۪n5[~Kᦫz\?ɔl(U\s3 ޖsTC*x{Go'2MwkRw^  <9ƃ_NaطD̰Rv=N뉞kO ]&It=͊f#OB}7@V@uHFx(d7]~7=P-D߷O")ɕxӣrWХQH-'d6~.hˋ z< 1˒t[^Y(!Aݕs`?HT]#b!L#*]SB"M1)w0:11+SPŅ<](lrvRCVQvBSFo¸Β~AxM$Zȉm@Id 4צJNep-j̓02`Hf$R6qi*Ռ0HUZ,H]"X3.B& RI4Y|D?8pಓϟUQڬ?'}L{#gnF3o INBG_+5;qPbm.*7/I_^”5 LwVUa =Y "U|tp:d3|K<" hO%cO$i[h" qVx>;(|FM2Epe]ݎ9eɎ ~1Uq.l##'-Zo$?naNrmx:.3=dmRQCߞ(ا=z(FT!Qp6<JGjh mylrlzĥ_ CW2ur)601mCԵ3c9kW5]"Hk̀o7~x-@Znlh6 'Q7ς)+h߇JAԀ~okXkJͱvn$R+I8` _瘪5Y5j ˕tFkud~|jǾ2]v[.5'=tՍToYJ$V!{(P`0*Un~;c,)Q" ^ɐhfuyZ4ilH1p"8jj-V[cgc?-9~YNvqmQ4Ch6#k3 7S?@G**XtLN`w÷X}O _(@kx!~;42,Cł=//nQε;~aro+uŞQ4PK~V  "setuptools/_distutils/file_util.pyYko_FJ\֩,(d` eq=jI5﹗<$N5h幯s97^WnЕ~+M]xmj' VVz]_ S J fx<X/-YX'w^aJp9jWN(;7N uㅮEV$*ħWVa gS--+*]_nJϣѨT qٮګڻTOżY,t?8yB"Oŷ{t2FBE&$eOX5΋yQy &ˁjBEiH0!^, Yo orbdSyq:WFH^=[RKYػ~IɵImeUSI*ƿG7%-Iu2eqP`u҃V-Nj  1:*b @" @/nzBE2;vX( -j8s.qaGy.Oᷟ>ga55s| l-r>B`OpὝYSS4{wrnF-R^o=9]&XmÂoc=-z;)F_.6KJom^Kl|pF;i=Z;.\$b#vuGBvQ"S /ʩW82kHP;P '`$OOPܺ:zb勵UNuzkԫYpGep+z[mjS*PDqXK*-RڪJ(uC*U\ B@mT`C60Th`ٖ`,7XjwȤ.o\FQL^;LDrbCy۾>-1CiC+a/&ޫ}GBD7?tfy+D_ELhtU9BEjR qKLvFTkSS˓hR|z/_Lew../"Pb1Щ俌 I"#{LR^H zx@VN#TOH{Hxё'sQS"phnPFjl4vMgdȕPFays53!"Piެ9fO }ِ\E=]D!l:0-[p?ʪIodKqCآ|~ʜjV12 W{Y&i6]/d )8-ō_T} N5e"4$o$ӫ`2FG=P1 jQtN韊 ?ϋ2W$ R]뙱WO$"RP2As }/MPp+%zë=Q6(,68SP|uCII X`? l@AȽ9`RQ-)H̥ne%Vko;~%7xkS?/~r [?2zn*}DVHؾChpk;.Ս.F7s~oǰBrߌ<{l@͙\^{lB #T.t*α6Tdb咈Zl~Ɍ`V٩r5rC8xU\8̧tPãAwCpCk; LmaɸtR7X+Df nP.Qً/~N):꾃~!G]"$b:F:]Ho'kuV]ýw|pb|ٟڲ~J~o3ϩN_T)%3d|uji/q`t;mQ9|`j+*W>u,2XS4.8oA Ebc]. t+Ux3#_’yXf&cْٙWrբ -q5R4bGmUE*HPB*lhzf=2`Ԉs6J6iE9؆jS] ګ 742Oj:QWE)9ɀۆ] B=.ɏf<ԅP LhF2NlpBf&YNk@.n/tF- IǠLxP@~R_X@ #K`ϫXjl>z$x^L R݌J1b󒃯* 3DI݇<@tm]hKYr{Ua"!`JׄQ Fd'tlf[8 D# |!2#5ǰ*-bZJXǀM 1 Fi_n}b^߻|t *tA.t0 d&˶I?p|#/TT6b}*.8L=h `$\l<gQD:1t3"kڠDt/.;.r-Bu??@I+edrE"R|ޠWcgeCDщRuUo*'1#lM CCFX+UB_owDg(@ F1 6M֮ ]eZ4j3+ $)6yyFɽe&R9$4c4q^n6D %h[6(m&㲎V3%vnĖe # %N{޽6(y M rKYk'd b*! W$畱6'V3Tr BXUIjOfC dž,a\c2ܺ-"/@ n]iFK 1ڇ @ky *roBrq1X'^H+%(n.7#٧3FO7hr':H^y,cJPm5tNW)zU2Ye1fwp )챂㠗|;{To 9ۺ0ck=GGM{'NRm7SA=^ފ?n*OlHUZᤛ\4;5z }u$|_cؔ&3kK,/4PXS@Q|OY@iA]W(_+[# ؝ǜwmΔDkŨHWm +R%jdI%4a˺hKqI $:qUuuW"P @ BA]nr4BpN=.8/.^`Ռ ~:FMOG鋳eUADw\j8T7\5Sp~l|: xwH +D:f!1֯ayWQa sA[|dP:Jm({upY1-eă! ~EG>==Q#tBi!, mzй7קRL0\pg( S7iD#eBaLB!/58(Wy=ݹxzwr jjҌW@NKвwh/!Aţ[+}}(@vtD͢` [\Si]? o8H.wFɎD8$ս졧 { Ro޽~|N!51^,_l/P{^s4ŷZg $1prHrG]|a@:N%]L)0t6rdzti~ljk0l5TTU齢7E*XWP6SttrT>tAi7eՌh\VE4XӘMҨ,;Ӽ'ɤ|Z :Nr}Cy)gI?_wI5cClg>߿Mi)&KV֋E^T4jƀ-/JDDk1{jav:kcTxXyB,c t>Q'^,Y| F&5;rA5+zF4*5V,;waV@'s^v:0ЇVEOxKׂowQRaZs¤d6@O.F}nIF0J+?rf5]l&Q@+eQ_U"JWĮ!];5vE'..BFreƸd~uE_` \i]ٻG%G~J~sN8Bٛx}\D H5˼.]9G1$g=>[W;?8}<:}`g_nB.V)h2ዊ6*(MqTB#@"0ܐZ2ϑ`zz߿Ew=`ʎ`EX6hͪD_;;./Ld 1/ΗNCJDK:BiĦQh@tsçDHjLr`o6~PQ%i 8U\[ma y#Y`\\5E(FQC?aӟsطqД\ CZ $m R/!NB,EDLEaْ)nC83ޔKM~n5frEm@-s#Гj'(4A3ZX!Hgl} )fw,VYgrw$=ad^?=#4Ҕe l@Z)r R,o8Ձ€U,A7d  ?!2AƗ(9$G mJFngb14*%"B H ڂɒ=a99tqܮ[A| J1-sJh8D0V9@/ i3%YRFA@9x;T6RmI d17)v4P5^d:UJ,_g\v#xV0&\ vX&&Y#ս?ʀiz4Y|؇.8q=bh_ p$'ll*Path"+a%Opϫ35l#n[h,UHvD-AAk\/J6V|uW<qy xF5n*n]3fhT$c(jy -GG8Zh+[`O2I%:KA(PACp;o_nFݍ ×]Ԍ%^XZ&~S-+9BtYe6%M\Pƒ bl10-[rTY6؉6U1EQiۆ0j~n(mpvE_vR_*Kq\: Hh$PLV j^(/i9ʛ\P5 5m-8i bKa dy֫Pg7d"NDD!f_W\TDVMUkAY$(0&pwUmi"vN" MG@QA QnƒZ_Csx0DG@p%tM_c1,sZ;mF-&6EʌsVAbikLzM2T' 8K`5oTagAv` hAB P DC~QL"&j#W[ADI֋D/Nb ³ۤ3NH\4UBNH\b;X,AIAd7P!iF8%$ d\FI,&n 0tx!8~0D bDC6$Xz$%/hr !.,@ε!R{HDY}-fAau")о3!.1ZPھI 7*RT= ]L]n P xM/S6V1@Wp +BiNIYgD(CAP9,'u՘Ɩjm[P;cXEH2Tk9Ospu[Fhf#0`i,imDF4-<| ׼Xإd۽UW&3G!?9!{EzN3yh$ARV%8]i^c%MU/reV"#~BUc ѵ1{Lm\:X%tZMnBt|[)S\3RukBl O} A}#,hig@d4 RfphBV.v3StESӚl5$lNG9GQ=u_9E+"Ô `֨o%gQ1 xR-GXO 1JkP86[I,&3$eh f\T,Ⱦ#l h1~򍉕JPUQµ ="M|r?d} jmT@;{&"0Fs3gCC\$QqTE,P5`ꤜ}17 ɉ57cpһUErRY,ĿuцT49VʆD$e,VJ=K1{ysYwW&.BIOߝ*(U\2P"["p=-4m5Ք$FI4(`mp ruwkb :ïxeZ@6eǦshDWTH@=%IThS\{XPj܈x0)N7msڞ 3YEBNMb'53]T ׫+z5#׹a19(d~@RXSc2M#@J< u XGղ1cwb s4&K.7>C}Fo(0#eQ;hn-Xڙ۹n&kF@$.e+RPt\&zcL-4+feiccReЛYP=88?:bhxkpn"+Vkt6'a|X+Z`.AIB v"5AS/,P3"U+0^aFLQQشǼ]tmƢxE)&+T=w5vFξHS".,;<&|SٗЩ7yFf`w+p 4-wf(JTcS`T%:6X˖ceH l\QoH JpsS/\l/̟f/0iH؉0WߍA?Z6QzaCj0`VJeVQ0=Ү {ekz4eW5㑢'k6~Vd<8;/}VYq9t""&%4>Z>&֍xuz4g0^վ`$Eq7 8 k.@= 9z+ $$dlصO a^N..6Z2QS{Pg}N}0uʻtdnvX oԥ"oqz &'K.買i~]yF%spzpˇ:"oDlZ=,/O[|0uz2>E_ zhO#_HqL4.G" wp|L1xt|x Ui@"wlCݝcƾVml`݂QAh_>HET P\d1{C1y]-~LaE4r P2!+ J]PV6K2Yx` zpY1qÊ`SFKdIVR P(@Vk/_\ gaPslKg;haX#޸}rӅ.>qsבlAtHD³XfUWP0zj c"A7qDĐJL?y Gny9NtKdzE -"O!VG|E#oVvq a qb'Ŗ. ? >'_NJ{Hi5/j+$ /(}t;Ңn5Yx!m,TeYţ&-l+`D#EW^qdaPT=p&CeiRoz @/QcBӚ F1*b(gEii mJZ Vi]*x*$'6ƿ#H fńw ]Cn@Փr~72)n],d%En|EVL!PO-e~?l)-k5\yEX/. .e~R K=V\6(]~Yyaexl hۆ%_H,<O+z16f^1W&Sr$i;knJ]b7o(%tA2ʮ9ɣOP&D"EJWީ}II E2d(<D(ZQY0c`ldg/nI蹍Az[þn^d8Ӓ s τq ?T osZ?urtMK+~ӅB<^~K?D|E秸}^,?_3I^v(7^7jyѽgC+[&&R1q`Lcp+vUl<.G'#)]!Mīs8iRkRM۹YPhir?1;6lyR) CŃ]P[cEAx:mv[F!.É9v胒PmWg˾/-TH,U7cKe9 \'3$vFLC*zBCx/Rfgh1jT.< Nutn{h Rn3_s,Zw ]QXC&.=EfvoǓ"1 wLV"Kyj8UzwX#,&P[Ń_xPK <։·8{c&F5y._](,??6' |ML|M x zg`Z@&-s%eZD}:Kv╅~ ݁t)/s;8>fKh9nAq] v/ ѶH` cggoj@"zݼA7 D~:yE/زg>5-Ӂ5TzP ͧzcA{<5Z-?qL(;ڰ+b&W_`Vц9Pd H o L;4prϘ5kP6eɗT|h ޒ -5 Ś?BYS壑3 6q-ˮbC EBRKkU=Gc L<*L+Y~}ve1ewZ/04ogLg{vUOwA5iFG=O?n;n(Ps[IDn7-ƅv+EPԴa~[;As_~_ ?|.ÒM%Mp~ F0!#ixW$V35;F%t$)'Dyo1EBD9?6_4nn-ŰwA2|En{r<5&xUZDՋuyiB &Fw86zDn!Y;PK~V ]\%setuptools/_distutils/msvccompiler.py\S8} =R3 Ud-!_ 30gy_wK%[Wulj?y^rUƉv6˖y2SNƟ S//9|.,eٜ ©(pV0%yVPxVd"S,Va*3y^>qYMgIG<){8"[ Wf,XȦk6c8[*"[]/_׃eEZY.,R]*%ɬL}vyxgX`d?K$4)yoWzqҭ#t Ahf$օg4(xk.LQ8=zլqQ8}Xu"xg?ק9O_e^% 0Yqʠx42dG*"Vua;ϲW}C[mP,lI*j-"h-dA)-%0|@i8g >g9'~KrNHy4<^g\ U,`˴Q$L t’*B I_A_HhI+C0_VrXxn_1 Z|<*s|88ya'788?=ejid39x^YX-E) c4E n/),|U}vPpcAs N>Ac}`:+YOUAg@T́R*֮׋*!j!q΁ǔ%JY3~Op mN=_څlCYu &Wfu@e+Zlt1vZy!;q^ɗ>;#,_tc G=\EC/0[ an#eDeJo8HH|VѪUtJl 1b= 7ivPnK$ߜ9a1[0b1˪yrMFJJ)P(-ya99xW+tCl5Z5IxC -1۞l}RJT&x\H{ߖ*ZFJȧE+F(:/y* B0:Ґa0aL)c,n vVѠHѯpsifmXha"F_$;XhsL9u10 O (%0ys2 M~ͱMDUO:xOsK2ud$կzt̿KoRek@9/t.Ʉ〢Vt?5W3G@E}B{JĥWV D@p eqAspQ+K֊38XQ/Ul b5.h#nHvΕ+RϘRIEFuzR._x ߚY"Y{~I(C98iA|0H(.A4nʅ 0iR*gYzmX@!\ U,l94*+`\ߟԧf JDv HDiKch|x+@!>`۸0 cď,Ik$tMWBK2 *g$&H¤+ȵ)o˰g&57 1k,4% ju|^es[HA`xcܬ>DH}[ 3\*6Hz;#.ivF@D$ZO|5[tR1խJWҨQ鎳UaUpp4k~[jLAbiX"~bXԣf{p>z|*7!ASz9|[Uje 6B>VlKUv&bZ j`ךU6h^y,nކWS^Y-4D4!u^AjN$%o>N/_?~( JFn4r>9pG] }K_E~xm%Oy^@Rp$CGO?\9pڿrLn\hͥUs:yW3.a6MEhF=&*,Mn [i u@Co|j q)Q²kxSqDq`*UIM<8$?_Ee0`L_r by)Ƭ53c|x_  cc).5D`W+ZЩ$ ҼU:*[NԆ+"IF]D🉦p*نl17b3M,!mn\`W'4%Rm߿8}Ty|h!J&HrzzwPChd^* ʀ[ʰ"uS61,#y5z9c)as*ЖOcO$!G%|-WznN#THkf͜BWZ㏵ce݆uۊhmhPήeks~aXA>iT 7?j![&JLj̷uŋly5"5\}: hCaվ+0GFD5ۣFDGC#Z5F,ωZVmkuB!Krh8oimIZtN#zjMR2bf #i  JoUm`d'Yulq v$p0.]aT K"]2&Iv'Pk鞪zD5 ᄂ_JH UXж --?v2ɲYB;8n_??lle-4 ktj.>]:P~M6äbj"}R8TƀmYxC%}g QXk3Yz^mzm$3G[ *yt0)Yn GAq -HtJ"Lex-j%irm\0x.hubI0CQ1y&IںP6z[2z`=ܥa^KwNl.{w>-$0dgFdBg&Q$n*SMp´z&@j>:E!İb?1`Ckx oO \> ;e[%NөX=ZNv'j}hM 2vQkGF7q:5R!GPї<7LҔ*P}"P34嵂jY&K°}rv(igfZmy]-/n5{(uWZ|hڍ#ܞB!k-yۧ/87ڶuD~r_u߾iM|j^S߶k7Yc2rՃkF,*S}|&ty3-}]{OۚwfEAM74,.,xБx$S V&dw ;ܸQ-Zn:ů ' T:8{BW%fl[)ә j:m~9;=أzـIeURwc&Û7ߠ3 TۆtN@0D 4Jh"-pΨUJkMU--d%e"Ȗ7ҥmZ)_/f^gה&03%2 XiPzua-w׋\r\fG 1TleԳzX`;iiQeg%I #ZuNVܲ_7x>;4R&>5U㣷Dq\bVt'{;WeJ t vqƾ [k -Bnx JpnڑqOl%}c"Fb$:oB!铟֖ *+Ă}m4{rP&4|X/j_pjOw^<}yO{_lǢ55i+A] Ü|Բj PyZ} ?W\[L\S[f`}&#qլgo!6vǏzMEa}V|uøNk 4H}J#f9.NiX\Sxs]%<ɍZ(+f;x-YxX2URaIˤ#\Zi}]i1&/JEuW">H^/V XJ·rěEh:t]q?7h+:AAu;=5?! (qCNy>t˾=~oS)S[@ϑİ1&V6Ëjz{grA@I^`4-Zg/ݚ@`"aB{ĭ*5!ȉ kx Gw{B{xۿ_7NON/?A-jō屷R̫͗dX=/˷:Ke.1#-ť5e*<-W>Y}3ήQ7*L? ;֌sO'5飌>^MPYWc_EgDs&hlIikcOT&kvA+ Lx0(2op< ՏwT*T9_7u#:=Q:[IZDm}( i7ءK pjG#lN yįǙ%$=0/4|wY簤+iFΎu@ cپ $&H7LL&oLa `=6>% `BDw,ѼN#3<~8͔wTp{ȂvU&@lr_U_" -Y_f[D4) (/uDx@--Q6[HʎQKq<%~{fR9yUkA'kNBʃ,ӅWF FKK*9hk+cĵP*5pj; Ap 5[+PA]āB:HkghzItԒ|ݝرE+7U{h"fIXG3<х$ XHkuɺAft $I)+Y-2U{i7ճJ{\Nӣ;emKx۳QvqR9A 0M#d5)!Z`H<#,(_: }XMIJb\{3c,;Y3bJ6!Mʻa>b@8(AaJUUhNrN5!FBz瀽$+,j#&uƈcT]FB$7vW:('%h(ɛ ! n#-ws!Ӿu}$t\TUhTG+CY;h"(2/(L4"(M3 EQR/;'wmL䔗"YFxO2J#XKi +ޘ. \(] 2y&ҒM36/} ?'dm&"X 2g{&-1p\i ~F&)ܸN*Tltj<HU;sW}w?޽}n6F==5%:IzΦO3aݟ;C+GN2 5i0e'֋P~|P Ӂ!45wIG9 y~t·wnfY."tOX`jՄY(:N<#jOn?!6?n|YlڋG%<S*>E2lGLHkpޒ2{}&YvGdI]x/U3r.iǐFmMk._%1:tQXRNQL_ϣ :D`M#ٓxT*M-v9>)뛪,_F9%OOH*WؒTw |PNεI|1JwLà)"V|{Ir>`Wty ^8PK~VQI"setuptools/_distutils/sysconfig.py )ܜ:)xx}A.oT $Jkє|\EERlֲBdź Z ]$[gx+LrY"U*Rqmj@ Y>3í[;RVM[VgfQṂb3,V-nDv\n39EtvGgi!"2G6ܪ"SEĺ-| jYY2~QX펅/j@U$F-hvF?SlLQYo,Ve kT+wydJ6yu90m\aj;UI-2 1[yZEYE|ODq\] $XjɊx[mb\==5ՊXW(wF/./-"6 s 2ߞx&-uG![j}\>Ƈ*m<8DIh70Zqbf6;d틺DM!~̊;+$`V!L]V|TAnFj%xjZ9,߼]dq NY]3\:&{J}iWʵb`0]į C ` OD" -j[5;DE֛'WZkZU+A8ڂZDkU+P[ Pr TEiP8S˶NT 邩@ߪ ns68aZ׬%iٱ$!K17jn/~' ؄;XlJ-yt+^^׮-yѩ-d_EQa9y҂}g=me>:m4Ŏl0*5Na Iޢ{t@[2Ve@&BReǨE33^/YS:0س[A2F[}LTՈg)xH? Vv:B @'R"h*FagX,F5ڈ[UkHk$ h52+Rl/Tl3P+8KG ˵qpO`rX Eșq|{@@v4zL4" j3yӫ1 1!: N,>8!}=$MÃ}!EIд7[@K!AVk pl>|d# ʛo)HER^ӲŷBԵwVM(~ UX:K#1mU@#Tae ”/*$ lҥ|ю=#U.sz@h'ޥ(+ ßt? | \ʇu"fL*v %➀ɷP;L".Aũb]|94 N=W-f5)l#EWBx3*h{;dשx)_Sh *uxD@7?4O:s'/$E8`Zղz8kyG{/|X(PІ,,= Ԅ2XّْN?H7Yp{6,vi=5VDy4fp ,E3ܷ>O0cHI)+xb iuSn5UaqWcq͓~^1\ %ēS7>M t_[Z׳ۍM98e+x!ք`} AV+BL6a)F!nvW[ U(Xb:usd[5掝Vx@2 xs!>V X6 C 0L&-p x.x,a+m|*L ur)'FAK1-ʬFBt6"n4ZkJbPBg9\ DxG߂|ur ;=& Ta ApK߃R 'sGrfÎMLkVx懗8{嫳YAq?Ɔ#Q'5.hr/nWh02I/(KU’MX-tQ{-~s-{eiސ4X|0zճ^ޞ^|Wϧ_|_/={;~3t/Hb>ULd?KvlJj. ϩͪI2!0x9=ŭQ>4U.6$⼸-n8NhEeu.sU j {NXCrF\y0zVB OUUC`DcBb\ٵNa'9#1y a l41+Igz)G@FIC [:@SY(tU"L8٦VWҧ~u=Vt` o1֥iQs)'#{g (9^˵x}Q0;v9’0eI3spThl/1` &#bܕG{0 m&jA) < إ+ZtC4( NDѵgqTd0~TΏ).+sq'@OF͒򻬸Zz%z,ɂK|@dç.oFan]Bf,2`Ē*o`-0ꌹgb{?$]z^ʣ=;Ǖg F_.`/x@ßF+|tro{>JW.zοXYL K35LƤ zTm(O77b↾M_(7lNjF6JDVd%7Pwb}U RgtYWQ oUn'E9PS+wӽ٢vvADu!BnP6uV!0cmDL \zgAZl;ʲ׷V ڴ0\0  z&n!]Yф)*/&B~n?xH`bIBmI{/ ߭-J6[ul ds({3xѳN+b%$Wwe}aޚLT2-ip4oK }icҠj )wgatI14=*ړ^pa\mBg}E7:tuw;ca[mq q:p-zO::9z`FunhsJ-|TK߮ŪQb2A] ,X{LnXi0C\pj7ILtj'd@ݧ:!ꦟO+ǞLkv\s:-,fxc76w>=F.sܷQ3x!8V.L3ҝ?-@vOU>P%cᇃ ?ɏi/7M U0{Ei]Vzex8%&y)V9i:+ar]?tH #:&0h{ޒ?͕ڙewWyӕ›#>4RH)w7LFJGsGgkeaϋ9NgͲޡ_)&ٍѶ[Ro=Kɱ|+Is$N֋pe{7AhTrHvʐmA6\dñFcrAfdMRV;G_d9h|6]++=ĞLUIA; G{٦,j7)|@~m32xFʺ_#oD䝬17>01U`TR 4I?.6y7PK~VolG5/"setuptools/_distutils/text_file.pyZ[~_1 T;E* Avm,FHQ8ew\8h/9s\sfF}nwennNTʊįx#.P/?Ԣԍw2W1haolܜhYUhVmt*^xTrMM7UJ]sV,]fx2u#0͍$*㬃í FWq! (-\YKNAZaQ1cX7Ʃ$C̱2DʊL@9"d-sX`Ysk&dZ (K>ՙjUiCN J,.`jԲo8G  8*$Ȭ. g=h37MUm7(Wv/2qT%be-!60ܳˆ Պt2K$t{ܪz)'خdN}Uf*L[ql<=j%W1zV$w~Z)^plVrՔG:<6y7JoJ./Jp{Ms5-Ւ]Ω.ܾ׍ؚ>K @*c3d{0mU->>>^  W.<::c*ye,ok޲. ()WtG/xmf8.(CY^ސᇾDH6;Tg" @ s֢eDJ6vH!Ąel)Pv$VAئ5$a*k]>ȻB$Y0pB}=Bqxj.NpD$_z<#4U"_F e2NQ24qDY.*Cx"<Y$;)eݻs$$)εѫbnY$XW4S(!1spNY͏8AfK OmkU&z&ilًAaV-;iuNYd=_)# lYG"Z S\%em[1&clAY%&%l(2A]5NxއJ+Z0맩l#^, -x~Ld9lN_CL"ru{zҏ%. l֣j[D|8 -CQq2 P#q"@'zn 3o6#@@p}˖a;âA!%|)=Tq #:JD i)o\QҘw:?sYaѲHb=L"HI ڳJȠji#(Csi%e6N'sB k|S>1t>QT,|_Vxx <*hE`ꚻNƇwvhC˓w zhQc鋠W{;N=_ECnS㮩@:5g%9#ח;diE/rƵt+}*>s%):$;lY󣠩<P\M&l)f)"6Ri\?-VO MLg(Mo& LhO[SI5w;| XRkFjPĽ׳Wbr`IyIF|/RoONGn{Fib;ӊpkɍZ2$DSiZ[f.WvA?.:K{Pƍ MD5dOu?Ⱥ972\3F~*xzőxKlnLUGvQ]vh_\ (#閗~@i Ch2kB< A/LoH4P 6 Y}p!Vhm<0<'3+Ǽ?_$XBRΡ,CI6cM/g@E^Maxls[,߮gMŇޚBU/l_МhO6Ȳ)ܺ 'rY o=}a$-_`OC{Ɣn_y`u%8]( MU[w&*tr;qJ8>A_rU~懚,*9bVTy_ӹLfwDrYR~Fqt0@Ш_P@cKDZs~ ~ wwOnڶU~ANۘ{>h3!+ Q4"{%V/|b 3Wp6fr|*ԆI/H F!::˔MRqLa}'CSU'sNZxGV\;Y*#޿6Cft-\5"1ghO.dٕ]͍ٔ+gVHѧ 0U@Qv{ܷۗ'$ #&S+T6d7S/Q"h.g^gIk qiT̙ E-fQހH\qqsF-zX\lR҈D&Qԁ@7m8 XdqU]TS]Nd0gh컢zuXkgWIhD7yfq?Oo6@G>߬"GjK>cL{stZ#1RLDjoGmKd?ko!o ] a®?!i"}qYHm"KNmvtP}PK~VUa <&setuptools/_distutils/unixccompiler.py;s6)Ӓ)ʹmsqR'M3禝8ˌQ!S miwtw4"y&u2ISC})sQf,4w} ;Ҝk3tg6wf;^d3R<`,rYvg3ƾb{VJLl`4cޱ˂qOk8&"͛L0-xX&+֪B3]Tnd  &ONs;Ȃ4 X*^漊8𔩲@0C$ebL׼iK nJkΏP@CN!JS0l>fr_fJ'}l+ѾZTR6ڳu*6rkg\fB܃^VO`}1ۊbUVT*^i6^XLT^^P,fRۧ-Ug=҈# XlZL78ܩNKN$Fłmy@Xq %`q Eu'9J&`(iE\ piQm\9 WYG_uCP.4eR߰hYǭZР뀕Sօp#G Ȩ82G`5Z 6zN@wyf#p+;Tz<3j%l b41뛘MkY,4Ogz&' 4ǁRށ:g t>okק7;?gs@87aow蚂iMҁ8 2=鋑Ä_ʜ`9{ Dr$l!@V?,2H5$R˖ <cС nJ^7wq,#6lxƾ[T[ѴYu}g,AD.[tsuo򜿉JݫJVѺyJ˗W?x%-gwLRBeVLF 0MF#%{^;;hFDZmI=D1x!@*O#tǸL J8h݀1 3odO-x؄4=z s٘S}&4)lb7ھvs],!]3?Ln,Ý}]4]dD=xM+)( >dXWFЈa%0!L^غ{B Sm׼HQmP(-^xuxOكUWtQ8:֞4%kJqp" 74K Q&cy#jהy4Ӣ 1_l4LPdUHRt"q5hݑc2~(*kQhkF kۘ{ F[yo& <:SV:m9560qyg !I[{ߙXgAv2xu/ /YwTX|8m  {9EPF S u'Nm-{ Ȑ k \C"k69w?ifΨ6Ƞ0-FGER UA ];j2@5:O:z` t:%N&o!1˒-2~y܎vk ;xޛ)TebԼz(1 n}Г=8Ѥq;fnZF|ٔVMz~Q+1unAV0*9>0$aG~ 3-eq#q[Y[+hJ;Egכ8QbSGOiFY;<t9&,邃xc6qsx  O zq6Bbqgo\t1Nt sXxP5[ɥ'tSŏIfO"i\:?u8C"(SQN^os œ=RCr8c 1*Ԋ)T.v9/;aWBx3Z`jc\;p]ןnAOc RcpQhʑ* LAh4y='lƍi=Y,o߇>#ǾTq?dF=&2VT$ꇥh?g,&Fb`Re,S} ޒc=*9“oP4m!Z乄NDrt \8QHU\]`أЙc7n Țv#2܉c(s*:W|U}9:Q{QT+nXOr1)`T'9MRr7Pxz,QM0nljtY&Tѹ*mU{{B*9uEOJ=-ҮCY*t|rcCI3Tv-[Am0}.B& #~2F}<#OoŌ P(XyhOeϾ{Зd=ѝf#Qkp=3P6ȣH6_<̳3E-]k JqBg?zh mwo?#IlJ^,I`?k,]sKP$yUDCCέZ>#I+rde9aVy\!ntMaPBV-\PנTSl!æ=4 /Q{hD[;J^cN0}*7eҌ>s:جK9 5Nʦga$1 sxK@K*\zw~~{7nid YcB;*^@n[M;7&jY-U-i`הtn;dNB l@ժ,r+DɨymhaB84LWi]V&5 O$b1w~xr[/ׄK^:,I*vnKq'rUB ֡{ K34y<aKOdF mgj9WRdSӕHm\pUyf͎ (NXu*$ b۫H]q"x?Ktd& zPUip*1CZĻ 9;MG!\9YWfb]UEҬM}Jlwyݒ;:k%ҽbY{˓LF7S7˝B48QsݴU~׹i[UxXxL‰Q5FA뛮Rj"CJmOʌiLm^ZV9_B?|^MA7;S묣qjv%-!Zj776xsR=;2m[?“Lz4:f^qXV T*&_=IJ|Mc;2/FgVv2juy² w}%v҂=qaMl؜~Bz>zNR?OO[]douIT*lE8tHGv9݀0 xB5`%>i:#DNQz&mNb2ޮ=]_?}|lw_;AV 7xGoﺫ&*._4/~ {Zv Lj?z#1O",_$f 3%@! ~"iVj+Rc,zŷ~Xs 'OٿV )TZ 7u?V,zbTi̬(b6Y%R^p,i7![iluA@SXV  h"ux&1̊|18c< 6 "X}8]Caмr:c}` C]CM ʭj'9 &@*Hdbخ/Lڢ=)mo5zvPf1Wm X+UިDYwaJ`t ·kஜfm*sFn"Ֆ:렉epl;a.MHS/SSR6;< $!'#J 0$F@s\Н)v,~DQIyp+u+{%Ӫ\l")[#.5!@#dݓ|+\C5Om mrjo[]z }c1ƚK+o8#Wgsw:Y=e;hpz D`Xj[M%qpEֲ;v\1mBW2r {|] 3E#`/+$q#)0l.A0DI-OnWϊZ5W3n%n10gvF D#7DGl(XoImDKX Y*`<,!|Z~J[xr8x J1), ZgJ i O*5dL*)Y:߀^[R,_i*00&įJ+W $iӲh9[7?sR7qW?a=#&\Ќ!KS^."$S$M 0} Fxު#Zov~:>φ/2 !UmL#?E֠p-ආ?s<uL$ɍ6l̿y:}2|HsG$N֪$.Eee ]b>ĘOpw@"H㵴9& :mި`0n2\QgRkAyܒ{úEaP2=E7:qIf-a&XS5ٹx}TEG&,pX%nN#!?dK/MuB任}6,5>go,B \lv_j7Fȏeʶ?W##>UY}.p#!soIT%u hȟ4Ɋ NHGvq3$96&G] ȀFuu&]M+J!@4-HqVA, EȒLKJٝ . {ƑޜSdL,C j`aބ[f0l W!; =H پ༑J9^1AUI /@yReqrs Ǜ0geB=eQ;D!3Y,V?8'< {;9V'4XcMvmb,)wurѫ1W<ǂP]i |W.d <"ugGYo߾w2.:{.t]U*Wo_.wy._ߟs;aNѥ0ź'1] 3G>kd]@0?/g?(d]2.y4 7?PmݒRBdS:-IIABS!͚ó٠} `$㼱ii3ee-1BY'm!]W]z/9N?zYӲw&nr6t8j>Gmh`ha*{ī4|ܫ}Ht]`Y"l} AU`Xgf܀gZU\$aRn3i'&Ң`ETȜ61׏` V @yɝ8d'ݒ~heaL ɉ2ӒrS>!I^SX*k ;Xs}sN\w-:Ĺ;B௭.STnH]nӳ!"x!7 8&I:f@?)6oYESU e9ݢGXJ̵bu蹂lR9 ׬iMShQr.%rbnPmfU四nnn8vn~hGI_`h(fZ |6īwxAdK4F wY40%vqWs=̮*ܞ+jͱH[兝#25ES0=8s%wTOr:w\!KCtpL)xӴSs TNs-W oF6rSo9CgEy'XC7Ϩ(~9^$½8! v—ųGY%wwh E", Vr$&29$FxnNayR?R=޹`X ){$" 65Y됝JϰKCO(A/zt@b>}h"ᚬ'ro);/+lGNU)'䆦/>BmJVƖ#M_7/19w"'tvćPM[O)uόEhX柑@ rJ!'h&*AY ґq)TΤ=wDgf:U̧V \X= hh#}%%+$fȩ7 tE=P(#vudRӥ#?y٭8_e# hc< OPh ͟|Q b&=Jq,z0Pw=a콟Dn=n Q WɥyrC PG0\[-XnfHQ,X"H4*1Ƙ^!}*lo*oOMP3j7ґՅbÙͣ3KkwG>*c.t43')Pa{f)hhjTcЉ(eByR%t6V=㾓fc˛5`QQNRTziVG}~ r+؜[oH&}oD .97z')sݱSޒgߍr~r8 yu>ٻ?h?4i<~=.<1 n2rM͝T.؁:3;*` ԋz"^%LaX! 6ϥ"ë71!O6^jw~.m\Td\LMڲ[:uYU۝@jeO+r\¼ҝLdS8U<* B%I_#|_*hA+5ڬ[p bzbÞ H[b= ݶ$'0A{ISt]WtVa@ds>$}-A~‹vxo%k~tQvOR2Riڅ:܁We R]V=Up?g'ۃc]~CI! S.M?juj5JջgvM{!;#;ߏKๆtKBW&_FWK|Iu/K SЪ5) ww,Z=5pG8x^h`ljWp^rxX9YXB /kzXA5m"V8}̓|70wG/>ޢmq+QyR`cg|Gq^-]\ Qn# lbGv`` bp@Gkw3>jLC/Yj`AҔ[}\X*D;y1>˶MwmEL-Q&_ΐښh1K0fyK_{x C,U>̮q>Dl#6{^Fו-^sA{մWh]Fn\^\DujWEK|Te*Țxnțl*D\?-*Q~sE`D7 ZDggxFTŽNq**+e,>FL\ ^i$w{U)!THQ?4ꖾ~*߂y"TF?DoacJwRhc$8 VB㸔Qc)H/ a@&٘"SL.S$[h1^:p;JjK/F*(V=ƪ$-v0o⚔o9E#e^xxos)TdF$6kXv!nKP'4*^mn|.Y%TT*Y&2P/21IvW(ghYRqVbV=(Qg0Z?+4Y>KHf^IMkGցFYbq =8_Lh Ll%&.ppb!*%2K0bϩxی99J< <_hø"D^g!-|JBe@2kR5iIж~}CIkLm,0]V"KN`6.y+y隸G >DB2X9.IQύ"J̊Bg3 ]|iW*Un#x"7"El{jrG͘8Vפ9M=3 oR$rH2@H1ǙLuCI0!3F*֑E| ´v Wgw6M_ljs+tiƔ&pBYXMmrBg=3Xsq4- ߂Wm-蹼os"T||(`vDWoU LC JZCWX  pfLYal`ۡYx Ql[C&?&ϦH؇w(|/o߈iHئ$=tEÝ@u~Fh&J_H$j&2ӯ: נ6IC~VN33b&^t:_ynDA9AH'xy7R@H<ldS`'Ż"l>j !Lf3LͨBl;}N9B,u֟^=-Џ4Kqs4ӳr]L ;Jz pݫ2dUPl1b.:?w3@5FM1ћ7=Ӡc{?ՍO=gŗ^j[ U S\ۚ/{_m2v})Gx_^KT&ns08f8ou[LrFeڽ1d/*J0ZiXNH\eI GVT]iGS$4jrQSDmb"J<=ƥcsΚ{bij[b^ݞc yQn}Z׷ASBTZR1ֶT6 6v-S i[ApRhcGp$ҚzԜzCe/kСϮʂpdNlsMnk3˴K$@㍪}0b-0'/ayM$Q,I7qAg$LNY }a-޸Zi ͠9g-{E㵖`t'E^ l\Q& GKbZxkWiޓm`B@2Z)UQOfdք (19wY2"nU CeāJ>2,Uf vh|Dv-WF8 Le-FV}b?S>FV/m LJtTܐ󏑓 jv5DUcI:%"Z:_D+G6b5;C v  |,ϬsCAnd4iic/t3W\Rx|-d.NǝTF$2#ۂip'Cu_wb䫤lREu,I ㉴x[;Oֹ6Eȝ=mb ʉRm2e "pS8Ǜ׍+> w$6T8>Zl{TETzj ω*s3]'_؈)Cmq˩>e딓B~O[21br esұ噕L K+.:~, ;Pa~wzjލ I%eĤa\5f|oZէBGn3w*ȁ]5% {w;&21%s4ٍ#7j!``Pߟ"h8bB$jZFT'}$BP&h']E˅l-ZOws^z-~-﫨L!)׵]l{xzj^Sl__`wfI+[#zf4il<ĝ\ $ ˖>3NT>> kN@ac-4`4)TϸPL8 Mkhh:htnDowc4>viO;b;8eHݶuж*2rr4c eSpI+EX^z8[?O݈G2zB$ }j8kMg{u&bB]+fHN{uxLnd_ ѶE>sPwN:fh{F/m>PP6P8_<:]qz8Tý_PK~VaU)setuptools/_distutils/versionpredicate.pyXn7}WL6 Ȅe'/  -&CcWv)yeIv{g e1Ȁw̙ e*YX,0JzdUlTJX8@A4@AkpӚR4e= j5,M>8Ye^ |F]<>> _O.DE8M{)N x4KIEH"."|}D8?KqN xRZi,5oQS]Ofӻݓt3|V"EijAEZ h2!/ $sqߕԅNG|sO5NWV(jƓe}FReKAC>uI[}[y3uϡJʎ&Tb]J:5WBo*kQ^D#vQT#Y,.9;Fv>sY,T]kz5Of5Fnd< lʤ dn=l6-+0VFe l,NFp~?qzJg0>])X2W ^n@Tu4Ҁjeƨh 6JuUdHP?v aL%ŮnUZXb^mrYJ&rK#D ,l+SapkC,,Z`zhOJU2_ކrebYRF# DUilK Ki-61K8$81-Xw 1[nlJ&p,CkzqNܰw2Ӈǽ_+ `~ *S7)yq*ԍ%\_ %ʃdoB`2S1ip/bqx/nwA^Z@HJ.qY^SLa>+'R[7ɏqƢ:ՙ^M |4uB~UڌP/(A6VL XL[4A-&.{Ñ[JÌƖ@8Qֆ 5@ekTRZfčv1#p[R?,\*rx@LfC^p|*N?_k_lв~!4EjPl5j|*Lw!*C.UY#1Ot$D`Y=t*HR3S;2XIgg_;]xTSBDV>9_Tk( Wg.Gæ@w*c7ɥ?8đ_$">+)? FAtϜV֣Lrk#=p䲟K^b DbB=\BF;ǒB@]j-(x]Aa51 sR{1,rCu|5D݅* ltN˄[WV bXnb:[R>aIWhkEaoL;k/bs=!kc`0 73pKߝݹpVYQ-EvCHXJ97"+ЩХr6c8CrGkJԴdҢw۵kV_n{oFDtxUj5't˗%ӈҏ٭z[w{h28;PK~V) )setuptools/_distutils/command/__init__.py]=k1 w YJLЭt^ʝͿSpwڞˋ֎K+K9=7 68> yj\9g@AmA堵Vʘxw: 쇙`mW kSlTKVC"Q.[ a2(*.k^:I">PK~VSOVN2setuptools/_distutils/command/_framework_compat.pyTMo0 WhRgCOf`C/Mva YdHr>俏g%P_,0 /l)U͜ȅnOJmZWؒD+R1xJ@T6ؽz\Nk9&`+?U)VHy'4g| QPP,PDǀCB`k.n o;RVVZOq@-jTA66I;vFbM6`*xm-|Z2*lm$oFt;LId&$BMJVnm(V7p3  bGu;ɺ.1+A7a2[,OaGn5Fu)Lݨova3[xX\[TL0O'+А҃P172 a}z dΎt6yH7o:!9_e[Ɔc+gBqpr*墔lewrh-7vR9v,cP="Ol;QdzIk|,AjՍ~R3Bon:05#v.}6 ĉuftQRs&Qv2Qqk $6?NZ[EαL _حpJxkbPK~V &setuptools/_distutils/command/bdist.pyX[o6~ 0[<Ev -2H*SDIΊ-s9߹Q\Jg<(--uLM̞7Ė!&$b0Bɶ!w[.:ȦH( ~2DjHԋN ň#IL)'vT:eƋ׀^dfS;bQ{ywjt^-7 C*K+q$lRDUK{QbGE~@ּųwh:n!dn-Hg($rw)r^]}}jVk1MX0f ,N9ٳN 9⭾RH^Q J@$ύ V6z+gzt=S4*Ϥ-4>jLmڬ]ERͮ%' yA/O{#n^w#4j\ zFA ;&;lYq.` &;^1$k ĝ6":e }CSc_A/_S +jښ#}s-`8q; N׍/F0"u"uNzZ?<#S&d:=~Ċ'iX>=![UHƂLD<#ky_\ /.l9؜wͱسnm Q1,av35$7lt6L6/-6Z.}:\!pۉFilkW!L]٨/*G)b!g\WȫC$p'o>\4K r*VVTq$oc?߾Y͖ ǂ3[|ǧgH\WLD%O|3af?ukG]g4*AXUN9A3TCm<12WkopP2l !69juNi-<\omlːfଔ 9jG3n^=}Ssu+&[7Dw|7F~g h[D*;CC~> ;LH{h8`Ӯ׋/.40]8,N +3rd14Z-~ Y^Ѻ^` EY4*0g$(̝U3{zR&RgP/EK-Y 0Z"d%!Gp1SO}ͺiL ބmES@0s>嚝yN'n ;[V{dѼ -R쟰vhq?rD}Q ;q=v#V:оq}Gl4=>NW?$PJ7=1ƥgO#s @!jW n;*JlNPv9Ìl75^MO7Y:SY }@ Z_;{NFIxZz}My8gYV?=;?TD)#rX/\,a>#U@Ѓ) 0 ^;wj|!MEHؖ0&)="RT,k6r}SS݌ne̜Y>k[⨀`e$aّ4=ej>03gl~| (vPЊ'&-p|`xrԠji-5)\o=  P\]q*G#qfvAџ,WU<4'88J1R'L={mX+: fYl\43wFHP,=4IpkNH=;}?~21k< 55Ҵ!46#*#O`Ŏh+!w̙^FyK̪5~-)D2o+$L)DHР鄣&^.;ԮgB=jZ3nΚb:坆EV>"Mj.0}x~NAΐN6{?8Ja=UpNH!sZ3>Lc7>#|ZGz4fvVoey=H"~|1K-Dž?0U&\WQxa\SM:~>f_U_e>U4pg:2pUǁjDu{`,7</ؾL# U.L}؁.Jf/sI=z}ܔpGNα湪F^)m4/D_RW۶`Fa Ulݐrz.pѳ uĨM16 '9|Np ^)F>MATi#KG*aX׹Ya3\7^4\漿io7$?`9-|ϓpqc3,QӨkϿPK~V4U*setuptools/_distutils/command/bdist_rpm.pyl+.n$@j&%FrgU]I9&L96E98xiUjer󢂇gYE64,_sd8Aɳbe=ϋ,eٴlYyp, gTu-^7'~M,H-ج@xQdEfrAfyVˬz[dDMt7xj /J^[$Y _$,K'39VŽ#7*LiMAE| >S#~<<,;egYʧ?O*+ h8]y'1IL-~\]!7:h"etRXWɼ_u›KP_:N`embsʜG2 z1qZ"A"EibK8- ͅ B29ңo?K~T ڃyXs$T b:,~-8҆ZVG}1#v0ZDuQ -k"34@p=+0GbmG 2`ttϿv 41j&,[4 ۤ BGU/*<N˓)RΙPE:@RtPWE|a0YxM KNcxC Q.pJfu u#%Q@΢j$^q- aT, esؔlX2I٘PLpD0$'N㴾 ]њuT2!>YH7٢NLx4tUչf$&h)7'Yx^s\[I,7s^tN/0 6xs'nxz~Dzp,_85neA/{2cbA+ kO~^JkP |=Xf )trY"iBw^k^ pym1178Z Dg#2| R86[t4875t0qWxXd?[q$R>EaG q:tޤ#y%`LmB j|iMZd Tl V74Q 4L*+zh4n<65_M.M.N2>-c둮#Q(«]]bu2 W 'GIޞcBu(lMG^Їtt43zY}Iి$6[4<'=/,(^XPt$b β8C9e|Gΐ- ,5a(FP t89h@4Sx;6>zzp~0 #wguz:@>J.ߦi3~g \?h> ӿGKӓWg|HnE\nXqrc[C}t-A*!5E)5@u\ }6vIfLn&Av.tЉ JYhM-Fm31iu'm+щI Gj{EKe=xZi?Dx)8NaO^ɓLiZJn0p' O).֮e|t (#aɉ~ s5 5 ߆T;d.Ý Ԛ.bgwec&,<ӝvؽ`I#3A""?Ds bN`m ; wz'Z7~upMJOi,`An^_,͆47r[`PII1IY$a"MH ~xTmkq?<aKWz6V\*rGɩ8`& 3D} +T:r} z\Te*pV3K,#0GWZP!3Cp+h+`bׂr4YhL ǢnAp<&wrYdjM\ԀܾGy3 a-?~t#ˏ' <=/zņ>/ l1W EH2tl HVSGںx{W+j \=-K)L3}_ʼW[SHu+ Fοݳ";EMOa2[ `P+BYt5HFdtm؅MUY:ߣȇ""QS՛4bo.5N *,ܫS0Yc+vx)w+ǒ ?!bgWށ*C0n<1l j/q@Ω@iG'xn*x=) "9DAWo˜ Kiz6 N`ZkNnсӅ)SV?]6VD0wwY>t{3(b] -P>]@>o|e(p^ ۨ9ehR;V퓳Z$F+mtQz[7VbeU( >Y ٺ΢B;+qS1 Q|YVq#kjK{%DX\vPL,-M!M4#,ǯ2,~hSeaFhƜtN)-4GPe"fZ xJ]u~7ͷh}j6sd 2*:XnLΕ9(5F?k\EmQXڊd ^{d{=oz'&$qe!,Ko"0jŽًu>TB,%r%ÓX pSq;m,иlM竏H3@Vu)%Y9NF0;].sކq"Jy$3 )_Qڳk*tLmIf6 SoN'J|? tnn݃.e Ɠ ҈ZgB"ʤCG@S3QkЦ8摪iǦ-{uY48?7qp:IuMGV~ >G' MU9zN.|,>ID]\da wLr0h&3}."4NDC <5s 4^6Px XajtFgϓ03S[W`No>K4dNb>Rb&4p >Gp>هqF_YqYׂO/eu~\r !wC5o.yr^E6K,7T"E3f囇0y]Q4&L}dJ0.pY^~y& ԜV<JC_ѥt%e7n哷k5l0M&,ۊw{o&UiU.8BeK uH#/k'}tSܿS1@ee|#и6RO'QIT@Q61ؒF=_WU~^2dei\53Z2JuVoF% (Ҿ}\\DC_,QzELI.#_nGaz'A-n]>C+ﰜɄ'pN͡}L"> LWՒ s'u &B%?nG.FG0wm]?c 3X$޻q(wݗ؆;0b[|(+, HQɩnf/g }Ua1}1ܫi7|'f 7~BA ܯQȲ TƇ&Ұ y*u/գbTxIJ yäoR@lzIQZ]D1Oݣa+jrU5Q]]w/mxus\ki7s7T2VU!Z,nyMz [켼8b&,2ɤʀX\VLD3ؽZIN7ŧ"9 ǎ(5mT{s%{I01S5toӼ:z!o{ev:سR6yTݟ+K,hQE40ƫCa0J(=rV@8iPnSnZAOHz5;^`V 6B>啗΀JHp><,,f+M"N11w`ɬQQlY|!F7:`h釵% I$فԍ&. #}&*׉k:ZLtMw\]|6aJ06Np!fprJ?.gs4%$8qmֱ퇥gcb%>z .ɛӓn{[*07CPLTC~:w}QYGOVi+NrʺSqnFdb:*KqbS7$!37+M֩ۖ`fi32ڦbBelIg? }^˘J/R Cʩ]Lh;?6W,#͖OߦͶ7}`WV-1~B?jA"#w.ݵ.r>Q~%P:L*t-jJNC uՠZSԪdϰtY-aE od]4=l*!LiKJJ{%P3@xj#4?qz~2BM7 aIPɀFKަ3巁˝+KcwbLs\6]`2\MВYUqA+yD iN$ݍ4&uҤQPK~V&setuptools/_distutils/command/build.pyXo6~_)$A3`Hm0Eam3HxE$rhjǻww$I͵ otYɶ.=o׶kX˄HjR$YxIe>QNɖX1޹Ma)%?:å$r{f6]CNvZlGA>l7L,Yxa!(WVvW ՚fqfRzInIbE;2u2.D0VIІ6M4ll_9T-6]'oUF:oH÷S4tbJ~w_!Xom޺v@XwzB oYV͢e9E2 70IHy5H76. R@?Z԰6l&1d BЖAKً$D8B5P@}IcoȵrU__@(=^s qd2ԣ9u,F k{(v rGšw^nglq#I ږT4y v <늡o|ۄ0M>{\C JH{dUoqΦZf1N1 3kN/[R6)#xk ,KEϘ G{N3ތ]BfN/ &3͚'R|:Kx W5BBnB@wK:nNɁB?wPiӳ R5 u9ԌP5^][ ˢ'$^ExbsɢPSKҪ͒6&~=P~qL!w8BV*1A ѳWdON# "s?pQM2i{GP1 gzƍGNIb=rAC2,5أ򒐟פ)+eP$ui] E4>>rL(.)`7%zNO^H&)j8~ !gh,WJA"`bq#'ɧd\vGZY@M*ƨ =#aTlWǑ! #!FdZ҈RrƏB$z]^H r,@>]ȞeǰQC˗aPBL{T6?kw1OZ0h|!bV\FMs~y`0#'%zpT^ U>⁹(ּ41fM:^}r!<%T^"AlZ 1n$IǞ"sNl)7%`x{UD3ƍ ๬3)=Ϲeax8Dwuy]p@R o~L#8;JPո|4aoCormobY4@S2j;/ddZa?֔)XΐqE? =o {3㘦z1ѯȟwlva[߀呿k^-1fhbWɽ*U.8PaA yMx[x]pIqL3'o/NhSV#@%lG|;2xN:UY>-M]Ceš+AT B PR^v$=1ůמם C[  ,P AtR/d8.Eղzf)THJ[@3uБ/$'#dPN;jJYOftN`#4 AיΆ?i,L[}P03-p:Ѿ6l@}:nwo6?0goxR .ki2q;9H{xy||γ\Dfǻa!|z #iN: \&t.5:첕MbJLMÆ3 -.wmX*K$$CY#AWiDl#ɩj|\>j:D(:jgS `7ͥbJh[(2n~Q/A8ga&&n{ch.h0ц*, d M"+5PP1JgTA"zmv4 :$/΄Lk7( u;7=^5WH׆zM!|3ƪCN%lW']{YnZy+BYY+#R+~BZRHF:R*1Ins٣N6ㆄ[dKt4Lgs YKlI&*:?2 8(pmg_H T)%5v+=C 5eG''*E=TԭFpeOa'ѭd-BsߎU,;ĬdK y.)Csu!nR6կ(4""G5^51.CUd/_hfݓӯ|z"v4ESuUtMQnogH'Tq>j2#.K\I* O$6ņ ngb\؝Qpx[A!3wl&Q0C|(h>@I%B(5Ry̋qVg;^|3FD +CN2sl3}ۉʗ<[W+f6\l<;R4cWRN'1olݭWV>_S6F8agN~a77D#1Z%;_m" :k%ew[}%0)XV>[b#Ԙ~Ȳ bpz&;xp O:=V%}ZkqNdz1eGjL{0c9F5[4(3M#aӚmަH[GX2(3ȨL&e|nES}U yxbV4C "y&n&,Y]Bϫf+U<ڶ}]CRWje2əiϒeRh[HΞXur?.OgI}y6ӯG:}Q)wzklzLtHYPp[?>O4g $R"jLYr8bVgdenWVX mwNx|5 (QD K7BM,r+ 6&Y@b3YMW7g /&aR \CvU@<RP$Qb_b~hl$Dk=t#D*zY|%`[*ÄKWXTM5,TA\PZhl j  oQSW-8'@$N):aԆVC=0a*W7Gx8 Z1r't#*@/[@h41݊$H`&I@Tu*nقU42251B.]z7R ⶨj\e}*E~'4 T?/jkBx̃ a0^k _Q,BHF)30>&QHfN|@=7DN7c/F_,*_"{׏ 函W^dCiV'g\r p#JUzWyLڅHd_?vH!X|Zi_FSЗB36Vö &洙 D6cG&Xa鱐MrJ4LǣT.1^kjVD,NK N+yIȳn;,CyuԴG>5ee]NO=pdFx/D0=T8XJdi?{0xS{;/P |[uþEs n4_cz^$_>A֡Q/~^W ȋN4sK[5zݍtjO!L4TyRhP4nW`8[}z/XROUfM;O;ח0FtaIztlhX>M:}HcCSg7.5I.QZtNM;6v4QfYշM-D(?d8@P<aqmмmXDao({{0g>?Vї&LFg?=E:J&AA%FOX ['I >@S > ¿c@9Q'[1(5+u:Qa6?$cВ'W ؟mhƾ aD-pܵݍ2C'U y+fSoFw>~AF(Lq Z œC$b ߼H#ۺ_#Y#Dؖ/NǟA/{b!HF 3%{9uL16- IH'&4Fx҃ vQe u%Z1+{0 Z~ԲU h S)Sq}(i!BaQ]i@2aLρBŪK ڞ:8J ʪ?N|w/͒oy;I*%meaqOߝJP//{uWOf t=N%d};SԻq"K @`ruA w?8Zâ]M2d!.}0-`WL, jsU:Ƕ70y4w7 RП^OgiDznj1ÉN, z%Oȕi `ypJa(Vڬ`,9B1%nhG60&;A^)h>PI<11_0ƒh8N `+ @pgF&h)1:F6Lc.잨kEz{fOb:bjLQ@e|L`>.4m Xs+ܬC\H!sQ9a2U/D$SLSFtqA. "wh@g6^;aN!:ȵ*lFGw ĹI8GJylWh -Lj,8&ROf)ISn@;RfG48,,]W,@ȃcca8pdx"L0jszx-b UC69\!yҴ[ \Y;x#xا`AF:G$ZLÑdHt5^Nnؤ} =sʧH8O\(UPeG=r:XA `5CEe]z'| V,ʩ7бT)$oE|fU4'Jb&<@ sB-W)w" cWD7|m?JŜhPPfS"?9UR(}dsPکzsYu;|4FVp-|4E;Q [$~v0 *&-:5`5ӥY]nrpьP82J@!ӚXwNMC;Tg!,8F䐹 ׆!7&s0V׺4B̿P֠RrUةޥL_E&(,oXn/`?z`D$ŵ!;{0J|A}ǚ+?_(88$gF=*:ǔS0kدࡉ߂gOhB_o=Z%DyF`K_j[ixmKݢsFHkQt{Zi!`P-f 0J3650LK/+DE?oU`Ϭ. T pM|S)L[@?FR6;WMgb80&$4mW9-x g) ﯩ.Fq[?:@ ?х[=Ӊr<ͱ|F͵C^ ;u?%/bL}~Wޜ~J?kOȵ'֊ONkC?鯏>GOwiP3HhI+cɶЉĈ bqOӊu 2I#{p<9xӈ;}@컨<7l&M@4; 3QlDԐfu.vbEGCc0BWWgV`NPl> "GFR@Wj%yV+몇|4MF )p 4Tv_ a͌$ /ϯ6P, 14H>{L|(:%G入RPI-n)ˀ^1\tHku[~F$¾@{{HLWaRU } @0z)%~ӹms/8'xY#_1/}@f]m}Nn>yzA #fzNx=ǢttD;Ea$d']jXHB{Xҁ5 '=^| FBcD :HE 9'Or<~MӋdiMALY̒ާNLbISqf8XAp:%ἉbXZB*hVqq@G4:MR rR*NzymHcK::r5 @1bsyt|f͈Gt:nجڒO;M/{b8JhAxtC H nmC^]zgvHCd J61yj| ߵɮ{!g(f4T6Pgz^חzچ%N#Cb|EWHb+tOz#>po!@ ~6{<҅Gw[|ʑ\J>2&ӽƤo['hݳb}DpwmYFb`+,`$O%CEA(( &l2čCdpf_|6?܃tU>ޒ7#F'Oy?+;} rn;MI򞘫[00΀=1;!abh ral(:Ѯ*K@<,"FRd:h$P+(kJ\GLn4 fԮ}d W+<Z2qOe>"֔TV\$Cr5.z%>%@V fL~%{qxYExD =͑B+!! \:\ @TIBk~0lܬwءo1`-ˢGVq?ra:];8Uu l$nL&?a j i}FE]UM ׭_jIwF[^̍h tUn2:R9JМ<HڈFe$=y&f/_4]cY<77-bˮtm~9G ,ʌ!;~' nE0Zsw+Leq wCC-\N78TUזy8 YꑋiUhc|J@gBSڵ|MU'O&mOcK: RMi$ܿÌi֩$7'ΖTBY")3)Ƀ?$X;B] bVf$]u.of-{Ovdz䡏L>Ο!5Ȥ՘l/vÆvx\+-Q`Y2ڟFʄYKC<DKnoEaAX(?KlœvY"CotGwFB'Q4`STz$ȥ~W5޿'ɼ 2F4 /B S'WT&lONZ&ZT] tywu{k&K}'.1 j݌T}Dنgb @k3xK,C90g:6|\fPImrpFj K*zۿ<0L3Lju널zS1Jd7Xyy>׍'v:_)fX,1f;(γu,ڦ8FGyb6" YɶuRc,)D$7U}o "r瑈/yPE$_6g&<+# 諃|JMc)D_ylk*,u_uͿb6UI%SbƅHV TGфRU#u=OĎ*$U|lQ,*O= O%ΞD_z[pk5c?*S,>Z,j򨮎犛E۰gֶG@{«:tL)l#S-1ja=`фQoXS4M3ݭ$7zgl/FOdP׌0; O˒{U'ku<.9+&x'|\CD(EDs>3׸MT&Ǣm,8Τ- |ʪ,u[-# ){ ||;1A-蹠AL ;]̯s_9 iFʽa81عk@,QUr-VHNNO(I&}iX>*:\O`+>}ft\5aq4g|sKâbqL)٫):d8*̿Oe[ #WG:~lp&Y?/$PWWo{0OwbRym3hW5{h,hp YZrȑ-ϩU$Ȕq7Ns˖<%tJ12tFy< ROd0k1eJ4IH{: |ފ,8~2Ꮢ}($Ɣ2+}egsBeadw35x%a|(̈́.GgcY^\ҝۙJJUfU~xYQr>%qN xX?WBl 7iʝálomo>gR|ѱmHfd)5ݮxӅӞ kUAE8S?'׈tW-u*jR-mPQ2 t_˭uth'q*z'9eNtǑFA]ϲb?&/ /' >9M#yKIϙㅉÉ3"@ИĄ2_TwtK<=@G]AMQK>F}ΎPҜ6eV5l *EWwoO_5U8o\˷ߝ^0*x]=;z lVRPJ{LƊK#' Pzv4˕U ,kF /~SKcxpZP5i* j@aD'[(iXᆂoַx_b1`3?tvm~.>ku:N-|Ps)n+NkNt?x%0 S#qA Sу}%{o.߾Y|o@g (GTv!,ć k]Sh\d O߼zbqy˔n?pzte*rӱTz$/_oPIB]ȑ ZgG PK~V!~S@)setuptools/_distutils/command/build_py.py[mƑbD V|o˱+NJtwZo`, POw 捻+Ғק{ժn|Nnp}ۮގ竫c'%}čɘZ8L3K׵ N4ʳp flLBOaWt#1M$Cqn>,_m;A[ G1ۑ{|8긔l7\o|j!% [q?xx0NHWxfa: 7$vbDL[?x,៕i~[C4LpvwQ?˜?%>C-S7v[#aO:`9%߿a0G%WOep ׸i:}$}}Z*_o;k] 0(XYh(| 9( #;0t]%hh/aqtuY&ٰow nC?kj?bxM>=|hsv?0f?!bVM?-)m/j;7ShF"J |)JHb1+K1S8Ű3#cό\ъmD^5*͏ZPe>|Gmj41+ӶyN;t"F|p'oYd^DRL";`(9SmU[~=z(؊όTC0:7yZz!jPCh>S-c߉+%T6S/^rJi.L)H Qv&?| *wG4 .AR$aDKC;Kd`F_%L> '/O?;btra$蝘G׆{pt1b"Ԃ9k(!51K#QB;F¨p&&оD-sl OY<8&t;5И Ah( @O3BD۴Kvtc'QIQmg|$-":Ms[4ur6ha^=V4L0!a'f^iByЎz'@(ZtDZVT[c$XVJi-O~ZQTث3M"sgB ;F-!㊊- =rBC7`U`+|ؾdSj(JxrC m<2:Ah52*0EoxLz33Yza&!^װu~ʀIvb V&GVoyhkc3暝cOR9Z׈ M3fƁfXmƕpx S0a $cEcJ2RjK›oy'ED0ȽnI2P P̠M(w-LD^)c! %_̖9%u`LŒ\l\{ti @ZV~hl񸙷4jdOpp8y(Ɖh.(T#kܹ]?xRk`8NLjg?G+/a ~ XJN~xG0?m!!-ƾoub'-gԃSp`3-?d+6W{Q=bHG^aϨHزXIs!݁`;|׹q. HCi~&LJ:z;p1cuf+A\ajZ:iYr-2ksVSu<04b#/n F>O*ot_$PJf ,\W.$ߖi=Tx4hcU;ޤoD*:oxb5!@zXᄐ.RVi):%Wԭ |Xp[b[V%w:) rn8- ЀENʧBLH :[lDnߜԃ+_1?+HH[|8asc/0'g >W^,Z]2`&CAQ.aV]p,$U9qUWqǵ/O mK4ۨ/n; *KP77 KI̪u`03 bkUP1Qa5Rµ/TIs4~izzo 0Jך*nGZ>KD䃾J?}7FrGuPH^ s3YOdͥ{xɈXwb3\0]WZZC5_}`dSM!td@YA"LFMl㓀'g=p!4ELT{`]]ّ6PVL+Ppi^'̹__8̧c;tL~\zy+8>q]WLM%)*z|WڏqMO^U0=Se)bzsfb\ivzjk.O{t9Zg `$DѦh'}NI\05;55wS]'$ 4eP3wq {8ܑ7ՙZĿo?1uҷl-Xa&0kz%g W4\xiLs`ry\ȩY6vŶ>4,Aj i("pcabZעGǍn_9ZO퐬A]pJD|E3yu|5zH'fgmFtFȌqV+A/rB voaZIn!@R6Q! ߑzC^rWehp/juZJ[ a{1Ҳ+$ \)]گoNJǗsQcy -cŏgPo>?%--,3vN !tI>4:g=!vpD2-שYs\VP/a- x`DJi \yn(jȗj\Ew05='i L0eKfu}~Cnu T+ztXGrM%:V7Tv|l.Eu;ϲ;Ѕ+Ö~\_"TK'*득#o;Y<~7yd ~'.X"'@z1_98uT)!hؕcaZԮ9l%l2.bA{iBzPK~V UT" &setuptools/_distutils/command/clean.pyVK0WBi4vx8"T958vd;[~=c>6?7q̅uf.KxVHd*>N;$@8.hyC~:|( J*]aZ3)0ivپټzEqmt0B3n quK}{gȐ^c( ɬϪG[Q9 +p3sh!HZH3 E%?K?V |3$`4Xqb `kLst>gf7<#f~(vL NǩNy.2W$x|1W#pMvURPں2t^ _XKLYP*U?ءV-?!dd .-*Y?ܝ"³wR YYtZ :ɫx(0[/#}P=˸Nr=2>Inf$ۨOCVl5y6pWP^PJ2w=.ZQW \bI?Ł.bҜHM592n{| ũ1"*N^n/ t |t6NS/=q$N`} $ iե2I*k2ým@ ·" Լם֊`zmKf⻯؆ (aIX,8l^/| V64hxK,@8 UA׆ ~&$$c|̺* WOrRy⪐F[QwBZ6?=@ )b/kAAZ 5Ѫ\h +⾆QZkilu)w-GfBC'ښ_ c'Q:C"p|LVRqdC *KkxXzwuD}OZUsU"͢GY4dvhy,3G/]TևExb[ c jX%* h@GKPE /;aBcz $/ɭ7-(?6-Oh@ +@ OĈZbXU<6ZÀhA[zRՇ+P)E@T XT5J]n5!~zC>j?_87PC#3@p8@H@!(sꙻck @TzEZ,]ws/rQGn/7sm&D#Oا2:z<4BkLGPju]t[_ j9 qxė^g&ml"D:0փo cIu ժyMC ?Sd,` $:yvbWC.jׇm^,5v5)Xl_ry/6T| N{KhP=;C}|RA;82ⴱN|r1h"j 5 68K_XW?'3ݥ{PFf~iO9f^ vݳGV&9bq͉cW :"M#n;xKf !@"p8[#s¶κ,<%wߵ0Ksnwb]^.`t'Z&r5KW.02ZCbr85<7h}# [u} c} 绢L -7E}mt8*un{޶M+5^_>}:sAwbMB^c-K/`/~ȫo^|bn~s//|ꗋų^?0$ K>. o}_.!X+HrxǦ^x+/eկMqe[:zo /<{ gA(?w-_|:TcwWx0>Dl7хxGxU7#$Ƨ"a>r]&B,m.96͖Cנ'5m+%|˚Ӫ7@GWRXy%&WFztDHj,Y5Rpųq}G0N,ĭrlE4T~ysf'ȳo|`S62%i%fE}`5߳5k`|qW9K*V+ i&aXh-z -OYj([ZbpK^5= #i&6<, !ҧ]זdݩiy׷uPϬi=#LRw9voJ FLY`50i_( :vE_uZR5Ұ4C%@b VQIA><., MÎw1ᙁP`y,3ЎF>M! `JjP& )y}x}r{wNנ&6!(j@mbctTM`bыؙ'v`S yת9ZS*UÚ:Ǧ&z5GH3bs"0ODf@-2&E$@Sx&h;rK ΀Y|0mWy"Qϥ\A\_sJGh&@oo_Dڐ\NGf\xI\  *eֆ&qoEEcDIZul۬S؝|ܨLϞQԴϟgO)El#0ր@$œI):&>{C'Qx3<O w-C&Nǧ(U7SfS#)P`@vpǭp%UQ~fj{+t¬j9M^ Lo hZKX>cWO$:\=b؊;z]Q,` n*٧R,9pH͛^Pm*Ms]0FntE؜d| 3]Abq@~SH xNQe>BL(URWw=8˜ni W|0׼f[L3lq1n[&(?3#9b3A]؀6TxXtF<JڙxQy w9zcqmx&`3M>0`&F,/ oK2rLNd`# A挙Uh:;e]veQрS i$4wfZ{ A hyW;Vdf9BdЊyOvQ=>q&Ɲ1) z"X4E#.{krZi0/9.sd̕1+NZL6tmG^Xӻ0b/nB!}P8ŗU86p@0Aw!ȱgڱY{/Nv}'QE)1p? D8e Y4h61>saNbɣF&:lhKJy'^SmfTs2vJpGD:[ \¢lX5-/X6 LThUJ;0)Kڂ:T~'H)q%-A6 XVbYAF8g;TבL4VUgzm ZJݚ/Oеojث{cό\uQY:9ӎmk(ີ W x.P i zth3e0Gx'Gcpt^vYOY0iF' A\3 uLt+B;%5q}s݄,>V\.5T D, =~'#+]V@;omYcpI*`7Қ ;a S(I2 /2[4.t}cP2&hͪ5(R)("(OFe1@banF =r:x.U='3qǃ)ש-b]@>+:$d{_P9eU+We;@ҋBŰe%vG?XFq$ TD!-*_[RG ީ4#jy{ dhhw\t@&X !74N1"HAh]Q7?mZLG]mDg8W}5F;r®ʥݝ)ەӝ?d f\i :<,صJAhnĤe= Kh.[6Zp/.BЁߊ3O\w[a̙"LJwF ԆW;8V;f4K dLtb9R^ V`%$=nD"<ǃCRK\ZXn9 4pD~VAOm@TԪsPT&LXlvì}-_^KZ.vl=NF󓃛Iq?{NMlNհ ef:*ѥ:uAc7|o`®-N`9Sc|D+('L|&ghXS_6WՑYfMp,[цń;`nr1Ѩ.0K;wJK:@~g:iO\?j Z>"UsE=7Kd=bӃf=`˦⫙~iF=n}8s>NĒ+ď.Ws?-H8qԥzgk";Bb+fYT*~S֏oėH領YvSX;::ntԆh+yS[W .ϾOg@d;w&XbGb<췽k;HN6X'W 0 ;_`3zo6٭4e+%E%٬bd&8eJ)Dg@4Q47r+D]M 1㿳瀽a.I,wѝj] -$bQjBRjDoSA{_'ERZJTd` *(; f1|ImlŸ.ugZa)lgg|N li+2,fL1aGm9diP?FgKfo\4bQZS5qRoAt Δ3mu΁裔!Aȁ5#B)d~) ӻv[Ւ/j)nR *j&Yc7WJe(:i3{~:;`v IBy,'o}c?T]R58+HH"8pǪޒZ 1(Ӈ'Fctpq̜yN )G2B7%QY&BBFME 3{I&: &`7hJy~f rt3orcm Uƽ̯ #s #e@S xNL~hT~צM}r :뗾)LON{ǵj컖4J)ԡ޺"\0 uNކjXw2DE9\ߖUo%%Lm$ʀqX`xL򷦮9seB7:~#LNL⡰{|}jsOힿgph`+q/>rgQ7aedUZ̒g#}+PQ/`ť6V5&QB!`<:_np9}ra$m>ff֘/2Z Sa}Qs9#>Аq]L.[0v̘bbIXf’a]nHhtHc{KWM97'C>Ϙm5iT}XC1428!_J^ ?^*qAl6Ҫo, SU.k;̄~ꔴ6diS88"8KK'?j8α뱲9Js{NxYϧL/HO N%LUjn(F(y+wGf*$3@KNǧ)z [jB_:SspƏZ:,f<=h&g&_@u*90;ϮFAzI;(dnnvA(GwX&c5cAj'W1SHL/X%ݷ7"<dFb`y,!Q/\*uqשYEL1eHS޹?6\M'Ľi?`gb !1V 6<)**Ue$.XbpO[;nU$b; =an'π҂ )-^*Fa<_2q" BkpPy Н Ao{Mny{MfI$d~\\2.D6{,wPL*Wmz= kO@%idgoAco颷ٷb6p1Eϰy${c_Λ?.0BE0Hs7*}+I(/ !zz{lX;Bg[bt')ɓ*^0$=Wf7U46GɏsC!GEw )U.v$(An XI_}ӕޣS%5 eNZ`e7"6KVpO~q<!oB]o`*~^-c?LUqj\5SUP.؈sU0Gwtꃪ3 ]~YLDb5"Khf {̃r;S<Pґ]^n*L@¢C5V,8:wR3u LRڶ ד:= S8{"l)f:^ouX~*-JsR~YڰLS'&H 9ݹxlјVtË9Z*mXO)+toX1q~ܺhuZ;0vߵMpmirE[rh~}-DΪbX3PK~VSFf -setuptools/_distutils/command/install_data.pyV͎0#V+@"ut{YU DQ!q 6nӧH[KIo8.u]t] UNTծND{TXr dKΡzTǨz%U җr*P :(z uܷ ؟ደN * e!(umdAwߺ8'3Z@vp(=SI9qg\kYn&kڪ5CYqE $6Nl8K ,E[.]dcp*棳H;i>rD(Xy46qRW4x]m 5 uuT=.X}Jy߱Х!v:uC;; O6j7u;/x2o">Qyϣ{Tw΢۵mₜ-ƫK'.M]9 71a"FLf}r.(Nu_+$AWL(Y%T`%<_433nJJtevL{!鬘WL mYJ1CHFc"^Imty<9#bxŨOnJm. oAEWSH, E) 81gu u( 3t}Ɩ I^z#mKa!M#?"i}Ѝm[i4%n6MkE<S &5$fHd(y_woT2HJκǕKIR;ۂU`z;zYiWr{m2Ii /J ni@VZP~@.hkfoJ;yB>򳊩/uLџ^ !Ȏi,b5 uв_1<6Ĝ~;kpsC3Fz4SNL\4}59 Jn #f;z֋7YC _T~1)\j'ؒ=.k,g }999ba'@ ;*v1IbzpP[WkWoH]T|`z9r-_1HG  ;l=*=>}0sQ7tf Y9jtRjCQ|u.Ö q\rs|9f~ V}-XZts 1!WwǴ&|j#,cu(CT*Y1ӃOg&>טyG'*U4UzmCs؝yG50":}m|١H*vPqcciGck*@`^!Ci3)yA2C{&kꓑ&H?=F${B@ݻbvh;q* D=hoF%!?1\`*2p)]LjYOE}BȳTiv=>ả RбSfa-D9>" :wǎ/ȟg<\~!F`7/sC Nȝ$(f3uHii@)hk 8Hgu nJ \LPeuaBi⑂d[lK\[yz& oxP OUЈÏ9($~p;[Hh'~lHqL/`_qK<(|ކR^P(0'7B_';v('5.`ƸR޸&V 5a@ )W[%pV%2ɺSa۾Ƃ6j+v1Q}=*$~e9Ը5H>DU WKPʏ(TLkG##+}AMuu7ǝ`=t mLlypL7LUch{ZбǢMd*2]G<-3ZSغ!tZN~SǁPfʦ,_R*{fs7@CaڼG.A.lƾ6Ujm;EgNdCtVyb\,}^xࣲ%ẃ_s;Xe9Pu([ ~vj>*lh*&Yx%2%{ΌFٸn;Ȟ*5[;akNW.B*횈02pZf5<)v'}AgYw$+" 8O5z߽-sXMFp>;m {sbU4}!^.y$2i#|b e0;Sj֛Weivǣu}#<"4» 퓝`+:TF̰H1 W37P<"7xi(`(m&Nl$Rәd-iYU N ەBScvry|f/?zsB09px'b&#mĈv3iAfT4󘯺̹ G_Co8]u_x/,͍ƧXgyk6PK~V 80setuptools/_distutils/command/install_scripts.pyUMo0 W.:ۀR (a@w+ ñD-$~(Yrbg:H>=>RLǵf0իJHmʶ-tDot;F!<H xRBnOG# oq]4Jl5lF޵($Ht=)FQ+ftJhi\s43??>jKaFqx8 N` w@sㄉQ{1lQB5>C M-:!2` a376 %3!<Zds.UnKtJ8-% Xe.(4E_E(0puHMuzEXsN0{ )!*6`v,ZFsnYFŘ7O!NJ)li$k+4ZώӋjjqa?wgI+5ȥ<IfgO\ h0STn_Q,BL/&;Ez%ˎ[e IOZ9+xDX ܴNKdρÞT[g@ra}e&EW;amnlo8~ 4[ x`K4vڗQ@ \8wfj5 ^Cv ~-e?^Zv]';%ECcsz85OHޢ/Ay◶ J<=ϑB!5PK~V>qp+setuptools/_distutils/command/py37compat.pyuR]O0}߯b6L@My4f)Ʈ]",EҦ=s-M3!5\9`qH3=@Fr脷xIF J5e 6b| UMPpK$fCg.<4#RGVХS(.n%ֱ=_Hz4G/`2Ƴm4t+OfIג[Bk#~wC"dZPW~ahC~N[7>\v<IQ ǓqW1 {iWz`('H.UƜ@Zi{}Xz`YwpC_e.xaCg=p$7޻"0S wPK~V ).)setuptools/_distutils/command/register.pyks6~"Ef,ǹܹ鴉ݸMj^xT$6d5~ JI;~E`/,$-J2*ʓPq1\_*YeXjmjI2 p8 X,xT<?8M/#AZb ^Ti~eb Z,Kg:% e5b6aY|ȩd\M\o/oF虎!d$G.]W;x."ЏB GbQTXCh($E,']/ٮ8Vi!k%d# s ~:rWvu~Fn*JLqnָloϮ^?h{6K0FF k,-+@\<;$yЫouC7XE)FE ـTdTsP Km9ɨP&(N@I<g [V-+P؀kwm2lM17#VK[/4)- #HzCFfKRo{R86(  f 74u CG.uE( vS"ڝv0:W⍽}S^FvEdgj+9 NCv%͕ ̸bVLH(!wv5m^lJE;$[+ZeRָ hz=7ƂEvV( ҠOC+J#pl|wbٛSxŕ=owksDK7iT\}᳆X޵ E+ ySO1@بSib V2æaRS/qztځR'iPvR(jlMaTr̂)7{յb!:{\]C-JL͌ow0J im}0"#>p 3SWYViC1=>~'/قS%PlJ/ȿtO D?9>vsP2gD8Nc+݅ 묀tK 0( 3zJ|Կ1 ǻѼ–:z 6a >`~$d?`*yD+eg֋J<_f4@.خ_p?ӯ(3#mA8xh^?_|&##vMwE5ܧ4I7UTkyb h[O\=#=D {^rDbExò#:\Utxp|oɥ]޿A=j?GpoJuUzɁi|^vlzd7T:,d g {]yeL5v`9JQ:1E{;"WoE ނ<}mSBPmmKh_PIdkTx̣ M^KP6v/:ʔǫ^ӻU"J* rU`ߘ_uQ(hp&Q|dwY]BN% ,&&AHlkuzQ4kK1Oe @qA$|#^B5ChYC*&كmBax&ujTZ /'U4*s Ů6 y,"_-,Ej^gQd1ڼ<0E6VR Զ[xw>fǧ^E>-^1.V̩՘\ݤ 6$+X!1Mkܹ>=H=ؼZ&'#CklD]e -GMND#pUѺqe#&ߍOȅQYȹü|qsKDQNʂYr] zGó?]O_>~tǟ_<9'ÓGJGG a:OL@k}d3K$-—aWFe_0j6+L+r>8`K+w)CifӰok ]4v{=w v j` }nj y=MK=8gҊ#0C,A{ o^܃rx4R7bG5j J  9$;Y '27`h"̓K%~ p??HKq4_S%Y'AQ;fه4HTQLOo~ 顆Sx@M ^|.n#V;"%Nc_&뉳:y]MYY4 ?&:_-nLƩsHH_e4}w6;>:iD/,,kln'拇2\eꗛxdY{nDSjNN'+x*@'/Hs N=~R n?eNPK~V5;˪ K&setuptools/_distutils/command/sdist.py<]6+rQtysJs9W]bKl"!hn|IxoXޕ4uA}5GxkDZBk$i5 |qP?Λw}fAk:C9-rGgCϯ cМ]^DW ~ue ;*6B[}V \0% դ5 = ڵ j*+iY=0BlPP47JK*؆}9?~Xӓmy]DQloj0f55}TOZ٤$#kw^%-B<XvU1^`ǛW3R6Tpw< .i,ۂ kْČy <Mϊ"=8Ath)L ۵7Wfһ*;,R)5YK~bee:]pDӒ$L^)&YïΎYd;  v U7o?|LK-z*!="]"2']I$( NV䢂sCMt2Aw*[P%t6oZ7C\v oX1i7' Oo+!ozB41pپ_ɇ|-{vBާhf"i*ճzN\Ģ[6= 7/YR  ;mec(7 a/׾FBsD^(Tۛt+ۗ~Q(? S?ַIJ.2ʯ\3ˏBk8:wzuɔ䡖Z8sn6DD]B)f)fMXxP]^h#T(0;LZ[jsp49ȸ)rc469=IfYoWE=i1' ׃ "#Oˈ, $"Ih#@vClBY g 05Ag̈`auJR퓎;_uo.X1 Uaнx;^ DLkhf_ڄj'PD>ZNc ڥ}rR. {1&Fe?}<ۏMu?"BDMx"|Yء]d`ؤ^# 8Hf85+JĶݗC53ۼ#ޝm]P\%H7K0[xXSa46a+Cg 3z oQ2=,V*QS΍"llbƵNL*8cnd4,G)l AR a 00AcZ SS(^ \fz@] F+$Hn01ږwjD:x¦Ya?u-l+j]6YN]zWN57&$t%;NR -UEet߳fK$SDR(+Pt_!Ը\.Á &A j^q,'#{nJJҐME<9dͯ6fXB a 9^ȁHw$]0rӃz"du).'8a:0"+^D` !pӡlh ,Qqj:tWj]֭b!6?xw0n20H%q&@TU "R{ @λ/nYc2|1">&TU0޽[m ḮD㪅*#M؀ ywsϩY^g }tf éZ2;ثiNwK͉wjns-Ss͓[뮰.w= |#|3,B>cQHTG$=qqGtU#]Sj$ ?*[% UqoFx;zUch%'|j5[(w |; 8x3nDbk'M6ڎߗ `\6}q$xm:ا8?W"k}!ٗ=~ڱgޅPun*[7]{]Eq{HmH laᩬ{g-]ϟC34 `<.+[vug<+W'87'gew5_*RB`!10/Utb"#ͷH: *hIWvl~Og#73`lϜ.˴%zBn](R %,0u@ڣ:^ukQzeL#S#7Ԧ-UlYېcϠn=[c_#Ǯ;n^$7jɦvyP;fDu박 u; ċ!_j\̨҇ڼk _Vo@Gu$t}. Ȃ@LZ0K8ER?OQFj)8֌%PvlY.35%Ie5ǜ3,OҀ;*AB<FۀݰaFJͧ)NyCߑ雊i[L膿f.tC_kRr^W`98L]4<"m𗟖;AnEWaz{O^Bm?? n.GB!}'nw+o\NUHvo,EXR@Tc vge -(;BVMɤP ueyVԗ>rpH#":1Fŕd1r0o"ҍ׭{ #PeV5ŗv൴('R3뭒{LcxKTzFɒWW>}uȏ[S #;{Av /@7eA"C١k[4wxթqDGγi%燈P^2rd6e0FAmsi.Wx✓X˙ngVPY\_|pO.M0=ټj\˰)+~Ĩ /u)p`@U$sd;~ޮCPK~Vz C'setuptools/_distutils/command/upload.pyr6]_CjK1c':zN&vwҌ$! dЎ9AD3q \FB&Y2Oh>omY$QFX@&mHdHM/t͈ /suL7ۺT~rS.jzՖTǤ= R/Ǭ̪F@ IeaY+w77BTCAk$ =V* VtT/wW_YYӻS @ÒR6UrɦFI>9G'cU/DS@(U'iISL*~¥l_,ߟ.__|9x|s,ȷ OO9_ł9Y3EQX1Xl11@3r/jTJb/;k9)XІiK*vn".D'Ų(p(6¶9[^ria,XVďO!WZ%2) wW|ϑTwKd TppvX3Z@vz':FzVm?tWnezzKBDflf[ Qo-#[pN U劯AQEe"O_XCO݂;Nvk`u%A7bA*~4(#J@64 )jn9bIw^5`0AVsj]w\6;{Tєtq绕lXT1H[L%V =b:A55"KcO4Acezw˄X˧cDq+1. ҈i:*霼q]IMu{%ҭ NKlö4&%SE8W*VF΂Tfhm^0)ƾBRBpJyRӂ!G ONq NBE$ۦlh~-::u5N`N},UqMbF`6a6 kհGb9|:}lf3p*-fX`?8FBLr[BeX7 g(ҺRE CSƽ-(6(MD+2]-ױ;XtdmiGC@W ʞ;I7bJxvCO<@:H^{hsI,MCբRUV6蠝V4 IZt[C֮>vDLTm5i]-PPo`]/f_ sEs>6Ϩ`DpL# n-[0˻PFp@.b9aS-L$*a-Ml^z'\1_xR_nEeQI4sC-Ɇ}5?ұ-ǁ~iIBP"6)/RƳFm Uv#J8%~O0/ 6sSB%Y=`- F6GʨCv6!u:"^ 2b@{g1MCW[Ν{@n EhU= O8Y ׀uc}gM罘VLt S JمMV_ !>tԺ )?%/x=stڬFz<\7閛o>H0']k{ٌ/c_.LŦboiweY-g_RKuto[Gvwsh(XXlv:FX(5)N5(!l+@g2 he@{T+1LP|ęgc#r,^ X\1^U\֠_%X2qqjܡqh%E d PuAA"S5:exY! ؐ^[-L^u D-p1;1i,8 D,/47Vjù.`2HQL+!hq)` zLڑ_>t.BYZఫ:q+Yljy`?1۫|7 P.s6/-Msd sUUDRӦ8Jy>jgm~Fȱ{86\Y_~4by缑IkN0x40^y.bD]qt㿜3>rP5H!gLT%# )Bm^ajA |wbE4Т.=7N98%Mh$<%q-*|(3 h"C^C3(5GbWJ|vEͶ2MɖFQ\D̑Cڞ=/ɧg2;mlA%]\PF*|DŽ]%3]?VVvZ@d,Pʢn &;^܋10|D {t`ԺF v[GV8lɗ#xU^!1jv~\fN莋n0#R&ml{/sD%󐤍)Y*Dq.ΙqYWO{R#b-ʁ޵n"vcΤW<޲gCi=G RcQq>A=]ڒn+zJ!}v0Vz%ڼn`_}B%(| q^A7=\sot?a4 'W3NKyyU9t% w}]h/tAB՞uTJ vad+yQ~JS/7ϙrOXwwAPQ@[@\]5{68(+*~D ]ꀵf S3{O?5#6i"&-z¾fkfM-{ A1\/-QowV(s-a"W 6^:m50ᴷT kH7ru ! sȦ;Cdl ol (Z Fmr(t s[HY9%؊%bA>s_dT*I* -"ZƎ$Z]te76p1V #)ZJ\\>+֫˙b ,.}AԾ سA5B#S7s \SOT^PDonׂNs}R&c>&!h[zˠ#7 {k+4F+ۼY,-6UAoJltb҆RKPgNv#\@4=ZM}VPQQT_ko'uje*e]]Z ^s[x"90{}~ j8;W^%}'T )Έ6Ԡt Vb|5#PW8)Ρci}M/W{q| S)]PJm uQuHe) D0Gzhj|"&r v=g9&{&>B!$i=5>iǀG Nw59gpYЧuf%Lؗ.w!eEͽ'c,+a. 5@{,AHA`C{h O4~BfeEU,twK4Gqض"@6t#B[bC]bIDȡ/`\XY@9PudQI_zN'*[LP6oQOi3q9"1P+; OKO9PK~VaLBmT'setuptools/_vendor/typing_extensions.py}ksFw tU$4c9Fڊ8YW+ٻө(%I@\_?3$q+H`g']t/|HUgeHWIWySڬbo z6"zu>Fi?xJD^2*ד*MT$Wy6eZF_R)Ft4vW9t1.Ⲍ.e.6uZ%QJ2},6=:cGQl}|8<>FO{{.yU?V,V Ɔ<ƪҸ۲\F SEקޙ-47Z>FYMI. e2};`tr"^&R A?hBzb Rђuގ )fdqlϒʅ.,3;-Ծ*AߦqJ.́u/ҌN.'2~2xhxHR>z^2_Ert'T`4Ő?M wKѼi{YƦ7H.^3ܳ˔qt1Fߦeu<;?7"4S~AhW3͎ D ot~ m ܱƾi ]P5`_k1 QPب+DkPP7dS Do9iL+(P *PhymI!n{'vࠀ{'mن` F&~ I)^UPHiB}gY2 _\ReZUr7e R?6HR0ED++ά\'h[?Ԋ<+$P~Kg[Q vecc,ҋEXa4FaMpidM,eϛ e!%C3]9E^ S"\bt{MU@=e᎜1g'R2i%JV[RPLD"y ^ Ef 0ll+b*ճ9~82ɓ']jDC\y{NkL Ef,\gsՋl07U\V!'lmFwC ( MdYQ*ЭG@Y~wYzlw'jN^ig%L"4zcf0 ٥a~{593` jր?< )+I1<`U 1% [DãMerG߄jԮ a"]BOW o],I@`O,W+ CѰ9j,հ3HW 9 Ǜr @S_kY/4;-B m ~jhI~ZR/zQ(XTjvR=.^"F ^2 2-q̽5OBH*J2W1kِF;wer"mnϒiN4\FtUi/5pP!T0HLpIӰEi zMfm/ s9x2m#4Fެ'}l2@o ;l]{m {$>ܦ_u]:w`dY°h r\*ڰv;v)]<9L Niޕʹ%@l*\@f $@Ts .=6^#OPKGl8n= ՞SB'a Z;+A@ڪY,ta[Jj2l!QMdP xl'fתGu=S6Dlg7[P"Kc7l;өNTv;2tcۑ~DMѤvUuP75@_r.u;4֨x%kHeEVLhoX:އTtrIF0UI?%=n4Em;4(2znPQJ-He!\|]S2][NW" =<|Y4OLa~1  {{j~ad[V{m:;Oj-uwe][氩eAlT9pN >m-i9=9cvfDǵV=}FPEE^\ࢠ6黰%aB to'#;< 6TZ#xvS.lޅm2úZ[ A˳xNуUJFܮ݌JOiZqz]~JR7# H&m]0EAWQyl$Ra1~pÛ't OWyC_6U,DPDrl5?" 骝Ns8m!DVT'm4GkAQӯ}Uo nĠA5ԍ4 w'/DU'R[H,rR0č{m6P,r{" 0Cؓמ!: o57mT!XhOt.jжcijcri{J=SOZe * 'lwKpRO_/O^| ۝NjԣOP(U aՠ+./u7h 9D%CB3??*ݜHN7P*exG Z0ӣLJgj;{@U[7(qqBYg} -@޶T) ;|_u4ǣɦ#~)U9B G@EU=Sf~xƠӎ]%"`ii擪o8V 7CZ"Q]^[^_b~FloNP0ҋhx JR.O CMK|3Y&b4RqQq *:gIӹ8Io2cn޿4,Ei?gKUgFFrChVZc,01Ւ$tD]"sJ$lvD Gѿ9YKJHvZ+zEH d]%%k9 ѕVTPN,"ЄO2ҫ/nUW8-FaX(t|ˬDY‚( ,(~jޖ=bvnQM4[4Y,8f ]:,KHٿ ,oJ! gm_{O8EQй*u[6'8ΫoK'/!.R@=P QL daqՆWfl0TmcB8?q!8RGf 58p(9f$$Oh3X,"D+qoMu?X(BAxf_d_=:5@icc'OX5x N&ؤ.286H^m07P*`Ӊ 4zr։O;ƛb7=bBq_]"3( J%Mæ§v_0 sㄵ*<7[=6' 5{.UU)63t4;z6\5ߩ3YeyJ]pF.:m@ ʭ++ )V:=Ц"!o,Y%Bi 7@Pxp0 5;Mjm5 h ~6:fC]k%KrJ]!ץJ<&BKBD)x0~*R3kc#P3 /{'u@6p!Hoao{L1@;EC%uF9981NH$t^I z &AU4OPzX(rUCMN #jkEx"l|Ef]uف,\/3lZP#9d|XÅ9|/h9 }ɝơ٪GDy7C3&MEJ eRE. poL\{[~:3g D9 iX*RrK]Ќ4Cv-fth\-CJЯp/|XWmrzjy(2K(60_BAmFrI5J200!3FR,WLx۩G@4͖krnN7g DEmi1̣l_b%s(]بU3& ':5SL8'R,gVZێky4׷ \d(APC^g{@hnqCq}6wm T^j|VVqNLSWQ궮} v9} Ӟ*cux}3*9רYX.  RDb!^LvFB̤:. KaNB%< 鶜vz"pSq.8LH3)mc;3&[Qv閺+إ^ 9D`[Ү5YBO m9$;h1]nU/6N8LcFbr`,~3yE7;zYԒRRgR1 Tt&I n3uQS=}Z?B~b|zVI^#TN. c;J-^g޳0_S*`0F9cJF%x.Uz|e5W]ɵ~ 6.ƶ&+hyV[XlbPw@ Ы'4y>.w6ɏ#1^,5Zv5{0~B}hFSQ$Si#b@tuz}aCtfwKb%L^怉;i*'-8GR\ x= }LAn5hUM) F tZ?R~)F'/r 2Ӕ*yd{tFߢa>p RRX(kȀPãcj@ng4xY0l n*/uDhVK] ns V_ζζζg[mwb91jg$\{Fp?PHcCL#kP㝢r&͉_ [% ?@P/6?SpӇ* {w.$,r0naLw ;ڏ;ݴO/?ZBhVƼfSx|/ļv:>:e_ dc);U)DQDCF%B ;L=D]7ۢx *ď>N^[˛`|3U:/Y` uBgrL/8lXJlw -zq01nn ӡ.mVkӲfo(6.u pkqkJՇ=.ɉ:VnfMߵ8m.{)+K\%, X:0s&<,YX>Hrtk_U4,Z} % ~Rjh ZIHUWy⯚!eI"%J/7I6^Ri}D G^t%}^'n&]/{|R S!t}^޺ YfEij:Seyæ%L{W=Et)&["2G+%2.D20I/tL80/mT Ƌ^#|ǟFO>:WY.h[ޯ*VاlۏjH,P5;' 8W.WUze^w@Ulg9k]8yHN=" !ٷXv&+`fit0 ̡ 4q'4E$xM/.Ě̏bTV3>RqWo$ባl5w ;Q)3XCAȹ{{;bc>{X~#V7yR,S?1mkLQ=S11­CzW}Qѳrd"`/p_T,QIwY#YZ%woD_b"]$Sk&BLˇ@?o˔-:ľБ“e2T>m{7ͶRUDe5ueyAڶ6n7E@ *YrO,>P$Yp Dw7_sLc#&޽7*2?uǤsЮUVVy} 43rUOr͛ߣ$^FGouQdGƴ*+y֟#wQz.>'aM3=_JEA$5aYOtS´:cl蔴X&A,2phtZ#oj(*na$Cr xv8NAJ'D4 Ӭ*V.<^FY9Ei~alfUvtp|M**6*+r2t?HL X=+G*k]@8{!M;Lis/Kӆ$yJj{A;2%IE*1-hᷩϧ9Ō1(%bf"ds`gz4>+ :POeK ڇ~q.#&m-%O}a?BCx&xO=t#=h W6)jw  ?'13+$鶚%Q19<-ʪG+3M䑗u OQAmGc!]v>7C̾YaT2"bC;BQE?o$h* qa0i"gxgBf׬ 5@t_ G0n:0v1'WU>%|@xĈb\|vcy~qB]`<d[GvY]\,k`;e#*Bd滜c;Gvh,i!I2Sԑ1>@Pk*mLnc9J;.nOw_%{?-pў2|(D8g?(y2XK8qDUM )P,ҿOI>Q0UћʪݼQP)sIM-ҷ Kzf$*2')O]Imuƥ߬YR,6:sf3-]Q|5ȶlJ9gEF}@%2P&7hp"]X?>K@s.5 ߷5辩na8pc8U^AL~ +ѓv<}ބmK:N':Qx_ Q@f`_lT5D"SNZgi:|WT0po0O9jq zlG 61\|ӱ !\_,X*;HPX l6 rq æf<9-S;GяbӌA,}g Qۅ[[IXiXg&`WX_[!ԆsQeLrvLL0q#I]GeVO¬="aYÚ@6Ly؄1D;I$rРh?3Q-F1%uh+NBH}zRΨGќ^/4 H%XMHq5_B3Ex}~JQew4Q E5`s7qyY[k=\ܶ5fMU~+bhO%{tUYoR ҕXtb博1J ̱ԏ"WwYl~TRY(+p`` M)|I9bgRPocJl!GWogl[tlV6SСIx9` (ϢJ]a7]%bEd<{'HzyDKoɕF\z,Y:jSfJ`LZⰦQRW%Æ0PFD{Ĵkf|Uxe|K{,NP$B#BI/-&!=2>N"Ǐ2d~~x=~L=$KR{dR |Ii`@kwe ,̯K]OP&q1YKV3H"~㈻4r:wuv 8`gtSi*_?t+!mp̊A]0-9QM]֩+d2/^2010XfҧJaA`#>zp);;|NF}Cqd pZOpQoZƊ̵LqrrsGv2+QX rsnkC$)YVJ 1>-tkXUt$23}†{4тPT%=<\ol&)S"e]|TqnQ p_A,iQحhI2yH%tH9&-(]*bw*w)rmlCqK0&Fփf"&_9X T*m]'iuz0ؚV #A*SCBfϨ1U& T#Pbe#>}z桖;mYGxz 2(Ϊj5%}0OT6 2 l/f2ZS7ת#7mN{&3'eιe)lodA̳-KfkHwj\,X)^J=au$w"e8 cIf@k){Ě-invv$1!9t4tHru+ !+̊]|΁B" r(>N 1*|X~mv`<=B`\/io8;Pr<%͕~Kf09ژb;3 ^6:'nOO-ю xyaz(xM藠]Q?*i7V6ա9gQҥ]|Po1KQh-|E1[P1n!u#5Q9dǍ q Q#,cJ"z ido>waK(nȌr^TV{i 7!eF*E(M +|G$8^h55 *#i^8˨#5Gέ1a wQ]mûmx°1σ/ƃ/72p;Ax3|gOi`hcku(&V@p ngo#>ӱ7W,?|{7H5GEGOt'%&z~fh[ֵSgnG; y3t6fgTXv/'jhԑ.}0ક/~m,$2լڜ?'&0tIiD 05H}`i!9W5x|짅6IpSTcV>J" N3\[=C=@OZT2HFGڧj__hgpZ@3bbg'+InNSR6hA+19:9z|x6 ?S`i_r=|Tz-!P7NSBL X3a=/\ g jcqzH NAc3~eJC?M*e\E֝Y+R1R<ٸmO][#|`,NL:vԷ1o &, Gl?B(!kƔs5l)nvy1WtEǍ&~#a~c}[0lKbmj9˧w]ɰΆO 6|nC ے6`a#Oosbomq1U6[KU%SH~~Ay15?p]f{˭mvL|[[w' udiPnr2-;)#?9݂ :Sy\itGI-7}^gʟNC:?nd+͒d%*"Y۪l,q};b(z`+#1jEנV@dHw gPJ.WV̇[WڠLbNF"WEyF*n 1\f~ wv^JH !j?H!^ZiDb#((-t] QZwZS j]6X1Wcq^A q3?0u;F(Ddp^~9vJvf^ˤM@cn.[J'y$bc`C'|A_ta:C<ԩ1ჄgxG\kJ]XBp 8;H%e<x~z32'D:3"Ja{fv:R[u:2+1ޟMtܹ_d]-Hu0@gUשy)7} b. ٚ > P_=6pM|xN/2(S[S ӿud`2u`"8XZpZmL0u57 q Qc:sCsr`I9] Z#w:L)ns tԠIؚda ѿ{TFn;x޻Mv{܅Tnϔ1&/h섹/FbA_0 >J9[+BjQBǢ%yܮu$$-yedHҴqؐkPU{!{"lVW9b8C\,R-?ֶ}iK8sۖ[ +BΠsamo(8Pi˄e;ᾢ gps֫1;f[T30잚{eMrQ jVlY0⏣bEvbzdr5U$X@oӫpzx)_i٩lr;g_f;=W\\gJf;zE$KnJ:Zs _ B[aMH\oҵR1x 8-0Ĭi[ QfxH: XzS `GW&+t2 )??5.HE&ٹH &6 "IncrdE _B.Sit'gcHRC cmR։(}~U+ 1S xgeZkz厴R%g0'*9{mmjGxaа@Yb?2vaf,_7xmz3EOCzt/z70ryы^2't:|Xdۼ'O Dz}AR9wC&vjGv^W3GyvqKֆ7Wglº7Z}Cg1]e~"gj=a]AwJAF?GgeqYx*ebіaḀ-G]ScVf@_ed S]f:IDcp#+k_y +ܚe`qJv_Jht>YӷOeLȯ9s / EB8*Ŕ15(%wI* EvIMϭ$w; PAt.ٓ#YM8!׫HSandH}Cs (J u/R$Ʋc6PzUw921@|:`€ `RC{;_9hV lzY[ݜɃĴ}đ ģ9n8 }Ď`{{~Ean09T>?Vҩ=6qS/4 qԦ]DUAWX E`grn bl}jpװ]DߎƷjmqĭ^0jdjb%A|=DA|= zQEPmc5緆:?Ń| _<Ń|ǔ/lOޘM-ް=JZt  AQvm Q/-@rBnm{lQjzD~I(YvA;h76yn<в^3ٮԵJ>u\O+e%R^mmxҕ̆u$vVAJM*e`ѷ1zWm aU%r Ŝ۾(\DVَZ)4‡{h^B,x/"黺2E>qc*{DML q-  wN݋F>ZTdzѓa{ґcmfcv?3zn H@Je@K*bvIxϒ(BfLDJ"Jy t^컶!O$ro>qkERkZM_GrF6VemrêxU"%t)!'44;Kyŕboںoe"?dwggz:oMڋFzM+)d32(t#?;#^ׂlx1n6D(asD7$Z+|b@C!zq}=Vy|/1'v$-F|B?)&=a|^pͩi?PnKiSLFĀ^q%=rڜ#ζy~؊1e cZ[Hp={3!3\*|FwGẠۘiDJw QC-mG#y 0MZ j]EgʚMA 2B%O= 0% CjJ:X%|gjqJ(**0?c[D3_6'Y$7(*ySZuΚA YU ^dNQKFZ7i@ i% }=ʯPXUU`aj; M:QvwGowwvs󠹼ؠV]+06^ˆ|U_-W wwr xK:[j -H.&Mi@2_ݬsBٝm?̮s/#JSwzlܸ]]"2 |l 8af=}j/Loi_.h3C6oIrT8 }ynF痧9Dxl3:fJɉ1@ő==z"}|3Ld"n  "WR4 5oFmR$1mPxxa>=}1DPu驜\!D4n SgΞLouFiF SKj)7)ԢQԫB{6v& y;۰Gѥx8OGGxp =nYL-ǘj3=ozqy+|EKsg{7nɸv9'̯gv_lĉ u(KXS5v"BP :-_ jzN@_]Թu s;Y8'l5L Ðz ӫEs*sv/[eU3pAAg73XSïW۫͆0y0\ FG /a+1Pb.$>`h EYQg_jp8?rt$q0|-rsLNx\cB,}Û6 ww~у޻G\D' ߧg籿 ./8*w,pTT zY.&Qsn;{y)G}FP[.g(fVș'+?m iV`~4Op6RlZOg|ꁡg:՞{x|7%KtPK~V6g1setuptools/_vendor/importlib_metadata/__init__.py=ksFr+*U%*_.">-%)4 ΒJ=7KJ%,3====amnOPJ}\~lfeY$Qz-EY/m1\UR}mZCө8\w{]tuY_jT6cAt΃ˏ8F*ʦ$ ˼X-QCݣ_5[ VN?e%M1Vr5,ӢL''^nA˺/m eORGW4u*VWbnjkrEu]zz/.ߏ"`BԪ#N_9-VK xQS$*_55,ӏmۉ^2?^|۬Jxm2*qN?' [îQ~.{XE/-[Q9:ϣ?]E>c&W}3].^=7e=gQ<Dz3qsêBx'/iug18<<|{%"9.He H#ZMQ'63tb:j45+G.]؊C=1("h oxlIZӨ'nhU"ZMy t%EǏp+lmg(։—g3I;')=GeB3:{z/E5·;v ȁ8\M' = Ƀ-`.$l' \zuGITIyt_.>ARMa[Cr~T ٹfM x}2[5B9]1uGSѯE7sxiV*jOHxbǪ}'2f>#ey'ڪX H$Ղ^RGQY"qEzCrv.5ҴQɊn1lKC ;[Sʚ_'".fDV&TַU9P$IXᴉ8ir"j-tnC]WI|QJ߈z-s4b`8~jd?hW(RTk#;RnRV2YOy Ҽp!3;\I*~;عi|( e " 9_岄e\} æ$O- ZhS܍_k$_cDu]Bscnk~;ډmAt{tANwXu({՚6v3i5 ?,1T\#`v ( յ^,z %CrG\`j;`ջ Ķԓ)Im VyAZA b <9'Ϝ%v3_iX-R`J&3hd7BD6ѺYQe껫ah{t۬׷bUnJfzq{/r2m֌L)Ҳ\K$g) 8.@vŃED;.[:ֵ|7O+ˤ9y,--6rPP=o~?>:;& B<6ߜV؂{YvsndZ[=\.ǪBᆹܖ r,W%% 4kӼԦ(\bϾ5DGgOf;lC-Z١Ɔ93Fh=FU%"Zr@Eeoٌd}A*54U\+"V ڙ1~ Zbv$7_I$+%*3|溾r4XYV_֦+RfbO,[NV 7cU G'Ql}&R/7 b_D$*RW {ee/`I ʎ" .;4=i4,w"# uȈHI,e!X"+=)8N`L|qۂ'0U ۖdAe!QL|pv_HKps#zaps9Â]Uto K/%xPHClpBB6I#^{,bۻҨ?[GqY!QǪN.mMĬ].:+xـٴ Lo4mv+uq LAbu$de 3C|MR*E21H3d7GQ6VL{* TT- h9C:hϴnWݏpwH~_dPτ 628kHISn9b6f6t`}2Y hFvWE5L,Oib62FQ B/2XzJ3${}B$Qz.c nX ҳ%aɛ >nHFE1&ӘovI|\v M#f6LbĎKطcP[dˏ6m@߁B+P#;6<Ѯ IfعP[/Erc3Z-fbFX6F'=2 L/6iNhp<Q+~_/NP9GV1JƐ!O,"94ɨ1˜GQtoXo|zdd=v\%YƳI&7)QKYNNbZ%Ftvh8r|j'n:k֠Nq5(V}$̀DwQR_6St⻉E07t-MJ&CA8\oA"J,sL z%GMxw8zx, ta%]$US[(e?V 86Av+`JU?aFĴ:9 v.ФX |͡HTދUJlaIUcsȐﻺ1[ 1H7jOJV40 j18CIriJb6>3-f4S)$}[ 6G*cH~>)v R)Q2'W8^X0 =wWvԘ %3:.h6FbJ#.Zۣ.8~vJ‰r*j,܈@)paS ƞRpǼ)  ;eNf}.ni|m~m, C둉ńe"HUi!;:6 ,>E+ Yuq On5x}#F0K㹫.[b)Hë^Tȏ_>HL`bBfm&|/H6U.7Cw &1OD/ luUuFjP1CvTP ՉzZwzcڸŐL7m kB[s1A{Fєq!WmN*$M'`N]Uf & r43 CنkE^~ۑ3=)Bc>/הm? WPhUDTc[P62:;h@Us 6te33CFR#f>}q}kxUV3:F_/>}SJܖ?pZށbAT&Rytچdz0%6$=FOc>m2U\y\{H#eV}),a3.^Jlؙ͊uoUd Z| [ٜgz19tkVDi$vaʵ*~Jo&:;K! "C itRgIeAxVp.BA>в̃=Y|myl#ImXXfYs(M^~H/0j71'i?Q%* FhkI,z/!Yp˛_oB Fq=k[M@x7AM|q@ ? 8}tA9{YeȉFȸY@L$=E酨H`nT_Fˤp-V O) z*d'Ռ="esH`JaZ}x/ dn g$k`~kBƒeW0ە}!d;Fl6tm'4 ESܼ6;8H s$ >0θ.T3<ԙ~5ij..Ģs#Gnc[,Uؓf"xDxJO@yfV"E@ Z=ooI/0ח#(Mj0 w|"'kN,3sib4p#Ɩ]7#Xa% Y\w5[ r C556q@[v{4@T;X/q(s(>cKK~K`zCs6QU ,D%>@mq<{Ǝ/Q$WxPmNoEeM9QI W3B?)z͹/n%9yTɫ%lX$Ι&<ȉD92 .4u{;Ar<=82ӣ&N>ovh`;Rr~x'$9D#o/~812Z%'h]Tt g$`6QxmLJ5\ð;Wޤx)SQhM$7PEdӥӽ*>O]1Q̮)KӢוhЩ1g8"]BquceQ΅S^`B"13A3QgZ햯e[ <|KΧx&jJgRpj.(&sFTb<%9RCh)؊Xbz"3I]E(SpOzЖ +'*c(U>X~e(ˮ 3FOSUԉ);lbe݌X=WuE?vr5GM;[r[zHj \LNYeX9)Pu#_=.î0Z\5ML[T˙)6qIh`),]$/(y<4` x E6gf_QrVs3 7|GrB׵xqO܈k34MݥQ`wP&(0vb^'!D}KF9nƄSc2~<6<-H3uoМg_wfLYc='.,_U>So_`,ٴ1!?-leѣа+%64o0kpK^R z7s0ay.tNR9Z=ck&"(+ J$ LEGMuڛDdF3 6.ǒM؁|lSthX=7 PnikwW8$`T#E(o_pZbn%..o~gh7 Syx4%DT6V;Y᪍b[bMJ P$s'#JއgR' .hV}wˆ*'ȩ쪽/x׷Dl$iU mn'Cp`k8(H7.8]F=y B~tssIwxvgߠ?:U{F^Cx᩼R"si,hWjRcs< ]wsGe}OMaK?[D/⅃'b*Lt* 8[vZ\< X5aOxWyMUD%@X^= |k U8} =E\l^kXѼDVRZQ&]BF)hA813I_ 2<=mޛsSl{I4ZKMc{ʳÜ=bkS_^*;2_"!Udm˚ 4[Df`XW#Q^u%G2cd*4G%|0G>X-q: E4Nyc,Z uNEGHMq Ic)fEGxt¤kϙ)ݳVjYc.d2`:fʑ$e}qVu7A0툓@/wZAUAL]TATl&̍/zLBh01꫎ xѣX?+ =v;6PK!ia,k HSIWlX"Lu vh%̴ּp램Z/!4Ѵ>G^+O][ڔʦDwr&oyd1 X] W3Eg͡)9D/}'v,nj[)NFLOѵJa<5B6GwxBPt7XcRXOޏB xȩbRα";I=U~ Qu>#aIJ&6YCY~hVz$6x9ױ_]&{bamxUS&ȱ>Rs@vGo(9 3L]J*{Bz/<(o^jׄ>՜%  !aǛqQ iܚi^ JՒgU?pBnQbeK^D4{#RGp/NS36X-pe|= Lw$@ȗ>hc+|ҵ-Noå.Vw>f:{V1eءI; hN4z=L&IޏM hNrWWx$f1$JtfYPK~V h 2setuptools/_vendor/importlib_metadata/_adapters.pyUn6}W IY[M"vhdQ!(4vH;$K4%XrWZZN)a#6H.W=VBk hiTYDY촅4Vos͌LDQt R;+FІ+NbnrqI@{Rf܇ >uD@K& Qi ૒Ƞɚ-p %jsX%l`p{m265)WFn+Q̆I&tj!]WɬWSU ǵ?R]_0`W}'}|cG:ō`nLuRgO,^5ߝ3-Ss󊋝@#8eXN^fŁߦQ4O]_-<"pYX][q;h$\_g;%M'c eR$ҝɚ]պ t'  ;rgQ,p̊:?A^OJ4 nޠft$m`ih,ik g uT4Em{I`[t ]~3/?@sg.2xx( &}SEΩ'hf=2[ CA &]f'9hG`gǗw@t?5&K. 0+CH utQO$G{3{CA[::!YG'#M1pMsE_=,x?4$Mx!wqW~Xmb~큌iLx0\$;Sغhr]EXpXJkk:76[|i!_X~u'x8sYMvz7$`EDwiBR#Fo^EStVT}[`߶mPK~V"bSC0setuptools/_vendor/importlib_metadata/_compat.pyuUn0+Rhr @)rhB͚"Yr~}Cz,fq'2=Z%Ҹn2ԵP~Τ0{앺E=;Lcy7L>Kg:z9p |9n: h٢8gJ.mjDj XX܋N!딵HwC4u?c-NvXP M!],6ix3z5hOSV;"}x;*3]9ϜHa,_XK~05b+͑hI"Ŵ2\i5cbS "盗|\on\ι~Epǟ^:Yh`yDJx%$Ls {$ㆱ>NlGYe{lw܁BVXQ?`X!{-۞,i2yvO /&ѴɖFH zia;PVq}~ 'yRQG)?C?ۈП]]^uNv/ʲqvĂE3/AB vQ&PK~V:oikO 3setuptools/_vendor/importlib_metadata/_functools.pyVMo8 W!lv(6s6Sc[^InKRGl1=4(|$%]zLŮkroLf%쬩EYt:] A46WyIX,AV۽Umv4]\\$vA׵ycu P^ndd/{Z9]:s3 wAԵi%ȅ~a_E|ҹzE3]Suj- h2|Q4d{󄯪H {W^,.sO  sCF4`qA-UU]@"iC A}DRě,4\  f˪7H,rajS6 F {TMRJ!RjD@D(OlVg;s_q%h?%MR9g(2Cl ds61~j1hIRbwm v<59jŻDMNH%|/pҳGE~Xh{TLgZKmH?aٹe$r߅ų\*oo rKO6xvt%ۋ39;#|<ˡ̻UލpjNY&PK~V:3setuptools/_vendor/importlib_metadata/_itertools.pyUMo@+FȒ-Ijs"X,1Ndy%ӪiQ[rɜe"7y^lK{1>6e)bcXϿÙtM>Kcp ,hP?'(7Z*֨Ay\tΎ*t-˛[wCX5,nO&QBr}G`j8CaN7X{H{.3niLPqJ+di8Np0,Hj-1O"n $aGjA\:s9L=`3HT%1V(nW+.Dmlֿ"X +ghf]x۰F%uKr(.0ד^,@Fp>k9y9!~ ꐔc.I)x8\8S!ZeWRXTXe8';h6)X|<Է FZI9'4ϔ-қ̉C1<&F&LY+7a5bzKN554/\}cK|9ccpAHeb}ãm- j-ÑcȽFa2m}l{vwZ;'%SL9l 9;9j7}FIg.:~F _5_ӽK+}pp\u:l(6JxNpOVuPK~V.setuptools/_vendor/importlib_metadata/_meta.pySˎ0 +? @K>nKC'ڕEWK)/oMu$pxn(kn;0m^`Yf51*Cg[!SK< >7WgeYVv:)ד^Cϸ$Cl5P\YNf7`qE :j>F5;A##. Op oOp4{"qs17 IIh홒Ö4)(N*F$FG&uh{ sڠ~9B!V</ m#/ÕS`w zj8m"?~4?W4[@ОҖV(6*ق*Kh3-Z}H":XS1<}׾ŃrE(ٸ\*9^mک_UMl:?9z])sOPK~VkAJ4setuptools/_vendor/importlib_metadata/_py39compat.pySKk@W vP\ZC 5M)B Zk6]1U}g%?(j|3i2 l׶B[$my˖wÍzFORPpme1fw/6ou$vsY0)E#%xIGPX*RA1AgPu u4LvD 枩]9iAI܈rѬv86^`'㩹9c4.DLw96xL#8<_=_l!|G"S`p˽,Ή)J&4qҤAY ^J92Yzditf'pHD91h!G4Z91V2!n)zos_XR b).~v?wc냿X215EȀ329P7`l-w: :%tFlZM^PK~Vxv.setuptools/_vendor/importlib_metadata/_text.pyUQO0~8C&m{ak7IӄMvhsvNw?xU+mAhԪ4[62J UhKUd9Kwc*>83Ε( d{ 9Kpȉ[`%{Dw P\X[UU3M0%AILsfϔ|l6Ӑ[T VJ"J:nS.7z/Ωcwf9&J,pG?L-n}]@-0>Umekq(\/^&="g$Tr8cMҞ jԾȫs/N=;I ;##ݽhwF.!˄ͲؠXNJ=4FKpT8ϭmHH&$j9{$ TL+irijH;bawEـ52;}{E-\ w D*Ur&4})_;#s˻=ɰ=!ú4׹&7 [;M6HnmQO#~_ߘ k$M\`LK49io_&}Z4̺͓^PK~Voq2setuptools/_vendor/importlib_resources/__init__.py]A0E=Ť41;4CZ֨RAV_/, kk h,23Fl,nn3G8> 1=fkqԢdbr#YU܁Fwܜ Ce0bT|]+iꅼB){^,yZ`ÒòbٝѦJpwhigj!0RWe}ژOӍ ##C]Q }D/ w.eV@M96EF߷9%xh:uۋ Zy!xP]bJ`+y0Ƣ& @T`eyxjm8"2\cw|@%C+Lb|qSRm7%T@a;]G`ǹzN6>cSyO%Hק9:1I$o(WyO{z9x/Jr0C`\skk3Nb!uE-vi 'Zg?73 {F'΍ŷ'x2Vw~nU>Waڗ>."հO;dVCW(!b!z_7[v(Ѹ ,~x&x&0Y<5P7<#7bv Ò71H8^6j=7n4ʕ_8aܸK7_wK0^2=af%c!O뤅D=zCkN"|nH/qMt{T\PK~VþQ1setuptools/_vendor/importlib_resources/_common.pyXM۶W7 K6Am"tiylWR{fHrwwwZWQzmN +2\*l%@Jo =S=Su^=[u IgOC{l+,syT *;Z)G5(kh5;3ZrGJ"A֛t"60Gu&Gjo ^-bh7Y}كBY\tY1}n` QwBzؾhNoD>N0/!{mF(@L%lo.!n5SZOsއ2m5=Q}tujح;TO"/SbjQK/g$^{#m(W߽~N2tvpI7ѹb+YȗѴO<#+J<0(5Sxb@ j˒ Z᧾⾩{0nJ#P\Ձ@ 9h/ 1evxY?Skv~ൂ#b~<6m`w[K t=QWSI@ d i&}&F( eLSeVGo$9keu!HRԈaua1"zX&^Tik<:[u0&"y![ 6@Vx#rhC P(rٷHQ(Mּ̔ؕnt?,˒\ˏ^׏aT`1+ ?كCJLt%6eX+XW(N5S [ƽˠ> 63O؉U V'UO[Qۓ0!n#JB`zWS1>LYTTp"&,~]&Cvtڄ{VQq 1"r\`_ǣnp\tFv;~\ń󟞯կD-0|I(E1ҚÛKwMԁ2ҵ,Lez+Y-b蟁 q HVfb64-D,,MmG@ȩj(L_Um Mt@kg=H}: %È+כy|p35,}&CaڒH= [n6Xwlfg = s-pɘh1b$:RM@kRn.1>Cw_bdlʭqq=Xi+0ʴّVxQ)@;x̤ Ϝx@t h<F 3h!=K5 t4:zbw b+ܩPJI2Il4P?&(Qff^;$#jL_j᠟Vo1*!al :ӢԤ -kTpiP|UoHr-EאfǣoK0*Yߊ+SCvCdЂ 5ǀ\x oR\Y xXD[xFx(:9U)6ǟu>X(i)Y7# (_Z*ywRZK)f:%JX\KiQ+H Vu05iĸ 4QP.]rjJWJ!bVT^FWl$ {$kޒzm*?PK~Va.;m 1setuptools/_vendor/importlib_resources/_compat.pyVo6~_q`!/M PFaC &LI%qIQd%M4ɻݏ9Tg882-M+{5w;)YeJ>8fSX Mg_*#KWX(427QWBb_iuX%3{Ҍ|\,sؓB7rErׄC?fmRڋh?1 pvYpNivz"yƔh?jVl6 WNd84F߉ N*6M-%B wd1`_-nH8p#*m`ut; ȍh 2P./h"#:,Tnޫ R WEY-Xd~{rF㎝*ACO}NFl:ޢ+L ޗin?-frS7tR"n~a jϋ(|R İzٚ|e\t>F۬zH,,R!,BzM>lO>@PqqɅ<,/ ?m'lr!}"իd.h}ჰ?ʟBv4񉛍7h<{ '7?^*C%}⡉~H(U.['m,>T;Դ9T\5/3ZA(̩~PST3{cɮMSguržoSNj3MI7"P4 ְYyɥױP1i[MC%qnt4V&^Xq ф ШYswCƊ(Oxzz|ۆƙU,/a⿠i6b~7/!u;A҈څ"!?=nh64;PK~Vht4setuptools/_vendor/importlib_resources/_itertools.pyuRMk0 W [K)maؙvOvYW_bIOɩAz@֍*(U 吱*StB)S:D.zm4Z.zG]Vm\TI@2E8 +3XI-c|=AU1֔bz9:DݩgŠ|WMqӁ|Ґep8 py瑀 kXthOaƖhFPC 4b( ]\Oftb߄a˛M԰-Se^"&,;i6qQ1I:- zeS/4s7 ,/̔V*!A)axɁ 'sMl[# PK~V.ev 1setuptools/_vendor/importlib_resources/_legacy.pyVM6WL݃,=\$mzC ݴE`hͮD $^' Iɟ[xqdiUnl" =>um}N%ՃMŏJjíC#6 V[~J9k}T1XYmJ|s } [] s֙O7%ITXCR8R4[$@WcΈΆ5ć]&psx?C?kԓ/lXhq QGPmT֡Xa\gy131M[:XRY7кyo)bV>5 =@\ D$K,&71wHt:RO,:dAvk)mZϸb6`d0J2L AxfloRHt>Vl 0q=V.,ciT"tM-9;YVtj6eřnLir1 U~lB@z-_942,<U +qrڻz4.2R:Z~Y9^af .< ?IoXՎhV!$Jc4no/ HcSs+?[,I-դ-f<Պ1Nn'IsnqqtW)=tEz/ 6uFZXGc;PDӌIxpa)kYҞ}PJ;-@A[Y!̏󅿖v5K54(>BǶ_n|k`@}DN1^MD_車qdӕG|8#RcLbR9n(#r'3%`'P8C7 Z.N6PVza~v\->ʊGt3z~:ImAggόD+Zr{0PiMhn[%< S'}ɶ }_;IFJIL}ERoc6wFOo;ӁLJ+:b撁+3&T@տˆoXh C,[??TC+4Sط9鳰أ0Ύu`M&4o kS"O7%k<,yD14%zNex"kcfXۺ(:#(;rCYB-Or9z7 ?3\9,RgY_uQUQn,hY0r|M\+– 7Zm~.ɜF"ʡ%͋ᮗ8bMG+* RxaZD#M aU&Ĺ.gJ ChQUx7ܺv6T3ĉ7ƙof,X*T^6rm ꗲ!*\) ހ@I^o ĞNm 9RRK_܁jvLRӇ J{sUP| rfF^O,n7cl2oXg1h"D8\ i,/- 1Re8k}ʸ|v=_B$']N0b. VdZS*M_?-Zo ³"Wu# BD<{.۱,gGt!luLKftSvC{) 0γ`ZcptAW_^A }~פҭ^ c% ֠͛eGD-Q>=L !Q'm'pn=PңS1!#5FhLsaGiDλ9ːW^v P'a_#Ǝt`%8f2hR*h)-AϤFAj E(1 WK,v םEYX+RBuX4j1q9B3=G=x:;wg/Օ 2ŧpƸƃ6U10(GN{!5@}TLOd_o> SZ\'/_J2vl[{@Cc$VE1%< c,ZV="SqWBxOF:#duTi o$hx5[cz8Ae~+6oia򜗽%,\Uk aF9_t()3{b+#5jYP帘80Vb-]tG? p|8flh Aq+]dGm?ƈvY&tVN2эӑn!Rltm/f=:xpy6Wq׼Aeb6Ó,RȋjK]~i~BuWaPK~V\ 0setuptools/_vendor/importlib_resources/simple.pyVMo0 Wv5@݊P+CQ-'Zmɓt$[ӏ\"#(sX, rqH!$)˪ZI!$XuZHM ͺ%~ZRǚi_^3[L$0(2|oS+tmXX]ΔܲJsF4lKQݤQ%QﲮnE[/u˼=& N֓63ܷ[l^4Hol8m\=S[TۙL<ȆL L[(a(u ݚ9y)N5F%rz`l*d;S^"a ӐpMa=ӛjl){z1נfJUԩjqNDFV.PF# 憈 jv )(Ԧ#̏ ʂTpX%;P7[[[rlAjdkaI{EJR{S˅_YMeڶ"FlEFU~U#fx.kfKb]7t)Ur~_z̶wlX¬J_N eSW0 /UioWmȎߜށzE䚕6jfb)%g q؍ٗjZ=J{U*mDT4ߐJw? ø)^&MΪ oւ 7QgnDC U-x_ѿ2QĦlD]{+ '`R sYV@\У"W -=apע[. %WIΒdr48OtOn]oD]T/,u+$nKс[˳nح {ې@ZsmaS (J ]nH&׸xmwBBK4s>lh=@C4mmwxpmBXL[nҧOi)V6dgv7v_$ B+/#MV]"KV6ijt,-ސR6+ eu&R>E 5<>]j߯qM'$Dt֥pcv3@KAT; 4dcLٳU>[+ucl}>TX/ -O  [|X9,]IUlAEgevt<$]4[V&],d~L<|oO ː@L(7[fzTA`LJضwRQBL? mCM`t9\K -k%p6N,sr#NY~,AoNM/ԔFq'$wJ'EAKۭεS҈9 Y \ܛv8[6z|wtbɡo:ߩzD@7nǓBڸA矽h-Qyeed_ŷRZ‥vj)uJ ]# w H\u5b/;Eш%W&*dlJcPD/{d8q[&f^%V$QzR5}]Չ&]2k8aNVblc6'79dVCu/ _H &g[V}d1D@(8O#M0+Ant#D;Y'qP\xM{h犂&8zp5R]Pʛt)nKX_s@^9' ?ojՃb-H!8{ 1-Ex/kcl#Άb47@& " :M!n[/0;7q gc U `| `aAD<բ^Qږi84ϢA(hO fq?VJ9Љޮ]4xI xșOPY#za \+O?2?n.#eFͱ-a'HGX6|~Lڔ)@&8>)Bq6|՛MgI]G"+?xa3GT35 jn ΢f@/? :\}( +2l 8 _wure'6AIR`Yps9|ف:x^Y1\J˜vW7#:1x=YJBǙ|vzyMoą7`>_% (O|s< !z߰hkyk{˽ Quˠ_'  sWIrp"afpgݪLx޻ T[\-zk ooA`l#c=h)YIƘM]/Ń|w`QV0+Gsc8F֟=}[&Z~6Ȑ/t0@fbhk&#дB6HθPΝ_X%7;QX5O7ZNQ Ej^4aPK~V )/:&setuptools/_vendor/jaraco/functools.py;]s6(A ;_)5<$<\4DBc-%~({RW7X"4ݍVMV]u]ړ_ƸEe&kcV%<uo(Z `=*{^Xv[27;]7uc~ɪ7)YE^&Q7?usrrݨ{Z]/ܛ|z+ܬ8ߚ ' Tګ,M*fH P[+,`[} -[쐼&Cض6rMF^Wx2 ƫ:+rӢq&Pn҄*e5pbm({k*Eп_(Sʃ!:Y IH J6^[dVve{$7eIedhN?9ٲޣB5- _;мiK;[fւ pƹB"%4*2=hD."gDL5)XhK霡)'f%hUY, _yFaaFWmY! ]W9%=v{]VfVNkLVl}wӇ3}1{1;8sxƇXTQj$th"&ٶXfq ps}TO kEq|߇( m3sHtȘF/Ӄ*6h+'ы 1$#{bfi"ԯ[? L)Jd ɺS_%a` tY"b.s>'?:$dpUT&d~3=SenT1ysa*s?c.tgB$CZiN$,M2{[@8#aeע!{ KThGXmt0Q( q[$0Rrg 78,hX$;+Y-sovh~Íra0_ah0KS),,GINYC q`4H~R@2]g-[-(݋y`Betl& Lh7('LpNX4Mi.GL 9DCQ7T#ȋ* 6esTĆ%D\T+;.cýgF5BU Rm Hʵm`hvĞuF2x: s`3~5mAjطuw~j0Uil6D9y'bڳ릣B+̆i"lּo0Cqf T.rsfBʝx( & Om weआIu!8z־UKqYHFWB*\r?'uG0k u#ڗx ˹Q9/< "=G6 Ύb=TXaч@ f(:.9uPч-ۦi&Kn_z6/Gcz7OQ&&&׀`d0̓`*,NZ^Yց*0uYveS4ՙȄF pŗ !\~J,&,.GhP$XeP WMbv)t]/ bCyo!0PZ  Ryd^έܹ'@8H5ŴX*LW!0:8b2T4ALN|$3t99%%2;pXqN3+KOO9wNUk*Oٽ6XL²23$+zh>CAc1(%fC=p5kE1:eu΁R!M G? &'([,\pA a+m'n "6!Y̖z"Oq4Ę@ ɨA {R7E} Za4!LܬOt?aJ^6{h?%n˒wSv"JOEl> @nni&HlGS_+)sSOۚͶM:}bSogXO-h)?csZ9-dJ&01u{&idԠh9*E1]F(iR'MzY\C^: }GnB^UI܎WTN/fґ'*j|8Xe~+6,L9/*,2de0]_:O a||S[( S&BRW`1yҕ uh E-VԌJ6~E#QI?Ϯ/jF"!4L~ [<ەaks  Ǫ|Xf=LZSdA&D"gׄP։ B%‰cgE3+~C1P{|[7G6R3VWF!łAPaEPR]`b6 yڐb)<ÊE~[|2\{}v;ہs5M1V]X@sP$F.Q^ut௠{):{T5#KJa Ǹ?t. h0yGluaWq^'q. DW8a|"wzm!tYr/!sU Š1]4 7_MGA!p׽>ޑ;GXУǴU7iQ#oʸ c*)K M6,ԹnK?ќ70F1q=^iU))7yG'Xs*ݶ4l4; h|LpM0ҵZϡ&+!C2R/WVw#"0W=z`Ո[[2!]y6y _;UȚh^ ۉ%6 ^4>?7N;|I!a5ƥaz ,x35z"߀MU -V=#g1 sN>9gkqʻw޽. ! 8p;7a<`eͲQb&*dZ7L3*|!-?Ðԯ n J}OfBI j _A\ 0bxҖPh¨ i,/>6,Ɂ; 7 jM8,,KE~Җ[c~oG~vK1‘{8}D=lsCHWNzAHޔt Bq!3W+L`l/@jCSIZ! Kl]H #6i%u7]dKB( P! &g$`ELf]JeJ4RN)8B QdZ岒BC*~0R].ECr=hUm-R[ -Q絃N8U9J:(jG xI`eU7-0rtKo(14%.ۍ( ^L(@\pZGTX&R\vC2E֤'Q*r#І5AЊu]:yݮ]{ͫsMV{˒"X[ׄ]1ԧ~j]@A%HǨZ3F58iMrQm}E2n1B)WS7D|xdsh"èa >\Jfdjj[,WB Rzk`K!tC"tjFnF^B9שʡ*o Pu  wGeS2{unk,mlY'Fp)P$@`x!V Zu1/xDŽOHf(6ՂSONA a:ݒӀkGΚ@OF[cdj% S ^y t@;0QJ]j#%7ĘVEEfF6Sfrg1Rɸn6ÒYV̵ V0@o[ 4[j׫n}7hվ^2t@ƀ=߀m4{F/G\- зT&GQə[79L; xkB2 M11dcqЯpɕ!;_yRTޓ2氧24(2(R feqxPU6o0$*=N>,9N 򦮶kğUݖf.-WA|?0` ށ1Cw1gMk{~6 DsiC.7u\!hbtZH5Ò>,/H:d[, *2[2+z5 _`=O{:XeX9|K1%I$i g/Tޞwar-Lȃ~Tٗ=ϼ08Uu\^[dzNaCV/a<]fI w2դTtUt8 D #])Y '3ٮsvl,ٺs-1-#5\}u!t/WtDMArR3w&+ߙyu!#:w@x`f1IqshF\$q7nr.!nfmXS,.\dnpfh;]@X9gO1(1RoӁbne|0lVrO;~*_29zƷittпs6 nӽ'O<c*IFG?ZS|pށ{N(>.f  N >8W}EO/>a%1t9 po%yMDW6֤! 6 MBHMn&]0!Md;0/i7:t4%toʛ3|"Y+-[T5 8}Vv]f"awo;_37t L8QZ"{g,T|3 BS .e{.'Ǣ:9>~]^&6]nx= V9G^l|Zn_3uujP)a㬐HJ}j>Gϐ%&|g^c)&SJf|,Zѫ7!o3{yr 1 BJUL 5Mpv1@64[N$r6IDC '~os( A~ح?IH& * 9:.@V, Y$y+5v ׋gYy'YF5Օ3AbWā@Q\z̊iʸ'r KGL,`;aG{氛xifd(uZmH·N g xyFn/Qe'O?|Q0Np9HdeXQy@)n. k?T̢~ Vu/STmA?u1ɐ[4"3/-˞jVPDSGei!G3t=|b yz(zCWAM협H J"Of)̓^Em+D}Z垔݃ ϭCu)|!Ј-߬T&WSM7 xA~/m.зW)֣ż4rnx'TKu ]N]SDq l| Α}QɓaYxNH =_t=\VuX\į=$2.3/ۧ)OTɽa4-pS@9bg§Rpn}>~ H t}}a}c6W‹x'%/Y>^|X>/e>JMW'֐&Ä_E1, 膲vժ,\l*|#;lVS26zW]M '`e푏xYևn.))!C"v :%9PHޣ~Dh:pFu4|c WhP)e0; Щ4(mԛ̳j~>v}z(_sLI!GFW!ݜ|Y{g0:D3ʃx>?_^78lX/.܆^ p/ S]mV/qy9ҰO 8zMQެsaQR\"yŮeSBZȹ0<ڤ4+eZenSnrثS+Hi_^lvMewj];0V5ٶq7mkD^vm&GRlIEdhO(nT`5zZR.uKpM٢n8&I\Oj;Gj)U ]ID|1%Z`7߷Rа ,mӇSSXvn:7g `4 wpŶs%6%k:oi^5ay(nb@=@#J~-̾A;]\Ŧf=uq I]߀3z믄 ֯ʡ+eZ?(WڑDXFhK0Z:-21dA}q sXDrPQwќlpF ?}s84n)9Lp95:)W ch2,gO xJM?Qr2JNGɳQ||| ?[%7}i8=)ur .YepPBQ%:1W34 }0P]Pբ :W}0(r/"-xA_Γ]hCgl1 Z4xPn0]#4^{,V!eˮw[{KDuc 9x ǣปq$ >:ը@Z/JW SyGe) ;ڭ<ɒrd%MB@w)=j#X*[nӢ/0y,\u1Nt|Okdj,;.쩦oKA.ⳒrKIZ' /`5Z[:T0*"`MQ0=Ć҃.YYi@ Gn:k~C#oH?~D9>P?8R@ SPgMРҾ n`p{[&M>h,euQ^׻'ml NkBWj^#?;>=8i'F]kra_ fys>)flzfۤ{?MX>Wv-nu>?z,ѩSwFޮ*~Iwa(xj@t}|LtnN(Xr-Tz?Vq̲p+ouYޤPƙKY^O6pg=a3ZYՈ \h p6odKj x59qɛkmY^Cj`F[a8)NX3e뿧! T+`r_ n###o'F:AYg׻5/nh]09,%snuUMJyAez.JQs nT(49]EۛHl9P$Ǹ.2M5Kxι2T#Ԗ¿xLն6I'߬-_ }A&`:7i֫يi-<kuvbк-S]x^va鵝 vr$j 7j'j 6^=I.&l&]AXgf.rĬBT=ƵoE\/F%!Z w- 86GsjRG|'FcLN%'/j$ߔ&]?=ovNAn2D O͌7@$H oXErmMZy{5m›~# k\shd dwaKЂ43ē YU|gi_HLS[8p<^3~)2/@Tcrn‹2DV|~u .c} ӧ8n` Ѧ%*gO)Ÿ>̋fg1 D4F,ݎO/TCUT yKI^ yI:'GAnIqApԤ H4<$$~\9#VwA@70 53f@̪r6G[! \$H2M&m>(JIF#Ft2W___ι+ C ٧?Qre[~,Ʊ,tr3фem~SaGxgGȌe OM3H8[$[ BdxE@*m8^?4t,TXE4{7+7tnKX3$D?Q&F b)t/Eo09Q@~Lx{JeO|'=^SK4Pآ!̞1s2{I0ǗhJ-c}=d qJ&XMk`ҎBN+3tTnJE T.pF^m|GtCD@,)uքd;& nx_V/ Udͦ@wD#0 boWT)N,b>Q/]SnE I{鳓T @oSE@mINl l=}v6ifP '`imaM9򫭧dLv V{oqo6ƅk=%piغ<\]ܪ >URϽ Bk*j \wS V-wxBk ]Ur |ɢ+ (:J8 *dȠ`KϞ}"ɞ@~ \  9{r=;"wXLKcir(LHkN暠i",ItJdGk3&,X'<`)J 瓞8dh%BRt⡢(0@ӆ DSl5Ym{G : VAFW,2idWIdJBz&sSً R"h8c` [JLDE!n#xuR];!,ïo=}^W~3_}؉åq3>CčuT} W>:}^ }[&_ρN ʈ7,V_g`[♰-Cۈ/x㢧ߔ6h@HIh 5(wDl32+wtGMiTW3::|A0lN?EF1DS(ȶ7>3-Z_:Φ,:7v6޷tf>&^Y;krjHyBM(Ϥ __GmZ7F]i)8{0La]TD}.;T|0[,&4EJX֏w">"ߗ .ↇ%_DI-,*y}O~LL=yvl]3;4g2ly҇aZ.G8v[z5WMngv I_tdMO/lҵk5bFWJa ޘC3P1~d77rsx|$NYEqB:q7Awh@O )^ ͙57GV44N[MNb76#WoU em? hi{Xc㚏ӗe7H^xOɝGT 2f X`ܢh~`NJSndon]毭)?smr3.K3mPm {tKiG'o/ſш,hp>zWp^G!> ːv˜6%3%m.rvyqv^#=)rt^6e\,_K~@%;:IA%2 75Ugn͟b9 %y+^{&LV E!~@!|S3Z0q1 3uD1z6w&7CX:G|@ŧoߕTמ9Z>ըC)4ބ fą[ MJ1F9;Pq:00 0:8Ȗ?7FQu,n]&@_Lܡ\XYp5vR?ԃsn퇫m LnqkwOe~/a?0%M %, Y9Kp  O{nMG ~eE[F1*?mY3ƭX/b :{ Im` rEg#uEwJ{ʕX Ye.!0&$r.ZrFGB̀Q˂hBG(DxDxCENSX09 \ NVm %Z$R]&3q+0U'p ^% n l_'v} ><4eB2l{G[08CMcFc^6`a؂Uu%r[Zw8@l;6LKnn5~heU\Yg=gBDU6dS#Y 0]|@(m.n+4b<\r8ݔj:7B6|xB?g<=VC p$˓͝]hIXel;]u䭍xå"CƬ8"yu].r0r.N+n"h=,;uMʊ45lup(Lw/zyZ(Ԣ3DNEqKp L]8ӐKJYzG&t5OG ,wUԛ'YVg/-2,nNq|OuZ_?He >dz݅ S1wI}}H ,V6ЕۅW|H9 __ZOF57!1\ZC b $VZThg/ [ |؟v5"S0FPؑXS_[JUuO<Ҕh4dymDZ뎐P *!$Pr5KZdr);m%ACl,!S4W\%^phObCӐ5 \$0ĉ̉d {EC.c^eڽ9᝜FKmK+\s41'B2&gؗI^?I,_96s˽F^- SG|&2ˉq|4b&Zr7Hod$눃+*~nɑ@m_a++wѾ,fo:Zn iw{нpnx!"ߩ4 .#~ytvD]Ȉ' Q&+G E7~[`T @CeZO.zu## ]҃JTʌSeq)X30>أ Ķ xg>RycCM<aN/T0Xt,6= Tۼ30d*=JЍY&1QT'#]=WrG,Ǡbٍ 26ʙw=iDtoFwl 6k=4C8^M5A_MVb+9:Յ ujDdr RM/)2!B5+YGu7tBǓ}QQh,z P๋ds6acyr Srzn.m?/} D,ùqAm)gশ T{s'RxBKeywGpv`64Z*<= K&Hш&~/5hVEmY0;u~P-,. %`Ыꜛxz]}Y$n C\[q$ur3^ [ҿ#!λɂA{D2URa.xQ¸#N7 zb5DJD?d+V1ˤL;PξMiNbu,,D2.6(a g Gt_g,BLۋa#AU=DXc9p؁zr)EAg &a#P U9ϋ{ɤL<9eQd1hPcd=yIIG=μD<]F)%ӚY8\9p#> hpFK8a#EV̨#A`sn6!FM3B{V{sA^ ƚADܶ)TzpY!1=(I"ZTQ"m6'+yAQ8`"Nzde_C*""?-X0To&ahh5"uE:PhߌQaQ DyLEVQH*z!T~a'2/wJ:xtx4fw„8h4fFݦ~Q.M5e[nfJJ£x! BCS-;"l~dłv_lYPTU#=⭦2ã`\˦yLSλ#tQqY 23Ԝa{&UK(Ycԝlkd-8ב96/ʩ<IGs0%EH׶uzP[d0e˯amEZ1[t9qDRBki n#ҼBu;J?/j̕7.9#v_ I3koj y|f 9IT)9mplҦj'&oFL;A?wS9W~id)/^ߋwO+`|pwt$ߵZ/`tNѕ8wv Am% H[D#b#P' #R7ѾsRNNn˩r<߯DA_0[/R9~xAP5y}h_\ q}p&|~h>nAkuٖ Ff<%\Imd+fY,htM Pt,S x*8 C0}Thks7-Ll+zP9:cimS'V~A l2?/3nc#eѧPdCP6f>$^0ӖxInN_ƻ0\,)&'9u/NHǢMBC^,9#J1̑w-ɀiE̕Y*L9隱ȉ9oAUHb-[PB2m$\AMk#!-.;PjbJ0pt(#u[9NKs<ٜZB]zR^ud']U_XuD^<6nޚT \Vˆ7-mKq^Kp\E$.X%J(߲2)}u{q5^EЪ,gnyؔ3G AD0u5}pii_hRf"4lIs[ٸT30h#8&QP~ X?'%8h[lԟD7 %e8z!toTns ,dr)e*#W;a,mbM ]eC҈MFo-Tc"b g\GdH-ybWѬxjd'ѳ|K=ֻ hK(mT\=/$O&]LGN=ЬB[XĞ۰p'0#p\*3Vӌ93 ujQ,5*dUĕBfI8m]L^Ȋe֘L*QAhyqG>}6:4Q<]61 @7hBGϪ.jE|1?FNڍ34ZcOqα\AׄqyƱTȦ.mZ K,F1/'870-#4iŧUq2Hǝ#ȁGpp-xl7|צEplh+ʑr ( IJ`%nMO}CwO` id;n)?WBUcEA|f4>Gqo2L~Gtf%B .י1[/՝kE?,k8W˰78ÔaMMQ &41Tմ>ѰӽU5y%RGCƱ /0e(3?0lf2QI^ $@%Ro(ӊ30֏ƠUIy 2g ,d%NUDB ЋĺM :> mm[/>-YZb}ʔL7guQ*NQ1.J~lZ|eZ3pGbRRXU꾔Hw"Wu9")BOhcO+zNVs2zƹ M;+_UWowǑb#.^? ZʐRs DYfSpmMO]M> ke dW6F1eLrS\9Y/{WВS2wd2|Dz0,5"^:RͼtϥaXK˫F_0@$ ӳzfc {lUݛ(3$^ȖtbC݉[/aK#^Lsz {}<|,ƘVF,˶--v:3.DˏUڴ;t=/[b {XJNYڡ:yi']nxjoTDIN/ɝx]6%9`^eBeFCH1wH"}b-Wd%o% 4^|k#lĦudh)/,>Z,rY*JI $zǓS5J_z>/?q)'lLOloEphټn31=NBedߊ E7;˺ x9s6 +!n) sm[%*"`<.ol4JEN"1xF8rI[[-K+ hp9`X正)X<0[ppRid2tq!0tZdpn=If5d$ 4lrULnCQu~xb{bTy:;Y#b2m2y666PjE^ÏSa>FIbXˢif"tSFę'GMhɬj:?Rج`;"5wԅfQ]{tet,巒U<ױ-r"ml+CqҢ IdP/3"Idc[0u~,Nш){o2jd9USϓ0#';O 'MqyEa3l20"v;Б.,(H1/.`n\t( M' NAdn .@<&̠[#^ UqE9|F49Vz@qQ\\<ު,# R`qƉFczIVmq, -e`B|~֙IiHyO,(|--q'o壖k [ .PIt)GXAP_w5 f/a_l =dE ~uHBDy64y6E{>zYwlGr(a?6gb̜-&xLem̰UҐjcĀ ܜ^"<) 98\b97m/ʭk igD7;R1w!}!=u_¾.5vsI%'l8:sa7eB]ݗ~PPu a4JyF]K*X5[~6p APûDj\^<`*\6e 5cDbe J1xU,$T-8$QW ]N.r@e)%sQPsVE!n ޠ-; UcniXeldC92UmYFLC[9t^@#9Y>b^Ut.o%v)X}7ZWcsE~$qGˎޗܻ+)sRcDմ7^6T5FeDy HuOAeFqvh!]LEho ,}N1@x%ĕ>gpp c`+4k61LRs> R ݂z'Pù[[W=;ŰEQቊk "OB5LOxuECfϿ/^9KnҖvޝ%迯˹1WgI7~[_NRi|!{/I坲97b ~I7rN27JkY؋Ѧ0"GC](Zc L5cMղ$;6߈07.1p#Z4DcüZ"j!{D8̈s #D(*i=-Vz T"\(ir$ڒ{ZGj-b7,)󽀟"I A9fnڎ#Yge fv ebx,C Aᓴj-$B 2- Lf3VKE3 B\aKӱ%b?Nt||^ڞcҁ>bn ]mgJ!NƓHNĺV0FO𷆧Ls$pOmTӽrd8f<lu$l X/A%mzqI sS5uMU>F{xeʈZk7=ߧ{2SN^?gU9 Ss:2NAeb 1%fq<`;0x&?I{n3Tl2ÎVRRQ/67uj^1f j0hV(k3Գ\&dcƔ[tc"wzFcԶ3 Qm rF5xబ #~oeD*h>RdjZ-"aq&x O5NC:{Ѧ n c*fOøb l0m԰6@ xaJXm#'"/5TM :`Sn۠gͥnA{MA l|/TT+$q3*>gՙEx_p.UnQRċ䘏lf`Qv͓|ypi/$)P0]OpRaT 7_Xމ2M%^1淑غM;OQuk3vG8#{ۇ< W2!-;׈I^}*W 7QtTCfɼ!h @dIJр{>_ 5[auTpGf>k_yHo$4W~YemWkm3/v =d ȃE;Lmn&=l)yIBa(KN^h9E0ؽ1]/^bO]i/Ъ3 n O 5^-4Yg($@7lep*eA)I \8OKV`UztxĿIxV%~ܪ_oc&8ӃX8}1>7h$|W/LL~.vANvh+Xya`)&ys# d6lHۚ"YXW= 6cP2?|]lm.K2!SembZKTKB+I\b(#KYǨ Ïޗe EIJk4eCZ8pi:D9!W_0H4T[Zkh6V41,OC`;^`t[Tm~ IկFTp1}rluΨٞ=ƃ!.E⧮ yRB[JYV`k}mzűR2uL` _ d#6ⷀ'%|C7@fta`NI XX)Tfl;[m?IHK`XeӪq4s̯H9q&Z`[3aYH3O8ZbmOcMa3*|]*O*ce LƟ>JNuxH]cԹ1jE.-J8~U߻[j;= 8)w~W|(G %[yX'{\Z6%QO[$Yk̮†!=85!7AtoMx$?=_~Z"ΕqN &W宖h@65՘{/̪{% =ny:,rK20E}nAL 4^$U!C:q!,˜??> pM+~ٵg+3Xw/t~?iFҏw6TGV?YY?7n?>>hll81=Wy2[;uL\kU5TvCOk̝#:Čѫ=CSZ#=QTp2 ~ (s]<3[V@ޚJneɋ|QjA3Sm09R7>ҝА ٨ɅGi0} ([js^ix`=5*62j;Y6u>c -M"*Ox\;/'~g5Ҷ[3cP{fPr3Fr9(7Wg:)!au DYK_GcMzΊ#1OU!A>rq48r4'_.%h5t[9YdNto0 rUmZl$Oy/Z\+¡gi"Z6MiϒNNZ6vo1r8Ukg2 uHRB!-F2K~؆B,~|GfΘ kF{7v`l e(XC~dsbȑ.˰^Sv7B '5#EpeސSo@ފ%02&_.~4nӿR=i tdrK[f5@e*O^b MAk4M| 2b4$BZVV2$1&kВ$cb3NE[J02Hf?0kƚhV$w椋n[Ü7pGK?9)@a"iQe^øvZZ7.u%5ٳK}ߝRVoDXkZl9r]&ћ$x٬ ‡އ5Ȓ9'S^s (r|>J^/Fg^3 /^;|TVm_PC=c~ok_6V2%2ڀ#(*WRp 8b<]ܦՒ^wS;4HBjbu#kTO@TAp[x/ *=m. #0?#N+O,& Π0IkJLT@M߇"LZ72+BՏz4J+߷m7 e漆u63JKWw)(cg*qÎO ٞ<)U{[}R(sr *_cJdVOX+ZDP:;p|zhyb6+[O4Mc?+S?Zx`iz\͍e8۽`VOn> ,⹲kmLӞ"1Z,d.Tc!BT]+}x1p☍2u+;SM*njw_8/VR}<f7,v3*E(0Ƶ-&^jcsi/ͮ=gp]J$)4HK{+m [Wydݳ dy2[ݡ]1eُEӿ\e6αYݢ&=PW +mjH/ְrp 㼂,9S_iQcb0j3%cb n5e#})%#)#)-{N'm-3.1K8US0#Zpcvl܏Si },Ï;5:,^6,aDAjNĭMӫxG2Hd?Fϥ2e"'KWiZN#'≴6mn[g>UCMEYX^꼜AnEED%>Pu^;zJFN~n7V}(F@F|CCL(Sd%DmK:pe9GW;%"%zG Pg$+\#ю0VyEQujb(]cJU|6g:Ԗ 2D$I"һ&oBB A@ʉȱy=pn)Qpjϡy0ٚ keh Pfohƺ1`]X~U0L䵈{:Ļ(:A+gɛ)7(=M1KiH)EwW69ZF"GRE G QEt-]\ZT"5㎞UnaWR PxIXnm 6Fb7pg 5mqx|2J>5v9oJNj'GQiUMJL hZ mX,- ,hYgᑔ_߁o@Mq<0E!j. 5AN-%uZH0 YaIDnJ"[5m׍AН8?  vKl'I}JɃ&V"rOz.!D:iGƮ_l`_GyzG`~%Pv 2Dyܡ%C5ޢ`M@ @Qm Ubhmf8 #1>ˁdH~͉@HAE*ĉ$,F3GQ%\J9%j6PN]Љ11Y~I#}x14#%}ȝQdKHr Ć}5[ع;]}š>m|vw@mcXFvj']h%H0v |9?Еb2~\c$[˱'̊Lm6`d*l6Bsku@?M!SdGt#'{Ƿ-BA;r%w,>78묭׺Fű"zqCfN$mJh εTZ UvYiě)91Kr 41ꚣlwXqq%I6IIc{(3j#:w{L&Bl=Pg3c.[ 03ނ*Zȩ&5c7'co=6JdpvٱI쳴P^CAx|EJ(1/ omn?qۦu'k<^f9~ѕUL wyxNF?'<)MݢjJ.ƨ`=i'"&˾=mh不Ck? @vj N]OZr|1)/Iu-QÐS7CFp ڝdߏU|!gu0)PhSIp,r6 '`=hxyJGQ "rO4B&X2+E΂S[JM5<:1 Oe>(0G%Lͤ 2aplIZd=0!\48⦧%r)rGwƺLjY2<Py;ZM3ǃHjꜸh}ӟN vp-l}L~c47D&v25 Sc"^zQwqPX)@zx(HA !gIA˴ć`Zs.5o!/mP,'׈CL)mYG|Z~e;V2 tc/{=T]l@'m FnMeq..wӫb7DE{]Qek2ctt;0vIڼzUAFb[j1hŸ01%W(PaQ52 CtGM ai=u. 7C*~aCy,ft})!zofc כG#≳,Y n0~z`7dZOiBPkwV1S!֕"Zk3o&&Nװs &oNO,K yT}>'?9OEL-=Yv2 XYxU q~c \SSp~:nn,581+Pe\6L0 $LǍB#ugg3@֣ZQ\sgPÄܛϑnY.Ш,_O_W{շLxW_ +G7eى*2 <&tf U_A)L]d@b'5!ǭٱѴf' ~%i`@=+[T)u)=`nY7O'ٙ4 s ea"5d΄11[L*Ou9%خMaV B(__G[jc>(/xKN!3{@2ssnZwqT1[ͪwCJ^fx\ KayA3uCq[VBUhRrL|&hyNYH2i5 "y6YF܉%qa1?F9t>x0q4 af?{hbzfCF~sƈA=߰^my]xseز=/BUDԎL@fwz@-ВWH$j}*RtF {ɡ2 LV'aalk.cM/EwtP'ʊQ)Y[r}_REIF 2@f3} .].nOfi(Ub*uS)ѕhVCu7"ƲXCޥ".i 4:ʹYC(y`Ev$"Io1\(L%VohXʁV"q=*ҾTFYٌpgԄDT-+q{&eaNS 8ι,he,=&lVqڝ˻Q5U4@Zjb2 (TYݎBƭ"`WkKK$Cjz/\Vj[ FTPb>Xc@&6Oڡ-h䘆 }>ؾGmXg(3/ $7EbL{8Kƀg Q_-z蘟ᭀR܌d́C#c;CEl,+lq쀬eLb,QW8JJ܍xN뱻+@N|is(H ʦet¼oLX"olGm%@~R5LOZqYi1;K0P6$hJL0١2W9> B8hɭ9׺&g0OF'_|-Z`3.3v0B5S4FLH篫}Iz97GHU%W),XM y-ZaZoм֖:@ę`fD̼ƏUϏg tYv1kbOmF' =pQ|Ҧhhԣ:rh=Je6$ Bb&EG j%Fh ,@1EaPz"h]PknTNM6̜M,$ Q3 ol-ruD4;O VxŌMݒ]16x}! ab.\|窱UG^π)JZoI#`r4kP^oQ7E=匩:-#f[Mv`́4/8HΏڳMu%B6uW Jsf΃`ݦ _wzxvN()e̷1wtvMZU?NA?+cZ V0SuL1q`,}x]?057i{`;Dsxjk~dșQ;v״54:e}ΟeOY(! .uՂV0#ъgq(ٚcU?"q7oc N6q9VT=Ƭo_>qEi/UkLnShMZLA÷Zwvs4h\L7E?.< ;x؅54z$V15;5nMl֟gWyQ'ɗK, M/{%` ,B &Qs#J9 ڻ.F`p|x@PI6r8f| 80}\cy6DY_j@MsA=[í)9v"Y L+&Ϡ4oU>WhWX|pN07LRH͉3쿒 'K.QI ƚYЊA9;<7;s">cf4\?RՍRS8gjF ^1#G݄ƔM~ֻA7o0켮CR;S{|NI0YJB1IBa95Q  3%)tbDC[p;?r _m }&΄WYՎA$p"ܐ 6mB0D61~UQēJ#SU]*WM*>M .ݾn@(=4;U0`sC'"_B<#l&y3o!K:T^ ƹ iT ʋ"ډ.Ѥ&^QgEbFADT Q, k}Cb7pɮ Ka.DF#*cM.qvQR !&. B9uifc]vµ:_'6ǗsD\ oD>'h55JoV D#b 8W{0h 9\Q,c܆`LBVl)^~غ "[կ2C-v}5:٣BK%HϱC3jO$WMmfEү`Ӓb1%Wl03la%G(:7;[ 'a7sI[bNDѭBڷ~ݸ(yXTt1Ej&u>让 :?4˿ܼB@7"AE Y2t% vЙD4h:@C4٢~Qz=?3\ 86;=FNȷ![s etdF7Q=F5؏{fR_el ĝW#渱V5OLԮ7- HCũ 0*xkJEg~1J*#}{v0߸nQC/6GB"5mzi?T[tcs~˖wH'5dr %a nǓlp7Ջfz{Ћ}wc-;o5sq'scLpoP4<֮AmidYU{h}Tfx=¿A=R {qI+?b'|.W+d a`aqXlHyE,/̍ku2k(jo&ᄫ7llw-tW.j% B(zė$9Xy~Ua]t/|Cꈾ:5_o䞵`bdG(qewL:5DPK~Vʀ8?,setuptools/_vendor/more_itertools/recipes.py;ks6+pN]Rh$d7x晚ڭV&w{.5Ej ʖ_?"E{|DAot7Z矶iU!MNaTuK#:oUJ|==;{SbhWտԫF6GjnzJ).zV{#WQaĝ| BFөHmwW/^ uyaw|q޵&@Ɯ=y]ʒi'{oI]r&_~'uz͸}گǼTuڔ:/S7lr3ݲ20mȨwm0 ėQ0r<;[.eY.Z>n(]?u b*yFֺ1m~κm*H="SꐫQlˈnx'T>Qiug}<;CUGG=3{cD0UIe{7]fPijVYWz ;iYdIH;YJ]L Nٚ |!L +ׯ_3T~&x_R1OW|" ϲUgT2[D⫊!<3x-8 ;WqE8]׳1Xkw5%fHh8XhVEpөx& rq!C[!sEر^V&Ё0p`U ߺphtkIHM'DBxyCصi8 z*Y'r2` kQp%b2,:Wr|*_ T$φe9+yz+ }]>k72/eߖ&0'`ћh_f|30I05EB][y(Uuٞt/*7,g:9U;3%Hs Z э*5 GwTu~}kI\j`-t<C~±U~}"cb_wtxR${unuc+Ah,}1,˞"N4g߈0*iaFye.kQ͡"'\fKE5M{gca6dQڀ6ʯeGG,*wdc쫽. plW觮2A]鳂س48Ϧ;.gC YrfR483sR`L@ޜK[t.ВcpEM1o_:$K^l=(GW;yP9 >ÔTa:Fy hAOYE9=1!ns[H? ٰGUC RZW)CZƩ%_Szԛ*byHNQ Yy1&B=eW)HY%6'F{.&xF!c2.R%{'>K >^2pmrޒ`w.+!*Z9qe+p]E"0)ЫSGhuk ޳R'z'Jq.CāR~4V%[ūo?_ JpHj/{ bF$lt6JiE4w KW{@dxBZpސ@PQl~'IM_t زJjr:с,WZ&ԘևۼIC1WQr1/*ma%y6GpmU9 >4M00ZD)̧ ͦ%(/15܍`w\l%8k}Pp}uo0Xu~Q^ޒF^sFoF2_hW^wjs LjJwk1/Ic Ppխ`L*彂44?dT9D|f}e/uC$/[1.. +wv IIw% T_ZqR#a)x(:vCɢhXjG9 |j\p /r0e LaxPq':Azi3]3] #B#~R"Y B0 ?1j6l*愀@rba /wb[1 ,Au_/C2HκNUumE&Ni0u|^,\x }-IOZ=S֚l 66ڂݘf⁂؎5Y YEѫX1#7- ;WKwα7{j8*N0; %7vbAąA3].d87@+8 tFf3Xx#>QoQ,7k|r*(4z-B3 p!$0e.-.lĐᐳ5ߜ5BqN&zѧ;o Pٯ`]!߸3>V49(3=6t1"W>[dp6%~QըWy)p {1C*9#Þns-<Rڢڻ~DƪC Qj)>'Z]=}sbY/}-zmݐu ֛ɈcqCG48vް6jy3eg%}\Cuhe^E@kT@u8);%Fo۷o߽{O3yS6 Zc:6%56BÕ)N;+$?+ǂJ9Ew{e j짤8cAZ ll<ː ,kJzO&d{f^6;PC']Lcn8lI zYل%Rԏ'7xd+r|-n4ka ._]5&GiӐYVC}L?ʊxS~FN7d 6EU4e*>ȳy &/KSҌd<5 EAh-%siJqL:3-.ؘKR(2BW&vonY,]%4o(O'9oe_;J93cw`שK|(rS _Z ۫z{ xKlMۯ|}]9s&:ӽHC.JeK~Nʻڵ(*:Bd+"E#uw30CV ZrǚZmz\/pGoC8SaR%a X!qe@oEY׻Rŀ9ZNw.Bnw1r1j0,}#o5,[kIQ XC lԞ#Qm z  M&pS՝iXScN'v@xf~3xۓe(=yGgueP\2x|+ 8j3}'>݅X8WpNZ0]Ʉ3ίA딲uBbNm$W5F=sՓr[iAC]`E oi˳U95"׉(:TX-%/Gp8B@:]KNplg?i"PȅĢV#S}!bzv~O!$ _xB3шxE } Oc5L9!%fB9_Tɩ?6^Tq"J]xmؼWޡ ;F1qE2sQw bB') зXVcN4h6bx4J?.O!g X ,c~*໬nE+x~du (QGOvNxb}~, >u{?@K4`SGL䮿+w_V]EIK @ 0m1Hc;Ƌ*Nt1ᓂ'uޢ}suuatnξa3;|zfi;^ę5qbEp}^4̈v1pPK~V=64(setuptools/_vendor/packaging/__init__.pyMQMk0 Wޚv`aP cJ"Ɩ sb!KCO !VEjGF7y0}Xrʴ5|dt]Y2^/19uœ1cѯ7:3Y )_E2¶'Xf@zXD܇in/{"&chWIp#u=\V"͒]Me`P}i%](3C0g:t>*27Zʽ#iGqW0VdPK~Vb  (setuptools/_vendor/packaging/_elffile.pyVn8}WꋴUv\&X,5ZD%J "n!%R$]>ؤ4sh87ux$R feǠ%QB"(yD.  *4] x*.4HF# :Cy^bR$kD"d'y'>' &ֹw:KtzU,(K;Z8;l{O0~ߏGwt8 ("cWhLS9D*L(XVj0$ >xӉVJpq{Rͤ& tf,"0Ds.z3 en$ 7~8D iRNp{x8+th^MO>3j}MF+.`ޤ8B=\͞=})rU>Ȟכ)W%( {3$c v#9\k ۧ5#rʎYF(3ÜC-; 0'FRڄvG\+w䛻\^BR~n!l`ؤ*p#5.yGxVl:vU2Pv{J>"ӈ5r(f&'L$L±4Ҍx (M Q -u¨9z4|ޒW({JwI"S1>3#<5UlXAByY^7 ]L<ĬS7 IP@[>9Y&ыx{zu~WWW5~WvUx(7FgjxG/=LFgs/XB޶H3d Q'mx >0sAoJϤ@UPsS b ^el>V|N޴ÊVb VZw~ZWh8We883 v3=̌x4z{A.iwg&*22WB!sԆ LoPK~V"r&` "*setuptools/_vendor/packaging/_manylinux.pyYis6_afR$_I/Rtm5M,, Q$BڒիE$g՛yEF~EV'2YyƬXe.{+"]v^TO<MEe#[E4HTh'W^pI8}ǃLp?~??z~_/Ϧ7ߘ-ÖC+^,^|`IZ0ğ wʤZ4akB$Y,U+9EJmdz1OB[3?$=Em߈D&;b`|>g Dƀp`ɓPLTU~.{,E=,.(@ob4ۑє8I)h7p5,LqSxڷz\e,2XCc-QAEUeڥm,0Sw#FeDя CCf~<,3Xc u%Z054C"Ot S05/Ӗf8P3WdcH@$btՕ)5!p;xt:.EDayX}*1 $"{0~.e.XZa)e^е_S H:XBuD pRuh+(\A 2hfq{ !HZ$xdB~IJJBg[NfLP{$+mY"h$hLxy%ZMԑ5 IPEx(M5p`,A%= ?h@-O*C!ʵ̋$jK"cOVDFdt;bXem&" dA" o!md Zp")}s3pڵަ{oV,hRo 짒 ѭU5h+~#j{BDk՜Q(GL-}-GiV/3iqMTH6]VylUĽSA #R7=#K8\R"n> Tyoh*\9&*T=3u"Ύ+lUlS]@H x)IZ!(N`q * aXFAL,l@ACE6l5́1^K9(URg~6zl*9g+j]fӟ]bg3y3ص|ۻ^u =po?~EUmöǭCzk̃6Y0j=}@"-٧-=b!Yqþ28 g8t{,Xi(/C0p㟈%]1w\x{3SŰis(^^ɮzӌ;^CDhoVʐ]Si)iUeꓥ@UMQ/U(GWz i6έK?7y)*\AC\?d 8 = m:Siv|WKvrgX8Ȱ;:Zf ޤae-F ӴI}Hd篐9/"{5ۺoO~_>=xY~| d# @ׯ dXrt@v,I d2A-d@Lp-A_cefC`-ig#FD!$ fNʌ $T#zY?B,M4 Ma ;5 |sLG`)Z-Aæ=CZlrBg߷R9h^׳#vہkwj|SSUUŝڽ@^/Fr_|郏o^?hg`3>Iy=2.nUlTвvI36 Ep_M҇UATaIO,BICܮ(V&pr:yfq0GQ5u ޷-![Oknkm.f'Kne5[,Pt?SHo4ۺd0nyX[x׋ZiQB3[,~+ :88*>}?ǣˋ{GZ 0T.}=cm`.ќ;+bp=܌"`ŚN~#4):ؘ^6Flv`~`9PK~V} *setuptools/_vendor/packaging/_musllinux.pyUmo6_q>Llb `0a6@~q ( Ej$(wG[H {4Moo+p]YT VFh@,Y? (;k^vZK}ۓ? %JQ?psj\Wt:Ή%i& XtQnX1nZS 7޹Kjk7^X]ވÄ蕫$B1aҺ~$)w?01l2_4vRxz8&I%j(Zn((O83o;m_ƈ6sWh(a6Z2PUY>ɢMZ%} )pp N՛-|L_Y;"&qXcf>P{颷z`_ڟ*f ;XӵE/buėyc”튒G/EyWa6~r|2lh._R@:D׊R2E~{ҼA{XAMƚ hH^ K @tXCPFs]|'z'ө i?7%j} +/!{骸zW2.%on9X~(qxz{$L+[@jiA=}R!:{fu%¶ޅx.E!Z wV{:NB$DHo$^WqM!d gw7 贤qUh88p!+,* Cq['3r Mbλg ](M"}_adq@i3EEמP׻]Wvc9|^`.mcHw"]C_fNbbD⧼SCjnG ͊s^6vq\Vc&d:[|;,[_?7]e@f8R@%Z"ܰ _g8I1ˤC3k%zz'ָeЊ" Y4=Y^]C+f2b#HVSRoib00 \{a(CJLaooOG8͛P0"!6G csuzsVTN0n{RK]fh0#tк,Wj('#B tSd_zzw)^`r=B4Gl4d,'<7s9/mgw||2gms^c[Ž[*9ǍՌx1TVnA+Lmioȗb֓|ziXzFH+Rey^}0f] ' w԰7Kti X# *:0mLX^u%"4o Rrb-Np&nexKcn"3Uާi( XG$_g_b̓d27DA8G|c9kiHrmXUAeV {m, -THtش277AɡB(S9JmI Z" %C~ r2b6F5A 󶌤KD_'WԢl@UBML^Hhm-Y`un)+pđZ;1ofP2\)٬fhB]F]AȈ/u@IS(' (Qmsnns_NմݿC],|q)g~@j<Jiu3D.d'<̠VrгWX+ l=6]1=O߯Awo*+:݅~~_N2]26dRJj $ *3JA~ni `n^ Ԁ3lY,FdeA g?}vЖ##6g|luj]:ԯ(1y`g ⁲{\-4m7;CctI@K H^ekUZ iۧI+ $c- ˎʡQPf"R|0磹ծ<2i Z@1֪#{@~0 ۮq L2|.(>u/[-Î3?c{GS99l2. W /3ֽ/_ѧ|4p1Ez=Bl^G_'(MG(eL?oF(S*umgmcM0 ^m\f<=d83N8._SZgs:U6ln0&Կ$WUjd*gVۆ*6u`e!/B'Sgr&-tcڅrZAh 84MՕ-)->9X|]N.\ʐqغeYַ^ŔEjUkw ~.0;$1T⿢\1qopvK~%߇}ntN77F4cjަcKzMd]a8Z*4{.[8phښNHv#NWދd<b hs^)BGt i̽-Y2u% ^ jl;/M'ۿ:=.^}i߷)☇҃U󨿺(ZR= /fS(mdͳv-?yj5a<U;>ӗ5@`QtVɯT8cA.,,4s@f2]ȋ)3j:zk%4H?%ྯ3]@C;J >yB'Ӵ{"?u6\.:>߁/P$@JQ#PK~V0`+setuptools/_vendor/packaging/_structures.py͓=k0wYpLh)BB϶"ҹ_YN?dt HQY̠V-ܿT&)oogw(kM-a 9s?{=qv{woڻ_ڻ{_tYPl^{rM tkΨ9{M;=f1BvDBq:u!/Jfl 9'M.ν٭6*q ꤬Fdv#Z jg6o_zy=esq8Q*=` S~j1d/vPjԗ5eI 3()m~4z׏nzqo8.Gy`2;k N?wN~Uִ\K%55|g+svՈLǞ) ǥ)#@2!@I2!O66F[ܲih*(GKRf oG{o7M7i{`斴}eC*>j&?QuG^r *7B<>^`bV9M>}ɔ/f/`YI3ݬݎ6"6K=)ب0m`Y9b "Yy-#W*Ō.3sҁ' cw-2;QʤH0|タLrKSI*BNDEb2u$M)k7iiz/(9@wIoE`_HB2x8F1E砫FMnhj p{>L40TwR~7!4hJ680+H+~{^AU4b aiRYkd -JmLW0aϹq%VVFYYWrc }& (=Ŗ,W.h9<)C(YnV*)Gv^%RyA# ohD>LdRkѐ'8T\  c|*XDddhA0;]U Bmu׽Gq{JI.v)hT;pɋz֛'%ר%M?y{ L-x/GJ\`lDe',Bt #ˁW0y<$lժ]\&jV>~?'r0 t-(ƹOA*݃Æ,2( * _{=M1A~Z*_+Q]=xvp(h/36dѲVєJ[%[Gf;.0?V>~=y'j[,@zۈӔը<PK~VM  'setuptools/_vendor/packaging/markers.pyY_o6O.%mѢXr hNڇB%THjp>Dje8>9g~3C;ɖ-iÿ́f E1;F S{M־h ~qdK;SK8"ߖ/ƒ@S3/oߜ;;SR4PnV*R}2H . 車%S(wXV/J ~]7my f *k3%.ǾO踔UGx>W.zm~[v}N۞GŭlfV{PMx`(p#/?Xmn7JEXͷ'PWYXѯ 8THkXݳŢ^UEN*ynI AH&O-B!;)b=(+c1HdD}E!N2*=iそO5BykM ,p`2ނM~X\9`BC+B+' :u I-ipQ  It^(!٦N@_7 pSoQglJڡ!yqx_ߤ.9 &y0NYJn>ʗ`Yyw*~0/cɃ[ZݭZ& "Hv2\;y@J?E ?ȘӃ9%cr72O=e Z!9x]$yԖ3LR9-FhRXČ"UXlЃ«:W$\{Yn*&x9K#x7lE`V #ێ!Y M/}{lGPBivqs֗\Yk-bM}m ^C<`4Pa۹IoӆBsX$(e(l^%ȡO"5eO ۿ&]8|u5&(@?[_W seU#&%G?a[ٶUhtȩ2B}W`O0a9A<^xP5p)ko4S֓w/C.?~=1Q̎A㹉9ɏ~rXVf5JW ̰xGO"Aezbafo,b^ul:!W0E3Q0A+RY*xBW :Hc_ȵx)?vN*^֌̀%q\sH Y5ƃJobYF+zEZ a;bf YzfQ| ` &43$oZpj2 x),;.z;AB* @*Q&|mBo.1D^}p,N04<{ڸΖY;<;D_t4ñ#ngo,אH/^`ŕXHv-Ä kp02=h˳. 7Ntݺ;+Ujҕښ~U͞cy&ΥGR3:iyנE^Uvi;]4AO"nM|oNֺ;SB@2U59 {Y⎎ؐ'"H*p3̪uzFClOOO_/IXU>@KBN֚4!n;# ՝a"N%k+&W=LE4ϒ( O ],TVj Z&M_kAk{[XqKSBkl,^%jߤsP[RyCvyŠ'Lz;YMSqR~9d)S|:'.2΍&KH{܇qBI@umHXv&&Sگkm:'`ߩz3~er^w rIZ"5YCd~7濇.#SmJ.}9e2u Ģvn_Rq .M~p\dFNVMÖOQ&d sgSSw_ YYG92mL$Y_.U%le`om[qȂJE4Ċˤw35#G\#1=b{/ذZfwnsd-uc@@adzMVcimQ \P(Z%diQX:i &g fGf0ͬٯy? .?plmP=W[޽|"6)??3x? ~:$ aKNm!J@ik"]q~zquSfnAĊɔ=g-59No.1e^:fGzm;B[ vxoZ4ߒŁgn˓3)"R2fxa;{ pwHH)EERL.;#"\GfgdqS:P)9yͧDLL1 I&XgBR3#6ٟ$wY\z>;WT)QsHQ<3y\\ڥMKU` 2c)`,'PNiZJgQPi#0>R챲ZBLEDgOQB1\me[b\̬Qҏ!b9ǁ;YiqFUVLråPe{Ck6;P3НDZo7SϦ7y F`鍺P:n(QA-;1SC±<f_GՇ%,*MN(2 YtD YCJ9㼅OF.X,Xd>˔H&T6 t@ܻ|iOU3рeg{ "Wz4d@^Z?N^YFDSbG2@PIW ?)NeDo0dNYyzlG$HL:jq}=+gPˍc#@?8_͑il2tkuI魏F?RF9W.ܻx/ILMArpK+"ߝ]C9ۭeCfKo\X&6y¹qS-w,IND[^P[fm&2!7(8 E,VNÛrr@9aJ1'@:dl]yeFN ˫1-mƓ[ȬiSoo<%﹩#r3j0$N 3)CVp.mZ7>qMX9r+l'T-)%ْ b)A)Ac,aR> ]ȱm"4mEqecɞgĒkֈ.}|Ag^S0 }v} BxuE=h-勗Ɨ/ Lt[hP  xC 節Q8~FZK XJDX4*7:Q㑓&mB=B%2<\+_A"8"bl4'76T`DG5344xv'xӇD̦k`wsk^ڑɨ40`,'*r,TƂӝd÷/-?"DF",2L Qn*ZJ=omQsXU,r<0t: n$խfN Mθ)p=Iu4p2]Д*фQEt67_ 0޼?yB'#[HaiÆ^\".eF/>N1w ;t&WՅA8rtTP&g_{vA_F| uD %Kf{5c@ MXtD~$0ȗf1ϓjddqub &Fd`\JjyFZ_jPA!E, 2A@ykF)حNѮ'2*On{ 4p (aƝ9'd=_3u[zF JFZY_1Zf_h7uh6+fd.FeGAR"N4*h&1G6Cy/pq\o™e[¯j)9;(qj< k! QQMȅ*1mzVxvaW RpGOw$rd9GuTA8i- C#M ?İ \ naZGAɷ2o]dXjM_`{h{$R>{$îhVsd';C345HZ Ez ]'L[qMm#1)޸L8DY<5ٝ|[Fȹf3IӘ6>đ)fgU-9bJq7RN"˷^1rGl~mYfڬ+/%+4=O:%,4.|#Fkv2US{v؁q;c FGi.*ɤs2}7&"ˠ;OJ_΍lH껫@l#I#d\sSwH5BG{]MӁwq!lSr%3~<nvDQEd⨼[ G)ř*'gGw#TIe}/$~Yv)+/Ԩiw5m%>\&? RNو0 D|x1e9iŖ C0t)ؽ$U A$y =ko 7:7r_3bA${Kƹ ׊絒N UҔL2{3.PfqI m h}M3:ƒ "_-H5ϩ Tj-{/ WjD2/`ڮֲ:XH*'UQR0;o _Ƭ}G#3r+ư,G{H's_M W@z-lqKٝƺlτ[UC}㢪v1n5J:1UYpeI͈戆D=#p ?҃$|A8BɉjM@ .*ʗTvs&Kj L1ʇu7<=_/W$xbv >C![JvWȌe%EYOd4jd`ޞ n9w?ߞw JYύh(#{+[LT&[D(mU%iWV5}##tO>E M᪱h!R4Į<8˝nk\Z`^تo\cK˞ϵVCL2\*] rl5˟Xr oD)~yT(%Ѽ+8zDQާ:C/KN5lU[+>_> d@XF/ˉj5ʄ5kR0fK)":o.}ؙ n0|) $j}`ET8H>1 ;r,X Fm@1B"N?%VPĊv ȂQp#Z$ $6י ӎWiw TiRXj{Gɡcf6' LK+xq)b9 SK~, 3C_Cm%>Npz@N~';]1 lC&s&>yEyN%NuB$ȿg ŚNǃRAn* ]5}]{3!xq܉G0=9eP#|ZpdžugJPK~V7]3!&*setuptools/_vendor/packaging/specifiers.py=ksǑ+`P''JNU>)V] 䚋]dwA~Aɉ$r13/Go*L3EEY:y%"2ETrVE%tLxX?AUZ{ѣ%}[5&^ A{9=+fذR̋*r.2y&jMEYu:85Uu%|0ۋ,f&J󫸚Izt6/:zͯEݏ^IN[RRHbd)39ztN |ooo*.I!ʉƪ+D ѩn@K/2Ҽ|"Ի{<n5~p'ѐش(X$Kvl!eQJ]ry-(k1.hR#s0F2akz r EԈF2;ě21Cԝr462O*#jq!a}f耧>TP4&Y t=Oy@.Q2|I )='JdY:Q"EP??FD'1cRTu],i4[; 뤺Jz=RIDvlbRQ2 Z覥Xȶ ,tٴ,%k`<#+E$gds'37`ֵfO!e^m P @H_9xb iZ }RL-@#b"R7)b4B?ws8:[E"G#ijqZ{F/vqWdn'J g l TOZ ̵b'P5A ,# Ȩ  K,V#O kJ0f}pp7Ʒ4 HYe\irj<(`ɺZh0]ר/Y'm5m%qB”xܠ(z9C tRڳ*+r`oN'٢fks t@82XGdj%ZE,!Hgr6Ӡt%G@aMMܢ. "<קIhi_. i^JتyOԨ/ûݓ){w {{)Sq=`9T?O$U"MTd.j q2ͲiA(KnF/IBxLlW|eqpmHyt9_4^E?>zi)pwqT$_i;:F]3&Ţ$[La9H<&+:\j5~d[P^3ؼ lEa_w~64))HoO /,聪߀f#  /Nļ%|E.Z@@hRd|q7 b77v/b?sbzϟ=}C}:t8wR袔.cPp̤TR9N RxpZpRʨ1ql/$ȧ:kM 5ogG{ނǃ J)ܐ8ZAbWx(r-?m%N+HfxY|HO z# VgyP_mJz3ď/A6A0r8;>o[VZpLZJE˦]ɹmQ.i0iw/{(axC: Cy\(kS$<#WᒨgC-_6yh6LiMtS"gQ<:m@p^Qh=f6_xB:cL<@rꙏujZa1ʶVZ>N6V?Rp^ L!fnZ^A K+H^+Q_8MOt+>BQ:!{(S-db6b1iך0ۥ:B5ދTp`ŚS&"M 3 4[LlB61hJf˄ܽ&WO:;hB?h܉*R<ҪzNn}pCG0u]`\AOөBgƨ)RmW;!W|iqG`Z # x^+:?FJ^RJ'HdS:-샖;Kp!`/,L<8K'rLw?  B٢'d@E>6|x#B&$zX,_k Z0oZ`jAn8>DAkMGX'd4[H2$#Gw((z\jGevq ɴ}﫮HՕdIQbon]lv$(2Dͤ,O]{7tD_pTHȾ\[/6b4^Tz':8*q F%aGF4I"e6 _(i%`L3 ƀ N6ͲK2o2A}tLK櫑5QL+i10"ͬ~Ɔ \5pm'VSRpz!hrQA밹5W$iϢN?-dmPC[! kS=tTaE?[ `jWE.zl25޵|RM^gs]Bj`R\2Gޖ>g;śC,^Fk?]Էm5#Է{lKeevhvUw+7KNB8aWH'H#B%C<k@_*(ͱ^+IL{- FUOM4H0W ͓js;fDi#*:°yf5˱qPho;,fύ!pӷbv]6caL'Cv鑕Sﭠ5ϢǂK(5]gp4*+AᡞFӂ'w lxQֿKI2! pG\ twNz:S]u MM^Sǖ mhAM̙\tdAyΣ~dي|Fwdr q19{M.%HB9j(vjauSJ+eR!>#鵿BO |.Y-퉽*v ,.-r^GnKrxWgSYQ]iyҜ}I~3hۈdׅ;UZ$pn1њkDwph-y=&\SٳT#fZw Ɏ@k}}ZU`[Wi7J~:Ӻڬ%u)!/2GMYLflߵsyׂ"U63X^}h1wŕ3iРƸ B»2PkP7Q]Zzat{\GQvڕM5ݯ!|;5Fnjxns))EFF;Bƞh~؊W˴L;Lqָ0"39clsn6!18]_9>7˥'3ށeFlrW3f;봑&hfp"f[:9mOt'jx·RԬ:dvI fTr;[RE7`_>jħo'B[Z{r^p0C)Si{MwG61< L2?1`Kqc^U| ۂrsVD4<(od7\bmTs h>|\sȧeE-tlT$']@#W&0D3K0k*>_oMhۣs Znu&8 hSq;`C78l23nr 77 zON_WX 8U/B;JwŎ|/Si1j8]TuJDO2`prЮ4xepf5kuܣ'jɍW,ekZMk Y~:.ԼdСa(M .*5a My enj>kuCW]~UNn0yThN?t7gx] J91ˆ.882ٺ.+z+0s[xUa z~kQ ܗF+==^dsg;v{<J27vrKLVTvT{**ʜ`k+\MZn g<kȬru[Aoe]PVQ2eee[A^_Sk))XگQI':tbuP( uaD{יN&kܝ\X)d0vogCuˌ2ceYz68Z\]T(~_G(#弢+ Dѣ I=}m 18z5` i EOezV_XJc3fH>sנr% 隼4 . eq} E/䉝fXYjv(`Ě2/~چۨ†+qWaab%ؗeSv*)IUpIVdb1l,_2Mܲ(um1DMm_pPpUpO{᧚}jcrR;+rgsqћ Xx?NˇG8!j ~罘CPl#``f`h0wDg5{}Ei BolٳXJv_)|A@<+c12`e!)1<%gΤ;EUzh۩e,/n劁~EG|`-q*sP%J'.Hy)wD ^DTзsj|Cd霄lp1&`W9ϸ~CRd94=4?5ͬd.[8,Y+p /@bP }ήE)<*2be\(K4L} !尸+[pPd `"^!VbAеHa19 L̖G^*K= K_D,+d'OJ. TL*P).210;%I9I~ ]xA\-+ A2`Xx`to@ђj30#LL1hlrlf-,mo|Vi5.1gKIF G j0gyy!u'&U}oLߕ۾F]D1D&'Jy1|==|MKx׍!k-Wn=r^Zp0b "vvI|ܶڵkÈcS&woV$ fƅ>_{fSc @ѯ F%<j< :@~ x(ɌzJ~$r'C0J~ƿ`s)TP#j*;+5U+'7upx/)^:V!$ f@ Av "ŀcqA]10L!e0N>0P@h _RDE*⢏q|􂓾Pʩ<ȮՆ^7ʫLaǤZ@s@8̚\Pkiu; j_&bpCYvE_ I{DY܅$wzW_MY4^`i nĊ V0Zwc&َx ze_ѕʾ7B# (hhK%jX z"°%&~jp7=g Z V- ?nqkּV]bCh]SPώ!FN Z&{`IA}s>Se5/K3@538YE~{VpB<1hce 9 [ʼSYdAsm z0ܖ ԉ8tO46~S{A6, qL!l9&z6԰e{ϟ_RiC?_s!ex)hxg_vaJd[U:vTڭ8\{@=qF4AV&}T["O?n1U ?P8"v܈1U&fy nhr_-<>}ߊf+ ]af{|<SSuwbV" U@y@9ӡg^%=/&#ZmP7YҟɝNz5˶rKk53"Y_Io.}SFE^4Lss"隑+!m8g.= #ב"8*C1m`yj^pI}%_7!1@? .@Pe\QяSقSu 5o`cyg0s.ȹ9QĶF:>$g_M\&ǜtb`PRϺDoihli)MAiBV(Rx& ̟QDoABP'Oyt9Pvk5 ֨6ID61E5#J,\d\zQUvҊ `M:{P4/wTs0N$:AҿR>^ D H@;%hh1PG*Fg "]eD 3bqkX ${_Y͌/KfE ;[ؑդS%cw)N*"W48LRHiߠ]S!V|?>ѣ=q) j8hVg hхy; |L nXv̭󡿠}geYh瀞Si ovQ9VU!įB`~|B1Vw8 R3^$ʸ8YGfh5yEV2do S;xH$.`4H=(^$/q?/Yw jj\儂Ƣ(8E,,.74sU*z>0 GMW-S.cOnF55P,2Vٿ 5 d%K`Dv"Y۬z@3"4RxGTvc98|I&hHuCRtrGWޢHڢ#~s6p[]bO:T:PEݾ.6G{AsEb6: MR1kˋԧ0}u!ޣ"DѠFmOeCXɕ+YOOhz-eW%rV"txn;[wn*8F]zJ%Pm5Q d ^ nVe$u4 2Sg[bٙY1j]ٚͶ60 Q_|q߿3ZsSN?{YOЮ /:cӺUc X=9:.e-q!T68@oBlX?\P=77 nR>Y\"Հ}#C(.ףg`mNonL8}zi{k^tH93[H.I,@(y\vu1 uW_bßHL.b?cj9ЩӼ*xg:NN)CÑd[_F-ߩ39}fb ;/m %s[w{kPeӝ`6dUdhĤ.>1I̹mHA6B+2Į{dܲH:`L) _5]_\̜7My:ڮ Q䄏sj.\]c"D -3cZ$|\\恸|YhaYUxpe飡cqb=PCp9AE))gfC`e|`d}M`?jCtc rmڳP{EmD,mieN`.l`?^ϡ-A 40~3C~Fckė]W\W,l7с#$VЄ5pȤ9`YjMH.qۻC6<;!hDQӌf2ƀ{c݃;$k:vZʟ)' I 3: nu _vvM&GQ̤FbO؈3Sg,FS:zoT4tWgw ]s.9CF3ZRqV'"nwsmPK~V*+$%setuptools/_vendor/packaging/utils.pyWmo6_qU XjdR 0}I[,iE:leQ#8N#E9Nb3"\;rb!1PZ!(k |aޖIJ_X_QH IIw.ũuX5mSK.bK\@e !C\%DkPےKU3\nK"|)(D*V Ue LĘu\#sV\'9l]λY{~dYA!I%șq&3͒5s;QIDb,H ?u>ua@*I@%)+^!3v"ȘTn 곟RVbCӤK `%C3|ѡS3ٜW\PT+2(4$%S1 {߳C _Q Dd0: SO,4:k 09ߓp(+ShG~ 7jU% qPx\?LmS}8W+c%hR[| W1i԰3$[<1e8&8P V,@TF$XhLv$[`Zr5`$M\P XA'Su/5Ķ{CegCmV oR,wbhq_KP ^!m*Vla.ֈ8vUIZGmӒZ)D/N`2d%p/tbl;Ϭv^7~okjM` fu9*Q8II-à0r8_)r<いA5Q/?4Hd|qsF[p]O뾫Ɯn2~tz>9NӰF9~*{rlR|uoǸjk w5.A71MJ:pHAKM^nV#jf:P{o',ܳ7/ ܈VFȟ]#u,RIs>Gf3K eo.ªPiOiRz_Ng^|Q795F-˜ʴ@Z|N ̱4KeL-Tc .n Y'*]5q꥽ɍ>~8)s {u[xXOPL]ov- Ogc]_4nÖOW^p8rVaRS:N.Jp=|cNx{iE7Ug@;+;"p3`s8pyۧ9-Es|cՓ, emh^l[9-+LiM1 w?S?'PK~V x?'setuptools/_vendor/packaging/version.py]s>wFdL]I9y(ǒjFXj xX(߻hdC/,v|(6OR3^I$2%bb!YrX>oV<5ؐ$J!YL`ߟ0!;xٱ0g2 M8bsɢ|JE!X, *A CIJd1|ɀ~df%U. .HguVl$O,qM ^ ^3>%:eZRsZKAU +, 3ZoGb1`B>DT#fzúJ{fҽR'VNF7b313-:tj1HK*& B$7)y/bEa$*QI Eɩ䞶2[)h5`, &_;(gw|osPc4S*Y:GV#^Z,֢d\ }zu,(}^WcBJsC?\kau~/'>c?u53_:i2bSѡ=H1ReAɺ&׻a2HAKdq+6b`Ŕ7!;v}MוG\<f;N_dBCAjFz 92}z.3C!zOW % D ;ˬުa+A~L ޿2f-i6>Q]Ҹ~T _4U.AU6Vt A&nVEn")O>Zd'ͧt\ELpolXu%RɵeɋhђJbTYoڠ &`@j?acX΄΂ !wfWO@|I61Ud\QÐ9/^XjӘo94JZ"}ئG'?|ڛZA)@%,yHb9f6nNj4 UxT@ s8+.ʤo؄0'W*LR$ϬԜlDK3޵tb[Ȍ&P(UTY;SCAm7K4gvCiZxLڑTyCG#H3K(BL}&`/X}E=uqey5p]i EJ~g 'j0MXe? )<Ƀ%-B.ŻE!.*q&tLAA.#;FbNfaƹб]b:=>e~;³t(qjsIW$3WXrJn$x4\F&$ZOAl~ .g*Xׅa&vKHc+()f^_}b;z7D656̎|f2"Aszd TP |jಇS*Sܪ,>!k<͚$!9Hf|r5gil9|HHKcnQRvQ4ឋ"4~͊Q-̦r|z.+ 'v: f6J6LZjHsZ/iLm,/hABs)6 2C xďNDG{Sy~7Eސ̍8&15dcy6Ir)[+. i7z9yYv!z$vynw1,ÿ7 bCAvՏzǴ)VfhKl 6d4tmORQYA/,HUF Ɇ&*h{Ev,ukzIԝ$}K\-R#7<{\O³>rI Eqbt٥ݪAԟ;k  ̐) pQy]FNɤ!x K.oB{P}Uޟ(|I y!讅:.>IEe.zyx;"%>yy;ɘ: zqpvZVio3)8oHbjZM;i~ E%ߢyJbIwK?-O.,/^yv ؗ OWWW+Bd(N Oo朿S${LT%˲%AppRJ9xT)̷+ց-A^nӉ Nu ocshGM}NeS"Lm'ϕ2'$5JR[!I /dnK-xJT%qJMIQ>x@͓F3vh}䵼slKLX"9W=%$ס/e6R|(`DRtG}lvhjIȋbw@CjԎP|-Qs!pY O}VCPVk8ϊGĞNY;HvDn; .h %[e7:7L=N2N^h^/! &݉ ҳÚC{GGLʵv1 ~"8;'[O iN"q)_Z@nDu_`zOls~#0g1`irBlw"խ:ȭ* VzPh ĜK9v[gk=*7XPxiNM];TVw C~ެGtpG C{Xgb9F6,FwC7ZDt˫KkC;Cv=^+=R4M(.,3􀕸"Ί$5ҮQ> oźДؠo.j G)AA%GgkUʉpl+$ya 5ſ[2Orsv?hXO._]Vqt\iQҼ4Ox'= Kbh8!rpPI,+ DpynLC:La[thKBձs9KlMSܫZ c%.)S:nEkyQ [Ea&lM*VPyQA Ю y,HigرǙ6RwFt믕[$yu6I]\טy+HU}2hدT:k6V٭1@j1OL,ľ9gV [ߎBlWWId+OOZ"6VC}fdY;{yv0 {jm#Rp} $X%B= m&7_v"?ԝjV50ԫmH<4PK~V%$setuptools/_vendor/tomli/__init__.py]Pˊ@+x `"a= c`2:]wttUuWߥߓBd:Ϛxu.s5-lrunx ZcnڐQg/ћ kS,#0QEA k)񅏰1`b.R*mE;mC4@bQ dsHN}/="/rq>gpf ֲ߫1Þ |#^uԺWu(oSER6kzҨPK~ViiX#setuptools/_vendor/tomli/_parser.py]}`y4 ieI0˳8aۄ Z$^1]Ypej'(μ,T0>={3_5ggOg֓翌.ϕK7ݹ,X0yf<鵺pC{|Apë=\ġ:2]XċnyqUjvtnzl #/M9=|-7cxwi>,#P6z.k3/ {'b& Z Wc~)Kc쓂? #HC4) :XzLX9lq{#lg f|Xt{CݦH /jb;ނ=$'!7P`!M!1 d+]q/J9Z<炒%*$yQ]k-P4㶡I/HO{_H C3z͈@3l&59`y&]};$NF6|Gx[}h|*%i/aq$ m$ ֭lL+ V[.`BSgLn/kDĉӼEF)z!Q bג!Y 3@T룖ܤpg+o&-D6"C"2|jgh! :AD@0="O7 S.OA`%d3f?01:)H8DB)· l5!:,}2A0ՄiaAdIy -}Kb`D]m K- Q= AΰnqMu ʦ nA7k`z8QJ@@H:;ճk`عPWÙyz~W~CC1t!*ɣ\t$R=P6'tMmazQb-JR;^;Ԣ(k#z (inelat"09B@J$zTLWjR68=3Yso 08RXk$LtOh!^W#]yy&QNi,"H-ua`rbc/ o@E{$Cj+Bkĵ [YmUFoi LIbN SuL3np_zOEV rE~<ٿ +jL)UcoRBC x b+ʹc"X||GRSaL #+jHRi[S_`62TfW Ƌ|.=J~؃ \ %U(90긂Bƀ°_@Zg [5o,ӰZ 0Qeb Ԡ֙{G5ɨ^Jz6R( qj&iIBH2ql&/yz6rBhL]?=OW1H&Id``Xz(SJd Ne!R](j}' DV%j]*!e 93sulGY )oIzs/㵴/T2whP,gw[ ayz |C^62enkZ<蚺nS.AQOgǬ[ɠtǁq08|j6&%h̀Z ?)tć@KCI7es)jMc&fW$e5V%,}T^Jvgȉpu7L")RY[^u%mh8xX"d2s]\ )BNDDž k `<%!^3n]jvn|u(6ri , 1PMƼoU9M66co ?83W`eU2ȇb#ʓ$8U!Zr ,eǙ:b0vE o{%$e%2)SڲgVUEzl(Y[QR$i+!]4{Qm 2 -٣@5V~EMJ+fyC< >GS0!<,@5;fP]?^0(-؜E0;zH=sq2'ďˢw,uL.6$gK`G 9Hc7+K߉8L>Ȃ='5Fz)Csږ襢^!!Eg/xz6>*n~mrS(8SMzݽخfklR-P e7آrbϦ+oy^+,[+q !{ ĎX̰AU)֠vXԮKIimwg3/ (e F&;cY,}ةrу"pΑLJAf L:@d'zڪXB=;J*G&ywQcS=bpk1'6ڿ2^>G~z'AslΊmU0z;`3oΥ%Kj4V,~S͢ZvYD&GS#筊lT6rk9FnI^ڪS HdcJj4s3j|H 9'ApsکB3t L6UO+HRm)z$^/EQW@0 JitnGziׇZ\-;hI)&=EYشItob vbsB-< 0v-$j͢V.~n@^<(漢xmz]؅dcjIDsZJ5vvҤR>;bѹ`XvЙ':eڔWgaENl #= Sђx2M4#%!֚3Ib5}83l.GYmX#;np&Q)mFfpZTl -biUը}2l-k.Θ\r*ZM4e ɪ* Ul@.izqv]%h*2V+@u.YəK-#թgcz۾ҿƽ[j0V eDDP5>duXK-?} /&|ڵ`9'?&He_\I*{X;VjP yУ\=}Erp&4`DI|*>x&Bt mGL8 ۡcS!ݫ\7%zT(UA?5.7`xas/%8=f.]UϸOϪMv T7gm*:6JY87}K r,M>`u{C_f L$RP+k:d:Q]^=1{^FCx#ݹWd}@@P#{x_Oi3t6p b E܆gJPܱ$>}Evˊˎ٦~n\KBbp$ v7bo-yVe,7  E{ޖPS3| Qu L)&C"֕(c3*J :^ 2= ޿pyUit1oKqRqUm&n׍xbߊCW)Q΋#_~G,_ Lj%ZCHW>HĝVF›fv,B?'ٱ)7p`Y'[sWJSЖo'ה-ǻw?,wym[o㫲J^Ԛ:D+'^ &a// 1t6,Qg)O =0ʉ;Pyk7l^ɦC"(wlmm DξH` CZ-%C/[" QQRV}%'=LH9h4 ND@JKp5O왮wᢖ4\><a9޺r4zn0]Mwz6]7 c\@1߬KVk:9o/PK~Vu1~ setuptools/_vendor/tomli/_re.pyVmo6_A(_T$gka i-yHU *b;نaǻuo?~svR\<1 8Ǘy/fK hoyKPWRDubbtAJFǚΗ5\FFEQV(BzC(CqYIcYZFo 厔\ˌH7Y]&~ ZGIKMR[ nocZ˂̀҃.dw&~=ͮn4ݡ15w;Y!ܟ&%ρc{bTx^B`2io>qs2vȢIgoZ'yJ3*zˎC#DfޘqiAyjjSqV#{n&k7e4NRSs<]^uӱOIk$+e5 N>|:p\8փx>I3Σ۵|>hHՉL@(C;7Ӧ(3b܍Ec-hP%YVajHF3Yt[BrHnFv.Ra|+NӋé/~y=5.j< VR0PB(1y*-Ax=չ1*FFѹxH=˚ᾬP)vuQGb6"I[T˲oW l(hTm'[Ђ.4hzAС@y>A[M4R ]Ҝ: 3B>uM>JN$ʲ++fu零Cm.*|6 ;γkijFlVkB^&ozA7U^]zAht˕f XAĀ<7N ^s2epo 1< \'PK~V;Nsetuptools/command/__init__.pyj0EK AP]w](Ad^$r Y339X*%䋞R&Z=O ZPPjQft|Bōs`tw'֬#wl<pZ* Mn˂ s~y5l Fh$ORq% yq/"8Rmrv7(eCoPK~V=/M setuptools/command/alias.pyUKo0 Wp hb9 0`40[Nْ']}ö|P"H~Z*tYr* X ~8i&g#ژ(N 6h[«D[Z1]laQtYC(h DUqဧ}KZHh:"{ѶϹ ͬ,qHdqhs`>V;%t0ObrO'mL'x E+{̵lR@FTP2|Ok1i 9,{ @R$~ReY+$Ϻ2)[R2 ks(.X3SH*b~=uzEe20AHڊ''.v iϱ=2LlapvG";_;l2Jx.?fa;RcL3MM 1if#3&$s.){\ 7t vIOl'~ђGϤvxӱIK3}s^:W&vdO pl3CJ]oT{n:Mr!=K\Xs>:ɸNOᣳKJwdc:#CٹwG [ 7@T fxRY=za&y~ 6$$?.=NZ)׍)չ0)L%?iK?sn͂ MWUHWb?/Re %D/K"zhogϩ 47bG$"$˥.$1W࿆edoGjDy-8hh{}#l' q|k`}qEPK~VØ5@setuptools/command/bdist_egg.py;ksܸW t9$ΥL\>[j벵ޓCbF <OwAGTDFoAھi*~E.Jѧ|],PVKacW}"b5{j6/.'VۦYO%ڬU=eS+hXwq1OZ:qULk0V'\wYw $Iq!:y7C!97V6uz;$cX|RlU_t MMqx)+^g{ 5 ND[H%.} r0"ՏgK~`/ED Ua;.  r]qv-k ۗ5HXd=|V I: /'U[t%Hv; 4K3~SЃuqG&hoGjO *t4=HD֦idmլתɀ!'OSlP=nM5*@eJD?Z 48.!ѴM <X<0[STPI_ZMK H F'HJO0pkv7N*oՊjHɒa26&$YHz3zy S"SpìXvf)p<9؂`]z9 X@:\< m/3,-o;]Ppu l {Fo߯y5`%A~A>fe gk6?.K^ ~&a(6'dYU=*#$}"`vſ=C>" vw[EsV֢ Heu͡.Xo;lr1l-lG{aq5"d\FM.V+3Ⰷe bMH( # \w n̍ZPgWn]:tF|P_fUk2b-Lȹ(Vr^$Tz]'jNA6@Sˌ =,y{[tP4|4BÓ )iYX] iZA dpx[u[S݆a׽Up eh1%͖)[`Qń|\))~=˲D˻GiC/I((H.ͧ]GwxdY82Ur2EeaGaqTy9 M{Zd}6VG|Gl Bf!g v'*E42x:z{ 74(7Y 4Nw2'\'Hw ,ܒa5g7Kyf#G+Y`?>aIN̓ѥj (P(c,GO)QƊFf5竧KA&BjՈD~0,q:MJGV qؒ&ݽ q@;NS,?C.V؉0.PzϚ]pW/^^.;vT)MqC4.,PPk: "".] AK5nw,=Sͯ/?xay7*Aa{g$ڛ;-b3ph.޼9|@6h1 C 8 G$m"xK5VHpN"-SVI϶ ا4 tuvl+ d}a[ z\(XK8lL\j"XpEhM}R5}xFOudtXimpFU0H;1KwaC Rsj){mxL7Uusoh|,Pc_@E4Os70 4 ~y7.!ZCn9 >O냟w⹎*A%.~ [<+`)vz ׬M+3F\CG A"7ʞ;ޭAneI̘'%e+[xzNo9(v,SSQviOߤ^^!(Tph1LICԗ}M<=XG JTk,>2lHlU%Ã}x gzech@؟g-O9BW]970z/ο&OO mo ,F%o#Q3sߏ7a6ؐCc;R}d!NTFd0V&1~,nPaciF/_qYE$eQp1%K+?ʟq_xZq+}=\>6XK<0e&ܡ |46H; - 8U&n)יFW3d~zT%{8YIR"'"ڎo/cwWj@4A`9.D1AP1])L`:pH(`loF SWdI뜐Բ+IdəV]e5^۾6 =$ !/."xuOo?SQ5_n' ƅ q5 R,θ~Cm3<uvt @V:q8E`=8cP WN;k'ȥ:ࠕ/tnUÇN!(MMU&1)-UXTgs!ܷ䢖2OZz})>/n2+ ӎ^ǔG<`bޥsPQ Yʶ7r u3p,٦HF#kj4,T[Ħ0wqq,bnEt (Vفd"1H h1V&3+v%5Y顰"%yVDӸuV;E*ꔶ?$aN8$)(ާ^{Dŵ)|s4>; Z{xvusae\l8ٺ!"%hq$ k!uu5q|~d11{I}Fių$gbDp(kIkw[t~m4IYFalp3*0u-8Ddrǒl{G\PI9,l!EO) An#ob t`?4FOf\kkCWǃ}Xgt:(}˔> k Lq>x,B;^URf8Caxk GӦABd)+ F.g01oz x&ME Te]-;|;PU=_/ngM_)ؒ_CD Oa>UB lRH~ (wT:$o]#;KC@ z#VzDt;b$jv=XWx>byk5Nӗ . UaAUw_s%{w|wPχ K_sr}h..-ĺ kC*Uogb'O@E'/ ˬ\`)< k6J$$9tN }eΕ6Ҩo5P.n2miM;JXs@Xo\4_n6U4hōy78( }t8aG=pbbY_ht N[ELގS gL90h:] ),p03zueΆ\J8Э_G(E^va %h\|PôE@`gN_".m4UL _O8lwPK~V> setuptools/command/build.pyYmo8_A>J_`l I6-{iyE-I%̐(۹.=Ip^yf7^;ZY~[Z^~yyϋ˟v~*uύץrȪȖ.vwxN)pk])ޘҝڪ\zm¢#"[e^u231~2̎~4g7)T ;*lw* y֭<:}go߼9<?^OPpյwoҹqb4o"7Vx#хpki|V,r/_ZZUyrxk-݂,}ij%NCHoO? ]:[bx ?~ -h?,\[H5ܳ ў>] Y8xZz5!'!V#g#˭@lW.t ykG쐵F lcܣdJNG5E+'צXx nh}f'']VoT'Ovt ŔHXxVpuxzҥU< omfG=0pBӀ*RteY2@(15ӧO,&b_~|}~|LR %_#u j `WhmVVi ęWVȭznwkJt}D-`2ݨ[Y6K䀩DH+S掞``Xp* x< oxSp1g+e`eQR_TYmd%}t~Yy]V~HyyҷXrPgAe=X]6 S{.WWXHBȔS&Q q䵪ői+Jr Ex/ qM`(myݦ5#0oHI  ,%}e{"*#%"ޙ,NҠ!&21]`#d ClLNVIJιT:*;ڊܕ@rc9 Է*6^0*2 gV']5`XsU|Uz; jQړ&UA""Mo+b8-RVұYa"kcOLFUN4!'2ÚVlII<Ǝ7j# ^xڍa H{).RB,О9}c`R'5 -2jYrBlC݋nK0x}ZAߎpv{7 SWE#! CpР.@ \ _~WdG \ǥ/uJ@"nY ${IP8bߒМR:d\[1`ܦ59kٗvpӔm(XN0?f4Reu|&%tL L߮<6iU¾zoʶWuvB'8~nH܁\p0A+QDuW-=9U|pYzT0| ږpVS賍nB.;!>:PSnME=q4}Φ,z[xaТ*"w5{vp(ٻ/_]U#vU4∥b ػ B'|VvG S*JZ\fHsROHmF(ՍKoKq~㤪MݔhH.*81 )8F`Z R vnN++]4v&/#1/ $ /\BxN`eEԄk4̷o~;qrM4(-rWzף& 7ԃFBv}Np*M'=&.R[CtTj;E',փ7Jm B$2!\j̐5dz' c(-85 }aI,u!F|9/ CD]\]*HZ*LvBO4 A`C?%T"<12DR Fm| qO܁DG'8!ZDe=,iTIDѯB%( J[iQ!H"Q9.QDTU{[ޗrR " (*J}RcQ2PA4#5͛[ aG{"c+4ҙpq;>NJ {&hTg c;yu'iҮ$^{JUjұ.0m-6 P*̲+.>]jMH$`jf:ڲweDP5+Rcmхe k'Jtkx  nu#,9YS?˙ty7MFHP>@%GΔ,kL|חÆA= Eb )f慷D+!ݨ#~zsuK&@J'.)7<9SjuMnKlԚwp0obI]Ls{"OLB-Y)8)&nxw1qwOv _ {'.Г( Wbg6Po UMNaξEhp&Ov75\GX_?_)"oK*xU#YJƕH4DZ}?,~B>N>!&Qvl4nvb[zl;DzoT9Q|aCx W,[2̸/US,xDY{wɻTVu,)D&< q`O43'5Kz\HS)7\^n_\X{Z3Mg , 4{rxe) gF[yqT7R7DmP /Nӹ!}ὖ "Z(yA?&Q_- IhdIiߤ湞-30l_PK~V}]=setuptools/command/build_ext.pyko~FF@٤Tnq4\1S%-E{gf|vТ_*\i3;.ݾnZVYɣZi뺔uS-UٶDsc]y.|z}ڢ43)NHe5`\4wyiwBhWE.udo ?vRFp5de qpޥv`BSJ M2.ua:ֻ‚,F> X͍m'%$iNKGZQɢvk8bs^[֬<[]׍ ߢKVEW r@+#BwDy,K!XG0]w`,AH<мu>5;cBiVN8x3 #i*8e@fgl]#IP ^Yx/>~5_\!U+f9m,եr .6C J%oaΎ]^yΛCQ%M$?ֈ_@UȺ`_}mH E7,e[~g [66l&X5f&I`򗽎mD7@QL|ery#"CŜwxѴn?_XKop~꽚l"9Ԋ<;}1F64Iy+bP$J)TfQ\|CpR0##e7l|l:1%2>\UA͞v䓻咷mE>}{Se0?DN24-In!'" S٭CA c4xe_Oh3` >s$}fQ#`.p'Xh}BKd#BSQA1e%Ѕ-W I\D`kMD~#rUz}K]\ypHaBFR=dJ%8K} ԱNa[h|G瞗jVdx.y-T*x-[Hύ`ݕ%H<.g,Xu Dցy, 8J1;!?D/uQ D(KtF\.%!kĦ+y3繴"iK>Ii# _5a0JuGf'{t=V ҭZ4f)+ S3g>~)LMJABh. [xK;d@&$=t6G4w(%]Yʁoi dn1Xiդޫ0b(쉹p/U-EO~@.VB䒂p=r:gC㴧jaQ,+&! `S;McD(֮Wp *Df1$BzjeHll\WP=CR&o>T̓).r} )Yp]@/KT%\$:f%~8j#';Y!\P!Y BǗ^xLP6^ظ 4q?&(\lGJE0m8w.Gz,$*'vog#9J3omr ?m-"fͳ&j cPsMm9MAne@I&d<uqN[HLöȶ#@ nMYʇnǨ{"w3!*.٭[VGf5+j XꍍQ= WcLѥHPiY-%8z>*Lb{< eɁKH`_h^&$By0pQϙ-<"$M!_`"X*z'%r])тA5z2_/fgz C j IѩʄbU#4WKf , a`>4GSwӶy;zu,{9!"6 ʞOYTwu3lHsV)~0Iy"\WÔFrٵS]~폩aJykd o?{wA]<:Ls9ԧO7%T2qpLN$4jw9niЩN,~~)K&~*}n xp!K`U;:J!AA@E|r!, )yQ67bԖ~] e42`DW’cʀfsJ&eyFDSTjP=o4}F>/ "ۇa9t ~ 2kz6]YH_lâ? Kю|xDó(3z;:L;րU5,qBQEcբJ^TTYä b^,KigiJT©al*+Fk^BM҉kn'' r&ʚ:@1Y GXpbiseaUc!s$%? 1^"?DH<@3swteNlDPsPƕ`R +H}zbͷd ˒V9g1{YAL;5Gw+HɋAK%Ah9yu^ݤN􉝃ɖ;bJ[nq̐.d@ y^(_zZ5K9%V=uT:xA'ˢ|<֘p;u `&R!͹pc8a0 ːCg-$vx;WYoxxqSOP?e-$8Ksם<ۜ@E)ZǸDC=͝C*?^ь~t|m FXu :{H$ͅ6eeԕoJ izҏ}Fuٻ?g_"R7Ay9_LeH)$q)Y0yRت:AXD(Sr .*}JcvGe=uOK4CD!Xp[R[Oo.;!C621VOr{4U/䪛!ua?ִZ (?D9A~MۄmMQ;m+;綦ufGw~nB_YYKJoF89y$>zڞw-{/.b;wR~~U!_џؗ2X^3tjutخ=,8l\ɿ1uۺz¶W<0JjQפ~.y[GpD=}ѨtXu;L!aj6j72h|sgNĶ$6OMwr(Ds}>-P^m% 4.M(HO}QD"]cEG7^mzEJ@osϱ9}+ZoI~ն zcS:oxPm +N[b}(=PK~VL6:setuptools/command/build_py.py;koƑ+ڳ0܌:dㅵF:N3CMI\{&9EXhgQ]U]jv}]覩4ky%.v8fJZV*nh'Ž +pum{Yy{d\{QӮ>p]xZ玷lE5 5$6#n$}@TiB[Y;Y{pn\Z-W+o+qa`d*:;4=h_E.IJy] GeB-m|'NYtqqQ;G?wR#v^t}N?ҎpwKӂ/q%oR]T\).(e/ׅ(-\8.\s||_K٭HM-{f:ĵ`o?_(bJN„%wP~1~h .%Zo/$D'A.?=@cKI*N+t$VT]>F3fEiûE>~Dd`@˃P0f~*Y?^XƤ*Wc m9\QjV]w4sB WdI3@DKTn@: IU4ffeYwXAͬZ6#:#; trn4K&xÀ*JHa[!x=M]I:ۢpr[XAjr gB ݃| VV T3: <EkEn]W~/7WDc4pS& D#a׺sRKK]C7]?bבa0 #;͉+f CY+nwzK!4 gx6o[QL\SF7-(L,+{WB@j*]2 -ammB,W\fLTp`8rJqh)@"1(a bo4,&`R5Wl`&l MARR\ƶGf׸ oW:Y-WvfUfb5ޥb ,j!w5Pen:v%^5X`Kl%:Fo3&ҭ+Xb{bm81;0ب΃_eecʤXetphod$w;r%[ hE4fT v@\9>if#Rz8ul}HAhe17ֿnMRf%;W ~38:cgZJ1Ϗ3`S\!VJ`rQ8l-lE QU4 bfߊ-nonn3Y@F| 7;9<!s:>̱I|?zپN6uT3un{t茜yq(C|227:K]_1VL/Hxe{}xh6®cX6W 8Dtt%X:pYO'gUvZ’d_\Z$jؓ^Nq/`7v %.^YVNg @@\un/şAz)AT y$2%ฟéIY׵CZ#x;VOP2l>CdX>x؅e|H\,fXG^2%;1têI*?o6 w td;b#AzpL>bpCnR2--,! )mk~/kŒNǣI@ ^oޤ`n^ Nb{ep~&#Cd&Q'C t#nˈ0Bx|D}vuE9HO`DK0ohbwAk#Gˬ87@T<-fU Ι M N%i/""@4CMo9E7dDC11:UGw-;1Z:գ#'/'AC F-Q\z §QN^ X5d9ܽyn(w" CIXDAbN FRV3Qpqiꓸa-ŕF( ۳#g3Oj_^xh .lfaOw Lw qz-#WY2gS#YԚjqP]NDmmű2~]Eih*PX7Y/A0dŊD)0ϵ .]`)gBS~>T-r'_ j~,C_TP{H}]cXN7W-DbɾjEuâ GKYXO!FEѰd.rtdÌTObڟ?wR؍㳮Ai6X*Y\9  jra]ؓϷe\]l&{e)T/Z6FN{qdҦyW((!Q+v.xj4'R, %xhQ5wr`SCD^ȥ}Y4YzcAhz"LG+5ZN .1-$n깚s-I IÒo>shF J rgmc~nxyW&sjeYvmt/dM@y{<[m14<mM4F [#%31[7}NB^+rSw a}2c&^iW^^]*Zch0q|^רcԼ,Y /Ap4fO5J!{4A|l*Bm_bNAwɹG?} oe:_tsyncB N p޻ow;:ՙ;K#TxApAHkԘPN,w}K1W8xy;qMҼ)9la]+jahoew^>T "\ {pSo\VH1" GОP>{W ifP-'3\W6+h㥝ĩkJ6pjрnB$͕Clل@Ncxo <ǼP2!Zo?ޭ/!p}[(5tP@hji# 1tg?L^B\VK]QAaNG6Ӑ#poP}Ҷ]ǡfxkۏ7?Z0bG$ZiG抾UBk5GNhϺ*, So:?rE)JťSW-cVusSݔ#5P1$f.M-nHJl;zü.:idyT4Mc-f]:!ϨIYQYQfla;kZ|?I6* 5샼 윹'V2q:]jGݹPK~VcJ*Xsetuptools/command/develop.pyYY~hv iWɼ6 ń: h۲T_*^"%gu|uiRI˕5T߄!5ًI T6Γt8;BO_E#nܙPױ;-܋mͨ6WvAٜD&,{zfj{ԙh6;J=N D\~W3Y%,4LOAH/z C2xlIl'yF΢ec0 xc5`Sdcd#~?!Qb{U*bfzu |7OHOD cYY:?Gkhˊ1s'áܪPyY(SD%oQżD}Xwpq?|R#iހHew9%UРEń+{T#b4d*{8$SZE kN;NgHS1)d0 孛RQ&Wc]UAkczK:',wiped tl$6]5n,I uLgͺ5e@#:5;%?ae2c&;}w'iFB]Q=[R= 5~ȕed؉o&y( PblYԈ7mU,~n=g[Mf؅x[ɶ^S$G6k3ĭ?u/P'zO+ض ֩&)׬u"Ovw>9JcDjʾIF,H y&Ab eŷg7!n4-ۨm6Viܙo)Zig)_ȟ-մpЀIS3iRo,"˗<1tϬu"Q 0_-pi?3}m(kڄ"I D' LBPOX 黃@#`_NL2 *LPI ^z,`hR!@ҒH_yU:ˌSl蠑c*6T$D 5Ɣۭ҄_|M&t V1-IYajRF`%kOV@(¡O Z0keʙMM 5+^創UOOOdXk:r_z~?;Ι$gA`1 CCia^C*С B n4_0@ԖYLIPiٟKhp ƅ>,`j\h 3^B?MV.z+i"ٜȉ`nOSQ["A qOv\ttvUMWzdp+nXzKN+$T]wwQPkpb9]k%# 岫5x@Ӈb-%7~\Dה$>*;e⢶e-94  0]{kY "UXXE-Ghn}7AWuj[.\^O_^Aߵty2lFc~Wlve52LPyԅsGcyt]8$E'BR([{ajۏ?ڵ+-?b wMvdBn? ف˄(yEA}m~oX-1+!Vf,4:'Շj-9Urʳ WpPK~VQɶ+csetuptools/command/dist_info.pyWmo6_Ah,d ΀AR(:l$Q#^;JDI$=Hf ,aW:U.IF ](A+{Lհ9$j!̌ eԮѼjKQ4|OY})*قlRWVۃh#$KQ~֊ԉލ?]"1,׆Э>%ն tSk! bif٦Hw^w\afc-I9JP 1b J (`"g ,U4i{&*v?EiË,.A'õzOCj#yMQsݚ]^ܰǫ]߮>^ܰۛKi Q * H?"MC6fNd9yGf&MLET`Z?_]}/WIb!]7z.D^LWgfÞv|3~ċ7O'r\v"_YS:)kCwge""@Ք))! 4%O pP`4Jj%|m6KWW^Xj aN/n?]'6Xx:UFR52Ikѩ`u }XK6>לZ X3"(Si $.dGc`C:djId mHji/ug v¾D1]0oBƷpAJ!Z66:z[Ȑ M'f1 -s~Qr{pGn5w:m07ju?nc{5JvYrU_&_j1%dySn:sY60UYYS=M~[ٖ"+gznZ6~,7ɴo] Rkc> P-7mTfJ8yUl󢵽VŝR֢벼?HI3ܜ /xN7kUFOzf~U< E](SqBkxmH_$ wdg9`xgrYq ?et۬G5=7UQp%̦ ; m*,2@3|+5=4l˔''M8`ϒ=[⋼ˡ}\ÛacۺV iWrMOtG5Kgө^3*:A?/v0O؋nvxt]YB2 :H>n*/|>z@(2߮8y` e(/8*v۲–|y9eV]]L`\IfrVvm`vL|5o(9>N~e]vpGqZTzQmpd^4fFI?Q@?6ۂ~+ŦKJlO -0kZp7ES\M:6\so|m"#UQۼ#PuH!G{, aa_!< 3͙[J4ĝ]ʀ,Hˈij-5-5؄qi <%t0'5`ǁm-LY]n5 9Í]:m6mrW'6#[;&_;0'"Q&i[%)i`f0Rpi^~p;~YX?@vQLTvy]WCõhn|uɼGy_Q "n OnɶYKrx"M] ݝ.%&p[,%MB8pqyHt,&ek(6)s@LJ.p5-rP _$wG4^ɻpt3 Ana).\Ȭ&@O/Afax*f{{UJ ?e{l_̶CK𿆈3;!Q.x_޹Yŭji50HXQQr  Te; hrYJDkRUO@@m87xTV2\U};ێg v+]}qdy̯\0I[Zl!*!E6!~:'@\E,mfXlgfBVܕ]2Fp Ķ//m} ^/7MF yL?N;;~<;nȫu5׹Lq*n(7N5/}h$..ac@i~rO 3o r,sh+jm[-GPƘkdD2'$J53+uR"+ =5 1rM-?+>wF˜e'Mn'XȲ랼F }Jc`*xg}*:kՒ'C?450DS Qvom1F(oߐ&n@Z3ъ5Y׋-ّ@{cjB@Ra]|_UܴP^D=wt_ :؈gqR<8xd sۻ0{QU,y˳im(AM):_P̵xBu=Y!9@]N8<{X]?6g1`Fҡ8)n -X@v;#YXE .K^30@b1MWeT-3,,ꗫC4-r@6M4LryI٣@J# mUo8! ^4[VӉ)ֳbwdf589R3[ɒfgn*)3-CKG8U)eޥjŪmw ''eƁ`Ug1ɔt5M F2­}&f?!MêEo#1oPM? )256EN1J쑘 g˱tl)/\e75ź!%k\p:}[aOD?Zpa3;BZQb#FZkvT:T$L`yZß~pRDݵ&] D}૊A48߭7YOM_G?J4y44HIt4ە%g+'wՆ (83E7O[/VpϾ_ۓ=EFq,K$VynV8\L]c  { C䜖!/('n0Rşfbj{u]Dk@h:hctnePzQPLib};WE@uϽoU]tUϧπEϬ\K_%5y5c>bvmOEnU&#%$"SE(:}. +8(cmc4 g_%Soubw8 ]^hFAgK`KMF#o>@29EXqp210dQLHb@m_Jo0KEfO`4]$ 6^ I-wA$q |M^]Ȧ ӢHt3~0HB"vN30-CJ CN0, /q}@b7|Z aT@)(>2N1`CZ9a&.}儭M={P 8wJa"̺7`"IEWi-SZTi!} CħgasDe:}A.)jh~aӎ#B(m|z_s ̒jԼmOP?%gge("Eeʃ$ojOχTh$BE8d ݕrxћx|͟}x#Uh]zyg>ͶRm^r-B^/WUE%  bR&mxXvTV`F'(&ڬYƎ|Nl5y ZNY21`yP8;(^oj DKbr0O naf霫h:ףh'#ٳ)wu;Oodٕ<2CXoQjGrgi6%A;7lb3&%vH?0ݔY T ,F` df#)cK)~'ȴzbdKyІڣ tⲫC9 V+6 8M0*T,3̌?~V;P*x:kI+NZ_Ku3Etxظ>,6p)lr,lAbAW<ߐ*&;QhDS v- ԘC @s?S}= ո̵nh;SAbǜ$:18>hTKr6ͻ_~fOPGR4 sby酆b}՘;?dU8v{UI* #1Xr.# ^ABq}藊b V:4s| v`a;c sB9 zt?ʡ+_`j\?Fh:_Fet9n,~'}x$RtrRQ#ƀC:G pg dq\e宁d /0-gȹ8!^znժ_CSLksjW8eXXK&aUe@jYHW6M3t|X,bय़IC?n$01a,cn9j菰J4CѴ}[BQlVxi_ȞËMO~3Ufˢ1 ҥl43g @fFӇ?~||_4W l]Q盌!҅§GK&*175pnۚrTP/) }†D9Qg7[(M_C{8xr1y xޅXtД{M?{y]{Hid(4 }>u_$}1[f!_8_r߳A^''lida׏P/}~z d/)sre46XoɈǮ:,>j@yH}Inp X"Ǫ.Vzdf7Q2ٽcy]nie },X3~iS^V:{<^Pݠ@=Rʦx8":@fl-5f\]~2_r8j:2D߫n` ! %ETl2fpӊ/yܼ 9UexQ𦍹&bQũD'M ,x޵' #/ [η?|7ĩ~Q#Wu*6Eꁌ>ѥ>)dcV:FZŢV<]_/s;n<0іٴyDb涼t8~7tk=cQm5܇@ Y? nctA`ZsXF$\6~vC(XC5D)jxq:s r_/BT}>Pt0wHz GJw ҙpD(5Oіp:x]2;J]R&Y|(^ &{t/1]ޮzzZ~g?n[Uӻkmj1&!ρbݬnvmJ,kWEMܣɗ3ԇWƲs~^ǜ8Nk_B_tŰ^$cVy)"w(iGo0(+Z~ճh+WdO - $u'Q8,g0!icv ܿfnHM%+9{hղa\h9UNie4)ًR?`/G V3jONtV@Ɖ.^r?V'4T]7n8{m!EU4*.NT#qæ:iX9=0cUf rQh)B|Aс9Փ%.}1p _ [v9{ t?LDIQ>!D?tMnЮLtoH4VJzR5!dtu2!qfc%4b݋V$3܃j< Elߥ)귉"OљU1=8!zEݥ bίկ~Lh5RfTʜEWfQ0*4XJBku7J"мVwE[^VTX䊿WG)3n7 \:i}>K!r.Q4eju=CLw!͑/3rL@Xhka'IC31@Rk:;we=p%G4w|Fګ ~ Yut|ֲPOagڷ!knR:gW7zvI"=´ >Cu'{E~ 26+뼪gxٓ@cŠx bQEJ"9wMdQ'0J~M?cߢ:2rp< 5jSi%^o(F}>] 6]'3QTmr/3t1ᓊ9djcş󋎴?|[A5?GÓ?qrwý~bh'fd?O^K5$RUD̤GL*z{eYr+ wB݃ Fu,TRFڧX($j34:Y/"``!ݰ[=c D61`ÉؼLd\ޞ͠fAwؗBN yCd{~4*n %oȽM~Ӈ>^aApph-M9Ŀ~7;lvQ,J{PD??ͱ V+UvL P9lsk?n5A c}86T<40!`=cvtP؟7s{9**̒y ;> aqد9SEcBd8CjhT2v[V ~ Yc3K&" S$tZv޹~4 ɶ({ee|| |Er :UOp1}PJ)ktEј|c c4/i,D]A_&2Ҥynh,jG6i\m@t^YO@thp (r, ksM>zAU`FnfA>J8l;gT4ݔ *wZ4f3rr)l Pα* Qybˀ5Q~E/*y$0 |]ݐUӶknyb<&ڈ2ʾ0M[afOO06!m.v 0KNYߨOLv#_F 7:%>>n״m\ ( ĕ\}P.IrZW)YQ_O6s3*҃' w3zC'cR'FWKVm$=8}1a[ms)ImJ-~"ji*~Vx8+SVR[n|߫h՛ljhY(gy6sv Ȇ7sm ^ "wR^}"S |NIѱuɜVA!B@=yw"gH!Щ/M3[W̓sSlu =gTbCƠ}cbVn8-یϗh˻SAYDg$Qs"kI 3?<'gR)л!=S \qgoG}Mч+Yɐeݟ^< V*g[Qge&&mV/t,{9$_.f^̪0ԉ$yprmlE߫s0sX]v:& 8iu&vHIr])JOBfіޤ$tr4S$BD_39X@s ?NU7 ?+E~U^^V7{x )sb[s?M.)ʢb~‰夑þaB)d:[@XEY[s&pBUw=َtX<luFkX,7jܮܕ[JEmV9"Æ[*w6Y*]F-|BQԐkӍ#j50+Hnɒ G[8(xS{y:Vf{\^ ??̘^SګfZiM:r" :9KvYWΕ<4N&6ҧw6w D*"'Ӏ\֪]; F7W*ض!&ڭ'Ga LH|K  ON䔩ʸXIfFCqcULGe{*J:4ȩZѩ1AN3G mZ DvIL+R:OKD0y8u3Ir HTXn8~Ŀ @m4* =`Ov=jw=7.^ S9zqBJuWq H @9Q"o.#t ~pf ?~TEo,Oq%'EqW_֓e89 'C{0:q4;QAGtĽ#g@kpմb").tKήLziUsWHFՂajx! s#Va&o iKԑFOV~KxA*rBQԯ?ړ>[&U`SML:^ki*ʭ0w%= NMg QJ<9E"Ond@d$kT&A 2RޠɿJ&4x6&' = uYUDKBa79ȴ3,Z0'pZE.uH Ӽӕ%\gaeIl12ݤ,V@9Xd_U/o?bhs&Q.Ǖ]mH/]ԀsJ]խS^ȚGLQ==5WG\*\3M[ \e {ʁ- ѡ"sM6VO`T? œvLS-D\7 RF*!Xw*ҝ({)~D+&)+[KSs0:EC MU˱FXnY ,fLc ^ 8Ua@+ZN.Sp\9k#l=XoIuDefEKWJXv9ɗp{}x.9̪0(MbA>t Vڻrس+wuϕ d`셜w7Z' B 0G>rnXa-Dݟ7JbΞ=',ʇK6Z-%9썔w<ܚbEV^ot7X [A^`(quLE1^CkD}= zQN5h;|>$ f""AůI njl*#01'r_HSP}/Y"(vV1~f߾{}r1J;yZWP6 JYd*93`4pE6'7Jp_m"%NTS;oehpS:Ytqcܳyq|C}:oޞa8PL^?aw9sDWB1m~.keay,w9䅮.m\ԫ݈$^ d4&٧ʞƭfp), Pð|х8tD0z- ItQ=4=8)t,S;yU`+^<rSZTNc59$-<"f9 I x7Iw#7fبMŦ\PKt{&%n3qal^rޔ3iϭ}09(a?ͻمmOP#JvokWG:'i_GͿ ZodHg 8QrH@g`i1ͺ еmѦ~%6X9?80n%yYLys<}:B륨df͸۳/^gZiePc7:U9;dmvZѐtM'54I\!JjU~듯_~p0ȸLEX9/%:c}H E -iHzt(UO(-tg0/1CS7F}:}ҪF}mχ, St+̥"t(TUVɱ-0krM٨kmѰ#ॣvv鱓 3+|04B ?T`ep._8_Gtj"UG[oPlۛ- p<8&B -6r% Cի#xBTwNN xNiHwJ%hXI%GoTfZ6P w&pJĿ}g} EšͶ{өۍWn/VSgȄ q56a>+T)yT@6 YY灓1׏=L}؟]ОT3+`O?Oy0Ņɻ .ȝ;T_\A{!3( |-c6w@>jy7|bi~'m'YAK%X`.:H _'o_IɇɋށSsNEHFԵnQ8iz!$4rz뉩>:FB.Yf x]Ҳ‚ۿpe-%?PҨ$znU C_̝]fF0c~E+olas t&bf V RǾFFFΫpÜt3U-\>Q |;H0bB-}7mAlUX<)j(yBetYqϋ +SG-{xCSRbU]M >)}\ԍ&*6;Ba3S\cXPeT5fZ:S5֩B{mʽsL.F//lO(+Ekz1ߖ hש7I0ycւUpx;5{YcHJ4.ΪBO=_81WEhZ6MY焋C'_QT%dzq{&?_'fۇ_)*Z5EU;[ pe]V 汨JY0r3<Иan%vTj|$b72 z LЋ ~ %UTt(ӲQwJo'dpv'?˰~Z[LfM(dP|o'Q8فKH1X`B1: (>NS9rҼTƘ}8ysCA7/?gg-'&3ȵun/?$'Y? bה|X.WHu}<?s%N*ޘub{4858Ϲ|"yvA:/ʊpӣ V–zl"=84 /]& _X ܝ/gdc<  ՏN>IB;hT"$NiKNgn@竼mݕLڦ"RǗ"AdDi4*U:>k<˔B_+[B :2 yz/8"+1 Mw(-'P . қuu: ʃUVΠ*jֹHnN6_1KYy0 3c)y-Ę3{4O?䷾^Fx]o3%ԕT7ة[PEE%b;82΁L ,&m\XL̽k]nHؽDqevZK}G%e~pbCJ*L!54\X(Ū6sc*+FwN tBv,&_KђmcbR˴r˖94̔qUu@B(dG,zZ@򎵋u o "zHFQg7Re&C\}Ѹ]XdJ`Wt؟i%n 6j [!&Fv_=lF%VmxC*?c3TrNZ_d!uhT9GiTʻB "1SJ˫b|Ÿ?DBD3VQJ軴'Tuz>ۊ5Peb++ߥ]D /n~XE,5֠qrB˕Yh}`Umʪq퉗PH) bn(Oֻq*쑞덵D=xkUwȤT_h3 ]:GbN}rC%;(;jЄ|IVl;W' [jd5/cgdigCAt2{N|`Fka즸b6<ܐFM9k"N7`Vk`S.oW4Ok‹i]ݱ-ɱe;~>=~I ??Q )'M<4V2mͷ:9]n9eW/GunZ=qkV6L09.:;9# /v` 7$3̩N7e#?QV`4^&U.-n*]t6f=FfbK%;WXڛJxSB,Fu먗|V0Kb5l#Nd-Fd\MQV>*FE2w&ΔT)7ʰ5CդL>ݺ!cLS 3ͯ[ZP8T[6R(Ǻ9sLFvl֦y x+pzSHel'W*MrS9L>M1 /'i8xi'hQҤuB^>=O։c{Y}ը]֟6{E{EAfSnǍ5 (R.DR5SLEXpMj9: #(1%n%Pf{+v[UnY^=k4MNp끨ܙz85 ۖ^(X&NN=:A'̋m&d`qm[S KJLqE Oװb]8 O AUZz @lq\$%(>Oֲ{ G1wk`(ctYYr #5&|zۃΎ<~ݏ,/SzX2M?TPչjchrJ~VD ^N!+ԑ-fKyx?ύ'UdU>mkTo9^g8 aE٢ɰ29mU^ܵ Yq`Q- I/p庮EWRZ +ZTR*XGi)?<(^a d섴)WTuFZt4)k[f F^b .$z*U%c$YŐ7 ;)l jGrxFȢF *9*Z4cMc8[7~t+8Zyr".p0ӎoeW7s#l1FzН`a#"9 6_oU_$휟iV{Pwܖ]DEe!Ӄ}q#R} nF*d@NrVL0: .:͗H6=}ш-Z`{Q НSVQ?J$0L,+닜'Yf5,/W6y,nZ-Lz(xƢvXtH$ez8/;lZN0M6;;pd/mogc9˛wĤ7KX}9'ks%1HZ)ƽ5˙0lRDsa"byǾ [97+ڶ";S(Ħ׺آ1rE(ny[zTea0q;CS!&b˶᳧裊9z峧}0pPWŦ7bZm?G_~9W/W7ϖ_}/ѯ|ΝX<4vHر<ܔf|y7;yt}rks 8qgel0j?/A(h*$P& Ů׋pc,읠  eD)GmD3J ja>"t L>@LlXVUr jVZt@ &vv3 Ep%[MأJ yӱ7UE f[Am~9dxt ^.1\+&D#͛mGXYT+ˆKk 1""߫a~"WB\#o b#NIJ)) 2]&9|cSsW4JAv$]@0,/+s4u9.rW!hXMqK5xk:I>4[>䛿\&KPnDb -kYyxUU+**yq/pY8+*ϼD!O!"`~9tE?HݗF_emCw:Vqp2k˿D*j#9h&Le QcMpg;;J1N>6A6֭ '^@_c5`cs,N۫$5<9f̗J)EXHD'+9] KeY#]wgjyN,WWofC8Um[n%-G7WZ'/Fk;j&c@)A2==1=.✩0"FH~NvoRg+ 0)w ,nx@7ul0x9v --H.mCgcT) G@\sLj#qd[]2r2<ڡluDϦiq ND:}#Sb=pS?dV{$s#z})=9~ɠTќ+XMOӛn܀6պV2:?F6uUVfFN@M詌I 2`Ib ."_>ʗl> /Gڨ@i,(&>Z 4rV$!|>VujJiLƔx]!I!e+}tZOx=(cY/i4=äG8u끇ﮞe򧓷޼oN>h06/ط~x~289>-˅ToXRzQYkil Y47Nx'V+0=An]藽0,ONҺv175zPl|@b~GŽH10åK.4`:!Aiو|U ҃/*Ihɓ'8{KUÄ+SbngqI},s@ї:& q(D 1y;/T5ʋxzA'wA k@gK)lT)Kn}ȷ:>a:Q +y4Sz#vfeSo7{!,ã á0{X'o3'N} %a&Hgo3忾{ ?q-7p 8HFo\y~6l"Qڵ3N3qM%;*2蔕. 8/Ul:jF5e-Ɉ&7c,CQYA\ qGdå) K2kZSCF[X\Ң4.wRMKX^A'{45jf6?l@S7U-zh}-Vwx%uoXqӰ#j譜;YqȘ:x9u2Kԕ ,]t{gN;υkFkduhv9^TjɉP(.qd /18Kű ,qjcM#6Ef{ #EnU=#70N>4J LuX_dgmdcXUZN\ 6P7.g1BX(M%A ;#x#ND+}|{tqT}`I h"=٭ɁV (d/;)xb51 CU޻μ h@EtVUIv15B=vNi&w2M>Ա 6(3(6I= = *}݋R|nbDTLj ƨJ@#,+Z xNLGȴ*j9Tx\-M NhNx} ZL !F$.B{+\bČT~Qރ/:CNB (¡^&V"5VXZ尌q<ʺ@( !+P'8-šc"1 w\þWv{T .OGn_(*^haVp+9)<U-HCLrCcSD.|C(5b+Iv0&2nin)D~f\)xZa:0xZ-H֍7gտ/ nx!aTWpx,_w&" 3H_3.'L_$w6moeB2FͱR Y݇%*P 5Nd)zQۡv'&Lg).fzYZ] t[:85,q7+}^ #;&zLvƀE;70[o}8yLA?R;xV?;!G HV]o}:b]э蹛.Cݍ>{+(Qu%HWhbZwvٿۑwkX|>$9t|G8ʂmˮ69p^2A)Q˯R{ri_}wpaO&W0,fBlgT6JTkR[ nUyU}}Sp_-wF\7_cH|#rRLT,_?u6$`fYa]Zt0ޖY96C@4`%,> Ʊ3~'@:efdL2,%ٖq%-=h|AfqSז C0L59`ΊP)/sQ0گ3][TTEbk"&f݅UJLEv윔\i&/9ΊؒHE =#)=Z3%C"=G ic[jhR&,$oֿrCv5⛩D4bk<SK<y04I F}2'KK! Fӻj2>w|ċq)͠;rg{`naEWo'DžNN>a><7GI}ߝd_~Vli߮6}_RS&{=F 9@m^QmR:ͳI[DhGnsuSAz5^#ˬX$k .)|4Һ6!Ć|˘\➥<sz3ﶰ Ie~Ny ā"($b?z")D d0ݱ~7Q{C˶Ο$KtЛ:ir8cr/AW=װ>7 Eb4QrS,x/VY$5I7ӦQəBGq (c? =fTFȋWO?Cl:7߼yhIzsU.B/Q -E*7cuk;E O}@ko"L ІMN €`Ylz0I:HhUt1 DyqqX~SU=R,hGVd:k9[ZM1~].'0^U6"i%\4^n`֠1mĦqhd4biժ-y[0|:5Zliaà3ش5\Ca;{}Q} 5Z'`4#݀u(-7ZxծfKf` .6 vP;<F d0,)(M3O<}pyvĕ[KtKZ4&Eה 3lp*>_eY "  .5Lܖ)!\o ɃkZP ?"D<5EQ,DB- **AMET/7퀨C6A%D:1@<~{TGKC7%W=RK,LAGጄH9CLs+Jy+@1gUݓC_A  =Bpԛ{AM=A Vzcɜ\7v^gDZGz{:=xou'vd'u=C3YRʒkj \-?BjC,Lqo³Ya; ; 0KݨضuU a|rb}Y Fcc AeձSBA DLwE b*;.nyR(e7RN/qM^#i'‡<s xQ5br7Ew-T FwH?qynh.[VJ,j 6cهWkDNDM"1@b{0A02 0.ȸ6@W^=6@j 5 hxg#* ϮNNONF7ty!C$q2 ,T2{_ YJXYr1xFQ63o; s:+I\ [G21fsJj_b+qD#Tg'2 %' >ix>OƉEkj<2QKڥq*;T` ;Tc9nc"sWvg<봊(or^ǀ0,xٷg .2JFsUYFG I)M$Ԗg3]W|'_֌ilV4e-2]av (;id  )[" M/&Q1 <()4A:ٽ4,jq8S VFek5+(á"0)<ÕNal{Q.9ɀ 8{8lM)7e#'i4k3vzTň2q=!{/zֺ qD$w}CP=cCMQe_C`l_FX)j yМJoSk/;0[jt&8K:ES}19n4cٴE0S3АYXy;+<%y4z^d p?.V1޽7v~ʛp\ǣ>VVO;[ʮ?{Fހh r]n]Lt5;29Lt#^NLLQW$U:Vߧ`<MJi%ϖ1"q& (ǃfI8?Dq؝Nޗyzk4-oX4f -ʹzA$"n Veh:C]#`*mL&:exNR*2dd)* tҚ̰ ,Yg#8C, h3Lo;ٟlaMkLi=:.gkplSطkNuC#/dٸWYgzcg!Qb" d쵏yqh yo*t':C<ӧ~>({ԛBXR㈐/c9 Fu%P ;S e..7rsnbI)Ż|4 [^߾6p8U¦- s84mBFUkrmlj = Y͛N4GgdZE*?O V 8_rGxۃa7uzˡ;6f2 =/UljU:ÚmնUstxe٫mYyC7kCWbT2@ﹸFȌ1_@ ( rT3EdI0"\[/6nJ +2[=PSj9A@d>=w%EaT4PsC)Ak8||`)t?h eHob)# !B>4Pg6s2+^4''; |r' &XP?y 6Ew~$C?(Lr=cvqDI-> R)6 cr܀|F ٓ AGbZL*:v 8D'SۭEq"r:Q_C3m3LycJ]G JҸbu""%V*mX &HiidҥGM48 E>p ym㥍O{1Ր@b)ٍx᪽*I֬|1{)'U8_빟yU 3y/|]k⠰Gyݟ{{Xe,/4.AqۂÓë G/rǡ?ttrUV%݅;;9$XI"]¼#F^.`+e,X-<1k-&'xi1b\4; 8ctudtkXftu_"n )\mؘK ЩE:jZpy n3('i `F۴˃?zͫO!Rf hܒYGٓ91v'78̉p\1S7SBle~S$1S$!Uc?wgnt!tSf9 7nT=-Iu&[֤ĺK eJ\!Y7c: Tw6ttV}(נbk*!>5= r7QI`xްqUV #=$OIPSZH|UX8\zxO/Gi;C"glv%&}b>lĆX;oi^nٍ.0s/xӨ,_)ԿԎqr;1ʰKflxO%E89Pmܠ) MN& 8ϔ?̡r9(8y]%Hk?<}셶&cNTrcO*KZytH0 9 pބy(*@_^߰Lǖ}V4HL0¸3.yݎH_ edGz hLiGlxO sʽYvG_$'j:~25+@8$hl2/Q"gNL'P"}N;"&& q>lۼ}㇪w&F=V۵x7,6m۞IPϥ+e~\$+^4@r(o.bm> ?H /g7Kc0| uҲb MEB! 'ZϦgOStWL1Y̹Øx>}kS><+L}F(8"a /:݂5\H[f6nN(uCMW)꘰ViƜ/V"q-Ge0CxgFKeИKTcWIahYTZtb J:tL@DՁBkqZ󅋠A϶V\}8!03=eALbBB&s{Mz?݇I?F`2zkO1Qi<ʐ o_]SM"giFc_ r@'!lwx>L6? ;W&9cEVG;Qg٥rσrX]wf;`^_wtPޱ#Q>kD<HSc5  ~axȕ X ţ؋3Ѯv*AF ʠ8qMAmT9_>x/͐FāQ(>L[rI`ɭt2{E-Tp <(~S@d ?)3;vt)D).)8CAǎ}h>4Gx3x5>-^@jo||V >U 0 zsxf:_({up4wVmM^*PCjVБ5|$ſI]/ NS_ 6f:n2:l5<  2 ^xfRB/_gIJKSߜ3_F>./X165="1gi%rSgVSZ|h awCpCFR%FcÞX/h)'̾֒0 ѐ9ӌa"V\wNqSdNhĸ:Kr?Wwғ7Ak:14^ܹv"^f5JnۂN'r27Ged~l͜GAj W0ߪ,~)Ӧ\J`(W)29Du\&u^F"YP]#0'nף1Rm~,K5J|bۀHKNЄ<?o)/v*2hCέ#{?U%ߟ8=1:{Lo]q0 xw ^lSQڢȔnS~)p)/'OR'>?8y{v/Nw'W?$@g?||3-- dUwI2vJQhL57 b(Ccd:;HҞ#t(p3Ja4tyK ^]%vJ !/)J?#ʅZW("y1 t=[:R77隞ry8,,ir>:ӯ˒23@*  % \ɏC;."LrzvurMsztREV+bUH 1snL/*_γkb>X;7JF+F[.<?e^杢`[𴔋xz3mxpH|\ E&2@7ЯL-9c7T;6_o~`(7t1Cg%%"Xdg <:$[܈ b)zۙUc(G/s_m0{H$2{>T9sO<Č8+ؒ u " 焗1++脑-'?Wq57 C4s3uu$&If.Ey)]Ҕy)kKĵo7b.hm V]=+6I ط(Y˛ H?S⿟=o03ߦ.{>)o^ }}g@*- p?DHiǁ& %OeOA.ɱ>ȏO3agdVs{#%Wpw]f!q)e ʬ˘9(KpU4~XT;f#7 M3#Ұ\K $^W}r3r;Hpg!'Tw?7wo3D/|=1著lbdKKy>&["?{zE3ZYXWcp!=t1>mOk_UU ^b괨j+j M܇/Gѡ6K/.F>Պ> Bl'݂{YRq3.=7}9 O|gPܦSITyfXr [Fj|%DN O F f1m+ryA=Jf7ytjgo? mC= 8<4lHx3  @z=Ld’cϣKIx |s}ށF9ߛc4vo8]BXZ7~쫳_w_|z̓31vnu܂ 1CVF?oT5;v<%[7يiX.RJƶ{Z4ռP Ye(? $@7n)cqte:=Rb߳s9 8$i:' lΏH!@7J]?Y1$a@(Ig4:6)@Wf;AR T4^{ɝ IDd_z56āc{dAhyKb 橳 ֔lI*r aqX9aזkѠ3xQ! B2~&p&$uI|@fa? ġ;"v 1fNގp~݀"yWal^ d9PPY;ׂ`iBVx0%.w,SYd;Z tO=* KsjCGigzG37S|[w: {F;d Ǜl2 q1\MXmTI)IiIu랤ON["ITY 3Hge3AaRM<*&"V&ۢ=AYmPd:s0  ",M-B#g~췿| 1VDv*4oQ(CVV%PLX^E_ZQɤtЭn%vz;pDM/\f%GyCHF}k 2600e_'`etV:EY9*$KnKLC_"[LBdz`VԶ|'6動@Y ZUdwhԟOCig5Ð~+L# ]ԬSd HY bۘ=/M߄D^o;J$Čfr<[&}pV90Ymi$un. 9BcDIf/ ;mm !&O7j 蓛@-P2sޡ|w>_\իgDh DgߗUs$BöbX;7\{5#ة`R L<6 Y϶# ׵.`USDz#5.:P)N58ptKy|G(~kT׀&9 ۉpfK#+$w-.t:V-8VPwj^Tj7o/!CـN &ߧPb<9=Ir%Ǜ=O='a~<R9jSqN&0䍘jUXَ$XP'/1i0=D:oׯg{6Eg58q@vuH*ހA Ab%'b " Gcn6cC٨4.Lt0o:DP6a ?MfO(VPL+W0'-Lae/WM8e޲]"?4rp{S3~9 p}l. Hm[tx{hܣ&] ;[p?81"vwM9Ν}4M9ҾCHb( 3rQ^/ݎ-ǐ'>T =9UH1qMNhA3 ٱX̼C_3XCa-.A+zsPeiA'0IU[p۲J41-j *MpK xz\]QhEuD=–q&di Au+1+a@-"Y3%6g#؂eTN=, vŞKIGD+;*h{K2 ETs+VԲt"n-xX zcP7/Y}2F2&c:H @݌ "?mnp=β,UTA"wtq~/ب5pm5ytiu,J\C_&>l?p:`(G/0 ,A2rK&C: frF=?âd Oӑf-MЅtfNS]g\B"_*,8 ,|YWi`^_&dOtĆz#V9B?IY9_B45jo^%n/[Ί7gaC A+T^]N>v izlY90ԡ$Ȝm%mV7_|};PY]y1&ؘ::Q L1,\{#v8MrG*z\eb<±!42Iإ%vUjwUŻ6@ 􂴯MʖG$4 , dDn6C\wv'wFJx`$T2be~b6Uǎ?2:?-C,bS\l;@Z^:a kR8\=^,[Ldoki`;֢Cti{ZϷn=/5h7K*u:Q=с)8Qd%t!`W`WF2.ϊ@C;é̛ex6;'L}a;ᑔ;@A( F8:~c$K]W /Ƕؿv٘NC6ٻ=Kx8jT@/cW㒿c FDk`Y%>SDz|΀ wK,Ml3zz ԣ^1hGu OLwTR!oBPuBo7ɹ[FETĵYpìhΌ|}v@$1ZBi7'96llvΨcDHɠ)\݋tdz5׽f i$ ݬ7}^xT:*{od)P]V蛬x$oa9Pl){5V+1;4B_62SER7G6X88 Oge~5BMU^Ԏ 2xqH ۨYaa@Dfgb)p'݃h+"'NAwɬ4!~`ׄ长5NfnP,aɮE'#2 >by;GҿnkIэn?t4"8^m¬{!|^_aVgR(#W]1vNc#([H5BC"FF[b^5(et$PE5!+Rh!u8/s|E>r|*' L}z큺-P" _-(#Vy# Ԛ6XДVh8L?v͘! 8p7_KPLevVGro~u^z+Wwjj0E <=y~J5vOİoPg$Q'׵hubh#*LfCIތ8n]$tfီ*@ [dl^Z/Ci_kzK/58Qޡ4b!s8RTld{]ebUsI…AJi Y֜s4vH)4=)ۂQvM^@ʿiJ'X F |92X X /bLq:˚#zب5WӢ0p$?6s5N "ׅvTV l5:Ұ `Y H2 {1w"vrb6'DiȲbӏ yѓ8d([ HLa9;'%oCWAńbnG\ZJj_}`!HQ$>}t[W4f$W휵lVj<'˂uǨx 윘N>rX_`Bo%sE>3TGrls_sLm΅5ɜG1YGH-[O>\sIzv)N/8h>NS)P9:أ+TfHl)&v RUQoYvkw{7H 6M4˳#q9F8ݣG` B K6rodpm!J7; 3nFtf`nq.Y`Fx'E\[:{-ܿ r`=,3pڿ#xQV!tPyG=R}R3|l f9!28`:l&xK);'z̅qOS_ʇ=uj}z00TUT:4=M>hHƥnjdwMٝK)'(M홐p6zY]$sj$f/D-ꬂkq->FJKutL-gscwh[&=/LO?` nP+.tJfoP`m?a~䙼eF*[C 4!?G7tTeA\iYbM7u<Ge >*Yg< |$pwkT@b*z1b\ Bs|cւ 9*,0( @H$?ȥ쫇T/YSd#@Ār'S/q1[/3!Y6'iRucirLꔟR:oMg?Q@gf&g-k8g3an3]AұyGCõc6ً !mKISdv{2ʒ!R$uwU,Ex`g9"EGkb:uix}VW^̕1W4ث&fb?W j*t@͞[{F~7%^W9"#m [82YPt.%LKKGsq=e:gC vVicduKD\2ؚb' ~y U1}|$TBli9Wkw^nxLqڄ{4ڸ~"IЎK8:glXxb<M_oncmWٛsɏS*EKiטS2?Wҳ"ԝ$b#Nwv٩hy:~3Ƿ9fٿ KC$ΤzO:V$}:SțTZ>yk+2oeA(PWGC 5|z/Dk;F_է sB "'b}3m*aI>@fK\}t %‚̇.?u38L #?1jŽigH:}! {225 g <,v9?ҹz{AMih9\}h=f?Х}֋fļMqb,)^hOӨkaC}AGfӆ9= W^5TW>H}^A?PK~V setuptools/command/install.pyX[6}zdOmy&X i6hS4@%F"$5|ԍ=zD~!sKIj'  cL6h?ʬ]V"uwUpkSvT%WY bNjq˴Y WWNr*O(>;ވʈ;ͦU~n{#aN3UՙǯWǔ( `QҪ1KbV' &E`)Uv;{lS+LHkka7_ofowfie"\\ng |> &=\`MW<PD%T&T @9,j+L+h־cElpk?"󴂨s;Cڿ̗"X? c}-8ad cӰ9Xelfl(;ؾE>$-5;+GEL}sOC3[L 4P┴NCZ$P5g_[bĎFVξ"3EƲɄL$/xFKUR1 O%P,ۑ{ ğ̘Ia>uL?}5`s<{ag+~|߭* LJ̈*&=S=NJ[!yWBW"zÍiu~stuu^80a~~i] Q(ӑ:ë3:] 6mD|jȟ&NG! B,R#O1eIFMYی/IEϨĞ˰~7Ũ"֐KKKϥz1G^yA0|FH{J I@FWk"Rm&.Qs|qtڂ'lnS7=QRj:!a O*p! EnR2(C)rFs8tgV s P|.'m>Q4zƊDnػF̽/[Mh1ꘫq amcANq}& hL,AinOЏMughMQe1"Wß HB}%%)͍,!ƻhnƠ?q\N3엚R~Q6ٺXvO8"9>h-Š+(-hdkxd*R%wNIqiѡ+2J`JL KL뒂w%FyޑkV-%7p'9$iDJ"HQsprN1 `g;~#:7W#]dYLl%ܖptHcI? +_Bf#'X_!HcI ߤCⲠZyё-'Lj̥&-uq0g/R ngc9υ[6 XRw@DTa!;yۡY3Z`l~[eYG:?nߒ!oAcy!|CQXCSYr mLTKjDZMoy?=#XG y.Ma% NNQeMD)UTI8UJۂJ_cj. 8k /إZW |¨ijG4bv !+ص:ANH/ QH,- c/ϚͿW ɮnxXr EBڈ*^Q9!b*k@bĎ6Wg?,gH+4bw >0pu{&cҀ]&-h0\/MZPgQ xjHEԛ #=ˁIX.SqjO]9V߉HkB8¸RIFӿwpNb']xD9a?~daQ{̶B8̟>-$j7[ 9# epO 1?Zw|&U9{ޛ7硶J9?x:P]o49ڂWiѸ(9ܡzooNm-;t,#Q=(bi?nXD,QtQ;/۶)MN{h 88=ajcXlшЕV^ w,'-OL.!'^ 8G䏠Ml/ ö_E83"ƅVb4VsdP_!g;!)\5f TEUuP *Sg88 ^p!u" spM?s]f%'lȱ'E'شQ:u>ɶp3~Vx>ל=BYr6]IȨhW!ҏMP;C[u8 ZqqzY] 9.tjk?Rҋ4EZKӿDuO]v1\OON/k.eb dA,Y7ZpAtzS @.)i|tQulIpN^$i=Z p*Pi}&QUʁlIHn^Z6TN7V-җзtD 1cjULbsiB"ܓtMr{6h$\PˬwSf@91Q9zնq9l꣐}t7A> MzDz4o?o6ϢPK~V,,#!setuptools/command/install_lib.pyW[o6~  [EXX 逵b-\(R 9ԍt";W1m7be{np`Ҳ5J1ix;qTº iR7 WU.HYHg2maٔ[ܥtm>VclՒ,sx(fJ믽;j-n jf:Z(zUu<3G Q/D¢V>k\z' e%NW=oh0¶U%; w Q^:[\<+[);hϽ%4bJGUtV m;z 0* 6 uPCL٬{Տ 2@f_(ŋ-ñԾ@KJR1%/ {)Ƒ= i3{> 1 ՙPE2yRcΙ0l bIh@9_9R,"kƽ3b@hRԟfSlɨ1޲䒃~NUnem_85Cjt[R,g(0p{-N`&E'oPOI MJHm-(+TX>[HEWhȪXojb j%Vtvȹ~!)LNmyȥ>,U=lmZSf9Ã!ѣ'nTm-3Y -fndYc&%0B:RTy}8ɵ:$% -X0Ey@RBlW )a`ԜstK#kUPQS';e(%'+@PK~V3= %setuptools/command/install_scripts.pyVMo6WLeM7|s "@9Fk&)_JDC̼z֚U@:sE&jR7BieUjc9kS5v?fwF/wudGj9vŪQko5˲̧` FGxyh@&QT/aFD_Ja@;t3 +3xe+v!n-ZKhS>Nu:yD6U;Xuٯnb^1hu!a2nFߪb {j=G} b<պR(jo0} Ёm[iƪ#KD%%؝N5ۖzs@hǹhkLf!VFz*FTkeT𤡦f=0`Uh/ =HpwMG[N1]}ېOx2C~QhuqB狵SͲ*7߰bBRV+ 1壂d?Be8`8OVu74z9<\ ەЛr^ibqF{54-mc#/۟w(3%ql^) g`z[~.=T?۔UA[Zgb<$ !hw;Xp]/FnM6*<>Mq&,9??]ba3h"sLm5:C<5]R#-6 c]wZBǧfubwDR/PK~V!Psetuptools/command/rotate.pyUMo0 W KP uu] PljK$K(9Z_$'R9{8XѿS'%V~Kc,5vrx2k0&7q㓻dr3gbV2I΁5^zd1Zi>a4%Ȫs:2&ѫDWX݈^;kxòR9ܔ#[E)}iܮBUW (0ZC`Ea'-I䷙NSNZ@&TL`]g|jh)ktiOp^(>;?dc (v LRDXM9PNi.#΁D7;܍ayNM1N?Y/Yg|s\+ΩSIJc9)3;y6ty3糬m7}?RI,T7<9͡A ]QSmZ8|SFכ b>gƉ\Өic0)^QފP 'zg"QH,҇2z294Y3M~?;󱌱;YtkP;W ۰D*ǻ9T*C7q>%uK PK~V4Fsetuptools/command/saveopts.pyeMn0 ¥r9EU2!0UUu2a4Y!9:x D [+s/.>DPQzqxF:5f;TwB!t0bU"0 ubPM߲^|TnikH]2Rdx;eM]n&U{Y(i'@[Xc5ܕN[- Օ=WVPK~V$b _setuptools/command/sdist.pyXmoۺ_9($c֮X7Ȱ- ۀ.ShȢ@RI݋w!%Q;@`G:O^[𶲹Obh~vv/37i)u*Ig >콬Kƫmd% kRh&7J30YfydmMժm#lېr7Nj G^*t) z0 NqqY9Fgw2nv'-3m%[&$F:Ww'M@=*YkUŠ̿"ju!(\ȉ[F4\sT5G! 0cW`Sj!c7pЂ[u~'ANM$Y^ѬJUi-0e1t~,Z=mGro9|py'6,d ֐HOMI?)IJs2[8$0{ s~=]}i/u^}^aV%No}>8PN=/IR-f3wU͢ӏ̝xlS#MP]osDl7*DBž-z+,3wQ0?x`Lp˾BcIN8iu|6D;Q`^~۶_3u#S{KѤQ[:r^!I4{(RByzZMaX=!dɗHyNq_x3l& 1#2qDB'ҥkqm<;EyyVPi e$:@ a6W%MS/`gO0@J@gp{`/kU4tKŏ$Rҏc՛IgiwiZ6&W~9 E1S{2z[*H̝]OH<EZhzqq8ZW[QO O*ʡOՔ:P$?;Cuc"0W[*IKc&U˲+$IJMueN5Ah]Bq! 94Z}ؒYO3m[L&GuIƞ8pll@\ΓWoG`)TZѺ G@ut>q=ӸoD9?鶉\趝9#٠^>G|`uLL98Cu..bp^H /@Ж*'['Z?p*0&D$ R XYL?Rf|渻55>Zzd :Ãoxe0~5f8Z݀$gj!X30j,*\!z{Y0Y[-c-:jm) !Y[R`cՖ"oxtu[Ʀ;Y0T v P5mϧd^py6 ;% NPQ`%l%9W3 s^kq)¿^ÓM!>1]qsixp"ޖt#՛1&  Ez)r u+GOS중a0<YXjQ3u: ro LNkiG Y1-/7c_ÜmʈO&KKG!l*hǡT $0c#kQ nD7])ԏp8\|Wpu[UoxZIU~T`wm2BgH zMVdER[`73%]|@BQ.6b,XIJg^[V7Y~߉w;9`zg~$3<\3ӀuKC[Q H`X7NM).i*S0! sv3f죬v /hT^Z$]1X^/o-@n!F]1QJ:D]R!Aѽ'- h8ddyp?fU!0 hAÏ9\ :`wtԯC9% C3V?yaق=zz;.߾5*?el%;u6:x445h)s-QN[nTďwT/G4{AG\$Y"]nk *aο,49SNm|wlݤ^خ.`YQ)_PK~V. nsetuptools/command/setopt.pyXKo8W Ib@v!dZln)DZJr$|̓Q*5r^ 7}'RtCᴪ9+Rwǫ 'x~ŕYs:E?TH&Vڂd]75 m*i]9ّXkY!1+w)J &_V%|mꮠuެ\Q2A6tzvlRِcgb;#h`OQ827$P;/ݎJ')*>+cjFAtU呡#-ddoa3K^/ęDPy=y/Jr(Bow)-3BnHh+Tj60#X;pQ( jȍt@ oqhAXJ)[9Ns6JO f&0%]ZBNmt@ӐP~>{zN&LwǪ hմ9<ސIDZʠH4Hpe|`Lisr`@n]=ɽ[{ag7ܵMXnD; 2(>ka+3P*GJQ!iHhӲ|T8us7#?xMYl-fH`g(̺͢cPYVQysfa=;͌h3HϕXsù[t~pk8W{]AgSOyK~[m}QL e``%] ä``L5ϸMcּ 7ջ׶Bp v"/#o&<5q|@/v6CDuB $~]EWlCQxUGx3.֪>yQTz$XU'o3"}~]׫oɝ>c>%RlK\qiՄ]g,;+g/Npo ٶsS<,TFi F[Sdaڹ=ؽ _g- kZ@'oڷܲμ|PK~VO\ setuptools/command/test.py]6ݿ 8Mz KmEa,ZʢJR~ )eo t2U2eҿBstۜR5V|+ m#]#.vZX%M.V0mWCZo7>WB/܁a_haTK1 rQJtU|eE[{TA6đ8p <:^U/qIRPL($BSbjˊQ&!3ާIޞL9 RJ ]b%yODy3 6b/EsL!3UZg\&БeӝǸ,xbxt+2Z4)gWB()ҟNS;ƞJ5 ^- @hi` " ZhiO3Ss|,p-LjLV s'r=ZA{a}~`jԊ8,Xac(n\bfN:RJ1Y_/`8]CuL|e[Rm' ãgĔZR} .h(AoUtFB'i^:K['X5S ̭(NzeLDp$nh2k"V3Tr-GD!%r=ĐFз[D FD)|, B`WF!P >W䲧~*EY$d ݞҰ]ҧz{DΑ-dl\6Pp0 ÍmilsCܟ!?If~n,`b,K`AklO^5$MLA^(`/E;a'1l^4f|^spߊ$"ʒN噘}Ct cG]d :8 )nK0&+Rwӥܜjj0509teIu RX Qu ZsC44ʏb\V}˗QE9TiΤ}7u3/ /a oexUML5(,1mQlfRA3wস&r Pl=~8xrl `OjURdXJ# p n2S j7~4AL 퉕Qu;|wX#8ۮ!4ӳs[7>d3Ab5A|K<B#7u%QH v\~oWW^no?F9+m< I_l^/_}4pT/d_CܤE@& (g7|KGWm.t!'<ҭkxBd{cDPK~V"]Ksetuptools/command/upload.pymMn bEGꪚJ](hp2#~Zdv2gY&XO!L*r k{G3) *3;b>v ꗍq50$Q1rA9W ü@!>֫SMk\deH!;qzlq_7܁v9>ɐ? q1a)[J>vmo$*n~% ;jR*(?b{ \s(2?)a?PK~V"(j !setuptools/command/upload_docs.pyYmoܸԬqŹImb,ˋVTI*Φ _$R+AQ0Vp^y!$IւVJj6ض5۲F+B)WӼV$ RU)[T$ :dh1S\3E0I6=rC"%I2ؒU1VHMTVճc֔b5rLJ!{ކw-v3&Dik^36zVN{Y)ۢ9ǿd]URs@Q,",xLӊj)nlxswtךNY+Yi%b]i6UlMֵOf$ iׇN$UjTI[ R$Â_!b_ӮְG+B7IcU]겊v b)F2ܭ:b$>=;͇wW],H>W'NoDSyc3Y1X i$@*ʥ0Hd^_f/П2$4]ɹ8G``oaF)$I[@|"X@Yɴ;V\.Dh3,6WJF΂тĚlZvÛobڡ(%rf1t _DirlHѵt()0[HW+@ދ̭Pz7\sZ`l]_֛eM rȚ7 e} T r  XFS0L4+|iu{* 'ޞ.[SWQx0P:2H C ڒ@X5~KИ8 U2"Y-HL,2Rb_Qc<Ӧ_AGMG0KfK `A!G[$ p e/l6?:CCJplĸH!RV=[Zٓ 9J Ş1aX2R9)0I#kpz;'B$PȟX:=;ߣig#ZNkj_1H BVF̋VSmqfŭ1#CKF@na+kX\v͸7&lğWdS$q1.l³b-FbM7(SD.l@Eޤ_Ȩ Z|VX"t83%\0 $-teD'F(%"^2E1B얪f/GGO/sr,c7J놣uW>O3| 0VK}qg3D-x c g$ͮ$CLʟOU(M܎PMTD8 3]YG܋h/wM=r%c^E禿*<`FT}-m# sػ]pR "+;c#rm~n&>iNN @g l4Op妈K3 +(WJQ^OuJisf-:xEߠ{ G{\v#x#gahXa@'=Q||n>7Ċr*+kklLp{vRSq7ӝa5o]wp#oid?-az}vy2ʶ!8~;@;))ѣG޶*W~T~n2/`K)L*K+*O=US5E5>8o9&jG2:=2_uCs>,R.Hnwƹ︦hKXF7gJ,1OS*0׸wPK~Vh0setuptools/config/__init__.pyTM0 WXU 6mN MݡZq۝4yvgq|JQ: 'm'HJ+RߝPTP;FE>U'UEqpYj;Nt~SY.>E1oC߅K eaGV"Dc6 t ,nhJyDQ>On|*+B>;eHb |&7T$n"~ow GݵpO:dֿrΰU[a %} fqաVO<<Z{9y db<9+ȶ:/܈OPl+ZE L:!bTa YxXJskeq,BA\]c`{U~v|FBqd0$$U;|7 A40s-DhJ㤂r7ezR:- Q}QD+2 ݏ$scpB#Yr3R Dz8;+:l!O2mV@s"+ a&tRh.UJ ω{T+'Q3s[pUq=@Y889V|3=8Ussiw4ڲ/gO*K$za &IPK~V}5-5)setuptools/config/_apply_pyprojecttoml.pykoF~!iT;%+-Ahr%3HuIofC{(@bkwvvv3rg*¬H23[.=/9fW,ʳebaզ`qR"h) ּ Y~ 0>kñPpchpÌi̞(pYQ`j퐕\%I d8`l(Y&ba:],22xQ{'xIկ xsdoOj7I”UE,ҭ?pg7(YVp4 ąGLMB`:LR1[ x@BHQ#g7(0qq !L29Wn7hq+& rJ/]]}γ]iާ|.@lHj , Ƚ}9loh3M K<"MWAd GDa}덌 7,{E闑f-w_ULk4y6,` qf>~3C{Fܕ$9{_2"PGIvuze8#  ]@@l#G.?B̢ؕ(sns'A5cp?X&`ɿy `,4xV`4ZX2oX*&1e\4~HOUp]i:|n''q. f%I4d''D ̝F,YQn&'wߴZ r $)*\J+H?ztnd?sjIP(Ơ  BXHM-HS Je`@Fo[7#^sF/0EY@ y < ƚ0(waEJ+Tpڶ q@]_@s\vJY4e/Ψ}[NXKB:Wi|Ej "6ۮap}}F;R, h7_ "9R.@3ߤa]xQȖwC0yU,ĈaYJu1Dޑqrq[U CX&ِd-.=Bq@~kemhH/nia'ec8Yl8V9Qf?q%fEQtV]a0' pYp7Z9&;AfRvD8zgIsz."TC+R={{yGpw mZ) =2uA hHV¤WD\93mvt"C9r^D"!!0%S>ʇjl)ő@/  PI H sRDS&+/^}vDBme Yl&g i$3GQvcRnxwQ֞a2!  `.k$V& 0]˸$9j1E;Vr!BDG r=D|nI]Zۧ]GIJsþ 7vӨ-&Bd2&j~Rke^bDwdLr\3PA,v%̡)D)F̏Uz  -lpӸeH6=p:`=n09"w.%gr $@$6B Cm:DZ|$:)^\\-ޮm*EAR3QwE#P-€{s("99'H u U#/s(9e,pP!$mv6*U[2i9嫋9Q}Naam\=H7@C݃: }?XtdD5oNF=s1Pp},!{qJCc:SE$'3u)EU>n‹K7gP'n_Wq0ܑW R q^L{kDaE{LHHm` 6(uՋ+Zc:n +c~A 4„Vqq p<\ؚȬبSSvA ႰCټQ}8ڽ;!f[ jȴSGWI V($)Yi_Q=vr &iΉ+@ft߽}~Ⱦ;dz!f?*u7"GEw-`i{ KG I)j1 :TN'?#k= |E)n4M=>=ҡ567l]mRZ5;ۤ缮潎w7C ܃n! 9qqX@L\6.a^wACFmt8<:L rs'+w'2|=`Lߓ}7% D&J,_vϸ~!A&y{9_}@eAi?hlnSr@#+*_Z`FpѸP&K0L4PK~VmΤ?setuptools/config/expand.py;kƵ+ {eBvI[.pU ׼H_3gks8髺Oj?4e_Q}hvl}u3t>P0^vuY/AYbVV]cl6Uh`2[][jj`ڱ}܌W5Z2>lV :Od%uh-8lB,g9:t+ֲ}tdCX,qkgI_5=4 z ~4|؉n;vl9 R߁ j@Gnwќ| B##F&+}c3ԃPR("wZS0E 6:2[Vl MUX~iNG3hC7@wS#b!R~Xpa~y AUoX!5KGFOYib$0*M"Nӿ1 EN!4Qh%I]1EKojآM YlϼiIy&LkGC%b)AYXptݶ};m0-iT;1- CKY)&Dfr6[TʮX$)d|9}`aN6sD"xXY00Y Ѥs\?֋yd@?O(MLE'H㘒V1~}efeF "[}qud`EPu ބNM@ OYPI0ظ1C4~v ɼ2;Gzb:vo~i/@qaK. vl$x=<n}`Z1^¸(S% ~Vb2]z)ۺԪaN K9V oX>-ωr %#Ffܢ/BR4 Gs /YEl` iXd%1?G" ˪a0kf2V(,yN|~F@ -Ɉ16 ],Rd#G>)i tUmvSm./7twA.u0^ w^RFi / UY<&76+RhpqT/k=Qo_dNIL!dγ[lt!A"9zP 4t`Ge-= # eYE6o2bLmN \m젪&:nQ&:)&MΨGrs\93 H@M/m$y0M9b0`BQIi gR= @_ iJ7hɴddm6_4 cvĶh\ W8/MPmbnk؄ XHw(i{+j tYi{Sm(j N?$3p()Prd eE Ff?exC<azV|ďw7':? r? |=6-@D ^=䷳Z510E`%.ŵפ@Ѕ)2Djc MbVxL wQ\E$\$.cx yIG,/Ϻ{(TW qMuꢫ# *A񅳺搯gnX^72H:W5JQ1te ثZ J2O00EiG78+6"vsde%E!`[}[c#N?ȳfD3PvU HbQW|MJn:`Ro#X8Or! RU`7HI]!rE&O*e.ʖ'7Ewo@uǡ8ը4 g:<ϤT4`L*e61mt!34oFѺYoH\bW@o+>LrA綟󠈖U0mU4y5xxyZycUGJSC]t%~H6ɸE$. ")MAd$'K1G 8K؟g#QnhθW`X L^Z:ߒrvu49^|fvX$b͢x,=8~d?v%§fI^%]+h7`SFGuҏ#kT+cga]b!j6V)vtݵW UӯP4Γ1"`'JAii:&ז*)pGiR{]^κ俉Twb[&n Ca< ?H,qnBۏLnBFz'#s-+,$_\u~1 8ub8pz>_E "= u|h9<"^_v X/>/0x-GBOm69v>V6\ACMTlÔt]w&(GcI;¸)ĵ/422oy iHt E`S~M҃ŗr5~M;tӤgIңpE^H*`u^gQ_?qabP͂T<] ^x(RMG _gsPTEEӛ=M oCoVp6#6+lMd~9_ 2k>+';nb|wRSߝ9iGIV B%V<[@h޹%=yZz%숁6:0{|4 9,T!ܥvUÁ;&ĮQj(^>e~֡ɏS0 `I Yy<0|g^,֌f O`K-Od6])SrA3,j,_t^8IϑDLCr9e&AwITx{W: oTR!CbJ7~7@ *s4RU° [+q9'e'ׄh}pDrCb(kpoLꪱ"%Q%Ym8o XŹ]2K DhY x&z̟*7aW6RM!_'ٗ6H3KR f/PoD|$RO^aTCf.~7Ļv\J׉e +? ]!.C<1C*_o*Cǖ/|LKi;|WzmSC\|撗ڌp0yhl0wX6jL8cjE3S0w2An4D0{Buc0@"W%z9 x|z 'OPK~Vk!C"setuptools/config/pyprojecttoml.py\[oȱ~ׯ<H4A3gI1b5(jYP$&m8頻-ٳDvWWbs(|5뛮K}uE]M[r웶ϻwr6EE2~}og_?~`o.޳ 5ԭK:VYzrVW\0Pue3Fk,(IeF[Tm5bqrlIޞ'4dL![Ů)99ŌYŲRԬkZ\u[N[f:~r_t[e  ES+`˟rL"2,U+[܇Y v&3uG]e6}+)A]Wd^Su׋۬,UgȻ58z>7欜K׿Vk" & oۺ5=~Y[E3yG CUk ʯ~ JQI/g:?/o ^ݢdRl|-& >Do.DIX%fIzYJ9^È8of5jKHnyiZe;dS4iƂ) 1\ـi> A`iBɄWfU4E%I?-D,Ah.+ p=ر f, Xt6jVaȅ fFsi^fBhF$>*}۫?]h㈦hJ@Ld]C 4 M,^TϋUQݞ đesVs?RXjHYHF Ӱ!HdE2P:y9~ߴG*.Xx6(>,#HԠL u4=%XO,RۏRmMY^&?z |@.'Igp) vU B$]rɚX9=2^C.)J1aUAQ䘟6GĊ 4DDHn*]S]qW2y2(ͤW>'/JIkک لҧ$Y Ind`A@#fze#K 6mUC mhFKH*G=3EF%LUųY  Z028趸P(ZLrs5FA2țH]< 3t5wDy XHc{p``WY_H7yk@0" kj́ A2҈ǑMVGJ3 bmoaEoVE]"yr5x%Tn_nZ@Hx'SoCe-GXdJbsTڗkXBZE*75ox+XDgDQkH֔d).+m%)T0N9)vȦ9|AQC+3:״ρoI7vBYU9D!S5HSLN"b&]BC9E=>0]huI`&N3,/PጳՌS ;ęĤx.Wtu|BTȴ !h5\W>/CHc]=.PWCa6@RH@ׄ.V !T%0T ! w3(oT VyٯgKJfLb_a[aA(u3QCEUr[50eXG9o efF9"trŒ4.5ZPhP0Q7, Sm;-h8 X+rsEKBK) P[S% ٸ|U~o\Kk׮!?N*sjJ^tVWH=e&\jyI9Cud} h}&)GV#ph ?BL==R=JE꺃:u;BAȑ4pSjkfǷ.3 v*'.4%k)m:er$ȿ*lgn26Ԇ=yg *J`9 @ș^nVDgOxHn%~5ݐGg=ź@ \*WN<vqG_=wZa!:]]|.AdEnHol>u䤏_)f3l ׻_8tML}/7XjZ BQNxxI(NG4Tu^ :cŵGP E'@}̈BHa@*tٟ.:%-r 6&а @&V=$n{m薂hrc&1#MMlW~Le!ݝCJ):\QS->>fQ9&dbTԟ€${M+6U<8bD7`;eK,"& (rV_4( t_7LO!ֆjXb,Z/jzfYλ 3_g,QAm&"雜615RƛxzxNͱCpTEO%o'qKF82d'r`5=#_n130a+`]LjȈK9Ѕ84u+ͧ\/k;Eiq0 J'pc u4d' a`@( ~m[j~Odž=Ϗԓ0`?AꜨb,`8L$|֍xaV n;gS#^'e@nLB>{hDs.l<KL;l|2@v{"of|/nӣ[kp(W{*Yub˴^!PYS.,@6Pqʮ7ȴ<&9Ol)Aධq~$l#SUP`tn Qaz͘qrhJM~IN}u(9訋die\yF;OqnvOtfjSKL4y!d[Ze bK%[H.y0{P]qKឭ8G; J 㩄|")jŚm=qkZb`wdfƦUt|h my?-5e`EOT< m!5\bx_yp?sw+p>}`&OS[HD5X le,kvWd(@ 1#RhqXuAROτڶO؏n2:GbTۃƔU5\~'ےCf}tKG᳈F&Zyg̃`?!t-L#Ꚛvf[41Vԣ6ѹ~h)}jh i`fphTSVU^YH6tmP5{Ǯcl>: q(Àの"D0:z `W'MB)J'1kEf=# UwtBt5iPQ=kyuP z*udMrYxV R 7g9"lRp!+qdhݔ@|DGn(~Ȟ7r&CF+{2 SBo{} bi&&!no/>aup|~umi׆ b`xv$Q%zԴ܋7k;=8zVw&IyFB㝪=+:\NPgs\7i`m,|aӡg7yOv'! sj}7EU#Fί <;)D4! ~ڸ\l`}р Q3ÐuDEIG % '``IobvxVYek9v0Պ_?u=wO&sǯw;Ǡ դ5G2剎DyK>>+Pmu\2^jRd9A0f97BQA XLafc H B/CʱgȚAO߫:}pWV*Px@n0#dM'e9>c.5nmw ˯?+XbtT0<+Nux |DPK~VwHfsetuptools/config/setupcfg.py=ks6+MDM)Ǧ(ޱLm&3$Һ(l)RˇmHq{u0H x<}WƩenk:;oBr#V+z&J\hѻ:sq&&NVUHb6m%>>0ζ*!Fldqp:Ke%:+GYi6Yyg϶m3~6 G_6NdWYYĮlE"R4Nl DU'ߍ_|+5`Q]dZ1(ț&u[$QGͤs[ *͛4Knc:0hUUGF>~yw^͌<>%:*vf߾ndյ.U[\FR=nu)Ǐ#jʪ*+'d&Q8a&.e0UVFk^@g T*γLX?ª2֙5 go+? EwzuC=[`_ݟ꣛ʛm\QvbN)ůx ߫Eo$ D?Q׹S${{ww1؂b -f $!>.0\*73D ͑Ich>1Ƨ(d7F$C\K~,^I' 'Rģi #sBA݁52;zr<Ӹ@U$X4'bw:]_kUKP2_:H)-٤M(H*S 5-}@5!w# v=fRhsG!; x^KoFVHyNL*sC90w^φIE"zbM. ,h0TAbLth!tL-Q5 V3g Gr@ۃVc~H9 9cj 0Sa=Xt=ݼսs⧭[dP&א$M Ҳ-.}55!idșcއa})~_8¡;tWS5e7e*"+∦dBܑ3tLV/5#P RYVI k!hEtb<@+X8u40 y1\m@ʶ~CC`Q Xxfl͜S]7nH3%[AS;_6 Nހ~J򸮅jk1ioAm6"GHR҃:1)5+# Ѝk֑#-ku&Jx523!1oD9{RwN쫶<4uYcY3I_eCqD./E35 尒.N*ZYꯀLIYE07( .>hn} X8-ymdgA}@#́ ;lKT4]z>7!0`nwm;]xYK9 n$4YE74Kc ]8wKP'B I}\'y=;~S7p_yr)jĈa WenѺ 8C4] 6>+Zء]m0 'әEƠ&~V4[ø<^DSBX`2a5 5m)guN>R)KOFD(o8(=]C8[PElYkt֓.y">TqQȂyqθT@jarAtViT 9tG!߅T1ig vw,H]̘%,okpʉUSG\oXLQN)̦U#(NsBeα7Er5om0ܩ-`*1ެJ沢/\sJje` 3ة@#^2FaH@ ńw{¥e;BeqNY*jB-.C 3%Rv1V$O 1YLRkjR) f~$2@a_ː[8{ k9+bN.X,p\-{Q-w 36Dc?{l=,&EY u:/i[q̂ɋ hզj%zrPTm*iuVJ=!&mA1shۘe;TsF.6nrVKAfKĦl%Obr{=U;5n2DBc(vO|!PK>NYQ&o!P)3B4K#n1Aap8LBg`6\/tC^=e$|qZvT]2eUT'|QUyMQK#o:EIٕBkŁ\-ddBndžh!8:+o0 6,& S \";x2 J(pvgpr9c4%񛓰W}wo 7@ƺJaiz٧_tGa~V5rG*sZ#.1#]~@P5 Vy@s"P6Z=R1Tʥ#mӞh숆+1r)K@uDt/Ҿ6_^3tn#3p{Y >.6, djo1Tnۜp⪘گ-be 벺4`-P3[6A#V2H̬ܾ݃v܄!Q`}"XQC qAj}]AKᇶf*+M[C[nl*mPj"LP/G#]+.dKYA6vG3VTTEhFX퓓*CyaMTw f!eNCɱ疽vн@qg>;e}Mu܆G!d_|RiXfV¿'_ twZT怑*CP 6I}zy%Z!1eI7%F7^O s 'v8$Ō =DӪ3fOћ7?{[#j!C̔\JxrcZA᪜qXDY6Ƈ\W\Bo=O]WQ:E2}0.&5383Er2'Nv7`pUeĩv ;Fݯn:T6u;UsHqlLs+Q eSuT\`=ԊdVxFD-ee-𜀏ׯL]4ܙ tv᫤L)]tn1|7`̸%/._:nIo{[P^+52 geS:bww~eO{uz2 elD< xNA %*0ugF#1> c9Jkn# VdX؟A`E?ċ3aj*6Bȗ)_>_塰<w϶רJT' 'A =Ph u v0IJl!!Dwft𶑟[ɇW۪~C@nu"^H7\tVe& F+pAu]ۙ'3.q.Gcw}m:,17r_T=j]c2_E?If;e+='}0ӷTxܣH[n n uUG~뉂 AUTtg/7q|wN|s`w^ùlվgDsfnhٌg4ہ@̠\oycjyV>K׉< @ƻ/2{خL)bE >ϳG: y8 [w| o=.E*WDx WM8wṥÈkPr>uP47mf@ʂF) %ph i8< Fn@F .>v!Bܦ;ko{dl3u)f{XŏMZmr+FuCr5$ѵ"F`@{Kq{|]H `Ur-.,6, EŴEOcX̓ \FEus~ǧm6ƷٲvgXw|pdp އƜ~LpYvѶan]eA|Z}ͤx+` fuseUQl,Wxs-ׄ?j3Džm{ɑ*b9gsȢFh瓮N!ԜZ^58%U[>c0mGeFNlޙpD;«RW5[xv]kְ7!t rx(^HcǮ {n#Ejhޮ 8w}>>^rPzX tK SKT9F|_+yB9EXFUQ&K_Ufor:`Cep ja] Ѕ(xzWC.&Y;w\w7N4h 9 DDWͯ8ZqmÓvj04_/]/KY&b{1c7&޸(,q'fO=6Wxy[zt[:lvҷ wPT*{ǻݙBe>Y vQ;1xnPRW%LE_ѿM)zr}E{,R |ʤ&%ԉ.&Ϩ73:,L[!sܱ|*%'Zw*[Rf!Uo}kp2Ap=-LdG~vK)q; +crSQEY\3"tXHf/K{?PK~V[1setuptools/config/_validate_pyproject/__init__.pymSj0|W,~AZ0m -\9AQubKAs-l+8CbfgW5 TޘځjzX*{D ^ԵAIyR(Zk,ouJBXw DJѳOo?ٯkߊOi1_J8(Q?%Έ.;p1-H2Z Z{Ax)^ָҤH>`B˴Fi Ƨ8x:_JbUǯ9~}" A29t1~Of/`}\8M~;x~֘J1* 5]y&,BZRtdB.H8H3^3㕍*Tx X Ar'kcl%D~9qud18_|#GN&2/|,_>.5W[w`wB@E"f@k%-"ؽn>"]/ &eD  斕ĺP†-\Ŷ"L -#`Z=Y"u C%{7p"6v$n&DfJ#bhx^VDzd&)? !$mVE D@o=XAraX,JJ+X [G'}zj }p/Xs@J%V ڢ_vע){)IcaCK|0?+J( y;"!A6aYF]!6B& ODCs!U (1. q/}8!-&oOjr~qɳR\2_CS.]?YȘg|1Do}0H8JoKHCR<s& 8xz|'֠M4 4k\c* Yᔨ*Gp  yx[Ԯ1/] 6Zs:hQ@Bg6m(IB7O6mһ։Z wRBXrGG:Ԝb/v&D͉QX'4[^PpgWo]7!x iT_|f=g0v /-kQaQ֠-6 ]~֒f${ ok_Ǜ%~7ͥ=̩9m<yQI#!<*+K R(i9v]jڞш V5QF* oV5%Gkn;>-'L<[Z!Õ䘾& t\hZ9x&#uD'Aj~ϣ'tۖ_Μ^ ԆtԨh)6aFcy#b -`k!9Uj/!lİw^.%75􃢽z \ ~ Q?_%ZG:,ep2|{1ף[j>Zlm8Vgs^DSHvqLF6_'gqT{udr]|:uTdnr6JGJ=fv]]K/kɢBdbLs%C+Ya!k\#^ 7K ZA^ӐY"\-y@bGGG|T $(XR{I2>Yذ z`+С$.<{f T@H]|u H-L֣&RMqoS-O{?TAe`fJ-q݈p^d]qD{%(JsjֳZLl~KnP۷DQж=Mx¥ Fr RZdgj:~-} ~ZlHXk:5 ܸO'eQE%-#x8t K`#ƣ)z2UUvh@GDaNQ@7uk[I@L!iSUAR-t)Ч͐sbL3fM_4ArԞE%^{s?"o}-i){ fFP׷yW/I囵!{F?(Ui]l9"-K[xi3-MhS;># f{ IF%F"ϟAem87P7k?VKD>b>z!uZ-^QVYz2>,EH+zZayf ވu""vi(7/R\lgZVZlsuS7qN˟Bf܎&C7 d7NwIU#Ssdij= g ~HZ#&1C񥨬xGh.QX~'~ut%X:u,/xm^ui$P;pE=כߝy A= _12fJ. ^HQ'TR!kg[&^RO{^Wĺ2iT#JRG)ݯI7ܛ:IE];].S0438W-'5 j~j+L|յ)} %{Zk{/?X7EN=5Yj] g/="@A=CTC~o޼ o\ZIm4 _3u igrɊXgPK~VGA:setuptools/config/_validate_pyproject/extra_validations.pyTmk0_qh_l` t/4n(%UsM$w3%}'Iǘ>=w<;qjS d(Q{YI(Y /vEVʪEB>p^lhQfp[_}'8uc*ދ;b=,9\{S-5wB!tX9B?ɒ [/92N"ss7gVTCE t]lxeQyyk|S+z˶5HBNHq{"TM[Hؑpq.p!^]#iAx`|Ԓ4 >m_5^u0?[3t&:uG0hvyD43cH|]\O(*glM׋k y^(gErFg2M㣇|G?~{=}|ux>{sߏǫ|,Q2\G,͓e:FqJaߎ߼}e(}?x7t1ϗ,LVhÿ9o׏ON߼| |&~`bg_U~WƳ% tH4~_ǀoy9'-n.{oytL1p'GUs@s~4K0igQ#Jt^.b8Flv1\\//<aHG9!z8NG$gl2/8ҏvHyYo/.<]Y뫻) áYFY͊e2 r7U$Y:Ip׃"HY>ba>baGMC@^6pGv~ "Ou(<y~l,:Cb~0O!CC}-')eާlDNjE>`s&q!k*ߏbgKng>2[&E]f=.V9X^kZGuL'8ZG@TMS ub]K^W(y`p.IqSh]Gbh9`W0^G!"ĉʗ‘V If ,Bƣ]lq~a$(6&KKQq9_MH֑PEHO;pmHxLdL^E/adRyiGE?PPZ`p؁(y:!=$yU(|5#-r>_!^}+Vɫtm3C_`ph]^O?3bsߦtpLeP i4_cSgH콿{ɸaFl"~p\'T8߱90uȳi_Gd!:iNT̃ʱ\yw0Ojj*q Gʶ"q^<ϵdj=3g\΋TJ`J>kD8KA%;㣪@ڐ `Aa; R/=\(ӷA8dIs HZs%f ѡDҏ `X- P(4|x\Ykgj# S &$(8}{$)Txlh{ P$}>ƅw|wK1"I_4G3lE[ J.@l'ŀf J|[4ξWA9E8ǯ_?& Ǽ^dt:`ZΦ U4Uђłm@U>ї 9 (#.<'Y:'3&d/bro~7` π;XpH"vln*`O&)]FpX"#{l|0C}˸//@@|ͧnV AvnnVǹ8;6)^)p8 uCq ~C4"Iz̐qf3* 7uBuԍ\|4W8<_F^e (lFpzKYVLURL\LVb{6c|,1700] GqZWiy~ 7WJe:*]}hR0;AMc*gQx^<Ӭy\ma`=p䥯xW,N" VQ4%lk:~H2-N.:x^8K ?F@pvʙ5w 1YƗĴ ՔvxȍRܩUsZ?Bя?bhB&O-A6mr6QPгkT:j d&doTj6tTA4teҟvBԂ.OkV]o\;!MK_눩2ڧ:uOHS/$s1^ͶΣ 7Sŧp$,N%rl@ yEWyWIx"cIP"O M?5^dED!Ԁ (ROjU|'UT}Ѱ$/R:t;H Q^p%,dK^\@xB X$BHIh<`E2c眀acir:ã"55ut["zAgBmrf an$h!IΑϞ iK>1J<n h 2uZ̈́!GtJ̸y<'*8T= ȁ>'(Ndɜnha2ĹpȴXGz`Y5~+9B,/Vg~ q('ysߣg/^??~}ted~Ġ_- e^6[ v3˴OddD&3 Z#*_93:e'jEvʁπ&.y jk xX?~ 'P(UFW`yOp'HQq^}:..t3>ٟftv*+@4e%t!@=o0YmJL {)3R(m,k,VH+WY\7'˹mp]:j"[p5}BY ϊȹa=Š Ɓw8(skH_/MCqv\KXt2?v$wg;MHpӋ9i\S~ gwO-Y䰇a8y7aɰt be JB]F|gX% h~;sttLNkb[܇A ӔӠ%qmq2Q}RZ0bn~" 8aqXJ-8 *Qz0ާJ\+i aa ]hWE Ij= [^6 EQ|dD2P Ԡa;KHgUk{JQ"<#3MKM \&IC#0`~,E (FiB2žg}|~]{CQBaA-7 #1ȟNtĢӆ ֲ8t}>_ƒ byEM{yҝ(:cLEq-MbYQH@vjvGS:0!m?16ʾ/j9L'"bEz}v_}e+';PA9Cx?[ U\{g;OmM`.k@ÑiR=~+&f5>m`"턃8b P ^=(A_Q,3-_<~'lv?h@ȸA:g:"M[Ja]ՔaB5z]x>q"+$,[pv7e7z%܇HR]d-Aö'OcƁѲ&ѢXavⰺtLbS–],'2dڕ l4J:czD Esg9`ec^?h飣!"Ē1+I(/)?U 6}\+gʳMFSr/&Pb9lhw6n}+5v%FuB9-ՋW=tEϭG-xKO@1ST]a8a?d/7mD,4n'/=y{zQ(nV@xH8bʤ4A1Uz)$ S"xc$\X|lY&R#FO%[g¤},CUigTJuFQ>U62EP71C\|L5hv.h,GcDkϺ=a AQN/~a>CE|!~"=h#lΧbVs=F2iАF\E4 a}9Yra|w#j;ΎYԒf!ZAHR|;ZidhnO+Y :+aRu}\}icO75_KP#k^CL#CR5Ft$Isxt2+{x B|';qk-Lxbgʒ,'Ք^0YKzuB0eIOpȢHJ|eHvd6T|e NAϖ#|xB"'$ xB"'$ xB"'$ xB"'$ xB"gDmp<_.0 5B 3Yg:YG GrW@զ@:^;gp Ń*YFɴǿ00EqɣVXe`wUUSe\}h 뉉{VeQ+a:C5OrSxh9(hGp6tx (Us7Vnϊt.5pCA7^ qP=tSd]le PUctVb;BJ.(+l}if@>{Y[9>bioNd>V>Q7݀% ;'4_pߵ>.~7AT4)jkhAw:kz+inӸ}3=O ]`i>xt=Px[TnJLulzu0a7D+W5סdh[zeVڿVWVI: ^ޜ{ ZUf Օo;6Ynvo m\srza ;<{E!}|,V׃+xb bD8h]sH15P YLp_ ِ? 蚡 !] qײC=Æ CKKڀ3u52P+ BP+ BP+ *4wɥ]~WeP+% ]_Q BP+ BP+ BPKP+bh ]~WwT4xϰ3t3ԦB4KKUK6Pu/j߰ {NЊS5!7ZkY,(Tjơb0ԑE56 d0+AetPD QځYe bq@G .BPHk( ે<_Q „ k( ેA3|_qBJ27̍!sc27̍!sc27̍!scCF1dn m27tt!s:*!sc27̍!sc27̍!sc27 27̍!sc27̍!s-dn xB"'$ xB"'$ xB"MTHs( xB"'$ xB"q9C"ǂ'$&'$ xB"'$ xB"/#gQJ"]RT<ʝotYCϬgVg{r>KX 윕kH]*WH0JF[w^Hd.!KHd.!KH|sC!k »w !-%PHZwB 먄Z!VHRhZ!VHRhUͧ!}cB2v>.;Ԛ9nmiVmWy &wu2;:G!h%z(6 DCM 17w$|ADChf?ѣz#7<+F^B8qۓwOFYV6`ǻJjˡ /Z{$e~ԏLRRGϕ%ݒrX1S;fS!HkK 2ͭpYŹpQkθW_zS2*B ^E#"6jꜪ}io4<9F.khI qQmZ)X gӒ^V2K~"Z{Hpi=|8fˇ`o*~of]QDVkKi7"dxh1Ν +mGvѾ^fn6 &7uw8TNL=m۵k{^/w^) '93Q[ʦ'.+`m]|B؃)x|,p*u(@ K O,F? ׅH[4Ш*i_DW#-9:4]/~|rrw ɧ T ol=lYر BTf{yG4=d(;Kxڤ; Na }ů烖(Vvkpĭm\S W|<:>Կ8?,c]vwl vT5&WWˠ^v^6p|3suTuxקj iMR!2mm[>c&VȴRCP!t߆T%2tԦ͝nاFN5jx:4KY7RigMT B:/my#جݼydωTu)⬄].ٶ"?^' sڔ{~Y23', Veei/|G'!`MycxfE zǪ <56lEUG@_r'ĪQFt5SΜXWjG>pUZjjYUw: o퉵#f6~(4uuu}&7浵6=u>Zr#TY:v|KL~v`N5FZl,ĭDa^8AޒOB͢q[/nnn=OYDmd-d. Vmf[s8{M;oDԲB7zٍs͸ka-|<ۧf<ٍCm1{}toLt㴦MѲ VUgQWr[Z~-R NֶF:*WuգU9uƽ;sPҢTNx-9Uİϴj-U"L Zֵʺ9[ V#k-k־^MQ>ϖs]SOዢ$k!5oܶTP-<!/KՖyFkIδc)dYi2+lHO#EX$±N%gAJJ?9)bHj T} V+m7?=~GlXCydtIvr\P9DI"+AO5ydZUY%Rד4d<$'@µ9zQ\ڰbX.UsFJGB eJhPm~G- OmfO*O٩C:Sq|ERikY/hXO[ VUngm}w4uyo>GQwTVg8&|WuXIr9Bݠh: @&0G~ hEOX\3LZfB9GP5A:;VUU>S;02+#BTi*z tovA:5p<EkCF3e}caٲApOm4@u+xB,Դblr Bӝ\3 +/&U:r]6#l\M˫jI%" )%=@.Sh⋇7K9hڥ:rZjtRbf*TbF"qV`aKOf6Zo8'-hTRF݂r&(:&5M8aMZm AX{#}.R_y۳^|(zn5dn:KQ9Syw]\AA jNb٘;rtd5Yn'/=y{zp/Tl!qu7:2k5]ּ^4mypܧ)ڹ*osmy_lYG-L\Ǭ,xKd:zzBXmA8|Nuѱ mˈx6cnA̓12SVc4]&8e'5K_y&&Ɖwu ʒ(tc \8F=bnd,LtX^F m@Yr 3, QbbvmUboEMb d3{߄)O1 NeZ82OO!fJzN?8jB4$^S&zC1(\m,6sYw_+XH-Ocp?t45p]S .6'ƀ8[b6|6!t ׮rv*.i^qҗYn[B@m]3n!b͖eL&ض+x*fѸx|]7HS6RI.s}Hp(Ӛ k (b׉p"q &҇qf@" 1ej;yUmzǡ}=?E{Ggn rks4 Be ޵]eA /y FضS$td,+BJ|!T7Mn{s` 'ogx֤֕9)ړ:]y9GvJj_!EPu/ LK=ؾ6FsgQƻ;{SԢ (ƻvGkwPm9 Mpoƿ6>ng>})k⯫u-^>uT~;."|PPDa~tQg(SO .)TM+kaWpk2]6s0yطx]g'yE4) V 5<rm ʩ zmz T{&o+Zo|?qk!=Ԗ{]ʷ$B9PnT]O N&t+ni[([뜹SJ?-tѷV͏fN$ݕZ9S=Օe`m69>W\ k;Lfҽc]X؆Wlj η-cwN,'֠S-As-]@_9Z1Gp F,Ƃzv)+%oΆBhgX&߰6b>Gd `$x< )%:[eq:.Js9Nx9$;_mq4">=+L~-0gofE`}_}G'7xÀ?8F {=ɒ=PXZ$ս.^V!= c̢zUE-_VPdlCh!%Y>r $zʝz`t~ û*;IWiά5Dž0 t.`4)li}^:9J1VayH%WlLXZWtd>ѮH\3~;νmABg~]p+I%V+/(*E9KY]*_GwJe}mm+O?>IY197XVuvN7A3g( LK0Dr,Ɩ~\Ɉlg z,Shtߟ`tV9X=2%QMnb(2z$#vf97xȜL `$֚ea)i(f2+Xz-FK`ovG٘*H;3:j.q@$paYG6:_Fri'Viz̻]QFmV&IEy 5/8`?9?Lg&3 hg Y@")dZayd=_ӵg5TgaX]KUrbi6v)@mu]L3|0C SnLy1tE[=HȌS!i?,K($w#ȩE|I== ! Q_V%5j [k"J {͝ ɪMϟ5RL_ԒRGVk41f&M.@LEE+Jm@)z-x?Jhdf 8 $?Yy`)챖y"1JopIAN-c q<^-,BlEMm]Z!4 [a݌~>~{2f{0W|и3hb-z]Cu F]\F'29d-V8Y;mbm5î^f v͎N )Ƨaz'O(Զb` )M@e뽬Z¥M\=WV6jOki^c єeݟAs P/#ƿgRm,,)8UR><_S tz;ZTe֢;zz-jC\o q_&{yhB߸2epLj!sbnBM17^yh`'7yL4gy'KβźzёzQEoœLBZ%Qd}ʼiɞ#_-*+1c"UfJ?|VcNj;),mg 2]q "Ԧ.dp%"nЉYk olL#P&m"-fA$>:bS;jk&}Q}m%ؚEl5 R]Es'ѸNwE~>ՋA'ӝ؋\w֪h]է̥S=UtNu۾1;zgm蘹$7PnvONM=ͪ|cS޽FPl~?J%Evtto[!$_B\u\u]ޢ珙c3+h姝aS CJ=#jjXk`.kǾ`c *M7ʷ6 e:JEA9L]QHj6L [Gox_{ſ:VGI) o&˿+&]u\2~Ro~r(ŇtgIǮ Ǫa}/%8z~OrmasN)j>J-Eu%{SgJZЃGSZ[f{kuE';_9Z+fۄ30SLO"d3./k Tx6bc&X_7̋:yM|wVڊ#s$4sdpdc?,:92ϑpGrd1&Y92Zg~!qd}YO_k:ȥEds|kVFY;݁uάD 7zYD(hqݵ5ӡugwURԖQ΀ ΀/5U '-آWm ӊv$h\>\d_R"*{pL!ߥ$cm(wxTwzTW]X=EҐeivV}c&og ]4gv+GDq5/&<3?Օ)bِ لy~1aȫU|UC CCxsY䢾<ȨNuJ bI۫tzi@RŲIqwyVvg5X]^emV@,X-ٻ(o*DYU ~b_ D2q\F ͊(Y̪{c }KxBTڭIk^ ^0-XVKRӄW~-kS(^Tk_ 34 )}lA +-Y-sj~eFr'K"w"‚ĆWQr''o^}4:`i沫sk 8iGM.ⓀsF1W-*E°עx3*#\$e=?iO걽X;Sy6Mk.@^dIL&:'3Z 2QI#Jd58vmR`; &ۊ jJxQ>hꢕL -)0?@ClB *AT >% ?}qC.4}[ԏz[PA"fpPr{J1.N"zZBQbE?wz]q>-ӏKד~qj*qd`twxPƨ¤yb]ErM%]\L({K(xB%77}`9}ui!-0P6c[XDOҘlsi<<ݘ z^Q[!xv/}5GI&ʗ#>"fNjy): Ȩ58nK#L82GZøg3{FG !/+Сl,aQ&2԰%(b4z:_MXld)X J`qH6>#+)Sk`^yf^:bF{: _ks@@RiKnA?Zަ`+UE\C w-[=P m #Qʾ~&G*t%s3@p^8\0dZsLU6fw7t?}>W/_ا jP1{ WL<ڼbL'Z-;g9s3hcU cuۅC 28[I)S[1ܜV2w;U,;5=ɚ o6H/b,HA?65waw%CȺ"c=Qg}ù㿐УP_9G 7~,1ctbT@h ajK"4QAogl07Ƙb$, 'dq&HSmij0=M`FY!jAtY\jNTͦU6΍IʄL4a>)w[WujC0sf!93`"73`@сC0sf#<#Ñ G&3`zWfw|[B0sf!93`øU&\e`f)@ڧhB%S[zv5Y쌕o˲nM:Vꈕ.MkE]*_vV*]+:ίP.mNPuxgYBb@wά\ac ɳq5wO8*;jG۱T;JQcAG஼lfkG(F'I婳p>Ke!EkZ9J:"~MɆvbW4QSK?J]'*NAZB_VȹZgKzâ{XԖ_I>2tuX'+|H^AdX^3m֥?\g]2];vx_؜"E2P^֐NADSSƯ @Ti?czV/͚a<[fS@wE⯽jiPH0 Iݪa ܨ. e;F^G\2MqAu#.p[8/ql%.ImM2(!B/ e3!!0Cv#IZo$It#unTBD ACa-ĭ}h#YCޑvTBA땑&%D_H| ^m+DC7//`҆b5G2ӏpwHnpyzd Pl^t!G`xw4̵;f~m$ڰ&Jh/zJl{TtMШ*Md FFZn)E6 '!k'mێqN6%Rc N%ڢ7M)}h.I:śxSi⿯aRsj؊[3HVAG hfٖF!A!e2b1}Aj6 zo=lRco#0`؊U؜ .ܵ%+`K)`++};ňwDxD:U q#ơQ nJ4G5-~q Wƌ!tCPŒ7Y_yv fߤB3 .&Gbnþ*RBwJ!P,}UAvJF,@ǩzcכ?{UiA١w7ӮŊ8!p(Y!0 YԇoPp!>C[o!>l'O{6d>7]Z(dNbmD|C'=4޶QA"P~D]y&AɊ< bAN4YxjSSL(D:Ć z;ѡCCiFjͷ/3$uOZ(/DuµQvNab#ć:!;KPN` rvyEq-GL'zP)/̻w0u٘DFj5>~J}f\ǯ4vU]\ŕk`ʈu\H2WMMv|ij 8:~;i}(8uyhu%\Kxm6lUdc9 1c:UɈNs- t@SjH扥PiƾOcӚtrUDG rlxƥC14V#Vp `jS ;_1'e D׮[ƾ9o׏ON߼|atTVo##h@h ^ >0^O;(?Satdm~;r* XUèbS%A^Y0:h̢1G*,+}OpPU|8EtjB'*u*=V b6CGEjw'CaI:[ ʱZcЏ1D@IWJw>{؃vd?T.lSLZy\kN+]x\\}vl9;. ZSY_TOS˵^xmP7}?|gXYf髾YVf_jX D8S7vlx5vNMZn^Nk=A TVNG+pʶicr=cW[giVE۽W[0S9/{3=۞_~[:8| Sv8tNV;V:mx[O k}fF+mV}o1f)%'gTxrf6eD%"(bbgl擂|%9㕈KLcN,K+>?5z'HAO|*p7 bت )szx3^]Kc:ͅahξ@؞TN(MO1Y Ķz&rٛnBM;4^kixw%` @E2v;5{5kM )|>dC_=`ſ3"69mlp=zz7@|W`zBA:+-^E;ڎ;,K} snuwXG 7VI& TI49@wsK,U6|I&+@/_ew;@o ^SL0|avفo!~۩ڵjv^KKSOMZNMzۖ3✧x< |V13أON9"q7ڞءZ"W]齸[RTS! jlF{[doFvU:9f^ƒ9K `{nEҩDzY!̆佶glmϾXǐ(GvMk<W+ECjyZ_AkmIdINY]ZOF$Ol|4Yk [&J:/*PP RF!uP {jԜcZUV]NoEzd ;3$MYq6NUOclJgm)DRI[I4ַfYe_95pߗ[a)RS[nJ:*Ym&탠L\km!ڳpK}f]l̀;m5|[{ln5nw'ia4qFnÊ;P:,H`}}FJ/UBhPK~Vk3 #0setuptools/config/_validate_pyproject/formats.pyZ{s۸_t&-q{<GGeHPBבm|߽Ht(M2NdbmW-ri|]%afP9 "503+Jm/RR33>_{'mW1H+ï1O 14\aQ3V\HXR"6r:4!4̫`e21+*=tTRo7[ȱ9vz`SEY9zE,GsxEs,sK"nMhXPsq\)>C Gnv`]<8H=l%.0ԣA))(,堚e"rKS%ׄ~H}[ :෴ k8J*$"K( %%ߴlzOM%:`n\n)<|+"4 V~  ouP UPf0^$A<`.+cbm]_]'7%FrC`,]?u'$ٕ%r g $hҨ&X /9(s# 3#xˤ}9`Kq ! y6+mCCs.rQrdSÏYY'LۧJ!lQҵVD1OD0i;g/?^|~d:r&M.5:::uST׊&(_aL qYk2hFuj^@fCg+nK/CݲYʲDYtYLFっDw?@f ѫ -hʈ[N熟̀;J Y6,pdz*%u c%D"Yu63ʾ*b399*`eE-]a*\ G؅w~ƮKSƺ-γ][Ų,WoC'"vȓO>6fѳϗhn>cC'1s2ԆFoFo]/o|AC|5w& ї7 =KvcgsrmIhx?- ع;@DŽ3HtmPS=h?ƃ[nf.YA>(` _4K7*M!{c'Y%BSP1j;  66<jB9@E-Uevi j 7bDfj?{m]y,-)i@i错3g2n4B1.iPK~V: setuptools/extern/__init__.pyVK6WHJho h) H^J# E2$] )a9Ua4or&㏒O:%?N޳Aw=p?>9>~{6@̊pWN 36HRӛ"&\=KL0(#:l? x;30gLZ ,f?sezY~c\[~<7=,aڋAQC(= z'Jo/V^0YlQ%_XpaZ< \{ƛ(z~bà6Ӗf3Op<{͊)MӍ B{e㔶~T>"/fF,>@` I$HqٓX $eyĚ=ʹ  w*a˄`eʣ(#9 y zIE|'-Uw q: :yz`y MQ^ֱ Nu`&ߤ?䧃V& uq ;g^Z~9Y&({'m7sw?Q,t_Fjڸ:( iQ*ʹ!.MfdTwpxZR!wGwO>84R15'BUeO,=CvÎ95vtB&2ih`iZhdqBag Ef@}Lܙ(%7¾K%}sC8f 'YunKˉȏayk<&Sd x &7ف@e bxɣ9EHe\i8$hykq.cϻK28A䓿G) ip5y&Vp#?`%qQq^[ . OsffcGBa~b18dpʕ'Zto|MiDI`Z'[&R;? F?'.Yx0VF'8꺑՟L$">_ 3ښdV/2)x*4 s^,Rܕؓ&ߧx5 e ;i( dX̥9&l+MA[Cɵ}5{]J|,UN U 2$kU| d*O6u.RjsK ;,Bhq6FblTa4 zHkYU 霈P qgS×x'T*OPK~V[$setuptools-68.0.0.dist-info/METADATAXms6_Lw$eNl]Ԝk;9q rE! %w$bK6!$v}_`,OA!G'ÐUi&X9zQ/Wyu=dYT- x&# c*3SgHYUumg`%Ox&C8d3kK3LY5e]xM Cv(X lݒs!,VVHtlYpwHlՐʡܢKEV#R$*;"'yx(]G3P/N \~B/W22y081^89 /;a!;b!C-*!kwYX(RHk"bk0W"8KvZZ͑?^t,(C+tPE& AçKB|7 . IZYC`vBNO:9iD]=<v1Jj;W \6*ٯA6h]~o.R$G!K@[k5sT/ F5~Hk\rxic#);qL+j,zo-RLmˡ.eQC{, 7a\E)K/ BWͣAC̓*Y-&֝`uhoaSU׍CCf9){0Y0JQhffZ $w$Xf(y>cJ~ayυP̝m~3g(It*">A%s}w?W/:lJaЁ :xp[?qWb%6.·V*?,x*nQ,dRs8u|z})w >bU$w~v}n/ Qw1M/eQ#"j7U\ǰաDrn c۶2lR 5hFD|< Hf5M&Vd1:ץб3-IøsKAn/h߭Z*+Lt)qHϜ&߀@W!(y{FS@+Vآ]5+PQҳuRFv0uA&U>'H(uX[:̃0JscjsZ_[EjCN|X~uҥ$({sM[[28r8 @Ԥ RB92jݥE__Ӣ@/lPwߩy޲ ; kb #Ŝsh4J@f7WEU>³ ۹ơD0lmlDv6hA?Y'A5fjg eP(D"eٟ4bީ%.vU?櫓 ڙȎ'FyW/oz`Iu]iW>62"Ǜ[4 j;e"Enu3Vu3Wd=^GH0P49Pz$Ot磃zY "Q8-JVwgKts(, ҡRpd]?M!UU,Dsbn u ?Y4WOź{U٭ |`J;Z= vx06VG}}(p{ډR+787Vvx\PiO@A%ODQ@FM?|N#m6oĒJBh u?$NrXk&K-[qӍl@SfGL}$|Qb{nw Kj:DX?h(JY}YIRǓG#JVIY@+d}2-0PZ?իp5۲-KюҾkz@v5}r'  c8=fᡉcRۃ{K%J\Le~3 gp͞ #RS|J9áP'#6ކ ?3K_YApVLS|y_8?>Ѯ]lҰNa=Sxb 'Qؾ4&];˹Ĉm !7WqvMu}b^w85g+~ *.LR' йy8 GUYP&8s[|a,NUJe;|۶վSiהݤ@gU]$ϒ *{eA7z2.ɴUBR^=vUz)knuWIc圎$0QRPx -8\m0uj?N,Njv]y*iz,[HL|9hA60.v jYQjGjޠd0 ]?3֚TfqNv 5k`i-u54^ HZOjuq.kqVFN]2 >}!M  ,N}5O:Hݺ 3rYljgD/O0` Xyb!YlF9>YBvtKkq+<裟EU,$ڦ8۞lʼ7~/(-y;Xݫk+hEBZ}$:$8 ^; q}4{9ѿ/aG2 f$H&raolڠ( >Ey=++uÔ:(77cb&r3!vLAwORd~:W71pmg Tꑫ -p 8nʘ'C:|hTq¥s=9Zl(}A]Ѹ{*y1}w}lR/uAl]_X{z wo=g(r/,Β=PVPJy5'M.l%UU0ej}=kW˗z8ZX~A+2 4iOư1x.@sQg'3ř*`H~\}|quwx#&]Rо$;(#tΌDm5ۼ$)v^n $M2(ԁH+}#cb#;f70@ßuVJ2?x<}H7BMqS)C8# 5&׸Y0}k `zF4-a;*Tz(a cgv-gy}8B$BV# m!ntdCTyvv̆gd}7x5.1_@čyH@QrPQ[6|iǻDkdRΗE2*8H7QW4Vp!NP8E`{T 'Ug506( u(ڞqDf3 faߍ9|ë9m'ed/; Ȏ[}(G~x☱"U0(wmxfhc~} 4F2x·G5&?lΎXdv a5!/3ϥD\OG}Pg;د7]= V\P֭bXišWoS aw:Pq{1: E*vDt6)92m.\ #`RW ~Glv/ x^|%%ygK_W~7|(5']ρ=`qU&&(6k9]H BG6J I!}bF/攩-vO0wWEQ6=e']sC¯"uncz^صpN+{'Hk CZ{bLhT~rR33@\r.o˃wG"4oj+37Bwq܍Ԯ,)!>ۂNkxUm+F.eBv-rMl%ۣRJmR擫|)L gߐm.3KKe`?յ淽Z:z,}-S;"Ľ 8 H UE(W~d+X@̄ l3nh"H^Ƹ?  Sқ(yKtWOnK>hFY洕h.~=Ĭw*/訫:7o#eY/(`GmW7T ds9%zJ_ƴ'Qj̀ñ޾ {Я PR)؝KF l^JvDYa6Ʌ|7B*צOWѬQjԖںB:$>)K[RSm)ұ_ d$U|S?ŧUndmE>u&;͝#H7:w1M~iAZZ9._`дB!0]RD 6VE0Ȏ5uwHÝ5"w~:ۢ(d ~.{e%i IQ!Ucgz@GЧs|<̰4:l>8WK>Aov1@⻼} iݏ;#9X 4;sJ!9w-ZeC>$/Gnn-;nA H8pZ:(>Sc^θMi[p/Bs¼:] N2D?-ut{^dyffjڞJ":s`qmC >mZ+B][d%f|@0L&Pj$&ƪɾA E#\ k9Kacj?ݙGM)E~IL4 qD國=m), ,z1l4abE=kSg/2˧UaώO.qE9&8y݁ݫ^ZSY즘 57n5q<ϑ~eT+vzEqdFf#oX9g(SD*7A-Z&! YHmkL ef_x}Օu"G.TQQzcco`~ib|sJvdvJhQtf\@]CᏢ˘g՘T1NRR,˩˶z5l8gˌvK%H,㍴ִ,=?)yᴤ[⣁eXxq.7߯va ^QX(qv:XxUD]k޿̖v{)t7 ܗM2s&a8ӖUeu&]C?32_aOZ=K9Cl*ƖSz;d& c?;X(ZzX=_Q rGj 4UEy$JLs:,pArVS{MeXTgw#"u:N\L;707O} g/"Nd'bdA]8 xM-hrNH'vr/K=f*e-rkNbO@~9#hwIܗ0,䬸۵!R_օO)|;>C灆njw |9`G^qm fլ;xuv^ ,Q)M7z[IOI;1  <U-i[dtGGy\TM>6V!pW@5_OߊKi.ε6IvTc:Y Qr-#zw.&EtCzp݁W9 Ŏ N"Aݓ6A]{ }u' Ҙ'̄h9C. ,]Ǔ_e@sl8UPW@kA/=f{a@b,{_Oz{[ڍG睹ESߪmn[k%mHo2qصlZ,2]F SnBUBc:U1q\ٿΚ*4U|_d e!]eXgwihTTŜVФ!e ( 8q~/]󏙲'e儠i2t>mI'vtdsc |?&- Mm%)ž.a. dz'Op |5_A7& }K}|R|⦌yڞ] *|k(CvZSFᬢ o8B)!f{ۂE-sl j8>A?[1y[ʮVv-pӚE؊cJ{5#"1;'Er/Vl9 Ӌq* Jϧ m/CZ} #6[A ܦfhq% az^#fP8}w.@JIڃfݼeWńb^~{uR{JWwiWk7zT+Gٻ v6a5q(l%|7 s μ2MA(S/a d`Rk[t y )Uլl$aɗY%0 uʅFdҚNf; >9ar ^{倵*2@j[P@.\k ݼL~ǿ2uPK~V}a~distutils-precedence.pthPK~V{j_distutils_hack/__init__.pyPK~V ., _distutils_hack/override.pyPK~VEdSts pkg_resources/__init__.pyPK~V!~pkg_resources/_vendor/__init__.pyPK~VIB/OI8*~pkg_resources/_vendor/typing_extensions.pyPK~VBq8u {pkg_resources/_vendor/zipp.pyPK~Voq5+pkg_resources/_vendor/importlib_resources/__init__.pyPK~V't6Rpkg_resources/_vendor/importlib_resources/_adapters.pyPK~VþQ4pkg_resources/_vendor/importlib_resources/_common.pyPK~Va.;m 4pkg_resources/_vendor/importlib_resources/_compat.pyPK~Vht7,pkg_resources/_vendor/importlib_resources/_itertools.pyPK~V.ev 4 pkg_resources/_vendor/importlib_resources/_legacy.pyPK~V۴L09pkg_resources/_vendor/importlib_resources/abc.pyPK~VUKax 4pkg_resources/_vendor/importlib_resources/readers.pyPK~V\ 3pkg_resources/_vendor/importlib_resources/simple.pyPK~V(Jpkg_resources/_vendor/jaraco/__init__.pyPK~V x 3 $'pkg_resources/_vendor/jaraco/context.pyPK~V/5:) pkg_resources/_vendor/jaraco/functools.pyPK~V.ɪ<-pkg_resources/_vendor/jaraco/text/__init__.pyPK~VtlOp0{,pkg_resources/_vendor/more_itertools/__init__.pyPK~VCz@,9-pkg_resources/_vendor/more_itertools/more.pyPK~Vi cHc/pkg_resources/_vendor/more_itertools/recipes.pyPK~V=64+1pkg_resources/_vendor/packaging/__init__.pyPK~Vb  +pkg_resources/_vendor/packaging/_elffile.pyPK~V"r&` "-pkg_resources/_vendor/packaging/_manylinux.pyPK~V} -pkg_resources/_vendor/packaging/_musllinux.pyPK~Vi '*Gpkg_resources/_vendor/packaging/_parser.pyPK~V0`.pkg_resources/_vendor/packaging/_structures.pyPK~Vsw)-pkg_resources/_vendor/packaging/_tokenizer.pyPK~VM  *pkg_resources/_vendor/packaging/markers.pyPK~VVN @+pkg_resources/_vendor/packaging/metadata.pyPK~VDz8 /O$pkg_resources/_vendor/packaging/requirements.pyPK~V7]3!&-)pkg_resources/_vendor/packaging/specifiers.pyPK~V=mF'Jpkg_resources/_vendor/packaging/tags.pyPK~V*+$(N_pkg_resources/_vendor/packaging/utils.pyPK~V x?*epkg_resources/_vendor/packaging/version.pyPK~V2.vpkg_resources/_vendor/platformdirs/__init__.pyPK~VZ7f.M|pkg_resources/_vendor/platformdirs/__main__.pyPK~VR"si-}pkg_resources/_vendor/platformdirs/android.pyPK~V Vp.)pkg_resources/_vendor/platformdirs/api.pyPK~VK |_ +pkg_resources/_vendor/platformdirs/macos.pyPK~V -\*pkg_resources/_vendor/platformdirs/unix.pyPK~V[є{p-őpkg_resources/_vendor/platformdirs/version.pyPK~V?ߖ-pkg_resources/_vendor/platformdirs/windows.pyPK~Vzk npkg_resources/extern/__init__.pyPK~V)S )$3setuptools/__init__.pyPK~V&lsetuptools/_entry_points.pyPK~V`K[ setuptools/_imp.pyPK~VλcDsetuptools/_importlib.pyPK~VTXlsetuptools/_itertools.pyPK~V].zsetuptools/_normalization.pyPK~Vb) bsetuptools/_path.pyPK~V+rjsetuptools/_reqs.pyPK~V4setuptools/archive_util.pyPK~V 3BM1setuptools/build_meta.pyPK~V5RX.setuptools/cli-32.exePK~V8%setuptools/cli-64.exePK~VN@6Tsetuptools/cli-arm64.exePK~V5RX.)setuptools/cli.exePK~V+JΟRBsetuptools/dep_util.pyPK~Viy{%Dsetuptools/depends.pyPK~Ve g}_RKsetuptools/discovery.pyPK~V1׶dsetuptools/dist.pyPK~Vq וsetuptools/errors.pyPK~Vu=̕setuptools/extension.pyPK~V޷Y }setuptools/glob.pyPK~V_[.setuptools/gui-32.exePK~V(8setuptools/gui-64.exePK~V A6:setuptools/gui-arm64.exePK~V_[.setuptools/gui.exePK~V)K:>< setuptools/installer.pyPK~V2S,setuptools/launch.pyPK~V|U-wsetuptools/logging.pyPK~V<Ysetuptools/monkey.pyPK~V0>%setuptools/msvc.pyPK~Vpm!N Dsetuptools/namespaces.pyPK~V +͕Hsetuptools/package_index.pyPK~VJtsetuptools/py312compat.pyPK~V c(3 8usetuptools/sandbox.pyPK~Vdsetuptools/script (dev).tmplPK~V3jAsetuptools/script.tmplPK~VT+~߆setuptools/unicode_utils.pyPK~VRplsetuptools/version.pyPK~V2P5setuptools/warnings.pyPK~Vpe !setuptools/wheel.pyPK~Vqousetuptools/windows_support.pyPK~Vr7g!\setuptools/_distutils/__init__.pyPK~V)L%}setuptools/_distutils/_collections.pyPK~V O#ãsetuptools/_distutils/_functools.pyPK~V=&-$+դsetuptools/_distutils/_log.pyPK~V];F&4setuptools/_distutils/_macos_compat.pyPK~VAA8L&setuptools/_distutils/_msvccompiler.pyPK~VGJq |!%zsetuptools/_distutils/archive_util.pyPK~VĠ 9%.setuptools/_distutils/bcppcompiler.pyPK~Va 3"setuptools/_distutils/ccompiler.pyPK~VÔ^ZE setuptools/_distutils/cmd.pyPK~V S/c#setuptools/_distutils/config.pyPK~VTP $a)setuptools/_distutils/core.pyPK~VE>.(_6setuptools/_distutils/cygwinccompiler.pyPK~V_H%uFsetuptools/_distutils/debug.pyPK~V5V !9Gsetuptools/_distutils/dep_util.pyPK~VS !oLsetuptools/_distutils/dir_util.pyPK~V?~94Wsetuptools/_distutils/dist.pyPK~VdX setuptools/_distutils/errors.pyPK~V~& ("[setuptools/_distutils/extension.pyPK~Vڍ E%setuptools/_distutils/fancy_getopt.pyPK~V  "дsetuptools/_distutils/file_util.pyPK~Vqq F5!˿setuptools/_distutils/filelist.pyPK~V9M8 Psetuptools/_distutils/log.pyPK~V!"u&setuptools/_distutils/msvc9compiler.pyPK~V ]\%setuptools/_distutils/msvccompiler.pyPK~V(#setuptools/_distutils/py38compat.pyPK~VC cq#setuptools/_distutils/py39compat.pyPK~V|V setuptools/_distutils/spawn.pyPK~VQI"Ksetuptools/_distutils/sysconfig.pyPK~VolG5/"-setuptools/_distutils/text_file.pyPK~VUa <&<setuptools/_distutils/unixccompiler.pyPK~V:VFPsetuptools/_distutils/util.pyPK~VYn<2 Njsetuptools/_distutils/version.pyPK~VaU){setuptools/_distutils/versionpredicate.pyPK~V) )setuptools/_distutils/command/__init__.pyPK~VSOVN2 setuptools/_distutils/command/_framework_compat.pyPK~V &setuptools/_distutils/command/bdist.pyPK~V y9+(setuptools/_distutils/command/bdist_dumb.pyPK~V4U*setuptools/_distutils/command/bdist_rpm.pyPK~V&+setuptools/_distutils/command/build.pyPK~V5+Esetuptools/_distutils/command/build_clib.pyPK~VDA#{*bsetuptools/_distutils/command/build_ext.pyPK~V!~S@)setuptools/_distutils/command/build_py.pyPK~VB#.setuptools/_distutils/command/build_scripts.pyPK~VY49&setuptools/_distutils/command/check.pyPK~V UT" &\setuptools/_distutils/command/clean.pyPK~V@?m3'setuptools/_distutils/command/config.pyPK~Vkf0u(setuptools/_distutils/command/install.pyPK~VSFf -0setuptools/_distutils/command/install_data.pyPK~V2L 14setuptools/_distutils/command/install_egg_info.pyPK~Vi0-9setuptools/_distutils/command/install_headers.pyPK~Vi ,N;setuptools/_distutils/command/install_lib.pyPK~V 80AEsetuptools/_distutils/command/install_scripts.pyPK~V>qp+lHsetuptools/_distutils/command/py37compat.pyPK~V ).)%Jsetuptools/_distutils/command/register.pyPK~V5;˪ K&Wsetuptools/_distutils/command/sdist.pyPK~Vz C'msetuptools/_distutils/command/upload.pyPK~V xsetuptools/_vendor/__init__.pyPK~VD*;!Jxsetuptools/_vendor/ordered_set.pyPK~VaLBmT'!setuptools/_vendor/typing_extensions.pyPK~VBq8u usetuptools/_vendor/zipp.pyPK~V6g1"setuptools/_vendor/importlib_metadata/__init__.pyPK~V h 2setuptools/_vendor/importlib_metadata/_adapters.pyPK~V5Zsetuptools/_vendor/importlib_metadata/_collections.pyPK~V"bSC0-setuptools/_vendor/importlib_metadata/_compat.pyPK~V:oikO 3setuptools/_vendor/importlib_metadata/_functools.pyPK~V:3setuptools/_vendor/importlib_metadata/_itertools.pyPK~V.setuptools/_vendor/importlib_metadata/_meta.pyPK~VkAJ4setuptools/_vendor/importlib_metadata/_py39compat.pyPK~Vxv.setuptools/_vendor/importlib_metadata/_text.pyPK~Voq2f setuptools/_vendor/importlib_resources/__init__.pyPK~V't3 setuptools/_vendor/importlib_resources/_adapters.pyPK~VþQ1Osetuptools/_vendor/importlib_resources/_common.pyPK~Va.;m 1Fsetuptools/_vendor/importlib_resources/_compat.pyPK~Vht4[setuptools/_vendor/importlib_resources/_itertools.pyPK~V.ev 17setuptools/_vendor/importlib_resources/_legacy.pyPK~V۴L-b#setuptools/_vendor/importlib_resources/abc.pyPK~VUKax 1)setuptools/_vendor/importlib_resources/readers.pyPK~V\ 0.setuptools/_vendor/importlib_resources/simple.pyPK~V%j2setuptools/_vendor/jaraco/__init__.pyPK~V x 3 $$2setuptools/_vendor/jaraco/context.pyPK~V )/:&$<setuptools/_vendor/jaraco/functools.pyPK~V]"ʨ<*Osetuptools/_vendor/jaraco/text/__init__.pyPK~V!Q8AR-csetuptools/_vendor/more_itertools/__init__.pyPK~VP,xy)dsetuptools/_vendor/more_itertools/more.pyPK~Vʀ8?,/setuptools/_vendor/more_itertools/recipes.pyPK~V=64(setuptools/_vendor/packaging/__init__.pyPK~Vb  (+setuptools/_vendor/packaging/_elffile.pyPK~V"r&` "*Nsetuptools/_vendor/packaging/_manylinux.pyPK~V} * setuptools/_vendor/packaging/_musllinux.pyPK~Vi '' setuptools/_vendor/packaging/_parser.pyPK~V0`+i setuptools/_vendor/packaging/_structures.pyPK~Vsw)* setuptools/_vendor/packaging/_tokenizer.pyPK~VM  ' setuptools/_vendor/packaging/markers.pyPK~VVN @(( setuptools/_vendor/packaging/metadata.pyPK~VDz8 ,> setuptools/_vendor/packaging/requirements.pyPK~V7]3!&*C setuptools/_vendor/packaging/specifiers.pyPK~V=mF$d setuptools/_vendor/packaging/tags.pyPK~V*+$%y setuptools/_vendor/packaging/utils.pyPK~V x?' setuptools/_vendor/packaging/version.pyPK~V%$G setuptools/_vendor/tomli/__init__.pyPK~ViiX# setuptools/_vendor/tomli/_parser.pyPK~Vu1~ m setuptools/_vendor/tomli/_re.pyPK~Vg"( setuptools/_vendor/tomli/_types.pyPK~V;N- setuptools/command/__init__.pyPK~V=/M - setuptools/command/alias.pyPK~VØ5@ setuptools/command/bdist_egg.pyPK~VDRI_ setuptools/command/bdist_rpm.pyPK~V>  setuptools/command/build.pyPK~VG setuptools/command/build_clib.pyPK~V}]= setuptools/command/build_ext.pyPK~VL6:j setuptools/command/build_py.pyPK~VcJ*XV setuptools/command/develop.pyPK~VQɶ+c setuptools/command/dist_info.pyPK~V\eP" setuptools/command/easy_install.pyPK~Vx$|$g setuptools/command/editable_wheel.pyPK~V j} setuptools/command/egg_info.pyPK~V ˫ setuptools/command/install.pyPK~V K& setuptools/command/install_egg_info.pyPK~V,,#!L setuptools/command/install_lib.pyPK~V3= % setuptools/command/install_scripts.pyPK~V])B7t( setuptools/command/launcher manifest.xmlPK~VvT setuptools/command/register.pyPK~V!P setuptools/command/rotate.pyPK~V4F setuptools/command/saveopts.pyPK~V$b _Y setuptools/command/sdist.pyPK~V. n setuptools/command/setopt.pyPK~VO\ 8 setuptools/command/test.pyPK~V"]Kn setuptools/command/upload.pyPK~V"(j ! setuptools/command/upload_docs.pyPK~Vh0P setuptools/config/__init__.pyPK~V}5-5): setuptools/config/_apply_pyprojecttoml.pyPK~VmΤ? setuptools/config/expand.pyPK~Vk!C" setuptools/config/pyprojecttoml.pyPK~VwHf4+ setuptools/config/setupcfg.pyPK~V[1F setuptools/config/_validate_pyproject/__init__.pyPK~VN#,8I setuptools/config/_validate_pyproject/error_reporting.pyPK~VGA:W setuptools/config/_validate_pyproject/extra_validations.pyPK~Vo^7LB&Z setuptools/config/_validate_pyproject/fastjsonschema_exceptions.pyPK~VUb<4|X1C!] setuptools/config/_validate_pyproject/fastjsonschema_validations.pyPK~Vk3 #0 setuptools/config/_validate_pyproject/formats.pyPK~V: l setuptools/extern/__init__.pyPK~VSEO#R setuptools-68.0.0.dist-info/LICENSEPK~V[$ setuptools-68.0.0.dist-info/METADATAPK~VI!\\! setuptools-68.0.0.dist-info/WHEELPK~V/דt ,j setuptools-68.0.0.dist-info/entry_points.txtPK~V;*))5 setuptools-68.0.0.dist-info/top_level.txtPK~Vo%U" setuptools-68.0.0.dist-info/RECORDPKK pdm-2.23.1/tests/fixtures/artifacts/typing_extensions-4.4.0-py3-none-any.whl000066400000000000000000000640461477560627500266100ustar00rootroot00000000000000PKy|FUIB/OI8typing_extensions.py}w+ǥC3Zq/b;$.t7,b% `0MUI6u^V--cl5߲y%o}UhWm ×Y_AO닦L=~ SW'dKFgM.jbdF~m6s 6ES}J>j_/y XlY]YQ~m\u檻<;18۟_?oscmWż5".5 -vnae>݌lzM.n!a6/EUe^8+kaZvZoܐYaʳzZAU-,,6# O:+eUͫ_jbF]\~Z[E*,*X(t2S_i6ҿba߿5&[y訁usJGU5_Ym;r^>>g_/jf~ȁ&,~+j7Uztvp 9IfNa/כ-fŪh^>9'S16o3x'9sM\,_UWIVɺZ"Gd4 \+@z嚪yJdjL f( uHj?t4O&sY:d0EL C:_z ˃]|R{tD ϰy]MW&i0>an'E\ e1*Λ`%gMuѬv~912J^ a 6@B(XHJ+YmLn`@_;P72WĽ㣷;/vレk7<4]>MliK73Z]O7>lhirqґk-MsxD%ǟ<FIEcUTUr3\g X+(;\W[1>g9H2` [XoAM(Aط\^`v piQ.+Z89|,\Vb*{ 5IiNN$e5 #=o}c Cyu^=a=kIWE;ky4D{ ^~s\dG"t@7Ue 2M!~PJE@uż% *<$_o5;HӓqMѴ`鞍F1L*<{5 7(~8.QA*bȅjX=rg\C4=>d!Tj-|VQQQ].t~@N[5 K#.9l'(ټy 5 ]yLpWρmXl!bm $lIޒ 3d83SyM0&_J}-žMŊM-[ms)x؏ clQ޹q6 COQ +)XJ {(C=Wz^d lJy82U^E:{Pot4v;z 7侃}:ҨZ)P> P&)EPC`J2'#"K96@Su0aF@orGwբ7^]ccܓ]%8]doF4x^pD\l*8/ 7E~e)`-ؑx<2K&4I+ & &eP~){ewt ؃ю9c n>Q掣L%cȹ?|:30Ѽ|ӣ'gUAG]_'ÄYKBV@J^;aBزEu JDtgsYo\n07rK8^yl%,*O(3- R5YTcX?O@|j ) ab#at@F%{;# A҂.TN_Ϗ,>{>5CܵiyCqG13?ʒ].|N^|OhϠˇ;;̍|5459Lm'הa\)Y - m"PhO(S\8MK!dfdm~$0,e"jHӯ|ipN?\\=tK8[TN筲*iٹS`@=ժslL%rabXsL_dM%6cG3L"ͽRl2B=}{M-{ܛS15Bὗi-9Q,o"sKܑq;h$5ю+\hՕR\@k@΂}Ѯ:QϛW[)ap.`.6,5 Uj wUUIo $%<7t<~U4Ń:JgGˌ\NsGVТ5OԾXĊΛM@#q4+$$Zޠz 3ۨ6MV̝~; ң33|0!coH(V1 *>=nR!懓2|ֳsFNnخ5d*-XG:)PGw(p6сk.:/w+B^1 P:e8 *2ca, ^xͺZl$ T䫢,T /@%ړc hiwgQ3S*2N_> @dM/~i¥ ,;k*vltj36W90U6sUᖴFa^BEYQ>6\VWW$j'=[ބao)hJXrݕg-j6}ou氲ʲ*YtzOv b[Y$!"i BjdC/H"b?4zTZP?.E7 r[O:Nàjfaï 3G{EfAy(Y0qiAr #:# `/)$K=~Ncj)~:rY o:=_^NTqE$ؕPEpr#!؁ ([ 6 ,yrL1wAN< xu&$5Vi?Glzj;QYW)!(k1aL]ʓ4:!qMؐԾ˩?Q-͓f@޴t'ҡX8h|_VE:mV>}#ߴpTTۆ];ź` Ha&Oeb kx%7nTTȹ:lс(ji*ȉӽ~~q9 *('Qݤ}G?L}TJ=rCU"b4[Un_ / =80ζ<9wT⴩L}*¯qRӏQ'O닓߼x3XO?C,wl@k\ _g?r_w>39o)RY8!*l9zY'WRY{P$jĀCT&CQ4 )RR өz=ܛ[ qb% ί'KUF"ѧ)X =oAghrGu/gIUM˨{_iVTzKiQ25pOس{,1YunmS`,!.yG>l(A[E7WU}1 ?~&b7M^Es44] j)VKM?ۘ2#u^g׳|-mZ1f Oh*k br<?*r ]=/bedU]L]8"`XzlSk2C:c|~s5[h׏ip7=H"48NN3\T{/& ‹%e,}YÄ lE`W5U HUS^ ^߬߱ʬA)@ڳ"]!P 6/H.kVV;F׿ giml[|»UXQ`A{vԁ]^:*3V% e%=_݃TYg ?fezRjw;BK,[c3OǦJTz;UQ #CRnv vavkv=nMm,r@]FY0ن/^iܪ8vw8eYQVxy^chsV#8tn%ݷ_l;݂J gmv4vO>V N! Vhx{uϳ-_?p_+eWDv]RI0;ɟLBmv+نI|'(S %&^Or'Wgn@2R'1Nrgϗaк2)YY{ c A1V>CK zvǴLZzN,a]cmWѺ(k^Zdq@icc X5x '^-ն2fX ྒ~rm: FdFo-؀O'g{ݎY['z1cv__xٻ/u!3*1[$ /i2],Z[ sed.ezHW|ؘ%Ȕ{.)=lH)t,wMCKG0ZP;2u)ؖǗB̿v_"΂ANN[9Yu;o;r>&66<c<< JGC(JŮ?HYC7?zC[ߞ ߘѼ-#eWWZ8㪾=P}$ u 䅻[L]BzةbɗjBTt99Mܢ8g8G4<kDYy4se}l$uל . ?A?@*8ŗ)yFz]վ0>Uy(- u z=Ab>MӋU5JC!vOl?e9EgqDQ{<{[| "C«jEK1yT/ vuץY_-!ڏ\a>MUo9؂vI֯n ߟr1 Hh g n}uVZJ je֢r~fvݨIW(5S2rN-,dz| Nb1u|v1Jw\wxDTZý6~wҨӣۀmQkx݀%|ΫNl;-d0|կLBqe4 7:]hV8I~hYϼh.?d{)K T9#w֔Ҕ‚6]4旈W:v b7~%A7p Mݴ 4M ]it0kϡ8s蚍^sr`KU{薅$T0j<ԾbGu %֨=oԙ&R#!ێh7[:e]Rr֗6-nw6%Df3tV,S\(AoOW,&a>ӏ>_?<V[6hAZ $骪Tţ^(r=b1=<:#fH4i,ϙ4juSnP!n Hx0cý?LB ]sO`FH50W:\>ArfA[-WE{3y3.q6E0CuBzHMynGHMTGuu/)(#"(hw)O!j'hBx⡌nE.ZyX]D^1ha$$Y^CG$+›|G]UP 7r_MݦIiwXlNӻQHгDEpih%4v6AՎ\XʃY7Pܬ s(/k {UCN\ڨ=,7ELpp_ߍDέ\m\/bnYT2|e܋28>JdyU&Y錿4u^"r݅kVk~ ]\R dM6q{j9fEPT ?{#ȨS45uoһX!i?ڒ)zHHz3ݻF6 ؃7MO`p%/yF=Eʭq\D2+vR-`^ʅU2RQVjQϔZy*I fwj gOԺ vþyUoM6uv† A ivkÂQ#^KUS΋B2]wdd]"$~d~j^y&*ŴnTyQ/Xηqx/iy7\ÄOL-"EU6Wk/Lc1x;]R:sOLx ŦC={$+F!G z,A`V')Ttz+μ M8i#rX P!ho bNq+_v h)7KMZNmLr(g!M׸?+94=+K-ݡpPf'RGIj?^>P#9l`Vdϒv*ӗ/xwBʎlڠF|GOiLPҏ)ANx݋83+\*9svf'KKN^0oЖ3lݶ:lz{)U!lWsiEs*CD!%ZkS^;sZ3fWeD$3d =g֐+w(cp%438~` t=z$erH;p:aG\pW^ȇ+%ƒ 2{ubꍻGu.¸<(j4jƅp;Ed5;G`<k_4&a#Pu&؏^? Eqm<<#B&y Wت*(; GsxLv@o[;>BV̀?xJ&!9x؄wQ/7ޮVąxؖ6,r|N^9{!Z4Am`y֞b1Jd I(FӬ*G@ rEeb&2:Oq~eKt;tykt#J$kS6y2\5b-9t?黜B;<Бk'P(;ÏC LO) ;2Kb-@ 3Y{ڎ_%}oOGc%l)k X21YdR?Y8o0LNO }5jJ򮨖8e\EVN(PBϟ'{)sxuP7Q ELU@{@s(@WŴ S6x>C-M+̅QF&3+EŸǘxzQǻ6жq`_ZYA.=mbOqib8ojԸf:G;J'gi:vKR:/-&ٖe0"{\L iD)`g|[Ƌą}CcFf}c ߇EXC$a`QM䊷ѓϰ>'5$1fWlgJc]}z@:Ge:Z@ *\'Ptx`1H>zF!a/y+:VVVe4ɋ;NLnqB1 &E('|R$6I~02"v8#u D +rFqw_qr\}eLI ҝ5=1$pGQ Giv9! @OB 99;!=}^N)S%={y8>]`d KwgC^&w1%ڦ6 }8HMvW\ͪ"fI K>+ :рd(Hxh9=l5 9FҢbS:X)8yh[{<'S:g7vSYUvtVE5&EkN)%_f3jKM1Q|kߊs+80kf% HP=wOz>;kki.L0cL %ۢd 8AKg5s"L]R[}O2Y/J KRF^jx(">"#dwfwe4,1 E1y=d|fFwO>~$dSi# =D]3!z=, m`/+ac2Xՙ[Me}+5:==bq\+mCR Lz2?0cG" <mbԸ=Gz.8!ERYӺ }r$rTnmQO蒏~A> G򋨆N8q%ތeObŘWB:dCMXiUrS#.u#[qgWN)'c Y-Npd3µXLE*)[nk"K:ң a;#bUHe4F>*$9X{z)ŷi;2to7co Q&Wyn]/(UI)[mJC}K(n gT:f{TKUc%K.##VMnw2Ğ]?;[*MXIuϰs^%L#nH*}B&~QMߛ{ ~:{ 4eg(x7xF9I%YL8YI" ?D_1fMp,rKkmRG tiUf>|Yе$fzr㼼Di+{ivEOҠ=Kqv P 7뵄'ҹ;,.0 /v lȕK~DAscPG:{~>2G?aL~^P GȤ6EMs LdIB:Y'7N(5Z@f'U"D,UuE}o}EZ9:w0pz,pNcZфr1m5!9qQc@g'^`IqO.&w(0|I 7V|mqE:~\FTA5䬛i077f^)?K(U kVJqe>gGv2#cWP "ת俬$|]892TIH6}ءCSWt̑^Ѯ AD$oTґm߫pܩp׍v[0a0G< Ĉ 7D嗼*Di+(OLwz .6@) |[4 vnhL4u.0̙qZBÐ櫪zM.`m=6ƈ-7*C `G"q62 }+ j#JRmp?g1Exf0N|TՇUB]&`;g|{yt53u_q'CeO 36p"y+QGDlsn)kH}~c9@nEr3Qu+\7|z.7QK悓kK"%c;^jR-4p6X-!̢<$0]Ů D]jVa-ل4,RP_+쾾};(,vIpHE6t {5V1$G1`ʮ+gʆ)Bt-*4-KvVjqjvG܋8O>S'Dws-=wn߈uOb_mV/9=57˩er#mբƽ0p 61Xr99zy!͵;Vr^^*6{IE<+]ȊuVK= d+C Zx>,xŊd0BNP mrNBm+Nf !X`̅jv-% o;Ii ++/Z}]f#.54tVHՒ4D5qƋLS^&Q@M+[fi̛,&y}R3sk)B|B&T]Gh4 q$+ XsPI xtX#勄(W+ _pvbLKkC`E`J>Xaa0 I+슴@$cZﲈċoiC']"#YD @tx-jzv8s( sEh]0 Che 4+ItAⲯMښRqvXg;x0q&L[uDoA4Go5tUD%%j5F ]dbݖddA8QpZaȢesRD8 nyWuT /$ynBM@ y. yvzieG3̊U^C} .ZhmˎNɗo/[#ֿGQrů"vzT8HGd_ %rVQOLtwr(^еqÿ;_S O6J}C^HOA=h W6ޝh9&]&]&]&]&]&]7?UdrQ< C9ޙ+JJ׼{pCCW׆NJ<甮-xYˉҕy*]9z#!Dl 6C~pXn8$z8Z85^ rwC/taշv\*7\KCRGg̋ƦJ {}_|\)W=e"rKj srs4Ij%ޡvÑ? C45_FΦlxTϞԤfB=Wհpgr!D(x"F"I0M$:K'^orm-נ 8*!5/=n@&N!9Xhǜ(fcD%VҀ3ofw!^8`/C7c[Gptlsc(4z-s~?SHr#&~9KTj447ږ1Lć'0n’I0Lh̿ s i7`tZwugG\rG&M_#.nxn#(ޑx_Ͱ"?Mͳ.M=;J&: XCpX»`E'99+'l7c723Ox n!6?;Eכ S{^C|u@>kQ[يoi<=w daS7{y"2`ѹV+ .taBG.'V&w&XP56 8 D j/+4oۇЄڶ`- J C jf )e .VЂ]k Br4@60⒓F+4 ߎ#4khzvk ƄMAI(VDHYg="ҿe~8X'?8XKKA5#U1ȬIߎ8hX1p^_>y`}"?;qX>󭠾wd?g4?\cl-3#xE 3 لrrP~knr |јi0lƒ9ɴ}w_(UÉ/q\NOV&!![!zsS'o86Tq슼r!2XN YyE:gS&QjỊ-QܜJa J/i{%ߚO볱 %T%|v=N.[ZaK'g~t/]s`6Yt[;b\@e&t/}aaGJCaw|@P#xT E[oG/qXdN瞣gXXlDIӯ:VwBo+x_y /yi&:1+m]_Dc(1u:4]Dmuܳ3nw˽ʩWྲ٬ }"%9.WxdW< `(6m/sN|[TC:G0UǥxsYXF٧4 ]AS 1Z]EeuU#fD&NZi?BWBH܆x[˒װA#Y?,`vcbEIsvA.Hڇi"&f[orlRlM"#[T<4yW兑bhޢʛrآYnBhI(̀/QBCid]ЯBWͨ~@tR;@̂=N]@+Lv[s^V A('p sX{Q4dfg0Tv01U7Cs ;@E3eK:v,c XOY)VY! +q Gȴ^ŕVK Wּ#5tmRB=ڰ*:Ahv,YNQG$8\O *dq t,RmV4X/_g*j^ ™pFC*fQU'9fAKdY4c9]ح/AwT#`lqz3>!Rd:e1+ELݼM-rŸuuK^61~1rKv9;Nt%A$-'ehdŹSF}!P 69YJ A"6u2Dq3Ӭ$54iD.W G4ćiH ؟qrsQv&j 6w֙f ۉN_481 e}t[ū-<2nu$];xHv5^i_7f8F5JTwW0ۈ ӌ[4oE|pZ6oN8^?κeZ)nQ:%<~vQ,5o+NOPQ|uȉ{AйXr#jm@έ#g9_"iiu(:/Gs'ܽZnA,1CE\C}垵}ϕ2VUz{pˎ:N7GyRDRzt:uKo*Ԝ\Xz:1+o6[A ftnn50ys_OLnc"YǬb(=P:pr#L= V }U+ rBmEy8_wOp~3MILQVw~X@B*'Ո~Q OU;*poa1a&ϣjѫiP]Ҥɽ&q/!n.PKDU)1)typing_extensions-4.4.0.dist-info/LICENSEZ[sF~_صx$֖ Z 3}Ѐ2B"_9clA.ڧNw;$rt`p;?!b&rrP3%X*,l]R&O\ɰAME,CUXDt);*)2|qY2WJ.b۷ƞn";+)}k,!y9gՍ!\gFP*\UE*n`{ ?)bb腓0D8DeF+%p[Ԓd7ZDP<9LFɉ=kad,cMdGlܲ=7~_3f|gYf :K!|&oLT%_i4(@qOPiQ®abk?c atX^ȕZ5<1NW62*yvZ%MGirGLeal’,pFI[FQk ) L vMb#_tiZYYGym̔VV"}B$UDU7$Y#?_TΝnDc1QņˀE0[V ּd=F3ȃg0:!h #P MQ(YAzSBXr=;Um5#.!4bP3K%E „ePN~WTlгaSr "SXNbIQ4 S@-bb1]l(NIVP6+!7| A_Uoa(R::^Y6d< =.Ai0B&QIIx/Cts<.$$73ƹz=7Ce ?:ftxeeLɏ^G 6bt]f}Q_k&7.rnz B<;q^uF``v<PE_cu;N 'FA׻- |gK%{.wn_z~]Zx| g|['nżҊQJP 50ydCOJd}g>5/</! Sx-B4nymj) }tآ|se2SuaZ%Nr R)1(2L_Lk5;+dVU-T,rN0+Bǂ9P&\+*]dޖ7[<9!R, C%.yB`Anbn `,&f | tW0 O|oOk&;o/eޤP:c (BىܒGnP|><6_r=P4Uo%/> ţbp<qہ]Ӑغ7 k*{ONZF)C fV=C j4mi d֙ZywtBgEzPuޥ]^ )is4!Tt R{G%cyѐY=gw9'Zvu<`x@F"Ny^9(*pMmƒ׺|<}.u/n u4GkWm 4;eD<3[tc8jAjq`yMv8eGF_ūW_VZmg?~G`|wOG^uRr`0Akg|•%FgaLogMx,}zk.1-jq%M~@7;H }[gv94RtS/H 8c D7!OkdHvIV-ui9>BfjCzϐ hqP̛#_3zt`^S{:ruRԷE;1=>8@K?`ڡH=̝5Z Pw+$? L4H _EkW#y绝ыA6[v 5:#HzC.F(|Z;qkdpGclHz j Ck -PK!H>WRQ'typing_extensions-4.4.0.dist-info/WHEEL1 0 R,B> n"sHw˜`+I ,l9UT+.fEPCE1Jw?PK!HZD Q*typing_extensions-4.4.0.dist-info/METADATAYmoz1"8N9n۵}A!QrJVf1@I$g80_)|F∽K#fץ,f#qoEA{&jį*Ϲ^dQ*mExRhݭK>6U]\l?~2,+SsBYn$0$s,d0 `G iS>SwW@Eevľ;dؒFSjB̭2yi1GCiLF0)p+¸"70@,A{L`ǿn\[%B=2c9oM*?!̕E.}q=F,^s4 p s{wm,B\KF1]RhAH![ J7ڎ !z["p + RZHLԄ4$݌慁s;]9㙰٭NAulhك,j +7ߊlڻs?l<7oE^Vyd}$Z}-J@F'*8C^o Gc=33 3dLn;]ؕ?MebO}0o{ar~),x#CGwBw#jeUzo1^#7LSg(H&^𾛉Zdm!@@s=.gg{1m 6( A'H ~[+EsIҖ}7~JLN4/K'χPV(M{>FG#G0EQOBtJF@:V4*;`WCnPT!ƴD'^ ,W0.͇MFW{p~xN%vqfj8Cc-V(u;%.2R]0-) z#Yfc"& ߌt-d_ǑS0޿ މM91wS{v%vEOJM\{juKj uI Z,j8i!PKy|FUIB/OI8typing_extensions.pyPKDU)1)Ityping_extensions-4.4.0.dist-info/LICENSEPK!H>WRQ'Ytyping_extensions-4.4.0.dist-info/WHEELPK!HZD Q*7Ztyping_extensions-4.4.0.dist-info/METADATAPK!HL) (1etyping_extensions-4.4.0.dist-info/RECORDPKtfpdm-2.23.1/tests/fixtures/artifacts/wheel-0.37.1-py2.py3-none-any.whl000066400000000000000000001047451477560627500247200ustar00rootroot00000000000000PKbSdwheel/__init__.py/K-*ϋWUP7363TPKbSLwheel/__main__.py]Pn0+F|˵HEr`)VJ5N@i639gE~5B%{Z'}g/3.9ena 4ikxa򬥟ML )CUBIꙤ86]^'VЋ(,24 m쵣?Vj46bwۊԴ98^vmzT|^R|,W_ۦ\ى,VZ\&OXM{V#gћӟ_ӯOOtnQE GbՑ}Wg *m>ʳ9_ID}?PQHiĬ'FMW )p> 8[:`S ILϣ$E("?VP85/x7e1^r3@+#[DaF(z];{J_EǗ?KR +V ,e׼iy7/껌zz( Z2.F(QLC2ZwϽ9h_F/_xp#/A|Eox)@MxrdX/Z "!8wYgRqg#g ee$jͷW-V vtfgD2{ߋwϟx×/^4'J_{~v-uRm}:?l8+8Oyʤ3#d05-_×,'^< aID;" ϑ~tp?;~pe8#>u up6ޱ̖ "5o)md[E0gCj$NS^N"efooogM2nX< TY'DZs\. Js(zJ en; ЅF G[#ZĆOc)T'rijtCl-LBMa6~nퟭ33t޻A[o>*Q{r֔M| I/EK.԰ 5N'hrvޝLf"(=z;ُA㴳a/ıg'oNOg=XrJϞ399s0#{,<A)>xtζsJ9&8m73ɼO*g\2 < !_.աK ^f?FCM:"/,zwrŀqtT9jΔ\仑 M$Z#\I2$2N"H權 UVj9:_=e$#J628,Oٗ}JDKoq$`KL"}U_LrY$K,.=9@<`#:QhX!t9%/YU=Z"#rYñ,aD+Ems6cҀ* M9N0XQk1|`L YkX3IVLRzy#7"MEV>0jVƐC؅<}Ώؓ D4S-1~Vm , IsFHzPd!C@XŅq0>ԓ?9[)i.شUr}<OA %ѐL' Av1qIׄei, ;rمqQQYBM= 4c$*bWsj jb>г4[g4^ϑ= cm3"k\?s|к!;Tä_e_eŒvn 5Ygs<_)4 e5G|aZ'"^J\ ٳ#\49琴^m92Um=N5291jtRQGwMJGQN[;caq]Q"e'8E#9tҖ=咭|~k3a)E|_!'GYJXxұȬ=:כmG¸bcc:F?w+uAhPr;dPjaӺ.k{:΀+E7o1|gd &a$ ڻIp_!? ??MZe;&P@~|}M,"~DP9M~ zIp hKq,$HUCA7gc}fGKiJ/0 $|';vE&s ~)Q+e z4+tRjEPwk0̈eG 2-t+)T %]%L,{-ee߻J]\FJPʡnh\݉k%(гUFE̕ԯ]ȃQ1;@15uKFsxpfsV0T}@i&(mg24CDp4?x캀8d7Vپ.4͂V*Gzs?`EC~轍ܓvGua`hl6t>9R^˄h?4C9RG28 AH[-;Ħls&wXͲ!GZn.J*uAD?tjLN},52ObwܴE`جDVFhvLdUʹk{-n?Ml_\ ,B1`(dw:Хl'/7*"L WV$׈$.T9.fl'~ޜ^;Kv@Y Up>1̲ALzQ0 K=ARF{(KCi^iYQ; \1m/QhDcTyY„5TũpdMTCtmH9sHW,&W{<ٷmR6Y7DO:$A {94GÄ-p6&:@x=Hl7Z)`G%+9 r,_x P^gT}ݞƔ5!L=9C-9wRgָX#ojS1g;ֲ"p߭QЯߺ(sRtDn ''kn5eyy6t̆؛>Hz% 8`Rȏ>bFj|`3 ~*#qA2TTqBf5cn|ߔhZ'C0 sY5ޜKZ Hө6Ӡ;HcZ^N_{{J#.HY"-Xѿ12tyz$gA|wr]nXVfq5Sy@l+Av Qlnm̮3%ݝG[J|呕s~\ $VʓRj1O@J&mY[eκ˼V^wP{svuq$lmV0Rw>D=磳'9nf=:fbN]|XNTGHbڂ>Y׍P 32mAuwi*Pu 1}.?|E?Ѭ#e&-)i33~٩kQf|kpÅ9]s2C^|6ŏf jQFWY/*Gm=\=4E6GřX\Z5)Ȱ`;F#rw)7%]%toɈs2?Sӡ1Hw@ǘx-->\Tx21?O n#7bc65=M<_B6%P΋A'<^Z'~}{b/K‰MT0QӠx&]zտWUJ=pFb#,G 9D.t$@¦WAl_Ã]cwE}KԖa%Jz596itch@(fN[ns\}q^lKu\=QAzIL͎`Ľ"mq̡F7jYXĬ7 UL#8M}M%߻\ H)H9taJ{NN?\|>pr~ه%ǟ/tf"'QSX}/EX\ T{ʃ*F繡YC//l5. /7j,fx}œ̊vdOMѨ,̓Ԕ;D]:cPq}:NI8:Ccf~/QKZМHNU9Ίf3z=`XEQh ߙ8JWBWg`N9\ T,ϊĪdZU±M&' BwUY9#G*LقsS{!U4 (\і)|IBZ#7'ݓv~v~t-S=u9?xrI܋{).~Ϫ)o{ ئSG8"-QAb wG*:F\ cձ7y{zy 8IYQ2q1k lĂ+ PKbS#e:>wheel/macosx_libfile.pykoFs+B52+;>pk'$ݴ=`WRb͇@R\|Ȏ{;N {`\̣%t diɣ`2 (KY1x]6]<Gkg.TUdź(E¢4#9ZHxͲvƃw?;If b%˂F1[E<O2Jg˨ R̋=a,aʢx)pJe2ϳ GhE$Xab#GX"xZ8fDfAی(QRqoWzsrx!6Y9deZ!g4 ,>P)$S1iYr ۱9kksKpB ]֒Y*j3ijݥJ#j0,:Rj% O%IaA7a{A;A2e[Qq$S% a2+yt%foh$>M-^$W7Ggwx\db{m?#/<#/<}FoN*nT7A2r&"KRt>ɖ%K4Jp26pM"K^נ-v"189JH$8ˮ &E]E2 (3t4'EF0 `eM3 % ZZ)T9g-xpgEVWcA.p)l 2R!R;A e %(?KZU@iOQ$;RlhC.KC5+,!WA -d|JE搿@H2y&wj'\5Z. m]T'&AdXuB33[ 7& b(%IR]!/@r"@PVӿA@J HJJ*RyFVoeՈ c ,+^uaLBs`!^"p]T+o00;N+g;P_?pH uKMK0ʤ2Kpg; ynG:I'E4iv  |C Wʉ /t[#G]>-o V#;Tz `$\3t*ɴ* `܄$m5(TǢR+Y4C5)_s圊 <44ws:,óKwi^7b }^#X\)^Nt} Һ\1JvKfDPvAbjT#q+uWZINow:o1nF uh.@ |]+ 4T鷯!m_CJ`td͊O;'9PItopP$y:`]Y^0=&@}g C/=ٚ܋B M3HOyۋ.A 5J%#e+B[\5U$` ^3 *FPZ؇\y3Wi'2\(1#&/ QI|,(ԯ|PO |['QnQDMFt},v{z> ߽=~w?^~G2\ЊQJi; ~{" 2!WHWk3?vn yoYļĝ,J z}Eۻp-~[d'![*n`ӂX7a=Dk?ZPu?F\b3E T(n . Gt)nَ\@iL+lgwƫ?,+$x,Ạ^$\f) JҙPAļ(| c8XbFBm (K^_LczcWo0+p2B%>^6A,]áG~^9UQɹ|殗jntY=}1Ä&7|U. }VQY(EcS^rk*v0mW=7ٮcV,5 NsH>0U2l!{#6'!h:t;j;g'ڛixdm%ƃ+Ϥ^& :N^t״VlBPEk/ޱÛ*6+Yxu@VwZUbtY-2̠!{m|'|H=56hgPrŸ!ʀAP ae]VG6k 0hfsp:NI'e#{ՓJ<{taU?N ZD;5uHgЄ?t4ze >; XݚF؅pR K&x&UD<|gNmWӆVQs[}`ljNn  1XdETSs>]}\MuueEgoC;qk-źUC5dVQqbm :yEVǦS}.hkD´=m JqUe@ihҗhC'zmк F@.ՈG,qy)̫\jngY9bFa:d,7W;% ?3pCt6_%-:%j 2>Fk^}Ĩ?Q.{ ]ḧ́R`xIysu}|ԶB5 誃@yI 2Ѭ2?zZwqWHo}0ND4M^z @M <5T'Cf1oٓJE epMZ&6zpwj6m*ؓ+Bwշ˱vnn rKZ뷦cnL خTּS¥j$񸳾l&Fke/&iA rS`/}zI,p |Y5Ѿ$oiˤ]Pj#,ZA뮕8mL];nz0 " j$8! >Ù]6A>BBT/]'6i]d ѝ73K[06;|i̶͹$PKbSXwheel/metadata.pyWmo6_A($!,kQx0`maXـ!5TFl6tcII8{8UTĤݚ^-HǞJZFLӚjZD1(E| PEOf^?Iڏu);Y1E[R!Ad.Q(Yg8(Z5W:u߶2"@$Ɠ,k(U`+Ro;u92B!)bG_}ấۇ]Y2˂Yq| g/Lu2\hcrz [ LI';G3,`a36"%UI;Pz:wޡOV|:@`7qSs UljMXjT #`g \h,_PKbS1wheel/pkginfo.pyTMo0 Wln|Ȱu9䲓ش#̖ F?I.i<H>>Zknv89^@x kREӬ;dO  lRϷ ք@{O ;fǷ:FEٚ,1;U~-λunlOp:&$=ۉy  z%aҡ.Dud 4!30ZKcMedB?ӛ\iIf5~x3jID%9 Q<fߞF ] 53~&o]RܵdL/+a>Ӿؑ>|m qz& +̵v}H+HOPKbSMh`J wheel/util.pyRN0+VE" hUUp@ENMJۄؓW;}G C*-Φt{+?KZ%rr 0Yp %6:C PS.i~'*3RoCvK$C{.n - .aEõgkJ4>E=;pa߫ShܯPKbSI wheel/wheelfile.pyYsԶ qyo,)4M0 %ǐydZf \Iqdy/~."'aV, K!5)%/4-bE1ԯcunבZg| HݣdNH7[n5$E2f('L&L$\JĊD [1ii>2s'<}8yyZ<]Y3qݪ7b!](*(e(aH  " ![n5S3r3kF T`f h}ҧgodToC%!8e/:-@&io9l=oFܫtz',S-bR! 'Gז,!oU^xԉ2\'O&(WdKHkPD%LeŁoQK&hKh!H1+h<WQRRD9SdórHFVWQ*;DhO ,+zG÷U0f?\V >ls)2s,JP#٧v_}i+!|m`-o^FwQU{اmMpP1֮ mLZ]1.C,Kg6LFkq ƙB8] 궲k@Auմ?|kr(]m[Ulܵ_ UpQn!xqh?n 0iCA[<ʋwTKE z}c@Bכs$:_?y_d[M}8uXA"sNMPHu錔乕aUb72Mp !-^E2^Hs`p6@3ηײ1J\"1~qư$|yW_Εid l4vJ, mK@ ڎܠl'FEܧiql虽9~S0j:=S w앰 }d!b^I^C쭬qj<KQ|q4A'I u}acG$)t3/RiY93(i>x1j:XB$2cAU{;0{o4@c[Y?FI='=;?u#%lmzLEjT+lm-H{EJM:ZC.% )h/>RE&9޹ hL;yX^adwCg`YN;K9iyYNi"#΄y>ˇ)e1eۼoz׾O9~YT8eq}6@ ҿ*Yq xVqf x,Fao|3v}%qnxY| B龳N`Y~ ɽ.bh#@~3P\q ;3PKbS; Of\ wheel/cli/__init__.pyVMo0 W CctNrح0h(6%WmK$ߣ%Jyg?=k0p٭z!MF^|pѰZk mF le^S0 3򧼷 LQǪ7>͊9<ˀ<ɑwͶ$u0E3OP?Z᜹qh} x@f Mf}i؀L1`|a·gkQHR##MDƯ2wj3 N]"uڲ'68$]Xy84fw0ta✹⇯˶^ƻX2* FŪKW4%$i |ͺ@6_bM c,mXd/"}"-: (eSm^ DY3ͯiwU(./uWU$ݱ]c%?IfrM~veED9K1٣BK}<<Pڎ.'kP `+V,+9R|u'8֗K;/ $m1QϣVDXGS3%{H{UY;ؐJ2>`7 `[zȍ*X&*=aԉ2Hsx0 V=?kzh:zƇ/PKbSl2 %wheel/cli/convert.pyZm_k%_IjFۈ8c+i%m"]Ҋz3/\A(aݙٙy[CSVԶhdhiTxe:ڪCev[SFۖ4ve >&n(V<*@2V 0oiY\?56ssZ6;lݙ햺K `u}h@(7YMɫ?WRvĺԪ^ԢFooVve+vRFj#VAГx@TД+YeEsjxFȕ&"SJWؘ0+Vkxl01V.7b+iń]*Yڮi {LV f1翵RLߚRxhHVBTz02pzVH1f2 =zj;S #$)hcdt(mt\ȌЋ<d,9偆>HF6R[<{^}=Bkq1+`@&S/v^[ba.pvۭfTWid2-<8YA7G%򃂹851z_W]7dA# Kki[& Ȥ]\e O-mM#B_"KpKc:ԺJ FrmNW2Q- 5J9w 6wdægҽkgruw$hhv&S6;C\# ?2 NYv9Ni%o^t ވld{YÍ[* ~&sn.ΨR2{1UaJ[^?W|vɌo."O|@qxRrŻxVJ/_ I#ө%LگhBH;!aV紧s>/7+=gc友;,~Aq~YoX@rOvųl>\=bn2&Xjumns2vګx[*DGlLΤm c28yIt́]`Hc]erh9Onp:>u]T+7SB,'M_F`0x@ʠƔe2c2w^z2S#"!6>S>BS40Ͷa6"5DN:Qþc[A*\Kd_Hoz8!ks+efyBe]%q ᗚS_ 2y؜WTS[\0U@HHO!$:qfֲ!hI[R3Vbls~Hq  Ǚ}]Ml$/.2ݚjHN뢢˟RǽFp)hq)غ6t2<<_aDM M^=`hYPS7" g՜E/.PT#uIA|MU{F]/|,Nҹ;V` H,Dʋ\:F sYt: &kld,_ G\$lg R0.:(۾s˝$9%txMpג-cVkfX/P&WmUGGe@*}C+(&,M@*Ze KS:8먻vUMޒbhBn5*M ˼5A?x%Z8S⫿s;$MqݑsC=߇|j zI~Āf0=5N:}VF>B1ʱbΪиT$64FR=R{"H1ywϿ.t|Lرﭮ!-he~ƖeM`1"P,c4}.T@X:~\Dh$S$>}=&Hd2E\^z(3 oݠ!# MG3r)IT{TH)@ ]*"}OZnW흸π K$UJ=+Jy z&I)/.*:Adܫo?Ϸg}55dyi#֞`|qVvhU¥F"s-վizIM\'HS@; *Pl^zV?|.m_p}U[nNfu2k|5I h.2Wx^4I\J~6C%8 "w#mq,.bUI[29G^c ?Χwp);aؓEqBWŶ> W#~zI[yz7PKbSߕ$ wheel/cli/pack.pyW[o6~ׯM P$鐦C D\%R xACFYR0DwsHgJ(T1E)AH "eIIͮ}U}O:2kkc,'I[S¥RR;9]B\|z\5x!,J -#Sg9:]ݤymHʵ9"oQO#[?Th%2;j.f>x eaUI-W. * 2+tO\P/Un42ETJ1azHdÁkuXCQ22~,8 {ၓg- V}[r3 Q(D擔$Zά_(kxxO&̙* =`iS/:3o!6aSE Fn0[ h# q]CDcKؚ % *K ́= K~߭X}('vm}>VF -!4ʜ=Q(^ѰF>q:eW<6ҡIV;`k;fjǟ둞S3ӱWVOkHbeN|[föN+3|,KT(fзjNu 0ڣ5 9Cth%*l1ר[QLHdBLu9FN"[lN'sbFLp|⌥vM)~aCmΏn GkfH-#kF'5lɯ5+9翝v_, AB0Nkź%:"s_tMz_rIRB9=ܝ;& __&w ,p#na,{͍u@ |h]v Q8%p02$+o(bл_kPKbSi+skwheel/cli/unpack.pyU=o0 h#VuХUGs%Ӊ85ǯtލ4LǦ3NLXm8+& 'EA Rzġ3 l'~_@\e- ,S$IO h%'3  5 NjU#.KjcPiPVCď;֓jZ[ʤ1e3x=*h%P<g5Zʽ>X:4TS{fx>nxˣwT۞f;o3Pco۱{k,RLvΏ;hYZ}J!i79-/yՒuk%PKbSwheel/vendored/__init__.pyPKbS$wheel/vendored/packaging/__init__.pyPKbSn_#wheel/vendored/packaging/_typing.pyuT]H|_h#@a_ǣ5QÎ}gl,4]]]U=UU}tzֱvC' QGk(= <~w0iEI&v 6Pt S c}S.5;gLԛhlWnJyg Oܳm;.;\\jk:|t$0]'h(nt&N<(V'A\TtbE U%;z׉,bПIRVubj\9("= L}##$^᭓V>PQ)1{ʜNIl G{+%iZd-MC #.Bɺ,E뒬zn$H [X3)ɪ*E˂>nn?oJ=Οn;Լ~|R&ְi3m$Byߪ*v;;TbRH) =DAZj>BRYi$C@;$іli<-קNoT/(0W<~WeRHaD2~؟ m+:ya'X~v!ۮjk-Xen!Uxo'6~O.n+g5̫rhy*op.Md{ьU&o闬^EWmn--Bx˹VPKbS(% xs wheel/vendored/packaging/tags.py=SJW̉ڍlK$`ٷ8Jы,#M͌$fꮎJ%L6M\ip*HX<-xĪ49+o8+y>/X6EŸgyg&z;}x`8<8.ϲR rȊ%f9 "%g/8)i͙Oʹx"KycC⢬Jl76|b@I<08R!<%{|v_|<:: dpYMȃY:klAB/=?mAܟW3p]aٯ<ƫ33j0Ix ~s⠟ o.OCe0Q~Lx쉙=_7Y*&UR _>j k@66p~AjQLNi0EjO/?8Ż{ԍ R㔼 qC .jؤ~Y!o7fy}|$ zwE^ِ _OO?>d<-.3vfLFg=?4vҋ4QI0D`Unfk -*QE]GB.]94A!8נ!F.M~={ǡr^\922 bXl<9N2rS(yU^7Aqo=v;p,. W<<% E@#>~de{xDoLb< =`^Sqn)XIOI ]3*Azl;R^!J$ 4$3kCW|  h=0yKVkax0 5*GЃCG= p| -OlF` nbh? Do HTa:uc8sA!P`֧hF(հAAiVBmFLj#[XPXYh E Rx ,C]䘰tO66\݇c:߶!#qan{(x|cfo,jB8}$Q`qܲP2,Pϱ}A 8ƈ=7>-|]z8)CS VP2%htZ"Tbt8B=fEOb򰥊w#BR<|2c|/1uyBJ^]gMЀ2Jcb)@O}t(8ս5-+ayA~O@ S=-( Wgyh MJCZ gm4Y׶R g9%o(V{?io 7:+zO'2IV&K{yDhjb" @&(KPfe(6/q VHzJm5-BJ :t",y +# X$&(-I؛\/g0jrl/v\/GM ߢm {~C.m~-ّa* 5MZ@Mpjj>T$I~nSWlwONy5i1U 9;H0:t掉E_Tif;vン7(hlߊd,0s.dR~v;Gc>Uv4kfve(cdAaL{_n&Ssi>\lH q-ʰnN1!%" _h._ivVN,V5'wt*D"Wb[ט~뱌Շ>x U)|-]+m2y^`]m$\YfJxgAF_Z}S?O?ICwmϨ טɥK!ҋy5/ %"),&j%0~!Z .AL0_ 바݃sno0|xP4 dyvcwH镱)+6a[rş[oڔ_|KT+t.'NTmc}oPҚ:؜elk[4c_ʣ-o\αGI\;#oqB jm/b?z ^QyYػbB7D9 &1zᢢ6pq\+ L:w]Z)NvUћG9ȽrpiPva4>l@iZɿ \|XC̔ŊMvy;!Z`U% @@P@l 1Ϫ x?u 3St3TRxvs1RX9* :I$})"}5Mi~H/U"!&"ֶӵ7CXZ$Xa#U,cT])p$}X:Vf[1 O鉗P`V D_8FG#1 4C @1L <[1;r>EuXaQyc2~P]3KP3縒g5WR ѳ (¥" GD=E'nt0:1(j'с[[~'T׸0L;x8` AAuKTyƒbC 1 NJEB?.6XhNN S(a!mXp6Na(Rl5Bm,\@Yq˕EbXfC[۔^SQ--ɡ="R/kNŦ!& Q8A!ryhH b_-5C&/5"]iTοFfj)a"|>ھڒ:ÍB:V=0Y{ԥ,hO@m T0~ѕ.C "c7i2j QMbXqK-ՙ^xXin,ĩMr4%+L`F:?GhŸD#ɦmd5 (cwuCsMk+`Lx)U9l}<kWB tM&;B'u^f<<{u2x>j;]sp=I~o'e_rAw8:mDL ]jn8)zvUCLJ ,m`C^f,B_tKhMaD*|>t)ZoI:F**qBs\J\NHz_YCUpdVu~?Ns! I&!R]zE@]J,=qg֣$[=xrH/$"t2B~]* T2Zc<ܑ6X/.oɈ`"h YUJll=)aVah+Vp6R;I(Y,e `9sP^ )%!82pTYuOlu-( P֍"1S\(T*j!mqGc0&JeCMEJhr9:H3:#5b̑H >V,p4gqID?.Aʉ" @@bnHDlpkyvVN ֹW&Geǒa= Dmw& ɰiʵuJV"ҫ]R%'hE,heNjm2.@_ۨ[|%/J]Ŭg*DΩ`h+lhvy=lS!Ș&.T,l~u:Z6^po#)0w}"j:AqcрԾ6V> kS#u;S[i}0Thl+'@4nP(.BpU:yLoUYEhEXP7mm "^£mKCMH\DpIaf5{$(jlj [ov5z`X֋.lqsKJ{+zX{~V%8(C|:n(=$AmK-V޷d'P8fJ_-^S)νYU WiOe0RL* 72Q]A_efVÜXLV rGkŽfZķGEY_8mQyX J.ݜ+@VImv ;ӥا7D^+x 5@Mp[PM7ܢNsTnRrT2+c`p"R)UZÓ#|xu;EczS6/x^϶iЇ.?hKd)sxj( ~Vg=>O?M=;}{ N/.FCL[hPz?_36~k~+?Q@v%S05&ާOK*w1C]Inѹ/B}°Cx//5z$gs>vvqˍ<9=}(u,:* #gG hT in苪ST_$uVF]{oY{V$7tN1RXdb56ɬ̆VrC^6 |u(H[٭kRbl 6Un?^$oWZ(p* Wj[JASpR6>uNF\0#o5]g7麱ľ.[8cҾ'q% GBM!Rڅ'K";ë/1qKo*',X7G4)9Q5"K~zg-.I^Z1̳gdqi8SL2+\$] tz9^--}.붼(64S꼁PKbSpe"wheel-0.37.1.dist-info/LICENSE.txt]R_o0ϧ8J۪=MS5CLc-đx4C<f~w$DyvgPh]>}~_ у5=|k:~oGmy{|=4Q09ϣ EMfh;:": =R{W[xи|4CЁZw%>1;ݽ^ņΝƣ0blsC^{{WƧ<)?{t@:c8ƶ5y[XM SJq>>{B{."ˉ &Kuq@J34#:P[Bp%Guz~w/cp^%No[]NfD.b'7N|ۤ'r(2AP(,xŰU*7`byȓB".2&eID %>PA+ Dx$5Wl!2Qh%0WRJ,7SPlT!K "_)dkWsdg<@, W)V,X\pTRe:\IDQSUlS>oY +]ѭ(y1%J d$S8!'6-tޔMKYX% oGPKbS wheel-0.37.1.dist-info/METADATAVr6}Wln5KR84Q$Dz ҖզM v F/r;ZMit"nd lrB< s͚v.GBȌ-[J7%xErkJYF)z[˸1lH&0ޛKT·o3<DY)o_=%)D+km^miqSQ܋5bwWƤMdSȢT>GeRi#GVX7PXeq, Mk-mkVvn7*GW+1-*%p1*s:Ӝ1rÌn$ %դx&Ja4V)23F{oj|uK m53חDp;Y$I!++˒^Q1PvvWC#~:qp#~9qr,S-A{wgqzҭz֭h}s!/k~L;c|O7+ kAj_G\;( H.yɂKAoGU}f4 ):=_#!&rǃ5D#}S{c 7g=k<#xf4.LbTyY,^R%Qlt/{Z`\ r [*Qz=:(nyê-E,L!t4g^hpfYlN'(yݵl:T!2v+\5֎ I3];$ i0u;R,=#/nRa84I]*zcVZ{wg#S,.hjys˸1l#;+\JF^_R5tW3[*wk2Җ13Ea6b:{#;韲0˘Wv|9}7ތFQ=_PKbS_nwheel-0.37.1.dist-info/WHEEL HM K-*ϳR03rOK-J,/RHJ,./Q0363 /, (-JLR()*M ILR(4KM̫#DPKbS7Il'wheel-0.37.1.dist-info/entry_points.txtN+I/N.,()*HMQUz9Vy\\)%%9zy)@I xT-HbVHl..PKbSce$wheel-0.37.1.dist-info/top_level.txt+HMPKbS&ywheel-0.37.1.dist-info/RECORDǒH< aH8'.0NX_D;1}KU2(j~2HM;?F!tX=C=OkxDQDGT"04qǩE2ΟKa\5eI545!&@JBIVS뽳%_"J|&&,!]:B:ի.M~jMv3$jO k;i0{G^bV[iwheel/macosx_libfile.pyPKbSX`(wheel/metadata.pyPKbS1`.wheel/pkginfo.pyPKbSMh`J /0wheel/util.pyPKbSI 1wheel/wheelfile.pyPKbS; Of\ ;wheel/cli/__init__.pyPKbSl2 %R?wheel/cli/convert.pyPKbSߕ$ Kwheel/cli/pack.pyPKbSi+skePwheel/cli/unpack.pyPKbSRwheel/vendored/__init__.pyPKbS$;Rwheel/vendored/packaging/__init__.pyPKbSn_#Rwheel/vendored/packaging/_typing.pyPKbS(% xs Vwheel/vendored/packaging/tags.pyPKbSpe"4wwheel-0.37.1.dist-info/LICENSE.txtPKbS zwheel-0.37.1.dist-info/METADATAPKbS_n/~wheel-0.37.1.dist-info/WHEELPKbS7Il'~wheel-0.37.1.dist-info/entry_points.txtPKbSce$Vwheel-0.37.1.dist-info/top_level.txtPKbS&ywheel-0.37.1.dist-info/RECORDPKpdm-2.23.1/tests/fixtures/artifacts/zipp-3.6.0-py3-none-any.whl000066400000000000000000000123011477560627500237650ustar00rootroot00000000000000PK0|=SBq8u zipp.pyY۸NXN-0IPŢ 4Xh(sDzfb>D~I(YvA;h76yn<в^3ٮԵJ>u\O+e%R^mmxҕ̆u$vVAJM*e`ѷ1zWm aU%r Ŝ۾(\DVَZ)4‡{h^B,x/"黺2E>qc*{DML q-  wN݋F>ZTdzѓa{ґcmfcv?3zn H@Je@K*bvIxϒ(BfLDJ"Jy t^컶!O$ro>qkERkZM_GrF6VemrêxU"%t)!'44;Kyŕboںoe"?dwggz:oMڋFzM+)d32(t#?;#^ׂlx1n6D(asD7$Z+|b@C!zq}=Vy|/1'v$-F|B?)&=a|^pͩi?PnKiSLFĀ^q%=rڜ#ζy~؊1e cZ[Hp={3!3\*|FwGẠۘiDJw QC-mG#y 0MZ j]EgʚMA 2B%O= 0% CjJ:X%|gjqJ(**0?c[D3_6'Y$7(*ySZuΚA YU ^dNQKFZ7i@ i% }=ʯPXUU`aj; M:QvwGowwvs󠹼ؠV]+06^ˆ|U_-W wwr xK:[j -H.&Mi@2_ݬsBٝm?̮s/#JSwzlܸ]]"2 |l 8af=}j/Loi_.h3C6oIrT8 }ynF痧9Dxl3:fJɉ1@ő==z"}|3Ld"n  "WR4 5oFmR$1mPxxa>=}1DPu驜\!D4n SgΞLouFiF SKj)7)ԢQԫB{6v& y;۰Gѥx8OGGxp =nYL-ǘj3=ozqy+|EKsg{7nɸv9'̯gv_lĉ u(KXS5v"BP :-_ jzN@_]Թu s;Y8'l5L Ðz ӫEs*sv/[eU3pAAg73XSïW۫͆0y0\ FG /a+1Pb.$>`h EYQg_jp8?rt$q0|-rsLNx\cB,}Û6 ww~у޻G\D' ߧg籿 ./8*w,pTT zY.&Qsn;{y)G}FP[.g(fVș'+?m iV`~4Op6RlZOg|ꁡg:՞{x|7%KtPK@|=SYď`zipp-3.6.0.dist-info/LICENSE]QK0W8RDy,nC9fYy*(6E;WHg{ͤ:SWz &0VBpXuz;NS=F%pM zW؉h|k75(p2H1puu5Aَwt -N"n꽵b&ZHpyxk gs@ίξsG9 . tkbbkGBa~b18=gp7ϐʅ+uo?M;^%nd8ɇwfc.Q#RkZYf"H`/F 9Z/[YԢ@i&e.dLOB\ r+ $"|%si [KSZiPrmd˹rKU ϐZ؊,Qk Ն9I1CAʃOg+*w) \n?E3FP8~*g)ye%ZVhBzZ'"LBYh"8B]% !k\L qjR*z2s;bmRrG"ʥ֊\ ѿ J+8 q AK&u6dpaa&Xa [\aQ?ۏ 0D];^ /[:}&wkS# ڰߌ|J7 \.4/7WoDFW1d*;7º.E~60sopK^:XǤהO9e?<:K~b /0-1]{_Ia;SJڃ@yS#1t^M^ =TJwɸ諑ʃol.g/ឪV }7]Ul)Pf Vhu-f7E@sKs{:KIW ;bĔ)"j6!]u 8SfKug# %<+ -_!]4tKy2-=;>.]_0uZTex2YJs+uCTjGfc!my){+ѷ!]DGx1㔲ڢC^:%Q.:-4.Df=>پQ~? \KH|*(TfdvI[O[];agрi}U[)vHPK@|=SP2\\zipp-3.6.0.dist-info/WHEEL HM K-*ϳR03rOK-J,/RHJ,./Q0363 /, (-JLR()*M ILR(4KM̫PK@|=SBOu"zipp-3.6.0.dist-info/top_level.txt,(PK@|=S#zipp-3.6.0.dist-info/RECORDu9z@@޳a/R80 aU>#ICg-U0Lu{z_iՔZ"2løԛXё,`MJYzg$eUGYo C+ ?X%}tہ! :=NJk|mڪ'k_|3N&*tYSygKyLDǒS^tZ0BJ995fAFцZS*x I?2Z{Hv[8j9Đf&״HEqwPK0|=SBq8u zipp.pyPK@|=SYď` zipp-3.6.0.dist-info/LICENSEPK@|=SZ4 zipp-3.6.0.dist-info/METADATAPK@|=SP2\\zipp-3.6.0.dist-info/WHEELPK@|=SBOu"]zipp-3.6.0.dist-info/top_level.txtPK@|=S#zipp-3.6.0.dist-info/RECORDPKpdm-2.23.1/tests/fixtures/artifacts/zipp-3.7.0-py3-none-any.whl000066400000000000000000000123001477560627500237650ustar00rootroot00000000000000PK8SBq8u zipp.pyY۸NXN-0IPŢ 4Xh(sDzfb>D~I(YvA;h76yn<в^3ٮԵJ>u\O+e%R^mmxҕ̆u$vVAJM*e`ѷ1zWm aU%r Ŝ۾(\DVَZ)4‡{h^B,x/"黺2E>qc*{DML q-  wN݋F>ZTdzѓa{ґcmfcv?3zn H@Je@K*bvIxϒ(BfLDJ"Jy t^컶!O$ro>qkERkZM_GrF6VemrêxU"%t)!'44;Kyŕboںoe"?dwggz:oMڋFzM+)d32(t#?;#^ׂlx1n6D(asD7$Z+|b@C!zq}=Vy|/1'v$-F|B?)&=a|^pͩi?PnKiSLFĀ^q%=rڜ#ζy~؊1e cZ[Hp={3!3\*|FwGẠۘiDJw QC-mG#y 0MZ j]EgʚMA 2B%O= 0% CjJ:X%|gjqJ(**0?c[D3_6'Y$7(*ySZuΚA YU ^dNQKFZ7i@ i% }=ʯPXUU`aj; M:QvwGowwvs󠹼ؠV]+06^ˆ|U_-W wwr xK:[j -H.&Mi@2_ݬsBٝm?̮s/#JSwzlܸ]]"2 |l 8af=}j/Loi_.h3C6oIrT8 }ynF痧9Dxl3:fJɉ1@ő==z"}|3Ld"n  "WR4 5oFmR$1mPxxa>=}1DPu驜\!D4n SgΞLouFiF SKj)7)ԢQԫB{6v& y;۰Gѥx8OGGxp =nYL-ǘj3=ozqy+|EKsg{7nɸv9'̯gv_lĉ u(KXS5v"BP :-_ jzN@_]Թu s;Y8'l5L Ðz ӫEs*sv/[eU3pAAg73XSïW۫͆0y0\ FG /a+1Pb.$>`h EYQg_jp8?rt$q0|-rsLNx\cB,}Û6 ww~у޻G\D' ߧg籿 ./8*w,pTT zY.&Qsn;{y)G}FP[.g(fVș'+?m iV`~4Op6RlZOg|ꁡg:՞{x|7%KtPKHSYď`zipp-3.7.0.dist-info/LICENSE]QK0W8RDy,nC9fYy*(6E;WHg{ͤ:SWz &0VBpXuz;NS=F%pM zW؉h|k75(p2H1puu5Aَwt -N"n꽵b&ZHpyxk gs@ίξsG9 . tkbbkGBa~b18=gp7ϐʅ+uo?M;^%nd8ɇwfc.Q#RkZYf"H`/F 9Z/[YԢ@i&e.dLOB\ r+ $"|%si [KSZiPrmd˹rKU ϐZ؊,Qk Ն9I1CAʃOg+*w) \n?E3FP8~*g)ye%ZVhBzZ'"LBYh"8B]% !qo{sv7^'naK왘2EDƷ㾧Fʬrnmag~;$ksl(NGqGKώK׫'L(UGֵ GoQٺ >hu[^5yJ~mdW(Ѽ8,y]6(vNuoCw6x4>E+K|.w'`Iz`k+><{ t0 .7jbS"YFGbh@>Y*k {=Y2PKHSh\\zipp-3.7.0.dist-info/WHEEL HM K-*ϳR03rOK-J,/RHJ,./Q0363 /, (-JLR()*M ILR(4KM̫PKHSBOu"zipp-3.7.0.dist-info/top_level.txt,(PKHSm$zipp-3.7.0.dist-info/RECORDuлv0gC@: b܊Y8<(A.O.lo1c(JmjmT^ЫޞƬ%4fݩ5"@FP\LR]]LMr9O"丣Q IfBF e/у[SgZSH0x>_^4~vO/qVBi^!nͤR3<KwoyA.SQ(=V,*>)vhgP%ilԛ48ðs]VٍҨh?O]-_PK8SBq8u zipp.pyPKHSYď` zipp-3.7.0.dist-info/LICENSEPKHSX4 zipp-3.7.0.dist-info/METADATAPKHSh\\zipp-3.7.0.dist-info/WHEELPKHSBOu"[zipp-3.7.0.dist-info/top_level.txtPKHSm$zipp-3.7.0.dist-info/RECORDPKpdm-2.23.1/tests/fixtures/constraints.txt000066400000000000000000000002151477560627500205230ustar00rootroot00000000000000# This is a pip constraints file requests==2.20.0b1 django==1.11.8 certifi==2018.11.17 chardet==3.0.4 idna==2.7 pytz==2019.3 urllib3==1.23b0 pdm-2.23.1/tests/fixtures/index/000077500000000000000000000000001477560627500165245ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/index/demo.html000066400000000000000000000005061477560627500203370ustar00rootroot00000000000000

Demo

demo-0.0.1-cp36-cp36m-win_amd64.whl demo-0.0.1-py2.py3-none-any.whl pdm-2.23.1/tests/fixtures/index/future-fstrings.html000066400000000000000000000007771477560627500225740ustar00rootroot00000000000000

future-fstrings

future_fstrings-1.2.0-py2.py3-none-any.whl future_fstrings-1.2.0.tar.gz pdm-2.23.1/tests/fixtures/index/pep345-legacy.html000066400000000000000000000004061477560627500216740ustar00rootroot00000000000000

pep345-legacy

pep345_legacy-0.0.1-py2.py3-none-any.whl pdm-2.23.1/tests/fixtures/index/wheel.html000066400000000000000000000003671477560627500205240ustar00rootroot00000000000000

wheel

wheel-0.37.1-py2.py3-none-any.whl pdm-2.23.1/tests/fixtures/json/000077500000000000000000000000001477560627500163665ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/json/zipp.json000066400000000000000000000013541477560627500202460ustar00rootroot00000000000000{ "meta": { "api-version": "1.0" }, "name": "zipp", "files": [ { "filename": "zipp-3.6.0-py3-none-any.whl", "url": "http://fixtures.test/artifacts/zipp-3.6.0-py3-none-any.whl", "hashes": { "sha256": "9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc" }, "upload-time": "2023-02-01T00:00:00.000000Z", "requires-python": ">=3.7" }, { "filename": "zipp-3.7.0-py3-none-any.whl", "url": "http://fixtures.test/artifacts/zipp-3.7.0-py3-none-any.whl", "hashes": { "sha256": "b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375" }, "upload-time": "2024-02-01T00:00:00.000000Z", "requires-python": ">=3.7" } ] } pdm-2.23.1/tests/fixtures/poetry-error.toml000066400000000000000000000004351477560627500207650ustar00rootroot00000000000000[tool.poetry] name = "test-poetry" version = "0.1.0" description = "" authors = ["Frost Ming "] readme = "README.md" [tool.poetry.dependencies] python = "^3.11" foo = ">=1.0||^2.1" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" pdm-2.23.1/tests/fixtures/poetry-new.toml000066400000000000000000000006251477560627500204260ustar00rootroot00000000000000[tool.poetry] name = "test-poetry" version = "0.1.0" description = "" authors = ["Frost Ming "] readme = "README.md" packages = [{include = "test_poetry"}] [tool.poetry.dependencies] python = "^3.9" httpx = "*" pendulum = "*" [tool.poetry.group.test.dependencies] pytest = "^6.0.0" pytest-mock = "*" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" pdm-2.23.1/tests/fixtures/projects/000077500000000000000000000000001477560627500172465ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/__init__.py000066400000000000000000000000001477560627500213450ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-#-with-hash/000077500000000000000000000000001477560627500222045ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-#-with-hash/demo.py000066400000000000000000000000521477560627500234770ustar00rootroot00000000000000import os import chardet print(os.name) pdm-2.23.1/tests/fixtures/projects/demo-#-with-hash/setup.py000066400000000000000000000005141477560627500237160ustar00rootroot00000000000000from setuptools import setup setup( name="demo", version="0.0.1", description="test demo", py_modules=["demo"], python_requires=">=3.3", install_requires=["idna", "chardet; os_name=='nt'"], extras_require={ "tests": ["pytest"], "security": ['requests; python_version>="3.6"'], }, ) pdm-2.23.1/tests/fixtures/projects/demo-combined-extras/000077500000000000000000000000001477560627500232545ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-combined-extras/demo.py000066400000000000000000000000321477560627500245450ustar00rootroot00000000000000import os print(os.name) pdm-2.23.1/tests/fixtures/projects/demo-combined-extras/pyproject.toml000066400000000000000000000007141477560627500261720ustar00rootroot00000000000000[project] # PEP 621 project metadata # See https://www.python.org/dev/peps/pep-0621/ authors = [ {name = "frostming", email = "mianghong@gmail.com"}, ] version = "0.1.0" requires-python = ">=3.5" license = {text = "MIT"} dependencies = ["urllib3"] description = "" name = "demo-package-extra" [project.optional-dependencies] be = ["idna"] te = ["chardet"] all = ["idna", "chardet"] [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" pdm-2.23.1/tests/fixtures/projects/demo-failure-no-dep/000077500000000000000000000000001477560627500227775ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-failure-no-dep/demo.py000066400000000000000000000000521477560627500242720ustar00rootroot00000000000000import os import chardet print(os.name) pdm-2.23.1/tests/fixtures/projects/demo-failure-no-dep/setup.py000066400000000000000000000003661477560627500245160ustar00rootroot00000000000000from setuptools import setup if True: raise RuntimeError("This mimics the build error on unmatched platform") setup( name="demo", version="0.0.1", description="test demo", py_modules=["demo"], python_requires=">=3.3", ) pdm-2.23.1/tests/fixtures/projects/demo-failure/000077500000000000000000000000001477560627500216175ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-failure/demo.py000066400000000000000000000000521477560627500231120ustar00rootroot00000000000000import os import chardet print(os.name) pdm-2.23.1/tests/fixtures/projects/demo-failure/setup.py000066400000000000000000000005311477560627500233300ustar00rootroot00000000000000from setuptools import setup import first setup( name="demo", version="0.0.1", description="test demo", py_modules=["demo"], python_requires=">=3.3", install_requires=["idna", "chardet; os_name=='nt'"], extras_require={ "tests": ["pytest"], "security": ['requests; python_version>="3.6"'], }, ) pdm-2.23.1/tests/fixtures/projects/demo-module/000077500000000000000000000000001477560627500214555ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-module/LICENSE000066400000000000000000000000141477560627500224550ustar00rootroot00000000000000MIT License pdm-2.23.1/tests/fixtures/projects/demo-module/README.md000066400000000000000000000000301477560627500227250ustar00rootroot00000000000000# This is a demo module pdm-2.23.1/tests/fixtures/projects/demo-module/bar_module.py000066400000000000000000000000161477560627500241350ustar00rootroot00000000000000bar = "Hello" pdm-2.23.1/tests/fixtures/projects/demo-module/foo_module.py000066400000000000000000000000441477560627500241550ustar00rootroot00000000000000__version__ = "0.1.0" foo = "hello" pdm-2.23.1/tests/fixtures/projects/demo-module/pyproject.toml000066400000000000000000000007021477560627500243700ustar00rootroot00000000000000[build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" [project] # PEP 621 project metadata # See https://www.python.org/dev/peps/pep-0621/ authors = [ {name = "frostming", email = "mianghong@gmail.com"}, ] dynamic = ["version"] requires-python = ">=3.5" license = {text = "MIT"} dependencies = [] description = "" name = "demo-module" [project.optional-dependencies] [tool.pdm.version] source = "file" path = "foo_module.py" pdm-2.23.1/tests/fixtures/projects/demo-package-has-dep-with-extras/000077500000000000000000000000001477560627500253575ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-package-has-dep-with-extras/pdm.lock000066400000000000000000000063511477560627500270160ustar00rootroot00000000000000# This file is @generated by PDM. # It is not intended for manual editing. [metadata] groups = ["default"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.2" content_hash = "sha256:436a711ecb4a672c165c06d03952762d4f43b85f49633c53654a53aca6584643" [[package]] name = "certifi" version = "2021.10.8" summary = "Python package for providing Mozilla's CA Bundle." groups = ["default"] files = [ {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, ] [[package]] name = "charset-normalizer" version = "2.0.7" requires_python = ">=3.5.0" summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." groups = ["default"] marker = "python_version >= \"3\"" files = [ {file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"}, {file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"}, ] [[package]] name = "idna" version = "3.3" requires_python = ">=3.5" summary = "Internationalized Domain Names in Applications (IDNA)" groups = ["default"] marker = "python_version >= \"3\"" files = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] [[package]] name = "requests" version = "2.26.0" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" summary = "Python HTTP for Humans." groups = ["default"] dependencies = [ "certifi>=2017.4.17", "charset-normalizer~=2.0.0; python_version >= \"3\"", "idna<4,>=2.5; python_version >= \"3\"", "urllib3<1.27,>=1.21.1", ] files = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, ] [[package]] name = "requests" version = "2.26.0" extras = ["security"] requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" summary = "Python HTTP for Humans." groups = ["default"] dependencies = [ "requests==2.26.0", ] files = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, ] [[package]] name = "urllib3" version = "1.26.7" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" summary = "HTTP library with thread-safe connection pooling, file post, and more." groups = ["default"] files = [ {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, ] pdm-2.23.1/tests/fixtures/projects/demo-package-has-dep-with-extras/pyproject.toml000066400000000000000000000004741477560627500303000ustar00rootroot00000000000000[project] name = "" version = "" description = "" authors = [ {name = "Frost Ming", email = "mianghong@gmail.com"}, ] dependencies = [ "requests[security]~=2.26", ] requires-python = ">=3.6" license = {text = "MIT"} [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" [tool] [tool.pdm] pdm-2.23.1/tests/fixtures/projects/demo-package-has-dep-with-extras/requirements.txt000066400000000000000000000003131477560627500306400ustar00rootroot00000000000000# This file is @generated by PDM. # Please do not edit it manually. certifi==2021.10.8 charset-normalizer==2.0.7; python_version >= "3" idna==3.3; python_version >= "3" requests==2.26.0 urllib3==1.26.7 pdm-2.23.1/tests/fixtures/projects/demo-package/000077500000000000000000000000001477560627500215635ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-package/LICENSE000066400000000000000000000000141477560627500225630ustar00rootroot00000000000000MIT License pdm-2.23.1/tests/fixtures/projects/demo-package/README.md000066400000000000000000000000151477560627500230360ustar00rootroot00000000000000# my-package pdm-2.23.1/tests/fixtures/projects/demo-package/data_out.json000066400000000000000000000000201477560627500242460ustar00rootroot00000000000000{"name": "foo"} pdm-2.23.1/tests/fixtures/projects/demo-package/my_package/000077500000000000000000000000001477560627500236635ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-package/my_package/__init__.py000066400000000000000000000000261477560627500257720ustar00rootroot00000000000000__version__ = "0.1.0" pdm-2.23.1/tests/fixtures/projects/demo-package/my_package/data.json000066400000000000000000000000301477560627500254600ustar00rootroot00000000000000{"name": "demo-module"} pdm-2.23.1/tests/fixtures/projects/demo-package/pdm.lock000066400000000000000000000277131477560627500232270ustar00rootroot00000000000000# This file is @generated by PDM. # It is not intended for manual editing. [metadata] groups = ["default"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.2" content_hash = "sha256:220e2d264de206a1fe5ce3cbf3d3ed70dc3fdc77ee73428c64e82e901fb222f9" [[package]] name = "click" version = "7.1.2" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" summary = "Composable command line interface toolkit" groups = ["default"] files = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, ] [[package]] name = "flask" version = "1.1.4" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" summary = "A simple framework for building complex web applications." groups = ["default"] dependencies = [ "Jinja2<3.0,>=2.10.1", "Werkzeug<2.0,>=0.15", "click<8.0,>=5.1", "itsdangerous<2.0,>=0.24", ] files = [ {file = "Flask-1.1.4-py2.py3-none-any.whl", hash = "sha256:c34f04500f2cbbea882b1acb02002ad6fe6b7ffa64a6164577995657f50aed22"}, {file = "Flask-1.1.4.tar.gz", hash = "sha256:0fbeb6180d383a9186d0d6ed954e0042ad9f18e0e8de088b2b419d526927d196"}, ] [[package]] name = "itsdangerous" version = "1.1.0" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" summary = "Various helpers to pass data to untrusted environments and back." groups = ["default"] files = [ {file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:72a1252c0b2cc2bcc351acf2cfe2ec0159d8578c54767d5c2aa67fd869346e55"}, {file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"}, {file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"}, {file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:ac4c9f590d59c36b7d2953f97fda415f2461280e5279650aafe1e593f129c4f7"}, ] [[package]] name = "jinja2" version = "2.11.3" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" summary = "A very fast and expressive template engine." groups = ["default"] dependencies = [ "MarkupSafe>=0.23", ] files = [ {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"}, {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, ] [[package]] name = "markupsafe" version = "1.1.1" requires_python = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" summary = "Safely add untrusted strings to HTML/XML markup." groups = ["default"] files = [ {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:780c6d675155c54556d877df22e4739295a085618ad8bc2d1d25ba07879c9522"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:b1423ef6a7f62c1d92bd1ce15e224f8d709fbae969fd7720623eb2ae9b8c3a34"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:bd597f350935cb5c9cc3a55f5a15c8ec3632c71274e079380ef2f4789ad824f0"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:2d3aa974bd1ee74affb31503dfe3ce55bffd208ef5d5dd1582a1300efbc232f4"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:dbc6ee2241abe4f518685f603e427a94ceb73c08b6d15d85c6c5c4a71bde9c3e"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:60f1467a898ed3402d2f4e679906aed9f55c14d6990a53c3f811f593ee425a88"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:bf13467480b37db64550c4f661e4dab34d2ce714d986254e609598f00360dcbb"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2a5df1859f9d06d414e60f793466b2549be4ea5fce872d8750215d0b22b4003c"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c9cc323768421f2dd2ab416e38c0302803771f73191ffa7134f00a4a5ca57e72"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:753e2b8c825b41dc3aec6c08a6f2eb786a8a3b266258e391375038f511fc4518"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:1e8a27f1c41cae51e8a5687aa63d2bf92dc96bebd6d5b33cc3ec8fa31071ee9b"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"}, {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:5881c17ae0378a26e2a8abde240387ca44ac778e0a8745b55522c4119d24dfff"}, {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:37fd53e8825dd3f3c6f6746afa8373b623a87fc0568513f1a5f071ce73ceedb0"}, {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, {file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"}, {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"}, {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"}, {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"}, {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"}, {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"}, {file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"}, {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"}, {file = "MarkupSafe-1.1.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3db7af9025f66e08c781093dbdb7b54e52b5506006e141dcbe5b740e578b5504"}, {file = "MarkupSafe-1.1.1-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:0db2ff381c2218c1ba7192f75e5c5cf180efa023ddfc6914ffe9a38b2bd303dd"}, {file = "MarkupSafe-1.1.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:5079d5041ace388bb57a5ebe38ae585fb18bc681a669030d76f99b510b33d53e"}, {file = "MarkupSafe-1.1.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:8b7fb692d27d6e17ca0fbcbc0edd7b32790e7c070624211499db5a758e89e38d"}, {file = "MarkupSafe-1.1.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c6ccfab7baaf835fa90cb7ef3e9e7c240e84394420a5d33ba707f05318522fd6"}, {file = "MarkupSafe-1.1.1-pp37-pypy37_pp73-manylinux1_x86_64.whl", hash = "sha256:5eba30ae5ad72351903ba340a6b4e427353de04542f36fc177ebaffafb8ae5e7"}, {file = "MarkupSafe-1.1.1-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:c6fc95b4f707efd506f3f7789140db9cec1b731999e7f033371bb6a5006a1ef8"}, {file = "MarkupSafe-1.1.1-pp37-pypy37_pp73-win32.whl", hash = "sha256:de603df0d005177f7ef7faa56578d2d47fc93aaef165cdef91d64959176edb15"}, {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:30323461f382a59bcb940be0adc5e0d6be5dfc6bd1c2c5cbe2d13b96414e1619"}, ] [[package]] name = "werkzeug" version = "1.0.1" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" summary = "The comprehensive WSGI web application library." groups = ["default"] files = [ {file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"}, {file = "Werkzeug-1.0.1.tar.gz", hash = "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"}, ] pdm-2.23.1/tests/fixtures/projects/demo-package/pyproject.toml000066400000000000000000000011041477560627500244730ustar00rootroot00000000000000[build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" [project] # PEP 621 project metadata # See https://www.python.org/dev/peps/pep-0621/ authors = [ {name = "frostming", email = "mianghong@gmail.com"}, ] dynamic = ["version"] requires-python = ">=3.5" license = {text = "MIT"} dependencies = ["flask"] description = "" name = "my-package" readme = "README.md" [project.optional-dependencies] [tool.pdm.version] source = "file" path = "my_package/__init__.py" [[tool.pdm.source]] url = "https://test.pypi.org/simple" verify_ssl = true name = "testpypi" pdm-2.23.1/tests/fixtures/projects/demo-package/requirements.ini000066400000000000000000000001721477560627500250070ustar00rootroot00000000000000# This file is @generated by PDM. # Please do not edit it manually. flask --extra-index-url https://test.pypi.org/simple pdm-2.23.1/tests/fixtures/projects/demo-package/requirements.txt000066400000000000000000000145451477560627500250600ustar00rootroot00000000000000# This file is @generated by PDM. # Please do not edit it manually. click==7.1.2 \ --hash=sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a \ --hash=sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc flask==1.1.4 \ --hash=sha256:0fbeb6180d383a9186d0d6ed954e0042ad9f18e0e8de088b2b419d526927d196 \ --hash=sha256:c34f04500f2cbbea882b1acb02002ad6fe6b7ffa64a6164577995657f50aed22 itsdangerous==1.1.0 \ --hash=sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19 \ --hash=sha256:72a1252c0b2cc2bcc351acf2cfe2ec0159d8578c54767d5c2aa67fd869346e55 \ --hash=sha256:ac4c9f590d59c36b7d2953f97fda415f2461280e5279650aafe1e593f129c4f7 \ --hash=sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749 jinja2==2.11.3 \ --hash=sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419 \ --hash=sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6 markupsafe==1.1.1 \ --hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \ --hash=sha256:0db2ff381c2218c1ba7192f75e5c5cf180efa023ddfc6914ffe9a38b2bd303dd \ --hash=sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42 \ --hash=sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f \ --hash=sha256:1e8a27f1c41cae51e8a5687aa63d2bf92dc96bebd6d5b33cc3ec8fa31071ee9b \ --hash=sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39 \ --hash=sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff \ --hash=sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b \ --hash=sha256:2a5df1859f9d06d414e60f793466b2549be4ea5fce872d8750215d0b22b4003c \ --hash=sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014 \ --hash=sha256:2d3aa974bd1ee74affb31503dfe3ce55bffd208ef5d5dd1582a1300efbc232f4 \ --hash=sha256:30323461f382a59bcb940be0adc5e0d6be5dfc6bd1c2c5cbe2d13b96414e1619 \ --hash=sha256:37fd53e8825dd3f3c6f6746afa8373b623a87fc0568513f1a5f071ce73ceedb0 \ --hash=sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f \ --hash=sha256:3db7af9025f66e08c781093dbdb7b54e52b5506006e141dcbe5b740e578b5504 \ --hash=sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e \ --hash=sha256:5079d5041ace388bb57a5ebe38ae585fb18bc681a669030d76f99b510b33d53e \ --hash=sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66 \ --hash=sha256:5881c17ae0378a26e2a8abde240387ca44ac778e0a8745b55522c4119d24dfff \ --hash=sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b \ --hash=sha256:5eba30ae5ad72351903ba340a6b4e427353de04542f36fc177ebaffafb8ae5e7 \ --hash=sha256:60f1467a898ed3402d2f4e679906aed9f55c14d6990a53c3f811f593ee425a88 \ --hash=sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15 \ --hash=sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1 \ --hash=sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85 \ --hash=sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1 \ --hash=sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e \ --hash=sha256:753e2b8c825b41dc3aec6c08a6f2eb786a8a3b266258e391375038f511fc4518 \ --hash=sha256:780c6d675155c54556d877df22e4739295a085618ad8bc2d1d25ba07879c9522 \ --hash=sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b \ --hash=sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905 \ --hash=sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850 \ --hash=sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0 \ --hash=sha256:8b7fb692d27d6e17ca0fbcbc0edd7b32790e7c070624211499db5a758e89e38d \ --hash=sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d \ --hash=sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb \ --hash=sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d \ --hash=sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c \ --hash=sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1 \ --hash=sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2 \ --hash=sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2 \ --hash=sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5 \ --hash=sha256:b1423ef6a7f62c1d92bd1ce15e224f8d709fbae969fd7720623eb2ae9b8c3a34 \ --hash=sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7 \ --hash=sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8 \ --hash=sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6 \ --hash=sha256:bd597f350935cb5c9cc3a55f5a15c8ec3632c71274e079380ef2f4789ad824f0 \ --hash=sha256:bf13467480b37db64550c4f661e4dab34d2ce714d986254e609598f00360dcbb \ --hash=sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193 \ --hash=sha256:c6ccfab7baaf835fa90cb7ef3e9e7c240e84394420a5d33ba707f05318522fd6 \ --hash=sha256:c6fc95b4f707efd506f3f7789140db9cec1b731999e7f033371bb6a5006a1ef8 \ --hash=sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f \ --hash=sha256:c9cc323768421f2dd2ab416e38c0302803771f73191ffa7134f00a4a5ca57e72 \ --hash=sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b \ --hash=sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2 \ --hash=sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5 \ --hash=sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c \ --hash=sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032 \ --hash=sha256:dbc6ee2241abe4f518685f603e427a94ceb73c08b6d15d85c6c5c4a71bde9c3e \ --hash=sha256:de603df0d005177f7ef7faa56578d2d47fc93aaef165cdef91d64959176edb15 \ --hash=sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be \ --hash=sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621 werkzeug==1.0.1 \ --hash=sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43 \ --hash=sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c --extra-index-url https://test.pypi.org/simple pdm-2.23.1/tests/fixtures/projects/demo-package/requirements_simple.txt000066400000000000000000000003231477560627500264160ustar00rootroot00000000000000# This file is @generated by PDM. # Please do not edit it manually. click==7.1.2 flask==1.1.4 itsdangerous==1.1.0 jinja2==2.11.3 markupsafe==1.1.1 werkzeug==1.0.1 --extra-index-url https://test.pypi.org/simple pdm-2.23.1/tests/fixtures/projects/demo-package/setup.txt000066400000000000000000000013261477560627500234660ustar00rootroot00000000000000 # -*- coding: utf-8 -*- from setuptools import setup import codecs with codecs.open('README.md', encoding="utf-8") as fp: long_description = fp.read() INSTALL_REQUIRES = [ 'flask', ] setup_kwargs = { 'name': 'my-package', 'version': '0.1.0', 'description': '', 'long_description': long_description, 'license': 'MIT', 'author': '', 'author_email': 'frostming ', 'maintainer': None, 'maintainer_email': None, 'url': '', 'packages': [ 'my_package', ], 'package_data': {'': ['*']}, 'long_description_content_type': 'text/markdown', 'install_requires': INSTALL_REQUIRES, 'python_requires': '>=3.5', } setup(**setup_kwargs) pdm-2.23.1/tests/fixtures/projects/demo-package/single_module.py000066400000000000000000000000251477560627500247600ustar00rootroot00000000000000print("hello world") pdm-2.23.1/tests/fixtures/projects/demo-parent-package/000077500000000000000000000000001477560627500230525ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-parent-package/README.md000066400000000000000000000000221477560627500243230ustar00rootroot00000000000000# Package Package pdm-2.23.1/tests/fixtures/projects/demo-parent-package/package-a/000077500000000000000000000000001477560627500246635ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-parent-package/package-a/foo.py000066400000000000000000000000261477560627500260160ustar00rootroot00000000000000__version__ = "0.1.0" pdm-2.23.1/tests/fixtures/projects/demo-parent-package/package-a/setup.py000066400000000000000000000001701477560627500263730ustar00rootroot00000000000000from setuptools import setup setup(name="package-a", py_modules=["foo"], version="0.1.0", install_requires=["flask"]) pdm-2.23.1/tests/fixtures/projects/demo-parent-package/package-b/000077500000000000000000000000001477560627500246645ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-parent-package/package-b/bar.py000066400000000000000000000000261477560627500260000ustar00rootroot00000000000000__version__ = "0.1.0" pdm-2.23.1/tests/fixtures/projects/demo-parent-package/package-b/pyproject.toml000066400000000000000000000003121477560627500275740ustar00rootroot00000000000000[build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" [project] name = "package-b" dependencies = ["django"] dynamic = ["version"] [tool.pdm.version] source = "file" path = "bar.py" pdm-2.23.1/tests/fixtures/projects/demo-prerelease/000077500000000000000000000000001477560627500223175ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-prerelease/demo.py000066400000000000000000000000521477560627500236120ustar00rootroot00000000000000import os import chardet print(os.name) pdm-2.23.1/tests/fixtures/projects/demo-prerelease/setup.py000066400000000000000000000005161477560627500240330ustar00rootroot00000000000000from setuptools import setup setup( name="demo", version="0.0.2b0", description="test demo", py_modules=["demo"], python_requires=">=3.3", install_requires=["idna", "chardet; os_name=='nt'"], extras_require={ "tests": ["pytest"], "security": ['requests; python_version>="3.6"'], }, ) pdm-2.23.1/tests/fixtures/projects/demo-src-package/000077500000000000000000000000001477560627500223505ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-src-package/LICENSE000066400000000000000000000000141477560627500233500ustar00rootroot00000000000000MIT License pdm-2.23.1/tests/fixtures/projects/demo-src-package/README.md000066400000000000000000000000301477560627500236200ustar00rootroot00000000000000# This is a demo module pdm-2.23.1/tests/fixtures/projects/demo-src-package/data_out.json000066400000000000000000000000201477560627500250330ustar00rootroot00000000000000{"name": "foo"} pdm-2.23.1/tests/fixtures/projects/demo-src-package/pyproject.toml000066400000000000000000000007201477560627500252630ustar00rootroot00000000000000[build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" [project] # PEP 621 project metadata # See https://www.python.org/dev/peps/pep-0621/ authors = [ {name = "frostming", email = "mianghong@gmail.com"}, ] dynamic = ["version"] requires-python = ">=3.5" license = {text = "MIT"} dependencies = [] description = "" name = "demo-package" [project.optional-dependencies] [tool.pdm.version] source = "file" path = "src/my_package/__init__.py" pdm-2.23.1/tests/fixtures/projects/demo-src-package/single_module.py000066400000000000000000000000251477560627500255450ustar00rootroot00000000000000print("hello world") pdm-2.23.1/tests/fixtures/projects/demo-src-package/src/000077500000000000000000000000001477560627500231375ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-src-package/src/my_package/000077500000000000000000000000001477560627500252375ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo-src-package/src/my_package/__init__.py000066400000000000000000000000261477560627500273460ustar00rootroot00000000000000__version__ = "0.1.0" pdm-2.23.1/tests/fixtures/projects/demo-src-package/src/my_package/data.json000066400000000000000000000000301477560627500270340ustar00rootroot00000000000000{"name": "demo-module"} pdm-2.23.1/tests/fixtures/projects/demo/000077500000000000000000000000001477560627500201725ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo/demo.py000066400000000000000000000000521477560627500214650ustar00rootroot00000000000000import os import chardet print(os.name) pdm-2.23.1/tests/fixtures/projects/demo/pyproject.toml000066400000000000000000000005301477560627500231040ustar00rootroot00000000000000 [project] name = "demo" version = "0.0.1" description = "test demo" requires-python = ">=3.3" dependencies = [ "idna", "chardet; os_name=='nt'", ] [project.optional-dependencies] tests = [ "pytest", ] security = [ "requests; python_version>=\"3.6\"", ] [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" pdm-2.23.1/tests/fixtures/projects/demo_extras/000077500000000000000000000000001477560627500215605ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/demo_extras/demo.py000066400000000000000000000000321477560627500230510ustar00rootroot00000000000000import os print(os.name) pdm-2.23.1/tests/fixtures/projects/demo_extras/setup.py000066400000000000000000000003721477560627500232740ustar00rootroot00000000000000from setuptools import setup setup( name="demo-extras", version="0.0.1", description="test demo", py_modules=["demo"], install_requires=[], extras_require={"extra1": ["requests[security]"], "extra2": ["requests[socks]"]}, ) pdm-2.23.1/tests/fixtures/projects/flit-demo/000077500000000000000000000000001477560627500211265ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/flit-demo/README.rst000066400000000000000000000000001477560627500226030ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/flit-demo/doc/000077500000000000000000000000001477560627500216735ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/flit-demo/doc/index.html000066400000000000000000000000001477560627500236560ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/flit-demo/flit.py000066400000000000000000000000611477560627500224330ustar00rootroot00000000000000"""An awesome flit demo""" __version__ = "0.1.0" pdm-2.23.1/tests/fixtures/projects/flit-demo/pyproject.toml000066400000000000000000000017111477560627500240420ustar00rootroot00000000000000[tool.flit.metadata] module="flit" author="Thomas Kluyver" author-email="thomas@kluyver.me.uk" home-page="https://github.com/takluyver/flit" requires = [ "requests>=2.6", "configparser; python_version == \"2.7\"", ] dist-name = "pyflit" requires-python=">=3.5" description-file="README.rst" classifiers=[ "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries :: Python Modules", ] [tool.flit.metadata.urls] Documentation = "https://flit.readthedocs.io/en/latest/" [tool.flit.metadata.requires-extra] test = [ "pytest >=2.7.3", "pytest-cov", ] doc = ["sphinx"] [tool.flit.scripts] flit = "flit:main" [tool.flit.entrypoints."pygments.lexers"] dogelang = "dogelang.lexer:DogeLexer" [tool.flit.sdist] include = ["doc/"] exclude = ["doc/*.html"] [build-system] requires = ["flit_core >=2,<4"] build-backend = "flit_core.buildapi" pdm-2.23.1/tests/fixtures/projects/poetry-demo/000077500000000000000000000000001477560627500215125ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/poetry-demo/mylib.py000066400000000000000000000000141477560627500231730ustar00rootroot00000000000000FOO = "bar" pdm-2.23.1/tests/fixtures/projects/poetry-demo/pyproject.toml000066400000000000000000000015371477560627500244340ustar00rootroot00000000000000[tool.poetry] name = "poetry-demo" version = "0.1.0" authors = ["Thomas Kluyver "] homepage = "https://github.com/takluyver/flit" license = "BSD-3-Clause" description = "A demo project for Poetry" classifiers = [ "Intended Audience :: Developers", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries :: Python Modules", ] packages = [ { include = "mylib.py" }, ] [tool.poetry.urls] Documentation = "https://flit.readthedocs.io/en/latest/" [tool.poetry.dependencies] python = "^3.6" requests = "^2.6" pytest = {version = "^2.7.3", optional = true} pytest-cov = {version = "*", optional = true} sphinx = {version = "*", optional = true} [tool.poetry.extras] test = ["pytest", "pytest-cov"] doc = ["sphinx"] [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" pdm-2.23.1/tests/fixtures/projects/poetry-with-circular-dep/000077500000000000000000000000001477560627500241115ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/poetry-with-circular-dep/packages/000077500000000000000000000000001477560627500256675ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/poetry-with-circular-dep/packages/child/000077500000000000000000000000001477560627500267525ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/poetry-with-circular-dep/packages/child/child/000077500000000000000000000000001477560627500300355ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/poetry-with-circular-dep/packages/child/child/__init__.py000066400000000000000000000000001477560627500321340ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/poetry-with-circular-dep/packages/child/pyproject.toml000066400000000000000000000006021477560627500316640ustar00rootroot00000000000000[tool.poetry] name = "child" version = "0.1.0" authors = ["PDM "] description = "Child project" license = "Apache-2.0" packages = [{include = "child"}] [tool.poetry.dependencies] python = "^3.9.1" [tool.poetry.group.dev.dependencies] parent = {path = "../..", develop = true} [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" pdm-2.23.1/tests/fixtures/projects/poetry-with-circular-dep/parent/000077500000000000000000000000001477560627500254025ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/poetry-with-circular-dep/parent/__init__.py000066400000000000000000000000001477560627500275010ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/poetry-with-circular-dep/pyproject.toml000066400000000000000000000006171477560627500270310ustar00rootroot00000000000000[tool.poetry] name = "parent" version = "0.1.0" authors = ["PDM "] description = "Parent project" license = "Apache-2.0" packages = [{include = "parent"}] [tool.poetry.dependencies] python = "^3.9.1" [tool.poetry.group.dev.dependencies] child = {path = "./packages/child", develop = true} [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" pdm-2.23.1/tests/fixtures/projects/test-hatch-static/000077500000000000000000000000001477560627500225775ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-hatch-static/README.md000066400000000000000000000000001477560627500240440ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-hatch-static/pyproject.toml000066400000000000000000000006021477560627500255110ustar00rootroot00000000000000[build-system] requires = ["hatchling>=0.15.0"] build-backend = "hatchling.build" [project] name = "test-hatch" version = "0.1.0" description = "Test hatch project" readme = "README.md" license = "MIT" requires-python = ">=3.7" authors = [{ name = "John", email = "john@example.org" }] classifiers = [ "License :: OSI Approved :: MIT License", ] dependencies = ["requests", "click"] pdm-2.23.1/tests/fixtures/projects/test-monorepo/000077500000000000000000000000001477560627500220615ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-monorepo/README.md000066400000000000000000000000131477560627500233320ustar00rootroot00000000000000# pdm_test pdm-2.23.1/tests/fixtures/projects/test-monorepo/core/000077500000000000000000000000001477560627500230115ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-monorepo/core/core.py000066400000000000000000000000001477560627500243010ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-monorepo/core/pyproject.toml000066400000000000000000000002611477560627500257240ustar00rootroot00000000000000[project] name = "core" version = "0.0.1" description = "" requires-python = ">= 3.7" dependencies = [] [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" pdm-2.23.1/tests/fixtures/projects/test-monorepo/package_a/000077500000000000000000000000001477560627500237545ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-monorepo/package_a/alice.py000066400000000000000000000000001477560627500253710ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-monorepo/package_a/pyproject.toml000066400000000000000000000003451477560627500266720ustar00rootroot00000000000000[project] name = "package_a" version = "0.0.1" description = "" requires-python = ">= 3.7" dependencies = [ "core @ file:///${PROJECT_ROOT}/../core", ] [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" pdm-2.23.1/tests/fixtures/projects/test-monorepo/package_b/000077500000000000000000000000001477560627500237555ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-monorepo/package_b/bob.py000066400000000000000000000000001477560627500250570ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-monorepo/package_b/pyproject.toml000066400000000000000000000003451477560627500266730ustar00rootroot00000000000000[project] name = "package_b" version = "0.0.1" description = "" requires-python = ">= 3.7" dependencies = [ "core @ file:///${PROJECT_ROOT}/../core", ] [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" pdm-2.23.1/tests/fixtures/projects/test-monorepo/pyproject.toml000066400000000000000000000002421477560627500247730ustar00rootroot00000000000000[project] requires-python = ">= 3.7" dependencies = [ "package_a @ file:///${PROJECT_ROOT}/package_a", "package_b @ file:///${PROJECT_ROOT}/package_b", ] pdm-2.23.1/tests/fixtures/projects/test-package-type-fixer/000077500000000000000000000000001477560627500237105ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-package-type-fixer/pyproject.toml000066400000000000000000000005461477560627500266310ustar00rootroot00000000000000[build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" [project] version = "0.0.1" dependencies = [] name = "test-package-type-fixer" requires-python = ">=3.9" [dependency-groups] dev = [ "requests==2.19.1" ] [tool.pdm.version] source = "file" path = "src/test_package_type_fixer/__init__.py" [tool.pdm] package-type = "application" pdm-2.23.1/tests/fixtures/projects/test-package-type-fixer/src/000077500000000000000000000000001477560627500244775ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-package-type-fixer/src/test_package_type_fixer/000077500000000000000000000000001477560627500313675ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-package-type-fixer/src/test_package_type_fixer/__init__.py000066400000000000000000000000251477560627500334750ustar00rootroot00000000000000__version__ = '0.1.0'pdm-2.23.1/tests/fixtures/projects/test-plugin-pdm/000077500000000000000000000000001477560627500222775ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-plugin-pdm/hello.py000066400000000000000000000005741477560627500237620ustar00rootroot00000000000000from pdm.cli.commands.base import BaseCommand class HelloCommand(BaseCommand): """Say hello to somebody""" def add_arguments(self, parser): parser.add_argument("-n", "--name", help="the person's name") def handle(self, project, options): print(f"Hello, {options.name or 'world'}") def main(core): core.register_command(HelloCommand, "hello") pdm-2.23.1/tests/fixtures/projects/test-plugin-pdm/pyproject.toml000066400000000000000000000004661477560627500252210ustar00rootroot00000000000000[build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" [project] # PEP 621 project metadata # See https://www.python.org/dev/peps/pep-0621/ version = "0.0.1" dependencies = [] name = "test-plugin-pdm" [project.optional-dependencies] [project.entry-points."pdm.plugin"] hello = "hello:main" pdm-2.23.1/tests/fixtures/projects/test-plugin/000077500000000000000000000000001477560627500215215ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-plugin/hello.py000066400000000000000000000005741477560627500232040ustar00rootroot00000000000000from pdm.cli.commands.base import BaseCommand class HelloCommand(BaseCommand): """Say hello to somebody""" def add_arguments(self, parser): parser.add_argument("-n", "--name", help="the person's name") def handle(self, project, options): print(f"Hello, {options.name or 'world'}") def main(core): core.register_command(HelloCommand, "hello") pdm-2.23.1/tests/fixtures/projects/test-plugin/setup.py000066400000000000000000000002501477560627500232300ustar00rootroot00000000000000from setuptools import setup setup( name="test-plugin", version="0.0.1", py_modules=["hello"], entry_points={"pdm.plugin": ["hello = hello:main"]}, ) pdm-2.23.1/tests/fixtures/projects/test-removal/000077500000000000000000000000001477560627500216705ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-removal/__init__.py000066400000000000000000000000001477560627500237670ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-removal/bar.py000066400000000000000000000000001477560627500227740ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-removal/foo.py000066400000000000000000000000001477560627500230130ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-removal/subdir/000077500000000000000000000000001477560627500231605ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-removal/subdir/__init__.py000066400000000000000000000000001477560627500252570ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-setuptools/000077500000000000000000000000001477560627500224445ustar00rootroot00000000000000pdm-2.23.1/tests/fixtures/projects/test-setuptools/AUTHORS000066400000000000000000000000121477560627500235050ustar00rootroot00000000000000frostming pdm-2.23.1/tests/fixtures/projects/test-setuptools/README.md000066400000000000000000000000141477560627500237160ustar00rootroot00000000000000# My Module pdm-2.23.1/tests/fixtures/projects/test-setuptools/mymodule.py000066400000000000000000000000261477560627500246470ustar00rootroot00000000000000__version__ = "0.1.0" pdm-2.23.1/tests/fixtures/projects/test-setuptools/setup.cfg000066400000000000000000000006171477560627500242710ustar00rootroot00000000000000[metadata] name = mymodule description = A test module keywords = one, two classifiers = Framework :: Django Programming Language :: Python :: 3 [options] zip_safe = False include_package_data = True python_requires = >=3.5 package_dir = = src install_requires = requests importlib-metadata; python_version<"3.10" [options.entry_points] console_scripts = mycli = mymodule:main pdm-2.23.1/tests/fixtures/projects/test-setuptools/setup.py000066400000000000000000000004641477560627500241620ustar00rootroot00000000000000from setuptools import setup from mymodule import __version__ with open("AUTHORS") as f: authors = f.read().strip() kwargs = { "name": "mymodule", "version": __version__, "author": authors, } if 1 + 1 >= 2: kwargs.update(license="MIT") if __name__ == "__main__": setup(**kwargs) pdm-2.23.1/tests/fixtures/pypi.json000066400000000000000000000032041477560627500172700ustar00rootroot00000000000000{ "certifi": { "2018.11.17": {} }, "chardet": { "3.0.4": {} }, "demo": { "0.0.1": { "dependencies": [ "idna", "chardet; os_name=='nt'", "pytest; extra=='tests'", "requests; python_version>='3.6' and extra=='security'" ], "requires_python": ">=3.3" } }, "django": { "1.11.8": { "dependencies": [ "pytz" ] }, "2.2.9": { "dependencies": [ "pytz", "sqlparse" ], "requires_python": ">=3.5" } }, "django-toolbar": { "1.0": { "dependencies": [ "django<2" ] } }, "editables": { "0.2": {} }, "idna": { "2.7": {} }, "pyopenssl": { "0.14": {} }, "pysocks": { "1.5.6": {} }, "pytz": { "2019.3": {} }, "requests": { "2.19.1": { "dependencies": [ "certifi>=2017.4.17", "chardet<3.1.0,>=3.0.2", "idna<2.8,>=2.5", "urllib3<1.24,>=1.21.1", "PySocks>=1.5.6,!=1.5.7; extra=='socks'", "pyOpenSSL>=0.14; extra=='security'" ] }, "2.20.0b1": { "dependencies": [ "certifi>=2017.4.17", "chardet<3.1.0,>=3.0.2", "idna<2.8,>=2.5", "urllib3<1.24,>=1.23b0", "PySocks>=1.5.6,!=1.5.7; extra=='socks'", "pyOpenSSL>=0.14; extra=='security'" ] } }, "sqlparse": { "0.3.0": { "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" } }, "urllib3": { "1.22": {}, "1.23b0": {} }, "using-demo": { "0.1.0": { "dependencies": [ "demo" ], "requires_python": ">=3.3" } } }pdm-2.23.1/tests/fixtures/pyproject.toml000066400000000000000000000025311477560627500203320ustar00rootroot00000000000000[tool.poetry] name = "poetry" version = "1.0.0" description = "Python dependency management and packaging made easy." authors = [ "Sébastien Eustace ", "Example, Inc. " ] license = "MIT" readme = "README.md" homepage = "https://python-poetry.org/" repository = "https://github.com/python-poetry/poetry" documentation = "https://python-poetry.org/docs" packages = [ {include="my_package", from="lib/"}, {include="tests", format="sdist"} ] include = ["CHANGELOG.md"] exclude = ["my_package/excluded.py"] [tool.poetry.dependencies] python = "~2.7 || ^3.4" cleo = { version = "^0.7.6", markers = "python_version ~= '2.7'" } cachecontrol = { version = "^0.12.4", extras = ["filecache"], python = "^3.4" } flask = { git = "https://github.com/pallets/flask.git", rev = "38eb5d3b" } psycopg2 = { version = "^2.7", optional = true } mysqlclient = { version = "^1.3", optional = true } babel = "2.9.0" [tool.poetry.dev-dependencies] demo-dir = { path = "./projects/demo" } demo = { path = "./artifacts/demo-0.0.1-py2.py3-none-any.whl" } [tool.poetry.extras] mysql = ["mysqlclient"] pgsql = ["psycopg2"] [tool.poetry.urls] "Bug Tracker" = "https://github.com/python-poetry/poetry/issues" [tool.poetry.plugins."blogtool.parsers"] ".rst" = "some_module:SomeClass" [tool.poetry.scripts] poetry = 'poetry.console:run' pdm-2.23.1/tests/fixtures/requirements-include.txt000066400000000000000000000000241477560627500223160ustar00rootroot00000000000000-r requirements.txt pdm-2.23.1/tests/fixtures/requirements.txt000066400000000000000000000007661477560627500207120ustar00rootroot00000000000000--index-url=https://pypi.org/simple --extra-index-url=https://pypi.example.com/simple webassets==2.0 werkzeug==0.16.0 whoosh==2.7.4; sys_platform == "win32" wtforms==2.2.1 --hash=sha256:0cdbac3e7f6878086c334aa25dc5a33869a3954e9d1e015130d65a69309b3b61 --hash=sha256:e3ee092c827582c50877cdbd49e9ce6d2c5c1f6561f849b3b068c1b8029626f1 -e git+https://github.com/pypa/pip.git@main#egg=pip git+https://github.com/techalchemy/test-project.git@master#egg=pep508-package&subdirectory=parent_folder/pep508-package pdm-2.23.1/tests/models/000077500000000000000000000000001477560627500150275ustar00rootroot00000000000000pdm-2.23.1/tests/models/__init__.py000066400000000000000000000000001477560627500171260ustar00rootroot00000000000000pdm-2.23.1/tests/models/test_backends.py000066400000000000000000000072361477560627500202220ustar00rootroot00000000000000import shutil from pathlib import Path import pytest from pdm.models.backends import _BACKENDS, get_backend, get_relative_path from pdm.project import Project from pdm.utils import cd from tests import FIXTURES def _setup_backend(project: Project, backend: str): project.pyproject.metadata["requires-python"] = ">=3.6" backend_cls = get_backend(backend) project.pyproject.build_system.update(backend_cls.build_system()) project.root.joinpath("test_project").mkdir() project.root.joinpath("test_project/__init__.py").touch() if backend == "setuptools": project.pyproject._data.setdefault("tool", {}).setdefault("setuptools", {}).update(packages=["test_project"]) elif backend == "hatchling": project.pyproject._data.setdefault("tool", {}).setdefault("hatch", {}).setdefault("metadata", {}).update( {"allow-direct-references": True} ) project.pyproject.write() project._environment = None assert isinstance(project.backend, backend_cls) @pytest.mark.parametrize("backend", _BACKENDS.keys()) def test_project_backend(project, working_set, backend, pdm): _setup_backend(project, backend) shutil.copytree(FIXTURES / "projects/demo", project.root / "demo") project.root.joinpath("sub").mkdir() with cd(project.root.joinpath("sub")): pdm(["add", "--no-self", "../demo"], obj=project, strict=True) assert "idna" in working_set assert "demo" in working_set dep = project.pyproject.metadata["dependencies"][0] demo_path = project.root.joinpath("demo") demo_url = demo_path.as_uri() if backend == "pdm-backend": assert dep == "demo @ file:///${PROJECT_ROOT}/demo" elif backend == "hatchling": assert dep == "demo @ {root:uri}/demo" else: assert dep == f"demo @ {demo_url}" assert project.backend.expand_line(dep) == f"demo @ {demo_url}" if backend not in ("hatchling", "pdm-backend"): candidate = project.make_self_candidate() # We skip hatchling here to avoid installing hatchling into the build env metadata_dependency = candidate.prepare(project.environment).metadata.requires[0] assert metadata_dependency == f"demo @ {demo_url}" def test_hatch_expand_variables(monkeypatch): root = Path().absolute() root_url = root.as_uri() backend = get_backend("hatchling")(root) monkeypatch.setenv("BAR", "bar") assert backend.expand_line("demo @ {root:uri}/demo") == f"demo @ {root_url}/demo" assert backend.expand_line("demo=={env:FOO:{env:BAR}}") == "demo==bar" assert backend.relative_path_to_url("demo package") == "{root:uri}/demo%20package" assert backend.relative_path_to_url("../demo") == "{root:uri}/../demo" def test_pdm_backend_expand_variables(monkeypatch): root = Path().absolute() root_url = root.as_uri() backend = get_backend("pdm-backend")(root) monkeypatch.setenv("BAR", "bar") assert backend.expand_line("demo @ file:///${PROJECT_ROOT}/demo") == f"demo @ {root_url}/demo" assert backend.expand_line("demo==${BAR}") == "demo==bar" assert backend.relative_path_to_url("demo package") == "file:///${PROJECT_ROOT}/demo%20package" assert backend.relative_path_to_url("../demo") == "file:///${PROJECT_ROOT}/../demo" @pytest.mark.parametrize( "url,path", [ ("file:///foo/bar", None), ("https://example.org", None), ("file:///${PROJECT_ROOT}/demo%20package", "demo package"), ("file:///${PROJECT_ROOT}/../demo", "../demo"), ("{root:uri}/demo%20package", "demo package"), ], ) def test_get_relative_path(url, path): assert get_relative_path(url) == path pdm-2.23.1/tests/models/test_candidates.py000066400000000000000000000312211477560627500205360ustar00rootroot00000000000000from __future__ import annotations import shutil import pytest from unearth import Link from pdm.exceptions import RequirementError from pdm.models.candidates import Candidate from pdm.models.requirements import Requirement, parse_requirement from pdm.models.specifiers import PySpecSet from pdm.utils import is_path_relative_to from tests import FIXTURES def to_lines(requirements: list[Requirement]) -> list[str]: return sorted([r.as_line() for r in requirements]) @pytest.mark.usefixtures("local_finder") def test_parse_local_directory_metadata(project, is_editable): requirement_line = f"{(FIXTURES / 'projects/demo').as_posix()}" req = parse_requirement(requirement_line, is_editable) candidate = Candidate(req) assert to_lines(candidate.prepare(project.environment).get_dependencies_from_metadata()) == [ 'chardet; os_name == "nt"', "idna", ] assert candidate.name == "demo" assert candidate.version == "0.0.1" @pytest.mark.usefixtures("vcs", "local_finder") def test_parse_vcs_metadata(project, is_editable): requirement_line = "git+https://github.com/test-root/demo.git@master#egg=demo" req = parse_requirement(requirement_line, is_editable) candidate = Candidate(req) assert to_lines(candidate.prepare(project.environment).get_dependencies_from_metadata()) == [ 'chardet; os_name == "nt"', "idna", ] assert candidate.name == "demo" assert candidate.version == "0.0.1" lockfile = candidate.as_lockfile_entry(project.root) assert lockfile["ref"] == "master" if is_editable: assert "revision" not in lockfile else: assert lockfile["revision"] == "1234567890abcdef" @pytest.mark.usefixtures("local_finder") @pytest.mark.parametrize( "requirement_line", [ f"{(FIXTURES / 'artifacts/demo-0.0.1.tar.gz').as_posix()}", f"{(FIXTURES / 'artifacts/demo-0.0.1-py2.py3-none-any.whl').as_posix()}", ], ) def test_parse_artifact_metadata(requirement_line, project): req = parse_requirement(requirement_line) candidate = Candidate(req) assert to_lines(candidate.prepare(project.environment).get_dependencies_from_metadata()) == [ 'chardet; os_name == "nt"', "idna", ] assert candidate.name == "demo" assert candidate.version == "0.0.1" @pytest.mark.usefixtures("local_finder") def test_parse_metadata_with_extras(project): req = parse_requirement( f"demo[tests,security] @ file://{(FIXTURES / 'artifacts/demo-0.0.1-py2.py3-none-any.whl').as_posix()}" ) candidate = Candidate(req) prepared = candidate.prepare(project.environment) assert prepared.link.is_wheel assert to_lines(prepared.get_dependencies_from_metadata()) == [ "pytest", 'requests; python_version >= "3.6"', ] @pytest.mark.usefixtures("local_finder") def test_parse_remote_link_metadata(project): req = parse_requirement("http://fixtures.test/artifacts/demo-0.0.1-py2.py3-none-any.whl") candidate = Candidate(req) prepared = candidate.prepare(project.environment) assert prepared.link.is_wheel assert to_lines(prepared.get_dependencies_from_metadata()) == [ 'chardet; os_name == "nt"', "idna", ] assert candidate.name == "demo" assert candidate.version == "0.0.1" @pytest.mark.usefixtures("local_finder") @pytest.mark.parametrize( "req_str", [ "demo @ file:///${PROJECT_ROOT}/tests/fixtures/artifacts/demo-0.0.1-py2.py3-none-any.whl", "demo @ file:///${PROJECT_ROOT}/tests/fixtures/artifacts/demo-0.0.1.tar.gz", "demo @ file:///${PROJECT_ROOT}/tests/fixtures/projects/demo", "-e ./tests/fixtures/projects/demo", "-e file:///${PROJECT_ROOT}/tests/fixtures/projects/demo#egg=demo", "-e file:///${PROJECT_ROOT}/tests/fixtures/projects/demo#-with-hash#egg=demo", ], ) def test_expand_project_root_in_url(req_str, core): project = core.create_project(FIXTURES.parent.parent) if req_str.startswith("-e "): req = parse_requirement(req_str[3:], True) else: req = parse_requirement(req_str) req.relocate(project.backend) candidate = Candidate(req) assert to_lines(candidate.prepare(project.environment).get_dependencies_from_metadata()) == [ 'chardet; os_name == "nt"', "idna", ] lockfile_entry = candidate.as_lockfile_entry(project.root) if "path" in lockfile_entry: assert lockfile_entry["path"].startswith("./") else: assert "${PROJECT_ROOT}" in lockfile_entry["url"] @pytest.mark.usefixtures("local_finder") def test_parse_project_file_on_build_error(project): req = parse_requirement(f"{(FIXTURES / 'projects/demo-failure').as_posix()}") candidate = Candidate(req) assert to_lines(candidate.prepare(project.environment).get_dependencies_from_metadata()) == [ 'chardet; os_name == "nt"', "idna", ] assert candidate.name == "demo" assert candidate.version == "0.0.1" @pytest.mark.usefixtures("local_finder") def test_parse_project_file_on_build_error_with_extras(project): req = parse_requirement(f"{(FIXTURES / 'projects/demo-failure').as_posix()}") req.extras = ("security", "tests") candidate = Candidate(req) deps = to_lines(candidate.prepare(project.environment).get_dependencies_from_metadata()) assert 'requests; python_version >= "3.6"' in deps assert "pytest" in deps assert candidate.name == "demo" assert candidate.version == "0.0.1" @pytest.mark.usefixtures("local_finder") def test_parse_project_file_on_build_error_no_dep(project): req = parse_requirement(f"{(FIXTURES / 'projects/demo-failure-no-dep').as_posix()}") candidate = Candidate(req) assert to_lines(candidate.prepare(project.environment).get_dependencies_from_metadata()) == [] assert candidate.name == "demo" assert candidate.version == "0.0.1" @pytest.mark.usefixtures("local_finder") def test_parse_poetry_project_metadata(project, is_editable): req = parse_requirement(f"{(FIXTURES / 'projects/poetry-demo').as_posix()}", is_editable) candidate = Candidate(req) requests_dep = "requests<3.0,>=2.6" assert to_lines(candidate.prepare(project.environment).get_dependencies_from_metadata()) == [requests_dep] assert candidate.name == "poetry-demo" assert candidate.version == "0.1.0" @pytest.mark.usefixtures("local_finder") def test_parse_flit_project_metadata(project, is_editable): req = parse_requirement(f"{(FIXTURES / 'projects/flit-demo').as_posix()}", is_editable) candidate = Candidate(req) deps = to_lines(candidate.prepare(project.environment).get_dependencies_from_metadata()) requests_dep = "requests>=2.6" assert requests_dep in deps assert 'configparser; python_version == "2.7"' in deps assert candidate.name == "pyflit" assert candidate.version == "0.1.0" @pytest.mark.usefixtures("vcs", "local_finder") def test_vcs_candidate_in_subdirectory(project, is_editable): line = "git+https://github.com/test-root/demo-parent-package.git@master#egg=package-a&subdirectory=package-a" req = parse_requirement(line, is_editable) candidate = Candidate(req) assert to_lines(candidate.prepare(project.environment).get_dependencies_from_metadata()) == ["flask"] assert candidate.version == "0.1.0" line = "git+https://github.com/test-root/demo-parent-package.git@master#egg=package-b&subdirectory=package-b" req = parse_requirement(line, is_editable) candidate = Candidate(req) expected_deps = ["django"] assert to_lines(candidate.prepare(project.environment).get_dependencies_from_metadata()) == expected_deps tail = "+editable" if is_editable else "" assert candidate.version == f"0.1.0{tail}" @pytest.mark.usefixtures("local_finder") def test_sdist_candidate_with_wheel_cache(project, mocker): file_link = Link((FIXTURES / "artifacts/demo-0.0.1.tar.gz").as_uri()) built_path = FIXTURES / "artifacts/demo-0.0.1-py2.py3-none-any.whl" wheel_cache = project.make_wheel_cache() cache_path = wheel_cache.get_path_for_link(file_link, project.environment.spec) if not cache_path.exists(): cache_path.mkdir(parents=True) shutil.copy2(built_path, cache_path) req = parse_requirement(file_link.url) downloader = mocker.patch("unearth.finder.unpack_link") prepared = Candidate(req).prepare(project.environment) prepared.metadata downloader.assert_not_called() assert prepared._cached.name == built_path.name prepared._cached = None builder = mocker.patch("pdm.builders.WheelBuilder.build") wheel = prepared.build() builder.assert_not_called() assert wheel.name == built_path.name @pytest.mark.usefixtures("vcs", "local_finder") def test_cache_vcs_immutable_revision(project): req = parse_requirement("git+https://github.com/test-root/demo.git@master#egg=demo") candidate = Candidate(req) candidate.prepare(project.environment).build() assert not is_path_relative_to(candidate.prepared._get_build_cache(), project.cache_dir) assert candidate.get_revision() == "1234567890abcdef" req = parse_requirement("git+https://github.com/test-root/demo.git@1234567890abcdef#egg=demo") candidate = Candidate(req) candidate.prepare(project.environment).build() assert is_path_relative_to(candidate.prepared._get_build_cache(), project.cache_dir) assert candidate.get_revision() == "1234567890abcdef" # test the revision can be got correctly after cached prepared = Candidate(req).prepare(project.environment) assert not prepared._source_dir assert prepared.revision == "1234567890abcdef" @pytest.mark.usefixtures("local_finder") def test_cache_egg_info_sdist(project): req = parse_requirement("demo @ http://fixtures.test/artifacts/demo-0.0.1.tar.gz") candidate = Candidate(req) candidate.prepare(project.environment).build() assert is_path_relative_to(candidate.prepared._get_build_cache(), project.cache_dir) def test_invalidate_incompatible_wheel_link(project): project.project_config["pypi.url"] = "https://my.pypi.org/simple" project.environment.python_requires = PySpecSet(">=3.6") project.environment.__dict__["spec"] = project.environment.spec.replace(platform="linux") req = parse_requirement("demo") prepared = Candidate( req, name="demo", version="0.0.1", link=Link("http://fixtures.test/artifacts/demo-0.0.1-cp36-cp36m-win_amd64.whl"), ).prepare(project.environment) prepared._obtain(True) assert prepared._cached.name == prepared.link.filename == "demo-0.0.1-cp36-cp36m-win_amd64.whl" prepared._obtain(False) assert prepared._cached.name == prepared.link.filename == "demo-0.0.1-py2.py3-none-any.whl" def test_legacy_pep345_tag_link(project): project.project_config["pypi.url"] = "https://my.pypi.org/simple" req = parse_requirement("pep345-legacy") repo = project.get_repository() _ = next(iter(repo.find_candidates(req))) @pytest.mark.filterwarnings("ignore::FutureWarning") def test_ignore_invalid_py_version(project): project.project_config["pypi.url"] = "https://my.pypi.org/simple" req = parse_requirement("wheel") repo = project.get_repository() _ = next(iter(repo.find_candidates(req))) def test_find_candidates_from_find_links(project): project.pyproject.settings["source"] = [ {"name": "test", "url": "http://fixtures.test/index/demo.html", "verify_ssl": False, "type": "find_links"} ] project.pyproject.write(False) repo = project.get_repository() candidates = list(repo.find_candidates(parse_requirement("demo"))) assert len(candidates) == 1 assert candidates[0].link.filename == "demo-0.0.1-py2.py3-none-any.whl" def test_parse_metadata_from_pep621(project, mocker): builder = mocker.patch("pdm.builders.wheel.WheelBuilder.build") req = parse_requirement(f"test-hatch @ file://{FIXTURES.as_posix()}/projects/test-hatch-static") candidate = Candidate(req) distribution = candidate.prepare(project.environment).metadata assert sorted(distribution.requires) == ["click", "requests"] assert distribution.metadata["Summary"] == "Test hatch project" builder.assert_not_called() def test_parse_metadata_with_dynamic_fields(project, local_finder): req = parse_requirement(f"demo-package @ file://{FIXTURES.as_posix()}/projects/demo-src-package") candidate = Candidate(req) metadata = candidate.prepare(project.environment).metadata assert not metadata.requires assert metadata.version == "0.1.0" def test_get_metadata_for_non_existing_path(project): req = parse_requirement("file:///${PROJECT_ROOT}/non-existing-path") candidate = Candidate(req) with pytest.raises(RequirementError, match="The local path '.+' does not exist"): candidate.prepare(project.environment).metadata pdm-2.23.1/tests/models/test_marker.py000066400000000000000000000042161477560627500177240ustar00rootroot00000000000000import pytest from pdm.models.markers import EnvSpec, get_marker from pdm.models.specifiers import PySpecSet @pytest.mark.parametrize( "original,marker,py_spec", [ ("python_version > '3'", "", ">=3.1"), ("python_version > '3.8'", "", ">=3.9"), ("python_version != '3.8'", "", "!=3.8.*"), ("python_version == '3.7'", "", "==3.7.*"), ("python_version in '3.6 3.7'", "", ">=3.6.0,<3.8.0"), ("python_full_version >= '3.6.0'", "", ">=3.6"), ("python_full_version not in '3.8.3'", "", "!=3.8.3"), # mixed marker and python version ("python_version > '3.7' and os_name == 'nt'", 'os_name == "nt"', ">=3.8"), ( "python_version > '3.7' or os_name == 'nt'", 'python_version > "3.7" or os_name == "nt"', "", ), ], ) def test_split_pyspec(original, marker, py_spec): m = get_marker(original) a, b = m.split_pyspec() assert marker == str(a) assert b == PySpecSet(py_spec) @pytest.mark.parametrize( "marker,env_spec,expected", [ ("os_name == 'nt'", EnvSpec.from_spec(">=3.10", "windows"), True), ("os_name == 'nt'", EnvSpec.from_spec(">=3.10"), True), ("os_name != 'nt'", EnvSpec.from_spec(">=3.10", "windows"), False), ("python_version >= '3.7' and os_name == 'nt'", EnvSpec.from_spec(">=3.10"), True), ("python_version < '3.7' and os_name == 'nt'", EnvSpec.from_spec(">=3.10"), False), ("python_version < '3.7' or os_name == 'nt'", EnvSpec.from_spec(">=3.10"), False), ("python_version >= '3.7' and os_name == 'nt'", EnvSpec.from_spec(">=3.10", "linux"), False), ("python_version >= '3.7' or os_name == 'nt'", EnvSpec.from_spec(">=3.10", "linux"), True), ("python_version >= '3.7' and implementation_name == 'pypy'", EnvSpec.from_spec(">=3.10"), True), ( "python_version >= '3.7' and implementation_name == 'pypy'", EnvSpec.from_spec(">=3.10", implementation="cpython"), False, ), ], ) def test_match_env_spec(marker, env_spec, expected): m = get_marker(marker) assert m.matches(env_spec) is expected pdm-2.23.1/tests/models/test_requirements.py000066400000000000000000000101311477560627500211570ustar00rootroot00000000000000from __future__ import annotations import os import pytest from pdm.models.requirements import RequirementError, filter_requirements_with_extras, parse_requirement from pdm.utils import PACKAGING_22 from tests import FIXTURES FILE_PREFIX = "file:///" if os.name == "nt" else "file://" REQUIREMENTS = [ ("requests", None), ("requests<2.21.0,>=2.20.0", None), ( 'requests==2.19.0; os_name == "nt"', None, ), ( 'requests[security,tests]==2.8.*,>=2.8.1; python_version < "2.7"', None, ), ( 'pip @ https://github.com/pypa/pip/archive/1.3.1.zip ; python_version > "3.4"', None, ), ( "git+http://git.example.com/MyProject.git@master#egg=MyProject", "MyProject @ git+http://git.example.com/MyProject.git@master", ), ( "https://github.com/pypa/pip/archive/1.3.1.zip", None, ), ( (FIXTURES / "projects/demo").as_posix(), "demo @ " + (FIXTURES / "projects/demo").as_uri(), ), ( (FIXTURES / "artifacts/demo-0.0.1-py2.py3-none-any.whl").as_posix(), "demo @ " + (FIXTURES / "artifacts/demo-0.0.1-py2.py3-none-any.whl").as_uri(), ), ( (FIXTURES / "projects/demo").as_posix() + "[security]", "demo[security] @ " + (FIXTURES / "projects/demo").as_uri(), ), ( 'requests; python_version=="3.7.*"', 'requests; python_version == "3.7.*"', ), ( "git+git@github.com:pypa/pip.git#egg=pip", "pip @ git+ssh://git@github.com/pypa/pip.git", ), pytest.param( "foo >=4.*, <=5.*", "foo<5.0,>=4.0", marks=pytest.mark.skipif(not PACKAGING_22, reason="packaging 22+ required"), ), pytest.param( "foo (>=4.*, <=5.*)", "foo<5.0,>=4.0", marks=pytest.mark.skipif(not PACKAGING_22, reason="packaging 22+ required"), ), pytest.param( "foo>=3.0+g1234; python_version>='3.6'", 'foo>=3.0; python_version >= "3.6"', marks=pytest.mark.skipif(not PACKAGING_22, reason="packaging 22+ required"), ), ] def filter_requirements_to_lines( requirements: list[str], extras: tuple[str, ...], include_default: bool = False ) -> list[str]: return [ req.as_line() for req in filter_requirements_with_extras(requirements, extras, include_default=include_default) ] @pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.parametrize("req, result", REQUIREMENTS) def test_convert_req_dict_to_req_line(req, result): r = parse_requirement(req) result = result or req assert r.as_line() == result @pytest.mark.parametrize( "line,expected", [ ("requests; os_name=>'nt'", None), ("django>=2<4", None), ], ) def test_illegal_requirement_line(line, expected): with pytest.raises(RequirementError, match=expected): parse_requirement(line) @pytest.mark.parametrize("line", ["requests >= 2.19.0", "https://github.com/pypa/pip/archive/1.3.1.zip"]) def test_not_supported_editable_requirement(line): with pytest.raises(RequirementError, match="Editable requirement is only supported"): parse_requirement(line, True) def test_filter_requirements_with_extras(): requirements = [ "foo; extra == 'a'", "bar; extra == 'b'", "baz; extra == 'a' or extra == 'b'", "qux; extra == 'a' and extra == 'b'", "ping; os_name == 'nt' and extra == 'a'", "blah", ] assert filter_requirements_to_lines(requirements, ()) == ["blah"] assert filter_requirements_to_lines(requirements, ("a",)) == ["foo", "baz", 'ping; os_name == "nt"'] assert filter_requirements_to_lines(requirements, ("b",)) == ["bar", "baz"] assert filter_requirements_to_lines(requirements, ("a", "b")) == [ "foo", "bar", "baz", "qux", 'ping; os_name == "nt"', ] assert filter_requirements_to_lines(requirements, ("c",)) == [] assert filter_requirements_to_lines(requirements, ("a", "b"), include_default=True) == [ "foo", "bar", "baz", "qux", 'ping; os_name == "nt"', "blah", ] pdm-2.23.1/tests/models/test_session.py000066400000000000000000000015401477560627500201230ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from pdm.project.core import Project def test_session_sources_all_proxy(project: Project, mocker, monkeypatch): monkeypatch.setenv("all_proxy", "http://localhost:8888") mock_get_transport = mocker.patch("pdm.models.session._get_transport") assert project.environment.session is not None transport_args = mock_get_transport.call_args assert transport_args is not None assert transport_args.kwargs["proxy"].url == "http://localhost:8888" monkeypatch.setenv("no_proxy", "pypi.org") mock_get_transport.reset_mock() del project.environment.session assert project.environment.session is not None transport_args = mock_get_transport.call_args assert transport_args is not None assert transport_args.kwargs["proxy"] is None pdm-2.23.1/tests/models/test_setup_parsing.py000066400000000000000000000047741477560627500213370ustar00rootroot00000000000000import pytest from pdm.models.setup import Setup @pytest.mark.parametrize( "content, result", [ ( """[metadata] name = foo version = 0.1.0 """, Setup("foo", "0.1.0"), ), ( """[metadata] name = foo version = attr:foo.__version__ """, Setup("foo", "0.0.0"), ), ( """[metadata] name = foo version = 0.1.0 [options] python_requires = >=3.6 install_requires = click requests [options.extras_require] tui = rich """, Setup("foo", "0.1.0", ["click", "requests"], {"tui": ["rich"]}, ">=3.6"), ), ], ) def test_parse_setup_cfg(content, result, tmp_path): tmp_path.joinpath("setup.cfg").write_text(content) assert Setup.from_directory(tmp_path) == result @pytest.mark.parametrize( "content,result", [ ( """from setuptools import setup setup(name="foo", version="0.1.0") """, Setup("foo", "0.1.0"), ), ( """import setuptools setuptools.setup(name="foo", version="0.1.0") """, Setup("foo", "0.1.0"), ), ( """from setuptools import setup kwargs = {"name": "foo", "version": "0.1.0"} setup(**kwargs) """, Setup("foo", "0.1.0"), ), ( """from setuptools import setup name = 'foo' setup(name=name, version="0.1.0") """, Setup("foo", "0.1.0"), ), ( """from setuptools import setup setup(name="foo", version="0.1.0", install_requires=['click', 'requests'], python_requires='>=3.6', extras_require={'tui': ['rich']}) """, Setup("foo", "0.1.0", ["click", "requests"], {"tui": ["rich"]}, ">=3.6"), ), ( """from pathlib import Path from setuptools import setup version = Path('__version__.py').read_text().strip() setup(name="foo", version=version) """, Setup("foo", "0.0.0"), ), ], ) def test_parse_setup_py(content, result, tmp_path): tmp_path.joinpath("setup.py").write_text(content) assert Setup.from_directory(tmp_path) == result def test_parse_pyproject_toml(tmp_path): content = """[project] name = "foo" version = "0.1.0" requires-python = ">=3.6" dependencies = ["click", "requests"] [project.optional-dependencies] tui = ["rich"] """ tmp_path.joinpath("pyproject.toml").write_text(content) result = Setup("foo", "0.1.0", ["click", "requests"], {"tui": ["rich"]}, ">=3.6") assert Setup.from_directory(tmp_path) == result pdm-2.23.1/tests/models/test_specifiers.py000066400000000000000000000062311477560627500205760ustar00rootroot00000000000000import pytest from pdm.models.specifiers import PySpecSet @pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.parametrize( "original,normalized", [ (">=3.6", ">=3.6"), ("<3.8", "<3.8"), ("~=2.7.0", "~=2.7.0"), ("", ""), (">=3.6,<3.8", "<3.8,>=3.6"), (">3.6", ">3.6"), ("<=3.7", "<=3.7"), (">=3.4.*", ">=3.4.0"), (">3.4.*", ">=3.4.0"), ("<=3.4.*", "<3.4.0"), ("<3.4.*", "<3.4.0"), (">=3.0+g1234", ">=3.0"), ("<3.0+g1234", "<3.0"), ("<3.10.0a6", "<3.10.0a6"), ("<3.10.2a3", "<3.10.2a3"), ], ) def test_normalize_pyspec(original, normalized): spec = PySpecSet(original) assert str(spec) == normalized @pytest.mark.parametrize( "left,right,result", [ (">=3.6", ">=3.0", ">=3.6"), (">=3.6", "<3.8", "<3.8,>=3.6"), ("", ">=3.6", ">=3.6"), (">=3.6", "<3.2", ""), (">=2.7,!=3.0.*", "!=3.1.*", "!=3.0.*,!=3.1.*,>=2.7"), (">=3.11.0a2", "<3.11.0b", ">=3.11.0a2,<3.11.0b0"), ("<3.11.0a2", ">3.11.0b", ""), ], ) def test_pyspec_and_op(left, right, result): left = PySpecSet(left) right = PySpecSet(right) assert left & right == PySpecSet(result) @pytest.mark.parametrize( "left,right,result", [ (">=3.6", ">=3.0", ">=3.0"), ("", ">=3.6", ""), (">=3.6", "<3.7", ""), (">=3.6,<3.8", ">=3.4,<3.7", "<3.8,>=3.4"), ("~=2.7", ">=3.6", "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"), ("<2.7.15", ">=3.0", "!=2.7.15,!=2.7.16,!=2.7.17,!=2.7.18"), (">3.11.0a2", ">3.11.0b", ">3.11.0a2"), ], ) def test_pyspec_or_op(left, right, result): left = PySpecSet(left) right = PySpecSet(right) assert str(left | right) == result def test_impossible_pyspec(): spec = PySpecSet(">=3.6,<3.4") a = PySpecSet(">=2.7") assert spec.is_empty() assert (spec & a).is_empty() assert spec | a == a @pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.parametrize( "left,right", [ ("~=2.7", ">=2.7"), (">=3.6", ""), (">=3.7", ">=3.6,<4.0"), (">=2.7,<3.0", ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"), (">=3.6", ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"), ( ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*", ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", ), (">=3.11.*", ">=3.11.0rc"), ], ) def test_pyspec_is_subset_superset(left, right): left = PySpecSet(left) right = PySpecSet(right) assert left.is_subset(right), f"{left}, {right}" assert right.is_superset(left), f"{left}, {right}" @pytest.mark.parametrize( "left,right", [ ("~=2.7", ">=2.6,<2.7.15"), (">=3.7", ">=3.6,<3.9"), (">=3.7,<3.6", "==2.7"), (">=3.0,!=3.4.*", ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"), (">=3.11.0", "<3.11.0a"), ], ) def test_pyspec_isnot_subset_superset(left, right): left = PySpecSet(left) right = PySpecSet(right) assert not left.is_subset(right), f"{left}, {right}" assert not left.is_superset(right), f"{left}, {right}" pdm-2.23.1/tests/models/test_versions.py000066400000000000000000000052171477560627500203150ustar00rootroot00000000000000import pytest from pdm.models.versions import InvalidPyVersion, Version def test_unsupported_post_version() -> None: with pytest.raises(InvalidPyVersion): Version("3.10.0post1") def test_support_prerelease_version() -> None: assert not Version("3.9.0").is_prerelease v = Version("3.9.0a4") assert v.is_prerelease assert str(v) == "3.9.0a4" assert v.complete() == v assert v.bump() == Version("3.9.0a5") assert v.bump(2) == Version("3.9.1") def test_normalize_non_standard_version(): version = Version("3.9*") assert str(version) == "3.9.*" def test_version_comparison(): assert Version("3.9.0") < Version("3.9.1") assert Version("3.4") < Version("3.9.1") assert Version("3.7.*") < Version("3.7.5") assert Version("3.7") == Version((3, 7)) assert Version("3.9.0a") != Version("3.9.0") assert Version("3.9.0a") == Version("3.9.0a0") assert Version("3.10.0a9") < Version("3.10.0a12") assert Version("3.10.0a12") < Version("3.10.0b1") assert Version("3.7.*") < Version("3.7.1b") def test_version_is_wildcard(): assert not Version("3").is_wildcard assert Version("3.*").is_wildcard def test_version_is_py2(): assert not Version("3.8").is_py2 assert Version("2.7").is_py2 @pytest.mark.parametrize( "version,args,result", [("3.9", (), "3.9.0"), ("3.9", ("*",), "3.9.*"), ("3", (0, 2), "3.0")], ) def test_version_complete(version, args, result): assert str(Version(version).complete(*args)) == result @pytest.mark.parametrize( "version,idx,result", [ ("3.8.0", -1, "3.8.1"), ("3.8", -1, "3.9.0"), ("3", 0, "4.0.0"), ("3.8.1", 1, "3.9.0"), ], ) def test_version_bump(version, idx, result): assert str(Version(version).bump(idx)) == result @pytest.mark.parametrize( "version,other,result", [ ("3.8.0", "3.8", True), ("3.8.*", "3.8", True), ("3.8.1", "3.7", False), ("3.8", "3.8.2", False), ], ) def test_version_startswith(version, other, result): assert Version(version).startswith(Version(other)) is result def test_version_getitem(): version = Version("3.8.6") assert version[0] == 3 assert version[1] == 8 assert version[2] == 6 assert version[1:2] == Version("8") assert version[:-1] == Version("3.8") def test_version_setitem(): version = Version("3.8.*") version1 = version.complete() version1[-1] = 0 assert version1 == Version("3.8.0") version2 = version.complete() version2[0] = 4 assert version2 == Version("4.8.*") version3 = version.complete() with pytest.raises(TypeError): version3[:2] = (1, 2) pdm-2.23.1/tests/resolver/000077500000000000000000000000001477560627500154055ustar00rootroot00000000000000pdm-2.23.1/tests/resolver/__init__.py000066400000000000000000000000001477560627500175040ustar00rootroot00000000000000pdm-2.23.1/tests/resolver/test_graph.py000066400000000000000000000010611477560627500201150ustar00rootroot00000000000000import itertools from pdm.resolver.graph import OrderedSet def test_ordered_set(): elems = ["A", "bb", "c3"] all_sets = set() for case in itertools.permutations(elems): s = OrderedSet(case) all_sets.add(s) assert list(s) == list(case) assert len(s) == len(case) for e in elems: assert e in s assert e + "1" not in s assert str(s) == f"{{{', '.join(map(repr, case))}}}" assert repr(s) == f"OrderedSet({{{', '.join(map(repr, case))}}})" assert len(all_sets) == 1 pdm-2.23.1/tests/resolver/test_resolve.py000066400000000000000000000400511477560627500204750ustar00rootroot00000000000000import pytest from resolvelib.resolvers import ResolutionImpossible from pdm.cli.actions import resolve_candidates_from_lockfile from pdm.exceptions import PackageWarning from pdm.models.markers import EnvSpec from pdm.models.requirements import parse_requirement from pdm.models.specifiers import PySpecSet from pdm.project.lockfile import FLAG_DIRECT_MINIMAL_VERSIONS, FLAG_INHERIT_METADATA from pdm.resolver.reporters import LockReporter from pdm.utils import get_requirement_from_override from tests import FIXTURES @pytest.fixture() def resolve(project, repository): def resolve_func( lines, requires_python=None, allow_prereleases=None, strategy="all", tracked_names=None, direct_minimal_versions=False, inherit_metadata=False, platform=None, ): env_spec = project.environment.allow_all_spec replace_dict = {} if requires_python: replace_dict["requires_python"] = PySpecSet(requires_python) if platform: replace_dict["platform"] = platform env_spec = env_spec.replace(**replace_dict) if allow_prereleases is not None: project.pyproject.settings.setdefault("resolution", {})["allow-prereleases"] = allow_prereleases requirements = [] for line in lines: if line.startswith("-e "): requirements.append(parse_requirement(line[3:], True)) else: requirements.append(parse_requirement(line)) ui = project.core.ui strategies = project.lockfile.default_strategies.copy() if inherit_metadata: strategies.add(FLAG_INHERIT_METADATA) if direct_minimal_versions: strategies.add(FLAG_DIRECT_MINIMAL_VERSIONS) with ui.logging("lock"): resolver = project.get_resolver()( environment=project.environment, requirements=requirements, update_strategy=strategy, strategies=strategies, target=env_spec, tracked_names=tracked_names, reporter=LockReporter(), ) return resolver.resolve().candidates return resolve_func def test_resolve_named_requirement(resolve): result = resolve(["requests"]) assert result["requests"].version == "2.19.1" assert result["urllib3"].version == "1.22" assert result["chardet"].version == "3.0.4" assert result["certifi"].version == "2018.11.17" assert result["idna"].version == "2.7" def test_resolve_exclude(resolve, project): project.pyproject.settings.setdefault("resolution", {})["excludes"] = ["urllib3"] result = resolve(["requests"]) assert result["requests"].version == "2.19.1" assert result["chardet"].version == "3.0.4" assert result["certifi"].version == "2018.11.17" assert result["idna"].version == "2.7" assert "urllib3" not in result def test_resolve_requires_python(resolve, project): project.environment.python_requires = PySpecSet(">=2.7") with pytest.warns(PackageWarning) as records: result = resolve(["django"]) assert len(records) > 0 assert result["django"].version == "1.11.8" assert "sqlparse" not in result result = resolve(["django"], ">=3.6") assert result["django"].version == "2.2.9" assert "sqlparse" in result result = resolve(["django; python_version>='3.7'"]) assert result["django"].version == "2.2.9" assert "sqlparse" in result def test_resolve_allow_prereleases(resolve, repository): repository.add_candidate("foo", "1.0.0") repository.add_candidate("foo", "1.1.0-alpha") repository.add_candidate("bar", "1.0.0-beta") result = resolve(["foo"]) assert result["foo"].version == "1.0.0" result = resolve(["foo"], allow_prereleases=True) assert result["foo"].version == "1.1.0-alpha" result = resolve(["foo==1.1.0a0"]) assert result["foo"].version == "1.1.0-alpha" result = resolve(["bar"]) assert result["bar"].version == "1.0.0-beta" with pytest.raises(ResolutionImpossible): resolve(["bar"], allow_prereleases=False) def test_resolve_prereleases_if_disabled_by_project(resolve): result = resolve(["urllib3==1.23b0"], allow_prereleases=False) assert result["urllib3"].version == "1.23b0" def test_resolve_with_extras(resolve): result = resolve(["requests[socks]"]) assert result["pysocks"].version == "1.5.6" assert result["urllib3"].version == "1.22" assert result["chardet"].version == "3.0.4" assert result["certifi"].version == "2018.11.17" assert result["idna"].version == "2.7" assert result["requests"].version == "2.19.1" def test_resolve_with_extras_and_excludes(resolve, project): project.pyproject.settings.setdefault("resolution", {})["excludes"] = ["requests"] result = resolve(["requests[socks]"]) assert result["pysocks"].version == "1.5.6" assert "requests" not in result assert "urllib3" not in result @pytest.mark.parametrize( "requirement_line", [ f"{(FIXTURES / 'artifacts/demo-0.0.1.tar.gz').as_posix()}", f"{(FIXTURES / 'artifacts/demo-0.0.1-py2.py3-none-any.whl').as_posix()}", ], ids=["sdist", "wheel"], ) def test_resolve_local_artifacts(resolve, requirement_line): result = resolve([requirement_line], ">=3.6") assert result["idna"].version == "2.7" @pytest.mark.parametrize( "line", [ (FIXTURES / "projects/demo").as_posix(), "git+https://github.com/test-root/demo.git#egg=demo", ], ) def test_resolve_vcs_and_local_requirements(resolve, line, is_editable, vcs): editable = "-e " if is_editable else "" result = resolve([editable + line], ">=3.6") assert result["idna"].version == "2.7" def test_resolve_vcs_without_explicit_name(resolve, vcs): requirement = "git+https://github.com/test-root/demo.git" result = resolve([requirement], ">=3.6") assert result["idna"].version == "2.7" def test_resolve_local_and_named_requirement(resolve, vcs): requirements = ["demo", "git+https://github.com/test-root/demo.git#egg=demo"] result = resolve(requirements, ">=3.6") assert result["demo"].req.is_vcs requirements = ["git+https://github.com/test-root/demo.git#egg=demo", "demo"] result = resolve(requirements, ">=3.6") assert result["demo"].req.is_vcs def test_resolving_auto_avoid_conflicts(resolve, repository): repository.add_candidate("foo", "0.1.0") repository.add_candidate("foo", "0.2.0") repository.add_dependencies("foo", "0.1.0", ["hoho<2.0"]) repository.add_dependencies("foo", "0.2.0", ["hoho>=2.0"]) repository.add_candidate("bar", "0.1.0") repository.add_dependencies("bar", "0.1.0", ["hoho~=1.1"]) repository.add_candidate("hoho", "2.1") repository.add_candidate("hoho", "1.5") result = resolve(["foo", "bar"]) assert result["foo"].version == "0.1.0" assert result["bar"].version == "0.1.0" assert result["hoho"].version == "1.5" def test_resolve_conflicting_dependencies(resolve, repository): repository.add_candidate("foo", "0.1.0") repository.add_dependencies("foo", "0.1.0", ["hoho>=2.0"]) repository.add_candidate("bar", "0.1.0") repository.add_dependencies("bar", "0.1.0", ["hoho~=1.1"]) repository.add_candidate("hoho", "2.1") repository.add_candidate("hoho", "1.5") with pytest.raises(ResolutionImpossible): resolve(["foo", "bar"]) @pytest.mark.parametrize("overrides", ["2.1", ">=1.8", "==2.1"]) def test_resolve_conflicting_dependencies_with_overrides(project, resolve, repository, overrides): repository.add_candidate("foo", "0.1.0") repository.add_dependencies("foo", "0.1.0", ["hoho>=2.0"]) repository.add_candidate("bar", "0.1.0") repository.add_dependencies("bar", "0.1.0", ["hoho~=1.1"]) repository.add_candidate("baz", "0.1.0") repository.add_dependencies("baz", "0.1.0", ["hoho[extra]~=1.1"]) repository.add_candidate("hoho", "2.1") repository.add_candidate("hoho", "1.5") project.pyproject.settings["resolution"] = {"overrides": {"hoho": overrides}} result = resolve(["foo", "bar"]) assert result["hoho"].version == "2.1" result = resolve(["foo", "baz"]) assert result["hoho"].version == "2.1" def test_resolve_no_available_versions(resolve, repository): repository.add_candidate("foo", "0.1.0") with pytest.raises(ResolutionImpossible): resolve(["foo>=0.2.0"]) def test_exclude_incompatible_requirements(resolve, repository): repository.add_candidate("foo", "0.1.0") repository.add_dependencies("foo", "0.1.0", ["bar; python_version < '3'"]) result = resolve(["foo"], ">=3.6") assert "bar" not in result def test_union_markers_from_different_parents(resolve, repository): repository.add_candidate("foo", "0.1.0") repository.add_dependencies("foo", "0.1.0", ["bar; python_version < '3'"]) repository.add_candidate("bar", "0.1.0") result = resolve(["foo", "bar"], ">=3.6") assert not result["bar"].requires_python def test_requirements_from_different_groups(resolve, repository): repository.add_candidate("foo", "0.1.0") repository.add_candidate("foo", "0.2.0") requirements = ["foo", "foo<0.2.0"] result = resolve(requirements) assert result["foo"].version == "0.1.0" def test_resolve_two_extras_from_the_same_package(resolve): # Case borrowed from pypa/pip#7096 line = (FIXTURES / "projects/demo_extras").as_posix() + "[extra1,extra2]" result = resolve([line]) assert "pysocks" in result assert "pyopenssl" in result def test_resolve_package_with_dummy_upbound(resolve, repository): repository.add_candidate("foo", "0.1.0", ">=3.5,<4.0") result = resolve(["foo"], ">=3.5") assert "foo" in result def test_resolve_dependency_with_extra_marker(resolve, repository): repository.add_candidate("foo", "0.1.0") repository.add_dependencies("foo", "0.1.0", ["pytz; extra=='tz' or extra=='all'"]) result = resolve(["foo"]) assert "pytz" not in result result = resolve(["foo[tz]"]) assert "pytz" in result def test_resolve_circular_dependencies(resolve, repository): repository.add_candidate("foo", "0.1.0") repository.add_dependencies("foo", "0.1.0", ["foobar"]) repository.add_candidate("foobar", "0.2.0") repository.add_dependencies("foobar", "0.2.0", ["foo"]) result = resolve(["foo"]) assert result["foo"].version == "0.1.0" assert result["foobar"].version == "0.2.0" def test_resolve_candidates_to_install(project): project.lockfile.set_data( { "metadata": {"strategy": ["cross_platform"]}, "package": [ { "name": "pytest", "version": "4.6.0", "summary": "pytest module", "dependencies": ["py>=3.0", "configparser; sys_platform=='win32'"], }, { "name": "configparser", "version": "1.2.0", "summary": "configparser module", "dependencies": ["backports"], }, { "name": "py", "version": "3.6.0", "summary": "py module", }, { "name": "backports", "version": "2.2.0", "summary": "backports module", }, ], } ) reqs = [parse_requirement("pytest")] result = resolve_candidates_from_lockfile(project, reqs, env_spec=EnvSpec.from_spec("==3.11", "linux", "cpython")) assert result["pytest"].version == "4.6.0" assert result["py"].version == "3.6.0" assert "configparser" not in result assert "backports" not in result result = resolve_candidates_from_lockfile(project, reqs, env_spec=EnvSpec.from_spec("==3.11", "windows", "cpython")) assert result["pytest"].version == "4.6.0" assert result["py"].version == "3.6.0" assert result["configparser"].version == "1.2.0" assert result["backports"].version == "2.2.0" def test_resolve_prefer_requirement_with_prereleases(resolve): result = resolve(["urllib3", "requests>=2.20.0b0"]) assert result["urllib3"].version == "1.23b0" def test_resolve_with_python_marker(resolve): result = resolve(["demo; python_version>='3.6'"]) assert result["demo"].version == "0.0.1" def test_resolve_file_req_with_prerelease(resolve, vcs): result = resolve( [ "using-demo==0.1.0", "demo @ git+https://github.com/test-root/demo-prerelease.git", ], ">=3.6", allow_prereleases=False, ) assert result["demo"].version == "0.0.2b0" def test_resolve_extra_requirements_no_break_constraints(resolve, repository): repository.add_candidate("foo", "0.1.0") repository.add_dependencies("foo", "0.1.0", ["chardet; extra=='chardet'"]) repository.add_candidate("foo", "0.2.0") repository.add_dependencies("foo", "0.2.0", ["chardet; extra=='chardet'"]) result = resolve(["foo[chardet]<0.2.0"]) assert "chardet" in result assert result["foo"].version == "0.1.0" def test_resolve_extra_and_underlying_to_the_same_version(resolve, repository): repository.add_candidate("foo", "0.1.0") repository.add_dependencies("foo", "0.1.0", ["chardet; extra=='enc'"]) repository.add_candidate("foo", "0.2.0") repository.add_dependencies("foo", "0.2.0", ["chardet; extra=='enc'"]) repository.add_candidate("bar", "0.1.0") repository.add_dependencies("bar", "0.1.0", ["foo[enc]>=0.1.0"]) result = resolve(["foo==0.1.0", "bar"]) assert result["foo"].version == result["foo[enc]"].version == "0.1.0" def test_resolve_skip_candidate_with_invalid_metadata(resolve, repository): repository.add_candidate("sqlparse", "0.4.0") repository.add_dependencies("sqlparse", "0.4.0", ["django>=1.11'"]) result = resolve(["sqlparse"], ">=3.6") assert result["sqlparse"].version == "0.3.0" def test_resolve_direct_minimal_versions(resolve, repository, project): repository.add_candidate("pytz", "2019.6") project.add_dependencies(["django"]) result = resolve(["django"], ">=3.6", direct_minimal_versions=True) assert result["django"].version == "1.11.8" assert result["pytz"].version == "2019.6" def test_resolve_record_markers(resolve, repository, project): repository.add_candidate("A", "1.0") repository.add_candidate("B", "1.0") repository.add_candidate("C", "1.0") repository.add_candidate("D", "1.0") repository.add_candidate("E", "1.0") repository.add_candidate("F", "1.0") repository.add_dependencies("A", "1.0", ["B; os_name == 'posix'", "C; os_name=='nt'"]) # package D has transitive markers that conflict repository.add_dependencies("C", "1.0", ["D; os_name!='nt'", "E; python_version < '3.8'"]) # package E has union markers repository.add_dependencies("B", "1.0", ["E; python_version >= '3.7'"]) # B -> E -> F -> B has circular dependency repository.add_dependencies("E", "1.0", ["F; platform_machine=='x86_64'"]) repository.add_dependencies("F", "1.0", ["B"]) result = resolve(["A"], ">=3.6", inherit_metadata=True) assert result["a"].version == "1.0" assert "d" not in result assert ( str(result["e"].req.marker) == 'python_version >= "3.7" and os_name == "posix" or python_version < "3.8" and os_name == "nt"' ) assert ( str(result["f"].req.marker) == 'python_version >= "3.7" and os_name == "posix" and platform_machine == "x86_64" or ' 'python_version < "3.8" and os_name == "nt" and platform_machine == "x86_64"' ) assert ( str(result["b"].req.marker) == 'os_name == "posix" or (os_name == "posix" or os_name == "nt") and ' 'platform_machine == "x86_64" and python_version < "3.8"' ) @pytest.mark.parametrize( "name,value,output", [ ("foo", "1.0", "foo==1.0"), ("foo", "==1.0", "foo==1.0"), ("foo", ">=1.0", "foo>=1.0"), ("foo", "http://foobar.com", "foo @ http://foobar.com"), ], ) def test_requirement_from_override(name, value, output): assert get_requirement_from_override(name, value) == output pdm-2.23.1/tests/resolver/test_uv_resolver.py000066400000000000000000000061301477560627500213710ustar00rootroot00000000000000import pytest from pdm.models.markers import EnvSpec from pdm.models.requirements import parse_requirement pytestmark = [pytest.mark.network, pytest.mark.uv] def resolve(environment, requirements, target=None): from pdm.resolver.uv import UvResolver reqs = [] for req in requirements: if isinstance(req, str): req = parse_requirement(req) req.groups = ["default"] reqs.append(req) resolver = UvResolver( environment, requirements=reqs, target=target or environment.spec, update_strategy="all", strategies=set(), ) return resolver.resolve() def test_resolve_requirements(project): requirements = ["requests==2.32.0", "urllib3<2"] resolution = resolve(project.environment, requirements) mapping = {p.candidate.identify(): p.candidate for p in resolution.packages} assert mapping["requests"].version == "2.32.0" assert mapping["urllib3"].version.startswith("1.26") def test_resolve_vcs_requirement(project): requirements = ["git+https://github.com/pallets/click.git@8.1.0"] resolution = resolve(project.environment, requirements) mapping = {p.candidate.identify(): p.candidate for p in resolution.packages} assert "colorama" in mapping assert mapping["click"].req.is_vcs def test_resolve_with_python_requires(project): requirements = ["urllib3<2; python_version<'3.10'", "urllib3>=2; python_version>='3.10'"] if project.python.version_tuple >= (3, 10): resolution = resolve(project.environment, requirements, EnvSpec.from_spec(">=3.10")) packages = list(resolution.packages) assert len(packages) == 1 assert packages[0].candidate.version.startswith("2.") resolution = resolve(project.environment, requirements, EnvSpec.from_spec(">=3.8")) packages = list(resolution.packages) assert len(packages) == 2 def test_resolve_dependencies_with_nested_extras(project): name = project.name project.add_dependencies(["urllib3"], "default", write=False) project.add_dependencies(["idna"], "extra1", write=False) project.add_dependencies(["chardet", f"{name}[extra1]"], "extra2", write=False) project.add_dependencies([f"{name}[extra1,extra2]"], "all") dependencies = [*project.get_dependencies(), *project.get_dependencies("all")] assert len(dependencies) == 3, [dep.identify() for dep in dependencies] resolution = resolve(project.environment, dependencies) assert resolution.collected_groups == {"default", "extra1", "extra2", "all"} mapping = {p.candidate.identify(): p.candidate for p in resolution.packages} assert set(mapping) == {"urllib3", "idna", "chardet"} @pytest.mark.parametrize("overrides", ("2.31.0", "==2.31.0")) def test_resolve_dependencies_with_overrides(project, overrides): requirements = ["requests==2.32.0"] project.pyproject.settings["resolution"] = {"overrides": {"requests": overrides}} resolution = resolve(project.environment, requirements) mapping = {p.candidate.identify(): p.candidate for p in resolution.packages} assert mapping["requests"].version == "2.31.0" pdm-2.23.1/tests/test_formats.py000066400000000000000000000236151477560627500166370ustar00rootroot00000000000000import shutil from argparse import Namespace import pytest from pdm.formats import MetaConvertError, flit, pipfile, poetry, requirements, setup_py from pdm.models.requirements import parse_requirement from pdm.utils import cd from tests import FIXTURES def ns(**kwargs): default_options = { "dev": False, "group": None, "expandvars": False, "self": False, "editable_self": False, "hashes": True, } kwargs = {**default_options, **kwargs} self = kwargs.pop("self") rv = Namespace(**kwargs) rv.self = self return rv def test_convert_pipfile(project): golden_file = FIXTURES / "Pipfile" assert pipfile.check_fingerprint(project, golden_file) result, settings = pipfile.convert(project, golden_file, None) assert settings["resolution"]["allow-prereleases"] assert result["requires-python"] == ">=3.6" assert not settings.get("dev-dependencies", {}).get("dev") assert "requests" in result["dependencies"] assert 'pywinusb; sys_platform == "win32"' in result["dependencies"] assert settings["source"][0]["url"] == "https://pypi.python.org/simple" @pytest.mark.parametrize("is_dev", [True, False]) def test_convert_requirements_file(project, is_dev): golden_file = FIXTURES / "requirements.txt" assert requirements.check_fingerprint(project, golden_file) options = ns(dev=is_dev) result, settings = requirements.convert(project, golden_file, options) group = settings["dev-dependencies"]["dev"] if is_dev else result["dependencies"] dev_group = settings["dev-dependencies"]["dev"] assert len(settings["source"]) == 2 assert "webassets==2.0" in group assert 'whoosh==2.7.4; sys_platform == "win32"' in group assert "-e git+https://github.com/pypa/pip.git@main#egg=pip" in dev_group if not is_dev: assert "-e git+https://github.com/pypa/pip.git@main#egg=pip" not in group assert ( "pep508-package @ git+https://github.com/techalchemy/test-project.git" "@master#subdirectory=parent_folder/pep508-package" in group ) def test_convert_requirements_file_without_name(project, vcs): req_file = project.root.joinpath("reqs.txt") project.root.joinpath("reqs.txt").write_text("git+https://github.com/test-root/demo.git\n") assert requirements.check_fingerprint(project, str(req_file)) result, _ = requirements.convert(project, str(req_file), ns()) assert result["dependencies"] == ["demo @ git+https://github.com/test-root/demo.git"] def test_convert_poetry(project): golden_file = FIXTURES / "pyproject.toml" assert poetry.check_fingerprint(project, golden_file) with cd(FIXTURES): result, settings = poetry.convert(project, golden_file, ns()) assert result["authors"] == [ { "name": "Sébastien Eustace", "email": "sebastien@eustace.io", }, { "name": "Example, Inc.", "email": "inc@example.com", }, ] assert result["name"] == "poetry" assert result["version"] == "1.0.0" assert result["license"] == {"text": "MIT"} assert "repository" in result["urls"] assert result["requires-python"] == "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,<4.0,>=2.7" assert 'cleo<1.0.0,>=0.7.6; python_version ~= "2.7"' in result["dependencies"] assert 'cachecontrol[filecache]<1.0.0,>=0.12.4; python_version ~= "3.4"' in result["dependencies"] assert "babel==2.9.0" in result["dependencies"] assert "mysql" in result["optional-dependencies"] assert "psycopg2<3.0,>=2.7" in result["optional-dependencies"]["pgsql"] assert len(settings["dev-dependencies"]["dev"]) == 2 assert result["scripts"] == {"poetry": "poetry.console:run"} assert result["entry-points"]["blogtool.parsers"] == {".rst": "some_module:SomeClass"} build = settings["build"] assert build["includes"] == ["lib/my_package", "tests", "CHANGELOG.md"] assert build["excludes"] == ["my_package/excluded.py"] def test_convert_poetry_12(project): golden_file = FIXTURES / "poetry-new.toml" with cd(FIXTURES): result, settings = poetry.convert(project, golden_file, ns()) assert result["dependencies"] == ["httpx", "pendulum"] assert settings["dev-dependencies"]["test"] == ["pytest<7.0.0,>=6.0.0", "pytest-mock"] def test_convert_flit(project): golden_file = FIXTURES / "projects/flit-demo/pyproject.toml" assert flit.check_fingerprint(project, golden_file) result, settings = flit.convert(project, golden_file, None) assert result["name"] == "pyflit" assert result["version"] == "0.1.0" assert result["description"] == "An awesome flit demo" assert "classifiers" in result["dynamic"] assert result["authors"][0] == { "name": "Thomas Kluyver", "email": "thomas@kluyver.me.uk", } assert result["urls"]["homepage"] == "https://github.com/takluyver/flit" assert result["requires-python"] == ">=3.5" assert result["readme"] == "README.rst" assert result["urls"]["Documentation"] == "https://flit.readthedocs.io/en/latest/" assert result["dependencies"] == [ "requests>=2.6", 'configparser; python_version == "2.7"', ] assert result["optional-dependencies"]["test"] == [ "pytest >=2.7.3", "pytest-cov", ] assert result["scripts"]["flit"] == "flit:main" assert result["entry-points"]["pygments.lexers"]["dogelang"] == "dogelang.lexer:DogeLexer" build = settings["build"] assert build["includes"] == ["doc/"] assert build["excludes"] == ["doc/*.html"] def test_convert_error_preserve_metadata(project): pyproject_file = FIXTURES / "poetry-error.toml" try: poetry.convert(project, pyproject_file, ns()) except MetaConvertError as e: assert e.data["name"] == "test-poetry" assert "dependencies: Invalid specifier" in str(e) else: pytest.fail("Should raise MetaConvertError") def test_import_requirements_with_group(project): golden_file = FIXTURES / "requirements.txt" assert requirements.check_fingerprint(project, golden_file) result, settings = requirements.convert(project, golden_file, ns(group="test")) group = result["optional-dependencies"]["test"] dev_group = settings["dev-dependencies"]["dev"] assert "webassets==2.0" in group assert 'whoosh==2.7.4; sys_platform == "win32"' in group assert "-e git+https://github.com/pypa/pip.git@main#egg=pip" not in group assert "-e git+https://github.com/pypa/pip.git@main#egg=pip" in dev_group assert not result.get("dependencies") def test_export_requirements_with_self(project): result = requirements.export(project, [], ns(self=True, hashes=False)) assert result.strip().splitlines()[-1] == ". # this package" def test_export_requirements_with_editable_self(project): result = requirements.export(project, [], ns(editable_self=True, hashes=False)) assert result.strip().splitlines()[-1] == "-e . # this package" def test_keep_env_vars_in_source(project, monkeypatch): monkeypatch.setenv("USER", "foo") monkeypatch.setenv("PASSWORD", "bar") project.pyproject.settings["source"] = [{"url": "https://${USER}:${PASSWORD}@test.pypi.org/simple", "name": "pypi"}] result = requirements.export(project, [], ns()) assert result.strip().splitlines()[-1] == "--index-url https://${USER}:${PASSWORD}@test.pypi.org/simple" def test_expand_env_vars_in_source(project, monkeypatch): monkeypatch.setenv("USER", "foo") monkeypatch.setenv("PASSWORD", "bar") project.pyproject.settings["source"] = [{"url": "https://foo:bar@test.pypi.org/simple", "name": "pypi"}] result = requirements.export(project, [], ns(expandvars=True)) assert result.strip().splitlines()[-1] == "--index-url https://foo:bar@test.pypi.org/simple" def test_export_find_links(project, monkeypatch): url = "https://storage.googleapis.com/jax-releases/jax_cuda_releases.html" project.pyproject.settings["source"] = [{"url": url, "name": "jax", "type": "find_links"}] result = requirements.export(project, [], ns()) assert result.strip().splitlines()[-1] == f"--find-links {url}" def test_export_replace_project_root(project): artifact = FIXTURES / "artifacts/first-2.0.2-py2.py3-none-any.whl" shutil.copy2(artifact, project.root) with cd(project.root): req = parse_requirement(f"./{artifact.name}") result = requirements.export(project, [req], ns(hashes=False)) assert "${PROJECT_ROOT}" not in result @pytest.mark.usefixtures("local_finder") def test_convert_setup_py_project(project, pdm): project._saved_python = None project.project_config["python.use_venv"] = True pdm(["add", "setuptools"], obj=project) golden_file = FIXTURES / "projects/test-setuptools/setup.py" assert setup_py.check_fingerprint(project, golden_file) result, settings = setup_py.convert(project, golden_file, ns()) assert result == { "name": "mymodule", "version": "0.1.0", "description": "A test module", "keywords": ["one", "two"], "readme": "README.md", "authors": [{"name": "frostming"}], "license": {"text": "MIT"}, "classifiers": ["Framework :: Django", "Programming Language :: Python :: 3"], "requires-python": ">=3.5", "dependencies": ['importlib-metadata; python_version<"3.10"', "requests"], "scripts": {"mycli": "mymodule:main"}, } assert settings == {"package-dir": "src"} def test_convert_poetry_project_with_circular_dependency(project): parent_file = FIXTURES / "projects/poetry-with-circular-dep/pyproject.toml" child_file = FIXTURES / "projects/poetry-with-circular-dep/packages/child/pyproject.toml" _, settings = poetry.convert(project, parent_file, ns()) assert settings["dev-dependencies"]["dev"] == ["child @ file:///${PROJECT_ROOT}/packages/child"] _, settings = poetry.convert(project, child_file, ns()) assert settings["dev-dependencies"]["dev"] == ["parent @ file:///${PROJECT_ROOT}/../.."] pdm-2.23.1/tests/test_installer.py000066400000000000000000000261761477560627500171660ustar00rootroot00000000000000from __future__ import annotations import logging import os import venv from pathlib import Path from typing import Callable import pytest from unearth import Link from pdm import utils from pdm.core import Core from pdm.environments.base import BaseEnvironment from pdm.environments.local import PythonLocalEnvironment from pdm.environments.python import PythonEnvironment from pdm.installers import InstallManager from pdm.models.cached_package import CachedPackage from pdm.models.candidates import Candidate from pdm.models.requirements import parse_requirement from pdm.project.core import Project from tests import FIXTURES pytestmark = pytest.mark.usefixtures("local_finder") @pytest.fixture() def supports_link(preferred: str | None, monkeypatch: pytest.MonkeyPatch) -> Callable[[str], bool]: original = utils.fs_supports_link_method def mocked_support(linker: str) -> bool: if preferred is None: return False if preferred == "hardlink" and linker == "symlink": return False return original(linker) monkeypatch.setattr(utils, "fs_supports_link_method", mocked_support) return mocked_support def _prepare_project_for_env(project: Project, env_cls: type[BaseEnvironment]): project._saved_python = None project._python = None if env_cls is PythonEnvironment: venv.create(project.root / ".venv", symlinks=True) project.project_config["python.use_venv"] = True @pytest.fixture(params=(PythonEnvironment, PythonLocalEnvironment), autouse=True) def environment(request: pytest.RequestFixture, project: Project) -> type[BaseEnvironment]: # Run all test against all environments as installation and cache behavior may differ env_cls: type[BaseEnvironment] = request.param _prepare_project_for_env(project, env_cls) return env_cls def test_install_wheel_with_inconsistent_dist_info(project): req = parse_requirement("pyfunctional") candidate = Candidate( req, link=Link("http://fixtures.test/artifacts/PyFunctional-1.4.3-py3-none-any.whl"), ) installer = InstallManager(project.environment) installer.install(candidate) assert "pyfunctional" in project.environment.get_working_set() def test_install_with_file_existing(project): req = parse_requirement("demo") candidate = Candidate( req, link=Link("http://fixtures.test/artifacts/demo-0.0.1-py2.py3-none-any.whl"), ) lib_path = project.environment.get_paths()["purelib"] os.makedirs(lib_path, exist_ok=True) with open(os.path.join(lib_path, "demo.py"), "w") as fp: fp.write("print('hello')\n") installer = InstallManager(project.environment) installer.install(candidate) def test_uninstall_commit_rollback(project): req = parse_requirement("demo") candidate = Candidate( req, link=Link("http://fixtures.test/artifacts/demo-0.0.1-py2.py3-none-any.whl"), ) installer = InstallManager(project.environment) lib_path = project.environment.get_paths()["purelib"] installer.install(candidate) lib_file = os.path.join(lib_path, "demo.py") assert os.path.exists(lib_file) remove_paths = installer.get_paths_to_remove(project.environment.get_working_set()["demo"]) remove_paths.remove() assert not os.path.exists(lib_file) remove_paths.rollback() assert os.path.exists(lib_file) def test_rollback_after_commit(project, caplog): caplog.set_level(logging.ERROR, logger="pdm.termui") req = parse_requirement("demo") candidate = Candidate( req, link=Link("http://fixtures.test/artifacts/demo-0.0.1-py2.py3-none-any.whl"), ) installer = InstallManager(project.environment) lib_path = project.environment.get_paths()["purelib"] installer.install(candidate) lib_file = os.path.join(lib_path, "demo.py") assert os.path.exists(lib_file) remove_paths = installer.get_paths_to_remove(project.environment.get_working_set()["demo"]) remove_paths.remove() remove_paths.commit() assert not os.path.exists(lib_file) caplog.clear() remove_paths.rollback() assert not os.path.exists(lib_file) assert any(record.message == "Can't rollback, not uninstalled yet" for record in caplog.records) @pytest.mark.parametrize("use_install_cache", [False, True]) def test_uninstall_with_console_scripts(project, use_install_cache): req = parse_requirement("celery") candidate = Candidate( req, link=Link("http://fixtures.test/artifacts/celery-4.4.2-py2.py3-none-any.whl"), ) installer = InstallManager(project.environment, use_install_cache=use_install_cache) installer.install(candidate) celery_script = os.path.join( project.environment.get_paths()["scripts"], "celery.exe" if os.name == "nt" else "celery", ) assert os.path.exists(celery_script) installer.uninstall(project.environment.get_working_set()["celery"]) assert not os.path.exists(celery_script) @pytest.mark.parametrize("preferred", ["symlink", "hardlink", None]) def test_install_wheel_with_cache(project, pdm, supports_link): req = parse_requirement("future-fstrings") candidate = Candidate( req, link=Link("http://fixtures.test/artifacts/future_fstrings-1.2.0-py2.py3-none-any.whl"), ) installer = InstallManager(project.environment, use_install_cache=True) installer.install(candidate) lib_path = project.environment.get_paths()["purelib"] if supports_link("symlink"): assert os.path.islink(os.path.join(lib_path, "future_fstrings.py")) assert os.path.islink(os.path.join(lib_path, "aaaaa_future_fstrings.pth")) else: assert os.path.isfile(os.path.join(lib_path, "future_fstrings.py")) assert os.path.isfile(os.path.join(lib_path, "aaaaa_future_fstrings.pth")) for file in CachedPackage.cache_files: assert not os.path.exists(os.path.join(lib_path, file)) cache_name = "future_fstrings-1.2.0-py2.py3-none-any.whl.cache" assert any(p.path.name == cache_name for p in project.package_cache.iter_packages()) pdm(["run", "python", "-m", "site"], object=project) r = pdm(["run", "python", "-c", "import future_fstrings"], obj=project) assert r.exit_code == 0 pdm(["cache", "clear", "packages"], obj=project, strict=True) assert supports_link("symlink") is any(p.path.name == cache_name for p in project.package_cache.iter_packages()) dist = project.environment.get_working_set()["future-fstrings"] installer.uninstall(dist) assert not os.path.exists(os.path.join(lib_path, "future_fstrings.py")) assert not os.path.exists(os.path.join(lib_path, "aaaaa_future_fstrings.pth")) assert not dist.read_text("direct_url.json") pdm(["cache", "clear", "packages"], obj=project, strict=True) assert not any(p.path.name == cache_name for p in project.package_cache.iter_packages()) @pytest.mark.parametrize("preferred", ["symlink", "hardlink", None]) def test_can_install_wheel_with_cache_in_multiple_projects( project: Project, core: Core, supports_link, tmp_path_factory, environment ): projects = [] for idx in range(3): path: Path = tmp_path_factory.mktemp(f"project-{idx}") p = core.create_project(path, global_config=project.global_config.config_file) _prepare_project_for_env(p, environment) projects.append(p) req = parse_requirement("future-fstrings") candidate = Candidate( req, link=Link("http://fixtures.test/artifacts/future_fstrings-1.2.0-py2.py3-none-any.whl"), ) for p in projects: installer = InstallManager(p.environment, use_install_cache=True) installer.install(candidate) lib_path = p.environment.get_paths()["purelib"] if supports_link("symlink"): assert os.path.islink(os.path.join(lib_path, "future_fstrings.py")) assert os.path.islink(os.path.join(lib_path, "aaaaa_future_fstrings.pth")) else: assert os.path.isfile(os.path.join(lib_path, "future_fstrings.py")) assert os.path.isfile(os.path.join(lib_path, "aaaaa_future_fstrings.pth")) for file in CachedPackage.cache_files: assert not os.path.exists(os.path.join(lib_path, file)) def test_url_requirement_is_not_cached(project): req = parse_requirement( "future-fstrings @ http://fixtures.test/artifacts/future_fstrings-1.2.0-py2.py3-none-any.whl" ) candidate = Candidate(req) installer = InstallManager(project.environment, use_install_cache=True) installer.install(candidate) cache_path = project.cache("packages") / "future_fstrings-1.2.0-py2.py3-none-any" assert not cache_path.is_dir() lib_path = project.environment.get_paths()["purelib"] assert os.path.isfile(os.path.join(lib_path, "future_fstrings.py")) assert os.path.isfile(os.path.join(lib_path, "aaaaa_future_fstrings.pth")) dist = project.environment.get_working_set()["future-fstrings"] assert dist.read_text("direct_url.json") def test_editable_is_not_cached(project, tmp_path_factory): editable_path: Path = tmp_path_factory.mktemp("editable-project") editable_setup = editable_path / "setup.py" editable_setup.write_text(""" from setuptools import setup setup(name='editable-project', version='0.1.0', description='', py_modules=['module'], ) """) editable_module = editable_path / "module.py" editable_module.write_text("") req = parse_requirement(f"file://{editable_path}#egg=editable-project", True) candidate = Candidate(req) installer = InstallManager(project.environment, use_install_cache=True) installer.install(candidate) cache_path = project.cache("packages") / "editable_project-0.1.0-0.editable-py3-none-any.whl.cache" assert not cache_path.is_dir() lib_path = Path(project.environment.get_paths()["purelib"]) for pth in lib_path.glob("*editable_project*.pth"): assert pth.is_file() assert not pth.is_symlink() @pytest.mark.parametrize("use_install_cache", [False, True]) def test_install_wheel_with_data_scripts(project, use_install_cache): req = parse_requirement("jmespath") candidate = Candidate( req, link=Link("http://fixtures.test/artifacts/jmespath-0.10.0-py2.py3-none-any.whl"), ) installer = InstallManager(project.environment, use_install_cache=use_install_cache) installer.install(candidate) bin_path = os.path.join(project.environment.get_paths()["scripts"], "jp.py") assert os.path.isfile(bin_path) if os.name != "nt": assert os.stat(bin_path).st_mode & 0o100 dist = project.environment.get_working_set()["jmespath"] installer.uninstall(dist) assert not os.path.exists(bin_path) def test_compress_file_list_for_rename(): from pdm.installers.uninstallers import compress_for_rename project_root = str(FIXTURES / "projects") paths = { "test-removal/subdir", "test-removal/subdir/__init__.py", "test-removal/__init__.py", "test-removal/bar.py", "test-removal/foo.py", "test-removal/non_exist.py", } abs_paths = {os.path.join(project_root, path) for path in paths} assert sorted(compress_for_rename(abs_paths)) == [os.path.join(project_root, "test-removal" + os.sep)] pdm-2.23.1/tests/test_integration.py000066400000000000000000000063461477560627500175110ustar00rootroot00000000000000import findpython import pytest from pdm.utils import cd DEFAULT_PYTHON_VERSIONS = ["3.9", "3.10", "3.11", "3.12", "3.13"] PYPROJECT = { "project": {"name": "test-project", "version": "0.1.0", "requires-python": ">=3.7"}, "build-system": {"requires": ["pdm-backend"], "build-backend": "pdm.backend"}, } def get_python_versions(): finder = findpython.Finder(resolve_symlinks=True) available_versions = [] for version in DEFAULT_PYTHON_VERSIONS: v = finder.find(version) if v and v.is_valid(): available_versions.append(version) return available_versions PYTHON_VERSIONS = get_python_versions() @pytest.mark.integration @pytest.mark.network @pytest.mark.flaky(reruns=3) @pytest.mark.parametrize("python_version", PYTHON_VERSIONS) def test_basic_integration(python_version, core, tmp_path, pdm): """An e2e test case to ensure PDM works on all supported Python versions""" project = core.create_project(tmp_path) project.project_config["python.use_venv"] = True project.pyproject.set_data(PYPROJECT) project.root.joinpath("foo.py").write_text("import django\n") project._environment = None pdm(["use", "-f", python_version], obj=project, strict=True, cleanup=False) pdm(["add", "django", "-v"], obj=project, strict=True, cleanup=False) with cd(project.root): pdm(["run", "python", "foo.py"], obj=project, strict=True, cleanup=False) pdm(["build", "-v"], obj=project, strict=True, cleanup=False) pdm(["remove", "-v", "django"], obj=project, strict=True, cleanup=False) result = pdm(["list"], obj=project, strict=True) assert not any(line.strip().lower().startswith("django") for line in result.output.splitlines()) @pytest.mark.integration @pytest.mark.skipif(len(PYTHON_VERSIONS) < 2, reason="Need at least 2 Python versions to test") def test_use_python_write_file(pdm, project): pdm(["use", PYTHON_VERSIONS[0]], obj=project, strict=True) assert f"{project.python.major}.{project.python.minor}" == PYTHON_VERSIONS[0] assert project.root.joinpath(".python-version").read_text().strip() == PYTHON_VERSIONS[0] pdm(["use", PYTHON_VERSIONS[1]], obj=project, strict=True) assert f"{project.python.major}.{project.python.minor}" == PYTHON_VERSIONS[1] assert project.root.joinpath(".python-version").read_text().strip() == PYTHON_VERSIONS[1] @pytest.mark.integration @pytest.mark.parametrize("python_version", PYTHON_VERSIONS) @pytest.mark.parametrize("via_env", [True, False]) def test_init_project_respect_version_file(pdm, project, python_version, via_env, monkeypatch): project.project_config["python.use_venv"] = True if via_env: monkeypatch.setenv("PDM_PYTHON_VERSION", python_version) else: project.root.joinpath(".python-version").write_text(python_version) project._saved_python = None project._environment = None pdm(["install"], obj=project, strict=True) assert f"{project.python.major}.{project.python.minor}" == python_version def test_actual_list_freeze(project, local_finder, pdm): pdm(["config", "-l", "install.parallel", "false"], obj=project, strict=True) pdm(["add", "first"], obj=project, strict=True) r = pdm(["list", "--freeze"], obj=project) assert "first==2.0.2" in r.output pdm-2.23.1/tests/test_plugin.py000066400000000000000000000105041477560627500164530ustar00rootroot00000000000000import sys from unittest import mock import pytest from pdm.cli.commands.base import BaseCommand from pdm.compat import importlib_metadata from pdm.project.config import ConfigItem from pdm.utils import cd class HelloCommand(BaseCommand): def add_arguments(self, parser) -> None: parser.add_argument("-n", "--name", help="The person's name") def handle(self, project, options) -> None: greeting = "Hello world" if options.name: greeting = f"Hello, {options.name}" print(greeting) def new_command(core): core.register_command(HelloCommand, "hello") def replace_command(core): core.register_command(HelloCommand, "info") def add_new_config(core): core.add_config("foo", ConfigItem("Test config", "bar")) def make_entry_point(plugin): ret = mock.Mock() ret.load.return_value = plugin return ret def test_plugin_new_command(pdm, mocker, project, core): mocker.patch.object( importlib_metadata, "entry_points", return_value=[make_entry_point(new_command)], ) core.init_parser() core.load_plugins() result = pdm(["--help"], obj=project) assert "hello" in result.output result = pdm(["hello"], obj=project) assert result.output.strip() == "Hello world" result = pdm(["hello", "-n", "Frost"], obj=project) assert result.output.strip() == "Hello, Frost" def test_plugin_replace_command(pdm, mocker, project, core): mocker.patch.object( importlib_metadata, "entry_points", return_value=[make_entry_point(replace_command)], ) core.init_parser() core.load_plugins() result = pdm(["info"], obj=project) assert result.output.strip() == "Hello world" result = pdm(["info", "-n", "Frost"], obj=project) assert result.output.strip() == "Hello, Frost" def test_load_multiple_plugins(pdm, mocker, core): mocker.patch.object( importlib_metadata, "entry_points", return_value=[make_entry_point(new_command), make_entry_point(add_new_config)], ) core.init_parser() core.load_plugins() result = pdm(["hello"]) assert result.output.strip() == "Hello world", result.outputs result = pdm(["config", "foo"]) assert result.output.strip() == "bar" def test_old_entry_point_compatibility(pdm, mocker, core): def get_entry_points(group): if group == "pdm": return [make_entry_point(new_command)] if group == "pdm.plugin": return [make_entry_point(add_new_config)] return [] mocker.patch.object(importlib_metadata, "entry_points", side_effect=get_entry_points) core.init_parser() core.load_plugins() result = pdm(["hello"]) assert result.output.strip() == "Hello world" result = pdm(["config", "foo"]) assert result.output.strip() == "bar" @pytest.mark.usefixtures("local_finder") def test_project_plugin_library(pdm, project, core, monkeypatch): monkeypatch.setattr(sys, "path", sys.path[:]) project.pyproject.settings["plugins"] = ["pdm-hello"] pdm(["install", "--plugins"], obj=project, strict=True) assert project.root.joinpath(".pdm-plugins").exists() assert "pdm-hello" not in project.environment.get_working_set() with cd(project.root): core.load_plugins() result = pdm(["hello", "Frost"], strict=True) assert result.stdout.strip() == "Hello, Frost!" @pytest.mark.parametrize( "req_str", [ "-e file:///${PROJECT_ROOT}/plugins/test-plugin-pdm", "-e test_plugin_pdm @ file:///${PROJECT_ROOT}/plugins/test-plugin-pdm", ], ) @pytest.mark.usefixtures("local_finder") def test_install_local_plugin_without_name(pdm, project, core, req_str): import shutil from . import FIXTURES test_plugin_path = FIXTURES / "projects" / "test-plugin-pdm" project.root.joinpath("plugins").mkdir(exist_ok=True) shutil.copytree(test_plugin_path, project.root / "plugins" / "test-plugin-pdm", dirs_exist_ok=True) project.pyproject.settings["plugins"] = [req_str] project.pyproject.write() with cd(project.root): result = pdm(["install", "--plugins", "-vv"], obj=project, strict=True) assert project.root.joinpath(".pdm-plugins").exists() core.load_plugins() result = pdm(["hello", "--name", "Frost"], strict=True) assert result.stdout.strip() == "Hello, Frost" pdm-2.23.1/tests/test_project.py000066400000000000000000000503141477560627500166260ustar00rootroot00000000000000from __future__ import annotations import os import sys import venv from pathlib import Path from typing import TYPE_CHECKING import pytest from pbs_installer import PythonVersion from pytest_httpserver import HTTPServer from pdm.environments import PythonEnvironment from pdm.exceptions import PdmException, ProjectError from pdm.models.requirements import parse_requirement from pdm.models.specifiers import PySpecSet from pdm.models.venv import get_venv_python from pdm.utils import cd, is_path_relative_to, parse_version if TYPE_CHECKING: from pdm.project.core import Project from pdm.pytest import PDMCallable PYTHON_VERSIONS = ["3.9.7", "3.10.12", "3.10.11", "3.9.0", "3.10.13", "3.9.12"] def get_python_versions() -> list[PythonVersion]: python_versions = [] for v in PYTHON_VERSIONS: major, minor, micro = v.split(".") python_versions.append(PythonVersion("cpython", int(major), int(minor), int(micro))) return python_versions def test_project_python_with_pyenv_support(project, mocker, monkeypatch): project._saved_python = None project._python = None monkeypatch.setenv("PDM_IGNORE_SAVED_PYTHON", "1") mocker.patch("pdm.project.core.PYENV_ROOT", str(project.root)) pyenv_python = project.root / "shims/python" if os.name == "nt": pyenv_python = pyenv_python.with_suffix(".bat") pyenv_python.parent.mkdir() pyenv_python.touch() mocker.patch( "findpython.python.PythonVersion._get_version", return_value=parse_version("3.9.0"), ) mocker.patch("findpython.python.PythonVersion._get_interpreter", return_value=sys.executable) assert Path(project.python.path) == pyenv_python assert project.python.executable == Path(sys.executable) # Clean cache project._python = None project.project_config["python.use_pyenv"] = False assert Path(project.python.path) != pyenv_python def test_project_config_items(project): config = project.config for item in ("python.use_pyenv", "pypi.url", "cache_dir"): assert item in config def test_project_config_set_invalid_key(project): config = project.project_config with pytest.raises(KeyError): config["foo"] = "bar" def test_project_sources_overriding_pypi(project): project.project_config["pypi.url"] = "https://test.pypi.org/simple" assert project.sources[0].url == "https://test.pypi.org/simple" project.pyproject.settings["source"] = [{"url": "https://example.org/simple", "name": "pypi", "verify_ssl": True}] assert project.sources[0].url == "https://example.org/simple" def test_project_sources_env_var_expansion(project, monkeypatch): monkeypatch.setenv("PYPI_USER", "user") monkeypatch.setenv("PYPI_PASS", "password") project.project_config["pypi.url"] = "https://${PYPI_USER}:${PYPI_PASS}@test.pypi.org/simple" # expanded in sources assert project.sources[0].url == "https://user:password@test.pypi.org/simple" # not expanded in project config assert project.project_config["pypi.url"] == "https://${PYPI_USER}:${PYPI_PASS}@test.pypi.org/simple" project.pyproject.settings["source"] = [ { "url": "https://${PYPI_USER}:${PYPI_PASS}@example.org/simple", "name": "pypi", "verify_ssl": True, } ] # expanded in sources assert project.sources[0].url == "https://user:password@example.org/simple" # not expanded in tool settings assert project.pyproject.settings["source"][0]["url"] == "https://${PYPI_USER}:${PYPI_PASS}@example.org/simple" project.pyproject.settings["source"] = [ { "url": "https://${PYPI_USER}:${PYPI_PASS}@example2.org/simple", "name": "example2", "verify_ssl": True, } ] # expanded in sources assert project.sources[1].url == "https://user:password@example2.org/simple" # not expanded in tool settings assert project.pyproject.settings["source"][0]["url"] == "https://${PYPI_USER}:${PYPI_PASS}@example2.org/simple" def test_global_project(tmp_path, core): project = core.create_project(tmp_path, True) assert project.is_global assert isinstance(project.environment, PythonEnvironment) def test_auto_global_project(tmp_path, core): tmp_path.joinpath(".pdm-home").mkdir() (tmp_path / ".pdm-home/config.toml").write_text("[global_project]\nfallback = true\n") with cd(tmp_path): project = core.create_project(global_config=tmp_path / ".pdm-home/config.toml") assert project.is_global def test_project_use_venv(project): project._saved_python = None project._python = None scripts = "Scripts" if os.name == "nt" else "bin" suffix = ".exe" if os.name == "nt" else "" venv.create(project.root / "venv", symlinks=True) project.project_config["python.use_venv"] = True env = project.environment assert env.interpreter.executable == project.root / "venv" / scripts / f"python{suffix}" assert not env.is_local def test_project_packages_path(project): packages_path = project.environment.packages_path version = ".".join(map(str, sys.version_info[:2])) if os.name == "nt" and sys.maxsize <= 2**32: assert packages_path.name == version + "-32" else: assert packages_path.name == version def test_project_auto_detect_venv(project): venv.create(project.root / "test_venv") scripts = "Scripts" if os.name == "nt" else "bin" suffix = ".exe" if os.name == "nt" else "" project.project_config["python.use_venv"] = True project._python = None project._saved_python = (project.root / "test_venv" / scripts / f"python{suffix}").as_posix() assert not project.environment.is_local @pytest.mark.path def test_ignore_saved_python(project, monkeypatch): project.project_config["python.use_venv"] = True project._python = None scripts = "Scripts" if os.name == "nt" else "bin" suffix = ".exe" if os.name == "nt" else "" venv.create(project.root / "venv", symlinks=True) monkeypatch.setenv("PDM_IGNORE_SAVED_PYTHON", "1") assert project.python.executable != project._saved_python assert project.python.executable == project.root / "venv" / scripts / f"python{suffix}" def test_select_dependencies(project): project.pyproject.metadata["dependencies"] = ["requests"] project.pyproject.metadata["optional-dependencies"] = { "security": ["cryptography"], "venv": ["virtualenv"], } project.pyproject.dependency_groups.update( { "test": ["pytest"], "doc": ["mkdocs"], "all": [{"include-group": "test"}, {"include-group": "doc"}], } ) assert sorted([r.key for r in project.get_dependencies()]) == ["requests"] assert sorted([r.key for r in project.get_dependencies("security")]) == ["cryptography"] assert sorted([r.key for r in project.get_dependencies("test")]) == ["pytest"] assert sorted([r.key for r in project.get_dependencies("all")]) == ["mkdocs", "pytest"] assert sorted(project.iter_groups()) == [ "all", "default", "doc", "security", "test", "venv", ] def test_invalid_dependency_group(project): project.pyproject.dependency_groups.update( { "invalid": [{"invalid-key": True}], "missing": [{"include-group": "missing-group"}], "doc": ["mkdocs"], "recursive": [{"include-group": "invalid"}], } ) assert sorted([r.key for r in project.get_dependencies("doc")]) == ["mkdocs"] with pytest.raises(ProjectError, match="Invalid dependency group item"): project.get_dependencies("invalid") with pytest.raises(ProjectError, match="Invalid dependency group item"): project.get_dependencies("recursive") with pytest.raises(ProjectError, match="Missing group 'missing-group'"): project.get_dependencies("missing") @pytest.mark.path def test_set_non_exist_python_path(project_no_init): project_no_init._saved_python = "non-exist-python" project_no_init._python = None assert project_no_init.python.executable.name != "non-exist-python" @pytest.mark.usefixtures("venv_backends") def test_create_venv_first_time(pdm, project, local_finder): project.project_config.update({"venv.in_project": False}) project._saved_python = None result = pdm(["install"], obj=project) assert result.exit_code == 0 venv_parent = project.root / "venvs" venv_path = next(venv_parent.iterdir(), None) assert venv_path is not None assert Path(project._saved_python).relative_to(venv_path) @pytest.mark.usefixtures("venv_backends", "local_finder") @pytest.mark.parametrize("with_pip", [True, False]) def test_create_venv_in_project(pdm, project, with_pip): project.project_config.update({"venv.in_project": True, "venv.with_pip": with_pip}) project._saved_python = None result = pdm(["install"], obj=project) assert result.exit_code == 0 assert project.root.joinpath(".venv").exists() working_set = project.environment.get_working_set() assert ("pip" in working_set) is with_pip @pytest.mark.usefixtures("venv_backends") def test_find_interpreters_from_venv(pdm, project, local_finder): project.project_config.update({"venv.in_project": False}) project._saved_python = None result = pdm(["install"], obj=project) assert result.exit_code == 0 venv_parent = project.root / "venvs" venv_path = next(venv_parent.iterdir(), None) venv_python = get_venv_python(venv_path) assert any(venv_python == p.executable for p in project.find_interpreters()) @pytest.mark.usefixtures("local_finder") def test_find_interpreters_without_duplicate_relative_paths(pdm, project): project._saved_python = None venv.create(project.root / ".venv", clear=True) with cd(project.root): bin_dir = "Scripts" if os.name == "nt" else "bin" suffix = ".exe" if os.name == "nt" else "" found = list(project.find_interpreters(f".venv/{bin_dir}/python{suffix}")) assert len(found) == 1 def test_iter_project_venvs(project): from pdm.cli.commands.venv.utils import get_venv_prefix, iter_venvs from pdm.models.venv import get_venv_python venv_parent = Path(project.config["venv.location"]) venv_prefix = get_venv_prefix(project) for name in ("foo", "bar", "baz"): venv_python = get_venv_python(venv_parent / (venv_prefix + name)) venv_python.parent.mkdir(parents=True) venv_python.touch() dot_venv_python = get_venv_python(project.root / ".venv") dot_venv_python.parent.mkdir(parents=True) dot_venv_python.touch() venv_keys = [key for key, _ in iter_venvs(project)] assert sorted(venv_keys) == ["bar", "baz", "foo", "in-project"] def test_load_extra_sources(project): project.pyproject.settings["source"] = [ { "name": "custom", "url": "https://custom.pypi.org/simple", } ] project.global_config["pypi.extra.url"] = "https://extra.pypi.org/simple" sources = project.sources assert len(sources) == 3 assert [item.name for item in sources] == ["pypi", "custom", "extra"] project.global_config["pypi.ignore_stored_index"] = True sources = project.sources assert len(sources) == 1 assert [item.name for item in sources] == ["custom"] def test_no_index_raise_error(project): project.global_config["pypi.ignore_stored_index"] = True with pytest.raises(PdmException, match="You must specify at least one index"): with project.environment.get_finder(): pass def test_access_index_with_auth(project, httpserver: HTTPServer): httpserver.expect_request( "/simple/my-package", method="GET", headers={"Authorization": "Basic Zm9vOmJhcg=="} ).respond_with_data("OK") project.global_config.update( { "pypi.extra.url": httpserver.url_for("/simple"), "pypi.extra.username": "foo", "pypi.extra.password": "bar", } ) session = project.environment.session resp = session.get(httpserver.url_for("/simple/my-package")) assert resp.is_success def test_configured_source_overwriting(project): project.pyproject.settings["source"] = [ { "name": "custom", "url": "https://custom.pypi.org/simple", } ] project.global_config["pypi.custom.url"] = "https://extra.pypi.org/simple" project.global_config["pypi.custom.verify_ssl"] = False project.project_config["pypi.custom.username"] = "foo" project.project_config["pypi.custom.password"] = "bar" sources = project.sources assert [source.name for source in sources] == ["pypi", "custom"] custom_source = sources[1] assert custom_source.url == "https://custom.pypi.org/simple" assert custom_source.verify_ssl is False assert custom_source.username == "foo" assert custom_source.password == "bar" def test_invoke_pdm_adding_configured_args(project, pdm, mocker): project.pyproject.settings["options"] = { "install": ["--no-self", "--no-editable"], "add": ["--no-isolation"], "lock": ["--no-cross-platform"], } project.pyproject.write() parser = mocker.patch("argparse.ArgumentParser.parse_args") pdm(["add", "requests"], obj=project) parser.assert_called_with(["add", "--no-isolation", "requests"]) pdm(["install", "--check"], obj=project) parser.assert_called_with(["install", "--no-self", "--no-editable", "--check"]) pdm(["lock", "--lockfile", "pdm.2.lock"], obj=project) parser.assert_called_with(["lock", "--no-cross-platform", "--lockfile", "pdm.2.lock"]) pdm(["-c", "/dev/null", "lock"], obj=project) parser.assert_called_with(["-c", "/dev/null", "lock", "--no-cross-platform"]) pdm(["--verbose", "add", "requests"], obj=project) parser.assert_called_with(["--verbose", "add", "--no-isolation", "requests"]) @pytest.fixture() def prepare_repository(repository, project): repository.add_candidate("foo", "3.0", ">=3.8,<3.13") repository.add_candidate("foo", "2.0", ">=3.7,<3.12") repository.add_candidate("foo", "1.0", ">=3.7") project.environment.python_requires = PySpecSet(">=3.9") project.add_dependencies(["foo"]) @pytest.mark.usefixtures("prepare_repository") @pytest.mark.parametrize("is_quiet,extra_args", [(True, ("-q",)), (False, ())]) def test_quiet_mode(pdm, project, is_quiet, extra_args, recwarn): result = pdm(["lock", *extra_args], obj=project) assert result.exit_code == 0 assert len(recwarn) > 0 assert 'For example, "<3.13,>=3.9"' in str(recwarn[0].message) assert 'For example, "<3.12,>=3.9"' in str(recwarn[1].message) assert ("to suppress these warnings" in result.stderr) is not is_quiet assert project.get_locked_repository().candidates["foo"].version == "1.0" @pytest.mark.usefixtures("prepare_repository") @pytest.mark.parametrize("pattern,suppressed", [("foo", True), ("bar", False), ("*", True), ("f?o", True)]) def test_ignore_package_warning(pdm, project, recwarn, pattern, suppressed): project.pyproject.settings["ignore_package_warnings"] = [pattern] result = pdm(["lock"], obj=project) assert result.exit_code == 0 assert (len(recwarn) == 0) is suppressed def test_filter_sources_with_config(project): project.pyproject.settings["source"] = [ {"name": "source1", "url": "https://source1.org/simple", "include_packages": ["foo", "foo-*"]}, { "name": "source2", "url": "https://source2.org/simple", "include_packages": ["foo-bar", "bar*"], "exclude_packages": ["baz-*"], }, {"name": "pypi", "url": "https://pypi.org/simple"}, ] repository = project.get_repository() def expect_sources(requirement: str, expected: list[str]) -> None: sources = repository.get_filtered_sources(parse_requirement(requirement)) assert sorted([source.name for source in sources]) == sorted(expected) expect_sources("foo", ["source1"]) expect_sources("foo-baz", ["source1"]) expect_sources("foo-bar", ["source1", "source2"]) expect_sources("bar-extra", ["source2"]) expect_sources("baz-extra", ["source1", "pypi"]) @pytest.mark.usefixtures("working_set") def test_preserve_log_file(project, pdm, tmp_path, mocker): pdm(["add", "requests"], obj=project, strict=True) all_logs = list(tmp_path.joinpath("logs").iterdir()) assert len(all_logs) == 0 mocker.patch("pdm.installers.Synchronizer.synchronize", side_effect=Exception) result = pdm(["add", "pytz"], obj=project) assert result.exit_code != 0 install_log = next(tmp_path.joinpath("logs").glob("pdm-install-*.log")) assert install_log.exists() @pytest.mark.parametrize("use_venv", [True, False]) def test_find_interpreters_with_PDM_IGNORE_ACTIVE_VENV( pdm: PDMCallable, project: Project, monkeypatch: pytest.MonkeyPatch, use_venv: bool, ): project._saved_python = None project._python = None project.project_config["python.use_venv"] = use_venv venv.create(venv_path := project.root / "venv", symlinks=True) monkeypatch.setenv("VIRTUAL_ENV", str(venv_path)) monkeypatch.setenv("PDM_IGNORE_ACTIVE_VENV", "1") venv_python = get_venv_python(venv_path) pythons = list(project.find_interpreters()) assert pythons, "PDM should find interpreters with PDM_IGNORE_ACTIVE_VENV" # Test requires that some interpreters are available outside the venv assert any(venv_python != p.executable for p in project.find_interpreters()) # No need to assert, exception raised if not found interpreter = project.resolve_interpreter() assert interpreter.executable != venv_python if use_venv: project.project_config["venv.in_project"] = True pdm("install", strict=True, obj=project) assert project._saved_python python = Path(project._saved_python) assert is_path_relative_to(python, project.root) assert not is_path_relative_to(python, venv_path) @pytest.mark.parametrize( "var,key,settings,expected", [ ("PDM_VAR", "var", {}, "from-env"), ("pdm_var", "var", {}, "from-env"), ("PDM_NOPE", "var", {"var": "from-settings"}, "from-settings"), ("PDM_VAR", "var", {"var": "from-settings"}, "from-env"), ("PDM_NOPE", "nested.var", {"nested": {"var": "from-settings"}}, "from-settings"), ("PDM_NOPE", "noop", {}, None), ], ) def test_env_or_setting( project: Project, monkeypatch: pytest.MonkeyPatch, var: str, key: str, settings: dict, expected: str | None, ): monkeypatch.setenv("PDM_VAR", "from-env") project.pyproject.settings.update(settings) assert project.env_or_setting(var, key) == expected @pytest.mark.parametrize( "var,setting,expected", [ (None, None, []), ("", None, []), (" ", None, []), (None, "", []), (None, " ", []), (None, [], []), ("var", None, ["var"]), ("val1,val2", None, ["val1", "val2"]), ("val1, val2", None, ["val1", "val2"]), ("val1, , , val2", None, ["val1", "val2"]), (None, "val1,val2", ["val1", "val2"]), (None, ["val1", "val2"], ["val1", "val2"]), (None, [" val1", "val2 "], ["val1", "val2"]), (None, [" val1", "", "val2 ", " "], ["val1", "val2"]), (None, ["val1,val2", "val3,val4"], ["val1,val2", "val3,val4"]), ("val1,val2", ["val3", "val4"], ["val1", "val2"]), ], ) def test_env_setting_list( project: Project, monkeypatch: pytest.MonkeyPatch, var: str | None, setting: str | list[str] | None, expected: list[str], ): if var is not None: monkeypatch.setenv("PDM_VAR", var) if setting is not None: project.pyproject.settings["var"] = setting assert project.environment._setting_list("PDM_VAR", "var") == expected def test_project_best_match_max(project, mocker): expected = PythonVersion("cpython", 3, 10, 13) mocker.patch( "pdm.project.core.get_all_installable_python_versions", return_value=get_python_versions(), ) assert project.get_best_matching_cpython_version() == expected def test_project_best_match_min(project, mocker): expected = PythonVersion("cpython", 3, 9, 0) mocker.patch( "pdm.project.core.get_all_installable_python_versions", return_value=get_python_versions(), ) assert project.get_best_matching_cpython_version(use_minimum=True) == expected pdm-2.23.1/tests/test_signals.py000066400000000000000000000123011477560627500166120ustar00rootroot00000000000000from itertools import chain from unittest import mock import pytest from pdm import signals from pdm.models.candidates import Candidate from pdm.models.repositories import Package from pdm.models.requirements import Requirement def test_post_init_signal(project_no_init, pdm): mock_handler = mock.Mock() with signals.post_init.connected_to(mock_handler): result = pdm(["init"], input="\n\n\n\n\n\n\n\n", obj=project_no_init) assert result.exit_code == 0 mock_handler.assert_called_once_with(project_no_init, hooks=mock.ANY) @pytest.mark.usefixtures("working_set") def test_post_lock_and_install_signals(project, pdm): pre_lock = signals.pre_lock.connect(mock.Mock(), weak=False) post_lock = signals.post_lock.connect(mock.Mock(), weak=False) pre_install = signals.pre_install.connect(mock.Mock(), weak=False) post_install = signals.post_install.connect(mock.Mock(), weak=False) pdm(["add", "requests"], obj=project, strict=True) signals.pre_lock.disconnect(pre_lock) signals.post_lock.disconnect(post_lock) signals.pre_install.disconnect(pre_install) signals.post_install.disconnect(post_install) for mocker in (pre_lock, post_lock, pre_install, post_install): mocker.assert_called_once() @pytest.mark.usefixtures("working_set") def test_lock_and_install_signals_injection_with_add(project, pdm): pre_lock = signals.pre_lock.connect(mock.Mock(), weak=False) post_lock = signals.post_lock.connect(mock.Mock(), weak=False) pre_install = signals.pre_install.connect(mock.Mock(), weak=False) post_install = signals.post_install.connect(mock.Mock(), weak=False) pdm(["add", "requests"], obj=project, strict=True) signals.pre_lock.disconnect(pre_lock) signals.post_lock.disconnect(post_lock) signals.pre_install.disconnect(pre_install) signals.post_install.disconnect(post_install) assert isinstance(pre_lock.call_args.kwargs["requirements"], list) assert all(isinstance(e, Requirement) for e in pre_lock.call_args.kwargs["requirements"]) assert len(pre_lock.call_args.kwargs["requirements"]) == 1 assert isinstance(post_lock.call_args.kwargs["resolution"], dict) assert all(isinstance(e, Candidate) for e in chain.from_iterable(post_lock.call_args.kwargs["resolution"].values())) assert len(post_lock.call_args.kwargs["resolution"]) == 5 assert isinstance(pre_install.call_args.kwargs["packages"], list) assert all(isinstance(e, Package) for e in pre_install.call_args.kwargs["packages"]) assert len(pre_install.call_args.kwargs["packages"]) == 5 assert isinstance(post_install.call_args.kwargs["packages"], list) assert all(isinstance(e, Package) for e in post_install.call_args.kwargs["packages"]) assert len(post_install.call_args.kwargs["packages"]) == 5 @pytest.mark.usefixtures("working_set") def test_lock_and_install_signals_injection_with_install(project, pdm): project.add_dependencies(["requests"]) pre_lock = signals.pre_lock.connect(mock.Mock(), weak=False) post_lock = signals.post_lock.connect(mock.Mock(), weak=False) pre_install = signals.pre_install.connect(mock.Mock(), weak=False) post_install = signals.post_install.connect(mock.Mock(), weak=False) pdm(["install"], obj=project, strict=True) signals.pre_lock.disconnect(pre_lock) signals.post_lock.disconnect(post_lock) signals.pre_install.disconnect(pre_install) signals.post_install.disconnect(post_install) assert isinstance(pre_lock.call_args.kwargs["requirements"], list) assert all(isinstance(e, Requirement) for e in pre_lock.call_args.kwargs["requirements"]) assert len(pre_lock.call_args.kwargs["requirements"]) == 1 assert isinstance(post_lock.call_args.kwargs["resolution"], dict) assert all(isinstance(e, Candidate) for e in chain.from_iterable(post_lock.call_args.kwargs["resolution"].values())) assert len(post_lock.call_args.kwargs["resolution"]) == 5 assert isinstance(pre_install.call_args.kwargs["packages"], list) assert all(isinstance(e, Package) for e in pre_install.call_args.kwargs["packages"]) assert len(pre_install.call_args.kwargs["packages"]) == 5 assert isinstance(post_install.call_args.kwargs["packages"], list) assert all(isinstance(e, Package) for e in post_install.call_args.kwargs["packages"]) assert len(post_install.call_args.kwargs["packages"]) == 5 @pytest.mark.usefixtures("working_set") def test_lock_signals_injection_with_update(project, pdm): project.add_dependencies(["requests"]) pre_lock = signals.pre_lock.connect(mock.Mock(), weak=False) post_lock = signals.post_lock.connect(mock.Mock(), weak=False) pdm(["update"], obj=project, strict=True) signals.pre_lock.disconnect(pre_lock) signals.post_lock.disconnect(post_lock) assert isinstance(pre_lock.call_args.kwargs["requirements"], list) assert all(isinstance(e, Requirement) for e in pre_lock.call_args.kwargs["requirements"]) assert len(pre_lock.call_args.kwargs["requirements"]) == 1 assert isinstance(post_lock.call_args.kwargs["resolution"], dict) assert all(isinstance(e, Candidate) for e in chain.from_iterable(post_lock.call_args.kwargs["resolution"].values())) assert len(post_lock.call_args.kwargs["resolution"]) == 5 pdm-2.23.1/tests/test_utils.py000066400000000000000000000433431477560627500163240ustar00rootroot00000000000000import pathlib import sys import unittest.mock as mock from pathlib import Path import pytest import tomlkit from pdm import utils from pdm._types import RepositoryConfig from pdm.cli import utils as cli_utils from pdm.cli.filters import GroupSelection from pdm.exceptions import PdmUsageError, PDMWarning @pytest.mark.parametrize( "given, dirname", [ ((None, None, None), "test_dirname1"), (("test_suffix", None, None), "test_dirname2"), (("test_suffix", "test_prefix", None), "test_dirname3"), (("test_suffix", "test_prefix", "test_dir"), "test_dirname4"), ((None, "test_prefix", None), "test_dirname5"), ((None, "test_prefix", "test_dir"), "test_dirname6"), ((None, None, "test_dir"), "test_dirname7"), ((None, "test_prefix", "test_dir"), "test_dirname8"), (("test_prefix", None, "test_dir"), "test_dirname9"), ], ) @mock.patch("pdm.utils.atexit.register") @mock.patch("pdm.utils.os.makedirs") @mock.patch("pdm.utils.tempfile.mkdtemp") def test_create_tracked_tempdir(mock_tempfile_mkdtemp, mock_os_makedirs, mock_atexit_register, given, dirname): test_suffix, test_prefix, test_dir = given mock_tempfile_mkdtemp.return_value = dirname received_dirname = utils.create_tracked_tempdir(suffix=test_suffix, prefix=test_prefix, dir=dirname) mock_tempfile_mkdtemp.assert_called_once_with(suffix=test_suffix, prefix=test_prefix, dir=dirname) mock_os_makedirs.assert_called_once_with(dirname, mode=0o777, exist_ok=True) mock_atexit_register.assert_called() assert received_dirname == dirname def test_get_trusted_hosts(mocker): repository_configs = [ { "url": "https://pypi.org", "verify_ssl": False, }, {"url": "https://untrusted.pypi.index", "verify_ssl": True}, {"url": "https://user:password@trusted1.pypi.index", "verify_ssl": False}, {"url": "https://user:password@trusted2.pypi.index", "verify_ssl": False}, ] sources = [mocker.create_autospec(RepositoryConfig, instance=False, **config) for config in repository_configs] expected = [ "pypi.org", "trusted1.pypi.index", "trusted2.pypi.index", ] received = utils.get_trusted_hosts(sources) assert received == expected @pytest.mark.parametrize( "given, expected", [ ("scheme://netloc", "scheme://netloc"), ("scheme://netloc/path", "scheme://netloc/path"), ("scheme://netloc/path/#", "scheme://netloc/path/"), ("scheme://netloc/path#fragment", "scheme://netloc/path"), ("scheme://netloc/path;parameters?query#fragment", "scheme://netloc/path;parameters?query"), ], ) def test_url_without_fragments(given, expected): received = utils.url_without_fragments(given) assert received == expected @pytest.mark.parametrize( "given, expected", [ ((["abc", "def", "ghi"], "/"), ["abc", "/", "def", "/", "ghi"]), (([], "/"), []), ((["abc"], "/"), ["abc"]), ], ) def test_join_list_with(given, expected): items, sep = given received = utils.join_list_with(items, sep) assert received == expected class TestGetUserEmailFromGit: @mock.patch("pdm.utils.shutil.which", return_value=None) def test_no_git(self, no_git_patch): with no_git_patch: assert utils.get_user_email_from_git() == ("", "") @mock.patch( "pdm.utils.subprocess.check_output", side_effect=[ utils.subprocess.CalledProcessError(-1, ["git", "config", "user.name"], "No username"), utils.subprocess.CalledProcessError(-1, ["git", "config", "user.email"], "No email"), ], ) @mock.patch("pdm.utils.shutil.which", return_value="git") def test_no_git_username_and_email(self, git_patch, no_git_username_and_email_patch): with git_patch: with no_git_username_and_email_patch: assert utils.get_user_email_from_git() == ("", "") @mock.patch( "pdm.utils.subprocess.check_output", side_effect=[ "username", utils.subprocess.CalledProcessError(-1, ["git", "config", "user.email"], "No email"), ], ) @mock.patch("pdm.utils.shutil.which", return_value="git") def test_no_git_email(self, git_patch, no_git_email_patch): with git_patch: with no_git_email_patch: assert utils.get_user_email_from_git() == ("username", "") @mock.patch( "pdm.utils.subprocess.check_output", side_effect=[utils.subprocess.CalledProcessError(-1, ["git", "config", "user.name"], "No username"), "email"], ) @mock.patch("pdm.utils.shutil.which", return_value="git") def test_no_git_username(self, git_patch, no_git_username_patch): with git_patch: with no_git_username_patch: assert utils.get_user_email_from_git() == ("", "email") @mock.patch("pdm.utils.subprocess.check_output", side_effect=["username", "email"]) @mock.patch("pdm.utils.shutil.which", return_value="git") def test_git_username_and_email(self, git_patch, git_username_and_email_patch): with git_patch: with git_username_and_email_patch: assert utils.get_user_email_from_git() == ("username", "email") @pytest.mark.parametrize( "given,expected", [ ("git@github.com/pdm-project/pdm", "ssh://git@github.com/pdm-project/pdm"), ("ssh://git@github.com/pdm-project/pdm", "ssh://git@github.com/pdm-project/pdm"), ("git+ssh://git@github.com/pdm-project/pdm", "git+ssh://git@github.com/pdm-project/pdm"), ("https://git@github.com/pdm-project/pdm", "https://git@github.com/pdm-project/pdm"), ("file:///my/local/pdm-project/pdm", "file:///my/local/pdm-project/pdm"), ], ) def test_add_ssh_scheme_to_git_uri(given, expected): assert utils.add_ssh_scheme_to_git_uri(given) == expected class TestUrlToPath: def test_non_file_url(self): with pytest.raises(ValueError): utils.url_to_path("not_a_file_scheme://netloc/path") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Non-Windows test") def test_non_windows_non_local_file_url(self): with pytest.raises(ValueError): utils.url_to_path("file://non_local_netloc/file/url") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Non-Windows test") def test_non_windows_localhost_local_file_url(self): assert utils.url_to_path("file://localhost/local/file/path") == "/local/file/path" @pytest.mark.skipif(not sys.platform.startswith("win"), reason="Windows test") def test_windows_localhost_local_file_url(self): assert utils.url_to_path("file://localhost/local/file/path") == "\\local\\file\\path" @pytest.mark.parametrize( "given,expected", [ ("test", "test"), ("", ""), ("${FOO}", "hello"), ("$FOO", "$FOO"), ("${BAR}", "${BAR}"), ("%FOO%", "%FOO%"), ("${FOO}_${FOO}", "hello_hello"), ], ) def test_expand_env_vars(given, expected, monkeypatch): monkeypatch.setenv("FOO", "hello") assert utils.expand_env_vars(given) == expected @pytest.mark.parametrize( "given,expected", [ ("https://example.org/path?arg=1", "https://example.org/path?arg=1"), ( "https://${FOO}@example.org/path?arg=1", "https://token%3Aoidc%2F1@example.org/path?arg=1", ), ( "https://${FOO}:${BAR}@example.org/path?arg=1", "https://token%3Aoidc%2F1:p%40ssword@example.org/path?arg=1", ), ( "https://${FOOBAR}@example.org/path?arg=1", "https://%24%7BFOOBAR%7D@example.org/path?arg=1", ), ], ) def test_expand_env_vars_in_auth(given, expected, monkeypatch): monkeypatch.setenv("FOO", "token:oidc/1") monkeypatch.setenv("BAR", "p@ssword") assert utils.expand_env_vars_in_auth(given) == expected @pytest.mark.parametrize( "os_name,given,expected", [ ("posix", ("match", "repl", "/a/b/match/c/match/d/e"), "/a/b/repl/c/repl/d/e"), ("posix", ("old", "new", "/path/to/old/pdm"), "/path/to/new/pdm"), ("posix", ("match", "repl", "match/a/math/b/match/c"), "repl/a/math/b/repl/c"), ("posix", ("match", "repl", "/some/path"), "/some/path"), ("posix", ("match", "repl", ""), ""), ("nt", ("old", "new", "C:\\Path\\tO\\old\\pdm"), "C:/Path/tO/new/pdm"), ("nt", ("old", "new", "C:\\Path\\tO\\Old\\pdm"), "C:/Path/tO/new/pdm"), ("nt", ("old", "new", "C:\\no\\matching\\path"), "C:/no/matching/path"), ], ) def test_path_replace(os_name, given, expected): with mock.patch("pdm.utils.os_name", os_name): pattern, replace_with, dest = given assert utils.path_replace(pattern, replace_with, dest) == expected # Only testing POSIX-style paths here @pytest.mark.parametrize( "given,expected", [ (("/", "/"), True), (("/a", "/"), True), (("/a/b", "/a"), True), (("/a", "/b"), False), (("a", "b"), False), (("/a/b", "/c/d"), False), (("/a/b/c", "/a"), True), (("../a/b/c", "../a"), True), ], ) def test_is_path_relative_to(given, expected): path, other = given assert utils.is_path_relative_to(path, other) == expected class TestGetVenvLikePrefix: def test_conda_env_with_conda_meta_in_bin(self, tmp_path: Path): path = tmp_path / "conda/bin/python3" path_parent = path.parent path_parent.mkdir(parents=True) path.touch() path_parent.joinpath("conda-meta").mkdir() received = utils.get_venv_like_prefix(path) expected = path_parent, True assert received == expected def test_py_env_with_pyvenv_cfg(self, tmp_path: Path): path = tmp_path / "venv/bin/python3" bin_path = path.parent venv_path = path.parent.parent bin_path.mkdir(parents=True) path.touch() venv_path.joinpath("pyvenv.cfg").touch() received = utils.get_venv_like_prefix(str(path)) expected = venv_path, False assert received == expected def test_conda_env_with_conda_meta(self, tmp_path: Path): path = tmp_path / "conda/bin/python3" interpreter_bin_path = path.parent interpreter_bin_parent_path = interpreter_bin_path.parent interpreter_bin_path.mkdir(parents=True) path.touch() interpreter_bin_parent_path.joinpath("conda-meta").mkdir() received = utils.get_venv_like_prefix(str(path)) expected = interpreter_bin_parent_path, True assert received == expected def test_virtual_env(self, monkeypatch): path = Path("/my/venv") expected = path, False monkeypatch.setenv("VIRTUAL_ENV", str(path)) received = utils.get_venv_like_prefix(path.joinpath("bin", "python3")) assert received == expected def test_conda_virtual_env(self, monkeypatch): path = Path("/my/conda/venv") expected = path, True monkeypatch.setenv("CONDA_PREFIX", str(path)) received = utils.get_venv_like_prefix(path.joinpath("bin", "python3")) assert received == expected def test_no_virtual_env(self): path = Path("/not/a/venv/bin/python3") expected = None, False received = utils.get_venv_like_prefix(str(path)) assert received == expected def compare_python_paths(path1, path2): return path1.parent == path2.parent @pytest.mark.path def test_find_python_in_path(tmp_path): assert utils.find_python_in_path(sys.executable) == pathlib.Path(sys.executable).absolute() posix_path_to_executable = pathlib.Path(sys.executable) assert compare_python_paths( utils.find_python_in_path(sys.prefix), posix_path_to_executable, ) assert not utils.find_python_in_path(tmp_path) @pytest.mark.parametrize( "given,expected", [ ("scheme://netloc/path@rev#fragment", "rev"), ("scheme://netloc/path@rev", "rev"), ("scheme://netloc/path", ""), ("scheme://netloc/path#fragment", ""), ], ) def test_get_rev_from_url(given, expected): assert utils.get_rev_from_url(given) == expected @pytest.mark.parametrize( "given,expected", [ (("ProjectName", False), "ProjectName"), (("ProjectName", True), "projectname"), (("1Project_Name", False), "1Project-Name"), (("1Project_Name", True), "1project-name"), (("Project-Name", False), "Project-Name"), (("Project-Name", True), "project-name"), (("Project123Name", False), "Project123Name"), (("Project123name", True), "project123name"), (("123$!ProjectName", False), "123-ProjectName"), (("123$!ProjectName", True), "123-projectname"), (("123$!Project_Name", False), "123-Project-Name"), (("123$!Project_Name", True), "123-project-name"), (("$!123Project_Name4", False), "-123Project-Name4"), (("$!123Project_Name4", True), "-123project-name4"), ], ) def test_normalize_name(given, expected): assert utils.normalize_name(*given) == expected class TestIsEditable: @mock.patch("pdm.utils.is_egg_link", return_value=True) @mock.patch("pdm.compat.Distribution") def test_is_egg_link(self, distribution, is_egg_link_patch): with is_egg_link_patch: assert utils.is_editable(distribution) is True @mock.patch("pdm.utils.is_egg_link", return_value=False) @mock.patch("pdm.compat.Distribution") def test_not_direct_url_distribution(self, distribution, is_egg_link_patch): distribution.read_text.return_value = None with is_egg_link_patch: assert utils.is_editable(distribution) is False @mock.patch("pdm.utils.is_egg_link", return_value=False) @mock.patch("pdm.compat.Distribution") def test_direct_url_distribution(self, distribution, is_egg_link_patch): distribution.read_text.return_value = """{"dir_info": {"editable": true}}""" with is_egg_link_patch: assert utils.is_editable(distribution) is True def test_merge_dictionary(): target = tomlkit.item( { "existing_dict": {"foo": "bar", "hello": "world"}, "existing_list": ["hello"], } ) input_dict = { "existing_dict": {"foo": "baz"}, "existing_list": ["world"], "new_dict": {"name": "Sam"}, } cli_utils.merge_dictionary(target, input_dict) assert target == { "existing_dict": {"foo": "baz", "hello": "world"}, "existing_list": ["hello", "world"], "new_dict": {"name": "Sam"}, } def setup_dependencies(project): project.pyproject.metadata.update( { "dependencies": ["requests"], "optional-dependencies": {"web": ["flask"], "auth": ["passlib"]}, } ) project.pyproject.dependency_groups.update({"test": ["pytest"], "doc": ["mkdocs"]}) project.pyproject.write() @pytest.mark.parametrize( "args,golden", [ ({"default": True, "dev": None, "groups": ()}, ["default", "test", "doc"]), ( {"default": True, "dev": None, "groups": [":all"]}, ["default", "web", "auth", "test", "doc"], ), ( {"default": True, "dev": True, "groups": ["web"]}, ["default", "web", "test", "doc"], ), ( {"default": True, "dev": None, "groups": ["web"]}, ["default", "web", "test", "doc"], ), ({"default": True, "dev": None, "groups": ["test"]}, ["default", "test"]), ( {"default": True, "dev": None, "groups": ["test", "web"]}, ["default", "test", "web"], ), ({"default": True, "dev": False, "groups": ["web"]}, ["default", "web"]), ({"default": False, "dev": None, "groups": ()}, ["test", "doc"]), ], ) def test_dependency_group_selection(project, args, golden): setup_dependencies(project) selection = GroupSelection(project, **args) assert sorted(golden) == sorted(selection) @pytest.mark.parametrize( "args,golden", [ ({"groups": [":all"], "excluded_groups": ["web"]}, ["default", "auth", "doc", "test"]), ({"groups": [":all"], "excluded_groups": ["web", "auth"]}, ["default", "doc", "test"]), ({"groups": [":all"], "excluded_groups": ["default", "test"]}, ["auth", "doc", "web"]), ], ) def test_exclude_optional_groups_from_all(project, args, golden): setup_dependencies(project) selection = GroupSelection(project, **args) assert golden == list(selection) def test_prod_should_not_be_with_dev(project): setup_dependencies(project) selection = GroupSelection(project, default=True, dev=False, groups=["test"]) with pytest.raises(PdmUsageError): list(selection) def test_deprecation_warning(): with pytest.warns(PDMWarning) as record: utils.deprecation_warning("Test warning", raise_since="99.99") assert len(record) == 1 assert str(record[0].message) == "Test warning" with pytest.raises(PDMWarning): utils.deprecation_warning("Test warning", raise_since="0.0") def test_comparable_version(): assert utils.comparable_version("1.2.3") == utils.parse_version("1.2.3") assert utils.comparable_version("1.2.3a1+local1") == utils.parse_version("1.2.3a1") @pytest.mark.parametrize("name", ["foO", "f", "3d", "f3", "333", "foo.bar", "foo-bar", "foo_bar", "foo-_.bar"]) def test_validate_project_name(name): assert utils.validate_project_name(name) @pytest.mark.parametrize("name", ["", "-", ".foo", "foo_", "-3", "foo$bar", "a.3-"]) def test_invalidate_project_name(name): assert not utils.validate_project_name(name) @pytest.mark.parametrize( "given,sanitized", [("foo", "foo"), ("Foo.Bar", "Foo.Bar"), ("-foo", "foo"), ("Foo%$Bar", "Foo-Bar"), ("Foo$", "Foo")], ) def test_sanitize_project_name(given, sanitized): assert utils.sanitize_project_name(given) == sanitized pdm-2.23.1/tox.ini000066400000000000000000000003171477560627500137160ustar00rootroot00000000000000# https://pypi.org/project/tox-pdm/ is needed to run this tox configuration [tox] envlist = py3{8,9,10,11,12,13} passenv = LD_PRELOAD isolated_build = True [testenv] groups = test commands = test {posargs} pdm-2.23.1/typings/000077500000000000000000000000001477560627500140775ustar00rootroot00000000000000pdm-2.23.1/typings/shellingham.pyi000066400000000000000000000002011477560627500171060ustar00rootroot00000000000000def detect_shell(pid: int | None = None, max_depth: int = 10) -> tuple[str, str]: ... class ShellDetectionFailure(OSError): ...