pax_global_header00006660000000000000000000000064147676105010014521gustar00rootroot0000000000000052 comment=ef77075c65e8107d7fba4ac063ddb09d6a7dfd59 mkdocstrings-python-legacy-0.2.6/000077500000000000000000000000001476761050100170165ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/.copier-answers.yml000066400000000000000000000012521476761050100225600ustar00rootroot00000000000000# Changes here will be overwritten by Copier. _commit: 1.7.1 _src_path: gh:pawamoy/copier-uv author_email: dev@pawamoy.fr author_fullname: Timothée Mazzucotelli author_username: pawamoy copyright_date: '2021' copyright_holder: Timothée Mazzucotelli copyright_holder_email: dev@pawamoy.fr copyright_license: ISC insiders: false project_description: A legacy Python handler for mkdocstrings. project_name: mkdocstrings-python (legacy) python_package_command_line_name: '' python_package_distribution_name: mkdocstrings-python-legacy python_package_import_name: mkdocstrings_handlers repository_name: python-legacy repository_namespace: mkdocstrings repository_provider: github.com mkdocstrings-python-legacy-0.2.6/.envrc000066400000000000000000000000211476761050100201250ustar00rootroot00000000000000PATH_add scripts mkdocstrings-python-legacy-0.2.6/.github/000077500000000000000000000000001476761050100203565ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/.github/FUNDING.yml000066400000000000000000000000371476761050100221730ustar00rootroot00000000000000github: pawamoy polar: pawamoy mkdocstrings-python-legacy-0.2.6/.github/ISSUE_TEMPLATE/000077500000000000000000000000001476761050100225415ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/.github/ISSUE_TEMPLATE/1-bug.md000066400000000000000000000027421476761050100240030ustar00rootroot00000000000000--- name: Bug report about: Create a bug report to help us improve. title: "bug: " labels: unconfirmed assignees: [pawamoy] --- ### Description of the bug ### To Reproduce ``` WRITE MRE / INSTRUCTIONS HERE ``` ### Full traceback
Full traceback ```python PASTE TRACEBACK HERE ```
### Expected behavior ### Environment information ```bash python -m mkdocstrings_handlers.python._internal.debug # | xclip -selection clipboard ``` PASTE MARKDOWN OUTPUT HERE ### Additional context mkdocstrings-python-legacy-0.2.6/.github/ISSUE_TEMPLATE/2-feature.md000066400000000000000000000012131476761050100246520ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project. title: "feature: " labels: feature assignees: pawamoy --- ### Is your feature request related to a problem? Please describe. ### Describe the solution you'd like ### Describe alternatives you've considered ### Additional context mkdocstrings-python-legacy-0.2.6/.github/ISSUE_TEMPLATE/3-docs.md000066400000000000000000000011311476761050100241470ustar00rootroot00000000000000--- name: Documentation update about: Point at unclear, missing or outdated documentation. title: "docs: " labels: docs assignees: pawamoy --- ### Is something unclear, missing or outdated in our documentation? ### Relevant code snippets ### Link to the relevant documentation section mkdocstrings-python-legacy-0.2.6/.github/ISSUE_TEMPLATE/4-change.md000066400000000000000000000011261476761050100244510ustar00rootroot00000000000000--- name: Change request about: Suggest any other kind of change for this project. title: "change: " assignees: pawamoy --- ### Is your change request related to a problem? Please describe. ### Describe the solution you'd like ### Describe alternatives you've considered ### Additional context mkdocstrings-python-legacy-0.2.6/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000003371476761050100245340ustar00rootroot00000000000000blank_issues_enabled: false contact_links: - name: I have a question / I need help url: https://github.com/mkdocstrings/python-legacy/discussions/new?category=q-a about: Ask and answer questions in the Discussions tab. mkdocstrings-python-legacy-0.2.6/.github/workflows/000077500000000000000000000000001476761050100224135ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/.github/workflows/ci.yml000066400000000000000000000046561476761050100235440ustar00rootroot00000000000000name: ci on: push: pull_request: branches: - main defaults: run: shell: bash env: LANG: en_US.utf-8 LC_ALL: en_US.utf-8 PYTHONIOENCODING: UTF-8 PYTHON_VERSIONS: "" jobs: quality: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 fetch-tags: true - name: Setup Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: Setup uv uses: astral-sh/setup-uv@v5 with: enable-cache: true cache-dependency-glob: pyproject.toml - name: Install dependencies run: make setup - name: Check if the documentation builds correctly run: make check-docs - name: Check the code quality run: make check-quality - name: Check if the code is correctly typed run: make check-types - name: Check for breaking changes in the API run: make check-api - name: Store objects inventory for tests uses: actions/upload-artifact@v4 with: name: objects.inv path: site/objects.inv tests: needs: - quality strategy: matrix: os: - ubuntu-latest - macos-latest - windows-latest python-version: - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" - "3.14" resolution: - highest - lowest-direct exclude: - os: macos-latest resolution: lowest-direct - os: windows-latest resolution: lowest-direct runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.python-version == '3.14' }} steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 fetch-tags: true - name: Setup Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - name: Setup uv uses: astral-sh/setup-uv@v5 with: enable-cache: true cache-dependency-glob: pyproject.toml cache-suffix: ${{ matrix.resolution }} - name: Install dependencies env: UV_RESOLUTION: ${{ matrix.resolution }} run: make setup - name: Download objects inventory uses: actions/download-artifact@v4 with: name: objects.inv path: site/ - name: Run the test suite run: make test mkdocstrings-python-legacy-0.2.6/.github/workflows/release.yml000066400000000000000000000012141476761050100245540ustar00rootroot00000000000000name: release on: push permissions: contents: write jobs: release: runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 fetch-tags: true - name: Setup Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: Setup uv uses: astral-sh/setup-uv@v5 - name: Prepare release notes run: uv tool run git-changelog --release-notes > release-notes.md - name: Create release uses: softprops/action-gh-release@v2 with: body_path: release-notes.md mkdocstrings-python-legacy-0.2.6/.gitignore000066400000000000000000000003311476761050100210030ustar00rootroot00000000000000# editors .idea/ .vscode/ # python *.egg-info/ *.py[cod] .venv/ .venvs/ /build/ /dist/ # tools .coverage* /.pdm-build/ /htmlcov/ /site/ uv.lock # cache .cache/ .pytest_cache/ .mypy_cache/ .ruff_cache/ __pycache__/ mkdocstrings-python-legacy-0.2.6/CHANGELOG.md000066400000000000000000000115571476761050100206400ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [0.2.6](https://github.com/mkdocstrings/python-legacy/releases/tag/0.2.6) - 2025-03-22 [Compare with 0.2.5](https://github.com/mkdocstrings/python-legacy/compare/0.2.5...0.2.6) ### Build - Add back sources to sdist/wheel ([94346e6](https://github.com/mkdocstrings/python-legacy/commit/94346e6a318983431120deb58022fa09d4dda0b8) by Timothée Mazzucotelli). ## [0.2.5](https://github.com/mkdocstrings/python-legacy/releases/tag/0.2.5) - 2025-03-09 [Compare with 0.2.4](https://github.com/mkdocstrings/python-legacy/compare/0.2.4...0.2.5) ### Build - Depend on mkdocstrings 0.28.3 ([a2317df](https://github.com/mkdocstrings/python-legacy/commit/a2317df0e27d9ae600bb04843871e3b2f7763c94) by Timothée Mazzucotelli). - Drop support for Python 3.8 ([2907379](https://github.com/mkdocstrings/python-legacy/commit/290737942189e5f285f170f2d1bb227f82a6017c) by Timothée Mazzucotelli). ## [0.2.4](https://github.com/mkdocstrings/python-legacy/releases/tag/0.2.4) - 2024-09-07 [Compare with 0.2.3](https://github.com/mkdocstrings/python-legacy/compare/0.2.3...0.2.4) ### Bug Fixes - Pass down docstring style and options to pytkdocs ([68934d5](https://github.com/mkdocstrings/python-legacy/commit/68934d5b9050359b2742edd07eb36afe4c51b6e2) by Timothée Mazzucotelli). [Issue-4](https://github.com/mkdocstrings/python-legacy/issues/4) ### Code Refactoring - Update cross-reference markup to new autorefs format ([89592bd](https://github.com/mkdocstrings/python-legacy/commit/89592bdba0597c1f637978caa19053afbfb124ad) by Matthias Schoettle). [Issue-6](https://github.com/mkdocstrings/python-legacy/issues/6), [PR-7](https://github.com/mkdocstrings/python-legacy/pull/7), Co-authored-by: Timothée Mazzucotelli ## [0.2.3](https://github.com/mkdocstrings/python-legacy/releases/tag/0.2.3) - 2022-05-28 [Compare with 0.2.2](https://github.com/mkdocstrings/python-legacy/compare/0.2.2...0.2.3) ### Packaging / Dependencies - Depend on mkdocstrings 0.19 ([71123dc](https://github.com/mkdocstrings/python-legacy/commit/71123dc4dda6ece390d94d0400920541ded76ede) by Timothée Mazzucotelli). ### Code Refactoring - Unify default configurations ([47c53fc](https://github.com/mkdocstrings/python-legacy/commit/47c53fcc1c6519025f0aec65b85bdc99e4eac2f5) by Timothée Mazzucotelli). - Stop using deprecated base classes ([5a28b12](https://github.com/mkdocstrings/python-legacy/commit/5a28b125a2ac87ddf1b804160deb11076a0ae409) by Timothée Mazzucotelli). - Use new `mkdocstrings_handlers` namespace ([d688c87](https://github.com/mkdocstrings/python-legacy/commit/d688c87dd3eca4d8cc25761d957e6855832da4b4) by Timothée Mazzucotelli). ## [0.2.2](https://github.com/mkdocstrings/python-legacy/releases/tag/0.2.2) - 2022-02-19 [Compare with 0.2.1](https://github.com/mkdocstrings/python-legacy/compare/0.2.1...0.2.2) ### Bug Fixes - Handle empty error in JSON output ([0e7ab59](https://github.com/mkdocstrings/python-legacy/commit/0e7ab594ae550b4c95a3a8b47ff190dbe88ff000) by rachmadani haryono). [PR #1](https://github.com/mkdocstrings/python-legacy/pull/1) ## [0.2.1](https://github.com/mkdocstrings/python-legacy/releases/tag/0.2.1) - 2022-02-05 [Compare with 0.2.0](https://github.com/mkdocstrings/python-legacy/compare/0.2.0...0.2.1) ### Dependencies - Require at least mkdocstrings 0.18 ([09d8e9c](https://github.com/mkdocstrings/python-legacy/commit/09d8e9c4a3d8aaf4ee1d95a702d4ad3c5b46638e) by Timothée Mazzucotelli). ## [0.2.0](https://github.com/mkdocstrings/python-legacy/releases/tag/0.2.0) - 2022-02-03 [Compare with 0.1.0](https://github.com/mkdocstrings/python-legacy/compare/0.1.0...0.2.0) ### Features - Add `show_signature` rendering option ([e741b37](https://github.com/mkdocstrings/python-legacy/commit/e741b3709e35e89372021a44f46c9b1939c8147d) by Will Da Silva). ### Dependencies - Depend on mkdocstrings ([a154c05](https://github.com/mkdocstrings/python-legacy/commit/a154c051aa6230870d2857ca911dcf797e0ec8b6) by Timothée Mazzucotelli). ### Code Refactoring - Add user warning about mkdocstrings extra ([71ea2d8](https://github.com/mkdocstrings/python-legacy/commit/71ea2d80f071e091f7a2f7b695ffcdd9dbe0351f) by Timothée Mazzucotelli). ## [0.1.0](https://github.com/mkdocstrings/python-legacy/releases/tag/0.1.0) - 2021-12-18 [Compare with first commit](https://github.com/mkdocstrings/python-legacy/compare/720f91ec264b37345d6a1fe7e77a3164c0bf642f...0.1.0) ### Features - Copy code from mkdocstrings ([720f91e](https://github.com/mkdocstrings/python-legacy/commit/720f91ec264b37345d6a1fe7e77a3164c0bf642f) by Timothée Mazzucotelli). mkdocstrings-python-legacy-0.2.6/CODE_OF_CONDUCT.md000066400000000000000000000125361476761050100216240ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at dev@pawamoy.fr. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations mkdocstrings-python-legacy-0.2.6/CONTRIBUTING.md000066400000000000000000000100751476761050100212520ustar00rootroot00000000000000# Contributing Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. ## Environment setup Nothing easier! Fork and clone the repository, then: ```bash cd python-legacy make setup ``` > NOTE: If it fails for some reason, you'll need to install [uv](https://github.com/astral-sh/uv) manually. > > You can install it with: > > ```bash > curl -LsSf https://astral.sh/uv/install.sh | sh > ``` > > Now you can try running `make setup` again, or simply `uv sync`. You now have the dependencies installed. Run `make help` to see all the available actions! ## Tasks The entry-point to run commands and tasks is the `make` Python script, located in the `scripts` directory. Try running `make` to show the available commands and tasks. The *commands* do not need the Python dependencies to be installed, while the *tasks* do. The cross-platform tasks are written in Python, thanks to [duty](https://github.com/pawamoy/duty). If you work in VSCode, we provide [an action to configure VSCode](https://pawamoy.github.io/copier-uv/work/#vscode-setup) for the project. ## Development As usual: 1. create a new branch: `git switch -c feature-or-bugfix-name` 1. edit the code and/or the documentation **Before committing:** 1. run `make format` to auto-format the code 1. run `make check` to check everything (fix any warning) 1. run `make test` to run the tests (fix any issue) 1. if you updated the documentation or the project dependencies: 1. run `make docs` 1. go to http://localhost:8000 and check that everything looks good 1. follow our [commit message convention](#commit-message-convention) If you are unsure about how to fix or ignore a warning, just let the continuous integration fail, and we will help you during review. Don't bother updating the changelog, we will take care of this. ## Commit message convention Commit messages must follow our convention based on the [Angular style](https://gist.github.com/stephenparish/9941e89d80e2bc58a153#format-of-the-commit-message) or the [Karma convention](https://karma-runner.github.io/4.0/dev/git-commit-msg.html): ``` [(scope)]: Subject [Body] ``` **Subject and body must be valid Markdown.** Subject must have proper casing (uppercase for first letter if it makes sense), but no dot at the end, and no punctuation in general. Scope and body are optional. Type can be: - `build`: About packaging, building wheels, etc. - `chore`: About packaging or repo/files management. - `ci`: About Continuous Integration. - `deps`: Dependencies update. - `docs`: About documentation. - `feat`: New feature. - `fix`: Bug fix. - `perf`: About performance. - `refactor`: Changes that are not features or bug fixes. - `style`: A change in code style/format. - `tests`: About tests. If you write a body, please add trailers at the end (for example issues and PR references, or co-authors), without relying on GitHub's flavored Markdown: ``` Body. Issue #10: https://github.com/namespace/project/issues/10 Related to PR namespace/other-project#15: https://github.com/namespace/other-project/pull/15 ``` These "trailers" must appear at the end of the body, without any blank lines between them. The trailer title can contain any character except colons `:`. We expect a full URI for each trailer, not just GitHub autolinks (for example, full GitHub URLs for commits and issues, not the hash or the #issue-number). We do not enforce a line length on commit messages summary and body, but please avoid very long summaries, and very long lines in the body, unless they are part of code blocks that must not be wrapped. ## Pull requests guidelines Link to any related issue in the Pull Request message. During the review, we recommend using fixups: ```bash # SHA is the SHA of the commit you want to fix git commit --fixup=SHA ``` Once all the changes are approved, you can squash your commits: ```bash git rebase -i --autosquash main ``` And force-push: ```bash git push -f ``` If this seems all too complicated, you can push or force-push each new commit, and we will squash them ourselves if needed, before merging. mkdocstrings-python-legacy-0.2.6/LICENSE000066400000000000000000000013621476761050100200250ustar00rootroot00000000000000ISC License Copyright (c) 2021, Timothée Mazzucotelli Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. mkdocstrings-python-legacy-0.2.6/Makefile000066400000000000000000000007601476761050100204610ustar00rootroot00000000000000# If you have `direnv` loaded in your shell, and allow it in the repository, # the `make` command will point at the `scripts/make` shell script. # This Makefile is just here to allow auto-completion in the terminal. actions = \ allrun \ changelog \ check \ check-api \ check-docs \ check-quality \ check-types \ clean \ coverage \ docs \ docs-deploy \ format \ help \ multirun \ release \ run \ setup \ test \ vscode .PHONY: $(actions) $(actions): @python scripts/make "$@" mkdocstrings-python-legacy-0.2.6/README.md000066400000000000000000000067741476761050100203130ustar00rootroot00000000000000

mkdocstrings-python-legacy

The legacy Python handler for mkdocstrings.

ci documentation pypi version gitpod gitter

---

WARNING: We suggest using the new handler instead: [mkdocstrings-python](https://mkdocstrings.github.io/python/). ## Installation You can install this handler as a *mkdocstrings* extra: ```toml title="pyproject.toml" # PEP 621 dependencies declaration # adapt to your dependencies manager [project] dependencies = [ "mkdocstrings[python-legacy]>=0.18", ] ``` You can also explicitely depend on the handler: ```toml title="pyproject.toml" # PEP 621 dependencies declaration # adapt to your dependencies manager [project] dependencies = [ "mkdocstrings-python-legacy", ] ``` ## Preview ![mkdocstrings_python_gif](https://user-images.githubusercontent.com/3999221/77157838-7184db80-6aa2-11ea-9f9a-fe77405202de.gif) ## Features - **Data collection from source code**: collection of the object-tree and the docstrings is done thanks to [pytkdocs](https://github.com/mkdocstrings/pytkdocs). - **Support for type annotations:** pytkdocs collects your type annotations and *mkdocstrings* uses them to display parameters types or return types. - **Recursive documentation of Python objects:** just use the module dotted-path as identifier, and you get the full module docs. You don't need to inject documentation for each class, function, etc. - **Support for documented attributes:** attributes (variables) followed by a docstring (triple-quoted string) will be recognized by Griffe in modules, classes and even in `__init__` methods. - **Multiple docstring-styles support:** common support for Google-style, Numpydoc-style, and Sphinx-style docstrings. - **Admonition support in Google docstrings:** blocks like `Note:` or `Warning:` will be transformed to their [admonition](https://squidfunk.github.io/mkdocs-material/reference/admonitions/) equivalent. *We do not support nested admonitions in docstrings!* - **Every object has a TOC entry:** we render a heading for each object, meaning *MkDocs* picks them into the Table of Contents, which is nicely display by the Material theme. Thanks to *mkdocstrings* cross-reference ability, you can reference other objects within your docstrings, with the classic Markdown syntax: `[this object][package.module.object]` or directly with `[package.module.object][]` - **Source code display:** *mkdocstrings* can add a collapsible div containing the highlighted source code of the Python object. mkdocstrings-python-legacy-0.2.6/config/000077500000000000000000000000001476761050100202635ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/config/coverage.ini000066400000000000000000000005631476761050100225630ustar00rootroot00000000000000[coverage:run] branch = true parallel = true source = src/ tests/ [coverage:paths] equivalent = src/ .venv/lib/*/site-packages/ .venvs/*/lib/*/site-packages/ [coverage:report] precision = 2 omit = src/*/__init__.py src/*/__main__.py tests/__init__.py exclude_lines = pragma: no cover if TYPE_CHECKING [coverage:json] output = htmlcov/coverage.json mkdocstrings-python-legacy-0.2.6/config/git-changelog.toml000066400000000000000000000003401476761050100236650ustar00rootroot00000000000000bump = "auto" convention = "angular" in-place = true output = "CHANGELOG.md" parse-refs = false parse-trailers = true sections = ["build", "deps", "feat", "fix", "refactor"] template = "keepachangelog" versioning = "pep440" mkdocstrings-python-legacy-0.2.6/config/mypy.ini000066400000000000000000000002511476761050100217600ustar00rootroot00000000000000[mypy] ignore_missing_imports = true exclude = tests/fixtures/ warn_unused_ignores = true show_error_codes = true namespace_packages = true explicit_package_bases = truemkdocstrings-python-legacy-0.2.6/config/pytest.ini000066400000000000000000000001551476761050100223150ustar00rootroot00000000000000[pytest] python_files = test_*.py addopts = --cov --cov-config config/coverage.ini testpaths = tests mkdocstrings-python-legacy-0.2.6/config/ruff.toml000066400000000000000000000044311476761050100221240ustar00rootroot00000000000000target-version = "py39" line-length = 120 [lint] exclude = [ "tests/fixtures/*.py", ] select = [ "A", "ANN", "ARG", "B", "BLE", "C", "C4", "COM", "D", "DTZ", "E", "ERA", "EXE", "F", "FBT", "G", "I", "ICN", "INP", "ISC", "N", "PGH", "PIE", "PL", "PLC", "PLE", "PLR", "PLW", "PT", "PYI", "Q", "RUF", "RSE", "RET", "S", "SIM", "SLF", "T", "T10", "T20", "TCH", "TID", "TRY", "UP", "W", "YTT", ] ignore = [ "A001", # Variable is shadowing a Python builtin "ANN101", # Missing type annotation for self "ANN102", # Missing type annotation for cls "ANN204", # Missing return type annotation for special method __str__ "ANN401", # Dynamically typed expressions (typing.Any) are disallowed "ARG005", # Unused lambda argument "C901", # Too complex "D105", # Missing docstring in magic method "D417", # Missing argument description in the docstring "E501", # Line too long "ERA001", # Commented out code "G004", # Logging statement uses f-string "PLR0911", # Too many return statements "PLR0912", # Too many branches "PLR0913", # Too many arguments to function call "PLR0915", # Too many statements "SLF001", # Private member accessed "TRY003", # Avoid specifying long messages outside the exception class ] [lint.per-file-ignores] "src/**/cli.py" = [ "T201", # Print statement ] "src/*/debug.py" = [ "T201", # Print statement ] "!src/*/*.py" = [ "D100", # Missing docstring in public module ] "!src/**.py" = [ "D101", # Missing docstring in public class "D103", # Missing docstring in public function ] "scripts/*.py" = [ "INP001", # File is part of an implicit namespace package "T201", # Print statement ] "tests/**.py" = [ "ARG005", # Unused lambda argument "FBT001", # Boolean positional arg in function definition "PLR2004", # Magic value used in comparison "S101", # Use of assert detected ] [lint.flake8-quotes] docstring-quotes = "double" [lint.flake8-tidy-imports] ban-relative-imports = "all" [lint.isort] known-first-party = ["mkdocstrings_handlers"] [lint.pydocstyle] convention = "google" [format] exclude = [ "tests/fixtures/*.py", ] docstring-code-format = true docstring-code-line-length = 80 mkdocstrings-python-legacy-0.2.6/config/vscode/000077500000000000000000000000001476761050100215465ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/config/vscode/launch.json000066400000000000000000000026771476761050100237270ustar00rootroot00000000000000{ "version": "0.2.0", "configurations": [ { "name": "python (current file)", "type": "python", "request": "launch", "program": "${file}", "console": "integratedTerminal", "justMyCode": false, "args": "${command:pickArgs}" }, { "name": "run", "type": "debugpy", "request": "launch", "module": "mkdocstrings_handlers", "console": "integratedTerminal", "justMyCode": false, "args": "${command:pickArgs}" }, { "name": "docs", "type": "debugpy", "request": "launch", "module": "mkdocs", "justMyCode": false, "args": [ "serve", "-v" ] }, { "name": "test", "type": "python", "request": "launch", "module": "pytest", "justMyCode": false, "args": [ "-c=config/pytest.ini", "-vvv", "--no-cov", "--dist=no", "tests", "-k=${input:tests_selection}" ] } ], "inputs": [ { "id": "tests_selection", "type": "promptString", "description": "Tests selection", "default": "" } ] }mkdocstrings-python-legacy-0.2.6/config/vscode/settings.json000066400000000000000000000017041476761050100243030ustar00rootroot00000000000000{ "files.watcherExclude": { "**/.venv*/**": true, "**/.venvs*/**": true, "**/venv*/**": true }, "mypy-type-checker.args": [ "--config-file=config/mypy.ini" ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "python.testing.pytestArgs": [ "--config-file=config/pytest.ini" ], "ruff.enable": true, "ruff.format.args": [ "--config=config/ruff.toml" ], "ruff.lint.args": [ "--config=config/ruff.toml" ], "yaml.schemas": { "https://squidfunk.github.io/mkdocs-material/schema.json": "mkdocs.yml" }, "yaml.customTags": [ "!ENV scalar", "!ENV sequence", "!relative scalar", "tag:yaml.org,2002:python/name:materialx.emoji.to_svg", "tag:yaml.org,2002:python/name:materialx.emoji.twemoji", "tag:yaml.org,2002:python/name:pymdownx.superfences.fence_code_format" ] }mkdocstrings-python-legacy-0.2.6/config/vscode/tasks.json000066400000000000000000000046051476761050100235730ustar00rootroot00000000000000{ "version": "2.0.0", "tasks": [ { "label": "changelog", "type": "process", "command": "scripts/make", "args": ["changelog"] }, { "label": "check", "type": "process", "command": "scripts/make", "args": ["check"] }, { "label": "check-quality", "type": "process", "command": "scripts/make", "args": ["check-quality"] }, { "label": "check-types", "type": "process", "command": "scripts/make", "args": ["check-types"] }, { "label": "check-docs", "type": "process", "command": "scripts/make", "args": ["check-docs"] }, { "label": "check-api", "type": "process", "command": "scripts/make", "args": ["check-api"] }, { "label": "clean", "type": "process", "command": "scripts/make", "args": ["clean"] }, { "label": "docs", "type": "process", "command": "scripts/make", "args": ["docs"] }, { "label": "docs-deploy", "type": "process", "command": "scripts/make", "args": ["docs-deploy"] }, { "label": "format", "type": "process", "command": "scripts/make", "args": ["format"] }, { "label": "release", "type": "process", "command": "scripts/make", "args": ["release", "${input:version}"] }, { "label": "setup", "type": "process", "command": "scripts/make", "args": ["setup"] }, { "label": "test", "type": "process", "command": "scripts/make", "args": ["test", "coverage"], "group": "test" }, { "label": "vscode", "type": "process", "command": "scripts/make", "args": ["vscode"] } ], "inputs": [ { "id": "version", "type": "promptString", "description": "Version" } ] }mkdocstrings-python-legacy-0.2.6/docs/000077500000000000000000000000001476761050100177465ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/docs/.overrides/000077500000000000000000000000001476761050100220265ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/docs/.overrides/main.html000066400000000000000000000005001476761050100236330ustar00rootroot00000000000000{% extends "base.html" %} {% block announce %} Follow @pawamoy on {% include ".icons/fontawesome/brands/mastodon.svg" %} Fosstodon for updates {% endblock %} mkdocstrings-python-legacy-0.2.6/docs/.overrides/partials/000077500000000000000000000000001476761050100236455ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/docs/.overrides/partials/comments.html000066400000000000000000000041211476761050100263560ustar00rootroot00000000000000 mkdocstrings-python-legacy-0.2.6/docs/.overrides/partials/path-item.html000066400000000000000000000012111476761050100264160ustar00rootroot00000000000000{# Fix breadcrumbs for when mkdocs-section-index is used. #} {# See https://github.com/squidfunk/mkdocs-material/issues/7614. #} {% macro render_content(nav_item) %} {{ nav_item.title }} {% endmacro %} {% macro render(nav_item, ref=nav_item) %} {% if nav_item.is_page %}
  • {{ render_content(ref) }}
  • {% elif nav_item.children %} {{ render(nav_item.children | first, ref) }} {% endif %} {% endmacro %} mkdocstrings-python-legacy-0.2.6/docs/changelog.md000066400000000000000000000000601476761050100222130ustar00rootroot00000000000000--- title: Changelog --- --8<-- "CHANGELOG.md" mkdocstrings-python-legacy-0.2.6/docs/code_of_conduct.md000066400000000000000000000000741476761050100234060ustar00rootroot00000000000000--- title: Code of Conduct --- --8<-- "CODE_OF_CONDUCT.md" mkdocstrings-python-legacy-0.2.6/docs/contributing.md000066400000000000000000000000661476761050100230010ustar00rootroot00000000000000--- title: Contributing --- --8<-- "CONTRIBUTING.md" mkdocstrings-python-legacy-0.2.6/docs/credits.md000066400000000000000000000001351476761050100217240ustar00rootroot00000000000000--- title: Credits hide: - toc --- ```python exec="yes" --8<-- "scripts/gen_credits.py" ``` mkdocstrings-python-legacy-0.2.6/docs/css/000077500000000000000000000000001476761050100205365ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/docs/css/material.css000066400000000000000000000001311476761050100230410ustar00rootroot00000000000000/* More space at the bottom of the page. */ .md-main__inner { margin-bottom: 1.5rem; } mkdocstrings-python-legacy-0.2.6/docs/css/mkdocstrings.css000066400000000000000000000021021476761050100237520ustar00rootroot00000000000000/* Indentation. */ div.doc-contents:not(.first) { padding-left: 25px; border-left: .05rem solid var(--md-typeset-table-color); } /* Mark external links as such. */ a.external::after, a.autorefs-external::after { /* https://primer.style/octicons/arrow-up-right-24 */ mask-image: url('data:image/svg+xml,'); -webkit-mask-image: url('data:image/svg+xml,'); content: ' '; display: inline-block; vertical-align: middle; position: relative; height: 1em; width: 1em; background-color: currentColor; } a.external:hover::after, a.autorefs-external:hover::after { background-color: var(--md-accent-fg-color); }mkdocstrings-python-legacy-0.2.6/docs/index.md000066400000000000000000000000751476761050100214010ustar00rootroot00000000000000--- title: Overview hide: - feedback --- --8<-- "README.md" mkdocstrings-python-legacy-0.2.6/docs/js/000077500000000000000000000000001476761050100203625ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/docs/js/feedback.js000066400000000000000000000007751476761050100224550ustar00rootroot00000000000000const feedback = document.forms.feedback; feedback.hidden = false; feedback.addEventListener("submit", function(ev) { ev.preventDefault(); const commentElement = document.getElementById("feedback"); commentElement.style.display = "block"; feedback.firstElementChild.disabled = true; const data = ev.submitter.getAttribute("data-md-value"); const note = feedback.querySelector(".md-feedback__note [data-md-value='" + data + "']"); if (note) { note.hidden = false; } }) mkdocstrings-python-legacy-0.2.6/docs/license.md000066400000000000000000000001151476761050100217070ustar00rootroot00000000000000--- title: License hide: - feedback --- # License ``` --8<-- "LICENSE" ``` mkdocstrings-python-legacy-0.2.6/docs/logo.png000066400000000000000000001627141476761050100214270ustar00rootroot00000000000000PNG  IHDRZrMzTXtRaw profile type exifxڭgvcvcrZo^+DN;JzzG|Ssg_^(c1}k%,~=o/|?[~.=oV߳k _|(uJa,pz a%WzN\XxGhq2.L&$2kH%Tz,q {L9)$cNuoB'3yH&%HHӑ`SCK)(k*l-JF=Ksđ2h1䦓'\0EKXf݆EʪnXsǝ6ȵn R:SO;3/vͷzwG־?k;keJlkCIQX́7e@BQStEL(9;(cd0 ?#s?ys9?9=oڞ zاztB)0qd1^W/)ɫӈ=o[imjhDv6mr\KbhZXٷgwNnE|:=w+z~9 b%t_Zo[@F-,uPn)Cx`brIq),&AN(gܬ_r6^~)$}wV&~'/Oi Xk [Vuk4"F-U K E9BWH{4&Tp䁼b6`;w:/iwg(LnΘ03u10g!GtI<2b"# Z_FJu8.%{(4Ivt?P.aiTŸ DAG9ܦ6nVxJ:"R}/p3WLݔ>x)UBJ+bTׁMCZH&< ow N.D҂p^LnDW)qq~'7kӨ ˤ T~`X7`R.Y=A 2ƚ$H8~}]7Zu6gJEiiDS|f XHiDf4#'a>(3#e)Rޕ"a_h_s -¯e3sZeR}HvA-Dʍ`zLH藬)zH !."B4/x2XD]۔LgF 6dS-Q62A;K/ o ~/WA&jI iԫʵRQS3.K z(Lb/͆r6Y#C)UYn~T]2NBn;(.8C"D.NŞfFS'<%  %*9? MWWe f2@|.ky|DP`{M4}3!֓i)ՠ 6 B(~V:РC+ `9n1U+7qæ8.v?w]Yb:)k/x.5wlA. gM _{ OP|=M{GAL=@ׇwHT"P}{'$v3꫟ԄDzmɬ?@er^Um?t,ory JFMT)jqlHVhuɘd#H%U#% =XKWXhoИQ44\)V.AM!:n#"Vxr)!T?, Y)0E[HapR|}L<<+Kp4n]#[UwY1߼8J=.BsGʢ mI/Fđd%6ϓ'h xww5.ɐrgPbDm,8K$G#项͋`3y1Uy%Ĭ lYhm/CiZ$ikBX6 z @7% ny2uljxQOLB֑CMpB-H"01Ѻ46U'p:4r"X[$Hѓ$(Ƙ-hπR ;tL`1)IX[2GLsBH?!|.}@*|D SRߏPp0ִR,1;|½ED hCB7(j_g) &aV14TV;3I::ޚ EqU36-)r_n1PxRm0cYa=d'B=PnaTp\~^{P7΅rm4aG {paϻ2G;b 9ںh{hZŰ4(pIBke|"0. c]~;6MR碟@x@5CD$c_JBH+ps0~GQ聅:D;3#~CNhcw@nxщvJ&G rZ"1"̟:AGPcӋAdyo7?ʚL#6R)V=EKZnXhj sb IRʅ %q:)_.bVZy6]vtT@x@TAQk |boS{-`p^—+cMκy(!w3"jWQ \L~cC2c=! t@ЫWo_f[+:hC;6~6Bn4 >5 H2ϑ + y_ .[`I^LLBJ n y84ryC 2mx#o#Oac4(EgI e>+'j"6JHtj*y$nK[PnAk1d4W3䑘$Cѥ8P-U<nt!F"U1(OJw) =umIM_[B::ԫG$ yo9G/O[a-Z?-I:(6#twthE-֗M'fB`FI$C_g0%(S . `@WgڏCǡi`xhjvaȊxEG 2®159-d(-QQΘ&@aX;LѩΗ1Tm=E1pnUhtdZ#ޚRی?3JrdYAgW(6b#, ! pi.ܠ|2p yB^rSDbSA܉|B3E$ q@+q7TǡKe[~@Q') xk(b. jrh&p8H$p)%>2o3 xh$.G!fxǑ-EK4jY :oK,2Cp޽] Z⻮N!p⨉-C[9ν#8C8SK3AbG$~u@b [ĆIEoD6 o9a+;YG8 Ov X ~ ? za`mgjI?;F/,Bچ~9.q|b#]{&#-0&M&N Qk0 bZoڬ"ĈpY$yԣM-$zzVabCUt.TRc2_.t@G%UXcBSjv97 /hJ?rOP `"hUJR|POe:O>F* |(_YN-n_ps-I<RUڇzΎ-7 3&K:m_уEr?.I_0/K ,Pbfze @'FQG>z/ & 54C$;x?4{uba@r +T}xD:LB^/#lt$xOv4^售>t^י6r zձ܆ @zZ|i7@Aw2 rIQ].d:t r* &mlOH2^[o$_i/ސH r݈ى6{Al@NwDn}(n Bl(lwHaUqP^;&D0\3$gfԮp#yK];Ot4>Y!jPAԴ2/,.P>4!~T;:mӺaRLvdVpsԉUߏ'Ϗ:0J Q*0nEEm?(+.OBKVztLw,c"-\3:XHy1K҆翨}W&|, xxz9GYIRω #e8̨IGbJJ 9bKGD pHYs  tIME  4S IDATxy|Օ=zSK[1"86l^`2$eB20I3I>y~' U,$!c H0cm1xhݒ%[K/խt>[UV=s  A(hnƆzM =Kn1ͺLL9Y13.Hpʔi%N/4^cIMCSrPe(.Wb411i?2ɣc h%BbF Nͪmvht@qZ6$GL-6v:@Fvy>~@2!"ՌH*AAaX o'ԳXZ;v5?',O0TlzǬ&Ü@c {"Dh0& W0k9Yzӏ^rJ{t b|@D R鷨k@ 8h 8!vH:6ɖ|V_?jQI^GlD3"H   zT-oЬoёmӅsMS1|Wt~˘1M5)8ciX$; рvaSgbySӥ2D|7l;wCԿSÒ AA)H)2``za'pq'pTA oE>2 dƑ]?38m++uDAAfΪD/'Lڛ|ݎ^TkW h935|*. HsZZo4^AϾEめkmǹ.T5D Z'qX )<+9KK>gR#݃&@C)e ev@;-amϯOюHd]$]A{olT#EٟьS&] s2)qh05k(*!gn!=8?%)2DJJ)ӂ:?ƬL:_f[WBfS8,0"  ҩӳ3>䘠a_b*,& 0v@K(t (g!bbS7h5D,GOmj=Gzdf]$]AR@m-# r]6Aà=L& +n<#%w}12,*F2-K6f0R =~hv'h'rx[b F-q"  ZFvoZdXКV̌Eh!.AW]Ao ):ߛ?O|{GjhDzDAA("+BW:D~d Xwx|e vlG;6+";<ARxb|K dZiOi"ôرöA7~`-[bˬH  E6 ,Zp{|[Mw)tv<d1Bt3ѩD2-S>8xnz<^wXJ)#ouSn3RAD.5k;7hiql FOs!!tAA uf:X۽ wuvt<=VNK.0=ZݪNM;hcѵ'w 5zW'% H  nSh LϪ'Lmc}`K;IPȂ 4~PAϢ- `0A٤ KY^8bOV?H  ΅]w|ezevY&Q,-JnYҕD]t5k(h|"[hj{*T  ?BHy%mkLraz(C!dXHa'6=[^ϋܲHFC>Y̤ °i:M^n2<;ayi/l I z6ŽdZ&D"|7o7O4tAAF5f*z3@C/JvfѝS%Dx.,;7u# DH  HӒ[֘o5Y_}$0vw DZ#,33S6r_.YYwhh8.mw,Ӛ㫨z3ߥr<)EbAXi<~!_gJQ_8K!}@AGqʱUl[bswd}*d&]AJF^e kDe-gYX7Sp9Cp;-<1Pڦ9r/ιڦ0+BdF}tAA(e>R&N/X~mORΈv;"Dݤ\nˁ^/~rAN"i˦PqŞ &Q/22.  aճ~~̉vlNѶc3;I~m[3L,p6c46pbѸlLįlj\ǡPHL  ,ЬϹi>M${QWv 2L_@)_aX~ a*bVZ; &:H>7"\"J)emvѦl -bcĉ ,\@AJf&"`}[19mIޗo "`Ўxp4 :;ҿ}+ VRTRc6,J;V֙MBEd6d\;:qngqqKr_vlXkVՒr(X&`jq6jN{O 8H~ؗ~N~TtxZ{-Nwn *L\2]܆qlxI\n [̦5_=TJ"uEKłW Q &Hk=+B;FYT$&9!~Gh=LO?# Xq4-H{HQ : Ƅ}wb1s]v04fw:}XL<]v?N BY!c`Aj`$,Lbcюg`+JMsk"55+YI42sq$auּy3j"k,ak[oHTP`1G*E@f0 `[o" dƔtKZ ?N}6Y^Nf HB <"(:NlNdg;Uc&êaCSFP1|\8!3;PY9lGEs A9+ϱ\x>T[iy= %"><5Շe Y|T6?m๫G} ;r#;ͧQQpD|f{v[FjN9pjLq55dw= Awcl ffy(vl2$. "yI|X` J+=xk^36 3?p>RwpIZ҅1~NtmWjaP[)Cg"'xfE8>-NϡXwN)tGY}\fz6Ȩnf*ӳ`Hd}K42-l"5֤ =o2_* :WFhonqU[>| @*-=C@2[%mh/ߡj|P1`f"]]6/D ;.HKf40;8Tx6sPBB*~RyWЧl{,2/x^- d}:=嚥؝f(E}i6keU LBpIHiotCN͜aJIH0I !"5s}]Z'xUO"uc ӗNQI礊F3Cu"`994(X5e++m¡m,k5U;IM`{8t!⊠Y9  8_E u)Db57!s"Bp'6Rʬt]TA Юgi0$P>I@{ +5DY<>AX3ւR5dUXJ$$HHjR7 BX~ H=0}wR7 B1KI?Td4ͅNOF>as'%xَ33ѕ,ݶa.n[OW]n8FjO%@ǐFi !-ϋRmB p9Rb6DĠwp0dMwԪ@ĵ`@)ǸzIۦ^ޑ$+irZ"qnԣH!5k("uS 3| Е5|T, J7'U2!t"֧?LD)ef9Dm}B"MӸ5IJdh{f[MY.} QL.r J_ 0Y\&RY6> axPԽ!#v4 c ٰQ敗W?e-峸հmDl˿hsD&RFy/sNz[J 6Ϡ(_Lر"͕8JK9$s8 WEAJWT04 ; m&|J F8LLn(F0i_XO2Id.p;wpo,}R}t9q!͙ZhJU5ox@L!b|9RJh|/ғkڊ!¡in&62uuihp"ďk|&|]!Req)4\e:fE* KRn-QII"e l?=ƎǞT[+'F6|D=h*edlR"eR9FBz.-iGO4JFAaɃ6.U1="zEd9vұ1V )Tv8e; "L:pAwC` <˼,)_B* 0< R]R}D1܈ 2-5ر>O D e9S -9mFJE] `R} Þ:K5H.vY$ @bo!|z6ȲnMw hG>&יCR-DEE*++v=***PVV`0!@hсVɓ8yd'NmX©ѼR~QhooGgggV8\8rK50bWV ΄JzGsL4 Ǐرc1n8;'Nĉ1fTTTt?Ʋ??5ں%?~ǎñcpߏ9Cرc`pRYYI&uٞwرw{+}a5)'NÇq^ѣ8x N<9X IDATqbW%n|+rCCP%t$^’>oWLEEjjjPSS3gb̙={6f͚ɓ's9k-x{͛chnn믿Vǃ磶gg;gFU  Q]]{;::ocΝx7o^Css3><һ"OP a xo7g]O.m$=x`]cFgJ0T41m4̜9sܹs1{lL6 ӦMÄ  v2Rz3g̙s&Ɖ'w^ݻwƮ]k׮n1:~\&2>gƜ9s0k,L:SNŴiP]]]𰓑HYYPWWwkmmmxww^ر:~mڵ ;wDggH9 JMmFFTq.>KW-`.to[fHzX|9.,_K,x<,_˗/ǃ>;ws=oQ_]]ݫ4MiQ¢Eh"{x׿>,֭[K 3!]۸ַ]&*BEȹHz."}ݸ80k,̚5 7p`֭OGydnDUJ >K.isŹ瞋[oZk `|Ǟ={#`ʕCv, .<#G'EЅ~r+Vڵkqa<#rF#R'؀BEyz0g=p0\٧[~ZyY^̫jIkcqgKk  q7bݺux?ؼy3\p*xx'cy7zGo9Zq3z(,渿|gA~ >%~;̛7OZHțy׿5>epe\veRBTWW?!֬Y#1 !6|gɢY, )ΰH(>%=.'ү$] p+!:bg[(:>HZ AA(I "=:Iv=SAtY /eϞ9 RgDisAgЖф(H  ֎;.um[te >{2?.9H$DdulX qs". Bd;^[Q |)>c0x}3%'Hlo.tWC{J]$]A۶1aS{E_9ՙk;8s߿kvG2kbpִ^f =`~,vtAA_^sZUEh)'ɉ(km;$[Qcg,,'h(Rlܽȳnh %@ w zV\J̠ft.tAAM0#v@MX +H+^p96swb .  G"p0V1~4IMLf<@(AAF:IRoAwsW 1C̤ 0(`XrK,JHkbG<G<ض 5ְ9s$ ÀR J)^/^/|>_eYR ( rA.fAA%p^;]wH{޽{҂#Gȑ#8zh]6D5i" bر?~|ɓ1i$L2ӧOQVV& * |A/\y :AϪ2+&ܞj%AFd"SCIϒd2#G`ؿ?ZZZ҂wqQ;v 'O,m'N'sA7nƏSLQAi>3]ɡ 8-رcz-8qBÇ~n̘1={6fϞsb޼yży$F‰83zu+իpXaFCצᴜ #_DyRoKi /猌7 5Y 7"J,Λ2r7Z)'qx|WM WI/"o6?駟3e S *nj |LڧAZnWqe|tq;lB^cc`MĄ2Af "&\KЋv,`#pl/IQ{Щ 3nʾ ?`Rjjjrc$L~դo>3W!i 0DxI?r^~eKذa6mڄmoD  l2\uU81~x(A@`"h,?,lâSNs'✨^f8u*Y3/G1pd}DJԩS5BZw{ ]Uv!>ZsɗX Vzt2 'E2ɤDng?`%ȌwVzϠ;06_un7r9:e p8j 5ַОڥi?l$ cu+. ]70"̸G}މYeOz9,";#I)JGRFẠ#n :XЋ&E!u zOXllBшC#hc|0? g030 VR$]A,ekK3G?l*"Hh;n" #U>,]~ץMAwS8s;,-spC Xe2rulJ]e\B@tZ^9CC " $R+R5&q//xiYʀH:1)R$3}#͆n2*ŞwQͿyvQ>΁]jׂzd@3dVGU 4yw^嗗XX.DH P̦ի&"_ULjô Nq;aZD`GCŐsI`E9sh-sAϣ_U{jcX L$mTxTMn^Z⯙C&Ϋ  *=EoUk09hBkm*2" uHĐQT)]t1tovDI 6dg9/,Q:Jjt IAJ Nl"Ϲ+_NvC"O*?2-Y OM`wSl+.}z{اULun~UD NcL7  B_d&]A(B!Z7~zLV ʎGLj"2I 'h>,y 'Cu3=_&Ef1Τܮ>ڒslǬfya&2~ܐbJ6HA]!DCQƕ}|WRNe|IAFaM+W>=?1=D]3Jaq#2HHE_!,^It1J٭̟Vdֺ!#F 9mu wA[АVӱcϙ?ىwj۱5)c 4^]A!~o!ƓO8uUU//l# *G]$|>TTTt?*++QQQr~B)0R j:q8dZk858(b8bXhmmEkk+N<Ӷmi,ACc'W <(+{#1I45 i *<$"p%diEQZ{+28]ܖZa ;`ŋ} j8"%@YYjjj0c L6 'O1}tuY6mZ\2Zk>|w޽{o>ڵ ݻoĉ M}}ihpjn _2њd$\$Z0PZB\AhsR-ٖż2!fnvE՘4i1ydTWW0aJ)L4 &M\6mmmؿ?8{[كfq8 O(jkoo/ ]n xf𔺠 :\lLPoS\.z. 1G#`N`tSwܹsq/ҥKQWW#üy|qxaū*'B"s9e?'mqRv1"j -ŞuͲ.ؒ-n͠gfѭL4ʃQ>ùe]Nۦ RrvG?3gb-)zQ[[wXl,X磬Ly`jjjPSSn с^{ ۶m֭[el޼ Fh5Ez⻾>mfFJzAA=e /ner΅_s<=Ӆif#sx /?u+LDcܸq}˗/ f(++ҥKtSݙ:z)7?T `Sq }eeٱM`dfnfq-}!.f`͔g]!A!io}%̒hy nLQJaΜ9 qE/Fmm\%K"q{qa hjj477q(A!s/F_ub]ܡNXeݎAw\ qqc@ t.!ąNU\+KւMQ}Y@9EU?ͺn<RDf#G_|hhhu]ӧ˕q0a\{kx'OT g7o_[ }[FNtjf i ΍,B|*#%"㳔qŹ+Mb6նᳮK'?`O>ÔAjDJ1f &Mn v}a.Ǧ- $m"ew%+fmIr&Lr3=zpy&b*29uA3z=cBz? ڲ d?]a">2va ni ;s>_Na}.!AGJǠ]g.s,&YCЎPlY0uk"fP "DR0 (;L&]+YgJY_ױuVW2M?QUU% _LBYf11&"#_bs:C$%>y3u-]Ϊ-iBĠg\:K&%@ڎ'hW91zK0.D$]F9w֮]Zy]wk]SVVEtVu?1`}7͎v$rD4Awc^t.htu@y~Fr=hYǸ%.\3`!Jsu!}VdD ~Vބ Cz- 7Zy?ϱi&i"Q_H9c14=8K߄8!q 7gteqE32MF0X,gC38Wm.eu$}[r4u7rǞM[a~kVg9X 3>g@0]@c}!.BX|aWzA04+ B,r<í2A7l(TK&U72 gķ0sI)!mmmhmmE,C,CGGGX xxdJ)"eYPJ4SMzz|`aHoߎ_W^{mAp5+WWƯ7O:D!V+_lRFH7=c1p❵]Y"/#K!b)"S[r{ehO@?@IE]q}1c&d=wy6UM컉\ t以DKK y;ؿ?ߏ#Gz#^18~8vP)TUUa2eJO4[Lٳg|H$_ҵ>K/-ʱ_tE];_׾#EB&όfz+'رW.C4\B IDATsVv |((^l u s;_jbAP@mg(NT)dx饗 /`hnnFGGǨ5=GbnWVVZ,Z .矏ŋ,| vZ޽ە*++)塇o~ R+QP$n!3Ơg[f[6X̻-]bL٠*XL]Lر>,.2  pYcI&zO=ձ1n{5˺p-͢[a9|*͠QMyD4d#f\諅A%o(=BL\/YV5č91*$/"6n܈xpA [n֭[}˖-_e˖~7***\ѵ>L:uHΣwk鍍"n\G, J. 'R\ ([s6^ΠJF;LPZL@rl;+͹P PH2gL0.jh?#2O/~ \qW\O=0!Hg׾5\y3f .)<2ˊ@vTF"zWʺ<$ot646Mά[`q1C`qEW.nƠsa75z*P2D~ fEaeD:6]}uRWu#ĥ5}$'"C4%]3y]+TtM~W:Io51)ӚvAw}{IXt-̃ :|n z?J:H:dv.gs.mAd fk೭1KzDa~z,֯_/%B!fgEhHYbu|\Ac썯Śs<^ǐMFלq1q5=z`oA?#gόtz+i<&݅\=sv(lZd{<9+@Da?ɕr,—:/ˮ\Iȃ|;5MiCe.p"&6!RuV&pA-ˮ~$k"&.TV0Ȃ)bENy 2:ݍц-5 r2dtoV&a`u/ևM46~BXӞMy[.#$Svf=hm,S;|7H Ѩ+-5\SxWxGd+٦770:#Ai9~v9 g݊A'sV0 D* W/AD̜8y-v^̞=*8gmַ̠jJv*)n2\,㐺P~ո{^xe bLD[tm8;mC `as `Hu3B|>g``}}}Coo8z(?>Ӂp'%KLqxG#??0hu-,y7̿w* MеE$e-W2e˨ׄx ".50K؂ Aq p5?Lя7ÇkG_p!M6ڿtR-v6md8SJ8\gr(s*,X:cЫ4hF2Y"#P$:*z.%D#Vzhl^KiR(XX x)R-&㨁A _kHY|뭷NK/d8p=9o,p:"ۖjM`Z|xy{]"3ΨBjq .)i_av jvbg…gE]T5~U4ԉܢ\WѩU>adU֌1#󞥷b`82`;ڞ_Z6ȶEDRx A/;$$L@ I70AٳGTMy9r̀dEVf<" qQdti!>!.~3hÕXy,[*7ղ#2ӶRaMHH@)8_|X~PcL$LP&\oo?5(gub$T3\B},3}s|7|7)K(mx5n`Pc8x #۹'n+ ]]]fPkސPL)â::`a[tMDq{ed[8vP6Wjf"?Z2Dz1D3F0.eDXDoo/ׇ^\|~  ,I')O'ضV?5Ιo:P+G3gά*X4('Fc =~/A-XF@=FT"GH\yVGD]ߘpW o/i8!CmJDN׿7`9IBY -YL{lk?f18g}8bh3}v5TFU"k؍ kQC-q?9b1j_`LPV"ee1{:{{oL2d[%Q[Xd"pBMU̢oB\83g] j'O4$ hs 3F"L-(s$`hY,=Ybx,$BH[;\sz0SkՑY N U7LUKIY zF#3$+,VHzss3l>~%4@aTX zyR2D2fI du.#U`E $ ˆ[zܾ\tj[l6:K)iQFJg7mw)AXz2Z\46$&+mSSSIСC%|FbpUfN'\:e_F[ :@Ă@B-BapUE/"(LFDTַ-h?^ؠ@osyn``H9HR5㓦$B3f@-XC̢DKw])~Xf1U\j&AD HQz=  <?tT~|Jg"khg!;a3LP, E1Asg2d!ZҨV@_k $=u>,V"xYb2E>kqg,'#sSW}M)A^9ӿfw?t@?S(Ooo/q~;f.&|<ى˯55%h8ܥ!.oT[J`o71?_{mowg+WJhQ0,pa-AٶvF[КÞzXF;Z8G͜WO|B>CŠ!b1 -.5~SC#NW_ oŋ1oDGzk!)'N޽{sNر;wΝ;m6?~ ! ZJZS NL|n HAeOHbÊ"i'Ҧ|n 9v~C=EK1kQ)McTƌr!S9_)d[?"l&qhL`@B5.x+(8-sڊ[o˖-^ҋSL)ShѢs>sxzj^iD'f'}4a2Dy/ S_(z_+cvdȐʚQ{siZݾZdJ0^:(ζ;e$= xƜ(H;Agkx:oIz*u]n7x#nf\f2mcٲeXl>C)]vaÆ شi:;;vZ9r8+"t֒.ࠖ1lp6 9dY/YɔTbcO۫CE4Bu h%%d3"ܩ2DUiuu1qdS :D !ɠB麌Ll"T o{w wq^!`,X|;KRظq#x <2 ]o~jp___cBDuO~vЫ9]YI`*Ds̶ $בdbg+ûv.C,<#`G?R߮YGgq{JED ~g«`a-!~lJ [)1 $o<~|lAq@Iq-`ٲex_%K*&7pn|ӟ+gyկ/bI>: IcWZf 0ᠨR+-Ao ~dvWY]Gydž+d[+u&l۶,dZ1‡d%/ q òoFXd'($ypI%)0 Pa=Ag/Z$|;G6i;wʕ+c&_!"Ղk-9 $AQNAQV+)?m| dy DZ m>zӇNAx[9L&sU> ˧=+QP$v,۲mD@&fhqbdo.\q1_blNz v11!lY#PNΈmIN Zc+`ԩZ;v&188'OV_k %X`,B(U玙fEXyL$-b7~ϾٚN[Xeܞޯ36e= uy[h_/KyaO"pӌDr!gjĂNm=LH؇Mp6pO,),!9aK- (X<)ݚXbͥSvUTrGz-qA[ b`PC2e;:vڡ˯5"7Xp0g)(X<ѧ[lZf0V g1U"{nn+so}= u.Ih:P[`@1$;47tzr6.lo0#3@1Caqn @9c@<5E7 4H8X2%xC6%M+ -Jvo͝R =%Fȁ\V0!CPPu Pv2YZАпA H!u:ԧ!)Q. }eHA Ap]0$}4N}w*'N<dutlKxr~Ԛ^eu,Ű^b>p|b;W3ef`4B%<5D\b *QƸ4IQGqᡈ-_ tcPjuR0aF,vi:7G]\yLAfσ~YLFP0$0mڴR爙َ=jH !58d%*bD9GLtVQXr& rYfOY,O߱[ (è|Zhp@$o } # A [?c⥫.(gc*Я$f͚Ç3g4kWZ6ՕkHD>RQApDΝGC=?V txD/ΜF폒KECBA'Bi+wq9j#Az[i/AǚH9PZ>͛:t&|2zpHf#_㢀jLミ_ (-%QÛ&]sZQ= Tb)2:9@ X°BƠk܋Py@YgJ!Tc`P[?~d _j!5{>Ġ%Xy`>֖PCCLyV}KGu\Ő$!|򁸉ddn3Tgķ+§RT:tԏ4I701\qm*WQ8+DB8)qegHc%d}SZAvmn`PcꫵٺuŁ"ۘ7oɤX#@i=8􄓴_Ua>$!Y;i}O=њ^eut|9, -tNu/578Y,1,몈' 1 B|_9긝l$őJ&+b;$>}3$62$Q/Oz_7lk1j4/#)MZa'd :dR eq@\a È\fDsߣ{ya[:ѕ L "wco¶q% ȞSVZ.0d2@dzXVjU7f'؈YfE9kqךA5 > "_̆UfQ-+s}_u^ 8Ey`%& sT_ \c+p mL&0yipu5$q饗FqFI_|Qo Qzw]V^ Ř7w<= +?"w~~zr,Iz";zв󸞠!m۶M5kD3w\ضm( |LH V'h LD4Ē"=è y{zo!#sZ%QF+"5eW)Vӧ$H/y=Vu20C.4o& I70A,ZHɪe c-b.Ib8 :$A}O1D$)WN/xշ˺uD.[!%H!QkhkŭZɷ`2Qr$A I70A,YD_򗓲/BU&qtKxČ\ 1:i{>:By?Wx; QL%K._Qš˰DyCʑ(bX8HRUBc*fgHA .C]]]d;6mRjҵƍZҜIt>Z@ZgmCRF4-h<^> 4誯"=NMڎTbr.د_yc g=:a.|>f)QWx"-f*2A_Ivn`PH$Xtid;Ǐ͛']ׯ_oHz`"q-ABXE߾8 WOxi+eSNY(d|oKM+.>y]`8h>B]hGO*cu9"c!ao%bI un`P;Ilذ!\yf qA&^ę_ ]ӳ01{v^Qp]ǘC]$eܚ7_f77LDft <$ZQ'r #ĥ A zdYƠ5HVjHA-oyyIҟ}Yv7"L4rWBE*Fa x?nb L_ Q+ca"'ZR:{)=rQ3 C})|eܚݑbYi e_h @y͐tō7ވv֯_CMv?Uȩe }\HϘ!4LQS2Q'ÿ/^*׷. ,L/AD!⟵t1Azv>nZDz3ᛠ3 \hH4$Fa6/^}ÀYZJp?[wuD`jr_:NVD\d( iw_&uaa,mҭoC$U/ INWGÎxe&;zą]9 l*.A;+Hy"׋ㆤ0nf-vtd,֮]v.B\s5fM ~X& :d#'&P&OPLRẈXt^Ze}i"zz֫L]>DЪmvq,Af1Aג%W\R^AQC j7t;}o5 f9 xqЯ2)=Ph@)H uT~ |hg6էނ:R<$V ^,>{_=5}btY6 z8< V [zo!5 ];ǎ~VU~i2 y%ΜqbA UBNŠ.k qv@!AD|p͉HJ[>D_:0KBŠ+*#IZ za7^_A{b0| "G읔2$`̝;7^xAKIжl23x&@["@7#h$ R LbD9B6Ji2MfO72(2H"ZH&Sڈ,[HY/8B\ˇ<"ce;y(pd>g`HA~wdb77P,#۩7;> HէN N\, o $Pmt3qJHzl\OAajAT\4>LIA zs% HK cuD(fFJK wn``Ho| _v~_bo}+9E #6L[FY@0D1 :vNqq:-(QW)q+ cz ҉Ad9vLKHjLaC!ũ̢e=HK[Z I708CmۆΪlcgg'mVUG&L;*7 Hsq "ԉ!eh*5M^*Z~ABFr\>'DE3ru,m*T7x+ c[$Una~siaHy)ShSOUetkʔ)̀9,o_-%6y|[bHDd|'ǀ\^u6<υm=_I2\AiI: @]A/a( 'E]dHy7MZJo u`P]Ҳ9^b 2:^My>ƟDu~,eU`I7-E;[.+t zPA0nr5SW}Dd+n`px;7oc=VUm{ǰi&-~~ >V5r>VSS3X8"$ 总q֡q#/'0 :~,ob˚bqaED "?qW^L._#t(:`ay8ivn`p>`ʔ)zW&6«}݇f3X Q |A^"B]"@$¯;Bټ8D'rsX1h+ TV'x+J#t(ț:} U IDATb60z 2$G>m֏>hU_;RJ_$Y$f&:yaDE-b.P<[}g믅iJ?"CK؀sB| - CY8afpC\t$ṫ:]$<&"0Vw(Ĥ7+tR-~`֭m֭[3hd,X hP."1y2=@9@Y%7*fBL >H"&h6@33O} n`pARfƇ?axWvx`5X bV;'ZH[!!< bP chNI+a-z3ӘI[(i\PE #!,qDiIdHy6m{9Z"Xz5{9mVXaGp$X(%HH 4.!{YPqITe76=LP3̸ C&97a >M4sXuI&@p{K% 4$ʆ"Ac.z89`_A=wD+d=߱5^jYC W|3f駟/~^Ъ.O -!$/ A^uf-wzÌD_ibe%D1U2y9b.[ J=Zʳ^gIKc!IqugGp!!-Z-)|DYߏ|Moŋ͠0.FPW s 4C^ 6^dJ *lйָ#c;LPuV/=!!i+k_ZY;vf} Еₙcv}D'.ЕCgƺ$$|i~(3dJY A{h23AגH1T!IC,2ʐto1{lm>ϡ/zfk]3!ZW˞/t;!_QZL=nm=)! /VWn9K$=* qa՞g-6cC34+hM?g^٩l"73]9fb108܌fo޽ԧ>kߏ={h|dՌVh;̪>(yaDEsP39_;AceE"p/W@l9~MqKx+8Ը{Z 觫NP em!)kf+_ ֬YK]jw5`F_})^vY)5eSk$]iJy1.cP 2$Rwt gy􅺁y ˲X=9>xٻaYAĭeggη!13*PxЊOyu >=Hc¦O 9aB11l( >R 2~C\bQ:f *|m;Qw I708sf逸OSut]/ޜ9sGGcP 3ŁvsԴQwu`r{j mPrZcg*AZjSW>:ڠX)|͌y,ܐtӧOǃ>͞yx{ރm6?cÆ }K_BKKɨ*kףDOBpL)cJTTi=U[aRÊ)fJ,>fɥ(yOm/1}ywj(O|'LGM" hR YyR% QPrhpՐ0L=P,>GB"鱓X1;z)iv})[B&H+_$,8]ׄ?A>9ASk&k zPI@Hpx*6- zLb+3#)/M`.!+_qb~^T:=z9vѦqm+VrBQǟ>vʱ=ڿNxoB(vQ'PwM̐tf7& ߐ\=iZI(cNu\!Ȯnġ0A2V{r/>K! y?zǾ8 t@4hN1_&=o!%Qpx vH%ȩU=x؀N #Hx$CVʔIt>co([E_U;>dy:{9V F;}C{hGE I70000b\);%!,Ja/7N IIx1LU4vr+$!Лm[nHAّ,d7.NXJjD?gidҡyx>dK](9#h- &#n~A5h]>=+(F!U_t4sa !eE6;iJK#"v%ulTőr36u*K ]Ŷ< h,(m;1OuWM[gnt50000"d2L;/LLl̬xxdwNMA9K t.V"IMRt94 F&V9.,9R‹s 5"C@R=sI-CݐtI %?dw~_M7V"D} s灈,@ht61ܾL=n哉CLЇxJك BBB"W\G}'q~e tuyAaHA5;·3>G:T]㖝xHs R !eժXIxAC˪= >'$~)0 apW-A3j\L‡@ C [DJJTw6Z=l)S]7n;1"nXݡx[ I70000f8a{1-T$ۅ+\!sy!nD!.Q u"lhGzFS_!b, RgTMÐyaWP7Ā8? n7 ^?o!*}V||`ꐄ"3hةt{[>C & "'߽_?;ws}CldS J2M X)E`-uPTw5젏gE>RE_[|Y;tO?( XD1KrcI' u+x i40x~llYr|n````0zIf V7f_zEk7z{m"/\JLbu'*Þ&,NxR[P!Q-Y,T_y 8jaQ xR4$༚g3D%;ฮs.$0)=hCezДDIq: 8IL2yMSA[ ]ٶʂ>8BU lhQC?[oD>$0 ø[;Ύ_=s2lޯ.UUxU},0.qcPVѵ7r*^m $ HXJе^VH:AjQgt\3*:囤a |@{{Ҽr7wG;:,ȥinrg=Fe $Wq讳ana@gg|$-nsYi{Leu]Ir [+ب&NIt0I7 0wN7QEYk-ɼqk 0 c zɂ t$0 0Jf]$ZuA/7nna1m=2?IU\"Z݈ 9Ir 0 czM"YZ3/{!!`upS mGEIaat*,QtIJ=:(V6 t0 0&Q 0Ar~8Iݽ1gG<< ;C.(^{menaFfc@г^d&^e_$@e$EF -k^Sp*}*ȣAҙx ΢La w,=MG+IqDB#eqQr_=rO|I\}ooOe<( r:&aar36@zVYu*]2D˽~*q?V['ID"xonnaluvZ9z719D**ڎrjt9 7$mAqV$0 0foע$LkUk_Z$FLxq9<:Sߨ"aa*nAMgYPZ&aadu LeAp6}ZZr&.&&a1C)v42],UJ}ZNP&mSʕtI7 0Th"Whon%JmkR~/uh).7Ǣ&P*P(P*(xu4\.GCCMMMr9@cʈ!(.k74RBب6A߼i͗.2ԁ7LҧBS8y$gϞΝ;顧/rҥBaʯ9ٳ={6mmm̝;?>c,\n f7```ӧOsΜ9Coo/ϟ9zzz8{,ϟjFQ4pdܶ1{l̙C[[ͻnq,]fg#X$Zy%S6T{`t"c=Jww7o&o&>@uzfey7Z[[{?̊+?իYr%ЇlCCC8p`d:tÇ3mWE#,̛7e˖|r^/_Κ5kXbfͲcBFONY.畔Z*FͽxO.]=zǏs 9‘#G8~8|u)&3>Ǿ}{nѢE,];3%Kd-[wͲe˘={u`ѣ#c;v{cǎqرi;bk׮ϟ]t)w}]`u1l~5ī~inA_MW𶙑Izb޽޽={/gUԩS:uW_}u-ZڵkY~==ׯg…qeNv52vw"d*ΝܹscYx17nfݺu_V}]z-9bQ)m6m6ؒ%KXb+V`\+Wr]wQeA/ǿeo`~W jOl ɿegե޽{ٹs' ۷o>%yb>6}}}Ͻc=Ϛ5kXz5vuQ%T&%A7թCN&A!Y8,'$=T*/ݻw[ epp]v]o<3 k׮\ո{\|L'Amu=AP"J*"H ~8+ADp5W[4TQOuaV:prʼn ~0߸d' lJ~|AЫHtZYOգ#@p\ &m# BxCyJ0/`þXW+;`aӖ[H+ zֶK>vMq]v1o`|=D ̈́6/n%-uMATPu8sI1rnnaUay=!ArtL*ws/FvhB5D VxKBg7/ϚK|h "Oؙ/UcI&aaT*sZomr2,fi;eO9B ?sXm}%`[?n{?'zЖK^_"^sldaӊ,).ՙ MZ,4@/Dž¿W~ѽ߾m>UnItUG򸣳]~Kf'8ZL}>)X$0 0R͊*i[($YO >: [7TqʦP:vF"tOnr?sφuZGA<ꇋk5eI7 0[3l _T&:4Η|suC*;3o#xyjgڃ{{~Y=!+ͺ@'0 t0 ø5<] Xu,|o0KTZ|jkЌZ֩'0n'-a(;c:7;M;~W A`ۣ z,&a1S^ɖ3]9A-^tMSƷ2ֈ }|_vBymYǏbPtF\T"&&a1cqJrus4e!z(z}Z'7xRǴ;8%Rd"]Cľth|@AA״OGOXut0 Ø/S:'rJwazK%).UܤTA%&/AE~> [u;.?עݰ{I]lS#t0 0,^o7 ט g_<5D7/NUG M 0 cF7e=%j7R͊ }&i@fy狨"A>6łq2;k*.Ju]jna3$7ƹ4ZV'T+ dʼnP QO9PEP=PoKd0+hna,HKԕ/^",yY}Be˔Y_ot~I)"$([e%ǥӫ"N^ߣA/oc5vPHRkJc:ȑd@hHkHQGKz F[Lc4yǮKwxܶ_[0n=Φc"B(Xf[!)"^U}.}fk]JEz Q[Mp8'ӯgס)F ۀ{%gsQp8M5]GveQ.3|6NۏGН?u".UEc, 8ab]+}aEƹ^d$䓓:Mb$A^?J:#3bM`Dg{q3Zǀ|7jc:~ߟ}QʧY7c.xX|?ް@:s?v__9y5 'C5fi[k-N@˹:b^DC_\eAuzֶzO&B1֞uOtklr=x7p%C1`7F? %=B"0[I~7$q[Z?D=I|Tu$QIRffJ5';oI.uEx5=>8tJӣ^zc@Fk 2MKkOA;HgM[ m+JsUuy@6 +#X~nnc7YꦂtiɅʓLBX8<zIa`I7cq$Qio!im'ظE/] )SE٭O4 «fZhh6*&6$ǁMN TEر||XCT="_L&gE.XTnLg9>hCu~ݗƨi<ĕDgOu#ƨ'L;k4o#.(ˤ&z7+,:Q2/|^]Iů0ι_RB+TQ4At[k]u]Q7gLd3I"BEO\+.JeFIȿ1s)q%M/F=~?C$Qϑl3D7H>"*k$~pGRanL!)Y+?e^nq"4,G0n%HsD(4OO!vM]#"tH;kʵ5$Q#,_&(ݵL^O_㰑*C}]]ds H?)/}iZj8_x(?tPiױks:$QWI]F-`dqv,Ә*I돎:nAMH"_Eҷ3.ᳮ\,8ܨ='QQ iNp',k1-A@Пs婣>sXIN*աw)apQjxf9_?)׿_ Ԃ%$mv,F=̜gdd?ZuTG}#=,5%}xխq "{/*7כW%pyc𕋧wE1i *I*G7Kk|B8e@K#{]Ra1aQe+$ I4֒^o^'1tĪ84n{lT3A7 E>LaǺe~to_pſM.skc(²*g9!.Fg=t-aL$uf_lH=xSE"ء՗-&F^*|P ߽Ot7tNx%.ni44(QWO*.Qnþ_jna3Wy~ףk7S-/%,+:^Dg ő)2J)%i$7Jqtw.Q>n6*PLaQh5TU _|ٟ҆!`)I}FNF-T~ Z}ZI{zIaٷyX$0 0";#ݵ.^:{b34b{cIꆖ*+ݰDfڨH˔mZ:̶7m]oԯUӉk}#)|oׂIaa7]vC ^4WDjG l{LրHJ,).*kѵPz҅_iنa1MYs7r ~_9}\d֚kvZ k6T#AkDKb]rOdܺ0 0|>ΝDŽ\*ߦz]O8U+ײ?Ũ2'%|tfBϵ~om\4X$0 0)J7^ODcT}դf3 zi/A*#f(( lN}SOϷaa@3صΆ]sg(P.m5=kzmV4y^ SX=@sz)us?~L 0 Øo#ĺu[7Wqn>QW@L K'Cm7I+X#A/xUDcЇ>r9}oNaQmQ-͏^{[!|!/:W:ezQi.Oez<Z{/ҁ/I^ժ na">q|ï~~Ο=M[#'JɗPCu3HгWv<^>UTcD hr_ meU}6=7I7 0 cٶ=ho?#)0k݇h'p-_Ȭ\1 E dVAJ7Tŷc¬С/>/aWŁ?n R< wznnaF[plE%MhS~Ӯ5B!NRMvNBDQ=O {j=t@cbR7~ǜ|l&N_}7L 0 èGYO뻌« Y kJ'և 7r "Y7}ME&:uGUr$ /F~];BCxUYj t0 0okhoGds _g|t柌%|\)=^xG njOFEYVA+ G"!H@  JI?P~}/ |ǎ 瘫`)-&aa:xX6A|i}ƅaGL EQPN:J{m'Uh+^# 'eE7OB'#{F[ۅ* $0 0[O5p&ǓOM6?ZK?q N_LkZr}C{T#$k\I5rŻ*_뛓M'#rM4\dg h9AwrtR˥m7eCR>ݙ\A;97I7 0 c ``w8)8#!BKp^-%M"ɑo^QT\8Lkgy%:.c4\a{dD y#RQtZcCb6OŇf-'e#rl\&茇&aada;5%Gmk358PA˽b@B=1U$ URgN ]zE诉GZI1H$EGMߒF=$ UPG@!G('b8 \pKT|[S\?|NVB/sO;  䒪^%DzK/Л5ZGsŸL [Y?(+g$qݺnz&aaw-ucB}BǸ`j5-m&iqa⥁YM.n yF6щx|a#8U "s} 21"H!"R4Az Q<w~su 5Wܫ3[ sc ?yEgIENDB`mkdocstrings-python-legacy-0.2.6/docs/reference/000077500000000000000000000000001476761050100217045ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/docs/reference/mkdocstrings_handlers/000077500000000000000000000000001476761050100262735ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/docs/reference/mkdocstrings_handlers/python.md000066400000000000000000000002031476761050100301310ustar00rootroot00000000000000--- title: API reference hide: - navigation --- # ::: mkdocstrings_handlers.python options: show_root_full_path: true mkdocstrings-python-legacy-0.2.6/docs/snippets/000077500000000000000000000000001476761050100216135ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/docs/snippets/function_annotations_google.py000066400000000000000000000007501476761050100277650ustar00rootroot00000000000000from typing import Optional def my_function(param1: int, param2: Optional[str] = None) -> str: """A short description of this function. Arguments: param1: An integer? param2: A string? If you have a long description, you can split it on multiple lines. Just remember to indent those lines consistently. Complex markup is supported in sections items. I'm a code block! """ return f"{param2}{param1}" mkdocstrings-python-legacy-0.2.6/docs/snippets/function_annotations_rst.py000066400000000000000000000006331476761050100273210ustar00rootroot00000000000000from typing import Optional def my_function(param1: int, param2: Optional[str] = None) -> str: """A short description of this function. Complex markup is supported in the main description section. I'm a code block! :param param1: An integer? :param param2: A string? If you have a long description, you can split it on multiple lines. """ return f"{param2}{param1}" mkdocstrings-python-legacy-0.2.6/docs/usage.md000066400000000000000000000326711476761050100214050ustar00rootroot00000000000000# Usage WARNING: **This is the documentation for the LEGACY Python handler.** To read the documentation for the NEW handler, go to the [new handler documentation](https://mkdocstrings.github.io/python). The tool used by the legacy Python handler to collect documentation from Python source code is [`pytkdocs`](https://mkdocstrings.github.io/pytkdocs). It stands for *(Python) Take Docs*, and is supposed to be a pun on MkDocs (*Make Docs*?). Like every handler, the legacy Python handler accepts both **global** and **local** options. ## Global-only options Some options are **global only**, and go directly under the handler's name. - `import`: this option is used to import Sphinx-compatible objects inventories from other documentation sites. For example, you can import the standard library objects inventory like this: ```yaml title="mkdocs.yml" plugins: - mkdocstrings: handlers: python: import: - https://docs.python-requests.org/en/master/objects.inv ``` When importing an inventory, you enable automatic cross-references to other documentation sites like the standard library docs or any third-party package docs. Typically, you want to import the inventories of your project's dependencies, at least those that are used in the public API. NOTE: This global option is common to *all* handlers, however they might implement it differently (or not even implement it). - `paths`: this option is used to provide filesystem paths in which to search for Python modules. Non-absolute paths are computed as relative to MkDocs configuration file. Example: ```yaml title="mkdocs.yml" plugins: - mkdocstrings: handlers: python: paths: [src] # search packages in the src folder ``` More details at [Finding modules](#finding-modules). - `setup_commands`: this option is used to instruct `pytkdocs`, the tool responsible for collecting data from sources, to run Python statements before starting to collect data. It is declared as a list of strings: ```yaml title="mkdocs.yml" plugins: - mkdocstrings: handlers: python: setup_commands: - import os - import django - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_django_app.settings") - django.setup() ``` The setup commands are executed only once, when the `pytkdocs` background process is started. ## Global/local options The other options can be used both globally *and* locally, under the `options` key. For example, globally: ```yaml title="mkdocs.yml" plugins: - mkdocstrings: handlers: python: options: do_something: true ``` ...and locally, overriding the global configuration: ```md title="docs/some_page.md" ::: package.module.class options: do_something: false ``` These options affect how the documentation is collected from sources and renderered: headings, members, docstrings, etc. ### ::: mkdocstrings_handlers.python.handler.PythonHandler.default_config options: show_root_heading: false show_root_toc_entry: false ## Supported docstrings styles Right now, `pytkdocs` supports the Google-style, Numpy-style and reStructuredText-style docstring formats. The style used by default is the Google-style. You can configure what style you want to use with the `docstring_style` and `docstring_options` options, both globally or per autodoc instruction. ### Google-style You can see examples of Google-style docstrings in [Napoleon's documentation](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html). #### Sections Docstrings sections are parsed by `pytkdocs` and rendered by *mkdocstrings*. Supported sections are: - `Arguments` (or `Args`, `Parameters`, `Params`) - `Attributes` - `Examples` (or `Example`) - `Raises` (or `Raise`, `Except`, `Exceptions`) - `Returns` (or `Return`) #### Admonitions Additionally, any section that is not recognized will be transformed into its admonition equivalent. For example: === "Original" ```python """ Note: You can disable this behavior with the `replace_admonitions` option. To prevent `pytkdocs` from converting sections to admonitions, use the `replace_admonitions`: ```md ::: my_package.my_module options: docstring_style: google # this is the default docstring_options: replace_admonitions: no ``` So meta! """ ``` === "Modified" ```python """ !!! note "You can disable this behavior with the `replace_admonitions` option." To prevent `pytkdocs` from converting sections to admonitions, use the `replace_admonitions`: ```md ::: my_package.my_module options: docstring_style: google # this is the default docstring_options: replace_admonitions: no ``` So meta! """ ``` === "Result" > NOTE: **You can disable this behavior with the `replace_admonitions` parser option.** > To prevent `pytkdocs` from converting sections to admonitions, > use the `replace_admonitions` parser option: > > ```md > ::: my_package.my_module > options: > docstring_style: google # this is the default > docstring_options: > replace_admonitions: no > ``` > > So meta! As shown in the above example, this can be disabled with the `replace_admonitions` option of the Google-style parser: ```yaml ::: my_package.my_module options: docstring_style: google # this is the default docstring_options: replace_admonitions: no ``` #### Annotations Type annotations are read both in the code and in the docstrings. > EXAMPLE: **Example with a function** > **Expand the source at the end to see the original code!** > > ### ::: snippets.function_annotations_google:my_function > options: > show_root_heading: false > show_root_toc_entry: false ### Numpy-style IMPORTANT: **Extra dependency required** You'll need an extra dependency to parse Numpy-style docstrings: ``` pdm add -d --group docs 'pytkdocs[numpy-style]' poetry add -D 'pytkdocs[numpy-style]' pip install 'pytkdocs[numpy-style]' # etc. ``` NOTE: As Numpy-style is partially supported by the underlying parser, you may experience problems in the building process if your docstring has a `Methods` section in the class docstring (see [#366](https://github.com/mkdocstrings/mkdocstrings/issues/366)). You can see examples of Numpy-style docstrings in [numpydoc's documentation](https://numpydoc.readthedocs.io/en/latest/format.html). ### reStructuredText-style WARNING: **Partial support** Only RST-**style** is supported, not the whole RST markup specification. You can see examples of reStructuredText-style docstrings in [Sphinx's documentation](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html). #### Sections Docstrings directives are parsed by `pytkdocs` and rendered by *mkdocstrings*. Supported directives are: - `param` (or `parameter`, `arg`, `argument`, `key`, `keyword`) - `type` - `raises` (or `raise`, `except`, `exception`) - `var` (or `ivar`, `cvar`) - `vartype` - `returns` (or `return1`) - `rtype` Details about how to use each directive can be found in the [Sphinx domain documentation](https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html?highlight=python%20domain#info-field-lists) #### Annotations Type annotations are read both in the code and in the docstrings. > EXAMPLE: **Example with a function** > **Expand the source at the end to see the original code!** > > ::: snippets.function_annotations_rst:my_function > options: > docstring_style: restructured-text > show_root_heading: no > show_root_toc_entry: no ## Finding modules There are multiple ways to tell the handler where to find your packages/modules. **The recommended method is to use the `paths` option, as it's the only one that works with the `-f` option of MkDocs, allowing to build the documentation from any location on the file system.** Indeed, the paths provided with the `paths` option are computed as relative to the configuration file (mkdocs.yml), so that the current working directory has no impact on the build process: *you can build the docs from any location on your filesystem*. ### Using the `paths` option TIP: **This is the recommended method.** 1. mkdocs.yml in root, package in root ```tree root/ mkdocs.yml package/ ``` ```yaml title="mkdocs.yml" plugins: - mkdocstrings: handlers: python: paths: [.] # actually not needed, default ``` 1. mkdocs.yml in root, package in subfolder ```tree root/ mkdocs.yml src/ package/ ``` ```yaml title="mkdocs.yml" plugins: - mkdocstrings: handlers: python: paths: [src] ``` 1. mkdocs.yml in subfolder, package in root ```tree root/ docs/ mkdocs.yml package/ ``` ```yaml title="mkdocs.yml" plugins: - mkdocstrings: handlers: python: paths: [..] ``` 1. mkdocs.yml in subfolder, package in subfolder ```tree root/ docs/ mkdocs.yml src/ package/ ``` ```yaml title="mkdocs.yml" plugins: - mkdocstrings: handlers: python: paths: [../src] ``` Except for case 1, which is supported by default, **we strongly recommend to set the path to your packages using this option, even if it works without it** (for example because your project manager automatically adds `src` to PYTHONPATH), to make sure anyone can build your docs from any location on their filesystem. Behind the scenes, the handler will actually insert the specified paths in front of `sys.path`. ### Using the PYTHONPATH environment variable WARNING: **This method has limitations.** This method might work for you, with your current setup, but not for others trying your build your docs with their own setup/environment. We recommend to use the [`paths` method](#using-the-paths-option) instead. You can take advantage of the usual Python loading mechanisms. In Bash and other shells, you can run your command like this (note the prepended `PYTHONPATH=...`): 1. mkdocs.yml in root, package in root ```tree root/ mkdocs.yml package/ ``` ```bash PYTHONPATH=. mkdocs build # actually not needed, default ``` 1. mkdocs.yml in root, package in subfolder ```tree root/ mkdocs.yml src/ package/ ``` ```bash PYTHONPATH=src mkdocs build ``` 1. mkdocs.yml in subfolder, package in root ```tree root/ docs/ mkdocs.yml package/ ``` ```bash PYTHONPATH=. mkdocs build -f docs/mkdocs.yml ``` 1. mkdocs.yml in subfolder, package in subfolder ```tree root/ docs/ mkdocs.yml src/ package/ ``` ```bash PYTHONPATH=src mkdocs build -f docs/mkdocs.yml ``` ### Installing your package in the current Python environment WARNING: **This method has limitations.** This method might work for you, with your current setup, but not for others trying your build your docs with their own setup/environment. We recommend to use the [`paths` method](#using-the-paths-option) instead. Install your package in the current environment, and run MkDocs: === "pip" ```bash . venv/bin/activate pip install -e . mkdocs build ``` === "PDM" ```bash pdm install pdm run mkdocs build ``` === "Poetry" ```bash poetry install poetry run mkdocs build ``` ### Using the setup commands WARNING: **This method has limitations.** This method might work for you, with your current setup, but not for others trying your build your docs with their own setup/environment. We recommend to use the [`paths` method](#using-the-paths-option) instead. You can use the setup commands to modify `sys.path`: ```yaml title="mkdocs.yml" plugins: - mkdocstrings: handlers: python: setup_commands: - import sys - sys.path.append("src") # or sys.path.insert(0, "src") ``` ## Mocking libraries You may want to generate documentation for a package while its dependencies are not available. The Python handler provides itself no builtin way to mock libraries, but you can use the `setup_commands` to mock them manually: ```yaml title="mkdocs.yml" plugins: - mkdocstrings: handlers: python: setup_commands: - import sys - from unittest.mock import MagicMock as mock - sys.modules["lib1"] = mock() - sys.modules["lib2"] = mock() - sys.modules["lib2.module1"] = mock() - sys.modules["lib2.module1.moduleB"] = mock() # etc ``` ## Recommended style (Material) Here are some CSS rules for the [*Material for MkDocs*](https://squidfunk.github.io/mkdocs-material/) theme: ```css /* Indentation. */ div.doc-contents:not(.first) { padding-left: 25px; border-left: .05rem solid var(--md-typeset-table-color); } ``` ## Recommended style (ReadTheDocs) Here are some CSS rules for the built-in *ReadTheDocs* theme: ```css /* Indentation. */ div.doc-contents:not(.first) { padding-left: 25px; border-left: 4px solid rgba(230, 230, 230); } ``` mkdocstrings-python-legacy-0.2.6/duties.py000066400000000000000000000146621476761050100206760ustar00rootroot00000000000000"""Development tasks.""" from __future__ import annotations import os import re import sys from contextlib import contextmanager from importlib.metadata import version as pkgversion from pathlib import Path from typing import TYPE_CHECKING from duty import duty, tools if TYPE_CHECKING: from collections.abc import Iterator from duty.context import Context PY_SRC_PATHS = (Path(_) for _ in ("src", "tests", "duties.py", "scripts")) PY_SRC_LIST = tuple(str(_) for _ in PY_SRC_PATHS) PY_SRC = " ".join(PY_SRC_LIST) CI = os.environ.get("CI", "0") in {"1", "true", "yes", ""} WINDOWS = os.name == "nt" PTY = not WINDOWS and not CI MULTIRUN = os.environ.get("MULTIRUN", "0") == "1" def pyprefix(title: str) -> str: if MULTIRUN: prefix = f"(python{sys.version_info.major}.{sys.version_info.minor})" return f"{prefix:14}{title}" return title @contextmanager def material_insiders() -> Iterator[bool]: if "+insiders" in pkgversion("mkdocs-material"): os.environ["MATERIAL_INSIDERS"] = "true" try: yield True finally: os.environ.pop("MATERIAL_INSIDERS") else: yield False def _get_changelog_version() -> str: changelog_version_re = re.compile(r"^## \[(\d+\.\d+\.\d+)\].*$") with Path(__file__).parent.joinpath("CHANGELOG.md").open("r", encoding="utf8") as file: return next(filter(bool, map(changelog_version_re.match, file))).group(1) # type: ignore[union-attr] @duty def changelog(ctx: Context, bump: str = "") -> None: """Update the changelog in-place with latest commits. Parameters: bump: Bump option passed to git-changelog. """ ctx.run(tools.git_changelog(bump=bump or None), title="Updating changelog") ctx.run(tools.yore.check(bump=bump or _get_changelog_version()), title="Checking legacy code") @duty(pre=["check-quality", "check-types", "check-docs", "check-api"]) def check(ctx: Context) -> None: """Check it all!""" @duty def check_quality(ctx: Context) -> None: """Check the code quality.""" ctx.run( tools.ruff.check(*PY_SRC_LIST, config="config/ruff.toml"), title=pyprefix("Checking code quality"), ) @duty def check_docs(ctx: Context) -> None: """Check if the documentation builds correctly.""" Path("htmlcov").mkdir(parents=True, exist_ok=True) Path("htmlcov/index.html").touch(exist_ok=True) with material_insiders(): ctx.run( tools.mkdocs.build(strict=True, verbose=True), title=pyprefix("Building documentation"), ) @duty def check_types(ctx: Context) -> None: """Check that the code is correctly typed.""" os.environ["MYPYPATH"] = "src" os.environ["FORCE_COLOR"] = "1" ctx.run( tools.mypy(*PY_SRC_LIST, config_file="config/mypy.ini"), title=pyprefix("Type-checking"), ) @duty def check_api(ctx: Context, *cli_args: str) -> None: """Check for API breaking changes.""" ctx.run( tools.griffe.check("mkdocstrings_handlers", search=["src"], color=True).add_args(*cli_args), title="Checking for API breaking changes", nofail=True, ) @duty def docs(ctx: Context, *cli_args: str, host: str = "127.0.0.1", port: int = 8000) -> None: """Serve the documentation (localhost:8000). Parameters: host: The host to serve the docs from. port: The port to serve the docs on. """ with material_insiders(): ctx.run( tools.mkdocs.serve(dev_addr=f"{host}:{port}").add_args(*cli_args), title="Serving documentation", capture=False, ) @duty def docs_deploy(ctx: Context) -> None: """Deploy the documentation to GitHub pages.""" os.environ["DEPLOY"] = "true" with material_insiders() as insiders: if not insiders: ctx.run(lambda: False, title="Not deploying docs without Material for MkDocs Insiders!") ctx.run(tools.mkdocs.gh_deploy(), title="Deploying documentation") @duty def format(ctx: Context) -> None: """Run formatting tools on the code.""" ctx.run( tools.ruff.check(*PY_SRC_LIST, config="config/ruff.toml", fix_only=True, exit_zero=True), title="Auto-fixing code", ) ctx.run(tools.ruff.format(*PY_SRC_LIST, config="config/ruff.toml"), title="Formatting code") @duty def build(ctx: Context) -> None: """Build source and wheel distributions.""" ctx.run( tools.build(), title="Building source and wheel distributions", pty=PTY, ) @duty def publish(ctx: Context) -> None: """Publish source and wheel distributions to PyPI.""" if not Path("dist").exists(): ctx.run("false", title="No distribution files found") dists = [str(dist) for dist in Path("dist").iterdir()] ctx.run( tools.twine.upload(*dists, skip_existing=True), title="Publishing source and wheel distributions to PyPI", pty=PTY, ) @duty(post=["build", "publish", "docs-deploy"]) def release(ctx: Context, version: str = "") -> None: """Release a new Python package. Parameters: version: The new version number to use. """ if not (version := (version or input("> Version to release: ")).strip()): ctx.run("false", title="A version must be provided") ctx.run("git add pyproject.toml CHANGELOG.md", title="Staging files", pty=PTY) ctx.run(["git", "commit", "-m", f"chore: Prepare release {version}"], title="Committing changes", pty=PTY) ctx.run(f"git tag {version}", title="Tagging commit", pty=PTY) ctx.run("git push", title="Pushing commits", pty=False) ctx.run("git push --tags", title="Pushing tags", pty=False) @duty(silent=True, aliases=["cov"]) def coverage(ctx: Context) -> None: """Report coverage as text and HTML.""" ctx.run(tools.coverage.combine(), nofail=True) ctx.run(tools.coverage.report(rcfile="config/coverage.ini"), capture=False) ctx.run(tools.coverage.html(rcfile="config/coverage.ini")) @duty def test(ctx: Context, *cli_args: str, match: str = "") -> None: """Run the test suite. Parameters: match: A pytest expression to filter selected tests. """ py_version = f"{sys.version_info.major}{sys.version_info.minor}" os.environ["COVERAGE_FILE"] = f".coverage.{py_version}" ctx.run( tools.pytest( "tests", config_file="config/pytest.ini", select=match, color="yes", ).add_args("-n", "auto", *cli_args), title=pyprefix("Running tests"), ) mkdocstrings-python-legacy-0.2.6/logo.png000077700000000000000000000000001476761050100230552docs/logo.pngustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/mkdocs.yml000066400000000000000000000106741476761050100210310ustar00rootroot00000000000000site_name: "mkdocstrings-python (legacy)" site_description: "A legacy Python handler for mkdocstrings." site_url: "https://mkdocstrings.github.io/python-legacy" repo_url: "https://github.com/mkdocstrings/python-legacy" repo_name: "mkdocstrings/python-legacy" site_dir: "site" watch: [mkdocs.yml, README.md, CONTRIBUTING.md, CHANGELOG.md, src/mkdocstrings_handlers] copyright: Copyright © 2021 Timothée Mazzucotelli edit_uri: edit/main/docs/ validation: omitted_files: warn absolute_links: warn unrecognized_links: warn nav: - Home: - Overview: index.md - Usage: usage.md - Changelog: changelog.md - Credits: credits.md - License: license.md - API reference: reference/mkdocstrings_handlers/python.md - Development: - Contributing: contributing.md - Code of Conduct: code_of_conduct.md - Coverage report: coverage.md - mkdocstrings: https://mkdocstrings.github.io/ theme: name: material custom_dir: docs/.overrides icon: logo: material/currency-sign features: - announce.dismiss - content.action.edit - content.action.view - content.code.annotate - content.code.copy - content.tooltips - navigation.footer - navigation.instant.preview - navigation.path - navigation.sections - navigation.tabs - navigation.tabs.sticky - navigation.top - search.highlight - search.suggest - toc.follow palette: - media: "(prefers-color-scheme)" toggle: icon: material/brightness-auto name: Switch to light mode - media: "(prefers-color-scheme: light)" scheme: default primary: teal accent: purple toggle: icon: material/weather-sunny name: Switch to dark mode - media: "(prefers-color-scheme: dark)" scheme: slate primary: black accent: lime toggle: icon: material/weather-night name: Switch to system preference extra_css: - css/material.css - css/mkdocstrings.css extra_javascript: - js/feedback.js markdown_extensions: - attr_list - admonition - callouts - footnotes - pymdownx.emoji: emoji_index: !!python/name:material.extensions.emoji.twemoji emoji_generator: !!python/name:material.extensions.emoji.to_svg - pymdownx.magiclink - pymdownx.snippets: base_path: [!relative $config_dir] check_paths: true - pymdownx.superfences - pymdownx.tabbed: alternate_style: true slugify: !!python/object/apply:pymdownx.slugs.slugify kwds: case: lower - pymdownx.tasklist: custom_checkbox: true - toc: permalink: "¤" plugins: - search - autorefs: resolve_closest: true - markdown-exec - section-index - coverage - mkdocstrings: handlers: python: import: - https://docs.python.org/3/objects.inv - https://mkdocstrings.github.io/objects.inv - https://mkdocstrings.github.io/pytkdocs/objects.inv paths: [src, docs] options: docstring_section_style: list filters: ["!^_"] group_by_category: true heading_level: 1 inherited_members: true merge_init_into_class: true separate_signature: true show_category_heading: true show_root_heading: true show_root_full_path: false show_signature_annotations: true show_source: true show_submodules: false signature_crossrefs: true summary: true - llmstxt: files: - output: llms-full.txt inputs: - index.md - reference/**.md - git-revision-date-localized: enabled: !ENV [DEPLOY, false] enable_creation_date: true type: timeago - minify: minify_html: !ENV [DEPLOY, false] - group: enabled: !ENV [MATERIAL_INSIDERS, false] plugins: - typeset extra: social: - icon: fontawesome/brands/github link: https://github.com/pawamoy - icon: fontawesome/brands/mastodon link: https://fosstodon.org/@pawamoy - icon: fontawesome/brands/twitter link: https://twitter.com/pawamoy - icon: fontawesome/brands/gitter link: https://gitter.im/python-legacy/community - icon: fontawesome/brands/python link: https://pypi.org/project/mkdocstrings-python-legacy/ analytics: feedback: title: Was this page helpful? ratings: - icon: material/emoticon-happy-outline name: This page was helpful data: 1 note: Thanks for your feedback! - icon: material/emoticon-sad-outline name: This page could be improved data: 0 note: Let us know how we can improve this page. mkdocstrings-python-legacy-0.2.6/pyproject.toml000066400000000000000000000060301476761050100217310ustar00rootroot00000000000000[build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" [project] name = "mkdocstrings-python-legacy" description = "A legacy Python handler for mkdocstrings." authors = [{name = "Timothée Mazzucotelli", email = "dev@pawamoy.fr"}] license = "ISC" license-files = ["LICENSE"] readme = "README.md" requires-python = ">=3.9" keywords = [] dynamic = ["version"] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Documentation", "Topic :: Software Development", "Topic :: Utilities", "Typing :: Typed", ] dependencies = [ "mkdocstrings>=0.28.3", "mkdocs-autorefs>=1.1", "pytkdocs>=0.14", ] [project.urls] Homepage = "https://mkdocstrings.github.io/python-legacy" Documentation = "https://mkdocstrings.github.io/python-legacy" Changelog = "https://mkdocstrings.github.io/python-legacy/changelog" Repository = "https://github.com/mkdocstrings/python-legacy" Issues = "https://github.com/mkdocstrings/python-legacy/issues" Discussions = "https://github.com/mkdocstrings/python-legacy/discussions" Gitter = "https://gitter.im/mkdocstrings/python-legacy" Funding = "https://github.com/sponsors/pawamoy" [tool.pdm.version] source = "call" getter = "scripts.get_version:get_version" [tool.pdm.build] includes = ["src/mkdocstrings_handlers"] # Include as much as possible in the source distribution, to help redistributors. excludes = ["**/.pytest_cache", "**/.mypy_cache", "**/__pycache__"] source-includes = [ "config", "docs", "scripts", "share", "tests", "duties.py", "mkdocs.yml", "*.md", "LICENSE", ] [tool.pdm.build.wheel-data] # Manual pages can be included in the wheel. # Depending on the installation tool, they will be accessible to users. # pipx supports it, uv does not yet, see https://github.com/astral-sh/uv/issues/4731. data = [ {path = "share/**/*", relative-to = "."}, ] [dependency-groups] maintain = [ "build>=1.2", "git-changelog>=2.5", "twine>=5.1", "yore>=0.3.3", ] ci = [ "duty>=1.6", "griffe>=1.6", "ruff>=0.4", "pytest>=8.2", "pytest-cov>=5.0", "pytest-randomly>=3.15", "pytest-xdist>=3.6", "mypy>=1.10", "types-markdown>=3.6", "types-pyyaml>=6.0", ] docs = [ "markdown-callouts>=0.4", "markdown-exec>=1.8", "mkdocs>=1.6", "mkdocs-coverage>=1.0", "mkdocs-git-revision-date-localized-plugin>=1.2", "mkdocs-llmstxt>=0.1", "mkdocs-material>=9.5", "mkdocs-minify-plugin>=0.8", "mkdocs-section-index>=0.3", # YORE: EOL 3.10: Remove line. "tomli>=2.0; python_version < '3.11'", ] [tool.uv] default-groups = ["maintain", "ci", "docs"] mkdocstrings-python-legacy-0.2.6/scripts/000077500000000000000000000000001476761050100205055ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/scripts/gen_api_ref.py000066400000000000000000000022721476761050100233200ustar00rootroot00000000000000# Generate the API reference pages and navigation. from pathlib import Path import mkdocs_gen_files nav = mkdocs_gen_files.Nav() mod_symbol = '' root = Path(__file__).parent.parent src = root / "src" for path in sorted(src.rglob("*.py")): module_path = path.relative_to(src).with_suffix("") doc_path = path.relative_to(src).with_suffix(".md") full_doc_path = Path("reference", doc_path) parts = tuple(module_path.parts) if parts[-1] == "__init__": parts = parts[:-1] doc_path = doc_path.with_name("index.md") full_doc_path = full_doc_path.with_name("index.md") if any(part.startswith("_") for part in parts): continue nav_parts = [f"{mod_symbol} {part}" for part in parts] nav[tuple(nav_parts)] = doc_path.as_posix() with mkdocs_gen_files.open(full_doc_path, "w") as fd: ident = ".".join(parts) fd.write(f"---\ntitle: {ident}\n---\n\n::: {ident}") mkdocs_gen_files.set_edit_path(full_doc_path, ".." / path.relative_to(root)) with mkdocs_gen_files.open("reference/SUMMARY.txt", "w") as nav_file: nav_file.writelines(nav.build_literate_nav()) mkdocstrings-python-legacy-0.2.6/scripts/gen_credits.py000066400000000000000000000150071476761050100233500ustar00rootroot00000000000000# Script to generate the project's credits. from __future__ import annotations import os import sys from collections import defaultdict from collections.abc import Iterable from importlib.metadata import distributions from itertools import chain from pathlib import Path from textwrap import dedent from typing import Union from jinja2 import StrictUndefined from jinja2.sandbox import SandboxedEnvironment from packaging.requirements import Requirement # YORE: EOL 3.10: Replace block with line 2. if sys.version_info >= (3, 11): import tomllib else: import tomli as tomllib project_dir = Path(os.getenv("MKDOCS_CONFIG_DIR", ".")) with project_dir.joinpath("pyproject.toml").open("rb") as pyproject_file: pyproject = tomllib.load(pyproject_file) project = pyproject["project"] project_name = project["name"] devdeps = [dep for group in pyproject["dependency-groups"].values() for dep in group if not dep.startswith("-e")] PackageMetadata = dict[str, Union[str, Iterable[str]]] Metadata = dict[str, PackageMetadata] def _merge_fields(metadata: dict) -> PackageMetadata: fields = defaultdict(list) for header, value in metadata.items(): fields[header.lower()].append(value.strip()) return { field: value if len(value) > 1 or field in ("classifier", "requires-dist") else value[0] for field, value in fields.items() } def _norm_name(name: str) -> str: return name.replace("_", "-").replace(".", "-").lower() def _requirements(deps: list[str]) -> dict[str, Requirement]: return {_norm_name((req := Requirement(dep)).name): req for dep in deps} def _extra_marker(req: Requirement) -> str | None: if not req.marker: return None try: return next(marker[2].value for marker in req.marker._markers if getattr(marker[0], "value", None) == "extra") except StopIteration: return None def _get_metadata() -> Metadata: metadata = {} for pkg in distributions(): name = _norm_name(pkg.name) # type: ignore[attr-defined,unused-ignore] metadata[name] = _merge_fields(pkg.metadata) # type: ignore[arg-type] metadata[name]["spec"] = set() metadata[name]["extras"] = set() metadata[name].setdefault("summary", "") _set_license(metadata[name]) return metadata def _set_license(metadata: PackageMetadata) -> None: license_field = metadata.get("license-expression", metadata.get("license", "")) license_name = license_field if isinstance(license_field, str) else " + ".join(license_field) check_classifiers = license_name in ("UNKNOWN", "Dual License", "") or license_name.count("\n") if check_classifiers: license_names = [] for classifier in metadata["classifier"]: if classifier.startswith("License ::"): license_names.append(classifier.rsplit("::", 1)[1].strip()) license_name = " + ".join(license_names) metadata["license"] = license_name or "?" def _get_deps(base_deps: dict[str, Requirement], metadata: Metadata) -> Metadata: deps = {} for dep_name, dep_req in base_deps.items(): if dep_name not in metadata or dep_name == "mkdocstrings-python-legacy": continue metadata[dep_name]["spec"] |= {str(spec) for spec in dep_req.specifier} # type: ignore[operator] metadata[dep_name]["extras"] |= dep_req.extras # type: ignore[operator] deps[dep_name] = metadata[dep_name] again = True while again: again = False for pkg_name in metadata: if pkg_name in deps: for pkg_dependency in metadata[pkg_name].get("requires-dist", []): requirement = Requirement(pkg_dependency) dep_name = _norm_name(requirement.name) extra_marker = _extra_marker(requirement) if ( dep_name in metadata and dep_name not in deps and dep_name != project["name"] and (not extra_marker or extra_marker in deps[pkg_name]["extras"]) ): metadata[dep_name]["spec"] |= {str(spec) for spec in requirement.specifier} # type: ignore[operator] deps[dep_name] = metadata[dep_name] again = True return deps def _render_credits() -> str: metadata = _get_metadata() dev_dependencies = _get_deps(_requirements(devdeps), metadata) prod_dependencies = _get_deps( _requirements( chain( # type: ignore[arg-type] project.get("dependencies", []), chain(*project.get("optional-dependencies", {}).values()), ), ), metadata, ) template_data = { "project_name": project_name, "prod_dependencies": sorted(prod_dependencies.values(), key=lambda dep: str(dep["name"]).lower()), "dev_dependencies": sorted(dev_dependencies.values(), key=lambda dep: str(dep["name"]).lower()), "more_credits": "http://pawamoy.github.io/credits/", } template_text = dedent( """ # Credits These projects were used to build *{{ project_name }}*. **Thank you!** [Python](https://www.python.org/) | [uv](https://github.com/astral-sh/uv) | [copier-uv](https://github.com/pawamoy/copier-uv) {% macro dep_line(dep) -%} [{{ dep.name }}](https://pypi.org/project/{{ dep.name }}/) | {{ dep.summary }} | {{ ("`" ~ dep.spec|sort(reverse=True)|join(", ") ~ "`") if dep.spec else "" }} | `{{ dep.version }}` | {{ dep.license }} {%- endmacro %} {% if prod_dependencies -%} ### Runtime dependencies Project | Summary | Version (accepted) | Version (last resolved) | License ------- | ------- | ------------------ | ----------------------- | ------- {% for dep in prod_dependencies -%} {{ dep_line(dep) }} {% endfor %} {% endif -%} {% if dev_dependencies -%} ### Development dependencies Project | Summary | Version (accepted) | Version (last resolved) | License ------- | ------- | ------------------ | ----------------------- | ------- {% for dep in dev_dependencies -%} {{ dep_line(dep) }} {% endfor %} {% endif -%} {% if more_credits %}**[More credits from the author]({{ more_credits }})**{% endif %} """, ) jinja_env = SandboxedEnvironment(undefined=StrictUndefined) return jinja_env.from_string(template_text).render(**template_data) print(_render_credits()) mkdocstrings-python-legacy-0.2.6/scripts/get_version.py000066400000000000000000000020061476761050100234010ustar00rootroot00000000000000# Get current project version from Git tags or changelog. import re from contextlib import suppress from pathlib import Path from pdm.backend.hooks.version import SCMVersion, Version, default_version_formatter, get_version_from_scm _root = Path(__file__).parent.parent _changelog = _root / "CHANGELOG.md" _changelog_version_re = re.compile(r"^## \[(\d+\.\d+\.\d+)\].*$") _default_scm_version = SCMVersion(Version("0.0.0"), None, False, None, None) # noqa: FBT003 def get_version() -> str: scm_version = get_version_from_scm(_root) or _default_scm_version if scm_version.version <= Version("0.1"): # Missing Git tags? with suppress(OSError, StopIteration): # noqa: SIM117 with _changelog.open("r", encoding="utf8") as file: match = next(filter(None, map(_changelog_version_re.match, file))) scm_version = scm_version._replace(version=Version(match.group(1))) return default_version_formatter(scm_version) if __name__ == "__main__": print(get_version()) mkdocstrings-python-legacy-0.2.6/scripts/make000077700000000000000000000000001476761050100226272make.pyustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/scripts/make.py000077500000000000000000000141721476761050100220040ustar00rootroot00000000000000#!/usr/bin/env python3 from __future__ import annotations import os import shutil import subprocess import sys from contextlib import contextmanager from pathlib import Path from textwrap import dedent from typing import TYPE_CHECKING, Any if TYPE_CHECKING: from collections.abc import Iterator PYTHON_VERSIONS = os.getenv("PYTHON_VERSIONS", "3.9 3.10 3.11 3.12 3.13 3.14").split() def shell(cmd: str, *, capture_output: bool = False, **kwargs: Any) -> str | None: """Run a shell command.""" if capture_output: return subprocess.check_output(cmd, shell=True, text=True, **kwargs) # noqa: S602 subprocess.run(cmd, shell=True, check=True, stderr=subprocess.STDOUT, **kwargs) # noqa: S602 return None @contextmanager def environ(**kwargs: str) -> Iterator[None]: """Temporarily set environment variables.""" original = dict(os.environ) os.environ.update(kwargs) try: yield finally: os.environ.clear() os.environ.update(original) def uv_install(venv: Path) -> None: """Install dependencies using uv.""" with environ(UV_PROJECT_ENVIRONMENT=str(venv), PYO3_USE_ABI3_FORWARD_COMPATIBILITY="1"): if "CI" in os.environ: shell("uv sync --no-editable") else: shell("uv sync") def setup() -> None: """Setup the project.""" if not shutil.which("uv"): raise ValueError("make: setup: uv must be installed, see https://github.com/astral-sh/uv") print("Installing dependencies (default environment)") default_venv = Path(".venv") if not default_venv.exists(): shell("uv venv") uv_install(default_venv) if PYTHON_VERSIONS: for version in PYTHON_VERSIONS: print(f"\nInstalling dependencies (python{version})") venv_path = Path(f".venvs/{version}") if not venv_path.exists(): shell(f"uv venv --python {version} {venv_path}") with environ(UV_PROJECT_ENVIRONMENT=str(venv_path.resolve())): uv_install(venv_path) def run(version: str, cmd: str, *args: str, **kwargs: Any) -> None: """Run a command in a virtual environment.""" kwargs = {"check": True, **kwargs} uv_run = ["uv", "run", "--no-sync"] if version == "default": with environ(UV_PROJECT_ENVIRONMENT=".venv"): subprocess.run([*uv_run, cmd, *args], **kwargs) # noqa: S603, PLW1510 else: with environ(UV_PROJECT_ENVIRONMENT=f".venvs/{version}", MULTIRUN="1"): subprocess.run([*uv_run, cmd, *args], **kwargs) # noqa: S603, PLW1510 def multirun(cmd: str, *args: str, **kwargs: Any) -> None: """Run a command for all configured Python versions.""" if PYTHON_VERSIONS: for version in PYTHON_VERSIONS: run(version, cmd, *args, **kwargs) else: run("default", cmd, *args, **kwargs) def allrun(cmd: str, *args: str, **kwargs: Any) -> None: """Run a command in all virtual environments.""" run("default", cmd, *args, **kwargs) if PYTHON_VERSIONS: multirun(cmd, *args, **kwargs) def clean() -> None: """Delete build artifacts and cache files.""" paths_to_clean = ["build", "dist", "htmlcov", "site", ".coverage*", ".pdm-build"] for path in paths_to_clean: shutil.rmtree(path, ignore_errors=True) cache_dirs = {".cache", ".pytest_cache", ".mypy_cache", ".ruff_cache", "__pycache__"} for dirpath in Path(".").rglob("*/"): if dirpath.parts[0] not in (".venv", ".venvs") and dirpath.name in cache_dirs: shutil.rmtree(dirpath, ignore_errors=True) def vscode() -> None: """Configure VSCode to work on this project.""" shutil.copytree("config/vscode", ".vscode", dirs_exist_ok=True) def main() -> int: """Main entry point.""" args = list(sys.argv[1:]) if not args or args[0] == "help": if len(args) > 1: run("default", "duty", "--help", args[1]) else: print( dedent( """ Available commands help Print this help. Add task name to print help. setup Setup all virtual environments (install dependencies). run Run a command in the default virtual environment. multirun Run a command for all configured Python versions. allrun Run a command in all virtual environments. 3.x Run a command in the virtual environment for Python 3.x. clean Delete build artifacts and cache files. vscode Configure VSCode to work on this project. """, ), flush=True, ) if os.path.exists(".venv"): print("\nAvailable tasks", flush=True) run("default", "duty", "--list") return 0 while args: cmd = args.pop(0) if cmd == "run": run("default", *args) return 0 if cmd == "multirun": multirun(*args) return 0 if cmd == "allrun": allrun(*args) return 0 if cmd.startswith("3."): run(cmd, *args) return 0 opts = [] while args and (args[0].startswith("-") or "=" in args[0]): opts.append(args.pop(0)) if cmd == "clean": clean() elif cmd == "setup": setup() elif cmd == "vscode": vscode() elif cmd == "check": multirun("duty", "check-quality", "check-types", "check-docs") run("default", "duty", "check-api") elif cmd in {"check-quality", "check-docs", "check-types", "test"}: multirun("duty", cmd, *opts) else: run("default", "duty", cmd, *opts) return 0 if __name__ == "__main__": try: sys.exit(main()) except subprocess.CalledProcessError as process: if process.output: print(process.output, file=sys.stderr) sys.exit(process.returncode) mkdocstrings-python-legacy-0.2.6/src/000077500000000000000000000000001476761050100176055ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/000077500000000000000000000000001476761050100241745ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/_internal/000077500000000000000000000000001476761050100261475ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/_internal/__init__.py000066400000000000000000000000001476761050100302460ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/_internal/debug.py000066400000000000000000000054621476761050100276160ustar00rootroot00000000000000from __future__ import annotations import os import platform import sys from dataclasses import dataclass from importlib import metadata @dataclass class _Variable: """Dataclass describing an environment variable.""" name: str """Variable name.""" value: str """Variable value.""" @dataclass class _Package: """Dataclass describing a Python package.""" name: str """Package name.""" version: str """Package version.""" @dataclass class _Environment: """Dataclass to store environment information.""" interpreter_name: str """Python interpreter name.""" interpreter_version: str """Python interpreter version.""" interpreter_path: str """Path to Python executable.""" platform: str """Operating System.""" packages: list[_Package] """Installed packages.""" variables: list[_Variable] """Environment variables.""" def _interpreter_name_version() -> tuple[str, str]: if hasattr(sys, "implementation"): impl = sys.implementation.version version = f"{impl.major}.{impl.minor}.{impl.micro}" kind = impl.releaselevel if kind != "final": version += kind[0] + str(impl.serial) return sys.implementation.name, version return "", "0.0.0" def _get_version(dist: str = "mkdocstrings-python-legacy") -> str: """Get version of the given distribution. Parameters: dist: A distribution name. Returns: A version number. """ try: return metadata.version(dist) except metadata.PackageNotFoundError: return "0.0.0" def _get_debug_info() -> _Environment: """Get debug/environment information. Returns: Environment information. """ py_name, py_version = _interpreter_name_version() packages = ["mkdocstrings-python-legacy"] variables = ["PYTHONPATH", *[var for var in os.environ if var.startswith("MKDOCSTRINGS_PYTHON_LEGACY")]] return _Environment( interpreter_name=py_name, interpreter_version=py_version, interpreter_path=sys.executable, platform=platform.platform(), variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))], packages=[_Package(pkg, _get_version(pkg)) for pkg in packages], ) def _print_debug_info() -> None: """Print debug/environment information.""" info = _get_debug_info() print(f"- __System__: {info.platform}") print(f"- __Python__: {info.interpreter_name} {info.interpreter_version} ({info.interpreter_path})") print("- __Environment variables__:") for var in info.variables: print(f" - `{var.name}`: `{var.value}`") print("- __Installed packages__:") for pkg in info.packages: print(f" - `{pkg.name}` v{pkg.version}") if __name__ == "__main__": _print_debug_info() mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/py.typed000066400000000000000000000000001476761050100256610ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/000077500000000000000000000000001476761050100255155ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/__init__.py000066400000000000000000000002321476761050100276230ustar00rootroot00000000000000"""This package implements a handler for the Python language.""" from mkdocstrings_handlers.python.handler import get_handler __all__ = ["get_handler"] mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/debug.py000066400000000000000000000052311476761050100271560ustar00rootroot00000000000000"""Debugging utilities.""" import os import platform import sys from dataclasses import dataclass from importlib import metadata @dataclass class Variable: """Dataclass describing an environment variable.""" name: str """Variable name.""" value: str """Variable value.""" @dataclass class Package: """Dataclass describing a Python package.""" name: str """Package name.""" version: str """Package version.""" @dataclass class Environment: """Dataclass to store environment information.""" interpreter_name: str """Python interpreter name.""" interpreter_version: str """Python interpreter version.""" platform: str """Operating System.""" packages: list[Package] """Installed packages.""" variables: list[Variable] """Environment variables.""" def _interpreter_name_version() -> tuple[str, str]: if hasattr(sys, "implementation"): impl = sys.implementation.version version = f"{impl.major}.{impl.minor}.{impl.micro}" kind = impl.releaselevel if kind != "final": version += kind[0] + str(impl.serial) return sys.implementation.name, version return "", "0.0.0" def get_version(dist: str = "mkdocstrings-python-legacy") -> str: """Get version of the given distribution. Parameters: dist: A distribution name. Returns: A version number. """ try: return metadata.version(dist) except metadata.PackageNotFoundError: return "0.0.0" def get_debug_info() -> Environment: """Get debug/environment information. Returns: Environment information. """ py_name, py_version = _interpreter_name_version() packages = ["mkdocstrings-python-legacy"] variables = ["PYTHONPATH", *[var for var in os.environ if var.startswith("MKDOCSTRINGS_PYTHON_LEGACY")]] return Environment( interpreter_name=py_name, interpreter_version=py_version, platform=platform.platform(), variables=[Variable(var, val) for var in variables if (val := os.getenv(var))], packages=[Package(pkg, get_version(pkg)) for pkg in packages], ) def print_debug_info() -> None: """Print debug/environment information.""" info = get_debug_info() print(f"- __System__: {info.platform}") print(f"- __Python__: {info.interpreter_name} {info.interpreter_version}") print("- __Environment variables__:") for var in info.variables: print(f" - `{var.name}`: `{var.value}`") print("- __Installed packages__:") for pkg in info.packages: print(f" - `{pkg.name}` v{pkg.version}") if __name__ == "__main__": print_debug_info() mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/handler.py000066400000000000000000000360251476761050100275120ustar00rootroot00000000000000"""This module implements a handler for the Python language. It collects data with [`pytkdocs`](https://github.com/pawamoy/pytkdocs). """ import json import os import posixpath import sys import traceback from collections.abc import Iterator, Mapping, MutableMapping from copy import deepcopy from pathlib import Path from subprocess import PIPE, Popen from typing import Any, BinaryIO, ClassVar, Optional from mkdocs.config.defaults import MkDocsConfig from mkdocs.exceptions import PluginError from mkdocstrings import BaseHandler, CollectionError, CollectorItem, Inventory, get_logger from mkdocstrings_handlers.python.rendering import ( do_brief_xref, rebuild_category_lists, sort_key_alphabetical, sort_key_source, sort_object, ) # TODO: add a deprecation warning once the new handler handles 95% of use-cases logger = get_logger(__name__) class PythonHandler(BaseHandler): """The Python handler class.""" name: ClassVar[str] = "python" """The handler name.""" domain: ClassVar[str] = "py" # to match Sphinx's default domain """The domain of the handler.""" enable_inventory: ClassVar[bool] = True """Whether the handler supports inventory files.""" fallback_theme: ClassVar[str] = "material" """The fallback theme to use when the user-selected theme is not supported.""" fallback_config: ClassVar[dict] = {"docstring_style": "markdown", "filters": ["!.*"]} """The configuration used when falling back to re-collecting an object to get its anchor. This configuration is used in [`Handlers.get_anchors`][mkdocstrings.Handlers.get_anchors]. When trying to fix (optional) cross-references, the autorefs plugin will try to collect an object with every configured handler until one succeeds. It will then try to get an anchor for it. It's because objects can have multiple identifiers (aliases), for example their definition path and multiple import paths in Python. When re-collecting the object, we have no use for its members, or for its docstring being parsed. This is why the fallback configuration filters every member out, and uses the Markdown style, which we know will not generate any warnings. """ default_config: ClassVar[dict] = { "filters": ["!^_[^_]"], "show_root_heading": False, "show_root_toc_entry": True, "show_root_full_path": True, "show_root_members_full_path": False, "show_object_full_path": False, "show_category_heading": False, "show_if_no_docstring": False, "show_signature": True, "show_signature_annotations": False, "show_source": True, "show_bases": True, "group_by_category": True, "heading_level": 2, "members_order": "alphabetical", } """ **Headings options:** - `heading_level` (`int`): The initial heading level to use. Default: `2`. - `show_root_heading` (`bool`): Show the heading of the object at the root of the documentation tree (i.e. the object referenced by the identifier after `:::`). Default: `False`. - `show_root_toc_entry` (`bool`): If the root heading is not shown, at least add a ToC entry for it. Default: `True`. - `show_root_full_path` (`bool`): Show the full Python path for the root object heading. Default: `True`. - `show_root_members_full_path` (`bool`): Show the full Python path of the root members. Default: `False`. - `show_object_full_path` (`bool`): Show the full Python path of every object. Default: `False`. - `show_category_heading` (`bool`): When grouped by categories, show a heading for each category. Default: `False`. **Members options:** - `members` (`list[str] | False | None`): An explicit list of members to render. Default: `None`. - `members_order` (`str`): The members ordering to use. Options: `alphabetical` - order by the members names, `source` - order members as they appear in the source file. Default: `"alphabetical"`. - `filters` (`list[str] | None`): A list of filters applied to filter objects based on their name. A filter starting with `!` will exclude matching objects instead of including them. The `members` option takes precedence over `filters` (filters will still be applied recursively to lower members in the hierarchy). Default: `["!^_[^_]"]`. - `group_by_category` (`bool`): Group the object's children by categories: attributes, classes, functions, and modules. Default: `True`. **Docstrings options:** - `docstring_style` (`str`): The docstring style to use: `google`, `numpy`, `restructured-text`, or `None`. Default: `"google"`. - `docstring_options` (`dict`): The options for the docstring parser. See parsers under [`pytkdocs.parsers.docstrings`][]. - `show_if_no_docstring` (`bool`): Show the object heading even if it has no docstring or children with docstrings. Default: `False`. **Signatures/annotations options:** - `show_signature` (`bool`): Show methods and functions signatures. Default: `True`. - `show_signature_annotations` (`bool`): Show the type annotations in methods and functions signatures. Default: `False`. **Additional options:** - `show_bases` (`bool`): Show the base classes of a class. Default: `True`. - `show_source` (`bool`): Show the source code of this object. Default: `True`. """ def __init__(self, config: dict[str, Any], base_dir: Path, **kwargs: Any) -> None: """Initialize the handler. When instantiating a Python handler, we open a `pytkdocs` subprocess in the background with `subprocess.Popen`. It will allow us to feed input to and read output from this subprocess, keeping it alive during the whole documentation generation. Spawning a new Python subprocess for each "autodoc" instruction would be too resource intensive, and would slow down `mkdocstrings` a lot. Parameters: config: The handler configuration. base_dir: The base directory of the project. **kwargs: Arguments passed to the parent constructor. """ super().__init__(**kwargs) self.base_dir = base_dir self.config = config self.global_options = config.get("options", {}) logger.debug("Opening 'pytkdocs' subprocess") env = os.environ.copy() env["PYTHONUNBUFFERED"] = "1" paths = config.get("paths") or [] if not paths and self.base_dir: paths.append(self.base_dir) search_paths = [] for path in paths: if not os.path.isabs(path) and self.base_dir: path = os.path.abspath(os.path.join(self.base_dir, path)) # noqa: PLW2901 if path not in search_paths: search_paths.append(path) self._paths = search_paths commands = [] if search_paths: commands.extend([f"sys.path.insert(0, {path!r})" for path in reversed(search_paths)]) if setup_commands := config.get("setup_commands"): # prevent the Python interpreter or the setup commands # from writing to stdout as it would break pytkdocs output commands.extend( [ "from io import StringIO", "sys.stdout = StringIO()", # redirect stdout to memory buffer *setup_commands, "sys.stdout.flush()", "sys.stdout = sys.__stdout__", # restore stdout ], ) if commands: final_commands = [ "import sys", *commands, "from pytkdocs.cli import main as pytkdocs", "pytkdocs(['--line-by-line'])", ] cmd = [sys.executable, "-c", "; ".join(final_commands)] else: cmd = [sys.executable, "-m", "pytkdocs", "--line-by-line"] self.process = Popen( # noqa: S603 cmd, universal_newlines=True, stdout=PIPE, stdin=PIPE, bufsize=-1, env=env, ) def get_inventory_urls(self) -> list[tuple[str, dict[str, Any]]]: """Return the URLs of the inventory files to download.""" return [ (inv.pop("url"), inv) if isinstance(inv, dict) else (inv, {}) for inv in deepcopy(self.config.get("import", [])) ] @classmethod def load_inventory( cls, in_file: BinaryIO, url: str, base_url: Optional[str] = None, **kwargs: Any, # noqa: ARG003 ) -> Iterator[tuple[str, str]]: """Yield items and their URLs from an inventory file streamed from `in_file`. This implements mkdocstrings' `load_inventory` "protocol" (see plugin.py). Arguments: in_file: The binary file-like object to read the inventory from. url: The URL that this file is being streamed from (used to guess `base_url`). base_url: The URL that this inventory's sub-paths are relative to. **kwargs: Ignore additional arguments passed from the config. Yields: Tuples of (item identifier, item URL). """ if base_url is None: base_url = posixpath.dirname(url) for item in Inventory.parse_sphinx(in_file, domain_filter=("py",)).values(): yield item.name, posixpath.join(base_url, item.uri) def get_options(self, local_options: Mapping[str, Any]) -> MutableMapping[str, Any]: """Return the options to use to collect an object. We merge the global options with the options specific to the object being collected. Arguments: local_options: The selection options. Returns: The options to use to collect an object. """ return {**self.default_config, **self.global_options, **local_options} def collect(self, identifier: str, options: MutableMapping[str, Any]) -> CollectorItem: """Collect the documentation tree given an identifier and selection options. In this method, we feed one line of JSON to the standard input of the subprocess that was opened during instantiation of the collector. Then we read one line of JSON on its standard output. We load back the JSON text into a Python dictionary. If there is a decoding error, we log it as error and raise a CollectionError. If the dictionary contains an `error` key, we log it as error (with the optional `traceback` value), and raise a CollectionError. If the dictionary values for keys `loading_errors` and `parsing_errors` are not empty, we log them as warnings. Then we pick up the only object within the `objects` list (there's always only one, because we collect them one by one), rebuild it's categories lists (see [`rebuild_category_lists()`][mkdocstrings_handlers.python.rendering.rebuild_category_lists]), and return it. Arguments: identifier: The dotted-path of a Python object available in the Python path. options: Selection options, used to alter the data collection done by `pytkdocs`. Raises: CollectionError: When there was a problem collecting the object documentation. Returns: The collected object-tree. """ pytkdocs_options = {} for option in ("filters", "members", "docstring_style", "docstring_options"): if option in options: pytkdocs_options[option] = options[option] logger.debug("Preparing input") json_input = json.dumps({"objects": [{"path": identifier, **pytkdocs_options}]}) logger.debug("Writing to process' stdin") self.process.stdin.write(json_input + "\n") # type: ignore[union-attr] self.process.stdin.flush() # type: ignore[union-attr] logger.debug("Reading process' stdout") stdout = self.process.stdout.readline() # type: ignore[union-attr] logger.debug("Loading JSON output as Python object") try: result = json.loads(stdout) except json.decoder.JSONDecodeError as exception: error = "\n".join(("Error while loading JSON:", stdout, traceback.format_exc())) raise CollectionError(error) from exception if "error" in result: error = result["error"] if "traceback" in result: error += f"\n{result['traceback']}" raise CollectionError(error) for loading_error in result["loading_errors"]: logger.warning(loading_error) for errors in result["parsing_errors"].values(): for parsing_error in errors: logger.warning(parsing_error) # We always collect only one object at a time result = result["objects"][0] logger.debug("Rebuilding categories and children lists") rebuild_category_lists(result) return result def teardown(self) -> None: """Terminate the opened subprocess, set it to `None`.""" logger.debug("Tearing process down") self.process.terminate() def render(self, data: CollectorItem, options: MutableMapping[str, Any]) -> str: """Render the collected data into HTML.""" template = self.env.get_template(f"{data['category']}.html") # Heading level is a "state" variable, that will change at each step # of the rendering recursion. Therefore, it's easier to use it as a plain value # than as an item in a dictionary. heading_level = options["heading_level"] members_order = options["members_order"] if members_order == "alphabetical": sort_function = sort_key_alphabetical elif members_order == "source": sort_function = sort_key_source else: raise PluginError(f"Unknown members_order '{members_order}', choose between 'alphabetical' and 'source'.") sort_object(data, sort_function=sort_function) return template.render( **{"config": options, data["category"]: data, "heading_level": heading_level, "root": True}, ) def get_aliases(self, identifier: str) -> tuple[str, ...]: """Return the aliases of an identifier.""" try: data = self.collect(identifier, self.fallback_config) return (data["path"],) except (CollectionError, KeyError): return () def update_env(self, config: dict) -> None: # noqa: ARG002,D102 self.env.trim_blocks = True self.env.lstrip_blocks = True self.env.keep_trailing_newline = False self.env.filters["brief_xref"] = do_brief_xref def get_handler( handler_config: MutableMapping[str, Any], tool_config: MkDocsConfig, **kwargs: Any, ) -> PythonHandler: """Simply return an instance of `PythonHandler`. Arguments: handler_config: The handler configuration. tool_config: The tool (SSG) configuration. Returns: An instance of `PythonHandler`. """ base_dir = Path(tool_config.config_file_path or "./mkdocs.yml").parent return PythonHandler(config=dict(handler_config), base_dir=base_dir, **kwargs) mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/rendering.py000066400000000000000000000063311476761050100300470ustar00rootroot00000000000000"""This module implements rendering utilities.""" import sys from typing import Any, Callable from markupsafe import Markup from mkdocstrings import CollectorItem, get_logger log = get_logger(__name__) def do_brief_xref(path: str) -> Markup: """Filter to create cross-reference with brief text and full identifier as hover text. Arguments: path: The path to shorten and render. Returns: A span containing the brief cross-reference and the full one on hover. """ brief = path.split(".")[-1] return Markup("{brief}").format(path=path, brief=brief) def sort_object(obj: CollectorItem, sort_function: Callable[[CollectorItem], Any]) -> None: """Sort the collected object's children. Sorts the object's children list, then each category separately, and then recurses into each. Arguments: obj: The collected object, as a dict. Note that this argument is mutated. sort_function: The sort key function used to determine the order of elements. """ obj["children"].sort(key=sort_function) for category in ("attributes", "classes", "functions", "methods", "modules"): obj[category].sort(key=sort_function) for child in obj["children"]: sort_object(child, sort_function=sort_function) def sort_key_alphabetical(item: CollectorItem) -> Any: """Return an item's name or the final unicode character. Arguments: item: A collected item. Returns: Name or final unicode character. """ # chr(sys.maxunicode) is a string that contains the final unicode # character, so if 'name' isn't found on the object, the item will go to # the end of the list. return item.get("name", chr(sys.maxunicode)) def sort_key_source(item: CollectorItem) -> Any: """Return an item's starting line number or -1. Arguments: item: A collected item. Returns: Starting line number or -1. """ # if 'line_start' isn't found on the object, the item will go to # the start of the list. return item.get("source", {}).get("line_start", -1) def rebuild_category_lists(obj: dict) -> None: """Recursively rebuild the category lists of a collected object. Since `pytkdocs` dumps JSON on standard output, it must serialize the object-tree and flatten it to reduce data duplication and avoid cycle-references. Indeed, each node of the object-tree has a `children` list, containing all children, and another list for each category of children: `attributes`, `classes`, `functions`, `methods` and `modules`. It replaces the values in category lists with only the paths of the objects. Here, we reconstruct these category lists by picking objects in the `children` list using their path. For each object, we recurse on every one of its children. Arguments: obj: The collected object, loaded back from JSON into a Python dictionary. """ for category in ("attributes", "classes", "functions", "methods", "modules"): obj[category] = [obj["children"][path] for path in obj[category]] obj["children"] = [child for _, child in obj["children"].items()] for child in obj["children"]: rebuild_category_lists(child) mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/000077500000000000000000000000001476761050100275135ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/000077500000000000000000000000001476761050100313115ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/attribute.html000066400000000000000000000034151476761050100342050ustar00rootroot00000000000000{{ log.debug() }} {% if config.show_if_no_docstring or attribute.has_contents %}
    {% with html_id = attribute.path %} {% if not root or config.show_root_heading %} {% if root %} {% set show_full_path = config.show_root_full_path %} {% set root_members = True %} {% elif root_members %} {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %} {% set root_members = False %} {% else %} {% set show_full_path = config.show_object_full_path %} {% endif %} {% filter heading(heading_level, role="data" if obj == module else "attr", id=html_id, class="doc doc-heading", toc_label=attribute.name) %} {% filter highlight(language="python", inline=True) %} {% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %} {% if attribute.type %}: {{ attribute.type }}{% endif %} {% endfilter %} {% with properties = attribute.properties %} {% include "properties.html" with context %} {% endwith %} {% endfilter %} {% else %} {% if config.show_root_toc_entry %} {% filter heading(heading_level, role="data" if obj == module else "attr", id=html_id, toc_label=attribute.path, hidden=True) %} {% endfilter %} {% endif %} {% set heading_level = heading_level - 1 %} {% endif %}
    {% with docstring_sections = attribute.docstring_sections %} {% include "docstring.html" with context %} {% endwith %}
    {% endwith %}
    {% endif %} mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/attributes.html000066400000000000000000000007771476761050100344000ustar00rootroot00000000000000{{ log.debug() }}

    Attributes:

    {% for attribute in attributes %} {% endfor %}
    Name Type Description
    {{ attribute.name }} {% if attribute.annotation %}{{ attribute.annotation }}{% endif %} {{ attribute.description|convert_markdown(heading_level, html_id) }}
    mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/children.html000066400000000000000000000063161476761050100337750ustar00rootroot00000000000000{{ log.debug() }} {% if obj.children %}
    {% if config.group_by_category %} {% with %} {% if config.show_category_heading %} {% set extra_level = 1 %} {% else %} {% set extra_level = 0 %} {% endif %} {% if config.show_category_heading and obj.attributes|any("has_contents") %} {% filter heading(heading_level, id=html_id ~ "-attributes") %}Attributes{% endfilter %} {% endif %} {% with heading_level = heading_level + extra_level %} {% for attribute in obj.attributes %} {% include "attribute.html" with context %} {% endfor %} {% endwith %} {% if config.show_category_heading and obj.classes|any("has_contents") %} {% filter heading(heading_level, id=html_id ~ "-classes") %}Classes{% endfilter %} {% endif %} {% with heading_level = heading_level + extra_level %} {% for class in obj.classes %} {% include "class.html" with context %} {% endfor %} {% endwith %} {% if config.show_category_heading and obj.functions|any("has_contents") %} {% filter heading(heading_level, id=html_id ~ "-functions") %}Functions{% endfilter %} {% endif %} {% with heading_level = heading_level + extra_level %} {% for function in obj.functions %} {% include "function.html" with context %} {% endfor %} {% endwith %} {% if config.show_category_heading and obj.methods|any("has_contents") %} {% filter heading(heading_level, id=html_id ~ "-methods") %}Methods{% endfilter %} {% endif %} {% with heading_level = heading_level + extra_level %} {% for method in obj.methods %} {% include "method.html" with context %} {% endfor %} {% endwith %} {% if config.show_category_heading and obj.modules|any("has_contents") %} {% filter heading(heading_level, id=html_id ~ "-modules") %}Modules{% endfilter %} {% endif %} {% with heading_level = heading_level + extra_level %} {% for module in obj.modules %} {% include "module.html" with context %} {% endfor %} {% endwith %} {% endwith %} {% else %} {% for child in obj.children %} {% if child.category == "attribute" %} {% with attribute = child %} {% include "attribute.html" with context %} {% endwith %} {% elif child.category == "class" %} {% with class = child %} {% include "class.html" with context %} {% endwith %} {% elif child.category == "function" %} {% with function = child %} {% include "function.html" with context %} {% endwith %} {% elif child.category == "method" %} {% with method = child %} {% include "method.html" with context %} {% endwith %} {% elif child.category == "module" %} {% with module = child %} {% include "module.html" with context %} {% endwith %} {% endif %} {% endfor %} {% endif %}
    {% endif %} mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/class.html000066400000000000000000000044521476761050100333110ustar00rootroot00000000000000{{ log.debug() }} {% if config.show_if_no_docstring or class.has_contents %}
    {% with html_id = class.path %} {% if not root or config.show_root_heading %} {% if root %} {% set show_full_path = config.show_root_full_path %} {% set root_members = True %} {% elif root_members %} {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %} {% set root_members = False %} {% else %} {% set show_full_path = config.show_object_full_path %} {% endif %} {% filter heading(heading_level, role="class", id=html_id, class="doc doc-heading", toc_label=class.name) %} {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} {% if config.show_bases and class.bases and class.bases != ['object'] %} ({% for base in class.bases -%} {{ base|brief_xref() }}{% if not loop.last %}, {% endif %} {% endfor %}) {% endif %} {% with properties = class.properties %} {% include "properties.html" with context %} {% endwith %} {% endfilter %} {% else %} {% if config.show_root_toc_entry %} {% filter heading(heading_level, role="class", id=html_id, toc_label=class.path, hidden=True) %} {% endfilter %} {% endif %} {% set heading_level = heading_level - 1 %} {% endif %}
    {% with docstring_sections = class.docstring_sections %} {% include "docstring.html" with context %} {% endwith %} {% if config.show_source and class.source %}
    Source code in {{ class.relative_file_path }} {{ class.source.code|highlight(language="python", linestart=class.source.line_start, linenums=False) }}
    {% endif %} {% with obj = class %} {% set root = False %} {% set heading_level = heading_level + 1 %} {% include "children.html" with context %} {% endwith %}
    {% endwith %}
    {% endif %} mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/docstring.html000066400000000000000000000024641476761050100342010ustar00rootroot00000000000000{{ log.debug() }} {% if docstring_sections %} {% for section in docstring_sections %} {% if section.type == "markdown" %} {{ section.value|convert_markdown(heading_level, html_id) }} {% elif section.type == "attributes" %} {% with attributes = section.value %} {% include "attributes.html" with context %} {% endwith %} {% elif section.type == "parameters" %} {% with parameters = section.value %} {% include "parameters.html" with context %} {% endwith %} {% elif section.type == "keyword_args" %} {% with kwargs = section.value %} {% include "keyword_args.html" with context %} {% endwith %} {% elif section.type == "exceptions" %} {% with exceptions = section.value %} {% include "exceptions.html" with context %} {% endwith %} {% elif section.type == "yield" %} {% with yield = section.value %} {% include "yield.html" with context %} {% endwith %} {% elif section.type == "return" %} {% with return = section.value %} {% include "return.html" with context %} {% endwith %} {% elif section.type == "examples" %} {% with examples = section.value %} {% include "examples.html" with context %} {% endwith %} {% endif %} {% endfor %} {% endif %} mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/examples.html000066400000000000000000000005171476761050100340200ustar00rootroot00000000000000{{ log.debug() }}

    Examples:

    {% for section_type, sub_section in examples %} {% if section_type == "markdown" %} {{ sub_section|convert_markdown(heading_level, html_id) }} {% elif section_type == "examples" %} {{ sub_section|highlight(language="python", linenums=False) }} {% endif %} {% endfor %} mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/exceptions.html000066400000000000000000000006201476761050100343560ustar00rootroot00000000000000{{ log.debug() }}

    Exceptions:

    {% for exception in exceptions %} {% endfor %}
    Type Description
    {{ exception.annotation }} {{ exception.description|convert_markdown(heading_level, html_id) }}
    mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/function.html000066400000000000000000000041211476761050100340220ustar00rootroot00000000000000{{ log.debug() }} {% if config.show_if_no_docstring or function.has_contents %}
    {% with html_id = function.path %} {% if not root or config.show_root_heading %} {% if root %} {% set show_full_path = config.show_root_full_path %} {% set root_members = True %} {% elif root_members %} {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %} {% set root_members = False %} {% else %} {% set show_full_path = config.show_object_full_path %} {% endif %} {% filter heading(heading_level, role="function", id=html_id, class="doc doc-heading", toc_label=function.name ~ "()") %} {% filter highlight(language="python", inline=True) %} {% if show_full_path %}{{ function.path }}{% else %}{{ function.name }}{% endif %} {% with signature = function.signature %}{% include "signature.html" with context %}{% endwith %} {% endfilter %} {% with properties = function.properties %} {% include "properties.html" with context %} {% endwith %} {% endfilter %} {% else %} {% if config.show_root_toc_entry %} {% filter heading(heading_level, role="function", id=html_id, toc_label=function.path, hidden=True) %} {% endfilter %} {% endif %} {% set heading_level = heading_level - 1 %} {% endif %}
    {% with docstring_sections = function.docstring_sections %} {% include "docstring.html" with context %} {% endwith %} {% if config.show_source and function.source %}
    Source code in {{ function.relative_file_path }} {{ function.source.code|highlight(language="python", linestart=function.source.line_start, linenums=False) }}
    {% endif %}
    {% endwith %}
    {% endif %} keyword_args.html000066400000000000000000000007561476761050100346300ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material{{ log.debug() }}

    Keyword arguments:

    {% for kwarg in kwargs %} {% endfor %}
    Name Type Description
    {{ kwarg.name }} {% if kwarg.annotation %}{{ kwarg.annotation }}{% endif %} {{ kwarg.description|convert_markdown(heading_level, html_id) }}
    mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/method.html000066400000000000000000000040611476761050100334600ustar00rootroot00000000000000{{ log.debug() }} {% if config.show_if_no_docstring or method.has_contents %}
    {% with html_id = method.path %} {% if not root or config.show_root_heading %} {% if root %} {% set show_full_path = config.show_root_full_path %} {% set root_members = True %} {% elif root_members %} {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %} {% set root_members = False %} {% else %} {% set show_full_path = config.show_object_full_path %} {% endif %} {% filter heading(heading_level, role="method", id=html_id, class="doc doc-heading", toc_label=method.name ~ "()") %} {% filter highlight(language="python", inline=True) %} {% if show_full_path %}{{ method.path }}{% else %}{{ method.name }}{% endif %} {% with signature = method.signature %}{% include "signature.html" with context %}{% endwith %} {% endfilter %} {% with properties = method.properties %} {% include "properties.html" with context %} {% endwith %} {% endfilter %} {% else %} {% if config.show_root_toc_entry %} {% filter heading(heading_level, role="method", id=html_id, toc_label=method.path, hidden=True) %} {% endfilter %} {% endif %} {% set heading_level = heading_level - 1 %} {% endif %}
    {% with docstring_sections = method.docstring_sections %} {% include "docstring.html" with context %} {% endwith %} {% if config.show_source and method.source %}
    Source code in {{ method.relative_file_path }} {{ method.source.code|highlight(language="python", linestart=method.source.line_start, linenums=False) }}
    {% endif %}
    {% endwith %}
    {% endif %} mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/module.html000066400000000000000000000033461476761050100334720ustar00rootroot00000000000000{{ log.debug() }} {% if config.show_if_no_docstring or module.has_contents %}
    {% with html_id = module.path %} {% if not root or config.show_root_heading %} {% if root %} {% set show_full_path = config.show_root_full_path %} {% set root_members = True %} {% elif root_members %} {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %} {% set root_members = False %} {% else %} {% set show_full_path = config.show_object_full_path %} {% endif %} {% filter heading(heading_level, role="module", id=html_id, class="doc doc-heading", toc_label=module.name) %} {% if show_full_path %}{{ module.path }}{% else %}{{ module.name }}{% endif %} {% with properties = module.properties %} {% include "properties.html" with context %} {% endwith %} {% endfilter %} {% else %} {% if config.show_root_toc_entry %} {% filter heading(heading_level, role="module", id=html_id, toc_label=module.path, hidden=True) %} {% endfilter %} {% endif %} {% set heading_level = heading_level - 1 %} {% endif %}
    {% with docstring_sections = module.docstring_sections %} {% include "docstring.html" with context %} {% endwith %} {% with obj = module %} {% set root = False %} {% set heading_level = heading_level + 1 %} {% include "children.html" with context %} {% endwith %}
    {% endwith %}
    {% endif %} mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/parameters.html000066400000000000000000000012141476761050100343400ustar00rootroot00000000000000{{ log.debug() }}

    Parameters:

    {% for parameter in parameters %} {% endfor %}
    Name Type Description Default
    {{ parameter.name }} {% if parameter.annotation %}{{ parameter.annotation }}{% endif %} {{ parameter.description|convert_markdown(heading_level, html_id) }} {% if parameter.default %}{{ parameter.default }}{% else %}required{% endif %}
    mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/properties.html000066400000000000000000000003741476761050100343770ustar00rootroot00000000000000{{ log.debug() }} {% if properties %} {% for property in properties %} {{ property }} {% endfor %} {% endif %} mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/return.html000066400000000000000000000005551476761050100335230ustar00rootroot00000000000000{{ log.debug() }}

    Returns:

    Type Description
    {% if return.annotation %}{{ return.annotation }}{% endif %} {{ return.description|convert_markdown(heading_level, html_id) }}
    mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/signature.html000066400000000000000000000026261476761050100342060ustar00rootroot00000000000000{{ log.debug() }} {%- if signature and config.show_signature -%} {%- with -%} {%- set ns = namespace(render_pos_only_separator=True, render_kw_only_separator=True, equal="=") -%} {%- if config.show_signature_annotations -%} {%- set ns.equal = " = " -%} {%- endif -%} ({%- for parameter in signature.parameters %}{% if parameter.kind == "POSITIONAL_ONLY" -%} {%- if ns.render_pos_only_separator -%} {%- set ns.render_pos_only_separator = False %}/, {% endif -%} {%- elif parameter.kind == "KEYWORD_ONLY" -%} {%- if ns.render_kw_only_separator -%} {%- set ns.render_kw_only_separator = False %}*, {% endif -%} {%- endif -%} {%- if config.show_signature_annotations and "annotation" in parameter -%} {%- set annotation = ": " + parameter.annotation|safe -%} {%- endif -%} {%- if "default" in parameter -%} {%- set default = ns.equal + parameter.default|safe -%} {%- endif -%} {%- if parameter.kind == "VAR_POSITIONAL" %}* {%- set render_kw_only_separator = False -%} {%- elif parameter.kind == "VAR_KEYWORD" %}** {%- endif %}{{ parameter.name }}{{ annotation }}{{ default }}{% if not loop.last %}, {% endif -%} {%- endfor %}){% if config.show_signature_annotations and "return_annotation" in signature %} -> {{ signature.return_annotation }} {%- endif -%} {%- endwith -%} {%- endif -%} mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/style.css000066400000000000000000000005241476761050100331640ustar00rootroot00000000000000/* Don't capitalize names. */ h5.doc-heading { text-transform: none !important; } /* Avoid breaking parameters name, etc. in table cells. */ .doc-contents td code { word-break: normal !important; } /* For pieces of Markdown rendered in table cells. */ .doc-contents td p { margin-top: 0 !important; margin-bottom: 0 !important; } mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/material/yield.html000066400000000000000000000005511476761050100333060ustar00rootroot00000000000000{{ log.debug() }}

    Yields:

    Type Description
    {% if yield.annotation %}{{ yield.annotation }}{% endif %} {{ yield.description|convert_markdown(heading_level, html_id) }}
    mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/mkdocs/000077500000000000000000000000001476761050100307735ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/mkdocs/exceptions.html000066400000000000000000000003401476761050100340370ustar00rootroot00000000000000{{ log.debug() }}
    Exceptions:
    {% for exception in exceptions %}
    {{ ("`" + exception.annotation + "`: " + exception.description)|convert_markdown(heading_level, html_id) }}
    {% endfor %}
    mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/mkdocs/keyword_args.html000066400000000000000000000004251476761050100343620ustar00rootroot00000000000000{{ log.debug() }}
    Keyword arguments:
    {% for kwarg in kwargs %}
    {{ ("**" + kwarg.name + ":** " + ("`" + kwarg.annotation + "` – " if kwarg.annotation else "") + kwarg.description)|convert_markdown(heading_level, html_id) }}
    {% endfor %}
    mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/mkdocs/parameters.html000066400000000000000000000004461476761050100340300ustar00rootroot00000000000000{{ log.debug() }}
    Parameters:
    {% for parameter in parameters %}
    {{ ("**" + parameter.name + ":** " + ("`" + parameter.annotation + "` – " if parameter.annotation else "") + parameter.description)|convert_markdown(heading_level, html_id) }}
    {% endfor %}
    mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/mkdocs/return.html000066400000000000000000000003101476761050100331720ustar00rootroot00000000000000{{ log.debug() }}
    Returns:
    {{ (("`" + return.annotation + "` – " if return.annotation else "") + return.description)|convert_markdown(heading_level, html_id) }}
    mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/mkdocs/style.css000066400000000000000000000002111476761050100326370ustar00rootroot00000000000000.doc-contents { padding-left: 20px; } .doc-contents dd>p { margin-bottom: 0.5rem; } .doc-contents dl+dl { margin-top: -0.5rem; } mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/mkdocs/yield.html000066400000000000000000000003041476761050100327640ustar00rootroot00000000000000{{ log.debug() }}
    Yields:
    {{ (("`" + yield.annotation + "` – " if yield.annotation else "") + yield.description)|convert_markdown(heading_level, html_id) }}
    mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/readthedocs/000077500000000000000000000000001476761050100320005ustar00rootroot00000000000000exceptions.html000066400000000000000000000010411476761050100347640ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/readthedocs{{ log.debug() }}
    Exceptions:
      {% for exception in exceptions %}
    • {{ ("`" + exception.annotation + "` – " + exception.description)|convert_markdown(heading_level, html_id) }}
    • {% endfor %}
    keyword_args.html000066400000000000000000000011311476761050100353030ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/readthedocs{{ log.debug() }}
    Keyword arguments:
      {% for kwarg in kwargs %}
    • {{ ("**" + kwarg.name + "**" + (" (`" + kwarg.annotation + "`)" if kwarg.annotation else "") + " – " + kwarg.description)|convert_markdown(heading_level, html_id) }}
    • {% endfor %}
    parameters.html000066400000000000000000000011521476761050100347510ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/readthedocs{{ log.debug() }}
    Parameters:
      {% for parameter in parameters %}
    • {{ ("**" + parameter.name + "**" + (" (`" + parameter.annotation + "`)" if parameter.annotation else "") + " – " + parameter.description)|convert_markdown(heading_level, html_id) }}
    • {% endfor %}
    mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/readthedocs/return.html000066400000000000000000000007561476761050100342150ustar00rootroot00000000000000{{ log.debug() }}
    Returns:
    • {{ ((("`" + return.annotation + "` – ") if return.annotation else "") + return.description)|convert_markdown(heading_level, html_id) }}
    mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/readthedocs/style.css000066400000000000000000000011301476761050100336450ustar00rootroot00000000000000/* Avoid breaking parameters name, etc. in table cells. */ .doc-contents td code { word-break: normal !important; } /* For pieces of Markdown rendered in table cells. */ .doc-contents td p { margin-top: 0 !important; margin-bottom: 0 !important; } /* Avoid breaking code headings. */ .doc-heading code { white-space: normal; } /* Improve rendering of parameters, returns and exceptions. */ .doc-contents .field-name { min-width: 100px; } .doc-contents .field-name, .field-body { border: none !important; padding: 0 !important; } .doc-contents .field-list { margin: 0 !important; } mkdocstrings-python-legacy-0.2.6/src/mkdocstrings_handlers/python/templates/readthedocs/yield.html000066400000000000000000000007521476761050100340000ustar00rootroot00000000000000{{ log.debug() }}
    Yields:
    • {{ ((("`" + yield.annotation + "` – ") if yield.annotation else "") + yield.description)|convert_markdown(heading_level, html_id) }}
    mkdocstrings-python-legacy-0.2.6/tests/000077500000000000000000000000001476761050100201605ustar00rootroot00000000000000mkdocstrings-python-legacy-0.2.6/tests/__init__.py000066400000000000000000000002631476761050100222720ustar00rootroot00000000000000"""Tests suite for `mkdocstrings-python-legacy`.""" from pathlib import Path TESTS_DIR = Path(__file__).parent TMP_DIR = TESTS_DIR / "tmp" FIXTURES_DIR = TESTS_DIR / "fixtures" mkdocstrings-python-legacy-0.2.6/tests/conftest.py000066400000000000000000000047621476761050100223700ustar00rootroot00000000000000"""Configuration for the pytest test suite.""" from __future__ import annotations from collections import ChainMap from typing import TYPE_CHECKING import pytest from markdown.core import Markdown from mkdocs.config.defaults import MkDocsConfig if TYPE_CHECKING: from collections.abc import Iterator from pathlib import Path from mkdocstrings import MkdocstringsExtension, MkdocstringsPlugin @pytest.fixture(name="mkdocs_conf") def fixture_mkdocs_conf(request: pytest.FixtureRequest, tmp_path: Path) -> Iterator[MkDocsConfig]: """Yield a MkDocs configuration object. Parameters: request: Pytest request fixture. tmp_path: Pytest temporary path fixture. Yields: MkDocs configuration object. """ conf = MkDocsConfig() while hasattr(request, "_parent_request") and hasattr(request._parent_request, "_parent_request"): request = request._parent_request conf_dict = { "site_name": "foo", "site_url": "https://example.org/", "site_dir": str(tmp_path), "plugins": [{"mkdocstrings": {"default_handler": "python"}}], **getattr(request, "param", {}), } # Re-create it manually as a workaround for https://github.com/mkdocs/mkdocs/issues/2289 mdx_configs = dict(ChainMap(*conf_dict.get("markdown_extensions", []))) conf.load_dict(conf_dict) assert conf.validate() == ([], []) conf["mdx_configs"] = mdx_configs conf["markdown_extensions"].insert(0, "toc") # Guaranteed to be added by MkDocs. conf = conf["plugins"]["mkdocstrings"].on_config(conf) conf = conf["plugins"]["autorefs"].on_config(conf) yield conf conf["plugins"]["mkdocstrings"].on_post_build(conf) @pytest.fixture(name="plugin") def fixture_plugin(mkdocs_conf: MkDocsConfig) -> MkdocstringsPlugin: """Return a plugin instance. Parameters: mkdocs_conf: MkDocs configuration object (fixture). Returns: Configurated plugin instance. """ plugin = mkdocs_conf["plugins"]["mkdocstrings"] plugin.md = Markdown(extensions=mkdocs_conf["markdown_extensions"], extension_configs=mkdocs_conf["mdx_configs"]) return plugin @pytest.fixture(name="ext_markdown") def fixture_ext_markdown(plugin: MkdocstringsPlugin) -> MkdocstringsExtension: """Return a Markdown instance with MkdocstringsExtension. Parameters: plugin: A configurated plugin instance. (fixture). Returns: The plugin Markdown instance. """ return plugin.md # type: ignore[attr-defined] mkdocstrings-python-legacy-0.2.6/tests/test_collector.py000066400000000000000000000021361476761050100235610ustar00rootroot00000000000000"""Tests for the `collector` module.""" from unittest import mock import pytest from mkdocstrings import CollectionError from mkdocstrings_handlers.python import get_handler class _FakeMkDocsConfig: config_file_path = "mkdocs.yml" @pytest.mark.parametrize( ("retval", "exp_res"), [ ({"error": "error1", "traceback": "hello"}, "error1\nhello"), ({"error": "error1"}, "error1"), ({"error": "", "traceback": "hello"}, "\nhello"), ], ) def test_collect_result_error(retval: dict, exp_res: str) -> None: """Test handling of errors when collecting an object. Args: retval: Return value to mock `json.loads` with. exp_res: Expected result. """ with mock.patch("mkdocstrings_handlers.python.handler.json.loads") as m_loads: # noqa: SIM117 with pytest.raises(CollectionError) as excinfo: # noqa: PT012 m_loads.return_value = retval handler = get_handler({}, _FakeMkDocsConfig, theme="material") # type: ignore[arg-type] assert handler.collect("", {}) assert str(excinfo.value) == exp_res mkdocstrings-python-legacy-0.2.6/tests/test_renderer.py000066400000000000000000000060731476761050100234050ustar00rootroot00000000000000"""Tests for the handlers.python module.""" from __future__ import annotations from copy import deepcopy from mkdocstrings_handlers.python.rendering import ( rebuild_category_lists, sort_key_alphabetical, sort_key_source, sort_object, ) def test_members_order() -> None: """Assert that members sorting functions work correctly.""" subcategories: dict[str, list] = {key: [] for key in ("attributes", "classes", "functions", "methods", "modules")} categories = {"children": {}, **subcategories} collected = { "name": "root", "children": { "b": {"name": "b", "source": {"line_start": 0}, **categories}, "a": {"name": "a", **categories}, "z": {"name": "z", "source": {"line_start": 100}, **categories}, "no_name": {"source": {"line_start": 10}, **categories}, "c": { "name": "c", "source": {"line_start": 30}, "children": { "z": {"name": "z", "source": {"line_start": 200}, **categories}, "a": {"name": "a", "source": {"line_start": 20}, **categories}, }, **subcategories, }, }, "attributes": ["b", "c", "no_name", "z", "a"], "classes": [], "functions": [], "methods": [], "modules": [], } rebuild_category_lists(collected) alphebetical = deepcopy(collected) sort_object(alphebetical, sort_key_alphabetical) rebuilt_categories = {"children": [], **subcategories} assert ( alphebetical["children"] == alphebetical["attributes"] == [ {"name": "a", **rebuilt_categories}, {"name": "b", "source": {"line_start": 0}, **rebuilt_categories}, { "name": "c", "source": {"line_start": 30}, "children": [ {"name": "a", "source": {"line_start": 20}, **rebuilt_categories}, {"name": "z", "source": {"line_start": 200}, **rebuilt_categories}, ], **subcategories, }, {"name": "z", "source": {"line_start": 100}, **rebuilt_categories}, {"source": {"line_start": 10}, **rebuilt_categories}, ] ) source = deepcopy(collected) sort_object(source, sort_key_source) assert ( source["children"] == source["attributes"] == [ {"name": "a", **rebuilt_categories}, {"name": "b", "source": {"line_start": 0}, **rebuilt_categories}, {"source": {"line_start": 10}, **rebuilt_categories}, { "name": "c", "source": {"line_start": 30}, "children": [ {"name": "a", "source": {"line_start": 20}, **rebuilt_categories}, {"name": "z", "source": {"line_start": 200}, **rebuilt_categories}, ], **subcategories, }, {"name": "z", "source": {"line_start": 100}, **rebuilt_categories}, ] ) mkdocstrings-python-legacy-0.2.6/tests/test_themes.py000066400000000000000000000024021476761050100230540ustar00rootroot00000000000000"""Tests for the different themes we claim to support.""" from __future__ import annotations from typing import TYPE_CHECKING import pytest if TYPE_CHECKING: from mkdocstrings import MkdocstringsPlugin @pytest.mark.parametrize( "plugin", [ {"theme": "mkdocs"}, {"theme": "readthedocs"}, {"theme": {"name": "material"}}, ], indirect=["plugin"], ) @pytest.mark.parametrize( "module", [ "mkdocstrings.extension", "mkdocstrings.inventory", "mkdocstrings.loggers", "mkdocstrings.handlers.base", "mkdocstrings.handlers.rendering", "mkdocstrings_handlers.python.handler", "mkdocstrings_handlers.python.rendering", ], ) def test_render_themes_templates(module: str, plugin: MkdocstringsPlugin) -> None: """Test rendering of a given theme's templates. Parameters: module: The module to load and render (parametrized). plugin: The plugin instance (parametrized fixture). """ handler = plugin.handlers.get_handler("python") handler._update_env(plugin.md, config=plugin.handlers._tool_config) # type: ignore[attr-defined] options = handler.get_options({}) data = handler.collect(module, options) handler.render(data, options)