pax_global_header00006660000000000000000000000064144475276300014526gustar00rootroot0000000000000052 comment=f24f2e8a762c715f113b11d1b97b65403a1576a2 pyproject-parser-0.9.1/000077500000000000000000000000001444752763000150465ustar00rootroot00000000000000pyproject-parser-0.9.1/.bumpversion.cfg000066400000000000000000000013161444752763000201570ustar00rootroot00000000000000[bumpversion] current_version = 0.9.1 commit = True tag = True [bumpversion:file:README.rst] [bumpversion:file:doc-source/index.rst] [bumpversion:file:doc-source/cli.rst] search = :rev: {current_version} replace = :rev: {new_version} [bumpversion:file:pyproject.toml] search = version = "{current_version}" replace = version = "{new_version}" [bumpversion:file:pyproject_parser/__init__.py] search = : str = "{current_version}" replace = : str = "{new_version}" [bumpversion:file:repo_helper.yml] [bumpversion:file:setup.cfg] search = version = {current_version} replace = version = {new_version} [bumpversion:file:.github/workflows/conda_ci.yml] search = ={current_version}=py_1 replace = ={new_version}=py_1 pyproject-parser-0.9.1/.dependabot/000077500000000000000000000000001444752763000172315ustar00rootroot00000000000000pyproject-parser-0.9.1/.dependabot/config.yml000066400000000000000000000003101444752763000212130ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. --- version: 1 update_configs: - package_manager: python directory: / update_schedule: weekly default_reviewers: - domdfcoding pyproject-parser-0.9.1/.github/000077500000000000000000000000001444752763000164065ustar00rootroot00000000000000pyproject-parser-0.9.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001444752763000205715ustar00rootroot00000000000000pyproject-parser-0.9.1/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000022411444752763000232620ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve labels: bug assignees: domdfcoding --- ## Description ## Steps to Reproduce 1. 2. 3. ## Actual result: ## Expected result: ## Reproduces how often: ## Version * Operating System: * Python: * pyproject-parser: ## Installation source ## Other Additional Information: pyproject-parser-0.9.1/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000012241444752763000243150ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project labels: "enhancement" assignees: domdfcoding --- ## Description ## Version * Operating System: * Python: * pyproject-parser: ## Other Additional Information: pyproject-parser-0.9.1/.github/auto_assign.yml000066400000000000000000000003471444752763000214510ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. --- addReviewers: true addAssignees: true reviewers: - domdfcoding numberOfReviewers: 0 # more settings at https://github.com/marketplace/actions/auto-assign-action pyproject-parser-0.9.1/.github/dependabot.yml000066400000000000000000000002771444752763000212440ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. --- version: 2 updates: - package-ecosystem: pip directory: / schedule: interval: weekly reviewers: - domdfcoding pyproject-parser-0.9.1/.github/milestones.py000077500000000000000000000012401444752763000211420ustar00rootroot00000000000000#!/usr/bin/env python # stdlib import os import sys # 3rd party from github3 import GitHub from github3.repos import Repository from packaging.version import InvalidVersion, Version latest_tag = os.environ["GITHUB_REF_NAME"] try: current_version = Version(latest_tag) except InvalidVersion: sys.exit() gh: GitHub = GitHub(token=os.environ["GITHUB_TOKEN"]) repo: Repository = gh.repository(*os.environ["GITHUB_REPOSITORY"].split('/', 1)) for milestone in repo.milestones(state="open"): try: milestone_version = Version(milestone.title) except InvalidVersion: continue if milestone_version == current_version: sys.exit(not milestone.update(state="closed")) pyproject-parser-0.9.1/.github/stale.yml000066400000000000000000000040211444752763000202360ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. # Configuration for probot-stale - https://github.com/probot/stale --- # Number of days of inactivity before an Issue or Pull Request becomes stale daysUntilStale: 180 # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. daysUntilClose: false # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) onlyLabels: [] # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable exemptLabels: - pinned - security - "[Status] Maybe Later" # Set to true to ignore issues in a project (defaults to false) exemptProjects: false # Set to true to ignore issues in a milestone (defaults to false) exemptMilestones: false # Set to true to ignore issues with an assignee (defaults to false) exemptAssignees: false # Label to use when marking as stale staleLabel: stale # Comment to post when marking as stale. Set to `false` to disable markComment: false # This issue has been automatically marked as stale because it has not had # recent activity. It will be closed if no further activity occurs. Thank you # for your contributions. # Comment to post when removing the stale label. # unmarkComment: > # Your comment here. # Comment to post when closing a stale Issue or Pull Request. # closeComment: > # Your comment here. # Limit the number of actions per hour, from 1-30. Default is 30 limitPerRun: 30 # Limit to only `issues` or `pulls` # only: issues # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': # pulls: # daysUntilStale: 30 # markComment: > # This pull request has been automatically marked as stale because it has not had # recent activity. It will be closed if no further activity occurs. Thank you # for your contributions. # issues: # exemptLabels: # - confirmed pyproject-parser-0.9.1/.github/workflows/000077500000000000000000000000001444752763000204435ustar00rootroot00000000000000pyproject-parser-0.9.1/.github/workflows/conda_ci.yml000066400000000000000000000037241444752763000227330ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. --- name: Conda Tests on: push: branches: ["master"] permissions: contents: read jobs: tests: name: "Conda" runs-on: ubuntu-22.04 defaults: run: shell: bash -l {0} steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v3" - name: Setup Python 🐍 uses: "actions/setup-python@v4" with: python-version: "3.8" - name: Setup Conda uses: conda-incubator/setup-miniconda@v2 with: activate-environment: env conda-build-version: 3.23.3 python-version: "3.8" miniforge-variant: Mambaforge - name: Install dependencies πŸ”§ run: | python -VV python -m site python -m pip install --upgrade pip setuptools wheel python -m pip install --upgrade "whey-conda" "setuptools!=61.*,>=40.6.0" "wheel>=0.34.2" # $CONDA is an environment variable pointing to the root of the miniconda directory $CONDA/bin/conda update -n base conda $CONDA/bin/conda config --add channels conda-forge $CONDA/bin/conda config --add channels domdfcoding - name: "Build and index channel" run: | python -m whey --builder whey_conda --out-dir conda-bld/noarch $CONDA/bin/conda index ./conda-bld || exit 1 - name: "Search for package" run: | $CONDA/bin/conda search -c file://$(pwd)/conda-bld pyproject-parser $CONDA/bin/conda search -c file://$(pwd)/conda-bld --override-channels pyproject-parser - name: "Install package" run: | $CONDA/bin/conda install -c file://$(pwd)/conda-bld pyproject-parser=0.9.1=py_1 -y || exit 1 - name: "Run Tests" run: | rm -rf pyproject_parser $CONDA/bin/conda install pytest coincidence || exit 1 pip install -r tests/requirements.txt pytest tests/ pyproject-parser-0.9.1/.github/workflows/docs_test_action.yml000066400000000000000000000015451444752763000245170ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. --- name: "Docs Check" on: push: branches-ignore: - 'repo-helper-update' - 'pre-commit-ci-update-config' - 'imgbot' pull_request: permissions: contents: read jobs: docs: runs-on: ubuntu-latest steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v3" - name: Check for changed files uses: dorny/paths-filter@v2 id: changes with: list-files: "json" filters: | code: - '!tests/**' - name: Install and Build πŸ”§ uses: sphinx-toolbox/sphinx-action@sphinx-3.3.1 if: steps.changes.outputs.code == 'true' with: pre-build-command: python -m pip install tox docs-folder: "doc-source/" build-command: "tox -e docs -- " pyproject-parser-0.9.1/.github/workflows/flake8.yml000066400000000000000000000023361444752763000223440ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. --- name: Flake8 on: push: branches-ignore: - 'repo-helper-update' - 'pre-commit-ci-update-config' - 'imgbot' pull_request: permissions: contents: read jobs: Run: name: "Flake8" runs-on: "ubuntu-20.04" steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v3" - name: Check for changed files uses: dorny/paths-filter@v2 id: changes with: list-files: "json" filters: | code: - '!(doc-source/**|CONTRIBUTING.rst|.imgbotconfig|.pre-commit-config.yaml|.pylintrc|.readthedocs.yml)' - name: Setup Python 🐍 if: steps.changes.outputs.code == 'true' uses: "actions/setup-python@v4" with: python-version: "3.6" - name: Install dependencies πŸ”§ if: steps.changes.outputs.code == 'true' run: | python -VV python -m site python -m pip install --upgrade pip setuptools wheel python -m pip install tox~=3.0 - name: "Run Flake8" if: steps.changes.outputs.code == 'true' run: "python -m tox -e lint -s false -- --format github" pyproject-parser-0.9.1/.github/workflows/mypy.yml000066400000000000000000000024501444752763000221650ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. --- name: mypy on: push: branches-ignore: - 'repo-helper-update' - 'pre-commit-ci-update-config' - 'imgbot' pull_request: permissions: contents: read jobs: Run: name: "mypy / ${{ matrix.os }}" runs-on: ${{ matrix.os }} strategy: matrix: os: ['ubuntu-20.04', 'windows-2019'] fail-fast: false steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v3" - name: Check for changed files uses: dorny/paths-filter@v2 id: changes with: list-files: "json" filters: | code: - '!(doc-source/**|CONTRIBUTING.rst|.imgbotconfig|.pre-commit-config.yaml|.pylintrc|.readthedocs.yml)' - name: Setup Python 🐍 if: steps.changes.outputs.code == 'true' uses: "actions/setup-python@v4" with: python-version: "3.6" - name: Install dependencies πŸ”§ run: | python -VV python -m site python -m pip install --upgrade pip setuptools wheel python -m pip install --upgrade tox~=3.0 virtualenv!=20.16.0 - name: "Run mypy" if: steps.changes.outputs.code == 'true' run: "python -m tox -e mypy -s false" pyproject-parser-0.9.1/.github/workflows/octocheese.yml000066400000000000000000000006231444752763000233100ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. --- name: "GitHub Releases" on: schedule: - cron: 0 12 * * * jobs: Run: runs-on: ubuntu-latest steps: - uses: domdfcoding/octocheese@master with: pypi_name: "pyproject-parser" env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} if: startsWith(github.ref, 'refs/tags/') != true pyproject-parser-0.9.1/.github/workflows/python_ci.yml000066400000000000000000000056641444752763000231750ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. --- name: Windows on: push: branches-ignore: - 'repo-helper-update' - 'pre-commit-ci-update-config' - 'imgbot' pull_request: permissions: actions: write issues: write contents: read jobs: tests: name: "windows-2019 / Python ${{ matrix.config.python-version }}" runs-on: "windows-2019" continue-on-error: ${{ matrix.config.experimental }} env: USING_COVERAGE: '3.6,3.7,3.8,3.9,3.10,3.11,3.12.0-beta.2,pypy-3.6,pypy-3.7,pypy-3.8,pypy-3.9' strategy: fail-fast: False matrix: config: - {python-version: "3.6", testenvs: "py36,build", experimental: False} - {python-version: "3.7", testenvs: "py37,build", experimental: False} - {python-version: "3.8", testenvs: "py38,build", experimental: False} - {python-version: "3.9", testenvs: "py39,build", experimental: False} - {python-version: "3.10", testenvs: "py310,build", experimental: False} - {python-version: "3.11", testenvs: "py311,build", experimental: False} - {python-version: "3.12.0-beta.2", testenvs: "py312-dev,build", experimental: True} - {python-version: "pypy-3.6", testenvs: "pypy36,build", experimental: False} - {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: True} - {python-version: "pypy-3.8", testenvs: "pypy38,build", experimental: True} - {python-version: "pypy-3.9", testenvs: "pypy39", experimental: True} steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v3" - name: Check for changed files if: startsWith(github.ref, 'refs/tags/') != true uses: dorny/paths-filter@v2 id: changes with: list-files: "json" filters: | code: - '!(doc-source/**|CONTRIBUTING.rst|.imgbotconfig|.pre-commit-config.yaml|.pylintrc|.readthedocs.yml)' - name: Setup Python 🐍 id: setup-python if: ${{ steps.changes.outputs.code == 'true' || steps.changes.outcome == 'skipped' }} uses: "actions/setup-python@v4" with: python-version: "${{ matrix.config.python-version }}" - name: Install dependencies πŸ”§ if: steps.setup-python.outcome == 'success' run: | python -VV python -m site python -m pip install --upgrade pip setuptools wheel python -m pip install --upgrade tox~=3.0 virtualenv!=20.16.0 - name: "Run Tests for Python ${{ matrix.config.python-version }}" if: steps.setup-python.outcome == 'success' run: python -m tox -e "${{ matrix.config.testenvs }}" -s false - name: "Upload Coverage πŸš€" uses: actions/upload-artifact@v3 if: ${{ always() && steps.setup-python.outcome == 'success' }} with: name: "coverage-${{ matrix.config.python-version }}" path: .coverage pyproject-parser-0.9.1/.github/workflows/python_ci_linux.yml000066400000000000000000000173111444752763000244040ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. --- name: Linux on: push: branches-ignore: - 'repo-helper-update' - 'pre-commit-ci-update-config' - 'imgbot' tags: - '*' pull_request: permissions: actions: write issues: write contents: read jobs: tests: name: "ubuntu-20.04 / Python ${{ matrix.config.python-version }}" runs-on: "ubuntu-20.04" continue-on-error: ${{ matrix.config.experimental }} env: USING_COVERAGE: '3.6,3.7,3.8,3.9,3.10,3.11,3.12.0-beta.2,pypy-3.6,pypy-3.7,pypy-3.8,pypy-3.9' strategy: fail-fast: False matrix: config: - {python-version: "3.6", testenvs: "py36,build", experimental: False} - {python-version: "3.7", testenvs: "py37,build", experimental: False} - {python-version: "3.8", testenvs: "py38,build", experimental: False} - {python-version: "3.9", testenvs: "py39,build", experimental: False} - {python-version: "3.10", testenvs: "py310,build", experimental: False} - {python-version: "3.11", testenvs: "py311,build", experimental: False} - {python-version: "3.12.0-beta.2", testenvs: "py312-dev,build", experimental: True} - {python-version: "pypy-3.6", testenvs: "pypy36,build", experimental: False} - {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: True} - {python-version: "pypy-3.8", testenvs: "pypy38,build", experimental: True} - {python-version: "pypy-3.9", testenvs: "pypy39", experimental: True} steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v3" - name: Check for changed files if: startsWith(github.ref, 'refs/tags/') != true uses: dorny/paths-filter@v2 id: changes with: list-files: "json" filters: | code: - '!(doc-source/**|CONTRIBUTING.rst|.imgbotconfig|.pre-commit-config.yaml|.pylintrc|.readthedocs.yml)' - name: Setup Python 🐍 id: setup-python if: ${{ steps.changes.outputs.code == 'true' || steps.changes.outcome == 'skipped' }} uses: "actions/setup-python@v4" with: python-version: "${{ matrix.config.python-version }}" - name: Install dependencies πŸ”§ if: steps.setup-python.outcome == 'success' run: | python -VV python -m site python -m pip install --upgrade pip setuptools wheel python -m pip install --upgrade tox~=3.0 virtualenv!=20.16.0 python -m pip install --upgrade coverage_pyver_pragma - name: "Run Tests for Python ${{ matrix.config.python-version }}" if: steps.setup-python.outcome == 'success' run: python -m tox -e "${{ matrix.config.testenvs }}" -s false - name: "Upload Coverage πŸš€" uses: actions/upload-artifact@v3 if: ${{ always() && steps.setup-python.outcome == 'success' }} with: name: "coverage-${{ matrix.config.python-version }}" path: .coverage Coverage: needs: tests runs-on: "ubuntu-20.04" steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v3" - name: Setup Python 🐍 uses: "actions/setup-python@v4" with: python-version: 3.8 - name: Install dependencies πŸ”§ run: | python -m pip install --upgrade pip setuptools wheel python -m pip install --upgrade "coveralls>=3.0.0" coverage_pyver_pragma - name: "Download Coverage πŸͺ‚" uses: actions/download-artifact@v3 with: path: coverage - name: Display structure of downloaded files id: show run: ls -R working-directory: coverage continue-on-error: true - name: Combine Coverage πŸ‘· if: ${{ steps.show.outcome != 'failure' }} run: | shopt -s globstar python -m coverage combine coverage/**/.coverage - name: "Upload Combined Coverage Artefact πŸš€" if: ${{ steps.show.outcome != 'failure' }} uses: actions/upload-artifact@v3 with: name: "combined-coverage" path: .coverage - name: "Upload Combined Coverage to Coveralls" if: ${{ steps.show.outcome != 'failure' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | coveralls --service=github Deploy: needs: tests runs-on: "ubuntu-20.04" steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v3" if: startsWith(github.ref, 'refs/tags/') - name: Setup Python 🐍 uses: "actions/setup-python@v4" if: startsWith(github.ref, 'refs/tags/') with: python-version: 3.8 - name: Install dependencies πŸ”§ if: startsWith(github.ref, 'refs/tags/') run: | python -m pip install --upgrade pip setuptools wheel python -m pip install --upgrade tox~=3.0 - name: Build distributions πŸ“¦ if: startsWith(github.ref, 'refs/tags/') run: | tox -e build - name: Upload distribution to PyPI πŸš€ if: startsWith(github.ref, 'refs/tags/') uses: pypa/gh-action-pypi-publish@v1.4.2 with: user: __token__ password: ${{ secrets.PYPI_TOKEN }} skip_existing: true - name: Close milestone πŸšͺ if: startsWith(github.ref, 'refs/tags/') run: | python -m pip install --upgrade github3.py packaging python .github/milestones.py env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} Conda: needs: deploy runs-on: ubuntu-22.04 if: startsWith(github.ref, 'refs/tags/') || (startsWith(github.event.head_commit.message, 'Bump version') != true) steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v3" - name: Setup Python 🐍 uses: "actions/setup-python@v4" with: python-version: 3.8 - name: Setup Conda uses: conda-incubator/setup-miniconda@v2 with: activate-environment: env conda-build-version: 3.23.3 python-version: "3.8" miniforge-variant: Mambaforge - name: Install dependencies πŸ”§ run: | python -VV python -m site python -m pip install --upgrade pip setuptools wheel python -m pip install --upgrade "mkrecipe" "setuptools!=61.*,>=40.6.0" "wheel>=0.34.2" # $CONDA is an environment variable pointing to the root of the miniconda directory $CONDA/bin/conda config --set always_yes yes --set changeps1 no $CONDA/bin/conda update -n base conda $CONDA/bin/conda info -a $CONDA/bin/conda config --add channels conda-forge $CONDA/bin/conda config --add channels domdfcoding $CONDA/bin/conda config --remove channels defaults - name: Build Conda Package πŸ“¦ run: | python -m mkrecipe --type wheel || exit 1 $CONDA/bin/conda build conda -c conda-forge -c domdfcoding --output-folder conda/dist - name: Deploy Conda Package πŸš€ if: startsWith(github.ref, 'refs/tags/') run: | $CONDA/bin/conda config --set always_yes yes --set changeps1 no $CONDA/bin/conda install anaconda-client $CONDA/bin/conda info -a for f in conda/dist/noarch/pyproject-parser-*.tar.bz2; do [ -e "$f" ] || continue echo "$f" conda install "$f" || exit 1 echo "Deploying to Anaconda.org..." $CONDA/bin/anaconda -t "$ANACONDA_TOKEN" upload "$f" || exit 1 echo "Successfully deployed to Anaconda.org." done env: ANACONDA_TOKEN: ${{ secrets.ANACONDA_TOKEN }} pyproject-parser-0.9.1/.github/workflows/python_ci_macos.yml000066400000000000000000000055211444752763000243470ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. --- name: macOS on: push: branches-ignore: - 'repo-helper-update' - 'pre-commit-ci-update-config' - 'imgbot' pull_request: permissions: actions: write issues: write contents: read jobs: tests: name: "macos-latest / Python ${{ matrix.config.python-version }}" runs-on: "macos-latest" continue-on-error: ${{ matrix.config.experimental }} env: USING_COVERAGE: '3.6,3.7,3.8,3.9,3.10,3.11,3.12.0-beta.2,pypy-3.7,pypy-3.8,pypy-3.9' strategy: fail-fast: False matrix: config: - {python-version: "3.6", testenvs: "py36,build", experimental: False} - {python-version: "3.7", testenvs: "py37,build", experimental: False} - {python-version: "3.8", testenvs: "py38,build", experimental: False} - {python-version: "3.9", testenvs: "py39,build", experimental: False} - {python-version: "3.10", testenvs: "py310,build", experimental: False} - {python-version: "3.11", testenvs: "py311,build", experimental: False} - {python-version: "3.12.0-beta.2", testenvs: "py312-dev,build", experimental: True} - {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: True} - {python-version: "pypy-3.8", testenvs: "pypy38,build", experimental: True} - {python-version: "pypy-3.9", testenvs: "pypy39", experimental: True} steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v3" - name: Check for changed files if: startsWith(github.ref, 'refs/tags/') != true uses: dorny/paths-filter@v2 id: changes with: list-files: "json" filters: | code: - '!(doc-source/**|CONTRIBUTING.rst|.imgbotconfig|.pre-commit-config.yaml|.pylintrc|.readthedocs.yml)' - name: Setup Python 🐍 id: setup-python if: ${{ steps.changes.outputs.code == 'true' || steps.changes.outcome == 'skipped' }} uses: "actions/setup-python@v4" with: python-version: "${{ matrix.config.python-version }}" - name: Install dependencies πŸ”§ if: steps.setup-python.outcome == 'success' run: | python -VV python -m site python -m pip install --upgrade pip setuptools wheel python -m pip install --upgrade tox~=3.0 virtualenv!=20.16.0 - name: "Run Tests for Python ${{ matrix.config.python-version }}" if: steps.setup-python.outcome == 'success' run: python -m tox -e "${{ matrix.config.testenvs }}" -s false - name: "Upload Coverage πŸš€" uses: actions/upload-artifact@v3 if: ${{ always() && steps.setup-python.outcome == 'success' }} with: name: "coverage-${{ matrix.config.python-version }}" path: .coverage pyproject-parser-0.9.1/.gitignore000066400000000000000000000020161444752763000170350ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. __pycache__/ *.py[cod] *$py.class *.so .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg *.egg* *.manifest *.spec pip-log.txt pip-delete-this-directory.txt htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ cover/ *.mo *.pot *.log local_settings.py db.sqlite3 instance/ .webassets-cache .scrapy docs/_build/ doc/build target/ .ipynb_checkpoints .python-version celerybeat-schedule celerybeat.pid *.sage.py .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ .spyderproject .spyproject .ropeproject /site .mypy_cache/ .dmypy.json dmypy.json *.iml *.ipr cmake-build-*/ .idea/**/mongoSettings.xml *.iws out/ atlassian-ide-plugin.xml com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties .idea build **/__pycache__ **/conda __pypackages__/ profile_default/ ipython_config.py Pipfile.lock .pyre/ pyproject-parser-0.9.1/.imgbotconfig000066400000000000000000000001151444752763000175130ustar00rootroot00000000000000{ "schedule": "weekly", "ignoredFiles": [ "**/*.svg" ] } pyproject-parser-0.9.1/.pre-commit-config.yaml000066400000000000000000000042621444752763000213330ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. --- exclude: ^$ ci: autoupdate_schedule: quarterly repos: - repo: https://github.com/repo-helper/pyproject-parser rev: v0.7.0 hooks: - id: reformat-pyproject - repo: https://github.com/pre-commit/pre-commit-hooks rev: v3.4.0 hooks: - id: check-added-large-files - id: check-ast - id: fix-byte-order-marker - id: check-byte-order-marker - id: check-case-conflict - id: check-executables-have-shebangs - id: check-json - id: check-toml - id: check-yaml - id: check-merge-conflict - id: check-symlinks - id: check-vcs-permalinks - id: detect-private-key - id: trailing-whitespace - id: mixed-line-ending - id: end-of-file-fixer - repo: https://github.com/domdfcoding/pre-commit-hooks rev: v0.4.0 hooks: - id: requirements-txt-sorter args: - --allow-git - id: check-docstring-first exclude: ^(doc-source/conf|__pkginfo__|setup|tests/.*)\.py$ - id: bind-requirements - repo: https://github.com/domdfcoding/flake8-dunder-all rev: v0.2.2 hooks: - id: ensure-dunder-all files: ^pyproject_parser/.*\.py$ - repo: https://github.com/domdfcoding/flake2lint rev: v0.4.2 hooks: - id: flake2lint - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: - id: python-no-eval - id: rst-backticks - id: rst-directive-colons - id: rst-inline-touching-normal - repo: https://github.com/asottile/pyupgrade rev: v2.12.0 hooks: - id: pyupgrade args: - --py36-plus - --keep-runtime-typing - repo: https://github.com/Lucas-C/pre-commit-hooks rev: v1.5.1 hooks: - id: remove-crlf - id: forbid-crlf - repo: https://github.com/python-formate/snippet-fmt rev: v0.1.4 hooks: - id: snippet-fmt - repo: https://github.com/python-formate/formate rev: v0.4.10 hooks: - id: formate exclude: ^(doc-source/conf|__pkginfo__|setup)\.(_)?py$ - repo: https://github.com/domdfcoding/dep_checker rev: v0.7.1 hooks: - id: dep_checker args: - pyproject_parser # Custom hooks can be added below this comment pyproject-parser-0.9.1/.pre-commit-hooks.yaml000066400000000000000000000010111444752763000211760ustar00rootroot00000000000000- id: check-pyproject name: Validate 'pyproject.toml' description: Validate 'pyproject.toml' entry: pyproject-parser check language: python files: "^pyproject\\.toml$" additional_dependencies: ["consolekit>=1.1.2", "click>=7.1.2"] - id: reformat-pyproject name: Reformat 'pyproject.toml' description: Reformat 'pyproject.toml' entry: pyproject-parser reformat language: python files: "^pyproject\\.toml$" additional_dependencies: ["consolekit>=1.1.2", "click>=7.1.2"] pyproject-parser-0.9.1/.pylintrc000066400000000000000000000346201444752763000167200ustar00rootroot00000000000000[MASTER] # Specify a configuration file. #rcfile= # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). #init-hook= # Add files or directories to the blacklist. They should be base names, not # paths. ignore=CVS # Add files or directories matching the regex patterns to the blacklist. The # regex matches against base names, not paths. ignore-patterns= # Pickle collected data for later comparisons. persistent=yes # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. load-plugins= # Use multiple processes to speed up Pylint. jobs=1 # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code extension-pkg-whitelist= # Allow optimization of some AST trees. This will activate a peephole AST # optimizer, which will apply various small optimizations. For instance, it can # be used to obtain the result of joining multiple strings with the addition # operator. Joining a lot of strings can lead to a maximum recursion error in # Pylint and this flag can prevent that. It has one side effect, the resulting # AST will be different than the one from reality. This option is deprecated # and it will be removed in Pylint 2.0. optimize-ast=no [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED confidence= # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). See also the "--disable" option for examples. #enable= # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this # option multiple times (only on the command line, not in the configuration # file where it should appear only once).You can also use "--disable=all" to # disable everything first and then reenable specific checks. For example, if # you want to run only the similarities checker, you can use "--disable=all # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" disable=all enable=assert-on-tuple,astroid-error,bad-except-order,bad-inline-option,bad-option-value,bad-reversed-sequence,bare-except,binary-op-exception,boolean-datetime,catching-non-exception,cell-var-from-loop,confusing-with-statement,consider-merging-isinstance,consider-using-enumerate,consider-using-ternary,continue-in-finally,deprecated-pragma,django-not-available,duplicate-except,duplicate-key,eval-used,exec-used,expression-not-assigned,fatal,file-ignored,fixme,global-at-module-level,global-statement,global-variable-not-assigned,global-variable-undefined,http-response-with-content-type-json,http-response-with-json-dumps,invalid-all-object,invalid-characters-in-docstring,len-as-condition,literal-comparison,locally-disabled,locally-enabled,lost-exception,lowercase-l-suffix,misplaced-bare-raise,missing-kwoa,mixed-line-endings,model-has-unicode,model-missing-unicode,model-no-explicit-unicode,model-unicode-not-callable,multiple-imports,new-db-field-with-default,non-ascii-bytes-literals,nonexistent-operator,not-in-loop,notimplemented-raised,overlapping-except,parse-error,pointless-statement,pointless-string-statement,raising-bad-type,raising-non-exception,raw-checker-failed,redefine-in-handler,redefined-argument-from-local,redefined-builtin,redundant-content-type-for-json-response,reimported,relative-import,return-outside-function,simplifiable-if-statement,singleton-comparison,syntax-error,trailing-comma-tuple,trailing-newlines,unbalanced-tuple-unpacking,undefined-all-variable,undefined-loop-variable,unexpected-line-ending-format,unidiomatic-typecheck,unnecessary-lambda,unnecessary-pass,unnecessary-semicolon,unneeded-not,unpacking-non-sequence,unreachable,unrecognized-inline-option,used-before-assignment,useless-else-on-loop,using-constant-test,wildcard-import,yield-outside-function,useless-return [REPORTS] # Set the output format. Available formats are text, parseable, colorized, msvs # (visual studio) and html. You can also give a reporter class, eg # mypackage.mymodule.MyReporterClass. output-format=text # Put messages in a separate file for each module / package specified on the # command line instead of printing them on stdout. Reports (if any) will be # written in a file name "pylint_global.[txt|html]". This option is deprecated # and it will be removed in Pylint 2.0. files-output=no # Tells whether to display a full report or only the messages reports=no # Python expression which should return a note less than 10 (10 is the highest # note). You have access to the variables errors warning, statement which # respectively contain the number of errors / warnings messages and the total # number of statements analyzed. This is used by the global evaluation report # (RP0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) # Template used to display messages. This is a python new-style format string # used to format the message information. See doc for all details #msg-template= [BASIC] # Good variable names which should always be accepted, separated by a comma good-names=i,j,k,ex,Run,_ # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata # Colon-delimited sets of names that determine each other's naming style when # the name regexes allow several styles. name-group= # Include a hint for the correct naming format with invalid-name include-naming-hint=no # List of decorators that produce properties, such as abc.abstractproperty. Add # to this list to register other decorators that produce valid properties. property-classes=abc.abstractproperty # Regular expression matching correct function names function-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for function names function-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct variable names variable-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for variable names variable-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct constant names const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ # Naming hint for constant names const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ # Regular expression matching correct attribute names attr-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for attribute names attr-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct argument names argument-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for argument names argument-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct class attribute names class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ # Naming hint for class attribute names class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ # Regular expression matching correct inline iteration names inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ # Naming hint for inline iteration names inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ # Regular expression matching correct class names class-rgx=[A-Z_][a-zA-Z0-9]+$ # Naming hint for class names class-name-hint=[A-Z_][a-zA-Z0-9]+$ # Regular expression matching correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ # Naming hint for module names module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ # Regular expression matching correct method names method-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for method names method-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression which should only match function or class names that do # not require a docstring. no-docstring-rgx=^_ # Minimum line length for functions/classes that require docstrings, shorter # ones are exempt. docstring-min-length=-1 [ELIF] # Maximum number of nested blocks for function / method body max-nested-blocks=5 [FORMAT] # Maximum number of characters on a single line. max-line-length=159 # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ # Allow the body of an if to be on the same line as the test if there is no # else. single-line-if-stmt=no # List of optional constructs for which whitespace checking is disabled. `dict- # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. # `trailing-comma` allows a space between comma and closing bracket: (a, ). # `empty-line` allows space-only lines. no-space-check=trailing-comma,dict-separator # Maximum number of lines in a module max-module-lines=1000 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). indent-string=' ' # Number of spaces of indent required inside a hanging or continued line. indent-after-paren=4 # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. expected-line-ending-format= [LOGGING] # Logging modules to check that the string format arguments are in logging # function parameter format logging-modules=logging [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes=FIXME,XXX,TODO [SIMILARITIES] # Minimum lines number of a similarity. min-similarity-lines=4 # Ignore comments when computing similarities. ignore-comments=yes # Ignore docstrings when computing similarities. ignore-docstrings=yes # Ignore imports when computing similarities. ignore-imports=no [SPELLING] # Spelling dictionary name. Available dictionaries: none. To make it working # install python-enchant package. spelling-dict= # List of comma separated words that should not be checked. spelling-ignore-words= # A path to a file that contains private dictionary; one word per line. spelling-private-dict-file= # Tells whether to store unknown words to indicated private dictionary in # --spelling-private-dict-file option instead of raising a message. spelling-store-unknown-words=no [TYPECHECK] # Tells whether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). ignore-mixin-members=yes # List of module names for which member attributes should not be checked # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis. It # supports qualified module names, as well as Unix pattern matching. ignored-modules= # List of class names for which member attributes should not be checked (useful # for classes with dynamically set attributes). This supports the use of # qualified names. ignored-classes=optparse.Values,thread._local,_thread._local # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. generated-members= # List of decorators that produce context managers, such as # contextlib.contextmanager. Add to this list to register other decorators that # produce valid context managers. contextmanager-decorators=contextlib.contextmanager [VARIABLES] # Tells whether we should check for unused import in __init__ files. init-import=no # A regular expression matching the name of dummy variables (i.e. expectedly # not used). dummy-variables-rgx=(_+[a-zA-Z0-9]*?$)|dummy # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. additional-builtins= # List of strings which can identify a callback function by name. A callback # name must start or end with one of those strings. callbacks=cb_,_cb # List of qualified module names which can have objects that can redefine # builtins. redefining-builtins-modules=six.moves,future.builtins [CLASSES] # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__,__new__,setUp # List of valid names for the first argument in a class method. valid-classmethod-first-arg=cls # List of valid names for the first argument in a metaclass class method. valid-metaclass-classmethod-first-arg=mcs # List of member names, which should be excluded from the protected access # warning. exclude-protected=_asdict,_fields,_replace,_source,_make [DESIGN] # Maximum number of arguments for function / method max-args=5 # Argument names that match this expression will be ignored. Default to name # with leading underscore ignored-argument-names=_.* # Maximum number of locals for function / method body max-locals=15 # Maximum number of return / yield for function / method body max-returns=6 # Maximum number of branch for function / method body max-branches=12 # Maximum number of statements in function / method body max-statements=60 # Maximum number of parents for a class (see R0901). max-parents=7 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Minimum number of public methods for a class (see R0903). min-public-methods=2 # Maximum number of public methods for a class (see R0904). max-public-methods=20 # Maximum number of boolean expressions in a if statement max-bool-expr=5 [IMPORTS] # Deprecated modules which should not be used, separated by a comma deprecated-modules=regsub,TERMIOS,Bastion,rexec # Create a graph of every (i.e. internal and external) dependencies in the # given file (report RP0402 must not be disabled) import-graph= # Create a graph of external dependencies in the given file (report RP0402 must # not be disabled) ext-import-graph= # Create a graph of internal dependencies in the given file (report RP0402 must # not be disabled) int-import-graph= # Force import order to recognize a module as part of the standard # compatibility libraries. known-standard-library= # Force import order to recognize a module as part of a third party library. known-third-party=enchant # Analyse import fallback blocks. This can be used to support both Python 2 and # 3 compatible code, which means that the block might have code that exists # only in one or another interpreter, leading to false positives when analysed. analyse-fallback-blocks=no [EXCEPTIONS] # Exceptions that will emit a warning when being caught. Defaults to # "Exception" overgeneral-exceptions=Exception pyproject-parser-0.9.1/.readthedocs.yml000066400000000000000000000006531444752763000201400ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. # Read the Docs configuration file --- version: 2 sphinx: builder: html configuration: doc-source/conf.py formats: - pdf - htmlzip python: install: - requirements: requirements.txt - requirements: doc-source/requirements.txt build: os: ubuntu-20.04 tools: python: '3.9' jobs: post_create_environment: - pip install .[readme,cli] pyproject-parser-0.9.1/.style.yapf000066400000000000000000000272001444752763000171460ustar00rootroot00000000000000[style] # Align closing bracket with visual indentation. align_closing_bracket_with_visual_indent=True # Allow dictionary keys to exist on multiple lines. For example: # # x = { # ('this is the first element of a tuple', # 'this is the second element of a tuple'): # value, # } allow_multiline_dictionary_keys=True # Allow lambdas to be formatted on more than one line. allow_multiline_lambdas=False # Allow splitting before a default / named assignment in an argument list. allow_split_before_default_or_named_assigns=True # Allow splits before the dictionary value. allow_split_before_dict_value=True # Let spacing indicate operator precedence. For example: # # a = 1 * 2 + 3 / 4 # b = 1 / 2 - 3 * 4 # c = (1 + 2) * (3 - 4) # d = (1 - 2) / (3 + 4) # e = 1 * 2 - 3 # f = 1 + 2 + 3 + 4 # # will be formatted as follows to indicate precedence: # # a = 1*2 + 3/4 # b = 1/2 - 3*4 # c = (1+2) * (3-4) # d = (1-2) / (3+4) # e = 1*2 - 3 # f = 1 + 2 + 3 + 4 # arithmetic_precedence_indication=False # Number of blank lines surrounding top-level function and class # definitions. blank_lines_around_top_level_definition=2 # Insert a blank line before a class-level docstring. blank_line_before_class_docstring=False # Insert a blank line before a module docstring. blank_line_before_module_docstring=False # Insert a blank line before a 'def' or 'class' immediately nested # within another 'def' or 'class'. For example: # # class Foo: # # <------ this blank line # def method(): # ... blank_line_before_nested_class_or_def=True # Do not split consecutive brackets. Only relevant when # dedent_closing_brackets is set. For example: # # call_func_that_takes_a_dict( # { # 'key1': 'value1', # 'key2': 'value2', # } # ) # # would reformat to: # # call_func_that_takes_a_dict({ # 'key1': 'value1', # 'key2': 'value2', # }) coalesce_brackets=True # The column limit. column_limit=115 # The style for continuation alignment. Possible values are: # # - SPACE: Use spaces for continuation alignment. This is default behavior. # - FIXED: Use fixed number (CONTINUATION_INDENT_WIDTH) of columns # (ie: CONTINUATION_INDENT_WIDTH/INDENT_WIDTH tabs or # CONTINUATION_INDENT_WIDTH spaces) for continuation alignment. # - VALIGN-RIGHT: Vertically align continuation lines to multiple of # INDENT_WIDTH columns. Slightly right (one tab or a few spaces) if # cannot vertically align continuation lines with indent characters. continuation_align_style=VALIGN-RIGHT # Indent width used for line continuations. continuation_indent_width=8 # Put closing brackets on a separate line, dedented, if the bracketed # expression can't fit in a single line. Applies to all kinds of brackets, # including function definitions and calls. For example: # # config = { # 'key1': 'value1', # 'key2': 'value2', # } # <--- this bracket is dedented and on a separate line # # time_series = self.remote_client.query_entity_counters( # entity='dev3246.region1', # key='dns.query_latency_tcp', # transform=Transformation.AVERAGE(window=timedelta(seconds=60)), # start_ts=now()-timedelta(days=3), # end_ts=now(), # ) # <--- this bracket is dedented and on a separate line dedent_closing_brackets=False # Disable the heuristic which places each list element on a separate line # if the list is comma-terminated. disable_ending_comma_heuristic=False # Place each dictionary entry onto its own line. each_dict_entry_on_separate_line=False # Require multiline dictionary even if it would normally fit on one line. # For example: # # config = { # 'key1': 'value1' # } force_multiline_dict=False # The regex for an i18n comment. The presence of this comment stops # reformatting of that line, because the comments are required to be # next to the string they translate. ;i18n_comment= # The i18n function call names. The presence of this function stops # reformattting on that line, because the string it has cannot be moved # away from the i18n comment. ;i18n_function_call= # Indent blank lines. indent_blank_lines=False # Put closing brackets on a separate line, indented, if the bracketed # expression can't fit in a single line. Applies to all kinds of brackets, # including function definitions and calls. For example: # # config = { # 'key1': 'value1', # 'key2': 'value2', # } # <--- this bracket is indented and on a separate line # # time_series = self.remote_client.query_entity_counters( # entity='dev3246.region1', # key='dns.query_latency_tcp', # transform=Transformation.AVERAGE(window=timedelta(seconds=60)), # start_ts=now()-timedelta(days=3), # end_ts=now(), # ) # <--- this bracket is indented and on a separate line indent_closing_brackets=True # Indent the dictionary value if it cannot fit on the same line as the # dictionary key. For example: # # config = { # 'key1': # 'value1', # 'key2': value1 + # value2, # } indent_dictionary_value=True # The number of columns to use for indentation. indent_width=4 # Join short lines into one line. E.g., single line 'if' statements. join_multiple_lines=False # Do not include spaces around selected binary operators. For example: # # 1 + 2 * 3 - 4 / 5 # # will be formatted as follows when configured with "*,/": # # 1 + 2*3 - 4/5 ;no_spaces_around_selected_binary_operators= # Use spaces around default or named assigns. spaces_around_default_or_named_assign=False # Adds a space after the opening '{' and before the ending '}' dict delimiters. # # {1: 2} # # will be formatted as: # # { 1: 2 } spaces_around_dict_delimiters=False # Adds a space after the opening '[' and before the ending ']' list delimiters. # # [1, 2] # # will be formatted as: # # [ 1, 2 ] spaces_around_list_delimiters=False # Use spaces around the power operator. spaces_around_power_operator=False # Use spaces around the subscript / slice operator. For example: # # my_list[1 : 10 : 2] spaces_around_subscript_colon=False # Adds a space after the opening '(' and before the ending ')' tuple delimiters. # # (1, 2, 3) # # will be formatted as: # # ( 1, 2, 3 ) spaces_around_tuple_delimiters=False # The number of spaces required before a trailing comment. # This can be a single value (representing the number of spaces # before each trailing comment) or list of values (representing # alignment column values; trailing comments within a block will # be aligned to the first column value that is greater than the maximum # line length within the block). For example: # # With spaces_before_comment=5: # # 1 + 1 # Adding values # # will be formatted as: # # 1 + 1 # Adding values <-- 5 spaces between the end of the statement and comment # # With spaces_before_comment=15, 20: # # 1 + 1 # Adding values # two + two # More adding # # longer_statement # This is a longer statement # short # This is a shorter statement # # a_very_long_statement_that_extends_beyond_the_final_column # Comment # short # This is a shorter statement # # will be formatted as: # # 1 + 1 # Adding values <-- end of line comments in block aligned to col 15 # two + two # More adding # # longer_statement # This is a longer statement <-- end of line comments in block aligned to col 20 # short # This is a shorter statement # # a_very_long_statement_that_extends_beyond_the_final_column # Comment <-- the end of line comments are aligned based on the line length # short # This is a shorter statement # spaces_before_comment=2 # Insert a space between the ending comma and closing bracket of a list, # etc. space_between_ending_comma_and_closing_bracket=True # Use spaces inside brackets, braces, and parentheses. For example: # # method_call( 1 ) # my_dict[ 3 ][ 1 ][ get_index( *args, **kwargs ) ] # my_set = { 1, 2, 3 } space_inside_brackets=False # Split before arguments split_all_comma_separated_values=False # Split before arguments, but do not split all subexpressions recursively # (unless needed). split_all_top_level_comma_separated_values=True # Split before arguments if the argument list is terminated by a # comma. split_arguments_when_comma_terminated=False # Set to True to prefer splitting before '+', '-', '*', '/', '//', or '@' # rather than after. split_before_arithmetic_operator=True # Set to True to prefer splitting before '&', '|' or '^' rather than # after. split_before_bitwise_operator=True # Split before the closing bracket if a list or dict literal doesn't fit on # a single line. split_before_closing_bracket=True # Split before a dictionary or set generator (comp_for). For example, note # the split before the 'for': # # foo = { # variable: 'Hello world, have a nice day!' # for variable in bar if variable != 42 # } split_before_dict_set_generator=True # Split before the '.' if we need to split a longer expression: # # foo = ('This is a really long string: {}, {}, {}, {}'.format(a, b, c, d)) # # would reformat to something like: # # foo = ('This is a really long string: {}, {}, {}, {}' # .format(a, b, c, d)) split_before_dot=False # Split after the opening paren which surrounds an expression if it doesn't # fit on a single line. split_before_expression_after_opening_paren=True # If an argument / parameter list is going to be split, then split before # the first argument. split_before_first_argument=False # Set to True to prefer splitting before 'and' or 'or' rather than # after. split_before_logical_operator=True # Split named assignments onto individual lines. split_before_named_assigns=True # Set to True to split list comprehensions and generators that have # non-trivial expressions and multiple clauses before each of these # clauses. For example: # # result = [ # a_long_var + 100 for a_long_var in xrange(1000) # if a_long_var % 10] # # would reformat to something like: # # result = [ # a_long_var + 100 # for a_long_var in xrange(1000) # if a_long_var % 10] split_complex_comprehension=True # The penalty for splitting right after the opening bracket. split_penalty_after_opening_bracket=100 # The penalty for splitting the line after a unary operator. split_penalty_after_unary_operator=10000 # The penalty of splitting the line around the '+', '-', '*', '/', '//', # ``%``, and '@' operators. split_penalty_arithmetic_operator=300 # The penalty for splitting right before an if expression. split_penalty_before_if_expr=0 # The penalty of splitting the line around the '&', '|', and '^' # operators. split_penalty_bitwise_operator=300 # The penalty for splitting a list comprehension or generator # expression. split_penalty_comprehension=80 # The penalty for characters over the column limit. split_penalty_excess_character=7000 # The penalty incurred by adding a line split to the unwrapped line. The # more line splits added the higher the penalty. split_penalty_for_added_line_split=30 # The penalty of splitting a list of "import as" names. For example: # # from a_very_long_or_indented_module_name_yada_yad import (long_argument_1, # long_argument_2, # long_argument_3) # # would reformat to something like: # # from a_very_long_or_indented_module_name_yada_yad import ( # long_argument_1, long_argument_2, long_argument_3) split_penalty_import_names=0 # The penalty of splitting the line around the 'and' and 'or' # operators. split_penalty_logical_operator=300 # Use the Tab character for indentation. use_tabs=True pyproject-parser-0.9.1/CONTRIBUTING.rst000066400000000000000000000025111444752763000175060ustar00rootroot00000000000000============== Contributing ============== .. This file based on https://github.com/PyGithub/PyGithub/blob/master/CONTRIBUTING.md ``pyproject-parser`` uses `tox `_ to automate testing and packaging, and `pre-commit `_ to maintain code quality. Install ``pre-commit`` with ``pip`` and install the git hook: .. code-block:: bash $ python -m pip install pre-commit $ pre-commit install Coding style -------------- `formate `_ is used for code formatting. It can be run manually via ``pre-commit``: .. code-block:: bash $ pre-commit run formate -a Or, to run the complete autoformatting suite: .. code-block:: bash $ pre-commit run -a Automated tests ------------------- Tests are run with ``tox`` and ``pytest``. To run tests for a specific Python version, such as Python 3.6: .. code-block:: bash $ tox -e py36 To run tests for all Python versions, simply run: .. code-block:: bash $ tox Type Annotations ------------------- Type annotations are checked using ``mypy``. Run ``mypy`` using ``tox``: .. code-block:: bash $ tox -e mypy Build documentation locally ------------------------------ The documentation is powered by Sphinx. A local copy of the documentation can be built with ``tox``: .. code-block:: bash $ tox -e docs pyproject-parser-0.9.1/LICENSE000066400000000000000000000020501444752763000160500ustar00rootroot00000000000000Copyright (c) 2021 Dominic Davis-Foster Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pyproject-parser-0.9.1/MANIFEST.in000066400000000000000000000002401444752763000166000ustar00rootroot00000000000000include __pkginfo__.py include LICENSE include requirements.txt prune **/__pycache__ recursive-include pyproject_parser *.pyi include pyproject_parser/py.typed pyproject-parser-0.9.1/README.rst000066400000000000000000000125401444752763000165370ustar00rootroot00000000000000################# pyproject-parser ################# .. start short_desc **Parser for 'pyproject.toml'** .. end short_desc .. start shields .. list-table:: :stub-columns: 1 :widths: 10 90 * - Docs - |docs| |docs_check| * - Tests - |actions_linux| |actions_windows| |actions_macos| |coveralls| * - PyPI - |pypi-version| |supported-versions| |supported-implementations| |wheel| * - Anaconda - |conda-version| |conda-platform| * - Activity - |commits-latest| |commits-since| |maintained| |pypi-downloads| * - QA - |codefactor| |actions_flake8| |actions_mypy| * - Other - |license| |language| |requires| .. |docs| image:: https://img.shields.io/readthedocs/pyproject-parser/latest?logo=read-the-docs :target: https://pyproject-parser.readthedocs.io/en/latest :alt: Documentation Build Status .. |docs_check| image:: https://github.com/repo-helper/pyproject-parser/workflows/Docs%20Check/badge.svg :target: https://github.com/repo-helper/pyproject-parser/actions?query=workflow%3A%22Docs+Check%22 :alt: Docs Check Status .. |actions_linux| image:: https://github.com/repo-helper/pyproject-parser/workflows/Linux/badge.svg :target: https://github.com/repo-helper/pyproject-parser/actions?query=workflow%3A%22Linux%22 :alt: Linux Test Status .. |actions_windows| image:: https://github.com/repo-helper/pyproject-parser/workflows/Windows/badge.svg :target: https://github.com/repo-helper/pyproject-parser/actions?query=workflow%3A%22Windows%22 :alt: Windows Test Status .. |actions_macos| image:: https://github.com/repo-helper/pyproject-parser/workflows/macOS/badge.svg :target: https://github.com/repo-helper/pyproject-parser/actions?query=workflow%3A%22macOS%22 :alt: macOS Test Status .. |actions_flake8| image:: https://github.com/repo-helper/pyproject-parser/workflows/Flake8/badge.svg :target: https://github.com/repo-helper/pyproject-parser/actions?query=workflow%3A%22Flake8%22 :alt: Flake8 Status .. |actions_mypy| image:: https://github.com/repo-helper/pyproject-parser/workflows/mypy/badge.svg :target: https://github.com/repo-helper/pyproject-parser/actions?query=workflow%3A%22mypy%22 :alt: mypy status .. |requires| image:: https://dependency-dash.repo-helper.uk/github/repo-helper/pyproject-parser/badge.svg :target: https://dependency-dash.repo-helper.uk/github/repo-helper/pyproject-parser/ :alt: Requirements Status .. |coveralls| image:: https://img.shields.io/coveralls/github/repo-helper/pyproject-parser/master?logo=coveralls :target: https://coveralls.io/github/repo-helper/pyproject-parser?branch=master :alt: Coverage .. |codefactor| image:: https://img.shields.io/codefactor/grade/github/repo-helper/pyproject-parser?logo=codefactor :target: https://www.codefactor.io/repository/github/repo-helper/pyproject-parser :alt: CodeFactor Grade .. |pypi-version| image:: https://img.shields.io/pypi/v/pyproject-parser :target: https://pypi.org/project/pyproject-parser/ :alt: PyPI - Package Version .. |supported-versions| image:: https://img.shields.io/pypi/pyversions/pyproject-parser?logo=python&logoColor=white :target: https://pypi.org/project/pyproject-parser/ :alt: PyPI - Supported Python Versions .. |supported-implementations| image:: https://img.shields.io/pypi/implementation/pyproject-parser :target: https://pypi.org/project/pyproject-parser/ :alt: PyPI - Supported Implementations .. |wheel| image:: https://img.shields.io/pypi/wheel/pyproject-parser :target: https://pypi.org/project/pyproject-parser/ :alt: PyPI - Wheel .. |conda-version| image:: https://img.shields.io/conda/v/conda-forge/pyproject-parser?logo=anaconda :target: https://anaconda.org/conda-forge/pyproject-parser :alt: Conda - Package Version .. |conda-platform| image:: https://img.shields.io/conda/pn/conda-forge/pyproject-parser?label=conda%7Cplatform :target: https://anaconda.org/conda-forge/pyproject-parser :alt: Conda - Platform .. |license| image:: https://img.shields.io/github/license/repo-helper/pyproject-parser :target: https://github.com/repo-helper/pyproject-parser/blob/master/LICENSE :alt: License .. |language| image:: https://img.shields.io/github/languages/top/repo-helper/pyproject-parser :alt: GitHub top language .. |commits-since| image:: https://img.shields.io/github/commits-since/repo-helper/pyproject-parser/v0.9.1 :target: https://github.com/repo-helper/pyproject-parser/pulse :alt: GitHub commits since tagged version .. |commits-latest| image:: https://img.shields.io/github/last-commit/repo-helper/pyproject-parser :target: https://github.com/repo-helper/pyproject-parser/commit/master :alt: GitHub last commit .. |maintained| image:: https://img.shields.io/maintenance/yes/2023 :alt: Maintenance .. |pypi-downloads| image:: https://img.shields.io/pypi/dm/pyproject-parser :target: https://pypi.org/project/pyproject-parser/ :alt: PyPI - Downloads .. end shields Installation -------------- .. start installation ``pyproject-parser`` can be installed from PyPI or Anaconda. To install with ``pip``: .. code-block:: bash $ python -m pip install pyproject-parser To install with ``conda``: .. code-block:: bash $ conda install -c conda-forge pyproject-parser .. end installation ``pyproject-parser`` also has an optional README validation feature, which checks the README will render correctly on PyPI. This requires that the ``readme`` extra is installed: .. code-block:: bash $ python -m pip install pyproject-parser[readme] pyproject-parser-0.9.1/__pkginfo__.py000066400000000000000000000005771444752763000176620ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. __all__ = ["extras_require"] extras_require = { "readme": ["docutils==0.16", "readme-renderer[md]>=27.0"], "cli": ["click>=7.1.2", "consolekit>=1.4.1", "sdjson>=0.3.1"], "all": [ "click>=7.1.2", "consolekit>=1.4.1", "docutils==0.16", "readme-renderer[md]>=27.0", "sdjson>=0.3.1" ] } pyproject-parser-0.9.1/doc-source/000077500000000000000000000000001444752763000171115ustar00rootroot00000000000000pyproject-parser-0.9.1/doc-source/404.rst000066400000000000000000000003271444752763000201540ustar00rootroot00000000000000:orphan: =============== 404 =============== We looked everywhere but we couldn't find that page! .. image:: not-found.png :align: center Try using the links in the sidebar to find what you are looking for. pyproject-parser-0.9.1/doc-source/Source.rst000066400000000000000000000026131444752763000211050ustar00rootroot00000000000000========================= Downloading source code ========================= The ``pyproject-parser`` source code is available on GitHub, and can be accessed from the following URL: https://github.com/repo-helper/pyproject-parser If you have ``git`` installed, you can clone the repository with the following command: .. prompt:: bash git clone https://github.com/repo-helper/pyproject-parser .. parsed-literal:: Cloning into 'pyproject-parser'... remote: Enumerating objects: 47, done. remote: Counting objects: 100% (47/47), done. remote: Compressing objects: 100% (41/41), done. remote: Total 173 (delta 16), reused 17 (delta 6), pack-reused 126 Receiving objects: 100% (173/173), 126.56 KiB | 678.00 KiB/s, done. Resolving deltas: 100% (66/66), done. | Alternatively, the code can be downloaded in a 'zip' file by clicking: | :guilabel:`Clone or download` --> :guilabel:`Download Zip` .. figure:: git_download.png :alt: Downloading a 'zip' file of the source code. Downloading a 'zip' file of the source code Building from source ----------------------- The recommended way to build ``pyproject-parser`` is to use `tox `_: .. prompt:: bash tox -e build The source and wheel distributions will be in the directory ``dist``. If you wish, you may also use `pep517.build `_ or another :pep:`517`-compatible build tool. pyproject-parser-0.9.1/doc-source/_static/000077500000000000000000000000001444752763000205375ustar00rootroot00000000000000pyproject-parser-0.9.1/doc-source/_static/style.css000066400000000000000000000002601444752763000224070ustar00rootroot00000000000000/* This file is managed by 'repo_helper'. Don't edit it directly. */ div.highlight { -moz-tab-size: 4; tab-size: 4; } .field-list dt, dl.simple dt { margin-top: 0.5rem; } pyproject-parser-0.9.1/doc-source/_templates/000077500000000000000000000000001444752763000212465ustar00rootroot00000000000000pyproject-parser-0.9.1/doc-source/_templates/base.html000066400000000000000000000003421444752763000230450ustar00rootroot00000000000000 {% extends "!base.html" %} {% block extrahead %} {% endblock %} pyproject-parser-0.9.1/doc-source/api/000077500000000000000000000000001444752763000176625ustar00rootroot00000000000000pyproject-parser-0.9.1/doc-source/api/classes.rst000066400000000000000000000003671444752763000220570ustar00rootroot00000000000000================================ :mod:`pyproject_parser.classes` ================================ .. autosummary-widths:: 1/2 .. automodule:: pyproject_parser.classes :exclude-members: __eq__,__ge__,__gt__,__le__,__lt__,__ne__,__str__,__repr__ pyproject-parser-0.9.1/doc-source/api/cli.rst000066400000000000000000000011661444752763000211670ustar00rootroot00000000000000============================== :mod:`pyproject_parser.cli` ============================== .. autosummary-widths:: 7/16 .. automodule:: pyproject_parser.cli :no-members: :autosummary-members: .. class:: ConfigTracebackHandler Bases: :class:`~consolekit.tracebacks.TracebackHandler` .. raw:: latex \begin{flushleft} :class:`consolekit.tracebacks.TracebackHandler` which handles :exc:`dom_toml.parser.BadConfigError`. .. raw:: latex \end{flushleft} .. autoattribute:: has_traceback_option .. autofunction:: pyproject_parser.cli.resolve_class .. autofunction:: pyproject_parser.cli.prettify_deprecation_warning pyproject-parser-0.9.1/doc-source/api/parsers.rst000066400000000000000000000003031444752763000220670ustar00rootroot00000000000000================================ :mod:`pyproject_parser.parsers` ================================ .. autosummary-widths:: 6/16 .. automodule:: pyproject_parser.parsers :member-order: bysource pyproject-parser-0.9.1/doc-source/api/pyproject-parser.rst000066400000000000000000000011451444752763000237260ustar00rootroot00000000000000======================= :mod:`pyproject_parser` ======================= .. py:module:: pyproject_parser Parser for ``pyproject.toml``. .. automodulesumm:: pyproject_parser :autosummary-sections: Classes .. autosummary-widths:: 1/3 .. latex:vspace:: -20px .. automodulesumm:: pyproject_parser :autosummary-sections: Data .. autoattrs:: pyproject_parser.PyProject :exclude-members: __ge__,__gt__,__le__,__lt__,__ne__ :no-autosummary: .. autoclass:: pyproject_parser.PyProjectTomlEncoder :exclude-members: __eq__,__ge__,__gt__,__le__,__lt__,__ne__,__str__,__repr__ .. autotypevar:: pyproject_parser._PP pyproject-parser-0.9.1/doc-source/api/type_hints.rst000066400000000000000000000002641444752763000226040ustar00rootroot00000000000000=================================== :mod:`pyproject_parser.type_hints` =================================== .. autosummary-widths:: 1/5 .. automodule:: pyproject_parser.type_hints pyproject-parser-0.9.1/doc-source/api/utils.rst000066400000000000000000000002411444752763000215510ustar00rootroot00000000000000============================== :mod:`pyproject_parser.utils` ============================== .. autosummary-widths:: 7/16 .. automodule:: pyproject_parser.utils pyproject-parser-0.9.1/doc-source/cli.rst000066400000000000000000000037611444752763000204210ustar00rootroot00000000000000======= CLI ======= In addition to the parsing library, ``pyproject-parser`` has a command-line interface for validating and reformatting ``pyproject.toml`` files. .. versionadded:: 0.2.0 .. extras-require:: cli :pyproject: :scope: CLI .. latex:vspace:: -9px Commands --------- .. latex:vspace:: -10px check ********* .. latex:vspace:: -5px .. latex:samepage:: .. click:: pyproject_parser.__main__:check :prog: pyproject-parser check :nested: none .. latex:vspace:: 15px The :option:`-P / --parser-class <-P>` and :option:`-E / --encoder-class <-E>` options must be in the form ``:``. or example, ``pyproject_parser:PyProject``, which corresponds to :class:`pyproject_parser.PyProject`. The ``module_name`` may be any valid Python module, including those containing ``.`` . .. latex:clearpage:: reformat ********* .. click:: pyproject_parser.__main__:reformat :prog: pyproject-parser reformat :nested: none info ********* .. versionadded:: 0.5.0 .. click:: pyproject_parser.__main__:info :prog: pyproject-parser info :nested: none .. latex:vspace:: 20px :bold-title:`Example Usage:` .. code-block:: bash # Print the readme text echo -e $(python3 -m pyproject_parser info project.readme.text -r | tr -d '"') # Print the license filename python3 -m pyproject_parser info project.license.file # Get one of the project's URLs python3 -m pyproject_parser info project.urls."Source Code" # Install the build-system requirements with pip pip install $(python3 -m pyproject_parser info build-system.requires | jq -r 'join(" ")') # Dump one of the tool sub-tables python3 -m pyproject_parser info tool.dependency-dash As a ``pre-commit`` hook ---------------------------- ``pyproject-parser`` can also be used as a `pre-commit `_ hook. To do so, add the following to your `.pre-commit-config.yaml `_ file: .. pre-commit:: :rev: 0.9.1 :hooks: check-pyproject,reformat-pyproject pyproject-parser-0.9.1/doc-source/conf.py000066400000000000000000000052751444752763000204210ustar00rootroot00000000000000#!/usr/bin/env python3 # This file is managed by 'repo_helper'. Don't edit it directly. # stdlib import os import re import sys # 3rd party from sphinx_pyproject import SphinxConfig sys.path.append('.') config = SphinxConfig(globalns=globals()) project = config["project"] author = config["author"] documentation_summary = config.description github_url = "https://github.com/{github_username}/{github_repository}".format_map(config) rst_prolog = f""".. |pkgname| replace:: pyproject-parser .. |pkgname2| replace:: ``pyproject-parser`` .. |browse_github| replace:: `Browse the GitHub Repository <{github_url}>`__ """ slug = re.sub(r'\W+', '-', project.lower()) release = version = config.version sphinx_builder = os.environ.get("SPHINX_BUILDER", "html").lower() todo_include_todos = int(os.environ.get("SHOW_TODOS", 0)) and sphinx_builder != "latex" intersphinx_mapping = { "python": ("https://docs.python.org/3/", None), "sphinx": ("https://www.sphinx-doc.org/en/stable/", None), "consolekit": ("https://consolekit.readthedocs.io/en/latest/", None), } html_theme_options = { "light_css_variables": { "toc-title-font-size": "12pt", "toc-font-size": "12pt", "admonition-font-size": "12pt", }, "dark_css_variables": { "toc-title-font-size": "12pt", "toc-font-size": "12pt", "admonition-font-size": "12pt", }, } html_context = {} htmlhelp_basename = slug latex_documents = [("index", f'{slug}.tex', project, author, "manual")] man_pages = [("index", slug, project, [author], 1)] texinfo_documents = [("index", slug, project, author, slug, project, "Miscellaneous")] toctree_plus_types = set(config["toctree_plus_types"]) autodoc_default_options = { "members": None, # Include all members (methods). "special-members": None, "autosummary": None, "show-inheritance": None, "exclude-members": ','.join(config["autodoc_exclude_members"]), } latex_elements = { "printindex": "\\begin{flushleft}\n\\printindex\n\\end{flushleft}", "tableofcontents": "\\pdfbookmark[0]{\\contentsname}{toc}\\sphinxtableofcontents", } def setup(app): # 3rd party from sphinx_toolbox.latex import better_header_layout app.connect("config-inited", lambda app, config: better_header_layout(config)) nitpicky = True toml_spec_version = "0.5.0" needspace_amount = r"5\baselineskip" ignore_missing_xrefs = ["^toml\\.(encoder\\.)?TomlEncoder$"] # 3rd party import pyproject_parser.type_hints import pyproject_parser.utils pyproject_parser.utils.__dict__["ContentTypes"] = pyproject_parser.type_hints.ContentTypes pyproject_parser.classes.__dict__["ContentTypes"] = pyproject_parser.type_hints.ContentTypes pyproject_parser.classes.__dict__["ReadmeDict"] = pyproject_parser.type_hints.ReadmeDict pyproject-parser-0.9.1/doc-source/docutils.conf000066400000000000000000000000501444752763000216010ustar00rootroot00000000000000[restructuredtext parser] tab_width = 4 pyproject-parser-0.9.1/doc-source/git_download.png000066400000000000000000000642761444752763000223100ustar00rootroot00000000000000‰PNG  IHDRͺnΖv­gAMA± όa cHRMz&€„ϊ€θu0κ`:˜pœΊQ<bKGD ½§“gΉIDATxΪν½]TΧώΦςΞΛ^φζ~ύŽv%k€τ›R~"E ‹Cι‚ώω[Κ)΄‘‚j#TΠx‚΄X©x΄Τφ¨ˆ΅ˆVJ‘RZε`-΄Š¨€€–„’π Ι€Ψω_L2™ΟOΧUη__4uρ˜ͺΰ΅Η6¬=Άam+އ ‹μ’vlΓΪc¦¦ηΪ#θ’ωΧΔ’Vrv±2γ&ώ² ά­>ΟO.ϊ§έΏφ˜ΙτkŠC֜°ΔΪ‘Δ"»€­)6· ŽYZ ΝΫ­ΖΩ9Δʌ\ύeΈ[}žŸξυŸ`ΖύkN„¬΅―BˆΕ »ςΆζ„₯°ζ?Π?ϊ‡ώ‹£΅G6¬9ΚrW!kO†²γybΡΒ¦°­ύΚXstΓΪ# τύ#§χΔ<μΟvΧ–xώλ0b‘‚]Ψ-Σ€τOΞ!Vf΄άΒΥ_N»Υηωι^G7¬=ΆaMqΘΪχ?_†@όΙaiΠλŽmXstaϊoΉέj η+3šoακ/§ΐέκσόδ€’ ¦‘Έ±Zτψτ€ώ‘ΔβιτXͺϊ?ΆPύί1³ˆ•Ν·pυ—Sΰnυy~Bˆ•«ζΫwτδ,beFσ-\ύeu½p·ϊ:?‘Δ Φ­;zγ,beFσM\ύeu½p·ϊ:?έ?ψw,dνρ΅'BΧ~ϊόΙΠηΏ†„O.Ύ{ώ$ύ>€Π΅ΗCΦ y‚ϊΌρ]~ϊ;Q/Ώ$β Χ½ςZό?$‡ΎoE΅Β9¦~―ήΊŽΟπ_Ϊ}Ι8«oΞ…' x‚Wv鍳zγθχΙ"‚' „ΫΎŸ\*ϊΏ]EπOψυ΅n3%žHζΊ·Ε=AK(OΕπOπ²μϊΌ‹§{N_/‡‘ψέ¨ή8u~«ˆΰ ή¦2Ν"f²η)YδςΏΈϊσ­«•†ϋ4³³δό΄z²£YU–ρc4kƒθ υ,EQΤ|cΖι§ΧXηŽΠ+,4™O.VV/φΙ.ύόΌλ ‘£Ϋxέ벟ϋΏ*ώn Α;Kέt=s½ι“στ'šσ)t†ΌSφΐZ{ηeρ―‡£kCώϊ¨ΧίϊθΌfv†Υγt¦OΞρΙκΏλxς›―Ώρζλ[ΛnΛΟηnυ₯u/ΏŸ"+»q­ήŸξN|+κε—D~λ£b’?Μύξ–u{ΒM٘1ΞΞ \B²ιυυλόBίόGφ©ΛΝ.Μ΄xΧbκΑΥ²]ΙoΎ²~_ΈξεΧβeeΧž”ώE)βΦγzkx’Ιή₯d‘Λβθ?τziκκšQΑ|.ͺ­TΞSEQ†‹οvζ€ψχn]lΠτifgu†ΎVυ―§Ύ/bM(dtwΛΗΊεΓ'ή*ΟάΧέ"Ÿž&g‡εκŸΨzΎ$μoηrŽ«ΪδΣΣδμ˜RΣRu+'ԍφ\7~έx™½ψξΩMmκΩξγ?:ωΘ<¨j“¦uΣέΝέGή½ΰЈ.Ώ‡>–%[2« ¦OθOΞξύ…ΞΙαΒ@NyβEV‡=_QβΒpŸΪ0KΞOk¦»›»Όw.Βσ“]²ϊΏygΖ@zͺγoΠ½ό ™ :λ?iΟgE½ϊχWB7ο g ͺ㱴ώ·ν’l x‚΅―•œ1Γu½*΄ύσ‹=~{Β΄«ϋ§7ωُίVΩOΞ΄eοXώΪΑΫφi{tΪ„Q―‡ZΟbΌDΕ}:ο™Dψχ]Ν䌁œι9ώΊi›ΏηŸ4D+δυ#6e{ ΌM%ύ€E9¦ν΅–κo‚SΎyM-·½ψKZ«?΅²τo g wsCι«ΆρυυV ζΏ–y„tx‚3ύΏ9œTzykε}—²AΞΘ™ŽΚD›#ίΰg2Σ5»3Z¬kρΰ‡m/»N§§ΧΛ›»•Όžg–nΞΙ66§ΖιΦπ0“½KΙ"—›‹‘ψ}cτ·Ηͺj#lϊ iέ}κιaυtΫΎ*GN*ίιpΚ@ΣWj£αω>ω΄υ–³εζvΖ₯ŸLν :Ν‰7œ΅ΈwΆρ[}z}Žυ_UΠ`°Ϊ iΣΩΡέα"ξφΡnφέ₯>!7m2|ŠώδΒOjŠ’(J~k·<ρ"«ΓB―·ιμ·0Xς™ΣΙ>mϊŸψΙΤ£]/½μΆ‘Λά₯^ί*έ•wόη~rfβ7S/9tΫρ†Ξϋ=wΟηΕΣΗ«wg δΜΔΕτ—θ Rχ}wρrΓΕΩFzΌαυ―T3νyΩΖWΧ›*&ΏΠ¨Χ·ž½οvŸ.Ά~Σ―*+Ώ;žώšiŸ1_©f i1¦·ϋaί\%ύγτΰŒΌ:ήͺ5` ¦―ΏNΞΰN9ŸγΠ?O@Ό΄1σ«ŸΨ9lN°mu^bκ…ΏόN~Yέ΅Ÿ8žb2(ρ»Aeƒœ1¨Jή1}ε•­ΗΛ~ψ©DΟ4œ™iΕΝ]τλSΏ¨Ήy»ω·²‚Tϊ/gύ6φτ/x=«€δτYvT6ΊΏ‹[Γ«Lφ<%‹\ώC§ΝSΓΕw=‘ŽNSM›$4\ΉλRζ{—OόbςΩlΓΥ7­4LQ”‘νΫζ‚Œ«'.Lš4ΦΪ|IΨσ§wšϊΚӍϋj7…žKMλ蠝€Ό›κ0ž—“χ]όΆγΔ{εš>{†M_TχΨuyοžΞΖ f#r8ά…Ÿ4¬3ϊΫεF’Y¬vύΝηKΒB―w°ZςΔ‹¬>sώ`Ίεψ՜wksφtvΣɐ›φΙεd—Όώ§ €gΡSBρo”tΉΩXUdͺGΦmώaω|΄ζΓLΊΦόaηazD!4šœξωiΧΦm›·nΫScώVΗρWy‚'Κ»I2τ]*]ϋlαΆO§i‹ΪΧͺ3%Μμ€—eΧ8νsβ7ZEό­G δ΅ΌΏ<ΑΛλ7<Α YΏt?Ρ2[/½8α(Μ‡‹όJeϊ€9?Θ¬œi9mО3Wη&Ό8G7q£εφ΄ηߺſ՟ZM‰'’+‡ δ΄αξ“ώΧ₯ט·μ―L²lu‚Σ#¬Ώ29Φq<’ήmlI—²aޞ{Φ\>u³6¬+ϋg\‹‘ΚDΣm’­ίTδ~*ψh—μ£]_]ς<Ϋ§ €7w«ΉX: σέd}8άήe²η)YάςΏ(ϊ7ΟώRcGB=Ω‚Ίέ0]ωΣM―2w|5τήΥυΡ4΄π·Z“,iεό­ώWIcΜS›Ž™χlŸ ώ;ύΛε7ž‘ω[€¦Π|ξοuY‘ΛαNο€ΥKφεό­$쭎aŠ’4†1Š’tέ;Va’·iΕ}žx“Υηφ~ΫΧψK_γ©ϊ7™ ”EQ”Ίγ}'k_Ά–Ί.o_Ά–.ύΗrΦΏιfΆρ‡θεΧ6FΎaŠ ΊKΗ‹Ί‡ΩRΧΥϊΫΉΣΗχdm‹1B:Σ?η}Ϊ₯ν%ιE»S{!λ·}š«§Πόk†Α―ίΌ G> ⠈7ŽίΈ»ο5Sγ`ΘΚρό—†ώ_ϊΥϊΡύd^ΪύuΏ]uoΞσιΣ1ψ΅©}hΉRΞΛΖ¨y &ξτ }VϋJΕ΅]–±ρuA±ξωκ§ί:΄Σ^‰Ιθίω­α]&/P>/‹«‡ϊ7KykϋιAσDψ―οYΟΑΏgwD]wζσ%ao˜FΞg5“ές1S¨MαŽ=ημ’αΡq].cΊιtέf&#r:œ9g¦+ί*ysΟ0EQcU·~RS5v"βtκρISγ@Δ-OΌΚj“ΡCά™qύΔ©ξΖVσΜ­N'λI ΐ½ϋ—„ώG~2uΡΦτGύΏ$ύ[-@/˜6θΊ~ψ(†=³(ρ]κŸΓ>9€ΝΊŽγ²Ο^Σπ~όΧΏ₯Ώ$ „©ηz~J a깎Κ8‘€ΰ‰βΎτ‰rΝ½'Λ­ΐΥύKCdη‰xϊζηΏ‘kΫ…gγθ±8aκΉ‡Nυ―5Οα±λŽΑM7›:υ€ΚΤ₯₯™«‰Αšm/ΈΤΏΫ}z‘nϋΌIKξ…υžΰε¬kSzέΕ¬uΜ""4ίi]ο‘r³rSzγ”^cώή?7ξΕ9Ί‰λ-·ΌΊϊζΛρBDjFήΑOςrΣb7˜žzΎ}¬ƒή²}eεΫ;OψcUI†ω‘ΎH‡'¨Χό(6―όO>xαζ―5E›Ν+γΚΥSœΚ†β˜ι(’W%Eηj.~·ιζrό«ρOΊζΎ»ο\ΓΝφŽφΖ_ΜϊιΓǍ^δΌww+KΊ“NΆ±Ή n ―2Ωσ”,nωΏΡr{q^ϋσώ)σΚsΚΠύKwεΑ['Nu·¨Νγ©;3ζp9zŸy9ΊΊ,£φύwλ-ΛΡ=θfVώχΤn <·ι­Λe­τJ:Ν‘‡+½;£‘}¦§ιf•ͺυ9{:Ϊ¬όwΈ0Fφk£yΌ27ΌΥ?‡40Η¨O€ΥΎξε2ζ?“ώ9¬›€gξ_*ϊΧ§Ί/fD8|ι―(H|Ά}άΉbυΖ)½QU#΅~—XŸϊuΗδ”ή8Υ}ΞΤGdfL#’L•iΦ―Zλϊšΰ ψo΅»έ§ϊηΆΟΑͺχΝΟϋEξ°2Α[—洦σ\9Ÿγβθ_ol2U»Ά!ό{ZΉω\ύΏ΄αeλ4Ώ[Τ8ξ°Ί7Nυ9,W’ ɏ¦kδΎlhο”ΔΩ$ο₯u|ίκίύ΅P(q8W-Š9Υ/wύ{“Ι ΥΏ―ΛbιΏ$μωs9U“_ϊ;«ξ+Œ8νμat‡ίšU«φ†z Ί°ΐϊ‹φΟύS†–=UΞ^QμΝqŽx€ΩfO³Σ€νsnφ|I˜ΘςΌ_ίΑ*ϊqJ΅ωέΙL§ά+ύsHΓΉ‚λΏλ&ϋ4¦}ξόχ“]Κώ]oΉ­Σ½ŒqeύιάΝ±ω%!\±1NΌοd“š΅₯©·ϋΊ¦γΗρΫ―†γ Χ½ρφζΌs}–Ώͺ›Jbώ‚PτrΔΫ›χ_TŒίόό¨W#’^•V›6SΧζmzυ%­6ϋ΄Giλ.ωΝuΗtκτF]ίΩ8Sυ'­§sζβf‘i䛇Ξ3₯Σ',εθτF^Saώ*Ζ9§‡s\kΉεύΥ¨¨ήa\lTΠK"ΎpCΠoΏϋ―³υ}“¬mΪsBM#ω΅ %iοl|ω%ΡΛooΞ»¨gΆqp‚Ίqeνι»±Q/Ώ$⯏Š|ηΓOj½GeΓ¨ιΎψ‰xΣ«λΧ½°>*NRRη\ΪQ―FDΕΉω'^ ucωΑΝ±QAλΧρy’ΦύΥw>όΌF‘φ6Ο½»[Ωu²Ν©qΎ5<ΜdΟS²ΈεϊβιΏ$μωΣρο^ιΝ°fv–šŸVOv7χUξΉπ¦›7ўΆΌŠŽ4τΙ‡=^ΏιoίΎη\Ο—„ύ­jο©Ύετ49?­™μψ₯£ΰ­r7©υψΈΞZηrŽχ΅) Σ:C_³κΘ»?\λSNφ5ίzŸσαhC6Sζ%ύtώœΞ1½SaΎeΧΩ°ιŸCDŽ\Π λζg5“ΏάΝ =ύώΑα>εdŸRs1­ά““]ΊϊoΎ­›1"Vf\kΎ΅˜ϋ·θΏΉν‹ΐέκσόΔώ"Vξώ’Bώ‘θϊ‡ώΠ?ϊ‡ώ‘θϊG<νϊΏΦ|{rƈX™q­ωΦbξί’fδΆoξVη'τXΙϊΏ59c@¬ΜΈΦ|™°¬ξVη'τXΩϊŸ6 Vf\kΊ‰LXNΧ w«―σΣ­ώΧΫ°ΆhÚ!kO„¬=ΊφλPHρ€bνΧ‘kO†=²ζDΘΪ’ kŽm€ώΠ?τ€ώΠΏ'ϊolΎ51m@¬ΜhlΊ‰LXNΧ w«―σϊG@θύCvϊ? ύ#–ͺώ.X£“Σ¨ τΏ’btrš‹ώ‰ΓΑkmXSlΦ?Zˆ'λ~ZΕ!km /H·Ϋ~οA]ύ# }ƒ#·Ϋ~w]uώί¬ΘωdύΪcΦY{"dνW–Ο†@,R°Kš©μ}²φDΘΪγ!kmψŸOΦί¬Hου―iΉsΠ?ϊ_Q]–;wϋ‡Ί:?ώμ―)K|ΌφxˆiόŸi˜γybΡΒ¦°Ρξ_s"dνρβ‹ΰΏ¦όοΗ՟y―Š’δ]=-wξφiΠ€ώΠS/ώώ!M˝»έ^ߟπΧ”%ςΧ―9b^`_…"‹vεmΝ‰5G6ωλšςΏ―οw[€βv υπΘνΆί›o!<Š«M7ΔςŠ[­χϊ‡ΰ̞ͺ‚νώšψ"αQό?οϊyχ,ΨΎ!―ΊKϋυ/X cllllll!{πT^Π?”ΰ »Ώ§GΩΣ£\` ΐ£¦τΏΈΦ 8gtttll|llΌ»»§»»gttΤΩ–Ύm@Ύ?J0οάO‹†K ΐ»fτοcρsΉ*σ€#΄Z-γ~›€V«΅ί~Mθ‘ξ‡ιψάύ\ZάΫn[Π‚Δo%.Ρh4cccΞ466v~χύϋݍΖΕNΈ4\4 Nξwm}‡f°cdddttΜ΅†FGΗΊΊξwuέ±ω:—¦€ΫFτο±ϋ]XίαežΜ<|ψptt”‹ŒFGG;;»:;»>|HΧ‘e\΄\· ξw(~·²'’$IrxxX«ε$­vT‘θT(:‡‡‡I’΄WŒ³¦€ΓFτΟΥύ;ύφβw,ϋΩYrvΦH’@IrΘCχ3-Ή’Sθ6šεΒRƒ¦€}#ΐα0τοXφξw!~ζ:°―΄ΑhdΐJfhhH«Υzη&­VΫ!WtΘƒCC£‘-¦)ΰΆ`ί€ώέtύΩξ·ΏυΩ¦7ΈD`%177·C=zτˆ-“hhι8jΨ4Ξ@žΉŸδδδππC΅zP©T!ρdC­~899iΣpΦ€ώϋ»pππΓήή““:/~d X ‘MNκz{ŒŒŒpi@»hhψαΓˆΐRγργΗΓΓΉ΄ ϊwκώΩΩΙΙΙΎΎ~Έΐ’m1 ύX&ϊΧMMMMM9‡ώqω‡ώ,/ύ;‡ώ=ΠΏvlϊ°,τ?©ΣAžλ_o˜šΡ³'ώGΗΖ‘ΛH:Ž=ύΟ<ώ·rυομ©?gλώθ‘θΐrΏέτ?τοFμuΜΔΏft ϊ°,τ?19ΙL³WA\—ύ3Π?€e€fϊί~ρ?τοFμuΠ?€ε¨zυτΟISΠ?€§HFθϊύCnτO?υύX^ϊgžύƒώ½Χˆvϊ°,τ?>1ύ/XγΠ?€ε§ ώ{‘Λ+ ΛYz½ϊ‡ώ@Π?τϊ‡ώ‘xβ[ϊV§ΚΧΥΝ:ψ[η೩ςηΚ ‹wταΞρ­‡U|iηκ΄Ξgs”‘G†OυΟ{±ΩΔ΅ΎU©ςΠˏγήΰ3©rώχFθϊπΔυ?Ή\Ή:UΎJZ<Έυ›Α·¨žK“―Jν\χ½ΑθαfΠ?τΐR‚$IΉ GΗνz.UΎϊΣ‘zΦ§“Σ[s«R{€ύžmύC\jdυ•ςΒμ”Ψπ @?‘Θ/(:*Q,Ι-¬lV;¨¨›e<ΑΒ)<ݞ’(ЬM:ΩF™Ÿ.+ͺQθμ’«“WοMOφ1„ΕnL”HςΟ\’λΰύSu£Ό{UjΧΦN»τά|6Uώb-ιΡfΠ?τΊΌAN+ύ€ΔΒ+θί^,ϋƝ€ώmŠMΚ™Λ©“=εβWΫFIk{0$–₯ώέΈ<τZ^χ³iŠΥ;Ί_Σ΅Wʊ ρŠa`@d‚8Ώ’Iγϊ<΅•‰Μr3Ι%‘>ύ³,Ÿ“υb³ήψ‰ΥGηλxΜ<ζdΓόΡ΅—εŠ£Μ§&Ξη6Ι½LυOQ‡’™{•Σ§κ΄ϋλ΄GzV§u'έ›χh3 ώCŽι)Ž΅Φu Ϋ–’ά“*Ž%N】ϊςH‡bUͺ|•D•Ρ9OQΥ?ΒO•?wd¬ΊuΚ6ξ‡ŒόOΖHΟO9r2)Ν‘―ΚiεΎτύ;AwήR)‡Θš=—v¨uYb Λ΅]ρεjGϊwό”Z‹%•gβ-kD~aΡαa|V ΰ@›su( ΓmŒNQdƒŒ5λlYΣ§)gΪ ΡEJΗη;Π\]V^Ϙ2LRT^QVg~ΪΝ²½€:[ΧNrΦ?‡X:ϊΧ–%Ϊ–+My2λJ…ΔKOV6΄xυ¬_vΞ'΄ϋ­Ζ]ΗΎ*ΝΫYήώΟώ{©žέ,8uΆ‚žΠhG‘4ΰΚαηR嫨UJU„^{DQ₯Ÿmν7²&έχvjC%ζ±}ΣΰΏΝNζ{•†ΔL2€ςU;ΤΥZΆδώ<ωͺ4uυχΝ θί1κˌlB™Ζ“―:ΏFb±iΈ¨A₯£(J§ΎRΜzβΛ_ΒL1Xλ?pcnυΉz@­ΈR*±LIXΖκuηΣΝm aμήf³ͺΫN2Γ|› xk«ν5wΑωι΅:»1j‚'π“6 Ϋ.ešVΠκͺΉΓeξ_@πDα™gΘUjΕ₯C –,bvώτθŸΤ©ΫΟη³ΞΡrΉU•ι|»dϋ…ΕΖ§Λφ–Φ6)u\ζCj~§ΗόΩ‚§?΄YχΧΫgΉ ϊΩ?6m=z’Fž`ΘΘ‘―J특f΅΄~Έsx½Ά’žz6UώlΙτ„₯?³UΚτΏ:¬X•Ϊ“ΑšnU»XϊΧϊ}ΟͺTω³‡ΗnLZ΅ZλϊžI•?sdrΒ“Ν θŸςv蘻ώYσλμtΫΡ`QR•Ξ^FΦρu—ΣσbKθΎΊšQŽΝκ°ΛΞ£(₯˜l’šg%‚ςš(Κ2"νΔ6™β@“*σsk ΠΏΥμ&—Υ*„e«Χώε³G8ȞΊBqLίΩHO˜ψ@ƒΪu–ΠΣω5?[¦BZn·9[ωΟ4zϋθ1<WjC%ςU©ςg€ͺ׎¨ί.ξ ΝλZ*_•Φ“ΤjκœqžbUj'π`Ζχ#%ύ뀊U©]―]3Οh'^Ϋ!_•Ϊ΅ξΘPΖχ#[(Ÿuωΰ5gόό`ΧͺTωͺ΄φ½]<ψφ‘ΎuRΕͺTωκœΑκIΟ6ƒώ‘·½δχώ³ύ­—Λ±aΝ²ϋIντ(ipΦ\ˆ5 Ώ[κEQŠΚͺj&Κς-λΖ]Ό’¬c'θ}ΆΠ 槜)KΡO©UκΨI ”Τ‘Τ‚υΟ4whš€–‡β8럟x²©Νϊ ½ΆφΦfφͺ‚%§~La«£$uͺ¦šŠ’ά¬€ΈθΫύˆ6+œε mρ€­φ ΊsO―όgZμVΒΏ³ωNθΙκοΥ‘Ÿφ<'Q¬–tρσΔ”[wΈ)jPτMί‹g«wtσ~~oΦJͺzCΡ7}λrΊV§u>›£ŠqωΪzAqO›dϊ-Ε3;z^<؟qΝ0μωfΠ?τοφάΏgΟ[;Π‘:‰5«mχ…‹TRͺuΆϊΙΆ^y0Pj«]•˜cwΣωι2γ’€₯<ΕPA2Έ>]9Σά‰c?©o~/D°Ώ£S€ώ’’5υ#Œ–5;ψ-ΐ+ΜΒLφ‹¬‘ηοιŸιτΣƒό6«όhύΏαΏP5ύCEQdsϋ~‘bΩ‘3•uWjͺΛeE±ί±Yh™Νuϋΰ_˜Έ€YM?ψΧTjυΰίyώqΠ?ϋύΔώβΛοΓj―δΗς-ζpmT–‡¬Η3,+ςΎœΞ­ώ# { η₯¬5?‚υަˆψ܊KΝν=jυ€RΡΪP} =šΟαyHzξΤΩ g]:؏όY.όν6ΜύύCVUsGi²ŸΫiu«W΅;Φ›ŠΓkT–Ÿgϊ§tV-Q@dlTdˆŸυοΕΉ{|Ό’ΙN‘(©ΖόbU2ίΑ;ܜ―ε]ΆΌΐ¨τΎšΏšΏ¦|Τ¨œ»Ϊ3{΅›lθ&ξ/wκ/+f~SLΧΛ§κ:tΏά›¨jT‘TXβ(•*7ϊΧ‘Τ ŒIγtLOληΗυσc3F§η΄S³9’#G&ΗυΓc3C£ΣjνΤ€FΧχp’]ύ€ώ‘`‰Ρrϊ‡ώ¬0jn@Π?€FU#τύ€ώ‘θτύC θϊύCΠ?θϊ θ€ώ‘h+ζ ˆΔ £?κjΔOΫς'βRqž8.:ΐ_ΔG&Δ§”΅i—WΉο8Mπ"φΆ-φqΤ%qΒ?λΚΣXw,(uν•ω’ψ˜θί?"<.Yœ_έͺ3U^Μay­ŽΏL^Κ $x’ψr-EQšͺd‚' ό%ηήΝy<Ÿ΄εi­Α¬’Όp{ΓDXAΗ’Φ~Ίκ$ž€° ΎHxLrviΛΙϊr„Οπ3‘θϊw¦‘`£ΏωŽ ŠŽŠ‹ <Α§œ±Tί”Ά,Q@πKΥΠςΤΏϋ+θujς’όι"»1Eœ”˜N—"a΄¬nG*D œξœl”ψ ar™†²θŸ'ΘlΤ=ύ?Ιύ;ΠΏ0$*19ή Qa!|ΊΦŠ)μ ‘θϊχ΄ΓΦ ζ ^H‘ϊ s‘šζŠμHΑψ₯T8«Ϋ ‚y‚πC θ±πq»Ώ‚^ζ‘^μ/ xβrΛΦΊŽͺ¬`Ί―cv.Ξow`™Oΐ7ίŒώ ^„¬™|Κυο««πtλ?ΘΑΈ‘N^‘$ x’ψ*-τύCΥڍ’ Α ‘Τiώ5;L@πB²Lυ―N­θ+t‹$'θ±υοώ z—‡Ί*1Ÿ'6Ϊ‰šlʍ x’€*EQ”²0œ' Β Zν7“†0#ŒώΓ“x"’Υ·ϋστo—WΠΣ?EQ=Ε±ΫχΠ?τύsa 4ΟdΦ;«β45Yα‘Ρ΅9r’(R#oi’{ΉζϊRβ‘»υQ;ΚΝ…ρq ’*ϊOͺ’‡γ-² ΛΘ?£₯ν—2Cž(ͺXυηλΟϊ_€ώIΪχιυ$τύ?₯ϊ'š+d)±ΑAτΌδμΖΫΩSS IŒ φρύ#’Rςʚ΅†7=¨eX΅’2…½ G$#]θJ±,).:@(ς ‹O·MXΗ‘h‚—\©V•₯‡Ξσ„’(ŠT_:”•α'„Ε&εV΄jμ“§λ©)HŠ ρŠό‚’γ₯VΫpΝ(W²ΣΏύ@œˆΰ…$U©HΞg}%SDπJ¬Ω”Bπb‹”sX[™("xΡδ–€œO<?½Φ€“†›sΥνδœ‡ΦΝΔς‚'ΰǝμ έ!Ί―f;ώί–Μωgι_Miͺ“θ5jτο.o9έS6¦Ki™†¨+³sΖ¦νL**σ%#Cόό#’R²ŠΤd„Ο ”48bXΨUπθ–χΈ΄p8„I­EYnrxˆ/ ΛΚΫu–ώJc vγ ϊ‡ώŸ2ύΤHxB±1E"IG‰ž ³žυ]uefŸ' ό£7¦H’£„‚_ͺp^!Σ‹­².‘^θŸμi¨(ΙMφγ ό JΚ+8›!Υ΅ˆ €Ζ§H’β"όθ₯UjλέƊΣ#aHT’$»΄t2U±7&ΰ όΒ’%ρ1!O@„e]°χŸ’ΰ' ‰JΙΚ–J6†‰ž€w¦Η£Œrs kύ›έ/f»ŸΓY»S§(O°šŸ&³ιUœA²&2ΖΓtΝέ^AnyhοZ±iέ_Bφ‘κ+r­Σ2₯>e7ώߚΑω·?E ”'σyΏ”ZOυοφžr€Ω!qp€δ@Uύ•†ϊi¬ŸMΞhκM+f"’ΕΓ ^ΰΖΈΒ©ώr<Ύε=--‘Ψ& ‚β#E„0$*Qν' δtΉδ‘γΛ#?#Ž ΄tdύ#6¦ηU5φθMX9)DZόΫ蟒TE1"‚"©ΣωNξο)Gϊa²+¬ga*SDVYHπγ‹™Ύ―)?–Ο8ΧΏχWΑ›[ήΓΒαнa‚' "eL+άΌ(©JλKύ C’Y•RbςΖΘ?ž€&)ic2θϊͺτOΦK„"Hv…e²½΅MaΆ‹β@€€πΟΊd]Υ’ ²' ­)Š’ΘΪ$‘€Κkr4˜fώ²&οτO?Νe—0z˜ι…Π•›%ΦΊκ$‘€ˆ,΄še$[φΖD‡§Tτ°φc“žŽό‚"kγœQξdΦΏ†v`RΉŠτό¬}£ζŒH¦{{ Tΐˆξα™{Ϋυο2]@j䍕ΕyβΈh?¦π#’Š­†‚ιώ₯₯XΚ Γ­GώντO‘mα<–gκ€.\ξο)Ηϊ·)₯t7]ΦL'Ί"^( bNZ “ν²0/υΏΠ’ΌΠΒε΄ώ-«ƒΩΫqΞgρ|ρά?~‘Yerϊ‡ώŸΦήΏφRf=²*9Tq©M₯±ι$k*βy"LRT^QƎbI0O@0}»Αl!WΊš¦SΕ,@ΚΒpφͺλωE~f=«Χ’έμ2»šeξΦBvŠ&x6ϋ1UΠmœ3ΚύΤ%qB½1Ζτv„¨b…gν#ύS­ΉŒtηΣEDP^“V,πSjuζ=0§ΓM.σ#€Ά£‘ΊHšΐ<‘•5ΥYγ=Ε±6#φϊgTΒσΫIίτώέέSŽυo6½₯IΗϊ°!ΛΟΡΕj’†x₯—δ–N‡Pμ ώYWls―E$ όeWsπŸΤ©›JΕ<”Χ„ηώ‘§vι©ΎRœ΅Ρτ6! O”•4›gX•'£œ·Ž·Αι₯@’Cξ¦ΫΨKύ·ε;T©ϊΜFV%Εe3½Ύ7ΌXΕmi‚“J“CFq8=|- „±{«Ξ$ω aB‰γ³φ•ώΙfYO~HEQ-ΩAt³C[–( όeWHZLηŒ›ώ]ζ‘£‚ΤZW{ΎAαP9¦KX 2iΛE棨ŠblGώ韒tυ’ !Œ-’ϋhιŸλ{Κ±ώms†­]˜οh‹vΉηϊ_hI^hiαtσ?ϋnC€€Jœ+ςΩ?Ίcετύ/ύ+O†σμ† ­Hνλ}²ύRΥIYz,½”L £Ϋι]6ωa^TUν΄² οR―υ/wΩNgχώέ=€Π σs5ͺΙ­δ’QξDλ?zo³ŽΙC~bΕ€‡gνHQδ₯L‘§ϊ§Θz‰Ώ€ˆ«†σt7Ί§8–ΰEμmΣV&ŠK-Ό8ϊ§Ϋˆφ]@Φ_yVƒLšςd>OœίNͺΟl΄ωw¬ŠΤˆMKνΌΡΏ]ήΊΎ§<Χ?Y—ΕwΨϋΟ ρ½ώ½½ε=(-œα²χo7±θeνηrε?½nΤς\ τύ/ύΣ3ΔΌΨ"λέTEq"‚(‘Λ΄Ί±δPaQΚΊS•`)εν²0d;KG)λKŠOVΆ9ΏΝ―ύa―Hg₯PQ'ZΨά½ΔίAΒθYπ(«Ήwϊ§+#›)yJQ™ΧΔU]2ΚύlόS•Π+˜…NάΞϊнLσΔκŸΕŠ‹Š^΄ι»mySΜA.σΦύ=ε±ώM£;ΆJ£_uμkύ{}Λ{PZΈΒ4χ/±Ύττά?»ΰy_ϋΉΣΟ‘h‚'0½W ϊ‡ώ—Ι/ώ‘­ωΡO@DfYύΈŽ¦½,3šΟ‘­€©Z‰βΩή‡₯¦ώ«j°Zjn^”±·ΝΥƒ}ΜKγΥ³iλδΥΩ1„0$ΐߍώ]v”ι·Ή Β₯υLη˜Y_¦¦<Π?₯­L $xYλΘθg·όλuœΥΕ!£άΘξΉωΙ(‘€b~Ÿ†ΣY7ε†<ΡΖR¦hιZ%πyτοzΜƒ–%Ÿ'ςσY,kZ~θΗ~*‡+θ•ώ)½ΤΛ?6»Όέ'Ս-sφVWSDπόŒό;Χ?E)OF ΝkΎœλί}ήr½§<ΡΏω€βK™y]kq‚·•ž^―oyK‹λC˜Wώ‡eW›62―ό·]θeνηVΕΡO°±ά“—ώ’Ί₯ͺG©ΥAΠϋΑ_RU–Αη žΘ/,:*.!*2„^2ΝΛͺ΄΄‹uW€O@ψGΗ§gI“£Βmy§HEI"½”)–ή&ά_@π7+ά>Υ?P# 7U©Α1 ρq±Αώt§?α@³Ά)7Δ©ώιwΈ%Hr *>^ΨB?CΟ‹O—$ΡkΒ…bΫηώ9Ό}H]+ £ͺNgf‰γ"ψ<ΆtΉ©‹KFΉ9ύ[Lu™εχi8œ5ΩV.Όΐπ™,W–ΒχOΘΌ΅?—fϊV3t\`3ξφ z§Š"[KΕt)†Η$ΔΗ%„›~‘EœRa: ϊ=Α„£‘Wϊ§HΣCq.υΟ!oέίSžλŸ’Τ΅β0ΑD&‹Σ%ρ‘!|„½ΉΙφλψ¬Ϋ4^]―oyΞ₯…Γ!{ΓDŒ8)Θό`^œιέQω-:ίΤ~nτOΏrΚςΒι: Ÿ' ‚bγmž^N'₯Θ*•¬ JΞ“Π?τ€τOQEjšΟΘ“£"Mοφژ(‘Yw‘θnTS©,>&‚ωA^q~΅έ+ΐt­εyIτφED%ΚJΤ\‹·¦₯,?+>2ΔO( „‘Ι’C΅:Ί&=“[έγΈΤ^ΚMŠψBΧ-}υ•β,Φϋο *ε:·sφΞͺŒσω’x7oύs«.εκ@Ž^ϊKΆΘΒθί§Ρq‘λRφ΅wάG „Οϊ‡ώ—’ώϋvvχj΄c“Ί©©©i@ψ$tΊ©[wΪ–rάϋ]ρpDύ―DύOθ:»{'uS33zϊςX8333ΣΣ3·ξ΄ΝΞ=Zša0’νΨέίε=ͺЊΣ—²O;:Χλ ƒΑ`4I£‘$ΙY@xtEj0 ΓRΦ?ΣΈϋ»άηcΠRΧ{Ηύ陃ΑH’³³³³sss,ŒΉΉΉΩΩ9£‘Όu§œ{΄ΔC£»Χ‘€ώWœώ IΞΞΝΝΝΟΟ?°`ζηη=z4;;·,τ―7’7o·B+PΖΩΩYΪύπσσσsss·n·‘³–~άΊέύ―8ύδάάν~<« η?ώxόψρ£Gnέn%gη–~άBοeκΡ£GΠ?ψ\7o·gη–~`π%κŸ$g‘€ώ‘θΐOοƒςΚͺςΚͺžήΠ?τύΐΦ?9·π¨©½τ‚ Α<Α ώ5΅—|²[& θ€Οτίr»Υ@Ξ-<^π ΏP{ιBν₯ Ππό}²[&Z θίΝyQΕ@iΑs£v.Ϋ<‘dcv€W1°ΐmXEΊΞθίγk±τ// η ‚σΫτ,ΟπƒbΕΕ-v”Ϊ¦β¬ψΘ>O@ƒγ${kTŽϋ 2?ž€π—5ΉΛ“ž>/BΦLz³½$3!Ψ_Δ†„§\Rλ.₯‹aΦ%Φ&—2E¬³v4ηΚ)8\ jΧ–% aΦΫm=5I‘!|‘ΘI>sAΧQU މπšΕ:ϋk‘m-ΟKŠ ρ ψπΔ¬’Vςt΅IBARycIJŸ' N―ξΡ)J£ύ„’€ΈΒV‡ΧBΣR”ΰ/"„Α‰y•r…#ϊ(=nŽEžO9­%xΡ”>ΝCΞηξΎ6Λόx"qΦT6xΎtR~ύ€ηeήύυr{Ώ+O†σ~™υ€]cBβ/ bNφxV~8ΧOTžτύ/rο?]+Ι?YRZ(‰ $x’¨R».8_^!‹ΌqqEY9΅­Κ³m˜c…%g:SVZ˜M«˜ύ³Tdk~4Α ٘{²¬¦φ|Υ™ιΡ|^`R•Φ{ύλκ%A~b΅Ζ›mΤe‰"‚%=YYUQ$M“ˆγ„?K·5Π\]V^QV~&;FδΈΪυ(\\ ΞϊO%dW”1ϋ)φ΄€ιšς£ωO@ψΛV…W2E.V\sΛ—Χ‚³ώ­ςYΧ( Aμ|ζ€Ό0œ'H―΅œ ΩΎ7L@ψK,υu[A0O^―³–”Χd:V½X( Β Z)ŠU' Dda‡ΉZχ“ΆpΏd³,€'Ξ΅”MΔg­@₯‡Σ±LΉTΜ„R,fz’Χε.AY—tVγφDda·eήι½Γα~ΧΥeωρDρεVεΉ2ED™όαX~<©7\Π–ΗEM·ξΜg= Zž~‹ΦΏ§ίb’ ϊ‡ώέκί/³‘}Ϋ”Δ9ΈΥ}₯~J΅Ξφ+Ρδ̍Ωώ"¦°ΓWΓvšZ±Ώΐ]z²MOq,Α ”°4ιj“„‹¨7Χ‚³ώmσYj“Οξi͏ x!²fλž™²₯©M₯!]mΣ”šר ότZ’’(ͺQβ/ΰ§ΠΆ――έ\‹ŽCΡΆΗ"λ%ώV τUzΈ‹‹ώ}”‡ž€‡ƒώr[¬ϊϊΦeΜ³2οβώβrΏΣ vWS/H™»€cωρM½ΡS»Ψϊ/ϋξϋΌ³Ι>ΚΎϋϊ‡ώŸΔΰΏΥПN|ͺΰ|«Šr 4ΰ‰$uVƒτ4a’΄°¬}`A/ΡC‚΅ηjSme5•«*ŠYDύ»Ήœυο.ŸέόW¦Έ}v\w>ΕΑωjΚž(©†dͺo?i#SGσ3ι·ΘθίΥ΅Έ’)²Λ=ՁHΆ}–Ηβ’_ε‘'ια ΧeΜ“2οςώβV{Šc ^Δ^9³A¬υšŽεΗ7υFSn'ύίΌ3c = “ώ dΩw•q ΄²ο*νΏeΏ‡Ηb’ι&τύ»ΥΏkψT63Кςd‚'אVK₯j %q~τβaH”΄’Γ»F€¦:^(πc czΊΝ•Μ@‚k}FΪ²ΈΕΤ?§e}ΆqŸΟΡ–% žνΌ,—mtUb‚'ˆ7M»Φ³iέιίΥ΅ /₯Ϋη­ͺΘjω›―ΓεX\το«<τ$=\το²όxPζ]ί_Λ‘¦:^(£τͺ‘63άˏκ+™‹­Χ1<2Ί'/λΏ©ZqγΑt\ο}|½χρuΥόuΥό5ε£Fεά՞٫έdC7ΩpίxΉSY1σ›bΊ^>UΧ‘ϋεήDUγΚΏMEiκ 48μ©[λ*φ¦GσyΏ”jΟ“MΉ„0ΆHιύ6M@‚gσΧEξύϋHδ³'½Rξ=W±Uo›KυνζZ8κ+φ†qνύ{”ΗβΤϋχQz’žλŸs™wwq-‡ΊK™„Φ%½ ΒfA¨'ΝΗΧMRϊŸ6­iωΫΥk?Ώ]½Ζήfhdτυ7ތKψ'σΙΏ?ϋβμw•ž‹‰' ίΎ΅xΉ³Ψ’nvMλΣzγԌA7­Ÿœš™ΠMλ¦Η'§FΗ'΅cšΡρνΨCΝθΠCM‡βώŠΏΝs>vsξζK™φΛƒέ£Ψ(Hu Ψ¦ηP4Α ΜfΟΉκjΕΒ'§6ϋ•β@˜ύΫζ³ηs‡ΜIkš«ΛΚ«™:LσΦ€•¬ŽΕΉϊvw-θωo›k‘$΄R ―ΓεX\ζώ}”‡ž€gΑϊηZζέή_œοw²-/˜'JͺΡ6ε†X-ŠτN ¨7zE/ΆώύΩΜC}ώμ χΣέ}―}ΏΨϊohl‚ώW’ώ[έ{Ϊέ6τJ`ΙyυΒΦ gͺνd|d¬MUx)3Πϊιf‡.VώλHC7― tΏ Ω σ³^qέSκύΚ.ωγζZ¨ΟDY/$Ϋ ΒyŽVώ[εscΆυJrNy(/ ηY―κ"ΫχF L5Συ*ΆΩFW/φ7/Οφ ϊζz-X‹ΒΜΧΒnεΒΣΓιXτο£<τ$= Φ?·2ΟαώβrΏ³Fψqβx«sτ όψ¨ή d‹­‡ΑΈίu@Π“ΡΏFβΗπΓΔ²C'‹Θ€q±νS[nΆ‘—ΕΔGJ”Χž―:#‹ !x’μg M%dWTΦԞ―ͺ8 υγ‰’΅;hζ»xξ_y2JhU…QήmC*D ^H|~Εωšκ’άδΰΘ,±υ@¨ωakζykΦ“ύΦ/oα’?ξ…ͺ(F@ce5-JEkMaRLr|Œ£₯q.σ™KRΊ+Ή|ž(81―¨Ό’¬΄@Hπ“ͺΤΦM(σ3λU΅ηΛ Ε‘"B!³Ότž[υΝεZPŠ1¦ηΡΛΚ+Š€ Α‘qŒ€Κkςyz8‹ƒώ}“‡œΓ©r)cΚ<§λΕε~gFDͺ’ωφ/Ββ~½|Uo .ϊΏΡrgJOzqρ$x‚)=ΩΩ­ϊνκu::»UμΏΊ Oz£ϊ‡ώ}‘Š^9$<_“,)·ΏU\ncZ €θ¨Ι‹4Ώ¬΄έvpNΣRΒό~š008F,«jw<€ητ6¦ΗύάΜΉrΨ†ξpΧοM‰ aHTzα΅Ί,Ρͺ*Ό’ιμνoΜKžδ»kA*ke‰Μ+δderν•Μ@B(ΉDZunβ«ΤUyρ‘!|žΘ/,ARκišr‰~ωMjŽήFGi[KeτΫΦψπΔΌ²6Oηn9_ MγτΨ‘€†'\Rk+SμΏπτp?–[ύϋ$9₯‡S9δxΏ»)σάΗϋ=Ώ`χΊOΧΒλ 7ψDXƒ>ϋ‚ώλΝ;w…’uΠ?τηκΟδ)ωuAUQŒ€+θ X!xUζ=Ήίιιψ*νRΞ–ώoO鍅YΖΞnεo ttv+™ nήiŠώ/ΑΌύ1Ψ„§½Ρrϊ‡ώ‘―Ρ^)–I€–Χ‰¨Οl:zQ9O >*σάοͺ’8‘Ηo₯\œs_lύ»ΊOOwύCΠb@vŠζσ‰yEεΥ•₯Ia"B{@ϋƒ§•yχ;©l¬,?™Bπ­γΰ‰;ύ_oΉ­Σ=Š·βIπ:½‘=ψΟ³Y˝φdq:σ_‚'x+ώŸž‹‰λΠ?τύ/°CΠT*ΫFΟ)†„'ΚJΪtPxΊ|Pζ9άοΊ*1½&@RXϊ j‹ώ›oλfŒ…I3Ζ_―48τ…M°·Tk™›το᱘Έή ύC–€ώ= θϊ‡ώ`λ?yK:ΑΔ%lς(ž yK:τύCπδυ­ωφδŒΡ£hΎέžΌ%ύ­ψzΙ[›o·{z,&AΠ?ίιΦδŒaιΗ΅ζ[Π?τΐwϊŸ6,ύ€ώ‘Π?τύπVΝ·&¦ K?‘θτύ?ύϊ7IθV¬ώG'§‘•¨ƒΡ877ύΐΚΤίΰΘνΆί‘•₯žcγ³³³σσσt ΐΒ™ŸŸŸ››[ϊϊœnΉs·θ!τΏ²τ?>‘“ίWΡt ΐ™ŸŸτθΡμμ’Φθδt¦εΞݎΞnŸ@K]Eυ«Κο«&&§Œ$977χΐ˜›››3ΙΖζ[K9n·ύξσ~?τΏlτOtυΈy»₯ ΐ²ΧΚΜ—?Μ<~όψργΗσσσσσσ=š›››%IH’F£Q―ΧΟΜΜLOO릦&uΊρ‰ ₯R…Rϊ‡ώθϊ θ€ώ‘ϊ‡ώθϊ θ€ώŸœώϋΤ„cdt™€@ψ6 τώ χύC θϊύCΠ?θϊ‡ώ@Π?ύCτύΠ?τ@Π?ύCτύΠ?τ@Π?ύCτύ€ώ‘θτύC θϊύCΠ?θϊG©ύCτύΠ?τ@Π?žΣ9ψlͺ|•m(ž‘tσφo­›ξ[’ WΤ*ιΤ>2iόSLξΟ3εk-oΪΏΪAΪ„κs-EQΤεoΊLŸδiτΧο >γθ+«%]όOΌύύ€b@Π?ΎΧ?K99ƒΥZθIθί;ϊІQvτύΰύ+ž‘φπszψ9έΟIV-FKΠ?GύwΏ˜Cg¦Mt±Ό*št«ζ’τ<·£“έ€XύιX/J/€ώ‘¬ε~V‡8i8Uάcφ"τΪ#θίύ;aΈ₯Ÿkydz‚r«Σ ½α󃝫]/ θθŸφάηŸ*lΝD7†§φ£~νӞg%Šgvτ¬;άΏ΅vR‘gώώ¨θ Ir/Φ’ζ R»η¦bθ»ΆvRE΅Φυ­ϋT΅ξSΥkuFγδΜηΕͺη$ŠΥi]Οευm½fœΰ wicTJVΧͺCσzž•(V§u>+U†”χΫ·r+ZGήώ΄ϋ™4ΕjIχΊbνεIoυ?<*a¦T†o˜ηο=Π?EQύ#|s›μν{(Ύϊ‡ώπ½ώ©‰fϋ=RMQ”βZsi'€ϋ‹†ΫΊωπ„ΙΩ“γ‘ΜΐυαIΣ‡Καηθ%κκ9+>sx0f‡ν!žϋffΒ₯ώΉ€’(J;iΏsΊςΪ΅YVΜ_.WΪΞβK¬“z9£4Οά–JS}ΞΚjΟτoΙFELλc”_ύCψ^”vlι―Iχ(Š’Œύ#|Ζ―’žΠ#κ˜ƒ=–ωl¦SΛx}ΗΠeΊS~OmΩL:|ƒξ_~`ΣJ°Έ0UΎ*UρlŽ*τSΦώΣX­;ύsM5_]άiΩ¬x0©ψΑ‹;Μnή1xΩά/ŸΈ§Ά¬‹”t―;¨β[­ŠΰωΛίt›ΏΥiέΒπLFεΠsζ–ΚV%Š/€ώ‘C–ΑyωkΧζ)jΎόˆYœ9ƒΝ&Ύ7ČHΏF―˜›N’XνVρ}Ο*ΫUo/–˜φΆnΦΦ…©έI­¦':-ϋ»υ±ύ{Ά·MiλΞPšύ­Ÿx-ΝΖΈ³ŸjJπ3ŸjoθMψύ–ΩwϊgOω?[¬0Q·Μ3}ζΰƒSε«R;ίΎχ˜΅†GΪoλBΛ¬EQ”Q*e·Bιίƒ΄Νήhͺnͺn50glΦΎh““γ摏ξ v?›5ϋΞIZΛ”ͺœαΛvμ{σΰ_ͺόΉrƒ₯@Π?FοίβΘΎSΦ]OcKίjk‡ _V™ϊ»ί(jfλωͺTyhέ½=Iι'MnσΫ…Ο–Μ°v?ϋyž;ύ{’6Š’(κρpτ©Ί‘­Ε}λrΊXόζ|`r†•<Š’(Κ°ugύΟχ³¦όχχ;Ψήsύ+ž;2ފ7θϊ`±τo3χίožΡOSWΫlΙΛ<―O kM½δOΗzMϋιΞPΞ—QΠ«†•Cτ‘Ÿ)ž2ΪλυοIڌΓIy6N]mFζΥ=9#­V»γΎςώς7Μ”GgθεY‡q|ΣΓΟS½V<΄ΏΕκ  θλίvεΏ«ΆE– ¦—LΨ§υŸΊΦΏ:UΎ*­ΏzΞ¬νCεu*σ”ό<΅pύsOۜ!#Ηό ΒΥΫεc§ξzυΜ4‡9Ϋυώ3Έ­όnU»˜ς离•@Π?‹¬YΫηώ-sΆσλ‹»›ϋ¦/š&έ»Χ}Ϊm°ΈMΉΞ΄s+Γy―ξiλdΌ>x‘B·u΄τ’†[ϊ˜%x«wτ„f½ΐΝOώ¨-;Lλβ;|žΊάύ;‘ύC,ώ9ώβίcΕ΅ώηn,ν?5l£CRšc―ΥΗΥG‰OϊFœΣ6[tΨvέίs‡Υζ±ϊ$K’U—tΫξJΓίρ§ύβτ θ€?OŠΥ’.ώΑώ­uΣ½Ž0›žΪ_ΚΌW`ΦΪ)…£ινŒΫv 2 θs―Jλ+·ώΦΒτΟ9mzγ©ςΎ₯«ΣΊψϋ3'¨ωκoT/ζ)_ΜS+žμ΅l:ίzmψ΅ΌgΟH•―k«‡ΙSΕτ–ͺŒ{Π?€ώ‘θτύC θϊπTλ_3:±cD;NΗCΝΨCΝΨπΘθπΘθΠCνΰ°F=420ψ°πaΏzψAΰƒ>uoί€ςAOo}僛·ΫQͺ χή?ύCτΏ|υo0 Oώ τοΉώu:θΐςΣΏNύ/T““Π?€e‘‰ΙIθ‘ϊŸšš‚ώ,;ύOaπϊύCnτo€ώ~όΚ€%ΒγǏΗΗ'†††tSSŒώ-]Y+]—ιΦψ?{@755’Ρ  ¨•J@ –HΤ~˜˜ΊϋπΥwβκ‘‘!­VK―ψϋ]qίςΈΏuΧϊη<ύο€@ ΐψΔΔψψψΨΨΨθθθθθ¨V«ΥŽŽj΄ZV;’Ρ0ρpd@ ‹Ώέ™œž[tϋι9#ΆJhΉhGG΅Z--±±±ρρρρ‰ ϊ ΜΓ~έlχیόCp°Yΰ°ΐn˜ΪZ­Ζ.؁@ Ύ_νJNΟ-Ί³μOΔ^4ŒeLβwδ~Άώ ŽΊώσσσΠΏcύ³Ψ+œ΅lμvΣ°Ψ\>Άλ½τάβΦ§κ€ΨB‘ΓΏ½ϋgffδ]=μY›JΧΏ³ρΗ+\Ά¬Φν6£“+E»ή{£βΆ§α\μ%ΒXŸ-~{χλυzΕ}₯ύ°?Συ‡ώ9 °§Ψ-f€}#ΐ078fDuση²ŸμίΎC*ޞ³]v0ψ―ήβπΕξ«?ž.άΏϋv©xΧώό“?ίθΉz\ΊωύΏjηΊΑϊcβχ³ί?~kΔzίζΟ›šμώ›?—:˜΅ƒNd}λΠHC‘tσϋ_ΦτYPq­ζΨΎΜ‘εlωπ#ιώcΗΚgΫμΖ2lρ3ψ3Λύ:»•μa›?το@,ΏΘjΨ Ѝ¦ΐn 8h bbb|’»ζОΝΫ²7o۳㣃ο+Ψ±#{σΆμΝŸ/ΧΈϊβpΫ7ϋvoή–½y[φιΎέ›·eo–Ψ—½yΫΗ'ξrήπ•Ω›wœΉ‘aοΰ‘έ›·νω²E³°dw_:όρζmΩ›·ν~£ƒœ=›·eoΙ9Q°/ϋ½χΏ¬ιg6λΏqr–χ³ί{?{‹tΏμ£ιΏ—Sςk―•DΏΠΊ™²λτ3ΛύέJ›‡ύΨ]Ǐ―tύs°} €yΐf€iLMMιΜΧ†ΉT°ν­SΫ>ΨΉyΟ7FΝ6Ÿ>°ωƒΫJξ8βθ­Σω›?Ψ)ήs²Άs„ώpψχΊοήΉωƒ›?Θ;ω;χύΤ}ρ―Νδέ΅μ_ύΫ§™;7οώζΦθ‚’}Ώφ°ψƒβΏωΝΌΩpΫΕO³vnήΆsσΆ#0mΦυ«χ·νܜuδl›Ϊτέ‡?}‘·yΫNρgΏ©¬•Οκπ[Δoy½yΎΏ³[eο~θίƒΫ€y3 ΐn0νvSΐͺMΐ\?@LM©ΏϋwaρΙΖ!«Ο»ͺw°kλ—M£ΞΎ¨iϊwζ”>ϋAiυωhϋwΩμJω`οΙφπrρΦve”άc§ϋbλ»²Ο(–μ{'w;HδΓΖ“Ϋ>ΨΉωƒ#΅jϊ“ώŸςwnώ χ?M#V{½S$ΝNΩv ¬ΣJ%Œeο°Εo0Οχwv«\Έϊχ²ΐ`7ΨνvSWΖZ/|±υƒ][Ώl8ΫζφΩ>Ψ•ZxeΘφέίζνJύ`o‰ά“ύkeξJέ}Άuœώΐ…οJύΰΣ3ς…%»λΗ}°+υΣϊ>Ϋ-›gμLωΰθΟƒ¦£ΜΨ™’uφζΈν.οœΩ›ςΑΞύuCφΚgυ›zόΜBΏΩY’$»zz]Έϊw -ζqλ‘vSΐΎAΐ–‰‡]χnφσ%_οϋτ“4ΙTΙTΙ΄4kœ|Cs₯8U²+σT‡ύΎώ#K•|R"χh£Ώuobffffΰ׏$»RχώΨ΅ΐdί9-‘μJϋκφ„ν7{ΎΝΫ•ϊΑџ‡fffffzΞύ`Wκ§?+νŽΠwρ‹Τvν,οadΟVΎ}Ÿ^θGΟψwυτΊpόύ{ά°i0#ϊΨ5μΡΠλυzύ䃆/re©iκ‡ŸΘ—œψξ—_wtά¬I€iiΡ:ω–ΆαDͺDϊai‡ύώΙR%ŸœRxΆ‡ '$ΟvθυzΥ?O•H?ϊoίB“}σLšDšvςŽέ·•ε{w₯JŽ]Φλυz}ο₯$»R?½€²όΓίώ“*‘ξͺΈo#“hXβ'ΝJbόο+Έp?το~ ΐ¦`ί°o°[μ6k†kεlΩώρΏPŒL±>VžΟΩΎ;ύθ­1gίλ(ίΎ}χ–/Gl0pώ“έ[Άο+νςp#M…;vo‘Uޝ’χp€vhΑΙVVK·οήrθς Ν·§Z‹vJ·HŠκθԏίψμC閝ίή™²=ŒβμΎ-iώ/CVBa‰ΖΖϊΜhάά\·ͺΟ‘ϋ‘˜€λF€₯@_λց@ lcΊνΛ»·μ8Ϋι£dΟ¨λζ‹·οoί³cίἏσ·οήώΕ•Š£9[Άo°eτζ™‚τν»·lί½ύ£Γlί±{Λφέ[>*½>μTφ6ΚgυΣϊwΨι‡ώ½oΨ4μΫ.Z¬Ρυ^ρσ²²φd|όΕΑU?ίΡΟ͍v6œ9vτΣc?ήuώUύୟ+Ž|φY–tΟϋŸžϊ΅ύ‘~τ^Mρ‰ͺ«j/φ?vυΨqΖΏ2Nܝτe²uχλ«>쳌¬=-ώY>:§»qtχ–₯7υV;T5לh8k X6Œ7Κ–mέsNnτΥΎΛ—mΝώ²vΘκScΧ»3ώ•qVα“γ8S’ͺOνLόΠΏχ-Χν΄ `Ή1}χμΏ·fΚώUΥλÝ>ψεΛm™²mώ‘Y©2ΞΗ΅έM?μέ-g–+=Ά?ι0†²ΧΏέ …6Έ7,=F/ߟ½[Ά5SΆuχ©k>ήysΩη™™²­μΘ>x΄ρ!ι»c8΄[½ύϋ²€f,7&›Λ>ΟΜώ(ϋ`yjf10=άΩάxΉκ‡ uΧnuφMx©~μCλί…Ρ ?©ό9PΥΫ?θZdΠ?šž峁ώΡ,πΤjήΠ?°β€ώθϊ δύϊτθΠ?–°ώ_:‹/α| %tEXtdate:create2020-05-24T20:00:42+00:00ΐ μv%tEXtdate:modify2020-05-24T20:00:42+00:00±ύTΚtEXtSoftwareShutterc‚Π IENDB`‚pyproject-parser-0.9.1/doc-source/index.rst000066400000000000000000000077231444752763000207630ustar00rootroot00000000000000================= pyproject-parser ================= .. start short_desc .. documentation-summary:: :meta: .. end short_desc .. start shields .. only:: html .. list-table:: :stub-columns: 1 :widths: 10 90 * - Docs - |docs| |docs_check| * - Tests - |actions_linux| |actions_windows| |actions_macos| |coveralls| * - PyPI - |pypi-version| |supported-versions| |supported-implementations| |wheel| * - Anaconda - |conda-version| |conda-platform| * - Activity - |commits-latest| |commits-since| |maintained| |pypi-downloads| * - QA - |codefactor| |actions_flake8| |actions_mypy| * - Other - |license| |language| |requires| .. |docs| rtfd-shield:: :project: pyproject-parser :alt: Documentation Build Status .. |docs_check| actions-shield:: :workflow: Docs Check :alt: Docs Check Status .. |actions_linux| actions-shield:: :workflow: Linux :alt: Linux Test Status .. |actions_windows| actions-shield:: :workflow: Windows :alt: Windows Test Status .. |actions_macos| actions-shield:: :workflow: macOS :alt: macOS Test Status .. |actions_flake8| actions-shield:: :workflow: Flake8 :alt: Flake8 Status .. |actions_mypy| actions-shield:: :workflow: mypy :alt: mypy status .. |requires| image:: https://dependency-dash.repo-helper.uk/github/repo-helper/pyproject-parser/badge.svg :target: https://dependency-dash.repo-helper.uk/github/repo-helper/pyproject-parser/ :alt: Requirements Status .. |coveralls| coveralls-shield:: :alt: Coverage .. |codefactor| codefactor-shield:: :alt: CodeFactor Grade .. |pypi-version| pypi-shield:: :project: pyproject-parser :version: :alt: PyPI - Package Version .. |supported-versions| pypi-shield:: :project: pyproject-parser :py-versions: :alt: PyPI - Supported Python Versions .. |supported-implementations| pypi-shield:: :project: pyproject-parser :implementations: :alt: PyPI - Supported Implementations .. |wheel| pypi-shield:: :project: pyproject-parser :wheel: :alt: PyPI - Wheel .. |conda-version| image:: https://img.shields.io/conda/v/conda-forge/pyproject-parser?logo=anaconda :target: https://anaconda.org/conda-forge/pyproject-parser :alt: Conda - Package Version .. |conda-platform| image:: https://img.shields.io/conda/pn/conda-forge/pyproject-parser?label=conda%7Cplatform :target: https://anaconda.org/conda-forge/pyproject-parser :alt: Conda - Platform .. |license| github-shield:: :license: :alt: License .. |language| github-shield:: :top-language: :alt: GitHub top language .. |commits-since| github-shield:: :commits-since: v0.9.1 :alt: GitHub commits since tagged version .. |commits-latest| github-shield:: :last-commit: :alt: GitHub last commit .. |maintained| maintained-shield:: 2023 :alt: Maintenance .. |pypi-downloads| pypi-shield:: :project: pyproject-parser :downloads: month :alt: PyPI - Downloads .. end shields Installation --------------- .. start installation .. installation:: pyproject-parser :pypi: :github: :anaconda: :conda-channels: conda-forge .. end installation .. latex:vspace:: 20px ``pyproject-parser`` also has an optional README validation feature, which checks the README will render correctly on PyPI. This requires that the ``readme`` extra is installed: .. prompt:: bash python -m pip install pyproject-parser[readme] Once the dependencies are installed the validation can be disabled by setting the ``CHECK_README`` environment variable to ``0``. Contents ----------- .. html-section:: .. toctree:: :hidden: Home .. toctree:: :maxdepth: 3 :glob: cli api/pyproject-parser api/* .. sidebar-links:: :caption: Links :github: :pypi: pyproject-parser Contributing Guide Source license .. start links .. only:: html View the :ref:`Function Index ` or browse the `Source Code <_modules/index.html>`__. :github:repo:`Browse the GitHub Repository ` .. end links pyproject-parser-0.9.1/doc-source/license.rst000066400000000000000000000002321444752763000212620ustar00rootroot00000000000000========= License ========= ``pyproject-parser`` is licensed under the :choosealicense:`MIT` .. license-info:: MIT .. license:: :py: pyproject-parser pyproject-parser-0.9.1/doc-source/local_extension.py000066400000000000000000000011041444752763000226450ustar00rootroot00000000000000# 3rd party from docutils import nodes from sphinx.util.docutils import SphinxDirective def configure(app, doctree): if app.builder.name.lower() == "html": app.config.toctree_plus_types.add("method") app.config.toctree_plus_types.add("attribute") class Space(SphinxDirective): def run(self): # TODO: other builders return [ nodes.raw('', "\n\\vspace{10px}\n", format="latex"), nodes.raw('', "\n
\n", format="html"), # nodes.line_block('', nodes.line()) ] def setup(app): app.connect("doctree-read", configure) app.add_directive("space", Space) pyproject-parser-0.9.1/doc-source/not-found.png000066400000000000000000001352371444752763000215430ustar00rootroot00000000000000‰PNG  IHDR\r¨f'ΈzTXtRaw profile type exifxΪ­œi’7’…γsμp«Ωά`Ž?ίC)J’š”ΝˆM»*3w‹ΓƒξόΟ_χ_όΧS4—K³Ϊkυό—{ξqπσŸϊϋ3ψόώόφ_ψϊσOίwίωšψš>?hγλ]ƒο—?ήπν3Βόσχ}ύ$ΪΧ…Ύ}ςΧ“>9ς—ύγ"ω~ό|?δ― υσωKνΦ~\ꌟ―λλ…o)_ΏS{—ώ~ύχγ7rc—vαU)Ζ“BςοΟόYAϊόό.ό“ιuό|€”ŠγKHίξ• ωΣνύ±Α?nΠO7ίύuχiσγψϊ~ϊΛ^ΦoQ«?A(?ίό·Ε?|pϊΎ’ψ—Œ˜ώv;_ΏοέvοωάέΘ•­_υ=ήexαdΛΣ{[εWγwαονύκό2?ό"8Ϋ/?ω΅B‘¨\rΨa„ΞϋΊΒb‰9žΨψγ",ϊž₯{\DŒ(κWΈ±₯žv2"·βq„.§ψ}-α}nŸ·‚ρΙ;πΈXΰ-ψΛύ§ώ›_ξή₯- ήΎολŠΚ\–‘ΘιO^E@ΒύŠ[yόνΧχ’υ?6ΑςΆΩΈΑαη糄?r+½8'^Wψϊ©ŠΰΪώΊ[Δgπ•„5ψc }44XyL9N"J‰›EƜREJ†Οζ=-ΌΧΖkΤ·Α&QRMΨτ4VΞ…όiΩΘ‘QRΙ₯”ZZ1Wz5Υ\K­΅Uάh©εVZm­YλmX²lΕͺ53λ6zμ ,½φΦ­χ>Ftƒ\kπϊΑwfœiζYfmΪμs,gεUV]mΩκkμΈΣ&vέmΫξ{œΰHqς)§žvμτ3.ΉvΣΝ·άzΫ΅Ϋοψ΅―¨ώνΧΏˆZψŠZ|‘λΪχ¨ρ]ΧΪ·KΑIQ̈X́ˆ7E€„ŽŠ™·sTδ3ί£π,²Θ’ΨΈ1B˜Oˆε†ο±ϋ#rΏ7Wμ·β9§ΠύDΞΊΏΗν'QΫβΉυ"φ©Bν©OT??6\΄!RψuΏΣ\‘Œ[ 5³Špςθmφ»χ‘ϊvώ•[]ΐΰν`c-₯΅[ηυs°ΎλΉ%YeS@°E"t_Σn1Λ—YκXν:Ώ@Τ[δŽΚ -κaί39rAΙ †³;μNi|Șν8)ΨvΩ n›Ϋ;ξͺϊ+ΕαΊ³ZXeξ³On mέ•άœΓNλ¬άΧfλΆπ<΅όή +šZxeηo “ο₯Ξm7°’ΩΝ‹ ͺmήQZšlŠ‚Ήγl©¬bΛvˆ‡PΥ•άMγμrΈΝpɝvήGŸΈ;Ψό7ΏΊίxaαΆ<·Λ§Δ0o¨—ΤU§Ak₯Sl7wœb„iuά?ΩζΪqπu°μδ8Ζ ©Iƒ]η;ΜΖ^λΤΘ$·NΏ7-Κ4τuΉΗ½ΨΥΕ~²ΣlMΫ'ψλΣ"gΦ;j™‰xŽc‘²¨z \RΕqΪτ7s‘Κ™m€k‘MccCΪ·—Ωϋ⡉eΟrWšχ$ΎMυy­₯ε·5ϊΘlv<~τiqJ?^ωšΙιChΪ G"mάs3;²Α'»;+}œά%ž‘|✠?Λ—:UQΡqsΝYΪ<ώH5²΄Κφqq½2ΨΘ6ΦrδΤΝN©ΤσΜΈ°£bj”‘eŠ–zλ$Ν²RE΄υ*B ”K,(,·kY„”=ΚvΟρJ»» ž:©“Ψ•¨ ξžl υ¬£[I£ ?Wρ uHm€Ω¬.΄ ’₯ΩΩ„Χκ| ‚X\~Ϊm0ˆΔŸ”ύα•\Σ7 'ϋΎBθ•Έ pδΌ-zM½ώ–6ΩS !8CυšŽηλΙA[Κ%@ΎΛά©€ΚΌO”8š@οŠδ {Dn’ΦΐΔ,”2a˜‡χ(ٟyJ6O‘M;››%vΠήΒ™Θ!p+B‘σ9ΰ³ώ·Ψ`R“π(ϊ¨qφθν¦?μΎ|Π­ΨΕ‰LδN}\Θ§VΖ!‡Iί=›()…R'χΓ‹,`b«r’c±Ύ`κ1Z&H,ΨN)KPΡΈρΈε²ΤN ΥX›B6&n ,ήƒrtž@WbI@δYZ½ς!΄Α-S`πΛIƒ•Γ qCkK•]υς}₯MANYFκξ‘1H„C£—‡&DαΧ_έί~²ΣΔbίΐ Q³Γ`χ΄#NZ=‚¨¬²ΰε<ŸNY½―‘kΔ:²-AQIh2Ά½Dpα(9»‘LΎδο\™ "Ξά Έx”"1P‘»ŽΜChΖvFͺ1ΗΖψ\ !ίN]dΎ·w7(†Ύx1D°SO#oaC‘ͺη†JαΪΌ” d"r€]ίHˆόHw¨WPmΓk²"t"w% ƒάΈϋΥ<Π°aši¬=Oou\n}³*κΚ7•ό1ΔfuYνΈΨηΊqωΨ+xSX·1ΰ&Θ”,37’Φσ=xP¨cWΫT$Ι|CψŠPΝ±fy*M⁦Dΐι”Q"x$ζ­ͺ»΄fŸa”4Νςτ²’ΰnοNƒΜ’>’i’—žθ`߈wΣύA/&u΅)KΫD°>@ι«Ί½Λ`8žΛ ,-©4Ψ"‡XšΥ—߁ψI„ Ώ₯kίZ ςwQεQ"Δapδ¦R=’i&( D uϊMΌα· —‘%+][ `”CvZnΓ(΄±ί&_p9ψ1tΌΘCͺ¦‘ D4ά5ξ[²ήΝKo0„JžΫΝJΨAύ:ΫK€όArHkl‘]]ΔK°Ÿ,ΔΞ`΅0JγΛ5‘έξϊ ΐ‚‹#uήt!50ζIe­3Β¨oβη2jj@yA‰eM½²Μ<ήυ¨]”„•=“8C#'Θ,½Τ” Ν²3ή9Kjβ‘S9~7€~£p²³…Bσ8MΚfτ•zˆ441*'3‹Έˆ$©:D*\ˆ€[Ψm9Έωε8eAP}*D§΅ςD‘g—PXΐ@€Γw•q΅EE6–ΐ½₯’D駃!%‰>ŽŠΛ΄aγPΒFzHd--€CθZd^Δ|ΟR¨ιη!Αš1}Ψ^$@u@cά5ς€G έ;±UlΥRΰ·ΐλ₯Š£4Γ£ΰόSδ‰Μ`w(}δΟ­"+«7’&z₯dGRr&uoώ&㣜΅Lqς‚IΨAΖL ΛCΰϊΙwLM τ―5$J«S΄m₯~kΨ2R "ZΉ\‚Ky•~Ώ-ι6Ήͺ Qνx'΅-ΤaO…€ ή₯¬ϋ¬»>dΞΖγl=Šw‰L $,ε‘9ΚΪwΊ}َ$ΊDψΒΛ<ϊ˜ ΦΰΩcP Φτ„ιhΨΆψc"Ρo”Ž †μ+8fύμPbθ6P‘ΙGQ™ϋ TΠ.Dθˆ„|ξ(σp;˜β¨W*œFέζ-΄λ`ΏvCΘ^0Ά$”Βs(ΊΓrρ=H(uX ρ mrθŸ{Uξ€+ΊΔμΫ`½Ψz+,{?X|! FFŠvΜιE₯ΜλΨς<Ψι,*Θ¦b:L’Ι>¨ƒ¨–|: |0:—:‡“Ωφα]€ͺa»sS!Ωδ%‚ΣZΩ`6И cΓν•‹W!Kΐ=°yj%ην ƒHp²]’UŸ ŒΛ=απ@­μΧhψAνΧ‘Ÿ³Γ…-Σ~UŒEˆGέϊοU9ε5q… ύ‘CMτ.7‰‘9Θ]dπˆna%€2uYяΰίΑρc‚b@B€2­Ζν+όˆδ,Γ~© U¦‰)C7!—rg‹Ns Ε:"ŠP@ΎXw4RsΘή<χ Ο‹’IΨ€ΓΥOΒ‚εμ•›)^8’>APGJέ€x\μ9²ˆΪ$Β¬ΩρFΓίΐΎη(…ΔΆ`—Xzρ8”εΚP(λdˆVJRΤcB3τ¨»K=)9Α/θ¨ J ͺέ!mEς…ΐž q`ΕU†$1-W”?―ƒ:Β`i‹Ž ζ"θœ‰€nM©Bΐ¬HjJ±‚ήy Ρ½pΗAΈ‹¬Ψmp‚οώQβ £ ξ:·OnLn²Ώ–ž@Ρhl•΄πή»žΙ¦B‘]ώ €tπλπL@f#ŸΤϊΎ»P#ΑΨ†@Γ7ΚΉςRΈε€πDœ ₯³ΉηcXσjqΒ“€η‡ΌΗ ZλΌΡ‹?/εκ€u`ύΨέΥ{šαS°CΆ™ήgη(l |’Œ‘s5Q^Mθ“ͺC ž*;e t>’0α?ͺzF=φ–€ΝuŸΕ; IόΑ5±—^6‹ |Φ퍩zλvΜοhrŽdΕ+”αΟ‰]'Q3rIΜ3 ϋ‹qΕσ@|J’‡X+—ίϊβϋPe&uΞ.7XIύ‹d¨—KΑ¬@2]‘Ð`A[vΘ³B^aM‰?πΆIDΜw ήwΤ`αΥΥYžX£ŽαUVRsd4šŸa+‘Š,ΙκY2Θj‹6'ΑR³Λπ±νωΡρs(Ζ^Q˜Δ})°V’ο‹ PΏKKBSα͘ iŽg>ω‰`Φα>Ύ^ LΊΔΞύ€ΞF°Κ6’ŒLHήφΟρφ,­!»λ)δά’`R‘VT˜JΔ€#cG¬ΐ—1H,’.Β’Ž d4 ˆ0M’r˜1‡Lˆ`ΈΠψΑ‡&0jͺ ₯„ ΌΥ(‚‘b~wκc&œOϊ !S―‘PΠϋSŽ'ΨSΘ‘ʞUe!J/ΏΦšC€¨p’ΆC#Έtμ§[¬ 7„ˆκ` Xe›ρΉ.Pν‘Λ‘Εp#+ 1N+Ήj(U&ΌDΔ}?Ψ₯’²Pψ1Ι#ή@ub_Βώ)&ΉΏ|/“ΤβA&IΩ”‡@ ²”w³…Ό‘―”P@HιVΥ½EŒΎ ƞς—²αΞ &@,πSΔθj2ϋBΰ”τ κ Θβxˆ λԐΗ;¨³‰ˆ€Β.w§Ž—Ώκ±Qχp dšΆΜšα>2kxΌ03ŠΤFmά ί»§Έ!«­>f‚5ŸFς ‰€‹Tρ V“ΖhsίλR‚x«\†ΊcΗ₯].€VPΏ Υ‹pζ}o°?¦§hˆΆ PΉ ΐ` χ"ά\hlU>…|C+ΜΞa>^[D(‘Nl-=ψ9y†ϊ1~?6BυR·ΙΡ—ΞΌκ πnCe\ςEd@e‡}¨ΰo–)λϊ6μςš$χζ N]ˆά#—ΪŠR0c°J5v4D›x‘ƒ;”=&± ε$nδ+»Ά‘-J2)ƒo“Nn„IOjsQΘ©ͺV*ψOŠξΎ ΓΩΎFeδΰι’£Α?l›ΐ „ νβ˜Ϋ@“ΑA˜%2o½ΰ†7όρΥ €_ΫΨOOj9œC‚bΰμη˜P@U}kgx?t5λΛΧ—ΉPϊ €dΖ€©#ΊΜΕW„lT %)Ώl³e@4₯Š›€Ζ1{:mAR WΆ¬9δΰρŠBόˆj…#ψdΡδζή‹;Ε»ŽΈƒΨ'{ŸΌΊ‰zτ:Ώ‹ωVΜ$|ΊTP λ]HqE€ꟍ 8<6‘ ’ίΕoH8.΅“Υ`Θͺvτ•3πa–QΡιHqβΤ؏υ0u!呍DΔ“;Ύ!’ ϋ$UxώΒψ+LT­Ε(a|Ζ($φ[θ―˜ΡsSΆΊN„'6 ut»G CκΏO‡I‘|*q.ΉG ΑΞ#Ÿ  ZΓ–e@ ΨXiΨE‚„ΌΓν¨„σ2εoΐ8p.Θκ`$ΈΙ±P’ι$θ}%©Χ8W'Η΅³E’ϊrqβ•αYL7±μͺΛ {½²»ΙAΤήͺ‘rΫ~>RujRδ™=|X‰”od§ιΤ ^ΑΣ'o³πω]ν–¬­Vϋi«λLI!‰Ά/ωLυ†ν|Έ%Ω‘Nͺ+xΌ.€f(= n’C'ƒƒzΒJύ²ΚκͺΑ0‚„|˜()Όl“δ.0:V'V€πΟ‡β-@΄K‰™έ¬Γu@ΓEg[u£€0Vΐ AYƈR5―Ώ’jՌηb)yg&uΜb„ίγμF› €Hˆ-Δρ΅Ζx%Π$¨7§ŽKœ Ϋ(Hς‚‘ŒMΩρΎ{RŒk‚Χ5³ vVύ 9γAFR»`Yžtδ†$™•ίή›ΐς±jΥ‘KŽδπΣ”H1'/7ΓήrCκF6œš?BA…0ΞΔr’ΠXΓ¦©ώ1’¨©K)D~¦ΰ՝κχ@$x`~b$u4 „,ββrΘ-Td"zΙΨ€˜a/#£Qi;o § γ^w‚ς\ΈS΄. ΊΣ|τ8뇉VPgθν7¨ϋΔBΡΈl}^ Ί©CΗΨάΨV·¦CΕαΤ«l!`KLΒ±Iύt<†Νp8₯ΑnδΠ!Ϊ0E•ΎΘRα¨°bΌKΧΆΞuQƒ’α‹ξΖ֐Ε{εGΦzγΈ°Vg²ιC™γž)V»Ή$o–D„Π‘υQ¦˜=\-'JEB^ϋ£ΎmΔχ%*αi™:ΆΓ™_Πσΰžσ€˜E΅vŒ3BΡιχ+ Mή}±Βp& ‰?.±Ω¨S—t0EGΗƒcΑΦDΰ·τ R-ΈΰJ hάρ‰6($d“π'l85Dˆ-¨>SE^–Q4{{H±cSŽ„k–Ο6hˆ¬άσΣ΄Ιι,ζ‘½TΗX3!ψΫς,,f%h8±ύ©Ά―ιH]θ¬Γ©>œή ¦ς°ͺ‘¦Βς@ WΡJ†šIΌ”p7ΕΝ%}Τ)’ ό²άΚ„ͺqΰ'H §ΊZ€h ƒS ~‘ύ―ΎΊb-‚Τ#ŒΉœSP“V*Ήb’Εϋφν»:#ʝ(«αΉp;ά~F„Τ† Τw–.OžΪ„λi£jΒ‚ΊF•v˜ –B°ΐ^uŠ…%fδρE|5ŸϊƜŸΞ¦6Ωu±Υ"1˜^P¦μ™ι£f-šŸŸJ’ϋIηΖ¨)§c›‹ΥOι"t„Α#υŒ+j¨0μ…ζ6κλ+@ξΆκU›{Ισίπ!HΒτΊ@“$2[ή}%nT~O'³ Μ‘ΜΉχω¦ut,; »P«ΰ5—V—$Ζlל£΄οΒΆwτO /’Ήη”JuX˜ά­WC|bŠBΆ!9`C–΄}Ξ3±2_΅1XΔ‹βτ یEJˆpBΐ•[ΤΩBλN”Xuχu2ΣjDkΜμβAIXu-‘κOI’±ψ:V žΠ)|Ή…’mΫM΅ˆΠ¬‚eΥΠm₯’HšuΨδ#ά…τFvM)Ž#Σ¨ξ0χ›,ͺS ίΪu^§€h*δΎ‡:μ‘>”ˆP Χ9Ωώœfk`h¦ I§Π Ω—₯F¨±ΦΑ6V„Mͺh‘ήΥΧ‘,¨₯ω¦+€,β κψ“WΗ“[‚PˆT-0]‡wΘ{uΠ—Τ5ΜΛ=;nθ·‡bςGΩ―I/χΗ—Έp¦iΦR¨4ΤΑŠšXMφ~θΠͺ“ς|QV“mό‚—Νˆ4ΩήΥϊ΅r†Ό}ZY‡^θOΓΠEvψ ”Π}₯#-`ά܏,ΉjΩ½^˜z+0ιο„©³δ!Lˆ›£>Ρλ# ²Ι4uκ‚*Ν‰Ψ₯F„Šαx²SfΚkκB5°#([ΰ-bάW#VπœΧ§†ΩΔ{6\-$š·ΞΠIαsLΫ·ΤKIΎ„9‰N©Q) /βά•XθΞːxΣ'ΏSZY*₯ΰš!Nt΄‰£ζ‚`n‰ιN0̐cw>%β2pX„t DJ„“„Ϊ(V…$°ORp†v€HΗ%Zχ$b$Ο‡bΧ‡p`©MΡAε]oj…FΉ2’KT‹Ξ„ΡΌ£θ’ŽΫS‘Ω£‹o8RγY0+Θg€ι`~Ξ&©IΙ1,P±Ί`eŸ©ΗέQέiπ§X‘Ϋδ›™t:‚©ΗS ΝjLˆ”ΟgΪεužIyΠΨ‚z&Ϊ*t+%B–Ι’TnFγ†j#XŸ-kZP―ψ‘­6ΒV6:IΩπK‹³ΎD>z5uΥŸ„]€<Ξw)gBΫ>ϊ‰·AzΤ7#}H:Κ’Τ`PΕ H^Ί£ήΑI:Χ-‘z νˆcjδj-G±Άά=’Όθ€ηjΟλ(D €`*-cU–‘Y:₯$˜ Ο˜„ ΤζΆ!0bΠΔuΓDΙsDΏP˜ {F–+ι`ζ~oΰεσ•­“&ΧPp ]€υšBrΒαa_`“­C 1HΪςkZΈ8ϋiv7!™4}†ˆΚΎRE°-]6 WEυgi½ cΓ7R‰%ΨΨDΗNs£έΛ―:€$™AՁ+ΘHT yά" HS€―‘ΐ=P»&ΆΰpΜ> ojŒab€ΙΘ γN(J@Ϊ‘ΑκμεMΌ¨Δ‘cj{#~ m£†¬ΡEGί@/†ULP2β']vκ‚βqjl¨aLrΙPφ―…¨©/'v;”XΠ< jqς Ώq#(Œο%ΟΑ‰Xς‘γs€",V1—pT^0ΗzΙN* šδΘΌ„ :“1ƒE„畝@€LuέA.[γόŒόΧL!rC3=@]ˆ δδΐ‰€nˆ,Κ•Ϊ@Ύλθ>χ#±—άͺEΣƒ° [„ρkθ1ΣXͺ½1€ β°‹}¦™4|φS¬δ˜½,NΑ7W;ΠJξ‡UΒΠΈ¬%•&?ΥpdΝl₯ω Z}Η¬B"Ή ¦ k|ƒ―9Χ_―A£±XΈ|Έηe`=%šς–nΒΆθδΡ8(΄‚bΤάƒ¦1£¦^q·…–œžžhΪ:u2’†UΖQ&ˆ=:’³ψ6tŒίBΡ8(σ °Ά –RΉΙψ騀hX^#U˜ί5‘ό ±Ό£σUΝβ0`•:šS§f€›Ώ}GΝ( F&&k– hQ δζD;9p;?utŒUΧa/ΕBNf^М zaM•,θH¦ŒLΑ6LϊΡA]œV‰“\†}’d2NΑΠΙι)n,€΄ήd,`Ο\UΟx’ΨEΚ%’VQEΐζΡ 2F>W™³ŒΦ’ΐΝ8*ƒ£±œs@GΧ±i2 ŸηΪRρUƒ(ΨΛΦέΘδa‘d•vAQPaδx8ž i"8A™›ΤDlRΝ!Οψ5WCΣkhbjxμ"§¨ιg¨‹žfςŠ$8:€gŒdDvΧτ›Ύ’αΜΗ(6I\ ‰°†,«YΜ½5eΘΤ 40ί4ςϊT«Ζ£Ε–'Ω½Cψƒ+ˆ’sWΤΔ‡H₯HεΜD6²εςωαJ뱌Œ½DΜj΄Ϋ“±–³S„κο«Q7ŸI@ «Pΐ˜aκ’Cywζ€ώΥ›ΚηΊ:#Βεύ5%μκ πΤ―ΊžHψBSXuΌ5_PAΚ›ί|5Άτ …\•W’α§OΨ―Ή]˧ϟp‘ς*[Κuρjοšά_XG2ψu>,¬g0ΠΩ(rOHI{Π΅Šqή Ώš”$8·Λ΅@4XQΡΓ ―FφΰϊHex{ ΰ¨£s‰eδΓφ²ΠœK΄}@όΕŒˆ―λI(a@Q󱲋겷χκα4Ψ=l¦F ΆλΝ3ϋΆ―Ξζ†mמυš3•φν)Λλu¬›NΛg_°ΘT³=z5ΑΡx–υ†14„€VHrbY±ΩK33°ΕΥ—@Ψ8ΐ;:…7p…nƒ’GΔιΑXj j+鎻 ‚ΒsΥόέΥ(֐I‘τ[ΒΨ7 C·7₯§0Xwi*‹Ÿκ1ŒH‘Ϋ”ΡΤδmκξ_ΌιΝsŒE: )πΕG‘¬‰„φ@U“ ¨Ϋ˜IOΡΔ‡ζ―mL`FN ν­λξΠ*SΓj|Σ7½Zνlb/Ή£Ÿ™¨ςAu|ή±[aΔκΑ'ΉˆΉ«²™**Ό·a7‘Οκώκ„ό™|ˆ†'³›ΊΚlƒίhΉ₯§YPzNO}lDƒLΦξ™~=^IΗΝH< ν—Kά±.›ΫU€§Ύžk–ί1žΗuΚ^‡b)ŸP!ιlΝ ςv6k^{ ›Τ Ο–&Νπτ―Lt†γΏyήεϋˆb‘γ“΅Žˆw"³±νβψŠ{σφzΧQͺŒTBP’ΚtHΞ'ςTσ™MšΥ‰°¦c4ω¬™ω»]ͺu«™«±ζΈΥˆί;κ‘° <α6α¨i\φR‡ΣΤθΌ <πŸi%ο[κCϊζ MFκŒI“ZW“§ω <Ζ]Q‘Έ^…2Χe™&:qEηB ηχƒ¨?{X—<”,W„Αgϋ£K’ύFlkšaΕh±$²`/¦;7‘ … T2 Ηϊγ‘^―~J;’ν0€tΜΡsLgΎqBiͺs­‰ΆζΨP–Œœ'Φ„·ΈY'θ,λΗϋ$ν‡νPΙΐΫWηπ€ψΩΊUΜξή4ŒvΛ¬ͺinB­^εΤ ηΑ끋9`ΑΔ$!Π{ƒž4Ξ†ZS^ͺ bjή³Mδε'R9οƒRrlΓTkAη5¬γΔ¨T\špNΟE3Ϋωσ0m%?L―Y p±εKγY3˜ο‰šŽΞ0ƒΒZ―?“5­>)εW€όVΙ–UJοΘ¦ŽΊHΪa„ύΉκgu ΰ{ι ‚Ν‹“§Δƒ\¦n\Nι8λJώx VγΦ XI]'yAΐ0#σP2MΘ­–BφωΨΤ‰α'άH€6u£ z ΌΤ 0€gDΞ³"οY ,hFΒk ΠΈyoΝP€ΰΐ^υ°¦,BδU ά—bŠ6αΧFωώλjv_εΜ¦ΕUΔ¦y`>zJ¨QchΒbνXtœσ―Α`*ŠTϋ•Vα‹Α$?πύκQ6o*ΖT΄2ˆQ«hVΫβξ+3Š8ƒΧs„;2cθλ«Џž8ΈͺFΣ₯IMΜςΑB»ΧθΥι³aκ~Ν.,0v}¨}Α†l5ο3d+nΏ±yό‘ψ¬δ₯>€Ϋ0"ο؎άΠXΊ^ξτϊw"θ)@Σΰπ‘έ3Υ7ΐƒϊcm(›PΔzπL-WŽπs]r[_Υ΄Ae=—©œd¨υΤ!—ˆ…Π±ΒΥƒοΨηLξΓW9˜ήΩAΖΉD=+ͺηξ¦£ e”ZπΈkΆΎΐ‘ΐ"ψΦAή _μ©ο­ΩΩόyατι¨όι‘HAΕ…HEIθ!ΌU—z·Uc oΪBG―Ι‘Nyκ―`&^ +ξΰojΤ³ ˜SU^NΟ ΤΧDυΰΔŒZP=ŸYΧη 6Ε·½¦œό{:γ²?8έΟσ’ͺ:ΫCE;ιΑ–Œμ]³j: ΚΛβσ=ΐI¦†…«iΨgH·Ntφ€‰ΰ{›š[ΌJ3S„-―C£ιBν±+ •αΔί>s]|a:ο•Œ‚_4ΆΚΞx`[σ3zκhˆr)~›ςƒ‡¬ΌοΑζppμΞpΏ`Ω|K—&‡΄Y Πίzώp‡ŽU!‰kΣ±q›‡±*@#eΧρ©r4&GIΆg֍αΕΡ@ƒŸ‡ίΠ‚H”pβM›¦BW‡HgE9#,τΠΪθK0•Jάq F:F $fυ]Κ  šZ,φΌζΘ"Έλη‚h:ܞE¦ζ—¦,‚ΦEΓη­Œ•ύΆφFττιtΦXρk _^šš~ |κ<)ί5gVιA –¦SQњΖε5 ΨEΛ¦ ˜€sZ‚71Τ{Ύ}δΟdέτκ£’Žπ ƒžί¦ƒPΤΨ‡'—‡ΫΗ•sOή†$±BΉ1³ΨYI 0EWεw₯^I"NE$Άτςz<ε ΩΈŒ;Θο"`pώ&35Xdz˜αˆ^(Xή[qθ>- yN½"Δό“¦Ψ-Wξψt¨ ―V²9xκΨΏιyξ+, YζHΤΦσέ0=’dšφaXАΠΑ2œεuΌ?Ύs“ŠVΩ¨Ξωώεΰ`žχΧΦ9.“c}Έ}ΓXΫi’d”Ι6Vu‘ττœڟ§•¨ξ±κ·‘Ε“±ΙΐeQ'ΩΊίΩ₯PQΝ‚&ͺ@=!`&ν‘^‘ωšΉ_9Q'ςΊ§ύΪ5μ6kδ£Θ…¨γϊtε.a!fAH„΄Ή2ΌIm―ͺ'‚5/LP?zIΟ?lRαΦ lZ]0Τ@Ήš|h ^g-z’/W#tευhIf!θάΟlZFΟψm@ι!V6UΌ ―Π#±ΔχB]€‡1―”$•ψœxΤ€ ?2]φψqΡeŽeςf&=OΜ Ε–;X)™ρ$qLΥtΚ粫Œ·k•šΊ'{a$――,3fI,b "Θ¨‘Œ lΔiΥI±¦ύ„Πυ‹δ’ΙU†BŽT‘Arύ`π»[«01ξ%E@θΕq>†π.Π¬;Ξχ±γ4O€ΰ3p₯·ύΥ0ύIz½­ΕŽ€ΎmΰβΊ­Ι{ΐε0πdH¦δJAš\‘ΌŸΡ7ε€ώ[ gΝλ­΅Σ C]₯n€ƒC`€HΩλ>οξξμνί3­ώ~ΦΝrΟδ}―bKGDΫΫΫ—»$Ί pHYs  šœtIMEδ#¦υ? IDATxΪμ½wΌΥy6ϊ¬Ω½ο}zΡ9G½‰& H˜jΐΫ€ω&_Κηλόntβ/χχݐ/Ώ$ζ`'ΎNΟ—›ζc\0ΫΫQD“PC]ηθτΆ{/kέ?¦­™Y³ΟFBΨ³πφў=³ΛΜΌοϋΌΟΫg9ΛYΞr–³œε,g9ΛYΞr–³œε,g9ΛYΞr–³œε,g9ΛYΞr–³œε,g9ΛYΞr–³œε,g9ΛYΐEœSpρΧ£>κΠΙλΠ ‘<"‚όάΚυh(’ςΘH˜0MH~όγwN¬³p9­‘‘‘WΨ`€UΚ£Wπχj 8ΰ$€cSJ|φ³Ÿ­8WΒYŽΈψΒξp%€\«ό{Ωϋό΅jŽ8ΰE/‹ΕS?ό°sΑœε(€χ@θWΈΐνŠΰG?_{ΐΙ„ΐησ!‰ # ΒοχΓησΑνvƒI’F”RΤj5T*”J%”Λe”Λe‹E‹E4χβgR?𯄐oμή½»δ\yGόΜGyΔMΉΐo(ίu‘οαv»Ρέݍφφv΄΅΅!##ΓγρhB~Α’J)κυ:j΅ …2™ ’Ι$R©²Ω,’Ι$ŠΕβ»ύωiψΗΑΑΑCΞέΰ(€Ÿ%kŸπk>`ΰBŽΕbX±bzzzΠήގD"—ΛuIΏ?₯•J•JιtΣΣӘ››ΓΜΜ ζηηΑ»Π·|ΐ)₯ίώμg?ΫpDΔQ?­‚ί ΰχό&δxό‚ΛεraΥͺUXΎ|9z{{Η/Λί¦Ί™Lγγ㘞žΖΨΨΚες…ΌΝiC„Ψ½{·ZtΐOΰwψ Τχ/Φ―[·«W―Ζ’%Kΰυz?PΏ·Ρh X,"ŸΟcbb8ώό…Έ £ώœςνή½»ζˆŒ£>¨‚π‡~@h‘ύ°eΛ,]Ί>Ÿο§β4 ?˜˜˜ΐθθ(FFF@)]ΜαgόO·Ϋύψοώο3GtπX>ϊ¨‹1φλώ@G³}}>ΌςJlΨ°‰Dβ'φΝk΅j΅κυΊFβ©J©­ΰI’I’ΰrΉ΄‡Ϋν†Ϋν†Ηγω‰EήMPΙΓ±±1œ>}Ωlv1‡Ύΰχ_rΔΗQ—»ΥΏΐίΨΪlΏX,†νΫ·cνΪ΅ο β«D\Ή\F΅ZE₯RA­vqΡ²Ϋν†ΟηƒΟηΣBŠοF)4 δr9€R)LMMαΤ©S˜žž^θ0ΰ?μœvΔΘQ—#άπλl₯"‘Hΰϊλ―ΗκΥ«/ˆ½gŒ‘\.£T*‘T*‘R©ΌΆύ½½„ΐλυ" "ΐοχƒrAΏ)›Ν"Ncff'NœΐδδδB‡₯ό‘ίοηίώνίvάG\Β'€°ΔnŸp8Œ›nΊ kΦ¬Y΄ΰ3Ζ42­X,.Φo~ί–$IƒZβΡb•ͺR©fggqτθQΜΞΞ.tΨσ~mpppΨ)GΌ_‚πΘμΎ-lΎρΖ±eΛx<žE½oΉ\F.—C>ŸΏμ…Ύ™2‡ΓˆD"πϋύ‹:†RŠT*…L&ƒ©©)ΌύφΫΘησΝΙψέb±ψ―Nρ‘£.΅π_ΰqkμφYΏ~=nΊι&„ΓαEέόω|ιtϊ’ϋς—zy<-Cq1œA­VΓάάςω<Ν;‡#GŽ,”ŠόuΏ188˜qΔΛQu=όπΓƒŸπW„±Ίx<ŽΫn»  'ω5 d2d2™Kbνλ †Zjό!··λβ_I’‹ΕFαv/άͺ ŸΟkŠΰπαΓkΆϋ9|Γ1G\”υΘ#!ΰνφΉζškpν΅Χ.ΘμΧλu€Σid³ΩχŒΜ+WHeʘM—ΞV0“© “«`4UΕdΆ†L©Ž:–["ˆάθŠzПπ"ρ‘#ζC<κC{܏D,Ώχ½I9&„ "/¨(₯˜››C.—Γδδ$ήzλ-T*ΆI‚Ώ588ψώ˜9 ཆό}ΎΉ ‡eE"άuΧ]θλλ[΄Ÿϋ“>₯ 3©"F§ς83žΓ‘σ9œš+γbV΅ω±Ή?‚½τu…Ρ‘B’ΘO€b±‰Δ‚A‘PΐΜΜ J₯<ˆρρρf»=€ίt²πžΥΎ  [τϊΪ΅k±kΧΙ.΅šξέBύBΉ†³£:“ΒίI!S~λfb~n^—ΐ– ,_C(ΰyWο#IZZZ‹ΕDMͺΑΑƒ›qΟψΉΑΑA§‰£~"α(€―Bn€i±`·ήz+ΈβЦ!―rΉŒΩΩYT«Υ ώόR₯ŽΓ)Όrl?>™Eƒ^ž‘o‰άΌ:Šk7΄cΝ@…·#τz½hoooͺHcH&“šϋ΄~δr9»έΈcppπΌ#vŽx7Βί Ηχ-Ξo0Δ}χέ‡ήήή¦7λόό<2™ #§€σ“YΌzdOœG₯ώΑ ϊάξΉ’Ϋ7v` ;zΑ<’΅΅΅©[ Ί•Jo½υV3—`ΐmƒƒƒGΡsΐ…οAŽρ[ΎoWWξ½χ^D£Ρ¦Vffζ‚BzυΓ±³σψΞ+γ80^|χ'˜€ωΌžbΘο)x½ž’ίη.yάξŠΫνͺI’ΤpΉ₯:Ρ~C½N]”Rw½ήπΤλu_©RT«΅`‘\ Uͺ΅ cοώš]ΡΔ=ΧφbÊΈ]‹Ov»έθθθ@ °έ§Z­brr΅Z 'Nœΐ±cΗμvp»!pΐb…ό™θ΅U«VαŽ;ξ°­ΦcŒ!•J!•J-ϊσjuŠ7ŽMγ‰—&0’^°°G‚ώt,βO†Ct8H‡B¬$Iο l ”J…B)’Ο—ωB)žΙ•ZrΕJόB•Β@ά‹―οΑUλ;αu/^$ $ [«Ρh`rr•Jcccxγ7μ8–ŒβΌβˆ £ή•πoέΊ7ί|³m*/OR-f5 NΜΰ«/Œ]ΰϋ½ξb["<Υ’ˆLΕγ‘9·ΫuIΩξz½αN§sν©Ts6•ο.WλΑΕΫχβ‘K°umΗ’sό~?:;;mC†”RLOO£X,b~~/Ώό²ςΚ)HΰeG  ώ ΐηE―mίΎ7άpƒ­%*—Λ˜ššZtσΜΣ£i|yΟ0ŽN/NYψΌξRW[t΄£=>‰„.+f;—+ΖgfS}ΣsΩΎr΅XΜ1λ;ψ₯K±ͺqŽ\.ΊΊΊl BΖfffΟη‘Νf±oί>»nD·Ύιˆ’£xαU,ϊ~7ήx#ΆmΫf+όΉ\³³³‹Šλ§s<ω£sxφθΒ.!`ν‰πDwgΛΉ––θ !δ²~cŒ‘d*Ϋ19•\:›Κχ.ΖMΈ}C|x)Q"ΞA{{;"ϋnjΣΣΣΘησΘησΨ·oŸ]’Y;;βθ( έ ΰIΨώ;v`ΫΆmΆΗͺ]rτ₯Γk‡§ρΟG%Έ$©ήΫ?ΫΫΣv&π½+6Πν"€-μυ΅…=Ύ€Ον x]ŸGrI„B@c΄Φ ΄T₯υR΅QΛ—j΅ιl΅œ―4κ?ι9-—+ΑρρΉc3ιemš φJψΝ]ύΨΆ© "*γρ8Z[[TΕb{χξ΅Sη !Χνή½{άɟa044΄ ΐ!ˆσίpΓ ΨΎ}»Πς3Ζ0;;Ϋ,­cΞ|_ωώόπdf‘•j}έ‰ΣKz;Ny<ξEϋυ~δZΥŠτ$ό‘XΨŒ=‘€ΟνWΎ5£ςa` `„10¨›δΧΓͺυF½P—2ωjq.[)œ›-ζΕϊ»βj΅Ίgl|vΥθdre}Epσͺ(~α#+,ά-‰ ½½έφΪLOO£P( P(`οή½vΌΜΫ„wοޝsΔςgP Ψyza]sΝ5Ψ±c‡ν 655΅¨&—ΗΞ&ρΕ§N#Yj4ƒΆ΄―+qf ΏσΈΗγ^Ψίβ퍴t΅ρ°/ͺ~MΞ α=Ζ΄ „QΖ@P ``LQ…R­<“)gFf ™ss₯‚vπβwδόԚ±©τJʘ}ΓΏ ΏϋΡΨ°’uΑχ ƒθκκ²½F“““(•JΘησxα…μjžpοΰΰ ΣŽό-Χεπ%}τΡ €=Vš_Ϋ΄inΉεa"Κb…ΏAΎϋώκ»Γ(Υνe₯-šάΌ~ΰ•ΞΞ–Q—KjzvDΌΎ›Φ΅φήΌ©cε–e‰ŽΈ?πΉ}Dκ™61Ύβ2«²A@ m'``DΝP6`ϊ>ς&eFΐ|š;5ž=9UΘ^$˜Mυœ<;Ή΅RkΨ²7,ΰΧξYƒpΠσ‘@½^ΗψψΈ’}ι₯—μΘΪ‡wΔσgάy睟άΫhΫΪpύχΫ†šΤXsΣ;UΒΠW6ΝδλilZΏτεh“p! ΫWΔΫοΈ²kνΊΎx_Θο ͺ֝(]QD΅κΠώŸpV_ΩLŒ―1}mώ/Qu‘† Τχ!x]ΎήΦ`|yg(.1VŸΛU5Π# δzΊZ†k•ͺ/W¬cηSU9=-ΛγM j΅ͺΥ*B‘E H’„@ €\.‡P(„@ `ΧwπŽ]»v}kϞ=³Žˆώ+₯²οkζοαχϋρΐΨV€ΝΞΞ.Ԟ ΓYό―ǎa,[³%ωΦ―κ}c` λΈΛeŸ©wε@΄εž«zΦ―μφψΌ./$’‚{Y]λ«.€’Tιk¦dΘΟS_ FW€ι"ΟˆŒ@Μ Ž€xέ’»»%[ΪŒVkΚbHCI’h[[l2δχfSι|eΜro$K Όrd"H4!ΥV葐uό‚ΪΪΌP( £Z­Š"7^ήΉsηΏ=χάsNρO£xδ‘G"„h7ϋ‹wί}·m-*•B:έ<χζδH όψ δͺbΉŽ†|©+6-ߏ…ηνήcIΒΈοšή΅–Ζ|>—WxY‰n•™4…  &γά}u“jΡΝ9gΫA`δ=„Ο πhοΑ s^Λ½€=”θˆzύσΉj±R_Έφ9 δ:ΪccΩL‘­R³&•κ {Ξcγ’ΪβΝkλΌ^―ΦJ½££σσσ"4ΧAiί³gΟӎ˜ώ*€Ϋn»νομ4oίΎ}»mI―Ϊ–ͺΩzηlύ$ͺ ±άέΩ΄aΩ«^GΘπ»%B>²Ή£ΗΖΞuα 'Θ[tθV_•uΣ3θ h  LJ€ “κvτ· E·πojεsΤνAΏΫ·Ό3œp1VŸΙVθq»k]‰‘JΉΘ \‚exαθ<ΦwΠΡbŸy\.—αυz…™€Φ]Ή³³£££¨Χ-ζU»vν:°gϞލώ)₯ΘΌ½ΏΏ;wξζ™W*LMM5}ίγΓ²πΫΥι―θo?ΌrΕ’ΓvY|ύ-ώΐΧ.ΩΨΧκ”$’±ξŸ―I«ζτή1W‰UXy4 +¦(u?όzFxYΆA2Ÿ¨b”|#V €φ˜?πfεB΅Αš’Bko‹OΊΐΙL±ΣJΒ{%±±7ˆφ„=(‹ƒ–kJΡψ—Λ…x<Žση…­nέ΅kΧΏοΩ³§θˆλOPΖq? ΣDή@ €{ο½W˜ZΪh4011Ρ΄{Ο™± ώψρΒ>{!tύͺήΧz{ΫΟΩγš–Ξ[Άtmzέ~HͺΉg<Ρ§Ήϊδ—tJ˜O9BΒ4:P–zΒ#}]qp˜ž©šBˆ¬²nΤ Ό`έ•xέΎώφ`¬Pͺ•²₯…ΉX,<ς{rsΙ|3½°οXW „Ρσ7U‘HΔΚUǞ‹E„B!0ΖD/ Ϟ=O8βϊS vνΪυ7v˜·οάΉK—.3==έ¬ω$Ζfςψγ―Όƒ² Ζ/I€±iν’WΫΫb2ϋ―ξ^Ή~ ±T"d‘Φ„3O4?! DRμΏ&δœ"ΠγψgΧGU :φ·ΊΌΐ3;5‘ ž$υ΄γn‚ΖtΆ²`T(ΘFBΎΤ\2Ϋ˘qeΐΛο$qυŠ8’aqσUJ)ͺΥͺPΉϋ|>m”Zkk+¦§§E…CwνΪυ֞={N:"ϋVCCC7ψ’ωξ\³f »ξ:a²ΪnΚn₯²όΩcΗ„Ω}.‰4Ά¬λ©₯%6#:6βwΉ?qέ’έ­Α6έjΛ4»(,'λΒΓ~=ήΰΒz<ωgπCŒ(€ƒ°ƒw8ΠΟ,ώΎ¨ŸΕN@HkΤŽέξρdΉ°Pή@0θ/Δ#Ή™ΉΜ³¨Q†·O§pνΪV|β2αZ­I’„a]Υ€ΦΦV ‹ςΫ΅kΧή³gψ *€‘‘!7€§`Jυ …BΈγŽ;„;ͺΥjΣA•εjωυc83_ΒώΝλϊ^ni‰ cΙ―οΑλϊ6ΗBވlΥ Ρ‘ΏόΧ(όzθ\fοη#{λNΈχeΔ¨ΜΏΡ 0H·eW`gρ ΐΛ½Ii„ƒž@GΔλK–ςt”bΏίWŒ…ύΙ™ω¬E δ«ηFΣΨΎΎ n›&#εr‘PΘΓA„\,΅!§33@φμΩσΌ#Άοέ’.αg}ΐ&σΖνΫ·£₯₯Œ1ΛcffΖΆ¬—1ΰk{NγπdQ@`m\³δU;ΛίχωοίΎdsΘο¨p·ΏΌ5U=w.Χ˜ ƒ"€ͺLLœœ*Φκ·7μ@Μ0žΛΞ`zc‘Ή7£ ρ‹ϊ¦–¨?rΣϊφ%^χΒ£‚‰θμΖΥKφB,„Μ‘©ίsΖΆz³kF΅ΞN+W΄kρφϋCCC«±ύ€)…ψ{ΨΌ½――λΦ­CϋTͺ©ίΏοΰΎs()|mΝ²mmqaŠΩ’Έ/pΟΆήM~ŸΛgb5›FaΤ₯‘(₯²Ϊ_Σλ²τY¨ϋζBKšH41}Eσ[1SΎi_\oΔBžΠŽu­KΌ…•@[[|rΝ²’מ>œΔ‹μ3ͺ+•ŠmG[[›†ΆnNtχA=rΦεώ@‹™ήΎ};|>ŸΑκ«ΠΏY²Οθtύ}qw遞–==bΆΏ=βυέyuΟŸΗε΅ΚΌQ6tήSS„ž©1{fPΊA7±Ω#Mk1X3ϋέ΄ˆγ‚ <”’Aoπ†um½E4θιi^ΪΣ*lβρ7?Εθ”}Uo*•ΆeχϋύQΨΪΪj7ΞνΎ‘‘‘Ρύ€(eŠΟo™·―_ΏέέέB˜8??o ύΛΥώα©Σ ‚ΧΫβ‘ΙεΛ{„-§#>—ϋž«{Φϋ½.ίγ₯žθ6Š…<ΑλV·t/fšψ²εέΗΪγ! ’ŒαοŸ:…r₯nλ Ψ%t΅΄΄h.Ρϊυλνz>ώΕΧΏώuGz? ΰΰ7kϊ-[Άΐνv[όώB‘Π΄‘ηw^Ζ‰9k2[ΠοΙ―[Σ:5ΙΗ%rΟΥ=kΒχB=ς˜™gΰ6©`”1€(υϊDιη_WtΣ…[{n<Ž€iΞ1μ]#0»χ²ώˆΕ,Σ-Q_dKlΑ&„­[ΫzΠο±fœœ―ΰ©GlU{˜—ΫνΦj@Φ]+:ό†αααΫρ½ΜΐΠΠP?€_1oί΄i“-ρ—L&mίομX_{mF@ϊΊaMί~»Ξ=·_Ρ1ΠυΕ Ι•μrΟy#₯i­|—Ϋ[z^ψ ϋ1Ζ#συΚnŠ‹ χπ>Q•S(FτηŒ¦ΈΜ ΤψηgΟ _[ΡΧ~Τ;οΥΛcmK»"έ inΟ£YQ₯p_7q²,kΝy,‡2“πΛ]{U$L;ƈ.”²π£₯'Άžθ@νX…Ψg:²Π_³šΕ»0"§Z.Ί+O$J―θo?"zν?{•šψ­j΅š0ΗΓεri(ΐγρΨΕΫ‡‡‡w:"|™*€G}΄ΐ―š·―[·±XΜbω)₯MΗv½x`'Π? ΜυυuœΣρϊ\ΩΆΜ„Ε/άΊΉΤ €jςu‘§Μ¬δFϊCξν‘wξΠ•ƒφ:'δL`±Α˜Qx•Χ S1ˆώ`„*nU2™‚ “‹…™ž)ΐΤ`ψά…@*΅Ζ΅εκ[q: Xr.NΟWπβ[M AQŠw,ΣPΐΐΐ€έπ—AG„/Sΐϋ4LΝ=C‘V―^ I’, —ΛΩφρOeΛψ—¬7‘DHcΝΚή·DΕ=„²sKηJ—D\Ά~±Α zίkέ|δN}ŒκΑ ¦ι¦l(ο €™¬±ςœZjQŒξ€šΘGtdΐ ¨0Υρ€l˜ΎΏξ^€>„σl8ΟΕjγ‚2ξ!lΝͺ%oI„X.βΏΌ0ŽdV\„Ψh4„(ΐνvk·Ϋ5kֈί544΄ΕγΛL<ϊθ£^έΌ}υκΥΒUΨ­§_:/ΚΉtIλρ`( <πϊΥ‰ΞxΘVnsέ=gFHΜ8²AaΖ5αb–c((“S4Ρ--Χσ“d¦½%3ψλΔ`ρ™Yiƒ0SΉW¨Ί]S(jύΖhhBύΚs -(J((Θ¨?εgίEηα`П_Ά€Ν¬6žήg?8NƒRj1 ό½200`7θ·1ΎΜcμc 1>Ηƒ•+WΒγρ™;λ?>“Η·Zϋv}ž|_§°8€-μρο‹/a: ΧpΈ‰Ζfœ)ΧχϊμιH@ν½GΘ_ύ£ =5 ?ŒŠ@w2`dσ™’£8«―*’³LxrPS χFFΊ₯'άCW,Lύ73Gΐ2₯wΧzΌ――σ€(*πΤΫσ›ΙΫ’‘πz½ZCυώ¬‡”D3g]F.ΐoZˆΊ+„ΎBΦ»―ŒŠ‰Ώ₯‡ν†n^ΏΆ΅ί%I‡φ¬ Π„Ϋ°i&”©@_JΖ ζžiŠA·ΨTΩWΡ„ιΦίδΣέΰ}y9¨yœuΥHBU!` # ”;N #}ΣP—yT6j΅}WM%‰Π•K»‰^{ζεQΫγ2™Œπήΰ+Db>εˆςe’†††VΈΙΧ—.5dύ©«\.Ϋ2cΣy|XZDόΝΪ•χμ F:[B £έ'̌TφŸi>Ύ@ P.¨o†υ¦{Uωκ~―`˜ΐΣ™|B gLεJ4λ)*>8©„„ͺƒ!l¨Nΰ””κ0Ν'·P°b₯^ωIΪΪⓉ¨•άσNΪ6C°V« ›ΌƒA-b …„Ιc~υ‘Gq€ω2AŸ‚)΅³³Σ29†‡vλGoŠΩγeGνŽΩΊ<ΡΛϋφ°₯Ίΐσ₯‘ώ%@ŸqKSΤ’(SίF•BΖ¬€ ΥͺEgg`Q˜!Ό†ˆNVmύ»#™HυcUΒ’1’έρΙ—κ%@E/οn-·ΉFΟΏiΘεr  Ύvν—-Ί‰r•#Ξο³ψάη>GόΌΘϊϋύ~ΛΕm4vSc1Ÿ)γ©CVίΏ5œŽΗ#Βfžk»CΡxΨV'θ¨I2Ζ€fLήa% K ξΟ›Sητ0P. ΐτω=ςvU9hΦ<]A(ΰ_±ΠJ(OŽίSΝE`<υ  2Σ…>7@\9Hτχΰω9Œ¨ΈGͺΛ‘+—dRP>‡κ΄Η…­X,2ίYzΈ=s8‰Ή΄8Σ³X,’^―[ξ“`P&΅··Ϋ…ΑηχYH’΄ ΐr~›ΫνFoo―όkΦΧΥΓSΒΫI»mƒΘ ±nžΜRXΑΚΰ ΕΊS«ΰFqρώΊμΫσa0]Π4Ÿ€2Ζ[Sαr Σ‚ϋΜΌ/3ΈΊΒ!@CvτΨ‰πή_m=yςLxv.ι)—«š›―rκo$τ‘œΒ "+:™©aSΎ %0 ΈVŒ―ΆορΟη-ف>ŸO‹H’dΧ9κ“Ÿόη%G€/lΉίγχ{ΠΚ χ ϋΓ«ώΏhUͺ <ωΊ5ε7ς₯‰¨°ΑG«?˜ϋΒJzΎ,–Jφ‹ΪTWFΚD~IVD"ΔP¨―¨{ώDŽσΙ-ϋ!L;€JΐΗ IDAT£η:oˆιs±6uζ'”ΰ“†x(’|ͺφ DύQ0<υέ½­?>pΖPΫpΗυrŸω_8GυφT{ObLeΦΑ F(+”λεjΦ‘w ΰLΒBEŒάŠΗ#sΡ°?™Ν— • ί|c»ΆχΑη±fƒ D£Q‹ƒZΎ@oo/Nœ°θ–nJιuφ9bύ> €‡~ξ7oονν…Χλ΅Xz½ŽZMi:>œDΆl φυ΄ž²ϋό}±Εάˆ<¦ψɚyeœD¨­q9ΛM(ψD?ΕΪ3δΩsB 4BPσΛ)•ίGφΗy«Ξ'šP€Ϋ‡fη’³πΐκύE(„ζR=τHxΞ‚¨Δ‘ύ)X*WΝx γƒ^(7ΠΧέzΪΒψ—8q.eKV«UΛύΒΟˆΗγΒR>ζˆτϋ€‚Αΰzόooo7ΐ…¬?μ=d5ς^·«n3»/ΰ•\νq J|ŸY”x%@ΥpŸΫ)SόtΥοgΪ Ο ’‘ Π_Χ³„τΈ€ς~”1ͺψ:άΧ"Œr$•9ΤΗ8•ύyyίΧ^[8ΎkσΖ΅iθ|…Α]1¦sІς©ΛΣιRΞ(μόƒΠ u ΪΫγγ·ΛRό£·νέQ5¨Ηγ1τŒ΄s·#οp—Θϊ››@ͺŠΐΫO&_ΑήΣ֚€φΨy»ΈΖ%Ρ„$I“;fΘς΅(ϊ2Ζc”)‰hTd9§&«Μ±ϋΚλΪƒͺ³ρ„ͺD ₯L!ڈj9‚PΝ6¦λi Š0ρυzίώώ+–Δ—mλϋK]νE%jυ{ιߟʍ ιΑ”°zΦ'R•ΌIπM<αΆ/¬$I’ένQK]π‹§³Hη*Ά @ ΰο₯ΞΞNΡ‘«†††V;bύώ(€Ϋ,BΫΥ%„F£ όO o«Ξ„mqyok0‘Ζϊ΅ψ{%`IΣεΘ=fΘHeΪΏ)W·$ /QΆΙΡYc0ͺ*=tHxaΡcξς%†Ο4‘’±‰ŸΧŠ~d…BΨα#'b#³Y wσ‘[ΆΝI>Β%ϊcΘRρˆ1 x2WΙ4£bΨo€δtuΆ ‹ΆΏsN\ϊ]­VΡh4,χ Οώ' »2α]ŽX_b044δ`iΣΤΦΦ&lϊ!j₯ύοX#|α€7…₯‚Ρ€Ϋ {CŒΛφS‰6³Π)/.OΝΦc¦Μ<93Fή—j‰@J؏h‘?ͺ‡ε€Ζ ½ˆ`Ώ@5.0έu L+4Π3 uΎAu+ΐΨ³{^Ά4κˆ}tσζ σz¦ŸΚc£{bjΒωςχO–RbθoUΔπΌΉ‡ƒΩpΐkΉ~ϋΫχ~(—Λ"xBˆ]R£ή° ¦?ρx@@ΨλίΞϊJuμ;cΝkoŒΩ}πŠŽ`”σk5kP~ͺaaΥi œ ρ„ bΙε¨!5ψο ”₯,Œ½6ΐX.’Ή”Q=7@&σUΏš2F,!Dy?rŒŒ^8xΖ2Œοηnί6π{λ*j`|Ι°ρ»¨τΔ ωsλ Z™+eΝBO¬(€iHε”@GkΤΒίΌt6‡|I|/¨."o8ΤIBΪ{vtˆ½ρ‘Gq—XάhήΠΩΩ)΄ώŒ1[02™χϊkMΪ}pg"αc΅|ʘζ0Ύ&_―ΠW_β;cΊ½Υo|έ7§Yp9¨ e*5KΘ`…)a–\E`ugL‡ά `ΟύψΛOΑυΧ~ho eœ!ΌBΑe6ŒΝ€ΚIj„Κo%"`vTm­1K c #Y[7@tοπ( ₯₯Eth !dƒ#Ϊ—Vl7ohmmΥ’7̍?μ*N ΪGω<ξ’]· ς†)ηΨσ!>•3…Υ”„’eΫYϊ{(œbΠΓ~<Θ’_₯ (S(Eθ9ΑΧIE¦ϋKJtΘΟ ‚6ŸLyžxωάΎ}]¦³³½ΣώΊ~βα:‘†‚!¦83Σω9!μ'fβOs!D‘B[%Ž3>―ΫBοŸΚ=xAΤƒvš―ΞYOMπ©ΚήΛnΏPή[±”„ΪψΠΊEgΌ€1Γ>LΞ †Πœόθ ϋ^koPjΙΐΉνΦλ&εοI˜1jΐ(t’2ƒbP ‘„–-Τ²³ΉjQHώ1#ρG €&‘T`h\ΛΧNΫwέ+ζžνννv.©³.…ξ`Έ ~Ώ_σEΡΚk81kΝ ˆΗB³vŸέχ5ΒήΧ›“kΘ‘ ;5xœ ΚΝNM)ΏŒ'βΈθ€œαή[%)W<€' žSΠstΤO)+KWΏσ²εNΏjuoqυκεi5ΔHτvΒ" 5š’£°sΣ…i•°!8Γ,‘ \ϋa,Ά\ΛSsed U[`ΎΜ  ΅UΨΌψJG΄/ °UΔώ«ΓσD€’55' ŒEΓσv yύJΞ,γ;z0cσ½ŒB΄Ÿb!uxΰxNψuΛΜΤP!Υ}~9Œ+Ν'QƒΒ!:a0ͺtΦ•IC’&ΎΊ`{¦X±\«ήqγ„\RΜs„=άGΉR`-3‘pω εJ½tf¦˜ΦΡa6>ΎIΈ­ΦίJb2“Ν]Θ΅!BˆXΆ!ΆασŸΌΗοK£,„K<·θ liwx\j0δΟΫ†—.?—8ΟΈt?%LΗeΤΙ ψΤ ‡uͺ@1ΉL΅Π„ƒχόkD ω+μžϊ:Q³ΔΟ <aeZlŸ)IG„R•{Π" ΅ZO<½Χ’υ²΄#V½bΛ†ήχ§†F$šΏOyT’)EΰΟMεΗ™όc건 ρg±ώJY).v0ΰ/xά.KφΟδΌΈ(LΔ˜Q€MJ°—RκΜΌD `½E0Γa!όWI@ѝ±ή‘°/έμƒ^·Χ υPšoσ~>εκω¨M4_™PƈΑ‚ΕgΘΞΗV4Šζ&hdΡ³Ή€`ΥϊRͺ"ω?½Μ}ύδcίyη΅“‘p¨ΚO!ΡZρΏŽΊ’huΒωr-|<7§DS)”y¨L1 2ΐ@$₯μY=?T1T)Φ?ΣΨθTέOύ’ }–kzzΎ‚zƒΑν"B`N&3?‡Γ˜™™YπΎtΦ{Œ†††BΪ Ξ—Χ Η#le§2ω*κԊ ‚AŸ­ςΉ\Z)―žπΚ8GVΫ«ρ|œ8u&τθϋ/Ky'Ϊ¨7λLu²PCrκΑίgκCνDγBˆΠπTΟρ'ϊC+Pd'EC”' Ώυ̏—X‘ΔnΊiϋ¨ΦHεdU¨F¨šnΜΈp#αHΙ£ηΣΓΜ¦ΜΖϊ[ΐb|+/Đ)Tl9#3’47΅αρΎψ OU«o†ϋvπΏX‡|>―mΛ ―[rQ΅Χ‡Ι²«HjGm’ΥΌώζα؏ή<όΡ›§‚kϋΪ:?~οΝ³Ίrsλ‘Τ4Y½UαK5λ½=”mͺΠΖθω‡DG†#¦Q`Œk~κτp|ί‘αˆωw?ΈλCΣ-‰xQ‹cθό8Lγψ'ΪΖ€Ωtifd”1ͺ Ιdι•£e Q$n;³"#:πϋ}BΌ_,ΦΠυΫ*s$€_6Ι@}Žx_|`©Ζ໘/œ-(ˆΒ=`~ŸΧvL°ΫE\Z›/ώ~`J{kuθ…|kjqΑrΉ"}ηΗ4Α:>:ηω_σ_=+{žkθώ[g>tΥζy―Χ#ΟΩ#zOn‰#dΤO4ˆΝ΄Š;¦M#\rnςˆIθΒ‘/”\§NŸMœ‹NNΟNž‰B`;oΎv˜#1UFφn֊ΘͺZLω6΅­½~&5¬ϋλκHAYΨ•ΜAN Eΐ™\&xΊΆΣ}>OY± ΔxT…+ΊgΜ €oμήtΦ{―:­VΫg«μ@©,ΘψrΉͺD"ΆΉε’DT#Hd΄‘ŽΚ’»ό(½οe³¦Υϋ€AvμΉ]TDvr,s6_W•S$]B)υ9§Τ―« 7/δ’±θ‘ηMu»\•Z½α7ήβόs#!G’>ϊh`χξέ%GΜ/ž°€aΩΐ±¦«Z΅2½’x±‰Ό ŒBνΥ§φΈ7ސ…—Χ=μiA9-ΏN§<ϊ·O.Y?°·γ‘ϋoΈκΚΝs’DΡ§p0ΒυΩ3˜]Ζ’)₯ΔFfL•¦Z½ŽΧίx»λkίϊQί±σsΕž§Ϋw^V)0 Σ­½nφ©:3 \Z$Γ|Ά2d,7 ½©eΨO,dŸ €ψ^"Ψ―½fQ„C<^«jV•j½)ΐ[}3ΰλχη˜#ζOXβ/*(Ϊvš<'Πώ^·ToφΑzπ™h° t$ ‘β†ι9ΐωk?·ο₯7ڟψΑώ–r΅nkqΜzη_zŊΊ~ώΑΫΖ6n\›”a όη›xB£Ο`ܘQ ₯ ―½q°σ+O>·τψθόiΚ·,K­\±4ΙΜώΎβPCΣ3N$Ή­ΥiεΥ“s§8_ί,ψΔδίΫYwμ·°ύBΐGh²kΗψ‹ϋφw~υι}ν9K ƒg&ύωχ•7l^–Δ9ΏzεŒώYZ% ‡σγ†vvβδΩΔΏ>φUožœ½›ύΡ;n:­°ώ)G ώ00C»(c”g“οδˍjΑηέ†μγ…έμsaNΦΤ nI²H{ΉΦxΧ.€ΫνΆ3:QGΔ/Ϊ)vmδλ !ΡΣt6]­AL/τΡ}ͺĝήοšΘΒ©3b ’Ρpυ;wŽ~ψ¦k'φΎΈΏϋ«ίy±C”o―}‡Ξ…χϊ‡υwέ°1υ±ξινιΚσρ~Ώ¬xαOg²ή―=ρέΥO< γݞδ«VχfΧ―_=­Ι7Ξ—Κ …J<ŠH@ŒLη‡ΟΞ3<τηcώΌ€@b°D(ψβΜΖ7 >5 =ŒύΦ{€ύD7’ΛεΥ™ΏΈ ΐo%η$ƒŸΆE Ϊ…,0œR£ ΉV›³Η4 `š'Λ!m6ΣΖb³P(DοΈύ–‘7nρήW»Ώό­ΊJΥΊ­"xfί‘Δ³/?τ‘kfοΊγζs-‰xYύ©Z@™ρχς+oτόν<³r.Wjzž=.»χΓ›''f’Α—XάͺϋοΊι$Qš{’Χ1Λd£>·Œ?{jωγ\Ά2σκ©δyφ›‰?Qi°φ¦Α’BfίΞΓ<™›Ohρυ›ά'‹Ή‡l€ίρ‹«\ΛέτΉh‰va LŸ(Tκ5P0F˜šΉΗ(Q‡’L“ˆ!b`Πε&Ί„°P(X»ϋŽ[GvάpΝΔsΟΏΈδ«ί}₯³R³ς”2ς•gχw<ωά›mΏςΐŽρΫn½q8ΤΤ;5™ΞψώυΛί\ύύΗ[šύ†€ΧMαξλFvήrύ—ΛE?υ»άjήg]{~λΖΑ#{’r Z œυΥV±άΘ½ptφ3 :U(IαO(kξϋ‹bό’P ­Πσ―1y–šά'‹Q’Φsxοί8 ΐJΖΓ–œY<|“D]jFfˍS[0Εί6ŒχS"b|Š­ΐgκ˜ FΤp‘>8' W>vgnΊιΪ±gΏγώ'~πF‡u*Χκί?ώΓΎ§~πZΧρρ]g·oΏjμΘΡγνωOŸΝΪ[}—$±‡ξΈfτž;o9GΛΨ7ΏύύuΕJΝ’PΈ{ΗI"I nΌΉa !QΙn~9P5Κ?:2}¨’OωQ££’Œˆξ/πΧΒζs[$ P―ŠεΪΊ₯…€9»t†Ζι x‘@c1k!4ΰσX―S£A›±‰¬ή ¬\§5ΏGrA0₯€OŒ—ƒŒ(|f$«΄’¦ΧΦΪ/ώCχΏyΗφΡoηωeί{υ˜­5Oζ=φί\3π.™Ιψš°o]1KŸΌϋπ’%έYυ„ε EΟγOΏl{»Ό+^Όζκ+†‰\¨rκh2¦Νϊf όΠƒzƒΦ^::s0S¬W`λEΐΕσ-p_ύ«JυBlώ…=œRjΉοΌiA J^%4Ÿΐ…ΈaΏ uXzμ¬Ώϊr₯^φΉ½A-ϋ–³RΪΌ¨Σ2™f' ΄lƒUcDσεU%±€·;χ™OβΫΩu.ώ΅oό`Ε«GGl™εfΒίUϋ/ί}dϋΆ­£ά‰bΑ_ΨΏ,]¬XΕwέxΒνv5ΤφΖ„˜Ÿ( Π‹š V}εψάΫSΩjAf΅Š#ώHΣPYφ[ A‘Χ•¬αυͺΰΪ†ύnχ,**`ΣiͺζˆψΕU₯E^ˆζΐkύΥFΓΫ `Ωb­ yΩg„¦L™cΙtςLΓ«Lθ«fς¨―εςί‹­\±,ωG»cώΰΑ#=ΉgερΡΕ'πά³cΣδ/~ς£oΗb‘²j¦Υω}₯RΙύψΣ/ρ"ΗO΄μγΘΝs™’'φΧρp©­%QhmεΫZ[r-­-ωΆΆ–ΌΗν¦A½Ak―Ÿ=x~ΎTPa?QΔΈ’oΏ±ΗŸ“ΠΈmF«HIΤκΦkπΉΝ\€p²/²(Ϊ]sγ†f ΠώυzΓ§€ω2‘π@ΊP--i ΕugT`51GŸμ­ηοͺc±”ΉΏZ"UQ {•I[r,ŸΒάΊirσ¦υS/ΎτZί—Ÿ|~ΕL¦θY€α§Λϊ{>Ÿ―ͺd(\•βή_[6›-yE‘‘ομ=΄`E›$ΦΫ+¬ξoΟλαtένŠ$Z¦cρΆιhKk†;k’YZ€δkͺΔ(ΐΘ{d€ZZΨy°hi:οˆψΕUY«ΰΦmή†©E,δh}J₯ζχϋω‚ Ξ½ΨT¦Rά gΓ1­.^X-N…’Đ΅GΤΙ½DcΠ΅βFτ’"™`P sΉ\ψπŽkΟ]}Υ–ΡονyaεcOΏ·OΓΝ Ο₯€S~ΊκhiΊrΠ<(₯ώ€DΤΦ_J‘’Βα`εΑϋο8rΣ ΧœύΖS{Φ}gο‘»uz2όŸ_όκΆλ7½²βηξŽΛ—χΟΏΈου•ΙόE‹USJύΉ\Ύ?—ΛχOOsΣx ˜ΟηOF£‘ιp$:ŽΖf’‰–ΩhKΫl0)›RpuΒ LαP)U„Εϋ‘€·© Π,`§csŽˆ_\`iσ¬ŽύV/±l@Ψ‰K2H±TΗbΆ]ε–Ωj!Πζφκ7±€λ=φ"MιV¦Tπ2m¬—$*‚Ο0J˜–ςK4·hooΝζϋΔώ[?Ό½ε+_ξζ׏%μNΨK‡‡Ϋ^>ς»ΈuλΩΧžzJVH₯\n-—[ggf ½σ<O>‹M‡£Ρ™H,>‰·ΜE­s‘H΄AL–Ÿη X±T ‹ψŽXΨΫ”7βο#³°™4])•JGΔ/˜ΆS*L3Wq‰|:‹`Y«gζŒmΑJΕJΔΖΧή`*]Κχ΄βψf 2 ˆ6λOƒςŒIŒ#ϊΈb!ΖΉF+Ηψ Bn¨¦βaΒ#lεŠ₯sόΩO?Ώυƒ}_ώ―lžΙμ,ΪΟ½΅|‘“;ΠΛo]Ώt²£-QΘζKžt:›žœOF+ΥϊEi{]«ΥΒsssαΉΉ91ιv»K±Xl.ΞEcρωp<1·$Γ±xAνΔτœ.f§ Š₯ŠeΊQ[ ΏΘh˜ουž3­ρ‡~˜9"~clΒ σ …‚¦D€ŸMΚ&6τ„, _δof6+ l:S)Vλ΄ζu»\Z[MΨυΆ―qΟ]·ΊvΫ•'ŸψΦχ―|zο‘₯‹1GΏrߎ»χ#oH‘ZY³’ΐd²4ςΓΓ3§ͺ Κβmν$ή֞p zE‘”ΊR³ΣΩΤ\O>“ιΞη²=₯b‘;—ΟwΥkυψ{}5 o*•κJ₯R]ΐ°~oΈ€z,KGc±T4‘H‡c‰t$–Θd KBUGΠξώζ˜Iΐ|^HφŸuΔϋ"+€έ»wW†††&φYόrΉ ΏίJ©₯<Ψ\Έ»έzTk Ή\ ψύΎ’€Ξ—²έ­ΧΚZ±άŒ1BTΐO΅LT’MΘ#r—N%%˜1πƒΦ`Di;¦ Έ\λ«#ΉU:1½ω&a­­‰όυΫ·žzσΘΩΞ‰d~Α܁j₯βrΉHPzs†r΅‘;t6uμθx.­P+*Ιfιι'IZ;»§Z;»gβ•C±eηgz2©dO!—ιΞηr]Ή\«\ͺ΄q~ί²Aέ©Tͺ-•J΅aXW Φψγ=¦k²U‹YΩlV„N;β}ρ98Ν+’© ΐά¬ΑyCW«ψ&Θdςm~ΏoMͺ“ωj9Wj”Β~—Κt Ιε+­΄ΙV‰?&ι1yΘΟΥμZΒ1ΞT” ΧTuˆ<μCo &η)€³Ύ―ύΧ3W?½οπ²ΕžΜΗΎχΪ¦mΧ\q|ŊΖP-œ}αΨμ°Z.«Ώ’ι,lξaΪ †ΒΕ`(|Ά«ω0wŒT―Χ<™ωَljΎ;ŸΝtε2™|.ΫYΘΫ)£οiAΛc zt΅…lΡ’˜‘%c ιtΪύcμGΌ/8 `‡’΅΄΄ ^―[ϊ΅Ω5oˆG|θ‹ϋ0š62ΊιL±­³³uTDΚ2.KιΔ|1½Ί'‘Θ1(ˆˆ–τcθZ£ΞS•Ρgφ)9CD}ψώςqͺ‘# *GH(7tν}ρΥΥώΔσ[ηs%ίς*δ±zφϊOϊWώκεγ³§ηςΥͺ‚h»/Η)ˆIπΉͺ>»„Ή7'ιΈέžzkgΟTkgŠ$£Τ•IΝ΅eSΙΞ|6έ™Λ€;ςΩl{.—kk4¨χέά,’IτΖΌHD|B_„ΝπΏP(”ΧFόˆ#ή—F³ƒdυz]HδΈέnaμφΊUQ|νucd1™)tΦΈ’ΦθCΉΡG抹₯‘KrAfLΞ~ՁΚ0F¨2Cφrw~IC~2!¨( Bdd F”> ŒILσΡ!`#ηΗγ_ώκSΧΎ~|¬½ΩI[Ωέ’ωΤ'oρ«OξΉφΨωYCŸΒWο-ύΫ3ž•Ά–AT9U2”A§^ΤΐT΄C΄2bΣg›κΘe2-…\Ά-Ξ΄Φj΅&n± €λVΕν‹Uα§”ζ=ΟžRˆW IDATGΌ/°hΪT*e€k’&Ž"°¦?˜@©R  ΕH0Θ2Οk0F'ηK©ΎŽP«ζΓ+q{ͺBwN€™R$*— K’š†ΐα—‰=IATžψ£ΟTΈ*ΏNΛ•ŠϋΩgΈω±g^Yί ΤΦyυΊ\_ΌηΊ·ξΎσΦ7Ό>oΝοχ•wΕΏ>ha°Nžx`εΖ­o*U5„Ӂ2Ε)γ(‰K`2(]Π‰!q¦$nΜyΌΞΥz*―…c±\8+0`Xι , Ήl€M΅δ2ιΦl*ٞΟfZR©t’V«ω%―_ξ2Ξ­΅b "E3λovΤ{~PJί€Žx_pΐβ“'“ ”B’$ΤλuKΧV―Χ+ έ τˆ νζη³έ‘P@ιΕgΑΪzf¦ξj D=ΚΠ₯g8#rMNδΡH>¦±χ„0F©œΤ£δ#©Υw”*ͺ@έΈ¬xΧΰΠΫG—όΫγί½fd&nv’ΆoθΤCχώ¨――;ΕZ©5 ‘Φžη‰x*•Ύ†ί7—Λ]55z»«oωΈ^kμκΓUυι €†Ž3σ˜± Έ²Υ’ SGˆ¦8B‘h>‰;z&ΐ)†Σ§F:76ϋ!σ9YjsΝyHU’ΘΜ̌YψΑ{ΝνK€熆†Fΐbj4ΘησˆF£¨Υjΐγρ@’$ œ‹…½ΈΊ?ŒΧΟΓ:3σΉήώώΞγΚν`ώ©ί―•:₯/=Ϋ›πΊώp,δ Ί]r};  Ύι΅taΞ+ω¦Γ<±Η$9j@K&S'žόξΥ{φΏΣ΄h§=,ύςƒ;_Ϊqγ5ο4(+ΟfΚ3η¦ςγΗΖs)F]Άzέc©ύ―\cΖΛgή9zGWί²RδU‰B(σM%bH_†J‹X ½AJυΙΘ/аˆ΄-ί]D‹†πΓ­±tΎΪg†Wυ… ‹ύYlW«U$“I³πƒ1φ’#Ϊ—ΐ~˜f±₯R)D£QT«Uαμ6―Χ+ΜΰΊn]‹Ed ε–b± }9Σ­ΒΜсcM– £ΙbN’$Φχϋ;βώP,θ ϊ½.!@†ξ\¦1ΔςΑ΄ΜBe˜Ÿ)eŒQF±oί+kσ?Ύ"[ͺΨ&έά΅cΛ±»ξάυ—Ϋ?ώΦιδΜ±‰|’Re0clΩڍGή9tΰT©T6L΄›½΅Ρhό“KƒH” […τ#MI>5—…žπ_‡ *ι©τ °“~πِΜέOπ”‹εhΆP± ‘Ή~½xDƒyΆ€P‘&/ό”R`Ÿ#Ϊ—VΌΰ㆛wn¨ΧλZ>οΛω|>‘X»¬ΐyΛφι™δΐ²₯έG¬ΦˆXšα«m²(el,Y*Œ%K9Πxΐγn‹ϊόρ ' z~―Ϋ#IZ8©=Q†sΛΦ_ϋ­_λ΅W΅™π{=žΜ¦­[>ψHkgΟ΄>O“€ΘYΈΜ8GƒϋvLGέ½½λΕ( <|ΰ:³η|zΣO%b_œhεΝzΔX€iΈ_‘θb.›Hn1oσΉ%l\!φ ψα²Ό΅7ϋ"αORJR+e}ίΌabbB»€εrY+V>ŸO˜νς»q•m–ν©l©3›-Δ/φIi4Ρ7^ΉιωgΎ5h—7ί›½½½?ΪyοƒcΝ–½JŠ !Μ0ΝGm‘Gx™f«6_ω‘€‚ŝšΫ₯υ-ar6„ Ν9Q“‘gMτ¦‰Wa‹S°ΝW6ShMηJ–©ίχmmCPΠL’$x½^Γ=bg@Ξ=k”§ώόΟάiϊ>)€οšo―ΉΉ9θ+•J I’Ε5Πβ-]Βν£cMς'^™δlΛσίώ―ί9~τΘέ”Ϊ§»†BΑρk?|Λ_l»υΞ/ϋC‘’.aD`Αˆ:ΑΓ€9 I$ψύΑj<{Α||.—Ώ6—N…‘$*Θe°ŽTbΔψωΔψmθΥΪhρ‹‹/±›»κB­ίοή#fnn™LF„Ύαˆτϋ€Η`J bŒAmEU­V…eΐ‘PHςιn α–ΥVΤ=ΜχηςΕΨβnP² Η ΫcyBΡ|½^σ7α;*«Φ­{rΧ}ŸόӞ₯+Ξ‚θ­D-V’10P9*g‘>fΪ$2θνΨ#ψMž“‡άjΒωκ$w`9;r΄nα9KΞ‘ƒΨ3‹!v”}tλŸ+΄Μ¦ς«Μ{xu =‚κ?• 1‹?22"„ψώ"°hΰσηυžΈ\.βτρΫΆ [μ‘α‘ιΝ&BM(Μ’[–ΰ1o²εώΐŸ―Ίqλ‡Ύ-ϊΰΆφφΓ7ίυΡ?έ|Ν {\7Υ3*ίƒˆsfbξH²rγΦ.—Λmzrς=!Hέ„(_ά™Πϊ›Xμ?±ψœPίWɜ"‹΅ψZ‘΅²†GfΆ‰.ΐνWχΨϊώjυ0‡Šk΅N:%‚_ϋάη>WqDϊύUOˆˆ@5νWmbΎΘ’D!XΉ$Žλ–Y&c6UθO§²&²Jk iχ;[H‘hφ―Zw¬³«σws¦·nΫώΟ7έuί?Ζ[Ϊ“ &ΓΞt&φrB§t̊ΐεr³Άφ6‹%+•JλΗΞ\ΞyϋDΞΠ]…YΠgÊ:t{+φ΄?α•*Yΐ‡`I&³=sι‚Εϊ_·,‚•ύb ' Zξ ΐΪοott•JΕ‚ό‡#Ξο³<ΰMσφρq9rG)Φx½^ۈΐύ7φ ·Ÿ>7ύ!fθΡΙ‘]‘―ʚ >1‰Λ–ν7~Ϋνv—–-ۻ뾟ϋ‹εk72[M’OαΆ:ίf¨nuΗ•AΉ†²^ €΄tΥZ!”>yόne½2F₯ΓLn1Δ’΄*¦‘nΔώ·4Ρ¨rφτΉ©›DίΎΔΧλυj±ή,—Λ–tρcǎY„ŸRz¬X,ξwΔωύGπŸζ gϞΥXά|>oΡτ„Δbβ(Ϋ²ήnYc}-W¬΄ŽOΜ­2ί‰Δΰ‡k’0]ΨΔ7Ρ… αX"³λΎβC;v~ΛοT¬œ!6n1±²qΰ]™EbP ’ˆ΄dωκ‘`0xT€¦n+ςAΘ³M„.+pœαU%Ρ0Q^g€ΉΟ΅’šˆv›Ω˜/U-Aώ›WΗ°|‰ψ› zO˜sDfff077g)ώπwύΧν4½cμ+ ΄m‘PΠrj΅š πω|Ά\ΐΗnZ*}φόμU•J5 Όa™N¬©BΚ»°Δlξ‚ »ΟŒBeΘEδzY„ή,œŒ+ηUώJœ *J€I€ήΎΎgΜ_’R:ρφ;˜$Χ.Βξ‘:FΦίE¬Ώg1ηΓ|jΥχWaΚεjθάΨόΛMFψπ€­οoύBP©T,€ρ‘#GD•YJ©/π‡ψ‡³ž2o?sFοΟ καρΈΨ?μj β‘νΦΜ±zƒzOžί.ΐ¬axΓ&|qJAαΫAύ(@EDώLw ­ήrΥ‹.—d9QγηΟίΛ(“”Η’ɐ‹?ˆHG„"<|³Eb  τΣsβΤΨ-υ΅dς<΄­ΓΆε[$nWω"u₯R) ‹*ών _ψBΞεΛΗ€0o˜œœΤΊ·V«UabΧλ΅%oΫΆέakςΘlΊ0011·Š1λΟY<­ήJ6³|M¬<±z`°ψŒήΙm“όώP­³«λ{ζ/QTϊO9°MnΒA,.΄ͺAγop C¬Ÿ~ž˜Π΅βΘVSs||fύ|¦h)ϊιŽxpΫ΅}ΆΔοϋ«j΅j ύ9rΔ"ό”:€ΏrΔψ2Sη‡΄ γQ€ΪΙΕ|ρ‰„0<πΰΧoχΦ<5<³½X,G eΉhU)†ρΑ6£?;hτσ’`hμΑ$#Ii; ­ΪxΕ3Œ`?wκΔfEΒύΥΘEWA¬nγΩAƒ‚ BC„Δ`‘PŠŸ™έ)Ί6ΏqϋR„ό‘@F…|P.—³X“'OŠ*ΒΎ0μˆρe¦~οχ~ψ’εΖ=wN#vj΅šζr'X;W`σͺ6άΎή:u«A©ηθρσ·Πu-`ΕAš+ޚ ‘½p³zλƒE!HζmŒ©­»wΎ΅­Υ’ΫžΟ6 Ÿ<ΆQΩΧκZΏ«$Ԟ’¦ά…« …u=>zOƒ2KΦδGΦ'°ie›πZF£QaάΏT*Y¬DΒOc8"|yΊ „όL£Γ(₯ Άs6ίΡhΤEH]ŸΨ΅\θ δŠΥΆγ§F―'bJ(τL”ŒΓ¬½@0˜ ώ3±j^ΩO2Zjν½,(@ν³·rύ¦oŠΞΓ飇?‘’Ζ»‚Ο‡M‘)* ΉΝ\fBHΗOžί•/U-3»Βn|bηr;„(tυc~hffgΜ%ώ|ύ‹_ό’ΣωχrU»wο.ψkˍ{ϊ΄†κυ:ι΄%ώKA{{»0E8τβ3χ¬~ζΤ\nΝωΡιυ6–Π"τM Ώ–­Ηl”€n]™ IsθΟ$bώ ”φ€ςs¦ &?–,[5Ešk&›έrώΜρ΅²¬ξk¦œD(p©”†ί.r ϟΪ:=Ÿί"ΊΏuχ αΘoBZZZ„Π?ŸΟš~0Ζπκ«―Š„ΏΑ{ΨίΛX(λo`!N)ΕΙ“'υ9“Ρ Ÿ9,hη ¬]šΐ§Φ=32{ύμlΊoρ^Σ› !΄X]&1«₯70ώ(B䈣“"­ή°ωλ’ίzβΠΫ?oEΌ’!β胑θ3ε[΄ΐϋ33ΙεgΟΟν}―_ΎΎΛΆΫO$†ύκυΊΕχΖδδ€(οίΏτ₯/pΔχ2WƒƒƒK{φμYνb3Ζ07'ξΰ”H$„υαpΗυΨ±""„ϋΗNοJ§σvBOΜ€±„Α ΐ@ιί„HR…P!ϊμ|u]!…”˜Bδη„I«ΦŒΕ’‡ΝΏ5›Νn8wόπγϋ^Ωπ€£dF%DœίΠ,Š$•ΚuΏszς~&Έn\ΑΧ‹cώ^―±XΜ‚ψTw/ω­V«xωε—EΒ_π8’ϋΑ@`Œύ€Y³―wδΘίή›GΗqίw‚Ÿ_]]} €‚IQ’‹’lE–EE–m­νŒ|g;™y;;ž·»ΩMΏού}Σ|«T*( Žjaoo―cT@ΰ >ύώ1 †%§ OΌqεύω|© Kxς™ΎO[¬τΔ΄yυ·:ΙHΐΎς;h‹‰AΔΜ x3YΰΖφξNίοΉS―ΜnΊ¨Y,7e vΙΜIs"Ή|1ώϊ™Ι;9ύΓ>σώ1|σοΕqΊ»»ΗΕ—J₯¦’Ÿίόζ7( N-ΏΎόΨcMw »IΰsŸϋ\ΐμηgggΝRaXXXp,E==ΞCv‚> ό‘έyš›Š¨šξyντ•Šε¨ΫΝΌ„Ίοn'7%φXWYBH# 9WΥΏΑIΘlΕs„Esΐˆ ξΌα\4m(·ŽD"―ίtϋΑΏuψlό¦ 9gaΝβ_(”»Nœžό„ͺιM)›A‰Η?² AΏ³σ6‹9Ζό5Mkšν—J₯pόψq'Ϋ ₯τ+؝πΧβ:tθ7> Α0Μd2Η-Žθ¬ΧλŽ™a’$AΧυ¦Κ0F{Άωρ‹“ M©ΎΊNΕωT~4τNzd©d«tkj)ήlM88ά_ސŽhζ΄ͺ l2 (YlEL΄βσω¦\Όπž@ pι¦Ϋn恻ξύI Ν;™6πs6ΝΐnpΟ{4‚δrΕΎγ§.kUΣN޽?θ†Ά†]νώH$βXν7??ίφΣ4 γγγnύώ>ύ7σ7':°έdpτθQνΠ‘C—|ΜzΎ^―ƒγ8s…WUΗ9Vz½^T«UG-‘+,cG·„ηΞdšžΣu*&ς;ƒ~yΦηυ,•2Ίμ‚‡šΪ¦|| Έi!‹Ηo6ϋw*β‘H>œ>pΧ=?ŒtΕSŽ~„FR±Ÿk>ޜ4δτ€,,δOœ™ϊ”¦ιŽν›ώτƒ;\γύ²,»FtςωΌ™Κδ΅Χ^ΓΩ³g*ώ~ϊo|γΟ;έ„χίYBΘ]vXΟ/,,`λΦ­&θ«Υ*Ό^oΣdB|>J₯RS‰(°ΨAh{TΒ ηH€R!™Κοςˆ|6τ-¬τΔύ”uΦF“Γ‘³½˜Βέαh>G+9$λNBμ«9gYΕ9 Ι›F% Ž$ΰDΣ3σϋN'f?ͺλΤΡϋΌοΨλήα·――Ο1α§^―79S©ž|ςI'π<ό«_ύ*ہμ&%€cǎαΠ‘CΏπo`kGžΝf±}ϋvs•¨T*ƒMΞ?ΦC©‡v鏰-$ΰ₯σY§₯KeŠ£Ίͺ" L/ΡΫήxΩ‰YβF@sHٟ@ΝH„ρj³ΩO`?{ΞΑ‡Α5ΎZ†Zε'3χ\˜L=δvŸόoοΒέ79‡cyžG__Ÿ£έ―λ:^½Ϊ@䊒ΰρΗGΉ\vͺψ«θΊNo½υΦ³―ΎϊjΎΫMH†)>tθΰέΦσ¬0ˆ™ΜήMͺ#k!ζFƒ}AŒtyπΌƒ&€δ •|ΆΕ‚—xžW[―ϊΞ΅EΔ±ΐ°αJΥœ:8έΜΐƒ΅Z3T@oΛγo²αيO΄Υ±hͺο GH[DP―+Ύ'/~d.]Όέ‰σ>χπξάοάά“γ8lΩ²Ε5„;77ΧΤθσωηŸΗεΛ—ΐ]ΧeJι;u]χˆή|σΝΏ<~όx§ύΧf#xΰ^π}vS §§ΗμΜFŠ9u ²,7ُLΆφψ±»Χ‹Ξf ;(ψ•šMΞηv|œwiΏΐRnka¬-r@Z8ώήΤ(ψIk=i΅έnwάHsϊ1gΡFšΜ……άΐ‰7.n©’8–π Αη?<Š[nˆ»κB}}}ζ$(ϋΆ°°Πτ»MLLΰ…^pΏu/θΊ~'₯τwχοίς΅Χ^›θ@x“ΐΡ£GuΓψŒύο'“I šφ­VΟσŽNAQαρx Š ·Λ‡Ϋ†Cxυ\΅™TM—ηζsϋ4E!ΡHpš¦Αξ „φΔήfΧ¦΄ ?k1Λzνΰ'„ΠF3ΐΝˆΦ G&ψ-ΧΗι:εΣο>w)ωaM£ŽΕϋ1™ΗŸ|7npΙςcΰwkυžΛεΜ*P«ωχӟώͺͺ.~λ>€λϊ'oΌρFύζ›oώ—':MC \=tθΰ=  TU‹Eτχχ› uΉ\†$IŽΕAK‘@$θΑΑ]1\žΚ!YTM‚bu`>•ΫπIIYφέοC ez1±;χ‰ˆc~1Ÿ3Τ{Je·šΤΩ°¦sVπS€ΛdςΫNœΎό©…lyΏ››co―ϊρ=θVώR©„d2Ωt~aa;vμΐΎ}ϋ°{χn #ƒγ8d³Yhšζδ₯”θΊ~Ÿ¦iΓ{φμ9|κΤ)­ηε Ή^ψ‹_ό"ΟqάSšZHνέ»»vνjΈΉΆlΩβΪ2¬R©49•| u ί2'^O·ϊ"τΎžΠ―w o}N’„ŠΠ­ϋΕ9œ²Ψπl<—1³Ηςz§ΰ΄υn˜ΎΫNςA#!5>vΚζsΣ Έz]ρ&.Μάw5UΈ-’Βή·/†?°²Gp΅ω™Ϊοφϋ°œ~φ{Š’A" ΰš¦™΅ηΟŸΗ‹/Ύˆd2ΩJ;xJΧυίωαΨ‰l€ΏόΛΏΐβ0‘¦AqΔΦ­[›Knέƒk΅fgg›FH›–:ž;>ƒΏyς Tέ=ς'π\eϋΆgβΗ9B΄f%@oϊ&ΐ›£Όa#ƒ%㑍­}ι›3½Ν EU"5Τύ+“WoΏ2“9€ΊΔφ€η>ϋΐ ήyσVΧ>η<Ο·tψU«UΜΞΞBΧusψ‡[©wσoΆ˜V©TpβΔ <ώψγ¨Χλn$pZΧυ‡~όγ_ξΐzƒ›S θΠ‘Χ|άNF³³³θλλ3O)E©T‚ΟηkΚ`ŽAΏίJ₯βH„Ϋ·qχξ(Lη1_rθ”Š™\ytv.½£΄ϊR†ΐ’άC­?Σg‰{³N{ρ‘k]“?€4₯7υpuR S“Ι§ΞN~2•)Π)έ~›=qιc{°oG—+ψ%IΒΦ­[]ΝHYΧu‚€@ ΰψΫΉ!¦–‰D°mΫ6LLL V«9‘@λ{ϊάΉs³ho ΐ’ <  )ΛK–eά{ο½ vεRš€λ˜››sœ?`ޘІ§~5Ώ{vϊ“o<’Ωj ?ώAΰλ+ώRZ<Ζ›ίL%&ΝS ήΫAˆYžH›Γ‘v’(ͺgj*yϋL2WMQ»ZύΏ!ψτ=[pίνύπˆξλƒΟηs-Τ²―όŒœΫΦ#"›Νš™‚,ϋ3•JατιΣ( ¨T*nQƒ₯τγ‡ώiή\`rο½χ>ΛσόMvΫo†d2‰mΫΆ™+₯Εb’θάkŽΥΨ+Μ,j>v„qΧXsσE\Ν»”Υ4έ›ΝWvLΟ¦o©”+$δ=©βζψ³:ˆ³ƒpŒ 4Œύz3€4UZJ}]€„$Ÿ/υ^Έtυή³³€sε}šξξΐm>όΗGvγ–έqNJ>&Ρhρxά1½—9n™O¦]πηr9\Ήr˜ŸŸG‘P0Wz+ιΔb1δσyhšf>oΣ$]Χ?2::ΊH$~Ձψ&Π - ΰ_άμtΓ½σοl}™LΖ¬acΈΐ­ΪΕ‰'N§±°°ΰ4&ŒΙΝqܟ>|Xο@}ƒ€Aύ^Πίt“vwγΞ;οlZυc±’ΡhK•:ŸΠ•,ͺ*ώε73ψ‡ηgQVΪΎ_τίs9ρŸ‹Eƒαp`n±―Qέ·D ¬?†κFSΐν± ύe@)H.[Ψ²Ξfrεέ…rm„ΆYζν9|β>άsΛVψd±υk}>Δγq𼻘Νf±°°`t p5Κε2Nœ8sηΞ™^Ά€x<ŽΎΎ>pΧ@LEΑ©S§077‡T*e&Ž9Θ9ŽϋέΓ‡W:pίΰ`ΐ†&ΠT[ΪΣΣƒƒ6‘@0t­8³ͺ˜ιtΪ5TΘ$_ͺγ™W§ρύ—ζPΧ–W(π|1π\ Ό“‘ o*ρϊ‚Pwρ89ώιa@’(ͺ”Λ·δσε|±²=_ «ͺ\ΞuJ<ΑΗφβή[·9φνkπ pb±˜λθ6λJnmιΕzώ»ύ―ΌςŠΩΒJΦcžη166―ΧλHΊγμΩ³˜ššΒΥ«W+E y ΐΓγγγσΘop0Hΰ·ό €·Β7ξ IDAT]M@–eτφφΆτ2+Š‚T*΅€6Εr/ŸJβ_œE²€τ_‘’Θηό1ι‘„΄Η#ζ$QΘK’X’DΎΒ |γΉ:Ρ™² jΊ¨+ͺ\WUΉ^WŠ’«55\­«]εͺWT-²ί­Η/ΰ‘;·ΰφ½q}K‡β|>Ί»»]Μ4¬ΉΉΉ‹(ŠfB>ŸΗ«―ΎjΪρφΥίΎ§”bttαpΨ‘€ΕvσΜwΰζχ0ΰ‘ρρρsΨop0Hΰ!?Πt§F"άuΧ]M‘žηΡΫΫ뚐b΅Sέ:Ω₯h8}!§~3‡.nΞ Twρž[zqΓH¬₯gίΤd]]]-ύ+ΐb‚Οάά\“Ε©’“½ώψργ&ψ[ί ’··ΧΥ/0;;‹3gΞ ™LΆ"ψ}3υŽ=zήΘψϋuV«UΜΜΜ ―――!M)E‘P₯Τ,FqT% ‘PΗ΅Z1 RαΠΧνǝϋβΈwoΒ"Ή2•y:σΰ£wnΑ§Ζ‘wlC_·Ώ₯gŸΩν±X ρxά5±‡}Ο™LσσσM™’(:ζ¨ͺŠD"|>ίΠ¨=!™LΊ›MEν ‡Νπ Sχ(>ŸM$‰Sψo` ΐ’ |ΐη€ ˆ’ˆƒ:φ τx<ˆΗγKfiš†L&ƒ|>οX^μ `&UΔΉΛYΌ|6ƒW&‹ Χy85!ΐm~Όc,†±νlν €vί»8Œ%Άtς‹]œ’Ι€ΐΰυzΏσιιi\ΊtΙ$ƒ₯V|·η"‘FFFL"b΅VνξΔ‰H₯R¦CΕ₯ϊ§ό£>Ϊ!€M@ψ‘ΑΰM7ο044δΊ’Ή­φŠ%ŸΠe’ΉXQ0“,bb*SWςxεJ©eΊρZˆΐά:ΰΗήν!Œφ‡°5@ΐ+.“4‰D–ΜΠ£”šŽT§ο§R©ΰ₯KxΗ;ήΡ€ώΧλuΌϊκ«PΗ™Ν@—|λήοχcηΝΰyήΡ/P­Vρϊλ―c~~ή IΊΘε8V;°ρIΰn,Žw¬EΖώύϋW0I’ΠΣΣγš=hΧςωίθπO:…cǎaff¦U;nθΊώ₯τ?θΊώˆΡžΫ|ΟηC?A€ͺͺ ιΈ,σεX΄γ»X‹0‘Χλ5Ν9Φ}˜3§₯’(Π4ΝM3κ1œƒΟ&‰ιlpωωΟΞ΄ΏЍΕ.C覦¦0==m†’–RΉ™Ί[.—‘Οη‘ΝfQ.—Ν›ˆ­^+uΞ΅γlT•J…B‘πμ:Ϊu:‹EΌφΪkxϊι§155εΊΚΗJιηόΑ7ΏωΝ“·ήzλ»u]²Ύfjj 0my§||Φ”εd,υ=9EΪ5€Ε^‚ΜΙk}Ž}G,LX©T@q‹°&<•H$Ξvœ€›ΛIx€ΰΞ₯^+Š"FFF088θ>\Ž0›_Σ‘gχxΫo~ζ΄: ™sKUU3ͺ°Z™››ΓΩ³gqρβΕ%U{}Q…ψ{JιϊλΏώk³γξώα>B)ύοφχοίΏϊΠ‡ ͺ*.^ΌAœ›μΈT*arr²ΙιΉ\η`»Y„ρx €fՎ²Ω,Nœ8|>T*εF’€?¬C›H}τQβσω> ΰ/`k>κ&έέέlhOΎ™₯X,brrηϟ7Λk—Α]׏ψγ―ύλΗνŸχιOšp\Χυ}φχ=ψΰƒ8xπ >Œ±±1Σ™i'Z­f]ŠΪMnυ\(Βθθ¨iΦΩ{ °0a.—3‡‘ΈΘΧ8Žϋ?>¬u`si€Oψ<€m}I„ ··ύύύˆΗγ›Š ΨTžΛ—/czzz9σχ^€”~ώ±ΗϋE«Ο½ίϋ½χPJκΊNμŸσπΓ›vοήmf]Ϊ‰@UU\Ύ|Ω¬l‡(₯ΛZΓ„NΥ„Œj΅^ύu€Σi$“I(ŠkΧθψδψψxΉC›LΎτ₯/ρ„G|ˆ[I,C__Ί»»Ϋ*₯½–’λ:rΉζηη155…ΉΉΉeyς)₯OQJΏT.—ŸόΦ·ΎΥΦίόΤ§>υ JιΏsϊΌΎΎ>Δb1Θ²ŒnΈΑ,D²λ˜œœDΉ\nΉϊ[Ο±tμ•˜‚ `ll ²,›OkoUUqϊτi$“Ι₯Z½ ΰγγγΙlByμ±ΗH΅Z}7€ΐϋ–λeM3»ΊΊ‹Ε ΫΆ[ Qω|™L©T sssŽΩzK€_₯”ώˆRϊ•―~υ«//χ>ρ‰OΘ”§)₯w8ύ­žžτυυAμή½αpΨΡ „`ffΉ\nI3ΐž9Έ’jBΨΉs'@ƒ&ΐΤ~J)Ν;‡ιιi€R)Χ!΄.xίψψψ™lnσ`;€ ΰχl]ιη°QV‘P@ΐ C± VM6„₯Χj5”Λe³L9“Ι˜7₯ΥyΈ πΟQJΏ₯λϊύκWΏΊͺπΦG?ϊΡnJι3”=N+cϋφν;wξ4ΓoNΞΑωωyΜΟΟ/™5θΤ[`ΉΩƒΊcddΡhΤ‘ΰςεΛΈpα‚9±ΘE><>>ώL‡6Ή|ε+_αu]?ΰ>„ΕΠΪ}ιΖt\I’ΜAƒ›YΣ (ŠYΒΊTHo™ΰ―x\ΧυοRJφε/YY«λ‘GιΡuύ°λ·9]G ΐΘΘ$IΒΰΰ 9ήIΘf³˜5C€­ΐ!°FL–c  ―――Ι`ίύάάήxγ  s‰ƒΤ|f||ό{xλh2€χψ€Δ7υ΅ ώ*₯τ₯τ‡”ώβΏ˜^―λωΰ?θπίt] ΣďΗcΪή[ΆlΑΠΠPSX”³0!3΅ΪΡ¬ΞΑεš===l 2°g³YΌώϊλζτ#—πŸ«Υκž~ϊiΪ!€·όΥ_ύ_«Υξp?kξ€K¦α„λΟRJΗ)₯GΎπ…/δΥu=όπΓ<₯τΏθΊώΖu™f@ @,ΓΝ;͜}»6ΐΒ„¬&£“€­ΰ+)& …BqMV*•pβΔ  ΜΝΝ΅ΚΙψ(₯Λ‘#Gκx‹Κ—Ώόe/₯τ6,&έΰΈΤ ¬3ψU]Χί ”ώ’Rϊ,₯τ…G}tβz?=τΠλϊWΰ0ͺŒ‚;v "˜aB;pg† λυz[š«œ\i˜ΠλυbηΝΑΡ/ΐΒ„,³E„ΰIŒη;πφ1’φΫ;m0€Ε΄δΥ”φeL˜ ”& П’”ΎώωΟ~CΞΎ{πΑ? ΰΏΉiJύύύζΈχnΈ>ŸΟΡ ”šaΒ₯r–Σ[`©0‘Ηγq$&L₯RKEN`1B0Υ!€ŽΖΐˆSJ»°Ψ‚* @Ζb»s+1(*rΖ–0'ς'›rΐεƒ>x‹I3½NΟΗγq BΕ¦0‘½bvvΩlΆ­d‘v‡[‘ŒŽŽΆ ž?ΣΣΣΘd2Θε\­¬i,Ξ&όu‡:ςΆ”ίώνί&„<ΰ§ηΓα0vμΨaϊXoA§ Ά›5θ”>άV λ:†‡‡‹ΕšH€ωX˜°P(ΈΞMPπ±ρρρ'6οΒwnΝM-dlmΙΔΔDvttτ{†―€ΙOR«ΥΛ凑ΝfΑqœc1!~Ώ’(6΄+_ͺΓ³½¬x9Υ„ιtΪ,ev’H$ŸΟ‡B‘I’P©TœH@πΡΡΡΡωD"ρJ‡: ]ΞΖm°/—Ξ%‰κΘΘΘχ !ŽEA:6›w*Šβ:Υ‰%WεrΉ°΅"«ƒ°ζ"ΦΗΉ\Š’ ‰4 ϋہ@‘HΩl²,»uβΌott4°{χ;wŽvΰνjξ:€[O0―τs&&&tY– …Mc„X@ EQP*•FWm6δ5—Λ5ΝZu^J(•JζυΨ›”PJ!Λ2Ί»»‘ΝfαρxP­Vέϊ ή₯λϊ £££' ΅C›άά€Γ[C#h›$’Ι$I$OοΨ±cŠς ύ>€”baa’$ηyd³Ysp©}uαpΕbΡ±jΟ‰μ>…ε˜Υj΅αzμΧ-Iz{{‘Λε I’™Ρι {Ό{ttτŸ‰DΉCδά ΈΦ ίŒκ[€011q|ddδeBΘ°Ψ“ΏA²Ω¬Ή².,, ›uφyαpΥjΥuj±0BYnΟAΦμ”MZ²>ΟσˆΗγ(—ΛfΒεΊ|httt<‘H€;°q@Ύΐηΰ9l •ΎχNLL\ώΗqa1,Ϊ …BΥjΑ`Π4 X?ϋt‘p8lΆJ[ςf0ήΫnΟAϋc]Χ1??`0θz=ρxάμΰΔσΌΫuuψψθθθσ‰Db²CλtnΑΏZΰsxλ8±’Χ_ΈpaΎΏΏ‡‚ ά‹Ε9} Βz†Γa€ΣiH’Τ0³Α <ζ4,‹mk+MF)E*•‚Ηγq!ΡΥΥžηQ*•ΰρxά">,φ<—H$NwΰΪ„ΑΦόA#XΙgαzΒ₯K— έέέ?eω&£φΊ^―#“Ι "—ˁ&dΰτϋύ$Ιuͺq+M`Ή3 “Ι˜aBkoζ‡ΓπϋύΘf³πz½(—ΛN$ ψΡΡΡj"‘xΎCλB[Oπ_KΐυΤ °Φο™ššR8ŽϋΗH$K9`ρ5MΓΒΒB‘*• κυΊΉβΫUuY–αχϋ#­ΘΐΙ9Έά0‘S£WΏίH$‚L&ŸΟη! ŽŽΖΗΖƎœ?žΎέ ΰz{ kΧ›_·°°@/^Όψ³ααα*!δ^Ψ’˜ν ͺ*ŠΕ’cXŽ | …BKΞ>t#•„ νΧc%Y–ΡΣΣƒt: ΗΣj\ων”[vξάωΣD"Q;ΐ΅ΈAΉ  %¬·F°‘ς°άΧPJΙΔΔΔKΓΓΓη8Ž{―‘7 .Š’i°Ζ*φ1`,LΈœvκφVeνšΥjΉ\Ξμi QΡΫΫ‹|>Q[EΖ<8::ϊΣD"Q|«ΐυϋ[Ι?°΄¬αkžΏpαΒƒƒƒΒσόϋΰPM˜ΛεΜ.Ώιt‘P¨‘έkΡV©TP«ΥΪκΧΈšΡd֌F§φo<Ο£··ΧΞΚζ:ȏŒŽŽK$Ι· ΄Ί!pnΰυ»m$ΐF$΄ϋάΕ‹§·nέϊΈ(ŠˆΪo¦R©„J₯‚P(d† Yϋq; #‘HΓ€υMfΝh΄^‹BΜ0a½^‡$InƒSΓF„ΰεD"qq=ΐx­@οφ˜,γ΅«}οZ½g9η\Ο£Θ‰1ŠœƒAβσωˆΧλ%²,Y–‰ DE"Ωo„Ω•VQ…ZzβQUUi­V£΅ZV«U”Λeέ©ΡR©D …Β’S‚m²\§]Εσ ΟέqΗ=‘Hδϋ„Ϋœ^μχϋ166Q±cΗΔγqΧjBΦ=y©Άγ­Ί/§šphhΘ±š9'―\Ή‚‰‰ ΤλuΜΝΝΉω+ͺ>2>>ώΣΝBk\²J“u €εψ|>Δγq‹ΕΈ. …Έ`0ΘΞλυς>ŸH’Δy<NEΒσ!†Ιφv €. ΫS]ΧΝMΣ4]Σ4ͺišͺͺͺͺ(Š( #­Z­κεrY+•JZ±XΤ2™Œ–N§΅t:­ΟΞΞ……½ GΪJWύΆΞΗb1ξΆΫnϋ Žγώg7•uŠΗγ1ΑkΧJ₯\ΉbΎo½G“uuuappΠ i²–nΑ`Π,xR—/_ΖwΏϋ]·ΎiοŸΨh@! \OBh8Ησ<ΆmΫΖυχχs[ΆlβρΈFω`0(A–eήλυς’$ρ‚ ’$1ΐ3α Π/"π!„!„pŒΨ―Γ ~φΠΠΨ^gb;Φ,d Υλu­^―k!h₯RI-‹Z‘PΠ鴚J₯΄d2©ΞΞΞκ©TŠΆˆ±―%4»ώϋ­ αζΗΪ±cΊΊΊ‡MΣΐi"Q½^Η₯K—Μtέ•Œ&[ŽYΰχϋ1:: Ηƒ-[Ά »»Ϋ¬'`ΔP.—qφμY|σ›ίtΣ^!„άυ³ŸύLΩ@֐6…Ζΐσ<†‡‡ΉΎΏΏ_θιι"‘ˆ‡EŸΟ'Θ²ΜΛ²,H’$0ΰ ‚ΐ‹’(@gK=ΫθyFL0Žϊ‰Σuΐo ¦0τ30φšAšAšAšͺͺZ½^W!T«U΅R©¨εrY+‹j>ŸW3™Œ:??―ΝΜΜ(333ϊόόΌή’Mφz½οΎϋ’$ι[pι7Θ»z½^άpΓ MG­D i._ΎŒZ­Φvϋq§ξΓνΗqΈνΆΫ°oίΎ¦jB‹)‡rΉŒΛ—/γ±ΗssώΗρρρΏ\_fΝ`½€½΅Χηb±懆†„­[·Š]]]B(ƒΑ `_”$IEQ`ϋEs^8Žθyž8Žγ !Όmo’ΫŒ•ίΤ(₯v"€ ψμ‘Ξ`ρž)₯T³€‰~J©‰FΖ^UUU5Θ@­V«j­VSΛε²Z*•Τ|>―ζr95•J)W―^U§¦¦ΤΙΙI½Z­΅^ρέΞύΦoύΦY–β8―!chhΘμ7ΝΦt΄X,ΆΥuΨ"lΧΠέݍ˜O}>Ÿ+ PJQ.—‘H$πΥ―~Υ)›±¨λϊΨ“O>yu…ΪΐυώzkM£Ρ(·kΧ.~xxXά²e‹‹Ε„p8,ΡλυІ]/ ‚ ˆ‹²ΈδσΌΐσΌh^`ΐη8N „Ζͺ/X‰€4Γρg7¬ΐ§ΖΝ [4Ÿ­ώ˜›Aͺ‘¨κ" ˜{Fυz]­ΧλJ΅ZeD  %›Νͺ κμμ¬299©\ΉrE―T*t-W|·ΧάuΧ]ƒΑ`πϋ„1§F£fΏΑ]»v™ιΊvmXl:šΙdΪn?ΎTχav<44„±±±¦4bŸΟΧrΈl©TΒ‹/Ύˆο|η;NOm||όV₯Y¬Ψ7" `±γΜΨΨΏsηN±ΏΏ_κξξ#‘ˆ %ŸΟ'Κ²,z<A’$Q’$QΡPχθEƒτ’A’ρ˜7ˆ€ΐφ<Σ π³cΣ`Χg‰P«+€ίΨ[Α―Y7 ψU]ΧΝΝΠΆ7H@©ΧλšͺͺŠ’(j­VSͺΥͺb˜Ša(™LF™ŸŸWfff”‹/ͺ333šmυ’kD oΊι¦p__ίw!w9έΌ^―{φμΟσAoo―£Oΐ&lΗ!hMζD###p–ΧλmπΨ5B‘€oϋΫ8~όΈύιB½^xκ©§ Άο£m"ΰΧxΥ'+<^Νg­Ελš’QΒα0Ήωζ›₯={φx=}}}žh4κ‰D"ž@ ΰρω|²,Λ’,ΛY–=’$±MEQΑάσ<οεy^AΑΛσΌll>žηeŽγΌ<Ο{½Οx½γ8ŸυΌρΨΗqœyly={lΩΛΗy,›dΩDƒDB;fš‰`h&¦ΟΒ’±˜> ÁΙσ<Ο’œ(Š iϊ|>"‚΅ §ΥwΥ<ž››«jšφO±Xl;!dύVU ˆΕbfAV½ΗVckNΗγiκ7θ”Ηa@Ψ“††‡‡Ρίίί\,²β€ °9’έέέxφΩg›"Ÿ<ϟK$―-±Έ­˜ΘθkEλN‘H„»ρΖ₯;vH[ΆlρτττˆαpX ’Οησψ|>ΙγρH’$y<GE(ŠA<‚ x °K‚ x8Ž3Οq'σ<ο1φ^ 2Ηq^c“ p{-ΰg χ{+!°χy !^ŽγdBˆΜqœ‡β!„HvΠ€6»ΒηLSΔnžXL“7Γά’Ώ’ε0y ΄ XΝc ΩlVΏ|ωςανΫ·σΗέiΏ™5MΓάά’Ρ¨Ω=(‰8‚ΪλυšΥ„K g΅ΎΧκθιιiΉςΫ―MΧuΗΤaVϊ<==ΩΩYϋΣB"‘ψA˜Y6¬ΥκΎ _ -£ελA cccΒΠАΗΕX,&ƒAΙοχ‹ΖŠ/J’$‰’(ΖΎ$Š’ΔσΌΉqgξ9Žσπω€ŒDI’ώybbbvΎ°7Ικ:z“ˆληz½^"I'Š"KΠiπΘΫbτΦΥ’·>ΆͺΝgo@)qΊω'''qι%T«UœΏίY–›¦σ<έ»w7΅[ Ψφάά^~ωe;ωΌ–H$― ŒΏ,Ψ¨«φšv‘P€λ$ FnΓ8»£Μj―Ω rμͺΏU7Σq ΫoάΰΦt]―išVΣ4W5M«θΊ^a$`Ωj–γΊͺͺUMΣjͺͺΦψσuUUθΝcEQκΖ^QE©Χ늒(J­VSj΅š™ T©T”rΉ¬Yj.—S3™ŒšJ₯”ΩΩYujjJΝf³ϊυΎWΚε²~ρβΕ£CCCUŽγή…ζtjΜΞΞ"’^―£R©44ω΄ξeYF hh:ΊuλVτττ`­D’€&˜žžn"J鋆°l€°’7­‘¬eΖΰš^‹λΈtι’V­ViΉ\F΅ZΥk΅D"΄^―Σz½N}>Ÿ(Š.I’ξρxtMΣ4EQ4Q5ATUUUžηU#Pε8NαyήL΄±%έπ-R‚ν!:8xμίΜΆω,鿌pΜάγ±™ώkSυΓ`€b¬ό K6@­T*J©T2³³Ω¬2??―\½zU™žžVŒπŸυ»¦Χλ˜RŠcǎύυ}χέ7)IcN‚“'ObΧ]Ϋ‘³jB]ΧΝr`–Ζ»cΗ\ΌxΡtώ­ιMιqpι˜YΑύN­€λtl rqTΑfggi6›­/,,hϊ–-[΄ξξn- j‘PHσω|ͺΗγQ½^―j¨’$),ΨVΐR€y πΰ­©ΐΦhƒeοδ,cΧΫ@Vƒ±J@VΘR-6?³ϋΝΝ°σ«ΚΟjŒΥŸ©όJ.—Sι΄:??_ŸUηζζtMΣΨ—”§žzκΗχάsόΟηϋ.Z={ύύύΔ©S§°{χnΘ²l‚Ÿ$IΨ±c&''›’ŠV#Ny Μpι•ώi)•J…ΎρΖκδδ€Ύ}ϋvuΫΆmJOOΤΥΥ₯„ΓaΑησ‰@@”eYπx<’Ηγα%IbuA¬(ˆ·T.v0Rk-©Άζή‘Θμh]ύbτ $ λ: /.φT3ΌώΦ`v¬Y~6ΏΖl}¦ώC¬5j*•R’Ι€šL&΅z½N7ϊoϋμ³ΟΎ|Χ]w½/ ~δ^Ÿšš‚ͺͺΑΙ“'±{χnψύ~όlΟσ<φξέ I’–ΥyΈ•8%1brΘxc΅+εΫΡΈ’χy<ΧΧΗmΫΆMŒΗγ|4eB‚Χλ}>o$ F‘ΐσΌ Š"/Οqγ~1cv±ˆ­$Ψ©'q"kΨ λQ[W Eΐ\ψίl Βΐo”›ž}EQ4όz½ή}«“Ομ J₯ΤT*₯f2έυΉ¦,ηx->£­γΔβρψλΦo0cίΎ}Π4 ;wξ4»χX‹ˆzzz°uλVs`ΙjWVΆl•\.‡Ο~φ³φ—W³Ωlό₯—^ͺ¬δίLŽΏ η„ €··—λξξzzz„h4*„B!ΑοχσFφ  Λ2ορxAXW s3ΡΟ2ŽήlBlΉφφ† ΔξŒ4QΤ’©Χ@¬%˜ ό¬=λ €±­V«™[Ή\6› 9ω΄L&£+ŠβΪk βUΖΝ;εαααopχ~'PΚ²Œ@Σ4 7U  »»Ϋ΄Σ«ΥκŠν~ΏίοΨ'ΰΕ_Δ7Ύρ ϋ韏Ώw₯χR&΅άl«9^ Y«kY³γb±ˆb±¨MLLh‚ Τc±ΧέέΝύψp8Μϋ|>Αοχσ>Ÿ—$‰χx<¬ oh 9!–,Dζl‘GkTŒ:°Τ\ ψY“P]UU³/ ’(ζވελΥj•^3@―±^6›Υ²Ω¬^,™³Χψλͺ)œ?ΎrυκΥ?8xπΰ£<Ο»¦eΆZΕ/ωKά~ϋνΈtιj΅L3ΐšΊΛ:Ή m)^―Χόͺͺβ‰'žpςUύd5w;υςo—μΐ5=ζ8މDΈP(Δ±Nΐ>Ÿχω|ΌΧλε<OC²K™5rL`y1 €ε0`Ω}πa¬ψPUUΧ4²F Š’ΠZ­¦ Χ+•Š^.—υR©€‚΅B‘@ …‚^«Υ¨ΓΌœzύkM«:Ύώϋ@„ .΅2ϋχοG @$Αππ0AΐΝ;‡›@λ2nZ†[ ρΛ/ΏŒ―ύλ.ͺΚΠ3Ο<“^Oθ/?’ΰϊ:Aˆ1Έ’ψύ~Θΐy<N–eN’$b4%F’ΩœηyΦœ³G,(₯Δpφ™mΑΩτf[pμζΎR©P£0-—Λ¨ΧλNM>Ϋνβ³RBؐdqί}χύΆ$IίΖβθξ&C<‡,ΛΨΉs'φξέΫDΜ4«V«PeΕΰΟf³ψ³?ϋ3d³YϋSίχ«Ρͺ–jyέςϊΌΏεsΨa  „γ8ˆ’H€ύ½ͺͺ‚R EQ(λNΓ6–ΧΎ skΉΟΣλLkNχάsΟΝ>Ÿο{“ϋΝ’ίχΏύθλλsύΒ4M3‡2’ε8‚ ˜&ƒ“Τλu|ϋΫίΖK/½Τd•¨ͺΊηΨ±c“«ψŽΪpΡυςίΏœηVςΈέίn­e­{kψ܊ˆΓθ7ψBΘn§Ό··»wοΖM7έ„[nΉΕ΅ΙηJDΣ4όθG?ΒO~ς'Ϋ GŽyt5ΰ–Χd³ƒχzυΔ ΐ½R€±σ«l½ŒΟ[©‰°^ZΑšj “““ΩP(τ~ΏVBΘvϋ?P*•Νf±cΗτττ˜}Χό?ώρΑΰT>Ÿ½©©)m5ΰ_Š6―g ς՜8Ϋo;€°œΧ/—ΦΪlΈfNΘ«W―Φ4Mϋ±Xl²ΟώOΤj5œŸ9šΜI …ήxγ <ρΔΈpα‚Ϋχ«RJΘ‘#?X ΰ―τΉ@ήHcΕΦӁ·R°_««•Δz›λe&,ωά{ήσž{EQό€X«/G–eάtΣMΨΆmΊΊΊ Λ2€ΕΔ’d2‰sηΞαΜ™3KEh ”ί=rδΘγ+ΈΦuΉy6Σ ΅|νz~΅¦ΉΞ€_oRΨ(~`q,وΧλύ€λψ}ΏiΪ'=zz­VύεϊΦ“ΦjE_©nΉ;²Ξ@Z­ΏΫΎέΟ[©ΰZ8 ―₯™€Λ—/§u]n4-BΦπώ¨PJΏΟη™gž™[kΰ―υ ½Φͺψ†™ΌBX+£««ΥΦe`θυπ#Όλ]οκυω|Dω *ΎΟ2€ΏSUυΛ–$Ÿ5ώzέ@ΧΪ ·‘νχλeη“ ψv^w-œ†ΧœξΎϋξίοW„‰ς.Έ€ΫW{ΟRJ€(Κ?>υΤS™eώ†ΦΔπΧsΕ_«ίŒ¬άλό·‚FΠπψΦ[o•b±ΨM„=„mXtŠκ„,₯τ ₯τT±X<ώΒ /ΤΧ{΅Ώž*δF]Ρ―…o½ˆ`3™+ώZƒZh λωήMKkΘ롚_o°“ ς»uz]ΗσΡl +Έž· `~­Υω΅NήΩL«ώzώZƒ#ϊπv"€Νψ`η“ φβX)ΨίR€·ΛFκ ΌΡ»Θ:΅8£. €-ΐIΫ.]ΐι:½FΏιzϋ6šF°aWݍ,›YΕίLaΎkEβΧJXkaS~3ΐZ™¬ςσVφυ σ‘ φζXoΨ\€°’Χ” –ϋέoζίθν’ Co bXkխՍό»]ΰZ'½-€ρv’hη“ ό›n§ΰ΅ΠήφθΘΖ±σ;&ΐϊi›Όσ¬λχχVrϊ­‡fp-4‚Žt`S}ΟdύΖk ,zvG:Πω6VΠ‘Žt€#ιHG:‘Žt€#ιHG:‘Žt€#ιHG:‘Žt€#ιHG:‘Žt€#ιHG:‘Žt€#i-?_=0.5.6 default-values>=0.5.0b1 extras-require>=0.2.0 furo>=2020.11.19b18 html-section>=0.1.0 seed-intersphinx-mapping>=0.3.1 sphinx>=3.0.3 sphinx-copybutton>=0.2.12 sphinx-debuginfo>=0.1.0 sphinx-licenseinfo>=0.1.1 sphinx-notfound-page>=0.7.1 sphinx-packaging>=0.1.1 sphinx-prompt>=1.1.0 sphinx-pyproject>=0.1.0 sphinx-tabs>=1.1.13 sphinx-toolbox>=2.13.0 sphinxcontrib-httpdomain>=1.7.0 sphinxemoji>=0.1.6 toctree-plus>=0.5.0b1 pyproject-parser-0.9.1/formate.toml000066400000000000000000000022001444752763000173720ustar00rootroot00000000000000[hooks] dynamic_quotes = 10 collections-import-rewrite = 20 reformat-generics = 40 noqa-reformat = 60 ellipsis-reformat = 70 squish_stubs = 80 [config] indent = "\t" line_length = 115 [hooks.yapf] priority = 30 [hooks.isort] priority = 50 [hooks.yapf.kwargs] yapf_style = ".style.yapf" [hooks.isort.kwargs] indent = "\t\t" multi_line_output = 8 import_heading_stdlib = "stdlib" import_heading_thirdparty = "3rd party" import_heading_firstparty = "this package" import_heading_localfolder = "this package" balanced_wrapping = false lines_between_types = 0 use_parentheses = true remove_redundant_aliases = true default_section = "THIRDPARTY" known_third_party = [ "apeye_core", "attr_utils", "attrs", "backports_entry_points_selectable", "coincidence", "consolekit", "coverage", "coverage_pyver_pragma", "dom_toml", "domdf_python_tools", "email_validator", "importlib_metadata", "natsort", "packaging", "pytest", "pytest_cov", "pytest_randomly", "pytest_timeout", "shippinglabel", "toml", "tomli", "typing_extensions", ] known_first_party = "pyproject_parser" pyproject-parser-0.9.1/justfile000066400000000000000000000005731444752763000166230ustar00rootroot00000000000000default: lint pdf-docs: latex-docs make -C doc-source/build/latex/ latex-docs: SPHINX_BUILDER=latex tox -e docs unused-imports: tox -e lint -- --select F401 incomplete-defs: tox -e lint -- --select MAN vdiff: git diff $(repo-helper show version -q)..HEAD bare-ignore: greppy '# type:? *ignore(?!\[|\w)' -s lint: unused-imports incomplete-defs bare-ignore tox -n qa pyproject-parser-0.9.1/pyproject.toml000066400000000000000000000131101444752763000177560ustar00rootroot00000000000000[build-system] requires = [ "setuptools!=61.*,>=40.6.0", "wheel>=0.34.2",] build-backend = "setuptools.build_meta" [project] name = "pyproject-parser" version = "0.9.1" description = "Parser for 'pyproject.toml'" readme = "README.rst" requires-python = ">=3.6.1" keywords = [ "metadata", "packaging", "pep518", "pep621", "pyproject", "toml",] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: System :: Archiving :: Packaging", "Typing :: Typed", ] dynamic = [ "dependencies",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.license] file = "LICENSE" [project.urls] Homepage = "https://github.com/repo-helper/pyproject-parser" "Issue Tracker" = "https://github.com/repo-helper/pyproject-parser/issues" "Source Code" = "https://github.com/repo-helper/pyproject-parser" Documentation = "https://pyproject-parser.readthedocs.io/en/latest" [project.scripts] pyproject-parser = "pyproject_parser.__main__:main" check-pyproject = "pyproject_parser.__main__:check" pyproject-fmt = "pyproject_parser.__main__:reformat" pyproject-info = "pyproject_parser.__main__:info" [project.optional-dependencies] readme = [ "docutils==0.16", "readme-renderer[md]>=27.0",] cli = [ "click>=7.1.2", "consolekit>=1.4.1", "sdjson>=0.3.1",] all = [ "click>=7.1.2", "consolekit>=1.4.1", "docutils==0.16", "readme-renderer[md]>=27.0", "sdjson>=0.3.1", ] [tool.mkrecipe] conda-channels = [ "conda-forge", "domdfcoding",] extras = "all" license-key = "MIT" package = "pyproject_parser" [tool.sphinx-pyproject] github_username = "repo-helper" github_repository = "pyproject-parser" author = "Dominic Davis-Foster" project = "pyproject-parser" copyright = "2021 Dominic Davis-Foster" language = "en" package_root = "pyproject_parser" extensions = [ "sphinx_toolbox", "sphinx_toolbox.more_autodoc", "sphinx_toolbox.more_autosummary", "sphinx_toolbox.documentation_summary", "sphinx_toolbox.tweaks.param_dash", "sphinxcontrib.toctree_plus", "sphinx_toolbox.tweaks.latex_layout", "sphinx_toolbox.tweaks.latex_toc", "sphinx.ext.intersphinx", "sphinx.ext.mathjax", "sphinxcontrib.extras_require", "sphinx.ext.todo", "sphinxemoji.sphinxemoji", "notfound.extension", "sphinx_copybutton", "sphinxcontrib.default_values", "sphinx_debuginfo", "sphinx_licenseinfo", "seed_intersphinx_mapping", "html_section", "sphinx_click", "attr_utils.autoattrs", "sphinx_toolbox.pre_commit", "sphinx_toolbox.more_autosummary.column_widths", "sphinx_packaging.peps", "sphinx_packaging.toml", "sphinx_toolbox.latex.succinct_seealso", "sphinx_toolbox_experimental.missing_xref", "local_extension", ] sphinxemoji_style = "twemoji" gitstamp_fmt = "%d %b %Y" templates_path = [ "_templates",] html_static_path = [ "_static",] source_suffix = ".rst" master_doc = "index" suppress_warnings = [ "image.nonlocal_uri",] pygments_style = "default" html_theme = "furo" html_theme_path = [ "../..",] html_show_sourcelink = true toctree_plus_types = [ "class", "confval", "data", "directive", "enum", "exception", "flag", "function", "namedtuple", "protocol", "role", "typeddict", ] add_module_names = false hide_none_rtype = true all_typevars = true overloads_location = "bottom" html_codeblock_linenos_style = "table" autodoc_exclude_members = [ "__dict__", "__class__", "__dir__", "__weakref__", "__module__", "__annotations__", "__orig_bases__", "__parameters__", "__subclasshook__", "__init_subclass__", "__attrs_attrs__", "__init__", "__new__", "__getnewargs__", "__abstractmethods__", "__hash__", ] [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: System :: Archiving :: Packaging", "Typing :: Typed", ] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10", "3.11",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" package = "pyproject_parser" [tool.mypy] python_version = "3.6" namespace_packages = true check_untyped_defs = true warn_unused_ignores = true no_implicit_optional = true plugins = [ "attr_utils.mypy_plugin",] show_error_codes = true [tool.snippet-fmt] directives = [ "code-block",] [tool.setuptools] zip-safe = false include-package-data = true platforms = [ "Windows", "macOS", "Linux",] [tool.dep_checker.name_mapping] attrs = "attr" [tool.dependency-dash."requirements.txt"] order = 10 [tool.dependency-dash."tests/requirements.txt"] order = 20 include = false [tool.dependency-dash."doc-source/requirements.txt"] order = 30 include = false [tool.snippet-fmt.languages.python] reformat = true [tool.snippet-fmt.languages.TOML] reformat = true [tool.snippet-fmt.languages.ini] [tool.snippet-fmt.languages.json] pyproject-parser-0.9.1/pyproject_parser/000077500000000000000000000000001444752763000204415ustar00rootroot00000000000000pyproject-parser-0.9.1/pyproject_parser/__init__.py000066400000000000000000000252771444752763000225670ustar00rootroot00000000000000#!/usr/bin/env python3 # # __init__.py """ Parser for ``pyproject.toml``. """ # # Copyright Β© 2021 Dominic Davis-Foster # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE # OR OTHER DEALINGS IN THE SOFTWARE. # # stdlib from typing import Any, ClassVar, Dict, Mapping, MutableMapping, Optional, Type, TypeVar, Union # 3rd party import attr import dom_toml import toml from dom_toml.encoder import _dump_str from dom_toml.parser import AbstractConfigParser, BadConfigError from domdf_python_tools.paths import PathPlus, in_directory from domdf_python_tools.typing import PathLike from domdf_python_tools.words import word_join from packaging.markers import Marker from packaging.requirements import Requirement from packaging.specifiers import SpecifierSet from packaging.version import Version from shippinglabel import normalize # this package from pyproject_parser.classes import License, Readme, _NormalisedName from pyproject_parser.parsers import BuildSystemParser, PEP621Parser from pyproject_parser.type_hints import ( # noqa: F401 Author, BuildSystemDict, ContentTypes, ProjectDict, _PyProjectAsTomlDict ) from pyproject_parser.utils import _load_toml __author__: str = "Dominic Davis-Foster" __copyright__: str = "2021 Dominic Davis-Foster" __license__: str = "MIT License" __version__: str = "0.9.1" __email__: str = "dominic@davis-foster.co.uk" __all__ = ["PyProject", "PyProjectTomlEncoder", "_PP"] _PP = TypeVar("_PP", bound="PyProject") class PyProjectTomlEncoder(dom_toml.TomlEncoder): """ Custom TOML encoder supporting types in :mod:`pyproject_parser.classes` and packaging_. .. _packaging: https://packaging.pypa.io/en/latest/ .. autosummary-widths:: 23/64 """ def __init__(self, _dict=dict, preserve: bool = False) -> None: # noqa: MAN001 super().__init__(_dict=_dict, preserve=preserve) self.dump_funcs[str] = _dump_str self.dump_funcs[_NormalisedName] = _dump_str self.dump_funcs[Version] = self.dump_packaging_types self.dump_funcs[Requirement] = self.dump_packaging_types self.dump_funcs[Marker] = self.dump_packaging_types self.dump_funcs[SpecifierSet] = self.dump_packaging_types @staticmethod def dump_packaging_types(obj: Union[Version, Requirement, Marker, SpecifierSet]) -> str: """ Convert types in packaging_ to TOML. .. _packaging: https://packaging.pypa.io/en/latest/ :param obj: """ return _dump_str(str(obj)) @attr.s class PyProject: """ Represents a ``pyproject.toml`` file. :param build_system: .. autosummary-widths:: 23/64 .. autoclasssumm:: PyProject :autosummary-sections: Methods :autosummary-exclude-members: __ge__,__gt__,__le__,__lt__,__ne__,__init__ .. latex:clearpage:: .. autoclasssumm:: PyProject :autosummary-sections: Attributes .. latex:vspace:: 10px """ #: Represents the :pep:`build-system table <518#build-system-table>` defined in :pep:`517` and :pep:`518`. build_system: Optional[BuildSystemDict] = attr.ib(default=None) #: Represents the :pep621:`project table ` defined in :pep:`621`. project: Optional[ProjectDict] = attr.ib(default=None) #: Represents the :pep:`tool table <518#tool-table>` defined in :pep:`518`. tool: Dict[str, Dict[str, Any]] = attr.ib(factory=dict) build_system_table_parser: ClassVar[BuildSystemParser] = BuildSystemParser() """ The :class:`~dom_toml.parser.AbstractConfigParser` to parse the :pep:`build-system table <518#build-system-table>` with. """ project_table_parser: ClassVar[PEP621Parser] = PEP621Parser() """ The :class:`~dom_toml.parser.AbstractConfigParser` to parse the :pep621:`project table ` with. """ tool_parsers: ClassVar[Mapping[str, AbstractConfigParser]] = {} """ A mapping of subtable names to :class:`~dom_toml.parser.AbstractConfigParser` objects to parse the :pep:`tool table <518#tool-table>` with. For example, to parse ``[tool.whey]``: .. code-block:: python class WheyParser(AbstractConfigParser): pass class CustomPyProject(PyProject): tool_parsers = {"whey": WheyParser()} """ @classmethod def load( cls: Type[_PP], filename: PathLike, set_defaults: bool = False, ) -> _PP: """ Load the ``pyproject.toml`` configuration mapping from the given file. :param filename: :param set_defaults: If :py:obj:`True`, passes ``set_defaults=True`` the :meth:`parse() ` method on :attr:`~.build_system_table_parser` and :attr:`~.project_table_parser`. """ filename = PathPlus(filename) project_dir = filename.parent config = _load_toml(filename) keys = set(config.keys()) build_system_table: Optional[BuildSystemDict] = None project_table: Optional[ProjectDict] = None tool_table: Dict[str, Dict[str, Any]] = {} with in_directory(project_dir): if "build-system" in config: build_system_table = cls.build_system_table_parser.parse( config["build-system"], set_defaults=set_defaults ) keys.remove("build-system") if "project" in config: project_table = cls.project_table_parser.parse(config["project"], set_defaults=set_defaults) keys.remove("project") if "tool" in config: tool_table = config["tool"] keys.remove("tool") for tool_name, tool_subtable in tool_table.items(): if tool_name in cls.tool_parsers: tool_table[tool_name] = cls.tool_parsers[tool_name].parse(tool_subtable) if keys: allowed_top_level = ("build-system", "project", "tool") for top_level_key in sorted(keys): if top_level_key in allowed_top_level: continue if normalize(top_level_key) in allowed_top_level: raise BadConfigError( f"Unexpected top-level key {top_level_key!r}. " f"Did you mean {normalize(top_level_key)!r}?", ) raise BadConfigError( f"Unexpected top-level key {top_level_key!r}. " f"Only {word_join(allowed_top_level, use_repr=True)} are allowed.", ) return cls( build_system=build_system_table, project=project_table, tool=tool_table, ) def dumps( self, encoder: Union[Type[toml.TomlEncoder], toml.TomlEncoder] = PyProjectTomlEncoder, ) -> str: """ Serialise to TOML. :param encoder: The :class:`toml.TomlEncoder` to use for constructing the output string. """ # TODO: filter out default values (lists and dicts) toml_dict: _PyProjectAsTomlDict = { "build-system": self.build_system, "project": self.project, "tool": self.tool, } if toml_dict["project"] is not None: if "license" in toml_dict["project"] and toml_dict["project"]["license"] is not None: toml_dict["project"] = { # type: ignore[typeddict-item] **toml_dict["project"], # type: ignore[misc,arg-type] "license": toml_dict["project"]["license"].to_pep621_dict() } if toml_dict["project"] is not None: if "readme" in toml_dict["project"] and toml_dict["project"]["readme"] is not None: readme_dict = toml_dict["project"]["readme"].to_pep621_dict() _project: Dict[str, Any] if set(readme_dict.keys()) == {"file"}: _project = {**toml_dict["project"], "readme": readme_dict["file"]} else: _project = {**toml_dict["project"], "readme": readme_dict} toml_dict["project"] = _project # type: ignore[typeddict-item] return dom_toml.dumps(toml_dict, encoder) def dump( self, filename: PathLike, encoder: Union[Type[toml.TomlEncoder], toml.TomlEncoder] = PyProjectTomlEncoder, ) -> str: """ Write as TOML to the given file. :param filename: The filename to write to. :param encoder: The :class:`toml.TomlEncoder` to use for constructing the output string. :returns: A string containing the TOML representation. """ filename = PathPlus(filename) as_toml = self.dumps(encoder=encoder) filename.write_clean(as_toml) return as_toml @classmethod def reformat( cls: Type[_PP], filename: PathLike, encoder: Union[Type[toml.TomlEncoder], toml.TomlEncoder] = PyProjectTomlEncoder, ) -> str: """ Reformat the given ``pyproject.toml`` file. :param filename: The file to reformat. :param encoder: The :class:`toml.TomlEncoder` to use for constructing the output string. :returns: A string containing the reformatted TOML. .. versionchanged:: 0.2.0 * Added the ``encoder`` argument. * The parser configured as :attr:`~.project_table_parser` is now used to parse the :pep621:`project table `, rather than always using :class:`~.PEP621Parser`. """ config = cls.load(filename, set_defaults=False) if config.project is not None and isinstance(config.project["name"], _NormalisedName): config.project["name"] = config.project["name"].unnormalized return config.dump(filename, encoder=encoder) def resolve_files(self) -> None: """ Resolve the ``file`` key in :pep621:`readme` and :pep621:`license` (if present) to retrieve the content of the file. Calling this method may mean it is no longer possible to recreate the original ``TOML`` file from this object. """ # noqa: D400 if self.project is not None: readme = self.project.get("readme", None) if readme is not None and isinstance(readme, Readme): readme.resolve(inplace=True) lic = self.project.get("license", None) if lic is not None and isinstance(lic, License): lic.resolve(inplace=True) @classmethod def from_dict(cls: Type[_PP], d: Mapping[str, Any]) -> _PP: """ Construct an instance of :class:`~.PyProject` from a dictionary. :param d: The dictionary. """ kwargs = {} for key, value in d.items(): if key == "build-system": key = "build_system" kwargs[key] = value return cls(**kwargs) def to_dict(self) -> MutableMapping[str, Any]: """ Returns a dictionary containing the contents of the class. .. seealso:: :func:`attr.asdict` """ return { "build_system": self.build_system, "project": self.project, "tool": self.tool, } pyproject-parser-0.9.1/pyproject_parser/__main__.py000066400000000000000000000223311444752763000225340ustar00rootroot00000000000000#!/usr/bin/env python3 # # __main__.py """ CLI entry point. .. versionadded:: 0.2.0 """ # # Copyright Β© 2021-2023 Dominic Davis-Foster # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE # OR OTHER DEALINGS IN THE SOFTWARE. # # stdlib import sys from typing import TYPE_CHECKING, Any, Iterable, List, Optional, Type, TypeVar # 3rd party import click # nodep from consolekit import click_group # nodep from consolekit.commands import MarkdownHelpCommand # nodep from consolekit.options import ( # nodep DescribedArgument, auto_default_argument, auto_default_option, colour_option, flag_option ) from consolekit.tracebacks import handle_tracebacks, traceback_option # nodep # this package from pyproject_parser import License from pyproject_parser.cli import prettify_deprecation_warning if TYPE_CHECKING: # 3rd party from consolekit.terminal_colours import ColourTrilean from domdf_python_tools.typing import PathLike from toml import TomlEncoder __all__ = ["main", "reformat", "check"] @click_group() def main() -> None: # pragma: no cover # noqa: D103 pass _C = TypeVar("_C", bound=click.Command) def options(c: _C) -> _C: pyproject_file_option = auto_default_argument( "pyproject_file", type=click.STRING, description="The ``pyproject.toml`` file.", cls=DescribedArgument, ) parser_class_option = auto_default_option( "-P", "--parser-class", type=click.STRING, help="The class to parse the 'pyproject.toml' file with.", show_default=True, ) pyproject_file_option(c) parser_class_option(c) traceback_option()(c) return c @options @main.command(cls=MarkdownHelpCommand) def check( pyproject_file: "PathLike" = "pyproject.toml", parser_class: str = "pyproject_parser:PyProject", show_traceback: bool = False, ) -> None: """ Validate the given ``pyproject.toml`` file. """ # 3rd party from dom_toml.parser import BadConfigError from domdf_python_tools.paths import PathPlus from domdf_python_tools.words import Plural, word_join # this package from pyproject_parser import PyProject from pyproject_parser.cli import ConfigTracebackHandler, resolve_class from pyproject_parser.parsers import BuildSystemParser, PEP621Parser from pyproject_parser.utils import _load_toml if not show_traceback: prettify_deprecation_warning() pyproject_file = PathPlus(pyproject_file) click.echo(f"Validating {str(pyproject_file)!r}") with handle_tracebacks(show_traceback, ConfigTracebackHandler): parser: Type[PyProject] = resolve_class(parser_class, "parser-class") parser.load(filename=pyproject_file) raw_config = _load_toml(pyproject_file) _keys = Plural("key", "keys") def error_on_unknown( keys: Iterable[str], expected_keys: Iterable[str], table_name: str, ) -> None: unknown_keys = set(keys) - set(expected_keys) if unknown_keys: raise BadConfigError( f"Unknown {_keys(len(unknown_keys))} in '[{table_name}]': " f"{word_join(sorted(unknown_keys), use_repr=True)}", ) # Implements PEPs 517 and 518 error_on_unknown(raw_config.get("build-system", {}).keys(), BuildSystemParser.keys, "build-system") # Implements PEP 621 error_on_unknown(raw_config.get("project", {}).keys(), {*PEP621Parser.keys, "dynamic"}, "project") _resolve_help = "Resolve file key in project.readme and project.license (if present) to retrieve the content of the file." @traceback_option() @auto_default_option( "-P", "--parser-class", type=click.STRING, help="The class to parse the 'pyproject.toml' file with.", show_default=True, ) @auto_default_option( "-f", "--file", "pyproject_file", type=click.STRING, help="The ``pyproject.toml`` file.", ) @auto_default_option( "-i", "--indent", help="Add indentation to the JSON output.", type=click.INT, ) @flag_option( "-r", "--resolve", help=_resolve_help, show_default=True, ) @auto_default_argument( "field", type=click.STRING, description="The field to retrieve from the ``pyproject.toml`` file.", cls=DescribedArgument, ) @main.command(cls=MarkdownHelpCommand) def info( field: Optional[str] = None, pyproject_file: "PathLike" = "pyproject.toml", parser_class: str = "pyproject_parser:PyProject", resolve: bool = False, show_traceback: bool = False, indent: Optional[int] = None, ) -> None: """ Extract information from the given ``pyproject.toml`` file and print the JSON representation. """ # stdlib import os import re # 3rd party import sdjson # nodep from domdf_python_tools.paths import PathPlus, in_directory # this package from pyproject_parser import PyProject from pyproject_parser.cli import _json_encoders # noqa: F401 from pyproject_parser.cli import ConfigTracebackHandler, resolve_class from pyproject_parser.type_hints import Readme from pyproject_parser.utils import _load_toml if not show_traceback: prettify_deprecation_warning() pyproject_file = PathPlus(pyproject_file) with handle_tracebacks(show_traceback, ConfigTracebackHandler): parser: Type[PyProject] = resolve_class(parser_class, "parser-class") check_readme = os.getenv("CHECK_README") try: if field is not None and not field.startswith("project.readme"): os.environ["CHECK_README"] = '0' config = parser.load(filename=pyproject_file) if resolve: with in_directory(pyproject_file.parent): config.resolve_files() raw_config = _load_toml(pyproject_file) output: Any if not field: print(sdjson.dumps(config, indent=indent)) sys.exit(0) field_parts = field.split('.') if field_parts[0] == "build-system": output = config.build_system elif field_parts[0] == "project": output = config.project else: output = raw_config[field_parts[0]] for part in field_parts[1:]: # TODO: slice? m = re.match(r"^\[(\d)]$", part) if m: # array index output = output[int(m.group(1))] elif isinstance(output, (Readme, License)): output = getattr(output, part) else: # field name output = output[part] print(sdjson.dumps(output, indent=indent)) finally: if check_readme is None: if "CHECK_README" in os.environ: del os.environ["CHECK_README"] else: os.environ["CHECK_README"] = check_readme @options @colour_option() @flag_option("-d", "--show-diff", help="Show a (coloured) diff of changes.") @auto_default_option( "-E", "--encoder-class", type=click.STRING, help="The class to encode the config to TOML with.", show_default=True, ) @main.command(cls=MarkdownHelpCommand) def reformat( pyproject_file: "PathLike" = "pyproject.toml", encoder_class: str = "pyproject_parser:PyProjectTomlEncoder", parser_class: str = "pyproject_parser:PyProject", show_traceback: bool = False, show_diff: bool = False, colour: "ColourTrilean" = None, ) -> None: """ Reformat the given ``pyproject.toml`` file. """ # 3rd party from consolekit.terminal_colours import resolve_color_default # nodep from consolekit.utils import coloured_diff # nodep from domdf_python_tools.paths import PathPlus # this package from pyproject_parser import PyProject, _NormalisedName from pyproject_parser.cli import ConfigTracebackHandler, resolve_class if not show_traceback: prettify_deprecation_warning() pyproject_file = PathPlus(pyproject_file) click.echo(f"Reformatting {str(pyproject_file)!r}") with handle_tracebacks(show_traceback, ConfigTracebackHandler): parser: Type[PyProject] = resolve_class(parser_class, "parser-class") encoder: Type["TomlEncoder"] = resolve_class(encoder_class, "encoder-class") original_content: List[str] = pyproject_file.read_lines() config = parser.load(filename=pyproject_file, set_defaults=False) if config.project is not None and isinstance(config.project["name"], _NormalisedName): config.project["name"] = config.project["name"].unnormalized reformatted_content: List[str] = config.dump(filename=pyproject_file, encoder=encoder).split('\n') changed = reformatted_content != original_content if show_diff and changed: diff = coloured_diff( original_content, reformatted_content, str(pyproject_file), str(pyproject_file), "(original)", "(reformatted)", lineterm='', ) click.echo(diff, color=resolve_color_default(colour)) sys.exit(int(changed)) if __name__ == "__main__": sys.exit(main()) pyproject-parser-0.9.1/pyproject_parser/classes.py000066400000000000000000000236521444752763000224600ustar00rootroot00000000000000#!/usr/bin/env python3 # # classes.py """ Classes to represent readme and license files. .. automodulesumm:: pyproject_parser.classes :autosummary-sections: Classes .. autosummary-widths:: 4/16 .. automodulesumm:: pyproject_parser.classes :autosummary-sections: Data """ # # Copyright Β© 2021 Dominic Davis-Foster # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE # OR OTHER DEALINGS IN THE SOFTWARE. # # stdlib import pathlib from contextlib import suppress from typing import TYPE_CHECKING, Any, Dict, Mapping, Optional, Type, TypeVar # 3rd party import attr from domdf_python_tools.paths import PathPlus from domdf_python_tools.typing import PathLike # this package from pyproject_parser.utils import content_type_from_filename if TYPE_CHECKING: # this package from pyproject_parser.type_hints import ContentTypes, ReadmeDict __all__ = ["License", "Readme", "_R", "_L"] _R = TypeVar("_R", bound="Readme") _L = TypeVar("_L", bound="License") # @overload # def _convert_filename(filename: None) -> None: ... # # # @overload # def _convert_filename(filename: PathLike) -> pathlib.Path: ... def _convert_filename(filename: Optional[PathLike]) -> Optional[pathlib.Path]: if filename is None: return filename return pathlib.Path(filename) # TODO: overloads for __init__ @attr.s class Readme: """ Represents a readme in :pep:`621` configuration. """ #: The content type of the readme. content_type: Optional["ContentTypes"] = attr.ib(default=None) #: The charset / encoding of the readme. charset: str = attr.ib(default="UTF-8") #: The path to the readme file. file: Optional[pathlib.Path] = attr.ib(default=None, converter=_convert_filename) #: The content of the readme. text: Optional[str] = attr.ib(default=None) def __attrs_post_init__(self) -> None: # Sanity checks the supplied arguments if self.content_type and not (self.text or self.file): raise ValueError( "'content_type' cannot be provided on its own; " "please provide either 'text' or 'file' or use the 'from_file' method." ) if self.text is None and self.file is None: raise TypeError(f"At least one of 'text' and 'file' must be supplied to {self.__class__!r}") if self.file is not None and self.content_type is None: with suppress(ValueError): self.content_type = content_type_from_filename(self.file) @content_type.validator def _check_content_type(self, attribute: Any, value: str) -> None: if value not in {"text/markdown", "text/x-rst", "text/plain", None}: raise ValueError(f"Unsupported readme content-type {value!r}") @classmethod def from_file(cls: Type[_R], file: PathLike, charset: str = "UTF-8") -> _R: """ Create a :class:`~.Readme` from a filename. :param file: The path to the readme file. :param charset: The charset / encoding of the readme file. """ filename = PathPlus(file) if filename.suffix.lower() == ".md": return cls(file=filename, charset=str(charset), content_type="text/markdown") elif filename.suffix.lower() == ".rst": return cls(file=filename, charset=str(charset), content_type="text/x-rst") elif filename.suffix.lower() == ".txt": return cls(file=filename, charset=str(charset), content_type="text/plain") else: raise ValueError(f"Unrecognised filetype for '{filename!s}'") def resolve(self: _R, inplace: bool = False) -> _R: """ Retrieve the contents of the readme file if the :attr:`self.file <.Readme.file>` is set. Returns a new :class:`~.Readme` object with :attr:`~.Readme.text` set to the content of the file. :param inplace: Modifies and returns the current object rather than creating a new one. """ text = self.text if text is None and self.file: text = self.file.read_text(encoding=self.charset) if inplace: self.text = text return self else: return self.__class__( content_type=self.content_type, charset=self.charset, file=self.file, text=text, ) def to_dict(self) -> "ReadmeDict": """ Construct a dictionary containing the keys of the :class:`~.Readme` object. .. seealso:: :meth:`~.Readme.to_pep621_dict` and :meth:`~.Readme.from_dict` """ as_dict: "ReadmeDict" = {} if self.content_type is not None: as_dict["content_type"] = self.content_type if self.charset != "UTF-8": as_dict["charset"] = self.charset if self.file is not None: as_dict["file"] = self.file.as_posix() if self.text is not None: as_dict["text"] = self.text return as_dict @classmethod def from_dict(cls: Type[_R], data: "ReadmeDict") -> _R: """ Construct a :class:`~.Readme` from a dictionary containing the same keys as the class constructor. In addition, ``content_type`` may instead be given as ``content-type``. :param data: :rtype: .. seealso:: :meth:`~.Readme.to_dict` and :meth:`~.Readme.to_pep621_dict` """ data_dict = dict(data) if "content-type" in data_dict: data_dict["content_type"] = data_dict.pop("content-type") return cls(**data_dict) # type: ignore[arg-type] def to_pep621_dict(self) -> Dict[str, str]: """ Construct a dictionary containing the keys of the :class:`~.Readme` object, suitable for use in :pep:`621` ``pyproject.toml`` configuration. Unlike :meth:`~.Readme.to_dict` this ignores the ``text`` key if :attr:`self.file <.Readme.file>` is set, and ignores :attr:`self.content_type <.Readme.content_type>` if it matches the content-type inferred from the file extension. .. seealso:: :meth:`~.Readme.from_dict` """ # noqa: D400 as_dict = {} if self.content_type is not None: as_dict["content-type"] = str(self.content_type) if self.charset != "UTF-8": as_dict["charset"] = self.charset if self.file is not None: as_dict["file"] = self.file.as_posix() if content_type_from_filename(self.file) == self.content_type: as_dict.pop("content-type") elif self.text is not None: as_dict["text"] = self.text return as_dict @attr.s class License: """ Represents a license in :pep:`621` configuration. :param file: .. latex:vspace:: 20px .. autosummary-widths:: 6/16 """ #: The path to the license file. file: Optional[pathlib.Path] = attr.ib(default=None, converter=_convert_filename) #: The content of the license. text: Optional[str] = attr.ib(default=None) def __attrs_post_init__(self) -> None: # Sanity checks the supplied arguments if self.text is None and self.file is None: raise TypeError(f"At least one of 'text' and 'file' must be supplied to {self.__class__!r}") # if self.text is not None and self.file is not None: # raise TypeError("'text' and 'filename' are mutually exclusive.") def resolve(self: _L, inplace: bool = False) -> _L: """ Retrieve the contents of the license file if the :attr:`~.License.file` is set. Returns a new :class:`~.License` object with :attr:`~.License.text` set to the content of the file. :param inplace: Modifies and returns the current object rather than creating a new one. """ text = self.text if text is None and self.file: text = self.file.read_text(encoding="UTF-8") if inplace: self.text = text return self else: return self.__class__( file=self.file, text=text, ) def to_dict(self) -> Dict[str, str]: """ Construct a dictionary containing the keys of the :class:`~.License` object. .. seealso:: :meth:`~.License.to_pep621_dict` and :meth:`~.License.from_dict` """ as_dict = {} if self.file is not None: as_dict["file"] = self.file.as_posix() if self.text is not None: as_dict["text"] = self.text return as_dict @classmethod def from_dict(cls: Type[_L], data: Mapping[str, str]) -> _L: """ Construct a :class:`~.License` from a dictionary containing the same keys as the class constructor. Functionally identical to ``License(**data)`` but provided to give an identical API to :class:`~.Readme`. :param data: :rtype: .. seealso:: :meth:`~.License.to_dict` and :meth:`~.License.to_pep621_dict` """ return cls(**data) def to_pep621_dict(self) -> Dict[str, str]: """ Construct a dictionary containing the keys of the :class:`~.License` object, suitable for use in :pep:`621` ``pyproject.toml`` configuration. Unlike :meth:`~.License.to_dict` this ignores the ``text`` key if :attr:`self.file <.License.file>` is set. :rtype: .. seealso:: :meth:`~.Readme.from_dict` .. latex:clearpage:: """ # noqa: D400 as_dict = self.to_dict() if "file" in as_dict and "text" in as_dict: as_dict.pop("text") return as_dict class _NormalisedName(str): """ Represents a name normalized per :pep:`503`, and allows the original name to be stored as an attribute. """ # noqa: D400 __slots__ = ("_unnormalized", ) _unnormalized: Optional[str] def __new__(cls, o, **kwargs): # noqa: MAN001 self = super().__new__(cls, o, **kwargs) self._unnormalized = None return self @property def unnormalized(self) -> str: if self._unnormalized is None: return str(self) else: return self._unnormalized @unnormalized.setter def unnormalized(self, value: str) -> None: self._unnormalized = str(value) pyproject-parser-0.9.1/pyproject_parser/cli/000077500000000000000000000000001444752763000212105ustar00rootroot00000000000000pyproject-parser-0.9.1/pyproject_parser/cli/__init__.py000066400000000000000000000143711444752763000233270ustar00rootroot00000000000000#!/usr/bin/env python3 # # cli.py """ Command line interface. .. versionadded:: 0.2.0 .. extras-require:: cli :pyproject: """ # # Copyright Β© 2021 Dominic Davis-Foster # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE # OR OTHER DEALINGS IN THE SOFTWARE. # # stdlib import functools import importlib import re import sys import warnings from pathlib import Path from typing import TYPE_CHECKING, Optional, Pattern, TextIO, Type, Union # 3rd party import click # nodep from consolekit.tracebacks import TracebackHandler # nodep from dom_toml.parser import BadConfigError from packaging.specifiers import InvalidSpecifier from packaging.version import InvalidVersion # this package from pyproject_parser.utils import PyProjectDeprecationWarning if sys.version_info >= (3, 7) or TYPE_CHECKING: # stdlib from typing import NoReturn __all__ = ["resolve_class", "ConfigTracebackHandler", "prettify_deprecation_warning"] class_string_re: Pattern[str] = re.compile("([A-Za-z_][A-Za-z_0-9.]+):([A-Za-z_][A-Za-z_0-9]+)") def resolve_class(raw_class_string: str, name: str) -> Type: """ Resolve the class name for the :option:`-P / --parser-class ` and :option:`-E / --encoder-class ` options. :param raw_class_string: :param name: The name of the option, e.g. ``encoder-class``. Used for error messages. """ # noqa: D400 class_string_m = class_string_re.match(raw_class_string) if class_string_m: module_name, class_name = class_string_m.groups() else: raise click.BadOptionUsage(f"{name}", f"Invalid syntax for '--{name}'") module = importlib.import_module(module_name) resolved_class: Type = getattr(module, class_name) return resolved_class class ConfigTracebackHandler(TracebackHandler): """ :class:`consolekit.tracebacks.TracebackHandler` which handles :exc:`dom_toml.parser.BadConfigError`. """ has_traceback_option: bool = True """ Whether to show the message ``Use '--traceback' to view the full traceback.`` on error. Enabled by default. .. versionadded:: 0.5.0 In previous versions this was effectively :py:obj:`False`. .. versionchanged:: 0.6.0 The message is now indented with four spaces. """ @property def _tb_option_msg(self) -> str: if self.has_traceback_option: return "\n Use '--traceback' to view the full traceback." else: return '' def format_exception(self, e: Exception) -> "NoReturn": """ Format the exception, showing the explanatory note and documentation link if applicable. .. versionadded:: 0.6.0 :param e: """ msg = [f"{e.__class__.__name__}: {e}"] if getattr(e, "note", None) is not None: msg.append(f"\n Note: {e.note}") # type: ignore[attr-defined] if getattr(e, "documentation", None) is not None: msg.append(f"\n Documentation: {e.documentation}") # type: ignore[attr-defined] msg.append(self._tb_option_msg) self.abort(msg) def handle_BadConfigError(self, e: "BadConfigError") -> "NoReturn": # noqa: D102 self.format_exception(e) def handle_ValueError(self, e: ValueError) -> "NoReturn": # noqa: D102 # Also covers InvalidRequirement self.format_exception(e) def handle_InvalidSpecifier(self, e: InvalidSpecifier) -> "NoReturn": # noqa: D102 if str(e).startswith("Invalid specifier: "): e.args = (str(e)[len("Invalid specifier: "):], ) self.format_exception(e) def handle_InvalidVersion(self, e: InvalidVersion) -> "NoReturn": # noqa: D102 if str(e).startswith("Invalid version: "): e.args = (str(e)[len("Invalid version: "):], ) self.format_exception(e) def handle_KeyError(self, e: KeyError) -> "NoReturn": # noqa: D102 self.format_exception(e) def handle_TypeError(self, e: TypeError) -> "NoReturn": # noqa: D102 self.format_exception(e) def handle_AttributeError(self, e: AttributeError) -> "NoReturn": # noqa: D102 self.format_exception(e) def handle_ImportError(self, e: ImportError) -> "NoReturn": # noqa: D102 self.format_exception(e) def handle_FileNotFoundError(self, e: FileNotFoundError) -> "NoReturn": # noqa: D102 msg = e.strerror no_such_file = "No such file or directory" if msg == "The system cannot find the file specified": msg = no_such_file if msg == no_such_file: # Probably from Python itself. if e.filename is not None: msg += f": {Path(e.filename).as_posix()!r}" if e.filename2 is not None: msg += f" -> {Path(e.filename2).as_posix()!r}" self.abort(msg) else: # Probably from 3rd party code. super().handle_FileNotFoundError(e) def prettify_deprecation_warning() -> None: """ Catch :class:`PyProjectDeprecationWarnings <.PyProjectDeprecationWarning>` and format them prettily for the command line. .. versionadded:: 0.5.0 """ # noqa: D400 orig_showwarning = warnings.showwarning if orig_showwarning is prettify_deprecation_warning: return @functools.wraps(warnings.showwarning) def showwarning( message: Union[Warning, str], category: Type[Warning], filename: str, lineno: int, file: Optional[TextIO] = None, line: Optional[str] = None, ) -> None: if isinstance(message, PyProjectDeprecationWarning): if file is None: file = sys.stderr s = f"WARNING: {message.args[0]}\n" file.write(s) else: orig_showwarning(message, category, filename, lineno, file, line) warnings.showwarning = showwarning pyproject-parser-0.9.1/pyproject_parser/cli/_json_encoders.py000066400000000000000000000046241444752763000245620ustar00rootroot00000000000000#!/usr/bin/env python3 # # _json_encoders.py """ Encoder functions for :mod:`sdjson`. """ # # Copyright Β© 2022 Dominic Davis-Foster # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE # OR OTHER DEALINGS IN THE SOFTWARE. # # stdlib from pathlib import PurePath from typing import Dict, Union # 3rd party import sdjson # nodep from packaging.requirements import Requirement from packaging.specifiers import SpecifierSet from packaging.version import Version from shippinglabel.requirements import ComparableRequirement # this package from pyproject_parser import PyProject from pyproject_parser.classes import License, Readme from pyproject_parser.type_hints import ReadmeDict @sdjson.register_encoder(ComparableRequirement) @sdjson.register_encoder(Requirement) @sdjson.register_encoder(Version) @sdjson.register_encoder(SpecifierSet) def _encode_from_packaging(obj: Union[ComparableRequirement, Requirement, Version, SpecifierSet]) -> str: return str(obj) @sdjson.register_encoder(PurePath) def _encode_pathlib(obj: PurePath) -> str: return obj.as_posix() @sdjson.register_encoder(PyProject) def _encode_pyproject(obj: PyProject) -> Dict: return { "build-system": obj.build_system, "project": obj.project, "tool": obj.tool, } @sdjson.register_encoder(Readme) @sdjson.register_encoder(License) def _encode_readme_license(obj: Union[Readme, License]) -> Union[ReadmeDict, Dict[str, str]]: return obj.to_dict() pyproject-parser-0.9.1/pyproject_parser/parsers.py000066400000000000000000001071031444752763000224740ustar00rootroot00000000000000#!/usr/bin/env python3 # # parsers.py """ TOML configuration parsers. """ # # Copyright Β© 2021 Dominic Davis-Foster # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE # OR OTHER DEALINGS IN THE SOFTWARE. # # stdlib import collections.abc import functools import os import re import warnings from abc import ABCMeta from typing import Any, Callable, ClassVar, Dict, Iterable, List, Mapping, Set, Union, cast # 3rd party from apeye_core import URL from apeye_core.email_validator import EmailSyntaxError, validate_email from dom_toml.parser import TOML_TYPES, AbstractConfigParser, BadConfigError, construct_path from natsort import natsorted, ns from packaging.requirements import InvalidRequirement from packaging.specifiers import InvalidSpecifier, SpecifierSet from packaging.version import InvalidVersion, Version from shippinglabel import normalize from shippinglabel.classifiers import validate_classifiers from shippinglabel.requirements import ComparableRequirement, combine_requirements # this package from pyproject_parser.classes import License, Readme, _NormalisedName from pyproject_parser.type_hints import Author, BuildSystemDict, ProjectDict from pyproject_parser.utils import PyProjectDeprecationWarning, content_type_from_filename, render_readme __all__ = [ "RequiredKeysConfigParser", "BuildSystemParser", "PEP621Parser", ] name_re = re.compile("^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", flags=re.IGNORECASE) extra_re = re.compile("^([a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9])$") def _documentation_url(__documentation_url: str) -> Callable: def deco(f) -> Callable: # noqa: MAN001 @functools.wraps(f) def wrapper(*args, **kwds): # noqa: MAN002 try: return f(*args, **kwds) except Exception as e: if getattr(e, "documentation", None) is None: e.documentation = __documentation_url # type: ignore[attr-defined] raise return wrapper return deco class RequiredKeysConfigParser(AbstractConfigParser, metaclass=ABCMeta): """ Abstract base class for TOML configuration parsers which have required keys. .. autosummary-widths:: 17/32 """ required_keys: ClassVar[List[str]] table_name: ClassVar[str] def parse( self, config: Dict[str, TOML_TYPES], set_defaults: bool = False, ) -> Dict[str, TOML_TYPES]: """ Parse the TOML configuration. :param config: :param set_defaults: If :py:obj:`True`, the values in :attr:`self.defaults ` and :attr:`self.factories ` will be set as defaults for the returned mapping. """ for key in self.required_keys: if key in config: continue elif set_defaults and (key in self.defaults or key in self.factories): continue # pragma: no cover https://github.com/nedbat/coveragepy/issues/198 else: raise BadConfigError(f"The {construct_path([self.table_name, key])!r} field must be provided.") return super().parse(config, set_defaults) def assert_sequence_not_str( self, obj: Any, path: Iterable[str], what: str = "type", ) -> None: """ Assert that ``obj`` is a :class:`~typing.Sequence` and not a :class:`str`, otherwise raise an error with a helpful message. :param obj: The object to check the type of. :param path: The elements of the path to ``obj`` in the TOML mapping. :param what: What ``obj`` is, e.g. ``'type'``, ``'value type'``. .. latex:clearpage:: """ # noqa: D400 if isinstance(obj, str): name = construct_path(path) raise TypeError( f"Invalid {what} for {name!r}: " f"expected , got {type(obj)!r}", ) self.assert_type(obj, collections.abc.Sequence, path, what=what) class BuildSystemParser(RequiredKeysConfigParser): """ Parser for the :pep:`build-system table <518#build-system-table>` table from ``pyproject.toml``. .. autosummary-widths:: 17/32 """ # noqa: RST399 table_name: ClassVar[str] = "build-system" required_keys: ClassVar[List[str]] = ["requires"] keys: ClassVar[List[str]] = ["requires", "build-backend", "backend-path"] factories: ClassVar[Dict[str, Callable[..., Any]]] = {"requires": list} defaults: ClassVar[Dict[str, Any]] = {"build-backend": None, "backend-path": None} @staticmethod def normalize_requirement_name(name: str) -> str: """ Function to normalize a requirement name per e.g. :pep:`503` (where underscores are replaced by hyphens). .. versionadded:: 0.9.0 """ return normalize(name) @_documentation_url("https://peps.python.org/pep-0518/") def parse_requires(self, config: Dict[str, TOML_TYPES]) -> List[ComparableRequirement]: """ Parse the :pep:`requires <518#build-system-table>` key. :param config: The unparsed TOML config for the :pep:`build-system table <518#build-system-table>`. """ # noqa: RST399 parsed_dependencies = set() key_path = [self.table_name, "requires"] self.assert_sequence_not_str(config["requires"], key_path) for idx, raw_requirement in enumerate(config["requires"]): self.assert_indexed_type(raw_requirement, str, key_path, idx=idx) try: requirement = ComparableRequirement(raw_requirement) except InvalidRequirement as e: e.args = (f"{raw_requirement!r}\n {str(e)}", ) e.note = "requirements must follow PEP 508" # type: ignore[attr-defined] e.documentation = "https://peps.python.org/pep-0508/" # type: ignore[attr-defined] raise parsed_dependencies.add(requirement) return sorted(combine_requirements(parsed_dependencies, normalize_func=self.normalize_requirement_name)) @_documentation_url("https://peps.python.org/pep-0517/") def parse_build_backend(self, config: Dict[str, TOML_TYPES]) -> str: """ Parse the ``build_backend`` key defined by :pep:`517`. :param config: The unparsed TOML config for the :pep:`build-system table <518#build-system-table>`. """ # noqa: RST399 build_backend = config["build-backend"] self.assert_type(build_backend, str, [self.table_name, "build-backend"]) return build_backend @_documentation_url("https://peps.python.org/pep-0517/") def parse_backend_path(self, config: Dict[str, TOML_TYPES]) -> List[str]: """ Parse the ``backend-path`` key defined by :pep:`517`. :param config: The unparsed TOML config for the :pep:`build-system table <518#build-system-table>`. """ # noqa: RST399 parsed_backend_paths = [] key_path = [self.table_name, "backend-path"] self.assert_sequence_not_str(config["backend-path"], key_path) for idx, path in enumerate(config["backend-path"]): self.assert_indexed_type(path, str, key_path, idx=idx) parsed_backend_paths.append(path) return parsed_backend_paths def parse( # type: ignore[override] self, config: Dict[str, TOML_TYPES], set_defaults: bool = False, ) -> BuildSystemDict: """ Parse the TOML configuration. :param config: :param set_defaults: If :py:obj:`True`, the values in :attr:`self.defaults ` and :attr:`self.factories ` will be set as defaults for the returned mapping. :rtype: .. latex:clearpage:: """ parsed_config = super().parse(config, set_defaults) if ( parsed_config.get("backend-path", None) is not None and parsed_config.get("build-backend", None) is None ): raise BadConfigError( f"{construct_path([self.table_name, 'backend-path'])!r} " f"cannot be specified without also specifying " f"{construct_path([self.table_name, 'build-backend'])!r}" ) return cast(BuildSystemDict, parsed_config) class PEP621Parser(RequiredKeysConfigParser): """ Parser for :pep:`621` metadata from ``pyproject.toml``. .. autosummary-widths:: 1/2 """ table_name: ClassVar[str] = "project" keys: List[str] = [ "name", "version", "description", "readme", "requires-python", "license", "authors", "maintainers", "keywords", "classifiers", "urls", "scripts", "gui-scripts", "entry-points", "dependencies", "optional-dependencies", ] required_keys: ClassVar[List[str]] = ["name"] defaults: ClassVar[Dict[str, Any]] = { "version": None, "description": None, "readme": None, "requires-python": None, "license": None, } factories: ClassVar[Dict[str, Callable[..., Any]]] = { "authors": list, "maintainers": list, "keywords": list, "classifiers": list, "urls": dict, "scripts": dict, "gui-scripts": dict, "entry-points": dict, "dependencies": list, "optional-dependencies": dict, } @staticmethod @_documentation_url("https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.name") def parse_name(config: Dict[str, TOML_TYPES]) -> str: """ Parse the :pep621:`name` key, giving the name of the project. * **Format**: :toml:`String` * **Core Metadata**: :core-meta:`Name` This key is required, and must be defined statically. Tools SHOULD normalize this name, as specified by :pep:`503`, as soon as it is read for internal consistency. :bold-title:`Example:` .. code-block:: TOML [project] name = "spam" :param config: The unparsed TOML config for the :pep621:`project table `. """ name = config["name"] normalized_name = _NormalisedName(normalize(name)) normalized_name.unnormalized = name # https://packaging.python.org/specifications/core-metadata/#name if not name_re.match(normalized_name): raise BadConfigError(f"The value {name!r} for 'project.name' is invalid.") return normalized_name @staticmethod def parse_version(config: Dict[str, TOML_TYPES]) -> Version: """ Parse the :pep621:`version` key, giving the version of the project as supported by :pep:`440`. * **Format**: :toml:`String` * **Core Metadata**: :core-meta:`Version` Users SHOULD prefer to specify normalized versions. :bold-title:`Example:` .. code-block:: TOML [project] version = "2020.0.0" :param config: The unparsed TOML config for the :pep621:`project table `. """ version = str(config["version"]) try: return Version(str(version)) except InvalidVersion as e: e.note = "versions must follow PEP 440" # type: ignore[attr-defined] e.documentation = "https://peps.python.org/pep-0440/" # type: ignore[attr-defined] raise @_documentation_url("https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.description") def parse_description(self, config: Dict[str, TOML_TYPES]) -> str: """ Parse the :pep621:`description` key, giving a summary description of the project. * **Format**: :toml:`String` * **Core Metadata**: :core-meta:`Summary` :bold-title:`Example:` .. code-block:: TOML [project] description = "Lovely Spam! Wonderful Spam!" :param config: The unparsed TOML config for the :pep621:`project table `. """ description = config["description"] self.assert_type(description, str, ["project", "description"]) return description.strip() @staticmethod @_documentation_url("https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.readme") def parse_readme(config: Dict[str, TOML_TYPES]) -> Readme: """ Parse the :pep621:`readme` key, giving the full description of the project (i.e. the README). * **Format**: :toml:`String` or :toml:`table` * **Core Metadata**: :core-meta:`Description` This field accepts either a string or a table. If it is a string then it is the relative path to a text file containing the full description. The file's encoding MUST be UTF-8, and have one of the following content types: * ``text/markdown``, with a case-insensitive ``.md`` suffix. * ``text/x-rst``, with a case-insensitive ``.rst`` suffix. * ``text/plain``, with a case-insensitive ``.txt`` suffix. If a tool recognizes more extensions than this PEP, they MAY infer the content-type for the user without specifying this field as dynamic. For all unrecognized suffixes when a content-type is not provided, tools MUST raise an error. .. space:: .. latex:clearpage:: The readme field may instead be a table with the following keys: * ``file`` -- a string value representing a relative path to a file containing the full description. * ``text`` -- a string value which is the full description. * ``content-type`` -- (required) a string specifying the content-type of the full description. * ``charset`` -- (optional, default UTF-8) the encoding of the ``file``. Tools MAY support other encodings if they choose to. The ``file`` and ``text`` keys are mutually exclusive, but one must be provided in the table. :bold-title:`Examples:` .. code-block:: TOML [project] readme = "README.rst" [project.readme] file = "README.rst" content-type = "text/x-rst" encoding = "UTF-8" [project.readme] text = "Spam is a brand of canned cooked pork made by Hormel Foods Corporation." content-type = "text/x-rst" :param config: The unparsed TOML config for the :pep621:`project table `. """ readme: Union[Dict, str] = config["readme"] if isinstance(readme, str): # path to readme_file readme_content_type = content_type_from_filename(readme) render_readme(readme, readme_content_type) return Readme(file=readme, content_type=readme_content_type) elif isinstance(readme, dict): if not readme: raise BadConfigError("The 'project.readme' table cannot be empty.") if "file" in readme and "text" in readme: raise BadConfigError( "The 'project.readme.file' and 'project.readme.text' keys " "are mutually exclusive." ) elif set(readme.keys()) in ({"file"}, {"file", "charset"}): readme_encoding = readme.get("charset", "UTF-8") render_readme(readme["file"], encoding=readme_encoding) readme_content_type = content_type_from_filename(readme["file"]) return Readme(file=readme["file"], content_type=readme_content_type, charset=readme_encoding) elif set(readme.keys()) in ({"file", "content-type"}, {"file", "charset", "content-type"}): readme_encoding = readme.get("charset", "UTF-8") render_readme(readme["file"], encoding=readme_encoding) return Readme(file=readme["file"], content_type=readme["content-type"], charset=readme_encoding) elif "content-type" in readme and "text" not in readme: raise BadConfigError( "The 'project.readme.content-type' key cannot be provided on its own; " "Please provide the 'project.readme.text' key too." ) elif "charset" in readme and "text" not in readme: raise BadConfigError( "The 'project.readme.charset' key cannot be provided on its own; " "Please provide the 'project.readme.text' key too." ) elif "text" in readme: if "content-type" not in readme: raise BadConfigError( "The 'project.readme.content-type' key must be provided " "when 'project.readme.text' is given." ) elif readme["content-type"] not in {"text/markdown", "text/x-rst", "text/plain"}: raise BadConfigError( f"Unrecognised value for 'project.readme.content-type': {readme['content-type']!r}" ) if "charset" in readme: raise BadConfigError( "The 'project.readme.charset' key cannot be provided " "when 'project.readme.text' is given." ) return Readme(text=readme["text"], content_type=readme["content-type"]) else: raise BadConfigError(f"Unknown format for 'project.readme': {readme!r}") raise TypeError(f"Unsupported type for 'project.readme': {type(readme)!r}") @staticmethod def parse_requires_python(config: Dict[str, TOML_TYPES]) -> SpecifierSet: """ Parse the :pep621:`requires-python` key, giving the Python version requirements of the project. The requirement should be in the form of a :pep:`508` marker. * **Format**: :toml:`String` * **Core Metadata**: :core-meta:`Requires-Python` :bold-title:`Example:` .. code-block:: TOML [project] requires-python = ">=3.6" :param config: The unparsed TOML config for the :pep621:`project table `. :rtype: .. latex:clearpage:: """ version = str(config["requires-python"]) try: return SpecifierSet(str(version)) except InvalidSpecifier as e: e.note = "specifiers must follow PEP 508" # type: ignore[attr-defined] e.documentation = "https://peps.python.org/pep-0508/" # type: ignore[attr-defined] raise @staticmethod @_documentation_url("https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.license") def parse_license(config: Dict[str, TOML_TYPES]) -> License: """ Parse the :pep621:`license` key. * **Format**: :toml:`Table` * **Core Metadata**: :core-meta:`License` The table may have one of two keys: * ``file`` -- a string value that is a relative file path to the file which contains the license for the project. The file's encoding MUST be UTF-8. * ``text`` -- string value which is the license of the project. These keys are mutually exclusive, so a tool MUST raise an error if the metadata specifies both keys. :bold-title:`Example:` .. code-block:: TOML [project.license] file = "LICENSE.rst" [project.license] file = "COPYING" [project.license] text = \"\"\" This software may only be obtained by sending the author a postcard, and then the user promises not to redistribute it. \"\"\" :param config: The unparsed TOML config for the :pep621:`project table `. """ # noqa: D300,D301 project_license = config["license"] if "text" in project_license and "file" in project_license: raise BadConfigError( "The 'project.license.file' and 'project.license.text' keys " "are mutually exclusive." ) elif "text" in project_license: return License(text=str(project_license["text"])) elif "file" in project_license: os.stat(project_license["file"]) return License(project_license["file"]) else: raise BadConfigError("The 'project.license' table should contain one of 'text' or 'file'.") @staticmethod def _parse_authors(config: Dict[str, TOML_TYPES], key_name: str = "authors") -> List[Author]: all_authors: List[Author] = [] for idx, author in enumerate(config[key_name]): name = author.get("name", None) email = author.get("email", None) if name is not None and ',' in name: raise BadConfigError(f"The 'project.{key_name}[{idx}].name' key cannot contain commas.") if email is not None: try: email = validate_email(email).email except EmailSyntaxError as e: raise BadConfigError(f"Invalid email {email!r}: {e} ") all_authors.append({"name": name, "email": email}) # TODO: error/warn on extra keys return all_authors @_documentation_url("https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.authors") def parse_authors(self, config: Dict[str, TOML_TYPES]) -> List[Author]: """ Parse the :pep621:`authors` key. * **Format**: :toml:`Array` of :toml:`inline tables ` with string keys and values * **Core Metadata**: :core-meta:`Author/Author-email` The tables list the people or organizations considered to be the "authors" of the project. Each table has 2 keys: ``name`` and ``email``. Both values must be strings. * The ``name`` value MUST be a valid email name (i.e. whatever can be put as a name, before an email, in :rfc:`822`) and not contain commas. * The ``email`` value MUST be a valid email address. Both keys are optional. Using the data to fill in core metadata is as follows: 1. If only ``name`` is provided, the value goes in :core-meta:`Author`. 2. If only ``email`` is provided, the value goes in :core-meta:`Author-email`. 3. If both ``email`` and ``name`` are provided, the value goes in :core-meta:`Author-email`. The value should be formatted as ``{name} <{email}>`` (with appropriate quoting, e.g. using :class:`email.headerregistry.Address`). 4. Multiple values should be separated by commas. :bold-title:`Example:` .. code-block:: TOML [project] authors = [ {email = "hi@pradyunsg.me"}, {name = "Tzu-Ping Chung"} ] [[project.authors]] name = "Tzu-Ping Chung" :param config: The unparsed TOML config for the :pep621:`project table `. """ return self._parse_authors(config, "authors") @_documentation_url("https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.maintainers") def parse_maintainers(self, config: Dict[str, TOML_TYPES]) -> List[Author]: """ Parse the :pep621:`maintainers` key. * **Format**: :toml:`Array` of :toml:`inline tables ` with string keys and values * **Core Metadata**: :core-meta:`Maintainer/Maintainer-email` The tables list the people or organizations considered to be the "maintainers" of the project. Each table has 2 keys: ``name`` and ``email``. Both values must be strings. * The ``name`` value MUST be a valid email name (i.e. whatever can be put as a name, before an email, in :rfc:`822`) and not contain commas. * The ``email`` value MUST be a valid email address. Both keys are optional. 1. If only ``name`` is provided, the value goes in :core-meta:`Maintainer`. 2. If only ``email`` is provided, the value goes in :core-meta:`Maintainer-email`. 3. If both ``email`` and ``name`` are provided, the value goes in :core-meta:`Maintainer-email`, with the format ``{name} <{email}>`` (with appropriate quoting, e.g. using :class:`email.headerregistry.Address`). 4. Multiple values should be separated by commas. :param config: The unparsed TOML config for the :pep621:`project table `. """ return self._parse_authors(config, "maintainers") @_documentation_url("https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.keywords") def parse_keywords(self, config: Dict[str, TOML_TYPES]) -> List[str]: """ Parse the :pep621:`keywords` key, giving the keywords for the project. * **Format**: :toml:`Array` of :toml:`strings ` * **Core Metadata**: :core-meta:`Keywords` .. latex:vspace:: -5px :bold-title:`Example:` .. code-block:: TOML [project] keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"] .. latex:vspace:: -5px :param config: The unparsed TOML config for the :pep621:`project table `. """ parsed_keywords = set() key_path = [self.table_name, "keywords"] self.assert_sequence_not_str(config["keywords"], key_path) for idx, keyword in enumerate(config["keywords"]): self.assert_indexed_type(keyword, str, key_path, idx=idx) parsed_keywords.add(keyword) return natsorted(parsed_keywords, alg=ns.GROUPLETTERS) @_documentation_url("https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.classifiers") def parse_classifiers(self, config: Dict[str, TOML_TYPES]) -> List[str]: """ Parse the :pep621:`classifiers` key, giving the `trove classifiers`_ which apply to the project. .. _trove classifiers: https://pypi.org/classifiers/ * **Format**: :toml:`Array` of :toml:`strings ` * **Core Metadata**: :core-meta:`Classifiers` :bold-title:`Example:` .. code-block:: TOML [project] classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python" ] :param config: The unparsed TOML config for the :pep621:`project table `. """ parsed_classifiers = set() key_path = [self.table_name, "classifiers"] self.assert_sequence_not_str(config["classifiers"], key_path) for idx, keyword in enumerate(config["classifiers"]): self.assert_indexed_type(keyword, str, key_path, idx=idx) parsed_classifiers.add(keyword) validate_classifiers(parsed_classifiers) return natsorted(parsed_classifiers) @_documentation_url("https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.urls") def parse_urls(self, config: Dict[str, TOML_TYPES]) -> Dict[str, str]: """ Parse the :pep621:`urls` table. * **Format**: :toml:`Table`, with keys and values of :toml:`strings ` * **Core Metadata**: :core-meta:`Project-URL` A table of URLs where the key is the URL label and the value is the URL itself. :bold-title:`Example:` .. code-block:: TOML [project.urls] homepage = "https://example.com" documentation = "https://readthedocs.org" repository = "https://github.com" changelog = "https://github.com/me/spam/blob/master/CHANGELOG.md" :param config: The unparsed TOML config for the :pep621:`project table `. """ parsed_urls = {} project_urls = config["urls"] self.assert_type(project_urls, dict, ["project", "urls"]) for category, url in project_urls.items(): path = ("project", "urls", category) self.assert_value_type(url, str, path) if len(category) > 32: name = construct_path(path) raise ValueError(f"{name!r}: label too long (max 32 characters)") parsed_urls[category] = str(URL(url)) return parsed_urls @_documentation_url("https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.scripts") def parse_scripts(self, config: Dict[str, TOML_TYPES]) -> Dict[str, str]: """ Parse the :pep621:`scripts` table. **Format**: :toml:`Table`, with keys and values of :toml:`strings ` The console scripts provided by the project. The keys are the names of the scripts and the values are the object references in the form ``module.submodule:object``. .. latex:vspace:: -5px :bold-title:`Example:` .. code-block:: TOML [project.scripts] spam-cli = "spam:main_cli" :param config: The unparsed TOML config for the :pep621:`project table `. """ scripts = config["scripts"] self.assert_type(scripts, dict, ["project", "scripts"]) for name, func in scripts.items(): self.assert_value_type(func, str, ["project", "scripts", name]) return scripts @_documentation_url("https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.gui-scripts") def parse_gui_scripts(self, config: Dict[str, TOML_TYPES]) -> Dict[str, str]: """ Parse the :pep621:`gui-scripts` table. **Format**: table, with keys and values of strings The graphical application scripts provided by the project. The keys are the names of the scripts and the values are the object references in the form ``module.submodule:object``. :bold-title:`Example:` .. code-block:: TOML [project.gui-scripts] spam-gui = "spam.gui:main_gui" :param config: The unparsed TOML config for the :pep621:`project table `. """ gui_scripts = config["gui-scripts"] self.assert_type(gui_scripts, dict, ["project", "gui-scripts"]) for name, func in gui_scripts.items(): self.assert_value_type(func, str, ["project", "gui-scripts", name]) return gui_scripts @_documentation_url("https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.entry-points") def parse_entry_points(self, config: Dict[str, TOML_TYPES]) -> Dict[str, Dict[str, str]]: """ Parse the :pep621:`entry-points` table. **Format**: :toml:`Table` of :toml:`tables `, with keys and values of :toml:`strings ` Each sub-table's name is an entry point group. * Users MUST NOT create nested sub-tables but instead keep the entry point groups to only one level deep. * Users MUST NOT created sub-tables for ``console_scripts`` or ``gui_scripts``. Use ``[project.scripts]`` and ``[project.gui-scripts]`` instead. See the `entry point specification`_ for more details. .. _entry point specification: https://packaging.python.org/specifications/entry-points/ :bold-title:`Example:` .. code-block:: TOML [project.entry-points."spam.magical"] tomatoes = "spam:main_tomatoes" # pytest plugins refer to a module, so there is no ':obj' [project.entry-points.pytest11] nbval = "nbval.plugin" :param config: The unparsed TOML config for the :pep621:`project table `. :rtype: .. latex:clearpage:: """ entry_points = config["entry-points"] self.assert_type(entry_points, dict, ["project", "entry-points"]) for group, sub_table in entry_points.items(): self.assert_value_type(sub_table, dict, ["project", "entry-points", group]) if normalize(group) in "console-scripts": name = construct_path(["project", "entry-points"]) suggested_name = construct_path(["project", "scripts"]) raise BadConfigError( f"{name!r} may not contain a {group!r} sub-table. Use {suggested_name!r} instead." ) elif normalize(group) in "gui-scripts": name = construct_path(["project", "entry-points"]) suggested_name = construct_path(["project", "gui-scripts"]) raise BadConfigError( f"{name!r} may not contain a {group!r} sub-table. Use {suggested_name!r} instead." ) for name, func in sub_table.items(): self.assert_value_type(func, str, ["project", "entry-points", group, name]) return entry_points @staticmethod def normalize_requirement_name(name: str) -> str: """ Function to normalize a requirement name per e.g. :pep:`503` (where underscores are replaced by hyphens). .. versionadded:: 0.9.0 """ return normalize(name) @_documentation_url("https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.dependencies") def parse_dependencies(self, config: Dict[str, TOML_TYPES]) -> List[ComparableRequirement]: """ Parse the :pep621:`dependencies` key, giving the dependencies of the project. * **Format**: :toml:`Array` of :pep:`508` strings * **Core Metadata**: :core-meta:`Requires-Dist` Each string MUST be formatted as a valid :pep:`508` string. :bold-title:`Example:` .. code-block:: TOML [project] dependencies = [ "httpx", "gidgethub[httpx]>4.0.0", "django>2.1; os_name != 'nt'", "django>2.0; os_name == 'nt'" ] :param config: The unparsed TOML config for the :pep621:`project table `. """ parsed_dependencies = set() key_path = [self.table_name, "dependencies"] self.assert_sequence_not_str(config["dependencies"], key_path) for idx, raw_requirement in enumerate(config["dependencies"]): self.assert_indexed_type(raw_requirement, str, key_path, idx=idx) try: requirement = ComparableRequirement(raw_requirement) except InvalidRequirement as e: e.args = (f"{raw_requirement!r}\n {str(e)}", ) e.note = "requirements must follow PEP 508" # type: ignore[attr-defined] e.documentation = "https://peps.python.org/pep-0508/" # type: ignore[attr-defined] raise parsed_dependencies.add(requirement) return sorted(combine_requirements( parsed_dependencies, normalize_func=self.normalize_requirement_name, )) @_documentation_url( "https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.optional-dependencies" ) def parse_optional_dependencies( self, config: Dict[str, TOML_TYPES], ) -> Dict[str, List[ComparableRequirement]]: """ Parse the :pep621:`optional-dependencies` table, giving the optional dependencies of the project. * **Format**: :toml:`Table` with values of :toml:`arrays ` of :pep:`508` strings * **Core Metadata**: :core-meta:`Requires-Dist` and :core-meta:`Provides-Extra` .. raw:: html
* The keys specify an extra, and must be valid Python identifiers. * The values are arrays of strings, which must be valid :pep:`508` strings. :bold-title:`Example:` .. code-block:: TOML [project.optional-dependencies] test = [ "pytest < 5.0.0", "pytest-cov[all]" ] :param config: The unparsed TOML config for the :pep621:`project table `. :rtype: .. versionchanged:: 0.5.0 Extra names with hyphens are now considered valid. If two extra names would normalize to the same string per :pep:`685` a warning is emitted. In a future version this will become an error. .. attention:: A future version of `pyproject-parser` will normalize all extra names and write them to ``METADATA`` in this normalized form. """ parsed_optional_dependencies: Dict[str, Set[ComparableRequirement]] = dict() normalized_names: Set[str] = set() # remove for part 2 optional_dependencies: Mapping[str, Any] = config["optional-dependencies"] if not isinstance(optional_dependencies, dict): raise TypeError( "Invalid type for 'project.optional-dependencies': " f"expected {dict!r}, got {type(optional_dependencies)!r}", ) for extra, dependencies in optional_dependencies.items(): # Normalize per PEP 685 normalized_extra = normalize(extra) path = ("project", "optional-dependencies", extra) if normalized_extra in normalized_names: # parsed_optional_dependencies for part 2 warnings.warn( f"{construct_path(path)!r}: " f"Multiple extras were defined with the same normalized name of {normalized_extra!r}", PyProjectDeprecationWarning, ) # For part 2 # raise BadConfigError( # f"{construct_path(path)!r}: " # f"Multiple extras were defined with the same normalized name of {normalized_extra!r}", # ) # https://packaging.python.org/specifications/core-metadata/#provides-extra-multiple-use # if not extra_re.match(normalized_extra): if not (extra.isidentifier() or extra_re.match(normalized_extra)): raise TypeError(f"Invalid extra name {extra!r} ({extra_re.match(normalized_extra)})") self.assert_sequence_not_str(dependencies, path=path) parsed_optional_dependencies[extra] = set() # normalized_extra for part 2 normalized_names.add(normalized_extra) # Remove for part 2 for idx, dep in enumerate(dependencies): if isinstance(dep, str): try: requirement = ComparableRequirement(dep) except InvalidRequirement as e: e.args = (f"{dep!r}\n {str(e)}", ) e.note = "requirements must follow PEP 508" # type: ignore[attr-defined] e.documentation = "https://peps.python.org/pep-0508/" # type: ignore[attr-defined] raise # normalized_extra for part 2 parsed_optional_dependencies[extra].add(requirement) else: raise TypeError( f"Invalid type for 'project.optional-dependencies.{extra}[{idx}]': " f"expected {str!r}, got {type(dep)!r}" ) combined_requirements = {} for extra, deps in parsed_optional_dependencies.items(): combined_requirements[extra] = sorted( combine_requirements(deps, normalize_func=self.normalize_requirement_name) ) return combined_requirements def parse( # type: ignore[override] self, config: Dict[str, TOML_TYPES], set_defaults: bool = False, ) -> ProjectDict: """ Parse the TOML configuration. :param config: :param set_defaults: If :py:obj:`True`, the values in :attr:`self.defaults ` and :attr:`self.factories ` will be set as defaults for the returned mapping. """ dynamic_fields = config.get("dynamic", []) if "name" in dynamic_fields: raise BadConfigError("The 'project.name' field may not be dynamic.") super_parsed_config = super().parse(config, set_defaults=set_defaults) return { **super_parsed_config, # type: ignore[misc] "dynamic": dynamic_fields, } pyproject-parser-0.9.1/pyproject_parser/py.typed000066400000000000000000000000001444752763000221260ustar00rootroot00000000000000pyproject-parser-0.9.1/pyproject_parser/type_hints.py000066400000000000000000000073021444752763000232030ustar00rootroot00000000000000#!/usr/bin/env python3 # # __init__.py """ Type hints for :mod:`pyproject_parser`. """ # # Copyright Β© 2021 Dominic Davis-Foster # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE # OR OTHER DEALINGS IN THE SOFTWARE. # # stdlib from typing import Any, Dict, List, Optional # 3rd party from packaging.markers import Marker from packaging.version import Version from shippinglabel.requirements import ComparableRequirement from typing_extensions import Literal, TypedDict # this package from pyproject_parser.classes import License, Readme __all__ = [ "BuildSystemDict", "Dynamic", "ProjectDict", "Author", "ContentTypes", "ReadmeDict", ] #: :class:`typing.TypedDict` representing the output from the :class:`~.BuildSystemParser` class. BuildSystemDict = TypedDict( "BuildSystemDict", { "requires": List[ComparableRequirement], "build-backend": Optional[str], "backend-path": Optional[List[str]] } ) #: Type hint for the :pep621:`dynamic` field defined in :pep:`621`. Dynamic = Literal[ "name", "version", "description", "readme", "requires-python", "license", "authors", "maintainers", "keywords", "classifiers", "urls", "scripts", "gui-scripts", "entry-points", "dependencies", "optional-dependencies" ] #: :class:`typing.TypedDict` representing the output from the :class:`~.PEP621Parser` class. ProjectDict = TypedDict( "ProjectDict", { "name": str, "version": Optional[Version], "description": Optional[str], "readme": Optional[Readme], "requires-python": Optional[Marker], "license": Optional[License], "authors": List["Author"], "maintainers": List["Author"], "keywords": List[str], "classifiers": List[str], "urls": Dict[str, str], "scripts": Dict[str, str], "gui-scripts": Dict[str, str], "entry-points": Dict[str, Dict[str, str]], "dependencies": List[ComparableRequirement], "optional-dependencies": Dict[str, List[ComparableRequirement]], "dynamic": List[Dynamic], } ) class Author(TypedDict, total=False): """ :class:`typing.TypedDict` representing the items in the :pep621:`authors/maintainers` key of :pep:`621`. """ name: Optional[str] email: Optional[str] ContentTypes = Literal["text/markdown", "text/x-rst", "text/plain"] """ Type hint for the valid content-types in the :pep621:`license` table defined in :pep:`621`. """ class ReadmeDict(TypedDict, total=False): """ :class:`typing.TypedDict` representing the return type of :meth:`~.Readme.to_dict`. """ text: str file: str charset: str content_type: ContentTypes _PyProjectAsTomlDict = TypedDict( "_PyProjectAsTomlDict", {"build-system": Optional[BuildSystemDict], "project": Optional[ProjectDict], "tool": Dict[str, Any]}, ) pyproject-parser-0.9.1/pyproject_parser/utils.py000066400000000000000000000125741444752763000221640ustar00rootroot00000000000000#!/usr/bin/env python3 # # utils.py """ Utility functions. """ # # Copyright Β© 2021-2023 Dominic Davis-Foster # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE # OR OTHER DEALINGS IN THE SOFTWARE. # # stdlib import functools import io import os import sys from typing import TYPE_CHECKING, Any, Dict, Optional # 3rd party from dom_toml.parser import BadConfigError from domdf_python_tools.paths import PathPlus from domdf_python_tools.typing import PathLike if sys.version_info < (3, 11): # 3rd party import tomli as tomllib else: # 3rd party import tomllib if TYPE_CHECKING: # this package from pyproject_parser.type_hints import ContentTypes __all__ = ["render_markdown", "render_rst", "content_type_from_filename", "PyProjectDeprecationWarning"] def render_markdown(content: str) -> None: """ Attempt to render the given content as :wikipedia:`Markdown`. .. extras-require:: readme :pyproject: :scope: function :param content: """ try: # 3rd party import cmarkgfm # type: ignore[import] # noqa: F401 import readme_renderer.markdown # type: ignore[import] except ImportError: # pragma: no cover return rendering_result = readme_renderer.markdown.render(content, stream=sys.stderr) if rendering_result is None: # pragma: no cover raise BadConfigError("Error rendering README.") def render_rst(content: str, filename: PathLike = "") -> None: """ Attempt to render the given content as :wikipedia:`ReStructuredText`. .. extras-require:: readme :pyproject: :scope: function :param content: :param filename: The original filename. .. versionchanged:: 0.8.0 Added the ``filename`` argument. """ try: # 3rd party import docutils.core import readme_renderer.rst # type: ignore[import] from docutils.utils import SystemMessage from docutils.writers.html4css1 import Writer except ImportError: # pragma: no cover return # Adapted from https://github.com/pypa/readme_renderer/blob/main/readme_renderer/rst.py#L106 settings = readme_renderer.rst.SETTINGS.copy() settings["warning_stream"] = io.StringIO() writer = Writer() writer.translator_class = readme_renderer.rst.ReadMeHTMLTranslator try: parts = docutils.core.publish_parts(content, str(filename), writer=writer, settings_overrides=settings) if parts.get("docinfo", '') + parts.get("fragment", ''): # Success! return except SystemMessage: pass if not settings["warning_stream"].tell(): raise BadConfigError("Error rendering README: No content rendered from RST source.") else: sys.stderr.write(settings["warning_stream"].getvalue()) raise BadConfigError("Error rendering README.") @functools.lru_cache() def content_type_from_filename(filename: PathLike) -> "ContentTypes": """ Return the inferred content type for the given (readme) filename. :param filename: """ filename = PathPlus(filename) if filename.suffix.lower() == ".md": return "text/markdown" elif filename.suffix.lower() == ".rst": return "text/x-rst" elif filename.suffix.lower() == ".txt": return "text/plain" raise ValueError(f"Unsupported extension for {filename.as_posix()!r}") def render_readme( readme_file: PathLike, content_type: Optional["ContentTypes"] = None, encoding: str = "UTF-8", ) -> None: """ Attempts to render the given readme file. :param readme_file: :param content_type: The content-type of the readme. If :py:obj:`None` the type will be inferred from the file extension. :param encoding: The encoding to read the file with. """ readme_file = PathPlus(readme_file) if content_type is None: content_type = content_type_from_filename(filename=readme_file) content = readme_file.read_text(encoding=encoding) if int(os.environ.get("CHECK_README", 1)): if content_type == "text/markdown": render_markdown(content) elif content_type == "text/x-rst": render_rst(content, readme_file) class PyProjectDeprecationWarning(Warning): """ Warning for the use of deprecated features in `pyproject.toml`. This is a user-facing warning which will be shown by default. For developer-facing warnings intended for direct consumers of this library, use a standard :class:`DeprecationWarning`. .. versionadded:: 0.5.0 """ def _load_toml(filename: PathLike, ) -> Dict[str, Any]: r""" Parse TOML from the given file. :param filename: The filename to read from to. :returns: A mapping containing the ``TOML`` data. """ return tomllib.loads(PathPlus(filename).read_text()) pyproject-parser-0.9.1/repo_helper.yml000066400000000000000000000045121444752763000200770ustar00rootroot00000000000000# Configuration for 'repo_helper' (https://github.com/domdfcoding/repo_helper) --- modname: 'pyproject-parser' copyright_years: '2021' author: 'Dominic Davis-Foster' email: 'dominic@davis-foster.co.uk' username: 'repo-helper' assignee: 'domdfcoding' primary_conda_channel: "domdfcoding" version: '0.9.1' license: 'MIT' short_desc: "Parser for 'pyproject.toml'" python_deploy_version: 3.6 sphinx_html_theme: furo preserve_custom_theme: true tox_testenv_extras: readme,cli min_coverage: 97.5 on_conda_forge: true mypy_plugins: - attr_utils.mypy_plugin conda_channels: - conda-forge # Versions to run tests for python_versions: - '3.6' - '3.7' - '3.8' - '3.9' - "3.10" - "3.11" - 3.12-dev - pypy36 - pypy37 - pypy38 - pypy39 sphinx_conf_epilogue: - nitpicky = True - toml_spec_version = "0.5.0" - needspace_amount = r"5\baselineskip" - 'ignore_missing_xrefs = ["^toml\.(encoder\.)?TomlEncoder$"]' - '' - import pyproject_parser.utils - import pyproject_parser.type_hints - '' - pyproject_parser.utils.__dict__["ContentTypes"] = pyproject_parser.type_hints.ContentTypes - pyproject_parser.classes.__dict__["ContentTypes"] = pyproject_parser.type_hints.ContentTypes - pyproject_parser.classes.__dict__["ReadmeDict"] = pyproject_parser.type_hints.ReadmeDict extra_sphinx_extensions: - sphinx_click - attr_utils.autoattrs - sphinx_toolbox.pre_commit - sphinx_toolbox.more_autosummary.column_widths - sphinx_packaging.peps - sphinx_packaging.toml - sphinx_toolbox.latex.succinct_seealso - sphinx_toolbox_experimental.missing_xref - local_extension classifiers: - 'Development Status :: 4 - Beta' - 'Intended Audience :: Developers' - 'Topic :: Software Development :: Libraries :: Python Modules' - "Topic :: System :: Archiving :: Packaging" keywords: - pep518 - pep621 - pyproject - toml - metadata - packaging extras_require: readme: - readme-renderer[md]>=27.0 - docutils==0.16 cli: - consolekit>=1.4.1 - click>=7.1.2 - sdjson>=0.3.1 console_scripts: - pyproject-parser=pyproject_parser.__main__:main - check-pyproject=pyproject_parser.__main__:check - pyproject-fmt=pyproject_parser.__main__:reformat - pyproject-info=pyproject_parser.__main__:info intersphinx_mapping: - "'consolekit': ('https://consolekit.readthedocs.io/en/latest/', None)" exclude_files: - contributing tox_unmanaged: - coverage:report pyproject-parser-0.9.1/requirements.txt000066400000000000000000000003241444752763000203310ustar00rootroot00000000000000apeye-core>=1.0.0 attrs>=20.3.0 dom-toml>=0.4.0 domdf-python-tools>=2.8.0 natsort>=7.1.1 packaging>=20.9 shippinglabel>=1.0.0 toml>=0.10.2 tomli>=1.2.3; python_version < "3.11" typing-extensions!=4.7.0,>=3.7.4.3 pyproject-parser-0.9.1/setup.cfg000066400000000000000000000037421444752763000166750ustar00rootroot00000000000000# This file is managed by 'repo_helper'. # You may add new sections, but any changes made to the following sections will be lost: # * metadata # * options # * options.packages.find # * mypy # * options.entry_points [metadata] name = pyproject-parser version = 0.9.1 author = Dominic Davis-Foster author_email = dominic@davis-foster.co.uk license = MIT License keywords = pep518, pep621, pyproject, toml, metadata, packaging long_description = file: README.rst long_description_content_type = text/x-rst platforms = Windows, macOS, Linux url = https://github.com/repo-helper/pyproject-parser project_urls = Documentation = https://pyproject-parser.readthedocs.io/en/latest Issue Tracker = https://github.com/repo-helper/pyproject-parser/issues Source Code = https://github.com/repo-helper/pyproject-parser classifiers = Development Status :: 4 - Beta Intended Audience :: Developers License :: OSI Approved :: MIT License Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: PyPy Topic :: Software Development :: Libraries :: Python Modules Topic :: System :: Archiving :: Packaging Typing :: Typed [options] python_requires = >=3.6.1 zip_safe = False include_package_data = True packages = find: [options.packages.find] exclude = doc-source tests tests.* [options.entry_points] console_scripts = pyproject-parser=pyproject_parser.__main__:main check-pyproject=pyproject_parser.__main__:check pyproject-fmt=pyproject_parser.__main__:reformat pyproject-info=pyproject_parser.__main__:info pyproject-parser-0.9.1/setup.py000066400000000000000000000012101444752763000165520ustar00rootroot00000000000000#!/usr/bin/env python # This file is managed by 'repo_helper'. Don't edit it directly. # stdlib import pathlib import shutil import sys # 3rd party from setuptools import setup sys.path.append('.') # this package from __pkginfo__ import * # pylint: disable=wildcard-import repo_root = pathlib.Path(__file__).parent install_requires = (repo_root / "requirements.txt").read_text(encoding="UTF-8").split('\n') setup( description="Parser for 'pyproject.toml'", extras_require=extras_require, install_requires=install_requires, name="pyproject-parser", py_modules=[], ) shutil.rmtree("pyproject_parser.egg-info", ignore_errors=True) pyproject-parser-0.9.1/stubs.txt000066400000000000000000000001221444752763000167420ustar00rootroot00000000000000git+https://github.com/repo-helper/toml@py.typed attr-utils>=0.5.6 docutils-stubs pyproject-parser-0.9.1/tests/000077500000000000000000000000001444752763000162105ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/__init__.py000066400000000000000000000000001444752763000203070ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/conftest.py000066400000000000000000000024421444752763000204110ustar00rootroot00000000000000# stdlib from typing import Callable, Type, TypeVar, Union # 3rd party from packaging.markers import Marker from packaging.requirements import Requirement from packaging.specifiers import SpecifierSet from packaging.version import Version from pytest_regressions.data_regression import RegressionYamlDumper # this package from pyproject_parser.classes import License, Readme _C = TypeVar("_C", bound=Callable) pytest_plugins = ("coincidence", "consolekit.testing") def _representer_for(*data_type: Type) -> Callable[[_C], _C]: def deco(representer_fn: _C) -> _C: for dtype in data_type: RegressionYamlDumper.add_custom_yaml_representer(dtype, representer_fn) return representer_fn return deco @_representer_for(Version, Requirement, Marker, SpecifierSet) def represent_packaging_types( # noqa: MAN002 dumper: RegressionYamlDumper, data: Union[Version, Requirement, Marker, SpecifierSet], ): return dumper.represent_str(str(data)) @_representer_for(str) def represent_string_subclasses(dumper: RegressionYamlDumper, data: str): # noqa: MAN002 return dumper.represent_str(str(data)) @_representer_for(Readme, License) def represent_readme_or_license( # noqa: MAN002 dumper: RegressionYamlDumper, data: Union[Readme, License], ): return dumper.represent_dict(data.to_dict()) pyproject-parser-0.9.1/tests/requirements.txt000066400000000000000000000003731444752763000214770ustar00rootroot00000000000000# git+https://github.com/repo-helper/pyproject-examples coincidence>=0.2.0 coverage>=5.1 coverage-pyver-pragma>=0.2.1 importlib-metadata>=3.6.0 pyproject-examples>=2023.6.30 pytest>=6.0.0 pytest-cov>=2.8.1 pytest-randomly>=3.7.0 pytest-timeout>=1.4.2 pyproject-parser-0.9.1/tests/test_classes.py000066400000000000000000000242741444752763000212670ustar00rootroot00000000000000# 3rd party import pytest from domdf_python_tools.paths import PathPlus # this package from pyproject_parser import License, Readme class TestReadme: def test_no_test_or_file(self): with pytest.raises(TypeError, match="At least one of 'text' and 'file' must be supplied to .*"): Readme() with pytest.raises(TypeError, match="At least one of 'text' and 'file' must be supplied to .*"): Readme(charset="UTF-8") def test_content_type_sole_arg(self): match = "'content_type' cannot be provided on its own; please provide either 'text' or 'file' or use the 'from_file' method." with pytest.raises(ValueError, match=match): Readme(content_type="text/x-rst") def test_invalid_content_types(self): with pytest.raises(ValueError, match="Unsupported readme content-type 'application/json'"): Readme(text="This is the readme", content_type="application/json") # type: ignore[arg-type] with pytest.raises(ValueError, match="Unsupported readme content-type 'image/jpeg'"): Readme(file="photo.jpg", content_type="image/jpeg") # type: ignore[arg-type] def test_from_file(self, tmp_pathplus: PathPlus): (tmp_pathplus / "README.rst").write_clean("This is the README") readme = Readme.from_file(tmp_pathplus / "README.rst") assert readme.file == tmp_pathplus / "README.rst" assert readme.content_type == "text/x-rst" assert readme.text is None readme.resolve(inplace=True) assert readme.file == tmp_pathplus / "README.rst" assert readme.content_type == "text/x-rst" assert readme.text == "This is the README\n" (tmp_pathplus / "README.txt").write_clean("This is the README") readme = Readme.from_file(tmp_pathplus / "README.txt") assert readme.file == tmp_pathplus / "README.txt" assert readme.content_type == "text/plain" assert readme.text is None readme.resolve(inplace=True) assert readme.file == tmp_pathplus / "README.txt" assert readme.content_type == "text/plain" assert readme.text == "This is the README\n" def test_from_file_bad_filetype(self, tmp_pathplus: PathPlus): with pytest.raises(ValueError, match="Unrecognised filetype for '.*README'"): Readme.from_file(tmp_pathplus / "README") with pytest.raises(ValueError, match="Unrecognised filetype for '.*README.rtf'"): Readme.from_file(tmp_pathplus / "README.rtf") with pytest.raises(ValueError, match="Unrecognised filetype for '.*README.doc'"): Readme.from_file(tmp_pathplus / "README.doc") def test_from_file_charset(self, tmp_pathplus: PathPlus): (tmp_pathplus / "README.md").write_clean("This is the README", encoding="cp1252") readme = Readme.from_file(tmp_pathplus / "README.md", charset="cp1252") assert readme.file == tmp_pathplus / "README.md" assert readme.content_type == "text/markdown" assert readme.text is None readme.resolve(inplace=True) assert readme.file == tmp_pathplus / "README.md" assert readme.content_type == "text/markdown" assert readme.text == "This is the README\n" def test_resolve(self, tmp_pathplus: PathPlus): (tmp_pathplus / "README.txt").write_clean("This is the README") readme = Readme(file=tmp_pathplus / "README.txt", content_type="text/plain") assert readme.file == tmp_pathplus / "README.txt" assert readme.content_type == "text/plain" assert readme.text is None resolved_readme = readme.resolve() assert readme is not resolved_readme assert resolved_readme.file == tmp_pathplus / "README.txt" assert resolved_readme.content_type == "text/plain" assert resolved_readme.text == "This is the README\n" def test_resolve_inplace(self, tmp_pathplus: PathPlus): (tmp_pathplus / "README.txt").write_clean("This is the README") readme = Readme(file=tmp_pathplus / "README.txt", content_type="text/plain") assert readme.file == tmp_pathplus / "README.txt" assert readme.content_type == "text/plain" assert readme.text is None resolved_readme = readme.resolve(inplace=True) assert readme is resolved_readme assert readme.file == tmp_pathplus / "README.txt" assert readme.content_type == "text/plain" assert readme.text == "This is the README\n" assert resolved_readme.file == tmp_pathplus / "README.txt" assert resolved_readme.content_type == "text/plain" assert resolved_readme.text == "This is the README\n" def test_to_dict(self, tmp_pathplus: PathPlus): (tmp_pathplus / "README.rst").write_clean("This is the README") readme = Readme(file=tmp_pathplus / "README.rst", content_type="text/x-rst") assert readme.to_dict() == { "file": f"{tmp_pathplus.as_posix()}/README.rst", "content_type": "text/x-rst", } assert Readme.from_dict(readme.to_dict()) == readme assert Readme(**readme.to_dict()) == readme (tmp_pathplus / "README.rst").write_clean("This is the README", encoding="cp1252") readme = Readme(file=tmp_pathplus / "README.rst", content_type="text/x-rst", charset="cp1252") assert readme.to_dict() == { "file": f"{tmp_pathplus.as_posix()}/README.rst", "content_type": "text/x-rst", "charset": "cp1252", } assert Readme.from_dict(readme.to_dict()) == readme assert Readme(**readme.to_dict()) == readme readme = Readme(text="This is the README", content_type="text/x-rst") assert readme.to_dict() == { "text": "This is the README", "content_type": "text/x-rst", } assert Readme.from_dict(readme.to_dict()) == readme assert Readme(**readme.to_dict()) == readme def test_to_pep621_dict(self, tmp_pathplus: PathPlus): (tmp_pathplus / "README.rst").write_clean("This is the README") readme = Readme(file=tmp_pathplus / "README.rst", content_type="text/x-rst") assert readme.to_pep621_dict() == { "file": f"{tmp_pathplus.as_posix()}/README.rst", } # TODO: the type ignore needs the TypedDict function fix in typing-extensions assert Readme.from_dict(readme.to_pep621_dict()) == readme # type: ignore[arg-type] (tmp_pathplus / "README.md").write_clean("This is the README") readme = Readme(file=tmp_pathplus / "README.md", content_type="text/x-rst") assert readme.to_pep621_dict() == { "file": f"{tmp_pathplus.as_posix()}/README.md", "content-type": "text/x-rst", } # TODO: the type ignore needs the TypedDict function fix in typing-extensions assert Readme.from_dict(readme.to_pep621_dict()) == readme # type: ignore[arg-type] (tmp_pathplus / "README.rst").write_clean("This is the README", encoding="cp1252") readme = Readme(file=tmp_pathplus / "README.rst", content_type="text/x-rst", charset="cp1252") assert readme.to_pep621_dict() == { "file": f"{tmp_pathplus.as_posix()}/README.rst", "charset": "cp1252", } # TODO: the type ignore needs the TypedDict function fix in typing-extensions assert Readme.from_dict(readme.to_pep621_dict()) == readme # type: ignore[arg-type] readme = Readme(text="This is the README", content_type="text/x-rst") assert readme.to_pep621_dict() == { "text": "This is the README", "content-type": "text/x-rst", } # TODO: the type ignore needs the TypedDict function fix in typing-extensions assert Readme.from_dict(readme.to_pep621_dict()) == readme # type: ignore[arg-type] class TestLicense: def test_no_test_or_file(self): with pytest.raises(TypeError, match="At least one of 'text' and 'file' must be supplied to .*"): License() def test_resolve(self, tmp_pathplus: PathPlus): (tmp_pathplus / "LICENSE.txt").write_clean("This is the LICENSE") lic = License(file=tmp_pathplus / "LICENSE.txt") assert lic.file == tmp_pathplus / "LICENSE.txt" assert lic.text is None resolved_lic = lic.resolve() assert lic is not resolved_lic assert resolved_lic.file == tmp_pathplus / "LICENSE.txt" assert resolved_lic.text == "This is the LICENSE\n" def test_resolve_inplace(self, tmp_pathplus: PathPlus): (tmp_pathplus / "LICENSE.txt").write_clean("This is the LICENSE") lic = License(file=tmp_pathplus / "LICENSE.txt") assert lic.file == tmp_pathplus / "LICENSE.txt" assert lic.text is None resolved_lic = lic.resolve(inplace=True) assert lic is resolved_lic assert lic.file == tmp_pathplus / "LICENSE.txt" assert lic.text == "This is the LICENSE\n" assert resolved_lic.file == tmp_pathplus / "LICENSE.txt" assert resolved_lic.text == "This is the LICENSE\n" def test_to_dict(self, tmp_pathplus: PathPlus): (tmp_pathplus / "LICENSE.rst").write_clean("This is the LICENSE") lic = License(file=tmp_pathplus / "LICENSE.rst") assert lic.to_dict() == {"file": f"{tmp_pathplus.as_posix()}/LICENSE.rst"} assert License.from_dict(lic.to_dict()) == lic assert License(**lic.to_dict()) == lic (tmp_pathplus / "LICENSE.rst").write_clean("This is the LICENSE", encoding="cp1252") lic = License(file=tmp_pathplus / "LICENSE.rst") assert lic.to_dict() == {"file": f"{tmp_pathplus.as_posix()}/LICENSE.rst"} assert License.from_dict(lic.to_dict()) == lic assert License(**lic.to_dict()) == lic lic = License(text="This is the LICENSE") assert lic.to_dict() == {"text": "This is the LICENSE"} assert License.from_dict(lic.to_dict()) == lic assert License(**lic.to_dict()) == lic def test_to_pep621_dict(self, tmp_pathplus: PathPlus): (tmp_pathplus / "LICENSE.rst").write_clean("This is the LICENSE") lic = License(file=tmp_pathplus / "LICENSE.rst") assert lic.to_pep621_dict() == {"file": f"{tmp_pathplus.as_posix()}/LICENSE.rst"} assert License.from_dict(lic.to_pep621_dict()) == lic (tmp_pathplus / "LICENSE.md").write_clean("This is the LICENSE") lic = License(file=tmp_pathplus / "LICENSE.md") assert lic.to_pep621_dict() == {"file": f"{tmp_pathplus.as_posix()}/LICENSE.md"} assert License.from_dict(lic.to_pep621_dict()) == lic (tmp_pathplus / "LICENSE.rst").write_clean("This is the LICENSE", encoding="cp1252") lic = License(file=tmp_pathplus / "LICENSE.rst") assert lic.to_pep621_dict() == {"file": f"{tmp_pathplus.as_posix()}/LICENSE.rst"} assert License.from_dict(lic.to_pep621_dict()) == lic lic = License(text="This is the LICENSE") assert lic.to_pep621_dict() == {"text": "This is the LICENSE"} assert License.from_dict(lic.to_pep621_dict()) == lic (tmp_pathplus / "LICENSE").write_clean("This is the LICENSE") lic = License(text="This is the LICENSE\n", file=tmp_pathplus / "LICENSE") assert lic.to_pep621_dict() == {"file": (tmp_pathplus / "LICENSE").as_posix()} assert License.from_dict(lic.to_pep621_dict()).resolve() == lic pyproject-parser-0.9.1/tests/test_cli.py000066400000000000000000000353761444752763000204060ustar00rootroot00000000000000# stdlib import json import re import subprocess import warnings from typing import Optional, Type # 3rd party import click import pytest from coincidence.regressions import AdvancedDataRegressionFixture, AdvancedFileRegressionFixture from consolekit.testing import CliRunner, Result from consolekit.tracebacks import handle_tracebacks from dom_toml.parser import BadConfigError from domdf_python_tools.paths import PathPlus, in_directory from pyproject_examples import valid_buildsystem_config, valid_pep621_config from pyproject_examples.example_configs import ( COMPLETE_A, COMPLETE_A_WITH_FILES, COMPLETE_B, COMPLETE_PROJECT_A, MINIMAL_CONFIG ) # this package from pyproject_parser.__main__ import check, info, reformat from pyproject_parser.cli import ConfigTracebackHandler from tests.test_dumping import COMPLETE_UNDERSCORE_NAME, UNORDERED @pytest.mark.parametrize( "toml_string", [ pytest.param(COMPLETE_A, id="COMPLETE_A"), pytest.param(COMPLETE_A_WITH_FILES, id="COMPLETE_A_WITH_FILES"), pytest.param(COMPLETE_B, id="COMPLETE_B"), pytest.param(COMPLETE_PROJECT_A, id="COMPLETE_PROJECT_A"), pytest.param(UNORDERED, id="UNORDERED"), pytest.param(COMPLETE_UNDERSCORE_NAME, id="COMPLETE_UNDERSCORE_NAME"), ] ) @pytest.mark.parametrize("show_diff", [True, False]) def test_reformat( tmp_pathplus: PathPlus, toml_string: str, cli_runner: CliRunner, advanced_file_regression: AdvancedFileRegressionFixture, show_diff: bool, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_string) (tmp_pathplus / "README.rst").write_clean("This is the README") (tmp_pathplus / "LICENSE").write_clean("This is the LICENSE") if show_diff: args = ["--no-colour", "--show-diff"] else: args = [] with in_directory(tmp_pathplus): result: Result = cli_runner.invoke(reformat, args=args, catch_exceptions=False) assert result.exit_code == 1 advanced_file_regression.check_file(tmp_pathplus / "pyproject.toml") result.check_stdout(advanced_file_regression, extension=".diff") # Should be no changes with in_directory(tmp_pathplus): result = cli_runner.invoke(reformat, args=args, catch_exceptions=False) assert result.exit_code == 0 advanced_file_regression.check_file(tmp_pathplus / "pyproject.toml") assert result.stdout == "Reformatting 'pyproject.toml'\n" @pytest.mark.parametrize("toml_string", [*valid_pep621_config, *valid_buildsystem_config]) def test_check( toml_string: str, tmp_pathplus: PathPlus, cli_runner: CliRunner, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_string) with in_directory(tmp_pathplus): result: Result = cli_runner.invoke(check, catch_exceptions=False) assert result.exit_code == 0 assert result.stdout == "Validating 'pyproject.toml'\n" @pytest.mark.parametrize( "toml_string", [ pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"dev_test" = []\n"dev-test" = []', id="duplicate_extra_1", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"dev-test" = []\n"dev_test" = []', id="duplicate_extra_2", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"dev.test" = []\n"dev_test" = []', id="duplicate_extra_3", ), ] ) def test_check_extra_deprecation( toml_string: str, tmp_pathplus: PathPlus, cli_runner: CliRunner, advanced_file_regression: AdvancedFileRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_string) cli_runner.mix_stderr = False with in_directory(tmp_pathplus), warnings.catch_warnings(): warnings.simplefilter("error") result: Result = cli_runner.invoke(check, catch_exceptions=False) assert result.exit_code == 1 assert result.stdout == "Validating 'pyproject.toml'\n" advanced_file_regression.check(result.stderr) @pytest.mark.parametrize( "toml_string", [ pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"dev_test" = []\n"dev-test" = []', id="duplicate_extra_1", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"dev-test" = []\n"dev_test" = []', id="duplicate_extra_2", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"dev.test" = []\n"dev_test" = []', id="duplicate_extra_3", ), ] ) def test_check_extra_deprecation_warning( toml_string: str, tmp_pathplus: PathPlus, cli_runner: CliRunner, advanced_file_regression: AdvancedFileRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_string) args = ["pyproject-parser", "check"] with in_directory(tmp_pathplus): process = subprocess.run( args, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, ) assert process.returncode == 0 advanced_file_regression.check(process.stdout.decode("UTF-8")) @pytest.mark.parametrize( "toml_string, match", [ pytest.param( "[build-system]\nrequires = []\nfoo = 'bar'", r"Unknown key in '\[build-system\]': 'foo'", id="build-system", ), pytest.param( "[project]\nname = 'whey'\nfoo = 'bar'\nbar = 123\ndynamic = ['version']", r"Unknown keys in '\[project\]': 'bar' and 'foo", id="project", ), pytest.param( "[coverage]\nomit = 'demo.py'\n[flake8]\nselect = ['F401']", "Unexpected top-level key 'coverage'. Only 'build-system', 'project' and 'tool' are allowed.", id="top-level", ), pytest.param( "[build_system]\nbackend = 'whey'", "Unexpected top-level key 'build_system'. Did you mean 'build-system'", id="top_level_typo_underscore", ), pytest.param( "[Build-System]\nbackend = 'whey'", "Unexpected top-level key 'Build-System'. Did you mean 'build-system'", id="top_level_typo_caps", ), ] ) def test_check_error( toml_string: str, tmp_pathplus: PathPlus, match: str, cli_runner: CliRunner, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_string) with pytest.raises(BadConfigError, match=match), in_directory(tmp_pathplus): cli_runner.invoke(check, catch_exceptions=False, args=["-T"]) @pytest.mark.parametrize( "toml_string", [ pytest.param( "[build-system]\nrequires = []\nfoo = 'bar'", id="build-system", ), pytest.param( "[project]\nname = 'whey'\nfoo = 'bar'\nbar = 123\ndynamic = ['version']", id="project", ), pytest.param( "[coverage]\nomit = 'demo.py'\n[flake8]\nselect = ['F401']", id="top-level", ), pytest.param( "[build_system]\nbackend = 'whey'", id="top_level_typo_underscore", ), pytest.param( "[Build-System]\nbackend = 'whey'", id="top_level_typo_caps", ), pytest.param( '[project]\nname = "???????12345=============β˜ƒ"\nversion = "2020.0.0"', id="bad_name" ), pytest.param('[project]\nname = "spam"\nversion = "???????12345=============β˜ƒ"', id="bad_version"), pytest.param( f'{MINIMAL_CONFIG}\nrequires-python = "???????12345=============β˜ƒ"', id="bad_requires_python" ), pytest.param(f'{MINIMAL_CONFIG}\nauthors = [{{name = "Bob, Alice"}}]', id="author_comma"), ] ) def test_check_error_caught( toml_string: str, tmp_pathplus: PathPlus, cli_runner: CliRunner, advanced_file_regression: AdvancedFileRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_string) cli_runner.mix_stderr = False with in_directory(tmp_pathplus): result: Result = cli_runner.invoke(check) assert result.exit_code == 1 assert result.stdout == "Validating 'pyproject.toml'\n" advanced_file_regression.check(result.stderr) exceptions = pytest.mark.parametrize( "exception", [ pytest.param( FileNotFoundError(2, "No such file or directory", "foo.txt"), id="FileNotFoundError", ), pytest.param( FileNotFoundError(2, "No such file or directory", PathPlus("foo.txt")), id="FileNotFoundError_path" ), pytest.param( FileNotFoundError(2, "No such file or directory", PathPlus("foo.txt"), -1, "bar.md"), id="FileNotFoundError_path_move_etc" ), pytest.param( FileNotFoundError(2, "The system cannot find the file specified", "foo.txt"), id="FileNotFoundError_win", ), pytest.param( FileNotFoundError(2, "The system cannot find the file specified", PathPlus("foo.txt")), id="FileNotFoundError_path_win" ), pytest.param( FileNotFoundError( 2, "The system cannot find the file specified", PathPlus("foo.txt"), -1, "bar.md", ), id="FileNotFoundError_path_move_etc_win" ), pytest.param(FileExistsError("foo.txt"), id="FileExistsError"), pytest.param(Exception("Something's awry!"), id="Exception"), pytest.param(ValueError("'age' must be >= 0"), id="ValueError"), pytest.param(TypeError("Expected type int, got type str"), id="TypeError"), pytest.param(NameError("name 'hello' is not defined"), id="NameError"), pytest.param(SyntaxError("invalid syntax"), id="SyntaxError"), pytest.param(BadConfigError("Expected a string value for 'name'"), id="BadConfigError"), pytest.param(KeyError("name"), id="KeyError"), pytest.param(AttributeError("type object 'list' has no attribute 'foo'"), id="AttributeError"), pytest.param(ImportError("No module named 'foo'"), id="ImportError"), pytest.param(ModuleNotFoundError("No module named 'foo'"), id="ModuleNotFoundError"), ] ) @exceptions def test_traceback_handler( exception: Exception, advanced_file_regression: AdvancedFileRegressionFixture, cli_runner: CliRunner, ): @click.command() def demo(): # noqa: MAN002 with handle_tracebacks(False, ConfigTracebackHandler): raise exception result: Result = cli_runner.invoke(demo, catch_exceptions=False) result.check_stdout(advanced_file_regression) assert result.exit_code == 1 @exceptions def test_traceback_handler_show_traceback(exception: Exception, cli_runner: CliRunner): @click.command() def demo(): # noqa: MAN002 with handle_tracebacks(True, ConfigTracebackHandler): raise exception with pytest.raises(type(exception), match=re.escape(str(exception))): cli_runner.invoke(demo, catch_exceptions=False) @pytest.mark.parametrize("exception", [EOFError(), KeyboardInterrupt(), click.Abort()]) def test_handle_tracebacks_ignored_exceptions_click( exception: Exception, cli_runner: CliRunner, ): @click.command() def demo(): # noqa: MAN002 with handle_tracebacks(False, ConfigTracebackHandler): raise exception result: Result = cli_runner.invoke(demo, catch_exceptions=False) assert result.stdout.strip() == "Aborted!" assert result.exit_code == 1 @pytest.mark.parametrize("exception", [EOFError, KeyboardInterrupt, click.Abort, SystemExit]) def test_handle_tracebacks_ignored_exceptions(exception: Type[Exception]): with pytest.raises(exception): # noqa: PT012 with handle_tracebacks(False, ConfigTracebackHandler): raise exception @pytest.mark.parametrize( "path", [ pytest.param(None, id="all"), "build-system", "build-system.requires", pytest.param("build-system.requires.[0]", id="first_build_requirement"), "project", "project.authors", pytest.param("project.authors.[0]", id="first_author"), pytest.param("project.keywords.[3]", id="fourth_keyword"), "project.urls.Source Code", # Written as `python3 -m pyproject_parser info project.urls."Source Code"` "tool.whey.base-classifiers" ] ) @pytest.mark.parametrize("indent", [None, 0, 2, 4]) def test_info( path: str, tmp_pathplus: PathPlus, cli_runner: CliRunner, advanced_data_regression: AdvancedDataRegressionFixture, advanced_file_regression: AdvancedFileRegressionFixture, indent: Optional[int], ): (tmp_pathplus / "pyproject.toml").write_clean(COMPLETE_A) if path is None: args = [] else: args = [path] if indent: args.append("--indent") args.append(str(indent)) with in_directory(tmp_pathplus): result: Result = cli_runner.invoke(info, catch_exceptions=False, args=args) print(result.stdout) assert result.exit_code == 0 output = json.loads(result.stdout) if isinstance(output, str): advanced_file_regression.check(output, extension=".md") else: advanced_data_regression.check(output) advanced_file_regression.check(result.stdout, extension=".json") if path is None: args = [] else: args = [path] if indent: args.append("-i") args.append(str(indent)) with in_directory(tmp_pathplus): result = cli_runner.invoke(info, catch_exceptions=False, args=args) print(result.stdout) assert result.exit_code == 0 output = json.loads(result.stdout) if isinstance(output, str): advanced_file_regression.check(output, extension=".md") else: advanced_data_regression.check(output) advanced_file_regression.check(result.stdout, extension=".json") @pytest.mark.parametrize( "path", [ "project.readme", "project.readme.file", "project.readme.text", "project.license", "project.license.file", "project.license.text", ] ) @pytest.mark.parametrize("check_readme", [0, 1]) @pytest.mark.parametrize("indent", [None, 0, 2, 4]) @pytest.mark.parametrize("resolve", [True, False]) def test_info_readme_license( path: str, check_readme: int, tmp_pathplus: PathPlus, cli_runner: CliRunner, advanced_data_regression: AdvancedDataRegressionFixture, advanced_file_regression: AdvancedFileRegressionFixture, monkeypatch, resolve: bool, indent: Optional[int], ): monkeypatch.setenv("CHECK_README", str(check_readme)) (tmp_pathplus / "pyproject.toml").write_clean(COMPLETE_A_WITH_FILES) (tmp_pathplus / "README.rst").write_clean("This is the README") (tmp_pathplus / "LICENSE").write_clean("This is the LICENSE") args = [path] if resolve: args.append("--resolve") elif indent: args.append("--indent") args.append(str(indent)) with in_directory(tmp_pathplus): result: Result = cli_runner.invoke(info, catch_exceptions=False, args=args) print(result.stdout) assert result.exit_code == 0 output = json.loads(result.stdout) if isinstance(output, str): advanced_file_regression.check(output, extension=".md") else: advanced_data_regression.check(output) advanced_file_regression.check(result.stdout, extension=".json") args = [path, "-f", (tmp_pathplus / "pyproject.toml").as_posix()] if resolve: args.append("-r") elif indent: args.append("-i") args.append(str(indent)) result = cli_runner.invoke(info, catch_exceptions=False, args=args) print(result.stdout) assert result.exit_code == 0 output = json.loads(result.stdout) if isinstance(output, str): advanced_file_regression.check(output, extension=".md") else: advanced_data_regression.check(output) advanced_file_regression.check(result.stdout, extension=".json") pyproject-parser-0.9.1/tests/test_cli_/000077500000000000000000000000001444752763000201555ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_cli_/test_check_error_caught_author_comma_.txt000066400000000000000000000003461444752763000304760ustar00rootroot00000000000000BadConfigError: The 'project.authors[0].name' key cannot contain commas. Documentation: https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.authors Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_check_error_caught_bad_name_.txt000066400000000000000000000003621444752763000275440ustar00rootroot00000000000000BadConfigError: The value '???????12345=============β˜ƒ' for 'project.name' is invalid. Documentation: https://whey.readthedocs.io/en/latest/configuration.html#tconf-project.name Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_check_error_caught_bad_requires_python_.txt000066400000000000000000000003121444752763000320570ustar00rootroot00000000000000InvalidSpecifier: '???????12345=============β˜ƒ' Note: specifiers must follow PEP 508 Documentation: https://peps.python.org/pep-0508/ Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_check_error_caught_bad_version_.txt000066400000000000000000000003061444752763000303070ustar00rootroot00000000000000InvalidVersion: '???????12345=============β˜ƒ' Note: versions must follow PEP 440 Documentation: https://peps.python.org/pep-0440/ Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_check_error_caught_build_system_.txt000066400000000000000000000001621444752763000305170ustar00rootroot00000000000000BadConfigError: Unknown key in '[build-system]': 'foo' Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_check_error_caught_project_.txt000066400000000000000000000001701444752763000274610ustar00rootroot00000000000000BadConfigError: Unknown keys in '[project]': 'bar' and 'foo' Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_check_error_caught_top_level_.txt000066400000000000000000000002471444752763000300110ustar00rootroot00000000000000BadConfigError: Unexpected top-level key 'coverage'. Only 'build-system', 'project' and 'tool' are allowed. Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_check_error_caught_top_level_typo_caps_.txt000066400000000000000000000002211444752763000320620ustar00rootroot00000000000000BadConfigError: Unexpected top-level key 'Build-System'. Did you mean 'build-system'? Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_check_error_caught_top_level_typo_underscore_.txt000066400000000000000000000002211444752763000333050ustar00rootroot00000000000000BadConfigError: Unexpected top-level key 'build_system'. Did you mean 'build-system'? Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_check_extra_deprecation_duplicate_extra_1_.txt000066400000000000000000000002171444752763000324260ustar00rootroot00000000000000An error occurred: 'project.optional-dependencies.dev-test': Multiple extras were defined with the same normalized name of 'dev-test' Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_check_extra_deprecation_duplicate_extra_2_.txt000066400000000000000000000002171444752763000324270ustar00rootroot00000000000000An error occurred: 'project.optional-dependencies.dev_test': Multiple extras were defined with the same normalized name of 'dev-test' Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_check_extra_deprecation_duplicate_extra_3_.txt000066400000000000000000000002171444752763000324300ustar00rootroot00000000000000An error occurred: 'project.optional-dependencies.dev_test': Multiple extras were defined with the same normalized name of 'dev-test' Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_check_extra_deprecation_warning_duplicate_extra_1_.txt000066400000000000000000000002301444752763000341460ustar00rootroot00000000000000Validating 'pyproject.toml' WARNING: 'project.optional-dependencies.dev-test': Multiple extras were defined with the same normalized name of 'dev-test' pyproject-parser-0.9.1/tests/test_cli_/test_check_extra_deprecation_warning_duplicate_extra_2_.txt000066400000000000000000000002301444752763000341470ustar00rootroot00000000000000Validating 'pyproject.toml' WARNING: 'project.optional-dependencies.dev_test': Multiple extras were defined with the same normalized name of 'dev-test' pyproject-parser-0.9.1/tests/test_cli_/test_check_extra_deprecation_warning_duplicate_extra_3_.txt000066400000000000000000000002301444752763000341500ustar00rootroot00000000000000Validating 'pyproject.toml' WARNING: 'project.optional-dependencies.dev_test': Multiple extras were defined with the same normalized name of 'dev-test' pyproject-parser-0.9.1/tests/test_cli_/test_info_0_all_.json000066400000000000000000000017651444752763000242610ustar00rootroot00000000000000{"build-system": {"requires": ["whey"], "build-backend": "whey"}, "project": {"name": "whey", "version": "2021.0.0", "description": "A simple Python wheel builder for simple projects.", "authors": [{"name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk"}], "keywords": ["build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel"], "urls": {"Homepage": "https://whey.readthedocs.io/en/latest", "Documentation": "https://whey.readthedocs.io/en/latest", "Issue Tracker": "https://github.com/repo-helper/whey/issues", "Source Code": "https://github.com/repo-helper/whey"}, "dependencies": ["django>2.1; os_name != \"nt\"", "django>2.0; os_name == \"nt\"", "gidgethub[httpx]>4.0.0", "httpx"], "dynamic": ["classifiers", "requires-python"]}, "tool": {"whey": {"base-classifiers": ["Development Status :: 4 - Beta"], "python-versions": ["3.6", "3.7", "3.8", "3.9", "3.10"], "python-implementations": ["CPython", "PyPy"], "platforms": ["Windows", "macOS", "Linux"], "license-key": "MIT"}}} pyproject-parser-0.9.1/tests/test_cli_/test_info_0_all_.yml000066400000000000000000000017741444752763000241110ustar00rootroot00000000000000build-system: build-backend: whey requires: - whey project: authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 tool: whey: base-classifiers: - 'Development Status :: 4 - Beta' license-key: MIT platforms: - Windows - macOS - Linux python-implementations: - CPython - PyPy python-versions: - '3.6' - '3.7' - '3.8' - '3.9' - '3.10' pyproject-parser-0.9.1/tests/test_cli_/test_info_0_build_system_.json000066400000000000000000000000601444752763000261770ustar00rootroot00000000000000{"requires": ["whey"], "build-backend": "whey"} pyproject-parser-0.9.1/tests/test_cli_/test_info_0_build_system_.yml000066400000000000000000000000451444752763000260320ustar00rootroot00000000000000build-backend: whey requires: - whey pyproject-parser-0.9.1/tests/test_cli_/test_info_0_build_system_requires_.json000066400000000000000000000000111444752763000301120ustar00rootroot00000000000000["whey"] pyproject-parser-0.9.1/tests/test_cli_/test_info_0_build_system_requires_.yml000066400000000000000000000000071444752763000277470ustar00rootroot00000000000000- whey pyproject-parser-0.9.1/tests/test_cli_/test_info_0_first_author_.json000066400000000000000000000001101444752763000262010ustar00rootroot00000000000000{"name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk"} pyproject-parser-0.9.1/tests/test_cli_/test_info_0_first_author_.yml000066400000000000000000000000751444752763000260430ustar00rootroot00000000000000email: dominic@davis-foster.co.uk name: Dominic Davis-Foster pyproject-parser-0.9.1/tests/test_cli_/test_info_0_first_build_requirement_.md000066400000000000000000000000051444752763000300500ustar00rootroot00000000000000whey pyproject-parser-0.9.1/tests/test_cli_/test_info_0_fourth_keyword_.md000066400000000000000000000000071444752763000261770ustar00rootroot00000000000000pep517 pyproject-parser-0.9.1/tests/test_cli_/test_info_0_project_.json000066400000000000000000000012611444752763000251460ustar00rootroot00000000000000{"name": "whey", "version": "2021.0.0", "description": "A simple Python wheel builder for simple projects.", "authors": [{"name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk"}], "keywords": ["build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel"], "urls": {"Homepage": "https://whey.readthedocs.io/en/latest", "Documentation": "https://whey.readthedocs.io/en/latest", "Issue Tracker": "https://github.com/repo-helper/whey/issues", "Source Code": "https://github.com/repo-helper/whey"}, "dependencies": ["django>2.1; os_name != \"nt\"", "django>2.0; os_name == \"nt\"", "gidgethub[httpx]>4.0.0", "httpx"], "dynamic": ["classifiers", "requires-python"]} pyproject-parser-0.9.1/tests/test_cli_/test_info_0_project_.yml000066400000000000000000000011501444752763000247730ustar00rootroot00000000000000authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 pyproject-parser-0.9.1/tests/test_cli_/test_info_0_project_authors_.json000066400000000000000000000001121444752763000267050ustar00rootroot00000000000000[{"name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk"}] pyproject-parser-0.9.1/tests/test_cli_/test_info_0_project_authors_.yml000066400000000000000000000001011444752763000265330ustar00rootroot00000000000000- email: dominic@davis-foster.co.uk name: Dominic Davis-Foster pyproject-parser-0.9.1/tests/test_cli_/test_info_0_project_urls_Source_Code_.md000066400000000000000000000000441444752763000301120ustar00rootroot00000000000000https://github.com/repo-helper/whey pyproject-parser-0.9.1/tests/test_cli_/test_info_0_tool_whey_base_classifiers_.json000066400000000000000000000000431444752763000310670ustar00rootroot00000000000000["Development Status :: 4 - Beta"] pyproject-parser-0.9.1/tests/test_cli_/test_info_0_tool_whey_base_classifiers_.yml000066400000000000000000000000431444752763000307170ustar00rootroot00000000000000- 'Development Status :: 4 - Beta' pyproject-parser-0.9.1/tests/test_cli_/test_info_2_all_.json000066400000000000000000000025771444752763000242650ustar00rootroot00000000000000{ "build-system": { "requires": [ "whey" ], "build-backend": "whey" }, "project": { "name": "whey", "version": "2021.0.0", "description": "A simple Python wheel builder for simple projects.", "authors": [ { "name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk" } ], "keywords": [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel" ], "urls": { "Homepage": "https://whey.readthedocs.io/en/latest", "Documentation": "https://whey.readthedocs.io/en/latest", "Issue Tracker": "https://github.com/repo-helper/whey/issues", "Source Code": "https://github.com/repo-helper/whey" }, "dependencies": [ "django>2.1; os_name != \"nt\"", "django>2.0; os_name == \"nt\"", "gidgethub[httpx]>4.0.0", "httpx" ], "dynamic": [ "classifiers", "requires-python" ] }, "tool": { "whey": { "base-classifiers": [ "Development Status :: 4 - Beta" ], "python-versions": [ "3.6", "3.7", "3.8", "3.9", "3.10" ], "python-implementations": [ "CPython", "PyPy" ], "platforms": [ "Windows", "macOS", "Linux" ], "license-key": "MIT" } } } pyproject-parser-0.9.1/tests/test_cli_/test_info_2_all_.yml000066400000000000000000000017741444752763000241130ustar00rootroot00000000000000build-system: build-backend: whey requires: - whey project: authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 tool: whey: base-classifiers: - 'Development Status :: 4 - Beta' license-key: MIT platforms: - Windows - macOS - Linux python-implementations: - CPython - PyPy python-versions: - '3.6' - '3.7' - '3.8' - '3.9' - '3.10' pyproject-parser-0.9.1/tests/test_cli_/test_info_2_build_system_.json000066400000000000000000000000761444752763000262100ustar00rootroot00000000000000{ "requires": [ "whey" ], "build-backend": "whey" } pyproject-parser-0.9.1/tests/test_cli_/test_info_2_build_system_.yml000066400000000000000000000000451444752763000260340ustar00rootroot00000000000000build-backend: whey requires: - whey pyproject-parser-0.9.1/tests/test_cli_/test_info_2_build_system_requires_.json000066400000000000000000000000151444752763000301200ustar00rootroot00000000000000[ "whey" ] pyproject-parser-0.9.1/tests/test_cli_/test_info_2_build_system_requires_.yml000066400000000000000000000000071444752763000277510ustar00rootroot00000000000000- whey pyproject-parser-0.9.1/tests/test_cli_/test_info_2_first_author_.json000066400000000000000000000001161444752763000262110ustar00rootroot00000000000000{ "name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk" } pyproject-parser-0.9.1/tests/test_cli_/test_info_2_first_author_.yml000066400000000000000000000000751444752763000260450ustar00rootroot00000000000000email: dominic@davis-foster.co.uk name: Dominic Davis-Foster pyproject-parser-0.9.1/tests/test_cli_/test_info_2_first_build_requirement_.md000066400000000000000000000000051444752763000300520ustar00rootroot00000000000000whey pyproject-parser-0.9.1/tests/test_cli_/test_info_2_fourth_keyword_.md000066400000000000000000000000071444752763000262010ustar00rootroot00000000000000pep517 pyproject-parser-0.9.1/tests/test_cli_/test_info_2_project_.json000066400000000000000000000014611444752763000251520ustar00rootroot00000000000000{ "name": "whey", "version": "2021.0.0", "description": "A simple Python wheel builder for simple projects.", "authors": [ { "name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk" } ], "keywords": [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel" ], "urls": { "Homepage": "https://whey.readthedocs.io/en/latest", "Documentation": "https://whey.readthedocs.io/en/latest", "Issue Tracker": "https://github.com/repo-helper/whey/issues", "Source Code": "https://github.com/repo-helper/whey" }, "dependencies": [ "django>2.1; os_name != \"nt\"", "django>2.0; os_name == \"nt\"", "gidgethub[httpx]>4.0.0", "httpx" ], "dynamic": [ "classifiers", "requires-python" ] } pyproject-parser-0.9.1/tests/test_cli_/test_info_2_project_.yml000066400000000000000000000011501444752763000247750ustar00rootroot00000000000000authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 pyproject-parser-0.9.1/tests/test_cli_/test_info_2_project_authors_.json000066400000000000000000000001321444752763000267110ustar00rootroot00000000000000[ { "name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk" } ] pyproject-parser-0.9.1/tests/test_cli_/test_info_2_project_authors_.yml000066400000000000000000000001011444752763000265350ustar00rootroot00000000000000- email: dominic@davis-foster.co.uk name: Dominic Davis-Foster pyproject-parser-0.9.1/tests/test_cli_/test_info_2_project_urls_Source_Code_.md000066400000000000000000000000441444752763000301140ustar00rootroot00000000000000https://github.com/repo-helper/whey pyproject-parser-0.9.1/tests/test_cli_/test_info_2_tool_whey_base_classifiers_.json000066400000000000000000000000471444752763000310750ustar00rootroot00000000000000[ "Development Status :: 4 - Beta" ] pyproject-parser-0.9.1/tests/test_cli_/test_info_2_tool_whey_base_classifiers_.yml000066400000000000000000000000431444752763000307210ustar00rootroot00000000000000- 'Development Status :: 4 - Beta' pyproject-parser-0.9.1/tests/test_cli_/test_info_4_all_.json000066400000000000000000000033511444752763000242560ustar00rootroot00000000000000{ "build-system": { "requires": [ "whey" ], "build-backend": "whey" }, "project": { "name": "whey", "version": "2021.0.0", "description": "A simple Python wheel builder for simple projects.", "authors": [ { "name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk" } ], "keywords": [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel" ], "urls": { "Homepage": "https://whey.readthedocs.io/en/latest", "Documentation": "https://whey.readthedocs.io/en/latest", "Issue Tracker": "https://github.com/repo-helper/whey/issues", "Source Code": "https://github.com/repo-helper/whey" }, "dependencies": [ "django>2.1; os_name != \"nt\"", "django>2.0; os_name == \"nt\"", "gidgethub[httpx]>4.0.0", "httpx" ], "dynamic": [ "classifiers", "requires-python" ] }, "tool": { "whey": { "base-classifiers": [ "Development Status :: 4 - Beta" ], "python-versions": [ "3.6", "3.7", "3.8", "3.9", "3.10" ], "python-implementations": [ "CPython", "PyPy" ], "platforms": [ "Windows", "macOS", "Linux" ], "license-key": "MIT" } } } pyproject-parser-0.9.1/tests/test_cli_/test_info_4_all_.yml000066400000000000000000000017741444752763000241150ustar00rootroot00000000000000build-system: build-backend: whey requires: - whey project: authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 tool: whey: base-classifiers: - 'Development Status :: 4 - Beta' license-key: MIT platforms: - Windows - macOS - Linux python-implementations: - CPython - PyPy python-versions: - '3.6' - '3.7' - '3.8' - '3.9' - '3.10' pyproject-parser-0.9.1/tests/test_cli_/test_info_4_build_system_.json000066400000000000000000000001101444752763000261770ustar00rootroot00000000000000{ "requires": [ "whey" ], "build-backend": "whey" } pyproject-parser-0.9.1/tests/test_cli_/test_info_4_build_system_.yml000066400000000000000000000000451444752763000260360ustar00rootroot00000000000000build-backend: whey requires: - whey pyproject-parser-0.9.1/tests/test_cli_/test_info_4_build_system_requires_.json000066400000000000000000000000171444752763000301240ustar00rootroot00000000000000[ "whey" ] pyproject-parser-0.9.1/tests/test_cli_/test_info_4_build_system_requires_.yml000066400000000000000000000000071444752763000277530ustar00rootroot00000000000000- whey pyproject-parser-0.9.1/tests/test_cli_/test_info_4_first_author_.json000066400000000000000000000001221444752763000262100ustar00rootroot00000000000000{ "name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk" } pyproject-parser-0.9.1/tests/test_cli_/test_info_4_first_author_.yml000066400000000000000000000000751444752763000260470ustar00rootroot00000000000000email: dominic@davis-foster.co.uk name: Dominic Davis-Foster pyproject-parser-0.9.1/tests/test_cli_/test_info_4_first_build_requirement_.md000066400000000000000000000000051444752763000300540ustar00rootroot00000000000000whey pyproject-parser-0.9.1/tests/test_cli_/test_info_4_fourth_keyword_.md000066400000000000000000000000071444752763000262030ustar00rootroot00000000000000pep517 pyproject-parser-0.9.1/tests/test_cli_/test_info_4_project_.json000066400000000000000000000016431444752763000251560ustar00rootroot00000000000000{ "name": "whey", "version": "2021.0.0", "description": "A simple Python wheel builder for simple projects.", "authors": [ { "name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk" } ], "keywords": [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel" ], "urls": { "Homepage": "https://whey.readthedocs.io/en/latest", "Documentation": "https://whey.readthedocs.io/en/latest", "Issue Tracker": "https://github.com/repo-helper/whey/issues", "Source Code": "https://github.com/repo-helper/whey" }, "dependencies": [ "django>2.1; os_name != \"nt\"", "django>2.0; os_name == \"nt\"", "gidgethub[httpx]>4.0.0", "httpx" ], "dynamic": [ "classifiers", "requires-python" ] } pyproject-parser-0.9.1/tests/test_cli_/test_info_4_project_.yml000066400000000000000000000011501444752763000247770ustar00rootroot00000000000000authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 pyproject-parser-0.9.1/tests/test_cli_/test_info_4_project_authors_.json000066400000000000000000000001461444752763000267200ustar00rootroot00000000000000[ { "name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk" } ] pyproject-parser-0.9.1/tests/test_cli_/test_info_4_project_authors_.yml000066400000000000000000000001011444752763000265370ustar00rootroot00000000000000- email: dominic@davis-foster.co.uk name: Dominic Davis-Foster pyproject-parser-0.9.1/tests/test_cli_/test_info_4_project_urls_Source_Code_.md000066400000000000000000000000441444752763000301160ustar00rootroot00000000000000https://github.com/repo-helper/whey pyproject-parser-0.9.1/tests/test_cli_/test_info_4_tool_whey_base_classifiers_.json000066400000000000000000000000511444752763000310720ustar00rootroot00000000000000[ "Development Status :: 4 - Beta" ] pyproject-parser-0.9.1/tests/test_cli_/test_info_4_tool_whey_base_classifiers_.yml000066400000000000000000000000431444752763000307230ustar00rootroot00000000000000- 'Development Status :: 4 - Beta' pyproject-parser-0.9.1/tests/test_cli_/test_info_None_all_.json000066400000000000000000000017651444752763000250210ustar00rootroot00000000000000{"build-system": {"requires": ["whey"], "build-backend": "whey"}, "project": {"name": "whey", "version": "2021.0.0", "description": "A simple Python wheel builder for simple projects.", "authors": [{"name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk"}], "keywords": ["build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel"], "urls": {"Homepage": "https://whey.readthedocs.io/en/latest", "Documentation": "https://whey.readthedocs.io/en/latest", "Issue Tracker": "https://github.com/repo-helper/whey/issues", "Source Code": "https://github.com/repo-helper/whey"}, "dependencies": ["django>2.1; os_name != \"nt\"", "django>2.0; os_name == \"nt\"", "gidgethub[httpx]>4.0.0", "httpx"], "dynamic": ["classifiers", "requires-python"]}, "tool": {"whey": {"base-classifiers": ["Development Status :: 4 - Beta"], "python-versions": ["3.6", "3.7", "3.8", "3.9", "3.10"], "python-implementations": ["CPython", "PyPy"], "platforms": ["Windows", "macOS", "Linux"], "license-key": "MIT"}}} pyproject-parser-0.9.1/tests/test_cli_/test_info_None_all_.yml000066400000000000000000000017741444752763000246510ustar00rootroot00000000000000build-system: build-backend: whey requires: - whey project: authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 tool: whey: base-classifiers: - 'Development Status :: 4 - Beta' license-key: MIT platforms: - Windows - macOS - Linux python-implementations: - CPython - PyPy python-versions: - '3.6' - '3.7' - '3.8' - '3.9' - '3.10' pyproject-parser-0.9.1/tests/test_cli_/test_info_None_build_system_.json000066400000000000000000000000601444752763000267370ustar00rootroot00000000000000{"requires": ["whey"], "build-backend": "whey"} pyproject-parser-0.9.1/tests/test_cli_/test_info_None_build_system_.yml000066400000000000000000000000451444752763000265720ustar00rootroot00000000000000build-backend: whey requires: - whey pyproject-parser-0.9.1/tests/test_cli_/test_info_None_build_system_requires_.json000066400000000000000000000000111444752763000306520ustar00rootroot00000000000000["whey"] pyproject-parser-0.9.1/tests/test_cli_/test_info_None_build_system_requires_.yml000066400000000000000000000000071444752763000305070ustar00rootroot00000000000000- whey pyproject-parser-0.9.1/tests/test_cli_/test_info_None_first_author_.json000066400000000000000000000001101444752763000267410ustar00rootroot00000000000000{"name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk"} pyproject-parser-0.9.1/tests/test_cli_/test_info_None_first_author_.yml000066400000000000000000000000751444752763000266030ustar00rootroot00000000000000email: dominic@davis-foster.co.uk name: Dominic Davis-Foster pyproject-parser-0.9.1/tests/test_cli_/test_info_None_first_build_requirement_.md000066400000000000000000000000051444752763000306100ustar00rootroot00000000000000whey pyproject-parser-0.9.1/tests/test_cli_/test_info_None_fourth_keyword_.md000066400000000000000000000000071444752763000267370ustar00rootroot00000000000000pep517 pyproject-parser-0.9.1/tests/test_cli_/test_info_None_project_.json000066400000000000000000000012611444752763000257060ustar00rootroot00000000000000{"name": "whey", "version": "2021.0.0", "description": "A simple Python wheel builder for simple projects.", "authors": [{"name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk"}], "keywords": ["build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel"], "urls": {"Homepage": "https://whey.readthedocs.io/en/latest", "Documentation": "https://whey.readthedocs.io/en/latest", "Issue Tracker": "https://github.com/repo-helper/whey/issues", "Source Code": "https://github.com/repo-helper/whey"}, "dependencies": ["django>2.1; os_name != \"nt\"", "django>2.0; os_name == \"nt\"", "gidgethub[httpx]>4.0.0", "httpx"], "dynamic": ["classifiers", "requires-python"]} pyproject-parser-0.9.1/tests/test_cli_/test_info_None_project_.yml000066400000000000000000000011501444752763000255330ustar00rootroot00000000000000authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 pyproject-parser-0.9.1/tests/test_cli_/test_info_None_project_authors_.json000066400000000000000000000001121444752763000274450ustar00rootroot00000000000000[{"name": "Dominic Davis-Foster", "email": "dominic@davis-foster.co.uk"}] pyproject-parser-0.9.1/tests/test_cli_/test_info_None_project_authors_.yml000066400000000000000000000001011444752763000272730ustar00rootroot00000000000000- email: dominic@davis-foster.co.uk name: Dominic Davis-Foster pyproject-parser-0.9.1/tests/test_cli_/test_info_None_project_urls_Source_Code_.md000066400000000000000000000000441444752763000306520ustar00rootroot00000000000000https://github.com/repo-helper/whey pyproject-parser-0.9.1/tests/test_cli_/test_info_None_tool_whey_base_classifiers_.json000066400000000000000000000000431444752763000316270ustar00rootroot00000000000000["Development Status :: 4 - Beta"] pyproject-parser-0.9.1/tests/test_cli_/test_info_None_tool_whey_base_classifiers_.yml000066400000000000000000000000431444752763000314570ustar00rootroot00000000000000- 'Development Status :: 4 - Beta' pyproject-parser-0.9.1/tests/test_cli_/test_info_all_.yml000066400000000000000000000017741444752763000236720ustar00rootroot00000000000000build-system: build-backend: whey requires: - whey project: authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 tool: whey: base-classifiers: - 'Development Status :: 4 - Beta' license-key: MIT platforms: - Windows - macOS - Linux python-implementations: - CPython - PyPy python-versions: - '3.6' - '3.7' - '3.8' - '3.9' - '3.10' pyproject-parser-0.9.1/tests/test_cli_/test_info_build_system_.yml000066400000000000000000000000451444752763000256130ustar00rootroot00000000000000build-backend: whey requires: - whey pyproject-parser-0.9.1/tests/test_cli_/test_info_build_system_requires_.yml000066400000000000000000000000071444752763000275300ustar00rootroot00000000000000- whey pyproject-parser-0.9.1/tests/test_cli_/test_info_first_author_.yml000066400000000000000000000000751444752763000256240ustar00rootroot00000000000000email: dominic@davis-foster.co.uk name: Dominic Davis-Foster pyproject-parser-0.9.1/tests/test_cli_/test_info_first_build_requirement_.md000066400000000000000000000000051444752763000276310ustar00rootroot00000000000000whey pyproject-parser-0.9.1/tests/test_cli_/test_info_fourth_keyword_.md000066400000000000000000000000071444752763000257600ustar00rootroot00000000000000pep517 pyproject-parser-0.9.1/tests/test_cli_/test_info_project_.yml000066400000000000000000000011501444752763000245540ustar00rootroot00000000000000authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 pyproject-parser-0.9.1/tests/test_cli_/test_info_project_authors_.yml000066400000000000000000000001011444752763000263140ustar00rootroot00000000000000- email: dominic@davis-foster.co.uk name: Dominic Davis-Foster pyproject-parser-0.9.1/tests/test_cli_/test_info_project_urls_Source_Code_.md000066400000000000000000000000441444752763000276730ustar00rootroot00000000000000https://github.com/repo-helper/whey pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_0_project_license_.json000066400000000000000000000000241444752763000332140ustar00rootroot00000000000000{"file": "LICENSE"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_0_project_license_.yml000066400000000000000000000000161444752763000330450ustar00rootroot00000000000000file: LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_0_project_license_file_.md000066400000000000000000000000101444752763000336350ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_0_project_license_text_.json000066400000000000000000000000051444752763000342570ustar00rootroot00000000000000null pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_0_project_license_text_.yml000066400000000000000000000000111444752763000341040ustar00rootroot00000000000000null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_0_project_readme_.json000066400000000000000000000000651444752763000330340ustar00rootroot00000000000000{"content_type": "text/x-rst", "file": "README.rst"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_0_project_readme_.yml000066400000000000000000000000521444752763000326600ustar00rootroot00000000000000content_type: text/x-rst file: README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_0_project_readme_file_.md000066400000000000000000000000131444752763000334530ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_0_project_readme_text_.json000066400000000000000000000000051444752763000340720ustar00rootroot00000000000000null pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_0_project_readme_text_.yml000066400000000000000000000000111444752763000337170ustar00rootroot00000000000000null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_1_project_license_.json000066400000000000000000000000241444752763000332150ustar00rootroot00000000000000{"file": "LICENSE"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_1_project_license_.yml000066400000000000000000000000161444752763000330460ustar00rootroot00000000000000file: LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_1_project_license_file_.md000066400000000000000000000000101444752763000336360ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_1_project_license_text_.json000066400000000000000000000000051444752763000342600ustar00rootroot00000000000000null pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_1_project_license_text_.yml000066400000000000000000000000111444752763000341050ustar00rootroot00000000000000null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_1_project_readme_.json000066400000000000000000000000651444752763000330350ustar00rootroot00000000000000{"content_type": "text/x-rst", "file": "README.rst"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_1_project_readme_.yml000066400000000000000000000000521444752763000326610ustar00rootroot00000000000000content_type: text/x-rst file: README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_1_project_readme_file_.md000066400000000000000000000000131444752763000334540ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_1_project_readme_text_.json000066400000000000000000000000051444752763000340730ustar00rootroot00000000000000null pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_0_1_project_readme_text_.yml000066400000000000000000000000111444752763000337200ustar00rootroot00000000000000null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_0_project_license_.json000066400000000000000000000000301444752763000332130ustar00rootroot00000000000000{ "file": "LICENSE" } pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_0_project_license_.yml000066400000000000000000000000161444752763000330470ustar00rootroot00000000000000file: LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_0_project_license_file_.md000066400000000000000000000000101444752763000336370ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_0_project_license_text_.json000066400000000000000000000000051444752763000342610ustar00rootroot00000000000000null pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_0_project_license_text_.yml000066400000000000000000000000111444752763000341060ustar00rootroot00000000000000null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_0_project_readme_.json000066400000000000000000000000731444752763000330350ustar00rootroot00000000000000{ "content_type": "text/x-rst", "file": "README.rst" } pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_0_project_readme_.yml000066400000000000000000000000521444752763000326620ustar00rootroot00000000000000content_type: text/x-rst file: README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_0_project_readme_file_.md000066400000000000000000000000131444752763000334550ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_0_project_readme_text_.json000066400000000000000000000000051444752763000340740ustar00rootroot00000000000000null pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_0_project_readme_text_.yml000066400000000000000000000000111444752763000337210ustar00rootroot00000000000000null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_1_project_license_.json000066400000000000000000000000301444752763000332140ustar00rootroot00000000000000{ "file": "LICENSE" } pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_1_project_license_.yml000066400000000000000000000000161444752763000330500ustar00rootroot00000000000000file: LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_1_project_license_file_.md000066400000000000000000000000101444752763000336400ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_1_project_license_text_.json000066400000000000000000000000051444752763000342620ustar00rootroot00000000000000null pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_1_project_license_text_.yml000066400000000000000000000000111444752763000341070ustar00rootroot00000000000000null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_1_project_readme_.json000066400000000000000000000000731444752763000330360ustar00rootroot00000000000000{ "content_type": "text/x-rst", "file": "README.rst" } pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_1_project_readme_.yml000066400000000000000000000000521444752763000326630ustar00rootroot00000000000000content_type: text/x-rst file: README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_1_project_readme_file_.md000066400000000000000000000000131444752763000334560ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_1_project_readme_text_.json000066400000000000000000000000051444752763000340750ustar00rootroot00000000000000null pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_2_1_project_readme_text_.yml000066400000000000000000000000111444752763000337220ustar00rootroot00000000000000null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_0_project_license_.json000066400000000000000000000000321444752763000332170ustar00rootroot00000000000000{ "file": "LICENSE" } pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_0_project_license_.yml000066400000000000000000000000161444752763000330510ustar00rootroot00000000000000file: LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_0_project_license_file_.md000066400000000000000000000000101444752763000336410ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_0_project_license_text_.json000066400000000000000000000000051444752763000342630ustar00rootroot00000000000000null pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_0_project_license_text_.yml000066400000000000000000000000111444752763000341100ustar00rootroot00000000000000null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_0_project_readme_.json000066400000000000000000000000771444752763000330430ustar00rootroot00000000000000{ "content_type": "text/x-rst", "file": "README.rst" } pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_0_project_readme_.yml000066400000000000000000000000521444752763000326640ustar00rootroot00000000000000content_type: text/x-rst file: README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_0_project_readme_file_.md000066400000000000000000000000131444752763000334570ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_0_project_readme_text_.json000066400000000000000000000000051444752763000340760ustar00rootroot00000000000000null pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_0_project_readme_text_.yml000066400000000000000000000000111444752763000337230ustar00rootroot00000000000000null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_1_project_license_.json000066400000000000000000000000321444752763000332200ustar00rootroot00000000000000{ "file": "LICENSE" } pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_1_project_license_.yml000066400000000000000000000000161444752763000330520ustar00rootroot00000000000000file: LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_1_project_license_file_.md000066400000000000000000000000101444752763000336420ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_1_project_license_text_.json000066400000000000000000000000051444752763000342640ustar00rootroot00000000000000null pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_1_project_license_text_.yml000066400000000000000000000000111444752763000341110ustar00rootroot00000000000000null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_1_project_readme_.json000066400000000000000000000000771444752763000330440ustar00rootroot00000000000000{ "content_type": "text/x-rst", "file": "README.rst" } pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_1_project_readme_.yml000066400000000000000000000000521444752763000326650ustar00rootroot00000000000000content_type: text/x-rst file: README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_1_project_readme_file_.md000066400000000000000000000000131444752763000334600ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_1_project_readme_text_.json000066400000000000000000000000051444752763000340770ustar00rootroot00000000000000null pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_4_1_project_readme_text_.yml000066400000000000000000000000111444752763000337240ustar00rootroot00000000000000null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_None_0_project_license_.json000066400000000000000000000000241444752763000337540ustar00rootroot00000000000000{"file": "LICENSE"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_None_0_project_license_.yml000066400000000000000000000000161444752763000336050ustar00rootroot00000000000000file: LICENSE test_info_readme_license_False_None_0_project_license_file_.md000066400000000000000000000000101444752763000343160ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_cli_LICENSE test_info_readme_license_False_None_0_project_license_text_.json000066400000000000000000000000051444752763000347400ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_cli_null test_info_readme_license_False_None_0_project_license_text_.yml000066400000000000000000000000111444752763000345650ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_cli_null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_None_0_project_readme_.json000066400000000000000000000000651444752763000335740ustar00rootroot00000000000000{"content_type": "text/x-rst", "file": "README.rst"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_None_0_project_readme_.yml000066400000000000000000000000521444752763000334200ustar00rootroot00000000000000content_type: text/x-rst file: README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_None_0_project_readme_file_.md000066400000000000000000000000131444752763000342130ustar00rootroot00000000000000README.rst test_info_readme_license_False_None_0_project_readme_text_.json000066400000000000000000000000051444752763000345530ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_cli_null test_info_readme_license_False_None_0_project_readme_text_.yml000066400000000000000000000000111444752763000344000ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_cli_null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_None_1_project_license_.json000066400000000000000000000000241444752763000337550ustar00rootroot00000000000000{"file": "LICENSE"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_None_1_project_license_.yml000066400000000000000000000000161444752763000336060ustar00rootroot00000000000000file: LICENSE test_info_readme_license_False_None_1_project_license_file_.md000066400000000000000000000000101444752763000343170ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_cli_LICENSE test_info_readme_license_False_None_1_project_license_text_.json000066400000000000000000000000051444752763000347410ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_cli_null test_info_readme_license_False_None_1_project_license_text_.yml000066400000000000000000000000111444752763000345660ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_cli_null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_None_1_project_readme_.json000066400000000000000000000000651444752763000335750ustar00rootroot00000000000000{"content_type": "text/x-rst", "file": "README.rst"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_None_1_project_readme_.yml000066400000000000000000000000521444752763000334210ustar00rootroot00000000000000content_type: text/x-rst file: README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_False_None_1_project_readme_file_.md000066400000000000000000000000131444752763000342140ustar00rootroot00000000000000README.rst test_info_readme_license_False_None_1_project_readme_text_.json000066400000000000000000000000051444752763000345540ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_cli_null test_info_readme_license_False_None_1_project_readme_text_.yml000066400000000000000000000000111444752763000344010ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_cli_null ... pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_0_project_license_.json000066400000000000000000000000651444752763000331060ustar00rootroot00000000000000{"file": "LICENSE", "text": "This is the LICENSE\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_0_project_license_.yml000066400000000000000000000000561444752763000327360ustar00rootroot00000000000000file: LICENSE text: 'This is the LICENSE ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_0_project_license_file_.md000066400000000000000000000000101444752763000335220ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_0_project_license_text_.md000066400000000000000000000000241444752763000335740ustar00rootroot00000000000000This is the LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_0_project_readme_.json000066400000000000000000000001251444752763000327160ustar00rootroot00000000000000{"content_type": "text/x-rst", "file": "README.rst", "text": "This is the README\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_0_project_readme_.yml000066400000000000000000000001111444752763000325410ustar00rootroot00000000000000content_type: text/x-rst file: README.rst text: 'This is the README ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_0_project_readme_file_.md000066400000000000000000000000131444752763000333400ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_0_project_readme_text_.md000066400000000000000000000000231444752763000334060ustar00rootroot00000000000000This is the README pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_1_project_license_.json000066400000000000000000000000651444752763000331070ustar00rootroot00000000000000{"file": "LICENSE", "text": "This is the LICENSE\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_1_project_license_.yml000066400000000000000000000000561444752763000327370ustar00rootroot00000000000000file: LICENSE text: 'This is the LICENSE ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_1_project_license_file_.md000066400000000000000000000000101444752763000335230ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_1_project_license_text_.md000066400000000000000000000000241444752763000335750ustar00rootroot00000000000000This is the LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_1_project_readme_.json000066400000000000000000000001251444752763000327170ustar00rootroot00000000000000{"content_type": "text/x-rst", "file": "README.rst", "text": "This is the README\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_1_project_readme_.yml000066400000000000000000000001111444752763000325420ustar00rootroot00000000000000content_type: text/x-rst file: README.rst text: 'This is the README ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_1_project_readme_file_.md000066400000000000000000000000131444752763000333410ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_0_1_project_readme_text_.md000066400000000000000000000000231444752763000334070ustar00rootroot00000000000000This is the README pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_0_project_license_.json000066400000000000000000000000651444752763000331100ustar00rootroot00000000000000{"file": "LICENSE", "text": "This is the LICENSE\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_0_project_license_.yml000066400000000000000000000000561444752763000327400ustar00rootroot00000000000000file: LICENSE text: 'This is the LICENSE ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_0_project_license_file_.md000066400000000000000000000000101444752763000335240ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_0_project_license_text_.md000066400000000000000000000000241444752763000335760ustar00rootroot00000000000000This is the LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_0_project_readme_.json000066400000000000000000000001251444752763000327200ustar00rootroot00000000000000{"content_type": "text/x-rst", "file": "README.rst", "text": "This is the README\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_0_project_readme_.yml000066400000000000000000000001111444752763000325430ustar00rootroot00000000000000content_type: text/x-rst file: README.rst text: 'This is the README ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_0_project_readme_file_.md000066400000000000000000000000131444752763000333420ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_0_project_readme_text_.md000066400000000000000000000000231444752763000334100ustar00rootroot00000000000000This is the README pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_1_project_license_.json000066400000000000000000000000651444752763000331110ustar00rootroot00000000000000{"file": "LICENSE", "text": "This is the LICENSE\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_1_project_license_.yml000066400000000000000000000000561444752763000327410ustar00rootroot00000000000000file: LICENSE text: 'This is the LICENSE ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_1_project_license_file_.md000066400000000000000000000000101444752763000335250ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_1_project_license_text_.md000066400000000000000000000000241444752763000335770ustar00rootroot00000000000000This is the LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_1_project_readme_.json000066400000000000000000000001251444752763000327210ustar00rootroot00000000000000{"content_type": "text/x-rst", "file": "README.rst", "text": "This is the README\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_1_project_readme_.yml000066400000000000000000000001111444752763000325440ustar00rootroot00000000000000content_type: text/x-rst file: README.rst text: 'This is the README ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_1_project_readme_file_.md000066400000000000000000000000131444752763000333430ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_2_1_project_readme_text_.md000066400000000000000000000000231444752763000334110ustar00rootroot00000000000000This is the README pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_0_project_license_.json000066400000000000000000000000651444752763000331120ustar00rootroot00000000000000{"file": "LICENSE", "text": "This is the LICENSE\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_0_project_license_.yml000066400000000000000000000000561444752763000327420ustar00rootroot00000000000000file: LICENSE text: 'This is the LICENSE ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_0_project_license_file_.md000066400000000000000000000000101444752763000335260ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_0_project_license_text_.md000066400000000000000000000000241444752763000336000ustar00rootroot00000000000000This is the LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_0_project_readme_.json000066400000000000000000000001251444752763000327220ustar00rootroot00000000000000{"content_type": "text/x-rst", "file": "README.rst", "text": "This is the README\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_0_project_readme_.yml000066400000000000000000000001111444752763000325450ustar00rootroot00000000000000content_type: text/x-rst file: README.rst text: 'This is the README ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_0_project_readme_file_.md000066400000000000000000000000131444752763000333440ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_0_project_readme_text_.md000066400000000000000000000000231444752763000334120ustar00rootroot00000000000000This is the README pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_1_project_license_.json000066400000000000000000000000651444752763000331130ustar00rootroot00000000000000{"file": "LICENSE", "text": "This is the LICENSE\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_1_project_license_.yml000066400000000000000000000000561444752763000327430ustar00rootroot00000000000000file: LICENSE text: 'This is the LICENSE ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_1_project_license_file_.md000066400000000000000000000000101444752763000335270ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_1_project_license_text_.md000066400000000000000000000000241444752763000336010ustar00rootroot00000000000000This is the LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_1_project_readme_.json000066400000000000000000000001251444752763000327230ustar00rootroot00000000000000{"content_type": "text/x-rst", "file": "README.rst", "text": "This is the README\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_1_project_readme_.yml000066400000000000000000000001111444752763000325460ustar00rootroot00000000000000content_type: text/x-rst file: README.rst text: 'This is the README ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_1_project_readme_file_.md000066400000000000000000000000131444752763000333450ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_4_1_project_readme_text_.md000066400000000000000000000000231444752763000334130ustar00rootroot00000000000000This is the README pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_0_project_license_.json000066400000000000000000000000651444752763000336460ustar00rootroot00000000000000{"file": "LICENSE", "text": "This is the LICENSE\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_0_project_license_.yml000066400000000000000000000000561444752763000334760ustar00rootroot00000000000000file: LICENSE text: 'This is the LICENSE ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_0_project_license_file_.md000066400000000000000000000000101444752763000342620ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_0_project_license_text_.md000066400000000000000000000000241444752763000343340ustar00rootroot00000000000000This is the LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_0_project_readme_.json000066400000000000000000000001251444752763000334560ustar00rootroot00000000000000{"content_type": "text/x-rst", "file": "README.rst", "text": "This is the README\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_0_project_readme_.yml000066400000000000000000000001111444752763000333010ustar00rootroot00000000000000content_type: text/x-rst file: README.rst text: 'This is the README ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_0_project_readme_file_.md000066400000000000000000000000131444752763000341000ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_0_project_readme_text_.md000066400000000000000000000000231444752763000341460ustar00rootroot00000000000000This is the README pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_1_project_license_.json000066400000000000000000000000651444752763000336470ustar00rootroot00000000000000{"file": "LICENSE", "text": "This is the LICENSE\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_1_project_license_.yml000066400000000000000000000000561444752763000334770ustar00rootroot00000000000000file: LICENSE text: 'This is the LICENSE ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_1_project_license_file_.md000066400000000000000000000000101444752763000342630ustar00rootroot00000000000000LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_1_project_license_text_.md000066400000000000000000000000241444752763000343350ustar00rootroot00000000000000This is the LICENSE pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_1_project_readme_.json000066400000000000000000000001251444752763000334570ustar00rootroot00000000000000{"content_type": "text/x-rst", "file": "README.rst", "text": "This is the README\n"} pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_1_project_readme_.yml000066400000000000000000000001111444752763000333020ustar00rootroot00000000000000content_type: text/x-rst file: README.rst text: 'This is the README ' pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_1_project_readme_file_.md000066400000000000000000000000131444752763000341010ustar00rootroot00000000000000README.rst pyproject-parser-0.9.1/tests/test_cli_/test_info_readme_license_True_None_1_project_readme_text_.md000066400000000000000000000000231444752763000341470ustar00rootroot00000000000000This is the README pyproject-parser-0.9.1/tests/test_cli_/test_info_tool_whey_base_classifiers_.yml000066400000000000000000000000431444752763000305000ustar00rootroot00000000000000- 'Development Status :: 4 - Beta' pyproject-parser-0.9.1/tests/test_cli_/test_reformat_False_COMPLETE_A_.diff000066400000000000000000000000361444752763000266650ustar00rootroot00000000000000Reformatting 'pyproject.toml' pyproject-parser-0.9.1/tests/test_cli_/test_reformat_False_COMPLETE_A_.toml000066400000000000000000000017371444752763000267410ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" pyproject-parser-0.9.1/tests/test_cli_/test_reformat_False_COMPLETE_A_WITH_FILES_.diff000066400000000000000000000000361444752763000304420ustar00rootroot00000000000000Reformatting 'pyproject.toml' pyproject-parser-0.9.1/tests/test_cli_/test_reformat_False_COMPLETE_A_WITH_FILES_.toml000066400000000000000000000020311444752763000305020ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." readme = "README.rst" keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.license] file = "LICENSE" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" pyproject-parser-0.9.1/tests/test_cli_/test_reformat_False_COMPLETE_B_.diff000066400000000000000000000000361444752763000266660ustar00rootroot00000000000000Reformatting 'pyproject.toml' pyproject-parser-0.9.1/tests/test_cli_/test_reformat_False_COMPLETE_B_.toml000066400000000000000000000020401444752763000267260ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "Whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" package = "whey" additional-files = [ "include whey/style.css",] pyproject-parser-0.9.1/tests/test_cli_/test_reformat_False_COMPLETE_PROJECT_A_.diff000066400000000000000000000000361444752763000300530ustar00rootroot00000000000000Reformatting 'pyproject.toml' pyproject-parser-0.9.1/tests/test_cli_/test_reformat_False_COMPLETE_PROJECT_A_.toml000066400000000000000000000017261444752763000301250ustar00rootroot00000000000000[project] name = "spam" version = "2020.0.0" description = "Lovely Spam! Wonderful Spam!" requires-python = ">=3.8" keywords = [ "bacon", "egg", "Lobster Thermidor", "sausage", "tomatoes",] classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [] [[project.authors]] email = "hi@pradyunsg.me" [[project.authors]] name = "Tzu-Ping Chung" [[project.maintainers]] name = "Brett Cannon" email = "brett@python.org" [tool] [project.urls] homepage = "example.com" documentation = "readthedocs.org" repository = "github.com" changelog = "github.com/me/spam/blob/master/CHANGELOG.md" [project.scripts] spam-cli = "spam:main_cli" [project.gui-scripts] spam-gui = "spam:main_gui" [project.optional-dependencies] test = [ "pytest<5.0.0", "pytest-cov[all]",] [project.entry-points."spam.magical"] tomatoes = "spam:main_tomatoes" pyproject-parser-0.9.1/tests/test_cli_/test_reformat_False_COMPLETE_UNDERSCORE_NAME_.diff000066400000000000000000000000361444752763000307560ustar00rootroot00000000000000Reformatting 'pyproject.toml' pyproject-parser-0.9.1/tests/test_cli_/test_reformat_False_COMPLETE_UNDERSCORE_NAME_.toml000066400000000000000000000017701444752763000310270ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "toctree_plus" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" package = "whey" pyproject-parser-0.9.1/tests/test_cli_/test_reformat_False_UNORDERED_.diff000066400000000000000000000000361444752763000265440ustar00rootroot00000000000000Reformatting 'pyproject.toml' pyproject-parser-0.9.1/tests/test_cli_/test_reformat_False_UNORDERED_.toml000066400000000000000000000017371444752763000266200ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" pyproject-parser-0.9.1/tests/test_cli_/test_reformat_True_COMPLETE_A_.diff000066400000000000000000000015641444752763000265610ustar00rootroot00000000000000Reformatting 'pyproject.toml' --- pyproject.toml (original) +++ pyproject.toml (reformatted) @@ -6,18 +6,14 @@ name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." -keywords = [ "pep517", "pep621", "build", "sdist", "wheel", "packaging", "distribution",] +keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] +dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] -dependencies = [ - "httpx", - "gidgethub[httpx]>4.0.0", - "django>2.1; os_name != 'nt'", - "django>2.0; os_name == 'nt'" -] [[project.authors]] +name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" -name = "Dominic Davis-Foster" + [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" pyproject-parser-0.9.1/tests/test_cli_/test_reformat_True_COMPLETE_A_.toml000066400000000000000000000017371444752763000266260ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" pyproject-parser-0.9.1/tests/test_cli_/test_reformat_True_COMPLETE_A_WITH_FILES_.diff000066400000000000000000000017511444752763000303340ustar00rootroot00000000000000Reformatting 'pyproject.toml' --- pyproject.toml (original) +++ pyproject.toml (reformatted) @@ -6,20 +6,18 @@ name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." -keywords = [ "pep517", "pep621", "build", "sdist", "wheel", "packaging", "distribution",] +readme = "README.rst" +keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] +dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] -dependencies = [ - "httpx", - "gidgethub[httpx]>4.0.0", - "django>2.1; os_name != 'nt'", - "django>2.0; os_name == 'nt'" -] -license = { file = "LICENSE" } -readme = "README.rst" [[project.authors]] +name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" -name = "Dominic Davis-Foster" + + +[project.license] +file = "LICENSE" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" pyproject-parser-0.9.1/tests/test_cli_/test_reformat_True_COMPLETE_A_WITH_FILES_.toml000066400000000000000000000020311444752763000303670ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." readme = "README.rst" keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.license] file = "LICENSE" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" pyproject-parser-0.9.1/tests/test_cli_/test_reformat_True_COMPLETE_B_.diff000066400000000000000000000021011444752763000265460ustar00rootroot00000000000000Reformatting 'pyproject.toml' --- pyproject.toml (original) +++ pyproject.toml (reformatted) @@ -6,18 +6,14 @@ name = "Whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." -keywords = [ "pep517", "pep621", "build", "sdist", "wheel", "packaging", "distribution",] +keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] +dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] -dependencies = [ - "httpx", - "gidgethub[httpx]>4.0.0", - "django>2.1; os_name != 'nt'", - "django>2.0; os_name == 'nt'" -] [[project.authors]] +name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" -name = "Dominic Davis-Foster" + [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" @@ -32,7 +28,5 @@ platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" package = "whey" -additional-files = [ - "include whey/style.css", -] +additional-files = [ "include whey/style.css",] pyproject-parser-0.9.1/tests/test_cli_/test_reformat_True_COMPLETE_B_.toml000066400000000000000000000020401444752763000266130ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "Whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" package = "whey" additional-files = [ "include whey/style.css",] pyproject-parser-0.9.1/tests/test_cli_/test_reformat_True_COMPLETE_PROJECT_A_.diff000066400000000000000000000027141444752763000277450ustar00rootroot00000000000000Reformatting 'pyproject.toml' --- pyproject.toml (original) +++ pyproject.toml (reformatted) @@ -3,31 +3,23 @@ version = "2020.0.0" description = "Lovely Spam! Wonderful Spam!" requires-python = ">=3.8" -keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"] -authors = [ - {email = "hi@pradyunsg.me"}, - {name = "Tzu-Ping Chung"} -] -maintainers = [ - {name = "Brett Cannon", email = "brett@python.org"} -] -classifiers = [ - "Development Status :: 4 - Beta", - "Programming Language :: Python" -] +keywords = [ "bacon", "egg", "Lobster Thermidor", "sausage", "tomatoes",] +classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python",] +dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] +dynamic = [] -dependencies = [ - "httpx", - "gidgethub[httpx]>4.0.0", - "django>2.1; os_name != 'nt'", - "django>2.0; os_name == 'nt'" -] +[[project.authors]] +email = "hi@pradyunsg.me" -[project.optional-dependencies] -test = [ - "pytest < 5.0.0", - "pytest-cov[all]" -] +[[project.authors]] +name = "Tzu-Ping Chung" + +[[project.maintainers]] +name = "Brett Cannon" +email = "brett@python.org" + + +[tool] [project.urls] homepage = "example.com" @@ -41,6 +33,9 @@ [project.gui-scripts] spam-gui = "spam:main_gui" +[project.optional-dependencies] +test = [ "pytest<5.0.0", "pytest-cov[all]",] + [project.entry-points."spam.magical"] tomatoes = "spam:main_tomatoes" pyproject-parser-0.9.1/tests/test_cli_/test_reformat_True_COMPLETE_PROJECT_A_.toml000066400000000000000000000017261444752763000300120ustar00rootroot00000000000000[project] name = "spam" version = "2020.0.0" description = "Lovely Spam! Wonderful Spam!" requires-python = ">=3.8" keywords = [ "bacon", "egg", "Lobster Thermidor", "sausage", "tomatoes",] classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [] [[project.authors]] email = "hi@pradyunsg.me" [[project.authors]] name = "Tzu-Ping Chung" [[project.maintainers]] name = "Brett Cannon" email = "brett@python.org" [tool] [project.urls] homepage = "example.com" documentation = "readthedocs.org" repository = "github.com" changelog = "github.com/me/spam/blob/master/CHANGELOG.md" [project.scripts] spam-cli = "spam:main_cli" [project.gui-scripts] spam-gui = "spam:main_gui" [project.optional-dependencies] test = [ "pytest<5.0.0", "pytest-cov[all]",] [project.entry-points."spam.magical"] tomatoes = "spam:main_tomatoes" pyproject-parser-0.9.1/tests/test_cli_/test_reformat_True_COMPLETE_UNDERSCORE_NAME_.diff000066400000000000000000000015741444752763000306530ustar00rootroot00000000000000Reformatting 'pyproject.toml' --- pyproject.toml (original) +++ pyproject.toml (reformatted) @@ -6,18 +6,14 @@ name = "toctree_plus" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." -keywords = [ "pep517", "pep621", "build", "sdist", "wheel", "packaging", "distribution",] +keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] +dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] -dependencies = [ - "httpx", - "gidgethub[httpx]>4.0.0", - "django>2.1; os_name != 'nt'", - "django>2.0; os_name == 'nt'" -] [[project.authors]] +name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" -name = "Dominic Davis-Foster" + [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" pyproject-parser-0.9.1/tests/test_cli_/test_reformat_True_COMPLETE_UNDERSCORE_NAME_.toml000066400000000000000000000017701444752763000307140ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "toctree_plus" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" package = "whey" pyproject-parser-0.9.1/tests/test_cli_/test_reformat_True_UNORDERED_.diff000066400000000000000000000030101444752763000264240ustar00rootroot00000000000000Reformatting 'pyproject.toml' --- pyproject.toml (original) +++ pyproject.toml (reformatted) @@ -1,22 +1,18 @@ -[project] -keywords = [ "pep517", "pep621", "build", "sdist", "wheel", "packaging", "distribution",] -version = "2021.0.0" -description = "A simple Python wheel builder for simple projects." -dynamic = [ "classifiers", "requires-python",] -dependencies = [ - "httpx", - "gidgethub[httpx]>4.0.0", - "django>2.1; os_name != 'nt'", - "django>2.0; os_name == 'nt'" -] -name = "whey" - [build-system] requires = [ "whey",] build-backend = "whey" +[project] +name = "whey" +version = "2021.0.0" +description = "A simple Python wheel builder for simple projects." +keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] +dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] +dynamic = [ "classifiers", "requires-python",] - +[[project.authors]] +name = "Dominic Davis-Foster" +email = "dominic@davis-foster.co.uk" [project.urls] @@ -24,6 +20,7 @@ Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" + [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] @@ -31,7 +28,3 @@ platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" -[[project.authors]] -email = "dominic@davis-foster.co.uk" -name = "Dominic Davis-Foster" - pyproject-parser-0.9.1/tests/test_cli_/test_reformat_True_UNORDERED_.toml000066400000000000000000000017371444752763000265050ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_AttributeError_.txt000066400000000000000000000001651444752763000305670ustar00rootroot00000000000000AttributeError: type object 'list' has no attribute 'foo' Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_BadConfigError_.txt000066400000000000000000000001561444752763000304400ustar00rootroot00000000000000BadConfigError: Expected a string value for 'name' Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_Exception_.txt000066400000000000000000000000561444752763000275470ustar00rootroot00000000000000An error occurred: Something's awry! Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_FileExistsError_.txt000066400000000000000000000000361444752763000307000ustar00rootroot00000000000000File Exists: foo.txt Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_FileNotFoundError_.txt000066400000000000000000000000561444752763000311570ustar00rootroot00000000000000No such file or directory: 'foo.txt' Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_FileNotFoundError_path_.txt000066400000000000000000000000561444752763000321730ustar00rootroot00000000000000No such file or directory: 'foo.txt' Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_FileNotFoundError_path_move_etc_.txt000066400000000000000000000000721444752763000340520ustar00rootroot00000000000000No such file or directory: 'foo.txt' -> 'bar.md' Aborted! test_traceback_handler_FileNotFoundError_path_move_etc_win_.txt000066400000000000000000000000721444752763000346500ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_cli_No such file or directory: 'foo.txt' -> 'bar.md' Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_FileNotFoundError_path_win_.txt000066400000000000000000000000561444752763000330500ustar00rootroot00000000000000No such file or directory: 'foo.txt' Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_FileNotFoundError_win_.txt000066400000000000000000000000561444752763000320340ustar00rootroot00000000000000No such file or directory: 'foo.txt' Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_ImportError_.txt000066400000000000000000000001361444752763000300740ustar00rootroot00000000000000ImportError: No module named 'foo' Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_KeyError_.txt000066400000000000000000000001141444752763000273460ustar00rootroot00000000000000KeyError: 'name' Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_ModuleNotFoundError_.txt000066400000000000000000000001461444752763000315250ustar00rootroot00000000000000ModuleNotFoundError: No module named 'foo' Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_NameError_.txt000066400000000000000000000000701444752763000274770ustar00rootroot00000000000000An error occurred: name 'hello' is not defined Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_SyntaxError_.txt000066400000000000000000000000531444752763000301060ustar00rootroot00000000000000An error occurred: invalid syntax Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_TypeError_.txt000066400000000000000000000001461444752763000275440ustar00rootroot00000000000000TypeError: Expected type int, got type str Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_/test_traceback_handler_ValueError_.txt000066400000000000000000000001321444752763000276720ustar00rootroot00000000000000ValueError: 'age' must be >= 0 Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_module.py000066400000000000000000000073211444752763000217400ustar00rootroot00000000000000# stdlib import collections import email.headerregistry import re import sys import warnings from typing import Type # 3rd party import click import pytest from coincidence.regressions import AdvancedFileRegressionFixture from consolekit.testing import CliRunner, Result from consolekit.tracebacks import handle_tracebacks from dom_toml.parser import BadConfigError from domdf_python_tools.utils import redirect_output # this package from pyproject_parser.cli import ConfigTracebackHandler, prettify_deprecation_warning, resolve_class from pyproject_parser.utils import PyProjectDeprecationWarning exceptions = pytest.mark.parametrize( "exception", [ pytest.param(FileNotFoundError("foo.txt"), id="FileNotFoundError"), pytest.param(FileExistsError("foo.txt"), id="FileExistsError"), pytest.param(Exception("Something's awry!"), id="Exception"), pytest.param(ValueError("'age' must be >= 0"), id="ValueError"), pytest.param(TypeError("Expected type int, got type str"), id="TypeError"), pytest.param(NameError("name 'hello' is not defined"), id="NameError"), pytest.param(SyntaxError("invalid syntax"), id="SyntaxError"), pytest.param(BadConfigError("Expected a string value for 'name'"), id="BadConfigError"), pytest.param(KeyError("name"), id="KeyError"), ] ) @exceptions def test_traceback_handler( exception: Exception, advanced_file_regression: AdvancedFileRegressionFixture, cli_runner: CliRunner, ): @click.command() def demo(): # noqa: MAN002 with handle_tracebacks(False, ConfigTracebackHandler): raise exception result: Result = cli_runner.invoke(demo, catch_exceptions=False) result.check_stdout(advanced_file_regression) assert result.exit_code == 1 @exceptions def test_traceback_handler_show_traceback(exception: Exception, cli_runner: CliRunner): @click.command() def demo(): # noqa: MAN002 with handle_tracebacks(True, ConfigTracebackHandler): raise exception with pytest.raises(type(exception), match=re.escape(str(exception))): cli_runner.invoke(demo, catch_exceptions=False) @pytest.mark.parametrize("exception", [EOFError(), KeyboardInterrupt(), click.Abort()]) def test_handle_tracebacks_ignored_exceptions_click( exception: Exception, cli_runner: CliRunner, ): @click.command() def demo(): # noqa: MAN002 with handle_tracebacks(False, ConfigTracebackHandler): raise exception result: Result = cli_runner.invoke(demo, catch_exceptions=False) assert result.stdout.strip() == "Aborted!" assert result.exit_code == 1 @pytest.mark.parametrize("exception", [EOFError, KeyboardInterrupt, click.Abort, SystemExit]) def test_handle_tracebacks_ignored_exceptions(exception: Type[Exception]): with pytest.raises(exception): # noqa: PT012 with handle_tracebacks(False, ConfigTracebackHandler): raise exception def test_resolve_class(): # stdlib import importlib assert resolve_class("collections:defaultdict", "class") is collections.defaultdict assert resolve_class("importlib:import_module", "class") is importlib.import_module if sys.version_info >= (3, 8): # stdlib import importlib.metadata assert resolve_class("importlib.metadata:metadata", "class") is importlib.metadata.metadata assert resolve_class("email.headerregistry:Address", "class") is email.headerregistry.Address with pytest.raises(click.BadOptionUsage, match="Invalid syntax for '--class'") as e: resolve_class("collections.Counter", "class") assert e.value.option_name == "class" @pytest.mark.filterwarnings("default") def test_prettify_deprecation_warning(): with redirect_output() as (stdout, stderr): prettify_deprecation_warning() warnings.warn("This is a warning", PyProjectDeprecationWarning) assert stderr.getvalue() == 'WARNING: This is a warning\n' pyproject-parser-0.9.1/tests/test_cli_module_/000077500000000000000000000000001444752763000215225ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_cli_module_/test_traceback_handler_BadConfigError_.txt000066400000000000000000000001561444752763000320050ustar00rootroot00000000000000BadConfigError: Expected a string value for 'name' Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_module_/test_traceback_handler_Exception_.txt000066400000000000000000000000561444752763000311140ustar00rootroot00000000000000An error occurred: Something's awry! Aborted! pyproject-parser-0.9.1/tests/test_cli_module_/test_traceback_handler_FileExistsError_.txt000066400000000000000000000000361444752763000322450ustar00rootroot00000000000000File Exists: foo.txt Aborted! pyproject-parser-0.9.1/tests/test_cli_module_/test_traceback_handler_FileNotFoundError_.txt000066400000000000000000000000411444752763000325160ustar00rootroot00000000000000File Not Found: foo.txt Aborted! pyproject-parser-0.9.1/tests/test_cli_module_/test_traceback_handler_KeyError_.txt000066400000000000000000000001141444752763000307130ustar00rootroot00000000000000KeyError: 'name' Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_module_/test_traceback_handler_NameError_.txt000066400000000000000000000000701444752763000310440ustar00rootroot00000000000000An error occurred: name 'hello' is not defined Aborted! pyproject-parser-0.9.1/tests/test_cli_module_/test_traceback_handler_SyntaxError_.txt000066400000000000000000000000531444752763000314530ustar00rootroot00000000000000An error occurred: invalid syntax Aborted! pyproject-parser-0.9.1/tests/test_cli_module_/test_traceback_handler_TypeError_.txt000066400000000000000000000001461444752763000311110ustar00rootroot00000000000000TypeError: Expected type int, got type str Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_cli_module_/test_traceback_handler_ValueError_.txt000066400000000000000000000001321444752763000312370ustar00rootroot00000000000000ValueError: 'age' must be >= 0 Use '--traceback' to view the full traceback. Aborted! pyproject-parser-0.9.1/tests/test_config.py000066400000000000000000000407571444752763000211030ustar00rootroot00000000000000# stdlib from textwrap import dedent from typing import Type # 3rd party import pytest from coincidence.regressions import AdvancedDataRegressionFixture from dom_toml.parser import BadConfigError from domdf_python_tools.paths import PathPlus, in_directory from pyproject_examples import ( OPTIONAL_DEPENDENCIES, bad_buildsystem_config, bad_pep621_config, valid_buildsystem_config, valid_pep621_config ) from pyproject_examples.example_configs import MINIMAL_CONFIG from shippinglabel import normalize_keep_dot # this package from pyproject_parser.parsers import BuildSystemParser, PEP621Parser, RequiredKeysConfigParser from pyproject_parser.utils import PyProjectDeprecationWarning, _load_toml @pytest.mark.parametrize("set_defaults", [True, False]) @pytest.mark.parametrize( "toml_config", [ *valid_pep621_config, pytest.param(f"{OPTIONAL_DEPENDENCIES}dev-test = ['black']\n", id="optional-dependencies-hyphen"), ] ) def test_pep621_class_valid_config( toml_config: str, tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, set_defaults: bool, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_config) with in_directory(tmp_pathplus): config = PEP621Parser().parse( _load_toml(tmp_pathplus / "pyproject.toml")["project"], set_defaults=set_defaults, ) advanced_data_regression.check(config) class ReducedPEP621Parser(PEP621Parser, inherit_defaults=True): keys = ["name", "version", "dependencies"] @pytest.mark.parametrize("toml_config", valid_pep621_config) def test_pep621_subclass( toml_config: str, tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_config) with in_directory(tmp_pathplus): config = ReducedPEP621Parser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["project"]) advanced_data_regression.check(config) class NormalizingPEP621Parser(PEP621Parser, inherit_defaults=True): keys = ["name", "dependencies", "optional-dependencies"] class NormalizingWithDotPEP621Parser(PEP621Parser, inherit_defaults=True): keys = ["name", "dependencies", "optional-dependencies"] @staticmethod def normalize_requirement_name(name: str) -> str: return normalize_keep_dot(name) class UnNormalizingPEP621Parser(PEP621Parser, inherit_defaults=True): keys = ["name", "dependencies", "optional-dependencies"] @staticmethod def normalize_requirement_name(name: str) -> str: return name pep621_config_for_normalize_test = [ MINIMAL_CONFIG, 'dependencies = ["whey", "A.b", "domdf_python_tools"]', '', "[project.optional-dependencies]", "test = [", ' "Pytest",', ' "d.e.f",', ' "chemistry_tools",', ']' ] def test_pep621_unnormalized( tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_lines(pep621_config_for_normalize_test) with in_directory(tmp_pathplus): config = UnNormalizingPEP621Parser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["project"]) advanced_data_regression.check(config) def test_pep621_normalize( tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_lines(pep621_config_for_normalize_test) with in_directory(tmp_pathplus): config = NormalizingPEP621Parser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["project"]) advanced_data_regression.check(config) def test_pep621_normalize_keep_dot( tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_lines(pep621_config_for_normalize_test) with in_directory(tmp_pathplus): config = NormalizingWithDotPEP621Parser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["project"]) advanced_data_regression.check(config) @pytest.mark.parametrize("filename", ["README.rst", "README.md", "INTRODUCTION.md", "readme.txt"]) def test_pep621_class_valid_config_readme( filename: str, tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_lines([ "[project]", 'name = "spam"', 'version = "2020.0.0"', f'readme = {filename!r}', ]) (tmp_pathplus / filename).write_text("This is the readme.") with in_directory(tmp_pathplus): config = PEP621Parser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["project"]) advanced_data_regression.check(config) @pytest.mark.parametrize( "readme", [ pytest.param('readme = {file = "README.rst"}', id="rst_file"), pytest.param('readme = {file = "README.md"}', id="md_file"), pytest.param('readme = {file = "README.txt"}', id="txt_file"), pytest.param( 'readme = {text = "This is the inline README README.", content-type = "text/x-rst"}', id="text_content_type_rst" ), pytest.param( 'readme = {text = "This is the inline markdown README.", content-type = "text/markdown"}', id="text_content_type_md" ), pytest.param( 'readme = {text = "This is the inline README.", content-type = "text/plain"}', id="text_content_type_plain" ), ] ) def test_pep621_class_valid_config_readme_dict( readme: str, tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_lines([ "[project]", 'name = "spam"', 'version = "2020.0.0"', readme, ]) (tmp_pathplus / "README.rst").write_text("This is the reStructuredText README.") (tmp_pathplus / "README.md").write_text("This is the markdown README.") (tmp_pathplus / "README.txt").write_text("This is the plaintext README.") (tmp_pathplus / "README").write_text("This is the README.") with in_directory(tmp_pathplus): config = PEP621Parser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["project"]) advanced_data_regression.check(config) @pytest.mark.parametrize( "readme, expected, exception", [ pytest.param( "readme = {}", "The 'project.readme' table cannot be empty.", BadConfigError, id="empty", ), pytest.param( "readme = {fil = 'README.md'}", "Unknown format for 'project.readme': {'fil': 'README.md'}", BadConfigError, id="unknown_key", ), pytest.param( 'readme = {text = "This is the inline README."}', "The 'project.readme.content-type' key must be provided when 'project.readme.text' is given.", BadConfigError, id="text_only" ), pytest.param( 'readme = {content-type = "text/x-rst"}', "The 'project.readme.content-type' key cannot be provided on its own; " "Please provide the 'project.readme.text' key too.", BadConfigError, id="content_type_only" ), pytest.param( 'readme = {charset = "cp1252"}', "The 'project.readme.charset' key cannot be provided on its own; " "Please provide the 'project.readme.text' key too.", BadConfigError, id="charset_only" ), pytest.param( 'readme = {charset = "cp1252", content-type = "text/x-rst"}', "The 'project.readme.content-type' key cannot be provided on its own; " "Please provide the 'project.readme.text' key too.", BadConfigError, id="content_type_charset" ), pytest.param( 'readme = {text = "This is the inline README", content-type = "application/x-abiword"}', "Unrecognised value for 'project.readme.content-type': 'application/x-abiword'", BadConfigError, id="bad_content_type" ), pytest.param( 'readme = {file = "README"}', "Unsupported extension for 'README'", ValueError, id="no_extension", ), pytest.param( 'readme = {file = "README.doc"}', "Unsupported extension for 'README.doc'", ValueError, id="bad_extension" ), pytest.param( 'readme = {file = "README.doc", text = "This is the README"}', "The 'project.readme.file' and 'project.readme.text' keys are mutually exclusive.", BadConfigError, id="file_and_readme" ), ] ) def test_pep621_class_bad_config_readme( readme: str, expected: str, exception: Type[Exception], tmp_pathplus: PathPlus, ): (tmp_pathplus / "pyproject.toml").write_lines([ "[project]", 'name = "spam"', 'version = "2020.0.0"', readme, ]) with in_directory(tmp_pathplus), pytest.raises(exception, match=expected): PEP621Parser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["project"]) @pytest.mark.parametrize("filename", ["LICENSE.rst", "LICENSE.md", "LICENSE.txt", "LICENSE"]) def test_pep621_class_valid_config_license( filename: str, tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_lines([ f'[project]', f'name = "spam"', f'version = "2020.0.0"', f'license = {{file = "{filename}"}}', ]) (tmp_pathplus / filename).write_text("This is the license.") with in_directory(tmp_pathplus): config = PEP621Parser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["project"]) advanced_data_regression.check(config) def test_pep621_class_valid_config_license_dict( tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_lines([ f'[project]', f'name = "spam"', f'version = "2020.0.0"', f'license = {{text = "This is the MIT License"}}', ]) with in_directory(tmp_pathplus): config = PEP621Parser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["project"]) advanced_data_regression.check(config) @pytest.mark.parametrize( "license_key, expected", [ pytest.param( "license = {}", "The 'project.license' table should contain one of 'text' or 'file'.", id="empty" ), pytest.param( 'license = {text = "MIT", file = "LICENSE.txt"}', "The 'project.license.file' and 'project.license.text' keys are mutually exclusive.", id="double_license" ), ] ) def test_pep621_class_bad_config_license( license_key: str, expected: str, tmp_pathplus: PathPlus, ): (tmp_pathplus / "pyproject.toml").write_lines([ f'[project]', f'name = "spam"', f'version = "2020.0.0"', license_key, ]) with in_directory(tmp_pathplus), pytest.raises(BadConfigError, match=expected): PEP621Parser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["project"]) @pytest.mark.parametrize( "config, expects, match", [ *bad_pep621_config, # pytest.param( # '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\nwith-hyphen = []', # TypeError, # "Invalid extra name 'with-hyphen'", # id="extra_invalid_a", # ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"quoted?" = []', TypeError, r"Invalid extra name 'quoted\?'", id="extra_invalid_b", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"number#1" = []', TypeError, "Invalid extra name 'number#1'", id="extra_invalid_c", ), ] ) def test_pep621_class_bad_config( config: str, expects: Type[Exception], match: str, tmp_pathplus: PathPlus, ): (tmp_pathplus / "pyproject.toml").write_clean(config) with in_directory(tmp_pathplus), pytest.raises(expects, match=match): PEP621Parser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["project"]) @pytest.mark.parametrize( "config, match", [ pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"dev_test" = []\n"dev-test" = []', "'project.optional-dependencies.dev-test': Multiple extras were defined with the same normalized name of 'dev-test'", id="duplicate_extra_1", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"dev-test" = []\n"dev_test" = []', "'project.optional-dependencies.dev_test': Multiple extras were defined with the same normalized name of 'dev-test'", id="duplicate_extra_2", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"dev.test" = []\n"dev_test" = []', "'project.optional-dependencies.dev_test': Multiple extras were defined with the same normalized name of 'dev-test'", id="duplicate_extra_3", ), ] ) def test_extra_deprecation( config: str, match: str, tmp_pathplus: PathPlus, ): (tmp_pathplus / "pyproject.toml").write_clean(config) with in_directory(tmp_pathplus), pytest.warns(PyProjectDeprecationWarning, match=match): PEP621Parser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["project"]) @pytest.mark.parametrize("filename", ["README", "README.rtf"]) def test_parse_config_readme_errors(filename: str, tmp_pathplus: PathPlus): config = dedent(f""" [project] name = "spam" version = "2020.0.0" readme = "{filename}" """) (tmp_pathplus / "pyproject.toml").write_clean(config) (tmp_pathplus / filename).write_text("This is the readme.") with in_directory(tmp_pathplus), pytest.raises(ValueError, match=f"Unsupported extension for '{filename}'"): PEP621Parser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["project"]) @pytest.mark.parametrize("set_defaults", [True, False]) @pytest.mark.parametrize("toml_config", valid_buildsystem_config) def test_buildsystem_parser_valid_config( toml_config: str, tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, set_defaults: bool, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_config) config = BuildSystemParser().parse( _load_toml(tmp_pathplus / "pyproject.toml")["build-system"], set_defaults=set_defaults, ) config["requires"] = list(map(str, config["requires"])) # type: ignore[arg-type] advanced_data_regression.check(config) @pytest.mark.parametrize("config, expects, match", bad_buildsystem_config) def test_buildsystem_parser_errors(config: str, expects: Type[Exception], match: str, tmp_pathplus: PathPlus): (tmp_pathplus / "pyproject.toml").write_clean(config) with in_directory(tmp_pathplus), pytest.raises(expects, match=match): BuildSystemParser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["build-system"]) class NormalizingBuildSystemParser(BuildSystemParser, inherit_defaults=True): keys = ["requires"] class NormalizingWithDotBuildSystemParser(BuildSystemParser, inherit_defaults=True): keys = ["requires"] @staticmethod def normalize_requirement_name(name: str) -> str: return normalize_keep_dot(name) class UnNormalizingBuildSystemParser(BuildSystemParser, inherit_defaults=True): keys = ["requires"] @staticmethod def normalize_requirement_name(name: str) -> str: return name def test_buildsystem_normalize( tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_lines([ "[build-system]", 'requires = ["whey", "Foo.bar", "domdf_python_tools"]', ]) with in_directory(tmp_pathplus): config = NormalizingBuildSystemParser().parse(_load_toml(tmp_pathplus / "pyproject.toml")["build-system"]) advanced_data_regression.check(config) def test_buildsystem_normalize_keep_dot( tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_lines([ "[build-system]", 'requires = ["whey", "Foo.bar", "domdf_python_tools"]', ]) with in_directory(tmp_pathplus): config = NormalizingWithDotBuildSystemParser().parse( _load_toml(tmp_pathplus / "pyproject.toml")["build-system"] ) advanced_data_regression.check(config) def test_buildsystem_unnormalized( tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_lines([ "[build-system]", 'requires = ["whey", "Foo.bar", "domdf_python_tools"]', ]) with in_directory(tmp_pathplus): config = UnNormalizingBuildSystemParser().parse( _load_toml(tmp_pathplus / "pyproject.toml")["build-system"] ) advanced_data_regression.check(config) def test_RequiredKeysConfigParser(): class MyConfigParser(RequiredKeysConfigParser): table_name = "my_table" required_keys = ["foo"] keys = ["foo", "bar"] defaults = {"foo": "foo-default", "bar": "bar-defaults"} with pytest.raises(BadConfigError, match="The 'my_table.foo' field must be provided."): MyConfigParser().parse({}) assert MyConfigParser().parse({}, set_defaults=True) == {"foo": "foo-default", "bar": "bar-defaults"} assert MyConfigParser().parse({"foo": "baz"}, set_defaults=True) == {"foo": "baz", "bar": "bar-defaults"} pyproject-parser-0.9.1/tests/test_config_/000077500000000000000000000000001444752763000206535ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_/test_buildsystem_normalize.yml000066400000000000000000000000601444752763000270550ustar00rootroot00000000000000requires: - domdf-python-tools - foo-bar - whey pyproject-parser-0.9.1/tests/test_config_/test_buildsystem_normalize_keep_dot.yml000066400000000000000000000000601444752763000307270ustar00rootroot00000000000000requires: - domdf-python-tools - foo.bar - whey test_buildsystem_parser_valid_config_backend_path_False_.yml000066400000000000000000000000741444752763000347370ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_backend-path: - ../foo build-backend: whey requires: - whey test_buildsystem_parser_valid_config_backend_path_True_.yml000066400000000000000000000000741444752763000346240ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_backend-path: - ../foo build-backend: whey requires: - whey test_buildsystem_parser_valid_config_backend_paths_False_.yml000066400000000000000000000001041444752763000351140ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_backend-path: - ../foo - ./bar build-backend: whey requires: - whey test_buildsystem_parser_valid_config_backend_paths_True_.yml000066400000000000000000000001041444752763000350010ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_backend-path: - ../foo - ./bar build-backend: whey requires: - whey pyproject-parser-0.9.1/tests/test_config_/test_buildsystem_parser_valid_config_complete_False_.yml000066400000000000000000000000451444752763000342210ustar00rootroot00000000000000build-backend: whey requires: - whey pyproject-parser-0.9.1/tests/test_config_/test_buildsystem_parser_valid_config_complete_True_.yml000066400000000000000000000000701444752763000341040ustar00rootroot00000000000000backend-path: null build-backend: whey requires: - whey test_buildsystem_parser_valid_config_requires_nothing_False_.yml000066400000000000000000000000151444752763000357140ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_requires: [] test_buildsystem_parser_valid_config_requires_nothing_True_.yml000066400000000000000000000000641444752763000356050ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_backend-path: null build-backend: null requires: [] test_buildsystem_parser_valid_config_requires_setuptools_False_.yml000066400000000000000000000000371444752763000364730ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_requires: - setuptools - wheel test_buildsystem_parser_valid_config_requires_setuptools_True_.yml000066400000000000000000000001061444752763000363550ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_backend-path: null build-backend: null requires: - setuptools - wheel test_buildsystem_parser_valid_config_requires_whey_False_.yml000066400000000000000000000000211444752763000352170ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_requires: - whey test_buildsystem_parser_valid_config_requires_whey_True_.yml000066400000000000000000000000701444752763000351100ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_backend-path: null build-backend: null requires: - whey pyproject-parser-0.9.1/tests/test_config_/test_buildsystem_unnormalized.yml000066400000000000000000000000601444752763000275640ustar00rootroot00000000000000requires: - Foo.bar - domdf_python_tools - whey pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_COMPLETE_A_False_.yml000066400000000000000000000011501444752763000323410ustar00rootroot00000000000000authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_COMPLETE_A_True_.yml000066400000000000000000000014001444752763000322240ustar00rootroot00000000000000authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster classifiers: [] dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python entry-points: {} gui-scripts: {} keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel license: null maintainers: [] name: whey optional-dependencies: {} readme: null requires-python: null scripts: {} urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_COMPLETE_B_False_.yml000066400000000000000000000011501444752763000323420ustar00rootroot00000000000000authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_COMPLETE_B_True_.yml000066400000000000000000000014001444752763000322250ustar00rootroot00000000000000authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster classifiers: [] dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python entry-points: {} gui-scripts: {} keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel license: null maintainers: [] name: whey optional-dependencies: {} readme: null requires-python: null scripts: {} urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 test_pep621_class_valid_config_COMPLETE_PROJECT_A_False_.yml000066400000000000000000000015311444752763000334530ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_authors: - email: hi@pradyunsg.me name: null - email: null name: Tzu-Ping Chung classifiers: - 'Development Status :: 4 - Beta' - 'Programming Language :: Python' dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: Lovely Spam! Wonderful Spam! dynamic: [] entry-points: spam.magical: tomatoes: spam:main_tomatoes gui-scripts: spam-gui: spam:main_gui keywords: - bacon - egg - Lobster Thermidor - sausage - tomatoes maintainers: - email: brett@python.org name: Brett Cannon name: spam optional-dependencies: test: - pytest<5.0.0 - pytest-cov[all] requires-python: '>=3.8' scripts: spam-cli: spam:main_cli urls: changelog: github.com/me/spam/blob/master/CHANGELOG.md documentation: readthedocs.org homepage: example.com repository: github.com version: 2020.0.0 test_pep621_class_valid_config_COMPLETE_PROJECT_A_True_.yml000066400000000000000000000015641444752763000333460ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_authors: - email: hi@pradyunsg.me name: null - email: null name: Tzu-Ping Chung classifiers: - 'Development Status :: 4 - Beta' - 'Programming Language :: Python' dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: Lovely Spam! Wonderful Spam! dynamic: [] entry-points: spam.magical: tomatoes: spam:main_tomatoes gui-scripts: spam-gui: spam:main_gui keywords: - bacon - egg - Lobster Thermidor - sausage - tomatoes license: null maintainers: - email: brett@python.org name: Brett Cannon name: spam optional-dependencies: test: - pytest<5.0.0 - pytest-cov[all] readme: null requires-python: '>=3.8' scripts: spam-cli: spam:main_cli urls: changelog: github.com/me/spam/blob/master/CHANGELOG.md documentation: readthedocs.org homepage: example.com repository: github.com version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_authors_False_.yml000066400000000000000000000001751444752763000323640ustar00rootroot00000000000000authors: - email: hi@pradyunsg.me name: null - email: null name: Tzu-Ping Chung dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_authors_True_.yml000066400000000000000000000005161444752763000322500ustar00rootroot00000000000000authors: - email: hi@pradyunsg.me name: null - email: null name: Tzu-Ping Chung classifiers: [] dependencies: [] description: null dynamic: [] entry-points: {} gui-scripts: {} keywords: [] license: null maintainers: [] name: spam optional-dependencies: {} readme: null requires-python: null scripts: {} urls: {} version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_classifiers_False_.yml000066400000000000000000000001741444752763000332050ustar00rootroot00000000000000classifiers: - 'Development Status :: 4 - Beta' - 'Programming Language :: Python' dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_classifiers_True_.yml000066400000000000000000000005111444752763000330650ustar00rootroot00000000000000authors: [] classifiers: - 'Development Status :: 4 - Beta' - 'Programming Language :: Python' dependencies: [] description: null dynamic: [] entry-points: {} gui-scripts: {} keywords: [] license: null maintainers: [] name: spam optional-dependencies: {} readme: null requires-python: null scripts: {} urls: {} version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_dependencies_False_.yml000066400000000000000000000002241444752763000333200ustar00rootroot00000000000000dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_dependencies_True_.yml000066400000000000000000000005401444752763000332060ustar00rootroot00000000000000authors: [] classifiers: [] dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: null dynamic: [] entry-points: {} gui-scripts: {} keywords: [] license: null maintainers: [] name: spam optional-dependencies: {} readme: null requires-python: null scripts: {} urls: {} version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_description_False_.yml000066400000000000000000000001231444752763000332130ustar00rootroot00000000000000description: Lovely Spam! Wonderful Spam! dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_description_True_.yml000066400000000000000000000004361444752763000331070ustar00rootroot00000000000000authors: [] classifiers: [] dependencies: [] description: Lovely Spam! Wonderful Spam! dynamic: [] entry-points: {} gui-scripts: {} keywords: [] license: null maintainers: [] name: spam optional-dependencies: {} readme: null requires-python: null scripts: {} urls: {} version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_entry_points_False_.yml000066400000000000000000000003521444752763000334310ustar00rootroot00000000000000dynamic: [] entry-points: flake8.extension: SXL: flake8_sphinx_links:Plugin spam.magical: tomatoes: spam:main_tomatoes gui-scripts: spam-gui: spam:main_gui name: spam scripts: spam-cli: spam:main_cli version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_entry_points_True_.yml000066400000000000000000000006321444752763000333170ustar00rootroot00000000000000authors: [] classifiers: [] dependencies: [] description: null dynamic: [] entry-points: flake8.extension: SXL: flake8_sphinx_links:Plugin spam.magical: tomatoes: spam:main_tomatoes gui-scripts: spam-gui: spam:main_gui keywords: [] license: null maintainers: [] name: spam optional-dependencies: {} readme: null requires-python: null scripts: spam-cli: spam:main_cli urls: {} version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_keywords_False_.yml000066400000000000000000000001521444752763000325410ustar00rootroot00000000000000dynamic: [] keywords: - bacon - egg - Lobster Thermidor - sausage - tomatoes name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_keywords_True_.yml000066400000000000000000000004721444752763000324330ustar00rootroot00000000000000authors: [] classifiers: [] dependencies: [] description: null dynamic: [] entry-points: {} gui-scripts: {} keywords: - bacon - egg - Lobster Thermidor - sausage - tomatoes license: null maintainers: [] name: spam optional-dependencies: {} readme: null requires-python: null scripts: {} urls: {} version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_license_LICENSE_.yml000066400000000000000000000001021444752763000323370ustar00rootroot00000000000000dynamic: [] license: file: LICENSE name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_license_LICENSE_md_.yml000066400000000000000000000001051444752763000330220ustar00rootroot00000000000000dynamic: [] license: file: LICENSE.md name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_license_LICENSE_rst_.yml000066400000000000000000000001061444752763000332330ustar00rootroot00000000000000dynamic: [] license: file: LICENSE.rst name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_license_LICENSE_txt_.yml000066400000000000000000000001061444752763000332420ustar00rootroot00000000000000dynamic: [] license: file: LICENSE.txt name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_license_dict.yml000066400000000000000000000001221444752763000320430ustar00rootroot00000000000000dynamic: [] license: text: This is the MIT License name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_maintainers_False_.yml000066400000000000000000000001451444752763000332060ustar00rootroot00000000000000dynamic: [] maintainers: - email: brett@python.org name: Brett Cannon name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_maintainers_True_.yml000066400000000000000000000004621444752763000330750ustar00rootroot00000000000000authors: [] classifiers: [] dependencies: [] description: null dynamic: [] entry-points: {} gui-scripts: {} keywords: [] license: null maintainers: - email: brett@python.org name: Brett Cannon name: spam optional-dependencies: {} readme: null requires-python: null scripts: {} urls: {} version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_minimal_False_.yml000066400000000000000000000000511444752763000323160ustar00rootroot00000000000000dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_minimal_True_.yml000066400000000000000000000004061444752763000322070ustar00rootroot00000000000000authors: [] classifiers: [] dependencies: [] description: null dynamic: [] entry-points: {} gui-scripts: {} keywords: [] license: null maintainers: [] name: spam optional-dependencies: {} readme: null requires-python: null scripts: {} urls: {} version: 2020.0.0 test_pep621_class_valid_config_optional_dependencies_False_.yml000066400000000000000000000002741444752763000351530ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_dynamic: [] name: spam optional-dependencies: test: - matplotlib>=3.0.0; platform_machine != "aarch64" or python_version > "3.6" - pytest<5.0.0 - pytest-cov[all] version: 2020.0.0 test_pep621_class_valid_config_optional_dependencies_True_.yml000066400000000000000000000005771444752763000350460ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_authors: [] classifiers: [] dependencies: [] description: null dynamic: [] entry-points: {} gui-scripts: {} keywords: [] license: null maintainers: [] name: spam optional-dependencies: test: - matplotlib>=3.0.0; platform_machine != "aarch64" or python_version > "3.6" - pytest<5.0.0 - pytest-cov[all] readme: null requires-python: null scripts: {} urls: {} version: 2020.0.0 test_pep621_class_valid_config_optional_dependencies_empty_group_False_.yml000066400000000000000000000003071444752763000376020ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_dynamic: [] name: spam optional-dependencies: docs: [] test: - matplotlib>=3.0.0; platform_machine != "aarch64" or python_version > "3.6" - pytest<5.0.0 - pytest-cov[all] version: 2020.0.0 test_pep621_class_valid_config_optional_dependencies_empty_group_True_.yml000066400000000000000000000006121444752763000374660ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_authors: [] classifiers: [] dependencies: [] description: null dynamic: [] entry-points: {} gui-scripts: {} keywords: [] license: null maintainers: [] name: spam optional-dependencies: docs: [] test: - matplotlib>=3.0.0; platform_machine != "aarch64" or python_version > "3.6" - pytest<5.0.0 - pytest-cov[all] readme: null requires-python: null scripts: {} urls: {} version: 2020.0.0 test_pep621_class_valid_config_optional_dependencies_hyphen_False_.yml000066400000000000000000000003221444752763000365200ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_dynamic: [] name: spam optional-dependencies: dev-test: - black test: - matplotlib>=3.0.0; platform_machine != "aarch64" or python_version > "3.6" - pytest<5.0.0 - pytest-cov[all] version: 2020.0.0 test_pep621_class_valid_config_optional_dependencies_hyphen_True_.yml000066400000000000000000000006251444752763000364130ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_authors: [] classifiers: [] dependencies: [] description: null dynamic: [] entry-points: {} gui-scripts: {} keywords: [] license: null maintainers: [] name: spam optional-dependencies: dev-test: - black test: - matplotlib>=3.0.0; platform_machine != "aarch64" or python_version > "3.6" - pytest<5.0.0 - pytest-cov[all] readme: null requires-python: null scripts: {} urls: {} version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_readme_INTRODUCTION_md_.yml000066400000000000000000000001471444752763000335020ustar00rootroot00000000000000dynamic: [] name: spam readme: content_type: text/markdown file: INTRODUCTION.md version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_readme_README_md_.yml000066400000000000000000000001411444752763000325100ustar00rootroot00000000000000dynamic: [] name: spam readme: content_type: text/markdown file: README.md version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_readme_README_rst_.yml000066400000000000000000000001371444752763000327250ustar00rootroot00000000000000dynamic: [] name: spam readme: content_type: text/x-rst file: README.rst version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_readme_dict_md_file_.yml000066400000000000000000000001411444752763000334750ustar00rootroot00000000000000dynamic: [] name: spam readme: content_type: text/markdown file: README.md version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_readme_dict_rst_file_.yml000066400000000000000000000001371444752763000337120ustar00rootroot00000000000000dynamic: [] name: spam readme: content_type: text/x-rst file: README.rst version: 2020.0.0 test_pep621_class_valid_config_readme_dict_text_content_type_md_.yml000066400000000000000000000001731444752763000362630ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_dynamic: [] name: spam readme: content_type: text/markdown text: This is the inline markdown README. version: 2020.0.0 test_pep621_class_valid_config_readme_dict_text_content_type_plain_.yml000066400000000000000000000001571444752763000367700ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_dynamic: [] name: spam readme: content_type: text/plain text: This is the inline README. version: 2020.0.0 test_pep621_class_valid_config_readme_dict_text_content_type_rst_.yml000066400000000000000000000001661444752763000364750ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_dynamic: [] name: spam readme: content_type: text/x-rst text: This is the inline README README. version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_readme_dict_txt_file_.yml000066400000000000000000000001371444752763000337210ustar00rootroot00000000000000dynamic: [] name: spam readme: content_type: text/plain file: README.txt version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_readme_readme_txt_.yml000066400000000000000000000001371444752763000332340ustar00rootroot00000000000000dynamic: [] name: spam readme: content_type: text/plain file: readme.txt version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_requires_python_False_.yml000066400000000000000000000001021444752763000341250ustar00rootroot00000000000000dynamic: [] name: spam requires-python: '>=3.8' version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_requires_python_True_.yml000066400000000000000000000004111444752763000340150ustar00rootroot00000000000000authors: [] classifiers: [] dependencies: [] description: null dynamic: [] entry-points: {} gui-scripts: {} keywords: [] license: null maintainers: [] name: spam optional-dependencies: {} readme: null requires-python: '>=3.8' scripts: {} urls: {} version: 2020.0.0 test_pep621_class_valid_config_requires_python_complex_False_.yml000066400000000000000000000001221444752763000355770ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_dynamic: [] name: spam requires-python: '!=3.0.*,!=3.2.*,>=2.7' version: 2020.0.0 test_pep621_class_valid_config_requires_python_complex_True_.yml000066400000000000000000000004311444752763000354670ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_authors: [] classifiers: [] dependencies: [] description: null dynamic: [] entry-points: {} gui-scripts: {} keywords: [] license: null maintainers: [] name: spam optional-dependencies: {} readme: null requires-python: '!=3.0.*,!=3.2.*,>=2.7' scripts: {} urls: {} version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_unicode_False_.yml000066400000000000000000000002051444752763000323170ustar00rootroot00000000000000authors: - email: null name: Łukasz Langa description: Factory βΈ» A code generator 🏭 dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_unicode_True_.yml000066400000000000000000000005041444752763000322060ustar00rootroot00000000000000authors: - email: null name: Łukasz Langa classifiers: [] dependencies: [] description: Factory βΈ» A code generator 🏭 dynamic: [] entry-points: {} gui-scripts: {} keywords: [] license: null maintainers: [] name: spam optional-dependencies: {} readme: null requires-python: null scripts: {} urls: {} version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_urls_False_.yml000066400000000000000000000002721444752763000316620ustar00rootroot00000000000000dynamic: [] name: spam urls: changelog: github.com/me/spam/blob/master/CHANGELOG.md documentation: readthedocs.org homepage: example.com repository: github.com version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_class_valid_config_urls_True_.yml000066400000000000000000000006161444752763000315510ustar00rootroot00000000000000authors: [] classifiers: [] dependencies: [] description: null dynamic: [] entry-points: {} gui-scripts: {} keywords: [] license: null maintainers: [] name: spam optional-dependencies: {} readme: null requires-python: null scripts: {} urls: changelog: github.com/me/spam/blob/master/CHANGELOG.md documentation: readthedocs.org homepage: example.com repository: github.com version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_normalize.yml000066400000000000000000000002171444752763000255320ustar00rootroot00000000000000dependencies: - a-b - domdf-python-tools - whey dynamic: [] name: spam optional-dependencies: test: - chemistry-tools - d-e-f - pytest pyproject-parser-0.9.1/tests/test_config_/test_pep621_normalize_keep_dot.yml000066400000000000000000000002171444752763000274040ustar00rootroot00000000000000dependencies: - a.b - domdf-python-tools - whey dynamic: [] name: spam optional-dependencies: test: - chemistry-tools - d.e.f - pytest pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_COMPLETE_A_.yml000066400000000000000000000002611444752763000272370ustar00rootroot00000000000000dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx dynamic: - classifiers - requires-python name: whey version: 2021.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_COMPLETE_B_.yml000066400000000000000000000002611444752763000272400ustar00rootroot00000000000000dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx dynamic: - classifiers - requires-python name: whey version: 2021.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_COMPLETE_PROJECT_A_.yml000066400000000000000000000002241444752763000304240ustar00rootroot00000000000000dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_authors_.yml000066400000000000000000000000511444752763000272510ustar00rootroot00000000000000dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_classifiers_.yml000066400000000000000000000000511444752763000300730ustar00rootroot00000000000000dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_dependencies_.yml000066400000000000000000000002241444752763000302140ustar00rootroot00000000000000dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_description_.yml000066400000000000000000000000511444752763000301070ustar00rootroot00000000000000dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_entry_points_.yml000066400000000000000000000000511444752763000303210ustar00rootroot00000000000000dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_keywords_.yml000066400000000000000000000000511444752763000274330ustar00rootroot00000000000000dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_maintainers_.yml000066400000000000000000000000511444752763000300760ustar00rootroot00000000000000dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_minimal_.yml000066400000000000000000000000511444752763000272120ustar00rootroot00000000000000dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_optional_dependencies_.yml000066400000000000000000000000511444752763000321170ustar00rootroot00000000000000dynamic: [] name: spam version: 2020.0.0 test_pep621_subclass_optional_dependencies_empty_group_.yml000066400000000000000000000000511444752763000344720ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_config_dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_requires_python_.yml000066400000000000000000000000511444752763000310240ustar00rootroot00000000000000dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_requires_python_complex_.yml000066400000000000000000000000511444752763000325530ustar00rootroot00000000000000dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_unicode_.yml000066400000000000000000000000511444752763000272120ustar00rootroot00000000000000dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_subclass_urls_.yml000066400000000000000000000000511444752763000265510ustar00rootroot00000000000000dynamic: [] name: spam version: 2020.0.0 pyproject-parser-0.9.1/tests/test_config_/test_pep621_unnormalized.yml000066400000000000000000000002171444752763000262410ustar00rootroot00000000000000dependencies: - A.b - domdf_python_tools - whey dynamic: [] name: spam optional-dependencies: test: - Pytest - chemistry_tools - d.e.f pyproject-parser-0.9.1/tests/test_dumping.py000066400000000000000000000134141444752763000212670ustar00rootroot00000000000000# 3rd party import pytest from coincidence.regressions import AdvancedFileRegressionFixture from domdf_python_tools.paths import PathPlus from pyproject_examples.example_configs import COMPLETE_A, COMPLETE_A_WITH_FILES, COMPLETE_B, COMPLETE_PROJECT_A # this package from pyproject_parser import PyProject UNORDERED = """\ [project] keywords = [ "pep517", "pep621", "build", "sdist", "wheel", "packaging", "distribution",] version = "2021.0.0" description = "A simple Python wheel builder for simple projects." dynamic = [ "classifiers", "requires-python",] dependencies = [ "httpx", "gidgethub[httpx]>4.0.0", "django>2.1; os_name != 'nt'", "django>2.0; os_name == 'nt'" ] name = "whey" [build-system] requires = [ "whey",] build-backend = "whey" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" [[project.authors]] email = "dominic@davis-foster.co.uk" name = "Dominic Davis-Foster" """ DUMPS_README_TEMPLATE = """\ [build-system] requires = [ "whey",] build-backend = "whey" [project] name = "Whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "pep517", "pep621", "build", "sdist", "wheel", "packaging", "distribution",] dynamic = [ "classifiers", "requires-python",] dependencies = [ "httpx", "gidgethub[httpx]>4.0.0", "django>2.1; os_name != 'nt'", "django>2.0; os_name == 'nt'" ] {readme_block} [[project.authors]] email = "dominic@davis-foster.co.uk" name = "Dominic Davis-Foster" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" """ COMPLETE_UNDERSCORE_NAME = """\ [build-system] requires = [ "whey",] build-backend = "whey" [project] name = "toctree_plus" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "pep517", "pep621", "build", "sdist", "wheel", "packaging", "distribution",] dynamic = [ "classifiers", "requires-python",] dependencies = [ "httpx", "gidgethub[httpx]>4.0.0", "django>2.1; os_name != 'nt'", "django>2.0; os_name == 'nt'" ] [[project.authors]] email = "dominic@davis-foster.co.uk" name = "Dominic Davis-Foster" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" package = "whey" """ @pytest.mark.parametrize( "toml_string", [ pytest.param(COMPLETE_A, id="COMPLETE_A"), pytest.param(COMPLETE_B, id="COMPLETE_B"), pytest.param(COMPLETE_PROJECT_A, id="COMPLETE_PROJECT_A"), ] ) def test_dumps( tmp_pathplus: PathPlus, toml_string: str, advanced_file_regression: AdvancedFileRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_string) config = PyProject.load(filename=tmp_pathplus / "pyproject.toml") config.dump(tmp_pathplus / "pyproject.toml") advanced_file_regression.check_file(tmp_pathplus / "pyproject.toml") advanced_file_regression.check(config.dumps(), extension=".toml") def _param(readme_block: str, **kwargs): # noqa: MAN002 return pytest.param(DUMPS_README_TEMPLATE.format(readme_block=readme_block), **kwargs) @pytest.mark.parametrize( "toml_string", [ _param(readme_block="readme = 'README.rst'", id="string"), _param( readme_block="[project.readme]\ntext = 'This is the README'\ncontent-type = 'text/x-rst'", id="dict_text" ), _param( readme_block="[project.readme]\nfile = 'README.rst'\ncontent-type = 'text/x-rst'", id="dict_file" ), ] ) def test_dumps_readme( tmp_pathplus: PathPlus, toml_string: str, advanced_file_regression: AdvancedFileRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_string) (tmp_pathplus / "README.rst").write_clean("This is the README") config = PyProject.load(filename=tmp_pathplus / "pyproject.toml") config.dump(tmp_pathplus / "pyproject.toml") advanced_file_regression.check_file(tmp_pathplus / "pyproject.toml") advanced_file_regression.check(config.dumps(), extension=".toml") @pytest.mark.parametrize( "toml_string", [ pytest.param(COMPLETE_A, id="COMPLETE_A"), pytest.param(COMPLETE_A_WITH_FILES, id="COMPLETE_A_WITH_FILES"), pytest.param(COMPLETE_B, id="COMPLETE_B"), pytest.param(COMPLETE_PROJECT_A, id="COMPLETE_PROJECT_A"), pytest.param(UNORDERED, id="UNORDERED"), pytest.param(COMPLETE_UNDERSCORE_NAME, id="COMPLETE_UNDERSCORE_NAME"), ] ) def test_reformat( tmp_pathplus: PathPlus, toml_string: str, advanced_file_regression: AdvancedFileRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_string) (tmp_pathplus / "README.rst").write_clean("This is the README") (tmp_pathplus / "LICENSE").write_clean("This is the LICENSE") PyProject.reformat(tmp_pathplus / "pyproject.toml") advanced_file_regression.check_file(tmp_pathplus / "pyproject.toml") # Should be no changes PyProject.reformat(tmp_pathplus / "pyproject.toml") advanced_file_regression.check_file(tmp_pathplus / "pyproject.toml") pyproject-parser-0.9.1/tests/test_dumping_/000077500000000000000000000000001444752763000210515ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_dumping_/test_dumps_COMPLETE_A_.toml000066400000000000000000000017371444752763000260340ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" pyproject-parser-0.9.1/tests/test_dumping_/test_dumps_COMPLETE_B_.toml000066400000000000000000000020401444752763000260210ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" package = "whey" additional-files = [ "include whey/style.css",] pyproject-parser-0.9.1/tests/test_dumping_/test_dumps_COMPLETE_PROJECT_A_.toml000066400000000000000000000017261444752763000272200ustar00rootroot00000000000000[project] name = "spam" version = "2020.0.0" description = "Lovely Spam! Wonderful Spam!" requires-python = ">=3.8" keywords = [ "bacon", "egg", "Lobster Thermidor", "sausage", "tomatoes",] classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [] [[project.authors]] email = "hi@pradyunsg.me" [[project.authors]] name = "Tzu-Ping Chung" [[project.maintainers]] name = "Brett Cannon" email = "brett@python.org" [tool] [project.urls] homepage = "example.com" documentation = "readthedocs.org" repository = "github.com" changelog = "github.com/me/spam/blob/master/CHANGELOG.md" [project.scripts] spam-cli = "spam:main_cli" [project.gui-scripts] spam-gui = "spam:main_gui" [project.optional-dependencies] test = [ "pytest<5.0.0", "pytest-cov[all]",] [project.entry-points."spam.magical"] tomatoes = "spam:main_tomatoes" pyproject-parser-0.9.1/tests/test_dumping_/test_dumps_readme_dict_file_.toml000066400000000000000000000014201444752763000276100ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." readme = "README.rst" keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [tool] [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" pyproject-parser-0.9.1/tests/test_dumping_/test_dumps_readme_dict_text_.toml000066400000000000000000000015041444752763000276600ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [tool] [project.readme] content-type = "text/x-rst" text = "This is the README" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" pyproject-parser-0.9.1/tests/test_dumping_/test_dumps_readme_string_.toml000066400000000000000000000014201444752763000271740ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." readme = "README.rst" keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [tool] [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" pyproject-parser-0.9.1/tests/test_dumping_/test_reformat_COMPLETE_A_.toml000066400000000000000000000017371444752763000265230ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" pyproject-parser-0.9.1/tests/test_dumping_/test_reformat_COMPLETE_A_WITH_FILES_.toml000066400000000000000000000020311444752763000302640ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." readme = "README.rst" keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.license] file = "LICENSE" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" pyproject-parser-0.9.1/tests/test_dumping_/test_reformat_COMPLETE_B_.toml000066400000000000000000000020401444752763000265100ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "Whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" package = "whey" additional-files = [ "include whey/style.css",] pyproject-parser-0.9.1/tests/test_dumping_/test_reformat_COMPLETE_PROJECT_A_.toml000066400000000000000000000017261444752763000277070ustar00rootroot00000000000000[project] name = "spam" version = "2020.0.0" description = "Lovely Spam! Wonderful Spam!" requires-python = ">=3.8" keywords = [ "bacon", "egg", "Lobster Thermidor", "sausage", "tomatoes",] classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [] [[project.authors]] email = "hi@pradyunsg.me" [[project.authors]] name = "Tzu-Ping Chung" [[project.maintainers]] name = "Brett Cannon" email = "brett@python.org" [tool] [project.urls] homepage = "example.com" documentation = "readthedocs.org" repository = "github.com" changelog = "github.com/me/spam/blob/master/CHANGELOG.md" [project.scripts] spam-cli = "spam:main_cli" [project.gui-scripts] spam-gui = "spam:main_gui" [project.optional-dependencies] test = [ "pytest<5.0.0", "pytest-cov[all]",] [project.entry-points."spam.magical"] tomatoes = "spam:main_tomatoes" pyproject-parser-0.9.1/tests/test_dumping_/test_reformat_COMPLETE_UNDERSCORE_NAME_.toml000066400000000000000000000017701444752763000306110ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "toctree_plus" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" package = "whey" pyproject-parser-0.9.1/tests/test_dumping_/test_reformat_UNORDERED_.toml000066400000000000000000000017371444752763000264020ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "whey" version = "2021.0.0" description = "A simple Python wheel builder for simple projects." keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist", "wheel",] dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",] dynamic = [ "classifiers", "requires-python",] [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://whey.readthedocs.io/en/latest" Documentation = "https://whey.readthedocs.io/en/latest" "Issue Tracker" = "https://github.com/repo-helper/whey/issues" "Source Code" = "https://github.com/repo-helper/whey" [tool.whey] base-classifiers = [ "Development Status :: 4 - Beta",] python-versions = [ "3.6", "3.7", "3.8", "3.9", "3.10",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" pyproject-parser-0.9.1/tests/test_pyproject_class.py000066400000000000000000000216231444752763000230310ustar00rootroot00000000000000# stdlib from typing import Type # 3rd party import pytest from coincidence.regressions import AdvancedDataRegressionFixture from dom_toml.parser import AbstractConfigParser, BadConfigError from domdf_python_tools.paths import PathPlus, in_directory from pyproject_examples import ( bad_buildsystem_config, bad_pep621_config, valid_buildsystem_config, valid_pep621_config ) from pyproject_examples.example_configs import COMPLETE_A, COMPLETE_A_WITH_FILES, COMPLETE_B, COMPLETE_PROJECT_A # this package from pyproject_parser import PyProject @pytest.mark.parametrize("toml_config", [*valid_pep621_config, *valid_buildsystem_config]) def test_valid_config( toml_config: str, tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_config) with in_directory(tmp_pathplus): config = PyProject.load(tmp_pathplus / "pyproject.toml") advanced_data_regression.check(config.to_dict()) @pytest.mark.parametrize("toml_config", [*valid_pep621_config, *valid_buildsystem_config]) def test_from_dict(toml_config: str, tmp_pathplus: PathPlus): (tmp_pathplus / "pyproject.toml").write_clean(toml_config) with in_directory(tmp_pathplus): config = PyProject.load(tmp_pathplus / "pyproject.toml") assert PyProject.from_dict(config.to_dict()) == config hyphen_map = config.to_dict() hyphen_map["build-system"] = hyphen_map.pop("build_system") assert PyProject.from_dict(hyphen_map) == config @pytest.mark.parametrize( "toml_config", [ *valid_pep621_config, *valid_buildsystem_config, pytest.param(COMPLETE_A_WITH_FILES, id="COMPLETE_A_WITH_FILES") ] ) def test_valid_config_resolve_files( toml_config: str, tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_config) (tmp_pathplus / "README.rst").write_clean("This is the README") (tmp_pathplus / "LICENSE").write_clean("This is the LICENSE") with in_directory(tmp_pathplus): config = PyProject.load(tmp_pathplus / "pyproject.toml") config.resolve_files() advanced_data_regression.check(config.to_dict()) @pytest.mark.parametrize( "config, expects, match", [ *bad_pep621_config, *bad_buildsystem_config, pytest.param( 'banana = "fruit"\n[project]\nname = "food"', BadConfigError, "Unexpected top-level key 'banana'. Only 'build-system', 'project' and 'tool' are allowed.", id="unexpected_top_level" ), pytest.param( "[coverage]\nomit = 'demo.py'\n[flake8]\nselect = ['F401']", BadConfigError, "Unexpected top-level key 'coverage'. Only 'build-system', 'project' and 'tool' are allowed.", id="top-level", ), pytest.param( "[build_system]\nbackend = 'whey'", BadConfigError, "Unexpected top-level key 'build_system'. Did you mean 'build-system'", id="top_level_typo_underscore", ), pytest.param( "[Build-System]\nbackend = 'whey'", BadConfigError, "Unexpected top-level key 'Build-System'. Did you mean 'build-system'", id="top_level_typo_caps", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[[project.authors]]\nemail = "foo.bar"', BadConfigError, "Invalid email 'foo.bar': The email address is not valid. It must have exactly one @-sign.", id="bad_email", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\noptional-dependencies = 123', TypeError, "Invalid type for 'project.optional-dependencies': expected , got ", id="invalid_optional_dependencies_type_int", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\noptional-dependencies = "123"', TypeError, "Invalid type for 'project.optional-dependencies': expected , got ", id="invalid_optional_dependencies_type_str", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\noptional-dependencies = ["123"]', TypeError, "Invalid type for 'project.optional-dependencies': expected , got ", id="invalid_optional_dependencies_type_list", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\nfoo = 123', TypeError, "Invalid type for 'project.optional-dependencies.foo': expected , got ", id="invalid_optional_dependencies_type_dict_int", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\nfoo = [123]', TypeError, r"Invalid type for 'project.optional-dependencies.foo\[0\]': expected , got ", id="invalid_optional_dependencies_type_dict_list_int", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\nfoo = "123"', TypeError, "Invalid type for 'project.optional-dependencies.foo': expected , got ", id="invalid_optional_dependencies_type_dict_str", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.entry-points.console_scripts]', BadConfigError, "'project.entry-points' may not contain a 'console_scripts' sub-table. Use 'project.scripts' instead.", id="console_scripts_entry_point", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.entry-points.gui_scripts]', BadConfigError, "'project.entry-points' may not contain a 'gui_scripts' sub-table. Use 'project.gui-scripts' instead.", id="gui_scripts_entry_point", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.entry-points.console-scripts]', BadConfigError, "'project.entry-points' may not contain a 'console-scripts' sub-table. Use 'project.scripts' instead.", id="console_scripts_hyphen_entry_point", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.entry-points.gui-scripts]', BadConfigError, "'project.entry-points' may not contain a 'gui-scripts' sub-table. Use 'project.gui-scripts' instead.", id="gui_scripts_hyphen_entry_point", ), # pytest.param( # '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\nwith-hyphen = []', # TypeError, # "Invalid extra name 'with-hyphen'", # id="extra_invalid_a", # ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"quoted?" = []', TypeError, r"Invalid extra name 'quoted\?'", id="extra_invalid_b", ), pytest.param( '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"number#1" = []', TypeError, "Invalid extra name 'number#1'", id="extra_invalid_c", ), # For Part 2 # pytest.param( # '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"dev_test" = []\n"dev-test" = []', # BadConfigError, # "'project.optional-dependencies.dev-test': Multiple extras were defined with the same normalized name of 'dev-test'", # id="duplicate_extra_1", # ), # pytest.param( # '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"dev-test" = []\n"dev_test" = []', # BadConfigError, # "'project.optional-dependencies.dev_test': Multiple extras were defined with the same normalized name of 'dev-test'", # id="duplicate_extra_2", # ), # pytest.param( # '[project]\nname = "foo"\nversion = "1.2.3"\n[project.optional-dependencies]\n"dev.test" = []\n"dev_test" = []', # BadConfigError, # "'project.optional-dependencies.dev_test': Multiple extras were defined with the same normalized name of 'dev-test'", # id="duplicate_extra_3", # ), ] ) def test_bad_config( config: str, expects: Type[Exception], match: str, tmp_pathplus: PathPlus, ): (tmp_pathplus / "pyproject.toml").write_clean(config) with in_directory(tmp_pathplus), pytest.raises(expects, match=match): PyProject.load(tmp_pathplus / "pyproject.toml").resolve_files() class WheyParser(AbstractConfigParser): keys = ["platforms", "package"] class WheyPyProject(PyProject): tool_parsers = {"whey": WheyParser()} @pytest.mark.parametrize( "toml_config", [ pytest.param(COMPLETE_PROJECT_A, id="COMPLETE_PROJECT_A"), pytest.param(COMPLETE_A, id="COMPLETE_A"), pytest.param(COMPLETE_B, id="COMPLETE_B"), ] ) def test_custom_pyproject_class( toml_config: str, tmp_pathplus: PathPlus, advanced_data_regression: AdvancedDataRegressionFixture, ): (tmp_pathplus / "pyproject.toml").write_clean(toml_config) with in_directory(tmp_pathplus): config = WheyPyProject.load(tmp_pathplus / "pyproject.toml") advanced_data_regression.check(config.to_dict()) pyproject-parser-0.9.1/tests/test_pyproject_class_/000077500000000000000000000000001444752763000226125ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_pyproject_class_/test_custom_pyproject_class_COMPLETE_A_.yml000066400000000000000000000014431444752763000330630ustar00rootroot00000000000000build_system: build-backend: whey requires: - whey project: authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 tool: whey: platforms: - Windows - macOS - Linux pyproject-parser-0.9.1/tests/test_pyproject_class_/test_custom_pyproject_class_COMPLETE_B_.yml000066400000000000000000000014651444752763000330700ustar00rootroot00000000000000build_system: build-backend: whey requires: - whey project: authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 tool: whey: package: whey platforms: - Windows - macOS - Linux test_custom_pyproject_class_COMPLETE_PROJECT_A_.yml000066400000000000000000000017241444752763000341740ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_pyproject_class_build_system: null project: authors: - email: hi@pradyunsg.me name: null - email: null name: Tzu-Ping Chung classifiers: - 'Development Status :: 4 - Beta' - 'Programming Language :: Python' dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: Lovely Spam! Wonderful Spam! dynamic: [] entry-points: spam.magical: tomatoes: spam:main_tomatoes gui-scripts: spam-gui: spam:main_gui keywords: - bacon - egg - Lobster Thermidor - sausage - tomatoes maintainers: - email: brett@python.org name: Brett Cannon name: spam optional-dependencies: test: - pytest<5.0.0 - pytest-cov[all] requires-python: '>=3.8' scripts: spam-cli: spam:main_cli urls: changelog: github.com/me/spam/blob/master/CHANGELOG.md documentation: readthedocs.org homepage: example.com repository: github.com version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_COMPLETE_A_.yml000066400000000000000000000017741444752763000307200ustar00rootroot00000000000000build_system: build-backend: whey requires: - whey project: authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 tool: whey: base-classifiers: - 'Development Status :: 4 - Beta' license-key: MIT platforms: - Windows - macOS - Linux python-implementations: - CPython - PyPy python-versions: - '3.6' - '3.7' - '3.8' - '3.9' - '3.10' pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_COMPLETE_B_.yml000066400000000000000000000021011444752763000307020ustar00rootroot00000000000000build_system: build-backend: whey requires: - whey project: authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 tool: whey: additional-files: - include whey/style.css base-classifiers: - 'Development Status :: 4 - Beta' license-key: MIT package: whey platforms: - Windows - macOS - Linux python-implementations: - CPython - PyPy python-versions: - '3.6' - '3.7' - '3.8' - '3.9' - '3.10' pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_COMPLETE_PROJECT_A_.yml000066400000000000000000000017241444752763000321010ustar00rootroot00000000000000build_system: null project: authors: - email: hi@pradyunsg.me name: null - email: null name: Tzu-Ping Chung classifiers: - 'Development Status :: 4 - Beta' - 'Programming Language :: Python' dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: Lovely Spam! Wonderful Spam! dynamic: [] entry-points: spam.magical: tomatoes: spam:main_tomatoes gui-scripts: spam-gui: spam:main_gui keywords: - bacon - egg - Lobster Thermidor - sausage - tomatoes maintainers: - email: brett@python.org name: Brett Cannon name: spam optional-dependencies: test: - pytest<5.0.0 - pytest-cov[all] requires-python: '>=3.8' scripts: spam-cli: spam:main_cli urls: changelog: github.com/me/spam/blob/master/CHANGELOG.md documentation: readthedocs.org homepage: example.com repository: github.com version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_authors_.yml000066400000000000000000000002621444752763000307240ustar00rootroot00000000000000build_system: null project: authors: - email: hi@pradyunsg.me name: null - email: null name: Tzu-Ping Chung dynamic: [] name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_backend_path_.yml000066400000000000000000000001531444752763000316410ustar00rootroot00000000000000build_system: backend-path: - ../foo build-backend: whey requires: - whey project: null tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_backend_paths_.yml000066400000000000000000000001651444752763000320270ustar00rootroot00000000000000build_system: backend-path: - ../foo - ./bar build-backend: whey requires: - whey project: null tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_classifiers_.yml000066400000000000000000000002551444752763000315500ustar00rootroot00000000000000build_system: null project: classifiers: - 'Development Status :: 4 - Beta' - 'Programming Language :: Python' dynamic: [] name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_complete_.yml000066400000000000000000000001201444752763000310400ustar00rootroot00000000000000build_system: build-backend: whey requires: - whey project: null tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_dependencies_.yml000066400000000000000000000003111444752763000316600ustar00rootroot00000000000000build_system: null project: dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx dynamic: [] name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_description_.yml000066400000000000000000000002001444752763000315520ustar00rootroot00000000000000build_system: null project: description: Lovely Spam! Wonderful Spam! dynamic: [] name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_entry_points_.yml000066400000000000000000000004471444752763000320010ustar00rootroot00000000000000build_system: null project: dynamic: [] entry-points: flake8.extension: SXL: flake8_sphinx_links:Plugin spam.magical: tomatoes: spam:main_tomatoes gui-scripts: spam-gui: spam:main_gui name: spam scripts: spam-cli: spam:main_cli version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_keywords_.yml000066400000000000000000000002411444752763000311030ustar00rootroot00000000000000build_system: null project: dynamic: [] keywords: - bacon - egg - Lobster Thermidor - sausage - tomatoes name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_maintainers_.yml000066400000000000000000000002261444752763000315510ustar00rootroot00000000000000build_system: null project: dynamic: [] maintainers: - email: brett@python.org name: Brett Cannon name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_minimal_.yml000066400000000000000000000001241444752763000306620ustar00rootroot00000000000000build_system: null project: dynamic: [] name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_optional_dependencies_.yml000066400000000000000000000003611444752763000335720ustar00rootroot00000000000000build_system: null project: dynamic: [] name: spam optional-dependencies: test: - matplotlib>=3.0.0; platform_machine != "aarch64" or python_version > "3.6" - pytest<5.0.0 - pytest-cov[all] version: 2020.0.0 tool: {} test_valid_config_optional_dependencies_empty_group_.yml000066400000000000000000000003761444752763000361530ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_pyproject_class_build_system: null project: dynamic: [] name: spam optional-dependencies: docs: [] test: - matplotlib>=3.0.0; platform_machine != "aarch64" or python_version > "3.6" - pytest<5.0.0 - pytest-cov[all] version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_requires_nothing_.yml000066400000000000000000000000641444752763000326240ustar00rootroot00000000000000build_system: requires: [] project: null tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_requires_python_.yml000066400000000000000000000001571444752763000325020ustar00rootroot00000000000000build_system: null project: dynamic: [] name: spam requires-python: '>=3.8' version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_requires_python_complex_.yml000066400000000000000000000001771444752763000342330ustar00rootroot00000000000000build_system: null project: dynamic: [] name: spam requires-python: '!=3.0.*,!=3.2.*,>=2.7' version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_requires_setuptools_.yml000066400000000000000000000001121444752763000333710ustar00rootroot00000000000000build_system: requires: - setuptools - wheel project: null tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_requires_whey_.yml000066400000000000000000000000721444752763000321310ustar00rootroot00000000000000build_system: requires: - whey project: null tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_COMPLETE_A_.yml000066400000000000000000000017741444752763000336410ustar00rootroot00000000000000build_system: build-backend: whey requires: - whey project: authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 tool: whey: base-classifiers: - 'Development Status :: 4 - Beta' license-key: MIT platforms: - Windows - macOS - Linux python-implementations: - CPython - PyPy python-versions: - '3.6' - '3.7' - '3.8' - '3.9' - '3.10' test_valid_config_resolve_files_COMPLETE_A_WITH_FILES_.yml000066400000000000000000000022441444752763000353300ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_pyproject_class_build_system: build-backend: whey requires: - whey project: authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel license: file: LICENSE text: 'This is the LICENSE ' name: whey readme: content_type: text/x-rst file: README.rst text: 'This is the README ' urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 tool: whey: base-classifiers: - 'Development Status :: 4 - Beta' license-key: MIT platforms: - Windows - macOS - Linux python-implementations: - CPython - PyPy python-versions: - '3.6' - '3.7' - '3.8' - '3.9' - '3.10' pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_COMPLETE_B_.yml000066400000000000000000000021011444752763000336230ustar00rootroot00000000000000build_system: build-backend: whey requires: - whey project: authors: - email: dominic@davis-foster.co.uk name: Dominic Davis-Foster dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: A simple Python wheel builder for simple projects. dynamic: - classifiers - requires-python keywords: - build - distribution - packaging - pep517 - pep621 - sdist - wheel name: whey urls: Documentation: https://whey.readthedocs.io/en/latest Homepage: https://whey.readthedocs.io/en/latest Issue Tracker: https://github.com/repo-helper/whey/issues Source Code: https://github.com/repo-helper/whey version: 2021.0.0 tool: whey: additional-files: - include whey/style.css base-classifiers: - 'Development Status :: 4 - Beta' license-key: MIT package: whey platforms: - Windows - macOS - Linux python-implementations: - CPython - PyPy python-versions: - '3.6' - '3.7' - '3.8' - '3.9' - '3.10' test_valid_config_resolve_files_COMPLETE_PROJECT_A_.yml000066400000000000000000000017241444752763000347430ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_pyproject_class_build_system: null project: authors: - email: hi@pradyunsg.me name: null - email: null name: Tzu-Ping Chung classifiers: - 'Development Status :: 4 - Beta' - 'Programming Language :: Python' dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx description: Lovely Spam! Wonderful Spam! dynamic: [] entry-points: spam.magical: tomatoes: spam:main_tomatoes gui-scripts: spam-gui: spam:main_gui keywords: - bacon - egg - Lobster Thermidor - sausage - tomatoes maintainers: - email: brett@python.org name: Brett Cannon name: spam optional-dependencies: test: - pytest<5.0.0 - pytest-cov[all] requires-python: '>=3.8' scripts: spam-cli: spam:main_cli urls: changelog: github.com/me/spam/blob/master/CHANGELOG.md documentation: readthedocs.org homepage: example.com repository: github.com version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_authors_.yml000066400000000000000000000002621444752763000336450ustar00rootroot00000000000000build_system: null project: authors: - email: hi@pradyunsg.me name: null - email: null name: Tzu-Ping Chung dynamic: [] name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_backend_path_.yml000066400000000000000000000001531444752763000345620ustar00rootroot00000000000000build_system: backend-path: - ../foo build-backend: whey requires: - whey project: null tool: {} test_valid_config_resolve_files_backend_paths_.yml000066400000000000000000000001651444752763000346710ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_pyproject_class_build_system: backend-path: - ../foo - ./bar build-backend: whey requires: - whey project: null tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_classifiers_.yml000066400000000000000000000002551444752763000344710ustar00rootroot00000000000000build_system: null project: classifiers: - 'Development Status :: 4 - Beta' - 'Programming Language :: Python' dynamic: [] name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_complete_.yml000066400000000000000000000001201444752763000337610ustar00rootroot00000000000000build_system: build-backend: whey requires: - whey project: null tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_dependencies_.yml000066400000000000000000000003111444752763000346010ustar00rootroot00000000000000build_system: null project: dependencies: - django>2.1; os_name != "nt" - django>2.0; os_name == "nt" - gidgethub[httpx]>4.0.0 - httpx dynamic: [] name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_description_.yml000066400000000000000000000002001444752763000344730ustar00rootroot00000000000000build_system: null project: description: Lovely Spam! Wonderful Spam! dynamic: [] name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_entry_points_.yml000066400000000000000000000004471444752763000347220ustar00rootroot00000000000000build_system: null project: dynamic: [] entry-points: flake8.extension: SXL: flake8_sphinx_links:Plugin spam.magical: tomatoes: spam:main_tomatoes gui-scripts: spam-gui: spam:main_gui name: spam scripts: spam-cli: spam:main_cli version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_keywords_.yml000066400000000000000000000002411444752763000340240ustar00rootroot00000000000000build_system: null project: dynamic: [] keywords: - bacon - egg - Lobster Thermidor - sausage - tomatoes name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_maintainers_.yml000066400000000000000000000002261444752763000344720ustar00rootroot00000000000000build_system: null project: dynamic: [] maintainers: - email: brett@python.org name: Brett Cannon name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_minimal_.yml000066400000000000000000000001241444752763000336030ustar00rootroot00000000000000build_system: null project: dynamic: [] name: spam version: 2020.0.0 tool: {} test_valid_config_resolve_files_optional_dependencies_.yml000066400000000000000000000003611444752763000364340ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_pyproject_class_build_system: null project: dynamic: [] name: spam optional-dependencies: test: - matplotlib>=3.0.0; platform_machine != "aarch64" or python_version > "3.6" - pytest<5.0.0 - pytest-cov[all] version: 2020.0.0 tool: {} test_valid_config_resolve_files_optional_dependencies_empty_group_.yml000066400000000000000000000003761444752763000410740ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_pyproject_class_build_system: null project: dynamic: [] name: spam optional-dependencies: docs: [] test: - matplotlib>=3.0.0; platform_machine != "aarch64" or python_version > "3.6" - pytest<5.0.0 - pytest-cov[all] version: 2020.0.0 tool: {} test_valid_config_resolve_files_requires_nothing_.yml000066400000000000000000000000641444752763000354660ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_pyproject_class_build_system: requires: [] project: null tool: {} test_valid_config_resolve_files_requires_python_.yml000066400000000000000000000001571444752763000353440ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_pyproject_class_build_system: null project: dynamic: [] name: spam requires-python: '>=3.8' version: 2020.0.0 tool: {} test_valid_config_resolve_files_requires_python_complex_.yml000066400000000000000000000001771444752763000370750ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_pyproject_class_build_system: null project: dynamic: [] name: spam requires-python: '!=3.0.*,!=3.2.*,>=2.7' version: 2020.0.0 tool: {} test_valid_config_resolve_files_requires_setuptools_.yml000066400000000000000000000001121444752763000362330ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_pyproject_class_build_system: requires: - setuptools - wheel project: null tool: {} test_valid_config_resolve_files_requires_whey_.yml000066400000000000000000000000721444752763000347730ustar00rootroot00000000000000pyproject-parser-0.9.1/tests/test_pyproject_class_build_system: requires: - whey project: null tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_unicode_.yml000066400000000000000000000002701444752763000336050ustar00rootroot00000000000000build_system: null project: authors: - email: null name: Łukasz Langa description: Factory βΈ» A code generator 🏭 dynamic: [] name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_resolve_files_urls_.yml000066400000000000000000000003571444752763000331520ustar00rootroot00000000000000build_system: null project: dynamic: [] name: spam urls: changelog: github.com/me/spam/blob/master/CHANGELOG.md documentation: readthedocs.org homepage: example.com repository: github.com version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_unicode_.yml000066400000000000000000000002701444752763000306640ustar00rootroot00000000000000build_system: null project: authors: - email: null name: Łukasz Langa description: Factory βΈ» A code generator 🏭 dynamic: [] name: spam version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_pyproject_class_/test_valid_config_urls_.yml000066400000000000000000000003571444752763000302310ustar00rootroot00000000000000build_system: null project: dynamic: [] name: spam urls: changelog: github.com/me/spam/blob/master/CHANGELOG.md documentation: readthedocs.org homepage: example.com repository: github.com version: 2020.0.0 tool: {} pyproject-parser-0.9.1/tests/test_utils.py000066400000000000000000000035031444752763000207620ustar00rootroot00000000000000# stdlib import pathlib from typing import Union # 3rd party import pytest from coincidence.regressions import AdvancedDataRegressionFixture from dom_toml.parser import BadConfigError # this package from pyproject_parser.utils import content_type_from_filename, render_rst @pytest.mark.parametrize( "filename, expected", [ ("foo.md", "text/markdown"), ("foo/bar.md", "text/markdown"), ("foo.rst", "text/x-rst"), ("foo/bar.rst", "text/x-rst"), ("foo.txt", "text/plain"), ("foo/bar.txt", "text/plain"), (pathlib.Path("foo.md"), "text/markdown"), (pathlib.Path("foo/bar.md"), "text/markdown"), (pathlib.Path("foo.rst"), "text/x-rst"), (pathlib.Path("foo/bar.rst"), "text/x-rst"), (pathlib.Path("foo.txt"), "text/plain"), (pathlib.Path("foo/bar.txt"), "text/plain"), ] ) def test_content_type_from_filename(filename: Union[str, pathlib.Path], expected: str): assert content_type_from_filename(filename) == expected def test_render_rst_error(capsys, advanced_data_regression: AdvancedDataRegressionFixture): pytest.importorskip("readme_renderer") with pytest.raises(BadConfigError, match="Error rendering README."): render_rst(".. seealso::", "README.rst") # A sphinx directive outerr = capsys.readouterr() assert outerr.err == 'README.rst:1: (ERROR/3) Unknown directive type "seealso".\n\n.. seealso::\n' assert outerr.out == '' def test_render_rst_error_filename(capsys, advanced_data_regression: AdvancedDataRegressionFixture): pytest.importorskip("readme_renderer") with pytest.raises(BadConfigError, match="Error rendering README."): render_rst(".. seealso::", "Different_filename.rst") # A sphinx directive outerr = capsys.readouterr() assert outerr.err == 'Different_filename.rst:1: (ERROR/3) Unknown directive type "seealso".\n\n.. seealso::\n' assert outerr.out == '' pyproject-parser-0.9.1/tox.ini000066400000000000000000000154251444752763000163700ustar00rootroot00000000000000# This file is managed by 'repo_helper'. # You may add new sections, but any changes made to the following sections will be lost: # * tox # * envlists # * testenv # * testenv:.package # * testenv:py312-dev # * testenv:docs # * testenv:build # * testenv:lint # * testenv:perflint # * testenv:mypy # * testenv:pyup # * testenv:coverage # * flake8 # * coverage:run # * check-wheel-contents # * pytest [tox] envlist = py36 py37 py38 py39 py310 py311 py312-dev pypy36 pypy37 pypy38 pypy39 mypy build skip_missing_interpreters = True isolated_build = True requires = pip>=21,!=22.2 tox-envlist>=0.2.1 tox~=3.0 virtualenv!=20.16.0 [envlists] test = py36 py37 py38 py39 py310 py311 py312-dev pypy36 pypy37 pypy38 pypy39 qa = mypy, lint cov = py36, coverage [testenv] setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 SETUPTOOLS_USE_DISTUTILS=stdlib deps = -r{toxinidir}/tests/requirements.txt extras = readme,cli commands = python --version python -m pytest --cov=pyproject_parser -r aR tests/ {posargs} [testenv:.package] setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 [testenv:py312-dev] setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 [testenv:docs] setenv = SHOW_TODOS = 1 passenv = SPHINX_BUILDER basepython = python3.8 changedir = {toxinidir}/doc-source extras = readme,cli deps = -r{toxinidir}/doc-source/requirements.txt commands = sphinx-build -M {env:SPHINX_BUILDER:html} . ./build {posargs} [testenv:build] setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 skip_install = True changedir = {toxinidir} deps = build[virtualenv]>=0.3.1 check-wheel-contents>=0.1.0 twine>=3.2.0 cryptography<40; implementation_name == "pypy" and python_version <= "3.7" commands = python -m build --sdist --wheel "{toxinidir}" twine check dist/*.tar.gz dist/*.whl check-wheel-contents dist/ [testenv:lint] basepython = python3.6 changedir = {toxinidir} ignore_errors = True skip_install = True deps = flake8>=3.8.2,<5 flake8-2020>=1.6.0 flake8-builtins>=1.5.3 flake8-docstrings>=1.5.0 flake8-dunder-all>=0.1.1 flake8-encodings>=0.1.0 flake8-github-actions>=0.1.0 flake8-noqa>=1.1.0,<=1.2.2 flake8-pyi>=20.10.0,<=22.8.0 flake8-pytest-style>=1.3.0 flake8-quotes>=3.3.0 flake8-slots>=0.1.0 flake8-sphinx-links>=0.0.4 flake8-strftime>=0.1.1 flake8-typing-imports>=1.10.0 git+https://github.com/domdfcoding/flake8-rst-docstrings-sphinx.git git+https://github.com/domdfcoding/flake8-rst-docstrings.git git+https://github.com/python-formate/flake8-unused-arguments.git@magic-methods git+https://github.com/python-formate/flake8-missing-annotations.git pydocstyle>=6.0.0 pygments>=2.7.1 importlib_metadata<4.5.0; python_version<'3.8' commands = python3 -m flake8_rst_docstrings_sphinx pyproject_parser tests --allow-toolbox {posargs} [testenv:perflint] basepython = python3.6 changedir = {toxinidir} ignore_errors = True skip_install = True deps = perflint commands = python3 -m perflint pyproject_parser {posargs} [testenv:mypy] basepython = python3.6 ignore_errors = True changedir = {toxinidir} extras = readme,cli deps = mypy==0.971 -r{toxinidir}/tests/requirements.txt -r{toxinidir}/stubs.txt commands = mypy pyproject_parser tests {posargs} [testenv:pyup] basepython = python3.6 skip_install = True ignore_errors = True changedir = {toxinidir} deps = pyupgrade-directories extras = readme,cli commands = pyup_dirs pyproject_parser tests --py36-plus --recursive [testenv:coverage] basepython = python3.6 skip_install = True ignore_errors = True whitelist_externals = /bin/bash passenv = COV_PYTHON_VERSION COV_PLATFORM COV_PYTHON_IMPLEMENTATION * changedir = {toxinidir} deps = coverage>=5 coverage_pyver_pragma>=0.2.1 commands = /bin/bash -c "rm -rf htmlcov" coverage html /bin/bash -c "DISPLAY=:0 firefox 'htmlcov/index.html'" [flake8] max-line-length = 120 select = E111 E112 E113 E121 E122 E125 E127 E128 E129 E131 E133 E201 E202 E203 E211 E222 E223 E224 E225 E225 E226 E227 E228 E231 E241 E242 E251 E261 E262 E265 E271 E272 E303 E304 E306 E402 E502 E703 E711 E712 E713 E714 E721 W291 W292 W293 W391 W504 YTT101 YTT102 YTT103 YTT201 YTT202 YTT203 YTT204 YTT301 YTT302 YTT303 STRFTIME001 STRFTIME002 SXL001 PT001 PT002 PT003 PT006 PT007 PT008 PT009 PT010 PT011 PT012 PT013 PT014 PT015 PT016 PT017 PT018 PT019 PT020 PT021 RST201 RST202 RST203 RST204 RST205 RST206 RST207 RST208 RST210 RST211 RST212 RST213 RST214 RST215 RST216 RST217 RST218 RST219 RST299 RST301 RST302 RST303 RST304 RST305 RST306 RST399 RST401 RST499 RST900 RST901 RST902 RST903 Q001 Q002 Q003 A001 A002 A003 TYP001 TYP002 TYP003 TYP004 TYP005 TYP006 ENC001 ENC002 ENC003 ENC004 ENC011 ENC012 ENC021 ENC022 ENC023 ENC024 ENC025 ENC026 Y001,Y002 Y003 Y004 Y005 Y006 Y007 Y008 Y009 Y010 Y011 Y012 Y013 Y014 Y015 Y090 Y091 NQA001 NQA002 NQA003 NQA004 NQA005 NQA102 NQA103 E301 E302 E305 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 SLOT000 SLOT001 SLOT002 extend-exclude = doc-source,old,build,dist,__pkginfo__.py,setup.py,venv rst-directives = TODO autosummary-widths envvar extras-require license license-info space rst-roles = choosealicense core-meta pep621 toml per-file-ignores = tests/*: D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 SLOT000 SLOT001 SLOT002 */*.pyi: E301 E302 E305 D100 D101 D102 D103 D104 D106 D201 D204 D207 D208 D209 D210 D211 D212 D213 D214 D215 D300 D301 D400 D402 D403 D404 D415 D417 DALL000 SLOT000 SLOT001 SLOT002 pytest-parametrize-names-type = csv inline-quotes = " multiline-quotes = """ docstring-quotes = """ count = True min_python_version = 3.6.1 unused-arguments-ignore-abstract-functions = True unused-arguments-ignore-overload-functions = True unused-arguments-ignore-magic-methods = True unused-arguments-ignore-variadic-names = True [coverage:run] plugins = coverage_pyver_pragma [check-wheel-contents] ignore = W002 toplevel = pyproject_parser package = pyproject_parser [pytest] addopts = --color yes --durations 25 timeout = 300 filterwarnings = error ignore:can't resolve package from __spec__ or __package__, falling back on __name__ and __path__:ImportWarning always:pkg_resources is deprecated as an API:DeprecationWarning [coverage:report] fail_under = 97.5 exclude_lines = raise AssertionError raise NotImplementedError if 0: if False: if TYPE_CHECKING: if typing.TYPE_CHECKING: if __name__ == .__main__.: omit = .tox/**/*.py