pax_global_header00006660000000000000000000000064147575736030014533gustar00rootroot0000000000000052 comment=f618c47c05a8b15425e8709160c937923c95d9c6 sphinx-toolbox-3.9.0/000077500000000000000000000000001475757360300145415ustar00rootroot00000000000000sphinx-toolbox-3.9.0/.bumpversion.cfg000066400000000000000000000015251475757360300176540ustar00rootroot00000000000000[bumpversion] current_version = 3.9.0 commit = True tag = True [bumpversion:file:repo_helper.yml] search = version: '{current_version}' replace = version: '{new_version}' [bumpversion:file:README.rst] [bumpversion:file:doc-source/index.rst] [bumpversion:file:sphinx_toolbox/__init__.py] search = : str = "{current_version}" replace = : str = "{new_version}" [bumpversion:file:sphinx_toolbox/issues.py] search = current_version="{current_version}", replace = current_version="{new_version}", [bumpversion:file:sphinx_toolbox/utils.py] search = current_version="{current_version}", replace = current_version="{new_version}", [bumpversion:file:pyproject.toml] 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 sphinx-toolbox-3.9.0/.github/000077500000000000000000000000001475757360300161015ustar00rootroot00000000000000sphinx-toolbox-3.9.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001475757360300202645ustar00rootroot00000000000000sphinx-toolbox-3.9.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000022371475757360300227620ustar00rootroot00000000000000--- 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: * sphinx-toolbox: ## Installation source ## Other Additional Information: sphinx-toolbox-3.9.0/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000012221475757360300240060ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project labels: "enhancement" assignees: domdfcoding --- ## Description ## Version * Operating System: * Python: * sphinx-toolbox: ## Other Additional Information: sphinx-toolbox-3.9.0/.github/auto_assign.yml000066400000000000000000000003471475757360300211440ustar00rootroot00000000000000# 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 sphinx-toolbox-3.9.0/.github/dependabot.yml000066400000000000000000000003351475757360300207320ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. --- version: 2 updates: - package-ecosystem: pip directory: / schedule: interval: weekly open-pull-requests-limit: 0 reviewers: - domdfcoding sphinx-toolbox-3.9.0/.github/milestones.py000077500000000000000000000012401475757360300206350ustar00rootroot00000000000000#!/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")) sphinx-toolbox-3.9.0/.github/stale.yml000066400000000000000000000040211475757360300177310ustar00rootroot00000000000000# 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 sphinx-toolbox-3.9.0/.github/workflows/000077500000000000000000000000001475757360300201365ustar00rootroot00000000000000sphinx-toolbox-3.9.0/.github/workflows/conda_ci.yml000066400000000000000000000037311475757360300224240ustar00rootroot00000000000000# 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@v4" - name: Setup Python 🐍 uses: "actions/setup-python@v5" with: python-version: "3.11" - name: Setup Conda uses: conda-incubator/setup-miniconda@v2.1.1 with: activate-environment: env conda-build-version: 3.28.4 miniconda-version: py311_24.1.2-0 python-version: "3.11" 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" "whey" # $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 sphinx-toolbox $CONDA/bin/conda search -c file://$(pwd)/conda-bld --override-channels sphinx-toolbox - name: "Install package" run: | $CONDA/bin/conda install -c file://$(pwd)/conda-bld sphinx-toolbox=3.9.0=py_1 -y || exit 1 - name: "Run Tests" run: | rm -rf sphinx_toolbox $CONDA/bin/conda install pytest coincidence || exit 1 pip install -r tests/requirements.txt pytest tests/ sphinx-toolbox-3.9.0/.github/workflows/docs_test_action.yml000066400000000000000000000015501475757360300242060ustar00rootroot00000000000000# 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@v4" - 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 -- -W " sphinx-toolbox-3.9.0/.github/workflows/flake8.yml000066400000000000000000000023361475757360300220370ustar00rootroot00000000000000# 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@v4" - 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@v5" with: python-version: "3.8" - 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" sphinx-toolbox-3.9.0/.github/workflows/mypy.yml000066400000000000000000000024501475757360300216600ustar00rootroot00000000000000# 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-22.04', 'windows-2019'] fail-fast: false steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v4" - 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@v5" with: python-version: "3.8" - 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" sphinx-toolbox-3.9.0/.github/workflows/octocheese.yml000066400000000000000000000006211475757360300230010ustar00rootroot00000000000000# 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: "sphinx-toolbox" env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} if: startsWith(github.ref, 'refs/tags/') != true sphinx-toolbox-3.9.0/.github/workflows/python_ci.yml000066400000000000000000000071211475757360300226560ustar00rootroot00000000000000# 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.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.7,pypy-3.8,pypy-3.9' strategy: fail-fast: False matrix: config: - {python-version: "3.7", testenvs: "py37-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3},build", experimental: False} - {python-version: "3.8", testenvs: "py38-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1},build", experimental: False} - {python-version: "3.9", testenvs: "py39-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4},build", experimental: False} - {python-version: "3.10", testenvs: "py310-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1},build", experimental: False} - {python-version: "3.11", testenvs: "py311-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2},build", experimental: False} - {python-version: "3.12", testenvs: "py312-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2},build", experimental: False} - {python-version: "3.13", testenvs: "py313-sphinx{6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2},build", experimental: False} - {python-version: "pypy-3.7", testenvs: "pypy37-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3},build", experimental: False} - {python-version: "pypy-3.8", testenvs: "pypy38-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1},build", experimental: False} - {python-version: "pypy-3.9-v7.3.15", testenvs: "pypy39-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4},build", experimental: True} steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v4" - 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@v5" 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@v4 if: ${{ always() && steps.setup-python.outcome == 'success' }} with: name: "coverage-${{ matrix.config.python-version }}" path: .coverage sphinx-toolbox-3.9.0/.github/workflows/python_ci_linux.yml000066400000000000000000000205511475757360300240770ustar00rootroot00000000000000# 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-22.04 / Python ${{ matrix.config.python-version }}" runs-on: "ubuntu-22.04" continue-on-error: ${{ matrix.config.experimental }} env: USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.7,pypy-3.8,pypy-3.9' strategy: fail-fast: False matrix: config: - {python-version: "3.7", testenvs: "py37-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3},build", experimental: False} - {python-version: "3.8", testenvs: "py38-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1},build", experimental: False} - {python-version: "3.9", testenvs: "py39-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4},build", experimental: False} - {python-version: "3.10", testenvs: "py310-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1},build", experimental: False} - {python-version: "3.11", testenvs: "py311-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2},build", experimental: False} - {python-version: "3.12", testenvs: "py312-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2},build", experimental: False} - {python-version: "3.13", testenvs: "py313-sphinx{6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2},build", experimental: False} - {python-version: "pypy-3.7", testenvs: "pypy37-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3},build", experimental: False} - {python-version: "pypy-3.8", testenvs: "pypy38-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1},build", experimental: False} - {python-version: "pypy-3.9", testenvs: "pypy39-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4},build", experimental: True} steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v4" - 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@v5" 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@v4 if: ${{ always() && steps.setup-python.outcome == 'success' }} with: name: "coverage-${{ matrix.config.python-version }}" path: .coverage Coverage: needs: tests runs-on: "ubuntu-22.04" steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v4" - name: Setup Python 🐍 uses: "actions/setup-python@v5" 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@v4 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@v4 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-22.04" steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v4" if: startsWith(github.ref, 'refs/tags/') - name: Setup Python 🐍 uses: "actions/setup-python@v5" 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@v4" - name: Setup Python 🐍 uses: "actions/setup-python@v5" with: python-version: 3.11 - name: Setup Conda uses: conda-incubator/setup-miniconda@v2.1.1 with: activate-environment: env conda-build-version: 3.28.4 miniconda-version: py311_24.1.2-0 python-version: "3.11" 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" "whey" # $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/sphinx-toolbox-*.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 }} sphinx-toolbox-3.9.0/.github/workflows/python_ci_macos.yml000066400000000000000000000070771475757360300240520ustar00rootroot00000000000000# 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-13 / Python ${{ matrix.config.python-version }}" runs-on: "macos-13" continue-on-error: ${{ matrix.config.experimental }} env: USING_COVERAGE: '3.7,3.8,3.9,3.10,3.11,3.12,3.13,pypy-3.7,pypy-3.8,pypy-3.9' strategy: fail-fast: False matrix: config: - {python-version: "3.7", testenvs: "py37-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3},build", experimental: False} - {python-version: "3.8", testenvs: "py38-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1},build", experimental: False} - {python-version: "3.9", testenvs: "py39-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4},build", experimental: False} - {python-version: "3.10", testenvs: "py310-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1},build", experimental: False} - {python-version: "3.11", testenvs: "py311-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2},build", experimental: False} - {python-version: "3.12", testenvs: "py312-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2},build", experimental: False} - {python-version: "3.13", testenvs: "py313-sphinx{6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2},build", experimental: False} - {python-version: "pypy-3.7", testenvs: "pypy37-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3},build", experimental: False} - {python-version: "pypy-3.8", testenvs: "pypy38-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1},build", experimental: False} - {python-version: "pypy-3.9", testenvs: "pypy39-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4},build", experimental: True} steps: - name: Checkout πŸ›ŽοΈ uses: "actions/checkout@v4" - 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@v5" 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@v4 if: ${{ always() && steps.setup-python.outcome == 'success' }} with: name: "coverage-${{ matrix.config.python-version }}" path: .coverage sphinx-toolbox-3.9.0/.gitignore000066400000000000000000000020431475757360300165300ustar00rootroot00000000000000# 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/ wip-shields-block.py sphinx-toolbox-3.9.0/.imgbotconfig000066400000000000000000000001151475757360300172060ustar00rootroot00000000000000{ "schedule": "weekly", "ignoredFiles": [ "**/*.svg" ] } sphinx-toolbox-3.9.0/.pre-commit-config.yaml000066400000000000000000000042701475757360300210250ustar00rootroot00000000000000# 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.11.1 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/python-formate/flake8-dunder-all rev: v0.4.1 hooks: - id: ensure-dunder-all files: ^sphinx_toolbox/.*\.py$ - repo: https://github.com/domdfcoding/flake2lint rev: v0.4.3 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.5 hooks: - id: snippet-fmt - repo: https://github.com/python-formate/formate rev: v0.8.0 hooks: - id: formate exclude: ^(doc-source/conf|__pkginfo__|setup)\.(_)?py$ - repo: https://github.com/python-coincidence/dep_checker rev: v0.8.0 hooks: - id: dep_checker args: - sphinx_toolbox # Custom hooks can be added below this comment sphinx-toolbox-3.9.0/.pylintrc000066400000000000000000000346201475757360300164130ustar00rootroot00000000000000[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 sphinx-toolbox-3.9.0/.readthedocs.yml000066400000000000000000000012701475757360300176270ustar00rootroot00000000000000# 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 - requirements: doc-source/rtd-extra-deps.txt build: os: ubuntu-20.04 tools: python: '3.9' jobs: post_create_environment: - pip install .[all] post_install: - pip install sphinxcontrib-applehelp==1.0.4 sphinxcontrib-devhelp==1.0.2 sphinxcontrib-htmlhelp==2.0.1 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.3 sphinxcontrib-serializinghtml==1.1.5 sphinx-toolbox-3.9.0/.style.yapf000066400000000000000000000272001475757360300166410ustar00rootroot00000000000000[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 sphinx-toolbox-3.9.0/CONTRIBUTING.rst000066400000000000000000000025071475757360300172060ustar00rootroot00000000000000============== Contributing ============== .. This file based on https://github.com/PyGithub/PyGithub/blob/master/CONTRIBUTING.md ``sphinx-toolbox`` 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 sphinx-toolbox-3.9.0/LICENSE000066400000000000000000000020551475757360300155500ustar00rootroot00000000000000Copyright (c) 2020-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. sphinx-toolbox-3.9.0/README.rst000066400000000000000000000124561475757360300162400ustar00rootroot00000000000000############### sphinx-toolbox ############### .. start short_desc **Box of handy tools for Sphinx 🧰 πŸ“”** .. 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/sphinx-toolbox/latest?logo=read-the-docs :target: https://sphinx-toolbox.readthedocs.io/en/latest :alt: Documentation Build Status .. |docs_check| image:: https://github.com/sphinx-toolbox/sphinx-toolbox/workflows/Docs%20Check/badge.svg :target: https://github.com/sphinx-toolbox/sphinx-toolbox/actions?query=workflow%3A%22Docs+Check%22 :alt: Docs Check Status .. |actions_linux| image:: https://github.com/sphinx-toolbox/sphinx-toolbox/workflows/Linux/badge.svg :target: https://github.com/sphinx-toolbox/sphinx-toolbox/actions?query=workflow%3A%22Linux%22 :alt: Linux Test Status .. |actions_windows| image:: https://github.com/sphinx-toolbox/sphinx-toolbox/workflows/Windows/badge.svg :target: https://github.com/sphinx-toolbox/sphinx-toolbox/actions?query=workflow%3A%22Windows%22 :alt: Windows Test Status .. |actions_macos| image:: https://github.com/sphinx-toolbox/sphinx-toolbox/workflows/macOS/badge.svg :target: https://github.com/sphinx-toolbox/sphinx-toolbox/actions?query=workflow%3A%22macOS%22 :alt: macOS Test Status .. |actions_flake8| image:: https://github.com/sphinx-toolbox/sphinx-toolbox/workflows/Flake8/badge.svg :target: https://github.com/sphinx-toolbox/sphinx-toolbox/actions?query=workflow%3A%22Flake8%22 :alt: Flake8 Status .. |actions_mypy| image:: https://github.com/sphinx-toolbox/sphinx-toolbox/workflows/mypy/badge.svg :target: https://github.com/sphinx-toolbox/sphinx-toolbox/actions?query=workflow%3A%22mypy%22 :alt: mypy status .. |requires| image:: https://dependency-dash.repo-helper.uk/github/sphinx-toolbox/sphinx-toolbox/badge.svg :target: https://dependency-dash.repo-helper.uk/github/sphinx-toolbox/sphinx-toolbox/ :alt: Requirements Status .. |coveralls| image:: https://img.shields.io/coveralls/github/sphinx-toolbox/sphinx-toolbox/master?logo=coveralls :target: https://coveralls.io/github/sphinx-toolbox/sphinx-toolbox?branch=master :alt: Coverage .. |codefactor| image:: https://img.shields.io/codefactor/grade/github/sphinx-toolbox/sphinx-toolbox?logo=codefactor :target: https://www.codefactor.io/repository/github/sphinx-toolbox/sphinx-toolbox :alt: CodeFactor Grade .. |pypi-version| image:: https://img.shields.io/pypi/v/sphinx-toolbox :target: https://pypi.org/project/sphinx-toolbox/ :alt: PyPI - Package Version .. |supported-versions| image:: https://img.shields.io/pypi/pyversions/sphinx-toolbox?logo=python&logoColor=white :target: https://pypi.org/project/sphinx-toolbox/ :alt: PyPI - Supported Python Versions .. |supported-implementations| image:: https://img.shields.io/pypi/implementation/sphinx-toolbox :target: https://pypi.org/project/sphinx-toolbox/ :alt: PyPI - Supported Implementations .. |wheel| image:: https://img.shields.io/pypi/wheel/sphinx-toolbox :target: https://pypi.org/project/sphinx-toolbox/ :alt: PyPI - Wheel .. |conda-version| image:: https://img.shields.io/conda/v/domdfcoding/sphinx-toolbox?logo=anaconda :target: https://anaconda.org/domdfcoding/sphinx-toolbox :alt: Conda - Package Version .. |conda-platform| image:: https://img.shields.io/conda/pn/domdfcoding/sphinx-toolbox?label=conda%7Cplatform :target: https://anaconda.org/domdfcoding/sphinx-toolbox :alt: Conda - Platform .. |license| image:: https://img.shields.io/github/license/sphinx-toolbox/sphinx-toolbox :target: https://github.com/sphinx-toolbox/sphinx-toolbox/blob/master/LICENSE :alt: License .. |language| image:: https://img.shields.io/github/languages/top/sphinx-toolbox/sphinx-toolbox :alt: GitHub top language .. |commits-since| image:: https://img.shields.io/github/commits-since/sphinx-toolbox/sphinx-toolbox/v3.9.0 :target: https://github.com/sphinx-toolbox/sphinx-toolbox/pulse :alt: GitHub commits since tagged version .. |commits-latest| image:: https://img.shields.io/github/last-commit/sphinx-toolbox/sphinx-toolbox :target: https://github.com/sphinx-toolbox/sphinx-toolbox/commit/master :alt: GitHub last commit .. |maintained| image:: https://img.shields.io/maintenance/yes/2025 :alt: Maintenance .. |pypi-downloads| image:: https://img.shields.io/pypi/dm/sphinx-toolbox :target: https://pypi.org/project/sphinx-toolbox/ :alt: PyPI - Downloads .. end shields | Installation -------------- .. start installation ``sphinx-toolbox`` can be installed from PyPI or Anaconda. To install with ``pip``: .. code-block:: bash $ python -m pip install sphinx-toolbox To install with ``conda``: * First add the required channels .. code-block:: bash $ conda config --add channels https://conda.anaconda.org/conda-forge $ conda config --add channels https://conda.anaconda.org/domdfcoding * Then install .. code-block:: bash $ conda install sphinx-toolbox .. end installation sphinx-toolbox-3.9.0/__pkginfo__.py000066400000000000000000000003651475757360300173500ustar00rootroot00000000000000# This file is managed by 'repo_helper'. Don't edit it directly. __all__ = ["extras_require"] extras_require = { "testing": ["coincidence>=0.4.3", "pygments<=2.13.0,>=2.7.4"], "all": ["coincidence>=0.4.3", "pygments<=2.13.0,>=2.7.4"] } sphinx-toolbox-3.9.0/autonamedtuple_demo.py000066400000000000000000000027021475757360300211470ustar00rootroot00000000000000# Examples from # https://docs.python.org/3/library/typing.html#typing.NamedTuple # https://www.python.org/dev/peps/pep-0589/#totality # https://github.com/python/typing/pull/700 # stdlib import collections from typing import NamedTuple __all__ = ("Animal", "Employee", "Movie") class Animal(NamedTuple): """ An animal. :param name: The name of the animal. :param voice: The animal's voice. """ name: str voice: str class Employee(NamedTuple): """ Represents an employee. :param id: The employee's ID number """ #: The employee's name name: str id: int = 3 def __repr__(self) -> str: return f'' def is_executive(self) -> bool: """ Returns whether the employee is an executive. Executives have ID numbers < 10. """ class Movie(NamedTuple): """ Represents a movie. """ #: The name of the movie. name: str #: The movie's release year. year: int based_on: str class Foo(NamedTuple): """ A Namedtuple :param a: An integer :param str b: A string :param str c: """ #: An integer (another doc) a: int b: str #: C's doc c: str class NoDocstring(NamedTuple): #: An integer a: int b: str #: C's doc c: str Traditional = collections.namedtuple("Traditional", "a, b, c") Traditional.__doc__ = "A traditional Namedtuple" class CustomisedNew(collections.namedtuple("CustomisedNew", "a, b, c")): def __new__(cls, values: str): return super().__new__(*values.split()) sphinx-toolbox-3.9.0/autoprotocol_demo.py000066400000000000000000000017251475757360300206560ustar00rootroot00000000000000# stdlib from abc import abstractmethod from typing import Any, TypeVar # 3rd party from domdf_python_tools.doctools import prettify_docstrings from typing_extensions import Protocol, runtime_checkable __all__ = ("HasLessThan", "HasGreaterThan", "Frobnicater") @prettify_docstrings class HasLessThan(Protocol): """ :class:`typing.Protocol` for classes that support the ``<`` operator. """ def __lt__(self, other) -> bool: ... @prettify_docstrings class HasGreaterThan(Protocol): def __gt__(self, other) -> bool: ... @runtime_checkable class Frobnicater(Protocol): def frobnicate(self, something) -> Any: ... # From https://github.com/python/cpython/blob/main/Lib/typing.py T_co = TypeVar("T_co", covariant=True) # Any type covariant containers. @runtime_checkable class SupportsAbs(Protocol[T_co]): """ An ABC with one abstract method __abs__ that is covariant in its return type. """ __slots__ = () @abstractmethod def __abs__(self) -> T_co: pass sphinx-toolbox-3.9.0/autotypeddict_demo.py000066400000000000000000000021741475757360300210050ustar00rootroot00000000000000# Examples from # https://www.python.org/dev/peps/pep-0589/#totality # https://github.com/python/typing/pull/700 """ Demo of ``.. autotypeddict::`` """ # 3rd party from typing_extensions import TypedDict __all__ = ("Movie", "Animal", "OldStyleAnimal", "Cat", "Bird", "AquaticBird") class Movie(TypedDict): """ Represents a movie. """ #: The name of the movie. name: str #: The movie's release year. year: int based_on: str class _Animal(TypedDict): """ Keys required by all animals. """ #: The name of the animal name: str class Animal(_Animal, total=False): """ Optional keys common to all animals. """ #: The animal's voice. voice: str #: Old style TypedDict for Python 2 and where keys aren't valid Python identifiers. OldStyleAnimal = TypedDict( "OldStyleAnimal", { "animal-name": str, "animal-voice": str, }, total=False ) class Cat(Animal): """ A cat. """ #: The colour of the cat's fur. fur_color: str class Bird(Animal): """ A bird. """ #: The size of the bird's egg, in mm. egg_size: float class AquaticBird(Bird): #: The bird's habitat (e.g. lake, sea) habitat: float sphinx-toolbox-3.9.0/doc-source/000077500000000000000000000000001475757360300166045ustar00rootroot00000000000000sphinx-toolbox-3.9.0/doc-source/404.rst000066400000000000000000000003271475757360300176470ustar00rootroot00000000000000: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. sphinx-toolbox-3.9.0/doc-source/Source.rst000066400000000000000000000026071475757360300206030ustar00rootroot00000000000000========================= Downloading source code ========================= The ``sphinx-toolbox`` source code is available on GitHub, and can be accessed from the following URL: https://github.com/sphinx-toolbox/sphinx-toolbox If you have ``git`` installed, you can clone the repository with the following command: .. prompt:: bash git clone https://github.com/sphinx-toolbox/sphinx-toolbox .. parsed-literal:: Cloning into 'sphinx-toolbox'... 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 ``sphinx-toolbox`` 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. sphinx-toolbox-3.9.0/doc-source/_internal_extension.py000066400000000000000000000016371475757360300232340ustar00rootroot00000000000000# stdlib from typing import Optional # 3rd party from domdf_python_tools.paths import PathPlus from sphinx.application import Sphinx # nodep # this package from sphinx_toolbox import latex def replace_emoji(app: Sphinx, exception: Optional[Exception] = None): if exception: return if app.builder.name.lower() != "latex": return output_file = PathPlus(app.builder.outdir) / f"{app.builder.titles[0][1]}.tex" output_content = output_file.read_text() output_content = output_content.replace('🧰', '') output_content = output_content.replace('πŸ“”', '') output_content = output_content.replace( r"\sphinxcode{\sphinxupquote{\textbackslash{}vspace\{\}}}", r"\mbox{\sphinxcode{\sphinxupquote{\textbackslash{}vspace\{\}}}}", ) output_file.write_clean(output_content) def setup(app: Sphinx): app.connect("build-finished", replace_emoji) app.connect("build-finished", latex.replace_unknown_unicode) sphinx-toolbox-3.9.0/doc-source/_static/000077500000000000000000000000001475757360300202325ustar00rootroot00000000000000sphinx-toolbox-3.9.0/doc-source/_static/style.css000066400000000000000000000002521475757360300221030ustar00rootroot00000000000000ul.bullet-hidden, ul.bullet-hidden li { list-style-type: none !important; } table.indextable ul li ul, .genindextable li > ul { margin-left: 24px !important; } sphinx-toolbox-3.9.0/doc-source/_templates/000077500000000000000000000000001475757360300207415ustar00rootroot00000000000000sphinx-toolbox-3.9.0/doc-source/_templates/layout.html000066400000000000000000000005431475757360300231460ustar00rootroot00000000000000 {% extends "!layout.html" %} {% block extrahead %} {% endblock %} sphinx-toolbox-3.9.0/doc-source/api/000077500000000000000000000000001475757360300173555ustar00rootroot00000000000000sphinx-toolbox-3.9.0/doc-source/api/config.rst000066400000000000000000000002711475757360300213540ustar00rootroot00000000000000============================== :mod:`sphinx_toolbox.config` ============================== .. autosummary-widths:: 1/3 .. automodule:: sphinx_toolbox.config :autosummary-no-nesting: sphinx-toolbox-3.9.0/doc-source/api/sphinx-toolbox.rst000066400000000000000000000003521475757360300231040ustar00rootroot00000000000000======================= :mod:`sphinx_toolbox` ======================= ``sphinx_toolbox`` also provides some utility functions for building Sphinx extensions. .. automodule:: sphinx_toolbox.__init__ :no-autosummary: :no-docstring: sphinx-toolbox-3.9.0/doc-source/api/testing.rst000066400000000000000000000002351475757360300215640ustar00rootroot00000000000000============================== :mod:`sphinx_toolbox.testing` ============================== .. automodule:: sphinx_toolbox.testing :member-order: bysource sphinx-toolbox-3.9.0/doc-source/api/utils.rst000066400000000000000000000003121475757360300212430ustar00rootroot00000000000000============================== :mod:`sphinx_toolbox.utils` ============================== .. automodule:: sphinx_toolbox.utils :exclude-members: metadata_add_version,__repr__ :member-order: bysource sphinx-toolbox-3.9.0/doc-source/assets/000077500000000000000000000000001475757360300201065ustar00rootroot00000000000000sphinx-toolbox-3.9.0/doc-source/assets/hello_world.txt000066400000000000000000000000141475757360300231540ustar00rootroot00000000000000Hello World sphinx-toolbox-3.9.0/doc-source/changelog.rst000066400000000000000000000215661475757360300212770ustar00rootroot00000000000000=============== Changelog =============== 3.6.0 ---------------------- * Documentation fixes in :mod:`~.collapse`. * Dunder methods added in Python 3.13 are hidden by :mod:`~.autoprotocol` (``__non_callable_proto_members__``, ``__firstlineno__``, ``__replace__``, ``__static_attributes__``) * mod:`~.autoprotocol` doesn't document subclasses of protocols, unless :class:`~.Protocol` is one of their direct base classes. * Support for Sphinx 7.x 3.5.0 ---------------------- * Increase minimum ``cachecontrol`` version to ``0.13.0`` * Private base classes are hidden with :rst:dir:`autotypeddict` 3.4.0 ---------------------- * Renamed :py:obj:`sphinx_toolbox.more_autodoc.variables.type_template` to :py:obj:`sphinx_toolbox.more_autodoc.variables.old_type_template` and replaced the original with a version using unicode codepoints instead of reST substitutions. * On non-ReStructuredText parsers :mod:`sphinx_toolbox.more_autodoc.variables` and :mod:`sphinx_toolbox.more_autodoc.autonamedtuple` no longer output spurrious ``|nbsp|``. 3.3.0 ---------------------- ``sphinx-toolbox`` is now compatible with Sphinx 6.x Bugs Fixed ^^^^^^^^^^^ * :mod:`sphinx_toolbox.github.issues` -- GitHub made a change to the page structure which broke the issue title parsing. 3.2.0 ---------------------- ``sphinx-toolbox`` is now compatible with Sphinx 5.x and docutils 0.18. 3.1.2 ---------------------- Bugs Fixed ^^^^^^^^^^^ * :mod:`sphinx_toolbox.more_autodoc.typehints` -- Fix crash when performing fix for ``*args`` and ``**kwargs`` on e.g. builtin functions. Features ^^^^^^^^^^ * Allow ``sphinx-tabs`` version 3.4.0 in requirements. By :github:user:`jorgepiloto` in :github:pull:`115`. 3.1.1 ---------------------- :rst:dir:`.. extensions:: ` -- Use flushleft text with the LaTeX builder. (PR :github:pull:`105`) Bugs Fixed ^^^^^^^^^^^ * :mod:`sphinx_toolbox.more_autodoc.typehints` -- Fix crash when performing fix for ``*args`` and ``**kwargs``. 3.1.0 ---------------------- Removed cap on acceptable versions for `sphinx-autodoc-typehints `_. However, because ``sphinx-toolbox`` customises some of the functions from that package, configuration options added after version ``1.14.1`` may not work (e.g. ``typehints_defaults`` and ``typehints_formatter``). Bugs Fixed ^^^^^^^^^^^ * :mod:`sphinx_toolbox.more_autodoc.typehints` -- ``*args`` and ``**kwargs`` arguments have type hints applied correctly. 3.0.0 ---------------------- Additions ^^^^^^^^^^^ * Official support for Sphinx 4.5 and docutils 0.17 * :mod:`sphinx_toolbox.collapse` -- Added the ``:open:`` option for having the collapsable section open by default. Suggested by :github:user:`tdegeus` in :github:issue:`96`. (PR :github:pull:`101`) * :mod:`sphinx_toolbox.more_autosummary.column_widths` -- Allows for the autosummary table column widths to be customised with the LaTeX builder. (PR :github:pull:`100`) * :mod:`sphinx_toolbox.tweaks.latex_layout` -- Added the :confval:`needspace_amount` option for configuring the LaTeX ``needspace`` extension. * Add :mod:`sphinx_toolbox.latex.succinct_seealso` and make :mod:`sphinx_toolbox.latex` a package. * Add :rst:role:`namedtuple-field` role for cross-references to namedtuple fields. (PR :github:pull:`104`) Moves ^^^^^^^^^^ * :mod:`sphinx_toolbox.tweaks.latex_layout` -- Moved to :mod:`sphinx_toolbox.latex.layout`. * :mod:`sphinx_toolbox.tweaks.latex_toc` -- Moved to :mod:`sphinx_toolbox.latex.toc`. Removals ^^^^^^^^ * Python 3.6 (including CPython 3.6 and PyPy 3.6) are no longer supported. (PR :github:pull:`99`) * :mod:`sphinx_toolbox.issues` -- ``IssueNode``, ``visit_issue_node``, ``depart_issue_node``, ``get_issue_title``. Import from :mod:`sphinx_toolbox.github.issues` instead. * ``sphinx_toolbox.tweaks.sphinx_panels_tabs.copy_assets`` -- renamed to :func:`~sphinx_toolbox.tweaks.sphinx_panels_tabs.copy_asset_files`. * ``sphinx_toolbox.utils.begin_generate`` Bugs Fixed ^^^^^^^^^^^ * :mod:`sphinx_toolbox.more_autodoc.overloads` -- Now try to resolve forward references in function overloads. * :mod:`sphinx_toolbox.confval` -- :rst:dir:`confval` directives now show up in the index. * :rst:dir:`autonamedtuple`, :rst:dir:`autoprotocol`, :rst:dir:`autotypeddict` -- Index entries are now created. (PR :github:pull:`103`) * Type hints for ``typing.ContextManager`` redirect to :class:`contextlib.AbstractContextManager` on Python 3.7 and 3.8. 2.18.2 -------------- Bugs Fixed ^^^^^^^^^^^ * :mod:`sphinx_toolbox.github` now correctly parses issue titles containing code and quote characters. Reported by :github:user:`arisp99` in :github:issue:`91`. * :mod:`sphinx_toolbox.more_autosummary` -- Restore compatibility with latest autodocsumm. For the time being autodocsumm's ``relative_ref_paths`` option is not supported. 2.18.0 -------------- ``consolekit`` is no longer a dependency; :class:`~.TerminalRegexParser` now contains the necessary code to create coloured text itself. 2.17.0 -------------- Additions ^^^^^^^^^^^ * :func:`sphinx_toolbox.testing.latex_regression` and :class:`sphinx_toolbox.testing.LaTeXRegressionFixture` * The ``jinja2_namespace`` argument to :class:`sphinx_toolbox.testing.HTMLRegressionFixture.check` Bugs Fixed ^^^^^^^^^^^ * Close the underlying requests session of :data:`sphinx_toolbox.utils.GITHUB_COM` when the Python interpreter exits. * :func:`sphinx_toolbox.more_autodoc.typehints.process_docstring` is skipped for ``variable`` and ``regex`` documenters where there aren't ``:param:`` and ``:rtype:`` attributes. 2.16.1 -------------- Removed top-end requirement for Sphinx. The officially supported versions are Sphinx 3.2 to 4.4 inclusive. 2.16.0 -------------- Bugs Fixed ^^^^^^^^^^^^ * :mod:`sphinx_toolbox.more_autodoc` -- Ensure the ``|nbsp|`` substitution is set up correctly when the ``rst_prolog`` option is specified in ``conf.py`` (Issue :issue:`80`). when using the extension on its own. * Set the ``class`` CSS class with custom autodocumenters (:mod:`~.autonamedtuple`, :mod:`~.autoprotocol` and :mod:`~.autotypeddict`) (Issue :issue:`79`) Additions ^^^^^^^^^^^ * :func:`sphinx_toolbox.utils.add_fallback_css_class` * :class:`sphinx_toolbox.testing.HTMLRegressionFixture` -- made the ``docutils_version`` variable available in jinja2 templates. 2.15.3 -------------- Features ^^^^^^^^^^^ * Support ``sphinx-autodoc-typehints`` versions 1.12-1.14. Bugs Fixed ^^^^^^^^^^^^ * :mod:`sphinx_toolbox.more_autodoc.regex` -- Ensure the ``|nbsp|`` substitution is set up when using the extension on its own (Issue :issue:`80`). Deprecations ^^^^^^^^^^^^^^ * ``sphinx_toolbox.utils.begin_generate`` -- Will be removed in v3.0.0. Users of this function should reimplement it in their own code. 2.15.2 -------------- Bugs Fixed ^^^^^^^^^^^^ * :mod:`sphinx_toolbox.decorators` -- Ensure the ``deco`` role correctly finds the targets of xrefs. 2.15.1 ------------ Bugs Fixed ^^^^^^^^^^^^^ * :mod:`sphinx_toolbox.github` -- fix crash when getting GitHub issue titles if there's no internet. 2.15.0 ------------ Features ^^^^^^^^^ * Added support for Sphinx 4.1 and 4.2 * Added support for autodocsumm > 0.2.2 * Improved support for Python 3.10.0 rc.2 Bugs Fixed ^^^^^^^^^^^^^ * :class:`~.RegexDocumenter` -- No longer outputs a ``csv-table`` directive when there is no pattern and no flags. This prevents docutils emitting a warning. * Correctly uses UTF-8 when reading files in HTML regression tests. 2.14.0 -------- Features ^^^^^^^^^ * :class:`sphinx_toolbox.testing.HTMLRegressionFixture` -- Added support for rendering the reference file as a jinja2 template, which can be used to account for differences between Python and Sphinx versions. * Added support for Sphinx 4.0 and sphinx-tabs versions up to 3.2.0 * Improved support for Python 3.10.0 rc.1 * :mod:`sphinx_toolbox.tweaks.latex_layout` -- Now configures ``hyperref`` to use correct page numbering for the frontmatter. Bugs Fixed ^^^^^^^^^^^^^ * URLs pointing to https://pypistats.org/ now use lowercased project names. * The ``cls`` and ``return`` attributes are ignored from ``__annotations__`` when deciding whether to include the ``__new__`` method for a :class:`~.NamedTuple` with :class:`~.NamedTupleDocumenter`. 2.13.0 -------- Features ^^^^^^^^^^ * Added support for Sphinx 3.4.x and 3.5.x. * :mod:`sphinx_toolbox.more_autodoc.autoprotocol` -- Added support for generic bases, such as ``class SupportsAbs(Protocol[T_co]): ...``. * :mod:`sphinx_toolbox.more_autosummary` -- Added the :confval:`autosummary_col_type` configuration option. * :func:`sphinx_toolbox.latex.replace_unknown_unicode` -- Add support for converting ``β‰₯`` and ``≀``. * :func:`sphinx_toolbox.more_autodoc.typehints.format_annotation` -- Added support for :py:obj:`True` and :py:obj:`False` Bugs Fixed ^^^^^^^^^^^^^ * :mod:`sphinx_toolbox.more_autosummary` -- Ensure ``__all__`` is respected for autosummary tables. ----- .. note:: The changlog prior to 2.13.0 has not been compiled yet. sphinx-toolbox-3.9.0/doc-source/conf.py000066400000000000000000000064471475757360300201160ustar00rootroot00000000000000#!/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:: sphinx-toolbox .. |pkgname2| replace:: ``sphinx-toolbox`` .. |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), "pytest": ("https://docs.pytest.org/en/stable", None), "pytest-regressions": ("https://pytest-regressions.readthedocs.io/en/latest/", None), "coincidence": ("https://coincidence.readthedocs.io/en/latest", None), "autodocsumm": ("https://autodocsumm.readthedocs.io/en/latest", None), } html_theme_options = {"logo_only": False} html_context = { "display_github": True, "github_user": "sphinx-toolbox", "github_repo": "sphinx-toolbox", "github_version": "master", "conf_py_path": "/doc-source/", } 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", } # Fix for pathlib issue with sphinxemoji on Python 3.9 and Sphinx 4.x def copy_asset_files(app, exc): # 3rd party from domdf_python_tools.compat import importlib_resources from sphinx.util.fileutil import copy_asset if exc: return asset_files = ["twemoji.js", "twemoji.css"] for path in asset_files: path_str = os.fspath(importlib_resources.files("sphinxemoji") / path) copy_asset(path_str, os.path.join(app.outdir, "_static")) def setup(app): # 3rd party from sphinx_toolbox.latex import better_header_layout from sphinxemoji import sphinxemoji app.connect("config-inited", lambda app, config: better_header_layout(config)) app.connect("build-finished", copy_asset_files) app.add_js_file("https://unpkg.com/twemoji@latest/dist/twemoji.min.js") app.add_js_file("twemoji.js") app.add_css_file("twemoji.css") app.add_transform(sphinxemoji.EmojiSubstitutions) html_logo = "../sphinx_toolbox.png" toctree_plus_types.add("fixture") sys.path.append(os.path.abspath("..")) latex_elements["preamble"] = r"\usepackage{multicol}" nitpicky = True needspace_amount = r"4\baselineskip" autodoc_type_aliases = {"ForwardRef": "ForwardRef"} sphinx-toolbox-3.9.0/doc-source/docutils.conf000066400000000000000000000000501475757360300212740ustar00rootroot00000000000000[restructuredtext parser] tab_width : 4 sphinx-toolbox-3.9.0/doc-source/extensions/000077500000000000000000000000001475757360300210035ustar00rootroot00000000000000sphinx-toolbox-3.9.0/doc-source/extensions/assets.rst000066400000000000000000000002561475757360300230420ustar00rootroot00000000000000======================================= :mod:`~sphinx_toolbox.assets` ======================================= .. automodule:: sphinx_toolbox.assets :member-order: bysource sphinx-toolbox-3.9.0/doc-source/extensions/changeset.rst000066400000000000000000000020601475757360300234740ustar00rootroot00000000000000================================= :mod:`~sphinx_toolbox.changeset` ================================= .. automodule:: sphinx_toolbox.changeset :member-order: bysource :no-autosummary: :exclude-members: run Examples --------------- .. rest-example:: .. versionadded:: 2.4 .. versionadded:: 2.5 The *spam* parameter. .. versionadded:: 2.6 The *parrot* parameter. .. rest-example:: .. deprecated:: 3.1 Use :func:`spam` instead. .. deprecated:: 3.2 Use :func:`lobster` instead. .. rest-example:: .. versionremoved:: 1.2.3 Use :func:`foo` instead. .. versionremoved:: 1.2.3 Due to an unfixable bug this function has been removed. If you desperately need this functionality please write to the mailing list at :email:`python-users@example.org` .. only:: html .. rest-example:: .. versionchanged:: 0.3.0 * Parameters for ``__init__`` can be documented either in the class docstring or alongside the attribute. The class docstring has priority. * Added support for `autodocsumm `_. sphinx-toolbox-3.9.0/doc-source/extensions/code.rst000066400000000000000000000002241475757360300224450ustar00rootroot00000000000000============================ :mod:`~sphinx_toolbox.code` ============================ .. automodule:: sphinx_toolbox.code :member-order: bysource sphinx-toolbox-3.9.0/doc-source/extensions/collapse.rst000066400000000000000000000003061475757360300233360ustar00rootroot00000000000000================================= :mod:`~sphinx_toolbox.collapse` ================================= .. autosummary-widths:: 55/100 .. automodule:: sphinx_toolbox.collapse :member-order: bysource sphinx-toolbox-3.9.0/doc-source/extensions/confval.rst000066400000000000000000000003321475757360300231630ustar00rootroot00000000000000================================ :mod:`~sphinx_toolbox.confval` ================================ .. latex:vspace:: -10px .. autosummary-widths:: 53/100 .. automodule:: sphinx_toolbox.confval :member-order: bysource sphinx-toolbox-3.9.0/doc-source/extensions/decorators.rst000066400000000000000000000002251475757360300237010ustar00rootroot00000000000000=================================== :mod:`~sphinx_toolbox.decorators` =================================== .. automodule:: sphinx_toolbox.decorators sphinx-toolbox-3.9.0/doc-source/extensions/documentation_summary.rst000066400000000000000000000003011475757360300261550ustar00rootroot00000000000000============================================== :mod:`~sphinx_toolbox.documentation_summary` ============================================== .. automodule:: sphinx_toolbox.documentation_summary sphinx-toolbox-3.9.0/doc-source/extensions/flake8.rst000066400000000000000000000002761475757360300227140ustar00rootroot00000000000000======================================= :mod:`~sphinx_toolbox.flake8` ======================================= .. automodule:: sphinx_toolbox.flake8 :no-autosummary: :exclude-members: run sphinx-toolbox-3.9.0/doc-source/extensions/formatting.rst000066400000000000000000000002661475757360300237130ustar00rootroot00000000000000======================================= :mod:`~sphinx_toolbox.formatting` ======================================= .. automodule:: sphinx_toolbox.formatting :member-order: bysource sphinx-toolbox-3.9.0/doc-source/extensions/github.rst000066400000000000000000000011101475757360300230100ustar00rootroot00000000000000============================== :mod:`~sphinx_toolbox.github` ============================== .. automodule:: sphinx_toolbox.github :autosummary-no-nesting: :member-order: bysource :mod:`.github.issues` submodule ================================================ .. automodule:: sphinx_toolbox.github.issues :member-order: bysource :exclude-members: copy .. latex:clearpage:: :mod:`.github.repos_and_users` submodule ======================================================= .. automodule:: sphinx_toolbox.github.repos_and_users :member-order: bysource :exclude-members: copy sphinx-toolbox-3.9.0/doc-source/extensions/index.rst000066400000000000000000000037771475757360300226620ustar00rootroot00000000000000============ Overview ============ .. py:module:: sphinx_toolbox .. extensions:: sphinx-toolbox :import-name: sphinx_toolbox :no-postamble: sphinx.ext.viewcode sphinx_tabs.tabs sphinx-prompt The following features are enabled by default: * :mod:`sphinx_toolbox.assets` * :mod:`sphinx_toolbox.changeset` * :mod:`sphinx_toolbox.code` * :mod:`sphinx_toolbox.collapse` * :mod:`sphinx_toolbox.confval` * :mod:`sphinx_toolbox.decorators` * :mod:`sphinx_toolbox.formatting` * :mod:`sphinx_toolbox.github` * :mod:`sphinx_toolbox.installation` * :mod:`sphinx_toolbox.issues` * :mod:`sphinx_toolbox.latex` * :mod:`sphinx_toolbox.rest_example` * :mod:`sphinx_toolbox.shields` * * :mod:`sphinx_toolbox.sidebar_links` * :mod:`sphinx_toolbox.source` * :mod:`sphinx_toolbox.wikipedia` * :mod:`sphinx_toolbox.more_autodoc.autonamedtuple` * :mod:`sphinx_toolbox.more_autodoc.autoprotocol` * :mod:`sphinx_toolbox.more_autodoc.autotypeddict` \* Indicates that the extension cannot be enabled separately from the rest of ``sphinx_toolbox``. .. latex:clearpage:: Some features must be enabled separately: * :mod:`sphinx_toolbox.more_autodoc` * :mod:`sphinx_toolbox.more_autodoc.augment_defaults` * :mod:`sphinx_toolbox.more_autodoc.generic_bases` * :mod:`sphinx_toolbox.more_autodoc.genericalias` * :mod:`sphinx_toolbox.more_autodoc.no_docstring` * :mod:`sphinx_toolbox.more_autodoc.overloads` * :mod:`sphinx_toolbox.more_autodoc.regex` * :mod:`sphinx_toolbox.more_autodoc.sourcelink` * :mod:`sphinx_toolbox.more_autodoc.typehints` * :mod:`sphinx_toolbox.more_autodoc.typevars` * :mod:`sphinx_toolbox.more_autodoc.variables` :mod:`sphinx_toolbox.more_autodoc` can also be specified as an extension, which enables all of the above features. * :mod:`sphinx_toolbox.more_autosummary` Provides a patched version of :class:`sphinx.ext.autosummary.Autosummary` to fix an issue where the module name is sometimes duplicated. I.e. ``foo.bar.baz()`` became ``foo.bar.foo.bar.baz()``, which of course doesn't exist and created a broken link. sphinx-toolbox-3.9.0/doc-source/extensions/installation.rst000066400000000000000000000006221475757360300242360ustar00rootroot00000000000000=================================== :mod:`~sphinx_toolbox.installation` =================================== .. automodule:: sphinx_toolbox.installation :noindex: :autosummary-members: :no-members: .. automodule:: sphinx_toolbox.installation :no-docstring: :autosummary-sections: Methods ;; Attributes :exclude-members: sources .. autovariable:: sphinx_toolbox.installation.sources :no-value: sphinx-toolbox-3.9.0/doc-source/extensions/issues.rst000066400000000000000000000004251475757360300230510ustar00rootroot00000000000000======================================== :mod:`~sphinx_toolbox.issues` ======================================== .. automodule:: sphinx_toolbox.issues :member-order: bysource :exclude-members: IssueNode,issue_role,pull_role,visit_issue_node,depart_issue_node,get_issue_title sphinx-toolbox-3.9.0/doc-source/extensions/latex.rst000066400000000000000000000012521475757360300226520ustar00rootroot00000000000000============================== :mod:`~sphinx_toolbox.latex` ============================== .. automodule:: sphinx_toolbox.latex :member-order: bysource :exclude-members: run .. latex:clearpage:: Submodules =============== :mod:`sphinx_toolbox.latex.layout` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: sphinx_toolbox.latex.layout :no-autosummary: :mod:`sphinx_toolbox.latex.succinct_seealso` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: sphinx_toolbox.latex.succinct_seealso :no-autosummary: :mod:`sphinx_toolbox.latex.toc` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. automodule:: sphinx_toolbox.latex.toc :no-autosummary: sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/000077500000000000000000000000001475757360300234635ustar00rootroot00000000000000sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/augment_defaults.rst000066400000000000000000000005331475757360300275450ustar00rootroot00000000000000==================================================================================== :mod:`more_autodoc.augment_defaults ` ==================================================================================== .. autosummary-widths:: 53/100 .. automodule:: sphinx_toolbox.more_autodoc.augment_defaults sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/autonamedtuple.rst000066400000000000000000000004661475757360300272520ustar00rootroot00000000000000==================================================================================== :mod:`more_autodoc.autonamedtuple ` ==================================================================================== .. automodule:: sphinx_toolbox.more_autodoc.autonamedtuple sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/autoprotocol.rst000066400000000000000000000004601475757360300267470ustar00rootroot00000000000000==================================================================================== :mod:`more_autodoc.autoprotocol ` ==================================================================================== .. automodule:: sphinx_toolbox.more_autodoc.autoprotocol sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/autotypeddict.rst000066400000000000000000000004631475757360300271020ustar00rootroot00000000000000==================================================================================== :mod:`more_autodoc.autotypeddict ` ==================================================================================== .. automodule:: sphinx_toolbox.more_autodoc.autotypeddict sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/generic_bases.rst000066400000000000000000000005051475757360300270060ustar00rootroot00000000000000==================================================================================== :mod:`more_autodoc.generic_bases ` ==================================================================================== .. automodule:: sphinx_toolbox.more_autodoc.generic_bases :no-autosummary: sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/genericalias.rst000066400000000000000000000005021475757360300266400ustar00rootroot00000000000000==================================================================================== :mod:`more_autodoc.genericalias ` ==================================================================================== .. automodule:: sphinx_toolbox.more_autodoc.genericalias :no-autosummary: sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/index.rst000066400000000000000000000011041475757360300253200ustar00rootroot00000000000000=================================== :mod:`~sphinx_toolbox.more_autodoc` =================================== .. html-section:: .. py:module:: sphinx_toolbox.more_autodoc .. only:: html :mod:`sphinx_toolbox.more_autodoc` can be enabled by adding the following to the ``extensions`` variable in your ``conf.py``: .. extensions:: sphinx_toolbox.more_autodoc :no-preamble: :no-postamble: Alternatively, the following features can be enabled individually: .. toctree:: :maxdepth: 1 :glob: * ----- .. only:: html .. autofunction:: sphinx_toolbox.more_autodoc.setup sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/no_docstring.rst000066400000000000000000000005171475757360300267100ustar00rootroot00000000000000==================================================================================== :mod:`more_autodoc.no_docstring ` ==================================================================================== .. autosummary-widths:: 55/100 .. automodule:: sphinx_toolbox.more_autodoc.no_docstring sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/overloads.rst000066400000000000000000000005301475757360300262110ustar00rootroot00000000000000==================================================================================== :mod:`more_autodoc.overloads ` ==================================================================================== .. latex:vspace:: -2px .. automodule:: sphinx_toolbox.more_autodoc.overloads :member-order: bysource sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/regex.rst000066400000000000000000000022421475757360300253270ustar00rootroot00000000000000==================================================================================== :mod:`more_autodoc.regex ` ==================================================================================== .. automodule:: sphinx_toolbox.more_autodoc.regex :noindex: :no-autosummary: :no-members: API Reference -------------- .. autosummary-widths:: 45/100 .. automodulesumm:: sphinx_toolbox.more_autodoc.regex :member-order: bysource .. autoclass:: sphinx_toolbox.more_autodoc.regex.RegexDocumenter .. autoclass:: sphinx_toolbox.more_autodoc.regex.RegexParser :exclude-members: AT_COLOUR,SUBPATTERN_COLOUR,IN_COLOUR,REPEAT_COLOUR,REPEAT_BRACE_COLOUR,CATEGORY_COLOUR,BRANCH_COLOUR,LITERAL_COLOUR,ANY_COLOUR .. autoclass:: sphinx_toolbox.more_autodoc.regex.TerminalRegexParser :no-members: .. autoclass:: sphinx_toolbox.more_autodoc.regex.HTMLRegexParser :no-members: .. autoclass:: sphinx_toolbox.more_autodoc.regex.LaTeXRegexParser :no-members: .. automodule:: sphinx_toolbox.more_autodoc.regex :no-docstring: :no-autosummary: :members: parse_regex_flags,no_formatting,span,latex_textcolor,copy_asset_files,setup :member-order: bysource sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/sourcelink.rst000066400000000000000000000005211475757360300263710ustar00rootroot00000000000000==================================================================================== :mod:`more_autodoc.sourcelink ` ==================================================================================== .. automodule:: sphinx_toolbox.more_autodoc.sourcelink :member-order: bysource :sourcelink: sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/typehints.rst000066400000000000000000000013271475757360300262470ustar00rootroot00000000000000==================================================================================== :mod:`more_autodoc.typehints ` ==================================================================================== .. automodule:: sphinx_toolbox.more_autodoc.typehints :no-autosummary: :exclude-members: docstring_hooks,setup,default_preprocessors :member-order: bysource .. automodulesumm:: sphinx_toolbox.more_autodoc.typehints :member-order: bysource .. autovariable:: sphinx_toolbox.more_autodoc.typehints.docstring_hooks :no-value: .. autovariable:: sphinx_toolbox.more_autodoc.typehints.default_preprocessors :no-value: .. autofunction:: sphinx_toolbox.more_autodoc.typehints.setup sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/typevars.rst000066400000000000000000000004751475757360300261000ustar00rootroot00000000000000==================================================================================== :mod:`more_autodoc.typevars ` ==================================================================================== .. automodule:: sphinx_toolbox.more_autodoc.typevars :member-order: bysource sphinx-toolbox-3.9.0/doc-source/extensions/more_autodoc/variables.rst000066400000000000000000000007151475757360300261700ustar00rootroot00000000000000==================================================================================== :mod:`more_autodoc.variables ` ==================================================================================== .. autosummary-widths:: 55/100 .. automodule:: sphinx_toolbox.more_autodoc.variables :member-order: bysource :exclude-members: is_function_or_method,document_members,isinstanceattribute,import_object,get_real_modname sphinx-toolbox-3.9.0/doc-source/extensions/more_autosummary/000077500000000000000000000000001475757360300244135ustar00rootroot00000000000000sphinx-toolbox-3.9.0/doc-source/extensions/more_autosummary/column_widths.rst000066400000000000000000000002741475757360300300270ustar00rootroot00000000000000========================================== :mod:`.more_autosummary.column_widths` ========================================== .. automodule:: sphinx_toolbox.more_autosummary.column_widths sphinx-toolbox-3.9.0/doc-source/extensions/more_autosummary/index.rst000066400000000000000000000004261475757360300262560ustar00rootroot00000000000000========================================== :mod:`.more_autosummary` ========================================== .. automodule:: sphinx_toolbox.more_autosummary :exclude-members: filter_members,get_object_members Submodules ------------ .. toctree:: :maxdepth: 1 :glob: * sphinx-toolbox-3.9.0/doc-source/extensions/pre_commit.rst000066400000000000000000000003241475757360300236720ustar00rootroot00000000000000======================================= :mod:`~sphinx_toolbox.pre_commit` ======================================= .. autosummary-widths:: 55/100 .. automodule:: sphinx_toolbox.pre_commit :exclude-members: run sphinx-toolbox-3.9.0/doc-source/extensions/rest_example.rst000066400000000000000000000002621475757360300242250ustar00rootroot00000000000000=================================== :mod:`~sphinx_toolbox.rest_example` =================================== .. automodule:: sphinx_toolbox.rest_example :member-order: bysource sphinx-toolbox-3.9.0/doc-source/extensions/shields.rst000066400000000000000000000010351475757360300231670ustar00rootroot00000000000000=================================== :mod:`~sphinx_toolbox.shields` =================================== .. automodule:: sphinx_toolbox.shields :noindex: :no-members: :autosummary-members: :member-order: bysource API Reference -------------- .. autovariable:: sphinx_toolbox.shields.SHIELDS_IO .. autovariable:: sphinx_toolbox.shields.shield_default_option_spec :no-value: .. automodule:: sphinx_toolbox.shields :no-docstring: :no-autosummary: :member-order: bysource :exclude-members: SHIELDS_IO,shield_default_option_spec, run sphinx-toolbox-3.9.0/doc-source/extensions/sidebar_links.rst000066400000000000000000000002371475757360300243500ustar00rootroot00000000000000===================================== :mod:`~sphinx_toolbox.sidebar_links` ===================================== .. automodule:: sphinx_toolbox.sidebar_links sphinx-toolbox-3.9.0/doc-source/extensions/source.rst000066400000000000000000000002341475757360300230340ustar00rootroot00000000000000============================== :mod:`~sphinx_toolbox.source` ============================== .. automodule:: sphinx_toolbox.source :member-order: bysource sphinx-toolbox-3.9.0/doc-source/extensions/tweaks/000077500000000000000000000000001475757360300223015ustar00rootroot00000000000000sphinx-toolbox-3.9.0/doc-source/extensions/tweaks/footnote_symbols.rst000066400000000000000000000003101475757360300264320ustar00rootroot00000000000000============================================== :mod:`.tweaks.footnote_symbols` ============================================== .. automodule:: sphinx_toolbox.tweaks.footnote_symbols :no-autosummary: sphinx-toolbox-3.9.0/doc-source/extensions/tweaks/index.rst000066400000000000000000000006671475757360300241530ustar00rootroot00000000000000=============================== :mod:`~sphinx_toolbox.tweaks` =============================== .. py:module:: sphinx_toolbox.tweaks The following tweaks are available: .. toctree:: :maxdepth: 1 :glob: * .. attention:: The :mod:`~.sphinx_toolbox.tweaks.latex_toc` and :mod:`~.sphinx_toolbox.tweaks.latex_layout` tweaks are deprecated. Please use :mod:`~.sphinx_toolbox.latex.toc` and :mod:`~.sphinx_toolbox.latex.layout` instead. sphinx-toolbox-3.9.0/doc-source/extensions/tweaks/latex_layout.rst000066400000000000000000000002701475757360300255440ustar00rootroot00000000000000========================================== :mod:`.tweaks.latex_layout` ========================================== .. automodule:: sphinx_toolbox.tweaks.latex_layout :no-autosummary: sphinx-toolbox-3.9.0/doc-source/extensions/tweaks/latex_toc.rst000066400000000000000000000002711475757360300250150ustar00rootroot00000000000000======================================== :mod:`.tweaks.latex_toc` ======================================== .. autosummary-widths:: 4/10 .. automodule:: sphinx_toolbox.tweaks.latex_toc sphinx-toolbox-3.9.0/doc-source/extensions/tweaks/param_dash.rst000066400000000000000000000003061475757360300251310ustar00rootroot00000000000000.. latex:clearpage:: ======================================== :mod:`.tweaks.param_dash` ======================================== .. automodule:: sphinx_toolbox.tweaks.param_dash :no-autosummary: sphinx-toolbox-3.9.0/doc-source/extensions/tweaks/revert_footnote_style.rst000066400000000000000000000003541475757360300275010ustar00rootroot00000000000000.. latex:clearpage:: ================================================= :mod:`.tweaks.revert_footnote_style` ================================================= .. automodule:: sphinx_toolbox.tweaks.revert_footnote_style .. clearpage:: sphinx-toolbox-3.9.0/doc-source/extensions/tweaks/sphinx_panels_tabs.rst000066400000000000000000000003461475757360300267220ustar00rootroot00000000000000.. latex:clearpage:: ================================================= :mod:`.tweaks.sphinx_panels_tabs` ================================================= .. automodule:: sphinx_toolbox.tweaks.sphinx_panels_tabs .. clearpage:: sphinx-toolbox-3.9.0/doc-source/extensions/tweaks/tabsize.rst000066400000000000000000000002441475757360300244740ustar00rootroot00000000000000===================================== :mod:`.tweaks.tabsize` ===================================== .. automodule:: sphinx_toolbox.tweaks.tabsize :no-autosummary: sphinx-toolbox-3.9.0/doc-source/extensions/wikipedia.rst000066400000000000000000000002331475757360300235010ustar00rootroot00000000000000======================================= :mod:`~sphinx_toolbox.wikipedia` ======================================= .. automodule:: sphinx_toolbox.wikipedia sphinx-toolbox-3.9.0/doc-source/git_download.png000066400000000000000000000642761475757360300220030ustar00rootroot00000000000000‰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`‚sphinx-toolbox-3.9.0/doc-source/ignore_missing_xref.py000066400000000000000000000035601475757360300232220ustar00rootroot00000000000000# 3rd party from docutils import nodes from sphinx.application import Sphinx from sphinx.errors import NoUri def handle_missing_xref(app: Sphinx, env, node: nodes.Node, contnode: nodes.Node) -> None: # Ignore missing reference warnings for the wheel_filename module if node.get("reftarget", '').startswith("docutils."): raise NoUri if node.get("reftarget", '').startswith("sphinx.ext.autodoc."): raise NoUri if node.get("reftarget", '').startswith("sphinx.ext.autosummary."): raise NoUri if node.get("reftarget", '').startswith("sphinx_toolbox._data_documenter."): # TODO: redirect raise NoUri if node.get("reftarget", '') in { "spam", "lobster", "foo", "typing_extensions", "bs4.BeautifulSoup", "pytest_regressions.file_regression.FileRegressionFixture", "sphinx_toolbox.patched_autosummary", "sphinx_toolbox.autodoc_augment_defaults", "sphinx_toolbox.autodoc_typehints", "sphinx_toolbox.autotypeddict", "sphinx_toolbox.autoprotocol", "sphinx_toolbox.utils._T", "sphinx_toolbox.testing.EventManager", # TODO "sphinx.registry.SphinxComponentRegistry", "sphinx.config.Config", "sphinx.config.Config.latex_elements", "sphinx.util.docfields.TypedField", "sphinx.writers.html.HTMLTranslator", "sphinx.writers.html5.HTML5Translator", "sphinx.writers.latex.LaTeXTranslator", "sphinx.domains.python.PyXRefRole", "sphinx.domains.std.GenericObject", "sphinx.domains.changeset.VersionChange", "sphinx.directives.code.CodeBlock", "sphinx.roles.Abbreviation", "sphinx.roles.XRefRole", # New 26 jun 24 with Sphinx 5.x and Python 3.8 "autodoc.Documenter", # TODO: why not sphinx.ext.autodoc.Documenter? }: raise NoUri if node.get("reftarget", '').startswith("consolekit.terminal_colours.Fore."): raise NoUri def setup(app: Sphinx): app.connect("missing-reference", handle_missing_xref, priority=950) sphinx-toolbox-3.9.0/doc-source/index.rst000066400000000000000000000074561475757360300204610ustar00rootroot00000000000000############### sphinx-toolbox ############### .. 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: sphinx-toolbox :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/sphinx-toolbox/sphinx-toolbox/badge.svg :target: https://dependency-dash.repo-helper.uk/github/sphinx-toolbox/sphinx-toolbox/ :alt: Requirements Status .. |coveralls| coveralls-shield:: :alt: Coverage .. |codefactor| codefactor-shield:: :alt: CodeFactor Grade .. |pypi-version| pypi-shield:: :project: sphinx-toolbox :version: :alt: PyPI - Package Version .. |supported-versions| pypi-shield:: :project: sphinx-toolbox :py-versions: :alt: PyPI - Supported Python Versions .. |supported-implementations| pypi-shield:: :project: sphinx-toolbox :implementations: :alt: PyPI - Supported Implementations .. |wheel| pypi-shield:: :project: sphinx-toolbox :wheel: :alt: PyPI - Wheel .. |conda-version| image:: https://img.shields.io/conda/v/domdfcoding/sphinx-toolbox?logo=anaconda :target: https://anaconda.org/domdfcoding/sphinx-toolbox :alt: Conda - Package Version .. |conda-platform| image:: https://img.shields.io/conda/pn/domdfcoding/sphinx-toolbox?label=conda%7Cplatform :target: https://anaconda.org/domdfcoding/sphinx-toolbox :alt: Conda - Platform .. |license| github-shield:: :license: :alt: License .. |language| github-shield:: :top-language: :alt: GitHub top language .. |commits-since| github-shield:: :commits-since: v3.9.0 :alt: GitHub commits since tagged version .. |commits-latest| github-shield:: :last-commit: :alt: GitHub last commit .. |maintained| maintained-shield:: 2025 :alt: Maintenance .. |pypi-downloads| pypi-shield:: :project: sphinx-toolbox :downloads: month :alt: PyPI - Downloads .. end shields Installation -------------- .. start installation .. installation:: sphinx-toolbox :pypi: :github: :anaconda: :conda-channels: conda-forge, domdfcoding .. end installation Contents ------------ .. phantom-section:: .. toctree:: :hidden: Home .. toctree:: :maxdepth: 1 :caption: Extensions :glob: extensions/index extensions/* .. toctree:: :maxdepth: 2 :glob: extensions/**/index .. toctree:: :maxdepth: 2 :glob: changelog .. toctree:: :maxdepth: 3 :caption: Developer API :glob: api/sphinx-toolbox api/* .. sidebar-links:: :caption: Links :github: :pypi: sphinx-toolbox 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 sphinx-toolbox-3.9.0/doc-source/license.rst000066400000000000000000000002261475757360300207600ustar00rootroot00000000000000========= License ========= ``sphinx-toolbox`` is licensed under the :choosealicense:`MIT` .. license-info:: MIT .. license:: :py: sphinx-toolbox sphinx-toolbox-3.9.0/doc-source/not-found.png000066400000000000000000001352371475757360300212360ustar00rootroot00000000000000‰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.6.0 domdf-sphinx-theme>=0.3.0 extras-require>=0.5.0 flake8-dunder-all>=0.0.4 html-section>=0.3.0 pygments<=2.13.0,>=2.7.4 pytest>=6.0.0 pytest-regressions>=2.0.2 seed-intersphinx-mapping>=1.2.2 sphinx-autofixture>=0.2.1 sphinx-copybutton>=0.2.12 sphinx-debuginfo>=0.2.2 sphinx-licenseinfo>=0.3.1 sphinx-notfound-page>=0.7.1 sphinx-pyproject>=0.1.0 sphinxcontrib-applehelp==1.0.4 sphinxcontrib-devhelp==1.0.2 sphinxcontrib-htmlhelp==2.0.1 sphinxcontrib-httpdomain>=1.7.0 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.3 sphinxcontrib-serializinghtml==1.1.5 sphinxemoji>=0.1.6 toctree-plus>=0.6.1 sphinx-toolbox-3.9.0/doc-source/rtd-extra-deps.txt000066400000000000000000000000261475757360300222060ustar00rootroot00000000000000#sphinx~=3.5.0 .[all] sphinx-toolbox-3.9.0/doc-source/sphinx_event_role.py000066400000000000000000000042241475757360300227130ustar00rootroot00000000000000# Based on Sphinx # Copyright (c) 2007-2021 by the Sphinx team. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib import re # 3rd party import sphinx.addnodes from sphinx.application import Sphinx from sphinx.util.docfields import GroupedField event_sig_re = re.compile(r'([a-zA-Z-]+)\s*\((.*)\)') def parse_event(env, sig, signode): m = event_sig_re.match(sig) if not m: signode += sphinx.addnodes.desc_name(sig, sig) return sig name, args = m.groups() signode += sphinx.addnodes.desc_name(name, name) plist = sphinx.addnodes.desc_parameterlist() for arg in args.split(','): arg = arg.strip() plist += sphinx.addnodes.desc_parameter(arg, arg) signode += plist return name def setup(app: Sphinx): fdesc = GroupedField("parameter", label="Parameters", names=["param"], can_collapse=True) app.add_object_type("event", "event", "pair: %s; event", parse_event, doc_field_types=[fdesc]) sphinx-toolbox-3.9.0/formate.toml000066400000000000000000000025321475757360300170750ustar00rootroot00000000000000[hooks] dynamic_quotes = 10 collections-import-rewrite = 20 reformat-generics = 40 noqa-reformat = 60 ellipsis-reformat = 70 squish_stubs = 80 [hooks.yapf] priority = 30 [hooks.yapf.kwargs] yapf_style = ".style.yapf" [hooks.isort] priority = 50 [hooks.isort.kwargs] indent = " " 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", "autodocsumm", "beautifulsoup4", "cachecontrol", "coincidence", "coverage", "coverage_pyver_pragma", "defusedxml", "dict2css", "docutils", "domdf_python_tools", "filelock", "flake8_dunder_all", "html5lib", "hypothesis", "importlib_metadata", "pprint36", "pygments", "pytest", "pytest_cov", "pytest_httpserver", "pytest_randomly", "pytest_timeout", "ruamel_yaml", "sphinx", "sphinx_autodoc_typehints", "sphinx_jinja2_compat", "sphinx_prompt", "sphinx_tabs", "sphobjinv", "tabulate", "typing_extensions", "typing_inspect", ] known_first_party = [ "sphinx_toolbox",] [config] indent = " " line_length = 115 sphinx-toolbox-3.9.0/justfile000066400000000000000000000005731475757360300163160ustar00rootroot00000000000000default: 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 sphinx-toolbox-3.9.0/pyproject.toml000066400000000000000000000110511475757360300174530ustar00rootroot00000000000000[build-system] requires = [ "whey",] build-backend = "whey" [project] name = "sphinx-toolbox" version = "3.9.0" description = "Box of handy tools for Sphinx 🧰 πŸ“”" readme = "README.rst" keywords = [ "documentation", "sphinx", "sphinx-extension",] dynamic = [ "requires-python", "classifiers", "dependencies",] [project.license] file = "LICENSE" [[project.authors]] name = "Dominic Davis-Foster" email = "dominic@davis-foster.co.uk" [project.urls] Homepage = "https://github.com/sphinx-toolbox/sphinx-toolbox" "Issue Tracker" = "https://github.com/sphinx-toolbox/sphinx-toolbox/issues" "Source Code" = "https://github.com/sphinx-toolbox/sphinx-toolbox" Documentation = "https://sphinx-toolbox.readthedocs.io/en/latest" [project.optional-dependencies] testing = [ "coincidence>=0.4.3", "pygments<=2.13.0,>=2.7.4",] all = [ "coincidence>=0.4.3", "pygments<=2.13.0,>=2.7.4",] [tool.whey] base-classifiers = [ "Development Status :: 5 - Production/Stable", "Framework :: Sphinx :: Extension", "Intended Audience :: Developers", "Topic :: Documentation", "Topic :: Documentation :: Sphinx", "Topic :: Software Development :: Documentation", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities", "Typing :: Typed", ] python-versions = [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13",] python-implementations = [ "CPython", "PyPy",] platforms = [ "Windows", "macOS", "Linux",] license-key = "MIT" package = "sphinx_toolbox" [tool.dep_checker] allowed_unused = [ "html5lib", "sphinx_prompt", "sphinx_tabs", "filelock", "cachecontrol", "sphinx_jinja2_compat",] namespace_packages = [ "ruamel.yaml",] [tool.dep_checker.name_mapping] beautifulsoup4 = "bs4" ruamel-yaml = "ruamel.yaml" [tool.mkrecipe] conda-channels = [ "conda-forge", "domdfcoding",] extras = "all" [tool.sphinx-pyproject] github_username = "sphinx-toolbox" github_repository = "sphinx-toolbox" author = "Dominic Davis-Foster" project = "sphinx-toolbox" copyright = "2020-2022 Dominic Davis-Foster" language = "en" package_root = "sphinx_toolbox" 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", "notfound.extension", "sphinx_copybutton", "sphinxcontrib.default_values", "sphinx_debuginfo", "sphinx_licenseinfo", "seed_intersphinx_mapping", "html_section", "sphinx_toolbox.pre_commit", "sphinx_toolbox.flake8", "sphinx_autofixture", "sphinx_event_role", "_internal_extension", "sphinxcontrib.email", "ignore_missing_xref", "sphinx_toolbox.latex.succinct_seealso", "sphinx_toolbox.more_autosummary.column_widths", ] 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 = "domdf_sphinx_theme" 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.mypy] python_version = "3.8" namespace_packages = true check_untyped_defs = true warn_unused_ignores = true no_implicit_optional = true incremental = false exclude = "^tests/.*?/[^/]*doc-test/.*" show_error_codes = true [tool.snippet-fmt] directives = [ "code-block",] [tool.snippet-fmt.languages.python] reformat = true [tool.snippet-fmt.languages.TOML] reformat = true [tool.snippet-fmt.languages.ini] [tool.snippet-fmt.languages.json] [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 sphinx-toolbox-3.9.0/repo_helper.yml000066400000000000000000000070511475757360300175730ustar00rootroot00000000000000# Configuration for 'repo_helper' (https://github.com/domdfcoding/repo_helper --- modname: 'sphinx-toolbox' copyright_years: 2020-2022 author: 'Dominic Davis-Foster' email: 'dominic@davis-foster.co.uk' username: 'sphinx-toolbox' assignee: 'domdfcoding' primary_conda_channel: 'domdfcoding' version: '3.9.0' license: 'MIT' short_desc: 'Box of handy tools for Sphinx 🧰 πŸ“”' use_whey: true min_coverage: 90 python_deploy_version: 3.8 tox_testenv_extras: all preserve_custom_theme: true docs_fail_on_warning: true mypy_version: 1.11.0 conda_channels: - conda-forge python_versions: 3.7: matrix_exclude: sphinx: - 6.0 - 6.1 - 6.2 - 7.0 - 7.1 - 7.2 - 7.3 - 7.4 - 8.0 - 8.1 - 8.2 - latest 3.8: matrix_exclude: sphinx: - 7.2 - 7.3 - 7.4 - 8.0 - 8.1 - 8.2 - latest 3.9: matrix_exclude: sphinx: - 8.0 - 8.1 - 8.2 "3.10": matrix_exclude: sphinx: - 8.2 "3.11": "3.12": "3.13": matrix_exclude: sphinx: - 3.2 - 3.3 - 3.4 - 3.5 - 4.0 - 4.1 - 4.2 - 4.3 - 4.4 - 4.5 - 5.0 - 5.1 - 5.2 - 5.3 - 6.0 - 6.1 pypy37: matrix_exclude: sphinx: - 3.2 - 3.3 - 3.4 - 3.5 - 4.0 - 4.1 - 4.2 - 6.0 - 6.1 - 6.2 - 7.0 - 7.1 - 7.2 - 7.3 - 7.4 - 8.0 - 8.1 - 8.2 - latest pypy38: matrix_exclude: sphinx: - 3.2 - 3.3 - 3.4 - 3.5 - 4.0 - 4.1 - 4.2 - 7.2 - 7.3 - 7.4 - 8.0 - 8.1 - 8.2 - latest pypy39: matrix_exclude: sphinx: - 3.2 - 3.3 - 3.4 - 3.5 - 4.0 - 4.1 - 4.2 - 8.0 - 8.1 - 8.2 - latest additional_ignore: - wip-shields-block.py extras_require: testing: - pygments>=2.7.4,<=2.13.0 - coincidence>=0.4.3 extra_sphinx_extensions: - sphinx_toolbox.pre_commit - sphinx_toolbox.flake8 - sphinx_autofixture - sphinx_event_role - _internal_extension - sphinxcontrib.email - ignore_missing_xref - sphinx_toolbox.latex.succinct_seealso - sphinx_toolbox.more_autosummary.column_widths tox_unmanaged: - pytest - testenv sphinx_conf_epilogue: - html_logo = "../sphinx_toolbox.png" - toctree_plus_types.add("fixture") - sys.path.append(os.path.abspath("..")) - latex_elements["preamble"] = r"\usepackage{multicol}" - nitpicky = True - needspace_amount = r"4\baselineskip" - 'autodoc_type_aliases = {"ForwardRef": "ForwardRef"}' keywords: - sphinx - documentation - sphinx-extension third_party_version_matrix: sphinx: - 3.2 - 3.3 - 3.4 - 3.5 - 4.0 - 4.1 - 4.2 - 4.3 - 4.4 - 4.5 - 5.0 - 5.1 - 5.2 - 5.3 - 6.0 - 6.1 - 6.2 - 7.0 - 7.1 - 7.2 - 7.3 - 7.4 - 8.0 - 8.1 - 8.2 # - latest classifiers: - 'Development Status :: 5 - Production/Stable' - "Framework :: Sphinx :: Extension" - 'Intended Audience :: Developers' - "Topic :: Documentation" - 'Topic :: Documentation :: Sphinx' - 'Topic :: Software Development :: Documentation' - 'Topic :: Software Development :: Libraries :: Python Modules' - "Topic :: Utilities" intersphinx_mapping: - "'pytest': ('https://docs.pytest.org/en/stable', None)" - "'pytest-regressions': ('https://pytest-regressions.readthedocs.io/en/latest/', None)" - "'coincidence': ('https://coincidence.readthedocs.io/en/latest', None)" - "'autodocsumm': ('https://autodocsumm.readthedocs.io/en/latest', None)" exclude_files: - contributing - doc_requirements sphinx-toolbox-3.9.0/requirements.txt000066400000000000000000000010231475757360300200210ustar00rootroot00000000000000# sphinx-tabs is capped to avoid breakage if they update their js code, which we patch # sphinx-tabs<3.5.0,>=1.2.1 apeye>=0.4.0 autodocsumm>=0.2.0 beautifulsoup4>=4.9.1 cachecontrol[filecache]>=0.13.0 dict2css>=0.2.3 docutils>=0.16 domdf-python-tools>=2.9.0 filelock>=3.8.0 html5lib>=1.1 ruamel.yaml>=0.16.12 sphinx>=3.2.0 sphinx-autodoc-typehints>=1.11.1 sphinx-jinja2-compat>=0.1.0 sphinx-prompt>=1.1.0 sphinx-tabs<3.4.7,>=1.2.1 tabulate>=0.8.7 typing-extensions!=3.10.0.1,>=3.7.4.3 typing-inspect>=0.6.0; python_version < "3.8" sphinx-toolbox-3.9.0/sphinx_toolbox.png000066400000000000000000000417271475757360300203410ustar00rootroot00000000000000‰PNG  IHDRϊqό›Ύ& pHYs Δ ΔUΓ&tEXtSoftwarewww.inkscape.org›ξ< IDATxœμwxTΧ™ΏηΆ©€υސDGTS%ΐ`Œƒ±8±―7ήl{ν€γdWΏέΔ{ν$»IΦ)N²qwlΣAτŽ©¨£.͌¦·ΫΞο‘1’²ϊ<Ο}4εήsίs4ί{Ϊ{ή 1ΔC 1ΔC 1ΔC 1ΔC 1ΔC 1ΔC 1ΔC 1ΔC 1ΔC 1ΔC 1Δ-ΉΡό=σΪk―ρ]]]FΠh4‘'Ÿ|2x£mβ&CBdΚ~]fωΧ@0@:ξΛoi;@*²…%τΣ§Ÿ~φč²sˆ[ }yι₯²x…ςλŒλωŒηyB I(₯_@° «W=ύΣu_³©CόcHθƒΔσΟ?ŸIXu€<N‰&ΓmΗuWθͺͺΒπΓεr’½½ ­m-e@@?§*χ½U«V5ήΐ, ρw̐ΠϋΑ /ΌM4L²¬2‰«r+τθf »/ξtΩO Ÿa̝=FcΤΣ’$uu΅8SUΩ#xzοͺ§~ΊσkΘΚ °lΩ2+ΓΘΙ*ƒxPb •„ψ(eΦ][ €^%™λΖsS–}”‘΄ηTΰυgκλ7έ({n$CBΏ€η^ώsΓ©‘"™I„TB˜tJhU© BΟΉΔr7šΛΚΚTxρşO¦„| Ζj‰ΗmS¦Ejσ+ qθπΨ읦D]πΜΣ?Ϋr½ς8Pf͚ΕY-S ͺs !ΕΠδ«\ζpΐ&Κ›οΎϋQνΧ`*Φl]vφο9–½?9=]c41’ΝΝΝΑP °—γωϋŸͺͺ²Άά, ύ<Ούχ_γXE©4šh½V« -+h4Ό αΑq8Ορΰ TUΑ‘ύ 2tδ³?ό4V―ώΕd#@γcbb1}κ πΌp₯Ϋ(₯8rτΞ56€Ÿ²Μτgώε™/_nϋΗςK@)ώ@τWΏ‹‰Q’““ˆ^―g!E’(©N§Kνκκ"‘Pˆ=*‘ϋΌ°φ>ΉžφΎ™ωK½Ιτhξ˜1zκσAυωΐ ΄11h¬―Ϋ[Z‚ͺͺώΣ3ηΞ½y=νΈ™ϊy^ψελοΗΕΖ,,ž0Ž' •R(*…ͺR¨”‚ͺ=γiχμ ‹aιGΟ<ωΉ0η^~€Qιf©ΡΡfL»m΄ZνUοO)ΕΑCϋΡά•‘€4Ύ¬¬,0ψ9ν??ς­·|~ίύ,Λ*,ΛYVUU#ί[¬qΘΝΝEAΑ0π<Ρ΅@υuu8{ΆJu8Ί%Ϋ@ΙΏ¬]»φΨ`ΫΊ:33Qa˜s#¦ND‘.0”‚₯Ό ΐ˜”„ ίϊκκ@8<ͺκS«ΞΫ;Ψvάl07Ϊ€›η^yύaŽcξ),Μηύ Ίά^xύ!„E’¬@QΤn±_ΠΫ4E›y0dΖWΣϊΙS?9Γu€:·Ϋ…»Ά#ΌΊ^ !_<±§o_¨1π?Ό^Zώ§ Τ1ωEΒ›{‡}χν΅ T¦ΐjMψƒΙεrΨ»°ί¬yχ=T©ΊθZ½^‡α#†cΙΕΜ=χά «ΕBAh)υΠ²ϋ–ώp°mUEζx«¬Ρj‘ΰ„η!s,‚²Œζf€RŽ―ΟΘϟΜi4ΫVggy!3sή`Ϋr3qsΧθΛΦ°YΑΊ 4_Qi«jΤm^{mN'ΟΏϊz6Ɓб,a&QͺS sQa>β¬qπψ‚`Y ˁεΨξΧ {I:m--¨―©m[ωΓ‡/ΫO}ρΕ“)‘·(Τλυ˜>uŒFγUν³;lΨ±s;ψ 9Ο>ϋlΗ΅δχλ`ιK³c’’ΰq{J(₯Δ‡™%3c†Νnwυ£ή η€ξ–K]]=vοΪ­Š’ΔP‚ΧύξΐγλΧ―†-«srΆζŒS₯7 ΰp€ͺ*TE…ͺvTU **–…>6£»Ά66C~§JιoYJφㆆφΑ°ηfα&:%Ωw>ŸGIΉ‹wpcj)ρ”€ͺ්QψΧ« Ύ¬¬ŒΡ™ΣςTJΖ‘naO€”Œh”Vδhs”d0κuQF zxA€ΓεƒJ)X–;/vφ’Χ σeΘοσα‹C‡Α°jΪΚ<Ϊ|9ž{ξ9+ΓΡ Ζi5ZLŸ6&Σ%έάKΨ³w'Ϊ;ΪAΚV>ύμλg!ή0–-[6Μ₯ύΐοq‹ι3¦###Cά΅sΧ–eγ γβYp»έXΏ~#υzΌΐφP@\°nέΊkκͺ”œ&;ΫΫΒ…BΨaƒ ‚^ rUQ»…―*έ’WTPB Ž†ΦdBΠ£#θκθΰ¨’œTUυΑ0ͺ«»ε§=_θΛΦ°ΑΪ»JΗSJμΪ ‘Ν H ϋ†DΓ‚κ©Jt*¨•R(!ΩN"–%“Q+₯$Ε3i)Φj‰ΐσ°šxTΦv`γcb(>Ϋ3νΘ‘Η$(++㴦ŒBΚ82„LΑPͺΣkΙ…h“‘ο΅,ΗBQ(‚‘0Β’J ΐ„E *%`Yφ‘³, –γΑ²,(₯p:¨ϋ’$‚P΅`εʟ”ΠΧȊ+n#¬Ί]QT‘€t233δuŸ~~ΨλςDGG›R¦Ll²Z­eΦoB[[@ΙΆP0Όp b>==[0θO—¬ΈOλ=w.RkχΤΰ_ΎΏ )―\ϊ₯¬^­ΑN§ƒ­±QjihRB&ό€κκ–ά|\}’·―”•1β!ω³h“~ψ°a™œp~yΔ°t(ŠYVA@ΐr (₯  ¬ͺ‚•d’¬B’ˆ²ŠNQ v~εφ‡Dˆ’»;BR’γ9B=%'X`2Ωθ(#’ΊE-(ͺŠ@0YVΰσPB Σia4κ‘(*|ώ<?::»dŸ7 ϊAA”$D›ŒRZj"o2κ + xžG8†­³{—(†Ež0d―’ͺΥͺτύ'τpW_Šζ駟φΏπβs»@θI’@Θ•Ÿ―„X,΄΅΅`'Έε„ώΞ;ομ½wΕ½%,!»vνΪΝΔ'Δs3gNΟωཏ»έnuϋΆΙ“§LŒIOOΓό;nΗ†υΡΦΦ^ͺΥiΦ-\Έp@b7660ΉΉŠ‘5EA Ώ¬΅ΟχΝ/_Γ_ζ»p``Δ€€π„e™ΦΊΊ΅F_‡βΊξ\:Κ4@2τσοΧhΨΗο]0ƒŽŽeh΄Zψ‚2!Α° oP„Λ‚Σ+’ΛB—'·/ o@„?(!– J2dE=_σͺPΞ ͺ›έψ’Ζž%Γ"|0σΠΒbF£ΐ² dYAX!Λ2τZ-t:xŽƒ,)πωƒ΄½έ.ΥΦ5£ͺφΣΤΤξο΄»ΎπΈ}…Β‘?)CύΧpX4ΩνbY‘Y”˜¦¦φpKKηυωΛ’ό2Γα;+πΘ―ΆnψδθƍŸτkPpΞν³'(‰‰ArR †ΉβασyΡiλ!¨έ²yΫ-ιΝUqͺ’iΔΘαAEV憂!ά.w­έnχPUEKKk0θτqqqΘΞΙFG{|>_Η³“†x―’’BξΟύΚ:Ηd²8Ϋ;Ζr‚†³d¦ƒΣλΐi΅`y C@ΊΛ˜=0 †½ψύ― U Γ”’Μ΄ΧΥ'”όΣSάΆk›χϊ”ΨυcΠjt–ΰ»9i ›ΣέMΠ/ΛΌGχkτφ}€V§°9Εξڝg 7hpΊΚ‹PX„^§( ‡ασΰυϊ/ »έ^M0!h₯„JPΠγ Gμ‰Gλ{ΙΖγΏxυυ?ΪmΞ?j¬*Ί«9Mμ?ύψ‡W_{ Q'Θ² –½ϊσU§ΣTBγ―ύή7UΖ+,Ο>]WWo|Ϋ$$Ÿ©¨„~ΌlΩ²EkΧνΧ5$Š?…έ>K σ:[ZŒ Λ‚cY€Ž(‚!!<ΗB… }~¨’xفΊ kxV§‡ Cg‘|FNύσ―ώι !ψ= γΓ•+ΏsKˆ~Π„NA‹-qfΖγC”δH³ϋŠbΗΕοUUB  ‡ΕY―‚!6”R‚&’€ΰΘΙzE`dxΌ~V‘• Ξ¨”$”£„ždΒΚ©U«sχ'Ο>ρΘџΏςσ»9‰lcζpλΧ¬YσΛ—/W©€œ €$KWzΟ ·ˆ“ EΜ5έχ³vνZεήεKώ@ωI{[;’“S²ΒaιCJ‘‚R•R¨GŽ|‘J²œ0|xζί1λ?߈φφφΉϊ-φ²ΦΦ@Ωπα“©Ο·4ΰχ@ss]έPšΕi΅$69ILΘΘ0Δ&'e!ωό½^Θ’xQŸžΥh ϊΠΖΖ ±ΎΙso'ΉGς@xj[‡}\k{ηžεŸ6•όQΟω6όΰ?”)ΒλΑ€…N”E:›Ή”Ρ Θ’I”/¨/3"οUHbbΠ )θ‡φCΓ†ay†Λ@)1*°eΑPΰ‹ŠζΧ “ ŸQY:΅ςΗm—­₯W’μ2NAe”τ²ΨβΕ_L₯²Ό yησωέΊϊjγk―½φΠcuςˆσBE±W‘_ΈŒ΅§` !‘ίσ&©’όώ4A/h…ψp8άB Q»kvͺ?~B•%9iΤθ‘ηϋμ›Πήή>—ϊΡ·ΏύνEωΛ_ϊ\e§O‹ήώκη /₯₯ewΥ7έΦΩΠΈ˜!dΆήl33˜Δœ,!Šγ!Ίέό~h,€ΝΏͺ*ΈκαhjΖΨΗUQΑ± R“¬Ίœτdψ|»Ϊ:ν³ΫΪ9ϊΒώρδυqλšvΝ*‡zΣ‘ή·WcΐB?;cΞqέ‘y’Ώ΄_~ Γ@Q{l‘—ώ‘έM"JΥσŸσΏj‚žŸχ—ƒU~0„€adYΑΓ²’Ζ Νg½ΞΞμ‡ρtξ μά|χW?~ι₯²xςvPδΖΖΔbτθ±Ψ΅{dY^ΡεΆ_yε•εCO νUθ_]«.+έ  κΘύn&(A<€ˆίΏ^―KUUŠp8Τύƒ *ΤΣ§*TI–“‹‹Η’žΊφφŽΫύAίΗύϋε =“Uπ*uώ#§ͺBP:œ₯½‹#,Cτ‚@†ΪTŸ/+0ό(KTΘο^# Cΐ°,Γ² (=₯$Ѝ!ͺJΏ‘(ςƒͺ–Ss$?«(~F•―–§s<ΨΉeΤΥOΌ”k¨Ρ©D(Ιθyo‘! τ΅V> €΄=ίΤΛFvQU~4r£’’°pΑ=Πιt0™’±~ΓgΕπQ~φβ‹/.ZΉreΏϊeΟ―ώωύτχ`΅X/ϊ%Α(ΞγυzΞEϊs―›”; )9Α`P! ‰AC #Šb+₯PiwP­:S₯Κ’œ2qxfώόyXΏa#:ΊΕώΑ¬Y³ξ.//οΧέ₯¨ΐΏ[† aρΠΏl΄©Αξί3€'„EΐwQJ_;a»ž+κό1hPΠ£½vΘΧύT† 66:œ”ŒEw/ξ+‘DήόΚ+e±}Mσ…ΥΏψgςaΔπ‘(™5ϋK‡†ωη+‡ΛεμN€ΠΣƒΟ―“eΛ–YΜ²Xγ`ŠŠ‚έξ`yA“*|Š hR4MŠ,KQ”šEQlΕpK]mmΣΎ½ϋe†epΗόyˆOˆ@ο°&ΔώϊFηηVeHθ@@xPd>Ÿ/rh5Z̝3z½&‰²PΎzυκΔ«₯χό‹ΏψwΏΐŒSŒI§@’€Θ!Λ2E‰=Ž’$‘­½ ΐzψ:fωλ ‰>οϊλσω |ΌF#€j4BŠΐσ©A“’Ȋ;,†›Γ’ΤΕ–†ϊsΝ»vξ‘C0οφΉ0™LΘχ–­Xϊ747·(ƒη0ΣOd½*ΛAπή섬ΧΑ5k˜€ζ]ϋΐ(ύ('΄”ΐοο8πεxΛ°˜1­»φμ€ίο©BΪωόκŸ?C ΣΜͺlS0lο BQVVΖhυόψ!cΗ#/7’4{{}A˜©Κ§ŸώYoS·ƒαœ?θUlv;K)EZZ*κκκ ΐ$Μ€RF’δ6)jνξ·757«ϋφμK:}ͺ0oώ\|ψΑΗPUυΏ—ήΏtηϋoΏf°mU9i“‘κ 0—οΌ ½€Dp·gΠμ7Dθ]γF!ξΎ †eaΫ± ±oτκ*ήg(!p}σ~ŒZΈννhMNFόΫk@ϊΡ‡§ΫLΨξΨn—03 ˜5³»vο€ΗγΞ# οJA‰ ­ž—^Xύ‹64ΡξrΔ0 ΖODjJDQμ³­­­=/£_~ωηO=υΣs}ΟΕΝΓo–Α(†χε*L`μξT:x'M@iιLμΪΉ'Ž0`@Ξw• aB’ΨFΓb ν­UΞ5©zΓ±΄βργ4Sn›‚];wρ<Γόΐνƒi+%€νΎ%H)-A|BN€$#ξιe ₯?4ώΓÈΙΛ…¨(π|π ,ΏώFΪ iΊ»ΗށΞhDJZf—ΐ±pώ5§ΆΔΑ— J)2233q<œ3¦υ+U«VΥ€SEψύΎΛφ zJfΞFNv.“mŠξ ΕH§ΐT“X–ΕΤΫf #=,Λ‚γΈΘΡ[ΏΌη(*ŽΨ˜XH–U²ωΏψEΒ5ΠΧΜ―DͺΘ1ϋr$’SSα5|¬ν?7€Fן­FCf8ŽCrr2σn‡«½ζCT …ΰρxΐ²,2rrΠ0§α3UΠttφ) B}aυ/vXΪήюΨΨΈξ΄ΏςTΧ뻣Ό^ˆ$Kψύπ0›czΔΪ»Ν½Τ,ΛbΖτlί±n·+ε±α…^˜΅jΥͺ~9ά(^]L•2Ϋdedηaςm³Ιœ π«Ον8uςTEΑmS§`ΖΜi8xπ0Zš[’A`Β€€F’Δ6Q ·œŸ‡U:Β€$'猟4žlψ|#–{ΐψΑ°7œÜdδδ@Ex<‚Aθ―±6o›7¦Q#‘›› §Σ‰ΊΪZDΉ1ΛnHž°{ΰ4΅Ž.ψjk»IA€%7žEw²}Ο&‘䨫―Τ°}©΅-bc㐖š†όΌaˆ·Ζ_΅ζΎRΊz½₯³ζΐ`0ΐΚ(λ^yεέ€ θkβεΒμ€Yω…#1uϊΌnŸqgVδU!ΞΘ ’’εΫw€‚Ι“'"33,ΛF ŸΒ šT­FHx!…Γ…ΕΛΡuξμ™*GZj* ΙRρςεK¦^«½”eΰYt,ΉΉ@ήΊzh§λ5šΣ1vά8x½^466Β_UψέW\θxέΈa£ξΉoΏ‡φͺ*΄΅΅Αοχ#'?†§~)z`SDUa:xφΖ&Έέn„ΓaX,Ď ΗάΩ}N'?H§ΗγFssS―⼚ˆzξ…ηF̝=Z­˜.JΑ΅―½φυ\ά~½ VŽ2›€ ƒρgBQ%΄T–ΓΡR>܊%©‡a"¨©©ΕŽς ”’xό8Œ5‚ ŒFCjZzFξθ±£ΗQ46ΦlŽ–E©E ‹ΝG=)Šay̘nŸ^«ύΫ²eΛia–cN)bG‚ΕbA8†Ϋν†­© Ρ‡ŽκΐœΫB‰ /^ˆρ“&Βοχ£΅΅΅΅Θz{Ν΅˜zM ΈΎŸ‘ύIπ)EτΙ 4d₯CΠ뻝Sbb`ΟΞ³g#ζ΄6;\1Ρ@bxž‡F£9ƌΑ€`u-W€ε[υίηκ/q‘€ςςreφάR…σœ.'Š ‡GζΌ{;!W]v₯s}>:::ΰt:‘( X–!Z­)Ι©¨«―ƒͺ*ω‘p gΪΤ•——ί°xι—γΧΒ€v3€9ωE˜0i&T%Œ–Κr½_FV扄Ό(ZΔT4ΆuΑεt#33V«ωωy(,*ΰrr²’Rγ²²³2GŽ9.#+#±£³³νφψ9ŽΥΖΫlv8»Ίbtz]Φπ‘#Θδ)γ=n¬&/'―Ά’’’Χ²ω~Fφύ΄W,CΖ°aPU.— ‡Αƒ‡Ώ{ί€ΚAΦλΡρ xςdΘ²ŒΆΆ6ԟ9ƒΌ?ΏFΊF_ ν7ηκ? oθ<:'ŠHc κΞVΑf³‚΄’BxωA―²f»7’6nEΧι x<ψ|>0 kV&‚wΝƒ*\=τ2΄QΏΕωΰŽ‡Ό¦&ψΥΞυω|θμμ„rώΑΦσž9ΟjΗΌΫοθτ{@cΰk@…s(ϋ6΄ͺJ>hqZz6&N)’Jh,GΘwι’}- `iζI€Ζρ¨――ΗG|Œ¦ζfPJ ‡ΠΪΦ†ϊϊz8N0 Γ$%%ζ-[Ύτ Š ΅ήίήΦή1cΖ4 ƒoJbψ}—Λσ”ΟγYe6zyδ‘«W9…wΒ’•yΨz<8NF¦­*•šύFΓ0°Ωl¨­ͺBζ;€ ήΨ% 7F? ΅ΫΡ–`…ήh„Ωlƒϊ§žΐW`œΫ gR4QQΠh40υ:t*2 g«+Τθ°~ύz₯tξœ#x¨³³ƒ1›coΏζšϋrGkk+!ˆGBBdY†,ˈŠŠŠœgŠ2ΑjGm] @ι„9sKΉ-›·mΏ†’Φ,+R² w$$§bFɝ ZΟξDΠλθυ:ƒA’I#Πjσ‘ΡBMu-Ž}q'ŽŸDuU5κκκQQQ‰–¦V%%%‰Ρj΅lzzjQ(:ΈΠΩaΓ²³³²455΅ΘΞΞBaa@l6žγΩ‡Gύκ‰'.Y„ΤS£ΫΜCόŒιHHLD(‚ΣιDkM Μ›ΆAΧ9°½Ξ}χΫΘ).†ΕbέnΗΩΚJΔ}Ί†Ζ¦₯w\£ίp‘€ΆΛ‰0Γ +Ϊ½^‹Ε9Ζ G0}]CΏΣœ.x’ŒP“Αυ4αΝfHQFψ‘±Ω―(tΨΊykӜΉsΒζž;Χ€˜˜XX­ρƒΦ\οωΌ«««;jNJ ΄Z- CD슒ΐh4FΞ7›ΝˆŽ6£ΎΎfΜ½½Τ½eσΆ3ΊƒξΥ`[F3Θύ±–x”Μ]–εΠ^½~W[―Χqœ)…3‘Υj ΅ν‡κ: ?)—ˆΠ ZJρ9@ΧBτ~Ώ?½κl΅œΕθt:&-=΅Θn³qμΨ‰š‘£†ηŒ5RΘΛΛE8ƈ‘Γ!Š"ΪZΫ8†'δδρS—TΝίΟΘΎί?,˜iιbdδεB–e8NΨm6HϋΒrπΘ€Κ’eΙέ°N¬¬,tuu‘¦ͺ όώƒ°^ΓLeΈBΟωgHθήvηšγΉ&―Γ§ ιuœ^―Gbb"Όq±ΪμNMk[WΏΣkhτu$%jKΛσ<΄Z-x6VζOœjϊmυΩ7–Η-›·ξ™3·ΤH)n«­­!©©i—τΩϋ*z—œ+Š"EA(‚Ιd‚F£9ί$ Fάd/»Εb…^o@CC=2oξœ9 [Άl=>Πγ΅`~€y ΐ?™’c0{ή=x-:λΓcοέΏ‡eX$Ξ€ΖƒΖ†Zœ8vZΞκΪk§όφΣΚ?UœόkΕιΚΟ+NWξ¬8]ωzΡπB^UΥ™΅΅ub^^«4LFzzauMΝή³grσr²Žύ’ε‹cΗύV«%*++‹œ:[;Άmό˜J²D)%ί|ςε­+έ―¬¬Œ9]qj3-ΝΝΝEIiwπ††Ζ3πρϋ·M2yόΔβΩP^Ύ5Υ5 ”|oΝ«/Lη“O>y<;;ϋ…άάά(I’`·ΫΡΪΪͺtvvώχβΕ‹Τίrxηwς­Vλ‰)S¦hΒα0šššPSSγŠ‰‰I*))Ή©†άtB€ 6$…ΓαšaΓ†ι“““‘ͺ*ͺͺͺš;::†εΎό«Aρ‘θΣόeπЊ‘Ιγ‹£, Μf3B‘j ι~ϋϋSB—«ΟT†°A½6-,π‰$š‰,;% ((TU…,ΛGŒQΥ°V»ΆΟ˜'q¬0fτ8”Μ*|/IZZZ ( t:#bομμŒΤμ_ϋζ-qκτIπR†™ύΜSΟτ»εΣ^]Αή Bία9ž™=kI€Χvm΅ϋz}Βq€Ν€Ζ‡ΞφfμΩ΄‰Žν("‚"ΤκdνUwDQM`Η©„πQQQΠλueYυ9ΊΌ/@k6 @₯Α$Y=ΖΠ/£ΌHζh!ψψc#r'Χi4Έ\.Ψl6΄>κΝόΫ;'‰ͺφiΊ’° x}·\›œœά4aΒ#ΗqhmmΕΩ³g@ ΰώϋο΄Ρ·Αβ†­^»σηΟo{ϋν·Kkjjvr'$&&"===5 νe(³žBάΧG‘*’?έ€N£Β(!N,¦mΈkώψ„7ίϊθκΘP ƒ?ƒ@«š4”Qώΰw»’:vό(Οq,¦MνήΎγ8$%%‘­­-"ξψψxh4X,ΨlΆΘt‘ΩlŽ$:»t.Bαjjͺ£ˆͺ~ώβ‹1cεʟυή~ΎώσφNBι[ Γ23f/ ±–ψœ­h―=Π«ΘY^‹Τ’Yτf΄4ΦcOωJd†%ƒ‡₯lδέI©ωηΊΓ 8œΠŒˆ†(ώξΘΠΦ//Ρ˜rA<.t-Ό™EΓ Σιΰχϋαρx`«FςϊMQ„ͺ·υωχ€%\y|||eaa‘Qttt ¦¦F …Bw܌"1άσ`σώϋο·,^ΌΨ ηλυz&&&Η%ΆZb¬†£Η-ύI‹ !r,‚‰ t:h΅ZΔΔΔΐΗ2πz½Π5}mpψ2ΪZž<“›ωYΪΦΚ¨ͺŠΤΤ4έ-ƒž’(ŠEz½>βό … Š"(₯‘ΰ‘„δdη’½½ n[B͟WϊΑ¦MΫΥ/ώΥ1•YG£™^z'IJIGΠcC[Υ.Π^<Θ]‡Ο―3‘‘ {wl’*TY/Ύ—αKFΘΰF`ι ηΤΙΠ—Μ@N^DQ„ΣιDGk+Ψ»]UΣοτκϋN~VQABRRͺͺͺ”@ πγεΛ—Ώ{ΜnZ‘ΐΪ΅kάsΟ=Ωαpx΄Α` V«’Aoqϋ|Π6τo;,CK+μΦ8°ρρΰ8<ΟΓd6Γ£Σ§*ΐoνρπΕοώO‡gΟ-­$ KZZ[†a’’ ΰK±Θ¨»N§ΟσΰyΑ`0"v¦{BrsςΠ ―ΟkRAά>wώΪΝ›7hΰζ«Ός&2”Ω Γm3n'i™9ϋ]h9³ͺryo/­1©ΓKΑ :TUžΔΑ½ΫA A!‹K»nΫ …<ώu]Œ1ƒΉο^d J)άn7uΜ4nϊ3K—.}Ψαpill„ΗγAVV K°Ÿ[’QŠΔΟ7ΑVQYψ’Ρh0lΊέΚ|½Eρ̏Ί–PςκΎύ{pθЁH€\žηίν  αp8 ͺj€%Bίο‡Χλ\Γqξ^x, @‘«BήτόσΟ_s¨θWΔHΜJ`œpΫ,ddηA ΈΠ\±Š|ω5φ:“)…₯`9 *NΕα;@Aέ Uoβ]eύ΅ΪΤgΞ%w#Ύ°&²`Εvφ,7lξs—­‡ Εΰ'OĈ#ˆΗγAcc#ΊΊΊŽ,Y²δώ딃Aγ¦:,]ΊtB{{ϋΉΦΦVψ|> +,„ωGί‡h‰»ϊΕ ψό0μ?{s <DQ„ΥjEάΨ1p”\²Υωugεʟό• {χοΑ‘£‡/»ΥjˆέιtFšμΡΡΡ ”ΒησΑοχGύAƒE —œί±•ŽdXυ³—^zι)>ς«ΗPf3₯ˆ[<ΉωΓ!…|hΨ₯—আ˜d€”€ey=΄Ηο:AΤY?|»jΛ@°—LGΜθQ°Z­‘ε§φ–Dν=ΑӿƎ?-ΚΒ;1qςdψ|>477£­­­eι₯“ωƒΚ-!tθμμ,jhhpwtt  )=dΥΏ@ΡτΝ½‡Έγ'>u.— ’$!#+ ¦Ω³Lνϋά8λŽΉθ|π>ψ†ευ37_ς̏Ÿ}ΐ`Οή]8yςKߍFƒΈΈ80 ƒ`0·»»Ϋ­Σι B|>@δ‘ΧλqΟέ‹aΠ@) ?όυ―­ΉμΝ―ΐ@U™-”"aΤΨI(1²DKεNΘε»9&K’σ§ ƒ{·βΜιc  ΤiOΌ…c)_a>lίΈωs»#Oυ‘`b’KK™ I’ΰρxΰrΉ>UΨγ'ϋeƒhΠΓυϋ1¦x:::ΠΤΤδΡj΅EύΝύβ–ϊc=‡ΓΕ΅ΥΥrgg'TUEJn.ό?ϊ§~­7€€ [ΰ8]·Ϋ Ώίί½p$'Ύ»ζAεϊ6α–‡˜»`β·Ύ qΩbψ ς’-ΐͺ?ϋ_„Π' |ηvœdY†Λε‚έnGθΰ!Xχ^ύϊ i|`ǍEFFͺΟVAΏs,ϋς›ΖΊ­e½AάR5zΦƒG@@}}=\.’““až?ξ ύ !–΄q+ΊNž‚Ηγίο!±iiά1Šζ*έZJ‘ϊΙηh?x$ %³ΰX±”Έ3+ŸύWJπ<₯[Άnκid€;ΎywΝή3§Υj‘Υj#5{Οτ₯©)iΈ}ξ|B@~φΒκŸ_vθ₯o"^ΰ˜­(ΘΞ-ΐΔi₯Uao91pω)ωΈ΄‘°€†(†±}ΣΗ°wΆ„§2½sελPo•eαXq/ JfA’$ttt ύΐ!€¬[ΥQrEΰ\x,™™‘™ Ηη©ΣHΪΤΏ½³g!jΤHδηηGͺ¨§+Ώχ†-Ό&nI‘@ΪΊυπUΧ ©© ^―ΉωωˆώΞCπgτ=ξ#Λ°lہΞΪZΈέξΘ ²„1cΰXpυΘ΄Œ$!ε£OΡxΰ`€+‘3»K^ƒsρ3O?ϋ€ξ{}CέEΣh=Νυ@ €@ UU‘Ρh (₯‡›ž !23²"xYύβKΟ}ηΒϋ½τMΔs2³•EYy˜4u6 RΈ;κ /Σ %€5c βR‡# `Ληΐaο@ΧΉΊδΙ΅˜S!°/ΉΩ₯³ l6Ξ<„€O>Ϋ‡pΩφw aΜh˜L&„B!ΈέntΦΥ!Ά|˜~„Ϋvt&ΖΓλυ’©© žͺjd|τι€²u3pΛ rήxΥΥ‘ΈsY99ύŽ;ghlsβœGd>55ΦιSαyυΠΌœΧΛη›Πpδ 8˜cbΏPμ…E=‘k ₯τ΅_|ξ^ΰK‘‘ž•‡ΫfάP wg-Δΰ₯•2%ρ™γ“\€€Ο‹Mλ?€Λε€·έmtIΩ_0ΰΕφ9³?k&bbcαp8PΕ1X7lξΣT˜wx!,S§ 5552Κξtt9vQύp %& ΄δnŒŸxqΌ·μ7―}οΙ--t¨*²χmœ«ͺB{{;Βα0’³2!>ωC¨|ίc(&nίΧιJΈέnx½^¨ͺ kz:Τys toΓtEτmν0l߁†σΣv©YY0Ξ){Bρ€³F‘«žϊΙχΌ¦ͺ*6oΩˆζ–¦‹ΔΡh ͺ*‚Α B‘TU… ΰ8ͺͺ" EΧPJ1zΤŒ5XJθ›/ύμΡε‰|ϊ\@Uakψβ²"'„ 1{Μ‰ωπϋ<Ψ²αCκσΈ@?Έ‡©ί(+Η€ƒ’ΉΗŽ‚Ύt2rsΰrΉp’ϊς]Π7·^υZY«…2o²² ͺ*Ό^/άn7\§N#‘|WŸmPuZΨΎυΖ#£½½ΥΥΘώΛ[`Γ&Θ_'·ΆΠp‘0ίyuUU‘XkiΓ‹ΰωΑχϊwŽ( 7l†­Ί‘P¨{ kΔpΨοΎ³Oi˜OW‚μΩ‡¦šΈέnŒ zϋlψs³œ7B €Η)‘S›6o@kkKDμ‚ D|ήƒΑ Βαpδ!ΐq\w ΆσbοΉf|ρD Λ/‘ΌΫ²ξm#2s†aκŒΫ»C@UνFΘiœ7€ΐ’1QΦ x\]ΨτΩϋΤησΏ|βmυ±²²οξΟL½cŠŠ‹αv»ΡT[ μڍΨ>†s,^€„Ρ£`4:ΫίVS‹„-ΫϋdTe€ΖGΏγƁeYtvv’ͺik>χυξ|]Έε…tG΅~ς9j«ͺΰp8`0§Ώ{/|§NEšπ²,#%3̜HΡ¦>₯‘Όa l’½½@E3gΐσΐrΘηkށ°|ωr%;3ο!ς,ΛΨ²m:m5γ{βΒαpdΤ½'δ•ͺͺ‘Uu±ι—wC Ψ0¬p¦Μ˜ -gvΒw‘@\r!Œ1Ιp»ΨΊα#  %xφ‰·ΥŸ8S­ξϋ—cDi ΪΫΫaΫΏ)ŸoμΣυ’ΙnN)rs Λ2Ό^oχζ 'Oυ+\sσ’»‘0r$RRRΰt:Q[S νΑÈΈ₯¦Κ―ΘߍΠ ±|7ĊJ4ž;·ΫŒŒ psJϋμ&K(Έ~μgΞFΎθt:$’λž…}I'ͺŠΤ?Cˁƒθρΰ+(-γΎ%PYvΐcρΛ—/WΜΡqί€ΐ'’$a󖍰Ωm‘ώwO(ιQχΤμ=1νTUEγρυΨώ?+ ]9f"Š'ΝUd4U”ΓοΌΌ_KtB.tΡ πxœΨΆαc  ‘ψΩ“o©Ο 4/@φϋ–’`N)TUEGGZFςǟƒ(WοPt-^„„αEΠj΅G’Ξ3g‘ΈqkŸ7G£M`G Gaa!άn7κλ!VT i{ίϋφ·WB€¬χ>†£ͺ­­­πz½HIKƒͺλϋNF·ΊΓGaok‹ŒΒ'&&Βζ1£˜˜e·΅ΆAθ4ύΨJIŠ2">1^―---pΥΦ!뽏š­›–[Ξ3ξϋιΩsΑ·I9q -ρty=π>ЍCύ α«omƒΓΖjxΝE›Νπ鴐OW€;ΡδJpα0X›±fθbb`·B2E±yyΪ7ή}wCΏ Ί€ςςrεΞ;|¨¨ΚE‘³Ο!))AJ)!‘ZžR E αθΟ’jϋoΐq<¦—ή‰΄Œˆ7Omψ•yrB81IΓ 3Yαρ8±uΓΗ4 όۏήQc ΆχpΧώησΙcFOMΝΜ$‡u_Cτg‘o»jT)@Ψjί½Θ1”RΈ\.tuuΑ}δ(’·–χΛΑγE{Ό­N'Ό΅΅Θϊί·―ή Ψ}«yΖύ] PŠ˜γ§΅{τ'Oθ>†sθLL€&6‚ @―ΧƒΡλΡΞσП8Ω§¦‘ΰφ@ αŒ‹ήdBRJ ρ…B‹-ςΌυΦ[ή${γƍς΄©Σίγxnͺ’Θ™ΝΝMHNNΟ ‰έΣ~{ό-ΨjvΓdŽΕμywΓbMDΠΣ‰¦ΣΫ‘\naU CLάn'Άmψƒв'ήQ} 6χπ駟>bML|ͺ`ψp½ΛεBΓ™3ΰ6m…Ή²»Œ2 ΊξΏ©ΖΓh4ΒοχΓιt’ύΤi€|ψIΏczˆ:vΡ{φ#ζψ©Ύ5ωoA‘έ5έ Ξ€iοΨ››# _, βƍ…cNίψbOœ‚²gšκκαυz1jΤ(³ΕbyφΓ?œu-φ•••XΒ/ ΐ`(ˆνε[αυz"Νψζcaχο—Γg«EfΞ0ΜΏkLΡ±πΪΞ‘ιτV¨½¬'·€„1.· Ϋ6|Τ#ςŸxGύΧb/¬[·nŠΥjύ·ΡcΖΔz½^474@ή΅q_τ=,½}N bǎ‰lŠYcΎο8ίεWΦ q“‡Ό,!ηT¨‡(%Χέ…!ϊT% ς󝱱1=Ž(ι™ sfΛΎΪΊjCu}Ÿ–)&lߍ&K\~«AΓqƍ—rπΰΑΧ>όπΓy‹/n¨}O?ύ΄…^XFΩ&”οά†©' zΣsh;½,ΗcςΤRdηͺ*:λΓΩΪ{͝‹Έ΄α|ΨΊρ#ƒ~Šxβυ_jcλΦ­K1›Ν;vlΊΟηCkK œGΎp₯oί}Ά―^™ιQζΉ³σ223ωˆχ›Σ‰PεYgόρΣU_Η“„P†%Lο;Tά€ά”αžo&Φ¬Y­ΥjχfggY,˜L&x<TTTœ°ΫνS—/_ή§P%ώσŸ΅111;ςσσ'¦€tΈ8|ψπQ‡ΓQΊ|ωςk δψκ«eζ°Δo!’³XSχh°ζ ¦ΝΌ&s,Đmgw!δλ}Ež!6)3!I"6―n§JΒTHIσγ™•Ο^“ΠΧ¬YcŒ‹‹Ϋ1~όψqΠ‚κκκ Γ”.\ΈπκΦ­[§7 ŠŠŠFτόμv;κκκΞ„B‘©Λ—/Ώœ—ΟηzX»v풘˜˜?₯§§ΗY­Vθυz444ΠΊκκw ^~ω‘Ύ¦SΉd‰EΝΚΪ4¬  011~ΏΗΏψbGβ[oέejjκ>Ρ°>£eΆ¬t|9ΐeε x °, ―£u‡zοZcR‹J”oύΆŽV˜sf‘Γ8 ¬ͺ<³xSω/b—#*б?ώψ'cƎm0ΠΡс³••g΅σ²ίx£Ο-gžzκυ¬άάYYY$ΐf³‘©©©Λνvγ’%KΦΔΆK ½Όώϋo¦¦€<˜”„ΨΨX°,‹Ζ3gUΟώδ4>Σ§Z ‚ρV‘kω’Έœ‰8«ΥŠφΦVΪ²ξΣ@όGŸ ΈFR‰’•)Ž Ο ΰ EA{‰0ΰ-1‚,K`8‚>²’@’ΊΌ$wq²οNpη’»b“ή₯OJI!6› 5‡Λqο}θΠ΅uτyΤΜW§3<φέ،ΒBFQtuu‘½­ -­­o.Y²δύ΅ι"·\ύF!Β£]•gξΥ ‚ ˆŽŽF\Z*γΈ}NœR[Η†ϊΆα‹Σ†¨[Ρ Υ‚;I))$0c†Αisbφ l­3CYh”σ( πΫ»·§4Ξ0 ΰΟ r0ˆ‚ˆŽ§Δ6ΣD’'‡β!‰Σdb&ι8τ/θL―zέιuήεΆΆ©1±¦iN*ž››ΡͺXAƒ,Ψ^€D3±aA<νϋ›ρΚ™ΟOεtχ{žh(|Α >~³P4{2B)$@ΐŠRlz€θβIσ4œΑ‘ ηa*)Λ²˜}ρšξ‡œ…7…Ι‘Λ±Ϊze₯±γΗ~Ώ^―o'^―†U9ίُ˜ν»Λk»εΝ;kί)sΏe…jΕ‘\Θd2¨ΥjπΚΌΙTn—”»έX ‡αΥεA™«AqY)ήH³°Ά΄ω֝βϋ‰Σ*DΪ³υπx<˜›œzώ€nTX0£λΛ0^8½^@ »”69]gΧεΎ1CΫ?pθ]ΥΌ}Ε7φnc!d2Y,³Έ‘σηΰœΖ‘ΡΏ’^+ί:{žσJ%€R)κ.^Δ_αBŸ p.eπ»ΘΌ}ψ[m0[,ΰ8σ6‚ΟzaκV Η}V ]ΓΩMgΜέ.Y£cPΝΞ%έ™Gθ:Ί`E>Ÿςω|ˆD"0–•­-XΣ¨­ezЏu‡@΅–Ο±|»UΚQμ».œ“Χν¨mlD ΐΒΒή ΒΤ-¬%¬Qƒo½„’ŠrD"‘ΔsξΕKνh<ό@ƒ.T4 γύ8γ_‚Α T* ƒϋΖ5AΩγ z‹ΦΑDξ\uS#Ψ―n₯•;·[’ ά·ΫQέΤ”Θ{[θ·’Έ£Ψ’Ÿνƒμk(<^ ₯R™Θ΄_šœDαοΑY‹ AO‰άΕB>4vq1Ρψb2™ 5›αiVά‘΅Ί ΣΟΏΐTY¦ LIDAT60˜Θ«li†σf› ']Ο{«hސχ68„’Žί’Κ{ΫΘ]ΪΊ“0™L‰·μ¬Σ ωΰNWfφΐΡ §Hί?ψΔ¦Ζ—Γ•ΘΎr «ω‚ώ9lί2 ξvavx,Λ"O§ƒ±ΙφrzΉs;Ιu© †F tωωρΌ·θ;@Ζ ƒ]Υj‘hmAYUUbΘ½^/–Η_Aί',™ΌCƒž"†Š:οo:»P(`ψ€ žΆ«IΗX­S-,Bυπq"w΄²ΚfKZΉs;…«;e³eCήΫ+¨=…r^XwϞ›Χa8ϊ^)βί―QΤΩ f'ξq= hΠΣ σω‘ΆΒew$β§ ΄'Nΐ{κ€ΰυ΄γΐσ>ΜMMγ8T›ΝiηΞeΪry)ψ«WPsκ8ŽΓΏΣ3ΐ³>δ%™χΆ‘χ΄Ϊ ₯ˆ^―.»κώΘΈ}Σ~΄'Ρ §I7<†Π{₯εGƒ©©Ni½Β'Ο±άo…ΓnOδΞ…oΆ₯E•)αΒν·PΧΛ{sΨνπχφΒπψYJλ15Υ(?rxS)bθε8tΓɟn#FƒΎ L]έ`γ½λλ₯ιΌΛ,ΎΧ go"wβl=i”8fJ ζhloρΌ7go?Šοu§Όήz¨εzΓ ϋj¦ΤΧ#ομΏk8{4‚φΙs,ζΖξ˜ ϊ|L&ίχ>&EIΗ]Μggcν΄a— [ς%;E1cƒmt ‚|8‡†Qq7©Ό·­H¦ώΣαΐΟcqr*VŠάQΛ{Α>Ί~³ϋ&.ΆΜ2 SΆΥηύ•ΰN‡raΪΎv…}LT"A ΄r‹l_R§awܚF•‚|(ηζ“ΞPίΓΐΣp"#4#cΠΜl}μ›ηy[υ“žςτΎ xΠ+ϊ6ROΟ@==³mλeE"PΟƒ}/Θφω·οIˆη‘ν΅"­MςAτ7:!"@ƒNˆΠ "4θ„ˆ :!"@ƒNˆΠ "4θ„ˆ :!"@ƒNˆΠ "4θ„ˆ :!"@ƒNˆΠ1U˜,F Ξέή‰.Hθ‡%D˜©ίν-B!„B!„B!„B!„B!„Bˆό^νf6Τp’IENDB`‚sphinx-toolbox-3.9.0/sphinx_toolbox.svg000066400000000000000000000735271475757360300203570ustar00rootroot00000000000000 image/svg+xml sphinx-toolbox-3.9.0/sphinx_toolbox/000077500000000000000000000000001475757360300176205ustar00rootroot00000000000000sphinx-toolbox-3.9.0/sphinx_toolbox/__init__.py000066400000000000000000000065041475757360300217360ustar00rootroot00000000000000#!/usr/bin/env python3 # # __init__.py """ Box of handy tools for Sphinx. """ # # Copyright Β© 2020-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 sys # This all has to be up here so it's triggered first. if sys.version_info >= (3, 10): # stdlib import types types.Union = types.UnionType # 3rd party from sphinx.application import Sphinx # this package from sphinx_toolbox import ( # noqa: F401 assets, code, config, confval, installation, issues, rest_example, shields, source, utils, wikipedia ) from sphinx_toolbox.cache import cache # noqa: F401 __author__: str = "Dominic Davis-Foster" __copyright__: str = "2020 Dominic Davis-Foster" __license__: str = "MIT License" __version__: str = "3.9.0" __email__: str = "dominic@davis-foster.co.uk" __all__ = ("setup", ) def setup(app: Sphinx) -> "utils.SphinxExtMetadata": """ Setup :mod:`sphinx_toolbox`. :param app: The Sphinx application. """ # Ensure dependencies are set up app.setup_extension("sphinx.ext.viewcode") app.setup_extension("sphinx_toolbox.github") app.connect("config-inited", config.validate_config, priority=850) # Setup standalone extensions app.setup_extension("sphinx_toolbox.assets") app.setup_extension("sphinx_toolbox.changeset") app.setup_extension("sphinx_toolbox.code") app.setup_extension("sphinx_toolbox.collapse") app.setup_extension("sphinx_toolbox.confval") app.setup_extension("sphinx_toolbox.decorators") app.setup_extension("sphinx_toolbox.formatting") app.setup_extension("sphinx_toolbox.installation") app.setup_extension("sphinx_toolbox.issues") app.setup_extension("sphinx_toolbox.latex") app.setup_extension("sphinx_toolbox.rest_example") app.setup_extension("sphinx_toolbox.shields") app.setup_extension("sphinx_toolbox.sidebar_links") app.setup_extension("sphinx_toolbox.source") app.setup_extension("sphinx_toolbox.wikipedia") app.setup_extension("sphinx_toolbox.more_autodoc.autoprotocol") app.setup_extension("sphinx_toolbox.more_autodoc.autotypeddict") app.setup_extension("sphinx_toolbox.more_autodoc.autonamedtuple") # Hack to get the docutils tab size, as there doesn't appear to be any other way app.setup_extension("sphinx_toolbox.tweaks.tabsize") return { "version": __version__, "parallel_read_safe": True, } sphinx-toolbox-3.9.0/sphinx_toolbox/__main__.py000066400000000000000000000030031475757360300217060ustar00rootroot00000000000000#!/usr/bin/env python3 # # __main__.py """ CLI entry point for sphinx-toolbox. """ # # Copyright Β© 2020-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 sys # this package from sphinx_toolbox import cache __all__ = ("clear_cache", ) def clear_cache() -> int: """ Clear any cached URLs. """ if cache.clear(): print("Cache cleared successfully.") return 0 else: return 1 if __name__ == "__main__": sys.exit(clear_cache()) sphinx-toolbox-3.9.0/sphinx_toolbox/_css.py000066400000000000000000000104541475757360300211250ustar00rootroot00000000000000#!/usr/bin/env python3 # # _css.py """ Internal Sphinx extension to provide custom CSS. .. versionadded:: 2.7.0 """ # # Copyright Β© 2020-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 MutableMapping, Optional # 3rd party import dict2css from domdf_python_tools.paths import PathPlus from sphinx.application import Sphinx # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ("copy_asset_files", "setup") installation_styles: MutableMapping[str, dict2css.Style] = { 'div[id*="installation"] .sphinx-tabs-tab': {"color": "#2980b9"}, "button.sphinx-tabs-tab,div.sphinx-tabs-panel": {"outline": (None, dict2css.IMPORTANT)}, } shields_styles: MutableMapping[str, dict2css.Style] = { ".table-wrapper td p img.sphinx_toolbox_shield": {"vertical-align": "middle"}, } regex_styles: MutableMapping[str, dict2css.Style] = { "span.regex_literal": {"color": "dimgrey"}, "span.regex_at": {"color": "orangered"}, "span.regex_repeat_brace": {"color": "orangered"}, "span.regex_branch": {"color": "orangered"}, "span.regex_subpattern": {"color": "dodgerblue"}, "span.regex_in": {"color": "darkorange"}, "span.regex_category": {"color": "darkseagreen"}, "span.regex_repeat": {"color": "orangered"}, "span.regex_any": {"color": "orangered"}, "code.regex": {"font-size": "80%"}, "span.regex": {"font-weight": "bold"}, } tweaks_sphinx_panels_tabs_styles: MutableMapping[str, dict2css.Style] = { ".docutils.container": { "padding-left": (0, dict2css.IMPORTANT), "padding-right": (0, dict2css.IMPORTANT), }, "div.ui.top.attached.tabular.menu.sphinx-menu.docutils.container": { "margin-left": (0, dict2css.IMPORTANT), "margin-right": (0, dict2css.IMPORTANT), }, } def copy_asset_files(app: Sphinx, exception: Optional[Exception] = None) -> None: """ Copy additional stylesheets into the HTML build directory. :param app: The Sphinx application. :param exception: Any exception which occurred and caused Sphinx to abort. """ if exception: # pragma: no cover return if app.builder is None or app.builder.format.lower() != "html": # pragma: no cover return extensions_selector = ", ".join([ "p.sphinx-toolbox-extensions", "div.sphinx-toolbox-extensions.highlight-python", "div.sphinx-toolbox-extensions.highlight-python div.highlight", ]) rest_example_style = { "padding-left": "5px", "border-style": "dotted", "border-width": "1px", "border-color": "darkgray", } style: MutableMapping[str, dict2css.Style] = { "p.source-link": {"margin-bottom": 0}, "p.source-link + hr.docutils": {"margin-top": "10px"}, extensions_selector: {"margin-bottom": "10px"}, "div.rest-example.docutils.container": rest_example_style, **installation_styles, **shields_styles, **regex_styles, } css_static_dir = PathPlus(app.outdir) / "_static" / "css" css_static_dir.maybe_make(parents=True) dict2css.dump(style, css_static_dir / "sphinx-toolbox.css") @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox._css`. :param app: The Sphinx application. """ app.add_css_file("css/sphinx-toolbox.css") app.connect("build-finished", copy_asset_files) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/_data_documenter.py000066400000000000000000000112501475757360300234660ustar00rootroot00000000000000#!/usr/bin/env python3 # # _data_documenter.py """ Relevant parts of :mod:`sphinx.ext.autodoc` from Sphinx 3.3.1. """ # # Based on Sphinx # Copyright (c) 2007-2020 by the Sphinx team. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib from typing import Any, Optional, get_type_hints # 3rd party from docutils.statemachine import StringList from sphinx.ext.autodoc import ( SUPPRESS, UNINITIALIZED_ATTR, ModuleDocumenter, ModuleLevelDocumenter, annotation_option ) from sphinx.util import logging from sphinx.util.inspect import object_description, safe_getattr try: # 3rd party from sphinx.util.typing import stringify_annotation as stringify_typehint except ImportError: from sphinx.util.typing import stringify as stringify_typehint # type: ignore[no-redef] # this package from sphinx_toolbox.more_autodoc import _documenter_add_content __all__ = ("DataDocumenter", ) logger = logging.getLogger(__name__) class DataDocumenter(ModuleLevelDocumenter): """ Specialized Documenter subclass for data items. """ objtype = "data" member_order = 40 priority = -10 option_spec = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option @classmethod def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any) -> bool: """ Called to see if a member can be documented by this documenter. """ return isinstance(parent, ModuleDocumenter) and isattr def add_directive_header(self, sig: str) -> None: """ Add the directive header and options to the generated content. """ super().add_directive_header(sig) sourcename = self.get_sourcename() if not self.options.annotation: # obtain annotation for this data try: annotations = get_type_hints(self.parent) except NameError: # Failed to evaluate ForwardRef (maybe TYPE_CHECKING) annotations = safe_getattr(self.parent, "__annotations__", {}) except TypeError: annotations = {} except KeyError: # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084) annotations = {} except AttributeError: # AttributeError is raised on 3.5.2 (fixed by 3.5.3) annotations = {} if self.objpath[-1] in annotations: objrepr = stringify_typehint(annotations.get(self.objpath[-1])) self.add_line(" :type: " + objrepr, sourcename) else: key = ('.'.join(self.objpath[:-1]), self.objpath[-1]) if self.analyzer and key in self.analyzer.annotations: self.add_line(" :type: " + self.analyzer.annotations[key], sourcename) try: if self.object is UNINITIALIZED_ATTR: pass else: objrepr = object_description(self.object) self.add_line(" :value: " + objrepr, sourcename) except ValueError: pass elif self.options.annotation is SUPPRESS: pass else: self.add_line(" :annotation: %s" % self.options.annotation, sourcename) def document_members(self, all_members: bool = False) -> None: # noqa: D102 pass def get_real_modname(self) -> str: """ Get the real module name of an object to document. It can differ from the name of the module through which the object was imported. """ return self.get_attr(self.parent or self.object, "__module__", None) or self.modname def add_content(self, more_content: Optional[StringList], no_docstring: bool = False) -> None: """ Add content from docstrings, attribute documentation and user. """ _documenter_add_content(self, more_content, no_docstring) sphinx-toolbox-3.9.0/sphinx_toolbox/assets.py000066400000000000000000000153641475757360300215050ustar00rootroot00000000000000#!/usr/bin/env python3 # # assets.py """ Role to provide a link to open a file within the web browser, rather than downloading it. .. versionadded:: 0.5.0 .. extensions:: sphinx_toolbox.assets .. TODO:: Handle non-HTML builders Perhaps just put a message to see the online documentation? Usage ------ .. latex:vspace:: -10px .. rst:role:: asset Adds a link to a local file that can be viewed within the web browser. The file will be copied from the directory set in :confval:`assets_dir` to ``/_assets`` in the HTML output. This is similar to the :rst:role:`download` role, but that role will download the file to the user's computer instead. This role may be useful for PDFs, which most web browsers can display. If the file can't be found an error will be shown in the build output: .. code-block:: text : Asset file '' not found. **Asset** .. rest-example:: :asset:`hello_world.txt` :asset:`hello_world ` **Download** .. rest-example:: :download:`hello_world.txt <../assets/hello_world.txt>` Configuration --------------- .. confval:: assets_dir :type: :class:`str` :required: False :default: ``'./assets'`` The directory in which to find assets for the :rst:role:`asset` role. API Reference --------------- """ # # Copyright Β© 2020-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 import posixpath import shutil from typing import Dict, List, Sequence, Tuple # 3rd party from docutils import nodes from docutils.nodes import system_message from docutils.parsers.rst.states import Inliner from domdf_python_tools.paths import PathPlus from domdf_python_tools.utils import stderr_writer from sphinx.application import Sphinx from sphinx.util import split_explicit_title from sphinx.writers.html5 import HTML5Translator __all__ = ( "asset_role", "AssetNode", "visit_asset_node", "depart_asset_node", "setup", ) # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version class AssetNode(nodes.reference): """ Node representing a link to an asset. """ def asset_role( name: str, rawtext: str, text: str, lineno: int, inliner: Inliner, options: Dict = {}, content: List[str] = [] ) -> Tuple[Sequence[AssetNode], List[system_message]]: """ Adds a link to an asset. :param name: The local name of the interpreted role, the role name actually used in the document. :param rawtext: A string containing the entire interpreted text input, including the role and markup. :param text: The interpreted text content. :param lineno: The line number where the interpreted text begins. :param inliner: The :class:`docutils.parsers.rst.states.Inliner` object that called :func:`~.source_role`. It contains the several attributes useful for error reporting and document tree access. :param options: A dictionary of directive options for customization (from the ``role`` directive), to be interpreted by the function. Used for additional attributes for the generated elements and other functionality. :param content: A list of strings, the directive content for customization (from the ``role`` directive). To be interpreted by the function. :return: A list containing the created node, and a list containing any messages generated during the function. """ has_t, title, target = split_explicit_title(text) title = nodes.unescape(title) target = nodes.unescape(target) if not has_t: if target.startswith('~'): target = target[1:] title = pathlib.PurePosixPath(text[1:]).name app = inliner.document.settings.env.app base = app.config.assets_dir node = AssetNode(rawtext, title, refuri=target, source_file=PathPlus(base) / target, **options) return [node], [] def visit_asset_node(translator: HTML5Translator, node: AssetNode) -> None: """ Visit an :class:`~.AssetNode`. :param translator: :param node: The node being visited. """ if not hasattr(translator, "_asset_node_seen_files"): # Files that have already been seen translator._asset_node_seen_files = [] # type: ignore[attr-defined] assets_out_dir = PathPlus(translator.builder.outdir) / "_assets" assets_out_dir.maybe_make(parents=True) source_file = PathPlus(translator.builder.confdir) / node["source_file"] source_file_exists = source_file.is_file() source_file_seen = source_file in translator._asset_node_seen_files # type: ignore[attr-defined] if not source_file_seen and source_file_exists: # Avoid unnecessary copies of potentially large files. translator._asset_node_seen_files.append(source_file) # type: ignore[attr-defined] shutil.copy2(source_file, assets_out_dir) elif not source_file_exists: stderr_writer( f"\x1b[31m{translator.builder.current_docname}: " f"Asset file '{source_file}' not found.\x1b[39m" ) translator.context.append('') return # Create the HTML current_uri = (pathlib.PurePosixPath('/') / translator.builder.current_docname).parent refuri = posixpath.relpath(f"/_assets/{node['refuri']}", str(current_uri)) translator.body.append(f'') translator.context.append("") def depart_asset_node(translator: HTML5Translator, node: AssetNode) -> None: """ Depart an :class:`~.AssetNode`. :param translator: :param node: The node being visited. """ translator.body.append(translator.context.pop()) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.assets`. .. versionadded:: 1.0.0 :param app: The Sphinx application. """ app.add_role("asset", asset_role) app.add_config_value("assets_dir", "./assets", "env", [str]) app.add_node(AssetNode, html=(visit_asset_node, depart_asset_node)) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/cache.py000066400000000000000000000026571475757360300212470ustar00rootroot00000000000000#!/usr/bin/env python3 # # cache.py """ Caching functions. """ # # Copyright (c) 2020 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 datetime import timedelta # 3rd party from apeye.rate_limiter import HTTPCache __all__ = ("cache", ) #: HTTP Cache that caches requests for up to 4 hours. cache = HTTPCache("sphinx-toolbox", expires_after=timedelta(hours=4)) sphinx-toolbox-3.9.0/sphinx_toolbox/changeset.py000066400000000000000000000161011475757360300221320ustar00rootroot00000000000000#!/usr/bin/env python3 # # changeset.py """ Customised versions of Sphinx's :rst:dir:`versionadded`, :rst:dir:`versionchanged` and :rst:dir:`deprecated` directives to correctly handle bullet lists. .. versionadded:: 2.11.0 .. extensions:: sphinx_toolbox.changeset Usage ------ .. rst:directive:: .. versionadded:: version Documents the version of the project which added the described feature. The first argument must be given and is the version in question; you can add a second argument consisting of a *brief* explanation of the change. Alternatively, a longer description my be given in the body of the directive. .. rst:directive:: .. versionchanged:: version Similar to :rst:dir:`versionadded`, but describes when and what changed in the feature in some way (new parameters, changed side effects, etc.). .. rst:directive:: .. deprecated:: version Similar to :rst:dir:`versionchanged`, but describes when the feature was deprecated. An explanation can also be given, for example to inform the reader what should be used instead. .. latex:vspace:: 10px This extension also adds the following directive: .. rst:directive:: .. versionremoved:: version [details] Similar to :rst:dir:`versionchanged`, but describes when the feature was or will be removed. An explanation can also be given, for example to inform the reader what should be used instead. .. latex:clearpage:: API Reference ---------------- """ # noqa: D400 # # 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. # # Based on Sphinx # Copyright (c) 2007-2021 by the Sphinx team. # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib from typing import List, Optional, cast # 3rd party import sphinx.domains.changeset from docutils import nodes from docutils.nodes import Node from sphinx import addnodes from sphinx.application import Sphinx from sphinx.locale import _ # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ("VersionChange", "setup") versionlabels = { "versionremoved": _("Removed in version %s"), **sphinx.domains.changeset.versionlabels, } versionlabel_classes = { "versionremoved": "removed", **sphinx.domains.changeset.versionlabel_classes, } class VersionChange(sphinx.domains.changeset.VersionChange): """ Directive to describe a addition/change/deprecation/removal in a specific version. """ def run(self) -> List[Node]: """ Process the content of the directive. """ node = addnodes.versionmodified() node.document = self.state.document self.set_source_info(node) node["type"] = self.name node["version"] = self.arguments[0] text = versionlabels[self.name] % self.arguments[0] if len(self.arguments) == 2: inodes, messages = self.state.inline_text(self.arguments[1], self.lineno + 1) para = nodes.paragraph(self.arguments[1], '', *inodes, translatable=False) self.set_source_info(para) node.append(para) else: messages = [] # pylint: disable=W8301 if self.content: self.state.nested_parse(self.content, self.content_offset, node) classes = ["versionmodified", versionlabel_classes[self.name]] # pylint: disable=W8301 if len(node): to_add: Optional[nodes.Node] = None if isinstance(node[0], nodes.paragraph) and node[0].rawsource: content = nodes.inline(node[0].rawsource, translatable=True) content.source = node[0].source content.line = node[0].line content += node[0].children node[0].replace_self(nodes.paragraph('', '', content, translatable=False)) elif isinstance(node[0], (nodes.bullet_list, nodes.enumerated_list)): # Fix for incorrect ordering with bullet lists node.insert(0, nodes.compound('')) to_add = nodes.paragraph('', '') para = cast(nodes.paragraph, node[0]) para.insert(0, nodes.inline('', f'{text}: ', classes=classes)) if to_add is not None: node.insert(0, to_add) else: para = nodes.paragraph( '', '', nodes.inline('', f'{text}.', classes=classes), translatable=False, ) node.append(para) domain = cast( sphinx.domains.changeset.ChangeSetDomain, self.env.get_domain("changeset"), ) domain.note_changeset(node) ret: List[Node] = [node] # pylint: disable=W8301 ret += messages return ret @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.changeset`. :param app: The Sphinx application. """ app.add_directive("deprecated", VersionChange, override=True) app.add_directive("versionadded", VersionChange, override=True) app.add_directive("versionchanged", VersionChange, override=True) app.add_directive("versionremoved", VersionChange, override=True) return { "parallel_read_safe": True, "parallel_write_safe": True, } sphinx-toolbox-3.9.0/sphinx_toolbox/code.py000066400000000000000000000277501475757360300211170ustar00rootroot00000000000000#!/usr/bin/env python3 # # code.py """ Customised ``.. code-block::`` directive with an adjustable indent size. .. extensions:: sphinx_toolbox.code Usage ------ .. rst:directive:: .. code-block:: [language] .. sourcecode:: [language] Customised ``.. code-block::`` directive with an adjustable indent size. .. rst:directive:option:: tab-width: width :type: integer Sets the size of the indentation in spaces. All other options from :rst:dir:`sphinx:code-block` are available, see the `Sphinx documentation`_ for details. .. _Sphinx documentation: https://www.sphinx-doc.org/en/3.x/usage/restructuredtext/directives.html#directive-code-block **Examples** .. rest-example:: .. code-block:: python def print(text): sys.stdout.write(text) .. rest-example:: .. code-block:: python :tab-width: 8 def print(text): sys.stdout.write(text) .. clearpage:: .. rst:directive:: .. code-cell:: [language] .. output-cell:: [language] Customised ``.. code-block::`` directives which display an execution count to the left of the code block, similar to a Jupyter Notebook cell. .. versionadded:: 2.6.0 .. rst:directive:option:: execution-count: count :type: positive integer The execution count of the cell. All other options from the :rst:dir:`code-block` directive above are available. **Examples** .. rest-example:: .. code-cell:: python :execution-count: 1 def print(text): sys.stdout.write(text) print("hello world") .. output-cell:: :execution-count: 1 hello world .. rest-example:: .. code-cell:: python :execution-count: 2 :tab-width: 8 def print(text): sys.stdout.write(text) .. seealso:: `nbsphinx `_, which inspired these directives and provides additional functionality for integrating Jupyter Notebooks with Sphinx. API Reference ---------------- """ # # Copyright Β© 2020-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. # # Based on Sphinx # Copyright (c) 2007-2020 by the Sphinx team. # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib from typing import List, MutableMapping, Optional # 3rd party import dict2css import docutils.nodes import docutils.statemachine import sphinx.directives.code from docutils.nodes import Node from docutils.parsers.rst import directives from domdf_python_tools.paths import PathPlus from domdf_python_tools.stringlist import StringList from domdf_python_tools.utils import convert_indents from sphinx.application import Sphinx from sphinx.writers.html5 import HTML5Translator from sphinx.writers.latex import LaTeXTranslator # this package from sphinx_toolbox.utils import Config, OptionSpec, SphinxExtMetadata, metadata_add_version __all__ = ( "CodeBlock", "CodeCell", "OutputCell", "Prompt", "visit_prompt_html", "visit_prompt_latex", "copy_asset_files", "configure", "setup", ) class CodeBlock(sphinx.directives.code.CodeBlock): """ Directive for a code block with special highlighting or line numbering settings. The indent_size can be adjusted with the ``:tab-width: `` option. .. autoclasssumm:: CodeBlock :autosummary-sections: ;; """ option_spec: OptionSpec = { # type: ignore[assignment] "force": directives.flag, "linenos": directives.flag, "tab-width": int, "dedent": int, "lineno-start": int, "emphasize-lines": directives.unchanged_required, "caption": directives.unchanged_required, "class": directives.class_option, "name": directives.unchanged, } def run(self) -> List[Node]: """ Process the content of the code block. """ code = '\n'.join(self.content) if "tab-width" in self.options: tab_width = self.options["tab-width"] else: tab_width = 4 code = convert_indents(code, tab_width=tab_width, from_=' ' * self.config.docutils_tab_width) self.content = docutils.statemachine.StringList(code.split('\n')) return super().run() class Prompt(docutils.nodes.General, docutils.nodes.FixedTextElement): """ Represents a cell prompt for a :class:`CodeCell` and :class:`OutputCell`. .. versionadded:: 2.6.0 """ class CodeCell(CodeBlock): """ Customised code block which displays an execution count to the left of the code block, similar to a Jupyter Notebook cell. The indent_size can be adjusted with the ``:tab-width: `` option. The execution count can be set using the ``:execution-count: `` option. .. autoclasssumm:: CodeCell :autosummary-sections: ;; .. versionadded:: 2.6.0 """ # noqa: D400 option_spec: OptionSpec = { **CodeBlock.option_spec, "execution-count": directives.positive_int, } _prompt: str = "In [%s]:" _class: str = "code-cell" def run(self) -> List[Node]: """ Process the content of the code block. """ self.options.setdefault("class", []) self.options["class"].append(f"{self._class}-code") prompt = self._prompt % self.options.get("execution-count", ' ') outer_node = docutils.nodes.container(classes=[self._class]) outer_node += Prompt( prompt, prompt, language="none", classes=["prompt", f"{self._class}-prompt"], ) outer_node += super().run()[0] return [outer_node] class OutputCell(CodeCell): """ Variant of :class:`~.CodeCell` for displaying the output of a cell in a Jupyter Notebook. The indent_size can be adjusted with the ``:tab-width: `` option. The execution count can be set using the ``:execution-count: `` option. .. versionadded:: 2.6.0 .. autoclasssumm:: OutputCell :autosummary-sections: ;; """ _prompt: str = "[%s]:" _class: str = "output-cell" def visit_prompt_html(translator: HTML5Translator, node: Prompt) -> None: """ Visit a :class:`~.Prompt` node with the HTML translator. .. versionadded:: 2.6.0 :param translator: :param node: """ starttag = translator.starttag(node, "div", suffix='', CLASS="notranslate") translator.body.append(starttag + node.rawsource + '\n') raise docutils.nodes.SkipNode def visit_prompt_latex(translator: LaTeXTranslator, node: Prompt) -> None: """ Visit a :class:`~.Prompt` node with the LaTeX translator. .. versionadded:: 2.6.0 :param translator: :param node: """ translator.body.append("\n\n") translator.body.append(r"\vspace{4mm}") if f"code-cell-prompt" in node["classes"]: colour = "nbsphinxin" elif f"output-cell-prompt" in node["classes"]: colour = "nbsphinxout" else: # pragma: no cover colour = "black" translator.body.append( rf"\llap{{\color{{{colour}}}\texttt{{{node.rawsource}}}" r"\,\hspace{\fboxrule}\hspace{\fboxrule}\hspace{\fboxsep}}" ) translator.body.append(r"\vspace{-7mm}") raise docutils.nodes.SkipNode def copy_asset_files(app: Sphinx, exception: Optional[Exception] = None) -> None: """ Copy additional stylesheets into the HTML build directory. .. versionadded:: 2.6.0 :param app: The Sphinx application. :param exception: Any exception which occurred and caused Sphinx to abort. """ if exception: # pragma: no cover return if app.builder is None or app.builder.format.lower() != "html": # pragma: no cover return prompt_style: dict2css.Style = { "user-select": None, "font-size": "13px", "font-family": '"SFMono-Regular", Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace', "border": None, "padding": "11px 0 0", "margin": "0 5px 0 0", "box-shadow": None, "wrap-option": None, "white-space": "nowrap", } container_style: dict2css.Style = { "padding-top": "5px", "display": "flex", "align-items": "stretch", "margin": 0, } code_style_string = "div.code-cell.container div.code-cell-code, div.output-cell.container div.output-cell-code" code_style: dict2css.Style = { "width": "100%", "padding-top": 0, "margin-top": 0, } style: MutableMapping[str, dict2css.Style] = { "div.code-cell.container div.prompt": {"color": "#307FC1"}, "div.output-cell.container div.prompt": {"color": "#BF5B3D"}, "div.code-cell.container div.prompt, div.output-cell.container div.prompt": prompt_style, "div.code-cell.container, div.output-cell.container": container_style, code_style_string: code_style, } static_dir = PathPlus(app.outdir) / "_static" static_dir.maybe_make(parents=True) dict2css.dump(style, static_dir / "sphinx-toolbox-code.css") def configure(app: Sphinx, config: Config) -> None: """ Configure :mod:`sphinx_toolbox.code`. .. versionadded:: 2.9.0 :param app: The Sphinx application. :param config: """ latex_elements = getattr(config, "latex_elements", {}) latex_preamble = StringList(latex_elements.get("preamble", '')) latex_preamble.blankline() latex_preamble.append(r"\definecolor{nbsphinxin}{HTML}{307FC1}") latex_preamble.append(r"\definecolor{nbsphinxout}{HTML}{BF5B3D}") latex_elements["preamble"] = str(latex_preamble) config.latex_elements = latex_elements @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.code`. .. versionadded:: 1.0.0 :param app: The Sphinx application. """ # Code block with customisable indent size. app.add_directive("code-block", CodeBlock, override=True) app.add_directive("sourcecode", CodeBlock, override=True) app.add_directive("code-cell", CodeCell) app.add_directive("output-cell", OutputCell) # Hack to get the docutils tab size, as there doesn't appear to be any other way app.setup_extension("sphinx_toolbox.tweaks.tabsize") app.add_node( Prompt, html=(visit_prompt_html, lambda *args, **kwargs: None), latex=(visit_prompt_latex, lambda *args, **kwargs: None) ) app.connect("config-inited", configure) app.add_css_file("sphinx-toolbox-code.css") app.connect("build-finished", copy_asset_files) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/collapse.py000066400000000000000000000124341475757360300220000ustar00rootroot00000000000000#!/usr/bin/env python3 # # collapse.py r""" Adds a collapsible section to an HTML page using a details_ element. .. _details: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details .. versionadded:: 2.5.0 .. extensions:: sphinx_toolbox.collapse Usage ------ .. rst:directive:: .. collapse:: [label] Adds a collapsible section to an HTML page using a details_ element. .. _details: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details With non-HTML builders, the content will be added as-is. .. rest-example:: .. collapse:: Details Something small enough to escape casual notice. .. collapse:: A Different Label :class: custom-summary :name: summary0 Something else that might escape notice. .. collapse:: A long code block .. code-block:: python print("Not really") .. rst:directive:option:: open :type: flag The ``:open:`` option can be used to have the section open by default. .. versionadded:: 3.0.0 .. rest-example:: .. collapse:: Open :open: This section is open by default. API Reference ---------------- """ # # 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 Optional, Sequence # 3rd party from docutils import nodes from docutils.parsers.rst import directives from docutils.parsers.rst.roles import set_classes from domdf_python_tools.stringlist import DelimitedList from sphinx.application import Sphinx from sphinx.util.docutils import SphinxDirective from sphinx.writers.html5 import HTML5Translator # this package from sphinx_toolbox.utils import SphinxExtMetadata, flag, metadata_add_version __all__ = ("CollapseDirective", "CollapseNode", "visit_collapse_node", "depart_collapse_node", "setup") class CollapseDirective(SphinxDirective): r""" A Sphinx directive to add a collapsible section to an HTML page using a details_ element. .. _details: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details """ final_argument_whitespace: bool = True has_content: bool = True # The label required_arguments: int = 1 option_spec = { "class": directives.class_option, "name": directives.unchanged, "open": flag, } def run(self) -> Sequence[nodes.Node]: # type: ignore[override] """ Process the content of the directive. """ set_classes(self.options) self.assert_has_content() text = '\n'.join(self.content) label = self.arguments[0] collapse_node = CollapseNode(text, label, **self.options) self.add_name(collapse_node) collapse_node["classes"].append(f"summary-{nodes.make_id(label)}") self.state.nested_parse(self.content, self.content_offset, collapse_node) return [collapse_node] class CollapseNode(nodes.Body, nodes.Element): """ Node that represents a collapsible section. :param rawsource: :param label: """ def __init__(self, rawsource: str = '', label: Optional[str] = None, *children, **attributes): super().__init__(rawsource, *children, **attributes) self["label"] = label def visit_collapse_node(translator: HTML5Translator, node: CollapseNode) -> None: """ Visit a :class:`~.CollapseNode`. :param translator: :param node: The node being visited. """ tag_parts = DelimitedList(["details"]) if node.get("names", None): names = DelimitedList(node["names"]) tag_parts.append(f'name="{names: }"') if node.get("classes", None): classes = DelimitedList(node["classes"]) tag_parts.append(f'class="{classes: }"') if node.attributes.get("open", False): tag_parts.append("open") translator.body.append(f"<{tag_parts: }>\n{node['label']}") translator.context.append("") def depart_collapse_node(translator: HTML5Translator, node: CollapseNode) -> None: """ Depart a :class:`~.CollapseNode`. :param translator: :param node: The node being visited. """ translator.body.append(translator.context.pop()) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.collapse`. :param app: The Sphinx application. """ app.add_directive("collapse", CollapseDirective) app.add_node( CollapseNode, html=(visit_collapse_node, depart_collapse_node), latex=(lambda *args, **kwargs: None, lambda *args, **kwargs: None) ) return { "parallel_read_safe": True, } sphinx-toolbox-3.9.0/sphinx_toolbox/config.py000066400000000000000000000100341475757360300214350ustar00rootroot00000000000000#!/usr/bin/env python3 # # config.py """ Internal configuration for ``sphinx-toolbox``. """ # # Copyright Β© 2020-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 List # 3rd party from apeye.requests_url import RequestsURL from sphinx.application import Sphinx # this package from sphinx_toolbox.utils import Config, add_nbsp_substitution __all__ = ("MissingOptionError", "InvalidOptionError", "validate_config", "ToolboxConfig") class MissingOptionError(ValueError): """ Subclass of :exc:`ValueError` to indicate a missing configuration option. """ class InvalidOptionError(ValueError): """ Subclass of :exc:`ValueError` to indicate an invalid value for a configuration value. """ class ToolboxConfig(Config): """ Subclass of :class:`sphinx.config.Config` with type annotations for the configuration values added by ``sphinx-toolbox``. Depending on the extensions enabled not all of these configuration values will be present. Functionally compatible with :class:`sphinx.config.Config`. """ # noqa: D400 source_link_target: str """ The target of the source link, either ``'github'`` or ``'sphinx'``. Will be lowercase after :func:`~.validate_config` has run. """ #: The username of the GitHub account that owns the repository this documentation corresponds to. github_username: str github_repository: str """ The GitHub repository this documentation corresponds to. .. clearpage:: """ #: The complete URL of the repository on GitHub. github_url: RequestsURL #: The base URL for the source code on GitHub. github_source_url: RequestsURL #: The base URL for the issues on GitHub. github_issues_url: RequestsURL #: The base URL for the pull requests on GitHub. github_pull_url: RequestsURL #: List of required Conda channels. conda_channels: List[str] #: The directory in which to find assets for the :rst:role:`asset` role. assets_dir: str docutils_tab_width: int """ The tab size used by docutils. This is usually 8 spaces, but can be configured in the ``docutils.conf`` file. """ #: The Wikipedia language to use for :rst:role:`wikipedia` roles. wikipedia_lang: str #: A string of reStructuredText that will be included at the beginning of every source file that is read. rst_prolog: str #: Document all :class:`typing.TypeVar`\s, even if they have no docstring. all_typevars: bool no_unbound_typevars: bool r""" Only document :class:`typing.TypeVar`\s that have a constraint of are bound. This option has no effect if :confval:`all_typevars` is False. """ def validate_config(app: Sphinx, config: ToolboxConfig) -> None: """ Validate the provided configuration values. See :class:`~sphinx_toolbox.config.ToolboxConfig` for a list of the configuration values. :param app: The Sphinx application. :param config: :type config: :class:`~sphinx.config.Config` """ # this package from sphinx_toolbox import github, source source._configure(app, config) github.validate_config(app, config) add_nbsp_substitution(config) sphinx-toolbox-3.9.0/sphinx_toolbox/confval.py000066400000000000000000000136031475757360300216250ustar00rootroot00000000000000#!/usr/bin/env python3 # # confval.py r""" The confval directive and role for configuration values. .. extensions:: sphinx_toolbox.confval Usage ------- .. rst:directive:: .. confval:: name Used to document a configuration value. .. raw:: latex \begin{multicols}{2} .. rst:directive:option:: type :type: string Indicates the configuration value's type. .. rst:directive:option:: required :type: flag Indicates the whether the configuration value is required. .. rst:directive:option:: default :type: string Indicates the default value. .. rst:directive:option:: noindex :type: flag Disables the index entry and cross-referencing for this configuration value. .. versionadded:: 2.11.0 .. raw:: latex \end{multicols} .. latex:vspace:: -0px .. rst:role:: confval Role which provides a cross-reference to a :rst:dir:`confval` directive. .. latex:vspace:: 10px **Examples:** .. rest-example:: .. confval:: demo :type: string :default: ``"Hello World"`` :required: False .. latex:vspace:: -20px .. rest-example:: To enable this feature set the :confval:`demo` configuration value to "True". .. latex:clearpage:: API Reference -------------- """ # # Copyright Β© 2020-2021 Dominic Davis-Foster # # Based on https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/conf.py # Copyright (c) 2013-2018 Dave Snider, Read the Docs, Inc. & contributors # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE # OR OTHER DEALINGS IN THE SOFTWARE. # # stdlib from typing import List # 3rd party from docutils.nodes import Node from docutils.parsers.rst import directives from docutils.statemachine import StringList from domdf_python_tools.utils import strtobool from sphinx.application import Sphinx from sphinx.domains import ObjType from sphinx.domains.std import GenericObject, StandardDomain from sphinx.errors import ExtensionError from sphinx.roles import XRefRole # this package from sphinx_toolbox.utils import OptionSpec, SphinxExtMetadata, flag, metadata_add_version __all__ = ("ConfigurationValue", "register_confval", "setup") class ConfigurationValue(GenericObject): """ The confval directive. .. versionchanged:: 1.1.0 The formatting of the type, required and default options can be customised using the ``self.format_*`` methods. .. versionchanged:: 2.11.0 Added the ``:noindex:`` option, which disables the index entry and cross-referencing for this configuration value. """ indextemplate: str = "%s (configuration value)" option_spec: OptionSpec = { # type: ignore[assignment] "type": directives.unchanged_required, "required": directives.unchanged_required, "default": directives.unchanged_required, "noindex": flag, } def run(self) -> List[Node]: """ Process the content of the directive. """ content: List[str] = [] if self.options and set(self.options.keys()) != {"noindex"}: content.extend(('', ".. raw:: latex", '', r" \vspace{-45px}", '')) if "type" in self.options: content.append(f"| **Type:** {self.format_type(self.options['type'])}") if "required" in self.options: content.append(f"| **Required:** ``{self.format_required(self.options['required'])}``") if "default" in self.options: content.append(f"| **Default:** {self.format_default(self.options['default'])}") if self.content: content.extend(( '', ".. raw:: latex", '', r" \vspace{-25px}", '', )) content.extend(self.content) self.content = StringList(content) return super().run() @staticmethod def format_type(the_type: str) -> str: """ Formats the ``:type:`` option. .. versionadded:: 1.1.0 :param the_type: """ return the_type @staticmethod def format_required(required: str) -> bool: """ Formats the ``:required:`` option. .. versionadded:: 1.1.0 :param required: """ return strtobool(required) @staticmethod def format_default(default: str) -> str: """ Formats the ``:default:`` option. .. versionadded:: 1.1.0 :param default: """ return default def register_confval(app: Sphinx, override: bool = False) -> None: """ Create and register the ``confval`` role and directive. :param app: The Sphinx application. :param override: """ if "std" not in app.registry.domains: app.add_domain(StandardDomain) # pragma: no cover name = "confval" app.registry.add_directive_to_domain("std", name, ConfigurationValue) app.registry.add_role_to_domain("std", name, XRefRole()) object_types = app.registry.domain_object_types.setdefault("std", {}) if name in object_types and not override: # pragma: no cover raise ExtensionError(f"The {name!r} object_type is already registered") object_types[name] = ObjType(name, name) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.confval`. .. versionadded:: 0.7.0 :param app: The Sphinx application. """ register_confval(app) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/decorators.py000066400000000000000000000110451475757360300223400ustar00rootroot00000000000000#!/usr/bin/env python3 # # decorators.py """ reStructuredText XRef role for decorators. .. versionadded:: 0.9.0 .. extensions:: sphinx_toolbox.decorators Usage ------ .. rst:role:: deco Adds a cross reference to a decorator, prefixed with an ``@``. .. rest-example:: .. decorator:: my_decorator A decorator. :deco:`my_decorator` :deco:`@my_decorator` :deco:`Title ` .. latex:clearpage:: API Reference ---------------- """ # # Copyright Β© 2020-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. # # Based on Sphinx # Copyright (c) 2007-2020 by the Sphinx team. # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib from typing import Tuple, Type # noqa: F401 # 3rd party from docutils.nodes import Element from sphinx.application import Sphinx from sphinx.domains.python import PyXRefRole from sphinx.environment import BuildEnvironment # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ("PyDecoXRefRole", "setup") class PyDecoXRefRole(PyXRefRole): """ XRef Role for decorators members. """ def process_link( self, env: BuildEnvironment, refnode: Element, has_explicit_title: bool, title: str, target: str, ) -> Tuple[str, str]: """ Called after parsing title and target text, and creating the reference node (given in ``refnode``). This method can alter the reference node and must return a new (or the same) ``(title, target)`` tuple. :param env: :param refnode: :param has_explicit_title: :param title: :param target: """ target = target.lstrip('@') title, target = super().process_link( env=env, refnode=refnode, has_explicit_title=has_explicit_title, title=title, target=target, ) if not has_explicit_title and not title.startswith('@'): title = f"@{title}" # Ensure the reference is correctly found in the broader scope. refnode["reftype"] = "obj" return title, target @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.decorators`. :param app: The Sphinx application. """ app.add_role_to_domain("py", "deco", PyDecoXRefRole()) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/documentation_summary.py000066400000000000000000000144171475757360300246270ustar00rootroot00000000000000#!/usr/bin/env python3 # # documentation_summary.py """ Allows insertion of a summary line on the title page generated with the LaTeX builder, and at a custom location throughout the document. .. versionadded:: 2.2.0 .. extensions:: sphinx_toolbox.documentation_summary Configuration -------------- .. latex:vspace:: -10px .. confval:: documentation_summary :type: :class:`str` The documentation summary to display on the title page with the LaTeX builder, and at the location of :rst:dir:`documentation-summary` directives for other builders. If undefined no summary is shown. Usage ------ .. latex:vspace:: -10px .. rst:directive:: documentation-summary Adds the documentation summary as configured above. .. only:: html :bold-title:`Example` .. rest-example:: .. documentation-summary:: .. rst:directive:option:: meta Include the summary as a meta_ "description" tag in the HTML output. The structure of the description is ``{project} -- {summary}``, where ``project`` is configured in ``conf.py``. See `the sphinx documentation`_ for more information on the ``project`` option. .. versionadded:: 2.10.0 .. _meta: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta .. _the sphinx documentation: https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-project .. latex:clearpage:: API Reference ---------------- """ # noqa: D400 # # 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 List # 3rd party from docutils import nodes from docutils.statemachine import StringList from docutils.utils.smartquotes import educateQuotes from sphinx import addnodes from sphinx.application import Sphinx from sphinx.util.docutils import SphinxDirective # this package from sphinx_toolbox.utils import Config, Purger, SphinxExtMetadata, flag, metadata_add_version __all__ = ("DocumentationSummaryDirective", "configure", "setup") summary_node_purger = Purger("all_summary_nodes") RENEW = r""" \makeatletter \renewcommand{\py@release}{ \releasename\space\version \par \vspace{25pt} \textup{\thesummary} } \makeatother """ RESET = r"\makeatletter\renewcommand{\py@release}{\releasename\space\version}\makeatother" class DocumentationSummaryDirective(SphinxDirective): """ A Sphinx directive for creating a summary line. """ option_spec = {"meta": flag} def run(self) -> List[nodes.Node]: """ Process the content of the directive. """ summary = getattr(self.config, "documentation_summary", '').strip() if not summary: return [] # pragma: no cover # if self.env.app.builder.format.lower() == "latex" or not summary: # return [] targetid = f'documentation-summary-{self.env.new_serialno("documentation-summary"):d}' onlynode = addnodes.only(expr="html") content = f'**{summary}**' content_node = nodes.paragraph(rawsource=content, ids=[targetid]) onlynode += content_node self.state.nested_parse(StringList([content]), self.content_offset, content_node) summary_node_purger.add_node(self.env, content_node, content_node, self.lineno) if "meta" in self.options: meta_content = f'.. meta::\n :description: {self.config.project} -- {summary}\n' meta_node = nodes.paragraph(rawsource=meta_content, ids=[targetid]) onlynode += meta_node self.state.nested_parse( StringList(meta_content.split('\n')), self.content_offset, meta_node, ) summary_node_purger.add_node(self.env, meta_node, meta_node, self.lineno) return [onlynode] def configure(app: Sphinx, config: Config) -> None: """ Configure :mod:`sphinx_toolbox.documentation_summary`. :param app: The Sphinx application. :param config: """ if not hasattr(config, "latex_elements"): # pragma: no cover config.latex_elements = {} latex_elements = (config.latex_elements or {}) latex_preamble = latex_elements.get("preamble", '') summary = getattr(config, "documentation_summary", '').strip() if not summary: return # pragma: no cover # Escape latex special characters summary = summary.replace("~ ", r"\textasciitilde\space ") summary = summary.replace("^ ", r"\textasciicircum\space ") summary = summary.replace("\\ ", r"\textbackslash\space ") summary = summary.translate({ 35: r"\#", 36: r"\$", 37: r"\%", 38: r"\&", 94: r"\textasciicircum", 95: r"\_", 123: r"\{", 125: r"\}", 126: r"\textasciitilde", }) # TODO: escape backslashes without breaking the LaTeX commands summary_command = rf"\newcommand{{\thesummary}}{{{educateQuotes(summary)}}}" if summary_command not in latex_preamble: config.latex_elements["preamble"] = '\n'.join([ latex_preamble, summary_command, RENEW, ]) config.latex_elements["maketitle"] = '\n'.join([ config.latex_elements.get("maketitle", r"\sphinxmaketitle"), RESET, ]) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.documentation_summary`. :param app: The Sphinx application. """ app.connect("config-inited", configure, priority=550) app.add_directive("documentation-summary", DocumentationSummaryDirective) app.add_config_value("documentation_summary", None, "env", types=[str, None]) app.connect("env-purge-doc", summary_node_purger.purge_nodes) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/flake8.py000066400000000000000000000100041475757360300213370ustar00rootroot00000000000000#!/usr/bin/env python3 # # flake8.py r""" A Sphinx directive for documenting flake8 codes. .. versionadded:: 1.6.0 .. extensions:: sphinx_toolbox.flake8 Usage ------ .. rst:directive:: .. flake8-codes:: plugin Adds a table documenting a flake8 plugin's codes. The directive takes a single argument -- the fully qualified name of the flake8 plugin module. Codes to document are given in the body of the directive. :bold-title:`Example` .. rest-example:: .. flake8-codes:: flake8_dunder_all DALL000 .. latex:clearpage:: API Reference ---------------- """ # # Copyright Β© 2020-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 warnings from typing import List, Sequence, Tuple # 3rd party import docutils import tabulate from docutils import nodes from docutils.statemachine import StringList from sphinx.application import Sphinx from sphinx.ext.autodoc.importer import import_module from sphinx.util.docutils import SphinxDirective # this package from sphinx_toolbox.utils import Purger, SphinxExtMetadata, metadata_add_version __all__ = ("Flake8CodesDirective", "setup") table_node_purger = Purger("all_flake8_code_table_nodes") class Flake8CodesDirective(SphinxDirective): """ A Sphinx directive for documenting flake8 codes. """ has_content: bool = True # the fully qualified name of the flake8 plugin module required_arguments: int = 1 def run(self) -> Sequence[nodes.Node]: # type: ignore[override] """ Process the content of the directive. """ plugin: str = self.arguments[0] if not self.content: warnings.warn("No codes specified") return [] module = import_module(plugin) codes: List[Tuple[str, str]] = [] for code in self.content: if code.strip(): if hasattr(module, code): description = getattr(module, code) if description.startswith(code): description = description[len(code):] codes.append((code, description.strip())) else: warnings.warn(f"No such code {code!r}") if not codes: warnings.warn("No codes specified") return [] targetid = f'flake8codes-{self.env.new_serialno("flake8codes"):d}' targetnode = nodes.section(ids=[targetid]) table = tabulate.tabulate(codes, headers=["Code", "Description"], tablefmt="rst") content = '\n' + table.replace('\t', " ") + '\n' view = StringList(content.split('\n')) table_node = nodes.paragraph(rawsource=content) self.state.nested_parse(view, self.content_offset, table_node) if docutils.__version_info__ >= (0, 18): table_node.children[0]["classes"] += ["colwidths-given"] # type: ignore[index] table_node_purger.add_node(self.env, table_node, targetnode, self.lineno) return [table_node] @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.flake8`. :param app: The Sphinx application. """ app.add_directive("flake8-codes", Flake8CodesDirective) app.connect("env-purge-doc", table_node_purger.purge_nodes) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/formatting.py000066400000000000000000000132411475757360300223450ustar00rootroot00000000000000#!/usr/bin/env python3 # # formatting.py """ Directives, roles and nodes for text formatting. .. versionadded:: 0.2.0 .. extensions:: sphinx_toolbox.formatting .. latex:vspace:: -10px Usage ------- .. rst:role:: iabbr An abbreviation. If the role content contains a parenthesized explanation, it will be treated specially: it will be shown in a tool-tip in HTML, and output only once in LaTeX. Unlike Sphinx's :rst:role:`abbr` role, this one shows the abbreviation in italics. .. versionadded:: 0.2.0 :bold-title:`Example` .. rest-example:: :iabbr:`LIFO (last-in, first-out)` .. rst:role:: bold-title Role for displaying a pseudo title in bold. This is useful for breaking up Python docstrings. .. versionadded:: 2.12.0 :bold-title:`Example` .. rest-example:: :bold-title:`Examples:` :bold-title:`Other Extensions` API Reference --------------- """ # # Copyright Β© 2020-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. # # Parts of the docstrings based on https://docutils.sourceforge.io/docs/howto/rst-roles.html # # stdlib from typing import List, Tuple # 3rd party from docutils import nodes from docutils.nodes import Node, system_message from docutils.parsers.rst import roles from sphinx.application import Sphinx from sphinx.roles import Abbreviation from sphinx.util.docutils import SphinxRole from sphinx.writers.html5 import HTML5Translator from sphinx.writers.latex import LaTeXTranslator # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ( "ItalicAbbreviationNode", "ItalicAbbreviation", "visit_iabbr_node", "depart_iabbr_node", "latex_visit_iabbr_node", "latex_depart_iabbr_node", "setup" ) class ItalicAbbreviationNode(nodes.abbreviation): """ Docutils Node to show an abbreviation in italics. """ class ItalicAbbreviation(Abbreviation): """ Docutils role to show an abbreviation in italics. """ def run(self) -> Tuple[List[Node], List[system_message]]: """ Process the content of the italic abbreviation role. """ options = self.options.copy() matched = self.abbr_re.search(self.text) if matched: text = self.text[:matched.start()].strip() options["explanation"] = matched.group(1) else: text = self.text return [ItalicAbbreviationNode(self.rawtext, text, **options)], [] class BoldTitle(SphinxRole): """ Role for displaying a pseudo title in bold. This is useful for breaking up Python docstrings. """ def run(self) -> Tuple[List[nodes.Node], List[nodes.system_message]]: assert self.text is not None node_list: List[nodes.Node] = [ nodes.raw('', r"\vspace{10px}", format="latex"), nodes.strong(f"**{self.text}**", self.text), ] return node_list, [] def visit_iabbr_node(translator: HTML5Translator, node: ItalicAbbreviationNode) -> None: """ Visit an :class:`~.ItalicAbbreviationNode`. :param translator: :param node: The node being visited. """ translator.body.append('') attrs = {} if node.hasattr("explanation"): attrs["title"] = node["explanation"] translator.body.append(translator.starttag(node, "abbr", '', **attrs)) def depart_iabbr_node(translator: HTML5Translator, node: ItalicAbbreviationNode) -> None: """ Depart an :class:`~.ItalicAbbreviationNode`. :param translator: :param node: The node being visited. """ translator.body.append("") def latex_visit_iabbr_node(translator: LaTeXTranslator, node: ItalicAbbreviationNode) -> None: """ Visit an :class:`~.ItalicAbbreviationNode`. :param translator: :param node: The node being visited. """ abbr = node.astext() translator.body.append(r"\textit{\sphinxstyleabbreviation{") # spell out the explanation once if node.hasattr("explanation") and abbr not in translator.handled_abbrs: translator.context.append(f'}} ({translator.encode(node["explanation"])})') translator.handled_abbrs.add(abbr) else: translator.context.append('}') def latex_depart_iabbr_node(translator: LaTeXTranslator, node: ItalicAbbreviationNode) -> None: """ Depart an :class:`~.ItalicAbbreviationNode`. :param translator: :param node: The node being visited. """ translator.body.append(translator.context.pop()) translator.body.append('}') @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.formatting`. :param app: The Sphinx application. """ roles.register_local_role("iabbr", ItalicAbbreviation()) app.add_role("bold-title", BoldTitle()) app.add_node( ItalicAbbreviationNode, html=(visit_iabbr_node, depart_iabbr_node), latex=(latex_visit_iabbr_node, latex_depart_iabbr_node), ) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/github/000077500000000000000000000000001475757360300211025ustar00rootroot00000000000000sphinx-toolbox-3.9.0/sphinx_toolbox/github/__init__.py000066400000000000000000000171161475757360300232210ustar00rootroot00000000000000#!/usr/bin/env python3 # # __init__.py r""" Sphinx domain for GitHub.com, and related utilities. .. versionadded:: 2.4.0 .. extensions:: sphinx_toolbox.github Configuration -------------- .. latex:vspace:: -10px .. confval:: github_username :type: :class:`str` :required: True The username of the GitHub account that owns the repository this documentation corresponds to. .. confval:: github_repository :type: :class:`str` :required: True The GitHub repository this documentation corresponds to. Usage ------ .. latex:vspace:: -10px .. rst:role:: github:issue Role which shows a link to the given issue on GitHub. If the issue exists, the link has a tooltip that shows the title of the issue. **Example** .. rest-example:: :github:issue:`1` You can also reference an issue in a different repository by adding the repository name inside ``<>``. .. rest-example:: :github:issue:`7680 ` .. rst:role:: github:pull Role which shows a link to the given pull request on GitHub. If the pull requests exists, the link has a tooltip that shows the title of the pull requests. **Example** .. rest-example:: :github:pull:`2` You can also reference a pull request in a different repository by adding the repository name inside ``<>``. .. rest-example:: :github:pull:`7671 ` .. rst:role:: github:repo Role which shows a link to the given repository on GitHub. **Example** .. rest-example:: :github:repo:`sphinx-toolbox/sphinx-toolbox` You can also use a different label for the link:. .. rest-example:: See more in the :github:repo:`pytest repository `. .. rst:role:: github:user Role which shows a link to the given user on GitHub. **Example** .. rest-example:: :github:user:`domdfcoding` You can also use a different label for the link:. .. rest-example:: See more of my :github:user:`repositories `. .. latex:clearpage:: .. rst:role:: github:org Role which shows a link to the given organization on GitHub. **Example** .. rest-example:: :github:org:`sphinx-toolbox` You can also use a different label for the link:. .. rest-example:: See more repositories in the :github:org:`pytest-dev org `. Caching ----------- HTTP requests to obtain issue/pull request titles are cached for four hours. To clear the cache manually, run: .. prompt:: bash python3 -m sphinx_toolbox API Reference --------------- Enable this extension from your extension's setup function like so: .. code-block:: def setup(app: Sphinx) -> Dict[str, Any]: app.setup_extension('sphinx_toolbox.github') return {} This will guarantee that the following values will be available via :attr:`app.config `: * **github_username** (:class:`str`\) -- The username of the GitHub account that owns the repository this documentation corresponds to. * **github_repository** (:class:`str`\) -- The GitHub repository this documentation corresponds to. * **github_url** (:class:`apeye.requests_url.RequestsURL`\) -- The complete URL of the repository on GitHub. * **github_source_url** (:class:`~apeye.requests_url.RequestsURL`\) -- The base URL for the source code on GitHub. * **github_issues_url** (:class:`~apeye.requests_url.RequestsURL`\) -- The base URL for the issues on GitHub. * **github_pull_url** (:class:`~apeye.requests_url.RequestsURL`\) -- The base URL for the pull requests on GitHub. If the user has not provided either ``github_username`` or ``github_repository`` a :exc:`~.MissingOptionError` will be raised. .. latex:clearpage:: """ # # Copyright Β© 2020-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. # # 3rd party from sphinx.application import Sphinx from sphinx.domains import Domain from sphinx.environment import BuildEnvironment # this package from sphinx_toolbox.config import MissingOptionError, ToolboxConfig from sphinx_toolbox.github.issues import ( IssueNode, _depart_issue_node_latex, _visit_issue_node_latex, depart_issue_node, issue_role, pull_role, visit_issue_node ) from sphinx_toolbox.github.repos_and_users import ( GitHubObjectLinkNode, _depart_github_object_link_node_latex, _visit_github_object_link_node_latex, depart_github_object_link_node, repository_role, user_role, visit_github_object_link_node ) from sphinx_toolbox.utils import SphinxExtMetadata, make_github_url, metadata_add_version _ = BuildEnvironment __all__ = ("GitHubDomain", "validate_config", "setup") class GitHubDomain(Domain): """ Sphinx domain for `GitHub.com `_. """ name = "github" label = "GitHub" roles = { "issue": issue_role, # type: ignore[dict-item] "pull": pull_role, # type: ignore[dict-item] "user": user_role, # type: ignore[dict-item] "org": user_role, # type: ignore[dict-item] "repo": repository_role, # type: ignore[dict-item] } def validate_config(app: Sphinx, config: ToolboxConfig) -> None: """ Validate the provided configuration values. See :class:`~sphinx_toolbox.config.ToolboxConfig` for a list of the configuration values. :param app: The Sphinx application. :param config: :type config: :class:`~sphinx.config.Config` """ if not config.github_username: raise MissingOptionError("The 'github_username' option is required.") else: config.github_username = str(config.github_username) if not config.github_repository: raise MissingOptionError("The 'github_repository' option is required.") else: config.github_repository = str(config.github_repository) config.github_url = make_github_url(config.github_username, config.github_repository) config.github_source_url = config.github_url / "blob" / "master" config.github_issues_url = config.github_url / "issues" config.github_pull_url = config.github_url / "pull" @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.github`. .. versionadded:: 1.0.0 :param app: The Sphinx application. """ app.connect("config-inited", validate_config, priority=850) app.add_config_value("github_username", None, "env", types=[str]) app.add_config_value("github_repository", None, "env", types=[str]) app.add_domain(GitHubDomain) # Custom node for issues and PRs app.add_node( IssueNode, html=(visit_issue_node, depart_issue_node), latex=(_visit_issue_node_latex, _depart_issue_node_latex) ) app.add_node( GitHubObjectLinkNode, html=(visit_github_object_link_node, depart_github_object_link_node), latex=(_visit_github_object_link_node_latex, _depart_github_object_link_node_latex) ) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/github/issues.py000066400000000000000000000254141475757360300227750ustar00rootroot00000000000000#!/usr/bin/env python3 # # issues.py """ Roles and nodes for GitHub issues and Pull Requests. """ # # Copyright Β© 2020-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. # # Based on pyspecific.py from the Python documentation. # Copyright 2008-2014 by Georg Brandl. # Licensed under the PSF License 2.0 # # Parts of the docstrings based on https://docutils.sourceforge.io/docs/howto/rst-roles.html # # stdlib import warnings import xml.sax.saxutils from typing import Any, Dict, List, Optional, Tuple, Union # 3rd party import requests # nodep from apeye.url import URL from bs4 import BeautifulSoup from docutils import nodes from docutils.nodes import system_message from docutils.parsers.rst.states import Inliner from sphinx.util.nodes import split_explicit_title from sphinx.writers.html5 import HTML5Translator from sphinx.writers.latex import LaTeXTranslator # this package from sphinx_toolbox.cache import cache from sphinx_toolbox.utils import make_github_url __all__ = ( "IssueNode", "IssueNodeWithName", "issue_role", "pull_role", "visit_issue_node", "depart_issue_node", "get_issue_title", ) class IssueNode(nodes.reference): """ Docutils Node to represent a link to a GitHub *Issue* or *Pull Request*. :param issue_number: The number of the issue or pull request. :param refuri: The URL of the issue / pull request on GitHub. """ has_tooltip: bool issue_number: int issue_url: str def __init__( self, issue_number: Union[str, int], refuri: Union[str, URL], **kwargs, ): self.has_tooltip = False self.issue_number = int(issue_number) self.issue_url = str(refuri) source = f"#{issue_number}" super().__init__(source, source, refuri=self.issue_url) @property def _copy_kwargs(self): # pragma: no cover # noqa: MAN002 return {"issue_number": self.issue_number, "refuri": self.issue_url} def copy(self) -> "IssueNode": # pragma: no cover """ Return a copy of the :class:`sphinx_toolbox.github.issues.IssueNode`. """ # This was required to stop some breakage, but it doesn't seem to run during the tests. obj = self.__class__(**self._copy_kwargs) obj.document = self.document obj.has_tooltip = self.has_tooltip obj.line = self.line return obj class IssueNodeWithName(IssueNode): """ Docutils Node to represent a link to a GitHub *Issue* or *Pull Request*, with the repository name shown. .. versionadded:: 2.4.0 :param repo_name: The full name of the repository, in the form ``owner/name``. :param issue_number: The number of the issue or pull request. :param refuri: The URL of the issue / pull request on GitHub. """ repo_name: str def __init__( self, repo_name: str, issue_number: Union[str, int], refuri: Union[str, URL], **kwargs, ): self.has_tooltip = False self.issue_number = int(issue_number) self.issue_url = str(refuri) self.repo_name = str(repo_name) source = f"{repo_name}#{issue_number}" nodes.reference.__init__(self, source, source, refuri=self.issue_url) @property def _copy_kwargs(self) -> Dict[str, Any]: # pragma: no cover return {"repo_name": self.repo_name, "issue_number": self.issue_number, "refuri": self.issue_url} def issue_role( name: str, rawtext: str, text: str, lineno: int, inliner: Inliner, options: Dict[str, Any] = {}, content: List[str] = [] ) -> Tuple[List[IssueNode], List[system_message]]: """ Adds a link to the given issue on GitHub. :param name: The local name of the interpreted role, the role name actually used in the document. :param rawtext: A string containing the entire interpreted text input, including the role and markup. :param text: The interpreted text content. :param lineno: The line number where the interpreted text begins. :param inliner: The :class:`docutils.parsers.rst.states.Inliner` object that called :func:`~.issue_role`. It contains the several attributes useful for error reporting and document tree access. :param options: A dictionary of directive options for customization (from the ``role`` directive), to be interpreted by the function. Used for additional attributes for the generated elements and other functionality. :param content: A list of strings, the directive content for customization (from the ``role`` directive). To be interpreted by the function. :return: A list containing the created node, and a list containing any messages generated during the function. .. latex:clearpage:: """ has_t, issue_number, repository = split_explicit_title(text) issue_number = nodes.unescape(issue_number) messages: List[system_message] = [] refnode: IssueNode if has_t: repository_parts = nodes.unescape(repository).split('/') if len(repository_parts) != 2: warning_message = inliner.document.reporter.warning( f"Invalid repository '{repository}' for issue #{issue_number}.", ) messages.append(warning_message) else: refnode = IssueNodeWithName( repo_name=repository, issue_number=issue_number, refuri=make_github_url(*repository_parts) / "issues" / str(int(issue_number)), ) return [refnode], messages issues_url = inliner.document.settings.env.app.config.github_issues_url refnode = IssueNode(issue_number=issue_number, refuri=issues_url / str(int(issue_number))) return [refnode], messages def pull_role( name: str, rawtext: str, text: str, lineno: int, inliner: Inliner, options: Dict[str, Any] = {}, content: List[str] = [] ) -> Tuple[List[IssueNode], List[system_message]]: """ Adds a link to the given pulll request on GitHub. :param name: The local name of the interpreted role, the role name actually used in the document. :param rawtext: A string containing the entire interpreted text input, including the role and markup. :param text: The interpreted text content. :param lineno: The line number where the interpreted text begins. :param inliner: The :class:`docutils.parsers.rst.states.Inliner` object that called :func:`~.pull_role`. It contains the several attributes useful for error reporting and document tree access. :param options: A dictionary of directive options for customization (from the ``role`` directive), to be interpreted by the function. Used for additional attributes for the generated elements and other functionality. :param content: A list of strings, the directive content for customization (from the ``role`` directive). To be interpreted by the function. :return: A list containing the created node, and a list containing any messages generated during the function. """ has_t, issue_number, repository = split_explicit_title(text) issue_number = nodes.unescape(issue_number) messages: List[system_message] = [] refnode: IssueNode if has_t: repository_parts = nodes.unescape(repository).split('/') if len(repository_parts) != 2: warning_message = inliner.document.reporter.warning( f"Invalid repository '{repository}' for pull request #{issue_number}." ) messages.append(warning_message) else: refnode = IssueNodeWithName( repo_name=repository, issue_number=issue_number, refuri=make_github_url(*repository_parts) / "pull" / str(int(issue_number)), ) return [refnode], messages pull_url = inliner.document.settings.env.app.config.github_pull_url refnode = IssueNode(issue_number=issue_number, refuri=pull_url / str(int(issue_number))) return [refnode], messages def visit_issue_node(translator: HTML5Translator, node: IssueNode) -> None: """ Visit an :class:`~.IssueNode`. If the node points to a valid issue / pull request, add a tooltip giving the title of the issue / pull request and a hyperlink to the page on GitHub. :param translator: :param node: The node being visited. """ issue_title = get_issue_title(node.issue_url) if issue_title: node.has_tooltip = True translator.body.append(f'') translator.visit_reference(node) else: warnings.warn(f"Issue/Pull Request #{node.issue_number} not found.") def depart_issue_node(translator: HTML5Translator, node: IssueNode) -> None: """ Depart an :class:`~.IssueNode`. :param translator: :param node: The node being visited. """ if node.has_tooltip: translator.depart_reference(node) translator.body.append("") def _visit_issue_node_latex(translator: LaTeXTranslator, node: IssueNode) -> None: """ Visit an :class:`~.IssueNode`. If the node points to a valid issue / pull request, add a tooltip giving the title of the issue / pull request and a hyperlink to the page on GitHub. :param translator: :param node: The node being visited. """ node.children = node.children[:1] translator.visit_reference(node) def _depart_issue_node_latex(translator: LaTeXTranslator, node: IssueNode) -> None: """ Depart an :class:`~.IssueNode`. :param translator: :param node: The node being visited. """ translator.depart_reference(node) def get_issue_title(issue_url: str) -> Optional[str]: """ Returns the title of the issue with the given url, or :py:obj:`None` if the issue isn't found. :param issue_url: """ # noqa: D400 try: r = cache.session.get(issue_url, timeout=30) except requests.exceptions.RequestException: return None if r.status_code == 200: soup = BeautifulSoup(r.content, "html5lib") try: content = soup.find_all("span", attrs={"class": "js-issue-title"})[0].text except IndexError: try: # As of 13 Jan 2023 GitHub seems to use a bidirectional text tag instead content = soup.find_all("bdi", attrs={"class": "js-issue-title"})[0].text except IndexError: try: # As of 17 2025, GitHub seems to have changed this again... content = soup.find_all("bdi", attrs={"class": "Box-sc-g0xbh4-0 markdown-title"})[0].text except IndexError: # Give up return None content = xml.sax.saxutils.escape(content).replace('"', """) return content.strip() return None sphinx-toolbox-3.9.0/sphinx_toolbox/github/repos_and_users.py000066400000000000000000000164441475757360300246600ustar00rootroot00000000000000#!/usr/bin/env python3 # # repos_and_users.py """ Roles and nodes for referencing GitHub repositories and organizations. """ # # Copyright Β© 2020-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. # # Parts of the docstrings based on https://docutils.sourceforge.io/docs/howto/rst-roles.html # # stdlib from typing import Any, Dict, List, Tuple, Union # 3rd party from apeye.url import URL from docutils import nodes from docutils.nodes import system_message from docutils.parsers.rst.states import Inliner from sphinx.util.nodes import split_explicit_title from sphinx.writers.html5 import HTML5Translator from sphinx.writers.latex import LaTeXTranslator # this package from sphinx_toolbox.utils import GITHUB_COM, make_github_url __all__ = ( "GitHubObjectLinkNode", "repository_role", "user_role", "visit_github_object_link_node", "depart_github_object_link_node", ) class GitHubObjectLinkNode(nodes.reference): """ Docutils Node to represent a link to a GitHub repository. :param repo_name: The full name of the repository, in the form ``owner/name``. :param refuri: The URL of the issue / pull request on GitHub. .. clearpage:: """ name: str url: str def __init__( self, name: str, refuri: Union[str, URL], **kwargs, ): self.name = str(name) self.url = str(refuri) super().__init__(self.name, self.name, refuri=self.url) def copy(self) -> "GitHubObjectLinkNode": # pragma: no cover """ Return a copy of the :class:`sphinx_toolbox.github.repos.GitHubObjectLinkNode`. """ # This was required to stop some breakage, but it doesn't seem to run during the tests. obj = self.__class__(self.name, self.url) obj.document = self.document obj.source = self.source obj.line = self.line return obj def repository_role( name: str, rawtext: str, text: str, lineno: int, inliner: Inliner, options: Dict[str, Any] = {}, content: List[str] = [] ) -> Tuple[List[nodes.reference], List[system_message]]: """ Adds a link to the given repository on GitHub. :param name: The local name of the interpreted role, the role name actually used in the document. :param rawtext: A string containing the entire interpreted text input, including the role and markup. :param text: The interpreted text content. :param lineno: The line number where the interpreted text begins. :param inliner: The :class:`docutils.parsers.rst.states.Inliner` object that called :func:`~.repository_role`. It contains the several attributes useful for error reporting and document tree access. :param options: A dictionary of directive options for customization (from the ``role`` directive), to be interpreted by the function. Used for additional attributes for the generated elements and other functionality. :param content: A list of strings, the directive content for customization (from the ``role`` directive). To be interpreted by the function. :return: A list containing the created node, and a list containing any messages generated during the function. """ has_t, text, repo_name = split_explicit_title(text) repo_name = nodes.unescape(repo_name) repository_parts = nodes.unescape(repo_name).split('/') if len(repository_parts) != 2: return [], [inliner.document.reporter.warning(f"Invalid repository '{repo_name}'.")] # refnode: nodes.reference if has_t: refnode = nodes.reference( text, text, refuri=str(make_github_url(*repository_parts)), ) else: refnode = GitHubObjectLinkNode( name=repo_name, refuri=make_github_url(*repository_parts), ) return [refnode], [] def user_role( name: str, rawtext: str, text: str, lineno: int, inliner: Inliner, options: Dict[str, Any] = {}, content: List[str] = [] ) -> Tuple[List[nodes.reference], List[system_message]]: """ Adds a link to the given user / organization on GitHub. :param name: The local name of the interpreted role, the role name actually used in the document. :param rawtext: A string containing the entire interpreted text input, including the role and markup. :param text: The interpreted text content. :param lineno: The line number where the interpreted text begins. :param inliner: The :class:`docutils.parsers.rst.states.Inliner` object that called :func:`~.user_role`. It contains the several attributes useful for error reporting and document tree access. :param options: A dictionary of directive options for customization (from the ``role`` directive), to be interpreted by the function. Used for additional attributes for the generated elements and other functionality. :param content: A list of strings, the directive content for customization (from the ``role`` directive). To be interpreted by the function. :return: A list containing the created node, and a list containing any messages generated during the function. .. clearpage:: """ has_t, text, username = split_explicit_title(text) username = nodes.unescape(username) messages: List[system_message] = [] if has_t: refnode = nodes.reference( text, text, refuri=str(GITHUB_COM / username), ) else: refnode = GitHubObjectLinkNode( name=f"@{username}", refuri=GITHUB_COM / username, ) return [refnode], messages def visit_github_object_link_node(translator: HTML5Translator, node: GitHubObjectLinkNode) -> None: """ Visit a :class:`~.GitHubObjectLinkNode`. :param translator: :param node: The node being visited. """ translator.body.append(f'') translator.visit_reference(node) def depart_github_object_link_node(translator: HTML5Translator, node: GitHubObjectLinkNode) -> None: """ Depart an :class:`~.GitHubObjectLinkNode`. :param translator: :param node: The node being visited. """ translator.depart_reference(node) translator.body.append("") def _visit_github_object_link_node_latex(translator: LaTeXTranslator, node: GitHubObjectLinkNode) -> None: """ Visit a :class:`~.GitHubObjectLinkNode`. :param translator: :param node: The node being visited. """ node.children = node.children[:1] translator.visit_reference(node) def _depart_github_object_link_node_latex(translator: LaTeXTranslator, node: GitHubObjectLinkNode) -> None: """ Depart an :class:`~.GitHubObjectLinkNode`. :param translator: :param node: The node being visited. """ translator.depart_reference(node) sphinx-toolbox-3.9.0/sphinx_toolbox/installation.py000066400000000000000000000535061475757360300227040ustar00rootroot00000000000000#!/usr/bin/env python3 # # installation.py r""" .. extensions:: sphinx_toolbox.installation Configuration -------------- .. confval:: conda_channels :type: :class:`~typing.List`\[:class:`str`\] :required: False :default: ``[]`` The conda channels required to install the library from Anaconda. An alternative to setting it within the :rst:dir:`installation` directive. Usage ------- .. rst:directive:: .. installation:: name Adds a series of tabs providing installation instructions for the project from a number of sources. The directive takes a single required argument -- the name of the project. If the project uses a different name on PyPI and/or Anaconda, the ``:pypi-name:`` and ``:conda-name:`` options can be used to set the name for those repositories. .. rst:directive:option:: pypi :type: flag Flag to indicate the project can be installed from PyPI. .. rst:directive:option:: pypi-name: name :type: string The name of the project on PyPI. .. rst:directive:option:: conda :type: flag Flag to indicate the project can be installed with Conda. .. rst:directive:option:: conda-name: name :type: string The name of the project on Conda. .. rst:directive:option:: conda-channels: channels :type: comma separated strings Comma-separated list of required Conda channels. This can also be set via the :confval:`conda_channels` option. .. rst:directive:option:: github :type: flag Flag to indicate the project can be installed from GitHub. To use this option add the following to your ``conf.py``: .. code-block:: python extensions = [ ... 'sphinx_toolbox.github', ] github_username = '' github_repository = '' See :mod:`sphinx_toolbox.github` for more information. :bold-title:`Example` .. rest-example:: .. installation:: sphinx-toolbox :pypi: :anaconda: :conda-channels: domdfcoding,conda-forge :github: .. rst:directive:: extensions Shows instructions on how to enable a Sphinx extension. The directive takes a single argument -- the name of the extension. .. rst:directive:option:: import-name :type: string The name used to import the extension, if different from the name of the extension. .. rst:directive:option:: no-preamble :type: flag Disables the preamble text. .. rst:directive:option:: no-postamble :type: flag Disables the postamble text. .. rst:directive:option:: first :type: flag Puts the entry for extension before its dependencies. By default is is placed at the end. .. versionadded:: 0.4.0 :bold-title:`Example` .. rest-example:: .. extensions:: sphinx-toolbox :import-name: sphinx_toolbox sphinx.ext.viewcode sphinx_tabs.tabs sphinx-prompt API Reference -------------- """ # noqa: D400 # # Copyright Β© 2020-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 inspect import re import textwrap import warnings from typing import Any, Callable, Dict, List, Optional, Tuple # 3rd party import dict2css import sphinx.environment from docutils import nodes from docutils.parsers.rst import directives from docutils.statemachine import ViewList from domdf_python_tools.paths import PathPlus from domdf_python_tools.stringlist import StringList from domdf_python_tools.words import word_join from sphinx.application import Sphinx from sphinx.environment import BuildEnvironment from sphinx.util.docutils import SphinxDirective # this package from sphinx_toolbox import _css from sphinx_toolbox.utils import Config, OptionSpec, Purger, SphinxExtMetadata, flag, metadata_add_version __all__ = ( "InstallationDirective", "ExtensionsDirective", "make_installation_instructions", "Sources", "sources", "pypi_installation", "conda_installation", "github_installation", "installation_node_purger", "extensions_node_purger", "copy_asset_files", "setup", ) class _Purger(Purger): def purge_nodes(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None: # pragma: no cover """ Remove all redundant nodes. :param app: The Sphinx application. :param env: The Sphinx build environment. :param docname: The name of the document to remove nodes for. """ if not hasattr(env, self.attr_name): return setattr(env, self.attr_name, []) installation_node_purger = _Purger("all_installation_node_nodes") extensions_node_purger = Purger("all_extensions_node_nodes") class Sources(List[Tuple[str, str, Callable, Callable, Optional[Dict[str, Callable]]]]): """ Class to store functions that provide installation instructions for different sources. The syntax of each entry is: .. code-block:: python (option_name, source_name, getter_function, validator_function, extra_options) * ``option_name`` -- a string to use in the directive to specify the source to use, * ``source_name`` -- a string to use in the tabs to indicate the installation source, * ``getter_function`` -- the function that returns the installation instructions, * ``validator_function`` -- a function to validate the option value provided by the user, * ``extra_options`` -- a mapping of additional options for the directive that are used by the getter_function. .. autoclasssumm:: Sources :autosummary-sections: ;; """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) _args = ["options", "env"] # pylint: disable=W8301 _directive_name = "installation" def register( self, option_name: str, source_name: str, validator: Callable = directives.unchanged, extra_options: Optional[Dict[str, Callable]] = None, ) -> Callable: """ Decorator to register a function. The function must have the following signature: .. code-block:: python def function( options: Dict[str, Any], # Mapping of option names to values. env: sphinx.environment.BuildEnvironment, # The Sphinx build environment. ) -> List[str]: ... :param option_name: A string to use in the directive to specify the source to use. :param source_name: A string to use in tabbed installation instructions to represent this source. :param validator: A function to validate the option value provided by the user. :default validator: :func:`docutils.parsers.rst.directives.unchanged` :param extra_options: An optional mapping of extra option names to validator functions. :default extra_options: ``{}`` :return: The registered function. :raises: :exc:`SyntaxError` if the decorated function does not take the correct arguments. """ def _decorator(function: Callable) -> Callable: signature = inspect.signature(function) if list(signature.parameters.keys()) != self._args: raise SyntaxError( # pragma: no cover "The decorated function must take only the following arguments: " f"{word_join(self._args, use_repr=True, oxford=True)}" ) self.append((option_name, source_name, function, validator, extra_options or {})) setattr(function, f"_{self._directive_name}_registered", True) return function return _decorator #: Instance of :class:`~.Sources`. sources: Sources = Sources() # pypi_name: The name of the project on PyPI. @sources.register("pypi", "PyPI", flag, {"pypi-name": directives.unchanged}) def pypi_installation( options: Dict[str, Any], env: sphinx.environment.BuildEnvironment, ) -> List[str]: """ Source to provide instructions for installing from PyPI. :param options: Mapping of option names to values. :param env: The Sphinx build environment. """ if "pypi-name" in options: pypi_name = options["pypi-name"] elif "project_name" in options: pypi_name = options["project_name"] else: raise ValueError("No PyPI project name supplied for the PyPI installation instructions.") return [".. prompt:: bash", '', f" python3 -m pip install {pypi_name} --user"] # conda_name: The name of the project on PyPI. @sources.register( "anaconda", "Anaconda", flag, {"conda-name": directives.unchanged, "conda-channels": directives.unchanged}, ) def conda_installation( options: Dict[str, Any], env: sphinx.environment.BuildEnvironment, ) -> List[str]: """ Source to provide instructions for installing from Anaconda. :param options: Mapping of option names to values. :param env: The Sphinx build environment. """ if "conda-name" in options: conda_name = options["conda-name"] elif "pypi-name" in options: conda_name = options["pypi-name"] elif "project_name" in options: conda_name = options["project_name"] else: raise ValueError("No username supplied for the Anaconda installation instructions.") lines: StringList = StringList() lines.indent_type = " " if "conda-channels" in options: channels = str(options["conda-channels"]).split(',') else: channels = env.config.conda_channels if channels: lines.append("First add the required channels\n\n.. prompt:: bash\n") with lines.with_indent_size(lines.indent_size + 1): for channel in channels: # pylint: disable=use-list-copy lines.append(f"conda config --add channels https://conda.anaconda.org/{channel.strip()}") lines.append("\nThen install") if lines: lines.blankline(ensure_single=True) lines.append(f".. prompt:: bash") lines.blankline(ensure_single=True) with lines.with_indent_size(lines.indent_size + 1): lines.append(f"conda install {conda_name}") lines.blankline(ensure_single=True) return list(lines) @sources.register("github", "GitHub", flag) def github_installation( options: Dict[str, Any], env: sphinx.environment.BuildEnvironment, ) -> List[str]: """ Source to provide instructions for installing from GitHub. :param options: Mapping of option names to values. :param env: The Sphinx build environment. """ if "sphinx_toolbox.github" not in env.app.extensions: raise ValueError( "The 'sphinx_toolbox.github' extension is required for the " ":github: option but it is not enabled!" ) username = getattr(env.config, "github_username", None) if username is None: raise ValueError("'github_username' has not been set in 'conf.py'!") repository = getattr(env.config, "github_repository", None) if repository is None: raise ValueError("'github_repository' has not been set in 'conf.py'!") return [ ".. prompt:: bash", '', f" python3 -m pip install git+https://github.com/{username}/{repository}@master --user" ] class InstallationDirective(SphinxDirective): """ Directive to show installation instructions. """ has_content: bool = True optional_arguments: int = 1 # The name of the project; can be overridden for each source # Registered sources option_spec: OptionSpec = { # type: ignore[assignment] source[0].lower(): source[3] for source in sources # pylint: disable=not-an-iterable } # Extra options for registered sources for source in sources: # pylint: disable=not-an-iterable,loop-global-usage if source[4] is not None: option_spec.update(source[4]) # type: ignore[attr-defined] options: Dict[str, Any] """ Mapping of option names to values. The options are as follows: * **pypi**: Flag to indicate the project can be installed from PyPI. * **pypi-name**: The name of the project on PyPI. * **conda**: Flag to indicate the project can be installed with Conda. * **conda-name**: The name of the project on Conda. * **conda-channels**: Comma-separated list of required Conda channels. * **github**: Flag to indicate the project can be installed from GitHub. The GitHub username and repository are configured in ``conf.py`` and are available in ``env.config``. """ def run_html(self) -> List[nodes.Node]: """ Generate output for ``HTML`` builders. :rtype: .. latex:clearpage:: """ targetid = f'installation-{self.env.new_serialno("sphinx-toolbox installation"):d}' targetnode = nodes.target('', '', ids=[targetid]) content = make_installation_instructions(self.options, self.env) view = ViewList(content) installation_node = nodes.paragraph(rawsource=content) # type: ignore[arg-type] self.state.nested_parse(view, self.content_offset, installation_node) # type: ignore[arg-type] installation_node_purger.add_node(self.env, installation_node, targetnode, self.lineno) return [targetnode, installation_node] def run_generic(self) -> List[nodes.Node]: """ Generate generic reStructuredText output. """ targetid = f'installation-{self.env.new_serialno("sphinx-toolbox installation"):d}' targetnode = nodes.target('', '', ids=[targetid]) tabs: Dict[str, List[str]] = _get_installation_instructions(self.options, self.env) if not tabs: warnings.warn("No installation source specified. No installation instructions will be shown.") return [] nodes_to_return: List[nodes.Node] = [targetnode] non_word_sub = re.compile(r'\W+') for tab_name, tab_content in tabs.items(): # pylint: disable=loop-global-usage section_id = non_word_sub.sub('_', tab_name) section = nodes.section(ids=[f"{targetid}-{section_id}"]) section += nodes.title(tab_name, tab_name) nodes_to_return.append(section) installation_node_purger.add_node(self.env, section, targetnode, self.lineno) view = ViewList(tab_content) paragraph_node = nodes.paragraph(rawsource=tab_content) # type: ignore[arg-type] self.state.nested_parse(view, self.content_offset, paragraph_node) # type: ignore[arg-type] nodes_to_return.append(paragraph_node) installation_node_purger.add_node(self.env, paragraph_node, targetnode, self.lineno) # pylint: enable=loop-global-usage return nodes_to_return def run(self) -> List[nodes.Node]: """ Create the installation node. """ assert self.env.app.builder is not None if self.arguments: self.options["project_name"] = self.arguments[0] if self.env.app.builder.format.lower() == "html": return self.run_html() else: return self.run_generic() def make_installation_instructions(options: Dict[str, Any], env: BuildEnvironment) -> List[str]: """ Make the content of an installation node. :param options: :param env: The Sphinx build environment. """ tabs: Dict[str, List[str]] = _get_installation_instructions(options, env) if not tabs: warnings.warn("No installation source specified. No installation instructions will be shown.") return [] content = StringList([".. tabs::", '']) content.set_indent_type(" ") for tab_name, tab_content in tabs.items(): with content.with_indent_size(1): content.append(f".. tab:: {tab_name}") content.blankline(ensure_single=True) with content.with_indent_size(2): content.extend([f"{line}" if line else '' for line in tab_content]) # pylint: disable=loop-invariant-statement return list(content) def _get_installation_instructions(options: Dict[str, Any], env: BuildEnvironment) -> Dict[str, List[str]]: """ Returns a mapping of tab/section names to their content. :param options: :param env: The Sphinx build environment. """ tabs: Dict[str, List[str]] = {} # pylint: disable=loop-global-usage,use-dict-comprehension for option_name, source_name, getter_function, validator_function, extra_options in sources: # pylint: enable=loop-global-usage,use-dict-comprehension if option_name in options: tabs[f"from {source_name}"] = getter_function(options, env) return tabs class ExtensionsDirective(SphinxDirective): """ Directive to show instructions for enabling the extension. """ has_content: bool = True # Other required extensions, one per line optional_arguments: int = 1 # The name of the project option_spec: OptionSpec = { # type: ignore[assignment] "import-name": directives.unchanged_required, # If different to project name "no-preamble": flag, "no-postamble": flag, "first": flag, } def run(self) -> List[nodes.Node]: """ Create the extensions node. :rtype: .. latex:clearpage:: """ extensions = list(self.content) first = self.options.get("first", False) if "import-name" in self.options and first: extensions.insert(0, self.options["import-name"]) elif "import-name" in self.options: extensions.append(self.options["import-name"]) elif first: extensions.insert(0, self.arguments[0]) else: extensions.append(self.arguments[0]) targetid = f'extensions-{self.env.new_serialno("sphinx-toolbox extensions"):d}' targetnode = nodes.target('', '', ids=[targetid]) top_text = ( ".. latex:vspace:: 10px", ".. rst-class:: sphinx-toolbox-extensions", '', f" Enable ``{self.arguments[0]}`` by adding the following", f" to the ``extensions`` variable in your ``conf.py``:", ) bottom_text = textwrap.dedent( r""" .. raw:: latex \begin{flushleft} For more information see https://www.sphinx-doc.org/en/master/usage/extensions#third-party-extensions . .. raw:: latex \end{flushleft} """ ).expandtabs(4).splitlines() if "no-preamble" in self.options: content = [] else: content = [*top_text, ''] content.append(".. code-block:: python", ) if "no-postamble" not in self.options: content.append(" :class: sphinx-toolbox-extensions") content.extend([ '', " extensions = [", " ...", ]) for extension in extensions: # pylint: disable=use-list-copy content.append(f" {extension!r},") content.extend([" ]", '']) if "no-postamble" not in self.options: content.extend(bottom_text) extensions_node = nodes.paragraph(rawsource=content) # type: ignore[arg-type] self.state.nested_parse(ViewList(content), self.content_offset, extensions_node) # type: ignore[arg-type] extensions_node_purger.add_node(self.env, extensions_node, targetnode, self.lineno) return [targetnode, extensions_node] def copy_asset_files(app: Sphinx, exception: Optional[Exception] = None) -> None: """ Copy additional stylesheets into the HTML build directory. .. versionadded:: 1.2.0 :param app: The Sphinx application. :param exception: Any exception which occurred and caused Sphinx to abort. """ if exception: # pragma: no cover return if app.builder is None or app.builder.format.lower() != "html": # pragma: no cover return static_dir = PathPlus(app.outdir) / "_static" static_dir.maybe_make(parents=True) dict2css.dump(_css.installation_styles, static_dir / "sphinx_toolbox_installation.css", minify=True) (static_dir / "sphinx_toolbox_installation.js").write_lines([ "// Based on https://github.com/executablebooks/sphinx-tabs/blob/master/sphinx_tabs/static/tabs.js", "// Copyright (c) 2017 djungelorm", "// MIT Licensed", '', "function deselectTabset(target) {", " const parent = target.parentNode;", " const grandparent = parent.parentNode;", '', ' if (parent.parentNode.parentNode.getAttribute("id").startsWith("installation")) {', '', " // Hide all tabs in current tablist, but not nested", " Array.from(parent.children).forEach(t => {", ' if (t.getAttribute("name") !== target.getAttribute("name")) {', ' t.setAttribute("aria-selected", "false");', " }", " });", '', " // Hide all associated panels", " Array.from(grandparent.children).slice(1).forEach(p => { // Skip tablist", ' if (p.getAttribute("name") !== target.getAttribute("name")) {', ' p.setAttribute("hidden", "false")', " }", " });", " }", '', " else {", " // Hide all tabs in current tablist, but not nested", " Array.from(parent.children).forEach(t => {", ' t.setAttribute("aria-selected", "false");', " });", '', " // Hide all associated panels", " Array.from(grandparent.children).slice(1).forEach(p => { // Skip tablist", ' p.setAttribute("hidden", "true")', " });", " }", '', '}', '', "// Compatibility with sphinx-tabs 2.1.0 and later", "function deselectTabList(tab) {deselectTabset(tab)}", '', ]) def _on_config_inited(app: Sphinx, config: Config) -> None: app.add_css_file("sphinx_toolbox_installation.css") app.add_js_file("sphinx_toolbox_installation.js") @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.installation`. .. versionadded:: 0.7.0 :param app: The Sphinx application. """ if "sphinx_inline_tabs" not in getattr(app, "extensions", ()): app.setup_extension("sphinx_tabs.tabs") app.setup_extension("sphinx-prompt") app.setup_extension("sphinx_toolbox._css") app.setup_extension("sphinx_toolbox.latex") app.add_config_value("conda_channels", [], "env", types=[list]) # Instructions for installing a python package app.add_directive("installation", InstallationDirective) app.connect("env-get-outdated", installation_node_purger.get_outdated_docnames) # app.connect("env-purge-doc", installation_node_purger.purge_nodes) # Instructions for enabling a sphinx extension app.add_directive("extensions", ExtensionsDirective) app.connect("env-purge-doc", extensions_node_purger.purge_nodes) # Ensure this happens after tabs.js has been added app.connect("config-inited", _on_config_inited, priority=510) app.connect("build-finished", copy_asset_files) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/issues.py000066400000000000000000000077331475757360300215170ustar00rootroot00000000000000#!/usr/bin/env python3 # # issues.py r""" Add links to GitHub issues and Pull Requests. .. extensions:: sphinx_toolbox.issues Usage ------ .. latex:vspace:: -10px .. rst:role:: issue Role which shows a link to the given issue on GitHub. If the issue exists, the link has a tooltip that shows the title of the issue. **Example** .. rest-example:: :issue:`1` You can also reference an issue in a different repository by adding the repository name inside ``<>``. .. rest-example:: :issue:`7680 ` .. rst:role:: pull Role which shows a link to the given pull request on GitHub. If the pull requests exists, the link has a tooltip that shows the title of the pull requests. **Example** .. rest-example:: :pull:`2` You can also reference a pull request in a different repository by adding the repository name inside ``<>``. .. rest-example:: :pull:`7671 ` .. versionchanged:: 2.4.0 :rst:role:`issue` and :rst:role:`pull` now show the repository name when the name differs from that configured in ``conf.py``. .. versionchanged:: 2.4.0 These directives are also available in the :mod:`~.sphinx_toolbox.github` domain. The only difference between the :rst:role:`issue` and :rst:role:`pull` roles is in the URL. GitHub uses the same numbering scheme for issues and pull requests, and automatically redirects to the pull request if the user tries to navigate to an issue with that same number. Caching ----------- HTTP requests to obtain issue/pull request titles are cached for four hours. To clear the cache manually, run: .. prompt:: bash python3 -m sphinx_toolbox API Reference --------------- .. versionchanged:: 2.4.0 The following moved to :mod:`sphinx_toolbox.github.issues`: * :class:`~.IssueNode` * :class:`~.IssueNodeWithName` * :func:`~.issue_role` * :func:`~.pull_role` * :func:`~.visit_issue_node` * :func:`~.depart_issue_node` * :func:`~.get_issue_title` .. latex:vspace:: 10px """ # # Copyright Β© 2020-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. # # Based on pyspecific.py from the Python documentation. # Copyright 2008-2014 by Georg Brandl. # Licensed under the PSF License 2.0 # # Parts of the docstrings based on https://docutils.sourceforge.io/docs/howto/rst-roles.html # # 3rd party from sphinx.application import Sphinx # this package from sphinx_toolbox.github.issues import issue_role, pull_role from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ( "issue_role", "pull_role", "setup", ) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.issues`. .. versionadded:: 1.0.0 :param app: The Sphinx application. """ app.setup_extension("sphinx_toolbox.github") # Link to GH issue app.add_role("issue", issue_role) # Link to GH pull request app.add_role("pr", pull_role) app.add_role("pull", pull_role) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/latex/000077500000000000000000000000001475757360300207355ustar00rootroot00000000000000sphinx-toolbox-3.9.0/sphinx_toolbox/latex/__init__.py000066400000000000000000000434211475757360300230520ustar00rootroot00000000000000#!/usr/bin/env python3 # # latex.py r""" Sphinx utilities for LaTeX builders. .. versionadded:: 2.8.0 In addition to the developer API (see below), :mod:`sphinx_toolbox.latex` configures Sphinx and LaTeX to correctly handle symbol footnotes. .. versionchanged:: 2.12.0 Sphinx is also configured to respect ``.. only:: html`` etc. directives surrounding toctree directives when determining the overall toctree depth. .. extensions:: sphinx_toolbox.latex .. latex:vspace:: -5px Example Footnotes -------------------- .. latex:vspace:: -10px | Hello [1]_ | Goodbye [2]_ | Symbol [*]_ | Another Symbol [*]_ | Number Again [3]_ | Symbol 3 [*]_ | Symbol 4 [*]_ | Symbol 5 [*]_ | Symbol 6 [*]_ | Symbol 7 [*]_ | Symbol 8 [*]_ | Symbol 9 [*]_ .. latex:vspace:: 20px .. [1] One .. [2] Two .. [*] Buckle my shoe .. [*] The second symbol .. [3] The number after the symbol .. [*] Symbol 3 .. [*] Symbol 4 .. [*] Symbol 5 .. [*] Symbol 6 .. [*] Symbol 7 .. [*] Symbol 8 .. [*] Symbol 9 Usage ------- .. latex:vspace:: -10px .. rst:directive:: latex:samepage samepage Configures LaTeX to make all content within this directive appear on the same page. This can be useful to avoid awkward page breaks. This directive has no effect with non-LaTeX builders. .. versionadded:: 2.9.0 .. rst:directive:: latex:clearpage clearpage Configures LaTeX to start a new page. This directive has no effect with non-LaTeX builders. .. versionadded:: 2.10.0 .. seealso:: :rst:dir:`latex:cleardoublepage` .. rst:directive:: latex:cleardoublepage cleardoublepage Configures LaTeX to start a new page. In a two-sided printing it also makes the next page a right-hand (odd-numbered) page, inserting a blank page if necessary. This directive has no effect with non-LaTeX builders. .. versionadded:: 2.10.0 .. seealso:: :rst:dir:`latex:clearpage` .. rst:directive:: .. latex:vspace:: space Configures LaTeX to add or remove vertical space. The value for ``space`` is passed verbatim to the ``\vspace{}`` command. Ensure you pass a valid value. This directive has no effect with non-LaTeX builders. .. versionadded:: 2.11.0 .. latex:clearpage:: API Reference ---------------- .. latex:vspace:: -20px """ # # 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. # # PatchedLaTeXBuilder based on Sphinx # Copyright (c) 2007-2020 by the Sphinx team. # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib import re from os.path import join as joinpath from textwrap import dedent from typing import Any, List, Optional, cast # 3rd party import sphinx from docutils import nodes from docutils.frontend import OptionParser from docutils.transforms.references import Footnotes from domdf_python_tools.paths import PathPlus from domdf_python_tools.stringlist import DelimitedList from sphinx import addnodes from sphinx.application import Sphinx from sphinx.builders.latex import LaTeXBuilder from sphinx.domains import Domain from sphinx.environment import BuildEnvironment from sphinx.locale import __ from sphinx.util.docutils import SphinxDirective, SphinxFileOutput from sphinx.util.nodes import process_only_nodes from sphinx.writers.latex import LaTeXTranslator, LaTeXWriter # this package from sphinx_toolbox.utils import Config, SphinxExtMetadata, metadata_add_version try: # 3rd party from sphinx.util.display import progress_message except ImportError: # 3rd party from sphinx.util import progress_message # type: ignore[no-redef] _ = BuildEnvironment __all__ = ( "use_package", "visit_footnote", "depart_footnote", "SamepageDirective", "ClearPageDirective", "ClearDoublePageDirective", "VSpaceDirective", "LaTeXDomain", "replace_unknown_unicode", "better_header_layout", "configure", "setup", ) footmisc_symbols = ['0', *Footnotes.symbols] # footmisc_symbols = ['0', '*', '†', '‑', 'Β§', 'ΒΆ', 'β€–', "**", "††", "‑‑"] def visit_footnote(translator: LaTeXTranslator, node: nodes.footnote) -> None: """ Visit a :class:`docutils.nodes.footnote` node with the LaTeX translator. Unlike the default ``visit_footnote`` function, this one handles footnotes using symbols. .. versionadded:: 2.8.0 :param translator: :param node: """ translator.in_footnote += 1 footnote_id = str(cast(nodes.label, node[0]).astext()) if not translator.in_parsed_literal: translator.body.append("%\n") if not footnote_id.isnumeric() and footnote_id in footmisc_symbols: footnote_id = str(footmisc_symbols.index(footnote_id)) translator.body.append(r"\renewcommand\thefootnote{\thesymbolfootnote}") translator.body.append(rf"\begin{{footnote}}[{footnote_id}]") translator.body.append("\\sphinxAtStartFootnote\n") def depart_footnote(translator: LaTeXTranslator, node: nodes.footnote) -> None: """ Depart a :class:`docutils.nodes.footnote` node with the LaTeX translator. .. versionadded:: 2.8.0 :param translator: :param node: """ if not translator.in_parsed_literal: translator.body.append("%\n") translator.body.append(r"\end{footnote}") translator.body.append(r"\renewcommand\thefootnote{\thenumberfootnote}") translator.in_footnote -= 1 def use_package(package: str, config: Config, *args: str, **kwargs: str) -> None: r""" Configure LaTeX to use the given package. The ``\usepackage`` entry is added to the :py:obj:`sphinx.config.Config.latex_elements` ``["preamble"]`` attribute. :param package: :param config: :param \*args: Passed as options to the LaTeX package. :param \*\*kwargs: Passed as named options to the LaTeX package. """ options: DelimitedList[str] = DelimitedList() options.extend(args) options.extend(map("{}={}".format, kwargs.items())) use_string = rf"\usepackage[{options:,}]{{{package}}}" if not hasattr(config, "latex_elements") or not config.latex_elements: # pragma: no cover config.latex_elements = {} latex_preamble = config.latex_elements.get("preamble", '') if use_string not in latex_preamble: config.latex_elements["preamble"] = f"{latex_preamble}\n{use_string}" class SamepageDirective(SphinxDirective): """ Directive which configures LaTeX to make all content within this directive appear on the same page. This can be useful to avoid awkward page breaks. This directive has no effect with non-LaTeX builders. .. versionadded:: 2.9.0 """ has_content = True def run(self) -> List[nodes.Node]: """ Process the content of the directive. """ content_node = nodes.container(rawsource='\n'.join(self.content)) self.state.nested_parse(self.content, self.content_offset, content_node) return [ nodes.raw('', r"\par\begin{samepage}", format="latex"), content_node, nodes.raw('', r"\end{samepage}\par", format="latex"), ] class ClearPageDirective(SphinxDirective): """ Directive which configures LaTeX to start a new page. This directive has no effect with non-LaTeX builders. .. versionadded:: 2.10.0 .. seealso:: :class:`~.ClearDoublePageDirective` """ has_content = True def run(self) -> List[nodes.Node]: """ Process the content of the directive. """ return [nodes.raw('', r"\clearpage", format="latex")] class ClearDoublePageDirective(SphinxDirective): """ Directive which configures LaTeX to start a new page. In a two-sided printing it also makes the next page a right-hand (odd-numbered) page, inserting a blank page if necessary. This directive has no effect with non-LaTeX builders. .. versionadded:: 2.10.0 .. seealso:: :class:`~.ClearPageDirective` """ has_content = True def run(self) -> List[nodes.Node]: """ Process the content of the directive. """ return [nodes.raw('', r"\cleardoublepage", format="latex")] class VSpaceDirective(SphinxDirective): """ Directive which configures LaTeX to add or remove vertical space. This directive has no effect with non-LaTeX builders. .. versionadded:: 2.11.0 """ required_arguments = 1 # the space def run(self) -> List[nodes.Node]: """ Process the content of the directive. """ return [nodes.raw('', f"\n\\vspace{{{self.arguments[0]}}}\n", format="latex")] class LaTeXDomain(Domain): """ Domain containing various LaTeX-specific directives. .. versionadded:: 2.11.0 """ name = "latex" label = "LaTeX" directives = { "samepage": SamepageDirective, "clearpage": ClearPageDirective, "cleardoublepage": ClearDoublePageDirective, "vspace": VSpaceDirective, } def replace_unknown_unicode(app: Sphinx, exception: Optional[Exception] = None) -> None: r""" Replaces certain unknown unicode characters in the Sphinx LaTeX output with the best equivalents. .. only:: html The mapping is as follows: * β™  -- \spadesuit * β™₯ -- \heartsuit * ♦ -- \diamondsuit * ♣ -- \clubsuit * Zero width space -- \hspace{0pt} * ΞΌ -- \textmu * ≑ -- \equiv (new in version 2.11.0) * β‰ˆ -- \approx (new in version 2.12.0) * β‰₯ -- \geq (new in version 2.13.0) * ≀ -- \leq (new in version 2.13.0) This function can be hooked into the :event:`build-finished` event as follows: .. code-block:: python app.connect("build-finished", replace_unknown_unicode) .. versionadded:: 2.9.0 :param app: The Sphinx application. :param exception: Any exception which occurred and caused Sphinx to abort. """ if exception: # pragma: no cover return if app.builder is None or app.builder.name.lower() != "latex": return builder = cast(LaTeXBuilder, app.builder) output_file = PathPlus(builder.outdir) / f"{builder.titles[0][1].lower()}.tex" output_content = output_file.read_text() output_content = output_content.replace('β™ ', r' $\spadesuit$ ') output_content = output_content.replace('β™₯', r' $\heartsuit$ ') output_content = output_content.replace('♦', r' $\diamondsuit$ ') output_content = output_content.replace('♣', r' $\clubsuit$ ') output_content = output_content.replace('\u200b', r'\hspace{0pt}') # Zero width space output_content = output_content.replace('ΞΌ', r"\textmu{}") output_content = output_content.replace('≑', r" $\equiv$ ") output_content = output_content.replace('β‰ˆ', r" $\approx$ ") output_content = output_content.replace('β‰₯', r" $\geq$ ") output_content = output_content.replace('≀', r" $\leq$ ") output_file.write_clean(output_content) def better_header_layout( config: Config, space_before: int = 10, space_after: int = 20, ) -> None: """ Makes LaTeX chapter names lowercase, and adjusts the spacing above and below the chapter name. .. versionadded:: 2.10.0 :param config: The Sphinx configuration object. :param space_before: The space, in pixels, before the chapter name. :param space_after: The space, in pixels, after the chapter name. """ begin = "% begin st better header layout" end = "% end st better header layout" commands = rf""" {begin} \makeatletter \renewcommand{{\DOCH}}{{% \mghrulefill{{\RW}}\par\nobreak \CNV\FmN{{\@chapapp}}\par\nobreak \CNoV\TheAlphaChapter\par\nobreak \vskip -1\baselineskip\vskip 5pt\mghrulefill{{\RW}}\par\nobreak \vskip 10\p@ }} \renewcommand{{\DOTI}}[1]{{% \CTV\FmTi{{#1}}\par\nobreak \vskip {space_before}\p@ }} \renewcommand{{\DOTIS}}[1]{{% \CTV\FmTi{{#1}}\par\nobreak \vskip {space_after}\p@ }} \makeatother {end} """ if not hasattr(config, "latex_elements") or not config.latex_elements: # pragma: no cover config.latex_elements = {} latex_preamble = config.latex_elements.get("preamble", '') if begin in latex_preamble: config.latex_elements["preamble"] = re.sub( f"{begin}.*{end}", dedent(commands), latex_preamble, count=1, flags=re.DOTALL, ) else: config.latex_elements["preamble"] = f"{latex_preamble}\n{dedent(commands)}" config.latex_elements["fncychap"] = "\\usepackage[Bjarne]{fncychap}\n\\ChNameAsIs\n\\ChTitleAsIs\n" class PatchedLaTeXBuilder(LaTeXBuilder): """ Patched version of Sphinx's LaTeX builder which skips toctrees under ``.. only:: html`` etc. directives when determining the max toctree depth. The default behaviour uses the ``:maxdepth:`` option of the first toctree, irrespective of whether the toctree will exist with the LaTeX builder. """ # noqa: D400 # TODO: respect toctree caption for LaTeX table of contents too # \addto\captionsenglish{\renewcommand{\contentsname}{Documentation}} def write(self, *ignored: Any) -> None: assert self.env is not None docwriter = LaTeXWriter(self) docsettings: Any = OptionParser( defaults=self.env.settings, components=(docwriter, ), read_config_files=True, ).get_default_values() if sphinx.version_info <= (4, 0): # 3rd party from sphinx.builders.latex import patch_settings # type: ignore[attr-defined] patch_settings(docsettings) self.init_document_data() self.write_stylesheet() for entry in self.document_data: docname, targetname, title, author, themename = entry[:5] theme = self.themes.get(themename) toctree_only = False if len(entry) > 5: toctree_only = entry[5] destination = SphinxFileOutput( destination_path=joinpath(self.outdir, targetname), encoding="utf-8", overwrite_if_changed=True ) with progress_message(__("processing %s") % targetname): doctree = self.env.get_doctree(docname) process_only_nodes(doctree, self.tags) if hasattr(doctree, "findall"): toctree = next(doctree.findall(addnodes.toctree), None) else: toctree = next(iter(doctree.traverse(addnodes.toctree)), None) if toctree and toctree.get("maxdepth") > 0: tocdepth = toctree.get("maxdepth") else: tocdepth = None doctree = self.assemble_doctree( docname, toctree_only, appendices=(self.config.latex_appendices if theme.name != "howto" else []) ) doctree["docclass"] = theme.docclass doctree["contentsname"] = self.get_contentsname(docname) doctree["tocdepth"] = tocdepth self.post_process_images(doctree) self.update_doc_context(title, author, theme) if hasattr(self, "update_context"): # pragma: no cover # Only present in newer Sphinx versions self.update_context() with progress_message(__("writing")): # pylint: disable=loop-invariant-statement docsettings._author = author docsettings._title = title docsettings._contentsname = doctree["contentsname"] docsettings._docname = docname docsettings._docclass = theme.name doctree.settings = docsettings docwriter.theme = theme docwriter.write(doctree, destination) # pylint: enable=loop-invariant-statement def configure(app: Sphinx, config: Config) -> None: """ Configure :mod:`sphinx_toolbox.latex`. :param app: The Sphinx application. :param config: """ if not hasattr(config, "latex_elements") or not config.latex_elements: # pragma: no cover config.latex_elements = {} latex_preamble = config.latex_elements.get("preamble", '') command_string = r"\newcommand\thesymbolfootnote{\fnsymbol{footnote}}\let\thenumberfootnote\thefootnote" if command_string not in latex_preamble: config.latex_elements["preamble"] = f"{latex_preamble}\n{command_string}" @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.latex`. .. versionadded:: 2.8.0 :param app: The Sphinx application. """ app.add_node(nodes.footnote, latex=(visit_footnote, depart_footnote), override=True) app.add_directive("samepage", SamepageDirective) app.add_directive("clearpage", ClearPageDirective) app.add_directive("cleardoublepage", ClearDoublePageDirective) app.add_domain(LaTeXDomain) app.add_builder(PatchedLaTeXBuilder, override=True) app.connect("config-inited", configure) return {} sphinx-toolbox-3.9.0/sphinx_toolbox/latex/layout.py000066400000000000000000000160551475757360300226330ustar00rootroot00000000000000#!/usr/bin/env python3 # # layout.py r""" Makes minor adjustments to the LaTeX layout. * Increases the whitespace above function signatures by 5px, to prevent the function visually merging with the previous one. * Remove unnecessary indentation and allow "raggedright" for the fields in the body of functions, which prevents ugly whitespace and line breaks. * Disables justification for function signatures. This is a backport of changes from Sphinx 4 added in :github:pull:`8997 `. .. versionadded:: 2.12.0 * With Sphinx 3.5, doesn't add ``\sphinxAtStartPar`` before every paragraph. The change in :github:issue:`8781 ` was to solve an issue with *tables*, but it isn't clear why it then gets added for *every* paragraph so this extension removes it. .. versionadded:: 2.13.0 * Configures hyperref to apply correct page numbering to the frontmatter. .. versionadded:: 2.14.0 * Optionally, configures the ``needspace`` package. The :confval:`needspace_amount` option can be set in ``conf.py`` to add the ``\needspace{}`` command before each ``addnodes.desc`` node (i.e. a function or class description). The amount of space is set by the ``needspace_amount`` option, e.g.: .. code-block:: python needspace_amount = r"4\baselineskip" .. versionadded:: 3.0.0 .. confval:: needspace_amount :type: :class:`str` :default: :py:obj:`None` Sets the value for the ``\needspace{}`` command. Values should be in the form ``r"4\baselineskip"``. .. versionadded:: 3.0.0 .. versionadded:: 2.10.0 .. versionchanged:: 3.0.0 Moved from :mod:`sphinx_toolbox.tweaks.latex_layout`. .. latex:clearpage:: .. extensions:: sphinx_toolbox.latex.layout ----- """ # # 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. # # 3rd party from docutils import nodes from domdf_python_tools.stringlist import StringList from sphinx import addnodes from sphinx.application import Sphinx from sphinx.builders.latex.nodes import footnotetext from sphinx.config import Config from sphinx.writers.latex import LaTeXTranslator # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ("setup", ) def visit_desc(translator: LaTeXTranslator, node: addnodes.desc) -> None: translator.body.append('\n\n\\vspace{5px}') needspace_amount = getattr(translator.config, "needspace_amount") if needspace_amount: translator.body.append(f"\\needspace{{{needspace_amount}}}") if "sphinxcontrib.toctree_plus" in translator.config.extensions: # 3rd party from sphinxcontrib import toctree_plus # type: ignore[import-untyped] # nodep toctree_plus.visit_desc(translator, node) else: LaTeXTranslator.visit_desc(translator, node) def visit_field_list(translator: LaTeXTranslator, node: nodes.field_list) -> None: translator.body.append('\\vspace{10px}\\begin{flushleft}\\begin{description}\n') if translator.table: # pragma: no cover translator.table.has_problematic = True def depart_field_list(translator: LaTeXTranslator, node: nodes.field_list) -> None: translator.body.append('\\end{description}\\end{flushleft}\\vspace{10px}\n') def configure(app: Sphinx, config: Config) -> None: """ Configure Sphinx Extension. :param app: The Sphinx application. :param config: """ if not hasattr(config, "latex_elements"): # pragma: no cover config.latex_elements = {} # type: ignore[attr-defined] latex_elements = (config.latex_elements or {}) latex_preamble = latex_elements.get("preamble", '') # Backported from Sphinx 4 # See https://github.com/sphinx-doc/sphinx/pull/8997 config.latex_elements["preamble"] = '\n'.join([ latex_preamble, r"\makeatletter", '', r"\renewcommand{\py@sigparams}[2]{%", r" \parbox[t]{\py@argswidth}{\raggedright #1\sphinxcode{)}#2\strut}%", " % final strut is to help get correct vertical separation in case of multi-line", " % box with the item contents.", '}', r"\makeatother", ]) config.latex_elements["hyperref"] = '\n'.join([ r"% Include hyperref last.", r"\usepackage[pdfpagelabels,hyperindex,hyperfigures]{hyperref}", r"% Fix anchor placement for figures with captions.", r"\usepackage{hypcap}% it must be loaded after hyperref.", ]) config.latex_elements["maketitle"] = '\n'.join([ r"\begingroup", r"\let\oldthepage\thepage", r"\renewcommand{\thepage}{T\oldthepage}", config.latex_elements.get("maketitle", r"\sphinxmaketitle"), r"\endgroup" ]) needspace_amount = getattr(config, "needspace_amount") if needspace_amount: latex_extrapackages = StringList(latex_elements.get("extrapackages", '')) latex_extrapackages.append(r"\usepackage{needspace}") latex_elements["extrapackages"] = str(latex_extrapackages) def visit_paragraph(translator: LaTeXTranslator, node: nodes.paragraph) -> None: index = node.parent.index(node) if ( index > 0 and isinstance(node.parent, nodes.compound) and not isinstance(node.parent[index - 1], nodes.paragraph) and not isinstance(node.parent[index - 1], nodes.compound) ): # insert blank line, if the paragraph follows a non-paragraph node in a compound translator.body.append("\\noindent\n") elif index == 1 and isinstance(node.parent, (nodes.footnote, footnotetext)): # don't insert blank line, if the paragraph is second child of a footnote # (first one is label node) pass else: # Sphinx 3.5 adds \sphinxAtStartPar here, but I don't see what it gains. translator.body.append('\n') @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.latex.layout`. :param app: The Sphinx application. """ app.connect("config-inited", configure, priority=500) app.add_node(addnodes.desc, latex=(visit_desc, LaTeXTranslator.depart_desc), override=True) app.add_node(nodes.field_list, latex=(visit_field_list, depart_field_list), override=True) app.add_node(nodes.paragraph, latex=(visit_paragraph, LaTeXTranslator.depart_paragraph), override=True) app.add_config_value("needspace_amount", default=None, rebuild="latex", types=[str]) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/latex/succinct_seealso.py000066400000000000000000000054131475757360300246400ustar00rootroot00000000000000#!/usr/bin/env python3 # # succinct_seealso.py """ Sphinx extension which customises :rst:dir:`seealso` directives to be on one line with the LaTeX builder. .. extensions:: sphinx_toolbox.latex.succinct_seealso .. versionadded:: 3.0.0 """ # # 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. # # 3rd party from sphinx import addnodes from sphinx.application import Sphinx from sphinx.locale import admonitionlabels from sphinx.writers.latex import LaTeXTranslator __all__ = ("setup", ) # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version def visit_seealso(translator: LaTeXTranslator, node: addnodes.seealso) -> None: """ Visit an :class:`addnodes.seealso`` node. :param translator: :param node: """ # translator.body.append('\n\n\\begin{description}\\item[{%s:}] \\leavevmode' % admonitionlabels['seealso']) # translator.body.append('\n\n\\sphinxstrong{%s:} ' % admonitionlabels["seealso"]) if len(node) > 1: LaTeXTranslator.visit_seealso(translator, node) else: translator.body.append('\n\n\\sphinxstrong{%s:} ' % admonitionlabels["seealso"]) def depart_seealso(translator: LaTeXTranslator, node: addnodes.seealso) -> None: """ Depart an :class:`addnodes.seealso`` node. :param translator: :param node: """ if len(node) > 1: LaTeXTranslator.depart_seealso(translator, node) else: # translator.body.append("\\end{description}\n\n") translator.body.append("\n\n") @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.latex.succinct_seealso`. :param app: The Sphinx application. """ app.add_node(addnodes.seealso, latex=(visit_seealso, depart_seealso), override=True) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/latex/toc.py000066400000000000000000000077551475757360300221120ustar00rootroot00000000000000#!/usr/bin/env python3 # # toc.py """ Adjusts the default LaTeX output as follows: * The captions from ``toctree`` directives are converted into document parts. * The PDF outline has the correct hierarchy, including having the indices as top-level elements. .. versionadded:: 2.1.0 .. versionchanged:: 3.0.0 Moved from :mod:`sphinx_toolbox.tweaks.latex_toc`. .. latex:clearpage:: .. extensions:: sphinx_toolbox.latex.toc ----- """ # noqa: D400 # # 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 List # 3rd party import sphinx.directives.other import sphinx.writers.latex from docutils import nodes from sphinx.application import Sphinx # this package from sphinx_toolbox.latex import use_package from sphinx_toolbox.utils import Config, SphinxExtMetadata, metadata_add_version __all__ = ("setup", ) nest_bookmark_level_part = "\\bookmarksetupnext{{level=part}}\n" class latex_toc(nodes.raw): pass class LaTeXTranslator(sphinx.writers.latex.LaTeXTranslator): def generate_indices(self) -> str: super_output = super().generate_indices() if not super_output: return nest_bookmark_level_part return '\n'.join([ nest_bookmark_level_part, *super_output.splitlines(), '', nest_bookmark_level_part, ]) def visit_latex_toc(self, node: latex_toc) -> None: if not self.is_inline(node): self.body.append('\n') if "latex" in node.get("format", '').split(): self.body.append(f"\\{self.sectionnames[self.sectionlevel]}{{{node.astext()}}}") if not self.is_inline(node): self.body.append('\n') raise nodes.SkipNode def depart_latex_toc(self, node: latex_toc) -> None: # pragma: no cover pass class LatexTocTreeDirective(sphinx.directives.other.TocTree): def run(self) -> List[nodes.Node]: """ Process the content of the directive. """ assert self.env.app.builder is not None output: List[nodes.Node] = [] caption = self.options.get("caption") if ( caption is not None and "hidden" not in self.options and self.env.app.builder.format.lower() == "latex" and self.env.docname == self.env.config.master_doc ): output.append(latex_toc(text=caption, format="latex")) output.extend(super().run()) return output def configure(app: Sphinx, config: Config) -> None: """ Configure :mod:`sphinx_toolbox.latex.toc`. :param app: The Sphinx application. :param config: """ use_package("bookmark", config) def purge_outdated(app: Sphinx, env, added, changed, removed) -> List[str]: # noqa: MAN001 return [env.config.master_doc] @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.tweaks.latex_toc`. :param app: The Sphinx application. """ app.connect("env-get-outdated", purge_outdated) app.connect("config-inited", configure) app.add_directive("toctree", LatexTocTreeDirective, override=True) app.set_translator("latex", LaTeXTranslator, override=True) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/000077500000000000000000000000001475757360300223005ustar00rootroot00000000000000sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/__init__.py000066400000000000000000000131721475757360300244150ustar00rootroot00000000000000#!/usr/bin/env python3 # # __init__.py """ Extensions to :mod:`sphinx.ext.autodoc`. .. versionadded:: 0.6.0 """ # # Copyright Β© 2020-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. # # Parts based on https://github.com/sphinx-doc/sphinx # | Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). # | BSD Licensed # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib from typing import TYPE_CHECKING, Any, List, Optional, Tuple # 3rd party from docutils.statemachine import StringList from sphinx.application import Sphinx from sphinx.ext.autodoc import Documenter # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version if TYPE_CHECKING: # 3rd party from sphinx.ext.autodoc import ObjectMembers ObjectMembers = ObjectMembers else: ObjectMembers = List[Tuple[str, Any]] __all__ = ("setup", ) def _documenter_add_content( self: Documenter, more_content: Optional[StringList], no_docstring: bool = False, ) -> None: """ Add content from docstrings, attribute documentation and user. """ # set sourcename and add content from attribute documentation sourcename = self.get_sourcename() if self.analyzer: attr_docs = self.analyzer.find_attr_docs() if self.objpath: key = ('.'.join(self.objpath[:-1]), self.objpath[-1]) if key in attr_docs: no_docstring = True # make a copy of docstring for attributes to avoid cache # the change of autodoc-process-docstring event. docstrings = [list(attr_docs[key])] for i, line in enumerate(self.process_doc(docstrings)): self.add_line(line, sourcename, i) # add content from docstrings if not no_docstring: docstrings = self.get_doc() or [] if docstrings is None: # Do not call autodoc-process-docstring on get_doc() returns None. pass else: if not docstrings: # append at least a dummy docstring, so that the event # autodoc-process-docstring is fired and can add some # content if desired docstrings.append([]) for i, line in enumerate(self.process_doc(docstrings)): self.add_line(line, sourcename, i) # add additional content (e.g. from document), if present if more_content: for line, src in zip(more_content.data, more_content.items): self.add_line(line, src[0], src[1]) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc`. :param app: The Sphinx application. """ # Setup sub-extensions app.setup_extension("sphinx_toolbox.more_autodoc.augment_defaults") app.setup_extension("sphinx_toolbox.more_autodoc.autoprotocol") app.setup_extension("sphinx_toolbox.more_autodoc.autotypeddict") app.setup_extension("sphinx_toolbox.more_autodoc.autonamedtuple") app.setup_extension("sphinx_toolbox.more_autodoc.genericalias") app.setup_extension("sphinx_toolbox.more_autodoc.typehints") app.setup_extension("sphinx_toolbox.more_autodoc.variables") app.setup_extension("sphinx_toolbox.more_autodoc.sourcelink") app.setup_extension("sphinx_toolbox.more_autodoc.no_docstring") app.setup_extension("sphinx_toolbox.more_autodoc.regex") app.setup_extension("sphinx_toolbox.more_autodoc.typevars") app.setup_extension("sphinx_toolbox.more_autodoc.overloads") app.setup_extension("sphinx_toolbox.more_autodoc.generic_bases") return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/augment_defaults.py000066400000000000000000000125401475757360300262030ustar00rootroot00000000000000#!/usr/bin/env python3 # # augment_defaults.py """ Sphinx's autodoc module allows for default options to be set, and allows for those defaults to be disabled for an ``:auto*:`` directive and different values given instead. However, it does not appear to be possible to augment the defaults, such as to globally exclude certain members and then exclude additional members of a single class. This module monkeypatches in that behaviour. .. extensions:: sphinx_toolbox.more_autodoc.augment_defaults .. versionchanged:: 0.6.0 Moved from :mod:`sphinx_toolbox.autodoc_augment_defaults`. """ # noqa: D400 # # Copyright Β© 2020-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. # # Based on Sphinx # Copyright (c) 2007-2020 by the Sphinx team. # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib from typing import Dict, List, Type # 3rd party import autodocsumm # type: ignore[import-untyped] import sphinx.ext.autodoc.directive from docutils.utils import assemble_option_dict from sphinx.application import Sphinx from sphinx.errors import ExtensionError from sphinx.ext.autodoc import Documenter, Options # this package import sphinx_toolbox import sphinx_toolbox.more_autosummary from sphinx_toolbox.utils import Config, SphinxExtMetadata __all__ = ("process_documenter_options", "setup") def process_documenter_options( documenter: Type[Documenter], config: Config, options: Dict, ) -> Options: """ Recognize options of Documenter from user input. :param documenter: :param config: :param options: """ for name in sphinx.ext.autodoc.directive.AUTODOC_DEFAULT_OPTIONS: if name not in documenter.option_spec: # pragma: no cover continue else: negated = options.pop("no-" + name, True) is None if name in config.autodoc_default_options and not negated: # pylint: disable=loop-invariant-statement default_value = config.autodoc_default_options[name] existing_value = options.get(name, None) values: List[str] = [v for v in [default_value, existing_value] if v not in {None, True, False}] if values: options[name] = ','.join(values) else: options[name] = None # pragma: no cover # pylint: enable=loop-invariant-statement return Options(assemble_option_dict(options.items(), documenter.option_spec)) def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc.augment_defaults`. :param app: The Sphinx application. """ if "sphinx.ext.autodoc" in app.extensions: raise ExtensionError( "'sphinx_toolbox.more_autodoc.augment_defaults' " "must be loaded before 'sphinx.ext.autodoc'." ) sphinx.ext.autodoc.directive.process_documenter_options = process_documenter_options # type: ignore[assignment] autodocsumm.process_documenter_options = process_documenter_options sphinx_toolbox.more_autosummary.process_documenter_options = process_documenter_options # type: ignore[assignment] app.setup_extension("sphinx.ext.autodoc") return {"version": sphinx_toolbox.__version__, "parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/autonamedtuple.py000066400000000000000000000414441475757360300257100ustar00rootroot00000000000000#!/usr/bin/env python3 # # autonamedtuple.py r""" A Sphinx directive for documenting :class:`NamedTuples ` in Python. .. versionadded:: 0.8.0 .. extensions:: sphinx_toolbox.more_autodoc.autonamedtuple .. versionchanged:: 1.5.0 ``__new__`` methods are documented regardless of other exclusion settings if the annotations differ from the namedtuple itself. Usage --------- .. rst:directive:: autonamedtuple Directive to automatically document a :class:`typing.NamedTuple` or :func:`collections.namedtuple`. The output is based on the :rst:dir:`autoclass` directive. The list of parameters and the attributes are replaced by a list of Fields, combining the types and docstrings from the class docstring individual attributes. These will always be shown regardless of the state of the ``:members:`` option. Otherwise the directive behaves the same as :rst:dir:`autoclass`, and takes all of its arguments. See https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html for further information. .. versionadded:: 0.8.0 .. rst:role:: namedtuple Role which provides a cross-reference to the documentation generated by :rst:dir:`autonamedtuple`. .. rst:role:: namedtuple-field Role which provides a cross-reference to a field in the namedtuple documentation. .. versionadded:: 3.0.0 .. note:: Due to limitations in docutils cross-references for all fields in a namedtuple will point to the top of the fields list, rather than the individial fields. .. seealso:: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html :bold-title:`Examples` .. literalinclude:: ../../../autonamedtuple_demo.py :language: python :tab-width: 4 :linenos: :lines: 1-59 .. rest-example:: .. automodule:: autonamedtuple_demo :no-autosummary: :exclude-members: Movie .. autonamedtuple:: autonamedtuple_demo.Movie This function takes a single argument, the :namedtuple:`~.Movie` to watch. The name of the movie can be accessed with the :namedtuple-field:`~.Movie.name` attribute. API Reference --------------- """ # # Copyright Β© 2020-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. # # Parts based on https://github.com/sphinx-doc/sphinx # | Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). # | BSD Licensed # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib import inspect import re import sys import textwrap from textwrap import dedent from typing import Any, Dict, List, Optional, Tuple, Type, cast, get_type_hints # 3rd party from docutils import nodes from docutils.statemachine import StringList from sphinx import addnodes from sphinx.application import Sphinx from sphinx.domains import ObjType from sphinx.domains.python import PyAttribute, PyClasslike, PythonDomain, PyXRefRole from sphinx.ext.autodoc import ClassDocumenter, Documenter, Options from sphinx.ext.autodoc.directive import DocumenterBridge from sphinx.locale import _ from sphinx.pycode import ModuleAnalyzer from sphinx.util.nodes import make_id # this package from sphinx_toolbox.more_autodoc import ObjectMembers, _documenter_add_content from sphinx_toolbox.more_autodoc.typehints import format_annotation from sphinx_toolbox.utils import ( Param, SphinxExtMetadata, add_fallback_css_class, add_nbsp_substitution, allow_subclass_add, baseclass_is_private, is_namedtuple, metadata_add_version, parse_parameters ) __all__ = ("NamedTupleDocumenter", "setup") class NamedTupleDocumenter(ClassDocumenter): r""" Sphinx autodoc :class:`~sphinx.ext.autodoc.Documenter` for documenting :class:`typing.NamedTuple`\s. .. versionadded:: 0.8.0 .. versionchanged:: 0.1.0 Will no longer attempt to find attribute docstrings from other namedtuple classes. """ # noqa: D400 objtype = "namedtuple" directivetype = "namedtuple" priority = 20 object: Type # noqa: A003 # pylint: disable=redefined-builtin field_alias_re = re.compile("Alias for field number [0-9]+") def __init__(self, directive: DocumenterBridge, name: str, indent: str = '') -> None: super().__init__(directive, name, indent) self.options = Options(self.options.copy()) @classmethod def can_document_member( cls, member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: """ Called to see if a member can be documented by this documenter. :param member: The member being checked. :param membername: The name of the member. :param isattr: :param parent: The parent of the member. """ return is_namedtuple(member) def add_content(self, more_content: Optional[StringList], no_docstring: bool = True) -> None: r""" Add extra content (from docstrings, attribute docs etc.), but not the :class:`typing.NamedTuple`\'s docstring. :param more_content: :param no_docstring: """ # noqa: D400 _documenter_add_content(self, more_content, True) # set sourcename and add content from attribute documentation sourcename = self.get_sourcename() params, pre_output, post_output = self._get_docstring() for line in pre_output: self.add_line(line, sourcename) def _get_docstring(self) -> Tuple[Dict[str, Param], List[str], List[str]]: """ Returns params, pre_output, post_output. """ # Size varies depending on docutils config tab_size = self.env.app.config.docutils_tab_width if self.object.__doc__: docstring = dedent(self.object.__doc__).expandtabs(tab_size).split('\n') elif "show-inheritance" not in self.options: docstring = [":class:`typing.NamedTuple`."] else: docstring = [''] # pylint: disable=W8301 docstring = list(self.process_doc([docstring])) return parse_parameters(docstring, tab_size=tab_size) def add_directive_header(self, sig: str) -> None: """ Add the directive's header, and the inheritance information if the ``:show-inheritance:`` flag set. :param sig: The NamedTuple's signature. """ super().add_directive_header(sig) if "show-inheritance" not in self.options: return acceptable_bases = { " Bases: :class:`tuple`", " Bases: :py:class:`tuple`", " Bases: :class:`tuple`, :class:`typing.Generic`", " Bases: :py:class:`tuple`, :py:class:`typing.Generic`", " Bases: :class:`NamedTuple`", " Bases: :py:class:`NamedTuple`", } # TODO: support generic bases for multiple inheritance if self.directive.result[-1] in acceptable_bases or baseclass_is_private(self.object): # TODO: multiple inheritance if getattr(self.env.config, "generic_bases_fully_qualified", False): # Might not be present; extension might not be enabled prefix = '' else: prefix = '~' if hasattr(self.object, "__annotations__"): self.directive.result[-1] = f" Bases: :class:`{prefix}typing.NamedTuple`" else: self.directive.result[-1] = f" Bases: :func:`{prefix}collections.namedtuple`" def filter_members( self, members: ObjectMembers, want_all: bool, ) -> List[Tuple[str, Any, bool]]: """ Filter the list of members to always include ``__new__`` if it has a different signature to the tuple. :param members: :param want_all: """ all_hints = get_type_hints(self.object) class_hints = {k: all_hints[k] for k in self.object._fields if k in all_hints} # TODO: need a better way to partially resolve type hints, and warn about failures new_hints = get_type_hints( self.object.__new__, globalns=sys.modules[self.object.__module__].__dict__, localns=self.object.__dict__, # type: ignore[arg-type] ) # Stock NamedTuples don't have these, but customised collections.namedtuple or hand-rolled classes may if "cls" in new_hints: new_hints.pop("cls") if "return" in new_hints: new_hints.pop("return") if class_hints and new_hints and class_hints != new_hints: #: __new__ has a different signature or different annotations def unskip_new(app, what, name, obj, skip, options) -> Optional[bool]: # noqa: MAN001 if name == "__new__": return False return None listener_id = self.env.app.connect("autodoc-skip-member", unskip_new) members_ = super().filter_members(members, want_all) self.env.app.disconnect(listener_id) return members_ else: return super().filter_members(members, want_all) def sort_members( self, documenters: List[Tuple[Documenter, bool]], order: str, ) -> List[Tuple[Documenter, bool]]: r""" Sort the :class:`typing.NamedTuple`\'s members. :param documenters: :param order: """ # The documenters for the fields and methods, in the desired order # The fields will be in bysource order regardless of the order option documenters = super().sort_members(documenters, order) # Size varies depending on docutils config a_tab = ' ' * self.env.app.config.docutils_tab_width # Mapping of member names to docstrings (as list of strings) member_docstrings: Dict[str, List[str]] try: namedtuple_source = textwrap.dedent(inspect.getsource(self.object)) # Mapping of member names to docstrings (as list of strings) member_docstrings = { k[1]: v for k, v in ModuleAnalyzer.for_string(namedtuple_source, self.object.__module__ ).find_attr_docs().items() } except (TypeError, OSError): member_docstrings = {} # set sourcename and add content from attribute documentation sourcename = self.get_sourcename() params, pre_output, post_output = self._get_docstring() self.add_line('', sourcename) self.add_line(":Fields:", sourcename) # TODO: support for default_values self.add_line('', sourcename) fields = self.object._fields fields_body = [] for pos, field in enumerate(fields): doc: List[str] = [''] # pylint: disable=W8201 arg_type: str = '' # Prefer doc from class docstring if field in params: doc, arg_type = params.pop(field).values() # type: ignore[assignment] # Otherwise use attribute docstring if not ''.join(doc).strip() and field in member_docstrings: doc = member_docstrings[field] # Fallback to namedtuple's default docstring if not ''.join(doc).strip(): doc = [getattr(self.object, field).__doc__] # pylint: disable=W8301 # Prefer annotations over docstring types type_hints = get_type_hints(self.object) if type_hints: if field in type_hints: arg_type = format_annotation(type_hints[field]) self.add_line(f"{a_tab}.. namedtuple-field:: {field}", sourcename) self.add_line('', sourcename) # field_entry = [f"{a_tab}{pos})", "|nbsp|", f"**{field}**"] field_entry = [f"{a_tab}{pos}) \u00A0**{field}**"] if arg_type: field_entry.append(f"({arg_type}\\)") field_entry.append("--") field_entry.extend(doc) if self.field_alias_re.match(getattr(self.object, field).__doc__ or ''): getattr(self.object, field).__doc__ = ' '.join(doc) fields_body.append(' '.join(field_entry)) fields_body.append('') for line in fields_body: self.add_line(line, sourcename) self.add_line('', sourcename) for line in post_output: self.add_line(line, sourcename) self.add_line('', sourcename) # Stop a duplicate set of parameters being added with `autodoc_typehints = 'both'` self.env.temp_data["annotations"] = {} # Remove documenters corresponding to fields and return the rest return [d for d in documenters if d[0].name.split('.')[-1] not in fields] class _PyNamedTuplelike(PyClasslike): """ Description of a namedtuple-like object. """ def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str: if self.objtype == "namedtuple": return _("%s (namedtuple in %s)") % (name_cls[0], modname) else: return super().get_index_text(modname, name_cls) class _PyNamedTupleField(PyAttribute): """ Index entry and target for a namedtuple field. """ def run(self) -> List[nodes.Node]: if ':' in self.name: self.domain, self.objtype = self.name.split(':', 1) else: self.domain, self.objtype = '', self.name self.indexnode = addnodes.index(entries=[]) modname = self.options.get("module", self.env.ref_context.get("py:module")) classname = self.env.ref_context.get("py:class") name = self.arguments[0] full_name = f"{classname}.{name}" fullname = (modname + '.' if modname else '') + full_name node_id = make_id(self.env, self.state.document, '', fullname) node = nodes.target('', '', ids=[node_id]) node.document = self.state.document node.attributes["namedtuple_field"] = True if "noindex" not in self.options: # only add target and index entry if this is the first # description of the object with this name in this desc block domain = cast(PythonDomain, self.env.get_domain("py")) domain.note_object(fullname, self.objtype, node_id, location=node) if "noindexentry" not in self.options: key = name[0].upper() pair = [ # pylint: disable=W8301 _("%s (namedtuple in %s)") % (classname, modname), _("%s (namedtuple field)") % name, ] self.indexnode["entries"].append(("pair", "; ".join(pair), node_id, '', key)) return [self.indexnode, node] def _patch_reorder_transform(): # Patch ReorderConsecutiveTargetAndIndexNodes on Sphinx 7.2+ # 3rd party import sphinx.transforms if sphinx.version_info >= (7, 2): orig_reorder_func = sphinx.transforms._reorder_index_target_nodes # type: ignore[attr-defined] def _reorder_index_target_nodes(start_node: nodes.target) -> None: if start_node.attributes.get("namedtuple_field", False): return orig_reorder_func(start_node) sphinx.transforms._reorder_index_target_nodes = _reorder_index_target_nodes # type: ignore[attr-defined] @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc.autonamedtuple`. .. versionadded:: 0.8.0 :param app: The Sphinx application. """ # Hack to get the docutils tab size, as there doesn't appear to be any other way app.setup_extension("sphinx_toolbox.tweaks.tabsize") app.registry.domains["py"].object_types["namedtuple"] = ObjType(_("namedtuple"), "namedtuple", "class", "obj") app.add_directive_to_domain("py", "namedtuple", _PyNamedTuplelike) app.add_role_to_domain("py", "namedtuple", PyXRefRole()) app.add_directive_to_domain("py", "namedtuple-field", _PyNamedTupleField) app.add_role_to_domain("py", "namedtuple-field", PyXRefRole()) app.registry.domains["py"].object_types["namedtuple-field"] = ObjType( _("namedtuple-field"), "namedtuple-field", "attr", "obj", ) app.connect("object-description-transform", add_fallback_css_class({"namedtuple": "class"})) allow_subclass_add(app, NamedTupleDocumenter) app.connect("config-inited", lambda _, config: add_nbsp_substitution(config)) _patch_reorder_transform() return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/autoprotocol.py000066400000000000000000000273511475757360300254140ustar00rootroot00000000000000#!/usr/bin/env python3 # # autoprotocol.py r""" A Sphinx directive for documenting :class:`Protocols ` in Python. .. versionadded:: 0.2.0 .. extensions:: sphinx_toolbox.more_autodoc.autoprotocol .. versionchanged:: 0.6.0 Moved from :mod:`sphinx_toolbox.autoprotocol`. .. versionchanged:: 2.13.0 Added support for generic bases, such as ``class SupportsAbs(Protocol[T_co]): ...``. Usage ------- .. latex:vspace:: -20px .. rst:directive:: autoprotocol Directive to automatically document a :class:`typing.Protocol`. The output is based on the :rst:dir:`autoclass` directive, but with a few differences: * Private members are always excluded. * Special members (dunder methods) are always included. * Undocumented members are always included. The following options from :rst:dir:`autoclass` are available: .. rst:directive:option:: noindex :type: flag Do not generate index entries for the documented object (and all autodocumented members). .. rst:directive:option:: member-order :type: string Override the global value of :any:`sphinx:autodoc_member_order` for one directive. .. rst:directive:option:: show-inheritance :type: flag Inserts a list of base classes just below the protocol's signature. .. rst:role:: protocol Role which provides a cross-reference to the documentation generated by :rst:dir:`autoprotocol`. .. latex:vspace:: 5px .. seealso:: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html .. latex:clearpage:: :bold-title:`Examples:` .. literalinclude:: ../../../autoprotocol_demo.py :language: python :tab-width: 4 :lines: 1-31 :linenos: .. rest-example:: .. automodule:: autoprotocol_demo :members: :no-autosummary: :exclude-members: HasGreaterThan .. autoprotocol:: autoprotocol_demo.HasGreaterThan The objects being sorted must implement the :protocol:`~.HasGreaterThan` protocol. .. latex:vspace:: 30px API Reference -------------- """ # # Copyright Β© 2020-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. # # Parts based on https://github.com/sphinx-doc/sphinx # | Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). # | BSD Licensed # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib import sys from typing import Any, Callable, Dict, List, Optional, Tuple # 3rd party import sphinx from docutils.statemachine import StringList from sphinx.application import Sphinx from sphinx.domains import ObjType from sphinx.domains.python import PyClasslike, PyXRefRole from sphinx.ext.autodoc import ( INSTANCEATTR, ClassDocumenter, Documenter, Options, exclude_members_option, member_order_option ) from sphinx.ext.autodoc.directive import DocumenterBridge from sphinx.locale import _ from sphinx.util.inspect import getdoc, safe_getattr # this package from sphinx_toolbox.more_autodoc import ObjectMembers, _documenter_add_content from sphinx_toolbox.more_autodoc.generic_bases import _add_generic_bases from sphinx_toolbox.utils import ( SphinxExtMetadata, add_fallback_css_class, allow_subclass_add, filter_members_warning, flag, metadata_add_version ) if sys.version_info < (3, 8): # pragma: no cover (>=py38) # 3rd party from typing_extensions import Protocol, _ProtocolMeta else: # pragma: no cover (`_." ) class ProtocolDocumenter(ClassDocumenter): r""" Sphinx autodoc :class:`~sphinx.ext.autodoc.Documenter` for documenting :class:`typing.Protocol`\s. """ # noqa: D400 objtype = "protocol" directivetype = "protocol" priority = 20 option_spec: Dict[str, Callable] = { "noindex": flag, "member-order": member_order_option, "show-inheritance": flag, "exclude-protocol-members": exclude_members_option, } globally_excluded_methods = { "__module__", "__new__", "__init__", "__subclasshook__", "__doc__", "__tree_hash__", "__extra__", "__orig_bases__", "__origin__", "__parameters__", "__next_in_mro__", "__slots__", "__args__", "__dict__", "__weakref__", "__annotations__", "__abstractmethods__", "__class_getitem__", "__init_subclass__", "__protocol_attrs__", "__non_callable_proto_members__", # Python 3.13 and above "__firstlineno__", # Python 3.13 and above "__replace__", # Python 3.13 and above "__static_attributes__", # Python 3.13 and above } def __init__(self, directive: DocumenterBridge, name: str, indent: str = '') -> None: super().__init__(directive, name, indent) self.options = Options(self.options.copy()) @classmethod def can_document_member( cls, member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: """ Called to see if a member can be documented by this documenter. :param member: The member being checked. :param membername: The name of the member. :param isattr: :param parent: The parent of the member. """ if isinstance(member, _ProtocolMeta): return Protocol in member.__bases__ return False def add_directive_header(self, sig: str) -> None: """ Add the directive header. :param sig: """ sourcename = self.get_sourcename() if self.doc_as_attr: self.directivetype = "attribute" Documenter.add_directive_header(self, sig) if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals: self.add_line(" :final:", sourcename) # add inheritance info, if wanted if not self.doc_as_attr and self.options.show_inheritance: _add_generic_bases(self) def format_signature(self, **kwargs: Any) -> str: """ Protocols do not have a signature. """ return '' # pragma: no cover def add_content(self, more_content: Optional[StringList], no_docstring: bool = False) -> None: """ Add the autodocumenter content. :param more_content: :param no_docstring: """ _documenter_add_content(self, more_content, no_docstring) sourcename = self.get_sourcename() if not getdoc(self.object) and "show-inheritance" not in self.options: self.add_line(":class:`typing.Protocol`.", sourcename) self.add_line('', sourcename) if hasattr(self.object, "_is_runtime_protocol") and self.object._is_runtime_protocol: self.add_line(runtime_message, sourcename) self.add_line('', sourcename) self.add_line( "Classes that implement this protocol must have the following methods / attributes:", sourcename ) self.add_line('', sourcename) def document_members(self, all_members: bool = False) -> None: """ Generate reST for member documentation. All members are always documented. """ super().document_members(True) def filter_members( self, members: ObjectMembers, want_all: bool, ) -> List[Tuple[str, Any, bool]]: """ Filter the given member list. :param members: :param want_all: """ ret = [] # process members and determine which to skip for m in members: if sphinx.version_info >= (7, 0): membername, member = m.__name__, m.object # type: ignore[union-attr] else: membername, member = m # if isattr is True, the member is documented as an attribute if safe_getattr(member, "__sphinx_mock__", False): # mocked module or object keep = False # pragma: no cover elif ( self.options.get("exclude-protocol-members", []) and membername in self.options["exclude-protocol-members"] ): # remove members given by exclude-protocol-members keep = False # pragma: no cover elif membername.startswith('_') and not (membername.startswith("__") and membername.endswith("__")): keep = False elif membername not in self.globally_excluded_methods: # Magic method you wouldn't overload, or private method. if membername in dir(self.object.__base__): keep = member is not getattr(self.object.__base__, membername) else: keep = True else: keep = False # give the user a chance to decide whether this member # should be skipped if self.env.app: # let extensions preprocess docstrings try: # pylint: disable=R8203 skip_user = self.env.app.emit_firstresult( "autodoc-skip-member", self.objtype, membername, member, not keep, self.options, ) if skip_user is not None: keep = not skip_user except Exception as exc: filter_members_warning(member, exc) keep = False if keep: ret.append((membername, member, member is INSTANCEATTR)) return ret class _PyProtocollike(PyClasslike): """ Description of a Protocol-like object. """ def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str: if self.objtype == "protocol": return _("%s (protocol in %s)") % (name_cls[0], modname) else: return super().get_index_text(modname, name_cls) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc.autoprotocol`. :param app: The Sphinx application. """ app.registry.domains["py"].object_types["protocol"] = ObjType(_("protocol"), "protocol", "class", "obj") app.add_directive_to_domain("py", "protocol", _PyProtocollike) app.add_role_to_domain("py", "protocol", PyXRefRole()) app.connect("object-description-transform", add_fallback_css_class({"protocol": "class"})) allow_subclass_add(app, ProtocolDocumenter) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/autotypeddict.py000066400000000000000000000374451475757360300255510ustar00rootroot00000000000000#!/usr/bin/env python3 # # autotypeddict.py r""" A Sphinx directive for documenting :class:`TypedDicts ` in Python. Only supports :mod:`typing_extensions`\'s TypedDict until :pull:`700 ` is implemented in CPython. .. versionadded:: 0.5.0 .. extensions:: sphinx_toolbox.more_autodoc.autotypeddict .. versionchanged:: 0.6.0 Moved from :mod:`sphinx_toolbox.autotypeddict`. Usage --------- .. latex:vspace:: -20px .. rst:directive:: autotypeddict Directive to automatically document a :class:`typing.TypedDict`. The output is based on the :rst:dir:`autoclass` directive, but with a few differences: * Private and Special members are always excluded. * Undocumented members are always included. * The default sort order is ``bysource``. The following options are available: .. rst:directive:option:: noindex :type: flag Do not generate index entries for the documented object (and all autodocumented members). .. rst:directive:option:: alphabetical :type: flag Sort the keys alphabetically. By default the keys are listed in the order they were defined. .. rst:directive:option:: show-inheritance :type: flag Inserts a list of base classes just below the TypedDict's signature. .. rst:role:: typeddict Role which provides a cross-reference to the documentation generated by :rst:dir:`autotypeddict`. .. latex:vspace:: 5px .. seealso:: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html .. latex:clearpage:: :bold-title:`Examples:` .. literalinclude:: ../../../autotypeddict_demo.py :language: python :tab-width: 4 :linenos: .. rest-example:: .. automodule:: autotypeddict_demo :no-autosummary: :exclude-members: Movie,AquaticBird,OldStyleAnimal .. autotypeddict:: autotypeddict_demo.Movie This function takes a single argument, the :typeddict:`~.Movie` to watch. .. latex:vspace:: -5px API Reference --------------- .. latex:vspace:: -20px """ # # Copyright Β© 2020-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. # # Parts based on https://github.com/sphinx-doc/sphinx # | Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). # | BSD Licensed # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib from typing import Any, Callable, Dict, List, Tuple, Type, get_type_hints # 3rd party import docutils.statemachine import sphinx from domdf_python_tools.stringlist import StringList from sphinx.application import Sphinx from sphinx.domains import ObjType from sphinx.domains.python import PyClasslike, PyXRefRole from sphinx.ext.autodoc import INSTANCEATTR, ClassDocumenter, Documenter, Options from sphinx.locale import _ from sphinx.pycode import ModuleAnalyzer from sphinx.util.inspect import safe_getattr # this package from sphinx_toolbox.more_autodoc import ObjectMembers from sphinx_toolbox.more_autodoc.typehints import format_annotation from sphinx_toolbox.utils import ( SphinxExtMetadata, add_fallback_css_class, allow_subclass_add, filter_members_warning, flag, metadata_add_version ) __all__ = ("TypedDictDocumenter", "setup") class TypedDictDocumenter(ClassDocumenter): r""" Sphinx autodoc :class:`~sphinx.ext.autodoc.Documenter` for documenting :class:`typing.TypedDict`\s. """ # noqa: D400 objtype = "typeddict" directivetype = "typeddict" priority = 20 option_spec: Dict[str, Callable] = { "noindex": flag, "alphabetical": flag, "show-inheritance": flag, } def __init__(self, *args: Any) -> None: super().__init__(*args) self.options = Options(self.options.copy()) alphabetical = self.options.get("alphabetical", False) if alphabetical: self.options["member-order"] = "alphabetical" else: self.options["member-order"] = "bysource" for key in {"inherited-members", "special-members"}: # pragma: no cover if key in self.options: del self.options[key] @classmethod def can_document_member( cls, member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: """ Called to see if a member can be documented by this documenter. :param member: The member being checked. :param membername: The name of the member. :param isattr: :param parent: The parent of the member. """ for attr in {"__optional_keys__", "__required_keys__", "__total__"}: if not hasattr(member, attr): return False return True def format_signature(self, **kwargs: Any) -> str: """ Typed Dicts do not have a signature. :rtype: .. latex:clearpage:: """ return '' def add_directive_header(self, sig: str) -> None: """ Add the directive header. :param sig: """ sourcename = self.get_sourcename() if self.doc_as_attr: self.directivetype = "attribute" Documenter.add_directive_header(self, sig) if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals: self.add_line(" :final:", sourcename) # add inheritance info, if wanted if not self.doc_as_attr and self.options.show_inheritance: self.add_line('', sourcename) def get_bases(obj: Type) -> List[Type]: bases: Tuple[Type, ...] = () if hasattr(obj, "__orig_bases__") and len(obj.__orig_bases__): # A subclass of generic types # refs: PEP-560 bases = obj.__orig_bases__ elif hasattr(obj, "__bases__") and len(obj.__bases__): # A normal class bases = obj.__bases__ output = [] for base in bases: if base.__name__.startswith('_'): output.extend(get_bases(base)) else: output.append(base) return output bases = [format_annotation(cls) for cls in get_bases(self.object)] self.add_line(" " + _("Bases: %s") % ", ".join(bases), sourcename) self.add_line('', sourcename) def add_content(self, more_content: Any, no_docstring: bool = False) -> None: """ Add the autodocumenter content. :param more_content: :param no_docstring: """ if self.doc_as_attr: # pragma: no cover (verbatim from Sphinx) classname = safe_getattr(self.object, "__qualname__", None) if not classname: classname = safe_getattr(self.object, "__name__", None) if classname: module = safe_getattr(self.object, "__module__", None) parentmodule = safe_getattr(self.parent, "__module__", None) if module and module != parentmodule: classname = str(module) + '.' + str(classname) more_content = docutils.statemachine.StringList([_("alias of :class:`%s`") % classname], source='') no_docstring = True # set sourcename and add content from attribute documentation sourcename = self.get_sourcename() if self.analyzer: attr_docs = self.analyzer.find_attr_docs() if self.objpath: key = ('.'.join(self.objpath[:-1]), self.objpath[-1]) if key in attr_docs: no_docstring = True # make a copy of docstring for attributes to avoid cache # the change of autodoc-process-docstring event. docstrings = [list(attr_docs[key])] for i, line in enumerate(self.process_doc(docstrings)): self.add_line(line, sourcename, i) # add content from docstrings if not no_docstring: docstrings = self.get_doc() or [] if not docstrings: # append at least a dummy docstring, so that the event # autodoc-process-docstring is fired and can add some # content if desired docstrings.append([":class:`typing.TypedDict`.", '']) for i, line in enumerate(self.process_doc(docstrings)): self.add_line(line, sourcename, i) # add additional content (e.g. from document), if present if more_content: # pragma: no cover (verbatim from Sphinx) for line, src in zip(more_content.data, more_content.items): self.add_line(line, src[0], src[1]) def document_members(self, all_members: bool = False) -> None: """ Generate reST for member documentation. All members are always documented. """ if sphinx.version_info >= (8, 2): # set current namespace for finding members self._current_document.autodoc_module = self.modname # type: ignore[attr-defined] if self.objpath: self._current_document.autodoc_class = self.objpath[0] # type: ignore[attr-defined] # find out which members are documentable members_check_module, members = self.get_object_members(True) # document non-skipped members member_documenters: List[Tuple[Documenter, bool]] = [] for mname, member, isattr in self.filter_members(members, True): classes = [ cls for cls in self.documenters.values() if cls.can_document_member(member, mname, isattr, self) ] if not classes: # don't know how to document this member continue # prefer the documenter with the highest priority classes.sort(key=lambda cls: cls.priority) # give explicitly separated module name, so that members # of inner classes can be documented full_mname = f'{self.modname}::' + '.'.join((*self.objpath, mname)) documenter = classes[-1](self.directive, full_mname, self.indent) member_documenters.append((documenter, isattr)) member_order = self.options.member_order or self.config.autodoc_member_order member_documenters = self.sort_members(member_documenters, member_order) for documenter, isattr in member_documenters: documenter.generate( all_members=True, real_modname=self.real_modname, check_module=members_check_module and not isattr, ) if sphinx.version_info >= (8, 2): # reset current objects self._current_document.autodoc_module = '' # type: ignore[attr-defined] self._current_document.autodoc_class = '' # type: ignore[attr-defined] def sort_members( self, documenters: List[Tuple[Documenter, bool]], order: str, ) -> List[Tuple[Documenter, bool]]: """ Sort the TypedDict's members. :param documenters: :param order: """ # The documenters for the keys, in the desired order documenters = super().sort_members(documenters, order) # Mapping of key names to docstrings (as list of strings) docstrings = { k[1]: v for k, v in ModuleAnalyzer.for_module(self.object.__module__).find_attr_docs().items() } required_keys = [] optional_keys = [] types = get_type_hints(self.object) for d in documenters: name = d[0].name.split('.')[-1] if name in self.object.__required_keys__: required_keys.append(name) elif name in self.object.__optional_keys__: optional_keys.append(name) # else: warn user. This shouldn't ever happen, though. sourcename = self.get_sourcename() if required_keys: self.add_line('', sourcename) self.add_line(":Required Keys:", sourcename) self.document_keys(required_keys, types, docstrings) self.add_line('', sourcename) if optional_keys: self.add_line('', sourcename) self.add_line(":Optional Keys:", sourcename) self.document_keys(optional_keys, types, docstrings) self.add_line('', sourcename) return [] def document_keys( self, keys: List[str], types: Dict[str, Type], docstrings: Dict[str, List[str]], ) -> None: """ Document keys in a :class:`typing.TypedDict`. :param keys: List of key names to document. :param types: Mapping of key names to types. :param docstrings: Mapping of key names to docstrings. """ content = StringList() for key in keys: if key in types: key_type = f"({format_annotation(types[key])}) " else: key_type = '' if key in docstrings: content.append(f" * **{key}** {key_type}-- {' '.join(docstrings.get(key, ''))}") else: content.append(f" * **{key}** {key_type}") for line in content: self.add_line(line, self.get_sourcename()) def filter_members( self, members: ObjectMembers, want_all: bool, ) -> List[Tuple[str, Any, bool]]: """ Filter the given member list. :param members: :param want_all: """ ret = [] # process members and determine which to skip for m in members: if sphinx.version_info >= (7, 0): membername, member = m.__name__, m.object # type: ignore[union-attr] else: membername, member = m # if isattr is True, the member is documented as an attribute if safe_getattr(member, "__sphinx_mock__", False): # mocked module or object keep = False elif membername.startswith('_'): keep = False else: keep = True # give the user a chance to decide whether this member # should be skipped if self.env.app: # let extensions preprocess docstrings try: # pylint: disable=R8203 skip_user = self.env.app.emit_firstresult( "autodoc-skip-member", self.objtype, membername, member, not keep, self.options, ) if skip_user is not None: keep = not skip_user except Exception as exc: filter_members_warning(member, exc) keep = False if keep: ret.append((membername, member, member is INSTANCEATTR)) return ret class _PyTypedDictlike(PyClasslike): """ Description of a typeddict-like object. """ def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str: if self.objtype == "typeddict": return _("%s (typeddict in %s)") % (name_cls[0], modname) else: return super().get_index_text(modname, name_cls) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc.autotypeddict`. :param app: The Sphinx application. """ app.registry.domains["py"].object_types["typeddict"] = ObjType("typeddict", "typeddict", "class", "obj") app.add_directive_to_domain("py", "typeddict", _PyTypedDictlike) app.add_role_to_domain("py", "typeddict", PyXRefRole()) app.connect("object-description-transform", add_fallback_css_class({"typeddict": "class"})) allow_subclass_add(app, TypedDictDocumenter) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/generic_bases.py000066400000000000000000000131271475757360300254470ustar00rootroot00000000000000#!/usr/bin/env python3 # # generic_bases.py r""" Modifies :class:`sphinx.ext.autodoc.ClassDocumenter`\'s ``:show-inheritence:`` option to show generic base classes. This requires a relatively new version of the :mod:`typing` module that implements ``__orig_bases__``. .. versionadded:: 1.5.0 .. extensions:: sphinx_toolbox.more_autodoc.generic_bases Configuration ---------------- .. confval:: generic_bases_fully_qualified :type: :class:`bool` :required: False :default: :py:obj:`False` Determines whether the fully qualified name should be shown for bases. If :py:obj:`False` (the default): .. class:: Foo :noindex: **Bases**: :class:`~typing.List`\[:class:`str`\] If :py:obj:`True`: .. class:: Foo :noindex: **Bases**: :class:`typing.List`\[:class:`str`\] Corresponds to the ``fully_qualified`` argument to :func:`sphinx_toolbox.more_autodoc.typehints.format_annotation`. .. versionadded:: 2.13.0 Example -------- .. autoclass:: sphinx_toolbox.more_autodoc.generic_bases.Example API Reference ----------------- """ # noqa: D400 # # Copyright Β© 2020-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 sys from typing import List, Tuple # 3rd party from sphinx.application import Sphinx from sphinx.ext.autodoc import Documenter from sphinx.locale import _ from typing_extensions import final # this package from sphinx_toolbox.more_autodoc.typehints import format_annotation from sphinx_toolbox.more_autosummary import PatchedAutoSummClassDocumenter from sphinx_toolbox.utils import SphinxExtMetadata, allow_subclass_add, metadata_add_version if sys.version_info >= (3, 8): # pragma: no cover ( None: """ Add the directive header. :param sig: """ sourcename = self.get_sourcename() if self.doc_as_attr: self.directivetype = "attribute" Documenter.add_directive_header(self, sig) if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals: self.add_line(" :final:", sourcename) # add inheritance info, if wanted if not self.doc_as_attr and self.options.show_inheritance: _add_generic_bases(self) def _add_generic_bases(documenter: Documenter) -> None: """ Add the generic bases to the output of the given Documenter. .. versionadded:: 2.13.0 (undocumented) :param documenter: """ sourcename = documenter.get_sourcename() # add inheritance info, if wanted fully_qualified = getattr(documenter.env.config, "generic_bases_fully_qualified", False) documenter.add_line('', sourcename) bases = [] # pylint: disable=W8301 if ( hasattr(documenter.object, "__orig_bases__") and len(documenter.object.__orig_bases__) and get_origin(documenter.object.__orig_bases__[0]) is documenter.object.__bases__[0] ): # Last condition guards against classes that don't directly subclass a Generic. bases = [format_annotation(b, fully_qualified) for b in documenter.object.__orig_bases__] elif hasattr(documenter.object, "__bases__") and len(documenter.object.__bases__): bases = [format_annotation(b, fully_qualified) for b in documenter.object.__bases__] if bases: bases_string = ", ".join(bases).replace("typing_extensions.", "typing.") documenter.add_line(" " + _("Bases: %s") % bases_string, sourcename) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc.generic_bases`. .. versionadded:: 1.5.0 :param app: The Sphinx application. """ allow_subclass_add(app, GenericBasesClassDocumenter) app.add_config_value( "generic_bases_fully_qualified", default=False, rebuild="env", types=[bool], ) return {"parallel_read_safe": True} class Example(List[Tuple[str, float, List[str]]]): """ An example of :mod:`sphinx_toolbox.more_autodoc.generic_bases`. """ def __init__(self, iterable=()): # pragma: no cover # noqa: MAN001 pass class Example2(Example): """ An example of :mod:`sphinx_toolbox.more_autodoc.generic_bases`. This one does not directly subclass a Generic. """ @final class FinalExample(List[Tuple[str, float, List[str]]]): """ An example of :mod:`sphinx_toolbox.more_autodoc.generic_bases` decorated with ``@final``. """ def __init__(self, iterable=()): # pragma: no cover # noqa: MAN001 pass sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/genericalias.py000066400000000000000000000116331475757360300253040ustar00rootroot00000000000000#!/usr/bin/env python3 # # genericalias.py """ Documenter for alias, which usually manifest as `type aliases`_. .. _type aliases: https://docs.python.org/3/library/typing.html#type-aliases .. versionadded:: 0.6.0 .. extensions:: sphinx_toolbox.more_autodoc.genericalias .. note:: :mod:`sphinx_toolbox.more_autodoc.genericalias` is only supported on Python 3.7 and above. """ # # Copyright Β© 2020-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. # # Parts based on https://github.com/sphinx-doc/sphinx # | Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). # | BSD Licensed # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib from typing import Any # 3rd party from docutils.statemachine import StringList from sphinx.application import Sphinx from sphinx.ext.autodoc import SUPPRESS, Options from sphinx.ext.autodoc.directive import DocumenterBridge # noqa: F401 from sphinx.locale import _ from sphinx.util import inspect # this package from sphinx_toolbox._data_documenter import DataDocumenter from sphinx_toolbox.more_autodoc.typehints import format_annotation from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ("PrettyGenericAliasDocumenter", "setup") class PrettyGenericAliasDocumenter(DataDocumenter): """ Specialized Documenter subclass for GenericAliases, with prettier output than Sphinx's one. """ objtype = "genericalias" directivetype = "data" priority = DataDocumenter.priority + 2 @classmethod def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any) -> bool: """ Called to see if a member can be documented by this documenter. """ return inspect.isgenericalias(member) def add_directive_header(self, sig: str) -> None: """ Add the directive header and options to the generated content. """ self.options = Options(self.options) self.options["annotation"] = SUPPRESS super().add_directive_header(sig) def add_content(self, more_content: Any, no_docstring: bool = False) -> None: """ Add the autodocumenter content. :param more_content: :param no_docstring: """ name = format_annotation(self.object) content = StringList([_("Alias of %s") % name], source='') DataDocumenter.add_content(self, content) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc.genericalias`. :param app: The Sphinx application. """ app.setup_extension("sphinx.ext.autodoc") app.add_autodocumenter(PrettyGenericAliasDocumenter, override=True) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/no_docstring.py000066400000000000000000000064421475757360300253500ustar00rootroot00000000000000#!/usr/bin/env python3 # # no_docstring.py """ Adds the ``:no-docstring:`` option to automodule directives to exclude the docstring from the output. .. versionadded:: 1.0.0 Functions in this module moved from ``sphinx_toolbox.more_autodoc.__init__.py`` .. extensions:: sphinx_toolbox.more_autodoc.no_docstring """ # # Copyright Β© 2020-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 types import ModuleType from typing import List # 3rd party import autodocsumm # type: ignore[import-untyped] import sphinx.ext.autodoc from sphinx.application import Sphinx # this package from sphinx_toolbox.more_autosummary import PatchedAutoSummModuleDocumenter from sphinx_toolbox.utils import SphinxExtMetadata, flag, metadata_add_version __all__ = ("automodule_add_nodocstring", "no_docstring_process_docstring", "setup") def automodule_add_nodocstring(app: Sphinx) -> None: """ Add the ``:no-docstring:`` option to automodule directives. The option is used to exclude the docstring from the output :param app: The Sphinx application. """ sphinx.ext.autodoc.ModuleDocumenter.option_spec["no-docstring"] = flag autodocsumm.AutoSummModuleDocumenter.option_spec["no-docstring"] = flag PatchedAutoSummModuleDocumenter.option_spec["no-docstring"] = flag app.setup_extension("sphinx.ext.autodoc") app.connect("autodoc-process-docstring", no_docstring_process_docstring, priority=1000) def no_docstring_process_docstring( # noqa: MAN001 app: Sphinx, what, name: str, obj, options, lines: List[str], ) -> None: """ Remove module docstrings if the ``:no-docstring:`` flag was set. :param app: The Sphinx application. :param what: :param name: The name of the object being documented. :param obj: The object being documented. :param options: Mapping of autodoc options to values. :param lines: List of strings representing the current contents of the docstring. """ if isinstance(obj, ModuleType): no_docstring = options.get("no-docstring", False) if no_docstring: lines.clear() @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc.no_docstring`. :param app: The Sphinx application. """ automodule_add_nodocstring(app) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/overloads.py000066400000000000000000000345601475757360300246600ustar00rootroot00000000000000#!/usr/bin/env python3 # # overloads.py r""" Documenters for functions and methods which display overloads differently. .. versionadded:: 1.4.0 .. extensions:: sphinx_toolbox.more_autodoc.overloads Configuration ---------------- .. latex:vspace:: -20px .. confval:: overloads_location :type: :class:`str` :default: ``'signature'`` The location to display overloads at: * ``'signature'`` -- Display overloads above the function signature. * ``'top'`` -- Display overloads at the top of the docstring, immediately below the signature. * ``'bottom'`` -- Display overloads at the bottom of the docstring, or immediately below the return type. API Reference ---------------- .. latex:vspace:: -20px .. automodulesumm:: sphinx_toolbox.more_autodoc.overloads :autosummary-sections: Classes .. latex:vspace:: -15px .. automodulesumm:: sphinx_toolbox.more_autodoc.overloads :autosummary-sections: Functions """ # # Copyright Β© 2020-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. # # Parts based on https://github.com/sphinx-doc/sphinx # | Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). # | BSD Licensed # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib import re from contextlib import suppress from inspect import Parameter, Signature from types import ModuleType from typing import TYPE_CHECKING, Any, Dict, ForwardRef, List # 3rd party from domdf_python_tools.stringlist import StringList from sphinx.application import Sphinx from sphinx.config import ENUM from sphinx.ext import autodoc from sphinx.ext.autodoc.directive import DocumenterBridge from sphinx.util import inspect from sphinx.util.inspect import evaluate_signature, safe_getattr, stringify_signature # this package from sphinx_toolbox.more_autodoc import _documenter_add_content from sphinx_toolbox.more_autodoc.typehints import _resolve_forwardref, default_preprocessors, format_annotation from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version if TYPE_CHECKING: _OverloadMixinBase = autodoc.ModuleLevelDocumenter else: _OverloadMixinBase = object _ = DocumenterBridge __all__ = ( "OverloadMixin", "FunctionDocumenter", "MethodDocumenter", "setup", ) _return_type_re = re.compile("^:(rtype|return(s)?):") class OverloadMixin(_OverloadMixinBase): """ Mixin class for function and class documenters that changes the appearance of overloaded functions. """ def create_body_overloads(self) -> StringList: """ Create the overloaded implementations for insertion into to the body of the documenter's output. """ output = StringList() formatted_overloads = [] output.blankline() # output.append(":Overloaded Implementations:") output.append(":Overloads:") output.blankline() # Size varies depending on docutils config output.indent_type = ' ' output.indent_size = self.env.app.config.docutils_tab_width if self.analyzer and '.'.join(self.objpath) in self.analyzer.overloads: the_overloads = self.analyzer.overloads.get('.'.join(self.objpath)) assert the_overloads is not None for overload in the_overloads: overload = self.process_overload_signature(overload) buf = [format_annotation(self.object), r"\("] for name, param in overload.parameters.items(): buf.append(f"**{name}**") if param.annotation is not Parameter.empty: buf.append(r"\: ") buf.append(format_annotation(param.annotation)) if param.default is not Parameter.empty: default = param.default # pylint: disable=loop-global-usage if hasattr(inspect, "DefaultValue") and isinstance(default, inspect.DefaultValue): default = default.value # pylint: enable=loop-global-usage buf.append(" = ") buf.append(default) buf.append(r"\, ") if buf[-2][-1] != '`': buf[-1] = r" )" else: buf[-1] = r")" if overload.return_annotation is not Parameter.empty: return_annotation = overload.return_annotation if isinstance(return_annotation, ForwardRef): with suppress(Exception): if isinstance(self.parent, ModuleType): module_name = self.parent.__name__ else: module_name = self.parent.__module__ return_annotation = _resolve_forwardref(return_annotation, module_name) buf.append(" -> ") buf.append(format_annotation(return_annotation)) formatted_overloads.append(''.join(buf)) if len(formatted_overloads) == 1: output.append(formatted_overloads[0]) else: for line in formatted_overloads: output.append(f"* {line}") output.blankline(ensure_single=True) return output return StringList() def process_overload_signature(self, overload: Signature) -> Signature: """ Processes the signature of the given overloaded implementation. :param overload: """ parameters = [] non_overload_sig = inspect.signature(self.object) for param, non_overload_param in zip(overload.parameters.values(), non_overload_sig.parameters.values()): default = param.default if default is not Parameter.empty: for check, preprocessor in default_preprocessors: if check(default): default = preprocessor(default) break if param.annotation is non_overload_param.annotation: annotation = Parameter.empty else: annotation = param.annotation parameters.append(param.replace(default=default, annotation=annotation)) if non_overload_sig.return_annotation is overload.return_annotation: overload = overload.replace(parameters=parameters, return_annotation=Parameter.empty) else: overload = overload.replace(parameters=parameters) return overload def add_content(self, more_content: Any, no_docstring: bool = False) -> None: """ Add content from docstrings, attribute documentation and the user. :param more_content: :param no_docstring: """ if self.env.config.overloads_location == "bottom": def process_docstring( app: Sphinx, what: str, name: str, obj: Any, options: Dict[str, Any], lines: List[str], ) -> None: if callable(obj): seen_return = False n_lines = len(lines) for i, line in enumerate(lines): if not line: continue if line[0].isspace(): continue if _return_type_re.match(line): # pylint: disable=loop-global-usage seen_return = True continue if seen_return and i != n_lines: lines.insert(i, '') for inner_line in reversed(self.create_body_overloads()): # pylint: disable=W8402 lines.insert(i, inner_line) lines.insert(i, '') break else: lines.append('') lines.extend(self.create_body_overloads()) lines.append('') listener_id = self.env.app.connect("autodoc-process-docstring", process_docstring, priority=600) try: _documenter_add_content(self, more_content, no_docstring) finally: self.env.app.disconnect(listener_id) else: _documenter_add_content(self, more_content, no_docstring) class FunctionDocumenter(OverloadMixin, autodoc.FunctionDocumenter): # type: ignore[misc] """ Custom :class:`autodoc.FunctionDocumenter ` which renders overloads differently. """ # noqa: D400 def format_signature(self, **kwargs: Any) -> str: """ Format the function's signature, including those for any overloaded implementations. :return: The signature(s), as a multi-line string. """ sigs = [] if self.analyzer and '.'.join(self.objpath) in self.analyzer.overloads: overloaded = True else: overloaded = False sig = super(autodoc.FunctionDocumenter, self).format_signature(**kwargs) sigs.append(sig) if inspect.is_singledispatch_function(self.object): # append signature of singledispatch'ed functions for typ, func in self.object.registry.items(): if typ is object: pass # default implementation. skipped. else: self.annotate_to_first_argument(func, typ) documenter = FunctionDocumenter(self.directive, '') documenter.object = func documenter.objpath = [None] # type: ignore[list-item] sigs.append(documenter.format_signature()) if overloaded: if self.env.config.overloads_location == "signature": the_overloads = self.analyzer.overloads.get('.'.join(self.objpath)) assert the_overloads is not None for overload in the_overloads: sig = stringify_signature(self.process_overload_signature(overload), **kwargs) sigs.append(sig) sig = super(autodoc.FunctionDocumenter, self).format_signature(**kwargs) sigs.append(sig) return '\n'.join(sigs) def add_directive_header(self, sig: str) -> None: """ Add the directive's header. :param sig: """ super().add_directive_header(sig) if self.env.config.overloads_location == "top": for line in self.create_body_overloads(): self.add_line(f"{self.content_indent}{line}", self.get_sourcename()) def process_overload_signature(self, overload: Signature) -> Signature: """ Processes the signature of the given overloaded implementation. :param overload: """ __globals__ = safe_getattr(self.object, "__globals__", {}) overload = evaluate_signature(overload, __globals__) return super().process_overload_signature(overload) class MethodDocumenter(OverloadMixin, autodoc.MethodDocumenter): # type: ignore[misc] """ Custom :class:`autodoc.MethodDocumenter ` which renders overloads differently. """ # noqa: D400 def format_signature(self, **kwargs: Any) -> str: """ Format the method's signature, including those for any overloaded implementations. :param kwargs: :return: The signature(s), as a multi-line string. """ sigs = [] if self.analyzer and '.'.join(self.objpath) in self.analyzer.overloads: overloaded = True else: overloaded = False sig = super(autodoc.MethodDocumenter, self).format_signature(**kwargs) sigs.append(sig) meth = self.parent.__dict__.get(self.objpath[-1]) if inspect.is_singledispatch_method(meth): # append signature of singledispatch'ed functions for typ, func in meth.dispatcher.registry.items(): if typ is object: pass # default implementation. skipped. else: self.annotate_to_first_argument(func, typ) documenter = MethodDocumenter(self.directive, '') documenter.parent = self.parent documenter.object = func documenter.objpath = [None] # type: ignore[list-item] sigs.append(documenter.format_signature()) if overloaded: if self.env.config.overloads_location == "signature": the_overloads = self.analyzer.overloads.get('.'.join(self.objpath)) assert the_overloads is not None for overload in the_overloads: sig = stringify_signature(self.process_overload_signature(overload), **kwargs) sigs.append(sig) sig = super(autodoc.MethodDocumenter, self).format_signature(**kwargs) sigs.append(sig) return '\n'.join(sigs) def add_directive_header(self, sig: str) -> None: """ Add the directive's header. :param sig: """ super().add_directive_header(sig) if self.env.config.overloads_location == "top": for line in self.create_body_overloads(): self.add_line(f"{self.content_indent}{line}", self.get_sourcename()) def process_overload_signature(self, overload: Signature) -> Signature: """ Processes the signature of the given overloaded implementation. :param overload: """ __globals__ = safe_getattr(self.object, "__globals__", {}) overload = evaluate_signature(overload, __globals__) if not inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name): overload = overload.replace(parameters=list(overload.parameters.values())[1:]) return super().process_overload_signature(overload) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc.overloads`. :param app: The Sphinx application. """ app.add_autodocumenter(FunctionDocumenter, override=True) app.add_autodocumenter(MethodDocumenter, override=True) app.add_config_value( "overloads_location", "signature", "env", ENUM("top", "bottom", "signature"), # top (of body), bottom (of body) ) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/regex.py000066400000000000000000000647411475757360300240000ustar00rootroot00000000000000#!/usr/bin/env python3 # # regex.py r""" Specialized Documenter for regular expression variables, similar to :rst:dir:`autodata`. .. versionadded:: 1.2.0 .. extensions:: sphinx_toolbox.more_autodoc.regex Usage ------- .. rst:directive:: autoregex Directive to automatically document a regular expression variable. The output is based on the :rst:dir:`autodata` directive, and takes all of its options except ``:annotation:``. .. rst:directive:option:: no-value Don't show the value of the variable. .. rst:directive:option:: value: value :type: string Show this instead of the value taken from the Python source code. .. rst:directive:option:: no-type Don't show the type of the variable. .. rst:directive:option:: no-flags Don't show the flags of the :class:`~typing.Pattern` object. .. rst:directive:option:: flags: flags :type: string Show this instead of the flags taken from the :class:`~typing.Pattern` object. This should be correctly formatted for insertion into reStructuredText, such as ``:py:data:`re.ASCII```. .. versionchanged:: 2.7.0 The flags :py:data:`re.DEBUG` and :py:data:`re.VERBOSE` are now hidden as they don't affect the regex itself. .. latex:clearpage:: .. rst:role:: regex Formats a regular expression with coloured output. .. rest-example:: :regex:`^Hello\s+[Ww]orld[.,](Lovely|Horrible) weather, isn't it (.*)?` .. versionchanged:: 2.11.0 Now generates coloured output with the LaTeX builder. """ # # Copyright Β© 2020-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. # # Parts based on https://github.com/sphinx-doc/sphinx # | Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). # | BSD Licensed # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib import itertools import re import sre_parse from sre_constants import ( ANY, AT, AT_BEGINNING, AT_BEGINNING_STRING, AT_BOUNDARY, AT_END, AT_END_STRING, AT_NON_BOUNDARY, BRANCH, CATEGORY, CATEGORY_DIGIT, CATEGORY_NOT_DIGIT, CATEGORY_NOT_SPACE, CATEGORY_NOT_WORD, CATEGORY_SPACE, CATEGORY_WORD, IN, LITERAL, MAX_REPEAT, MAXREPEAT, MIN_REPEAT, RANGE, SUBPATTERN ) from textwrap import dedent from typing import Any, Callable, List, Optional, Pattern, Tuple # 3rd party import dict2css from docutils import nodes from docutils.nodes import Node, system_message from domdf_python_tools.paths import PathPlus from domdf_python_tools.stringlist import StringList from sphinx.application import Sphinx from sphinx.ext.autodoc import UNINITIALIZED_ATTR, ModuleDocumenter from sphinx.util import texescape from sphinx.util.docutils import SphinxRole from sphinx.writers.html5 import HTML5Translator # this package from sphinx_toolbox import _css from sphinx_toolbox.more_autodoc.variables import VariableDocumenter from sphinx_toolbox.utils import Config, SphinxExtMetadata, add_nbsp_substitution, flag, metadata_add_version __all__ = ( "RegexDocumenter", "RegexParser", "TerminalRegexParser", "HTMLRegexParser", "LaTeXRegexParser", "parse_regex_flags", "no_formatting", "span", "latex_textcolor", "copy_asset_files", "setup", ) class RegexDocumenter(VariableDocumenter): """ Specialized Documenter subclass for regex patterns. """ directivetype = "data" objtype = "regex" priority = VariableDocumenter.priority + 1 option_spec = { **VariableDocumenter.option_spec, "no-flag": flag, "flag": str, } del option_spec["type"] del option_spec["annotation"] @classmethod def can_document_member( cls, member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: """ Called to see if a member can be documented by this documenter. :param member: The member being checked. :param membername: The name of the member. :param isattr: :param parent: The parent of the member. """ return isinstance(parent, ModuleDocumenter) and isattr and isinstance(member, Pattern) def add_content(self, more_content: Any, no_docstring: bool = False) -> None: """ Add content from docstrings, attribute documentation and the user. :param more_content: :param no_docstring: """ # set sourcename and add content from attribute documentation sourcename = self.get_sourcename() if self.analyzer: attr_docs = self.analyzer.find_attr_docs() if self.objpath: key = ('.'.join(self.objpath[:-1]), self.objpath[-1]) if key in attr_docs: no_docstring = True # make a copy of docstring for attributes to avoid cache # the change of autodoc-process-docstring event. docstrings = [list(attr_docs[key])] for i, line in enumerate(self.process_doc(docstrings)): self.add_line(line, sourcename, i) # add content from docstrings if not no_docstring: docstrings = self.get_doc() or [] if not docstrings: # append at least a dummy docstring, so that the event # autodoc-process-docstring is fired and can add some # content if desired docstrings.append([]) if docstrings == [["Compiled regular expression objects", '']] or docstrings == [[]]: docstrings = [["Compiled regular expression object.", '']] # pylint: disable=W8301 for i, line in enumerate(self.process_doc(docstrings)): self.add_line(line, sourcename, i) # add additional content (e.g. from document), if present if more_content: for line, src in zip(more_content.data, more_content.items): self.add_line(line, src[0], src[1]) no_value = self.options.get("no-value", False) no_flag = self.options.get("no-flag", False) if self.object is not UNINITIALIZED_ATTR and (not no_value or not no_flag): self.add_line('', sourcename) self.add_line('', sourcename) the_flag: Optional[str] = None if not no_flag: if "flag" in self.options: the_flag = self.options["flag"] else: raw_flags = self.object.flags raw_flags = (raw_flags & ~re.DEBUG) & ~re.VERBOSE the_flag = parse_regex_flags(raw_flags) if no_value and not the_flag: return self.add_line(".. csv-table::", sourcename) self.add_line(" :widths: auto", sourcename) self.add_line(" :stub-columns: 1", sourcename) self.add_line('', sourcename) if not no_value: if "value" in self.options: the_pattern = self.options["value"] else: the_pattern = self.object.pattern the_pattern = the_pattern.replace('`', r"\`") leading_spaces = len(tuple(itertools.takewhile(str.isspace, the_pattern))) trailing_spaces = len(tuple(itertools.takewhile(str.isspace, the_pattern[::-1]))) the_pattern = the_pattern.strip(' ') if leading_spaces > 1: the_pattern = f"[ ]{leading_spaces}{the_pattern}" elif leading_spaces == 1: the_pattern = f"[ ]{the_pattern}" if trailing_spaces > 1: the_pattern += f" {trailing_spaces}" elif trailing_spaces == 1: the_pattern += "[ ]" self.add_line(f' **Pattern**, ":regex:`{the_pattern}`"', sourcename) if the_flag: self.add_line(f" **Flags**, {the_flag}", sourcename) self.add_line('', sourcename) def add_directive_header(self, sig: str) -> None: """ Add the directive's header. :param sig: """ user_no_value = self.options.get("no-value", False) self.options["no-value"] = True super().add_directive_header(sig) self.options["no-value"] = user_no_value def parse_regex_flags(flags: int) -> str: """ Convert regex flags into "bitwise-or'd" Sphinx xrefs. :param flags: """ buf = [] if flags & re.ASCII: buf.append("ASCII") if flags & re.DEBUG: buf.append("DEBUG") if flags & re.IGNORECASE: buf.append("IGNORECASE") if flags & re.LOCALE: buf.append("LOCALE") if flags & re.MULTILINE: buf.append("MULTILINE") if flags & re.DOTALL: buf.append("DOTALL") if flags & re.VERBOSE: buf.append("VERBOSE") return " ``|`` ".join(f":py:data:`re.{x}`" for x in buf) def no_formatting(value: Any) -> str: """ No-op that returns the value as a string. Used for unformatted output. """ return str(value) class RegexParser: r""" Parser for regular expressions that outputs coloured output. The formatting is controlled by the following callable attributes: * ``AT_COLOUR`` -- Used for e.g. :regex:`^\A\b\B\Z$` * ``SUBPATTERN_COLOUR`` -- Used for the parentheses around subpatterns, e.g. :regex:`(Hello) World` * ``IN_COLOUR`` -- Used for the square brackets around character sets, e.g. :regex:`[Hh]ello` * ``REPEAT_COLOUR`` -- Used for repeats, e.g. :regex:`A?B+C*D{2,4}E{5}` * ``REPEAT_BRACE_COLOUR`` -- Used for the braces around numerical repeats. * ``CATEGORY_COLOUR`` -- Used for categories, e.g. :regex:`\d\D\s\D\w\W` * ``BRANCH_COLOUR`` -- Used for branches, e.g. :regex:`(Lovely|Horrible) Weather` * ``LITERAL_COLOUR`` -- Used for literal characters. * ``ANY_COLOUR`` -- Used for the "any" dot. These are all :class:`~typing.Callable`\[[:class:`~typing.Any`], :class:`str`\]. By default no formatting is performed. Subclasses should set these attributes to appropriate functions. """ # Colours AT_COLOUR: Callable[[Any], str] = no_formatting SUBPATTERN_COLOUR: Callable[[Any], str] = no_formatting IN_COLOUR: Callable[[Any], str] = no_formatting REPEAT_COLOUR: Callable[[Any], str] = no_formatting REPEAT_BRACE_COLOUR: Callable[[Any], str] = no_formatting CATEGORY_COLOUR: Callable[[Any], str] = no_formatting BRANCH_COLOUR: Callable[[Any], str] = no_formatting LITERAL_COLOUR: Callable[[Any], str] = no_formatting ANY_COLOUR: Callable[[Any], str] = no_formatting def parse_pattern(self, regex: Pattern) -> str: """ Parse the given regular expression and return the formatted pattern. :param regex: """ buf = [] def _parse_pattern(pattern) -> None: # noqa: MAN001 for what, content in pattern: # print(what, content) if what is AT: if content is AT_BEGINNING: buf.append(type(self).AT_COLOUR('^')) continue elif content is AT_END: buf.append(type(self).AT_COLOUR('$')) continue elif content is AT_BEGINNING_STRING: buf.append(type(self).AT_COLOUR(r"\A")) continue elif content is AT_BOUNDARY: buf.append(type(self).AT_COLOUR(r"\b")) continue elif content is AT_NON_BOUNDARY: buf.append(type(self).AT_COLOUR(r"\B")) continue elif content is AT_END_STRING: buf.append(type(self).AT_COLOUR(r"\Z")) continue if what is SUBPATTERN: buf.append(type(self).SUBPATTERN_COLOUR('(')) group, add_flags, del_flags, subpattern = content # print(group, add_flags, del_flags) _parse_pattern(subpattern) buf.append(type(self).SUBPATTERN_COLOUR(')')) continue if what is LITERAL: # TODO: escape characters that have meaning to avoid ambiguity buf.append(type(self).LITERAL_COLOUR(chr(content))) continue if what is IN: if len(content) > 1 or content[0][0] is RANGE: buf.append(type(self).IN_COLOUR('[')) _parse_pattern(content) if len(content) > 1 or content[0][0] is RANGE: buf.append(type(self).IN_COLOUR(']')) continue if what is MAX_REPEAT or what is MIN_REPEAT: min_, max_, item = content _parse_pattern(item) if min_ == 0 and max_ is MAXREPEAT: buf.append(type(self).REPEAT_COLOUR('*')) elif min_ == 1 and max_ is MAXREPEAT: buf.append(type(self).REPEAT_COLOUR('+')) elif min_ == 0 and max_ == 1: buf.append(type(self).REPEAT_COLOUR('?')) elif min_ == max_: buf.append(type(self).REPEAT_BRACE_COLOUR('{')) buf.append(type(self).REPEAT_COLOUR(str(min_))) buf.append(type(self).REPEAT_BRACE_COLOUR('}')) else: buf.append(type(self).REPEAT_BRACE_COLOUR('{')) buf.append(type(self).REPEAT_COLOUR(str(min_))) buf.append(type(self).LITERAL_COLOUR(',')) buf.append(type(self).REPEAT_COLOUR(str(max_))) buf.append(type(self).REPEAT_BRACE_COLOUR('}')) if what is MIN_REPEAT: buf.append(type(self).REPEAT_COLOUR('?')) continue # # if what is MIN_REPEAT: # min_, max_, item = content # _parse_pattern(item) # print(min_, max_, item) # input(">>>") if what is CATEGORY: if content is CATEGORY_DIGIT: buf.append(type(self).CATEGORY_COLOUR(r"\d")) continue elif content is CATEGORY_NOT_DIGIT: buf.append(type(self).CATEGORY_COLOUR(r"\D")) continue elif content is CATEGORY_SPACE: buf.append(type(self).CATEGORY_COLOUR(r"\s")) continue elif content is CATEGORY_NOT_SPACE: buf.append(type(self).CATEGORY_COLOUR(r"\S")) continue elif content is CATEGORY_WORD: buf.append(type(self).CATEGORY_COLOUR(r"\w")) continue elif content is CATEGORY_NOT_WORD: buf.append(type(self).CATEGORY_COLOUR(r"\W")) continue if what is BRANCH: for branch in content[1]: _parse_pattern(branch) buf.append(type(self).BRANCH_COLOUR('|')) buf.pop(-1) continue if what is RANGE: buf.append(type(self).LITERAL_COLOUR(chr(content[0]))) buf.append(type(self).AT_COLOUR('-')) buf.append(type(self).LITERAL_COLOUR(chr(content[1]))) continue if what is ANY: buf.append(type(self).ANY_COLOUR('.')) continue print(what, content) # pragma: no cover pattern = regex.pattern.replace('\t', r"\t") # Remove leading and trailing spaces from the pattern. They will be added back at the end. leading_spaces = len(tuple(itertools.takewhile(str.isspace, pattern))) trailing_spaces = len(tuple(itertools.takewhile(str.isspace, pattern[::-1]))) pattern = pattern.strip(' ') tokens: List = list(sre_parse.parse(pattern, regex.flags)) # type: ignore[call-overload] if not leading_spaces: while tokens[0] == (LITERAL, ord(' ')): leading_spaces += 1 tokens.pop(0) if not trailing_spaces: while tokens[-1] == (LITERAL, ord(' ')): trailing_spaces += 1 tokens.pop(-1) if leading_spaces: buf.append(type(self).IN_COLOUR('[')) buf.append(type(self).LITERAL_COLOUR(' ')) buf.append(type(self).IN_COLOUR(']')) if leading_spaces > 1: buf.append(type(self).REPEAT_BRACE_COLOUR('{')) buf.append(type(self).REPEAT_COLOUR(str(leading_spaces))) buf.append(type(self).REPEAT_BRACE_COLOUR('}')) _parse_pattern(tokens) if trailing_spaces == 1: buf.append(type(self).IN_COLOUR('[')) buf.append(type(self).LITERAL_COLOUR(' ')) buf.append(type(self).IN_COLOUR(']')) elif trailing_spaces > 1: buf.append(type(self).LITERAL_COLOUR(' ')) buf.append(type(self).REPEAT_BRACE_COLOUR('{')) buf.append(type(self).REPEAT_COLOUR(str(trailing_spaces))) buf.append(type(self).REPEAT_BRACE_COLOUR('}')) return ''.join(buf) def span(css_class: str) -> Callable[[Any], str]: """ Returns a function that wraps a value in a ``span`` tag with the given class. :param css_class: """ def f(value: Any) -> str: return f'{value}' return f def latex_textcolor(colour_name: str) -> Callable[[Any], str]: """ Returns a function that wraps a value in a LaTeX ``textcolor`` command for the given colour. .. versionadded:: 2.11.0 :param colour_name: """ def f(value: Any) -> str: if value == ' ': return "\\enspace" return f'\\textcolor{{{colour_name}}}{{{texescape.escape(value)}}}' return f class HTMLRegexParser(RegexParser): r""" :class:`~.RegexParser` that outputs styled HTML. The formatting is controlled by the following functions, which wrap the character in a ``span`` tag with an appropriate CSS class: * ``AT_COLOUR`` -> ``regex_at`` -- Used for e.g. :regex:`^\A\b\B\Z$` * ``SUBPATTERN_COLOUR`` -> ``regex_subpattern`` -- Used for the parentheses around subpatterns, e.g. :regex:`(Hello) World` * ``IN_COLOUR`` -> ``regex_in`` -- Used for the square brackets around character sets, e.g. :regex:`[Hh]ello` * ``REPEAT_COLOUR`` -> ``regex_repeat`` -- Used for repeats, e.g. :regex:`A?B+C*D{2,4}E{5}` * ``REPEAT_BRACE_COLOUR`` -> ``regex_repeat_brace`` -- Used for the braces around numerical repeats. * ``CATEGORY_COLOUR`` -> ``regex_category`` -- Used for categories, e.g. :regex:`\d\D\s\D\w\W` * ``BRANCH_COLOUR`` -> ``regex_branch`` -- Used for branches, e.g. :regex:`(Lovely|Horrible) Weather` * ``LITERAL_COLOUR`` -> ``regex_literal`` -- Used for literal characters. * ``ANY_COLOUR`` -> ``regex_any`` -- Used for the "any" dot. Additionally, all ``span`` tags the ``regex`` class, and the surrounding ``code`` tag has the following classes: ``docutils literal notranslate regex``. """ # Colours AT_COLOUR = span("regex regex_at") SUBPATTERN_COLOUR = span("regex regex_subpattern") IN_COLOUR = span("regex regex_in") REPEAT_COLOUR = span("regex regex_repeat") REPEAT_BRACE_COLOUR = span("regex regex_repeat_brace") CATEGORY_COLOUR = span("regex regex_category") BRANCH_COLOUR = span("regex regex_branch") LITERAL_COLOUR = span("regex regex_literal") ANY_COLOUR = span("regex regex_any") def parse_pattern(self, regex: Pattern) -> str: """ Parse the given regular expression and return the formatted pattern. :param regex: """ return dedent( f""" {super().parse_pattern(regex)} """ ) class LaTeXRegexParser(RegexParser): r""" :class:`~.RegexParser` that outputs styled LaTeX. The formatting is controlled by the following functions, which wrap the character in a LaTeX ``textcolor`` command for an appropriate colour: * ``AT_COLOUR`` -> ``regex_at`` -- Used for e.g. :regex:`^\A\b\B\Z$` * ``SUBPATTERN_COLOUR`` -> ``regex_subpattern`` -- Used for the parentheses around subpatterns, e.g. :regex:`(Hello) World` * ``IN_COLOUR`` -> ``regex_in`` -- Used for the square brackets around character sets, e.g. :regex:`[Hh]ello` * ``REPEAT_COLOUR`` -> ``regex_repeat`` -- Used for repeats, e.g. :regex:`A?B+C*D{2,4}E{5}` * ``REPEAT_BRACE_COLOUR`` -> ``regex_repeat_brace`` -- Used for the braces around numerical repeats. * ``CATEGORY_COLOUR`` -> ``regex_category`` -- Used for categories, e.g. :regex:`\d\D\s\D\w\W` * ``BRANCH_COLOUR`` -> ``regex_branch`` -- Used for branches, e.g. :regex:`(Lovely|Horrible) Weather` * ``LITERAL_COLOUR`` -> ``regex_literal`` -- Used for literal characters. * ``ANY_COLOUR`` -> ``regex_any`` -- Used for the "any" dot. .. versionadded:: 2.11.0 """ # Colours AT_COLOUR = latex_textcolor("regex_at") SUBPATTERN_COLOUR = latex_textcolor("regex_subpattern") IN_COLOUR = latex_textcolor("regex_in") REPEAT_COLOUR = latex_textcolor("regex_repeat") REPEAT_BRACE_COLOUR = latex_textcolor("regex_repeat_brace") CATEGORY_COLOUR = latex_textcolor("regex_category") BRANCH_COLOUR = latex_textcolor("regex_branch") LITERAL_COLOUR = latex_textcolor("regex_literal") ANY_COLOUR = latex_textcolor("regex_any") def parse_pattern(self, regex: Pattern) -> str: """ Parse the given regular expression and return the formatted pattern. :param regex: """ return f"\\sphinxcode{{\\sphinxupquote{{{super().parse_pattern(regex)}}}}}" class TerminalRegexParser(RegexParser): r""" :class:`~.RegexParser` that outputs ANSI coloured output for the terminal. The formatting is controlled by the following callable attributes, which set ANSI escape codes for the appropriate colour: * ``AT_COLOUR`` -> YELLOW, Used for e.g. :regex:`^\A\b\B\Z$` * ``SUBPATTERN_COLOUR`` -> LIGHTYELLOW_EX, Used for the parentheses around subpatterns, e.g. :regex:`(Hello) World` * ``IN_COLOUR`` -> LIGHTRED_EX, Used for the square brackets around character sets, e.g. :regex:`[Hh]ello` * ``REPEAT_COLOUR`` -> LIGHTBLUE_EX, Used for repeats, e.g. :regex:`A?B+C*D{2,4}E{5}` * ``REPEAT_BRACE_COLOUR`` -> YELLOW, Used for the braces around numerical repeats. * ``CATEGORY_COLOUR`` -> LIGHTYELLOW_EX, Used for categories, e.g. :regex:`\d\D\s\D\w\W` * ``BRANCH_COLOUR`` -> YELLOW, Used for branches, e.g. :regex:`(Lovely|Horrible) Weather` * ``LITERAL_COLOUR`` -> GREEN, Used for literal characters. * ``ANY_COLOUR`` -> YELLOW, Used for the "any" dot. """ # Colours @staticmethod def AT_COLOUR(s: str) -> str: # noqa: D102 return f"\x1b[33m{s}\x1b[39m" @staticmethod def SUBPATTERN_COLOUR(s: str) -> str: # noqa: D102 return f"\x1b[93m{s}\x1b[39m" @staticmethod def IN_COLOUR(s: str) -> str: # noqa: D102 return f"\x1b[91m{s}\x1b[39m" @staticmethod def REPEAT_COLOUR(s: str) -> str: # noqa: D102 return f"\x1b[94m{s}\x1b[39m" @staticmethod def LITERAL_COLOUR(s: str) -> str: # noqa: D102 return f"\x1b[32m{s}\x1b[39m" REPEAT_BRACE_COLOUR = BRANCH_COLOUR = ANY_COLOUR = AT_COLOUR CATEGORY_COLOUR = SUBPATTERN_COLOUR class RegexNode(nodes.literal): """ Docutils Node to show a highlighted regular expression. """ def __init__(self, rawsource: str = '', text: str = '', *children, **attributes) -> None: super().__init__(rawsource, text, *children, **attributes) @property def pattern(self) -> re.Pattern: return re.compile(':'.join(self.rawsource.split(':')[2:])[1:-1]) class Regex(SphinxRole): """ Docutils role to show a highlighted regular expression. """ def run(self) -> Tuple[List[Node], List[system_message]]: """ Process the content of the regex role. """ options = self.options.copy() return [RegexNode(self.rawtext, self.text, **options)], [] def visit_regex_node(translator: HTML5Translator, node: RegexNode) -> None: """ Visit an :class:`~.RegexNode`. :param translator: :param node: The node being visited. """ translator.body.append(regex_parser.parse_pattern(node.pattern)) def depart_regex_node(translator: HTML5Translator, node: RegexNode) -> None: """ Depart an :class:`~.RegexNode`. :param translator: :param node: The node being visited. """ translator.body.pop(-1) def visit_regex_node_latex(translator: HTML5Translator, node: RegexNode) -> None: """ Visit an :class:`~.RegexNode` with the LaTeX builder. .. versionadded:: 2.11.0 :param translator: :param node: The node being visited. """ translator.body.append(latex_regex_parser.parse_pattern(node.pattern)) def depart_regex_node_latex(translator: HTML5Translator, node: RegexNode) -> None: """ Depart an :class:`~.RegexNode` with the LaTeX builder. .. versionadded:: 2.11.0 :param translator: :param node: The node being visited. """ translator.body.pop(-1) def copy_asset_files(app: Sphinx, exception: Optional[Exception] = None) -> None: """ Copy additional stylesheets into the HTML build directory. :param app: The Sphinx application. :param exception: Any exception which occurred and caused Sphinx to abort. """ if exception: # pragma: no cover return if app.builder is None or app.builder.format.lower() != "html": # pragma: no cover return static_dir = PathPlus(app.outdir) / "_static" static_dir.maybe_make(parents=True) dict2css.dump(_css.regex_styles, static_dir / "regex.css", minify=True) regex_parser = HTMLRegexParser() latex_regex_parser = LaTeXRegexParser() def configure(app: Sphinx, config: Config) -> None: """ Configure :mod:`sphinx_toolbox.code`. .. versionadded:: 2.11.0 :param app: The Sphinx application. :param config: """ latex_elements = getattr(app.config, "latex_elements", {}) latex_preamble = StringList(latex_elements.get("preamble", '')) latex_preamble.blankline() latex_preamble.append(r"\definecolor{regex_literal}{HTML}{696969}") latex_preamble.append(r"\definecolor{regex_at}{HTML}{FF4500}") latex_preamble.append(r"\definecolor{regex_repeat_brace}{HTML}{FF4500}") latex_preamble.append(r"\definecolor{regex_branch}{HTML}{FF4500}") latex_preamble.append(r"\definecolor{regex_subpattern}{HTML}{1e90ff}") latex_preamble.append(r"\definecolor{regex_in}{HTML}{ff8c00}") latex_preamble.append(r"\definecolor{regex_category}{HTML}{8fbc8f}") latex_preamble.append(r"\definecolor{regex_repeat}{HTML}{FF4500}") latex_preamble.append(r"\definecolor{regex_any}{HTML}{FF4500}") latex_elements["preamble"] = str(latex_preamble) app.config.latex_elements = latex_elements # type: ignore[attr-defined] add_nbsp_substitution(config) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc.regex`. :param app: The Sphinx application. """ app.setup_extension("sphinx.ext.autodoc") app.setup_extension("sphinx_toolbox._css") app.connect("config-inited", configure) app.add_autodocumenter(RegexDocumenter) app.add_role("regex", Regex()) app.add_node( RegexNode, html=(visit_regex_node, depart_regex_node), latex=(visit_regex_node_latex, depart_regex_node_latex) ) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/sourcelink.py000066400000000000000000000112311475757360300250260ustar00rootroot00000000000000#!/usr/bin/env python3 # # sourcelink.py r""" Show a link to the corresponding source code at the top of :rst:dir:`automodule` output. .. versionadded:: 0.6.0 .. extensions:: sphinx_toolbox.more_autodoc.sourcelink Configuration ---------------- .. raw:: latex \begin{flushleft} :mod:`sphinx_toolbox.more_autodoc.sourcelink` can be configured using the :confval:`autodoc_default_options` option in ``conf.py``, or with the :rst:dir:`:sourcelink: ` option flag to :rst:dir:`automodule`. .. raw:: latex \end{flushleft} .. confval:: autodoc_show_sourcelink :type: :class:`bool` :default: :py:obj:`False` If :py:obj:`True`, shows a link to the corresponding source code at the top of each :rst:dir:`automodule` directive. .. rst:directive:option:: sourcelink When passed as an option flag to an :rst:dir:`automodule` directive, show a link to the corresponding source code at the top of the output *for that module only*. .. latex:vspace:: 5px .. versionchanged:: 1.1.0 Added support for the :rst:dir:`:sourcelink: ` option flag to :rst:dir:`automodule`. .. latex:clearpage:: API Reference -------------- """ # # Copyright Β© 2020-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 types import ModuleType from typing import Any, List, Mapping # 3rd party import autodocsumm # type: ignore[import-untyped] import sphinx.ext.autodoc from sphinx.application import Sphinx # this package from sphinx_toolbox.more_autosummary import PatchedAutoSummModuleDocumenter from sphinx_toolbox.utils import SphinxExtMetadata, flag, metadata_add_version __all__ = ("sourcelinks_process_docstring", "setup") def sourcelinks_process_docstring( # noqa: MAN001 app: Sphinx, what, name: str, obj, options: Mapping[str, Any], lines: List[str], ) -> None: """ Process the docstring of a module and add a link to the source code if given in the configuration. :param app: The Sphinx application. :param what: :param name: The name of the object being documented. :param obj: The object being documented. :param options: Mapping of autodoc options to values. :param lines: List of strings representing the current contents of the docstring. """ show_sourcelink = options.get("sourcelink", app.config.autodoc_show_sourcelink) if isinstance(obj, ModuleType) and what == "module" and show_sourcelink: if not obj.__file__: return elif obj.__file__.endswith("/__init__.py"): source_target = f"{name.replace('.', '/')}/__init__.py" elif obj.__file__.endswith("\\__init__.py"): source_target = f"{name.replace('.', '/')}/__init__.py" elif obj.__file__.endswith(".py"): source_target = f"{name.replace('.', '/')}.py" else: return lines_to_insert = ( ".. rst-class:: source-link", '', f" **Source code:** :source:`{source_target}`", '', "--------------------", '', ) for line in reversed(lines_to_insert): # pylint: disable=W8402 lines.insert(0, line) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc.sourcelink`. :param app: The Sphinx application. """ sphinx.ext.autodoc.ModuleDocumenter.option_spec["sourcelink"] = flag autodocsumm.AutoSummModuleDocumenter.option_spec["sourcelink"] = flag PatchedAutoSummModuleDocumenter.option_spec["sourcelink"] = flag app.setup_extension("sphinx_toolbox.source") app.setup_extension("sphinx_toolbox._css") app.setup_extension("sphinx.ext.autodoc") app.connect("autodoc-process-docstring", sourcelinks_process_docstring) app.add_config_value("autodoc_show_sourcelink", False, "env", [bool]) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/typehints.py000066400000000000000000000667411475757360300247170ustar00rootroot00000000000000#!/usr/bin/env python3 # # autodoc_typehints.py r""" | Enhanced version of `sphinx-autodoc-typehints `_. | Copyright (c) Alex GrΓΆnholm The changes are: * *None* is formatted as :py:obj:`None` and not ``None``. If `intersphinx `_ is used this will now be a link to the Python documentation. Since :github:pull:`154 ` this feature is now available upstream. * If the signature of the object cannot be read, the signature provided by Sphinx will be used rather than raising an error. This usually occurs for methods of builtin types. * :class:`typing.TypeVar`\s are linked to if they have been included in the documentation. * If a function/method argument has a :class:`module `, :class:`class ` or :py:obj:`function ` object as its default value a better representation will be shown in the signature. For example: .. autofunction:: sphinx_toolbox.more_autodoc.typehints.serialise :noindex: Previously this would have shown the full path to the source file. Now it displays ````. * The ability to hook into the :func:`~.process_docstring` function to edit the object's properties before the annotations are added to the docstring. This is used by `attr-utils `_ to add annotations based on converter functions in `attrs `_ classes. To use this, in your extension's ``setup`` function: .. code-block:: python def setup(app: Sphinx) -> Dict[str, Any]: from sphinx_toolbox.more_autodoc.typehints import docstring_hooks docstring_hooks.append((my_hook, 75)) return {} .. latex:clearpage:: ``my_hook`` is a function that takes the object being documented as its only argument and returns that object after modification. The ``75`` is the priority of the hook: * ``< 20`` runs before ``fget`` functions are extracted from properties * ``< 90`` runs before ``__new__`` functions are extracted from :class:`~typing.NamedTuple`\s. * ``< 100`` runs before ``__init__`` functions are extracted from classes. * Unresolved forward references are handled better. * Many of the built in types from the :mod:`types` module are now formatted and linked to correctly. ----- .. versionadded:: 0.4.0 .. versionchanged:: 0.6.0 Moved from :mod:`sphinx_toolbox.autodoc_typehints`. .. versionchanged:: 0.8.0 Added support for :func:`collections.namedtuple`. ----- .. extensions:: sphinx_toolbox.more_autodoc.typehints .. latex:vspace:: -20px | For configuration information see https://github.com/agronholm/sphinx-autodoc-typehints | In addition, the following configuration value is added by this extension: .. latex:vspace:: -20px .. confval:: hide_none_rtype :type: :class:`bool` :default: :py:obj:`False` Hides return types of :py:obj:`None`. API Reference ----------------- """ # noqa: SXL001 # # Copyright (c) Alex GrΓΆnholm # Changes copyright Β© 2020-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 import inspect import itertools import json import re import sys import types from contextlib import suppress from operator import itemgetter from tempfile import TemporaryDirectory from types import FunctionType, ModuleType from typing import ( Any, AnyStr, Callable, Dict, ForwardRef, List, Mapping, NewType, Optional, Tuple, Type, TypeVar, Union, get_type_hints ) # 3rd party import sphinx.util.typing import sphinx_autodoc_typehints from domdf_python_tools.stringlist import DelimitedList from domdf_python_tools.typing import ( ClassMethodDescriptorType, MethodDescriptorType, MethodWrapperType, WrapperDescriptorType ) from domdf_python_tools.utils import etc from sphinx.application import Sphinx from sphinx.errors import ExtensionError from sphinx.util.inspect import signature as Signature from sphinx.util.inspect import stringify_signature from typing_extensions import Self # this package from sphinx_toolbox.utils import ( SphinxExtMetadata, code_repr, escape_trailing__, is_namedtuple, metadata_add_version ) try: # 3rd party import attr except ImportError: # pragma: no cover # attrs is used in a way that it is only required in situations # where it is available to import, so it is fine to do this. pass try: # 3rd party from sphinx_autodoc_typehints import logger as sat_logger # type: ignore[attr-defined] from sphinx_autodoc_typehints import pydata_annotations # type: ignore[attr-defined] except ImportError: # Privatised in 1.14.1 # 3rd party from sphinx_autodoc_typehints import _LOGGER as sat_logger from sphinx_autodoc_typehints import _PYDATA_ANNOTATIONS as pydata_annotations try: # 3rd party from sphinx_autodoc_typehints import _future_annotations_imported except ImportError: def _future_annotations_imported(obj: Any) -> bool: if sys.version_info < (3, 7): # pragma: no cover (py37+) # Only Python β‰₯ 3.7 supports PEP563. return False _annotations = getattr(inspect.getmodule(obj), "annotations", None) if _annotations is None: return False # Make sure that annotations is imported from __future__ - defined in cpython/Lib/__future__.py # annotations become strings at runtime CO_FUTURE_ANNOTATIONS = 0x100000 if sys.version_info[0:2] == (3, 7) else 0x1000000 return _annotations.compiler_flag == CO_FUTURE_ANNOTATIONS __all__ = ( "ObjectAlias", "Module", "Function", "Class", "process_signature", "process_docstring", "format_annotation", "get_all_type_hints", "docstring_hooks", "setup", "get_annotation_module", "get_annotation_class_name", "get_annotation_args", "backfill_type_hints", "load_args", "split_type_comment_args", "default_preprocessors", "Preprocessor", ) get_annotation_module = sphinx_autodoc_typehints.get_annotation_module get_annotation_class_name = sphinx_autodoc_typehints.get_annotation_class_name get_annotation_args = sphinx_autodoc_typehints.get_annotation_args backfill_type_hints = sphinx_autodoc_typehints.backfill_type_hints load_args = sphinx_autodoc_typehints.load_args split_type_comment_args = sphinx_autodoc_typehints.split_type_comment_args # Demonstration of module default argument in signature def serialise(obj: Any, library=json) -> str: # type: ignore[empty-body] # noqa: MAN001 """ Serialise an object into a JSON string. :param obj: The object to serialise. :param library: The JSON library to use. :no-default library: :return: The JSON string. """ class ObjectAlias: """ Used to represent a module, class, function etc in a Sphinx function/class signature. .. versionadded:: 0.9.0 :param name: The name of the object being aliased. """ _alias_type: str def __init__(self, name: str): self.name: str = name def __repr__(self) -> str: """ Returns a string representation of the :class:`~.ObjectAlias`. """ return f"<{self._alias_type} {self.name!r}>" class Module(ObjectAlias): """ Used to represent a module in a Sphinx function/class signature. :param name: The name of the module. """ _alias_type = "module" class Function(ObjectAlias): """ Used to represent a function in a Sphinx function/class signature. .. versionadded:: 0.9.0 :param name: The name of the function. """ _alias_type = "function" class Class(ObjectAlias): """ Used to represent a class in a Sphinx function/class signature. .. versionadded:: 0.9.0 :param name: The name of the class. """ _alias_type = "class" def format_annotation(annotation: Any, fully_qualified: bool = False) -> str: """ Format a type annotation. :param annotation: :param fully_qualified: Whether the fully qualified name should be shown (e.g. ``typing.List``) or only the object name (e.g. ``List``). :rtype: .. versionchanged:: 2.13.0 Added support for :py:obj:`True` and :py:obj:`False` .. latex:clearpage:: """ prefix = '' if fully_qualified else '~' # Special cases if annotation is None or annotation is type(None): # noqa: E721 return ":py:obj:`None`" elif isinstance(annotation, bool): return f":py:obj:`{annotation}`" elif annotation is Ellipsis: return "..." elif annotation is Self: return f":py:obj:`{prefix}typing.Self`" elif annotation is itertools.cycle: return f":func:`{prefix}itertools.cycle`" elif annotation is types.GetSetDescriptorType: # noqa: E721 return f":py:data:`{prefix}types.GetSetDescriptorType`" elif annotation is types.MemberDescriptorType: # noqa: E721 return f":py:data:`{prefix}types.MemberDescriptorType`" elif annotation is types.MappingProxyType: # noqa: E721 return f":py:class:`{prefix}types.MappingProxyType`" elif annotation is types.ModuleType: # noqa: E721 return f":py:class:`{prefix}types.ModuleType`" elif annotation is types.FunctionType: # noqa: E721 return f":py:data:`{prefix}types.FunctionType`" elif annotation is types.BuiltinFunctionType: # noqa: E721 return f":py:data:`{prefix}types.BuiltinFunctionType`" elif annotation is types.MethodType: # noqa: E721 return f":py:data:`{prefix}types.MethodType`" elif annotation is MethodDescriptorType: return f":py:data:`{prefix}types.MethodDescriptorType`" elif annotation is ClassMethodDescriptorType: return f":py:data:`{prefix}types.ClassMethodDescriptorType`" elif annotation is MethodWrapperType: return f":py:data:`{prefix}types.MethodWrapperType`" elif annotation is WrapperDescriptorType: return f":py:data:`{prefix}types.WrapperDescriptorType`" elif isinstance(annotation, ForwardRef): # Unresolved forward ref return f":py:obj:`{prefix}.{annotation.__forward_arg__}`" elif annotation is type(re.compile('')): # noqa: E721 return f":py:class:`{prefix}typing.Pattern`" elif annotation is TemporaryDirectory: return f":py:obj:`{prefix}tempfile.TemporaryDirectory`" elif sys.version_info >= (3, 10): # pragma: no cover (= (3, 10) and isinstance(annotation, NewType): # pragma: no cover (`" # Some types require special handling if full_name == "typing.NewType": args_format = f"\\(:py:data:`~{annotation.__name__}`, {{}})" role = "class" if sys.version_info > (3, 10) else "func" elif full_name in {"typing.Union", "types.UnionType"} and len(args) == 2 and type(None) in args: full_name = "typing.Optional" elif full_name == "types.UnionType": full_name = "typing.Union" role = "data" elif full_name == "typing.Callable" and args and args[0] is not ...: formatted_args = "\\[\\[" + ", ".join(format_annotation(arg) for arg in args[:-1]) + ']' formatted_args += ", " + format_annotation(args[-1]) + ']' elif full_name == "typing.Literal": # TODO: Enums? formatted_arg_list: DelimitedList[str] = DelimitedList() for arg in args: if isinstance(arg, bool): formatted_arg_list.append(format_annotation(arg)) else: formatted_arg_list.append(code_repr(arg)) formatted_args = f"\\[{formatted_arg_list:, }]" if full_name == "typing.Optional": args = tuple(x for x in args if x is not type(None)) # noqa: E721 # TODO: unions with one or more forward refs if args and not formatted_args: formatted_args = args_format.format(", ".join(format_annotation(arg, fully_qualified) for arg in args)) return f":py:{role}:`{prefix}{full_name}`{formatted_args}" #: Type hint for default preprocessor functions. Preprocessor = Callable[[Type], Any] default_preprocessors: List[Tuple[Callable[[Type], bool], Preprocessor]] = [ (lambda d: isinstance(d, ModuleType), lambda d: Module(d.__name__)), (lambda d: isinstance(d, FunctionType), lambda d: Function(d.__name__)), (inspect.isclass, lambda d: Class(d.__name__)), (lambda d: d is Ellipsis, lambda d: etc), (lambda d: d == "...", lambda d: etc), ] """ A list of 2-element tuples, comprising a function to check the default value against and a preprocessor to pass the function to if True. """ def preprocess_function_defaults(obj: Callable) -> Tuple[Optional[inspect.Signature], List[inspect.Parameter]]: """ Pre-processes the default values for the arguments of a function. .. versionadded:: 0.8.0 :param obj: The function. :return: The function signature and a list of arguments/parameters. """ try: signature = Signature(inspect.unwrap(obj)) except ValueError: # pragma: no cover return None, [] parameters = [] preprocessor_list = default_preprocessors for param in signature.parameters.values(): default = param.default # pylint: disable=dotted-import-in-loop if default is not inspect.Parameter.empty: for check, preprocessor in preprocessor_list: if check(default): default = preprocessor(default) break parameters.append(param.replace(annotation=inspect.Parameter.empty, default=default)) # pylint: enable=dotted-import-in-loop return signature, parameters def preprocess_class_defaults( obj: Callable ) -> Tuple[Optional[Callable], Optional[inspect.Signature], List[inspect.Parameter]]: """ Pre-processes the default values for the arguments of a class. .. versionadded:: 0.8.0 :param obj: The class. :return: The class signature and a list of arguments/parameters. """ init: Optional[Callable[..., Any]] = getattr(obj, "__init__", getattr(obj, "__new__", None)) if is_namedtuple(obj): init = getattr(obj, "__new__") try: signature = Signature(inspect.unwrap(init)) # type: ignore[arg-type] except ValueError: # pragma: no cover return init, None, [] parameters = [] preprocessor_list = default_preprocessors for argname, param in signature.parameters.items(): default = param.default if default is not inspect.Parameter.empty: for check, preprocessor in preprocessor_list: if check(default): default = preprocessor(default) break else: if hasattr(obj, "__attrs_attrs__") and default is attr.NOTHING: # Special casing for attrs classes for value in obj.__attrs_attrs__: if value.name == argname: if isinstance(value.default, attr.Factory): # type: ignore[arg-type] default = value.default.factory() parameters.append(param.replace(annotation=inspect.Parameter.empty, default=default)) return init, signature, parameters def process_signature( # noqa: MAN001 app: Sphinx, what: str, name: str, obj, options, signature, return_annotation: Any, ) -> Optional[Tuple[str, None]]: """ Process the signature for a function/method. :param app: The Sphinx application. :param what: :param name: The name of the object being documented. :param obj: The object being documented. :param options: Mapping of autodoc options to values. :param signature: :param return_annotation: :rtype: .. versionchanged:: 0.8.0 Added support for factory function default values in attrs classes. """ if not callable(obj): return None original_obj = obj if inspect.isclass(obj): obj, signature, parameters = preprocess_class_defaults(obj) else: signature, parameters = preprocess_function_defaults(obj) obj = inspect.unwrap(obj) if not getattr(obj, "__annotations__", None): return None # The generated dataclass __init__() and class are weird and need extra checks # This helper function operates on the generated class and methods # of a dataclass, not an instantiated dataclass object. As such, # it cannot be replaced by a call to `dataclasses.is_dataclass()`. def _is_dataclass(name: str, what: str, qualname: str) -> bool: if what == "method" and name.endswith(".__init__"): # generated __init__() return True if what == "class" and qualname.endswith(".__init__"): # generated class return True return False # The generated dataclass __init__() is weird and needs the second condition if ( hasattr(obj, "__qualname__") and "" in obj.__qualname__ and not _is_dataclass(name, what, obj.__qualname__) ): sat_logger.warning( "Cannot treat a function defined as a local function: '%s' (use @functools.wraps)", name ) return None if parameters: if inspect.isclass(original_obj) or (what == "method" and name.endswith(".__init__")): del parameters[0] elif what == "method": try: outer = inspect.getmodule(obj) if outer is not None: for clsname in obj.__qualname__.split('.')[:-1]: outer = getattr(outer, clsname) except AttributeError: outer = None method_name = obj.__name__ if method_name.startswith("__") and not method_name.endswith("__"): # If the method starts with double underscore (dunder) # Python applies mangling so we need to prepend the class name. # This doesn't happen if it always ends with double underscore. class_name = obj.__qualname__.split('.')[-2] method_name = f"_{class_name}{method_name}" if outer is not None: method_object = outer.__dict__[method_name] if outer else obj if not isinstance(method_object, (classmethod, staticmethod)): del parameters[0] else: if not inspect.ismethod(obj) and parameters[0].name in {"self", "cls", "_cls"}: del parameters[0] signature = signature.replace(parameters=parameters, return_annotation=inspect.Signature.empty) return stringify_signature(signature), None # .replace('\\', '\\\\') def _docstring_property_hook(obj: Any) -> Callable: if isinstance(obj, property): obj = obj.fget return obj def _docstring_class_hook(obj: Any) -> Callable: if callable(obj): if inspect.isclass(obj): obj = getattr(obj, "__init__") return obj def _docstring_namedtuple_hook(obj: Any) -> Callable: if is_namedtuple(obj): obj = getattr(obj, "__new__") return obj docstring_hooks: List[Tuple[Callable[[Any], Callable], int]] = [ (_docstring_property_hook, 20), (_docstring_namedtuple_hook, 90), (_docstring_class_hook, 100), ] """ List of additional hooks to run in :func:`~sphinx_toolbox.more_autodoc.typehints.process_docstring`. Each entry in the list consists of: * a function that takes the object being documented as its only argument and returns that object after modification. * a number giving the priority of the hook, in ascending order. * ``< 20`` runs before ``fget`` functions are extracted from properties * ``< 90`` runs before ``__new__`` functions are extracted from :class:`NamedTuples `. * ``< 100`` runs before ``__init__`` functions are extracted from classes. """ def process_docstring( app: Sphinx, what: str, name: str, obj: Any, options: Dict[str, Any], lines: List[str], ) -> None: """ Process the docstring of a class, function, method etc. :param app: The Sphinx application. :param what: :param name: The name of the object being documented. :param obj: The object being documented. :param options: Mapping of autodoc options to values. :param lines: List of strings representing the current contents of the docstring. .. versionchanged:: 1.1.0 An empty ``:rtype:`` flag can be used to control the position of the return type annotation in the docstring. """ if what in {"variable", "regex"}: # Doesn't have parameters or return type return original_obj = obj for hook, priority in sorted(docstring_hooks, key=itemgetter(1)): obj = hook(obj) if callable(obj): obj = inspect.unwrap(obj) type_hints = get_all_type_hints(obj, name, original_obj) signature_params: Mapping[str, Any] try: signature_params = inspect.signature(obj).parameters except Exception: # Ignore errors. Can be a multitude of things including builtin functions or extension modules. signature_params = {} for argname, annotation in type_hints.items(): if argname == "return": continue # this is handled separately later if argname in signature_params: # Ensure *args and **kwargs have *s if signature_params[argname].kind == 2: # *args argname = f"\\*{argname}" elif signature_params[argname].kind == 4: # **kwargs argname = f"\\*\\*{argname}" argname = escape_trailing__(argname) formatted_annotation = format_annotation( annotation, fully_qualified=app.config.typehints_fully_qualified, ) searchfor = [f":{field} {argname}:" for field in ("param", "parameter", "arg", "argument")] insert_index = None for i, line in enumerate(lines): if any(line.startswith(search_string) for search_string in searchfor): insert_index = i break if insert_index is None and app.config.always_document_param_types: lines.append(f":param {argname}:") insert_index = len(lines) if insert_index is not None: lines.insert(insert_index, f":type {argname}: {formatted_annotation}") if "return" in type_hints and not inspect.isclass(original_obj): # This avoids adding a return type for data class __init__ methods if what == "method" and name.endswith(".__init__"): return formatted_annotation = format_annotation( type_hints["return"], fully_qualified=app.config.typehints_fully_qualified, ) insert_index = len(lines) for i, line in enumerate(lines): if line.startswith(":rtype:"): if line[7:].strip(): insert_index = None else: insert_index = i lines.pop(i) break elif line.startswith(":return:") or line.startswith(":returns:"): insert_index = i if insert_index is not None and app.config.typehints_document_rtype: if insert_index == len(lines): # Ensure that :rtype: doesn't get joined with a paragraph of text, which # prevents it being interpreted. lines.append('') insert_index += 1 if not (formatted_annotation == ":py:obj:`None`" and app.config.hide_none_rtype): lines.insert(insert_index, f":rtype: {formatted_annotation}") def _class_get_type_hints(obj, globalns=None, localns=None): # noqa: MAN001,MAN002 """ Return type hints for an object. For classes, unlike :func:`typing.get_type_hints` this will attempt to use the global namespace of the modules where the class and its parents were defined until it can resolve all forward references. """ if not inspect.isclass(obj): return get_type_hints(obj, localns=localns, globalns=globalns) mro_stack = list(obj.__mro__) if localns is None: localns = {} while True: try: # pylint: disable=R8203 return get_type_hints(obj.__init__, localns=localns, globalns=globalns) except NameError: if not mro_stack: raise klasse = mro_stack.pop(0) if klasse is object or klasse.__module__ == "builtins": raise localns = {**sys.modules[klasse.__module__].__dict__, **localns} def get_all_type_hints(obj: Any, name: str, original_obj: Any) -> Dict[str, Any]: """ Returns the resolved type hints for the given objects. :param obj: :param name: :param original_obj: The original object, before the class if ``obj`` is its ``__init__`` method. """ def log(exc: Exception) -> None: sat_logger.warning( 'Cannot resolve forward reference in type annotations of "%s": %s', name, exc, ) rv = {} try: if inspect.isclass(original_obj): rv = _class_get_type_hints(original_obj) else: rv = get_type_hints(obj) except (AttributeError, TypeError, RecursionError) as exc: # Introspecting a slot wrapper will raise TypeError, and some recursive type # definitions will cause a RecursionError (https://github.com/python/typing/issues/574). # If one is using PEP563 annotations, Python will raise a (e.g.,) # TypeError("TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'") # on 'str | None', therefore we accept TypeErrors with that error message # if 'annotations' is imported from '__future__'. if isinstance(exc, TypeError): if _future_annotations_imported(obj) and "unsupported operand type" in str(exc): rv = obj.__annotations__ except NameError as exc: log(exc) rv = obj.__annotations__ if rv: return rv rv = backfill_type_hints(obj, name) try: if rv != {}: obj.__annotations__ = rv except (AttributeError, TypeError): return rv try: if inspect.isclass(original_obj): rv = _class_get_type_hints(original_obj) else: rv = get_type_hints(obj) except (AttributeError, TypeError): pass except NameError as exc: log(exc) rv = obj.__annotations__ return rv @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc.typehints`. :param app: The Sphinx application. """ if "sphinx_autodoc_typehints" in app.extensions: raise ExtensionError( "'sphinx_toolbox.more_autodoc.typehints' must be loaded before 'sphinx_autodoc_typehints'." ) sphinx_autodoc_typehints.format_annotation = format_annotation # type: ignore[assignment] sphinx_autodoc_typehints.process_signature = process_signature sphinx_autodoc_typehints.process_docstring = process_docstring # type: ignore[assignment] app.setup_extension("sphinx.ext.autodoc") app.setup_extension("sphinx_autodoc_typehints") app.add_config_value("hide_none_rtype", False, "env", [bool]) return {"parallel_read_safe": True} def _resolve_forwardref( fr: Union[ForwardRef, "sphinx.util.typing.ForwardRef"], module: str, ) -> object: """ Resolve a forward reference. This is not part of the public API. :param fr: :param module: The module the forward reference was defined in. :return: The evaluated object. """ module_dict = sys.modules[module].__dict__ if sys.version_info < (3, 9): return fr._evaluate(module_dict, module_dict) elif sys.version_info >= (3, 12): return fr._evaluate(module_dict, module_dict, set(), recursive_guard=set()) else: return fr._evaluate(module_dict, module_dict, set()) sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/typevars.py000066400000000000000000000251721475757360300245360ustar00rootroot00000000000000#!/usr/bin/env python3 # # typevars.py r""" Documenter for module level :class:`typing.TypeVar`\'s, similar to Sphinx's ``autotypevar`` but with a different appearance. .. versionadded:: 1.3.0 .. extensions:: sphinx_toolbox.more_autodoc.typevars .. latex:vspace:: -15px Configuration ------------- .. confval:: all_typevars :type: :class:`bool` :default: False Document all :class:`typing.TypeVar`\s, even if they have no docstring. .. confval:: no_unbound_typevars :type: :class:`bool` :default: True Only document :class:`typing.TypeVar`\s that have a constraint of are bound. This option has no effect if :confval:`all_typevars` is False. .. latex:clearpage:: Usage ---------- .. rst:directive:: autotypevar Directive to automatically document a :class:`typing.TypeVar`. The output is based on the :rst:dir:`autodata` directive, and takes all of its options plus these additional ones: .. rst:directive:option:: no-value Don't show the value of the variable. .. rst:directive:option:: value: value :type: string Show this instead of the value taken from the Python source code. .. rst:directive:option:: no-type Don't show the type of the variable. API Reference ---------------- """ # noqa: D400 # # Copyright Β© 2020-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. # # Parts based on https://github.com/sphinx-doc/sphinx # | Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). # | BSD Licensed # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib import sys from typing import Any, Dict, ForwardRef, List, Optional, Tuple, Type, TypeVar, Union # 3rd party from docutils.statemachine import StringList from domdf_python_tools.words import word_join from sphinx.application import Sphinx from typing_extensions import Protocol # this package from sphinx_toolbox._data_documenter import DataDocumenter from sphinx_toolbox.config import ToolboxConfig from sphinx_toolbox.more_autodoc import _documenter_add_content from sphinx_toolbox.more_autodoc.typehints import format_annotation from sphinx_toolbox.more_autodoc.variables import VariableDocumenter from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ( "TypeVarDocumenter", "unskip_typevars", "setup", ) class TypeVarDocumenter(VariableDocumenter): r""" Alternative version of :class:`sphinx.ext.autodoc.TypeVarDocumenter` with better type hint rendering. Specialized Documenter subclass for :class:`typing.TypeVar`\s. """ # noqa: D400 objtype = "typevar" directivetype = "data" priority = DataDocumenter.priority + 1 @classmethod def can_document_member( cls, member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: """ Called to see if a member can be documented by this documenter. :param member: The member being checked. :param membername: The name of the member. :param isattr: :param parent: The parent of the member. """ return isinstance(member, TypeVar) def resolve_type(self, forward_ref: "ForwardRef") -> Type: """ Resolve a :class:`typing.ForwardRef` using the module the :class:`~typing.TypeVar` belongs to. :param forward_ref: """ if forward_ref.__forward_evaluated__: return forward_ref.__forward_value__ # type: ignore[return-value] else: globanls = sys.modules[self.object.__module__].__dict__ eval_ = eval return eval_(forward_ref.__forward_code__, globanls, globanls) def add_content(self, more_content: Optional[StringList], no_docstring: bool = False) -> None: """ Add content from docstrings, attribute documentation and user. :param more_content: :param no_docstring: """ obj: _TypeVar = self.object sourcename = self.get_sourcename() constraints = [self.resolve_type(c) if isinstance(c, ForwardRef) else c for c in obj.__constraints__] description = [] bound_to: Optional[Type] if isinstance(obj.__bound__, ForwardRef): bound_to = self.resolve_type(obj.__bound__) else: bound_to = obj.__bound__ if obj.__covariant__: description.append("Covariant") elif obj.__contravariant__: description.append("Contravariant") else: description.append("Invariant") description.append(":class:`~typing.TypeVar`") if constraints: description.append("constrained to") description.append(word_join(format_annotation(c, fully_qualified=True) for c in constraints)) elif bound_to: description.append("bound to") description.append(format_annotation(bound_to, fully_qualified=True)) # if self.analyzer: # attr_docs = self.analyzer.find_attr_docs() # if self.objpath: # key = ('.'.join(self.objpath[:-1]), self.objpath[-1]) # if key in attr_docs: # return self.add_line('', sourcename) self.add_line(' '.join(description).rstrip() + '.', sourcename) # " " + self.add_line('', sourcename) _documenter_add_content(self, more_content, no_docstring) def add_directive_header(self, sig: str) -> None: """ Add the directive's header. :param sig: """ obj: _TypeVar = self.object sourcename = self.get_sourcename() constraints = [self.resolve_type(c) if isinstance(c, ForwardRef) else c for c in obj.__constraints__] sig_elements = [obj.__name__, *(c.__name__ for c in constraints)] bound_to: Optional[Type] if isinstance(obj.__bound__, ForwardRef): bound_to = self.resolve_type(obj.__bound__) else: bound_to = obj.__bound__ if bound_to is not None: try: sig_elements.append(f"bound={bound_to.__name__}") except AttributeError: sig_elements.append(f"bound={repr(bound_to)}") if obj.__covariant__: sig_elements.append(f"covariant=True") elif obj.__contravariant__: sig_elements.append(f"contravariant=True") self.options["value"] = f"TypeVar({', '.join(sig_elements)})" self.add_line('', sourcename) super().add_directive_header(sig) def get_doc( self, encoding: Optional[str] = None, ignore: Optional[int] = None, ) -> List[List[str]]: """ Decode and return lines of the docstring(s) for the object. :param encoding: :param ignore: :rtype: .. latex:clearpage:: """ if self.object.__doc__ != TypeVar.__doc__: return super().get_doc() or [] else: return [] def validate_config(app: Sphinx, config: ToolboxConfig) -> None: r""" Validate the provided configuration values. See :class:`~sphinx_toolbox.config.ToolboxConfig` for a list of the configuration values. :param app: The Sphinx application. :param config: :type config: :class:`~sphinx.config.Config` """ if config.all_typevars: app.connect("autodoc-skip-member", unskip_typevars) def unskip_typevars( app: Sphinx, what: str, name: str, obj: Any, skip: bool, options: Dict[str, Any], ) -> Optional[bool]: r""" Unskip undocumented :class:`typing.TypeVar`\s if :confval:`all_typevars` is :py:obj:`True`. :param app: The Sphinx application. :param what: The type of the object which the docstring belongs to (one of ``'module'``, ``'class'``, ``'exception'``, ``'function'``, ``'method'``, ``'attribute'``). :param name: The fully qualified name of the object. :param obj: The object itself. :param skip: A boolean indicating if autodoc will skip this member if the user handler does not override the decision. :param options: The options given to the directive: an object with attributes ``inherited_members``, ``undoc_members``, ``show_inheritance`` and ``noindex`` that are true if the flag option of same name was given to the auto directive. """ assert app.env is not None if isinstance(obj, TypeVar): if app.env.config.no_unbound_typevars: if obj.__bound__ or obj.__constraints__: return False else: return True else: return False return None @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc.typevars`. :param app: The Sphinx application. """ app.setup_extension("sphinx.ext.autodoc") app.add_autodocumenter(TypeVarDocumenter, override=True) app.add_config_value("all_typevars", False, "env", types=[bool]) app.add_config_value("no_unbound_typevars", True, "env", types=[bool]) app.connect("config-inited", validate_config, priority=850) return {"parallel_read_safe": True} class _TypeVar(Protocol): __constraints__: Tuple[Union[Type, ForwardRef], ...] __bound__: Union[Type, ForwardRef, None] __covariant__: bool __contravariant__: bool __name__: str sphinx-toolbox-3.9.0/sphinx_toolbox/more_autodoc/variables.py000066400000000000000000000544661475757360300246410ustar00rootroot00000000000000#!/usr/bin/env python3 # # variables.py r""" Documenter for module level variables, similar to :rst:dir:`autodata` but with a different appearance and more customisation options. .. versionadded:: 0.6.0 .. extensions:: sphinx_toolbox.more_autodoc.variables .. versionchanged:: 0.7.0 Added ``*AttributeDocumenter``\s .. versionchanged:: 1.1.0 Added :class:`~.SlotsAttributeDocumenter` .. versionchanged:: 3.7.0 Added :class:`~.PropertyDocumenter` Usage ---------- .. rst:directive:: autovariable Directive to automatically document a variable. The output is based on the :rst:dir:`autodata` directive, and takes all of its options, plus these additional ones: .. rst:directive:option:: no-value Don't show the value of the variable. .. rst:directive:option:: value: value :type: string Show this instead of the value taken from the Python source code. .. rst:directive:option:: no-type Don't show the type of the variable. .. rst:directive:option:: type: type :type: string Show this instead of the type taken from the Python source code. An example of the output cen be seen below for :py:obj:`~.type_template`. API Reference ---------------- """ # noqa: D400 # # Copyright Β© 2020-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. # # Parts based on https://github.com/sphinx-doc/sphinx # | Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). # | BSD Licensed # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib import importlib import sys import warnings from contextlib import suppress from typing import Any, List, Optional, Type, cast, get_type_hints # 3rd party import sphinx from docutils.statemachine import StringList from sphinx.application import Sphinx from sphinx.errors import PycodeError from sphinx.ext.autodoc import ( INSTANCEATTR, SLOTSATTR, UNINITIALIZED_ATTR, ClassDocumenter, ClassLevelDocumenter, DocstringStripSignatureMixin, Documenter, ModuleDocumenter, ModuleLevelDocumenter, Options, annotation_option, import_object, logger, mock ) from sphinx.ext.autodoc.directive import DocumenterBridge from sphinx.pycode import ModuleAnalyzer from sphinx.util import inspect from sphinx.util.inspect import ForwardRef, getdoc, object_description, safe_getattr # this package from sphinx_toolbox._data_documenter import DataDocumenter from sphinx_toolbox.more_autodoc import _documenter_add_content from sphinx_toolbox.more_autodoc.typehints import _resolve_forwardref, format_annotation from sphinx_toolbox.utils import ( RemovedInSphinx50Warning, SphinxExtMetadata, add_nbsp_substitution, flag, metadata_add_version, prepare_docstring ) __all__ = ( "VariableDocumenter", "TypedAttributeDocumenter", "InstanceAttributeDocumenter", "PropertyDocumenter", "SlotsAttributeDocumenter", "type_template", "old_type_template", "get_variable_type", "setup", ) def get_variable_type(documenter: Documenter) -> str: """ Returns the formatted type annotation for a variable. :param documenter: """ try: annotations = get_type_hints(documenter.parent) except NameError: # Failed to evaluate ForwardRef (maybe TYPE_CHECKING) annotations = safe_getattr(documenter.parent, "__annotations__", {}) except (TypeError, KeyError, AttributeError): # KeyError: a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084) # AttributeError is raised on 3.5.2 (fixed by 3.5.3) annotations = {} if documenter.objpath[-1] in annotations: ann = annotations.get(documenter.objpath[-1]) if isinstance(ann, str): return format_annotation(ann.strip("'\"")) return format_annotation(ann) else: # Instance attribute key = ('.'.join(documenter.objpath[:-1]), documenter.objpath[-1]) if documenter.analyzer and key in documenter.analyzer.annotations: # Forward references will have quotes annotation: str = documenter.analyzer.annotations[key].strip("'\"") try: module_dict = sys.modules[documenter.parent.__module__].__dict__ if annotation.isidentifier() and annotation in module_dict: return format_annotation(module_dict[annotation]) else: ref = ForwardRef(annotation) evaled = _resolve_forwardref(ref, documenter.parent.__module__) return format_annotation(evaled) except (NameError, TypeError, ValueError, AttributeError): return annotation else: return '' old_type_template = " **Type:** |nbsp| |nbsp| |nbsp| |nbsp| %s" """ Old template for rendering type annotations in :class:`~.VariableDocumenter`, :class:`~.TypedAttributeDocumenter` and :class:`~.InstanceAttributeDocumenter`. Renders like: **Type:** |nbsp| |nbsp| |nbsp| |nbsp| :class:`str` .. note:: Be sure to call :func:~.add_nbsp_substitution` in the ``setup`` function of any extensions using this template. """ type_template = " **Type:**Β Β Β Β %s" """ Template for rendering type annotations in :class:`~.VariableDocumenter`, :class:`~.TypedAttributeDocumenter` and :class:`~.InstanceAttributeDocumenter`. Renders like: **Type:** \u00A0\u00A0\u00A0\u00A0 :class:`str` .. versionchanged:: 3.4.0 This template now uses the unicode codepoint for a non-breaking space, rather than the ReStructuredText substitution used in 3.3.0 and earlier. The old template is available as ``old_type_template``. """ class VariableDocumenter(DataDocumenter): """ Specialized Documenter subclass for data items. """ directivetype = "data" objtype = "variable" # keeps it below TypeVarDocumenter priority: float = DataDocumenter.priority + 0.5 # type: ignore[assignment] option_spec = { "no-value": flag, "no-type": flag, "type": str, "value": str, **DataDocumenter.option_spec, } def __init__(self, directive: DocumenterBridge, name: str, indent: str = '') -> None: super().__init__(directive=directive, name=name, indent=indent) self.options = Options(self.options.copy()) add_nbsp_substitution(self.env.app.config) def add_directive_header(self, sig: str) -> None: """ Add the directive's header. :param sig: """ sourcename = self.get_sourcename() no_value = self.options.get("no-value", False) no_type = self.options.get("no-type", False) if not self.options.get("annotation", ''): ModuleLevelDocumenter.add_directive_header(self, sig) if not no_value: if "value" in self.options: self.add_line(f" :value: {self.options['value']}", sourcename) else: with suppress(ValueError): if self.object is not UNINITIALIZED_ATTR: objrepr = object_description(self.object) self.add_line(f" :value: {objrepr}", sourcename) self.add_line('', sourcename) if not no_type: if "type" in self.options: the_type = self.options["type"] else: # obtain type annotation for this data the_type = get_variable_type(self) if not the_type.strip(): obj_type = type(self.object) if obj_type is object: return try: the_type = format_annotation(obj_type) except Exception: return line = type_template % the_type self.add_line(line, sourcename) else: super().add_directive_header(sig) class TypedAttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore[misc] """ Alternative version of :class:`autodoc.AttributeDocumenter ` with better type hint rendering. Specialized Documenter subclass for attributes. .. versionadded:: 0.7.0 .. versionchanged:: 1.0.0 Now uses the type of the variable if it is not explicitly annotated. """ # noqa: D400 objtype = "attribute" member_order = 60 option_spec = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option # must be higher than the MethodDocumenter, else it will recognize # some non-data descriptors as methods priority = 10 def __init__(self, directive: DocumenterBridge, name: str, indent: str = '') -> None: super().__init__(directive=directive, name=name, indent=indent) self.options = Options(self.options.copy()) self._datadescriptor = True @staticmethod def is_function_or_method(obj: Any) -> bool: # noqa: D102 return inspect.isfunction(obj) or inspect.isbuiltin(obj) or inspect.ismethod(obj) @classmethod def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any) -> bool: """ Called to see if a member can be documented by this documenter. """ if inspect.isattributedescriptor(member): return True elif ( not isinstance(parent, ModuleDocumenter) and not inspect.isroutine(member) and not isinstance(member, type) ): return True else: return False def document_members(self, all_members: bool = False) -> None: # noqa: D102 pass def isinstanceattribute(self) -> bool: """ Check the subject is an instance attribute. """ try: analyzer = ModuleAnalyzer.for_module(self.modname) attr_docs = analyzer.find_attr_docs() if self.objpath: key = ('.'.join(self.objpath[:-1]), self.objpath[-1]) if key in attr_docs: return True return False except PycodeError: return False def import_object(self, raiseerror: bool = False) -> bool: """ Import the object given by *self.modname* and *self.objpath* and set it as ``self.object``. :returns: :py:obj:`True` if successful, :py:obj:`False` if an error occurred. """ try: ret = super().import_object(raiseerror=True) if inspect.isenumattribute(self.object): self.object = self.object.value if inspect.isattributedescriptor(self.object): self._datadescriptor = True else: # if it's not a data descriptor self._datadescriptor = False except ImportError as exc: if self.isinstanceattribute(): self.object = INSTANCEATTR self._datadescriptor = False ret = True elif raiseerror: raise else: logger.warning(exc.args[0], type="autodoc", subtype="import_object") self.env.note_reread() ret = False return ret def get_real_modname(self) -> str: """ Get the real module name of an object to document. It can differ from the name of the module through which the object was imported. """ return self.get_attr(self.parent or self.object, "__module__", None) or self.modname def add_directive_header(self, sig: str) -> None: """ Add the directive's header. :param sig: """ sourcename = self.get_sourcename() no_value = self.options.get("no-value", False) no_type = self.options.get("no-type", False) if not self.options.get("annotation", ''): ClassLevelDocumenter.add_directive_header(self, sig) # data descriptors do not have useful values if not no_value and not self._datadescriptor: if "value" in self.options: self.add_line(" :value: " + self.options["value"], sourcename) else: with suppress(ValueError): if self.object is not INSTANCEATTR: objrepr = object_description(self.object) self.add_line(" :value: " + objrepr, sourcename) self.add_line('', sourcename) if not no_type: if "type" in self.options: self.add_line(type_template % self.options["type"], sourcename) else: # obtain type annotation for this attribute the_type = get_variable_type(self) if not the_type.strip(): obj_type = type(self.object) if obj_type is object: return try: the_type = format_annotation(obj_type) except Exception: return line = type_template % the_type self.add_line(line, sourcename) else: super().add_directive_header(sig) def get_doc( self, encoding: Optional[str] = None, ignore: Optional[int] = None, ) -> List[List[str]]: """ Decode and return lines of the docstring(s) for the object. :param encoding: :param ignore: """ # Disable `autodoc_inherit_docstring` temporarily to avoid to obtain # a docstring from the value which descriptor returns unexpectedly. # ref: https://github.com/sphinx-doc/sphinx/issues/7805 orig = self.env.config.autodoc_inherit_docstrings try: self.env.config.autodoc_inherit_docstrings = False # type: ignore[attr-defined] # Sphinx's signature is wrong wrt Optional if sphinx.version_info >= (4, 0): if encoding is not None: raise TypeError("The 'encoding' argument to get_doc was removed in Sphinx 4") if self._new_docstrings is not None: return self._new_docstrings or [] docstring = getdoc( self.object, self.get_attr, self.config.autodoc_inherit_docstrings, self.parent, self.object_name, ) if docstring: tab_width = self.directive.state.document.settings.tab_width return [prepare_docstring(docstring, ignore, tab_width)] return [] else: enc = cast(str, encoding) ign = cast(int, ignore) super_get_doc = super().get_doc(enc, ign) # type: ignore[call-arg] return super_get_doc or [] finally: self.env.config.autodoc_inherit_docstrings = orig # type: ignore[attr-defined] def add_content(self, more_content: Optional[StringList], no_docstring: bool = False) -> None: """ Add content from docstrings, attribute documentation and user. """ if not self._datadescriptor: # if it's not a data descriptor, its docstring is very probably the wrong thing to display no_docstring = True _documenter_add_content(self, more_content, no_docstring) class InstanceAttributeDocumenter(TypedAttributeDocumenter): """ Alternative version of :class:`autodoc.InstanceAttributeDocumenter ` with better type hint rendering. Specialized Documenter subclass for attributes that cannot be imported because they are instance attributes (e.g. assigned in ``__init__``). .. versionadded:: 0.7.0 .. versionchanged:: 1.0.0 Now uses the type of the variable if it is not explicitly annotated. """ # noqa: D400 objtype = "instanceattribute" directivetype = "attribute" member_order = 60 # must be higher than TypedAttributeDocumenter priority = 11 @classmethod def can_document_member( cls, member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: """ Called to see if a member can be documented by this documenter. This documenter only documents INSTANCEATTR members. :param member: The member being checked. :param membername: The name of the member. :param isattr: :param parent: The parent of the member. """ return not isinstance(parent, ModuleDocumenter) and isattr and member is INSTANCEATTR def import_parent(self) -> Any: """ Import and return the attribute's parent. """ try: parent = importlib.import_module(self.modname) for name in self.objpath[:-1]: parent = self.get_attr(parent, name) return parent except (ImportError, AttributeError): return None def import_object(self, raiseerror: bool = False) -> bool: """ Never import anything. :param raiseerror: """ # disguise as an attribute self.objtype = "attribute" self.object = INSTANCEATTR self.parent = self.import_parent() self._datadescriptor = False return True def add_content(self, more_content: Any, no_docstring: bool = False) -> None: """ Never try to get a docstring from the object. """ super().add_content(more_content, no_docstring=True) class SlotsAttributeDocumenter(TypedAttributeDocumenter): r""" Alternative version of :class:`autodoc.InstanceAttributeDocumenter ` with better type hint rendering. Specialized Documenter subclass for attributes that cannot be imported because they are attributes in __slots__. .. versionadded:: 1.1.0 .. latex:vspace:: 10px """ # noqa: D400 objtype = "slotsattribute" directivetype = "attribute" member_order = 60 # must be higher than AttributeDocumenter priority = 11 @classmethod def can_document_member( cls, member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: """ Called to see if a member can be documented by this documenter. This documenter only documents SLOTSATTR members. :param member: The member being checked. :param membername: The name of the member. :param isattr: :param parent: The parent of the member. """ return member is SLOTSATTR def import_object(self, raiseerror: bool = False) -> bool: """ Never import anything. :param raiseerror: """ # disguise as an attribute self.objtype = "attribute" self._datadescriptor = True with mock(self.env.config.autodoc_mock_imports): try: if sphinx.version_info >= (8, 1): kwargs = {} else: kwargs = {"warningiserror": self.env.config.autodoc_warningiserror} ret = import_object( self.modname, self.objpath[:-1], "class", attrgetter=self.get_attr, **kwargs, ) self.module, _, _, self.parent = ret return True except ImportError as exc: if raiseerror: raise else: logger.warning(exc.args[0], type="autodoc", subtype="import_object") self.env.note_reread() return False def get_doc( self, encoding: Optional[str] = None, ignore: Optional[int] = None, ) -> List[List[str]]: """ Decode and return lines of the docstring(s) for the object. :param encoding: :param ignore: """ if sphinx.version_info >= (4, 0): if encoding is not None: raise TypeError("The 'encoding' argument to get_doc was removed in Sphinx 4") if ignore is not None: if sphinx.version_info >= (5, 0): raise TypeError("The 'ignore' argument to get_doc was removed in Sphinx 5") else: warnings.warn( "The 'ignore' argument to get_doc() is deprecated.", RemovedInSphinx50Warning, # type: ignore[arg-type] stacklevel=2 ) name = self.objpath[-1] __slots__ = safe_getattr(self.parent, "__slots__", []) if isinstance(__slots__, dict) and isinstance(__slots__.get(name), str): return [prepare_docstring(__slots__[name])] else: return [] class PropertyDocumenter(TypedAttributeDocumenter): """ Specialized Documenter subclass for properties. .. versionadded:: 3.7.0 """ objtype = "property" member_order = 60 priority = 11 if sphinx.version_info[0] < 4: directivetype = "method" @classmethod def can_document_member( cls: Type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: """ Called to see if a member can be documented by this documenter. This documenter only documents SLOTSATTR members. :param member: The member being checked. :param membername: The name of the member. :param isattr: :param parent: The parent of the member. """ return inspect.isproperty(member) and isinstance(parent, ClassDocumenter) def add_directive_header(self, sig: str) -> None: """ Add the directive's header. :param sig: """ sourcename = self.get_sourcename() no_value = self.options.get("no-value", False) ClassLevelDocumenter.add_directive_header(self, sig) if inspect.isabstractmethod(self.object): self.add_line(" :abstractmethod:", sourcename) if sphinx.version_info[0] < 4: self.add_line(" :property:", self.get_sourcename()) if not self.options.get("annotation", ''): # data descriptors do not have useful values if not no_value and not self._datadescriptor: if "value" in self.options: self.add_line(" :value: " + self.options["value"], sourcename) else: with suppress(ValueError): if self.object is not INSTANCEATTR: objrepr = object_description(self.object) self.add_line(" :value: " + objrepr, sourcename) self.add_line('', sourcename) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autodoc.variables`. :param app: The Sphinx application. """ app.setup_extension("sphinx.ext.autodoc") app.setup_extension("sphinx_toolbox.more_autosummary") app.add_autodocumenter(VariableDocumenter) app.add_autodocumenter(TypedAttributeDocumenter, override=True) app.add_autodocumenter(InstanceAttributeDocumenter, override=True) app.add_autodocumenter(SlotsAttributeDocumenter, override=True) app.add_autodocumenter(PropertyDocumenter, override=True) app.connect("config-inited", lambda _, config: add_nbsp_substitution(config)) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/more_autosummary/000077500000000000000000000000001475757360300232305ustar00rootroot00000000000000sphinx-toolbox-3.9.0/sphinx_toolbox/more_autosummary/__init__.py000066400000000000000000000605371475757360300253540ustar00rootroot00000000000000#!/usr/bin/env python3 # # __init__.py r""" Extensions to :mod:`sphinx.ext.autosummary`. Provides an enhanced version of https://autodocsumm.readthedocs.io/ which respects the autodoc ``member-order`` option. This can be given for an individual directive, in the `autodoc_member_order `_ configuration value, or via :confval:`autodocsumm_member_order`. Also patches :class:`sphinx.ext.autosummary.Autosummary` to fix an issue where the module name is sometimes duplicated. I.e. ``foo.bar.baz()`` became ``foo.bar.foo.bar.baz()``, which of course doesn't exist and created a broken link. .. versionadded:: 0.7.0 .. versionchanged:: 1.3.0 Autosummary now selects the appropriate documenter for attributes rather than falling back to :class:`~sphinx.ext.autodoc.DataDocumenter`. .. versionchanged:: 2.13.0 Also patches :class:`sphinx.ext.autodoc.ModuleDocumenter` to fix an issue where ``__all__`` is not respected for autosummary tables. Configuration -------------- .. latex:vspace:: -20px .. confval:: autodocsumm_member_order :type: :py:obj:`str` :default: ``'alphabetical'`` Determines the sort order of members in ``autodocsumm`` summary tables. Valid values are ``'alphabetical'`` and ``'bysource'``. Note that for ``'bysource'`` the module must be a Python module with the source code available. The member order can also be set on a per-directive basis using the ``:member-order: [order]`` option. This applies not only to :rst:dir:`automodule` etc. directives, but also to :rst:dir:`automodulesumm` etc. directives. .. confval:: autosummary_col_type :type: :py:obj:`str` :default: ``'\X'`` The LaTeX column type to use for autosummary tables. Custom columns can be defined in the LaTeX preamble for use with this option. For example: .. code-block:: python latex_elements["preamble"] = r''' \makeatletter \newcolumntype{\Xx}[2]{>{\raggedright\arraybackslash}p{\dimexpr (\linewidth-\arrayrulewidth)*#1/#2-\tw@\tabcolsep-\arrayrulewidth\relax}} \makeatother ''' autosummary_col_type = "\\Xx" .. versionadded:: 2.13.0 API Reference ---------------- """ # # Copyright Β© 2020-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. # # Parts based on https://github.com/sphinx-doc/sphinx # | Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). # | BSD Licensed # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Builds on top of, and PatchedAutoDocSummDirective based on, https://github.com/Chilipp/autodocsumm # | Copyright 2016-2019, Philipp S. Sommer # | Copyright 2020-2021, Helmholtz-Zentrum Hereon # | # | Licensed under the Apache License, Version 2.0 (the "License"); # | you may not use this file except in compliance with the License. # | You may obtain a copy of the License at # | # | http://www.apache.org/licenses/LICENSE-2.0 # | # | Unless required by applicable law or agreed to in writing, # | software distributed under the License is distributed on an "AS IS" BASIS, # | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # | See the License for the specific language governing permissions and limitations under the License. # # stdlib import inspect import operator import re from typing import Any, Dict, List, Optional, Tuple, Type # 3rd party import autodocsumm # type: ignore[import-untyped] import docutils import sphinx from docutils import nodes from domdf_python_tools.stringlist import StringList from sphinx import addnodes from sphinx.application import Sphinx from sphinx.config import ENUM from sphinx.ext.autodoc import ( ALL, INSTANCEATTR, ClassDocumenter, Documenter, ModuleDocumenter, logger, special_member_re ) from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options from sphinx.ext.autosummary import Autosummary, FakeDirective, autosummary_table from sphinx.locale import __ from sphinx.util.inspect import getdoc, safe_getattr # this package from sphinx_toolbox._data_documenter import DataDocumenter from sphinx_toolbox.more_autodoc import ObjectMembers from sphinx_toolbox.utils import SphinxExtMetadata, allow_subclass_add, get_first_matching, metadata_add_version if sphinx.version_info > (4, 1): # 3rd party from sphinx.util.docstrings import separate_metadata else: # 3rd party from sphinx.util.docstrings import extract_metadata # type: ignore[attr-defined] __all__ = ( "PatchedAutosummary", "PatchedAutoSummModuleDocumenter", "PatchedAutoSummClassDocumenter", "get_documenter", "setup", ) if sphinx.version_info < (3, 5): # 3rd party from sphinx.ext.autodoc.importer import get_module_members as _get_module_members # type: ignore[attr-defined] else: # pragma: no cover # 3rd party from sphinx.util.inspect import getannotations def _get_module_members(module: Any) -> List[Tuple[str, Any]]: """Get members of target module.""" # 3rd party from sphinx.ext.autodoc import INSTANCEATTR members: Dict[str, Tuple[str, Any]] = {} for name in dir(module): try: value = safe_getattr(module, name, None) members[name] = (name, value) except AttributeError: continue # annotation only member (ex. attr: int) for name in getannotations(module): if name not in members: members[name] = (name, INSTANCEATTR) return sorted(list(members.values())) def add_autosummary(self, relative_ref_paths: bool = False) -> None: """ Add the :rst:dir:`autosummary` table of this documenter. :param relative_ref_paths: Use paths relative to the current module instead of absolute import paths for each object. """ if not self.options.get("autosummary", False): return content = StringList() content.indent_type = ' ' * 4 sourcename = self.get_sourcename() grouped_documenters = self.get_grouped_documenters() if not self.options.get("autosummary-no-titles", False) and grouped_documenters: content.blankline() content.append(".. latex:vspace:: 10px") content.blankline() member_order = get_first_matching( lambda x: x != "groupwise", ( self.options.get("member-order", ''), self.env.config.autodocsumm_member_order, self.env.config.autodoc_member_order, ), default="alphabetical", ) for section, documenters in grouped_documenters.items(): if not self.options.get("autosummary-no-titles", False): content.append(f"**{section}:**") content.blankline() content.append(".. latex:vspace:: -5px") content.blankline(ensure_single=True) # TODO transform to make caption associated with table in LaTeX content.append(".. autosummary::") if self.options.autosummary_nosignatures: content.append(" :nosignatures:") content.blankline(ensure_single=True) with content.with_indent_size(content.indent_size + 1): for documenter, _ in self.sort_members(documenters, member_order): obj_ref_path = documenter.fullname # if relative_ref_paths: # modname = self.modname + '.' # if documenter.fullname.startswith(modname): # obj_ref_path = documenter.fullname[len(modname):] content.append(f"~{obj_ref_path}") content.blankline() for line in content: self.add_line(line, sourcename) class PatchedAutosummary(Autosummary): """ Pretty table containing short signatures and summaries of functions etc. Patched version of :class:`sphinx.ext.autosummary.Autosummary` to fix an issue where the module name is sometimes duplicated. I.e. ``foo.bar.baz()`` became ``foo.bar.foo.bar.baz()``, which of course doesn't exist and created a broken link. .. versionadded:: 0.5.1 .. versionchanged:: 0.7.0 Moved from :mod:`sphinx_toolbox.patched_autosummary`. .. versionchanged:: 2.13.0 Added support for customising the column type with the :confval:`autosummary_col_type` option. """ def import_by_name(self, name: str, prefixes: List[Optional[str]]) -> Tuple[str, Any, Any, str]: """ Import the object with the give name. :param name: :param prefixes: :return: The real name of the object, the object, the parent of the object, and the name of the module. """ real_name, obj, parent, modname = super().import_by_name(name=name, prefixes=prefixes) real_name = re.sub(rf"((?:{modname}\.)+)", f"{modname}.", real_name) return real_name, obj, parent, modname if sphinx.version_info >= (8, 2): def create_documenter( # type: ignore[override] self, obj: Any, parent: Any, full_name: str, *, registry: Any = None, ) -> Documenter: """ Get an :class:`autodoc.Documenter` class suitable for documenting the given object. :param app: The Sphinx application. :param obj: The object being documented. :param parent: The parent of the object (e.g. a module or a class). :param full_name: The full name of the object. :param registry: .. versionchanged:: 1.3.0 Now selects the appropriate documenter for attributes rather than falling back to :class:`~sphinx.ext.autodoc.DataDocumenter`. .. versionchanged:: 3.9.0 Function arguments now mirror those from Sphinx 8.2 or older versions depending on the Sphinx version installed. """ # 3rd party from sphinx.ext.autosummary import _get_documenter # type: ignore[attr-defined] doccls = _get_documenter(obj, parent, registry=registry) return doccls(self.bridge, full_name) else: def create_documenter( # type: ignore[misc] self, app: Sphinx, obj: Any, parent: Any, full_name: str, ) -> Documenter: """ Get an :class:`autodoc.Documenter` class suitable for documenting the given object. :param app: The Sphinx application. :param obj: The object being documented. :param parent: The parent of the object (e.g. a module or a class). :param full_name: The full name of the object. .. versionchanged:: 1.3.0 Now selects the appropriate documenter for attributes rather than falling back to :class:`~sphinx.ext.autodoc.DataDocumenter`. .. versionchanged:: 3.9.0 Function arguments now mirror those from Sphinx 8.2 or older versions depending on the Sphinx version installed. """ doccls = get_documenter(app, obj, parent) return doccls(self.bridge, full_name) def get_table(self, items: List[Tuple[str, str, str, str]]) -> List[nodes.Node]: """ Generate a list of table nodes for the :rst:dir:`autosummary` directive. :param items: A list produced by ``self.get_items``. :rtype: .. latex:clearpage:: """ table_spec, table, *other_nodes = super().get_table(items) assert isinstance(table_spec, addnodes.tabular_col_spec) assert isinstance(table, autosummary_table) if docutils.__version_info__ >= (0, 18): table.children[0]["classes"] += ["colwidths-given"] # type: ignore[index] column_type = getattr(self.env.config, "autosummary_col_type", r"\X") table_spec["spec"] = f'{column_type}{{1}}{{2}}{column_type}{{1}}{{2}}' return [table_spec, table, *other_nodes] def get_documenter(app: Sphinx, obj: Any, parent: Any) -> Type[Documenter]: """ Returns an :class:`autodoc.Documenter` class suitable for documenting the given object. .. versionadded:: 1.3.0 :param app: The Sphinx application. :param obj: The object being documented. :param parent: The parent of the object (e.g. a module or a class). """ if inspect.ismodule(obj): # ModuleDocumenter.can_document_member always returns False return ModuleDocumenter # Construct a fake documenter for *parent* if parent is not None: parent_doc_cls = get_documenter(app, parent, None) else: parent_doc_cls = ModuleDocumenter if hasattr(parent, "__name__"): parent_doc = parent_doc_cls(FakeDirective(), parent.__name__) else: parent_doc = parent_doc_cls(FakeDirective(), '') # Get the correct documenter class for *obj* classes = [ cls for cls in app.registry.documenters.values() if cls.can_document_member(obj, '', False, parent_doc) ] data_doc_classes = [ cls for cls in app.registry.documenters.values() if cls.can_document_member(obj, '', True, parent_doc) ] if classes: classes.sort(key=lambda cls: cls.priority) return classes[-1] elif data_doc_classes: data_doc_classes.sort(key=lambda cls: cls.priority) return data_doc_classes[-1] else: return DataDocumenter class PatchedAutoSummModuleDocumenter(autodocsumm.AutoSummModuleDocumenter): """ Patched version of :class:`autodocsumm.AutoSummClassDocumenter` which works around a bug in Sphinx 3.4 and above where ``__all__`` is not respected. .. versionadded:: 2.13.0 """ # noqa: D400 def filter_members(self, members: ObjectMembers, want_all: bool) -> List[Tuple[str, Any, bool]]: """ Filter the given member list. Members are skipped if: * they are private (except if given explicitly or the ``private-members`` option is set) * they are special methods (except if given explicitly or the ``special-members`` option is set) * they are undocumented (except if the ``undoc-members`` option is set) The user can override the skipping decision by connecting to the :event:`autodoc-skip-member` event. """ def is_filtered_inherited_member(name: str) -> bool: if inspect.isclass(self.object): for cls in self.object.__mro__: if cls.__name__ == self.options.inherited_members and cls != self.object: # given member is a member of specified *super class* return True elif name in cls.__dict__: return False elif name in self.get_attr(cls, "__annotations__", {}): return False return False ret = [] # search for members in source code too namespace = '.'.join(self.objpath) # will be empty for modules if self.analyzer: attr_docs = self.analyzer.find_attr_docs() else: attr_docs = {} doc: Optional[str] sphinx_gt_41 = sphinx.version_info > (4, 1) # process members and determine which to skip for (membername, member) in members: # if isattr is True, the member is documented as an attribute isattr = (member is INSTANCEATTR or (namespace, membername) in attr_docs) doc = getdoc( member, self.get_attr, self.env.config.autodoc_inherit_docstrings, self.parent, self.object_name ) if not isinstance(doc, str): # Ignore non-string __doc__ doc = None # if the member __doc__ is the same as self's __doc__, it's just # inherited and therefore not the member's doc cls = self.get_attr(member, "__class__", None) if cls: cls_doc = self.get_attr(cls, "__doc__", None) if cls_doc == doc: doc = None if sphinx_gt_41: doc, metadata = separate_metadata(doc) # type: ignore[arg-type] else: metadata = extract_metadata(doc) has_doc = bool(doc) if "private" in metadata: # consider a member private if docstring has "private" metadata isprivate = True elif "public" in metadata: # consider a member public if docstring has "public" metadata isprivate = False else: isprivate = membername.startswith('_') keep = False if safe_getattr(member, "__sphinx_mock__", False): # mocked module or object pass elif self.options.exclude_members and membername in self.options.exclude_members: # remove members given by exclude-members keep = False elif want_all and special_member_re.match(membername): # special __methods__ if self.options.special_members and membername in self.options.special_members: if membername == "__doc__": keep = False elif is_filtered_inherited_member(membername): keep = False else: keep = has_doc or self.options.undoc_members else: keep = False elif (namespace, membername) in attr_docs: if want_all and isprivate: if self.options.private_members is None: keep = False else: keep = membername in self.options.private_members else: # keep documented attributes keep = True isattr = True elif want_all and isprivate: if has_doc or self.options.undoc_members: if self.options.private_members is None: keep = False elif is_filtered_inherited_member(membername): keep = False else: keep = membername in self.options.private_members else: keep = False else: if self.options.members is ALL and is_filtered_inherited_member(membername): keep = False else: # ignore undocumented members if :undoc-members: is not given keep = has_doc or self.options.undoc_members # give the user a chance to decide whether this member # should be skipped if self.env.app: # let extensions preprocess docstrings try: # pylint: disable=R8203 skip_user = self.env.app.emit_firstresult( "autodoc-skip-member", self.objtype, membername, member, not keep, self.options, ) if skip_user is not None: keep = not skip_user except Exception as exc: msg = 'autodoc: failed to determine %r to be documented, the following exception was raised:\n%s' logger.warning(__(msg), member, exc, type="autodoc") keep = False if keep: ret.append((membername, member, isattr)) return ret def get_object_members(self, want_all: bool) -> Tuple[bool, ObjectMembers]: """ Return a tuple of ``(members_check_module, members)``, where ``members`` is a list of ``(membername, member)`` pairs of the members of ``self.object``. If ``want_all`` is :py:obj:`True`, return all members. Otherwise, only return those members given by ``self.options.members`` (which may also be none). """ # noqa: D400 if want_all: if self.__all__: memberlist = self.__all__ else: # for implicit module members, check __module__ to avoid # documenting imported objects return True, _get_module_members(self.object) else: memberlist = self.options.members or [] ret = [] for mname in memberlist: try: # pylint: disable=R8203 if sphinx.version_info >= (8, 0): # 3rd party from sphinx.ext.autodoc import ObjectMember ret.append(ObjectMember(mname, safe_getattr(self.object, mname))) else: ret.append((mname, safe_getattr(self.object, mname))) # type: ignore[arg-type] except AttributeError: # pylint: disable=dotted-import-in-loop) logger.warning( operator.mod( __("missing attribute mentioned in :members: or __all__: module %s, attribute %s"), (safe_getattr(self.object, "__name__", "???"), mname), ), type="autodoc" ) # pylint: enable=dotted-import-in-loop) return False, ret class PatchedAutoSummClassDocumenter(autodocsumm.AutoSummClassDocumenter): """ Patched version of :class:`autodocsumm.AutoSummClassDocumenter` which doesn't show summary tables for aliased objects. .. versionadded:: 0.9.0 """ # noqa: D400 def add_content(self, *args, **kwargs) -> None: """ Add content from docstrings, attribute documentation and user. """ ClassDocumenter.add_content(self, *args, **kwargs) if not self.doc_as_attr: self.add_autosummary() class PatchedAutoDocSummDirective(autodocsumm.AutoDocSummDirective): """ Patched ``AutoDocSummDirective`` which uses :py:obj:`None` for the members option rather than an empty string. .. attention:: This class is not part of the public API. """ def run(self) -> List[nodes.Node]: reporter = self.state.document.reporter if hasattr(reporter, "get_source_and_line"): _, lineno = reporter.get_source_and_line(self.lineno) else: _, lineno = (None, None) # look up target Documenter objtype = self.name[4:-4] # strip prefix (auto-) and suffix (-summ). doccls = self.env.app.registry.documenters[objtype] self.options["autosummary-force-inline"] = "True" self.options["autosummary"] = "True" if "no-members" not in self.options: self.options["members"] = None # process the options with the selected documenter's option_spec try: documenter_options = process_documenter_options(doccls, self.config, self.options) except (KeyError, ValueError, TypeError) as exc: # an option is either unknown or has a wrong type logger.error( "An option to %s is either unknown or has an invalid value: %s", self.name, exc, location=(self.env.docname, lineno), ) return [] # generate the output params = DocumenterBridge(self.env, reporter, documenter_options, lineno, self.state) documenter = doccls(params, self.arguments[0]) documenter.add_autosummary() node = nodes.paragraph() node.document = self.state.document self.state.nested_parse(params.result, 0, node) return node.children def _patch_filter_members() -> None: # 3rd party from sphinx.ext.autodoc import ObjectMember orig_filter_members = Documenter.filter_members def _documenter_filter_members( self, members: List[ObjectMember], want_all: bool, ) -> List[Tuple[str, Any, bool]]: if members and isinstance(members[0], tuple): members = [ObjectMember(*m) for m in members] return orig_filter_members(self, members, want_all) Documenter.filter_members = _documenter_filter_members # type: ignore[method-assign, assignment] @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autosummary`. :param app: The Sphinx application. """ app.setup_extension("sphinx.ext.autosummary") app.setup_extension("autodocsumm") app.setup_extension("sphinx_toolbox.latex") app.add_directive("autosummary", PatchedAutosummary, override=True) app.add_directive("autoclasssumm", PatchedAutoDocSummDirective, override=True) app.add_directive("automodulesumm", PatchedAutoDocSummDirective, override=True) autodocsumm.AutosummaryDocumenter.add_autosummary = add_autosummary allow_subclass_add(app, PatchedAutoSummModuleDocumenter) allow_subclass_add(app, PatchedAutoSummClassDocumenter) app.add_config_value( "autodocsumm_member_order", default="alphabetical", rebuild=True, types=ENUM("alphabetic", "alphabetical", "bysource"), ) app.add_config_value( "autosummary_col_type", default=r"\X", rebuild="latex", types=[str], ) if sphinx.version_info >= (8, 0): # 3rd party from sphinx.ext.autodoc import ObjectMember # Restore deprecated and removed functionality to fix autodocsumm ObjectMember.__getitem__ = lambda self, idx: (self.__name__, self.object)[idx] # type: ignore[method-assign] _patch_filter_members() return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/more_autosummary/column_widths.py000066400000000000000000000214631475757360300264670ustar00rootroot00000000000000#!/usr/bin/env python3 # # column_widths.py """ Sphinx extension to allow customisation of column widths in autosummary tables with the LaTeX builder. .. versionadded:: 3.0.0 Usage -------------- This extension provides the :rst:dir:`autosummary-widths` directive. This sets the autosummary table's column widths with the LaTeX builder until the end of the current reStructuredText document, or until the next :rst:dir:`autosummary-widths` directive. .. rst:directive:: autosummary-widths Set the width of the autosummary table's columns with the LaTeX builder. The directive takes up to two arguments -- the column widths as vulgar fractions (e.g. ``5/10``). If only one argument is provided, this sets the width of the first column, and the width of the second column is calculated from it. If both arguments are provided, they set the width of the first and second columns respectively. .. latex:clearpage:: :bold-title:`Examples:` .. code-block:: rst .. autosummary-widths:: 5/10 .. autosummary-widths:: 3/10, 7/10 .. autosummary-widths:: 35/100 .. attention:: This directive ignores the :confval:`autosummary_col_type` configuration option. API Reference ---------------- """ # 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. # # Parts based on https://github.com/sphinx-doc/sphinx/blob/3.x/sphinx/ext/autosummary/__init__.py # | Copyright (c) 2007-2022 by the Sphinx team (see AUTHORS file). # | BSD Licensed # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib from contextlib import suppress from fractions import Fraction from itertools import chain from typing import Iterable, List, Tuple, cast # 3rd party import docutils from docutils import nodes from docutils.statemachine import StringList from domdf_python_tools import stringlist from sphinx import addnodes from sphinx.application import Sphinx from sphinx.config import Config from sphinx.ext.autosummary import autosummary_table from sphinx.util import rst from sphinx.util.docutils import SphinxDirective, switch_source_input # this package from sphinx_toolbox import latex from sphinx_toolbox.more_autosummary import PatchedAutosummary from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ("AutosummaryWidths", "WidthsDirective", "configure", "setup") class AutosummaryWidths(PatchedAutosummary): """ Customised :rst:dir:`autosummary` directive with customisable width with the LaTeX builder. .. attention:: This directive ignores the :confval:`autosummary_col_type` configuration option. """ def get_table(self, items: List[Tuple[str, str, str, str]]) -> List[nodes.Node]: """ Generate a proper list of table nodes for autosummary:: directive. :param items: A list produced by ``self.get_items``. """ table_spec = addnodes.tabular_col_spec() widths = tuple(chain.from_iterable(getattr(self.state.document, "autosummary_widths", ((1, 2), (1, 2))))) assert len(widths) == 4 table_spec["spec"] = r'\Xx{%d}{%d}\Xx{%d}{%d}' % widths table = autosummary_table('') classes = ["autosummary", "longtable"] if docutils.__version_info__ >= (0, 18): classes.append("colwidths-given") real_table = nodes.table('', classes=classes) table.append(real_table) group = nodes.tgroup('', cols=2) real_table.append(group) group.append(nodes.colspec('', colwidth=10)) group.append(nodes.colspec('', colwidth=90)) body = nodes.tbody('') group.append(body) def append_row(*column_texts: str) -> None: row = nodes.row('') source, line = self.state_machine.get_source_and_line() for text in column_texts: node = nodes.paragraph('') vl = StringList() vl.append(text, f"{source}:{line:d}:") # pylint: disable=loop-invariant-statement with switch_source_input(self.state, vl): self.state.nested_parse(vl, 0, node) with suppress(IndexError): if isinstance(node[0], nodes.paragraph): node = node[0] row.append(nodes.entry('', node)) body.append(row) add_signature = "nosignatures" not in self.options for name, sig, summary, real_name in items: col1 = f":obj:`{name} <{real_name}>`" if add_signature: col1 += f"\\ {rst.escape(sig)}" append_row(col1, summary) return [table_spec, table] class WidthsDirective(SphinxDirective): """ Sphinx directive which configures the column widths of an :rst:dir:`autosummary` table for the remainder of the document, or until the next `autosummary-widths` directive. """ # noqa: D400 required_arguments = 1 optional_arguments = 1 @staticmethod def parse_widths(raw_widths: Iterable[str]) -> List[Tuple[int, int]]: """ Parse a width string (as a vulgar fraction) into a list of 2-element ``(numerator, denominator)`` tuples. For example, ``'5/10'`` becomes ``(5, 10)``. :param raw_widths: """ widths = [cast(Tuple[int, int], tuple(map(int, arg.split('/')))) for arg in raw_widths] if len(widths) == 1: left_width = Fraction(*widths[0]) right_width = 1 - left_width widths.append((right_width.numerator, right_width.denominator)) return widths def run(self) -> List: """ Process the directive's arguments. """ self.state.document.autosummary_widths = self.parse_widths(self.arguments) # type: ignore[attr-defined] return [] def configure(app: Sphinx, config: Config) -> None: """ Configure :mod:`sphinx_toolbox.more_autosummary.column_widths`. :param app: The Sphinx application. :param config: """ latex_elements = getattr(config, "latex_elements", {}) latex_preamble = stringlist.StringList(latex_elements.get("preamble", '')) latex_preamble.blankline() latex_preamble.append(r"\makeatletter") latex_preamble.append(r"\newcolumntype{\Xx}[2]{>{\raggedright\arraybackslash}p{\dimexpr") latex_preamble.append(r" (\linewidth-\arrayrulewidth)*#1/#2-\tw@\tabcolsep-\arrayrulewidth\relax}}") latex_preamble.append(r"\makeatother") latex_preamble.blankline() latex_elements["preamble"] = str(latex_preamble) config.latex_elements = latex_elements # type: ignore[attr-defined] @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.more_autosummary.column_widths`. :param app: The Sphinx application. """ app.setup_extension("sphinx_toolbox.more_autosummary") app.add_directive("autosummary", AutosummaryWidths, override=True) app.add_directive("autosummary-widths", WidthsDirective) app.connect("config-inited", configure) app.connect("build-finished", latex.replace_unknown_unicode) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/pre_commit.py000066400000000000000000000245111475757360300223330ustar00rootroot00000000000000#!/usr/bin/env python3 # # pre_commit.py """ Sphinx extension to show examples of ``.pre-commit-config.yaml`` configuration. .. versionadded:: 1.6.0 .. extensions:: sphinx_toolbox.pre_commit Usage ------ .. rst:directive:: pre-commit Directive which shows an example snippet of ``.pre-commit-config.yaml``. .. rst:directive:option:: rev :type: string The revision or tag to clone at. .. rst:directive:option:: hooks :type: comma separated list A list of hooks IDs to document. If not given the hooks will be parsed from ``.pre-commit-hooks.yaml``. .. rst:directive:option:: args :type: comma separated list A list arguments that should or can be provided to the first hook ID. .. versionadded:: 1.7.2 :bold-title:`Example` .. rest-example:: .. pre-commit:: :rev: v0.0.4 :hooks: some-hook,some-other-hook .. clearpage:: .. rst:directive:: .. pre-commit:flake8:: version Directive which shows an example snippet of ``.pre-commit-config.yaml`` for a flake8 plugin. The directive takes a single argument -- the version of the flake8 plugin to install from PyPI. .. rst:directive:option:: flake8-version :type: string The version of flake8 to use. Default ``3.8.4``. .. rst:directive:option:: plugin-name :type: string The name of the plugin to install from PyPI. Defaults to the repository name. :bold-title:`Example` .. rest-example:: .. pre-commit:flake8:: 0.0.4 .. versionchanged:: 2.8.0 The repository URL now points to GitHub. API Reference ---------------- """ # # Copyright Β© 2020-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 re import warnings from io import StringIO from textwrap import indent from typing import Any, List, Sequence # 3rd party import sphinx.util.docutils from docutils import nodes from docutils.statemachine import StringList from domdf_python_tools.paths import PathPlus from ruamel.yaml import YAML from sphinx.application import Sphinx from sphinx.util.docutils import SphinxDirective from typing_extensions import TypedDict # this package from sphinx_toolbox.utils import Purger, SphinxExtMetadata, make_github_url, metadata_add_version __all__ = ( "pre_commit_node_purger", "pre_commit_f8_node_purger", "parse_hooks", "PreCommitDirective", "Flake8PreCommitDirective", "setup", ) pre_commit_node_purger = Purger("all_pre_commit_nodes") pre_commit_f8_node_purger = Purger("all_pre_commit_f8_nodes") def parse_hooks(hooks: str) -> List[str]: """ Parses the comma, semicolon and/or space delimited list of hook IDs. :param hooks: """ return list(filter(None, re.split("[,; ]", hooks))) class _BaseHook(TypedDict): id: str # noqa: A003 # pylint: disable=redefined-builtin class _Hook(_BaseHook, total=False): args: List[str] class _BaseConfig(TypedDict): repo: str class _Config(_BaseConfig, total=False): rev: str hooks: List[_Hook] class PreCommitDirective(SphinxDirective): """ A Sphinx directive for documenting pre-commit hooks. .. clearpage:: """ has_content: bool = False option_spec = { "rev": str, # the revision or tag to clone at. "hooks": parse_hooks, "args": parse_hooks, } def run(self) -> Sequence[nodes.Node]: # type: ignore[override] """ Process the content of the directive. """ if "hooks" in self.options: hooks = self.options["hooks"] else: cwd = PathPlus.cwd() for directory in (cwd, *cwd.parents): hook_file = directory / ".pre-commit-hooks.yaml" if hook_file.is_file(): hooks_dict = YAML(typ="safe", pure=True).load(hook_file.read_text()) hooks = [h["id"] for h in hooks_dict] # pylint: disable=loop-invariant-statement break else: warnings.warn("No hooks specified and no .pre-commit-hooks.yaml file found.") return [] repo = make_github_url(self.env.config.github_username, self.env.config.github_repository) config: _Config = {"repo": str(repo)} if "rev" in self.options: config["rev"] = self.options["rev"] config["hooks"] = [{"id": hook_name} for hook_name in hooks] if "args" in self.options: config["hooks"][0]["args"] = self.options["args"] targetid = f'pre-commit-{self.env.new_serialno("pre-commit"):d}' targetnode = nodes.section(ids=[targetid]) yaml_dumper = YAML() yaml_dumper.default_flow_style = False yaml_output_stream = StringIO() yaml_dumper.dump([config], stream=yaml_output_stream) yaml_output = yaml_output_stream.getvalue() if not yaml_output: return [] content = f".. code-block:: yaml\n\n{indent(yaml_output, ' ')}\n\n" view = StringList(content.split('\n')) pre_commit_node = nodes.paragraph(rawsource=content) self.state.nested_parse(view, self.content_offset, pre_commit_node) pre_commit_node_purger.add_node(self.env, pre_commit_node, targetnode, self.lineno) return [pre_commit_node] class Flake8PreCommitDirective(SphinxDirective): """ A Sphinx directive for documenting flake8 plugins' pre-commit hooks. """ has_content: bool = False option_spec = { "flake8-version": str, "plugin-name": str, # defaults to repository name } required_arguments = 1 # the plugin version def run(self) -> Sequence[nodes.Node]: # type: ignore[override] """ Process the content of the directive. """ plugin_name = self.options.get("plugin-name", self.env.config.github_repository) flake8_version = self.options.get("flake8-version", "3.8.4") config = { "repo": "https://github.com/pycqa/flake8", "rev": flake8_version, "hooks": [{"id": "flake8", "additional_dependencies": [f"{plugin_name}=={self.arguments[0]}"]}] } targetid = f'pre-commit-{self.env.new_serialno("pre-commit"):d}' targetnode = nodes.section(ids=[targetid]) yaml_dumper = YAML() yaml_dumper.default_flow_style = False yaml_output_stream = StringIO() yaml_dumper.dump([config], stream=yaml_output_stream) yaml_output = yaml_output_stream.getvalue() if not yaml_output: return [] content = f".. code-block:: yaml\n\n{indent(yaml_output, ' ')}\n\n" view = StringList(content.split('\n')) pre_commit_node = nodes.paragraph(rawsource=content) self.state.nested_parse(view, self.content_offset, pre_commit_node) pre_commit_f8_node_purger.add_node(self.env, pre_commit_node, targetnode, self.lineno) return [pre_commit_node] def revert_8345() -> None: # pragma: no cover # Only for Sphinx 4+ """ Remove the incorrect warning emitted since https://github.com/sphinx-doc/sphinx/pull/8345. """ # Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). # BSD Licensed # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. def lookup_domain_element(self, type: str, name: str) -> Any: # noqa: A002 # pylint: disable=redefined-builtin """ Lookup a markup element (directive or role), given its name which can be a full name (with domain). """ name = name.lower() # explicit domain given? if ':' in name: domain_name, name = name.split(':', 1) if domain_name in self.env.domains: domain = self.env.get_domain(domain_name) element = getattr(domain, type)(name) if element is not None: return element, [] # else look in the default domain else: def_domain = self.env.temp_data.get("default_domain") if def_domain is not None: element = getattr(def_domain, type)(name) if element is not None: return element, [] # always look in the std domain element = getattr(self.env.get_domain("std"), type)(name) if element is not None: return element, [] raise sphinx.util.docutils.ElementLookupError sphinx.util.docutils.sphinx_domains.lookup_domain_element = lookup_domain_element # type: ignore[method-assign] @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.pre_commit`. :param app: The Sphinx application. """ app.add_directive("pre-commit", PreCommitDirective) app.add_directive("pre-commit:flake8", Flake8PreCommitDirective) app.connect("env-purge-doc", pre_commit_node_purger.purge_nodes) app.connect("env-purge-doc", pre_commit_f8_node_purger.purge_nodes) if sphinx.version_info >= (4, 0): revert_8345() return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/py.typed000066400000000000000000000000001475757360300213050ustar00rootroot00000000000000sphinx-toolbox-3.9.0/sphinx_toolbox/rest_example.py000066400000000000000000000120751475757360300226670ustar00rootroot00000000000000#!/usr/bin/env python3 # # rest_example.py """ Directive to show example reStructuredText and the rendered output. .. extensions:: sphinx_toolbox.rest_example Usage --------- .. rst:directive:: rest-example Directive to show example reStructuredText and the rendered output. .. rst:directive:option:: force :type: flag If given, minor errors on highlighting are ignored. .. rst:directive:option:: emphasize-lines: line numbers :type: comma separated numbers Emphasize particular lines of the code block: .. rst:directive:option:: tab-width: number :type: number Sets the size of the indentation in spaces. .. rst:directive:option:: dedent: number :type: number Strip indentation characters from the code block, .. latex:clearpage:: :bold-title:`Example` .. rest-example:: .. rest-example:: :source:`sphinx_toolbox/config.py` Here is the :source:`source code ` API Reference --------------- """ # # Copyright Β© 2020-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, Sequence # 3rd party import sphinx.environment from docutils import nodes from docutils.parsers.rst import directives from docutils.statemachine import ViewList from domdf_python_tools.stringlist import StringList from sphinx.application import Sphinx from sphinx.util.docutils import SphinxDirective # this package from sphinx_toolbox.utils import OptionSpec, Purger, SphinxExtMetadata, metadata_add_version __all__ = ("reSTExampleDirective", "make_rest_example", "rest_example_purger", "setup") class reSTExampleDirective(SphinxDirective): """ Directive to show some reStructuredText source, and the rendered output. """ has_content: bool = True # Options to pass through to .. code-block:: option_spec: OptionSpec = { # type: ignore[assignment] "force": directives.flag, "emphasize-lines": directives.unchanged, "tab-width": int, "dedent": int, } def run(self) -> List[nodes.Node]: """ Create the rest_example node. """ targetid = f'example-{self.env.new_serialno("sphinx-toolbox rest_example"):d}' targetnode = nodes.target('', '', ids=[targetid]) content = make_rest_example( self.options, self.env, self.content, # type: ignore[arg-type] ) view = ViewList(content) example_node = nodes.paragraph(rawsource=content) # type: ignore[arg-type] self.state.nested_parse(view, self.content_offset, example_node) # type: ignore[arg-type] rest_example_purger.add_node(self.env, example_node, targetnode, self.lineno) return [targetnode, example_node] def make_rest_example( options: Dict[str, Any], env: sphinx.environment.BuildEnvironment, content: Sequence[str], ) -> List[str]: """ Make the content of a reST Example node. :param options: :param content: The user-provided content of the directive. """ output = StringList(".. container:: rest-example") output.indent_type = ' ' * env.config.docutils_tab_width output.blankline() with output.with_indent_size(1): output.append(".. code-block:: rest") with output.with_indent_size(2): for option, value in options.items(): if value is None: output.append(f":{option}:") else: output.append(f":{option}: {value}") output.blankline() output.extend(content) output.blankline(ensure_single=True) output.extend(content) output.blankline(ensure_single=True) return list(output) #: Purger to track rest-example nodes, and remove redundant ones. rest_example_purger = Purger("all_rest_example_nodes") @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.rest_example`. .. versionadded:: 0.7.0 :param app: The Sphinx application. """ # Hack to get the docutils tab size, as there doesn't appear to be any other way app.setup_extension("sphinx_toolbox.tweaks.tabsize") app.setup_extension("sphinx_toolbox._css") app.add_directive("rest-example", reSTExampleDirective) app.connect("env-purge-doc", rest_example_purger.purge_nodes) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/shields.py000066400000000000000000000561471475757360300216420ustar00rootroot00000000000000#!/usr/bin/env python3 # # shields.py r""" Directives for shield/badge images. .. extensions:: sphinx_toolbox.shields Usage --------- Several shield/badge directives are available. They function similarly to the ``.. image::`` directives, although not all options are available. As with the image directive, shields can be used as part of substitutions, e.g. .. code-block:: rest This repository uses pre-commit |pre-commit| .. |pre-commit| pre-commit:: All shields have the following options: .. rst:directive:option:: alt Alternative text for the shield, used when the image cannot be displayed or the user uses a screen reader. .. rst:directive:option:: height width scale The height/width/scale of the shield. .. rst:directive:option:: name .. rst:directive:option:: class :type: string Additional CSS class for the shield. All shields have the ``sphinx_toolbox_shield`` class by default. .. latex:clearpage:: Shields ^^^^^^^^^^^^ .. rst:directive:: rtfd-shield Shield to show the `ReadTheDocs `_ documentation build status. .. rst:directive:option:: project The name of the project on *ReadTheDocs*. .. rst:directive:option:: version The documentation version. Default ``latest``. .. rst:directive:option:: target The hyperlink target of the shield. Useful if the documentation uses a custom domain. .. versionadded:: 1.8.0 .. only:: html **Examples** .. rest-example:: .. rtfd-shield:: :project: sphinx-toolbox .. rest-example:: .. rtfd-shield:: :project: attrs :target: https://www.attrs.org/ .. rst:directive:: pypi-shield Shield to show information about the project on `PyPI `_. .. rst:directive:option:: project The name of the project on *PyPI*. Only one of the following options is permitted: .. rst:directive:option:: version Show the package version. .. rst:directive:option:: py-versions Show the supported python versions. .. rst:directive:option:: implementations Show the supported python implementations. .. rst:directive:option:: wheel Show whether the package has a wheel. .. rst:directive:option:: license Show the license listed on PyPI. .. rst:directive:option:: downloads Show the downloads for the given period (day / week / month) .. versionchanged:: 2.5.0 Shields created with this option now link to pypistats_. .. _pypistats: https://pypistats.org .. only:: html **Examples** .. rest-example:: .. pypi-shield:: :version: \ .. pypi-shield:: :project: sphinx :downloads: month .. rst:directive:: maintained-shield Shield to indicate whether the project is maintained. Takes a single argument: the current year. .. only:: html **Example** .. rest-example:: .. maintained-shield:: 2020 .. rst:directive:: github-shield Shield to show information about a GitHub repository. .. rst:directive:option:: username The GitHub username. Defaults to :confval:`github_username`. .. rst:directive:option:: repository The GitHub repository. Defaults to :confval:`github_repository`. .. rst:directive:option:: branch The branch to show information about. Default ``master``. Required for ``commits-since`` and ``last-commit``. Only one of the following options is permitted: .. rst:directive:option:: contributors :type: flag Show the number of contributors. .. rst:directive:option:: commits-since: tag :type: string Show the number of commits since the given tag. .. rst:directive:option:: last-commit :type: flag Show the date of the last commit. .. rst:directive:option:: top-language :type: flag Show the top language and percentage. .. rst:directive:option:: license :type: flag Show the license detected by GitHub. .. only:: html **Examples** .. rest-example:: .. github-shield:: :last-commit: \ .. github-shield:: :commits-since: v0.1.0 .. rst:directive:: actions-shield Shield to show the *GitHub Actions* build status. .. rst:directive:option:: username The GitHub username. Defaults to :confval:`github_username`. .. rst:directive:option:: repository The GitHub repository. Defaults to :confval:`github_repository`. .. rst:directive:option:: workflow The workflow to show the status for. .. only:: html **Example** .. rest-example:: .. actions-shield:: :workflow: Windows Tests .. rst:directive:: requires-io-shield Shield to show the *Requires.io* status. .. rst:directive:option:: username The GitHub username. Defaults to :confval:`github_username`. .. rst:directive:option:: repository The GitHub repository. Defaults to :confval:`github_repository`. .. rst:directive:option:: branch The branch to show the build status for. Default ``master``. .. only:: html **Example** .. rest-example:: .. requires-io-shield:: .. rst:directive:: coveralls-shield Shield to show the code coverage from `Coveralls.io `_. .. rst:directive:option:: username The GitHub username. Defaults to :confval:`github_username`. .. rst:directive:option:: repository The GitHub repository. Defaults to :confval:`github_repository`. .. rst:directive:option:: branch The branch to show the build status for. Default ``master``. .. only:: html **Example** .. rest-example:: .. coveralls-shield:: .. rst:directive:: codefactor-shield Shield to show the code quality score from `Codefactor `_. .. rst:directive:option:: username The GitHub username. Defaults to :confval:`github_username`. .. rst:directive:option:: repository The GitHub repository. Defaults to :confval:`github_repository`. .. only:: html **Example** .. rest-example:: .. codefactor-shield:: .. rst:directive:: pre-commit-shield Shield to indicate that the project uses `pre-commit `_. .. only:: html **Example** .. rest-example:: .. pre-commit-shield:: .. rst:directive:: pre-commit-ci-shield .. versionadded:: 1.7.0 Shield to show the `pre-commit.ci `_ status. .. rst:directive:option:: username The GitHub username. Defaults to :confval:`github_username`. .. rst:directive:option:: repository The GitHub repository. Defaults to :confval:`github_repository`. .. rst:directive:option:: branch The branch to show the status for. Default ``master``. .. only:: html **Example** .. rest-example:: .. pre-commit-ci-shield:: .. latex:vspace:: 5mm """ # # Copyright Β© 2020-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. # # Based on public domain code from Docutils # # stdlib from typing import List, Optional, Tuple from urllib.parse import quote # 3rd party import dict2css import docutils from apeye.url import URL from docutils import nodes from docutils.nodes import fully_normalize_name, whitespace_normalize_name from docutils.parsers.rst import directives from docutils.parsers.rst.roles import set_classes from domdf_python_tools.paths import PathPlus from sphinx.application import Sphinx from sphinx.util.docutils import SphinxDirective # this package from sphinx_toolbox import _css from sphinx_toolbox.utils import OptionSpec, SphinxExtMetadata, flag, make_github_url, metadata_add_version __all__ = ( "SHIELDS_IO", "shield_default_option_spec", "Shield", "RTFDShield", "PyPIShield", "MaintainedShield", "GitHubBackedShield", "GitHubShield", "GitHubActionsShield", "RequiresIOShield", "CoverallsShield", "CodefactorShield", "PreCommitShield", "PreCommitCIShield", "copy_asset_files", "setup", ) #: Base URL for shields.io SHIELDS_IO = URL("https://img.shields.io") #: Options common to all shields. shield_default_option_spec: OptionSpec = { "alt": directives.unchanged, "height": directives.length_or_unitless, "width": directives.length_or_percentage_or_unitless, "scale": directives.percentage, "name": directives.unchanged, "class": directives.class_option, } class Shield(SphinxDirective): """ Directive for `shields.io `_ shields/badges. """ required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True option_spec: OptionSpec = { # type: ignore[assignment] "target": directives.unchanged_required, **shield_default_option_spec, } def run(self) -> List[nodes.Node]: """ Process the content of the shield directive. """ if "class" in self.options: self.options["class"].append("sphinx_toolbox_shield") else: self.options["class"] = ["sphinx_toolbox_shield"] self.arguments = [str(x) for x in self.arguments] messages = [] reference = directives.uri(self.arguments[0]) self.options["uri"] = reference reference_node = None if "target" in self.options: block = docutils.utils.escape2null(self.options["target"]).splitlines() block = [line for line in block] target_type, data = self.state.parse_target(block, self.block_text, self.lineno) # type: ignore[attr-defined] if target_type == "refuri": reference_node = nodes.reference(refuri=data) elif target_type == "refname": # pragma: no cover reference_node = nodes.reference( refname=fully_normalize_name(data), name=whitespace_normalize_name(data), ) reference_node.indirect_reference_name = data # type: ignore[attr-defined] self.state.document.note_refname(reference_node) else: # pragma: no cover # malformed target # data is a system message messages.append(data) del self.options["target"] set_classes(self.options) image_node = nodes.image(self.block_text, **self.options) self.add_name(image_node) if reference_node: reference_node += image_node return messages + [reference_node] else: return messages + [image_node] class GitHubBackedShield(Shield): """ Base class for badges that are based around GitHub. """ required_arguments = 0 option_spec: OptionSpec = { "username": str, # Defaults to "github_username" if undefined "repository": str, # Defaults to "github_repository" if undefined **shield_default_option_spec, } def get_repo_details(self) -> Tuple[str, str]: """ Returns the username and repository name, either parsed from the directive's options or from ``conf.py``. """ username = self.options.pop("username", self.env.config.github_username) if username is None: raise ValueError("'github_username' has not been set in 'conf.py'!") repository = self.options.pop("repository", self.env.config.github_repository) if repository is None: raise ValueError("'github_repository' has not been set in 'conf.py'!") return username, repository class RTFDShield(Shield): """ Shield to show the `ReadTheDocs `_ documentation build status. .. versionchanged:: 1.8.0 Added the ``:target:`` option, to allow a custom target to be specified. Useful if the documentation uses a custom domain. """ required_arguments = 0 option_spec: OptionSpec = { "project": directives.unchanged_required, "version": str, "target": str, **shield_default_option_spec, } def run(self) -> List[nodes.Node]: """ Process the content of the shield directive. """ project = self.options.pop("project") version = self.options.pop("version", "latest") image = SHIELDS_IO / "readthedocs" / project / f"{version}?logo=read-the-docs" self.arguments = [image] if "target" not in self.options: self.options["target"] = f"https://{project}.readthedocs.io/en/{version}/" return super().run() class GitHubActionsShield(GitHubBackedShield): """ Shield to show the *GitHub Actions* build status. """ option_spec: OptionSpec = { "username": str, # Defaults to "github_username" if undefined "repository": str, # Defaults to "github_repository" if undefined "workflow": directives.unchanged_required, # The name of the workflow **shield_default_option_spec, } def run(self) -> List[nodes.Node]: """ Process the content of the shield directive. """ username, repository = self.get_repo_details() workflow = quote(self.options["workflow"]) self.arguments = [str(make_github_url(username, repository) / "workflows" / workflow / "badge.svg")] self.options["target"] = str( make_github_url(username, repository) / f"actions?query=workflow%3A%22{workflow}%22" ) return super().run() class RequiresIOShield(GitHubBackedShield): """ Shield to show the `Requires.io `_ status. """ option_spec: OptionSpec = { "username": str, # Defaults to "github_username" if undefined "repository": str, # Defaults to "github_repository" if undefined "branch": str, **shield_default_option_spec, } def run(self) -> List[nodes.Node]: """ Process the content of the shield directive. """ username, repository = self.get_repo_details() branch = self.options.pop("branch", "master") base_url = URL("https://requires.io/github/") / username / repository self.arguments = [str(base_url / f"requirements.svg?branch={branch}")] self.options["target"] = str(base_url / f"requirements/?branch={branch}") return super().run() class CoverallsShield(GitHubBackedShield): """ Shield to show the code coverage from `Coveralls.io `_. """ option_spec: OptionSpec = { "username": str, # Defaults to "github_username" if undefined "repository": str, # Defaults to "github_repository" if undefined "branch": str, **shield_default_option_spec, } def run(self) -> List[nodes.Node]: """ Process the content of the shield directive. """ username, repository = self.get_repo_details() branch = self.options.pop("branch", "master") url = SHIELDS_IO / "coveralls" / "github" / username / repository / f"{branch}?logo=coveralls" self.arguments = [str(url)] self.options["target"] = f"https://coveralls.io/github/{username}/{repository}?branch={branch}" return super().run() class CodefactorShield(GitHubBackedShield): """ Shield to show the code quality score from `Codefactor `_. """ def run(self) -> List[nodes.Node]: """ Process the content of the shield directive. """ username, repository = self.get_repo_details() url = SHIELDS_IO / "codefactor" / "grade" / "github" / username / f"{repository}?logo=codefactor" self.arguments = [str(url)] self.options["target"] = f"https://codefactor.io/repository/github/{username}/{repository}" return super().run() class PyPIShield(Shield): """ Shield to show information about the project on `PyPI `_. """ required_arguments = 0 option_spec: OptionSpec = { "project": directives.unchanged_required, "version": flag, # Show the package version. "py-versions": flag, # Show the supported python versions. "implementations": flag, # Show the supported python implementations. "wheel": flag, # Show whether the package has a wheel on PyPI. "license": flag, # Show the license listed on PyPI. "downloads": str, # Show the downloads for the given period (day / week / month). **shield_default_option_spec, } def run(self) -> List[nodes.Node]: """ Process the content of the shield directive. """ base_url = SHIELDS_IO / "pypi" project = self.options.pop("project", self.env.config.github_repository) self.options["target"] = f"https://pypi.org/project/{project}" info = { 'v': self.options.pop("version", False), "py-versions": self.options.pop("py-versions", False), "implementation": self.options.pop("implementations", False), "wheel": self.options.pop("wheel", False), 'l': self.options.pop("license", False), "downloads": self.options.pop("downloads", False), } n_info_options: int = len([k for k, v in info.items() if v]) if n_info_options > 1: raise ValueError("Only one information option is allowed for the 'pypi-badge' directive.") elif n_info_options == 0: raise ValueError("An information option is required for the 'pypi-badge' directive.") for option in {'v', "implementation", "wheel", 'l'}: if info[option]: self.arguments = [base_url / option / project] break if info["py-versions"]: self.arguments = [str(base_url / "pyversions" / f"{project}?logo=python&logoColor=white")] elif info["downloads"]: if info["downloads"] in {"week", "dw"}: self.arguments = [base_url / "dw" / project] elif info["downloads"] in {"month", "dm"}: self.arguments = [base_url / "dm" / project] elif info["downloads"] in {"day", "dd"}: self.arguments = [base_url / "dd" / project] else: raise ValueError("Unknown time period for the PyPI download statistics.") self.options["target"] = f"https://pypistats.org/packages/{project.lower()}" return super().run() class GitHubShield(GitHubBackedShield): """ Shield to show information about a GitHub repository. """ option_spec: OptionSpec = { "username": str, # Defaults to "github_username" if undefined "repository": str, # Defaults to "github_repository" if undefined "branch": str, "contributors": flag, # Show the number of contributors. "commits-since": str, # Show the number of commits since the given tag. "last-commit": flag, # Show the date of the last commit. "top-language": flag, # Show the top language and % "license": flag, **shield_default_option_spec, } def run(self) -> List[nodes.Node]: """ Process the content of the shield directive. """ base_url = "https://img.shields.io/github" username, repository = self.get_repo_details() branch = self.options.pop("branch", "master") info = { "contributors": self.options.pop("contributors", False), "commits-since": self.options.pop("commits-since", False), "last-commit": self.options.pop("last-commit", False), "top-language": self.options.pop("top-language", False), "license": self.options.pop("license", False), } n_info_options: int = len([k for k, v in info.items() if v]) if n_info_options > 1: raise ValueError("Only one information option is allowed for the 'github-badge' directive.") elif n_info_options == 0: raise ValueError("An information option is required for the 'github-badge' directive.") if info["contributors"]: self.arguments = [f"{base_url}/contributors/{username}/{repository}"] self.options["target"] = f"https://github.com/{username}/{repository}/graphs/contributors" elif info["commits-since"]: self.arguments = [f"{base_url}/commits-since/{username}/{repository}/{info['commits-since']}/{branch}"] self.options["target"] = f"https://github.com/{username}/{repository}/pulse" elif info["last-commit"]: self.arguments = [f"{base_url}/last-commit/{username}/{repository}/{branch}"] self.options["target"] = f"https://github.com/{username}/{repository}/commit/{branch}" elif info["top-language"]: self.arguments = [f"{base_url}/languages/top/{username}/{repository}"] elif info["license"]: self.arguments = [f"{base_url}/license/{username}/{repository}"] self.options["target"] = f"https://github.com/{username}/{repository}/blob/master/LICENSE" return super().run() class MaintainedShield(Shield): """ Shield to indicate whether the project is maintained. """ required_arguments = 1 # The year option_spec = dict(shield_default_option_spec) def run(self) -> List[nodes.Node]: """ Process the content of the shield directive. """ self.arguments = [f"https://img.shields.io/maintenance/yes/{self.arguments[0]}"] return super().run() class PreCommitShield(Shield): """ Shield to indicate that the project uses `pre-commit `_. """ required_arguments = 0 option_spec = dict(shield_default_option_spec) def run(self) -> List[nodes.Node]: """ Process the content of the shield directive. """ self.arguments = [ "https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white" ] self.options["target"] = "https://github.com/pre-commit/pre-commit" return super().run() RESULTS_PRE_COMMIT_CI = URL("https://results.pre-commit.ci") class PreCommitCIShield(GitHubBackedShield): """ Shield to show the `pre-commit.ci `_ status. .. versionadded:: 1.7.0 """ option_spec: OptionSpec = { "username": str, # Defaults to "github_username" if undefined "repository": str, # Defaults to "github_repository" if undefined "branch": str, **shield_default_option_spec, } def run(self) -> List[nodes.Node]: """ Process the content of the shield directive. """ username, repository = self.get_repo_details() branch = self.options.pop("branch", "master") url = RESULTS_PRE_COMMIT_CI / "badge" / "github" / username / repository / f"{branch}.svg" self.arguments = [str(url)] self.options["target"] = str(RESULTS_PRE_COMMIT_CI / "latest" / "github" / username / repository / branch) return super().run() def copy_asset_files(app: Sphinx, exception: Optional[Exception] = None) -> None: """ Copy additional stylesheets into the HTML build directory. .. versionadded:: 2.3.1 :param app: The Sphinx application. :param exception: Any exception which occurred and caused Sphinx to abort. """ if exception: # pragma: no cover return if app.builder is None or app.builder.format.lower() != "html": # pragma: no cover return static_dir = PathPlus(app.outdir) / "_static" static_dir.maybe_make(parents=True) dict2css.dump(_css.shields_styles, static_dir / "toolbox-shields.css", minify=True) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.shields`. :param app: The Sphinx application. """ app.setup_extension("sphinx_toolbox.github") app.setup_extension("sphinx_toolbox._css") # Shields/badges app.add_directive("rtfd-shield", RTFDShield) app.add_directive("actions-shield", GitHubActionsShield) app.add_directive("requires-io-shield", RequiresIOShield) app.add_directive("coveralls-shield", CoverallsShield) app.add_directive("codefactor-shield", CodefactorShield) app.add_directive("pypi-shield", PyPIShield) app.add_directive("github-shield", GitHubShield) app.add_directive("maintained-shield", MaintainedShield) app.add_directive("pre-commit-shield", PreCommitShield) app.add_directive("pre-commit-ci-shield", PreCommitCIShield) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/sidebar_links.py000066400000000000000000000126371475757360300230140ustar00rootroot00000000000000#!/usr/bin/env python3 # # sidebar_links.py r""" Directive which adds a toctree to the sidebar containing links to the GitHub repository, PyPI project page etc. .. versionadded:: 2.9.0 .. extensions:: sphinx_toolbox.sidebar_links .. latex:vspace:: -5px Usage ------- .. latex:vspace:: -10px .. rst:directive:: sidebar-links Adds a toctree to the sidebar containing links to the GitHub repository, PyPI project page etc. The toctree is only shown in the sidebar and is hidden with non-HTML builders. .. only:: html You can see an example of this in the sidebar of this documentation. .. note:: This directive can only be used on the root document (i.e. index.rst). .. rst:directive:option:: github :type: flag Flag to add a link to the project's GitHub repository. To use this option add the following to your ``conf.py``: .. code-block:: python extensions = [ ... 'sphinx_toolbox.github', ] github_username = '' github_repository = '' See :mod:`sphinx_toolbox.github` for more information. .. rst:directive:option:: pypi :type: string Flag to add a link to the project page on PyPI. The name of the project on PyPI must be passed as the option's value. .. rst:directive:option:: caption :type: string The caption of the toctree. Defaults to ``Links`` Additional toctree entries may be added as the content of the directive, in the same manner as normal toctrees. API Reference -------------- """ # # 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 warnings from typing import List # 3rd party import docutils from docutils import nodes from docutils.parsers.rst import directives from domdf_python_tools.stringlist import StringList from sphinx import addnodes from sphinx.application import Sphinx from sphinx.util.docutils import SphinxDirective # this package from sphinx_toolbox.utils import OptionSpec, SphinxExtMetadata, flag, metadata_add_version __all__ = ("SidebarLinksDirective", "setup") class SidebarLinksDirective(SphinxDirective): """ Directive which adds a toctree to the sidebar containing links to the GitHub repository, PyPI project page etc. """ has_content: bool = True option_spec: OptionSpec = { # type: ignore[assignment] "pypi": directives.unchanged_required, "github": flag, "caption": directives.unchanged_required, } def process_github_option(self) -> str: """ Process the ``:github:`` flag. """ if "sphinx_toolbox.github" not in self.env.app.extensions: raise ValueError( "The 'sphinx_toolbox.github' extension is required for the " ":github: option but it is not enabled!" ) username = getattr(self.env.config, "github_username", None) if username is None: raise ValueError("'github_username' has not been set in 'conf.py'!") repository = getattr(self.env.config, "github_repository", None) if repository is None: raise ValueError("'github_repository' has not been set in 'conf.py'!") return f"GitHub " def run(self) -> List[nodes.Node]: """ Create the installation node. """ if self.env.docname != self.env.config.master_doc: # pragma: no cover warnings.warn( "The 'sidebar-links' directive can only be used on the Sphinx master doc. " "No links will be shown.", UserWarning, ) return [] body = StringList([ ".. toctree::", " :hidden:", ]) with body.with_indent(" ", 1): if "caption" in self.options: body.append(f":caption: {self.options['caption']}") else: # pragma: no cover body.append(":caption: Links") body.blankline() if "github" in self.options: body.append(self.process_github_option()) if "pypi" in self.options: body.append(f"PyPI ") body.extend(self.content) body.blankline() body.blankline() only_node = addnodes.only(expr="html") content_node = nodes.paragraph(rawsource=str(body)) only_node += content_node self.state.nested_parse(docutils.statemachine.StringList(body), self.content_offset, content_node) return [only_node] @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.sidebar_links`. :param app: The Sphinx application. """ app.add_directive("sidebar-links", SidebarLinksDirective) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/source.py000066400000000000000000000171611475757360300215000ustar00rootroot00000000000000#!/usr/bin/env python3 # # source.py """ Add hyperlinks to source files, either on GitHub or in the documentation itself. .. extensions:: sphinx_toolbox.source If you're looking for a ``[source]`` button to go at the end of your class and function signatures, checkout :mod:`sphinx.ext.linkcode` and :mod:`sphinx.ext.viewcode`. Usage ------- .. confval:: source_link_target :type: :class:`str` :required: False :default: ``'Sphinx'`` The target of the source link, either ``'GitHub'`` or ``'Sphinx'``. Case insensitive. .. rst:role:: source Role which shows a link to the given source file, either on GitHub or within the Sphinx documentation. By default, the link points to the code within the documentation, but can be configured to point to GitHub by setting :confval:`source_link_target` to ``'GitHub'``. :bold-title:`Example` .. rest-example:: :source:`sphinx_toolbox/config.py` Here is the :source:`source code ` API Reference -------------- """ # # Copyright Β© 2020-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. # # Based on pyspecific.py from the Python documentation. # Copyright 2008-2014 by Georg Brandl. # Licensed under the PSF License 2.0 # # Parts of the docstrings based on https://docutils.sourceforge.io/docs/howto/rst-roles.html # # stdlib from typing import TYPE_CHECKING, Dict, List, Sequence, Tuple # 3rd party import sphinx from docutils import nodes from docutils.nodes import system_message from docutils.parsers.rst.states import Inliner from sphinx import addnodes from sphinx.application import Sphinx from sphinx.environment import BuildEnvironment from sphinx.errors import NoUri from sphinx.util import split_explicit_title # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version if TYPE_CHECKING: # this package from sphinx_toolbox.utils import Config __all__ = ("source_role", "setup") # TODO: rawstring: Return it as a problematic node linked to a system message if a problem is encountered. _sphinx_version = sphinx.version_info[:3] def _make_viewcode_node( title: str, pagename: str, env: BuildEnvironment, ) -> nodes.Node: """ Construct a node for the :mod:`sphinx.ext.viewcode` link. Handles Sphinx 3.5+ compatibility. """ if _sphinx_version < (3, 5, 0): return addnodes.pending_xref( title, nodes.inline(title, title), reftype="viewcode", refdomain="std", refexplicit=False, reftarget=pagename, refid=title, refdoc=env.docname, ) else: # 3rd party from sphinx.util.nodes import make_refnode assert env.app.builder is not None try: return make_refnode( env.app.builder, fromdocname=env.docname, todocname=pagename, targetid=title, child=nodes.inline(title, title), ) except NoUri: return nodes.inline(title, title) def source_role( name: str, rawtext: str, text: str, lineno: int, inliner: Inliner, options: Dict = {}, content: List[str] = [] ) -> Tuple[Sequence[nodes.Node], List[system_message]]: """ Adds a link to the given Python source file in the documentation or on GitHub. :param name: The local name of the interpreted role, the role name actually used in the document. :param rawtext: A string containing the entire interpreted text input, including the role and markup. :param text: The interpreted text content. :param lineno: The line number where the interpreted text begins. :param inliner: The :class:`docutils.parsers.rst.states.Inliner` object that called :func:`~.source_role`. It contains the several attributes useful for error reporting and document tree access. :param options: A dictionary of directive options for customization (from the ``role`` directive), to be interpreted by the function. Used for additional attributes for the generated elements and other functionality. :param content: A list of strings, the directive content for customization (from the ``role`` directive). To be interpreted by the function. :return: A list containing the created node, and a list containing any messages generated during the function. .. versionchanged:: 2.8.0 Now returns a sequence of :class:`nodes.reference ` and :class:`addnodes.pending_xref ` as the first tuple element, rather than :class:`nodes.reference ` and :class:`addnodes.pending_xref ` as in previous versions. """ has_t, title, target = split_explicit_title(text) title = nodes.unescape(title) target = nodes.unescape(target) env = inliner.document.settings.env config = env.app.config nodes_: List[nodes.Node] = [] messages: List[system_message] = [] refnode: nodes.Node if config.source_link_target == "sphinx": if target.endswith("/__init__.py"): pagename = "_modules/" + target.rsplit('/', 1)[0] else: pagename = "_modules/" + target.replace(".py", '') # refnode = addnodes.only(expr="html") # refnode += addnodes.pending_xref( refnode = _make_viewcode_node( title, pagename, env, ) # refnode = addnodes.pending_xref( # title, # nodes.inline(title, title), # reftype="viewcode", # refdomain="std", # refexplicit=False, # reftarget=pagename, # refid=title, # refdoc=env.docname, # ) nodes_.append(refnode) elif config.source_link_target == "github": refnode = nodes.reference( title, title, refuri=str(config.github_source_url / target), ) nodes_.append(refnode) else: message = inliner.document.reporter.error(f"Unsupported source link target '{config.source_link_target}'.") messages.append(message) return nodes_, messages def _configure(app: Sphinx, config: "Config") -> None: """ Validate the provided configuration values. :param app: The Sphinx application. :param config: """ config.source_link_target = str(config.source_link_target).lower().strip() # type: ignore[attr-defined] if config.source_link_target not in {"sphinx", "github"}: # this package from sphinx_toolbox.config import InvalidOptionError raise InvalidOptionError("Invalid value for 'source_link_target'.") @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.source`. :param app: The Sphinx application. """ # Link to source code app.add_role("source", source_role) # The target for the source link. One of GitHub or Sphinx (GitLab coming soonβ„’) app.add_config_value("source_link_target", "Sphinx", "env", types=[str]) app.connect("config-inited", _configure) app.setup_extension("sphinx_toolbox.github") return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/testing.py000066400000000000000000000672321475757360300216610ustar00rootroot00000000000000#!/usr/bin/env python3 # # testing.py r""" Functions for testing Sphinx extensions. .. extras-require:: testing :pyproject: .. seealso:: Sphinx's own ``testing`` library: https://github.com/sphinx-doc/sphinx/tree/3.x/sphinx/testing .. latex:vspace:: 10px .. _pytest-regressions: https://pypi.org/project/pytest-regressions/ """ # # Copyright Β© 2020-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. # # Based on Sphinx # Copyright (c) 2007-2020 by the Sphinx team. # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib import copy import re import sys import tempfile from functools import partial from types import SimpleNamespace from typing import Any, Callable, Dict, List, NamedTuple, Optional, Set, Tuple, Type, Union, cast # 3rd party import pytest # nodep import sphinx.application from bs4 import BeautifulSoup from coincidence.regressions import ( # nodep AdvancedFileRegressionFixture, check_file_output, check_file_regression ) from docutils import __version_info__ as docutils_version from docutils import nodes from docutils.parsers.rst import Directive, roles from docutils.transforms import Transform from domdf_python_tools.doctools import prettify_docstrings from domdf_python_tools.paths import PathPlus from domdf_python_tools.stringlist import StringList from domdf_python_tools.typing import PathLike from jinja2 import Template # nodep from pygments.lexer import Lexer # type: ignore[import-untyped] # nodep from pytest_regressions.common import check_text_files # nodep from pytest_regressions.file_regression import FileRegressionFixture # nodep from sphinx.builders import Builder from sphinx.domains import Domain, Index from sphinx.domains.python import PythonDomain from sphinx.environment.collectors import EnvironmentCollector from sphinx.events import EventListener from sphinx.events import EventManager as BaseEventManager from sphinx.ext.autodoc.directive import AutodocDirective from sphinx.highlighting import lexer_classes from sphinx.registry import SphinxComponentRegistry from sphinx.roles import XRefRole from sphinx.util import docutils from sphinx.util.typing import RoleFunction, TitleGetter # this package from sphinx_toolbox.utils import Config, SphinxExtMetadata # _ModuleWrapper unwrapping BS due to # https://github.com/sphinx-doc/sphinx/commit/8866adeacfb045c97302cc9c7e3b60dec5ca38fd try: # 3rd party from sphinx.deprecation import _ModuleWrapper # type: ignore[attr-defined] if isinstance(docutils, _ModuleWrapper): docutils = docutils._module except ImportError: # Unnecessary if unimportable pass __all__ = ( "Sphinx", "run_setup", "RunSetupOutput", "remove_html_footer", "check_html_regression", "remove_html_link_tags", "check_asset_copy", "HTMLRegressionFixture", "html_regression", "LaTeXRegressionFixture", "latex_regression", ) class FakeBuilder(Builder): pass class EventManager(BaseEventManager): def connect(self, name: str, callback: Callable, priority: int) -> int: """ Connect a handler to specific event. """ listener_id = self.next_listener_id self.next_listener_id += 1 self.listeners[name].append(EventListener(listener_id, callback, priority)) return listener_id class Sphinx: """ A class that pretends to be :class:`sphinx.application.Sphinx` but that is stripped back to allow the internals to be inspected. This can be used in tests to ensure the nodes, roles etc. being registered in an extension's ``setup()`` function are actually being registered. """ # noqa: D400 registry: SphinxComponentRegistry #: Instance of :class:`sphinx.registry.SphinxComponentRegistry` config: Config #: Instance of :class:`sphinx.config.Config` events: EventManager #: Instance of :class:`sphinx.events.EventManager` html_themes: Dict[str, str] #: Mapping of HTML theme names to filesystem paths. # builder: Builder #: Instance of :class:`sphinx.builder.Builder` def __init__(self): # , buildername: str = "html" self.registry = SphinxComponentRegistry() self.config = Config({}, {}) self.events = EventManager(self) # type: ignore[arg-type] self.html_themes: Dict[str, str] = {} # self.builder = self.registry.create_builder(self, buildername) def add_builder(self, builder: Type[Builder], override: bool = False) -> None: r""" Register a new builder. The registered values are stored in the ``app.registry.builders`` dictionary (:class:`typing.Dict`\[:class:`str`\, :class:`typing.Type`\[:class:`sphinx.builders.Builder`\]]). """ self.registry.add_builder(builder, override=override) def add_config_value( self, name: str, default: Any, rebuild: Union[bool, str], types: Any = (), ) -> None: r""" Register a configuration value. The registered values are stored in the ``app.config.values`` dictionary (:class:`typing.Dict`\[:class:`str`\, :class:`typing.Tuple`]). """ if rebuild in {False, True}: rebuild = "env" if rebuild else '' self.config.add(name, default, rebuild, types) def add_event(self, name: str) -> None: r""" Register an event called ``name``. The registered values are stored in the ``app.events.events`` dictionary (:class:`typing.Dict`\[:class:`str`\, :class:`str`\]). """ self.events.add(name) def set_translator( self, name: str, translator_class: Type[nodes.NodeVisitor], override: bool = False, ) -> None: r""" Register or override a Docutils translator class. The registered values are stored in the ``app.registry.translators`` dictionary. (:class:`typing.Dict`\[:class:`str`\, :class:`typing.Type`\[:class:`docutils.nodes.NodeVisitor`\]]). .. clearpage:: """ self.registry.add_translator(name, translator_class, override=override) def add_node( self, node: Type[nodes.Element], override: bool = False, **kwargs: Tuple[Callable, Callable], ) -> None: r""" Register a Docutils node class. The registered values are stored in the ``additional_nodes`` set returned by :func:`~sphinx_toolbox.testing.run_setup` (:class:`typing.Set`\[:class:`typing.Type`\[:class:`docutils.nodes.Node`\]]). """ if not override and docutils.is_node_registered(node): raise ValueError( f"node class {node.__name__!r} is already registered, its visitors will be overridden" ) docutils.register_node(node) self.registry.add_translation_handlers(node, **kwargs) def add_enumerable_node( self, node: Type[nodes.Element], figtype: str, title_getter: Optional[TitleGetter] = None, override: bool = False, **kwargs: Tuple[Callable, Callable], ) -> None: """ Register a Docutils node class as a numfig target. """ # Sphinx's signature is wrong WRT Optional self.registry.add_enumerable_node( node, figtype, title_getter, override=override, ) self.add_node(node, override=override, **kwargs) def add_directive(self, name: str, cls: Type[Directive], override: bool = False) -> None: """ Register a Docutils directive. """ if not override and docutils.is_directive_registered(name): raise ValueError(f"directive {name!r} is already registered, it will be overridden") docutils.register_directive(name, cls) def add_role(self, name: str, role: Any, override: bool = False) -> None: r""" Register a Docutils role. The registered values are stored in the ``roles`` dictionary returned by :func:`~sphinx_toolbox.testing.run_setup`. (:class:`typing.Dict`\[:class:`str`\, :class:`typing.Callable`\]). """ if not override and docutils.is_role_registered(name): raise ValueError(f"role {name!r} is already registered, it will be overridden") docutils.register_role(name, role) def add_generic_role(self, name: str, nodeclass: Any, override: bool = False) -> None: """ Register a generic Docutils role. """ if not override and docutils.is_role_registered(name): raise ValueError(f"role {name!r} is already registered, it will be overridden") role = roles.GenericRole(name, nodeclass) docutils.register_role(name, role) def add_domain( self, domain: Type[Domain], override: bool = False, ) -> None: """ Register a domain. """ self.registry.add_domain(domain, override=override) def add_directive_to_domain( self, domain: str, name: str, cls: Type[Directive], override: bool = False, ) -> None: """ Register a Docutils directive in a domain. """ self.registry.add_directive_to_domain(domain, name, cls, override=override) def add_role_to_domain( self, domain: str, name: str, role: Union[RoleFunction, XRefRole], override: bool = False, ) -> None: """ Register a Docutils role in a domain. """ self.registry.add_role_to_domain(domain, name, role, override=override) def add_index_to_domain( self, domain: str, index: Type[Index], override: bool = False, ) -> None: """ Register a custom index for a domain. """ self.registry.add_index_to_domain(domain, index) def add_object_type( self, directivename: str, rolename: str, indextemplate: str = '', parse_node: Optional[Callable] = None, ref_nodeclass: Optional[Type[nodes.TextElement]] = None, objname: str = '', doc_field_types: List = [], override: bool = False, ) -> None: """ Register a new object type. """ # Sphinx's signature is wrong WRT Optional self.registry.add_object_type( directivename, rolename, indextemplate, parse_node, ref_nodeclass, objname, doc_field_types, override=override, ) def add_crossref_type( self, directivename: str, rolename: str, indextemplate: str = '', ref_nodeclass: Optional[Type[nodes.TextElement]] = None, objname: str = '', override: bool = False, ) -> None: """ Register a new crossref object type. """ # Sphinx's signature is wrong WRT Optional self.registry.add_crossref_type( directivename, rolename, indextemplate, ref_nodeclass, objname, override=override, ) def add_transform(self, transform: Type[Transform]) -> None: """ Register a Docutils transform to be applied after parsing. """ self.registry.add_transform(transform) def add_post_transform(self, transform: Type[Transform]) -> None: """ Register a Docutils transform to be applied before writing. """ self.registry.add_post_transform(transform) def add_js_file(self, filename: str, **kwargs: str) -> None: """ Register a JavaScript file to include in the HTML output. .. versionadded:: 2.8.0 """ self.registry.add_js_file(filename, **kwargs) # if hasattr(self.builder, 'add_js_file'): # self.builder.add_js_file(filename, **kwargs) # def add_css_file(self, filename: str, **kwargs: str) -> None: """ Register a stylesheet to include in the HTML output. .. versionadded:: 2.7.0 """ self.registry.add_css_files(filename, **kwargs) # if hasattr(self.builder, 'add_css_file'): # self.builder.add_css_file(filename, **kwargs) def add_latex_package( self, packagename: str, options: Optional[str] = None, after_hyperref: bool = False, ) -> None: """ Register a package to include in the LaTeX source code. """ # Sphinx's signature is wrong WRT Optional self.registry.add_latex_package(packagename, cast(str, options), after_hyperref) def add_lexer(self, alias: str, lexer: Type[Lexer]) -> None: """ Register a new lexer for source code. """ if isinstance(lexer, Lexer): raise TypeError("app.add_lexer() API changed; Please give lexer class instead instance") else: lexer_classes[alias] = lexer def add_autodocumenter(self, cls: Any, override: bool = False) -> None: """ Register a new documenter class for the autodoc extension. """ self.registry.add_documenter(cls.objtype, cls) self.add_directive("auto" + cls.objtype, AutodocDirective, override=override) def add_autodoc_attrgetter( self, typ: Type, getter: Callable[[Any, str, Any], Any], ) -> None: """ Register a new ``getattr``-like function for the autodoc extension. """ self.registry.add_autodoc_attrgetter(typ, getter) def add_source_suffix(self, suffix: str, filetype: str, override: bool = False) -> None: """ Register a suffix of source files. """ self.registry.add_source_suffix(suffix, filetype, override=override) def add_source_parser(self, *args: Any, **kwargs: Any) -> None: """ Register a parser class. """ self.registry.add_source_parser(*args, **kwargs) def add_env_collector(self, collector: Type[EnvironmentCollector]) -> None: """ No-op for now. .. TODO:: Make this do something """ # def add_env_collector(self, collector: Type[EnvironmentCollector]) -> None: # """ # Register an environment collector class. # """ # # collector().enable(self) def add_html_theme(self, name: str, theme_path: str) -> None: """ Register an HTML Theme. """ self.html_themes[name] = theme_path def add_html_math_renderer( self, name: str, inline_renderers: Optional[Tuple[Callable, Callable]] = None, block_renderers: Optional[Tuple[Callable, Callable]] = None, ) -> None: """ Register a math renderer for HTML. """ self.registry.add_html_math_renderer(name, inline_renderers, block_renderers) def setup_extension(self, extname: str) -> None: """ Import and setup a Sphinx extension module. .. TODO:: implement this """ # self.registry.load_extension(self, extname) def require_sphinx(self, version: str) -> None: """ Check the Sphinx version if requested. No-op when testing """ # event interface def connect(self, event: str, callback: Callable, priority: int = 500) -> int: """ Register *callback* to be called when *event* is emitted. """ listener_id = self.events.connect(event, callback, priority) return listener_id @prettify_docstrings class RunSetupOutput(NamedTuple): """ :class:`~typing.NamedTuple` representing the output from :func:`~sphinx_toolbox.testing.run_setup`. """ setup_ret: Union[None, Dict[str, Any], "SphinxExtMetadata"] #: The output from the ``setup()`` function. directives: Dict[str, Callable] #: Mapping of directive names to directive functions. roles: Dict[str, Callable] #: Mapping of role names to role functions. additional_nodes: Set[Type[Any]] #: Set of custom docutils nodes registered in ``setup()``. app: Sphinx #: Instance of :class:`sphinx_toolbox.testing.Sphinx`. _sphinx_dict_setup = Callable[[sphinx.application.Sphinx], Optional[Dict[str, Any]]] _sphinx_metadata_setup = Callable[[sphinx.application.Sphinx], Optional["SphinxExtMetadata"]] _fake_dict_setup = Callable[[Sphinx], Optional[Dict[str, Any]]] _fake_metadata_setup = Callable[[Sphinx], Optional["SphinxExtMetadata"]] _setup_func_type = Union[_sphinx_dict_setup, _sphinx_metadata_setup, _fake_dict_setup, _fake_metadata_setup] class GenericNodeVisitor(nodes.NodeVisitor): pass def run_setup(setup_func: _setup_func_type) -> RunSetupOutput: # , buildername: str = "html" """ Function for running an extension's ``setup()`` function for testing. :param setup_func: The ``setup()`` function under test. :returns: 5-element namedtuple """ app = Sphinx() # buildername app.add_domain(PythonDomain) _additional_nodes = copy.copy(docutils.additional_nodes) orig_gnv = nodes.GenericNodeVisitor try: nodes.GenericNodeVisitor = GenericNodeVisitor # type: ignore[misc,assignment] docutils.additional_nodes = set() with docutils.docutils_namespace(): setup_ret = setup_func(app) # type: ignore[arg-type] directives = copy.copy(docutils.directives._directives) # type: ignore[attr-defined] roles = copy.copy(docutils.roles._roles) # type: ignore[attr-defined] additional_nodes = copy.copy(docutils.additional_nodes) finally: docutils.additional_nodes = _additional_nodes nodes.GenericNodeVisitor = orig_gnv # type: ignore[misc] return RunSetupOutput(setup_ret, directives, roles, additional_nodes, app) def remove_html_footer(page: BeautifulSoup) -> BeautifulSoup: """ Remove the Sphinx footer from HTML pages. The footer contains the Sphinx and theme versions and therefore changes between versions. This can cause unwanted, false positive test failures. :param page: The page to remove the footer from. :return: The page without the footer. """ for div in page.select("div.footer"): div.extract() return page def remove_html_link_tags(page: BeautifulSoup) -> BeautifulSoup: """ Remove link tags from HTML pages. These may vary between different versions of Sphinx and its extensions. This can cause unwanted, false positive test failures. :param page: The page to remove the link tags from. :return: The page without the link tags. """ for div in page.select("head link"): div.extract() return page def check_html_regression(page: BeautifulSoup, file_regression: FileRegressionFixture) -> None: """ Check an HTML page generated by Sphinx for regressions, using `pytest-regressions`_. :param page: The page to test. :param file_regression: The file regression fixture. **Example usage** .. code-block:: python @pytest.mark.parametrize("page", ["index.html"], indirect=True) def test_page(page: BeautifulSoup, file_regression: FileRegressionFixture): check_html_regression(page, file_regression) """ # noqa: RST306 __tracebackhide__ = True page = remove_html_footer(page) page = remove_html_link_tags(page) for div in page.select("script"): if "_static/language_data.js" in str(div): div.extract() for div in page.select("div.sphinxsidebar"): div.extract() check_file_regression( StringList(page.prettify()), file_regression, extension=".html", ) class HTMLRegressionFixture(FileRegressionFixture): """ Subclass of :class:`pytest_regressions.file_regression.FileRegressionFixture` for checking HTML files. .. versionadded:: 2.0.0 """ def check( # type: ignore[override] self, page: BeautifulSoup, *, extension: str = ".html", jinja2: bool = False, jinja2_namespace: Optional[Dict[str, Any]] = None, **kwargs ) -> None: r""" Check an HTML page generated by Sphinx for regressions, using `pytest-regressions`_. :param page: The page to test. :param jinja2: Whether to render the reference file as a jinja2 template. :param jinja2_namespace: If ``jinja2`` is :py:obj:`True`, a mapping of variable names to values to make available in the jinja2 template. :param \*\*kwargs: Additional keyword arguments passed to :meth:`pytest_regressions.file_regression.FileRegressionFixture.check`. .. versionchanged:: 2.14.0 Added the ``jinja2`` keyword argument. .. versionchanged:: 2.17.0 Added the ``jinja2_namespace`` keyword argument. .. latex:clearpage:: When ``jinja2`` is :py:obj:`True`, the reference file will be rendered as a jinja2 template. The template is passed the following variables: * ``sphinx_version`` -- the Sphinx version number, as a tuple of integers. * ``python_version`` -- the Python version number, in the form returned by :data:`sys.version_info`. * ``docutils_version`` -- the docutils version number, as a tuple of integers (*New in version 2.16.0*). **Example usage** .. code-block:: python @pytest.mark.parametrize("page", ["index.html"], indirect=True) def test_page(page: BeautifulSoup, html_regression: HTMLRegressionFixture): html_regression.check(page, file_regression) """ # noqa: RST306 __tracebackhide__ = True page = remove_html_footer(page) page = remove_html_link_tags(page) for div in page.select("script"): if "_static/language_data.js" in str(div): div.extract() for div in page.select("div.sphinxsidebar"): div.extract() for div in page.select("div.related"): if div["aria-label"] == "Related": div.extract() if sphinx.version_info >= (4, 3): # pragma: no cover for div in page.select("dt.sig em.property span.k"): div.replace_with_children() for div in page.select("span.w"): div.extract() for div in page.select("span.p"): if div.string == '=': sibling = div.next_sibling assert sibling is not None div.replace_with('') # type: ignore[arg-type] sibling.replace_with(f"= {sibling.text}") # type: ignore[arg-type] kwargs.pop("encoding", None) kwargs.pop("extension", None) if jinja2: def check_fn(obtained_filename: PathPlus, expected_filename: PathPlus): # noqa: MAN002 __tracebackhide__ = True expected_filename = PathPlus(expected_filename) template = Template(expected_filename.read_text()) expected_filename.write_text( template.render( sphinx_version=sphinx.version_info, python_version=sys.version_info, docutils_version=docutils_version, **jinja2_namespace or {}, ) ) return check_text_files(obtained_filename, expected_filename, encoding="UTF-8") else: check_fn = partial(check_text_files, encoding="UTF-8") super().check( str(StringList(page.prettify())), encoding="UTF-8", extension=extension, check_fn=check_fn, ) @pytest.fixture() def html_regression(datadir, original_datadir, request) -> HTMLRegressionFixture: # noqa: MAN001 """ Returns an :class:`~.HTMLRegressionFixture` scoped to the test function. .. versionadded:: 2.0.0 """ return HTMLRegressionFixture(datadir, original_datadir, request) def check_asset_copy( func: Callable[[sphinx.application.Sphinx, Exception], Any], *asset_files: PathLike, file_regression: FileRegressionFixture, ) -> None: r""" Helper to test functions which respond to Sphinx ``build-finished`` events and copy asset files. .. versionadded:: 2.0.0 :param func: The function to test. :param \*asset_files: The paths of asset files copied by the function, relative to the Sphinx output directory. :param file_regression: """ __tracebackhide__ = True with tempfile.TemporaryDirectory() as tmpdir: tmp_pathplus = PathPlus(tmpdir) fake_app = SimpleNamespace() fake_app.builder = SimpleNamespace() fake_app.builder.format = "html" fake_app.outdir = fake_app.builder.outdir = tmp_pathplus func(fake_app, None) # type: ignore[arg-type] for filename in asset_files: filename = tmp_pathplus / filename check_file_output(filename, file_regression, extension=f"_{filename.stem}{filename.suffix}") _latex_date_re = re.compile(r"\\date{.*}") class LaTeXRegressionFixture(AdvancedFileRegressionFixture): """ Subclass of :class:`coincidence.regressions.AdvancedFileRegressionFixture` for checking LaTeX files. .. versionadded:: 2.17.0 """ def check( # type: ignore[override] # noqa: MAN002 self, contents: Union[str, StringList], *, extension: str = ".html", jinja2: bool = False, jinja2_namespace: Optional[Dict[str, Any]] = None, **kwargs ): r""" Check a LaTeX file generated by Sphinx for regressions, using `pytest-regressions `__ :param contents: :param jinja2: Whether to render the reference file as a jinja2 template. :param jinja2_namespace: If ``jinja2`` is :py:obj:`True`, a mapping of variable names to values to make available in the jinja2 template. :param \*\*kwargs: Additional keyword arguments passed to :meth:`pytest_regressions.file_regression.FileRegressionFixture.check`. When ``jinja2`` is :py:obj:`True`, the reference file will be rendered as a jinja2 template. The template is passed the following variables: * ``sphinx_version`` -- the Sphinx version number, as a tuple of integers. * ``python_version`` -- the Python version number, in the form returned by :data:`sys.version_info`. * ``docutils_version`` -- the docutils version number, as a tuple of integers (*New in version 2.16.0*). .. note:: Unlike standard HTML jinja2 templates, this class expects the use of ``<`` and ``>`` rather than ``{`` and ``}``. For example:: <% if foo %> <# This should only happen on Tuesdays #> << foo.upper() >> <% endif %> **Example usage** .. code-block:: python @pytest.mark.sphinx("latex") def test_latex_output(app: Sphinx, latex_regression: LaTeXRegressionFixture): app.build() output_file = app.outdir / "python.tex" latex_regression.check(output_file.read_text()) """ # noqa: D400 __tracebackhide__ = True if jinja2: def check_fn(obtained_filename: PathPlus, expected_filename: PathLike): # noqa: MAN002 __tracebackhide__ = True expected_filename = PathPlus(expected_filename) template = Template( expected_filename.read_text(), block_start_string="<%", block_end_string="%>", variable_start_string="<<", variable_end_string=">>", comment_start_string="<#", comment_end_string="#>", ) expected_filename.write_text( template.render( sphinx_version=sphinx.version_info, python_version=sys.version_info, docutils_version=docutils_version, **jinja2_namespace or {}, ) ) return check_text_files(obtained_filename, expected_filename, encoding="UTF-8") else: check_fn = partial(check_text_files, encoding="UTF-8") new_contents = _latex_date_re.sub( r"\\date{Mar 11, 2021}", str(contents).replace("\\sphinxAtStartPar\n", ''), ) new_contents = new_contents.replace("%% let collapsible ", "%% let collapsable ") # changed in Sphinx 4.2 return super().check( new_contents, extension=".tex", check_fn=check_fn, ) @pytest.fixture() def latex_regression(datadir, original_datadir, request) -> LaTeXRegressionFixture: # noqa: MAN001 """ Returns a :class:`~.LaTeXRegressionFixture` scoped to the test function. .. versionadded:: 2.17.0 """ return LaTeXRegressionFixture(datadir, original_datadir, request) sphinx-toolbox-3.9.0/sphinx_toolbox/tweaks/000077500000000000000000000000001475757360300211165ustar00rootroot00000000000000sphinx-toolbox-3.9.0/sphinx_toolbox/tweaks/__init__.py000066400000000000000000000023631475757360300232330ustar00rootroot00000000000000#!/usr/bin/env python3 # # tweaks.py """ Small tweaks for Sphinx you may or may not want. .. versionadded:: 0.9.0 """ # # Copyright Β© 2020 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. # sphinx-toolbox-3.9.0/sphinx_toolbox/tweaks/footnote_symbols.py000066400000000000000000000051731475757360300251030ustar00rootroot00000000000000#!/usr/bin/env python3 # # footnote_symbols.py r""" Tweak which monkeypatches docutils to use the following symbols for footnotes: .. rst-class:: bullet-hidden * † -- dagger * ‑ -- double dagger * Β§ -- section mark * ΒΆ -- paragraph mark (pilcrow) * # -- number sign * β™  -- spade suit * β™₯ -- heart suit * ♦ -- diamond suit * ♣ -- club suit With some themes the superscript asterisk becomes very hard to see. .. versionadded:: 2.7.0 .. extensions:: sphinx_toolbox.tweaks.footnote_symbols ----- """ # noqa: D400 # # 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 List # 3rd party from docutils.transforms.references import Footnotes from sphinx.application import Sphinx # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ("setup", ) #: The list of symbols symbols: List[str] = [ # Entries 1-3 and 5 below are from section 12.51 of # The Chicago Manual of Style, 14th edition. '†', # dagger † '‑', # double dagger ‡ 'Β§', # section mark § 'ΒΆ', # paragraph mark (pilcrow) ¶ # (parallels ['||'] in CMoS) '#', # number sign # The entries below were chosen arbitrarily. 'β™ ', # spade suit ♠ 'β™₯', # heart suit ♥ '♦', # diamond suit ♦ '♣', # club suit ♣ ] @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.tweaks.footnote_symbols`. :param app: The Sphinx application. """ Footnotes.symbols = symbols return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/tweaks/latex_layout.py000066400000000000000000000060751475757360300242120ustar00rootroot00000000000000#!/usr/bin/env python3 # # latex_layout.py r""" Makes minor adjustments to the LaTeX layout. * Increases the whitespace above function signatures by 5px, to prevent the function visually merging with the previous one. * Remove unnecessary indentation and allow "raggedright" for the fields in the body of functions, which prevents ugly whitespace and line breaks. * Disables justification for function signatures. This is a backport of changes from Sphinx 4 added in :github:pull:`8997 `. .. versionadded:: 2.12.0 * With Sphinx 3.5, doesn't add ``\sphinxAtStartPar`` before every paragraph. The change in :github:issue:`8781 ` was to solve an issue with *tables*, but it isn't clear why it then gets added for *every* paragraph so this extension removes it. .. versionadded:: 2.13.0 * Configures hyperref to apply correct page numbering to the frontmatter. .. versionadded:: 2.14.0 * Optionally, configures the ``needspace`` package. The :confval:`needspace_amount` option can be set in ``conf.py`` to add the ``\needspace{}`` command before each ``addnodes.desc`` node (i.e. a function or class description). The amount of space is set by the ``needspace_amount`` option, e.g.: .. code-block:: python needspace_amount = r"4\baselineskip" .. versionadded:: 3.0.0 .. versionadded:: 2.10.0 .. extensions:: sphinx_toolbox.tweaks.latex_layout .. versionchanged:: 3.0.0 The functionality has moved to :mod:`sphinx_toolbox.latex.toc`. Please use that extension instead. ----- """ # # 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. # # 3rd party from sphinx.application import Sphinx # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ("setup", ) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.tweaks.latex_layout`. :param app: The Sphinx application. """ app.setup_extension("sphinx_toolbox.latex.layout") return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/tweaks/latex_toc.py000066400000000000000000000044351475757360300234600ustar00rootroot00000000000000#!/usr/bin/env python3 # # latex_toc.py """ Adjusts the default LaTeX output as follows: * The captions from ``toctree`` directives are converted into document parts. * The PDF outline has the correct hierarchy, including having the indices as top-level elements. .. versionadded:: 2.1.0 .. extensions:: sphinx_toolbox.tweaks.latex_toc .. versionchanged:: 3.0.0 The functionality has moved to :mod:`sphinx_toolbox.latex.toc`. Please use that extension instead. ----- """ # noqa: D400 # # 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 # 3rd party from sphinx.application import Sphinx # this package from sphinx_toolbox.latex import use_package from sphinx_toolbox.utils import Config, SphinxExtMetadata, metadata_add_version __all__ = ("setup", "configure") def configure(app: Sphinx, config: Config) -> None: """ Configure :mod:`sphinx_toolbox.tweaks.latex_toc`. :param app: The Sphinx application. :param config: """ use_package("bookmark", config) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.tweaks.latex_toc`. :param app: The Sphinx application. """ app.setup_extension("sphinx_toolbox.latex.toc") return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/tweaks/param_dash.py000066400000000000000000000125351475757360300235750ustar00rootroot00000000000000#!/usr/bin/env python3 # # param_dash.py """ Monkeypatches :class:`sphinx.util.docfields.TypedField` to only output the endash (--) separating the parameter name from its description if a description was given. .. versionadded:: 0.9.0 :bold-title:`Example` .. rest-example:: .. class:: MyClass(foo, bar) This is my class. :param foo: An argument :param bar: .. extensions:: sphinx_toolbox.tweaks.param_dash ----- """ # noqa: D400 # # Copyright Β© 2020 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. # # Parts based on https://github.com/sphinx-doc/sphinx # | Copyright (c) 2007-2020 by the Sphinx team (see AUTHORS file). # | BSD Licensed # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib from typing import Dict, List, Optional, Tuple # 3rd party import sphinx.util.docfields from docutils import nodes from sphinx import addnodes from sphinx.application import Sphinx from sphinx.environment import BuildEnvironment # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ("setup", ) def make_field( self, types: Dict[str, List[nodes.Node]], domain: str, items: Tuple, env: Optional[BuildEnvironment] = None, **kwargs, # To support Sphinx 4.1 and later ) -> nodes.field: def handle_item(fieldarg: str, content: List[nodes.Element]) -> nodes.paragraph: par = nodes.paragraph() par.extend( self.make_xrefs( self.rolename, domain, fieldarg, addnodes.literal_strong, env=env, **kwargs, # To support Sphinx 4.1 and later ) ) if fieldarg in types: par += nodes.Text(" (") # NOTE: using .pop() here to prevent a single type node to be # inserted twice into the doctree, which leads to # inconsistencies later when references are resolved fieldtype = types.pop(fieldarg) if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text): typename = fieldtype[0].astext() par.extend( self.make_xrefs(self.typerolename, domain, typename, addnodes.literal_emphasis, env=env) ) else: par += fieldtype par += nodes.Text(')') if (content and len(content) == 1 and isinstance(content[0], nodes.inline) and not content[0].children): return par par += nodes.Text(" -- ") par += content return par fieldname = nodes.field_name('', self.label) bodynode: nodes.Node if len(items) == 1 and self.can_collapse: fieldarg, content = items[0] bodynode = handle_item(fieldarg, content) else: bodynode = self.list_type() for fieldarg, content in items: bodynode += nodes.list_item('', handle_item(fieldarg, content)) # type: ignore[assignment,operator] fieldbody = nodes.field_body('', bodynode) return nodes.field('', fieldname, fieldbody) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.tweaks.param_dash`. :param app: The Sphinx application. """ sphinx.util.docfields.TypedField.make_field = make_field # type: ignore[assignment] return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/tweaks/revert_footnote_style.py000066400000000000000000000137121475757360300261400ustar00rootroot00000000000000#!/usr/bin/env python3 # # revert_footnote_style.py """ Reverts the docutils footnote behaviour from ``>=0.18`` to that of ``<=0.17``. .. versionadded:: 3.1.2 .. extensions:: sphinx_toolbox.tweaks.revert_footnote_style ----- """ # # 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. # # Based on docutils # | Copyright Β© 2016 David Goodger, GΓΌnter Milde # | BSD Licensed # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # 3rd party import docutils from sphinx.application import Sphinx from sphinx.writers.html import HTMLTranslator from sphinx.writers.html5 import HTML5Translator # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ["setup"] def visit_footnote(self: HTML5Translator, node: docutils.nodes.footnote) -> None: # pragma: no cover if not self.in_footnote_list: listnode = node.copy() listnode["ids"] = [] classes = [node.tagname, self.settings.footnote_references] self.body.append(self.starttag(listnode, "dl", CLASS=' '.join(classes))) # role="note" self.in_footnote_list = True def depart_footnote(self, node: docutils.nodes.footnote) -> None: # pragma: no cover self.body.append('\n') if not isinstance(node.next_node(descend=False, siblings=True), docutils.nodes.footnote): self.body.append('\n') self.in_footnote_list = False def visit_footnote_reference(self, node: docutils.nodes.footnote_reference) -> None: # pragma: no cover href = '#' + node["refid"] classes = ["footnote-reference", self.settings.footnote_references] self.body.append(self.starttag(node, 'a', suffix='', CLASS=' '.join(classes), href=href)) # role='doc-noteref' def depart_footnote_reference(self, node: docutils.nodes.footnote_reference) -> None: # pragma: no cover self.body.append("") # footnote and citation labels: def visit_label(self, node: docutils.nodes.label) -> None: # pragma: no cover if (isinstance(node.parent, docutils.nodes.footnote)): classes = self.settings.footnote_references else: classes = "brackets" # pass parent node to get id into starttag: self.body.append(self.starttag(node.parent, "dt", '', CLASS="label")) self.body.append(self.starttag(node, "span", '', CLASS=classes)) # footnote/citation backrefs: if self.settings.footnote_backlinks: backrefs = node.parent.get("backrefs", []) if len(backrefs) == 1: self.body.append('' % backrefs[0]) def depart_label(self, node: docutils.nodes.label) -> None: # pragma: no cover if self.settings.footnote_backlinks: backrefs = node.parent["backrefs"] if len(backrefs) == 1: self.body.append("") self.body.append("") if self.settings.footnote_backlinks and len(backrefs) > 1: backlinks = [f'{i}' for (i, ref) in enumerate(backrefs, 1)] self.body.append('(%s)' % ','.join(backlinks)) self.body.append('\n
') @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.tweaks.revert_footnote_style`. :param app: The Sphinx application. """ if docutils.__version_info__ >= (0, 18): # pragma: no cover app.add_node( docutils.nodes.footnote, html=(visit_footnote, depart_footnote), override=True, ) app.add_node( docutils.nodes.footnote_reference, html=(visit_footnote_reference, depart_footnote_reference), override=True, ) app.add_node( docutils.nodes.label, html=(visit_label, depart_label), override=True, ) HTMLTranslator.in_footnote_list = False HTML5Translator.in_footnote_list = False return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/tweaks/sphinx_panels_tabs.py000066400000000000000000000100511475757360300253510ustar00rootroot00000000000000#!/usr/bin/env python3 # # sphinx_panels_tabs.py """ Tweak to :github:repo:`executablebooks/sphinx-tabs` to fix a CSS conflict with :github:repo:`executablebooks/sphinx-panels`. Fix for :github:issue:`51 `. .. versionadded:: 1.9.0 .. extensions:: sphinx_toolbox.tweaks.sphinx_panels_tabs ----- """ # noqa: D400 # # 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. # # Parts based on https://github.com/executablebooks/sphinx-panels # Copyright (c) 2020 Executable Books # MIT Licensed # # stdlib from typing import Optional # 3rd party import dict2css from docutils import nodes from domdf_python_tools.paths import PathPlus from sphinx.application import Sphinx from sphinx.writers.html5 import HTML5Translator # this package from sphinx_toolbox import _css from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ("copy_asset_files", "setup") def visit_container(self: HTML5Translator, node: nodes.container) -> None: classes = "docutils container" if node.get("is_div", False): # we don't want the CSS for container for these nodes classes = "docutils" if any(c.startswith("sphinx-data-tab-") for c in node["classes"]): classes = "docutils" self.body.append(self.starttag(node, "div", CLASS=classes)) def depart_container(self: HTML5Translator, node: nodes.Node) -> None: self.body.append("\n") def copy_asset_files(app: Sphinx, exception: Optional[Exception] = None) -> None: """ Copy asset files to the output. :param app: The Sphinx application. :param exception: Any exception which occurred and caused Sphinx to abort. .. versionchanged:: 2.7.0 Renamed from ``copy_assets``. The former name was kept as an alias until version 3.0.0 """ if exception: # pragma: no cover return if app.builder is None or app.builder.format.lower() != "html": # pragma: no cover return # style = StringList([ # ".docutils.container {", # " padding-left: 0 !important;", # " padding-right: 0 !important;", # '}', # '', # # "div.sphinx-tabs.docutils.container {", # # " padding-left: 0 !important;", # # " padding-right: 0 !important;", # # "}", # # '', # "div.ui.top.attached.tabular.menu.sphinx-menu.docutils.container {", # # " padding-left: 0 !important;", # # " padding-right: 0 !important;", # " margin-left: 0 !important;", # " margin-right: 0 !important;", # '}', # ]) css_static_dir = PathPlus(app.builder.outdir) / "_static" / "css" css_static_dir.maybe_make(parents=True) dict2css.dump(_css.tweaks_sphinx_panels_tabs_styles, css_static_dir / "tabs_customise.css") @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.tweaks.sphinx_panels_tabs`. :param app: The Sphinx application. """ app.setup_extension("sphinx_tabs.tabs") app.setup_extension("sphinx_toolbox._css") app.add_node(nodes.container, override=True, html=(visit_container, depart_container)) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/tweaks/tabsize.py000066400000000000000000000050731475757360300231360ustar00rootroot00000000000000#!/usr/bin/env python3 # # tabsize.py r""" Hack to get the docutils tab size, as there doesn't appear to be any other way. .. versionadded:: 1.0.0 You probably don't need to use this extension directly, but if you're developing an extension of your own you can enable it like so: .. code-block:: def setup(app: Sphinx) -> Dict[str, Any]: app.setup_extension('sphinx_toolbox.github') return {} This will guarantee that the following value will be available via :attr:`app.config `: * **docutils_tab_width** (:class:`int`\) -- The number of spaces that correspond to a tab when Docutils parses source files. """ # # Copyright Β© 2020 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 Union # 3rd party from docutils.nodes import document from docutils.statemachine import StringList from sphinx.application import Sphinx from sphinx.parsers import RSTParser # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ("setup", ) @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.tweaks.tabsize`. :param app: The Sphinx application. """ class CustomRSTParser(RSTParser): def parse(self, inputstring: Union[str, StringList], document: document) -> None: app.config.docutils_tab_width = document.settings.tab_width # type: ignore[attr-defined] super().parse(inputstring, document) app.add_source_parser(CustomRSTParser, override=True) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox/utils.py000066400000000000000000000504471475757360300213440ustar00rootroot00000000000000#!/usr/bin/env python3 # # utils.py """ General utility functions. """ # # Copyright Β© 2020-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. # # singleton function based on attrs # https://github.com/python-attrs/attrs # Copyright (c) 2015 Hynek Schlawack # MIT Licensed # # stdlib import atexit import functools import re import sys from typing import ( TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Mapping, Optional, Pattern, Set, Tuple, Type, TypeVar, cast ) # 3rd party import sphinx.config from apeye.requests_url import RequestsURL from docutils.nodes import Node from domdf_python_tools.doctools import prettify_docstrings from sphinx.addnodes import desc_content from sphinx.application import Sphinx from sphinx.environment import BuildEnvironment from sphinx.ext.autodoc import Documenter, logger from sphinx.locale import __ from typing_extensions import TypedDict __all__ = ( "add_nbsp_substitution", "allow_subclass_add", "baseclass_is_private", "code_repr", "escape_trailing__", "filter_members_warning", "flag", "get_first_matching", "GITHUB_COM", "is_namedtuple", "make_github_url", "metadata_add_version", "NoMatchError", "OptionSpec", "Param", "parse_parameters", "Purger", "SetupFunc", "SphinxExtMetadata", "typed_flag_regex", "typed_param_regex", "unknown_module_warning", "untyped_param_regex", "add_fallback_css_class", ) #: Instance of :class:`apeye.requests_url.RequestsURL` that points to the GitHub website. GITHUB_COM: RequestsURL = RequestsURL("https://github.com") #: Type hint for the ``option_spec`` variable of Docutils directives. OptionSpec = Mapping[str, Callable[[str], Any]] _T = TypeVar("_T") atexit.register(GITHUB_COM.session.close) @functools.lru_cache() def make_github_url(username: str, repository: str) -> RequestsURL: """ Construct a URL to a GitHub repository from a username and repository name. :param username: The username of the GitHub account that owns the repository. :param repository: The name of the repository. """ return GITHUB_COM / username / repository def flag(argument: Any) -> bool: """ Check for a valid flag option (no argument) and return :py:obj:`True`. Used in the ``option_spec`` of directives. .. seealso:: :class:`docutils.parsers.rst.directives.flag`, which returns :py:obj:`None` instead of :py:obj:`True`. :raises: :exc:`ValueError` if an argument is given. """ if argument and argument.strip(): raise ValueError(f"No argument is allowed; {argument!r} supplied") else: return True @prettify_docstrings class Purger: """ Class to purge redundant nodes. :param attr_name: The name of the build environment's attribute that stores the list of nodes, e.g. ``all_installation_nodes``. .. autosummary-widths:: 55/100 """ def __init__(self, attr_name: str): self.attr_name = str(attr_name) def __repr__(self) -> str: return f"{self.__class__.__name__}({self.attr_name!r})" def purge_nodes( # pragma: no cover self, app: Sphinx, env: BuildEnvironment, docname: str, ) -> None: """ Remove all redundant nodes. This function can be configured for the :event:`env-purge-doc` event: .. code-block:: my_node_purger = Purger("all_my_node_nodes") def setup(app: Sphinx): app.connect("env-purge-doc", my_node_purger.purge_nodes) :param app: The Sphinx application. :param env: The Sphinx build environment. :param docname: The name of the document to remove nodes for. """ if not hasattr(env, self.attr_name): return all_nodes = [todo for todo in getattr(env, self.attr_name) if todo["docname"] != docname] setattr(env, self.attr_name, all_nodes) def get_outdated_docnames( self, app: Sphinx, env: BuildEnvironment, added: Set[str], changed: Set[str], removed: Set[str], ) -> List[str]: """ Returns a list of all docnames containing one or more nodes this :class:`~.Purger` is aware of. This function can be configured for the :event:`env-get-outdated` event: .. code-block:: my_node_purger = Purger("all_my_node_nodes") def setup(app: Sphinx): app.connect("env-get-outdated", my_node_purger.get_outdated_docnames) .. versionadded:: 2.7.0 :param app: The Sphinx application. :param env: The Sphinx build environment. :param added: A set of newly added documents. :param changed: A set of document names whose content has changed. :param removed: A set of document names which have been removed. """ if not hasattr(env, self.attr_name): return [] return list({todo["docname"] for todo in getattr(env, self.attr_name)}) def add_node(self, env: BuildEnvironment, node: Node, targetnode: Node, lineno: int) -> None: """ Add a node. :param env: The Sphinx build environment. :param node: :param targetnode: :param lineno: """ if not hasattr(env, self.attr_name): setattr(env, self.attr_name, []) all_nodes = getattr(env, self.attr_name) all_nodes.append({ "docname": env.docname, "lineno": lineno, "installation_node": node.deepcopy(), "target": targetnode, }) def singleton(name: str) -> object: """ Factory function to return a string singleton. :param name: The name of the singleton. """ name = str(name) class Singleton: _singleton = None def __new__(cls): if Singleton._singleton is None: Singleton._singleton = super().__new__(cls) return Singleton._singleton def __repr__(self) -> str: return name def __str__(self) -> str: return name Singleton.__name__ = name Singleton.__doc__ = f"Singleton {name}" return Singleton() no_default = singleton("no_default") class NoMatchError(ValueError): """ Raised when no matching values were found in :func:`~.get_first_matching`. .. versionadded:: 0.7.0 """ def get_first_matching( condition: Callable[[Any], bool], iterable: Iterable[_T], default: _T = no_default # type: ignore[assignment] ) -> _T: """ Returns the first value in ``iterable`` that meets ``condition``, or ``default`` if none match. .. versionadded:: 0.7.0 :param condition: The condition to evaluate. :param iterable: :param default: The default value to return if no values in ``iterable`` match. """ if default is not no_default: if not condition(default): raise ValueError("The condition must evaluate to True for the default value.") iterable = (*iterable, default) for match in iterable: if condition(match): return match raise NoMatchError(f"No matching values for '{condition}' in {iterable}") def escape_trailing__(string: str) -> str: """ Returns the given string with trailing underscores escaped to prevent Sphinx treating them as references. .. versionadded:: 0.8.0 :param string: """ if string.endswith('_'): return f"{string[:-1]}\\_" return string def code_repr(obj: Any) -> str: """ Returns the repr of the given object as reStructuredText inline code. .. versionadded:: 0.9.0 :param obj: """ return f"``{obj!r}``" class SphinxExtMetadata(TypedDict, total=False): """ :class:`typing.TypedDict` representing the metadata dictionary returned by Sphinx extensions' ``setup`` functions. This is treated by Sphinx as metadata of the extension. """ # noqa: D400 version: str """ A string that identifies the extension version. It is used for extension version requirement checking and informational purposes. If not given, ``'unknown version'`` is substituted. """ env_version: int """ An integer that identifies the version of env data structure if the extension stores any data to environment. It is used to detect the data structure has been changed from last build. Extensions have to increment the version when data structure has changed. If not given, Sphinx considers the extension does not stores any data to environment. """ parallel_read_safe: bool """ A boolean that specifies if parallel reading of source files can be used when the extension is loaded. It defaults to :py:obj:`False`, i.e. you have to explicitly specify your extension to be parallel-read-safe after checking that it is. """ parallel_write_safe: bool """ A boolean that specifies if parallel writing of output files can be used when the extension is loaded. Since extensions usually don’t negatively influence the process, this defaults to :py:obj:`True`. """ SetupFunc = Callable[[Sphinx], Optional[SphinxExtMetadata]] """ Type annotation for Sphinx extensions' ``setup`` functions. .. versionadded:: 1.9.0 """ def unknown_module_warning(documenter: Documenter) -> None: """ Log a warning that the module to import the object from is unknown. .. versionadded:: 0.2.0 :param documenter: """ msg = __( "don't know which module to import for autodocumenting %r " '(try placing a "module" or "currentmodule" directive in the document, ' "or giving an explicit module name)" ) logger.warning(msg % documenter.name, type="autodoc") def filter_members_warning(member: Any, exception: Exception) -> None: """ Log a warning when filtering members. .. versionadded:: 0.2.0 :param member: :param exception: """ logger.warning( __("autodoc: failed to determine %r to be documented, the following exception was raised:\n%s"), member, exception, type="autodoc" ) class Param(TypedDict): """ :class:`~typing.TypedDict` to represent a parameter parsed from a class or function's docstring. .. versionadded:: 0.8.0 """ #: The docstring of the parameter. doc: List[str] #: The type of the parameter. type: str # noqa: A003 # pylint: disable=redefined-builtin _identifier_pattern = r"[A-Za-z_]\w*" typed_param_regex: Pattern[str] = re.compile( fr"^:(param|parameter|arg|argument)\s*({_identifier_pattern}\s+)({_identifier_pattern}\s*):\s*(.*)", flags=re.ASCII, ) """ Regex to match ``:param : `` flags. .. versionadded:: 0.8.0 """ untyped_param_regex: Pattern[str] = re.compile( fr"^:(param|parameter|arg|argument)\s*({_identifier_pattern}\s*):\s*(.*)", flags=re.ASCII, ) """ Regex to match ``:param : `` flags. .. versionadded:: 0.8.0 """ typed_flag_regex: Pattern[str] = re.compile( fr"^:(paramtype|type)\s*({_identifier_pattern}\s*):\s*(.*)", flags=re.ASCII, ) """ Regex to match ``:type : `` flags. .. versionadded:: 0.8.0 """ def parse_parameters(lines: List[str], tab_size: int = 8) -> Tuple[Dict[str, Param], List[str], List[str]]: """ Parse parameters from the docstring of a class/function. .. versionadded:: 0.8.0 :param lines: The lines of the docstring :param tab_size: :return: A mapping of parameter names to their docstrings and types, a list of docstring lines that appeared before the parameters, and the list of docstring lines that appear after the parameters. """ a_tab = ' ' * tab_size params: Dict[str, Param] = {} last_arg: Optional[str] = None pre_output: List[str] = [] post_output: List[str] = [] def add_empty(param_name: str) -> None: if param_name not in params: params[param_name] = {"doc": [], "type": ''} for line in lines: if post_output: post_output.append(line) continue # pylint: disable=loop-global-usage typed_m = typed_param_regex.match(line) untyped_m = untyped_param_regex.match(line) type_only_m = typed_flag_regex.match(line) # pylint: enable=loop-global-usage if typed_m: last_arg = typed_m.group(3).strip() add_empty(cast(str, last_arg)) params[last_arg]["doc"] = [typed_m.group(4)] params[last_arg]["type"] = typed_m.group(2).strip() elif untyped_m: last_arg = untyped_m.group(2).strip() add_empty(cast(str, last_arg)) params[last_arg]["doc"] = [untyped_m.group(3)] elif type_only_m: add_empty(type_only_m.group(2)) params[type_only_m.group(2)]["type"] = type_only_m.group(3) elif line.startswith(a_tab) and last_arg is not None: params[last_arg]["doc"].append(line) elif last_arg is None: pre_output.append(line) else: post_output.append(line) return params, pre_output, post_output def is_namedtuple(obj: Any) -> bool: """ Returns whether the given object is a :func:`collections.namedtuple` class. .. versionadded:: 0.8.0 :param obj: """ return isinstance(obj, type) and issubclass(obj, tuple) and hasattr(obj, "_fields") def allow_subclass_add(app: Sphinx, *documenters: Type[Documenter]) -> None: """ Add the given autodocumenters, but only if a subclass of it is not already registered. This allows other libraries to extend the autodocumenters. .. versionadded:: 0.8.0 :param app: The Sphinx application. :param documenters: """ for cls in documenters: existing_documenter = app.registry.documenters.get(cls.objtype) if existing_documenter is None or not issubclass(existing_documenter, cls): app.add_autodocumenter(cls, override=True) def baseclass_is_private(obj: Type) -> bool: """ Returns :py:obj:`True` if the first and only base class starts with a double underscore. :param obj: """ if hasattr(obj, "__bases__") and len(obj.__bases__) == 1: return obj.__bases__[0].__name__.startswith("__") return False def metadata_add_version(func: SetupFunc) -> SetupFunc: """ Internal decorator for Sphinx ``setup`` functions to add the ``sphinx-toolbox`` version number to the returned metadata dict. .. versionadded:: 1.9.0 :param func: """ # noqa: D400 @functools.wraps(func) def wrapper(app: Sphinx) -> SphinxExtMetadata: # this package from sphinx_toolbox import __version__ ret = func(app) or {} ret["version"] = __version__ return ret return wrapper def add_nbsp_substitution(config: sphinx.config.Config) -> None: """ Adds the ``|nbsp|`` substitution directive to the reStructuredText prolog. .. versionadded:: 2.1.0 :param config: """ nbsp_sub = ".. |nbsp| unicode:: 0xA0\n :trim:" if not config.rst_prolog: config.rst_prolog = '' # type: ignore[attr-defined] if nbsp_sub not in config.rst_prolog: config.rst_prolog = '\n'.join([config.rst_prolog, '', nbsp_sub]) # type: ignore[attr-defined] _OBJTYPES_CSS_FALLBACKS = { "namedtuple": "class", "protocol": "class", "typeddict": "class", } # From https://github.com/mansenfranzen/autodoc_pydantic/pull/86/files # MIT Licensed # Copyright (c) 2021 Franz WΓΆllert def add_fallback_css_class( objtypes_css_fallbacks: Dict[str, str] ) -> Callable[[Sphinx, str, str, desc_content], None]: """ Registers a transform which will edit the CSS classes of documented objects based on their ``objtype``. :param objtypes_css_fallbacks: A mapping of Sphinx objtypes to the CSS class which should be added to them. The class is usually the ``objtype`` attribute from the documenter's parent class. .. versionadded:: 2.16.0 Used as follows: .. code-block:: python app.connect("object-description-transform", add_fallback_css_class({"typeddict": "class"})) This will apply the transformation to documented objects with the ``typeddict`` CSS class by adding the ``class`` CSS class. :param objtypes_css_fallbacks: """ def func( app: Sphinx, domain: str, objtype: str, contentnode: desc_content, ) -> None: if objtype not in objtypes_css_fallbacks: return classes = contentnode.parent.attributes["classes"] # for older sphinx versions, add objtype explicitly if sphinx.version_info < (3, 6): classes.append(objtype) idx = classes.index(objtype) fallback = objtypes_css_fallbacks[objtype] classes.insert(idx, fallback) return func if TYPE_CHECKING: class Config(sphinx.config.Config): project: Any author: Any project_copyright: str copyright: str # noqa: A003 # pylint: disable=redefined-builtin version: Any release: Any today: Any today_fmt: str language: str locale_dirs: Any figure_language_filename: str gettext_allow_fuzzy_translations: Any master_doc: Any root_doc: Any source_suffix: Any source_encoding: Any exclude_patterns: Any default_role: str add_function_parentheses: Any add_module_names: Any trim_footnote_reference_space: Any show_authors: Any pygments_style: Any highlight_language: Any highlight_options: Any templates_path: Any template_bridge: Any keep_warnings: Any suppress_warnings: Any modindex_common_prefix: Any rst_epilog: str rst_prolog: str trim_doctest_flags: Any primary_domain: Any needs_sphinx: str needs_extensions: Any manpages_url: Any nitpicky: Any nitpick_ignore: Any nitpick_ignore_regex: Any numfig: Any numfig_secnum_depth: Any numfig_format: Any math_number_all: Any math_eqref_format: str math_numfig: Any tls_verify: Any tls_cacerts: Any user_agent: str smartquotes: Any smartquotes_action: Any smartquotes_excludes: Any config_values: Dict[str, Tuple] overrides: Dict values: Dict[str, Tuple] setup: Optional[Callable] extensions: List[str] latex_engine: str latex_documents: Any latex_logo: str latex_appendices: Any latex_use_latex_multicolumn: Any latex_use_xindy: bool latex_toplevel_sectioning: str latex_domain_indices: List latex_show_urls: Any latex_show_pagerefs: Any latex_elements: Any latex_additional_files: Any latex_theme: str latex_theme_options: Any latex_theme_path: Any latex_docclass: Any html_theme: Any html_theme_path: Any html_theme_options: Any html_title: str html_short_title: Any html_style: str html_logo: str html_favicon: str html_css_files: Any html_js_files: Any html_static_path: Any html_extra_path: Any html_last_updated_fmt: str html_sidebars: Any html_additional_pages: Any html_domain_indices: List html_add_permalinks: Any html_permalinks: Any html_permalinks_icon: Any html_use_index: Any html_split_index: Any html_copy_source: Any html_show_sourcelink: Any html_sourcelink_suffix: Any html_use_opensearch: Any html_file_suffix: str html_link_suffix: str html_show_copyright: Any html_show_search_summary: Any html_show_sphinx: Any html_context: Any html_output_encoding: Any html_compact_lists: Any html_secnumber_suffix: Any html_search_language: str html_search_options: Any html_search_scorer: Any html_scaled_image_link: Any html_baseurl: Any html_codeblock_linenos_style: str html_math_renderer: Any html4_writer: Any else: Config = sphinx.config.Config if sphinx.version_info[0] >= 5 or TYPE_CHECKING: # pragma no cover class RemovedInSphinx50Warning(RuntimeError): pass def prepare_docstring(s: str, ignore: Optional[int] = None, tabsize: int = 8) -> List[str]: if ignore is None: ignore = 1 else: raise TypeError("The 'ignore' argument to prepare_docstring() was removed in Sphinx 5.0") lines = s.expandtabs(tabsize).splitlines() # Find minimum indentation of any non-blank lines after ignored lines. margin = sys.maxsize for line in lines[ignore:]: content = len(line.lstrip()) if content: indent = len(line) - content margin = min(margin, indent) # Remove indentation from ignored lines. for i in range(ignore): if i < len(lines): lines[i] = lines[i].lstrip() if margin < sys.maxsize: for i in range(ignore, len(lines)): lines[i] = lines[i][margin:] # Remove any leading blank lines. while lines and not lines[0]: lines.pop(0) # make sure there is an empty line at the end if lines and lines[-1]: lines.append('') return lines else: # 3rd party from sphinx.deprecation import RemovedInSphinx50Warning # type: ignore[attr-defined,no-redef] # noqa: F401 from sphinx.util.docstrings import prepare_docstring # noqa: F401 sphinx-toolbox-3.9.0/sphinx_toolbox/wikipedia.py000066400000000000000000000114571475757360300221500ustar00rootroot00000000000000#!/usr/bin/env python3 # # wikipedia.py """ Sphinx extension to create links to Wikipedia articles. .. versionadded:: 0.2.0 .. extensions:: sphinx_toolbox.wikipedia Configuration -------------- .. latex:vspace:: -5px .. confval:: wikipedia_lang :type: :class:`str` :required: False :default: ``'en'`` The Wikipedia language to use for :rst:role:`wikipedia` roles. .. versionadded:: 0.2.0 Usage ------ .. latex:vspace:: -5px .. rst:role:: wikipedia Role which shows a link to the given article on Wikipedia. The title and language can be customised. :bold-title:`Example` .. rest-example:: :wikipedia:`Sphinx` :wikipedia:`mythical creature ` :wikipedia:`Answer to the Ultimate Question of Life, the Universe, and Everything <:de:42 (Antwort)>` .. only:: html .. rest-example:: :wikipedia:`:zh:ζ–―θŠ¬ε…‹ζ–―` API Reference ---------------- """ # # Copyright Β© 2020-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. # # Based on https://github.com/quiver/sphinx-ext-wikipedia # BSD Licensed # # Parts of the docstrings based on https://docutils.sourceforge.io/docs/howto/rst-roles.html # # stdlib import re from typing import Dict, List, Tuple from urllib.parse import quote # 3rd party from apeye.url import URL from docutils import nodes from docutils.nodes import system_message from docutils.parsers.rst.states import Inliner from sphinx.application import Sphinx from sphinx.util.nodes import split_explicit_title # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ("make_wikipedia_link", "setup") base_url = "https://%s.wikipedia.org/wiki" _wiki_lang_re = re.compile(":(.*?):(.*)") def _get_wikipedia_lang(inliner: Inliner) -> str: # pragma: no cover return inliner.document.settings.env.config.wikipedia_lang def make_wikipedia_link( name: str, rawtext: str, text: str, lineno: int, inliner: Inliner, options: Dict = {}, content: List[str] = [] ) -> Tuple[List[nodes.reference], List[system_message]]: """ Adds a link to the given article on :wikipedia:`Wikipedia`. :param name: The local name of the interpreted role, the role name actually used in the document. :param rawtext: A string containing the entire interpreted text input, including the role and markup. :param text: The interpreted text content. :param lineno: The line number where the interpreted text begins. :param inliner: The :class:`docutils.parsers.rst.states.Inliner` object that called :func:`~.source_role`. It contains the several attributes useful for error reporting and document tree access. :param options: A dictionary of directive options for customization (from the ``role`` directive), to be interpreted by the function. Used for additional attributes for the generated elements and other functionality. :param content: A list of strings, the directive content for customization (from the ``role`` directive). To be interpreted by the function. :return: A list containing the created node, and a list containing any messages generated during the function. """ text = nodes.unescape(text) has_explicit, title, target = split_explicit_title(text) m = _wiki_lang_re.match(target) if m: lang, target = m.groups() if not has_explicit: title = target else: lang = _get_wikipedia_lang(inliner) ref = URL(base_url % lang) / quote(target.replace(' ', '_'), safe='') node = nodes.reference(rawtext, title, refuri=str(ref), **options) return [node], [] @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.wikipedia`. .. versionadded:: 1.0.0 :param app: The Sphinx application. """ app.add_role("wikipedia", make_wikipedia_link) app.add_config_value("wikipedia_lang", "en", "env", [str]) return {"parallel_read_safe": True} sphinx-toolbox-3.9.0/sphinx_toolbox_square.png000066400000000000000000002141351475757360300217140ustar00rootroot00000000000000‰PNG  IHDRΪZJP pHYs½½<―tEXtSoftwarewww.inkscape.org›ξ< IDATxœμέyœ[ΥaχοΉWΫHšΝφx{Ό€ΑΖΫ,a‡,%!„¬NRš΄MH›§iŸ€6€―gϊk0ς#)ύ5mν―-»! aKΨ’HΜn6ολx<›€ΡvοyώF£™‘43ΆgŒνΟ›—±tοΡΥ‘Ζ#έο=›ŽZζPWΐ‘α–[n©σΌδτΌ L2y―ΞΊ6βψŽρίΊΦΝxŽI9^>ΡΧηmmkkKκϊ€G˜0jmmmN$Y"Χ;Ρϊ:Ι8:QV'H:FRΓΥ#i»Œ^΅Ύ^–±/ΚsŸ[΅jΥΫγSs0F₯­­-© ώ»Œ>3ŽO³]/¬τ˜ŸΣ½Χ]wέξq|.p€FΤΦ֏D?”ΜLΰΣϊVzΚ1φ/ηόΧ΅Χ^Ϋ>Ο F0 ¦o}«mJήή/ιτCX¬¬~l_V]υ7Βz€2„ Uέxγ3­ρμ’C]—~VzΑHοΌΦγώλSŸϊ”w¨λΐь0 ’ΥίZ½Θρνƒ* ~'zUΖ^³ςͺoά{¨+ΐъ0`˜Υ7―>Σ±φ>I“ͺ•1Ζ¨©©Y ŠΗκUWW§P(¬H8¬@0(Η8r]WžηΙ·ΎςωΌ²™Œι΄ϊ}J${ΥΣέ­žήyށ40˜Gε;W\ΉrύμΒ€AΦ|λϊΚΧ:I±je\ΧΥ™gœ­iΣ¦πσYkΥέέ₯½νjίΫέ»wνOΈΘY«›buυχ•―|%sΐ•£B˜P²ζζλ?+«Ϋ%«• …Β:η¬sΥά\΅Ρβ€xž§={vkێ­Ϊ±c»ςωόXώŠ#}ξκ«―{~\*!L$έxσ7ΪZs‹j|.D£QsφωͺΧOHrΉœΆnέ¬·ή~S=½=£}XF2W¬ΌϊΪΟΊΒpԳ֚oΎ~΅dVΦ*ΧPί sΞ>_uuuU΅k­vξά‘ ―Ώͺ}ϋFυ#sW0ΉόΚ+―μηκ8ΜέΊ`A8ιyη[ί?Χ1f‘•ZdLΤZ»Χ1f›οϋΟcY΅iΣ¦C]Wΰ†0ζΪΪڜhtΞTίυ§ωrfΙΨiΖΨc$[uΕjIZ½zu‹°Ώ’4―VΉeKOΥά֚E&ΔΆν[υβK/(•JŽ\Ψθι€Ι}δλ_oΫ;ώ5Γ‘bŊu’fγΝ΄ŽfH&ndš}ߏI&nΜΐx"#›χ­ιuγϋΎίauίtψŽι0žΩΆnέΊ­’μ‘{5¨ε¦yσŽΛ[ϋUcνK©ΙΥcυ₯[―έΈρ§Q?ΰp@˜ήΑnψξw›M:0Ӛΐ cμL+;CΎ™)cfHv¦€’ŽU•1ΖΙΟYωΥ/mιy λIδ.iQ΅2Ζ-[zͺZηΜέί—sΠψΎ―Χ6Όͺ ―Ώ*ίχG*ώ†kό‹Ίκo6NDέpxψΰ?nhˆœΰs‚¬Yd€­tΌ ΏOs@PŸ€7JŒ}NžϋΜΊuλΆΔηΐ­™7―ΡZ»ZΦώ™jŒ«Ζσˆρύ}ΝζΝ―ŽCυ€Γ a˜`mmwDšn;Σχ½ιŽœYΎ5S±ΗΘΪ©2ζIΣ$MΥώŽkhε•_Όs4e‹+]? iy­r§œΌT ζw Υ:hΊ»»τάσΟͺ³«s€’[¬η\ΈjΥͺ·'’^xgY±b…λόΕΖ7§ΛΨӍΥι’K Βjm±FO«§ε;χ―[·Ž°;AΦΜ™s–5ζ.I³Λ·ΗQΌ±Q ŠD"’γΘσ<₯ υττ(HHvP#SŸ¬ύϊΚΝ›ΙΠϊ„£a8ˆΪΎ}GSΘj‰#o–oΜtc5SFΣ%ΝΜ +;Γά«ž5YΩΊφŠ/~y΄εΧ¬YΣhο^#[«άβE'ι„…U1&”οϋzρ₯υzσ­7F*ΊΕ5ώ΄PωV¬XαJz—=’.TαίsόΠΦjD/Xkb¬ϋ“uλΦ=/NNΗŚ9s>dY+)ΪΏΝ 4cφlM›>]‘PHF’)††ώ“$ΟZυ$ΪΊy³z::†φ§ΑPθς―Ώώ:έ)qT"L‰΅Φ¬ωΞνIzίD>―γ‚!…#ΉŽQWgWωξW]qωΙc9^[[[4 ή#«‹j•;ώΈ…Zr)ϋSεq±uΫ=χό³΅Χ¨0zΣΟ™³½φΪφ‰«&ΒΗ>φ±ΙΑ s‰5ΊT…Ρ|¨λtή–τ/'wΗ]wύtχ‘Μ‘β†9sώHΖά‘²nM“§NΥμγW(8!²Άt{h°°Φjχή½ΪΈaΓΠϚŽ1zΝƍ?χΌΓ&€ƒδ†[nΏBΖήr0ŽeŒ‘λΊ ‡C …Γ …B …B …B φ tχυ}_Ο<ρ€μΐ8_αΜ”U_ώςˆύ€Κ΅΅΅…"±ΰΙκ“΅ΚΝ›;_ο:e™Œyg|”tvξΣΣΏzRιtΊV±gΣ©άmmm©‰ͺΖNJ+fΧ¨υΝGdμy:΄έ–ΖCVŒ΅ί[»φžΗEkΕ~[έΪz†‘žPY˜=Ύ¦Ξ.τt28!ς3ωι΄|Ο“λΊ D"r†΄X€R)mxυU₯‰ς§±Vϊ~6ΈΊνΝ7G=—5pΈ{gœ‡Ή5·ά±Δ:ώod©VΖq9ŽQ0T$Q(R8V0*„†PH`H‘PPΑ`HŽλΞ¬τw—]k+ŸW¬ξyυφ |ΩKV^ρΕϋΗϊšΦ]λΎ½ιΫ%ύq­rΗ3[§zΖ;&P$ =ρΤγJ₯ͺg#σƒkΎφ3X-$ŸψΓO,6žύ˜qΜGe΅Lω{,hζΜ™ŠΗcŠΖ’ŠΕβ ‡ƒ ƒ Γ †ƒ­η29₯3}J§3Κd2Jχ₯Υέέ­ξξuww+›ΝhΥ~k¬ώΟΪ΅?όفθh³fήΌFωώο¬Tš=bφqΗiΚ±Η΄@X«\o―ς³ΩA­FR S΄₯En(TΪξ{žήή°A{φμϊ”[Œτη+7mzpΌ_πNπΞψφc·ήzk8ιΕ~#ιdIr]W“'5©eΪT…‚!…#Ő Κ δ§xyΡΚΪβ₯F+ΩΒ‡βνώΏΚΛT ›ήzKΫΆl-ΫboXuΕ―ݟΧf­57ήtΓ-2φ―k•›>m†ή}ΖYr]wžζ K₯’ϊŏ4}μWV^}έ?LT°ΪΪڜ—^{ιέΖڏJϊ¨€qύ‹E5gΡΝ£3fΘqœ>f_ΊO{Ϋ;΄{χnνΩ½G{ΪΫ•ΛζφηPΏ.† NTGι†ΦΦ;$ύiύ™­­š:Ύ€bKƒο+Ήs§ςΙδ°Ρ_Ζ¨0@;:mšBυυƒΚtμΩ£·ήxCωάΰŸ§‘ξ±ΧX›G:Βp€nψΞmί’ΥΧϊο·LnΦ ‹*ήΠPάRψ5+΅(CDa›-ΫWήςPΉΜ ΐQV¦\Ηή½zυΕ—ΚΆΨ'V]ρΕσθ5ή|ύjc΅ͺV™©S§ιΜ3ΞV πΞθi’H$τψ/U&S΅ΛSΦX{ξ5Χ|γ7Y/ŒlŊ!ςο1Ύω¨•ωˆ€ι‡ͺ.‘HDΗœN\t‚JΏΣΞZ«½ν{΅iσfmή΄YƒΗ:Ζ/ωωƒόθ₯‘‹½V·Άž`€—$Ή’TίΨ¨ωΛ—KSψ,υ<%ΆmS>.…iψX‰ς`Q7y²"S¦ΆΛδ3m|ύuu œέ'ι¦p>γ•ΫΆ±€&ŽHοŒΛˆΐajΝwώυ“Q •qΙΉ€&OͺX<ήξξςΑΩAIxŽσΗοknN~jώόξΫΉsΔΕq€Γ -ΐ~jϋφMaΩυ’-NζΝ=Vσ,γ \Λ*}A•΅8ψ₯°`…†*=—Ζμω_fИ+{Ξ΅W|ρ©=ξ7~σΛ֘T΅ίGcc£Ξ=ϋ|…ΓU‡L¨ν;ΆιWΏ~ΊF ϋ­•Wγͺ «Ζμ²Λ.kΞη3Λε˜ε²vΉ-¬…Ru9vcL§‘ής­έeŒIΗ »ΖΜς₯iΎηM±Φ†€> υυZΊό]Z°`ΑAι5ΤΆmΫυκ+―iΛ–-£Y”Q’6«Ώb<Εp7΄Άn”Τ*IS¦OWλ₯Εn€VιΞ.₯φμQ©•·σX<£S• Ηu›6MΑx|πξ|^;·nՎ ??#m΄“Ω΄ι?Ϋ$.bΰˆ@˜φΣκοάv—±* β2©I‹–œ¨pΈT&Ω—•ίίTnLαjfρορ°όζ†Χ΅kǎ Ζ\υΧ_Όρ`ϋΖWΞ{»j̜ΧλάsΞW΄.Z­Θ„zαΕυzγΝ Υv{ΦqΞ\υυUΟNdp`.»μ²ζhSτΓoΞιθΪΧh|4²χ9NοώιŽZ½τKλΓαπ)rόΣ%ξ8ξΎοMk΄lωRΝ_0\~Ÿ“Ι€~»υΪ°αυΪSΩοYί½bέΊut§‘tύόω'9žχbύN;Mυ--… 7ωΌΊ7n’υύξ£…&αqk£ ‘†E[Zδ[dϋƒE&ΦΆ΅wχξ‘‹έIλƘΏkž4ι_zξ9Z•pX#LϋaΝwn¬΅φ?ϊο‡CA-Y²HM“šUl=W_&§L6?(<”ώhp°8ΨφμΪ₯Χ_}m`ƒΥ}«ΌόΓλψ7άό͏kξ’Tυ o4ΣΉgŸ―xόΠ―f­ΥγΏxDϋ:χUάo€gΎκΪ³1L½y”ϊΔeŸ˜ηxϊ¨1ΞΗ%{¦΅vΤMMMM:ντΣΤΪ:{δΒϋ!™LθwΏ[― ―½>š–Šδ;ŸZ·n]Υτ|΄Έ‘΅u•€ΥRa6½εΈHŽγΚΚͺ―£CιŽ}Ε.e-‚EιvωŠ!γ)ŒγΖR45 Ϊ.I©dRΫή~[]{+i·Kχl.χΧnί>lΐp8 Lctγwn[h­~e₯¦ώm ζΟQλάVI…nMΎο«'Ρ7,H ΞΈ΄R€Σ}ϊν3Ώ.έ·ΎlχΦ–ΆΆΆƒΦWχΖ―ΏΠ:ϊ©lυ•…#‘ˆΞ9λ<566U+2a‰^=όθCΥ―πύαΚ«»{bk…w’+V΄γTFΛJΛFϋΈΩsf묳ΟTύ8θDo―ž~ϊΧΪΌyσˆEΥ_¬]ϋΓ—Š&n˜;χIY{Ά$M™1C N;΅΄RGΟ–Νς2Ωbχ²1€ΛΣΠ 1xίπ`‡UΧ’Pέ@ u°H&ΪΎq£Ί::*΅TddνZcν+·lyyΌή`<&€ΦόύχfϋΎ»LVKΜ2-“4³ΌΜ€¦-9y±Αώ^?F=‰”ςž_ΉU’,DTΪ°όζιg”-Θι;vΙu_ύβAωe͚ož#Ηά'©±Z™P(€³Ο:O“š'Μ§ή/^M/½όB΅έ›©άΒΆΆΆ^GŽOϊγ§yΖό/Gζ2kνˆ€–-_ͺ%KN—ρ’΄yΣf=υΤ3J&kN},kuλI‹–\q0/".VϚ5Ωƒ»UœhζΈeK5₯Έ@ŸΛ©gΣ¦²qlC[&μ }CƒFεΠ18X£QE[Z‡KΫϊΛ$“IνΪΆM»wΛΏΈa%=`­ύΧl<~_ΫΛ/σy„w<Β Βz 7ίϊo |Ο_jYf₯eFv™€Ι΅ΈZΆt‰βυ…+‘VR&›S"•Φ₯Ι§FΈ,Τk―Ό’½»S22_^yΕώι€<ΔM7]ΏΜ—ΤR­ŒλΊ:λΜs4΅eΜέ*k­~τ!υτtWή/]ΎκκλnŸΰjα0pΩe—5gσιΏ4Ζ|Me­’ΥLšΤ¬ /<_“&I&z“‰D_zJΛδfΧuχ+qδr9ύφΩητςΛ―T]s¦θ‡±ΊϊΟώΫΏύ[Νeα4kζΞΤυτ‹?¨`$"+)ΫΥ₯T{{Ωϊ>vH°°Uχυ‹Zγ,€²ρρΈκ¦LQ ±»²ŸU6“ΡžmΫ΄gηNωΉαΓ&ŒΤe₯΅ΖqώcεΫo?9^οp G©Ω­YμΈώ{eΝb΅Ϊβμ<Ζ*%c_‘qΦ[™υ›οΏζΥC]ΧƒmνΪ΅ξΫΫΊN°Ž³HΦ,–Υr©‚C% ζΝΡ±sf•ξϋΎUgOBΦͺœΡ…UίΏΏvlί¦·_s`ƒ΅ΉκΚ/~nΏXΓκo­>ΑρνΟ%S­Œγ8:γ΄35sζ¬jE&Δέ;υΤΣOTΫύV:•;‘­­YVPΡ₯—^Z‰†Ύl­]₯-rR!DŸ~Ζi:ι€ΕΓφmέΊmηSO>ύφ1ΗΜj™=ϋΨ)Σ¦Oo ‡CcžSyϋφzό±Η•JΥsύ+ωΞ₯λΦ­kλρW«[[ο2*LQίά¬w½χ=₯ύDqΊA ΚΐλŠΑbH˜M°ΗγŠLšTh©(Φ±?XxωΌφνέ«=Ϋ·«―§§ΪKzUZϘ;Ύ±qγˆύά€‰D˜8Z\Π˜ žk¬σIQqͺΌQx]Žγήωφ}WΏ1nυ'mmkCαζξ%ςέ₯Vώ2cœ₯²φIu#>Έ‚H8€h]D qΕβ1M™<©0ΟxQO"₯t6?ͺ–‡±μߟ_ΥD"‘ί?ϋΫςM›W]qyλώΌξΡΈωζΏ›λYηa՘²Σqvκ:fΦ±γUQω哏«½}OΕ}Vφ3«ώΖ&ΈJ8Μ¬X±b’qμ5Vφ Φ¨jήόΉ:οΌs .ΦΣέΣϋπC½ΤέΫ-ΧuκŽ=ζΨIΗ/\0yΖΜ±±t‘J&SzτΡΗ΄kηZΕ^υ\ϋΑ{ξΌηˆ?m“αΦΦ=*O2wΙ{β …“~ΟͺgΣΖβ@φΡΌ–΅LTC»< gΰΈFR NuΝΝ Εb’wς­Uow·ΪwνRϞ=ς+ος$="ιGŽηύτš­[kΞ\LΒΔlξΕߜγη}ΖΧϋ$σ~™±_y/cΡΖ1ΧΎ}οΚ-­’Q[ΫΪP€‘λ89ξrλkΉLρΥ~-x‰„Φ)©Ύ>ͺX,ͺP0('ΠρΠ Z@.›Λ«³+qpΒÐΑΩϋΣυΙZ«_=ω”ΌόΐEvΟ7³Ώρ΅/lݟχc4nΊι¦ιΎςIvI΅2Ζ-[zͺZηΜ―jŒhOϋn=ρδ/ͺν~lεΥΧ½g"λƒΓΧ'ώπ‹_$ιάZετώχΏgX·§L6›yψη?aΟφΌq݈kœH}}}|Ρβ'͟?/κάQύβϋΎ―η~ϋΌ~ϋυ΅ŠmrwαέwxΣhŽyΈZ=wξΉΖΪ_φί?υ’?P¬8ΛR.™TjΧQ Όe°qvεΐˆDJ‘’³½U}}Lυρ˜’Ρ:Χ‘¬Q ΆtK*υ±VjίΧ-ΟχG5Ί4U 2žβε^PgGωt¨ζ²UW|α±ΎGcqΛ-m“²ωΰΟ$Q«άΙKNΡq ŽgUjϊΕ/ΥގŠS5ΚXΡ5ΧόΝΧΕγΖ|ςӟόS#&ΙL©V( ιώΰ}š1sΖ νω|.θ#ΏxaϋφνiǘH!T˜H8‰žΈθ„Ζ.ŒŒ6TΌφκ=υΤΣ΅¦=βΕ ­­GR›$…"υ±Κ˜Βw\_{»²=½ͺ9’J°ΝXŠAΊGXŒλ*άΠ pc£ά@`ΨVR*•RW1XdΛ#­ΰUƒΕΥ7>kΚΏ€qD˜8]Π˜Ž,4Ž}—ŒN‘΄ΤȜ*ΩŽκ£a57Δ4sΖdMžΤ€ΊH€p,)“Ιͺ»'©φŽnmΪΊ[έ½U?Έ~Ke>ϋςγm‰ƒχΒ*kϋφMΗ?© cNPU˜«1Ζ(ZQ}}L ρ˜κλcŠΕ Αa 2ψύwμ@ ŠΛ{ž©΄R}ιš ϊΎJ-£{μHΆnή¬Νoo,―σ?^{Εε5Φχm¬Φ¬YΣ(Χ»OVηΤ*·xѝ°πΔρNEΫΆoΥ―σLΕ}ΖθζkΊξκ s+V¬h‘㻀V+γΊ.|Οωš;wp˜ηωή“O<ω[omLΈqŒ‰Ǎ8Ž©‹F£u‹/ͺ_pάόΐh~ο·lΩ’G~Lω|Υ‘?Gt Έ‘΅υ~IKτΦ9ZtφΩ₯q ½[6Λζ½*―U΅ΛΣΘΑbHkΖHΗ,¬ 3@EššŒΕ*.ŽηY«d"‘Ξέ»Υ½w―²}5ΗΙl±ΗΉύκ·ί>μΊ(γπB˜¨`ήϋΦ4ζΓv$9Φχΐ[~zMοDΦaΑo η‰ΉŽ§ΎΥλ˜ωΖj€ω*Œw¨ΩG·Ÿ1R]$€x]H“šβ:ff‹&57©‘±ΎμdΤ–NŒKχ¬d}«φ½zαΥMΪΌmw₯ΓN‘Μ{6ύΈ­λ_nɍ·ώΛ1žο,3VKU˜ί}™j κ­Ε£h4’¦†z56ΔΤPW4Z'·Τγ.”/o‘)έ²Vύέ™¬€|.―L.§l6―l.§\Ξ+|1ŒΉK“Scί(»CΙΘ8…γT›Ν₯»«K/ώξχε›^XuΕε§μΟϋ9VmmmΡH,x¬.ͺUnΑόγtΚΙK'’JƒψΎ―Ÿ=pŸ2™Š“άlΊζͺkη±ΓΨ™O~κγWcΎ©ŸΣgŸs–-€}ί·Ώxό—λ7nά”tŒq]1ŽqŒ1Ž™<₯9zΪ©§Fš'5ψέέήήηηκKW=αάδΉφ‚#q Ε ­­―J:A’Ž[ΎLs(«Β”°‰­[kΝ"v•iΧ΄]kχτ$l IDATΠǚ`P‘x\‘†9‘PΕ`α[«T2©½{Υ΅w―‰D΅Pž€’τVmΪ΄i|ήqνEσ/Ίa°Ÿχe>U²ύ_εJο‡VΩ\^ιLFιLV™lN™lπAo 'ρ‘PPαPP‘`PΑ`PΩlN‰ώU†žτ—ˆKaGŽcδG2’QaΊΩ‘RΙ€φΆ·«}Χnυ ΎzεgδLn»βσ-Φrλ­·†Sιή»$}¬VΉγ[¨%'MHΖδΕ—Φλυ7*w3ΆŽsκͺ――zn‚«„#Ċ+Ξ”γί%iN΅2gžυξa3=yž—πΑ‡žί½swΦ8NΔqάB+…λD\ΗDǍ,\x|tΙΙ'9# ξκκύχύ¬ΦLOGd Έ‘΅u£Š“Œ,9ηlMŸ?O²Ερ{ ΖF3Έzδ`QΦMͺΒ8‹‘Α’Φ8‹‘Α’Ά +―WΈΎ^Ž[―W>%G‹ΎtZέκloWͺ§§ϊI+]›έ΄ιk+Œ΅š£>LsΡ-“nζzI—k”Wϋί)c  ΥΨΣδζΥ7Δ―)^•q ?βώ«Βm•n{Ύ―\ΎπΑ6­ΉNο>qͺ)Οziγ>mάέ«l6―'žyAΫvνgnΏ°ιgΧήQ­žkΧuίά•Xh<»ΤHΛ¬Μ2Ι.ΥΣ*VcŒQ¬N Mq5[κγ1Ή[σt­0‘Λε”μK«―/£ΎtFιt¦4ώ!ΰΊ …CŠDŠ„C₯πΠoΛήWΟχ•Hφ©/)ˆΓΓ]š\Η‘γ:rβ±4πkTΌ-υ₯ϊΤΡΎW{vοQ_ͺϊbVΦw>tνΧ>³ύyοχG[[[  άn¬©:-mcc£ήχžš γ’«»K<ϊPΕ}ΦκϊUΧ\χ Ž +V¬˜$ΗΏWY•φctΑηiΑqƒ―_e³Ήμύχ=πΫΞ}ϋΌώΦ cœ:Η5Χ8γΈ‘Ζ¦ΖΊ3Ο<Γmll¨Y‡ξξnέwο(Uύ3a‹οκΒήωΓ·χγ%Ύ#­nm}ΣZρuyηjζόΉ²VΚχ&Τ··½b·₯jΑ’xˆj`θxˆ±wΰ8C[0ϊΏcuu ΥΧ+‹•D¬,2™Œφ΅·«}λVeΣCZ^Ήί …ώπš &΄·ŽlGu˜˜sΙκ3oξ–4ϋPΧ₯ΧuΊ ŠD‚j¬ͺ±!X,ͺh,ͺX΄N‘Ppxht£πΑ•ΙyJeςJe<ε󾬬ΒAW?{ŽBAW#\GA·pυΫZι±υ;”HεδωVςyνή30¨Χ½Ήω΄ΜB΅΅ωίϋήχ‚ϋξIr΄ΤΚΊ+ΖuΔφη΅cEΥΤWCC\ υ1ΥΗ’rέ1OΗ^zoϊi%S}₯?©Ύ΄<Ο“1F‘PPΡHD‘Ί°"αŸώη*οJΤ«R«²•Q6—So’OωβqΗ:–Βu γ]Χ-l·ΕπPxS 2Ω¬φuthοžv%zGυέ°[V±κΚΛ4ζ7π΄΅΅9αXπŒΥ—+νF£ϊΰE—Ld•Jxθώj+ ?»ςκλNŸθϊΰΘςΉΟ}.–Ξ¦ΦͺΨ‡(Ηqτ^€Y³fڞJ%“?\χ£_ηrΉ γΈ§8ŽΒq܈qLΔqœH(ŠœqΖιΑ™³fT:tΙΡ(nhmέ ιxIZrήΉšΉ`Ύ$)ΧΫ«tΗήacF†žθW ΅ΖY }lΕQλΈεΑ"Q0S(—H,|ΟΣξνΫ΅cΣ¦‘-/\χύW½υVεω±1:jΓĜ¬ΎΨ8ζΏUa½γ8Š„Γ \yΎ§l:£Ό7~­‚nρΔ=t Έ \…Á‰sΌ"‘"αˆΒ‘°Bα`ρŠvεΦ†’βZΞσ•ΞzJgσκΛzςύ²ζάβγΫ¨3M-έ·*œ³†ƒ‚£W·tιυm]²’R}έϋΐ3Κ• ξ»θ䦟΄N £ύœQI*‡x!84ΦΗ 3+νgp(Ό «L&«dͺO‰dJ©bxΘ{žΗQ΄.’ΊHDu₯ΰ*?ΤυIύ_$₯ڞΛC (>nΰgΣ—Ι*‘κ“΅±KS ΰ*(Π ϊΓCρΧ΄Œ‹ηyκμθTηΎ}κκξ‘υύAAgΨϋ*uY™{γέ=wf㣟ϊΤ§*N^>ΦάόΝΫeΝη‡nϊΘ‡?~(ͺ€^\―7ή¬ΨΥΙ“οN^Ήreε岁Qϊσ?σ`gwΗm’*ΆΞ…Β!]zι‡Υά/ίχd}+ΟσεO ΗΘq€€λ*pε:Ž"‘ \ΗU8P0θ( ͺ.R8R(P0T0P T θ*θόh*uQtςXvβΫίςΙϋΚζΠ΅¬π^ •Jυ©/“••‘±" ΊMάbΛƒ)Τ§ψ…bd$SψχΥΫΣ«}]κκκRήσjMύ(₯ελaγh]Ί.ίm_ϊ;β ΓΘyΚΚ ω|^ύγQ&ZKΛΤjaΒu\IχMp•p„ωώχΏŸ“τ'+>ύ OŸݟΝdυσ‡ΦΗ>ώ‘A Ϋ͞3{φ‰'Ώϋ΅WήΨη8Ύ΅FVΖΚΘ•¬/9…ΟŠΦΏhχΆο ½ϋΜ3TώZojj‡.ΉΈV hu=σΨΗ/ϋψ…‡{ 0σΆ&z:φIŽ‘±’Λq]Yί§]΅2CNΞ­-ίgΚξ§j-~7šβ‰Ύ)~^›ώνCŽkmaπt₯γγΪWωΈεΑ’τΉœJοΫ'(Τ€HSS)L„’Q-\Ύ\oΏψ’z:;ϋί£ΣΓΑΰ?Kϊγ‰ αHsΤ…‰ΉsŽοk);σ 8FsηΞ΄©ΥΧt«‹„T ©eπnώ'-œ=¬»KU•ύUΦ0ψΎ-»WΌ]šY¨ό–gσΎ²yO™¬―Lή+Μ.T,T*mΛc‡Τ΅pΏ'™­ru»pε}ϋή€¬oKΟ>­₯iP˜ΨΧ[u ΒBp¨©©‘ΨβΠU}YŸΟ±κ‰TJΙδ@w%ίχU +­SScƒfΘͺX΄N!Α¬ΌyΉΤŠPͺla³υ₯ΌοΙσ|εry%’)%Siυυ₯Υ—Ξ(“ώ~uχ$Τ2ΉY--“‰„Z¬F‡”HυΙΘ( *κoa2ΑΕλU¬C"™PwW·Ί»Ί•ΝεδΥn!ΛJζAIwrŸ^uΥW8q¨ψκ¬ΦšΛε.^UZ­1cΡ2₯₯κŒXΎό3E˜ΐΑa›'ygΧΎι2φCwvwwλ™§ŸΡyηŸ7hϋ駟~ΚΆ­;Kτφ¦YyŽ+Gž•q¬5²οXλX»sη.ϋΔ/Ÿ ŸsΞY †*kjj%—|HχέW΅ΛΣ\Χ3Ώ\±bΕλΦ­Ϋx0^τ‘ΰσ|ψξ½{e}+ΗqeFhT^ͺ―x’_+”ν«Jέ€†‹ ΗΥ°γšaΟ9βq+‹Jυ“η©―c―2έ݊Ά΄(‹BJ  yK–θηŸW2QœΡέ˜Ο™;χ_WnάψΛJο#0ZGY˜°ΖκΖ;ŒliQ‘`0 Ε'΄*ΉKI C+†6 ψ~ω†Ÿΐ—mi\Cϋ7ωΎUΦσ”ΛKηŠ"οωƒCΓ ΊΥƒΒŽ€7wτhι‚Ιͺ »²KέΙ¬^xk―fM‰•67Ζ£ƒͺ»/QΖΥ7ΔΤάP_θ¦U_lq8€ΰΞd ‘,8IΡh’u΅Ln֜cf(ZW7ζ+ΫVVΎοΛσ¬<ΟS_:­ήDq,EͺO}}…–¨ΡΘη=νά½W]½š6u’&57*Π•ΡΜ7Δ’ΕχΠ ΄0•WΩ·Jυ₯ΤΣέ«ne2Yεσ5{%yΖκqίΡ]&”ΉgΥ—Ώά9¦7`ΒΩͺυΛεsŠDj/V^~Β°Z1‚Α šššΥΩΉoψNk&~Š)±Ύύοη>όα" ="ιέCχoΨπ†Z[[5{Ξΐ0ΎP(:λμ3π\ο[kί³VŽυ¬kdύβ5ΩΒt°β‰πΉηž­H€r0oljΤ%—|°V ˜-ΗόpΖΪ'Tψ3ΩdR{·οΠτ9³%…› dT ­ύί™#‹‘'ϊ#‹ͺϋ*–ςη¨v\ΩJΑΒΚzž’;w*2e²"Ν“ ί;Α ζ,^¬ Ο>+―Π²m¬΅+ιΒCςƒΒγ¨ s/^sΉ-ϋ₯q£Ÿ=,Hœ΄pΆ|_}αέBυα‘‘pcPγΑ° 0δ~…Аχm±λŒUήχ•χ|ε=«Όη+ηωecΚP3( i(»_"lΩ2Y_ώv«ώ`ω1ͺ JϋΊ“=πμ₯³yeσž#+[œΥΘU6W8ΡνJϊ:γτSΤίΰ IΩ\RJ¦ϊΤ›HΚ1Žb±:Ε’ušVμ¦T g5­ΒΌάΆψ^{ž§ήdŸz{“J$R….K©ΤH'ξ•€$΅«lϊΗt&£Ν[wj_g·¦OkQΌ!&Η”χ`ν―Q «L:£ήDR]]]κλΛ(—«ήS|θ―¬1w²fνΥW~ΧX+}¨£Ξjν Ή\b@1Ts΅0aτqyB΅ξ½χήԊ+>&Η­€YCχ?υτ―4sΦΜAέ•fΟ>ΆuƌvμΨΩgΗZYk_ςŠσ‡—:vdlηΎ}φ±Gœwώ9ŠΕ*_$kljΤ‡.ω@­.O³εψ]žVmΪ΄iukλΓFzΏ$½ωάσš1·U’U°."ΏΉYΩξ².E¦f ¨Τ‚0¬E f°(ΆFhψˆ‘‚EΕ–‰*A¨ό9ϊχeφν“γnl”΅V‘ϊz΅Μœ©]ΫΆή,kΟikmmjΫ΄iB¦ Ǒ騀}̊[κάdζuSΆΪ©§,Π©KOt"žΙyκJδ”)žΜυ_8La¬CqžcΚfMθ ­B+‚νΫZωΕ_hί/όΙϋ…“Zίσ ·ύώ •Ž1(8Τ ε­…»ε%5¨ ₯ZˆpΡ±Sγͺ― ͺ+™ΥΦέ½ςŠ―iZsT±°[:ΖγOό^ν=₯Η~χͺχkζ”x₯·˜R‹C©΅‘0φ  )ZQ¬ΨκPWW7ζpRΈXγΛιLN½½Iυ&SJ$S…πЗޏn2¦S²―Θκ9γθ9ωήsσŽizmŊώϋη¬ΥΝ’¦–?Βq57ΥkκΤɊD"32©°^2™RΗΎ.₯R}#Ικ­“΅ΉκΚ/Ύ9ΖΚΏ#|λ[ߜ“χΝ¦JϋΞ;χB͘^˜•ζ`ua­7ίzCΟξ·χYΟ™΄jΥͺwx‹7ŸόΜ'/0Φ>,iΨ@±SO]₯ΛηΨ]»voωΡ=?yΚuœlNŽq#¦4]¬ιŸ:ΦqœH,‹œwώy¦V«ϋ(eoτ\{XŠζΜω„ŒωoI’1šΏd‰β …itgŸΌXωdRΩξnωΉ\ιϋ²ΦΰκςΫ₯Vε!]“ 7mε}C[ c=nωqφΥ:‘Qύœ9r¬•ϋφιΥηŸ/#c.]Ήqγ½γύ3Α‘λ¨i™pS™Ο—‰ζƘN^΄`PkC’/―Ξήta5—²–_’—•Yo”-ΆόαƒΫ.Κο vπƒΖΠςPωƒƒΒH]­ χ³Ύτζφξ]=ΟζTrJukhŒ ›vφT VVιt¦l:ΦB @8V,Q}<¦i-“‡ΖΨΪP[!λ{ΎR©΄zΙBh(‡ΡvS*““τͺ€υ²Z/cΦ½άοΏώυ/ ]`£άΓwΏ{―²αoΚκK*ž ψΎ―Ž}έκιIjΚ”f577*“Ι¨³³G‰DJΩ܈u{CέΦ5w_ϋ•/Ό2ΦςN“ε;#ιΚύΉ³ΩLιφDΔnjlͺΎ3€VI„ T}χ?Ύβӟψ{IWέχβ‹/jΡβE ‡&΅˜>}Ϊ±S&Otttτ9…¬udε;Φ7Φ#k%©B‹C’jΞpT‘ι4Ζώ^Vλ­΄^Ύ]ŸιmxΉ­νSΩ1¨δϊoίΎΜ1ϊGY;¬Ot ΰŽ¦+Υ6ύΐρuχ5W^^ωrωaΚZknΌyuV.dœΊμ4-Xpό!¨•”L&uού?ΈΟXσ‘kΉφ§\%V¬X—γΏ"ιΨ‘ϋN;ύT½λ]ƒ‡μΌωΦΫΏ聇žsd"Ζuϋ§‰­s\'βφ―’έίJQόŽ„#ηœsΆ3yς€ͺυθκκ}χώL}}GV ŚΦ֏X©τ‹­―ΧάE‹Ζ² 1ϋδΒjδ]‘€ώ+ύ’d}O^&#/“-όΝΘΟεΛZ *·4ΤjAά šΗρΈ@]μΐχ¦¬™4IαζfIR_w·^~ϊιώΡ?j7sζύ‡uό;W}υϞ;Xο?ŽGE˜˜wρ§ϊςŸνΏFτΙKΟW 0°َŽ”ςήΐ/£484 ½_ϋδ{ΰF΅Φ…š]”7)Œͺ‹D„ŸPΠΡ±-ρλθθμΦ/Ÿy©TΟ“η5λO.š§P±«R4Q$<Φ± …χ’Τβΰ[₯ϊκ-†…ώΦ†Ύtfδ ζKzΫHΏ—±λεk½\oύΚ―~iΛX4ΦZ³ζ;·}^2k$΅T/ι©Ψˆ±ΧΚsŒξNwm{²­­mό69ΔΦάt}»€)C·ŸΌδ-^΄δΤ¨πoξλξ¬Φ½κ―V^}έ?Ntptψδ§?ωy#{ϋΠνρxLŸωΓOj₯Λ岩οσmf€p‘[“[\kBS\ΨΞ5¦pΫΊBΗ‰„BΑΘΩgŸιN6uθΣ”twuΧ”-I[δ;‡έ μΥ­­l€/υί†ΓšΥΪͺ†Ι“eͺt‘}ςIΈPW%XOϊύ\^^:#/Σ§|:-?›ͺt[ͺ,ΚΗU¨β}•‚EωsΤ:nύμce‚AΙJ{ΆlΡζ—ŠίέΑ τg_+ό]°A]γάyυW?Ɓp48*Ί9yς>R~B;·u†Χ)LΏj₯d:§lήρ{Πή!'άϋΣEΙ~ΰώ·. }Ξ1‡Ÿ‘―ix½ ݜ|yώΐΐ°h$,cκ½·'§ω­£_LΌπX\η‘8πΌ·7Y ύkFμǠ謬ޔΡsΖΪη¬q^ΆAχwΧώ՟tŒυ@ϋΛc%έήφν;ξ [oeτ—Ϊ7ΪKeΫλϊNxΝ€xόoΏτ₯/Ή?ΦaͺSΒD΅ΨγΝΪΒϊαpXιtΊΒώZa80{wού–i“―“4Ώ|{"‘ΤŽ;4kΦΐν`0]pό‚IoΌφΖ^c|+ίXγXλ[Η:Φ·Φ³ΦsλΘΨώ©©Ye³VO>ωtδέgžαΜYy΅μώAΩ5Z(fΛρ)Ξς4.aΖC<ψj2ŸŸ#ι’”Λd΄iΓC!Ε ‡/VjŒΪ·mS0R0Φά₯§(‹–Ζ»f{’Œ)”wCA9Α ‚υ1ΙJ~ή“—ξS/%/Υ'ίσΟΌ4΄k’4ΠeΙͺΚ νΑϋ†Ϊ50άX+'R )…Ž;KοOdρΙΚΧEΚΏgJjσ|Ών†oίφ¬•ξtZ{ΝΎ|ΗώΨp˜9*Β„#saωyρμYS%ΰ”:‘ΚVξ/Pγ*ύθCΓΐc†ή·e\ltγ>44Œoψ‘•2ΩΌBΑΒ՜`0¨H(¨ΎLαxWGRΩΌ§P`x¦‘­ }™Œz{ΛZ’)υυeͺ]e»¬]/£υƚίGληΞ¬σPς\νŠΟwIϊκυίΎύί‹]ŸN1ύΎΧ΅ΫρΣa%Ηxι•]]™·$έq¨λ;A*Ž?Θf³&*Ν UF¦~B*…£γ?ž_ριO|OMCχmέ²mP˜€…ΗίΊα΅ [o¬dlα?YλKΦqδτχ„7FŽd}S؟Οεν―žωuδΤΣ–fΟΦ«JRa Ε‡?\{ 9ώ‡Σ΄±_yσΝΜ-ΗσρŒλ~_Ζ|Ά{.›UηήZΓΰ ή|ρE9Ž£H,¦)ΗΜR¬©QυΝ͊O™’ΊHD2Nq΅"+k¬œ`@N0`ΌΠŠοη²Κ₯Rςϊ -¦l‘ΤΪc §«ΞθT ƒ[,*7άΨPZΈ/“N+±o`ζΊEΎDMK—kοή.νnοΠގΞςυŒN3i~^ίZsΛm[cοΜΘ½§ψ½”ρabωςο;΄oy}Χ8šΤT/ΏxνωΎR™\ρ*y©1œ`Ή_-4 μ+o`s…0PΈZλΒ φ…αgΠ}k•Οηδε2ςr}Κg 'οPDαx³ŒΥ^G&—WΠνhU_U_¦[Ra0τ–έ½š7³‘|ΙσΌ θd²°vCOor4‡Κι%+½ k^π]…pΞ[?Β θwŒλψΒσmmmg«₯%Z—κώ΄cνχUœΥQ’k½νΖ›ΏΩxΝUίψΞ‘¬η„0κ”ΎyΌΓΔHSΜV[9Xς Wωο¬³FŸ ’€-[Άκέgž1¨μ€Ι“ζxžχsΧΈςεIŎ€Sψšπ}§xΙ\VΦ·Žo¬o΅Žom.gσλg#ΩL&ΈΰΈλΨΤ¨‹?T˜6φHi‘ΈrΫΆ>IŸ[ΣΪzΏ•—4o,χ}_©ή^myυ΅6㺊Δγjš6UMS§©aΚ$Υ76*άί`lqΥνάPHΆ±IΦΛZ,’)εϊϊ€βÉ0>«n‡ε8ެ΅κΪ΅«t~ŒΗΥ²μ49€fΝhΡτi“•Λε΅gΟ>νjοΠΎΞξώΟMΧ½W2ο Λξ ίΉνg֚;³έζΎΆΆΟΏƒ£Ξ&φLνžνJ₯•° +/ϋΕ՜sωBם‚!]{j„ˆςώ›COΎ·. ½_-4 ½_14 ½_¬υ•Ο€•Ο₯•Οφϊrζ3ςrYΙϊεG(6Χ%»v«~Κlύ_φή<:ŽλΎχόώnUwWυtcίb# € ER$%ξ«Vj§,)q’—7‰'οΌqŽy‘ψΜ '¦(JŠδ—Ι;3™ηΜ™9^bَύdKΆIIΤfΛ’,kγ"ξ$6bG£—ͺξκΊwώθKWƒV²>η TUwέ*tWέο½Ώίχ'»Όη1.~τ˜ ·‹gŽ™ηs£0˜yΏŽwΑΤ|ˆD’G5D£ΧbΑŠ^|Β >†ΐΗ¦„OŒ‘—ρN]ΨtttπΟμB²σ•HzαΰΑύκ“Oώνωhߜ‘£ φlˆ‰Λ ˆ‰8r$e±+σ;Ά±ΉFώύϋχώ}_xψ}“”ΓΨΨb±ψ$W'―ΧS┝Ξx"‘ΙHD\€Λ`2!˜‚³€;6@fκ+—όχ£>ΙΉcιRkΓƒ@ =Cρ2’Ρ¨Υ.‹n†ΎvώόχΌxnΙ’Bˆ»9°œˆŠHˆ1!„ πP’bΦ•Σ„ B Ρ{ςˆ18žΒBWVΐ_Zo^UΝ "._„7 7‘ˆF‘ˆD`hη³Ru›Ή\ŽLhΦ@wwζ ΦMˆ$ƒδr’ΊΊ ••%Πυ8.υ’―Α±pz7$ˆ]ωbμΐ ϊοβ» UΎWJD€ΝάsΓ‹ ƌˆρΑ―GG`$x&n?ΝlZ―f~ΏμΜCŽΩ†)3&¦‡‹ Ӑ04$bΜD|ςϋM8/"ˆ 1ˆΤΊπH/ΌE5`Μ1α“Ϋ­Η αΜΌ·Χ­N:Ζ§'»αΑχ½β … OΔ'ϊDHψψoΎό§Wϊ‹‰§Ÿyκ/ρ0¬&W IDATί0ρ <υτ3ίτ?ω_ώφk©Ό‹ΚζdΔfDL\€˜ά,λ}§1ΨΨΜο`Š˜B`td₯e₯™uŒ1©¦ΆΊκΜΩ³Α…Ζ%SHœ§δp87I\L$ΰŒD2Θ…aσβγ> #αlmk±lL²RφέΣ Š0ώϊΎ}ϋΆ/&Aρ(`βάΉC]nίη«ͺ bNg™f“ j@3–   €sβΎ‚sΔ£QΔ£QŒtv‚Iœ^/Ό…(ͺͺFAqς~P*܈H‚ΣηƒΓλ…Β9ŒH‰p =–-,r+¦ζR,Γε/@|,ˆ„ΝΌΦ•ηΛ3Š 22~ϋ-X;.tφ€1†‚@>Όn7€fΊυ%ˆι…Δ8τΧŸ}*Ώ££γ?ݘNbΔκ2\ΟΜΔ΅ ˆ\ο1…E=#f³8 Ο(λn„Γa”’t:Ώί_$‘4`2ΒΊI₯œα,ΥΥδŒ ALHάœXrƒCΖΔΡ£Η`†sεͺvΛοM2διιBžjΑψα” X!OWΓW»Ί† xβϊŽΦV§#ncŒ­B¬&ΰ«1A`pΣ„ B1xφͺ ΕοGIM5 JKα »aαΚΛƒΣη7 ‘β‘0Dœ6Ή)1hkEΙΪ[AL‚€ΐΘργΈτφ[ΰ\ΐεσ% ινΙ<˝ω~ΤήvH’0M ŒβRί œNŠ‹P]Ι€"‚$IΘσyΰσΊΡ°€ ‘P—ϊ‡q©ϊΈ³b)_ζΔΎόΤ r‚}O"σ»O~εώΟφίΙfώΉαŐ'φ[8rqJ~‘Θ„<™Ύ>²g2Di:ѐΩΥZD$›ΗaθΔ΅0 =ŒD,’%¦ŠJΝ6c©€°δ ,ωsrV‚RΫR"”yXαjj2ϊΔ–«NiΌΠŸΔ”Naάδœ€π>ΡΗ7ςlΓ•πτ3ϋBΌ€«³bώ’βvψώωŸωOn@Α5# Ψ3! &Ώ_έFžΊ·™u˜ƒΒβclXδ—Ή=nc€@H”ΰ Mζ²ΰΰ‚ N$Μ”H:=%]ž’ 4~#bΔpκδiΗ]·[kω=Ί‚§z0ώζb›‘Έ:Žψ0΅ž―ͺR5IΪΐˆΆΨ†δ,S&ΔΪΠ4š†Po/.( ”ό|V”£Έͺ E…r2ΗΒι„TPWAFT‡1DBΧr±σVV’lΓm™ώPΠ‚D$ŒΡŸƒ9IΡΑΎ γz―lΣfPͺή“,I()*DIQ!4]ΗΠπ(Β‘(ς|^ΰL…€dIBΐŸ‡ό|λ«02:†Ύώaτ gr! Τίΰ`ίxϊ…oȁοH2Ύo;BέΈάπbS:.1έΘ$=eϊςSÜ3D);―aό‡‰!JΙqΔυ = CΐˆE&ΎΕΔ©‘Tμ#RH b`„ΜLK‰†‰?³ŒhΔ@)ŠδφδΆδ 9c™;υ<œ†|―ΟlŒŽMvόš―ν+qlξ+Ο]γ˟ ϊ:::νθθΈa’άˆΨˆΥ,€a$ύΩYοw`ζΔDfŽ A7Μ΅·YΈ‰λΰٟ}ΣΜΦ².Eρc*ƒ &IB"ΞΉ “CΰIΙg‚#. ΐ’cΫηΟ_&ηΚϊυ·Z~nφŠ+!•δύzjΑ?66Ί’¦Ή^{ Δ}2EtΊŽ°#άΧ‡ξγ'ΰP^ΏΕεΘσϋ!@‚ΑιqΓιv#! "‰`b’5 όΖ¦Τ½09€$ϋeΛ‘υχ§žρα± ’ΑρόΖDύ2„#Qx=ξIη‘* ͺΚΛ 0 £³ϋↁΒ@> ώLΑYF&Λ()*@Q‘K›κ0<<ŠήΎ! g‘°š€Υ<gž~ώΫG8θ;,nόψk_ϋR67 ‹NLœΨΊ{ΈψΣ+έ?yΣs\―/wΙ(>œLη€Ÿ>ŠAδϊ-gTΔ”MΏΣ7ΰ<€FE/MιΘξTGŠ&ό¬›fIŒσ’7 ΔΔ'mΐΙσΐΩ.hN'œn7œ.gFΨ9ΈS <‘€0ΝΜ &~ΡwήΟ>"ς‘ξsPp ·W$·I2π›waώζ]D2—N§ŒMΎΏ§! #Έa€Αιp@–eL½{T Ξβq±Έ#‘Hwl$;μ$ΰ_ή½ο ύ˜Φ«hΪεœΎIaτ―Λή8τβ|7γjXtb‚„h„;π+IOυŠ֘ϊ&@½ύ“φ™»τΚ9;Π cššβ 0Z΅³ή€B €(Π¬­|‘ƒχ]@<5έ>K›—aΣΖ-ψ䓏πώοή›nΧ킇ŸΎγ―~΅cZε±89EQ܈CUΥ9±ͺ1‘<>l1a3λXƒΥ=UQ²Ε„™0“˜‚Τ„ € A¦0I“‚“)ˆ’‰ΧΙŠ€P@’3 €"bE_ί%ρΦ[o+7o$§…»™=C1™@@sqά ˆd΅i‰Α+³΄=I*ΆZ˜:|ΗŽ‘¬Fφ¦VqΡ˜9ξ‘ς„ϋhΎΘWR†)Dΐ@―Ε+rίk%LˆΩΊ Οδ–Nό…(K-!™Λ³X»H3D*Μ‘‘oΜsSšάρ &θΚ–Τx{fA:g"΅ζjΑ"]9%“«}―τo²Pd­\?žΩA˜VH,[Ί›7mc «V­ΖΖΫ6]ΣΌΞH8ήΨΒ~λς΅‹„§Ÿ~ΊFώ1Χφτμ@Ϊ$`Γ0,ΓIœΩbΒfΦB¬³Zοψ³Φ%LS"&©Œ±ρP%‰©I*7…)4. ™άΤ9η:7Ήnš\η&ΧL!4ΑΉΖΉ©ρΤΟ&ηBΤή:ςΟ5SψqΟ½wCUUΛνHΞPΌΎoίΎšk½‹’k{₯C’³–i:φSIΧ–˜ΊLiβ„χ†Υ#=«o4³KŽc¦Z'’Ow£.XΔ}ςEΫp›… ξ›nϋςe-Ψ²ywΦjm]m[wLŸ/΄Iή:pΰ@έL·y.xκΉ§ZΐΜw,Λ΅O: {—pxΊ2Ρ5σWΓΖfœ/~ρ‹YΉfŠ’ΐηΝΆΪ‡C‚1R‰˜J4AP0I!‰I’ϋΤœ«sΙEGG‡|πΩ§ώ…rΔ€§)--Γƒχ? ŸΟ7/bβΧΧ—³‚πa΍663‰¬ΔŸΧ EΝvυB4ͺQΖΕ‰&†;₯s%’αNDL•S“‰A‘$Ia`*I䇙²†OΊf*‚σT…©s.tžHZΛƌΈφΞΫΏ‰wuZœμΩ³+§‰„xξΡG~ΰ:―˜ΝΓ66sΔD‘KHX­W\ ξΌγnXζu§‘„ύΐ3ϋηmδοωηŸW·γ‡¦­P_]Uƒ{ξΪ Y–§T—›ΉΙ΅Xz¬OCWwNηΧ™Žί_ω°±Ή²b·uΈΠθθhζg– )‘Μ™$¦P&δ‰&1U–’3™}˜€2—iš8’³[ΨTM nκΒ4u3ζΔ9ΧLΑ΅dΆ©½ϋξ{±sηΞ[Ά±¬Ό [ΆnΞφιf ΒwΎπ…W^Λ₯²±±Y˜ΨbΒΖf)w•kΘQκ<‹]Ρl„Υ"Λμήu'JJJ§;<πόΑgχ?=+'7 /ΌΠαΪ/ά?έ~ υΨΉc7\ΣuΈ’}¦fVοΥΥ•³PοO>ωδt(llf‚¬j‰œ[P,(˜lˆΖΉX*άIbikXJ )5CA*#y<9›˜šIΜζ\7Σ³©\$4!R.OάΜΤ‘ΘδX˜\3¦ώΑϋΏ‹:iνšάΨΨ€5kVη:_7ϋήή½{§M³±±Y<ΨbΒΖfω—Ύd[mΣuνΊ:½D„­›·£ΌΌbΪ6'Ÿ~f·„3^έۊύ/μ/%δ# l™nΏεΛZ±ρφΝ–α^3-τ½ΖΖΖ0‰ΐ4­Γ¦’#ή8ΓΪ5λ I.\<Ÿ{'Aαμ…SΎƒ~EΣ΄K‰+:ψςΜ3ϋWsπ—䌽""¬]³Kκκ3b(½ώJΈ’ύη½Ξž·UςΈςsEolcsπ τΰΤυηϝΗΚ•YFOΨ΄yήyη M³ "cιρAF‚Fά "ƈ a A˜S˜:3™nJ&$N ΑΑ@BB0p&8 AA& “Ώ}χ=8΅¬lς­€1†];wΰG?ϊ ’Q‹JΪ‚ώο‡žxθύχΗύ ήΨ$άn ··"\]‰„Ϋ )‡§»…ǘ…i³0°ΕΔ<χϋ1܌Έ?2bp #οΔI8t}Ύ›6ηŒ,kFοmΰŒΑαp@–eΔb1DΛΛ0ΈfΚήxGOΜw3― ΒYd G’‘+ξψ^φDX}ΛZΘ²Œ3g­γ–J<’ΈζΣΟμΏDΐEτ艋υpAŒ³Ξ@ Π› ΣΊ,ξί΁ŸΘΛ΅$IX·vΚΛ+ZHΜ…ΨˆjQ ZϋζCΰη_ώς—cWԛ뀄τ ˆύΤυǎCkk+dyςδ’ΛεΔ–-›ρξ»οαRο₯ΙοEδ`L"‡IB0"!ˆ1A ‰ 8 Ι:Œ@r Ι”H‘›¦©“ „0IBBbL&1H8˜H$βΧοόFlέΆΩ]X8Ω(BQlΫΎ/ό«ΣH&ύ €έΧ~o\FΪZΠ»εvpY†Δœ.‰KŠ0ΌrΚAΰΔΙωn朣ϋσ1°αVθE…ŒAEρ»@ΝQΕfφ±ΕΔ΄Ά΅‘ΈΈΊ# ‘··ύύθΩΉ ―%Ώ]ΆBœΆ*―‰„gLL€YΩ~ œN'ŽŸ8vΉ]%•¨og²- `&F‚ƒόιƒϋϋ@θΠ#]Dƒθ!bM“Ί=OwD»Wߐ³D·Γαΐ†υ·£° œΟ|ι™/žŸΖυ‰z‘Y66WΕ?<†jb?ώκ‡ζnƒg‡5ŽΰύχήΗm·oΘz,ΛΨΈρ6όξƒqώόδA}"Θ`€II΅ ΰ&ˆqb  .’3‚“)‘IΊ)L8 `DIι!Œ3˜Βd’`’ΔΉ €ΰ"‹‹7ίx;wmwηεω&΅©²²«V­ΔG}luvν{μ‘_όώύš/δ H†[ΡΏn άͺŠβ’TTTΐλυBQ ΰθgŸ‘{χvH¦‰ΌSΣ„kήH0† {οB¨Ά:υ+a̟P}ςNEυΛΏœηFޜΨbbθέΈϊ¦ΫΠ°d ΚΚΚ ͺ*t]ΗΘΘ̊ τ‘'ί‡ŠΧߞο¦Ξ: UAχξνΘΛΛΓΊυλαp8 „€’(0M₯₯₯πω|θξξFΊ5pŒ"?‡ƒΘBA€ΞXuac±b±%Ϋ?ώzhYއÁO>΅|P_ „rε@ΙΎ$ DυΔ1MΎ•’(ΨxΫδη'c»―Ζ¦u¦™zμ‰Ώwuuζ~!‰―|φ©cOώ—Ώωφl΅Νζζ₯£,οsφώBx*θ Ή쐦Ο>; Ξ9nίx›e •5kWCQœ8ώωΤmcLα`$H0'#“ˆΘ4‰'"ƈ q Α„ !„.L‘›0’ΰ$ˆ ΑLp!L!LΖ A i2ρλ·M»vοTeΗδξŚ΅«ΡΫΫ‹ΎΎώμ "θ[{χξύεK/½΄¨βYg‹ΰ&τ―[ƒ‚‚”——ΓλυBUU(Š!ΦoΨ€χ~ϋ[tοήwg7δ› ’αΤ㏠VX€ΌΌ΅½Œ1lέΎ5WA»Υγϊ««Ύ˜7 Fž=;·Βγv£ΉΉ9σ LB₯kιΐeΛΐ ­Z1ŸMžΞίb…(++Css3ΚΛΛ‘ͺ*LΣ„ΫνF{{;<F[–"‘δœ4·™%l11Η ·΅€9p»έˆΗγ0 #SlΛγρΐ› yjnn†qϝ65Μw“g•±eΝπey΅ΰœC–e(ŠEQ λ:VZ αp kΟ̍ι顏Δ?·Ϊ6222kε%uυXΏξ6Œ'aΞ-ώ|?ΆmΩ ―Χ;m'~Ύ—t[κΡ²ΌmΊS’‰οxζΐžΉΊ†676Ο=Že&gοŠ) Χh άυ=Ž7ίz;η,_cSn»}ƒUGQ²Ύ„’vx’$¦cͺD€2‰©’”¬˜-±qΡU’$€dš¦&L‘snꦙry\"ιπΔM‘ͺO‘twβ¦ΠΞ_Έ:yκTΦ=0?/+Ϊ­ΏsBˆ'όρ’ι―ΰ $ŸoVZ]Χ3Ο@I’&= Γ€,Λψύ[Φ<ίMŸUz·άŽpm5 QQQŸΟΗ3ιZ†’’!0’γ3f3{ΨbbŽΡώL…ήtΨ‹aΛJ―Χ ―Χ‹±±1,oiΑπƒ{‘•M[¨lΡ’πzχ%ΟΧ0 $ †‘™H‡;₯GfB‘©(ΗΠ- ·€jGG‡ΐrv’ oV;Κ5Υ΅ΨxϋfΘςάF0—`ΫΆPUuΪNόBιΆ,_Φ‚¦Ζ₯ӝš“ΐόΤ³Oέ6WΧζΖδ…'€‡$°,·Ϊ >T©œ―?yβ$ŽΌώFΞ€ΚΚ lΪ|;Η€υ”DM΄£ Φ°ΔX²¨Δ’λ“³ŠΔ$E"I!‰&›ΒΤL“§lcΣ3f²¨]ΊFE².E¦ŠφG~Ν²―[΅j%<ΛzuS™ϋ ήψ έ²‘Šr446bll,3#‘oJ?'>/=^/by>˜ξ³ΰhλr ­j‡ΧλEee%|>Ό^ο$›οt_*=¦όσάκ›[LΜ1,5ϊiZζ ΗašfζFαυzασω‡ΡΊbΊy`Α‡φ\ q―@υ'}cLϋO™xC-((€??}·―‡^Tx™#Μ‚ΘVtxx†aΜjGΉ’Ό?ψ(ξΫϋ vοΊ›6nΑκ[ΦbΩε¨­©CQQ1Ό^οŒΝ`TVVaλζνp:œW53°ΖVΆ―B]έ’ιNΡΓ„ψω³ΟξΟj·±Ήž\ϊβH…5YQ\RŽιξfμY™ϋ^ϊτΌφΪλ9Eqq1Άoί Υ­NZOD”,f—Iq!S€tΈ“JUΡ&ΖTƐ¬EΑH%"ΈΠ8L άΤΈH†4%Μd1»„ΙuSp]˜γ!Q‰DB{οέχƒSΫκp8°nύ:Λφ !ώσόΑδtˆ»‘‰ φυΘΟΛCAAΑ€΄‰ΟΓτ32ύΌLΟF₯Ÿ§7ZE9Ίwn…ΣιDuu5ςσσαυz3ΒΚ4ΝL?*‹AOεΘΊmΔ7ΧΨ ΨsLώιsnoC8†’(Y+ ΩΉφz½ΰœcttKoY…Sα0jΏϋ"€ΈeτΜ’D€n‚ιiΚτhρΤlI’2 Ωαpν+WβΧοΌƒΞ»v£α»/‚]aν†Ή„ΐΠžΊ^ΎώK¨™>^Fp«nΈU7€qΡ55LBΧuhZš!‰@Χ5D’hQ šET‹N[£‘ΎkVߚωμ^)D4―‰Ω!"¬]½†a »;»ψVŠ€)Δ/8°ωλ_ϊΒv°YP<ϋKβXYΌ!yΏ_³n ›[ξ+œαgf­€sgΟγPβUμΪ½Γ2 /?Ϋ·mΕ›o½ƒphrΖXΚύ ΐ81Α‰XςŽk FŒ„ΔΔ… ‰™Μ„0…n@0.Ηx- !8γBOUμ IDAT7'&††Ε™3g•¦¦ΖIΓ捍 ψύ‡atttjσύ†ϋ ―θβή IB睻ΐd+ΪΫ‰Dΰρx,Γ›& ‰tTyVλ“Ξ9q―ηΪ b 555(((Θ„Π¦g%& ‰X,†`0πί,ξV [LΜ1žnȚ† cPU5«γμr%‡dYΞŠH$‚κ[Χ’38†ΊŸώ˜«Νω€Ε’Β(‹MΊS― LΖNΘŒŒŒ yιR3a`Γ­(}ηέω< KτHβuΕνˆΑΒ>΅««΅5usί(dwβUU…ͺͺΣΌˆΕt躎H$M׍F‰†‘ŸηΗςe-3Φ–ωD’$άΆ~#ήzη τυ]Κ±•‘Δ}σωonϊΖWΏΡ=§ ΄Y”|λqφΏ ΏΛ΅ένυaσΆ»PXT2iύ«|p»^|wΤ!όβΕ‹xε•_ΰŽ;φd…5€ΫγΖφν[-‹Ϋ1Ζ"Aœ@ΰ  .@NΔ‰IO;Ί™€ (ΑšΜeΑΔI€’Φ°$RNOI—§dΨώώ£‘šκjΕ₯Έ&M…ZՎ#GήΜj»ψςΎ}ϋ^xρΕoœ‘³ΛΠΏαVθ…hYΆ Α`0#$\.W&jΑJH$ ΔSŒRμΊ\²Œ3O<!IXRW‡’’"x<Θ²ŒD"‘%$βρ8Βα0"‘$MƒΪΣ;ίgpΣa‡9ΝU―7M N wJ)ΣwN§3sSΙΛΛCΡζθήΆiΎ›?c(Α t]ΟΊA¦G\‘_œσΜ΄―’(πx<(.*Βΰκ•ˆ”—Νχ©dΡΡΡθ-«mέ=]H˜‰yΟΈύE…ί@eešΠΎb%n[Ώ-Λ[g€-σκ”^dYΖ¦Ϋ· °pΪΠ:9AΏz›:QΤζςΌπ8ϋ¦@n!QZ^‰»ξ}4KH€Ω²άƒ}ς‘k―·η~ρ‹_Αˆ[Χ™tΉœΨΊu ΚΛΛ³Ά‘‹ˆTb€2bͺ$1…IL‘ΛΈ<Iɐ(’dΨ“”JΜ&IεœΗLnj‚‹tX“n&Lέ43.OΙ< Ξ΅Έ‹œ8ρypjΰυY…t‰ ΑΔcΉΫF€Ό ƒ«W’Έ¨n·;+ΌibHΥsRΧupŒf]βEΛ©Η†©(¨FQQΌ^ο$aΗ'υ›4MC?„¨yεΥωnώM‰-&ζoWJήύρxύύύΠu=KP€§ρTUΝΔVUUΑ΅k†V/άδ㫁ŒάƒCˆF"–#.Sݝc™m"‘ΐeΛΰp8Π}η.p§sΎO' ρ’ΥzΣ4ΡΥΥ9οη…‘_H‰ΩN§ΫΆl‡ί˜ξOΫ’ΰŽ—‰Ψζr;ώ­££ΓΞ=³|λ1φ _Ν΅}iK;Φoά "λAwn&Π{ςmτžώ L#)Μ„ γ#άWw²dύΊΑ‘!ΌτύF,·ZZ–c•…₯69ν€t- –*d'%݈IγEοS˜*I,ςn Sh\@3SΆ°<εςΔM™"5;ρι±MΧ&ΕdΙ²Œ₯KsΤH΄ΫΆm7μw+΄€#­ΛQQQ"šτ\›Ξ½iβcΑ $]‡ΔΙω>λ&Έ¬λΧΒνv_Φ6½ Β52Šκ—-΅6s„-&ζ™ϊοR8Œ`0ˆ`0˜υeIΗΘXƎ‘eΕ τ>ς ŸwžΟΰϊβq”Ύσ.ŒDΑ`0§ ˜˜„–Ύρ¦“Υλλλ‘Ί\θή΅ u “ψοΉ68qlή;Ν ­ΏΪβυϊ°kǞΜηΜςΟ άηςΘΪΡΡaίKoržŒύΟ‚πχΉΆ/o»kΦmΙΌcΔΒθϊμ0BΓΦaΥάSu rŽOΪθθ(~φ³W‰X ι’΄~Γϊ,[h"’%IR˜ΔT&ηR0"•IΙ "¦₯TΖ’u($IRπ„™Π1©ΧΈš0…ΖMΕ #zό艬ΜΨΆmΉκβ4—”ώQΞ“YΔ$Tέ»ΆCuΉ°dΙ’Ib‚ˆ2ƒgιN΄Υs1 ΒH$PϊΞ»rδΝ,τ’btνήI’P]] Ώί―7Ω·±²€ΕbA(‚€ihϊήηω lμΰ<Γ8GγΏύ”H`xx‘P(+&0­ΜcA‰DΠΌϊ\xθ>˜ 0_ΰj(8vΎ …‘iZNA1qvbbuμP(„Άφv˜ͺ‚ž]Ϋζϋt&‘G?`ιSwώΒ9ƒ£ ¦σΌPΪ±Ϊβχ°cϋnKǜ4$θ‹.γ˜­ΟΝΒη[OHϋˆπOΉΆ·Ά―Α-k7ζ|}\ ’σΣW‘G³¬R'QλΔ5ŸΑ™cΌ>8ΔK?ύΖΖ¬me ΊΊ›·l„š―!%Eˈ†ta»d„$3E’˜"ΙΙzιY 0¨$H˜ΒΤLn&+cOΚ‘0uΑMύΣO?λŽD’“ŠΈέ*ΪΪ¬s'αφνΫW9νEY„τlίSUΠΊbEΖ&ήεrA–εIΟΉ\BBΣ4DΒax:»Pplq‡8%άnœΩχH’PSSƒΒΒΒL$+ Ψx<ŽP(„ααai’α{?‰ω>›žEgLόΏΤΦo°kΎΫ1“°Έί…N ·,ƒ¦λpΉ\$Ι²s%ΛrΖ[œsOI Ί%}•ˆΝζ΅ΰνξΑHΫrθ†ΥνžtΞO¨G榏Ǒ¨*ϊpŒ… ]Υρ% OγΩ³3^ιζΘ‘#bמ2@wXmΗγXRW’δpε|wžΣ‹έ–ρΕγφ Έ¨η/œΛieKΐ­;wν”^=όΪλ3ύ²YΨΌπ„Ό Bό€₯βlm_‹•«sηti‘t;3qe·ŸCG•{ §Γ₯0-\Βγρ8Ο»ˆšΊ(9fΥ<–,©C4$<(YmB¦ΤτI 0ΰ¨φTWUψj—ΤΤΦΦ”W”JKKύnUuΖc±„aœˆˆHΒ$A† A$š… „ aΔγ‰Ϊښβ‰ν)**Βρ'¬jΪ( Ρ|μθρο]ΡΕ™cώ²Ύ^α&Ύv5―iY†Α΅· ²,CUUΈέnΈέξΜσ,-$βρ8 ΓΘϊhpˆΕ°δ§?_Τ–°œ1œϊ£Ηΐ].ΤΥΥ‘΄΄>Ÿ/“3b5#FΡw鄨αO‘ \ώ@‹ ^ύ§ gߞοv\ Ά˜X Θ‘(\c!ŒΦΧAΧυL>ΐTA$+ˆ2ƐH$’ρ•E…ŽιπΏ8Οgqν°ΈΧΘ(F›2Ž—ιŸΣΣΑ@£##./EώΙӐbW fKLΐ¦½[ŽΚ ιK²†ƒΑQTUΥΐγρ,ˆŽσBκΔ_O[I‹†a€ώΎuίησΑοΰβΕ Ήaλξ=;‚‡½Άπ ŸΨΜ ίϊnαn«νMKΫ°z]n;οΘh/zOΌ ΣΌΊ‘U―CG'ˆ3ΡR$,L| #ŽsgΟ£ΊΊjŽΠOI’PUU  Ί  ͺnTWWQk[‹cν­«Υ₯K›½UU•ž²ς2oqq‘·¨¨0―΄΄€ ’’Ό€Ύ‘Ύvεͺ•­­m-%e₯~bΜ ŽŒ…M‘0…Rb‚A Τ―CΓ#ZUUeΗλΙάe9ωύμξξ±jjsK[λιcG}zUiΈZ1aδωpρΎ»αρωΠΨΤ!DFL€έ›Ο³\Bbxx±x•‡^‡§7WMœΕΑιΗ†αΟGuuuFH€ ΤZ97iš†ήή^˜œ£ςΥ7wξΒ|ŸΒ¬`‹‰9ΰF  D•—&GΪSκ‰b"έ±JΗq뺎ΒΒBŒϊσ†ΪΧ?Ÿ§p]ΈFFψΞσA’e8KA‘ž™IL”,~FQUU…žDKŠα?ώω{ Ξ¦˜8ςΚ‘Ψξ=;vXmΔ₯ΛoˆNό|ΆΖΖΖΠίߏƒAŒeώ7 ΓrΦοJΎ^―Σ‰vΊcηξέ―zνΓ™ϋΩ,DώλhΔ(°Ϊ^][ ›ve>›SΌˆK§~Ι‰»ZŸ—$)GΆχN§«°° ΄±©±₯uΕςe^—υ_ˆ'β:₯TΔ„xj" ŒDš››ͺhΒΕ)))FWg’ΡhΦ1Ψ½’mΩKGžXPΈ«‚€‹χή…Έ?kΦ…¦i™ ‡Γ1)ι:—‡Γ!δi”ώφƒΩ>½Y₯뎝ˆΤT£€€$“pνρx2B"]Ccͺ¬a(ώέG(ϊπ£ω>…YΓsΐ,&ΐΣΥ­΄QŸ73σ0UP€GζEαpεεεθΜχuuΓ52}άνBΖΣٍ‘eΝΠRMιŽίt‚bbΈS8F^~>ϊβqH†woίw6ΕμήuΗG ώη°ΑŒjQx<^/ΚNόBh‹aθκκΒΨΨLӜτ}!JŠΝX,†±±1°Tυωk9~AA!œN'Ί»sz™χμή΅λ³Γ‡_=>Ÿ%›ωηC5ˆ`Ο_VQ-;ξΞJtNμ;ώ³ο]wx·Cwg#ˆ[ Σ4qφά9TVVL+(f §Σ©–W”7¬XΩΊRq)±‹»:Ή€ ˆτD€h$WU.)-Ξ1"Bqi >?qκΊΈθΞ•ν«ΎϋΘ#θΕΕΕZΫ—ίέΦΪϊ‡­m-χ,o]Ύ΅΅­eYkK›³ΆΆΆοτιΣsRtαjΔΔКUi]ަ¦¦L ©©αMBˆΜŒκT!‘λ:†Α"QΤ½τ2˜Υ”Τ"a`έ ­ZΏίššδεεΑησeŒVΞMΕD?4MƒοάT>2ί§0«ΨbbΈΡΕψ??…±¦h² !œNg– Hw¬\.„Cee%Ξωσ ž: 9ͺΝσY\Μ4‘ cdiLΞαrΉ,C¦v0%I‚Bx<DΒa "ομω+Ί³-&>ί΅{g ΐVΫ/υυ’±±.§kQuβB[t]Gggη$aυ9I/š¦eμ–―εψ%Ε₯ΙΏΩ₯,cš4 „vξΩώ^?;£$›yηΉΗQ${ @£ΥφΒ’Rlί΅’΅CFΊO όΜM\©R ωAœΧΚ‘ΩβΔ4Mœ9{Ώ~ΏƎ;²,;+*+š—.mͺικκώ<ͺEγ€H~SMμξξ­[R[μvϋ/»UBOεw+ΐ…yχΐ@ΧΑΔWμ°ΐz"lp7Hό©’ΊώΧΦΆε΅-ΛΫΞ;vl`6ΟσJΕD¬°]wνF^~>ͺkj IάnwΖzzbž„•ˆΗγ†a¨~ε0”‘αΩ<­Y%T_‡ž[‘( κκκΰχϋασω29S-`u]Ηΰΰ Βα0œ#£hxρ'σ} ³ΞbΆ›Σ₯ρϋ?Σ4ƒAŒŽŽZ:—…–thSΊφˆ•Ψ˜n]<ΟΜd]Λρ«*« ‡0<œΣ9Μ’ΆοΩωςk‡^[P±ή6WOǟ@ρθτ’£ΰYΈ½>μΌγ¨ͺu8QxΈ—ΞόvΦΪ'AlZ݈—‘˜υ}Ώ――gϜ…Ϋν†ίο·έS‰Εb…ΒIΫςX ΐ!ηΆJΞj—$9κ–Τ­€ ώΞΞ~b1Ν0β‰XMmu&\ŒˆP[[‹σηΞ#Ώ.§"°Φιt|qως–Ÿ;vμκ,ώ€+™™θ»}Ζκ±Ό₯ρx<ή€ͺjVq:«‰X,†αΑA8Gƒ¨}εW‹φ™žP\8ύ‡_d uuu())Οηƒ$I–°ΙΟ]ƒƒƒ ΣDΣw~9G•χΕ83a‹‰ŒdπvχbdωŒeμΤNQZ`L΅ŒUŠ‹Ρ#ςOžΉβ$δ… Ο₯~ ·.Cά021ξΣ…<Ɋͺιp'§Σ‰„a`0?žn8CαœΗ› 1―zύάΞ=;–h•ΥφžήnTVTΒηΛ[°ψ…–žžžŒ[ΣΥ ‰τΊXΚρ˝²#ΎΪ₯Ά¦£££ΝiO¨2Π#»wm{ιπαΧgΌ3c37ό`€Έ οtΥv—’bמϋαΛ³ϋΡ{ςνλΞ‘Έ· ·―ͺΓ±.aέΊΣ‹Εpξμ9œ:y ‘Hœ'g*1Δβ1  ³³G?;Šχήϋούφ};vŸŸψǎΗ'ŠΟOœΒππdفΌΌΌΛΆ‹ˆXUuεr―Χ9sφl'%W‚„@ίΐ`¨°°ΐιύe‡ŒͺκJœ>}ΖΚ.φͺBδßΆ΅.{νθΡγӏ*]%—ъ2τμά†’βb”””@–εIαMιY‰ιά›H˜¨}ιe8ΗB3Ωό9ƒ3ΰΤ=SQP[[‹²²²I°œsK ΨK—.B φ'?ƒ:pσά>m11άLb‘0œΑ1λλ iZ–eμΔ„μ΄e¬aπx<ˆϋύΥ4ψ.Ξθύsΐ#Q€cεΙυtξˆU2vΊƒ+IRΖ.VΧu”——γROBUπ=fe̎Ήpοέ[Ž˜\ϊCYOa!:».fεO,€NόBh‹¦iΙ’EΪ2ρ{‘( ΚΛΛQXXI’ λzNqqέ‚’Άύ}…r σ€θξέ»ξψααΓ‡goΰ&Ft¨}DOXmwΘNμΨsόΕV›‘‡GΠsβL§}6ρψ+(,Εκ%nœνc$’ϋ˜ρx}}ύ8sϊ,Ž=†?ώŸ~ς>ό$.^μLZζ°Χ6Œ8†††qϊΤiœ=snՍ@ΰ²ΡDTRZ(π›§Nž9 0ΊpώBM]M‰ΗνΞLλ(Š‚’’bœ9}v&D˜Œ=²¬}ω‹Η?=>c… ¦\–qώΑ½`Vέ² ±X,“p.Nw9χ¦ΡΡQhš†’χ~η§fͺΩsΞ™GB<@uuuFHL΅€κάΤΫΫ nš(υ δŸ97ί§0§ΨbbΈΩĐ΄ŒεN'"₯%™i\#―iΛXMΣP\\Œ!βƒCPϋg5mΦπτ^B¨ΆQ‡ §Σ Y–-ΕΤp§τμD8FiYΊ‡‡aͺjN_κΉΏόε}ΧΝΗ@ψ {βΘ0 twu’Ήyiζ|J'~‘΄%έΡ™ψH·EUUTUUe*ΚΊέnH’MΣ²ή'ύΉI‹kqybŒaI]=zzΊ‰DrύΩDόŽΫwύΰΥW_]œξ7)'ΨσώΒj“$lΫ΅Ε₯ε–―5τ0ΊŽ½31ϋ…Ε$Ω‰ςΖυ I†S&¬kτ@‹ \œέc뺎³gΟatdUU•™ς\Υy|žΘΉΣη: 0B@\ΌpαRCcc΅ΣιΜTΪKΪ…zqαΒδϋv ΰΗκ5·`νΊ΅XΉͺK—5£ΈΈ‰„P(‡^PHˆ=λn]ν>ϊhFJ&O'&zvlA€ͺν+Ϋ‘λzfFBQ”IUsΉ7iš†Ρ‘¨ƒ¨ϊΥk EZ”Ά{χ„λjPZZŠςςrδεεΑγρdfd ΓΘrnκνν…a(όδ(Jήέ|ŸΒœc‹‰9ΰfΰ½Ψ‰hE’n7LΣΜ yšΨ™N[Ζ†B!TTT 3ίωB'œΑœ#§ !ΰνJVǎ'PSω—݄HΊˆ ¨ ”‘aΈ,*fΞ₯˜€Γ‡_;³{ΟN°ŽΏΦ4 }}}hjlΆ¬‹άά‚bhh‰D"sμτN§5)·”‰ϋ§?7ΕΤ ]Χ―Ω6V’$Τ/©ΗΕ‹ ι9΅B 1lΫ΄qΛχ9²xΛΦήD|λ1φ όΥ6Ζ6m»U΅–―MΊΎŽD<»nΒlP²d ΤΌρΩF@k΅Ώ+Ž“]a˜ΦΊgŒ‘‘Qœ=s%₯%—΅‘-).n”eyδό… έι*F"avvvχ665ΤΚςxRFQQ!8ηΈt©D„•+Ϋ±cηv”––ΒνvΓεrΑ­ͺ(**DSsΚΚΚΠΫΫ‹xά°82‰Έ~μθρ·fβœs‰‰P}.mά€ΚΚJx½ήdsTΉΆ±X Cƒƒΰqu?ωdmqŽ? ­Z΅· //555ΘΟΟΟXΐζrnJ[ΐz/t’ϊ—‡ηϋζ[LΜ7«˜‰“mj€.'l—³Œ’58πC9yjQή”€X R<Ž`MU&βrΙΨιίΣ#?%%%θλC°²γ'Α&ˆΜΉ˜€M·Ό&;Ψm5Xm…Ζ00Π‡Ζ†¦œ…Φ€›ST A‘% 2ξ S_“ iA1•τΆ΄3ΨΥΆI–h¨oΐωση Ηr& VΚιΆM·όΫ‘#GfdtΤfvxα φώΑr#λoߎΪϊfΛΝfΒ@χρΧΧζf'―x ŠͺWXnΌπ{°w0D)bMγρ8NŸ:‚Βΐ΄6΄D„ςŠς¦x,ήΣs©·_@€@€kšΡs©―Ώ±±ΎN’€Œ5UEE9 Γΐϊ λ±tisΞϊ—ηCccΊ»Ί‘Y?οΦ·―hϊ>ϋμσλ9΄ EΑ…ξ…ΣγAKk ©A°τ ιΔ*ΧΉl`ƒΑ`2LχΝwΰ»Πy½ΝœBKjΡ΅g\.κλλ/k‹Ε044„P(gp φγω>…yc1Š Ϋv‘ΡψύAuƒAƒΑ¬/γDΛXΗŸΟ‡H$‚₯ν+Πυπύ0έY5ΣŸ…οΒE„Cahš6­]lzδG–εŒ_(B[{;L— έ;·Μχι:::ΈD‰/ ΐ:φ ΐΕΞ‹ψω+/!‘HXv`Š]λ\·ΕΚͺ6+3][ QXX8mΫΣ΄ki—ΫνΑ½χ܏{Ϊ‘ΩνŠΫρ½ŽŽkO›yηωΗ€' πOΉΆ―^» υM-–Ϋ„i’ηΔ›ˆEζ¦x¨β+FiΓZΛmΊατ‰£@-}†΅τ ΤΡ§πbΔ( o|@Οψ€œ3k¦iβπ‘ΧpξμωiߘˆΨ¦Ν―_R"L‘›άΤ8Ϊ₯ήήΎ_ύβΠ« c<6Œˆ°αΆυ––±œsτtχBΧΗΗ‚TUΕwνΫϊyηαpώΙτ§}ντμΨSUΠΦΎαp8σ JηI€ŸOΉl`5MC$†§³ …Ÿ›­fΞ*Ί?χή 9e›ŸŸŸ™­šΞvttRά@γχl ΨΕ†=3±Θ Ξ‘wϊ,†ΪZ Εb—΅ŒMϋXG£QT$’πΐ IDATTΰΌκ‚Δⴌυv'ÝtΓ€šJ–Ν5CdWǎΗγPT}8ΖBPΗέ!ζcf:Ω³kϋ/Aτ8ΥjŸ±± :»:ΡPί‡ΓiΩ‰nŠ\• Γ€ΧλΝδšX΅%έΑHηIX MΣΰp8ΰre'Α_nQΥ5΅8}ζT&Λ‚e²Cͺί΄qΛO9²8ƒ‘oP^xLΊ‡H|€₯Ψk]ω³χήρQ\iΎχοTUη ά ­„ D0˜`²ˆΖƒ½λΩwߝζ½c{ΧX{οςξŒν±=kϋzί½»ή™»γ'’Σ0Ωcƒq Ϊd‘€BKSuWχQε–TΥRƒDKΈΏŸOUυιs„TUΏσ<Οο™Œ±UΚοTΡtαœ’ŒΦ`AαθΉ`Xε¦γG?A{ϋχsa ΒB:Λ\ύ²^3€~‚/|ΰk'!;@θλ"!Οf¦eύμΏγοΞ9χρΩ3ηŸ9svΟΩ3ηώ8ΎjΒΏ‰Tά …ηJ)^­CFzFάΒl†aΨa₯#―\Ήz.θϊ€νMΗlow8†••–²ŒzF0::ώΉzυc/΄C«Υv~o΄Z˜Ν&5QSpφΜΉQXι™pŽ€cςD”––‚γ8Ε.Χρl`#‘Ϊ Ζ°χ?zِQ­—y"Ϋi›““‹ΕŽγβZΐΆ΅΅’ˆαon†Ζ{R+C12‘C6†ιϊu8G@HΑ2–aY`HORJˆ.'M’λΕ‘gΛπθœ.ΈF ‡ r*J„hΉ|ΎφΫΣTL£5’hΜ|pZΕ=ΤΧ]Ζ7_U8{ζάήΚq£w @fΟ uuΧPP/§*ΑqœlΨ°aηΞ~{†πAB ‘p;έA§Λν(+VΚ0Lg•6‡#ΈoΟ~‡Ϋν Q˜ΆΦ6vxy™|NFF._Ί‚P¨GΪaNΕψΚ(’jύί>}„Φ"Nϊ€ΫνΒ;›ήΒΕ‹`H)OV«UυsDQDCCƒΉP›Kvv6ΊΜΉϋΉννν75ΗόΌ|,»wyoξ6?{ώΕgώ‘ί R$ΜΛaΐ젘£VZ6“§ΟQ}[έWπ΄]¨ιuΥθ`=œN9ΞνjΗΡΓ»…˜Ύ*Œ [ϋc[ίήϊ…–ΣO°WιΈ Ψ΅k7ܞψ΅#«ΕΆΊfε «ΡT ‚E/]Ό\Ώ{χž½‚π}ˆRŠ ߝΏΎsϋK~Ώί/ŠbHzωύώH4Ϊ΅nψπ²ŸŒΐ(η©έ” ηCΌΡε:tιr{?RKΡυϋύH;ιη/φΧΤn+—Χ¬DΔjAaa!rrr`6›{XΐJΟ'RΑuKK DQDώ‘£0 Ρϊ)11€Ι:u™_ŸB8Fkkk—_p8άε‘Ϊd2Αl6ƒηy”——ƒ.]ηΨ~»–ήVςχλσΓεt"« )¬L)•/μ:ΓΛΛΘΟCϋΔͺd/GfύOBkGP„ω0>Ϊρ!|²ΏKšOΌβΑ.(€Δ£z½1=d€ρA@ccc―‚Βf³!---nc»ΆΆΆ›ΕE%X²ψ^y^JPŠΟΏψΛq‹?>)n—ώ ε„2;€)Ο+(Β΄™Υ *ρ܎ϊΣp^n §(Γ²Ψ+ζBk°(Dy:π1’%7#ˆβ―݌~kzρΖox 2ΛA‰b„- γγ»:»gΗ!Ηf+[±κΎ{ !ˆ4(BB‚—.^Ϋ·οΐA’Ρh4zψπ§ί<όι! ˆ’”^ ͘1pέΊsgeχšά@,Ώ©+Π>iόφ|Œ9^―:NΎηΔή‡Τ„D8†Ϋιλ ΰ@ΏMέv«η Ÿ‹μμlΨl6˜ΝfΩVQݝ›ZZZ‰Dyϊ,²Ύϊ&ΩKHq ½@‡©@1ΩΣ,δ8oIρB„;Ψl6Ε‡50›ΝEn·£FΒ7Λ—Š"Α™'Ο ©Ž0L(„œ/Ύ,»>g柹έnp'?DwΕζΞλt:D£Q‚“Ι„ττtΪrΟ4QΣαϊMf[“ͺύΞνdύOψ«ŸY ŠMT«εΏώζKΤΥ]Α’EKa/°χ8Nπn»}Aϊή+Ν…ηy|ωΥ8yκ,]² E…Ε m³Ωδˆ\μgID"466’¨¨¨KΚSχΉHΏ3ž˜έΣξc΅΅uφi‰—‘FωπXP½»χ|¬z₯δ•_½πŒkύ“O½žπ€Έ%^}…‚ΐμ‘@žq[nζΜ_…L€«ε §tŽ Λ’ b6τζžBθΡχ~«Cρ8ώιρ· œϋt lήΌ9ΈtιUf«ρOPHCv»έΨ³g–,]Χ‰©Έ€xό₯KB;vμάG@ ΰόΉ uT€{|>Ÿθhλˆ0 £‘RΒPV¨HzΟΜιYΉyΆΟ4š5¨Ρ„|yΪ2cκ_[­V6##CΞˆMν‘κ$Τ6Ύ\.QDή‰―ήd‚‘‹ΙΏr'FΣΌΩSœc*—Z,ΐb±ΐl6Λ‰ξB"Γαpt¦i»άuω{?ωύP[σ@BX2€Rœ…†Y)†”RfΫΆm(₯eYYYΘΘȐwEb$€ή ƒί|σΧοχO©­­½=ΫjύΘ–-[ώΐOΣΣΣa΅Z‘Σι Υj{ό)’3 žηαχϋασω`΅ZqδΘQΕο<Ο€ψΗƒBPΐsΏ~n ΕδΖ;‚qc«0cϊL5η’Aƒτ/Nžϊǎ†@ ³ΠN£Ρΰώ•k`·&4f$A}}½b‘³$΄Z- »€u”R΄΅΅Ι.NJcB›› ƒA9G½7Ύϊϊ>9x ή)QZ³ώ‰§ίΏ©H‘0Ώ^‡lΜ'P Σ¦gdcΑϋΣG­ ήΆ:4_ϊμΆw†°Θ―˜ SΊrƒΏ-B‚eβ PΰΨ‘}ͺB@}„£€:SžX]&₯γ§OŸΑ·ηzί·ͺšP5χξiS&°„5ˆ’D!HE*₯4…„¨1$4tχΤ»sΥ„΄4«:kέr₯ΌίοGγ***—Λ%ί[”’Jχ'žηαrΉDτz½b'νΑΜ–-[Fπ<1!„””” ##f³„Ω¦Ύϋ³ˆΫν–„„ΧλυŽK ‰;ƒ”˜ΈC¨­­υQJ§Š’ioo‡ίοWμA!X–…Ωl†ΕbA4EEEEΗqοoίΎ]ω9HΉIψ3bGGU»`K5)w56bc2™““OnΪ΄I½Ί2 lΨ°αr(™J@ήκνάp8„Oΐo~χŽq αpΈKπή^I0Δ±γŸγ·Ώϋw|ΌkΌ^εbLžηρξϋ[Ρά|=‘ρ9ŽCQQQ—”ΆX!‡Γhllμ⬀$€\_5Ρt¦<)Ή@υε5cϊLLœ0)ήrτ„2ο=ό/‡¦CΒa㏠ΧΙϋUτx5͘Ώd% εh_Θλ@σ…OAιΐ?±œ£ηΖŸݏKΟ)&@ˆPqνΐm±ΙyϋνχλEBWPŒτ9ς)š{w*"wO²hό„ͺ Β=DπB‚ „DQ RΪY'1yςΔΌ1c*KΥαΓ<ΟΏ tHΠhτ·”ξ΅uλΦ{!› £ΡΨ₯./φ~#₯ω(ݟ:::(νΌωςεΛ‡”κk―½fpŒaMII ²³³a6›Α²¬ͺ¬ΟηƒΓα!$Κqά΄ψΗ·§KŠ'%&ξ jjj.X%•Zw«J;%άf  ”––NƒΏ§”©Τ·5kΦ|)Šβ3<ϏΗΣ'w'†aδ 4Ε¨Q£ΐqœΘ²μοψΗ?Z“½¦X6nάθϋω“χ€ŸBεζK ΐ‘ßΰ7Ώ{ ϋφοΓΡΦ§ΟIDxτε%Š"κκβγ];πΏύ7>r^_ο gΓα0Ά½·--‰Ω"j4š©L@ΧΪ%AΡ©(Ϋd2© J©‘ΈζΞ™ΚΚΈζJΘφ_ύκΗάΤ€ˆΛ¦΅`ΣΒδdžqή€ω‹WΐdR.pϋ]hόφ Daΰ£€¬FΒ1σa°¨Ž)πΕ±Opιό΅!Jɟ?ϊ6>¨9*±υν­ŸSŸ(E»wοΧzΐ0 3kΞΜeΓΛΛJΐ₯4‘EQŠ’?a\ώΔ»&ŽW{?₯Ÿ|r<―XψύωoΌqΣνΙ_ύu€Χ5 ­¨¨@4•ο)’ loξMn·<ΟBΘ/jjjnλΟ­B)e²²²NH/))‘7a€Τ.Ι6φω# ₯₯(Γ0kW­Z54;ς₯P$%&ξ0jjjΆB~FϋlKA^^ςσσΨΊuλί'{ ‰’ύKΗ½^― γ ιAWͺ©Πλυπx<;v,K)-Ρλυ/%{=J¬ς©2ΐg}9?γ›“_γoόoΎυ?ώ9œ.η€Ξ1’ξZφΨ‹ΝΏbλ»›qζμι„S―B‘Ά½·mm­ Fƒ‚‚Ή―ΠS$I‚B„ΈQšœœΩ‰EϊZ¬ΈEQvPKB/\в²αρNΛΓξzρΕ_ KψR¨BΘ1ΏHq §ΕάΛaMSvŠ„|h8wBtΰ›‰iuf]Q½ιΫ—_Ζωs§Τ‹ δ/{[HJ;α-οly€ό“±P(Œ]οF„Wtœ’ ‚ΎŽ6'Ο²Œ%¬A„(P+ΗŽΞ™6mκ,¨Τ}RJρι§GqυκUΕq Ε-]ηFγ«”²1cΖ°GŽHHΣ}FMH„B!x½^ΐW‡γΩ[™K2ΨΊuλ^ev»=lwη¦9λοΏώχ’Ό„ύ̐څNΡw6oήό[BΘ_FΉh46½'Ά0ΩλυΒγρΐb±ΰΤ©S΄½½ύ‘ššš?&{ ‰πξ»οEρ$Ηq›ΝFԊ±5\Œ ~Ώ_ξWΡά܌¦¦&¨©©©ιφώfΣ¦Mμε«η!Š„­…PXX„B{rmΉΘΘȌλ@ €–Φ΄Ά6£‘±MMύZ³a4Q³ΊYYͺιЊ¨E bΕ€Α`@^^^ά΅K‚A)!%₯Fιt‰gF£QΌϋώV44ΔυVΏ$h0λ©ΗžJ,χ+…"/―c^π¨1–e1wα δζυtG€(Dύ™=ˆ„ό9E€Ξ˜ϋθ9ΰ4ΚΕώ'>;ˆσίͺ €ΰΏ=φ¦ψ―4ΕΎ@Φ>°ζw~€xT/˜aΓJίμχ|πα>W»Σ%AQB”Πˆς™‹–,ψs–eUmš>ύτ3œ9­­97¦rά؍7ήTŽΪΦ­[WPJί·Ϋν°ΩlΠh4rΡ59­Vͺ‰θž …ΠΦΦFyžSJ' 5σ“Ν›7ΏLy4;;ΕΕΕHKKƒΕbι"$Ί\_Ώ~Α`”·Φ]ϋP²Χ’I‰‰;˜-[Ά%„L³X,ςNkμ+φΑΪγρΐνvΓl6γλ―ΏŽz½ή…kΧ=Π}Μs³ͺ_B 6Ή΄N›RΨzχδ‘f³ͺξN’•,Λ²ˆF£πω|πϋύ08ρΕ”χz£#ήxηsΞγψνΗ›D`Yίl(γ9MU3ΏοœFŽcΑ²X†½Q'πύ9””Š$g’‚xλVυ„hΐhΰΎ_©xάh4bmΝƒΘΜPσŠW&£©© ‚Π9G₯”₯›έΗbY9997%(xžΗΦm›Π7­‹œrόάΗί¨μχ™’OΌό σ Ψ¨tŒa̜·…Eʁ 1Fύι½o:3¦Ο¬9(5l·~ ς\DGξFέΥΞΖfUJθ£]v’Ί‹FΑp{Zq«ΠΞ±%n–¨„ί¬ςxw¨(ŠξΦ6/  * ‘€Š§aMΉ99$Ξ/­Ηγ…O5₯’ˆω|τ„ήs.ΰ&ΝεGœΚY,š»&O&Α`PξαΔqœ,$’Ρ¨ͺ{“Σι„Οηƒν³γηmΗΎhΈ™y 4”βpε‘½=šinΫΆνΏPJΥh4bψπαΘΘΘ€ΥjνR]H΄ΆΆJ)mŸΥΤΤΔu H1tI‰‰;˜–±—”fdd 33³‹˜0 ΰ8,Λv±ŒΥιt8yς€;L­­νŠσΫ9 ή%+)Θΰ³…&u+—oQΙΞΙ†ΙdŠk+Ήώ„B!9B‘ΥjρΕργ°\­£Εμ|kμ%–5©C”$hΤ‡?ž?]υIάd2‘ΆfΣΥΣ=”ƒ²‹Sw$A Eξβ7•£hii‘]œ”ΖbΉΉΉΠh”γΟ3€Ν[ήA{G{ΌΣ> " 7nάθKψRΰεuΜ£^Vš5»e:LQƒάμštfs%υΪ%_—ΤθνzΥσw–’―ΧΉψ†”άΒχζΪ²EΔ;|ΉkςdD"9"‘ΧλεZx6°~ΏνL ΄τݏh/sM ”P‚χ+>ΩsμΧίyηΩ,ΛξΧj΅Lw XAF Υ,`OŸ>]ΊqγΖΑk7˜β–HΥLάΑά°Œ½ €ΧεrΑλυͺ:<ΕZΖϊύ~TTT€i4šoΌρFξHD(3θ^Tdμ{6ΒΓΩсH$«]lχξΨ<Ο£€΄žβ¬™ό5υς"7nΛ„‘υ@nΒΎ]tΐχOμ1OοdΠ\—Τ^}»>ΔΎϊ~‹χΉ·π½qŽΕx†#%₯₯ΰyΎΛƜT#Ο6Βεt‚π<μ»χP1ιχ₯Wηύ½+›6m²s·‹a¦¨¨¨‡¬’s“ΗγΣι!$&₯„ĝ “μ €Xjkk;ΒαπLQ£‡CΆŒ-’.ΗΑl6Γj΅‚RŠQ£F ΧιtnΪ΄iΰZˆφ3œΟϋξύD.—KυΒ.ΰJyυR16Ηq<Νf\Ÿ7 |Ϊ 2wΊ#!@”¦€jξ°ΧηΕ–m›zu€ιŽ^―—S™b;`NJ‹@ €ΦΦVY\*½$A‘Υj»Ό7φœh4ŠΦΦVπ<Ÿ°-―Ωdƚϋk{k<Έ0φΎΉiΣ&ε§Ν=xεAφϊ[¨μ’?•c&*Ύ—RŠζ‹Gp«φ)θ7σG"·μnΥYΐηΕξν[αhK•Ξ$ƒˆΥ‚ζΉ3a6™PPPF#ί3Θ׎x6°N§QA@ΑΎƒΠx‡N€ρ΅Χ^32 s€΄΄Tvn’Ίb…„τ\!YΐA˜ύΘ#ά[βΙ#%&~<τΠC'AX-ŠbΛΨΨ €ž•,c₯ eeeχpχoΙ^C"X/]Aϊω‹rquov±ΗΙ»L@γͺͺ ‹ζƒ΅’!Θ“O>Ωεh5.ͺγρx°eΫ&ψ|‰έˆ ƒb*SμΏύ~?ΪΪΪβF(†ANNŽά ―ϋX.ΓαH¨]iiiX½ͺF~@Q>«/ם‘fᜠ^z€[D ύ=Eρ5²rͺ&NU~3Z/‡·=nq|Ώi―„­t’jRΗՁ];·ΑνVvc|‰2w” ηCδ8Œ«ͺB ο,ΛφΙΦοχ# νόE€ŸW½Δ J²²²ŽΘ,..–›΄Z­\έΉ) Κ›3’(>P[[{"ΩkH1π€ΔΔ„xΰCO'b yyy°Ωl?ήΌyσΟ“»‚ΔΘί¬Ο—Σ‰p8¬*(€θDχt'Ηƒαεεδη‘}bU²—σƒΰιǟn„ΐVΈ’vŽΫνΒΦm›ΰ$ζ¨Σ½6Bi8 ½½= `YΆGmDχ±€ί1©ψ;²³s°rΕκψ΅”όψωŸU΄άLΡΙΛγˆΫ(¦… ^»¦ΞV}ΏγΪΧp·^¨ιuB€œa‘]¬Ϊ*Žμήω.κ=Zx¨M¬)KŠ„hŸ4~{>FŒ ―Χ+G$bΣ›(₯ͺB"Γνt‚υPpΰP²—“[ΆlΩ `t~~Ύ‘θ‹μkί?ΦΦΦ&Ε–8Εν'%&~@¬Y³ζYJιλ7¬ιzδ8ΖξΦK‘P£FBffζs[ΆlY—μ5τ6FαξύEn·[υB[?!Šb—Eff&ΣΠ2c*BΩYΙ^‚υλΧ_£3ΐ5΅sœ.'Άnی`0±†±F£6›M΅ˆ|>ΪΫγBƒeYΩRm,AΠΦΦ·AžωyωΈoΩΚ ψΊρΨ―^xφι„πΚƒ˜ ‘Ω@Ρ*¨Έt8¦Ν¬†š Z{Γt4};S! ς†OEFή(Υs7ΦcΟΞχΥNρ‚0Λ8Κέ”3QŠή ge’uϊέH³Z‘‘‘Ρ£Λul„Ϊ†•Λε‚ Š°ο96”x_šd±eΛ–,ΞΚΚB~~>Μf3L&S—(Lχgˆ––©IচššnP)ξ\RbβΖΪ΅k‚ς₯΄ ΫύbFε]“ɋŷۍ1cΖ“ΙτŸž’Δ<:“ˆΉΎ™'Ο  Βησυ©›eYω†‡Q5~ Έα‘mΛΗμωχ‚QΩπ:κΠze`Σ»YN{ε|˜3 UΟΉrι[ήΏ#^š\aŏ½…£2Ι€–i“ΚΚΔ¨ΚJΈ\.ωž •PΊ―π<·Σ Λό£Ÿ'{9}&`ΛAۜ“8Ž“›IiΟRmewΙ-’RΪ&‰] IDATBͺ—Đ”˜ψ²qγΖ¨V«­’”ϊ:::dΛX%‡'†a`6›a±X0ͺjœΆ~Ν VΈ‰]Ι€}ΌTΡΡΡΧέIŠNˆ’ΨΕφΟd2Α–“Η]Ή³)nO>ωχηT5'Ό­­ [ί݌p81Aa6›ε…DwaαυzαrΉβŽ#5¬λž’;V$AGGΗME(*+FcΞμyρN!ψχη^ψεš„ΏƒxυaΩ @ρ4#3sάN₯\ΐ݌–‹ΗβΦΛά*§ƒ}τ|¬κέΏ;{Gο‰χ³rN€β¬G¨ξ|–βΦ δ‘}dggΓh4φHo’ξρά›:ΪΫ!Šφ]{A"CΓ5j6ακΪ•,!EEEΘΜΜ„ΩlΓ0q-`o€†΅ZνΈΪΪΪAΫμ5Εΐ‘?P–/_ξEq€.–±J F# BJ¦LFΓ½ Έ)ƒ}krŽΟσπx<}rwbFΎyD£QŒ5 ŽCγβjΪΔ“₯Έ9~ώσΏ’Έ€C퇣 ο½Ώ a>±|d)B(dB Eοβ!E($A‘4V$A{{ϋM Š γ'bΚdΧ‘NXςζs/<?Œq‡ςΚ:δ "³@©ρ΄Œ,T/^ ­Vy$ΰnEΣ·![οθ†FgBαΨΠ›z΄νι„ߜ8ŠΗͺΪ3J>£QqζίΌ·˜ϊ#rUƒγ4¨¨¨@4•ΕΓ0}ror»έΰ#؎Ÿ€±yhΈ’Š ƒ‹λj ²,JJJδ‚kFΣ%"ϋςϋύp8 ”ŠΗΝ[Ήrεΐϋ(§” §ΑBmmνΧ”GA ‡CΡ2VڍΠιt²%œΥjEζΜ{Π4Oέ e°a;ώ% -­πz<…Bq…΄λ€Υjε›ˆΗγΑ˜qγΐ›Νhž5#ΩΛωA±~ύ<‘]@΅2ΊΉ₯οΏΏ αp8‘T"“Ι„Μ̞Y1±‚@κ Žγδ ΅±€ΕΝμ~Ϙ~&N˜ο-Έεωη99αΑ‡0/­E&³ ΐH₯γK:ζ/Z­NΩn7δkGΣωC7%ςϊŠΦ`EαΨjh Κι‘”R;ΊgNΕK±’»#aΑγ›1πέσ~ΰ\Ÿ;ΌΥ‚1γΖΒγρΘ›JZ­ΆΛύAMH„B!ψΌ^ΪΘ9ώe²—Σg.―«AΤ`@II‰l«ΣιT›b-`Y–ύΛU«V \ύNJLόΐ©©©y‹Rϊ‘H€WΛX£Ρ³Ω QQTTmυά‘c›*Š(ΪΉ$•»cΗswRJw"„   Ξ1•π”—%{E?(Φ―_ e™…”Νφ\oΎŽ>|Wrι3‹ίο+Eάn7άnw\aΒq²²²ΊΌΏϋX±ŠDšΪΐμYs1zτ˜ΈK‘„μxφΧώNθ0DyυaX Ηξ x2šΜ˜Ώx ES'„.4;Q½Θω–Ρ›3P<¦­r3BQpδΰ.\<F}JίwλθŠ'ώ€ΔόS$Œ·¬ΞΡ°Ϋν „tΉώχΕ½‰ηy8;:€¨€Β{@P€φ'uχ-F(+yyy²0½Zΐή¨{nυκΥΏOφR$—”˜H΅kΧώΐ{‘P‡CΡαI sšΝf˜ΝfŒ1ΑεχΒSVšμ%τ ­ΫΌ#Ÿ!r# ­&(€τ.QεŠξFHYY :šͺη@ˆί­8E?³αo6|E(½€ͺfΣυ&lίρ§„šΖ‚"===m¬Ηγι΅·F£Avvv—ŽΫέΗI4B+,ͺη-ΔπαερNΟfDΊλΕ‘ntπΪr‘ύ w+Χι ˜ΏpLfεφ‘ g 8«Nƒ5φΚω`4ΚιUB4Šƒ{?Β΅+β σΊ»™ΦlόO$V”"a’z=«ηB―Υ‘΄΄„Ήθš₯Λ΅ZdΫγρ "οπ§Π9γ§GZ§N†·lΣΣe X³Ω,ί•œ›Z[[₯(π»kΧύ»d―!EςI‰‰€5kΦά/Šβ)ΏίίΕ2VΪ‘ˆ΅Œ•.6£G†cυ smΙ^BŸΘ?\N§\’tÐ’έӝ<†——#Ÿ‡φIγ“½œ$žά°‹‚Ή€j%νΕK°kχΞ„ElΨ_"VXPJαv» ㎣Σιz-ξ–š?%‘0›ΜXΉ|5ŒρΝͺ!οΫ7nδβ4X‘yεAζ_ό•qBfΜZˆ‚ΒRεχSΧΟŠ §m`&H€œ’ Θ.VΏ΄;Z±{η»Tv­ ΙͺGίή˜I¦P’}Dψνω(1^―WŽHΔ¦7QJU…D8†Ϋιλ ΰΐ‘d/§O\]ƒˆΡˆ’’"E Ψξχϊ`0ˆζζfPJAωΙΪ΅k?MφR >Rb"…"<πΐAψu$’ ˆν-YΖ ‚€’’°‹ ½*…ε € ‡QΈ{?DQŒλξ[?!Šb—Nff&ΣΠ2ύn„²γ₯œ$ο°Τ/ΖεΪΥΈzr΄Μ˜ >=-ΩΣκ6<Ήa'%tUΟ Οcοώέ ?¬§§§Γdκ΄U@g E8ίH§Σ!##CC©–‚ηω^έ’ΤζΈjΕjθTz)ά`•ΞΔύ†R:δBh―¬c~‚ͺtŒ€`ϊ¬…(6Bρ½”R΄\ϊ·jυ[‚‚ά²)Θ(¨P=§΅ΉϋvΎ‡pHUtΊ*.~μaΗ€Lς6Α§§‘eΖT\½9.ΧFύβjx‡•${Zͺ„³2Ρ:} ¬Vdffφθrέ› l$Λε‚ Š°ο964pΞ`ύEύΒyΩr››+G$ŒFc—nήέm`›››% Ψ^³fΝo’½†ƒε ±)lήΌywmmνTAFH]@ !²νe¬ύ₯΄“γσωPPP€ϊ4+˜ϊθ\ξ$―">ZQƒή¬L0 FΣc} ΓΘ'„€eY9m% ’Έ€πΪσ‘qζΘ *Ύ9Χ–-FλΤΙrmΠδ#šf…Η–ƒφͺ±ΓΐΤ¨Zz{ €γ«»όΟ·2ΔήέϋΎ­^8,Y •M‡Γ―Ο‹’Δ\S₯.θ’;T¬ …BΠh4ΰ8υΝ–eΑqœ’πΖ’ΊdEάWŒF#μv\ΌtA΅θœ€L8|δpΖή={w&4xyι!ζΨ xwOŸ‡²•Ї)₯h½ό|υςΏϋ†a?ςX³KUΟiΌvχmGTP5dj>ϊŽέμ<~Z:ό΄ J€Φιw£~ιBνω`smΠζΪΰ³ZΰYŽ -ΦΛWUΟΚ²ΈΊκ>P‹“§LA0„Ρh„Ρh„F£ιRt-=`wΣησΑησ!σ›ΣΘ>y:ΩKκ•Ά)“Π>qή*Šθθθˆλξ€ΦΫd2Α–“Η]ΰ·{Pvά£F 77%%ι‚ @―Χ#++ ΓΛΛQPP€Ž±£α˜2)Ι³νΦ?ρΤ&BΙ@υξ~ζμi<τIBγBžž.Gη”Ž€σFύM;΄'ž8Η2β̟½‰Έλ;Ž)“ΰ;^^ެ¬,Yx@II rssαͺ‰pΦΰp)δ‘}dggΓh4φHo’ορά›:ΪΫ!Šφ]{A"ƒ» H(+ υχ-Ηq(..ΗγΗγ!Δ-Β]I^BŠ!@JL€θ•Ÿόδ'AΖ‹’κθθ€ΟηλΥ2Φj΅" aδ]wαΪͺeγ€ τ­mΘ9v<ΟΓγρτΙέ‰aωFF1rΤ(h8‹«!h“o‘λ--deeΙu.άM&Μf3ςςς••…ζiSΰ.οϋnύ`ζη?»ίSΰFAqςΤΧ8|δ`BγB‘‘Ρ%©{탙λMPθυzE¦Ψ±‚Α`ŸE,ΓΛΚ1ξ‚ΈηPJ^zξ…g~”πශ—Χ±E)^U;>vόŒ©RΎq6} OΛ₯™ΗιP8z. –ΥsΎως3œψό ι 9¦Ρˆ³ώΗM,ΫGRΊŸqIPπ<w,½^/G(€χv' υj?«DEE%fΝTlύύΗώΒ Οή—πΰΘ+±τ5@Ω_΅bΜTMœͺϊ~wΛΈ[.ΘάXφ1σ 3)gQP?zgN~‘:%t―6*Tτu΄Θ$o</€Ιd‚έnWνœ+ͺ#ρΗn ΧηΞo΅`ΜΈ±πxKsΈšμ5€Z€ΔDŠ„Ι?ό™hjhD0„ΓαθqQ’Š•A€Ιd‚Ιd‚ΧλEee%|+–ΑWT˜μ%Δ%σδXκΑηυ! ͺ ŠΨ°xlwl―Χ‹±UUt:4VΟNΪ: Ν-0Ά΄ΒγvΓοχχπ—"&“ 999(6 αŒtΤ/[Δ-β:lxς©—ϊDΌs>ϋό(ΎόκDBγJEΩJK’ΈEQΑ‰‡δ&―Έ; JόYτž³0rΔ¨x§X)!;žώΚ«ΜΛ²5 τ-¨‰’rL›Y­ΪY:ΰn†³ι0–ΜzSŠΗTƒΣ*o D£Ψύ!κ―ΕM­zι±·ΔG6ΐΰΤν †Aύ²Eg€£tΨ0ΨlΆ.i3Jωχ~Ώ^ΖλΝ0Ά$―Ύ iώl=ΖVƒΟη“78ŽλrWΑ`~Ÿ¦k Θ:uKζ[ŽcόXΈΖTΒj΅"??f³ΉK š’hmmE8†©Ύϋ ξKŠAɝρ΄βΆ3콏 u»ασωwΎ₯έ{Qε‹™ΫνΖθ1cΰ¨]=hœ=‘φ½Ÿ€πrΊSŸ£ͺΖ‘~Ν D†ή?'Ip>?μ»χCΈ‘ϋΟ.VΊ!ΆŸŽWAA,f3Ο›>­§sΟν@ηt‘θ£]ΝΝΝ=:™Η ƒΑ³Ω ›Ν»έήi;q|Rζ=lψωS ΟΖ;ηΣ£‡qκΤ7 ₯B––&7¬‹νg Υ>H]ΦjQŠ@ Πkqww†Α’Εχ’ _½/;αΔέ/ΌπB^Bƒί$/­γ–τmŠΦgφbά3{±ͺΝmΘ׎φ†sύތŒVμ•sΑrΚB" bοΞχΰhUmψH)Ε㏾-ώ²ί'—Ϊ'GΗΨΡΘΟΟ—#ƒAUHHωχ’(’p瞀₯E¬4ϝ ³Ι„‚‚h4Ω0@—τ&U؎DϋBγΌε.|zκ—/Γ0],`₯λRδΘεrΑεrDPώΖ&`Χ€Ό€ΔDŠ›†‰FQφφ@Π›e,Λ²² ΰyε“'£nΥ} šΑkk½tιη/"ΐοχχΙ.66έ)`\U Ρ aΡ|P•έՁΖRw ω‡‚ηy9œέ=Š$Ν_Š"Ωl6dff’eΦtx†'Φ1z0³ώΙ§Ÿ"ΟΗ;ηΰαOpϊΜ©„Ζeιιι=E¬θˆPΔ'ƒAή5U+π–~Χ)η8Λ–-‡-'ŽcEΉˆθηž{.#‘o@‚Ός·˜ψ.Ε§υ|{1fΝ_F₯ p‘£ρ[Δq½iΜ°WΞΓ*Ϋ;ƒ~μύψ=8;ΪΤ†@Ι_=ώΆψrΏO. x‡• ωžiHOO—-`₯έniG_)m†ηyδ<λε«I™7%@ΓΒω9γͺͺδk3Λ²}²υϋύƒH;ιη/&e}!ͺΥββΊΠBBб,«jλσωΠήή"Š({{+˜7(R€ˆ%%&RάZ·₯ού B4ŠΆnO’[„tΑΦj΅0›Νr£ ‚©w£aαόN“ΚAJώώCΰ|~Έn4#SRt"6έI§ΣΑγρ`xy9ωyhŸ”Ό]ώ¬―N"σ›ΣŠu.j–±F£ K {gXΖΐϟxj=@οœƒ‡ΰμΉ3 +(Τ’ R„"N33Šξύ,Ίz-D…†ΣΰΎe+‘/͐Ž#¬Έ}γƍΚΗ·Θ+λΈ… β{Π+Ο+(ΒμωΛT;‰σA:šΞ΄…„5§ω£f‚¨DCό>voί·SΥΩ•%>φΆπ»~Ÿ\ΩrPο"ŒFΖ΅€•^‡@™gΞ!λλΔDy>i"όφ|Œ9^―WŽHΔ¦7QJU…D8†Ϋι ΰΐ nίΝVŠIv·’(φpn’-`)Eι‡;}AyŠΑOJL€ΈeL MΘδQDkkkάT½^/‡^sssaž3 ­S“j$6†}χ~ω!°―v±±7ΜΜL€§₯‘eϊέe+{ΤίςεJ<ά©9φΣ½“yZZŠŠ‹ΑκtΈΆb)"–yΆL ?β©')ΑŽwΞ'χγόωoNyŠPHĊΎ “ɝNΧCHΔΪΖ&"($τz–/[Ω₯Η…ΣτFν»―ΎϊͺržΟMςƒ˜E!Ύ«*$ς 1'ސˆ„Όp6ƒ( ύ9-@š­ ΉεΣ@ˆςmΡγκΐξΫΰσΊ• $’ŠQ IDATP0+{[Ψο“KQ³ uΛ—‚ΥjQΤKηdι:βrΉΰυx`ΎV‚ύΙ{ge’uϊ€Y­ΘΘΘθΡεΊ7ΨH$—ΛAQ°ϋΨPbuJ·“+«—ƒ·ZPXX( © ΉZ ZKK A@ώα£0ΥΥ'{ )ξRb"EΏuς42Ύ:‰p8άΕ26Φα©{χεp8ŒςςrKΑU12ΩKPΕ\ί€Μ“g ασωϊTŒΝ²¬| ‡Γ¨?¬Fƒϊ₯ !ͺ<( 4„R}Όϊφ8;:ΰρxTΣ€Nζί[ΖQ·κΎ;Ζ2–BΧνίύΐkjηPJ±w\Όt!Ρ±a±Xΐ²¬ͺΥ«”ς$Υά¨½ŒFc—~έΗE~Ώ_ŽŽ©₯8uΗl6cΕ}χΓΧΊ˜.„ΌomΪ΄©_~`Χ:Μ$„Ω gc 9Ή N9υ1φ£γϊw Χž[Ϋπ)ͺŽQν­Ψ½σ]όͺ9σ>PfωγoEwφϋδ’€¨Ρ nε2DΝ&#++«O“;ΪΫ‘uΊP΄}wςο)Λ’~ɐιM±EΧοdoEΧ~Ώ‘P™ίœ†₯ξZRΦΡΟ™ A>²³³e!‘Τ…Ό»ˆD"Θ8uY_~“μ%€ΈCH‰‰ύFΑΑ#0]οt jookλπTQQΧύΛα/Œ[šTς…Ξι„ηFgγή…"N§#GB8#mΣ¦$m ϣ住ΐp΄΅)ΦΉtοdn³ΩP:lBιhXΊπޱŒ%„ΠP ςί(‘P;‡RŠ={wαβ₯ E(†ΕbQ,–’ ‚ ΐγρτ9ε)žm¬TΣ£Ά₯—ΥjΕςe+»€S)p₯ΊσΏ₯τΦϊΧΏό0ξΑμ ήΚΆεcή‚εΰ4Κu ‘p SHύ‘Θ΄WΒV6YUH΄Ά4bοΞχ©6μ T\πΨ[Ρ}ύ>Ή$@ Π°€Α¬L”––φΉsr[K Ψ`₯ο}6‰ωχ-Σ&#”•‰ŠΚJΈέnω¬•PΊŽσ<·Σ Λό£Ÿ'm½Ρ>n4ΪǏ•SR»§ )Yΐ:„B!―ΓΎ`²—ββΞx*H1hΆυhάnx<žΈ–±”.OcƎEkΝ*πΚf“ #(ϊx¨(vΊ{ΔqwŠυ-΅!4™L°εδΐqΧψνωI[‹ΖοGρہh­ –±±ŠXΛΨββbxKŠp}ζτ€Ν½ΏΩΈq£8Όdδ)ΘjηPJ±oΤ]»šΠΨ,Λ"--­‹P²υz½ςόZdA―Χw±ŸUK‰ˆžŒŒL,]r_΄¬.ŸCΙ_<β³7]Lόƒ˜±!±H]HDω ά-@ ΅)Σ^‰μbυZ¦Ζ†:μίύ!"Υ‡γFˆβΜGίΖΰ}κLζΩχΐ3¬ΕΕΕΘΙΙιaΫύZ!YΐRA@Ι‡; ρz“6χ@AΪ'M@vvΆld+$€λr\χ¦φvˆ"…}Χ^Θΰl β/,@σΌΩΠj΅],`₯¨§š¬ΟηƒΖηGΩ»&{ )ξ0Rb"E"Š({s Ψ9§J©4Ε\²Œ5›Νπϋύ5Ύ ΧV―@Τ ˜Ntτ­mΘ9v<ΟˍΘzswbFΎ‘E£QŒ5 ŽCγβjZ凧ہ‘Ձ{ ˆ’’ellIκdn³Ω——‡φ γΠ1~lζήίΤΦΦ ΓKΛo«#vνή‰ΊkWŽPX­ίΫ+₯E£Q9BOP ω‘?žm¬ΰξ}^n–,ΊW΅Nα?ϋΥ ΟOh`t B˜(hdfΩ0wΑ}ΠpΚιsB$Oλ%ˆ‘Θ*_HΤ_Ε‘ύΫ!¨Ϋω^₯¬8χ±wpί'—$:ΖT’}ό8Ψl69mΖh4ΖένnnnF$}χ~š[’6w‘γΠ°¨§AEE…\£§ΧλΑ0LŸά›άn7ψHΆc_ΐ؜Ό&{ρΰΣ¬¨[u–Eqq1222δ:Di3«{„T'ΗD’ρΦζ”lŠ~'%&Rτ;ΟcΨ[[ F"p8πϋύ= υ”,c£‚€αwOΑ΅χ‚&© 7lΗΏ„‘₯^‘P( vΑ΄Z­jχx<3nx³Ν³f$u-ΦKW`;ςY―–±±iiωωωΘHOΗυΩχΐ;¬$©σοOjkk…ŒτμGΌ―vŽ$(λ{΅a…eYX­Φ>E(Τj$ŒF#8ŽSόLιο’ HDτΨν…¨ž·P΅9\'τΩ_½ψμOβN0†—Φa΍ !‘ƒω‹V@«UN³ŠFΒp·^(τσξ0r†MDVαΥSκ\ΐ‘ύΫγ‰˜s,#ΞzόΌ~‘ β+)FΣόΩHOO‡έnο99Φ6φAUΆ€ύτX­S―ϝ ήjΑ˜qcαρxδˆ„δj$]Υ„D(‚Ο녑́œ/ΎJκZΤ9\zCHH‘£x°~Ώmmm½‘6Ώ &Jφ2R܁€ΔDŠAοr£ψ£Ao8<)u_ξnk0`0?c:ηΟNφ”Eνά Κέ±ϋβξ›ξDέn‡sL%<εeI]NΞ‰―‘yκlΛΨΨ‡%ΛΨΒ’" Τ/]ˆPNςά©ϊ›Ÿόδ'‘P R @5@|ΌkššUΗQzXgY‹E~XW‚ ΐησōPPJεV΅’lJ)ό~Ώ,(ϊΚ°ae˜3{^ΌS(ύίΏzρ™{땇ΈΕ$NDFfζ/Z ­N9 )π!xš/@sͺκ Bώφή<:ŽϋΊσύΦϋ ±w£pw‰)Q–(.β.j1%[NfζLςς^r^r[±΅Ωs†o’;ΛΔ/ΞLN&~Ά$“’%Qβ"n )QI‘’(Q Ε Ρ Π{UWWΧςώ«ΣΊ  °ΰοsH’Ρ¨ 6«λ[χήο—BσŒ¨kΡO₯Ώ|ρή9~P—…Β9FRψφ/ }=ΕΘΧ{Π³e=lVΫΈ-`Αqκ>ύ g½ψΞΜ!1Ώ>ŸE ;ߎǽIE$βq@’Ρvΰ0¨*½sωΙνP,ƒA] Ψ‘»,‘HPUψχ‚y@ט@ψ΅ b‚0iΈv‘εΔIH’TφΞw%ΛXλš[ΎΤθΚbN₯Πrς=n΄ΕυEi:Άφ§-»Ά··Γf±ΰϊΊ WtΤ™|ZΏ GO/2:{.ε,c‘«Χي‚sϊXΖξΨ±CψΒγP±Wο1’$αΐΑ}ΈήίwΣ#Oš΅¦^'C Κͺ$(Š*v(ΚΩΖjδrΉ1…ΙΘ_sfΟŊεχTϊΡPρ³şnΥ{ΐϋ›ΜCͺͺΌ lΔ½§Ύλ6κ IΜ!~ύ3ΘςΔ.ρRζ™+PΣ€/ΰΏψτύΌh[n6€e¬ ˜;w. [7!9wΆΡ%”Εsώ\]έΘf²Θεrݝ4AQšŽΙd°pρbΘ ϊΦΫ…‘½oΒO >8xS–±²έξ‡7C©°ΐ;ΥΨ±c‡h6ٞ(]wI’pπΰDc77W=²CQSQ*(ΖZΚΤ‘Pε+ Š%‹οΐ’ΕwT*Γ»~ό—2κ…ϋ“'™­ͺ’Ύ dkO}SŎD!Ο#Φ}~R„Dλμ•p7κ§Ήϊρ9œ=}Ί:κ1³$oύφ/žΠƒ3…eΡ½m3$§σ¦,``I¦x}?(ΩΨ»ψΧΧ‚l³bαβEΘf³Ε›6,Λ;κ ‰\..›…£»υjh-zτί·Ω6κλλΡΪΪ:¦l>ŸG$(ЍΉx υοŸ3ΊΒ4‡ˆ Β€γ;| ΆH<ΟΓ*YΖΊ\.€Σi̟?‰Gίlt £QUψŽS‹γNγɟ(uEΑPιφστΗ.nŒX@hΟ>Π9±ht؞KeΛΨr υθΩ²j'™ί,O=υTNΰΕmuLο1bAΔώo ‹ήt‡ΒαŠZΠΫ}( Θf³ΕEQ°Ωl`Fw![UU‚0¦ύμHV,Ώσ:ζWzˆ υϊώςGΛ΄ΏψΙ“Μγ ΤWPQH<¬?ΪT0ΠuJabΒ(ŠAσμ•pΦϋuσι'ηπαΩw*<‹Ί7eQ7ώ.θML5T θέΈ|s#ΑΰMYΐω<‚―ν“7Ξσ;ž5ΑPhXžDiΚu%!!I’‰(QDΫ‘cΐMŒή*’s0Έt ΗΈ-`c±rΉl‘(ό]α6€ˆ Β-‘}Χ―ΐf9€R©Q‚Bw.Έ΅Ε?mY9όδvˆ5ξ±ΏΙ-†Νrπꄬ(H&“νb΅76Εel–eαυzαr:ΡΏζ~Γk4₯3νΩ΅ !RΖ2Άt,M³Œmll„ίοG&Dδή» =ώ‰fǎΜhŸY© 7₯(ϋμΟώxή_}‹ω(υEe-Κ›Z±nΣ£ϊ 1‡π₯w!‰»J1 Zη| Ξ:oω¨ΐgNβΓχ+ μ΄»ΤΗvόoL«ΝΥΘΧV"=³~ΏΏ8_j[.99άί?d»g?Μ)c4· αΥχΑιpΐλυΒd2Ο­†7ιΪΐΖγdή£'`ΚTŸNΜy[Ρ»~ Μf3jjjаΪΪΘ§D"L&†ηΡΎλU£K ά&1AΈ%Π 0σΏ-YΖf2έQΝ2ΦεrηyΜYΌ=_rεp-Cp_ΎŠΪ‹—γω’ΟXv±₯γN<ΟcΡβΕ€Ι„ή k Ώ»o‹DΡvπ$iHPŒtα*ΝΠ,c›››Ρ܌ew ΎHί!g*ςύοŸl…mNλ=Fσxσΰ>Δρβߍ·CQš@­Χ‘ΰy~LΫX‹ΕRzΟ₯u—Ζ EQXύΐZψΫτοθh€’½MAύ€²³nMΝ^¬ή° &SyϋWIδΠΕ[(δΛ §― E {MSω¨ΐΩ3'πΩύεaŠΒ ©~ε7χŸ1±›ΰ“˜ί₯KFYΐ–f0Œ4c‡Γ$ ήC°χ‡ =~•zΧ―…r#εšηωa)Χ㱁ε8B.‡š‹— w’*‡θ°γκΧ·fψύ~x<ža°εFΠι4βρ8(IΒμŸο]₯‹δ„ι„[+δΡΎσ¨’„π<―+(X–- EQZ±έΫ6Ceͺο%ΫΪωΨ,‡δŽ‹ž Πξ&wJ§Σ˜9kψΦ .Υχ½ΏUΤ\Ί‚¦Sο#/ŠˆD"e-cG&™·΄Ά’Ά¦ύ«ο¨xρ9εΨρνi(Μολ=Fμ?°ρDό¦ξώ3 3LP£;…B‚ TμxP‹Ε2*+’t'C»ψΈΫXŠ’°nν΄4—Y€γηaκzΥ]!αΓκ·ιζHHy=Ÿ›!1λXεέΖTUΕ©wŽΰ‹OΟλ?‰ŠNΞQώσŽc¨Ξδ²―ησβϊΪUp»\πz½ΕωϋJΞMšFσ©χQϋΕ—F—€Α₯w‚σ΅bφœ9Θd2ŎDιx“&Ζ˝σωŸΗβ%Kΐ˜LθΩΌŠΡ‘}ͺ οαcpτφ!•L"•JιZΖjci555π Μft=²ΕpΛΫ‰ζ»ίέ‘T$j|’χ˜\.‡7νG:Ίι…vWUoχαf:4=ϊΤ^*.DQ¬ψ<#Γ̰qύfΤΦΤΨθ[`{χBΟϊΘΫΐͺ΅[ΑθΈ|‰Ή Ί?>„‚0±sκ4Λ’yζ X΅e?―(2N?€ξ«ξ«ψ³οΎ¨όUΑΧi*"Y­θ~x (›ώΰXΐjφ©εξv'oόί·χ‡α;Τiψ‚²Κ0θΩτ ¨γM₯KΧ ΓŒkιšγ8‚ΟGŸΐΥΥmh=εθzx ςυž’¬Γα€Νf«ΌΛC–e4> χΕΛF—@Έ !b‚`§Ο’φ³‹±X¬¬Γ“φ¦ δr9Μ™3ω‡6#=SίβΡ(Zή~–DιT ’(Ž)(΄ξ„Φ‘H$˜3w.ςu΅ˆέ³άθr†,cχ„)™Β@,VqΟEK2―――G ‚δrαΪΓ[ š¦e,<όσ1š*¬ λ!ΙqφΨ‹l6sSΟm2™`³Ω*v΄;Ηcu(Μfσ¨.ΓΘΗi]ΐρ ‹Ε‚υn„-|LίAέ:ΪόνCBBGηsiτ|r6ь Ννwι Y’pβΘ^τt]©τ4Οχ%εΩ =°*@at?²’Ϋ…@0ˆ†††1““³Ω,β0§3Όq”~ψ-#rΟ]κ=θ˜7©Tͺxξ,Χ•(wώE©D–d ­οž2ΊœQDοYŽL(€ΊΊ:΄΄΄ίϋΖe{ι šή;ct „Ϋ"&†Ρvθ(¬±²£4’(–ΝO₯R˜Ώ`·?ΎEg± hY†Ν£PeΘ%€‚»S©y©‘Γα@Sc#–έΞW~NύVΒy„^Ϋ ZΘ#‰ŒΪs)—dήΨ؈P(„\SzΧ―¦‘e,|ϋ;’rk|χŽη°οΐ^d2™›ξP” ½₯μ±€b@βHQQΊG‘νςŒE.ΰ£έί‡Rχ1ΠLά·f3hΊΌωz?9 I̍λ{Žš5‘±}Μv}!qόθ^\οΣ½­ͺ*žϊξ‹Κ&τΐͺŠΒυWƒonB0*ž™œƒE„^Ϋ 6gόό=οmΑΰ;ΠΠΠ›Ν6jΌI;ŸVto„’¨π<ͺP]«0©Ž9ˆέ}μv{Y X-©»τίi``Ή\ΦΨόϋτ>0Ω1A0”Ώ|,ΗUυFi²3™ ,Z„ώΗCΑU]ιΛΦh §ΟBE€ΣιqΉ;Ρ4]|c”$ sζΞ…‰eΡ·qdsωyσ[‰9•FΰP%αώώ’e¬φΖV.ΙΌ‘‘>Ÿ©Y3Ήϋ.£K˜p~πƒD(•]@χ67Ηeqππώ1θF^μ›L¦’½₯ΖΘ.…vaQ‰‘;#Η¦΄‹―ρt( yΗεΏ λƒΧtΏ_hζ\ά»jcΩ+Θs‰‘‰‰Œ M‘e°ΨΚ[+KRΗΏŽπυ½§P@QΏχΤKΚO&τΐͺ„Θ=Λ‘œ;mmm““΅‚ ‰@ΥΒ,« ZaYτnX–5‘£££xΡl‘Ηγή”J₯  h:ύ>μακΪ)šΡ»~ X–…ίοW y2™D:›0‹Xΐ †ˆ ‚‘Π 0σ…—AIΗ™Ύμt:‘ε8ΜΉσt}ύaΘζςN1FΡtζl‘(2ι4A¨((΄ :³Ω\lΩkω’Σ‰πύχ]ΐq½ΎCGoXƎvx*—dήά܌¦ΖFΔV,C’cŽΡ%L8Ο<σL/f €«zI§Σxσΰ>πΉ±GzJ/ήY–…Εb)+΄?kcc SΙt₯nΗHA‘QΘ₯ΠωίBηΗt}NΗb¬ΌA]!!dθΉprabοpӌM‘;`²–Ώ© I?ς"α>½§UPΏύέδžΠ«’sf!vםhhh(:UJN‘p’(Β{¨ŽݟΫ-₯υ}έ.,X΄ιtΊΨ‘ΠΊ+ΪyTOH‚€l&[lολ[δtΰς‚Ίa;V Ήv£*ƒ’eΜxρe@ͺ. αφƒˆ ‚α°<»^ƒƒƒΊ–±₯ιΛn·4M#tχ τnYθ\Δ‚’ΐΰ(I*¦cWrw*7ξDQ|> ζ!=k†Ρ@ΡΝ%/ώ‡UδHA12ΙΌΥλEΛ…λλΧ€σϋŒ.aΒyφΩg»YZ]£]zIg8xp?xž΅Gž€αcKΪλ©ΪEΉΏΧPΥ!λΨRTUŸμΗ‘Ÿ>Ё«ϊ³ΨK–ήƒ»ξY εΗΩ„lό†¨μšSnG€4kFcp XKy!QDtάƒHΏξq’¨o=υ’όο7υ§œ·}ΦΒuΓv<ΙΙςω’›[5™Bb~|>(Švž{“(ŠHΔγ€$£νΐaPU”½ Π4.=ω8T†f«₯λYΐΖb1¨Š‚»χΐœ­Ύ°=ΒνG]ng¬ΡΪuB–eΔb±q₯/›L&ΈέnΤ~ν^„ο»Ηθ†aN₯Πrς=n΄Χυ…&’4W$νŽ΄··Γf±ΰϊΊͺΖIσ™η8ƒƒƒΊ–±ͺͺE_[ ‹Ε‚ξ­!Φ•ŸiŸΚ|ο{?μb ¬p]ο1©t ‡ŽΌ‰|ώζl(΅T_@ίκ΅P(wΖΣ‘(Χν(½³«ύ9ω‡ϊRα/ΚEQXΎr5,ΦcΛebθωδ0ibν7ƌ†ΐ"˜,޲ŸEoξA,Ϊ―χ’ͺRίόΞ ς =°*A¬q£gΫ&˜,ψG$'λYΐΖγqd³ΩbΞL5 Y­θ[·V³‘Ph˜˜Π\¨Jk*wžM§Σ(HZή~–*Ω*εκ7ƒd· •RΘyžG8TώC°…#F—@ b‚PEΤ|~ gΞAΕ²O₯‚BK_V@ʚΐy_X.Εsώ\]έΘf²Θεrݝ΄‹ΉtμL&ƒ…‹CΆXΠ·Jς5Τ!ΛX{8R΄ωο€έQΣ,ckkkαAY­ΈφθVH6λΨίgŠρτΣνK…‘Φ@…ξΥk2™ΐΑÐΟWΞ‹(Χ‘`YV·C‘ͺjΡ=¬σP“Ι4j‰’¨a{’$!vεŽώνΧΑ%zΛΦBΣ ΎΆjfΟ]¨ϋ3αSτ^θ„"/όα¨*|GŽƒ)ˆΕq§ρδO”Ί”ˆ’ˆ`(„t{‰ysΐkU`Ο~˜Σ ΖΖeΫΠΠ€@(„‚Λ…žm›‘£1 ˚°jέVΪgλ~―LΌ½Ÿ·/ŒΙ OΫ°¦ς‚Tσθ<Έƒ„₯RO|χEyZn¬ͺ4ξ­Q¨©A0΅€-½γΝqb1˜2Y„φμ―—£Δό€gΝ@0–'Qšr]IHH’„d"JΡvδXΥ$Ψςe`YΝΝΝΕχ2Mμι₯‹’Χ•.΄Ό{ΪθΓ'†AΔ‘κΎ±ζΑ88Ž3}Y›ΝηyκάΩUgCΚf9ψuBV$“ΙŠv±Ϊ$€β26Λ²CσΞN'ϊΧά±¦ΌcΝ­†_Ϋ J‹DΚ:<•³Œ ƒΰZšΡ·~MΥύ[MΟοωΟ‘0 κ=&Dη±Γ£vΖκP°, ΆL\©ΈΠ^Scu(Κ=TŸωkœ}ωi]`6[°vΓΓπϊΊ?ƒtτ ϊ?κΟ¦Σ΄ u­`L–²Ÿ―ap@WΛεUPη%yΟ„X΅pΓ–σyoΞΆ? Ί !΄g~b³?Ύ*· αΥχΑιpΐλυΗύ4—³ρ&]Ψx’,Γ{τL™κΪ+Ph’έ—ΛUtn*΅€-·ΛΒσ<¬‰$‚oμ7ϊπ „Q1A¨JfΏ΄L.‡t:=dι7βMp€e¬Γα€Κ²Ua₯:χ嫨½x 9žΗq㲋-wβy‹/L&τnζ΅• IDATX ΅J.Β-‰$―οƒ"YƎ•dξt:ΡΤΤ―Χ‹δœYˆ._jt “Β³Ο>ϋ < ό­}±:Ӎ©œ Π’~5a0νg^ MPhέ YΜαΤ ΏΟ΅ξΧΨ.lΨ² Mϊγ„‰λŸ£»PΥ  ‹:ο\°f[ΩΟη…οƒ:–ŸxPτCO½(Ώ>‘VED—/E’cN19YΟΆtl&‰@QdφΎ Λ ξΛυ–’R@οϊ΅Pn€\σT"–ηα=φ–Ρε”…Ιf ˜3€—B>Μφ…]Δ–P΅1A¨ZΜΩ,Ϊ_yͺ’```Η•]φ- H§R ―uͺ±Ψ"L>ί‘N(ŠRΡέ©tBQ”ao€΅55ˆ¬\‘‘ήθ’ŠΤ}ϊ9Ξ~A0000.ΛX―Ο §Σ‰λΧζ›~ٞyζ‡ο)΅ @Fο1±XΗίκs4©άRv₯έmξz€ I’ηϋG‘θύH·Ž6;6lέ‡ΓUφσͺͺ"|ωβ}ŸέδOhl(š»q†ΎΘρ8|ΰ$ΊSeYUQ6ύαK‘ ?Έ*χΆZϋ6ƒΓα(›œ¬'$rΉΞ~O-&ηλ=ˆ\Ž·uuu£RΗ²- H&“ήCΗΐλ"6QΠ ΐζrˆF£‘μR|6›ύ ΨWή °„ͺ†ˆ BUc»ήοαcCai‘Θ¨P;ŽγΠΣΣƒ«W―’ρΤY£·"Ξ^xΞ_@.—C6›Χ26Γ0Ε7Τ|>ΕK–€1™Π³y=”*ZbnyηjΏΌŒL&SvΟEK+΅Œ †B0Y,θΪΊΑ·sηΞYFΧ0<ύηίU@mέ+pΈ'ή>6ζhH΄‘'½…lΓƁα‚γΚ©pόΏ‰\JwYsη/ΖύλΆ€eˏͺŠ‚ώΟίB*2ρ£$CB’¬₯ΌkS^ΘαΘΑאNκŽηpP•mO½„κΌ==μάΉ³½λ‘M^ΦbA f«—SH$ΝfαΎ|Νοœ2Ί„"*Γ gΣƒ nŒ7•.]k£}c-]sAΰωθΈΊΊ.©"MoΏI’pϊτιQx‰DύύύPΎ#Ηa»kG TΥs5B˜όAhΖ“5·PˆΪb0g9€‚~d9ΩμΥj:F$AΈΏ΅GO v Xε9zϋž=9š†ΥfMΣ iE ϋ¨ύ@1ΐHUU€Σix<„³€aΰμ)oέiΞkέΘϊېe0 “Ι4¬.ζ†ψΡΖ(ŠΓ²Hf2EQ›yδ‘_όκWΏšΨˆδ*ΰΘ‘#=λ6¬9Cz@Ω«ςl6ƒT: ›ώbs9JGJ”‘»rAΐΉέΟασ#£»Ϋ@QξΊϋ,Ίc…n,‰θϋτ(Έδ―w‘CΡ (šM3 iΝ€5™QΫ2&«^ŽΔkS23Ϊ€ZΩπΤ‹xϋΧ:Έ*ζη?ΉΫd2₯Lls°½pΉ\eΗf΄ρΓt:ΨbξΩΊŠά"χ@zζ ̟?’(Βf³Αn·Γf³κJhVΘ₯σω<β0'Sξ?XUαtε° B²Ϋͺ«E__‰‰"‘²Ω,¨n€uŸ~~‹ŒRAαβίv]ωε-ώΖ„) „›Β1ΦΨ<η/ ΰ΄#Ο2ς"rρ8pε*|ϋ‘ζΛκ[²+₯ͺp„£ˆ/θ€X(ΐf³₯΅‹ΐœ³Ω ©Pΐ@Žή>˜«Δ©„RΈ―v!5g8©³ΕRΌsύ2΄»κ,ΛΒd6#‘HxX–½χρΗaΧ]7w‹~ pδPη•uλן‘ > Œ•N§‘L%Ρζσ; zdFD₯.EQΘ\ΕΙύ/ˆ|yBχ9M¬χ­έ‚φ™st#ε9τ^8 !ϋλ/νŽ4kF}Ϋ˜¬εΗͺDQDηΑΧΤYΆ’”ͺlzκETΟmχ ζŸώιŸLn·ϋuŠ’V΄··SΝΝΝpΉ\0™LeηοAΗqˆF"`y‘έ{ΐήd€βdΒ{[p}έj446’©© ,Λ…€a–rB’P(` ƒ$ΙΎΎζ΄ξdaUαΊΦ G8Ύή ’*—ƒλjΪυ:ύϊ]ΓΙƒˆ ΒΝSφM@¨FXA€Ν£FΖ―5Cγ鳈ή}R©κκκŠέˆ‘Ώ΄ AmάI–ed³YΜ™;ΙD}ΧaζΟ FœX?― ΛσΎϊ|σλˆF"ΕΩώRA‘%Όj–±7ξ6R]]]«†ωGΏmt“ΑsO?wπGώ£Η((―(λoΪΫΫƒχN½ƒ{ξΎwTΈ\9΄Ž„ΦυQF܍-ν\tŸϋ>~ύ$rΊΟηrΧβu[ΰΡY΄ dΠχωqHβΔ6‘†^4κύ•…ΔΡ7+ ‰εΑοόη&τΰͺŒϊϊϊŸX ‡Yΐjcmεœ›"α0 ŠΎΆ&N5p«QX½ցeMθθθΗqE'*­Λ2–{S*•‚X( ιτϋ°‡u_U‰£«³»ͺgž@ψ* ΑšΞœƒ-E6“ νb΅»rf³ΉΈN§±`Ρ"ˆN'Βχίkt9Γ°Ζμ;Y’DoΚ2VUΥίΪ½{χΣFΧ0Y<χτs(•z€ϊλξιΒιχί+ώۏχ—&ΪF.e„ Ξνϊ#|°ϋιŠBΒηaγCOT™Αnt_8<αBbΑ%°ΨkΛ~V’ 8qd/βƒΊ9)•R6}wš ‰]»v=ΰχ4 X‡Γ1¦lΈΏ²,Γΰ0¬1έeuCθ_}D· -D:..\kωΪωOOH‚€l&[lο`t9Βm ‚( όŽ€’€b:v%w'ν ΅4–’(ψ|>$ΜCzΦ £+†³«ήΞ ""‘HY‡§RΛX‡Γ––ΤΧΧ«ͺͺώxΧ]O]ΓdρΜ3ΟΏͺBύ]ŸΗkΧβΜΩSc&cddG+ΩϋNόΓcθ;――@Qέ±«Φm…Ω\>*0Ψσ1ρΤ›\h4†–Ββ,οR&KŽ~ΡHŸήSpͺͺl{κLλhΰ]»v}¦ιy<΅₯₯₯h·\Ι6‰@,ΰ=q«]F—0ŒΜŒσ;ΰσω@QΤ°σΫxά›DQD"$mWύž0]!b‚@0s*…–“ο‘p£M―'(JΫόΪΕ2tΡΧήή›Ε‚λλ€l/οzcž Ÿ‘ώϑΛεΚZƎL2w:πz½”ΓαPišώί»wοΎΗθ&‹ηžώαnκo ‚ Έzυ ΞΌκ¦ΊΐΡ&EΖGŠ“λ[ΰγϊ6V›λ6>RqΡZUτ_zέη'5V»Έ.ύ?‹ΕΛεPΑyx>ϊΔθ†!Y­θ[·V³‘P¨8©έ()MΉΦλά¦Σi$ -oΏK"itIΒm ‚xΞ_€««Ω̐CU%»X­;QšŽΙd°pρbΘ ϊΦ­2ΊœQ΄Όuξ«ΧN§‘ΈΨ§:•K2―©©A  †1©ͺΊgχξέAƒK˜4ž{ϊ‡»(•ϊΊ·S―^»‚sœS@”ΒΗ»πΞΏώΎ<φwPύ.BS³›·}M-mΊ‘ yτ\8ŒtτΚΝw4„ξ„Σγ+ύeǏξEDOHPΰ‘*ύα4vm€;wϊhš~ƒeYK  5 X |rr>ŸG2™D&“««-oΏkp£ΉΎvd› /B6›-žΣX–vΎΣΉ\\6 Gw/κ«(+ƒ@Έ!b‚@0’φLA,Ž;'’4ΜIEC!€ΫCHΜ›ktEΓ T νΐXγHΔγ£rBF&™;NΤΥΥ! EΥ8°sηΞ£λ˜,žyζωKP\Ί|~€? ^*,z>xϋ0=κ>ž¦i,ΊcΦmz 6{yλUΈ8ΊΞ@._1_†ΐbΤ6—OAWouξGψΊξrͺ¨€zβ»/αΨd_5πΪk―Ήhš>@Σt“ίο§λλλαt:ΗLN„%ž@ΫώC nr\n²IΜο@zΦ C‘ay₯)Χ•„„$IH& DmGŽUVp»Aā`0l–ƒοP'dEA2™Τ}Ռ΅;ωΪHΛ²πz½p9θ_s?Δ·Α ‡.zm/ŽΗ@4 žηuCνL&œN'ΡήήN˜KΣτΞΞΞΞiλ<χΜ3Ο› ό.* }yι |tώέξ„˜KαΜ Ώs/²Θλ~/‡Σ7=64ΦTΑ~6½‚ LžνpmλΤϋ•ύœ’(xλΨ\ヲχεy(Τ£τ‚Όo²Ž―ΨΉs'S(^°0 MMMp:Εμ™rB‚ηy Δb`ωάΠ»*qzΣ(Έ]―ΎN‡^―&“©x.0lΌIOHΔγqH² οΡ0U‰56p;CΔα¦QU•Όn&χ嫨Ήx 9žΗqݝ΄_₯γN<ΟcΡβΕ€Ι„ή k‘Ž3§ΰVΑf9χμ$ ‘px” (ux²X,p:hhh€ίο§lˆΗγ`t “ΙsOΰQ”ϊJΉψεψτ³ £ώ>Ωχ1Žέ£θτ`Εοœ1›~ M­ΊQαKο‘Λw'eΡZΓέBσŒ»t8uςϊΊ―κ}Ή¨‚zβ»Ώ”χOΦρU 4M5€‡@ΡV³V.ηά”Λε ‡‘J―ο―Ί m•zΧ―…r#εšηωa)Χ₯η7=!Αq„\5_|‰Ϊ‹S#_h*Aήί _ς’!ά4”ή¦&αΧΒΫωΨ,‡δέ=A‘u'FŽ;₯Σi̜5 |k —.1ΊœQΨbπο;ι† ¨dk·ΫGZΖώΞΛ/Ώό‡FΧ0™<σύώTžͺτ˜O?ϋŸ}~‘Ψ‘ΈϊξΟpς_žD.©Ÿ„n6›qούλρ΅Ua6›u'ζ2θϊθR‘Λ_½ˆqΰ¨mEλμ{ΣΘΩ3'pυςz_^PUκΙ§^”υν©¦ »wοώ€ίomm…Φ‘°Ϋν-`#αpΡΦ©ΎΌ…Α₯w‚σ΅bφœ9Θd2ŎDιx“ͺͺΊB"ŸΟ#•H€εyxOλ5Γ οο„―Β΄ L&ώ+―ͺV£cZ’`xnUΑιψe*•’X–Υ ³cYΆ8¦b±XŠΞH‡΅΅΅jψή»₯l[λFίΎ}U΅™hν낉ηO€¨Ρh---Γ±5[SΝαIQ477#ŸΟ«ρxό'»vνΊφΔOΌft“ΕsOΰ'?ώσ?aκ/τsαΣO rΰ?ώ7„?;Tρωšš}XΉκA8εCΰ4±kˆ\> Ežά‘›«ΎySφσŸ|t_|zΎόSdχ¬~οΏxe±*Ψ΅kΧfUUg]]ΪBingΪ8 žsS^Αf³έΦ}εŸ &£«ΞυΝ›ηfΫΫΥΈέ¦ΊΊ:JΛ™±X,㲁- H&“UU5σόo*²pT©²§ vŠšŒ Β4†(P‘Κxωε—ΐΤΦΦΒνvœF~d¦˜0-Š"8ŽC6›…ΫνΖΙ“'EQΎH§ΣKλ·~«κήvνΪυwEύίn·°Z­°ΩlΓ]†EQH§ΣˆΗγΈrεŠΒσΌHQΤͺνΫ·Ÿ1Ί†ΙδGφ§;( ½άη(1Σε_€το<Σ4Ewލω‹–κZΎ€"½v©π䏋Xμ΅,z4[>ΛββηγύχŽλ|5)Έ²ηŽ(₯Κ«žyζΏιΆ.¦:/ΏόςE½m±X¬³fΝ’=άnχ0›TA rΉAΐΐΐR©όλγ?ώ;FΧ0’}ϋφYxž?Γ0Μ‚{ο½—Ξf³Ε|-œndΗeδΗL&ƒD"UUζ‰'žψΆΡ5„€Œ9UF&“ω>EQŸ₯ΣiYΕ1ݝEΕb)Ž $ tttΠͺͺΞs»έ;Œ§ͺͺ~[UΥ½#-cG.dC–±΅΅΅΄Ιd2©ͺΊηΥW_υ\Β€ςά3?Ψ‘ͺψΣ‘Os]0ρΟ…„έαΒƒ›Γ‚EΛ* !;ˆkξ»%B‚5ΫΠ6θιΎ‚³§Nθ~½δΫΩ³€ΪŠ>τγ80I‡j(»wοnπ:Γ0–P(DΧΦ֎Λ6N«ΦΧמ‘θΐqά°¨££ƒN&“ΕsUΉDΉσ(ŠH&“ €/­Vλ³FΧC †CāPeάθ$ό'UUΥx[ώfε%kUΕ`ο…Iwk Ύy«ΑZΚ‡*†ϋ{qςψ›ΊiίRΛZΘMχ¬~Pς‘Ώψ‹εSξ¦(―ΏώΊΐš¦½~ΏŸρxŸΎ₯ϋ«Υ†uZvD…±¦Μ@}Έ|κΦΉό4ξ„«!Tφs©Δ :Ώ©P~ι»yώ&Xζ}«Σί‘/˜~΅cǎ)o±pαΒΏπˆίο—¬ ˆD"Šͺͺ€­=φΨδZp}EμvϋOUU±`Α&N;ڞ„vή‚ “Ι(>ό‘Ρυ„ςl‘Šyε•Wf*ŠržeY[SS₯·Œm2™ŠΛΨΐq\1―"‰ ――φΎώoΟΨω«Κaqε_ߐσ΅ώ΅ΩbΧλ₯΄»—ΪRΆΙd*.eg³Y€Σiτφφ"ΓόγΩ?ιFΧ0Ρ ZξΟκώƒy™ήcά5΅XΎςΨμϊ_ŠTΐ@ΧGΰRύ“rœzΈκh–·(Ξ ή>~9όΘV,xhΔ‚Œχϝ‚ δuΏ[:ο?}ώA˜Ό`ŒIδΛo=ρDΎ₯鏛››αχϋαv»‹ΞMΪ…u鲡 θοοWA€-ύήΜwν5Ί†rt=ώΨڌίϋχ>ŸMMM0™LΕ₯kΕ1Mm'bδ΅ ˆΕbͺ(ŠyUUοψΖ7Ύ1m—ξ „©B•σςΛ/€Ώq:¨««Σuw¬d†$IΘf³ΰ8v»gϜA!Ζμο—`ψκ9XΎα•+`·ΫΡ2ΚαIL Γ N#•JαΪΥ«H&Όq«]F—@ άΩ Χή‚ΪΪZ„ΪΫ‘9Έi MH”ЉπΠΗζwN‘ρύŒ.‘,’ΝŠKιI°55XΆό.δr988N°,[ΦβvδΗD"l6 πψ㏝Ρ5}ȘPεlίΎύοTU=ΝfΥ\.WΡέI(MΗΞd2XΈx1d‹½kW]Ž. gΞΑσΙ§ΰyƒƒƒ£F;΄, -ƒΒεr‘Νο‡ΝfCΟζυκ.@7‚§=[6ΐf΅‘mDGBλJŒ uΟσ¨ϋμ‹ͺp}ν*Θ6+.^„l6;ΜςΉτ<₯7Ί™ΛεΝfU‡·oίώχFΧC *CāPεP₯šΝζƒ’¨T"G‘PΣ.vd:Ά(ІBΘΜ!1oΡ%ιzμm8zϋN₯L&Η΄Œ­©©?c± λα-ν僄jB²ZΡύπPV+όΑΖc›J₯J₯ΰΈήοQ½<γIΜο@zζ C!ˆ’8ΜeN•„„$IH&aYφ·)Š*oσE ͺ†iν„B L^zι₯ΜO<ρ₯ |SKŽΥR£G~Τφ&΄ίkaWuuuH&ˆ·6£ζβ%0yύ9t£ TξΛW‘žΡެͺ‚½±+‘₯dS†:m1 Sά£`XqžGΦοCνηAέ §"α+ °,=ϊ υ†Bhjj‚ΛεΓ0Ί°™L±h–d ‘_ν]¨:X@ΑνBχΓ[ΰpΉ0kφl¨ͺ ›Ν»έ^toΞGZ†ΞȏZWRUΥΊ}ϋφwŒ‰@ Œ ΒaΧ]ŸύŸKοz–s9Y†ea2™Κ νb[ϋ=EQPU<Ο£­­ ½Χ―ƒojDνg_Tε-+p]λFrξp’«ΝV¬M«S«K,Λ‚eY Dδ=΅p_Ί\•΅ns( ½Φ"τ# ’ΉΉ.—kXx›Ά|¬ žηιο•Ο£ύW{`βͺsηI₯€ξ‡6C¬­Α²»†φ$μv;μvϋ0‹[­ΖrB"›Ν"›Ι ζΛΛΒΆίύΏ*Zy„κˆ a ρmώΓδά9Žά:ν’Ί’ 0™LΕρ‚l6 wM "’F’`ο\Qy˜ΌG8ŒdΗp<ΫA‘Υ  (ͺ4ac€aΰμι3Έ a8‘―έƒψ’ωπϋύE!a³ΩFYΐjb"—Λ!άίY’ά³ΆΨ€Ρ%θ2ΈμN$t`Μ9eΉΨ‘°ΫνΕσͺͺΕ1Ν‘BBΔΐπ<―οOώύε‹atMa| a A yΥw¨Š’ •JιΞ—ξO(ŠR̞°Z­πx<¨­©AdεŠͺ^ZΆ_Γχζ‘’½νΘяΠ>Ν)¦©© -Ν͈έu'β η]P$1o.bΛξ@SSS1KΒαp m™%FQ(ΰ;ά Gίu£KΠ%_οAtεrΤΈέ¨««•r]Ί'‘·+‘L&!+ Ό‡ŽΝ dN‘@˜B1A L1œ=½πœΏ 9žŒk›a˜β|>ŸΗβ%Kΐ˜LθΩΌ S½ ʚ//£ιτYδσyD£ΡQO…Ba˜Γ“ΣιDsK jάnτ―ΉY›Ρ%ΰ|­ΈΎξΈ].΄΄΄ΐεr sn)$DQD,ƒ h:υ>j?θtQ=›Ε²XΌdΙ°₯k†aΖ΅tΝqA€η£Oΰκκ6Ί$p“1A LAZή~–DιTͺ8&PIPhέ ­C‘H$0·£ωΊZΔξYnt9i:}΅Ÿ_ΗqΥ‘Π,cUU…Σι„Ϋν†―­ ‹=Ϋ6!_ο1ΊΒmLΎέoΩjE›ίšš8Ξβ"ςH Ψ|>x<ŽγPsρšΞœ3Ί„ŠDξΉ B½σζ!™LΟ1εΊεΞS’("•Hΐ’L‘υέSF—C ΎDLSZ–ασ(TEA<Χ}£Φ¬T΅»ƒ₯6v»MXv8_«Ρ%ι£ͺπ9Gίu$“I]ΛXY–AQ΄p?0ΚlFΧΓ[ ΩmFWAΈ ‘­Vt=Ό°XQWW§Σ Š’t›R©‰ύa΄κΤκuFε½-\z†LjBB;οhέ—rη©ψΰ E…οΰPUκRE *Cā0E±Fch<}vθΞ^*₯+(Jο4]|Γ—$ sζΞ…‰eΡ·qd³Ιθ’t‘d½aN₯180€l6;j,D»8cN§ „B(ΈœθήΊ©ͺΗΉΣ•‘Ρ½u# 5n‚A444ΐιt³€9Ά—Νf1‹ΑœΙΒΏχMP²ltΊ(,‹ή λΐ²&ttt@Ό’Φ&ŠIDAT³¬ΆZ­EΨ}rη§T*±P@ΣιχaG.‰@ |Eˆ˜ ¦0MgΞΑ‰"›Ι@„Š‚B»Kh6›‹£ιt -‚θt"|½F—SFzu/˜Όˆh8 žηG νβΕl6Γιt’±±ΑP|k3ϊ6¬(bKΈPϊΦ­ηkE DSSœN'Μfσ0{ΤΧ/ΟσˆF" DΑWίΛ猒"ύ«οƒθvaΑ’…H§ΣŎ„V£vΎΡ‚ ›Ιΐ¨κ4o06DLSEΐP’-{<ξN₯γNEΑησ!±`³f]QEΜ©όoμ‡"Ιχχ#—Λιv(¬VkQPψ|>€fΟDtΕ2£K άDοΎ ΙŽΩhkk+ «ΥͺΫ‘‘pŠ,ΓΏχMXI£K¨HfF‰ωπω| (jΨωd<ξM’("’ŒΆ‡IΘ$0Ε!b‚@˜β˜S)΄œ|…cz‚B[RV₯x`±Xννν°Y,ΈΎξΘv»ΑUΖΡΧ_ηqH²Œh$RΦαi€elss3ššš]± Ɏ9F—@˜Ζ€ζΜBtωR444…D% Ψ|>H$‚‚$ΑΫωVΥη£H6+ϊΦ­†ΥlA(&&(Š–r­Χ)M§Σ(HZή~§κ…@"&„i€ηόΈΊΊ‘Νd‘Λε*Ί;i‚‚eΩβE@&“ΑΒΕ‹![,θ]»ΚθrΖ€φΣ/Πpζ„–±ε2(J-c].Z[[QSSƒΎυkΐ΅ωŒ.0 αZ[Π»a-\.|>ߘ°ω|ΎhΫψώ¨ϋδS£K“λkWAΆY±pρ"d³ΩβM –e‡_τ„D.——ΝΒΡέ‹ϊ«Ώ^06DLΣU…οΘq0±8ξ4žό‰ρQ …†FζΝ5Ί’1iyο jΎψΩlρxΌ¬e¬V§fΫζχΓl6£{λFˆu΅F—@˜Fˆ5nτ<Ό&‹ώ@n·»h«]\—³€Νf³p_Ί‚¦)`‹š˜ίτΜ†BΓς$¬VλΈς$$IB2‘%Šh;r¬ͺͺΒψ!b‚@˜&°YΎCΙd²’]¬φΖ ˜?Α²,Ό^/\N'ϊΧά±ΖmpEc ͺπ>{$Šd"t:­»?‘YΖΦΦΦΒ 6+=²’Υjt„i€l΅ λΡ‡ X­Ί°#ΗρR©’‰lΡό‡Ž‚ͺςλκ‚Ϋ…πκϋΰt8ΰυza2™ŠF†7ιΪΐΖγdή£'`Κd ˆ@ LDLΣχ嫨Ήx 9žΗq㲋-wβy‹/L&τnX ΅ΚݏhYFΰ΅}`Σi ΔbΘd2Ίše¬ζπTp»ΠσΠ&¨Δ2–πk 4zΆl€XγF0Bcc#œN'X–ΥΝ’ΰ8ƒ`3Y„φμ―ϊ|•zΧ―…Β²X΄x1xž–r=XŽγ δr¨ωβKΤ^ΌdtIa!b‚@˜fx;ί›εL$Šγ>εήΰ΅ξDιΈ“ΕbA:ΖΜY³ΐ·Ά`pι£ΛVΪ³Θη‹DΚ:<ιYΖrήτMBυr}υ}ΘΆω …†Yΐj‘₯ Q‡,`ΓaPbΑ=ϋΐπΌΡ%ŒΙΰ;ΑωZ1{Ξd2™bG’tΌIUU]!‘Οη‘J$ΐς<ΌΗί6Ί0Α1A L3˜|ΎCPnŒ;Χ.ΆτΑγρ Ά¦‘•+ 4Τ]˜Xβ φ„,+χχΊ\Ι269o.bwέit „)HlωR$ΞGkkk±#1–l4$Λh;pΦΑΈΡ%ŒIΎήƒθΚε¨q»QWW7*εz,ΨB‘€d2 YQΰ=t Œ7Ί$0Α1A LCœ=½πœΏ0 •ΝŽk›a˜β…B>ŸΗβ%Kΐ˜LθΩΌ~J€G;»{αλ<‚$!‰”ux*gΫΨЈΘΚH͝mt „)DzφLDW@}}=Z[[ΗeF! h=φ6\W»Œ.aLT†AΟ¦Aέo*]Ίff\KΧΗAx>ϊn£K"“Β4₯ενwa‰'N₯ Šβ˜‚BλNhŠD"ΉΘΧΥ"vΟr£Λu>CύΉΛε0000.ΛX―Ο §Σ‰Ύυkΐ·4]a kjD°Ϋνe-`Λ97Εb1π<†s‘ώγ F—0."χά‘ήƒŽyσJ₯Šη†r]‰rηQ‘J$`I¦Π:άͺΒWƒˆ ašBΛ2όBU”!• ξN₯ώπ₯vv»MXv8_«Ρ%‹–“οΒ}ω*ιτ(ΛXmάi€el „ΙbEχ#[«ίŊ`(· ]l{ΓΉ©¦¦f˜lΉ…λD"L&η΅.4Ÿ|ΟθΖοmΑΰ;ΠΠΠ›Ν6jΌ©΄^]χ¦ΑA(Š ίΑ#UΏdN Ύ:DLΣk4†ΖΣg‡ξ¦Rγrw’iΊxα Iζ̝ Λ’oγ:Θf“Ρ% ₯mo=+k[κπ€YΖΦΥΥΑ @΅YΡυπΘ7’Α „Rd³ ]ob·Αΐγρΐαp ³€ωZΣD­56€ΐΎC ¦@Ά‚Β²θέ°,Λ’£££Έkd΅ZAΣτΈά›R©ΔBM§ί‡=5Ί$0‰1A LsšΞœƒ-E6“ …v·Ρl6Gι4,ZΡιDψώ{.g\Π’„ΐλϋΑf9 D£ΰ8nΤ2¬vρΗ²,!ΦΥ’ϋ‘MPrz$”@Σθή²‚§`p80™Le;’(‚γ8 Δb`9ΑΧ€–¦ΖέωώΥχAt»°`Ρ"€ΣιbGΒl6;Oθ AΝd`‹  ρύŒ.‡@ L2δέ’@˜ξ( όŽ€’€b:v%w§rγNEΑησ!±`³f]ΡΈ`9‘Χφ…"α0xž/k+Λ2,KQPψpΎV\_}ΏΡ%ͺˆλ| \  Α`°hk±Xt›rΉ"α0P( ΄g/LΩ©–™Bb~|>(Šv{“(ŠHΔγ€$‘νΐaP7Β1 Βτ…ˆ α6ΐœJ‘εδ{(ά?ΠΪ>’(Šˍ‘ŸφφvΨ,\_χd»έΰŠΖ‡e0ΎC%‘p‚ θZΖΪl68N455…ΣTΘΩ L>K— ΎhΑ0 X›ΝVΡ6άίEQΰ?pΦΨ Ρ%Œ ΙfEίΊΥ°š-…BΓΔEQΓRυ:œιtI2€H$.‰@ άˆ˜ n<η/ΐΥՍl&‹\.WΡέIλN”¦cg2,\Ό²Ε‚ή)τζκκFλ[ο@,ΚZΖ–:Z9Ξ’ πx<ί·ι™νF—@0L{‘―έƒΊΊ:΄΄΄_#₯wθu-`Ÿ„λΚ5£K7ΧΧ‚l³bαβEΘf³Ε› ,Λ;/θ ‰\..›…£»υjt9αAāp» ͺπ>¦ "ycάi<ω₯..’(" !3#„ΤœYFW4nκ?ϊž>Ρ΅ŒΥΊ2₯–±>Ÿv»½›ίdt šΡ³el_ΑΦsα3x>ϊΨθΖMjΞ,€gΞ@0–'Qšr]IH $γqΠ’ˆΆCΐX4'Βm„‰γΠzτ€ιΨ•μb΅ Εel–eαυza·ΩΎ%–5Έ’ρΣzbθ.q:F"‘(λπ€Υλt:QSS ΖbAχΆΝ(ΈœW@Έ•HNΊΆmc6Γ ΆΆNηΠk`δΒ΅6ζT΄€νκ†·σ-ƒ+?ͺ‰Eψώ{a·Ωΰυza2™Šη o»‘L&!) Zž€‰γ ˆ@ άJˆ˜ n3j/^BΝΕKΘρ<8Ž—]lιΈΟσθ˜7‡cJQͺ Α£° "#Nλ:<Ρ4 §Σ Ηƒ@0ΕnGΧ£A6›.ƒp PΜf\{d+d§ώ`υυυp: iZΧ6“Ι ΓOΐΏ00…“3g ΰ°cώ‚ΰy~XΚυxl`9ŽCŽηQsρj/^2Ίp‹!b‚@Έ ρvžΛσHέΈC―'(΄ξDΉq'›Ε‚TΗ£KΉ)hQDhΟ>0hΩlV·Ca2™ΰt:ΡΨ؈P{;„ΊZτl^Πδ΄9Q) =ΧB¨χ  ›τ,`σωΦΦΦνvSψRŽ-^υθ₯UwφΞίj΅ϊuHά=Ο΄½©ΉΎžbXδΔ›Neg|ί€G7>{1½²šg><Ÿ•_"­V+υz=ΥjυΎ―ʝϋ#j΅ZvvvR-ŠΤ6·F|fαέχϊιά>•LLL|}œί|Υj΅ΜΟΟgzf:½ιρ»΄‹‡Nν]κ³ί Ψ»―άn΅2wνzή}oΔS?šC››©άΉΑϊξΆͺ‡moj΅ZιομδψΉσ™]^υ!#ζ› ΗΟ-fve5v;[[[\{χΏ–«++™½ωU*cώpͺJQδΤ™·2ΩάΘϊΪZΪνφ}7<έ]ΫΫΪΚ‘v{ΤcsuΊιoo?tμϊΪZ&›9uζ­TΖhμ}EfoήΚκΚΚ=ηχ~!±΅΅•N»Ω•Υ?·8κιο1$Εήε  Σ\_OΏίΰv§F£‘V+‡―\υδE­ίΟιΧ^O΅ΧΛκςrΊέξ·>DE‘•••tΊέ<υΩ£™πΤ§Ÿηv»εεεEρ­ΏN§“ΥεεT{½œΓ°ϋ9|ωjšY]]}ΰφ¦~ΏŸζϊz2ξ½_Œ{H…˜’$S͍,Όs6ƒα0FcίηOlnnζ?.d²έΞ‘OŒzμΗfςv;§q&ιοδ֍i4_oyκυzYZZΚβωσ9ΤνζG>υΈ€c>Κ‘Ν­\X\Μ=ΏF£‘―nέΚnΏŸΣ―ΙδνοΟ·SυΛW2Ωι棋³ΉΉΉοσ$֍ †Γ,Όs6S͍Q |GŒΧ‚xψ»όΫW—“?ȟρΥ+ΏJγ—?O­ZMύθΡ9r$ΣΣΣ™˜˜H§ΣΙ΅/ΏΜΞφv^ψΫίΗrζΓl=s,ΧjΆλGRΙή}EQdX™ΪhεΤ?εƒΤχΨvύHύιΩ>όtjwξ ΩM2΅ήΜΙ7ήΞLcmΤc>v[ΟΟgωsMMεΤιΣ™ŸŸΟ`0H―ΧK³ΩΜF³™aQδ™ΟηΩΞτ8+/ž}ϋΩƒώ!ΐγ!&`Œ<‰˜H’ζΛ/εΦo^I19™J’j΅Άw=uv3±ΥΛσ―Ώ™Ή›·zŒ‘Ω­T~αΗιœz>ƒΉΩT{Ϋ™Ώ~#‡―~šΚp8κρ8`E­–ΦO’ξΙ)¦§2ΡέΜό΅₯<υΩcΠƒtžΛ—ΑΜL*©€Z­¦(†ΩΝήΣγŸ;ϋ~κ—.?‰QΔŒ1cδIΕD’ §§Σ|ιgιž\Θ`n6nζ―ίHύ•TΏ'׊χ*&'Σ|ωΕtNžΨ;ο»›™»~3υO.§ΦΫ~Rcˆ #bΖΘ“Œ €0Fά€ ”"&€RΔPŠ˜J@)b(EL₯ˆ  1”"&€RΔPŠ˜J@)b(EL₯ˆ  1”"&€RΔPŠ˜J@)b(EL₯ˆ  1”"&€RΔPŠ˜J@)b(EL₯ˆ  1”"&€RΔPŠ˜J@)b(EL₯ˆ  1”"&€RΔPŠ˜J@)b(EL₯ˆ  1”"&`¬μ£žΰ`yŸƒq21κ€C%­€˜υe7•ή¨gώΛj€.Ί;„IENDB`‚sphinx-toolbox-3.9.0/static_original/000077500000000000000000000000001475757360300177145ustar00rootroot00000000000000sphinx-toolbox-3.9.0/static_original/regex.css000066400000000000000000000013311475757360300215360ustar00rootroot00000000000000span.regex_literal { color: dimgrey; } span.regex_at { /*color: rgb(204, 120, 50)*/ color: orangered; } span.regex_repeat_brace { /*color: rgb(204, 120, 50)*/ color: orangered; } span.regex_branch { /*color: rgb(204, 120, 50)*/ color: orangered; } span.regex_subpattern { /*color: indianred !* rgb(255, 150, 50) *!*/ color: dodgerblue /* rgb(255, 150, 50) */ } span.regex_in { /*color: rgb(255, 150, 50)*/ color: darkorange } span.regex_category { /*color: rgb(255, 150, 50)*/ color: darkseagreen; } span.regex_repeat { /*color: rgb(51, 204, 255)*/ color: orangered; } span.regex_any { /*color: rgb(204, 120, 50)*/ color: orangered; } code.regex { font-size: 80%; } span.regex { font-weight: bold; } sphinx-toolbox-3.9.0/static_original/tabs-no-collapse.js000066400000000000000000000023611475757360300234170ustar00rootroot00000000000000// Based on https://github.com/executablebooks/sphinx-tabs/blob/master/sphinx_tabs/static/tabs.js // Copyright (c) 2017 djungelorm // MIT Licensed function deselectTabset(target) { const parent = target.parentNode; const grandparent = parent.parentNode; if (parent.parentNode.parentNode.getAttribute("id").startsWith("installation")) { // Hide all tabs in current tablist, but not nested Array.from(parent.children).forEach(t => { if (t.getAttribute("name") !== target.getAttribute("name")) { t.setAttribute("aria-selected", "false"); } }); // Hide all associated panels Array.from(grandparent.children).slice(1).forEach(p => { // Skip tablist if (p.getAttribute("name") !== target.getAttribute("name")) { p.setAttribute("hidden", "false") } }); } else { // Hide all tabs in current tablist, but not nested Array.from(parent.children).forEach(t => { t.setAttribute("aria-selected", "false"); }); // Hide all associated panels Array.from(grandparent.children).slice(1).forEach(p => { // Skip tablist p.setAttribute("hidden", "true") }); } } // Compatibility with sphinx-tabs 2.1.0 and later function deselectTabList(tab) {deselectTabset(tab)} sphinx-toolbox-3.9.0/stubs.txt000066400000000000000000000002451475757360300164430ustar00rootroot00000000000000# Type stubs git+https://github.com/domdfcoding/docutils-stubs@docutils-16 git+https://github.com/domdfcoding/pytest-regressions-stubs types-tabulate types-requests sphinx-toolbox-3.9.0/tests/000077500000000000000000000000001475757360300157035ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/__init__.py000066400000000000000000000000001475757360300200020ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/augment_defaults_demo/000077500000000000000000000000001475757360300222365ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/augment_defaults_demo/__init__.py000066400000000000000000000004751475757360300243550ustar00rootroot00000000000000class Foo: """ I am ``Foo``. """ def __init__(self): """ This is ``__init__`` """ def excluded(self): # noqa: MAN002 """ I should be excluded. """ def function(self): # noqa: MAN002 """ I shouldn't be excluded. """ def __repr__(self): # noqa: MAN002 """ This is ``__repr__`` """ sphinx-toolbox-3.9.0/tests/common.py000066400000000000000000000022761475757360300175540ustar00rootroot00000000000000# stdlib from http import HTTPStatus from types import SimpleNamespace from typing import Any, Dict, NamedTuple, Sequence, Tuple # 3rd party import pytest import sphinx class AttrDict(dict): def __getattr__(self, item): # noqa: MAN001,MAN002 try: return self[item] except KeyError as e: raise AttributeError(str(e)) def __setattr__(self, item, value): # noqa: MAN001,MAN002 self[item] = value error_codes_list = [x for x in HTTPStatus if x not in {100, 200}] # pylint: disable=not-an-iterable error_codes = pytest.mark.parametrize("error_code", error_codes_list) class AppParams(NamedTuple): args: Sequence[Any] kwargs: Dict[str, Any] def get_app_config_values(config: Any) -> Tuple[str, str, Any]: if sphinx.version_info >= (7, 3): valid_types = config.valid_types default = config.default rebuild = config.rebuild else: default, rebuild, valid_types = config if isinstance(valid_types, (set, frozenset, tuple, list)): valid_types = sorted(valid_types) if hasattr(valid_types, "_candidates"): new_valid_types = SimpleNamespace() new_valid_types.candidates = sorted(valid_types._candidates) valid_types = new_valid_types return (default, rebuild, valid_types) sphinx-toolbox-3.9.0/tests/conftest.py000066400000000000000000000114631475757360300201070ustar00rootroot00000000000000# Based on https://github.com/agronholm/sphinx-autodoc-typehints # Copyright (c) Alex GrΓΆnholm # MIT Licensed # # 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 os import pathlib import shutil import sys import types from typing import Iterator, Optional, Tuple # 3rd party import docutils.nodes import pytest import sphinx.writers.html5 from domdf_python_tools.paths import PathPlus from pytest_httpserver import HTTPServer from pytest_httpserver.pytest_plugin import Plugin, PluginHTTPServer, get_httpserver_listen_address from sphobjinv import Inventory # type: ignore[import-untyped] # this package from tests.common import error_codes_list if sys.version_info >= (3, 10): types.Union = types.UnionType pytest_plugins = ( "pytest_regressions", "sphinx.testing.fixtures", "coincidence", "sphinx_toolbox.testing", ) collect_ignore = ["roots"] @pytest.fixture(scope="session") def httpserver(httpserver_listen_address: Tuple[str, int]) -> Iterator[Optional[HTTPServer]]: if Plugin.SERVER: Plugin.SERVER.clear() yield Plugin.SERVER return host, port = httpserver_listen_address if not host: host = HTTPServer.DEFAULT_LISTEN_HOST if not port: port = HTTPServer.DEFAULT_LISTEN_PORT server = PluginHTTPServer(host=host, port=port) server.start() yield server @pytest.fixture(scope="session") def httpserver_listen_address() -> Tuple[str, int]: return get_httpserver_listen_address() @pytest.fixture(scope="session") def error_server(httpserver: HTTPServer) -> HTTPServer: for status_code in error_codes_list: httpserver.expect_request(f"/{status_code:d}").respond_with_json('', status=status_code) return httpserver @pytest.fixture(scope="session") def inv(pytestconfig) -> Inventory: cache_path = "python{v.major}.{v.minor}/objects.inv".format(v=sys.version_info) inv_dict = pytestconfig.cache.get(cache_path, None) if inv_dict is not None: return Inventory(inv_dict) print("Downloading objects.inv") url = "https://docs.python.org/{v.major}.{v.minor}/objects.inv".format(v=sys.version_info) inv = Inventory(url=url) pytestconfig.cache.set(cache_path, inv.json_dict()) return inv @pytest.fixture(autouse=True) def _remove_sphinx_projects(sphinx_test_tempdir: pathlib.Path) -> None: # Remove any directory which appears to be a Sphinx project from # the temporary directory area. # See https://github.com/sphinx-doc/sphinx/issues/4040 roots_path = pathlib.Path(sphinx_test_tempdir) for entry in roots_path.iterdir(): try: if entry.is_dir() and pathlib.Path(entry, "_build").exists(): shutil.rmtree(str(entry)) except PermissionError: pass @pytest.fixture() def rootdir() -> PathPlus: return PathPlus(os.path.dirname(__file__) or '.').abspath() / "roots" @pytest.fixture() def docutils_17_compat(monkeypatch) -> None: def visit_section(self, node: docutils.nodes.section) -> None: self.section_level += 1 self.body.append(self.starttag(node, "div", CLASS="section")) # self.body.append(self.starttag(node, 'section')) def depart_section(self, node: docutils.nodes.section) -> None: self.section_level -= 1 self.body.append('\n') def visit_figure(self, node: docutils.nodes.figure) -> None: atts = {"class": "figure"} if node.get("width"): atts["style"] = f"width: {node['width']}" atts["class"] += f" align-{node.get('align', 'default')}" self.body.append(self.starttag(node, "div", **atts)) def depart_figure(self, node: docutils.nodes.figure) -> None: self.body.append('\n') monkeypatch.setattr(sphinx.writers.html5.HTML5Translator, "visit_section", visit_section) monkeypatch.setattr(sphinx.writers.html5.HTML5Translator, "depart_section", depart_section) monkeypatch.setattr(sphinx.writers.html5.HTML5Translator, "visit_figure", visit_figure) monkeypatch.setattr(sphinx.writers.html5.HTML5Translator, "depart_figure", depart_figure) sphinx-toolbox-3.9.0/tests/demo.py000066400000000000000000000004401475757360300171770ustar00rootroot00000000000000def ellipsis_function(foo: str = ...) -> int: ... # type: ignore[empty-body] def ellipsis_function_2(foo: str = ..., *args: int, **kwargs: str) -> int: # type: ignore[empty-body] r""" A function with ellipses in the docstring. :param foo: :param \*args: :param \*\*kwargs: """ sphinx-toolbox-3.9.0/tests/genericalias_demo.py000066400000000000000000000004341475757360300217100ustar00rootroot00000000000000# stdlib from typing import Callable, List, Tuple, Union #: Some type alias VarType = Union[List[str], Tuple[str, int, float], int, bytes, Callable[[str], int]] #: Some variable VARIABLE: VarType = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] CONSTANT: int = 42 """ Don't change this!" """ sphinx-toolbox-3.9.0/tests/regex_demo.py000066400000000000000000000012061475757360300203720ustar00rootroot00000000000000# stdlib import re no_flags = re.compile(r"Hello\s+[Ww]orld[.,](Lovely|Horrible) weather, isn't it (.*)\?") one_flag = re.compile(r"Hello\s+[Ww]orld[.,](Lovely|Horrible) weather, isn't it (.*)\?", flags=re.IGNORECASE) two_flags = re.compile( r"Hello \s+ [Ww]orld [.,] (Lovely|Horrible)\ weather,\ isn't\ it (.*) \?", flags=re.ASCII | re.VERBOSE, ) backticks = re.compile(":py:class:`([A-Za-z_][A-Za-z0-9._]+)`") leading_whitespace = re.compile(" :py:class:`([A-Za-z_][A-Za-z0-9._]+)`") trailing_whitespace = re.compile(":py:class:`([A-Za-z_][A-Za-z0-9._]+)` ") single_whitespace = re.compile(" :py:class:`([A-Za-z_][A-Za-z0-9._]+)` ") sphinx-toolbox-3.9.0/tests/requirements.txt000066400000000000000000000004601475757360300211670ustar00rootroot00000000000000coincidence>=0.2.0 coverage>=5.1 coverage-pyver-pragma>=0.2.1 defusedxml>=0.7.1 flake8-dunder-all>=0.0.4 hypothesis>=5.35.4 importlib-metadata>=3.6.0 pprint36>=3.9.0.2 pygments>=2.11.0 pytest>=6.0.0 pytest-cov>=2.8.1 pytest-httpserver>=0.3.5 pytest-randomly>=3.7.0 pytest-timeout>=1.4.2 sphobjinv>=2.0.1 sphinx-toolbox-3.9.0/tests/test-root-aw/000077500000000000000000000000001475757360300202505ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test-root-aw/conf.py000066400000000000000000000017231475757360300215520ustar00rootroot00000000000000# 3rd party from sphinx.application import Sphinx # this package from sphinx_toolbox import latex extensions = [ "sphinx.ext.viewcode", "sphinx_toolbox.more_autosummary", "sphinx_toolbox.more_autosummary.column_widths", ] github_username = "sphinx-toolbox" github_repository = "sphinx-toolbox" source_link_target = "GitHub" _exclude_members = "__repr__,__weakref__,__dict__,__annotations__,__firstlineno__,__replace__,__static_attributes__" autodoc_default_options = {"exclude-members": _exclude_members} all_typevars = True no_unbound_typevars = False sphinx_tabs_disable_tab_closing = True html_codeblock_linenos_style = "table" overloads_location = "bottom" documentation_summary = " This is an awesome tool! πŸš€ ~ intersphinx_mapping # 100% 'Quotes'" intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)} project = "Python" author = "unknown" def setup(app: Sphinx) -> None: app.connect("build-finished", latex.replace_unknown_unicode) sphinx-toolbox-3.9.0/tests/test-root-aw/index.rst000066400000000000000000000003501475757360300221070ustar00rootroot00000000000000===================== Autosummary Widths ===================== .. autosummary-widths:: 3/10 .. automodule:: string :autosummary: :members: .. autosummary-widths:: 4/10 60/100 .. automodule:: textwrap :autosummary: :members: sphinx-toolbox-3.9.0/tests/test_assets.py000066400000000000000000000013121475757360300206130ustar00rootroot00000000000000# this package from sphinx_toolbox import __version__, assets from sphinx_toolbox.testing import run_setup from tests.common import get_app_config_values def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(assets.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == {} assert roles == {"asset": assets.asset_role} assert additional_nodes == {assets.AssetNode} assert app.registry.translation_handlers == { "html": {"AssetNode": (assets.visit_asset_node, assets.depart_asset_node)} } assert get_app_config_values(app.config.values["assets_dir"]) == ("./assets", "env", [str]) assert app.registry.source_parsers == {} sphinx-toolbox-3.9.0/tests/test_autosummary_widths.py000066400000000000000000000020221475757360300232600ustar00rootroot00000000000000# stdlib import shutil from typing import cast # 3rd party import pytest from domdf_python_tools.paths import PathPlus from domdf_python_tools.stringlist import StringList from sphinx.application import Sphinx from sphinx.builders import Builder # this package from sphinx_toolbox.testing import LaTeXRegressionFixture @pytest.fixture() def doc_root(tmp_pathplus: PathPlus) -> None: doc_root = tmp_pathplus.parent / "test-autosummary-widths" doc_root.maybe_make() test_root = PathPlus(__file__).parent / "test-root-aw" shutil.copy2(test_root / "conf.py", doc_root / "conf.py") shutil.copy2(test_root / "index.rst", doc_root / "index.rst") @pytest.mark.usefixtures("doc_root") @pytest.mark.sphinx("latex", testroot="test-autosummary-widths") def test_latex_output( app: Sphinx, latex_regression: LaTeXRegressionFixture, ): assert cast(Builder, app.builder).name.lower() == "latex" app.build() output_file = PathPlus(app.outdir) / "python.tex" latex_regression.check(StringList(output_file.read_lines()), jinja2=True) sphinx-toolbox-3.9.0/tests/test_autosummary_widths_/000077500000000000000000000000001475757360300230515ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_autosummary_widths_/test_latex_output.tex000066400000000000000000001024221475757360300273700ustar00rootroot00000000000000<%- set open_smart_quote = "\\textquotesingle{}" if (3, 5) > sphinx_version >= (4, 2) else "β€˜" -%> <%- set close_smart_quote = "\\textquotesingle{}" if (3, 5) > sphinx_version >= (4, 2) else "’" -%> <%- set open_smart_quote_b = "\\textquotesingle{}" if sphinx_version >= (4, 2) else "β€˜" -%> <%- set close_smart_quote_b = "\\textquotesingle{}" if sphinx_version >= (4, 2) else "’" -%> <%- set hline = "\\sphinxhline" if sphinx_version >= (5, 3) else "\\hline" -%> <%- set sphinxparam = "sphinxparam" if sphinx_version >= (6, 2) else "emph" -%> <%- if sphinx_version >= (7, 1) -%> <%- set sphinxparamcomma = "\\sphinxparamcomma \\sphinxparam" -%> <%- elif sphinx_version >= (6, 2) -%> <%- set sphinxparamcomma = ", \\sphinxparam" -%> <%- else -%> <%- set sphinxparamcomma = ", \\emph" -%> <%- endif -%> <%- macro sphinx6x_2nd(char) -%> <% if (7, 2) > sphinx_version >= (6, 2) %>,<><% endif %> <%- endmacro -%> <%- set sphinx81_linebreak = "\n" if sphinx_version >= (8, 1) else "" -%> %% Generated by Sphinx. \def\sphinxdocclass{report} \documentclass[letterpaper,10pt,english]{sphinxmanual} \ifdefined\pdfpxdimen \let\sphinxpxdimen\pdfpxdimen\else\newdimen\sphinxpxdimen \fi \sphinxpxdimen=.75bp\relax <% if sphinx_version >= (4, 0) %>\ifdefined\pdfimageresolution \pdfimageresolution= \numexpr \dimexpr1in\relax/\sphinxpxdimen\relax \fi %% let collapsable pdf bookmarks panel have high depth per default \PassOptionsToPackage{bookmarksdepth=5}{hyperref}<% if sphinx_version >= (5, 3) %> <% endif %><% if sphinx_version >= (6, 0) %> \PassOptionsToPackage{booktabs}{sphinx} \PassOptionsToPackage{colorrows}{sphinx}<% endif %> <% endif %> \PassOptionsToPackage{warn}{textcomp} \usepackage[utf8]{inputenc} \ifdefined\DeclareUnicodeCharacter % support both utf8 and utf8x syntaxes \ifdefined\DeclareUnicodeCharacterAsOptional \def\sphinxDUC#1{\DeclareUnicodeCharacter{"#1}} \else \let\sphinxDUC\DeclareUnicodeCharacter \fi \sphinxDUC{00A0}{\nobreakspace} \sphinxDUC{2500}{\sphinxunichar{2500}} \sphinxDUC{2502}{\sphinxunichar{2502}} \sphinxDUC{2514}{\sphinxunichar{2514}} \sphinxDUC{251C}{\sphinxunichar{251C}} \sphinxDUC{2572}{\textbackslash} \fi \usepackage{cmap} \usepackage[T1]{fontenc} \usepackage{amsmath,amssymb,amstext} \usepackage{babel} <%+ if sphinx_version >= (4, 0) %> \usepackage{tgtermes} \usepackage{tgheros} \renewcommand{\ttdefault}{txtt} <% else %> \usepackage{times} \expandafter\ifx\csname T@LGR\endcsname\relax \else % LGR was declared as font encoding \substitutefont{LGR}{\rmdefault}{cmr} \substitutefont{LGR}{\sfdefault}{cmss} \substitutefont{LGR}{\ttdefault}{cmtt} \fi \expandafter\ifx\csname T@X2\endcsname\relax \expandafter\ifx\csname T@T2A\endcsname\relax \else % T2A was declared as font encoding \substitutefont{T2A}{\rmdefault}{cmr} \substitutefont{T2A}{\sfdefault}{cmss} \substitutefont{T2A}{\ttdefault}{cmtt} \fi \else % X2 was declared as font encoding \substitutefont{X2}{\rmdefault}{cmr} \substitutefont{X2}{\sfdefault}{cmss} \substitutefont{X2}{\ttdefault}{cmtt} \fi <% endif %> \usepackage[Bjarne]{fncychap} \usepackage{sphinx} <% if sphinx_version >= (4, 0) %> \fvset{fontsize=auto} <% else %> \fvset{fontsize=\small} <% endif -%> \usepackage{geometry} % Include hyperref last. \usepackage{hyperref} % Fix anchor placement for figures with captions. \usepackage{hypcap}% it must be loaded after hyperref. % Set up styles of URL: it should be placed after hyperref. \urlstyle{same} \usepackage{sphinxmessages} \newcommand\thesymbolfootnote{\fnsymbol{footnote}}\let\thenumberfootnote\thefootnote \makeatletter \newcolumntype{\Xx}[2]{>{\raggedright\arraybackslash}p{\dimexpr (\linewidth-\arrayrulewidth)*#1/#2-\tw@\tabcolsep-\arrayrulewidth\relax}} \makeatother \title{Python} \date{Mar 11, 2021} \release{} \author{unknown} \newcommand{\sphinxlogo}{\vbox{}} \renewcommand{\releasename}{} \makeindex \begin{document} <% if sphinx_version >= (5, 0) %>\ifdefined\shorthandoff \ifnum\catcode`\=\string=\active\shorthandoff{=}\fi \ifnum\catcode`\"=\active\shorthandoff{"}\fi \fi <% endif %>\pagestyle{empty} \sphinxmaketitle \pagestyle{plain} \sphinxtableofcontents \pagestyle{normal} \phantomsection\label{\detokenize{index::doc}} \index{module@\spxentry{module}!string@\spxentry{string}}\index{string@\spxentry{string}!module@\spxentry{module}} A collection of string constants. Public module variables: whitespace \textendash{} a string containing all ASCII whitespace ascii\_lowercase \textendash{} a string containing all ASCII lowercase letters ascii\_uppercase \textendash{} a string containing all ASCII uppercase letters ascii\_letters \textendash{} a string containing all ASCII letters digits \textendash{} a string containing all ASCII decimal digits hexdigits \textendash{} a string containing all ASCII hexadecimal digits octdigits \textendash{} a string containing all ASCII octal digits punctuation \textendash{} a string containing all ASCII punctuation characters printable \textendash{} a string containing all ASCII characters considered printable \vspace{10px} \sphinxstylestrong{Classes:} \vspace{-5px} \begin{savenotes}<% if sphinx_version >= (5, 3) %> \sphinxatlongtablestart \sphinxthistablewithglobalstyle \sphinxthistablewithnovlinesstyle <% endif %><% if sphinx_version >= (6, 2) %>\makeatletter \LTleft \@totalleftmargin plus1fill \LTright\dimexpr\columnwidth-\@totalleftmargin-\linewidth\relax plus1fill \makeatother \begin{longtable}{\Xx{3}{10}\Xx{7}{10}}<% elif sphinx_version >= (5, 3) %>\begin{longtable}[c]{\Xx{3}{10}\Xx{7}{10}}<% else %>\sphinxatlongtablestart\begin{longtable}[c]{\Xx{3}{10}\Xx{7}{10}}<% endif %><% if sphinx_version >= (5, 3) %> \sphinxtoprule <% else %> \hline <% endif %>\endfirsthead \multicolumn{2}{c}<% if sphinx_version >= (5, 3) %>{\sphinxnorowcolor \makebox[0pt]<% else %>% {\makebox[0pt]<% endif %>{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}<% if sphinx_version >= (5, 3) %>% <% endif %>}\\ <% if sphinx_version >= (5, 3) %>\sphinxtoprule <% else %>\hline <% endif %>\endhead <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif%> \multicolumn{2}{r}{<% if sphinx_version >= (5, 3) %>\sphinxnorowcolor <% endif %>\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}<% if sphinx_version >= (5, 3) %>% <% endif %>}\\ \endfoot \endlastfoot<% if sphinx_version >= (5, 3) %> \sphinxtableatstartofbodyhook<% endif%> {\hyperref[\detokenize{index:string.Template}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{Template}}}}}(template) & A string class for supporting \$\sphinxhyphen{}substitutions. \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{longtable}<% if sphinx_version >= (5, 3) %> \sphinxtableafterendhook <% endif %>\sphinxatlongtableend<% if sphinx_version >= (5, 3) %> <% endif %>\end{savenotes} \sphinxstylestrong{Functions:} \vspace{-5px} \begin{savenotes}<% if sphinx_version >= (5, 3) %> <% endif %>\sphinxatlongtablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle \sphinxthistablewithnovlinesstyle <% endif %><% if sphinx_version >= (6, 2) %>\makeatletter \LTleft \@totalleftmargin plus1fill \LTright\dimexpr\columnwidth-\@totalleftmargin-\linewidth\relax plus1fill \makeatother \begin{longtable}{\Xx{3}{10}\Xx{7}{10}}<% else %>\begin{longtable}[c]{\Xx{3}{10}\Xx{7}{10}}<% endif %><% if sphinx_version >= (5, 3) %> \sphinxtoprule <% else %> \hline <% endif %>\endfirsthead \multicolumn{2}{c}<% if sphinx_version >= (5, 3) %>{\sphinxnorowcolor \makebox[0pt]<% else %>% {\makebox[0pt]<% endif %>{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}<% if sphinx_version >= (5, 3) %>% <% endif %>}\\ <% if sphinx_version >= (5, 3) %>\sphinxtoprule <% else %>\hline <% endif %>\endhead <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif%> \multicolumn{2}{r}{<% if sphinx_version >= (5, 3) %>\sphinxnorowcolor <% endif %>\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}<% if sphinx_version >= (5, 3) %>% <% endif %>}\\ \endfoot \endlastfoot<% if sphinx_version >= (5, 3) %> \sphinxtableatstartofbodyhook<% endif%> {\hyperref[\detokenize{index:string.capwords}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{capwords}}}}}(s {[},sep{]}) & Split the argument into words using split, capitalize each word using capitalize, and join the capitalized words using join. \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{longtable}<% if sphinx_version >= (5, 3) %> \sphinxtableafterendhook <% endif %>\sphinxatlongtableend<% if sphinx_version >= (5, 3) %> <% endif %>\end{savenotes} \index{Template (class in string)@\spxentry{Template}\spxextra{class in string}} \begin{fulllineitems} \phantomsection\label{\detokenize{index:string.Template}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<% if sphinx_version >= (8, 1) %> <% endif %>{\sphinxbfcode{\sphinxupquote{<% if sphinx_version >= (8, 2) %>\DUrole{k}{class}<% else %>class<% endif %><% if sphinx_version >= (4, 3) %>\DUrole{w<>}{<% if sphinx_version < (7, 2) %> <% endif %> }<% else %> <% endif %>}}\sphinxcode{\sphinxupquote{string.}}\sphinxbfcode{\sphinxupquote{Template}}}<< sphinx81_linebreak >>{\<< sphinxparam >>{\DUrole{n<>}{template}}}<< sphinx81_linebreak >>{} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> A string class for supporting \$\sphinxhyphen{}substitutions. \end{fulllineitems} \index{capwords() (in module string)@\spxentry{capwords()}\spxextra{in module string}} \begin{fulllineitems} \phantomsection\label{\detokenize{index:string.capwords}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{string.}}\sphinxbfcode{\sphinxupquote{capwords}}}<< sphinx81_linebreak >>{\<>{<% if sphinx_version >= (4, 3)%>\DUrole{n<< sphinx6x_2nd('n') >>}{s}}<% else %>s}<% endif %>\sphinxoptional{<>{<% if sphinx_version >= (4, 3)%>\DUrole{n<>}{sep}}<% else %>sep}<% endif %>}}<< sphinx81_linebreak >>{{ <%- if sphinx_version >= (4, 4) %> $\rightarrow$ string}} <%- else %> $\rightarrow$ {\hyperref[\detokenize{index:module-string}]{\sphinxcrossref{string}}}}} <%- endif -%> <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> Split the argument into words using split, capitalize each word using capitalize, and join the capitalized words using join. If the optional second argument sep is absent or None, runs of whitespace characters are replaced by a single space and leading and trailing whitespace are removed, otherwise sep is used to split and join the words. \end{fulllineitems} <% if sphinx_version < (7, 2) %>\phantomsection\label{\detokenize{index:module-textwrap}}<% endif -%> \index{module@\spxentry{module}!textwrap@\spxentry{textwrap}}\index{textwrap@\spxentry{textwrap}!module@\spxentry{module}} <%- if sphinx_version >= (7, 2) %>\phantomsection\label{\detokenize{index:module-textwrap}}<% endif %> Text wrapping and filling. \vspace{10px} \sphinxstylestrong{Classes:} \vspace{-5px} \begin{savenotes}<% if sphinx_version >= (5, 3) %> <% endif %>\sphinxatlongtablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle \sphinxthistablewithnovlinesstyle <% endif %><% if sphinx_version >= (6, 2) %>\makeatletter \LTleft \@totalleftmargin plus1fill \LTright\dimexpr\columnwidth-\@totalleftmargin-\linewidth\relax plus1fill \makeatother \begin{longtable}{\Xx{4}{10}\Xx{60}{100}}<% else %>\begin{longtable}[c]{\Xx{4}{10}\Xx{60}{100}}<% endif %><% if sphinx_version >= (5, 3) %> \sphinxtoprule <% else %> \hline <% endif %>\endfirsthead \multicolumn{2}{c}<% if sphinx_version >= (5, 3) %>{\sphinxnorowcolor \makebox[0pt]<% else %>% {\makebox[0pt]<% endif %>{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}<% if sphinx_version >= (5, 3) %>% <% endif %>}\\ <% if sphinx_version >= (5, 3) %>\sphinxtoprule <% else %>\hline <% endif %>\endhead <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif%> \multicolumn{2}{r}{<% if sphinx_version >= (5, 3) %>\sphinxnorowcolor <% endif %>\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}<% if sphinx_version >= (5, 3) %>% <% endif %>}\\ \endfoot \endlastfoot<% if sphinx_version >= (5, 3) %> \sphinxtableatstartofbodyhook<% endif%> {\hyperref[\detokenize{index:textwrap.TextWrapper}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{TextWrapper}}}}}({[}width, initial\_indent, <% if sphinx_version >= (4, 2) %>...<% else %>…<% endif %>{]}) & Object for wrapping/filling text. \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{longtable}<% if sphinx_version >= (5, 3) %> \sphinxtableafterendhook <% endif %>\sphinxatlongtableend<% if sphinx_version >= (5, 3) %> <% endif %>\end{savenotes} \sphinxstylestrong{Functions:} \vspace{-5px} \begin{savenotes}<% if sphinx_version >= (5, 3) %> <% endif %>\sphinxatlongtablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle \sphinxthistablewithnovlinesstyle <% endif %><% if sphinx_version >= (6, 2) %>\makeatletter \LTleft \@totalleftmargin plus1fill \LTright\dimexpr\columnwidth-\@totalleftmargin-\linewidth\relax plus1fill \makeatother \begin{longtable}{\Xx{4}{10}\Xx{60}{100}}<% else %>\begin{longtable}[c]{\Xx{4}{10}\Xx{60}{100}}<% endif %><% if sphinx_version >= (5, 3) %> \sphinxtoprule <% else %> \hline <% endif %>\endfirsthead \multicolumn{2}{c}<% if sphinx_version >= (5, 3) %>{\sphinxnorowcolor \makebox[0pt]<% else %>% {\makebox[0pt]<% endif %>{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}<% if sphinx_version >= (5, 3) %>% <% endif %>}\\ <% if sphinx_version >= (5, 3) %>\sphinxtoprule <% else %>\hline <% endif %>\endhead <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif%> \multicolumn{2}{r}{<% if sphinx_version >= (5, 3) %>\sphinxnorowcolor <% endif %>\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}<% if sphinx_version >= (5, 3) %>% <% endif %>}\\ \endfoot \endlastfoot<% if sphinx_version >= (5, 3) %> \sphinxtableatstartofbodyhook<% endif%> {\hyperref[\detokenize{index:textwrap.dedent}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{dedent}}}}}(text) & Remove any common leading whitespace from every line in \sphinxtitleref{text}. \\ << hline >> {\hyperref[\detokenize{index:textwrap.fill}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{fill}}}}}(text{[}, width{]}) & Fill a single paragraph of text, returning a new string. \\ << hline >> {\hyperref[\detokenize{index:textwrap.indent}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{indent}}}}}(text, prefix{[}, predicate{]}) & Adds <>prefix<> to the beginning of selected lines in <>text<>. \\ << hline >> {\hyperref[\detokenize{index:textwrap.shorten}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{shorten}}}}}(text, width, **kwargs) & Collapse and truncate the given text to fit in the given width. \\ << hline >> {\hyperref[\detokenize{index:textwrap.wrap}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{wrap}}}}}(text{[}, width{]}) & Wrap a single paragraph of text, returning a list of wrapped lines. \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{longtable}<% if sphinx_version >= (5, 3) %> \sphinxtableafterendhook <% endif %>\sphinxatlongtableend<% if sphinx_version >= (5, 3) %> <% endif %>\end{savenotes} \index{TextWrapper (class in textwrap)@\spxentry{TextWrapper}\spxextra{class in textwrap}} \begin{fulllineitems} \phantomsection\label{\detokenize{index:textwrap.TextWrapper}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<% if sphinx_version >= (8, 1) %> <% endif %>{\sphinxbfcode{\sphinxupquote{<% if sphinx_version >= (8, 2) %>\DUrole{k}{class}<% else %>class<% endif %><% if sphinx_version >= (4, 3) %>\DUrole{w<< sphinx6x_2nd('w') >>}{<% if sphinx_version < (7, 2) %> <% endif %> }<% else %> <% endif -%>}}\sphinxcode{\sphinxupquote{textwrap.}}\sphinxbfcode{\sphinxupquote{TextWrapper}}} <<- sphinx81_linebreak >>{\<< sphinxparam >>{\DUrole{n<< sphinx6x_2nd('n') >>}{width}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{70}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{initial\_indent}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{\textquotesingle{}\textquotesingle{}}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{subsequent\_indent}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{\textquotesingle{}\textquotesingle{}}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{expand\_tabs}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{True}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{replace\_whitespace}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{True}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{fix\_sentence\_endings}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{False}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{break\_long\_words}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{True}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{drop\_whitespace}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{True}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{break\_on\_hyphens}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{True}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{tabsize}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{8}}<>{ <%- if sphinx_version >= (8, 2) %>\DUrole{keyword-only-separator}<% endif %> <%- if sphinx_version >= (8, 2) %>{<% endif %>\DUrole{o<< sphinx6x_2nd('o') >>}{<% if sphinx_version >= (8, 2) %>\sphinxstyleabbreviation{*} (Keyword\sphinxhyphen{}only parameters separator (PEP 3102))}<% else %>*<% endif %>}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{max\_lines}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{None}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{placeholder}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{\textquotesingle{} {[}...{]}\textquotesingle{}}}}<< sphinx81_linebreak >>{} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> Object for wrapping/filling text. The public interface consists of the wrap() and fill() methods; the other methods are just there for subclasses to override in order to tweak the default behaviour. If you want to completely replace the main wrapping algorithm, you’ll probably have to override \_wrap\_chunks(). \begin{description} <% if sphinx_version >= (5, 0) %>\sphinxlineitem<% else %>\item[<% endif %>{Several instance attributes control various aspects of wrapping:}<% if sphinx_version < (5, 0) %>] \leavevmode<% endif %>\begin{description} <% if sphinx_version >= (5, 0) %>\sphinxlineitem<% else %>\item[<% endif %>{width (default: 70)}<% if sphinx_version < (5, 0) %>] \leavevmode<% endif %> the maximum width of wrapped lines (unless break\_long\_words is false) <% if sphinx_version >= (5, 0) %>\sphinxlineitem<% else %>\item[<% endif %>{initial\_indent (default: β€œβ€)}<% if sphinx_version < (5, 0) %>] \leavevmode<% endif %> string that will be prepended to the first line of wrapped output. Counts towards the line’s width. <% if sphinx_version >= (5, 0) %>\sphinxlineitem<% else %>\item[<% endif %>{subsequent\_indent (default: β€œβ€)}<% if sphinx_version < (5, 0) %>] \leavevmode<% endif %> string that will be prepended to all lines save the first of wrapped output; also counts towards each line’s width. <% if sphinx_version >= (5, 0) %>\sphinxlineitem<% else %>\item[<% endif %>{expand\_tabs (default: true)}<% if sphinx_version < (5, 0) %>] \leavevmode<% endif %> Expand tabs in input text to spaces before further processing. Each tab will become 0 .. β€˜tabsize’ spaces, depending on its position in its line. If false, each tab is treated as a single character. <% if sphinx_version >= (5, 0) %>\sphinxlineitem<% else %>\item[<% endif %>{tabsize (default: 8)}<% if sphinx_version < (5, 0) %>] \leavevmode<% endif %> Expand tabs in input text to 0 .. β€˜tabsize’ spaces, unless β€˜expand\_tabs’ is false. <% if sphinx_version >= (5, 0) %>\sphinxlineitem<% else %>\item[<% endif %>{replace\_whitespace (default: true)}<% if sphinx_version < (5, 0) %>] \leavevmode<% endif %> Replace all whitespace characters in the input text by spaces after tab expansion. Note that if expand\_tabs is false and replace\_whitespace is true, every tab will be converted to a single space! <% if sphinx_version >= (5, 0) %>\sphinxlineitem<% else %>\item[<% endif %>{fix\_sentence\_endings (default: false)}<% if sphinx_version < (5, 0) %>] \leavevmode<% endif %> Ensure that sentence\sphinxhyphen{}ending punctuation is always followed by two spaces. Off by default because the algorithm is (unavoidably) imperfect. <% if sphinx_version >= (5, 0) %>\sphinxlineitem<% else %>\item[<% endif %>{break\_long\_words (default: true)}<% if sphinx_version < (5, 0) %>] \leavevmode<% endif %> Break words longer than β€˜width’. If false, those words will not be broken, and some lines might be longer than β€˜width’. <% if sphinx_version >= (5, 0) %>\sphinxlineitem<% else %>\item[<% endif %>{break\_on\_hyphens (default: true)}<% if sphinx_version < (5, 0) %>] \leavevmode<% endif %> Allow breaking hyphenated words. If true, wrapping will occur preferably on whitespaces and right after hyphens part of compound words. <% if sphinx_version >= (5, 0) %>\sphinxlineitem<% else %>\item[<% endif %>{drop\_whitespace (default: true)}<% if sphinx_version < (5, 0) %>] \leavevmode<% endif %> Drop leading and trailing whitespace from lines. <% if sphinx_version >= (5, 0) %>\sphinxlineitem<% else %>\item[<% endif %>{max\_lines (default: None)}<% if sphinx_version < (5, 0) %>] \leavevmode<% endif %> Truncate wrapped lines. <% if sphinx_version >= (5, 0) %>\sphinxlineitem<% else %>\item[<% endif %>{placeholder (default: β€˜ {[}…{]}’)}<% if sphinx_version < (5, 0) %>] \leavevmode<% endif %> Append to the last line of truncated text. \end{description} \end{description} \vspace{10px} \sphinxstylestrong{Methods:} \vspace{-5px} \begin{savenotes}<% if sphinx_version >= (5, 3) %> <% endif %>\sphinxatlongtablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle \sphinxthistablewithnovlinesstyle <% endif %><% if sphinx_version >= (6, 2) %>\makeatletter \LTleft \@totalleftmargin plus1fill \LTright\dimexpr\columnwidth-\@totalleftmargin-\linewidth\relax plus1fill \makeatother \begin{longtable}{\Xx{4}{10}\Xx{60}{100}}<% else %>\begin{longtable}[c]{\Xx{4}{10}\Xx{60}{100}}<% endif %><% if sphinx_version >= (5, 3) %> \sphinxtoprule <% else %> \hline <% endif %>\endfirsthead \multicolumn{2}{c}<% if sphinx_version >= (5, 3) %>{\sphinxnorowcolor \makebox[0pt]<% else %>% {\makebox[0pt]<% endif %>{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}<% if sphinx_version >= (5, 3) %>% <% endif %>}\\ <% if sphinx_version >= (5, 3) %>\sphinxtoprule <% else %>\hline <% endif %>\endhead <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif%> \multicolumn{2}{r}{<% if sphinx_version >= (5, 3) %>\sphinxnorowcolor <% endif %>\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}<% if sphinx_version >= (5, 3) %>% <% endif %>}\\ \endfoot \endlastfoot<% if sphinx_version >= (5, 3) %> \sphinxtableatstartofbodyhook<% endif%> {\hyperref[\detokenize{index:textwrap.TextWrapper.fill}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{fill}}}}}(text) & Reformat the single paragraph in <>text<> to fit in lines of no more than <>self.width<> columns, and return a new string containing the entire wrapped paragraph. \\ << hline >> {\hyperref[\detokenize{index:textwrap.TextWrapper.wrap}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{wrap}}}}}(text) & Reformat the single paragraph in <>text<> so it fits in lines of no more than <>self.width<> columns, and return a list of wrapped lines. \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{longtable}<% if sphinx_version >= (5, 3) %> \sphinxtableafterendhook <% endif %>\sphinxatlongtableend<% if sphinx_version >= (5, 3) %> <% endif %>\end{savenotes} \index{fill() (textwrap.TextWrapper method)@\spxentry{fill()}\spxextra{textwrap.TextWrapper method}} \begin{fulllineitems} \phantomsection\label{\detokenize{index:textwrap.TextWrapper.fill}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{fill}}}<< sphinx81_linebreak >>{\<< sphinxparam >>{\DUrole{n<< sphinx6x_2nd('n') >>}{text}\DUrole{p<< sphinx6x_2nd('p') >>}{:}<% if sphinx_version >= (4, 3) %>\DUrole{w<>}{<% if sphinx_version < (7, 2) %> <% endif %> }<% else %> <% endif %>\DUrole{n<>}{ <%- if sphinx_version >= (4, 4) %>string}}}<< sphinx81_linebreak >>{{ $\rightarrow$ string}} <%- else %>{\hyperref[\detokenize{index:module-string}]{\sphinxcrossref{string}}}}}}{{ $\rightarrow$ {\hyperref[\detokenize{index:module-string}]{\sphinxcrossref{string}}}}} <%- endif %> <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> Reformat the single paragraph in β€˜text’ to fit in lines of no more than β€˜self.width’ columns, and return a new string containing the entire wrapped paragraph. \end{fulllineitems} \index{wrap() (textwrap.TextWrapper method)@\spxentry{wrap()}\spxextra{textwrap.TextWrapper method}} \begin{fulllineitems} \phantomsection\label{\detokenize{index:textwrap.TextWrapper.wrap}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{wrap}}}<< sphinx81_linebreak >>{\<< sphinxparam >>{\DUrole{n<< sphinx6x_2nd('n') >>}{text}\DUrole{p<>}{:}<% if sphinx_version >= (4, 3) %>\DUrole{w<>}{<% if sphinx_version < (7, 2) %> <% endif %> }<% else %> <% endif %>\DUrole{n<>}{ <%- if sphinx_version >= (4, 4) -%> string}}}<< sphinx81_linebreak >>{{ $\rightarrow$ \DUrole{p<>}{{[}}string\DUrole{p<>}{{]}}}} <%- else -%> {\hyperref[\detokenize{index:module-string}]{\sphinxcrossref{string}}}}}}{{ $\rightarrow$ \DUrole{p<>}{{[}}{\hyperref[\detokenize{index:module-string}]{\sphinxcrossref{string}}}\DUrole{p}{{]}}}} <%- endif -%> <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> Reformat the single paragraph in β€˜text’ so it fits in lines of no more than β€˜self.width’ columns, and return a list of wrapped lines. Tabs in β€˜text’ are expanded with string.expandtabs(), and all other whitespace characters (including newline) are converted to space. \end{fulllineitems} \end{fulllineitems} \index{dedent() (in module textwrap)@\spxentry{dedent()}\spxextra{in module textwrap}} \begin{fulllineitems} \phantomsection\label{\detokenize{index:textwrap.dedent}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{textwrap.}}\sphinxbfcode{\sphinxupquote{dedent}}}<< sphinx81_linebreak >>{\<>{\DUrole{n<< sphinx6x_2nd('n') >>}{text}}}<< sphinx81_linebreak >>{} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> Remove any common leading whitespace from every line in \sphinxtitleref{text}. This can be used to make triple\sphinxhyphen{}quoted strings line up with the left edge of the display, while still presenting them in the source code in indented form. Note that tabs and spaces are both treated as whitespace, but they are not equal: the lines << '”' if docutils_version < (0, 19) else 'β€œ' >> hello” and β€œthello” are considered to have no common leading whitespace. Entirely blank lines are normalized to a newline character. \end{fulllineitems} \index{fill() (in module textwrap)@\spxentry{fill()}\spxextra{in module textwrap}} \begin{fulllineitems} \phantomsection\label{\detokenize{index:textwrap.fill}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{textwrap.}}\sphinxbfcode{\sphinxupquote{fill}}}<< sphinx81_linebreak >>{\<>{\DUrole{n<< sphinx6x_2nd('n') >>}{text}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{width}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{70}}<>{\DUrole{o<< sphinx6x_2nd('o') >>}{**}\DUrole{n<< sphinx6x_2nd('n') >>}{kwargs}}}<< sphinx81_linebreak >>{} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> Fill a single paragraph of text, returning a new string. Reformat the single paragraph in β€˜text’ to fit in lines of no more than β€˜width’ columns, and return a new string containing the entire wrapped paragraph. As with wrap(), tabs are expanded and other whitespace characters converted to space. See TextWrapper class for available keyword args to customize wrapping behaviour. \end{fulllineitems} \index{indent() (in module textwrap)@\spxentry{indent()}\spxextra{in module textwrap}} \begin{fulllineitems} \phantomsection\label{\detokenize{index:textwrap.indent}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{textwrap.}}\sphinxbfcode{\sphinxupquote{indent}}}<< sphinx81_linebreak >>{\<>{\DUrole{n<< sphinx6x_2nd('n') >>}{text}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{prefix}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{predicate}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{None}}}<< sphinx81_linebreak >>{} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> Adds <>prefix<> to the beginning of selected lines in β€˜text’. If β€˜predicate’ is provided, β€˜prefix’ will only be added to the lines where β€˜predicate(line)’ is True. If β€˜predicate’ is not provided, it will default to adding β€˜prefix’ to all non\sphinxhyphen{}empty lines that do not consist solely of whitespace characters. \end{fulllineitems} \index{shorten() (in module textwrap)@\spxentry{shorten()}\spxextra{in module textwrap}} \begin{fulllineitems} \phantomsection\label{\detokenize{index:textwrap.shorten}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{textwrap.}}\sphinxbfcode{\sphinxupquote{shorten}}}<< sphinx81_linebreak >>{\<>{\DUrole{n<< sphinx6x_2nd('n') >>}{text}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{width}}<>{\DUrole{o<< sphinx6x_2nd('o') >>}{**}\DUrole{n<< sphinx6x_2nd('n') >>}{kwargs}}}<< sphinx81_linebreak >>{} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> Collapse and truncate the given text to fit in the given width. The text first has its whitespace collapsed. If it then fits in the \sphinxstyleemphasis{width}, it is returned as is. Otherwise, as many words as possible are joined and then the placeholder is appended: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{g+gp}{\PYGZgt{}\PYGZgt{}\PYGZgt{} }\PYG{n}{textwrap}\PYG{o}{.}\PYG{n}{shorten}\PYG{p}{(}\PYG{l+s+s2}{\PYGZdq{}}\PYG{l+s+s2}{Hello world!}\PYG{l+s+s2}{\PYGZdq{}}\PYG{p}{,} \PYG{n}{width}\PYG{o}{=}\PYG{l+m+mi}{12}\PYG{p}{)} \PYG{g+go}{\PYGZsq{}Hello world!\PYGZsq{}} \PYG{g+gp}{\PYGZgt{}\PYGZgt{}\PYGZgt{} }\PYG{n}{textwrap}\PYG{o}{.}\PYG{n}{shorten}\PYG{p}{(}\PYG{l+s+s2}{\PYGZdq{}}\PYG{l+s+s2}{Hello world!}\PYG{l+s+s2}{\PYGZdq{}}\PYG{p}{,} \PYG{n}{width}\PYG{o}{=}\PYG{l+m+mi}{11}\PYG{p}{)} \PYG{g+go}{\PYGZsq{}Hello [...]\PYGZsq{}} \end{sphinxVerbatim} \end{fulllineitems} \index{wrap() (in module textwrap)@\spxentry{wrap()}\spxextra{in module textwrap}} \begin{fulllineitems} \phantomsection\label{\detokenize{index:textwrap.wrap}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{textwrap.}}\sphinxbfcode{\sphinxupquote{wrap}}}<< sphinx81_linebreak >>{\<>{\DUrole{n<< sphinx6x_2nd('n') >>}{text}}<>{\DUrole{n<< sphinx6x_2nd('n') >>}{width}\DUrole{o<< sphinx6x_2nd('o') >>}{=}\DUrole{default_value}{70}}<>{\DUrole{o<< sphinx6x_2nd('o') >>}{**}\DUrole{n<< sphinx6x_2nd('n') >>}{kwargs}}}<< sphinx81_linebreak >>{} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> Wrap a single paragraph of text, returning a list of wrapped lines. Reformat the single paragraph in β€˜text’ so it fits in lines of no more than β€˜width’ columns, and return a list of wrapped lines. By default, tabs in β€˜text’ are expanded with string.expandtabs(), and all other whitespace characters (including newline) are converted to space. See TextWrapper class for available keyword args to customize wrapping behaviour. \end{fulllineitems} \renewcommand{\indexname}{Python Module Index} \begin{sphinxtheindex} \let\bigletter\sphinxstyleindexlettergroup \bigletter{s} \item\relax\sphinxstyleindexentry{string}\sphinxstyleindexpageref{index:\detokenize{module-string}} \indexspace \bigletter{t} \item\relax\sphinxstyleindexentry{textwrap}\sphinxstyleindexpageref{index:\detokenize{module-textwrap}} \end{sphinxtheindex} \renewcommand{\indexname}{Index} \printindex \end{document} sphinx-toolbox-3.9.0/tests/test_code.py000066400000000000000000000021611475757360300202260ustar00rootroot00000000000000# 3rd party from coincidence.regressions import AdvancedFileRegressionFixture from domdf_python_tools.paths import PathPlus from sphinx.events import EventListener # this package from sphinx_toolbox import __version__, code from sphinx_toolbox.testing import check_asset_copy, run_setup def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(code.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == { "code-block": code.CodeBlock, "sourcecode": code.CodeBlock, "code-cell": code.CodeCell, "output-cell": code.OutputCell, } assert not roles assert additional_nodes == {code.Prompt} assert app.events.listeners == { "build-finished": [EventListener(id=1, handler=code.copy_asset_files, priority=500)], "config-inited": [EventListener(id=0, handler=code.configure, priority=500)], } def test_copy_asset_files(tmp_pathplus: PathPlus, advanced_file_regression: AdvancedFileRegressionFixture): check_asset_copy( code.copy_asset_files, "_static/sphinx-toolbox-code.css", file_regression=advanced_file_regression, ) sphinx-toolbox-3.9.0/tests/test_code_/000077500000000000000000000000001475757360300200135ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_code_/test_copy_asset_files_sphinx-toolbox-code.css000066400000000000000000000013101475757360300310770ustar00rootroot00000000000000div.code-cell.container div.prompt { color: #307FC1 } div.output-cell.container div.prompt { color: #BF5B3D } div.code-cell.container div.prompt, div.output-cell.container div.prompt { user-select: None; font-size: 13px; font-family: "SFMono-Regular", Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace; border: None; padding: 11px 0 0; margin: 0 5px 0 0; box-shadow: None; wrap-option: None; white-space: nowrap } div.code-cell.container, div.output-cell.container { padding-top: 5px; display: flex; align-items: stretch; margin: 0 } div.code-cell.container div.code-cell-code, div.output-cell.container div.output-cell-code { width: 100%; padding-top: 0; margin-top: 0 } sphinx-toolbox-3.9.0/tests/test_config.py000066400000000000000000000045611475757360300205670ustar00rootroot00000000000000# 3rd party import pytest from apeye.requests_url import RequestsURL # this package from sphinx_toolbox.config import InvalidOptionError, MissingOptionError, validate_config from tests.common import AttrDict def test_validate_config(): config = AttrDict({ "source_link_target": "Sphinx", "github_username": "sphinx-toolbox", "github_repository": "sphinx-toolbox", "rst_prolog": '', }) validate_config(None, config) # type: ignore[arg-type] assert config == { "source_link_target": "sphinx", "github_username": "sphinx-toolbox", "github_repository": "sphinx-toolbox", "github_url": RequestsURL("https://github.com/sphinx-toolbox/sphinx-toolbox"), "github_source_url": RequestsURL("https://github.com/sphinx-toolbox/sphinx-toolbox/blob/master"), "github_issues_url": RequestsURL("https://github.com/sphinx-toolbox/sphinx-toolbox/issues"), "github_pull_url": RequestsURL("https://github.com/sphinx-toolbox/sphinx-toolbox/pull"), "rst_prolog": "\n\n.. |nbsp| unicode:: 0xA0\n :trim:", } config = AttrDict({ "source_link_target": "Sphinx", "github_username": None, "github_repository": "sphinx-toolbox", }) with pytest.raises(MissingOptionError, match="The 'github_username' option is required."): validate_config(None, config) # type: ignore[arg-type] config = AttrDict({ "source_link_target": "Sphinx", "github_username": "sphinx-toolbox", "github_repository": None, }) with pytest.raises(MissingOptionError, match="The 'github_repository' option is required."): validate_config(None, config) # type: ignore[arg-type] config = AttrDict({ "source_link_target": "bananas", "github_username": "sphinx-toolbox", "github_repository": "sphinx-toolbox", }) with pytest.raises(InvalidOptionError, match="Invalid value for 'source_link_target'."): validate_config(None, config) # type: ignore[arg-type] @pytest.mark.parametrize( "target, expects", [ ("Sphinx", "sphinx"), ("sphinx", "sphinx"), ("GitHub", "github"), ("Github", "github"), ("github", "github"), ] ) def test_source_link_target(target: str, expects: str): config = AttrDict({ "source_link_target": target, "github_username": "sphinx-toolbox", "github_repository": "sphinx-toolbox", "rst_prolog": '', }) validate_config(None, config) # type: ignore[arg-type] assert config.source_link_target == expects sphinx-toolbox-3.9.0/tests/test_css.py000066400000000000000000000017171475757360300201120ustar00rootroot00000000000000# 3rd party from coincidence.regressions import AdvancedFileRegressionFixture from domdf_python_tools.paths import PathPlus from sphinx.events import EventListener # this package import sphinx_toolbox from sphinx_toolbox import _css from sphinx_toolbox.testing import check_asset_copy, run_setup def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(_css.setup) assert setup_ret == {"version": sphinx_toolbox.__version__, "parallel_read_safe": True} assert additional_nodes == set() assert app.registry.translation_handlers == {} assert app.events.listeners == {"build-finished": [EventListener(0, _css.copy_asset_files, 500)]} assert app.registry.css_files == [("css/sphinx-toolbox.css", {})] def test_copy_asset_files(tmp_pathplus: PathPlus, advanced_file_regression: AdvancedFileRegressionFixture): check_asset_copy( _css.copy_asset_files, "_static/css/sphinx-toolbox.css", file_regression=advanced_file_regression, ) sphinx-toolbox-3.9.0/tests/test_css_/000077500000000000000000000000001475757360300176715ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_css_/test_copy_asset_files_sphinx-toolbox.css000066400000000000000000000020121475757360300300450ustar00rootroot00000000000000p.source-link { margin-bottom: 0 } p.source-link + hr.docutils { margin-top: 10px } p.sphinx-toolbox-extensions, div.sphinx-toolbox-extensions.highlight-python, div.sphinx-toolbox-extensions.highlight-python div.highlight { margin-bottom: 10px } div.rest-example.docutils.container { padding-left: 5px; border-style: dotted; border-width: 1px; border-color: darkgray } div[id*="installation"] .sphinx-tabs-tab { color: #2980b9 } button.sphinx-tabs-tab, div.sphinx-tabs-panel { outline: None !important } .table-wrapper td p img.sphinx_toolbox_shield { vertical-align: middle } span.regex_literal { color: dimgrey } span.regex_at { color: orangered } span.regex_repeat_brace { color: orangered } span.regex_branch { color: orangered } span.regex_subpattern { color: dodgerblue } span.regex_in { color: darkorange } span.regex_category { color: darkseagreen } span.regex_repeat { color: orangered } span.regex_any { color: orangered } code.regex { font-size: 80% } span.regex { font-weight: bold } sphinx-toolbox-3.9.0/tests/test_github.py000066400000000000000000000303251475757360300206010ustar00rootroot00000000000000# stdlib from http import HTTPStatus from typing import Set, Type # 3rd party import docutils.nodes import pytest from apeye.requests_url import RequestsURL from coincidence.params import count from docutils import nodes from docutils.utils import Reporter from pytest_httpserver import HTTPServer from sphinx.events import EventListener # this package import sphinx_toolbox from sphinx_toolbox import github from sphinx_toolbox.config import MissingOptionError from sphinx_toolbox.github.issues import ( IssueNode, _depart_issue_node_latex, _visit_issue_node_latex, depart_issue_node, issue_role, pull_role, visit_issue_node ) from sphinx_toolbox.github.repos_and_users import ( GitHubObjectLinkNode, _depart_github_object_link_node_latex, _visit_github_object_link_node_latex, depart_github_object_link_node, repository_role, user_role, visit_github_object_link_node ) from sphinx_toolbox.testing import run_setup from sphinx_toolbox.utils import make_github_url from tests.common import AttrDict, error_codes, get_app_config_values class FakeGitHubInliner: def __init__(self): app = AttrDict({"config": AttrDict()}) env = AttrDict({"app": app}) settings = AttrDict({"env": env}) reporter = Reporter('', 0, 100) self.document = AttrDict({"settings": settings, "reporter": reporter}) class FakePullInliner: def __init__(self, github_issues_url: str): config = AttrDict({"github_pull_url": RequestsURL(github_issues_url)}) app = AttrDict({"config": config}) env = AttrDict({"app": app}) settings = AttrDict({"env": env}) reporter = Reporter('', 0, 100) self.document = AttrDict({"settings": settings, "reporter": reporter}) class FakeIssueInliner: def __init__(self, github_issues_url: str): config = AttrDict({"github_issues_url": RequestsURL(github_issues_url)}) app = AttrDict({"config": config}) env = AttrDict({"app": app}) settings = AttrDict({"env": env}) reporter = Reporter('', 0, 100) self.document = AttrDict({"settings": settings, "reporter": reporter}) def test_missing_options(): config = AttrDict({ "github_username": "octocat", "github_repository": None, }) with pytest.raises(MissingOptionError, match="The 'github_repository' option is required."): github.validate_config('', config) # type: ignore[arg-type] config = AttrDict({ "github_username": None, "github_repository": "hello_world", }) with pytest.raises(MissingOptionError, match="The 'github_username' option is required."): github.validate_config('', config) # type: ignore[arg-type] issues_repositories = pytest.mark.parametrize( "url, repository", [ ("https://github.com/sphinx-toolbox/sphinx-toolbox/issues", "sphinx-toolbox/sphinx-toolbox"), ("https://github.com/pytest-dev/pytest/issues", "pytest-dev/pytest"), ("https://github.com/tox-dev/tox/issues", "tox-dev/tox"), ("https://github.com/python/cpython/issues", "python/cpython"), ("https://github.com/psf/requests/issues", "psf/requests"), ] ) pull_repositories = pytest.mark.parametrize( "url, repository", [ ("https://github.com/sphinx-toolbox/sphinx-toolbox/pull", "sphinx-toolbox/sphinx-toolbox"), ("https://github.com/pytest-dev/pytest/pull", "pytest-dev/pytest"), ("https://github.com/tox-dev/tox/pull", "tox-dev/tox"), ("https://github.com/python/cpython/pull", "python/cpython"), ("https://github.com/psf/requests/pull", "psf/requests"), ] ) @pytest.mark.parametrize( "url", [ "https://github.com/sphinx-toolbox/sphinx-toolbox/issues", "https://github.com/pytest-dev/pytest/issues", "https://github.com/tox-dev/tox/issues", "https://github.com/python/cpython/issues", "https://github.com/psf/requests/issues", ] ) @count(100) def test_issue_role(count: int, url: str): issue_number = count nodes, messages = issue_role('', '', str(issue_number), 0, FakeIssueInliner(url)) # type: ignore[arg-type] assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages assert isinstance(nodes[0], IssueNode) assert nodes[0].issue_url == f"{url}/{issue_number}" assert nodes[0].issue_number == issue_number assert not nodes[0].has_tooltip @issues_repositories @count(100, 0, 10) def test_issue_role_with_repository(count: int, url: str, repository: str): issue_number = count nodes, messages = issue_role('', '', f"{issue_number} <{repository}>", 0, "Not a URL") # type: ignore[arg-type] assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages assert isinstance(nodes[0], IssueNode) assert nodes[0].issue_url == f"{url}/{issue_number}" assert nodes[0].issue_number == issue_number assert not nodes[0].has_tooltip def test_issue_role_invalid_repository(capsys): url = "https://github.com/sphinx-toolbox/sphinx-toolbox" nodes, messages = issue_role('', '', f"7 ", 0, FakeIssueInliner(url)) # type: ignore[arg-type] assert capsys.readouterr().err == ":: (WARNING/2) Invalid repository 'foo' for issue #7.\n" issue_number = 7 assert isinstance(nodes, list) assert nodes assert isinstance(nodes[0], IssueNode) assert nodes[0].issue_url == f"{url}/{issue_number}" assert nodes[0].issue_number == issue_number assert not nodes[0].has_tooltip assert isinstance(messages, list) assert messages assert isinstance(messages[0], docutils.nodes.system_message) assert messages[0].astext() == ":: (WARNING/2) Invalid repository 'foo' for issue #7." @pytest.mark.parametrize( "url", [ "https://github.com/sphinx-toolbox/sphinx-toolbox/pull", "https://github.com/pytest-dev/pytest/pull", "https://github.com/tox-dev/tox/pull", "https://github.com/python/cpython/pull", "https://github.com/psf/requests/pull", ] ) @count(100) def test_pull_role(count: int, url: str): issue_number = count nodes, messages = pull_role('', '', str(issue_number), 0, FakePullInliner(url)) # type: ignore[arg-type] assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages assert isinstance(nodes[0], IssueNode) assert nodes[0].issue_url == f"{url}/{issue_number}" assert nodes[0].issue_number == issue_number assert not nodes[0].has_tooltip @pull_repositories @count(100, 0, 10) def test_pull_role_with_repository(count: int, url: str, repository: str): issue_number = count nodes, messages = pull_role('', '', f"{issue_number} <{repository}>", 0, "Not a URL") # type: ignore[arg-type] assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages assert isinstance(nodes[0], IssueNode) assert nodes[0].issue_url == f"{url}/{issue_number}" assert nodes[0].issue_number == issue_number assert not nodes[0].has_tooltip def test_pull_role_invalid_repository(capsys): url = "https://github.com/sphinx-toolbox/sphinx-toolbox" nodes, messages = pull_role('', '', f"7 ", 0, FakePullInliner(url)) # type: ignore[arg-type] assert capsys.readouterr().err == ":: (WARNING/2) Invalid repository 'foo' for pull request #7.\n" issue_number = 7 assert isinstance(nodes, list) assert nodes assert isinstance(nodes[0], IssueNode) assert nodes[0].issue_url == f"{url}/{issue_number}" assert nodes[0].issue_number == issue_number assert not nodes[0].has_tooltip assert isinstance(messages, list) assert messages assert isinstance(messages[0], docutils.nodes.system_message) assert messages[0].astext() == ":: (WARNING/2) Invalid repository 'foo' for pull request #7." def test_user_role(): nodes, messages = user_role('', '', "domdfcoding", 0, FakeGitHubInliner()) # type: ignore[arg-type] assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages assert isinstance(nodes[0], GitHubObjectLinkNode) assert nodes[0].name == "@domdfcoding" assert nodes[0].url == "https://github.com/domdfcoding" def test_user_role_with_text(): nodes, messages = user_role('', '', "Checkout my user page ", 0, FakeGitHubInliner()) # type: ignore[arg-type] assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages assert isinstance(nodes[0], docutils.nodes.reference) assert nodes[0].rawsource == "Checkout my user page" assert nodes[0].attributes["refuri"] == "https://github.com/domdfcoding" def test_repository_role(): nodes, messages = repository_role( '', '', "sphinx-toolbox/sphinx-toolbox", 0, FakeGitHubInliner(), # type: ignore[arg-type] ) assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages assert isinstance(nodes[0], GitHubObjectLinkNode) assert nodes[0].name == "sphinx-toolbox/sphinx-toolbox" assert nodes[0].url == "https://github.com/sphinx-toolbox/sphinx-toolbox" def test_repository_role_with_text(): nodes, messages = repository_role( '', '', "Checkout my repository ", 0, FakeGitHubInliner(), # type: ignore[arg-type] ) assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages assert isinstance(nodes[0], docutils.nodes.reference) assert nodes[0].rawsource == "Checkout my repository" assert nodes[0].attributes["refuri"] == "https://github.com/sphinx-toolbox/sphinx-toolbox" def test_repository_role_invalid(capsys): nodes, messages = repository_role( '', '', "Checkout my repository ", 0, FakeGitHubInliner(), # type: ignore[arg-type] ) expected_stderr = ":: (WARNING/2) Invalid repository 'sphinx-toolbox/sphinx-toolbox/default-values'." assert capsys.readouterr().err == f"{expected_stderr}\n" assert isinstance(nodes, list) assert not nodes assert isinstance(messages, list) assert messages assert isinstance(messages[0], docutils.nodes.system_message) assert messages[0].astext() == expected_stderr class FakeTranslator: def __init__(self): self.body = [] def visit_reference(self, node: nodes.Node): # noqa: MAN002 pass def depart_reference(self, node: nodes.Node): # noqa: MAN002 pass def test_visit_issue_node(): node = IssueNode(7680, make_github_url("pytest-dev", "pytest") / "issues/7680") translator = FakeTranslator() assert not node.has_tooltip visit_issue_node(translator, node) # type: ignore[arg-type] assert translator.body == [''] assert node.has_tooltip @error_codes def test_visit_issue_node_errors(error_code: HTTPStatus, error_server: HTTPServer): node = IssueNode(7680, error_server.url_for(f"/{error_code:d}")) translator = FakeTranslator() assert not node.has_tooltip with pytest.warns(UserWarning) as w: visit_issue_node(translator, node) # type: ignore[arg-type] assert w[0].message.args[0] == "Issue/Pull Request #7680 not found." # type: ignore[union-attr] assert translator.body == [] assert not node.has_tooltip def test_depart_issue_node(): node = IssueNode(7680, make_github_url("pytest-dev", "pytest") / "issues/7680") translator = FakeTranslator() assert not node.has_tooltip depart_issue_node(translator, node) # type: ignore[arg-type] assert translator.body == [] node = IssueNode(7680, make_github_url("pytest-dev", "pytest") / "issues/7680") translator = FakeTranslator() node.has_tooltip = True depart_issue_node(translator, node) # type: ignore[arg-type] assert translator.body == [""] def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(github.setup) assert setup_ret == {"version": sphinx_toolbox.__version__, "parallel_read_safe": True} expected_additional_nodes: Set[Type[nodes.reference]] = {IssueNode, GitHubObjectLinkNode} assert additional_nodes == expected_additional_nodes assert app.registry.translation_handlers == { "html": { "IssueNode": (visit_issue_node, depart_issue_node), "GitHubObjectLinkNode": (visit_github_object_link_node, depart_github_object_link_node) }, "latex": { "IssueNode": (_visit_issue_node_latex, _depart_issue_node_latex), "GitHubObjectLinkNode": ( _visit_github_object_link_node_latex, _depart_github_object_link_node_latex, ) }, } # Moved to own setup function assert get_app_config_values(app.config.values["github_username"]) == (None, "env", [str]) assert get_app_config_values(app.config.values["github_repository"]) == (None, "env", [str]) assert app.events.listeners == { "config-inited": [EventListener(id=0, handler=github.validate_config, priority=850)], } assert directives == {} sphinx-toolbox-3.9.0/tests/test_installation.py000066400000000000000000000142511475757360300220200ustar00rootroot00000000000000# stdlib from typing import List # 3rd party import pytest from sphinx.events import EventListener # this package from sphinx_toolbox import installation from sphinx_toolbox.installation import make_installation_instructions from sphinx_toolbox.testing import run_setup from tests.common import AttrDict, get_app_config_values class FakeBuildEnvironment(AttrDict): def __init__(self, tab_width: int): config = AttrDict({ "docutils_tab_width": tab_width, "github_username": "octocat", "github_repository": "hello_world", "conda_channels": [], }) app = AttrDict(extensions=[ "sphinx_toolbox.installation", "sphinx_toolbox.github", ]) super().__init__({"config": config, "app": app}) def test_make_installation_instructions_errors(): with pytest.warns(UserWarning) as w: assert make_installation_instructions({}, FakeBuildEnvironment(4)) == [] # type: ignore[arg-type] assert len(w) == 1 args: List[str] = w[0].message.args # type: ignore[union-attr, assignment] assert args[0] == "No installation source specified. No installation instructions will be shown." with pytest.raises(ValueError, match="No PyPI project name supplied for the PyPI installation instructions."): make_installation_instructions({"pypi": True}, FakeBuildEnvironment(4)) # type: ignore[arg-type] with pytest.raises(ValueError, match="No username supplied for the Anaconda installation instructions."): make_installation_instructions({"anaconda": True}, FakeBuildEnvironment(4)) # type: ignore[arg-type] def test_make_installation_instructions(): assert make_installation_instructions( {"pypi": True, "project_name": "my_project"}, FakeBuildEnvironment(4), # type: ignore[arg-type] ) == [ ".. tabs::", '', " .. tab:: from PyPI", '', " .. prompt:: bash", '', " python3 -m pip install my_project --user", ] assert make_installation_instructions( {"pypi": True, "project_name": "my_project", "pypi-name": "my-project"}, FakeBuildEnvironment(4), # type: ignore[arg-type] ) == [ ".. tabs::", '', " .. tab:: from PyPI", '', " .. prompt:: bash", '', " python3 -m pip install my-project --user", ] assert make_installation_instructions( {"anaconda": True, "project_name": "my_project"}, FakeBuildEnvironment(4), # type: ignore[arg-type] ) == [ ".. tabs::", '', " .. tab:: from Anaconda", '', " .. prompt:: bash", '', " conda install my_project", '', ] assert make_installation_instructions( {"anaconda": True, "project_name": "my_project", "conda-name": "my-project"}, FakeBuildEnvironment(4), # type: ignore[arg-type] ) == [ ".. tabs::", '', " .. tab:: from Anaconda", '', " .. prompt:: bash", '', " conda install my-project", '', ] assert make_installation_instructions( {"anaconda": True, "project_name": "my_project", "pypi-name": "pypi-project"}, FakeBuildEnvironment(4), # type: ignore[arg-type] ) == [ ".. tabs::", '', " .. tab:: from Anaconda", '', " .. prompt:: bash", '', " conda install pypi-project", '', ] assert make_installation_instructions( {"github": True, "project_name": "my_project"}, FakeBuildEnvironment(4), # type: ignore[arg-type] ) == [ ".. tabs::", '', " .. tab:: from GitHub", '', " .. prompt:: bash", '', " python3 -m pip install git+https://github.com/octocat/hello_world@master --user", ] assert make_installation_instructions( { "anaconda": True, "conda-channels": "foo,bar", "project_name": "my_project", }, FakeBuildEnvironment(4), # type: ignore[arg-type] ) == [ ".. tabs::", '', " .. tab:: from Anaconda", '', " First add the required channels", '', " .. prompt:: bash", '', " conda config --add channels https://conda.anaconda.org/foo", " conda config --add channels https://conda.anaconda.org/bar", '', " Then install", '', " .. prompt:: bash", '', " conda install my_project", '', ] # Testd whitespace assert make_installation_instructions( { "anaconda": True, "conda-channels": " foo , bar ", "project_name": "my_project", }, FakeBuildEnvironment(4), # type: ignore[arg-type] ) == [ ".. tabs::", '', " .. tab:: from Anaconda", '', " First add the required channels", '', " .. prompt:: bash", '', " conda config --add channels https://conda.anaconda.org/foo", " conda config --add channels https://conda.anaconda.org/bar", '', " Then install", '', " .. prompt:: bash", '', " conda install my_project", '', ] def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(installation.setup) assert app.events.listeners == { "env-purge-doc": [ EventListener(id=1, handler=installation.extensions_node_purger.purge_nodes, priority=500), ], "env-get-outdated": [ EventListener( id=0, handler=installation.installation_node_purger.get_outdated_docnames, priority=500 ), ], "build-finished": [EventListener(id=3, handler=installation.copy_asset_files, priority=500)], "config-inited": [EventListener(id=2, handler=installation._on_config_inited, priority=510)], } assert get_app_config_values(app.config.values["conda_channels"]) == ([], "env", [list]) assert directives == { "installation": installation.InstallationDirective, "extensions": installation.ExtensionsDirective, } assert app.registry.source_parsers == {} assert app.registry.css_files == [] assert app.registry.js_files == [] installation._on_config_inited(app, app.config) # type: ignore[arg-type] assert app.registry.css_files == [("sphinx_toolbox_installation.css", {})] assert app.registry.js_files == [("sphinx_toolbox_installation.js", {})] sphinx-toolbox-3.9.0/tests/test_issues.py000066400000000000000000000171551475757360300206400ustar00rootroot00000000000000# 3rd party import pytest from apeye.requests_url import RequestsURL from coincidence.params import count from docutils import nodes from docutils.nodes import system_message from docutils.utils import Reporter # this package import sphinx_toolbox from sphinx_toolbox import issues from sphinx_toolbox.github.issues import IssueNode, issue_role, pull_role from sphinx_toolbox.testing import run_setup from tests.common import AttrDict class FakePullInliner: def __init__(self, github_issues_url: str): config = AttrDict({"github_pull_url": RequestsURL(github_issues_url)}) app = AttrDict({"config": config}) env = AttrDict({"app": app}) settings = AttrDict({"env": env}) reporter = Reporter('', 0, 100) self.document = AttrDict({"settings": settings, "reporter": reporter}) class FakeIssueInliner: def __init__(self, github_issues_url: str): config = AttrDict({"github_issues_url": RequestsURL(github_issues_url)}) app = AttrDict({"config": config}) env = AttrDict({"app": app}) settings = AttrDict({"env": env}) reporter = Reporter('', 0, 100) self.document = AttrDict({"settings": settings, "reporter": reporter}) @pytest.mark.parametrize( "url", [ "https://github.com/sphinx-toolbox/sphinx-toolbox/issues", "https://github.com/pytest-dev/pytest/issues", "https://github.com/tox-dev/tox/issues", "https://github.com/python/cpython/issues", "https://github.com/psf/requests/issues", ] ) @count(100) def test_issue_role(count: int, url: str): issue_number = count nodes, messages = issue_role( '', '', str(issue_number), 0, FakeIssueInliner(url), # type: ignore[arg-type] ) assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages assert isinstance(nodes[0], IssueNode) assert nodes[0].issue_url == f"{url}/{issue_number}" assert nodes[0].issue_number == issue_number assert not nodes[0].has_tooltip issues_repositories = pytest.mark.parametrize( "url, repository", [ ("https://github.com/sphinx-toolbox/sphinx-toolbox/issues", "sphinx-toolbox/sphinx-toolbox"), ("https://github.com/pytest-dev/pytest/issues", "pytest-dev/pytest"), ("https://github.com/tox-dev/tox/issues", "tox-dev/tox"), ("https://github.com/python/cpython/issues", "python/cpython"), ("https://github.com/psf/requests/issues", "psf/requests"), ] ) pull_repositories = pytest.mark.parametrize( "url, repository", [ ("https://github.com/sphinx-toolbox/sphinx-toolbox/pull", "sphinx-toolbox/sphinx-toolbox"), ("https://github.com/pytest-dev/pytest/pull", "pytest-dev/pytest"), ("https://github.com/tox-dev/tox/pull", "tox-dev/tox"), ("https://github.com/python/cpython/pull", "python/cpython"), ("https://github.com/psf/requests/pull", "psf/requests"), ] ) @issues_repositories @count(100, 0, 10) def test_issue_role_with_repository(count: int, url: str, repository: str): issue_number = count nodes, messages = issue_role('', '', f"{issue_number} <{repository}>", 0, "Not a URL") # type: ignore[arg-type] assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages assert isinstance(nodes[0], IssueNode) assert nodes[0].issue_url == f"{url}/{issue_number}" assert nodes[0].issue_number == issue_number assert not nodes[0].has_tooltip @pytest.mark.parametrize( "url", [ "https://github.com/sphinx-toolbox/sphinx-toolbox/pull", "https://github.com/pytest-dev/pytest/pull", "https://github.com/tox-dev/tox/pull", "https://github.com/python/cpython/pull", "https://github.com/psf/requests/pull", ] ) @count(100) def test_pull_role(count: int, url: str): issue_number = count nodes, messages = pull_role( '', '', str(issue_number), 0, FakePullInliner(url), # type: ignore[arg-type] ) assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages assert isinstance(nodes[0], IssueNode) assert nodes[0].issue_url == f"{url}/{issue_number}" assert nodes[0].issue_number == issue_number assert not nodes[0].has_tooltip @pull_repositories @count(100, 0, 10) def test_pull_role_with_repository(count: int, url: str, repository: str): issue_number = count nodes, messages = pull_role( '', '', f"{issue_number} <{repository}>", 0, "Not a URL", # type: ignore[arg-type] ) assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages assert isinstance(nodes[0], IssueNode) assert nodes[0].issue_url == f"{url}/{issue_number}" assert nodes[0].issue_number == issue_number assert not nodes[0].has_tooltip def test_issue_role_invalid_repository(capsys): url = "https://github.com/sphinx-toolbox/sphinx-toolbox" nodes, messages = issue_role('', '', f"7 ", 0, FakeIssueInliner(url)) # type: ignore[arg-type] assert capsys.readouterr().err == ":: (WARNING/2) Invalid repository 'foo' for issue #7.\n" issue_number = 7 assert isinstance(nodes, list) assert nodes assert isinstance(nodes[0], IssueNode) assert nodes[0].issue_url == f"{url}/{issue_number}" assert nodes[0].issue_number == issue_number assert not nodes[0].has_tooltip assert isinstance(messages, list) assert messages assert isinstance(messages[0], system_message) assert messages[0].astext() == ":: (WARNING/2) Invalid repository 'foo' for issue #7." def test_pull_role_invalid_repository(capsys): url = "https://github.com/sphinx-toolbox/sphinx-toolbox" nodes, messages = pull_role('', '', f"7 ", 0, FakePullInliner(url)) # type: ignore[arg-type] assert capsys.readouterr().err == ":: (WARNING/2) Invalid repository 'foo' for pull request #7.\n" issue_number = 7 assert isinstance(nodes, list) assert nodes assert isinstance(nodes[0], IssueNode) assert nodes[0].issue_url == f"{url}/{issue_number}" assert nodes[0].issue_number == issue_number assert not nodes[0].has_tooltip assert isinstance(messages, list) assert messages assert isinstance(messages[0], system_message) assert messages[0].astext() == ":: (WARNING/2) Invalid repository 'foo' for pull request #7." class FakeTranslator: def __init__(self): self.body = [] def visit_reference(self, node: nodes.Node) -> None: pass def depart_reference(self, node: nodes.Node) -> None: pass # # @pytest.fixture() # def rootdir(): # rdir = PathPlus(__file__).parent.absolute() / "github-doc-test" # (rdir / "sphinx-test-github-root").maybe_make(parents=True) # return rdir # # # @pytest.fixture() # def github_issues_app(app): # return app # # # @pytest.fixture() # def issues_content(github_issues_app): # github_issues_app.build(force_all=True) # yield github_issues_app # # # @pytest.fixture() # def issues_page(issues_content, request) -> BeautifulSoup: # pagename = request.param # c = (issues_content.outdir / pagename).read_text() # # yield BeautifulSoup(c, "html5lib") # # # @pytest.mark.parametrize("issues_page", ["index.html"], indirect=True) # def test_output_github(issues_page: BeautifulSoup): # # Make sure the page title is what you expect # title = issues_page.find("h1").contents[0].strip() # assert "sphinx-toolbox Demo" == title # # tag_count = 0 # # for a_tag in issues_page.select("a.reference.external"): # if a_tag["href"] == "https://github.com/sphinx-toolbox/sphinx-toolbox/blob/master/sphinx_toolbox/config.py": # if a_tag.contents[0] == "sphinx_toolbox/config.py": # tag_count += 1 # # assert tag_count == 1 def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(issues.setup) assert setup_ret == {"version": sphinx_toolbox.__version__, "parallel_read_safe": True} assert roles == { "issue": issue_role, "pr": pull_role, "pull": pull_role, } assert additional_nodes == set() assert app.registry.translation_handlers == {} sphinx-toolbox-3.9.0/tests/test_issues_output/000077500000000000000000000000001475757360300216755ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_issues_output/__init__.py000066400000000000000000000000001475757360300237740ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_issues_output/conftest.py000066400000000000000000000071011475757360300240730ustar00rootroot00000000000000# Based on Sphinx # Copyright (c) 2007-2020 by the Sphinx team. # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib import pathlib import shutil from typing import Any, Dict, List, Sequence, Tuple, cast # 3rd party import pytest from bs4 import BeautifulSoup from domdf_python_tools.paths import PathPlus from sphinx.application import Sphinx from sphinx.testing.fixtures import app as gh_src_app from sphinx.testing.fixtures import make_app, shared_result, sphinx_test_tempdir, test_params # this package from tests.common import AppParams fixtures = [make_app, shared_result, sphinx_test_tempdir, test_params, gh_src_app] @pytest.fixture(scope="session") def rootdir() -> PathPlus: rdir = PathPlus(__file__).parent.absolute() / "github-issues-doc-test" (rdir / "test-github-issues-root").maybe_make(parents=True) return PathPlus(rdir) @pytest.fixture() def app_params( request: Any, test_params: Dict, sphinx_test_tempdir: pathlib.Path, rootdir: pathlib.Path, ) -> Tuple[Sequence, Dict]: """ parameters that is specified by 'pytest.mark.sphinx' for sphinx.application.Sphinx initialization """ # ##### process pytest.mark.sphinx markers = request.node.iter_markers("sphinx") pargs = {} kwargs: Dict[str, Any] = {} if markers is not None: # to avoid stacking positional args for info in reversed(list(markers)): for i, a in enumerate(info.args): pargs[i] = a kwargs.update(info.kwargs) args = [pargs[i] for i in sorted(pargs.keys())] # ##### prepare Application params testroot = "github-issues-root" kwargs["srcdir"] = srcdir = sphinx_test_tempdir / kwargs.get("srcdir", testroot) # special support for sphinx/tests if rootdir and not srcdir.exists(): testroot_path = rootdir / ("test-" + testroot) shutil.copytree(testroot_path, srcdir) return AppParams(args, kwargs) @pytest.fixture() def github_source_page(gh_src_app: Sphinx, request) -> BeautifulSoup: gh_src_app.build(force_all=True) pagename = request.param c = (PathPlus(gh_src_app.outdir) / pagename).read_text() soup = BeautifulSoup(c, "html5lib") for meta in cast(List[Dict], soup.find_all("meta")): if meta.get("content", '') == "width=device-width, initial-scale=0.9, maximum-scale=0.9": meta.extract() # type: ignore[attr-defined] return soup sphinx-toolbox-3.9.0/tests/test_issues_output/github-issues-doc-test/000077500000000000000000000000001475757360300262105ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_issues_output/github-issues-doc-test/test-github-issues-root/000077500000000000000000000000001475757360300327415ustar00rootroot00000000000000autoprotocol.rst000066400000000000000000000006251475757360300361510ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_issues_output/github-issues-doc-test/test-github-issues-root:orphan: ===================================== sphinx-toolbox Demo - Autoprotocol ===================================== .. automodule:: autoprotocol_demo :members: :exclude-members: HasGreaterThan .. autoprotocol:: autoprotocol_demo.HasGreaterThan .. autoprotocol:: autoprotocol_demo.HasGreaterThan :noindex: :show-inheritance: .. autoprotocol:: autoprotocol_demo.SupportsAbs :show-inheritance: sphinx-toolbox-3.9.0/tests/test_issues_output/github-issues-doc-test/test-github-issues-root/conf.py000066400000000000000000000017741475757360300342510ustar00rootroot00000000000000# 3rd party from sphinx import addnodes from sphinx.application import Sphinx from sphinx.locale import _ extensions = [ "sphinx.ext.viewcode", "sphinx.ext.autodoc", "sphinx_toolbox", "sphinx_toolbox.more_autodoc.generic_bases", "sphinx_toolbox.more_autodoc.autoprotocol", ] github_username = "sphinx-toolbox" github_repository = "sphinx-toolbox" source_link_target = "GitHub" generic_bases_fully_qualified = True project = "Python" author = "unknown" # These revert https://github.com/sphinx-doc/sphinx/pull/8472 def visit_desc_signature(self, node: addnodes.desc_signature) -> None: # the id is set automatically self.body.append(self.starttag(node, "dt")) def depart_desc_signature(self, node: addnodes.desc_signature) -> None: if not node.get("is_multiline"): self.add_permalink_ref(node, _("Permalink to this definition")) self.body.append('\n') def setup(app: Sphinx) -> None: app.add_node(addnodes.desc_signature, html=(visit_desc_signature, depart_desc_signature), override=True) generic_bases.rst000066400000000000000000000004471475757360300362120ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_issues_output/github-issues-doc-test/test-github-issues-root:orphan: ===================================== sphinx-toolbox Demo - Generic Bases ===================================== .. autoclass:: sphinx_toolbox.more_autodoc.generic_bases.Example :show-inheritance: .. autoclass:: sphinx_toolbox.more_autodoc.generic_bases.Example2 :show-inheritance: index.rst000066400000000000000000000010471475757360300345250ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_issues_output/github-issues-doc-test/test-github-issues-root===================================== sphinx-toolbox Demo - GitHub Issues ===================================== :issue:`1` :pr:`2` :pull:`2` :issue:`7680 ` :issue:`7671 ` :source:`sphinx_toolbox/source.py` :source:`sphinx_toolbox/more_autodoc/__init__.py` Issue with code in its title: :github:issue:`10241 `. Issue with code in the beginning of the title: :github:issue:`9575 `. Issue with HTML entities in title: :github:issue:`56 `. sphinx-toolbox-3.9.0/tests/test_issues_output/test_source_output.py000066400000000000000000000130631475757360300262310ustar00rootroot00000000000000# stdlib from typing import Dict, List, Optional, Union, cast # 3rd party import pytest from _pytest.mark import ParameterSet from bs4 import BeautifulSoup, Tag from coincidence.selectors import min_version, only_version from domdf_python_tools.paths import PathPlus from sphinx.application import Sphinx # this package from sphinx_toolbox.testing import HTMLRegressionFixture def test_build_github(gh_src_app: Sphinx): # app is a Sphinx application object for default sphinx project (`tests/doc-test/sphinx-test-github-root`). gh_src_app.build() gh_src_app.build() @pytest.mark.usefixtures("docutils_17_compat") @pytest.mark.parametrize("github_source_page", ["index.html"], indirect=True) def test_output_github(github_source_page: BeautifulSoup, html_regression: HTMLRegressionFixture): # Make sure the page title is what you expect h1 = cast(Optional[Tag], github_source_page.find("h1")) assert h1 is not None title = cast(str, h1.contents[0]).strip() assert "sphinx-toolbox Demo - GitHub Issues" == title links = cast(List[Tag], github_source_page.select('p')) assert len(links) == 10 assert links[1] == links[2] assert links[0].abbr["title"] == "Example Issue" # type: ignore[index] # check the abbr tag # check the a tag's class expected_classes = ["reference", "external"] assert links[0].abbr.a["class"] == expected_classes # type: ignore[index,union-attr] expected_href = "https://github.com/sphinx-toolbox/sphinx-toolbox/issues/1" assert links[0].abbr.a["href"] == expected_href # type: ignore[index,union-attr] # check the a tag's href assert links[0].abbr.a.contents[0] == "#1" # type: ignore[union-attr] # check the body assert [str(x) for x in links] == [ '

#1

', '

#2

', '

#2

', '

pytest-dev/pytest#7680

', '

pytest-dev/pytest#7671

', '

sphinx_toolbox/source.py

', '

sphinx_toolbox/more_autodoc/__init__.py

', '

Issue with code in its title: sphinx-doc/sphinx#10241.

', '

Issue with code in the beginning of the title: sphinx-doc/sphinx#9575.

', '

Issue with HTML entities in title: sphinx-toolbox/toctree_plus#56.

', ] html_regression.check(github_source_page, jinja2=True) # The following is in here because it needs to run with different options to tests/test_output pages_to_check: List[Union[str, ParameterSet]] = [ "autoprotocol.html", pytest.param( "generic_bases.html", marks=only_version(3.7, reason="Output differs on Python 3.7"), id="generic_bases_37" ), pytest.param( "generic_bases.html", marks=min_version(3.8, reason="Output differs on Python 3.8+"), id="generic_bases" ), ] @pytest.mark.usefixtures("docutils_17_compat") def test_html_output(gh_src_app: Sphinx, html_regression: HTMLRegressionFixture): """ Parametrize new files here rather than as their own function. """ gh_src_app.build(force_all=True) caught_exceptions: List[BaseException] = [] for page in pages_to_check: if isinstance(page, str): page = pytest.param(page, id=page) pagename: str = page.values[0] # type: ignore[assignment] page_id: str = page.id or pagename for mark in page.marks: if mark.kwargs.get("condition", False): if "reason" in mark.kwargs: print(f"Skipping {page_id!r}: {mark.kwargs['reason']}") break else: print(f"Skipping {page_id!r}") break else: print(f"Checking output for {page_id}") page_id = page_id.replace('.', '_').replace('-', '_') content = (PathPlus(gh_src_app.outdir) / pagename).read_text() soup = BeautifulSoup(content, "html5lib") for meta in cast(List[Dict], soup.find_all("meta")): if meta.get("content", '') == "width=device-width, initial-scale=0.9, maximum-scale=0.9": meta.extract() # type: ignore[attr-defined] try: html_regression.check( soup, extension=f"_{page_id}_.html", jinja2=True, ) except BaseException as e: caught_exceptions.append(e) continue print(caught_exceptions) for exception in caught_exceptions: raise exception sphinx-toolbox-3.9.0/tests/test_issues_output/test_source_output_/000077500000000000000000000000001475757360300260135ustar00rootroot00000000000000test_html_output_autoprotocol_html_.html000066400000000000000000000401671475757360300362520ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_issues_output/test_source_output_{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} {% set tco_annotation=('autoprotocol_demo.T_co' if python_version >= (3, 7) and sphinx_version < (5, 0) else 'T_co') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Autoprotocol β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Autoprotocol ΒΆ

protocol <{{ sig_prename_tag }} class="sig-prename descclassname"> autoprotocol_demo. <{{ sig_prename_tag }} class="sig-name descname"> Frobnicater [source] ΒΆ

typing.Protocol .

This protocol is runtime checkable .

Classes that implement this protocol must have the following methods / attributes:

<{{ sig_prename_tag }} class="sig-name descname"> frobnicate ( something ) {%- if sphinx_version >= (4, 1) %} β†’ Any {% else %} β†’ Any {% endif -%} [source] ΒΆ
protocol <{{ sig_prename_tag }} class="sig-prename descclassname"> autoprotocol_demo. <{{ sig_prename_tag }} class="sig-name descname"> HasLessThan [source] ΒΆ

typing.Protocol for classes that support the < operator.

Classes that implement this protocol must have the following methods / attributes:

<{{ sig_prename_tag }} class="sig-name descname"> __lt__ ( other ) {%- if sphinx_version >= (4, 1) %} β†’ bool {% else %} β†’ bool {% endif -%} [source] ΒΆ

Return self < other .

protocol <{{ sig_prename_tag }} class="sig-prename descclassname"> autoprotocol_demo. <{{ sig_prename_tag }} class="sig-name descname"> HasGreaterThan [source] ΒΆ

typing.Protocol .

Classes that implement this protocol must have the following methods / attributes:

<{{ sig_prename_tag }} class="sig-name descname"> __gt__ ( other ) {%- if sphinx_version >= (4, 1) %} β†’ bool {% else %} β†’ bool {% endif -%} [source] ΒΆ

Return self > other .

protocol <{{ sig_prename_tag }} class="sig-prename descclassname"> autoprotocol_demo. <{{ sig_prename_tag }} class="sig-name descname"> HasGreaterThan [source]

Bases: typing.Protocol

Classes that implement this protocol must have the following methods / attributes:

<{{ sig_prename_tag }} class="sig-name descname"> __gt__ ( other ) {%- if sphinx_version >= (4, 1) %} β†’ bool {% else %} β†’ bool {% endif -%} [source]

Return self > other .

protocol <{{ sig_prename_tag }} class="sig-prename descclassname"> autoprotocol_demo. <{{ sig_prename_tag }} class="sig-name descname"> SupportsAbs [source] ΒΆ

Bases: typing.Protocol [ +T_co ]

An ABC with one abstract method __abs__ that is covariant in its return type.

This protocol is runtime checkable .

Classes that implement this protocol must have the following methods / attributes:

abstract{% if sphinx_version >= (8, 2) %}method{% endif %} <{{ sig_prename_tag }} class="sig-name descname"> __abs__ ( ) {%- if sphinx_version >= (4, 1) %} β†’ {{ tco_annotation }} {% elif sphinx_version >= (4, 0) %} β†’ {{ tco_annotation }} {% else %} β†’ T_co {% endif -%} [source] ΒΆ
{% if sphinx_version >= (8, 1) %}
{% endif %}
test_html_output_generic_bases_.html000066400000000000000000000161771475757360300352710ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_issues_output/test_source_output_{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Generic Bases β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Generic Bases ΒΆ

class <{{ sig_prename_tag }} class="sig-prename descclassname"> sphinx_toolbox.more_autodoc.generic_bases. <{{ sig_prename_tag }} class="sig-name descname"> Example ( iterable = () ) [source] ΒΆ

Bases: typing.List [ typing.Tuple [ str , float , typing.List [ str ]]]

An example of sphinx_toolbox.more_autodoc.generic_bases .

class <{{ sig_prename_tag }} class="sig-prename descclassname"> sphinx_toolbox.more_autodoc.generic_bases. <{{ sig_prename_tag }} class="sig-name descname"> Example2 ( iterable = () ) [source] ΒΆ

Bases: sphinx_toolbox.more_autodoc.generic_bases.Example

An example of sphinx_toolbox.more_autodoc.generic_bases .

This one does not directly subclass a Generic.

{% if sphinx_version >= (8, 1) %}
{% endif %}
test_html_output_generic_bases_37_.html000066400000000000000000000160521475757360300355720ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_issues_output/test_source_output_{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Generic Bases β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Generic Bases ΒΆ

class <{{ sig_prename_tag }} class="sig-prename descclassname"> sphinx_toolbox.more_autodoc.generic_bases. <{{ sig_prename_tag }} class="sig-name descname"> Example ( iterable = () ) [source] ΒΆ

Bases: typing.List [ typing.Tuple [ str , float , typing.List [ str ]]]

An example of sphinx_toolbox.more_autodoc.generic_bases .

class <{{ sig_prename_tag }} class="sig-prename descclassname"> sphinx_toolbox.more_autodoc.generic_bases. <{{ sig_prename_tag }} class="sig-name descname"> Example2 ( iterable = () ) [source] ΒΆ

Bases: sphinx_toolbox.more_autodoc.generic_bases.Example

An example of sphinx_toolbox.more_autodoc.generic_bases .

This one does not directly subclass a Generic.

test_output_github_index_html_.html000066400000000000000000000120341475757360300351350ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_issues_output/test_source_output_{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - GitHub Issues β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - GitHub Issues ΒΆ

#1

#2

#2

pytest-dev/pytest#7680

pytest-dev/pytest#7671

sphinx_toolbox/source.py

sphinx_toolbox/more_autodoc/__init__.py

Issue with code in its title: sphinx-doc/sphinx#10241 .

Issue with code in the beginning of the title: sphinx-doc/sphinx#9575 .

Issue with HTML entities in title: sphinx-toolbox/toctree_plus#56 .

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_latex.py000066400000000000000000000053141475757360300204340ustar00rootroot00000000000000# 3rd party from docutils import nodes from sphinx import addnodes from sphinx.events import EventListener from sphinx.writers.latex import LaTeXTranslator # this package import sphinx_toolbox from sphinx_toolbox import latex from sphinx_toolbox.latex import layout, succinct_seealso, toc from sphinx_toolbox.testing import run_setup def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(latex.setup) assert app.events.listeners == { "config-inited": [EventListener(id=0, handler=latex.configure, priority=500)], } assert directives == { "samepage": latex.SamepageDirective, "clearpage": latex.ClearPageDirective, "cleardoublepage": latex.ClearDoublePageDirective, } assert app.registry.source_parsers == {} assert app.registry.translation_handlers["latex"]["footnote"] == (latex.visit_footnote, latex.depart_footnote) assert app.registry.css_files == [] assert app.registry.js_files == [] def test_setup_succinct_seealso(): setup_ret, directives, roles, additional_nodes, app = run_setup(succinct_seealso.setup) assert additional_nodes == {addnodes.seealso} assert app.events.listeners == {} assert directives == {} assert app.registry.source_parsers == {} assert app.registry.translation_handlers["latex"]["seealso"] == ( succinct_seealso.visit_seealso, succinct_seealso.depart_seealso, ) assert app.registry.css_files == [] assert app.registry.js_files == [] def test_setup_toc(): setup_ret, directives, roles, additional_nodes, app = run_setup(toc.setup) assert setup_ret == {"version": sphinx_toolbox.__version__, "parallel_read_safe": True} assert additional_nodes == set() assert app.registry.translation_handlers == {} assert app.events.listeners == { "env-get-outdated": [EventListener(id=0, handler=toc.purge_outdated, priority=500)], "config-inited": [EventListener(id=1, handler=toc.configure, priority=500)], } assert directives == {"toctree": toc.LatexTocTreeDirective} assert app.registry.translators["latex"] == toc.LaTeXTranslator def test_setup_layout(): setup_ret, directives, roles, additional_nodes, app = run_setup(layout.setup) assert setup_ret == {"version": sphinx_toolbox.__version__, "parallel_read_safe": True} assert additional_nodes == {addnodes.desc, nodes.field_list, nodes.paragraph} assert app.registry.translation_handlers == { "latex": { "desc": (layout.visit_desc, LaTeXTranslator.depart_desc), "field_list": (layout.visit_field_list, layout.depart_field_list), "paragraph": (layout.visit_paragraph, LaTeXTranslator.depart_paragraph) } } assert app.events.listeners == { "config-inited": [EventListener(id=0, handler=layout.configure, priority=500)], } assert directives == {} sphinx-toolbox-3.9.0/tests/test_main.py000066400000000000000000000001211475757360300202320ustar00rootroot00000000000000def test_import(): # this package import sphinx_toolbox.__main__ # noqa: F401 sphinx-toolbox-3.9.0/tests/test_more_autodoc/000077500000000000000000000000001475757360300214225ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/__init__.py000066400000000000000000000000001475757360300235210ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_augment_defaults.py000066400000000000000000000017541475757360300263710ustar00rootroot00000000000000# 3rd party import pytest from sphinx.errors import ExtensionError # this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import augment_defaults from sphinx_toolbox.testing import Sphinx, run_setup def test_setup(): try: Sphinx.extensions = [] # type: ignore[attr-defined] setup_ret, directives, roles, additional_nodes, app = run_setup(augment_defaults.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == {} assert roles == {} assert additional_nodes == set() finally: del Sphinx.extensions # type: ignore[attr-defined] def test_setup_wrong_order(): try: Sphinx.extensions = ["sphinx.ext.autodoc"] # type: ignore[attr-defined] with pytest.raises( ExtensionError, match="'sphinx_toolbox.more_autodoc.augment_defaults' " "must be loaded before 'sphinx.ext.autodoc'.", ): run_setup(augment_defaults.setup) finally: del Sphinx.extensions # type: ignore[attr-defined] sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_autodoc_typehints.py000066400000000000000000000234011475757360300266000ustar00rootroot00000000000000# Based on https://github.com/agronholm/sphinx-autodoc-typehints # Copyright (c) Alex GrΓΆnholm # MIT Licensed # # 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 re import sys import typing from types import ModuleType from typing import ( IO, Any, AnyStr, Callable, Dict, Generic, List, Mapping, NewType, Optional, Pattern, Tuple, Type, TypeVar, Union ) # 3rd party import pytest import typing_extensions from coincidence import min_version from sphobjinv import Inventory # type: ignore[import-untyped] # this package from sphinx_toolbox.more_autodoc.typehints import format_annotation, process_docstring T = TypeVar('T') U = TypeVar('U', covariant=True) V = TypeVar('V', contravariant=True) W = NewType('W', str) class A: def get_type(self): # noqa: MAN002 return type(self) class Inner: pass class B(Generic[T]): # This is set to make sure the correct class name ("B") is picked up name = "Foo" class C(B[str]): pass class D(typing_extensions.Protocol): pass class E(typing_extensions.Protocol[T]): # type: ignore[misc] pass class Slotted: __slots__ = () class Metaclass(type): pass if sys.version_info >= (3, 10): union_str_bool = str | bool union_str_any = str | Any else: union_str_bool = NotImplemented union_str_any = NotImplemented @pytest.mark.parametrize( "annotation, expected_result", [ (str, ":py:class:`str`"), (int, ":py:class:`int`"), (type(None), ":py:obj:`None`"), (type, ":py:class:`type`"), (Type, ":py:class:`~typing.Type`"), (Type[A], ":py:class:`~typing.Type`\\[:py:class:`~%s.A`]" % __name__), (Any, ":py:data:`~typing.Any`"), (AnyStr, ":py:data:`~typing.AnyStr`"), (Mapping, ":py:class:`~typing.Mapping`"), (Mapping[str, bool], ":py:class:`~typing.Mapping`\\[:py:class:`str`, :py:class:`bool`]"), (Dict, ":py:class:`~typing.Dict`"), (Dict[str, bool], ":py:class:`~typing.Dict`\\[:py:class:`str`, :py:class:`bool`]"), (Tuple, ":py:data:`~typing.Tuple`"), (Tuple[str, bool], ":py:data:`~typing.Tuple`\\[:py:class:`str`, :py:class:`bool`]"), ( Tuple[int, int, int], ":py:data:`~typing.Tuple`\\[:py:class:`int`, :py:class:`int`, :py:class:`int`]" ), (Tuple[str, ...], ":py:data:`~typing.Tuple`\\[:py:class:`str`, ...]"), (Union, ":py:data:`~typing.Union`"), (Union[str, bool], ":py:data:`~typing.Union`\\[:py:class:`str`, :py:class:`bool`]"), (Union[str, Any], ":py:data:`~typing.Union`\\[:py:class:`str`, :py:data:`~typing.Any`]"), pytest.param( union_str_bool, ":py:data:`~typing.Union`\\[:py:class:`str`, :py:class:`bool`]", marks=min_version("3.10", reason="Introduced in 3.10"), ), pytest.param( union_str_any, ":py:data:`~typing.Union`\\[:py:class:`str`, :py:data:`~typing.Any`]", marks=min_version("3.10", reason="Introduced in 3.10"), ), (Optional[str], ":py:data:`~typing.Optional`\\[:py:class:`str`]"), (Callable, ":py:data:`~typing.Callable`"), (Callable[..., int], ":py:data:`~typing.Callable`\\[..., :py:class:`int`]"), (Callable[[int], int], ":py:data:`~typing.Callable`\\[\\[:py:class:`int`], :py:class:`int`]"), ( Callable[[int, str], bool], ":py:data:`~typing.Callable`\\[\\[:py:class:`int`, :py:class:`str`], :py:class:`bool`]" ), ( Callable[[int, str], None], ":py:data:`~typing.Callable`\\[\\[:py:class:`int`, :py:class:`str`], :py:obj:`None`]" ), (Pattern, ":py:class:`~typing.Pattern`"), (Pattern[str], ":py:class:`~typing.Pattern`\\[:py:class:`str`]"), (IO, ":py:class:`~typing.IO`"), (IO[str], ":py:class:`~typing.IO`\\[:py:class:`str`]"), (Metaclass, ":py:class:`~%s.Metaclass`" % __name__), (A, ":py:class:`~%s.A`" % __name__), (B, ":py:class:`~%s.B`" % __name__), (B[int], ":py:class:`~%s.B`\\[:py:class:`int`]" % __name__), (C, ":py:class:`~%s.C`" % __name__), (D, ":py:class:`~%s.D`" % __name__), (E, ":py:class:`~%s.E`" % __name__), (E[int], ":py:class:`~%s.E`\\[:py:class:`int`]" % __name__), ( W, ( f':py:{"class" if sys.version_info >= (3, 10) else "func"}:' f'`~typing.NewType`\\(:py:data:`~W`, :py:class:`str`)' ) ), ] ) def test_format_annotation(inv: Inventory, annotation: Any, expected_result: str): result = format_annotation(annotation) assert result == expected_result # Test with the "fully_qualified" flag turned on if "typing" in expected_result or __name__ in expected_result: expected_result = expected_result.replace("~typing", "typing") expected_result = expected_result.replace('~' + __name__, __name__) assert format_annotation(annotation, fully_qualified=True) == expected_result # Test for the correct role (class vs data) using the official Sphinx inventory if "typing" in expected_result: m = re.match("^:py:(?Pclass|data|func):`~(?P[^`]+)`", result) assert m, "No match" name = m.group("name") expected_role = next((o.role for o in inv.objects if o.name == name), None) if expected_role: if expected_role == "function": expected_role = "func" assert m.group("role") == expected_role @pytest.mark.parametrize( "annotation, expected_result", [ ( Generic[T], ":py:class:`~typing.Generic`\\[:py:data:`~T `]", ), ( Mapping[T, int], # type: ignore[valid-type] ":py:class:`~typing.Mapping`\\[:py:data:`~T `, " ":py:class:`int`]" ), ( Mapping[str, V], # type: ignore[valid-type] ":py:class:`~typing.Mapping`\\[:py:class:`str`, " ":py:data:`-V `]" ), ( Mapping[T, U], # type: ignore[valid-type] ":py:class:`~typing.Mapping`\\[:py:data:`~T `, " ":py:data:`+U `]" ), ( Dict[T, int], # type: ignore[valid-type] ":py:class:`~typing.Dict`\\[:py:data:`~T `, " ":py:class:`int`]" ), ( Dict[str, V], # type: ignore[valid-type] ":py:class:`~typing.Dict`\\[:py:class:`str`, " ":py:data:`-V `]" ), ( Dict[T, U], # type: ignore[valid-type] ":py:class:`~typing.Dict`\\[:py:data:`~T `, " ":py:data:`+U `]" ), ( Callable[[T], T], ":py:data:`~typing.Callable`\\[\\[:py:data:`~T `], " ":py:data:`~T `]" ), ] ) def test_format_annotation_typevar(inv: Inventory, annotation: Any, expected_result: str): result = format_annotation(annotation) assert result == expected_result # Test with the "fully_qualified" flag turned on if "typing" in expected_result or __name__ in expected_result: expected_result = expected_result.replace("~typing", "typing") expected_result = expected_result.replace('~' + __name__, __name__) assert format_annotation(annotation, fully_qualified=True) == expected_result # Test for the correct role (class vs data) using the official Sphinx inventory if "typing" in expected_result: m = re.match("^:py:(?Pclass|data|func):`~(?P[^`]+)`", result) assert m, "No match" name = m.group("name") expected_role = next((o.role for o in inv.objects if o.name == name), None) if expected_role: if expected_role == "function": expected_role = "func" assert m.group("role") == expected_role @pytest.mark.parametrize("library", [typing, typing_extensions], ids=["typing", "typing_extensions"]) @pytest.mark.parametrize( "annotation, params, expected_result", [("ClassVar", int, ":py:data:`~typing.ClassVar`\\[:py:class:`int`]"), ("NoReturn", None, ":py:data:`~typing.NoReturn`"), ("Literal", ('a', 1), ":py:data:`~typing.Literal`\\[``'a'``, ``1``]"), ("Type", None, ":py:class:`~typing.Type`"), ("Type", (A, ), ":py:class:`~typing.Type`\\[:py:class:`~%s.A`]" % __name__)] ) def test_format_annotation_both_libs( inv: Inventory, library: ModuleType, annotation: str, params: Any, expected_result: str, ): try: annotation_cls = getattr(library, annotation) except AttributeError: pytest.skip(f"{annotation} not available in the {library.__name__} module") ann = annotation_cls if params is None else annotation_cls[params] result = format_annotation(ann) assert result == expected_result def test_process_docstring_slot_wrapper(): lines: List[str] = [] process_docstring(None, "class", "SlotWrapper", Slotted, None, lines) # type: ignore[arg-type] assert not lines sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_autonamedtuple.py000066400000000000000000000014261475757360300260650ustar00rootroot00000000000000# 3rd party from sphinx.ext.autodoc.directive import AutodocDirective # this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import autonamedtuple from sphinx_toolbox.testing import run_setup def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(autonamedtuple.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == {"autonamedtuple": AutodocDirective} assert roles == {} assert additional_nodes == set() assert "namedtuple" in app.registry.domains["py"].object_types assert "namedtuple" in app.registry.domain_directives["py"] assert "namedtuple" in app.registry.domain_roles["py"] assert app.registry.documenters["namedtuple"] == autonamedtuple.NamedTupleDocumenter sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_autoprotocol.py000066400000000000000000000014041475757360300255640ustar00rootroot00000000000000# 3rd party from sphinx.ext.autodoc.directive import AutodocDirective # this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import autoprotocol from sphinx_toolbox.testing import run_setup def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(autoprotocol.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == {"autoprotocol": AutodocDirective} assert roles == {} assert additional_nodes == set() assert "protocol" in app.registry.domains["py"].object_types assert "protocol" in app.registry.domain_directives["py"] assert "protocol" in app.registry.domain_roles["py"] assert app.registry.documenters["protocol"] == autoprotocol.ProtocolDocumenter sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_autotypeddict.py000066400000000000000000000014151475757360300257160ustar00rootroot00000000000000# 3rd party from sphinx.ext.autodoc.directive import AutodocDirective # this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import autotypeddict from sphinx_toolbox.testing import run_setup def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(autotypeddict.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == {"autotypeddict": AutodocDirective} assert roles == {} assert additional_nodes == set() assert "typeddict" in app.registry.domains["py"].object_types assert "typeddict" in app.registry.domain_directives["py"] assert "typeddict" in app.registry.domain_roles["py"] assert app.registry.documenters["typeddict"] == autotypeddict.TypedDictDocumenter sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_generic_bases.py000066400000000000000000000011321475757360300256210ustar00rootroot00000000000000# 3rd party from sphinx.ext.autodoc.directive import AutodocDirective # this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import generic_bases from sphinx_toolbox.testing import run_setup def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(generic_bases.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == {"autoclass": AutodocDirective} assert roles == {} assert additional_nodes == set() assert app.registry.documenters["class"] == generic_bases.GenericBasesClassDocumenter sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_genericalias.py000066400000000000000000000011461475757360300254630ustar00rootroot00000000000000# 3rd party from sphinx.ext.autodoc.directive import AutodocDirective # this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import genericalias from sphinx_toolbox.testing import run_setup def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(genericalias.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == {"autogenericalias": AutodocDirective} assert roles == {} assert additional_nodes == set() assert app.registry.documenters["genericalias"] == genericalias.PrettyGenericAliasDocumenter sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_no_docstring.py000066400000000000000000000006341475757360300255260ustar00rootroot00000000000000# this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import no_docstring from sphinx_toolbox.testing import run_setup def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(no_docstring.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == {} assert roles == {} assert additional_nodes == set() sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_overloads.py000066400000000000000000000017041475757360300250330ustar00rootroot00000000000000# 3rd party from sphinx.ext.autodoc.directive import AutodocDirective # this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import overloads from sphinx_toolbox.testing import run_setup from tests.common import get_app_config_values def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(overloads.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == {"autofunction": AutodocDirective, "automethod": AutodocDirective} assert roles == {} assert additional_nodes == set() assert app.registry.documenters["function"] == overloads.FunctionDocumenter assert app.registry.documenters["method"] == overloads.MethodDocumenter config_values = get_app_config_values(app.config.values["overloads_location"]) assert config_values[:2] == ("signature", "env") assert sorted(config_values[2].candidates) == [ "bottom", "signature", "top", ] sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex.py000066400000000000000000000126341475757360300241530ustar00rootroot00000000000000# stdlib import re # 3rd party import pytest from coincidence.regressions import AdvancedFileRegressionFixture from domdf_python_tools.paths import PathPlus from sphinx.events import EventListener from sphinx.ext.autodoc.directive import AutodocDirective # this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import regex from sphinx_toolbox.testing import check_asset_copy, run_setup from tests.regex_demo import no_flags, one_flag, two_flags parser = regex.RegexParser() @pytest.mark.parametrize( "regex, expected", [ ( re.compile(r"(?s)(\.\. start installation)(.*?)(\.\. end installation)"), "(.. start installation)(.*?)(.. end installation)", ), ( re.compile(r"[A-Za-z_]\w*"), r"[A-Za-z_]\w*", ), ( re.compile(r"^:(param|parameter|arg|argument)\s*"), r"^:(param|parameter|arg|argument)\s*", ), ( re.compile("^:(default|Default)[ ]"), "^:(default|Default)[ ]", ), ( re.compile("^:(default|Default) "), "^:(default|Default)[ ]", ), ( re.compile(r"\A:(default|Default) "), r"\A:(default|Default)[ ]", ), ( re.compile("^:(default|Default) "), "^:(default|Default) {3}", ), ( re.compile(":(default|Default) $"), ":(default|Default) $", ), ( re.compile(r":(default|Default) \Z"), r":(default|Default) \Z", ), ( re.compile(" :(default|Default)"), "[ ]:(default|Default)", ), ( re.compile(" :(default|Default)"), "[ ]{3}:(default|Default)", ), ( re.compile("[ ]:(default|Default)"), "[ ]:(default|Default)", ), ( re.compile("hello (world)?"), "hello (world)?", ), ( re.compile("(hello){1,3} (world)?"), "(hello){1,3} (world)?", ), ( re.compile("(hello){3,3} (world)?"), "(hello){3} (world)?", ), ( re.compile(r"Issue #\d"), "Issue #\\d", ), ( re.compile(r"Hello \w"), "Hello \\w", ), ( re.compile(r"Not a word: \W"), "Not a word: \\W", ), ( re.compile(r"Not whitespace: \S"), "Not whitespace: \\S", ), ( re.compile(r"Not a number: \D"), "Not a number: \\D", ), (no_flags, no_flags.pattern.replace("\\?", '?')), (one_flag, one_flag.pattern.replace("\\?", '?')), (two_flags, "Hello\\s+[Ww]orld[.,](Lovely|Horrible) weather, isn't it(.*)?"), ] ) def test_regex_parser(regex: re.Pattern, expected: str): assert parser.parse_pattern(regex) == expected terminal_parser = regex.TerminalRegexParser() @pytest.mark.parametrize( "regex", [ re.compile(r"(?s)(\.\. start installation)(.*?)(\.\. end installation)"), re.compile(r"[A-Za-z_]\w*"), re.compile(r"^:(param|parameter|arg|argument)\s*"), pytest.param(re.compile("^:(default|Default)[ ]"), id="default_square_brackets"), re.compile("^:(default|Default) "), re.compile(r"\A:(default|Default) "), pytest.param(re.compile("^:(default|Default) "), id="default_spaces"), re.compile(":(default|Default) $"), re.compile(r":(default|Default) \Z"), re.compile(" :(default|Default)"), pytest.param(re.compile(" :(default|Default)"), id="default_leading_spaces"), re.compile("[ ]:(default|Default)"), re.compile("hello (world)?"), re.compile("(hello){1,3} (world)?"), re.compile("(hello){3,3} (world)?"), re.compile(r"Issue #\d"), re.compile(r"Hello \w"), re.compile(r"Not a word: \W"), re.compile(r"Not whitespace: \S"), re.compile(r"Not a number: \D"), no_flags, one_flag, two_flags, ] ) def test_terminal_regex_parser(regex: re.Pattern, advanced_file_regression: AdvancedFileRegressionFixture): advanced_file_regression.check(terminal_parser.parse_pattern(regex)) def test_copy_asset_files(tmp_pathplus: PathPlus, advanced_file_regression: AdvancedFileRegressionFixture): check_asset_copy( regex.copy_asset_files, "_static/regex.css", file_regression=advanced_file_regression, ) @pytest.mark.parametrize( "flags, expected", [ pytest.param(re.ASCII, ":py:data:`re.ASCII`", id="re.ASCII"), pytest.param(re.DEBUG, ":py:data:`re.DEBUG`", id="re.DEBUG"), pytest.param(re.IGNORECASE, ":py:data:`re.IGNORECASE`", id="re.IGNORECASE"), pytest.param(re.LOCALE, ":py:data:`re.LOCALE`", id="re.LOCALE"), pytest.param(re.MULTILINE, ":py:data:`re.MULTILINE`", id="re.MULTILINE"), pytest.param(re.DOTALL, ":py:data:`re.DOTALL`", id="re.DOTALL"), pytest.param(re.VERBOSE, ":py:data:`re.VERBOSE`", id="re.VERBOSE"), pytest.param( re.VERBOSE | re.IGNORECASE, ":py:data:`re.IGNORECASE` ``|`` :py:data:`re.VERBOSE`", id="re.VERBOSE|re.IGNORECASE" ), ] ) def test_parse_regex_flags(flags: int, expected: str): assert regex.parse_regex_flags(flags) == expected def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(regex.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == {"autoregex": AutodocDirective} assert "regex" in roles assert isinstance(roles["regex"], regex.Regex) assert additional_nodes == {regex.RegexNode} assert app.registry.documenters["regex"] == regex.RegexDocumenter assert app.events.listeners == {"config-inited": [EventListener(0, regex.configure, 500)]} assert app.registry.css_files == [] sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_/000077500000000000000000000000001475757360300237325ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_/test_copy_asset_files_regex.css000066400000000000000000000005551475757360300322350ustar00rootroot00000000000000span.regex_literal{color:dimgrey}span.regex_at{color:orangered}span.regex_repeat_brace{color:orangered}span.regex_branch{color:orangered}span.regex_subpattern{color:dodgerblue}span.regex_in{color:darkorange}span.regex_category{color:darkseagreen}span.regex_repeat{color:orangered}span.regex_any{color:orangered}code.regex{font-size:80%}span.regex{font-weight:bold} 2a6e51d80151b6b9d23ee5244af80b1185461965.paxheader00006660000000000000000000000256147575736030020231xustar00rootroot00000000000000174 path=sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_/test_terminal_regex_parser_Hello___s___Ww_orld_______Lovely_Horrible____weather____isn_t___it__________.txt 2a6e51d80151b6b9d23ee5244af80b1185461965.data000066400000000000000000000012131475757360300170610ustar00rootroot00000000000000Hello\s+[Ww]orld[.,](Lovely|Horrible) weather, isn't it(.*)? sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_/test_terminal_regex_parser_Hello___w_.txt000066400000000000000000000001171475757360300342200ustar00rootroot00000000000000Hello \w test_terminal_regex_parser_Hello__s__Ww_orld_____Lovely_Horrible__weather__isn_t_it________0_.txt000066400000000000000000000012261475757360300471650ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_Hello\s+[Ww]orld[.,](Lovely|Horrible) weather, isn't it (.*)? test_terminal_regex_parser_Hello__s__Ww_orld_____Lovely_Horrible__weather__isn_t_it________1_.txt000066400000000000000000000012261475757360300471660ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_Hello\s+[Ww]orld[.,](Lovely|Horrible) weather, isn't it (.*)? sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_/test_terminal_regex_parser_Issue____d_.txt000066400000000000000000000001321475757360300343560ustar00rootroot00000000000000Issue #\d test_terminal_regex_parser_Not_a_number____D_.txt000066400000000000000000000002471475757360300355660ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_Not a number: \D test_terminal_regex_parser_Not_a_word____W_.txt000066400000000000000000000002211475757360300352640ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_Not a word: \W test_terminal_regex_parser_Not_whitespace____S_.txt000066400000000000000000000002751475757360300361520ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_Not whitespace: \S test_terminal_regex_parser__A_Za_z____w__.txt000066400000000000000000000001731475757360300347000ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_[A-Za-z_]\w* test_terminal_regex_parser___A__default_Default___.txt000066400000000000000000000003641475757360300365410ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_\A:(default|Default)[ ] test_terminal_regex_parser______default_Default__.txt000066400000000000000000000003501475757360300364330ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_[ ]:(default|Default) test_terminal_regex_parser____default_Default__.txt000066400000000000000000000003501475757360300361350ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_[ ]:(default|Default) test_terminal_regex_parser____default_Default___.txt000066400000000000000000000003631475757360300363000ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_^:(default|Default)[ ] test_terminal_regex_parser____param_parameter_arg_argument___s__.txt000066400000000000000000000005701475757360300415640ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_^:(param|parameter|arg|argument)\s* test_terminal_regex_parser___default_Default______.txt000066400000000000000000000003631475757360300365760ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_:(default|Default)   $ test_terminal_regex_parser___default_Default______Z_.txt000066400000000000000000000003641475757360300370700ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_:(default|Default)   \Z test_terminal_regex_parser___s_________start_installation______________end_installation__.txt000066400000000000000000000010341475757360300464700ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_(.. start installation)(.*?)(.. end installation) test_terminal_regex_parser__hello__1_3___world___.txt000066400000000000000000000003501475757360300363170ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_(hello){1,3} (world)? test_terminal_regex_parser__hello__3_3___world___.txt000066400000000000000000000003221475757360300363200ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_(hello){3} (world)? test_terminal_regex_parser_default_leading_spaces_.txt000066400000000000000000000004111475757360300367140ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_[ ]{3}:(default|Default) test_terminal_regex_parser_default_spaces_.txt000066400000000000000000000003761475757360300352430ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_^:(default|Default) {3} test_terminal_regex_parser_default_square_brackets_.txt000066400000000000000000000003631475757360300371370ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_^:(default|Default)[ ] test_terminal_regex_parser_hello__world___.txt000066400000000000000000000002331475757360300352200ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_regex_hello (world)? sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_setup.py000066400000000000000000000007141475757360300241750ustar00rootroot00000000000000# this package import sphinx_toolbox from sphinx_toolbox import more_autodoc from sphinx_toolbox.testing import run_setup def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(more_autodoc.setup) assert setup_ret == {"version": sphinx_toolbox.__version__, "parallel_read_safe": True} assert additional_nodes == set() assert app.registry.translation_handlers == {} assert app.events.listeners == {} assert directives == {} sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_source_link.py000066400000000000000000000026021475757360300253500ustar00rootroot00000000000000# 3rd party from autodocsumm import AutoSummModuleDocumenter # type: ignore[import-untyped] from sphinx.events import EventListener from sphinx.ext.autodoc import ModuleDocumenter # this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import sourcelink from sphinx_toolbox.more_autosummary import PatchedAutoSummModuleDocumenter from sphinx_toolbox.testing import run_setup from sphinx_toolbox.utils import flag from tests.common import get_app_config_values def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(sourcelink.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert "sourcelink" in ModuleDocumenter.option_spec assert ModuleDocumenter.option_spec["sourcelink"] is flag assert "sourcelink" in AutoSummModuleDocumenter.option_spec assert AutoSummModuleDocumenter.option_spec["sourcelink"] is flag assert "sourcelink" in PatchedAutoSummModuleDocumenter.option_spec assert PatchedAutoSummModuleDocumenter.option_spec["sourcelink"] is flag assert app.events.listeners == { "autodoc-process-docstring": [ EventListener(id=0, handler=sourcelink.sourcelinks_process_docstring, priority=500), ], } assert get_app_config_values(app.config.values["autodoc_show_sourcelink"]) == (False, "env", [bool]) assert directives == {} assert roles == {} assert additional_nodes == set() sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_sourcelink.py000066400000000000000000000014201475757360300252060ustar00rootroot00000000000000# 3rd party from sphinx.events import EventListener # this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import sourcelink from sphinx_toolbox.testing import run_setup from tests.common import get_app_config_values def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(sourcelink.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == {} assert roles == {} assert additional_nodes == set() _listener = [EventListener(id=0, handler=sourcelink.sourcelinks_process_docstring, priority=500)] assert app.events.listeners == {"autodoc-process-docstring": _listener} assert get_app_config_values(app.config.values["autodoc_show_sourcelink"]) == (False, "env", [bool]) sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_typehints.py000066400000000000000000000134311475757360300250640ustar00rootroot00000000000000# stdlib import ast import io import itertools import re import sys import types import typing from email.headerregistry import Address from tempfile import TemporaryDirectory from typing import Any, List # 3rd party import pytest from coincidence.selectors import max_version, min_version, not_pypy, only_pypy from domdf_python_tools.typing import ( ClassMethodDescriptorType, MethodDescriptorType, MethodWrapperType, WrapperDescriptorType ) from sphinx.errors import ExtensionError from typing_extensions import Literal, Protocol # this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import typehints from sphinx_toolbox.testing import Sphinx, run_setup from tests.common import get_app_config_values if sys.version_info >= (3, 10): UnionType = types.UnionType else: UnionType = None @pytest.mark.parametrize( "annotation, expected", [ pytest.param(True, ":py:obj:`True`", id="True"), pytest.param(False, ":py:obj:`False`", id="False"), pytest.param(None, ":py:obj:`None`", id="None"), pytest.param(type(None), ":py:obj:`None`", id="NoneType"), pytest.param(Ellipsis, "...", id="Ellipsis"), pytest.param(..., "...", id="..."), pytest.param(itertools.cycle, ":func:`itertools.cycle`", id="itertools.cycle"), pytest.param( types.GetSetDescriptorType, ":py:data:`types.GetSetDescriptorType`", id="types.GetSetDescriptorType" ), pytest.param( types.MemberDescriptorType, ":py:data:`types.MemberDescriptorType`", id="types.MemberDescriptorType" ), pytest.param( ClassMethodDescriptorType, ":py:data:`types.ClassMethodDescriptorType`", id="types.ClassMethodDescriptorType" ), pytest.param( typing.ContextManager[str], r":py:class:`contextlib.AbstractContextManager`\[:py:class:`str`]", id="typing.ContextManager", marks=max_version("3.9"), ), pytest.param( MethodDescriptorType, ":py:data:`types.MethodDescriptorType`", id="types.MethodDescriptorType", marks=not_pypy("PyPy reuses some types"), ), pytest.param( MethodDescriptorType, ":py:data:`types.FunctionType`", id="types.MethodDescriptorType", marks=only_pypy("PyPy reuses some types"), ), pytest.param( MethodWrapperType, ":py:data:`types.MethodWrapperType`", id="types.MethodWrapperType", marks=not_pypy("PyPy reuses some types"), ), pytest.param( MethodWrapperType, ":py:data:`types.MethodType`", id="types.MethodWrapperType", marks=only_pypy("PyPy reuses some types"), ), pytest.param( WrapperDescriptorType, ":py:data:`types.WrapperDescriptorType`", id="types.WrapperDescriptorType", marks=not_pypy("PyPy reuses some types") ), pytest.param( WrapperDescriptorType, ":py:data:`types.FunctionType`", id="types.WrapperDescriptorType", marks=only_pypy("PyPy reuses some types") ), pytest.param( types.BuiltinFunctionType, ":py:data:`types.BuiltinFunctionType`", id="types.BuiltinFunctionType" ), pytest.param(types.FunctionType, ":py:data:`types.FunctionType`", id="types.FunctionType"), pytest.param( types.MethodType, ":py:data:`types.MethodType`", id="types.MethodType", ), pytest.param( types.MappingProxyType, ":py:class:`types.MappingProxyType`", id="types.MappingProxyType" ), pytest.param(types.ModuleType, ":py:class:`types.ModuleType`", id="types.ModuleType"), pytest.param(type(re.compile('')), ":py:class:`typing.Pattern`", id="regex"), pytest.param(List, ":py:class:`typing.List`", id="typing.List"), pytest.param(Protocol, ":py:class:`typing.Protocol`", id="typing_extensions.Protocol"), pytest.param( Address, ":py:class:`email.headerregistry.Address`", id="email.headerregistry.Address" ), pytest.param(io.StringIO, ":py:class:`io.StringIO`", id="io.StringIO"), pytest.param(ast.AST, ":py:class:`ast.AST`", id="ast.AST"), pytest.param( TemporaryDirectory, ":py:obj:`tempfile.TemporaryDirectory`", id="tempfile.TemporaryDirectory" ), pytest.param(Literal[True], r":py:data:`typing.Literal`\[:py:obj:`True`]", id="Literal_True"), pytest.param(Literal[False], r":py:data:`typing.Literal`\[:py:obj:`False`]", id="Literal_False"), pytest.param( Literal[True, "Hello"], r":py:data:`typing.Literal`\[:py:obj:`True`, ``'Hello'``]", id="Literal_True_String" ), pytest.param( Literal[True, 123], r":py:data:`typing.Literal`\[:py:obj:`True`, ``123``]", id="Literal_True_Int" ), pytest.param( UnionType, ":py:data:`types.UnionType`", id="types.UnionType", marks=min_version("3.10", reason="Introduced in 3.10") ), ] ) def test_format_annotation(annotation: Any, expected: str): assert typehints.format_annotation(annotation, True) == expected def test_setup(): try: Sphinx.extensions = [] # type: ignore[attr-defined] setup_ret, directives, roles, additional_nodes, app = run_setup(typehints.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert get_app_config_values(app.config.values["hide_none_rtype"]) == (False, "env", [bool]) assert directives == {} assert roles == {} assert additional_nodes == set() finally: del Sphinx.extensions # type: ignore[attr-defined] def test_setup_wrong_order(): try: Sphinx.extensions = ["sphinx_autodoc_typehints"] # type: ignore[attr-defined] with pytest.raises( ExtensionError, match="'sphinx_toolbox.more_autodoc.typehints' " "must be loaded before 'sphinx_autodoc_typehints'.", ): run_setup(typehints.setup) finally: del Sphinx.extensions # type: ignore[attr-defined] sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_typevars.py000066400000000000000000000016011475757360300247060ustar00rootroot00000000000000# 3rd party from sphinx.events import EventListener from sphinx.ext.autodoc.directive import AutodocDirective # this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import typevars from sphinx_toolbox.testing import run_setup from tests.common import get_app_config_values def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(typevars.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == {"autotypevar": AutodocDirective} assert roles == {} assert additional_nodes == set() assert app.registry.documenters["typevar"] == typevars.TypeVarDocumenter assert app.events.listeners == { "config-inited": [EventListener(id=0, handler=typevars.validate_config, priority=850)], } assert get_app_config_values(app.config.values["no_unbound_typevars"]) == (True, "env", [bool]) sphinx-toolbox-3.9.0/tests/test_more_autodoc/test_variables.py000066400000000000000000000106331475757360300250060ustar00rootroot00000000000000# stdlib from typing import Any, Dict, List, NamedTuple, Sequence, Type, Union, no_type_check # noqa: F401 # 3rd party from coincidence import PEP_563 from domdf_python_tools.secrets import Secret from sphinx.ext.autodoc.directive import AutodocDirective # this package from sphinx_toolbox import __version__ from sphinx_toolbox.more_autodoc import variables from sphinx_toolbox.more_autodoc.variables import get_variable_type from sphinx_toolbox.testing import run_setup class Foo: a: str b: int c: float d: "float" e: "List" class Bar: a: str b: int c: float d: "float" e: "List" f: "Set" # type: ignore[name-defined] def __init__(self): self.g: Secret = Secret('') self.h: "Secret" = Secret('') class Analyzer(NamedTuple): annotations: Dict[Sequence[str], Any] class Documenter(NamedTuple): parent: Type objpath: List[str] analyzer: Analyzer @no_type_check def test_get_variable_type(): assert get_variable_type(Documenter(Foo, ["Foo", 'a'], Analyzer({}))) == ":py:class:`str`" assert get_variable_type(Documenter(Foo, ["Foo", 'b'], Analyzer({}))) == ":py:class:`int`" assert get_variable_type(Documenter(Foo, ["Foo", 'c'], Analyzer({}))) == ":py:class:`float`" assert get_variable_type(Documenter(Foo, ["Foo", 'd'], Analyzer({}))) == ":py:class:`float`" assert get_variable_type(Documenter(Foo, ["Foo", 'e'], Analyzer({}))) == ":py:class:`~typing.List`" if PEP_563: # On 3.10 with PEP 563 failed forward references break things earlier assert get_variable_type(Documenter(Bar, ["Bar", 'a'], Analyzer({}))) == ":py:obj:`~.str`" assert get_variable_type(Documenter(Bar, ["Bar", 'b'], Analyzer({}))) == ":py:obj:`~.int`" assert get_variable_type(Documenter(Bar, ["Bar", 'c'], Analyzer({}))) == ":py:obj:`~.float`" else: assert get_variable_type(Documenter(Bar, ["Bar", 'a'], Analyzer({}))) == ":py:class:`str`" assert get_variable_type(Documenter(Bar, ["Bar", 'b'], Analyzer({}))) == ":py:class:`int`" assert get_variable_type(Documenter(Bar, ["Bar", 'c'], Analyzer({}))) == ":py:class:`float`" # Failed forward reference throws everything else out of whack assert get_variable_type(Documenter(Bar, ["Bar", 'd'], Analyzer({}))) == ":py:obj:`~.float`" assert get_variable_type(Documenter(Bar, ["Bar", 'e'], Analyzer({}))) == ":py:obj:`~.List`" assert get_variable_type(Documenter(Bar, ["Bar", 'f'], Analyzer({}))) == ":py:obj:`~.Set`" assert get_variable_type(Documenter(Bar, ["Bar", 'g'], Analyzer({}))) == '' # assert get_variable_type(Documenter(Bar, ["Bar", "g"], Analyzer({("Bar", "g"): "Secret"}))) == ":py:class:`~domdf_python_tools.secrets.Secret`" assert get_variable_type( Documenter(Bar, ["Bar", 'h'], Analyzer({("Bar", 'h'): "'Secret'"})) ) == ":py:class:`~domdf_python_tools.secrets.Secret`" assert get_variable_type( Documenter(Bar, ["Bar", 'h'], Analyzer({("Bar", 'h'): '"Secret"'})) ) == ":py:class:`~domdf_python_tools.secrets.Secret`" assert get_variable_type( Documenter(Bar, ["Bar", 'h'], Analyzer({("Bar", 'h'): "Union[str, float, int]"})) ) == ":py:data:`~typing.Union`\\[:py:class:`str`, :py:class:`float`, :py:class:`int`]" assert get_variable_type( Documenter(Bar, ["Bar", 'h'], Analyzer({("Bar", 'h'): "'Union[str, float, int]'"})) ) == ":py:data:`~typing.Union`\\[:py:class:`str`, :py:class:`float`, :py:class:`int`]" assert get_variable_type( Documenter(Bar, ["Bar", 'h'], Analyzer({("Bar", 'h'): '"Union[str, float, int]"'})) ) == ":py:data:`~typing.Union`\\[:py:class:`str`, :py:class:`float`, :py:class:`int`]" assert get_variable_type(Documenter("Bar", ["Bar", 'f'], Analyzer({}))) == '' def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(variables.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == { "autovariable": AutodocDirective, "autoattribute": AutodocDirective, "autoinstanceattribute": AutodocDirective, "autoslotsattribute": AutodocDirective, "autoproperty": AutodocDirective, } assert roles == {} assert additional_nodes == set() assert app.registry.documenters["variable"] == variables.VariableDocumenter assert app.registry.documenters["attribute"] == variables.TypedAttributeDocumenter assert app.registry.documenters["instanceattribute"] == variables.InstanceAttributeDocumenter assert app.registry.documenters["slotsattribute"] == variables.SlotsAttributeDocumenter assert app.registry.documenters["property"] == variables.PropertyDocumenter sphinx-toolbox-3.9.0/tests/test_more_autosummary.py000066400000000000000000000024331475757360300227260ustar00rootroot00000000000000# 3rd party from coincidence.regressions import AdvancedFileRegressionFixture from sphinx.ext.autodoc.directive import AutodocDirective # this package from sphinx_toolbox import __version__, more_autosummary from sphinx_toolbox.testing import run_setup from tests.common import get_app_config_values def test_setup(advanced_file_regression: AdvancedFileRegressionFixture): setup_ret, directives, roles, additional_nodes, app = run_setup(more_autosummary.setup) assert setup_ret == {"parallel_read_safe": True, "version": __version__} assert directives == { "autosummary": more_autosummary.PatchedAutosummary, "autoclass": AutodocDirective, "automodule": AutodocDirective, "autoclasssumm": more_autosummary.PatchedAutoDocSummDirective, "automodulesumm": more_autosummary.PatchedAutoDocSummDirective, } assert app.registry.documenters["module"] == more_autosummary.PatchedAutoSummModuleDocumenter assert app.registry.documenters["class"] == more_autosummary.PatchedAutoSummClassDocumenter assert not roles assert not additional_nodes config_values = get_app_config_values(app.config.values["autodocsumm_member_order"]) assert config_values[:2] == ( "alphabetical", "env", ) assert sorted(config_values[2].candidates) == ["alphabetic", "alphabetical", "bysource"] sphinx-toolbox-3.9.0/tests/test_output/000077500000000000000000000000001475757360300203025ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_output/__init__.py000066400000000000000000000000001475757360300224010ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_output/_autonamedtuple_demo_pep563.py000066400000000000000000000007031475757360300261500ustar00rootroot00000000000000from __future__ import annotations # stdlib from typing import Callable, NamedTuple # Examples from # https://docs.python.org/3/library/typing.html#typing.NamedTuple # https://www.python.org/dev/peps/pep-0589/#totality # https://github.com/python/typing/pull/700 __all__ = ("Animal", ) class Animal(NamedTuple): """ An animal. :param name: The name of the animal. :param voice: The animal's voice. """ name: str voice: Callable[[], str] sphinx-toolbox-3.9.0/tests/test_output/autonamedtuple_demo_pep563.py000066400000000000000000000010671475757360300260150ustar00rootroot00000000000000# Examples from # https://docs.python.org/3/library/typing.html#typing.NamedTuple # https://www.python.org/dev/peps/pep-0589/#totality # https://github.com/python/typing/pull/700 # stdlib import sys __all__ = ("Animal", ) if sys.version_info < (3, 7): # stdlib from typing import Callable, NamedTuple class Animal(NamedTuple): """ An animal. :param name: The name of the animal. :param voice: The animal's voice. """ name: str voice: "Callable[[], str]" else: # this package from tests.test_output._autonamedtuple_demo_pep563 import Animal sphinx-toolbox-3.9.0/tests/test_output/conftest.py000066400000000000000000000106061475757360300225040ustar00rootroot00000000000000# Based on Sphinx # Copyright (c) 2007-2020 by the Sphinx team. # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib import pathlib import shutil from contextlib import contextmanager from typing import Any, Callable, ContextManager, Dict, Iterator, List, Sequence, Tuple, cast # 3rd party import pytest from bs4 import BeautifulSoup from domdf_python_tools.paths import PathPlus, in_directory from sphinx.application import Sphinx from sphinx.testing.fixtures import app as testing_app from sphinx.testing.fixtures import make_app, shared_result, sphinx_test_tempdir, test_params # this package from tests.common import AppParams fixtures = [make_app, shared_result, sphinx_test_tempdir, test_params, testing_app] @pytest.fixture() def pre_commit_hooks(tmp_pathplus: PathPlus) -> None: (tmp_pathplus / ".pre-commit-hooks.yaml").write_text( """ - id: flake2lint name: Flake8 -> PyLint description: Augment Flake8 noqa comments with PyLint comments. entry: flake2lint language: python types_or: [python, pyi] """ ) @pytest.fixture() def pre_commit_flake8_contextmanager( tmp_pathplus: PathPlus, pre_commit_hooks: None, ) -> Callable[[], ContextManager]: @contextmanager def cm() -> Iterator[None]: with pytest.warns(UserWarning, match="(No codes specified|No such code 'F401')"), in_directory(tmp_pathplus): yield return cm @pytest.fixture(scope="session") def rootdir() -> PathPlus: rdir = PathPlus(__file__).parent.absolute() / "doc-test" (rdir / "test-root").maybe_make(parents=True) return PathPlus(rdir) @pytest.fixture() def app_params( request: Any, test_params: Dict, sphinx_test_tempdir: pathlib.Path, rootdir: pathlib.Path, ) -> Tuple[Sequence, Dict]: """ parameters that is specified by 'pytest.mark.sphinx' for sphinx.application.Sphinx initialization """ # ##### process pytest.mark.sphinx markers = request.node.iter_markers("sphinx") pargs = {} kwargs: Dict[str, Any] = {} if markers is not None: # to avoid stacking positional args for info in reversed(list(markers)): for i, a in enumerate(info.args): pargs[i] = a kwargs.update(info.kwargs) args = [pargs[i] for i in sorted(pargs.keys())] # ##### prepare Application params testroot = "root" kwargs["srcdir"] = srcdir = sphinx_test_tempdir / kwargs.get("srcdir", testroot) # special support for sphinx/tests if rootdir and not srcdir.exists(): testroot_path = rootdir / ("test-" + testroot) shutil.copytree(testroot_path, srcdir) return AppParams(args, kwargs) @pytest.fixture() def page(testing_app: Sphinx, request, tmp_pathplus: PathPlus) -> BeautifulSoup: with pytest.warns(UserWarning, match="(No codes specified|No such code 'F401')"), in_directory(tmp_pathplus): testing_app.build(force_all=True) pagename = request.param c = (testing_app.outdir / pagename).read_text(encoding="UTF-8") soup = BeautifulSoup(c, "html5lib") for meta in cast(List[Dict], soup.find_all("meta")): if meta.get("content", '') == "width=device-width, initial-scale=0.9, maximum-scale=0.9": meta.extract() # type: ignore[attr-defined] return soup sphinx-toolbox-3.9.0/tests/test_output/doc-test/000077500000000000000000000000001475757360300220245ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/000077500000000000000000000000001475757360300237645ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/assets.rst000066400000000000000000000004261475757360300260220ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Assets ===================================== :asset:`hello_world.txt` :asset:`See here ` :asset:`Missing file ` :asset:`~hello_world.txt` :asset:`~/foo/bar/baz/missing.txt` sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/assets/000077500000000000000000000000001475757360300252665ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/assets/hello_world.txt000066400000000000000000000000141475757360300303340ustar00rootroot00000000000000Hello World sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/augment-defaults.rst000066400000000000000000000003101475757360300277550ustar00rootroot00000000000000:orphan: ============================== Autodoc Augment Defaults ============================== .. automodule:: tests.augment_defaults_demo :members: :special-members: :exclude-members: excluded sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/autodoc-ellipsis.rst000066400000000000000000000003441475757360300277770ustar00rootroot00000000000000:orphan: ======================================== sphinx-toolbox Demo - Autodoc Ellipsis ======================================== .. autofunction:: tests.demo.ellipsis_function .. autofunction:: tests.demo.ellipsis_function_2 sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/autonamedtuple.rst000066400000000000000000000013441475757360300275470ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - AutoNamedTuple ===================================== .. automodule:: autonamedtuple_demo :members: :exclude-members: Movie .. autonamedtuple:: autonamedtuple_demo.Movie .. autonamedtuple:: autonamedtuple_demo.Foo :show-inheritance: .. autonamedtuple:: autonamedtuple_demo.Traditional :show-inheritance: .. autonamedtuple:: autonamedtuple_demo.NoDocstring :show-inheritance: .. autonamedtuple:: autonamedtuple_demo.NoDocstring :noindex: .. autonamedtuple:: autonamedtuple_demo.CustomisedNew This function takes a single argument, the :namedtuple:`~.Movie` to watch. The name of the movie can be accessed with the :namedtuple-field:`~.Movie.name` attribute. sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/autonamedtuple_pep563.rst000066400000000000000000000003441475757360300306500ustar00rootroot00000000000000:orphan: ================================================ sphinx-toolbox Demo - AutoNamedTuple - PEP 563 ================================================ .. automodule:: tests.test_output.autonamedtuple_demo_pep563 :members: sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/autoprotocol.rst000066400000000000000000000006251475757360300272530ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Autoprotocol ===================================== .. automodule:: autoprotocol_demo :members: :exclude-members: HasGreaterThan .. autoprotocol:: autoprotocol_demo.HasGreaterThan .. autoprotocol:: autoprotocol_demo.HasGreaterThan :noindex: :show-inheritance: .. autoprotocol:: autoprotocol_demo.SupportsAbs :show-inheritance: sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/autotypeddict.rst000066400000000000000000000004001475757360300273720ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - AutoTypedDict ===================================== .. automodule:: autotypeddict_demo :members: :exclude-members: Movie :undoc-members: .. autotypeddict:: autotypeddict_demo.Movie sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/changeset.rst000066400000000000000000000013551475757360300264630ustar00rootroot00000000000000:orphan: ========= Changeset ========= Removed --------- .. versionremoved:: 1.2.3 Use :func:`foo` instead. .. versionremoved:: 1.2.3 Due to an unfixable bug this function has been removed. If you desperately need this functionality please write to the mailing list at python-users@example.org .. versionremoved:: 1.2.3 * Removed :mod:`mozarella` * Removed :func:`parrot` from :mod:`pet_shop` Changed --------- .. versionchanged:: 1.2.3 Moved from :mod:`mozarella` .. versionchanged:: 0.3.0 * Parameters for ``__init__`` can be documented either in the class docstring or alongside the attribute. The class docstring has priority. * Added support for `autodocsumm `_. sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/code-block.rst000066400000000000000000000014411475757360300265200ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Code ===================================== .. code-block:: python def print(text): sys.stdout.write(text) .. code-block:: python :tab-width: 4 def print(text): sys.stdout.write(text) .. code-block:: python :tab-width: 8 def print(text): sys.stdout.write(text) .. code-block:: python :dedent: 4 def print(text): sys.stdout.write(text) .. code-block:: python :linenos: def print(text): sys.stdout.write(text) Code Cell ----------- .. code-cell:: python :execution-count: 1 def print(text): sys.stdout.write(text) print("hello world") .. output-cell:: :execution-count: 1 hello world .. code-cell:: python :execution-count: 2 :tab-width: 8 def print(text): sys.stdout.write(text) sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/collapse.rst000066400000000000000000000005201475757360300263150ustar00rootroot00000000000000:orphan: ========= Collapse ========= .. collapse:: Details Something small enough to escape casual notice. .. collapse:: A Different Label :class: custom-summary :name: summary0 Something else that might escape notice. .. collapse:: Open by default :name: summary3 :open: The text should be visible when the page loads. sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/conf.py000066400000000000000000000046761475757360300253000ustar00rootroot00000000000000# 3rd party from sphinx import addnodes from sphinx.application import Sphinx from sphinx.locale import _ # this package from sphinx_toolbox import latex extensions = [ "sphinx.ext.viewcode", "sphinx.ext.intersphinx", "sphinx_toolbox", "sphinx_toolbox.more_autodoc.augment_defaults", "sphinx_toolbox.more_autodoc.typehints", "sphinx_toolbox.more_autodoc.genericalias", "sphinx_toolbox.more_autodoc.variables", "sphinx_toolbox.more_autodoc.no_docstring", "sphinx_toolbox.more_autodoc.sourcelink", "sphinx_toolbox.more_autodoc.regex", "sphinx_toolbox.more_autodoc.typevars", "sphinx_toolbox.more_autodoc.overloads", "sphinx_toolbox.more_autodoc.generic_bases", "sphinx_toolbox.documentation_summary", "sphinx_toolbox.tweaks.latex_toc", "sphinx_toolbox.tweaks.footnote_symbols", "sphinx_toolbox.tweaks.param_dash", "sphinx_toolbox.flake8", "sphinx_toolbox.pre_commit", "sphinx_tabs.tabs", "sphinx-prompt", "sphinx.ext.autodoc", "sphinx_toolbox.more_autosummary", "sphinx_toolbox.latex.succinct_seealso", "sphinx_toolbox.tweaks.revert_footnote_style", ] github_username = "sphinx-toolbox" github_repository = "sphinx-toolbox" source_link_target = "GitHub" _exclude_members = "__repr__,__weakref__,__dict__,__annotations__,__firstlineno__,__replace__,__static_attributes__" autodoc_default_options = {"exclude-members": _exclude_members} all_typevars = True no_unbound_typevars = False sphinx_tabs_disable_tab_closing = True html_codeblock_linenos_style = "table" overloads_location = "bottom" documentation_summary = " This is an awesome tool! πŸš€ ~ intersphinx_mapping # 100% 'Quotes'" intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)} rst_prolog = ".. |hello| replace:: world" project = "Python" author = "unknown" # These revert https://github.com/sphinx-doc/sphinx/pull/8472 def visit_desc_signature(self, node: addnodes.desc_signature) -> None: # the id is set automatically self.body.append(self.starttag(node, "dt")) def depart_desc_signature(self, node: addnodes.desc_signature) -> None: if not node.get("is_multiline"): self.add_permalink_ref(node, _("Permalink to this definition")) self.body.append('\n') def setup(app: Sphinx) -> None: app.connect("build-finished", latex.replace_unknown_unicode) app.add_node(addnodes.desc_signature, html=(visit_desc_signature, depart_desc_signature), override=True) # TODO: add test matrix with this option enabled # autodoc_typehints = "both" sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/confval.rst000066400000000000000000000014531475757360300261510ustar00rootroot00000000000000:orphan: ========= Confval ========= .. confval:: source_link_target :type: :class:`str` :required: False :default: ``'Sphinx'`` The target of the source link, either ``'GitHub'`` or ``'Sphinx'``. Case insensitive. .. confval:: github_username :type: :class:`str` :required: True .. confval:: github_repository :type: :class:`str` :required: True The GitHub repository this documentation corresponds to. .. confval:: conda_channels :type: Comma-separated list of strings :required: True The conda channels required to install the library from Anaconda. .. confval:: github_repository :type: :class:`str` :noindex: This is a duplicate with ``:noindex:`` set. This is an xref to the :confval:`github_repository` configuration option. .. confval:: something A configuration value. sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/decorators.rst000066400000000000000000000003561475757360300266670ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Decorators ===================================== .. decorator:: my_decorator A decorator. :deco:`my_decorator` :deco:`@my_decorator` :deco:`Title ` sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/documentation-summary-meta.rst000066400000000000000000000003111475757360300320010ustar00rootroot00000000000000:orphan: =================================================== sphinx-toolbox Demo - documentation-summary :meta: =================================================== .. documentation-summary:: :meta: sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/documentation-summary.rst000066400000000000000000000002561475757360300310650ustar00rootroot00000000000000:orphan: ============================================= sphinx-toolbox Demo - documentation-summary ============================================= .. documentation-summary:: sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/example.rst000066400000000000000000000003231475757360300261470ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - reST Example ===================================== .. rest-example:: .. code-block:: python def print(text): sys.stdout.write(text) sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/flake8.rst000066400000000000000000000004521475757360300256710ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Flake8 ===================================== .. flake8-codes:: flake8_dunder_all DALL000 .. flake8-codes:: flake8_dunder_all .. flake8-codes:: flake8_dunder_all F401 .. flake8-codes:: flake8_dunder_all DALL000 F401 sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/footnote_symbols.rst000066400000000000000000000005471475757360300301310ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - footnotes ===================================== Some text [*]_ Some more text [*]_ Sometimes [*]_ strange things happen. .. [*] An auto-symbol footnote .. [*] Another footnote .. [*] A footnote with lots of content. .. code-block:: python print("Look ma! I'm in a footnote") sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/formatting.rst000066400000000000000000000003461475757360300266730ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Formatting ===================================== :iabbr:`m/z (mass to charge ratio)` :iabbr:`doodad` :bold-title:`Examples:` :bold-title:`Other Extensions` sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/generic_bases.rst000066400000000000000000000006361475757360300273140ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Generic Bases ===================================== .. autoclass:: sphinx_toolbox.more_autodoc.generic_bases.Example :show-inheritance: .. autoclass:: sphinx_toolbox.more_autodoc.generic_bases.Example2 :show-inheritance: .. autoclass:: sphinx_toolbox.more_autodoc.generic_bases.FinalExample :show-inheritance: :exclude-members: __init__ sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/genericalias.rst000066400000000000000000000003671475757360300271520ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Genericalias ===================================== .. automodule:: tests.test_output.variables_genericalias_demo :members: :exclude-members: variable,CONSTANT,Demo,SlotsDemo sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/github.rst000066400000000000000000000012171475757360300260010ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - GitHub ===================================== Issues -------- :github:issue:`1` :github:issue:`7680 ` Pull Requests --------------- :github:pull:`2` :github:pull:`7671 ` Repositories --------------- :github:repo:`sphinx-toolbox/sphinx-toolbox` See more in the :github:repo:`pytest repository `. Users --------------- :github:user:`domdfcoding` See more of my :github:user:`repositories `. Orgs ----- :github:org:`sphinx-toolbox` See more repositories in the :github:org:`pytest-dev org `. sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/index.rst000066400000000000000000000010041475757360300256200ustar00rootroot00000000000000 .. only:: html .. toctree:: :hidden: Home .. toctree:: :caption: Documentation :maxdepth: 2 installation collapse confval code-block formatting footnote_symbols latex regex github changeset .. sidebar-links:: :caption: Links :github: :pypi: sphinx-toolbox Contributing Guide regex .. function:: foo(bar, baz) :param bar: Does something :param baz: Does something else .. automodulesumm:: autonamedtuple_demo sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/installation.rst000066400000000000000000000031241475757360300272170ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Installation ===================================== .. installation:: my_project :pypi: .. installation:: my_project :pypi: :pypi-name: my-project .. installation:: :pypi: :pypi-name: my-project .. installation:: my_project :anaconda: .. installation:: my_project :anaconda: :conda-name: my-project .. installation:: :anaconda: :conda-name: my-project .. installation:: my_project :github: .. installation:: :github: .. installation:: my_project :pypi: :anaconda: .. installation:: my_project :pypi: :pypi-name: my-project :anaconda: .. installation:: :pypi: :pypi-name: my-project :anaconda: .. installation:: my_project :pypi: :pypi-name: my-project :anaconda: :conda-name: conda-project .. installation:: :pypi: :pypi-name: my-project :anaconda: :conda-name: conda-project .. installation:: my_project :pypi: :anaconda: :github: .. installation:: my_project :pypi: :pypi-name: my-project :anaconda: :github: .. installation:: :pypi: :pypi-name: my-project :anaconda: :github: .. installation:: my_project :pypi: :pypi-name: my-project :anaconda: :conda-name: conda-project :github: .. installation:: :pypi: :pypi-name: my-project :anaconda: :conda-name: conda-project :github: .. extensions:: my-extension foo bar baz .. extensions:: my-extension :import-name: my_extension foo bar baz .. extensions:: my-extension :import-name: my_extension .. extensions:: my-extension .. extensions:: my-extension :no-preamble: .. extensions:: my-extension :no-postamble: sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/instancevar.rst000066400000000000000000000003011475757360300270250ustar00rootroot00000000000000:orphan: ========================================= sphinx-toolbox Demo - Instance Variables ========================================= .. automodule:: tests.test_output.instancevar :members: sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/latex.rst000066400000000000000000000026721475757360300256420ustar00rootroot00000000000000===================================== sphinx-toolbox Demo - LaTeX ===================================== .. clearpage:: .. samepage:: Content under the samepage directive .. figure:: https://avatars.githubusercontent.com/u/75883961 .. cleardoublepage:: The ​following ​characters ​are ​problematic ​in ​LaTeX: * β™  * β™₯ * ♦ * ♣ * ΞΌ * ≑ .. clearpage:: Hello .. latex:clearpage:: .. latex:samepage:: Content under the samepage directive .. figure:: https://avatars.githubusercontent.com/u/75883961 .. latex:vspace:: -5px .. latex:cleardoublepage:: The ​following ​characters ​are ​problematic ​in ​LaTeX: * β™  * β™₯ * ♦ * ♣ * ΞΌ * ≑ * β‰ˆ This is a ΞΌ-library for doing something. The Greek letter ΞΌ has several uses. .. latex:vspace:: 10px .. latex:clearpage:: .. latex:vspace:: 30mm Goodbyeee! Example Footnotes -------------------- | Hello [1]_ | Goodbye [2]_ | Symbol [*]_ | Another Symbol [*]_ | Number Again [3]_ | Symbol 3 [*]_ | Symbol 4 [*]_ | Symbol 5 [*]_ | Symbol 6 [*]_ | Symbol 7 [*]_ | Symbol 8 [*]_ | Symbol 9 [*]_ .. latex:vspace:: 20px .. [1] One .. [2] Two .. [*] Buckle my shoe .. [*] The second symbol .. [3] The number after the symbol .. [*] Symbol 3 .. [*] Symbol 4 .. [*] Symbol 5 .. [*] Symbol 6 .. [*] Symbol 7 .. [*] Symbol 8 .. [*] Symbol 9 .. seealso:: Check this out on GitHub .. seealso:: These are helpful resources: * Google * StackOverflow * RealPython sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/no_docstring.rst000066400000000000000000000003631475757360300272100ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - :no-docstring: ===================================== .. automodule:: autotypeddict_demo :members: :no-docstring: :exclude-members: OldStyleAnimal,AquaticBird :noindex: sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/overloads.rst000066400000000000000000000003271475757360300265160ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Overloads ===================================== .. automodule:: tests.test_output.overloads_demo :members: :undoc-members: :special-members: sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/pre-commit.rst000066400000000000000000000003661475757360300265770ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - pre-commit ===================================== .. pre-commit:: :rev: v0.0.4 :hooks: some-hook,some-other-hook .. pre-commit:: :rev: v0.0.4 .. pre-commit:flake8:: 0.0.4 sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/regex.rst000066400000000000000000000011721475757360300256310ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Regex ===================================== .. automodule:: sphinx_toolbox.utils :members: untyped_param_regex .. autoregex:: sphinx_toolbox.utils.typed_flag_regex :regex:`^Hello\s+[Ww]orld[.,](Lovely|Horrible) weather, isn't it (.*)\?` .. autoregex:: tests.regex_demo.no_flags .. autoregex:: tests.regex_demo.one_flag .. autoregex:: tests.regex_demo.two_flags .. autoregex:: tests.regex_demo.backticks .. autoregex:: tests.regex_demo.leading_whitespace .. autoregex:: tests.regex_demo.trailing_whitespace .. autoregex:: tests.regex_demo.single_whitespace sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/shields.rst000066400000000000000000000041611475757360300261530ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Shields ===================================== .. rtfd-shield:: :project: sphinx-toolbox :alt: Documentation Build Status .. rtfd-shield:: :project: sphinx-toolbox :version: 3.x .. pre-commit-ci-shield:: :username: sphinx-toolbox :repository: sphinx-toolbox :branch: master .. actions-shield:: :workflow: Docs Check :alt: Docs Check Status .. actions-shield:: :workflow: Docs Check :alt: Docs Check Status :username: octocat :repository: hello_world .. requires-io-shield:: :alt: Requirements Status .. requires-io-shield:: :alt: Requirements Status :username: octocat :repository: hello_world .. coveralls-shield:: :alt: Coverage .. coveralls-shield:: :alt: Coverage :username: octocat :repository: hello_world :branch: dev .. codefactor-shield:: :alt: CodeFactor Grade .. codefactor-shield:: :alt: CodeFactor Grade :username: octocat :repository: hello_world .. pypi-shield:: :version: :alt: PyPI - Package Version .. pypi-shield:: :py-versions: :alt: PyPI - Supported Python Versions .. pypi-shield:: :implementations: :alt: PyPI - Supported Implementations .. pypi-shield:: :wheel: :alt: PyPI - Wheel .. pypi-shield:: :license: :alt: PyPI - License .. pypi-shield:: :downloads: day :alt: PyPI - Downloads .. pypi-shield:: :downloads: week :alt: PyPI - Downloads .. pypi-shield:: :downloads: month :alt: PyPI - Downloads .. pypi-shield:: :project: sphinx :downloads: month :alt: PyPI - Downloads .. github-shield:: :contributors: :alt: Contributors .. github-shield:: :license: :alt: License .. github-shield:: :top-language: :alt: GitHub top language .. github-shield:: :commits-since: v0.0.0 :alt: GitHub commits since tagged version .. github-shield:: :last-commit: :alt: GitHub last commit .. github-shield:: :username: octocat :repository: hello_world :branch: dev :last-commit: :alt: GitHub last commit .. maintained-shield:: 2020 :alt: Maintenance .. pre-commit-shield:: :alt: pre-commit .. maintained-shield:: 2020 .. pre-commit-shield:: .. pre-commit-shield:: :class: pre-commit-shield sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/sourcelink.rst000066400000000000000000000005161475757360300266760ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - :sourcelink: ===================================== .. automodule:: autotypeddict_demo :members: :sourcelink: :exclude-members: OldStyleAnimal,AquaticBird :noindex: .. automodule:: sphinx_toolbox.more_autosummary :sourcelink: :no-members: :no-autosummary: sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/typevars.rst000066400000000000000000000002611475757360300263720ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - TypeVars ===================================== .. automodule:: tests.test_output.typevars_demo :members: sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/variables.rst000066400000000000000000000003471475757360300264720ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Variables ===================================== .. automodule:: tests.test_output.variables_genericalias_demo :members: :exclude-members: PathLike :noindex: sphinx-toolbox-3.9.0/tests/test_output/doc-test/test-root/wikipedia.rst000066400000000000000000000004721475757360300264670ustar00rootroot00000000000000:orphan: ===================================== sphinx-toolbox Demo - Wikipedia ===================================== :wikipedia:`Sphinx` :wikipedia:`mythical creature ` :wikipedia:`:zh:ζ–―θŠ¬ε…‹ζ–―` :wikipedia:`Answer to the Ultimate Question of Life, the Universe, and Everything <:de:42 (Antwort)>` sphinx-toolbox-3.9.0/tests/test_output/instancevar.py000066400000000000000000000013271475757360300231740ustar00rootroot00000000000000# stdlib from typing import Union # 3rd party from apeye.slumber_url import SlumberURL from domdf_python_tools.secrets import Secret class OctoAPI: """ The primary interface to the Octopus Energy API. :param api_key: API key to access the Octopus Energy API. If you are an Octopus Energy customer, you can generate an API key from your `online dashboard `_. """ def __init__(self, api_key: str) -> None: #: The API key to access the Octopus Energy API. self.API_KEY: Union[Secret, str, float] = Secret(api_key) #: The base URL of the Octopus Energy API. self.API_BASE: "SlumberURL" = SlumberURL("https://api.octopus.energy/v1", auth=(self.API_KEY.value, '')) sphinx-toolbox-3.9.0/tests/test_output/overloads_demo.py000066400000000000000000000035711475757360300236640ustar00rootroot00000000000000# stdlib from typing import Callable, List, Optional, Type, Union, overload __all__ = ("serde", "Foo", "Bar") @overload def serde(cls: Type[object], from_key: str = ..., to_key: str = ...) -> Type[object]: ... # pragma: no cover @overload def serde(cls: None = None, from_key: str = ..., to_key: str = ...) -> "Callable[[Type[object]], Type[object]]": ... # pragma: no cover def serde( # type: ignore[empty-body] cls: Optional[Type[object]] = None, from_key: str = "from", to_key: str = "to", ) -> Union[Type[object], Callable[[Type[object]], Type[object]]]: r""" Decorator to add serialisation and deserialisation capabilities to attrs classes. :param cls: The attrs class to add the methods to. :param from_key: :param to_key: :rtype: Classes decorated with :deco:`~attr_utils.serialise.serde` will have two new methods added: .. py:classmethod:: from_dict(d) Construct an instance of the class from a dictionary. :param d: :class:`~typing.Mapping`\[:class:`str`, :py:obj:`~typing.Any`\] .. py:method:: to_dict() -> MutableMapping[str, Any]: Returns a dictionary containing the contents of the class. :rtype: :class:`~typing.MutableMapping`\[:class:`str`, :py:obj:`~typing.Any`\] """ class Foo: @overload def __getitem__(self, item: int) -> str: ... @overload def __getitem__(self, item: slice) -> List[str]: ... def __getitem__(self, item: Union[int, slice]) -> Union[str, List[str]]: # type: ignore[empty-body] """ Return the item with the given index. :param item: :rtype: .. versionadded:: 1.2.3 """ class Bar: @overload def __getitem__(self, item: int) -> str: ... @overload def __getitem__(self, item: slice) -> List[str]: ... def __getitem__(self, item: Union[int, slice]) -> Union[str, List[str]]: # type: ignore[empty-body] """ Return the item with the given index. .. versionadded:: 1.2.3 :param item: """ sphinx-toolbox-3.9.0/tests/test_output/test_output.py000066400000000000000000000255531475757360300232650ustar00rootroot00000000000000# stdlib import sys from pathlib import Path from pprint import pformat from typing import Callable, ContextManager, Dict, List, Optional, cast # 3rd party import docutils import docutils.nodes import pytest import sphinx import sphinx.writers.html5 from _pytest.mark import ParameterSet from bs4 import BeautifulSoup, PageElement, Tag from coincidence.params import param from coincidence.regressions import AdvancedFileRegressionFixture from coincidence.selectors import min_version from docutils import nodes from domdf_python_tools.paths import PathPlus from domdf_python_tools.stringlist import StringList from domdf_python_tools.typing import PathLike from jinja2 import Template from pytest_regressions.common import check_text_files from sphinx.application import Sphinx # this package from sphinx_toolbox.latex import better_header_layout from sphinx_toolbox.testing import ( HTMLRegressionFixture, LaTeXRegressionFixture, remove_html_footer, remove_html_link_tags ) from sphinx_toolbox.utils import Config @pytest.mark.usefixtures("pre_commit_hooks") def test_build_example( testing_app: Sphinx, pre_commit_flake8_contextmanager: Callable[[], ContextManager], ): with pre_commit_flake8_contextmanager(): testing_app.build() testing_app.build() @pytest.mark.usefixtures("docutils_17_compat", "pre_commit_hooks") @pytest.mark.parametrize("page", ["example.html"], indirect=True) def test_example_html_output(page: BeautifulSoup): # Make sure the page title is what you expect h1 = cast(Optional[Tag], page.find("h1")) assert h1 is not None title = cast(str, h1.contents[0]).strip() assert "sphinx-toolbox Demo - reST Example" == title selector_string = "div.body div#sphinx-toolbox-demo-rest-example" body = list(filter(lambda a: a != '\n', page.select(selector_string)[0].contents))[1:] assert len(body) == 3, pformat(body) assert body[0].name == 'p' # type: ignore[attr-defined] assert body[0]["id"] == "example-0" # type: ignore[index] assert body[0].contents == [] # type: ignore[attr-defined] assert body[1].name == "div" # type: ignore[attr-defined] assert body[1]["class"] == ["rest-example", "docutils", "container"] # type: ignore[index] body_body: List[PageElement] = list( filter(lambda a: a != '\n', body[1].contents) # type: ignore[arg-type,attr-defined] ) assert len(body_body) == 2 assert body_body[0].name == "div" # type: ignore[attr-defined] assert body_body[0]["class"] == ["highlight-rest", "notranslate"] # type: ignore[index] assert body_body[0].contents[0].name == "div" # type: ignore[attr-defined] assert body_body[0].contents[0]["class"] == ["highlight"] # type: ignore[attr-defined] assert body_body[1].name == "div" # type: ignore[attr-defined] assert body_body[1]["class"] == ["highlight-python", "notranslate"] # type: ignore[index] assert body[2].name == 'p' # type: ignore[attr-defined] assert body[2].contents == [] # type: ignore[attr-defined] pages_to_check: List[ParameterSet] = [ param("assets.html", idx=0), param("augment-defaults.html", idx=0), param("autodoc-ellipsis.html", idx=0), pytest.param( "autonamedtuple.html", True, marks=pytest.mark.skipif( condition=sys.version_info >= (3, 10), reason="Output differs on Python 3.10", ), id="autonamedtuple.html" ), pytest.param( "autonamedtuple.html", True, marks=min_version((3, 10), reason="Output differs on Python 3.10"), id="autonamedtuple_3_10", ), param("autoprotocol.html", idx=0), param("autotypeddict.html", idx=0), param("code-block.html", idx=0), param("changeset.html", idx=0), param("confval.html", idx=0), param("decorators.html", idx=0), param("example.html", idx=0), param("flake8.html", idx=0), param("formatting.html", idx=0), param("installation.html", idx=0), param("no_docstring.html", idx=0), param("overloads.html", idx=0), param("pre-commit.html", idx=0), param("regex.html", idx=0), param("shields.html", idx=0), param("sourcelink.html", idx=0), param("typevars.html", idx=0), param("variables.html", idx=0), param("wikipedia.html", idx=0), param("documentation-summary.html", idx=0), param("documentation-summary-meta.html", idx=0), param("github.html", idx=0), param("latex.html", idx=0), param("collapse.html", idx=0), param("footnote_symbols.html", idx=0), param("instancevar.html", idx=0), pytest.param( "generic_bases.html", marks=min_version(3.7, reason="Output differs on Python 3.8+"), id="generic_bases" ), pytest.param("autonamedtuple_pep563.html", id="autonamedtuple_pep563"), pytest.param( "genericalias.html", id="genericalias", marks=pytest.mark.skipif( condition=sys.version_info >= (3, 13), reason="Link not created on 3.13", ) ), # Should be xfail ] @pytest.mark.usefixtures("docutils_17_compat", "pre_commit_hooks") def test_html_output( testing_app: Sphinx, html_regression: HTMLRegressionFixture, pre_commit_flake8_contextmanager: Callable[[], ContextManager] ): """ Parametrize new files here rather than as their own function. """ with pre_commit_flake8_contextmanager(): testing_app.build(force_all=True) caught_exceptions: List[BaseException] = [] for page in pages_to_check: pagename: str = page.values[0] # type: ignore[assignment] page_id: str = page.id or pagename for mark in page.marks: if mark.kwargs.get("condition", False): if "reason" in mark.kwargs: print(f"Skipping {page_id!r}: {mark.kwargs['reason']}") break else: print(f"Skipping {page_id!r}") break else: print(f"Checking output for {page_id}") page_id = page_id.replace('.', '_').replace('-', '_') content = (PathPlus(testing_app.outdir) / pagename).read_text() soup = BeautifulSoup(content, "html5lib") for meta in cast(List[Dict], soup.find_all("meta")): if meta.get("content", '') == "width=device-width, initial-scale=0.9, maximum-scale=0.9": meta.extract() # type: ignore[attr-defined] try: html_regression.check(soup, extension=f"_{page_id}_.html", jinja2=True) except BaseException as e: caught_exceptions.append(e) continue print(caught_exceptions) for exception in caught_exceptions: raise exception @pytest.mark.skipif(sphinx.version_info >= (8, 1), reason="Currently failing on Sphinx 8.1") @pytest.mark.usefixtures("pre_commit_hooks") def test_sidebar_links_output( testing_app: Sphinx, advanced_file_regression: AdvancedFileRegressionFixture, monkeypatch, pre_commit_flake8_contextmanager: Callable[[], ContextManager] ): def visit_caption(self, node: nodes.Node) -> None: if isinstance(node.parent, docutils.nodes.container) and node.parent.get("literal_block"): self.body.append('
') else: self.body.append(self.starttag(node, 'p', '', CLASS="caption")) self.add_fignumber(node.parent) self.body.append(self.starttag(node, "span", '', CLASS="caption-text")) def depart_caption(self, node: nodes.Node) -> None: self.body.append('

\n') monkeypatch.setattr(sphinx.writers.html5.HTML5Translator, "visit_caption", visit_caption) monkeypatch.setattr(sphinx.writers.html5.HTML5Translator, "depart_caption", depart_caption) with pre_commit_flake8_contextmanager(): testing_app.build(force_all=True) content = (PathPlus(testing_app.outdir) / "index.html").read_text() page = BeautifulSoup(content, "html5lib") for meta in cast(List[Dict], page.find_all("meta")): if meta.get("content", '') == "width=device-width, initial-scale=0.9, maximum-scale=0.9": meta.extract() # type: ignore[attr-defined] page = remove_html_footer(page) page = remove_html_link_tags(page) for div in page.select("script"): if "_static/language_data.js" in str(div): div.extract() def check_fn(obtained_filename: Path, expected_filename: PathLike): # noqa: MAN002 print(obtained_filename, expected_filename) expected_filename = PathPlus(expected_filename) template = Template(expected_filename.read_text()) expected_filename.write_text( template.render( sphinx_version=sphinx.version_info, python_version=sys.version_info, docutils_version=docutils.__version_info__, ) ) return check_text_files(obtained_filename, expected_filename, encoding="UTF-8") advanced_file_regression.check( str(StringList(page.prettify())), extension=".html", check_fn=check_fn, ) @pytest.mark.usefixtures("pre_commit_hooks") @pytest.mark.sphinx("latex", srcdir="test-root") def test_latex_output( app: Sphinx, latex_regression: LaTeXRegressionFixture, pre_commit_flake8_contextmanager: Callable[[], ContextManager], ): assert app.builder is not None assert app.builder.name.lower() == "latex" with pre_commit_flake8_contextmanager(): app.build() output_file = PathPlus(app.outdir) / "python.tex" latex_regression.check(StringList(output_file.read_lines()), jinja2=True) @pytest.mark.usefixtures("pre_commit_hooks") @pytest.mark.sphinx("latex", srcdir="test-root") def test_latex_output_latex_layout( app: Sphinx, latex_regression: LaTeXRegressionFixture, pre_commit_flake8_contextmanager: Callable[[], ContextManager], ): assert app.builder is not None assert app.builder.name.lower() == "latex" app.setup_extension("sphinx_toolbox.tweaks.latex_layout") app.config.needspace_amount = r"4\baselineskip" # type: ignore[attr-defined] app.config.intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)} # type: ignore[attr-defined] app.events.emit("config-inited", app.config) with pre_commit_flake8_contextmanager(): app.build(force_all=True) output_file = PathPlus(app.outdir) / "python.tex" latex_regression.check(StringList(output_file.read_lines()), jinja2=True) @pytest.mark.usefixtures("pre_commit_hooks") @pytest.mark.sphinx("latex", srcdir="test-root") def test_latex_output_better_header_layout( app: Sphinx, latex_regression: LaTeXRegressionFixture, pre_commit_flake8_contextmanager: Callable[[], ContextManager], ): assert app.builder is not None assert app.builder.name.lower() == "latex" better_header_layout(cast(Config, app.config), 9, 19) app.builder.context.update(app.config.latex_elements) # type: ignore[attr-defined] with pre_commit_flake8_contextmanager(): app.build(force_all=True) output_file = PathPlus(app.outdir) / "python.tex" latex_regression.check(StringList(output_file.read_lines()), jinja2=True) @pytest.mark.usefixtures("pre_commit_hooks") @pytest.mark.sphinx("latex", srcdir="test-root") def test_latex_output_autosummary_col_type( app: Sphinx, latex_regression: LaTeXRegressionFixture, pre_commit_flake8_contextmanager: Callable[[], ContextManager], ): assert app.builder is not None assert app.builder.name.lower() == "latex" app.config.autosummary_col_type = r"\Y" # type: ignore[attr-defined] with pre_commit_flake8_contextmanager(): app.build() output_file = PathPlus(app.outdir) / "python.tex" latex_regression.check(StringList(output_file.read_lines()), jinja2=True) sphinx-toolbox-3.9.0/tests/test_output/test_output_/000077500000000000000000000000001475757360300230405ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_assets_html_.html000066400000000000000000000057761475757360300321350ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Assets β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Assets ΒΆ

hello_world.txt

See here

Missing file

hello_world.txt

missing.txt

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_augment_defaults_html_.html000066400000000000000000000132131475757360300341430ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=('class="sig sig-object py" ' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} Autodoc Augment Defaults β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

Autodoc Augment Defaults ΒΆ

= (4, 0) else '' }}id="tests.augment_defaults_demo.Foo"> class <{{ sig_prename_tag }} class="sig-prename descclassname"> tests.augment_defaults_demo. <{{ sig_prename_tag }} class="sig-name descname"> Foo [source] ΒΆ

I am Foo .

= (4, 0) else '' }}id="tests.augment_defaults_demo.Foo.__init__"> <{{ sig_prename_tag }} class="sig-name descname"> __init__ ( ) [source] ΒΆ

This is __init__

= (4, 0) else '' }}id="tests.augment_defaults_demo.Foo.function"> <{{ sig_prename_tag }} class="sig-name descname"> function ( ) [source] ΒΆ

I shouldn’t be excluded.

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_autodoc_ellipsis_html_.html000066400000000000000000000206221475757360300341600ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Autodoc Ellipsis β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Autodoc Ellipsis ΒΆ

= (4, 0) else '' }}id="tests.demo.ellipsis_function"> <{{ "span" if sphinx_version >= (4, 0) else 'code' }} class="sig-prename descclassname"> tests.demo. = (4, 0) else 'code' }}> <{{ "span" if sphinx_version >= (4, 0) else 'code' }} class="sig-name descname"> ellipsis_function = (4, 0) else 'code' }}> ( foo = ... ) [source] ΒΆ
Return type{% if docutils_version >= (0, 18) %} : {% endif %}

int

= (4, 0) else '' }}id="tests.demo.ellipsis_function_2"> <{{ "span" if sphinx_version >= (4, 0) else 'code' }} class="sig-prename descclassname"> tests.demo. = (4, 0) else 'code' }}> <{{ "span" if sphinx_version >= (4, 0) else 'code' }} class="sig-name descname"> ellipsis_function_2 = (4, 0) else 'code' }}> ( foo = ... , * args , ** kwargs ) [source] ΒΆ

A function with ellipses in the docstring.

Parameters{% if docutils_version >= (0, 18) %} : {% endif %}
Return type{% if docutils_version >= (0, 18) %} : {% endif %}

int

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_autonamedtuple_3_10_.html000066400000000000000000000725161475757360300333540ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - AutoNamedTuple β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - AutoNamedTuple ΒΆ

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> Animal ( name , voice ) [source] ΒΆ

An animal.

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. name ( str ) – The name of the animal.

  2. voice ( str ) – The animal’s voice.

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> Employee ( name , id = 3 ) [source] ΒΆ

Represents an employee.

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. name ( str ) – The employee’s name

  2. id ( int ) – The employee’s ID number

<{{ sig_prename_tag }} class="sig-name descname"> is_executive ( ) [source] ΒΆ

Returns whether the employee is an executive.

Executives have ID numbers < 10.

Return type{% if docutils_version >= (0, 18) %} : {% endif %}

bool

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> Movie ( name , year , based_on ) [source] ΒΆ

Represents a movie.

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. name ( str ) – The name of the movie.

  2. year ( int ) – The movie’s release year.

  3. based_on ( str ) – Alias for field number 2

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> Foo ( a , b , c ) [source] ΒΆ

Bases: NamedTuple

A Namedtuple

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. a ( int ) – An integer

  2. b ( str ) – A string

  3. c ( str ) – C’s doc

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> Traditional ( a , b , c ) ΒΆ

Bases: NamedTuple

A traditional Namedtuple

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. a – Alias for field number 0

  2. b – Alias for field number 1

  3. c – Alias for field number 2

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> NoDocstring ( a , b , c ) [source] ΒΆ

Bases: NamedTuple

NoDocstring(a, b, c)

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. a ( int ) – An integer

  2. b ( str ) – Alias for field number 1

  3. c ( str ) – C’s doc

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> NoDocstring ( a , b , c ) [source]

NoDocstring(a, b, c)

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. a ( int ) – An integer

  2. b ( str ) – Alias for field number 1

  3. c ( str ) – C’s doc

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> CustomisedNew ( values ) [source] ΒΆ

typing.NamedTuple .

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. a – Alias for field number 0

  2. b – Alias for field number 1

  3. c – Alias for field number 2

This function takes a single argument, the Movie to watch. The name of the movie can be accessed with the name attribute.

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_autonamedtuple_html_.html000066400000000000000000000730771475757360300336610ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - AutoNamedTuple β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - AutoNamedTuple ΒΆ

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> Animal ( name , voice ) [source] ΒΆ

An animal.

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. name ( str ) – The name of the animal.

  2. voice ( str ) – The animal’s voice.

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> Employee ( name , id = 3 ) [source] ΒΆ

Represents an employee.

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. name ( str ) – The employee’s name

  2. id ( int ) – The employee’s ID number

<{{ sig_prename_tag }} class="sig-name descname"> is_executive ( ) [source] ΒΆ

Returns whether the employee is an executive.

Executives have ID numbers < 10.

Return type{% if docutils_version >= (0, 18) %} : {% endif %}

bool

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> Movie ( name , year , based_on ) [source] ΒΆ

Represents a movie.

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. name ( str ) – The name of the movie.

  2. year ( int ) – The movie’s release year.

  3. based_on ( str ) – Alias for field number 2

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> Foo ( a , b , c ) [source] ΒΆ

Bases: NamedTuple

A Namedtuple

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. a ( int ) – An integer

  2. b ( str ) – A string

  3. c ( str ) – C’s doc

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> Traditional ( a , b , c ) ΒΆ

Bases: namedtuple()

A traditional Namedtuple

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. a – Alias for field number 0

  2. b – Alias for field number 1

  3. c – Alias for field number 2

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> NoDocstring ( a , b , c ) [source] ΒΆ

Bases: NamedTuple

NoDocstring(a, b, c)

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. a ( int ) – An integer

  2. b ( str ) – Alias for field number 1

  3. c ( str ) – C’s doc

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> NoDocstring ( a , b , c ) [source]

NoDocstring(a, b, c)

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. a ( int ) – An integer

  2. b ( str ) – Alias for field number 1

  3. c ( str ) – C’s doc

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> autonamedtuple_demo. <{{ sig_prename_tag }} class="sig-name descname"> CustomisedNew ( values ) [source] ΒΆ

typing.NamedTuple .

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. a – Alias for field number 0

  2. b – Alias for field number 1

  3. c – Alias for field number 2

This function takes a single argument, the Movie to watch. The name of the movie can be accessed with the name attribute.

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_autonamedtuple_pep563_.html000066400000000000000000000137411475757360300337270ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - AutoNamedTuple - PEP 563 β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - AutoNamedTuple - PEP 563 ΒΆ

namedtuple <{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.autonamedtuple_demo_pep563. <{{ sig_prename_tag }} class="sig-name descname"> Animal ( name , voice ) [source] ΒΆ

An animal.

Fields{% if docutils_version >= (0, 18) %} : {% endif %}
  1. name ( str ) – The name of the animal.

  2. voice ( Callable [[], str ]) – The animal’s voice.

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_autoprotocol_html_.html000066400000000000000000000442511475757360300333540ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Autoprotocol β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Autoprotocol ΒΆ

protocol <{{ sig_prename_tag }} class="sig-prename descclassname"> autoprotocol_demo. <{{ sig_prename_tag }} class="sig-name descname"> Frobnicater [source] ΒΆ

typing.Protocol .

This protocol is runtime checkable .

Classes that implement this protocol must have the following methods / attributes:

<{{ sig_prename_tag }} class="sig-name descname"> frobnicate ( something ) [source] ΒΆ
Return type{% if docutils_version >= (0, 18) %} : {% endif %}

Any

protocol <{{ sig_prename_tag }} class="sig-prename descclassname"> autoprotocol_demo. <{{ sig_prename_tag }} class="sig-name descname"> HasLessThan [source] ΒΆ

typing.Protocol for classes that support the < operator.

Classes that implement this protocol must have the following methods / attributes:

<{{ sig_prename_tag }} class="sig-name descname"> __lt__ ( other ) [source] ΒΆ

Return self < other .

Return type{% if docutils_version >= (0, 18) %} : {% endif %}

bool

protocol <{{ sig_prename_tag }} class="sig-prename descclassname"> autoprotocol_demo. <{{ sig_prename_tag }} class="sig-name descname"> HasGreaterThan [source] ΒΆ

typing.Protocol .

Classes that implement this protocol must have the following methods / attributes:

<{{ sig_prename_tag }} class="sig-name descname"> __gt__ ( other ) [source] ΒΆ

Return self > other .

Return type{% if docutils_version >= (0, 18) %} : {% endif %}

bool

protocol <{{ sig_prename_tag }} class="sig-prename descclassname"> autoprotocol_demo. <{{ sig_prename_tag }} class="sig-name descname"> HasGreaterThan [source]

Bases: Protocol

Classes that implement this protocol must have the following methods / attributes:

<{{ sig_prename_tag }} class="sig-name descname"> __gt__ ( other ) [source]

Return self > other .

Return type{% if docutils_version >= (0, 18) %} : {% endif %}

bool

protocol <{{ sig_prename_tag }} class="sig-prename descclassname"> autoprotocol_demo. <{{ sig_prename_tag }} class="sig-name descname"> SupportsAbs [source] ΒΆ

Bases: Protocol [ +T_co ]

An ABC with one abstract method __abs__ that is covariant in its return type.

This protocol is runtime checkable .

Classes that implement this protocol must have the following methods / attributes:

abstract{% if sphinx_version >= (8, 2) %}method{% endif %} <{{ sig_prename_tag }} class="sig-name descname"> __abs__ ( ) [source] ΒΆ
Return type{% if docutils_version >= (0, 18) %} : {% endif %}

+T_co

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_autotypeddict_html_.html000066400000000000000000000475711475757360300335140ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - AutoTypedDict β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - AutoTypedDict ΒΆ

Demo of .. autotypeddict::

typeddict <{{ sig_prename_tag }} class="sig-prename descclassname"> autotypeddict_demo. <{{ sig_prename_tag }} class="sig-name descname"> Animal [source] ΒΆ

Optional keys common to all animals.

Required Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • name ( str ) – The name of the animal

Optional Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • voice ( str ) – The animal’s voice.

typeddict <{{ sig_prename_tag }} class="sig-prename descclassname"> autotypeddict_demo. <{{ sig_prename_tag }} class="sig-name descname"> AquaticBird [source] ΒΆ

typing.TypedDict .

Required Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • habitat ( float ) – The bird’s habitat (e.g. lake, sea)

  • name ( str ) – The name of the animal

  • egg_size ( float ) – The size of the bird’s egg, in mm.

Optional Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • voice ( str ) – The animal’s voice.

typeddict <{{ sig_prename_tag }} class="sig-prename descclassname"> autotypeddict_demo. <{{ sig_prename_tag }} class="sig-name descname"> Bird [source] ΒΆ

A bird.

Required Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • egg_size ( float ) – The size of the bird’s egg, in mm.

  • name ( str ) – The name of the animal

Optional Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • voice ( str ) – The animal’s voice.

typeddict <{{ sig_prename_tag }} class="sig-prename descclassname"> autotypeddict_demo. <{{ sig_prename_tag }} class="sig-name descname"> Cat [source] ΒΆ

A cat.

Required Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • fur_color ( str ) – The colour of the cat’s fur.

  • name ( str ) – The name of the animal

Optional Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • voice ( str ) – The animal’s voice.

typeddict <{{ sig_prename_tag }} class="sig-prename descclassname"> autotypeddict_demo. <{{ sig_prename_tag }} class="sig-name descname"> OldStyleAnimal ΒΆ

Old style TypedDict for Python 2 and where keys aren’t valid Python identifiers.

Optional Keys{% if docutils_version >= (0, 18) %} : {% endif %}
typeddict <{{ sig_prename_tag }} class="sig-prename descclassname"> autotypeddict_demo. <{{ sig_prename_tag }} class="sig-name descname"> Movie [source] ΒΆ

Represents a movie.

Required Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • name ( str ) – The name of the animal

  • year ( int ) – The movie’s release year.

  • based_on ( str )

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_changeset_html_.html000066400000000000000000000140501475757360300325550ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} Changeset β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

Changeset ΒΆ

Removed ΒΆ

Removed in version 1.2.3: Use foo() instead.

Removed in version 1.2.3: Due to an unfixable bug this function has been removed. If you desperately need this functionality please write to the mailing list at python-users @ example . org

Removed in version 1.2.3:
  • Removed mozarella

  • Removed parrot() from pet_shop

Changed ΒΆ

Changed in version 1.2.3: Moved from mozarella

Changed in version 0.3.0:
  • Parameters for __init__ can be documented either in the class docstring or alongside the attribute. The class docstring has priority.

  • Added support for autodocsumm .

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_code_block_html_.html000066400000000000000000000165211475757360300327050ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Code β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Code ΒΆ

def print(text):
    sys.stdout.write(text)
def print(text):
    sys.stdout.write(text)
def print(text):
        sys.stdout.write(text)
def print(text):
    sys.stdout.write(text)
1
2
def print(text):
    sys.stdout.write(text)

Code Cell ΒΆ

In [1]:
def print(text):
    sys.stdout.write(text)

print("hello world")
[1]:
hello world
In [2]:
def print(text):
        sys.stdout.write(text)
{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_collapse_html_.html000066400000000000000000000061301475757360300324160ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} Collapse β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

Collapse ΒΆ

Details

Something small enough to escape casual notice.

A Different Label

Something else that might escape notice.

Open by default

The text should be visible when the page loads.

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_confval_html_.html000066400000000000000000000222761475757360300322550ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object std"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} Confval β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

Confval ΒΆ

<{{ sig_prename_tag }} class="sig-name descname"> source_link_target ΒΆ
Type: str
Required: False
Default: 'Sphinx'

The target of the source link, either 'GitHub' or 'Sphinx' . Case insensitive.

<{{ sig_prename_tag }} class="sig-name descname"> github_username ΒΆ
Type: str
Required: True
<{{ sig_prename_tag }} class="sig-name descname"> github_repository ΒΆ
Type: str
Required: True

The GitHub repository this documentation corresponds to.

<{{ sig_prename_tag }} class="sig-name descname"> conda_channels ΒΆ
Type: Comma-separated list of strings
Required: True

The conda channels required to install the library from Anaconda.

<{{ sig_prename_tag }} class="sig-name descname"> github_repository
Type: str

This is a duplicate with :noindex: set.

This is an xref to the github_repository configuration option.

<{{ sig_prename_tag }} class="sig-name descname"> something ΒΆ

A configuration value.

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_decorators_html_.html000066400000000000000000000100121475757360300327530ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Decorators β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Decorators ΒΆ

<{{ sig_prename_tag }} class="sig-prename descclassname"> @ <{{ sig_prename_tag }} class="sig-name descname"> my_decorator ΒΆ

A decorator.

@my_decorator

@my_decorator

Title

{% if sphinx_version >= (8, 1) %}
{% endif %}
test_html_output_documentation_summary_html_.html000066400000000000000000000054461475757360300351740ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_output/test_output_{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - documentation-summary β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - documentation-summary ΒΆ

This is an awesome tool! πŸš€ ~ intersphinx_mapping # {{ jinja2 }} 100% β€˜Quotes’

{% if sphinx_version >= (8, 1) %}
{% endif %}
test_html_output_documentation_summary_meta_html_.html000066400000000000000000000057341475757360300362020ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_output/test_output_{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - documentation-summary :meta: β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - documentation-summary :meta: ΒΆ

This is an awesome tool! πŸš€ ~ intersphinx_mapping # 100% β€˜Quotes’

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_example_html_.html000066400000000000000000000073661475757360300322630ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - reST Example β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - reST Example ΒΆ

.. code-block:: python

    def print(text):
        sys.stdout.write(text)
def print(text):
    sys.stdout.write(text)

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_flake8_html_.html000066400000000000000000000102311475757360300317630ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Flake8 β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Flake8 ΒΆ

Code

Description

DALL000

Module lacks __all__.

Code

Description

DALL000

Module lacks __all__.

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_footnote_symbols_html_.html000066400000000000000000000100641475757360300342220ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - footnotes β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - footnotes ΒΆ

Some text †

Some more text ‑

Sometimes Β§ strange things happen.

†

An auto-symbol footnote

‑

Another footnote

Β§

A footnote with lots of content.

print("Look ma! I'm in a footnote")
{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_formatting_html_.html000066400000000000000000000057001475757360300327700ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Formatting β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Formatting ΒΆ

m/z

doodad

Examples:

Other Extensions

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_generic_bases_.html000066400000000000000000000267301475757360300323710ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Generic Bases β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Generic Bases ΒΆ

class <{{ sig_prename_tag }} class="sig-prename descclassname"> sphinx_toolbox.more_autodoc.generic_bases. <{{ sig_prename_tag }} class="sig-name descname"> Example ( iterable = () ) [source] ΒΆ

Bases: List [ Tuple [ str , float , List [ str ]]]

An example of sphinx_toolbox.more_autodoc.generic_bases .

class <{{ sig_prename_tag }} class="sig-prename descclassname"> sphinx_toolbox.more_autodoc.generic_bases. <{{ sig_prename_tag }} class="sig-name descname"> Example2 ( iterable = () ) [source] ΒΆ

Bases: Example

An example of sphinx_toolbox.more_autodoc.generic_bases .

This one does not directly subclass a Generic.

class <{{ sig_prename_tag }} class="sig-prename descclassname"> sphinx_toolbox.more_autodoc.generic_bases. <{{ sig_prename_tag }} class="sig-name descname"> FinalExample ( iterable = () ) [source] ΒΆ

Bases: List [ Tuple [ str , float , List [ str ]]]

An example of sphinx_toolbox.more_autodoc.generic_bases decorated with @final .

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_genericalias_.html000066400000000000000000000113751475757360300322250ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Genericalias β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Genericalias ΒΆ

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.variables_genericalias_demo. <{{ sig_prename_tag }} class="sig-name descname"> PathLike ΒΆ

Type hint for filesystem paths

Alias of Union [ str , PathLike , Path ]

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_github_html_.html000066400000000000000000000132551475757360300321040ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - GitHub β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}
{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_installation_html_.html000066400000000000000000000762511475757360300333300ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Installation β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}{% if sphinx_version >= (3, 5) %} {% endif %}

sphinx-toolbox Demo - Installation ΒΆ

python3 -m pip install my_project --user

python3 -m pip install my-project --user

python3 -m pip install my-project --user

conda install my_project

conda install my-project

conda install my-project

python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user

python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user

python3 -m pip install my_project --user

python3 -m pip install my-project --user

python3 -m pip install my-project --user

python3 -m pip install my-project --user

python3 -m pip install my-project --user

python3 -m pip install my_project --user

python3 -m pip install my-project --user

python3 -m pip install my-project --user

python3 -m pip install my-project --user

python3 -m pip install my-project --user

Enable my-extension by adding the following to the extensions variable in your conf.py :

extensions = [
    ...
    'foo',
    'bar',
    'baz',
    'my-extension',
    ]

For more information see https://www.sphinx-doc.org/en/master/usage/extensions#third-party-extensions .

Enable my-extension by adding the following to the extensions variable in your conf.py :

extensions = [
    ...
    'foo',
    'bar',
    'baz',
    'my_extension',
    ]

For more information see https://www.sphinx-doc.org/en/master/usage/extensions#third-party-extensions .

Enable my-extension by adding the following to the extensions variable in your conf.py :

extensions = [
    ...
    'my_extension',
    ]

For more information see https://www.sphinx-doc.org/en/master/usage/extensions#third-party-extensions .

Enable my-extension by adding the following to the extensions variable in your conf.py :

extensions = [
    ...
    'my-extension',
    ]

For more information see https://www.sphinx-doc.org/en/master/usage/extensions#third-party-extensions .

extensions = [
    ...
    'my-extension',
    ]

For more information see https://www.sphinx-doc.org/en/master/usage/extensions#third-party-extensions .

Enable my-extension by adding the following to the extensions variable in your conf.py :

extensions = [
    ...
    'my-extension',
    ]

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_instancevar_html_.html000066400000000000000000000167331475757360300331430ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Instance Variables β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Instance Variables ΒΆ

class <{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.instancevar. <{{ sig_prename_tag }} class="sig-name descname"> OctoAPI ( api_key ) [source] ΒΆ

The primary interface to the Octopus Energy API.

Parameters{% if docutils_version >= (0, 18) %} : {% endif %}

api_key ( str ) – API key to access the Octopus Energy API.

If you are an Octopus Energy customer, you can generate an API key from your online dashboard .

<{{ sig_prename_tag }} class="sig-name descname"> API_BASE ΒΆ

Type: SlumberURL

The base URL of the Octopus Energy API.

<{{ sig_prename_tag }} class="sig-name descname"> API_KEY ΒΆ

Type: Union [ Secret , str , float ]

The API key to access the Octopus Energy API.

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_latex_html_.html000066400000000000000000000247421475757360300317420ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - LaTeX β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - LaTeX ΒΆ

Content under the samepage directive

https://avatars.githubusercontent.com/u/75883961

The ​following ​characters ​are ​problematic ​in ​LaTeX:

  • β™ 

  • β™₯

  • ♦

  • ♣

  • ΞΌ

  • ≑

Hello

Content under the samepage directive

https://avatars.githubusercontent.com/u/75883961

The ​following ​characters ​are ​problematic ​in ​LaTeX:

  • β™ 

  • β™₯

  • ♦

  • ♣

  • ΞΌ

  • ≑

  • β‰ˆ

This is a ΞΌ-library for doing something.

The Greek letter ΞΌ has several uses.

Goodbyeee!

Example Footnotes ΒΆ

Hello 1
Goodbye 2
Symbol †
Another Symbol ‑
Number Again 3
Symbol 3 Β§
Symbol 4 ΒΆ
Symbol 5 #
Symbol 6 β™ 
Symbol 7 β™₯
Symbol 8 ♦
Symbol 9 ♣
1

One

2

Two

†

Buckle my shoe

‑

The second symbol

3

The number after the symbol

Β§

Symbol 3

ΒΆ

Symbol 4

#

Symbol 5

β™ 

Symbol 6

β™₯

Symbol 7

♦

Symbol 8

♣

Symbol 9

See also

Check this out on GitHub

See also

These are helpful resources:

  • Google

  • StackOverflow

  • RealPython

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_no_docstring_html_.html000066400000000000000000000321501475757360300333050ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - :no-docstring: β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - :no-docstring: ΒΆ

typeddict <{{ sig_prename_tag }} class="sig-prename descclassname"> autotypeddict_demo. <{{ sig_prename_tag }} class="sig-name descname"> Animal [source]

Optional keys common to all animals.

Required Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • name ( str ) – The name of the animal

Optional Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • voice ( str ) – The animal’s voice.

typeddict <{{ sig_prename_tag }} class="sig-prename descclassname"> autotypeddict_demo. <{{ sig_prename_tag }} class="sig-name descname"> Bird [source]

A bird.

Required Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • egg_size ( float ) – The size of the bird’s egg, in mm.

  • name ( str ) – The name of the animal

Optional Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • voice ( str ) – The animal’s voice.

typeddict <{{ sig_prename_tag }} class="sig-prename descclassname"> autotypeddict_demo. <{{ sig_prename_tag }} class="sig-name descname"> Cat [source]

A cat.

Required Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • fur_color ( str ) – The colour of the cat’s fur.

  • name ( str ) – The name of the animal

Optional Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • voice ( str ) – The animal’s voice.

typeddict <{{ sig_prename_tag }} class="sig-prename descclassname"> autotypeddict_demo. <{{ sig_prename_tag }} class="sig-name descname"> Movie [source]

Represents a movie.

Required Keys{% if docutils_version >= (0, 18) %} : {% endif %}
  • name ( str ) – The name of the animal

  • year ( int ) – The movie’s release year.

  • based_on ( str )

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_overloads_html_.html000066400000000000000000001237051475757360300326220ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Overloads β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Overloads ΒΆ

class <{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.overloads_demo. <{{ sig_prename_tag }} class="sig-name descname"> Bar [source] ΒΆ
<{{ sig_prename_tag }} class="sig-name descname"> __getitem__ ( item ) [source] ΒΆ

Return the item with the given index.

{% if sphinx_version >= (7, 3) %}Added{% else %}New{% endif %} in version 1.2.3.

Parameters{% if docutils_version >= (0, 18) %} : {% endif %}

item ( Union [ int , slice ])

Return type{% if docutils_version >= (0, 18) %} : {% endif %}

Union [ str , List [ str ]]

Overloads{% if docutils_version >= (0, 18) %} : {% endif %}
<{{ sig_prename_tag }} class="sig-name descname"> __module__ = 'tests.test_output.overloads_demo' ΒΆ

Type: str

class <{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.overloads_demo. <{{ sig_prename_tag }} class="sig-name descname"> Foo [source] ΒΆ
<{{ sig_prename_tag }} class="sig-name descname"> __getitem__ ( item ) [source] ΒΆ

Return the item with the given index.

Parameters{% if docutils_version >= (0, 18) %} : {% endif %}

item ( Union [ int , slice ])

Return type{% if docutils_version >= (0, 18) %} : {% endif %}

Union [ str , List [ str ]]

Overloads{% if docutils_version >= (0, 18) %} : {% endif %}

{% if sphinx_version >= (7, 3) %}Added{% else %}New{% endif %} in version 1.2.3.

<{{ sig_prename_tag }} class="sig-name descname"> __module__ = 'tests.test_output.overloads_demo' ΒΆ

Type: str

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.overloads_demo. <{{ sig_prename_tag }} class="sig-name descname"> serde ( cls = None , from_key = 'from' , to_key = 'to' ) [source] ΒΆ

Decorator to add serialisation and deserialisation capabilities to attrs classes.

Parameters{% if docutils_version >= (0, 18) %} : {% endif %}
Return type{% if docutils_version >= (0, 18) %} : {% endif %}

Union [ Type [ object ], Callable [[ Type [ object ]], Type [ object ]]]

Overloads{% if docutils_version >= (0, 18) %} : {% endif %}
  • serde ( cls :{% if python_version >= (3, 12) %} Type[object] , from_key : str = …, to_key :{% else %} Type [ object ], from_key = …, to_key {% endif %}{% if python_version >= (3, 12) %} str = … ) -> Type[object] {% else %} = … ) -> Type [ object ]{% endif %}

  • serde ( cls : None = None, from_key {% if python_version >= (3, 12) %} : str = …, to_key : str = … ) -> 'Callable[[Type[object]], Type[object]]' {% else %} = …, to_key = … ) -> Callable [[ Type [ object ]], Type [ object ]]{% endif %}

Classes decorated with @serde will have two new methods added:

classmethod <{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.overloads_demo. <{{ sig_prename_tag }} class="sig-name descname"> from_dict ( d ) ΒΆ

Construct an instance of the class from a dictionary.

Parameters{% if docutils_version >= (0, 18) %} : {% endif %}

d – Mapping [ str , Any ]

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.overloads_demo. <{{ sig_prename_tag }} class="sig-name descname"> to_dict ( ) {% if sphinx_version >= (4, 1) -%} β†’ MutableMapping[str, Any]: {% else -%}β†’ MutableMapping[str, Any]: {% endif -%} ΒΆ

Returns a dictionary containing the contents of the class.

Return type{% if docutils_version >= (0, 18) %} : {% endif %}

MutableMapping [ str , Any ]

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_pre_commit_html_.html000066400000000000000000000175321475757360300327620ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - pre-commit β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - pre-commit ΒΆ

{% if sphinx_version >= (4, 3) %}
-repo:https://github.com/sphinx-toolbox/sphinx-toolbox
rev:v0.0.4
hooks:
-id:some-hook
-id:some-other-hook
{% else %}
- repo: https://github.com/sphinx-toolbox/sphinx-toolbox
  rev: v0.0.4
  hooks:
  - id: some-hook
  - id: some-other-hook
{% endif %}

{% if sphinx_version >= (4, 3) %}
-repo:https://github.com/sphinx-toolbox/sphinx-toolbox
rev:v0.0.4
hooks:
-id:flake2lint
{% else %}
- repo: https://github.com/sphinx-toolbox/sphinx-toolbox
  rev: v0.0.4
  hooks:
  - id: flake2lint
{% endif %}

{% if sphinx_version >= (4, 3) %}
-repo:https://github.com/pycqa/flake8
rev:3.8.4
hooks:
-id:flake8
additional_dependencies:
-sphinx-toolbox==0.0.4
{% else %}
- repo: https://github.com/pycqa/flake8
  rev: 3.8.4
  hooks:
  - id: flake8
    additional_dependencies:
    - sphinx-toolbox==0.0.4
{% endif %}

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_regex_html_.html000066400000000000000000001766221475757360300317440ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%}{% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Regex β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Regex ΒΆ

General utility functions.

<{{ sig_prename_tag }} class="sig-prename descclassname"> sphinx_toolbox.utils. <{{ sig_prename_tag }} class="sig-name descname"> untyped_param_regex ΒΆ

Type: Pattern [ str ]

Regex to match :param <name>: <docstring> flags.

{% if sphinx_version >= (7, 3) %}Added{% else %}New{% endif %} in version 0.8.0.

Pattern

^ : ( p a r a m | p a r a m e t e r | a r g | a r g u m e n t ) \s * ( [ A - Z a - z _ ] \w * \s * ) : \s * ( . * )

Flags

re.ASCII

<{{ sig_prename_tag }} class="sig-prename descclassname"> sphinx_toolbox.utils. <{{ sig_prename_tag }} class="sig-name descname"> typed_flag_regex ΒΆ

Type: Pattern [ str ]

Regex to match :type <name>: <type> flags.

{% if sphinx_version >= (7, 3) %}Added{% else %}New{% endif %} in version 0.8.0.

Pattern

^ : ( p a r a m t y p e | t y p e ) \s * ( [ A - Z a - z _ ] \w * \s * ) : \s * ( . * )

Flags

re.ASCII

^ H e l l o \s + [ W w ] o r l d [ . , ] ( L o v e l y | H o r r i b l e ) w e a t h e r , i s n ' t i t ( . * ) ?

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.regex_demo. <{{ sig_prename_tag }} class="sig-name descname"> no_flags ΒΆ

Type: Pattern

Compiled regular expression object.

Pattern

H e l l o \s + [ W w ] o r l d [ . , ] ( L o v e l y | H o r r i b l e ) w e a t h e r , i s n ' t i t ( . * ) ?

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.regex_demo. <{{ sig_prename_tag }} class="sig-name descname"> one_flag ΒΆ

Type: Pattern

Compiled regular expression object.

Pattern

H e l l o \s + [ W w ] o r l d [ . , ] ( L o v e l y | H o r r i b l e ) w e a t h e r , i s n ' t i t ( . * ) ?

Flags

re.IGNORECASE

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.regex_demo. <{{ sig_prename_tag }} class="sig-name descname"> two_flags ΒΆ

Type: Pattern

Compiled regular expression object.

Pattern

H e l l o \s + [ W w ] o r l d [ . , ] ( L o v e l y | H o r r i b l e ) w e a t h e r , i s n ' t i t ( . * ) ?

Flags

re.ASCII

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.regex_demo. <{{ sig_prename_tag }} class="sig-name descname"> backticks ΒΆ

Type: Pattern

Compiled regular expression object.

Pattern

: p y : c l a s s : ` ( [ A - Z a - z _ ] [ A - Z a - z 0 - 9 . _ ] + ) `

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.regex_demo. <{{ sig_prename_tag }} class="sig-name descname"> leading_whitespace ΒΆ

Type: Pattern

Compiled regular expression object.

Pattern

[ ] 3 : p y : c l a s s : ` ( [ A - Z a - z _ ] [ A - Z a - z 0 - 9 . _ ] + ) `

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.regex_demo. <{{ sig_prename_tag }} class="sig-name descname"> trailing_whitespace ΒΆ

Type: Pattern

Compiled regular expression object.

Pattern

: p y : c l a s s : ` ( [ A - Z a - z _ ] [ A - Z a - z 0 - 9 . _ ] + ) ` 3

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.regex_demo. <{{ sig_prename_tag }} class="sig-name descname"> single_whitespace ΒΆ

Type: Pattern

Compiled regular expression object.

Pattern

[ ] : p y : c l a s s : ` ( [ A - Z a - z _ ] [ A - Z a - z 0 - 9 . _ ] + ) ` [ ]

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_shields_html_.html000066400000000000000000000254241475757360300322560ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Shields β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}
{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_sourcelink_html_.html000066400000000000000000000557731475757360300330130ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class_std=(' class="sig sig-object std"' if sphinx_version >= (4, 0) else '') -%} {% set sig_object_class_py=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - :sourcelink: β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}
{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_typevars_html_.html000066400000000000000000000446341475757360300325040ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - TypeVars β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - TypeVars ΒΆ

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.typevars_demo. <{{ sig_prename_tag }} class="sig-name descname"> DS = TypeVar(DS, SlotsDemo, Demo) ΒΆ

Type: TypeVar

Invariant TypeVar constrained to tests.test_output.typevars_demo.SlotsDemo and tests.test_output.typevars_demo.Demo .

class <{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.typevars_demo. <{{ sig_prename_tag }} class="sig-name descname"> Demo ( arg1 , arg2 ) [source] ΒΆ

An attrs class

<{{ sig_prename_tag }} class="sig-name descname"> arg1 ΒΆ

Type: str

An argument

<{{ sig_prename_tag }} class="sig-name descname"> arg2 ΒΆ

Type: int

Another argument

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.typevars_demo. <{{ sig_prename_tag }} class="sig-name descname"> FR = TypeVar(FR, bound=SlotsDemo) ΒΆ

Type: TypeVar

Invariant TypeVar bound to tests.test_output.typevars_demo.SlotsDemo .

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.typevars_demo. <{{ sig_prename_tag }} class="sig-name descname"> S = TypeVar(S, bound=SlotsDemo) ΒΆ

Type: TypeVar

Invariant TypeVar bound to tests.test_output.typevars_demo.SlotsDemo .

class <{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.typevars_demo. <{{ sig_prename_tag }} class="sig-name descname"> SlotsDemo ( arg1 , arg2 ) [source] ΒΆ

An attrs class with slots=True

<{{ sig_prename_tag }} class="sig-name descname"> arg1 ΒΆ

Type: str

An argument

<{{ sig_prename_tag }} class="sig-name descname"> arg2 ΒΆ

Type: int

Another argument

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.typevars_demo. <{{ sig_prename_tag }} class="sig-name descname"> T = TypeVar(T) ΒΆ

Type: TypeVar

Invariant TypeVar .

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.typevars_demo. <{{ sig_prename_tag }} class="sig-name descname"> T_co = TypeVar(T_co, covariant=True) ΒΆ

Type: TypeVar

Covariant TypeVar .

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.typevars_demo. <{{ sig_prename_tag }} class="sig-name descname"> T_contra = TypeVar(T_contra, contravariant=True) ΒΆ

Type: TypeVar

Contravariant TypeVar .

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_variables_html_.html000066400000000000000000000336401475757360300325720ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Variables β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo - Variables ΒΆ

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.variables_genericalias_demo. <{{ sig_prename_tag }} class="sig-name descname"> CONSTANT = 42

Type: int

Don’t change this!”

class <{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.variables_genericalias_demo. <{{ sig_prename_tag }} class="sig-name descname"> Demo ( arg1 , arg2 ) [source]

An attrs class

<{{ sig_prename_tag }} class="sig-name descname"> arg1

Type: str

An argument

<{{ sig_prename_tag }} class="sig-name descname"> arg2

Type: int

Another argument

{% if sphinx_version >= (4, 3) %} abstract property{% else %} abstract property{% endif %} <{{ sig_prename_tag }} class="sig-name descname"> foo

A property.

Return type{% if docutils_version >= (0, 18) %} : {% endif %}

str

class <{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.variables_genericalias_demo. <{{ sig_prename_tag }} class="sig-name descname"> SlotsDemo ( arg1 , arg2 ) [source]

An attrs class with slots=True

<{{ sig_prename_tag }} class="sig-name descname"> arg1

Type: str

An argument

<{{ sig_prename_tag }} class="sig-name descname"> arg2

Type: int

Another argument

<{{ sig_prename_tag }} class="sig-prename descclassname"> tests.test_output.variables_genericalias_demo. <{{ sig_prename_tag }} class="sig-name descname"> variable = ['a', 'b', 'c', 'd', 'e', 'f', 'g']

Type: Union [ List [ str ], Tuple [ str , int , float ], int , bytes , Callable [[ str ], int ]]

Some variable

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_html_output_wikipedia_html_.html000066400000000000000000000062471475757360300325730ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Wikipedia β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %} sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_latex_output.tex000066400000000000000000001737171475757360300273760ustar00rootroot00000000000000<% set tilde = "~" if sphinx_version < (4, 4) else "" -%> <%- set hline = "\\sphinxhline" if sphinx_version >= (5, 3) else "\\hline" -%> <%- set par = "\\sphinxtableafterendhook\\par" if sphinx_version >= (5, 3) else "\\par" -%> <%- set sphinxparam = "sphinxparam" if sphinx_version >= (6, 2) else "emph" -%> <%- if sphinx_version >= (7, 1) -%> <%- set sphinxparamcomma = "\\sphinxparamcomma \\sphinxparam" -%> <%- elif sphinx_version >= (6, 2) -%> <%- set sphinxparamcomma = ", \\sphinxparam" -%> <%- else -%> <%- set sphinxparamcomma = ", \\emph" -%> <%- endif -%> <%- set du_role_2nd_n = ",n" if (7, 2) > sphinx_version >= (6, 2) else "" -%> <# Latest Sphinx removed vertical rules from csv-table. Inconsequential for us #> <%- set regex_columns = "TT" if sphinx_version >= (6, 0) else "|T|T|" -%> <%- set sphinx81_linebreak = "\n" if sphinx_version >= (8, 1) else "" -%> <%- macro versionadded(v) -%> <%- if sphinx_version >= (8, 1) %>\DUrole{versionmodified}{\DUrole{added}{Added in version <>.}} <%- else %>\DUrole{versionmodified,added}{<% if sphinx_version >= (7, 3) %>Added<% else %>New<% endif %> in version <>.}<% endif %> <%- endmacro -%> <%- macro versionremoved(v) -%> <%- if sphinx_version >= (8, 1) %>\DUrole{versionmodified}{\DUrole{removed}{Removed in version <>: }} <%- else %>\DUrole{versionmodified,removed}{Removed in version <>: } <%- endif %><%- endmacro -%> <%- macro versionchanged(v) -%> <%- if sphinx_version >= (8, 1) %>\DUrole{versionmodified}{\DUrole{changed}{Changed in version <>: }} <%- else %>\DUrole{versionmodified,changed}{Changed in version <>: } <%- endif %><%- endmacro -%> %% Generated by Sphinx. \def\sphinxdocclass{report} \documentclass[letterpaper,10pt,english]{sphinxmanual} \ifdefined\pdfpxdimen \let\sphinxpxdimen\pdfpxdimen\else\newdimen\sphinxpxdimen \fi \sphinxpxdimen=.75bp\relax <% if sphinx_version >= (4, 0) %>\ifdefined\pdfimageresolution \pdfimageresolution= \numexpr \dimexpr1in\relax/\sphinxpxdimen\relax \fi %% let collapsable pdf bookmarks panel have high depth per default \PassOptionsToPackage{bookmarksdepth=5}{hyperref}<% if sphinx_version >= (5, 3) %> <% endif %><% if sphinx_version >= (6, 0) %> \PassOptionsToPackage{booktabs}{sphinx} \PassOptionsToPackage{colorrows}{sphinx}<% endif %> <% endif %> \PassOptionsToPackage{warn}{textcomp} \usepackage[utf8]{inputenc} \ifdefined\DeclareUnicodeCharacter % support both utf8 and utf8x syntaxes \ifdefined\DeclareUnicodeCharacterAsOptional \def\sphinxDUC#1{\DeclareUnicodeCharacter{"#1}} \else \let\sphinxDUC\DeclareUnicodeCharacter \fi \sphinxDUC{00A0}{\nobreakspace} \sphinxDUC{2500}{\sphinxunichar{2500}} \sphinxDUC{2502}{\sphinxunichar{2502}} \sphinxDUC{2514}{\sphinxunichar{2514}} \sphinxDUC{251C}{\sphinxunichar{251C}} \sphinxDUC{2572}{\textbackslash} \fi \usepackage{cmap} \usepackage[T1]{fontenc} \usepackage{amsmath,amssymb,amstext} \usepackage{babel} <%+ if sphinx_version >= (4, 0) %> \usepackage{tgtermes} \usepackage{tgheros} \renewcommand{\ttdefault}{txtt} <% else %> \usepackage{times} \expandafter\ifx\csname T@LGR\endcsname\relax \else % LGR was declared as font encoding \substitutefont{LGR}{\rmdefault}{cmr} \substitutefont{LGR}{\sfdefault}{cmss} \substitutefont{LGR}{\ttdefault}{cmtt} \fi \expandafter\ifx\csname T@X2\endcsname\relax \expandafter\ifx\csname T@T2A\endcsname\relax \else % T2A was declared as font encoding \substitutefont{T2A}{\rmdefault}{cmr} \substitutefont{T2A}{\sfdefault}{cmss} \substitutefont{T2A}{\ttdefault}{cmtt} \fi \else % X2 was declared as font encoding \substitutefont{X2}{\rmdefault}{cmr} \substitutefont{X2}{\sfdefault}{cmss} \substitutefont{X2}{\ttdefault}{cmtt} \fi <% endif %> \usepackage[Bjarne]{fncychap} \usepackage{sphinx} <% if sphinx_version >= (4, 0) %> \fvset{fontsize=auto} <% else %> \fvset{fontsize=\small} <% endif -%> \usepackage{geometry} % Include hyperref last. \usepackage{hyperref} % Fix anchor placement for figures with captions. \usepackage{hypcap}% it must be loaded after hyperref. % Set up styles of URL: it should be placed after hyperref. \urlstyle{same} \usepackage{sphinxmessages} \setcounter{tocdepth}{1} \definecolor{nbsphinxin}{HTML}{307FC1} \definecolor{nbsphinxout}{HTML}{BF5B3D} \newcommand\thesymbolfootnote{\fnsymbol{footnote}}\let\thenumberfootnote\thefootnote \definecolor{regex_literal}{HTML}{696969} \definecolor{regex_at}{HTML}{FF4500} \definecolor{regex_repeat_brace}{HTML}{FF4500} \definecolor{regex_branch}{HTML}{FF4500} \definecolor{regex_subpattern}{HTML}{1e90ff} \definecolor{regex_in}{HTML}{ff8c00} \definecolor{regex_category}{HTML}{8fbc8f} \definecolor{regex_repeat}{HTML}{FF4500} \definecolor{regex_any}{HTML}{FF4500} \usepackage[]{bookmark} \newcommand{\thesummary}{This is an awesome tool! πŸš€ \textasciitilde\space intersphinx\_mapping \# 100\% β€˜Quotes’} \makeatletter \renewcommand{\py@release}{ \releasename\space\version \par \vspace{25pt} \textup{\thesummary} } \makeatother \title{Python} \date{Mar 11, 2021} \release{} \author{unknown} \newcommand{\sphinxlogo}{\vbox{}} \renewcommand{\releasename}{} \makeindex \begin{document} <% if sphinx_version >= (5, 0) %>\ifdefined\shorthandoff \ifnum\catcode`\=\string=\active\shorthandoff{=}\fi \ifnum\catcode`\"=\active\shorthandoff{"}\fi \fi <% endif %>\pagestyle{empty} \sphinxmaketitle \makeatletter\renewcommand{\py@release}{\releasename\space\version}\makeatother \pagestyle{plain} \sphinxtableofcontents \pagestyle{normal} \phantomsection\label{\detokenize{index::doc}} \part{Documentation} <% if sphinx_version > (4, 5) -%> \sphinxstepscope <% endif %> \chapter{from PyPI} \label{\detokenize{installation:installation-0-from_PyPI}}\label{\detokenize{installation:installation-0}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my_project --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-1-from_PyPI}}\label{\detokenize{installation:installation-1}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-2-from_PyPI}}\label{\detokenize{installation:installation-2}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-3-from_Anaconda}}\label{\detokenize{installation:installation-3}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my_project \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-4-from_Anaconda}}\label{\detokenize{installation:installation-4}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-5-from_Anaconda}}\label{\detokenize{installation:installation-5}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-6-from_GitHub}}\label{\detokenize{installation:installation-6}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-7-from_GitHub}}\label{\detokenize{installation:installation-7}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-8-from_PyPI}}\label{\detokenize{installation:installation-8}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my_project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-8-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my_project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-9-from_PyPI}}\label{\detokenize{installation:installation-9}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-9-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-10-from_PyPI}}\label{\detokenize{installation:installation-10}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-10-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-11-from_PyPI}}\label{\detokenize{installation:installation-11}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-11-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-12-from_PyPI}}\label{\detokenize{installation:installation-12}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-12-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-13-from_PyPI}}\label{\detokenize{installation:installation-13}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my_project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-13-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my_project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-13-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-14-from_PyPI}}\label{\detokenize{installation:installation-14}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-14-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-14-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-15-from_PyPI}}\label{\detokenize{installation:installation-15}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-15-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-15-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-16-from_PyPI}}\label{\detokenize{installation:installation-16}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-16-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-16-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-17-from_PyPI}}\label{\detokenize{installation:installation-17}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-17-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-17-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \phantomsection\label{\detokenize{installation:extensions-0}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{foo}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{bar}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{baz}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-1}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{foo}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{bar}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{baz}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZus{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-2}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZus{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-3}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-4}} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-5}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{Collapse} \label{\detokenize{collapse:collapse}}\label{\detokenize{collapse::doc}} Something small enough to escape casual notice. Something else that might escape notice. The text should be visible when the page loads. <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{Confval} \label{\detokenize{confval:confval}}\label{\detokenize{confval::doc}}\index{source\_link\_target (configuration value)@\spxentry{source\_link\_target}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-source_link_target}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{source\_link\_target}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{False}} \item[] \sphinxstylestrong{Default:} \sphinxcode{\sphinxupquote{\textquotesingle{}Sphinx\textquotesingle{}}} \end{DUlineblock} \vspace{-25px} The target of the source link, either \sphinxcode{\sphinxupquote{\textquotesingle{}GitHub\textquotesingle{}}} or \sphinxcode{\sphinxupquote{\textquotesingle{}Sphinx\textquotesingle{}}}. Case insensitive. \end{fulllineitems} \index{github\_username (configuration value)@\spxentry{github\_username}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-github_username}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{github\_username}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{True}} \end{DUlineblock} \end{fulllineitems} \index{github\_repository (configuration value)@\spxentry{github\_repository}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-github_repository}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{github\_repository}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{True}} \end{DUlineblock} \vspace{-25px} The GitHub repository this documentation corresponds to. \end{fulllineitems} \index{conda\_channels (configuration value)@\spxentry{conda\_channels}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-conda_channels}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{conda\_channels}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} Comma\sphinxhyphen{}separated list of strings \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{True}} \end{DUlineblock} \vspace{-25px} The conda channels required to install the library from Anaconda. \end{fulllineitems} \begin{fulllineitems} <% if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{github\_repository}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \end{DUlineblock} \vspace{-25px} This is a duplicate with \sphinxcode{\sphinxupquote{:noindex:}} set. \end{fulllineitems} This is an xref to the {\hyperref[\detokenize{confval:confval-github_repository}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{github\_repository}}}}} configuration option. \index{something (configuration value)@\spxentry{something}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-something}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{something}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-25px} A configuration value. \end{fulllineitems} <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} Code} \label{\detokenize{code-block:sphinx-toolbox-demo-code}}\label{\detokenize{code-block::doc}} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \section{Code Cell} \label{\detokenize{code-block:code-cell}} <%- if sphinx_version >= (4, 1) %> \begin{sphinxuseclass}{code-cell}<% endif %> \vspace{4mm}\llap{\color{nbsphinxin}\texttt{In [1]:}\,\hspace{\fboxrule}\hspace{\fboxrule}\hspace{\fboxsep}}\vspace{-7mm} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \PYG{n+nb}{print}\PYG{p}{(}\PYG{l+s+s2}{\PYGZdq{}}\PYG{l+s+s2}{hello world}\PYG{l+s+s2}{\PYGZdq{}}\PYG{p}{)} \end{sphinxVerbatim} <% if sphinx_version >= (4, 1) %> \end{sphinxuseclass} \begin{sphinxuseclass}{output-cell}<% endif %> \vspace{4mm}\llap{\color{nbsphinxout}\texttt{[1]:}\,\hspace{\fboxrule}\hspace{\fboxrule}\hspace{\fboxsep}}\vspace{-7mm} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{hello} \PYG{n}{world} \end{sphinxVerbatim} <% if sphinx_version >= (4, 1) %> \end{sphinxuseclass} \begin{sphinxuseclass}{code-cell}<% endif %> \vspace{4mm}\llap{\color{nbsphinxin}\texttt{In [2]:}\,\hspace{\fboxrule}\hspace{\fboxrule}\hspace{\fboxsep}}\vspace{-7mm} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} <% if sphinx_version >= (4, 1) %> \end{sphinxuseclass}<% endif %> <%- if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} Formatting} \label{\detokenize{formatting:sphinx-toolbox-demo-formatting}}\label{\detokenize{formatting::doc}} \textit{\sphinxstyleabbreviation{m/z} (mass to charge ratio)} \textit{\sphinxstyleabbreviation{doodad}} \vspace{10px}\sphinxstylestrong{Examples:} \vspace{10px}\sphinxstylestrong{Other Extensions} <%- if sphinx_version > (4, 5) %> \sphinxstepscope<% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} footnotes} \label{\detokenize{footnote_symbols:sphinx-toolbox-demo-footnotes}}\label{\detokenize{footnote_symbols::doc}} Some text % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[2]\sphinxAtStartFootnote An auto\sphinxhyphen{}symbol footnote % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} Some more text % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[3]\sphinxAtStartFootnote Another footnote % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} Sometimes % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[4]\sphinxAtStartFootnote A footnote with lots of content. \sphinxSetupCodeBlockInFootnote \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n+nb}{print}\PYG{p}{(}\PYG{l+s+s2}{\PYGZdq{}}\PYG{l+s+s2}{Look ma! I}\PYG{l+s+s2}{\PYGZsq{}}\PYG{l+s+s2}{m in a footnote}\PYG{l+s+s2}{\PYGZdq{}}\PYG{p}{)} \end{sphinxVerbatim} % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} strange things happen. <%- if sphinx_version > (4, 5) %> \sphinxstepscope<% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} LaTeX} \label{\detokenize{latex:sphinx-toolbox-demo-latex}}\label{\detokenize{latex::doc}} \clearpage \par\begin{samepage} Content under the samepage directive \begin{figure}[htbp] \centering \noindent\sphinxincludegraphics{{75883961}.png} \end{figure} \end{samepage}\par \cleardoublepage The \hspace{0pt}following \hspace{0pt}characters \hspace{0pt}are \hspace{0pt}problematic \hspace{0pt}in \hspace{0pt}LaTeX: \begin{itemize} \item {} $\spadesuit$ \item {} $\heartsuit$ \item {} $\diamondsuit$ \item {} $\clubsuit$ \item {} \textmu{} \item {} $\equiv$ \end{itemize} \clearpage Hello \clearpage \par\begin{samepage} Content under the samepage directive \begin{figure}[htbp] \centering \noindent\sphinxincludegraphics{{75883961}.png} \end{figure} \end{samepage}\par \vspace{-5px} \cleardoublepage The \hspace{0pt}following \hspace{0pt}characters \hspace{0pt}are \hspace{0pt}problematic \hspace{0pt}in \hspace{0pt}LaTeX: \begin{itemize} \item {} $\spadesuit$ \item {} $\heartsuit$ \item {} $\diamondsuit$ \item {} $\clubsuit$ \item {} \textmu{} \item {} $\equiv$ \item {} $\approx$ \end{itemize} This is a \textmu{}\sphinxhyphen{}library for doing something. The Greek letter \textmu{} has several uses. \vspace{10px} \clearpage \vspace{30mm} Goodbyeee! \section{Example Footnotes} \label{\detokenize{latex:example-footnotes}} \begin{DUlineblock}{0em} \item[] Hello % \begin{footnote}[1]\sphinxAtStartFootnote One % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Goodbye % \begin{footnote}[2]\sphinxAtStartFootnote Two % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[2]\sphinxAtStartFootnote Buckle my shoe % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Another Symbol % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[3]\sphinxAtStartFootnote The second symbol % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Number Again % \begin{footnote}[3]\sphinxAtStartFootnote The number after the symbol % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 3 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[4]\sphinxAtStartFootnote Symbol 3 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 4 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[5]\sphinxAtStartFootnote Symbol 4 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 5 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[6]\sphinxAtStartFootnote Symbol 5 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 6 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[7]\sphinxAtStartFootnote Symbol 6 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 7 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[8]\sphinxAtStartFootnote Symbol 7 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 8 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[9]\sphinxAtStartFootnote Symbol 8 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 9 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[10]\sphinxAtStartFootnote Symbol 9 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \end{DUlineblock} \vspace{20px} \sphinxstrong{See also:} Check this out on GitHub <% if sphinx_version >= (6, 2) %>\begin{sphinxseealso}{See also:}<% elif sphinx_version >= (6, 1) %>\begin{sphinxseealso}{See also}<% else %>\sphinxstrong{See also:} <% if sphinx_version >= (3, 4) %>\nopagebreak <% endif %><% endif %> These are helpful resources: \begin{itemize} \item {} Google \item {} StackOverflow \item {} RealPython \end{itemize} <% if sphinx_version >= (6, 1) %> \end{sphinxseealso} <% endif %><% if sphinx_version > (4, 5) %> \sphinxstepscope<% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} Regex} \label{\detokenize{regex:module-sphinx_toolbox.utils}}\label{\detokenize{regex:sphinx-toolbox-demo-regex}}\label{\detokenize{regex::doc}}\index{module@\spxentry{module}!sphinx\_toolbox.utils@\spxentry{sphinx\_toolbox.utils}}\index{sphinx\_toolbox.utils@\spxentry{sphinx\_toolbox.utils}!module@\spxentry{module}} General utility functions. \index{untyped\_param\_regex (in module sphinx\_toolbox.utils)@\spxentry{untyped\_param\_regex}\spxextra{in module sphinx\_toolbox.utils}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:sphinx_toolbox.utils.untyped_param_regex}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{sphinx\_toolbox.utils.}}\sphinxbfcode{\sphinxupquote{untyped\_param\_regex}}} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}}{[}\sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}}{]} Regex to match \sphinxcode{\sphinxupquote{:param \textless{}name\textgreater{}: \textless{}docstring\textgreater{}}} flags. << versionadded("0.8.0") >> \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_at}{\textasciicircum{}}\textcolor{regex_literal}{:}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{p}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{a}\textcolor{regex_literal}{m}\textcolor{regex_branch}{|}\textcolor{regex_literal}{p}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{a}\textcolor{regex_literal}{m}\textcolor{regex_literal}{e}\textcolor{regex_literal}{t}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_branch}{|}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{g}\textcolor{regex_branch}{|}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{g}\textcolor{regex_literal}{u}\textcolor{regex_literal}{m}\textcolor{regex_literal}{e}\textcolor{regex_literal}{n}\textcolor{regex_literal}{t}\textcolor{regex_subpattern}{)}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_category}{\textbackslash{}w}\textcolor{regex_repeat}{*}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{:}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}}} \\ << hline >>\sphinxstyletheadfamily \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.ASCII}{\sphinxcode{\sphinxupquote{re.ASCII}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{typed\_flag\_regex (in module sphinx\_toolbox.utils)@\spxentry{typed\_flag\_regex}\spxextra{in module sphinx\_toolbox.utils}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:sphinx_toolbox.utils.typed_flag_regex}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{sphinx\_toolbox.utils.}}\sphinxbfcode{\sphinxupquote{typed\_flag\_regex}}} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}}{[}\sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}}{]} Regex to match \sphinxcode{\sphinxupquote{:type \textless{}name\textgreater{}: \textless{}type\textgreater{}}} flags. << versionadded("0.8.0") >> \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_at}{\textasciicircum{}}\textcolor{regex_literal}{:}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{p}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{a}\textcolor{regex_literal}{m}\textcolor{regex_literal}{t}\textcolor{regex_literal}{y}\textcolor{regex_literal}{p}\textcolor{regex_literal}{e}\textcolor{regex_branch}{|}\textcolor{regex_literal}{t}\textcolor{regex_literal}{y}\textcolor{regex_literal}{p}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_category}{\textbackslash{}w}\textcolor{regex_repeat}{*}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{:}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}}} \\ << hline >>\sphinxstyletheadfamily \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.ASCII}{\sphinxcode{\sphinxupquote{re.ASCII}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \sphinxcode{\sphinxupquote{\textcolor{regex_at}{\textasciicircum{}}\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{?}}} \index{no\_flags (in module tests.regex\_demo)@\spxentry{no\_flags}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.no_flags}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{no\_flags}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{?}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{one\_flag (in module tests.regex\_demo)@\spxentry{one\_flag}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.one_flag}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{one\_flag}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{?}}} \\<% if sphinx_version >= (5, 3) %> \sphinxhline\sphinxstyletheadfamily<% else %> \hline\sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.IGNORECASE}{\sphinxcode{\sphinxupquote{re.IGNORECASE}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{two\_flags (in module tests.regex\_demo)@\spxentry{two\_flags}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.two_flags}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{two\_flags}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\enspace\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\enspace\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\enspace\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{?}}} \\ << hline >>\sphinxstyletheadfamily \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.ASCII}{\sphinxcode{\sphinxupquote{re.ASCII}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{backticks (in module tests.regex\_demo)@\spxentry{backticks}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.backticks}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{backticks}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{leading\_whitespace (in module tests.regex\_demo)@\spxentry{leading\_whitespace}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.leading_whitespace}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{leading\_whitespace}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_in}{{[}}\enspace\textcolor{regex_in}{{]}}\textcolor{regex_literal}{3}\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{trailing\_whitespace (in module tests.regex\_demo)@\spxentry{trailing\_whitespace}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.trailing_whitespace}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{trailing\_whitespace}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}\enspace\textcolor{regex_literal}{3}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{single\_whitespace (in module tests.regex\_demo)@\spxentry{single\_whitespace}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.single_whitespace}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{single\_whitespace}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_in}{{[}}\enspace\textcolor{regex_in}{{]}}\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_in}{{[}}\enspace\textcolor{regex_in}{{]}}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} GitHub} \label{\detokenize{github:sphinx-toolbox-demo-github}}\label{\detokenize{github::doc}} \section{Issues} \label{\detokenize{github:issues}} \sphinxhref{https://github.com/sphinx-toolbox/sphinx-toolbox/issues/1}{\#1} \sphinxhref{https://github.com/pytest-dev/pytest/issues/7680}{pytest\sphinxhyphen{}dev/pytest\#7680} \section{Pull Requests} \label{\detokenize{github:pull-requests}} \sphinxhref{https://github.com/sphinx-toolbox/sphinx-toolbox/pull/2}{\#2} \sphinxhref{https://github.com/pytest-dev/pytest/pull/7671}{pytest\sphinxhyphen{}dev/pytest\#7671} \section{Repositories} \label{\detokenize{github:repositories}} \sphinxhref{https://github.com/sphinx-toolbox/sphinx-toolbox}{sphinx\sphinxhyphen{}toolbox/sphinx\sphinxhyphen{}toolbox} See more in the \sphinxhref{https://github.com/pytest-dev/pytest}{pytest repository}. \section{Users} \label{\detokenize{github:users}} \sphinxhref{https://github.com/domdfcoding}{@domdfcoding} See more of my \sphinxhref{https://github.com/domdfcoding}{repositories}. \section{Orgs} \label{\detokenize{github:orgs}} \sphinxhref{https://github.com/sphinx-toolbox}{@sphinx\sphinxhyphen{}toolbox} See more repositories in the \sphinxhref{https://github.com/pytest-dev}{pytest\sphinxhyphen{}dev org}. <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{Changeset} \label{\detokenize{changeset:changeset}}\label{\detokenize{changeset::doc}} \section{Removed} \label{\detokenize{changeset:removed}} << versionremoved("1.2.3")>>Use {\hyperref[\detokenize{index:foo}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{foo()}}}}} instead. << versionremoved("1.2.3")>>Due to an unfixable bug this function has been removed. If you desperately need this functionality please write to the mailing list at \sphinxhref{mailto:python-users@example.org}{python\sphinxhyphen{}users@example.org} << versionremoved("1.2.3")>>\begin{itemize} \item {} Removed \sphinxcode{\sphinxupquote{mozarella}} \item {} Removed \sphinxcode{\sphinxupquote{parrot()}} from \sphinxcode{\sphinxupquote{pet\_shop}} \end{itemize} \section{Changed} \label{\detokenize{changeset:changed}} << versionchanged("1.2.3") >>Moved from \sphinxcode{\sphinxupquote{mozarella}} << versionchanged("0.3.0") >>\begin{itemize} \item {} Parameters for \sphinxcode{\sphinxupquote{\_\_init\_\_}} can be documented either in the class docstring or alongside the attribute. The class docstring has priority. \item {} Added support for \sphinxhref{https://github.com/Chilipp/autodocsumm}{autodocsumm}. \end{itemize} \index{built\sphinxhyphen{}in function@\spxentry{built\sphinxhyphen{}in function}!foo()@\spxentry{foo()}}\index{foo()@\spxentry{foo()}!built\sphinxhyphen{}in function@\spxentry{built\sphinxhyphen{}in function}} \begin{fulllineitems} \phantomsection\label{\detokenize{index:foo}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{foo}}}<< sphinx81_linebreak >>{\<>{\DUrole{n<< du_role_2nd_n >>}{bar}}<>{\DUrole{n<< du_role_2nd_n >>}{baz}}}<< sphinx81_linebreak >>{}<< tilde >> <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif -%> \begin{quote}\begin{description} <% if sphinx_version >= (5, 0) %>\sphinxlineitem{Parameters}<% else %>\item[{Parameters}] \leavevmode<% endif %>\begin{itemize} \item {} \sphinxstyleliteralstrong{\sphinxupquote{bar}} \textendash{} Does something \item {} \sphinxstyleliteralstrong{\sphinxupquote{baz}} \textendash{} Does something else \end{itemize} \end{description}\end{quote} \end{fulllineitems} \vspace{10px} \sphinxstylestrong{Classes:} \vspace{-5px} \begin{savenotes}<%- if sphinx_version > (8, 2) %> \sphinxatlongtablestart \sphinxthistablewithglobalstyle \sphinxthistablewithnovlinesstyle \makeatletter \LTleft \@totalleftmargin plus1fill \LTright\dimexpr\columnwidth-\@totalleftmargin-\linewidth\relax plus1fill \makeatother \begin{longtable}{\X{1}{2}\X{1}{2}} \sphinxtoprule \endfirsthead \multicolumn{2}{c}{\sphinxnorowcolor \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}% }\\ \sphinxtoprule \endhead \sphinxbottomrule \multicolumn{2}{r}{\sphinxnorowcolor \makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}% }\\ \endfoot \endlastfoot \sphinxtableatstartofbodyhook <% elif sphinx_version > (4, 5) -%> \sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle \sphinxthistablewithnovlinesstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{\X{1}{2}\X{1}{2}}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook<% else %> \hline <%- endif %><% else %>\sphinxatlongtablestart\begin{longtable}[c]{\X{1}{2}\X{1}{2}} \hline \endfirsthead \multicolumn{2}{c}% {\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}}\\ \hline \endhead \hline \multicolumn{2}{r}{\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}}\\ \endfoot \endlastfoot <%- endif %> <% if sphinx_version < (5, 3) %> <% endif %>\sphinxcode{\sphinxupquote{Animal}}(name, voice) & An animal. \\ << hline >> \sphinxcode{\sphinxupquote{Employee}}(name{[}, id{]}) & Represents an employee. \\ << hline >> \sphinxcode{\sphinxupquote{Movie}}(name, year, based\_on) & Represents a movie. \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> <% if sphinx_version > (8, 2) -%> \end{longtable} \sphinxtableafterendhook \sphinxatlongtableend \end{savenotes}<% elif sphinx_version > (4, 5) -%> \end{tabulary} << par >> \sphinxattableend\end{savenotes} <%- else -%> \end{longtable}\sphinxatlongtableend\end{savenotes} <%- endif %> \bookmarksetupnext{{level=part}} \renewcommand{\indexname}{Python Module Index} \begin{sphinxtheindex} \let\bigletter\sphinxstyleindexlettergroup \bigletter{s} \item\relax\sphinxstyleindexentry{sphinx\_toolbox.utils}\sphinxstyleindexpageref{regex:\detokenize{module-sphinx_toolbox.utils}} \end{sphinxtheindex} \bookmarksetupnext{{level=part}} \renewcommand{\indexname}{Index} \printindex \end{document} sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_latex_output_autosummary_col_type.tex000066400000000000000000001737171475757360300337420ustar00rootroot00000000000000<% set tilde = "~" if sphinx_version < (4, 4) else "" -%> <%- set hline = "\\sphinxhline" if sphinx_version >= (5, 3) else "\\hline" -%> <%- set par = "\\sphinxtableafterendhook\\par" if sphinx_version >= (5, 3) else "\\par" -%> <%- set sphinxparam = "sphinxparam" if sphinx_version >= (6, 2) else "emph" -%> <%- if sphinx_version >= (7, 1) -%> <%- set sphinxparamcomma = "\\sphinxparamcomma \\sphinxparam" -%> <%- elif sphinx_version >= (6, 2) -%> <%- set sphinxparamcomma = ", \\sphinxparam" -%> <%- else -%> <%- set sphinxparamcomma = ", \\emph" -%> <%- endif -%> <%- set du_role_2nd_n = ",n" if (7, 2) > sphinx_version >= (6, 2) else "" -%> <# Latest Sphinx removed vertical rules from csv-table. Inconsequential for us #> <%- set regex_columns = "TT" if sphinx_version >= (6, 0) else "|T|T|" -%> <%- set sphinx81_linebreak = "\n" if sphinx_version >= (8, 1) else "" -%> <%- macro versionadded(v) -%> <%- if sphinx_version >= (8, 1) %>\DUrole{versionmodified}{\DUrole{added}{Added in version <>.}} <%- else %>\DUrole{versionmodified,added}{<% if sphinx_version >= (7, 3) %>Added<% else %>New<% endif %> in version <>.}<% endif %> <%- endmacro -%> <%- macro versionremoved(v) -%> <%- if sphinx_version >= (8, 1) %>\DUrole{versionmodified}{\DUrole{removed}{Removed in version <>: }} <%- else %>\DUrole{versionmodified,removed}{Removed in version <>: } <%- endif %><%- endmacro -%> <%- macro versionchanged(v) -%> <%- if sphinx_version >= (8, 1) %>\DUrole{versionmodified}{\DUrole{changed}{Changed in version <>: }} <%- else %>\DUrole{versionmodified,changed}{Changed in version <>: } <%- endif %><%- endmacro -%> %% Generated by Sphinx. \def\sphinxdocclass{report} \documentclass[letterpaper,10pt,english]{sphinxmanual} \ifdefined\pdfpxdimen \let\sphinxpxdimen\pdfpxdimen\else\newdimen\sphinxpxdimen \fi \sphinxpxdimen=.75bp\relax <% if sphinx_version >= (4, 0) %>\ifdefined\pdfimageresolution \pdfimageresolution= \numexpr \dimexpr1in\relax/\sphinxpxdimen\relax \fi %% let collapsable pdf bookmarks panel have high depth per default \PassOptionsToPackage{bookmarksdepth=5}{hyperref}<% if sphinx_version >= (5, 3) %> <% endif %><% if sphinx_version >= (6, 0) %> \PassOptionsToPackage{booktabs}{sphinx} \PassOptionsToPackage{colorrows}{sphinx}<% endif %> <% endif %> \PassOptionsToPackage{warn}{textcomp} \usepackage[utf8]{inputenc} \ifdefined\DeclareUnicodeCharacter % support both utf8 and utf8x syntaxes \ifdefined\DeclareUnicodeCharacterAsOptional \def\sphinxDUC#1{\DeclareUnicodeCharacter{"#1}} \else \let\sphinxDUC\DeclareUnicodeCharacter \fi \sphinxDUC{00A0}{\nobreakspace} \sphinxDUC{2500}{\sphinxunichar{2500}} \sphinxDUC{2502}{\sphinxunichar{2502}} \sphinxDUC{2514}{\sphinxunichar{2514}} \sphinxDUC{251C}{\sphinxunichar{251C}} \sphinxDUC{2572}{\textbackslash} \fi \usepackage{cmap} \usepackage[T1]{fontenc} \usepackage{amsmath,amssymb,amstext} \usepackage{babel} <%+ if sphinx_version >= (4, 0) %> \usepackage{tgtermes} \usepackage{tgheros} \renewcommand{\ttdefault}{txtt} <% else %> \usepackage{times} \expandafter\ifx\csname T@LGR\endcsname\relax \else % LGR was declared as font encoding \substitutefont{LGR}{\rmdefault}{cmr} \substitutefont{LGR}{\sfdefault}{cmss} \substitutefont{LGR}{\ttdefault}{cmtt} \fi \expandafter\ifx\csname T@X2\endcsname\relax \expandafter\ifx\csname T@T2A\endcsname\relax \else % T2A was declared as font encoding \substitutefont{T2A}{\rmdefault}{cmr} \substitutefont{T2A}{\sfdefault}{cmss} \substitutefont{T2A}{\ttdefault}{cmtt} \fi \else % X2 was declared as font encoding \substitutefont{X2}{\rmdefault}{cmr} \substitutefont{X2}{\sfdefault}{cmss} \substitutefont{X2}{\ttdefault}{cmtt} \fi <% endif %> \usepackage[Bjarne]{fncychap} \usepackage{sphinx} <% if sphinx_version >= (4, 0) %> \fvset{fontsize=auto} <% else %> \fvset{fontsize=\small} <% endif -%> \usepackage{geometry} % Include hyperref last. \usepackage{hyperref} % Fix anchor placement for figures with captions. \usepackage{hypcap}% it must be loaded after hyperref. % Set up styles of URL: it should be placed after hyperref. \urlstyle{same} \usepackage{sphinxmessages} \setcounter{tocdepth}{1} \definecolor{nbsphinxin}{HTML}{307FC1} \definecolor{nbsphinxout}{HTML}{BF5B3D} \newcommand\thesymbolfootnote{\fnsymbol{footnote}}\let\thenumberfootnote\thefootnote \definecolor{regex_literal}{HTML}{696969} \definecolor{regex_at}{HTML}{FF4500} \definecolor{regex_repeat_brace}{HTML}{FF4500} \definecolor{regex_branch}{HTML}{FF4500} \definecolor{regex_subpattern}{HTML}{1e90ff} \definecolor{regex_in}{HTML}{ff8c00} \definecolor{regex_category}{HTML}{8fbc8f} \definecolor{regex_repeat}{HTML}{FF4500} \definecolor{regex_any}{HTML}{FF4500} \usepackage[]{bookmark} \newcommand{\thesummary}{This is an awesome tool! πŸš€ \textasciitilde\space intersphinx\_mapping \# 100\% β€˜Quotes’} \makeatletter \renewcommand{\py@release}{ \releasename\space\version \par \vspace{25pt} \textup{\thesummary} } \makeatother \title{Python} \date{Mar 11, 2021} \release{} \author{unknown} \newcommand{\sphinxlogo}{\vbox{}} \renewcommand{\releasename}{} \makeindex \begin{document} <% if sphinx_version >= (5, 0) %>\ifdefined\shorthandoff \ifnum\catcode`\=\string=\active\shorthandoff{=}\fi \ifnum\catcode`\"=\active\shorthandoff{"}\fi \fi <% endif %>\pagestyle{empty} \sphinxmaketitle \makeatletter\renewcommand{\py@release}{\releasename\space\version}\makeatother \pagestyle{plain} \sphinxtableofcontents \pagestyle{normal} \phantomsection\label{\detokenize{index::doc}} \part{Documentation} <% if sphinx_version > (4, 5) -%> \sphinxstepscope <% endif %> \chapter{from PyPI} \label{\detokenize{installation:installation-0-from_PyPI}}\label{\detokenize{installation:installation-0}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my_project --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-1-from_PyPI}}\label{\detokenize{installation:installation-1}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-2-from_PyPI}}\label{\detokenize{installation:installation-2}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-3-from_Anaconda}}\label{\detokenize{installation:installation-3}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my_project \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-4-from_Anaconda}}\label{\detokenize{installation:installation-4}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-5-from_Anaconda}}\label{\detokenize{installation:installation-5}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-6-from_GitHub}}\label{\detokenize{installation:installation-6}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-7-from_GitHub}}\label{\detokenize{installation:installation-7}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-8-from_PyPI}}\label{\detokenize{installation:installation-8}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my_project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-8-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my_project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-9-from_PyPI}}\label{\detokenize{installation:installation-9}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-9-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-10-from_PyPI}}\label{\detokenize{installation:installation-10}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-10-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-11-from_PyPI}}\label{\detokenize{installation:installation-11}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-11-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-12-from_PyPI}}\label{\detokenize{installation:installation-12}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-12-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-13-from_PyPI}}\label{\detokenize{installation:installation-13}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my_project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-13-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my_project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-13-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-14-from_PyPI}}\label{\detokenize{installation:installation-14}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-14-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-14-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-15-from_PyPI}}\label{\detokenize{installation:installation-15}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-15-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-15-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-16-from_PyPI}}\label{\detokenize{installation:installation-16}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-16-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-16-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-17-from_PyPI}}\label{\detokenize{installation:installation-17}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-17-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-17-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \phantomsection\label{\detokenize{installation:extensions-0}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{foo}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{bar}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{baz}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-1}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{foo}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{bar}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{baz}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZus{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-2}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZus{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-3}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-4}} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-5}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{Collapse} \label{\detokenize{collapse:collapse}}\label{\detokenize{collapse::doc}} Something small enough to escape casual notice. Something else that might escape notice. The text should be visible when the page loads. <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{Confval} \label{\detokenize{confval:confval}}\label{\detokenize{confval::doc}}\index{source\_link\_target (configuration value)@\spxentry{source\_link\_target}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-source_link_target}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{source\_link\_target}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{False}} \item[] \sphinxstylestrong{Default:} \sphinxcode{\sphinxupquote{\textquotesingle{}Sphinx\textquotesingle{}}} \end{DUlineblock} \vspace{-25px} The target of the source link, either \sphinxcode{\sphinxupquote{\textquotesingle{}GitHub\textquotesingle{}}} or \sphinxcode{\sphinxupquote{\textquotesingle{}Sphinx\textquotesingle{}}}. Case insensitive. \end{fulllineitems} \index{github\_username (configuration value)@\spxentry{github\_username}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-github_username}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{github\_username}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{True}} \end{DUlineblock} \end{fulllineitems} \index{github\_repository (configuration value)@\spxentry{github\_repository}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-github_repository}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{github\_repository}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{True}} \end{DUlineblock} \vspace{-25px} The GitHub repository this documentation corresponds to. \end{fulllineitems} \index{conda\_channels (configuration value)@\spxentry{conda\_channels}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-conda_channels}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{conda\_channels}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} Comma\sphinxhyphen{}separated list of strings \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{True}} \end{DUlineblock} \vspace{-25px} The conda channels required to install the library from Anaconda. \end{fulllineitems} \begin{fulllineitems} <% if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{github\_repository}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \end{DUlineblock} \vspace{-25px} This is a duplicate with \sphinxcode{\sphinxupquote{:noindex:}} set. \end{fulllineitems} This is an xref to the {\hyperref[\detokenize{confval:confval-github_repository}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{github\_repository}}}}} configuration option. \index{something (configuration value)@\spxentry{something}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-something}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{something}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-25px} A configuration value. \end{fulllineitems} <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} Code} \label{\detokenize{code-block:sphinx-toolbox-demo-code}}\label{\detokenize{code-block::doc}} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \section{Code Cell} \label{\detokenize{code-block:code-cell}} <%- if sphinx_version >= (4, 1) %> \begin{sphinxuseclass}{code-cell}<% endif %> \vspace{4mm}\llap{\color{nbsphinxin}\texttt{In [1]:}\,\hspace{\fboxrule}\hspace{\fboxrule}\hspace{\fboxsep}}\vspace{-7mm} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \PYG{n+nb}{print}\PYG{p}{(}\PYG{l+s+s2}{\PYGZdq{}}\PYG{l+s+s2}{hello world}\PYG{l+s+s2}{\PYGZdq{}}\PYG{p}{)} \end{sphinxVerbatim} <% if sphinx_version >= (4, 1) %> \end{sphinxuseclass} \begin{sphinxuseclass}{output-cell}<% endif %> \vspace{4mm}\llap{\color{nbsphinxout}\texttt{[1]:}\,\hspace{\fboxrule}\hspace{\fboxrule}\hspace{\fboxsep}}\vspace{-7mm} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{hello} \PYG{n}{world} \end{sphinxVerbatim} <% if sphinx_version >= (4, 1) %> \end{sphinxuseclass} \begin{sphinxuseclass}{code-cell}<% endif %> \vspace{4mm}\llap{\color{nbsphinxin}\texttt{In [2]:}\,\hspace{\fboxrule}\hspace{\fboxrule}\hspace{\fboxsep}}\vspace{-7mm} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} <% if sphinx_version >= (4, 1) %> \end{sphinxuseclass}<% endif %> <%- if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} Formatting} \label{\detokenize{formatting:sphinx-toolbox-demo-formatting}}\label{\detokenize{formatting::doc}} \textit{\sphinxstyleabbreviation{m/z} (mass to charge ratio)} \textit{\sphinxstyleabbreviation{doodad}} \vspace{10px}\sphinxstylestrong{Examples:} \vspace{10px}\sphinxstylestrong{Other Extensions} <%- if sphinx_version > (4, 5) %> \sphinxstepscope<% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} footnotes} \label{\detokenize{footnote_symbols:sphinx-toolbox-demo-footnotes}}\label{\detokenize{footnote_symbols::doc}} Some text % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[2]\sphinxAtStartFootnote An auto\sphinxhyphen{}symbol footnote % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} Some more text % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[3]\sphinxAtStartFootnote Another footnote % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} Sometimes % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[4]\sphinxAtStartFootnote A footnote with lots of content. \sphinxSetupCodeBlockInFootnote \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n+nb}{print}\PYG{p}{(}\PYG{l+s+s2}{\PYGZdq{}}\PYG{l+s+s2}{Look ma! I}\PYG{l+s+s2}{\PYGZsq{}}\PYG{l+s+s2}{m in a footnote}\PYG{l+s+s2}{\PYGZdq{}}\PYG{p}{)} \end{sphinxVerbatim} % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} strange things happen. <%- if sphinx_version > (4, 5) %> \sphinxstepscope<% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} LaTeX} \label{\detokenize{latex:sphinx-toolbox-demo-latex}}\label{\detokenize{latex::doc}} \clearpage \par\begin{samepage} Content under the samepage directive \begin{figure}[htbp] \centering \noindent\sphinxincludegraphics{{75883961}.png} \end{figure} \end{samepage}\par \cleardoublepage The \hspace{0pt}following \hspace{0pt}characters \hspace{0pt}are \hspace{0pt}problematic \hspace{0pt}in \hspace{0pt}LaTeX: \begin{itemize} \item {} $\spadesuit$ \item {} $\heartsuit$ \item {} $\diamondsuit$ \item {} $\clubsuit$ \item {} \textmu{} \item {} $\equiv$ \end{itemize} \clearpage Hello \clearpage \par\begin{samepage} Content under the samepage directive \begin{figure}[htbp] \centering \noindent\sphinxincludegraphics{{75883961}.png} \end{figure} \end{samepage}\par \vspace{-5px} \cleardoublepage The \hspace{0pt}following \hspace{0pt}characters \hspace{0pt}are \hspace{0pt}problematic \hspace{0pt}in \hspace{0pt}LaTeX: \begin{itemize} \item {} $\spadesuit$ \item {} $\heartsuit$ \item {} $\diamondsuit$ \item {} $\clubsuit$ \item {} \textmu{} \item {} $\equiv$ \item {} $\approx$ \end{itemize} This is a \textmu{}\sphinxhyphen{}library for doing something. The Greek letter \textmu{} has several uses. \vspace{10px} \clearpage \vspace{30mm} Goodbyeee! \section{Example Footnotes} \label{\detokenize{latex:example-footnotes}} \begin{DUlineblock}{0em} \item[] Hello % \begin{footnote}[1]\sphinxAtStartFootnote One % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Goodbye % \begin{footnote}[2]\sphinxAtStartFootnote Two % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[2]\sphinxAtStartFootnote Buckle my shoe % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Another Symbol % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[3]\sphinxAtStartFootnote The second symbol % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Number Again % \begin{footnote}[3]\sphinxAtStartFootnote The number after the symbol % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 3 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[4]\sphinxAtStartFootnote Symbol 3 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 4 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[5]\sphinxAtStartFootnote Symbol 4 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 5 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[6]\sphinxAtStartFootnote Symbol 5 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 6 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[7]\sphinxAtStartFootnote Symbol 6 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 7 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[8]\sphinxAtStartFootnote Symbol 7 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 8 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[9]\sphinxAtStartFootnote Symbol 8 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 9 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[10]\sphinxAtStartFootnote Symbol 9 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \end{DUlineblock} \vspace{20px} \sphinxstrong{See also:} Check this out on GitHub <% if sphinx_version >= (6, 2) %>\begin{sphinxseealso}{See also:}<% elif sphinx_version >= (6, 1) %>\begin{sphinxseealso}{See also}<% else %>\sphinxstrong{See also:} <% if sphinx_version >= (3, 4) %>\nopagebreak <% endif %><% endif %> These are helpful resources: \begin{itemize} \item {} Google \item {} StackOverflow \item {} RealPython \end{itemize} <% if sphinx_version >= (6, 1) %> \end{sphinxseealso} <% endif %><% if sphinx_version > (4, 5) %> \sphinxstepscope<% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} Regex} \label{\detokenize{regex:module-sphinx_toolbox.utils}}\label{\detokenize{regex:sphinx-toolbox-demo-regex}}\label{\detokenize{regex::doc}}\index{module@\spxentry{module}!sphinx\_toolbox.utils@\spxentry{sphinx\_toolbox.utils}}\index{sphinx\_toolbox.utils@\spxentry{sphinx\_toolbox.utils}!module@\spxentry{module}} General utility functions. \index{untyped\_param\_regex (in module sphinx\_toolbox.utils)@\spxentry{untyped\_param\_regex}\spxextra{in module sphinx\_toolbox.utils}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:sphinx_toolbox.utils.untyped_param_regex}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{sphinx\_toolbox.utils.}}\sphinxbfcode{\sphinxupquote{untyped\_param\_regex}}} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}}{[}\sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}}{]} Regex to match \sphinxcode{\sphinxupquote{:param \textless{}name\textgreater{}: \textless{}docstring\textgreater{}}} flags. << versionadded("0.8.0") >> \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_at}{\textasciicircum{}}\textcolor{regex_literal}{:}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{p}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{a}\textcolor{regex_literal}{m}\textcolor{regex_branch}{|}\textcolor{regex_literal}{p}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{a}\textcolor{regex_literal}{m}\textcolor{regex_literal}{e}\textcolor{regex_literal}{t}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_branch}{|}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{g}\textcolor{regex_branch}{|}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{g}\textcolor{regex_literal}{u}\textcolor{regex_literal}{m}\textcolor{regex_literal}{e}\textcolor{regex_literal}{n}\textcolor{regex_literal}{t}\textcolor{regex_subpattern}{)}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_category}{\textbackslash{}w}\textcolor{regex_repeat}{*}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{:}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}}} \\ << hline >>\sphinxstyletheadfamily \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.ASCII}{\sphinxcode{\sphinxupquote{re.ASCII}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{typed\_flag\_regex (in module sphinx\_toolbox.utils)@\spxentry{typed\_flag\_regex}\spxextra{in module sphinx\_toolbox.utils}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:sphinx_toolbox.utils.typed_flag_regex}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{sphinx\_toolbox.utils.}}\sphinxbfcode{\sphinxupquote{typed\_flag\_regex}}} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}}{[}\sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}}{]} Regex to match \sphinxcode{\sphinxupquote{:type \textless{}name\textgreater{}: \textless{}type\textgreater{}}} flags. << versionadded("0.8.0") >> \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_at}{\textasciicircum{}}\textcolor{regex_literal}{:}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{p}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{a}\textcolor{regex_literal}{m}\textcolor{regex_literal}{t}\textcolor{regex_literal}{y}\textcolor{regex_literal}{p}\textcolor{regex_literal}{e}\textcolor{regex_branch}{|}\textcolor{regex_literal}{t}\textcolor{regex_literal}{y}\textcolor{regex_literal}{p}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_category}{\textbackslash{}w}\textcolor{regex_repeat}{*}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{:}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}}} \\ << hline >>\sphinxstyletheadfamily \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.ASCII}{\sphinxcode{\sphinxupquote{re.ASCII}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \sphinxcode{\sphinxupquote{\textcolor{regex_at}{\textasciicircum{}}\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{?}}} \index{no\_flags (in module tests.regex\_demo)@\spxentry{no\_flags}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.no_flags}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{no\_flags}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{?}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{one\_flag (in module tests.regex\_demo)@\spxentry{one\_flag}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.one_flag}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{one\_flag}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{?}}} \\<% if sphinx_version >= (5, 3) %> \sphinxhline\sphinxstyletheadfamily<% else %> \hline\sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.IGNORECASE}{\sphinxcode{\sphinxupquote{re.IGNORECASE}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{two\_flags (in module tests.regex\_demo)@\spxentry{two\_flags}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.two_flags}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{two\_flags}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\enspace\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\enspace\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\enspace\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{?}}} \\ << hline >>\sphinxstyletheadfamily \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.ASCII}{\sphinxcode{\sphinxupquote{re.ASCII}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{backticks (in module tests.regex\_demo)@\spxentry{backticks}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.backticks}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{backticks}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{leading\_whitespace (in module tests.regex\_demo)@\spxentry{leading\_whitespace}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.leading_whitespace}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{leading\_whitespace}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_in}{{[}}\enspace\textcolor{regex_in}{{]}}\textcolor{regex_literal}{3}\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{trailing\_whitespace (in module tests.regex\_demo)@\spxentry{trailing\_whitespace}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.trailing_whitespace}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{trailing\_whitespace}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}\enspace\textcolor{regex_literal}{3}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{single\_whitespace (in module tests.regex\_demo)@\spxentry{single\_whitespace}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.single_whitespace}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{single\_whitespace}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_in}{{[}}\enspace\textcolor{regex_in}{{]}}\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_in}{{[}}\enspace\textcolor{regex_in}{{]}}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} GitHub} \label{\detokenize{github:sphinx-toolbox-demo-github}}\label{\detokenize{github::doc}} \section{Issues} \label{\detokenize{github:issues}} \sphinxhref{https://github.com/sphinx-toolbox/sphinx-toolbox/issues/1}{\#1} \sphinxhref{https://github.com/pytest-dev/pytest/issues/7680}{pytest\sphinxhyphen{}dev/pytest\#7680} \section{Pull Requests} \label{\detokenize{github:pull-requests}} \sphinxhref{https://github.com/sphinx-toolbox/sphinx-toolbox/pull/2}{\#2} \sphinxhref{https://github.com/pytest-dev/pytest/pull/7671}{pytest\sphinxhyphen{}dev/pytest\#7671} \section{Repositories} \label{\detokenize{github:repositories}} \sphinxhref{https://github.com/sphinx-toolbox/sphinx-toolbox}{sphinx\sphinxhyphen{}toolbox/sphinx\sphinxhyphen{}toolbox} See more in the \sphinxhref{https://github.com/pytest-dev/pytest}{pytest repository}. \section{Users} \label{\detokenize{github:users}} \sphinxhref{https://github.com/domdfcoding}{@domdfcoding} See more of my \sphinxhref{https://github.com/domdfcoding}{repositories}. \section{Orgs} \label{\detokenize{github:orgs}} \sphinxhref{https://github.com/sphinx-toolbox}{@sphinx\sphinxhyphen{}toolbox} See more repositories in the \sphinxhref{https://github.com/pytest-dev}{pytest\sphinxhyphen{}dev org}. <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{Changeset} \label{\detokenize{changeset:changeset}}\label{\detokenize{changeset::doc}} \section{Removed} \label{\detokenize{changeset:removed}} << versionremoved("1.2.3")>>Use {\hyperref[\detokenize{index:foo}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{foo()}}}}} instead. << versionremoved("1.2.3")>>Due to an unfixable bug this function has been removed. If you desperately need this functionality please write to the mailing list at \sphinxhref{mailto:python-users@example.org}{python\sphinxhyphen{}users@example.org} << versionremoved("1.2.3")>>\begin{itemize} \item {} Removed \sphinxcode{\sphinxupquote{mozarella}} \item {} Removed \sphinxcode{\sphinxupquote{parrot()}} from \sphinxcode{\sphinxupquote{pet\_shop}} \end{itemize} \section{Changed} \label{\detokenize{changeset:changed}} << versionchanged("1.2.3") >>Moved from \sphinxcode{\sphinxupquote{mozarella}} << versionchanged("0.3.0") >>\begin{itemize} \item {} Parameters for \sphinxcode{\sphinxupquote{\_\_init\_\_}} can be documented either in the class docstring or alongside the attribute. The class docstring has priority. \item {} Added support for \sphinxhref{https://github.com/Chilipp/autodocsumm}{autodocsumm}. \end{itemize} \index{built\sphinxhyphen{}in function@\spxentry{built\sphinxhyphen{}in function}!foo()@\spxentry{foo()}}\index{foo()@\spxentry{foo()}!built\sphinxhyphen{}in function@\spxentry{built\sphinxhyphen{}in function}} \begin{fulllineitems} \phantomsection\label{\detokenize{index:foo}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{foo}}}<< sphinx81_linebreak >>{\<>{\DUrole{n<< du_role_2nd_n >>}{bar}}<>{\DUrole{n<< du_role_2nd_n >>}{baz}}}<< sphinx81_linebreak >>{}<< tilde >> <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif -%> \begin{quote}\begin{description} <% if sphinx_version >= (5, 0) %>\sphinxlineitem{Parameters}<% else %>\item[{Parameters}] \leavevmode<% endif %>\begin{itemize} \item {} \sphinxstyleliteralstrong{\sphinxupquote{bar}} \textendash{} Does something \item {} \sphinxstyleliteralstrong{\sphinxupquote{baz}} \textendash{} Does something else \end{itemize} \end{description}\end{quote} \end{fulllineitems} \vspace{10px} \sphinxstylestrong{Classes:} \vspace{-5px} \begin{savenotes}<%- if sphinx_version > (8, 2) %> \sphinxatlongtablestart \sphinxthistablewithglobalstyle \sphinxthistablewithnovlinesstyle \makeatletter \LTleft \@totalleftmargin plus1fill \LTright\dimexpr\columnwidth-\@totalleftmargin-\linewidth\relax plus1fill \makeatother \begin{longtable}{\Y{1}{2}\Y{1}{2}} \sphinxtoprule \endfirsthead \multicolumn{2}{c}{\sphinxnorowcolor \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}% }\\ \sphinxtoprule \endhead \sphinxbottomrule \multicolumn{2}{r}{\sphinxnorowcolor \makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}% }\\ \endfoot \endlastfoot \sphinxtableatstartofbodyhook <% elif sphinx_version > (4, 5) -%> \sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle \sphinxthistablewithnovlinesstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{\Y{1}{2}\Y{1}{2}}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook<% else %> \hline <%- endif %><% else %>\sphinxatlongtablestart\begin{longtable}[c]{\Y{1}{2}\Y{1}{2}} \hline \endfirsthead \multicolumn{2}{c}% {\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}}\\ \hline \endhead \hline \multicolumn{2}{r}{\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}}\\ \endfoot \endlastfoot <%- endif %> <% if sphinx_version < (5, 3) %> <% endif %>\sphinxcode{\sphinxupquote{Animal}}(name, voice) & An animal. \\ << hline >> \sphinxcode{\sphinxupquote{Employee}}(name{[}, id{]}) & Represents an employee. \\ << hline >> \sphinxcode{\sphinxupquote{Movie}}(name, year, based\_on) & Represents a movie. \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> <% if sphinx_version > (8, 2) -%> \end{longtable} \sphinxtableafterendhook \sphinxatlongtableend \end{savenotes}<% elif sphinx_version > (4, 5) -%> \end{tabulary} << par >> \sphinxattableend\end{savenotes} <%- else -%> \end{longtable}\sphinxatlongtableend\end{savenotes} <%- endif %> \bookmarksetupnext{{level=part}} \renewcommand{\indexname}{Python Module Index} \begin{sphinxtheindex} \let\bigletter\sphinxstyleindexlettergroup \bigletter{s} \item\relax\sphinxstyleindexentry{sphinx\_toolbox.utils}\sphinxstyleindexpageref{regex:\detokenize{module-sphinx_toolbox.utils}} \end{sphinxtheindex} \bookmarksetupnext{{level=part}} \renewcommand{\indexname}{Index} \printindex \end{document} sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_latex_output_better_header_layout.tex000066400000000000000000001746511475757360300336460ustar00rootroot00000000000000<% set tilde = "~" if sphinx_version < (4, 4) else "" -%> <%- set hline = "\\sphinxhline" if sphinx_version >= (5, 3) else "\\hline" -%> <%- set par = "\\sphinxtableafterendhook\\par" if sphinx_version >= (5, 3) else "\\par" -%> <%- set sphinxparam = "sphinxparam" if sphinx_version >= (6, 2) else "emph" -%> <%- if sphinx_version >= (7, 1) %> <%- set sphinxparamcomma = "\\sphinxparamcomma \\sphinxparam" -%> <%- elif sphinx_version >= (6, 2) -%> <%- set sphinxparamcomma = ", \\sphinxparam" -%> <%- else -%> <%- set sphinxparamcomma = ", \\emph" -%> <%- endif -%> <%- set du_role_2nd_n = ",n" if (7, 2) > sphinx_version >= (6, 2) else "" -%> <# Latest Sphinx removed vertical rules from csv-table. Inconsequential for us #> <%- set regex_columns = "TT" if sphinx_version >= (6, 0) else "|T|T|" -%> <%- set sphinx81_linebreak = "\n" if sphinx_version >= (8, 1) else "" -%> <%- macro versionadded(v) -%> <%- if sphinx_version >= (8, 1) %>\DUrole{versionmodified}{\DUrole{added}{Added in version <>.}} <%- else %>\DUrole{versionmodified,added}{<% if sphinx_version >= (7, 3) %>Added<% else %>New<% endif %> in version <>.}<% endif %> <%- endmacro -%> <%- macro versionremoved(v) -%> <%- if sphinx_version >= (8, 1) %>\DUrole{versionmodified}{\DUrole{removed}{Removed in version <>: }} <%- else %>\DUrole{versionmodified,removed}{Removed in version <>: } <%- endif %><%- endmacro -%> <%- macro versionchanged(v) -%> <%- if sphinx_version >= (8, 1) %>\DUrole{versionmodified}{\DUrole{changed}{Changed in version <>: }} <%- else %>\DUrole{versionmodified,changed}{Changed in version <>: } <%- endif %><%- endmacro -%> %% Generated by Sphinx. \def\sphinxdocclass{report} \documentclass[letterpaper,10pt,english]{sphinxmanual} \ifdefined\pdfpxdimen \let\sphinxpxdimen\pdfpxdimen\else\newdimen\sphinxpxdimen \fi \sphinxpxdimen=.75bp\relax <% if sphinx_version >= (4, 0) %>\ifdefined\pdfimageresolution \pdfimageresolution= \numexpr \dimexpr1in\relax/\sphinxpxdimen\relax \fi %% let collapsable pdf bookmarks panel have high depth per default \PassOptionsToPackage{bookmarksdepth=5}{hyperref}<% if sphinx_version >= (5, 3) %> <% endif %><% if sphinx_version >= (6, 0) %> \PassOptionsToPackage{booktabs}{sphinx} \PassOptionsToPackage{colorrows}{sphinx}<% endif %> <% endif %> \PassOptionsToPackage{warn}{textcomp} \usepackage[utf8]{inputenc} \ifdefined\DeclareUnicodeCharacter % support both utf8 and utf8x syntaxes \ifdefined\DeclareUnicodeCharacterAsOptional \def\sphinxDUC#1{\DeclareUnicodeCharacter{"#1}} \else \let\sphinxDUC\DeclareUnicodeCharacter \fi \sphinxDUC{00A0}{\nobreakspace} \sphinxDUC{2500}{\sphinxunichar{2500}} \sphinxDUC{2502}{\sphinxunichar{2502}} \sphinxDUC{2514}{\sphinxunichar{2514}} \sphinxDUC{251C}{\sphinxunichar{251C}} \sphinxDUC{2572}{\textbackslash} \fi \usepackage{cmap} \usepackage[T1]{fontenc} \usepackage{amsmath,amssymb,amstext} \usepackage{babel} <%+ if sphinx_version >= (4, 0) %> \usepackage{tgtermes} \usepackage{tgheros} \renewcommand{\ttdefault}{txtt} <% else %> \usepackage{times} \expandafter\ifx\csname T@LGR\endcsname\relax \else % LGR was declared as font encoding \substitutefont{LGR}{\rmdefault}{cmr} \substitutefont{LGR}{\sfdefault}{cmss} \substitutefont{LGR}{\ttdefault}{cmtt} \fi \expandafter\ifx\csname T@X2\endcsname\relax \expandafter\ifx\csname T@T2A\endcsname\relax \else % T2A was declared as font encoding \substitutefont{T2A}{\rmdefault}{cmr} \substitutefont{T2A}{\sfdefault}{cmss} \substitutefont{T2A}{\ttdefault}{cmtt} \fi \else % X2 was declared as font encoding \substitutefont{X2}{\rmdefault}{cmr} \substitutefont{X2}{\sfdefault}{cmss} \substitutefont{X2}{\ttdefault}{cmtt} \fi <% endif %> \usepackage[Bjarne]{fncychap} \ChNameAsIs \ChTitleAsIs \usepackage{sphinx} <% if sphinx_version >= (4, 0) %> \fvset{fontsize=auto} <% else %> \fvset{fontsize=\small} <% endif -%> \usepackage{geometry} % Include hyperref last. \usepackage{hyperref} % Fix anchor placement for figures with captions. \usepackage{hypcap}% it must be loaded after hyperref. % Set up styles of URL: it should be placed after hyperref. \urlstyle{same} \usepackage{sphinxmessages} \setcounter{tocdepth}{1} \definecolor{nbsphinxin}{HTML}{307FC1} \definecolor{nbsphinxout}{HTML}{BF5B3D} \newcommand\thesymbolfootnote{\fnsymbol{footnote}}\let\thenumberfootnote\thefootnote \definecolor{regex_literal}{HTML}{696969} \definecolor{regex_at}{HTML}{FF4500} \definecolor{regex_repeat_brace}{HTML}{FF4500} \definecolor{regex_branch}{HTML}{FF4500} \definecolor{regex_subpattern}{HTML}{1e90ff} \definecolor{regex_in}{HTML}{ff8c00} \definecolor{regex_category}{HTML}{8fbc8f} \definecolor{regex_repeat}{HTML}{FF4500} \definecolor{regex_any}{HTML}{FF4500} \usepackage[]{bookmark} \newcommand{\thesummary}{This is an awesome tool! πŸš€ \textasciitilde\space intersphinx\_mapping \# 100\% β€˜Quotes’} \makeatletter \renewcommand{\py@release}{ \releasename\space\version \par \vspace{25pt} \textup{\thesummary} } \makeatother % begin st better header layout \makeatletter \renewcommand{\DOCH}{% \mghrulefill{\RW}\par\nobreak \CNV\FmN{\@chapapp}\par\nobreak \CNoV\TheAlphaChapter\par\nobreak \vskip -1\baselineskip\vskip 5pt\mghrulefill{\RW}\par\nobreak \vskip 10\p@ } \renewcommand{\DOTI}[1]{% \CTV\FmTi{#1}\par\nobreak \vskip 9\p@ } \renewcommand{\DOTIS}[1]{% \CTV\FmTi{#1}\par\nobreak \vskip 19\p@ } \makeatother % end st better header layout \title{Python} \date{Mar 11, 2021} \release{} \author{unknown} \newcommand{\sphinxlogo}{\vbox{}} \renewcommand{\releasename}{} \makeindex \begin{document} <% if sphinx_version >= (5, 0) %>\ifdefined\shorthandoff \ifnum\catcode`\=\string=\active\shorthandoff{=}\fi \ifnum\catcode`\"=\active\shorthandoff{"}\fi \fi <% endif %>\pagestyle{empty} \sphinxmaketitle \makeatletter\renewcommand{\py@release}{\releasename\space\version}\makeatother \pagestyle{plain} \sphinxtableofcontents \pagestyle{normal} \phantomsection\label{\detokenize{index::doc}} \part{Documentation} <% if sphinx_version > (4, 5) -%> \sphinxstepscope <% endif %> \chapter{from PyPI} \label{\detokenize{installation:installation-0-from_PyPI}}\label{\detokenize{installation:installation-0}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my_project --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-1-from_PyPI}}\label{\detokenize{installation:installation-1}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-2-from_PyPI}}\label{\detokenize{installation:installation-2}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-3-from_Anaconda}}\label{\detokenize{installation:installation-3}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my_project \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-4-from_Anaconda}}\label{\detokenize{installation:installation-4}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-5-from_Anaconda}}\label{\detokenize{installation:installation-5}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-6-from_GitHub}}\label{\detokenize{installation:installation-6}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-7-from_GitHub}}\label{\detokenize{installation:installation-7}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-8-from_PyPI}}\label{\detokenize{installation:installation-8}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my_project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-8-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my_project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-9-from_PyPI}}\label{\detokenize{installation:installation-9}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-9-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-10-from_PyPI}}\label{\detokenize{installation:installation-10}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-10-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-11-from_PyPI}}\label{\detokenize{installation:installation-11}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-11-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-12-from_PyPI}}\label{\detokenize{installation:installation-12}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-12-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-13-from_PyPI}}\label{\detokenize{installation:installation-13}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my_project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-13-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my_project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-13-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-14-from_PyPI}}\label{\detokenize{installation:installation-14}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-14-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-14-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-15-from_PyPI}}\label{\detokenize{installation:installation-15}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-15-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-15-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-16-from_PyPI}}\label{\detokenize{installation:installation-16}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-16-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-16-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-17-from_PyPI}}\label{\detokenize{installation:installation-17}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-17-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-17-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \phantomsection\label{\detokenize{installation:extensions-0}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{foo}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{bar}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{baz}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-1}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{foo}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{bar}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{baz}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZus{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-2}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZus{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-3}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-4}} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-5}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{Collapse} \label{\detokenize{collapse:collapse}}\label{\detokenize{collapse::doc}} Something small enough to escape casual notice. Something else that might escape notice. The text should be visible when the page loads. <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{Confval} \label{\detokenize{confval:confval}}\label{\detokenize{confval::doc}}\index{source\_link\_target (configuration value)@\spxentry{source\_link\_target}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-source_link_target}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{source\_link\_target}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{False}} \item[] \sphinxstylestrong{Default:} \sphinxcode{\sphinxupquote{\textquotesingle{}Sphinx\textquotesingle{}}} \end{DUlineblock} \vspace{-25px} The target of the source link, either \sphinxcode{\sphinxupquote{\textquotesingle{}GitHub\textquotesingle{}}} or \sphinxcode{\sphinxupquote{\textquotesingle{}Sphinx\textquotesingle{}}}. Case insensitive. \end{fulllineitems} \index{github\_username (configuration value)@\spxentry{github\_username}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-github_username}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{github\_username}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{True}} \end{DUlineblock} \end{fulllineitems} \index{github\_repository (configuration value)@\spxentry{github\_repository}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-github_repository}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{github\_repository}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{True}} \end{DUlineblock} \vspace{-25px} The GitHub repository this documentation corresponds to. \end{fulllineitems} \index{conda\_channels (configuration value)@\spxentry{conda\_channels}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-conda_channels}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{conda\_channels}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} Comma\sphinxhyphen{}separated list of strings \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{True}} \end{DUlineblock} \vspace{-25px} The conda channels required to install the library from Anaconda. \end{fulllineitems} \begin{fulllineitems} <% if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{github\_repository}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \end{DUlineblock} \vspace{-25px} This is a duplicate with \sphinxcode{\sphinxupquote{:noindex:}} set. \end{fulllineitems} This is an xref to the {\hyperref[\detokenize{confval:confval-github_repository}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{github\_repository}}}}} configuration option. \index{something (configuration value)@\spxentry{something}\spxextra{configuration value}} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-something}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{something}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-25px} A configuration value. \end{fulllineitems} <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} Code} \label{\detokenize{code-block:sphinx-toolbox-demo-code}}\label{\detokenize{code-block::doc}} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \section{Code Cell} \label{\detokenize{code-block:code-cell}} <%- if sphinx_version >= (4, 1) %> \begin{sphinxuseclass}{code-cell}<% endif %> \vspace{4mm}\llap{\color{nbsphinxin}\texttt{In [1]:}\,\hspace{\fboxrule}\hspace{\fboxrule}\hspace{\fboxsep}}\vspace{-7mm} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \PYG{n+nb}{print}\PYG{p}{(}\PYG{l+s+s2}{\PYGZdq{}}\PYG{l+s+s2}{hello world}\PYG{l+s+s2}{\PYGZdq{}}\PYG{p}{)} \end{sphinxVerbatim} <% if sphinx_version >= (4, 1) %> \end{sphinxuseclass} \begin{sphinxuseclass}{output-cell}<% endif %> \vspace{4mm}\llap{\color{nbsphinxout}\texttt{[1]:}\,\hspace{\fboxrule}\hspace{\fboxrule}\hspace{\fboxsep}}\vspace{-7mm} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{hello} \PYG{n}{world} \end{sphinxVerbatim} <% if sphinx_version >= (4, 1) %> \end{sphinxuseclass} \begin{sphinxuseclass}{code-cell}<% endif %> \vspace{4mm}\llap{\color{nbsphinxin}\texttt{In [2]:}\,\hspace{\fboxrule}\hspace{\fboxrule}\hspace{\fboxsep}}\vspace{-7mm} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} <% if sphinx_version >= (4, 1) %> \end{sphinxuseclass}<% endif %> <%- if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} Formatting} \label{\detokenize{formatting:sphinx-toolbox-demo-formatting}}\label{\detokenize{formatting::doc}} \textit{\sphinxstyleabbreviation{m/z} (mass to charge ratio)} \textit{\sphinxstyleabbreviation{doodad}} \vspace{10px}\sphinxstylestrong{Examples:} \vspace{10px}\sphinxstylestrong{Other Extensions} <%- if sphinx_version > (4, 5) %> \sphinxstepscope<% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} footnotes} \label{\detokenize{footnote_symbols:sphinx-toolbox-demo-footnotes}}\label{\detokenize{footnote_symbols::doc}} Some text % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[2]\sphinxAtStartFootnote An auto\sphinxhyphen{}symbol footnote % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} Some more text % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[3]\sphinxAtStartFootnote Another footnote % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} Sometimes % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[4]\sphinxAtStartFootnote A footnote with lots of content. \sphinxSetupCodeBlockInFootnote \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n+nb}{print}\PYG{p}{(}\PYG{l+s+s2}{\PYGZdq{}}\PYG{l+s+s2}{Look ma! I}\PYG{l+s+s2}{\PYGZsq{}}\PYG{l+s+s2}{m in a footnote}\PYG{l+s+s2}{\PYGZdq{}}\PYG{p}{)} \end{sphinxVerbatim} % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} strange things happen. <%- if sphinx_version > (4, 5) %> \sphinxstepscope<% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} LaTeX} \label{\detokenize{latex:sphinx-toolbox-demo-latex}}\label{\detokenize{latex::doc}} \clearpage \par\begin{samepage} Content under the samepage directive \begin{figure}[htbp] \centering \noindent\sphinxincludegraphics{{75883961}.png} \end{figure} \end{samepage}\par \cleardoublepage The \hspace{0pt}following \hspace{0pt}characters \hspace{0pt}are \hspace{0pt}problematic \hspace{0pt}in \hspace{0pt}LaTeX: \begin{itemize} \item {} $\spadesuit$ \item {} $\heartsuit$ \item {} $\diamondsuit$ \item {} $\clubsuit$ \item {} \textmu{} \item {} $\equiv$ \end{itemize} \clearpage Hello \clearpage \par\begin{samepage} Content under the samepage directive \begin{figure}[htbp] \centering \noindent\sphinxincludegraphics{{75883961}.png} \end{figure} \end{samepage}\par \vspace{-5px} \cleardoublepage The \hspace{0pt}following \hspace{0pt}characters \hspace{0pt}are \hspace{0pt}problematic \hspace{0pt}in \hspace{0pt}LaTeX: \begin{itemize} \item {} $\spadesuit$ \item {} $\heartsuit$ \item {} $\diamondsuit$ \item {} $\clubsuit$ \item {} \textmu{} \item {} $\equiv$ \item {} $\approx$ \end{itemize} This is a \textmu{}\sphinxhyphen{}library for doing something. The Greek letter \textmu{} has several uses. \vspace{10px} \clearpage \vspace{30mm} Goodbyeee! \section{Example Footnotes} \label{\detokenize{latex:example-footnotes}} \begin{DUlineblock}{0em} \item[] Hello % \begin{footnote}[1]\sphinxAtStartFootnote One % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Goodbye % \begin{footnote}[2]\sphinxAtStartFootnote Two % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[2]\sphinxAtStartFootnote Buckle my shoe % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Another Symbol % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[3]\sphinxAtStartFootnote The second symbol % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Number Again % \begin{footnote}[3]\sphinxAtStartFootnote The number after the symbol % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 3 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[4]\sphinxAtStartFootnote Symbol 3 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 4 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[5]\sphinxAtStartFootnote Symbol 4 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 5 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[6]\sphinxAtStartFootnote Symbol 5 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 6 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[7]\sphinxAtStartFootnote Symbol 6 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 7 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[8]\sphinxAtStartFootnote Symbol 7 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 8 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[9]\sphinxAtStartFootnote Symbol 8 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 9 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[10]\sphinxAtStartFootnote Symbol 9 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \end{DUlineblock} \vspace{20px} \sphinxstrong{See also:} Check this out on GitHub <% if sphinx_version >= (6, 2) %>\begin{sphinxseealso}{See also:}<% elif sphinx_version >= (6, 1) %>\begin{sphinxseealso}{See also}<% else %>\sphinxstrong{See also:} <% if sphinx_version >= (3, 4) %>\nopagebreak <% endif %><% endif %> These are helpful resources: \begin{itemize} \item {} Google \item {} StackOverflow \item {} RealPython \end{itemize} <% if sphinx_version >= (6, 1) %> \end{sphinxseealso} <% endif %><% if sphinx_version > (4, 5) %> \sphinxstepscope<% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} Regex} \label{\detokenize{regex:module-sphinx_toolbox.utils}}\label{\detokenize{regex:sphinx-toolbox-demo-regex}}\label{\detokenize{regex::doc}}\index{module@\spxentry{module}!sphinx\_toolbox.utils@\spxentry{sphinx\_toolbox.utils}}\index{sphinx\_toolbox.utils@\spxentry{sphinx\_toolbox.utils}!module@\spxentry{module}} General utility functions. \index{untyped\_param\_regex (in module sphinx\_toolbox.utils)@\spxentry{untyped\_param\_regex}\spxextra{in module sphinx\_toolbox.utils}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:sphinx_toolbox.utils.untyped_param_regex}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{sphinx\_toolbox.utils.}}\sphinxbfcode{\sphinxupquote{untyped\_param\_regex}}} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}}{[}\sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}}{]} Regex to match \sphinxcode{\sphinxupquote{:param \textless{}name\textgreater{}: \textless{}docstring\textgreater{}}} flags. << versionadded("0.8.0")>> \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_at}{\textasciicircum{}}\textcolor{regex_literal}{:}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{p}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{a}\textcolor{regex_literal}{m}\textcolor{regex_branch}{|}\textcolor{regex_literal}{p}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{a}\textcolor{regex_literal}{m}\textcolor{regex_literal}{e}\textcolor{regex_literal}{t}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_branch}{|}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{g}\textcolor{regex_branch}{|}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{g}\textcolor{regex_literal}{u}\textcolor{regex_literal}{m}\textcolor{regex_literal}{e}\textcolor{regex_literal}{n}\textcolor{regex_literal}{t}\textcolor{regex_subpattern}{)}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_category}{\textbackslash{}w}\textcolor{regex_repeat}{*}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{:}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}}} \\ << hline >>\sphinxstyletheadfamily \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.ASCII}{\sphinxcode{\sphinxupquote{re.ASCII}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{typed\_flag\_regex (in module sphinx\_toolbox.utils)@\spxentry{typed\_flag\_regex}\spxextra{in module sphinx\_toolbox.utils}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:sphinx_toolbox.utils.typed_flag_regex}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{sphinx\_toolbox.utils.}}\sphinxbfcode{\sphinxupquote{typed\_flag\_regex}}} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}}{[}\sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}}{]} Regex to match \sphinxcode{\sphinxupquote{:type \textless{}name\textgreater{}: \textless{}type\textgreater{}}} flags. << versionadded("0.8.0")>> \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_at}{\textasciicircum{}}\textcolor{regex_literal}{:}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{p}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{a}\textcolor{regex_literal}{m}\textcolor{regex_literal}{t}\textcolor{regex_literal}{y}\textcolor{regex_literal}{p}\textcolor{regex_literal}{e}\textcolor{regex_branch}{|}\textcolor{regex_literal}{t}\textcolor{regex_literal}{y}\textcolor{regex_literal}{p}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_category}{\textbackslash{}w}\textcolor{regex_repeat}{*}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{:}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}}} \\ << hline >>\sphinxstyletheadfamily \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.ASCII}{\sphinxcode{\sphinxupquote{re.ASCII}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \sphinxcode{\sphinxupquote{\textcolor{regex_at}{\textasciicircum{}}\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{?}}} \index{no\_flags (in module tests.regex\_demo)@\spxentry{no\_flags}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.no_flags}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{no\_flags}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{?}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{one\_flag (in module tests.regex\_demo)@\spxentry{one\_flag}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.one_flag}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{one\_flag}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{?}}} \\<% if sphinx_version >= (5, 3) %> \sphinxhline\sphinxstyletheadfamily<% else %> \hline\sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.IGNORECASE}{\sphinxcode{\sphinxupquote{re.IGNORECASE}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{two\_flags (in module tests.regex\_demo)@\spxentry{two\_flags}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.two_flags}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{two\_flags}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\enspace\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\enspace\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\enspace\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{?}}} \\ << hline >>\sphinxstyletheadfamily \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.ASCII}{\sphinxcode{\sphinxupquote{re.ASCII}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{backticks (in module tests.regex\_demo)@\spxentry{backticks}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.backticks}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{backticks}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{leading\_whitespace (in module tests.regex\_demo)@\spxentry{leading\_whitespace}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.leading_whitespace}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{leading\_whitespace}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_in}{{[}}\enspace\textcolor{regex_in}{{]}}\textcolor{regex_literal}{3}\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{trailing\_whitespace (in module tests.regex\_demo)@\spxentry{trailing\_whitespace}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.trailing_whitespace}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{trailing\_whitespace}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}\enspace\textcolor{regex_literal}{3}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{single\_whitespace (in module tests.regex\_demo)@\spxentry{single\_whitespace}\spxextra{in module tests.regex\_demo}} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.single_whitespace}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{single\_whitespace}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_in}{{[}}\enspace\textcolor{regex_in}{{]}}\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_in}{{[}}\enspace\textcolor{regex_in}{{]}}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} GitHub} \label{\detokenize{github:sphinx-toolbox-demo-github}}\label{\detokenize{github::doc}} \section{Issues} \label{\detokenize{github:issues}} \sphinxhref{https://github.com/sphinx-toolbox/sphinx-toolbox/issues/1}{\#1} \sphinxhref{https://github.com/pytest-dev/pytest/issues/7680}{pytest\sphinxhyphen{}dev/pytest\#7680} \section{Pull Requests} \label{\detokenize{github:pull-requests}} \sphinxhref{https://github.com/sphinx-toolbox/sphinx-toolbox/pull/2}{\#2} \sphinxhref{https://github.com/pytest-dev/pytest/pull/7671}{pytest\sphinxhyphen{}dev/pytest\#7671} \section{Repositories} \label{\detokenize{github:repositories}} \sphinxhref{https://github.com/sphinx-toolbox/sphinx-toolbox}{sphinx\sphinxhyphen{}toolbox/sphinx\sphinxhyphen{}toolbox} See more in the \sphinxhref{https://github.com/pytest-dev/pytest}{pytest repository}. \section{Users} \label{\detokenize{github:users}} \sphinxhref{https://github.com/domdfcoding}{@domdfcoding} See more of my \sphinxhref{https://github.com/domdfcoding}{repositories}. \section{Orgs} \label{\detokenize{github:orgs}} \sphinxhref{https://github.com/sphinx-toolbox}{@sphinx\sphinxhyphen{}toolbox} See more repositories in the \sphinxhref{https://github.com/pytest-dev}{pytest\sphinxhyphen{}dev org}. <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{Changeset} \label{\detokenize{changeset:changeset}}\label{\detokenize{changeset::doc}} \section{Removed} \label{\detokenize{changeset:removed}} << versionremoved("1.2.3") >>Use {\hyperref[\detokenize{index:foo}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{foo()}}}}} instead. << versionremoved("1.2.3") >>Due to an unfixable bug this function has been removed. If you desperately need this functionality please write to the mailing list at \sphinxhref{mailto:python-users@example.org}{python\sphinxhyphen{}users@example.org} << versionremoved("1.2.3") >>\begin{itemize} \item {} Removed \sphinxcode{\sphinxupquote{mozarella}} \item {} Removed \sphinxcode{\sphinxupquote{parrot()}} from \sphinxcode{\sphinxupquote{pet\_shop}} \end{itemize} \section{Changed} \label{\detokenize{changeset:changed}} << versionchanged("1.2.3") >>Moved from \sphinxcode{\sphinxupquote{mozarella}} << versionchanged("0.3.0") >>\begin{itemize} \item {} Parameters for \sphinxcode{\sphinxupquote{\_\_init\_\_}} can be documented either in the class docstring or alongside the attribute. The class docstring has priority. \item {} Added support for \sphinxhref{https://github.com/Chilipp/autodocsumm}{autodocsumm}. \end{itemize} \index{built\sphinxhyphen{}in function@\spxentry{built\sphinxhyphen{}in function}!foo()@\spxentry{foo()}}\index{foo()@\spxentry{foo()}!built\sphinxhyphen{}in function@\spxentry{built\sphinxhyphen{}in function}} \begin{fulllineitems} \phantomsection\label{\detokenize{index:foo}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{foo}}}<< sphinx81_linebreak >>{\<>{\DUrole{n<< du_role_2nd_n >>}{bar}}<>{\DUrole{n<< du_role_2nd_n >>}{baz}}}<< sphinx81_linebreak >>{}<< tilde >> <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif -%> \begin{quote}\begin{description} <% if sphinx_version >= (5, 0) %>\sphinxlineitem{Parameters}<% else %>\item[{Parameters}] \leavevmode<% endif %>\begin{itemize} \item {} \sphinxstyleliteralstrong{\sphinxupquote{bar}} \textendash{} Does something \item {} \sphinxstyleliteralstrong{\sphinxupquote{baz}} \textendash{} Does something else \end{itemize} \end{description}\end{quote} \end{fulllineitems} \vspace{10px} \sphinxstylestrong{Classes:} \vspace{-5px} \begin{savenotes}<%- if sphinx_version > (8, 2) %> \sphinxatlongtablestart \sphinxthistablewithglobalstyle \sphinxthistablewithnovlinesstyle \makeatletter \LTleft \@totalleftmargin plus1fill \LTright\dimexpr\columnwidth-\@totalleftmargin-\linewidth\relax plus1fill \makeatother \begin{longtable}{\X{1}{2}\X{1}{2}} \sphinxtoprule \endfirsthead \multicolumn{2}{c}{\sphinxnorowcolor \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}% }\\ \sphinxtoprule \endhead \sphinxbottomrule \multicolumn{2}{r}{\sphinxnorowcolor \makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}% }\\ \endfoot \endlastfoot \sphinxtableatstartofbodyhook <% elif sphinx_version > (4, 5) -%> \sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle \sphinxthistablewithnovlinesstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{\X{1}{2}\X{1}{2}}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook<% else %> \hline <%- endif %><% else %>\sphinxatlongtablestart\begin{longtable}[c]{\X{1}{2}\X{1}{2}} \hline \endfirsthead \multicolumn{2}{c}% {\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}}\\ \hline \endhead \hline \multicolumn{2}{r}{\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}}\\ \endfoot \endlastfoot <%- endif %> <% if sphinx_version < (5, 3) %> <% endif %>\sphinxcode{\sphinxupquote{Animal}}(name, voice) & An animal. \\ << hline >> \sphinxcode{\sphinxupquote{Employee}}(name{[}, id{]}) & Represents an employee. \\ << hline >> \sphinxcode{\sphinxupquote{Movie}}(name, year, based\_on) & Represents a movie. \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> <% if sphinx_version > (8, 2) -%> \end{longtable} \sphinxtableafterendhook \sphinxatlongtableend \end{savenotes}<% elif sphinx_version > (4, 5) -%> \end{tabulary} << par >> \sphinxattableend\end{savenotes} <%- else -%> \end{longtable}\sphinxatlongtableend\end{savenotes} <%- endif %> \bookmarksetupnext{{level=part}} \renewcommand{\indexname}{Python Module Index} \begin{sphinxtheindex} \let\bigletter\sphinxstyleindexlettergroup \bigletter{s} \item\relax\sphinxstyleindexentry{sphinx\_toolbox.utils}\sphinxstyleindexpageref{regex:\detokenize{module-sphinx_toolbox.utils}} \end{sphinxtheindex} \bookmarksetupnext{{level=part}} \renewcommand{\indexname}{Index} \printindex \end{document} sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_latex_output_latex_layout.tex000066400000000000000000001751631475757360300321650ustar00rootroot00000000000000<% set tilde = "~" if sphinx_version < (4, 4) else "" -%> <%- set hline = "\\sphinxhline" if sphinx_version >= (5, 3) else "\\hline" -%> <%- set par = "\\sphinxtableafterendhook\\par" if sphinx_version >= (5, 3) else "\\par" -%> <%- set sphinxparam = "sphinxparam" if sphinx_version >= (6, 2) else "emph" -%> <%- if sphinx_version >= (7, 1) %> <%- set sphinxparamcomma = "\\sphinxparamcomma \\sphinxparam" -%> <%- elif sphinx_version >= (6, 2) -%> <%- set sphinxparamcomma = ", \\sphinxparam" -%> <%- else -%> <%- set sphinxparamcomma = ", \\emph" -%> <%- endif -%> <%- set du_role_2nd_n = ",n" if (7, 2) > sphinx_version >= (6, 2) else "" -%> <# Latest Sphinx removed vertical rules from csv-table. Inconsequential for us #> <%- set regex_columns = "TT" if sphinx_version >= (6, 0) else "|T|T|" -%> <%- set sphinx81_linebreak = "\n" if sphinx_version >= (8, 1) else "" -%> <%- macro versionadded(v) -%> <%- if sphinx_version >= (8, 1) %>\DUrole{versionmodified}{\DUrole{added}{Added in version <>.}} <%- else %>\DUrole{versionmodified,added}{<% if sphinx_version >= (7, 3) %>Added<% else %>New<% endif %> in version <>.}<% endif %> <%- endmacro -%> <%- macro versionremoved(v) -%> <%- if sphinx_version >= (8, 1) %>\DUrole{versionmodified}{\DUrole{removed}{Removed in version <>: }} <%- else %>\DUrole{versionmodified,removed}{Removed in version <>: } <%- endif %><%- endmacro -%> <%- macro versionchanged(v) -%> <%- if sphinx_version >= (8, 1) %>\DUrole{versionmodified}{\DUrole{changed}{Changed in version <>: }} <%- else %>\DUrole{versionmodified,changed}{Changed in version <>: } <%- endif %><%- endmacro -%> %% Generated by Sphinx. \def\sphinxdocclass{report} \documentclass[letterpaper,10pt,english]{sphinxmanual} \ifdefined\pdfpxdimen \let\sphinxpxdimen\pdfpxdimen\else\newdimen\sphinxpxdimen \fi \sphinxpxdimen=.75bp\relax <% if sphinx_version >= (4, 0) %>\ifdefined\pdfimageresolution \pdfimageresolution= \numexpr \dimexpr1in\relax/\sphinxpxdimen\relax \fi %% let collapsable pdf bookmarks panel have high depth per default \PassOptionsToPackage{bookmarksdepth=5}{hyperref}<% if sphinx_version >= (5, 3) %> <% endif %><% if sphinx_version >= (6, 0) %> \PassOptionsToPackage{booktabs}{sphinx} \PassOptionsToPackage{colorrows}{sphinx}<% endif %> <% endif %> \PassOptionsToPackage{warn}{textcomp} \usepackage[utf8]{inputenc} \ifdefined\DeclareUnicodeCharacter % support both utf8 and utf8x syntaxes \ifdefined\DeclareUnicodeCharacterAsOptional \def\sphinxDUC#1{\DeclareUnicodeCharacter{"#1}} \else \let\sphinxDUC\DeclareUnicodeCharacter \fi \sphinxDUC{00A0}{\nobreakspace} \sphinxDUC{2500}{\sphinxunichar{2500}} \sphinxDUC{2502}{\sphinxunichar{2502}} \sphinxDUC{2514}{\sphinxunichar{2514}} \sphinxDUC{251C}{\sphinxunichar{251C}} \sphinxDUC{2572}{\textbackslash} \fi \usepackage{cmap} \usepackage[T1]{fontenc} \usepackage{amsmath,amssymb,amstext} \usepackage{babel} <%+ if sphinx_version >= (4, 0) %> \usepackage{tgtermes} \usepackage{tgheros} \renewcommand{\ttdefault}{txtt} <% else %> \usepackage{times} \expandafter\ifx\csname T@LGR\endcsname\relax \else % LGR was declared as font encoding \substitutefont{LGR}{\rmdefault}{cmr} \substitutefont{LGR}{\sfdefault}{cmss} \substitutefont{LGR}{\ttdefault}{cmtt} \fi \expandafter\ifx\csname T@X2\endcsname\relax \expandafter\ifx\csname T@T2A\endcsname\relax \else % T2A was declared as font encoding \substitutefont{T2A}{\rmdefault}{cmr} \substitutefont{T2A}{\sfdefault}{cmss} \substitutefont{T2A}{\ttdefault}{cmtt} \fi \else % X2 was declared as font encoding \substitutefont{X2}{\rmdefault}{cmr} \substitutefont{X2}{\sfdefault}{cmss} \substitutefont{X2}{\ttdefault}{cmtt} \fi <% endif %> \usepackage[Bjarne]{fncychap} \usepackage{sphinx} <% if sphinx_version >= (4, 0) %> \fvset{fontsize=auto} <% else %> \fvset{fontsize=\small} <% endif -%> \usepackage{geometry} % Include hyperref last. \usepackage{hyperref} % Fix anchor placement for figures with captions. \usepackage{hypcap}% it must be loaded after hyperref. % Set up styles of URL: it should be placed after hyperref. \urlstyle{same} \usepackage{sphinxmessages} \setcounter{tocdepth}{1} \definecolor{nbsphinxin}{HTML}{307FC1} \definecolor{nbsphinxout}{HTML}{BF5B3D} \newcommand\thesymbolfootnote{\fnsymbol{footnote}}\let\thenumberfootnote\thefootnote \definecolor{regex_literal}{HTML}{696969} \definecolor{regex_at}{HTML}{FF4500} \definecolor{regex_repeat_brace}{HTML}{FF4500} \definecolor{regex_branch}{HTML}{FF4500} \definecolor{regex_subpattern}{HTML}{1e90ff} \definecolor{regex_in}{HTML}{ff8c00} \definecolor{regex_category}{HTML}{8fbc8f} \definecolor{regex_repeat}{HTML}{FF4500} \definecolor{regex_any}{HTML}{FF4500} \usepackage[]{bookmark} \newcommand{\thesummary}{This is an awesome tool! πŸš€ \textasciitilde\space intersphinx\_mapping \# 100\% β€˜Quotes’} \makeatletter \renewcommand{\py@release}{ \releasename\space\version \par \vspace{25pt} \textup{\thesummary} } \makeatother \title{Python} \date{Mar 11, 2021} \release{} \author{unknown} \newcommand{\sphinxlogo}{\vbox{}} \renewcommand{\releasename}{} \makeindex \begin{document} <% if sphinx_version >= (5, 0) %>\ifdefined\shorthandoff \ifnum\catcode`\=\string=\active\shorthandoff{=}\fi \ifnum\catcode`\"=\active\shorthandoff{"}\fi \fi <% endif %>\pagestyle{empty} \sphinxmaketitle \makeatletter\renewcommand{\py@release}{\releasename\space\version}\makeatother \pagestyle{plain} \sphinxtableofcontents \pagestyle{normal} \phantomsection\label{\detokenize{index::doc}} \part{Documentation} <% if sphinx_version > (4, 5) -%> \sphinxstepscope <% endif %> \chapter{from PyPI} \label{\detokenize{installation:installation-0-from_PyPI}}\label{\detokenize{installation:installation-0}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my_project --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-1-from_PyPI}}\label{\detokenize{installation:installation-1}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-2-from_PyPI}}\label{\detokenize{installation:installation-2}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-3-from_Anaconda}}\label{\detokenize{installation:installation-3}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my_project \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-4-from_Anaconda}}\label{\detokenize{installation:installation-4}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-5-from_Anaconda}}\label{\detokenize{installation:installation-5}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-6-from_GitHub}}\label{\detokenize{installation:installation-6}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-7-from_GitHub}}\label{\detokenize{installation:installation-7}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-8-from_PyPI}}\label{\detokenize{installation:installation-8}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my_project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-8-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my_project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-9-from_PyPI}}\label{\detokenize{installation:installation-9}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-9-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-10-from_PyPI}}\label{\detokenize{installation:installation-10}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-10-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-11-from_PyPI}}\label{\detokenize{installation:installation-11}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-11-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-12-from_PyPI}}\label{\detokenize{installation:installation-12}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-12-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-13-from_PyPI}}\label{\detokenize{installation:installation-13}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my_project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-13-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my_project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-13-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-14-from_PyPI}}\label{\detokenize{installation:installation-14}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-14-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-14-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-15-from_PyPI}}\label{\detokenize{installation:installation-15}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-15-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install my-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-15-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-16-from_PyPI}}\label{\detokenize{installation:installation-16}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-16-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-16-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \chapter{from PyPI} \label{\detokenize{installation:installation-17-from_PyPI}}\label{\detokenize{installation:installation-17}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install my-project --user \end{Verbatim} \chapter{from Anaconda} \label{\detokenize{installation:installation-17-from_Anaconda}} \begin{Verbatim}[commandchars=\\\{\}] $ conda install conda-project \end{Verbatim} \chapter{from GitHub} \label{\detokenize{installation:installation-17-from_GitHub}} \begin{Verbatim}[commandchars=\\\{\}] $ python3 -m pip install git+https://github.com/sphinx-toolbox/sphinx-toolbox@master --user \end{Verbatim} \phantomsection\label{\detokenize{installation:extensions-0}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{foo}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{bar}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{baz}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-1}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{foo}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{bar}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{baz}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZus{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-2}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZus{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-3}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-4}} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} \begin{flushleft} For more information see \sphinxurl{https://www.sphinx-doc.org/en/master/usage/extensions\#third-party-extensions} . \end{flushleft} \phantomsection\label{\detokenize{installation:extensions-5}} \vspace{10px} Enable \sphinxcode{\sphinxupquote{my\sphinxhyphen{}extension}} by adding the following to the \sphinxcode{\sphinxupquote{extensions}} variable in your \sphinxcode{\sphinxupquote{conf.py}}: \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{extensions} \PYG{o}{=} \PYG{p}{[} \PYG{o}{.}\PYG{o}{.}\PYG{o}{.} \PYG{l+s+s1}{\PYGZsq{}}\PYG{l+s+s1}{my\PYGZhy{}extension}\PYG{l+s+s1}{\PYGZsq{}}\PYG{p}{,} \PYG{p}{]} \end{sphinxVerbatim} <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{Collapse} \label{\detokenize{collapse:collapse}}\label{\detokenize{collapse::doc}} Something small enough to escape casual notice. Something else that might escape notice. The text should be visible when the page loads. <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{Confval} \label{\detokenize{confval:confval}}\label{\detokenize{confval::doc}}\index{source\_link\_target (configuration value)@\spxentry{source\_link\_target}\spxextra{configuration value}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-source_link_target}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{source\_link\_target}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{False}} \item[] \sphinxstylestrong{Default:} \sphinxcode{\sphinxupquote{\textquotesingle{}Sphinx\textquotesingle{}}} \end{DUlineblock} \vspace{-25px} The target of the source link, either \sphinxcode{\sphinxupquote{\textquotesingle{}GitHub\textquotesingle{}}} or \sphinxcode{\sphinxupquote{\textquotesingle{}Sphinx\textquotesingle{}}}. Case insensitive. \end{fulllineitems} \index{github\_username (configuration value)@\spxentry{github\_username}\spxextra{configuration value}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-github_username}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{github\_username}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{True}} \end{DUlineblock} \end{fulllineitems} \index{github\_repository (configuration value)@\spxentry{github\_repository}\spxextra{configuration value}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-github_repository}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{github\_repository}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{True}} \end{DUlineblock} \vspace{-25px} The GitHub repository this documentation corresponds to. \end{fulllineitems} \index{conda\_channels (configuration value)@\spxentry{conda\_channels}\spxextra{configuration value}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-conda_channels}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{conda\_channels}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} Comma\sphinxhyphen{}separated list of strings \item[] \sphinxstylestrong{Required:} \sphinxcode{\sphinxupquote{True}} \end{DUlineblock} \vspace{-25px} The conda channels required to install the library from Anaconda. \end{fulllineitems} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} <% if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{github\_repository}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-45px} \begin{DUlineblock}{0em} \item[] \sphinxstylestrong{Type:} \sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}} \end{DUlineblock} \vspace{-25px} This is a duplicate with \sphinxcode{\sphinxupquote{:noindex:}} set. \end{fulllineitems} This is an xref to the {\hyperref[\detokenize{confval:confval-github_repository}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{github\_repository}}}}} configuration option. \index{something (configuration value)@\spxentry{something}\spxextra{configuration value}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{confval:confval-something}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{something}}}<< tilde >> <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \vspace{-25px} A configuration value. \end{fulllineitems} <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} Code} \label{\detokenize{code-block:sphinx-toolbox-demo-code}}\label{\detokenize{code-block::doc}} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \begin{sphinxVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} \section{Code Cell} \label{\detokenize{code-block:code-cell}} <%- if sphinx_version >= (4, 1) %> \begin{sphinxuseclass}{code-cell}<% endif %> \vspace{4mm}\llap{\color{nbsphinxin}\texttt{In [1]:}\,\hspace{\fboxrule}\hspace{\fboxrule}\hspace{\fboxsep}}\vspace{-7mm} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \PYG{n+nb}{print}\PYG{p}{(}\PYG{l+s+s2}{\PYGZdq{}}\PYG{l+s+s2}{hello world}\PYG{l+s+s2}{\PYGZdq{}}\PYG{p}{)} \end{sphinxVerbatim} <% if sphinx_version >= (4, 1) %> \end{sphinxuseclass} \begin{sphinxuseclass}{output-cell}<% endif %> \vspace{4mm}\llap{\color{nbsphinxout}\texttt{[1]:}\,\hspace{\fboxrule}\hspace{\fboxrule}\hspace{\fboxsep}}\vspace{-7mm} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n}{hello} \PYG{n}{world} \end{sphinxVerbatim} <% if sphinx_version >= (4, 1) %> \end{sphinxuseclass} \begin{sphinxuseclass}{code-cell}<% endif %> \vspace{4mm}\llap{\color{nbsphinxin}\texttt{In [2]:}\,\hspace{\fboxrule}\hspace{\fboxrule}\hspace{\fboxsep}}\vspace{-7mm} \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{k}{def} \PYG{n+nf}{print}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)}\PYG{p}{:} \PYG{n}{sys}\PYG{o}{.}\PYG{n}{stdout}\PYG{o}{.}\PYG{n}{write}\PYG{p}{(}\PYG{n}{text}\PYG{p}{)} \end{sphinxVerbatim} <% if sphinx_version >= (4, 1) %> \end{sphinxuseclass}<% endif %> <%- if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} Formatting} \label{\detokenize{formatting:sphinx-toolbox-demo-formatting}}\label{\detokenize{formatting::doc}} \textit{\sphinxstyleabbreviation{m/z} (mass to charge ratio)} \textit{\sphinxstyleabbreviation{doodad}} \vspace{10px}\sphinxstylestrong{Examples:} \vspace{10px}\sphinxstylestrong{Other Extensions} <%- if sphinx_version > (4, 5) %> \sphinxstepscope<% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} footnotes} \label{\detokenize{footnote_symbols:sphinx-toolbox-demo-footnotes}}\label{\detokenize{footnote_symbols::doc}} Some text % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[2]\sphinxAtStartFootnote An auto\sphinxhyphen{}symbol footnote % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} Some more text % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[3]\sphinxAtStartFootnote Another footnote % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} Sometimes % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[4]\sphinxAtStartFootnote A footnote with lots of content. \sphinxSetupCodeBlockInFootnote \begin{sphinxVerbatim}[commandchars=\\\{\}] \PYG{n+nb}{print}\PYG{p}{(}\PYG{l+s+s2}{\PYGZdq{}}\PYG{l+s+s2}{Look ma! I}\PYG{l+s+s2}{\PYGZsq{}}\PYG{l+s+s2}{m in a footnote}\PYG{l+s+s2}{\PYGZdq{}}\PYG{p}{)} \end{sphinxVerbatim} % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} strange things happen. <%- if sphinx_version > (4, 5) %> \sphinxstepscope<% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} LaTeX} \label{\detokenize{latex:sphinx-toolbox-demo-latex}}\label{\detokenize{latex::doc}} \clearpage \par\begin{samepage} Content under the samepage directive \begin{figure}[htbp] \centering \noindent\sphinxincludegraphics{{75883961}.png} \end{figure} \end{samepage}\par \cleardoublepage The \hspace{0pt}following \hspace{0pt}characters \hspace{0pt}are \hspace{0pt}problematic \hspace{0pt}in \hspace{0pt}LaTeX: \begin{itemize} \item {} $\spadesuit$ \item {} $\heartsuit$ \item {} $\diamondsuit$ \item {} $\clubsuit$ \item {} \textmu{} \item {} $\equiv$ \end{itemize} \clearpage Hello \clearpage \par\begin{samepage} Content under the samepage directive \begin{figure}[htbp] \centering \noindent\sphinxincludegraphics{{75883961}.png} \end{figure} \end{samepage}\par \vspace{-5px} \cleardoublepage The \hspace{0pt}following \hspace{0pt}characters \hspace{0pt}are \hspace{0pt}problematic \hspace{0pt}in \hspace{0pt}LaTeX: \begin{itemize} \item {} $\spadesuit$ \item {} $\heartsuit$ \item {} $\diamondsuit$ \item {} $\clubsuit$ \item {} \textmu{} \item {} $\equiv$ \item {} $\approx$ \end{itemize} This is a \textmu{}\sphinxhyphen{}library for doing something. The Greek letter \textmu{} has several uses. \vspace{10px} \clearpage \vspace{30mm} Goodbyeee! \section{Example Footnotes} \label{\detokenize{latex:example-footnotes}} \begin{DUlineblock}{0em} \item[] Hello % \begin{footnote}[1]\sphinxAtStartFootnote One % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Goodbye % \begin{footnote}[2]\sphinxAtStartFootnote Two % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[2]\sphinxAtStartFootnote Buckle my shoe % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Another Symbol % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[3]\sphinxAtStartFootnote The second symbol % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Number Again % \begin{footnote}[3]\sphinxAtStartFootnote The number after the symbol % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 3 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[4]\sphinxAtStartFootnote Symbol 3 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 4 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[5]\sphinxAtStartFootnote Symbol 4 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 5 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[6]\sphinxAtStartFootnote Symbol 5 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 6 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[7]\sphinxAtStartFootnote Symbol 6 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 7 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[8]\sphinxAtStartFootnote Symbol 7 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 8 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[9]\sphinxAtStartFootnote Symbol 8 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \item[] Symbol 9 % \renewcommand\thefootnote{\thesymbolfootnote}\begin{footnote}[10]\sphinxAtStartFootnote Symbol 9 % \end{footnote}\renewcommand\thefootnote{\thenumberfootnote} \end{DUlineblock} \vspace{20px} \sphinxstrong{See also:} Check this out on GitHub <% if sphinx_version >= (6, 2) %>\begin{sphinxseealso}{See also:}<% elif sphinx_version >= (6, 1) %>\begin{sphinxseealso}{See also}<% else %>\sphinxstrong{See also:} <% if sphinx_version >= (3, 4) %>\nopagebreak <% endif %><% endif %> These are helpful resources: \begin{itemize} \item {} Google \item {} StackOverflow \item {} RealPython \end{itemize} <% if sphinx_version >= (6, 1) %> \end{sphinxseealso} <% endif %><% if sphinx_version > (4, 5) %> \sphinxstepscope<% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} Regex} \label{\detokenize{regex:module-sphinx_toolbox.utils}}\label{\detokenize{regex:sphinx-toolbox-demo-regex}}\label{\detokenize{regex::doc}}\index{module@\spxentry{module}!sphinx\_toolbox.utils@\spxentry{sphinx\_toolbox.utils}}\index{sphinx\_toolbox.utils@\spxentry{sphinx\_toolbox.utils}!module@\spxentry{module}} General utility functions. \index{untyped\_param\_regex (in module sphinx\_toolbox.utils)@\spxentry{untyped\_param\_regex}\spxextra{in module sphinx\_toolbox.utils}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:sphinx_toolbox.utils.untyped_param_regex}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{sphinx\_toolbox.utils.}}\sphinxbfcode{\sphinxupquote{untyped\_param\_regex}}} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}}{[}\sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}}{]} Regex to match \sphinxcode{\sphinxupquote{:param \textless{}name\textgreater{}: \textless{}docstring\textgreater{}}} flags. << versionadded("0.8.0") >> \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_at}{\textasciicircum{}}\textcolor{regex_literal}{:}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{p}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{a}\textcolor{regex_literal}{m}\textcolor{regex_branch}{|}\textcolor{regex_literal}{p}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{a}\textcolor{regex_literal}{m}\textcolor{regex_literal}{e}\textcolor{regex_literal}{t}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_branch}{|}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{g}\textcolor{regex_branch}{|}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{g}\textcolor{regex_literal}{u}\textcolor{regex_literal}{m}\textcolor{regex_literal}{e}\textcolor{regex_literal}{n}\textcolor{regex_literal}{t}\textcolor{regex_subpattern}{)}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_category}{\textbackslash{}w}\textcolor{regex_repeat}{*}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{:}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}}} \\ << hline >>\sphinxstyletheadfamily \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.ASCII}{\sphinxcode{\sphinxupquote{re.ASCII}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{typed\_flag\_regex (in module sphinx\_toolbox.utils)@\spxentry{typed\_flag\_regex}\spxextra{in module sphinx\_toolbox.utils}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:sphinx_toolbox.utils.typed_flag_regex}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{sphinx\_toolbox.utils.}}\sphinxbfcode{\sphinxupquote{typed\_flag\_regex}}} <% if sphinx_version > (4, 5) %>\pysigstopsignatures <% endif -%> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}}{[}\sphinxhref{https://docs.python.org/3/library/stdtypes.html\#str}{\sphinxcode{\sphinxupquote{str}}}{]} Regex to match \sphinxcode{\sphinxupquote{:type \textless{}name\textgreater{}: \textless{}type\textgreater{}}} flags. << versionadded("0.8.0") >> \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_at}{\textasciicircum{}}\textcolor{regex_literal}{:}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{p}\textcolor{regex_literal}{a}\textcolor{regex_literal}{r}\textcolor{regex_literal}{a}\textcolor{regex_literal}{m}\textcolor{regex_literal}{t}\textcolor{regex_literal}{y}\textcolor{regex_literal}{p}\textcolor{regex_literal}{e}\textcolor{regex_branch}{|}\textcolor{regex_literal}{t}\textcolor{regex_literal}{y}\textcolor{regex_literal}{p}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_category}{\textbackslash{}w}\textcolor{regex_repeat}{*}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{:}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}}} \\ << hline >>\sphinxstyletheadfamily \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.ASCII}{\sphinxcode{\sphinxupquote{re.ASCII}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \sphinxcode{\sphinxupquote{\textcolor{regex_at}{\textasciicircum{}}\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{?}}} \index{no\_flags (in module tests.regex\_demo)@\spxentry{no\_flags}\spxextra{in module tests.regex\_demo}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.no_flags}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{no\_flags}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{?}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{one\_flag (in module tests.regex\_demo)@\spxentry{one\_flag}\spxextra{in module tests.regex\_demo}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.one_flag}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{one\_flag}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{?}}} \\<% if sphinx_version >= (5, 3) %> \sphinxhline\sphinxstyletheadfamily<% else %> \hline\sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.IGNORECASE}{\sphinxcode{\sphinxupquote{re.IGNORECASE}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{two\_flags (in module tests.regex\_demo)@\spxentry{two\_flags}\spxextra{in module tests.regex\_demo}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.two_flags}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{two\_flags}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{H}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{l}\textcolor{regex_literal}{o}\enspace\textcolor{regex_category}{\textbackslash{}s}\textcolor{regex_repeat}{+}\enspace\textcolor{regex_in}{{[}}\textcolor{regex_literal}{W}\textcolor{regex_literal}{w}\textcolor{regex_in}{{]}}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{l}\textcolor{regex_literal}{d}\enspace\textcolor{regex_in}{{[}}\textcolor{regex_literal}{.}\textcolor{regex_literal}{,}\textcolor{regex_in}{{]}}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_literal}{L}\textcolor{regex_literal}{o}\textcolor{regex_literal}{v}\textcolor{regex_literal}{e}\textcolor{regex_literal}{l}\textcolor{regex_literal}{y}\textcolor{regex_branch}{|}\textcolor{regex_literal}{H}\textcolor{regex_literal}{o}\textcolor{regex_literal}{r}\textcolor{regex_literal}{r}\textcolor{regex_literal}{i}\textcolor{regex_literal}{b}\textcolor{regex_literal}{l}\textcolor{regex_literal}{e}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{w}\textcolor{regex_literal}{e}\textcolor{regex_literal}{a}\textcolor{regex_literal}{t}\textcolor{regex_literal}{h}\textcolor{regex_literal}{e}\textcolor{regex_literal}{r}\textcolor{regex_literal}{,}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{s}\textcolor{regex_literal}{n}\textcolor{regex_literal}{\textquotesingle{}}\textcolor{regex_literal}{t}\enspace\textcolor{regex_literal}{i}\textcolor{regex_literal}{t}\enspace\textcolor{regex_subpattern}{(}\textcolor{regex_any}{.}\textcolor{regex_repeat}{*}\textcolor{regex_subpattern}{)}\enspace\textcolor{regex_literal}{?}}} \\ << hline >>\sphinxstyletheadfamily \sphinxstylestrong{Flags} & \sphinxhref{https://docs.python.org/3/library/re.html\#re.ASCII}{\sphinxcode{\sphinxupquote{re.ASCII}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{backticks (in module tests.regex\_demo)@\spxentry{backticks}\spxextra{in module tests.regex\_demo}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.backticks}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{backticks}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{leading\_whitespace (in module tests.regex\_demo)@\spxentry{leading\_whitespace}\spxextra{in module tests.regex\_demo}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.leading_whitespace}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{leading\_whitespace}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_in}{{[}}\enspace\textcolor{regex_in}{{]}}\textcolor{regex_literal}{3}\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{trailing\_whitespace (in module tests.regex\_demo)@\spxentry{trailing\_whitespace}\spxextra{in module tests.regex\_demo}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.trailing_whitespace}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{trailing\_whitespace}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}\enspace\textcolor{regex_literal}{3}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} \index{single\_whitespace (in module tests.regex\_demo)@\spxentry{single\_whitespace}\spxextra{in module tests.regex\_demo}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{regex:tests.regex_demo.single_whitespace}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysigline<< sphinx81_linebreak >>{\sphinxcode{\sphinxupquote{tests.regex\_demo.}}\sphinxbfcode{\sphinxupquote{single\_whitespace}}} <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif %> \sphinxstylestrong{Type:}Β Β Β Β \sphinxhref{https://docs.python.org/3/library/typing.html\#typing.Pattern}{\sphinxcode{\sphinxupquote{Pattern}}} Compiled regular expression object. \begin{savenotes}\sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{<>}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook\sphinxstyletheadfamily<% else %> \hline \sphinxstyletheadfamily<% endif %> \sphinxstylestrong{Pattern} & \sphinxcode{\sphinxupquote{\textcolor{regex_in}{{[}}\enspace\textcolor{regex_in}{{]}}\textcolor{regex_literal}{:}\textcolor{regex_literal}{p}\textcolor{regex_literal}{y}\textcolor{regex_literal}{:}\textcolor{regex_literal}{c}\textcolor{regex_literal}{l}\textcolor{regex_literal}{a}\textcolor{regex_literal}{s}\textcolor{regex_literal}{s}\textcolor{regex_literal}{:}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_subpattern}{(}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_in}{{[}}\textcolor{regex_literal}{A}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{Z}\textcolor{regex_literal}{a}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{z}\textcolor{regex_literal}{0}\textcolor{regex_at}{\sphinxhyphen{}}\textcolor{regex_literal}{9}\textcolor{regex_literal}{.}\textcolor{regex_literal}{\_}\textcolor{regex_in}{{]}}\textcolor{regex_repeat}{+}\textcolor{regex_subpattern}{)}\textcolor{regex_literal}{\textasciigrave{}}\textcolor{regex_in}{{[}}\enspace\textcolor{regex_in}{{]}}}} \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> \end{tabulary} << par >> \sphinxattableend\end{savenotes} \end{fulllineitems} <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{sphinx\sphinxhyphen{}toolbox Demo \sphinxhyphen{} GitHub} \label{\detokenize{github:sphinx-toolbox-demo-github}}\label{\detokenize{github::doc}} \section{Issues} \label{\detokenize{github:issues}} \sphinxhref{https://github.com/sphinx-toolbox/sphinx-toolbox/issues/1}{\#1} \sphinxhref{https://github.com/pytest-dev/pytest/issues/7680}{pytest\sphinxhyphen{}dev/pytest\#7680} \section{Pull Requests} \label{\detokenize{github:pull-requests}} \sphinxhref{https://github.com/sphinx-toolbox/sphinx-toolbox/pull/2}{\#2} \sphinxhref{https://github.com/pytest-dev/pytest/pull/7671}{pytest\sphinxhyphen{}dev/pytest\#7671} \section{Repositories} \label{\detokenize{github:repositories}} \sphinxhref{https://github.com/sphinx-toolbox/sphinx-toolbox}{sphinx\sphinxhyphen{}toolbox/sphinx\sphinxhyphen{}toolbox} See more in the \sphinxhref{https://github.com/pytest-dev/pytest}{pytest repository}. \section{Users} \label{\detokenize{github:users}} \sphinxhref{https://github.com/domdfcoding}{@domdfcoding} See more of my \sphinxhref{https://github.com/domdfcoding}{repositories}. \section{Orgs} \label{\detokenize{github:orgs}} \sphinxhref{https://github.com/sphinx-toolbox}{@sphinx\sphinxhyphen{}toolbox} See more repositories in the \sphinxhref{https://github.com/pytest-dev}{pytest\sphinxhyphen{}dev org}. <% if sphinx_version > (4, 5) %> \sphinxstepscope <% endif %> \chapter{Changeset} \label{\detokenize{changeset:changeset}}\label{\detokenize{changeset::doc}} \section{Removed} \label{\detokenize{changeset:removed}} << versionremoved("1.2.3") >>Use {\hyperref[\detokenize{index:foo}]{\sphinxcrossref{\sphinxcode{\sphinxupquote{foo()}}}}} instead. << versionremoved("1.2.3") >>Due to an unfixable bug this function has been removed. If you desperately need this functionality please write to the mailing list at \sphinxhref{mailto:python-users@example.org}{python\sphinxhyphen{}users@example.org} << versionremoved("1.2.3") >>\begin{itemize} \item {} Removed \sphinxcode{\sphinxupquote{mozarella}} \item {} Removed \sphinxcode{\sphinxupquote{parrot()}} from \sphinxcode{\sphinxupquote{pet\_shop}} \end{itemize} \section{Changed} \label{\detokenize{changeset:changed}} << versionchanged("1.2.3") >>Moved from \sphinxcode{\sphinxupquote{mozarella}} << versionchanged("0.3.0") >>\begin{itemize} \item {} Parameters for \sphinxcode{\sphinxupquote{\_\_init\_\_}} can be documented either in the class docstring or alongside the attribute. The class docstring has priority. \item {} Added support for \sphinxhref{https://github.com/Chilipp/autodocsumm}{autodocsumm}. \end{itemize} \index{built\sphinxhyphen{}in function@\spxentry{built\sphinxhyphen{}in function}!foo()@\spxentry{foo()}}\index{foo()@\spxentry{foo()}!built\sphinxhyphen{}in function@\spxentry{built\sphinxhyphen{}in function}} \vspace{5px}\needspace{4\baselineskip} \begin{fulllineitems} \phantomsection\label{\detokenize{index:foo}} <%- if sphinx_version > (4, 5) %> \pysigstartsignatures <% endif -%> \pysiglinewithargsret<< sphinx81_linebreak >>{\sphinxbfcode{\sphinxupquote{foo}}}<< sphinx81_linebreak >>{\<>{\DUrole{n<< du_role_2nd_n >>}{bar}}<>{\DUrole{n<< du_role_2nd_n >>}{baz}}}<< sphinx81_linebreak >>{}<< tilde >> <%- if sphinx_version > (4, 5) %> \pysigstopsignatures <%- endif -%> \vspace{10px}\begin{flushleft}\begin{description} <% if sphinx_version >= (5, 0) %>\sphinxlineitem{Parameters}<% else %>\item[{Parameters}] \leavevmode<% endif %>\begin{itemize} \item {} \sphinxstyleliteralstrong{\sphinxupquote{bar}} \textendash{} Does something \item {} \sphinxstyleliteralstrong{\sphinxupquote{baz}} \textendash{} Does something else \end{itemize} \end{description}\end{flushleft}\vspace{10px} \end{fulllineitems} \vspace{10px} \sphinxstylestrong{Classes:} \vspace{-5px} \begin{savenotes}<%- if sphinx_version > (8, 2) %> \sphinxatlongtablestart \sphinxthistablewithglobalstyle \sphinxthistablewithnovlinesstyle \makeatletter \LTleft \@totalleftmargin plus1fill \LTright\dimexpr\columnwidth-\@totalleftmargin-\linewidth\relax plus1fill \makeatother \begin{longtable}{\X{1}{2}\X{1}{2}} \sphinxtoprule \endfirsthead \multicolumn{2}{c}{\sphinxnorowcolor \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}% }\\ \sphinxtoprule \endhead \sphinxbottomrule \multicolumn{2}{r}{\sphinxnorowcolor \makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}% }\\ \endfoot \endlastfoot \sphinxtableatstartofbodyhook <% elif sphinx_version > (4, 5) -%> \sphinxattablestart<% if sphinx_version >= (5, 3) %> \sphinxthistablewithglobalstyle \sphinxthistablewithnovlinesstyle<% endif %> \centering \begin{tabulary}{\linewidth}[t]{\X{1}{2}\X{1}{2}}<% if sphinx_version >= (5, 3) %> \sphinxtoprule \sphinxtableatstartofbodyhook<% else %> \hline <%- endif %><% else %>\sphinxatlongtablestart\begin{longtable}[c]{\X{1}{2}\X{1}{2}} \hline \endfirsthead \multicolumn{2}{c}% {\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}}\\ \hline \endhead \hline \multicolumn{2}{r}{\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}}\\ \endfoot \endlastfoot <%- endif %> <% if sphinx_version < (5, 3) %> <% endif %>\sphinxcode{\sphinxupquote{Animal}}(name, voice) & An animal. \\ << hline >> \sphinxcode{\sphinxupquote{Employee}}(name{[}, id{]}) & Represents an employee. \\ << hline >> \sphinxcode{\sphinxupquote{Movie}}(name, year, based\_on) & Represents a movie. \\ <% if sphinx_version >= (5, 3) %>\sphinxbottomrule<% else %>\hline<% endif %> <% if sphinx_version > (8, 2) -%> \end{longtable} \sphinxtableafterendhook \sphinxatlongtableend \end{savenotes}<% elif sphinx_version > (4, 5) -%> \end{tabulary} << par >> \sphinxattableend\end{savenotes} <%- else -%> \end{longtable}\sphinxatlongtableend\end{savenotes} <%- endif %> \bookmarksetupnext{{level=part}} \renewcommand{\indexname}{Python Module Index} \begin{sphinxtheindex} \let\bigletter\sphinxstyleindexlettergroup \bigletter{s} \item\relax\sphinxstyleindexentry{sphinx\_toolbox.utils}\sphinxstyleindexpageref{regex:\detokenize{module-sphinx_toolbox.utils}} \end{sphinxtheindex} \bookmarksetupnext{{level=part}} \renewcommand{\indexname}{Index} \printindex \end{document} sphinx-toolbox-3.9.0/tests/test_output/test_output_/test_sidebar_links_output.html000066400000000000000000000376671475757360300312410ustar00rootroot00000000000000{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set sig_prename_tag=("span" if sphinx_version >= (4, 0) else 'code') -%} {% set sig_object_class=(' class="sig sig-object py"' if sphinx_version >= (4, 0) else '') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} <no title> β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

<{{ sig_prename_tag }} class="sig-name descname"> foo ( bar , baz ) ΒΆ
Parameters{% if docutils_version >= (0, 18) %} : {% endif %}
  • bar – Does something

  • baz – Does something else

Classes:

Animal (name,Β voice)

An animal.

Employee (name[,Β id])

Represents an employee.

Movie (name,Β year,Β based_on)

Represents a movie.

sphinx-toolbox-3.9.0/tests/test_output/typevars_demo.py000066400000000000000000000012571475757360300235420ustar00rootroot00000000000000# stdlib from typing import TypeVar # 3rd party import attr __all__ = ( "Demo", "SlotsDemo", 'T', "T_co", "T_contra", 'S', "DS", "FR", ) FR = TypeVar("FR", bound="SlotsDemo") @attr.s(slots=False) class Demo: """ An attrs class """ #: An argument arg1: str = attr.ib() #: Another argument arg2: int = attr.ib() @attr.s(slots=True) class SlotsDemo: """ An attrs class with slots=True """ #: An argument arg1: str = attr.ib() #: Another argument arg2: int = attr.ib() T = TypeVar('T') T_co = TypeVar("T_co", covariant=True) T_contra = TypeVar("T_contra", contravariant=True) S = TypeVar('S', bound=SlotsDemo) DS = TypeVar("DS", SlotsDemo, Demo) sphinx-toolbox-3.9.0/tests/test_output/variables_genericalias_demo.py000066400000000000000000000014761475757360300263460ustar00rootroot00000000000000# stdlib import os import pathlib from abc import abstractmethod from typing import Callable, List, Tuple, Union # 3rd party import attr #: Some variable variable: Union[List[str], Tuple[str, int, float], int, bytes, Callable[[str], int]] = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] CONSTANT: int = 42 """ Don't change this!" """ #: Type hint for filesystem paths PathLike = Union[str, os.PathLike, pathlib.Path] @attr.s(slots=False) class Demo: """ An attrs class """ #: An argument arg1: str = attr.ib() #: Another argument arg2: int = attr.ib() @property @abstractmethod def foo(self) -> str: """ A property. """ raise NotImplementedError @attr.s(slots=True) class SlotsDemo: """ An attrs class with slots=True """ #: An argument arg1: str = attr.ib() #: Another argument arg2: int = attr.ib() sphinx-toolbox-3.9.0/tests/test_rest_example.py000066400000000000000000000060001475757360300220000ustar00rootroot00000000000000# 3rd party from sphinx.events import EventListener # this package from sphinx_toolbox import rest_example from sphinx_toolbox.rest_example import make_rest_example, rest_example_purger, reSTExampleDirective from sphinx_toolbox.testing import run_setup from tests.common import AttrDict class FakeBuildEnvironment(AttrDict): def __init__(self, tab_width: int): config = AttrDict({"docutils_tab_width": tab_width}) super().__init__({"config": config}) def test_make_rest_example(): assert make_rest_example( {}, FakeBuildEnvironment(4), # type: ignore[arg-type] [], ) == [".. container:: rest-example", '', " .. code-block:: rest", ''] assert make_rest_example( {"hello": "world"}, FakeBuildEnvironment(4), # type: ignore[arg-type] [], ) == [".. container:: rest-example", '', " .. code-block:: rest", " :hello: world", ''] assert make_rest_example( {"hello": "world", "flag": None}, FakeBuildEnvironment(4), # type: ignore[arg-type] [], ) == [ ".. container:: rest-example", '', " .. code-block:: rest", " :hello: world", " :flag:", '' ] assert make_rest_example( {"hello": "world", "flag": None}, FakeBuildEnvironment(4), # type: ignore[arg-type] ["this is some content"], ) == [ ".. container:: rest-example", '', " .. code-block:: rest", " :hello: world", " :flag:", '', " this is some content", '', " this is some content", '', ] assert make_rest_example( {}, FakeBuildEnvironment(8), # type: ignore[arg-type] [], ) == [".. container:: rest-example", '', " .. code-block:: rest", ''] assert make_rest_example( {"hello": "world"}, FakeBuildEnvironment(8), # type: ignore[arg-type] [], ) == [ ".. container:: rest-example", '', " .. code-block:: rest", " :hello: world", '' ] assert make_rest_example( {"hello": "world", "flag": None}, FakeBuildEnvironment(8), # type: ignore[arg-type] [], ) == [ ".. container:: rest-example", '', " .. code-block:: rest", " :hello: world", " :flag:", '', ] assert make_rest_example( {"hello": "world", "flag": None}, FakeBuildEnvironment(8), # type: ignore[arg-type] ["this is some content"], ) == [ ".. container:: rest-example", '', " .. code-block:: rest", " :hello: world", " :flag:", '', " this is some content", '', " this is some content", '', ] def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(rest_example.setup) assert app.events.listeners == { "env-purge-doc": [EventListener(id=0, handler=rest_example_purger.purge_nodes, priority=500), ], } assert directives == { "rest-example": reSTExampleDirective, } assert app.registry.source_parsers == {} sphinx-toolbox-3.9.0/tests/test_setup.py000066400000000000000000000011021475757360300204460ustar00rootroot00000000000000# 3rd party from sphinx.events import EventListener # this package import sphinx_toolbox from sphinx_toolbox import config from sphinx_toolbox.testing import run_setup def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(sphinx_toolbox.setup) assert setup_ret == {"version": sphinx_toolbox.__version__, "parallel_read_safe": True} assert additional_nodes == set() assert app.registry.translation_handlers == {} assert app.events.listeners == { "config-inited": [EventListener(id=0, handler=config.validate_config, priority=850)], } sphinx-toolbox-3.9.0/tests/test_shields.py000066400000000000000000000024361475757360300207540ustar00rootroot00000000000000# 3rd party from coincidence.regressions import AdvancedFileRegressionFixture from domdf_python_tools.paths import PathPlus # this package import sphinx_toolbox from sphinx_toolbox import shields from sphinx_toolbox.testing import check_asset_copy, run_setup def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(shields.setup) assert setup_ret == {"version": sphinx_toolbox.__version__, "parallel_read_safe": True} assert additional_nodes == set() assert app.registry.translation_handlers == {} assert app.events.listeners == {} assert directives == { "rtfd-shield": shields.RTFDShield, "actions-shield": shields.GitHubActionsShield, "requires-io-shield": shields.RequiresIOShield, "coveralls-shield": shields.CoverallsShield, "codefactor-shield": shields.CodefactorShield, "pypi-shield": shields.PyPIShield, "github-shield": shields.GitHubShield, "maintained-shield": shields.MaintainedShield, "pre-commit-shield": shields.PreCommitShield, "pre-commit-ci-shield": shields.PreCommitCIShield, } def test_copy_asset_files(tmp_pathplus: PathPlus, advanced_file_regression: AdvancedFileRegressionFixture): check_asset_copy( shields.copy_asset_files, "_static/toolbox-shields.css", file_regression=advanced_file_regression, ) sphinx-toolbox-3.9.0/tests/test_shields_/000077500000000000000000000000001475757360300205345ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_shields_/test_copy_asset_files_toolbox-shields.css000066400000000000000000000001051475757360300310330ustar00rootroot00000000000000.table-wrapper td p img.sphinx_toolbox_shield{vertical-align:middle} sphinx-toolbox-3.9.0/tests/test_sidebar_links.py000066400000000000000000000042171475757360300221310ustar00rootroot00000000000000# stdlib from types import SimpleNamespace # 3rd party import pytest # this package from sphinx_toolbox import sidebar_links from sphinx_toolbox.testing import run_setup from tests.common import AttrDict class FakeBuildEnvironment(AttrDict): def __init__(self, tab_width: int): config = AttrDict({ "docutils_tab_width": tab_width, "github_username": "octocat", "github_repository": "hello_world", "conda_channels": [], }) app = AttrDict(extensions=[ "sphinx_toolbox.installation", "sphinx_toolbox.github", ]) super().__init__({"config": config, "app": app}) def test_missing_extension(): directive = SimpleNamespace() directive.env = SimpleNamespace() directive.env.app = AttrDict(extensions=["sphinx_toolbox.sidebar_links"]) with pytest.raises( ValueError, match="The 'sphinx_toolbox.github' extension is required for the :github: option but it is not enabled!" ): sidebar_links.SidebarLinksDirective.process_github_option(directive) # type: ignore[arg-type] def test_missing_username(): directive = SimpleNamespace() directive.env = SimpleNamespace() directive.env.app = AttrDict(extensions=["sphinx_toolbox.sidebar_links", "sphinx_toolbox.github"]) directive.env.config = AttrDict({"github_repository": "hello_world"}) with pytest.raises(ValueError, match="'github_username' has not been set in 'conf.py'!"): sidebar_links.SidebarLinksDirective.process_github_option(directive) # type: ignore[arg-type] def test_missing_repo(): directive = SimpleNamespace() directive.env = SimpleNamespace() directive.env.app = AttrDict(extensions=["sphinx_toolbox.sidebar_links", "sphinx_toolbox.github"]) directive.env.config = AttrDict({"github_username": "octocat"}) with pytest.raises(ValueError, match="'github_repository' has not been set in 'conf.py'!"): sidebar_links.SidebarLinksDirective.process_github_option(directive) # type: ignore[arg-type] def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(sidebar_links.setup) assert app.events.listeners == {} assert directives == {"sidebar-links": sidebar_links.SidebarLinksDirective} assert app.registry.source_parsers == {} sphinx-toolbox-3.9.0/tests/test_source.py000066400000000000000000000064151475757360300206220ustar00rootroot00000000000000# 3rd party import sphinx from apeye.requests_url import RequestsURL from docutils.nodes import inline, reference, system_message from docutils.utils import Reporter from sphinx import addnodes from sphinx.events import EventListener # this package from sphinx_toolbox import source from sphinx_toolbox.source import source_role from sphinx_toolbox.testing import run_setup from tests.common import AttrDict, get_app_config_values class FakeSourceInliner: def __init__(self, source_link_target: str, github_source_url: str): config = AttrDict({ "source_link_target": source_link_target, "github_source_url": RequestsURL(github_source_url), }) app = AttrDict({"config": config, "builder": AttrDict({"get_relative_uri": lambda from_, to: to})}) env = AttrDict({"app": app, "docname": ''}) settings = AttrDict({"env": env}) reporter = Reporter('', 0, 100) self.document = AttrDict({"settings": settings, "reporter": reporter}) def test_source_role_github(): github_source_url = "https://github.com/python/cpython/blob/master" nodes, messages = source_role('', '', "Lib/typing.py", 0, FakeSourceInliner("github", github_source_url)) # type: ignore[arg-type] assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages assert isinstance(nodes[0], reference) ref_node: reference = nodes[0] assert ref_node.attributes["refuri"] == "https://github.com/python/cpython/blob/master/Lib/typing.py" assert ref_node.rawsource == "Lib/typing.py" def test_source_role_sphinx(): nodes, messages = source_role('', '', "Lib/typing.py", 0, FakeSourceInliner("sphinx", '')) # type: ignore[arg-type] assert isinstance(nodes, list) assert isinstance(messages, list) assert not messages if sphinx.version_info[:3] < (3, 5, 0): assert isinstance(nodes[0], addnodes.pending_xref) assert isinstance(nodes[0].children[0], inline) assert nodes[0].attributes["reftype"] == "viewcode" assert nodes[0].attributes["refdomain"] == "std" assert nodes[0].attributes["reftarget"] == "_modules/Lib/typing" assert nodes[0].attributes["refid"] == "Lib/typing.py" assert not nodes[0].attributes["refexplicit"] else: assert isinstance(nodes[0], reference) assert isinstance(nodes[0].children[0], inline) assert nodes[0].attributes["refuri"] == "_modules/Lib/typing#Lib/typing.py" assert "reftitle" not in nodes[0].attributes assert "refid" not in nodes[0].attributes assert nodes[0].attributes["internal"] is True def test_source_role_unknown_target(capsys): nodes, messages = source_role('', '', "Lib/typing.py", 0, FakeSourceInliner("gitlab", '')) # type: ignore[arg-type] assert capsys.readouterr().err == ":: (ERROR/3) Unsupported source link target 'gitlab'.\n" assert isinstance(nodes, list) assert isinstance(messages, list) assert not nodes assert isinstance(messages[0], system_message) assert messages[0].astext() == ":: (ERROR/3) Unsupported source link target 'gitlab'." def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(source.setup) assert roles == {"source": source.source_role} assert get_app_config_values(app.config.values["source_link_target"]) == ("Sphinx", "env", [str]) assert app.registry.source_parsers == {} assert app.events.listeners == {"config-inited": [EventListener(0, source._configure, 500)]} sphinx-toolbox-3.9.0/tests/test_source_output_github/000077500000000000000000000000001475757360300232245ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_source_output_github/__init__.py000066400000000000000000000000001475757360300253230ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_source_output_github/conftest.py000066400000000000000000000070541475757360300254310ustar00rootroot00000000000000# Based on Sphinx # Copyright (c) 2007-2020 by the Sphinx team. # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib import pathlib import shutil from typing import Any, Dict, List, Sequence, Tuple, cast # 3rd party import pytest from bs4 import BeautifulSoup from domdf_python_tools.paths import PathPlus from sphinx.application import Sphinx from sphinx.testing.fixtures import app as gh_src_app from sphinx.testing.fixtures import make_app, shared_result, sphinx_test_tempdir, test_params # this package from tests.common import AppParams fixtures = [make_app, shared_result, sphinx_test_tempdir, test_params, gh_src_app] @pytest.fixture(scope="session") def rootdir() -> PathPlus: rdir = PathPlus(__file__).parent.absolute() / "github-doc-test" (rdir / "test-github-root").maybe_make(parents=True) return PathPlus(rdir) @pytest.fixture() def app_params( request: Any, test_params: Dict, sphinx_test_tempdir: pathlib.Path, rootdir: pathlib.Path, ) -> Tuple[Sequence, Dict]: """ parameters that is specified by 'pytest.mark.sphinx' for sphinx.application.Sphinx initialization """ # ##### process pytest.mark.sphinx markers = request.node.iter_markers("sphinx") pargs = {} kwargs: Dict[str, Any] = {} if markers is not None: # to avoid stacking positional args for info in reversed(list(markers)): for i, a in enumerate(info.args): pargs[i] = a kwargs.update(info.kwargs) args = [pargs[i] for i in sorted(pargs.keys())] # ##### prepare Application params testroot = "github-root" kwargs["srcdir"] = srcdir = sphinx_test_tempdir / kwargs.get("srcdir", testroot) # special support for sphinx/tests if rootdir and not srcdir.exists(): testroot_path = rootdir / ("test-" + testroot) shutil.copytree(testroot_path, srcdir) return AppParams(args, kwargs) @pytest.fixture() def github_source_page(gh_src_app: Sphinx, request) -> BeautifulSoup: gh_src_app.build(force_all=True) pagename = request.param c = (PathPlus(gh_src_app.outdir) / pagename).read_text() soup = BeautifulSoup(c, "html5lib") for meta in cast(List[Dict], soup.find_all("meta")): if meta.get("content", '') == "width=device-width, initial-scale=0.9, maximum-scale=0.9": meta.extract() # type: ignore[attr-defined] return soup sphinx-toolbox-3.9.0/tests/test_source_output_github/github-doc-test/000077500000000000000000000000001475757360300262265ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_source_output_github/github-doc-test/test-github-root/000077500000000000000000000000001475757360300314465ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_source_output_github/github-doc-test/test-github-root/conf.py000066400000000000000000000003151475757360300327440ustar00rootroot00000000000000extensions = [ "sphinx.ext.viewcode", "sphinx_toolbox", ] github_username = "sphinx-toolbox" github_repository = "sphinx-toolbox" source_link_target = "GitHub" project = "Python" author = "unknown" sphinx-toolbox-3.9.0/tests/test_source_output_github/github-doc-test/test-github-root/index.rst000066400000000000000000000002441475757360300333070ustar00rootroot00000000000000====================== sphinx-toolbox Demo ====================== :source:`sphinx_toolbox/config.py` Here is the :source:`source code ` sphinx-toolbox-3.9.0/tests/test_source_output_github/test_source_output.py000066400000000000000000000026221475757360300275570ustar00rootroot00000000000000# stdlib from typing import Optional, cast # 3rd party import pytest from bs4 import BeautifulSoup, Tag from sphinx.application import Sphinx # this package from sphinx_toolbox.testing import HTMLRegressionFixture def test_build_github(gh_src_app: Sphinx): # app is a Sphinx application object for default sphinx project (`tests/doc-test/sphinx-test-github-root`). gh_src_app.build() gh_src_app.build() @pytest.mark.usefixtures("docutils_17_compat") @pytest.mark.parametrize("github_source_page", ["index.html"], indirect=True) def test_output_github(github_source_page: BeautifulSoup, html_regression: HTMLRegressionFixture): # Make sure the page title is what you expect h1 = cast(Optional[Tag], github_source_page.find("h1")) assert h1 is not None title = cast(str, h1.contents[0]).strip() assert "sphinx-toolbox Demo" == title tag_count = 0 for a_tag in github_source_page.select("a.reference.external"): if tag_count == 0: if a_tag.contents[0] == "sphinx_toolbox/config.py": assert a_tag[ "href" ] == "https://github.com/sphinx-toolbox/sphinx-toolbox/blob/master/sphinx_toolbox/config.py" elif tag_count == 1: if a_tag.contents[0] == "source code": assert a_tag[ "href" ] == "https://github.com/sphinx-toolbox/sphinx-toolbox/blob/master/sphinx_toolbox/config.py" tag_count += 1 assert tag_count == 2 html_regression.check(github_source_page, jinja2=True) sphinx-toolbox-3.9.0/tests/test_source_output_github/test_source_output_/000077500000000000000000000000001475757360300273425ustar00rootroot00000000000000test_output_github_index_html_.html000066400000000000000000000056021475757360300364670ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_source_output_github/test_source_output_{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}

sphinx-toolbox Demo ΒΆ

sphinx_toolbox/config.py

Here is the source code

{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_source_output_sphinx/000077500000000000000000000000001475757360300232535ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_source_output_sphinx/__init__.py000066400000000000000000000000001475757360300253520ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_source_output_sphinx/conftest.py000066400000000000000000000070571475757360300254630ustar00rootroot00000000000000# Based on Sphinx # Copyright (c) 2007-2020 by the Sphinx team. # | All rights reserved. # | # | Redistribution and use in source and binary forms, with or without # | modification, are permitted provided that the following conditions are # | met: # | # | * Redistributions of source code must retain the above copyright # | notice, this list of conditions and the following disclaimer. # | # | * Redistributions in binary form must reproduce the above copyright # | notice, this list of conditions and the following disclaimer in the # | documentation and/or other materials provided with the distribution. # | # | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # stdlib import pathlib import shutil from typing import Any, Dict, List, Sequence, Tuple, cast # 3rd party import pytest from bs4 import BeautifulSoup from domdf_python_tools.paths import PathPlus from sphinx.application import Sphinx from sphinx.testing.fixtures import app as sphinx_src_app from sphinx.testing.fixtures import make_app, shared_result, sphinx_test_tempdir, test_params # this package from tests.common import AppParams fixtures = [make_app, shared_result, sphinx_test_tempdir, test_params, sphinx_src_app] @pytest.fixture() def rootdir() -> PathPlus: rdir = PathPlus(__file__).parent.absolute() / "sphinx-doc-test" (rdir / "test-sphinx-root").maybe_make(parents=True) return PathPlus(rdir) @pytest.fixture() def app_params( request: Any, test_params: Dict, sphinx_test_tempdir: pathlib.Path, rootdir: pathlib.Path, ) -> Tuple[Sequence, Dict]: """ parameters that is specified by 'pytest.mark.sphinx' for sphinx.application.Sphinx initialization """ # ##### process pytest.mark.sphinx markers = request.node.iter_markers("sphinx") pargs = {} kwargs: Dict[str, Any] = {} if markers is not None: # to avoid stacking positional args for info in reversed(list(markers)): for i, a in enumerate(info.args): pargs[i] = a kwargs.update(info.kwargs) args = [pargs[i] for i in sorted(pargs.keys())] # ##### prepare Application params testroot = "sphinx-root" kwargs["srcdir"] = srcdir = sphinx_test_tempdir / kwargs.get("srcdir", testroot) # special support for sphinx/tests if rootdir and not srcdir.exists(): testroot_path = rootdir / "test-sphinx-root" shutil.copytree(testroot_path, srcdir) return AppParams(args, kwargs) @pytest.fixture() def sphinx_source_page(sphinx_src_app: Sphinx, request) -> BeautifulSoup: sphinx_src_app.build(force_all=True) pagename = request.param c = (PathPlus(sphinx_src_app.outdir) / pagename).read_text() soup = BeautifulSoup(c, "html5lib") for meta in cast(List[Dict], soup.find_all("meta")): if meta.get("content", '') == "width=device-width, initial-scale=0.9, maximum-scale=0.9": meta.extract() # type: ignore[attr-defined] return soup sphinx-toolbox-3.9.0/tests/test_source_output_sphinx/sphinx-doc-test/000077500000000000000000000000001475757360300263045ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_source_output_sphinx/sphinx-doc-test/test-sphinx-root/000077500000000000000000000000001475757360300315535ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_source_output_sphinx/sphinx-doc-test/test-sphinx-root/code.rst000066400000000000000000000000551475757360300332170ustar00rootroot00000000000000 .. automodule:: sphinx_toolbox.more_autodoc sphinx-toolbox-3.9.0/tests/test_source_output_sphinx/sphinx-doc-test/test-sphinx-root/conf.py000066400000000000000000000003151475757360300330510ustar00rootroot00000000000000extensions = [ "sphinx.ext.viewcode", "sphinx_toolbox", ] github_username = "sphinx-toolbox" github_repository = "sphinx-toolbox" source_link_target = "Sphinx" project = "Python" author = "unknown" sphinx-toolbox-3.9.0/tests/test_source_output_sphinx/sphinx-doc-test/test-sphinx-root/index.rst000066400000000000000000000004671475757360300334230ustar00rootroot00000000000000============================================ sphinx-toolbox Demo - Sphinx source ============================================ :source:`sphinx_toolbox/config.py` Here is the :source:`source code ` :source:`sphinx_toolbox/source.py` :source:`sphinx_toolbox/more_autodoc/__init__.py` sphinx-toolbox-3.9.0/tests/test_source_output_sphinx/test_source_output.py000066400000000000000000000025401475757360300276050ustar00rootroot00000000000000# stdlib from typing import Optional, cast # 3rd party import pytest from bs4 import BeautifulSoup, Tag from sphinx.application import Sphinx # this package from sphinx_toolbox.testing import HTMLRegressionFixture def test_build_sphinx(sphinx_src_app: Sphinx): # app is a Sphinx application object for default sphinx project (`tests/doc-test/sphinx-test-github-root`). sphinx_src_app.build() sphinx_src_app.build() @pytest.mark.usefixtures("docutils_17_compat") @pytest.mark.parametrize("sphinx_source_page", ["index.html"], indirect=True) def test_output_sphinx(sphinx_source_page: BeautifulSoup, html_regression: HTMLRegressionFixture): # Make sure the page title is what you expect h1 = cast(Optional[Tag], sphinx_source_page.find("h1")) assert h1 is not None title = cast(str, h1.contents[0]).strip() assert "sphinx-toolbox Demo - Sphinx source" == title tag_count = 0 for a_tag in sphinx_source_page.select("a.reference.internal"): if tag_count == 0: if a_tag.contents[0] == "sphinx_toolbox/config.py": assert a_tag["href"] == "_modules/sphinx_toolbox/config.html#sphinx_toolbox/config.py" elif tag_count == 1: if a_tag.contents[0] == "source code": assert a_tag["href"] == "_modules/sphinx_toolbox/config.html#sphinx_toolbox/config.py" tag_count += 1 assert tag_count == 4 html_regression.check(sphinx_source_page, jinja2=True) sphinx-toolbox-3.9.0/tests/test_source_output_sphinx/test_source_output_/000077500000000000000000000000001475757360300273715ustar00rootroot00000000000000test_output_sphinx_index_html_.html000066400000000000000000000066151475757360300365520ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_source_output_sphinx/test_source_output_{% set permalink = ("Link" if sphinx_version >= (7, 2) else 'Permalink') -%} {% set heading = ("heading" if sphinx_version >= (5, 0) else 'headline') -%} = (7, 2) %} data-content_root="./"{% endif %}{% if sphinx_version >= (5, 0) %} lang="en"{% endif %}> {% if docutils_version[1] == 18 %} {% elif docutils_version[1] == 17 %} {% elif docutils_version[1] == 19 %} {% elif docutils_version[1] >= 20 %} {% endif %} sphinx-toolbox Demo - Sphinx source β€” Python documentation {% if sphinx_version >= (7, 2) %} {% if sphinx_version < (6, 0) %} {% if sphinx_version >= (5, 0) %} {% endif %}{% endif %} {% if sphinx_version >= (5, 2) %} {% endif %}
{% if sphinx_version >= (8, 1) %}
{% endif %}
sphinx-toolbox-3.9.0/tests/test_testing.py000066400000000000000000000117431475757360300207770ustar00rootroot00000000000000# stdlib from typing import Any, Dict # 3rd party import pytest from docutils.nodes import NodeVisitor from docutils.transforms import Transform from pygments.lexer import Lexer # type: ignore[import-untyped] from sphinx.builders import Builder from sphinx.domains import Domain from sphinx.events import EventListener from sphinx.highlighting import lexer_classes # this package from sphinx_toolbox.config import validate_config from sphinx_toolbox.github.issues import IssueNode, depart_issue_node, visit_issue_node from sphinx_toolbox.source import source_role from sphinx_toolbox.testing import Sphinx, run_setup from tests.common import get_app_config_values class FakeBuilder(Builder): name = "FakeBuilder" class FakeNodeVisitor(NodeVisitor): pass class FakeDomain(Domain): name = "FakeDomain" class FakeTransform(Transform): pass class FakeLexer(Lexer): pass def get_fake_lexer(*args) -> None: return None def __setup(app: Sphinx) -> Dict[str, Any]: """ Setup Sphinx Extension. :param app: The Sphinx app. """ app.add_builder(FakeBuilder, override=True) app.add_config_value("source_link_target", "Sphinx", "env", types=[str]) app.add_config_value("github_username", None, "env", types=[str]) app.add_config_value("rebuild_true", None, True) app.add_config_value("rebuild_false", None, False) app.add_event("my-event") app.set_translator("my-translator", FakeNodeVisitor) app.add_node(IssueNode, html=(visit_issue_node, depart_issue_node)) with pytest.raises( ValueError, match="node class 'IssueNode' is already registered, its visitors will be overridden" ): app.add_node(IssueNode, html=(visit_issue_node, depart_issue_node)) app.add_node(IssueNode, html=(visit_issue_node, depart_issue_node), override=True) # TODO: add_enumerable_node # TODO: add_directive app.add_role("source", source_role) with pytest.raises(ValueError, match="role 'source' is already registered, it will be overridden"): app.add_role("source", source_role) app.add_role("source", source_role, override=True) # TODO: add_generic_role app.add_domain(FakeDomain) app.add_domain(FakeDomain, override=True) app.add_role_to_domain("FakeDomain", "source", source_role) # type: ignore[arg-type] app.add_role_to_domain("FakeDomain", "source", source_role, override=True) # type: ignore[arg-type] # TODO: add_directive_to_domain # TODO: add_role_to_domain # TODO: add_index_to_domain # TODO: add_object_type # TODO: add_crossref_type app.add_transform(FakeTransform) app.add_post_transform(FakeTransform) app.add_latex_package("booktabs") app.add_latex_package("glossaries", options="acronyms") app.add_latex_package("chemformula", after_hyperref=True) app.add_lexer("my-lexer", FakeLexer) assert isinstance(FakeLexer(code='', language=''), Lexer) with pytest.raises( TypeError, match=r"app.add_lexer\(\) API changed; Please give lexer class instead instance" ): app.add_lexer("my-lexer", FakeLexer(code='', language='')) # TODO: add_autodocumenter app.add_autodoc_attrgetter(FakeLexer, get_fake_lexer) app.add_source_suffix(".py", "python") app.add_source_suffix(".py", "python", override=True) # TODO: add_source_parser # TODO: add_env_collector app.add_html_theme("domdf_sphinx_theme", '.') # TODO: add_html_math_renderer app.connect("config-inited", validate_config, priority=850) return {"version": 12345, "parallel_read_safe": True} def test_testing(): setup_ret, directives, roles, additional_nodes, app = run_setup(__setup) assert app.registry.builders["FakeBuilder"] == FakeBuilder assert get_app_config_values(app.config.values["source_link_target"]) == ("Sphinx", "env", [str]) assert get_app_config_values(app.config.values["github_username"]) == (None, "env", [str]) assert get_app_config_values(app.config.values["rebuild_true"]) == (None, "env", []) assert get_app_config_values(app.config.values["rebuild_false"]) == (None, '', []) assert app.events.events["my-event"] == '' assert app.registry.translators["my-translator"] is FakeNodeVisitor assert additional_nodes == {IssueNode} assert app.registry.translation_handlers == {"html": {"IssueNode": (visit_issue_node, depart_issue_node)}} assert roles == {"source": source_role} assert app.registry.domains["FakeDomain"] == FakeDomain assert app.registry.domain_roles.setdefault("FakeDomain", {})["source"] is source_role assert app.registry.transforms == [FakeTransform] assert app.registry.post_transforms == [FakeTransform] assert app.registry.latex_packages == [ ("booktabs", None), ("glossaries", "acronyms"), ] assert app.registry.latex_packages_after_hyperref == [("chemformula", None)] assert lexer_classes["my-lexer"] == FakeLexer assert app.registry.autodoc_attrgettrs[FakeLexer] is get_fake_lexer assert app.registry.source_suffix[".py"] == "python" assert app.html_themes == {"domdf_sphinx_theme": '.'} assert app.events.listeners["config-inited"] == [EventListener(id=0, handler=validate_config, priority=850)] assert setup_ret == {"version": 12345, "parallel_read_safe": True} sphinx-toolbox-3.9.0/tests/test_tweaks/000077500000000000000000000000001475757360300202405ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_tweaks/__init__.py000066400000000000000000000000001475757360300223370ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_tweaks/test_footnote_symbols.py000066400000000000000000000012741475757360300252620ustar00rootroot00000000000000# 3rd party from docutils.transforms.references import Footnotes # this package from sphinx_toolbox import __version__ from sphinx_toolbox.testing import run_setup from sphinx_toolbox.tweaks import footnote_symbols def test_setup(): original_symbols = Footnotes.symbols try: setup_ret, directives, roles, additional_nodes, app = run_setup(footnote_symbols.setup) assert setup_ret == {"version": __version__, "parallel_read_safe": True} assert directives == {} assert additional_nodes == set() assert app.registry.translation_handlers == {} assert app.events.listeners == {} assert Footnotes.symbols == footnote_symbols.symbols finally: Footnotes.symbols = original_symbols sphinx-toolbox-3.9.0/tests/test_tweaks/test_param_dash.py000066400000000000000000000012671475757360300237560ustar00rootroot00000000000000# 3rd party import sphinx.util.docfields # this package import sphinx_toolbox from sphinx_toolbox.testing import run_setup from sphinx_toolbox.tweaks import param_dash def test_setup(): original_make_field = sphinx.util.docfields.TypedField.make_field try: setup_ret, directives, roles, additional_nodes, app = run_setup(param_dash.setup) assert setup_ret == {"version": sphinx_toolbox.__version__, "parallel_read_safe": True} assert directives == {} assert additional_nodes == set() assert app.registry.translation_handlers == {} assert app.events.listeners == {} finally: sphinx.util.docfields.TypedField.make_field = original_make_field # type: ignore[method-assign] sphinx-toolbox-3.9.0/tests/test_tweaks/test_sphinx_panels_tabs.py000066400000000000000000000017771475757360300255510ustar00rootroot00000000000000# 3rd party from coincidence.regressions import AdvancedFileRegressionFixture from docutils import nodes from domdf_python_tools.paths import PathPlus # this package import sphinx_toolbox from sphinx_toolbox.testing import check_asset_copy, run_setup from sphinx_toolbox.tweaks import sphinx_panels_tabs def test_copy_asset_files(tmp_pathplus: PathPlus, advanced_file_regression: AdvancedFileRegressionFixture): check_asset_copy( sphinx_panels_tabs.copy_asset_files, "_static/css/tabs_customise.css", file_regression=advanced_file_regression, ) def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(sphinx_panels_tabs.setup) assert setup_ret == {"version": sphinx_toolbox.__version__, "parallel_read_safe": True} assert directives == {} assert additional_nodes == {nodes.container} assert app.registry.translation_handlers == { "html": {"container": (sphinx_panels_tabs.visit_container, sphinx_panels_tabs.depart_container)} } assert app.events.listeners == {} sphinx-toolbox-3.9.0/tests/test_tweaks/test_sphinx_panels_tabs_/000077500000000000000000000000001475757360300253225ustar00rootroot00000000000000test_copy_asset_files_tabs_customise.css000066400000000000000000000003171475757360300354540ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_tweaks/test_sphinx_panels_tabs_.docutils.container { padding-left: 0 !important; padding-right: 0 !important } div.ui.top.attached.tabular.menu.sphinx-menu.docutils.container { margin-left: 0 !important; margin-right: 0 !important } sphinx-toolbox-3.9.0/tests/test_tweaks/test_tabsize.py000066400000000000000000000011751475757360300233160ustar00rootroot00000000000000# 3rd party from coincidence.regressions import AdvancedFileRegressionFixture from pprint36 import pformat # this package from sphinx_toolbox import __version__ from sphinx_toolbox.testing import run_setup from sphinx_toolbox.tweaks import tabsize def test_setup(advanced_file_regression: AdvancedFileRegressionFixture): setup_ret, directives, roles, additional_nodes, app = run_setup(tabsize.setup) assert setup_ret == {"version": __version__, "parallel_read_safe": True} advanced_file_regression.check( pformat( app.registry.source_parsers, sort_dicts=True, ), extension=".dict", encoding="UTF-8", ) sphinx-toolbox-3.9.0/tests/test_tweaks/test_tabsize_/000077500000000000000000000000001475757360300230775ustar00rootroot00000000000000sphinx-toolbox-3.9.0/tests/test_tweaks/test_tabsize_/test_setup.dict000066400000000000000000000007621475757360300261500ustar00rootroot00000000000000{'rest': .CustomRSTParser'>, 'restructuredtext': .CustomRSTParser'>, 'restx': .CustomRSTParser'>, 'rst': .CustomRSTParser'>, 'rstx': .CustomRSTParser'>, 'rtxt': .CustomRSTParser'>} sphinx-toolbox-3.9.0/tests/test_utils.py000066400000000000000000000123011475757360300204510ustar00rootroot00000000000000# stdlib import collections import inspect import string import sys from typing import List, NamedTuple # 3rd party import pytest from apeye.requests_url import RequestsURL from domdf_python_tools.utils import strtobool from hypothesis import given from hypothesis.strategies import text from sphinx.application import Sphinx # this package from sphinx_toolbox import __version__ from sphinx_toolbox.utils import ( NoMatchError, Purger, SphinxExtMetadata, code_repr, escape_trailing__, flag, get_first_matching, is_namedtuple, make_github_url, metadata_add_version, parse_parameters, singleton ) def test_make_github_url(): url = make_github_url("sphinx-toolbox", "sphinx-toolbox") assert isinstance(url, RequestsURL) assert url == RequestsURL("https://github.com/sphinx-toolbox/sphinx-toolbox") def test_flag(): assert flag('') assert flag(' ') assert flag(" ") assert flag(" ") assert flag(" ") assert flag('\t') assert flag(False) with pytest.raises(AttributeError): flag(True) with pytest.raises(ValueError, match="No argument is allowed; 'hello' supplied"): flag("hello") class MockBuildEnvironment: pass demo_purger = Purger("all_demo_nodes") @pytest.mark.parametrize( "nodes, output", [ ([], []), ([{"docname": "document"}], []), ([{"docname": "foo"}], [{"docname": "foo"}]), ([{"docname": "foo"}, {"docname": "document"}], [{"docname": "foo"}]), ] ) def test_purge_extras_require(nodes: List[str], output: List[str]): env = MockBuildEnvironment() demo_purger.purge_nodes('', env, "document") # type: ignore[arg-type] assert not hasattr(env, "all_extras_requires") env.all_demo_nodes = nodes # type: ignore[attr-defined] demo_purger.purge_nodes('', env, "document") # type: ignore[arg-type] assert hasattr(env, "all_demo_nodes") assert env.all_demo_nodes == output def test_get_first_matching(): assert get_first_matching(strtobool, [True, "True", 0, "False", False]) assert get_first_matching(strtobool, (True, "True", 0, "False", False)) with pytest.raises( NoMatchError, match=r"No matching values for '' in \[0, 'False', False\]", ): get_first_matching(strtobool, [0, "False", False]) assert get_first_matching(strtobool, [0, "False", False], default=True) assert get_first_matching(lambda x: x.isupper(), string.ascii_letters) == 'A' with pytest.raises( ValueError, match="The condition must evaluate to True for the default value.", ): get_first_matching(lambda x: x.isdigit(), string.ascii_letters, default='A') def test_singleton(): s = singleton('s') assert str(s) == 's' assert repr(s) == 's' assert s is s assert s is s.__class__() assert s is type(s)() @given(text(alphabet=string.ascii_letters + string.digits, min_size=1)) def test_escape_trailing_underscore(s: str): assert escape_trailing__(s) == s assert escape_trailing__(f"{s}_") == rf"{s}\_" assert escape_trailing__(f"{s}__") == rf"{s}_\_" assert escape_trailing__(f"_{s}") == f"_{s}" @pytest.mark.parametrize( "value, expected", [ pytest.param("hello", "``'hello'``"), pytest.param("it's me!", "``\"it's me!\"``"), ] ) def test_code_repr(value: str, expected: str): assert code_repr(value) == expected @pytest.mark.xfail( sys.version_info >= (3, 13), reason="Tabs are now converted into 8 spaces unconditionally (see python/cpython#81283)" ) def test_parse_parameters(): docstring = inspect.cleandoc((parse_parameters.__doc__ or '').expandtabs(4)) docstring_dict = { "lines": {"doc": ["The lines of the docstring"], "type": ''}, "tab_size": {"doc": [''], "type": ''}, } pre_output = [ "Parse parameters from the docstring of a class/function.", '', ".. versionadded:: 0.8.0", '', ] post_output = [ '', ":return: A mapping of parameter names to their docstrings and types, a list of docstring lines that", " appeared before the parameters, and the list of docstring lines that appear after the parameters.", ] assert parse_parameters(docstring.split('\n'), tab_size=4) == (docstring_dict, pre_output, post_output) class NT(NamedTuple): foo: str bar: int @pytest.mark.parametrize( "obj, result", [ pytest.param("abc", False, id="str"), pytest.param(123, False, id="int"), pytest.param(123.456, False, id="float"), pytest.param(("abc", 123), False, id="tuple"), pytest.param( collections.namedtuple("Foo", "str, int")("abc", 123), # type: ignore[call-arg] False, id="namedtuple", ), pytest.param(NT("abc", 123), False, id="typing.NamedTuple"), pytest.param(str, False, id="type str"), pytest.param(int, False, id="type int"), pytest.param(float, False, id="type float"), pytest.param(tuple, False, id="type tuple"), pytest.param(collections.namedtuple("Foo", "str, int"), True, id="type namedtuple"), pytest.param(NT, True, id="type typing.NamedTuple"), ] ) def test_is_namedtuple(obj: object, result: bool): assert is_namedtuple(obj) is result def test_metadata_add_version(): @metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: return {"parallel_read_safe": True} assert setup(None) == {"parallel_read_safe": True, "version": __version__} # type: ignore[arg-type] sphinx-toolbox-3.9.0/tests/test_wikipedia.py000066400000000000000000000052311475757360300212630ustar00rootroot00000000000000# 3rd party from docutils import nodes # this package from sphinx_toolbox import wikipedia from sphinx_toolbox.testing import run_setup from tests.common import get_app_config_values def test_make_wikipedia_link(monkeypatch): monkeypatch.setattr(wikipedia, "_get_wikipedia_lang", lambda *args: "en") generated_nodes, warnings = wikipedia.make_wikipedia_link( "wikipedia", ":wikipedia:`Python (programming language)`", "Python (programming language)", lineno=27, inliner=None, # type: ignore[arg-type] ) assert isinstance(generated_nodes, list) assert generated_nodes assert isinstance(generated_nodes[0], nodes.reference) assert generated_nodes[0].rawsource == ":wikipedia:`Python (programming language)`" assert generated_nodes[0].astext() == "Python (programming language)" assert generated_nodes[0]["refuri"] == "https://en.wikipedia.org/wiki/Python_%28programming_language%29" assert isinstance(warnings, list) assert not warnings def test_make_wikipedia_link_lang(monkeypatch): generated_nodes, warnings = wikipedia.make_wikipedia_link( "wikipedia", ":wikipedia:`:zh:ζ–―θŠ¬ε…‹ζ–―`", ":zh:ζ–―θŠ¬ε…‹ζ–―", lineno=27, inliner=None, # type: ignore[arg-type] ) assert isinstance(generated_nodes, list) assert generated_nodes assert isinstance(generated_nodes[0], nodes.reference) assert generated_nodes[0].rawsource == ":wikipedia:`:zh:ζ–―θŠ¬ε…‹ζ–―`" assert generated_nodes[0].astext() == "ζ–―θŠ¬ε…‹ζ–―" assert generated_nodes[0]["refuri"] == "https://zh.wikipedia.org/wiki/%E6%96%AF%E8%8A%AC%E5%85%8B%E6%96%AF" assert isinstance(warnings, list) assert not warnings def test_make_wikipedia_link_with_label(monkeypatch): monkeypatch.setattr(wikipedia, "_get_wikipedia_lang", lambda *args: "en") generated_nodes, warnings = wikipedia.make_wikipedia_link( "wikipedia", ":wikipedia:`Python `", "Python ", lineno=27, inliner=None, # type: ignore[arg-type] ) assert isinstance(generated_nodes, list) assert generated_nodes assert isinstance(generated_nodes[0], nodes.reference) assert generated_nodes[0].rawsource == ":wikipedia:`Python `" assert generated_nodes[0].astext() == "Python" assert generated_nodes[0]["refuri"] == "https://en.wikipedia.org/wiki/Python_%28programming_language%29" assert isinstance(warnings, list) assert not warnings def test_setup(): setup_ret, directives, roles, additional_nodes, app = run_setup(wikipedia.setup) assert roles == {"wikipedia": wikipedia.make_wikipedia_link} assert get_app_config_values(app.config.values["wikipedia_lang"]) == ("en", "env", [str]) assert app.registry.source_parsers == {} sphinx-toolbox-3.9.0/tox.ini000066400000000000000000000330301475757360300160530ustar00rootroot00000000000000# 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:.package # * testenv:py313-dev # * testenv:py313 # * testenv:py312-dev # * testenv:py312 # * testenv:docs # * testenv:build # * testenv:lint # * testenv:perflint # * testenv:mypy # * testenv:pyup # * testenv:coverage # * flake8 # * coverage:run # * coverage:report # * check-wheel-contents [tox] envlist = py37-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3} py38-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1} py39-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4} py310-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1} py311-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2} py312-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2} py313-sphinx{6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2} pypy37-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3} pypy38-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1} pypy39-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4} 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 = py37-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3} py38-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1} py39-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4} py310-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1} py311-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2} py312-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2} py313-sphinx{6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2} pypy37-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3} pypy38-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1} pypy39-sphinx{4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4} qa = mypy, lint cov = py38-sphinx3.2, coverage raspi = py3{7,8,9,10}-sphinx4.5 [testenv:.package] setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 [testenv:py313] download = True setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 UNSAFE_PYO3_SKIP_VERSION_CHECK=1 [testenv:py312] download = True 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 = all 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 PIP_PREFER_BINARY=1 UNSAFE_PYO3_SKIP_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.8 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,<2 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 sphinx_toolbox tests --allow-toolbox {posargs} [testenv:perflint] basepython = python3.8 changedir = {toxinidir} ignore_errors = True skip_install = True deps = perflint commands = python3 -m perflint sphinx_toolbox {posargs} [testenv:mypy] basepython = python3.8 ignore_errors = True changedir = {toxinidir} extras = all deps = mypy==1.11.0 -r{toxinidir}/tests/requirements.txt -r{toxinidir}/stubs.txt commands = mypy sphinx_toolbox tests {posargs} [testenv:pyup] basepython = python3.8 skip_install = True ignore_errors = True changedir = {toxinidir} deps = pyupgrade-directories extras = all commands = pyup_dirs sphinx_toolbox tests --py36-plus --recursive [testenv:coverage] basepython = python3.8 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 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 autoclasssumm envvar extras-require license license-info versionremoved rst-roles = bold-title choosealicense event 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.7 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 [coverage:report] fail_under = 90 show_missing = True exclude_lines = raise AssertionError raise NotImplementedError if 0: if False: if TYPE_CHECKING if typing.TYPE_CHECKING if __name__ == .__main__.: [check-wheel-contents] ignore = W002 toplevel = sphinx_toolbox package = sphinx_toolbox [testenv:py312-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2}] download = True setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 [testenv:py313-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0,5.1,5.2,5.3,6.0,6.1,6.2,7.0,7.1,7.2,7.3,7.4,8.0,8.1,8.2}] download = True setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 UNSAFE_PYO3_SKIP_VERSION_CHECK=1 [testenv] setenv = PYTHONDEVMODE=1 PIP_DISABLE_PIP_VERSION_CHECK=1 SETUPTOOLS_USE_DISTUTILS=stdlib deps = -r{toxinidir}/tests/requirements.txt -r{toxinidir}/requirements.txt sphinx3.2: sphinx==3.2.1 sphinx3.3: sphinx==3.3.1 sphinx3.4: sphinx==3.4.3 sphinx3.5: sphinx==3.5.4 sphinx3.{2,3,4,5}: docutils==0.16 sphinx4.0: sphinx==4.0.3 sphinx4.1: sphinx==4.1.2 sphinx4.2: sphinx==4.2.0 sphinx4.3: sphinx==4.3.2 sphinx4.4: sphinx==4.4.0 sphinx4.5: sphinx==4.5.0 sphinx4.{0,1,2,3,4,5}: docutils==0.17.1 sphinx5.0: sphinx==5.0.2 sphinx5.1: sphinx==5.1.1 sphinx5.2: sphinx==5.2.3 sphinx5.3: sphinx==5.3.0 sphinx6.0: sphinx==6.0.1 sphinx6.1: sphinx==6.1.3 sphinx6.2: sphinx==6.2.1 sphinx7.0: sphinx==7.0.1 sphinx7.1: sphinx==7.1.2 sphinxcontrib-applehelp<=1.0.4 sphinxcontrib-devhelp<=1.0.2 sphinxcontrib-htmlhelp<=2.0.1 sphinxcontrib-jsmath<=1.0.1 sphinxcontrib-qthelp<=1.0.3 sphinxcontrib-serializinghtml<=1.1.5 alabaster<=0.7.13 extras = all commands = python --version python -m pytest --cov=sphinx_toolbox -r aR tests/ {posargs} [testenv:py{39,310,311,312,313-dev,py39}-sphinx7.2] deps = -r{toxinidir}/tests/requirements.txt -r{toxinidir}/requirements.txt commands = pip install sphinx==7.2.6 pip install pygments>=2.7.4,<=2.13.0 python --version python -m pytest --cov=sphinx_toolbox -r aR tests/ {posargs} [testenv:py{39,310,311,312,313-dev,py39}-sphinx7.3] deps = -r{toxinidir}/tests/requirements.txt -r{toxinidir}/requirements.txt commands = pip install sphinx==7.3.7 pip install pygments>=2.7.4,<=2.13.0 python --version python -m pytest --cov=sphinx_toolbox -r aR tests/ {posargs} [testenv:py{39,310,311,312,313-dev,py39}-sphinx7.4] deps = -r{toxinidir}/tests/requirements.txt -r{toxinidir}/requirements.txt commands = pip install sphinx==7.4.7 pip install pygments>=2.7.4,<=2.13.0 python --version python -m pytest --cov=sphinx_toolbox -r aR tests/ {posargs} [testenv:py{310,311,312,313-dev}-sphinx8.0] deps = -r{toxinidir}/tests/requirements.txt -r{toxinidir}/requirements.txt commands = pip install sphinx==8.0.2 pip install pygments>=2.7.4,<=2.13.0 python --version python -m pytest --cov=sphinx_toolbox -r aR tests/ {posargs} [testenv:py{310,311,312,313-dev}-sphinx8.1] deps = -r{toxinidir}/tests/requirements.txt -r{toxinidir}/requirements.txt commands = pip install sphinx==8.1.3 pip install pygments>=2.7.4,<=2.13.0 python --version python -m pytest --cov=sphinx_toolbox -r aR tests/ {posargs} [testenv:py{311,312,313-dev}-sphinx8.2] deps = -r{toxinidir}/tests/requirements.txt -r{toxinidir}/requirements.txt commands = pip install sphinx==8.2.1 pip install pygments>=2.7.4,<=2.13.0 python --version python -m pytest --cov=sphinx_toolbox -r aR tests/ {posargs} [pytest] addopts = --color yes --durations 25 timeout = 600 markers = sphinx filterwarnings = error ignore:distutils Version classes are deprecated. Use packaging.version instead.:DeprecationWarning ignore:The distutils package is deprecated and slated for removal in Python 3.12. Use setuptools or check PEP 632 for potential alternatives:DeprecationWarning ignore:can't resolve package from __spec__ or __package__, falling back on __name__ and __path__:ImportWarning always:module 'sre_(constants|parse)' is deprecated:DeprecationWarning always:'imghdr' is deprecated and slated for removal in Python 3.13:DeprecationWarning:sphinx.util.images always:pkg_resources is deprecated as an API:DeprecationWarning always:Deprecated call to `pkg_resources.declare_namespace:DeprecationWarning always:datetime.datetime.utcfromtimestamp\(\) is deprecated and scheduled for removal in a future version:DeprecationWarning:sphinx.builders[.*] always:The frontend.OptionParser class will be replaced by a subclass of argparse.ArgumentParser in Docutils 0.21 or later.:DeprecationWarning always:The frontend.Option class will be removed in Docutils 0.21 or later.:DeprecationWarning always:ast.Str is deprecated and will be removed in Python 3.14; use ast.Constant instead:DeprecationWarning always:Attribute s is deprecated and will be removed in Python 3.14; use value instead:DeprecationWarning always:The str interface for .JavaScript objects is deprecated. Use js.filename instead. always:The tuple interface of ObjectMember is deprecated. Use \(obj.__name__, obj.object\) instead.:DeprecationWarning:autodocsumm always:Returning tuples of \(name, object\) as the second return value from get_object_members\(\) is deprecated.:DeprecationWarning:autodocsumm always:The `docutils.parsers.rst.directive.html` module will be removed in Docutils 2.0.:DeprecationWarning always:'cgi' is deprecated and slated for removal in Python 3.13:DeprecationWarning always:nodes.Node.traverse\(\) is obsoleted by Node.findall\(\).:PendingDeprecationWarning [testenv:py{py37,py38,py39}-sphinx{3.3,3.4,3.5,4.0,4.1,4.2,4.3,4.4,4.5,5.0}] commands = python --version python -m pytest -p no:cov -r aR tests/ {posargs}