diff --git a/.copier-answers.yml b/.copier-answers.yml deleted file mode 100644 index b709d322..00000000 --- a/.copier-answers.yml +++ /dev/null @@ -1,19 +0,0 @@ -# Changes here will be overwritten by Copier. -_commit: 1.11.15 -_src_path: gh:pawamoy/copier-uv -author_email: dev@pawamoy.fr -author_fullname: Timothée Mazzucotelli -author_username: pawamoy -copyright_date: '2019' -copyright_holder: Timothée Mazzucotelli -copyright_holder_email: dev@pawamoy.fr -copyright_license: ISC -project_description: Automatic documentation from sources, for MkDocs. -project_name: mkdocstrings -python_package_command_line_name: '' -python_package_distribution_name: mkdocstrings -python_package_import_name: mkdocstrings -repository_name: mkdocstrings -repository_namespace: mkdocstrings -repository_provider: github.com - diff --git a/.envrc b/.envrc deleted file mode 100644 index f9d77ee3..00000000 --- a/.envrc +++ /dev/null @@ -1 +0,0 @@ -PATH_add scripts diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 812789e6..00000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,2 +0,0 @@ -github: pawamoy -polar: pawamoy diff --git a/.github/ISSUE_TEMPLATE/1-bug.md b/.github/ISSUE_TEMPLATE/1-bug.md deleted file mode 100644 index 5677ad2a..00000000 --- a/.github/ISSUE_TEMPLATE/1-bug.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -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._internal.debug # | xclip -selection clipboard -``` - -PASTE MARKDOWN OUTPUT HERE - -### Additional context - diff --git a/.github/ISSUE_TEMPLATE/2-feature.md b/.github/ISSUE_TEMPLATE/2-feature.md deleted file mode 100644 index 2df98fbc..00000000 --- a/.github/ISSUE_TEMPLATE/2-feature.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -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 - diff --git a/.github/ISSUE_TEMPLATE/3-docs.md b/.github/ISSUE_TEMPLATE/3-docs.md deleted file mode 100644 index 92ac8ec5..00000000 --- a/.github/ISSUE_TEMPLATE/3-docs.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -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 - diff --git a/.github/ISSUE_TEMPLATE/4-change.md b/.github/ISSUE_TEMPLATE/4-change.md deleted file mode 100644 index dc9a8f17..00000000 --- a/.github/ISSUE_TEMPLATE/4-change.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -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 - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 23000298..00000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -blank_issues_enabled: false -contact_links: -- name: I have a question / I need help - url: https://github.com/mkdocstrings/mkdocstrings/discussions/new?category=q-a - about: Ask and answer questions in the Discussions tab. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 6f0f2faf..00000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,15 +0,0 @@ -### For reviewers - - -- [ ] I did not use AI -- [ ] I used AI and thoroughly reviewed every code/docs change - -### Description of the change - - -### Relevant resources - - -- -- -- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index a8d0a185..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,154 +0,0 @@ -name: ci - -on: - push: - branches: - - main - - test-me-* - pull_request: - branches: - - main - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} - -defaults: - run: - shell: bash - -env: - LANG: en_US.utf-8 - LC_ALL: en_US.utf-8 - PYTHONIOENCODING: UTF-8 - PYTHONPATH: docs - PYTHONWARNDEFAULTENCODING: "1" - PYTHON_VERSIONS: "" - -jobs: - - quality: - strategy: - matrix: - os: - - ubuntu-latest - - macos-latest - - windows-latest - python-version: - - "3.10" - - "3.14" - include: - - os: ubuntu-latest - python-version: "3.11" - - os: ubuntu-latest - python-version: "3.12" - - os: ubuntu-latest - python-version: "3.13" - - os: ubuntu-latest - python-version: "3.15-dev" - - runs-on: ${{ matrix.os }} - continue-on-error: true - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Setup Python - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python-version }} - - - 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 - if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13' }} - with: - name: objects.inv - path: site/objects.inv - - tests: - - needs: - - quality - strategy: - matrix: - os: - - ubuntu-latest - - macos-latest - - windows-latest - python-version: - - "3.10" - - "3.11" - - "3.12" - - "3.13" - - "3.14" - - "3.15-dev" - resolution: - - highest - - lowest-direct - exclude: - - os: macos-latest - resolution: lowest-direct - - os: windows-latest - resolution: lowest-direct - - runs-on: ${{ matrix.os }} - continue-on-error: true - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Setup Python - uses: actions/setup-python@v6 - 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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 1c7cda36..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: 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@v6 - with: - python-version: "3.13" - - 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 - diff --git a/.github/workflows/sponsors.yml b/.github/workflows/sponsors.yml deleted file mode 100644 index 8dd9150f..00000000 --- a/.github/workflows/sponsors.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Update sponsors - -on: - schedule: - - cron: '0 0 * * *' - workflow_dispatch: - -permissions: - contents: write - pull-requests: write - -jobs: - update-readme: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Update README and create PR - uses: pawamoy/readme-insert@main - with: - markup-url: https://pawamoy.github.io/sponsors.txt - start-marker: '' - end-marker: '' - commit-message: 'chore: Update sponsors section in README' - pr-title: 'chore: Update sponsors section in README' - pr-body: 'This PR updates the sponsors section in the README file.' diff --git a/.gitignore b/.gitignore deleted file mode 100644 index faeb06ae..00000000 --- a/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# editors -.idea/ -.vscode/ - -# python -*.egg-info/ -*.py[cod] -.venv/ -.venvs/ -/build/ -/dist/ - -# tools -.coverage* -/.pdm-build/ -/htmlcov/ -/site/ -uv.lock - -# cache -.cache/ -.pytest_cache/ -.ruff_cache/ -__pycache__/ diff --git a/src/mkdocstrings/_internal/__init__.py b/.nojekyll similarity index 100% rename from src/mkdocstrings/_internal/__init__.py rename to .nojekyll diff --git a/404.html b/404.html new file mode 100644 index 00000000..a3184de8 --- /dev/null +++ b/404.html @@ -0,0 +1 @@ + mkdocstrings

404 - Not found

\ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 4e6fb131..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,1140 +0,0 @@ -# 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). - - -## [1.0.4](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.4) - 2026-04-15 - -[Compare with 1.0.3](https://github.com/mkdocstrings/mkdocstrings/compare/1.0.3...1.0.4) - -### Bug Fixes - -- Add timeout when downloading inventories (10 seconds) ([3d1969a](https://github.com/mkdocstrings/mkdocstrings/commit/3d1969a279ea396792c682810d029503e48d8fcd) by Simon Lloyd). [Issue-819](https://github.com/mkdocstrings/mkdocstrings/issues/819) - -## [1.0.3](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.3) - 2026-02-07 - -[Compare with 1.0.2](https://github.com/mkdocstrings/mkdocstrings/compare/1.0.2...1.0.3) - -### Bug Fixes - -- Forward extension instances directly passed from Zensical ([65b27ec](https://github.com/mkdocstrings/mkdocstrings/commit/65b27ec8d1d671eddf021e48b0114cc3f8aca14a) by Timothée Mazzucotelli). -- Propagate Zensical's `zrelpath` processor ([dbf263d](https://github.com/mkdocstrings/mkdocstrings/commit/dbf263dfdd2fdd769d66fa62bdd388e05988bc78) by Timothée Mazzucotelli). - -## [1.0.2](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.2) - 2026-01-24 - -[Compare with 1.0.1](https://github.com/mkdocstrings/mkdocstrings/compare/1.0.1...1.0.2) - -### Code Refactoring - -- Use global instances for handlers and autorefs ([9f79141](https://github.com/mkdocstrings/mkdocstrings/commit/9f79141d7eb35aba0c89a43795df0ee22a25a61e) by Timothée Mazzucotelli). - -## [1.0.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.1) - 2026-01-19 - -[Compare with 1.0.0](https://github.com/mkdocstrings/mkdocstrings/compare/1.0.0...1.0.1) - -### Code Refactoring - -- Support manual cross-references in Zensical too ([d37d907](https://github.com/mkdocstrings/mkdocstrings/commit/d37d9079e5381350b2e3ffc5f698e28a5b572d36) by Timothée Mazzucotelli). -- Support cross-references in Zensical ([f43f1ee](https://github.com/mkdocstrings/mkdocstrings/commit/f43f1ee2cd38a0dba64fc7d0db3c5ffb037bf7f7) by Timothée Mazzucotelli). [PR-812](https://github.com/mkdocstrings/mkdocstrings/pull/812) - -## [1.0.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.0) - 2025-11-27 - -[Compare with 0.30.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.30.1...1.0.0) - -### Breaking Changes - -- `BaseHandler.name`: *Attribute value was changed*: `''` -> unset -- `BaseHandler.domain`: *Attribute value was changed*: `''` -> unset -- `BaseHandler.fallback_config`: *Public object was removed* -- `BaseHandler.__init__(args)`: *Parameter was removed* -- `BaseHandler.__init__(kwargs)`: *Parameter was removed* -- `BaseHandler.__init__(theme)`: *Parameter was added as required* -- `BaseHandler.__init__(custom_templates)`: *Parameter was added as required* -- `BaseHandler.__init__(mdx)`: *Parameter was added as required* -- `BaseHandler.__init__(mdx_config)`: *Parameter was added as required* -- `BaseHandler.update_env(args)`: *Parameter was removed* -- `BaseHandler.update_env(kwargs)`: *Parameter was removed* -- `BaseHandler.update_env(config)`: *Parameter was added as required* -- `Handlers.get_anchors`: *Public object was removed* (import from `mkdocstrings` directly) -- `mkdocstrings.plugin`: *Public module was removed* (import from `mkdocstrings` directly) -- `mkdocstrings.loggers`: *Public module was removed* (import from `mkdocstrings` directly) -- `mkdocstrings.inventory`: *Public module was removed* (import from `mkdocstrings` directly) -- `mkdocstrings.extension`: *Public module was removed* (import from `mkdocstrings` directly) -- `mkdocstrings.handlers`: *Public module was removed* (import from `mkdocstrings` directly) - -### Code Refactoring - -- Remove deprecated code before v1 ([de34044](https://github.com/mkdocstrings/mkdocstrings/commit/de34044a02b45250e215af0f969dca581dfb82c5) by Timothée Mazzucotelli). -- Expect Zensical to pass extension configuration instead of loading it again from YAML ([6b73d5a](https://github.com/mkdocstrings/mkdocstrings/commit/6b73d5a2f455062ab6c68376c85adce6adc037a3) by Timothée Mazzucotelli). -- Expose the Markdown extension, to make mkdocstrings compatible with Zensical ([6de2667](https://github.com/mkdocstrings/mkdocstrings/commit/6de266759b79eb72cddd300e6a0a8576085fae40) by Timothée Mazzucotelli). - -## [0.30.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.30.1) - 2025-09-19 - -[Compare with 0.30.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.30.0...0.30.1) - -### Bug Fixes - -- Create default SSL context in main thread before downloading inventories ([eec7fb4](https://github.com/mkdocstrings/mkdocstrings/commit/eec7fb4bab948ef6db594fc1d1688be0554c5780) by Çağlar Kutlu). [Issue-796](https://github.com/mkdocstrings/mkdocstrings/issue/796), [PR-797](https://github.com/mkdocstrings/mkdocstrings/pull/797) - -## [0.30.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.30.0) - 2025-07-23 - -[Compare with 0.29.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.29.1...0.30.0) - -### Features - -- Add `data-skip-inventory` boolean attribute for elements to skip registration in local inventory ([f856160](https://github.com/mkdocstrings/mkdocstrings/commit/f856160b03b2c27e1d75fdf4f315c273cb9d9247) by Bartosz Sławecki). [Issue-671](https://github.com/mkdocstrings/mkdocstrings/issues/671), [PR-774](https://github.com/mkdocstrings/mkdocstrings/pull/774) -- Add I18N support (translations) ([2b4ed54](https://github.com/mkdocstrings/mkdocstrings/commit/2b4ed541bc707e55d959092d950ebeecc4fbd136) by Nyuan Zhang). [PR-645](https://github.com/mkdocstrings/mkdocstrings/pull/645), Co-authored-by: Timothée Mazzucotelli - -## [0.29.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.29.1) - 2025-03-31 - -[Compare with 0.29.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.29.0...0.29.1) - -### Dependencies - -- Remove unused typing-extensions dependency ([ba98661](https://github.com/mkdocstrings/mkdocstrings/commit/ba98661b50e2cde19d8696d6c8ceecdbb49ce83f) by Timothée Mazzucotelli). - -### Bug Fixes - -- Ignore invalid inventory lines ([81caff5](https://github.com/mkdocstrings/mkdocstrings/commit/81caff5ff76f1a6606da9d2980e81ae9d2e02246) by Josh Mitchell). [PR-748](https://github.com/mkdocstrings/mkdocstrings/pull/748) - -### Code Refactoring - -- Rename loggers to "mkdocstrings" ([1a98040](https://github.com/mkdocstrings/mkdocstrings/commit/1a980402c39728ce265d8998b396c34bf76a113d) by Timothée Mazzucotelli). - -## [0.29.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.29.0) - 2025-03-10 - -[Compare with 0.28.3](https://github.com/mkdocstrings/mkdocstrings/compare/0.28.3...0.29.0) - -**This is the last version before v1!** - -### Build - -- Depend on MkDocs 1.6 ([11bc400](https://github.com/mkdocstrings/mkdocstrings/commit/11bc400ab7089a47755f24a790c08f2f904c570b) by Timothée Mazzucotelli). - -### Features - -- Support rendering backlinks through handlers ([d4c7b9c](https://github.com/mkdocstrings/mkdocstrings/commit/d4c7b9c42f2de5df234c1ffefae0405a120e383c) by Timothée Mazzucotelli). [Issue-723](https://github.com/mkdocstrings/mkdocstrings/issues/723), [Issue-mkdocstrings-python-153](https://github.com/mkdocstrings/python/issues/153), [PR-739](https://github.com/mkdocstrings/mkdocstrings/pull/739) - -### Code Refactoring - -- Save and forward titles to autorefs ([f49fb29](https://github.com/mkdocstrings/mkdocstrings/commit/f49fb29582714795ca03febf1ee243aa2992917e) by Timothée Mazzucotelli). -- Use a combined event (each split with a different priority) for `on_env` ([8d1dd75](https://github.com/mkdocstrings/mkdocstrings/commit/8d1dd754b4babd3c4f9e6c1d8856be57fe4ba9ea) by Timothée Mazzucotelli). - -## [0.28.3](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.28.3) - 2025-03-08 - -[Compare with 0.28.2](https://github.com/mkdocstrings/mkdocstrings/compare/0.28.2...0.28.3) - -### Deprecations - -All public objects must now be imported from the top-level `mkdocstrings` module. Importing from submodules is deprecated, and will raise errors starting with v1. This should be the last deprecation before v1. - -### Build - -- Make `python` extra depend on latest mkdocstrings-python (1.16.2) ([ba9003e](https://github.com/mkdocstrings/mkdocstrings/commit/ba9003e96c8e5e01900743d5c464cbd228d732f4) by Timothée Mazzucotelli). - -### Code Refactoring - -- Finish exposing/hiding public/internal objects ([0723fc2](https://github.com/mkdocstrings/mkdocstrings/commit/0723fc25fdf5d45bc3b949f370712a706b85fbab) by Timothée Mazzucotelli). -- Re-expose public API in the top-level `mkdocstrings` module ([e66e080](https://github.com/mkdocstrings/mkdocstrings/commit/e66e08096d45f6790492d9a0b767d512e42f67a9) by Timothée Mazzucotelli). -- Move modules to internal folder ([23fe23f](https://github.com/mkdocstrings/mkdocstrings/commit/23fe23f11011d0470a6342ca85e060e5ac2b6bd6) by Timothée Mazzucotelli). - -## [0.28.2](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.28.2) - 2025-02-24 - -[Compare with 0.28.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.28.1...0.28.2) - -### Build - -- Depend on mkdocs-autorefs >= 1.4 ([2c22bdc](https://github.com/mkdocstrings/mkdocstrings/commit/2c22bdc49f6bf5600aefd5ec711747686fda96a8) by Timothée Mazzucotelli). - -## [0.28.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.28.1) - 2025-02-14 - -[Compare with 0.28.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.28.0...0.28.1) - -### Bug Fixes - -- Renew MkDocs' `relpath` processor instead of using same instance ([4ab180d](https://github.com/mkdocstrings/mkdocstrings/commit/4ab180d01964c3ef8005cd72c8d91ba3fd241e27) by Timothée Mazzucotelli). [Issue-mkdocs-3919](https://github.com/mkdocs/mkdocs/issues/3919) - -## [0.28.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.28.0) - 2025-02-03 - -[Compare with 0.27.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.27.0...0.28.0) - -### Breaking Changes - -Although the following changes are "breaking" in terms of public API, we didn't find any public use of these classes and methods on GitHub. - -- `mkdocstrings.extension.AutoDocProcessor.__init__(parser)`: *Parameter was removed* -- `mkdocstrings.extension.AutoDocProcessor.__init__(md)`: *Positional parameter was moved* -- `mkdocstrings.extension.AutoDocProcessor.__init__(config)`: *Parameter was removed* -- `mkdocstrings.extension.AutoDocProcessor.__init__(handlers)`: *Parameter kind was changed*: `positional or keyword` -> `keyword-only` -- `mkdocstrings.extension.AutoDocProcessor.__init__(autorefs)`: *Parameter kind was changed*: `positional or keyword` -> `keyword-only` -- `mkdocstrings.extension.MkdocstringsExtension.__init__(config)`: *Parameter was removed* -- `mkdocstrings.extension.MkdocstringsExtension.__init__(handlers)`: *Positional parameter was moved* -- `mkdocstrings.extension.MkdocstringsExtension.__init__(autorefs)`: *Positional parameter was moved* -- `mkdocstrings.handlers.base.Handlers.__init__(config)`: *Parameter was removed* -- `mkdocstrings.handlers.base.Handlers.__init__(theme)`: *Parameter was added as required* -- `mkdocstrings.handlers.base.Handlers.__init__(default)`: *Parameter was added as required* -- `mkdocstrings.handlers.base.Handlers.__init__(inventory_project)`: *Parameter was added as required* -- `mkdocstrings.handlers.base.Handlers.__init__(tool_config)`: *Parameter was added as required* - -Similarly, the following parameters were renamed, but the methods are only called from our own code, using positional arguments. - -- `mkdocstrings.handlers.base.BaseHandler.collect(config)`: *Parameter was renamed `options`* -- `mkdocstrings.handlers.base.BaseHandler.render(config)`: *Parameter was renamed `options`* - -Finally, the following method was removed, but this is again taken into account in our own code: - -- `mkdocstrings.handlers.base.BaseHandler.get_anchors`: *Public object was removed* - -For these reasons, and because we're still in v0, we do not bump to v1 yet. See following deprecations. - -### Deprecations - -*mkdocstrings* 0.28 will start emitting these deprecations warnings: - -> The `handler` argument is deprecated. The handler name must be specified as a class attribute. - -Previously, the `get_handler` function would pass a `handler` (name) argument to the handler constructor. This name must now be set on the handler's class directly. - -```python -class MyHandler: - name = "myhandler" -``` - -> The `domain` attribute must be specified as a class attribute. - -The `domain` class attribute on handlers is now mandatory and cannot be an empty string. - -```python -class MyHandler: - domain = "mh" -``` - -> The `theme` argument must be passed as a keyword argument. - -This argument could previously be passed as a positional argument (from the `get_handler` function), and must now be passed as a keyword argument. - -> The `custom_templates` argument must be passed as a keyword argument. - -Same as for `theme`, but with `custom_templates`. - -> The `mdx` argument must be provided (as a keyword argument). - -The `get_handler` function now receives a `mdx` argument, which it must forward to the handler constructor and then to the base handler, either explicitly or through `**kwargs`: - -=== "Explicitly" - - ```python - def get_handler(..., mdx, ...): - return MyHandler(..., mdx=mdx, ...) - - - class MyHandler: - def __init__(self, ..., mdx, ...): - super().__init__(..., mdx=mdx, ...) - ``` - -=== "Through `**kwargs`" - - ```python - def get_handler(..., **kwargs): - return MyHandler(..., **kwargs) - - - class MyHandler: - def __init__(self, ..., **kwargs): - super().__init__(**kwargs) - ``` - -In the meantime we still retrieve this `mdx` value at a different moment, by reading it from the MkDocs configuration. - -> The `mdx_config` argument must be provided (as a keyword argument). - -Same as for `mdx`, but with `mdx_config`. - -> mkdocstrings v1 will stop handling 'import' in handlers configuration. Instead your handler must define a `get_inventory_urls` method that returns a list of URLs to download. - -Previously, mkdocstrings would pop the `import` key from a handler's configuration to download each item (URLs). Items could be strings, or dictionaries with a `url` key. Now mkdocstrings gives back control to handlers, which must store this inventory configuration within them, and expose it again through a `get_inventory_urls` method. This method returns a list of tuples: an URL, and a dictionary of options that will be passed again to their `load_inventory` method. Handlers have now full control over the "inventory" setting. - -```python -from copy import deepcopy - - -def get_handler(..., handler_config, ...): - return MyHandler(..., config=handler_config, ...) - - -class MyHandler: - def __init__(self, ..., config, ...): - self.config = config - - def get_inventory_urls(self): - config = deepcopy(self.config["import"]) - return [(inv, {}) if isinstance(inv, str) else (inv.pop("url"), inv) for inv in config] -``` - -Changing the name of the key (for example from `import` to `inventories`) involves a change in user configuration, and both keys will have to be supported by your handler for some time. - -```python -def get_handler(..., handler_config, ...): - if "inventories" not in handler_config and "import" in handler_config: - warn("The 'import' key is renamed 'inventories'", FutureWarning) - handler_config["inventories"] = handler_config.pop("import") - return MyHandler(..., config=handler_config, ...) -``` - -> Setting a fallback anchor function is deprecated and will be removed in a future release. - -This comes from mkdocstrings and mkdocs-autorefs, and will disappear with mkdocstrings v0.28. - -> mkdocstrings v1 will start using your handler's `get_options` method to build options instead of merging the global and local options (dictionaries). - -Handlers must now store their own global options (in an instance attribute), and implement a `get_options` method that receives `local_options` (a dict) and returns combined options (dict or custom object). These combined options are then passed to `collect` and `render`, so that these methods can use them right away. - -```python -def get_handler(..., handler_config, ...): - return MyHandler(..., config=handler_config, ...) - - -class MyHandler: - def __init__(self, ..., config, ...): - self.config = config - - def get_options(local_options): - return {**self.default_options, **self.config["options"], **local_options} -``` - -> The `update_env(md)` parameter is deprecated. Use `self.md` instead. - -Handlers can remove the `md` parameter from their `update_env` method implementation, and use `self.md` instead, if they need it. - -> No need to call `super().update_env()` anymore. - -Handlers don't have to call the parent `update_env` method from their own implementation anymore, and can just drop the call. - -> The `get_anchors` method is deprecated. Declare a `get_aliases` method instead, accepting a string (identifier) instead of a collected object. - -Previously, handlers would implement a `get_anchors` method that received a data object (typed `CollectorItem`) to return aliases for this object. This forced mkdocstrings to collect this object through the handler's `collect` method, which then required some logic with "fallback config" as to prevent unwanted collection. mkdocstrings gives back control to handlers and now calls `get_aliases` instead, which accepts an `identifier` (string) and lets the handler decide how to return aliases for this identifier. For example, it can replicate previous behavior by calling its own `collect` method with its own "fallback config", or do something different (cache lookup, etc.). - -```python -class MyHandler: - def get_aliases(identifier): - try: - obj = self.collect(identifier, self.fallback_config) - # or obj = self._objects_cache[identifier] - except CollectionError: # or KeyError - return () - return ... # previous logic in `get_anchors` -``` - -> The `config_file_path` argument in `get_handler` functions is deprecated. Use `tool_config.get('config_file_path')` instead. - -The `config_file_path` argument is now deprecated and only passed to `get_handler` functions if they accept it. If you used it to compute a "base directory", you can now use the `tool_config` argument instead, which is the configuration of the SSG tool in use (here MkDocs): - -```python -base_dir = Path(tool_config.config_file_path or "./mkdocs.yml").parent -``` - -**Most of these warnings will disappear with the next version of mkdocstrings-python.** - -### Bug Fixes - -- Update handlers in JSON schema to be an object instead of an array ([3cf7d51](https://github.com/mkdocstrings/mkdocstrings/commit/3cf7d51704378adc50d4ea50080aacae39e0e731) by Matthew Messinger). [Issue-733](https://github.com/mkdocstrings/mkdocstrings/issues/733), [PR-734](https://github.com/mkdocstrings/mkdocstrings/pull/734) -- Fix broken table of contents when nesting autodoc instructions ([12c8f82](https://github.com/mkdocstrings/mkdocstrings/commit/12c8f82e9a959ce32cada09f0d2b5c651a705fdb) by Timothée Mazzucotelli). [Issue-348](https://github.com/mkdocstrings/mkdocstrings/issues/348) - -### Code Refactoring - -- Pass `config_file_path` to `get_handler` if it expects it ([8c476ee](https://github.com/mkdocstrings/mkdocstrings/commit/8c476ee0b82c09a5b20d7a773ecaf4be17b9e4d1) by Timothée Mazzucotelli). -- Give back inventory control to handlers ([b84653f](https://github.com/mkdocstrings/mkdocstrings/commit/b84653f2b175824c73bd0291fafff8343ba80125) by Timothée Mazzucotelli). [Related-to-issue-719](https://github.com/mkdocstrings/mkdocstrings/issues/719) -- Give back control to handlers on how they want to handle global/local options ([c00de7a](https://github.com/mkdocstrings/mkdocstrings/commit/c00de7a42b9072cbaa47ecbf18e3e15a6d5ab634) by Timothée Mazzucotelli). [Issue-719](https://github.com/mkdocstrings/mkdocstrings/issues/719) -- Deprecate base handler's `get_anchors` method in favor of `get_aliases` method ([7a668f0](https://github.com/mkdocstrings/mkdocstrings/commit/7a668f0f731401b07123bd02aafbbfc55cd24c0d) by Timothée Mazzucotelli). -- Register all identifiers of rendered objects into autorefs ([434d8c7](https://github.com/mkdocstrings/mkdocstrings/commit/434d8c7cd1e3edbdb9d4c45a9b44b290b19d88f1) by Timothée Mazzucotelli). -- Use mkdocs-get-deps' download utility to remove duplicated code ([bb87cd8](https://github.com/mkdocstrings/mkdocstrings/commit/bb87cd833f2333e77cb2c2926aa24a434c97391f) by Timothée Mazzucotelli). -- Clean up data passed down from plugin to extension and handlers ([b8e8703](https://github.com/mkdocstrings/mkdocstrings/commit/b8e87036e0e1ec5c181b4a2ec5931f1a60636a32) by Timothée Mazzucotelli). [PR-726](https://github.com/mkdocstrings/mkdocstrings/pull/726) - -## [0.27.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.27.0) - 2024-11-08 - -[Compare with 0.26.2](https://github.com/mkdocstrings/mkdocstrings/compare/0.26.2...0.27.0) - -### Features - -- Add support for authentication in inventory file URLs ([1c23c1b](https://github.com/mkdocstrings/mkdocstrings/commit/1c23c1b0fc4a9bdec5e0eb43c8647beab66fec55) by Stefan Mejlgaard). [Issue-707](https://github.com/mkdocstrings/mkdocstrings/issues/707), [PR-710](https://github.com/mkdocstrings/mkdocstrings/pull/710) - -### Performance Improvements - -- Reduce footprint of template debug messages ([5648e5a](https://github.com/mkdocstrings/mkdocstrings/commit/5648e5aca80a5d8ba9e5456efb36b517b9f3cdeb) by Timothée Mazzucotelli). - -### Code Refactoring - -- Use %-formatting for logging messages ([0bbb8ca](https://github.com/mkdocstrings/mkdocstrings/commit/0bbb8caddf34b0a4faa0ed6f26e33102dc892fc8) by Timothée Mazzucotelli). - -## [0.26.2](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.26.2) - 2024-10-12 - -[Compare with 0.26.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.26.1...0.26.2) - -### Build - -- Drop support for Python 3.8 ([f26edeb](https://github.com/mkdocstrings/mkdocstrings/commit/f26edebe01337caa802a98c13240acdd8332a5fa) by Timothée Mazzucotelli). - -## [0.26.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.26.1) - 2024-09-06 - -[Compare with 0.26.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.26.0...0.26.1) - -### Bug Fixes - -- Instantiate config of the autorefs plugin when it is not enabled by the user ([db2ab34](https://github.com/mkdocstrings/mkdocstrings/commit/db2ab3403a95034987d574a517ddc426a4b4e1bd) by Timothée Mazzucotelli). [Issue-autorefs#57](https://github.com/mkdocstrings/autorefs/issues/57) - -## [0.26.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.26.0) - 2024-09-02 - -[Compare with 0.25.2](https://github.com/mkdocstrings/mkdocstrings/compare/0.25.2...0.26.0) - -### Build - -- Upgrade Python-Markdown lower bound to 3.6 ([28565f9](https://github.com/mkdocstrings/mkdocstrings/commit/28565f97f21bf81b2bc554679c641fba3f639882) by Timothée Mazzucotelli). - -### Dependencies - -- Depend on mkdocs-autorefs v1 ([33aa573](https://github.com/mkdocstrings/mkdocstrings/commit/33aa573efb17b13e7b9da77e29aeccb3fbddd8e8) by Timothée Mazzucotelli). - -### Features - -- Allow hooking into autorefs when converting Markdown docstrings ([b63e726](https://github.com/mkdocstrings/mkdocstrings/commit/b63e72691a8f92dd59b56304125de4a19e0d028c) by Timothée Mazzucotelli). [Based-on-PR-autorefs#46](https://github.com/mkdocstrings/autorefs/pull/46) - -## [0.25.2](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.25.2) - 2024-07-25 - -[Compare with 0.25.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.25.1...0.25.2) - -### Code Refactoring - -- Give precedence to Markdown heading level (`##`) ([2e5f89e](https://github.com/mkdocstrings/mkdocstrings/commit/2e5f89e8cef11e6447425d3700c29558cd6d241b) by Timothée Mazzucotelli). - -## [0.25.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.25.1) - 2024-05-05 - -[Compare with 0.25.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.25.0...0.25.1) - -### Bug Fixes - -- Always descend into sub-headings when re-applying their label ([cb86e08](https://github.com/mkdocstrings/mkdocstrings/commit/cb86e08bbc5e8057393aa1cd7ca29bc2b40ab5eb) by Timothée Mazzucotelli). [Issue-mkdocstrings/python-158](https://github.com/mkdocstrings/python/issues/158) - -## [0.25.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.25.0) - 2024-04-27 - -[Compare with 0.24.3](https://github.com/mkdocstrings/mkdocstrings/compare/0.24.3...0.25.0) - -### Features - -- Support `once` parameter in logging methods, allowing to log a message only once with a given logger ([1532b59](https://github.com/mkdocstrings/mkdocstrings/commit/1532b59a6efd99fed846cf7edfd0b26525700d3f) by Timothée Mazzucotelli). -- Support blank line between `::: path` and YAML options ([d799d2f](https://github.com/mkdocstrings/mkdocstrings/commit/d799d2f3903bce44fb751f8cf3fb8078d25549da) by Timothée Mazzucotelli). [Issue-450](https://github.com/mkdocstrings/mkdocstrings/issues/450) - -### Code Refactoring - -- Allow specifying name of template loggers ([c5b5f69](https://github.com/mkdocstrings/mkdocstrings/commit/c5b5f697c83271d961c7ac795412d6b4964ba2b7) by Timothée Mazzucotelli). - -## [0.24.3](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.24.3) - 2024-04-05 - -[Compare with 0.24.2](https://github.com/mkdocstrings/mkdocstrings/compare/0.24.2...0.24.3) - -### Bug Fixes - -- Support HTML toc labels with Python-Markdown 3.6+ (uncomment code...) ([7fe3e5f](https://github.com/mkdocstrings/mkdocstrings/commit/7fe3e5f28239c08094fb656728369998f52630ea) by Timothée Mazzucotelli). - -## [0.24.2](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.24.2) - 2024-04-02 - -[Compare with 0.24.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.24.1...0.24.2) - -### Bug Fixes - -- Support HTML toc labels with Python-Markdown 3.6+ ([c0d0090](https://github.com/mkdocstrings/mkdocstrings/commit/c0d009000678a2ccbfb0c8adfeff3dc83845ee41) by Timothée Mazzucotelli). [Issue-mkdocstrings/python-143](https://github.com/mkdocstrings/python/issues/143) - -## [0.24.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.24.1) - 2024-02-27 - -[Compare with 0.24.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.24.0...0.24.1) - -### Code Refactoring - -- Support new pymdownx-highlight options ([a7a2907](https://github.com/mkdocstrings/mkdocstrings/commit/a7a29079aebcd79be84ac38ce275797920e4c75e) by Timothée Mazzucotelli). -- Backup anchors with id and no href, for compatibility with autorefs' Markdown anchors ([b5236b4](https://github.com/mkdocstrings/mkdocstrings/commit/b5236b4333ebde9648c84f6e4b0f4c2b10f3ecd4) by Timothée Mazzucotelli). [PR-#651](https://github.com/mkdocstrings/mkdocstrings/pull/651), [Related-to-mkdocs-autorefs#39](https://github.com/mkdocstrings/autorefs/pull/39), Co-authored-by: Oleh Prypin - -## [0.24.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.24.0) - 2023-11-14 - -[Compare with 0.23.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.23.0...0.24.0) - -### Features - -- Cache downloaded inventories as local file ([ce84dd5](https://github.com/mkdocstrings/mkdocstrings/commit/ce84dd57dc5cd3bf3f4be9623ddaa73e1c1868f0) by Oleh Prypin). [PR #632](https://github.com/mkdocstrings/mkdocstrings/pull/632) - -### Bug Fixes - -- Make `custom_templates` relative to the config file ([370a61d](https://github.com/mkdocstrings/mkdocstrings/commit/370a61d12b33f3fb61f6bddb3939eb8ff6018620) by Waylan Limberg). [Issue #477](https://github.com/mkdocstrings/mkdocstrings/issues/477), [PR #627](https://github.com/mkdocstrings/mkdocstrings/pull/627) -- Remove duplicated headings for docstrings nested in tabs/admonitions ([e2123a9](https://github.com/mkdocstrings/mkdocstrings/commit/e2123a935edea0abdc1b439e2c2b76e002c76e2b) by Perceval Wajsburt, [f4a94f7](https://github.com/mkdocstrings/mkdocstrings/commit/f4a94f7d8b8eb1ac01d65bb7237f0077e320ddac) by Oleh Prypin). [Issue #609](https://github.com/mkdocstrings/mkdocstrings/issues/609), [PR #610](https://github.com/mkdocstrings/mkdocstrings/pull/610), [PR #613](https://github.com/mkdocstrings/mkdocstrings/pull/613) - -### Code Refactoring - -- Drop support for MkDocs < 1.4, modernize usages ([b61d4d1](https://github.com/mkdocstrings/mkdocstrings/commit/b61d4d15258c66b14266aa04b456f191f101b2c6) by Oleh Prypin). [PR #629](https://github.com/mkdocstrings/mkdocstrings/pull/629) - -## [0.23.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.23.0) - 2023-08-28 - -[Compare with 0.22.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.22.0...0.23.0) - -### Breaking Changes - -- Removed `BaseCollector` and `BaseRenderer` classes: they were merged into the `BaseHandler` class. -- Removed the watch feature, as MkDocs now provides it natively. -- Removed support for `selection` and `rendering` keys in YAML blocks: use `options` instead. -- Removed support for loading handlers from the `mkdocstrings.handler` namespace. - Handlers must now be packaged under the `mkdocstrings_handlers` namespace. - -### Features - -- Register all anchors for each object in the inventory ([228fb73](https://github.com/mkdocstrings/mkdocstrings/commit/228fb737caca4e20e600053bf59cbfa3e9c73906) by Timothée Mazzucotelli). - -### Bug Fixes - -- Don't add `codehilite` CSS class to inline code ([7690d41](https://github.com/mkdocstrings/mkdocstrings/commit/7690d41e2871997464367e673023585c4fb05e26) by Timothée Mazzucotelli). -- Support cross-references for API docs rendered in top-level index page ([b194452](https://github.com/mkdocstrings/mkdocstrings/commit/b194452be93aee33b3c28a468762b4d96c501f4f) by Timothée Mazzucotelli). - -### Code Refactoring - -- Sort inventories before writing them to disk ([9371e9f](https://github.com/mkdocstrings/mkdocstrings/commit/9371e9fc7dd68506b73aa1580a12c5f5cd779aba) by Timothée Mazzucotelli). -- Stop accepting sets as return value of `get_anchors` (only tuples), to preserve order ([2e10374](https://github.com/mkdocstrings/mkdocstrings/commit/2e10374be258e9713b26f73dd06d0c2520ec07a5) by Timothée Mazzucotelli). -- Remove deprecated parts ([0a90a47](https://github.com/mkdocstrings/mkdocstrings/commit/0a90a474c8dcbd95821700d7dab63f03e392c40f) by Timothée Mazzucotelli). -- Use proper parameters in `Inventory.register` method ([433c6e0](https://github.com/mkdocstrings/mkdocstrings/commit/433c6e01aab9333589f755e483f124db0836f143) by Timothée Mazzucotelli). - -## [0.22.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.22.0) - 2023-05-25 - -[Compare with 0.21.2](https://github.com/mkdocstrings/mkdocstrings/compare/0.21.2...0.22.0) - -### Features - -- Allow extensions to add templates ([cf0af05](https://github.com/mkdocstrings/mkdocstrings/commit/cf0af059eb89240eba0437de417c124389e2f20e) by Timothée Mazzucotelli). [PR #569](https://github.com/mkdocstrings/mkdocstrings/pull/569) - -### Code Refactoring - -- Report inventory loading errors ([2c05d78](https://github.com/mkdocstrings/mkdocstrings/commit/2c05d7854b87251e26c1a2e1810b85702ff110f3) by Timothée Mazzucotelli). Co-authored-by: Oleh Prypin - -## [0.21.2](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.21.2) - 2023-04-06 - -[Compare with 0.21.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.21.1...0.21.2) - -### Bug Fixes - -- Fix regression with LRU cached method ([85efbd2](https://github.com/mkdocstrings/mkdocstrings/commit/85efbd285d4c8977755bda1c36220b241a9e1502) by Timothée Mazzucotelli). [Issue #549](https://github.com/mkdocstrings/mkdocstrings/issues/549) - -## [0.21.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.21.1) - 2023-04-05 - -[Compare with 0.21.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.21.0...0.21.1) - -### Bug Fixes - -- Fix missing typing-extensions dependency on Python less than 3.10 ([bff760b](https://github.com/mkdocstrings/mkdocstrings/commit/bff760b77c1eedfa770e852aa994a69ff51b0a32) by Timothée Mazzucotelli). [Issue #548](https://github.com/mkdocstrings/mkdocstrings/issues/548) - -## [0.21.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.21.0) - 2023-04-05 - -[Compare with 0.20.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.20.0...0.21.0) - -### Features - -- Expose the full config to handlers ([15dacf6](https://github.com/mkdocstrings/mkdocstrings/commit/15dacf62f8479a05e9604383155ffa6fade0522d) by David Patterson). [Issue #501](https://github.com/mkdocstrings/mkdocstrings/issues/501), [PR #509](https://github.com/mkdocstrings/mkdocstrings/pull/509) - -## [0.20.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.20.0) - 2023-01-19 - -[Compare with 0.19.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.19.1...0.20.0) - -### Features -- Add `enabled` configuration option ([8cf117d](https://github.com/mkdocstrings/mkdocstrings/commit/8cf117daeefb4fc522145cc567b40eb4256c0a94) by StefanBRas). [Issue #478](https://github.com/mkdocstrings/mkdocstrings/issues/478), [PR #504](https://github.com/mkdocstrings/mkdocstrings/pull/504) - -### Bug Fixes -- Handle updating Jinja environment of multiple handlers ([a6ea80c](https://github.com/mkdocstrings/mkdocstrings/commit/a6ea80c992f2a200d8cee3c9ff3b651ddd049a3d) by David Patterson). [Related PR #201](https://github.com/mkdocstrings/mkdocstrings/pull/201), [Issue #502](https://github.com/mkdocstrings/mkdocstrings/issues/502), [PR #507](https://github.com/mkdocstrings/mkdocstrings/pull/507) - -### Code Refactoring -- Make `_load_inventory` accept lists as arguments ([105ed82](https://github.com/mkdocstrings/mkdocstrings/commit/105ed8210d4665f6b52f2cc04d56df2d35cd3caf) by Sorin Sbarnea). [Needed by PR mkdocstrings/python#49](https://github.com/mkdocstrings/python/issues/49), [PR #511](https://github.com/mkdocstrings/mkdocstrings/pull/511) -- Remove support for MkDocs < 1.2 (we already depended on MkDocs >= 1.2) ([ac963c8](https://github.com/mkdocstrings/mkdocstrings/commit/ac963c88c793e640d2a7a31392aff1fc2d15ba52) by Timothée Mazzucotelli). - -## [0.19.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.19.1) - 2022-12-13 - -[Compare with 0.19.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.19.0...0.19.1) - -### Bug Fixes -- Fix regular expression for Sphinx inventory parsing ([348bdd5](https://github.com/mkdocstrings/mkdocstrings/commit/348bdd5e930f3cf7a8e27835189794ec940ae1b7) by Luis Michaelis). [Issue #496](https://github.com/mkdocstrings/mkdocstrings/issues/496), [PR #497](https://github.com/mkdocstrings/mkdocstrings/issues/497) - -### Code Refactoring -- Small fixes to type annotations ([9214b74](https://github.com/mkdocstrings/mkdocstrings/commit/9214b74367da1f9c808eacc8ceecc4134d5c9d3c) by Oleh Prypin). [PR #470](https://github.com/mkdocstrings/mkdocstrings/issues/470) -- Report usage-based warnings as user-facing messages ([03dd7a6](https://github.com/mkdocstrings/mkdocstrings/commit/03dd7a6e4fefa44889bda9899d9b698bcfd07990) by Oleh Prypin). [PR #464](https://github.com/mkdocstrings/mkdocstrings/issues/464) - - -## [0.19.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.19.0) - 2022-05-28 - -[Compare with 0.18.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.18.1...0.19.0) - -### Highlights -We decided to deprecate a few things to pave the way towards a more -stable code base, bringing us closer to a v1. - -- Selection and rendering options are now combined into a single - `options` key. Using the old keys will emit a deprecation warning. -- The `BaseCollector` and `BaseRenderer` classes are deprecated in favor - of `BaseHandler`, which merges their functionality. Using the old - classes will emit a deprecation warning. - -New versions of the Python handler and the legacy Python handler -were also released in coordination with *mkdocstrings* 0.19. -See their respective changelogs: -[python](https://mkdocstrings.github.io/python/changelog/#070-2022-05-28), -[python-legacy](https://mkdocstrings.github.io/python-legacy/changelog/#023-2022-05-28). -Most notably, the Python handler gained the `members` and `filters` options -that prevented many users to switch to it. - -*mkdocstrings* stopped depending directly on the legacy Python handler. -It means you now have to explicitely depend on it, directly or through -the extra provided by *mkdocstrings*, if you want to continue using it. - -### Packaging / Dependencies -- Stop depending directly on mkdocstrings-python-legacy ([9055d58](https://github.com/mkdocstrings/mkdocstrings/commit/9055d582a6244a45a1af1aeccd8bf3436889a1a5) by Timothée Mazzucotelli). [Issue #376](https://github.com/mkdocstrings/mkdocstrings/issues/376) - -### Features -- Pass config file path to handlers ([cccebc4](https://github.com/mkdocstrings/mkdocstrings/commit/cccebc40c0d51c23381d53432d9355fba9a290ae) by Timothée Mazzucotelli). [Issue #311](https://github.com/mkdocstrings/mkdocstrings/issues/311), [PR #425](https://github.com/mkdocstrings/mkdocstrings/issues/425) - -### Code Refactoring -- Support options / deprecated options mix-up ([7c71f26](https://github.com/mkdocstrings/mkdocstrings/commit/7c71f2623b667d43c5e9eb8aea881df2c9984a0e) by Timothée Mazzucotelli). -- Deprecate watch feature in favor of MkDocs' built-in one ([c20022e](https://github.com/mkdocstrings/mkdocstrings/commit/c20022e6adfd3a18fd698f50355dfce534b9feb9) by Timothée Mazzucotelli). -- Log relative template paths if possible, instead of absolute ([91f5f83](https://github.com/mkdocstrings/mkdocstrings/commit/91f5f83408c7aab9124cc19fa47c940541d6f5ec) by Timothée Mazzucotelli). -- Deprecate `selection` and `rendering` YAML keys ([3335310](https://github.com/mkdocstrings/mkdocstrings/commit/3335310b985401642fea8322aba503cafa1c50b1) by Timothée Mazzucotelli). [PR #420](https://github.com/mkdocstrings/mkdocstrings/issues/420) -- Deprecate `BaseCollector` and `BaseRenderer` ([eb822cb](https://github.com/mkdocstrings/mkdocstrings/commit/eb822cb11ec065da0b1277299aae4ffeeffadc6f) by Timothée Mazzucotelli). [PR #413](https://github.com/mkdocstrings/mkdocstrings/issues/413) - - -## [0.18.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.18.1) - 2022-03-01 - -[Compare with 0.18.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.18.0...0.18.1) - -### Bug Fixes -- Don't preemptively register identifiers as anchors ([c7ac043](https://github.com/mkdocstrings/mkdocstrings/commit/c7ac04324d005d9cf7d2c1f3b2c39f212275d451) by Timothée Mazzucotelli). - - -## [0.18.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.18.0) - 2022-02-06 - -[Compare with 0.17.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.17.0...0.18.0) - -### Highlights -- Python 3.6 support is dropped. -- We provide a new, experimental Python handler based on [Griffe](https://github.com/mkdocstrings/griffe). - This new handler brings automatic cross-references for every annotation in your code, - including references to third-party libraries' APIs if they provide objects inventories - and you explicitely [load them](https://mkdocstrings.github.io/usage/#cross-references-to-other-projects-inventories) in `mkdocs.yml`. - [See migration notes in the documentation](https://mkdocstrings.github.io/handlers/overview/#about-the-python-handlers). -- The "legacy" Python handler now lives in its own repository at https://github.com/mkdocstrings/python-legacy. - -### Packaging / Dependencies -- Add Crystal extra, update Python extras versions ([b8222b0](https://github.com/mkdocstrings/mkdocstrings/commit/b8222b0150d4743be857bcbf40f014265095885b) by Timothée Mazzucotelli). [PR #374](https://github.com/mkdocstrings/mkdocstrings/pull/374) -- Update autorefs to actually required version ([fc6c7f6](https://github.com/mkdocstrings/mkdocstrings/commit/fc6c7f652a420ac29cf16cbb99b11a55aa9b38ea) by Timothée Mazzucotelli). -- Drop Python 3.6 support ([7205ac6](https://github.com/mkdocstrings/mkdocstrings/commit/7205ac6cf2861db61c2a5b8bf07d0e6b1a7f49fb) by Timothée Mazzucotelli). - -### Features -- Allow unwrapping the `

` tag in `convert_markdown` filter ([5351fc8](https://github.com/mkdocstrings/mkdocstrings/commit/5351fc8b417fb20f0681a22f49fcc902579eacdb) by Oleh Prypin). [PR #369](https://github.com/mkdocstrings/mkdocstrings/pull/369) -- Support handlers spanning multiple locations ([f42dfc6](https://github.com/mkdocstrings/mkdocstrings/commit/f42dfc61ce4f9f317c4bd17f568e504ed9764d35) by Timothée Mazzucotelli). [PR #355](https://github.com/mkdocstrings/mkdocstrings/pull/355) - -### Code Refactoring -- Prefix logs with the package name only ([6c2b734](https://github.com/mkdocstrings/mkdocstrings/commit/6c2b7348ae40989e4adccc087feae599fcea949d) by Timothée Mazzucotelli). [PR #375](https://github.com/mkdocstrings/mkdocstrings/pull/375) -- Extract the Python handler into its own repository ([74371e4](https://github.com/mkdocstrings/mkdocstrings/commit/74371e49c32059fefd34c7cc7f7b8f085b383237) by Timothée Mazzucotelli). [PR #356](https://github.com/mkdocstrings/mkdocstrings/pull/356) -- Support Jinja2 3.1 ([b377227](https://github.com/mkdocstrings/mkdocstrings/commit/b37722716b1e0ed6393ec71308dfb0f85e142f3b) by Timothée Mazzucotelli). [Issue #360](https://github.com/mkdocstrings/mkdocstrings/issues/360), [PR #361](https://github.com/mkdocstrings/mkdocstrings/pull/361) -- Find templates in new and deprecated namespaces ([d5d5f18](https://github.com/mkdocstrings/mkdocstrings/commit/d5d5f1844dbac3affacc95f2f3eab57a61d2068c) by Timothée Mazzucotelli). [PR #367](https://github.com/mkdocstrings/mkdocstrings/pull/367) -- Support loading handlers from the `mkdocstrings_handlers` namespace ([5c22c6c](https://github.com/mkdocstrings/mkdocstrings/commit/5c22c6ce4e056ac2334e2dfcd47c1f1a7884d352) by Timothée Mazzucotelli). [PR #367](https://github.com/mkdocstrings/mkdocstrings/pull/367) - - -## [0.17.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.17.0) - 2021-12-27 - -[Compare with 0.16.2](https://github.com/mkdocstrings/mkdocstrings/compare/0.16.2...0.17.0) - -### Features -- Add `show_signature` rendering option ([024ee82](https://github.com/mkdocstrings/mkdocstrings/commit/024ee826bb6f0aa297ba857bc18075d6f4162cad) by Will Da Silva). [Issue #341](https://github.com/mkdocstrings/mkdocstrings/issues/341), [PR #342](https://github.com/mkdocstrings/mkdocstrings/pull/342) -- Support Keyword Args and Yields sections ([1286427](https://github.com/mkdocstrings/mkdocstrings/commit/12864271b7f997af7b421a834919b1e686793905) by Timothée Mazzucotelli). [Issue #205](https://github.com/mkdocstrings/mkdocstrings/issues/205) and [#324](https://github.com/mkdocstrings/mkdocstrings/issues/324), [PR #331](https://github.com/mkdocstrings/mkdocstrings/pull/331) - -### Bug Fixes -- Do minimum work when falling back to re-collecting an object to get its anchor ([f6cf570](https://github.com/mkdocstrings/mkdocstrings/commit/f6cf570255df17db1088b6e6cd94bcc823b3b17f) by Timothée Mazzucotelli). [Issue #329](https://github.com/mkdocstrings/mkdocstrings/issues/329), [PR #330](https://github.com/mkdocstrings/mkdocstrings/pull/330) - -### Code Refactoring -- Return multiple identifiers from fallback method ([78c498c](https://github.com/mkdocstrings/mkdocstrings/commit/78c498c4a6cfc33cc6ceab9829426bd64e518d44) by Timothée Mazzucotelli). [Issue mkdocstrings/autorefs#11](https://github.com/mkdocstrings/autorefs/issues/11), [PR #350](https://github.com/mkdocstrings/mkdocstrings/pull/350) - - -## [0.16.2](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.16.2) - 2021-10-04 - -[Compare with 0.16.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.16.1...0.16.2) - -### Dependencies -- Support `pymdown-extensions` v9.x ([0831343](https://github.com/mkdocstrings/mkdocstrings/commit/0831343aa8726ed785b17bba1c8d4adf49b46748) by Ofek Lev and [38b22ec](https://github.com/mkdocstrings/mkdocstrings/commit/38b22ec11cded4689115dafc43e16a1e8e40feda) by Timothée Mazzucotelli). - - -## [0.16.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.16.1) - 2021-09-23 - -[Compare with 0.16.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.16.0...0.16.1) - -### Bug Fixes -- Fix ReadTheDocs "return" template ([598621b](https://github.com/mkdocstrings/mkdocstrings/commit/598621bff29d2aeda0e14f350cda36c1a1f418d5) by Timothée Mazzucotelli). - - -## [0.16.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.16.0) - 2021-09-20 - -[Compare with 0.15.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.15.0...0.16.0) - -### Features -- Add a rendering option to change the sorting of members ([b1fff8b](https://github.com/mkdocstrings/mkdocstrings/commit/b1fff8b8ef4d6d77417fc43ed8be4b578d6437e4) by Joe Rickerby). [Issue #114](https://github.com/mkdocstrings/mkdocstrings/issues/114), [PR #274](https://github.com/mkdocstrings/mkdocstrings/pull/274) -- Add option to show Python base classes ([436f550](https://github.com/mkdocstrings/mkdocstrings/commit/436f5504ad72ab6d1f5b4303e6b68bc84562c32b) by Brian Koropoff). [Issue #269](https://github.com/mkdocstrings/mkdocstrings/issues/269), [PR #297](https://github.com/mkdocstrings/mkdocstrings/pull/297) -- Support loading external Python inventories in Sphinx format ([a8418cb](https://github.com/mkdocstrings/mkdocstrings/commit/a8418cb4c6193d35cdc72508b118a712cf0334e1) by Oleh Prypin). [PR #287](https://github.com/mkdocstrings/mkdocstrings/pull/287) -- Support loading external inventories and linking to them ([8b675f4](https://github.com/mkdocstrings/mkdocstrings/commit/8b675f4671f8bbfd2f337ed043e3682b0a0ad0f6) by Oleh Prypin). [PR #277](https://github.com/mkdocstrings/mkdocstrings/pull/277) -- Very basic support for MkDocs theme ([974ca90](https://github.com/mkdocstrings/mkdocstrings/commit/974ca9010efca1b8279767faf8efcd2470a8371d) by Oleh Prypin). [PR #272](https://github.com/mkdocstrings/mkdocstrings/pull/272) -- Generate objects inventory ([14ed959](https://github.com/mkdocstrings/mkdocstrings/commit/14ed959860a784a835cd71f911081f2026d66c81) and [bbd85a9](https://github.com/mkdocstrings/mkdocstrings/commit/bbd85a92fa70bddfe10a907a4d63b8daf0810cb2) by Timothée Mazzucotelli). [Issue #251](https://github.com/mkdocstrings/mkdocstrings/issues/251), [PR #253](https://github.com/mkdocstrings/mkdocstrings/pull/253) - -### Bug Fixes -- Don't render empty code blocks for missing type annotations ([d2e9e1e](https://github.com/mkdocstrings/mkdocstrings/commit/d2e9e1ef3cf304081b07f763843a9722bf9b117e) by Oleh Prypin). -- Fix custom handler not being used ([6dcf342](https://github.com/mkdocstrings/mkdocstrings/commit/6dcf342fb83b19e385d56d37235f2b23e8c8c767) by Timothée Mazzucotelli). [Issue #259](https://github.com/mkdocstrings/mkdocstrings/issues/259), [PR #263](https://github.com/mkdocstrings/mkdocstrings/pull/263) -- Don't hide `setup_commands` errors ([92418c4](https://github.com/mkdocstrings/mkdocstrings/commit/92418c4b3e80b67d5116efa73931fc113daa60e9) by Gabriel Vîjială). [PR #258](https://github.com/mkdocstrings/mkdocstrings/pull/258) - -### Code Refactoring -- Move writing extra files to an earlier stage in the build ([3890ab5](https://github.com/mkdocstrings/mkdocstrings/commit/3890ab597692e56d7ece576c166373b66ff4e615) by Oleh Prypin). [PR #275](https://github.com/mkdocstrings/mkdocstrings/pull/275) - - -## [0.15.2](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.15.2) - 2021-06-09 - -[Compare with 0.15.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.15.1...0.15.2) - -### Packaging -- MkDocs default schema needs to be obtained differently now ([b3e122b](https://github.com/mkdocstrings/mkdocstrings/commit/b3e122b36d586632738ddedaed7d3df8d5dead44) by Oleh Prypin). [PR #273](https://github.com/mkdocstrings/mkdocstrings/pull/273) -- Compatibility with MkDocs 1.2: livereload isn't guaranteed now ([36e8024](https://github.com/mkdocstrings/mkdocstrings/commit/36e80248d2ab9e61975f6c83ae517115c9410fc1) by Oleh Prypin). [PR #294](https://github.com/mkdocstrings/mkdocstrings/pull/294) - - -## [0.15.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.15.1) - 2021-05-16 - -[Compare with 0.15.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.15.0...0.15.1) - -### Bug Fixes -- Prevent error during parallel installations ([fac2c71](https://github.com/mkdocstrings/mkdocstrings/commit/fac2c711351f7b62bf5308f19cfc612a3944588a) by Timothée Mazzucotelli). - -### Packaging -- Support the upcoming major Jinja and MarkupSafe releases ([bb4f9de](https://github.com/mkdocstrings/mkdocstrings/commit/bb4f9de08a77bef85e550d70deb0db13e6aa0c96) by Oleh Prypin). [PR #283](https://github.com/mkdocstrings/mkdocstrings/pull/283) -- Accept a higher version of mkdocs-autorefs ([c8de08e](https://github.com/mkdocstrings/mkdocstrings/commit/c8de08e177f78290d3baaca2716d1ec64c9059b6) by Oleh Prypin). [PR #282](https://github.com/mkdocstrings/mkdocstrings/pull/282) - - -## [0.15.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.15.0) - 2021-02-28 - -[Compare with 0.14.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.14.0...0.15.0) - -### Breaking Changes - -The following items are *possible* breaking changes: - -- Cross-linking to arbitrary headings now requires to opt-in to the *autorefs* plugin, - which is installed as a dependency of *mkdocstrings*. - See [Cross-references to any Markdown heading](https://mkdocstrings.github.io/usage/#cross-references-to-any-markdown-heading). -- *mkdocstrings* now respects your configured code highlighting method, - so if you are using the CodeHilite extension, the `.highlight` CSS class in the rendered HTML will become `.codehilite`. - So make sure to adapt your extra CSS accordingly. Or just switch to using [pymdownx.highlight](https://facelessuser.github.io/pymdown-extensions/extensions/highlight/), it's better supported by *mkdocstrings* anyway. - See [Syntax highlighting](https://mkdocstrings.github.io/theming/#syntax-highlighting). -- Most of the [CSS rules that *mkdocstrings* used to recommend](https://mkdocstrings.github.io/handlers/python/#recommended-style-material) for manual addition, now become mandatory (auto-injected into the site). This shouldn't *break* any of your styles, but you are welcome to remove the now-redundant lines that you had copied into `extra_css`, [similarly to this diff](https://github.com/mkdocstrings/mkdocstrings/pull/218/files#diff-7889a1924c66ff9318f1d81c4a3b75658d09bebf0db3b2e4023ba3e40294eb73). - -### Features -- Nicer-looking error outputs - no tracebacks from mkdocstrings ([6baf720](https://github.com/mkdocstrings/mkdocstrings/commit/6baf720850d359ddb55713553a757fe7b2283e10) by Oleh Prypin). [PR #230](https://github.com/mkdocstrings/mkdocstrings/pull/230) -- Let handlers add CSS to the pages, do so for Python handler ([05c7a3f](https://github.com/mkdocstrings/mkdocstrings/commit/05c7a3fc83b67d3244ea3bfe97dab19aa53f2d38) by Oleh Prypin). [Issue #189](https://github.com/mkdocstrings/mkdocstrings/issues/189), [PR #218](https://github.com/mkdocstrings/mkdocstrings/pull/218) -- Allow linking to an object heading not only by its canonical identifier, but also by its possible aliases ([4789950](https://github.com/mkdocstrings/mkdocstrings/commit/4789950ff43c354d47afbed5c89d5abb917ffee6) by Oleh Prypin). [PR #217](https://github.com/mkdocstrings/mkdocstrings/pull/217) - -### Bug Fixes -- Propagate the CSS class to inline highlighting as well ([c7d80e6](https://github.com/mkdocstrings/mkdocstrings/commit/c7d80e63a042913b7511c38a788967796dd10997) by Oleh Prypin). [PR #245](https://github.com/mkdocstrings/mkdocstrings/pull/245) -- Don't double-escape characters in highlighted headings ([6357144](https://github.com/mkdocstrings/mkdocstrings/commit/6357144b100be6a2e7e6140e035c289c225cec22) by Oleh Prypin). [Issue #228](https://github.com/mkdocstrings/mkdocstrings/issues/228), [PR #241](https://github.com/mkdocstrings/mkdocstrings/pull/241) - -### Code Refactoring -- Use the autorefs plugin from its new external location ([e2d74ef](https://github.com/mkdocstrings/mkdocstrings/commit/e2d74efb0d59f9a1aa45e42525ceb1d4b7638426) by Oleh Prypin). [PR #235](https://github.com/mkdocstrings/mkdocstrings/pull/235) -- Split out Markdown extensions from `handlers` to `handlers.rendering` ([7533852](https://github.com/mkdocstrings/mkdocstrings/commit/7533852e3ac0a378b70a380cef1100421b7d5763) by Oleh Prypin). [PR #233](https://github.com/mkdocstrings/mkdocstrings/pull/233) -- Theme-agnostic code highlighting, respecting configs ([f9ea009](https://github.com/mkdocstrings/mkdocstrings/commit/f9ea00979545e39983ba377f1930d73ae94165ea) by Oleh Prypin). [PR #202](https://github.com/mkdocstrings/mkdocstrings/pull/202) -- Split out autorefs plugin, make it optional ([fc67656](https://github.com/mkdocstrings/mkdocstrings/commit/fc676564f9b11269b3e0b0482703ac924069a3fa) by Oleh Prypin). [PR #220](https://github.com/mkdocstrings/mkdocstrings/pull/220) -- Remove the extra wrapper div from the final doc ([7fe438c](https://github.com/mkdocstrings/mkdocstrings/commit/7fe438c4040a2124b00c39e582ef4c38be7c55c9) by Oleh Prypin). [PR #209](https://github.com/mkdocstrings/mkdocstrings/pull/209) -- Don't re-parse the whole subdoc, expose only headings ([15f84f9](https://github.com/mkdocstrings/mkdocstrings/commit/15f84f981982c8e2b15498f5c869ac207f3ce5d7) by Oleh Prypin). [PR #209](https://github.com/mkdocstrings/mkdocstrings/pull/209) -- Actually exclude hidden headings from the doc ([0fdb082](https://github.com/mkdocstrings/mkdocstrings/commit/0fdb0821867eb0e14a972a603c22301aafecf4f4) by Oleh Prypin). [PR #209](https://github.com/mkdocstrings/mkdocstrings/pull/209) - - -## [0.14.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.14.0) - 2021-01-06 - -[Compare with 0.13.6](https://github.com/pawamoy/mkdocstrings/compare/0.13.6...0.14.0) - -Special thanks to Oleh [@oprypin](https://github.com/oprypin) Prypin who did an amazing job (this is a euphemism) -at improving *mkdocstrings*, fixing hard-to-fix bugs with clever solutions, implementing great new features -and refactoring the code for better performance and readability! Thanks Oleh! - -### Bug Fixes -- Fix double code tags ([e84d401](https://github.com/pawamoy/mkdocstrings/commit/e84d401c6dcb9aecb8cc1a58d3a0f339e1c3e78f) by Timothée Mazzucotelli). -- Don't mutate the original Markdown config for permalinks ([8f6b163](https://github.com/pawamoy/mkdocstrings/commit/8f6b163b50551da22f65e9b736e042562f77f2d7) by Oleh Prypin). -- Preserve text immediately before an autodoc ([07466fa](https://github.com/pawamoy/mkdocstrings/commit/07466fafb54963a4e35e69007b6291a0382aaeb4) by Oleh Prypin). [PR #207](https://github.com/pawamoy/mkdocstrings/pull/207) -- Remove `href` attributes from headings in templates ([d5602ff](https://github.com/pawamoy/mkdocstrings/commit/d5602ff3bb1a75ac1c8c457e972271b6c66eb8dd) by Oleh Prypin). [PR #204](https://github.com/pawamoy/mkdocstrings/pull/204) -- Don't let `toc` extension append its permalink twice ([a154f5c](https://github.com/pawamoy/mkdocstrings/commit/a154f5c4c6ef9abd221e1f89e44847ae2cf25436) by Oleh Prypin). [PR #203](https://github.com/pawamoy/mkdocstrings/pull/203) -- Fix undefined entity for `¶` ([2c29211](https://github.com/pawamoy/mkdocstrings/commit/2c29211002d515db40e5bdabf6cbf32ec8633a05) by Timothée Mazzucotelli). -- Make ids of Markdown sub-documents prefixed with the parent item id ([d493d33](https://github.com/pawamoy/mkdocstrings/commit/d493d33b3827d93e84a7b2e39f0a10dfcb782402) by Oleh Prypin). [Issue #186](https://github.com/pawamoy/mkdocstrings/issues/186) and [#193](https://github.com/pawamoy/mkdocstrings/issues/193), [PR #199](https://github.com/pawamoy/mkdocstrings/pull/199) -- More lenient regex for data-mkdocstrings-identifier ([dcfec8e](https://github.com/pawamoy/mkdocstrings/commit/dcfec8edfdff050debc5856dfc213d3119a84792) by Oleh Prypin). -- Shift Markdown headings according to the current `heading_level` ([13f41ae](https://github.com/pawamoy/mkdocstrings/commit/13f41aec5a95c82c1229baa4ac3caf4abb2add51) by Oleh Prypin). [Issue #192](https://github.com/pawamoy/mkdocstrings/issues/192), [PR #195](https://github.com/pawamoy/mkdocstrings/pull/195) -- Fix footnotes appearing in all following objects ([af24bc2](https://github.com/pawamoy/mkdocstrings/commit/af24bc246a6938ebcae7cf6ff677b194cf1af95c) by Oleh Prypin). [Issue #186](https://github.com/pawamoy/mkdocstrings/issues/186), [PR #195](https://github.com/pawamoy/mkdocstrings/pull/195) -- Fix cross-references from the root index page ([9c9f2a0](https://github.com/pawamoy/mkdocstrings/commit/9c9f2a04af94e0d88f57fd76249f7985166a9b88) by Oleh Prypin). [Issue #184](https://github.com/pawamoy/mkdocstrings/issues/184), [PR #185](https://github.com/pawamoy/mkdocstrings/pull/185) -- Fix incorrect argument name passed to Markdown ([10ce502](https://github.com/pawamoy/mkdocstrings/commit/10ce502d5fd58f1e5a4e14308ffad1bc3d7116ee) by Timothée Mazzucotelli). -- Fix error when a digit immediately follows a code tag ([9b92341](https://github.com/pawamoy/mkdocstrings/commit/9b9234160edc54b53c81a618b12095e7dd829059) by Oleh Prypin). [Issue #169](https://github.com/pawamoy/mkdocstrings/issues/169), [PR #175](https://github.com/pawamoy/mkdocstrings/pull/175) -- Detecting paths relative to template directory in logging ([a50046b](https://github.com/pawamoy/mkdocstrings/commit/a50046b5d58d62df4ba13f4c197e80edd1995eb9) by Oleh Prypin). [Issue #166](https://github.com/pawamoy/mkdocstrings/issues/166) - -### Code Refactoring -- BlockProcessor already receives strings, use them as such ([bcf7da9](https://github.com/pawamoy/mkdocstrings/commit/bcf7da911a310a63351c5082e84bb763d90d5b3b) by Oleh Prypin). -- Remove some unused code ([8504084](https://github.com/pawamoy/mkdocstrings/commit/850408421cc027be8374673cc74c71fff26f3833) by Oleh Prypin). [PR #206](https://github.com/pawamoy/mkdocstrings/pull/206) -- Improve XML parsing error handling ([ad86410](https://github.com/pawamoy/mkdocstrings/commit/ad864100b644ab1ee8daaa0d3923bc87dee1c5ca) by Timothée Mazzucotelli). -- Explicitly use MarkupSafe ([6b9ebe7](https://github.com/pawamoy/mkdocstrings/commit/6b9ebe7d510e82971acef89e9e946af3c0cc96d3) by Oleh Prypin). -- Split out the handler cache, expose it through the plugin ([6453026](https://github.com/pawamoy/mkdocstrings/commit/6453026fac287387090a67cce70c078377d107dd) by Oleh Prypin). [Issue #179](https://github.com/pawamoy/mkdocstrings/issues/179), [PR #191](https://github.com/pawamoy/mkdocstrings/pull/191) -- Use ChainMap instead of copying dicts ([c634d2c](https://github.com/pawamoy/mkdocstrings/commit/c634d2ce6377de26caa553048bb28ef1e672f7aa) by Oleh Prypin). [PR #171](https://github.com/pawamoy/mkdocstrings/pull/171) -- Rename logging to loggers to avoid confusion ([7a119cc](https://github.com/pawamoy/mkdocstrings/commit/7a119ccf27cf77cf2cbd114e7fad0a9e4e97bbd8) by Timothée Mazzucotelli). -- Simplify logging ([409f93e](https://github.com/pawamoy/mkdocstrings/commit/409f93ed26d7d8292a8bc7a6c32cb270b3769409) by Timothée Mazzucotelli). - -### Features -- Allow specifying `heading_level` as a Markdown heading ([10efc28](https://github.com/pawamoy/mkdocstrings/commit/10efc281e04b2a430cec53e49208ccc09e591667) by Oleh Prypin). [PR #170](https://github.com/pawamoy/mkdocstrings/pull/170) -- Allow any characters in identifiers ([7ede68a](https://github.com/pawamoy/mkdocstrings/commit/7ede68a0917b494eda2198931a8ad1c97fc8fce4) by Oleh Prypin). [PR #174](https://github.com/pawamoy/mkdocstrings/pull/174) -- Allow namespace packages for handlers ([39b0465](https://github.com/pawamoy/mkdocstrings/commit/39b046548f57dc59993241b24d2cf12fb5e488eb) by Timothée Mazzucotelli). -- Add template debugging/logging ([33b32c1](https://github.com/pawamoy/mkdocstrings/commit/33b32c1410bf6e8432768865c8aa86b8e091ab59) by Timothée Mazzucotelli). -- Add initial support for the ReadTheDocs theme ([1028115](https://github.com/pawamoy/mkdocstrings/commit/1028115682ed0806d6570c749af0e382c67d6120) by Timothée Mazzucotelli). [Issue #107](https://github.com/pawamoy/mkdocstrings/issues/107), [PR #159](https://github.com/pawamoy/mkdocstrings/pull/159) -- Add option to show type annotations in signatures ([f94ce9b](https://github.com/pawamoy/mkdocstrings/commit/f94ce9bdb2afc2c41c21a53636980ca077b757ce) by Timothée Mazzucotelli). [Issue #106](https://github.com/pawamoy/mkdocstrings/issues/106) - -### Packaging -- Accept verions of `pytkdocs` up to 0.10.x (see [changelog](https://pawamoy.github.io/pytkdocs/changelog/#0100-2020-12-06)). - -### Performance Improvements -- Call `update_env` only once per `Markdown` instance ([b198c74](https://github.com/pawamoy/mkdocstrings/commit/b198c74338dc3b54b999eadeef9946d69277ad77) by Oleh Prypin). [PR #201](https://github.com/pawamoy/mkdocstrings/pull/201) -- Disable Jinja's `auto_reload` to reduce disk reads ([3b28c58](https://github.com/pawamoy/mkdocstrings/commit/3b28c58c77642071419d4a98e007d5a854b7984f) by Oleh Prypin). [PR #200](https://github.com/pawamoy/mkdocstrings/pull/200) -- Rework autorefs replacement to not re-parse the final HTML ([22a9e4b](https://github.com/pawamoy/mkdocstrings/commit/22a9e4bf1b73f9b9b1a7c4876f0c677f919bc4d7) by Oleh Prypin). [Issue #187](https://github.com/pawamoy/mkdocstrings/issues/187), [PR #188](https://github.com/pawamoy/mkdocstrings/pull/188) - - -## [0.13.6](https://github.com/pawamoy/mkdocstrings/releases/tag/0.13.6) - 2020-09-28 - -[Compare with 0.13.5](https://github.com/pawamoy/mkdocstrings/compare/0.13.5...0.13.6) - -### Bug Fixes -- Fix rendering when clicking on hidden toc entries ([2af4d31](https://github.com/pawamoy/mkdocstrings/commit/2af4d310adefec614235a2c1d04d5ff56bf9c220) by Timothée Mazzucotelli). Issue [#60](https://github.com/pawamoy/mkdocstrings/issues/60). - - -## [0.13.5](https://github.com/pawamoy/mkdocstrings/releases/tag/0.13.5) - 2020-09-28 - -[Compare with 0.13.4](https://github.com/pawamoy/mkdocstrings/compare/0.13.4...0.13.5) - -## Packaging -- Accept `pytkdocs` version up to 0.9.x ([changelog](https://pawamoy.github.io/pytkdocs/changelog/#090-2020-09-28)). - - -## [0.13.4](https://github.com/pawamoy/mkdocstrings/releases/tag/0.13.4) - 2020-09-25 - -[Compare with 0.13.3](https://github.com/pawamoy/mkdocstrings/compare/0.13.3...0.13.4) - -### Bug Fixes -- Bring back arbitrary `**config` to Python handler ([fca7d4c](https://github.com/pawamoy/mkdocstrings/commit/fca7d4c75ffd7a84eaeccd27facd5575604dbfab) by Florimond Manca). Issue [#154](https://github.com/pawamoy/mkdocstrings/issues/154), PR [#155](https://github.com/pawamoy/mkdocstrings/pull/155) - - -## [0.13.3](https://github.com/pawamoy/mkdocstrings/releases/tag/0.13.3) - 2020-09-25 - -[Compare with 0.13.2](https://github.com/pawamoy/mkdocstrings/compare/0.13.2...0.13.3) - -### Packaging -- Accept `pytkdocs` version up to 0.8.x ([changelog](https://pawamoy.github.io/pytkdocs/changelog/#080-2020-09-25)). - - -## [0.13.2](https://github.com/pawamoy/mkdocstrings/releases/tag/0.13.2) - 2020-09-08 - -[Compare with 0.13.1](https://github.com/pawamoy/mkdocstrings/compare/0.13.1...0.13.2) - -### Bug Fixes -- Fix relative URLs when `use_directory_urls` is false ([421d189](https://github.com/pawamoy/mkdocstrings/commit/421d189fff9ea2608e40d85e0a93e30334782b90) by Timothée Mazzucotelli). References: [#149](https://github.com/pawamoy/mkdocstrings/issues/149) - - -## [0.13.1](https://github.com/pawamoy/mkdocstrings/releases/tag/0.13.1) - 2020-09-03 - -[Compare with 0.13.0](https://github.com/pawamoy/mkdocstrings/compare/0.13.0...0.13.1) - -### Bug Fixes -- Use relative links for cross-references ([9c77f1f](https://github.com/pawamoy/mkdocstrings/commit/9c77f1f461fa87842ae39945f9521ee85b1e413b) by Timothée Mazzucotelli). References: [#144](https://github.com/pawamoy/mkdocstrings/issues/144), [#147](https://github.com/pawamoy/mkdocstrings/issues/147) - - -## [0.13.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.13.0) - 2020-08-21 - -[Compare with 0.12.2](https://github.com/pawamoy/mkdocstrings/compare/0.12.2...0.13.0) - -### Bug Fixes -- Accept dashes in module names ([fcf79d0](https://github.com/pawamoy/mkdocstrings/commit/fcf79d0024ec46c3862c94202864e054c04a6d0b) by Timothée Mazzucotelli). References: [#140](https://github.com/pawamoy/mkdocstrings/issues/140) - -### Features -- Add option to show full path of direct members only ([d1b9401](https://github.com/pawamoy/mkdocstrings/commit/d1b9401afecb20d3123eec7334605cb15bf9d877) by Aaron Dunmore). References: [#134](https://github.com/pawamoy/mkdocstrings/issues/134), [#136](https://github.com/pawamoy/mkdocstrings/issues/136) - -### Packaging -- Accept `pymdown-extensions` versions up to 0.8.x ([see release notes](https://facelessuser.github.io/pymdown-extensions/about/releases/8.0/#8.0)) ([178d48d](https://github.com/pawamoy/mkdocstrings/commit/178d48da7a62daf285dfc5f6ff230e8bce82ed53) by Hugo van Kemenade). PR [#146](https://github.com/pawamoy/mkdocstrings/issue/146) - - -## [0.12.2](https://github.com/pawamoy/mkdocstrings/releases/tag/0.12.2) - 2020-07-24 - -[Compare with 0.12.1](https://github.com/pawamoy/mkdocstrings/compare/0.12.1...0.12.2) - -### Packaging -- Accept `pytkdocs` version up to 0.7.x ([changelog](https://pawamoy.github.io/pytkdocs/changelog/#070-2020-07-24)). - - -## [0.12.1](https://github.com/pawamoy/mkdocstrings/releases/tag/0.12.1) - 2020-07-07 - -[Compare with 0.12.0](https://github.com/pawamoy/mkdocstrings/compare/0.12.0...0.12.1) - -### Bug Fixes -- Fix HTML-escaped sequence parsing as XML ([db297f1](https://github.com/pawamoy/mkdocstrings/commit/db297f19013fc402eeff1f2827057a959e481c66) by Timothée Mazzucotelli). -- Allow running mkdocs from non-default interpreter ([283dd7b](https://github.com/pawamoy/mkdocstrings/commit/283dd7b83eeba675a16d96d2e829851c1273a625) by Jared Khan). - - -## [0.12.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.12.0) - 2020-06-14 - -[Compare with 0.11.4](https://github.com/pawamoy/mkdocstrings/compare/0.11.4...0.12.0) - -### Features -- Support attributes section in Google-style docstrings ([8300253](https://github.com/pawamoy/mkdocstrings/commit/83002532b2294ea33dcec4f2672a5a6d0f64def1) by Timothée Mazzucotelli). References: [#88](https://github.com/pawamoy/mkdocstrings/issues/88) -- Support examples section in Google-style docstrings ([650c754](https://github.com/pawamoy/mkdocstrings/commit/650c754afdd5d4fb96b1e2529f378d025a2e7daf) by Iago González). References: [#112](https://github.com/pawamoy/mkdocstrings/issues/112) - -### Packaging -- Accept `pytkdocs` version up to 0.6.x ([changelog](https://pawamoy.github.io/pytkdocs/changelog/#060-2020-06-14)). - -## [0.11.4](https://github.com/pawamoy/mkdocstrings/releases/tag/0.11.4) - 2020-06-08 - -[Compare with 0.11.3](https://github.com/pawamoy/mkdocstrings/compare/0.11.3...0.11.4) - -### Packaging -- Accept `pytkdocs` version up to 0.5.x ([changelog](https://pawamoy.github.io/pytkdocs/changelog/#050-2020-06-08)). - If it breaks your docs, please [open issues on `pytkdocs`' bug-tracker](https://github.com/pawamoy/pytkdocs/issues), - or pin `pytkdocs` version to while waiting for bug fixes <0.5.0 :clown:. - - -## [0.11.3](https://github.com/pawamoy/mkdocstrings/releases/tag/0.11.3) - 2020-06-07 - -[Compare with 0.11.2](https://github.com/pawamoy/mkdocstrings/compare/0.11.2...0.11.3) - -### Bug Fixes -- Support custom theme directory configuration ([1243cf6](https://github.com/pawamoy/mkdocstrings/commit/1243cf673aaf371e5cbf42a3e0d1aa80482398a3) by Abhishek Thakur). References: [#120](https://github.com/pawamoy/mkdocstrings/issues/120), [#121](https://github.com/pawamoy/mkdocstrings/issues/121) - - -## [0.11.2](https://github.com/pawamoy/mkdocstrings/releases/tag/0.11.2) - 2020-05-20 - -[Compare with 0.11.1](https://github.com/pawamoy/mkdocstrings/compare/0.11.1...0.11.2) - -### Packaging -- Increase `pytkdocs` version range to accept 0.4.0 - ([changelog](https://pawamoy.github.io/pytkdocs/changelog/#040-2020-05-17)). - - -## [0.11.1](https://github.com/pawamoy/mkdocstrings/releases/tag/0.11.1) - 2020-05-14 - -[Compare with 0.11.0](https://github.com/pawamoy/mkdocstrings/compare/0.11.0...0.11.1) - -### Bug Fixes -- Fix integration with mkdocs logging *une bonne fois pour toute* ([3293cbf](https://github.com/pawamoy/mkdocstrings/commit/3293cbf161f05d36de6c1d50b5de9742bf99066e) by Timothée Mazzucotelli). -- Discard setup commands stdout ([ea44cea](https://github.com/pawamoy/mkdocstrings/commit/ea44cea33159ed3a6b0b34b4cd52a17a40bd6460) by Timothée Mazzucotelli). References: [#91](https://github.com/pawamoy/mkdocstrings/issues/91) -- Use the proper python executable to start subprocesses ([9fe3b39](https://github.com/pawamoy/mkdocstrings/commit/9fe3b3915bd8f15011f8f3632a227d1eb56603fd) by Reece Dunham). References: [#91](https://github.com/pawamoy/mkdocstrings/issues/91), [#103](https://github.com/pawamoy/mkdocstrings/issues/103) - - -## [0.11.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.11.0) - 2020-04-23 - -[Compare with 0.10.3](https://github.com/pawamoy/mkdocstrings/compare/0.10.3...0.11.0) - -### Bug Fixes -- Properly raise on errors (respect strict mode) ([2097208](https://github.com/pawamoy/mkdocstrings/commit/20972082a94b64bec02c77d6a80384d8042f60ea) by Timothée Mazzucotelli). Related issues/PRs: [#86](https://github.com/pawamoy/mkdocstrings/issues/86) -- Hook properly to MkDocs logging ([b23daed](https://github.com/pawamoy/mkdocstrings/commit/b23daed3743bbd2d3f024df34582a317c51a1af0) by Timothée Mazzucotelli). Related issues/PRs: [#86](https://github.com/pawamoy/mkdocstrings/issues/86) - -### Features -- Add `setup_commands` option to python handler ([599f8e5](https://github.com/pawamoy/mkdocstrings/commit/599f8e528f55093b0011b732da959b747c1e02c0) by Ross Mechanic). Related issues/PRs: [#89](https://github.com/pawamoy/mkdocstrings/issues/89), [#90](https://github.com/pawamoy/mkdocstrings/issues/90) -- Add option to allow overriding templates ([7360021](https://github.com/pawamoy/mkdocstrings/commit/7360021ab4753706d0f6209ed960050f5d424ad8) by Mikaël Capelle). Related issues/PRs: [#59](https://github.com/pawamoy/mkdocstrings/issues/59), [#82](https://github.com/pawamoy/mkdocstrings/issues/82) - - -## [0.10.3](https://github.com/pawamoy/mkdocstrings/releases/tag/0.10.3) - 2020-04-10 - -[Compare with 0.10.2](https://github.com/pawamoy/mkdocstrings/compare/0.10.2...0.10.3) - -### Bug Fixes -- Handle `site_url` not being defined ([9fb4a9b](https://github.com/pawamoy/mkdocstrings/commit/9fb4a9bbebe2457b389921ba1ee3e1f924c5691b) by Timothée Mazzucotelli). Related issues/PRs: [#77](https://github.com/pawamoy/mkdocstrings/issues/77) - -### Packaging -This version increases the accepted range of versions for the `pytkdocs` dependency to `>=0.2.0, <0.4.0`. -The `pytkdocs` project just released [version 0.3.0](https://pawamoy.github.io/pytkdocs/changelog/#030-2020-04-10) -which: - -- adds support for complex markup in docstrings sections items descriptions -- adds support for different indentations in docstrings sections (tabulations or less/more than 4 spaces) -- fixes docstring parsing for arguments whose names start with `*`, like `*args` and `**kwargs` - - -## [0.10.2](https://github.com/pawamoy/mkdocstrings/releases/tag/0.10.2) - 2020-04-07 - -[Compare with 0.10.1](https://github.com/pawamoy/mkdocstrings/compare/0.10.1...0.10.2) - -### Packaging -This version increases the accepted range of versions for the `pymdown-extensions` dependency, -as well as for the `mkdocs-material` development dependency. Indeed, both these projects recently -released major versions 7 and 5 respectively. Users who wish to use these new versions will be able to. -See issue [#74](https://github.com/pawamoy/mkdocstrings/issues/74). - -## [0.10.1](https://github.com/pawamoy/mkdocstrings/releases/tag/0.10.1) - 2020-04-03 - -[Compare with 0.10.0](https://github.com/pawamoy/mkdocstrings/compare/0.10.0...0.10.1) - -### Bug Fixes -- Fix jinja2 error for jinja2 < 2.11 ([387f970](https://github.com/pawamoy/mkdocstrings/commit/387f97088ad2b7b25389ae6cf303bae071e90e6c) by Timothée Mazzucotelli). Related issues/PRs: [#67](https://github.com/pawamoy/mkdocstrings/issues/67), [#72](https://github.com/pawamoy/mkdocstrings/issues/72) -- Fix missing dependency pymdown-extensions ([648b99d](https://github.com/pawamoy/mkdocstrings/commit/648b99dab9d1af87db474ce7683de50c9bf8996d) by Timothée Mazzucotelli). Related issues/PRs: [#66](https://github.com/pawamoy/mkdocstrings/issues/66) -- Fix heading level of hidden toc entries ([475cc62](https://github.com/pawamoy/mkdocstrings/commit/475cc62b1cf4342b82ca8685166306441e4b83c4) by Timothée Mazzucotelli). Related issues/PRs: [#65](https://github.com/pawamoy/mkdocstrings/issues/65) -- Fix rendering signatures containing keyword_only ([c6c5add](https://github.com/pawamoy/mkdocstrings/commit/c6c5addd8be65beaf7055c9d0f512e0de8b3eba4) by Timothée Mazzucotelli). Related issues/PRs: [#68](https://github.com/pawamoy/mkdocstrings/issues/68) - - -## [0.10.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.10.0) - 2020-03-27 - -[Compare with 0.9.1](https://github.com/pawamoy/mkdocstrings/compare/0.9.1...0.10.0) - -### Features -- Prepare for new `pytkdocs` version ([336421a](https://github.com/pawamoy/mkdocstrings/commit/336421af95d752671276c2e88c5c173bff4093cc)). - Add options `filters` and `members` to the Python collector to reflect the new `pytkdocs` options. - -## [0.9.1](https://github.com/pawamoy/mkdocstrings/releases/tag/0.9.1) - 2020-03-21 - -[Compare with 0.9.0](https://github.com/pawamoy/mkdocstrings/compare/0.9.0...0.9.1) - -### Bug fixes -- Fix cross-references when deploying to GitHub pages ([36f804b](https://github.com/pawamoy/mkdocstrings/commit/36f804beab01531c0331ed89d21f3e5e15bd8585)). - - -## [0.9.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.9.0) - 2020-03-21 - -[Compare with 0.8.0](https://github.com/pawamoy/mkdocstrings/compare/0.8.0...0.9.0) - -This version is a big refactor. We will just list the new features without pointing to particular commits. -The documentation rendering looks slightly different, and should be better than before. -No identified breaking changes for end-users. - -### Features -- **Language agnostic:** we moved the code responsible for loading Python documentation into a new project, - [`pytkdocs`](https://github.com/pawamoy/pytkdocs), and implemented a "handlers" logic, effectively allowing to - support any given language. Waiting for your handlers contributions :wink:! -- **Multiple themes support:** handlers can offer templates for multiple `mkdocs` themes. -- **Better cross-references:** cross-references now not only work between documented objects (between all languages, - given the objects' identifiers are unique), but also for every heading of your Markdown pages. -- **Configuration options:** the rendering of Python documentation can now be configured, - (globally and locally thanks to the handlers system). - Also see the [recommended CSS](https://pawamoy.github.io/mkdocstrings/handlers/python/#recommended-style). -- **Proper logging messages:** `mkdocstrings` now logs debug, warning and error messages, useful when troubleshooting. - -### Bug fixes -- Various fixes and better error handling. - - -## [0.8.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.8.0) - 2020-03-04 - -[Compare with 0.7.2](https://github.com/pawamoy/mkdocstrings/compare/0.7.2...0.8.0) - -### Breaking Changes -- Be compatible with Mkdocs >= 1.1 ([5a974a4](https://github.com/pawamoy/mkdocstrings/commit/5a974a4eb810904d6836e216d8539affc8acaa6f)). - This is a breaking change as we're not compatible with versions of Mkdocs below 1.1 anymore. - If you cannot upgrade Mkdocs to 1.1, pin mkdocstrings' version to 0.7.2. - -## [0.7.2](https://github.com/pawamoy/mkdocstrings/releases/tag/0.7.2) - 2020-03-04 - -[Compare with 0.7.1](https://github.com/pawamoy/mkdocstrings/compare/0.7.1...0.7.2) - -### Bug Fixes -- Catch `OSError` when trying to get source lines ([8e8d604](https://github.com/pawamoy/mkdocstrings/commit/8e8d604ba95363c140841c84535d2350d7ebbfe3)). -- Do not render signature empty sentinel ([16dfd73](https://github.com/pawamoy/mkdocstrings/commit/16dfd73cf30d01314dba756d3f10308b99c87dcc)). -- Fix for nested classes and their attributes ([7fef903](https://github.com/pawamoy/mkdocstrings/commit/7fef9037c5299d6106347b0db29f85a644f85c16)). -- Fix `relative_file_path` method ([52715ad](https://github.com/pawamoy/mkdocstrings/commit/52715adc59fe2e26a9e91df88bac8b8b32d4635e)). -- Wrap file path in backticks to escape it ([2525f39](https://github.com/pawamoy/mkdocstrings/commit/2525f39ad8c181679fa33db8e6dfaa28eb39c289)). - -## [0.7.1](https://github.com/pawamoy/mkdocstrings/releases/tag/0.7.1) - 2020-02-18 - -[Compare with 0.7.0](https://github.com/pawamoy/mkdocstrings/compare/0.7.0...0.7.1) - -### Bug Fixes -- Replace literal slash with os.sep for Windows compatibility ([70f9af5](https://github.com/pawamoy/mkdocstrings/commit/70f9af5e33cda694cda33870c84a770c853d84b5)). - - -## [0.7.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.7.0) - 2020-01-13 - -[Compare with 0.6.1](https://github.com/pawamoy/mkdocstrings/compare/0.6.1...0.7.0) - -### Bug Fixes -- Don't mark args or kwargs as required ([4049d6f](https://github.com/pawamoy/mkdocstrings/commit/4049d6f27c332b05db06bcfe6cc564f3c1c0f013)). -- Filter submodules ([7b11095](https://github.com/pawamoy/mkdocstrings/commit/7b110959529c5fc0275fb98c6d97e7c71e205900)). - -### Code Refactoring -- Don't guess lang in generated docs ([db4f60a](https://github.com/pawamoy/mkdocstrings/commit/db4f60a13dd0d191d7515683d7d42ae374b39fae)). -- Render at HTML step with custom markdown converter ([9b5a3e1](https://github.com/pawamoy/mkdocstrings/commit/9b5a3e126cd584893a8d0858ca9b67b8251e88f1)). - -### Features -- Change forward ref to ref, fix optional unions ([2f0bfaa](https://github.com/pawamoy/mkdocstrings/commit/2f0bfaabf367bfa513c20fb1230409306e43add2)). -- Discover package submodules ([231062a](https://github.com/pawamoy/mkdocstrings/commit/231062a3a107abc02134f102a06693969cf603ad)). -- Implement watched source code (hacks) ([4a67953](https://github.com/pawamoy/mkdocstrings/commit/4a67953c0af9da363d52ba058b3c51cf4cbfaabe)). - - -## [0.6.1](https://github.com/pawamoy/mkdocstrings/releases/tag/0.6.1) - 2020-01-02 - -[Compare with 0.6.0](https://github.com/pawamoy/mkdocstrings/compare/0.6.0...0.6.1) - -### Bug Fixes -- Break docstring discarding loop if found ([5a17fec](https://github.com/pawamoy/mkdocstrings/commit/5a17fec5beed2003d19ccdcb359b46b79bfcf469)). -- Fix discarding docstring ([143f7cb](https://github.com/pawamoy/mkdocstrings/commit/143f7cb00f02a7d3179cc5606ed7903566250227)). -- Fix getting annotation from nodes ([ecde72b](https://github.com/pawamoy/mkdocstrings/commit/ecde72bb22ccedb36aa847dd50504c63ad04498c)). -- Fix various things ([affbf06](https://github.com/pawamoy/mkdocstrings/commit/affbf064d457d4b626e8f67d38e94d7919bc2df2)). - -### Code Refactoring -- Break as soon as we find the same attr in a parent class while trying to discard the docstring ([65d7908](https://github.com/pawamoy/mkdocstrings/commit/65d7908f489ec465b2803ea8f55c70d0f9d7586b)). -- Split Docstring.parse method to improve readability ([2226e2d](https://github.com/pawamoy/mkdocstrings/commit/2226e2d55a6b9bbdd5a56183f1a9ba3c5f01b5ac)). - - -## [0.6.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.6.0) - 2019-12-28 - -[Compare with 0.5.0](https://github.com/pawamoy/mkdocstrings/compare/0.5.0...0.6.0) - -### Bug Fixes -- Fix GenericMeta import error on Python 3.7+ ([febf2b9](https://github.com/pawamoy/mkdocstrings/commit/febf2b9749d97cce80f5d20339372842fdffc908)). - -### Code Refactoring -- More classes. Still ugly code though :'( ([f41c119](https://github.com/pawamoy/mkdocstrings/commit/f41c11988d8d849a0310cca511c2d93a74cab86f)). -- Split into more modules ([f1872a4](https://github.com/pawamoy/mkdocstrings/commit/f1872a4c8d41a0b9603b7f344de3186110a4e1bd)). -- Use Object subclasses ([40dd106](https://github.com/pawamoy/mkdocstrings/commit/40dd1062188e6ad6ef6fbc12ddead2132fe6af1e)). - - -## [0.5.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.5.0) - 2019-12-22 - -[Compare with 0.4.0](https://github.com/pawamoy/mkdocstrings/compare/0.4.0...0.5.0) - -### Features -- Use divs in HTML contents to ease styling ([2a36a0e](https://github.com/pawamoy/mkdocstrings/commit/2a36a0eba7f52c43a3eba593ddd971acaa0a9c92)). - - -## [0.4.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.4.0) - 2019-12-22 - -[Compare with 0.3.0](https://github.com/pawamoy/mkdocstrings/compare/0.3.0...0.4.0) - -### Features -- Parse docstrings Google-style blocks, get types from signature ([5af0c7b](https://github.com/pawamoy/mkdocstrings/commit/5af0c7b766ea7158d603b44c6df278dbcd189864)). - - -## [0.3.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.3.0) - 2019-12-21 - -[Compare with 0.2.0](https://github.com/pawamoy/mkdocstrings/compare/0.2.0...0.3.0) - -### Features -- Allow object referencing in docstrings ([2dd50c0](https://github.com/pawamoy/mkdocstrings/commit/2dd50c06f96acaf0e2f969f217f0cbcfb1de2fd4)). - - -## [0.2.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.2.0) - 2019-12-15 - -[Compare with 0.1.0](https://github.com/pawamoy/mkdocstrings/compare/0.1.0...0.2.0) - -### Misc -- Refactor, features, etc. ([111fa85](https://github.com/pawamoy/mkdocstrings/commit/111fa85a6305a198ac4e19a75bb491b98683929c)). - - -## [0.1.0](https://github.com/pawamoy/mkdocstrings/releases/tag/0.1.0) - 2019-12-12 - -[Compare with first commit](https://github.com/pawamoy/mkdocstrings/compare/f1dd8fb2b4a4ae81f9144fe062ca9743ae82bd69...0.1.0) - -### Misc -- Clean up (delete unused files) ([c227043](https://github.com/pawamoy/mkdocstrings/commit/c227043814381b95031e426725e97106931f4ef9)). -- Clean up unused makefile rules ([edc01e9](https://github.com/pawamoy/mkdocstrings/commit/edc01e99aa7b762e800d9ae25cd5b842812dc326)). -- Initial commit ([f1dd8fb](https://github.com/pawamoy/mkdocstrings/commit/f1dd8fb2b4a4ae81f9144fe062ca9743ae82bd69)). -- Update readme ([ae56bdd](https://github.com/pawamoy/mkdocstrings/commit/ae56bdd9ac5692665409e99eb0fd509d8dfc605e)). -- Add plugin ([6ed5cb1](https://github.com/pawamoy/mkdocstrings/commit/6ed5cb1879b498ddc8d0fe1c04db7e3527f2ff81)). -- First PoC, needs better theming ([18a00b9](https://github.com/pawamoy/mkdocstrings/commit/18a00b9405a94405256a1ad2ae45886da40296e4)). -- Get attributes docstrings ([7838fff](https://github.com/pawamoy/mkdocstrings/commit/7838fffa5b1d5a481fd2ea5a94d305a96b06c321)). -- Refactor ([f68f1a8](https://github.com/pawamoy/mkdocstrings/commit/f68f1a89d477a55a6e86a9eb4c92bd5d6416b5cc)). diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 2d46305a..00000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,84 +0,0 @@ -# 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 - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 82526a81..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,130 +0,0 @@ -# Contributing - -Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. - -**Please always create an issue before working on a new feature or a bug fix, so that we can discuss the implementation and make sure that your work will be merged.** - -## Environment setup - -Nothing easier! - -Fork and clone the repository. The project uses dynamic versioning, so to get the correct package version when building, make sure to pull Git tags: - -```bash -cd mkdocstrings - -# Assuming you authenticate with SSH. -git remote add upstream git@github.com:mkdocstrings/mkdocstrings -git pull upstream --tags -``` - -Then: - -```bash -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. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 6270ba3e..00000000 --- a/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -ISC License - -Copyright (c) 2019, Timothée Mazzucotelli and contributors - -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. diff --git a/Makefile b/Makefile deleted file mode 100644 index 1b3391da..00000000 --- a/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -# 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. - -default: help - @echo - @echo 'Enable direnv in your shell to use the `make` command: `direnv allow`' - @echo 'Or use `python scripts/make ARGS` to run the commands/tasks directly.' - -.DEFAULT_GOAL: default - -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 "$@" diff --git a/README.md b/README.md deleted file mode 100644 index 8ddfc16b..00000000 --- a/README.md +++ /dev/null @@ -1,192 +0,0 @@ -# mkdocstrings - -[![ci](https://github.com/mkdocstrings/mkdocstrings/workflows/ci/badge.svg)](https://github.com/mkdocstrings/mkdocstrings/actions?query=workflow%3Aci) -[![documentation](https://img.shields.io/badge/docs-zensical-FF9100.svg?style=flat)](https://mkdocstrings.github.io/mkdocstrings/) -[![pypi version](https://img.shields.io/pypi/v/mkdocstrings.svg)](https://pypi.org/project/mkdocstrings/) -[![conda version](https://img.shields.io/conda/vn/conda-forge/mkdocstrings)](https://anaconda.org/conda-forge/mkdocstrings) -[![gitter](https://img.shields.io/badge/matrix-chat-4DB798.svg?style=flat)](https://app.gitter.im/#/room/#mkdocstrings:gitter.im) - -Automatic documentation from sources, for [MkDocs](https://www.mkdocs.org/). -Come have a chat or ask questions on our [Gitter channel](https://gitter.im/mkdocstrings/community). - ---- - -**[Features](#features)** - **[Installation](#installation)** - **[Quick usage](#quick-usage)** - -![mkdocstrings_gif1](https://user-images.githubusercontent.com/3999221/77157604-fb807480-6aa1-11ea-99e0-d092371d4de0.gif) - -## Features - -- [**Language-agnostic:**](https://mkdocstrings.github.io/handlers/overview/) - just like *MkDocs*, *mkdocstrings* is written in Python but is language-agnostic. - It means you can use it with any programming language, as long as there is a - [**handler**](https://mkdocstrings.github.io/reference/api/#mkdocstrings.BaseHandler) for it. - We currently have [handlers](https://mkdocstrings.github.io/handlers/overview/) for the - [C](https://mkdocstrings.github.io/c/), - [Crystal](https://mkdocstrings.github.io/crystal/), - [GitHub Actions](https://watermarkhu.nl/mkdocstrings-github/), - [Python](https://mkdocstrings.github.io/python/), - [MATLAB](https://watermarkhu.nl/mkdocstrings-matlab/), - [TypeScript](https://mkdocstrings.github.io/typescript/), and - [VBA](https://pypi.org/project/mkdocstrings-vba/) languages, - as well as for [shell scripts/libraries](https://mkdocstrings.github.io/shell/). - Maybe you'd like to add another one to the list? :wink: - -- [**Multiple themes support:**](https://mkdocstrings.github.io/theming/) - each handler can offer multiple themes. Currently, we offer the - :star: [Material theme](https://squidfunk.github.io/mkdocs-material/) :star: - as well as basic support for the ReadTheDocs and MkDocs themes for the Python handler. - -- [**Cross-references across pages:**](https://mkdocstrings.github.io/usage/#cross-references) - *mkdocstrings* makes it possible to reference headings in other Markdown files with the classic Markdown linking - syntax: `[identifier][]` or `[title][identifier]` -- and you don't need to remember which exact page this object was - on. This works for any heading that's produced by a *mkdocstrings* language handler, and you can opt to include - *any* Markdown heading into the global referencing scheme. - - **Note**: in versions prior to 0.15 *all* Markdown headers were included, but now you need to - [opt in](https://mkdocstrings.github.io/usage/#cross-references-to-any-markdown-heading). - -- [**Cross-references across sites:**](https://mkdocstrings.github.io/usage/#cross-references-to-other-projects-inventories) - similarly to [Sphinx's intersphinx extension](https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html), - *mkdocstrings* can reference API items from other libraries, given they provide an inventory and you load - that inventory in your MkDocs configuration. - -- [**Inline injection in Markdown:**](https://mkdocstrings.github.io/usage/) - instead of generating Markdown files, *mkdocstrings* allows you to inject - documentation anywhere in your Markdown contents. The syntax is simple: `::: identifier` followed by a 4-spaces - indented YAML block. The identifier and YAML configuration will be passed to the appropriate handler - to collect and render documentation. - -- [**Global and local configuration:**](https://mkdocstrings.github.io/usage/#global-options) - each handler can be configured globally in `mkdocs.yml`, and locally for each - "autodoc" instruction. - -- **Reasonable defaults:** - you should be able to just drop the plugin in your configuration and enjoy your auto-generated docs. - -## Used by - -*mkdocstrings* is used by well-known companies, projects and scientific teams: -[Ansible](https://molecule.readthedocs.io/configuration/), -[Apache](https://streampipes.apache.org/docs/docs/python/latest/reference/client/client/), -[FastAPI](https://fastapi.tiangolo.com/reference/fastapi/), -[Google](https://docs.kidger.site/jaxtyping/api/runtime-type-checking/), -[IBM](https://ds4sd.github.io/docling/api_reference/document_converter/), -[Jitsi](https://jitsi.github.io/jiwer/reference/alignment/), -[Microsoft](https://microsoft.github.io/presidio/api/analyzer_python/), -[NVIDIA](https://nvidia.github.io/bionemo-framework/main/references/API_reference/bionemo/core/api/), -[Prefect](https://docs.prefect.io/2.10.12/api-ref/prefect/agent/), -[Pydantic](https://docs.pydantic.dev/dev-v2/api/main/), -[Textual](https://textual.textualize.io/api/app/), -[and more...](https://github.com/mkdocstrings/mkdocstrings/network/dependents) - -## Installation - -The `mkdocstrings` package doesn't provide support for any language: it's just a common base for language handlers. -It means you likely want to install it with one or more official handlers, using [extras](https://packaging.python.org/en/latest/specifications/dependency-specifiers/#extras). -For example, to install it with Python support: - -```bash -pip install 'mkdocstrings[python]' -``` - -Alternatively, you can directly install the language handlers themselves, -which depend on `mkdocstrings` anyway: - -```bash -pip install mkdocstrings-python -``` - -This will give you more control over the accepted range of versions for the handlers themselves. - -See the [official language handlers](https://mkdocstrings.github.io/handlers/overview/). - ---- - -With `conda`: - -```bash -conda install -c conda-forge mkdocstrings mkdocstrings-python -``` - -## Quick usage - -In `mkdocs.yml`: - -```yaml -site_name: "My Library" - -theme: - name: "material" - -plugins: -- search -- mkdocstrings -``` - -In one of your markdown files: - -```markdown -# Reference - -::: my_library.my_module.my_class -``` - -See the [Usage](https://mkdocstrings.github.io/usage) section of the docs for more examples! - -## Sponsors - - - -

- -
Silver sponsors

-FastAPI
-

- -
Bronze sponsors

-Nixtla
-

-
- ---- - -

-ofek -samuelcolvin -tlambert03 -ssbarnea -femtomc -cmarqu -kolenaIO -ramnes -machow -BenHammersley -trevorWieland -MarcoGorelli -analog-cbarber -OdinManiac -rstudio-sponsorship -schlich -butterlyn -livingbio -NemetschekAllplan -EricJayHartman -15r10nk -activeloopai -roboflow -cmclaughlin -blaisep -RapidataAI -rodolphebarbanneau -theSymbolSyndicate -blakeNaccarato -ChargeStorm -Alphadelta14 -Cusp-AI -

- - -*And 7 more private sponsor(s).* - - diff --git a/assets/_markdown_exec_pyodide.css b/assets/_markdown_exec_pyodide.css new file mode 100644 index 00000000..5e8e12c5 --- /dev/null +++ b/assets/_markdown_exec_pyodide.css @@ -0,0 +1,53 @@ +html[data-theme="light"] { + @import "https://cdn.jsdelivr.net/npm/highlightjs-themes@1.0.0/tomorrow.css" +} + +html[data-theme="dark"] { + @import "https://cdn.jsdelivr.net/npm/highlightjs-themes@1.0.0/tomorrow-night-blue.min.css" +} + + +.ace_gutter { + z-index: 1; +} + +.pyodide-editor { + width: 100%; + font-size: .85em; +} + +.pyodide-editor-bar { + color: var(--md-primary-bg-color); + background-color: var(--md-primary-fg-color); + width: 100%; + font: monospace; + font-size: 0.75em; + padding: 2px 0 2px; +} + +.pyodide-bar-item { + padding: 0 18px 0; + display: inline-block; + width: 50%; +} + +.pyodide pre { + margin: 0; +} + +.pyodide-output { + width: 100%; + margin-bottom: -15px; + min-height: 46px; + max-height: 400px +} + +.pyodide-clickable { + cursor: pointer; + text-align: right; +} + +/* For themes other than Material. */ +.pyodide .twemoji svg { + width: 1rem; +} diff --git a/assets/_markdown_exec_pyodide.js b/assets/_markdown_exec_pyodide.js new file mode 100644 index 00000000..0400bc5a --- /dev/null +++ b/assets/_markdown_exec_pyodide.js @@ -0,0 +1,131 @@ +var _sessions = {}; + +function getSession(name, pyodide) { + if (!(name in _sessions)) { + _sessions[name] = pyodide.globals.get("dict")(); + } + return _sessions[name]; +} + +function writeOutput(element, string) { + element.innerHTML += string + '\n'; +} + +function clearOutput(element) { + element.innerHTML = ''; +} + +async function evaluatePython(pyodide, editor, output, session) { + pyodide.setStdout({ batched: (string) => { writeOutput(output, new Option(string).innerHTML); } }); + let result, code = editor.getValue(); + clearOutput(output); + try { + result = await pyodide.runPythonAsync(code, { globals: getSession(session, pyodide) }); + } catch (error) { + writeOutput(output, new Option(error.toString()).innerHTML); + } + if (result) writeOutput(output, new Option(result).innerHTML); + hljs.highlightElement(output); +} + +async function initPyodide() { + try { + let pyodide = await loadPyodide(); + await pyodide.loadPackage("micropip"); + return pyodide; + } catch(error) { + return null; + } +} + +function getTheme() { + return document.body.getAttribute('data-md-color-scheme'); +} + +function setTheme(editor, currentTheme, light, dark) { + // https://gist.github.com/RyanNutt/cb8d60997d97905f0b2aea6c3b5c8ee0 + if (currentTheme === "default") { + editor.setTheme("ace/theme/" + light); + document.querySelector(`link[title="light"]`).removeAttribute("disabled"); + document.querySelector(`link[title="dark"]`).setAttribute("disabled", "disabled"); + } else if (currentTheme === "slate") { + editor.setTheme("ace/theme/" + dark); + document.querySelector(`link[title="dark"]`).removeAttribute("disabled"); + document.querySelector(`link[title="light"]`).setAttribute("disabled", "disabled"); + } +} + +function updateTheme(editor, light, dark) { + // Create a new MutationObserver instance + const observer = new MutationObserver((mutations) => { + // Loop through the mutations that occurred + mutations.forEach((mutation) => { + // Check if the mutation was a change to the data-md-color-scheme attribute + if (mutation.attributeName === 'data-md-color-scheme') { + // Get the new value of the attribute + const newColorScheme = mutation.target.getAttribute('data-md-color-scheme'); + // Update the editor theme + setTheme(editor, newColorScheme, light, dark); + } + }); + }); + + // Configure the observer to watch for changes to the data-md-color-scheme attribute + observer.observe(document.body, { + attributes: true, + attributeFilter: ['data-md-color-scheme'], + }); +} + +async function setupPyodide( + idPrefix, + install = null, + themeLight = 'tomorrow', + themeDark = 'tomorrow_night', + session = null, + minLines = 5, + maxLines = 30, +) { + const editor = ace.edit(idPrefix + "editor"); + const run = document.getElementById(idPrefix + "run"); + const clear = document.getElementById(idPrefix + "clear"); + const output = document.getElementById(idPrefix + "output"); + + updateTheme(editor, themeLight, themeDark); + + editor.session.setMode("ace/mode/python"); + setTheme(editor, getTheme(), themeLight, themeDark); + + editor.setOption("minLines", minLines); + editor.setOption("maxLines", maxLines); + + // Force editor to resize after setting options + editor.resize(); + + writeOutput(output, "Initializing..."); + let pyodide = await pyodidePromise; + if (install && install.length) { + try { + micropip = pyodide.pyimport("micropip"); + for (const package of install) + await micropip.install(package); + clearOutput(output); + } catch (error) { + clearOutput(output); + writeOutput(output, `Could not install one or more packages: ${install.join(", ")}\n`); + writeOutput(output, new Option(error.toString()).innerHTML); + } + } else { + clearOutput(output); + } + run.onclick = () => evaluatePython(pyodide, editor, output, session); + clear.onclick = () => clearOutput(output); + output.parentElement.parentElement.addEventListener("keydown", (event) => { + if (event.ctrlKey && event.key.toLowerCase() === 'enter') { + event.preventDefault(); + run.click(); + } + }); +} + +var pyodidePromise = initPyodide(); diff --git a/assets/_mkdocstrings.css b/assets/_mkdocstrings.css new file mode 100644 index 00000000..854048ca --- /dev/null +++ b/assets/_mkdocstrings.css @@ -0,0 +1,237 @@ + +/* Avoid breaking parameter names, etc. in table cells. */ +.doc-contents td code { + word-break: normal !important; +} + +/* No line break before first paragraph of descriptions. */ +.doc-md-description, +.doc-md-description>p:first-child { + display: inline; +} + +/* No text transformation from Material for MkDocs for H5 headings. */ +.md-typeset h5 .doc-object-name { + text-transform: none; +} + +/* Max width for docstring sections tables. */ +.doc .md-typeset__table, +.doc .md-typeset__table table { + display: table !important; + width: 100%; +} + +.doc .md-typeset__table tr { + display: table-row; +} + +/* Defaults in Spacy table style. */ +.doc-param-default, +.doc-type_param-default { + float: right; +} + +/* Parameter headings must be inline, not blocks. */ +.doc-heading-parameter, +.doc-heading-type_parameter { + display: inline; +} + +/* Default font size for parameter headings. */ +.md-typeset .doc-heading-parameter { + font-size: inherit; +} + +/* Prefer space on the right, not the left of parameter permalinks. */ +.doc-heading-parameter .headerlink, +.doc-heading-type_parameter .headerlink { + margin-left: 0 !important; + margin-right: 0.2rem; +} + +/* Backward-compatibility: docstring section titles in bold. */ +.doc-section-title { + font-weight: bold; +} + +/* Backlinks crumb separator. */ +.doc-backlink-crumb { + display: inline-flex; + gap: .2rem; + white-space: nowrap; + align-items: center; + vertical-align: middle; +} +.doc-backlink-crumb:not(:first-child)::before { + background-color: var(--md-default-fg-color--lighter); + content: ""; + display: inline; + height: 1rem; + --md-path-icon: url('data:image/svg+xml;charset=utf-8,'); + -webkit-mask-image: var(--md-path-icon); + mask-image: var(--md-path-icon); + width: 1rem; +} +.doc-backlink-crumb.last { + font-weight: bold; +} + +/* Symbols in Navigation and ToC. */ +:root, :host, +[data-md-color-scheme="default"] { + --doc-symbol-parameter-fg-color: #df50af; + --doc-symbol-type_parameter-fg-color: #df50af; + --doc-symbol-attribute-fg-color: #953800; + --doc-symbol-function-fg-color: #8250df; + --doc-symbol-method-fg-color: #8250df; + --doc-symbol-class-fg-color: #0550ae; + --doc-symbol-type_alias-fg-color: #0550ae; + --doc-symbol-module-fg-color: #5cad0f; + + --doc-symbol-parameter-bg-color: #df50af1a; + --doc-symbol-type_parameter-bg-color: #df50af1a; + --doc-symbol-attribute-bg-color: #9538001a; + --doc-symbol-function-bg-color: #8250df1a; + --doc-symbol-method-bg-color: #8250df1a; + --doc-symbol-class-bg-color: #0550ae1a; + --doc-symbol-type_alias-bg-color: #0550ae1a; + --doc-symbol-module-bg-color: #5cad0f1a; +} + +[data-md-color-scheme="slate"] { + --doc-symbol-parameter-fg-color: #ffa8cc; + --doc-symbol-type_parameter-fg-color: #ffa8cc; + --doc-symbol-attribute-fg-color: #ffa657; + --doc-symbol-function-fg-color: #d2a8ff; + --doc-symbol-method-fg-color: #d2a8ff; + --doc-symbol-class-fg-color: #79c0ff; + --doc-symbol-type_alias-fg-color: #79c0ff; + --doc-symbol-module-fg-color: #baff79; + + --doc-symbol-parameter-bg-color: #ffa8cc1a; + --doc-symbol-type_parameter-bg-color: #ffa8cc1a; + --doc-symbol-attribute-bg-color: #ffa6571a; + --doc-symbol-function-bg-color: #d2a8ff1a; + --doc-symbol-method-bg-color: #d2a8ff1a; + --doc-symbol-class-bg-color: #79c0ff1a; + --doc-symbol-type_alias-bg-color: #79c0ff1a; + --doc-symbol-module-bg-color: #baff791a; +} + +code.doc-symbol { + border-radius: .1rem; + font-size: .85em; + padding: 0 .3em; + font-weight: bold; +} + +code.doc-symbol-parameter, +a code.doc-symbol-parameter { + color: var(--doc-symbol-parameter-fg-color); + background-color: var(--doc-symbol-parameter-bg-color); +} + +code.doc-symbol-parameter::after { + content: "param"; +} + +code.doc-symbol-type_parameter, +a code.doc-symbol-type_parameter { + color: var(--doc-symbol-type_parameter-fg-color); + background-color: var(--doc-symbol-type_parameter-bg-color); +} + +code.doc-symbol-type_parameter::after { + content: "type-param"; +} + +code.doc-symbol-attribute, +a code.doc-symbol-attribute { + color: var(--doc-symbol-attribute-fg-color); + background-color: var(--doc-symbol-attribute-bg-color); +} + +code.doc-symbol-attribute::after { + content: "attr"; +} + +code.doc-symbol-function, +a code.doc-symbol-function { + color: var(--doc-symbol-function-fg-color); + background-color: var(--doc-symbol-function-bg-color); +} + +code.doc-symbol-function::after { + content: "func"; +} + +code.doc-symbol-method, +a code.doc-symbol-method { + color: var(--doc-symbol-method-fg-color); + background-color: var(--doc-symbol-method-bg-color); +} + +code.doc-symbol-method::after { + content: "meth"; +} + +code.doc-symbol-class, +a code.doc-symbol-class { + color: var(--doc-symbol-class-fg-color); + background-color: var(--doc-symbol-class-bg-color); +} + +code.doc-symbol-class::after { + content: "class"; +} + + +code.doc-symbol-type_alias, +a code.doc-symbol-type_alias { + color: var(--doc-symbol-type_alias-fg-color); + background-color: var(--doc-symbol-type_alias-bg-color); +} + +code.doc-symbol-type_alias::after { + content: "type"; +} + +code.doc-symbol-module, +a code.doc-symbol-module { + color: var(--doc-symbol-module-fg-color); + background-color: var(--doc-symbol-module-bg-color); +} + +code.doc-symbol-module::after { + content: "mod"; +} + +.doc-signature .autorefs { + color: inherit; + border-bottom: 1px dotted currentcolor; +} + +/* Source code blocks (admonitions). */ +:root { + --md-admonition-icon--mkdocstrings-source: url('data:image/svg+xml;charset=utf-8,') +} +.md-typeset .admonition.mkdocstrings-source, +.md-typeset details.mkdocstrings-source { + border: none; + padding: 0; +} +.md-typeset .admonition.mkdocstrings-source:focus-within, +.md-typeset details.mkdocstrings-source:focus-within { + box-shadow: none; +} +.md-typeset .mkdocstrings-source > .admonition-title, +.md-typeset .mkdocstrings-source > summary { + background-color: inherit; +} +.md-typeset .mkdocstrings-source > .admonition-title::before, +.md-typeset .mkdocstrings-source > summary::before { + background-color: var(--md-default-fg-color); + -webkit-mask-image: var(--md-admonition-icon--mkdocstrings-source); + mask-image: var(--md-admonition-icon--mkdocstrings-source); +} diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 00000000..1cf13b9f Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.e71a0d61.min.js b/assets/javascripts/bundle.e71a0d61.min.js new file mode 100644 index 00000000..c76b3b2b --- /dev/null +++ b/assets/javascripts/bundle.e71a0d61.min.js @@ -0,0 +1,16 @@ +"use strict";(()=>{var Zi=Object.create;var _r=Object.defineProperty;var ea=Object.getOwnPropertyDescriptor;var ta=Object.getOwnPropertyNames,Bt=Object.getOwnPropertySymbols,ra=Object.getPrototypeOf,Ar=Object.prototype.hasOwnProperty,bo=Object.prototype.propertyIsEnumerable;var ho=(e,t,r)=>t in e?_r(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Ar.call(t,r)&&ho(e,r,t[r]);if(Bt)for(var r of Bt(t))bo.call(t,r)&&ho(e,r,t[r]);return e};var vo=(e,t)=>{var r={};for(var o in e)Ar.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Bt)for(var o of Bt(e))t.indexOf(o)<0&&bo.call(e,o)&&(r[o]=e[o]);return r};var Cr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var oa=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ta(t))!Ar.call(e,n)&&n!==r&&_r(e,n,{get:()=>t[n],enumerable:!(o=ea(t,n))||o.enumerable});return e};var $t=(e,t,r)=>(r=e!=null?Zi(ra(e)):{},oa(t||!e||!e.__esModule?_r(r,"default",{value:e,enumerable:!0}):r,e));var go=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var xo=Cr((kr,yo)=>{(function(e,t){typeof kr=="object"&&typeof yo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(kr,(function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(k){return!!(k&&k!==document&&k.nodeName!=="HTML"&&k.nodeName!=="BODY"&&"classList"in k&&"contains"in k.classList)}function c(k){var ut=k.type,je=k.tagName;return!!(je==="INPUT"&&s[ut]&&!k.readOnly||je==="TEXTAREA"&&!k.readOnly||k.isContentEditable)}function p(k){k.classList.contains("focus-visible")||(k.classList.add("focus-visible"),k.setAttribute("data-focus-visible-added",""))}function l(k){k.hasAttribute("data-focus-visible-added")&&(k.classList.remove("focus-visible"),k.removeAttribute("data-focus-visible-added"))}function f(k){k.metaKey||k.altKey||k.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(k){o=!1}function d(k){a(k.target)&&(o||c(k.target))&&p(k.target)}function v(k){a(k.target)&&(k.target.classList.contains("focus-visible")||k.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(k.target))}function S(k){document.visibilityState==="hidden"&&(n&&(o=!0),X())}function X(){document.addEventListener("mousemove",ee),document.addEventListener("mousedown",ee),document.addEventListener("mouseup",ee),document.addEventListener("pointermove",ee),document.addEventListener("pointerdown",ee),document.addEventListener("pointerup",ee),document.addEventListener("touchmove",ee),document.addEventListener("touchstart",ee),document.addEventListener("touchend",ee)}function re(){document.removeEventListener("mousemove",ee),document.removeEventListener("mousedown",ee),document.removeEventListener("mouseup",ee),document.removeEventListener("pointermove",ee),document.removeEventListener("pointerdown",ee),document.removeEventListener("pointerup",ee),document.removeEventListener("touchmove",ee),document.removeEventListener("touchstart",ee),document.removeEventListener("touchend",ee)}function ee(k){k.target.nodeName&&k.target.nodeName.toLowerCase()==="html"||(o=!1,re())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",S,!0),X(),r.addEventListener("focus",d,!0),r.addEventListener("blur",v,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)}))});var ro=Cr((jy,Rn)=>{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var qa=/["'&<>]/;Rn.exports=Ka;function Ka(e){var t=""+e,r=qa.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Nt=="object"&&typeof io=="object"?io.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Nt=="object"?Nt.ClipboardJS=r():t.ClipboardJS=r()})(Nt,function(){return(function(){var e={686:(function(o,n,i){"use strict";i.d(n,{default:function(){return Xi}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(q){try{return document.execCommand(q)}catch(C){return!1}}var d=function(C){var _=f()(C);return u("cut"),_},v=d;function S(q){var C=document.documentElement.getAttribute("dir")==="rtl",_=document.createElement("textarea");_.style.fontSize="12pt",_.style.border="0",_.style.padding="0",_.style.margin="0",_.style.position="absolute",_.style[C?"right":"left"]="-9999px";var D=window.pageYOffset||document.documentElement.scrollTop;return _.style.top="".concat(D,"px"),_.setAttribute("readonly",""),_.value=q,_}var X=function(C,_){var D=S(C);_.container.appendChild(D);var N=f()(D);return u("copy"),D.remove(),N},re=function(C){var _=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},D="";return typeof C=="string"?D=X(C,_):C instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(C==null?void 0:C.type)?D=X(C.value,_):(D=f()(C),u("copy")),D},ee=re;function k(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?k=function(_){return typeof _}:k=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},k(q)}var ut=function(){var C=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},_=C.action,D=_===void 0?"copy":_,N=C.container,G=C.target,We=C.text;if(D!=="copy"&&D!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(G!==void 0)if(G&&k(G)==="object"&&G.nodeType===1){if(D==="copy"&&G.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(D==="cut"&&(G.hasAttribute("readonly")||G.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(We)return ee(We,{container:N});if(G)return D==="cut"?v(G):ee(G,{container:N})},je=ut;function R(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?R=function(_){return typeof _}:R=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},R(q)}function se(q,C){if(!(q instanceof C))throw new TypeError("Cannot call a class as a function")}function ce(q,C){for(var _=0;_0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof N.action=="function"?N.action:this.defaultAction,this.target=typeof N.target=="function"?N.target:this.defaultTarget,this.text=typeof N.text=="function"?N.text:this.defaultText,this.container=R(N.container)==="object"?N.container:document.body}},{key:"listenClick",value:function(N){var G=this;this.listener=p()(N,"click",function(We){return G.onClick(We)})}},{key:"onClick",value:function(N){var G=N.delegateTarget||N.currentTarget,We=this.action(G)||"copy",Yt=je({action:We,container:this.container,target:this.target(G),text:this.text(G)});this.emit(Yt?"success":"error",{action:We,text:Yt,trigger:G,clearSelection:function(){G&&G.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(N){return Mr("action",N)}},{key:"defaultTarget",value:function(N){var G=Mr("target",N);if(G)return document.querySelector(G)}},{key:"defaultText",value:function(N){return Mr("text",N)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(N){var G=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return ee(N,G)}},{key:"cut",value:function(N){return v(N)}},{key:"isSupported",value:function(){var N=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],G=typeof N=="string"?[N]:N,We=!!document.queryCommandSupported;return G.forEach(function(Yt){We=We&&!!document.queryCommandSupported(Yt)}),We}}]),_})(a()),Xi=Ji}),828:(function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s}),438:(function(o,n,i){var s=i(828);function a(l,f,u,d,v){var S=p.apply(this,arguments);return l.addEventListener(u,S,v),{destroy:function(){l.removeEventListener(u,S,v)}}}function c(l,f,u,d,v){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(S){return a(S,f,u,d,v)}))}function p(l,f,u,d){return function(v){v.delegateTarget=s(v.target,f),v.delegateTarget&&d.call(l,v)}}o.exports=c}),879:(function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}}),370:(function(o,n,i){var s=i(879),a=i(438);function c(u,d,v){if(!u&&!d&&!v)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(v))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,d,v);if(s.nodeList(u))return l(u,d,v);if(s.string(u))return f(u,d,v);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,d,v){return u.addEventListener(d,v),{destroy:function(){u.removeEventListener(d,v)}}}function l(u,d,v){return Array.prototype.forEach.call(u,function(S){S.addEventListener(d,v)}),{destroy:function(){Array.prototype.forEach.call(u,function(S){S.removeEventListener(d,v)})}}}function f(u,d,v){return a(document.body,u,d,v)}o.exports=c}),817:(function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n}),279:(function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function K(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function B(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||c(d,S)})},v&&(n[d]=v(n[d])))}function c(d,v){try{p(o[d](v))}catch(S){u(i[0][3],S)}}function p(d){d.value instanceof dt?Promise.resolve(d.value.v).then(l,f):u(i[0][2],d)}function l(d){c("next",d)}function f(d){c("throw",d)}function u(d,v){d(v),i.shift(),i.length&&c(i[0][0],i[0][1])}}function To(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Oe=="function"?Oe(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function I(e){return typeof e=="function"}function yt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Jt=yt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ze(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var qe=(function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Oe(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(S){t={error:S}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(I(l))try{l()}catch(S){i=S instanceof Jt?S.errors:[S]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=Oe(f),d=u.next();!d.done;d=u.next()){var v=d.value;try{So(v)}catch(S){i=i!=null?i:[],S instanceof Jt?i=B(B([],K(i)),K(S.errors)):i.push(S)}}}catch(S){o={error:S}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Jt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)So(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ze(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ze(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=(function(){var t=new e;return t.closed=!0,t})(),e})();var $r=qe.EMPTY;function Xt(e){return e instanceof qe||e&&"closed"in e&&I(e.remove)&&I(e.add)&&I(e.unsubscribe)}function So(e){I(e)?e():e.unsubscribe()}var De={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var xt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?$r:(this.currentObservers=null,a.push(r),new qe(function(){o.currentObservers=null,Ze(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,o){return new Ho(r,o)},t})(F);var Ho=(function(e){ie(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:$r},t})(T);var jr=(function(e){ie(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t})(T);var Rt={now:function(){return(Rt.delegate||Date).now()},delegate:void 0};var It=(function(e){ie(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=Rt);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t})(St);var Ro=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t})(Ot);var Dr=new Ro(Po);var Io=(function(e){ie(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=Tt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&o===r._scheduled&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(Tt.cancelAnimationFrame(o),r._scheduled=void 0)},t})(St);var Fo=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o;r?o=r.id:(o=this._scheduled,this._scheduled=void 0);var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t})(Ot);var ye=new Fo(Io);var y=new F(function(e){return e.complete()});function tr(e){return e&&I(e.schedule)}function Vr(e){return e[e.length-1]}function pt(e){return I(Vr(e))?e.pop():void 0}function Fe(e){return tr(Vr(e))?e.pop():void 0}function rr(e,t){return typeof Vr(e)=="number"?e.pop():t}var Lt=(function(e){return e&&typeof e.length=="number"&&typeof e!="function"});function or(e){return I(e==null?void 0:e.then)}function nr(e){return I(e[wt])}function ir(e){return Symbol.asyncIterator&&I(e==null?void 0:e[Symbol.asyncIterator])}function ar(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function fa(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var sr=fa();function cr(e){return I(e==null?void 0:e[sr])}function pr(e){return wo(this,arguments,function(){var r,o,n,i;return Gt(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,dt(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,dt(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,dt(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function lr(e){return I(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(nr(e))return ua(e);if(Lt(e))return da(e);if(or(e))return ha(e);if(ir(e))return jo(e);if(cr(e))return ba(e);if(lr(e))return va(e)}throw ar(e)}function ua(e){return new F(function(t){var r=e[wt]();if(I(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function da(e){return new F(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?g(function(n,i){return e(n,i,o)}):be,Ee(1),r?Qe(t):tn(function(){return new fr}))}}function Yr(e){return e<=0?function(){return y}:E(function(t,r){var o=[];t.subscribe(w(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new T}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,d=0,v=!1,S=!1,X=function(){f==null||f.unsubscribe(),f=void 0},re=function(){X(),l=u=void 0,v=S=!1},ee=function(){var k=l;re(),k==null||k.unsubscribe()};return E(function(k,ut){d++,!S&&!v&&X();var je=u=u!=null?u:r();ut.add(function(){d--,d===0&&!S&&!v&&(f=Br(ee,c))}),je.subscribe(ut),!l&&d>0&&(l=new bt({next:function(R){return je.next(R)},error:function(R){S=!0,X(),f=Br(re,n,R),je.error(R)},complete:function(){v=!0,X(),f=Br(re,s),je.complete()}}),U(k).subscribe(l))})(p)}}function Br(e,t){for(var r=[],o=2;oe.next(document)),e}function M(e,t=document){return Array.from(t.querySelectorAll(e))}function j(e,t=document){let r=ue(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ue(e,t=document){return t.querySelector(e)||void 0}function Ne(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var Ra=L(h(document.body,"focusin"),h(document.body,"focusout")).pipe(Ae(1),Q(void 0),m(()=>Ne()||document.body),Z(1));function Ye(e){return Ra.pipe(m(t=>e.contains(t)),Y())}function it(e,t){return H(()=>L(h(e,"mouseenter").pipe(m(()=>!0)),h(e,"mouseleave").pipe(m(()=>!1))).pipe(t?jt(r=>He(+!r*t)):be,Q(e.matches(":hover"))))}function sn(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)sn(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)sn(o,n);return o}function br(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function _t(e){let t=x("script",{src:e});return H(()=>(document.head.appendChild(t),L(h(t,"load"),h(t,"error").pipe(b(()=>Nr(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),A(()=>document.head.removeChild(t)),Ee(1))))}var cn=new T,Ia=H(()=>typeof ResizeObserver=="undefined"?_t("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>cn.next(t)))),b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Le(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return Ia.pipe(O(r=>r.observe(t)),b(r=>cn.pipe(g(o=>o.target===t),A(()=>r.unobserve(t)))),m(()=>de(e)),Q(de(e)))}function At(e){return{width:e.scrollWidth,height:e.scrollHeight}}function vr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function pn(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function ln(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function mn(e){return L(h(window,"load"),h(window,"resize")).pipe($e(0,ye),m(()=>Be(e)),Q(Be(e)))}function gr(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ge(e){return L(h(e,"scroll"),h(window,"scroll"),h(window,"resize")).pipe($e(0,ye),m(()=>gr(e)),Q(gr(e)))}var fn=new T,Fa=H(()=>$(new IntersectionObserver(e=>{for(let t of e)fn.next(t)},{threshold:0}))).pipe(b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function mt(e){return Fa.pipe(O(t=>t.observe(e)),b(t=>fn.pipe(g(({target:r})=>r===e),A(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function un(e,t=16){return Ge(e).pipe(m(({y:r})=>{let o=de(e),n=At(e);return r>=n.height-o.height-t}),Y())}var yr={drawer:j("[data-md-toggle=drawer]"),search:j("[data-md-toggle=search]")};function dn(e){return yr[e].checked}function at(e,t){yr[e].checked!==t&&yr[e].click()}function Je(e){let t=yr[e];return h(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function ja(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ua(){return L(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function hn(){let e=h(window,"keydown").pipe(g(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:dn("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),g(({mode:t,type:r})=>{if(t==="global"){let o=Ne();if(typeof o!="undefined")return!ja(o,r)}return!0}),le());return Ua().pipe(b(t=>t?y:e))}function we(){return new URL(location.href)}function st(e,t=!1){if(V("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function bn(){return new T}function vn(){return location.hash.slice(1)}function gn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Zr(e){return L(h(window,"hashchange"),e).pipe(m(vn),Q(vn()),g(t=>t.length>0),Z(1))}function yn(e){return Zr(e).pipe(m(t=>ue(`[id="${t}"]`)),g(t=>typeof t!="undefined"))}function Wt(e){let t=matchMedia(e);return ur(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function xn(){let e=matchMedia("print");return L(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function eo(e,t){return e.pipe(b(r=>r?t():y))}function to(e,t){return new F(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let s=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+s*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function ze(e,t){return to(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),Z(1))}function xr(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),Z(1))}function En(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),Z(1))}function wn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function Tn(){return L(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(wn),Q(wn()))}function Sn(){return{width:innerWidth,height:innerHeight}}function On(){return h(window,"resize",{passive:!0}).pipe(m(Sn),Q(Sn()))}function Ln(){return z([Tn(),On()]).pipe(m(([e,t])=>({offset:e,size:t})),Z(1))}function Er(e,{viewport$:t,header$:r}){let o=t.pipe(ne("size")),n=z([o,r]).pipe(m(()=>Be(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function Wa(e){return h(e,"message",t=>t.data)}function Da(e){let t=new T;return t.subscribe(r=>e.postMessage(r)),t}function Mn(e,t=new Worker(e)){let r=Wa(t),o=Da(t),n=new T;n.subscribe(o);let i=o.pipe(oe(),ae(!0));return n.pipe(oe(),Ve(r.pipe(W(i))),le())}var Va=j("#__config"),Ct=JSON.parse(Va.textContent);Ct.base=`${new URL(Ct.base,we())}`;function Te(){return Ct}function V(e){return Ct.features.includes(e)}function Me(e,t){return typeof t!="undefined"?Ct.translations[e].replace("#",t.toString()):Ct.translations[e]}function Ce(e,t=document){return j(`[data-md-component=${e}]`,t)}function me(e,t=document){return M(`[data-md-component=${e}]`,t)}function Na(e){let t=j(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>j(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function _n(e){if(!V("announce.dismiss")||!e.childElementCount)return y;if(!e.hidden){let t=j(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new T;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),Na(e).pipe(O(r=>t.next(r)),A(()=>t.complete()),m(r=>P({ref:e},r)))})}function za(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function An(e,t){let r=new T;return r.subscribe(({hidden:o})=>{e.hidden=o}),za(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))}function Dt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function wr(...e){return x("div",{class:"md-tooltip2",role:"dialog"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function Cn(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function kn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function Hn(e){return x("button",{class:"md-code__button",title:Me("clipboard.copy"),"data-clipboard-target":`#${e} > code`,"data-md-type":"copy"})}function $n(){return x("button",{class:"md-code__button",title:"Toggle line selection","data-md-type":"select"})}function Pn(){return x("nav",{class:"md-code__nav"})}var In=$t(ro());function oo(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,x("del",null,(0,In.default)(p))," "],[]).slice(0,-1),i=Te(),s=new URL(e.location,i.base);V("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=Te();return x("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&x("nav",{class:"md-tags"},e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${p}`},c)})),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Me("search.result.term.missing"),": ",...n)))}function Fn(e){let t=e[0].score,r=[...e],o=Te(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreoo(l,1)),...c.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,c.length>0&&c.length===1?Me("search.result.more.one"):Me("search.result.more.other",c.length))),...c.map(l=>oo(l,1)))]:[]];return x("li",{class:"md-search-result__item"},p)}function jn(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?br(r):r)))}function no(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function Un(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function Qa(e){var o;let t=Te(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function Wn(e,t){var o;let r=Te();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Me("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map(Qa)))}var Ya=0;function Ba(e,t=250){let r=z([Ye(e),it(e,t)]).pipe(m(([n,i])=>n||i),Y()),o=H(()=>pn(e)).pipe(J(Ge),gt(1),Pe(r),m(()=>ln(e)));return r.pipe(Re(n=>n),b(()=>z([r,o])),m(([n,i])=>({active:n,offset:i})),le())}function Vt(e,t,r=250){let{content$:o,viewport$:n}=t,i=`__tooltip2_${Ya++}`;return H(()=>{let s=new T,a=new jr(!1);s.pipe(oe(),ae(!1)).subscribe(a);let c=a.pipe(jt(l=>He(+!l*250,Dr)),Y(),b(l=>l?o:y),O(l=>l.id=i),le());z([s.pipe(m(({active:l})=>l)),c.pipe(b(l=>it(l,250)),Q(!1))]).pipe(m(l=>l.some(f=>f))).subscribe(a);let p=a.pipe(g(l=>l),te(c,n),m(([l,f,{size:u}])=>{let d=e.getBoundingClientRect(),v=d.width/2;if(f.role==="tooltip")return{x:v,y:8+d.height};if(d.y>=u.height/2){let{height:S}=de(f);return{x:v,y:-16-S}}else return{x:v,y:16+d.height}}));return z([c,s,p]).subscribe(([l,{offset:f},u])=>{l.style.setProperty("--md-tooltip-host-x",`${f.x}px`),l.style.setProperty("--md-tooltip-host-y",`${f.y}px`),l.style.setProperty("--md-tooltip-x",`${u.x}px`),l.style.setProperty("--md-tooltip-y",`${u.y}px`),l.classList.toggle("md-tooltip2--top",u.y<0),l.classList.toggle("md-tooltip2--bottom",u.y>=0)}),a.pipe(g(l=>l),te(c,(l,f)=>f),g(l=>l.role==="tooltip")).subscribe(l=>{let f=de(j(":scope > *",l));l.style.setProperty("--md-tooltip-width",`${f.width}px`),l.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(Y(),xe(ye),te(c)).subscribe(([l,f])=>{f.classList.toggle("md-tooltip2--active",l)}),z([a.pipe(g(l=>l)),c]).subscribe(([l,f])=>{f.role==="dialog"?(e.setAttribute("aria-controls",i),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",i)}),a.pipe(g(l=>!l)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),Ba(e,r).pipe(O(l=>s.next(l)),A(()=>s.complete()),m(l=>P({ref:e},l)))})}function Xe(e,{viewport$:t},r=document.body){return Vt(e,{content$:new F(o=>{let n=e.title,i=Cn(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t},0)}function Ga(e,t){let r=H(()=>z([mn(e),Ge(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=de(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return Ye(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),Ee(+!o||1/0))))}function Dn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),mt(e).pipe(W(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),L(i.pipe(g(({active:a})=>a)),i.pipe(Ae(250),g(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe($e(16,ye)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(W(s),g(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),h(n,"mousedown").pipe(W(s),te(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Ne())==null||p.blur()}}),r.pipe(W(s),g(a=>a===o),nt(125)).subscribe(()=>e.focus()),Ga(e,t).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function Ja(e){let t=Te();if(e.tagName!=="CODE")return[e];let r=[".c",".c1",".cm"];if(t.annotate&&typeof t.annotate=="object"){let o=e.closest("[class|=language]");if(o)for(let n of Array.from(o.classList)){if(!n.startsWith("language-"))continue;let[,i]=n.split("-");i in t.annotate&&r.push(...t.annotate[i])}}return M(r.join(", "),e)}function Xa(e){let t=[];for(let r of Ja(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function Vn(e,t){t.append(...Array.from(e.childNodes))}function Tr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of Xa(t)){let[,c]=a.textContent.match(/\((\d+)\)/);ue(`:scope > li:nth-child(${c})`,e)&&(s.set(c,kn(c,i)),a.replaceWith(s.get(c)))}return s.size===0?y:H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=[];for(let[l,f]of s)p.push([j(".md-typeset",f),j(`:scope > li:nth-child(${l})`,e)]);return o.pipe(W(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?Vn(f,u):Vn(u,f)}),L(...[...s].map(([,l])=>Dn(l,t,{target$:r}))).pipe(A(()=>a.complete()),le())})}function Nn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Nn(t)}}function zn(e,t){return H(()=>{let r=Nn(e);return typeof r!="undefined"?Tr(r,e,t):y})}var Kn=$t(ao());var Za=0,qn=L(h(window,"keydown").pipe(m(()=>!0)),L(h(window,"keyup"),h(window,"contextmenu")).pipe(m(()=>!1))).pipe(Q(!1),Z(1));function Qn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Qn(t)}}function es(e){return Le(e).pipe(m(({width:t})=>({scrollable:At(e).width>t})),ne("scrollable"))}function Yn(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new T,i=n.pipe(Yr(1));n.subscribe(({scrollable:d})=>{d&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let s=[],a=e.closest("pre"),c=a.closest("[id]"),p=c?c.id:Za++;a.id=`__code_${p}`;let l=[],f=e.closest(".highlight");if(f instanceof HTMLElement){let d=Qn(f);if(typeof d!="undefined"&&(f.classList.contains("annotate")||V("content.code.annotate"))){let v=Tr(d,e,t);l.push(Le(f).pipe(W(i),m(({width:S,height:X})=>S&&X),Y(),b(S=>S?v:y)))}}let u=M(":scope > span[id]",e);if(u.length&&(e.classList.add("md-code__content"),e.closest(".select")||V("content.code.select")&&!e.closest(".no-select"))){let d=+u[0].id.split("-").pop(),v=$n();s.push(v),V("content.tooltips")&&l.push(Xe(v,{viewport$}));let S=h(v,"click").pipe(Ut(R=>!R,!1),O(()=>v.blur()),le());S.subscribe(R=>{v.classList.toggle("md-code__button--active",R)});let X=fe(u).pipe(J(R=>it(R).pipe(m(se=>[R,se]))));S.pipe(b(R=>R?X:y)).subscribe(([R,se])=>{let ce=ue(".hll.select",R);if(ce&&!se)ce.replaceWith(...Array.from(ce.childNodes));else if(!ce&&se){let he=document.createElement("span");he.className="hll select",he.append(...Array.from(R.childNodes).slice(1)),R.append(he)}});let re=fe(u).pipe(J(R=>h(R,"mousedown").pipe(O(se=>se.preventDefault()),m(()=>R)))),ee=S.pipe(b(R=>R?re:y),te(qn),m(([R,se])=>{var he;let ce=u.indexOf(R)+d;if(se===!1)return[ce,ce];{let Se=M(".hll",e).map(Ue=>u.indexOf(Ue.parentElement)+d);return(he=window.getSelection())==null||he.removeAllRanges(),[Math.min(ce,...Se),Math.max(ce,...Se)]}})),k=Zr(y).pipe(g(R=>R.startsWith(`__codelineno-${p}-`)));k.subscribe(R=>{let[,,se]=R.split("-"),ce=se.split(":").map(Se=>+Se-d+1);ce.length===1&&ce.push(ce[0]);for(let Se of M(".hll:not(.select)",e))Se.replaceWith(...Array.from(Se.childNodes));let he=u.slice(ce[0]-1,ce[1]);for(let Se of he){let Ue=document.createElement("span");Ue.className="hll",Ue.append(...Array.from(Se.childNodes).slice(1)),Se.append(Ue)}}),k.pipe(Ee(1),xe(pe)).subscribe(R=>{if(R.includes(":")){let se=document.getElementById(R.split(":")[0]);se&&setTimeout(()=>{let ce=se,he=-64;for(;ce!==document.body;)he+=ce.offsetTop,ce=ce.offsetParent;window.scrollTo({top:he})},1)}});let je=fe(M('a[href^="#__codelineno"]',f)).pipe(J(R=>h(R,"click").pipe(O(se=>se.preventDefault()),m(()=>R)))).pipe(W(i),te(qn),m(([R,se])=>{let he=+j(`[id="${R.hash.slice(1)}"]`).parentElement.id.split("-").pop();if(se===!1)return[he,he];{let Se=M(".hll",e).map(Ue=>+Ue.parentElement.id.split("-").pop());return[Math.min(he,...Se),Math.max(he,...Se)]}}));L(ee,je).subscribe(R=>{let se=`#__codelineno-${p}-`;R[0]===R[1]?se+=R[0]:se+=`${R[0]}:${R[1]}`,history.replaceState({},"",se),window.dispatchEvent(new HashChangeEvent("hashchange",{newURL:window.location.origin+window.location.pathname+se,oldURL:window.location.href}))})}if(Kn.default.isSupported()&&(e.closest(".copy")||V("content.code.copy")&&!e.closest(".no-copy"))){let d=Hn(a.id);s.push(d),V("content.tooltips")&&l.push(Xe(d,{viewport$}))}if(s.length){let d=Pn();d.append(...s),a.insertBefore(d,e)}return es(e).pipe(O(d=>n.next(d)),A(()=>n.complete()),m(d=>P({ref:e},d)),Ve(L(...l).pipe(W(i))))});return V("content.lazy")?mt(e).pipe(g(n=>n),Ee(1),b(()=>o)):o}function ts(e,{target$:t,print$:r}){let o=!0;return L(t.pipe(m(n=>n.closest("details:not([open])")),g(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(g(n=>n||!o),O(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Bn(e,t){return H(()=>{let r=new T;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),ts(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}var Gn=0;function rs(e){let t=document.createElement("h3");t.innerHTML=e.innerHTML;let r=[t],o=e.nextElementSibling;for(;o&&!(o instanceof HTMLHeadingElement);)r.push(o),o=o.nextElementSibling;return r}function os(e,t){for(let r of M("[href], [src]",e))for(let o of["href","src"]){let n=r.getAttribute(o);if(n&&!/^(?:[a-z]+:)?\/\//i.test(n)){r[o]=new URL(r.getAttribute(o),t).toString();break}}for(let r of M("[name^=__], [for]",e))for(let o of["id","for","name"]){let n=r.getAttribute(o);n&&r.setAttribute(o,`${n}$preview_${Gn}`)}return Gn++,$(e)}function Jn(e,t){let{sitemap$:r}=t;if(!(e instanceof HTMLAnchorElement))return y;if(!(V("navigation.instant.preview")||e.hasAttribute("data-preview")))return y;e.removeAttribute("title");let o=z([Ye(e),it(e)]).pipe(m(([i,s])=>i||s),Y(),g(i=>i));return rt([r,o]).pipe(b(([i])=>{let s=new URL(e.href);return s.search=s.hash="",i.has(`${s}`)?$(s):y}),b(i=>xr(i).pipe(b(s=>os(s,i)))),b(i=>{let s=e.hash?`article [id="${e.hash.slice(1)}"]`:"article h1",a=ue(s,i);return typeof a=="undefined"?y:$(rs(a))})).pipe(b(i=>{let s=new F(a=>{let c=wr(...i);return a.next(c),document.body.append(c),()=>c.remove()});return Vt(e,P({content$:s},t))}))}var Xn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.flowchartTitleText{fill:var(--md-mermaid-label-fg-color)}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel p,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel p{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color)}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}.classDiagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs marker.marker.composition.class path,defs marker.marker.dependency.class path,defs marker.marker.extension.class path{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs marker.marker.aggregation.class path{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}.statediagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}[id^=entity] path,[id^=entity] rect{fill:var(--md-default-bg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs .marker.oneOrMore.er *,defs .marker.onlyOne.er *,defs .marker.zeroOrMore.er *,defs .marker.zeroOrOne.er *{stroke:var(--md-mermaid-edge-color)!important}text:not([class]):last-child{fill:var(--md-mermaid-label-fg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var so,is=0;function as(){return typeof mermaid=="undefined"||mermaid instanceof Element?_t("https://unpkg.com/mermaid@11/dist/mermaid.min.js"):$(void 0)}function Zn(e){return e.classList.remove("mermaid"),so||(so=as().pipe(O(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Xn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),Z(1))),so.subscribe(()=>go(null,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${is++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),so.pipe(m(()=>({ref:e})))}var ei=x("table");function ti(e){return e.replaceWith(ei),ei.replaceWith(Un(e)),$({ref:e})}function ss(e){let t=e.find(r=>r.checked)||e[0];return L(...e.map(r=>h(r,"change").pipe(m(()=>j(`label[for="${r.id}"]`))))).pipe(Q(j(`label[for="${t.id}"]`)),m(r=>({active:r})))}function ri(e,{viewport$:t,target$:r}){let o=j(".tabbed-labels",e),n=M(":scope > input",e),i=no("prev");e.append(i);let s=no("next");return e.append(s),H(()=>{let a=new T,c=a.pipe(oe(),ae(!0));z([a,Le(e),mt(e)]).pipe(W(c),$e(1,ye)).subscribe({next([{active:p},l]){let f=Be(p),{width:u}=de(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let d=gr(o);(f.xd.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([Ge(o),Le(o)]).pipe(W(c)).subscribe(([p,l])=>{let f=At(o);i.hidden=p.x<16,s.hidden=p.x>f.width-l.width-16}),L(h(i,"click").pipe(m(()=>-1)),h(s,"click").pipe(m(()=>1))).pipe(W(c)).subscribe(p=>{let{width:l}=de(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(W(c),g(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=j(`label[for="${p.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),h(l.firstElementChild,"click").pipe(W(c),g(f=>!(f.metaKey||f.ctrlKey)),O(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return V("content.tabs.link")&&a.pipe(Ie(1),te(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let v of M("[data-tabs]"))for(let S of M(":scope > input",v)){let X=j(`label[for="${S.id}"]`);if(X!==p&&X.innerText.trim()===f){X.setAttribute("data-md-switching",""),S.click();break}}window.scrollTo({top:e.offsetTop-u});let d=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...d])])}}),a.pipe(W(c)).subscribe(()=>{for(let p of M("audio, video",e))p.offsetWidth&&p.autoplay?p.play().catch(()=>{}):p.pause()}),ss(n).pipe(O(p=>a.next(p)),A(()=>a.complete()),m(p=>P({ref:e},p)))}).pipe(et(pe))}function oi(e,t){let{viewport$:r,target$:o,print$:n}=t;return L(...M(".annotate:not(.highlight)",e).map(i=>zn(i,{target$:o,print$:n})),...M("pre:not(.mermaid) > code",e).map(i=>Yn(i,{target$:o,print$:n})),...M("a",e).map(i=>Jn(i,t)),...M("pre.mermaid",e).map(i=>Zn(i)),...M("table:not([class])",e).map(i=>ti(i)),...M("details",e).map(i=>Bn(i,{target$:o,print$:n})),...M("[data-tabs]",e).map(i=>ri(i,{viewport$:r,target$:o})),...M("[title]:not([data-preview])",e).filter(()=>V("content.tooltips")).map(i=>Xe(i,{viewport$:r})),...M(".footnote-ref",e).filter(()=>V("content.footnote.tooltips")).map(i=>Vt(i,{content$:new F(s=>{let a=new URL(i.href).hash.slice(1),c=Array.from(document.getElementById(a).cloneNode(!0).children),p=wr(...c);return s.next(p),document.body.append(p),()=>p.remove()}),viewport$:r})))}function cs(e,{alert$:t}){return t.pipe(b(r=>L($(!0),$(!1).pipe(nt(2e3))).pipe(m(o=>({message:r,active:o})))))}function ni(e,t){let r=j(".md-typeset",e);return H(()=>{let o=new T;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),cs(e,t).pipe(O(n=>o.next(n)),A(()=>o.complete()),m(n=>P({ref:e},n)))})}var ps=0;function ls(e,t){document.body.append(e);let{width:r}=de(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=vr(t),n=typeof o!="undefined"?Ge(o):$({x:0,y:0}),i=L(Ye(t),it(t)).pipe(Y());return z([i,n]).pipe(m(([s,a])=>{let{x:c,y:p}=Be(t),l=de(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:s,offset:{x:c-a.x+l.width/2-r/2,y:p-a.y+l.height+8}}}))}function ii(e){let t=e.title;if(!t.length)return y;let r=`__tooltip_${ps++}`,o=Dt(r,"inline"),n=j(".md-typeset",o);return n.innerHTML=t,H(()=>{let i=new T;return i.subscribe({next({offset:s}){o.style.setProperty("--md-tooltip-x",`${s.x}px`),o.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),L(i.pipe(g(({active:s})=>s)),i.pipe(Ae(250),g(({active:s})=>!s))).subscribe({next({active:s}){s?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe($e(16,ye)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?o.style.setProperty("--md-tooltip-0",`${-s}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),ls(o,e).pipe(O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))}).pipe(et(pe))}function ms({viewport$:e}){if(!V("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),ot(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=Je("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),b(n=>n?r:$(!1)),Q(!1))}function ai(e,t){return H(()=>z([Le(e),ms(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),Z(1))}function si(e,{header$:t,main$:r}){return H(()=>{let o=new T,n=o.pipe(oe(),ae(!0));o.pipe(ne("active"),Pe(t)).subscribe(([{active:s},{hidden:a}])=>{e.classList.toggle("md-header--shadow",s&&!a),e.hidden=a});let i=fe(M("[title]",e)).pipe(g(()=>V("content.tooltips")),J(s=>ii(s)));return r.subscribe(o),t.pipe(W(n),m(s=>P({ref:e},s)),Ve(i.pipe(W(n))))})}function fs(e,{viewport$:t,header$:r}){return Er(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=de(e);return{active:n>0&&o>=n}}),ne("active"))}function ci(e,t){return H(()=>{let r=new T;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=ue(".md-content h1");return typeof o=="undefined"?y:fs(o,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))})}function pi(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(b(()=>Le(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ne("bottom"))));return z([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),Y((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function us(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(J(o=>h(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),Z(1))}function li(e){let t=M("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=Wt("(prefers-color-scheme: light)");return H(()=>{let i=new T;return i.subscribe(s=>{if(document.body.setAttribute("data-md-color-switching",""),s.color.media==="(prefers-color-scheme)"){let a=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(a.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");s.color.scheme=c.getAttribute("data-md-color-scheme"),s.color.primary=c.getAttribute("data-md-color-primary"),s.color.accent=c.getAttribute("data-md-color-accent")}for(let[a,c]of Object.entries(s.color))document.body.setAttribute(`data-md-color-${a}`,c);for(let a=0;as.key==="Enter"),te(i,(s,a)=>a)).subscribe(({index:s})=>{s=(s+1)%t.length,t[s].click(),t[s].focus()}),i.pipe(m(()=>{let s=Ce("header"),a=window.getComputedStyle(s);return o.content=a.colorScheme,a.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(s=>r.content=`#${s}`),i.pipe(xe(pe)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),us(t).pipe(W(n.pipe(Ie(1))),vt(),O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))})}function mi(e,{progress$:t}){return H(()=>{let r=new T;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(O(o=>r.next({value:o})),A(()=>r.complete()),m(o=>({ref:e,value:o})))})}function fi(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function ds(e,t){let r=new Map;for(let o of M("url",e)){let n=j("loc",o),i=[fi(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let s of M("[rel=alternate]",o)){let a=s.getAttribute("href");a!=null&&i.push(fi(new URL(a),t))}}return r}function kt(e){return En(new URL("sitemap.xml",e)).pipe(m(t=>ds(t,new URL(e))),ve(()=>$(new Map)),le())}function ui({document$:e}){let t=new Map;e.pipe(b(()=>M("link[rel=alternate]")),m(r=>new URL(r.href)),g(r=>!t.has(r.toString())),J(r=>kt(r).pipe(m(o=>[r,o]),ve(()=>y)))).subscribe(([r,o])=>{t.set(r.toString().replace(/\/$/,""),o)}),h(document.body,"click").pipe(g(r=>!r.metaKey&&!r.ctrlKey),b(r=>{if(r.target instanceof Element){let o=r.target.closest("a");if(o&&!o.target){let n=[...t].find(([f])=>o.href.startsWith(`${f}/`));if(typeof n=="undefined")return y;let[i,s]=n,a=we();if(a.href.startsWith(i))return y;let c=Te(),p=a.href.replace(c.base,"");p=`${i}/${p}`;let l=s.has(p.split("#")[0])?new URL(p,c.base):new URL(i);return r.preventDefault(),$(l)}}return y})).subscribe(r=>st(r,!0))}var co=$t(ao());function hs(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function di({alert$:e}){co.default.isSupported()&&new F(t=>{new co.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||hs(j(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(O(t=>{t.trigger.focus()}),m(()=>Me("clipboard.copied"))).subscribe(e)}function hi(e,t){if(!(e.target instanceof Element))return y;let r=e.target.closest("a");if(r===null)return y;if(r.target||e.metaKey||e.ctrlKey)return y;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(r)):y}function bi(e){let t=new Map;for(let r of M(":scope > *",e.head))t.set(r.outerHTML,r);return t}function vi(e){for(let t of M("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function bs(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...V("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=ue(o),i=ue(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=bi(document);for(let[o,n]of bi(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Ce("container");return Ke(M("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new F(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),y}),oe(),ae(document))}function gi({sitemap$:e,location$:t,viewport$:r,progress$:o}){if(location.protocol==="file:")return y;$(document).subscribe(vi);let n=h(document.body,"click").pipe(Pe(e),b(([a,c])=>hi(a,c)),m(({href:a})=>new URL(a)),le()),i=h(window,"popstate").pipe(m(we),le());n.pipe(te(r)).subscribe(([a,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",a)}),L(n,i).subscribe(t);let s=t.pipe(ne("pathname"),b(a=>xr(a,{progress$:o}).pipe(ve(()=>(st(a,!0),y)))),b(vi),b(bs),le());return L(s.pipe(te(t,(a,c)=>c)),s.pipe(b(()=>t),ne("hash")),t.pipe(Y((a,c)=>a.pathname===c.pathname&&a.hash===c.hash),b(()=>n),O(()=>history.back()))).subscribe(a=>{var c,p;history.state!==null||!a.hash?window.scrollTo(0,(p=(c=history.state)==null?void 0:c.y)!=null?p:0):(history.scrollRestoration="auto",gn(a.hash),history.scrollRestoration="manual")}),t.subscribe(()=>{history.scrollRestoration="manual"}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),r.pipe(ne("offset"),Ae(100)).subscribe(({offset:a})=>{history.replaceState(a,"")}),V("navigation.instant.prefetch")&&L(h(document.body,"mousemove"),h(document.body,"focusin")).pipe(Pe(e),b(([a,c])=>hi(a,c)),Ae(25),Qr(({href:a})=>a),hr(a=>{let c=document.createElement("link");return c.rel="prefetch",c.href=a.toString(),document.head.appendChild(c),h(c,"load").pipe(m(()=>c),Ee(1))})).subscribe(a=>a.remove()),s}var yi=$t(ro());function xi(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").replace(/&/g,"&").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,yi.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function zt(e){return e.type===1}function Sr(e){return e.type===3}function Ei(e,t){let r=Mn(e);return L($(location.protocol!=="file:"),Je("search")).pipe(Re(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:V("search.suggest")}}})),r}function wi(e){var l;let{selectedVersionSitemap:t,selectedVersionBaseURL:r,currentLocation:o,currentBaseURL:n}=e,i=(l=po(n))==null?void 0:l.pathname;if(i===void 0)return;let s=ys(o.pathname,i);if(s===void 0)return;let a=Es(t.keys());if(!t.has(a))return;let c=po(s,a);if(!c||!t.has(c.href))return;let p=po(s,r);if(p)return p.hash=o.hash,p.search=o.search,p}function po(e,t){try{return new URL(e,t)}catch(r){return}}function ys(e,t){if(e.startsWith(t))return e.slice(t.length)}function xs(e,t){let r=Math.min(e.length,t.length),o;for(o=0;oy)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>h(document.body,"click").pipe(g(i=>!i.metaKey&&!i.ctrlKey),te(o),b(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?y:(i.preventDefault(),$(new URL(c)))}}return y}),b(i=>kt(i).pipe(m(s=>{var a;return(a=wi({selectedVersionSitemap:s,selectedVersionBaseURL:i,currentLocation:we(),currentBaseURL:t.base}))!=null?a:i})))))).subscribe(n=>st(n,!0)),z([r,o]).subscribe(([n,i])=>{j(".md-header__topic").appendChild(Wn(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var a;let i=new URL(t.base),s=__md_get("__outdated",sessionStorage,i);if(s===null){s=!0;let c=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(c)||(c=[c]);e:for(let p of c)for(let l of n.aliases.concat(n.version))if(new RegExp(p,"i").test(l)){s=!1;break e}__md_set("__outdated",s,sessionStorage,i)}if(s)for(let c of me("outdated"))c.hidden=!1})}function ws(e,{worker$:t}){let{searchParams:r}=we();r.has("q")&&(at("search",!0),e.value=r.get("q"),e.focus(),Je("search").pipe(Re(i=>!i)).subscribe(()=>{let i=we();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=Ye(e),n=L(t.pipe(Re(zt)),h(e,"keyup"),o).pipe(m(()=>e.value),Y());return z([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),Z(1))}function Si(e,{worker$:t}){let r=new T,o=r.pipe(oe(),ae(!0));z([t.pipe(Re(zt)),r],(i,s)=>s).pipe(ne("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ne("focus")).subscribe(({focus:i})=>{i&&at("search",i)}),h(e.form,"reset").pipe(W(o)).subscribe(()=>e.focus());let n=j("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),ws(e,{worker$:t}).pipe(O(i=>r.next(i)),A(()=>r.complete()),m(i=>P({ref:e},i)),Z(1))}function Oi(e,{worker$:t,query$:r}){let o=new T,n=un(e.parentElement).pipe(g(Boolean)),i=e.parentElement,s=j(":scope > :first-child",e),a=j(":scope > :last-child",e);Je("search").subscribe(l=>{a.setAttribute("role",l?"list":"presentation"),a.hidden=!l}),o.pipe(te(r),Gr(t.pipe(Re(zt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?Me("search.result.none"):Me("search.result.placeholder");break;case 1:s.textContent=Me("search.result.one");break;default:let u=br(l.length);s.textContent=Me("search.result.other",u)}});let c=o.pipe(O(()=>a.innerHTML=""),b(({items:l})=>L($(...l.slice(0,10)),$(...l.slice(10)).pipe(ot(4),Xr(n),b(([f])=>f)))),m(Fn),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(J(l=>{let f=ue("details",l);return typeof f=="undefined"?y:h(f,"toggle").pipe(W(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(g(Sr),m(({data:l})=>l)).pipe(O(l=>o.next(l)),A(()=>o.complete()),m(l=>P({ref:e},l)))}function Ts(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=we();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Li(e,t){let r=new T,o=r.pipe(oe(),ae(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(W(o)).subscribe(n=>n.preventDefault()),Ts(e,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))}function Mi(e,{worker$:t,keyboard$:r}){let o=new T,n=Ce("search-query"),i=L(h(n,"keydown"),h(n,"focus")).pipe(xe(pe),m(()=>n.value),Y());return o.pipe(Pe(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(g(({mode:a})=>a==="search")).subscribe(a=>{switch(a.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(g(Sr),m(({data:a})=>a)).pipe(O(a=>o.next(a)),A(()=>o.complete()),m(()=>({ref:e})))}function _i(e,{index$:t,keyboard$:r}){let o=Te();try{let n=Ei(o.search,t),i=Ce("search-query",e),s=Ce("search-result",e);h(e,"click").pipe(g(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>at("search",!1)),r.pipe(g(({mode:c})=>c==="search")).subscribe(c=>{let p=Ne();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of M(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}c.claim()}break;case"Escape":case"Tab":at("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...M(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Ne()&&i.focus()}}),r.pipe(g(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Si(i,{worker$:n});return L(a,Oi(s,{worker$:n,query$:a})).pipe(Ve(...me("search-share",e).map(c=>Li(c,{query$:a})),...me("search-suggest",e).map(c=>Mi(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,tt}}function Ai(e,{index$:t,location$:r}){return z([t,r.pipe(Q(we()),g(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>xi(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=x("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Ss(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),Y((i,s)=>i.height===s.height&&i.locked===s.locked))}function lo(e,o){var n=o,{header$:t}=n,r=vo(n,["header$"]);let i=j(".md-sidebar__scrollwrap",e),{y:s}=Be(i);return H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=a.pipe($e(0,ye));return p.pipe(te(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(Re()).subscribe(()=>{for(let l of M(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2})}}}),fe(M("label[tabindex]",e)).pipe(J(l=>h(l,"click").pipe(xe(pe),m(()=>l),W(c)))).subscribe(l=>{let f=j(`[id="${l.htmlFor}"]`);j(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),V("content.tooltips")&&fe(M("abbr[title]",e)).pipe(J(l=>Xe(l,{viewport$})),W(c)).subscribe(),Ss(e,r).pipe(O(l=>a.next(l)),A(()=>a.complete()),m(l=>P({ref:e},l)))})}function Ci(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return rt(ze(`${r}/releases/latest`).pipe(ve(()=>y),m(o=>({version:o.tag_name})),Qe({})),ze(r).pipe(ve(()=>y),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return ze(r).pipe(m(o=>({repositories:o.public_repos})),Qe({}))}}function ki(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return rt(ze(`${r}/releases/permalink/latest`).pipe(ve(()=>y),m(({tag_name:o})=>({version:o})),Qe({})),ze(r).pipe(ve(()=>y),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}function Hi(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return Ci(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ki(r,o)}return y}var Os;function Ls(e){return Os||(Os=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(me("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return y}return Hi(e.href).pipe(O(o=>__md_set("__source",o,sessionStorage)))}).pipe(ve(()=>y),g(t=>Object.keys(t).length>0),m(t=>({facts:t})),Z(1)))}function $i(e){let t=j(":scope > :last-child",e);return H(()=>{let r=new T;return r.subscribe(({facts:o})=>{t.appendChild(jn(o)),t.classList.add("md-source__repository--active")}),Ls(e).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function Ms(e,{viewport$:t,header$:r}){return Le(document.body).pipe(b(()=>Er(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ne("hidden"))}function Pi(e,t){return H(()=>{let r=new T;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(V("navigation.tabs.sticky")?$({hidden:!1}):Ms(e,t)).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function _s(e,{viewport$:t,header$:r}){let o=new Map,n=M(".md-nav__link",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=ue(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(ne("height"),m(({height:a})=>{let c=Ce("main"),p=j(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return Le(document.body).pipe(ne("height"),b(a=>H(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),Pe(i),b(([c,p])=>t.pipe(Ut(([l,f],{offset:{y:u},size:d})=>{let v=u+d.height>=Math.floor(a.height);for(;f.length;){let[,S]=f[0];if(S-p=u&&!v)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),Q({prev:[],next:[]}),ot(2,1),m(([a,c])=>a.prev.length{let i=new T,s=i.pipe(oe(),ae(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),V("toc.follow")){let a=L(t.pipe(Ae(1),m(()=>{})),t.pipe(Ae(250),m(()=>"smooth")));i.pipe(g(({prev:c})=>c.length>0),Pe(o.pipe(xe(pe))),te(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=vr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2,behavior:p})}}})}return V("navigation.tracking")&&t.pipe(W(s),ne("offset"),Ae(250),Ie(1),W(n.pipe(Ie(1))),vt({delay:250}),te(i)).subscribe(([,{prev:a}])=>{let c=we(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),_s(e,{viewport$:t,header$:r}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function As(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),ot(2,1),m(([s,a])=>s>a&&a>0),Y()),i=r.pipe(m(({active:s})=>s));return z([i,n]).pipe(m(([s,a])=>!(s&&a)),Y(),W(o.pipe(Ie(1))),ae(!0),vt({delay:250}),m(s=>({hidden:s})))}function Ii(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(W(s),ne("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),h(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),As(e,{viewport$:t,main$:o,target$:n}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))}function Fi({document$:e,viewport$:t}){e.pipe(b(()=>M(".md-ellipsis")),J(r=>mt(r).pipe(W(e.pipe(Ie(1))),g(o=>o),m(()=>r),Ee(1))),g(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,V("content.tooltips")?Xe(n,{viewport$:t}).pipe(W(e.pipe(Ie(1))),A(()=>n.removeAttribute("title"))):y})).subscribe(),V("content.tooltips")&&e.pipe(b(()=>M(".md-status")),J(r=>Xe(r,{viewport$:t}))).subscribe()}function ji({document$:e,tablet$:t}){e.pipe(b(()=>M(".md-toggle--indeterminate")),O(r=>{r.indeterminate=!0,r.checked=!1}),J(r=>h(r,"change").pipe(Jr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),te(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function Cs(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Ui({document$:e}){e.pipe(b(()=>M("[data-md-scrollfix]")),O(t=>t.removeAttribute("data-md-scrollfix")),g(Cs),J(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Wi({viewport$:e,tablet$:t}){z([Je("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>$(r).pipe(nt(r?400:100))),te(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ks(){return location.protocol==="file:"?_t(`${new URL("search/search_index.js",Or.base)}`).pipe(m(()=>__index),Z(1)):ze(new URL("search/search_index.json",Or.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ct=an(),Kt=bn(),Ht=yn(Kt),mo=hn(),ke=Ln(),Lr=Wt("(min-width: 60em)"),Vi=Wt("(min-width: 76.25em)"),Ni=xn(),Or=Te(),zi=document.forms.namedItem("search")?ks():tt,fo=new T;di({alert$:fo});ui({document$:ct});var uo=new T,qi=kt(Or.base);V("navigation.instant")&&gi({sitemap$:qi,location$:Kt,viewport$:ke,progress$:uo}).subscribe(ct);var Di;((Di=Or.version)==null?void 0:Di.provider)==="mike"&&Ti({document$:ct});L(Kt,Ht).pipe(nt(125)).subscribe(()=>{at("drawer",!1),at("search",!1)});mo.pipe(g(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=ue("link[rel=prev]");typeof t!="undefined"&&st(t);break;case"n":case".":let r=ue("link[rel=next]");typeof r!="undefined"&&st(r);break;case"Enter":let o=Ne();o instanceof HTMLLabelElement&&o.click()}});Fi({viewport$:ke,document$:ct});ji({document$:ct,tablet$:Lr});Ui({document$:ct});Wi({viewport$:ke,tablet$:Lr});var ft=ai(Ce("header"),{viewport$:ke}),qt=ct.pipe(m(()=>Ce("main")),b(e=>pi(e,{viewport$:ke,header$:ft})),Z(1)),Hs=L(...me("consent").map(e=>An(e,{target$:Ht})),...me("dialog").map(e=>ni(e,{alert$:fo})),...me("palette").map(e=>li(e)),...me("progress").map(e=>mi(e,{progress$:uo})),...me("search").map(e=>_i(e,{index$:zi,keyboard$:mo})),...me("source").map(e=>$i(e))),$s=H(()=>L(...me("announce").map(e=>_n(e)),...me("content").map(e=>oi(e,{sitemap$:qi,viewport$:ke,target$:Ht,print$:Ni})),...me("content").map(e=>V("search.highlight")?Ai(e,{index$:zi,location$:Kt}):y),...me("header").map(e=>si(e,{viewport$:ke,header$:ft,main$:qt})),...me("header-title").map(e=>ci(e,{viewport$:ke,header$:ft})),...me("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?eo(Vi,()=>lo(e,{viewport$:ke,header$:ft,main$:qt})):eo(Lr,()=>lo(e,{viewport$:ke,header$:ft,main$:qt}))),...me("tabs").map(e=>Pi(e,{viewport$:ke,header$:ft})),...me("toc").map(e=>Ri(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})),...me("top").map(e=>Ii(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})))),Ki=ct.pipe(b(()=>$s),Ve(Hs),Z(1));Ki.subscribe();window.document$=ct;window.location$=Kt;window.target$=Ht;window.keyboard$=mo;window.viewport$=ke;window.tablet$=Lr;window.screen$=Vi;window.print$=Ni;window.alert$=fo;window.progress$=uo;window.component$=Ki;})(); +//# sourceMappingURL=bundle.e71a0d61.min.js.map + diff --git a/assets/javascripts/bundle.e71a0d61.min.js.map b/assets/javascripts/bundle.e71a0d61.min.js.map new file mode 100644 index 00000000..23451b54 --- /dev/null +++ b/assets/javascripts/bundle.e71a0d61.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/escape-html/index.js", "node_modules/clipboard/dist/clipboard.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/tslib/tslib.es6.mjs", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/BehaviorSubject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/QueueAction.ts", "node_modules/rxjs/src/internal/scheduler/QueueScheduler.ts", "node_modules/rxjs/src/internal/scheduler/queue.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounce.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinct.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/exhaustMap.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip2/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/link/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/alternate/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/findurl/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*\n * Copyright (c) 2016-2025 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n fetchSitemap,\n setupAlternate,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 60em)\")\nconst screen$ = watchMedia(\"(min-width: 76.25em)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up language selector */\nsetupAlternate({ document$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up sitemap for instant navigation and previews */\nconst sitemap$ = fetchSitemap(config.base)\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ sitemap$, location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ viewport$, document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { sitemap$, viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/******************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */\n/* global Reflect, Promise, SuppressedError, Symbol, Iterator */\n\nvar extendStatics = function(d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n return extendStatics(d, b);\n};\n\nexport function __extends(d, b) {\n if (typeof b !== \"function\" && b !== null)\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n}\n\nexport var __assign = function() {\n __assign = Object.assign || function __assign(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\n }\n return t;\n }\n return __assign.apply(this, arguments);\n}\n\nexport function __rest(s, e) {\n var t = {};\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\n t[p] = s[p];\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\n t[p[i]] = s[p[i]];\n }\n return t;\n}\n\nexport function __decorate(decorators, target, key, desc) {\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n}\n\nexport function __param(paramIndex, decorator) {\n return function (target, key) { decorator(target, key, paramIndex); }\n}\n\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\n var _, done = false;\n for (var i = decorators.length - 1; i >= 0; i--) {\n var context = {};\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\n if (kind === \"accessor\") {\n if (result === void 0) continue;\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\n if (_ = accept(result.get)) descriptor.get = _;\n if (_ = accept(result.set)) descriptor.set = _;\n if (_ = accept(result.init)) initializers.unshift(_);\n }\n else if (_ = accept(result)) {\n if (kind === \"field\") initializers.unshift(_);\n else descriptor[key] = _;\n }\n }\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\n done = true;\n};\n\nexport function __runInitializers(thisArg, initializers, value) {\n var useValue = arguments.length > 2;\n for (var i = 0; i < initializers.length; i++) {\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\n }\n return useValue ? value : void 0;\n};\n\nexport function __propKey(x) {\n return typeof x === \"symbol\" ? x : \"\".concat(x);\n};\n\nexport function __setFunctionName(f, name, prefix) {\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\n};\n\nexport function __metadata(metadataKey, metadataValue) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\n}\n\nexport function __awaiter(thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n}\n\nexport function __generator(thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n}\n\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nexport function __exportStar(m, o) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\n}\n\nexport function __values(o) {\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\n if (m) return m.call(o);\n if (o && typeof o.length === \"number\") return {\n next: function () {\n if (o && i >= o.length) o = void 0;\n return { value: o && o[i++], done: !o };\n }\n };\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\n}\n\nexport function __read(o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n}\n\n/** @deprecated */\nexport function __spread() {\n for (var ar = [], i = 0; i < arguments.length; i++)\n ar = ar.concat(__read(arguments[i]));\n return ar;\n}\n\n/** @deprecated */\nexport function __spreadArrays() {\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\n r[k] = a[j];\n return r;\n}\n\nexport function __spreadArray(to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i in from)) {\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\n ar[i] = from[i];\n }\n }\n return to.concat(ar || Array.prototype.slice.call(from));\n}\n\nexport function __await(v) {\n return this instanceof __await ? (this.v = v, this) : new __await(v);\n}\n\nexport function __asyncGenerator(thisArg, _arguments, generator) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\n return i = Object.create((typeof AsyncIterator === \"function\" ? AsyncIterator : Object).prototype), verb(\"next\"), verb(\"throw\"), verb(\"return\", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;\n function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }\n function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\n function fulfill(value) { resume(\"next\", value); }\n function reject(value) { resume(\"throw\", value); }\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\n}\n\nexport function __asyncDelegator(o) {\n var i, p;\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\n}\n\nexport function __asyncValues(o) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var m = o[Symbol.asyncIterator], i;\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\n}\n\nexport function __makeTemplateObject(cooked, raw) {\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\n return cooked;\n};\n\nvar __setModuleDefault = Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n};\n\nexport function __importStar(mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n}\n\nexport function __importDefault(mod) {\n return (mod && mod.__esModule) ? mod : { default: mod };\n}\n\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\n}\n\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\n}\n\nexport function __classPrivateFieldIn(state, receiver) {\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\n}\n\nexport function __addDisposableResource(env, value, async) {\n if (value !== null && value !== void 0) {\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === void 0) {\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async) inner = dispose;\n }\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\n if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };\n env.stack.push({ value: value, dispose: dispose, async: async });\n }\n else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n}\n\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n};\n\nexport function __disposeResources(env) {\n function fail(e) {\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\n }\n else s |= 1;\n }\n catch (e) {\n fail(e);\n }\n }\n if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError) throw env.error;\n }\n return next();\n}\n\nexport default {\n __extends,\n __assign,\n __rest,\n __decorate,\n __param,\n __metadata,\n __awaiter,\n __generator,\n __createBinding,\n __exportStar,\n __values,\n __read,\n __spread,\n __spreadArrays,\n __spreadArray,\n __await,\n __asyncGenerator,\n __asyncDelegator,\n __asyncValues,\n __makeTemplateObject,\n __importStar,\n __importDefault,\n __classPrivateFieldGet,\n __classPrivateFieldSet,\n __classPrivateFieldIn,\n __addDisposableResource,\n __disposeResources,\n};\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n */\nexport class Subscription implements SubscriptionLike {\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param value The `next` value.\n */\n next(value: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param err The `error` exception.\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as ((value: T) => void) | undefined,\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent.\n * @param subscriber The stopped subscriber.\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @param subscribe The function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @param subscribe the subscriber function to be passed to the Observable constructor\n * @return A new observable.\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @param operator the operator defining the operation to take on the observable\n * @return A new observable with the Operator applied.\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param observerOrNext Either an {@link Observer} with some or all callback methods,\n * or the `next` handler that is called for each value emitted from the subscribed Observable.\n * @param error A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param complete A handler for a terminal event resulting from successful completion.\n * @return A subscription reference to the registered handlers.\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next A handler for each value emitted by the observable.\n * @return A promise that either resolves on observable completion or\n * rejects with the handled error.\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @return This instance of the observable.\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n *\n * @return The Observable result of all the operators having been called\n * in the order they were passed in.\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return Observable that this Subject casts to.\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { Subject } from './Subject';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\n\n/**\n * A variant of Subject that requires an initial value and emits its current\n * value whenever it is subscribed to.\n */\nexport class BehaviorSubject extends Subject {\n constructor(private _value: T) {\n super();\n }\n\n get value(): T {\n return this.getValue();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n\n getValue(): T {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n\n next(value: T): void {\n super.next((this._value = value));\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param _bufferSize The size of the buffer to replay on subscription\n * @param _windowTime The amount of time the buffered items will stay buffered\n * @param _timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param state Some contextual data that the `work` function uses when called by the\n * Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is implicit\n * and defined by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param work A function representing a task, or some unit of work to be\n * executed by the Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is\n * implicit and defined by the Scheduler itself.\n * @param state Some contextual data that the `work` function uses when called\n * by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { Subscription } from '../Subscription';\nimport { QueueScheduler } from './QueueScheduler';\nimport { SchedulerAction } from '../types';\nimport { TimerHandle } from './timerHandle';\n\nexport class QueueAction extends AsyncAction {\n constructor(protected scheduler: QueueScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (delay > 0) {\n return super.schedule(state, delay);\n }\n this.delay = delay;\n this.state = state;\n this.scheduler.flush(this);\n return this;\n }\n\n public execute(state: T, delay: number): any {\n return delay > 0 || this.closed ? super.execute(state, delay) : this._execute(state, delay);\n }\n\n protected requestAsyncId(scheduler: QueueScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n\n // Otherwise flush the scheduler starting with this action.\n scheduler.flush(this);\n\n // HACK: In the past, this was returning `void`. However, `void` isn't a valid\n // `TimerHandle`, and generally the return value here isn't really used. So the\n // compromise is to return `0` which is both \"falsy\" and a valid `TimerHandle`,\n // as opposed to refactoring every other instanceo of `requestAsyncId`.\n return 0;\n }\n}\n", "import { AsyncScheduler } from './AsyncScheduler';\n\nexport class QueueScheduler extends AsyncScheduler {\n}\n", "import { QueueAction } from './QueueAction';\nimport { QueueScheduler } from './QueueScheduler';\n\n/**\n *\n * Queue Scheduler\n *\n * Put every next task on a queue, instead of executing it immediately\n *\n * `queue` scheduler, when used with delay, behaves the same as {@link asyncScheduler} scheduler.\n *\n * When used without delay, it schedules given task synchronously - executes it right when\n * it is scheduled. However when called recursively, that is when inside the scheduled task,\n * another task is scheduled with queue scheduler, instead of executing immediately as well,\n * that task will be put on a queue and wait for current one to finish.\n *\n * This means that when you execute task with `queue` scheduler, you are sure it will end\n * before any other task scheduled with that scheduler will start.\n *\n * ## Examples\n * Schedule recursively first, then do something\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(() => {\n * queueScheduler.schedule(() => console.log('second')); // will not happen now, but will be put on a queue\n *\n * console.log('first');\n * });\n *\n * // Logs:\n * // \"first\"\n * // \"second\"\n * ```\n *\n * Reschedule itself recursively\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(function(state) {\n * if (state !== 0) {\n * console.log('before', state);\n * this.schedule(state - 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * console.log('after', state);\n * }\n * }, 0, 3);\n *\n * // In scheduler that runs recursively, you would expect:\n * // \"before\", 3\n * // \"before\", 2\n * // \"before\", 1\n * // \"after\", 1\n * // \"after\", 2\n * // \"after\", 3\n *\n * // But with queue it logs:\n * // \"before\", 3\n * // \"after\", 3\n * // \"before\", 2\n * // \"after\", 2\n * // \"before\", 1\n * // \"after\", 1\n * ```\n */\n\nexport const queueScheduler = new QueueScheduler(QueueAction);\n\n/**\n * @deprecated Renamed to {@link queueScheduler}. Will be removed in v8.\n */\nexport const queue = queueScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && id === scheduler._scheduled && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n let flushId;\n if (action) {\n flushId = action.id;\n } else {\n flushId = this._scheduled;\n this._scheduled = undefined;\n }\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an

\ No newline at end of file diff --git a/credits/index.html b/credits/index.html new file mode 100644 index 00000000..02305c78 --- /dev/null +++ b/credits/index.html @@ -0,0 +1,35 @@ + Credits - mkdocstrings

Credits¤

These projects were used to build mkdocstrings. Thank you!

Python | uv | copier-uv

Runtime dependencies¤

Project Summary Version (accepted) Version (last resolved) License
click Composable command line interface toolkit >=7.0 8.3.1 BSD-3-Clause
colorama Cross-platform colored terminal text. >=0.4 0.4.6 BSD License
ghp-import Copy your docs directly to the gh-pages branch. >=1.0 2.1.0 Apache Software License
griffe Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API. >=1.13 1.15.0 ISC
Jinja2 A very fast and expressive template engine. >=3.1, >=3.0 3.1.6 BSD License
Markdown Python implementation of John Gruber's Markdown. >=3.6, >=3.2 3.10 BSD-3-Clause
MarkupSafe Safely add untrusted strings to HTML/XML markup. >=2.0, >=1.1 3.0.3 BSD-3-Clause
mergedeep A deep merge function for 🐍. >=1.3.4 1.3.4 MIT License
mkdocs Project documentation with Markdown. >=1.6 1.6.1 BSD-2-Clause
mkdocs-autorefs Automatically link across pages in MkDocs. >=1.4 1.4.3 ISC
mkdocs-get-deps MkDocs extension that lists all dependencies according to a mkdocs.yml file >=0.2.0 0.2.0 MIT
mkdocstrings-python A Python handler for mkdocstrings. >=1.16.2 1.19.0 ISC
packaging Core utilities for Python packages >=22, >=20.5 25.0 Apache Software License + BSD License
pathspec Utility library for gitignore style pattern matching of file paths. >=0.9.0, >=0.11.1 0.12.1 Mozilla Public License 2.0 (MPL 2.0)
platformdirs A small Python package for determining appropriate platform-specific dirs, e.g. a user data dir. >=4.4, >=2.2.0 4.5.0 MIT
pymdown-extensions Extension pack for Python Markdown. >=9, >=6.3 10.17.1 MIT
python-dateutil Extensions to the standard Python datetime module >=2.8.1 2.9.0.post0 BSD License + Apache Software License
PyYAML YAML parser and emitter for Python 6.0.3 MIT
pyyaml_env_tag A custom YAML tag for referencing environment variables in YAML files. >=0.1 1.1 MIT
six Python 2 and 3 compatibility utilities >=1.5, >=1.15, <2 1.17.0 MIT
typing_extensions Backported and Experimental Type Hints for Python 3.9+ >=4.9, >=4.0 4.15.0 PSF-2.0
watchdog Filesystem events monitoring >=2.0 6.0.0 Apache-2.0

Development dependencies¤

Project Summary Version (accepted) Version (last resolved) License
ansimarkup Produce colored terminal text with an xml-like markup ~=1.4 1.5.0 Revised BSD License
babel Internationalization utilities >=2.7.0 2.17.0 BSD-3-Clause
backrefs A wrapper around re and regex that adds additional back references. >=5.7.post1 6.1 MIT
beautifulsoup4 Screen-scraping library >=4.12 4.14.2 MIT License
build A simple, correct Python build frontend >=1.2 1.3.0 MIT
cappa Declarative CLI argument parser. >=0.29 0.30.4 ?
certifi Python package for providing Mozilla's CA Bundle. >=2017.4.17 2025.11.12 MPL-2.0
cffi Foreign Function Interface for Python calling C code. >=1.14 2.0.0 MIT
charset-normalizer The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet. >=2, <4 3.4.4 MIT
click Composable command line interface toolkit >=7.0 8.3.1 BSD-3-Clause
colorama Cross-platform colored terminal text. >=0.4 0.4.6 BSD License
coverage Code coverage measurement for Python >=7.10.6 7.12.0 Apache-2.0
cryptography cryptography is a package which provides cryptographic recipes and primitives to Python developers. >=2.0 46.0.3 Apache-2.0 OR BSD-3-Clause
csscompressor A python port of YUI CSS Compressor >=0.9.5 0.9.5 BSD
dirty-equals Doing dirty (but extremely useful) things with equals. >=0.9 0.11 MIT
docutils Docutils -- Python Documentation Utilities >=0.21.2 0.22.3 Public Domain + BSD License + GNU General Public License (GPL)
duty A simple task runner. >=1.6 1.6.3 ISC
execnet execnet: rapid multi-Python deployment >=2.1 2.1.2 MIT
failprint Run a command, print its output only if it fails. >=1.0.5 1.0.6 ISC
ghp-import Copy your docs directly to the gh-pages branch. >=1.0 2.1.0 Apache Software License
git-changelog Automatic Changelog generator using Jinja2 templates. >=2.5 2.6.3 ISC
gitdb Git Object Database >=4.0.1, <5 4.0.12 BSD License
GitPython GitPython is a Python library used to interact with Git repositories >=3.1.44 3.1.45 BSD-3-Clause
griffe Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API. >=1.13 1.15.0 ISC
htmlmin2 An HTML Minifier >=0.1.13 0.1.13 BSD
humanize Python humanize utilities >=4.9 4.14.0 MIT
id A tool for generating OIDC identities 1.5.0 Apache Software License
idna Internationalized Domain Names in Applications (IDNA) >=2.5, <4 3.11 BSD-3-Clause
iniconfig brain-dead simple config-ini parsing >=1.0.1 2.3.0 MIT
jaraco.classes Utility functions for Python class constructs 3.4.0 MIT License
jaraco.context Useful decorators and context managers 6.0.1 MIT License
jaraco.functools Functools like those found in stdlib 4.3.0 MIT
jeepney Low-level, pure Python DBus protocol wrapper. >=0.4.2 0.9.0 MIT
Jinja2 A very fast and expressive template engine. >=3.1, >=3.0 3.1.6 BSD License
jsmin JavaScript minifier. >=3.0.1 3.0.1 MIT License
keyring Store and access your passwords safely. >=21.2.0 25.7.0 MIT
Markdown Python implementation of John Gruber's Markdown. >=3.6, >=3.2 3.10 BSD-3-Clause
markdown-callouts Markdown extension: a classier syntax for admonitions >=0.4 0.4.0 MIT
markdown-exec Utilities to execute code blocks in Markdown files. >=1.8 1.12.1 ISC
markdown-it-py Python port of markdown-it. Markdown parsing, done right! >=2.2.0 3.0.0 MIT License
markdownify Convert HTML to markdown. >=0.14 1.2.2 MIT License
MarkupSafe Safely add untrusted strings to HTML/XML markup. >=2.0, >=1.1 3.0.3 BSD-3-Clause
mdformat CommonMark compliant Markdown formatter >=0.7.21 0.7.22 MIT License
mdformat_tables An mdformat plugin for rendering tables. >=1.0 1.0.0 MIT License
mdurl Markdown URL utilities ~=0.1 0.1.2 MIT License
mergedeep A deep merge function for 🐍. >=1.3.4 1.3.4 MIT License
mkdocs Project documentation with Markdown. >=1.6 1.6.1 BSD-2-Clause
mkdocs-autorefs Automatically link across pages in MkDocs. >=1.4 1.4.3 ISC
mkdocs-coverage MkDocs plugin to integrate your coverage HTML report into your site. >=1.0 2.0.0 ISC
mkdocs-get-deps MkDocs extension that lists all dependencies according to a mkdocs.yml file >=0.2.0 0.2.0 MIT
mkdocs-git-revision-date-localized-plugin Mkdocs plugin that enables displaying the localized date of the last git modification of a markdown file. >=1.2 1.5.0 MIT
mkdocs-llmstxt MkDocs plugin to generate an /llms.txt file. >=0.2 0.4.0 ISC
mkdocs-material Documentation that simply works >=9.5 9.7.0 MIT
mkdocs-material-extensions Extension pack for Python Markdown and MkDocs Material. >=1.3 1.3.1 MIT
mkdocs-minify-plugin An MkDocs plugin to minify HTML, JS or CSS files prior to being written to disk >=0.8 0.8.0 MIT
mkdocs-redirects A MkDocs plugin for dynamic page redirects to prevent broken links >=1.2.1 1.2.2 MIT
mkdocs-section-index MkDocs plugin to allow clickable sections that lead to an index page >=0.3 0.3.10 MIT
mkdocstrings-python A Python handler for mkdocstrings. >=1.16.2 1.19.0 ISC
more-itertools More routines for operating on iterables, beyond itertools 10.8.0 MIT
mypy Optional static typing for Python >=1.10 1.18.2 MIT
mypy_extensions Type system extensions for programs checked with the mypy type checker. >=1.0.0 1.1.0 MIT
nh3 Python binding to Ammonia HTML sanitizer Rust crate >=0.2.14 0.3.2 MIT
packaging Core utilities for Python packages >=22, >=20.5 25.0 Apache Software License + BSD License
paginate Divides large result sets into pages for easier browsing >=0.5 0.5.7 MIT
pathspec Utility library for gitignore style pattern matching of file paths. >=0.9.0, >=0.11.1 0.12.1 Mozilla Public License 2.0 (MPL 2.0)
platformdirs A small Python package for determining appropriate platform-specific dirs, e.g. a user data dir. >=4.4, >=2.2.0 4.5.0 MIT
pluggy plugin and hook calling mechanisms for python >=1.5, <2 1.6.0 MIT
ptyprocess Run a subprocess in a pseudo terminal ~=0.6 0.7.0 ISC License (ISCL)
pycparser C parser in Python 2.23 BSD-3-Clause
Pygments Pygments is a syntax highlighting package written in Python. >=2.7.2 2.19.2 BSD-2-Clause
pymdown-extensions Extension pack for Python Markdown. >=9, >=6.3 10.17.1 MIT
pyproject_hooks Wrappers to call pyproject.toml-based build backend hooks. 1.2.0 MIT License
pytest pytest: simple powerful testing with Python >=8.2 9.0.1 MIT
pytest-cov Pytest plugin for measuring coverage. >=5.0 7.0.0 MIT
pytest-randomly Pytest plugin to randomly order tests and control random.seed. >=3.15 4.0.1 MIT
pytest-xdist pytest xdist plugin for distributed testing, most importantly across multiple CPUs >=3.6 3.8.0 MIT
python-dateutil Extensions to the standard Python datetime module >=2.8.1 2.9.0.post0 BSD License + Apache Software License
PyYAML YAML parser and emitter for Python 6.0.3 MIT
pyyaml_env_tag A custom YAML tag for referencing environment variables in YAML files. >=0.1 1.1 MIT
readme_renderer readme_renderer is a library for rendering readme descriptions for Warehouse >=35.0 44.0 Apache License, Version 2.0
requests Python HTTP for Humans. >=2.20 2.32.5 Apache-2.0
requests-toolbelt A utility belt for advanced users of python-requests >=0.8.0, !=0.9.0 1.0.0 Apache 2.0
rfc3986 Validating URI References per RFC 3986 >=1.4.0 2.0.0 Apache 2.0
rich Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal >=12.0.0 14.2.0 MIT
ruff An extremely fast Python linter and code formatter, written in Rust. >=0.4 0.14.5 MIT License
SecretStorage Python bindings to FreeDesktop.org Secret Service API >=3.2 3.4.1 BSD-3-Clause
semver Python helper for Semantic Versioning (https://semver.org) >=3.0 3.0.4 BSD License
six Python 2 and 3 compatibility utilities >=1.5, >=1.15, <2 1.17.0 MIT
smmap A pure Python implementation of a sliding window memory map manager >=3.0.1, <6 5.0.2 BSD-3-Clause
soupsieve A modern CSS selector implementation for Beautiful Soup. >1.2 2.8 MIT
twine Collection of utilities for publishing packages on PyPI >=5.1 6.2.0 Apache-2.0
type-lens type-lens is a Python template project designed to simplify the setup of a new project. >=0.2.5 0.2.6 MIT
types-Markdown Typing stubs for Markdown >=3.6 3.10.0.20251106 Apache-2.0
types-PyYAML Typing stubs for PyYAML >=6.0 6.0.12.20250915 Apache-2.0
typing_extensions Backported and Experimental Type Hints for Python 3.9+ >=4.9, >=4.0 4.15.0 PSF-2.0
urllib3 HTTP library with thread-safe connection pooling, file post, and more. >=1.26.0 2.5.0 MIT
watchdog Filesystem events monitoring >=2.0 6.0.0 Apache-2.0
wcwidth Measures the displayed width of unicode strings in a terminal >=0.2.13 0.2.14 MIT
yore Manage legacy code with comments. >=0.3.3 0.4.6 ISC

More credits from the author

\ No newline at end of file diff --git a/css/material.css b/css/material.css new file mode 100644 index 00000000..9e8c14a6 --- /dev/null +++ b/css/material.css @@ -0,0 +1,4 @@ +/* More space at the bottom of the page. */ +.md-main__inner { + margin-bottom: 1.5rem; +} diff --git a/css/mkdocstrings.css b/css/mkdocstrings.css new file mode 100644 index 00000000..3447549c --- /dev/null +++ b/css/mkdocstrings.css @@ -0,0 +1,82 @@ +/* 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); +} + +/* Avoid breaking parameters name, etc. in table cells. */ +td code { + word-break: normal !important; +} + +/* Hide parameters in ToC. */ +li.md-nav__item:has(> a[href*="("]) { + display: none; +} + +/* Tree-like output for backlinks. */ +.doc-backlink-list { + --tree-clr: var(--md-default-fg-color); + --tree-font-size: 1rem; + --tree-item-height: 1; + --tree-offset: 1rem; + --tree-thickness: 1px; + --tree-style: solid; + display: grid; + list-style: none !important; +} + +.doc-backlink-list li > span:first-child { + text-indent: .3rem; +} +.doc-backlink-list li { + padding-inline-start: var(--tree-offset); + border-left: var(--tree-thickness) var(--tree-style) var(--tree-clr); + position: relative; + margin-left: 0 !important; + + &:last-child { + border-color: transparent; + } + &::before{ + content: ''; + position: absolute; + top: calc(var(--tree-item-height) / 2 * -1 * var(--tree-font-size) + var(--tree-thickness)); + left: calc(var(--tree-thickness) * -1); + width: calc(var(--tree-offset) + var(--tree-thickness) * 2); + height: calc(var(--tree-item-height) * var(--tree-font-size)); + border-left: var(--tree-thickness) var(--tree-style) var(--tree-clr); + border-bottom: var(--tree-thickness) var(--tree-style) var(--tree-clr); + } + &::after{ + content: ''; + position: absolute; + border-radius: 50%; + background-color: var(--tree-clr); + top: calc(var(--tree-item-height) / 2 * 1rem); + left: var(--tree-offset) ; + translate: calc(var(--tree-thickness) * -1) calc(var(--tree-thickness) * -1); + } +} diff --git a/docs/css/style.css b/css/style.css similarity index 100% rename from docs/css/style.css rename to css/style.css diff --git a/css/timeago.css b/css/timeago.css new file mode 100644 index 00000000..f7ab7d69 --- /dev/null +++ b/css/timeago.css @@ -0,0 +1,15 @@ +/* + timeago output is dynamic, which breaks when you print a page. + + This CSS is only included when type: timeago + and ensures fallback to type "iso_date" when printing. + + */ + +.git-revision-date-localized-plugin-iso_date { display: none } + +@media print { + .git-revision-date-localized-plugin-iso_date { display: inline } + .git-revision-date-localized-plugin-timeago { display: none } +} + diff --git a/docs/.overrides/main.html b/docs/.overrides/main.html deleted file mode 100644 index 3bfd4775..00000000 --- a/docs/.overrides/main.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "base.html" %} - -{% block announce %} - ⚠️ mkdocstrings is in maintenance mode! - blog post - -{% endblock %} diff --git a/docs/.overrides/partials/comments.html b/docs/.overrides/partials/comments.html deleted file mode 100644 index 793b075c..00000000 --- a/docs/.overrides/partials/comments.html +++ /dev/null @@ -1,57 +0,0 @@ - - - \ No newline at end of file diff --git a/docs/changelog.md b/docs/changelog.md deleted file mode 100644 index 0536cbbe..00000000 --- a/docs/changelog.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Changelog ---- - ---8<-- "CHANGELOG.md" diff --git a/docs/code_of_conduct.md b/docs/code_of_conduct.md deleted file mode 100644 index 002b2a04..00000000 --- a/docs/code_of_conduct.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Code of Conduct ---- - ---8<-- "CODE_OF_CONDUCT.md" diff --git a/docs/contributing.md b/docs/contributing.md deleted file mode 100644 index 61935e5d..00000000 --- a/docs/contributing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Contributing ---- - ---8<-- "CONTRIBUTING.md" diff --git a/docs/credits.md b/docs/credits.md deleted file mode 100644 index f6ab1aa2..00000000 --- a/docs/credits.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: Credits -hide: -- toc ---- - -```python exec="yes" ---8<-- "scripts/gen_credits.py" -``` diff --git a/docs/css/apidocs.css b/docs/css/apidocs.css deleted file mode 100644 index 35ec8f19..00000000 --- a/docs/css/apidocs.css +++ /dev/null @@ -1,21 +0,0 @@ -/* 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); -} diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 82377e21..00000000 --- a/docs/index.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Overview -hide: -- feedback ---- - ---8<-- "README.md" diff --git a/docs/license.md b/docs/license.md deleted file mode 100644 index 5b25a00f..00000000 --- a/docs/license.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: License -hide: -- feedback ---- - -# License - -``` ---8<-- "LICENSE" -``` diff --git a/docs/recipes.md b/docs/recipes.md deleted file mode 100644 index a52347bd..00000000 --- a/docs/recipes.md +++ /dev/null @@ -1,456 +0,0 @@ -On this page you will find various recipes, tips and tricks -for *mkdocstrings* and more generally Markdown documentation. - -## Automatic code reference pages - -TIP: **[mkdocs-autoapi](https://github.com/jcayers20/mkdocs-autoapi) and [mkdocs-api-autonav](https://github.com/tlambert03/mkdocs-api-autonav) are MkDocs plugins that automatically generate API documentation from your project's source code. They were inspired by the recipe below.** - -*mkdocstrings* allows to inject documentation for any object -into Markdown pages. But as the project grows, it quickly becomes -quite tedious to keep the autodoc instructions, or even the dedicated -Markdown files in sync with all your source files and objects. - -In this recipe, we will iteratively automate the process -of generating these pages at each build of the documentation. - ---- - -Let say you have a project called `project`. -This project has a lot of source files, or modules, -which live in the `src` folder: - -```tree -repo/ - src/ - project/ - lorem - ipsum - dolor - sit - amet -``` - -Without an automatic process, you will have to manually -create a Markdown page for each one of these modules, -with the corresponding autodoc instruction, for example `::: project.lorem`, -and also add entry in MkDocs' navigation option (`nav` in `mkdocs.yml`). -With a lot of modules, this is quickly getting cumbersome. - -Lets fix that. - -### Generate pages on-the-fly - -In this recipe, we suggest to use the [mkdocs-gen-files plugin](https://github.com/oprypin/mkdocs-gen-files). -This plugin exposes utilities to generate files at build time. -These files won't be written to the docs directory: you don't have -to track and version them. They are transparently generated each -time you build your docs. This is perfect for our use-case! - -Add `mkdocs-gen-files` to your project's docs dependencies, -and configure it like so: - -```yaml title="mkdocs.yml" -plugins: -- search # (1)! -- gen-files: - scripts: - - scripts/gen_ref_pages.py # (2)! -- mkdocstrings -``` - -1. Don't forget to load the `search` plugin when redefining the `plugins` item. -2. The magic happens here, see below how it works. - -mkdocs-gen-files is able to run Python scripts at build time. -The Python script that we will execute lives in a scripts folder, -and is named `gen_ref_pages.py`, like "generate code reference pages". - -```tree -repo/ - docs/ - index.md - scripts/ - gen_ref_pages.py - src/ - project/ - mkdocs.yml -``` - -```python title="scripts/gen_ref_pages.py" -"""Generate the code reference pages.""" - -from pathlib import Path - -import mkdocs_gen_files - -root = Path(__file__).parent.parent -src = root / "src" # (1)! - -for path in sorted(src.rglob("*.py")): # (2)! - module_path = path.relative_to(src).with_suffix("") # (3)! - doc_path = path.relative_to(src).with_suffix(".md") # (4)! - full_doc_path = Path("reference", doc_path) # (5)! - - parts = tuple(module_path.parts) - - if parts[-1] == "__init__": # (6)! - parts = parts[:-1] - elif parts[-1] == "__main__": - continue - - with mkdocs_gen_files.open(full_doc_path, "w") as fd: # (7)! - identifier = ".".join(parts) # (8)! - print("::: " + identifier, file=fd) # (9)! - - mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root)) # (10)! -``` - -1. It's important to build a path relative to the script itself, - to make it possible to build the docs with MkDocs' - [`-f` option](https://www.mkdocs.org/user-guide/cli/#mkdocs-build). -2. Here we recursively list all `.py` files, but you can adapt the code to list - files with other extensions of course, supporting other languages than Python. -3. The module path will look like `project/lorem`. - It will be used to build the *mkdocstrings* autodoc identifier. -4. This is the partial path of the Markdown page for the module. -5. This is the full path of the Markdown page within the docs. - Here we put all reference pages into a `reference` folder. -6. This part is only relevant for Python modules. We skip `__main__` modules and - remove `__init__` from the module parts as it's implicit during imports. -7. Magic! Add the file to MkDocs pages, without actually writing it in the docs folder. -8. Build the autodoc identifier. Here we document Python modules, so the identifier - is a dot-separated path, like `project.lorem`. -9. Actually write to the magic file. -10. We can even set the `edit_uri` on the pages. - -> NOTE: -> It is important to look out for correct edit page behaviour when using generated pages. -> For example, if we have `edit_uri` set to `blob/master/docs/` and the following -> file structure: -> -> ```tree -> repo/ -> mkdocs.yml -> docs/ -> index.md -> scripts/ -> gen_ref_pages.py -> src/ -> project/ -> lorem.py -> ipsum.py -> dolor.py -> sit.py -> amet.py -> ``` -> -> Then we will have to change our `set_edit_path` call to: -> -> ```python -> mkdocs_gen_files.set_edit_path(full_doc_path, Path("../") / path) # (1)! -> ``` -> -> 1. Path can be used to traverse the structure in any way you may need, but -> remember to use relative paths! -> -> ...so that it correctly sets the edit path of (for example) `lorem.py` to -> `/blob/master/src/project/lorem.py` instead of -> `/blob/master/docs/src/project/lorem.py`. - -With this script, a `reference` folder is automatically created -each time we build our docs. This folder contains a Markdown page -for each of our source modules, and each of these pages -contains a single line of the form `::: project.module` -(module being `lorem`, `ipsum`, etc.). Great! -But, we still have to actually add those pages into our MkDocs -navigation: - -```yaml title="mkdocs.yml" -nav: -# rest of the navigation... -- Code Reference: - - project: - - lorem: reference/project/lorem.md - - ipsum: reference/project/ipsum.md - - dolor: reference/project/dolor.md - - sit: reference/project/sit.md - - amet: reference/project/amet.md -# rest of the navigation... -``` - -Err... so this process is only semi-automatic? -Yes, but don't worry, we can fully automate it. - -### Generate a literate navigation file - -mkdocs-gen-files is able to generate a literate navigation file. -But to make use of it, we will need an additional plugin: -[mkdocs-literate-nav](https://github.com/oprypin/mkdocs-literate-nav). -This plugin allows to specify the whole navigation, or parts of it, -into Markdown pages, as plain Markdown lists. -We use it here to specify the navigation for the code reference pages. - -First, add `mkdocs-literate-nav` to your project's docs dependencies, -and configure the plugin in your MkDocs configuration: - -```yaml title="mkdocs.yml" hl_lines="6 7" -plugins: -- search -- gen-files: - scripts: - - scripts/gen_ref_pages.py -- literate-nav: - nav_file: SUMMARY.md -- mkdocstrings -``` - -Then, the previous script is updated like so: - -```python title="scripts/gen_ref_pages.py" hl_lines="7 24 32 33" -"""Generate the code reference pages and navigation.""" - -from pathlib import Path - -import mkdocs_gen_files - -nav = mkdocs_gen_files.Nav() - -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] - elif parts[-1] == "__main__": - continue - - nav[parts] = doc_path.as_posix() # (1)! - - with mkdocs_gen_files.open(full_doc_path, "w") as fd: - ident = ".".join(parts) - fd.write(f"::: {ident}") - - mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root)) - -with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file: # (2)! - nav_file.writelines(nav.build_literate_nav()) # (3)! -``` - -1. Progressively build the navigation object. -2. At the end, create a magic, literate navigation file called `SUMMARY.md` in the `reference` folder. -3. Write the navigation as a Markdown list in the literate navigation file. - -Now we are able to remove our hard-coded navigation in `mkdocs.yml`, -and replace it with a single line! - -```yaml title="mkdocs.yml" -nav: -# rest of the navigation... -# defer to gen-files + literate-nav -- Code Reference: reference/ # (1)! -# rest of the navigation... -``` - -1. Note the trailing slash! It is needed so that `mkdocs-literate-nav` knows - it has to look for a `SUMMARY.md` file in that folder. - -At this point, we should be able to see the tree of our modules -in the navigation. - -### Bind pages to sections themselves - -There's a last improvement we can do. -With the current script, sections, corresponding to folders, -will expand or collapse when you click on them, -revealing `__init__` modules under them -(or equivalent modules in other languages, if relevant). -Since we are documenting a public API, and given users -never explicitly import `__init__` modules, it would be nice -if we could get rid of them and instead render their documentation -inside the section itself. - -Well, this is possible thanks to a third plugin: -[mkdocs-section-index](https://github.com/oprypin/mkdocs-section-index). - -Update the script like this: - -```python title="scripts/gen_ref_pages.py" hl_lines="21 22" -"""Generate the code reference pages and navigation.""" - -from pathlib import Path - -import mkdocs_gen_files - -nav = mkdocs_gen_files.Nav() - -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") - elif parts[-1] == "__main__": - continue - - nav[parts] = doc_path.as_posix() - - with mkdocs_gen_files.open(full_doc_path, "w") as fd: - ident = ".".join(parts) - fd.write(f"::: {ident}") - - mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root)) - -with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file: - nav_file.writelines(nav.build_literate_nav()) -``` - -And update your MkDocs configuration to list the plugin: - -```yaml title="mkdocs.yml" hl_lines="8" -plugins: -- search -- gen-files: - scripts: - - scripts/gen_ref_pages.py -- literate-nav: - nav_file: SUMMARY.md -- section-index -- mkdocstrings -``` - -With this, `__init__` modules will be documented and bound to the sections -themselves, better reflecting our public API. - -## Prevent selection of prompts and output in Python code blocks - -To prevent the selection of `>>>`, `...` and output in Python "Console" code blocks, -you can use the `pycon` syntax highlighting on your code blocks, -and add global CSS rules to your site using MkDocs `extra_css` option: - -````md -```pycon ->>> for word in ("Hello", "mkdocstrings!"): -... print(word, end=" ") -... -Hello mkdocstrings! -``` -```` - -```css title="docs/css/code_select.css" -.highlight .gp, .highlight .go { /* Generic.Prompt, Generic.Output */ - user-select: none; -} -``` - -```yaml title="mkdocs.yml" -extra_css: -- css/code_select.css -``` - -> WARNING: -> The `.highlight .gp, .highlight .go` CSS selector can have unintended side-effects. -> To target `pycon` code blocks more specifically, you can configure the -> `pymdownx.highlight` extension to use Pygments and set language classes -> on code blocks: -> -> ```yaml title="mkdocs.yml" -> markdown_extensions: -> - pymdownx.highlight: -> use_pygments: true -> pygments_lang_class: true -> ``` -> -> Then you can update the CSS selector like this: -> -> ```css title="docs/css/code_select.css" -> .language-pycon .gp, .language-pycon .go { /* Generic.Prompt, Generic.Output */ -> user-select: none; -> } -> ``` - -If you don't want to enable this globally, -you can still use `style` tags in the relevant pages, -with more accurate CSS selectors: - -```html - -``` - -Try to select the following code block's text: - - - -```pycon ->>> for word in ("Hello", "mkdocstrings!"): -... print(word, end=" ") -Hello mkdocstrings! -``` - -## Hide documentation strings from source code blocks - -Since documentation strings are rendered by handlers, it can sometimes feel redundant to show these same documentation strings in source code blocks (when handlers render those). - -There is a general workaround to hide these docstrings from source blocks using CSS: - -```css -/* These CSS classes depend on the handler. */ -.doc-contents details .highlight code { - line-height: 0; -} -.doc-contents details .highlight code > * { - line-height: initial; -} -.doc-contents details .highlight code > .sd { /* Literal.String.Doc */ - display: none; -} -``` - -Note that this is considered a workaround and not a proper solution, because it has side-effects like also removing blank lines. - -## Automatic highlighting for indented code blocks in docstrings - -Depending on the language used in your code base and the mkdocstrings handler used to document it, you might want to set a default syntax for code blocks added to your docstrings. For example, to default to the Python syntax: - -```yaml title="mkdocs.yml" -markdown_extensions: -- pymdownx.highlight: - default_lang: python -``` - -Then in your docstrings, indented code blocks will be highlighted as Python code: - -```python -def my_function(): - """This is my function. - - The following code will be highlighted as Python: - - result = my_function() - print(result) - - End of the docstring. - """ - pass -``` diff --git a/docs/reference/api.md b/docs/reference/api.md deleted file mode 100644 index 15af3021..00000000 --- a/docs/reference/api.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: API reference -hide: -- navigation ---- - -# ::: mkdocstrings - options: - show_submodules: true diff --git a/docs/usage/handlers.md b/docs/usage/handlers.md deleted file mode 100644 index 6f326431..00000000 --- a/docs/usage/handlers.md +++ /dev/null @@ -1,320 +0,0 @@ -# Handlers - -A handler is what makes it possible to collect and render documentation for a particular language. - -## Available handlers - -- [C](https://mkdocstrings.github.io/c/){ .external } -- [Crystal](https://mkdocstrings.github.io/crystal/){ .external } -- [GitHub Actions](https://watermarkhu.nl/mkdocstrings-github/){ .external } -- [Python](https://mkdocstrings.github.io/python/){ .external } -- [Python (Legacy)](https://mkdocstrings.github.io/python-legacy/){ .external } -- [MATLAB](https://watermarkhu.nl/mkdocstrings-matlab/){ .external } -- [Shell](https://mkdocstrings.github.io/shell/){ .external } -- [TypeScript](https://mkdocstrings.github.io/typescript/){ .external } -- [VBA](https://pypi.org/project/mkdocstrings-vba/){ .external } - -## About the Python handlers - -Since version 0.18, a new Python handler is available. -It is based on [Griffe](https://github.com/mkdocstrings/griffe), -which is an improved version of [pytkdocs](https://github.com/mkdocstrings/pytkdocs). - -If you want to keep using the legacy handler as long as possible, -you can depend on `mkdocstrings-python-legacy` directly, -or specify the `python-legacy` extra when depending on *mkdocstrings*: - -```toml title="pyproject.toml" -# PEP 621 dependencies declaration -# adapt to your dependencies manager -[project] -dependencies = [ - "mkdocstrings[python-legacy]>=0.18", -] -``` - -The legacy handler will continue to "work" for many releases, -as long as the new handler does not cover all previous use-cases. - -### Migrate to the new Python handler - -To use the new Python handler, -you can depend on `mkdocstrings-python` directly, -or specify the `python` extra when depending on *mkdocstrings*: - -```toml title="pyproject.toml" -# PEP 621 dependencies declaration -# adapt to your dependencies manager -[project] -dependencies = [ - "mkdocstrings[python]>=0.18", -] -``` - -#### Selection options - -WARNING: Since *mkdocstrings* 0.19, the YAML `selection` key is merged into the `options` key. - -- [x] `filters` is implemented, and used as before. -- [x] `members` is implemented, and used as before. -- [x] `inherited_members` is implemented. -- [x] `docstring_style` is implemented, and used as before, - except for the `restructured-text` style which is renamed `sphinx`. - Numpy-style is now built-in, so you can stop depending on `pytkdocs[numpy-style]` - or `docstring_parser`. -- [x] `docstring_options` is implemented, and used as before. - Refer to the [`griffe` documentation](https://mkdocstrings.github.io/griffe/docstrings/#parsing-options) - for the updated list of supported docstring options. -- [x] `new_path_syntax` is irrelevant now. If you were setting it to True, - remove the option and replace every colon (`:`) in your autodoc identifiers - by dots (`.`). - -See [all the handler's options](https://mkdocstrings.github.io/python/usage/). - -#### Rendering options - -WARNING: Since *mkdocstrings* 0.19, the YAML `rendering` key is merged into the `options` key. - -Every previous option is supported. -Additional options are available: - -- [x] `separate_signature`: Render the signature (or attribute value) in a code block below the heading, - instead as inline code. Useful for long signatures. If Black is installed, - the signature is formatted. Default: `False`. -- [x] `line_length`: The maximum line length to use when formatting signatures. Default: `60`. -- [x] `show_submodules`: Whether to render submodules of a module when iterating on children. - Default: `False`. -- [x] `docstring_section_style`: The style to use to render docstring sections such as attributes, - parameters, etc. Available styles: `"table"` (default), `"list"` and `"spacy"`. The SpaCy style - is a poor implementation of their [table style](https://spacy.io/api/doc/#init). - We are open to improvements through PRs! - -See [all the handler's options](https://mkdocstrings.github.io/python/usage/). - -#### Templates - -Templates are mostly the same as before, but the file layout has changed, -as well as some file names. -See [the documentation about the Python handler templates](https://mkdocstrings.github.io/python/usage/customization/#templates). - -## Custom handlers - -Since version 0.14, you can create and use custom handlers -thanks to namespace packages. For more information about namespace packages, -[see their documentation](https://packaging.python.org/guides/packaging-namespace-packages/). - -TIP: **TL;DR - Project template for handlers.** -*mkdocstrings* provides a [Copier](https://github.com/copier-org/copier) template to kickstart -new handlers: https://github.com/mkdocstrings/handler-template. To use it, install Copier -(`pipx install copier`), then run `copier gh:mkdocstrings/handler-template my_handler` -to generate a new project. See [its upstream documentation](https://pawamoy.github.io/copier-pdm/) -to learn how to work on the generated project. - -### Packaging - -For *mkdocstrings*, a custom handler package would have the following structure: - -``` -📁 your_repository -└─╴📁 mkdocstrings_handlers - └─╴📁 custom_handler - ├─╴📁 templates - │  ├─╴📁 material - │ ├─╴📁 mkdocs - │ └─╴📁 readthedocs - └─╴📄 __init__.py -``` - -NOTE: **Note the absence of `__init__.py` module in `mkdocstrings_handlers`!** - -### Code - -A handler is a subclass of the base handler provided by *mkdocstrings*. -See the documentation for the [`BaseHandler`][mkdocstrings.BaseHandler]. - -Subclasses of the base handler must declare a `name` and `domain` as class attributes, -as well as implement the following methods: - -- `collect(identifier, options)` (**required**): method responsible for collecting and returning data (extracting - documentation from source code, loading introspecting objects in memory, other sources? etc.) -- `render(identifier, options)` (**required**): method responsible for actually rendering the data to HTML, - using the Jinja templates provided by your package. -- `get_options(local_options)` (**required**): method responsible for combining global options with local ones. -- `get_aliases(identifier)` (**recommended**): method responsible for returning known aliases of object identifiers, - in order to register cross-references in the autorefs plugin. -- `get_inventory_urls()` (optional): method responsible for returning a list of URLs to download (object inventories) - along with configuration options (for loading the inventory with `load_inventory`). -- `load_inventory(in_file, url, **options)` (optional): method responsible for loading an inventory (binary file-handle) - and yielding tuples of identifiers and URLs. -- `update_env(config)` (optional): Gives you a chance to customize the Jinja environment used to render templates, - for examples by adding/removing Jinja filters and global context variables. -- `teardown()` (optional): Clean up / teardown anything that needs it at the end of the build. - -You must implement a `get_handler` method at the module level, -which returns an instance of your handler. -This function takes the following parameters: - -- `theme` (string, theme name) -- `custom_templates` (optional string, path to custom templates directory) -- `mdx` (list, Markdown extensions) -- `mdx_config` (dict, extensions configuration) -- `handler_config` (dict, handle configuration) -- `tool_config` (dict, the whole MkDocs configuration) - -These arguments are all passed as keyword arguments, so you can ignore them -by adding `**kwargs` or similar to your signature. - -You should not modify the MkDocs config but can use it to get -information about the MkDocs instance such as where the current `site_dir` lives. -See the [Mkdocs Configuration](https://www.mkdocs.org/user-guide/configuration/) for -more info about what is accessible from it. - -Check out how the -[Python handler](https://github.com/mkdocstrings/python/blob/master/src/mkdocstrings_handlers/python) -is written for inspiration. - -### Templates - -Your handler's implementation should normally be backed by templates, which go -to the directory `mkdocstrings_handlers/custom_handler/templates/some_theme` -(`custom_handler` here should be replaced with the actual name of your handler, -and `some_theme` should be the name of an actual MkDocs theme that you support, -e.g. `material`). - -With that structure, you can use `self.env.get_template("foo.html")` inside -your `render` method. This already chooses the subdirectory based on -the current MkDocs theme. - -If you wish to support *any* MkDocs theme, rather than a few specifically -selected ones, you can pick one theme's subdirectory to be the fallback for -when an unknown theme is encountered. Then you just need to set the -`fallback_theme` variable on your handler subclass. The fallback directory can -be used even for themes you explicitly support: you can omit some template from -one of the other theme directories in case they're exactly the same as in the -fallback theme. - -If your theme's HTML requires CSS to go along with it, put it into a file named -`mkdocstrings_handlers/custom_handler/templates/some_theme/style.css`, then this will be -included into the final site automatically if this handler is ever used. -Alternatively, you can put the CSS as a string into the `extra_css` variable of -your handler. - -Finally, it's possible to entirely omit templates, and tell *mkdocstrings* -to use the templates of another handler. In you handler, override the -`get_templates_dir()` method to return the other handlers templates path: - -```python -from pathlib import Path -from mkdocstrings import BaseHandler - - -class CobraHandler(BaseHandler): - def get_templates_dir(self, handler: str | None = None) -> Path: - # use the python handler templates - # (it assumes the python handler is installed) - return super().get_templates_dir("python") -``` - -### Usage - -When a custom handler is installed, it is then available to *mkdocstrings*. -You can configure it as usual: - -```yaml title="mkdocs.yml" -plugins: -- mkdocstrings: - handlers: - custom_handler: - handler_config_option: yes - options: - some_config_option: "a" - other_config_option: 0 -``` - -...and use it in your autodoc instructions: - -```md title="docs/some_page.md" -# Documentation for an object - -::: some.objects.path - handler: custom_handler - options: - some_config_option: "b" - other_config_option: 1 -``` - -## Handler extensions - -*mkdocstrings* provides a way for third-party packages -to extend or alter the behavior of handlers. -For example, an extension of the Python handler -could add specific support for another Python library. - -NOTE: This feature is intended for developers. -If you are a user and want to customize how objects are rendered, -see [Theming / Customization](theming.md#customization). - -Such extensions can register additional template folders -that will be used when rendering collected data. -Extensions are responsible for synchronizing -with the handler itself so that it uses the additional templates. - -An extension is a Python package -that defines an entry-point for a specific handler: - -```toml title="pyproject.toml" -[project.entry-points."mkdocstrings.python.templates"] # (1)! -extension-name = "extension_package:get_templates_path" # (2)! -``` - -1. Replace `python` by the name of the handler you want to add templates to. -1. Replace `extension-name` by any name you want, - and replace `extension_package:get_templates_path` - by the actual module path and function name in your package. - -This entry-point assumes that the extension provides -a `get_templates_path` function directly under the `extension_package` package: - -```tree -pyproject.toml -extension_package/ - __init__.py - templates/ -``` - -```python title="extension_package/__init__.py" -from pathlib import Path - - -def get_templates_path() -> Path: - return Path(__file__).parent / "templates" -``` - -This function doesn't accept any argument -and returns the path ([`pathlib.Path`][] or [`str`][]) -to a directory containing templates. -The directory must contain one subfolder -for each supported theme, even if empty -(see "fallback theme" in [custom handlers templates](#templates_1)). -For example: - -```tree -pyproject.toml -extension_package/ - __init__.py - templates/ - material/ - readthedocs/ - mkdocs/ -``` - -*mkdocstrings* will add the folders corresponding to the user-selected theme, -and to the handler's defined fallback theme, as usual. - -The names of the extension templates -must not overlap with the handler's original templates. - -The extension is then responsible, in collaboration with its target handler, -for mutating the collected data in order to instruct the handler -to use one of the extension template when rendering particular objects. -See each handler's docs to see if they support extensions, and how. diff --git a/docs/usage/index.md b/docs/usage/index.md deleted file mode 100644 index 64588fdf..00000000 --- a/docs/usage/index.md +++ /dev/null @@ -1,363 +0,0 @@ -# Usage - -## Autodoc syntax - -*mkdocstrings* works by processing special expressions in your Markdown files. - -The syntax is as follows: - -```md -::: identifier - YAML block -``` - -> NOTE: **Resources on YAML.** -> YAML can sometimes be a bit tricky, particularly on indentation. -> Here are some resources that other users found useful to better -> understand YAML's peculiarities. -> -> - [YAML idiosyncrasies](https://salt-zh.readthedocs.io/en/latest/topics/troubleshooting/yaml_idiosyncrasies.html) -> - [YAML multiline](https://yaml-multiline.info/) - -The `identifier` is a string identifying the object you want to document. -The format of an identifier can vary from one handler to another. -For example, the Python handler expects the full dotted-path to a Python object: -`my_package.my_module.MyClass.my_method`. - -The YAML block is optional, and contains some configuration options: - -- `handler`: the name of the handler to use to collect and render this object. - By default, it will use the value defined in the [Global options](#global-options)'s - `default_handler` key, or `"python"`. -- `options`: a dictionary of options passed to the handler's methods responsible both - for collecting and rendering the documentation. These options can be defined - globally (in `mkdocs.yml`, see [Global options](#global-options)), - locally (as described here), or both. - -!!! example "Example with the Python handler" - === "docs/my_page.md" - ```md - # Documentation for `MyClass` - - ::: my_package.my_module.MyClass - handler: python - options: - members: - - method_a - - method_b - show_root_heading: false - show_source: false - ``` - - === "mkdocs.yml" - ```yaml - nav: - - "My page": my_page.md - ``` - - === "src/my_package/my_module.py" - ```python - class MyClass: - """Print print print!""" - - def method_a(self): - """Print A!""" - print("A!") - - def method_b(self): - """Print B!""" - print("B!") - - def method_c(self): - """Print C!""" - print("C!") - ``` - - === "Result" -

Documentation for MyClass

-

Print print print!

-

- - method_a(self) -

-

Print A!

- - method_b(self) -

Print B!

- -It is also possible to integrate a mkdocstrings identifier into a Markdown header: - -```md -## ::: my_package.my_module.MyClass - options: - show_source: false -``` - -The above is equivalent to: - -```md -::: my_package.my_module.MyClass - options: - show_source: false - heading_level: 2 -``` - -## Global options - -*mkdocstrings* accepts a few top-level configuration options in `mkdocs.yml`: - -- `default_handler`: The handler that is used by default when no handler is specified. -- `custom_templates`: The path to a directory containing custom templates. - The path is relative to the MkDocs configuration file. - See [Theming](theming.md). -- `handlers`: The handlers' global configuration. -- `enable_inventory`: Whether to enable inventory file generation. - See [Cross-references to other projects / inventories](#cross-references-to-other-projects-inventories) -- `locale`: The locale used for translations. See [Internationalization](#internationalization-i18n). -- `enabled` **(New in version 0.20)**: Whether to enable the plugin. Defaults to `true`. - Can be used to reduce build times when doing local development. - Especially useful when used with environment variables (see example below). - -!!! example - ```yaml title="mkdocs.yml" - plugins: - - mkdocstrings: - enabled: !ENV [ENABLE_MKDOCSTRINGS, true] - custom_templates: templates - default_handler: python - locale: en - handlers: - python: - options: - show_source: false - ``` - - The handlers global configuration can then be overridden by local configurations: - - ```yaml title="docs/some_page.md" - ::: my_package.my_module.MyClass - options: - show_source: true - ``` - -Some handlers accept additional global configuration. -Check the documentation for your handler of interest in [Handlers](handlers.md). - -## Internationalization (I18N) - -Some handlers support multiple languages. - -If the handler supports localization, the locale it uses is determined by the following order of precedence: - -- `locale` in [global options](#global-options) -- `theme.language`: used by the [MkDocs Material theme](https://squidfunk.github.io/mkdocs-material/setup/changing-the-language/) -- `theme.locale` in [MkDocs configuration](https://www.mkdocs.org/user-guide/configuration/#theme) - -## Cross-references - -Cross-references are written as Markdown *reference-style* links: - -=== "Markdown" - ```md - With a custom title: - [`Object 1`][full.path.object1] - - With the identifier as title: - [full.path.object2][] - ``` - -=== "HTML Result" - ```html -

With a custom title: - Object 1

-

With the identifier as title: - full.path.object2

- ``` - -Any item that was inserted using the [autodoc syntax](#autodoc-syntax) -(e.g. `::: full.path.object1`) is possible to link to by using the same identifier with the -cross-reference syntax (`[example][full.path.object1]`). -But the cross-references are also applicable to the items' children that get pulled in. - -### Finding out the anchor - -If you're not sure which exact identifier a doc item uses, you can look at its "anchor", which your -Web browser will show in the URL bar when clicking an item's entry in the table of contents. -If the URL is `https://example.com/some/page.html#full.path.object1` then you know that this item -is possible to link to with `[example][full.path.object1]`, regardless of the current page. - -### Cross-references to any Markdown heading - -TIP: **Changed in version 0.15.** -Linking to any Markdown heading used to be the default, but now opt-in is required. - -If you want to link to *any* Markdown heading, not just *mkdocstrings*-inserted items, please -enable the [*autorefs* plugin for *MkDocs*](https://github.com/mkdocstrings/autorefs) by adding -`autorefs` to `plugins`: - -```yaml title="mkdocs.yml" hl_lines="3" -plugins: -- search -- autorefs -- mkdocstrings: - [...] -``` - -Note that you don't need to (`pip`) install anything more; this plugin is guaranteed to be pulled in with *mkdocstrings*. - -!!! example - === "doc1.md" - ```md - ## Hello, world! - - Testing - ``` - - === "doc2.md" - ```md - ## Something else - - Please see the [Hello, World!][hello-world] section. - ``` - - === "Result HTML for doc2" - ```html -

Please see the Hello, World! section.

- ``` - -### Cross-references to a sub-heading in a docstring - -TIP: **New in version 0.14.** - -If you have a Markdown heading *inside* your docstring, you can also link directly to it. -In the example below you see the identifier to be linked is `foo.bar--tips`, because it's the "Tips" heading that's part of the `foo.bar` object, joined with "`--`". - -!!! example - === "foo.py" - ```python - def bar(): - """Hello, world! - - # Tips - - - Stay hydrated. - """ - ``` - - === "doc1.md" - ```md - ::: foo.bar - ``` - - === "doc2.md" - ```md - Check out the [tips][foo.bar--tips] - ``` - - === "HTML result for doc2" - ```html -

Check out the tips

- ``` - -The above tip about [Finding out the anchor](#finding-out-the-anchor) also applies the same way here. - -You may also notice that such a heading does not get rendered as a `

` element directly, but rather the level gets shifted to fit the encompassing document structure. If you're curious about the implementation, check out [mkdocstrings.HeadingShiftingTreeprocessor][] and others. - -### Cross-references to other projects / inventories - -TIP: **New in version 0.16.** - -Python developers coming from Sphinx might know about its `intersphinx` extension, -that allows to cross-reference items between several projects. -*mkdocstrings* has a similar feature. - -To reference an item from another project, you must first tell *mkdocstrings* -to load the inventory it provides. Each handler will be responsible of loading -inventories specific to its language. For example, the Python handler -can load Sphinx-generated inventories (`objects.inv`). - -In the following snippet, we load the inventory provided by `installer`: - -```yaml title="mkdocs.yml" -plugins: -- mkdocstrings: - handlers: - python: - inventories: - - https://installer.readthedocs.io/en/stable/objects.inv -``` - -Now it is possible to cross-reference `installer`'s items. For example: - -=== "Markdown" - ```md - See [installer.records][] to learn about records. - ``` - -=== "Result (HTML)" - ```html -

See installer.records - to learn about records.

- ``` - -=== "Result (displayed)" - See [installer.records][] to learn about records. - -You can of course select another version of the inventory, for example: - -```yaml -plugins: -- mkdocstrings: - handlers: - python: - inventories: - # latest instead of stable - - https://installer.readthedocs.io/en/latest/objects.inv -``` - -In case the inventory file is not served under the base documentation URL, -you can explicitly specify both URLs: - -```yaml -plugins: -- mkdocstrings: - handlers: - python: - inventories: - - url: https://cdn.example.com/version/objects.inv - base_url: https://docs.example.com/version -``` - -Absolute URLs to cross-referenced items will then be based -on `https://docs.example.com/version/` instead of `https://cdn.example.com/version/`. - -If you need authentication to access the inventory file, you can provide the credentials in the URL, either as `username:password`: - -```yaml -- url: https://username:password@private.example.com/version/objects.inv -``` - -...or with token authentication: - -```yaml -- url: https://token123@private.example.com/version/objects.inv -``` - -The credentials can also be specified using environment variables in the form `${ENV_VAR}`: - -```yaml -- url: https://${USERNAME}:${PASSWORD}@private.example.com/version/objects.inv -``` - -Reciprocally, *mkdocstrings* also allows to *generate* an inventory file in the Sphinx format. -It will be enabled by default if the Python handler is used, and generated as `objects.inv` in the final site directory. -Other projects will be able to cross-reference items from your project. - -To explicitly enable or disable the generation of the inventory file, use the global -`enable_inventory` option: - -```yaml -plugins: -- mkdocstrings: - enable_inventory: false -``` diff --git a/duties.py b/duties.py deleted file mode 100644 index dfc81027..00000000 --- a/duties.py +++ /dev/null @@ -1,206 +0,0 @@ -"""Development tasks.""" - -from __future__ import annotations - -import os -import re -import sys -from pathlib import Path -from typing import TYPE_CHECKING - -from duty import duty, tools - -if TYPE_CHECKING: - 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" -PY_VERSION = f"{sys.version_info.major}{sys.version_info.minor}" -PY_DEV = "315" - - -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 - - -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) # ty: ignore[invalid-argument-type,unresolved-attribute] - - -@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(nofail=PY_VERSION == PY_DEV) -def check_quality(ctx: Context) -> None: - """Check the code quality.""" - ctx.run( - tools.ruff.check(*PY_SRC_LIST, config="config/ruff.toml", color=True), - title=pyprefix("Checking code quality"), - ) - - -@duty(nofail=PY_VERSION == PY_DEV) -def check_docs(ctx: Context) -> None: - """Check if the documentation builds correctly.""" - ctx.run( - tools.zensical.build(strict=True), - title=pyprefix("Building documentation"), - ) - - -@duty(nofail=PY_VERSION == PY_DEV) -def check_types(ctx: Context) -> None: - """Check that the code is correctly typed.""" - py = f"{sys.version_info.major}.{sys.version_info.minor}" - ctx.run( - tools.ty.check( - *PY_SRC_LIST, - config_file="config/ty.toml", - color=True, - error_on_warning=True, - python_version=py, - ), - title=pyprefix("Type-checking"), - ) - - -@duty(nofail=PY_VERSION == PY_DEV) -def check_api(ctx: Context, *cli_args: str) -> None: - """Check for API breaking changes.""" - ctx.run( - tools.griffe.check("mkdocstrings", 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. - """ - ctx.run( - tools.zensical.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.""" - from ghp_import import ghp_import # noqa: PLC0415 - - ctx.run(tools.zensical.build(), title="Building documentation site") - ctx.run( - ghp_import, - kwargs={ - "srcdir": "site", - "mesg": "chore: Update documentation", - "push": True, - "force": True, - "remote": "org-pages", - }, - title="Deploying site to GitHub Pages", - command="ghp-import site -r org-pages -fpm 'chore: Update documentation'", - pty=PTY, - ) - - -@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( - ["uv", "build"], - title="Building 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() if dist.suffix in (".gz", ".whl")] - ctx.run( - tools.twine.upload(*dists, skip_existing=True), - title="Publishing 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 -m '' -a {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(nofail=PY_VERSION == PY_DEV) -def test(ctx: Context, *cli_args: str) -> None: - """Run the test suite.""" - os.environ["COVERAGE_FILE"] = f".coverage.{PY_VERSION}" - os.environ["PYTHONWARNDEFAULTENCODING"] = "1" - ctx.run( - tools.pytest( - "tests", - config_file="config/pytest.ini", - color="yes", - ).add_args("-n", "auto", *cli_args), - title=pyprefix("Running tests"), - ) diff --git a/handlers/overview/index.html b/handlers/overview/index.html new file mode 100644 index 00000000..4aa8bfe1 --- /dev/null +++ b/handlers/overview/index.html @@ -0,0 +1,14 @@ + + + + + + Redirecting... + + + + + +You're being redirected to a new destination. + + diff --git a/index.html b/index.html new file mode 100644 index 00000000..6cde9bc8 --- /dev/null +++ b/index.html @@ -0,0 +1,49 @@ + Overview - mkdocstrings

mkdocstrings¤

ci documentation pypi version conda version gitter

Automatic documentation from sources, for MkDocs. Come have a chat or ask questions on our Gitter channel.


Features - Installation - Quick usage

mkdocstrings_gif1

Features¤

  • Language-agnostic: just like MkDocs, mkdocstrings is written in Python but is language-agnostic. It means you can use it with any programming language, as long as there is a handler for it. We currently have handlers for the C, Crystal, GitHub Actions, Python, MATLAB, TypeScript, and VBA languages, as well as for shell scripts/libraries. Maybe you'd like to add another one to the list? 😉

  • Multiple themes support: each handler can offer multiple themes. Currently, we offer the ⭐ Material theme ⭐ as well as basic support for the ReadTheDocs and MkDocs themes for the Python handler.

  • Cross-references across pages: mkdocstrings makes it possible to reference headings in other Markdown files with the classic Markdown linking syntax: [identifier][] or [title][identifier] -- and you don't need to remember which exact page this object was on. This works for any heading that's produced by a mkdocstrings language handler, and you can opt to include any Markdown heading into the global referencing scheme.

    Note: in versions prior to 0.15 all Markdown headers were included, but now you need to opt in.

  • Cross-references across sites: similarly to Sphinx's intersphinx extension, mkdocstrings can reference API items from other libraries, given they provide an inventory and you load that inventory in your MkDocs configuration.

  • Inline injection in Markdown: instead of generating Markdown files, mkdocstrings allows you to inject documentation anywhere in your Markdown contents. The syntax is simple: ::: identifier followed by a 4-spaces indented YAML block. The identifier and YAML configuration will be passed to the appropriate handler to collect and render documentation.

  • Global and local configuration: each handler can be configured globally in mkdocs.yml, and locally for each "autodoc" instruction.

  • Reasonable defaults: you should be able to just drop the plugin in your configuration and enjoy your auto-generated docs.

Used by¤

mkdocstrings is used by well-known companies, projects and scientific teams: Ansible, Apache, FastAPI, Google, IBM, Jitsi, Microsoft, NVIDIA, Prefect, Pydantic, Textual, and more...

Installation¤

The mkdocstrings package doesn't provide support for any language: it's just a common base for language handlers. It means you likely want to install it with one or more official handlers, using extras. For example, to install it with Python support:

pip install 'mkdocstrings[python]'
+

Alternatively, you can directly install the language handlers themselves, which depend on mkdocstrings anyway:

pip install mkdocstrings-python
+

This will give you more control over the accepted range of versions for the handlers themselves.

See the official language handlers.


With conda:

conda install -c conda-forge mkdocstrings mkdocstrings-python
+

Quick usage¤

In mkdocs.yml:

site_name: "My Library"
+
+theme:
+  name: "material"
+
+plugins:
+- search
+- mkdocstrings
+

In one of your markdown files:

# Reference
+
+::: my_library.my_module.my_class
+

See the Usage section of the docs for more examples!

Sponsors¤

Silver sponsors

Material for MkDocs
FastAPI
Pydantic

Bronze sponsors

Nixtla


ofek samuelcolvin tlambert03 ssbarnea femtomc cmarqu kolenaIO ramnes machow BenHammersley trevorWieland laenan8466 MarcoGorelli analog-cbarber OdinManiac rstudio-sponsorship schlich SuperCowPowers butterlyn livingbio NemetschekAllplan EricJayHartman 15r10nk cdwilson activeloopai roboflow wrath-codes leodevian cmclaughlin blaisep RapidataAI rodolphebarbanneau theSymbolSyndicate blakeNaccarato ChargeStorm Alphadelta14

And 8 more private sponsor(s).

\ No newline at end of file diff --git a/index.md b/index.md new file mode 100644 index 00000000..bcd92aa8 --- /dev/null +++ b/index.md @@ -0,0 +1,90 @@ +# mkdocstrings + +Automatic documentation from sources, for [MkDocs](https://www.mkdocs.org/). Come have a chat or ask questions on our [Gitter channel](https://gitter.im/mkdocstrings/community). + +______________________________________________________________________ + +**[Features](#features)** - **[Installation](#installation)** - **[Quick usage](#quick-usage)** + +## Features + +- [**Language-agnostic:**](https://mkdocstrings.github.io/handlers/overview/) just like *MkDocs*, *mkdocstrings* is written in Python but is language-agnostic. It means you can use it with any programming language, as long as there is a [**handler**](https://mkdocstrings.github.io/reference/handlers/base/) for it. We currently have [handlers](https://mkdocstrings.github.io/handlers/overview/) for the [C](https://mkdocstrings.github.io/c/), [Crystal](https://mkdocstrings.github.io/crystal/), [GitHub Actions](https://watermarkhu.nl/mkdocstrings-github/), [Python](https://mkdocstrings.github.io/python/), [MATLAB](https://watermarkhu.nl/mkdocstrings-matlab/), [TypeScript](https://mkdocstrings.github.io/typescript/), and [VBA](https://pypi.org/project/mkdocstrings-vba/) languages, as well as for [shell scripts/libraries](https://mkdocstrings.github.io/shell/). Maybe you'd like to add another one to the list? + +- [**Multiple themes support:**](https://mkdocstrings.github.io/theming/) each handler can offer multiple themes. Currently, we offer the [Material theme](https://squidfunk.github.io/mkdocs-material/) as well as basic support for the ReadTheDocs and MkDocs themes for the Python handler. + +- [**Cross-references across pages:**](https://mkdocstrings.github.io/usage/#cross-references) *mkdocstrings* makes it possible to reference headings in other Markdown files with the classic Markdown linking syntax: `[identifier][]` or `[title][identifier]` -- and you don't need to remember which exact page this object was on. This works for any heading that's produced by a *mkdocstrings* language handler, and you can opt to include *any* Markdown heading into the global referencing scheme. + + **Note**: in versions prior to 0.15 *all* Markdown headers were included, but now you need to [opt in](https://mkdocstrings.github.io/usage/#cross-references-to-any-markdown-heading). + +- [**Cross-references across sites:**](https://mkdocstrings.github.io/usage/#cross-references-to-other-projects-inventories) similarly to [Sphinx's intersphinx extension](https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html), *mkdocstrings* can reference API items from other libraries, given they provide an inventory and you load that inventory in your MkDocs configuration. + +- [**Inline injection in Markdown:**](https://mkdocstrings.github.io/usage/) instead of generating Markdown files, *mkdocstrings* allows you to inject documentation anywhere in your Markdown contents. The syntax is simple: `::: identifier` followed by a 4-spaces indented YAML block. The identifier and YAML configuration will be passed to the appropriate handler to collect and render documentation. + +- [**Global and local configuration:**](https://mkdocstrings.github.io/usage/#global-options) each handler can be configured globally in `mkdocs.yml`, and locally for each "autodoc" instruction. + +- **Reasonable defaults:** you should be able to just drop the plugin in your configuration and enjoy your auto-generated docs. + +## Used by + +*mkdocstrings* is used by well-known companies, projects and scientific teams: [Ansible](https://molecule.readthedocs.io/configuration/), [Apache](https://streampipes.apache.org/docs/docs/python/latest/reference/client/client/), [FastAPI](https://fastapi.tiangolo.com/reference/fastapi/), [Google](https://docs.kidger.site/jaxtyping/api/runtime-type-checking/), [IBM](https://ds4sd.github.io/docling/api_reference/document_converter/), [Jitsi](https://jitsi.github.io/jiwer/reference/alignment/), [Microsoft](https://microsoft.github.io/presidio/api/analyzer_python/), [NVIDIA](https://nvidia.github.io/bionemo-framework/main/references/API_reference/bionemo/core/api/), [Prefect](https://docs.prefect.io/2.10.12/api-ref/prefect/agent/), [Pydantic](https://docs.pydantic.dev/dev-v2/api/main/), [Textual](https://textual.textualize.io/api/app/), [and more...](https://github.com/mkdocstrings/mkdocstrings/network/dependents) + +## Installation + +The `mkdocstrings` package doesn't provide support for any language: it's just a common base for language handlers. It means you likely want to install it with one or more official handlers, using [extras](https://packaging.python.org/en/latest/specifications/dependency-specifiers/#extras). For example, to install it with Python support: + +```bash +pip install 'mkdocstrings[python]' +``` + +Alternatively, you can directly install the language handlers themselves, which depend on `mkdocstrings` anyway: + +```bash +pip install mkdocstrings-python +``` + +This will give you more control over the accepted range of versions for the handlers themselves. + +See the [official language handlers](https://mkdocstrings.github.io/handlers/overview/). + +______________________________________________________________________ + +With `conda`: + +```bash +conda install -c conda-forge mkdocstrings mkdocstrings-python +``` + +## Quick usage + +In `mkdocs.yml`: + +```yaml +site_name: "My Library" + +theme: + name: "material" + +plugins: +- search +- mkdocstrings +``` + +In one of your markdown files: + +```markdown +# Reference + +::: my_library.my_module.my_class +``` + +See the [Usage](https://mkdocstrings.github.io/usage) section of the docs for more examples! + +## Sponsors + +**Silver sponsors** + +**Bronze sponsors** + +______________________________________________________________________ + +*And 8 more private sponsor(s).* diff --git a/docs/js/feedback.js b/js/feedback.js similarity index 100% rename from docs/js/feedback.js rename to js/feedback.js diff --git a/js/timeago.min.js b/js/timeago.min.js new file mode 100644 index 00000000..a8530a5f --- /dev/null +++ b/js/timeago.min.js @@ -0,0 +1,2 @@ +/* Taken from https://cdnjs.cloudflare.com/ajax/libs/timeago.js/4.0.2/timeago.min.js */ +!function(s,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((s=s||self).timeago={})}(this,function(s){"use strict";var a=["second","minute","hour","day","week","month","year"];function n(s,n){if(0===n)return["just now","right now"];var e=a[Math.floor(n/2)];return 1=m[t]&&t=m[e]&&e 0) { + var locale = getLocale(nodes[0]); + timeago.render(nodes, locale); + } + }) +} else { + var nodes = document.querySelectorAll('.timeago'); + if (nodes.length > 0) { + var locale = getLocale(nodes[0]); + timeago.render(nodes, locale); + } +} diff --git a/license/index.html b/license/index.html new file mode 100644 index 00000000..8bd2f07d --- /dev/null +++ b/license/index.html @@ -0,0 +1,50 @@ + License - mkdocstrings

License¤

ISC License
+
+Copyright (c) 2019, 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.
+
\ No newline at end of file diff --git a/llms-full.txt b/llms-full.txt new file mode 100644 index 00000000..c6e087f5 --- /dev/null +++ b/llms-full.txt @@ -0,0 +1,4827 @@ +# mkdocstrings + +> Automatic documentation from sources, for MkDocs. + +# Usage + +# mkdocstrings + +Automatic documentation from sources, for [MkDocs](https://www.mkdocs.org/). Come have a chat or ask questions on our [Gitter channel](https://gitter.im/mkdocstrings/community). + +______________________________________________________________________ + +**[Features](#features)** - **[Installation](#installation)** - **[Quick usage](#quick-usage)** + +## Features + +- [**Language-agnostic:**](https://mkdocstrings.github.io/handlers/overview/) just like *MkDocs*, *mkdocstrings* is written in Python but is language-agnostic. It means you can use it with any programming language, as long as there is a [**handler**](https://mkdocstrings.github.io/reference/handlers/base/) for it. We currently have [handlers](https://mkdocstrings.github.io/handlers/overview/) for the [C](https://mkdocstrings.github.io/c/), [Crystal](https://mkdocstrings.github.io/crystal/), [GitHub Actions](https://watermarkhu.nl/mkdocstrings-github/), [Python](https://mkdocstrings.github.io/python/), [MATLAB](https://watermarkhu.nl/mkdocstrings-matlab/), [TypeScript](https://mkdocstrings.github.io/typescript/), and [VBA](https://pypi.org/project/mkdocstrings-vba/) languages, as well as for [shell scripts/libraries](https://mkdocstrings.github.io/shell/). Maybe you'd like to add another one to the list? + +- [**Multiple themes support:**](https://mkdocstrings.github.io/theming/) each handler can offer multiple themes. Currently, we offer the [Material theme](https://squidfunk.github.io/mkdocs-material/) as well as basic support for the ReadTheDocs and MkDocs themes for the Python handler. + +- [**Cross-references across pages:**](https://mkdocstrings.github.io/usage/#cross-references) *mkdocstrings* makes it possible to reference headings in other Markdown files with the classic Markdown linking syntax: `[identifier][]` or `[title][identifier]` -- and you don't need to remember which exact page this object was on. This works for any heading that's produced by a *mkdocstrings* language handler, and you can opt to include *any* Markdown heading into the global referencing scheme. + + **Note**: in versions prior to 0.15 *all* Markdown headers were included, but now you need to [opt in](https://mkdocstrings.github.io/usage/#cross-references-to-any-markdown-heading). + +- [**Cross-references across sites:**](https://mkdocstrings.github.io/usage/#cross-references-to-other-projects-inventories) similarly to [Sphinx's intersphinx extension](https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html), *mkdocstrings* can reference API items from other libraries, given they provide an inventory and you load that inventory in your MkDocs configuration. + +- [**Inline injection in Markdown:**](https://mkdocstrings.github.io/usage/) instead of generating Markdown files, *mkdocstrings* allows you to inject documentation anywhere in your Markdown contents. The syntax is simple: `::: identifier` followed by a 4-spaces indented YAML block. The identifier and YAML configuration will be passed to the appropriate handler to collect and render documentation. + +- [**Global and local configuration:**](https://mkdocstrings.github.io/usage/#global-options) each handler can be configured globally in `mkdocs.yml`, and locally for each "autodoc" instruction. + +- **Reasonable defaults:** you should be able to just drop the plugin in your configuration and enjoy your auto-generated docs. + +## Used by + +*mkdocstrings* is used by well-known companies, projects and scientific teams: [Ansible](https://molecule.readthedocs.io/configuration/), [Apache](https://streampipes.apache.org/docs/docs/python/latest/reference/client/client/), [FastAPI](https://fastapi.tiangolo.com/reference/fastapi/), [Google](https://docs.kidger.site/jaxtyping/api/runtime-type-checking/), [IBM](https://ds4sd.github.io/docling/api_reference/document_converter/), [Jitsi](https://jitsi.github.io/jiwer/reference/alignment/), [Microsoft](https://microsoft.github.io/presidio/api/analyzer_python/), [NVIDIA](https://nvidia.github.io/bionemo-framework/main/references/API_reference/bionemo/core/api/), [Prefect](https://docs.prefect.io/2.10.12/api-ref/prefect/agent/), [Pydantic](https://docs.pydantic.dev/dev-v2/api/main/), [Textual](https://textual.textualize.io/api/app/), [and more...](https://github.com/mkdocstrings/mkdocstrings/network/dependents) + +## Installation + +The `mkdocstrings` package doesn't provide support for any language: it's just a common base for language handlers. It means you likely want to install it with one or more official handlers, using [extras](https://packaging.python.org/en/latest/specifications/dependency-specifiers/#extras). For example, to install it with Python support: + +```bash +pip install 'mkdocstrings[python]' +``` + +Alternatively, you can directly install the language handlers themselves, which depend on `mkdocstrings` anyway: + +```bash +pip install mkdocstrings-python +``` + +This will give you more control over the accepted range of versions for the handlers themselves. + +See the [official language handlers](https://mkdocstrings.github.io/handlers/overview/). + +______________________________________________________________________ + +With `conda`: + +```bash +conda install -c conda-forge mkdocstrings mkdocstrings-python +``` + +## Quick usage + +In `mkdocs.yml`: + +```yaml +site_name: "My Library" + +theme: + name: "material" + +plugins: +- search +- mkdocstrings +``` + +In one of your markdown files: + +```markdown +# Reference + +::: my_library.my_module.my_class +``` + +See the [Usage](https://mkdocstrings.github.io/usage) section of the docs for more examples! + +## Sponsors + +**Silver sponsors** + +**Bronze sponsors** + +______________________________________________________________________ + +*And 8 more private sponsor(s).* + +# Usage + +## Autodoc syntax + +*mkdocstrings* works by processing special expressions in your Markdown files. + +The syntax is as follows: + +```md +::: identifier + YAML block +``` + +Resources on YAML. + +YAML can sometimes be a bit tricky, particularly on indentation. Here are some resources that other users found useful to better understand YAML's peculiarities. + +- [YAML idiosyncrasies](https://salt-zh.readthedocs.io/en/latest/topics/troubleshooting/yaml_idiosyncrasies.html) +- [YAML multiline](https://yaml-multiline.info/) + +The `identifier` is a string identifying the object you want to document. The format of an identifier can vary from one handler to another. For example, the Python handler expects the full dotted-path to a Python object: `my_package.my_module.MyClass.my_method`. + +The YAML block is optional, and contains some configuration options: + +- `handler`: the name of the handler to use to collect and render this object. By default, it will use the value defined in the [Global options](#global-options)'s `default_handler` key, or `"python"`. +- `options`: a dictionary of options passed to the handler's methods responsible both for collecting and rendering the documentation. These options can be defined globally (in `mkdocs.yml`, see [Global options](#global-options)), locally (as described here), or both. + +Example with the Python handler + +```md +# Documentation for `MyClass` + +::: my_package.my_module.MyClass + handler: python + options: + members: + - method_a + - method_b + show_root_heading: false + show_source: false +``` + +```yaml +nav: + - "My page": my_page.md +``` + +```python +class MyClass: + """Print print print!""" + + def method_a(self): + """Print A!""" + print("A!") + + def method_b(self): + """Print B!""" + print("B!") + + def method_c(self): + """Print C!""" + print("C!") +``` + +### Documentation for `MyClass` + +Print print print! + +#### `method_a(self)` + +Print A! + +#### `method_b(self)` + +Print B! + +It is also possible to integrate a mkdocstrings identifier into a Markdown header: + +```md +## ::: my_package.my_module.MyClass + options: + show_source: false +``` + +The above is equivalent to: + +```md +::: my_package.my_module.MyClass + options: + show_source: false + heading_level: 2 +``` + +## Global options + +*mkdocstrings* accepts a few top-level configuration options in `mkdocs.yml`: + +- `default_handler`: The handler that is used by default when no handler is specified. +- `custom_templates`: The path to a directory containing custom templates. The path is relative to the MkDocs configuration file. See [Theming](theming/). +- `handlers`: The handlers' global configuration. +- `enable_inventory`: Whether to enable inventory file generation. See [Cross-references to other projects / inventories](#cross-references-to-other-projects-inventories) +- `locale`: The locale used for translations. See [Internationalization](#internationalization-i18n). +- `enabled` **(New in version 0.20)**: Whether to enable the plugin. Defaults to `true`. Can be used to reduce build times when doing local development. Especially useful when used with environment variables (see example below). + +Example + +mkdocs.yml + +```yaml +plugins: +- mkdocstrings: + enabled: !ENV [ENABLE_MKDOCSTRINGS, true] + custom_templates: templates + default_handler: python + locale: en + handlers: + python: + options: + show_source: false +``` + +The handlers global configuration can then be overridden by local configurations: + +docs/some_page.md + +```yaml +::: my_package.my_module.MyClass + options: + show_source: true +``` + +Some handlers accept additional global configuration. Check the documentation for your handler of interest in [Handlers](handlers/). + +## Internationalization (I18N) + +Some handlers support multiple languages. + +If the handler supports localization, the locale it uses is determined by the following order of precedence: + +- `locale` in [global options](#global-options) +- `theme.language`: used by the [MkDocs Material theme](https://squidfunk.github.io/mkdocs-material/setup/changing-the-language/) +- `theme.locale` in [MkDocs configuration](https://www.mkdocs.org/user-guide/configuration/#theme) + +## Cross-references + +Cross-references are written as Markdown *reference-style* links: + +```md +With a custom title: +[`Object 1`][full.path.object1] + +With the identifier as title: +[full.path.object2][] +``` + +```html +

With a custom title: +Object 1

+

With the identifier as title: +full.path.object2

+``` + +Any item that was inserted using the [autodoc syntax](#autodoc-syntax) (e.g. `::: full.path.object1`) is possible to link to by using the same identifier with the cross-reference syntax (`[example][full.path.object1]`). But the cross-references are also applicable to the items' children that get pulled in. + +### Finding out the anchor + +If you're not sure which exact identifier a doc item uses, you can look at its "anchor", which your Web browser will show in the URL bar when clicking an item's entry in the table of contents. If the URL is `https://example.com/some/page.html#full.path.object1` then you know that this item is possible to link to with `[example][full.path.object1]`, regardless of the current page. + +### Cross-references to any Markdown heading + +Changed in version 0.15. + +Linking to any Markdown heading used to be the default, but now opt-in is required. + +If you want to link to *any* Markdown heading, not just *mkdocstrings*-inserted items, please enable the [*autorefs* plugin for *MkDocs*](https://github.com/mkdocstrings/autorefs) by adding `autorefs` to `plugins`: + +mkdocs.yml + +```yaml +plugins: +- search +- autorefs +- mkdocstrings: + [...] +``` + +Note that you don't need to (`pip`) install anything more; this plugin is guaranteed to be pulled in with *mkdocstrings*. + +Example + +```md +## Hello, world! + +Testing +``` + +```md +## Something else + +Please see the [Hello, World!][hello-world] section. +``` + +```html +

Please see the Hello, World! section.

+``` + +### Cross-references to a sub-heading in a docstring + +New in version 0.14. + +If you have a Markdown heading *inside* your docstring, you can also link directly to it. In the example below you see the identifier to be linked is `foo.bar--tips`, because it's the "Tips" heading that's part of the `foo.bar` object, joined with "`--`". + +Example + +```python +def bar(): + """Hello, world! + + # Tips + + - Stay hydrated. + """ +``` + +```md +::: foo.bar +``` + +```md +Check out the [tips][foo.bar--tips] +``` + +```html +

Check out the tips

+``` + +The above tip about [Finding out the anchor](#finding-out-the-anchor) also applies the same way here. + +You may also notice that such a heading does not get rendered as a `

` element directly, but rather the level gets shifted to fit the encompassing document structure. If you're curious about the implementation, check out mkdocstrings.HeadingShiftingTreeprocessor and others. + +### Cross-references to other projects / inventories + +New in version 0.16. + +Python developers coming from Sphinx might know about its `intersphinx` extension, that allows to cross-reference items between several projects. *mkdocstrings* has a similar feature. + +To reference an item from another project, you must first tell *mkdocstrings* to load the inventory it provides. Each handler will be responsible of loading inventories specific to its language. For example, the Python handler can load Sphinx-generated inventories (`objects.inv`). + +In the following snippet, we load the inventory provided by `installer`: + +mkdocs.yml + +```yaml +plugins: +- mkdocstrings: + handlers: + python: + inventories: + - https://installer.readthedocs.io/en/stable/objects.inv +``` + +Now it is possible to cross-reference `installer`'s items. For example: + +```md +See [installer.records][] to learn about records. +``` + +```html +

See installer.records +to learn about records.

+``` + +See installer.records to learn about records. + +You can of course select another version of the inventory, for example: + +```yaml +plugins: +- mkdocstrings: + handlers: + python: + inventories: + # latest instead of stable + - https://installer.readthedocs.io/en/latest/objects.inv +``` + +In case the inventory file is not served under the base documentation URL, you can explicitly specify both URLs: + +```yaml +plugins: +- mkdocstrings: + handlers: + python: + inventories: + - url: https://cdn.example.com/version/objects.inv + base_url: https://docs.example.com/version +``` + +Absolute URLs to cross-referenced items will then be based on `https://docs.example.com/version/` instead of `https://cdn.example.com/version/`. + +If you need authentication to access the inventory file, you can provide the credentials in the URL, either as `username:password`: + +```yaml +- url: https://username:password@private.example.com/version/objects.inv +``` + +...or with token authentication: + +```yaml +- url: https://token123@private.example.com/version/objects.inv +``` + +The credentials can also be specified using environment variables in the form `${ENV_VAR}`: + +```yaml +- url: https://${USERNAME}:${PASSWORD}@private.example.com/version/objects.inv +``` + +Reciprocally, *mkdocstrings* also allows to *generate* an inventory file in the Sphinx format. It will be enabled by default if the Python handler is used, and generated as `objects.inv` in the final site directory. Other projects will be able to cross-reference items from your project. + +To explicitly enable or disable the generation of the inventory file, use the global `enable_inventory` option: + +```yaml +plugins: +- mkdocstrings: + enable_inventory: false +``` + +# Handlers + +A handler is what makes it possible to collect and render documentation for a particular language. + +## Available handlers + +- [C](https://mkdocstrings.github.io/c/) +- [Crystal](https://mkdocstrings.github.io/crystal/) +- [GitHub Actions](https://watermarkhu.nl/mkdocstrings-github/) +- [Python](https://mkdocstrings.github.io/python/) +- [Python (Legacy)](https://mkdocstrings.github.io/python-legacy/) +- [MATLAB](https://watermarkhu.nl/mkdocstrings-matlab/) +- [Shell](https://mkdocstrings.github.io/shell/) +- [TypeScript](https://mkdocstrings.github.io/typescript/) +- [VBA](https://pypi.org/project/mkdocstrings-vba/) + +## About the Python handlers + +Since version 0.18, a new Python handler is available. It is based on [Griffe](https://github.com/mkdocstrings/griffe), which is an improved version of [pytkdocs](https://github.com/mkdocstrings/pytkdocs). + +If you want to keep using the legacy handler as long as possible, you can depend on `mkdocstrings-python-legacy` directly, or specify the `python-legacy` extra when depending on *mkdocstrings*: + +pyproject.toml + +```toml +# PEP 621 dependencies declaration +# adapt to your dependencies manager +[project] +dependencies = [ + "mkdocstrings[python-legacy]>=0.18", +] +``` + +The legacy handler will continue to "work" for many releases, as long as the new handler does not cover all previous use-cases. + +### Migrate to the new Python handler + +To use the new Python handler, you can depend on `mkdocstrings-python` directly, or specify the `python` extra when depending on *mkdocstrings*: + +pyproject.toml + +```toml +# PEP 621 dependencies declaration +# adapt to your dependencies manager +[project] +dependencies = [ + "mkdocstrings[python]>=0.18", +] +``` + +#### Selection options + +Warning + +Since *mkdocstrings* 0.19, the YAML `selection` key is merged into the `options` key. + +- `filters` is implemented, and used as before. +- `members` is implemented, and used as before. +- `inherited_members` is implemented. +- `docstring_style` is implemented, and used as before, except for the `restructured-text` style which is renamed `sphinx`. Numpy-style is now built-in, so you can stop depending on `pytkdocs[numpy-style]` or `docstring_parser`. +- `docstring_options` is implemented, and used as before. Refer to the [`griffe` documentation](https://mkdocstrings.github.io/griffe/docstrings/#parsing-options) for the updated list of supported docstring options. +- `new_path_syntax` is irrelevant now. If you were setting it to True, remove the option and replace every colon (`:`) in your autodoc identifiers by dots (`.`). + +See [all the handler's options](https://mkdocstrings.github.io/python/usage/). + +#### Rendering options + +Warning + +Since *mkdocstrings* 0.19, the YAML `rendering` key is merged into the `options` key. + +Every previous option is supported. Additional options are available: + +- `separate_signature`: Render the signature (or attribute value) in a code block below the heading, instead as inline code. Useful for long signatures. If Black is installed, the signature is formatted. Default: `False`. +- `line_length`: The maximum line length to use when formatting signatures. Default: `60`. +- `show_submodules`: Whether to render submodules of a module when iterating on children. Default: `False`. +- `docstring_section_style`: The style to use to render docstring sections such as attributes, parameters, etc. Available styles: `"table"` (default), `"list"` and `"spacy"`. The SpaCy style is a poor implementation of their [table style](https://spacy.io/api/doc/#init). We are open to improvements through PRs! + +See [all the handler's options](https://mkdocstrings.github.io/python/usage/). + +#### Templates + +Templates are mostly the same as before, but the file layout has changed, as well as some file names. See [the documentation about the Python handler templates](https://mkdocstrings.github.io/python/usage/customization/#templates). + +## Custom handlers + +Since version 0.14, you can create and use custom handlers thanks to namespace packages. For more information about namespace packages, [see their documentation](https://packaging.python.org/guides/packaging-namespace-packages/). + +TL;DR - Project template for handlers. + +*mkdocstrings* provides a [Copier](https://github.com/copier-org/copier) template to kickstart new handlers: . To use it, install Copier (`pipx install copier`), then run `copier gh:mkdocstrings/handler-template my_handler` to generate a new project. See [its upstream documentation](https://pawamoy.github.io/copier-pdm/) to learn how to work on the generated project. + +### Packaging + +For *mkdocstrings*, a custom handler package would have the following structure: + +```text +📁 your_repository +└─╴📁 mkdocstrings_handlers + └─╴📁 custom_handler + ├─╴📁 templates + │  ├─╴📁 material + │ ├─╴📁 mkdocs + │ └─╴📁 readthedocs + └─╴📄 __init__.py +``` + +Note the absence of `__init__.py` module in `mkdocstrings_handlers`! + +### Code + +A handler is a subclass of the base handler provided by *mkdocstrings*. See the documentation for the BaseHandler. + +Subclasses of the base handler must declare a `name` and `domain` as class attributes, as well as implement the following methods: + +- `collect(identifier, options)` (**required**): method responsible for collecting and returning data (extracting documentation from source code, loading introspecting objects in memory, other sources? etc.) +- `render(identifier, options)` (**required**): method responsible for actually rendering the data to HTML, using the Jinja templates provided by your package. +- `get_options(local_options)` (**required**): method responsible for combining global options with local ones. +- `get_aliases(identifier)` (**recommended**): method responsible for returning known aliases of object identifiers, in order to register cross-references in the autorefs plugin. +- `get_inventory_urls()` (optional): method responsible for returning a list of URLs to download (object inventories) along with configuration options (for loading the inventory with `load_inventory`). +- `load_inventory(in_file, url, **options)` (optional): method responsible for loading an inventory (binary file-handle) and yielding tuples of identifiers and URLs. +- `update_env(config)` (optional): Gives you a chance to customize the Jinja environment used to render templates, for examples by adding/removing Jinja filters and global context variables. +- `teardown()` (optional): Clean up / teardown anything that needs it at the end of the build. + +You must implement a `get_handler` method at the module level, which returns an instance of your handler. This function takes the following parameters: + +- `theme` (string, theme name) +- `custom_templates` (optional string, path to custom templates directory) +- `mdx` (list, Markdown extensions) +- `mdx_config` (dict, extensions configuration) +- `handler_config` (dict, handle configuration) +- `tool_config` (dict, the whole MkDocs configuration) + +These arguments are all passed as keyword arguments, so you can ignore them by adding `**kwargs` or similar to your signature. + +You should not modify the MkDocs config but can use it to get information about the MkDocs instance such as where the current `site_dir` lives. See the [Mkdocs Configuration](https://www.mkdocs.org/user-guide/configuration/) for more info about what is accessible from it. + +Check out how the [Python handler](https://github.com/mkdocstrings/python/blob/master/src/mkdocstrings_handlers/python) is written for inspiration. + +### Templates + +Your handler's implementation should normally be backed by templates, which go to the directory `mkdocstrings_handlers/custom_handler/templates/some_theme` (`custom_handler` here should be replaced with the actual name of your handler, and `some_theme` should be the name of an actual MkDocs theme that you support, e.g. `material`). + +With that structure, you can use `self.env.get_template("foo.html")` inside your `render` method. This already chooses the subdirectory based on the current MkDocs theme. + +If you wish to support *any* MkDocs theme, rather than a few specifically selected ones, you can pick one theme's subdirectory to be the fallback for when an unknown theme is encountered. Then you just need to set the `fallback_theme` variable on your handler subclass. The fallback directory can be used even for themes you explicitly support: you can omit some template from one of the other theme directories in case they're exactly the same as in the fallback theme. + +If your theme's HTML requires CSS to go along with it, put it into a file named `mkdocstrings_handlers/custom_handler/templates/some_theme/style.css`, then this will be included into the final site automatically if this handler is ever used. Alternatively, you can put the CSS as a string into the `extra_css` variable of your handler. + +Finally, it's possible to entirely omit templates, and tell *mkdocstrings* to use the templates of another handler. In you handler, override the `get_templates_dir()` method to return the other handlers templates path: + +```python +from pathlib import Path +from mkdocstrings.handlers.base import BaseHandler + + +class CobraHandler(BaseHandler): + def get_templates_dir(self, handler: str | None = None) -> Path: + # use the python handler templates + # (it assumes the python handler is installed) + return super().get_templates_dir("python") +``` + +### Usage + +When a custom handler is installed, it is then available to *mkdocstrings*. You can configure it as usual: + +mkdocs.yml + +```yaml +plugins: +- mkdocstrings: + handlers: + custom_handler: + handler_config_option: yes + options: + some_config_option: "a" + other_config_option: 0 +``` + +...and use it in your autodoc instructions: + +docs/some_page.md + +```md +# Documentation for an object + +::: some.objects.path + handler: custom_handler + options: + some_config_option: "b" + other_config_option: 1 +``` + +## Handler extensions + +*mkdocstrings* provides a way for third-party packages to extend or alter the behavior of handlers. For example, an extension of the Python handler could add specific support for another Python library. + +Note + +This feature is intended for developers. If you are a user and want to customize how objects are rendered, see [Theming / Customization](../theming/#customization). + +Such extensions can register additional template folders that will be used when rendering collected data. Extensions are responsible for synchronizing with the handler itself so that it uses the additional templates. + +An extension is a Python package that defines an entry-point for a specific handler: + +pyproject.toml + +```toml +[project.entry-points."mkdocstrings.python.templates"] # (1)! +extension-name = "extension_package:get_templates_path" # (2)! +``` + +1. Replace `python` by the name of the handler you want to add templates to. +1. Replace `extension-name` by any name you want, and replace `extension_package:get_templates_path` by the actual module path and function name in your package. + +This entry-point assumes that the extension provides a `get_templates_path` function directly under the `extension_package` package: + +```bash + pyproject.toml +📁 extension_package/ +├── __init__.py +└── 📁 templates/ +``` + +extension_package/__init__.py + +```python +from pathlib import Path + + +def get_templates_path() -> Path: + return Path(__file__).parent / "templates" +``` + +This function doesn't accept any argument and returns the path (pathlib.Path or str) to a directory containing templates. The directory must contain one subfolder for each supported theme, even if empty (see "fallback theme" in [custom handlers templates](#templates_1)). For example: + +```bash + pyproject.toml +📁 extension_package/ +├── __init__.py +└── 📁 templates/ + ├── 📁 material/ + ├── 📁 readthedocs/ + └── 📁 mkdocs/ +``` + +*mkdocstrings* will add the folders corresponding to the user-selected theme, and to the handler's defined fallback theme, as usual. + +The names of the extension templates must not overlap with the handler's original templates. + +The extension is then responsible, in collaboration with its target handler, for mutating the collected data in order to instruct the handler to use one of the extension template when rendering particular objects. See each handler's docs to see if they support extensions, and how. + +# Themes + +*mkdocstrings* can support multiple MkDocs themes. It currently supports the *[Material for MkDocs](https://squidfunk.github.io/mkdocs-material/)* theme and, partially, the built-in MkDocs and ReadTheDocs themes. + +Each handler can fallback to a particular theme when the user selected theme is not supported. For example, the Python handler will fallback to the *Material for MkDocs* templates. + +## Customization + +There is some degree of customization possible in *mkdocstrings*. First, you can write custom templates to override the theme templates. Second, the provided templates make use of CSS classes, so you can tweak the look and feel with extra CSS rules. + +### Templates + +To use custom templates and override the theme ones, specify the relative path from your configuration file to your templates directory with the `custom_templates` global configuration option: + +mkdocs.yml + +```yaml +plugins: +- mkdocstrings: + custom_templates: templates +``` + +Your directory structure must be identical to the provided templates one: + +```text +📁 templates/ +├─╴📁 / +│ ├── 📁 / +│ └── 📁 / +└── 📁 / + ├── 📁 / + └── 📁 / +``` + +For example, check out the Python [template tree](https://github.com/mkdocstrings/python/tree/master/src/mkdocstrings_handlers/python/templates/) on GitHub. + +You don't have to replicate the whole tree, only the handlers, themes or templates you want to override. For example, to override some templates of the *Material* theme for Python: + +```text +📁 templates/ +└── 📁 python/ + └── 📁 material/ + ├── 📄 parameters.html + └── 📄 exceptions.html +``` + +In the HTML files, replace the original contents with your modified version. In the future, the templates will use Jinja blocks, so it will be easier to modify small part of the templates without copy-pasting the whole files. + +See the documentation about templates for: + +- the Crystal handler: +- the Python handler: + +#### Debugging + +Every template has access to a `log` function, allowing to log messages as usual: + +```jinja +{{ log.debug("A DEBUG message.") }} +{{ log.info("An INFO message.") }} +{{ log.warning("A WARNING message.") }} +{{ log.error("An ERROR message.") }} +{{ log.critical("A CRITICAL message.") }} +``` + +### CSS classes + +Since each handler provides its own set of templates, with their own CSS classes, we cannot list them all here. See the documentation about CSS classes for: + +- the Crystal handler: +- the Python handler: + +### Syntax highlighting + +Code blocks that occur in the docstring of an item inserted with *mkdocstrings*, as well as code blocks (such as *Source code*) that *mkdocstrings* inserts itself, are syntax-highlighted according to the same rules as other normal code blocks in your document. See more details in mkdocstrings.Highlighter. + +As for the CSS class used for code blocks -- it will also match the "normal" config, so the default (`.codehilite` or `.highlight`) will match your chosen Markdown extension for highlighting. + +Changed in version 0.15. + +The CSS class used to always be `.highlight`, but now it depends on the configuration. + +Long story short, you probably should add `pymdownx.highlight` to your `markdown_extensions`, and then use `.doc-contents .highlight` as the CSS selector in case you want to change something about *mkdocstrings'* code blocks specifically. + +On this page you will find various recipes, tips and tricks for *mkdocstrings* and more generally Markdown documentation. + +## Automatic code reference pages + +[mkdocs-autoapi](https://github.com/jcayers20/mkdocs-autoapi) and [mkdocs-api-autonav](https://github.com/tlambert03/mkdocs-api-autonav) are MkDocs plugins that automatically generate API documentation from your project's source code. They were inspired by the recipe below. + +*mkdocstrings* allows to inject documentation for any object into Markdown pages. But as the project grows, it quickly becomes quite tedious to keep the autodoc instructions, or even the dedicated Markdown files in sync with all your source files and objects. + +In this recipe, we will iteratively automate the process of generating these pages at each build of the documentation. + +______________________________________________________________________ + +Let say you have a project called `project`. This project has a lot of source files, or modules, which live in the `src` folder: + +```bash +📁 repo/ +└── 📁 src/ + └── 📁 project/ + ├── lorem + ├── ipsum + ├── dolor + ├── sit + └── amet +``` + +Without an automatic process, you will have to manually create a Markdown page for each one of these modules, with the corresponding autodoc instruction, for example `::: project.lorem`, and also add entry in MkDocs' navigation option (`nav` in `mkdocs.yml`). With a lot of modules, this is quickly getting cumbersome. + +Lets fix that. + +### Generate pages on-the-fly + +In this recipe, we suggest to use the [mkdocs-gen-files plugin](https://github.com/oprypin/mkdocs-gen-files). This plugin exposes utilities to generate files at build time. These files won't be written to the docs directory: you don't have to track and version them. They are transparently generated each time you build your docs. This is perfect for our use-case! + +Add `mkdocs-gen-files` to your project's docs dependencies, and configure it like so: + +mkdocs.yml + +```yaml +plugins: +- search # (1)! +- gen-files: + scripts: + - scripts/gen_ref_pages.py # (2)! +- mkdocstrings +``` + +1. Don't forget to load the `search` plugin when redefining the `plugins` item. +1. The magic happens here, see below how it works. + +mkdocs-gen-files is able to run Python scripts at build time. The Python script that we will execute lives in a scripts folder, and is named `gen_ref_pages.py`, like "generate code reference pages". + +```bash +📁 repo/ +├── 📁 docs/ +│ └── index.md +├── 📁 scripts/ +│ └── gen_ref_pages.py +├── 📁 src/ +│ └── 📁 project/ +└── mkdocs.yml +``` + +scripts/gen_ref_pages.py + +```python +"""Generate the code reference pages.""" + +from pathlib import Path + +import mkdocs_gen_files + +root = Path(__file__).parent.parent +src = root / "src" # (1)! + +for path in sorted(src.rglob("*.py")): # (2)! + module_path = path.relative_to(src).with_suffix("") # (3)! + doc_path = path.relative_to(src).with_suffix(".md") # (4)! + full_doc_path = Path("reference", doc_path) # (5)! + + parts = tuple(module_path.parts) + + if parts[-1] == "__init__": # (6)! + parts = parts[:-1] + elif parts[-1] == "__main__": + continue + + with mkdocs_gen_files.open(full_doc_path, "w") as fd: # (7)! + identifier = ".".join(parts) # (8)! + print("::: " + identifier, file=fd) # (9)! + + mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root)) # (10)! +``` + +1. It's important to build a path relative to the script itself, to make it possible to build the docs with MkDocs' [`-f` option](https://www.mkdocs.org/user-guide/cli/#mkdocs-build). +1. Here we recursively list all `.py` files, but you can adapt the code to list files with other extensions of course, supporting other languages than Python. +1. The module path will look like `project/lorem`. It will be used to build the *mkdocstrings* autodoc identifier. +1. This is the partial path of the Markdown page for the module. +1. This is the full path of the Markdown page within the docs. Here we put all reference pages into a `reference` folder. +1. This part is only relevant for Python modules. We skip `__main__` modules and remove `__init__` from the module parts as it's implicit during imports. +1. Magic! Add the file to MkDocs pages, without actually writing it in the docs folder. +1. Build the autodoc identifier. Here we document Python modules, so the identifier is a dot-separated path, like `project.lorem`. +1. Actually write to the magic file. +1. We can even set the `edit_uri` on the pages. + +Note + +It is important to look out for correct edit page behaviour when using generated pages. For example, if we have `edit_uri` set to `blob/master/docs/` and the following file structure: + +```bash +📁 repo/ +├── mkdocs.yml +├── 📁 docs/ +│ └── index.md +├── 📁 scripts/ +│ └── gen_ref_pages.py +└── 📁 src/ + └── 📁 project/ + ├── lorem.py + ├── ipsum.py + ├── dolor.py + ├── sit.py + └── amet.py +``` + +Then we will have to change our `set_edit_path` call to: + +```python +mkdocs_gen_files.set_edit_path(full_doc_path, Path("../") / path) # (1)! +``` + +1. Path can be used to traverse the structure in any way you may need, but remember to use relative paths! + +...so that it correctly sets the edit path of (for example) `lorem.py` to `/blob/master/src/project/lorem.py` instead of `/blob/master/docs/src/project/lorem.py`. + +With this script, a `reference` folder is automatically created each time we build our docs. This folder contains a Markdown page for each of our source modules, and each of these pages contains a single line of the form `::: project.module` (module being `lorem`, `ipsum`, etc.). Great! But, we still have to actually add those pages into our MkDocs navigation: + +mkdocs.yml + +```yaml +nav: +# rest of the navigation... +- Code Reference: + - project: + - lorem: reference/project/lorem.md + - ipsum: reference/project/ipsum.md + - dolor: reference/project/dolor.md + - sit: reference/project/sit.md + - amet: reference/project/amet.md +# rest of the navigation... +``` + +Err... so this process is only semi-automatic? Yes, but don't worry, we can fully automate it. + +### Generate a literate navigation file + +mkdocs-gen-files is able to generate a literate navigation file. But to make use of it, we will need an additional plugin: [mkdocs-literate-nav](https://github.com/oprypin/mkdocs-literate-nav). This plugin allows to specify the whole navigation, or parts of it, into Markdown pages, as plain Markdown lists. We use it here to specify the navigation for the code reference pages. + +First, add `mkdocs-literate-nav` to your project's docs dependencies, and configure the plugin in your MkDocs configuration: + +mkdocs.yml + +```yaml +plugins: +- search +- gen-files: + scripts: + - scripts/gen_ref_pages.py +- literate-nav: + nav_file: SUMMARY.md +- mkdocstrings +``` + +Then, the previous script is updated like so: + +scripts/gen_ref_pages.py + +```python +"""Generate the code reference pages and navigation.""" + +from pathlib import Path + +import mkdocs_gen_files + +nav = mkdocs_gen_files.Nav() + +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] + elif parts[-1] == "__main__": + continue + + nav[parts] = doc_path.as_posix() # (1)! + + with mkdocs_gen_files.open(full_doc_path, "w") as fd: + ident = ".".join(parts) + fd.write(f"::: {ident}") + + mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root)) + +with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file: # (2)! + nav_file.writelines(nav.build_literate_nav()) # (3)! +``` + +1. Progressively build the navigation object. +1. At the end, create a magic, literate navigation file called `SUMMARY.md` in the `reference` folder. +1. Write the navigation as a Markdown list in the literate navigation file. + +Now we are able to remove our hard-coded navigation in `mkdocs.yml`, and replace it with a single line! + +mkdocs.yml + +```yaml +nav: +# rest of the navigation... +# defer to gen-files + literate-nav +- Code Reference: reference/ # (1)! +# rest of the navigation... +``` + +1. Note the trailing slash! It is needed so that `mkdocs-literate-nav` knows it has to look for a `SUMMARY.md` file in that folder. + +At this point, we should be able to see the tree of our modules in the navigation. + +### Bind pages to sections themselves + +There's a last improvement we can do. With the current script, sections, corresponding to folders, will expand or collapse when you click on them, revealing `__init__` modules under them (or equivalent modules in other languages, if relevant). Since we are documenting a public API, and given users never explicitly import `__init__` modules, it would be nice if we could get rid of them and instead render their documentation inside the section itself. + +Well, this is possible thanks to a third plugin: [mkdocs-section-index](https://github.com/oprypin/mkdocs-section-index). + +Update the script like this: + +scripts/gen_ref_pages.py + +```python +"""Generate the code reference pages and navigation.""" + +from pathlib import Path + +import mkdocs_gen_files + +nav = mkdocs_gen_files.Nav() + +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") + elif parts[-1] == "__main__": + continue + + nav[parts] = doc_path.as_posix() + + with mkdocs_gen_files.open(full_doc_path, "w") as fd: + ident = ".".join(parts) + fd.write(f"::: {ident}") + + mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root)) + +with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file: + nav_file.writelines(nav.build_literate_nav()) +``` + +And update your MkDocs configuration to list the plugin: + +mkdocs.yml + +```yaml +plugins: +- search +- gen-files: + scripts: + - scripts/gen_ref_pages.py +- literate-nav: + nav_file: SUMMARY.md +- section-index +- mkdocstrings +``` + +With this, `__init__` modules will be documented and bound to the sections themselves, better reflecting our public API. + +## Prevent selection of prompts and output in Python code blocks + +To prevent the selection of `>>>`, `...` and output in Python "Console" code blocks, you can use the `pycon` syntax highlighting on your code blocks, and add global CSS rules to your site using MkDocs `extra_css` option: + +````md +```pycon +>>> for word in ("Hello", "mkdocstrings!"): +... print(word, end=" ") +... +Hello mkdocstrings! +```` + +```` + +docs/css/code_select.css + +```css +.highlight .gp, .highlight .go { /* Generic.Prompt, Generic.Output */ + user-select: none; +} +```` + +mkdocs.yml + +```yaml +extra_css: +- css/code_select.css +``` + +Warning + +The `.highlight .gp, .highlight .go` CSS selector can have unintended side-effects. To target `pycon` code blocks more specifically, you can configure the `pymdownx.highlight` extension to use Pygments and set language classes on code blocks: + +mkdocs.yml + +```yaml +markdown_extensions: +- pymdownx.highlight: + use_pygments: true + pygments_lang_class: true +``` + +Then you can update the CSS selector like this: + +docs/css/code_select.css + +```css +.language-pycon .gp, .language-pycon .go { /* Generic.Prompt, Generic.Output */ + user-select: none; +} +``` + +If you don't want to enable this globally, you can still use `style` tags in the relevant pages, with more accurate CSS selectors: + +```html + +``` + +Try to select the following code block's text: + +```pycon +>>> for word in ("Hello", "mkdocstrings!"): +... print(word, end=" ") +Hello mkdocstrings! +``` + +## Hide documentation strings from source code blocks + +Since documentation strings are rendered by handlers, it can sometimes feel redundant to show these same documentation strings in source code blocks (when handlers render those). + +There is a general workaround to hide these docstrings from source blocks using CSS: + +```css +/* These CSS classes depend on the handler. */ +.doc-contents details .highlight code { + line-height: 0; +} +.doc-contents details .highlight code > * { + line-height: initial; +} +.doc-contents details .highlight code > .sd { /* Literal.String.Doc */ + display: none; +} +``` + +Note that this is considered a workaround and not a proper solution, because it has side-effects like also removing blank lines. + +## Automatic highlighting for indented code blocks in docstrings + +Depending on the language used in your code base and the mkdocstrings handler used to document it, you might want to set a default syntax for code blocks added to your docstrings. For example, to default to the Python syntax: + +mkdocs.yml + +```yaml +markdown_extensions: +- pymdownx.highlight: + default_lang: python +``` + +Then in your docstrings, indented code blocks will be highlighted as Python code: + +```python +def my_function(): + """This is my function. + + The following code will be highlighted as Python: + + result = my_function() + print(result) + + End of the docstring. + """ + pass +``` + +# Troubleshooting + +## Code blocks in admonitions (in docstrings or else) are not rendered correctly + +To render code blocks in admonitions, you need to add the `pymdownx.superfences` extensions to the list of Markdown extensions in `mkdocs.yml`. For example: + +````markdown +!!! note + Some text. + + ```bash + echo "some code" + ``` +```` + +mkdocs.yml + +```yaml +markdown_extensions: +- admonition +- codehilite +- pymdownx.superfences +``` + +For code blocks in docstrings, make sure to escape newlines (`\n` -> `\\n`), or prefix the entire docstring with 'r' to make it a raw-docstring: `r"""`. Indeed, docstrings are still strings and therefore subject to how Python parses strings. + +## Footnotes are duplicated or overridden + +Before version 0.14, footnotes could be duplicated over a page. Please upgrade to version 0.14 or higher. + +See also: + +- [Issue #186](https://github.com/mkdocstrings/mkdocstrings/issues/186) +- [Tabs in docstrings (from `pymdownx.tabbed`) are not working properly](#tabs-in-docstrings-from-pymdownxtabbed-are-not-working-properly). + +## MkDocs warns me about links to unfound documentation files + +A warning like this one: + +> WARNING - Documentation file 'reference/parsers/docstrings.md' contains a link to 'reference/parsers/pytkdocs.parsers.docstrings.Section' which is not found in the documentation files. + +...generally means you used parentheses `()` instead of brackets `[]` for a cross-reference. Notice the dots in `reference/parsers/pytkdocs.parsers.docstrings.Section`? It shows that it's probably a cross-reference, not a direct link. It's probably written like `[Section](pytkdocs.parsers.docstrings.Section)` in the docs, when it should be `[Section][pytkdocs.parsers.docstrings.Section]`. + +## Some objects are not rendered (they do not appear in the generated docs) + +- Make sure the configuration options of the handler are correct. Check the documentation for [Handlers](../usage/handlers/) to see the available options for each handler. +- Also make sure your documentation in your source code is formatted correctly. For Python code, check the [supported docstring styles](https://mkdocstrings.github.io/python/usage/#supported-docstrings-styles) page. +- Re-run the Mkdocs command with `-v`, and carefully read any traceback. + +## Tabs in docstrings (from `pymdownx.tabbed`) are not working properly + +Before version 0.14, multiple tab blocks injected on the same page would result in broken links: clicking on a tab would bring the user to the wrong one. Please upgrade to version 0.14 or higher. + +See also: + +- [Issue #193](https://github.com/mkdocstrings/mkdocstrings/issues/193) +- [Footnotes are duplicated or overridden](#footnotes-are-duplicated-or-overridden). + +If you are stuck on a version before 0.14, and want to use multiple tab blocks in one page, use this workaround. + +JavaScript workaround + +Put the following code in a .js file, and list it in MkDocs' `extra_javascript`: + +```javascript +// Credits to Nikolaos Zioulis (@zuru on GitHub) +function setID(){ + var tabs = document.getElementsByClassName("tabbed-set"); + for (var i = 0; i < tabs.length; i++) { + children = tabs[i].children; + var counter = 0; + var iscontent = 0; + for(var j = 0; j < children.length;j++){ + if(typeof children[j].htmlFor === 'undefined'){ + if((iscontent + 1) % 2 == 0){ + // check if it is content + if(iscontent == 1){ + btn = children[j].childNodes[1].getElementsByTagName("button"); + } + } + else{ + // if not change the id + children[j].id = "__tabbed_" + String(i + 1) + "_" + String(counter + 1); + children[j].name = "__tabbed_" + String(i + 1); + // make default tab open + if(j == 0) + children[j].click(); + } + iscontent++; + } + else{ + // link to the correct tab + children[j].htmlFor = "__tabbed_" + String(i+1) + "_" + String(counter + 1); + counter ++; + } + } + } +} +setID(); +``` + +This code will correctly reset the IDs for tabs on a same page. + +## The generated documentation does not look good + +Please open an ticket on the [bugtracker](https://github.com/mkdocstrings/mkdocstrings) with a detailed explanation and screenshots of the bad-looking parts. Note that you can always [customize the look](../usage/theming/) of *mkdocstrings* blocks -- through both HTML and CSS. + +## Warning: could not find cross-reference target + +New in version 0.15. + +Cross-linking used to include any Markdown heading, but now it's only for *mkdocstrings* identifiers by default. See [Cross-references to any Markdown heading](../usage/#cross-references-to-any-markdown-heading) to opt back in. + +Make sure the referenced object is properly rendered: verify your configuration options. + +For false-positives, you can wrap the text in backticks (`) to prevent `mkdocstrings\` from trying to process it. + +______________________________________________________________________ + +## Python specifics + +### Nothing is rendered at all + +Is your package available in the Python path? + +See [Python handler: Finding modules](https://mkdocstrings.github.io/python/usage/#finding-modules). + +### LaTeX in docstrings is not rendered correctly + +If you are using a Markdown extension like [Arithmatex Mathjax](https://squidfunk.github.io/mkdocs-material/setup/extensions/python-markdown-extensions/#arithmatex) or [`markdown-katex`](https://gitlab.com/mbarkhau/markdown-katex) to render LaTeX, add `r` in front of your docstring to make sure nothing is escaped. You'll still maybe have to play with escaping to get things right. + +Example: + +````python +def math_function(x, y): + r""" + Look at these formulas: + + ```math + f(x) = \int_{-\infty}^\infty + \hat f(\xi)\,e^{2 \pi i \xi x} + \,d\xi + ``` + """ +```` + +### My docstrings in comments (`#:`) are not picked up + +We only support docstrings in comments through the [griffe-sphinx](https://mkdocstrings.github.io/griffe-sphinx) extension. + +Alternatively, instead of: + +```python +import enum + + +class MyEnum(enum.Enum): + v1 = 1 #: The first choice. + v2 = 2 #: The second choice. +``` + +You can use: + +```python +import enum + + +class MyEnum(enum.Enum): + v1 = 1 + """The first choice.""" + + v2 = 2 + """The second choice.""" +``` + +Or: + +```python +import enum + + +class MyEnum(enum.Enum): + """My enum. + + Attributes: + v1: The first choice. + v2: The second choice. + """ + + v1 = 1 + v2 = 2 +``` + +### My wrapped function shows documentation/code for its wrapper instead of its own + +Use [`functools.wraps()`](https://docs.python.org/3.6/library/functools.html#functools.wraps): + +```python +from functools import wraps + + +def my_decorator(function): + """The decorator docs.""" + + @wraps(function) + def wrapped_function(*args, **kwargs): + print("hello") + function(*args, **kwargs) + print("bye") + + return wrapped_function + + +@my_decorator +def my_function(*args, **kwargs): + """The function docs.""" + print(*args, **kwargs) +``` + +### Footnotes do not render + +The library that parses docstrings, [Griffe](https://mkdocstrings.github.io/griffe/), splits docstrings in several "sections" (example: [Google-style sections syntax](https://mkdocstrings.github.io/griffe/reference/docstrings/#google-syntax)). If a footnote is used in a section, while referenced in another, mkdocstrings won't be able to render it correctly. The footnote and its reference must appear in the same section. + +```python +def my_function(): + """Summary. + + This is the first section[^1]. + + Note: + This is the second section[^2]. + + Note: + This is the third section[^3]. + + References at the end are part of yet another section (fourth here)[^4]. + + [^1]: Some text. + [^2]: Some text. + [^3]: Some text. + [^4]: Some text. + """ +``` + +Here only the fourth footnote will work, because it is the only one that appear in the same section as its reference. To fix this, make sure all footnotes appear in the same section as their references: + +```python +def my_function(): + """Summary. + + This is the first section[^1]. + + [^1]: Some text. + + Note: + This is the second section[^2]. + + [^2]: Some text. + + Note: + This is the third section[^3]. + + [^3]: Some text. + + References at the end are part of yet another section (fourth here)[^4]. + + [^4]: Some text. + """ +``` + +### Submodules are not rendered + +In previous versions of mkdocstrings-python, submodules were rendered by default. This was changed and you now need to set the following option: + +mkdocs.yml + +```yaml +plugins: +- mkdocstrings: + handlers: + python: + options: + show_submodules: true +``` +# API + +# mkdocstrings + +mkdocstrings package. + +Automatic documentation from sources, for MkDocs. + +Classes: + +- **`AutoDocProcessor`** – Our "autodoc" Markdown block processor. +- **`BaseHandler`** – The base handler class. +- **`CollectionError`** – An exception raised when some collection of data failed. +- **`Handlers`** – A collection of handlers. +- **`HeadingShiftingTreeprocessor`** – Shift levels of all Markdown headings according to the configured base level. +- **`Highlighter`** – Code highlighter that tries to match the Markdown configuration. +- **`IdPrependingTreeprocessor`** – Prepend the configured prefix to IDs of all HTML elements. +- **`Inventory`** – Inventory of collected and rendered objects. +- **`InventoryItem`** – Inventory item. +- **`LoggerAdapter`** – A logger adapter to prefix messages. +- **`MkdocstringsExtension`** – Our Markdown extension. +- **`MkdocstringsInnerExtension`** – Extension that should always be added to Markdown sub-documents that handlers request (and only them). +- **`MkdocstringsPlugin`** – An mkdocs plugin. +- **`ParagraphStrippingTreeprocessor`** – Unwraps the

element around the whole output. +- **`PluginConfig`** – The configuration options of mkdocstrings, written in mkdocs.yml. +- **`TemplateLogger`** – A wrapper class to allow logging in templates. +- **`ThemeNotSupported`** – An exception raised to tell a theme is not supported. + +Functions: + +- **`do_any`** – Check if at least one of the item in the sequence evaluates to true. +- **`get_logger`** – Return a pre-configured logger. +- **`get_template_logger`** – Return a logger usable in templates. +- **`get_template_logger_function`** – Create a wrapper function that automatically receives the Jinja template context. +- **`get_template_path`** – Return the path to the template currently using the given context. +- **`makeExtension`** – Create the extension instance. + +Attributes: + +- **`CollectorItem`** – The type of the item returned by the collect method of a handler. +- **`HandlerConfig`** – The type of the configuration of a handler. +- **`HandlerOptions`** – The type of the options passed to a handler. +- **`TEMPLATES_DIRS`** (`Sequence[Path]`) – The directories where the handler templates are located. + +## CollectorItem + +```python +CollectorItem = Any +``` + +The type of the item returned by the `collect` method of a handler. + +## HandlerConfig + +```python +HandlerConfig = Any +``` + +The type of the configuration of a handler. + +## HandlerOptions + +```python +HandlerOptions = Any +``` + +The type of the options passed to a handler. + +## TEMPLATES_DIRS + +```python +TEMPLATES_DIRS: Sequence[Path] = tuple(__path__) +``` + +The directories where the handler templates are located. + +## AutoDocProcessor + +```python +AutoDocProcessor( + md: Markdown, + *, + handlers: Handlers, + autorefs: AutorefsPlugin, +) +``` + +Bases: `BlockProcessor` + +Our "autodoc" Markdown block processor. + +It has a test method that tells if a block matches a criterion, and a run method that processes it. + +It also has utility methods allowing to get handlers and their configuration easily, useful when processing a matched block. + +Parameters: + +- ### **`md`** + + (`Markdown`) – A markdown.Markdown instance. + +- ### **`handlers`** + + (`Handlers`) – The handlers container. + +- ### **`autorefs`** + + (`AutorefsPlugin`) – The autorefs plugin instance. + +Methods: + +- **`run`** – Run code on the matched blocks. +- **`test`** – Match our autodoc instructions. + +Attributes: + +- **`md`** – The Markdown instance. +- **`regex`** – The regular expression to match our autodoc instructions. + +Source code in `src/mkdocstrings/_internal/extension.py` + +```python +def __init__( + self, + md: Markdown, + *, + handlers: Handlers, + autorefs: AutorefsPlugin, +) -> None: + """Initialize the object. + + Arguments: + md: A `markdown.Markdown` instance. + handlers: The handlers container. + autorefs: The autorefs plugin instance. + """ + super().__init__(parser=md.parser) + self.md = md + """The Markdown instance.""" + self._handlers = handlers + self._autorefs = autorefs + self._updated_envs: set = set() +``` + +### md + +```python +md = md +``` + +The Markdown instance. + +### regex + +```python +regex = compile( + "^(?P#{1,6} *|)::: ?(?P.+?) *$", + flags=MULTILINE, +) +``` + +The regular expression to match our autodoc instructions. + +### run + +```python +run(parent: Element, blocks: MutableSequence[str]) -> None +``` + +Run code on the matched blocks. + +The identifier and configuration lines are retrieved from a matched block and used to collect and render an object. + +Parameters: + +- #### **`parent`** + + (`Element`) – The parent element in the XML tree. + +- #### **`blocks`** + + (`MutableSequence[str]`) – The rest of the blocks to be processed. + +Source code in `src/mkdocstrings/_internal/extension.py` + +```python +def run(self, parent: Element, blocks: MutableSequence[str]) -> None: + """Run code on the matched blocks. + + The identifier and configuration lines are retrieved from a matched block + and used to collect and render an object. + + Arguments: + parent: The parent element in the XML tree. + blocks: The rest of the blocks to be processed. + """ + block = blocks.pop(0) + match = self.regex.search(block) + + if match: + if match.start() > 0: + self.parser.parseBlocks(parent, [block[: match.start()]]) + # removes the first line + block = block[match.end() :] + + block, the_rest = self.detab(block) + + if not block and blocks and blocks[0].startswith((" handler:", " options:")): + # YAML options were separated from the `:::` line by a blank line. + block = blocks.pop(0) + + if match: + identifier = match["name"] + heading_level = match["heading"].count("#") + _logger.debug("Matched '::: %s'", identifier) + + html, handler, _ = self._process_block(identifier, block, heading_level) + el = Element("div", {"class": "mkdocstrings"}) + # The final HTML is inserted as opaque to subsequent processing, and only revealed at the end. + el.text = self.md.htmlStash.store(html) + + if handler.outer_layer: + self._process_headings(handler, el) + + parent.append(el) + + if the_rest: + # This block contained unindented line(s) after the first indented + # line. Insert these lines as the first block of the master blocks + # list for future processing. + blocks.insert(0, the_rest) +``` + +### test + +```python +test(parent: Element, block: str) -> bool +``` + +Match our autodoc instructions. + +Parameters: + +- #### **`parent`** + + (`Element`) – The parent element in the XML tree. + +- #### **`block`** + + (`str`) – The block to be tested. + +Returns: + +- `bool` – Whether this block should be processed or not. + +Source code in `src/mkdocstrings/_internal/extension.py` + +```python +def test(self, parent: Element, block: str) -> bool: # noqa: ARG002 + """Match our autodoc instructions. + + Arguments: + parent: The parent element in the XML tree. + block: The block to be tested. + + Returns: + Whether this block should be processed or not. + """ + return bool(self.regex.search(block)) +``` + +## BaseHandler + +```python +BaseHandler( + *, + theme: str, + custom_templates: str | None, + mdx: Sequence[str | Extension], + mdx_config: Mapping[str, Any], +) +``` + +The base handler class. + +Inherit from this class to implement a handler. + +You will have to implement the `collect` and `render` methods. You can also implement the `teardown` method, and override the `update_env` method, to add more filters to the Jinja environment, making them available in your Jinja templates. + +To define a fallback theme, add a `fallback_theme` class-variable. To add custom CSS, add an `extra_css` variable or create an 'style.css' file beside the templates. + +If the given theme is not supported (it does not exist), it will look for a `fallback_theme` attribute in `self` to use as a fallback theme. + +Other Parameters: + +- **`theme`** (`str`) – The theme to use. +- **`custom_templates`** (`str | None`) – The path to custom templates. +- **`mdx`** (`list[str | Extension]`) – A list of Markdown extensions to use. +- **`mdx_config`** (`Mapping[str, Mapping[str, Any]]`) – Configuration for the Markdown extensions. + +Methods: + +- **`collect`** – Collect data given an identifier and user configuration. +- **`do_convert_markdown`** – Render Markdown text; for use inside templates. +- **`do_heading`** – Render an HTML heading and register it for the table of contents. For use inside templates. +- **`get_aliases`** – Return the possible aliases for a given identifier. +- **`get_extended_templates_dirs`** – Load template extensions for the given handler, return their templates directories. +- **`get_headings`** – Return and clear the headings gathered so far. +- **`get_inventory_urls`** – Return the URLs (and configuration options) of the inventory files to download. +- **`get_options`** – Get combined options. +- **`get_templates_dir`** – Return the path to the handler's templates directory. +- **`load_inventory`** – Yield items and their URLs from an inventory file streamed from in_file. +- **`render`** – Render a template using provided data and configuration options. +- **`render_backlinks`** – Render backlinks. +- **`teardown`** – Teardown the handler. +- **`update_env`** – Update the Jinja environment. + +Attributes: + +- **`custom_templates`** – The path to custom templates. +- **`domain`** (`str`) – The handler's domain, used to register objects in the inventory, for example "py". +- **`enable_inventory`** (`bool`) – Whether the inventory creation is enabled. +- **`env`** – The Jinja environment. +- **`extra_css`** (`str`) – Extra CSS. +- **`fallback_theme`** (`str`) – Fallback theme to use when a template isn't found in the configured theme. +- **`md`** (`Markdown`) – The Markdown instance. +- **`mdx`** – The Markdown extensions to use. +- **`mdx_config`** – The configuration for the Markdown extensions. +- **`name`** (`str`) – The handler's name, for example "python". +- **`outer_layer`** (`bool`) – Whether we're in the outer Markdown conversion layer. +- **`theme`** – The selected theme. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def __init__( + self, + *, + theme: str, + custom_templates: str | None, + mdx: Sequence[str | Extension], + mdx_config: Mapping[str, Any], +) -> None: + """Initialize the object. + + If the given theme is not supported (it does not exist), it will look for a `fallback_theme` attribute + in `self` to use as a fallback theme. + + Keyword Arguments: + theme (str): The theme to use. + custom_templates (str | None): The path to custom templates. + mdx (list[str | Extension]): A list of Markdown extensions to use. + mdx_config (Mapping[str, Mapping[str, Any]]): Configuration for the Markdown extensions. + """ + self.theme = theme + """The selected theme.""" + self.custom_templates = custom_templates + """The path to custom templates.""" + self.mdx = mdx + """The Markdown extensions to use.""" + self.mdx_config = mdx_config + """The configuration for the Markdown extensions.""" + self._md: Markdown | None = None + self._headings: list[Element] = [] + + paths = [] + + # add selected theme templates + themes_dir = self.get_templates_dir(self.name) + paths.append(themes_dir / self.theme) + + # add extended theme templates + extended_templates_dirs = self.get_extended_templates_dirs(self.name) + for templates_dir in extended_templates_dirs: + paths.append(templates_dir / self.theme) + + # add fallback theme templates + if self.fallback_theme and self.fallback_theme != self.theme: + paths.append(themes_dir / self.fallback_theme) + + # add fallback theme of extended templates + for templates_dir in extended_templates_dirs: + paths.append(templates_dir / self.fallback_theme) + + for path in paths: + css_path = path / "style.css" + if css_path.is_file(): + self.extra_css += "\n" + css_path.read_text(encoding="utf-8") + break + + if self.custom_templates is not None: + paths.insert(0, Path(self.custom_templates) / self.name / self.theme) + + self.env = Environment( + autoescape=True, + loader=FileSystemLoader(paths), + auto_reload=False, # Editing a template in the middle of a build is not useful. + ) + """The Jinja environment.""" + + self.env.filters["convert_markdown"] = self.do_convert_markdown + self.env.filters["heading"] = self.do_heading + self.env.filters["any"] = do_any + self.env.globals["log"] = get_template_logger(self.name) +``` + +### custom_templates + +```python +custom_templates = custom_templates +``` + +The path to custom templates. + +### domain + +```python +domain: str +``` + +The handler's domain, used to register objects in the inventory, for example "py". + +### enable_inventory + +```python +enable_inventory: bool = False +``` + +Whether the inventory creation is enabled. + +### env + +```python +env = Environment( + autoescape=True, + loader=FileSystemLoader(paths), + auto_reload=False, +) +``` + +The Jinja environment. + +### extra_css + +```python +extra_css: str = '' +``` + +Extra CSS. + +### fallback_theme + +```python +fallback_theme: str = '' +``` + +Fallback theme to use when a template isn't found in the configured theme. + +### md + +```python +md: Markdown +``` + +The Markdown instance. + +Raises: + +- `RuntimeError` – When the Markdown instance is not set yet. + +### mdx + +```python +mdx = mdx +``` + +The Markdown extensions to use. + +### mdx_config + +```python +mdx_config = mdx_config +``` + +The configuration for the Markdown extensions. + +### name + +```python +name: str +``` + +The handler's name, for example "python". + +### outer_layer + +```python +outer_layer: bool +``` + +Whether we're in the outer Markdown conversion layer. + +### theme + +```python +theme = theme +``` + +The selected theme. + +### collect + +```python +collect( + identifier: str, options: HandlerOptions +) -> CollectorItem +``` + +Collect data given an identifier and user configuration. + +In the implementation, you typically call a subprocess that returns JSON, and load that JSON again into a Python dictionary for example, though the implementation is completely free. + +Parameters: + +- #### **`identifier`** + + (`str`) – An identifier for which to collect data. For example, in Python, it would be 'mkdocstrings.handlers' to collect documentation about the handlers module. It can be anything that you can feed to the tool of your choice. + +- #### **`options`** + + (`HandlerOptions`) – The final configuration options. + +Returns: + +- `CollectorItem` – Anything you want, as long as you can feed it to the handler's render method. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem: + """Collect data given an identifier and user configuration. + + In the implementation, you typically call a subprocess that returns JSON, and load that JSON again into + a Python dictionary for example, though the implementation is completely free. + + Arguments: + identifier: An identifier for which to collect data. For example, in Python, + it would be 'mkdocstrings.handlers' to collect documentation about the handlers module. + It can be anything that you can feed to the tool of your choice. + options: The final configuration options. + + Returns: + Anything you want, as long as you can feed it to the handler's `render` method. + """ + raise NotImplementedError +``` + +### do_convert_markdown + +```python +do_convert_markdown( + text: str, + heading_level: int, + html_id: str = "", + *, + strip_paragraph: bool = False, + autoref_hook: AutorefsHookInterface | None = None, +) -> Markup +``` + +Render Markdown text; for use inside templates. + +Parameters: + +- #### **`text`** + + (`str`) – The text to convert. + +- #### **`heading_level`** + + (`int`) – The base heading level to start all Markdown headings from. + +- #### **`html_id`** + + (`str`, default: `''` ) – The HTML id of the element that's considered the parent of this element. + +- #### **`strip_paragraph`** + + (`bool`, default: `False` ) – Whether to exclude the

tag from around the whole output. + +Returns: + +- `Markup` – An HTML string. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def do_convert_markdown( + self, + text: str, + heading_level: int, + html_id: str = "", + *, + strip_paragraph: bool = False, + autoref_hook: AutorefsHookInterface | None = None, +) -> Markup: + """Render Markdown text; for use inside templates. + + Arguments: + text: The text to convert. + heading_level: The base heading level to start all Markdown headings from. + html_id: The HTML id of the element that's considered the parent of this element. + strip_paragraph: Whether to exclude the `

` tag from around the whole output. + + Returns: + An HTML string. + """ + global _markdown_conversion_layer # noqa: PLW0603 + _markdown_conversion_layer += 1 + treeprocessors = self.md.treeprocessors + treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = heading_level # type: ignore[attr-defined] + treeprocessors[IdPrependingTreeprocessor.name].id_prefix = html_id and html_id + "--" # type: ignore[attr-defined] + treeprocessors[ParagraphStrippingTreeprocessor.name].strip = strip_paragraph # type: ignore[attr-defined] + if BacklinksTreeProcessor.name in treeprocessors: + treeprocessors[BacklinksTreeProcessor.name].initial_id = html_id # type: ignore[attr-defined] + if autoref_hook and AutorefsInlineProcessor.name in self.md.inlinePatterns: + self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = autoref_hook # type: ignore[attr-defined] + + try: + return Markup(self.md.convert(text)) + finally: + treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = 0 # type: ignore[attr-defined] + treeprocessors[IdPrependingTreeprocessor.name].id_prefix = "" # type: ignore[attr-defined] + treeprocessors[ParagraphStrippingTreeprocessor.name].strip = False # type: ignore[attr-defined] + if BacklinksTreeProcessor.name in treeprocessors: + treeprocessors[BacklinksTreeProcessor.name].initial_id = None # type: ignore[attr-defined] + if AutorefsInlineProcessor.name in self.md.inlinePatterns: + self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = None # type: ignore[attr-defined] + self.md.reset() + _markdown_conversion_layer -= 1 +``` + +### do_heading + +```python +do_heading( + content: Markup, + heading_level: int, + *, + role: str | None = None, + hidden: bool = False, + toc_label: str | None = None, + skip_inventory: bool = False, + **attributes: str, +) -> Markup +``` + +Render an HTML heading and register it for the table of contents. For use inside templates. + +Parameters: + +- #### **`content`** + + (`Markup`) – The HTML within the heading. + +- #### **`heading_level`** + + (`int`) – The level of heading (e.g. 3 -> h3). + +- #### **`role`** + + (`str | None`, default: `None` ) – An optional role for the object bound to this heading. + +- #### **`hidden`** + + (`bool`, default: `False` ) – If True, only register it for the table of contents, don't render anything. + +- #### **`toc_label`** + + (`str | None`, default: `None` ) – The title to use in the table of contents ('data-toc-label' attribute). + +- #### **`skip_inventory`** + + (`bool`, default: `False` ) – Flag element to not be registered in the inventory (by setting a data-skip-inventory attribute). + +- #### **`**attributes`** + + (`str`, default: `{}` ) – Any extra HTML attributes of the heading. + +Returns: + +- `Markup` – An HTML string. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def do_heading( + self, + content: Markup, + heading_level: int, + *, + role: str | None = None, + hidden: bool = False, + toc_label: str | None = None, + skip_inventory: bool = False, + **attributes: str, +) -> Markup: + """Render an HTML heading and register it for the table of contents. For use inside templates. + + Arguments: + content: The HTML within the heading. + heading_level: The level of heading (e.g. 3 -> `h3`). + role: An optional role for the object bound to this heading. + hidden: If True, only register it for the table of contents, don't render anything. + toc_label: The title to use in the table of contents ('data-toc-label' attribute). + skip_inventory: Flag element to not be registered in the inventory (by setting a `data-skip-inventory` attribute). + **attributes: Any extra HTML attributes of the heading. + + Returns: + An HTML string. + """ + # Produce a heading element that will be used later, in `AutoDocProcessor.run`, to: + # - register it in the ToC: right now we're in the inner Markdown conversion layer, + # so we have to bubble up the information to the outer Markdown conversion layer, + # for the ToC extension to pick it up. + # - register it in autorefs: right now we don't know what page is being rendered, + # so we bubble up the information again to where autorefs knows the page, + # and can correctly register the heading anchor (id) to its full URL. + # - register it in the objects inventory: same as for autorefs, + # we don't know the page here, or the handler (and its domain), + # so we bubble up the information to where the mkdocstrings extension knows that. + el = Element(f"h{heading_level}", attributes) + if toc_label is None: + toc_label = content.unescape() if isinstance(content, Markup) else content + el.set("data-toc-label", toc_label) + if skip_inventory: + el.set("data-skip-inventory", "true") + if role: + el.set("data-role", role) + if content: + el.text = str(content).strip() + self._headings.append(el) + + if hidden: + return Markup('').format(attributes["id"]) + + # Now produce the actual HTML to be rendered. The goal is to wrap the HTML content into a heading. + # Start with a heading that has just attributes (no text), and add a placeholder into it. + el = Element(f"h{heading_level}", attributes) + el.append(Element("mkdocstrings-placeholder")) + # Tell the inner 'toc' extension to make its additions if configured so. + toc = cast("TocTreeprocessor", self.md.treeprocessors["toc"]) + if toc.use_anchors: + toc.add_anchor(el, attributes["id"]) + if toc.use_permalinks: + toc.add_permalink(el, attributes["id"]) + + # The content we received is HTML, so it can't just be inserted into the tree. We had marked the middle + # of the heading with a placeholder that can never occur (text can't directly contain angle brackets). + # Now this HTML wrapper can be "filled" by replacing the placeholder. + html_with_placeholder = tostring(el, encoding="unicode") + assert ( # noqa: S101 + html_with_placeholder.count("") == 1 + ), f"Bug in mkdocstrings: failed to replace in {html_with_placeholder!r}" + html = html_with_placeholder.replace("", content) + return Markup(html) +``` + +### get_aliases + +```python +get_aliases(identifier: str) -> tuple[str, ...] +``` + +Return the possible aliases for a given identifier. + +Parameters: + +- #### **`identifier`** + + (`str`) – The identifier to get the aliases of. + +Returns: + +- `tuple[str, ...]` – A tuple of strings - aliases. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_aliases(self, identifier: str) -> tuple[str, ...]: # noqa: ARG002 + """Return the possible aliases for a given identifier. + + Arguments: + identifier: The identifier to get the aliases of. + + Returns: + A tuple of strings - aliases. + """ + return () +``` + +### get_extended_templates_dirs + +```python +get_extended_templates_dirs(handler: str) -> list[Path] +``` + +Load template extensions for the given handler, return their templates directories. + +Parameters: + +- #### **`handler`** + + (`str`) – The name of the handler to get the extended templates directory of. + +Returns: + +- `list[Path]` – The extensions templates directories. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_extended_templates_dirs(self, handler: str) -> list[Path]: + """Load template extensions for the given handler, return their templates directories. + + Arguments: + handler: The name of the handler to get the extended templates directory of. + + Returns: + The extensions templates directories. + """ + discovered_extensions = entry_points(group=f"mkdocstrings.{handler}.templates") + return [extension.load()() for extension in discovered_extensions] +``` + +### get_headings + +```python +get_headings() -> Sequence[Element] +``` + +Return and clear the headings gathered so far. + +Returns: + +- `Sequence[Element]` – A list of HTML elements. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_headings(self) -> Sequence[Element]: + """Return and clear the headings gathered so far. + + Returns: + A list of HTML elements. + """ + result = list(self._headings) + self._headings.clear() + return result +``` + +### get_inventory_urls + +```python +get_inventory_urls() -> list[tuple[str, dict[str, Any]]] +``` + +Return the URLs (and configuration options) of the inventory files to download. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_inventory_urls(self) -> list[tuple[str, dict[str, Any]]]: + """Return the URLs (and configuration options) of the inventory files to download.""" + return [] +``` + +### get_options + +```python +get_options( + local_options: Mapping[str, Any], +) -> HandlerOptions +``` + +Get combined options. + +Override this method to customize how options are combined, for example by merging the global options with the local options. By combining options here, you don't have to do it twice in `collect` and `render`. + +Parameters: + +- #### **`local_options`** + + (`Mapping[str, Any]`) – The local options. + +Returns: + +- `HandlerOptions` – The combined options. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_options(self, local_options: Mapping[str, Any]) -> HandlerOptions: + """Get combined options. + + Override this method to customize how options are combined, + for example by merging the global options with the local options. + By combining options here, you don't have to do it twice in `collect` and `render`. + + Arguments: + local_options: The local options. + + Returns: + The combined options. + """ + return local_options +``` + +### get_templates_dir + +```python +get_templates_dir(handler: str | None = None) -> Path +``` + +Return the path to the handler's templates directory. + +Override to customize how the templates directory is found. + +Parameters: + +- #### **`handler`** + + (`str | None`, default: `None` ) – The name of the handler to get the templates directory of. + +Raises: + +- `ModuleNotFoundError` – When no such handler is installed. +- `FileNotFoundError` – When the templates directory cannot be found. + +Returns: + +- `Path` – The templates directory path. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_templates_dir(self, handler: str | None = None) -> Path: + """Return the path to the handler's templates directory. + + Override to customize how the templates directory is found. + + Arguments: + handler: The name of the handler to get the templates directory of. + + Raises: + ModuleNotFoundError: When no such handler is installed. + FileNotFoundError: When the templates directory cannot be found. + + Returns: + The templates directory path. + """ + handler = handler or self.name + try: + import mkdocstrings_handlers # noqa: PLC0415 + except ModuleNotFoundError as error: + raise ModuleNotFoundError(f"Handler '{handler}' not found, is it installed?") from error + + for path in mkdocstrings_handlers.__path__: + theme_path = Path(path, handler, "templates") + if theme_path.exists(): + return theme_path + + raise FileNotFoundError(f"Can't find 'templates' folder for handler '{handler}'") +``` + +### load_inventory + +```python +load_inventory( + in_file: BinaryIO, + url: str, + base_url: str | None = None, + **kwargs: Any, +) -> Iterator[tuple[str, str]] +``` + +Yield items and their URLs from an inventory file streamed from `in_file`. + +Parameters: + +- #### **`in_file`** + + (`BinaryIO`) – The binary file-like object to read the inventory from. + +- #### **`url`** + + (`str`) – The URL that this file is being streamed from (used to guess base_url). + +- #### **`base_url`** + + (`str | None`, default: `None` ) – The URL that this inventory's sub-paths are relative to. + +- #### **`**kwargs`** + + (`Any`, default: `{}` ) – Ignore additional arguments passed from the config. + +Yields: + +- `tuple[str, str]` – Tuples of (item identifier, item URL). + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +@classmethod +def load_inventory( + cls, + in_file: BinaryIO, # noqa: ARG003 + url: str, # noqa: ARG003 + base_url: str | None = None, # noqa: ARG003 + **kwargs: Any, # noqa: ARG003 +) -> Iterator[tuple[str, str]]: + """Yield items and their URLs from an inventory file streamed from `in_file`. + + 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). + """ + yield from () +``` + +### render + +```python +render( + data: CollectorItem, + options: HandlerOptions, + *, + locale: str | None = None, +) -> str +``` + +Render a template using provided data and configuration options. + +Parameters: + +- #### **`data`** + + (`CollectorItem`) – The collected data to render. + +- #### **`options`** + + (`HandlerOptions`) – The final configuration options. + +- #### **`locale`** + + (`str | None`, default: `None` ) – The locale to use for translations, if any. + +Returns: + +- `str` – The rendered template as HTML. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str: + """Render a template using provided data and configuration options. + + Arguments: + data: The collected data to render. + options: The final configuration options. + locale: The locale to use for translations, if any. + + Returns: + The rendered template as HTML. + """ + raise NotImplementedError +``` + +### render_backlinks + +```python +render_backlinks( + backlinks: Mapping[str, Iterable[Backlink]], + *, + locale: str | None = None, +) -> str +``` + +Render backlinks. + +Parameters: + +- #### **`backlinks`** + + (`Mapping[str, Iterable[Backlink]]`) – A mapping of identifiers to backlinks. + +- #### **`locale`** + + (`str | None`, default: `None` ) – The locale to use for translations, if any. + +Returns: + +- `str` – The rendered backlinks as HTML. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def render_backlinks(self, backlinks: Mapping[str, Iterable[Backlink]], *, locale: str | None = None) -> str: # noqa: ARG002 + """Render backlinks. + + Parameters: + backlinks: A mapping of identifiers to backlinks. + locale: The locale to use for translations, if any. + + Returns: + The rendered backlinks as HTML. + """ + return "" +``` + +### teardown + +```python +teardown() -> None +``` + +Teardown the handler. + +This method should be implemented to, for example, terminate a subprocess that was started when creating the handler instance. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def teardown(self) -> None: + """Teardown the handler. + + This method should be implemented to, for example, terminate a subprocess + that was started when creating the handler instance. + """ +``` + +### update_env + +```python +update_env(config: Any) -> None +``` + +Update the Jinja environment. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def update_env(self, config: Any) -> None: + """Update the Jinja environment.""" +``` + +## CollectionError + +Bases: `Exception` + +An exception raised when some collection of data failed. + +## Handlers + +```python +Handlers( + *, + theme: str, + default: str, + inventory_project: str, + inventory_version: str = "0.0.0", + handlers_config: dict[str, HandlerConfig] | None = None, + custom_templates: str | None = None, + mdx: Sequence[str | Extension] | None = None, + mdx_config: Mapping[str, Any] | None = None, + locale: str = "en", + tool_config: Any, +) +``` + +A collection of handlers. + +Do not instantiate this directly. The plugin will keep one instance of this for the purpose of caching. Use mkdocstrings.MkdocstringsPlugin.get_handler for convenient access. + +Parameters: + +- ### **`theme`** + + (`str`) – The theme to use. + +- ### **`default`** + + (`str`) – The default handler to use. + +- ### **`inventory_project`** + + (`str`) – The project name to use in the inventory. + +- ### **`inventory_version`** + + (`str`, default: `'0.0.0'` ) – The project version to use in the inventory. + +- ### **`handlers_config`** + + (`dict[str, HandlerConfig] | None`, default: `None` ) – The handlers configuration. + +- ### **`custom_templates`** + + (`str | None`, default: `None` ) – The path to custom templates. + +- ### **`mdx`** + + (`Sequence[str | Extension] | None`, default: `None` ) – A list of Markdown extensions to use. + +- ### **`mdx_config`** + + (`Mapping[str, Any] | None`, default: `None` ) – Configuration for the Markdown extensions. + +- ### **`locale`** + + (`str`, default: `'en'` ) – The locale to use for translations. + +- ### **`tool_config`** + + (`Any`) – Tool configuration to pass down to handlers. + +Methods: + +- **`get_handler`** – Get a handler thanks to its name. +- **`get_handler_config`** – Return the global configuration of the given handler. +- **`get_handler_name`** – Return the handler name defined in an "autodoc" instruction YAML configuration, or the global default handler. +- **`teardown`** – Teardown all cached handlers and clear the cache. + +Attributes: + +- **`inventory`** (`Inventory`) – The objects inventory. +- **`seen_handlers`** (`Iterable[BaseHandler]`) – Get the handlers that were encountered so far throughout the build. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def __init__( + self, + *, + theme: str, + default: str, + inventory_project: str, + inventory_version: str = "0.0.0", + handlers_config: dict[str, HandlerConfig] | None = None, + custom_templates: str | None = None, + mdx: Sequence[str | Extension] | None = None, + mdx_config: Mapping[str, Any] | None = None, + locale: str = "en", + tool_config: Any, +) -> None: + """Initialize the object. + + Arguments: + theme: The theme to use. + default: The default handler to use. + inventory_project: The project name to use in the inventory. + inventory_version: The project version to use in the inventory. + handlers_config: The handlers configuration. + custom_templates: The path to custom templates. + mdx: A list of Markdown extensions to use. + mdx_config: Configuration for the Markdown extensions. + locale: The locale to use for translations. + tool_config: Tool configuration to pass down to handlers. + """ + self._theme = theme + self._default = default + self._handlers_config = handlers_config or {} + self._custom_templates = custom_templates + self._mdx = mdx or [] + self._mdx_config = mdx_config or {} + self._handlers: dict[str, BaseHandler] = {} + self._locale = locale + self._tool_config = tool_config + + self.inventory: Inventory = Inventory(project=inventory_project, version=inventory_version) + """The objects inventory.""" + + self._inv_futures: dict[futures.Future, tuple[BaseHandler, str, Any]] = {} +``` + +### inventory + +```python +inventory: Inventory = Inventory( + project=inventory_project, version=inventory_version +) +``` + +The objects inventory. + +### seen_handlers + +```python +seen_handlers: Iterable[BaseHandler] +``` + +Get the handlers that were encountered so far throughout the build. + +Returns: + +- `Iterable[BaseHandler]` – An iterable of instances of BaseHandler +- `Iterable[BaseHandler]` – (usable only to loop through it). + +### get_handler + +```python +get_handler( + name: str, handler_config: dict | None = None +) -> BaseHandler +``` + +Get a handler thanks to its name. + +This function dynamically imports a module named "mkdocstrings.handlers.NAME", calls its `get_handler` method to get an instance of a handler, and caches it in dictionary. It means that during one run (for each reload when serving, or once when building), a handler is instantiated only once, and reused for each "autodoc" instruction asking for it. + +Parameters: + +- #### **`name`** + + (`str`) – The name of the handler. Really, it's the name of the Python module holding it. + +- #### **`handler_config`** + + (`dict | None`, default: `None` ) – Configuration passed to the handler. + +Returns: + +- `BaseHandler` – An instance of a subclass of BaseHandler, as instantiated by the get_handler method of the handler's module. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_handler(self, name: str, handler_config: dict | None = None) -> BaseHandler: + """Get a handler thanks to its name. + + This function dynamically imports a module named "mkdocstrings.handlers.NAME", calls its + `get_handler` method to get an instance of a handler, and caches it in dictionary. + It means that during one run (for each reload when serving, or once when building), + a handler is instantiated only once, and reused for each "autodoc" instruction asking for it. + + Arguments: + name: The name of the handler. Really, it's the name of the Python module holding it. + handler_config: Configuration passed to the handler. + + Returns: + An instance of a subclass of [`BaseHandler`][mkdocstrings.BaseHandler], + as instantiated by the `get_handler` method of the handler's module. + """ + if name not in self._handlers: + if handler_config is None: + handler_config = self._handlers_config.get(name, {}) + module = importlib.import_module(f"mkdocstrings_handlers.{name}") + + self._handlers[name] = module.get_handler( + theme=self._theme, + custom_templates=self._custom_templates, + mdx=self._mdx, + mdx_config=self._mdx_config, + handler_config=handler_config, + tool_config=self._tool_config, + ) + return self._handlers[name] +``` + +### get_handler_config + +```python +get_handler_config(name: str) -> dict +``` + +Return the global configuration of the given handler. + +Parameters: + +- #### **`name`** + + (`str`) – The name of the handler to get the global configuration of. + +Returns: + +- `dict` – The global configuration of the given handler. It can be an empty dictionary. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_handler_config(self, name: str) -> dict: + """Return the global configuration of the given handler. + + Arguments: + name: The name of the handler to get the global configuration of. + + Returns: + The global configuration of the given handler. It can be an empty dictionary. + """ + return self._handlers_config.get(name, None) or {} +``` + +### get_handler_name + +```python +get_handler_name(config: dict) -> str +``` + +Return the handler name defined in an "autodoc" instruction YAML configuration, or the global default handler. + +Parameters: + +- #### **`config`** + + (`dict`) – A configuration dictionary, obtained from YAML below the "autodoc" instruction. + +Returns: + +- `str` – The name of the handler to use. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_handler_name(self, config: dict) -> str: + """Return the handler name defined in an "autodoc" instruction YAML configuration, or the global default handler. + + Arguments: + config: A configuration dictionary, obtained from YAML below the "autodoc" instruction. + + Returns: + The name of the handler to use. + """ + return config.get("handler", self._default) +``` + +### teardown + +```python +teardown() -> None +``` + +Teardown all cached handlers and clear the cache. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def teardown(self) -> None: + """Teardown all cached handlers and clear the cache.""" + for future in self._inv_futures: + future.cancel() + for handler in self.seen_handlers: + handler.teardown() + self._handlers.clear() +``` + +## HeadingShiftingTreeprocessor + +```python +HeadingShiftingTreeprocessor(md: Markdown, shift_by: int) +``` + +Bases: `Treeprocessor` + +Shift levels of all Markdown headings according to the configured base level. + +Parameters: + +- ### **`md`** + + (`Markdown`) – A markdown.Markdown instance. + +- ### **`shift_by`** + + (`int`) – The number of heading "levels" to add to every heading. + +Methods: + +- **`run`** – Shift the levels of all headings in the document. + +Attributes: + +- **`name`** (`str`) – The name of the treeprocessor. +- **`regex`** (`Pattern`) – The regex to match heading tags. +- **`shift_by`** (`int`) – The number of heading "levels" to add to every heading.

with shift_by = 3 becomes

. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def __init__(self, md: Markdown, shift_by: int): + """Initialize the object. + + Arguments: + md: A `markdown.Markdown` instance. + shift_by: The number of heading "levels" to add to every heading. + """ + super().__init__(md) + self.shift_by = shift_by +``` + +### name + +```python +name: str = 'mkdocstrings_headings' +``` + +The name of the treeprocessor. + +### regex + +```python +regex: Pattern = compile('([Hh])([1-6])') +``` + +The regex to match heading tags. + +### shift_by + +```python +shift_by: int = shift_by +``` + +The number of heading "levels" to add to every heading. `

` with `shift_by = 3` becomes `

`. + +### run + +```python +run(root: Element) -> None +``` + +Shift the levels of all headings in the document. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def run(self, root: Element) -> None: + """Shift the levels of all headings in the document.""" + if not self.shift_by: + return + for el in root.iter(): + match = self.regex.fullmatch(el.tag) + if match: + level = int(match[2]) + self.shift_by + level = max(1, min(level, 6)) + el.tag = f"{match[1]}{level}" +``` + +## Highlighter + +```python +Highlighter(md: Markdown) +``` + +Bases: `Highlight` + +Code highlighter that tries to match the Markdown configuration. + +Picking up the global config and defaults works only if you use the `codehilite` or `pymdownx.highlight` (recommended) Markdown extension. + +- If you use `pymdownx.highlight`, highlighting settings are picked up from it, and the default CSS class is `.highlight`. This also means the default of `guess_lang: false`. +- Otherwise, if you use the `codehilite` extension, settings are picked up from it, and the default CSS class is `.codehilite`. Also consider setting `guess_lang: false`. +- If neither are added to `markdown_extensions`, highlighting is enabled anyway. This is for backwards compatibility. If you really want to disable highlighting even in *mkdocstrings*, add one of these extensions anyway and set `use_pygments: false`. + +The underlying implementation is `pymdownx.highlight` regardless. + +Parameters: + +- ### **`md`** + + (`Markdown`) – The Markdown instance to read configs from. + +Methods: + +- **`highlight`** – Highlight a code-snippet. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def __init__(self, md: Markdown): + """Configure to match a `markdown.Markdown` instance. + + Arguments: + md: The Markdown instance to read configs from. + """ + config: dict[str, Any] = {} + self._highlighter: str | None = None + for ext in md.registeredExtensions: + if isinstance(ext, HighlightExtension) and (ext.enabled or not config): + self._highlighter = "highlight" + config = ext.getConfigs() + break # This one takes priority, no need to continue looking + if isinstance(ext, CodeHiliteExtension) and not config: + self._highlighter = "codehilite" + config = ext.getConfigs() + config["language_prefix"] = config["lang_prefix"] + self._css_class = config.pop("css_class", "highlight") + super().__init__(**{name: opt for name, opt in config.items() if name in self._highlight_config_keys}) +``` + +### highlight + +```python +highlight( + src: str, + language: str | None = None, + *, + inline: bool = False, + dedent: bool = True, + linenums: bool | None = None, + **kwargs: Any, +) -> str +``` + +Highlight a code-snippet. + +Parameters: + +- #### **`src`** + + (`str`) – The code to highlight. + +- #### **`language`** + + (`str | None`, default: `None` ) – Explicitly tell what language to use for highlighting. + +- #### **`inline`** + + (`bool`, default: `False` ) – Whether to highlight as inline. + +- #### **`dedent`** + + (`bool`, default: `True` ) – Whether to dedent the code before highlighting it or not. + +- #### **`linenums`** + + (`bool | None`, default: `None` ) – Whether to add line numbers in the result. + +- #### **`**kwargs`** + + (`Any`, default: `{}` ) – Pass on to pymdownx.highlight.Highlight.highlight. + +Returns: + +- `str` – The highlighted code as HTML text, marked safe (not escaped for HTML). + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def highlight( + self, + src: str, + language: str | None = None, + *, + inline: bool = False, + dedent: bool = True, + linenums: bool | None = None, + **kwargs: Any, +) -> str: + """Highlight a code-snippet. + + Arguments: + src: The code to highlight. + language: Explicitly tell what language to use for highlighting. + inline: Whether to highlight as inline. + dedent: Whether to dedent the code before highlighting it or not. + linenums: Whether to add line numbers in the result. + **kwargs: Pass on to `pymdownx.highlight.Highlight.highlight`. + + Returns: + The highlighted code as HTML text, marked safe (not escaped for HTML). + """ + if isinstance(src, Markup): + src = src.unescape() + if dedent: + src = textwrap.dedent(src) + + kwargs.setdefault("css_class", self._css_class) + old_linenums = self.linenums # type: ignore[has-type] + if linenums is not None: + self.linenums = linenums + try: + result = super().highlight(src, language, inline=inline, **kwargs) + finally: + self.linenums = old_linenums + + if inline: + # From the maintainer of codehilite, the codehilite CSS class, as defined by the user, + # should never be added to inline code, because codehilite does not support inline code. + # See https://github.com/Python-Markdown/markdown/issues/1220#issuecomment-1692160297. + css_class = "" if self._highlighter == "codehilite" else kwargs["css_class"] + return Markup(f'{result.text}') + return Markup(result) +``` + +## IdPrependingTreeprocessor + +```python +IdPrependingTreeprocessor(md: Markdown, id_prefix: str) +``` + +Bases: `Treeprocessor` + +Prepend the configured prefix to IDs of all HTML elements. + +Parameters: + +- ### **`md`** + + (`Markdown`) – A markdown.Markdown instance. + +- ### **`id_prefix`** + + (`str`) – The prefix to add to every ID. It is prepended without any separator. + +Methods: + +- **`run`** – Prepend the configured prefix to all IDs in the document. + +Attributes: + +- **`id_prefix`** (`str`) – The prefix to add to every ID. It is prepended without any separator; specify your own separator if needed. +- **`name`** (`str`) – The name of the treeprocessor. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def __init__(self, md: Markdown, id_prefix: str): + """Initialize the object. + + Arguments: + md: A `markdown.Markdown` instance. + id_prefix: The prefix to add to every ID. It is prepended without any separator. + """ + super().__init__(md) + self.id_prefix = id_prefix +``` + +### id_prefix + +```python +id_prefix: str = id_prefix +``` + +The prefix to add to every ID. It is prepended without any separator; specify your own separator if needed. + +### name + +```python +name: str = 'mkdocstrings_ids' +``` + +The name of the treeprocessor. + +### run + +```python +run(root: Element) -> None +``` + +Prepend the configured prefix to all IDs in the document. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def run(self, root: Element) -> None: + """Prepend the configured prefix to all IDs in the document.""" + if self.id_prefix: + self._prefix_ids(root) +``` + +## Inventory + +```python +Inventory( + items: list[InventoryItem] | None = None, + project: str = "project", + version: str = "0.0.0", +) +``` + +Bases: `dict` + +Inventory of collected and rendered objects. + +Parameters: + +- ### **`items`** + + (`list[InventoryItem] | None`, default: `None` ) – A list of items. + +- ### **`project`** + + (`str`, default: `'project'` ) – The project name. + +- ### **`version`** + + (`str`, default: `'0.0.0'` ) – The project version. + +Methods: + +- **`format_sphinx`** – Format this inventory as a Sphinx objects.inv file. +- **`parse_sphinx`** – Parse a Sphinx v2 inventory file and return an Inventory from it. +- **`register`** – Create and register an item. + +Attributes: + +- **`project`** – The project name. +- **`version`** – The project version. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +def __init__(self, items: list[InventoryItem] | None = None, project: str = "project", version: str = "0.0.0"): + """Initialize the object. + + Arguments: + items: A list of items. + project: The project name. + version: The project version. + """ + super().__init__() + items = items or [] + for item in items: + self[item.name] = item + self.project = project + """The project name.""" + self.version = version + """The project version.""" +``` + +### project + +```python +project = project +``` + +The project name. + +### version + +```python +version = version +``` + +The project version. + +### format_sphinx + +```python +format_sphinx() -> bytes +``` + +Format this inventory as a Sphinx `objects.inv` file. + +Returns: + +- `bytes` – The inventory as bytes. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +def format_sphinx(self) -> bytes: + """Format this inventory as a Sphinx `objects.inv` file. + + Returns: + The inventory as bytes. + """ + header = ( + dedent( + f""" + # Sphinx inventory version 2 + # Project: {self.project} + # Version: {self.version} + # The remainder of this file is compressed using zlib. + """, + ) + .lstrip() + .encode("utf8") + ) + + lines = [ + item.format_sphinx().encode("utf8") + for item in sorted(self.values(), key=lambda item: (item.domain, item.name)) + ] + return header + zlib.compress(b"\n".join(lines) + b"\n", 9) +``` + +### parse_sphinx + +```python +parse_sphinx( + in_file: BinaryIO, + *, + domain_filter: Collection[str] = (), +) -> Inventory +``` + +Parse a Sphinx v2 inventory file and return an `Inventory` from it. + +Parameters: + +- #### **`in_file`** + + (`BinaryIO`) – The binary file-like object to read from. + +- #### **`domain_filter`** + + (`Collection[str]`, default: `()` ) – A collection of domain values to allow (and filter out all other ones). + +Returns: + +- `Inventory` – An inventory containing the collected items. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +@classmethod +def parse_sphinx(cls, in_file: BinaryIO, *, domain_filter: Collection[str] = ()) -> Inventory: + """Parse a Sphinx v2 inventory file and return an `Inventory` from it. + + Arguments: + in_file: The binary file-like object to read from. + domain_filter: A collection of domain values to allow (and filter out all other ones). + + Returns: + An inventory containing the collected items. + """ + for _ in range(4): + in_file.readline() + lines = zlib.decompress(in_file.read()).splitlines() + items: list[InventoryItem] = [ + item for line in lines if (item := InventoryItem.parse_sphinx(line.decode("utf8"), return_none=True)) + ] + if domain_filter: + items = [item for item in items if item.domain in domain_filter] + return cls(items) +``` + +### register + +```python +register( + name: str, + domain: str, + role: str, + uri: str, + priority: int = 1, + dispname: str | None = None, +) -> None +``` + +Create and register an item. + +Parameters: + +- #### **`name`** + + (`str`) – The item name. + +- #### **`domain`** + + (`str`) – The item domain, like 'python' or 'crystal'. + +- #### **`role`** + + (`str`) – The item role, like 'class' or 'method'. + +- #### **`uri`** + + (`str`) – The item URI. + +- #### **`priority`** + + (`int`, default: `1` ) – The item priority. Only used internally by mkdocstrings and Sphinx. + +- #### **`dispname`** + + (`str | None`, default: `None` ) – The item display name. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +def register( + self, + name: str, + domain: str, + role: str, + uri: str, + priority: int = 1, + dispname: str | None = None, +) -> None: + """Create and register an item. + + Arguments: + name: The item name. + domain: The item domain, like 'python' or 'crystal'. + role: The item role, like 'class' or 'method'. + uri: The item URI. + priority: The item priority. Only used internally by mkdocstrings and Sphinx. + dispname: The item display name. + """ + self[name] = InventoryItem( + name=name, + domain=domain, + role=role, + uri=uri, + priority=priority, + dispname=dispname, + ) +``` + +## InventoryItem + +```python +InventoryItem( + name: str, + domain: str, + role: str, + uri: str, + priority: int = 1, + dispname: str | None = None, +) +``` + +Inventory item. + +Parameters: + +- ### **`name`** + + (`str`) – The item name. + +- ### **`domain`** + + (`str`) – The item domain, like 'python' or 'crystal'. + +- ### **`role`** + + (`str`) – The item role, like 'class' or 'method'. + +- ### **`uri`** + + (`str`) – The item URI. + +- ### **`priority`** + + (`int`, default: `1` ) – The item priority. Only used internally by mkdocstrings and Sphinx. + +- ### **`dispname`** + + (`str | None`, default: `None` ) – The item display name. + +Methods: + +- **`format_sphinx`** – Format this item as a Sphinx inventory line. +- **`parse_sphinx`** – Parse a line from a Sphinx v2 inventory file and return an InventoryItem from it. + +Attributes: + +- **`dispname`** (`str`) – The item display name. +- **`domain`** (`str`) – The item domain. +- **`name`** (`str`) – The item name. +- **`priority`** (`int`) – The item priority. +- **`role`** (`str`) – The item role. +- **`sphinx_item_regex`** – Regex to parse a Sphinx v2 inventory line. +- **`uri`** (`str`) – The item URI. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +def __init__( + self, + name: str, + domain: str, + role: str, + uri: str, + priority: int = 1, + dispname: str | None = None, +): + """Initialize the object. + + Arguments: + name: The item name. + domain: The item domain, like 'python' or 'crystal'. + role: The item role, like 'class' or 'method'. + uri: The item URI. + priority: The item priority. Only used internally by mkdocstrings and Sphinx. + dispname: The item display name. + """ + self.name: str = name + """The item name.""" + self.domain: str = domain + """The item domain.""" + self.role: str = role + """The item role.""" + self.uri: str = uri + """The item URI.""" + self.priority: int = priority + """The item priority.""" + self.dispname: str = dispname or name + """The item display name.""" +``` + +### dispname + +```python +dispname: str = dispname or name +``` + +The item display name. + +### domain + +```python +domain: str = domain +``` + +The item domain. + +### name + +```python +name: str = name +``` + +The item name. + +### priority + +```python +priority: int = priority +``` + +The item priority. + +### role + +```python +role: str = role +``` + +The item role. + +### sphinx_item_regex + +```python +sphinx_item_regex = compile( + "^(.+?)\\s+(\\S+):(\\S+)\\s+(-?\\d+)\\s+(\\S+)\\s*(.*)$" +) +``` + +Regex to parse a Sphinx v2 inventory line. + +### uri + +```python +uri: str = uri +``` + +The item URI. + +### format_sphinx + +```python +format_sphinx() -> str +``` + +Format this item as a Sphinx inventory line. + +Returns: + +- `str` – A line formatted for an objects.inv file. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +def format_sphinx(self) -> str: + """Format this item as a Sphinx inventory line. + + Returns: + A line formatted for an `objects.inv` file. + """ + dispname = self.dispname + if dispname == self.name: + dispname = "-" + uri = self.uri + if uri.endswith(self.name): + uri = uri[: -len(self.name)] + "$" + return f"{self.name} {self.domain}:{self.role} {self.priority} {uri} {dispname}" +``` + +### parse_sphinx + +```python +parse_sphinx( + line: str, *, return_none: Literal[False] +) -> InventoryItem +``` + +```python +parse_sphinx( + line: str, *, return_none: Literal[True] +) -> InventoryItem | None +``` + +```python +parse_sphinx( + line: str, *, return_none: bool = False +) -> InventoryItem | None +``` + +Parse a line from a Sphinx v2 inventory file and return an `InventoryItem` from it. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +@classmethod +def parse_sphinx(cls, line: str, *, return_none: bool = False) -> InventoryItem | None: + """Parse a line from a Sphinx v2 inventory file and return an `InventoryItem` from it.""" + match = cls.sphinx_item_regex.search(line) + if not match: + if return_none: + return None + raise ValueError(line) + name, domain, role, priority, uri, dispname = match.groups() + if uri.endswith("$"): + uri = uri[:-1] + name + if dispname == "-": + dispname = name + return cls(name, domain, role, uri, int(priority), dispname) +``` + +## LoggerAdapter + +```python +LoggerAdapter(prefix: str, logger: Logger) +``` + +Bases: `LoggerAdapter` + +A logger adapter to prefix messages. + +This adapter also adds an additional parameter to logging methods called `once`: if `True`, the message will only be logged once. + +Examples: + +In Python code: + +```pycon +>>> logger = get_logger("myplugin") +>>> logger.debug("This is a debug message.") +>>> logger.info("This is an info message.", once=True) +``` + +In Jinja templates (logger available in context as `log`): + +```jinja +{{ log.debug("This is a debug message.") }} +{{ log.info("This is an info message.", once=True) }} +``` + +Parameters: + +- ### **`prefix`** + + (`str`) – The string to insert in front of every message. + +- ### **`logger`** + + (`Logger`) – The logger instance. + +Methods: + +- **`log`** – Log a message. +- **`process`** – Process the message. + +Attributes: + +- **`prefix`** – The prefix to insert in front of every message. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def __init__(self, prefix: str, logger: logging.Logger): + """Initialize the object. + + Arguments: + prefix: The string to insert in front of every message. + logger: The logger instance. + """ + super().__init__(logger, {}) + self.prefix = prefix + """The prefix to insert in front of every message.""" + self._logged: set[tuple[LoggerAdapter, str]] = set() +``` + +### prefix + +```python +prefix = prefix +``` + +The prefix to insert in front of every message. + +### log + +```python +log( + level: int, msg: object, *args: object, **kwargs: object +) -> None +``` + +Log a message. + +Parameters: + +- #### **`level`** + + (`int`) – The logging level. + +- #### **`msg`** + + (`object`) – The message. + +- #### **`*args`** + + (`object`, default: `()` ) – Additional arguments passed to parent method. + +- #### **`**kwargs`** + + (`object`, default: `{}` ) – Additional keyword arguments passed to parent method. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def log(self, level: int, msg: object, *args: object, **kwargs: object) -> None: + """Log a message. + + Arguments: + level: The logging level. + msg: The message. + *args: Additional arguments passed to parent method. + **kwargs: Additional keyword arguments passed to parent method. + """ + if kwargs.pop("once", False): + if (key := (self, str(msg))) in self._logged: + return + self._logged.add(key) + super().log(level, msg, *args, **kwargs) # type: ignore[arg-type] +``` + +### process + +```python +process( + msg: str, kwargs: MutableMapping[str, Any] +) -> tuple[str, Any] +``` + +Process the message. + +Parameters: + +- #### **`msg`** + + (`str`) – The message: + +- #### **`kwargs`** + + (`MutableMapping[str, Any]`) – Remaining arguments. + +Returns: + +- `tuple[str, Any]` – The processed message. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def process(self, msg: str, kwargs: MutableMapping[str, Any]) -> tuple[str, Any]: + """Process the message. + + Arguments: + msg: The message: + kwargs: Remaining arguments. + + Returns: + The processed message. + """ + return f"{self.prefix}: {msg}", kwargs +``` + +## MkdocstringsExtension + +```python +MkdocstringsExtension( + handlers: Handlers, + autorefs: AutorefsPlugin, + **kwargs: Any, +) +``` + +Bases: `Extension` + +Our Markdown extension. + +It cannot work outside of `mkdocstrings`. + +Parameters: + +- ### **`handlers`** + + (`Handlers`) – The handlers container. + +- ### **`autorefs`** + + (`AutorefsPlugin`) – The autorefs plugin instance. + +- ### **`**kwargs`** + + (`Any`, default: `{}` ) – Keyword arguments used by markdown.extensions.Extension. + +Methods: + +- **`extendMarkdown`** – Register the extension. + +Source code in `src/mkdocstrings/_internal/extension.py` + +```python +def __init__(self, handlers: Handlers, autorefs: AutorefsPlugin, **kwargs: Any) -> None: + """Initialize the object. + + Arguments: + handlers: The handlers container. + autorefs: The autorefs plugin instance. + **kwargs: Keyword arguments used by `markdown.extensions.Extension`. + """ + super().__init__(**kwargs) + self._handlers = handlers + self._autorefs = autorefs +``` + +### extendMarkdown + +```python +extendMarkdown(md: Markdown) -> None +``` + +Register the extension. + +Add an instance of our AutoDocProcessor to the Markdown parser. + +Parameters: + +- #### **`md`** + + (`Markdown`) – A markdown.Markdown instance. + +Source code in `src/mkdocstrings/_internal/extension.py` + +```python +def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent method's name) + """Register the extension. + + Add an instance of our [`AutoDocProcessor`][mkdocstrings.AutoDocProcessor] to the Markdown parser. + + Arguments: + md: A `markdown.Markdown` instance. + """ + md.parser.blockprocessors.register( + AutoDocProcessor(md, handlers=self._handlers, autorefs=self._autorefs), + "mkdocstrings", + priority=75, # Right before markdown.blockprocessors.HashHeaderProcessor + ) + md.treeprocessors.register( + _HeadingsPostProcessor(md), + "mkdocstrings_post_headings", + priority=4, # Right after 'toc'. + ) + md.treeprocessors.register( + _TocLabelsTreeProcessor(md), + "mkdocstrings_post_toc_labels", + priority=4, # Right after 'toc'. + ) +``` + +## MkdocstringsInnerExtension + +```python +MkdocstringsInnerExtension(headings: list[Element]) +``` + +Bases: `Extension` + +Extension that should always be added to Markdown sub-documents that handlers request (and *only* them). + +Parameters: + +- ### **`headings`** + + (`list[Element]`) – A list that will be populated with all HTML heading elements encountered in the document. + +Methods: + +- **`extendMarkdown`** – Register the extension. + +Attributes: + +- **`headings`** – The list that will be populated with all HTML heading elements encountered in the document. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def __init__(self, headings: list[Element]): + """Initialize the object. + + Arguments: + headings: A list that will be populated with all HTML heading elements encountered in the document. + """ + super().__init__() + self.headings = headings + """The list that will be populated with all HTML heading elements encountered in the document.""" +``` + +### headings + +```python +headings = headings +``` + +The list that will be populated with all HTML heading elements encountered in the document. + +### extendMarkdown + +```python +extendMarkdown(md: Markdown) -> None +``` + +Register the extension. + +Parameters: + +- #### **`md`** + + (`Markdown`) – A markdown.Markdown instance. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent method's name) + """Register the extension. + + Arguments: + md: A `markdown.Markdown` instance. + """ + md.registerExtension(self) + md.treeprocessors.register( + HeadingShiftingTreeprocessor(md, 0), + HeadingShiftingTreeprocessor.name, + priority=12, + ) + md.treeprocessors.register( + IdPrependingTreeprocessor(md, ""), + IdPrependingTreeprocessor.name, + priority=4, # Right after 'toc' (needed because that extension adds ids to headers). + ) + md.treeprocessors.register( + _HeadingReportingTreeprocessor(md, self.headings), + _HeadingReportingTreeprocessor.name, + priority=1, # Close to the end. + ) + md.treeprocessors.register( + ParagraphStrippingTreeprocessor(md), + ParagraphStrippingTreeprocessor.name, + priority=0.99, # Close to the end. + ) +``` + +## MkdocstringsPlugin + +```python +MkdocstringsPlugin() +``` + +Bases: `BasePlugin[PluginConfig]` + +An `mkdocs` plugin. + +This plugin defines the following event hooks: + +- `on_config` +- `on_env` +- `on_post_build` + +Check the [Developing Plugins](https://www.mkdocs.org/user-guide/plugins/#developing-plugins) page of `mkdocs` for more information about its plugin system. + +Methods: + +- **`get_handler`** – Get a handler by its name. See mkdocstrings.Handlers.get_handler. +- **`on_config`** – Instantiate our Markdown extension. +- **`on_post_build`** – Teardown the handlers. + +Attributes: + +- **`css_filename`** (`str`) – The path of the CSS file to write in the site directory. +- **`handlers`** (`Handlers`) – Get the instance of mkdocstrings.Handlers for this plugin/build. +- **`inventory_enabled`** (`bool`) – Tell if the inventory is enabled or not. +- **`on_env`** – Extra actions that need to happen after all Markdown-to-HTML page rendering. +- **`plugin_enabled`** (`bool`) – Tell if the plugin is enabled or not. + +Source code in `src/mkdocstrings/_internal/plugin.py` + +```python +def __init__(self) -> None: + """Initialize the object.""" + super().__init__() + self._handlers: Handlers | None = None +``` + +### css_filename + +```python +css_filename: str = 'assets/_mkdocstrings.css' +``` + +The path of the CSS file to write in the site directory. + +### handlers + +```python +handlers: Handlers +``` + +Get the instance of mkdocstrings.Handlers for this plugin/build. + +Raises: + +- `RuntimeError` – If the plugin hasn't been initialized with a config. + +Returns: + +- `Handlers` – An instance of mkdocstrings.Handlers (the same throughout the build). + +### inventory_enabled + +```python +inventory_enabled: bool +``` + +Tell if the inventory is enabled or not. + +Returns: + +- `bool` – Whether the inventory is enabled. + +### on_env + +```python +on_env = CombinedEvent( + _on_env_load_inventories, + _on_env_add_css, + _on_env_write_inventory, + _on_env_apply_backlinks, +) +``` + +Extra actions that need to happen after all Markdown-to-HTML page rendering. + +Hook for the [`on_env` event](https://www.mkdocs.org/user-guide/plugins/#on_env). + +- Gather results from background inventory download tasks. +- Write mkdocstrings' extra files (CSS, inventory) into the site directory. +- Apply backlinks to the HTML output of each page. + +### plugin_enabled + +```python +plugin_enabled: bool +``` + +Tell if the plugin is enabled or not. + +Returns: + +- `bool` – Whether the plugin is enabled. + +### get_handler + +```python +get_handler(handler_name: str) -> BaseHandler +``` + +Get a handler by its name. See mkdocstrings.Handlers.get_handler. + +Parameters: + +- #### **`handler_name`** + + (`str`) – The name of the handler. + +Returns: + +- `BaseHandler` – An instance of a subclass of BaseHandler. + +Source code in `src/mkdocstrings/_internal/plugin.py` + +```python +def get_handler(self, handler_name: str) -> BaseHandler: + """Get a handler by its name. See [mkdocstrings.Handlers.get_handler][]. + + Arguments: + handler_name: The name of the handler. + + Returns: + An instance of a subclass of [`BaseHandler`][mkdocstrings.BaseHandler]. + """ + return self.handlers.get_handler(handler_name) +``` + +### on_config + +```python +on_config(config: MkDocsConfig) -> MkDocsConfig | None +``` + +Instantiate our Markdown extension. + +Hook for the [`on_config` event](https://www.mkdocs.org/user-guide/plugins/#on_config). In this hook, we instantiate our MkdocstringsExtension and add it to the list of Markdown extensions used by `mkdocs`. + +We pass this plugin's configuration dictionary to the extension when instantiating it (it will need it later when processing markdown to get handlers and their global configurations). + +Parameters: + +- #### **`config`** + + (`MkDocsConfig`) – The MkDocs config object. + +Returns: + +- `MkDocsConfig | None` – The modified config. + +Source code in `src/mkdocstrings/_internal/plugin.py` + +```python +def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: + """Instantiate our Markdown extension. + + Hook for the [`on_config` event](https://www.mkdocs.org/user-guide/plugins/#on_config). + In this hook, we instantiate our [`MkdocstringsExtension`][mkdocstrings.MkdocstringsExtension] + and add it to the list of Markdown extensions used by `mkdocs`. + + We pass this plugin's configuration dictionary to the extension when instantiating it (it will need it + later when processing markdown to get handlers and their global configurations). + + Arguments: + config: The MkDocs config object. + + Returns: + The modified config. + """ + if not self.plugin_enabled: + _logger.debug("Plugin is not enabled. Skipping.") + return config + _logger.debug("Adding extension to the list") + + locale = self.config.locale or config.theme.get("language") or config.theme.get("locale") or "en" + locale = str(locale).replace("_", "-") + + handlers = Handlers( + default=self.config.default_handler, + handlers_config=self.config.handlers, + theme=config.theme.name or os.path.dirname(config.theme.dirs[0]), + custom_templates=self.config.custom_templates, + mdx=config.markdown_extensions, + mdx_config=config.mdx_configs, + inventory_project=config.site_name, + inventory_version="0.0.0", # TODO: Find a way to get actual version. + locale=locale, + tool_config=config, + ) + + handlers._download_inventories() + + AutorefsPlugin.record_backlinks = True + autorefs: AutorefsPlugin + try: + # If autorefs plugin is explicitly enabled, just use it. + autorefs = config.plugins["autorefs"] # type: ignore[assignment] + _logger.debug("Picked up existing autorefs instance %r", autorefs) + except KeyError: + # Otherwise, add a limited instance of it that acts only on what's added through `register_anchor`. + autorefs = AutorefsPlugin() + autorefs.config = AutorefsConfig() + autorefs.scan_toc = False + config.plugins["autorefs"] = autorefs + _logger.debug("Added a subdued autorefs instance %r", autorefs) + + mkdocstrings_extension = MkdocstringsExtension(handlers, autorefs) + config.markdown_extensions.append(mkdocstrings_extension) # type: ignore[arg-type] + + config.extra_css.insert(0, self.css_filename) # So that it has lower priority than user files. + + self._autorefs = autorefs + self._handlers = handlers + return config +``` + +### on_post_build + +```python +on_post_build(config: MkDocsConfig, **kwargs: Any) -> None +``` + +Teardown the handlers. + +Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build). This hook is used to teardown all the handlers that were instantiated and cached during documentation buildup. + +For example, a handler could open a subprocess in the background and keep it open to feed it "autodoc" instructions and get back JSON data. If so, it should then close the subprocess at some point: the proper place to do this is in the handler's `teardown` method, which is indirectly called by this hook. + +Parameters: + +- #### **`config`** + + (`MkDocsConfig`) – The MkDocs config object. + +- #### **`**kwargs`** + + (`Any`, default: `{}` ) – Additional arguments passed by MkDocs. + +Source code in `src/mkdocstrings/_internal/plugin.py` + +```python +def on_post_build( + self, + config: MkDocsConfig, # noqa: ARG002 + **kwargs: Any, # noqa: ARG002 +) -> None: + """Teardown the handlers. + + Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build). + This hook is used to teardown all the handlers that were instantiated and cached during documentation buildup. + + For example, a handler could open a subprocess in the background and keep it open + to feed it "autodoc" instructions and get back JSON data. If so, it should then close the subprocess at some point: + the proper place to do this is in the handler's `teardown` method, which is indirectly called by this hook. + + Arguments: + config: The MkDocs config object. + **kwargs: Additional arguments passed by MkDocs. + """ + if not self.plugin_enabled: + return + + if self._handlers: + _logger.debug("Tearing handlers down") + self.handlers.teardown() +``` + +## ParagraphStrippingTreeprocessor + +Bases: `Treeprocessor` + +Unwraps the `

` element around the whole output. + +Methods: + +- **`run`** – Unwrap the root element if it's a single

element. + +Attributes: + +- **`name`** (`str`) – The name of the treeprocessor. +- **`strip`** (`bool`) – Whether to strip

elements or not. + +### name + +```python +name: str = 'mkdocstrings_strip_paragraph' +``` + +The name of the treeprocessor. + +### strip + +```python +strip: bool = False +``` + +Whether to strip `

` elements or not. + +### run + +```python +run(root: Element) -> Element | None +``` + +Unwrap the root element if it's a single `

` element. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def run(self, root: Element) -> Element | None: + """Unwrap the root element if it's a single `

` element.""" + if self.strip and len(root) == 1 and root[0].tag == "p": + # Turn the single `

` element into the root element and inherit its tag name (it's significant!) + root[0].tag = root.tag + return root[0] + return None +``` + +## PluginConfig + +Bases: `Config` + +The configuration options of `mkdocstrings`, written in `mkdocs.yml`. + +Attributes: + +- **`custom_templates`** – Location of custom templates to use when rendering API objects. +- **`default_handler`** – The default handler to use. The value is the name of the handler module. Default is "python". +- **`enable_inventory`** – Whether to enable object inventory creation. +- **`enabled`** – Whether to enable the plugin. Default is true. If false, mkdocstrings will not collect or render anything. +- **`handlers`** – Global configuration of handlers. +- **`locale`** – The locale to use for translations. + +### custom_templates + +```python +custom_templates = Optional(Dir(exists=True)) +``` + +Location of custom templates to use when rendering API objects. + +Value should be the path of a directory relative to the MkDocs configuration file. + +### default_handler + +```python +default_handler = Type(str, default='python') +``` + +The default handler to use. The value is the name of the handler module. Default is "python". + +### enable_inventory + +```python +enable_inventory = Optional(Type(bool)) +``` + +Whether to enable object inventory creation. + +### enabled + +```python +enabled = Type(bool, default=True) +``` + +Whether to enable the plugin. Default is true. If false, *mkdocstrings* will not collect or render anything. + +### handlers + +```python +handlers = Type(dict, default={}) +``` + +Global configuration of handlers. + +You can set global configuration per handler, applied everywhere, but overridable in each "autodoc" instruction. Example: + +```yaml +plugins: + - mkdocstrings: + handlers: + python: + options: + option1: true + option2: "value" + rust: + options: + option9: 2 +``` + +### locale + +```python +locale = Optional(Type(str)) +``` + +The locale to use for translations. + +## TemplateLogger + +```python +TemplateLogger(logger: LoggerAdapter) +``` + +A wrapper class to allow logging in templates. + +The logging methods provided by this class all accept two parameters: + +- `msg`: The message to log. +- `once`: If `True`, the message will only be logged once. + +Methods: + +- **`debug`** – Function to log a DEBUG message. +- **`info`** – Function to log an INFO message. +- **`warning`** – Function to log a WARNING message. +- **`error`** – Function to log an ERROR message. +- **`critical`** – Function to log a CRITICAL message. + +Parameters: + +- ### **`logger`** + + (`LoggerAdapter`) – A logger adapter. + +Attributes: + +- **`critical`** – Log a CRITICAL message. +- **`debug`** – Log a DEBUG message. +- **`error`** – Log an ERROR message. +- **`info`** – Log an INFO message. +- **`warning`** – Log a WARNING message. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def __init__(self, logger: LoggerAdapter): + """Initialize the object. + + Arguments: + logger: A logger adapter. + """ + self.debug = get_template_logger_function(logger.debug) + """Log a DEBUG message.""" + self.info = get_template_logger_function(logger.info) + """Log an INFO message.""" + self.warning = get_template_logger_function(logger.warning) + """Log a WARNING message.""" + self.error = get_template_logger_function(logger.error) + """Log an ERROR message.""" + self.critical = get_template_logger_function(logger.critical) + """Log a CRITICAL message.""" +``` + +### critical + +```python +critical = get_template_logger_function(critical) +``` + +Log a CRITICAL message. + +### debug + +```python +debug = get_template_logger_function(debug) +``` + +Log a DEBUG message. + +### error + +```python +error = get_template_logger_function(error) +``` + +Log an ERROR message. + +### info + +```python +info = get_template_logger_function(info) +``` + +Log an INFO message. + +### warning + +```python +warning = get_template_logger_function(warning) +``` + +Log a WARNING message. + +## ThemeNotSupported + +Bases: `Exception` + +An exception raised to tell a theme is not supported. + +## do_any + +```python +do_any(seq: Sequence, attribute: str | None = None) -> bool +``` + +Check if at least one of the item in the sequence evaluates to true. + +The `any` builtin as a filter for Jinja templates. + +Parameters: + +- ### **`seq`** + + (`Sequence`) – An iterable object. + +- ### **`attribute`** + + (`str | None`, default: `None` ) – The attribute name to use on each object of the iterable. + +Returns: + +- `bool` – A boolean telling if any object of the iterable evaluated to True. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def do_any(seq: Sequence, attribute: str | None = None) -> bool: + """Check if at least one of the item in the sequence evaluates to true. + + The `any` builtin as a filter for Jinja templates. + + Arguments: + seq: An iterable object. + attribute: The attribute name to use on each object of the iterable. + + Returns: + A boolean telling if any object of the iterable evaluated to True. + """ + if attribute is None: + return any(seq) + return any(_[attribute] for _ in seq) +``` + +## get_logger + +```python +get_logger(name: str) -> LoggerAdapter +``` + +Return a pre-configured logger. + +Parameters: + +- ### **`name`** + + (`str`) – The name to use with logging.getLogger. + +Returns: + +- `LoggerAdapter` – A logger configured to work well in MkDocs. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def get_logger(name: str) -> LoggerAdapter: + """Return a pre-configured logger. + + Arguments: + name: The name to use with `logging.getLogger`. + + Returns: + A logger configured to work well in MkDocs. + """ + logger = logging.getLogger(f"mkdocs.plugins.{name}") + return LoggerAdapter(name.split(".", 1)[0], logger) +``` + +## get_template_logger + +```python +get_template_logger( + handler_name: str | None = None, +) -> TemplateLogger +``` + +Return a logger usable in templates. + +Parameters: + +- ### **`handler_name`** + + (`str | None`, default: `None` ) – The name of the handler. + +Returns: + +- `TemplateLogger` – A template logger. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def get_template_logger(handler_name: str | None = None) -> TemplateLogger: + """Return a logger usable in templates. + + Parameters: + handler_name: The name of the handler. + + Returns: + A template logger. + """ + handler_name = handler_name or "base" + return TemplateLogger(get_logger(f"mkdocstrings_handlers.{handler_name}.templates")) +``` + +## get_template_logger_function + +```python +get_template_logger_function( + logger_func: Callable, +) -> Callable +``` + +Create a wrapper function that automatically receives the Jinja template context. + +Parameters: + +- ### **`logger_func`** + + (`Callable`) – The logger function to use within the wrapper. + +Returns: + +- `Callable` – A function. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def get_template_logger_function(logger_func: Callable) -> Callable: + """Create a wrapper function that automatically receives the Jinja template context. + + Arguments: + logger_func: The logger function to use within the wrapper. + + Returns: + A function. + """ + + @pass_context + def wrapper(context: Context, msg: str | None = None, *args: Any, **kwargs: Any) -> str: + """Log a message. + + Arguments: + context: The template context, automatically provided by Jinja. + msg: The message to log. + **kwargs: Additional arguments passed to the logger function. + + Returns: + An empty string. + """ + logger_func(f"%s: {msg or 'Rendering'}", _Lazy(get_template_path, context), *args, **kwargs) + return "" + + return wrapper +``` + +## get_template_path + +```python +get_template_path(context: Context) -> str +``` + +Return the path to the template currently using the given context. + +Parameters: + +- ### **`context`** + + (`Context`) – The template context. + +Returns: + +- `str` – The relative path to the template. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def get_template_path(context: Context) -> str: + """Return the path to the template currently using the given context. + + Arguments: + context: The template context. + + Returns: + The relative path to the template. + """ + context_name: str = str(context.name) + filename = context.environment.get_template(context_name).filename + if filename: + for template_dir in TEMPLATES_DIRS: + with suppress(ValueError): + return str(Path(filename).relative_to(template_dir)) + with suppress(ValueError): + return str(Path(filename).relative_to(Path.cwd())) + return filename + return context_name +``` + +## makeExtension + +```python +makeExtension( + *, + default_handler: str | None = None, + inventory_project: str | None = None, + inventory_version: str | None = None, + handlers: dict[str, dict] | None = None, + custom_templates: str | None = None, + markdown_extensions: list[str | dict] | None = None, + locale: str | None = None, + config_file_path: str | None = None, +) -> MkdocstringsExtension +``` + +Create the extension instance. + +We only support this function being used by Zensical. Consider this function private API. + +Source code in `src/mkdocstrings/_internal/extension.py` + +```python +def makeExtension( # noqa: N802 + *, + default_handler: str | None = None, + inventory_project: str | None = None, + inventory_version: str | None = None, + handlers: dict[str, dict] | None = None, + custom_templates: str | None = None, + markdown_extensions: list[str | dict] | None = None, + locale: str | None = None, + config_file_path: str | None = None, +) -> MkdocstringsExtension: + """Create the extension instance. + + We only support this function being used by Zensical. + Consider this function private API. + """ + mdx, mdx_config = _split_configs(markdown_extensions or []) + tool_config = _ToolConfig(config_file_path=config_file_path) + + handlers_instance = Handlers( + theme="material", + default=default_handler or _default_config["default_handler"], + inventory_project=inventory_project or "Project", + inventory_version=inventory_version or "0.0.0", + handlers_config=handlers or _default_config["handlers"], + custom_templates=custom_templates or _default_config["custom_templates"], + mdx=mdx, + mdx_config=mdx_config, + locale=locale or _default_config["locale"], + tool_config=tool_config, + ) + + handlers_instance._download_inventories() + + autorefs = AutorefsPlugin() + autorefs.config = AutorefsConfig() + autorefs.scan_toc = False + + return MkdocstringsExtension(handlers=handlers_instance, autorefs=autorefs) +``` diff --git a/llms.txt b/llms.txt new file mode 100644 index 00000000..095fcdd0 --- /dev/null +++ b/llms.txt @@ -0,0 +1,17 @@ +# mkdocstrings + +> Automatic documentation from sources, for MkDocs. + +## Usage + +- [Overview](https://mkdocstrings.github.io/index.md) +- [Usage](https://mkdocstrings.github.io/usage/index.md) +- [Handlers](https://mkdocstrings.github.io/usage/handlers/index.md) +- [Theming](https://mkdocstrings.github.io/usage/theming/index.md) +- [Recipes](https://mkdocstrings.github.io/recipes/index.md) +- [Troubleshooting](https://mkdocstrings.github.io/troubleshooting/index.md) + +## API + +- [API reference](https://mkdocstrings.github.io/reference/api/index.md) + diff --git a/docs/logo.svg b/logo.svg similarity index 100% rename from docs/logo.svg rename to logo.svg diff --git a/objects.inv b/objects.inv new file mode 100644 index 00000000..6188474b Binary files /dev/null and b/objects.inv differ diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 9c722187..00000000 --- a/pyproject.toml +++ /dev/null @@ -1,116 +0,0 @@ -[build-system] -requires = ["pdm-backend"] -build-backend = "pdm.backend" - -[project] -name = "mkdocstrings" -description = "Automatic documentation from sources, for MkDocs." -authors = [{name = "Timothée Mazzucotelli", email = "dev@pawamoy.fr"}] -license = "ISC" -license-files = ["LICENSE"] -readme = "README.md" -requires-python = ">=3.10" -keywords = ["mkdocs", "mkdocs-plugin", "docstrings", "autodoc", "documentation"] -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.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 :: Software Development :: Documentation", - "Topic :: Utilities", - "Typing :: Typed", -] -dependencies = [ - "Jinja2>=3.1", - "Markdown>=3.6", - "MarkupSafe>=1.1", - "mkdocs>=1.6", - "mkdocs-autorefs>=1.4", - "pymdown-extensions>=6.3", -] - -[project.optional-dependencies] -crystal = ["mkdocstrings-crystal>=0.3.4"] -python-legacy = ["mkdocstrings-python-legacy>=0.2.1"] -python = ["mkdocstrings-python>=1.16.2"] - -[project.urls] -Homepage = "https://mkdocstrings.github.io" -Documentation = "https://mkdocstrings.github.io" -Changelog = "https://mkdocstrings.github.io/changelog" -Repository = "https://github.com/mkdocstrings/mkdocstrings" -Issues = "https://github.com/mkdocstrings/mkdocstrings/issues" -Discussions = "https://github.com/mkdocstrings/mkdocstrings/discussions" -Gitter = "https://gitter.im/mkdocstrings/community" -Funding = "https://github.com/sponsors/pawamoy" - -[project.entry-points."mkdocs.plugins"] -mkdocstrings = "mkdocstrings:MkdocstringsPlugin" - -[tool.pdm.version] -source = "call" -getter = "scripts.get_version:get_version" - -[tool.pdm.build] -# Include as much as possible in the source distribution, to help redistributors. -excludes = ["**/.pytest_cache"] -source-includes = [ - "config", - "docs", - "scripts", - "share", - "tests", - "duties.py", - "zensical.toml", - "*.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 = [ - "git-changelog>=2.5", - "twine>=5.1", - "yore>=0.3.3", -] -ci = [ - "dirty-equals>=0.9", - "duty>=1.6", - "griffe>=2.0", - "pytest>=8.2", - "pytest-cov>=5.0", - "pytest-randomly>=3.15", - "pytest-xdist>=3.6", - "ruff>=0.4", - "ty>=0.0.14", - "types-markdown>=3.6", - "types-pyyaml>=6.0", -] - docs = [ - "ghp-import>=2.1", - "markdown-callouts>=0.4", - "markdown-exec>=1.8", - "mkdocstrings[python]>=0.29", - # YORE: EOL 3.10: Remove line. - "tomli>=2.0; python_version < '3.11'", - "zensical>=0.0.21", -] - -[tool.uv] -default-groups = ["maintain", "ci", "docs"] diff --git a/recipes/index.html b/recipes/index.html new file mode 100644 index 00000000..22c9d87a --- /dev/null +++ b/recipes/index.html @@ -0,0 +1,251 @@ + Recipes - mkdocstrings

Recipes

On this page you will find various recipes, tips and tricks for mkdocstrings and more generally Markdown documentation.

Automatic code reference pages¤

mkdocs-autoapi and mkdocs-api-autonav are MkDocs plugins that automatically generate API documentation from your project's source code. They were inspired by the recipe below.

mkdocstrings allows to inject documentation for any object into Markdown pages. But as the project grows, it quickly becomes quite tedious to keep the autodoc instructions, or even the dedicated Markdown files in sync with all your source files and objects.

In this recipe, we will iteratively automate the process of generating these pages at each build of the documentation.


Let say you have a project called project. This project has a lot of source files, or modules, which live in the src folder:

📁 repo/
+└── 📁 src/
+    └── 📁 project/
+        ├──  lorem
+        ├──  ipsum
+        ├──  dolor
+        ├──  sit
+        └──  amet
+

Without an automatic process, you will have to manually create a Markdown page for each one of these modules, with the corresponding autodoc instruction, for example ::: project.lorem, and also add entry in MkDocs' navigation option (nav in mkdocs.yml). With a lot of modules, this is quickly getting cumbersome.

Lets fix that.

Generate pages on-the-fly¤

In this recipe, we suggest to use the mkdocs-gen-files plugin. This plugin exposes utilities to generate files at build time. These files won't be written to the docs directory: you don't have to track and version them. They are transparently generated each time you build your docs. This is perfect for our use-case!

Add mkdocs-gen-files to your project's docs dependencies, and configure it like so:

mkdocs.yml
plugins:
+- search  # (1)!
+- gen-files:
+    scripts:
+    - scripts/gen_ref_pages.py  # (2)!
+- mkdocstrings
+
  1. Don't forget to load the search plugin when redefining the plugins item.
  2. The magic happens here, see below how it works.

mkdocs-gen-files is able to run Python scripts at build time. The Python script that we will execute lives in a scripts folder, and is named gen_ref_pages.py, like "generate code reference pages".

📁 repo/
+├── 📁 docs/
+│   └──  index.md
+├── 📁 scripts/
+│   └──  gen_ref_pages.py
+├── 📁 src/
+│   └── 📁 project/
+└──  mkdocs.yml
+
scripts/gen_ref_pages.py
"""Generate the code reference pages."""
+
+from pathlib import Path
+
+import mkdocs_gen_files
+
+root = Path(__file__).parent.parent
+src = root / "src"  # (1)!
+
+for path in sorted(src.rglob("*.py")):  # (2)!
+    module_path = path.relative_to(src).with_suffix("")  # (3)!
+    doc_path = path.relative_to(src).with_suffix(".md")  # (4)!
+    full_doc_path = Path("reference", doc_path)  # (5)!
+
+    parts = tuple(module_path.parts)
+
+    if parts[-1] == "__init__":  # (6)!
+        parts = parts[:-1]
+    elif parts[-1] == "__main__":
+        continue
+
+    with mkdocs_gen_files.open(full_doc_path, "w") as fd:  # (7)!
+        identifier = ".".join(parts)  # (8)!
+        print("::: " + identifier, file=fd)  # (9)!
+
+    mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root))  # (10)!
+
  1. It's important to build a path relative to the script itself, to make it possible to build the docs with MkDocs' -f option.
  2. Here we recursively list all .py files, but you can adapt the code to list files with other extensions of course, supporting other languages than Python.
  3. The module path will look like project/lorem. It will be used to build the mkdocstrings autodoc identifier.
  4. This is the partial path of the Markdown page for the module.
  5. This is the full path of the Markdown page within the docs. Here we put all reference pages into a reference folder.
  6. This part is only relevant for Python modules. We skip __main__ modules and remove __init__ from the module parts as it's implicit during imports.
  7. Magic! Add the file to MkDocs pages, without actually writing it in the docs folder.
  8. Build the autodoc identifier. Here we document Python modules, so the identifier is a dot-separated path, like project.lorem.
  9. Actually write to the magic file.
  10. We can even set the edit_uri on the pages.

Note

It is important to look out for correct edit page behaviour when using generated pages. For example, if we have edit_uri set to blob/master/docs/ and the following file structure:

📁 repo/
+├──  mkdocs.yml
+├── 📁 docs/
+│   └──  index.md
+├── 📁 scripts/
+│   └──  gen_ref_pages.py
+└── 📁 src/
+    └── 📁 project/
+        ├──  lorem.py
+        ├──  ipsum.py
+        ├──  dolor.py
+        ├──  sit.py
+        └──  amet.py
+

Then we will have to change our set_edit_path call to:

mkdocs_gen_files.set_edit_path(full_doc_path, Path("../") / path)  # (1)!
+
  1. Path can be used to traverse the structure in any way you may need, but remember to use relative paths!

...so that it correctly sets the edit path of (for example) lorem.py to <repo_url>/blob/master/src/project/lorem.py instead of <repo_url>/blob/master/docs/src/project/lorem.py.

With this script, a reference folder is automatically created each time we build our docs. This folder contains a Markdown page for each of our source modules, and each of these pages contains a single line of the form ::: project.module (module being lorem, ipsum, etc.). Great! But, we still have to actually add those pages into our MkDocs navigation:

mkdocs.yml
nav:
+# rest of the navigation...
+- Code Reference:
+  - project:
+    - lorem: reference/project/lorem.md
+    - ipsum: reference/project/ipsum.md
+    - dolor: reference/project/dolor.md
+    - sit: reference/project/sit.md
+    - amet: reference/project/amet.md
+# rest of the navigation...
+

Err... so this process is only semi-automatic? Yes, but don't worry, we can fully automate it.

Generate a literate navigation file¤

mkdocs-gen-files is able to generate a literate navigation file. But to make use of it, we will need an additional plugin: mkdocs-literate-nav. This plugin allows to specify the whole navigation, or parts of it, into Markdown pages, as plain Markdown lists. We use it here to specify the navigation for the code reference pages.

First, add mkdocs-literate-nav to your project's docs dependencies, and configure the plugin in your MkDocs configuration:

mkdocs.yml
plugins:
+- search
+- gen-files:
+    scripts:
+    - scripts/gen_ref_pages.py
+- literate-nav:
+    nav_file: SUMMARY.md
+- mkdocstrings
+

Then, the previous script is updated like so:

scripts/gen_ref_pages.py
"""Generate the code reference pages and navigation."""
+
+from pathlib import Path
+
+import mkdocs_gen_files
+
+nav = mkdocs_gen_files.Nav()
+
+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]
+    elif parts[-1] == "__main__":
+        continue
+
+    nav[parts] = doc_path.as_posix()  # (1)!
+
+    with mkdocs_gen_files.open(full_doc_path, "w") as fd:
+        ident = ".".join(parts)
+        fd.write(f"::: {ident}")
+
+    mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root))
+
+with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file:  # (2)!
+    nav_file.writelines(nav.build_literate_nav())  # (3)!
+
  1. Progressively build the navigation object.
  2. At the end, create a magic, literate navigation file called SUMMARY.md in the reference folder.
  3. Write the navigation as a Markdown list in the literate navigation file.

Now we are able to remove our hard-coded navigation in mkdocs.yml, and replace it with a single line!

mkdocs.yml
nav:
+# rest of the navigation...
+# defer to gen-files + literate-nav
+- Code Reference: reference/  # (1)!
+# rest of the navigation...
+
  1. Note the trailing slash! It is needed so that mkdocs-literate-nav knows it has to look for a SUMMARY.md file in that folder.

At this point, we should be able to see the tree of our modules in the navigation.

Bind pages to sections themselves¤

There's a last improvement we can do. With the current script, sections, corresponding to folders, will expand or collapse when you click on them, revealing __init__ modules under them (or equivalent modules in other languages, if relevant). Since we are documenting a public API, and given users never explicitly import __init__ modules, it would be nice if we could get rid of them and instead render their documentation inside the section itself.

Well, this is possible thanks to a third plugin: mkdocs-section-index.

Update the script like this:

scripts/gen_ref_pages.py
"""Generate the code reference pages and navigation."""
+
+from pathlib import Path
+
+import mkdocs_gen_files
+
+nav = mkdocs_gen_files.Nav()
+
+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")
+    elif parts[-1] == "__main__":
+        continue
+
+    nav[parts] = doc_path.as_posix()
+
+    with mkdocs_gen_files.open(full_doc_path, "w") as fd:
+        ident = ".".join(parts)
+        fd.write(f"::: {ident}")
+
+    mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root))
+
+with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file:
+    nav_file.writelines(nav.build_literate_nav())
+

And update your MkDocs configuration to list the plugin:

mkdocs.yml
plugins:
+- search
+- gen-files:
+    scripts:
+    - scripts/gen_ref_pages.py
+- literate-nav:
+    nav_file: SUMMARY.md
+- section-index
+- mkdocstrings
+

With this, __init__ modules will be documented and bound to the sections themselves, better reflecting our public API.

Prevent selection of prompts and output in Python code blocks¤

To prevent the selection of >>>, ... and output in Python "Console" code blocks, you can use the pycon syntax highlighting on your code blocks, and add global CSS rules to your site using MkDocs extra_css option:

```pycon
+>>> for word in ("Hello", "mkdocstrings!"):
+...     print(word, end=" ")
+...
+Hello mkdocstrings!
+```
+
docs/css/code_select.css
.highlight .gp, .highlight .go { /* Generic.Prompt, Generic.Output */
+    user-select: none;
+}
+
mkdocs.yml
extra_css:
+- css/code_select.css
+

Warning

The .highlight .gp, .highlight .go CSS selector can have unintended side-effects. To target pycon code blocks more specifically, you can configure the pymdownx.highlight extension to use Pygments and set language classes on code blocks:

mkdocs.yml
markdown_extensions:
+- pymdownx.highlight:
+    use_pygments: true
+    pygments_lang_class: true
+

Then you can update the CSS selector like this:

docs/css/code_select.css
.language-pycon .gp, .language-pycon .go { /* Generic.Prompt, Generic.Output */
+    user-select: none;
+}
+

If you don't want to enable this globally, you can still use style tags in the relevant pages, with more accurate CSS selectors:

<style>
+#my-div .highlight .gp, #my-div .highlight .go { /* Generic.Prompt, Generic.Output */
+    user-select: none;
+}
+</style>
+

Try to select the following code block's text:

>>> for word in ("Hello", "mkdocstrings!"):
+...     print(word, end=" ")
+Hello mkdocstrings!
+

Hide documentation strings from source code blocks¤

Since documentation strings are rendered by handlers, it can sometimes feel redundant to show these same documentation strings in source code blocks (when handlers render those).

There is a general workaround to hide these docstrings from source blocks using CSS:

/* These CSS classes depend on the handler. */
+.doc-contents details .highlight code {
+  line-height: 0;
+}
+.doc-contents details .highlight code > * {
+  line-height: initial;
+}
+.doc-contents details .highlight code > .sd {  /* Literal.String.Doc */
+  display: none;
+}
+

Note that this is considered a workaround and not a proper solution, because it has side-effects like also removing blank lines.

Automatic highlighting for indented code blocks in docstrings¤

Depending on the language used in your code base and the mkdocstrings handler used to document it, you might want to set a default syntax for code blocks added to your docstrings. For example, to default to the Python syntax:

mkdocs.yml
markdown_extensions:
+- pymdownx.highlight:
+    default_lang: python
+

Then in your docstrings, indented code blocks will be highlighted as Python code:

def my_function():
+    """This is my function.
+
+    The following code will be highlighted as Python:
+
+        result = my_function()
+        print(result)
+
+    End of the docstring.
+    """
+    pass
+
\ No newline at end of file diff --git a/recipes/index.md b/recipes/index.md new file mode 100644 index 00000000..961ededf --- /dev/null +++ b/recipes/index.md @@ -0,0 +1,416 @@ +On this page you will find various recipes, tips and tricks for *mkdocstrings* and more generally Markdown documentation. + +## Automatic code reference pages + +[mkdocs-autoapi](https://github.com/jcayers20/mkdocs-autoapi) and [mkdocs-api-autonav](https://github.com/tlambert03/mkdocs-api-autonav) are MkDocs plugins that automatically generate API documentation from your project's source code. They were inspired by the recipe below. + +*mkdocstrings* allows to inject documentation for any object into Markdown pages. But as the project grows, it quickly becomes quite tedious to keep the autodoc instructions, or even the dedicated Markdown files in sync with all your source files and objects. + +In this recipe, we will iteratively automate the process of generating these pages at each build of the documentation. + +______________________________________________________________________ + +Let say you have a project called `project`. This project has a lot of source files, or modules, which live in the `src` folder: + +```bash +📁 repo/ +└── 📁 src/ + └── 📁 project/ + ├── lorem + ├── ipsum + ├── dolor + ├── sit + └── amet +``` + +Without an automatic process, you will have to manually create a Markdown page for each one of these modules, with the corresponding autodoc instruction, for example `::: project.lorem`, and also add entry in MkDocs' navigation option (`nav` in `mkdocs.yml`). With a lot of modules, this is quickly getting cumbersome. + +Lets fix that. + +### Generate pages on-the-fly + +In this recipe, we suggest to use the [mkdocs-gen-files plugin](https://github.com/oprypin/mkdocs-gen-files). This plugin exposes utilities to generate files at build time. These files won't be written to the docs directory: you don't have to track and version them. They are transparently generated each time you build your docs. This is perfect for our use-case! + +Add `mkdocs-gen-files` to your project's docs dependencies, and configure it like so: + +mkdocs.yml + +```yaml +plugins: +- search # (1)! +- gen-files: + scripts: + - scripts/gen_ref_pages.py # (2)! +- mkdocstrings +``` + +1. Don't forget to load the `search` plugin when redefining the `plugins` item. +1. The magic happens here, see below how it works. + +mkdocs-gen-files is able to run Python scripts at build time. The Python script that we will execute lives in a scripts folder, and is named `gen_ref_pages.py`, like "generate code reference pages". + +```bash +📁 repo/ +├── 📁 docs/ +│ └── index.md +├── 📁 scripts/ +│ └── gen_ref_pages.py +├── 📁 src/ +│ └── 📁 project/ +└── mkdocs.yml +``` + +scripts/gen_ref_pages.py + +```python +"""Generate the code reference pages.""" + +from pathlib import Path + +import mkdocs_gen_files + +root = Path(__file__).parent.parent +src = root / "src" # (1)! + +for path in sorted(src.rglob("*.py")): # (2)! + module_path = path.relative_to(src).with_suffix("") # (3)! + doc_path = path.relative_to(src).with_suffix(".md") # (4)! + full_doc_path = Path("reference", doc_path) # (5)! + + parts = tuple(module_path.parts) + + if parts[-1] == "__init__": # (6)! + parts = parts[:-1] + elif parts[-1] == "__main__": + continue + + with mkdocs_gen_files.open(full_doc_path, "w") as fd: # (7)! + identifier = ".".join(parts) # (8)! + print("::: " + identifier, file=fd) # (9)! + + mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root)) # (10)! +``` + +1. It's important to build a path relative to the script itself, to make it possible to build the docs with MkDocs' [`-f` option](https://www.mkdocs.org/user-guide/cli/#mkdocs-build). +1. Here we recursively list all `.py` files, but you can adapt the code to list files with other extensions of course, supporting other languages than Python. +1. The module path will look like `project/lorem`. It will be used to build the *mkdocstrings* autodoc identifier. +1. This is the partial path of the Markdown page for the module. +1. This is the full path of the Markdown page within the docs. Here we put all reference pages into a `reference` folder. +1. This part is only relevant for Python modules. We skip `__main__` modules and remove `__init__` from the module parts as it's implicit during imports. +1. Magic! Add the file to MkDocs pages, without actually writing it in the docs folder. +1. Build the autodoc identifier. Here we document Python modules, so the identifier is a dot-separated path, like `project.lorem`. +1. Actually write to the magic file. +1. We can even set the `edit_uri` on the pages. + +Note + +It is important to look out for correct edit page behaviour when using generated pages. For example, if we have `edit_uri` set to `blob/master/docs/` and the following file structure: + +```bash +📁 repo/ +├── mkdocs.yml +├── 📁 docs/ +│ └── index.md +├── 📁 scripts/ +│ └── gen_ref_pages.py +└── 📁 src/ + └── 📁 project/ + ├── lorem.py + ├── ipsum.py + ├── dolor.py + ├── sit.py + └── amet.py +``` + +Then we will have to change our `set_edit_path` call to: + +```python +mkdocs_gen_files.set_edit_path(full_doc_path, Path("../") / path) # (1)! +``` + +1. Path can be used to traverse the structure in any way you may need, but remember to use relative paths! + +...so that it correctly sets the edit path of (for example) `lorem.py` to `/blob/master/src/project/lorem.py` instead of `/blob/master/docs/src/project/lorem.py`. + +With this script, a `reference` folder is automatically created each time we build our docs. This folder contains a Markdown page for each of our source modules, and each of these pages contains a single line of the form `::: project.module` (module being `lorem`, `ipsum`, etc.). Great! But, we still have to actually add those pages into our MkDocs navigation: + +mkdocs.yml + +```yaml +nav: +# rest of the navigation... +- Code Reference: + - project: + - lorem: reference/project/lorem.md + - ipsum: reference/project/ipsum.md + - dolor: reference/project/dolor.md + - sit: reference/project/sit.md + - amet: reference/project/amet.md +# rest of the navigation... +``` + +Err... so this process is only semi-automatic? Yes, but don't worry, we can fully automate it. + +### Generate a literate navigation file + +mkdocs-gen-files is able to generate a literate navigation file. But to make use of it, we will need an additional plugin: [mkdocs-literate-nav](https://github.com/oprypin/mkdocs-literate-nav). This plugin allows to specify the whole navigation, or parts of it, into Markdown pages, as plain Markdown lists. We use it here to specify the navigation for the code reference pages. + +First, add `mkdocs-literate-nav` to your project's docs dependencies, and configure the plugin in your MkDocs configuration: + +mkdocs.yml + +```yaml +plugins: +- search +- gen-files: + scripts: + - scripts/gen_ref_pages.py +- literate-nav: + nav_file: SUMMARY.md +- mkdocstrings +``` + +Then, the previous script is updated like so: + +scripts/gen_ref_pages.py + +```python +"""Generate the code reference pages and navigation.""" + +from pathlib import Path + +import mkdocs_gen_files + +nav = mkdocs_gen_files.Nav() + +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] + elif parts[-1] == "__main__": + continue + + nav[parts] = doc_path.as_posix() # (1)! + + with mkdocs_gen_files.open(full_doc_path, "w") as fd: + ident = ".".join(parts) + fd.write(f"::: {ident}") + + mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root)) + +with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file: # (2)! + nav_file.writelines(nav.build_literate_nav()) # (3)! +``` + +1. Progressively build the navigation object. +1. At the end, create a magic, literate navigation file called `SUMMARY.md` in the `reference` folder. +1. Write the navigation as a Markdown list in the literate navigation file. + +Now we are able to remove our hard-coded navigation in `mkdocs.yml`, and replace it with a single line! + +mkdocs.yml + +```yaml +nav: +# rest of the navigation... +# defer to gen-files + literate-nav +- Code Reference: reference/ # (1)! +# rest of the navigation... +``` + +1. Note the trailing slash! It is needed so that `mkdocs-literate-nav` knows it has to look for a `SUMMARY.md` file in that folder. + +At this point, we should be able to see the tree of our modules in the navigation. + +### Bind pages to sections themselves + +There's a last improvement we can do. With the current script, sections, corresponding to folders, will expand or collapse when you click on them, revealing `__init__` modules under them (or equivalent modules in other languages, if relevant). Since we are documenting a public API, and given users never explicitly import `__init__` modules, it would be nice if we could get rid of them and instead render their documentation inside the section itself. + +Well, this is possible thanks to a third plugin: [mkdocs-section-index](https://github.com/oprypin/mkdocs-section-index). + +Update the script like this: + +scripts/gen_ref_pages.py + +```python +"""Generate the code reference pages and navigation.""" + +from pathlib import Path + +import mkdocs_gen_files + +nav = mkdocs_gen_files.Nav() + +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") + elif parts[-1] == "__main__": + continue + + nav[parts] = doc_path.as_posix() + + with mkdocs_gen_files.open(full_doc_path, "w") as fd: + ident = ".".join(parts) + fd.write(f"::: {ident}") + + mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root)) + +with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file: + nav_file.writelines(nav.build_literate_nav()) +``` + +And update your MkDocs configuration to list the plugin: + +mkdocs.yml + +```yaml +plugins: +- search +- gen-files: + scripts: + - scripts/gen_ref_pages.py +- literate-nav: + nav_file: SUMMARY.md +- section-index +- mkdocstrings +``` + +With this, `__init__` modules will be documented and bound to the sections themselves, better reflecting our public API. + +## Prevent selection of prompts and output in Python code blocks + +To prevent the selection of `>>>`, `...` and output in Python "Console" code blocks, you can use the `pycon` syntax highlighting on your code blocks, and add global CSS rules to your site using MkDocs `extra_css` option: + +````md +```pycon +>>> for word in ("Hello", "mkdocstrings!"): +... print(word, end=" ") +... +Hello mkdocstrings! +```` + +```` + +docs/css/code_select.css + +```css +.highlight .gp, .highlight .go { /* Generic.Prompt, Generic.Output */ + user-select: none; +} +```` + +mkdocs.yml + +```yaml +extra_css: +- css/code_select.css +``` + +Warning + +The `.highlight .gp, .highlight .go` CSS selector can have unintended side-effects. To target `pycon` code blocks more specifically, you can configure the `pymdownx.highlight` extension to use Pygments and set language classes on code blocks: + +mkdocs.yml + +```yaml +markdown_extensions: +- pymdownx.highlight: + use_pygments: true + pygments_lang_class: true +``` + +Then you can update the CSS selector like this: + +docs/css/code_select.css + +```css +.language-pycon .gp, .language-pycon .go { /* Generic.Prompt, Generic.Output */ + user-select: none; +} +``` + +If you don't want to enable this globally, you can still use `style` tags in the relevant pages, with more accurate CSS selectors: + +```html + +``` + +Try to select the following code block's text: + +```pycon +>>> for word in ("Hello", "mkdocstrings!"): +... print(word, end=" ") +Hello mkdocstrings! +``` + +## Hide documentation strings from source code blocks + +Since documentation strings are rendered by handlers, it can sometimes feel redundant to show these same documentation strings in source code blocks (when handlers render those). + +There is a general workaround to hide these docstrings from source blocks using CSS: + +```css +/* These CSS classes depend on the handler. */ +.doc-contents details .highlight code { + line-height: 0; +} +.doc-contents details .highlight code > * { + line-height: initial; +} +.doc-contents details .highlight code > .sd { /* Literal.String.Doc */ + display: none; +} +``` + +Note that this is considered a workaround and not a proper solution, because it has side-effects like also removing blank lines. + +## Automatic highlighting for indented code blocks in docstrings + +Depending on the language used in your code base and the mkdocstrings handler used to document it, you might want to set a default syntax for code blocks added to your docstrings. For example, to default to the Python syntax: + +mkdocs.yml + +```yaml +markdown_extensions: +- pymdownx.highlight: + default_lang: python +``` + +Then in your docstrings, indented code blocks will be highlighted as Python code: + +```python +def my_function(): + """This is my function. + + The following code will be highlighted as Python: + + result = my_function() + print(result) + + End of the docstring. + """ + pass +``` diff --git a/reference/api/index.html b/reference/api/index.html new file mode 100644 index 00000000..cbc89740 --- /dev/null +++ b/reference/api/index.html @@ -0,0 +1,2413 @@ + API reference - mkdocstrings

mkdocstrings ¤

mkdocstrings package.

Automatic documentation from sources, for MkDocs.

Classes:

Functions:

Attributes:

CollectorItem module-attribute ¤

CollectorItem = Any
+

The type of the item returned by the collect method of a handler.

HandlerConfig module-attribute ¤

HandlerConfig = Any
+

The type of the configuration of a handler.

HandlerOptions module-attribute ¤

HandlerOptions = Any
+

The type of the options passed to a handler.

TEMPLATES_DIRS module-attribute ¤

TEMPLATES_DIRS: Sequence[Path] = tuple(__path__)
+

The directories where the handler templates are located.

AutoDocProcessor ¤

AutoDocProcessor(
+    md: Markdown,
+    *,
+    handlers: Handlers,
+    autorefs: AutorefsPlugin,
+)
+

Bases: BlockProcessor

Our "autodoc" Markdown block processor.

It has a test method that tells if a block matches a criterion, and a run method that processes it.

It also has utility methods allowing to get handlers and their configuration easily, useful when processing a matched block.

Parameters:

Methods:

  • run

    Run code on the matched blocks.

  • test

    Match our autodoc instructions.

Attributes:

  • md

    The Markdown instance.

  • regex

    The regular expression to match our autodoc instructions.

Source code in src/mkdocstrings/_internal/extension.py
64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
def __init__(
+    self,
+    md: Markdown,
+    *,
+    handlers: Handlers,
+    autorefs: AutorefsPlugin,
+) -> None:
+    """Initialize the object.
+
+    Arguments:
+        md: A `markdown.Markdown` instance.
+        handlers: The handlers container.
+        autorefs: The autorefs plugin instance.
+    """
+    super().__init__(parser=md.parser)
+    self.md = md
+    """The Markdown instance."""
+    self._handlers = handlers
+    self._autorefs = autorefs
+    self._updated_envs: set = set()
+

md instance-attribute ¤

md = md
+

The Markdown instance.

regex class-attribute instance-attribute ¤

regex = compile(
+    "^(?P<heading>#{1,6} *|)::: ?(?P<name>.+?) *$",
+    flags=MULTILINE,
+)
+

The regular expression to match our autodoc instructions.

run ¤

Run code on the matched blocks.

The identifier and configuration lines are retrieved from a matched block and used to collect and render an object.

Parameters:

Source code in src/mkdocstrings/_internal/extension.py
 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
def run(self, parent: Element, blocks: MutableSequence[str]) -> None:
+    """Run code on the matched blocks.
+
+    The identifier and configuration lines are retrieved from a matched block
+    and used to collect and render an object.
+
+    Arguments:
+        parent: The parent element in the XML tree.
+        blocks: The rest of the blocks to be processed.
+    """
+    block = blocks.pop(0)
+    match = self.regex.search(block)
+
+    if match:
+        if match.start() > 0:
+            self.parser.parseBlocks(parent, [block[: match.start()]])
+        # removes the first line
+        block = block[match.end() :]
+
+    block, the_rest = self.detab(block)
+
+    if not block and blocks and blocks[0].startswith(("    handler:", "    options:")):
+        # YAML options were separated from the `:::` line by a blank line.
+        block = blocks.pop(0)
+
+    if match:
+        identifier = match["name"]
+        heading_level = match["heading"].count("#")
+        _logger.debug("Matched '::: %s'", identifier)
+
+        html, handler, _ = self._process_block(identifier, block, heading_level)
+        el = Element("div", {"class": "mkdocstrings"})
+        # The final HTML is inserted as opaque to subsequent processing, and only revealed at the end.
+        el.text = self.md.htmlStash.store(html)
+
+        if handler.outer_layer:
+            self._process_headings(handler, el)
+
+        parent.append(el)
+
+    if the_rest:
+        # This block contained unindented line(s) after the first indented
+        # line. Insert these lines as the first block of the master blocks
+        # list for future processing.
+        blocks.insert(0, the_rest)
+

test ¤

test(parent: Element, block: str) -> bool
+

Match our autodoc instructions.

Parameters:

  • parent ¤

    (Element) –

    The parent element in the XML tree.

  • block ¤

    (str) –

    The block to be tested.

Returns:

  • bool

    Whether this block should be processed or not.

Source code in src/mkdocstrings/_internal/extension.py
85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
def test(self, parent: Element, block: str) -> bool:  # noqa: ARG002
+    """Match our autodoc instructions.
+
+    Arguments:
+        parent: The parent element in the XML tree.
+        block: The block to be tested.
+
+    Returns:
+        Whether this block should be processed or not.
+    """
+    return bool(self.regex.search(block))
+

BaseHandler ¤

BaseHandler(
+    *,
+    theme: str,
+    custom_templates: str | None,
+    mdx: Sequence[str | Extension],
+    mdx_config: Mapping[str, Any],
+)
+

The base handler class.

Inherit from this class to implement a handler.

You will have to implement the collect and render methods. You can also implement the teardown method, and override the update_env method, to add more filters to the Jinja environment, making them available in your Jinja templates.

To define a fallback theme, add a fallback_theme class-variable. To add custom CSS, add an extra_css variable or create an 'style.css' file beside the templates.

If the given theme is not supported (it does not exist), it will look for a fallback_theme attribute in self to use as a fallback theme.

Other Parameters:

  • theme (str) –

    The theme to use.

  • custom_templates (str | None) –

    The path to custom templates.

  • mdx (list[str | Extension]) –

    A list of Markdown extensions to use.

  • mdx_config (Mapping[str, Mapping[str, Any]]) –

    Configuration for the Markdown extensions.

Methods:

  • collect

    Collect data given an identifier and user configuration.

  • do_convert_markdown

    Render Markdown text; for use inside templates.

  • do_heading

    Render an HTML heading and register it for the table of contents. For use inside templates.

  • get_aliases

    Return the possible aliases for a given identifier.

  • get_extended_templates_dirs

    Load template extensions for the given handler, return their templates directories.

  • get_headings

    Return and clear the headings gathered so far.

  • get_inventory_urls

    Return the URLs (and configuration options) of the inventory files to download.

  • get_options

    Get combined options.

  • get_templates_dir

    Return the path to the handler's templates directory.

  • load_inventory

    Yield items and their URLs from an inventory file streamed from in_file.

  • render

    Render a template using provided data and configuration options.

  • render_backlinks

    Render backlinks.

  • teardown

    Teardown the handler.

  • update_env

    Update the Jinja environment.

Attributes:

  • custom_templates

    The path to custom templates.

  • domain (str) –

    The handler's domain, used to register objects in the inventory, for example "py".

  • enable_inventory (bool) –

    Whether the inventory creation is enabled.

  • env

    The Jinja environment.

  • extra_css (str) –

    Extra CSS.

  • fallback_theme (str) –

    Fallback theme to use when a template isn't found in the configured theme.

  • md (Markdown) –

    The Markdown instance.

  • mdx

    The Markdown extensions to use.

  • mdx_config

    The configuration for the Markdown extensions.

  • name (str) –

    The handler's name, for example "python".

  • outer_layer (bool) –

    Whether we're in the outer Markdown conversion layer.

  • theme

    The selected theme.

Source code in src/mkdocstrings/_internal/handlers/base.py
115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
def __init__(
+    self,
+    *,
+    theme: str,
+    custom_templates: str | None,
+    mdx: Sequence[str | Extension],
+    mdx_config: Mapping[str, Any],
+) -> None:
+    """Initialize the object.
+
+    If the given theme is not supported (it does not exist), it will look for a `fallback_theme` attribute
+    in `self` to use as a fallback theme.
+
+    Keyword Arguments:
+        theme (str): The theme to use.
+        custom_templates (str | None): The path to custom templates.
+        mdx (list[str | Extension]): A list of Markdown extensions to use.
+        mdx_config (Mapping[str, Mapping[str, Any]]): Configuration for the Markdown extensions.
+    """
+    self.theme = theme
+    """The selected theme."""
+    self.custom_templates = custom_templates
+    """The path to custom templates."""
+    self.mdx = mdx
+    """The Markdown extensions to use."""
+    self.mdx_config = mdx_config
+    """The configuration for the Markdown extensions."""
+    self._md: Markdown | None = None
+    self._headings: list[Element] = []
+
+    paths = []
+
+    # add selected theme templates
+    themes_dir = self.get_templates_dir(self.name)
+    paths.append(themes_dir / self.theme)
+
+    # add extended theme templates
+    extended_templates_dirs = self.get_extended_templates_dirs(self.name)
+    for templates_dir in extended_templates_dirs:
+        paths.append(templates_dir / self.theme)
+
+    # add fallback theme templates
+    if self.fallback_theme and self.fallback_theme != self.theme:
+        paths.append(themes_dir / self.fallback_theme)
+
+        # add fallback theme of extended templates
+        for templates_dir in extended_templates_dirs:
+            paths.append(templates_dir / self.fallback_theme)
+
+    for path in paths:
+        css_path = path / "style.css"
+        if css_path.is_file():
+            self.extra_css += "\n" + css_path.read_text(encoding="utf-8")
+            break
+
+    if self.custom_templates is not None:
+        paths.insert(0, Path(self.custom_templates) / self.name / self.theme)
+
+    self.env = Environment(
+        autoescape=True,
+        loader=FileSystemLoader(paths),
+        auto_reload=False,  # Editing a template in the middle of a build is not useful.
+    )
+    """The Jinja environment."""
+
+    self.env.filters["convert_markdown"] = self.do_convert_markdown
+    self.env.filters["heading"] = self.do_heading
+    self.env.filters["any"] = do_any
+    self.env.globals["log"] = get_template_logger(self.name)
+

custom_templates instance-attribute ¤

custom_templates = custom_templates
+

The path to custom templates.

domain class-attribute ¤

domain: str
+

The handler's domain, used to register objects in the inventory, for example "py".

enable_inventory class-attribute ¤

enable_inventory: bool = False
+

Whether the inventory creation is enabled.

env instance-attribute ¤

env = Environment(
+    autoescape=True,
+    loader=FileSystemLoader(paths),
+    auto_reload=False,
+)
+

The Jinja environment.

extra_css class-attribute instance-attribute ¤

extra_css: str = ''
+

Extra CSS.

fallback_theme class-attribute ¤

fallback_theme: str = ''
+

Fallback theme to use when a template isn't found in the configured theme.

md property ¤

md: Markdown
+

The Markdown instance.

Raises:

  • RuntimeError

    When the Markdown instance is not set yet.

mdx instance-attribute ¤

mdx = mdx
+

The Markdown extensions to use.

mdx_config instance-attribute ¤

mdx_config = mdx_config
+

The configuration for the Markdown extensions.

name class-attribute ¤

name: str
+

The handler's name, for example "python".

outer_layer property ¤

outer_layer: bool
+

Whether we're in the outer Markdown conversion layer.

theme instance-attribute ¤

theme = theme
+

The selected theme.

collect ¤

Collect data given an identifier and user configuration.

In the implementation, you typically call a subprocess that returns JSON, and load that JSON again into a Python dictionary for example, though the implementation is completely free.

Parameters:

  • identifier ¤

    (str) –

    An identifier for which to collect data. For example, in Python, it would be 'mkdocstrings.handlers' to collect documentation about the handlers module. It can be anything that you can feed to the tool of your choice.

  • options ¤

    (HandlerOptions) –

    The final configuration options.

Returns:

  • CollectorItem

    Anything you want, as long as you can feed it to the handler's render method.

Source code in src/mkdocstrings/_internal/handlers/base.py
236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem:
+    """Collect data given an identifier and user configuration.
+
+    In the implementation, you typically call a subprocess that returns JSON, and load that JSON again into
+    a Python dictionary for example, though the implementation is completely free.
+
+    Arguments:
+        identifier: An identifier for which to collect data. For example, in Python,
+            it would be 'mkdocstrings.handlers' to collect documentation about the handlers module.
+            It can be anything that you can feed to the tool of your choice.
+        options: The final configuration options.
+
+    Returns:
+        Anything you want, as long as you can feed it to the handler's `render` method.
+    """
+    raise NotImplementedError
+

do_convert_markdown ¤

do_convert_markdown(
+    text: str,
+    heading_level: int,
+    html_id: str = "",
+    *,
+    strip_paragraph: bool = False,
+    autoref_hook: AutorefsHookInterface | None = None,
+) -> Markup
+

Render Markdown text; for use inside templates.

Parameters:

  • text ¤

    (str) –

    The text to convert.

  • heading_level ¤

    (int) –

    The base heading level to start all Markdown headings from.

  • html_id ¤

    (str, default: '' ) –

    The HTML id of the element that's considered the parent of this element.

  • strip_paragraph ¤

    (bool, default: False ) –

    Whether to exclude the <p> tag from around the whole output.

Returns:

Source code in src/mkdocstrings/_internal/handlers/base.py
341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
def do_convert_markdown(
+    self,
+    text: str,
+    heading_level: int,
+    html_id: str = "",
+    *,
+    strip_paragraph: bool = False,
+    autoref_hook: AutorefsHookInterface | None = None,
+) -> Markup:
+    """Render Markdown text; for use inside templates.
+
+    Arguments:
+        text: The text to convert.
+        heading_level: The base heading level to start all Markdown headings from.
+        html_id: The HTML id of the element that's considered the parent of this element.
+        strip_paragraph: Whether to exclude the `<p>` tag from around the whole output.
+
+    Returns:
+        An HTML string.
+    """
+    global _markdown_conversion_layer  # noqa: PLW0603
+    _markdown_conversion_layer += 1
+    treeprocessors = self.md.treeprocessors
+    treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = heading_level  # type: ignore[attr-defined]
+    treeprocessors[IdPrependingTreeprocessor.name].id_prefix = html_id and html_id + "--"  # type: ignore[attr-defined]
+    treeprocessors[ParagraphStrippingTreeprocessor.name].strip = strip_paragraph  # type: ignore[attr-defined]
+    if BacklinksTreeProcessor.name in treeprocessors:
+        treeprocessors[BacklinksTreeProcessor.name].initial_id = html_id  # type: ignore[attr-defined]
+    if autoref_hook and AutorefsInlineProcessor.name in self.md.inlinePatterns:
+        self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = autoref_hook  # type: ignore[attr-defined]
+
+    try:
+        return Markup(self.md.convert(text))
+    finally:
+        treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = 0  # type: ignore[attr-defined]
+        treeprocessors[IdPrependingTreeprocessor.name].id_prefix = ""  # type: ignore[attr-defined]
+        treeprocessors[ParagraphStrippingTreeprocessor.name].strip = False  # type: ignore[attr-defined]
+        if BacklinksTreeProcessor.name in treeprocessors:
+            treeprocessors[BacklinksTreeProcessor.name].initial_id = None  # type: ignore[attr-defined]
+        if AutorefsInlineProcessor.name in self.md.inlinePatterns:
+            self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = None  # type: ignore[attr-defined]
+        self.md.reset()
+        _markdown_conversion_layer -= 1
+

do_heading ¤

do_heading(
+    content: Markup,
+    heading_level: int,
+    *,
+    role: str | None = None,
+    hidden: bool = False,
+    toc_label: str | None = None,
+    skip_inventory: bool = False,
+    **attributes: str,
+) -> Markup
+

Render an HTML heading and register it for the table of contents. For use inside templates.

Parameters:

  • content ¤

    (Markup) –

    The HTML within the heading.

  • heading_level ¤

    (int) –

    The level of heading (e.g. 3 -> h3).

  • role ¤

    (str | None, default: None ) –

    An optional role for the object bound to this heading.

  • hidden ¤

    (bool, default: False ) –

    If True, only register it for the table of contents, don't render anything.

  • toc_label ¤

    (str | None, default: None ) –

    The title to use in the table of contents ('data-toc-label' attribute).

  • skip_inventory ¤

    (bool, default: False ) –

    Flag element to not be registered in the inventory (by setting a data-skip-inventory attribute).

  • **attributes ¤

    (str, default: {} ) –

    Any extra HTML attributes of the heading.

Returns:

Source code in src/mkdocstrings/_internal/handlers/base.py
385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
def do_heading(
+    self,
+    content: Markup,
+    heading_level: int,
+    *,
+    role: str | None = None,
+    hidden: bool = False,
+    toc_label: str | None = None,
+    skip_inventory: bool = False,
+    **attributes: str,
+) -> Markup:
+    """Render an HTML heading and register it for the table of contents. For use inside templates.
+
+    Arguments:
+        content: The HTML within the heading.
+        heading_level: The level of heading (e.g. 3 -> `h3`).
+        role: An optional role for the object bound to this heading.
+        hidden: If True, only register it for the table of contents, don't render anything.
+        toc_label: The title to use in the table of contents ('data-toc-label' attribute).
+        skip_inventory: Flag element to not be registered in the inventory (by setting a `data-skip-inventory` attribute).
+        **attributes: Any extra HTML attributes of the heading.
+
+    Returns:
+        An HTML string.
+    """
+    # Produce a heading element that will be used later, in `AutoDocProcessor.run`, to:
+    # - register it in the ToC: right now we're in the inner Markdown conversion layer,
+    #   so we have to bubble up the information to the outer Markdown conversion layer,
+    #   for the ToC extension to pick it up.
+    # - register it in autorefs: right now we don't know what page is being rendered,
+    #   so we bubble up the information again to where autorefs knows the page,
+    #   and can correctly register the heading anchor (id) to its full URL.
+    # - register it in the objects inventory: same as for autorefs,
+    #   we don't know the page here, or the handler (and its domain),
+    #   so we bubble up the information to where the mkdocstrings extension knows that.
+    el = Element(f"h{heading_level}", attributes)
+    if toc_label is None:
+        toc_label = content.unescape() if isinstance(content, Markup) else content
+    el.set("data-toc-label", toc_label)
+    if skip_inventory:
+        el.set("data-skip-inventory", "true")
+    if role:
+        el.set("data-role", role)
+    if content:
+        el.text = str(content).strip()
+    self._headings.append(el)
+
+    if hidden:
+        return Markup('<a id="{0}"></a>').format(attributes["id"])
+
+    # Now produce the actual HTML to be rendered. The goal is to wrap the HTML content into a heading.
+    # Start with a heading that has just attributes (no text), and add a placeholder into it.
+    el = Element(f"h{heading_level}", attributes)
+    el.append(Element("mkdocstrings-placeholder"))
+    # Tell the inner 'toc' extension to make its additions if configured so.
+    toc = cast("TocTreeprocessor", self.md.treeprocessors["toc"])
+    if toc.use_anchors:
+        toc.add_anchor(el, attributes["id"])
+    if toc.use_permalinks:
+        toc.add_permalink(el, attributes["id"])
+
+    # The content we received is HTML, so it can't just be inserted into the tree. We had marked the middle
+    # of the heading with a placeholder that can never occur (text can't directly contain angle brackets).
+    # Now this HTML wrapper can be "filled" by replacing the placeholder.
+    html_with_placeholder = tostring(el, encoding="unicode")
+    assert (  # noqa: S101
+        html_with_placeholder.count("<mkdocstrings-placeholder />") == 1
+    ), f"Bug in mkdocstrings: failed to replace in {html_with_placeholder!r}"
+    html = html_with_placeholder.replace("<mkdocstrings-placeholder />", content)
+    return Markup(html)
+

get_aliases ¤

get_aliases(identifier: str) -> tuple[str, ...]
+

Return the possible aliases for a given identifier.

Parameters:

  • identifier ¤

    (str) –

    The identifier to get the aliases of.

Returns:

  • tuple[str, ...]

    A tuple of strings - aliases.

Source code in src/mkdocstrings/_internal/handlers/base.py
325
+326
+327
+328
+329
+330
+331
+332
+333
+334
def get_aliases(self, identifier: str) -> tuple[str, ...]:  # noqa: ARG002
+    """Return the possible aliases for a given identifier.
+
+    Arguments:
+        identifier: The identifier to get the aliases of.
+
+    Returns:
+        A tuple of strings - aliases.
+    """
+    return ()
+

get_extended_templates_dirs ¤

get_extended_templates_dirs(handler: str) -> list[Path]
+

Load template extensions for the given handler, return their templates directories.

Parameters:

  • handler ¤

    (str) –

    The name of the handler to get the extended templates directory of.

Returns:

  • list[Path]

    The extensions templates directories.

Source code in src/mkdocstrings/_internal/handlers/base.py
313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
def get_extended_templates_dirs(self, handler: str) -> list[Path]:
+    """Load template extensions for the given handler, return their templates directories.
+
+    Arguments:
+        handler: The name of the handler to get the extended templates directory of.
+
+    Returns:
+        The extensions templates directories.
+    """
+    discovered_extensions = entry_points(group=f"mkdocstrings.{handler}.templates")
+    return [extension.load()() for extension in discovered_extensions]
+

get_headings ¤

get_headings() -> Sequence[Element]
+

Return and clear the headings gathered so far.

Returns:

Source code in src/mkdocstrings/_internal/handlers/base.py
456
+457
+458
+459
+460
+461
+462
+463
+464
def get_headings(self) -> Sequence[Element]:
+    """Return and clear the headings gathered so far.
+
+    Returns:
+        A list of HTML elements.
+    """
+    result = list(self._headings)
+    self._headings.clear()
+    return result
+

get_inventory_urls ¤

get_inventory_urls() -> list[tuple[str, dict[str, Any]]]
+

Return the URLs (and configuration options) of the inventory files to download.

Source code in src/mkdocstrings/_internal/handlers/base.py
196
+197
+198
def get_inventory_urls(self) -> list[tuple[str, dict[str, Any]]]:
+    """Return the URLs (and configuration options) of the inventory files to download."""
+    return []
+

get_options ¤

get_options(
+    local_options: Mapping[str, Any],
+) -> HandlerOptions
+

Get combined options.

Override this method to customize how options are combined, for example by merging the global options with the local options. By combining options here, you don't have to do it twice in collect and render.

Parameters:

Returns:

Source code in src/mkdocstrings/_internal/handlers/base.py
221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
def get_options(self, local_options: Mapping[str, Any]) -> HandlerOptions:
+    """Get combined options.
+
+    Override this method to customize how options are combined,
+    for example by merging the global options with the local options.
+    By combining options here, you don't have to do it twice in `collect` and `render`.
+
+    Arguments:
+        local_options: The local options.
+
+    Returns:
+        The combined options.
+    """
+    return local_options
+

get_templates_dir ¤

get_templates_dir(handler: str | None = None) -> Path
+

Return the path to the handler's templates directory.

Override to customize how the templates directory is found.

Parameters:

  • handler ¤

    (str | None, default: None ) –

    The name of the handler to get the templates directory of.

Raises:

Returns:

  • Path

    The templates directory path.

Source code in src/mkdocstrings/_internal/handlers/base.py
285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
def get_templates_dir(self, handler: str | None = None) -> Path:
+    """Return the path to the handler's templates directory.
+
+    Override to customize how the templates directory is found.
+
+    Arguments:
+        handler: The name of the handler to get the templates directory of.
+
+    Raises:
+        ModuleNotFoundError: When no such handler is installed.
+        FileNotFoundError: When the templates directory cannot be found.
+
+    Returns:
+        The templates directory path.
+    """
+    handler = handler or self.name
+    try:
+        import mkdocstrings_handlers  # noqa: PLC0415
+    except ModuleNotFoundError as error:
+        raise ModuleNotFoundError(f"Handler '{handler}' not found, is it installed?") from error
+
+    for path in mkdocstrings_handlers.__path__:
+        theme_path = Path(path, handler, "templates")
+        if theme_path.exists():
+            return theme_path
+
+    raise FileNotFoundError(f"Can't find 'templates' folder for handler '{handler}'")
+

load_inventory classmethod ¤

load_inventory(
+    in_file: BinaryIO,
+    url: str,
+    base_url: str | None = None,
+    **kwargs: Any,
+) -> Iterator[tuple[str, str]]
+

Yield items and their URLs from an inventory file streamed from in_file.

Parameters:

  • in_file ¤

    (BinaryIO) –

    The binary file-like object to read the inventory from.

  • url ¤

    (str) –

    The URL that this file is being streamed from (used to guess base_url).

  • base_url ¤

    (str | None, default: None ) –

    The URL that this inventory's sub-paths are relative to.

  • **kwargs ¤

    (Any, default: {} ) –

    Ignore additional arguments passed from the config.

Yields:

  • tuple[str, str]

    Tuples of (item identifier, item URL).

Source code in src/mkdocstrings/_internal/handlers/base.py
200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
@classmethod
+def load_inventory(
+    cls,
+    in_file: BinaryIO,  # noqa: ARG003
+    url: str,  # noqa: ARG003
+    base_url: str | None = None,  # noqa: ARG003
+    **kwargs: Any,  # noqa: ARG003
+) -> Iterator[tuple[str, str]]:
+    """Yield items and their URLs from an inventory file streamed from `in_file`.
+
+    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).
+    """
+    yield from ()
+

render ¤

render(
+    data: CollectorItem,
+    options: HandlerOptions,
+    *,
+    locale: str | None = None,
+) -> str
+

Render a template using provided data and configuration options.

Parameters:

  • data ¤

    (CollectorItem) –

    The collected data to render.

  • options ¤

    (HandlerOptions) –

    The final configuration options.

  • locale ¤

    (str | None, default: None ) –

    The locale to use for translations, if any.

Returns:

  • str

    The rendered template as HTML.

Source code in src/mkdocstrings/_internal/handlers/base.py
253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str:
+    """Render a template using provided data and configuration options.
+
+    Arguments:
+        data: The collected data to render.
+        options: The final configuration options.
+        locale: The locale to use for translations, if any.
+
+    Returns:
+        The rendered template as HTML.
+    """
+    raise NotImplementedError
+
render_backlinks(
+    backlinks: Mapping[str, Iterable[Backlink]],
+    *,
+    locale: str | None = None,
+) -> str
+

Render backlinks.

Parameters:

  • backlinks ¤

    (Mapping[str, Iterable[Backlink]]) –

    A mapping of identifiers to backlinks.

  • locale ¤

    (str | None, default: None ) –

    The locale to use for translations, if any.

Returns:

  • str

    The rendered backlinks as HTML.

Source code in src/mkdocstrings/_internal/handlers/base.py
266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
def render_backlinks(self, backlinks: Mapping[str, Iterable[Backlink]], *, locale: str | None = None) -> str:  # noqa: ARG002
+    """Render backlinks.
+
+    Parameters:
+        backlinks: A mapping of identifiers to backlinks.
+        locale: The locale to use for translations, if any.
+
+    Returns:
+        The rendered backlinks as HTML.
+    """
+    return ""
+

teardown ¤

teardown() -> None
+

Teardown the handler.

This method should be implemented to, for example, terminate a subprocess that was started when creating the handler instance.

Source code in src/mkdocstrings/_internal/handlers/base.py
278
+279
+280
+281
+282
+283
def teardown(self) -> None:
+    """Teardown the handler.
+
+    This method should be implemented to, for example, terminate a subprocess
+    that was started when creating the handler instance.
+    """
+

update_env ¤

update_env(config: Any) -> None
+

Update the Jinja environment.

Source code in src/mkdocstrings/_internal/handlers/base.py
466
+467
def update_env(self, config: Any) -> None:
+    """Update the Jinja environment."""
+

CollectionError ¤

Bases: Exception

An exception raised when some collection of data failed.

Handlers ¤

Handlers(
+    *,
+    theme: str,
+    default: str,
+    inventory_project: str,
+    inventory_version: str = "0.0.0",
+    handlers_config: dict[str, HandlerConfig] | None = None,
+    custom_templates: str | None = None,
+    mdx: Sequence[str | Extension] | None = None,
+    mdx_config: Mapping[str, Any] | None = None,
+    locale: str = "en",
+    tool_config: Any,
+)
+

A collection of handlers.

Do not instantiate this directly. The plugin will keep one instance of this for the purpose of caching. Use mkdocstrings.MkdocstringsPlugin.get_handler for convenient access.

Parameters:

  • theme ¤

    (str) –

    The theme to use.

  • default ¤

    (str) –

    The default handler to use.

  • inventory_project ¤

    (str) –

    The project name to use in the inventory.

  • inventory_version ¤

    (str, default: '0.0.0' ) –

    The project version to use in the inventory.

  • handlers_config ¤

    (dict[str, HandlerConfig] | None, default: None ) –

    The handlers configuration.

  • custom_templates ¤

    (str | None, default: None ) –

    The path to custom templates.

  • mdx ¤

    (Sequence[str | Extension] | None, default: None ) –

    A list of Markdown extensions to use.

  • mdx_config ¤

    (Mapping[str, Any] | None, default: None ) –

    Configuration for the Markdown extensions.

  • locale ¤

    (str, default: 'en' ) –

    The locale to use for translations.

  • tool_config ¤

    (Any) –

    Tool configuration to pass down to handlers.

Methods:

  • get_handler

    Get a handler thanks to its name.

  • get_handler_config

    Return the global configuration of the given handler.

  • get_handler_name

    Return the handler name defined in an "autodoc" instruction YAML configuration, or the global default handler.

  • teardown

    Teardown all cached handlers and clear the cache.

Attributes:

Source code in src/mkdocstrings/_internal/handlers/base.py
495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
def __init__(
+    self,
+    *,
+    theme: str,
+    default: str,
+    inventory_project: str,
+    inventory_version: str = "0.0.0",
+    handlers_config: dict[str, HandlerConfig] | None = None,
+    custom_templates: str | None = None,
+    mdx: Sequence[str | Extension] | None = None,
+    mdx_config: Mapping[str, Any] | None = None,
+    locale: str = "en",
+    tool_config: Any,
+) -> None:
+    """Initialize the object.
+
+    Arguments:
+        theme: The theme to use.
+        default: The default handler to use.
+        inventory_project: The project name to use in the inventory.
+        inventory_version: The project version to use in the inventory.
+        handlers_config: The handlers configuration.
+        custom_templates: The path to custom templates.
+        mdx: A list of Markdown extensions to use.
+        mdx_config: Configuration for the Markdown extensions.
+        locale: The locale to use for translations.
+        tool_config: Tool configuration to pass down to handlers.
+    """
+    self._theme = theme
+    self._default = default
+    self._handlers_config = handlers_config or {}
+    self._custom_templates = custom_templates
+    self._mdx = mdx or []
+    self._mdx_config = mdx_config or {}
+    self._handlers: dict[str, BaseHandler] = {}
+    self._locale = locale
+    self._tool_config = tool_config
+
+    self.inventory: Inventory = Inventory(project=inventory_project, version=inventory_version)
+    """The objects inventory."""
+
+    self._inv_futures: dict[futures.Future, tuple[BaseHandler, str, Any]] = {}
+

inventory instance-attribute ¤

inventory: Inventory = Inventory(
+    project=inventory_project, version=inventory_version
+)
+

The objects inventory.

seen_handlers property ¤

seen_handlers: Iterable[BaseHandler]
+

Get the handlers that were encountered so far throughout the build.

Returns:

get_handler ¤

get_handler(
+    name: str, handler_config: dict | None = None
+) -> BaseHandler
+

Get a handler thanks to its name.

This function dynamically imports a module named "mkdocstrings.handlers.NAME", calls its get_handler method to get an instance of a handler, and caches it in dictionary. It means that during one run (for each reload when serving, or once when building), a handler is instantiated only once, and reused for each "autodoc" instruction asking for it.

Parameters:

  • name ¤

    (str) –

    The name of the handler. Really, it's the name of the Python module holding it.

  • handler_config ¤

    (dict | None, default: None ) –

    Configuration passed to the handler.

Returns:

  • BaseHandler

    An instance of a subclass of BaseHandler, as instantiated by the get_handler method of the handler's module.

Source code in src/mkdocstrings/_internal/handlers/base.py
560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
def get_handler(self, name: str, handler_config: dict | None = None) -> BaseHandler:
+    """Get a handler thanks to its name.
+
+    This function dynamically imports a module named "mkdocstrings.handlers.NAME", calls its
+    `get_handler` method to get an instance of a handler, and caches it in dictionary.
+    It means that during one run (for each reload when serving, or once when building),
+    a handler is instantiated only once, and reused for each "autodoc" instruction asking for it.
+
+    Arguments:
+        name: The name of the handler. Really, it's the name of the Python module holding it.
+        handler_config: Configuration passed to the handler.
+
+    Returns:
+        An instance of a subclass of [`BaseHandler`][mkdocstrings.BaseHandler],
+            as instantiated by the `get_handler` method of the handler's module.
+    """
+    if name not in self._handlers:
+        if handler_config is None:
+            handler_config = self._handlers_config.get(name, {})
+        module = importlib.import_module(f"mkdocstrings_handlers.{name}")
+
+        self._handlers[name] = module.get_handler(
+            theme=self._theme,
+            custom_templates=self._custom_templates,
+            mdx=self._mdx,
+            mdx_config=self._mdx_config,
+            handler_config=handler_config,
+            tool_config=self._tool_config,
+        )
+    return self._handlers[name]
+

get_handler_config ¤

get_handler_config(name: str) -> dict
+

Return the global configuration of the given handler.

Parameters:

  • name ¤

    (str) –

    The name of the handler to get the global configuration of.

Returns:

  • dict

    The global configuration of the given handler. It can be an empty dictionary.

Source code in src/mkdocstrings/_internal/handlers/base.py
549
+550
+551
+552
+553
+554
+555
+556
+557
+558
def get_handler_config(self, name: str) -> dict:
+    """Return the global configuration of the given handler.
+
+    Arguments:
+        name: The name of the handler to get the global configuration of.
+
+    Returns:
+        The global configuration of the given handler. It can be an empty dictionary.
+    """
+    return self._handlers_config.get(name, None) or {}
+

get_handler_name ¤

get_handler_name(config: dict) -> str
+

Return the handler name defined in an "autodoc" instruction YAML configuration, or the global default handler.

Parameters:

  • config ¤

    (dict) –

    A configuration dictionary, obtained from YAML below the "autodoc" instruction.

Returns:

  • str

    The name of the handler to use.

Source code in src/mkdocstrings/_internal/handlers/base.py
538
+539
+540
+541
+542
+543
+544
+545
+546
+547
def get_handler_name(self, config: dict) -> str:
+    """Return the handler name defined in an "autodoc" instruction YAML configuration, or the global default handler.
+
+    Arguments:
+        config: A configuration dictionary, obtained from YAML below the "autodoc" instruction.
+
+    Returns:
+        The name of the handler to use.
+    """
+    return config.get("handler", self._default)
+

teardown ¤

teardown() -> None
+

Teardown all cached handlers and clear the cache.

Source code in src/mkdocstrings/_internal/handlers/base.py
658
+659
+660
+661
+662
+663
+664
def teardown(self) -> None:
+    """Teardown all cached handlers and clear the cache."""
+    for future in self._inv_futures:
+        future.cancel()
+    for handler in self.seen_handlers:
+        handler.teardown()
+    self._handlers.clear()
+

HeadingShiftingTreeprocessor ¤

HeadingShiftingTreeprocessor(md: Markdown, shift_by: int)
+

Bases: Treeprocessor

Shift levels of all Markdown headings according to the configured base level.

Parameters:

  • md ¤

    (Markdown) –

    A markdown.Markdown instance.

  • shift_by ¤

    (int) –

    The number of heading "levels" to add to every heading.

Methods:

  • run

    Shift the levels of all headings in the document.

Attributes:

  • name (str) –

    The name of the treeprocessor.

  • regex (Pattern) –

    The regex to match heading tags.

  • shift_by (int) –

    The number of heading "levels" to add to every heading. <h2> with shift_by = 3 becomes <h5>.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
203
+204
+205
+206
+207
+208
+209
+210
+211
def __init__(self, md: Markdown, shift_by: int):
+    """Initialize the object.
+
+    Arguments:
+        md: A `markdown.Markdown` instance.
+        shift_by: The number of heading "levels" to add to every heading.
+    """
+    super().__init__(md)
+    self.shift_by = shift_by
+

name class-attribute instance-attribute ¤

name: str = 'mkdocstrings_headings'
+

The name of the treeprocessor.

regex class-attribute instance-attribute ¤

regex: Pattern = compile('([Hh])([1-6])')
+

The regex to match heading tags.

shift_by instance-attribute ¤

shift_by: int = shift_by
+

The number of heading "levels" to add to every heading. <h2> with shift_by = 3 becomes <h5>.

run ¤

run(root: Element) -> None
+

Shift the levels of all headings in the document.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
213
+214
+215
+216
+217
+218
+219
+220
+221
+222
def run(self, root: Element) -> None:
+    """Shift the levels of all headings in the document."""
+    if not self.shift_by:
+        return
+    for el in root.iter():
+        match = self.regex.fullmatch(el.tag)
+        if match:
+            level = int(match[2]) + self.shift_by
+            level = max(1, min(level, 6))
+            el.tag = f"{match[1]}{level}"
+

Highlighter ¤

Highlighter(md: Markdown)
+

Bases: Highlight

Code highlighter that tries to match the Markdown configuration.

Picking up the global config and defaults works only if you use the codehilite or pymdownx.highlight (recommended) Markdown extension.

  • If you use pymdownx.highlight, highlighting settings are picked up from it, and the default CSS class is .highlight. This also means the default of guess_lang: false.

  • Otherwise, if you use the codehilite extension, settings are picked up from it, and the default CSS class is .codehilite. Also consider setting guess_lang: false.

  • If neither are added to markdown_extensions, highlighting is enabled anyway. This is for backwards compatibility. If you really want to disable highlighting even in mkdocstrings, add one of these extensions anyway and set use_pygments: false.

The underlying implementation is pymdownx.highlight regardless.

Parameters:

  • md ¤

    (Markdown) –

    The Markdown instance to read configs from.

Methods:

Source code in src/mkdocstrings/_internal/handlers/rendering.py
67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
def __init__(self, md: Markdown):
+    """Configure to match a `markdown.Markdown` instance.
+
+    Arguments:
+        md: The Markdown instance to read configs from.
+    """
+    config: dict[str, Any] = {}
+    self._highlighter: str | None = None
+    for ext in md.registeredExtensions:
+        if isinstance(ext, HighlightExtension) and (ext.enabled or not config):
+            self._highlighter = "highlight"
+            config = ext.getConfigs()
+            break  # This one takes priority, no need to continue looking
+        if isinstance(ext, CodeHiliteExtension) and not config:
+            self._highlighter = "codehilite"
+            config = ext.getConfigs()
+            config["language_prefix"] = config["lang_prefix"]
+    self._css_class = config.pop("css_class", "highlight")
+    super().__init__(**{name: opt for name, opt in config.items() if name in self._highlight_config_keys})
+

highlight ¤

highlight(
+    src: str,
+    language: str | None = None,
+    *,
+    inline: bool = False,
+    dedent: bool = True,
+    linenums: bool | None = None,
+    **kwargs: Any,
+) -> str
+

Highlight a code-snippet.

Parameters:

  • src ¤

    (str) –

    The code to highlight.

  • language ¤

    (str | None, default: None ) –

    Explicitly tell what language to use for highlighting.

  • inline ¤

    (bool, default: False ) –

    Whether to highlight as inline.

  • dedent ¤

    (bool, default: True ) –

    Whether to dedent the code before highlighting it or not.

  • linenums ¤

    (bool | None, default: None ) –

    Whether to add line numbers in the result.

  • **kwargs ¤

    (Any, default: {} ) –

    Pass on to pymdownx.highlight.Highlight.highlight.

Returns:

  • str

    The highlighted code as HTML text, marked safe (not escaped for HTML).

Source code in src/mkdocstrings/_internal/handlers/rendering.py
 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
def highlight(
+    self,
+    src: str,
+    language: str | None = None,
+    *,
+    inline: bool = False,
+    dedent: bool = True,
+    linenums: bool | None = None,
+    **kwargs: Any,
+) -> str:
+    """Highlight a code-snippet.
+
+    Arguments:
+        src: The code to highlight.
+        language: Explicitly tell what language to use for highlighting.
+        inline: Whether to highlight as inline.
+        dedent: Whether to dedent the code before highlighting it or not.
+        linenums: Whether to add line numbers in the result.
+        **kwargs: Pass on to `pymdownx.highlight.Highlight.highlight`.
+
+    Returns:
+        The highlighted code as HTML text, marked safe (not escaped for HTML).
+    """
+    if isinstance(src, Markup):
+        src = src.unescape()
+    if dedent:
+        src = textwrap.dedent(src)
+
+    kwargs.setdefault("css_class", self._css_class)
+    old_linenums = self.linenums  # type: ignore[has-type]
+    if linenums is not None:
+        self.linenums = linenums
+    try:
+        result = super().highlight(src, language, inline=inline, **kwargs)
+    finally:
+        self.linenums = old_linenums
+
+    if inline:
+        # From the maintainer of codehilite, the codehilite CSS class, as defined by the user,
+        # should never be added to inline code, because codehilite does not support inline code.
+        # See https://github.com/Python-Markdown/markdown/issues/1220#issuecomment-1692160297.
+        css_class = "" if self._highlighter == "codehilite" else kwargs["css_class"]
+        return Markup(f'<code class="{css_class} language-{language}">{result.text}</code>')
+    return Markup(result)
+

IdPrependingTreeprocessor ¤

IdPrependingTreeprocessor(md: Markdown, id_prefix: str)
+

Bases: Treeprocessor

Prepend the configured prefix to IDs of all HTML elements.

Parameters:

  • md ¤

    (Markdown) –

    A markdown.Markdown instance.

  • id_prefix ¤

    (str) –

    The prefix to add to every ID. It is prepended without any separator.

Methods:

  • run

    Prepend the configured prefix to all IDs in the document.

Attributes:

  • id_prefix (str) –

    The prefix to add to every ID. It is prepended without any separator; specify your own separator if needed.

  • name (str) –

    The name of the treeprocessor.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
142
+143
+144
+145
+146
+147
+148
+149
+150
def __init__(self, md: Markdown, id_prefix: str):
+    """Initialize the object.
+
+    Arguments:
+        md: A `markdown.Markdown` instance.
+        id_prefix: The prefix to add to every ID. It is prepended without any separator.
+    """
+    super().__init__(md)
+    self.id_prefix = id_prefix
+

id_prefix instance-attribute ¤

id_prefix: str = id_prefix
+

The prefix to add to every ID. It is prepended without any separator; specify your own separator if needed.

name class-attribute instance-attribute ¤

name: str = 'mkdocstrings_ids'
+

The name of the treeprocessor.

run ¤

run(root: Element) -> None
+

Prepend the configured prefix to all IDs in the document.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
152
+153
+154
+155
def run(self, root: Element) -> None:
+    """Prepend the configured prefix to all IDs in the document."""
+    if self.id_prefix:
+        self._prefix_ids(root)
+

Inventory ¤

Inventory(
+    items: list[InventoryItem] | None = None,
+    project: str = "project",
+    version: str = "0.0.0",
+)
+

Bases: dict

Inventory of collected and rendered objects.

Parameters:

  • items ¤

    (list[InventoryItem] | None, default: None ) –

    A list of items.

  • project ¤

    (str, default: 'project' ) –

    The project name.

  • version ¤

    (str, default: '0.0.0' ) –

    The project version.

Methods:

  • format_sphinx

    Format this inventory as a Sphinx objects.inv file.

  • parse_sphinx

    Parse a Sphinx v2 inventory file and return an Inventory from it.

  • register

    Create and register an item.

Attributes:

Source code in src/mkdocstrings/_internal/inventory.py
 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
def __init__(self, items: list[InventoryItem] | None = None, project: str = "project", version: str = "0.0.0"):
+    """Initialize the object.
+
+    Arguments:
+        items: A list of items.
+        project: The project name.
+        version: The project version.
+    """
+    super().__init__()
+    items = items or []
+    for item in items:
+        self[item.name] = item
+    self.project = project
+    """The project name."""
+    self.version = version
+    """The project version."""
+

project instance-attribute ¤

project = project
+

The project name.

version instance-attribute ¤

version = version
+

The project version.

format_sphinx ¤

format_sphinx() -> bytes
+

Format this inventory as a Sphinx objects.inv file.

Returns:

  • bytes

    The inventory as bytes.

Source code in src/mkdocstrings/_internal/inventory.py
141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
def format_sphinx(self) -> bytes:
+    """Format this inventory as a Sphinx `objects.inv` file.
+
+    Returns:
+        The inventory as bytes.
+    """
+    header = (
+        dedent(
+            f"""
+            # Sphinx inventory version 2
+            # Project: {self.project}
+            # Version: {self.version}
+            # The remainder of this file is compressed using zlib.
+            """,
+        )
+        .lstrip()
+        .encode("utf8")
+    )
+
+    lines = [
+        item.format_sphinx().encode("utf8")
+        for item in sorted(self.values(), key=lambda item: (item.domain, item.name))
+    ]
+    return header + zlib.compress(b"\n".join(lines) + b"\n", 9)
+

parse_sphinx classmethod ¤

parse_sphinx(
+    in_file: BinaryIO,
+    *,
+    domain_filter: Collection[str] = (),
+) -> Inventory
+

Parse a Sphinx v2 inventory file and return an Inventory from it.

Parameters:

  • in_file ¤

    (BinaryIO) –

    The binary file-like object to read from.

  • domain_filter ¤

    (Collection[str], default: () ) –

    A collection of domain values to allow (and filter out all other ones).

Returns:

  • Inventory

    An inventory containing the collected items.

Source code in src/mkdocstrings/_internal/inventory.py
166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
@classmethod
+def parse_sphinx(cls, in_file: BinaryIO, *, domain_filter: Collection[str] = ()) -> Inventory:
+    """Parse a Sphinx v2 inventory file and return an `Inventory` from it.
+
+    Arguments:
+        in_file: The binary file-like object to read from.
+        domain_filter: A collection of domain values to allow (and filter out all other ones).
+
+    Returns:
+        An inventory containing the collected items.
+    """
+    for _ in range(4):
+        in_file.readline()
+    lines = zlib.decompress(in_file.read()).splitlines()
+    items: list[InventoryItem] = [
+        item for line in lines if (item := InventoryItem.parse_sphinx(line.decode("utf8"), return_none=True))
+    ]
+    if domain_filter:
+        items = [item for item in items if item.domain in domain_filter]
+    return cls(items)
+

register ¤

register(
+    name: str,
+    domain: str,
+    role: str,
+    uri: str,
+    priority: int = 1,
+    dispname: str | None = None,
+) -> None
+

Create and register an item.

Parameters:

  • name ¤

    (str) –

    The item name.

  • domain ¤

    (str) –

    The item domain, like 'python' or 'crystal'.

  • role ¤

    (str) –

    The item role, like 'class' or 'method'.

  • uri ¤

    (str) –

    The item URI.

  • priority ¤

    (int, default: 1 ) –

    The item priority. Only used internally by mkdocstrings and Sphinx.

  • dispname ¤

    (str | None, default: None ) –

    The item display name.

Source code in src/mkdocstrings/_internal/inventory.py
113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
def register(
+    self,
+    name: str,
+    domain: str,
+    role: str,
+    uri: str,
+    priority: int = 1,
+    dispname: str | None = None,
+) -> None:
+    """Create and register an item.
+
+    Arguments:
+        name: The item name.
+        domain: The item domain, like 'python' or 'crystal'.
+        role: The item role, like 'class' or 'method'.
+        uri: The item URI.
+        priority: The item priority. Only used internally by mkdocstrings and Sphinx.
+        dispname: The item display name.
+    """
+    self[name] = InventoryItem(
+        name=name,
+        domain=domain,
+        role=role,
+        uri=uri,
+        priority=priority,
+        dispname=dispname,
+    )
+

InventoryItem ¤

InventoryItem(
+    name: str,
+    domain: str,
+    role: str,
+    uri: str,
+    priority: int = 1,
+    dispname: str | None = None,
+)
+

Inventory item.

Parameters:

  • name ¤

    (str) –

    The item name.

  • domain ¤

    (str) –

    The item domain, like 'python' or 'crystal'.

  • role ¤

    (str) –

    The item role, like 'class' or 'method'.

  • uri ¤

    (str) –

    The item URI.

  • priority ¤

    (int, default: 1 ) –

    The item priority. Only used internally by mkdocstrings and Sphinx.

  • dispname ¤

    (str | None, default: None ) –

    The item display name.

Methods:

  • format_sphinx

    Format this item as a Sphinx inventory line.

  • parse_sphinx

    Parse a line from a Sphinx v2 inventory file and return an InventoryItem from it.

Attributes:

Source code in src/mkdocstrings/_internal/inventory.py
20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
def __init__(
+    self,
+    name: str,
+    domain: str,
+    role: str,
+    uri: str,
+    priority: int = 1,
+    dispname: str | None = None,
+):
+    """Initialize the object.
+
+    Arguments:
+        name: The item name.
+        domain: The item domain, like 'python' or 'crystal'.
+        role: The item role, like 'class' or 'method'.
+        uri: The item URI.
+        priority: The item priority. Only used internally by mkdocstrings and Sphinx.
+        dispname: The item display name.
+    """
+    self.name: str = name
+    """The item name."""
+    self.domain: str = domain
+    """The item domain."""
+    self.role: str = role
+    """The item role."""
+    self.uri: str = uri
+    """The item URI."""
+    self.priority: int = priority
+    """The item priority."""
+    self.dispname: str = dispname or name
+    """The item display name."""
+

dispname instance-attribute ¤

dispname: str = dispname or name
+

The item display name.

domain instance-attribute ¤

domain: str = domain
+

The item domain.

name instance-attribute ¤

name: str = name
+

The item name.

priority instance-attribute ¤

priority: int = priority
+

The item priority.

role instance-attribute ¤

role: str = role
+

The item role.

sphinx_item_regex class-attribute instance-attribute ¤

sphinx_item_regex = compile(
+    "^(.+?)\\s+(\\S+):(\\S+)\\s+(-?\\d+)\\s+(\\S+)\\s*(.*)$"
+)
+

Regex to parse a Sphinx v2 inventory line.

uri instance-attribute ¤

uri: str = uri
+

The item URI.

format_sphinx ¤

format_sphinx() -> str
+

Format this item as a Sphinx inventory line.

Returns:

  • str

    A line formatted for an objects.inv file.

Source code in src/mkdocstrings/_internal/inventory.py
52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
def format_sphinx(self) -> str:
+    """Format this item as a Sphinx inventory line.
+
+    Returns:
+        A line formatted for an `objects.inv` file.
+    """
+    dispname = self.dispname
+    if dispname == self.name:
+        dispname = "-"
+    uri = self.uri
+    if uri.endswith(self.name):
+        uri = uri[: -len(self.name)] + "$"
+    return f"{self.name} {self.domain}:{self.role} {self.priority} {uri} {dispname}"
+

parse_sphinx classmethod ¤

parse_sphinx(
+    line: str, *, return_none: Literal[False]
+) -> InventoryItem
+
parse_sphinx(
+    line: str, *, return_none: Literal[True]
+) -> InventoryItem | None
+
parse_sphinx(
+    line: str, *, return_none: bool = False
+) -> InventoryItem | None
+

Parse a line from a Sphinx v2 inventory file and return an InventoryItem from it.

Source code in src/mkdocstrings/_internal/inventory.py
77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
@classmethod
+def parse_sphinx(cls, line: str, *, return_none: bool = False) -> InventoryItem | None:
+    """Parse a line from a Sphinx v2 inventory file and return an `InventoryItem` from it."""
+    match = cls.sphinx_item_regex.search(line)
+    if not match:
+        if return_none:
+            return None
+        raise ValueError(line)
+    name, domain, role, priority, uri, dispname = match.groups()
+    if uri.endswith("$"):
+        uri = uri[:-1] + name
+    if dispname == "-":
+        dispname = name
+    return cls(name, domain, role, uri, int(priority), dispname)
+

LoggerAdapter ¤

LoggerAdapter(prefix: str, logger: Logger)
+

Bases: LoggerAdapter

A logger adapter to prefix messages.

This adapter also adds an additional parameter to logging methods called once: if True, the message will only be logged once.

Examples:

In Python code:

>>> logger = get_logger("myplugin")
+>>> logger.debug("This is a debug message.")
+>>> logger.info("This is an info message.", once=True)
+

In Jinja templates (logger available in context as log):

{{ log.debug("This is a debug message.") }}
+{{ log.info("This is an info message.", once=True) }}
+

Parameters:

  • prefix ¤

    (str) –

    The string to insert in front of every message.

  • logger ¤

    (Logger) –

    The logger instance.

Methods:

  • log

    Log a message.

  • process

    Process the message.

Attributes:

  • prefix

    The prefix to insert in front of every message.

Source code in src/mkdocstrings/_internal/loggers.py
48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
def __init__(self, prefix: str, logger: logging.Logger):
+    """Initialize the object.
+
+    Arguments:
+        prefix: The string to insert in front of every message.
+        logger: The logger instance.
+    """
+    super().__init__(logger, {})
+    self.prefix = prefix
+    """The prefix to insert in front of every message."""
+    self._logged: set[tuple[LoggerAdapter, str]] = set()
+

prefix instance-attribute ¤

prefix = prefix
+

The prefix to insert in front of every message.

log ¤

log(
+    level: int, msg: object, *args: object, **kwargs: object
+) -> None
+

Log a message.

Parameters:

  • level ¤

    (int) –

    The logging level.

  • msg ¤

    (object) –

    The message.

  • *args ¤

    (object, default: () ) –

    Additional arguments passed to parent method.

  • **kwargs ¤

    (object, default: {} ) –

    Additional keyword arguments passed to parent method.

Source code in src/mkdocstrings/_internal/loggers.py
72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
def log(self, level: int, msg: object, *args: object, **kwargs: object) -> None:
+    """Log a message.
+
+    Arguments:
+        level: The logging level.
+        msg: The message.
+        *args: Additional arguments passed to parent method.
+        **kwargs: Additional keyword arguments passed to parent method.
+    """
+    if kwargs.pop("once", False):
+        if (key := (self, str(msg))) in self._logged:
+            return
+        self._logged.add(key)
+    super().log(level, msg, *args, **kwargs)  # type: ignore[arg-type]
+

process ¤

process(
+    msg: str, kwargs: MutableMapping[str, Any]
+) -> tuple[str, Any]
+

Process the message.

Parameters:

Returns:

Source code in src/mkdocstrings/_internal/loggers.py
60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
def process(self, msg: str, kwargs: MutableMapping[str, Any]) -> tuple[str, Any]:
+    """Process the message.
+
+    Arguments:
+        msg: The message:
+        kwargs: Remaining arguments.
+
+    Returns:
+        The processed message.
+    """
+    return f"{self.prefix}: {msg}", kwargs
+

MkdocstringsExtension ¤

MkdocstringsExtension(
+    handlers: Handlers,
+    autorefs: AutorefsPlugin,
+    **kwargs: Any,
+)
+

Bases: Extension

Our Markdown extension.

It cannot work outside of mkdocstrings.

Parameters:

  • handlers ¤

    (Handlers) –

    The handlers container.

  • autorefs ¤

    (AutorefsPlugin) –

    The autorefs plugin instance.

  • **kwargs ¤

    (Any, default: {} ) –

    Keyword arguments used by markdown.extensions.Extension.

Methods:

Source code in src/mkdocstrings/_internal/extension.py
319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
def __init__(self, handlers: Handlers, autorefs: AutorefsPlugin, **kwargs: Any) -> None:
+    """Initialize the object.
+
+    Arguments:
+        handlers: The handlers container.
+        autorefs: The autorefs plugin instance.
+        **kwargs: Keyword arguments used by `markdown.extensions.Extension`.
+    """
+    super().__init__(**kwargs)
+    self._handlers = handlers
+    self._autorefs = autorefs
+

extendMarkdown ¤

extendMarkdown(md: Markdown) -> None
+

Register the extension.

Add an instance of our AutoDocProcessor to the Markdown parser.

Parameters:

  • md ¤

    (Markdown) –

    A markdown.Markdown instance.

Source code in src/mkdocstrings/_internal/extension.py
331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
def extendMarkdown(self, md: Markdown) -> None:  # noqa: N802 (casing: parent method's name)
+    """Register the extension.
+
+    Add an instance of our [`AutoDocProcessor`][mkdocstrings.AutoDocProcessor] to the Markdown parser.
+
+    Arguments:
+        md: A `markdown.Markdown` instance.
+    """
+    md.parser.blockprocessors.register(
+        AutoDocProcessor(md, handlers=self._handlers, autorefs=self._autorefs),
+        "mkdocstrings",
+        priority=75,  # Right before markdown.blockprocessors.HashHeaderProcessor
+    )
+    md.treeprocessors.register(
+        _HeadingsPostProcessor(md),
+        "mkdocstrings_post_headings",
+        priority=4,  # Right after 'toc'.
+    )
+    md.treeprocessors.register(
+        _TocLabelsTreeProcessor(md),
+        "mkdocstrings_post_toc_labels",
+        priority=4,  # Right after 'toc'.
+    )
+

MkdocstringsInnerExtension ¤

MkdocstringsInnerExtension(headings: list[Element])
+

Bases: Extension

Extension that should always be added to Markdown sub-documents that handlers request (and only them).

Parameters:

  • headings ¤

    (list[Element]) –

    A list that will be populated with all HTML heading elements encountered in the document.

Methods:

Attributes:

  • headings

    The list that will be populated with all HTML heading elements encountered in the document.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
275
+276
+277
+278
+279
+280
+281
+282
+283
def __init__(self, headings: list[Element]):
+    """Initialize the object.
+
+    Arguments:
+        headings: A list that will be populated with all HTML heading elements encountered in the document.
+    """
+    super().__init__()
+    self.headings = headings
+    """The list that will be populated with all HTML heading elements encountered in the document."""
+

headings instance-attribute ¤

headings = headings
+

The list that will be populated with all HTML heading elements encountered in the document.

extendMarkdown ¤

extendMarkdown(md: Markdown) -> None
+

Register the extension.

Parameters:

  • md ¤

    (Markdown) –

    A markdown.Markdown instance.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
def extendMarkdown(self, md: Markdown) -> None:  # noqa: N802 (casing: parent method's name)
+    """Register the extension.
+
+    Arguments:
+        md: A `markdown.Markdown` instance.
+    """
+    md.registerExtension(self)
+    md.treeprocessors.register(
+        HeadingShiftingTreeprocessor(md, 0),
+        HeadingShiftingTreeprocessor.name,
+        priority=12,
+    )
+    md.treeprocessors.register(
+        IdPrependingTreeprocessor(md, ""),
+        IdPrependingTreeprocessor.name,
+        priority=4,  # Right after 'toc' (needed because that extension adds ids to headers).
+    )
+    md.treeprocessors.register(
+        _HeadingReportingTreeprocessor(md, self.headings),
+        _HeadingReportingTreeprocessor.name,
+        priority=1,  # Close to the end.
+    )
+    md.treeprocessors.register(
+        ParagraphStrippingTreeprocessor(md),
+        ParagraphStrippingTreeprocessor.name,
+        priority=0.99,  # Close to the end.
+    )
+

MkdocstringsPlugin ¤

MkdocstringsPlugin()
+

Bases: BasePlugin[PluginConfig]

An mkdocs plugin.

This plugin defines the following event hooks:

  • on_config
  • on_env
  • on_post_build

Check the Developing Plugins page of mkdocs for more information about its plugin system.

Methods:

Attributes:

Source code in src/mkdocstrings/_internal/plugin.py
 97
+ 98
+ 99
+100
def __init__(self) -> None:
+    """Initialize the object."""
+    super().__init__()
+    self._handlers: Handlers | None = None
+

css_filename class-attribute instance-attribute ¤

css_filename: str = 'assets/_mkdocstrings.css'
+

The path of the CSS file to write in the site directory.

handlers property ¤

handlers: Handlers
+

Get the instance of mkdocstrings.Handlers for this plugin/build.

Raises:

  • RuntimeError

    If the plugin hasn't been initialized with a config.

Returns:

inventory_enabled property ¤

inventory_enabled: bool
+

Tell if the inventory is enabled or not.

Returns:

  • bool

    Whether the inventory is enabled.

on_env class-attribute instance-attribute ¤

on_env = CombinedEvent(
+    _on_env_load_inventories,
+    _on_env_add_css,
+    _on_env_write_inventory,
+    _on_env_apply_backlinks,
+)
+

Extra actions that need to happen after all Markdown-to-HTML page rendering.

Hook for the on_env event.

  • Gather results from background inventory download tasks.
  • Write mkdocstrings' extra files (CSS, inventory) into the site directory.
  • Apply backlinks to the HTML output of each page.

plugin_enabled property ¤

plugin_enabled: bool
+

Tell if the plugin is enabled or not.

Returns:

  • bool

    Whether the plugin is enabled.

get_handler ¤

get_handler(handler_name: str) -> BaseHandler
+

Get a handler by its name. See mkdocstrings.Handlers.get_handler.

Parameters:

  • handler_name ¤

    (str) –

    The name of the handler.

Returns:

Source code in src/mkdocstrings/_internal/plugin.py
289
+290
+291
+292
+293
+294
+295
+296
+297
+298
def get_handler(self, handler_name: str) -> BaseHandler:
+    """Get a handler by its name. See [mkdocstrings.Handlers.get_handler][].
+
+    Arguments:
+        handler_name: The name of the handler.
+
+    Returns:
+        An instance of a subclass of [`BaseHandler`][mkdocstrings.BaseHandler].
+    """
+    return self.handlers.get_handler(handler_name)
+

on_config ¤

on_config(config: MkDocsConfig) -> MkDocsConfig | None
+

Instantiate our Markdown extension.

Hook for the on_config event. In this hook, we instantiate our MkdocstringsExtension and add it to the list of Markdown extensions used by mkdocs.

We pass this plugin's configuration dictionary to the extension when instantiating it (it will need it later when processing markdown to get handlers and their global configurations).

Parameters:

  • config ¤

    (MkDocsConfig) –

    The MkDocs config object.

Returns:

  • MkDocsConfig | None

    The modified config.

Source code in src/mkdocstrings/_internal/plugin.py
116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None:
+    """Instantiate our Markdown extension.
+
+    Hook for the [`on_config` event](https://www.mkdocs.org/user-guide/plugins/#on_config).
+    In this hook, we instantiate our [`MkdocstringsExtension`][mkdocstrings.MkdocstringsExtension]
+    and add it to the list of Markdown extensions used by `mkdocs`.
+
+    We pass this plugin's configuration dictionary to the extension when instantiating it (it will need it
+    later when processing markdown to get handlers and their global configurations).
+
+    Arguments:
+        config: The MkDocs config object.
+
+    Returns:
+        The modified config.
+    """
+    if not self.plugin_enabled:
+        _logger.debug("Plugin is not enabled. Skipping.")
+        return config
+    _logger.debug("Adding extension to the list")
+
+    locale = self.config.locale or config.theme.get("language") or config.theme.get("locale") or "en"
+    locale = str(locale).replace("_", "-")
+
+    handlers = Handlers(
+        default=self.config.default_handler,
+        handlers_config=self.config.handlers,
+        theme=config.theme.name or os.path.dirname(config.theme.dirs[0]),
+        custom_templates=self.config.custom_templates,
+        mdx=config.markdown_extensions,
+        mdx_config=config.mdx_configs,
+        inventory_project=config.site_name,
+        inventory_version="0.0.0",  # TODO: Find a way to get actual version.
+        locale=locale,
+        tool_config=config,
+    )
+
+    handlers._download_inventories()
+
+    AutorefsPlugin.record_backlinks = True
+    autorefs: AutorefsPlugin
+    try:
+        # If autorefs plugin is explicitly enabled, just use it.
+        autorefs = config.plugins["autorefs"]  # type: ignore[assignment]
+        _logger.debug("Picked up existing autorefs instance %r", autorefs)
+    except KeyError:
+        # Otherwise, add a limited instance of it that acts only on what's added through `register_anchor`.
+        autorefs = AutorefsPlugin()
+        autorefs.config = AutorefsConfig()
+        autorefs.scan_toc = False
+        config.plugins["autorefs"] = autorefs
+        _logger.debug("Added a subdued autorefs instance %r", autorefs)
+
+    mkdocstrings_extension = MkdocstringsExtension(handlers, autorefs)
+    config.markdown_extensions.append(mkdocstrings_extension)  # type: ignore[arg-type]
+
+    config.extra_css.insert(0, self.css_filename)  # So that it has lower priority than user files.
+
+    self._autorefs = autorefs
+    self._handlers = handlers
+    return config
+

on_post_build ¤

on_post_build(config: MkDocsConfig, **kwargs: Any) -> None
+

Teardown the handlers.

Hook for the on_post_build event. This hook is used to teardown all the handlers that were instantiated and cached during documentation buildup.

For example, a handler could open a subprocess in the background and keep it open to feed it "autodoc" instructions and get back JSON data. If so, it should then close the subprocess at some point: the proper place to do this is in the handler's teardown method, which is indirectly called by this hook.

Parameters:

  • config ¤

    (MkDocsConfig) –

    The MkDocs config object.

  • **kwargs ¤

    (Any, default: {} ) –

    Additional arguments passed by MkDocs.

Source code in src/mkdocstrings/_internal/plugin.py
264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
def on_post_build(
+    self,
+    config: MkDocsConfig,  # noqa: ARG002
+    **kwargs: Any,  # noqa: ARG002
+) -> None:
+    """Teardown the handlers.
+
+    Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build).
+    This hook is used to teardown all the handlers that were instantiated and cached during documentation buildup.
+
+    For example, a handler could open a subprocess in the background and keep it open
+    to feed it "autodoc" instructions and get back JSON data. If so, it should then close the subprocess at some point:
+    the proper place to do this is in the handler's `teardown` method, which is indirectly called by this hook.
+
+    Arguments:
+        config: The MkDocs config object.
+        **kwargs: Additional arguments passed by MkDocs.
+    """
+    if not self.plugin_enabled:
+        return
+
+    if self._handlers:
+        _logger.debug("Tearing handlers down")
+        self.handlers.teardown()
+

ParagraphStrippingTreeprocessor ¤

Bases: Treeprocessor

Unwraps the <p> element around the whole output.

Methods:

  • run

    Unwrap the root element if it's a single <p> element.

Attributes:

  • name (str) –

    The name of the treeprocessor.

  • strip (bool) –

    Whether to strip <p> elements or not.

name class-attribute instance-attribute ¤

name: str = 'mkdocstrings_strip_paragraph'
+

The name of the treeprocessor.

strip class-attribute instance-attribute ¤

strip: bool = False
+

Whether to strip <p> elements or not.

run ¤

run(root: Element) -> Element | None
+

Unwrap the root element if it's a single <p> element.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
263
+264
+265
+266
+267
+268
+269
def run(self, root: Element) -> Element | None:
+    """Unwrap the root element if it's a single `<p>` element."""
+    if self.strip and len(root) == 1 and root[0].tag == "p":
+        # Turn the single `<p>` element into the root element and inherit its tag name (it's significant!)
+        root[0].tag = root.tag
+        return root[0]
+    return None
+

PluginConfig ¤

Bases: Config

The configuration options of mkdocstrings, written in mkdocs.yml.

Attributes:

  • custom_templates

    Location of custom templates to use when rendering API objects.

  • default_handler

    The default handler to use. The value is the name of the handler module. Default is "python".

  • enable_inventory

    Whether to enable object inventory creation.

  • enabled

    Whether to enable the plugin. Default is true. If false, mkdocstrings will not collect or render anything.

  • handlers

    Global configuration of handlers.

  • locale

    The locale to use for translations.

custom_templates class-attribute instance-attribute ¤

custom_templates = Optional(Dir(exists=True))
+

Location of custom templates to use when rendering API objects.

Value should be the path of a directory relative to the MkDocs configuration file.

default_handler class-attribute instance-attribute ¤

default_handler = Type(str, default='python')
+

The default handler to use. The value is the name of the handler module. Default is "python".

enable_inventory class-attribute instance-attribute ¤

enable_inventory = Optional(Type(bool))
+

Whether to enable object inventory creation.

enabled class-attribute instance-attribute ¤

enabled = Type(bool, default=True)
+

Whether to enable the plugin. Default is true. If false, mkdocstrings will not collect or render anything.

handlers class-attribute instance-attribute ¤

handlers = Type(dict, default={})
+

Global configuration of handlers.

You can set global configuration per handler, applied everywhere, but overridable in each "autodoc" instruction. Example:

plugins:
+  - mkdocstrings:
+      handlers:
+        python:
+          options:
+            option1: true
+            option2: "value"
+        rust:
+          options:
+            option9: 2
+

locale class-attribute instance-attribute ¤

locale = Optional(Type(str))
+

The locale to use for translations.

TemplateLogger ¤

TemplateLogger(logger: LoggerAdapter)
+

A wrapper class to allow logging in templates.

The logging methods provided by this class all accept two parameters:

  • msg: The message to log.
  • once: If True, the message will only be logged once.

Methods:

  • debug

    Function to log a DEBUG message.

  • info

    Function to log an INFO message.

  • warning

    Function to log a WARNING message.

  • error

    Function to log an ERROR message.

  • critical

    Function to log a CRITICAL message.

Parameters:

Attributes:

  • critical

    Log a CRITICAL message.

  • debug

    Log a DEBUG message.

  • error

    Log an ERROR message.

  • info

    Log an INFO message.

  • warning

    Log a WARNING message.

Source code in src/mkdocstrings/_internal/loggers.py
105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
def __init__(self, logger: LoggerAdapter):
+    """Initialize the object.
+
+    Arguments:
+        logger: A logger adapter.
+    """
+    self.debug = get_template_logger_function(logger.debug)
+    """Log a DEBUG message."""
+    self.info = get_template_logger_function(logger.info)
+    """Log an INFO message."""
+    self.warning = get_template_logger_function(logger.warning)
+    """Log a WARNING message."""
+    self.error = get_template_logger_function(logger.error)
+    """Log an ERROR message."""
+    self.critical = get_template_logger_function(logger.critical)
+    """Log a CRITICAL message."""
+

critical instance-attribute ¤

critical = get_template_logger_function(critical)
+

Log a CRITICAL message.

debug instance-attribute ¤

Log a DEBUG message.

error instance-attribute ¤

Log an ERROR message.

info instance-attribute ¤

Log an INFO message.

warning instance-attribute ¤

warning = get_template_logger_function(warning)
+

Log a WARNING message.

ThemeNotSupported ¤

Bases: Exception

An exception raised to tell a theme is not supported.

do_any ¤

do_any(seq: Sequence, attribute: str | None = None) -> bool
+

Check if at least one of the item in the sequence evaluates to true.

The any builtin as a filter for Jinja templates.

Parameters:

  • seq ¤

    (Sequence) –

    An iterable object.

  • attribute ¤

    (str | None, default: None ) –

    The attribute name to use on each object of the iterable.

Returns:

  • bool

    A boolean telling if any object of the iterable evaluated to True.

Source code in src/mkdocstrings/_internal/handlers/base.py
69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
def do_any(seq: Sequence, attribute: str | None = None) -> bool:
+    """Check if at least one of the item in the sequence evaluates to true.
+
+    The `any` builtin as a filter for Jinja templates.
+
+    Arguments:
+        seq: An iterable object.
+        attribute: The attribute name to use on each object of the iterable.
+
+    Returns:
+        A boolean telling if any object of the iterable evaluated to True.
+    """
+    if attribute is None:
+        return any(seq)
+    return any(_[attribute] for _ in seq)
+

get_logger ¤

get_logger(name: str) -> LoggerAdapter
+

Return a pre-configured logger.

Parameters:

  • name ¤

    (str) –

    The name to use with logging.getLogger.

Returns:

Source code in src/mkdocstrings/_internal/loggers.py
193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
def get_logger(name: str) -> LoggerAdapter:
+    """Return a pre-configured logger.
+
+    Arguments:
+        name: The name to use with `logging.getLogger`.
+
+    Returns:
+        A logger configured to work well in MkDocs.
+    """
+    logger = logging.getLogger(f"mkdocs.plugins.{name}")
+    return LoggerAdapter(name.split(".", 1)[0], logger)
+

get_template_logger ¤

get_template_logger(
+    handler_name: str | None = None,
+) -> TemplateLogger
+

Return a logger usable in templates.

Parameters:

  • handler_name ¤

    (str | None, default: None ) –

    The name of the handler.

Returns:

Source code in src/mkdocstrings/_internal/loggers.py
206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
def get_template_logger(handler_name: str | None = None) -> TemplateLogger:
+    """Return a logger usable in templates.
+
+    Parameters:
+        handler_name: The name of the handler.
+
+    Returns:
+        A template logger.
+    """
+    handler_name = handler_name or "base"
+    return TemplateLogger(get_logger(f"mkdocstrings_handlers.{handler_name}.templates"))
+

get_template_logger_function ¤

get_template_logger_function(
+    logger_func: Callable,
+) -> Callable
+

Create a wrapper function that automatically receives the Jinja template context.

Parameters:

  • logger_func ¤

    (Callable) –

    The logger function to use within the wrapper.

Returns:

Source code in src/mkdocstrings/_internal/loggers.py
144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
def get_template_logger_function(logger_func: Callable) -> Callable:
+    """Create a wrapper function that automatically receives the Jinja template context.
+
+    Arguments:
+        logger_func: The logger function to use within the wrapper.
+
+    Returns:
+        A function.
+    """
+
+    @pass_context
+    def wrapper(context: Context, msg: str | None = None, *args: Any, **kwargs: Any) -> str:
+        """Log a message.
+
+        Arguments:
+            context: The template context, automatically provided by Jinja.
+            msg: The message to log.
+            **kwargs: Additional arguments passed to the logger function.
+
+        Returns:
+            An empty string.
+        """
+        logger_func(f"%s: {msg or 'Rendering'}", _Lazy(get_template_path, context), *args, **kwargs)
+        return ""
+
+    return wrapper
+

get_template_path ¤

get_template_path(context: Context) -> str
+

Return the path to the template currently using the given context.

Parameters:

  • context ¤

    (Context) –

    The template context.

Returns:

  • str

    The relative path to the template.

Source code in src/mkdocstrings/_internal/loggers.py
172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
def get_template_path(context: Context) -> str:
+    """Return the path to the template currently using the given context.
+
+    Arguments:
+        context: The template context.
+
+    Returns:
+        The relative path to the template.
+    """
+    context_name: str = str(context.name)
+    filename = context.environment.get_template(context_name).filename
+    if filename:
+        for template_dir in TEMPLATES_DIRS:
+            with suppress(ValueError):
+                return str(Path(filename).relative_to(template_dir))
+        with suppress(ValueError):
+            return str(Path(filename).relative_to(Path.cwd()))
+        return filename
+    return context_name
+

makeExtension ¤

makeExtension(
+    *,
+    default_handler: str | None = None,
+    inventory_project: str | None = None,
+    inventory_version: str | None = None,
+    handlers: dict[str, dict] | None = None,
+    custom_templates: str | None = None,
+    markdown_extensions: list[str | dict] | None = None,
+    locale: str | None = None,
+    config_file_path: str | None = None,
+) -> MkdocstringsExtension
+

Create the extension instance.

We only support this function being used by Zensical. Consider this function private API.

Source code in src/mkdocstrings/_internal/extension.py
392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
def makeExtension(  # noqa: N802
+    *,
+    default_handler: str | None = None,
+    inventory_project: str | None = None,
+    inventory_version: str | None = None,
+    handlers: dict[str, dict] | None = None,
+    custom_templates: str | None = None,
+    markdown_extensions: list[str | dict] | None = None,
+    locale: str | None = None,
+    config_file_path: str | None = None,
+) -> MkdocstringsExtension:
+    """Create the extension instance.
+
+    We only support this function being used by Zensical.
+    Consider this function private API.
+    """
+    mdx, mdx_config = _split_configs(markdown_extensions or [])
+    tool_config = _ToolConfig(config_file_path=config_file_path)
+
+    handlers_instance = Handlers(
+        theme="material",
+        default=default_handler or _default_config["default_handler"],
+        inventory_project=inventory_project or "Project",
+        inventory_version=inventory_version or "0.0.0",
+        handlers_config=handlers or _default_config["handlers"],
+        custom_templates=custom_templates or _default_config["custom_templates"],
+        mdx=mdx,
+        mdx_config=mdx_config,
+        locale=locale or _default_config["locale"],
+        tool_config=tool_config,
+    )
+
+    handlers_instance._download_inventories()
+
+    autorefs = AutorefsPlugin()
+    autorefs.config = AutorefsConfig()
+    autorefs.scan_toc = False
+
+    return MkdocstringsExtension(handlers=handlers_instance, autorefs=autorefs)
+
\ No newline at end of file diff --git a/reference/api/index.md b/reference/api/index.md new file mode 100644 index 00000000..9b9e16e1 --- /dev/null +++ b/reference/api/index.md @@ -0,0 +1,3362 @@ +# mkdocstrings + +mkdocstrings package. + +Automatic documentation from sources, for MkDocs. + +Classes: + +- **`AutoDocProcessor`** – Our "autodoc" Markdown block processor. +- **`BaseHandler`** – The base handler class. +- **`CollectionError`** – An exception raised when some collection of data failed. +- **`Handlers`** – A collection of handlers. +- **`HeadingShiftingTreeprocessor`** – Shift levels of all Markdown headings according to the configured base level. +- **`Highlighter`** – Code highlighter that tries to match the Markdown configuration. +- **`IdPrependingTreeprocessor`** – Prepend the configured prefix to IDs of all HTML elements. +- **`Inventory`** – Inventory of collected and rendered objects. +- **`InventoryItem`** – Inventory item. +- **`LoggerAdapter`** – A logger adapter to prefix messages. +- **`MkdocstringsExtension`** – Our Markdown extension. +- **`MkdocstringsInnerExtension`** – Extension that should always be added to Markdown sub-documents that handlers request (and only them). +- **`MkdocstringsPlugin`** – An mkdocs plugin. +- **`ParagraphStrippingTreeprocessor`** – Unwraps the

element around the whole output. +- **`PluginConfig`** – The configuration options of mkdocstrings, written in mkdocs.yml. +- **`TemplateLogger`** – A wrapper class to allow logging in templates. +- **`ThemeNotSupported`** – An exception raised to tell a theme is not supported. + +Functions: + +- **`do_any`** – Check if at least one of the item in the sequence evaluates to true. +- **`get_logger`** – Return a pre-configured logger. +- **`get_template_logger`** – Return a logger usable in templates. +- **`get_template_logger_function`** – Create a wrapper function that automatically receives the Jinja template context. +- **`get_template_path`** – Return the path to the template currently using the given context. +- **`makeExtension`** – Create the extension instance. + +Attributes: + +- **`CollectorItem`** – The type of the item returned by the collect method of a handler. +- **`HandlerConfig`** – The type of the configuration of a handler. +- **`HandlerOptions`** – The type of the options passed to a handler. +- **`TEMPLATES_DIRS`** (`Sequence[Path]`) – The directories where the handler templates are located. + +## CollectorItem + +```python +CollectorItem = Any +``` + +The type of the item returned by the `collect` method of a handler. + +## HandlerConfig + +```python +HandlerConfig = Any +``` + +The type of the configuration of a handler. + +## HandlerOptions + +```python +HandlerOptions = Any +``` + +The type of the options passed to a handler. + +## TEMPLATES_DIRS + +```python +TEMPLATES_DIRS: Sequence[Path] = tuple(__path__) +``` + +The directories where the handler templates are located. + +## AutoDocProcessor + +```python +AutoDocProcessor( + md: Markdown, + *, + handlers: Handlers, + autorefs: AutorefsPlugin, +) +``` + +Bases: `BlockProcessor` + +Our "autodoc" Markdown block processor. + +It has a test method that tells if a block matches a criterion, and a run method that processes it. + +It also has utility methods allowing to get handlers and their configuration easily, useful when processing a matched block. + +Parameters: + +- ### **`md`** + + (`Markdown`) – A markdown.Markdown instance. + +- ### **`handlers`** + + (`Handlers`) – The handlers container. + +- ### **`autorefs`** + + (`AutorefsPlugin`) – The autorefs plugin instance. + +Methods: + +- **`run`** – Run code on the matched blocks. +- **`test`** – Match our autodoc instructions. + +Attributes: + +- **`md`** – The Markdown instance. +- **`regex`** – The regular expression to match our autodoc instructions. + +Source code in `src/mkdocstrings/_internal/extension.py` + +```python +def __init__( + self, + md: Markdown, + *, + handlers: Handlers, + autorefs: AutorefsPlugin, +) -> None: + """Initialize the object. + + Arguments: + md: A `markdown.Markdown` instance. + handlers: The handlers container. + autorefs: The autorefs plugin instance. + """ + super().__init__(parser=md.parser) + self.md = md + """The Markdown instance.""" + self._handlers = handlers + self._autorefs = autorefs + self._updated_envs: set = set() +``` + +### md + +```python +md = md +``` + +The Markdown instance. + +### regex + +```python +regex = compile( + "^(?P#{1,6} *|)::: ?(?P.+?) *$", + flags=MULTILINE, +) +``` + +The regular expression to match our autodoc instructions. + +### run + +```python +run(parent: Element, blocks: MutableSequence[str]) -> None +``` + +Run code on the matched blocks. + +The identifier and configuration lines are retrieved from a matched block and used to collect and render an object. + +Parameters: + +- #### **`parent`** + + (`Element`) – The parent element in the XML tree. + +- #### **`blocks`** + + (`MutableSequence[str]`) – The rest of the blocks to be processed. + +Source code in `src/mkdocstrings/_internal/extension.py` + +```python +def run(self, parent: Element, blocks: MutableSequence[str]) -> None: + """Run code on the matched blocks. + + The identifier and configuration lines are retrieved from a matched block + and used to collect and render an object. + + Arguments: + parent: The parent element in the XML tree. + blocks: The rest of the blocks to be processed. + """ + block = blocks.pop(0) + match = self.regex.search(block) + + if match: + if match.start() > 0: + self.parser.parseBlocks(parent, [block[: match.start()]]) + # removes the first line + block = block[match.end() :] + + block, the_rest = self.detab(block) + + if not block and blocks and blocks[0].startswith((" handler:", " options:")): + # YAML options were separated from the `:::` line by a blank line. + block = blocks.pop(0) + + if match: + identifier = match["name"] + heading_level = match["heading"].count("#") + _logger.debug("Matched '::: %s'", identifier) + + html, handler, _ = self._process_block(identifier, block, heading_level) + el = Element("div", {"class": "mkdocstrings"}) + # The final HTML is inserted as opaque to subsequent processing, and only revealed at the end. + el.text = self.md.htmlStash.store(html) + + if handler.outer_layer: + self._process_headings(handler, el) + + parent.append(el) + + if the_rest: + # This block contained unindented line(s) after the first indented + # line. Insert these lines as the first block of the master blocks + # list for future processing. + blocks.insert(0, the_rest) +``` + +### test + +```python +test(parent: Element, block: str) -> bool +``` + +Match our autodoc instructions. + +Parameters: + +- #### **`parent`** + + (`Element`) – The parent element in the XML tree. + +- #### **`block`** + + (`str`) – The block to be tested. + +Returns: + +- `bool` – Whether this block should be processed or not. + +Source code in `src/mkdocstrings/_internal/extension.py` + +```python +def test(self, parent: Element, block: str) -> bool: # noqa: ARG002 + """Match our autodoc instructions. + + Arguments: + parent: The parent element in the XML tree. + block: The block to be tested. + + Returns: + Whether this block should be processed or not. + """ + return bool(self.regex.search(block)) +``` + +## BaseHandler + +```python +BaseHandler( + *, + theme: str, + custom_templates: str | None, + mdx: Sequence[str | Extension], + mdx_config: Mapping[str, Any], +) +``` + +The base handler class. + +Inherit from this class to implement a handler. + +You will have to implement the `collect` and `render` methods. You can also implement the `teardown` method, and override the `update_env` method, to add more filters to the Jinja environment, making them available in your Jinja templates. + +To define a fallback theme, add a `fallback_theme` class-variable. To add custom CSS, add an `extra_css` variable or create an 'style.css' file beside the templates. + +If the given theme is not supported (it does not exist), it will look for a `fallback_theme` attribute in `self` to use as a fallback theme. + +Other Parameters: + +- **`theme`** (`str`) – The theme to use. +- **`custom_templates`** (`str | None`) – The path to custom templates. +- **`mdx`** (`list[str | Extension]`) – A list of Markdown extensions to use. +- **`mdx_config`** (`Mapping[str, Mapping[str, Any]]`) – Configuration for the Markdown extensions. + +Methods: + +- **`collect`** – Collect data given an identifier and user configuration. +- **`do_convert_markdown`** – Render Markdown text; for use inside templates. +- **`do_heading`** – Render an HTML heading and register it for the table of contents. For use inside templates. +- **`get_aliases`** – Return the possible aliases for a given identifier. +- **`get_extended_templates_dirs`** – Load template extensions for the given handler, return their templates directories. +- **`get_headings`** – Return and clear the headings gathered so far. +- **`get_inventory_urls`** – Return the URLs (and configuration options) of the inventory files to download. +- **`get_options`** – Get combined options. +- **`get_templates_dir`** – Return the path to the handler's templates directory. +- **`load_inventory`** – Yield items and their URLs from an inventory file streamed from in_file. +- **`render`** – Render a template using provided data and configuration options. +- **`render_backlinks`** – Render backlinks. +- **`teardown`** – Teardown the handler. +- **`update_env`** – Update the Jinja environment. + +Attributes: + +- **`custom_templates`** – The path to custom templates. +- **`domain`** (`str`) – The handler's domain, used to register objects in the inventory, for example "py". +- **`enable_inventory`** (`bool`) – Whether the inventory creation is enabled. +- **`env`** – The Jinja environment. +- **`extra_css`** (`str`) – Extra CSS. +- **`fallback_theme`** (`str`) – Fallback theme to use when a template isn't found in the configured theme. +- **`md`** (`Markdown`) – The Markdown instance. +- **`mdx`** – The Markdown extensions to use. +- **`mdx_config`** – The configuration for the Markdown extensions. +- **`name`** (`str`) – The handler's name, for example "python". +- **`outer_layer`** (`bool`) – Whether we're in the outer Markdown conversion layer. +- **`theme`** – The selected theme. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def __init__( + self, + *, + theme: str, + custom_templates: str | None, + mdx: Sequence[str | Extension], + mdx_config: Mapping[str, Any], +) -> None: + """Initialize the object. + + If the given theme is not supported (it does not exist), it will look for a `fallback_theme` attribute + in `self` to use as a fallback theme. + + Keyword Arguments: + theme (str): The theme to use. + custom_templates (str | None): The path to custom templates. + mdx (list[str | Extension]): A list of Markdown extensions to use. + mdx_config (Mapping[str, Mapping[str, Any]]): Configuration for the Markdown extensions. + """ + self.theme = theme + """The selected theme.""" + self.custom_templates = custom_templates + """The path to custom templates.""" + self.mdx = mdx + """The Markdown extensions to use.""" + self.mdx_config = mdx_config + """The configuration for the Markdown extensions.""" + self._md: Markdown | None = None + self._headings: list[Element] = [] + + paths = [] + + # add selected theme templates + themes_dir = self.get_templates_dir(self.name) + paths.append(themes_dir / self.theme) + + # add extended theme templates + extended_templates_dirs = self.get_extended_templates_dirs(self.name) + for templates_dir in extended_templates_dirs: + paths.append(templates_dir / self.theme) + + # add fallback theme templates + if self.fallback_theme and self.fallback_theme != self.theme: + paths.append(themes_dir / self.fallback_theme) + + # add fallback theme of extended templates + for templates_dir in extended_templates_dirs: + paths.append(templates_dir / self.fallback_theme) + + for path in paths: + css_path = path / "style.css" + if css_path.is_file(): + self.extra_css += "\n" + css_path.read_text(encoding="utf-8") + break + + if self.custom_templates is not None: + paths.insert(0, Path(self.custom_templates) / self.name / self.theme) + + self.env = Environment( + autoescape=True, + loader=FileSystemLoader(paths), + auto_reload=False, # Editing a template in the middle of a build is not useful. + ) + """The Jinja environment.""" + + self.env.filters["convert_markdown"] = self.do_convert_markdown + self.env.filters["heading"] = self.do_heading + self.env.filters["any"] = do_any + self.env.globals["log"] = get_template_logger(self.name) +``` + +### custom_templates + +```python +custom_templates = custom_templates +``` + +The path to custom templates. + +### domain + +```python +domain: str +``` + +The handler's domain, used to register objects in the inventory, for example "py". + +### enable_inventory + +```python +enable_inventory: bool = False +``` + +Whether the inventory creation is enabled. + +### env + +```python +env = Environment( + autoescape=True, + loader=FileSystemLoader(paths), + auto_reload=False, +) +``` + +The Jinja environment. + +### extra_css + +```python +extra_css: str = '' +``` + +Extra CSS. + +### fallback_theme + +```python +fallback_theme: str = '' +``` + +Fallback theme to use when a template isn't found in the configured theme. + +### md + +```python +md: Markdown +``` + +The Markdown instance. + +Raises: + +- `RuntimeError` – When the Markdown instance is not set yet. + +### mdx + +```python +mdx = mdx +``` + +The Markdown extensions to use. + +### mdx_config + +```python +mdx_config = mdx_config +``` + +The configuration for the Markdown extensions. + +### name + +```python +name: str +``` + +The handler's name, for example "python". + +### outer_layer + +```python +outer_layer: bool +``` + +Whether we're in the outer Markdown conversion layer. + +### theme + +```python +theme = theme +``` + +The selected theme. + +### collect + +```python +collect( + identifier: str, options: HandlerOptions +) -> CollectorItem +``` + +Collect data given an identifier and user configuration. + +In the implementation, you typically call a subprocess that returns JSON, and load that JSON again into a Python dictionary for example, though the implementation is completely free. + +Parameters: + +- #### **`identifier`** + + (`str`) – An identifier for which to collect data. For example, in Python, it would be 'mkdocstrings.handlers' to collect documentation about the handlers module. It can be anything that you can feed to the tool of your choice. + +- #### **`options`** + + (`HandlerOptions`) – The final configuration options. + +Returns: + +- `CollectorItem` – Anything you want, as long as you can feed it to the handler's render method. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem: + """Collect data given an identifier and user configuration. + + In the implementation, you typically call a subprocess that returns JSON, and load that JSON again into + a Python dictionary for example, though the implementation is completely free. + + Arguments: + identifier: An identifier for which to collect data. For example, in Python, + it would be 'mkdocstrings.handlers' to collect documentation about the handlers module. + It can be anything that you can feed to the tool of your choice. + options: The final configuration options. + + Returns: + Anything you want, as long as you can feed it to the handler's `render` method. + """ + raise NotImplementedError +``` + +### do_convert_markdown + +```python +do_convert_markdown( + text: str, + heading_level: int, + html_id: str = "", + *, + strip_paragraph: bool = False, + autoref_hook: AutorefsHookInterface | None = None, +) -> Markup +``` + +Render Markdown text; for use inside templates. + +Parameters: + +- #### **`text`** + + (`str`) – The text to convert. + +- #### **`heading_level`** + + (`int`) – The base heading level to start all Markdown headings from. + +- #### **`html_id`** + + (`str`, default: `''` ) – The HTML id of the element that's considered the parent of this element. + +- #### **`strip_paragraph`** + + (`bool`, default: `False` ) – Whether to exclude the

tag from around the whole output. + +Returns: + +- `Markup` – An HTML string. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def do_convert_markdown( + self, + text: str, + heading_level: int, + html_id: str = "", + *, + strip_paragraph: bool = False, + autoref_hook: AutorefsHookInterface | None = None, +) -> Markup: + """Render Markdown text; for use inside templates. + + Arguments: + text: The text to convert. + heading_level: The base heading level to start all Markdown headings from. + html_id: The HTML id of the element that's considered the parent of this element. + strip_paragraph: Whether to exclude the `

` tag from around the whole output. + + Returns: + An HTML string. + """ + global _markdown_conversion_layer # noqa: PLW0603 + _markdown_conversion_layer += 1 + treeprocessors = self.md.treeprocessors + treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = heading_level # type: ignore[attr-defined] + treeprocessors[IdPrependingTreeprocessor.name].id_prefix = html_id and html_id + "--" # type: ignore[attr-defined] + treeprocessors[ParagraphStrippingTreeprocessor.name].strip = strip_paragraph # type: ignore[attr-defined] + if BacklinksTreeProcessor.name in treeprocessors: + treeprocessors[BacklinksTreeProcessor.name].initial_id = html_id # type: ignore[attr-defined] + if autoref_hook and AutorefsInlineProcessor.name in self.md.inlinePatterns: + self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = autoref_hook # type: ignore[attr-defined] + + try: + return Markup(self.md.convert(text)) + finally: + treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = 0 # type: ignore[attr-defined] + treeprocessors[IdPrependingTreeprocessor.name].id_prefix = "" # type: ignore[attr-defined] + treeprocessors[ParagraphStrippingTreeprocessor.name].strip = False # type: ignore[attr-defined] + if BacklinksTreeProcessor.name in treeprocessors: + treeprocessors[BacklinksTreeProcessor.name].initial_id = None # type: ignore[attr-defined] + if AutorefsInlineProcessor.name in self.md.inlinePatterns: + self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = None # type: ignore[attr-defined] + self.md.reset() + _markdown_conversion_layer -= 1 +``` + +### do_heading + +```python +do_heading( + content: Markup, + heading_level: int, + *, + role: str | None = None, + hidden: bool = False, + toc_label: str | None = None, + skip_inventory: bool = False, + **attributes: str, +) -> Markup +``` + +Render an HTML heading and register it for the table of contents. For use inside templates. + +Parameters: + +- #### **`content`** + + (`Markup`) – The HTML within the heading. + +- #### **`heading_level`** + + (`int`) – The level of heading (e.g. 3 -> h3). + +- #### **`role`** + + (`str | None`, default: `None` ) – An optional role for the object bound to this heading. + +- #### **`hidden`** + + (`bool`, default: `False` ) – If True, only register it for the table of contents, don't render anything. + +- #### **`toc_label`** + + (`str | None`, default: `None` ) – The title to use in the table of contents ('data-toc-label' attribute). + +- #### **`skip_inventory`** + + (`bool`, default: `False` ) – Flag element to not be registered in the inventory (by setting a data-skip-inventory attribute). + +- #### **`**attributes`** + + (`str`, default: `{}` ) – Any extra HTML attributes of the heading. + +Returns: + +- `Markup` – An HTML string. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def do_heading( + self, + content: Markup, + heading_level: int, + *, + role: str | None = None, + hidden: bool = False, + toc_label: str | None = None, + skip_inventory: bool = False, + **attributes: str, +) -> Markup: + """Render an HTML heading and register it for the table of contents. For use inside templates. + + Arguments: + content: The HTML within the heading. + heading_level: The level of heading (e.g. 3 -> `h3`). + role: An optional role for the object bound to this heading. + hidden: If True, only register it for the table of contents, don't render anything. + toc_label: The title to use in the table of contents ('data-toc-label' attribute). + skip_inventory: Flag element to not be registered in the inventory (by setting a `data-skip-inventory` attribute). + **attributes: Any extra HTML attributes of the heading. + + Returns: + An HTML string. + """ + # Produce a heading element that will be used later, in `AutoDocProcessor.run`, to: + # - register it in the ToC: right now we're in the inner Markdown conversion layer, + # so we have to bubble up the information to the outer Markdown conversion layer, + # for the ToC extension to pick it up. + # - register it in autorefs: right now we don't know what page is being rendered, + # so we bubble up the information again to where autorefs knows the page, + # and can correctly register the heading anchor (id) to its full URL. + # - register it in the objects inventory: same as for autorefs, + # we don't know the page here, or the handler (and its domain), + # so we bubble up the information to where the mkdocstrings extension knows that. + el = Element(f"h{heading_level}", attributes) + if toc_label is None: + toc_label = content.unescape() if isinstance(content, Markup) else content + el.set("data-toc-label", toc_label) + if skip_inventory: + el.set("data-skip-inventory", "true") + if role: + el.set("data-role", role) + if content: + el.text = str(content).strip() + self._headings.append(el) + + if hidden: + return Markup('').format(attributes["id"]) + + # Now produce the actual HTML to be rendered. The goal is to wrap the HTML content into a heading. + # Start with a heading that has just attributes (no text), and add a placeholder into it. + el = Element(f"h{heading_level}", attributes) + el.append(Element("mkdocstrings-placeholder")) + # Tell the inner 'toc' extension to make its additions if configured so. + toc = cast("TocTreeprocessor", self.md.treeprocessors["toc"]) + if toc.use_anchors: + toc.add_anchor(el, attributes["id"]) + if toc.use_permalinks: + toc.add_permalink(el, attributes["id"]) + + # The content we received is HTML, so it can't just be inserted into the tree. We had marked the middle + # of the heading with a placeholder that can never occur (text can't directly contain angle brackets). + # Now this HTML wrapper can be "filled" by replacing the placeholder. + html_with_placeholder = tostring(el, encoding="unicode") + assert ( # noqa: S101 + html_with_placeholder.count("") == 1 + ), f"Bug in mkdocstrings: failed to replace in {html_with_placeholder!r}" + html = html_with_placeholder.replace("", content) + return Markup(html) +``` + +### get_aliases + +```python +get_aliases(identifier: str) -> tuple[str, ...] +``` + +Return the possible aliases for a given identifier. + +Parameters: + +- #### **`identifier`** + + (`str`) – The identifier to get the aliases of. + +Returns: + +- `tuple[str, ...]` – A tuple of strings - aliases. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_aliases(self, identifier: str) -> tuple[str, ...]: # noqa: ARG002 + """Return the possible aliases for a given identifier. + + Arguments: + identifier: The identifier to get the aliases of. + + Returns: + A tuple of strings - aliases. + """ + return () +``` + +### get_extended_templates_dirs + +```python +get_extended_templates_dirs(handler: str) -> list[Path] +``` + +Load template extensions for the given handler, return their templates directories. + +Parameters: + +- #### **`handler`** + + (`str`) – The name of the handler to get the extended templates directory of. + +Returns: + +- `list[Path]` – The extensions templates directories. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_extended_templates_dirs(self, handler: str) -> list[Path]: + """Load template extensions for the given handler, return their templates directories. + + Arguments: + handler: The name of the handler to get the extended templates directory of. + + Returns: + The extensions templates directories. + """ + discovered_extensions = entry_points(group=f"mkdocstrings.{handler}.templates") + return [extension.load()() for extension in discovered_extensions] +``` + +### get_headings + +```python +get_headings() -> Sequence[Element] +``` + +Return and clear the headings gathered so far. + +Returns: + +- `Sequence[Element]` – A list of HTML elements. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_headings(self) -> Sequence[Element]: + """Return and clear the headings gathered so far. + + Returns: + A list of HTML elements. + """ + result = list(self._headings) + self._headings.clear() + return result +``` + +### get_inventory_urls + +```python +get_inventory_urls() -> list[tuple[str, dict[str, Any]]] +``` + +Return the URLs (and configuration options) of the inventory files to download. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_inventory_urls(self) -> list[tuple[str, dict[str, Any]]]: + """Return the URLs (and configuration options) of the inventory files to download.""" + return [] +``` + +### get_options + +```python +get_options( + local_options: Mapping[str, Any], +) -> HandlerOptions +``` + +Get combined options. + +Override this method to customize how options are combined, for example by merging the global options with the local options. By combining options here, you don't have to do it twice in `collect` and `render`. + +Parameters: + +- #### **`local_options`** + + (`Mapping[str, Any]`) – The local options. + +Returns: + +- `HandlerOptions` – The combined options. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_options(self, local_options: Mapping[str, Any]) -> HandlerOptions: + """Get combined options. + + Override this method to customize how options are combined, + for example by merging the global options with the local options. + By combining options here, you don't have to do it twice in `collect` and `render`. + + Arguments: + local_options: The local options. + + Returns: + The combined options. + """ + return local_options +``` + +### get_templates_dir + +```python +get_templates_dir(handler: str | None = None) -> Path +``` + +Return the path to the handler's templates directory. + +Override to customize how the templates directory is found. + +Parameters: + +- #### **`handler`** + + (`str | None`, default: `None` ) – The name of the handler to get the templates directory of. + +Raises: + +- `ModuleNotFoundError` – When no such handler is installed. +- `FileNotFoundError` – When the templates directory cannot be found. + +Returns: + +- `Path` – The templates directory path. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_templates_dir(self, handler: str | None = None) -> Path: + """Return the path to the handler's templates directory. + + Override to customize how the templates directory is found. + + Arguments: + handler: The name of the handler to get the templates directory of. + + Raises: + ModuleNotFoundError: When no such handler is installed. + FileNotFoundError: When the templates directory cannot be found. + + Returns: + The templates directory path. + """ + handler = handler or self.name + try: + import mkdocstrings_handlers # noqa: PLC0415 + except ModuleNotFoundError as error: + raise ModuleNotFoundError(f"Handler '{handler}' not found, is it installed?") from error + + for path in mkdocstrings_handlers.__path__: + theme_path = Path(path, handler, "templates") + if theme_path.exists(): + return theme_path + + raise FileNotFoundError(f"Can't find 'templates' folder for handler '{handler}'") +``` + +### load_inventory + +```python +load_inventory( + in_file: BinaryIO, + url: str, + base_url: str | None = None, + **kwargs: Any, +) -> Iterator[tuple[str, str]] +``` + +Yield items and their URLs from an inventory file streamed from `in_file`. + +Parameters: + +- #### **`in_file`** + + (`BinaryIO`) – The binary file-like object to read the inventory from. + +- #### **`url`** + + (`str`) – The URL that this file is being streamed from (used to guess base_url). + +- #### **`base_url`** + + (`str | None`, default: `None` ) – The URL that this inventory's sub-paths are relative to. + +- #### **`**kwargs`** + + (`Any`, default: `{}` ) – Ignore additional arguments passed from the config. + +Yields: + +- `tuple[str, str]` – Tuples of (item identifier, item URL). + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +@classmethod +def load_inventory( + cls, + in_file: BinaryIO, # noqa: ARG003 + url: str, # noqa: ARG003 + base_url: str | None = None, # noqa: ARG003 + **kwargs: Any, # noqa: ARG003 +) -> Iterator[tuple[str, str]]: + """Yield items and their URLs from an inventory file streamed from `in_file`. + + 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). + """ + yield from () +``` + +### render + +```python +render( + data: CollectorItem, + options: HandlerOptions, + *, + locale: str | None = None, +) -> str +``` + +Render a template using provided data and configuration options. + +Parameters: + +- #### **`data`** + + (`CollectorItem`) – The collected data to render. + +- #### **`options`** + + (`HandlerOptions`) – The final configuration options. + +- #### **`locale`** + + (`str | None`, default: `None` ) – The locale to use for translations, if any. + +Returns: + +- `str` – The rendered template as HTML. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str: + """Render a template using provided data and configuration options. + + Arguments: + data: The collected data to render. + options: The final configuration options. + locale: The locale to use for translations, if any. + + Returns: + The rendered template as HTML. + """ + raise NotImplementedError +``` + +### render_backlinks + +```python +render_backlinks( + backlinks: Mapping[str, Iterable[Backlink]], + *, + locale: str | None = None, +) -> str +``` + +Render backlinks. + +Parameters: + +- #### **`backlinks`** + + (`Mapping[str, Iterable[Backlink]]`) – A mapping of identifiers to backlinks. + +- #### **`locale`** + + (`str | None`, default: `None` ) – The locale to use for translations, if any. + +Returns: + +- `str` – The rendered backlinks as HTML. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def render_backlinks(self, backlinks: Mapping[str, Iterable[Backlink]], *, locale: str | None = None) -> str: # noqa: ARG002 + """Render backlinks. + + Parameters: + backlinks: A mapping of identifiers to backlinks. + locale: The locale to use for translations, if any. + + Returns: + The rendered backlinks as HTML. + """ + return "" +``` + +### teardown + +```python +teardown() -> None +``` + +Teardown the handler. + +This method should be implemented to, for example, terminate a subprocess that was started when creating the handler instance. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def teardown(self) -> None: + """Teardown the handler. + + This method should be implemented to, for example, terminate a subprocess + that was started when creating the handler instance. + """ +``` + +### update_env + +```python +update_env(config: Any) -> None +``` + +Update the Jinja environment. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def update_env(self, config: Any) -> None: + """Update the Jinja environment.""" +``` + +## CollectionError + +Bases: `Exception` + +An exception raised when some collection of data failed. + +## Handlers + +```python +Handlers( + *, + theme: str, + default: str, + inventory_project: str, + inventory_version: str = "0.0.0", + handlers_config: dict[str, HandlerConfig] | None = None, + custom_templates: str | None = None, + mdx: Sequence[str | Extension] | None = None, + mdx_config: Mapping[str, Any] | None = None, + locale: str = "en", + tool_config: Any, +) +``` + +A collection of handlers. + +Do not instantiate this directly. The plugin will keep one instance of this for the purpose of caching. Use mkdocstrings.MkdocstringsPlugin.get_handler for convenient access. + +Parameters: + +- ### **`theme`** + + (`str`) – The theme to use. + +- ### **`default`** + + (`str`) – The default handler to use. + +- ### **`inventory_project`** + + (`str`) – The project name to use in the inventory. + +- ### **`inventory_version`** + + (`str`, default: `'0.0.0'` ) – The project version to use in the inventory. + +- ### **`handlers_config`** + + (`dict[str, HandlerConfig] | None`, default: `None` ) – The handlers configuration. + +- ### **`custom_templates`** + + (`str | None`, default: `None` ) – The path to custom templates. + +- ### **`mdx`** + + (`Sequence[str | Extension] | None`, default: `None` ) – A list of Markdown extensions to use. + +- ### **`mdx_config`** + + (`Mapping[str, Any] | None`, default: `None` ) – Configuration for the Markdown extensions. + +- ### **`locale`** + + (`str`, default: `'en'` ) – The locale to use for translations. + +- ### **`tool_config`** + + (`Any`) – Tool configuration to pass down to handlers. + +Methods: + +- **`get_handler`** – Get a handler thanks to its name. +- **`get_handler_config`** – Return the global configuration of the given handler. +- **`get_handler_name`** – Return the handler name defined in an "autodoc" instruction YAML configuration, or the global default handler. +- **`teardown`** – Teardown all cached handlers and clear the cache. + +Attributes: + +- **`inventory`** (`Inventory`) – The objects inventory. +- **`seen_handlers`** (`Iterable[BaseHandler]`) – Get the handlers that were encountered so far throughout the build. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def __init__( + self, + *, + theme: str, + default: str, + inventory_project: str, + inventory_version: str = "0.0.0", + handlers_config: dict[str, HandlerConfig] | None = None, + custom_templates: str | None = None, + mdx: Sequence[str | Extension] | None = None, + mdx_config: Mapping[str, Any] | None = None, + locale: str = "en", + tool_config: Any, +) -> None: + """Initialize the object. + + Arguments: + theme: The theme to use. + default: The default handler to use. + inventory_project: The project name to use in the inventory. + inventory_version: The project version to use in the inventory. + handlers_config: The handlers configuration. + custom_templates: The path to custom templates. + mdx: A list of Markdown extensions to use. + mdx_config: Configuration for the Markdown extensions. + locale: The locale to use for translations. + tool_config: Tool configuration to pass down to handlers. + """ + self._theme = theme + self._default = default + self._handlers_config = handlers_config or {} + self._custom_templates = custom_templates + self._mdx = mdx or [] + self._mdx_config = mdx_config or {} + self._handlers: dict[str, BaseHandler] = {} + self._locale = locale + self._tool_config = tool_config + + self.inventory: Inventory = Inventory(project=inventory_project, version=inventory_version) + """The objects inventory.""" + + self._inv_futures: dict[futures.Future, tuple[BaseHandler, str, Any]] = {} +``` + +### inventory + +```python +inventory: Inventory = Inventory( + project=inventory_project, version=inventory_version +) +``` + +The objects inventory. + +### seen_handlers + +```python +seen_handlers: Iterable[BaseHandler] +``` + +Get the handlers that were encountered so far throughout the build. + +Returns: + +- `Iterable[BaseHandler]` – An iterable of instances of BaseHandler +- `Iterable[BaseHandler]` – (usable only to loop through it). + +### get_handler + +```python +get_handler( + name: str, handler_config: dict | None = None +) -> BaseHandler +``` + +Get a handler thanks to its name. + +This function dynamically imports a module named "mkdocstrings.handlers.NAME", calls its `get_handler` method to get an instance of a handler, and caches it in dictionary. It means that during one run (for each reload when serving, or once when building), a handler is instantiated only once, and reused for each "autodoc" instruction asking for it. + +Parameters: + +- #### **`name`** + + (`str`) – The name of the handler. Really, it's the name of the Python module holding it. + +- #### **`handler_config`** + + (`dict | None`, default: `None` ) – Configuration passed to the handler. + +Returns: + +- `BaseHandler` – An instance of a subclass of BaseHandler, as instantiated by the get_handler method of the handler's module. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_handler(self, name: str, handler_config: dict | None = None) -> BaseHandler: + """Get a handler thanks to its name. + + This function dynamically imports a module named "mkdocstrings.handlers.NAME", calls its + `get_handler` method to get an instance of a handler, and caches it in dictionary. + It means that during one run (for each reload when serving, or once when building), + a handler is instantiated only once, and reused for each "autodoc" instruction asking for it. + + Arguments: + name: The name of the handler. Really, it's the name of the Python module holding it. + handler_config: Configuration passed to the handler. + + Returns: + An instance of a subclass of [`BaseHandler`][mkdocstrings.BaseHandler], + as instantiated by the `get_handler` method of the handler's module. + """ + if name not in self._handlers: + if handler_config is None: + handler_config = self._handlers_config.get(name, {}) + module = importlib.import_module(f"mkdocstrings_handlers.{name}") + + self._handlers[name] = module.get_handler( + theme=self._theme, + custom_templates=self._custom_templates, + mdx=self._mdx, + mdx_config=self._mdx_config, + handler_config=handler_config, + tool_config=self._tool_config, + ) + return self._handlers[name] +``` + +### get_handler_config + +```python +get_handler_config(name: str) -> dict +``` + +Return the global configuration of the given handler. + +Parameters: + +- #### **`name`** + + (`str`) – The name of the handler to get the global configuration of. + +Returns: + +- `dict` – The global configuration of the given handler. It can be an empty dictionary. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_handler_config(self, name: str) -> dict: + """Return the global configuration of the given handler. + + Arguments: + name: The name of the handler to get the global configuration of. + + Returns: + The global configuration of the given handler. It can be an empty dictionary. + """ + return self._handlers_config.get(name, None) or {} +``` + +### get_handler_name + +```python +get_handler_name(config: dict) -> str +``` + +Return the handler name defined in an "autodoc" instruction YAML configuration, or the global default handler. + +Parameters: + +- #### **`config`** + + (`dict`) – A configuration dictionary, obtained from YAML below the "autodoc" instruction. + +Returns: + +- `str` – The name of the handler to use. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def get_handler_name(self, config: dict) -> str: + """Return the handler name defined in an "autodoc" instruction YAML configuration, or the global default handler. + + Arguments: + config: A configuration dictionary, obtained from YAML below the "autodoc" instruction. + + Returns: + The name of the handler to use. + """ + return config.get("handler", self._default) +``` + +### teardown + +```python +teardown() -> None +``` + +Teardown all cached handlers and clear the cache. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def teardown(self) -> None: + """Teardown all cached handlers and clear the cache.""" + for future in self._inv_futures: + future.cancel() + for handler in self.seen_handlers: + handler.teardown() + self._handlers.clear() +``` + +## HeadingShiftingTreeprocessor + +```python +HeadingShiftingTreeprocessor(md: Markdown, shift_by: int) +``` + +Bases: `Treeprocessor` + +Shift levels of all Markdown headings according to the configured base level. + +Parameters: + +- ### **`md`** + + (`Markdown`) – A markdown.Markdown instance. + +- ### **`shift_by`** + + (`int`) – The number of heading "levels" to add to every heading. + +Methods: + +- **`run`** – Shift the levels of all headings in the document. + +Attributes: + +- **`name`** (`str`) – The name of the treeprocessor. +- **`regex`** (`Pattern`) – The regex to match heading tags. +- **`shift_by`** (`int`) – The number of heading "levels" to add to every heading.

with shift_by = 3 becomes

. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def __init__(self, md: Markdown, shift_by: int): + """Initialize the object. + + Arguments: + md: A `markdown.Markdown` instance. + shift_by: The number of heading "levels" to add to every heading. + """ + super().__init__(md) + self.shift_by = shift_by +``` + +### name + +```python +name: str = 'mkdocstrings_headings' +``` + +The name of the treeprocessor. + +### regex + +```python +regex: Pattern = compile('([Hh])([1-6])') +``` + +The regex to match heading tags. + +### shift_by + +```python +shift_by: int = shift_by +``` + +The number of heading "levels" to add to every heading. `

` with `shift_by = 3` becomes `

`. + +### run + +```python +run(root: Element) -> None +``` + +Shift the levels of all headings in the document. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def run(self, root: Element) -> None: + """Shift the levels of all headings in the document.""" + if not self.shift_by: + return + for el in root.iter(): + match = self.regex.fullmatch(el.tag) + if match: + level = int(match[2]) + self.shift_by + level = max(1, min(level, 6)) + el.tag = f"{match[1]}{level}" +``` + +## Highlighter + +```python +Highlighter(md: Markdown) +``` + +Bases: `Highlight` + +Code highlighter that tries to match the Markdown configuration. + +Picking up the global config and defaults works only if you use the `codehilite` or `pymdownx.highlight` (recommended) Markdown extension. + +- If you use `pymdownx.highlight`, highlighting settings are picked up from it, and the default CSS class is `.highlight`. This also means the default of `guess_lang: false`. +- Otherwise, if you use the `codehilite` extension, settings are picked up from it, and the default CSS class is `.codehilite`. Also consider setting `guess_lang: false`. +- If neither are added to `markdown_extensions`, highlighting is enabled anyway. This is for backwards compatibility. If you really want to disable highlighting even in *mkdocstrings*, add one of these extensions anyway and set `use_pygments: false`. + +The underlying implementation is `pymdownx.highlight` regardless. + +Parameters: + +- ### **`md`** + + (`Markdown`) – The Markdown instance to read configs from. + +Methods: + +- **`highlight`** – Highlight a code-snippet. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def __init__(self, md: Markdown): + """Configure to match a `markdown.Markdown` instance. + + Arguments: + md: The Markdown instance to read configs from. + """ + config: dict[str, Any] = {} + self._highlighter: str | None = None + for ext in md.registeredExtensions: + if isinstance(ext, HighlightExtension) and (ext.enabled or not config): + self._highlighter = "highlight" + config = ext.getConfigs() + break # This one takes priority, no need to continue looking + if isinstance(ext, CodeHiliteExtension) and not config: + self._highlighter = "codehilite" + config = ext.getConfigs() + config["language_prefix"] = config["lang_prefix"] + self._css_class = config.pop("css_class", "highlight") + super().__init__(**{name: opt for name, opt in config.items() if name in self._highlight_config_keys}) +``` + +### highlight + +```python +highlight( + src: str, + language: str | None = None, + *, + inline: bool = False, + dedent: bool = True, + linenums: bool | None = None, + **kwargs: Any, +) -> str +``` + +Highlight a code-snippet. + +Parameters: + +- #### **`src`** + + (`str`) – The code to highlight. + +- #### **`language`** + + (`str | None`, default: `None` ) – Explicitly tell what language to use for highlighting. + +- #### **`inline`** + + (`bool`, default: `False` ) – Whether to highlight as inline. + +- #### **`dedent`** + + (`bool`, default: `True` ) – Whether to dedent the code before highlighting it or not. + +- #### **`linenums`** + + (`bool | None`, default: `None` ) – Whether to add line numbers in the result. + +- #### **`**kwargs`** + + (`Any`, default: `{}` ) – Pass on to pymdownx.highlight.Highlight.highlight. + +Returns: + +- `str` – The highlighted code as HTML text, marked safe (not escaped for HTML). + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def highlight( + self, + src: str, + language: str | None = None, + *, + inline: bool = False, + dedent: bool = True, + linenums: bool | None = None, + **kwargs: Any, +) -> str: + """Highlight a code-snippet. + + Arguments: + src: The code to highlight. + language: Explicitly tell what language to use for highlighting. + inline: Whether to highlight as inline. + dedent: Whether to dedent the code before highlighting it or not. + linenums: Whether to add line numbers in the result. + **kwargs: Pass on to `pymdownx.highlight.Highlight.highlight`. + + Returns: + The highlighted code as HTML text, marked safe (not escaped for HTML). + """ + if isinstance(src, Markup): + src = src.unescape() + if dedent: + src = textwrap.dedent(src) + + kwargs.setdefault("css_class", self._css_class) + old_linenums = self.linenums # type: ignore[has-type] + if linenums is not None: + self.linenums = linenums + try: + result = super().highlight(src, language, inline=inline, **kwargs) + finally: + self.linenums = old_linenums + + if inline: + # From the maintainer of codehilite, the codehilite CSS class, as defined by the user, + # should never be added to inline code, because codehilite does not support inline code. + # See https://github.com/Python-Markdown/markdown/issues/1220#issuecomment-1692160297. + css_class = "" if self._highlighter == "codehilite" else kwargs["css_class"] + return Markup(f'{result.text}') + return Markup(result) +``` + +## IdPrependingTreeprocessor + +```python +IdPrependingTreeprocessor(md: Markdown, id_prefix: str) +``` + +Bases: `Treeprocessor` + +Prepend the configured prefix to IDs of all HTML elements. + +Parameters: + +- ### **`md`** + + (`Markdown`) – A markdown.Markdown instance. + +- ### **`id_prefix`** + + (`str`) – The prefix to add to every ID. It is prepended without any separator. + +Methods: + +- **`run`** – Prepend the configured prefix to all IDs in the document. + +Attributes: + +- **`id_prefix`** (`str`) – The prefix to add to every ID. It is prepended without any separator; specify your own separator if needed. +- **`name`** (`str`) – The name of the treeprocessor. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def __init__(self, md: Markdown, id_prefix: str): + """Initialize the object. + + Arguments: + md: A `markdown.Markdown` instance. + id_prefix: The prefix to add to every ID. It is prepended without any separator. + """ + super().__init__(md) + self.id_prefix = id_prefix +``` + +### id_prefix + +```python +id_prefix: str = id_prefix +``` + +The prefix to add to every ID. It is prepended without any separator; specify your own separator if needed. + +### name + +```python +name: str = 'mkdocstrings_ids' +``` + +The name of the treeprocessor. + +### run + +```python +run(root: Element) -> None +``` + +Prepend the configured prefix to all IDs in the document. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def run(self, root: Element) -> None: + """Prepend the configured prefix to all IDs in the document.""" + if self.id_prefix: + self._prefix_ids(root) +``` + +## Inventory + +```python +Inventory( + items: list[InventoryItem] | None = None, + project: str = "project", + version: str = "0.0.0", +) +``` + +Bases: `dict` + +Inventory of collected and rendered objects. + +Parameters: + +- ### **`items`** + + (`list[InventoryItem] | None`, default: `None` ) – A list of items. + +- ### **`project`** + + (`str`, default: `'project'` ) – The project name. + +- ### **`version`** + + (`str`, default: `'0.0.0'` ) – The project version. + +Methods: + +- **`format_sphinx`** – Format this inventory as a Sphinx objects.inv file. +- **`parse_sphinx`** – Parse a Sphinx v2 inventory file and return an Inventory from it. +- **`register`** – Create and register an item. + +Attributes: + +- **`project`** – The project name. +- **`version`** – The project version. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +def __init__(self, items: list[InventoryItem] | None = None, project: str = "project", version: str = "0.0.0"): + """Initialize the object. + + Arguments: + items: A list of items. + project: The project name. + version: The project version. + """ + super().__init__() + items = items or [] + for item in items: + self[item.name] = item + self.project = project + """The project name.""" + self.version = version + """The project version.""" +``` + +### project + +```python +project = project +``` + +The project name. + +### version + +```python +version = version +``` + +The project version. + +### format_sphinx + +```python +format_sphinx() -> bytes +``` + +Format this inventory as a Sphinx `objects.inv` file. + +Returns: + +- `bytes` – The inventory as bytes. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +def format_sphinx(self) -> bytes: + """Format this inventory as a Sphinx `objects.inv` file. + + Returns: + The inventory as bytes. + """ + header = ( + dedent( + f""" + # Sphinx inventory version 2 + # Project: {self.project} + # Version: {self.version} + # The remainder of this file is compressed using zlib. + """, + ) + .lstrip() + .encode("utf8") + ) + + lines = [ + item.format_sphinx().encode("utf8") + for item in sorted(self.values(), key=lambda item: (item.domain, item.name)) + ] + return header + zlib.compress(b"\n".join(lines) + b"\n", 9) +``` + +### parse_sphinx + +```python +parse_sphinx( + in_file: BinaryIO, + *, + domain_filter: Collection[str] = (), +) -> Inventory +``` + +Parse a Sphinx v2 inventory file and return an `Inventory` from it. + +Parameters: + +- #### **`in_file`** + + (`BinaryIO`) – The binary file-like object to read from. + +- #### **`domain_filter`** + + (`Collection[str]`, default: `()` ) – A collection of domain values to allow (and filter out all other ones). + +Returns: + +- `Inventory` – An inventory containing the collected items. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +@classmethod +def parse_sphinx(cls, in_file: BinaryIO, *, domain_filter: Collection[str] = ()) -> Inventory: + """Parse a Sphinx v2 inventory file and return an `Inventory` from it. + + Arguments: + in_file: The binary file-like object to read from. + domain_filter: A collection of domain values to allow (and filter out all other ones). + + Returns: + An inventory containing the collected items. + """ + for _ in range(4): + in_file.readline() + lines = zlib.decompress(in_file.read()).splitlines() + items: list[InventoryItem] = [ + item for line in lines if (item := InventoryItem.parse_sphinx(line.decode("utf8"), return_none=True)) + ] + if domain_filter: + items = [item for item in items if item.domain in domain_filter] + return cls(items) +``` + +### register + +```python +register( + name: str, + domain: str, + role: str, + uri: str, + priority: int = 1, + dispname: str | None = None, +) -> None +``` + +Create and register an item. + +Parameters: + +- #### **`name`** + + (`str`) – The item name. + +- #### **`domain`** + + (`str`) – The item domain, like 'python' or 'crystal'. + +- #### **`role`** + + (`str`) – The item role, like 'class' or 'method'. + +- #### **`uri`** + + (`str`) – The item URI. + +- #### **`priority`** + + (`int`, default: `1` ) – The item priority. Only used internally by mkdocstrings and Sphinx. + +- #### **`dispname`** + + (`str | None`, default: `None` ) – The item display name. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +def register( + self, + name: str, + domain: str, + role: str, + uri: str, + priority: int = 1, + dispname: str | None = None, +) -> None: + """Create and register an item. + + Arguments: + name: The item name. + domain: The item domain, like 'python' or 'crystal'. + role: The item role, like 'class' or 'method'. + uri: The item URI. + priority: The item priority. Only used internally by mkdocstrings and Sphinx. + dispname: The item display name. + """ + self[name] = InventoryItem( + name=name, + domain=domain, + role=role, + uri=uri, + priority=priority, + dispname=dispname, + ) +``` + +## InventoryItem + +```python +InventoryItem( + name: str, + domain: str, + role: str, + uri: str, + priority: int = 1, + dispname: str | None = None, +) +``` + +Inventory item. + +Parameters: + +- ### **`name`** + + (`str`) – The item name. + +- ### **`domain`** + + (`str`) – The item domain, like 'python' or 'crystal'. + +- ### **`role`** + + (`str`) – The item role, like 'class' or 'method'. + +- ### **`uri`** + + (`str`) – The item URI. + +- ### **`priority`** + + (`int`, default: `1` ) – The item priority. Only used internally by mkdocstrings and Sphinx. + +- ### **`dispname`** + + (`str | None`, default: `None` ) – The item display name. + +Methods: + +- **`format_sphinx`** – Format this item as a Sphinx inventory line. +- **`parse_sphinx`** – Parse a line from a Sphinx v2 inventory file and return an InventoryItem from it. + +Attributes: + +- **`dispname`** (`str`) – The item display name. +- **`domain`** (`str`) – The item domain. +- **`name`** (`str`) – The item name. +- **`priority`** (`int`) – The item priority. +- **`role`** (`str`) – The item role. +- **`sphinx_item_regex`** – Regex to parse a Sphinx v2 inventory line. +- **`uri`** (`str`) – The item URI. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +def __init__( + self, + name: str, + domain: str, + role: str, + uri: str, + priority: int = 1, + dispname: str | None = None, +): + """Initialize the object. + + Arguments: + name: The item name. + domain: The item domain, like 'python' or 'crystal'. + role: The item role, like 'class' or 'method'. + uri: The item URI. + priority: The item priority. Only used internally by mkdocstrings and Sphinx. + dispname: The item display name. + """ + self.name: str = name + """The item name.""" + self.domain: str = domain + """The item domain.""" + self.role: str = role + """The item role.""" + self.uri: str = uri + """The item URI.""" + self.priority: int = priority + """The item priority.""" + self.dispname: str = dispname or name + """The item display name.""" +``` + +### dispname + +```python +dispname: str = dispname or name +``` + +The item display name. + +### domain + +```python +domain: str = domain +``` + +The item domain. + +### name + +```python +name: str = name +``` + +The item name. + +### priority + +```python +priority: int = priority +``` + +The item priority. + +### role + +```python +role: str = role +``` + +The item role. + +### sphinx_item_regex + +```python +sphinx_item_regex = compile( + "^(.+?)\\s+(\\S+):(\\S+)\\s+(-?\\d+)\\s+(\\S+)\\s*(.*)$" +) +``` + +Regex to parse a Sphinx v2 inventory line. + +### uri + +```python +uri: str = uri +``` + +The item URI. + +### format_sphinx + +```python +format_sphinx() -> str +``` + +Format this item as a Sphinx inventory line. + +Returns: + +- `str` – A line formatted for an objects.inv file. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +def format_sphinx(self) -> str: + """Format this item as a Sphinx inventory line. + + Returns: + A line formatted for an `objects.inv` file. + """ + dispname = self.dispname + if dispname == self.name: + dispname = "-" + uri = self.uri + if uri.endswith(self.name): + uri = uri[: -len(self.name)] + "$" + return f"{self.name} {self.domain}:{self.role} {self.priority} {uri} {dispname}" +``` + +### parse_sphinx + +```python +parse_sphinx( + line: str, *, return_none: Literal[False] +) -> InventoryItem +``` + +```python +parse_sphinx( + line: str, *, return_none: Literal[True] +) -> InventoryItem | None +``` + +```python +parse_sphinx( + line: str, *, return_none: bool = False +) -> InventoryItem | None +``` + +Parse a line from a Sphinx v2 inventory file and return an `InventoryItem` from it. + +Source code in `src/mkdocstrings/_internal/inventory.py` + +```python +@classmethod +def parse_sphinx(cls, line: str, *, return_none: bool = False) -> InventoryItem | None: + """Parse a line from a Sphinx v2 inventory file and return an `InventoryItem` from it.""" + match = cls.sphinx_item_regex.search(line) + if not match: + if return_none: + return None + raise ValueError(line) + name, domain, role, priority, uri, dispname = match.groups() + if uri.endswith("$"): + uri = uri[:-1] + name + if dispname == "-": + dispname = name + return cls(name, domain, role, uri, int(priority), dispname) +``` + +## LoggerAdapter + +```python +LoggerAdapter(prefix: str, logger: Logger) +``` + +Bases: `LoggerAdapter` + +A logger adapter to prefix messages. + +This adapter also adds an additional parameter to logging methods called `once`: if `True`, the message will only be logged once. + +Examples: + +In Python code: + +```pycon +>>> logger = get_logger("myplugin") +>>> logger.debug("This is a debug message.") +>>> logger.info("This is an info message.", once=True) +``` + +In Jinja templates (logger available in context as `log`): + +```jinja +{{ log.debug("This is a debug message.") }} +{{ log.info("This is an info message.", once=True) }} +``` + +Parameters: + +- ### **`prefix`** + + (`str`) – The string to insert in front of every message. + +- ### **`logger`** + + (`Logger`) – The logger instance. + +Methods: + +- **`log`** – Log a message. +- **`process`** – Process the message. + +Attributes: + +- **`prefix`** – The prefix to insert in front of every message. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def __init__(self, prefix: str, logger: logging.Logger): + """Initialize the object. + + Arguments: + prefix: The string to insert in front of every message. + logger: The logger instance. + """ + super().__init__(logger, {}) + self.prefix = prefix + """The prefix to insert in front of every message.""" + self._logged: set[tuple[LoggerAdapter, str]] = set() +``` + +### prefix + +```python +prefix = prefix +``` + +The prefix to insert in front of every message. + +### log + +```python +log( + level: int, msg: object, *args: object, **kwargs: object +) -> None +``` + +Log a message. + +Parameters: + +- #### **`level`** + + (`int`) – The logging level. + +- #### **`msg`** + + (`object`) – The message. + +- #### **`*args`** + + (`object`, default: `()` ) – Additional arguments passed to parent method. + +- #### **`**kwargs`** + + (`object`, default: `{}` ) – Additional keyword arguments passed to parent method. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def log(self, level: int, msg: object, *args: object, **kwargs: object) -> None: + """Log a message. + + Arguments: + level: The logging level. + msg: The message. + *args: Additional arguments passed to parent method. + **kwargs: Additional keyword arguments passed to parent method. + """ + if kwargs.pop("once", False): + if (key := (self, str(msg))) in self._logged: + return + self._logged.add(key) + super().log(level, msg, *args, **kwargs) # type: ignore[arg-type] +``` + +### process + +```python +process( + msg: str, kwargs: MutableMapping[str, Any] +) -> tuple[str, Any] +``` + +Process the message. + +Parameters: + +- #### **`msg`** + + (`str`) – The message: + +- #### **`kwargs`** + + (`MutableMapping[str, Any]`) – Remaining arguments. + +Returns: + +- `tuple[str, Any]` – The processed message. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def process(self, msg: str, kwargs: MutableMapping[str, Any]) -> tuple[str, Any]: + """Process the message. + + Arguments: + msg: The message: + kwargs: Remaining arguments. + + Returns: + The processed message. + """ + return f"{self.prefix}: {msg}", kwargs +``` + +## MkdocstringsExtension + +```python +MkdocstringsExtension( + handlers: Handlers, + autorefs: AutorefsPlugin, + **kwargs: Any, +) +``` + +Bases: `Extension` + +Our Markdown extension. + +It cannot work outside of `mkdocstrings`. + +Parameters: + +- ### **`handlers`** + + (`Handlers`) – The handlers container. + +- ### **`autorefs`** + + (`AutorefsPlugin`) – The autorefs plugin instance. + +- ### **`**kwargs`** + + (`Any`, default: `{}` ) – Keyword arguments used by markdown.extensions.Extension. + +Methods: + +- **`extendMarkdown`** – Register the extension. + +Source code in `src/mkdocstrings/_internal/extension.py` + +```python +def __init__(self, handlers: Handlers, autorefs: AutorefsPlugin, **kwargs: Any) -> None: + """Initialize the object. + + Arguments: + handlers: The handlers container. + autorefs: The autorefs plugin instance. + **kwargs: Keyword arguments used by `markdown.extensions.Extension`. + """ + super().__init__(**kwargs) + self._handlers = handlers + self._autorefs = autorefs +``` + +### extendMarkdown + +```python +extendMarkdown(md: Markdown) -> None +``` + +Register the extension. + +Add an instance of our AutoDocProcessor to the Markdown parser. + +Parameters: + +- #### **`md`** + + (`Markdown`) – A markdown.Markdown instance. + +Source code in `src/mkdocstrings/_internal/extension.py` + +```python +def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent method's name) + """Register the extension. + + Add an instance of our [`AutoDocProcessor`][mkdocstrings.AutoDocProcessor] to the Markdown parser. + + Arguments: + md: A `markdown.Markdown` instance. + """ + md.parser.blockprocessors.register( + AutoDocProcessor(md, handlers=self._handlers, autorefs=self._autorefs), + "mkdocstrings", + priority=75, # Right before markdown.blockprocessors.HashHeaderProcessor + ) + md.treeprocessors.register( + _HeadingsPostProcessor(md), + "mkdocstrings_post_headings", + priority=4, # Right after 'toc'. + ) + md.treeprocessors.register( + _TocLabelsTreeProcessor(md), + "mkdocstrings_post_toc_labels", + priority=4, # Right after 'toc'. + ) +``` + +## MkdocstringsInnerExtension + +```python +MkdocstringsInnerExtension(headings: list[Element]) +``` + +Bases: `Extension` + +Extension that should always be added to Markdown sub-documents that handlers request (and *only* them). + +Parameters: + +- ### **`headings`** + + (`list[Element]`) – A list that will be populated with all HTML heading elements encountered in the document. + +Methods: + +- **`extendMarkdown`** – Register the extension. + +Attributes: + +- **`headings`** – The list that will be populated with all HTML heading elements encountered in the document. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def __init__(self, headings: list[Element]): + """Initialize the object. + + Arguments: + headings: A list that will be populated with all HTML heading elements encountered in the document. + """ + super().__init__() + self.headings = headings + """The list that will be populated with all HTML heading elements encountered in the document.""" +``` + +### headings + +```python +headings = headings +``` + +The list that will be populated with all HTML heading elements encountered in the document. + +### extendMarkdown + +```python +extendMarkdown(md: Markdown) -> None +``` + +Register the extension. + +Parameters: + +- #### **`md`** + + (`Markdown`) – A markdown.Markdown instance. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent method's name) + """Register the extension. + + Arguments: + md: A `markdown.Markdown` instance. + """ + md.registerExtension(self) + md.treeprocessors.register( + HeadingShiftingTreeprocessor(md, 0), + HeadingShiftingTreeprocessor.name, + priority=12, + ) + md.treeprocessors.register( + IdPrependingTreeprocessor(md, ""), + IdPrependingTreeprocessor.name, + priority=4, # Right after 'toc' (needed because that extension adds ids to headers). + ) + md.treeprocessors.register( + _HeadingReportingTreeprocessor(md, self.headings), + _HeadingReportingTreeprocessor.name, + priority=1, # Close to the end. + ) + md.treeprocessors.register( + ParagraphStrippingTreeprocessor(md), + ParagraphStrippingTreeprocessor.name, + priority=0.99, # Close to the end. + ) +``` + +## MkdocstringsPlugin + +```python +MkdocstringsPlugin() +``` + +Bases: `BasePlugin[PluginConfig]` + +An `mkdocs` plugin. + +This plugin defines the following event hooks: + +- `on_config` +- `on_env` +- `on_post_build` + +Check the [Developing Plugins](https://www.mkdocs.org/user-guide/plugins/#developing-plugins) page of `mkdocs` for more information about its plugin system. + +Methods: + +- **`get_handler`** – Get a handler by its name. See mkdocstrings.Handlers.get_handler. +- **`on_config`** – Instantiate our Markdown extension. +- **`on_post_build`** – Teardown the handlers. + +Attributes: + +- **`css_filename`** (`str`) – The path of the CSS file to write in the site directory. +- **`handlers`** (`Handlers`) – Get the instance of mkdocstrings.Handlers for this plugin/build. +- **`inventory_enabled`** (`bool`) – Tell if the inventory is enabled or not. +- **`on_env`** – Extra actions that need to happen after all Markdown-to-HTML page rendering. +- **`plugin_enabled`** (`bool`) – Tell if the plugin is enabled or not. + +Source code in `src/mkdocstrings/_internal/plugin.py` + +```python +def __init__(self) -> None: + """Initialize the object.""" + super().__init__() + self._handlers: Handlers | None = None +``` + +### css_filename + +```python +css_filename: str = 'assets/_mkdocstrings.css' +``` + +The path of the CSS file to write in the site directory. + +### handlers + +```python +handlers: Handlers +``` + +Get the instance of mkdocstrings.Handlers for this plugin/build. + +Raises: + +- `RuntimeError` – If the plugin hasn't been initialized with a config. + +Returns: + +- `Handlers` – An instance of mkdocstrings.Handlers (the same throughout the build). + +### inventory_enabled + +```python +inventory_enabled: bool +``` + +Tell if the inventory is enabled or not. + +Returns: + +- `bool` – Whether the inventory is enabled. + +### on_env + +```python +on_env = CombinedEvent( + _on_env_load_inventories, + _on_env_add_css, + _on_env_write_inventory, + _on_env_apply_backlinks, +) +``` + +Extra actions that need to happen after all Markdown-to-HTML page rendering. + +Hook for the [`on_env` event](https://www.mkdocs.org/user-guide/plugins/#on_env). + +- Gather results from background inventory download tasks. +- Write mkdocstrings' extra files (CSS, inventory) into the site directory. +- Apply backlinks to the HTML output of each page. + +### plugin_enabled + +```python +plugin_enabled: bool +``` + +Tell if the plugin is enabled or not. + +Returns: + +- `bool` – Whether the plugin is enabled. + +### get_handler + +```python +get_handler(handler_name: str) -> BaseHandler +``` + +Get a handler by its name. See mkdocstrings.Handlers.get_handler. + +Parameters: + +- #### **`handler_name`** + + (`str`) – The name of the handler. + +Returns: + +- `BaseHandler` – An instance of a subclass of BaseHandler. + +Source code in `src/mkdocstrings/_internal/plugin.py` + +```python +def get_handler(self, handler_name: str) -> BaseHandler: + """Get a handler by its name. See [mkdocstrings.Handlers.get_handler][]. + + Arguments: + handler_name: The name of the handler. + + Returns: + An instance of a subclass of [`BaseHandler`][mkdocstrings.BaseHandler]. + """ + return self.handlers.get_handler(handler_name) +``` + +### on_config + +```python +on_config(config: MkDocsConfig) -> MkDocsConfig | None +``` + +Instantiate our Markdown extension. + +Hook for the [`on_config` event](https://www.mkdocs.org/user-guide/plugins/#on_config). In this hook, we instantiate our MkdocstringsExtension and add it to the list of Markdown extensions used by `mkdocs`. + +We pass this plugin's configuration dictionary to the extension when instantiating it (it will need it later when processing markdown to get handlers and their global configurations). + +Parameters: + +- #### **`config`** + + (`MkDocsConfig`) – The MkDocs config object. + +Returns: + +- `MkDocsConfig | None` – The modified config. + +Source code in `src/mkdocstrings/_internal/plugin.py` + +```python +def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: + """Instantiate our Markdown extension. + + Hook for the [`on_config` event](https://www.mkdocs.org/user-guide/plugins/#on_config). + In this hook, we instantiate our [`MkdocstringsExtension`][mkdocstrings.MkdocstringsExtension] + and add it to the list of Markdown extensions used by `mkdocs`. + + We pass this plugin's configuration dictionary to the extension when instantiating it (it will need it + later when processing markdown to get handlers and their global configurations). + + Arguments: + config: The MkDocs config object. + + Returns: + The modified config. + """ + if not self.plugin_enabled: + _logger.debug("Plugin is not enabled. Skipping.") + return config + _logger.debug("Adding extension to the list") + + locale = self.config.locale or config.theme.get("language") or config.theme.get("locale") or "en" + locale = str(locale).replace("_", "-") + + handlers = Handlers( + default=self.config.default_handler, + handlers_config=self.config.handlers, + theme=config.theme.name or os.path.dirname(config.theme.dirs[0]), + custom_templates=self.config.custom_templates, + mdx=config.markdown_extensions, + mdx_config=config.mdx_configs, + inventory_project=config.site_name, + inventory_version="0.0.0", # TODO: Find a way to get actual version. + locale=locale, + tool_config=config, + ) + + handlers._download_inventories() + + AutorefsPlugin.record_backlinks = True + autorefs: AutorefsPlugin + try: + # If autorefs plugin is explicitly enabled, just use it. + autorefs = config.plugins["autorefs"] # type: ignore[assignment] + _logger.debug("Picked up existing autorefs instance %r", autorefs) + except KeyError: + # Otherwise, add a limited instance of it that acts only on what's added through `register_anchor`. + autorefs = AutorefsPlugin() + autorefs.config = AutorefsConfig() + autorefs.scan_toc = False + config.plugins["autorefs"] = autorefs + _logger.debug("Added a subdued autorefs instance %r", autorefs) + + mkdocstrings_extension = MkdocstringsExtension(handlers, autorefs) + config.markdown_extensions.append(mkdocstrings_extension) # type: ignore[arg-type] + + config.extra_css.insert(0, self.css_filename) # So that it has lower priority than user files. + + self._autorefs = autorefs + self._handlers = handlers + return config +``` + +### on_post_build + +```python +on_post_build(config: MkDocsConfig, **kwargs: Any) -> None +``` + +Teardown the handlers. + +Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build). This hook is used to teardown all the handlers that were instantiated and cached during documentation buildup. + +For example, a handler could open a subprocess in the background and keep it open to feed it "autodoc" instructions and get back JSON data. If so, it should then close the subprocess at some point: the proper place to do this is in the handler's `teardown` method, which is indirectly called by this hook. + +Parameters: + +- #### **`config`** + + (`MkDocsConfig`) – The MkDocs config object. + +- #### **`**kwargs`** + + (`Any`, default: `{}` ) – Additional arguments passed by MkDocs. + +Source code in `src/mkdocstrings/_internal/plugin.py` + +```python +def on_post_build( + self, + config: MkDocsConfig, # noqa: ARG002 + **kwargs: Any, # noqa: ARG002 +) -> None: + """Teardown the handlers. + + Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build). + This hook is used to teardown all the handlers that were instantiated and cached during documentation buildup. + + For example, a handler could open a subprocess in the background and keep it open + to feed it "autodoc" instructions and get back JSON data. If so, it should then close the subprocess at some point: + the proper place to do this is in the handler's `teardown` method, which is indirectly called by this hook. + + Arguments: + config: The MkDocs config object. + **kwargs: Additional arguments passed by MkDocs. + """ + if not self.plugin_enabled: + return + + if self._handlers: + _logger.debug("Tearing handlers down") + self.handlers.teardown() +``` + +## ParagraphStrippingTreeprocessor + +Bases: `Treeprocessor` + +Unwraps the `

` element around the whole output. + +Methods: + +- **`run`** – Unwrap the root element if it's a single

element. + +Attributes: + +- **`name`** (`str`) – The name of the treeprocessor. +- **`strip`** (`bool`) – Whether to strip

elements or not. + +### name + +```python +name: str = 'mkdocstrings_strip_paragraph' +``` + +The name of the treeprocessor. + +### strip + +```python +strip: bool = False +``` + +Whether to strip `

` elements or not. + +### run + +```python +run(root: Element) -> Element | None +``` + +Unwrap the root element if it's a single `

` element. + +Source code in `src/mkdocstrings/_internal/handlers/rendering.py` + +```python +def run(self, root: Element) -> Element | None: + """Unwrap the root element if it's a single `

` element.""" + if self.strip and len(root) == 1 and root[0].tag == "p": + # Turn the single `

` element into the root element and inherit its tag name (it's significant!) + root[0].tag = root.tag + return root[0] + return None +``` + +## PluginConfig + +Bases: `Config` + +The configuration options of `mkdocstrings`, written in `mkdocs.yml`. + +Attributes: + +- **`custom_templates`** – Location of custom templates to use when rendering API objects. +- **`default_handler`** – The default handler to use. The value is the name of the handler module. Default is "python". +- **`enable_inventory`** – Whether to enable object inventory creation. +- **`enabled`** – Whether to enable the plugin. Default is true. If false, mkdocstrings will not collect or render anything. +- **`handlers`** – Global configuration of handlers. +- **`locale`** – The locale to use for translations. + +### custom_templates + +```python +custom_templates = Optional(Dir(exists=True)) +``` + +Location of custom templates to use when rendering API objects. + +Value should be the path of a directory relative to the MkDocs configuration file. + +### default_handler + +```python +default_handler = Type(str, default='python') +``` + +The default handler to use. The value is the name of the handler module. Default is "python". + +### enable_inventory + +```python +enable_inventory = Optional(Type(bool)) +``` + +Whether to enable object inventory creation. + +### enabled + +```python +enabled = Type(bool, default=True) +``` + +Whether to enable the plugin. Default is true. If false, *mkdocstrings* will not collect or render anything. + +### handlers + +```python +handlers = Type(dict, default={}) +``` + +Global configuration of handlers. + +You can set global configuration per handler, applied everywhere, but overridable in each "autodoc" instruction. Example: + +```yaml +plugins: + - mkdocstrings: + handlers: + python: + options: + option1: true + option2: "value" + rust: + options: + option9: 2 +``` + +### locale + +```python +locale = Optional(Type(str)) +``` + +The locale to use for translations. + +## TemplateLogger + +```python +TemplateLogger(logger: LoggerAdapter) +``` + +A wrapper class to allow logging in templates. + +The logging methods provided by this class all accept two parameters: + +- `msg`: The message to log. +- `once`: If `True`, the message will only be logged once. + +Methods: + +- **`debug`** – Function to log a DEBUG message. +- **`info`** – Function to log an INFO message. +- **`warning`** – Function to log a WARNING message. +- **`error`** – Function to log an ERROR message. +- **`critical`** – Function to log a CRITICAL message. + +Parameters: + +- ### **`logger`** + + (`LoggerAdapter`) – A logger adapter. + +Attributes: + +- **`critical`** – Log a CRITICAL message. +- **`debug`** – Log a DEBUG message. +- **`error`** – Log an ERROR message. +- **`info`** – Log an INFO message. +- **`warning`** – Log a WARNING message. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def __init__(self, logger: LoggerAdapter): + """Initialize the object. + + Arguments: + logger: A logger adapter. + """ + self.debug = get_template_logger_function(logger.debug) + """Log a DEBUG message.""" + self.info = get_template_logger_function(logger.info) + """Log an INFO message.""" + self.warning = get_template_logger_function(logger.warning) + """Log a WARNING message.""" + self.error = get_template_logger_function(logger.error) + """Log an ERROR message.""" + self.critical = get_template_logger_function(logger.critical) + """Log a CRITICAL message.""" +``` + +### critical + +```python +critical = get_template_logger_function(critical) +``` + +Log a CRITICAL message. + +### debug + +```python +debug = get_template_logger_function(debug) +``` + +Log a DEBUG message. + +### error + +```python +error = get_template_logger_function(error) +``` + +Log an ERROR message. + +### info + +```python +info = get_template_logger_function(info) +``` + +Log an INFO message. + +### warning + +```python +warning = get_template_logger_function(warning) +``` + +Log a WARNING message. + +## ThemeNotSupported + +Bases: `Exception` + +An exception raised to tell a theme is not supported. + +## do_any + +```python +do_any(seq: Sequence, attribute: str | None = None) -> bool +``` + +Check if at least one of the item in the sequence evaluates to true. + +The `any` builtin as a filter for Jinja templates. + +Parameters: + +- ### **`seq`** + + (`Sequence`) – An iterable object. + +- ### **`attribute`** + + (`str | None`, default: `None` ) – The attribute name to use on each object of the iterable. + +Returns: + +- `bool` – A boolean telling if any object of the iterable evaluated to True. + +Source code in `src/mkdocstrings/_internal/handlers/base.py` + +```python +def do_any(seq: Sequence, attribute: str | None = None) -> bool: + """Check if at least one of the item in the sequence evaluates to true. + + The `any` builtin as a filter for Jinja templates. + + Arguments: + seq: An iterable object. + attribute: The attribute name to use on each object of the iterable. + + Returns: + A boolean telling if any object of the iterable evaluated to True. + """ + if attribute is None: + return any(seq) + return any(_[attribute] for _ in seq) +``` + +## get_logger + +```python +get_logger(name: str) -> LoggerAdapter +``` + +Return a pre-configured logger. + +Parameters: + +- ### **`name`** + + (`str`) – The name to use with logging.getLogger. + +Returns: + +- `LoggerAdapter` – A logger configured to work well in MkDocs. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def get_logger(name: str) -> LoggerAdapter: + """Return a pre-configured logger. + + Arguments: + name: The name to use with `logging.getLogger`. + + Returns: + A logger configured to work well in MkDocs. + """ + logger = logging.getLogger(f"mkdocs.plugins.{name}") + return LoggerAdapter(name.split(".", 1)[0], logger) +``` + +## get_template_logger + +```python +get_template_logger( + handler_name: str | None = None, +) -> TemplateLogger +``` + +Return a logger usable in templates. + +Parameters: + +- ### **`handler_name`** + + (`str | None`, default: `None` ) – The name of the handler. + +Returns: + +- `TemplateLogger` – A template logger. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def get_template_logger(handler_name: str | None = None) -> TemplateLogger: + """Return a logger usable in templates. + + Parameters: + handler_name: The name of the handler. + + Returns: + A template logger. + """ + handler_name = handler_name or "base" + return TemplateLogger(get_logger(f"mkdocstrings_handlers.{handler_name}.templates")) +``` + +## get_template_logger_function + +```python +get_template_logger_function( + logger_func: Callable, +) -> Callable +``` + +Create a wrapper function that automatically receives the Jinja template context. + +Parameters: + +- ### **`logger_func`** + + (`Callable`) – The logger function to use within the wrapper. + +Returns: + +- `Callable` – A function. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def get_template_logger_function(logger_func: Callable) -> Callable: + """Create a wrapper function that automatically receives the Jinja template context. + + Arguments: + logger_func: The logger function to use within the wrapper. + + Returns: + A function. + """ + + @pass_context + def wrapper(context: Context, msg: str | None = None, *args: Any, **kwargs: Any) -> str: + """Log a message. + + Arguments: + context: The template context, automatically provided by Jinja. + msg: The message to log. + **kwargs: Additional arguments passed to the logger function. + + Returns: + An empty string. + """ + logger_func(f"%s: {msg or 'Rendering'}", _Lazy(get_template_path, context), *args, **kwargs) + return "" + + return wrapper +``` + +## get_template_path + +```python +get_template_path(context: Context) -> str +``` + +Return the path to the template currently using the given context. + +Parameters: + +- ### **`context`** + + (`Context`) – The template context. + +Returns: + +- `str` – The relative path to the template. + +Source code in `src/mkdocstrings/_internal/loggers.py` + +```python +def get_template_path(context: Context) -> str: + """Return the path to the template currently using the given context. + + Arguments: + context: The template context. + + Returns: + The relative path to the template. + """ + context_name: str = str(context.name) + filename = context.environment.get_template(context_name).filename + if filename: + for template_dir in TEMPLATES_DIRS: + with suppress(ValueError): + return str(Path(filename).relative_to(template_dir)) + with suppress(ValueError): + return str(Path(filename).relative_to(Path.cwd())) + return filename + return context_name +``` + +## makeExtension + +```python +makeExtension( + *, + default_handler: str | None = None, + inventory_project: str | None = None, + inventory_version: str | None = None, + handlers: dict[str, dict] | None = None, + custom_templates: str | None = None, + markdown_extensions: list[str | dict] | None = None, + locale: str | None = None, + config_file_path: str | None = None, +) -> MkdocstringsExtension +``` + +Create the extension instance. + +We only support this function being used by Zensical. Consider this function private API. + +Source code in `src/mkdocstrings/_internal/extension.py` + +```python +def makeExtension( # noqa: N802 + *, + default_handler: str | None = None, + inventory_project: str | None = None, + inventory_version: str | None = None, + handlers: dict[str, dict] | None = None, + custom_templates: str | None = None, + markdown_extensions: list[str | dict] | None = None, + locale: str | None = None, + config_file_path: str | None = None, +) -> MkdocstringsExtension: + """Create the extension instance. + + We only support this function being used by Zensical. + Consider this function private API. + """ + mdx, mdx_config = _split_configs(markdown_extensions or []) + tool_config = _ToolConfig(config_file_path=config_file_path) + + handlers_instance = Handlers( + theme="material", + default=default_handler or _default_config["default_handler"], + inventory_project=inventory_project or "Project", + inventory_version=inventory_version or "0.0.0", + handlers_config=handlers or _default_config["handlers"], + custom_templates=custom_templates or _default_config["custom_templates"], + mdx=mdx, + mdx_config=mdx_config, + locale=locale or _default_config["locale"], + tool_config=tool_config, + ) + + handlers_instance._download_inventories() + + autorefs = AutorefsPlugin() + autorefs.config = AutorefsConfig() + autorefs.scan_toc = False + + return MkdocstringsExtension(handlers=handlers_instance, autorefs=autorefs) +``` diff --git a/reference/extension/index.html b/reference/extension/index.html new file mode 100644 index 00000000..f9aa3b46 --- /dev/null +++ b/reference/extension/index.html @@ -0,0 +1,14 @@ + + + + + + Redirecting... + + + + + +You're being redirected to a new destination. + + diff --git a/reference/handlers/base/index.html b/reference/handlers/base/index.html new file mode 100644 index 00000000..c0d0516c --- /dev/null +++ b/reference/handlers/base/index.html @@ -0,0 +1,14 @@ + + + + + + Redirecting... + + + + + +You're being redirected to a new destination. + + diff --git a/reference/handlers/index.html b/reference/handlers/index.html new file mode 100644 index 00000000..b447cdae --- /dev/null +++ b/reference/handlers/index.html @@ -0,0 +1,14 @@ + + + + + + Redirecting... + + + + + +You're being redirected to a new destination. + + diff --git a/reference/handlers/rendering/index.html b/reference/handlers/rendering/index.html new file mode 100644 index 00000000..700e995b --- /dev/null +++ b/reference/handlers/rendering/index.html @@ -0,0 +1,14 @@ + + + + + + Redirecting... + + + + + +You're being redirected to a new destination. + + diff --git a/reference/index.html b/reference/index.html new file mode 100644 index 00000000..fe9104e3 --- /dev/null +++ b/reference/index.html @@ -0,0 +1,14 @@ + + + + + + Redirecting... + + + + + +You're being redirected to a new destination. + + diff --git a/reference/inventory/index.html b/reference/inventory/index.html new file mode 100644 index 00000000..e060d9f3 --- /dev/null +++ b/reference/inventory/index.html @@ -0,0 +1,14 @@ + + + + + + Redirecting... + + + + + +You're being redirected to a new destination. + + diff --git a/reference/loggers/index.html b/reference/loggers/index.html new file mode 100644 index 00000000..fa489efc --- /dev/null +++ b/reference/loggers/index.html @@ -0,0 +1,14 @@ + + + + + + Redirecting... + + + + + +You're being redirected to a new destination. + + diff --git a/reference/mkdocstrings/index.html b/reference/mkdocstrings/index.html new file mode 100644 index 00000000..291df342 --- /dev/null +++ b/reference/mkdocstrings/index.html @@ -0,0 +1,14 @@ + + + + + + Redirecting... + + + + + +You're being redirected to a new destination. + + diff --git a/reference/plugin/index.html b/reference/plugin/index.html new file mode 100644 index 00000000..434f53d8 --- /dev/null +++ b/reference/plugin/index.html @@ -0,0 +1,14 @@ + + + + + + Redirecting... + + + + + +You're being redirected to a new destination. + + diff --git a/docs/schema.json b/schema.json similarity index 100% rename from docs/schema.json rename to schema.json diff --git a/scripts/gen_credits.py b/scripts/gen_credits.py deleted file mode 100644 index e8712895..00000000 --- a/scripts/gen_credits.py +++ /dev/null @@ -1,178 +0,0 @@ -# Script to generate the project's credits. - -from __future__ import annotations - -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 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.cwd() -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, 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: Iterable[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) - metadata[name] = _merge_fields(pkg.metadata) # ty: ignore[invalid-argument-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 = [ - classifier.rsplit("::", 1)[1].strip() - for classifier in metadata["classifier"] - if classifier.startswith("License ::") - ] - 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": - continue - metadata[dep_name]["spec"] |= {str(spec) for spec in dep_req.specifier} # ty: ignore[unsupported-operator] - metadata[dep_name]["extras"] |= dep_req.extras # ty: ignore[unsupported-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} # ty: ignore[unsupported-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( - 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()) diff --git a/scripts/get_version.py b/scripts/get_version.py deleted file mode 100644 index d56f5858..00000000 --- a/scripts/get_version.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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 ( # ty: ignore[unresolved-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))) # ty: ignore[invalid-argument-type] - scm_version = scm_version._replace(version=Version(match.group(1))) - return default_version_formatter(scm_version) - - -if __name__ == "__main__": - print(get_version()) diff --git a/scripts/make b/scripts/make deleted file mode 120000 index c2eda0df..00000000 --- a/scripts/make +++ /dev/null @@ -1 +0,0 @@ -make.py \ No newline at end of file diff --git a/scripts/make.py b/scripts/make.py deleted file mode 100755 index 7fa7b56d..00000000 --- a/scripts/make.py +++ /dev/null @@ -1,224 +0,0 @@ -#!/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.10 3.11 3.12 3.13 3.14 3.15").split() -PYTHON_DEV = "3.15" - - -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) - - -class _RunError(subprocess.CalledProcessError): - def __init__(self, *args: Any, python_version: str, **kwargs: Any): - super().__init__(*args, **kwargs) - self.python_version = python_version - - -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"] - try: - 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 - except subprocess.CalledProcessError as process: - raise _RunError( - returncode=process.returncode, - python_version=version, - cmd=process.cmd, - output=process.output, - stderr=process.stderr, - ) from process - - -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", ".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 Path(".venv").exists(): - print("\nAvailable tasks", flush=True) - run("default", "duty", "--list") - return 0 - - while args: - cmd = args.pop(0) - - if cmd == "run": - if not args: - print("make: run: missing command", file=sys.stderr) - return 1 - run("default", *args) - return 0 - - if cmd == "multirun": - if not args: - print("make: run: missing command", file=sys.stderr) - return 1 - multirun(*args) - return 0 - - if cmd == "allrun": - if not args: - print("make: run: missing command", file=sys.stderr) - return 1 - allrun(*args) - return 0 - - if cmd.startswith("3."): - if not args: - print("make: run: missing command", file=sys.stderr) - return 1 - 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 _RunError as process: - if process.output: - print(process.output, file=sys.stderr) - if (code := process.returncode) == 139: # noqa: PLR2004 - print( - f"✗ (python{process.python_version}) '{' '.join(process.cmd)}' failed with return code {code} (segfault)", - file=sys.stderr, - ) - if process.python_version == PYTHON_DEV: - code = 0 - sys.exit(code) diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 00000000..12d68c55 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"mkdocstrings","text":"

Automatic documentation from sources, for MkDocs. Come have a chat or ask questions on our Gitter channel.

Features - Installation - Quick usage

"},{"location":"#features","title":"Features","text":"
  • Language-agnostic: just like MkDocs, mkdocstrings is written in Python but is language-agnostic. It means you can use it with any programming language, as long as there is a handler for it. We currently have handlers for the C, Crystal, GitHub Actions, Python, MATLAB, TypeScript, and VBA languages, as well as for shell scripts/libraries. Maybe you'd like to add another one to the list?

  • Multiple themes support: each handler can offer multiple themes. Currently, we offer the Material theme as well as basic support for the ReadTheDocs and MkDocs themes for the Python handler.

  • Cross-references across pages: mkdocstrings makes it possible to reference headings in other Markdown files with the classic Markdown linking syntax: [identifier][] or [title][identifier] -- and you don't need to remember which exact page this object was on. This works for any heading that's produced by a mkdocstrings language handler, and you can opt to include any Markdown heading into the global referencing scheme.

    Note: in versions prior to 0.15 all Markdown headers were included, but now you need to opt in.

  • Cross-references across sites: similarly to Sphinx's intersphinx extension, mkdocstrings can reference API items from other libraries, given they provide an inventory and you load that inventory in your MkDocs configuration.

  • Inline injection in Markdown: instead of generating Markdown files, mkdocstrings allows you to inject documentation anywhere in your Markdown contents. The syntax is simple: ::: identifier followed by a 4-spaces indented YAML block. The identifier and YAML configuration will be passed to the appropriate handler to collect and render documentation.

  • Global and local configuration: each handler can be configured globally in mkdocs.yml, and locally for each \"autodoc\" instruction.

  • Reasonable defaults: you should be able to just drop the plugin in your configuration and enjoy your auto-generated docs.

"},{"location":"#used-by","title":"Used by","text":"

mkdocstrings is used by well-known companies, projects and scientific teams: Ansible, Apache, FastAPI, Google, IBM, Jitsi, Microsoft, NVIDIA, Prefect, Pydantic, Textual, and more...

"},{"location":"#installation","title":"Installation","text":"

The mkdocstrings package doesn't provide support for any language: it's just a common base for language handlers. It means you likely want to install it with one or more official handlers, using extras. For example, to install it with Python support:

pip install 'mkdocstrings[python]'\n

Alternatively, you can directly install the language handlers themselves, which depend on mkdocstrings anyway:

pip install mkdocstrings-python\n

This will give you more control over the accepted range of versions for the handlers themselves.

See the official language handlers.

With conda:

conda install -c conda-forge mkdocstrings mkdocstrings-python\n
"},{"location":"#quick-usage","title":"Quick usage","text":"

In mkdocs.yml:

site_name: \"My Library\"\n\ntheme:\n  name: \"material\"\n\nplugins:\n- search\n- mkdocstrings\n

In one of your markdown files:

# Reference\n\n::: my_library.my_module.my_class\n

See the Usage section of the docs for more examples!

"},{"location":"#sponsors","title":"Sponsors","text":"Silver sponsors Bronze sponsors

And 8 more private sponsor(s).

"},{"location":"changelog/","title":"Changelog","text":"

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog and this project adheres to Semantic Versioning.

"},{"location":"changelog/#100-2025-11-27","title":"1.0.0 - 2025-11-27","text":"

Compare with 0.30.1

"},{"location":"changelog/#breaking-changes","title":"Breaking Changes","text":"
  • BaseHandler.name: Attribute value was changed: '' -> unset
  • BaseHandler.domain: Attribute value was changed: '' -> unset
  • BaseHandler.fallback_config: Public object was removed
  • BaseHandler.__init__(args): Parameter was removed
  • BaseHandler.__init__(kwargs): Parameter was removed
  • BaseHandler.__init__(theme): Parameter was added as required
  • BaseHandler.__init__(custom_templates): Parameter was added as required
  • BaseHandler.__init__(mdx): Parameter was added as required
  • BaseHandler.__init__(mdx_config): Parameter was added as required
  • BaseHandler.update_env(args): Parameter was removed
  • BaseHandler.update_env(kwargs): Parameter was removed
  • BaseHandler.update_env(config): Parameter was added as required
  • Handlers.get_anchors: Public object was removed (import from mkdocstrings directly)
  • mkdocstrings.plugin: Public module was removed (import from mkdocstrings directly)
  • mkdocstrings.loggers: Public module was removed (import from mkdocstrings directly)
  • mkdocstrings.inventory: Public module was removed (import from mkdocstrings directly)
  • mkdocstrings.extension: Public module was removed (import from mkdocstrings directly)
  • mkdocstrings.handlers: Public module was removed (import from mkdocstrings directly)
"},{"location":"changelog/#code-refactoring","title":"Code Refactoring","text":"
  • Remove deprecated code before v1 (de34044 by Timoth\u00e9e Mazzucotelli).
  • Expect Zensical to pass extension configuration instead of loading it again from YAML (6b73d5a by Timoth\u00e9e Mazzucotelli).
  • Expose the Markdown extension, to make mkdocstrings compatible with Zensical (6de2667 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0301-2025-09-19","title":"0.30.1 - 2025-09-19","text":"

Compare with 0.30.0

"},{"location":"changelog/#bug-fixes","title":"Bug Fixes","text":"
  • Create default SSL context in main thread before downloading inventories (eec7fb4 by \u00c7a\u011flar Kutlu). Issue-796, PR-797
"},{"location":"changelog/#0300-2025-07-23","title":"0.30.0 - 2025-07-23","text":"

Compare with 0.29.1

"},{"location":"changelog/#features","title":"Features","text":"
  • Add data-skip-inventory boolean attribute for elements to skip registration in local inventory (f856160 by Bartosz S\u0142awecki). Issue-671, PR-774
  • Add I18N support (translations) (2b4ed54 by Nyuan Zhang). PR-645, Co-authored-by: Timoth\u00e9e Mazzucotelli dev@pawamoy.fr
"},{"location":"changelog/#0291-2025-03-31","title":"0.29.1 - 2025-03-31","text":"

Compare with 0.29.0

"},{"location":"changelog/#dependencies","title":"Dependencies","text":"
  • Remove unused typing-extensions dependency (ba98661 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#bug-fixes_1","title":"Bug Fixes","text":"
  • Ignore invalid inventory lines (81caff5 by Josh Mitchell). PR-748
"},{"location":"changelog/#code-refactoring_1","title":"Code Refactoring","text":"
  • Rename loggers to \"mkdocstrings\" (1a98040 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0290-2025-03-10","title":"0.29.0 - 2025-03-10","text":"

Compare with 0.28.3

This is the last version before v1!

"},{"location":"changelog/#build","title":"Build","text":"
  • Depend on MkDocs 1.6 (11bc400 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#features_1","title":"Features","text":"
  • Support rendering backlinks through handlers (d4c7b9c by Timoth\u00e9e Mazzucotelli). Issue-723, Issue-mkdocstrings-python-153, PR-739
"},{"location":"changelog/#code-refactoring_2","title":"Code Refactoring","text":"
  • Save and forward titles to autorefs (f49fb29 by Timoth\u00e9e Mazzucotelli).
  • Use a combined event (each split with a different priority) for on_env (8d1dd75 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0283-2025-03-08","title":"0.28.3 - 2025-03-08","text":"

Compare with 0.28.2

"},{"location":"changelog/#deprecations","title":"Deprecations","text":"

All public objects must now be imported from the top-level mkdocstrings module. Importing from submodules is deprecated, and will raise errors starting with v1. This should be the last deprecation before v1.

"},{"location":"changelog/#build_1","title":"Build","text":"
  • Make python extra depend on latest mkdocstrings-python (1.16.2) (ba9003e by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#code-refactoring_3","title":"Code Refactoring","text":"
  • Finish exposing/hiding public/internal objects (0723fc2 by Timoth\u00e9e Mazzucotelli).
  • Re-expose public API in the top-level mkdocstrings module (e66e080 by Timoth\u00e9e Mazzucotelli).
  • Move modules to internal folder (23fe23f by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0282-2025-02-24","title":"0.28.2 - 2025-02-24","text":"

Compare with 0.28.1

"},{"location":"changelog/#build_2","title":"Build","text":"
  • Depend on mkdocs-autorefs >= 1.4 (2c22bdc by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0281-2025-02-14","title":"0.28.1 - 2025-02-14","text":"

Compare with 0.28.0

"},{"location":"changelog/#bug-fixes_2","title":"Bug Fixes","text":"
  • Renew MkDocs' relpath processor instead of using same instance (4ab180d by Timoth\u00e9e Mazzucotelli). Issue-mkdocs-3919
"},{"location":"changelog/#0280-2025-02-03","title":"0.28.0 - 2025-02-03","text":"

Compare with 0.27.0

"},{"location":"changelog/#breaking-changes_1","title":"Breaking Changes","text":"

Although the following changes are \"breaking\" in terms of public API, we didn't find any public use of these classes and methods on GitHub.

  • mkdocstrings.extension.AutoDocProcessor.__init__(parser): Parameter was removed
  • mkdocstrings.extension.AutoDocProcessor.__init__(md): Positional parameter was moved
  • mkdocstrings.extension.AutoDocProcessor.__init__(config): Parameter was removed
  • mkdocstrings.extension.AutoDocProcessor.__init__(handlers): Parameter kind was changed: positional or keyword -> keyword-only
  • mkdocstrings.extension.AutoDocProcessor.__init__(autorefs): Parameter kind was changed: positional or keyword -> keyword-only
  • mkdocstrings.extension.MkdocstringsExtension.__init__(config): Parameter was removed
  • mkdocstrings.extension.MkdocstringsExtension.__init__(handlers): Positional parameter was moved
  • mkdocstrings.extension.MkdocstringsExtension.__init__(autorefs): Positional parameter was moved
  • mkdocstrings.handlers.base.Handlers.__init__(config): Parameter was removed
  • mkdocstrings.handlers.base.Handlers.__init__(theme): Parameter was added as required
  • mkdocstrings.handlers.base.Handlers.__init__(default): Parameter was added as required
  • mkdocstrings.handlers.base.Handlers.__init__(inventory_project): Parameter was added as required
  • mkdocstrings.handlers.base.Handlers.__init__(tool_config): Parameter was added as required

Similarly, the following parameters were renamed, but the methods are only called from our own code, using positional arguments.

  • mkdocstrings.handlers.base.BaseHandler.collect(config): Parameter was renamed options
  • mkdocstrings.handlers.base.BaseHandler.render(config): Parameter was renamed options

Finally, the following method was removed, but this is again taken into account in our own code:

  • mkdocstrings.handlers.base.BaseHandler.get_anchors: Public object was removed

For these reasons, and because we're still in v0, we do not bump to v1 yet. See following deprecations.

"},{"location":"changelog/#deprecations_1","title":"Deprecations","text":"

mkdocstrings 0.28 will start emitting these deprecations warnings:

The handler argument is deprecated. The handler name must be specified as a class attribute.

Previously, the get_handler function would pass a handler (name) argument to the handler constructor. This name must now be set on the handler's class directly.

class MyHandler:\n    name = \"myhandler\"\n

The domain attribute must be specified as a class attribute.

The domain class attribute on handlers is now mandatory and cannot be an empty string.

class MyHandler:\n    domain = \"mh\"\n

The theme argument must be passed as a keyword argument.

This argument could previously be passed as a positional argument (from the get_handler function), and must now be passed as a keyword argument.

The custom_templates argument must be passed as a keyword argument.

Same as for theme, but with custom_templates.

The mdx argument must be provided (as a keyword argument).

The get_handler function now receives a mdx argument, which it must forward to the handler constructor and then to the base handler, either explicitly or through **kwargs:

ExplicitlyThrough **kwargs
def get_handler(..., mdx, ...):\n    return MyHandler(..., mdx=mdx, ...)\n\n\nclass MyHandler:\n    def __init__(self, ..., mdx, ...):\n        super().__init__(..., mdx=mdx, ...)\n
def get_handler(..., **kwargs):\n    return MyHandler(..., **kwargs)\n\n\nclass MyHandler:\n    def __init__(self, ..., **kwargs):\n        super().__init__(**kwargs)\n

In the meantime we still retrieve this mdx value at a different moment, by reading it from the MkDocs configuration.

The mdx_config argument must be provided (as a keyword argument).

Same as for mdx, but with mdx_config.

mkdocstrings v1 will stop handling 'import' in handlers configuration. Instead your handler must define a get_inventory_urls method that returns a list of URLs to download.

Previously, mkdocstrings would pop the import key from a handler's configuration to download each item (URLs). Items could be strings, or dictionaries with a url key. Now mkdocstrings gives back control to handlers, which must store this inventory configuration within them, and expose it again through a get_inventory_urls method. This method returns a list of tuples: an URL, and a dictionary of options that will be passed again to their load_inventory method. Handlers have now full control over the \"inventory\" setting.

from copy import deepcopy\n\n\ndef get_handler(..., handler_config, ...):\n    return MyHandler(..., config=handler_config, ...)\n\n\nclass MyHandler:\n    def __init__(self, ..., config, ...):\n        self.config = config\n\n    def get_inventory_urls(self):\n        config = deepcopy(self.config[\"import\"])\n        return [(inv, {}) if isinstance(inv, str) else (inv.pop(\"url\"), inv) for inv in config]\n

Changing the name of the key (for example from import to inventories) involves a change in user configuration, and both keys will have to be supported by your handler for some time.

def get_handler(..., handler_config, ...):\n    if \"inventories\" not in handler_config and \"import\" in handler_config:\n        warn(\"The 'import' key is renamed 'inventories'\", FutureWarning)\n        handler_config[\"inventories\"] = handler_config.pop(\"import\")\n    return MyHandler(..., config=handler_config, ...)\n

Setting a fallback anchor function is deprecated and will be removed in a future release.

This comes from mkdocstrings and mkdocs-autorefs, and will disappear with mkdocstrings v0.28.

mkdocstrings v1 will start using your handler's get_options method to build options instead of merging the global and local options (dictionaries).

Handlers must now store their own global options (in an instance attribute), and implement a get_options method that receives local_options (a dict) and returns combined options (dict or custom object). These combined options are then passed to collect and render, so that these methods can use them right away.

def get_handler(..., handler_config, ...):\n    return MyHandler(..., config=handler_config, ...)\n\n\nclass MyHandler:\n    def __init__(self, ..., config, ...):\n        self.config = config\n\n    def get_options(local_options):\n        return {**self.default_options, **self.config[\"options\"], **local_options}\n

The update_env(md) parameter is deprecated. Use self.md instead.

Handlers can remove the md parameter from their update_env method implementation, and use self.md instead, if they need it.

No need to call super().update_env() anymore.

Handlers don't have to call the parent update_env method from their own implementation anymore, and can just drop the call.

The get_anchors method is deprecated. Declare a get_aliases method instead, accepting a string (identifier) instead of a collected object.

Previously, handlers would implement a get_anchors method that received a data object (typed CollectorItem) to return aliases for this object. This forced mkdocstrings to collect this object through the handler's collect method, which then required some logic with \"fallback config\" as to prevent unwanted collection. mkdocstrings gives back control to handlers and now calls get_aliases instead, which accepts an identifier (string) and lets the handler decide how to return aliases for this identifier. For example, it can replicate previous behavior by calling its own collect method with its own \"fallback config\", or do something different (cache lookup, etc.).

class MyHandler:\n    def get_aliases(identifier):\n        try:\n            obj = self.collect(identifier, self.fallback_config)\n            # or obj = self._objects_cache[identifier]\n        except CollectionError:  # or KeyError\n            return ()\n        return ...  # previous logic in `get_anchors`\n

The config_file_path argument in get_handler functions is deprecated. Use tool_config.get('config_file_path') instead.

The config_file_path argument is now deprecated and only passed to get_handler functions if they accept it. If you used it to compute a \"base directory\", you can now use the tool_config argument instead, which is the configuration of the SSG tool in use (here MkDocs):

base_dir = Path(tool_config.config_file_path or \"./mkdocs.yml\").parent\n

Most of these warnings will disappear with the next version of mkdocstrings-python.

"},{"location":"changelog/#bug-fixes_3","title":"Bug Fixes","text":"
  • Update handlers in JSON schema to be an object instead of an array (3cf7d51 by Matthew Messinger). Issue-733, PR-734
  • Fix broken table of contents when nesting autodoc instructions (12c8f82 by Timoth\u00e9e Mazzucotelli). Issue-348
"},{"location":"changelog/#code-refactoring_4","title":"Code Refactoring","text":"
  • Pass config_file_path to get_handler if it expects it (8c476ee by Timoth\u00e9e Mazzucotelli).
  • Give back inventory control to handlers (b84653f by Timoth\u00e9e Mazzucotelli). Related-to-issue-719
  • Give back control to handlers on how they want to handle global/local options (c00de7a by Timoth\u00e9e Mazzucotelli). Issue-719
  • Deprecate base handler's get_anchors method in favor of get_aliases method (7a668f0 by Timoth\u00e9e Mazzucotelli).
  • Register all identifiers of rendered objects into autorefs (434d8c7 by Timoth\u00e9e Mazzucotelli).
  • Use mkdocs-get-deps' download utility to remove duplicated code (bb87cd8 by Timoth\u00e9e Mazzucotelli).
  • Clean up data passed down from plugin to extension and handlers (b8e8703 by Timoth\u00e9e Mazzucotelli). PR-726
"},{"location":"changelog/#0270-2024-11-08","title":"0.27.0 - 2024-11-08","text":"

Compare with 0.26.2

"},{"location":"changelog/#features_2","title":"Features","text":"
  • Add support for authentication in inventory file URLs (1c23c1b by Stefan Mejlgaard). Issue-707, PR-710
"},{"location":"changelog/#performance-improvements","title":"Performance Improvements","text":"
  • Reduce footprint of template debug messages (5648e5a by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#code-refactoring_5","title":"Code Refactoring","text":"
  • Use %-formatting for logging messages (0bbb8ca by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0262-2024-10-12","title":"0.26.2 - 2024-10-12","text":"

Compare with 0.26.1

"},{"location":"changelog/#build_3","title":"Build","text":"
  • Drop support for Python 3.8 (f26edeb by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0261-2024-09-06","title":"0.26.1 - 2024-09-06","text":"

Compare with 0.26.0

"},{"location":"changelog/#bug-fixes_4","title":"Bug Fixes","text":"
  • Instantiate config of the autorefs plugin when it is not enabled by the user (db2ab34 by Timoth\u00e9e Mazzucotelli). Issue-autorefs#57
"},{"location":"changelog/#0260-2024-09-02","title":"0.26.0 - 2024-09-02","text":"

Compare with 0.25.2

"},{"location":"changelog/#build_4","title":"Build","text":"
  • Upgrade Python-Markdown lower bound to 3.6 (28565f9 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#dependencies_1","title":"Dependencies","text":"
  • Depend on mkdocs-autorefs v1 (33aa573 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#features_3","title":"Features","text":"
  • Allow hooking into autorefs when converting Markdown docstrings (b63e726 by Timoth\u00e9e Mazzucotelli). Based-on-PR-autorefs#46
"},{"location":"changelog/#0252-2024-07-25","title":"0.25.2 - 2024-07-25","text":"

Compare with 0.25.1

"},{"location":"changelog/#code-refactoring_6","title":"Code Refactoring","text":"
  • Give precedence to Markdown heading level (##) (2e5f89e by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0251-2024-05-05","title":"0.25.1 - 2024-05-05","text":"

Compare with 0.25.0

"},{"location":"changelog/#bug-fixes_5","title":"Bug Fixes","text":"
  • Always descend into sub-headings when re-applying their label (cb86e08 by Timoth\u00e9e Mazzucotelli). Issue-mkdocstrings/python-158
"},{"location":"changelog/#0250-2024-04-27","title":"0.25.0 - 2024-04-27","text":"

Compare with 0.24.3

"},{"location":"changelog/#features_4","title":"Features","text":"
  • Support once parameter in logging methods, allowing to log a message only once with a given logger (1532b59 by Timoth\u00e9e Mazzucotelli).
  • Support blank line between ::: path and YAML options (d799d2f by Timoth\u00e9e Mazzucotelli). Issue-450
"},{"location":"changelog/#code-refactoring_7","title":"Code Refactoring","text":"
  • Allow specifying name of template loggers (c5b5f69 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0243-2024-04-05","title":"0.24.3 - 2024-04-05","text":"

Compare with 0.24.2

"},{"location":"changelog/#bug-fixes_6","title":"Bug Fixes","text":"
  • Support HTML toc labels with Python-Markdown 3.6+ (uncomment code...) (7fe3e5f by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0242-2024-04-02","title":"0.24.2 - 2024-04-02","text":"

Compare with 0.24.1

"},{"location":"changelog/#bug-fixes_7","title":"Bug Fixes","text":"
  • Support HTML toc labels with Python-Markdown 3.6+ (c0d0090 by Timoth\u00e9e Mazzucotelli). Issue-mkdocstrings/python-143
"},{"location":"changelog/#0241-2024-02-27","title":"0.24.1 - 2024-02-27","text":"

Compare with 0.24.0

"},{"location":"changelog/#code-refactoring_8","title":"Code Refactoring","text":"
  • Support new pymdownx-highlight options (a7a2907 by Timoth\u00e9e Mazzucotelli).
  • Backup anchors with id and no href, for compatibility with autorefs' Markdown anchors (b5236b4 by Timoth\u00e9e Mazzucotelli). PR-#651, Related-to-mkdocs-autorefs#39, Co-authored-by: Oleh Prypin oleh@pryp.in
"},{"location":"changelog/#0240-2023-11-14","title":"0.24.0 - 2023-11-14","text":"

Compare with 0.23.0

"},{"location":"changelog/#features_5","title":"Features","text":"
  • Cache downloaded inventories as local file (ce84dd5 by Oleh Prypin). PR #632
"},{"location":"changelog/#bug-fixes_8","title":"Bug Fixes","text":"
  • Make custom_templates relative to the config file (370a61d by Waylan Limberg). Issue #477, PR #627
  • Remove duplicated headings for docstrings nested in tabs/admonitions (e2123a9 by Perceval Wajsburt, f4a94f7 by Oleh Prypin). Issue #609, PR #610, PR #613
"},{"location":"changelog/#code-refactoring_9","title":"Code Refactoring","text":"
  • Drop support for MkDocs < 1.4, modernize usages (b61d4d1 by Oleh Prypin). PR #629
"},{"location":"changelog/#0230-2023-08-28","title":"0.23.0 - 2023-08-28","text":"

Compare with 0.22.0

"},{"location":"changelog/#breaking-changes_2","title":"Breaking Changes","text":"
  • Removed BaseCollector and BaseRenderer classes: they were merged into the BaseHandler class.
  • Removed the watch feature, as MkDocs now provides it natively.
  • Removed support for selection and rendering keys in YAML blocks: use options instead.
  • Removed support for loading handlers from the mkdocstrings.handler namespace. Handlers must now be packaged under the mkdocstrings_handlers namespace.
"},{"location":"changelog/#features_6","title":"Features","text":"
  • Register all anchors for each object in the inventory (228fb73 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#bug-fixes_9","title":"Bug Fixes","text":"
  • Don't add codehilite CSS class to inline code (7690d41 by Timoth\u00e9e Mazzucotelli).
  • Support cross-references for API docs rendered in top-level index page (b194452 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#code-refactoring_10","title":"Code Refactoring","text":"
  • Sort inventories before writing them to disk (9371e9f by Timoth\u00e9e Mazzucotelli).
  • Stop accepting sets as return value of get_anchors (only tuples), to preserve order (2e10374 by Timoth\u00e9e Mazzucotelli).
  • Remove deprecated parts (0a90a47 by Timoth\u00e9e Mazzucotelli).
  • Use proper parameters in Inventory.register method (433c6e0 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0220-2023-05-25","title":"0.22.0 - 2023-05-25","text":"

Compare with 0.21.2

"},{"location":"changelog/#features_7","title":"Features","text":"
  • Allow extensions to add templates (cf0af05 by Timoth\u00e9e Mazzucotelli). PR #569
"},{"location":"changelog/#code-refactoring_11","title":"Code Refactoring","text":"
  • Report inventory loading errors (2c05d78 by Timoth\u00e9e Mazzucotelli). Co-authored-by: Oleh Prypin oleh@pryp.in
"},{"location":"changelog/#0212-2023-04-06","title":"0.21.2 - 2023-04-06","text":"

Compare with 0.21.1

"},{"location":"changelog/#bug-fixes_10","title":"Bug Fixes","text":"
  • Fix regression with LRU cached method (85efbd2 by Timoth\u00e9e Mazzucotelli). Issue #549
"},{"location":"changelog/#0211-2023-04-05","title":"0.21.1 - 2023-04-05","text":"

Compare with 0.21.0

"},{"location":"changelog/#bug-fixes_11","title":"Bug Fixes","text":"
  • Fix missing typing-extensions dependency on Python less than 3.10 (bff760b by Timoth\u00e9e Mazzucotelli). Issue #548
"},{"location":"changelog/#0210-2023-04-05","title":"0.21.0 - 2023-04-05","text":"

Compare with 0.20.0

"},{"location":"changelog/#features_8","title":"Features","text":"
  • Expose the full config to handlers (15dacf6 by David Patterson). Issue #501, PR #509
"},{"location":"changelog/#0200-2023-01-19","title":"0.20.0 - 2023-01-19","text":"

Compare with 0.19.1

"},{"location":"changelog/#features_9","title":"Features","text":"
  • Add enabled configuration option (8cf117d by StefanBRas). Issue #478, PR #504
"},{"location":"changelog/#bug-fixes_12","title":"Bug Fixes","text":"
  • Handle updating Jinja environment of multiple handlers (a6ea80c by David Patterson). Related PR #201, Issue #502, PR #507
"},{"location":"changelog/#code-refactoring_12","title":"Code Refactoring","text":"
  • Make _load_inventory accept lists as arguments (105ed82 by Sorin Sbarnea). Needed by PR mkdocstrings/python#49, PR #511
  • Remove support for MkDocs < 1.2 (we already depended on MkDocs >= 1.2) (ac963c8 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0191-2022-12-13","title":"0.19.1 - 2022-12-13","text":"

Compare with 0.19.0

"},{"location":"changelog/#bug-fixes_13","title":"Bug Fixes","text":"
  • Fix regular expression for Sphinx inventory parsing (348bdd5 by Luis Michaelis). Issue #496, PR #497
"},{"location":"changelog/#code-refactoring_13","title":"Code Refactoring","text":"
  • Small fixes to type annotations (9214b74 by Oleh Prypin). PR #470
  • Report usage-based warnings as user-facing messages (03dd7a6 by Oleh Prypin). PR #464
"},{"location":"changelog/#0190-2022-05-28","title":"0.19.0 - 2022-05-28","text":"

Compare with 0.18.1

"},{"location":"changelog/#highlights","title":"Highlights","text":"

We decided to deprecate a few things to pave the way towards a more stable code base, bringing us closer to a v1.

  • Selection and rendering options are now combined into a single options key. Using the old keys will emit a deprecation warning.
  • The BaseCollector and BaseRenderer classes are deprecated in favor of BaseHandler, which merges their functionality. Using the old classes will emit a deprecation warning.

New versions of the Python handler and the legacy Python handler were also released in coordination with mkdocstrings 0.19. See their respective changelogs: python, python-legacy. Most notably, the Python handler gained the members and filters options that prevented many users to switch to it.

mkdocstrings stopped depending directly on the legacy Python handler. It means you now have to explicitely depend on it, directly or through the extra provided by mkdocstrings, if you want to continue using it.

"},{"location":"changelog/#packaging-dependencies","title":"Packaging / Dependencies","text":"
  • Stop depending directly on mkdocstrings-python-legacy (9055d58 by Timoth\u00e9e Mazzucotelli). Issue #376
"},{"location":"changelog/#features_10","title":"Features","text":"
  • Pass config file path to handlers (cccebc4 by Timoth\u00e9e Mazzucotelli). Issue #311, PR #425
"},{"location":"changelog/#code-refactoring_14","title":"Code Refactoring","text":"
  • Support options / deprecated options mix-up (7c71f26 by Timoth\u00e9e Mazzucotelli).
  • Deprecate watch feature in favor of MkDocs' built-in one (c20022e by Timoth\u00e9e Mazzucotelli).
  • Log relative template paths if possible, instead of absolute (91f5f83 by Timoth\u00e9e Mazzucotelli).
  • Deprecate selection and rendering YAML keys (3335310 by Timoth\u00e9e Mazzucotelli). PR #420
  • Deprecate BaseCollector and BaseRenderer (eb822cb by Timoth\u00e9e Mazzucotelli). PR #413
"},{"location":"changelog/#0181-2022-03-01","title":"0.18.1 - 2022-03-01","text":"

Compare with 0.18.0

"},{"location":"changelog/#bug-fixes_14","title":"Bug Fixes","text":"
  • Don't preemptively register identifiers as anchors (c7ac043 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0180-2022-02-06","title":"0.18.0 - 2022-02-06","text":"

Compare with 0.17.0

"},{"location":"changelog/#highlights_1","title":"Highlights","text":"
  • Python 3.6 support is dropped.
  • We provide a new, experimental Python handler based on Griffe. This new handler brings automatic cross-references for every annotation in your code, including references to third-party libraries' APIs if they provide objects inventories and you explicitely load them in mkdocs.yml. See migration notes in the documentation.
  • The \"legacy\" Python handler now lives in its own repository at https://github.com/mkdocstrings/python-legacy.
"},{"location":"changelog/#packaging-dependencies_1","title":"Packaging / Dependencies","text":"
  • Add Crystal extra, update Python extras versions (b8222b0 by Timoth\u00e9e Mazzucotelli). PR #374
  • Update autorefs to actually required version (fc6c7f6 by Timoth\u00e9e Mazzucotelli).
  • Drop Python 3.6 support (7205ac6 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#features_11","title":"Features","text":"
  • Allow unwrapping the <p> tag in convert_markdown filter (5351fc8 by Oleh Prypin). PR #369
  • Support handlers spanning multiple locations (f42dfc6 by Timoth\u00e9e Mazzucotelli). PR #355
"},{"location":"changelog/#code-refactoring_15","title":"Code Refactoring","text":"
  • Prefix logs with the package name only (6c2b734 by Timoth\u00e9e Mazzucotelli). PR #375
  • Extract the Python handler into its own repository (74371e4 by Timoth\u00e9e Mazzucotelli). PR #356
  • Support Jinja2 3.1 (b377227 by Timoth\u00e9e Mazzucotelli). Issue #360, PR #361
  • Find templates in new and deprecated namespaces (d5d5f18 by Timoth\u00e9e Mazzucotelli). PR #367
  • Support loading handlers from the mkdocstrings_handlers namespace (5c22c6c by Timoth\u00e9e Mazzucotelli). PR #367
"},{"location":"changelog/#0170-2021-12-27","title":"0.17.0 - 2021-12-27","text":"

Compare with 0.16.2

"},{"location":"changelog/#features_12","title":"Features","text":"
  • Add show_signature rendering option (024ee82 by Will Da Silva). Issue #341, PR #342
  • Support Keyword Args and Yields sections (1286427 by Timoth\u00e9e Mazzucotelli). Issue #205 and #324, PR #331
"},{"location":"changelog/#bug-fixes_15","title":"Bug Fixes","text":"
  • Do minimum work when falling back to re-collecting an object to get its anchor (f6cf570 by Timoth\u00e9e Mazzucotelli). Issue #329, PR #330
"},{"location":"changelog/#code-refactoring_16","title":"Code Refactoring","text":"
  • Return multiple identifiers from fallback method (78c498c by Timoth\u00e9e Mazzucotelli). Issue mkdocstrings/autorefs#11, PR #350
"},{"location":"changelog/#0162-2021-10-04","title":"0.16.2 - 2021-10-04","text":"

Compare with 0.16.1

"},{"location":"changelog/#dependencies_2","title":"Dependencies","text":"
  • Support pymdown-extensions v9.x (0831343 by Ofek Lev and 38b22ec by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0161-2021-09-23","title":"0.16.1 - 2021-09-23","text":"

Compare with 0.16.0

"},{"location":"changelog/#bug-fixes_16","title":"Bug Fixes","text":"
  • Fix ReadTheDocs \"return\" template (598621b by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#0160-2021-09-20","title":"0.16.0 - 2021-09-20","text":"

Compare with 0.15.0

"},{"location":"changelog/#features_13","title":"Features","text":"
  • Add a rendering option to change the sorting of members (b1fff8b by Joe Rickerby). Issue #114, PR #274
  • Add option to show Python base classes (436f550 by Brian Koropoff). Issue #269, PR #297
  • Support loading external Python inventories in Sphinx format (a8418cb by Oleh Prypin). PR #287
  • Support loading external inventories and linking to them (8b675f4 by Oleh Prypin). PR #277
  • Very basic support for MkDocs theme (974ca90 by Oleh Prypin). PR #272
  • Generate objects inventory (14ed959 and bbd85a9 by Timoth\u00e9e Mazzucotelli). Issue #251, PR #253
"},{"location":"changelog/#bug-fixes_17","title":"Bug Fixes","text":"
  • Don't render empty code blocks for missing type annotations (d2e9e1e by Oleh Prypin).
  • Fix custom handler not being used (6dcf342 by Timoth\u00e9e Mazzucotelli). Issue #259, PR #263
  • Don't hide setup_commands errors (92418c4 by Gabriel V\u00eejial\u0103). PR #258
"},{"location":"changelog/#code-refactoring_17","title":"Code Refactoring","text":"
  • Move writing extra files to an earlier stage in the build (3890ab5 by Oleh Prypin). PR #275
"},{"location":"changelog/#0152-2021-06-09","title":"0.15.2 - 2021-06-09","text":"

Compare with 0.15.1

"},{"location":"changelog/#packaging","title":"Packaging","text":"
  • MkDocs default schema needs to be obtained differently now (b3e122b by Oleh Prypin). PR #273
  • Compatibility with MkDocs 1.2: livereload isn't guaranteed now (36e8024 by Oleh Prypin). PR #294
"},{"location":"changelog/#0151-2021-05-16","title":"0.15.1 - 2021-05-16","text":"

Compare with 0.15.0

"},{"location":"changelog/#bug-fixes_18","title":"Bug Fixes","text":"
  • Prevent error during parallel installations (fac2c71 by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#packaging_1","title":"Packaging","text":"
  • Support the upcoming major Jinja and MarkupSafe releases (bb4f9de by Oleh Prypin). PR #283
  • Accept a higher version of mkdocs-autorefs (c8de08e by Oleh Prypin). PR #282
"},{"location":"changelog/#0150-2021-02-28","title":"0.15.0 - 2021-02-28","text":"

Compare with 0.14.0

"},{"location":"changelog/#breaking-changes_3","title":"Breaking Changes","text":"

The following items are possible breaking changes:

  • Cross-linking to arbitrary headings now requires to opt-in to the autorefs plugin, which is installed as a dependency of mkdocstrings. See Cross-references to any Markdown heading.
  • mkdocstrings now respects your configured code highlighting method, so if you are using the CodeHilite extension, the .highlight CSS class in the rendered HTML will become .codehilite. So make sure to adapt your extra CSS accordingly. Or just switch to using pymdownx.highlight, it's better supported by mkdocstrings anyway. See Syntax highlighting.
  • Most of the CSS rules that mkdocstrings used to recommend for manual addition, now become mandatory (auto-injected into the site). This shouldn't break any of your styles, but you are welcome to remove the now-redundant lines that you had copied into extra_css, similarly to this diff.
"},{"location":"changelog/#features_14","title":"Features","text":"
  • Nicer-looking error outputs - no tracebacks from mkdocstrings (6baf720 by Oleh Prypin). PR #230
  • Let handlers add CSS to the pages, do so for Python handler (05c7a3f by Oleh Prypin). Issue #189, PR #218
  • Allow linking to an object heading not only by its canonical identifier, but also by its possible aliases (4789950 by Oleh Prypin). PR #217
"},{"location":"changelog/#bug-fixes_19","title":"Bug Fixes","text":"
  • Propagate the CSS class to inline highlighting as well (c7d80e6 by Oleh Prypin). PR #245
  • Don't double-escape characters in highlighted headings (6357144 by Oleh Prypin). Issue #228, PR #241
"},{"location":"changelog/#code-refactoring_18","title":"Code Refactoring","text":"
  • Use the autorefs plugin from its new external location (e2d74ef by Oleh Prypin). PR #235
  • Split out Markdown extensions from handlers to handlers.rendering (7533852 by Oleh Prypin). PR #233
  • Theme-agnostic code highlighting, respecting configs (f9ea009 by Oleh Prypin). PR #202
  • Split out autorefs plugin, make it optional (fc67656 by Oleh Prypin). PR #220
  • Remove the extra wrapper div from the final doc (7fe438c by Oleh Prypin). PR #209
  • Don't re-parse the whole subdoc, expose only headings (15f84f9 by Oleh Prypin). PR #209
  • Actually exclude hidden headings from the doc (0fdb082 by Oleh Prypin). PR #209
"},{"location":"changelog/#0140-2021-01-06","title":"0.14.0 - 2021-01-06","text":"

Compare with 0.13.6

Special thanks to Oleh @oprypin Prypin who did an amazing job (this is a euphemism) at improving mkdocstrings, fixing hard-to-fix bugs with clever solutions, implementing great new features and refactoring the code for better performance and readability! Thanks Oleh!

"},{"location":"changelog/#bug-fixes_20","title":"Bug Fixes","text":"
  • Fix double code tags (e84d401 by Timoth\u00e9e Mazzucotelli).
  • Don't mutate the original Markdown config for permalinks (8f6b163 by Oleh Prypin).
  • Preserve text immediately before an autodoc (07466fa by Oleh Prypin). PR #207
  • Remove href attributes from headings in templates (d5602ff by Oleh Prypin). PR #204
  • Don't let toc extension append its permalink twice (a154f5c by Oleh Prypin). PR #203
  • Fix undefined entity for &para; (2c29211 by Timoth\u00e9e Mazzucotelli).
  • Make ids of Markdown sub-documents prefixed with the parent item id (d493d33 by Oleh Prypin). Issue #186 and #193, PR #199
  • More lenient regex for data-mkdocstrings-identifier (dcfec8e by Oleh Prypin).
  • Shift Markdown headings according to the current heading_level (13f41ae by Oleh Prypin). Issue #192, PR #195
  • Fix footnotes appearing in all following objects (af24bc2 by Oleh Prypin). Issue #186, PR #195
  • Fix cross-references from the root index page (9c9f2a0 by Oleh Prypin). Issue #184, PR #185
  • Fix incorrect argument name passed to Markdown (10ce502 by Timoth\u00e9e Mazzucotelli).
  • Fix error when a digit immediately follows a code tag (9b92341 by Oleh Prypin). Issue #169, PR #175
  • Detecting paths relative to template directory in logging (a50046b by Oleh Prypin). Issue #166
"},{"location":"changelog/#code-refactoring_19","title":"Code Refactoring","text":"
  • BlockProcessor already receives strings, use them as such (bcf7da9 by Oleh Prypin).
  • Remove some unused code (8504084 by Oleh Prypin). PR #206
  • Improve XML parsing error handling (ad86410 by Timoth\u00e9e Mazzucotelli).
  • Explicitly use MarkupSafe (6b9ebe7 by Oleh Prypin).
  • Split out the handler cache, expose it through the plugin (6453026 by Oleh Prypin). Issue #179, PR #191
  • Use ChainMap instead of copying dicts (c634d2c by Oleh Prypin). PR #171
  • Rename logging to loggers to avoid confusion (7a119cc by Timoth\u00e9e Mazzucotelli).
  • Simplify logging (409f93e by Timoth\u00e9e Mazzucotelli).
"},{"location":"changelog/#features_15","title":"Features","text":"
  • Allow specifying heading_level as a Markdown heading (10efc28 by Oleh Prypin). PR #170
  • Allow any characters in identifiers (7ede68a by Oleh Prypin). PR #174
  • Allow namespace packages for handlers (39b0465 by Timoth\u00e9e Mazzucotelli).
  • Add template debugging/logging (33b32c1 by Timoth\u00e9e Mazzucotelli).
  • Add initial support for the ReadTheDocs theme (1028115 by Timoth\u00e9e Mazzucotelli). Issue #107, PR #159
  • Add option to show type annotations in signatures (f94ce9b by Timoth\u00e9e Mazzucotelli). Issue #106
"},{"location":"changelog/#packaging_2","title":"Packaging","text":"
  • Accept verions of pytkdocs up to 0.10.x (see changelog).
"},{"location":"changelog/#performance-improvements_1","title":"Performance Improvements","text":"
  • Call update_env only once per Markdown instance (b198c74 by Oleh Prypin). PR #201
  • Disable Jinja's auto_reload to reduce disk reads (3b28c58 by Oleh Prypin). PR #200
  • Rework autorefs replacement to not re-parse the final HTML (22a9e4b by Oleh Prypin). Issue #187, PR #188
"},{"location":"changelog/#0136-2020-09-28","title":"0.13.6 - 2020-09-28","text":"

Compare with 0.13.5

"},{"location":"changelog/#bug-fixes_21","title":"Bug Fixes","text":"
  • Fix rendering when clicking on hidden toc entries (2af4d31 by Timoth\u00e9e Mazzucotelli). Issue #60.
"},{"location":"changelog/#0135-2020-09-28","title":"0.13.5 - 2020-09-28","text":"

Compare with 0.13.4

"},{"location":"changelog/#packaging_3","title":"Packaging","text":"
  • Accept pytkdocs version up to 0.9.x (changelog).
"},{"location":"changelog/#0134-2020-09-25","title":"0.13.4 - 2020-09-25","text":"

Compare with 0.13.3

"},{"location":"changelog/#bug-fixes_22","title":"Bug Fixes","text":"
  • Bring back arbitrary **config to Python handler (fca7d4c by Florimond Manca). Issue #154, PR #155
"},{"location":"changelog/#0133-2020-09-25","title":"0.13.3 - 2020-09-25","text":"

Compare with 0.13.2

"},{"location":"changelog/#packaging_4","title":"Packaging","text":"
  • Accept pytkdocs version up to 0.8.x (changelog).
"},{"location":"changelog/#0132-2020-09-08","title":"0.13.2 - 2020-09-08","text":"

Compare with 0.13.1

"},{"location":"changelog/#bug-fixes_23","title":"Bug Fixes","text":"
  • Fix relative URLs when use_directory_urls is false (421d189 by Timoth\u00e9e Mazzucotelli). References: #149
"},{"location":"changelog/#0131-2020-09-03","title":"0.13.1 - 2020-09-03","text":"

Compare with 0.13.0

"},{"location":"changelog/#bug-fixes_24","title":"Bug Fixes","text":"
  • Use relative links for cross-references (9c77f1f by Timoth\u00e9e Mazzucotelli). References: #144, #147
"},{"location":"changelog/#0130-2020-08-21","title":"0.13.0 - 2020-08-21","text":"

Compare with 0.12.2

"},{"location":"changelog/#bug-fixes_25","title":"Bug Fixes","text":"
  • Accept dashes in module names (fcf79d0 by Timoth\u00e9e Mazzucotelli). References: #140
"},{"location":"changelog/#features_16","title":"Features","text":"
  • Add option to show full path of direct members only (d1b9401 by Aaron Dunmore). References: #134, #136
"},{"location":"changelog/#packaging_5","title":"Packaging","text":"
  • Accept pymdown-extensions versions up to 0.8.x (see release notes) (178d48d by Hugo van Kemenade). PR #146
"},{"location":"changelog/#0122-2020-07-24","title":"0.12.2 - 2020-07-24","text":"

Compare with 0.12.1

"},{"location":"changelog/#packaging_6","title":"Packaging","text":"
  • Accept pytkdocs version up to 0.7.x (changelog).
"},{"location":"changelog/#0121-2020-07-07","title":"0.12.1 - 2020-07-07","text":"

Compare with 0.12.0

"},{"location":"changelog/#bug-fixes_26","title":"Bug Fixes","text":"
  • Fix HTML-escaped sequence parsing as XML (db297f1 by Timoth\u00e9e Mazzucotelli).
  • Allow running mkdocs from non-default interpreter (283dd7b by Jared Khan).
"},{"location":"changelog/#0120-2020-06-14","title":"0.12.0 - 2020-06-14","text":"

Compare with 0.11.4

"},{"location":"changelog/#features_17","title":"Features","text":"
  • Support attributes section in Google-style docstrings (8300253 by Timoth\u00e9e Mazzucotelli). References: #88
  • Support examples section in Google-style docstrings (650c754 by Iago Gonz\u00e1lez). References: #112
"},{"location":"changelog/#packaging_7","title":"Packaging","text":"
  • Accept pytkdocs version up to 0.6.x (changelog).
"},{"location":"changelog/#0114-2020-06-08","title":"0.11.4 - 2020-06-08","text":"

Compare with 0.11.3

"},{"location":"changelog/#packaging_8","title":"Packaging","text":"
  • Accept pytkdocs version up to 0.5.x (changelog). If it breaks your docs, please open issues on pytkdocs' bug-tracker, or pin pytkdocs version to while waiting for bug fixes <0.5.0 .
"},{"location":"changelog/#0113-2020-06-07","title":"0.11.3 - 2020-06-07","text":"

Compare with 0.11.2

"},{"location":"changelog/#bug-fixes_27","title":"Bug Fixes","text":"
  • Support custom theme directory configuration (1243cf6 by Abhishek Thakur). References: #120, #121
"},{"location":"changelog/#0112-2020-05-20","title":"0.11.2 - 2020-05-20","text":"

Compare with 0.11.1

"},{"location":"changelog/#packaging_9","title":"Packaging","text":"
  • Increase pytkdocs version range to accept 0.4.0 (changelog).
"},{"location":"changelog/#0111-2020-05-14","title":"0.11.1 - 2020-05-14","text":"

Compare with 0.11.0

"},{"location":"changelog/#bug-fixes_28","title":"Bug Fixes","text":"
  • Fix integration with mkdocs logging une bonne fois pour toute (3293cbf by Timoth\u00e9e Mazzucotelli).
  • Discard setup commands stdout (ea44cea by Timoth\u00e9e Mazzucotelli). References: #91
  • Use the proper python executable to start subprocesses (9fe3b39 by Reece Dunham). References: #91, #103
"},{"location":"changelog/#0110-2020-04-23","title":"0.11.0 - 2020-04-23","text":"

Compare with 0.10.3

"},{"location":"changelog/#bug-fixes_29","title":"Bug Fixes","text":"
  • Properly raise on errors (respect strict mode) (2097208 by Timoth\u00e9e Mazzucotelli). Related issues/PRs: #86
  • Hook properly to MkDocs logging (b23daed by Timoth\u00e9e Mazzucotelli). Related issues/PRs: #86
"},{"location":"changelog/#features_18","title":"Features","text":"
  • Add setup_commands option to python handler (599f8e5 by Ross Mechanic). Related issues/PRs: #89, #90
  • Add option to allow overriding templates (7360021 by Mika\u00ebl Capelle). Related issues/PRs: #59, #82
"},{"location":"changelog/#0103-2020-04-10","title":"0.10.3 - 2020-04-10","text":"

Compare with 0.10.2

"},{"location":"changelog/#bug-fixes_30","title":"Bug Fixes","text":"
  • Handle site_url not being defined (9fb4a9b by Timoth\u00e9e Mazzucotelli). Related issues/PRs: #77
"},{"location":"changelog/#packaging_10","title":"Packaging","text":"

This version increases the accepted range of versions for the pytkdocs dependency to >=0.2.0, <0.4.0. The pytkdocs project just released version 0.3.0 which:

  • adds support for complex markup in docstrings sections items descriptions
  • adds support for different indentations in docstrings sections (tabulations or less/more than 4 spaces)
  • fixes docstring parsing for arguments whose names start with *, like *args and **kwargs
"},{"location":"changelog/#0102-2020-04-07","title":"0.10.2 - 2020-04-07","text":"

Compare with 0.10.1

"},{"location":"changelog/#packaging_11","title":"Packaging","text":"

This version increases the accepted range of versions for the pymdown-extensions dependency, as well as for the mkdocs-material development dependency. Indeed, both these projects recently released major versions 7 and 5 respectively. Users who wish to use these new versions will be able to. See issue #74.

"},{"location":"changelog/#0101-2020-04-03","title":"0.10.1 - 2020-04-03","text":"

Compare with 0.10.0

"},{"location":"changelog/#bug-fixes_31","title":"Bug Fixes","text":"
  • Fix jinja2 error for jinja2 < 2.11 (387f970 by Timoth\u00e9e Mazzucotelli). Related issues/PRs: #67, #72
  • Fix missing dependency pymdown-extensions (648b99d by Timoth\u00e9e Mazzucotelli). Related issues/PRs: #66
  • Fix heading level of hidden toc entries (475cc62 by Timoth\u00e9e Mazzucotelli). Related issues/PRs: #65
  • Fix rendering signatures containing keyword_only (c6c5add by Timoth\u00e9e Mazzucotelli). Related issues/PRs: #68
"},{"location":"changelog/#0100-2020-03-27","title":"0.10.0 - 2020-03-27","text":"

Compare with 0.9.1

"},{"location":"changelog/#features_19","title":"Features","text":"
  • Prepare for new pytkdocs version (336421a). Add options filters and members to the Python collector to reflect the new pytkdocs options. See the default configuration of the Python collector.
"},{"location":"changelog/#091-2020-03-21","title":"0.9.1 - 2020-03-21","text":"

Compare with 0.9.0

"},{"location":"changelog/#bug-fixes_32","title":"Bug fixes","text":"
  • Fix cross-references when deploying to GitHub pages (36f804b).
"},{"location":"changelog/#090-2020-03-21","title":"0.9.0 - 2020-03-21","text":"

Compare with 0.8.0

This version is a big refactor. We will just list the new features without pointing to particular commits. The documentation rendering looks slightly different, and should be better than before. No identified breaking changes for end-users.

"},{"location":"changelog/#features_20","title":"Features","text":"
  • Language agnostic: we moved the code responsible for loading Python documentation into a new project, pytkdocs, and implemented a \"handlers\" logic, effectively allowing to support any given language. Waiting for your handlers contributions !
  • Multiple themes support: handlers can offer templates for multiple mkdocs themes.
  • Better cross-references: cross-references now not only work between documented objects (between all languages, given the objects' identifiers are unique), but also for every heading of your Markdown pages.
  • Configuration options: the rendering of Python documentation can now be configured, (globally and locally thanks to the handlers system), check the docs! Also see the recommended CSS.
  • Proper logging messages: mkdocstrings now logs debug, warning and error messages, useful when troubleshooting.
"},{"location":"changelog/#bug-fixes_33","title":"Bug fixes","text":"
  • Various fixes and better error handling.
"},{"location":"changelog/#080-2020-03-04","title":"0.8.0 - 2020-03-04","text":"

Compare with 0.7.2

"},{"location":"changelog/#breaking-changes_4","title":"Breaking Changes","text":"
  • Be compatible with Mkdocs >= 1.1 (5a974a4). This is a breaking change as we're not compatible with versions of Mkdocs below 1.1 anymore. If you cannot upgrade Mkdocs to 1.1, pin mkdocstrings' version to 0.7.2.
"},{"location":"changelog/#072-2020-03-04","title":"0.7.2 - 2020-03-04","text":"

Compare with 0.7.1

"},{"location":"changelog/#bug-fixes_34","title":"Bug Fixes","text":"
  • Catch OSError when trying to get source lines (8e8d604).
  • Do not render signature empty sentinel (16dfd73).
  • Fix for nested classes and their attributes (7fef903).
  • Fix relative_file_path method (52715ad).
  • Wrap file path in backticks to escape it (2525f39).
"},{"location":"changelog/#071-2020-02-18","title":"0.7.1 - 2020-02-18","text":"

Compare with 0.7.0

"},{"location":"changelog/#bug-fixes_35","title":"Bug Fixes","text":"
  • Replace literal slash with os.sep for Windows compatibility (70f9af5).
"},{"location":"changelog/#070-2020-01-13","title":"0.7.0 - 2020-01-13","text":"

Compare with 0.6.1

"},{"location":"changelog/#bug-fixes_36","title":"Bug Fixes","text":"
  • Don't mark args or kwargs as required (4049d6f).
  • Filter submodules (7b11095).
"},{"location":"changelog/#code-refactoring_20","title":"Code Refactoring","text":"
  • Don't guess lang in generated docs (db4f60a).
  • Render at HTML step with custom markdown converter (9b5a3e1).
"},{"location":"changelog/#features_21","title":"Features","text":"
  • Change forward ref to ref, fix optional unions (2f0bfaa).
  • Discover package submodules (231062a).
  • Implement watched source code (hacks) (4a67953).
"},{"location":"changelog/#061-2020-01-02","title":"0.6.1 - 2020-01-02","text":"

Compare with 0.6.0

"},{"location":"changelog/#bug-fixes_37","title":"Bug Fixes","text":"
  • Break docstring discarding loop if found (5a17fec).
  • Fix discarding docstring (143f7cb).
  • Fix getting annotation from nodes (ecde72b).
  • Fix various things (affbf06).
"},{"location":"changelog/#code-refactoring_21","title":"Code Refactoring","text":"
  • Break as soon as we find the same attr in a parent class while trying to discard the docstring (65d7908).
  • Split Docstring.parse method to improve readability (2226e2d).
"},{"location":"changelog/#060-2019-12-28","title":"0.6.0 - 2019-12-28","text":"

Compare with 0.5.0

"},{"location":"changelog/#bug-fixes_38","title":"Bug Fixes","text":"
  • Fix GenericMeta import error on Python 3.7+ (febf2b9).
"},{"location":"changelog/#code-refactoring_22","title":"Code Refactoring","text":"
  • More classes. Still ugly code though :'( (f41c119).
  • Split into more modules (f1872a4).
  • Use Object subclasses (40dd106).
"},{"location":"changelog/#050-2019-12-22","title":"0.5.0 - 2019-12-22","text":"

Compare with 0.4.0

"},{"location":"changelog/#features_22","title":"Features","text":"
  • Use divs in HTML contents to ease styling (2a36a0e).
"},{"location":"changelog/#040-2019-12-22","title":"0.4.0 - 2019-12-22","text":"

Compare with 0.3.0

"},{"location":"changelog/#features_23","title":"Features","text":"
  • Parse docstrings Google-style blocks, get types from signature (5af0c7b).
"},{"location":"changelog/#030-2019-12-21","title":"0.3.0 - 2019-12-21","text":"

Compare with 0.2.0

"},{"location":"changelog/#features_24","title":"Features","text":"
  • Allow object referencing in docstrings (2dd50c0).
"},{"location":"changelog/#020-2019-12-15","title":"0.2.0 - 2019-12-15","text":"

Compare with 0.1.0

"},{"location":"changelog/#misc","title":"Misc","text":"
  • Refactor, features, etc. (111fa85).
"},{"location":"changelog/#010-2019-12-12","title":"0.1.0 - 2019-12-12","text":"

Compare with first commit

"},{"location":"changelog/#misc_1","title":"Misc","text":"
  • Clean up (delete unused files) (c227043).
  • Clean up unused makefile rules (edc01e9).
  • Initial commit (f1dd8fb).
  • Update readme (ae56bdd).
  • Add plugin (6ed5cb1).
  • First PoC, needs better theming (18a00b9).
  • Get attributes docstrings (7838fff).
  • Refactor (f68f1a8).
"},{"location":"code_of_conduct/","title":"Contributor Covenant Code of Conduct","text":""},{"location":"code_of_conduct/#our-pledge","title":"Our Pledge","text":"

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.

"},{"location":"code_of_conduct/#our-standards","title":"Our Standards","text":"

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
"},{"location":"code_of_conduct/#enforcement-responsibilities","title":"Enforcement Responsibilities","text":"

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.

"},{"location":"code_of_conduct/#scope","title":"Scope","text":"

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.

"},{"location":"code_of_conduct/#enforcement","title":"Enforcement","text":"

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.

"},{"location":"code_of_conduct/#enforcement-guidelines","title":"Enforcement Guidelines","text":"

Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:

"},{"location":"code_of_conduct/#1-correction","title":"1. Correction","text":"

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.

"},{"location":"code_of_conduct/#2-warning","title":"2. Warning","text":"

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.

"},{"location":"code_of_conduct/#3-temporary-ban","title":"3. Temporary Ban","text":"

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.

"},{"location":"code_of_conduct/#4-permanent-ban","title":"4. Permanent Ban","text":"

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.

"},{"location":"code_of_conduct/#attribution","title":"Attribution","text":"

This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

Community Impact Guidelines were inspired by Mozilla's code of conduct enforcement ladder.

For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.

"},{"location":"contributing/","title":"Contributing","text":"

Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.

"},{"location":"contributing/#environment-setup","title":"Environment setup","text":"

Nothing easier!

Fork and clone the repository. The project uses dynamic versioning, so to get the correct package version when building, make sure to pull Git tags:

cd mkdocstrings\n\n# Assuming you authenticate with SSH.\ngit remote add upstream git@github.com:mkdocstrings/mkdocstrings\ngit pull upstream --tags\n

Then:

make setup\n

Note

If it fails for some reason, you'll need to install uv manually.

You can install it with:

curl -LsSf https://astral.sh/uv/install.sh | sh\n

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!

"},{"location":"contributing/#tasks","title":"Tasks","text":"

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.

If you work in VSCode, we provide an action to configure VSCode for the project.

"},{"location":"contributing/#development","title":"Development","text":"

As usual:

  1. create a new branch: git switch -c feature-or-bugfix-name
  2. edit the code and/or the documentation

Before committing:

  1. run make format to auto-format the code
  2. run make check to check everything (fix any warning)
  3. run make test to run the tests (fix any issue)
  4. if you updated the documentation or the project dependencies:
    1. run make docs
    2. go to http://localhost:8000 and check that everything looks good
  5. follow our 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.

"},{"location":"contributing/#commit-message-convention","title":"Commit message convention","text":"

Commit messages must follow our convention based on the Angular style or the Karma convention:

<type>[(scope)]: Subject\n\n[Body]\n

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.\n\nIssue #10: https://github.com/namespace/project/issues/10\nRelated to PR namespace/other-project#15: https://github.com/namespace/other-project/pull/15\n

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.

"},{"location":"contributing/#pull-requests-guidelines","title":"Pull requests guidelines","text":"

Link to any related issue in the Pull Request message.

During the review, we recommend using fixups:

# SHA is the SHA of the commit you want to fix\ngit commit --fixup=SHA\n

Once all the changes are approved, you can squash your commits:

git rebase -i --autosquash main\n

And force-push:

git push -f\n

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.

"},{"location":"credits/","title":"Credits","text":""},{"location":"credits/#exec-1--credits","title":"Credits","text":"

These projects were used to build mkdocstrings. Thank you!

Python | uv | copier-uv

"},{"location":"credits/#exec-1--runtime-dependencies","title":"Runtime dependencies","text":"Project Summary Version (accepted) Version (last resolved) License click Composable command line interface toolkit >=7.0 8.3.1 BSD-3-Clause colorama Cross-platform colored terminal text. >=0.4 0.4.6 BSD License ghp-import Copy your docs directly to the gh-pages branch. >=1.0 2.1.0 Apache Software License griffe Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API. >=1.13 1.15.0 ISC Jinja2 A very fast and expressive template engine. >=3.1, >=3.0 3.1.6 BSD License Markdown Python implementation of John Gruber's Markdown. >=3.6, >=3.2 3.10 BSD-3-Clause MarkupSafe Safely add untrusted strings to HTML/XML markup. >=2.0, >=1.1 3.0.3 BSD-3-Clause mergedeep A deep merge function for \ud83d\udc0d. >=1.3.4 1.3.4 MIT License mkdocs Project documentation with Markdown. >=1.6 1.6.1 BSD-2-Clause mkdocs-autorefs Automatically link across pages in MkDocs. >=1.4 1.4.3 ISC mkdocs-get-deps MkDocs extension that lists all dependencies according to a mkdocs.yml file >=0.2.0 0.2.0 MIT mkdocstrings-python A Python handler for mkdocstrings. >=1.16.2 1.19.0 ISC packaging Core utilities for Python packages >=22, >=20.5 25.0 Apache Software License + BSD License pathspec Utility library for gitignore style pattern matching of file paths. >=0.9.0, >=0.11.1 0.12.1 Mozilla Public License 2.0 (MPL 2.0) platformdirs A small Python package for determining appropriate platform-specific dirs, e.g. a user data dir. >=4.4, >=2.2.0 4.5.0 MIT pymdown-extensions Extension pack for Python Markdown. >=9, >=6.3 10.17.1 MIT python-dateutil Extensions to the standard Python datetime module >=2.8.1 2.9.0.post0 BSD License + Apache Software License PyYAML YAML parser and emitter for Python 6.0.3 MIT pyyaml_env_tag A custom YAML tag for referencing environment variables in YAML files. >=0.1 1.1 MIT six Python 2 and 3 compatibility utilities >=1.5, >=1.15, <2 1.17.0 MIT typing_extensions Backported and Experimental Type Hints for Python 3.9+ >=4.9, >=4.0 4.15.0 PSF-2.0 watchdog Filesystem events monitoring >=2.0 6.0.0 Apache-2.0"},{"location":"credits/#exec-1--development-dependencies","title":"Development dependencies","text":"Project Summary Version (accepted) Version (last resolved) License ansimarkup Produce colored terminal text with an xml-like markup ~=1.4 1.5.0 Revised BSD License babel Internationalization utilities >=2.7.0 2.17.0 BSD-3-Clause backrefs A wrapper around re and regex that adds additional back references. >=5.7.post1 6.1 MIT beautifulsoup4 Screen-scraping library >=4.12 4.14.2 MIT License build A simple, correct Python build frontend >=1.2 1.3.0 MIT cappa Declarative CLI argument parser. >=0.29 0.30.4 ? certifi Python package for providing Mozilla's CA Bundle. >=2017.4.17 2025.11.12 MPL-2.0 cffi Foreign Function Interface for Python calling C code. >=1.14 2.0.0 MIT charset-normalizer The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet. >=2, <4 3.4.4 MIT click Composable command line interface toolkit >=7.0 8.3.1 BSD-3-Clause colorama Cross-platform colored terminal text. >=0.4 0.4.6 BSD License coverage Code coverage measurement for Python >=7.10.6 7.12.0 Apache-2.0 cryptography cryptography is a package which provides cryptographic recipes and primitives to Python developers. >=2.0 46.0.3 Apache-2.0 OR BSD-3-Clause csscompressor A python port of YUI CSS Compressor >=0.9.5 0.9.5 BSD dirty-equals Doing dirty (but extremely useful) things with equals. >=0.9 0.11 MIT docutils Docutils -- Python Documentation Utilities >=0.21.2 0.22.3 Public Domain + BSD License + GNU General Public License (GPL) duty A simple task runner. >=1.6 1.6.3 ISC execnet execnet: rapid multi-Python deployment >=2.1 2.1.2 MIT failprint Run a command, print its output only if it fails. >=1.0.5 1.0.6 ISC ghp-import Copy your docs directly to the gh-pages branch. >=1.0 2.1.0 Apache Software License git-changelog Automatic Changelog generator using Jinja2 templates. >=2.5 2.6.3 ISC gitdb Git Object Database >=4.0.1, <5 4.0.12 BSD License GitPython GitPython is a Python library used to interact with Git repositories >=3.1.44 3.1.45 BSD-3-Clause griffe Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API. >=1.13 1.15.0 ISC htmlmin2 An HTML Minifier >=0.1.13 0.1.13 BSD humanize Python humanize utilities >=4.9 4.14.0 MIT id A tool for generating OIDC identities 1.5.0 Apache Software License idna Internationalized Domain Names in Applications (IDNA) >=2.5, <4 3.11 BSD-3-Clause iniconfig brain-dead simple config-ini parsing >=1.0.1 2.3.0 MIT jaraco.classes Utility functions for Python class constructs 3.4.0 MIT License jaraco.context Useful decorators and context managers 6.0.1 MIT License jaraco.functools Functools like those found in stdlib 4.3.0 MIT jeepney Low-level, pure Python DBus protocol wrapper. >=0.4.2 0.9.0 MIT Jinja2 A very fast and expressive template engine. >=3.1, >=3.0 3.1.6 BSD License jsmin JavaScript minifier. >=3.0.1 3.0.1 MIT License keyring Store and access your passwords safely. >=21.2.0 25.7.0 MIT Markdown Python implementation of John Gruber's Markdown. >=3.6, >=3.2 3.10 BSD-3-Clause markdown-callouts Markdown extension: a classier syntax for admonitions >=0.4 0.4.0 MIT markdown-exec Utilities to execute code blocks in Markdown files. >=1.8 1.12.1 ISC markdown-it-py Python port of markdown-it. Markdown parsing, done right! >=2.2.0 3.0.0 MIT License markdownify Convert HTML to markdown. >=0.14 1.2.2 MIT License MarkupSafe Safely add untrusted strings to HTML/XML markup. >=2.0, >=1.1 3.0.3 BSD-3-Clause mdformat CommonMark compliant Markdown formatter >=0.7.21 0.7.22 MIT License mdformat_tables An mdformat plugin for rendering tables. >=1.0 1.0.0 MIT License mdurl Markdown URL utilities ~=0.1 0.1.2 MIT License mergedeep A deep merge function for \ud83d\udc0d. >=1.3.4 1.3.4 MIT License mkdocs Project documentation with Markdown. >=1.6 1.6.1 BSD-2-Clause mkdocs-autorefs Automatically link across pages in MkDocs. >=1.4 1.4.3 ISC mkdocs-coverage MkDocs plugin to integrate your coverage HTML report into your site. >=1.0 2.0.0 ISC mkdocs-get-deps MkDocs extension that lists all dependencies according to a mkdocs.yml file >=0.2.0 0.2.0 MIT mkdocs-git-revision-date-localized-plugin Mkdocs plugin that enables displaying the localized date of the last git modification of a markdown file. >=1.2 1.5.0 MIT mkdocs-llmstxt MkDocs plugin to generate an /llms.txt file. >=0.2 0.4.0 ISC mkdocs-material Documentation that simply works >=9.5 9.7.0 MIT mkdocs-material-extensions Extension pack for Python Markdown and MkDocs Material. >=1.3 1.3.1 MIT mkdocs-minify-plugin An MkDocs plugin to minify HTML, JS or CSS files prior to being written to disk >=0.8 0.8.0 MIT mkdocs-redirects A MkDocs plugin for dynamic page redirects to prevent broken links >=1.2.1 1.2.2 MIT mkdocs-section-index MkDocs plugin to allow clickable sections that lead to an index page >=0.3 0.3.10 MIT mkdocstrings-python A Python handler for mkdocstrings. >=1.16.2 1.19.0 ISC more-itertools More routines for operating on iterables, beyond itertools 10.8.0 MIT mypy Optional static typing for Python >=1.10 1.18.2 MIT mypy_extensions Type system extensions for programs checked with the mypy type checker. >=1.0.0 1.1.0 MIT nh3 Python binding to Ammonia HTML sanitizer Rust crate >=0.2.14 0.3.2 MIT packaging Core utilities for Python packages >=22, >=20.5 25.0 Apache Software License + BSD License paginate Divides large result sets into pages for easier browsing >=0.5 0.5.7 MIT pathspec Utility library for gitignore style pattern matching of file paths. >=0.9.0, >=0.11.1 0.12.1 Mozilla Public License 2.0 (MPL 2.0) platformdirs A small Python package for determining appropriate platform-specific dirs, e.g. a user data dir. >=4.4, >=2.2.0 4.5.0 MIT pluggy plugin and hook calling mechanisms for python >=1.5, <2 1.6.0 MIT ptyprocess Run a subprocess in a pseudo terminal ~=0.6 0.7.0 ISC License (ISCL) pycparser C parser in Python 2.23 BSD-3-Clause Pygments Pygments is a syntax highlighting package written in Python. >=2.7.2 2.19.2 BSD-2-Clause pymdown-extensions Extension pack for Python Markdown. >=9, >=6.3 10.17.1 MIT pyproject_hooks Wrappers to call pyproject.toml-based build backend hooks. 1.2.0 MIT License pytest pytest: simple powerful testing with Python >=8.2 9.0.1 MIT pytest-cov Pytest plugin for measuring coverage. >=5.0 7.0.0 MIT pytest-randomly Pytest plugin to randomly order tests and control random.seed. >=3.15 4.0.1 MIT pytest-xdist pytest xdist plugin for distributed testing, most importantly across multiple CPUs >=3.6 3.8.0 MIT python-dateutil Extensions to the standard Python datetime module >=2.8.1 2.9.0.post0 BSD License + Apache Software License PyYAML YAML parser and emitter for Python 6.0.3 MIT pyyaml_env_tag A custom YAML tag for referencing environment variables in YAML files. >=0.1 1.1 MIT readme_renderer readme_renderer is a library for rendering readme descriptions for Warehouse >=35.0 44.0 Apache License, Version 2.0 requests Python HTTP for Humans. >=2.20 2.32.5 Apache-2.0 requests-toolbelt A utility belt for advanced users of python-requests >=0.8.0, !=0.9.0 1.0.0 Apache 2.0 rfc3986 Validating URI References per RFC 3986 >=1.4.0 2.0.0 Apache 2.0 rich Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal >=12.0.0 14.2.0 MIT ruff An extremely fast Python linter and code formatter, written in Rust. >=0.4 0.14.5 MIT License SecretStorage Python bindings to FreeDesktop.org Secret Service API >=3.2 3.4.1 BSD-3-Clause semver Python helper for Semantic Versioning (https://semver.org) >=3.0 3.0.4 BSD License six Python 2 and 3 compatibility utilities >=1.5, >=1.15, <2 1.17.0 MIT smmap A pure Python implementation of a sliding window memory map manager >=3.0.1, <6 5.0.2 BSD-3-Clause soupsieve A modern CSS selector implementation for Beautiful Soup. >1.2 2.8 MIT twine Collection of utilities for publishing packages on PyPI >=5.1 6.2.0 Apache-2.0 type-lens type-lens is a Python template project designed to simplify the setup of a new project. >=0.2.5 0.2.6 MIT types-Markdown Typing stubs for Markdown >=3.6 3.10.0.20251106 Apache-2.0 types-PyYAML Typing stubs for PyYAML >=6.0 6.0.12.20250915 Apache-2.0 typing_extensions Backported and Experimental Type Hints for Python 3.9+ >=4.9, >=4.0 4.15.0 PSF-2.0 urllib3 HTTP library with thread-safe connection pooling, file post, and more. >=1.26.0 2.5.0 MIT watchdog Filesystem events monitoring >=2.0 6.0.0 Apache-2.0 wcwidth Measures the displayed width of unicode strings in a terminal >=0.2.13 0.2.14 MIT yore Manage legacy code with comments. >=0.3.3 0.4.6 ISC

More credits from the author

"},{"location":"license/","title":"License","text":"
ISC License\n\nCopyright (c) 2019, Timoth\u00e9e Mazzucotelli\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n
"},{"location":"recipes/","title":"Recipes","text":"

On this page you will find various recipes, tips and tricks for mkdocstrings and more generally Markdown documentation.

"},{"location":"recipes/#automatic-code-reference-pages","title":"Automatic code reference pages","text":"

mkdocs-autoapi and mkdocs-api-autonav are MkDocs plugins that automatically generate API documentation from your project's source code. They were inspired by the recipe below.

mkdocstrings allows to inject documentation for any object into Markdown pages. But as the project grows, it quickly becomes quite tedious to keep the autodoc instructions, or even the dedicated Markdown files in sync with all your source files and objects.

In this recipe, we will iteratively automate the process of generating these pages at each build of the documentation.

Let say you have a project called project. This project has a lot of source files, or modules, which live in the src folder:

\ud83d\udcc1 repo/\n\u2514\u2500\u2500 \ud83d\udcc1 src/\n    \u2514\u2500\u2500 \ud83d\udcc1 project/\n        \u251c\u2500\u2500  lorem\n        \u251c\u2500\u2500  ipsum\n        \u251c\u2500\u2500  dolor\n        \u251c\u2500\u2500  sit\n        \u2514\u2500\u2500  amet\n

Without an automatic process, you will have to manually create a Markdown page for each one of these modules, with the corresponding autodoc instruction, for example ::: project.lorem, and also add entry in MkDocs' navigation option (nav in mkdocs.yml). With a lot of modules, this is quickly getting cumbersome.

Lets fix that.

"},{"location":"recipes/#generate-pages-on-the-fly","title":"Generate pages on-the-fly","text":"

In this recipe, we suggest to use the mkdocs-gen-files plugin. This plugin exposes utilities to generate files at build time. These files won't be written to the docs directory: you don't have to track and version them. They are transparently generated each time you build your docs. This is perfect for our use-case!

Add mkdocs-gen-files to your project's docs dependencies, and configure it like so:

mkdocs.yml
plugins:\n- search  # (1)!\n- gen-files:\n    scripts:\n    - scripts/gen_ref_pages.py  # (2)!\n- mkdocstrings\n
  1. Don't forget to load the search plugin when redefining the plugins item.
  2. The magic happens here, see below how it works.

mkdocs-gen-files is able to run Python scripts at build time. The Python script that we will execute lives in a scripts folder, and is named gen_ref_pages.py, like \"generate code reference pages\".

\ud83d\udcc1 repo/\n\u251c\u2500\u2500 \ud83d\udcc1 docs/\n\u2502   \u2514\u2500\u2500  index.md\n\u251c\u2500\u2500 \ud83d\udcc1 scripts/\n\u2502   \u2514\u2500\u2500  gen_ref_pages.py\n\u251c\u2500\u2500 \ud83d\udcc1 src/\n\u2502   \u2514\u2500\u2500 \ud83d\udcc1 project/\n\u2514\u2500\u2500  mkdocs.yml\n
scripts/gen_ref_pages.py
\"\"\"Generate the code reference pages.\"\"\"\n\nfrom pathlib import Path\n\nimport mkdocs_gen_files\n\nroot = Path(__file__).parent.parent\nsrc = root / \"src\"  # (1)!\n\nfor path in sorted(src.rglob(\"*.py\")):  # (2)!\n    module_path = path.relative_to(src).with_suffix(\"\")  # (3)!\n    doc_path = path.relative_to(src).with_suffix(\".md\")  # (4)!\n    full_doc_path = Path(\"reference\", doc_path)  # (5)!\n\n    parts = tuple(module_path.parts)\n\n    if parts[-1] == \"__init__\":  # (6)!\n        parts = parts[:-1]\n    elif parts[-1] == \"__main__\":\n        continue\n\n    with mkdocs_gen_files.open(full_doc_path, \"w\") as fd:  # (7)!\n        identifier = \".\".join(parts)  # (8)!\n        print(\"::: \" + identifier, file=fd)  # (9)!\n\n    mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root))  # (10)!\n
  1. It's important to build a path relative to the script itself, to make it possible to build the docs with MkDocs' -f option.
  2. Here we recursively list all .py files, but you can adapt the code to list files with other extensions of course, supporting other languages than Python.
  3. The module path will look like project/lorem. It will be used to build the mkdocstrings autodoc identifier.
  4. This is the partial path of the Markdown page for the module.
  5. This is the full path of the Markdown page within the docs. Here we put all reference pages into a reference folder.
  6. This part is only relevant for Python modules. We skip __main__ modules and remove __init__ from the module parts as it's implicit during imports.
  7. Magic! Add the file to MkDocs pages, without actually writing it in the docs folder.
  8. Build the autodoc identifier. Here we document Python modules, so the identifier is a dot-separated path, like project.lorem.
  9. Actually write to the magic file.
  10. We can even set the edit_uri on the pages.

Note

It is important to look out for correct edit page behaviour when using generated pages. For example, if we have edit_uri set to blob/master/docs/ and the following file structure:

\ud83d\udcc1 repo/\n\u251c\u2500\u2500  mkdocs.yml\n\u251c\u2500\u2500 \ud83d\udcc1 docs/\n\u2502   \u2514\u2500\u2500  index.md\n\u251c\u2500\u2500 \ud83d\udcc1 scripts/\n\u2502   \u2514\u2500\u2500  gen_ref_pages.py\n\u2514\u2500\u2500 \ud83d\udcc1 src/\n    \u2514\u2500\u2500 \ud83d\udcc1 project/\n        \u251c\u2500\u2500  lorem.py\n        \u251c\u2500\u2500  ipsum.py\n        \u251c\u2500\u2500  dolor.py\n        \u251c\u2500\u2500  sit.py\n        \u2514\u2500\u2500  amet.py\n

Then we will have to change our set_edit_path call to:

mkdocs_gen_files.set_edit_path(full_doc_path, Path(\"../\") / path)  # (1)!\n
  1. Path can be used to traverse the structure in any way you may need, but remember to use relative paths!

...so that it correctly sets the edit path of (for example) lorem.py to <repo_url>/blob/master/src/project/lorem.py instead of <repo_url>/blob/master/docs/src/project/lorem.py.

With this script, a reference folder is automatically created each time we build our docs. This folder contains a Markdown page for each of our source modules, and each of these pages contains a single line of the form ::: project.module (module being lorem, ipsum, etc.). Great! But, we still have to actually add those pages into our MkDocs navigation:

mkdocs.yml
nav:\n# rest of the navigation...\n- Code Reference:\n  - project:\n    - lorem: reference/project/lorem.md\n    - ipsum: reference/project/ipsum.md\n    - dolor: reference/project/dolor.md\n    - sit: reference/project/sit.md\n    - amet: reference/project/amet.md\n# rest of the navigation...\n

Err... so this process is only semi-automatic? Yes, but don't worry, we can fully automate it.

"},{"location":"recipes/#generate-a-literate-navigation-file","title":"Generate a literate navigation file","text":"

mkdocs-gen-files is able to generate a literate navigation file. But to make use of it, we will need an additional plugin: mkdocs-literate-nav. This plugin allows to specify the whole navigation, or parts of it, into Markdown pages, as plain Markdown lists. We use it here to specify the navigation for the code reference pages.

First, add mkdocs-literate-nav to your project's docs dependencies, and configure the plugin in your MkDocs configuration:

mkdocs.yml
plugins:\n- search\n- gen-files:\n    scripts:\n    - scripts/gen_ref_pages.py\n- literate-nav:\n    nav_file: SUMMARY.md\n- mkdocstrings\n

Then, the previous script is updated like so:

scripts/gen_ref_pages.py
\"\"\"Generate the code reference pages and navigation.\"\"\"\n\nfrom pathlib import Path\n\nimport mkdocs_gen_files\n\nnav = mkdocs_gen_files.Nav()\n\nroot = Path(__file__).parent.parent\nsrc = root / \"src\"\n\nfor path in sorted(src.rglob(\"*.py\")):\n    module_path = path.relative_to(src).with_suffix(\"\")\n    doc_path = path.relative_to(src).with_suffix(\".md\")\n    full_doc_path = Path(\"reference\", doc_path)\n\n    parts = tuple(module_path.parts)\n\n    if parts[-1] == \"__init__\":\n        parts = parts[:-1]\n    elif parts[-1] == \"__main__\":\n        continue\n\n    nav[parts] = doc_path.as_posix()  # (1)!\n\n    with mkdocs_gen_files.open(full_doc_path, \"w\") as fd:\n        ident = \".\".join(parts)\n        fd.write(f\"::: {ident}\")\n\n    mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root))\n\nwith mkdocs_gen_files.open(\"reference/SUMMARY.md\", \"w\") as nav_file:  # (2)!\n    nav_file.writelines(nav.build_literate_nav())  # (3)!\n
  1. Progressively build the navigation object.
  2. At the end, create a magic, literate navigation file called SUMMARY.md in the reference folder.
  3. Write the navigation as a Markdown list in the literate navigation file.

Now we are able to remove our hard-coded navigation in mkdocs.yml, and replace it with a single line!

mkdocs.yml
nav:\n# rest of the navigation...\n# defer to gen-files + literate-nav\n- Code Reference: reference/  # (1)!\n# rest of the navigation...\n
  1. Note the trailing slash! It is needed so that mkdocs-literate-nav knows it has to look for a SUMMARY.md file in that folder.

At this point, we should be able to see the tree of our modules in the navigation.

"},{"location":"recipes/#bind-pages-to-sections-themselves","title":"Bind pages to sections themselves","text":"

There's a last improvement we can do. With the current script, sections, corresponding to folders, will expand or collapse when you click on them, revealing __init__ modules under them (or equivalent modules in other languages, if relevant). Since we are documenting a public API, and given users never explicitly import __init__ modules, it would be nice if we could get rid of them and instead render their documentation inside the section itself.

Well, this is possible thanks to a third plugin: mkdocs-section-index.

Update the script like this:

scripts/gen_ref_pages.py
\"\"\"Generate the code reference pages and navigation.\"\"\"\n\nfrom pathlib import Path\n\nimport mkdocs_gen_files\n\nnav = mkdocs_gen_files.Nav()\n\nroot = Path(__file__).parent.parent\nsrc = root / \"src\"\n\nfor path in sorted(src.rglob(\"*.py\")):\n    module_path = path.relative_to(src).with_suffix(\"\")\n    doc_path = path.relative_to(src).with_suffix(\".md\")\n    full_doc_path = Path(\"reference\", doc_path)\n\n    parts = tuple(module_path.parts)\n\n    if parts[-1] == \"__init__\":\n        parts = parts[:-1]\n        doc_path = doc_path.with_name(\"index.md\")\n        full_doc_path = full_doc_path.with_name(\"index.md\")\n    elif parts[-1] == \"__main__\":\n        continue\n\n    nav[parts] = doc_path.as_posix()\n\n    with mkdocs_gen_files.open(full_doc_path, \"w\") as fd:\n        ident = \".\".join(parts)\n        fd.write(f\"::: {ident}\")\n\n    mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root))\n\nwith mkdocs_gen_files.open(\"reference/SUMMARY.md\", \"w\") as nav_file:\n    nav_file.writelines(nav.build_literate_nav())\n

And update your MkDocs configuration to list the plugin:

mkdocs.yml
plugins:\n- search\n- gen-files:\n    scripts:\n    - scripts/gen_ref_pages.py\n- literate-nav:\n    nav_file: SUMMARY.md\n- section-index\n- mkdocstrings\n

With this, __init__ modules will be documented and bound to the sections themselves, better reflecting our public API.

"},{"location":"recipes/#prevent-selection-of-prompts-and-output-in-python-code-blocks","title":"Prevent selection of prompts and output in Python code blocks","text":"

To prevent the selection of >>>, ... and output in Python \"Console\" code blocks, you can use the pycon syntax highlighting on your code blocks, and add global CSS rules to your site using MkDocs extra_css option:

```pycon\n>>> for word in (\"Hello\", \"mkdocstrings!\"):\n...     print(word, end=\" \")\n...\nHello mkdocstrings!\n```\n
docs/css/code_select.css
.highlight .gp, .highlight .go { /* Generic.Prompt, Generic.Output */\n    user-select: none;\n}\n
mkdocs.yml
extra_css:\n- css/code_select.css\n

Warning

The .highlight .gp, .highlight .go CSS selector can have unintended side-effects. To target pycon code blocks more specifically, you can configure the pymdownx.highlight extension to use Pygments and set language classes on code blocks:

mkdocs.yml
markdown_extensions:\n- pymdownx.highlight:\n    use_pygments: true\n    pygments_lang_class: true\n

Then you can update the CSS selector like this:

docs/css/code_select.css
.language-pycon .gp, .language-pycon .go { /* Generic.Prompt, Generic.Output */\n    user-select: none;\n}\n

If you don't want to enable this globally, you can still use style tags in the relevant pages, with more accurate CSS selectors:

<style>\n#my-div .highlight .gp, #my-div .highlight .go { /* Generic.Prompt, Generic.Output */\n    user-select: none;\n}\n</style>\n

Try to select the following code block's text:

>>> for word in (\"Hello\", \"mkdocstrings!\"):\n...     print(word, end=\" \")\nHello mkdocstrings!\n
"},{"location":"recipes/#hide-documentation-strings-from-source-code-blocks","title":"Hide documentation strings from source code blocks","text":"

Since documentation strings are rendered by handlers, it can sometimes feel redundant to show these same documentation strings in source code blocks (when handlers render those).

There is a general workaround to hide these docstrings from source blocks using CSS:

/* These CSS classes depend on the handler. */\n.doc-contents details .highlight code {\n  line-height: 0;\n}\n.doc-contents details .highlight code > * {\n  line-height: initial;\n}\n.doc-contents details .highlight code > .sd {  /* Literal.String.Doc */\n  display: none;\n}\n

Note that this is considered a workaround and not a proper solution, because it has side-effects like also removing blank lines.

"},{"location":"recipes/#automatic-highlighting-for-indented-code-blocks-in-docstrings","title":"Automatic highlighting for indented code blocks in docstrings","text":"

Depending on the language used in your code base and the mkdocstrings handler used to document it, you might want to set a default syntax for code blocks added to your docstrings. For example, to default to the Python syntax:

mkdocs.yml
markdown_extensions:\n- pymdownx.highlight:\n    default_lang: python\n

Then in your docstrings, indented code blocks will be highlighted as Python code:

def my_function():\n    \"\"\"This is my function.\n\n    The following code will be highlighted as Python:\n\n        result = my_function()\n        print(result)\n\n    End of the docstring.\n    \"\"\"\n    pass\n
"},{"location":"troubleshooting/","title":"Troubleshooting","text":""},{"location":"troubleshooting/#code-blocks-in-admonitions-in-docstrings-or-else-are-not-rendered-correctly","title":"Code blocks in admonitions (in docstrings or else) are not rendered correctly","text":"

To render code blocks in admonitions, you need to add the pymdownx.superfences extensions to the list of Markdown extensions in mkdocs.yml. For example:

!!! note\n    Some text.\n\n    ```bash\n    echo \"some code\"\n    ```\n
mkdocs.yml
markdown_extensions:\n- admonition\n- codehilite\n- pymdownx.superfences\n

For code blocks in docstrings, make sure to escape newlines (\\n -> \\\\n), or prefix the entire docstring with 'r' to make it a raw-docstring: r\"\"\". Indeed, docstrings are still strings and therefore subject to how Python parses strings.

"},{"location":"troubleshooting/#footnotes-are-duplicated-or-overridden","title":"Footnotes are duplicated or overridden","text":"

Before version 0.14, footnotes could be duplicated over a page. Please upgrade to version 0.14 or higher.

See also:

  • Issue #186
  • Tabs in docstrings (from pymdownx.tabbed) are not working properly.
"},{"location":"troubleshooting/#mkdocs-warns-me-about-links-to-unfound-documentation-files","title":"MkDocs warns me about links to unfound documentation files","text":"

A warning like this one:

WARNING - Documentation file 'reference/parsers/docstrings.md' contains a link to 'reference/parsers/pytkdocs.parsers.docstrings.Section' which is not found in the documentation files.

...generally means you used parentheses () instead of brackets [] for a cross-reference. Notice the dots in reference/parsers/pytkdocs.parsers.docstrings.Section? It shows that it's probably a cross-reference, not a direct link. It's probably written like [Section](pytkdocs.parsers.docstrings.Section) in the docs, when it should be [Section][pytkdocs.parsers.docstrings.Section].

"},{"location":"troubleshooting/#some-objects-are-not-rendered-they-do-not-appear-in-the-generated-docs","title":"Some objects are not rendered (they do not appear in the generated docs)","text":"
  • Make sure the configuration options of the handler are correct. Check the documentation for Handlers to see the available options for each handler.
  • Also make sure your documentation in your source code is formatted correctly. For Python code, check the supported docstring styles page.
  • Re-run the Mkdocs command with -v, and carefully read any traceback.
"},{"location":"troubleshooting/#tabs-in-docstrings-from-pymdownxtabbed-are-not-working-properly","title":"Tabs in docstrings (from pymdownx.tabbed) are not working properly","text":"

Before version 0.14, multiple tab blocks injected on the same page would result in broken links: clicking on a tab would bring the user to the wrong one. Please upgrade to version 0.14 or higher.

See also:

  • Issue #193
  • Footnotes are duplicated or overridden.

If you are stuck on a version before 0.14, and want to use multiple tab blocks in one page, use this workaround.

JavaScript workaround

Put the following code in a .js file, and list it in MkDocs' extra_javascript:

// Credits to Nikolaos Zioulis (@zuru on GitHub)\nfunction setID(){\n    var tabs = document.getElementsByClassName(\"tabbed-set\");\n    for (var i = 0; i < tabs.length; i++) {\n        children = tabs[i].children;\n        var counter = 0;\n        var iscontent = 0;\n        for(var j = 0; j < children.length;j++){\n            if(typeof children[j].htmlFor === 'undefined'){\n                if((iscontent + 1) % 2 == 0){\n                    // check if it is content\n                    if(iscontent == 1){\n                        btn = children[j].childNodes[1].getElementsByTagName(\"button\");\n                    }\n                }\n                else{\n                    // if not change the id\n                    children[j].id = \"__tabbed_\" + String(i + 1) + \"_\" + String(counter + 1);\n                    children[j].name = \"__tabbed_\" + String(i + 1);\n                    // make default tab open\n                    if(j == 0)\n                        children[j].click();\n                }\n                iscontent++;\n            }\n            else{\n                // link to the correct tab\n                children[j].htmlFor = \"__tabbed_\" + String(i+1) + \"_\" + String(counter + 1);\n                counter ++;\n            }\n        }\n    }\n}\nsetID();\n

This code will correctly reset the IDs for tabs on a same page.

"},{"location":"troubleshooting/#the-generated-documentation-does-not-look-good","title":"The generated documentation does not look good","text":"

Please open an ticket on the bugtracker with a detailed explanation and screenshots of the bad-looking parts. Note that you can always customize the look of mkdocstrings blocks -- through both HTML and CSS.

"},{"location":"troubleshooting/#warning-could-not-find-cross-reference-target","title":"Warning: could not find cross-reference target","text":"

New in version 0.15.

Cross-linking used to include any Markdown heading, but now it's only for mkdocstrings identifiers by default. See Cross-references to any Markdown heading to opt back in.

Make sure the referenced object is properly rendered: verify your configuration options.

For false-positives, you can wrap the text in backticks (`) to prevent mkdocstrings from trying to process it.

"},{"location":"troubleshooting/#python-specifics","title":"Python specifics","text":""},{"location":"troubleshooting/#nothing-is-rendered-at-all","title":"Nothing is rendered at all","text":"

Is your package available in the Python path?

See Python handler: Finding modules.

"},{"location":"troubleshooting/#latex-in-docstrings-is-not-rendered-correctly","title":"LaTeX in docstrings is not rendered correctly","text":"

If you are using a Markdown extension like Arithmatex Mathjax or markdown-katex to render LaTeX, add r in front of your docstring to make sure nothing is escaped. You'll still maybe have to play with escaping to get things right.

Example:

def math_function(x, y):\n    r\"\"\"\n    Look at these formulas:\n\n    ```math\n    f(x) = \\int_{-\\infty}^\\infty\n    \\hat f(\\xi)\\,e^{2 \\pi i \\xi x}\n    \\,d\\xi\n    ```\n    \"\"\"\n
"},{"location":"troubleshooting/#my-docstrings-in-comments-are-not-picked-up","title":"My docstrings in comments (#:) are not picked up","text":"

We only support docstrings in comments through the griffe-sphinx extension.

Alternatively, instead of:

import enum\n\n\nclass MyEnum(enum.Enum):\n    v1 = 1  #: The first choice.\n    v2 = 2  #: The second choice.\n

You can use:

import enum\n\n\nclass MyEnum(enum.Enum):\n    v1 = 1\n    \"\"\"The first choice.\"\"\"\n\n    v2 = 2\n    \"\"\"The second choice.\"\"\"\n

Or:

import enum\n\n\nclass MyEnum(enum.Enum):\n    \"\"\"My enum.\n\n    Attributes:\n        v1: The first choice.\n        v2: The second choice.\n    \"\"\"\n\n    v1 = 1\n    v2 = 2\n
"},{"location":"troubleshooting/#my-wrapped-function-shows-documentationcode-for-its-wrapper-instead-of-its-own","title":"My wrapped function shows documentation/code for its wrapper instead of its own","text":"

Use functools.wraps():

from functools import wraps\n\n\ndef my_decorator(function):\n    \"\"\"The decorator docs.\"\"\"\n\n    @wraps(function)\n    def wrapped_function(*args, **kwargs):\n        print(\"hello\")\n        function(*args, **kwargs)\n        print(\"bye\")\n\n    return wrapped_function\n\n\n@my_decorator\ndef my_function(*args, **kwargs):\n    \"\"\"The function docs.\"\"\"\n    print(*args, **kwargs)\n
"},{"location":"troubleshooting/#footnotes-do-not-render","title":"Footnotes do not render","text":"

The library that parses docstrings, Griffe, splits docstrings in several \"sections\" (example: Google-style sections syntax). If a footnote is used in a section, while referenced in another, mkdocstrings won't be able to render it correctly. The footnote and its reference must appear in the same section.

def my_function():\n    \"\"\"Summary.\n\n    This is the first section[^1].\n\n    Note:\n        This is the second section[^2].\n\n    Note:\n        This is the third section[^3].\n\n    References at the end are part of yet another section (fourth here)[^4].\n\n    [^1]: Some text.\n    [^2]: Some text.\n    [^3]: Some text.\n    [^4]: Some text.\n    \"\"\"\n

Here only the fourth footnote will work, because it is the only one that appear in the same section as its reference. To fix this, make sure all footnotes appear in the same section as their references:

def my_function():\n    \"\"\"Summary.\n\n    This is the first section[^1].\n\n    [^1]: Some text.\n\n    Note:\n        This is the second section[^2].\n\n        [^2]: Some text.\n\n    Note:\n        This is the third section[^3].\n\n        [^3]: Some text.\n\n    References at the end are part of yet another section (fourth here)[^4].\n\n    [^4]: Some text.\n    \"\"\"\n
"},{"location":"troubleshooting/#submodules-are-not-rendered","title":"Submodules are not rendered","text":"

In previous versions of mkdocstrings-python, submodules were rendered by default. This was changed and you now need to set the following option:

mkdocs.yml
plugins:\n- mkdocstrings:\n    handlers:\n      python:\n        options:\n          show_submodules: true\n
"},{"location":"reference/api/","title":"API reference","text":""},{"location":"reference/api/#mkdocstrings","title":"mkdocstrings","text":"

mkdocstrings package.

Automatic documentation from sources, for MkDocs.

Classes:

  • AutoDocProcessor \u2013

    Our \"autodoc\" Markdown block processor.

  • BaseHandler \u2013

    The base handler class.

  • CollectionError \u2013

    An exception raised when some collection of data failed.

  • Handlers \u2013

    A collection of handlers.

  • HeadingShiftingTreeprocessor \u2013

    Shift levels of all Markdown headings according to the configured base level.

  • Highlighter \u2013

    Code highlighter that tries to match the Markdown configuration.

  • IdPrependingTreeprocessor \u2013

    Prepend the configured prefix to IDs of all HTML elements.

  • Inventory \u2013

    Inventory of collected and rendered objects.

  • InventoryItem \u2013

    Inventory item.

  • LoggerAdapter \u2013

    A logger adapter to prefix messages.

  • MkdocstringsExtension \u2013

    Our Markdown extension.

  • MkdocstringsInnerExtension \u2013

    Extension that should always be added to Markdown sub-documents that handlers request (and only them).

  • MkdocstringsPlugin \u2013

    An mkdocs plugin.

  • ParagraphStrippingTreeprocessor \u2013

    Unwraps the <p> element around the whole output.

  • PluginConfig \u2013

    The configuration options of mkdocstrings, written in mkdocs.yml.

  • TemplateLogger \u2013

    A wrapper class to allow logging in templates.

  • ThemeNotSupported \u2013

    An exception raised to tell a theme is not supported.

Functions:

  • do_any \u2013

    Check if at least one of the item in the sequence evaluates to true.

  • get_logger \u2013

    Return a pre-configured logger.

  • get_template_logger \u2013

    Return a logger usable in templates.

  • get_template_logger_function \u2013

    Create a wrapper function that automatically receives the Jinja template context.

  • get_template_path \u2013

    Return the path to the template currently using the given context.

  • makeExtension \u2013

    Create the extension instance.

Attributes:

  • CollectorItem \u2013

    The type of the item returned by the collect method of a handler.

  • HandlerConfig \u2013

    The type of the configuration of a handler.

  • HandlerOptions \u2013

    The type of the options passed to a handler.

  • TEMPLATES_DIRS (Sequence[Path]) \u2013

    The directories where the handler templates are located.

"},{"location":"reference/api/#mkdocstrings.CollectorItem","title":"CollectorItem module-attribute","text":"
CollectorItem = Any\n

The type of the item returned by the collect method of a handler.

Returned by:
  • \u00a0mkdocstrings \u00a0BaseHandler \u00a0collect
Used by:
  • \u00a0mkdocstrings \u00a0BaseHandler \u00a0render
"},{"location":"reference/api/#mkdocstrings.HandlerConfig","title":"HandlerConfig module-attribute","text":"
HandlerConfig = Any\n

The type of the configuration of a handler.

Used by:
  • \u00a0mkdocstrings \u00a0Handlers
"},{"location":"reference/api/#mkdocstrings.HandlerOptions","title":"HandlerOptions module-attribute","text":"
HandlerOptions = Any\n

The type of the options passed to a handler.

Returned by:
  • \u00a0mkdocstrings \u00a0BaseHandler \u00a0get_options
Used by:
  • \u00a0mkdocstrings \u00a0BaseHandler
    • \u00a0collect
    • \u00a0render
"},{"location":"reference/api/#mkdocstrings.TEMPLATES_DIRS","title":"TEMPLATES_DIRS module-attribute","text":"
TEMPLATES_DIRS: Sequence[Path] = tuple(__path__)\n

The directories where the handler templates are located.

"},{"location":"reference/api/#mkdocstrings.AutoDocProcessor","title":"AutoDocProcessor","text":"
AutoDocProcessor(\n    md: Markdown,\n    *,\n    handlers: Handlers,\n    autorefs: AutorefsPlugin,\n)\n

Bases: BlockProcessor

Our \"autodoc\" Markdown block processor.

It has a test method that tells if a block matches a criterion, and a run method that processes it.

It also has utility methods allowing to get handlers and their configuration easily, useful when processing a matched block.

Parameters:

Referenced by:
  • \u00a0mkdocstrings \u00a0MkdocstringsExtension \u00a0extendMarkdown

Methods:

  • run \u2013

    Run code on the matched blocks.

  • test \u2013

    Match our autodoc instructions.

Attributes:

  • md \u2013

    The Markdown instance.

  • regex \u2013

    The regular expression to match our autodoc instructions.

Source code in src/mkdocstrings/_internal/extension.py
def __init__(\n    self,\n    md: Markdown,\n    *,\n    handlers: Handlers,\n    autorefs: AutorefsPlugin,\n) -> None:\n    \"\"\"Initialize the object.\n\n    Arguments:\n        md: A `markdown.Markdown` instance.\n        handlers: The handlers container.\n        autorefs: The autorefs plugin instance.\n    \"\"\"\n    super().__init__(parser=md.parser)\n    self.md = md\n    \"\"\"The Markdown instance.\"\"\"\n    self._handlers = handlers\n    self._autorefs = autorefs\n    self._updated_envs: set = set()\n
"},{"location":"reference/api/#mkdocstrings.AutoDocProcessor(md)","title":"md","text":"(Markdown) \u2013

A markdown.Markdown instance.

"},{"location":"reference/api/#mkdocstrings.AutoDocProcessor(handlers)","title":"handlers","text":"(Handlers) \u2013

The handlers container.

"},{"location":"reference/api/#mkdocstrings.AutoDocProcessor(autorefs)","title":"autorefs","text":"(AutorefsPlugin) \u2013

The autorefs plugin instance.

"},{"location":"reference/api/#mkdocstrings.AutoDocProcessor.md","title":"md instance-attribute","text":"
md = md\n

The Markdown instance.

"},{"location":"reference/api/#mkdocstrings.AutoDocProcessor.regex","title":"regex class-attribute instance-attribute","text":"
regex = compile(\n    \"^(?P<heading>#{1,6} *|)::: ?(?P<name>.+?) *$\",\n    flags=MULTILINE,\n)\n

The regular expression to match our autodoc instructions.

"},{"location":"reference/api/#mkdocstrings.AutoDocProcessor.run","title":"run","text":"
run(parent: Element, blocks: MutableSequence[str]) -> None\n

Run code on the matched blocks.

The identifier and configuration lines are retrieved from a matched block and used to collect and render an object.

Parameters:

Referenced by:
  • \u00a0mkdocstrings \u00a0AutoDocProcessor
Source code in src/mkdocstrings/_internal/extension.py
def run(self, parent: Element, blocks: MutableSequence[str]) -> None:\n    \"\"\"Run code on the matched blocks.\n\n    The identifier and configuration lines are retrieved from a matched block\n    and used to collect and render an object.\n\n    Arguments:\n        parent: The parent element in the XML tree.\n        blocks: The rest of the blocks to be processed.\n    \"\"\"\n    block = blocks.pop(0)\n    match = self.regex.search(block)\n\n    if match:\n        if match.start() > 0:\n            self.parser.parseBlocks(parent, [block[: match.start()]])\n        # removes the first line\n        block = block[match.end() :]\n\n    block, the_rest = self.detab(block)\n\n    if not block and blocks and blocks[0].startswith((\"    handler:\", \"    options:\")):\n        # YAML options were separated from the `:::` line by a blank line.\n        block = blocks.pop(0)\n\n    if match:\n        identifier = match[\"name\"]\n        heading_level = match[\"heading\"].count(\"#\")\n        _logger.debug(\"Matched '::: %s'\", identifier)\n\n        html, handler, _ = self._process_block(identifier, block, heading_level)\n        el = Element(\"div\", {\"class\": \"mkdocstrings\"})\n        # The final HTML is inserted as opaque to subsequent processing, and only revealed at the end.\n        el.text = self.md.htmlStash.store(html)\n\n        if handler.outer_layer:\n            self._process_headings(handler, el)\n\n        parent.append(el)\n\n    if the_rest:\n        # This block contained unindented line(s) after the first indented\n        # line. Insert these lines as the first block of the master blocks\n        # list for future processing.\n        blocks.insert(0, the_rest)\n
"},{"location":"reference/api/#mkdocstrings.AutoDocProcessor.run(parent)","title":"parent","text":"(Element) \u2013

The parent element in the XML tree.

"},{"location":"reference/api/#mkdocstrings.AutoDocProcessor.run(blocks)","title":"blocks","text":"(MutableSequence[str]) \u2013

The rest of the blocks to be processed.

"},{"location":"reference/api/#mkdocstrings.AutoDocProcessor.test","title":"test","text":"
test(parent: Element, block: str) -> bool\n

Match our autodoc instructions.

Parameters:

Returns:

  • bool \u2013

    Whether this block should be processed or not.

Referenced by:
  • \u00a0mkdocstrings \u00a0AutoDocProcessor
Source code in src/mkdocstrings/_internal/extension.py
def test(self, parent: Element, block: str) -> bool:  # noqa: ARG002\n    \"\"\"Match our autodoc instructions.\n\n    Arguments:\n        parent: The parent element in the XML tree.\n        block: The block to be tested.\n\n    Returns:\n        Whether this block should be processed or not.\n    \"\"\"\n    return bool(self.regex.search(block))\n
"},{"location":"reference/api/#mkdocstrings.AutoDocProcessor.test(parent)","title":"parent","text":"(Element) \u2013

The parent element in the XML tree.

"},{"location":"reference/api/#mkdocstrings.AutoDocProcessor.test(block)","title":"block","text":"(str) \u2013

The block to be tested.

"},{"location":"reference/api/#mkdocstrings.BaseHandler","title":"BaseHandler","text":"
BaseHandler(\n    *,\n    theme: str,\n    custom_templates: str | None,\n    mdx: Sequence[str | Extension],\n    mdx_config: Mapping[str, Any],\n)\n

The base handler class.

Inherit from this class to implement a handler.

You will have to implement the collect and render methods. You can also implement the teardown method, and override the update_env method, to add more filters to the Jinja environment, making them available in your Jinja templates.

To define a fallback theme, add a fallback_theme class-variable. To add custom CSS, add an extra_css variable or create an 'style.css' file beside the templates.

If the given theme is not supported (it does not exist), it will look for a fallback_theme attribute in self to use as a fallback theme.

Other Parameters:

  • theme (str) \u2013

    The theme to use.

  • custom_templates (str | None) \u2013

    The path to custom templates.

  • mdx (list[str | Extension]) \u2013

    A list of Markdown extensions to use.

  • mdx_config (Mapping[str, Mapping[str, Any]]) \u2013

    Configuration for the Markdown extensions.

Referenced by:
  • \u00a0mkdocstrings
    • \u00a0Handlers
      • \u00a0seen_handlers
      • \u00a0get_handler
    • \u00a0MkdocstringsPlugin \u00a0get_handler
  • Usage Handlers Custom handlers Code
Returned by:
  • \u00a0mkdocstrings
    • \u00a0Handlers
      • \u00a0seen_handlers
      • \u00a0get_handler
    • \u00a0MkdocstringsPlugin \u00a0get_handler

Methods:

  • collect \u2013

    Collect data given an identifier and user configuration.

  • do_convert_markdown \u2013

    Render Markdown text; for use inside templates.

  • do_heading \u2013

    Render an HTML heading and register it for the table of contents. For use inside templates.

  • get_aliases \u2013

    Return the possible aliases for a given identifier.

  • get_extended_templates_dirs \u2013

    Load template extensions for the given handler, return their templates directories.

  • get_headings \u2013

    Return and clear the headings gathered so far.

  • get_inventory_urls \u2013

    Return the URLs (and configuration options) of the inventory files to download.

  • get_options \u2013

    Get combined options.

  • get_templates_dir \u2013

    Return the path to the handler's templates directory.

  • load_inventory \u2013

    Yield items and their URLs from an inventory file streamed from in_file.

  • render \u2013

    Render a template using provided data and configuration options.

  • render_backlinks \u2013

    Render backlinks.

  • teardown \u2013

    Teardown the handler.

  • update_env \u2013

    Update the Jinja environment.

Attributes:

  • custom_templates \u2013

    The path to custom templates.

  • domain (str) \u2013

    The handler's domain, used to register objects in the inventory, for example \"py\".

  • enable_inventory (bool) \u2013

    Whether the inventory creation is enabled.

  • env \u2013

    The Jinja environment.

  • extra_css (str) \u2013

    Extra CSS.

  • fallback_theme (str) \u2013

    Fallback theme to use when a template isn't found in the configured theme.

  • md (Markdown) \u2013

    The Markdown instance.

  • mdx \u2013

    The Markdown extensions to use.

  • mdx_config \u2013

    The configuration for the Markdown extensions.

  • name (str) \u2013

    The handler's name, for example \"python\".

  • outer_layer (bool) \u2013

    Whether we're in the outer Markdown conversion layer.

  • theme \u2013

    The selected theme.

Source code in src/mkdocstrings/_internal/handlers/base.py
def __init__(\n    self,\n    *,\n    theme: str,\n    custom_templates: str | None,\n    mdx: Sequence[str | Extension],\n    mdx_config: Mapping[str, Any],\n) -> None:\n    \"\"\"Initialize the object.\n\n    If the given theme is not supported (it does not exist), it will look for a `fallback_theme` attribute\n    in `self` to use as a fallback theme.\n\n    Keyword Arguments:\n        theme (str): The theme to use.\n        custom_templates (str | None): The path to custom templates.\n        mdx (list[str | Extension]): A list of Markdown extensions to use.\n        mdx_config (Mapping[str, Mapping[str, Any]]): Configuration for the Markdown extensions.\n    \"\"\"\n    self.theme = theme\n    \"\"\"The selected theme.\"\"\"\n    self.custom_templates = custom_templates\n    \"\"\"The path to custom templates.\"\"\"\n    self.mdx = mdx\n    \"\"\"The Markdown extensions to use.\"\"\"\n    self.mdx_config = mdx_config\n    \"\"\"The configuration for the Markdown extensions.\"\"\"\n    self._md: Markdown | None = None\n    self._headings: list[Element] = []\n\n    paths = []\n\n    # add selected theme templates\n    themes_dir = self.get_templates_dir(self.name)\n    paths.append(themes_dir / self.theme)\n\n    # add extended theme templates\n    extended_templates_dirs = self.get_extended_templates_dirs(self.name)\n    for templates_dir in extended_templates_dirs:\n        paths.append(templates_dir / self.theme)\n\n    # add fallback theme templates\n    if self.fallback_theme and self.fallback_theme != self.theme:\n        paths.append(themes_dir / self.fallback_theme)\n\n        # add fallback theme of extended templates\n        for templates_dir in extended_templates_dirs:\n            paths.append(templates_dir / self.fallback_theme)\n\n    for path in paths:\n        css_path = path / \"style.css\"\n        if css_path.is_file():\n            self.extra_css += \"\\n\" + css_path.read_text(encoding=\"utf-8\")\n            break\n\n    if self.custom_templates is not None:\n        paths.insert(0, Path(self.custom_templates) / self.name / self.theme)\n\n    self.env = Environment(\n        autoescape=True,\n        loader=FileSystemLoader(paths),\n        auto_reload=False,  # Editing a template in the middle of a build is not useful.\n    )\n    \"\"\"The Jinja environment.\"\"\"\n\n    self.env.filters[\"convert_markdown\"] = self.do_convert_markdown\n    self.env.filters[\"heading\"] = self.do_heading\n    self.env.filters[\"any\"] = do_any\n    self.env.globals[\"log\"] = get_template_logger(self.name)\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.custom_templates","title":"custom_templates instance-attribute","text":"
custom_templates = custom_templates\n

The path to custom templates.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.domain","title":"domain class-attribute","text":"
domain: str\n

The handler's domain, used to register objects in the inventory, for example \"py\".

"},{"location":"reference/api/#mkdocstrings.BaseHandler.enable_inventory","title":"enable_inventory class-attribute","text":"
enable_inventory: bool = False\n

Whether the inventory creation is enabled.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.env","title":"env instance-attribute","text":"
env = Environment(\n    autoescape=True,\n    loader=FileSystemLoader(paths),\n    auto_reload=False,\n)\n

The Jinja environment.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.extra_css","title":"extra_css class-attribute instance-attribute","text":"
extra_css: str = ''\n

Extra CSS.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.fallback_theme","title":"fallback_theme class-attribute","text":"
fallback_theme: str = ''\n

Fallback theme to use when a template isn't found in the configured theme.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.md","title":"md property","text":"
md: Markdown\n

The Markdown instance.

Raises:

  • RuntimeError \u2013

    When the Markdown instance is not set yet.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.mdx","title":"mdx instance-attribute","text":"
mdx = mdx\n

The Markdown extensions to use.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.mdx_config","title":"mdx_config instance-attribute","text":"
mdx_config = mdx_config\n

The configuration for the Markdown extensions.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.name","title":"name class-attribute","text":"
name: str\n

The handler's name, for example \"python\".

"},{"location":"reference/api/#mkdocstrings.BaseHandler.outer_layer","title":"outer_layer property","text":"
outer_layer: bool\n

Whether we're in the outer Markdown conversion layer.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.theme","title":"theme instance-attribute","text":"
theme = theme\n

The selected theme.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.collect","title":"collect","text":"
collect(\n    identifier: str, options: HandlerOptions\n) -> CollectorItem\n

Collect data given an identifier and user configuration.

In the implementation, you typically call a subprocess that returns JSON, and load that JSON again into a Python dictionary for example, though the implementation is completely free.

Parameters:

Returns:

  • CollectorItem \u2013

    Anything you want, as long as you can feed it to the handler's render method.

Source code in src/mkdocstrings/_internal/handlers/base.py
def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem:\n    \"\"\"Collect data given an identifier and user configuration.\n\n    In the implementation, you typically call a subprocess that returns JSON, and load that JSON again into\n    a Python dictionary for example, though the implementation is completely free.\n\n    Arguments:\n        identifier: An identifier for which to collect data. For example, in Python,\n            it would be 'mkdocstrings.handlers' to collect documentation about the handlers module.\n            It can be anything that you can feed to the tool of your choice.\n        options: The final configuration options.\n\n    Returns:\n        Anything you want, as long as you can feed it to the handler's `render` method.\n    \"\"\"\n    raise NotImplementedError\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.collect(identifier)","title":"identifier","text":"(str) \u2013

An identifier for which to collect data. For example, in Python, it would be 'mkdocstrings.handlers' to collect documentation about the handlers module. It can be anything that you can feed to the tool of your choice.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.collect(options)","title":"options","text":"(HandlerOptions) \u2013

The final configuration options.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.do_convert_markdown","title":"do_convert_markdown","text":"
do_convert_markdown(\n    text: str,\n    heading_level: int,\n    html_id: str = \"\",\n    *,\n    strip_paragraph: bool = False,\n    autoref_hook: AutorefsHookInterface | None = None,\n) -> Markup\n

Render Markdown text; for use inside templates.

Parameters:

Returns:

  • Markup \u2013

    An HTML string.

Source code in src/mkdocstrings/_internal/handlers/base.py
def do_convert_markdown(\n    self,\n    text: str,\n    heading_level: int,\n    html_id: str = \"\",\n    *,\n    strip_paragraph: bool = False,\n    autoref_hook: AutorefsHookInterface | None = None,\n) -> Markup:\n    \"\"\"Render Markdown text; for use inside templates.\n\n    Arguments:\n        text: The text to convert.\n        heading_level: The base heading level to start all Markdown headings from.\n        html_id: The HTML id of the element that's considered the parent of this element.\n        strip_paragraph: Whether to exclude the `<p>` tag from around the whole output.\n\n    Returns:\n        An HTML string.\n    \"\"\"\n    global _markdown_conversion_layer  # noqa: PLW0603\n    _markdown_conversion_layer += 1\n    treeprocessors = self.md.treeprocessors\n    treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = heading_level  # type: ignore[attr-defined]\n    treeprocessors[IdPrependingTreeprocessor.name].id_prefix = html_id and html_id + \"--\"  # type: ignore[attr-defined]\n    treeprocessors[ParagraphStrippingTreeprocessor.name].strip = strip_paragraph  # type: ignore[attr-defined]\n    if BacklinksTreeProcessor.name in treeprocessors:\n        treeprocessors[BacklinksTreeProcessor.name].initial_id = html_id  # type: ignore[attr-defined]\n    if autoref_hook and AutorefsInlineProcessor.name in self.md.inlinePatterns:\n        self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = autoref_hook  # type: ignore[attr-defined]\n\n    try:\n        return Markup(self.md.convert(text))\n    finally:\n        treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = 0  # type: ignore[attr-defined]\n        treeprocessors[IdPrependingTreeprocessor.name].id_prefix = \"\"  # type: ignore[attr-defined]\n        treeprocessors[ParagraphStrippingTreeprocessor.name].strip = False  # type: ignore[attr-defined]\n        if BacklinksTreeProcessor.name in treeprocessors:\n            treeprocessors[BacklinksTreeProcessor.name].initial_id = None  # type: ignore[attr-defined]\n        if AutorefsInlineProcessor.name in self.md.inlinePatterns:\n            self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = None  # type: ignore[attr-defined]\n        self.md.reset()\n        _markdown_conversion_layer -= 1\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.do_convert_markdown(text)","title":"text","text":"(str) \u2013

The text to convert.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.do_convert_markdown(heading_level)","title":"heading_level","text":"(int) \u2013

The base heading level to start all Markdown headings from.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.do_convert_markdown(html_id)","title":"html_id","text":"(str, default: '' ) \u2013

The HTML id of the element that's considered the parent of this element.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.do_convert_markdown(strip_paragraph)","title":"strip_paragraph","text":"(bool, default: False ) \u2013

Whether to exclude the <p> tag from around the whole output.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.do_heading","title":"do_heading","text":"
do_heading(\n    content: Markup,\n    heading_level: int,\n    *,\n    role: str | None = None,\n    hidden: bool = False,\n    toc_label: str | None = None,\n    skip_inventory: bool = False,\n    **attributes: str,\n) -> Markup\n

Render an HTML heading and register it for the table of contents. For use inside templates.

Parameters:

Returns:

  • Markup \u2013

    An HTML string.

Source code in src/mkdocstrings/_internal/handlers/base.py
def do_heading(\n    self,\n    content: Markup,\n    heading_level: int,\n    *,\n    role: str | None = None,\n    hidden: bool = False,\n    toc_label: str | None = None,\n    skip_inventory: bool = False,\n    **attributes: str,\n) -> Markup:\n    \"\"\"Render an HTML heading and register it for the table of contents. For use inside templates.\n\n    Arguments:\n        content: The HTML within the heading.\n        heading_level: The level of heading (e.g. 3 -> `h3`).\n        role: An optional role for the object bound to this heading.\n        hidden: If True, only register it for the table of contents, don't render anything.\n        toc_label: The title to use in the table of contents ('data-toc-label' attribute).\n        skip_inventory: Flag element to not be registered in the inventory (by setting a `data-skip-inventory` attribute).\n        **attributes: Any extra HTML attributes of the heading.\n\n    Returns:\n        An HTML string.\n    \"\"\"\n    # Produce a heading element that will be used later, in `AutoDocProcessor.run`, to:\n    # - register it in the ToC: right now we're in the inner Markdown conversion layer,\n    #   so we have to bubble up the information to the outer Markdown conversion layer,\n    #   for the ToC extension to pick it up.\n    # - register it in autorefs: right now we don't know what page is being rendered,\n    #   so we bubble up the information again to where autorefs knows the page,\n    #   and can correctly register the heading anchor (id) to its full URL.\n    # - register it in the objects inventory: same as for autorefs,\n    #   we don't know the page here, or the handler (and its domain),\n    #   so we bubble up the information to where the mkdocstrings extension knows that.\n    el = Element(f\"h{heading_level}\", attributes)\n    if toc_label is None:\n        toc_label = content.unescape() if isinstance(content, Markup) else content\n    el.set(\"data-toc-label\", toc_label)\n    if skip_inventory:\n        el.set(\"data-skip-inventory\", \"true\")\n    if role:\n        el.set(\"data-role\", role)\n    if content:\n        el.text = str(content).strip()\n    self._headings.append(el)\n\n    if hidden:\n        return Markup('<a id=\"{0}\"></a>').format(attributes[\"id\"])\n\n    # Now produce the actual HTML to be rendered. The goal is to wrap the HTML content into a heading.\n    # Start with a heading that has just attributes (no text), and add a placeholder into it.\n    el = Element(f\"h{heading_level}\", attributes)\n    el.append(Element(\"mkdocstrings-placeholder\"))\n    # Tell the inner 'toc' extension to make its additions if configured so.\n    toc = cast(\"TocTreeprocessor\", self.md.treeprocessors[\"toc\"])\n    if toc.use_anchors:\n        toc.add_anchor(el, attributes[\"id\"])\n    if toc.use_permalinks:\n        toc.add_permalink(el, attributes[\"id\"])\n\n    # The content we received is HTML, so it can't just be inserted into the tree. We had marked the middle\n    # of the heading with a placeholder that can never occur (text can't directly contain angle brackets).\n    # Now this HTML wrapper can be \"filled\" by replacing the placeholder.\n    html_with_placeholder = tostring(el, encoding=\"unicode\")\n    assert (  # noqa: S101\n        html_with_placeholder.count(\"<mkdocstrings-placeholder />\") == 1\n    ), f\"Bug in mkdocstrings: failed to replace in {html_with_placeholder!r}\"\n    html = html_with_placeholder.replace(\"<mkdocstrings-placeholder />\", content)\n    return Markup(html)\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.do_heading(content)","title":"content","text":"(Markup) \u2013

The HTML within the heading.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.do_heading(heading_level)","title":"heading_level","text":"(int) \u2013

The level of heading (e.g. 3 -> h3).

"},{"location":"reference/api/#mkdocstrings.BaseHandler.do_heading(role)","title":"role","text":"(str | None, default: None ) \u2013

An optional role for the object bound to this heading.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.do_heading(hidden)","title":"hidden","text":"(bool, default: False ) \u2013

If True, only register it for the table of contents, don't render anything.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.do_heading(toc_label)","title":"toc_label","text":"(str | None, default: None ) \u2013

The title to use in the table of contents ('data-toc-label' attribute).

"},{"location":"reference/api/#mkdocstrings.BaseHandler.do_heading(skip_inventory)","title":"skip_inventory","text":"(bool, default: False ) \u2013

Flag element to not be registered in the inventory (by setting a data-skip-inventory attribute).

"},{"location":"reference/api/#mkdocstrings.BaseHandler.do_heading(**attributes)","title":"**attributes","text":"(str, default: {} ) \u2013

Any extra HTML attributes of the heading.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.get_aliases","title":"get_aliases","text":"
get_aliases(identifier: str) -> tuple[str, ...]\n

Return the possible aliases for a given identifier.

Parameters:

Returns:

  • tuple[str, ...] \u2013

    A tuple of strings - aliases.

Source code in src/mkdocstrings/_internal/handlers/base.py
def get_aliases(self, identifier: str) -> tuple[str, ...]:  # noqa: ARG002\n    \"\"\"Return the possible aliases for a given identifier.\n\n    Arguments:\n        identifier: The identifier to get the aliases of.\n\n    Returns:\n        A tuple of strings - aliases.\n    \"\"\"\n    return ()\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.get_aliases(identifier)","title":"identifier","text":"(str) \u2013

The identifier to get the aliases of.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.get_extended_templates_dirs","title":"get_extended_templates_dirs","text":"
get_extended_templates_dirs(handler: str) -> list[Path]\n

Load template extensions for the given handler, return their templates directories.

Parameters:

Returns:

  • list[Path] \u2013

    The extensions templates directories.

Source code in src/mkdocstrings/_internal/handlers/base.py
def get_extended_templates_dirs(self, handler: str) -> list[Path]:\n    \"\"\"Load template extensions for the given handler, return their templates directories.\n\n    Arguments:\n        handler: The name of the handler to get the extended templates directory of.\n\n    Returns:\n        The extensions templates directories.\n    \"\"\"\n    discovered_extensions = entry_points(group=f\"mkdocstrings.{handler}.templates\")\n    return [extension.load()() for extension in discovered_extensions]\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.get_extended_templates_dirs(handler)","title":"handler","text":"(str) \u2013

The name of the handler to get the extended templates directory of.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.get_headings","title":"get_headings","text":"
get_headings() -> Sequence[Element]\n

Return and clear the headings gathered so far.

Returns:

  • Sequence[Element] \u2013

    A list of HTML elements.

Source code in src/mkdocstrings/_internal/handlers/base.py
def get_headings(self) -> Sequence[Element]:\n    \"\"\"Return and clear the headings gathered so far.\n\n    Returns:\n        A list of HTML elements.\n    \"\"\"\n    result = list(self._headings)\n    self._headings.clear()\n    return result\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.get_inventory_urls","title":"get_inventory_urls","text":"
get_inventory_urls() -> list[tuple[str, dict[str, Any]]]\n

Return the URLs (and configuration options) of the inventory files to download.

Source code in src/mkdocstrings/_internal/handlers/base.py
def get_inventory_urls(self) -> list[tuple[str, dict[str, Any]]]:\n    \"\"\"Return the URLs (and configuration options) of the inventory files to download.\"\"\"\n    return []\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.get_options","title":"get_options","text":"
get_options(\n    local_options: Mapping[str, Any],\n) -> HandlerOptions\n

Get combined options.

Override this method to customize how options are combined, for example by merging the global options with the local options. By combining options here, you don't have to do it twice in collect and render.

Parameters:

Returns:

  • HandlerOptions \u2013

    The combined options.

Source code in src/mkdocstrings/_internal/handlers/base.py
def get_options(self, local_options: Mapping[str, Any]) -> HandlerOptions:\n    \"\"\"Get combined options.\n\n    Override this method to customize how options are combined,\n    for example by merging the global options with the local options.\n    By combining options here, you don't have to do it twice in `collect` and `render`.\n\n    Arguments:\n        local_options: The local options.\n\n    Returns:\n        The combined options.\n    \"\"\"\n    return local_options\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.get_options(local_options)","title":"local_options","text":"(Mapping[str, Any]) \u2013

The local options.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.get_templates_dir","title":"get_templates_dir","text":"
get_templates_dir(handler: str | None = None) -> Path\n

Return the path to the handler's templates directory.

Override to customize how the templates directory is found.

Parameters:

Raises:

  • ModuleNotFoundError \u2013

    When no such handler is installed.

  • FileNotFoundError \u2013

    When the templates directory cannot be found.

Returns:

  • Path \u2013

    The templates directory path.

Source code in src/mkdocstrings/_internal/handlers/base.py
def get_templates_dir(self, handler: str | None = None) -> Path:\n    \"\"\"Return the path to the handler's templates directory.\n\n    Override to customize how the templates directory is found.\n\n    Arguments:\n        handler: The name of the handler to get the templates directory of.\n\n    Raises:\n        ModuleNotFoundError: When no such handler is installed.\n        FileNotFoundError: When the templates directory cannot be found.\n\n    Returns:\n        The templates directory path.\n    \"\"\"\n    handler = handler or self.name\n    try:\n        import mkdocstrings_handlers  # noqa: PLC0415\n    except ModuleNotFoundError as error:\n        raise ModuleNotFoundError(f\"Handler '{handler}' not found, is it installed?\") from error\n\n    for path in mkdocstrings_handlers.__path__:\n        theme_path = Path(path, handler, \"templates\")\n        if theme_path.exists():\n            return theme_path\n\n    raise FileNotFoundError(f\"Can't find 'templates' folder for handler '{handler}'\")\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.get_templates_dir(handler)","title":"handler","text":"(str | None, default: None ) \u2013

The name of the handler to get the templates directory of.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.load_inventory","title":"load_inventory classmethod","text":"
load_inventory(\n    in_file: BinaryIO,\n    url: str,\n    base_url: str | None = None,\n    **kwargs: Any,\n) -> Iterator[tuple[str, str]]\n

Yield items and their URLs from an inventory file streamed from in_file.

Parameters:

Yields:

  • tuple[str, str] \u2013

    Tuples of (item identifier, item URL).

Source code in src/mkdocstrings/_internal/handlers/base.py
@classmethod\ndef load_inventory(\n    cls,\n    in_file: BinaryIO,  # noqa: ARG003\n    url: str,  # noqa: ARG003\n    base_url: str | None = None,  # noqa: ARG003\n    **kwargs: Any,  # noqa: ARG003\n) -> Iterator[tuple[str, str]]:\n    \"\"\"Yield items and their URLs from an inventory file streamed from `in_file`.\n\n    Arguments:\n        in_file: The binary file-like object to read the inventory from.\n        url: The URL that this file is being streamed from (used to guess `base_url`).\n        base_url: The URL that this inventory's sub-paths are relative to.\n        **kwargs: Ignore additional arguments passed from the config.\n\n    Yields:\n        Tuples of (item identifier, item URL).\n    \"\"\"\n    yield from ()\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.load_inventory(in_file)","title":"in_file","text":"(BinaryIO) \u2013

The binary file-like object to read the inventory from.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.load_inventory(url)","title":"url","text":"(str) \u2013

The URL that this file is being streamed from (used to guess base_url).

"},{"location":"reference/api/#mkdocstrings.BaseHandler.load_inventory(base_url)","title":"base_url","text":"(str | None, default: None ) \u2013

The URL that this inventory's sub-paths are relative to.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.load_inventory(**kwargs)","title":"**kwargs","text":"(Any, default: {} ) \u2013

Ignore additional arguments passed from the config.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.render","title":"render","text":"
render(\n    data: CollectorItem,\n    options: HandlerOptions,\n    *,\n    locale: str | None = None,\n) -> str\n

Render a template using provided data and configuration options.

Parameters:

Returns:

  • str \u2013

    The rendered template as HTML.

Source code in src/mkdocstrings/_internal/handlers/base.py
def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str:\n    \"\"\"Render a template using provided data and configuration options.\n\n    Arguments:\n        data: The collected data to render.\n        options: The final configuration options.\n        locale: The locale to use for translations, if any.\n\n    Returns:\n        The rendered template as HTML.\n    \"\"\"\n    raise NotImplementedError\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.render(data)","title":"data","text":"(CollectorItem) \u2013

The collected data to render.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.render(options)","title":"options","text":"(HandlerOptions) \u2013

The final configuration options.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.render(locale)","title":"locale","text":"(str | None, default: None ) \u2013

The locale to use for translations, if any.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.render_backlinks","title":"render_backlinks","text":"
render_backlinks(\n    backlinks: Mapping[str, Iterable[Backlink]],\n    *,\n    locale: str | None = None,\n) -> str\n

Render backlinks.

Parameters:

Returns:

  • str \u2013

    The rendered backlinks as HTML.

Source code in src/mkdocstrings/_internal/handlers/base.py
def render_backlinks(self, backlinks: Mapping[str, Iterable[Backlink]], *, locale: str | None = None) -> str:  # noqa: ARG002\n    \"\"\"Render backlinks.\n\n    Parameters:\n        backlinks: A mapping of identifiers to backlinks.\n        locale: The locale to use for translations, if any.\n\n    Returns:\n        The rendered backlinks as HTML.\n    \"\"\"\n    return \"\"\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.render_backlinks(backlinks)","title":"backlinks","text":"(Mapping[str, Iterable[Backlink]]) \u2013

A mapping of identifiers to backlinks.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.render_backlinks(locale)","title":"locale","text":"(str | None, default: None ) \u2013

The locale to use for translations, if any.

"},{"location":"reference/api/#mkdocstrings.BaseHandler.teardown","title":"teardown","text":"
teardown() -> None\n

Teardown the handler.

This method should be implemented to, for example, terminate a subprocess that was started when creating the handler instance.

Source code in src/mkdocstrings/_internal/handlers/base.py
def teardown(self) -> None:\n    \"\"\"Teardown the handler.\n\n    This method should be implemented to, for example, terminate a subprocess\n    that was started when creating the handler instance.\n    \"\"\"\n
"},{"location":"reference/api/#mkdocstrings.BaseHandler.update_env","title":"update_env","text":"
update_env(config: Any) -> None\n

Update the Jinja environment.

Source code in src/mkdocstrings/_internal/handlers/base.py
def update_env(self, config: Any) -> None:\n    \"\"\"Update the Jinja environment.\"\"\"\n
"},{"location":"reference/api/#mkdocstrings.CollectionError","title":"CollectionError","text":"

Bases: Exception

An exception raised when some collection of data failed.

"},{"location":"reference/api/#mkdocstrings.Handlers","title":"Handlers","text":"
Handlers(\n    *,\n    theme: str,\n    default: str,\n    inventory_project: str,\n    inventory_version: str = \"0.0.0\",\n    handlers_config: dict[str, HandlerConfig] | None = None,\n    custom_templates: str | None = None,\n    mdx: Sequence[str | Extension] | None = None,\n    mdx_config: Mapping[str, Any] | None = None,\n    locale: str = \"en\",\n    tool_config: Any,\n)\n

A collection of handlers.

Do not instantiate this directly. The plugin will keep one instance of this for the purpose of caching. Use mkdocstrings.MkdocstringsPlugin.get_handler for convenient access.

Parameters:

Referenced by:
  • \u00a0mkdocstrings \u00a0MkdocstringsPlugin \u00a0handlers
Returned by:
  • \u00a0mkdocstrings \u00a0MkdocstringsPlugin \u00a0handlers
Used by:
  • \u00a0mkdocstrings
    • \u00a0AutoDocProcessor
    • \u00a0MkdocstringsExtension

Methods:

  • get_handler \u2013

    Get a handler thanks to its name.

  • get_handler_config \u2013

    Return the global configuration of the given handler.

  • get_handler_name \u2013

    Return the handler name defined in an \"autodoc\" instruction YAML configuration, or the global default handler.

  • teardown \u2013

    Teardown all cached handlers and clear the cache.

Attributes:

  • inventory (Inventory) \u2013

    The objects inventory.

  • seen_handlers (Iterable[BaseHandler]) \u2013

    Get the handlers that were encountered so far throughout the build.

Source code in src/mkdocstrings/_internal/handlers/base.py
def __init__(\n    self,\n    *,\n    theme: str,\n    default: str,\n    inventory_project: str,\n    inventory_version: str = \"0.0.0\",\n    handlers_config: dict[str, HandlerConfig] | None = None,\n    custom_templates: str | None = None,\n    mdx: Sequence[str | Extension] | None = None,\n    mdx_config: Mapping[str, Any] | None = None,\n    locale: str = \"en\",\n    tool_config: Any,\n) -> None:\n    \"\"\"Initialize the object.\n\n    Arguments:\n        theme: The theme to use.\n        default: The default handler to use.\n        inventory_project: The project name to use in the inventory.\n        inventory_version: The project version to use in the inventory.\n        handlers_config: The handlers configuration.\n        custom_templates: The path to custom templates.\n        mdx: A list of Markdown extensions to use.\n        mdx_config: Configuration for the Markdown extensions.\n        locale: The locale to use for translations.\n        tool_config: Tool configuration to pass down to handlers.\n    \"\"\"\n    self._theme = theme\n    self._default = default\n    self._handlers_config = handlers_config or {}\n    self._custom_templates = custom_templates\n    self._mdx = mdx or []\n    self._mdx_config = mdx_config or {}\n    self._handlers: dict[str, BaseHandler] = {}\n    self._locale = locale\n    self._tool_config = tool_config\n\n    self.inventory: Inventory = Inventory(project=inventory_project, version=inventory_version)\n    \"\"\"The objects inventory.\"\"\"\n\n    self._inv_futures: dict[futures.Future, tuple[BaseHandler, str, Any]] = {}\n
"},{"location":"reference/api/#mkdocstrings.Handlers(theme)","title":"theme","text":"(str) \u2013

The theme to use.

"},{"location":"reference/api/#mkdocstrings.Handlers(default)","title":"default","text":"(str) \u2013

The default handler to use.

"},{"location":"reference/api/#mkdocstrings.Handlers(inventory_project)","title":"inventory_project","text":"(str) \u2013

The project name to use in the inventory.

"},{"location":"reference/api/#mkdocstrings.Handlers(inventory_version)","title":"inventory_version","text":"(str, default: '0.0.0' ) \u2013

The project version to use in the inventory.

"},{"location":"reference/api/#mkdocstrings.Handlers(handlers_config)","title":"handlers_config","text":"(dict[str, HandlerConfig] | None, default: None ) \u2013

The handlers configuration.

"},{"location":"reference/api/#mkdocstrings.Handlers(custom_templates)","title":"custom_templates","text":"(str | None, default: None ) \u2013

The path to custom templates.

"},{"location":"reference/api/#mkdocstrings.Handlers(mdx)","title":"mdx","text":"(Sequence[str | Extension] | None, default: None ) \u2013

A list of Markdown extensions to use.

"},{"location":"reference/api/#mkdocstrings.Handlers(mdx_config)","title":"mdx_config","text":"(Mapping[str, Any] | None, default: None ) \u2013

Configuration for the Markdown extensions.

"},{"location":"reference/api/#mkdocstrings.Handlers(locale)","title":"locale","text":"(str, default: 'en' ) \u2013

The locale to use for translations.

"},{"location":"reference/api/#mkdocstrings.Handlers(tool_config)","title":"tool_config","text":"(Any) \u2013

Tool configuration to pass down to handlers.

"},{"location":"reference/api/#mkdocstrings.Handlers.inventory","title":"inventory instance-attribute","text":"
inventory: Inventory = Inventory(\n    project=inventory_project, version=inventory_version\n)\n

The objects inventory.

"},{"location":"reference/api/#mkdocstrings.Handlers.seen_handlers","title":"seen_handlers property","text":"
seen_handlers: Iterable[BaseHandler]\n

Get the handlers that were encountered so far throughout the build.

Returns:

  • Iterable[BaseHandler] \u2013

    An iterable of instances of BaseHandler

  • Iterable[BaseHandler] \u2013

    (usable only to loop through it).

"},{"location":"reference/api/#mkdocstrings.Handlers.get_handler","title":"get_handler","text":"
get_handler(\n    name: str, handler_config: dict | None = None\n) -> BaseHandler\n

Get a handler thanks to its name.

This function dynamically imports a module named \"mkdocstrings.handlers.NAME\", calls its get_handler method to get an instance of a handler, and caches it in dictionary. It means that during one run (for each reload when serving, or once when building), a handler is instantiated only once, and reused for each \"autodoc\" instruction asking for it.

Parameters:

Returns:

  • BaseHandler \u2013

    An instance of a subclass of BaseHandler, as instantiated by the get_handler method of the handler's module.

Referenced by:
  • \u00a0mkdocstrings \u00a0MkdocstringsPlugin \u00a0get_handler
Source code in src/mkdocstrings/_internal/handlers/base.py
def get_handler(self, name: str, handler_config: dict | None = None) -> BaseHandler:\n    \"\"\"Get a handler thanks to its name.\n\n    This function dynamically imports a module named \"mkdocstrings.handlers.NAME\", calls its\n    `get_handler` method to get an instance of a handler, and caches it in dictionary.\n    It means that during one run (for each reload when serving, or once when building),\n    a handler is instantiated only once, and reused for each \"autodoc\" instruction asking for it.\n\n    Arguments:\n        name: The name of the handler. Really, it's the name of the Python module holding it.\n        handler_config: Configuration passed to the handler.\n\n    Returns:\n        An instance of a subclass of [`BaseHandler`][mkdocstrings.BaseHandler],\n            as instantiated by the `get_handler` method of the handler's module.\n    \"\"\"\n    if name not in self._handlers:\n        if handler_config is None:\n            handler_config = self._handlers_config.get(name, {})\n        module = importlib.import_module(f\"mkdocstrings_handlers.{name}\")\n\n        self._handlers[name] = module.get_handler(\n            theme=self._theme,\n            custom_templates=self._custom_templates,\n            mdx=self._mdx,\n            mdx_config=self._mdx_config,\n            handler_config=handler_config,\n            tool_config=self._tool_config,\n        )\n    return self._handlers[name]\n
"},{"location":"reference/api/#mkdocstrings.Handlers.get_handler(name)","title":"name","text":"(str) \u2013

The name of the handler. Really, it's the name of the Python module holding it.

"},{"location":"reference/api/#mkdocstrings.Handlers.get_handler(handler_config)","title":"handler_config","text":"(dict | None, default: None ) \u2013

Configuration passed to the handler.

"},{"location":"reference/api/#mkdocstrings.Handlers.get_handler_config","title":"get_handler_config","text":"
get_handler_config(name: str) -> dict\n

Return the global configuration of the given handler.

Parameters:

Returns:

  • dict \u2013

    The global configuration of the given handler. It can be an empty dictionary.

Source code in src/mkdocstrings/_internal/handlers/base.py
def get_handler_config(self, name: str) -> dict:\n    \"\"\"Return the global configuration of the given handler.\n\n    Arguments:\n        name: The name of the handler to get the global configuration of.\n\n    Returns:\n        The global configuration of the given handler. It can be an empty dictionary.\n    \"\"\"\n    return self._handlers_config.get(name, None) or {}\n
"},{"location":"reference/api/#mkdocstrings.Handlers.get_handler_config(name)","title":"name","text":"(str) \u2013

The name of the handler to get the global configuration of.

"},{"location":"reference/api/#mkdocstrings.Handlers.get_handler_name","title":"get_handler_name","text":"
get_handler_name(config: dict) -> str\n

Return the handler name defined in an \"autodoc\" instruction YAML configuration, or the global default handler.

Parameters:

Returns:

  • str \u2013

    The name of the handler to use.

Source code in src/mkdocstrings/_internal/handlers/base.py
def get_handler_name(self, config: dict) -> str:\n    \"\"\"Return the handler name defined in an \"autodoc\" instruction YAML configuration, or the global default handler.\n\n    Arguments:\n        config: A configuration dictionary, obtained from YAML below the \"autodoc\" instruction.\n\n    Returns:\n        The name of the handler to use.\n    \"\"\"\n    return config.get(\"handler\", self._default)\n
"},{"location":"reference/api/#mkdocstrings.Handlers.get_handler_name(config)","title":"config","text":"(dict) \u2013

A configuration dictionary, obtained from YAML below the \"autodoc\" instruction.

"},{"location":"reference/api/#mkdocstrings.Handlers.teardown","title":"teardown","text":"
teardown() -> None\n

Teardown all cached handlers and clear the cache.

Source code in src/mkdocstrings/_internal/handlers/base.py
def teardown(self) -> None:\n    \"\"\"Teardown all cached handlers and clear the cache.\"\"\"\n    for future in self._inv_futures:\n        future.cancel()\n    for handler in self.seen_handlers:\n        handler.teardown()\n    self._handlers.clear()\n
"},{"location":"reference/api/#mkdocstrings.HeadingShiftingTreeprocessor","title":"HeadingShiftingTreeprocessor","text":"
HeadingShiftingTreeprocessor(md: Markdown, shift_by: int)\n

Bases: Treeprocessor

Shift levels of all Markdown headings according to the configured base level.

Parameters:

Referenced by:
  • Usage Cross-references Cross-references to a sub-heading in a docstring

Methods:

  • run \u2013

    Shift the levels of all headings in the document.

Attributes:

  • name (str) \u2013

    The name of the treeprocessor.

  • regex (Pattern) \u2013

    The regex to match heading tags.

  • shift_by (int) \u2013

    The number of heading \"levels\" to add to every heading. <h2> with shift_by = 3 becomes <h5>.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
def __init__(self, md: Markdown, shift_by: int):\n    \"\"\"Initialize the object.\n\n    Arguments:\n        md: A `markdown.Markdown` instance.\n        shift_by: The number of heading \"levels\" to add to every heading.\n    \"\"\"\n    super().__init__(md)\n    self.shift_by = shift_by\n
"},{"location":"reference/api/#mkdocstrings.HeadingShiftingTreeprocessor(md)","title":"md","text":"(Markdown) \u2013

A markdown.Markdown instance.

"},{"location":"reference/api/#mkdocstrings.HeadingShiftingTreeprocessor(shift_by)","title":"shift_by","text":"(int) \u2013

The number of heading \"levels\" to add to every heading.

"},{"location":"reference/api/#mkdocstrings.HeadingShiftingTreeprocessor.name","title":"name class-attribute instance-attribute","text":"
name: str = 'mkdocstrings_headings'\n

The name of the treeprocessor.

"},{"location":"reference/api/#mkdocstrings.HeadingShiftingTreeprocessor.regex","title":"regex class-attribute instance-attribute","text":"
regex: Pattern = compile('([Hh])([1-6])')\n

The regex to match heading tags.

"},{"location":"reference/api/#mkdocstrings.HeadingShiftingTreeprocessor.shift_by","title":"shift_by instance-attribute","text":"
shift_by: int = shift_by\n

The number of heading \"levels\" to add to every heading. <h2> with shift_by = 3 becomes <h5>.

"},{"location":"reference/api/#mkdocstrings.HeadingShiftingTreeprocessor.run","title":"run","text":"
run(root: Element) -> None\n

Shift the levels of all headings in the document.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
def run(self, root: Element) -> None:\n    \"\"\"Shift the levels of all headings in the document.\"\"\"\n    if not self.shift_by:\n        return\n    for el in root.iter():\n        match = self.regex.fullmatch(el.tag)\n        if match:\n            level = int(match[2]) + self.shift_by\n            level = max(1, min(level, 6))\n            el.tag = f\"{match[1]}{level}\"\n
"},{"location":"reference/api/#mkdocstrings.Highlighter","title":"Highlighter","text":"
Highlighter(md: Markdown)\n

Bases: Highlight

Code highlighter that tries to match the Markdown configuration.

Picking up the global config and defaults works only if you use the codehilite or pymdownx.highlight (recommended) Markdown extension.

  • If you use pymdownx.highlight, highlighting settings are picked up from it, and the default CSS class is .highlight. This also means the default of guess_lang: false.

  • Otherwise, if you use the codehilite extension, settings are picked up from it, and the default CSS class is .codehilite. Also consider setting guess_lang: false.

  • If neither are added to markdown_extensions, highlighting is enabled anyway. This is for backwards compatibility. If you really want to disable highlighting even in mkdocstrings, add one of these extensions anyway and set use_pygments: false.

The underlying implementation is pymdownx.highlight regardless.

Parameters:

Referenced by:
  • Usage Themes Customization Syntax highlighting

Methods:

  • highlight \u2013

    Highlight a code-snippet.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
def __init__(self, md: Markdown):\n    \"\"\"Configure to match a `markdown.Markdown` instance.\n\n    Arguments:\n        md: The Markdown instance to read configs from.\n    \"\"\"\n    config: dict[str, Any] = {}\n    self._highlighter: str | None = None\n    for ext in md.registeredExtensions:\n        if isinstance(ext, HighlightExtension) and (ext.enabled or not config):\n            self._highlighter = \"highlight\"\n            config = ext.getConfigs()\n            break  # This one takes priority, no need to continue looking\n        if isinstance(ext, CodeHiliteExtension) and not config:\n            self._highlighter = \"codehilite\"\n            config = ext.getConfigs()\n            config[\"language_prefix\"] = config[\"lang_prefix\"]\n    self._css_class = config.pop(\"css_class\", \"highlight\")\n    super().__init__(**{name: opt for name, opt in config.items() if name in self._highlight_config_keys})\n
"},{"location":"reference/api/#mkdocstrings.Highlighter(md)","title":"md","text":"(Markdown) \u2013

The Markdown instance to read configs from.

"},{"location":"reference/api/#mkdocstrings.Highlighter.highlight","title":"highlight","text":"
highlight(\n    src: str,\n    language: str | None = None,\n    *,\n    inline: bool = False,\n    dedent: bool = True,\n    linenums: bool | None = None,\n    **kwargs: Any,\n) -> str\n

Highlight a code-snippet.

Parameters:

Returns:

  • str \u2013

    The highlighted code as HTML text, marked safe (not escaped for HTML).

Source code in src/mkdocstrings/_internal/handlers/rendering.py
def highlight(\n    self,\n    src: str,\n    language: str | None = None,\n    *,\n    inline: bool = False,\n    dedent: bool = True,\n    linenums: bool | None = None,\n    **kwargs: Any,\n) -> str:\n    \"\"\"Highlight a code-snippet.\n\n    Arguments:\n        src: The code to highlight.\n        language: Explicitly tell what language to use for highlighting.\n        inline: Whether to highlight as inline.\n        dedent: Whether to dedent the code before highlighting it or not.\n        linenums: Whether to add line numbers in the result.\n        **kwargs: Pass on to `pymdownx.highlight.Highlight.highlight`.\n\n    Returns:\n        The highlighted code as HTML text, marked safe (not escaped for HTML).\n    \"\"\"\n    if isinstance(src, Markup):\n        src = src.unescape()\n    if dedent:\n        src = textwrap.dedent(src)\n\n    kwargs.setdefault(\"css_class\", self._css_class)\n    old_linenums = self.linenums  # type: ignore[has-type]\n    if linenums is not None:\n        self.linenums = linenums\n    try:\n        result = super().highlight(src, language, inline=inline, **kwargs)\n    finally:\n        self.linenums = old_linenums\n\n    if inline:\n        # From the maintainer of codehilite, the codehilite CSS class, as defined by the user,\n        # should never be added to inline code, because codehilite does not support inline code.\n        # See https://github.com/Python-Markdown/markdown/issues/1220#issuecomment-1692160297.\n        css_class = \"\" if self._highlighter == \"codehilite\" else kwargs[\"css_class\"]\n        return Markup(f'<code class=\"{css_class} language-{language}\">{result.text}</code>')\n    return Markup(result)\n
"},{"location":"reference/api/#mkdocstrings.Highlighter.highlight(src)","title":"src","text":"(str) \u2013

The code to highlight.

"},{"location":"reference/api/#mkdocstrings.Highlighter.highlight(language)","title":"language","text":"(str | None, default: None ) \u2013

Explicitly tell what language to use for highlighting.

"},{"location":"reference/api/#mkdocstrings.Highlighter.highlight(inline)","title":"inline","text":"(bool, default: False ) \u2013

Whether to highlight as inline.

"},{"location":"reference/api/#mkdocstrings.Highlighter.highlight(dedent)","title":"dedent","text":"(bool, default: True ) \u2013

Whether to dedent the code before highlighting it or not.

"},{"location":"reference/api/#mkdocstrings.Highlighter.highlight(linenums)","title":"linenums","text":"(bool | None, default: None ) \u2013

Whether to add line numbers in the result.

"},{"location":"reference/api/#mkdocstrings.Highlighter.highlight(**kwargs)","title":"**kwargs","text":"(Any, default: {} ) \u2013

Pass on to pymdownx.highlight.Highlight.highlight.

"},{"location":"reference/api/#mkdocstrings.IdPrependingTreeprocessor","title":"IdPrependingTreeprocessor","text":"
IdPrependingTreeprocessor(md: Markdown, id_prefix: str)\n

Bases: Treeprocessor

Prepend the configured prefix to IDs of all HTML elements.

Parameters:

Methods:

  • run \u2013

    Prepend the configured prefix to all IDs in the document.

Attributes:

  • id_prefix (str) \u2013

    The prefix to add to every ID. It is prepended without any separator; specify your own separator if needed.

  • name (str) \u2013

    The name of the treeprocessor.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
def __init__(self, md: Markdown, id_prefix: str):\n    \"\"\"Initialize the object.\n\n    Arguments:\n        md: A `markdown.Markdown` instance.\n        id_prefix: The prefix to add to every ID. It is prepended without any separator.\n    \"\"\"\n    super().__init__(md)\n    self.id_prefix = id_prefix\n
"},{"location":"reference/api/#mkdocstrings.IdPrependingTreeprocessor(md)","title":"md","text":"(Markdown) \u2013

A markdown.Markdown instance.

"},{"location":"reference/api/#mkdocstrings.IdPrependingTreeprocessor(id_prefix)","title":"id_prefix","text":"(str) \u2013

The prefix to add to every ID. It is prepended without any separator.

"},{"location":"reference/api/#mkdocstrings.IdPrependingTreeprocessor.id_prefix","title":"id_prefix instance-attribute","text":"
id_prefix: str = id_prefix\n

The prefix to add to every ID. It is prepended without any separator; specify your own separator if needed.

"},{"location":"reference/api/#mkdocstrings.IdPrependingTreeprocessor.name","title":"name class-attribute instance-attribute","text":"
name: str = 'mkdocstrings_ids'\n

The name of the treeprocessor.

"},{"location":"reference/api/#mkdocstrings.IdPrependingTreeprocessor.run","title":"run","text":"
run(root: Element) -> None\n

Prepend the configured prefix to all IDs in the document.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
def run(self, root: Element) -> None:\n    \"\"\"Prepend the configured prefix to all IDs in the document.\"\"\"\n    if self.id_prefix:\n        self._prefix_ids(root)\n
"},{"location":"reference/api/#mkdocstrings.Inventory","title":"Inventory","text":"
Inventory(\n    items: list[InventoryItem] | None = None,\n    project: str = \"project\",\n    version: str = \"0.0.0\",\n)\n

Bases: dict

Inventory of collected and rendered objects.

Parameters:

Returned by:
  • \u00a0mkdocstrings
    • \u00a0Handlers \u00a0inventory
    • \u00a0Inventory \u00a0parse_sphinx
Used by:
  • \u00a0mkdocstrings \u00a0Handlers \u00a0inventory

Methods:

  • format_sphinx \u2013

    Format this inventory as a Sphinx objects.inv file.

  • parse_sphinx \u2013

    Parse a Sphinx v2 inventory file and return an Inventory from it.

  • register \u2013

    Create and register an item.

Attributes:

  • project \u2013

    The project name.

  • version \u2013

    The project version.

Source code in src/mkdocstrings/_internal/inventory.py
def __init__(self, items: list[InventoryItem] | None = None, project: str = \"project\", version: str = \"0.0.0\"):\n    \"\"\"Initialize the object.\n\n    Arguments:\n        items: A list of items.\n        project: The project name.\n        version: The project version.\n    \"\"\"\n    super().__init__()\n    items = items or []\n    for item in items:\n        self[item.name] = item\n    self.project = project\n    \"\"\"The project name.\"\"\"\n    self.version = version\n    \"\"\"The project version.\"\"\"\n
"},{"location":"reference/api/#mkdocstrings.Inventory(items)","title":"items","text":"(list[InventoryItem] | None, default: None ) \u2013

A list of items.

"},{"location":"reference/api/#mkdocstrings.Inventory(project)","title":"project","text":"(str, default: 'project' ) \u2013

The project name.

"},{"location":"reference/api/#mkdocstrings.Inventory(version)","title":"version","text":"(str, default: '0.0.0' ) \u2013

The project version.

"},{"location":"reference/api/#mkdocstrings.Inventory.project","title":"project instance-attribute","text":"
project = project\n

The project name.

"},{"location":"reference/api/#mkdocstrings.Inventory.version","title":"version instance-attribute","text":"
version = version\n

The project version.

"},{"location":"reference/api/#mkdocstrings.Inventory.format_sphinx","title":"format_sphinx","text":"
format_sphinx() -> bytes\n

Format this inventory as a Sphinx objects.inv file.

Returns:

  • bytes \u2013

    The inventory as bytes.

Source code in src/mkdocstrings/_internal/inventory.py
def format_sphinx(self) -> bytes:\n    \"\"\"Format this inventory as a Sphinx `objects.inv` file.\n\n    Returns:\n        The inventory as bytes.\n    \"\"\"\n    header = (\n        dedent(\n            f\"\"\"\n            # Sphinx inventory version 2\n            # Project: {self.project}\n            # Version: {self.version}\n            # The remainder of this file is compressed using zlib.\n            \"\"\",\n        )\n        .lstrip()\n        .encode(\"utf8\")\n    )\n\n    lines = [\n        item.format_sphinx().encode(\"utf8\")\n        for item in sorted(self.values(), key=lambda item: (item.domain, item.name))\n    ]\n    return header + zlib.compress(b\"\\n\".join(lines) + b\"\\n\", 9)\n
"},{"location":"reference/api/#mkdocstrings.Inventory.parse_sphinx","title":"parse_sphinx classmethod","text":"
parse_sphinx(\n    in_file: BinaryIO,\n    *,\n    domain_filter: Collection[str] = (),\n) -> Inventory\n

Parse a Sphinx v2 inventory file and return an Inventory from it.

Parameters:

Returns:

  • Inventory \u2013

    An inventory containing the collected items.

Source code in src/mkdocstrings/_internal/inventory.py
@classmethod\ndef parse_sphinx(cls, in_file: BinaryIO, *, domain_filter: Collection[str] = ()) -> Inventory:\n    \"\"\"Parse a Sphinx v2 inventory file and return an `Inventory` from it.\n\n    Arguments:\n        in_file: The binary file-like object to read from.\n        domain_filter: A collection of domain values to allow (and filter out all other ones).\n\n    Returns:\n        An inventory containing the collected items.\n    \"\"\"\n    for _ in range(4):\n        in_file.readline()\n    lines = zlib.decompress(in_file.read()).splitlines()\n    items: list[InventoryItem] = [\n        item for line in lines if (item := InventoryItem.parse_sphinx(line.decode(\"utf8\"), return_none=True))\n    ]\n    if domain_filter:\n        items = [item for item in items if item.domain in domain_filter]\n    return cls(items)\n
"},{"location":"reference/api/#mkdocstrings.Inventory.parse_sphinx(in_file)","title":"in_file","text":"(BinaryIO) \u2013

The binary file-like object to read from.

"},{"location":"reference/api/#mkdocstrings.Inventory.parse_sphinx(domain_filter)","title":"domain_filter","text":"(Collection[str], default: () ) \u2013

A collection of domain values to allow (and filter out all other ones).

"},{"location":"reference/api/#mkdocstrings.Inventory.register","title":"register","text":"
register(\n    name: str,\n    domain: str,\n    role: str,\n    uri: str,\n    priority: int = 1,\n    dispname: str | None = None,\n) -> None\n

Create and register an item.

Parameters:

Source code in src/mkdocstrings/_internal/inventory.py
def register(\n    self,\n    name: str,\n    domain: str,\n    role: str,\n    uri: str,\n    priority: int = 1,\n    dispname: str | None = None,\n) -> None:\n    \"\"\"Create and register an item.\n\n    Arguments:\n        name: The item name.\n        domain: The item domain, like 'python' or 'crystal'.\n        role: The item role, like 'class' or 'method'.\n        uri: The item URI.\n        priority: The item priority. Only used internally by mkdocstrings and Sphinx.\n        dispname: The item display name.\n    \"\"\"\n    self[name] = InventoryItem(\n        name=name,\n        domain=domain,\n        role=role,\n        uri=uri,\n        priority=priority,\n        dispname=dispname,\n    )\n
"},{"location":"reference/api/#mkdocstrings.Inventory.register(name)","title":"name","text":"(str) \u2013

The item name.

"},{"location":"reference/api/#mkdocstrings.Inventory.register(domain)","title":"domain","text":"(str) \u2013

The item domain, like 'python' or 'crystal'.

"},{"location":"reference/api/#mkdocstrings.Inventory.register(role)","title":"role","text":"(str) \u2013

The item role, like 'class' or 'method'.

"},{"location":"reference/api/#mkdocstrings.Inventory.register(uri)","title":"uri","text":"(str) \u2013

The item URI.

"},{"location":"reference/api/#mkdocstrings.Inventory.register(priority)","title":"priority","text":"(int, default: 1 ) \u2013

The item priority. Only used internally by mkdocstrings and Sphinx.

"},{"location":"reference/api/#mkdocstrings.Inventory.register(dispname)","title":"dispname","text":"(str | None, default: None ) \u2013

The item display name.

"},{"location":"reference/api/#mkdocstrings.InventoryItem","title":"InventoryItem","text":"
InventoryItem(\n    name: str,\n    domain: str,\n    role: str,\n    uri: str,\n    priority: int = 1,\n    dispname: str | None = None,\n)\n

Inventory item.

Parameters:

Returned by:
  • \u00a0mkdocstrings \u00a0InventoryItem \u00a0parse_sphinx
Used by:
  • \u00a0mkdocstrings \u00a0Inventory

Methods:

  • format_sphinx \u2013

    Format this item as a Sphinx inventory line.

  • parse_sphinx \u2013

    Parse a line from a Sphinx v2 inventory file and return an InventoryItem from it.

Attributes:

  • dispname (str) \u2013

    The item display name.

  • domain (str) \u2013

    The item domain.

  • name (str) \u2013

    The item name.

  • priority (int) \u2013

    The item priority.

  • role (str) \u2013

    The item role.

  • sphinx_item_regex \u2013

    Regex to parse a Sphinx v2 inventory line.

  • uri (str) \u2013

    The item URI.

Source code in src/mkdocstrings/_internal/inventory.py
def __init__(\n    self,\n    name: str,\n    domain: str,\n    role: str,\n    uri: str,\n    priority: int = 1,\n    dispname: str | None = None,\n):\n    \"\"\"Initialize the object.\n\n    Arguments:\n        name: The item name.\n        domain: The item domain, like 'python' or 'crystal'.\n        role: The item role, like 'class' or 'method'.\n        uri: The item URI.\n        priority: The item priority. Only used internally by mkdocstrings and Sphinx.\n        dispname: The item display name.\n    \"\"\"\n    self.name: str = name\n    \"\"\"The item name.\"\"\"\n    self.domain: str = domain\n    \"\"\"The item domain.\"\"\"\n    self.role: str = role\n    \"\"\"The item role.\"\"\"\n    self.uri: str = uri\n    \"\"\"The item URI.\"\"\"\n    self.priority: int = priority\n    \"\"\"The item priority.\"\"\"\n    self.dispname: str = dispname or name\n    \"\"\"The item display name.\"\"\"\n
"},{"location":"reference/api/#mkdocstrings.InventoryItem(name)","title":"name","text":"(str) \u2013

The item name.

"},{"location":"reference/api/#mkdocstrings.InventoryItem(domain)","title":"domain","text":"(str) \u2013

The item domain, like 'python' or 'crystal'.

"},{"location":"reference/api/#mkdocstrings.InventoryItem(role)","title":"role","text":"(str) \u2013

The item role, like 'class' or 'method'.

"},{"location":"reference/api/#mkdocstrings.InventoryItem(uri)","title":"uri","text":"(str) \u2013

The item URI.

"},{"location":"reference/api/#mkdocstrings.InventoryItem(priority)","title":"priority","text":"(int, default: 1 ) \u2013

The item priority. Only used internally by mkdocstrings and Sphinx.

"},{"location":"reference/api/#mkdocstrings.InventoryItem(dispname)","title":"dispname","text":"(str | None, default: None ) \u2013

The item display name.

"},{"location":"reference/api/#mkdocstrings.InventoryItem.dispname","title":"dispname instance-attribute","text":"
dispname: str = dispname or name\n

The item display name.

"},{"location":"reference/api/#mkdocstrings.InventoryItem.domain","title":"domain instance-attribute","text":"
domain: str = domain\n

The item domain.

"},{"location":"reference/api/#mkdocstrings.InventoryItem.name","title":"name instance-attribute","text":"
name: str = name\n

The item name.

"},{"location":"reference/api/#mkdocstrings.InventoryItem.priority","title":"priority instance-attribute","text":"
priority: int = priority\n

The item priority.

"},{"location":"reference/api/#mkdocstrings.InventoryItem.role","title":"role instance-attribute","text":"
role: str = role\n

The item role.

"},{"location":"reference/api/#mkdocstrings.InventoryItem.sphinx_item_regex","title":"sphinx_item_regex class-attribute instance-attribute","text":"
sphinx_item_regex = compile(\n    \"^(.+?)\\\\s+(\\\\S+):(\\\\S+)\\\\s+(-?\\\\d+)\\\\s+(\\\\S+)\\\\s*(.*)$\"\n)\n

Regex to parse a Sphinx v2 inventory line.

"},{"location":"reference/api/#mkdocstrings.InventoryItem.uri","title":"uri instance-attribute","text":"
uri: str = uri\n

The item URI.

"},{"location":"reference/api/#mkdocstrings.InventoryItem.format_sphinx","title":"format_sphinx","text":"
format_sphinx() -> str\n

Format this item as a Sphinx inventory line.

Returns:

  • str \u2013

    A line formatted for an objects.inv file.

Source code in src/mkdocstrings/_internal/inventory.py
def format_sphinx(self) -> str:\n    \"\"\"Format this item as a Sphinx inventory line.\n\n    Returns:\n        A line formatted for an `objects.inv` file.\n    \"\"\"\n    dispname = self.dispname\n    if dispname == self.name:\n        dispname = \"-\"\n    uri = self.uri\n    if uri.endswith(self.name):\n        uri = uri[: -len(self.name)] + \"$\"\n    return f\"{self.name} {self.domain}:{self.role} {self.priority} {uri} {dispname}\"\n
"},{"location":"reference/api/#mkdocstrings.InventoryItem.parse_sphinx","title":"parse_sphinx classmethod","text":"
parse_sphinx(\n    line: str, *, return_none: Literal[False]\n) -> InventoryItem\n
parse_sphinx(\n    line: str, *, return_none: Literal[True]\n) -> InventoryItem | None\n
parse_sphinx(\n    line: str, *, return_none: bool = False\n) -> InventoryItem | None\n

Parse a line from a Sphinx v2 inventory file and return an InventoryItem from it.

Source code in src/mkdocstrings/_internal/inventory.py
@classmethod\ndef parse_sphinx(cls, line: str, *, return_none: bool = False) -> InventoryItem | None:\n    \"\"\"Parse a line from a Sphinx v2 inventory file and return an `InventoryItem` from it.\"\"\"\n    match = cls.sphinx_item_regex.search(line)\n    if not match:\n        if return_none:\n            return None\n        raise ValueError(line)\n    name, domain, role, priority, uri, dispname = match.groups()\n    if uri.endswith(\"$\"):\n        uri = uri[:-1] + name\n    if dispname == \"-\":\n        dispname = name\n    return cls(name, domain, role, uri, int(priority), dispname)\n
"},{"location":"reference/api/#mkdocstrings.LoggerAdapter","title":"LoggerAdapter","text":"
LoggerAdapter(prefix: str, logger: Logger)\n

Bases: LoggerAdapter

A logger adapter to prefix messages.

This adapter also adds an additional parameter to logging methods called once: if True, the message will only be logged once.

Examples:

In Python code:

>>> logger = get_logger(\"myplugin\")\n>>> logger.debug(\"This is a debug message.\")\n>>> logger.info(\"This is an info message.\", once=True)\n

In Jinja templates (logger available in context as log):

{{ log.debug(\"This is a debug message.\") }}\n{{ log.info(\"This is an info message.\", once=True) }}\n

Parameters:

Returned by:
  • \u00a0mkdocstrings \u00a0get_logger
Used by:
  • \u00a0mkdocstrings \u00a0TemplateLogger

Methods:

  • log \u2013

    Log a message.

  • process \u2013

    Process the message.

Attributes:

  • prefix \u2013

    The prefix to insert in front of every message.

Source code in src/mkdocstrings/_internal/loggers.py
def __init__(self, prefix: str, logger: logging.Logger):\n    \"\"\"Initialize the object.\n\n    Arguments:\n        prefix: The string to insert in front of every message.\n        logger: The logger instance.\n    \"\"\"\n    super().__init__(logger, {})\n    self.prefix = prefix\n    \"\"\"The prefix to insert in front of every message.\"\"\"\n    self._logged: set[tuple[LoggerAdapter, str]] = set()\n
"},{"location":"reference/api/#mkdocstrings.LoggerAdapter(prefix)","title":"prefix","text":"(str) \u2013

The string to insert in front of every message.

"},{"location":"reference/api/#mkdocstrings.LoggerAdapter(logger)","title":"logger","text":"(Logger) \u2013

The logger instance.

"},{"location":"reference/api/#mkdocstrings.LoggerAdapter.prefix","title":"prefix instance-attribute","text":"
prefix = prefix\n

The prefix to insert in front of every message.

"},{"location":"reference/api/#mkdocstrings.LoggerAdapter.log","title":"log","text":"
log(\n    level: int, msg: object, *args: object, **kwargs: object\n) -> None\n

Log a message.

Parameters:

Source code in src/mkdocstrings/_internal/loggers.py
def log(self, level: int, msg: object, *args: object, **kwargs: object) -> None:\n    \"\"\"Log a message.\n\n    Arguments:\n        level: The logging level.\n        msg: The message.\n        *args: Additional arguments passed to parent method.\n        **kwargs: Additional keyword arguments passed to parent method.\n    \"\"\"\n    if kwargs.pop(\"once\", False):\n        if (key := (self, str(msg))) in self._logged:\n            return\n        self._logged.add(key)\n    super().log(level, msg, *args, **kwargs)  # type: ignore[arg-type]\n
"},{"location":"reference/api/#mkdocstrings.LoggerAdapter.log(level)","title":"level","text":"(int) \u2013

The logging level.

"},{"location":"reference/api/#mkdocstrings.LoggerAdapter.log(msg)","title":"msg","text":"(object) \u2013

The message.

"},{"location":"reference/api/#mkdocstrings.LoggerAdapter.log(*args)","title":"*args","text":"(object, default: () ) \u2013

Additional arguments passed to parent method.

"},{"location":"reference/api/#mkdocstrings.LoggerAdapter.log(**kwargs)","title":"**kwargs","text":"(object, default: {} ) \u2013

Additional keyword arguments passed to parent method.

"},{"location":"reference/api/#mkdocstrings.LoggerAdapter.process","title":"process","text":"
process(\n    msg: str, kwargs: MutableMapping[str, Any]\n) -> tuple[str, Any]\n

Process the message.

Parameters:

Returns:

  • tuple[str, Any] \u2013

    The processed message.

Source code in src/mkdocstrings/_internal/loggers.py
def process(self, msg: str, kwargs: MutableMapping[str, Any]) -> tuple[str, Any]:\n    \"\"\"Process the message.\n\n    Arguments:\n        msg: The message:\n        kwargs: Remaining arguments.\n\n    Returns:\n        The processed message.\n    \"\"\"\n    return f\"{self.prefix}: {msg}\", kwargs\n
"},{"location":"reference/api/#mkdocstrings.LoggerAdapter.process(msg)","title":"msg","text":"(str) \u2013

The message:

"},{"location":"reference/api/#mkdocstrings.LoggerAdapter.process(kwargs)","title":"kwargs","text":"(MutableMapping[str, Any]) \u2013

Remaining arguments.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsExtension","title":"MkdocstringsExtension","text":"
MkdocstringsExtension(\n    handlers: Handlers,\n    autorefs: AutorefsPlugin,\n    **kwargs: Any,\n)\n

Bases: Extension

Our Markdown extension.

It cannot work outside of mkdocstrings.

Parameters:

Referenced by:
  • \u00a0mkdocstrings \u00a0MkdocstringsPlugin \u00a0on_config
Returned by:
  • \u00a0mkdocstrings \u00a0makeExtension

Methods:

  • extendMarkdown \u2013

    Register the extension.

Source code in src/mkdocstrings/_internal/extension.py
def __init__(self, handlers: Handlers, autorefs: AutorefsPlugin, **kwargs: Any) -> None:\n    \"\"\"Initialize the object.\n\n    Arguments:\n        handlers: The handlers container.\n        autorefs: The autorefs plugin instance.\n        **kwargs: Keyword arguments used by `markdown.extensions.Extension`.\n    \"\"\"\n    super().__init__(**kwargs)\n    self._handlers = handlers\n    self._autorefs = autorefs\n
"},{"location":"reference/api/#mkdocstrings.MkdocstringsExtension(handlers)","title":"handlers","text":"(Handlers) \u2013

The handlers container.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsExtension(autorefs)","title":"autorefs","text":"(AutorefsPlugin) \u2013

The autorefs plugin instance.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsExtension(**kwargs)","title":"**kwargs","text":"(Any, default: {} ) \u2013

Keyword arguments used by markdown.extensions.Extension.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsExtension.extendMarkdown","title":"extendMarkdown","text":"
extendMarkdown(md: Markdown) -> None\n

Register the extension.

Add an instance of our AutoDocProcessor to the Markdown parser.

Parameters:

Source code in src/mkdocstrings/_internal/extension.py
def extendMarkdown(self, md: Markdown) -> None:  # noqa: N802 (casing: parent method's name)\n    \"\"\"Register the extension.\n\n    Add an instance of our [`AutoDocProcessor`][mkdocstrings.AutoDocProcessor] to the Markdown parser.\n\n    Arguments:\n        md: A `markdown.Markdown` instance.\n    \"\"\"\n    md.parser.blockprocessors.register(\n        AutoDocProcessor(md, handlers=self._handlers, autorefs=self._autorefs),\n        \"mkdocstrings\",\n        priority=75,  # Right before markdown.blockprocessors.HashHeaderProcessor\n    )\n    md.treeprocessors.register(\n        _HeadingsPostProcessor(md),\n        \"mkdocstrings_post_headings\",\n        priority=4,  # Right after 'toc'.\n    )\n    md.treeprocessors.register(\n        _TocLabelsTreeProcessor(md),\n        \"mkdocstrings_post_toc_labels\",\n        priority=4,  # Right after 'toc'.\n    )\n
"},{"location":"reference/api/#mkdocstrings.MkdocstringsExtension.extendMarkdown(md)","title":"md","text":"(Markdown) \u2013

A markdown.Markdown instance.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsInnerExtension","title":"MkdocstringsInnerExtension","text":"
MkdocstringsInnerExtension(headings: list[Element])\n

Bases: Extension

Extension that should always be added to Markdown sub-documents that handlers request (and only them).

Parameters:

Methods:

  • extendMarkdown \u2013

    Register the extension.

Attributes:

  • headings \u2013

    The list that will be populated with all HTML heading elements encountered in the document.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
def __init__(self, headings: list[Element]):\n    \"\"\"Initialize the object.\n\n    Arguments:\n        headings: A list that will be populated with all HTML heading elements encountered in the document.\n    \"\"\"\n    super().__init__()\n    self.headings = headings\n    \"\"\"The list that will be populated with all HTML heading elements encountered in the document.\"\"\"\n
"},{"location":"reference/api/#mkdocstrings.MkdocstringsInnerExtension(headings)","title":"headings","text":"(list[Element]) \u2013

A list that will be populated with all HTML heading elements encountered in the document.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsInnerExtension.headings","title":"headings instance-attribute","text":"
headings = headings\n

The list that will be populated with all HTML heading elements encountered in the document.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsInnerExtension.extendMarkdown","title":"extendMarkdown","text":"
extendMarkdown(md: Markdown) -> None\n

Register the extension.

Parameters:

Source code in src/mkdocstrings/_internal/handlers/rendering.py
def extendMarkdown(self, md: Markdown) -> None:  # noqa: N802 (casing: parent method's name)\n    \"\"\"Register the extension.\n\n    Arguments:\n        md: A `markdown.Markdown` instance.\n    \"\"\"\n    md.registerExtension(self)\n    md.treeprocessors.register(\n        HeadingShiftingTreeprocessor(md, 0),\n        HeadingShiftingTreeprocessor.name,\n        priority=12,\n    )\n    md.treeprocessors.register(\n        IdPrependingTreeprocessor(md, \"\"),\n        IdPrependingTreeprocessor.name,\n        priority=4,  # Right after 'toc' (needed because that extension adds ids to headers).\n    )\n    md.treeprocessors.register(\n        _HeadingReportingTreeprocessor(md, self.headings),\n        _HeadingReportingTreeprocessor.name,\n        priority=1,  # Close to the end.\n    )\n    md.treeprocessors.register(\n        ParagraphStrippingTreeprocessor(md),\n        ParagraphStrippingTreeprocessor.name,\n        priority=0.99,  # Close to the end.\n    )\n
"},{"location":"reference/api/#mkdocstrings.MkdocstringsInnerExtension.extendMarkdown(md)","title":"md","text":"(Markdown) \u2013

A markdown.Markdown instance.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsPlugin","title":"MkdocstringsPlugin","text":"
MkdocstringsPlugin()\n

Bases: BasePlugin[PluginConfig]

An mkdocs plugin.

This plugin defines the following event hooks:

  • on_config
  • on_env
  • on_post_build

Check the Developing Plugins page of mkdocs for more information about its plugin system.

Referenced by:
  • \u00a0mkdocstrings \u00a0Handlers

Methods:

  • get_handler \u2013

    Get a handler by its name. See mkdocstrings.Handlers.get_handler.

  • on_config \u2013

    Instantiate our Markdown extension.

  • on_post_build \u2013

    Teardown the handlers.

Attributes:

  • css_filename (str) \u2013

    The path of the CSS file to write in the site directory.

  • handlers (Handlers) \u2013

    Get the instance of mkdocstrings.Handlers for this plugin/build.

  • inventory_enabled (bool) \u2013

    Tell if the inventory is enabled or not.

  • on_env \u2013

    Extra actions that need to happen after all Markdown-to-HTML page rendering.

  • plugin_enabled (bool) \u2013

    Tell if the plugin is enabled or not.

Source code in src/mkdocstrings/_internal/plugin.py
def __init__(self) -> None:\n    \"\"\"Initialize the object.\"\"\"\n    super().__init__()\n    self._handlers: Handlers | None = None\n
"},{"location":"reference/api/#mkdocstrings.MkdocstringsPlugin.css_filename","title":"css_filename class-attribute instance-attribute","text":"
css_filename: str = 'assets/_mkdocstrings.css'\n

The path of the CSS file to write in the site directory.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsPlugin.handlers","title":"handlers property","text":"
handlers: Handlers\n

Get the instance of mkdocstrings.Handlers for this plugin/build.

Raises:

  • RuntimeError \u2013

    If the plugin hasn't been initialized with a config.

Returns:

  • Handlers \u2013

    An instance of mkdocstrings.Handlers (the same throughout the build).

"},{"location":"reference/api/#mkdocstrings.MkdocstringsPlugin.inventory_enabled","title":"inventory_enabled property","text":"
inventory_enabled: bool\n

Tell if the inventory is enabled or not.

Returns:

  • bool \u2013

    Whether the inventory is enabled.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsPlugin.on_env","title":"on_env class-attribute instance-attribute","text":"
on_env = CombinedEvent(\n    _on_env_load_inventories,\n    _on_env_add_css,\n    _on_env_write_inventory,\n    _on_env_apply_backlinks,\n)\n

Extra actions that need to happen after all Markdown-to-HTML page rendering.

Hook for the on_env event.

  • Gather results from background inventory download tasks.
  • Write mkdocstrings' extra files (CSS, inventory) into the site directory.
  • Apply backlinks to the HTML output of each page.
"},{"location":"reference/api/#mkdocstrings.MkdocstringsPlugin.plugin_enabled","title":"plugin_enabled property","text":"
plugin_enabled: bool\n

Tell if the plugin is enabled or not.

Returns:

  • bool \u2013

    Whether the plugin is enabled.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsPlugin.get_handler","title":"get_handler","text":"
get_handler(handler_name: str) -> BaseHandler\n

Get a handler by its name. See mkdocstrings.Handlers.get_handler.

Parameters:

Returns:

  • BaseHandler \u2013

    An instance of a subclass of BaseHandler.

Referenced by:
  • \u00a0mkdocstrings \u00a0Handlers
Source code in src/mkdocstrings/_internal/plugin.py
def get_handler(self, handler_name: str) -> BaseHandler:\n    \"\"\"Get a handler by its name. See [mkdocstrings.Handlers.get_handler][].\n\n    Arguments:\n        handler_name: The name of the handler.\n\n    Returns:\n        An instance of a subclass of [`BaseHandler`][mkdocstrings.BaseHandler].\n    \"\"\"\n    return self.handlers.get_handler(handler_name)\n
"},{"location":"reference/api/#mkdocstrings.MkdocstringsPlugin.get_handler(handler_name)","title":"handler_name","text":"(str) \u2013

The name of the handler.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsPlugin.on_config","title":"on_config","text":"
on_config(config: MkDocsConfig) -> MkDocsConfig | None\n

Instantiate our Markdown extension.

Hook for the on_config event. In this hook, we instantiate our MkdocstringsExtension and add it to the list of Markdown extensions used by mkdocs.

We pass this plugin's configuration dictionary to the extension when instantiating it (it will need it later when processing markdown to get handlers and their global configurations).

Parameters:

Returns:

  • MkDocsConfig | None \u2013

    The modified config.

Source code in src/mkdocstrings/_internal/plugin.py
def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None:\n    \"\"\"Instantiate our Markdown extension.\n\n    Hook for the [`on_config` event](https://www.mkdocs.org/user-guide/plugins/#on_config).\n    In this hook, we instantiate our [`MkdocstringsExtension`][mkdocstrings.MkdocstringsExtension]\n    and add it to the list of Markdown extensions used by `mkdocs`.\n\n    We pass this plugin's configuration dictionary to the extension when instantiating it (it will need it\n    later when processing markdown to get handlers and their global configurations).\n\n    Arguments:\n        config: The MkDocs config object.\n\n    Returns:\n        The modified config.\n    \"\"\"\n    if not self.plugin_enabled:\n        _logger.debug(\"Plugin is not enabled. Skipping.\")\n        return config\n    _logger.debug(\"Adding extension to the list\")\n\n    locale = self.config.locale or config.theme.get(\"language\") or config.theme.get(\"locale\") or \"en\"\n    locale = str(locale).replace(\"_\", \"-\")\n\n    handlers = Handlers(\n        default=self.config.default_handler,\n        handlers_config=self.config.handlers,\n        theme=config.theme.name or os.path.dirname(config.theme.dirs[0]),\n        custom_templates=self.config.custom_templates,\n        mdx=config.markdown_extensions,\n        mdx_config=config.mdx_configs,\n        inventory_project=config.site_name,\n        inventory_version=\"0.0.0\",  # TODO: Find a way to get actual version.\n        locale=locale,\n        tool_config=config,\n    )\n\n    handlers._download_inventories()\n\n    AutorefsPlugin.record_backlinks = True\n    autorefs: AutorefsPlugin\n    try:\n        # If autorefs plugin is explicitly enabled, just use it.\n        autorefs = config.plugins[\"autorefs\"]  # type: ignore[assignment]\n        _logger.debug(\"Picked up existing autorefs instance %r\", autorefs)\n    except KeyError:\n        # Otherwise, add a limited instance of it that acts only on what's added through `register_anchor`.\n        autorefs = AutorefsPlugin()\n        autorefs.config = AutorefsConfig()\n        autorefs.scan_toc = False\n        config.plugins[\"autorefs\"] = autorefs\n        _logger.debug(\"Added a subdued autorefs instance %r\", autorefs)\n\n    mkdocstrings_extension = MkdocstringsExtension(handlers, autorefs)\n    config.markdown_extensions.append(mkdocstrings_extension)  # type: ignore[arg-type]\n\n    config.extra_css.insert(0, self.css_filename)  # So that it has lower priority than user files.\n\n    self._autorefs = autorefs\n    self._handlers = handlers\n    return config\n
"},{"location":"reference/api/#mkdocstrings.MkdocstringsPlugin.on_config(config)","title":"config","text":"(MkDocsConfig) \u2013

The MkDocs config object.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsPlugin.on_post_build","title":"on_post_build","text":"
on_post_build(config: MkDocsConfig, **kwargs: Any) -> None\n

Teardown the handlers.

Hook for the on_post_build event. This hook is used to teardown all the handlers that were instantiated and cached during documentation buildup.

For example, a handler could open a subprocess in the background and keep it open to feed it \"autodoc\" instructions and get back JSON data. If so, it should then close the subprocess at some point: the proper place to do this is in the handler's teardown method, which is indirectly called by this hook.

Parameters:

Source code in src/mkdocstrings/_internal/plugin.py
def on_post_build(\n    self,\n    config: MkDocsConfig,  # noqa: ARG002\n    **kwargs: Any,  # noqa: ARG002\n) -> None:\n    \"\"\"Teardown the handlers.\n\n    Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build).\n    This hook is used to teardown all the handlers that were instantiated and cached during documentation buildup.\n\n    For example, a handler could open a subprocess in the background and keep it open\n    to feed it \"autodoc\" instructions and get back JSON data. If so, it should then close the subprocess at some point:\n    the proper place to do this is in the handler's `teardown` method, which is indirectly called by this hook.\n\n    Arguments:\n        config: The MkDocs config object.\n        **kwargs: Additional arguments passed by MkDocs.\n    \"\"\"\n    if not self.plugin_enabled:\n        return\n\n    if self._handlers:\n        _logger.debug(\"Tearing handlers down\")\n        self.handlers.teardown()\n
"},{"location":"reference/api/#mkdocstrings.MkdocstringsPlugin.on_post_build(config)","title":"config","text":"(MkDocsConfig) \u2013

The MkDocs config object.

"},{"location":"reference/api/#mkdocstrings.MkdocstringsPlugin.on_post_build(**kwargs)","title":"**kwargs","text":"(Any, default: {} ) \u2013

Additional arguments passed by MkDocs.

"},{"location":"reference/api/#mkdocstrings.ParagraphStrippingTreeprocessor","title":"ParagraphStrippingTreeprocessor","text":"

Bases: Treeprocessor

Unwraps the <p> element around the whole output.

Methods:

  • run \u2013

    Unwrap the root element if it's a single <p> element.

Attributes:

  • name (str) \u2013

    The name of the treeprocessor.

  • strip (bool) \u2013

    Whether to strip <p> elements or not.

"},{"location":"reference/api/#mkdocstrings.ParagraphStrippingTreeprocessor.name","title":"name class-attribute instance-attribute","text":"
name: str = 'mkdocstrings_strip_paragraph'\n

The name of the treeprocessor.

"},{"location":"reference/api/#mkdocstrings.ParagraphStrippingTreeprocessor.strip","title":"strip class-attribute instance-attribute","text":"
strip: bool = False\n

Whether to strip <p> elements or not.

"},{"location":"reference/api/#mkdocstrings.ParagraphStrippingTreeprocessor.run","title":"run","text":"
run(root: Element) -> Element | None\n

Unwrap the root element if it's a single <p> element.

Source code in src/mkdocstrings/_internal/handlers/rendering.py
def run(self, root: Element) -> Element | None:\n    \"\"\"Unwrap the root element if it's a single `<p>` element.\"\"\"\n    if self.strip and len(root) == 1 and root[0].tag == \"p\":\n        # Turn the single `<p>` element into the root element and inherit its tag name (it's significant!)\n        root[0].tag = root.tag\n        return root[0]\n    return None\n
"},{"location":"reference/api/#mkdocstrings.PluginConfig","title":"PluginConfig","text":"

Bases: Config

The configuration options of mkdocstrings, written in mkdocs.yml.

Subclassed by:
  • \u00a0mkdocstrings \u00a0MkdocstringsPlugin

Attributes:

  • custom_templates \u2013

    Location of custom templates to use when rendering API objects.

  • default_handler \u2013

    The default handler to use. The value is the name of the handler module. Default is \"python\".

  • enable_inventory \u2013

    Whether to enable object inventory creation.

  • enabled \u2013

    Whether to enable the plugin. Default is true. If false, mkdocstrings will not collect or render anything.

  • handlers \u2013

    Global configuration of handlers.

  • locale \u2013

    The locale to use for translations.

"},{"location":"reference/api/#mkdocstrings.PluginConfig.custom_templates","title":"custom_templates class-attribute instance-attribute","text":"
custom_templates = Optional(Dir(exists=True))\n

Location of custom templates to use when rendering API objects.

Value should be the path of a directory relative to the MkDocs configuration file.

"},{"location":"reference/api/#mkdocstrings.PluginConfig.default_handler","title":"default_handler class-attribute instance-attribute","text":"
default_handler = Type(str, default='python')\n

The default handler to use. The value is the name of the handler module. Default is \"python\".

"},{"location":"reference/api/#mkdocstrings.PluginConfig.enable_inventory","title":"enable_inventory class-attribute instance-attribute","text":"
enable_inventory = Optional(Type(bool))\n

Whether to enable object inventory creation.

"},{"location":"reference/api/#mkdocstrings.PluginConfig.enabled","title":"enabled class-attribute instance-attribute","text":"
enabled = Type(bool, default=True)\n

Whether to enable the plugin. Default is true. If false, mkdocstrings will not collect or render anything.

"},{"location":"reference/api/#mkdocstrings.PluginConfig.handlers","title":"handlers class-attribute instance-attribute","text":"
handlers = Type(dict, default={})\n

Global configuration of handlers.

You can set global configuration per handler, applied everywhere, but overridable in each \"autodoc\" instruction. Example:

plugins:\n  - mkdocstrings:\n      handlers:\n        python:\n          options:\n            option1: true\n            option2: \"value\"\n        rust:\n          options:\n            option9: 2\n
"},{"location":"reference/api/#mkdocstrings.PluginConfig.locale","title":"locale class-attribute instance-attribute","text":"
locale = Optional(Type(str))\n

The locale to use for translations.

"},{"location":"reference/api/#mkdocstrings.TemplateLogger","title":"TemplateLogger","text":"
TemplateLogger(logger: LoggerAdapter)\n

A wrapper class to allow logging in templates.

The logging methods provided by this class all accept two parameters:

  • msg: The message to log.
  • once: If True, the message will only be logged once.

Methods:

  • debug \u2013

    Function to log a DEBUG message.

  • info \u2013

    Function to log an INFO message.

  • warning \u2013

    Function to log a WARNING message.

  • error \u2013

    Function to log an ERROR message.

  • critical \u2013

    Function to log a CRITICAL message.

Parameters:

Returned by:
  • \u00a0mkdocstrings \u00a0get_template_logger

Attributes:

  • critical \u2013

    Log a CRITICAL message.

  • debug \u2013

    Log a DEBUG message.

  • error \u2013

    Log an ERROR message.

  • info \u2013

    Log an INFO message.

  • warning \u2013

    Log a WARNING message.

Source code in src/mkdocstrings/_internal/loggers.py
def __init__(self, logger: LoggerAdapter):\n    \"\"\"Initialize the object.\n\n    Arguments:\n        logger: A logger adapter.\n    \"\"\"\n    self.debug = get_template_logger_function(logger.debug)\n    \"\"\"Log a DEBUG message.\"\"\"\n    self.info = get_template_logger_function(logger.info)\n    \"\"\"Log an INFO message.\"\"\"\n    self.warning = get_template_logger_function(logger.warning)\n    \"\"\"Log a WARNING message.\"\"\"\n    self.error = get_template_logger_function(logger.error)\n    \"\"\"Log an ERROR message.\"\"\"\n    self.critical = get_template_logger_function(logger.critical)\n    \"\"\"Log a CRITICAL message.\"\"\"\n
"},{"location":"reference/api/#mkdocstrings.TemplateLogger(logger)","title":"logger","text":"(LoggerAdapter) \u2013

A logger adapter.

"},{"location":"reference/api/#mkdocstrings.TemplateLogger.critical","title":"critical instance-attribute","text":"
critical = get_template_logger_function(critical)\n

Log a CRITICAL message.

"},{"location":"reference/api/#mkdocstrings.TemplateLogger.debug","title":"debug instance-attribute","text":"
debug = get_template_logger_function(debug)\n

Log a DEBUG message.

"},{"location":"reference/api/#mkdocstrings.TemplateLogger.error","title":"error instance-attribute","text":"
error = get_template_logger_function(error)\n

Log an ERROR message.

"},{"location":"reference/api/#mkdocstrings.TemplateLogger.info","title":"info instance-attribute","text":"
info = get_template_logger_function(info)\n

Log an INFO message.

"},{"location":"reference/api/#mkdocstrings.TemplateLogger.warning","title":"warning instance-attribute","text":"
warning = get_template_logger_function(warning)\n

Log a WARNING message.

"},{"location":"reference/api/#mkdocstrings.ThemeNotSupported","title":"ThemeNotSupported","text":"

Bases: Exception

An exception raised to tell a theme is not supported.

"},{"location":"reference/api/#mkdocstrings.do_any","title":"do_any","text":"
do_any(seq: Sequence, attribute: str | None = None) -> bool\n

Check if at least one of the item in the sequence evaluates to true.

The any builtin as a filter for Jinja templates.

Parameters:

Returns:

  • bool \u2013

    A boolean telling if any object of the iterable evaluated to True.

Source code in src/mkdocstrings/_internal/handlers/base.py
def do_any(seq: Sequence, attribute: str | None = None) -> bool:\n    \"\"\"Check if at least one of the item in the sequence evaluates to true.\n\n    The `any` builtin as a filter for Jinja templates.\n\n    Arguments:\n        seq: An iterable object.\n        attribute: The attribute name to use on each object of the iterable.\n\n    Returns:\n        A boolean telling if any object of the iterable evaluated to True.\n    \"\"\"\n    if attribute is None:\n        return any(seq)\n    return any(_[attribute] for _ in seq)\n
"},{"location":"reference/api/#mkdocstrings.do_any(seq)","title":"seq","text":"(Sequence) \u2013

An iterable object.

"},{"location":"reference/api/#mkdocstrings.do_any(attribute)","title":"attribute","text":"(str | None, default: None ) \u2013

The attribute name to use on each object of the iterable.

"},{"location":"reference/api/#mkdocstrings.get_logger","title":"get_logger","text":"
get_logger(name: str) -> LoggerAdapter\n

Return a pre-configured logger.

Parameters:

Returns:

  • LoggerAdapter \u2013

    A logger configured to work well in MkDocs.

Source code in src/mkdocstrings/_internal/loggers.py
def get_logger(name: str) -> LoggerAdapter:\n    \"\"\"Return a pre-configured logger.\n\n    Arguments:\n        name: The name to use with `logging.getLogger`.\n\n    Returns:\n        A logger configured to work well in MkDocs.\n    \"\"\"\n    logger = logging.getLogger(f\"mkdocs.plugins.{name}\")\n    return LoggerAdapter(name.split(\".\", 1)[0], logger)\n
"},{"location":"reference/api/#mkdocstrings.get_logger(name)","title":"name","text":"(str) \u2013

The name to use with logging.getLogger.

"},{"location":"reference/api/#mkdocstrings.get_template_logger","title":"get_template_logger","text":"
get_template_logger(\n    handler_name: str | None = None,\n) -> TemplateLogger\n

Return a logger usable in templates.

Parameters:

Returns:

  • TemplateLogger \u2013

    A template logger.

Source code in src/mkdocstrings/_internal/loggers.py
def get_template_logger(handler_name: str | None = None) -> TemplateLogger:\n    \"\"\"Return a logger usable in templates.\n\n    Parameters:\n        handler_name: The name of the handler.\n\n    Returns:\n        A template logger.\n    \"\"\"\n    handler_name = handler_name or \"base\"\n    return TemplateLogger(get_logger(f\"mkdocstrings_handlers.{handler_name}.templates\"))\n
"},{"location":"reference/api/#mkdocstrings.get_template_logger(handler_name)","title":"handler_name","text":"(str | None, default: None ) \u2013

The name of the handler.

"},{"location":"reference/api/#mkdocstrings.get_template_logger_function","title":"get_template_logger_function","text":"
get_template_logger_function(\n    logger_func: Callable,\n) -> Callable\n

Create a wrapper function that automatically receives the Jinja template context.

Parameters:

Returns:

  • Callable \u2013

    A function.

Used by:
  • \u00a0mkdocstrings \u00a0TemplateLogger
    • \u00a0critical
    • \u00a0debug
    • \u00a0error
    • \u00a0info
    • \u00a0warning
Source code in src/mkdocstrings/_internal/loggers.py
def get_template_logger_function(logger_func: Callable) -> Callable:\n    \"\"\"Create a wrapper function that automatically receives the Jinja template context.\n\n    Arguments:\n        logger_func: The logger function to use within the wrapper.\n\n    Returns:\n        A function.\n    \"\"\"\n\n    @pass_context\n    def wrapper(context: Context, msg: str | None = None, *args: Any, **kwargs: Any) -> str:\n        \"\"\"Log a message.\n\n        Arguments:\n            context: The template context, automatically provided by Jinja.\n            msg: The message to log.\n            **kwargs: Additional arguments passed to the logger function.\n\n        Returns:\n            An empty string.\n        \"\"\"\n        logger_func(f\"%s: {msg or 'Rendering'}\", _Lazy(get_template_path, context), *args, **kwargs)\n        return \"\"\n\n    return wrapper\n
"},{"location":"reference/api/#mkdocstrings.get_template_logger_function(logger_func)","title":"logger_func","text":"(Callable) \u2013

The logger function to use within the wrapper.

"},{"location":"reference/api/#mkdocstrings.get_template_path","title":"get_template_path","text":"
get_template_path(context: Context) -> str\n

Return the path to the template currently using the given context.

Parameters:

Returns:

  • str \u2013

    The relative path to the template.

Source code in src/mkdocstrings/_internal/loggers.py
def get_template_path(context: Context) -> str:\n    \"\"\"Return the path to the template currently using the given context.\n\n    Arguments:\n        context: The template context.\n\n    Returns:\n        The relative path to the template.\n    \"\"\"\n    context_name: str = str(context.name)\n    filename = context.environment.get_template(context_name).filename\n    if filename:\n        for template_dir in TEMPLATES_DIRS:\n            with suppress(ValueError):\n                return str(Path(filename).relative_to(template_dir))\n        with suppress(ValueError):\n            return str(Path(filename).relative_to(Path.cwd()))\n        return filename\n    return context_name\n
"},{"location":"reference/api/#mkdocstrings.get_template_path(context)","title":"context","text":"(Context) \u2013

The template context.

"},{"location":"reference/api/#mkdocstrings.makeExtension","title":"makeExtension","text":"
makeExtension(\n    *,\n    default_handler: str | None = None,\n    inventory_project: str | None = None,\n    inventory_version: str | None = None,\n    handlers: dict[str, dict] | None = None,\n    custom_templates: str | None = None,\n    markdown_extensions: list[str | dict] | None = None,\n    locale: str | None = None,\n    config_file_path: str | None = None,\n) -> MkdocstringsExtension\n

Create the extension instance.

We only support this function being used by Zensical. Consider this function private API.

Source code in src/mkdocstrings/_internal/extension.py
def makeExtension(  # noqa: N802\n    *,\n    default_handler: str | None = None,\n    inventory_project: str | None = None,\n    inventory_version: str | None = None,\n    handlers: dict[str, dict] | None = None,\n    custom_templates: str | None = None,\n    markdown_extensions: list[str | dict] | None = None,\n    locale: str | None = None,\n    config_file_path: str | None = None,\n) -> MkdocstringsExtension:\n    \"\"\"Create the extension instance.\n\n    We only support this function being used by Zensical.\n    Consider this function private API.\n    \"\"\"\n    mdx, mdx_config = _split_configs(markdown_extensions or [])\n    tool_config = _ToolConfig(config_file_path=config_file_path)\n\n    handlers_instance = Handlers(\n        theme=\"material\",\n        default=default_handler or _default_config[\"default_handler\"],\n        inventory_project=inventory_project or \"Project\",\n        inventory_version=inventory_version or \"0.0.0\",\n        handlers_config=handlers or _default_config[\"handlers\"],\n        custom_templates=custom_templates or _default_config[\"custom_templates\"],\n        mdx=mdx,\n        mdx_config=mdx_config,\n        locale=locale or _default_config[\"locale\"],\n        tool_config=tool_config,\n    )\n\n    handlers_instance._download_inventories()\n\n    autorefs = AutorefsPlugin()\n    autorefs.config = AutorefsConfig()\n    autorefs.scan_toc = False\n\n    return MkdocstringsExtension(handlers=handlers_instance, autorefs=autorefs)\n
"},{"location":"usage/","title":"Usage","text":""},{"location":"usage/#autodoc-syntax","title":"Autodoc syntax","text":"

mkdocstrings works by processing special expressions in your Markdown files.

The syntax is as follows:

::: identifier\n    YAML block\n

Resources on YAML.

YAML can sometimes be a bit tricky, particularly on indentation. Here are some resources that other users found useful to better understand YAML's peculiarities.

  • YAML idiosyncrasies
  • YAML multiline

The identifier is a string identifying the object you want to document. The format of an identifier can vary from one handler to another. For example, the Python handler expects the full dotted-path to a Python object: my_package.my_module.MyClass.my_method.

The YAML block is optional, and contains some configuration options:

  • handler: the name of the handler to use to collect and render this object. By default, it will use the value defined in the Global options's default_handler key, or \"python\".
  • options: a dictionary of options passed to the handler's methods responsible both for collecting and rendering the documentation. These options can be defined globally (in mkdocs.yml, see Global options), locally (as described here), or both.

Example with the Python handler

docs/my_page.mdmkdocs.ymlsrc/my_package/my_module.pyResult
# Documentation for `MyClass`\n\n::: my_package.my_module.MyClass\n    handler: python\n    options:\n      members:\n        - method_a\n        - method_b\n      show_root_heading: false\n      show_source: false\n
nav:\n  - \"My page\": my_page.md\n
class MyClass:\n    \"\"\"Print print print!\"\"\"\n\n    def method_a(self):\n        \"\"\"Print A!\"\"\"\n        print(\"A!\")\n\n    def method_b(self):\n        \"\"\"Print B!\"\"\"\n        print(\"B!\")\n\n    def method_c(self):\n        \"\"\"Print C!\"\"\"\n        print(\"C!\")\n

It is also possible to integrate a mkdocstrings identifier into a Markdown header:

## ::: my_package.my_module.MyClass\n    options:\n      show_source: false\n

The above is equivalent to:

::: my_package.my_module.MyClass\n    options:\n      show_source: false\n      heading_level: 2\n
"},{"location":"usage/#documentation-for-myclass","title":"Documentation for MyClass","text":"

Print print print!

"},{"location":"usage/#mkdocstrings.my_module.MyClass.method_a","title":" method_a(self) ","text":"

Print A!

"},{"location":"usage/#mkdocstrings.my_module.MyClass.method_b","title":" method_b(self) ","text":"

Print B!

"},{"location":"usage/#global-options","title":"Global options","text":"

mkdocstrings accepts a few top-level configuration options in mkdocs.yml:

  • default_handler: The handler that is used by default when no handler is specified.
  • custom_templates: The path to a directory containing custom templates. The path is relative to the MkDocs configuration file. See Theming.
  • handlers: The handlers' global configuration.
  • enable_inventory: Whether to enable inventory file generation. See Cross-references to other projects / inventories
  • locale: The locale used for translations. See Internationalization.
  • enabled (New in version 0.20): Whether to enable the plugin. Defaults to true. Can be used to reduce build times when doing local development. Especially useful when used with environment variables (see example below).

Example

mkdocs.yml
plugins:\n- mkdocstrings:\n    enabled: !ENV [ENABLE_MKDOCSTRINGS, true]\n    custom_templates: templates\n    default_handler: python\n    locale: en\n    handlers:\n      python:\n        options:\n          show_source: false\n

The handlers global configuration can then be overridden by local configurations:

docs/some_page.md
::: my_package.my_module.MyClass\n    options:\n      show_source: true\n

Some handlers accept additional global configuration. Check the documentation for your handler of interest in Handlers.

"},{"location":"usage/#internationalization-i18n","title":"Internationalization (I18N)","text":"

Some handlers support multiple languages.

If the handler supports localization, the locale it uses is determined by the following order of precedence:

  • locale in global options
  • theme.language: used by the MkDocs Material theme
  • theme.locale in MkDocs configuration
"},{"location":"usage/#cross-references","title":"Cross-references","text":"

Cross-references are written as Markdown reference-style links:

MarkdownHTML Result
With a custom title:\n[`Object 1`][full.path.object1]\n\nWith the identifier as title:\n[full.path.object2][]\n
<p>With a custom title:\n<a href=\"https://example.com/page1#full.path.object1\"><code>Object 1</code></a><p>\n<p>With the identifier as title:\n<a href=\"https://example.com/page2#full.path.object2\">full.path.object2</a></p>\n

Any item that was inserted using the autodoc syntax (e.g. ::: full.path.object1) is possible to link to by using the same identifier with the cross-reference syntax ([example][full.path.object1]). But the cross-references are also applicable to the items' children that get pulled in.

"},{"location":"usage/#finding-out-the-anchor","title":"Finding out the anchor","text":"

If you're not sure which exact identifier a doc item uses, you can look at its \"anchor\", which your Web browser will show in the URL bar when clicking an item's entry in the table of contents. If the URL is https://example.com/some/page.html#full.path.object1 then you know that this item is possible to link to with [example][full.path.object1], regardless of the current page.

"},{"location":"usage/#cross-references-to-any-markdown-heading","title":"Cross-references to any Markdown heading","text":"

Changed in version 0.15.

Linking to any Markdown heading used to be the default, but now opt-in is required.

If you want to link to any Markdown heading, not just mkdocstrings-inserted items, please enable the autorefs plugin for MkDocs by adding autorefs to plugins:

mkdocs.yml
plugins:\n- search\n- autorefs\n- mkdocstrings:\n    [...]\n

Note that you don't need to (pip) install anything more; this plugin is guaranteed to be pulled in with mkdocstrings.

Example

doc1.mddoc2.mdResult HTML for doc2
## Hello, world!\n\nTesting\n
## Something else\n\nPlease see the [Hello, World!][hello-world] section.\n
<p>Please see the <a href=\"doc1.html#hello-world\">Hello, World!</a> section.</p>\n
"},{"location":"usage/#cross-references-to-a-sub-heading-in-a-docstring","title":"Cross-references to a sub-heading in a docstring","text":"

New in version 0.14.

If you have a Markdown heading inside your docstring, you can also link directly to it. In the example below you see the identifier to be linked is foo.bar--tips, because it's the \"Tips\" heading that's part of the foo.bar object, joined with \"--\".

Example

foo.pydoc1.mddoc2.mdHTML result for doc2
def bar():\n    \"\"\"Hello, world!\n\n    # Tips\n\n    - Stay hydrated.\n    \"\"\"\n
::: foo.bar\n
Check out the [tips][foo.bar--tips]\n
<p>Check out the <a href=\"doc1.html#foo.bar--tips\">tips</a></p>\n

The above tip about Finding out the anchor also applies the same way here.

You may also notice that such a heading does not get rendered as a <h1> element directly, but rather the level gets shifted to fit the encompassing document structure. If you're curious about the implementation, check out mkdocstrings.HeadingShiftingTreeprocessor and others.

"},{"location":"usage/#cross-references-to-other-projects-inventories","title":"Cross-references to other projects / inventories","text":"

New in version 0.16.

Python developers coming from Sphinx might know about its intersphinx extension, that allows to cross-reference items between several projects. mkdocstrings has a similar feature.

To reference an item from another project, you must first tell mkdocstrings to load the inventory it provides. Each handler will be responsible of loading inventories specific to its language. For example, the Python handler can load Sphinx-generated inventories (objects.inv).

In the following snippet, we load the inventory provided by installer:

mkdocs.yml
plugins:\n- mkdocstrings:\n    handlers:\n      python:\n        inventories:\n        - https://installer.readthedocs.io/en/stable/objects.inv\n

Now it is possible to cross-reference installer's items. For example:

MarkdownResult (HTML)Result (displayed)
See [installer.records][] to learn about records.\n
<p>See <a href=\"https://installer.readthedocs.io/en/stable/api/records/#module-installer.records\">installer.records</a>\nto learn about records.</p>\n

See installer.records to learn about records.

You can of course select another version of the inventory, for example:

plugins:\n- mkdocstrings:\n    handlers:\n      python:\n        inventories:\n        # latest instead of stable\n        - https://installer.readthedocs.io/en/latest/objects.inv\n

In case the inventory file is not served under the base documentation URL, you can explicitly specify both URLs:

plugins:\n- mkdocstrings:\n    handlers:\n      python:\n        inventories:\n        - url: https://cdn.example.com/version/objects.inv\n          base_url: https://docs.example.com/version\n

Absolute URLs to cross-referenced items will then be based on https://docs.example.com/version/ instead of https://cdn.example.com/version/.

If you need authentication to access the inventory file, you can provide the credentials in the URL, either as username:password:

- url: https://username:password@private.example.com/version/objects.inv\n

...or with token authentication:

- url: https://token123@private.example.com/version/objects.inv\n

The credentials can also be specified using environment variables in the form ${ENV_VAR}:

- url: https://${USERNAME}:${PASSWORD}@private.example.com/version/objects.inv\n

Reciprocally, mkdocstrings also allows to generate an inventory file in the Sphinx format. It will be enabled by default if the Python handler is used, and generated as objects.inv in the final site directory. Other projects will be able to cross-reference items from your project.

To explicitly enable or disable the generation of the inventory file, use the global enable_inventory option:

plugins:\n- mkdocstrings:\n    enable_inventory: false\n
"},{"location":"usage/handlers/","title":"Handlers","text":"

A handler is what makes it possible to collect and render documentation for a particular language.

"},{"location":"usage/handlers/#available-handlers","title":"Available handlers","text":"
  • C
  • Crystal
  • GitHub Actions
  • Python
  • Python (Legacy)
  • MATLAB
  • Shell
  • TypeScript
  • VBA
"},{"location":"usage/handlers/#about-the-python-handlers","title":"About the Python handlers","text":"

Since version 0.18, a new Python handler is available. It is based on Griffe, which is an improved version of pytkdocs.

If you want to keep using the legacy handler as long as possible, you can depend on mkdocstrings-python-legacy directly, or specify the python-legacy extra when depending on mkdocstrings:

pyproject.toml
# PEP 621 dependencies declaration\n# adapt to your dependencies manager\n[project]\ndependencies = [\n    \"mkdocstrings[python-legacy]>=0.18\",\n]\n

The legacy handler will continue to \"work\" for many releases, as long as the new handler does not cover all previous use-cases.

"},{"location":"usage/handlers/#migrate-to-the-new-python-handler","title":"Migrate to the new Python handler","text":"

To use the new Python handler, you can depend on mkdocstrings-python directly, or specify the python extra when depending on mkdocstrings:

pyproject.toml
# PEP 621 dependencies declaration\n# adapt to your dependencies manager\n[project]\ndependencies = [\n    \"mkdocstrings[python]>=0.18\",\n]\n
"},{"location":"usage/handlers/#selection-options","title":"Selection options","text":"

Warning

Since mkdocstrings 0.19, the YAML selection key is merged into the options key.

  • filters is implemented, and used as before.
  • members is implemented, and used as before.
  • inherited_members is implemented.
  • docstring_style is implemented, and used as before, except for the restructured-text style which is renamed sphinx. Numpy-style is now built-in, so you can stop depending on pytkdocs[numpy-style] or docstring_parser.
  • docstring_options is implemented, and used as before. Refer to the griffe documentation for the updated list of supported docstring options.
  • new_path_syntax is irrelevant now. If you were setting it to True, remove the option and replace every colon (:) in your autodoc identifiers by dots (.).

See all the handler's options.

"},{"location":"usage/handlers/#rendering-options","title":"Rendering options","text":"

Warning

Since mkdocstrings 0.19, the YAML rendering key is merged into the options key.

Every previous option is supported. Additional options are available:

  • separate_signature: Render the signature (or attribute value) in a code block below the heading, instead as inline code. Useful for long signatures. If Black is installed, the signature is formatted. Default: False.
  • line_length: The maximum line length to use when formatting signatures. Default: 60.
  • show_submodules: Whether to render submodules of a module when iterating on children. Default: False.
  • docstring_section_style: The style to use to render docstring sections such as attributes, parameters, etc. Available styles: \"table\" (default), \"list\" and \"spacy\". The SpaCy style is a poor implementation of their table style. We are open to improvements through PRs!

See all the handler's options.

"},{"location":"usage/handlers/#templates","title":"Templates","text":"

Templates are mostly the same as before, but the file layout has changed, as well as some file names. See the documentation about the Python handler templates.

"},{"location":"usage/handlers/#custom-handlers","title":"Custom handlers","text":"

Since version 0.14, you can create and use custom handlers thanks to namespace packages. For more information about namespace packages, see their documentation.

TL;DR - Project template for handlers.

mkdocstrings provides a Copier template to kickstart new handlers: https://github.com/mkdocstrings/handler-template. To use it, install Copier (pipx install copier), then run copier gh:mkdocstrings/handler-template my_handler to generate a new project. See its upstream documentation to learn how to work on the generated project.

"},{"location":"usage/handlers/#packaging","title":"Packaging","text":"

For mkdocstrings, a custom handler package would have the following structure:

\ud83d\udcc1 your_repository\n\u2514\u2500\u2574\ud83d\udcc1 mkdocstrings_handlers\n   \u2514\u2500\u2574\ud83d\udcc1 custom_handler\n      \u251c\u2500\u2574\ud83d\udcc1 templates\n      \u2502\u00a0 \u251c\u2500\u2574\ud83d\udcc1 material\n      \u2502  \u251c\u2500\u2574\ud83d\udcc1 mkdocs\n      \u2502  \u2514\u2500\u2574\ud83d\udcc1 readthedocs\n      \u2514\u2500\u2574\ud83d\udcc4 __init__.py\n

Note the absence of __init__.py module in mkdocstrings_handlers!

"},{"location":"usage/handlers/#code","title":"Code","text":"

A handler is a subclass of the base handler provided by mkdocstrings. See the documentation for the BaseHandler.

Subclasses of the base handler must declare a name and domain as class attributes, as well as implement the following methods:

  • collect(identifier, options) (required): method responsible for collecting and returning data (extracting documentation from source code, loading introspecting objects in memory, other sources? etc.)
  • render(identifier, options) (required): method responsible for actually rendering the data to HTML, using the Jinja templates provided by your package.
  • get_options(local_options) (required): method responsible for combining global options with local ones.
  • get_aliases(identifier) (recommended): method responsible for returning known aliases of object identifiers, in order to register cross-references in the autorefs plugin.
  • get_inventory_urls() (optional): method responsible for returning a list of URLs to download (object inventories) along with configuration options (for loading the inventory with load_inventory).
  • load_inventory(in_file, url, **options) (optional): method responsible for loading an inventory (binary file-handle) and yielding tuples of identifiers and URLs.
  • update_env(config) (optional): Gives you a chance to customize the Jinja environment used to render templates, for examples by adding/removing Jinja filters and global context variables.
  • teardown() (optional): Clean up / teardown anything that needs it at the end of the build.

You must implement a get_handler method at the module level, which returns an instance of your handler. This function takes the following parameters:

  • theme (string, theme name)
  • custom_templates (optional string, path to custom templates directory)
  • mdx (list, Markdown extensions)
  • mdx_config (dict, extensions configuration)
  • handler_config (dict, handle configuration)
  • tool_config (dict, the whole MkDocs configuration)

These arguments are all passed as keyword arguments, so you can ignore them by adding **kwargs or similar to your signature.

You should not modify the MkDocs config but can use it to get information about the MkDocs instance such as where the current site_dir lives. See the Mkdocs Configuration for more info about what is accessible from it.

Check out how the Python handler is written for inspiration.

"},{"location":"usage/handlers/#templates_1","title":"Templates","text":"

Your handler's implementation should normally be backed by templates, which go to the directory mkdocstrings_handlers/custom_handler/templates/some_theme (custom_handler here should be replaced with the actual name of your handler, and some_theme should be the name of an actual MkDocs theme that you support, e.g. material).

With that structure, you can use self.env.get_template(\"foo.html\") inside your render method. This already chooses the subdirectory based on the current MkDocs theme.

If you wish to support any MkDocs theme, rather than a few specifically selected ones, you can pick one theme's subdirectory to be the fallback for when an unknown theme is encountered. Then you just need to set the fallback_theme variable on your handler subclass. The fallback directory can be used even for themes you explicitly support: you can omit some template from one of the other theme directories in case they're exactly the same as in the fallback theme.

If your theme's HTML requires CSS to go along with it, put it into a file named mkdocstrings_handlers/custom_handler/templates/some_theme/style.css, then this will be included into the final site automatically if this handler is ever used. Alternatively, you can put the CSS as a string into the extra_css variable of your handler.

Finally, it's possible to entirely omit templates, and tell mkdocstrings to use the templates of another handler. In you handler, override the get_templates_dir() method to return the other handlers templates path:

from pathlib import Path\nfrom mkdocstrings.handlers.base import BaseHandler\n\n\nclass CobraHandler(BaseHandler):\n    def get_templates_dir(self, handler: str | None = None) -> Path:\n        # use the python handler templates\n        # (it assumes the python handler is installed)\n        return super().get_templates_dir(\"python\")\n
"},{"location":"usage/handlers/#usage","title":"Usage","text":"

When a custom handler is installed, it is then available to mkdocstrings. You can configure it as usual:

mkdocs.yml
plugins:\n- mkdocstrings:\n    handlers:\n      custom_handler:\n        handler_config_option: yes\n        options:\n          some_config_option: \"a\"\n          other_config_option: 0\n

...and use it in your autodoc instructions:

docs/some_page.md
# Documentation for an object\n\n::: some.objects.path\n    handler: custom_handler\n    options:\n      some_config_option: \"b\"\n      other_config_option: 1\n
"},{"location":"usage/handlers/#handler-extensions","title":"Handler extensions","text":"

mkdocstrings provides a way for third-party packages to extend or alter the behavior of handlers. For example, an extension of the Python handler could add specific support for another Python library.

Note

This feature is intended for developers. If you are a user and want to customize how objects are rendered, see Theming / Customization.

Such extensions can register additional template folders that will be used when rendering collected data. Extensions are responsible for synchronizing with the handler itself so that it uses the additional templates.

An extension is a Python package that defines an entry-point for a specific handler:

pyproject.toml
[project.entry-points.\"mkdocstrings.python.templates\"] # (1)!\nextension-name = \"extension_package:get_templates_path\" # (2)!\n
  1. Replace python by the name of the handler you want to add templates to.
  2. Replace extension-name by any name you want, and replace extension_package:get_templates_path by the actual module path and function name in your package.

This entry-point assumes that the extension provides a get_templates_path function directly under the extension_package package:

 pyproject.toml\n\ud83d\udcc1 extension_package/\n\u251c\u2500\u2500  __init__.py\n\u2514\u2500\u2500 \ud83d\udcc1 templates/\n
extension_package/__init__.py
from pathlib import Path\n\n\ndef get_templates_path() -> Path:\n    return Path(__file__).parent / \"templates\"\n

This function doesn't accept any argument and returns the path (pathlib.Path or str) to a directory containing templates. The directory must contain one subfolder for each supported theme, even if empty (see \"fallback theme\" in custom handlers templates). For example:

 pyproject.toml\n\ud83d\udcc1 extension_package/\n\u251c\u2500\u2500  __init__.py\n\u2514\u2500\u2500 \ud83d\udcc1 templates/\n    \u251c\u2500\u2500 \ud83d\udcc1 material/\n    \u251c\u2500\u2500 \ud83d\udcc1 readthedocs/\n    \u2514\u2500\u2500 \ud83d\udcc1 mkdocs/\n

mkdocstrings will add the folders corresponding to the user-selected theme, and to the handler's defined fallback theme, as usual.

The names of the extension templates must not overlap with the handler's original templates.

The extension is then responsible, in collaboration with its target handler, for mutating the collected data in order to instruct the handler to use one of the extension template when rendering particular objects. See each handler's docs to see if they support extensions, and how.

"},{"location":"usage/theming/","title":"Themes","text":"

mkdocstrings can support multiple MkDocs themes. It currently supports the Material for MkDocs theme and, partially, the built-in MkDocs and ReadTheDocs themes.

Each handler can fallback to a particular theme when the user selected theme is not supported. For example, the Python handler will fallback to the Material for MkDocs templates.

"},{"location":"usage/theming/#customization","title":"Customization","text":"

There is some degree of customization possible in mkdocstrings. First, you can write custom templates to override the theme templates. Second, the provided templates make use of CSS classes, so you can tweak the look and feel with extra CSS rules.

"},{"location":"usage/theming/#templates","title":"Templates","text":"

To use custom templates and override the theme ones, specify the relative path from your configuration file to your templates directory with the custom_templates global configuration option:

mkdocs.yml
plugins:\n- mkdocstrings:\n    custom_templates: templates\n

Your directory structure must be identical to the provided templates one:

\ud83d\udcc1 templates/\n\u251c\u2500\u2574\ud83d\udcc1 <HANDLER 1>/\n\u2502   \u251c\u2500\u2500 \ud83d\udcc1 <THEME 1>/\n\u2502   \u2514\u2500\u2500 \ud83d\udcc1 <THEME 2>/\n\u2514\u2500\u2500 \ud83d\udcc1 <HANDLER 2>/\n    \u251c\u2500\u2500 \ud83d\udcc1 <THEME 1>/\n    \u2514\u2500\u2500 \ud83d\udcc1 <THEME 2>/\n

For example, check out the Python template tree on GitHub.

You don't have to replicate the whole tree, only the handlers, themes or templates you want to override. For example, to override some templates of the Material theme for Python:

\ud83d\udcc1 templates/\n\u2514\u2500\u2500 \ud83d\udcc1 python/\n    \u2514\u2500\u2500 \ud83d\udcc1 material/\n        \u251c\u2500\u2500 \ud83d\udcc4 parameters.html\n        \u2514\u2500\u2500 \ud83d\udcc4 exceptions.html\n

In the HTML files, replace the original contents with your modified version. In the future, the templates will use Jinja blocks, so it will be easier to modify small part of the templates without copy-pasting the whole files.

See the documentation about templates for:

  • the Crystal handler: https://mkdocstrings.github.io/crystal/styling.html
  • the Python handler: https://mkdocstrings.github.io/python/usage/customization/#templates
"},{"location":"usage/theming/#debugging","title":"Debugging","text":"

Every template has access to a log function, allowing to log messages as usual:

{{ log.debug(\"A DEBUG message.\") }}\n{{ log.info(\"An INFO message.\") }}\n{{ log.warning(\"A WARNING message.\") }}\n{{ log.error(\"An ERROR message.\") }}\n{{ log.critical(\"A CRITICAL message.\") }}\n
"},{"location":"usage/theming/#css-classes","title":"CSS classes","text":"

Since each handler provides its own set of templates, with their own CSS classes, we cannot list them all here. See the documentation about CSS classes for:

  • the Crystal handler: https://mkdocstrings.github.io/crystal/styling.html#custom-styles
  • the Python handler: https://mkdocstrings.github.io/python/usage/customization/#css-classes
"},{"location":"usage/theming/#syntax-highlighting","title":"Syntax highlighting","text":"

Code blocks that occur in the docstring of an item inserted with mkdocstrings, as well as code blocks (such as Source code) that mkdocstrings inserts itself, are syntax-highlighted according to the same rules as other normal code blocks in your document. See more details in mkdocstrings.Highlighter.

As for the CSS class used for code blocks -- it will also match the \"normal\" config, so the default (.codehilite or .highlight) will match your chosen Markdown extension for highlighting.

Changed in version 0.15.

The CSS class used to always be .highlight, but now it depends on the configuration.

Long story short, you probably should add pymdownx.highlight to your markdown_extensions, and then use .doc-contents .highlight as the CSS selector in case you want to change something about mkdocstrings' code blocks specifically.

"},{"location":"coverage/","title":"Coverage report","text":""}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 00000000..7fcbe8f2 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,55 @@ + + + + https://mkdocstrings.github.io/ + 2025-11-30 + + + https://mkdocstrings.github.io/changelog/ + 2025-11-30 + + + https://mkdocstrings.github.io/code_of_conduct/ + 2025-11-30 + + + https://mkdocstrings.github.io/contributing/ + 2025-11-30 + + + https://mkdocstrings.github.io/credits/ + 2025-11-30 + + + https://mkdocstrings.github.io/license/ + 2025-11-30 + + + https://mkdocstrings.github.io/recipes/ + 2025-11-30 + + + https://mkdocstrings.github.io/troubleshooting/ + 2025-11-30 + + + https://mkdocstrings.github.io/reference/api/ + 2025-11-30 + + + https://mkdocstrings.github.io/usage/ + 2025-11-30 + + + https://mkdocstrings.github.io/usage/handlers/ + 2025-11-30 + + + https://mkdocstrings.github.io/usage/theming/ + 2025-11-30 + + + https://mkdocstrings.github.io/coverage/ + 2025-11-30 + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 00000000..2a86b7d7 Binary files /dev/null and b/sitemap.xml.gz differ diff --git a/docs/snippets/function_annotations_google.py b/snippets/function_annotations_google.py similarity index 100% rename from docs/snippets/function_annotations_google.py rename to snippets/function_annotations_google.py diff --git a/docs/snippets/function_annotations_rst.py b/snippets/function_annotations_rst.py similarity index 100% rename from docs/snippets/function_annotations_rst.py rename to snippets/function_annotations_rst.py diff --git a/src/mkdocstrings/__init__.py b/src/mkdocstrings/__init__.py deleted file mode 100644 index 137811b1..00000000 --- a/src/mkdocstrings/__init__.py +++ /dev/null @@ -1,66 +0,0 @@ -"""mkdocstrings package. - -Automatic documentation from sources, for MkDocs. -""" - -from __future__ import annotations - -from mkdocstrings._internal.extension import AutoDocProcessor, MkdocstringsExtension, makeExtension -from mkdocstrings._internal.handlers.base import ( - BaseHandler, - CollectionError, - CollectorItem, - HandlerConfig, - HandlerOptions, - Handlers, - ThemeNotSupported, - do_any, -) -from mkdocstrings._internal.handlers.rendering import ( - HeadingShiftingTreeprocessor, - Highlighter, - IdPrependingTreeprocessor, - MkdocstringsInnerExtension, - ParagraphStrippingTreeprocessor, -) -from mkdocstrings._internal.inventory import Inventory, InventoryItem -from mkdocstrings._internal.loggers import ( - TEMPLATES_DIRS, - LoggerAdapter, - TemplateLogger, - get_logger, - get_template_logger, - get_template_logger_function, - get_template_path, -) -from mkdocstrings._internal.plugin import MkdocstringsPlugin, PluginConfig - -__all__: list[str] = [ - "TEMPLATES_DIRS", - "AutoDocProcessor", - "BaseHandler", - "CollectionError", - "CollectorItem", - "HandlerConfig", - "HandlerOptions", - "Handlers", - "HeadingShiftingTreeprocessor", - "Highlighter", - "IdPrependingTreeprocessor", - "Inventory", - "InventoryItem", - "LoggerAdapter", - "MkdocstringsExtension", - "MkdocstringsInnerExtension", - "MkdocstringsPlugin", - "ParagraphStrippingTreeprocessor", - "PluginConfig", - "TemplateLogger", - "ThemeNotSupported", - "do_any", - "get_logger", - "get_template_logger", - "get_template_logger_function", - "get_template_path", - "makeExtension", -] diff --git a/src/mkdocstrings/_internal/debug.py b/src/mkdocstrings/_internal/debug.py deleted file mode 100644 index 7b56409b..00000000 --- a/src/mkdocstrings/_internal/debug.py +++ /dev/null @@ -1,107 +0,0 @@ -from __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") -> 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"] - variables = ["PYTHONPATH", *[var for var in os.environ if var.startswith("MKDOCSTRINGS")]] - 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() diff --git a/src/mkdocstrings/_internal/download.py b/src/mkdocstrings/_internal/download.py deleted file mode 100644 index 52bf42f5..00000000 --- a/src/mkdocstrings/_internal/download.py +++ /dev/null @@ -1,89 +0,0 @@ -from __future__ import annotations - -import base64 -import gzip -import os -import re -import urllib.parse -import urllib.request -from typing import TYPE_CHECKING, BinaryIO - -from mkdocstrings._internal.loggers import get_logger - -if TYPE_CHECKING: - from collections.abc import Mapping - - -_logger = get_logger("mkdocstrings") - -# Regex pattern for an environment variable in the form ${ENV_VAR}. -_ENV_VAR_PATTERN = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}") - -# Timeout in seconds for downloading. -_TIMEOUT = 10 - - -def _download_url_with_gz(url: str) -> bytes: - url, auth_header = _extract_auth_from_url(url) - - req = urllib.request.Request( # noqa: S310 - url, - headers={"Accept-Encoding": "gzip", "User-Agent": "mkdocstrings/0.15.0", **auth_header}, - ) - with urllib.request.urlopen(req, timeout=_TIMEOUT) as resp: # noqa: S310 - content: BinaryIO = resp - if "gzip" in resp.headers.get("content-encoding", ""): - content = gzip.GzipFile(fileobj=resp) # ty: ignore[invalid-assignment] - return content.read() - - -def _expand_env_vars(credential: str, url: str, env: Mapping[str, str] | None = None) -> str: - """A safe implementation of environment variable substitution. - - It only supports the following forms: `${ENV_VAR}`. - Neither `$ENV_VAR` or `%ENV_VAR` are supported. - """ - if env is None: - env = os.environ - - def replace_func(match: re.Match) -> str: - try: - return env[match.group(1)] - except KeyError: - _logger.warning( - "Environment variable '%s' is not set, but is used in inventory URL %s", - match.group(1), - url, - ) - return match.group(0) - - return re.sub(_ENV_VAR_PATTERN, replace_func, credential) - - -# Implementation adapted from PDM: https://github.com/pdm-project/pdm. -def _extract_auth_from_url(url: str) -> tuple[str, dict[str, str]]: - """Extract credentials from the URL if present, and return the URL and the appropriate auth header for the credentials.""" - if "@" not in url: - return url, {} - - scheme, netloc, *rest = urllib.parse.urlparse(url) - auth, host = netloc.split("@", 1) - auth = _expand_env_vars(credential=auth, url=url) - auth_header = _create_auth_header(credential=auth, url=url) - - url = urllib.parse.urlunparse((scheme, host, *rest)) - return url, auth_header - - -def _create_auth_header(credential: str, url: str) -> dict[str, str]: - """Create the Authorization header for basic or bearer authentication, depending on credential.""" - if ":" not in credential: - # We assume that the user is using a token. - _logger.debug("Using bearer token authentication for %s", url) - return {"Authorization": f"Bearer {credential}"} - - # Else, we assume that the user is using user:password. - user, pwd = credential.split(":", 1) - _logger.debug("Using basic authentication for %s", url) - credentials = base64.encodebytes(f"{user}:{pwd}".encode()).decode().strip() - return {"Authorization": f"Basic {credentials}"} diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py deleted file mode 100644 index 0722c539..00000000 --- a/src/mkdocstrings/_internal/extension.py +++ /dev/null @@ -1,493 +0,0 @@ -# This module holds the code of the Markdown extension responsible for matching "autodoc" instructions. -# -# The extension is composed of a Markdown [block processor](https://python-markdown.github.io/extensions/api/#blockparser) -# that matches indented blocks starting with a line like `::: identifier`. -# -# For each of these blocks, it uses a [handler][mkdocstrings.BaseHandler] to collect documentation about -# the given identifier and render it with Jinja templates. -# -# Both the collection and rendering process can be configured by adding YAML configuration under the "autodoc" -# instruction: -# -# ```yaml -# ::: some.identifier -# handler: python -# options: -# option1: value1 -# option2: -# - value2a -# - value2b -# option_x: etc -# ``` - -from __future__ import annotations - -import re -from functools import partial -from inspect import signature -from typing import TYPE_CHECKING, Any -from xml.etree.ElementTree import Element - -import yaml -from jinja2.exceptions import TemplateNotFound -from markdown.blockprocessors import BlockProcessor -from markdown.extensions import Extension -from markdown.treeprocessors import Treeprocessor -from mkdocs.exceptions import PluginError -from mkdocs_autorefs import AutorefsConfig, AutorefsExtension, AutorefsPlugin - -from mkdocstrings._internal.handlers.base import BaseHandler, CollectionError, CollectorItem, Handlers -from mkdocstrings._internal.loggers import get_logger - -if TYPE_CHECKING: - from collections.abc import MutableSequence - - from markdown import Markdown - - -_logger = get_logger("mkdocstrings") - - -class AutoDocProcessor(BlockProcessor): - """Our "autodoc" Markdown block processor. - - It has a [`test` method][mkdocstrings.AutoDocProcessor.test] that tells if a block matches a criterion, - and a [`run` method][mkdocstrings.AutoDocProcessor.run] that processes it. - - It also has utility methods allowing to get handlers and their configuration easily, useful when processing - a matched block. - """ - - regex = re.compile(r"^(?P#{1,6} *|)::: ?(?P.+?) *$", flags=re.MULTILINE) - """The regular expression to match our autodoc instructions.""" - - def __init__( - self, - md: Markdown, - *, - handlers: Handlers, - autorefs: AutorefsPlugin, - ) -> None: - """Initialize the object. - - Arguments: - md: A `markdown.Markdown` instance. - handlers: The handlers container. - autorefs: The autorefs plugin instance. - """ - super().__init__(parser=md.parser) - self.md = md - """The Markdown instance.""" - self._handlers = handlers - self._autorefs = autorefs - self._updated_envs: set = set() - - def test(self, parent: Element, block: str) -> bool: # noqa: ARG002 - """Match our autodoc instructions. - - Arguments: - parent: The parent element in the XML tree. - block: The block to be tested. - - Returns: - Whether this block should be processed or not. - """ - return bool(self.regex.search(block)) - - def run(self, parent: Element, blocks: MutableSequence[str]) -> None: - """Run code on the matched blocks. - - The identifier and configuration lines are retrieved from a matched block - and used to collect and render an object. - - Arguments: - parent: The parent element in the XML tree. - blocks: The rest of the blocks to be processed. - """ - block = blocks.pop(0) - match = self.regex.search(block) - - if match: - if match.start() > 0: - self.parser.parseBlocks(parent, [block[: match.start()]]) - # removes the first line - block = block[match.end() :] - - block, the_rest = self.detab(block) - - if not block and blocks and blocks[0].startswith((" handler:", " options:")): - # YAML options were separated from the `:::` line by a blank line. - block = blocks.pop(0) - - if match: - identifier = match["name"] - heading_level = match["heading"].count("#") - _logger.debug("Matched '::: %s'", identifier) - - html, handler, _ = self._process_block(identifier, block, heading_level) - el = Element("div", {"class": "mkdocstrings"}) - # The final HTML is inserted as opaque to subsequent processing, and only revealed at the end. - el.text = self.md.htmlStash.store(html) - - if handler.outer_layer: - self._process_headings(handler, el) - - parent.append(el) - - if the_rest: - # This block contained unindented line(s) after the first indented - # line. Insert these lines as the first block of the master blocks - # list for future processing. - blocks.insert(0, the_rest) - - def _process_block( - self, - identifier: str, - yaml_block: str, - heading_level: int = 0, - ) -> tuple[str, BaseHandler, CollectorItem]: - """Process an autodoc block. - - Arguments: - identifier: The identifier of the object to collect and render. - yaml_block: The YAML configuration. - heading_level: Suggested level of the heading to insert (0 to ignore). - - Raises: - PluginError: When something wrong happened during collection. - TemplateNotFound: When a template used for rendering could not be found. - - Returns: - Rendered HTML, the handler that was used, and the collected item. - """ - local_config = yaml.safe_load(yaml_block) or {} - handler_name = self._handlers.get_handler_name(local_config) - - _logger.debug("Using handler '%s'", handler_name) - handler = self._handlers.get_handler(handler_name) - - local_options = local_config.get("options", {}) - if heading_level: - # Heading level obtained from Markdown (`##`) takes precedence. - local_options["heading_level"] = heading_level - - options = handler.get_options(local_options) - - _logger.debug("Collecting data") - try: - data: CollectorItem = handler.collect(identifier, options) - except CollectionError as exception: - _logger.error("%s", exception) # noqa: TRY400 - raise PluginError(f"Could not collect '{identifier}'") from exception - - if handler_name not in self._updated_envs: # We haven't seen this handler before on this document. - _logger.debug("Updating handler's rendering env") - handler._update_env(self.md, config=self._handlers._tool_config) - self._updated_envs.add(handler_name) - - _logger.debug("Rendering templates") - if "locale" in signature(handler.render).parameters: - render = partial(handler.render, locale=self._handlers._locale) - else: - render = handler.render - try: - rendered = render(data, options) - except TemplateNotFound as exc: - _logger.error( # noqa: TRY400 - "Template '%s' not found for '%s' handler and theme '%s'.", - exc.name, - handler_name, - self._handlers._theme, - ) - raise - - return rendered, handler, data - - def _process_headings(self, handler: BaseHandler, element: Element) -> None: - # We're in the outer handler layer, as well as the outer extension layer. - # - # The "handler layer" tracks the nesting of the autodoc blocks, which can appear in docstrings. - # - # - Render ::: Object1 # Outer handler layer - # - Render Object1's docstring # Outer handler layer - # - Docstring renders ::: Object2 # Inner handler layers - # - etc. # Inner handler layers - # - # The "extension layer" tracks whether we're converting an autodoc instruction - # or nested content within it, like docstrings. Markdown conversion within Markdown conversion. - # - # - Render ::: Object1 # Outer extension layer - # - Render Object1's docstring # Inner extension layer - # - # The generated HTML was just stashed, and the `toc` extension won't be able to see headings. - # We need to duplicate the headings directly, just so `toc` can pick them up, - # otherwise they wouldn't appear in the final table of contents. - # - # These headings are generated by the `BaseHandler.do_heading` method (Jinja filter), - # which runs in the inner extension layer, and not in the outer one where we are now. - headings = handler.get_headings() - element.extend(headings) - # These duplicated headings will later be removed by our `_HeadingsPostProcessor` processor, - # which runs right after `toc` (see `MkdocstringsExtension.extendMarkdown`). - # - # If we were in an inner handler layer, we wouldn't do any of this - # and would just let headings bubble up to the outer handler layer. - - if (page := self._autorefs.current_page) is None: - return - - for heading in headings: - rendered_id = heading.attrib["id"] - - skip_inventory = "data-skip-inventory" in heading.attrib - if skip_inventory: - _logger.debug( - "Skipping heading with id %r because data-skip-inventory is present", - rendered_id, - ) - continue - - # The title is registered to be used as tooltip by autorefs. - self._autorefs.register_anchor(page, rendered_id, title=heading.text, primary=True) - - # Register all identifiers for this object - # both in the autorefs plugin and in the inventory. - aliases: tuple[str, ...] - aliases = handler.get_aliases(rendered_id) - - for alias in aliases: - if alias != rendered_id: - self._autorefs.register_anchor(page, alias, rendered_id, primary=False) - - if "data-role" in heading.attrib: - self._handlers.inventory.register( - name=rendered_id, - domain=handler.domain, - role=heading.attrib["data-role"], - priority=1, # Register with standard priority. - uri=f"{page.url}#{rendered_id}", - ) - for alias in aliases: - if alias not in self._handlers.inventory: - self._handlers.inventory.register( - name=alias, - domain=handler.domain, - role=heading.attrib["data-role"], - priority=2, # Register with lower priority. - uri=f"{page.url}#{rendered_id}", - ) - - -class _HeadingsPostProcessor(Treeprocessor): - def run(self, root: Element) -> None: - self._remove_duplicated_headings(root) - - def _remove_duplicated_headings(self, parent: Element) -> None: - carry_text = "" - for el in reversed(parent): # Reversed mainly for the ability to mutate during iteration. - if el.tag == "div" and el.get("class") == "mkdocstrings": - # Delete the duplicated headings along with their container, but keep the text (i.e. the actual HTML). - carry_text = (el.text or "") + carry_text - parent.remove(el) - else: - if carry_text: - el.tail = (el.tail or "") + carry_text - carry_text = "" - self._remove_duplicated_headings(el) - - if carry_text: - parent.text = (parent.text or "") + carry_text - - -class _TocLabelsTreeProcessor(Treeprocessor): - def run(self, root: Element) -> None: # noqa: ARG002 - self._override_toc_labels(self.md.toc_tokens) # ty: ignore[unresolved-attribute] - - def _override_toc_labels(self, tokens: list[dict[str, Any]]) -> None: - for token in tokens: - if (label := token.get("data-toc-label")) and token["name"] != label: - token["name"] = label - self._override_toc_labels(token["children"]) - - -class MkdocstringsExtension(Extension): - """Our Markdown extension. - - It cannot work outside of `mkdocstrings`. - """ - - def __init__( - self, - handlers: Handlers, - autorefs: AutorefsPlugin, - *, - autorefs_extension: bool = False, - **kwargs: Any, - ) -> None: - """Initialize the object. - - Arguments: - handlers: The handlers container. - autorefs: The autorefs plugin instance. - autorefs_extension: Whether the autorefs extension must be registered. - **kwargs: Keyword arguments used by `markdown.extensions.Extension`. - """ - super().__init__(**kwargs) - self._handlers = handlers - self._autorefs = autorefs - self._autorefs_extension = autorefs_extension - - def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent method's name) - """Register the extension. - - Add an instance of our [`AutoDocProcessor`][mkdocstrings.AutoDocProcessor] to the Markdown parser. - - Arguments: - md: A `markdown.Markdown` instance. - """ - md.registerExtension(self) - - # Zensical integration: get the current page from the Zensical-specific preprocessor. - if "zensical_current_page" in md.preprocessors: - self._autorefs.current_page = md.preprocessors["zensical_current_page"] - - md.parser.blockprocessors.register( - AutoDocProcessor(md, handlers=self._handlers, autorefs=self._autorefs), - "mkdocstrings", - priority=75, # Right before markdown.blockprocessors.HashHeaderProcessor - ) - md.treeprocessors.register( - _HeadingsPostProcessor(md), - "mkdocstrings_post_headings", - priority=4, # Right after 'toc'. - ) - md.treeprocessors.register( - _TocLabelsTreeProcessor(md), - "mkdocstrings_post_toc_labels", - priority=4, # Right after 'toc'. - ) - - if self._autorefs_extension: - AutorefsExtension(self._autorefs).extendMarkdown(md) - - -# ----------------------------------------------------------------------------- -# The following is only used by Zensical. The goal is to provide temporary -# compatibility for users migrating from MkDocs (and Material for MkDocs) -# to Zensical. When detecting the use of the mkdocstrings plugin in mkdocs.yml, -# Zensical will add the mkdocstrings extension to its Markdown extensions. - -_default_config: dict[str, Any] = { - "default_handler": "python", - "handlers": {}, - "custom_templates": None, - "locale": "en", - "enable_inventory": True, - "enabled": True, -} - - -def _split_configs( - markdown_extensions: list[str | dict[str, dict[str, Any]] | Extension], -) -> tuple[list[str | Extension], dict[str, dict[str, Any]]]: - # Split markdown extensions and their configs from mkdocs.yml - mdx: list[str | Extension] = [] - mdx_config: dict[str, dict[str, Any]] = {} - for item in markdown_extensions: - if isinstance(item, (str, Extension)): - mdx.append(item) - elif isinstance(item, dict): - for key, value in item.items(): - mdx.append(key) - mdx_config[key] = value - break # Only one item per dict - return mdx, mdx_config - - -class _ToolConfig: - def __init__(self, config_file_path: str | None = None) -> None: - self.config_file_path = config_file_path - - -_AUTOREFS = None -_HANDLERS = None - - -def makeExtension( # noqa: N802 - *, - default_handler: str | None = None, - inventory_project: str | None = None, - inventory_version: str | None = None, - handlers: dict[str, dict] | None = None, - custom_templates: str | None = None, - markdown_extensions: list[str | dict | Extension] | None = None, - locale: str | None = None, - config_file_path: str | None = None, -) -> MkdocstringsExtension: - """Create the extension instance. - - We only support this function being used by Zensical. - Consider this function private API. - """ - global _AUTOREFS # noqa: PLW0603 - if _AUTOREFS is None: - _AUTOREFS = AutorefsPlugin() - _AUTOREFS.config = AutorefsConfig() # ty:ignore[invalid-assignment] - _AUTOREFS.config.resolve_closest = True - _AUTOREFS.config.link_titles = "auto" - _AUTOREFS.config.strip_title_tags = "auto" - _AUTOREFS.scan_toc = True - _AUTOREFS._link_titles = "external" - _AUTOREFS._strip_title_tags = False - - global _HANDLERS # noqa: PLW0603 - if _HANDLERS is None: - mdx, mdx_config = _split_configs(markdown_extensions or []) - tool_config = _ToolConfig(config_file_path=config_file_path) - mdx.append(AutorefsExtension(_AUTOREFS)) - _HANDLERS = Handlers( - theme="material", - default=default_handler or _default_config["default_handler"], - inventory_project=inventory_project or "Project", - inventory_version=inventory_version or "0.0.0", - handlers_config=handlers or _default_config["handlers"], - custom_templates=custom_templates or _default_config["custom_templates"], - mdx=mdx, - mdx_config=mdx_config, - locale=locale or _default_config["locale"], - tool_config=tool_config, - ) - - _HANDLERS._download_inventories() - register = _AUTOREFS.register_url - for identifier, url in _HANDLERS._yield_inventory_items(): - register(identifier, url) - - return MkdocstringsExtension( - handlers=_HANDLERS, - autorefs=_AUTOREFS, - autorefs_extension=True, - ) - - -def _reset() -> None: - global _AUTOREFS, _HANDLERS # noqa: PLW0603 - _AUTOREFS = None - _HANDLERS = None - - -def _get_autorefs() -> dict[str, Any]: - if _AUTOREFS: - return { - "primary": _AUTOREFS._primary_url_map, - "secondary": _AUTOREFS._secondary_url_map, - "inventory": _AUTOREFS._abs_url_map, - "titles": _AUTOREFS._title_map, - } - return {} - - -def _get_inventory() -> bytes: - if _HANDLERS: - return _HANDLERS.inventory.format_sphinx() - return b"" diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py deleted file mode 100644 index 799c4499..00000000 --- a/src/mkdocstrings/_internal/handlers/base.py +++ /dev/null @@ -1,666 +0,0 @@ -# Base module for handlers. -# -# This module contains the base classes for implementing handlers. - -from __future__ import annotations - -import datetime -import importlib -import ssl -from concurrent import futures -from importlib.metadata import entry_points -from io import BytesIO -from pathlib import Path -from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar, cast -from warnings import warn -from xml.etree.ElementTree import Element, tostring - -from jinja2 import Environment, FileSystemLoader -from markdown import Markdown -from markupsafe import Markup -from mkdocs.utils.cache import download_and_cache_url -from mkdocs_autorefs import AutorefsInlineProcessor, BacklinksTreeProcessor - -from mkdocstrings._internal.download import _download_url_with_gz -from mkdocstrings._internal.handlers.rendering import ( - HeadingShiftingTreeprocessor, - Highlighter, - IdPrependingTreeprocessor, - MkdocstringsInnerExtension, - ParagraphStrippingTreeprocessor, -) -from mkdocstrings._internal.inventory import Inventory -from mkdocstrings._internal.loggers import get_logger, get_template_logger - -if TYPE_CHECKING: - from collections.abc import Iterable, Iterator, Mapping, Sequence - - from markdown import Extension - from markdown.extensions.toc import TocTreeprocessor - from mkdocs_autorefs import AutorefsHookInterface, Backlink - -_logger = get_logger("mkdocstrings") - -CollectorItem = Any -"""The type of the item returned by the `collect` method of a handler.""" -HandlerConfig = Any -"""The type of the configuration of a handler.""" -HandlerOptions = Any -"""The type of the options passed to a handler.""" - - -# Autodoc instructions can appear in nested Markdown, -# so we need to keep track of the Markdown conversion layer we're in. -# Since any handler can be called from any Markdown conversion layer, -# we need to keep track of the layer globally. -# This global variable is incremented/decremented in `do_convert_markdown`, -# and used in `outer_layer`. -_markdown_conversion_layer: int = 0 - - -class CollectionError(Exception): - """An exception raised when some collection of data failed.""" - - -class ThemeNotSupported(Exception): # noqa: N818 - """An exception raised to tell a theme is not supported.""" - - -def do_any(seq: Sequence, attribute: str | None = None) -> bool: - """Check if at least one of the item in the sequence evaluates to true. - - The `any` builtin as a filter for Jinja templates. - - Arguments: - seq: An iterable object. - attribute: The attribute name to use on each object of the iterable. - - Returns: - A boolean telling if any object of the iterable evaluated to True. - """ - if attribute is None: - return any(seq) - return any(_[attribute] for _ in seq) - - -class BaseHandler: - """The base handler class. - - Inherit from this class to implement a handler. - - You will have to implement the `collect` and `render` methods. - You can also implement the `teardown` method, - and override the `update_env` method, to add more filters to the Jinja environment, - making them available in your Jinja templates. - - To define a fallback theme, add a `fallback_theme` class-variable. - To add custom CSS, add an `extra_css` variable or create an 'style.css' file beside the templates. - """ - - name: ClassVar[str] - """The handler's name, for example "python".""" - - domain: ClassVar[str] - """The handler's domain, used to register objects in the inventory, for example "py".""" - - enable_inventory: ClassVar[bool] = False - """Whether the inventory creation is enabled.""" - - fallback_theme: ClassVar[str] = "" - """Fallback theme to use when a template isn't found in the configured theme.""" - - extra_css: str = "" - """Extra CSS.""" - - def __init__( - self, - *, - theme: str, - custom_templates: str | None, - mdx: Sequence[str | Extension], - mdx_config: Mapping[str, Any], - ) -> None: - """Initialize the object. - - If the given theme is not supported (it does not exist), it will look for a `fallback_theme` attribute - in `self` to use as a fallback theme. - - Keyword Arguments: - theme (str): The theme to use. - custom_templates (str | None): The path to custom templates. - mdx (list[str | Extension]): A list of Markdown extensions to use. - mdx_config (Mapping[str, Mapping[str, Any]]): Configuration for the Markdown extensions. - """ - self.theme = theme - """The selected theme.""" - self.custom_templates = custom_templates - """The path to custom templates.""" - self.mdx = mdx - """The Markdown extensions to use.""" - self.mdx_config = mdx_config - """The configuration for the Markdown extensions.""" - self._md: Markdown | None = None - self._headings: list[Element] = [] - - paths = [] - - # add selected theme templates - themes_dir = self.get_templates_dir(self.name) - paths.append(themes_dir / self.theme) - - # add extended theme templates - extended_templates_dirs = self.get_extended_templates_dirs(self.name) - paths.extend(templates_dir / self.theme for templates_dir in extended_templates_dirs) - - # add fallback theme templates - if self.fallback_theme and self.fallback_theme != self.theme: - paths.append(themes_dir / self.fallback_theme) - - # add fallback theme of extended templates - paths.extend(templates_dir / self.fallback_theme for templates_dir in extended_templates_dirs) - - for path in paths: - css_path = path / "style.css" - if css_path.is_file(): - self.extra_css += "\n" + css_path.read_text(encoding="utf-8") - break - - if self.custom_templates is not None: - paths.insert(0, Path(self.custom_templates) / self.name / self.theme) - - self.env = Environment( - autoescape=True, - loader=FileSystemLoader(paths), - auto_reload=False, # Editing a template in the middle of a build is not useful. - ) - """The Jinja environment.""" - - self.env.filters["convert_markdown"] = self.do_convert_markdown - self.env.filters["heading"] = self.do_heading - self.env.filters["any"] = do_any - self.env.globals["log"] = get_template_logger(self.name) # ty:ignore[invalid-assignment] - - @property - def md(self) -> Markdown: - """The Markdown instance. - - Raises: - RuntimeError: When the Markdown instance is not set yet. - """ - if self._md is None: - raise RuntimeError("Markdown instance not set yet") - return self._md - - def get_inventory_urls(self) -> list[tuple[str, dict[str, Any]]]: - """Return the URLs (and configuration options) of the inventory files to download.""" - return [] - - @classmethod - def load_inventory( - cls, - in_file: BinaryIO, # noqa: ARG003 - url: str, # noqa: ARG003 - base_url: str | None = None, # noqa: ARG003 - **kwargs: Any, # noqa: ARG003 - ) -> Iterator[tuple[str, str]]: - """Yield items and their URLs from an inventory file streamed from `in_file`. - - 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). - """ - yield from () - - def get_options(self, local_options: Mapping[str, Any]) -> HandlerOptions: - """Get combined options. - - Override this method to customize how options are combined, - for example by merging the global options with the local options. - By combining options here, you don't have to do it twice in `collect` and `render`. - - Arguments: - local_options: The local options. - - Returns: - The combined options. - """ - return local_options - - def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem: - """Collect data given an identifier and user configuration. - - In the implementation, you typically call a subprocess that returns JSON, and load that JSON again into - a Python dictionary for example, though the implementation is completely free. - - Arguments: - identifier: An identifier for which to collect data. For example, in Python, - it would be 'mkdocstrings.BaseHandler' to collect documentation about the BaseHandler class. - It can be anything that you can feed to the tool of your choice. - options: The final configuration options. - - Returns: - Anything you want, as long as you can feed it to the handler's `render` method. - """ - raise NotImplementedError - - def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str: - """Render a template using provided data and configuration options. - - Arguments: - data: The collected data to render. - options: The final configuration options. - locale: The locale to use for translations, if any. - - Returns: - The rendered template as HTML. - """ - raise NotImplementedError - - def render_backlinks(self, backlinks: Mapping[str, Iterable[Backlink]], *, locale: str | None = None) -> str: # noqa: ARG002 - """Render backlinks. - - Parameters: - backlinks: A mapping of identifiers to backlinks. - locale: The locale to use for translations, if any. - - Returns: - The rendered backlinks as HTML. - """ - return "" - - def teardown(self) -> None: - """Teardown the handler. - - This method should be implemented to, for example, terminate a subprocess - that was started when creating the handler instance. - """ - - def get_templates_dir(self, handler: str | None = None) -> Path: - """Return the path to the handler's templates directory. - - Override to customize how the templates directory is found. - - Arguments: - handler: The name of the handler to get the templates directory of. - - Raises: - ModuleNotFoundError: When no such handler is installed. - FileNotFoundError: When the templates directory cannot be found. - - Returns: - The templates directory path. - """ - handler = handler or self.name - try: - import mkdocstrings_handlers # noqa: PLC0415 - except ModuleNotFoundError as error: - raise ModuleNotFoundError(f"Handler '{handler}' not found, is it installed?") from error - - for path in mkdocstrings_handlers.__path__: - theme_path = Path(path, handler, "templates") - if theme_path.exists(): - return theme_path - - raise FileNotFoundError(f"Can't find 'templates' folder for handler '{handler}'") - - def get_extended_templates_dirs(self, handler: str) -> list[Path]: - """Load template extensions for the given handler, return their templates directories. - - Arguments: - handler: The name of the handler to get the extended templates directory of. - - Returns: - The extensions templates directories. - """ - discovered_extensions = entry_points(group=f"mkdocstrings.{handler}.templates") - return [extension.load()() for extension in discovered_extensions] - - def get_aliases(self, identifier: str) -> tuple[str, ...]: # noqa: ARG002 - """Return the possible aliases for a given identifier. - - Arguments: - identifier: The identifier to get the aliases of. - - Returns: - A tuple of strings - aliases. - """ - return () - - @property - def outer_layer(self) -> bool: - """Whether we're in the outer Markdown conversion layer.""" - return _markdown_conversion_layer == 0 - - def do_convert_markdown( - self, - text: str, - heading_level: int, - html_id: str = "", - *, - strip_paragraph: bool = False, - autoref_hook: AutorefsHookInterface | None = None, - ) -> Markup: - """Render Markdown text; for use inside templates. - - Arguments: - text: The text to convert. - heading_level: The base heading level to start all Markdown headings from. - html_id: The HTML id of the element that's considered the parent of this element. - strip_paragraph: Whether to exclude the `

` tag from around the whole output. - - Returns: - An HTML string. - """ - global _markdown_conversion_layer # noqa: PLW0603 - _markdown_conversion_layer += 1 - treeprocessors = self.md.treeprocessors - treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = heading_level - treeprocessors[IdPrependingTreeprocessor.name].id_prefix = html_id and html_id + "--" - treeprocessors[ParagraphStrippingTreeprocessor.name].strip = strip_paragraph - if BacklinksTreeProcessor.name in treeprocessors: - treeprocessors[BacklinksTreeProcessor.name].initial_id = html_id - if autoref_hook and AutorefsInlineProcessor.name in self.md.inlinePatterns: - self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = autoref_hook # ty: ignore[unresolved-attribute] - - try: - return Markup(self.md.convert(text)) - finally: - treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = 0 - treeprocessors[IdPrependingTreeprocessor.name].id_prefix = "" - treeprocessors[ParagraphStrippingTreeprocessor.name].strip = False - if BacklinksTreeProcessor.name in treeprocessors: - treeprocessors[BacklinksTreeProcessor.name].initial_id = None - if AutorefsInlineProcessor.name in self.md.inlinePatterns: - self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = None - self.md.reset() - _markdown_conversion_layer -= 1 - - def do_heading( - self, - content: Markup, - heading_level: int, - *, - role: str | None = None, - hidden: bool = False, - toc_label: str | None = None, - skip_inventory: bool = False, - **attributes: str, - ) -> Markup: - """Render an HTML heading and register it for the table of contents. For use inside templates. - - Arguments: - content: The HTML within the heading. - heading_level: The level of heading (e.g. 3 -> `h3`). - role: An optional role for the object bound to this heading. - hidden: If True, only register it for the table of contents, don't render anything. - toc_label: The title to use in the table of contents ('data-toc-label' attribute). - skip_inventory: Flag element to not be registered in the inventory (by setting a `data-skip-inventory` attribute). - **attributes: Any extra HTML attributes of the heading. - - Returns: - An HTML string. - """ - # Produce a heading element that will be used later, in `AutoDocProcessor.run`, to: - # - register it in the ToC: right now we're in the inner Markdown conversion layer, - # so we have to bubble up the information to the outer Markdown conversion layer, - # for the ToC extension to pick it up. - # - register it in autorefs: right now we don't know what page is being rendered, - # so we bubble up the information again to where autorefs knows the page, - # and can correctly register the heading anchor (id) to its full URL. - # - register it in the objects inventory: same as for autorefs, - # we don't know the page here, or the handler (and its domain), - # so we bubble up the information to where the mkdocstrings extension knows that. - el = Element(f"h{heading_level}", attributes) - if toc_label is None: - toc_label = content.unescape() if isinstance(content, Markup) else content - el.set("data-toc-label", toc_label) - if skip_inventory: - el.set("data-skip-inventory", "true") - if role: - el.set("data-role", role) - if content: - el.text = str(content).strip() - self._headings.append(el) - - if hidden: - return Markup('').format(attributes["id"]) - - # Now produce the actual HTML to be rendered. The goal is to wrap the HTML content into a heading. - # Start with a heading that has just attributes (no text), and add a placeholder into it. - el = Element(f"h{heading_level}", attributes) - el.append(Element("mkdocstrings-placeholder")) - # Tell the inner 'toc' extension to make its additions if configured so. - toc = cast("TocTreeprocessor", self.md.treeprocessors["toc"]) - if toc.use_anchors: - toc.add_anchor(el, attributes["id"]) - if toc.use_permalinks: - toc.add_permalink(el, attributes["id"]) - - # The content we received is HTML, so it can't just be inserted into the tree. We had marked the middle - # of the heading with a placeholder that can never occur (text can't directly contain angle brackets). - # Now this HTML wrapper can be "filled" by replacing the placeholder. - html_with_placeholder = tostring(el, encoding="unicode") - assert ( # noqa: S101 - html_with_placeholder.count("") == 1 - ), f"Bug in mkdocstrings: failed to replace in {html_with_placeholder!r}" - html = html_with_placeholder.replace("", content) - return Markup(html) - - def get_headings(self) -> Sequence[Element]: - """Return and clear the headings gathered so far. - - Returns: - A list of HTML elements. - """ - result = list(self._headings) - self._headings.clear() - return result - - def update_env(self, config: Any) -> None: - """Update the Jinja environment.""" - - def _update_env(self, md: Markdown, *, config: Any | None = None) -> None: - """Update our handler to point to our configured Markdown instance, grabbing some of the config from `md`.""" - extensions: list[str | Extension] = [*self.mdx, MkdocstringsInnerExtension(self._headings)] - - new_md = Markdown(extensions=extensions, extension_configs=self.mdx_config) - - # MkDocs adds its own (required) extension that's not part of the config. Propagate it. - if "relpath" in md.treeprocessors: - relpath = md.treeprocessors["relpath"] - new_relpath = type(relpath)(relpath.file, relpath.files, relpath.config) - new_md.treeprocessors.register(new_relpath, "relpath", priority=0) - elif "zrelpath" in md.treeprocessors: - zrelpath = md.treeprocessors["zrelpath"] - new_zrelpath = type(zrelpath)(new_md, zrelpath.path, zrelpath.use_directory_urls) - new_md.treeprocessors.register(new_zrelpath, "zrelpath", priority=0) - - self._md = new_md - - self.env.filters["highlight"] = Highlighter(new_md).highlight - - self.update_env(config) - - -class Handlers: - """A collection of handlers. - - Do not instantiate this directly. [The plugin][mkdocstrings.MkdocstringsPlugin] will keep one instance of - this for the purpose of caching. Use [mkdocstrings.MkdocstringsPlugin.get_handler][] for convenient access. - """ - - def __init__( - self, - *, - theme: str, - default: str, - inventory_project: str, - inventory_version: str = "0.0.0", - handlers_config: dict[str, HandlerConfig] | None = None, - custom_templates: str | None = None, - mdx: Sequence[str | Extension] | None = None, - mdx_config: Mapping[str, Any] | None = None, - locale: str = "en", - tool_config: Any, - ) -> None: - """Initialize the object. - - Arguments: - theme: The theme to use. - default: The default handler to use. - inventory_project: The project name to use in the inventory. - inventory_version: The project version to use in the inventory. - handlers_config: The handlers configuration. - custom_templates: The path to custom templates. - mdx: A list of Markdown extensions to use. - mdx_config: Configuration for the Markdown extensions. - locale: The locale to use for translations. - tool_config: Tool configuration to pass down to handlers. - """ - self._theme = theme - self._default = default - self._handlers_config = handlers_config or {} - self._custom_templates = custom_templates - self._mdx = mdx or [] - self._mdx_config = mdx_config or {} - self._handlers: dict[str, BaseHandler] = {} - self._locale = locale - self._tool_config = tool_config - - self.inventory: Inventory = Inventory(project=inventory_project, version=inventory_version) - """The objects inventory.""" - - self._inv_futures: dict[futures.Future, tuple[BaseHandler, str, Any]] = {} - - def get_handler_name(self, config: dict) -> str: - """Return the handler name defined in an "autodoc" instruction YAML configuration, or the global default handler. - - Arguments: - config: A configuration dictionary, obtained from YAML below the "autodoc" instruction. - - Returns: - The name of the handler to use. - """ - return config.get("handler", self._default) - - def get_handler_config(self, name: str) -> dict: - """Return the global configuration of the given handler. - - Arguments: - name: The name of the handler to get the global configuration of. - - Returns: - The global configuration of the given handler. It can be an empty dictionary. - """ - return self._handlers_config.get(name, None) or {} - - def get_handler(self, name: str, handler_config: dict | None = None) -> BaseHandler: - """Get a handler thanks to its name. - - This function dynamically imports a module named "mkdocstrings_handlers.NAME", calls its - `get_handler` method to get an instance of a handler, and caches it in dictionary. - It means that during one run (for each reload when serving, or once when building), - a handler is instantiated only once, and reused for each "autodoc" instruction asking for it. - - Arguments: - name: The name of the handler. Really, it's the name of the Python module holding it. - handler_config: Configuration passed to the handler. - - Returns: - An instance of a subclass of [`BaseHandler`][mkdocstrings.BaseHandler], - as instantiated by the `get_handler` method of the handler's module. - """ - if name not in self._handlers: - if handler_config is None: - handler_config = self._handlers_config.get(name, {}) - module = importlib.import_module(f"mkdocstrings_handlers.{name}") - - self._handlers[name] = module.get_handler( - theme=self._theme, - custom_templates=self._custom_templates, - mdx=self._mdx, - mdx_config=self._mdx_config, - handler_config=handler_config, - tool_config=self._tool_config, - ) - return self._handlers[name] - - def _download_inventories(self) -> None: - """Download an inventory file from an URL. - - Arguments: - url: The URL of the inventory. - """ - to_download: list[tuple[BaseHandler, str, Any]] = [] - - for handler_name, conf in self._handlers_config.items(): - handler = self.get_handler(handler_name) - - if handler.get_inventory_urls.__func__ is BaseHandler.get_inventory_urls: - if inv_configs := conf.pop("import", ()): - warn( - "mkdocstrings v1 will stop handling 'import' in handlers configuration. " - "Instead your handler must define a `get_inventory_urls` method " - "that returns a list of URLs to download. ", - DeprecationWarning, - stacklevel=1, - ) - inv_configs = [{"url": inv} if isinstance(inv, str) else inv for inv in inv_configs] - inv_configs = [(inv.pop("url"), inv) for inv in inv_configs] - else: - inv_configs = handler.get_inventory_urls() - - to_download.extend((handler, url, conf) for url, conf in inv_configs) - - if to_download: - # YORE: EOL 3.12: Remove block. - # NOTE: Create context in main thread to fix issue - # https://github.com/mkdocstrings/mkdocstrings/issues/796. - _ = ssl.create_default_context() - - thread_pool = futures.ThreadPoolExecutor(4) - for handler, url, conf in to_download: - _logger.debug("Downloading inventory from %s", url) - future = thread_pool.submit( - download_and_cache_url, - url, - datetime.timedelta(days=1), - download=_download_url_with_gz, - ) - self._inv_futures[future] = (handler, url, conf) - thread_pool.shutdown(wait=False) - - def _yield_inventory_items(self) -> Iterator[tuple[str, str]]: - if self._inv_futures: - _logger.debug("Waiting for %s inventory download(s)", len(self._inv_futures)) - futures.wait(self._inv_futures, timeout=30) - # Reversed order so that pages from first futures take precedence: - for fut, (handler, url, conf) in reversed(self._inv_futures.items()): - try: - yield from handler.load_inventory(BytesIO(fut.result()), url, **conf) - except Exception as error: # noqa: BLE001,PERF203 - _logger.error("Couldn't load inventory %s through handler '%s': %s", url, handler.name, error) # noqa: TRY400 - self._inv_futures = {} - - @property - def seen_handlers(self) -> Iterable[BaseHandler]: - """Get the handlers that were encountered so far throughout the build. - - Returns: - An iterable of instances of [`BaseHandler`][mkdocstrings.BaseHandler] - (usable only to loop through it). - """ - return self._handlers.values() - - def teardown(self) -> None: - """Teardown all cached handlers and clear the cache.""" - for future in self._inv_futures: - future.cancel() - for handler in self.seen_handlers: - handler.teardown() - self._handlers.clear() diff --git a/src/mkdocstrings/_internal/handlers/rendering.py b/src/mkdocstrings/_internal/handlers/rendering.py deleted file mode 100644 index 264a77ef..00000000 --- a/src/mkdocstrings/_internal/handlers/rendering.py +++ /dev/null @@ -1,311 +0,0 @@ -# This module holds helpers responsible for augmentations to the Markdown sub-documents produced by handlers. - -from __future__ import annotations - -import copy -import re -import textwrap -from typing import TYPE_CHECKING, Any - -from markdown.extensions import Extension -from markdown.extensions.codehilite import CodeHiliteExtension -from markdown.treeprocessors import Treeprocessor -from markupsafe import Markup -from pymdownx.highlight import Highlight, HighlightExtension - -if TYPE_CHECKING: - from xml.etree.ElementTree import Element - - from markdown import Markdown - - -class Highlighter(Highlight): - """Code highlighter that tries to match the Markdown configuration. - - Picking up the global config and defaults works only if you use the `codehilite` or - `pymdownx.highlight` (recommended) Markdown extension. - - - If you use `pymdownx.highlight`, highlighting settings are picked up from it, and the - default CSS class is `.highlight`. This also means the default of `guess_lang: false`. - - - Otherwise, if you use the `codehilite` extension, settings are picked up from it, and the - default CSS class is `.codehilite`. Also consider setting `guess_lang: false`. - - - If neither are added to `markdown_extensions`, highlighting is enabled anyway. This is for - backwards compatibility. If you really want to disable highlighting even in *mkdocstrings*, - add one of these extensions anyway and set `use_pygments: false`. - - The underlying implementation is `pymdownx.highlight` regardless. - """ - - # https://raw.githubusercontent.com/facelessuser/pymdown-extensions/main/docs/src/markdown/extensions/highlight.md - _highlight_config_keys = frozenset( - ( - "css_class", - "guess_lang", - "default_lang", - "pygments_style", - "noclasses", - "use_pygments", - "linenums", - "linenums_special", - "linenums_style", - "linenums_class", - "extend_pygments_lang", - "language_prefix", - "code_attr_on_pre", - "auto_title", - "auto_title_map", - "line_spans", - "anchor_linenums", - "line_anchors", - "pygments_lang_class", - "stripnl", - ), - ) - - def __init__(self, md: Markdown): - """Configure to match a `markdown.Markdown` instance. - - Arguments: - md: The Markdown instance to read configs from. - """ - config: dict[str, Any] = {} - self._highlighter: str | None = None - for ext in md.registeredExtensions: - if isinstance(ext, HighlightExtension) and (ext.enabled or not config): - self._highlighter = "highlight" - config = ext.getConfigs() - break # This one takes priority, no need to continue looking - if isinstance(ext, CodeHiliteExtension) and not config: - self._highlighter = "codehilite" - config = ext.getConfigs() - config["language_prefix"] = config["lang_prefix"] - self._css_class = config.pop("css_class", "highlight") - super().__init__(**{name: opt for name, opt in config.items() if name in self._highlight_config_keys}) - - def highlight( # ty: ignore[invalid-method-override] - self, - src: str, - language: str | None = None, - *, - inline: bool = False, - dedent: bool = True, - linenums: bool | None = None, - **kwargs: Any, - ) -> str: - """Highlight a code-snippet. - - Arguments: - src: The code to highlight. - language: Explicitly tell what language to use for highlighting. - inline: Whether to highlight as inline. - dedent: Whether to dedent the code before highlighting it or not. - linenums: Whether to add line numbers in the result. - **kwargs: Pass on to `pymdownx.highlight.Highlight.highlight`. - - Returns: - The highlighted code as HTML text, marked safe (not escaped for HTML). - """ - if isinstance(src, Markup): - src = src.unescape() - if dedent: - src = textwrap.dedent(src) - - kwargs.setdefault("css_class", self._css_class) - old_linenums = self.linenums - if linenums is not None: - self.linenums = linenums - try: - result = super().highlight(src, language, inline=inline, **kwargs) - finally: - self.linenums = old_linenums - - if inline: - # From the maintainer of codehilite, the codehilite CSS class, as defined by the user, - # should never be added to inline code, because codehilite does not support inline code. - # See https://github.com/Python-Markdown/markdown/issues/1220#issuecomment-1692160297. - css_class = "" if self._highlighter == "codehilite" else kwargs["css_class"] - return Markup(f'{result.text}') - return Markup(result) - - -class IdPrependingTreeprocessor(Treeprocessor): - """Prepend the configured prefix to IDs of all HTML elements.""" - - name: str = "mkdocstrings_ids" - """The name of the treeprocessor.""" - - id_prefix: str - """The prefix to add to every ID. It is prepended without any separator; specify your own separator if needed.""" - - def __init__(self, md: Markdown, id_prefix: str): - """Initialize the object. - - Arguments: - md: A `markdown.Markdown` instance. - id_prefix: The prefix to add to every ID. It is prepended without any separator. - """ - super().__init__(md) - self.id_prefix = id_prefix - - def run(self, root: Element) -> None: - """Prepend the configured prefix to all IDs in the document.""" - if self.id_prefix: - self._prefix_ids(root) - - def _prefix_ids(self, root: Element) -> None: - index = len(root) - for el in reversed(root): # Reversed mainly for the ability to mutate during iteration. - index -= 1 - - self._prefix_ids(el) - href_attr = el.get("href") - - if id_attr := el.get("id"): - if el.tag == "a" and not href_attr: - # An anchor with id and no href is used by autorefs: - # leave it untouched and insert a copy with updated id after it. - new_el = copy.deepcopy(el) - new_el.set("id", self.id_prefix + id_attr) - root.insert(index + 1, new_el) - else: - # Anchors with id and href are not used by autorefs: - # update in place. - el.set("id", self.id_prefix + id_attr) - - # Always update hrefs, names and labels-for: - # there will always be a corresponding id. - if href_attr and href_attr.startswith("#"): - el.set("href", "#" + self.id_prefix + href_attr[1:]) - - if name_attr := el.get("name"): - el.set("name", self.id_prefix + name_attr) - - if el.tag == "label": - for_attr = el.get("for") - if for_attr: - el.set("for", self.id_prefix + for_attr) - - -class HeadingShiftingTreeprocessor(Treeprocessor): - """Shift levels of all Markdown headings according to the configured base level.""" - - name: str = "mkdocstrings_headings" - """The name of the treeprocessor.""" - - regex: re.Pattern = re.compile(r"([Hh])([1-6])") - """The regex to match heading tags.""" - - shift_by: int - """The number of heading "levels" to add to every heading. `

` with `shift_by = 3` becomes `

`.""" - - def __init__(self, md: Markdown, shift_by: int): - """Initialize the object. - - Arguments: - md: A `markdown.Markdown` instance. - shift_by: The number of heading "levels" to add to every heading. - """ - super().__init__(md) - self.shift_by = shift_by - - def run(self, root: Element) -> None: - """Shift the levels of all headings in the document.""" - if not self.shift_by: - return - for el in root.iter(): - match = self.regex.fullmatch(el.tag) - if match: - level = int(match[2]) + self.shift_by - level = max(1, min(level, 6)) - el.tag = f"{match[1]}{level}" - - -class _HeadingReportingTreeprocessor(Treeprocessor): - """Records the heading elements encountered in the document.""" - - name: str = "mkdocstrings_headings_list" - """The name of the treeprocessor.""" - - regex: re.Pattern = re.compile(r"[Hh][1-6]") - """The regex to match heading tags.""" - - headings: list[Element] - """The list (the one passed in the initializer) that is used to record the heading elements (by appending to it).""" - - def __init__(self, md: Markdown, headings: list[Element]): - super().__init__(md) - self.headings = headings - - def run(self, root: Element) -> None: - """Record all heading elements encountered in the document.""" - permalink_class = self.md.treeprocessors["toc"].permalink_class - for el in root.iter(): - if self.regex.fullmatch(el.tag): - el = copy.copy(el) # noqa: PLW2901 - # 'toc' extension's first pass (which we require to build heading stubs/ids) also edits the HTML. - # Undo the permalink edit so we can pass this heading to the outer pass of the 'toc' extension. - if len(el) > 0 and el[-1].get("class") == permalink_class: - del el[-1] - self.headings.append(el) - - -class ParagraphStrippingTreeprocessor(Treeprocessor): - """Unwraps the `

` element around the whole output.""" - - name: str = "mkdocstrings_strip_paragraph" - """The name of the treeprocessor.""" - - strip: bool = False - """Whether to strip `

` elements or not.""" - - def run(self, root: Element) -> Element | None: - """Unwrap the root element if it's a single `

` element.""" - if self.strip and len(root) == 1 and root[0].tag == "p": - # Turn the single `

` element into the root element and inherit its tag name (it's significant!) - root[0].tag = root.tag - return root[0] - return None - - -class MkdocstringsInnerExtension(Extension): - """Extension that should always be added to Markdown sub-documents that handlers request (and *only* them).""" - - def __init__(self, headings: list[Element]): - """Initialize the object. - - Arguments: - headings: A list that will be populated with all HTML heading elements encountered in the document. - """ - super().__init__() - self.headings = headings - """The list that will be populated with all HTML heading elements encountered in the document.""" - - def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent method's name) - """Register the extension. - - Arguments: - md: A `markdown.Markdown` instance. - """ - md.registerExtension(self) - md.treeprocessors.register( - HeadingShiftingTreeprocessor(md, 0), - HeadingShiftingTreeprocessor.name, - priority=12, - ) - md.treeprocessors.register( - IdPrependingTreeprocessor(md, ""), - IdPrependingTreeprocessor.name, - priority=4, # Right after 'toc' (needed because that extension adds ids to headers). - ) - md.treeprocessors.register( - _HeadingReportingTreeprocessor(md, self.headings), - _HeadingReportingTreeprocessor.name, - priority=1, # Close to the end. - ) - md.treeprocessors.register( - ParagraphStrippingTreeprocessor(md), - ParagraphStrippingTreeprocessor.name, - priority=0.99, # Close to the end. - ) diff --git a/src/mkdocstrings/_internal/inventory.py b/src/mkdocstrings/_internal/inventory.py deleted file mode 100644 index 241bbb12..00000000 --- a/src/mkdocstrings/_internal/inventory.py +++ /dev/null @@ -1,185 +0,0 @@ -# Module responsible for the objects inventory. -# -# Credits to Brian Skinn and the sphobjinv project: -# https://github.com/bskinn/sphobjinv. - -from __future__ import annotations - -import re -import zlib -from textwrap import dedent -from typing import TYPE_CHECKING, BinaryIO, Literal, overload - -if TYPE_CHECKING: - from collections.abc import Collection - - -class InventoryItem: - """Inventory item.""" - - def __init__( - self, - name: str, - domain: str, - role: str, - uri: str, - priority: int = 1, - dispname: str | None = None, - ): - """Initialize the object. - - Arguments: - name: The item name. - domain: The item domain, like 'python' or 'crystal'. - role: The item role, like 'class' or 'method'. - uri: The item URI. - priority: The item priority. Only used internally by mkdocstrings and Sphinx. - dispname: The item display name. - """ - self.name: str = name - """The item name.""" - self.domain: str = domain - """The item domain.""" - self.role: str = role - """The item role.""" - self.uri: str = uri - """The item URI.""" - self.priority: int = priority - """The item priority.""" - self.dispname: str = dispname or name - """The item display name.""" - - def format_sphinx(self) -> str: - """Format this item as a Sphinx inventory line. - - Returns: - A line formatted for an `objects.inv` file. - """ - dispname = self.dispname - if dispname == self.name: - dispname = "-" - uri = self.uri - if uri.endswith(self.name): - uri = uri[: -len(self.name)] + "$" - return f"{self.name} {self.domain}:{self.role} {self.priority} {uri} {dispname}" - - sphinx_item_regex = re.compile(r"^(.+?)\s+(\S+):(\S+)\s+(-?\d+)\s+(\S+)\s*(.*)$") - """Regex to parse a Sphinx v2 inventory line.""" - - @overload - @classmethod - def parse_sphinx(cls, line: str, *, return_none: Literal[False]) -> InventoryItem: ... - - @overload - @classmethod - def parse_sphinx(cls, line: str, *, return_none: Literal[True]) -> InventoryItem | None: ... - - @classmethod - def parse_sphinx(cls, line: str, *, return_none: bool = False) -> InventoryItem | None: - """Parse a line from a Sphinx v2 inventory file and return an `InventoryItem` from it.""" - match = cls.sphinx_item_regex.search(line) - if not match: - if return_none: - return None - raise ValueError(line) - name, domain, role, priority, uri, dispname = match.groups() - if uri.endswith("$"): - uri = uri[:-1] + name - if dispname == "-": - dispname = name - return cls(name, domain, role, uri, int(priority), dispname) - - -class Inventory(dict): - """Inventory of collected and rendered objects.""" - - def __init__(self, items: list[InventoryItem] | None = None, project: str = "project", version: str = "0.0.0"): - """Initialize the object. - - Arguments: - items: A list of items. - project: The project name. - version: The project version. - """ - super().__init__() - items = items or [] - for item in items: - self[item.name] = item - self.project = project - """The project name.""" - self.version = version - """The project version.""" - - def register( - self, - name: str, - domain: str, - role: str, - uri: str, - priority: int = 1, - dispname: str | None = None, - ) -> None: - """Create and register an item. - - Arguments: - name: The item name. - domain: The item domain, like 'python' or 'crystal'. - role: The item role, like 'class' or 'method'. - uri: The item URI. - priority: The item priority. Only used internally by mkdocstrings and Sphinx. - dispname: The item display name. - """ - self[name] = InventoryItem( - name=name, - domain=domain, - role=role, - uri=uri, - priority=priority, - dispname=dispname, - ) - - def format_sphinx(self) -> bytes: - """Format this inventory as a Sphinx `objects.inv` file. - - Returns: - The inventory as bytes. - """ - header = ( - dedent( - f""" - # Sphinx inventory version 2 - # Project: {self.project} - # Version: {self.version} - # The remainder of this file is compressed using zlib. - """, - ) - .lstrip() - .encode("utf8") - ) - - lines = [ - item.format_sphinx().encode("utf8") - for item in sorted(self.values(), key=lambda item: (item.domain, item.name)) - ] - return header + zlib.compress(b"\n".join(lines) + b"\n", 9) - - @classmethod - def parse_sphinx(cls, in_file: BinaryIO, *, domain_filter: Collection[str] = ()) -> Inventory: - """Parse a Sphinx v2 inventory file and return an `Inventory` from it. - - Arguments: - in_file: The binary file-like object to read from. - domain_filter: A collection of domain values to allow (and filter out all other ones). - - Returns: - An inventory containing the collected items. - """ - for _ in range(4): - in_file.readline() - lines = zlib.decompress(in_file.read()).splitlines() - items: list[InventoryItem] = [ - item for line in lines if (item := InventoryItem.parse_sphinx(line.decode("utf8"), return_none=True)) - ] - if domain_filter: - items = [item for item in items if item.domain in domain_filter] - return cls(items) diff --git a/src/mkdocstrings/_internal/loggers.py b/src/mkdocstrings/_internal/loggers.py deleted file mode 100644 index 6c8817ac..00000000 --- a/src/mkdocstrings/_internal/loggers.py +++ /dev/null @@ -1,216 +0,0 @@ -# Logging functions. - -from __future__ import annotations - -import logging -from contextlib import suppress -from pathlib import Path -from typing import TYPE_CHECKING, Any - -from jinja2 import pass_context - -if TYPE_CHECKING: - from collections.abc import Callable, MutableMapping, Sequence - - from jinja2.runtime import Context - - -try: - import mkdocstrings_handlers -except ImportError: - TEMPLATES_DIRS: Sequence[Path] = () -else: - TEMPLATES_DIRS = tuple(mkdocstrings_handlers.__path__) - """The directories where the handler templates are located.""" - - -class LoggerAdapter(logging.LoggerAdapter): - """A logger adapter to prefix messages. - - This adapter also adds an additional parameter to logging methods - called `once`: if `True`, the message will only be logged once. - - Examples: - In Python code: - - >>> logger = get_logger("myplugin") - >>> logger.debug("This is a debug message.") - >>> logger.info("This is an info message.", once=True) - - In Jinja templates (logger available in context as `log`): - - ```jinja - {{ log.debug("This is a debug message.") }} - {{ log.info("This is an info message.", once=True) }} - ``` - """ - - def __init__(self, prefix: str, logger: logging.Logger): - """Initialize the object. - - Arguments: - prefix: The string to insert in front of every message. - logger: The logger instance. - """ - super().__init__(logger, {}) - self.prefix = prefix - """The prefix to insert in front of every message.""" - self._logged: set[tuple[LoggerAdapter, str]] = set() - - def process(self, msg: str, kwargs: MutableMapping[str, Any]) -> tuple[str, Any]: - """Process the message. - - Arguments: - msg: The message: - kwargs: Remaining arguments. - - Returns: - The processed message. - """ - return f"{self.prefix}: {msg}", kwargs - - def log(self, level: int, msg: object, *args: object, **kwargs: object) -> None: - """Log a message. - - Arguments: - level: The logging level. - msg: The message. - *args: Additional arguments passed to parent method. - **kwargs: Additional keyword arguments passed to parent method. - """ - if kwargs.pop("once", False): - if (key := (self, str(msg))) in self._logged: - return - self._logged.add(key) - super().log(level, msg, *args, **kwargs) # ty: ignore[invalid-argument-type] - - -class TemplateLogger: - """A wrapper class to allow logging in templates. - - The logging methods provided by this class all accept - two parameters: - - - `msg`: The message to log. - - `once`: If `True`, the message will only be logged once. - - Methods: - debug: Function to log a DEBUG message. - info: Function to log an INFO message. - warning: Function to log a WARNING message. - error: Function to log an ERROR message. - critical: Function to log a CRITICAL message. - """ - - def __init__(self, logger: LoggerAdapter): - """Initialize the object. - - Arguments: - logger: A logger adapter. - """ - self.debug = get_template_logger_function(logger.debug) - """Log a DEBUG message.""" - self.info = get_template_logger_function(logger.info) - """Log an INFO message.""" - self.warning = get_template_logger_function(logger.warning) - """Log a WARNING message.""" - self.error = get_template_logger_function(logger.error) - """Log an ERROR message.""" - self.critical = get_template_logger_function(logger.critical) - """Log a CRITICAL message.""" - - -class _Lazy: - unset = object() - - def __init__(self, func: Callable, *args: Any, **kwargs: Any): - self.func = func - self.args = args - self.kwargs = kwargs - self.result = self.unset - - def __call__(self): - if self.result is self.unset: - self.result = self.func(*self.args, **self.kwargs) - return self.result - - def __str__(self) -> str: - return str(self()) - - def __repr__(self) -> str: - return repr(self()) - - -def get_template_logger_function(logger_func: Callable) -> Callable: - """Create a wrapper function that automatically receives the Jinja template context. - - Arguments: - logger_func: The logger function to use within the wrapper. - - Returns: - A function. - """ - - @pass_context - def wrapper(context: Context, msg: str | None = None, *args: Any, **kwargs: Any) -> str: - """Log a message. - - Arguments: - context: The template context, automatically provided by Jinja. - msg: The message to log. - **kwargs: Additional arguments passed to the logger function. - - Returns: - An empty string. - """ - logger_func(f"%s: {msg or 'Rendering'}", _Lazy(get_template_path, context), *args, **kwargs) - return "" - - return wrapper - - -def get_template_path(context: Context) -> str: - """Return the path to the template currently using the given context. - - Arguments: - context: The template context. - - Returns: - The relative path to the template. - """ - context_name: str = str(context.name) - filename = context.environment.get_template(context_name).filename - if filename: - for template_dir in TEMPLATES_DIRS: - with suppress(ValueError): - return str(Path(filename).relative_to(template_dir)) - with suppress(ValueError): - return str(Path(filename).relative_to(Path.cwd())) - return filename - return context_name - - -def get_logger(name: str) -> LoggerAdapter: - """Return a pre-configured logger. - - Arguments: - name: The name to use with `logging.getLogger`. - - Returns: - A logger configured to work well in MkDocs. - """ - logger = logging.getLogger(f"mkdocs.plugins.{name}") - return LoggerAdapter(name.split(".", 1)[0], logger) - - -def get_template_logger(handler_name: str | None = None) -> TemplateLogger: - """Return a logger usable in templates. - - Parameters: - handler_name: The name of the handler. - - Returns: - A template logger. - """ - handler_name = handler_name or "base" - return TemplateLogger(get_logger(f"mkdocstrings_handlers.{handler_name}.templates")) diff --git a/src/mkdocstrings/_internal/plugin.py b/src/mkdocstrings/_internal/plugin.py deleted file mode 100644 index 4f9bd29d..00000000 --- a/src/mkdocstrings/_internal/plugin.py +++ /dev/null @@ -1,298 +0,0 @@ -# This module contains the "mkdocstrings" plugin for MkDocs. -# -# The plugin instantiates a Markdown extension ([`MkdocstringsExtension`][mkdocstrings.MkdocstringsExtension]), -# and adds it to the list of Markdown extensions used by `mkdocs` -# during the [`on_config` event hook](https://www.mkdocs.org/user-guide/plugins/#on_config). -# -# Once the documentation is built, the [`on_post_build` event hook](https://www.mkdocs.org/user-guide/plugins/#on_post_build) -# is triggered and calls the [`handlers.teardown()` method][mkdocstrings.Handlers.teardown]. This method is -# used to teardown the handlers that were instantiated during documentation buildup. -# -# Finally, when serving the documentation, it can add directories to watch -# during the [`on_serve` event hook](https://www.mkdocs.org/user-guide/plugins/#on_serve). - -from __future__ import annotations - -import os -import re -from functools import partial -from inspect import signature -from re import Match -from typing import TYPE_CHECKING, Any - -from mkdocs.config import Config -from mkdocs.config import config_options as opt -from mkdocs.plugins import BasePlugin, CombinedEvent, event_priority -from mkdocs.utils import write_file -from mkdocs_autorefs import AutorefsConfig, AutorefsPlugin - -from mkdocstrings._internal.extension import MkdocstringsExtension -from mkdocstrings._internal.handlers.base import BaseHandler, Handlers -from mkdocstrings._internal.loggers import get_logger - -if TYPE_CHECKING: - from jinja2.environment import Environment - from mkdocs.config.defaults import MkDocsConfig - from mkdocs.structure.files import Files - - -_logger = get_logger("mkdocstrings") - - -class PluginConfig(Config): - """The configuration options of `mkdocstrings`, written in `mkdocs.yml`.""" - - handlers = opt.Type(dict, default={}) - """ - Global configuration of handlers. - - You can set global configuration per handler, applied everywhere, - but overridable in each "autodoc" instruction. Example: - - ```yaml - plugins: - - mkdocstrings: - handlers: - python: - options: - option1: true - option2: "value" - rust: - options: - option9: 2 - ``` - """ - - default_handler = opt.Type(str, default="python") - """The default handler to use. The value is the name of the handler module. Default is "python".""" - custom_templates = opt.Optional(opt.Dir(exists=True)) - """Location of custom templates to use when rendering API objects. - - Value should be the path of a directory relative to the MkDocs configuration file. - """ - enable_inventory = opt.Optional(opt.Type(bool)) - """Whether to enable object inventory creation.""" - enabled = opt.Type(bool, default=True) - """Whether to enable the plugin. Default is true. If false, *mkdocstrings* will not collect or render anything.""" - locale = opt.Optional(opt.Type(str)) - """The locale to use for translations.""" - - -class MkdocstringsPlugin(BasePlugin[PluginConfig]): - """An `mkdocs` plugin. - - This plugin defines the following event hooks: - - - `on_config` - - `on_env` - - `on_post_build` - - Check the [Developing Plugins](https://www.mkdocs.org/user-guide/plugins/#developing-plugins) page of `mkdocs` - for more information about its plugin system. - """ - - css_filename: str = "assets/_mkdocstrings.css" - """The path of the CSS file to write in the site directory.""" - - def __init__(self) -> None: - """Initialize the object.""" - super().__init__() - self._handlers: Handlers | None = None - - @property - def handlers(self) -> Handlers: - """Get the instance of [mkdocstrings.Handlers][] for this plugin/build. - - Raises: - RuntimeError: If the plugin hasn't been initialized with a config. - - Returns: - An instance of [mkdocstrings.Handlers][] (the same throughout the build). - """ - if not self._handlers: - raise RuntimeError("The plugin hasn't been initialized with a config yet") - return self._handlers - - def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: - """Instantiate our Markdown extension. - - Hook for the [`on_config` event](https://www.mkdocs.org/user-guide/plugins/#on_config). - In this hook, we instantiate our [`MkdocstringsExtension`][mkdocstrings.MkdocstringsExtension] - and add it to the list of Markdown extensions used by `mkdocs`. - - We pass this plugin's configuration dictionary to the extension when instantiating it (it will need it - later when processing markdown to get handlers and their global configurations). - - Arguments: - config: The MkDocs config object. - - Returns: - The modified config. - """ - if not self.plugin_enabled: - _logger.debug("Plugin is not enabled. Skipping.") - return config - _logger.debug("Adding extension to the list") - - locale = self.config.locale or config.theme.get("language") or config.theme.get("locale") or "en" - locale = str(locale).replace("_", "-") - - handlers = Handlers( - default=self.config.default_handler, - handlers_config=self.config.handlers, - theme=config.theme.name or os.path.dirname(config.theme.dirs[0]), # noqa: PTH120 - custom_templates=self.config.custom_templates, - mdx=config.markdown_extensions, - mdx_config=config.mdx_configs, - inventory_project=config.site_name, - inventory_version="0.0.0", # TODO: Find a way to get actual version. - locale=locale, - tool_config=config, - ) - - handlers._download_inventories() - - AutorefsPlugin.record_backlinks = True - autorefs: AutorefsPlugin - try: - # If autorefs plugin is explicitly enabled, just use it. - autorefs = config.plugins["autorefs"] # ty: ignore[invalid-assignment] - _logger.debug("Picked up existing autorefs instance %r", autorefs) - except KeyError: - # Otherwise, add a limited instance of it that acts only on what's added through `register_anchor`. - autorefs = AutorefsPlugin() - autorefs.config = AutorefsConfig() # ty:ignore[invalid-assignment] - autorefs.scan_toc = False - config.plugins["autorefs"] = autorefs - _logger.debug("Added a subdued autorefs instance %r", autorefs) - - mkdocstrings_extension = MkdocstringsExtension(handlers, autorefs) - config.markdown_extensions.append(mkdocstrings_extension) # ty: ignore[invalid-argument-type] - - config.extra_css.insert(0, self.css_filename) # So that it has lower priority than user files. - - self._autorefs = autorefs - self._handlers = handlers - return config - - @property - def inventory_enabled(self) -> bool: - """Tell if the inventory is enabled or not. - - Returns: - Whether the inventory is enabled. - """ - inventory_enabled = self.config.enable_inventory - if inventory_enabled is None: - inventory_enabled = any(handler.enable_inventory for handler in self.handlers.seen_handlers) - return inventory_enabled - - @property - def plugin_enabled(self) -> bool: - """Tell if the plugin is enabled or not. - - Returns: - Whether the plugin is enabled. - """ - return self.config.enabled - - @event_priority(50) # Early, before autorefs' starts applying cross-refs and collecting backlinks. - def _on_env_load_inventories(self, env: Environment, config: MkDocsConfig, *args: Any, **kwargs: Any) -> None: # noqa: ARG002 - if self.plugin_enabled and self._handlers: - register = config.plugins["autorefs"].register_url # ty: ignore[unresolved-attribute] - for identifier, url in self._handlers._yield_inventory_items(): - register(identifier, url) - - @event_priority(-20) # Late, not important. - def _on_env_add_css(self, env: Environment, config: MkDocsConfig, *args: Any, **kwargs: Any) -> None: # noqa: ARG002 - if self.plugin_enabled and self._handlers: - css_content = "\n".join(handler.extra_css for handler in self.handlers.seen_handlers) - write_file(css_content.encode("utf-8"), os.path.join(config.site_dir, self.css_filename)) # noqa: PTH118 - - @event_priority(-20) # Late, not important. - def _on_env_write_inventory(self, env: Environment, config: MkDocsConfig, *args: Any, **kwargs: Any) -> None: # noqa: ARG002 - if self.plugin_enabled and self._handlers and self.inventory_enabled: - _logger.debug("Creating inventory file objects.inv") - inv_contents = self.handlers.inventory.format_sphinx() - write_file(inv_contents, os.path.join(config.site_dir, "objects.inv")) # noqa: PTH118 - - @event_priority(-100) # Last, after autorefs has finished applying cross-refs and collecting backlinks. - def _on_env_apply_backlinks(self, env: Environment, /, *, config: MkDocsConfig, files: Files) -> Environment: # noqa: ARG002 - regex = re.compile(r"") - - def repl(match: Match) -> str: - handler_name = match.group(2) - handler = self.handlers.get_handler(handler_name) - - # The handler doesn't implement backlinks, - # return early to avoid computing them. - if handler.render_backlinks.__func__ is BaseHandler.render_backlinks: - return "" - - identifier = match.group(1) - aliases = handler.get_aliases(identifier) - backlinks = self._autorefs.get_backlinks(identifier, *aliases, from_url=file.page.url) - - # No backlinks, avoid calling the handler's method. - if not backlinks: - return "" - - if "locale" in signature(handler.render_backlinks).parameters: - render_backlinks = partial(handler.render_backlinks, locale=self.handlers._locale) - else: - render_backlinks = handler.render_backlinks - - return render_backlinks(backlinks) - - for file in files: - if file.page and file.page.content: - _logger.debug("Applying backlinks in page %s", file.page.file.src_path) - file.page.content = regex.sub(repl, file.page.content) - - return env - - on_env = CombinedEvent(_on_env_load_inventories, _on_env_add_css, _on_env_write_inventory, _on_env_apply_backlinks) - """Extra actions that need to happen after all Markdown-to-HTML page rendering. - - Hook for the [`on_env` event](https://www.mkdocs.org/user-guide/plugins/#on_env). - - - Gather results from background inventory download tasks. - - Write mkdocstrings' extra files (CSS, inventory) into the site directory. - - Apply backlinks to the HTML output of each page. - """ - - def on_post_build( - self, - config: MkDocsConfig, # noqa: ARG002 - **kwargs: Any, # noqa: ARG002 - ) -> None: - """Teardown the handlers. - - Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build). - This hook is used to teardown all the handlers that were instantiated and cached during documentation buildup. - - For example, a handler could open a subprocess in the background and keep it open - to feed it "autodoc" instructions and get back JSON data. If so, it should then close the subprocess at some point: - the proper place to do this is in the handler's `teardown` method, which is indirectly called by this hook. - - Arguments: - config: The MkDocs config object. - **kwargs: Additional arguments passed by MkDocs. - """ - if not self.plugin_enabled: - return - - if self._handlers: - _logger.debug("Tearing handlers down") - self.handlers.teardown() - - def get_handler(self, handler_name: str) -> BaseHandler: - """Get a handler by its name. See [mkdocstrings.Handlers.get_handler][]. - - Arguments: - handler_name: The name of the handler. - - Returns: - An instance of a subclass of [`BaseHandler`][mkdocstrings.BaseHandler]. - """ - return self.handlers.get_handler(handler_name) diff --git a/src/mkdocstrings/py.typed b/src/mkdocstrings/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 9d29353a..00000000 --- a/tests/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Tests suite for `mkdocstrings`.""" - -from pathlib import Path - -TESTS_DIR = Path(__file__).parent -TMP_DIR = TESTS_DIR / "tmp" -FIXTURES_DIR = TESTS_DIR / "fixtures" diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index a2a40652..00000000 --- a/tests/conftest.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Configuration for the pytest test suite.""" - -from __future__ import annotations - -from collections import ChainMap -from typing import TYPE_CHECKING, Any - -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 mkdocs import config - - from mkdocstrings._internal.plugin import MkdocstringsPlugin - - -@pytest.fixture(name="mkdocs_conf") -def fixture_mkdocs_conf(request: pytest.FixtureRequest, tmp_path: Path) -> Iterator[config.Config]: - """Yield a MkDocs configuration object.""" - conf = MkDocsConfig() - while hasattr(request, "_parent_request") and hasattr(request._parent_request, "_parent_request"): - request = request._parent_request # ty: ignore[invalid-assignment] - - 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[str, Any] = dict(ChainMap(*conf_dict.get("markdown_extensions", []))) # ty: ignore[invalid-argument-type,invalid-assignment] - - 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: config.Config) -> MkdocstringsPlugin: - """Return a plugin instance.""" - return mkdocs_conf["plugins"]["mkdocstrings"] - - -@pytest.fixture(name="ext_markdown") -def fixture_ext_markdown(mkdocs_conf: MkDocsConfig) -> Markdown: - """Return a Markdown instance with MkdocstringsExtension.""" - return Markdown(extensions=mkdocs_conf["markdown_extensions"], extension_configs=mkdocs_conf["mdx_configs"]) diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py deleted file mode 100644 index fa812cec..00000000 --- a/tests/fixtures/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Some fixtures for tests.""" diff --git a/tests/fixtures/cross_reference.py b/tests/fixtures/cross_reference.py deleted file mode 100644 index 1d8c9a90..00000000 --- a/tests/fixtures/cross_reference.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Link to [something.Else][]. -""" diff --git a/tests/fixtures/footnotes.py b/tests/fixtures/footnotes.py deleted file mode 100644 index ff1786ef..00000000 --- a/tests/fixtures/footnotes.py +++ /dev/null @@ -1,15 +0,0 @@ -def func_a(): - """func_a[^1]. - - [^1]: Footnote\x20A - """ - -def func_b(): - """func_b[^x]. - - [^x]: Footnote\x20B - """ - -def func_c(): - """func_c. - """ diff --git a/tests/fixtures/headings.py b/tests/fixtures/headings.py deleted file mode 100644 index a8be8db1..00000000 --- a/tests/fixtures/headings.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -Foo -=== - -### Bar - -###### Baz -""" diff --git a/tests/fixtures/headings_many.py b/tests/fixtures/headings_many.py deleted file mode 100644 index fa643a48..00000000 --- a/tests/fixtures/headings_many.py +++ /dev/null @@ -1,10 +0,0 @@ -def heading_1(): - """## Heading one""" - - -def heading_2(): - """### Heading two""" - - -def heading_3(): - """#### Heading three""" diff --git a/tests/fixtures/html_tokens.py b/tests/fixtures/html_tokens.py deleted file mode 100644 index 3c4058b8..00000000 --- a/tests/fixtures/html_tokens.py +++ /dev/null @@ -1,2 +0,0 @@ -def func(foo="

HELLO

"): - """test""" diff --git a/tests/fixtures/markdown_anchors.py b/tests/fixtures/markdown_anchors.py deleted file mode 100644 index 74cea744..00000000 --- a/tests/fixtures/markdown_anchors.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Module docstring. - -[](){#anchor} - -Paragraph. - -[](){#heading-anchor-1} -[](){#heading-anchor-2} -[](){#heading-anchor-3} -## Heading - -[](#has-href1) -[](#has-href2){#with-id} - -Pararaph. -""" \ No newline at end of file diff --git a/tests/fixtures/nesting.py b/tests/fixtures/nesting.py deleted file mode 100644 index 92f7a9ee..00000000 --- a/tests/fixtures/nesting.py +++ /dev/null @@ -1,10 +0,0 @@ -class Class: - """A class. - - ## ::: tests.fixtures.nesting.Class.method - options: - show_root_heading: true - """ - - def method(self) -> None: - """A method.""" diff --git a/tests/fixtures/string_annotation.py b/tests/fixtures/string_annotation.py deleted file mode 100644 index cc0f09f3..00000000 --- a/tests/fixtures/string_annotation.py +++ /dev/null @@ -1,8 +0,0 @@ -from typing import Literal - - -class Foo: - @property - def foo() -> Literal["hi"]: - "hi" - return "hi" diff --git a/tests/test_api.py b/tests/test_api.py deleted file mode 100644 index 7ae732cb..00000000 --- a/tests/test_api.py +++ /dev/null @@ -1,184 +0,0 @@ -"""Tests for our own API exposition.""" - -from __future__ import annotations - -from collections import defaultdict -from pathlib import Path -from typing import TYPE_CHECKING - -import griffe -import pytest - -import mkdocstrings -from mkdocstrings import Inventory - -if TYPE_CHECKING: - from collections.abc import Iterator - - -@pytest.fixture(name="loader", scope="module") -def _fixture_loader() -> griffe.GriffeLoader: - loader = griffe.GriffeLoader() - loader.load("mkdocstrings") - loader.resolve_aliases() - return loader - - -@pytest.fixture(name="internal_api", scope="module") -def _fixture_internal_api(loader: griffe.GriffeLoader) -> griffe.Module: - return loader.modules_collection["mkdocstrings._internal"] - - -@pytest.fixture(name="public_api", scope="module") -def _fixture_public_api(loader: griffe.GriffeLoader) -> griffe.Module: - return loader.modules_collection["mkdocstrings"] - - -def _yield_public_objects( - obj: griffe.Module | griffe.Class, - *, - modules: bool = False, - modulelevel: bool = True, - inherited: bool = False, - special: bool = False, -) -> Iterator[griffe.Object | griffe.Alias]: - for member in obj.all_members.values() if inherited else obj.members.values(): - try: - if member.is_module: - if member.is_alias or not member.is_public: - continue - if modules: - yield member - yield from _yield_public_objects( - member, # ty: ignore[invalid-argument-type] - modules=modules, - modulelevel=modulelevel, - inherited=inherited, - special=special, - ) - elif member.is_public and (special or not member.is_special): - yield member - else: - continue - if member.is_class and not modulelevel: - yield from _yield_public_objects( - member, # ty: ignore[invalid-argument-type] - modules=modules, - modulelevel=False, - inherited=inherited, - special=special, - ) - except (griffe.AliasResolutionError, griffe.CyclicAliasError): - continue - - -@pytest.fixture(name="modulelevel_internal_objects", scope="module") -def _fixture_modulelevel_internal_objects(internal_api: griffe.Module) -> list[griffe.Object | griffe.Alias]: - return list(_yield_public_objects(internal_api, modulelevel=True)) - - -@pytest.fixture(name="internal_objects", scope="module") -def _fixture_internal_objects(internal_api: griffe.Module) -> list[griffe.Object | griffe.Alias]: - return list(_yield_public_objects(internal_api, modulelevel=False, special=True)) - - -@pytest.fixture(name="public_objects", scope="module") -def _fixture_public_objects(public_api: griffe.Module) -> list[griffe.Object | griffe.Alias]: - return list(_yield_public_objects(public_api, modulelevel=False, inherited=True, special=True)) - - -@pytest.fixture(name="inventory", scope="module") -def _fixture_inventory() -> Inventory: - inventory_file = Path(__file__).parent.parent / "site" / "objects.inv" - if not inventory_file.exists(): - pytest.skip("The objects inventory is not available.") - with inventory_file.open("rb") as file: - return Inventory.parse_sphinx(file) - - -def test_exposed_objects(modulelevel_internal_objects: list[griffe.Object | griffe.Alias]) -> None: - """All public objects in the internal API are exposed under `mkdocstrings`.""" - not_exposed = [ - obj.path - for obj in modulelevel_internal_objects - if obj.name not in mkdocstrings.__all__ or not hasattr(mkdocstrings, obj.name) - ] - assert not not_exposed, "Objects not exposed:\n" + "\n".join(sorted(not_exposed)) - - -def test_unique_names(modulelevel_internal_objects: list[griffe.Object | griffe.Alias]) -> None: - """All internal objects have unique names.""" - names_to_paths = defaultdict(list) - for obj in modulelevel_internal_objects: - names_to_paths[obj.name].append(obj.path) - non_unique = [paths for paths in names_to_paths.values() if len(paths) > 1] - assert not non_unique, "Non-unique names:\n" + "\n".join(str(paths) for paths in non_unique) - - -def test_single_locations(public_api: griffe.Module) -> None: - """All objects have a single public location.""" - - def _public_path(obj: griffe.Object | griffe.Alias) -> bool: - return obj.is_public and (obj.parent is None or _public_path(obj.parent)) - - multiple_locations = {} - for obj_name in mkdocstrings.__all__: - obj = public_api[obj_name] - if obj.aliases and ( - public_aliases := [path for path, alias in obj.aliases.items() if path != obj.path and _public_path(alias)] - ): - multiple_locations[obj.path] = public_aliases - assert not multiple_locations, "Multiple public locations:\n" + "\n".join( - f"{path}: {aliases}" for path, aliases in multiple_locations.items() - ) - - -def test_api_matches_inventory(inventory: Inventory, public_objects: list[griffe.Object | griffe.Alias]) -> None: - """All public objects are added to the inventory.""" - ignore_names = {"__getattr__", "__init__", "__repr__", "__str__", "__post_init__"} - not_in_inventory = [ - f"{obj.relative_filepath}:{obj.lineno}: {obj.path}" - for obj in public_objects - if obj.name not in ignore_names and obj.path not in inventory - ] - msg = "Objects not in the inventory (try running `make run zensical build --clean`):\n{paths}" - assert not not_in_inventory, msg.format(paths="\n".join(sorted(not_in_inventory))) - - -def test_inventory_matches_api( - inventory: Inventory, - public_objects: list[griffe.Object | griffe.Alias], - loader: griffe.GriffeLoader, -) -> None: - """The inventory doesn't contain any additional Python object.""" - not_in_api = [] - public_api_paths = {obj.path for obj in public_objects} - public_api_paths.add("mkdocstrings") - for item in inventory.values(): - if ( - item.domain == "py" - and "(" not in item.name - and (item.name == "mkdocstrings" or item.name.startswith("mkdocstrings.")) - ): - obj = loader.modules_collection[item.name] - - if obj.path not in public_api_paths and not any(path in public_api_paths for path in obj.aliases): - not_in_api.append(item.name) - msg = "Inventory objects not in public API (try running `make run zensical build --clean`):\n{paths}" - assert not not_in_api, msg.format(paths="\n".join(sorted(not_in_api))) - - -def test_no_module_docstrings_in_internal_api(internal_api: griffe.Module) -> None: - """No module docstrings should be written in our internal API. - - The reasoning is that docstrings are addressed to users of the public API, - but internal modules are not exposed to users, so they should not have docstrings. - """ - - def _modules(obj: griffe.Module) -> Iterator[griffe.Module]: - for member in obj.modules.values(): - yield member - yield from _modules(member) - - for obj in _modules(internal_api): - assert not obj.docstring diff --git a/tests/test_download.py b/tests/test_download.py deleted file mode 100644 index 4aa19fd7..00000000 --- a/tests/test_download.py +++ /dev/null @@ -1,103 +0,0 @@ -"""Tests for the internal mkdocstrings _download module.""" - -from __future__ import annotations - -import logging -from typing import TYPE_CHECKING - -import pytest - -from mkdocstrings._internal import download - -if TYPE_CHECKING: - from collections.abc import Mapping - - -@pytest.mark.parametrize( - ("credential", "expected", "env"), - [ - ("USER", "USER", {"USER": "testuser"}), - ("$USER", "$USER", {"USER": "testuser"}), - ("${USER", "${USER", {"USER": "testuser"}), - ("$USER}", "$USER}", {"USER": "testuser"}), - ("${TOKEN}", "testtoken", {"TOKEN": "testtoken"}), - ("${USER}:${PASSWORD}", "${USER}:testpass", {"PASSWORD": "testpass"}), - ("${USER}:${PASSWORD}", "testuser:testpass", {"USER": "testuser", "PASSWORD": "testpass"}), - ( - "user_prefix_${USER}_user_$uffix:pwd_prefix_${PASSWORD}_pwd_${uffix", - "user_prefix_testuser_user_$uffix:pwd_prefix_testpass_pwd_${uffix", - {"USER": "testuser", "PASSWORD": "testpass"}, - ), - ], -) -def test_expand_env_vars(credential: str, expected: str, env: Mapping[str, str]) -> None: - """Test expanding environment variables.""" - assert download._expand_env_vars(credential, url="https://test.example.com", env=env) == expected - - -def test_expand_env_vars_with_missing_env_var(caplog: pytest.LogCaptureFixture) -> None: - """Test expanding environment variables with a missing environment variable.""" - caplog.set_level(logging.WARNING, logger="mkdocs.plugins.mkdocstrings._download") - - credential = "${USER}" - env: dict[str, str] = {} - assert download._expand_env_vars(credential, url="https://test.example.com", env=env) == "${USER}" - - output = caplog.records[0].getMessage() - assert "'USER' is not set" in output - - -@pytest.mark.parametrize( - ("url", "expected_url"), - [ - ("http://host/path", "http://host/path"), - ("http://token@host/path", "http://host/path"), - ("http://${token}@host/path", "http://host/path"), - ("http://username:password@host/path", "http://host/path"), - ("http://username:${PASSWORD}@host/path", "http://host/path"), - ("http://${USERNAME}:${PASSWORD}@host/path", "http://host/path"), - ("http://prefix${USERNAME}suffix:prefix${PASSWORD}suffix@host/path", "http://host/path"), - ], -) -def test_extract_auth_from_url(monkeypatch: pytest.MonkeyPatch, url: str, expected_url: str) -> None: - """Test extracting the auth part from the URL.""" - monkeypatch.setattr(download, "_create_auth_header", lambda *args, **kwargs: {}) - result_url, _result_auth_header = download._extract_auth_from_url(url) - assert result_url == expected_url - - -def test_create_auth_header_basic_auth() -> None: - """Test creating the Authorization header for basic authentication.""" - auth_header = download._create_auth_header(credential="testuser:testpass", url="https://test.example.com") - assert auth_header == {"Authorization": "Basic dGVzdHVzZXI6dGVzdHBhc3M="} - - -def test_create_auth_header_bearer_auth() -> None: - """Test creating the Authorization header for bearer token authentication.""" - auth_header = download._create_auth_header(credential="token123", url="https://test.example.com") - assert auth_header == {"Authorization": "Bearer token123"} - - -@pytest.mark.parametrize( - ("var", "match"), - [ - ("${var}", "var"), - ("${VAR}", "VAR"), - ("${_}", "_"), - ("${_VAR}", "_VAR"), - ("${VAR123}", "VAR123"), - ("${VAR123_}", "VAR123_"), - ("VAR", None), - ("$1VAR", None), - ("${1VAR}", None), - ("${}", None), - ("${ }", None), - ], -) -def test_env_var_pattern(var: str, match: str | None) -> None: - """Test the environment variable regex pattern.""" - _match = download._ENV_VAR_PATTERN.match(var) - if _match is None: - assert match is _match - else: - assert _match.group(1) == match diff --git a/tests/test_extension.py b/tests/test_extension.py deleted file mode 100644 index 5b031842..00000000 --- a/tests/test_extension.py +++ /dev/null @@ -1,247 +0,0 @@ -"""Tests for the extension module.""" - -from __future__ import annotations - -import re -from textwrap import dedent -from typing import TYPE_CHECKING - -import pytest - -if TYPE_CHECKING: - from markdown import Markdown - - from mkdocstrings import MkdocstringsPlugin - - -@pytest.mark.parametrize("ext_markdown", [{"markdown_extensions": [{"footnotes": {}}]}], indirect=["ext_markdown"]) -def test_multiple_footnotes(ext_markdown: Markdown) -> None: - """Assert footnotes don't get added to subsequent docstrings.""" - output = ext_markdown.convert( - dedent( - """ - Top.[^aaa] - - ::: tests.fixtures.footnotes.func_a - - ::: tests.fixtures.footnotes.func_b - - ::: tests.fixtures.footnotes.func_c - - [^aaa]: Top footnote - """, - ), - ) - assert output.count("Footnote A") == 1 - assert output.count("Footnote B") == 1 - assert output.count("Top footnote") == 1 - - -def test_markdown_heading_level(ext_markdown: Markdown) -> None: - """Assert that Markdown headings' level doesn't exceed heading_level.""" - output = ext_markdown.convert("::: tests.fixtures.headings\n options:\n show_root_heading: true") - assert ">Foo
" in output - assert ">Bar
" in output - assert ">Baz

" in output - - -def test_keeps_preceding_text(ext_markdown: Markdown) -> None: - """Assert that autodoc is recognized in the middle of a block and preceding text is kept.""" - output = ext_markdown.convert("**preceding**\n::: tests.fixtures.headings") - assert "preceding" in output - assert ">Foo

" in output - assert ":::" not in output - - -def test_reference_inside_autodoc(ext_markdown: Markdown) -> None: - """Assert cross-reference Markdown extension works correctly.""" - output = ext_markdown.convert("::: tests.fixtures.cross_reference") - assert re.search(r"Link to <.*something\.Else.*>something\.Else<.*>\.", output) - - -def test_quote_inside_annotation(ext_markdown: Markdown) -> None: - """Assert that inline highlighting doesn't double-escape HTML.""" - output = ext_markdown.convert("::: tests.fixtures.string_annotation.Foo") - assert ";hi&" in output - assert "&" not in output - - -def test_html_inside_heading(ext_markdown: Markdown) -> None: - """Assert that headings don't double-escape HTML.""" - output = ext_markdown.convert("::: tests.fixtures.html_tokens") - assert "'<" in output - assert "&" not in output - - -@pytest.mark.parametrize( - ("ext_markdown", "expect_permalink"), - [ - ({"markdown_extensions": [{"toc": {"permalink": "@@@"}}]}, "@@@"), - ({"markdown_extensions": [{"toc": {"permalink": "TeSt"}}]}, "TeSt"), - ({"markdown_extensions": [{"toc": {"permalink": True}}]}, "¶"), - ], - indirect=["ext_markdown"], -) -def test_no_double_toc(ext_markdown: Markdown, expect_permalink: str) -> None: - """Assert that the 'toc' extension doesn't apply its modification twice.""" - output = ext_markdown.convert( - dedent( - """ - # aa - - ::: tests.fixtures.headings - options: - show_root_toc_entry: false - - # bb - """, - ), - ) - assert output.count(expect_permalink) == 5 - assert 'id="tests.fixtures.headings--foo"' in output - assert ext_markdown.toc_tokens == [ # ty: ignore[unresolved-attribute] - { - "level": 1, - "id": "aa", - "html": "aa", - "name": "aa", - "data-toc-label": "", - "children": [ - { - "level": 2, - "id": "tests.fixtures.headings--foo", - "html": "Foo", - "name": "Foo", - "data-toc-label": "", - "children": [ - { - "level": 4, - "id": "tests.fixtures.headings--bar", - "html": "Bar", - "name": "Bar", - "data-toc-label": "", - "children": [ - { - "level": 6, - "id": "tests.fixtures.headings--baz", - "html": "Baz", - "name": "Baz", - "data-toc-label": "", - "children": [], - }, - ], - }, - ], - }, - ], - }, - { - "level": 1, - "id": "bb", - "html": "bb", - "name": "bb", - "data-toc-label": "", - "children": [], - }, - ] - - -def test_use_custom_handler(ext_markdown: Markdown) -> None: - """Assert that we use the custom handler declared in an individual autodoc instruction.""" - with pytest.raises(ModuleNotFoundError): - ext_markdown.convert("::: tests.fixtures.headings\n handler: not_here") - - -def test_register_every_identifier_alias(plugin: MkdocstringsPlugin, ext_markdown: Markdown) -> None: - """Assert that we don't preemptively register all identifiers of a rendered object.""" - handler = plugin._handlers.get_handler("python") # ty: ignore[unresolved-attribute] - ids = ("id1", "id2", "id3") - handler.get_aliases = lambda _: ids # ty: ignore[invalid-assignment] - autorefs = ext_markdown.parser.blockprocessors["mkdocstrings"]._autorefs - - class Page: - url = "foo" - - autorefs.current_page = Page() - ext_markdown.convert("::: tests.fixtures.headings") - for identifier in ids: - assert identifier in autorefs._secondary_url_map - - -def test_use_options_yaml_key(ext_markdown: Markdown) -> None: - """Check that using the 'options' YAML key works as expected.""" - assert "h1" in ext_markdown.convert("::: tests.fixtures.headings\n options:\n heading_level: 1") - assert "h1" not in ext_markdown.convert("::: tests.fixtures.headings\n options:\n heading_level: 2") - - -def test_use_yaml_options_after_blank_line(ext_markdown: Markdown) -> None: - """Check that YAML options are detected even after a blank line.""" - assert "h1" not in ext_markdown.convert("::: tests.fixtures.headings\n\n options:\n heading_level: 2") - - -@pytest.mark.parametrize("ext_markdown", [{"markdown_extensions": [{"admonition": {}}]}], indirect=["ext_markdown"]) -def test_removing_duplicated_headings(ext_markdown: Markdown) -> None: - """Assert duplicated headings are removed from the output.""" - output = ext_markdown.convert( - dedent( - """ - ::: tests.fixtures.headings_many.heading_1 - - !!! note - - ::: tests.fixtures.headings_many.heading_2 - - ::: tests.fixtures.headings_many.heading_3 - """, - ), - ) - assert output.count(">Heading one<") == 1 - assert output.count(">Heading two<") == 1 - assert output.count(">Heading three<") == 1 - - -def _assert_contains_in_order(items: list[str], string: str) -> None: - index = 0 - for item in items: - assert item in string[index:] - index = string.index(item, index) + len(item) - - -@pytest.mark.parametrize("ext_markdown", [{"markdown_extensions": [{"attr_list": {}}]}], indirect=["ext_markdown"]) -def test_backup_of_anchors(ext_markdown: Markdown) -> None: - """Anchors with empty `href` are backed up.""" - output = ext_markdown.convert("::: tests.fixtures.markdown_anchors") - - # Anchors with id and no href have been backed up and updated. - _assert_contains_in_order( - [ - 'id="anchor"', - 'id="tests.fixtures.markdown_anchors--anchor"', - 'id="heading-anchor-1"', - 'id="tests.fixtures.markdown_anchors--heading-anchor-1"', - 'id="heading-anchor-2"', - 'id="tests.fixtures.markdown_anchors--heading-anchor-2"', - 'id="heading-anchor-3"', - 'id="tests.fixtures.markdown_anchors--heading-anchor-3"', - ], - output, - ) - - # Anchors with href and with or without id have been updated but not backed up. - _assert_contains_in_order( - [ - 'id="tests.fixtures.markdown_anchors--with-id"', - ], - output, - ) - assert 'id="with-id"' not in output - - _assert_contains_in_order( - [ - 'href="#tests.fixtures.markdown_anchors--has-href1"', - 'href="#tests.fixtures.markdown_anchors--has-href2"', - ], - output, - ) - assert 'href="#has-href1"' not in output - assert 'href="#has-href2"' not in output diff --git a/tests/test_handlers.py b/tests/test_handlers.py deleted file mode 100644 index a1ef4ee6..00000000 --- a/tests/test_handlers.py +++ /dev/null @@ -1,138 +0,0 @@ -"""Tests for the handlers.base module.""" - -from __future__ import annotations - -from textwrap import dedent -from typing import TYPE_CHECKING - -import pytest -from dirty_equals import IsStr -from jinja2.exceptions import TemplateNotFound -from markdown import Markdown - -from mkdocstrings import Highlighter - -if TYPE_CHECKING: - from pathlib import Path - - from mkdocstrings import MkdocstringsPlugin - - -@pytest.mark.parametrize("extension_name", ["codehilite", "pymdownx.highlight"]) -def test_highlighter_without_pygments(extension_name: str) -> None: - """Assert that it's possible to disable Pygments highlighting. - - Arguments: - extension_name: The "user-chosen" Markdown extension for syntax highlighting. - """ - configs = {extension_name: {"use_pygments": False, "css_class": "hiiii"}} - md = Markdown(extensions=[extension_name], extension_configs=configs) - hl = Highlighter(md) - assert ( - hl.highlight("import foo", language="python") - == '
import foo
' - ) - assert ( - hl.highlight("import foo", language="python", inline=True) - == f'import foo' - ) - - -@pytest.mark.parametrize("extension_name", [None, "codehilite", "pymdownx.highlight"]) -@pytest.mark.parametrize("inline", [False, True]) -def test_highlighter_basic(extension_name: str | None, inline: bool) -> None: - """Assert that Pygments syntax highlighting works. - - Arguments: - extension_name: The "user-chosen" Markdown extension for syntax highlighting. - inline: Whether the highlighting was inline. - """ - md = Markdown(extensions=[extension_name], extension_configs={extension_name: {}}) if extension_name else Markdown() - hl = Highlighter(md) - - actual = hl.highlight("import foo", language="python", inline=inline) - assert "import" in actual - assert "import foo" not in actual # Highlighting has split it up. - - -def test_extended_templates(tmp_path: Path, plugin: MkdocstringsPlugin) -> None: - """Test the extended templates functionality. - - Parameters: - tmp_path: Temporary folder. - plugin: Instance of our plugin. - """ - handler = plugin._handlers.get_handler("python") # ty: ignore[unresolved-attribute] - - # monkeypatch Jinja env search path - search_paths = [ - base_theme := tmp_path / "base_theme", - base_fallback_theme := tmp_path / "base_fallback_theme", - extended_theme := tmp_path / "extended_theme", - extended_fallback_theme := tmp_path / "extended_fallback_theme", - ] - handler.env.loader.searchpath = search_paths # ty: ignore[invalid-assignment] - - # assert "new" template is not found - with pytest.raises(expected_exception=TemplateNotFound): - handler.env.get_template("new.html") - - # check precedence: base theme, base fallback theme, extended theme, extended fallback theme - # start with last one and go back up - handler.env.cache = None - - extended_fallback_theme.mkdir() - extended_fallback_theme.joinpath("new.html").write_text("extended fallback new") - assert handler.env.get_template("new.html").render() == "extended fallback new" - - extended_theme.mkdir() - extended_theme.joinpath("new.html").write_text("extended new") - assert handler.env.get_template("new.html").render() == "extended new" - - base_fallback_theme.mkdir() - base_fallback_theme.joinpath("new.html").write_text("base fallback new") - assert handler.env.get_template("new.html").render() == "base fallback new" - - base_theme.mkdir() - base_theme.joinpath("new.html").write_text("base new") - assert handler.env.get_template("new.html").render() == "base new" - - -@pytest.mark.parametrize( - "ext_markdown", - [{"markdown_extensions": [{"toc": {"permalink": True}}]}], - indirect=["ext_markdown"], -) -def test_nested_autodoc(ext_markdown: Markdown) -> None: - """Assert that nested autodocs render well and do not mess up the TOC.""" - output = ext_markdown.convert( - dedent( - """ - # ::: tests.fixtures.nesting.Class - options: - members: false - show_root_heading: true - """, - ), - ) - assert 'id="tests.fixtures.nesting.Class"' in output - assert 'id="tests.fixtures.nesting.Class.method"' in output - assert ext_markdown.toc_tokens == [ # ty: ignore[unresolved-attribute] - { - "level": 1, - "id": "tests.fixtures.nesting.Class", - "html": IsStr(), - "name": "Class", - "data-toc-label": "Class", - "children": [ - { - "level": 2, - "id": "tests.fixtures.nesting.Class.method", - "html": IsStr(), - "name": "method", - "data-toc-label": "method", - "children": [], - }, - ], - }, - ] diff --git a/tests/test_inventory.py b/tests/test_inventory.py deleted file mode 100644 index 858ac340..00000000 --- a/tests/test_inventory.py +++ /dev/null @@ -1,85 +0,0 @@ -"""Tests for the inventory module.""" - -from __future__ import annotations - -from io import BytesIO -from os.path import join -from pathlib import Path - -import pytest -from mkdocs.commands.build import build -from mkdocs.config import load_config - -from mkdocstrings import Inventory, InventoryItem - - -@pytest.mark.parametrize( - "our_inv", - [ - Inventory(), - Inventory([InventoryItem(name="object_path", domain="py", role="obj", uri="page_url")]), - Inventory([InventoryItem(name="object_path", domain="py", role="obj", uri="page_url#object_path")]), - Inventory([InventoryItem(name="object_path", domain="py", role="obj", uri="page_url#other_anchor")]), - Inventory([InventoryItem(name="o", domain="py", role="obj", uri="u#o", dispname="first line\nsecond line")]), - ], -) -def test_sphinx_load_inventory_file(our_inv: Inventory) -> None: - """Perform the 'live' inventory load test.""" - sphinx = pytest.importorskip("sphinx.util.inventory", reason="Sphinx is not installed") - - buffer = BytesIO(our_inv.format_sphinx()) - sphinx_inv = sphinx.InventoryFile.load(buffer, "", join) - - sphinx_inv_length = sum(len(sphinx_inv[key]) for key in sphinx_inv) - assert sphinx_inv_length == len(our_inv.values()) - - for item in our_inv.values(): - assert item.name in sphinx_inv[f"{item.domain}:{item.role}"] - - -def test_sphinx_load_mkdocstrings_inventory_file() -> None: - """Perform the 'live' inventory load test on mkdocstrings own inventory.""" - sphinx = pytest.importorskip("sphinx.util.inventory", reason="Sphinx is not installed") - - mkdocs_config = load_config() - mkdocs_config["plugins"].run_event("startup", command="build", dirty=False) - try: - build(mkdocs_config) - finally: - mkdocs_config["plugins"].run_event("shutdown") - own_inv = mkdocs_config["plugins"]["mkdocstrings"].handlers.inventory - - with Path("site/objects.inv").open("rb") as fp: - sphinx_inv = sphinx.InventoryFile.load(fp, "", join) - - sphinx_inv_length = sum(len(sphinx_inv[key]) for key in sphinx_inv) - assert sphinx_inv_length == len(own_inv.values()) - - for item in own_inv.values(): - assert item.name in sphinx_inv[f"{item.domain}:{item.role}"] - - -@pytest.mark.parametrize( - "our_inv", - [ - Inventory(), - Inventory([InventoryItem(name="object_path", domain="py", role="obj", uri="page_url")]), - Inventory([InventoryItem(name="object_path", domain="py", role="obj", uri="page_url#object_path")]), - Inventory([InventoryItem(name="object_path", domain="py", role="obj", uri="page_url#other_anchor")]), - Inventory([InventoryItem(name="o", domain="py", role="obj", uri="u#o", dispname="first line\nsecond line")]), - ], -) -def test_mkdocstrings_roundtrip_inventory_file(our_inv: Inventory) -> None: - """Save some inventory files, then load them in again.""" - buffer = BytesIO(our_inv.format_sphinx()) - round_tripped = Inventory.parse_sphinx(buffer) - - assert our_inv.keys() == round_tripped.keys() - for key, value in our_inv.items(): - round_tripped_item = round_tripped[key] - assert round_tripped_item.name == value.name - assert round_tripped_item.domain == value.domain - assert round_tripped_item.role == value.role - assert round_tripped_item.uri == value.uri - assert round_tripped_item.priority == value.priority - assert round_tripped_item.dispname == value.dispname.splitlines()[0] diff --git a/tests/test_loggers.py b/tests/test_loggers.py deleted file mode 100644 index 35e4dc86..00000000 --- a/tests/test_loggers.py +++ /dev/null @@ -1,64 +0,0 @@ -"""Tests for the loggers module.""" - -from unittest.mock import MagicMock - -import pytest - -from mkdocstrings import get_logger, get_template_logger - - -@pytest.mark.parametrize( - "kwargs", - [ - {}, - {"once": False}, - {"once": True}, - ], -) -def test_logger(kwargs: dict, caplog: pytest.LogCaptureFixture) -> None: - """Test logger methods. - - Parameters: - kwargs: Keyword arguments passed to the logger methods. - """ - logger = get_logger("mkdocstrings.test") - caplog.set_level(0) - for _ in range(2): - logger.debug("Debug message", **kwargs) - logger.info("Info message", **kwargs) - logger.warning("Warning message", **kwargs) - logger.error("Error message", **kwargs) - logger.critical("Critical message", **kwargs) - if kwargs.get("once", False): - assert len(caplog.records) == 5 - else: - assert len(caplog.records) == 10 - - -@pytest.mark.parametrize( - "kwargs", - [ - {}, - {"once": False}, - {"once": True}, - ], -) -def test_template_logger(kwargs: dict, caplog: pytest.LogCaptureFixture) -> None: - """Test template logger methods. - - Parameters: - kwargs: Keyword arguments passed to the template logger methods. - """ - logger = get_template_logger() - mock = MagicMock() - caplog.set_level(0) - for _ in range(2): - logger.debug(mock, "Debug message", **kwargs) - logger.info(mock, "Info message", **kwargs) - logger.warning(mock, "Warning message", **kwargs) - logger.error(mock, "Error message", **kwargs) - logger.critical(mock, "Critical message", **kwargs) - if kwargs.get("once", False): - assert len(caplog.records) == 5 - else: - assert len(caplog.records) == 10 diff --git a/tests/test_plugin.py b/tests/test_plugin.py deleted file mode 100644 index 833de692..00000000 --- a/tests/test_plugin.py +++ /dev/null @@ -1,82 +0,0 @@ -"""Tests for the mkdocstrings plugin.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from mkdocs.commands.build import build -from mkdocs.config import load_config - -from mkdocstrings import MkdocstringsPlugin - -if TYPE_CHECKING: - from pathlib import Path - - -def test_disabling_plugin(tmp_path: Path) -> None: - """Test disabling plugin.""" - docs_dir = tmp_path / "docs" - site_dir = tmp_path / "site" - docs_dir.mkdir() - site_dir.mkdir() - docs_dir.joinpath("index.md").write_text("::: mkdocstrings") - config_file = tmp_path / "mkdocs.yml" - config_file.write_text( - """ - site_name: Test - theme: mkdocs - plugins: - - mkdocstrings: - enabled: false - """, - ) - - mkdocs_config = load_config(str(config_file)) - mkdocs_config["docs_dir"] = str(docs_dir) - mkdocs_config["site_dir"] = str(site_dir) - mkdocs_config["plugins"].run_event("startup", command="build", dirty=False) - try: - build(mkdocs_config) - finally: - mkdocs_config["plugins"].run_event("shutdown") - - # make sure the instruction was not processed - assert "::: mkdocstrings" in site_dir.joinpath("index.html").read_text(encoding="utf8") - - -def test_plugin_default_config(tmp_path: Path) -> None: - """Test default config options are set for Plugin.""" - config_file_path = tmp_path / "mkdocs.yml" - plugin = MkdocstringsPlugin() - errors, warnings = plugin.load_config({}, config_file_path=str(config_file_path)) - assert errors == [] - assert warnings == [] - assert plugin.config == { - "handlers": {}, - "default_handler": "python", - "custom_templates": None, - "enable_inventory": None, - "enabled": True, - "locale": None, - } - - -def test_plugin_config_custom_templates(tmp_path: Path) -> None: - """Test custom_templates option is relative to config file.""" - config_file_path = tmp_path / "mkdocs.yml" - options = {"custom_templates": "docs/templates"} - template_dir = tmp_path / options["custom_templates"] - # Path must exist or config validation will fail. - template_dir.mkdir(parents=True) - plugin = MkdocstringsPlugin() - errors, warnings = plugin.load_config(options, config_file_path=str(config_file_path)) - assert errors == [] - assert warnings == [] - assert plugin.config == { - "handlers": {}, - "default_handler": "python", - "custom_templates": str(template_dir), - "enable_inventory": None, - "enabled": True, - "locale": None, - } diff --git a/tests/tmp/.gitkeep b/tests/tmp/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/theming/index.html b/theming/index.html new file mode 100644 index 00000000..d03b2ac1 --- /dev/null +++ b/theming/index.html @@ -0,0 +1,14 @@ + + + + + + Redirecting... + + + + + +You're being redirected to a new destination. + + diff --git a/troubleshooting/index.html b/troubleshooting/index.html new file mode 100644 index 00000000..40ac78ce --- /dev/null +++ b/troubleshooting/index.html @@ -0,0 +1,181 @@ + Troubleshooting - mkdocstrings

Troubleshooting¤

Code blocks in admonitions (in docstrings or else) are not rendered correctly¤

To render code blocks in admonitions, you need to add the pymdownx.superfences extensions to the list of Markdown extensions in mkdocs.yml. For example:

!!! note
+    Some text.
+
+    ```bash
+    echo "some code"
+    ```
+
mkdocs.yml
markdown_extensions:
+- admonition
+- codehilite
+- pymdownx.superfences
+

For code blocks in docstrings, make sure to escape newlines (\n -> \\n), or prefix the entire docstring with 'r' to make it a raw-docstring: r""". Indeed, docstrings are still strings and therefore subject to how Python parses strings.

Footnotes are duplicated or overridden¤

Before version 0.14, footnotes could be duplicated over a page. Please upgrade to version 0.14 or higher.

See also:

A warning like this one:

WARNING - Documentation file 'reference/parsers/docstrings.md' contains a link to 'reference/parsers/pytkdocs.parsers.docstrings.Section' which is not found in the documentation files.

...generally means you used parentheses () instead of brackets [] for a cross-reference. Notice the dots in reference/parsers/pytkdocs.parsers.docstrings.Section? It shows that it's probably a cross-reference, not a direct link. It's probably written like [Section](pytkdocs.parsers.docstrings.Section) in the docs, when it should be [Section][pytkdocs.parsers.docstrings.Section].

Some objects are not rendered (they do not appear in the generated docs)¤

  • Make sure the configuration options of the handler are correct. Check the documentation for Handlers to see the available options for each handler.
  • Also make sure your documentation in your source code is formatted correctly. For Python code, check the supported docstring styles page.
  • Re-run the Mkdocs command with -v, and carefully read any traceback.

Tabs in docstrings (from pymdownx.tabbed) are not working properly¤

Before version 0.14, multiple tab blocks injected on the same page would result in broken links: clicking on a tab would bring the user to the wrong one. Please upgrade to version 0.14 or higher.

See also:

If you are stuck on a version before 0.14, and want to use multiple tab blocks in one page, use this workaround.

JavaScript workaround

Put the following code in a .js file, and list it in MkDocs' extra_javascript:

// Credits to Nikolaos Zioulis (@zuru on GitHub)
+function setID(){
+    var tabs = document.getElementsByClassName("tabbed-set");
+    for (var i = 0; i < tabs.length; i++) {
+        children = tabs[i].children;
+        var counter = 0;
+        var iscontent = 0;
+        for(var j = 0; j < children.length;j++){
+            if(typeof children[j].htmlFor === 'undefined'){
+                if((iscontent + 1) % 2 == 0){
+                    // check if it is content
+                    if(iscontent == 1){
+                        btn = children[j].childNodes[1].getElementsByTagName("button");
+                    }
+                }
+                else{
+                    // if not change the id
+                    children[j].id = "__tabbed_" + String(i + 1) + "_" + String(counter + 1);
+                    children[j].name = "__tabbed_" + String(i + 1);
+                    // make default tab open
+                    if(j == 0)
+                        children[j].click();
+                }
+                iscontent++;
+            }
+            else{
+                // link to the correct tab
+                children[j].htmlFor = "__tabbed_" + String(i+1) + "_" + String(counter + 1);
+                counter ++;
+            }
+        }
+    }
+}
+setID();
+

This code will correctly reset the IDs for tabs on a same page.

The generated documentation does not look good¤

Please open an ticket on the bugtracker with a detailed explanation and screenshots of the bad-looking parts. Note that you can always customize the look of mkdocstrings blocks -- through both HTML and CSS.

Warning: could not find cross-reference target¤

New in version 0.15.

Cross-linking used to include any Markdown heading, but now it's only for mkdocstrings identifiers by default. See Cross-references to any Markdown heading to opt back in.

Make sure the referenced object is properly rendered: verify your configuration options.

For false-positives, you can wrap the text in backticks (`) to prevent mkdocstrings from trying to process it.


Python specifics¤

Nothing is rendered at all¤

Is your package available in the Python path?

See Python handler: Finding modules.

LaTeX in docstrings is not rendered correctly¤

If you are using a Markdown extension like Arithmatex Mathjax or markdown-katex to render LaTeX, add r in front of your docstring to make sure nothing is escaped. You'll still maybe have to play with escaping to get things right.

Example:

def math_function(x, y):
+    r"""
+    Look at these formulas:
+
+    ```math
+    f(x) = \int_{-\infty}^\infty
+    \hat f(\xi)\,e^{2 \pi i \xi x}
+    \,d\xi
+    ```
+    """
+

My docstrings in comments (#:) are not picked up¤

We only support docstrings in comments through the griffe-sphinx extension.

Alternatively, instead of:

import enum
+
+
+class MyEnum(enum.Enum):
+    v1 = 1  #: The first choice.
+    v2 = 2  #: The second choice.
+

You can use:

import enum
+
+
+class MyEnum(enum.Enum):
+    v1 = 1
+    """The first choice."""
+
+    v2 = 2
+    """The second choice."""
+

Or:

import enum
+
+
+class MyEnum(enum.Enum):
+    """My enum.
+
+    Attributes:
+        v1: The first choice.
+        v2: The second choice.
+    """
+
+    v1 = 1
+    v2 = 2
+

My wrapped function shows documentation/code for its wrapper instead of its own¤

Use functools.wraps():

from functools import wraps
+
+
+def my_decorator(function):
+    """The decorator docs."""
+
+    @wraps(function)
+    def wrapped_function(*args, **kwargs):
+        print("hello")
+        function(*args, **kwargs)
+        print("bye")
+
+    return wrapped_function
+
+
+@my_decorator
+def my_function(*args, **kwargs):
+    """The function docs."""
+    print(*args, **kwargs)
+

Footnotes do not render¤

The library that parses docstrings, Griffe, splits docstrings in several "sections" (example: Google-style sections syntax). If a footnote is used in a section, while referenced in another, mkdocstrings won't be able to render it correctly. The footnote and its reference must appear in the same section.

def my_function():
+    """Summary.
+
+    This is the first section[^1].
+
+    Note:
+        This is the second section[^2].
+
+    Note:
+        This is the third section[^3].
+
+    References at the end are part of yet another section (fourth here)[^4].
+
+    [^1]: Some text.
+    [^2]: Some text.
+    [^3]: Some text.
+    [^4]: Some text.
+    """
+

Here only the fourth footnote will work, because it is the only one that appear in the same section as its reference. To fix this, make sure all footnotes appear in the same section as their references:

def my_function():
+    """Summary.
+
+    This is the first section[^1].
+
+    [^1]: Some text.
+
+    Note:
+        This is the second section[^2].
+
+        [^2]: Some text.
+
+    Note:
+        This is the third section[^3].
+
+        [^3]: Some text.
+
+    References at the end are part of yet another section (fourth here)[^4].
+
+    [^4]: Some text.
+    """
+

Submodules are not rendered¤

In previous versions of mkdocstrings-python, submodules were rendered by default. This was changed and you now need to set the following option:

mkdocs.yml
plugins:
+- mkdocstrings:
+    handlers:
+      python:
+        options:
+          show_submodules: true
+
\ No newline at end of file diff --git a/docs/troubleshooting.md b/troubleshooting/index.md similarity index 56% rename from docs/troubleshooting.md rename to troubleshooting/index.md index 5e5386e3..03505478 100644 --- a/docs/troubleshooting.md +++ b/troubleshooting/index.md @@ -2,33 +2,31 @@ ## Code blocks in admonitions (in docstrings or else) are not rendered correctly -To render code blocks in admonitions, you need to add the `pymdownx.superfences` extensions to the list of -Markdown extensions in `mkdocs.yml`. For example: +To render code blocks in admonitions, you need to add the `pymdownx.superfences` extensions to the list of Markdown extensions in `mkdocs.yml`. For example: -```markdown +````markdown !!! note Some text. ```bash echo "some code" ``` -``` +```` + +mkdocs.yml -```yaml title="mkdocs.yml" +```yaml markdown_extensions: - admonition - codehilite - pymdownx.superfences ``` -For code blocks in docstrings, make sure to escape newlines (`\n` -> `\\n`), -or prefix the entire docstring with 'r' to make it a raw-docstring: `r"""`. -Indeed, docstrings are still strings and therefore subject to how Python parses strings. +For code blocks in docstrings, make sure to escape newlines (`\n` -> `\\n`), or prefix the entire docstring with 'r' to make it a raw-docstring: `r"""`. Indeed, docstrings are still strings and therefore subject to how Python parses strings. ## Footnotes are duplicated or overridden -Before version 0.14, footnotes could be duplicated over a page. -Please upgrade to version 0.14 or higher. +Before version 0.14, footnotes could be duplicated over a page. Please upgrade to version 0.14 or higher. See also: @@ -39,100 +37,85 @@ See also: A warning like this one: -> WARNING - Documentation file 'reference/parsers/docstrings.md' - contains a link to 'reference/parsers/pytkdocs.parsers.docstrings.Section' - which is not found in the documentation files. +> WARNING - Documentation file 'reference/parsers/docstrings.md' contains a link to 'reference/parsers/pytkdocs.parsers.docstrings.Section' which is not found in the documentation files. -...generally means you used parentheses `()` instead of brackets `[]` for a cross-reference. -Notice the dots in `reference/parsers/pytkdocs.parsers.docstrings.Section`? -It shows that it's probably a cross-reference, not a direct link. -It's probably written like `[Section](pytkdocs.parsers.docstrings.Section)` in the docs, -when it should be `[Section][pytkdocs.parsers.docstrings.Section]`. +...generally means you used parentheses `()` instead of brackets `[]` for a cross-reference. Notice the dots in `reference/parsers/pytkdocs.parsers.docstrings.Section`? It shows that it's probably a cross-reference, not a direct link. It's probably written like `[Section](pytkdocs.parsers.docstrings.Section)` in the docs, when it should be `[Section][pytkdocs.parsers.docstrings.Section]`. ## Some objects are not rendered (they do not appear in the generated docs) -- Make sure the configuration options of the handler are correct. - Check the documentation for [Handlers](usage/handlers.md) to see the available options for each handler. -- Also make sure your documentation in your source code is formatted correctly. - For Python code, check the [supported docstring styles](https://mkdocstrings.github.io/python/usage/#supported-docstrings-styles) page. +- Make sure the configuration options of the handler are correct. Check the documentation for [Handlers](../usage/handlers/) to see the available options for each handler. +- Also make sure your documentation in your source code is formatted correctly. For Python code, check the [supported docstring styles](https://mkdocstrings.github.io/python/usage/#supported-docstrings-styles) page. - Re-run the Mkdocs command with `-v`, and carefully read any traceback. ## Tabs in docstrings (from `pymdownx.tabbed`) are not working properly -Before version 0.14, multiple tab blocks injected on the same page -would result in broken links: clicking on a tab would bring the user to the wrong one. -Please upgrade to version 0.14 or higher. +Before version 0.14, multiple tab blocks injected on the same page would result in broken links: clicking on a tab would bring the user to the wrong one. Please upgrade to version 0.14 or higher. See also: - [Issue #193](https://github.com/mkdocstrings/mkdocstrings/issues/193) - [Footnotes are duplicated or overridden](#footnotes-are-duplicated-or-overridden). -If you are stuck on a version before 0.14, -and want to use multiple tab blocks in one page, -use this workaround. - -??? example "JavaScript workaround" - - Put the following code in a .js file, - and list it in MkDocs' `extra_javascript`: - - ```javascript - // Credits to Nikolaos Zioulis (@zuru on GitHub) - function setID(){ - var tabs = document.getElementsByClassName("tabbed-set"); - for (var i = 0; i < tabs.length; i++) { - children = tabs[i].children; - var counter = 0; - var iscontent = 0; - for(var j = 0; j < children.length;j++){ - if(typeof children[j].htmlFor === 'undefined'){ - if((iscontent + 1) % 2 == 0){ - // check if it is content - if(iscontent == 1){ - btn = children[j].childNodes[1].getElementsByTagName("button"); - } - } - else{ - // if not change the id - children[j].id = "__tabbed_" + String(i + 1) + "_" + String(counter + 1); - children[j].name = "__tabbed_" + String(i + 1); - // make default tab open - if(j == 0) - children[j].click(); +If you are stuck on a version before 0.14, and want to use multiple tab blocks in one page, use this workaround. + +JavaScript workaround + +Put the following code in a .js file, and list it in MkDocs' `extra_javascript`: + +```javascript +// Credits to Nikolaos Zioulis (@zuru on GitHub) +function setID(){ + var tabs = document.getElementsByClassName("tabbed-set"); + for (var i = 0; i < tabs.length; i++) { + children = tabs[i].children; + var counter = 0; + var iscontent = 0; + for(var j = 0; j < children.length;j++){ + if(typeof children[j].htmlFor === 'undefined'){ + if((iscontent + 1) % 2 == 0){ + // check if it is content + if(iscontent == 1){ + btn = children[j].childNodes[1].getElementsByTagName("button"); } - iscontent++; } else{ - // link to the correct tab - children[j].htmlFor = "__tabbed_" + String(i+1) + "_" + String(counter + 1); - counter ++; + // if not change the id + children[j].id = "__tabbed_" + String(i + 1) + "_" + String(counter + 1); + children[j].name = "__tabbed_" + String(i + 1); + // make default tab open + if(j == 0) + children[j].click(); } + iscontent++; + } + else{ + // link to the correct tab + children[j].htmlFor = "__tabbed_" + String(i+1) + "_" + String(counter + 1); + counter ++; } } } - setID(); - ``` +} +setID(); +``` - This code will correctly reset the IDs for tabs on a same page. +This code will correctly reset the IDs for tabs on a same page. ## The generated documentation does not look good -Please open an ticket on the [bugtracker][bugtracker] with a detailed -explanation and screenshots of the bad-looking parts. -Note that you can always [customize the look](usage/theming.md) of *mkdocstrings* blocks -- through both HTML and CSS. +Please open an ticket on the [bugtracker](https://github.com/mkdocstrings/mkdocstrings) with a detailed explanation and screenshots of the bad-looking parts. Note that you can always [customize the look](../usage/theming/) of *mkdocstrings* blocks -- through both HTML and CSS. ## Warning: could not find cross-reference target -TIP: **New in version 0.15.** -Cross-linking used to include any Markdown heading, but now it's only for *mkdocstrings* identifiers by default. -See [Cross-references to any Markdown heading](usage/index.md#cross-references-to-any-markdown-heading) to opt back in. +New in version 0.15. + +Cross-linking used to include any Markdown heading, but now it's only for *mkdocstrings* identifiers by default. See [Cross-references to any Markdown heading](../usage/#cross-references-to-any-markdown-heading) to opt back in. Make sure the referenced object is properly rendered: verify your configuration options. -For false-positives, you can wrap the text in backticks (\`) to prevent `mkdocstrings` from trying to process it. +For false-positives, you can wrap the text in backticks (`) to prevent `mkdocstrings\` from trying to process it. ---- +______________________________________________________________________ ## Python specifics @@ -144,15 +127,11 @@ See [Python handler: Finding modules](https://mkdocstrings.github.io/python/usag ### LaTeX in docstrings is not rendered correctly -If you are using a Markdown extension like -[Arithmatex Mathjax](https://squidfunk.github.io/mkdocs-material/setup/extensions/python-markdown-extensions/#arithmatex) -or [`markdown-katex`][markdown-katex] to render LaTeX, -add `r` in front of your docstring to make sure nothing is escaped. -You'll still maybe have to play with escaping to get things right. +If you are using a Markdown extension like [Arithmatex Mathjax](https://squidfunk.github.io/mkdocs-material/setup/extensions/python-markdown-extensions/#arithmatex) or [`markdown-katex`](https://gitlab.com/mbarkhau/markdown-katex) to render LaTeX, add `r` in front of your docstring to make sure nothing is escaped. You'll still maybe have to play with escaping to get things right. Example: -```python +````python def math_function(x, y): r""" Look at these formulas: @@ -163,7 +142,7 @@ def math_function(x, y): \,d\xi ``` """ -``` +```` ### My docstrings in comments (`#:`) are not picked up @@ -280,7 +259,7 @@ def my_function(): Note: This is the third section[^3]. - + [^3]: Some text. References at the end are part of yet another section (fourth here)[^4]. @@ -293,7 +272,9 @@ def my_function(): In previous versions of mkdocstrings-python, submodules were rendered by default. This was changed and you now need to set the following option: -```yaml title="mkdocs.yml" +mkdocs.yml + +```yaml plugins: - mkdocstrings: handlers: @@ -301,6 +282,3 @@ plugins: options: show_submodules: true ``` - -[bugtracker]: https://github.com/mkdocstrings/mkdocstrings -[markdown-katex]: https://gitlab.com/mbarkhau/markdown-katex diff --git a/usage/handlers/index.html b/usage/handlers/index.html new file mode 100644 index 00000000..c5e377b8 --- /dev/null +++ b/usage/handlers/index.html @@ -0,0 +1,97 @@ + Handlers - mkdocstrings

Handlers¤

A handler is what makes it possible to collect and render documentation for a particular language.

Available handlers¤

About the Python handlers¤

Since version 0.18, a new Python handler is available. It is based on Griffe, which is an improved version of pytkdocs.

If you want to keep using the legacy handler as long as possible, you can depend on mkdocstrings-python-legacy directly, or specify the python-legacy extra when depending on mkdocstrings:

pyproject.toml
# PEP 621 dependencies declaration
+# adapt to your dependencies manager
+[project]
+dependencies = [
+    "mkdocstrings[python-legacy]>=0.18",
+]
+

The legacy handler will continue to "work" for many releases, as long as the new handler does not cover all previous use-cases.

Migrate to the new Python handler¤

To use the new Python handler, you can depend on mkdocstrings-python directly, or specify the python extra when depending on mkdocstrings:

pyproject.toml
# PEP 621 dependencies declaration
+# adapt to your dependencies manager
+[project]
+dependencies = [
+    "mkdocstrings[python]>=0.18",
+]
+

Selection options¤

Warning

Since mkdocstrings 0.19, the YAML selection key is merged into the options key.

  • filters is implemented, and used as before.
  • members is implemented, and used as before.
  • inherited_members is implemented.
  • docstring_style is implemented, and used as before, except for the restructured-text style which is renamed sphinx. Numpy-style is now built-in, so you can stop depending on pytkdocs[numpy-style] or docstring_parser.
  • docstring_options is implemented, and used as before. Refer to the griffe documentation for the updated list of supported docstring options.
  • new_path_syntax is irrelevant now. If you were setting it to True, remove the option and replace every colon (:) in your autodoc identifiers by dots (.).

See all the handler's options.

Rendering options¤

Warning

Since mkdocstrings 0.19, the YAML rendering key is merged into the options key.

Every previous option is supported. Additional options are available:

  • separate_signature: Render the signature (or attribute value) in a code block below the heading, instead as inline code. Useful for long signatures. If Black is installed, the signature is formatted. Default: False.
  • line_length: The maximum line length to use when formatting signatures. Default: 60.
  • show_submodules: Whether to render submodules of a module when iterating on children. Default: False.
  • docstring_section_style: The style to use to render docstring sections such as attributes, parameters, etc. Available styles: "table" (default), "list" and "spacy". The SpaCy style is a poor implementation of their table style. We are open to improvements through PRs!

See all the handler's options.

Templates¤

Templates are mostly the same as before, but the file layout has changed, as well as some file names. See the documentation about the Python handler templates.

Custom handlers¤

Since version 0.14, you can create and use custom handlers thanks to namespace packages. For more information about namespace packages, see their documentation.

TL;DR - Project template for handlers.

mkdocstrings provides a Copier template to kickstart new handlers: https://github.com/mkdocstrings/handler-template. To use it, install Copier (pipx install copier), then run copier gh:mkdocstrings/handler-template my_handler to generate a new project. See its upstream documentation to learn how to work on the generated project.

Packaging¤

For mkdocstrings, a custom handler package would have the following structure:

📁 your_repository
+└─╴📁 mkdocstrings_handlers
+   └─╴📁 custom_handler
+      ├─╴📁 templates
+      │  ├─╴📁 material
+      │  ├─╴📁 mkdocs
+      │  └─╴📁 readthedocs
+      └─╴📄 __init__.py
+

Note the absence of __init__.py module in mkdocstrings_handlers!

Code¤

A handler is a subclass of the base handler provided by mkdocstrings. See the documentation for the BaseHandler.

Subclasses of the base handler must declare a name and domain as class attributes, as well as implement the following methods:

  • collect(identifier, options) (required): method responsible for collecting and returning data (extracting documentation from source code, loading introspecting objects in memory, other sources? etc.)
  • render(identifier, options) (required): method responsible for actually rendering the data to HTML, using the Jinja templates provided by your package.
  • get_options(local_options) (required): method responsible for combining global options with local ones.
  • get_aliases(identifier) (recommended): method responsible for returning known aliases of object identifiers, in order to register cross-references in the autorefs plugin.
  • get_inventory_urls() (optional): method responsible for returning a list of URLs to download (object inventories) along with configuration options (for loading the inventory with load_inventory).
  • load_inventory(in_file, url, **options) (optional): method responsible for loading an inventory (binary file-handle) and yielding tuples of identifiers and URLs.
  • update_env(config) (optional): Gives you a chance to customize the Jinja environment used to render templates, for examples by adding/removing Jinja filters and global context variables.
  • teardown() (optional): Clean up / teardown anything that needs it at the end of the build.

You must implement a get_handler method at the module level, which returns an instance of your handler. This function takes the following parameters:

  • theme (string, theme name)
  • custom_templates (optional string, path to custom templates directory)
  • mdx (list, Markdown extensions)
  • mdx_config (dict, extensions configuration)
  • handler_config (dict, handle configuration)
  • tool_config (dict, the whole MkDocs configuration)

These arguments are all passed as keyword arguments, so you can ignore them by adding **kwargs or similar to your signature.

You should not modify the MkDocs config but can use it to get information about the MkDocs instance such as where the current site_dir lives. See the Mkdocs Configuration for more info about what is accessible from it.

Check out how the Python handler is written for inspiration.

Templates¤

Your handler's implementation should normally be backed by templates, which go to the directory mkdocstrings_handlers/custom_handler/templates/some_theme (custom_handler here should be replaced with the actual name of your handler, and some_theme should be the name of an actual MkDocs theme that you support, e.g. material).

With that structure, you can use self.env.get_template("foo.html") inside your render method. This already chooses the subdirectory based on the current MkDocs theme.

If you wish to support any MkDocs theme, rather than a few specifically selected ones, you can pick one theme's subdirectory to be the fallback for when an unknown theme is encountered. Then you just need to set the fallback_theme variable on your handler subclass. The fallback directory can be used even for themes you explicitly support: you can omit some template from one of the other theme directories in case they're exactly the same as in the fallback theme.

If your theme's HTML requires CSS to go along with it, put it into a file named mkdocstrings_handlers/custom_handler/templates/some_theme/style.css, then this will be included into the final site automatically if this handler is ever used. Alternatively, you can put the CSS as a string into the extra_css variable of your handler.

Finally, it's possible to entirely omit templates, and tell mkdocstrings to use the templates of another handler. In you handler, override the get_templates_dir() method to return the other handlers templates path:

from pathlib import Path
+from mkdocstrings.handlers.base import BaseHandler
+
+
+class CobraHandler(BaseHandler):
+    def get_templates_dir(self, handler: str | None = None) -> Path:
+        # use the python handler templates
+        # (it assumes the python handler is installed)
+        return super().get_templates_dir("python")
+

Usage¤

When a custom handler is installed, it is then available to mkdocstrings. You can configure it as usual:

mkdocs.yml
plugins:
+- mkdocstrings:
+    handlers:
+      custom_handler:
+        handler_config_option: yes
+        options:
+          some_config_option: "a"
+          other_config_option: 0
+

...and use it in your autodoc instructions:

docs/some_page.md
# Documentation for an object
+
+::: some.objects.path
+    handler: custom_handler
+    options:
+      some_config_option: "b"
+      other_config_option: 1
+

Handler extensions¤

mkdocstrings provides a way for third-party packages to extend or alter the behavior of handlers. For example, an extension of the Python handler could add specific support for another Python library.

Note

This feature is intended for developers. If you are a user and want to customize how objects are rendered, see Theming / Customization.

Such extensions can register additional template folders that will be used when rendering collected data. Extensions are responsible for synchronizing with the handler itself so that it uses the additional templates.

An extension is a Python package that defines an entry-point for a specific handler:

pyproject.toml
[project.entry-points."mkdocstrings.python.templates"] # (1)!
+extension-name = "extension_package:get_templates_path" # (2)!
+
  1. Replace python by the name of the handler you want to add templates to.
  2. Replace extension-name by any name you want, and replace extension_package:get_templates_path by the actual module path and function name in your package.

This entry-point assumes that the extension provides a get_templates_path function directly under the extension_package package:

 pyproject.toml
+📁 extension_package/
+├──  __init__.py
+└── 📁 templates/
+
extension_package/__init__.py
from pathlib import Path
+
+
+def get_templates_path() -> Path:
+    return Path(__file__).parent / "templates"
+

This function doesn't accept any argument and returns the path (pathlib.Path or str) to a directory containing templates. The directory must contain one subfolder for each supported theme, even if empty (see "fallback theme" in custom handlers templates). For example:

 pyproject.toml
+📁 extension_package/
+├──  __init__.py
+└── 📁 templates/
+    ├── 📁 material/
+    ├── 📁 readthedocs/
+    └── 📁 mkdocs/
+

mkdocstrings will add the folders corresponding to the user-selected theme, and to the handler's defined fallback theme, as usual.

The names of the extension templates must not overlap with the handler's original templates.

The extension is then responsible, in collaboration with its target handler, for mutating the collected data in order to instruct the handler to use one of the extension template when rendering particular objects. See each handler's docs to see if they support extensions, and how.

\ No newline at end of file diff --git a/usage/handlers/index.md b/usage/handlers/index.md new file mode 100644 index 00000000..adc939ce --- /dev/null +++ b/usage/handlers/index.md @@ -0,0 +1,252 @@ +# Handlers + +A handler is what makes it possible to collect and render documentation for a particular language. + +## Available handlers + +- [C](https://mkdocstrings.github.io/c/) +- [Crystal](https://mkdocstrings.github.io/crystal/) +- [GitHub Actions](https://watermarkhu.nl/mkdocstrings-github/) +- [Python](https://mkdocstrings.github.io/python/) +- [Python (Legacy)](https://mkdocstrings.github.io/python-legacy/) +- [MATLAB](https://watermarkhu.nl/mkdocstrings-matlab/) +- [Shell](https://mkdocstrings.github.io/shell/) +- [TypeScript](https://mkdocstrings.github.io/typescript/) +- [VBA](https://pypi.org/project/mkdocstrings-vba/) + +## About the Python handlers + +Since version 0.18, a new Python handler is available. It is based on [Griffe](https://github.com/mkdocstrings/griffe), which is an improved version of [pytkdocs](https://github.com/mkdocstrings/pytkdocs). + +If you want to keep using the legacy handler as long as possible, you can depend on `mkdocstrings-python-legacy` directly, or specify the `python-legacy` extra when depending on *mkdocstrings*: + +pyproject.toml + +```toml +# PEP 621 dependencies declaration +# adapt to your dependencies manager +[project] +dependencies = [ + "mkdocstrings[python-legacy]>=0.18", +] +``` + +The legacy handler will continue to "work" for many releases, as long as the new handler does not cover all previous use-cases. + +### Migrate to the new Python handler + +To use the new Python handler, you can depend on `mkdocstrings-python` directly, or specify the `python` extra when depending on *mkdocstrings*: + +pyproject.toml + +```toml +# PEP 621 dependencies declaration +# adapt to your dependencies manager +[project] +dependencies = [ + "mkdocstrings[python]>=0.18", +] +``` + +#### Selection options + +Warning + +Since *mkdocstrings* 0.19, the YAML `selection` key is merged into the `options` key. + +- `filters` is implemented, and used as before. +- `members` is implemented, and used as before. +- `inherited_members` is implemented. +- `docstring_style` is implemented, and used as before, except for the `restructured-text` style which is renamed `sphinx`. Numpy-style is now built-in, so you can stop depending on `pytkdocs[numpy-style]` or `docstring_parser`. +- `docstring_options` is implemented, and used as before. Refer to the [`griffe` documentation](https://mkdocstrings.github.io/griffe/docstrings/#parsing-options) for the updated list of supported docstring options. +- `new_path_syntax` is irrelevant now. If you were setting it to True, remove the option and replace every colon (`:`) in your autodoc identifiers by dots (`.`). + +See [all the handler's options](https://mkdocstrings.github.io/python/usage/). + +#### Rendering options + +Warning + +Since *mkdocstrings* 0.19, the YAML `rendering` key is merged into the `options` key. + +Every previous option is supported. Additional options are available: + +- `separate_signature`: Render the signature (or attribute value) in a code block below the heading, instead as inline code. Useful for long signatures. If Black is installed, the signature is formatted. Default: `False`. +- `line_length`: The maximum line length to use when formatting signatures. Default: `60`. +- `show_submodules`: Whether to render submodules of a module when iterating on children. Default: `False`. +- `docstring_section_style`: The style to use to render docstring sections such as attributes, parameters, etc. Available styles: `"table"` (default), `"list"` and `"spacy"`. The SpaCy style is a poor implementation of their [table style](https://spacy.io/api/doc/#init). We are open to improvements through PRs! + +See [all the handler's options](https://mkdocstrings.github.io/python/usage/). + +#### Templates + +Templates are mostly the same as before, but the file layout has changed, as well as some file names. See [the documentation about the Python handler templates](https://mkdocstrings.github.io/python/usage/customization/#templates). + +## Custom handlers + +Since version 0.14, you can create and use custom handlers thanks to namespace packages. For more information about namespace packages, [see their documentation](https://packaging.python.org/guides/packaging-namespace-packages/). + +TL;DR - Project template for handlers. + +*mkdocstrings* provides a [Copier](https://github.com/copier-org/copier) template to kickstart new handlers: . To use it, install Copier (`pipx install copier`), then run `copier gh:mkdocstrings/handler-template my_handler` to generate a new project. See [its upstream documentation](https://pawamoy.github.io/copier-pdm/) to learn how to work on the generated project. + +### Packaging + +For *mkdocstrings*, a custom handler package would have the following structure: + +```text +📁 your_repository +└─╴📁 mkdocstrings_handlers + └─╴📁 custom_handler + ├─╴📁 templates + │  ├─╴📁 material + │ ├─╴📁 mkdocs + │ └─╴📁 readthedocs + └─╴📄 __init__.py +``` + +Note the absence of `__init__.py` module in `mkdocstrings_handlers`! + +### Code + +A handler is a subclass of the base handler provided by *mkdocstrings*. See the documentation for the BaseHandler. + +Subclasses of the base handler must declare a `name` and `domain` as class attributes, as well as implement the following methods: + +- `collect(identifier, options)` (**required**): method responsible for collecting and returning data (extracting documentation from source code, loading introspecting objects in memory, other sources? etc.) +- `render(identifier, options)` (**required**): method responsible for actually rendering the data to HTML, using the Jinja templates provided by your package. +- `get_options(local_options)` (**required**): method responsible for combining global options with local ones. +- `get_aliases(identifier)` (**recommended**): method responsible for returning known aliases of object identifiers, in order to register cross-references in the autorefs plugin. +- `get_inventory_urls()` (optional): method responsible for returning a list of URLs to download (object inventories) along with configuration options (for loading the inventory with `load_inventory`). +- `load_inventory(in_file, url, **options)` (optional): method responsible for loading an inventory (binary file-handle) and yielding tuples of identifiers and URLs. +- `update_env(config)` (optional): Gives you a chance to customize the Jinja environment used to render templates, for examples by adding/removing Jinja filters and global context variables. +- `teardown()` (optional): Clean up / teardown anything that needs it at the end of the build. + +You must implement a `get_handler` method at the module level, which returns an instance of your handler. This function takes the following parameters: + +- `theme` (string, theme name) +- `custom_templates` (optional string, path to custom templates directory) +- `mdx` (list, Markdown extensions) +- `mdx_config` (dict, extensions configuration) +- `handler_config` (dict, handle configuration) +- `tool_config` (dict, the whole MkDocs configuration) + +These arguments are all passed as keyword arguments, so you can ignore them by adding `**kwargs` or similar to your signature. + +You should not modify the MkDocs config but can use it to get information about the MkDocs instance such as where the current `site_dir` lives. See the [Mkdocs Configuration](https://www.mkdocs.org/user-guide/configuration/) for more info about what is accessible from it. + +Check out how the [Python handler](https://github.com/mkdocstrings/python/blob/master/src/mkdocstrings_handlers/python) is written for inspiration. + +### Templates + +Your handler's implementation should normally be backed by templates, which go to the directory `mkdocstrings_handlers/custom_handler/templates/some_theme` (`custom_handler` here should be replaced with the actual name of your handler, and `some_theme` should be the name of an actual MkDocs theme that you support, e.g. `material`). + +With that structure, you can use `self.env.get_template("foo.html")` inside your `render` method. This already chooses the subdirectory based on the current MkDocs theme. + +If you wish to support *any* MkDocs theme, rather than a few specifically selected ones, you can pick one theme's subdirectory to be the fallback for when an unknown theme is encountered. Then you just need to set the `fallback_theme` variable on your handler subclass. The fallback directory can be used even for themes you explicitly support: you can omit some template from one of the other theme directories in case they're exactly the same as in the fallback theme. + +If your theme's HTML requires CSS to go along with it, put it into a file named `mkdocstrings_handlers/custom_handler/templates/some_theme/style.css`, then this will be included into the final site automatically if this handler is ever used. Alternatively, you can put the CSS as a string into the `extra_css` variable of your handler. + +Finally, it's possible to entirely omit templates, and tell *mkdocstrings* to use the templates of another handler. In you handler, override the `get_templates_dir()` method to return the other handlers templates path: + +```python +from pathlib import Path +from mkdocstrings.handlers.base import BaseHandler + + +class CobraHandler(BaseHandler): + def get_templates_dir(self, handler: str | None = None) -> Path: + # use the python handler templates + # (it assumes the python handler is installed) + return super().get_templates_dir("python") +``` + +### Usage + +When a custom handler is installed, it is then available to *mkdocstrings*. You can configure it as usual: + +mkdocs.yml + +```yaml +plugins: +- mkdocstrings: + handlers: + custom_handler: + handler_config_option: yes + options: + some_config_option: "a" + other_config_option: 0 +``` + +...and use it in your autodoc instructions: + +docs/some_page.md + +```md +# Documentation for an object + +::: some.objects.path + handler: custom_handler + options: + some_config_option: "b" + other_config_option: 1 +``` + +## Handler extensions + +*mkdocstrings* provides a way for third-party packages to extend or alter the behavior of handlers. For example, an extension of the Python handler could add specific support for another Python library. + +Note + +This feature is intended for developers. If you are a user and want to customize how objects are rendered, see [Theming / Customization](../theming/#customization). + +Such extensions can register additional template folders that will be used when rendering collected data. Extensions are responsible for synchronizing with the handler itself so that it uses the additional templates. + +An extension is a Python package that defines an entry-point for a specific handler: + +pyproject.toml + +```toml +[project.entry-points."mkdocstrings.python.templates"] # (1)! +extension-name = "extension_package:get_templates_path" # (2)! +``` + +1. Replace `python` by the name of the handler you want to add templates to. +1. Replace `extension-name` by any name you want, and replace `extension_package:get_templates_path` by the actual module path and function name in your package. + +This entry-point assumes that the extension provides a `get_templates_path` function directly under the `extension_package` package: + +```bash + pyproject.toml +📁 extension_package/ +├── __init__.py +└── 📁 templates/ +``` + +extension_package/__init__.py + +```python +from pathlib import Path + + +def get_templates_path() -> Path: + return Path(__file__).parent / "templates" +``` + +This function doesn't accept any argument and returns the path (pathlib.Path or str) to a directory containing templates. The directory must contain one subfolder for each supported theme, even if empty (see "fallback theme" in [custom handlers templates](#templates_1)). For example: + +```bash + pyproject.toml +📁 extension_package/ +├── __init__.py +└── 📁 templates/ + ├── 📁 material/ + ├── 📁 readthedocs/ + └── 📁 mkdocs/ +``` + +*mkdocstrings* will add the folders corresponding to the user-selected theme, and to the handler's defined fallback theme, as usual. + +The names of the extension templates must not overlap with the handler's original templates. + +The extension is then responsible, in collaboration with its target handler, for mutating the collected data in order to instruct the handler to use one of the extension template when rendering particular objects. See each handler's docs to see if they support extensions, and how. diff --git a/usage/index.html b/usage/index.html new file mode 100644 index 00000000..1e01da02 --- /dev/null +++ b/usage/index.html @@ -0,0 +1,143 @@ + Usage - mkdocstrings

Usage¤

Autodoc syntax¤

mkdocstrings works by processing special expressions in your Markdown files.

The syntax is as follows:

::: identifier
+    YAML block
+

Resources on YAML.

YAML can sometimes be a bit tricky, particularly on indentation. Here are some resources that other users found useful to better understand YAML's peculiarities.

The identifier is a string identifying the object you want to document. The format of an identifier can vary from one handler to another. For example, the Python handler expects the full dotted-path to a Python object: my_package.my_module.MyClass.my_method.

The YAML block is optional, and contains some configuration options:

  • handler: the name of the handler to use to collect and render this object. By default, it will use the value defined in the Global options's default_handler key, or "python".
  • options: a dictionary of options passed to the handler's methods responsible both for collecting and rendering the documentation. These options can be defined globally (in mkdocs.yml, see Global options), locally (as described here), or both.

Example with the Python handler

# Documentation for `MyClass`
+
+::: my_package.my_module.MyClass
+    handler: python
+    options:
+      members:
+        - method_a
+        - method_b
+      show_root_heading: false
+      show_source: false
+
nav:
+  - "My page": my_page.md
+
class MyClass:
+    """Print print print!"""
+
+    def method_a(self):
+        """Print A!"""
+        print("A!")
+
+    def method_b(self):
+        """Print B!"""
+        print("B!")
+
+    def method_c(self):
+        """Print C!"""
+        print("C!")
+

Documentation for MyClass

Print print print!

method_a(self)

Print A!

method_b(self)

Print B!

It is also possible to integrate a mkdocstrings identifier into a Markdown header:

## ::: my_package.my_module.MyClass
+    options:
+      show_source: false
+

The above is equivalent to:

::: my_package.my_module.MyClass
+    options:
+      show_source: false
+      heading_level: 2
+

Global options¤

mkdocstrings accepts a few top-level configuration options in mkdocs.yml:

  • default_handler: The handler that is used by default when no handler is specified.
  • custom_templates: The path to a directory containing custom templates. The path is relative to the MkDocs configuration file. See Theming.
  • handlers: The handlers' global configuration.
  • enable_inventory: Whether to enable inventory file generation. See Cross-references to other projects / inventories
  • locale: The locale used for translations. See Internationalization.
  • enabled (New in version 0.20): Whether to enable the plugin. Defaults to true. Can be used to reduce build times when doing local development. Especially useful when used with environment variables (see example below).

Example

mkdocs.yml
plugins:
+- mkdocstrings:
+    enabled: !ENV [ENABLE_MKDOCSTRINGS, true]
+    custom_templates: templates
+    default_handler: python
+    locale: en
+    handlers:
+      python:
+        options:
+          show_source: false
+

The handlers global configuration can then be overridden by local configurations:

docs/some_page.md
::: my_package.my_module.MyClass
+    options:
+      show_source: true
+

Some handlers accept additional global configuration. Check the documentation for your handler of interest in Handlers.

Internationalization (I18N)¤

Some handlers support multiple languages.

If the handler supports localization, the locale it uses is determined by the following order of precedence:

Cross-references¤

Cross-references are written as Markdown reference-style links:

With a custom title:
+[`Object 1`][full.path.object1]
+
+With the identifier as title:
+[full.path.object2][]
+
<p>With a custom title:
+<a href="https://example.com/page1#full.path.object1"><code>Object 1</code></a><p>
+<p>With the identifier as title:
+<a href="https://example.com/page2#full.path.object2">full.path.object2</a></p>
+

Any item that was inserted using the autodoc syntax (e.g. ::: full.path.object1) is possible to link to by using the same identifier with the cross-reference syntax ([example][full.path.object1]). But the cross-references are also applicable to the items' children that get pulled in.

Finding out the anchor¤

If you're not sure which exact identifier a doc item uses, you can look at its "anchor", which your Web browser will show in the URL bar when clicking an item's entry in the table of contents. If the URL is https://example.com/some/page.html#full.path.object1 then you know that this item is possible to link to with [example][full.path.object1], regardless of the current page.

Cross-references to any Markdown heading¤

Changed in version 0.15.

Linking to any Markdown heading used to be the default, but now opt-in is required.

If you want to link to any Markdown heading, not just mkdocstrings-inserted items, please enable the autorefs plugin for MkDocs by adding autorefs to plugins:

mkdocs.yml
plugins:
+- search
+- autorefs
+- mkdocstrings:
+    [...]
+

Note that you don't need to (pip) install anything more; this plugin is guaranteed to be pulled in with mkdocstrings.

Example

## Hello, world!
+
+Testing
+
## Something else
+
+Please see the [Hello, World!][hello-world] section.
+
<p>Please see the <a href="doc1.html#hello-world">Hello, World!</a> section.</p>
+

Cross-references to a sub-heading in a docstring¤

New in version 0.14.

If you have a Markdown heading inside your docstring, you can also link directly to it. In the example below you see the identifier to be linked is foo.bar--tips, because it's the "Tips" heading that's part of the foo.bar object, joined with "--".

Example

def bar():
+    """Hello, world!
+
+    # Tips
+
+    - Stay hydrated.
+    """
+
::: foo.bar
+
Check out the [tips][foo.bar--tips]
+
<p>Check out the <a href="doc1.html#foo.bar--tips">tips</a></p>
+

The above tip about Finding out the anchor also applies the same way here.

You may also notice that such a heading does not get rendered as a <h1> element directly, but rather the level gets shifted to fit the encompassing document structure. If you're curious about the implementation, check out mkdocstrings.HeadingShiftingTreeprocessor and others.

Cross-references to other projects / inventories¤

New in version 0.16.

Python developers coming from Sphinx might know about its intersphinx extension, that allows to cross-reference items between several projects. mkdocstrings has a similar feature.

To reference an item from another project, you must first tell mkdocstrings to load the inventory it provides. Each handler will be responsible of loading inventories specific to its language. For example, the Python handler can load Sphinx-generated inventories (objects.inv).

In the following snippet, we load the inventory provided by installer:

mkdocs.yml
plugins:
+- mkdocstrings:
+    handlers:
+      python:
+        inventories:
+        - https://installer.readthedocs.io/en/stable/objects.inv
+

Now it is possible to cross-reference installer's items. For example:

See [installer.records][] to learn about records.
+
<p>See <a href="https://installer.readthedocs.io/en/stable/api/records/#module-installer.records">installer.records</a>
+to learn about records.</p>
+

See installer.records to learn about records.

You can of course select another version of the inventory, for example:

plugins:
+- mkdocstrings:
+    handlers:
+      python:
+        inventories:
+        # latest instead of stable
+        - https://installer.readthedocs.io/en/latest/objects.inv
+

In case the inventory file is not served under the base documentation URL, you can explicitly specify both URLs:

plugins:
+- mkdocstrings:
+    handlers:
+      python:
+        inventories:
+        - url: https://cdn.example.com/version/objects.inv
+          base_url: https://docs.example.com/version
+

Absolute URLs to cross-referenced items will then be based on https://docs.example.com/version/ instead of https://cdn.example.com/version/.

If you need authentication to access the inventory file, you can provide the credentials in the URL, either as username:password:

- url: https://username:password@private.example.com/version/objects.inv
+

...or with token authentication:

- url: https://token123@private.example.com/version/objects.inv
+

The credentials can also be specified using environment variables in the form ${ENV_VAR}:

- url: https://${USERNAME}:${PASSWORD}@private.example.com/version/objects.inv
+

Reciprocally, mkdocstrings also allows to generate an inventory file in the Sphinx format. It will be enabled by default if the Python handler is used, and generated as objects.inv in the final site directory. Other projects will be able to cross-reference items from your project.

To explicitly enable or disable the generation of the inventory file, use the global enable_inventory option:

plugins:
+- mkdocstrings:
+    enable_inventory: false
+
\ No newline at end of file diff --git a/usage/index.md b/usage/index.md new file mode 100644 index 00000000..cc48734f --- /dev/null +++ b/usage/index.md @@ -0,0 +1,327 @@ +# Usage + +## Autodoc syntax + +*mkdocstrings* works by processing special expressions in your Markdown files. + +The syntax is as follows: + +```md +::: identifier + YAML block +``` + +Resources on YAML. + +YAML can sometimes be a bit tricky, particularly on indentation. Here are some resources that other users found useful to better understand YAML's peculiarities. + +- [YAML idiosyncrasies](https://salt-zh.readthedocs.io/en/latest/topics/troubleshooting/yaml_idiosyncrasies.html) +- [YAML multiline](https://yaml-multiline.info/) + +The `identifier` is a string identifying the object you want to document. The format of an identifier can vary from one handler to another. For example, the Python handler expects the full dotted-path to a Python object: `my_package.my_module.MyClass.my_method`. + +The YAML block is optional, and contains some configuration options: + +- `handler`: the name of the handler to use to collect and render this object. By default, it will use the value defined in the [Global options](#global-options)'s `default_handler` key, or `"python"`. +- `options`: a dictionary of options passed to the handler's methods responsible both for collecting and rendering the documentation. These options can be defined globally (in `mkdocs.yml`, see [Global options](#global-options)), locally (as described here), or both. + +Example with the Python handler + +```md +# Documentation for `MyClass` + +::: my_package.my_module.MyClass + handler: python + options: + members: + - method_a + - method_b + show_root_heading: false + show_source: false +``` + +```yaml +nav: + - "My page": my_page.md +``` + +```python +class MyClass: + """Print print print!""" + + def method_a(self): + """Print A!""" + print("A!") + + def method_b(self): + """Print B!""" + print("B!") + + def method_c(self): + """Print C!""" + print("C!") +``` + +### Documentation for `MyClass` + +Print print print! + +#### `method_a(self)` + +Print A! + +#### `method_b(self)` + +Print B! + +It is also possible to integrate a mkdocstrings identifier into a Markdown header: + +```md +## ::: my_package.my_module.MyClass + options: + show_source: false +``` + +The above is equivalent to: + +```md +::: my_package.my_module.MyClass + options: + show_source: false + heading_level: 2 +``` + +## Global options + +*mkdocstrings* accepts a few top-level configuration options in `mkdocs.yml`: + +- `default_handler`: The handler that is used by default when no handler is specified. +- `custom_templates`: The path to a directory containing custom templates. The path is relative to the MkDocs configuration file. See [Theming](theming/). +- `handlers`: The handlers' global configuration. +- `enable_inventory`: Whether to enable inventory file generation. See [Cross-references to other projects / inventories](#cross-references-to-other-projects-inventories) +- `locale`: The locale used for translations. See [Internationalization](#internationalization-i18n). +- `enabled` **(New in version 0.20)**: Whether to enable the plugin. Defaults to `true`. Can be used to reduce build times when doing local development. Especially useful when used with environment variables (see example below). + +Example + +mkdocs.yml + +```yaml +plugins: +- mkdocstrings: + enabled: !ENV [ENABLE_MKDOCSTRINGS, true] + custom_templates: templates + default_handler: python + locale: en + handlers: + python: + options: + show_source: false +``` + +The handlers global configuration can then be overridden by local configurations: + +docs/some_page.md + +```yaml +::: my_package.my_module.MyClass + options: + show_source: true +``` + +Some handlers accept additional global configuration. Check the documentation for your handler of interest in [Handlers](handlers/). + +## Internationalization (I18N) + +Some handlers support multiple languages. + +If the handler supports localization, the locale it uses is determined by the following order of precedence: + +- `locale` in [global options](#global-options) +- `theme.language`: used by the [MkDocs Material theme](https://squidfunk.github.io/mkdocs-material/setup/changing-the-language/) +- `theme.locale` in [MkDocs configuration](https://www.mkdocs.org/user-guide/configuration/#theme) + +## Cross-references + +Cross-references are written as Markdown *reference-style* links: + +```md +With a custom title: +[`Object 1`][full.path.object1] + +With the identifier as title: +[full.path.object2][] +``` + +```html +

With a custom title: +Object 1

+

With the identifier as title: +full.path.object2

+``` + +Any item that was inserted using the [autodoc syntax](#autodoc-syntax) (e.g. `::: full.path.object1`) is possible to link to by using the same identifier with the cross-reference syntax (`[example][full.path.object1]`). But the cross-references are also applicable to the items' children that get pulled in. + +### Finding out the anchor + +If you're not sure which exact identifier a doc item uses, you can look at its "anchor", which your Web browser will show in the URL bar when clicking an item's entry in the table of contents. If the URL is `https://example.com/some/page.html#full.path.object1` then you know that this item is possible to link to with `[example][full.path.object1]`, regardless of the current page. + +### Cross-references to any Markdown heading + +Changed in version 0.15. + +Linking to any Markdown heading used to be the default, but now opt-in is required. + +If you want to link to *any* Markdown heading, not just *mkdocstrings*-inserted items, please enable the [*autorefs* plugin for *MkDocs*](https://github.com/mkdocstrings/autorefs) by adding `autorefs` to `plugins`: + +mkdocs.yml + +```yaml +plugins: +- search +- autorefs +- mkdocstrings: + [...] +``` + +Note that you don't need to (`pip`) install anything more; this plugin is guaranteed to be pulled in with *mkdocstrings*. + +Example + +```md +## Hello, world! + +Testing +``` + +```md +## Something else + +Please see the [Hello, World!][hello-world] section. +``` + +```html +

Please see the Hello, World! section.

+``` + +### Cross-references to a sub-heading in a docstring + +New in version 0.14. + +If you have a Markdown heading *inside* your docstring, you can also link directly to it. In the example below you see the identifier to be linked is `foo.bar--tips`, because it's the "Tips" heading that's part of the `foo.bar` object, joined with "`--`". + +Example + +```python +def bar(): + """Hello, world! + + # Tips + + - Stay hydrated. + """ +``` + +```md +::: foo.bar +``` + +```md +Check out the [tips][foo.bar--tips] +``` + +```html +

Check out the tips

+``` + +The above tip about [Finding out the anchor](#finding-out-the-anchor) also applies the same way here. + +You may also notice that such a heading does not get rendered as a `

` element directly, but rather the level gets shifted to fit the encompassing document structure. If you're curious about the implementation, check out mkdocstrings.HeadingShiftingTreeprocessor and others. + +### Cross-references to other projects / inventories + +New in version 0.16. + +Python developers coming from Sphinx might know about its `intersphinx` extension, that allows to cross-reference items between several projects. *mkdocstrings* has a similar feature. + +To reference an item from another project, you must first tell *mkdocstrings* to load the inventory it provides. Each handler will be responsible of loading inventories specific to its language. For example, the Python handler can load Sphinx-generated inventories (`objects.inv`). + +In the following snippet, we load the inventory provided by `installer`: + +mkdocs.yml + +```yaml +plugins: +- mkdocstrings: + handlers: + python: + inventories: + - https://installer.readthedocs.io/en/stable/objects.inv +``` + +Now it is possible to cross-reference `installer`'s items. For example: + +```md +See [installer.records][] to learn about records. +``` + +```html +

See installer.records +to learn about records.

+``` + +See installer.records to learn about records. + +You can of course select another version of the inventory, for example: + +```yaml +plugins: +- mkdocstrings: + handlers: + python: + inventories: + # latest instead of stable + - https://installer.readthedocs.io/en/latest/objects.inv +``` + +In case the inventory file is not served under the base documentation URL, you can explicitly specify both URLs: + +```yaml +plugins: +- mkdocstrings: + handlers: + python: + inventories: + - url: https://cdn.example.com/version/objects.inv + base_url: https://docs.example.com/version +``` + +Absolute URLs to cross-referenced items will then be based on `https://docs.example.com/version/` instead of `https://cdn.example.com/version/`. + +If you need authentication to access the inventory file, you can provide the credentials in the URL, either as `username:password`: + +```yaml +- url: https://username:password@private.example.com/version/objects.inv +``` + +...or with token authentication: + +```yaml +- url: https://token123@private.example.com/version/objects.inv +``` + +The credentials can also be specified using environment variables in the form `${ENV_VAR}`: + +```yaml +- url: https://${USERNAME}:${PASSWORD}@private.example.com/version/objects.inv +``` + +Reciprocally, *mkdocstrings* also allows to *generate* an inventory file in the Sphinx format. It will be enabled by default if the Python handler is used, and generated as `objects.inv` in the final site directory. Other projects will be able to cross-reference items from your project. + +To explicitly enable or disable the generation of the inventory file, use the global `enable_inventory` option: + +```yaml +plugins: +- mkdocstrings: + enable_inventory: false +``` diff --git a/usage/theming/index.html b/usage/theming/index.html new file mode 100644 index 00000000..d5109bc3 --- /dev/null +++ b/usage/theming/index.html @@ -0,0 +1,55 @@ + Theming - mkdocstrings

Themes¤

mkdocstrings can support multiple MkDocs themes. It currently supports the Material for MkDocs theme and, partially, the built-in MkDocs and ReadTheDocs themes.

Each handler can fallback to a particular theme when the user selected theme is not supported. For example, the Python handler will fallback to the Material for MkDocs templates.

Customization¤

There is some degree of customization possible in mkdocstrings. First, you can write custom templates to override the theme templates. Second, the provided templates make use of CSS classes, so you can tweak the look and feel with extra CSS rules.

Templates¤

To use custom templates and override the theme ones, specify the relative path from your configuration file to your templates directory with the custom_templates global configuration option:

mkdocs.yml
plugins:
+- mkdocstrings:
+    custom_templates: templates
+

Your directory structure must be identical to the provided templates one:

📁 templates/
+├─╴📁 <HANDLER 1>/
+│   ├── 📁 <THEME 1>/
+│   └── 📁 <THEME 2>/
+└── 📁 <HANDLER 2>/
+    ├── 📁 <THEME 1>/
+    └── 📁 <THEME 2>/
+

For example, check out the Python template tree on GitHub.

You don't have to replicate the whole tree, only the handlers, themes or templates you want to override. For example, to override some templates of the Material theme for Python:

📁 templates/
+└── 📁 python/
+    └── 📁 material/
+        ├── 📄 parameters.html
+        └── 📄 exceptions.html
+

In the HTML files, replace the original contents with your modified version. In the future, the templates will use Jinja blocks, so it will be easier to modify small part of the templates without copy-pasting the whole files.

See the documentation about templates for:

Debugging¤

Every template has access to a log function, allowing to log messages as usual:

{{ log.debug("A DEBUG message.") }}
+{{ log.info("An INFO message.") }}
+{{ log.warning("A WARNING message.") }}
+{{ log.error("An ERROR message.") }}
+{{ log.critical("A CRITICAL message.") }}
+

CSS classes¤

Since each handler provides its own set of templates, with their own CSS classes, we cannot list them all here. See the documentation about CSS classes for:

Syntax highlighting¤

Code blocks that occur in the docstring of an item inserted with mkdocstrings, as well as code blocks (such as Source code) that mkdocstrings inserts itself, are syntax-highlighted according to the same rules as other normal code blocks in your document. See more details in mkdocstrings.Highlighter.

As for the CSS class used for code blocks -- it will also match the "normal" config, so the default (.codehilite or .highlight) will match your chosen Markdown extension for highlighting.

Changed in version 0.15.

The CSS class used to always be .highlight, but now it depends on the configuration.

Long story short, you probably should add pymdownx.highlight to your markdown_extensions, and then use .doc-contents .highlight as the CSS selector in case you want to change something about mkdocstrings' code blocks specifically.

\ No newline at end of file diff --git a/docs/usage/theming.md b/usage/theming/index.md similarity index 54% rename from docs/usage/theming.md rename to usage/theming/index.md index 04d053f6..b2bf7726 100644 --- a/docs/usage/theming.md +++ b/usage/theming/index.md @@ -1,27 +1,20 @@ # Themes -*mkdocstrings* can support multiple MkDocs themes. -It currently supports the -*[Material for MkDocs](https://squidfunk.github.io/mkdocs-material/)* -theme and, partially, the built-in MkDocs and ReadTheDocs themes. +*mkdocstrings* can support multiple MkDocs themes. It currently supports the *[Material for MkDocs](https://squidfunk.github.io/mkdocs-material/)* theme and, partially, the built-in MkDocs and ReadTheDocs themes. -Each handler can fallback to a particular theme when the user selected theme is not supported. -For example, the Python handler will fallback to the *Material for MkDocs* templates. +Each handler can fallback to a particular theme when the user selected theme is not supported. For example, the Python handler will fallback to the *Material for MkDocs* templates. ## Customization -There is some degree of customization possible in *mkdocstrings*. -First, you can write custom templates to override the theme templates. -Second, the provided templates make use of CSS classes, -so you can tweak the look and feel with extra CSS rules. +There is some degree of customization possible in *mkdocstrings*. First, you can write custom templates to override the theme templates. Second, the provided templates make use of CSS classes, so you can tweak the look and feel with extra CSS rules. ### Templates -To use custom templates and override the theme ones, specify the relative path from your -configuration file to your templates directory with the `custom_templates` global -configuration option: +To use custom templates and override the theme ones, specify the relative path from your configuration file to your templates directory with the `custom_templates` global configuration option: -```yaml title="mkdocs.yml" +mkdocs.yml + +```yaml plugins: - mkdocstrings: custom_templates: templates @@ -29,7 +22,7 @@ plugins: Your directory structure must be identical to the provided templates one: -``` +```text 📁 templates/ ├─╴📁 / │ ├── 📁 / @@ -39,15 +32,11 @@ Your directory structure must be identical to the provided templates one: └── 📁 / ``` -For example, check out the Python -[template tree](https://github.com/mkdocstrings/python/tree/master/src/mkdocstrings_handlers/python/templates/) -on GitHub. +For example, check out the Python [template tree](https://github.com/mkdocstrings/python/tree/master/src/mkdocstrings_handlers/python/templates/) on GitHub. -You don't have to replicate the whole tree, -only the handlers, themes or templates you want to override. -For example, to override some templates of the *Material* theme for Python: +You don't have to replicate the whole tree, only the handlers, themes or templates you want to override. For example, to override some templates of the *Material* theme for Python: -``` +```text 📁 templates/ └── 📁 python/ └── 📁 material/ @@ -55,14 +44,12 @@ For example, to override some templates of the *Material* theme for Python: └── 📄 exceptions.html ``` -In the HTML files, replace the original contents with your modified version. -In the future, the templates will use Jinja blocks, so it will be easier -to modify small part of the templates without copy-pasting the whole files. +In the HTML files, replace the original contents with your modified version. In the future, the templates will use Jinja blocks, so it will be easier to modify small part of the templates without copy-pasting the whole files. See the documentation about templates for: -- the Crystal handler: https://mkdocstrings.github.io/crystal/styling.html -- the Python handler: https://mkdocstrings.github.io/python/usage/customization/#templates +- the Crystal handler: +- the Python handler: #### Debugging @@ -78,19 +65,19 @@ Every template has access to a `log` function, allowing to log messages as usual ### CSS classes -Since each handler provides its own set of templates, with their own CSS classes, -we cannot list them all here. See the documentation about CSS classes for: +Since each handler provides its own set of templates, with their own CSS classes, we cannot list them all here. See the documentation about CSS classes for: -- the Crystal handler: https://mkdocstrings.github.io/crystal/styling.html#custom-styles -- the Python handler: https://mkdocstrings.github.io/python/usage/customization/#css-classes +- the Crystal handler: +- the Python handler: ### Syntax highlighting -Code blocks that occur in the docstring of an item inserted with *mkdocstrings*, as well as code blocks (such as *Source code*) that *mkdocstrings* inserts itself, are syntax-highlighted according to the same rules as other normal code blocks in your document. See more details in [mkdocstrings.Highlighter][]. +Code blocks that occur in the docstring of an item inserted with *mkdocstrings*, as well as code blocks (such as *Source code*) that *mkdocstrings* inserts itself, are syntax-highlighted according to the same rules as other normal code blocks in your document. See more details in mkdocstrings.Highlighter. As for the CSS class used for code blocks -- it will also match the "normal" config, so the default (`.codehilite` or `.highlight`) will match your chosen Markdown extension for highlighting. -IMPORTANT: **Changed in version 0.15.** +Changed in version 0.15. + The CSS class used to always be `.highlight`, but now it depends on the configuration. Long story short, you probably should add `pymdownx.highlight` to your `markdown_extensions`, and then use `.doc-contents .highlight` as the CSS selector in case you want to change something about *mkdocstrings'* code blocks specifically. diff --git a/zensical.toml b/zensical.toml deleted file mode 100644 index d04f4896..00000000 --- a/zensical.toml +++ /dev/null @@ -1,214 +0,0 @@ -[project] -site_name = "mkdocstrings" -site_description = "Automatic documentation from sources, for MkDocs." -site_author = "Timothée Mazzucotelli" -site_url = "https://mkdocstrings.github.io/mkdocstrings" -repo_url = "https://github.com/mkdocstrings/mkdocstrings" -repo_name = "mkdocstrings/mkdocstrings" -copyright = "Copyright © 2019 Timothée Mazzucotelli" -extra_css = ["css/apidocs.css"] -extra_javascript = ["js/feedback.js"] -nav = [ - { "Home" = [ - { "Overview" = "index.md" }, - { "Changelog" = "changelog.md" }, - { "Credits" = "credits.md" }, - { "License" = "license.md" }, - ] }, - { "Usage" = [ - "usage/index.md", - { "Theming" = "usage/theming.md" }, - { "Handlers" = "usage/handlers.md" }, - { "All handlers" = [ - { "C" = "https://mkdocstrings.github.io/c/" }, - { "Crystal" = "https://mkdocstrings.github.io/crystal/" }, - { "GitHub Actions" = "https://watermarkhu.nl/mkdocstrings-github/" }, - { "Python" = "https://mkdocstrings.github.io/python/" }, - { "Python (Legacy)" = "https://mkdocstrings.github.io/python-legacy/" }, - { "MATLAB" = "https://watermarkhu.nl/mkdocstrings-matlab/" }, - { "Shell" = "https://mkdocstrings.github.io/shell/" }, - { "TypeScript" = "https://mkdocstrings.github.io/typescript/" }, - { "VBA" = "https://pypi.org/project/mkdocstrings-vba" }, - ] }, - { "Guides" = [ - { "Recipes" = "recipes.md" }, - { "Troubleshooting" = "troubleshooting.md" }, - ] }, - ] }, - { "API reference" = "reference/api.md" }, - { "Development" = [ - { "Contributing" = "contributing.md" }, - { "Code of Conduct" = "code_of_conduct.md" }, - ] }, - { "Author's website" = "https://pawamoy.github.io" }, -] - -# ---------------------------------------------------------------------------- -# Theme configuration -# ---------------------------------------------------------------------------- -[project.theme] -logo = "logo.svg" -custom_dir = "docs/.overrides" -language = "en" -features = [ - "announce.dismiss", - "content.action.edit", - "content.action.view", - "content.code.annotate", - "content.code.copy", - "content.code.select", - "content.footnote.tooltips", - "content.tabs.link", - "content.tooltips", - "navigation.footer", - "navigation.indexes", - "navigation.instant", - "navigation.instant.prefetch", - "navigation.path", - "navigation.sections", - "navigation.tabs", - "navigation.tabs.sticky", - "navigation.top", - "search.highlight", - "toc.follow", -] - -[[project.theme.palette]] -media = "(prefers-color-scheme)" -toggle.icon = "material/brightness-auto" -toggle.name = "Switch to light mode" - -[[project.theme.palette]] -media = "(prefers-color-scheme: light)" -scheme = "default" -primary = "teal" -accent = "purple" -toggle.icon = "material/weather-sunny" -toggle.name = "Switch to dark mode" - -[[project.theme.palette]] -media = "(prefers-color-scheme: dark)" -scheme = "slate" -primary = "black" -accent = "lime" -toggle.icon = "material/weather-night" -toggle.name = "Switch to system preference" - -[project.theme.icon] -logo = "material/currency-sign" - -# ---------------------------------------------------------------------------- -# Social configuration -# ---------------------------------------------------------------------------- -[[project.extra.social]] -icon = "fontawesome/brands/github" -link = "https://github.com/pawamoy" - -[[project.extra.social]] -icon = "fontawesome/brands/mastodon" -link = "https://fosstodon.org/@pawamoy" - -[[project.extra.social]] -icon = "fontawesome/brands/twitter" -link = "https://twitter.com/pawamoy" - -[[project.extra.social]] -icon = "fontawesome/brands/gitter" -link = "https://gitter.im/mkdocstrings/community" - -[[project.extra.social]] -icon = "fontawesome/brands/python" -link = "https://pypi.org/project/mkdocstrings/" - -[project.extra.analytics.feedback] -title = "Was this page helpful?" - -[[project.extra.analytics.feedback.ratings]] -icon = "material/emoticon-happy-outline" -name = "This page was helpful" -data = 1 -note = "Thank you for your feedback!" - -[[project.extra.analytics.feedback.ratings]] -icon = "material/emoticon-sad-outline" -name = "This page could be improved" -data = 0 -note = "Let us know how we can improve this page." - -# ---------------------------------------------------------------------------- -# Markdown extensions configuration -# ---------------------------------------------------------------------------- -[project.markdown_extensions.abbr] -[project.markdown_extensions.admonition] -[project.markdown_extensions.attr_list] -[project.markdown_extensions.callouts] -[project.markdown_extensions.def_list] -[project.markdown_extensions.footnotes] -[project.markdown_extensions.md_in_html] -[project.markdown_extensions.toc] -permalink = "¤" -[project.markdown_extensions.pymdownx.arithmatex] -generic = true -[project.markdown_extensions.pymdownx.betterem] -smart_enable = "all" -[project.markdown_extensions.pymdownx.caret] -[project.markdown_extensions.pymdownx.details] -[project.markdown_extensions.pymdownx.emoji] -emoji_generator = "zensical.extensions.emoji.to_svg" -emoji_index = "zensical.extensions.emoji.twemoji" -[project.markdown_extensions.pymdownx.highlight] -[project.markdown_extensions.pymdownx.inlinehilite] -[project.markdown_extensions.pymdownx.keys] -[project.markdown_extensions.pymdownx.magiclink] -[project.markdown_extensions.pymdownx.mark] -[project.markdown_extensions.pymdownx.smartsymbols] -[project.markdown_extensions.pymdownx.snippets] -check_paths = true -[project.markdown_extensions.pymdownx.superfences] -[[project.markdown_extensions.pymdownx.superfences.custom_fences]] -name = "python" -class = "python" -validator = "markdown_exec.validator" -format = "markdown_exec.formatter" -[project.markdown_extensions.pymdownx.tabbed] -alternate_style = true -[project.markdown_extensions.pymdownx.tasklist] -custom_checkbox = true -[project.markdown_extensions.pymdownx.tilde] -[project.markdown_extensions.zensical.extensions.preview] -configurations = [{targets.include = ["reference/api.md"]}] - -# ---------------------------------------------------------------------------- -# Plugins configuration -# ---------------------------------------------------------------------------- -[project.plugins.mkdocstrings.handlers.python] -paths = ["src"] -inventories = [ - "https://docs.python.org/3/objects.inv", - "https://installer.readthedocs.io/en/stable/objects.inv", # demonstration purpose in the docs - "https://mkdocstrings.github.io/autorefs/objects.inv", - "https://www.mkdocs.org/objects.inv", - "https://python-markdown.github.io/objects.inv", - "https://jinja.palletsprojects.com/en/stable/objects.inv", - "https://markupsafe.palletsprojects.com/en/stable/objects.inv", -] - -[project.plugins.mkdocstrings.handlers.python.options] -backlinks = "tree" -docstring_options = { "ignore_init_summary" = true } -docstring_section_style = "list" -filters = "public" -heading_level = 1 -inherited_members = true -merge_init_into_class = true -parameter_headings = true -separate_signature = true -show_root_heading = true -show_root_full_path = false -show_signature_annotations = true -show_source = true -show_symbol_type_heading = true -show_symbol_type_toc = true -signature_crossrefs = true -summary = true -unwrap_annotated = true