diff --git a/.copier-answers.yml b/.copier-answers.yml index a7f91ade..d2a2b345 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ -# Changes here will be overwritten by Copier -_commit: 1.2.3 +# Changes here will be overwritten by Copier. +_commit: 1.6.1 _src_path: gh:mkdocstrings/handler-template author_email: dev@pawamoy.fr author_fullname: Timothée Mazzucotelli @@ -7,14 +7,10 @@ author_username: pawamoy copyright_date: '2021' copyright_holder: Timothée Mazzucotelli copyright_holder_email: dev@pawamoy.fr -copyright_license: ISC License -insiders: true -insiders_email: insiders@pawamoy.fr -insiders_repository_name: mkdocstrings-python +copyright_license: ISC language: Python project_description: A Python handler for mkdocstrings. project_name: mkdocstrings-python -public_release: true python_package_distribution_name: mkdocstrings-python python_package_import_name: python repository_name: python diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index a502284a..812789e6 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,5 +1,2 @@ github: pawamoy -ko_fi: pawamoy polar: pawamoy -custom: -- https://www.paypal.me/pawamoy diff --git a/.github/ISSUE_TEMPLATE/1-bug.md b/.github/ISSUE_TEMPLATE/1-bug.md index 0df6e967..a0e35e01 100644 --- a/.github/ISSUE_TEMPLATE/1-bug.md +++ b/.github/ISSUE_TEMPLATE/1-bug.md @@ -50,7 +50,7 @@ PASTE TRACEBACK HERE redacting sensitive information. --> ```bash -python -m mkdocstrings_handlers.python.debug # | xclip -selection clipboard +python -m mkdocstrings_handlers.python._internal.debug # | xclip -selection clipboard ``` PASTE MARKDOWN OUTPUT HERE diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..6f0f2faf --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +### 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 index 594d1c42..cde0a41b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,10 +2,17 @@ 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 @@ -14,13 +21,30 @@ env: LANG: en_US.utf-8 LC_ALL: en_US.utf-8 PYTHONIOENCODING: UTF-8 + 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" - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} steps: - name: Checkout @@ -30,12 +54,12 @@ jobs: fetch-tags: true - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: "3.12" + python-version: ${{ matrix.python-version }} - name: Setup uv - uses: astral-sh/setup-uv@v3 + uses: astral-sh/setup-uv@v5 with: enable-cache: true cache-dependency-glob: pyproject.toml @@ -55,33 +79,17 @@ jobs: - name: Check for breaking changes in the API run: make check-api - exclude-test-jobs: - runs-on: ubuntu-latest - outputs: - jobs: ${{ steps.exclude-jobs.outputs.jobs }} - steps: - - id: exclude-jobs - run: | - if ${{ github.repository_owner == 'pawamoy-insiders' }}; then - echo 'jobs=[ - {"os": "macos-latest"}, - {"os": "windows-latest"}, - {"python-version": "3.10"}, - {"python-version": "3.11"}, - {"python-version": "3.12"}, - {"python-version": "3.13"}, - {"python-version": "3.14"} - ]' | tr -d '[:space:]' >> $GITHUB_OUTPUT - else - echo 'jobs=[ - {"os": "macos-latest", "resolution": "lowest-direct"}, - {"os": "windows-latest", "resolution": "lowest-direct"} - ]' | tr -d '[:space:]' >> $GITHUB_OUTPUT - fi + - 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: exclude-test-jobs + needs: + - quality strategy: max-parallel: 4 matrix: @@ -90,18 +98,22 @@ jobs: - macos-latest - windows-latest python-version: - - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" - "3.14" + - "3.15" resolution: - highest - lowest-direct - exclude: ${{ fromJSON(needs.exclude-test-jobs.outputs.jobs) }} + exclude: + - os: macos-latest + resolution: lowest-direct + - os: windows-latest + resolution: lowest-direct runs-on: ${{ matrix.os }} - continue-on-error: ${{ matrix.python-version == '3.14' }} + continue-on-error: true steps: - name: Checkout @@ -111,22 +123,28 @@ jobs: fetch-tags: true - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - name: Setup uv - uses: astral-sh/setup-uv@v3 + uses: astral-sh/setup-uv@v5 with: enable-cache: true cache-dependency-glob: pyproject.toml - cache-suffix: py${{ matrix.python-version }} + 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 index 9388125b..1c7cda36 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,30 +15,15 @@ jobs: fetch-depth: 0 fetch-tags: true - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: "3.12" + python-version: "3.13" - name: Setup uv - uses: astral-sh/setup-uv@v3 - - name: Build dists - if: github.repository_owner == 'pawamoy-insiders' - run: uv tool run --from build pyproject-build - - name: Upload dists artifact - uses: actions/upload-artifact@v4 - if: github.repository_owner == 'pawamoy-insiders' - with: - name: python-insiders - path: ./dist/* + uses: astral-sh/setup-uv@v5 - name: Prepare release notes - if: github.repository_owner != 'pawamoy-insiders' run: uv tool run git-changelog --release-notes > release-notes.md - - name: Create release with assets - uses: softprops/action-gh-release@v2 - if: github.repository_owner == 'pawamoy-insiders' - with: - files: ./dist/* - name: Create release uses: softprops/action-gh-release@v2 - if: github.repository_owner != 'pawamoy-insiders' with: body_path: release-notes.md + diff --git a/.github/workflows/sponsors.yml b/.github/workflows/sponsors.yml new file mode 100644 index 00000000..8dd9150f --- /dev/null +++ b/.github/workflows/sponsors.yml @@ -0,0 +1,26 @@ +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/CHANGELOG.md b/CHANGELOG.md index 127f5295..29729f36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,202 @@ 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). +## [2.0.1](https://github.com/mkdocstrings/python/releases/tag/2.0.1) - 2025-12-03 + +[Compare with 2.0.0](https://github.com/mkdocstrings/python/compare/2.0.0...2.0.1) + +### Bug Fixes + +- Don't ignore filters when category grouping is disabled ([63aa1b0](https://github.com/mkdocstrings/python/commit/63aa1b0af0d14912ebf83a4e3c2cd0c7f2a19dae) by Timothée Mazzucotelli). [Issue-324](https://github.com/mkdocstrings/python/issues/324) + +### Code Refactoring + +- Localize more contents in templates ([854b6a6](https://github.com/mkdocstrings/python/commit/854b6a601bd334fe544285aa9eae11482388a583) by Zhikang Yan). [PR-321](https://github.com/mkdocstrings/python/pull/321) +- Improve ja/zh translations ([b83107c](https://github.com/mkdocstrings/python/commit/b83107c8e86d9650fe4544e569f6da16a46b8472) by Zhikang Yan). [PR-322](https://github.com/mkdocstrings/python/pull/322) + +## [2.0.0](https://github.com/mkdocstrings/python/releases/tag/2.0.0) - 2025-11-27 + +[Compare with 1.19.0](https://github.com/mkdocstrings/python/compare/1.19.0...2.0.0) + +### Code Refactoring + +- Remove deprecated code for v2 ([c10afdb](https://github.com/mkdocstrings/python/commit/c10afdb98d590a23c8840c7c0cdd6c358094dc2c) by Timothée Mazzucotelli). + +## [1.19.0](https://github.com/mkdocstrings/python/releases/tag/1.19.0) - 2025-11-10 + +[Compare with 1.18.2](https://github.com/mkdocstrings/python/compare/1.18.2...1.19.0) + +### Features + +- Release scoped and relative cross-references ([872afc5](https://github.com/mkdocstrings/python/commit/872afc584f33f50a133472afdc9355734a5e51ec) by Timothée Mazzucotelli). +- Release `__all__` ordering feature ([84aaebc](https://github.com/mkdocstrings/python/commit/84aaebcb4991c0245bf7ca8d7024c9d04942b0c1) by Timothée Mazzucotelli). +- Release public filter feature ([3be14cc](https://github.com/mkdocstrings/python/commit/3be14cc07bc9429d7ce01c748d825e2db1559212) by Timothée Mazzucotelli). +- Release backlinks feature ([ae7cc2d](https://github.com/mkdocstrings/python/commit/ae7cc2d7d8ea5711d8ce06620edd534a3e2b47aa) by Timothée Mazzucotelli). +- Release expression modernization feature ([dbadd1e](https://github.com/mkdocstrings/python/commit/dbadd1e898bb2e67515077d152890bdbbf0b3eb1) by Timothée Mazzucotelli). +- Release visually-lighter admonitions for source code blocks ([fdaeb48](https://github.com/mkdocstrings/python/commit/fdaeb48a0f2208bafd14f2f7ead42bca37bea665) by Timothée Mazzucotelli). +- Release inheritance diagram features ([669b42e](https://github.com/mkdocstrings/python/commit/669b42ebd7c154c81764fa98c052cf857f7aa406) by Timothée Mazzucotelli). + +### Code Refactoring + +- Update code base for Python 3.10 ([b696ed2](https://github.com/mkdocstrings/python/commit/b696ed2224756472a3617fa3cc18b69d0418ed71) by Timothée Mazzucotelli). + +## [1.18.2](https://github.com/mkdocstrings/python/releases/tag/1.18.2) - 2025-08-28 + +[Compare with 1.18.1](https://github.com/mkdocstrings/python/compare/1.18.1...1.18.2) + +### Bug Fixes + +- Normalize spaces to underscores when passing object to rendering context using its kind as key ([6f79be0](https://github.com/mkdocstrings/python/commit/6f79be0ea83522021e16e5d401209e58576ef93a) by Timothée Mazzucotelli). [Issue-mkdocstrings-791](https://github.com/mkdocstrings/mkdocstrings/issues/791) + +## [1.18.1](https://github.com/mkdocstrings/python/releases/tag/1.18.1) - 2025-08-28 + +[Compare with 1.18.0](https://github.com/mkdocstrings/python/compare/1.18.0...1.18.1) + +### Bug Fixes + +- Don't show implementation signature of `__init__` method when `overloads_only` is true and it is merged into the class ([9ef620f](https://github.com/mkdocstrings/python/commit/9ef620f2b1ae80b3711a2e84ab12d7d2c4a2dbdd) by Timothée Mazzucotelli). [Issue-308](https://github.com/mkdocstrings/python/issues/308) + +## [1.18.0](https://github.com/mkdocstrings/python/releases/tag/1.18.0) - 2025-08-26 + +[Compare with 1.17.0](https://github.com/mkdocstrings/python/compare/1.17.0...1.18.0) + +### Features + +- Support PEP 695 generics ([dc8c3ad](https://github.com/mkdocstrings/python/commit/dc8c3adb23b37add6601de9e74085f76e5fc9ee5) by Victor Westerhuis). [PR-221](https://github.com/mkdocstrings/python/pull/221), Co-authored-by: Timothée Mazzucotelli + +### Bug Fixes + +- Increase maximum recursion limit in case of deeply nested ASTs (rare occurrence) ([6004ccf](https://github.com/mkdocstrings/python/commit/6004ccf3576c7a20e21c880bb2235b7b426ba382) by Timothée Mazzucotelli). [Issue-griffe-402](https://github.com/mkdocstrings/griffe/issues/402) + +## [1.17.0](https://github.com/mkdocstrings/python/releases/tag/1.17.0) - 2025-08-14 + +[Compare with 1.16.12](https://github.com/mkdocstrings/python/compare/1.16.12...1.17.0) + +### Features + +- Support new Griffe parsing options `warn_missing_types` and `warnings` ([0e3bdb8](https://github.com/mkdocstrings/python/commit/0e3bdb857b5ede3e15aa7a9b8b87b33f68889c9e) by Timothée Mazzucotelli). [Issue-mkdocstrings-437](https://github.com/mkdocstrings/mkdocstrings/issues/437) +- Add `skip_local_inventory` option to prevent objects from being registered in the local objects inventory ([e82c24f](https://github.com/mkdocstrings/python/commit/e82c24f17513fba4cff22e90f0a82c00a01a077d) by Bartosz Sławecki). [Issue-296](https://github.com/mkdocstrings/python/issues/296), [Issue-mkdocstrings-671](https://github.com/mkdocstrings/mkdocstrings/issues/671), [PR-297](https://github.com/mkdocstrings/python/pull/297) +- Support hiding attribute values ([6cf34b9](https://github.com/mkdocstrings/python/commit/6cf34b9882e20d9147a6481e672ae09989a27796) by Bartosz Sławecki). Issue-292: #292, PR-293: #293 +- Support hiding implementation signature (showing overload only) ([d3b35e1](https://github.com/mkdocstrings/python/commit/d3b35e17384901e7280b8b6926f10fb033480358) by Bartosz Sławecki). [Issue-213](https://github.com/mkdocstrings/python/issues/213), [PR-286](https://github.com/mkdocstrings/python/pull/286) + +### Code Refactoring + +- Deprecate `locale` option in favor of mkdocstrings' ([17f71ba](https://github.com/mkdocstrings/python/commit/17f71babf11081869478b21b2bde1a33fc97be41) by Timothée Mazzucotelli). [PR-288](https://github.com/mkdocstrings/python/pull/288) + +## [1.16.12](https://github.com/mkdocstrings/python/releases/tag/1.16.12) - 2025-06-03 + +[Compare with 1.16.11](https://github.com/mkdocstrings/python/compare/1.16.11...1.16.12) + +### Bug Fixes + +- Only replace CSS class in first *highlighting* span ([d57740f](https://github.com/mkdocstrings/python/commit/d57740f874f056fb3ba1c6013ad04227df0f0af8) by Timothée Mazzucotelli). [Issue-281](https://github.com/mkdocstrings/python/issues/281) + +## [1.16.11](https://github.com/mkdocstrings/python/releases/tag/1.16.11) - 2025-05-24 + +[Compare with 1.16.10](https://github.com/mkdocstrings/python/compare/1.16.10...1.16.11) + +### Bug Fixes + +- Fix highlighting for signature with known special names like `__init__` ([7f95686](https://github.com/mkdocstrings/python/commit/7f956868f93a766346455fedb296c26787894d5c) by Timothée Mazzucotelli). [Issue-mkdocstrings-757](https://github.com/mkdocstrings/mkdocstrings/issues/757) +- Use default font-size for parameter headings ([0a35b20](https://github.com/mkdocstrings/python/commit/0a35b20a6050a28ba8492d93e5f9940a69462ce3) by Timothée Mazzucotelli). [Issue-mkdocstrings-697](https://github.com/mkdocstrings/mkdocstrings/issues/697) +- Prevent uppercasing H5 titles (by Material for MkDocs) ([ba66969](https://github.com/mkdocstrings/python/commit/ba669697daad5067ea5db3fdf8a2d5ba2f966b25) by Timothée Mazzucotelli). [Issue-mkdocstrings-697](https://github.com/mkdocstrings/mkdocstrings/issues/697), [Issue-276](https://github.com/mkdocstrings/python/issues/276) +- Use configured heading even when signature is not separated ([096960a](https://github.com/mkdocstrings/python/commit/096960abd79831d6fd45e2a7962dfd2bd49e4edd) by Timothée Mazzucotelli). [Issue-mkdocstrings-767](https://github.com/mkdocstrings/mkdocstrings/issues/767), [PR-278](https://github.com/mkdocstrings/python/pull/278) +- Render attribute names without full path in ToC ([d4e618a](https://github.com/mkdocstrings/python/commit/d4e618ab794747b84dced848b1be824639fea2b8) by David Lee). [Issue-271](https://github.com/mkdocstrings/python/issues/271), [PR-272](https://github.com/mkdocstrings/python/pull/272) + +## [1.16.10](https://github.com/mkdocstrings/python/releases/tag/1.16.10) - 2025-04-03 + +[Compare with 1.16.9](https://github.com/mkdocstrings/python/compare/1.16.9...1.16.10) + +### Bug Fixes + +- Fix inventory `base_url` being ignored ([8870eb9](https://github.com/mkdocstrings/python/commit/8870eb9af837666f59f96149c67c849e02f7ee25) by Stefan Mejlgaard). [Issue-268](https://github.com/mkdocstrings/python/issues/268), [PR-269](https://github.com/mkdocstrings/python/pull/269) + +## [1.16.9](https://github.com/mkdocstrings/python/releases/tag/1.16.9) - 2025-04-03 + +[Compare with 1.16.8](https://github.com/mkdocstrings/python/compare/1.16.8...1.16.9) + +### Bug Fixes + +- Use `toc_label` option in a few missing places ([337b46b](https://github.com/mkdocstrings/python/commit/337b46be912ff69e70b398bb252c8217c917db0a) by Timothée Mazzucotelli). [Issue-267](https://github.com/mkdocstrings/python/discussions/267) + +## [1.16.8](https://github.com/mkdocstrings/python/releases/tag/1.16.8) - 2025-03-24 + +[Compare with 1.16.7](https://github.com/mkdocstrings/python/compare/1.16.7...1.16.8) + +### Bug Fixes + +- Prevent infinite recursion by detecting parent-member cycles ([f3917e9](https://github.com/mkdocstrings/python/commit/f3917e9dd50ca7f94d0dd22b6e4e11885b4617e7) by Timothée Mazzucotelli). [Issue-griffe-368](https://github.com/mkdocstrings/griffe/issues/368) + +### Code Refactoring + +- Prepare feature for ordering by `__all__` value ([bfb5b30](https://github.com/mkdocstrings/python/commit/bfb5b303f4ea2187c15bccc688f7eba25e7edfcc) by Timothée Mazzucotelli). [Issue-219](https://github.com/mkdocstrings/python/issues/219) +- Sort objects without line numbers last instead of first ([681afb1](https://github.com/mkdocstrings/python/commit/681afb146225d98350a8eb2178aab07aec95fe6b) by Timothée Mazzucotelli). + +## [1.16.7](https://github.com/mkdocstrings/python/releases/tag/1.16.7) - 2025-03-20 + +[Compare with 1.16.6](https://github.com/mkdocstrings/python/compare/1.16.6...1.16.7) + +### Code Refactoring + +- Prepare `public` filtering method feature ([fde2019](https://github.com/mkdocstrings/python/commit/fde20191cab20f39d9e5e729a95cdfa3390b8f1f) by Timothée Mazzucotelli). [Issue-78](https://github.com/mkdocstrings/python/issues/78) + +## [1.16.6](https://github.com/mkdocstrings/python/releases/tag/1.16.6) - 2025-03-18 + +[Compare with 1.16.5](https://github.com/mkdocstrings/python/compare/1.16.5...1.16.6) + +### Deprecations + +Importing from submodules is now deprecated: the public API is fully exposed under the top-level `mkdocstrings_handler.python` module. + +### Bug Fixes + +- Add back default compiled filters (regression) ([2d83900](https://github.com/mkdocstrings/python/commit/2d83900c9e258399c90ecbac350ad03ff5d8f311) by Timothée Mazzucotelli). [Issue-264](https://github.com/mkdocstrings/python/issues/264) + +### Code Refactoring + +- Start logging warnings instead of info messages about deprecated use of templates ([7606f33](https://github.com/mkdocstrings/python/commit/7606f33559ced6962ecf9a1bc9aa76f24d87f515) by Timothée Mazzucotelli). +- Move modules into internal folder, expose API in top-level module ([93a68d0](https://github.com/mkdocstrings/python/commit/93a68d0d7afce38c78a8264189cfa812d737666c) by Timothée Mazzucotelli). + +## [1.16.5](https://github.com/mkdocstrings/python/releases/tag/1.16.5) - 2025-03-10 + +[Compare with 1.16.4](https://github.com/mkdocstrings/python/compare/1.16.4...1.16.5) + +### Code Refactoring + +- Prepare backlinks support ([56bf627](https://github.com/mkdocstrings/python/commit/56bf627b9483a12228b769ae4690b84733061ea5) by Timothée Mazzucotelli). [Issue-153](https://github.com/mkdocstrings/python/issues/153), [PR-252](https://github.com/mkdocstrings/python/pull/252) + +## [1.16.4](https://github.com/mkdocstrings/python/releases/tag/1.16.4) - 2025-03-10 + +[Compare with 1.16.3](https://github.com/mkdocstrings/python/compare/1.16.3...1.16.4) + +### Bug Fixes + +- Fix de-duplication of summary sections ([dc46ac9](https://github.com/mkdocstrings/python/commit/dc46ac9b4cfc642decd153dceb62e9f45c5c750e) by Timothée Mazzucotelli). + +## [1.16.3](https://github.com/mkdocstrings/python/releases/tag/1.16.3) - 2025-03-08 + +[Compare with 1.16.2](https://github.com/mkdocstrings/python/compare/1.16.2...1.16.3) + +### Build + +- Depend on mkdocstrings 0.28.3 ([9fa4f16](https://github.com/mkdocstrings/python/commit/9fa4f1636af240bb695661b7172f052cb11e0ec9) by Timothée Mazzucotelli). + +### Bug Fixes + +- De-duplicate summary sections ([a657d07](https://github.com/mkdocstrings/python/commit/a657d07499eb82d22337c169aa86b1cdd85543fa) by Timothée Mazzucotelli). [Issue-134](https://github.com/mkdocstrings/python/issues/134) + +### Code Refactoring + +- Import from top-level `mkdocstrings` module ([da2ba13](https://github.com/mkdocstrings/python/commit/da2ba13b1367ce107416d08f382fb9f2384c015c) by Timothée Mazzucotelli). + +## [1.16.2](https://github.com/mkdocstrings/python/releases/tag/1.16.2) - 2025-02-24 + +[Compare with 1.16.1](https://github.com/mkdocstrings/python/compare/1.16.1...1.16.2) + +### Build + +- Depend on mkdocs-autorefs >= 1.4 and mkdocstrings >= 0.28.2 ([ea1ab49](https://github.com/mkdocstrings/python/commit/ea1ab498be836c94eb695ace05c41357b12f2c95) by Timothée Mazzucotelli). + ## [1.16.1](https://github.com/mkdocstrings/python/releases/tag/1.16.1) - 2025-02-18 [Compare with 1.16.0](https://github.com/mkdocstrings/python/compare/1.16.0...1.16.1) @@ -330,9 +526,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - [`griffe-inherited-docstrings`](https://mkdocstrings.github.io/griffe-inherited-docstrings/), a Griffe extension for inheriting docstrings - [`griffe2md`](https://mkdocstrings.github.io/griffe2md/), a tool to output API docs to Markdown using Griffe - See the complete list of features and projects here: - https://pawamoy.github.io/insiders/#500-plasmavac-user-guide. - ## [1.7.5](https://github.com/mkdocstrings/python/releases/tag/1.7.5) - 2023-11-21 [Compare with 1.7.4](https://github.com/mkdocstrings/python/compare/1.7.4...1.7.5) @@ -549,7 +742,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Breaking changes -- The signature of the [`format_signature` filter](https://mkdocstrings.github.io/python/reference/mkdocstrings_handlers/python/rendering/#mkdocstrings_handlers.python.rendering.do_format_signature) has changed. +- The signature of the [`format_signature` filter][mkdocstrings_handlers.python.do_format_signature] has changed. If you override templates in your project to customize the output, make sure to update the following templates so that they use the new filter signature: diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 255e0eed..2d46305a 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,128 +2,79 @@ ## 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 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. +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: +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 +* 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 +* 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 +* 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 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. +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. +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. +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. +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: +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. +**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. +**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. +**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. +**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. +**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. +**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. +**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. +**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]. +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]. +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]. +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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3e3dc294..920ae3a9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,6 @@ # Contributing -Contributions are welcome, and they are greatly appreciated! -Every little bit helps, and credit will always be given. +Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. ## Environment setup @@ -14,11 +13,7 @@ cd python make setup ``` -> NOTE: -> If it fails for some reason, -> you'll need to install -> [uv](https://github.com/astral-sh/uv) -> manually. +> 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: > @@ -26,8 +21,7 @@ make setup > curl -LsSf https://astral.sh/uv/install.sh | sh > ``` > -> Now you can try running `make setup` again, -> or simply `uv sync`. +> Now you can try running `make setup` again, or simply `uv sync`. You now have the dependencies installed. @@ -35,15 +29,10 @@ 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). +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. +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 @@ -62,17 +51,13 @@ As usual: 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. +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): +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 @@ -80,10 +65,7 @@ or the [Karma convention](https://karma-runner.github.io/4.0/dev/git-commit-msg. [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. +**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: @@ -99,9 +81,7 @@ Scope and body are optional. Type can be: - `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: +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. @@ -110,16 +90,9 @@ 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). +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. +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 @@ -144,5 +117,4 @@ And force-push: 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. +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/Makefile b/Makefile index 5e88121d..1b3391da 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,13 @@ # 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 \ diff --git a/README.md b/README.md index 937a3a84..6210b54a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![ci](https://github.com/mkdocstrings/python/workflows/ci/badge.svg)](https://github.com/mkdocstrings/python/actions?query=workflow%3Aci) [![documentation](https://img.shields.io/badge/docs-mkdocs-708FCC.svg?style=flat)](https://mkdocstrings.github.io/python/) [![pypi version](https://img.shields.io/pypi/v/mkdocstrings-python.svg)](https://pypi.org/project/mkdocstrings-python/) -[![gitter](https://badges.gitter.im/join%20chat.svg)](https://app.gitter.im/#/room/#python:gitter.im) +[![gitter](https://img.shields.io/badge/matrix-chat-4DB798.svg?style=flat)](https://app.gitter.im/#/room/#mkdocstrings_python:gitter.im) --- @@ -77,3 +77,61 @@ dependencies = [ - **Source code display:** *mkdocstrings* can add a collapsible div containing the highlighted source code of the Python object. + +## Sponsors + + + +
+ +
Silver sponsors

+FastAPI
+Pydantic
+

+ +
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/config/pytest.ini b/config/pytest.ini index 4f43c18e..39ee3a3c 100644 --- a/config/pytest.ini +++ b/config/pytest.ini @@ -10,6 +10,8 @@ testpaths = # action:message_regex:warning_class:module_regex:line filterwarnings = error + default::EncodingWarning + error::EncodingWarning:python # TODO: Remove once pytest-xdist 4 is released. ignore:.*rsyncdir:DeprecationWarning:xdist # TODO: Remove once mkdocstrings stops setting fallback function. diff --git a/config/ruff.toml b/config/ruff.toml index 4c91b364..04af7c19 100644 --- a/config/ruff.toml +++ b/config/ruff.toml @@ -47,17 +47,24 @@ ignore = [ ] [lint.per-file-ignores] -"src/*/cli.py" = [ +"src/**/cli.py" = [ "T201", # Print statement ] "src/*/debug.py" = [ "T201", # Print statement ] +"!src/*/*.py" = [ + "D100", # Missing docstring in public module +] +"!src/**.py" = [ + "D101", # Missing docstring in public class + "D103", # Missing docstring in public function +] "scripts/*.py" = [ "INP001", # File is part of an implicit namespace package "T201", # Print statement ] -"tests/*.py" = [ +"tests/**.py" = [ "ARG005", # Unused lambda argument "FBT001", # Boolean positional arg in function definition "PLR2004", # Magic value used in comparison diff --git a/config/vscode/launch.json b/config/vscode/launch.json index e3288388..9d632bf0 100644 --- a/config/vscode/launch.json +++ b/config/vscode/launch.json @@ -7,7 +7,17 @@ "request": "launch", "program": "${file}", "console": "integratedTerminal", - "justMyCode": false + "justMyCode": false, + "args": "${command:pickArgs}" + }, + { + "name": "run", + "type": "debugpy", + "request": "launch", + "module": "python", + "console": "integratedTerminal", + "justMyCode": false, + "args": "${command:pickArgs}" }, { "name": "docs", diff --git a/docs/.overrides/main.html b/docs/.overrides/main.html deleted file mode 100644 index 5bedfd03..00000000 --- a/docs/.overrides/main.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "base.html" %} - -{% block announce %} - - Fund this project through - sponsorship - - {% include ".icons/octicons/heart-fill-16.svg" %} - — - - Follow - @pawamoy on - - - {% include ".icons/fontawesome/brands/mastodon.svg" %} - - Fosstodon - - for updates -{% endblock %} diff --git a/docs/.overrides/partials/path-item.html b/docs/.overrides/partials/path-item.html new file mode 100644 index 00000000..a9c95446 --- /dev/null +++ b/docs/.overrides/partials/path-item.html @@ -0,0 +1,22 @@ +{# Fix breadcrumbs for when mkdocs-section-index is used. #} +{# See https://github.com/squidfunk/mkdocs-material/issues/7614. #} + + +{% macro render_content(nav_item) %} + + {{ nav_item.title }} + +{% endmacro %} + + +{% macro render(nav_item, ref=nav_item) %} + {% if nav_item.is_page %} +
  • + + {{ render_content(ref) }} + +
  • + {% elif nav_item.children %} + {{ render(nav_item.children | first, ref) }} + {% endif %} +{% endmacro %} diff --git a/docs/changelog.md b/docs/changelog.md index 786b75d5..0536cbbe 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1 +1,5 @@ +--- +title: Changelog +--- + --8<-- "CHANGELOG.md" diff --git a/docs/code_of_conduct.md b/docs/code_of_conduct.md index 01f2ea20..002b2a04 100644 --- a/docs/code_of_conduct.md +++ b/docs/code_of_conduct.md @@ -1 +1,5 @@ +--- +title: Code of Conduct +--- + --8<-- "CODE_OF_CONDUCT.md" diff --git a/docs/contributing.md b/docs/contributing.md index ea38c9bf..61935e5d 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1 +1,5 @@ +--- +title: Contributing +--- + --8<-- "CONTRIBUTING.md" diff --git a/docs/credits.md b/docs/credits.md index f758db87..f6ab1aa2 100644 --- a/docs/credits.md +++ b/docs/credits.md @@ -1,10 +1,9 @@ --- +title: Credits hide: - toc --- - ```python exec="yes" --8<-- "scripts/gen_credits.py" ``` - diff --git a/docs/css/insiders.css b/docs/css/insiders.css deleted file mode 100644 index e7b9c74f..00000000 --- a/docs/css/insiders.css +++ /dev/null @@ -1,124 +0,0 @@ -@keyframes heart { - - 0%, - 40%, - 80%, - 100% { - transform: scale(1); - } - - 20%, - 60% { - transform: scale(1.15); - } -} - -@keyframes vibrate { - 0%, 2%, 4%, 6%, 8%, 10%, 12%, 14%, 16%, 18% { - -webkit-transform: translate3d(-2px, 0, 0); - transform: translate3d(-2px, 0, 0); - } - 1%, 3%, 5%, 7%, 9%, 11%, 13%, 15%, 17%, 19% { - -webkit-transform: translate3d(2px, 0, 0); - transform: translate3d(2px, 0, 0); - } - 20%, 100% { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.heart { - color: #e91e63; -} - -.pulse { - animation: heart 1000ms infinite; -} - -.vibrate { - animation: vibrate 2000ms infinite; -} - -.new-feature svg { - fill: var(--md-accent-fg-color) !important; -} - -a.insiders { - color: #e91e63; -} - -.sponsorship-list { - width: 100%; -} - -.sponsorship-item { - border-radius: 100%; - display: inline-block; - height: 1.6rem; - margin: 0.1rem; - overflow: hidden; - width: 1.6rem; -} - -.sponsorship-item:focus, .sponsorship-item:hover { - transform: scale(1.1); -} - -.sponsorship-item img { - filter: grayscale(100%) opacity(75%); - height: auto; - width: 100%; -} - -.sponsorship-item:focus img, .sponsorship-item:hover img { - filter: grayscale(0); -} - -.sponsorship-item.private { - background: var(--md-default-fg-color--lightest); - color: var(--md-default-fg-color); - font-size: .6rem; - font-weight: 700; - line-height: 1.6rem; - text-align: center; -} - -.mastodon { - color: #897ff8; - border-radius: 100%; - box-shadow: inset 0 0 0 .05rem currentcolor; - display: inline-block; - height: 1.2rem !important; - padding: .25rem; - transition: all .25s; - vertical-align: bottom !important; - width: 1.2rem; -} - -.premium-sponsors { - text-align: center; -} - -#silver-sponsors img { - height: 140px; -} - -#bronze-sponsors img { - height: 140px; -} - -#bronze-sponsors p { - display: flex; - flex-wrap: wrap; - justify-content: center; -} - -#bronze-sponsors a { - display: block; - flex-shrink: 0; -} - -.sponsors-total { - font-weight: bold; -} \ No newline at end of file diff --git a/docs/css/material.css b/docs/css/material.css index 19c6b076..235ef946 100644 --- a/docs/css/material.css +++ b/docs/css/material.css @@ -23,4 +23,4 @@ background-color: rgb(220, 139, 240); -webkit-mask-image: var(--md-admonition-icon--preview); mask-image: var(--md-admonition-icon--preview); -} \ No newline at end of file +} diff --git a/docs/css/mkdocstrings.css b/docs/css/mkdocstrings.css index 03c39d33..7d66153a 100644 --- a/docs/css/mkdocstrings.css +++ b/docs/css/mkdocstrings.css @@ -25,3 +25,48 @@ a.external:hover::after, a.autorefs-external:hover::after { background-color: var(--md-accent-fg-color); } + +/* 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/index.md b/docs/index.md index 8e6f2fb4..82377e21 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,5 @@ --- +title: Overview hide: - feedback --- diff --git a/docs/insiders/changelog.md b/docs/insiders/changelog.md deleted file mode 100644 index a6c7b907..00000000 --- a/docs/insiders/changelog.md +++ /dev/null @@ -1,82 +0,0 @@ -# Changelog - -## mkdocstrings-python Insiders - -### 1.9.0 September 03, 2024 { id="1.9.0" } - -- [Relative cross-references][relative_crossrefs] -- [Scoped cross-references][scoped_crossrefs] - -### 1.8.3 June 19, 2024 { id="1.8.3" } - -- Update code for Griffe 0.46+ to avoid deprecation warnings - -### 1.8.2 May 09, 2024 { id="1.8.2" } - -- Don't render cross-refs for default values when signatures aren't separated - -### 1.8.1 April 19, 2024 { id="1.8.1" } - -- Render enumeration instance name instead of just "value", allowing proper cross-reference - -### 1.8.0 March 24, 2024 { id="1.8.0" } - -- [Annotations modernization][modernize_annotations] - -### 1.7.0 March 24, 2024 { id="1.7.0" } - -- [Class inheritance diagrams with Mermaid][show_inheritance_diagram] - -### 1.6.0 January 30, 2024 { id="1.6.0" } - -- Render cross-references to parameters documentation in signatures and attribute values. -- Add [`parameter_headings`][parameter_headings] option to render headings for parameters (enabling permalinks and ToC/inventory entries). -- Render cross-references for default parameter values in signatures. - -### 1.5.1 September 12, 2023 { id="1.5.1" } - -- Prevent empty auto-summarized Methods section. - -### 1.5.0 September 05, 2023 { id="1.5.0" } - -- Render function signature overloads. - -### 1.4.0 August 27, 2023 { id="1.4.0" } - -- Render cross-references in attribute signatures. - -### 1.3.0 August 24, 2023 { id="1.3.0" } - -- Add "method" symbol type. - -### 1.2.0 August 20, 2023 { id="1.2.0" } - -- Add [member auto-summaries](../usage/configuration/members.md#summary). - -### 1.1.4 July 17, 2023 { id="1.1.4" } - -- Fix heading level increment for class members. - -### 1.1.3 July 17, 2023 { id="1.1.3" } - -- Fix heading level (avoid with clause preventing to decrease it). - -### 1.1.2 July 15, 2023 { id="1.1.2" } - -- Use non-breaking spaces after symbol types. - -### 1.1.1 June 27, 2023 { id="1.1.1" } - -- Correctly escape expressions in signatures and other rendered types. - -### 1.1.0 June 4, 2023 { id="1.1.0" } - -- Add [Symbol types in headings and table of contents](../usage/configuration/headings.md#show_symbol_type_toc). - -### 1.0.0 May 10, 2023 { id="1.0.0" } - -- Add [cross-references for type annotations in signatures](../usage/configuration/signatures.md#signature_crossrefs). - Make sure to update your local templates as the signature of the - [`format_signature` filter][mkdocstrings_handlers.python.rendering.do_format_signature] - has changed. The templates that must be updated: - `class.html`, `expression.html`, `function.html` and `signature.html`. diff --git a/docs/insiders/goals.yml b/docs/insiders/goals.yml deleted file mode 100644 index 16d1d507..00000000 --- a/docs/insiders/goals.yml +++ /dev/null @@ -1,44 +0,0 @@ -goals: - 500: - name: PlasmaVac User Guide - features: - - name: Cross-references for type annotations in signatures - ref: /usage/configuration/signatures/#signature_crossrefs - since: 2023/05/10 - - name: Symbol types in headings and table of contents - ref: /usage/configuration/headings/#show_symbol_type_toc - since: 2023/06/04 - 1000: - name: GraviFridge Fluid Renewal - features: - - name: Auto-summary of object members - ref: /usage/configuration/members/#summary - since: 2023/08/20 - - name: Automatic rendering of function signature overloads - since: 2023/09/05 - - name: Parameter headings - ref: /usage/configuration/headings/#parameter_headings - since: 2024/01/30 - - name: Automatic cross-references to parameters - ref: /usage/configuration/headings/#parameter_headings - since: 2024/01/30 - - name: Automatic cross-references for default parameter values in signatures - since: 2024/01/30 - 1500: - name: HyperLamp Navigation Tips - features: - - name: Class inheritance diagrams with Mermaid - ref: /usage/configuration/general/#show_inheritance_diagram - since: 2024/03/24 - - name: Annotations modernization - ref: /usage/configuration/signatures/#modernize_annotations - since: 2024/03/24 - 2000: - name: FusionDrive Ejection Configuration - features: - - name: Relative cross-references - ref: /usage/configuration/docstrings/#relative_crossrefs - since: 2024/09/03 - - name: Scoped cross-references - ref: /usage/configuration/docstrings/#scoped_crossrefs - since: 2024/09/03 diff --git a/docs/insiders/index.md b/docs/insiders/index.md deleted file mode 100644 index 288075b1..00000000 --- a/docs/insiders/index.md +++ /dev/null @@ -1,244 +0,0 @@ -# Insiders - -*mkdocstrings-python* follows the **sponsorware** release strategy, which means -that new features are first exclusively released to sponsors as part of -[Insiders][insiders]. Read on to learn [what sponsorships achieve][sponsorship], -[how to become a sponsor][sponsors] to get access to Insiders, -and [what's in it for you][features]! - -## What is Insiders? - -*mkdocstrings-python Insiders* is a private fork of *mkdocstrings-python*, hosted as -a private GitHub repository. Almost[^1] [all new features][features] -are developed as part of this fork, which means that they are immediately -available to all eligible sponsors, as they are made collaborators of this -repository. - - [^1]: - In general, every new feature is first exclusively released to sponsors, but - sometimes upstream dependencies enhance - existing features that must be supported by *mkdocstrings-python*. - -Every feature is tied to a [funding goal][funding] in monthly subscriptions. When a -funding goal is hit, the features that are tied to it are merged back into -*mkdocstrings-python* and released for general availability, making them available -to all users. Bugfixes are always released in tandem. - -Sponsorships start as low as [**$10 a month**][sponsors].[^2] - - [^2]: - Note that $10 a month is the minimum amount to become eligible for - Insiders. While GitHub Sponsors also allows to sponsor lower amounts or - one-time amounts, those can't be granted access to Insiders due to - technical reasons. Such contributions are still very much welcome as - they help ensuring the project's sustainability. - -## What sponsorships achieve - -Sponsorships make this project sustainable, as they buy the maintainers of this -project time – a very scarce resource – which is spent on the development of new -features, bug fixing, stability improvement, issue triage and general support. -The biggest bottleneck in Open Source is time.[^3] - - [^3]: - Making an Open Source project sustainable is exceptionally hard: maintainers - burn out, projects are abandoned. That's not great and very unpredictable. - The sponsorware model ensures that if you decide to use *mkdocstrings-python*, - you can be sure that bugs are fixed quickly and new features are added - regularly. - -If you're unsure if you should sponsor this project, check out the list of -[completed funding goals][goals completed] to learn whether you're already using features that -were developed with the help of sponsorships. You're most likely using at least -a handful of them, [thanks to our awesome sponsors][sponsors]! - -## What's in it for me? - -```python exec="1" session="insiders" -data_source = [ - "docs/insiders/goals.yml", - ("griffe-inherited-docstrings", "https://mkdocstrings.github.io/griffe-inherited-docstrings/", "insiders/goals.yml"), - ("griffe-pydantic", "https://mkdocstrings.github.io/griffe-pydantic/", "insiders/goals.yml"), - ("griffe-warnings-deprecated", "https://mkdocstrings.github.io/griffe-warnings-deprecated/", "insiders/goals.yml"), -] -``` - - -```python exec="1" session="insiders" idprefix="" ---8<-- "scripts/insiders.py" - -if unreleased_features: - print( - "The moment you [become a sponsor](#how-to-become-a-sponsor), you'll get **immediate " - f"access to {len(unreleased_features)} additional features** that you can start using right away, and " - "which are currently exclusively available to sponsors:\n" - ) - - for feature in unreleased_features: - feature.render(badge=True) - - print( - "\n\nThese are just the features related to this project. " - "[See the complete feature list on the author's main Insiders page](https://pawamoy.github.io/insiders/#whats-in-it-for-me)." - ) -else: - print( - "The moment you [become a sponsor](#how-to-become-a-sponsor), you'll get immediate " - "access to all released features that you can start using right away, and " - "which are exclusively available to sponsors. At this moment, there are no " - "Insiders features for this project, but checkout the [next funding goals](#goals) " - "to see what's coming, as well as **[the feature list for all Insiders projects](https://pawamoy.github.io/insiders/#whats-in-it-for-me).**" - ) -``` - - -Additionally, your sponsorship will give more weight to your upvotes on issues, helping us prioritize work items in our backlog. For more information on how we prioritize work, see this page: [Backlog management](https://pawamoy.github.io/backlog/). - -## How to become a sponsor - -Thanks for your interest in sponsoring! In order to become an eligible sponsor -with your GitHub account, visit [pawamoy's sponsor profile][github sponsor profile], -and complete a sponsorship of **$10 a month or more**. -You can use your individual or organization GitHub account for sponsoring. - -Sponsorships lower than $10 a month are also very much appreciated, and useful. -They won't grant you access to Insiders, but they will be counted towards reaching sponsorship goals. -*Every* sponsorship helps us implementing new features and releasing them to the public. - -**Important**: If you're sponsoring **[@pawamoy][github sponsor profile]** -through a GitHub organization, please send a short email -to insiders@pawamoy.fr with the name of your -organization and the GitHub account of the individual -that should be added as a collaborator.[^4] - -You can cancel your sponsorship anytime.[^5] - - [^4]: - It's currently not possible to grant access to each member of an - organization, as GitHub only allows for adding users. Thus, after - sponsoring, please send an email to insiders@pawamoy.fr, stating which - account should become a collaborator of the Insiders repository. We're - working on a solution which will make access to organizations much simpler. - To ensure that access is not tied to a particular individual GitHub account, - create a bot account (i.e. a GitHub account that is not tied to a specific - individual), and use this account for the sponsoring. After being added to - the list of collaborators, the bot account can create a private fork of the - private Insiders GitHub repository, and grant access to all members of the - organizations. - - [^5]: - If you cancel your sponsorship, GitHub schedules a cancellation request - which will become effective at the end of the billing cycle. This means - that even though you cancel your sponsorship, you will keep your access to - Insiders as long as your cancellation isn't effective. All charges are - processed by GitHub through Stripe. As we don't receive any information - regarding your payment, and GitHub doesn't offer refunds, sponsorships are - non-refundable. - -[:octicons-heart-fill-24:{ .pulse }   Join our awesome sponsors](https://github.com/sponsors/pawamoy){ .md-button .md-button--primary } - -
    -
    -
    -
    -
    -
    -
    - -
    - - - If you sponsor publicly, you're automatically added here with a link to - your profile and avatar to show your support for *mkdocstrings-python*. - Alternatively, if you wish to keep your sponsorship private, you'll be a - silent +1. You can select visibility during checkout and change it - afterwards. - - -## Funding - -### Goals - -The following section lists all funding goals. Each goal contains a list of -features prefixed with a checkmark symbol, denoting whether a feature is -:octicons-check-circle-fill-24:{ style="color: #00e676" } already available or -:octicons-check-circle-fill-24:{ style="color: var(--md-default-fg-color--lightest)" } planned, -but not yet implemented. When the funding goal is hit, -the features are released for general availability. - -```python exec="1" session="insiders" idprefix="" -for goal in goals.values(): - if not goal.complete: - goal.render() -``` - -### Goals completed - -This section lists all funding goals that were previously completed, which means -that those features were part of Insiders, but are now generally available and -can be used by all users. - -```python exec="1" session="insiders" idprefix="" -for goal in goals.values(): - if goal.complete: - goal.render() -``` - -## Frequently asked questions - -### Compatibility - -> We're building an open source project and want to allow outside collaborators -to use *mkdocstrings-python* locally without having access to Insiders. -Is this still possible? - -Yes. Insiders is compatible with *mkdocstrings-python*. Almost all new features -and configuration options are either backward-compatible or implemented behind -feature flags. Most Insiders features enhance the overall experience, -though while these features add value for the users of your project, they -shouldn't be necessary for previewing when making changes to content. - -### Payment - -> We don't want to pay for sponsorship every month. Are there any other options? - -Yes. You can sponsor on a yearly basis by [switching your GitHub account to a -yearly billing cycle][billing cycle]. If for some reason you cannot do that, you -could also create a dedicated GitHub account with a yearly billing cycle, which -you only use for sponsoring (some sponsors already do that). - -If you have any problems or further questions, please reach out to insiders@pawamoy.fr. - -### Terms - -> Are we allowed to use Insiders under the same terms and conditions as -*mkdocstrings-python*? - -Yes. Whether you're an individual or a company, you may use *mkdocstrings-python -Insiders* precisely under the same terms as *mkdocstrings-python*, which are given -by the [ISC License][license]. However, we kindly ask you to respect our -**fair use policy**: - -- Please **don't distribute the source code** of Insiders. You may freely use - it for public, private or commercial projects, privately fork or mirror it, - but please don't make the source code public, as it would counteract the - sponsorware strategy. - -- If you cancel your subscription, you're automatically removed as a - collaborator and will miss out on all future updates of Insiders. However, you - may **use the latest version** that's available to you **as long as you like**. - Just remember that [GitHub deletes private forks][private forks]. - -[insiders]: #what-is-insiders -[sponsorship]: #what-sponsorships-achieve -[sponsors]: #how-to-become-a-sponsor -[features]: #whats-in-it-for-me -[funding]: #funding -[goals completed]: #goals-completed -[github sponsor profile]: https://github.com/sponsors/pawamoy -[billing cycle]: https://docs.github.com/en/github/setting-up-and-managing-billing-and-payments-on-github/changing-the-duration-of-your-billing-cycle -[license]: ../license.md -[private forks]: https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/removing-a-collaborator-from-a-personal-repository - - - diff --git a/docs/insiders/installation.md b/docs/insiders/installation.md deleted file mode 100644 index 3588e691..00000000 --- a/docs/insiders/installation.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -title: Getting started with Insiders ---- - -# Getting started with Insiders - -*mkdocstrings-python Insiders* is a compatible drop-in replacement for *mkdocstrings-python*, -and can be installed similarly using `pip` or `git`. -Note that in order to access the Insiders repository, -you need to [become an eligible sponsor] of @pawamoy on GitHub. - - [become an eligible sponsor]: index.md#how-to-become-a-sponsor - -## Installation - -### with PyPI Insiders - -[PyPI Insiders](https://pawamoy.github.io/pypi-insiders/) -is a tool that helps you keep up-to-date versions -of Insiders projects in the PyPI index of your choice -(self-hosted, Google registry, Artifactory, etc.). - -See [how to install it](https://pawamoy.github.io/pypi-insiders/#installation) -and [how to use it](https://pawamoy.github.io/pypi-insiders/#usage). - -**We kindly ask that you do not upload the distributions to public registries, -as it is against our [Terms of use](index.md#terms).** - -### with pip (ssh/https) - -*mkdocstrings-python Insiders* can be installed with `pip` [using SSH][using ssh]: - -```bash -pip install git+ssh://git@github.com/pawamoy-insiders/mkdocstrings-python.git -``` - - [using ssh]: https://docs.github.com/en/authentication/connecting-to-github-with-ssh - -Or using HTTPS: - -```bash -pip install git+https://${GH_TOKEN}@github.com/pawamoy-insiders/mkdocstrings-python.git -``` - ->? NOTE: **How to get a GitHub personal access token?** -> The `GH_TOKEN` environment variable is a GitHub token. -> It can be obtained by creating a [personal access token] for -> your GitHub account. It will give you access to the Insiders repository, -> programmatically, from the command line or GitHub Actions workflows: -> -> 1. Go to https://github.com/settings/tokens -> 2. Click on [Generate a new token] -> 3. Enter a name and select the [`repo`][scopes] scope -> 4. Generate the token and store it in a safe place -> -> [personal access token]: https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token -> [Generate a new token]: https://github.com/settings/tokens/new -> [scopes]: https://docs.github.com/en/developers/apps/scopes-for-oauth-apps#available-scopes -> -> Note that the personal access -> token must be kept secret at all times, as it allows the owner to access your -> private repositories. - -### with Git - -Of course, you can use *mkdocstrings-python Insiders* directly using Git: - -``` -git clone git@github.com:pawamoy-insiders/mkdocstrings-python -``` - -When cloning with Git, the package must be installed: - -``` -pip install -e mkdocstrings-python -``` - -## Upgrading - -When upgrading Insiders, you should always check the version of *mkdocstrings-python* -which makes up the first part of the version qualifier. For example, a version like -`8.x.x.4.x.x` means that Insiders `4.x.x` is currently based on `8.x.x`. - -If the major version increased, it's a good idea to consult the [changelog] -and go through the steps to ensure your configuration is up to date and -all necessary changes have been made. - - [changelog]: ./changelog.md diff --git a/docs/js/insiders.js b/docs/js/insiders.js deleted file mode 100644 index 8bb68485..00000000 --- a/docs/js/insiders.js +++ /dev/null @@ -1,74 +0,0 @@ -function humanReadableAmount(amount) { - const strAmount = String(amount); - if (strAmount.length >= 4) { - return `${strAmount.slice(0, strAmount.length - 3)},${strAmount.slice(-3)}`; - } - return strAmount; -} - -function getJSON(url, callback) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.responseType = 'json'; - xhr.onload = function () { - var status = xhr.status; - if (status === 200) { - callback(null, xhr.response); - } else { - callback(status, xhr.response); - } - }; - xhr.send(); -} - -function updatePremiumSponsors(dataURL, rank) { - let capRank = rank.charAt(0).toUpperCase() + rank.slice(1); - getJSON(dataURL + `/sponsors${capRank}.json`, function (err, sponsors) { - const sponsorsDiv = document.getElementById(`${rank}-sponsors`); - if (sponsors.length > 0) { - let html = ''; - html += `${capRank} sponsors

    ` - sponsors.forEach(function (sponsor) { - html += ` - - ${sponsor.name} - - ` - }); - html += '

    ' - sponsorsDiv.innerHTML = html; - } - }); -} - -function updateInsidersPage(author_username) { - const sponsorURL = `https://github.com/sponsors/${author_username}` - const dataURL = `https://raw.githubusercontent.com/${author_username}/sponsors/main`; - getJSON(dataURL + '/numbers.json', function (err, numbers) { - document.getElementById('sponsors-count').innerHTML = numbers.count; - Array.from(document.getElementsByClassName('sponsors-total')).forEach(function (element) { - element.innerHTML = '$ ' + humanReadableAmount(numbers.total); - }); - getJSON(dataURL + '/sponsors.json', function (err, sponsors) { - const sponsorsElem = document.getElementById('sponsors'); - const privateSponsors = numbers.count - sponsors.length; - sponsors.forEach(function (sponsor) { - sponsorsElem.innerHTML += ` - - - - `; - }); - if (privateSponsors > 0) { - sponsorsElem.innerHTML += ` - - +${privateSponsors} - - `; - } - }); - }); - updatePremiumSponsors(dataURL, "gold"); - updatePremiumSponsors(dataURL, "silver"); - updatePremiumSponsors(dataURL, "bronze"); -} diff --git a/docs/license.md b/docs/license.md index e81c0edf..5b25a00f 100644 --- a/docs/license.md +++ b/docs/license.md @@ -1,4 +1,5 @@ --- +title: License hide: - feedback --- diff --git a/docs/reference/api.md b/docs/reference/api.md new file mode 100644 index 00000000..587e99db --- /dev/null +++ b/docs/reference/api.md @@ -0,0 +1,9 @@ +--- +title: API reference +hide: +- navigation +--- + +# ::: mkdocstrings_handlers.python + options: + show_submodules: true diff --git a/docs/snippets/package/generics.py b/docs/snippets/package/generics.py new file mode 100644 index 00000000..0aef12d4 --- /dev/null +++ b/docs/snippets/package/generics.py @@ -0,0 +1,49 @@ +"""Some module showing generics. + +Type Aliases: + SomeType: Some type alias. +""" + +type SomeType[Z] = int | list[Z] +"""Some type alias. + +Type parameters: + Z: Some type parameter. +""" + + +class MagicBag[T: (str, bytes) = str](list[T]): + """A magic bag of items. + + Type parameters: + T: Some type. + """ + + def __init__[U: (int, bool)](self, *args: T, flag1: U | None = None, flag2: U | None = None) -> None: + """Initialize bag. + + Type parameters: + U: Some flag type. + + Parameters: + flag1: Some flag. + flag2: Some flag. + """ + super().__init__(args) + self.flag1 = flag1 + self.flag2 = flag2 + + def mutate[K](self, item: T, into: K) -> K: + """Shake the bag to mutate an item into something else (and eject it). + + Type parameters: + K: Some other type. + + Parameters: + item: The item to mutate. + into: Mutate the item into something like this. + + Returns: + The mutated item. + """ + ... diff --git a/docs/usage/configuration/docstrings.md b/docs/usage/configuration/docstrings.md index ae925f23..95f9032f 100644 --- a/docs/usage/configuration/docstrings.md +++ b/docs/usage/configuration/docstrings.md @@ -327,9 +327,6 @@ class Thing: [](){#option-relative_crossrefs} ## `relative_crossrefs` -[:octicons-heart-fill-24:{ .pulse } Sponsors only](../../insiders/index.md){ .insiders } — -[:octicons-tag-24: Insiders 1.9.0](../../insiders/changelog.md#1.9.0) - - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -440,9 +437,6 @@ INFO: **There is an alternative, third-party Python handler that handles relativ [](){#option-scoped_crossrefs} ## `scoped_crossrefs` -[:octicons-heart-fill-24:{ .pulse } Sponsors only](../../insiders/index.md){ .insiders } — -[:octicons-tag-24: Insiders 1.9.0](../../insiders/changelog.md#1.9.0) - - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -623,7 +617,7 @@ class ClassWithoutDocstring: - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** -Whether to render the "Attributes" sections of docstrings. +Whether to render the "Attributes" section of docstrings. ```yaml title="in mkdocs.yml (global configuration)" plugins: @@ -676,7 +670,7 @@ class Class: - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** -Whether to render the "Functions" or "Methods" sections of docstrings. +Whether to render the "Functions" or "Methods" section of docstrings. ```yaml title="in mkdocs.yml (global configuration)" plugins: @@ -751,7 +745,7 @@ class Class: - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** -Whether to render the "Classes" sections of docstrings. +Whether to render the "Classes" section of docstrings. ```yaml title="in mkdocs.yml (global configuration)" plugins: @@ -804,13 +798,71 @@ class Class: //// /// +[](){#option-show_docstring_type_aliases} +## `show_docstring_type_aliases` + +- **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** + +Whether to render the "Type Aliases" section of docstrings. + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + show_docstring_type_aliases: true +``` + +```md title="or in docs/some_page.md (local configuration)" +::: path.to.module + options: + show_docstring_type_aliases: false +``` + +```python +"""Summary. + +Type Aliases: + TypeAlias: Some type alias. +""" + + +type TypeAlias = int +"""Summary.""" +``` + +/// admonition | Preview + type: preview + +//// tab | With type_aliases +

    module

    +

    Summary.

    +

    Type Aliases:

    + +**Name** | **Description** +------------ | ---------------- +`TypeAlias` | Some type alias. + +

    TypeAlias

    +

    Summary.

    +//// + +//// tab | Without classes +

    module

    +

    Summary.

    +

    TypeAlias

    +

    Summary.

    +//// +/// + [](){#option-show_docstring_modules} ## `show_docstring_modules` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** -Whether to render the "Modules" sections of docstrings. +Whether to render the "Modules" section of docstrings. ```yaml title="in mkdocs.yml (global configuration)" plugins: @@ -1245,6 +1297,57 @@ def rand() -> int: //// /// +[](){#option-show_docstring_type_parameters} +## `show_docstring_type_parameters` + +- **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** + + +Whether to render the "Type Parameters" section of docstrings. + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + show_docstring_type_parameters: true +``` + +```md title="or in docs/some_page.md (local configuration)" +::: path.to.module + options: + show_docstring_type_parameters: false +``` + +```python +class AClass[X: (int, str) = str]: + """Represents something. + + Type Parameters: + X: Something. + """ +``` + +/// admonition | Preview + type: preview + +//// tab | With parameters +

    AClass

    +

    Represents something.

    +

    Type Parameters:

    + +**Name** | **Bound or Constraints** | **Description** | **Default** +---------- | ------------------------ | --------------- | ----------- +`whatever` | `(int, str)` | Something. | `str` +//// + +//// tab | Without parameters +

    AClass

    +

    Represents something.

    +//// +/// + [](){#option-show_docstring_warns} ## `show_docstring_warns` diff --git a/docs/usage/configuration/general.md b/docs/usage/configuration/general.md index 3983a616..921f3b27 100644 --- a/docs/usage/configuration/general.md +++ b/docs/usage/configuration/general.md @@ -60,6 +60,45 @@ plugins: //// /// +[](){#option-backlinks} +## `backlinks` + +- **:octicons-package-24: Type Literal["flat", "tree", False] :material-equal: `False`{ title="default value" }** + +The `backlinks` option enables rendering of backlinks within your API documentation. + +When an arbitrary section of your documentation links to an API symbol, this link will be collected as a backlink, and rendered below your API symbol. In short, the API symbol will link back to the section that links to it. Such backlinks will help your users navigate the documentation, as they will immediately which functions return a specific symbol, or where a specific symbol is accepted as parameter, etc.. + +Each backlink is a list of breadcrumbs that represent the navigation, from the root page down to the given section. + +The available styles for rendering backlinks are **`flat`** and **`tree`**. + +- **`flat`** will render backlinks as a single-layer list. This can lead to repetition of breadcrumbs. +- **`tree`** will combine backlinks into a tree, to remove repetition of breadcrumbs. + +WARNING: **Global-only option.** For now, the option only works when set globally in `mkdocs.yml`. + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + backlinks: tree +``` + +/// admonition | Preview + type: preview + +//// tab | Flat +![backlinks-flat](https://github.com/user-attachments/assets/f7a15b01-f194-4c55-8281-50adbb4a74af) +//// + +//// tab | Tree +![backlinks-tree](https://github.com/user-attachments/assets/3457db21-45e1-4e03-bd8c-2e9e75dc778b) +//// +/// + [](){#option-extensions} ## `extensions` @@ -227,6 +266,143 @@ plugins: WARNING: **Packages are loaded only once.** When mkdocstrings-python collects data from a Python package (thanks to [Griffe](https://mkdocstrings.github.io/griffe/)), it collects *the entire package* and *caches it*. Next time an object from the same package is rendered, the package is retrieved from the cache and not collected again. The `force_inspection` option will therefore only have an effect the first time a package is collected, and will do nothing for objects rendered afterwards. +[](){#option-inheritance_diagram_direction} +## `inheritance_diagram_direction` + +The direction of the Mermaid chart presenting the inheritance diagram of a class, `TD` by default. + +```yaml title="mkdocs.yml" +extra_javascript: +- https://unpkg.com/mermaid@10.9.0/dist/mermaid.min.js +``` + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + inheritance_diagram_direction: TD +``` + +```md title="or in docs/some_page.md (local configuration)" +::: path.to.object + options: + inheritance_diagram_direction: TD +``` + +/// admonition | Preview + type: preview + + +With the following classes: + +```python +class SuperAbstract: + """Super abstract class.""" +class Mixin1: + """Mixin 1.""" +class Abstract(SuperAbstract, Mixin1): + """Abstract class.""" +class Mixin2A: + """Mixin 2A.""" +class Mixin2B(Mixin2A): + """Mixin 2B.""" +class Concrete(Abstract, Mixin2B): + """Concrete class.""" +class SuperConcrete(Concrete): + """Super concrete class.""" +``` + +//// tab | `TD` (or `TB`) + +```mermaid +flowchart TD +SuperConcrete[SuperConcrete] +Concrete[Concrete] +Abstract[Abstract] +SuperAbstract[SuperAbstract] +Mixin1[Mixin1] +Mixin2B[Mixin2B] +Mixin2A[Mixin2A] + +Concrete --> SuperConcrete +Abstract --> Concrete +SuperAbstract --> Abstract +Mixin1 --> Abstract +Mixin2B --> Concrete +Mixin2A --> Mixin2B +``` + +//// + +//// tab | `BT` + +```mermaid +flowchart BT +SuperConcrete[SuperConcrete] +Concrete[Concrete] +Abstract[Abstract] +SuperAbstract[SuperAbstract] +Mixin1[Mixin1] +Mixin2B[Mixin2B] +Mixin2A[Mixin2A] + +Concrete --> SuperConcrete +Abstract --> Concrete +SuperAbstract --> Abstract +Mixin1 --> Abstract +Mixin2B --> Concrete +Mixin2A --> Mixin2B +``` + +//// + +//// tab | `RL` + +```mermaid +flowchart RL +SuperConcrete[SuperConcrete] +Concrete[Concrete] +Abstract[Abstract] +SuperAbstract[SuperAbstract] +Mixin1[Mixin1] +Mixin2B[Mixin2B] +Mixin2A[Mixin2A] + +Concrete --> SuperConcrete +Abstract --> Concrete +SuperAbstract --> Abstract +Mixin1 --> Abstract +Mixin2B --> Concrete +Mixin2A --> Mixin2B +``` + +//// + +//// tab | `LR` + +```mermaid +flowchart LR +SuperConcrete[SuperConcrete] +Concrete[Concrete] +Abstract[Abstract] +SuperAbstract[SuperAbstract] +Mixin1[Mixin1] +Mixin2B[Mixin2B] +Mixin2A[Mixin2A] + +Concrete --> SuperConcrete +Abstract --> Concrete +SuperAbstract --> Abstract +Mixin1 --> Abstract +Mixin2B --> Concrete +Mixin2A --> Mixin2B +``` + +//// +/// + [](){#option-preload_modules} ## `preload_modules` @@ -324,9 +500,6 @@ plugins: [](){#option-show_inheritance_diagram} ## `show_inheritance_diagram` -[:octicons-heart-fill-24:{ .pulse } Sponsors only](../../insiders/index.md){ .insiders } — -[:octicons-tag-24: Insiders 1.7.0](../../insiders/changelog.md#1.7.0) - - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -452,3 +625,63 @@ def some_function():

    Docstring of the function.

    //// /// + +[](){#option-skip_local_inventory} +## `skip_local_inventory` + +- **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** + + +Whether to skip registering symbols in the objects inventory. + +With this option enabled, re-rendering docstrings for objects from external inventories is possible with their cross-references pointing to the original external inventory, not local. Similarly, it becomes possible to render the same symbol several times in the same documentation, with only one canonical location being used for cross-references (preventing confusion in mkdocs-autorefs). + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + skip_local_inventory: false +``` + +```md title="or in docs/some_page.md (local configuration)" +::: path.to.module + options: + skip_local_inventory: true +``` + +/// admonition | Preview + type: preview + + +//// tab | Without `skip_local_inventory` + +```md exec="on" +::: bisect.bisect_left + options: + heading_level: 3 + skip_local_inventory: false + show_docstring_description: false +``` + +Notice how [`bisect.bisect_left`][] now points to the section above. + +//// + +//// tab | With `skip_local_inventory` + +```md exec="on" +::: bisect.bisect_right + inventories: + - https://docs.python.org/3/objects.inv + options: + heading_level: 3 + skip_local_inventory: true + show_docstring_description: false +``` + +Notice how [`bisect.bisect_right`][] points to the original Python documentation. + +//// +/// diff --git a/docs/usage/configuration/headings.md b/docs/usage/configuration/headings.md index b4314b77..593b6fb0 100644 --- a/docs/usage/configuration/headings.md +++ b/docs/usage/configuration/headings.md @@ -77,8 +77,6 @@ plugins: [](){#option-parameter_headings} ## `parameter_headings` -[:octicons-tag-24: Insiders 1.6.0](../../insiders/changelog.md#1.6.0) - - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -88,7 +86,7 @@ With this option enabled, each function/method parameter (including parameters of `__init__` methods merged in their parent class with the [`merge_init_into_class`][] option) gets a permalink, an entry in the Table of Contents, -and an entry in the generated objects inventory. +and an entry in the generated objects inventory (unless [`skip_local_inventory`][] is enabled). The permalink and inventory entry allow cross-references from internal and external pages. @@ -537,8 +535,6 @@ plugins: [](){#option-show_symbol_type_heading} ## `show_symbol_type_heading` -[:octicons-tag-24: Insiders 1.1.0](../../insiders/changelog.md#1.1.0) - - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -602,8 +598,6 @@ plugins: [](){#option-show_symbol_type_toc} ## `show_symbol_type_toc` -[:octicons-tag-24: Insiders 1.1.0](../../insiders/changelog.md#1.1.0) - - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -682,3 +676,135 @@ NOTE: **Use with/without `heading`.** If you use this option without specifying heading: "My fancy module" toc_label: "My fancy module" ``` + +[](){#option-type_parameter_headings} +## `type_parameter_headings` + +- **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** + +Whether to render headings for generic class, function/method and type alias +type parameters. + +With this option enabled, each type parameter of a generic object (including +type parameters of `__init__` methods merged in their parent class with the +[`merge_init_into_class`][] option) gets a permalink, an entry in the Table of +Contents, and an entry in the generated objects inventory. The permalink and +inventory entry allow cross-references from internal and external pages. + + + +Enabling this option along with [`signature_crossrefs`][] will automatically +render cross-references to type parameters in class/function/method/type alias +signatures. + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + type_parameter_headings: false +``` + +```md title="or in docs/some_page.md (local configuration)" +::: path.to.module + options: + type_parameter_headings: true +``` + +/// admonition | Preview: Cross-references + type: preview + +```md exec="on" +::: package.generics + options: + show_root_heading: false + heading_level: 3 + docstring_section_style: list + show_bases: true + summary: false + separate_signature: true + show_signature_type_parameters: true + show_inheritance_diagram: false + type_parameter_headings: true +``` + +/// + +/// admonition | Preview: Type parameter sections + type: preview + +//// tab | Table style +```md exec="on" +::: package.generics.MagicBag + options: + members: false + heading_level: 3 + show_root_heading: false + show_root_toc_entry: false + parameter_headings: true + docstring_section_style: table + show_docstring_description: false + show_docstring_parameters: false + show_docstring_returns: false + show_inheritance_diagram: false +``` +//// + +//// tab | List style +```md exec="on" +::: package.generics.MagicBag + options: + members: false + heading_level: 3 + show_root_heading: false + show_root_toc_entry: false + parameter_headings: true + docstring_section_style: list + show_docstring_description: false + show_docstring_parameters: false + show_docstring_returns: false + show_inheritance_diagram: false +``` +//// + +//// tab | Spacy style +```md exec="on" +::: package.generics.MagicBag + options: + members: false + heading_level: 3 + show_root_heading: false + show_root_toc_entry: false + parameter_headings: true + docstring_section_style: spacy + show_docstring_description: false + show_docstring_parameters: false + show_docstring_returns: false + show_inheritance_diagram: false +``` +//// +/// + +/// admonition | Preview: Table of contents (with symbol types) + type: preview + + mutate
    + U + +To customize symbols, see [Customizing symbol types](../customization.md/#symbol-types). + +/// diff --git a/docs/usage/configuration/members.md b/docs/usage/configuration/members.md index 363f7e0a..53d955fa 100644 --- a/docs/usage/configuration/members.md +++ b/docs/usage/configuration/members.md @@ -264,13 +264,14 @@ class Main(Base): [](){#option-members_order} ## `members_order` -- **:octicons-package-24: Type [`str`][] :material-equal: `"alphabetical"`{ title="default value" }** +- **:octicons-package-24: Type `str | list[str]` :material-equal: `"alphabetical"`{ title="default value" }** The members ordering to use. Possible values: -- `alphabetical`: order by the members names. -- `source`: order members as they appear in the source file. +- `__all__`: Order according to `__all__` attributes. Since classes do not define `__all__` attributes, you can specify a second ordering method by using a list. +- `alphabetical`: Order by the members names. +- `source`: Order members as they appear in the source file. The order applies for all members, recursively. The order will be ignored for members that are explicitely sorted using the [`members`][] option. @@ -292,6 +293,12 @@ plugins: members_order: source ``` +```md title="or in docs/some_page.md (local configuration)" +::: package.module + options: + members_order: [__all__, source] +``` + ```python title="package/module.py" """Module docstring.""" @@ -335,10 +342,18 @@ def function_c(): [](){#option-filters} ## `filters` -- **:octicons-package-24: Type list[str] | None :material-equal: `["!^_[^_]"]`{ title="default value" }** +- **:octicons-package-24: Type list[str] | Literal["public"] | None :material-equal: `["!^_[^_]"]`{ title="default value" }** -A list of filters applied to filter objects based on their name. +A list of filters, or `"public"`. + +**Filtering methods** + +[](){#option-filters-public} + +The `public` filtering method will include only public objects: those added to the `__all__` attribute of modules, or not starting with a single underscore. Special methods and attributes ("dunder" methods/attributes, starting and ending with two underscores), like `__init__`, `__call__`, `__mult__`, etc., are always considered public. + +**List of filters** Filters are regular expressions. These regular expressions are evaluated by Python and so must match the syntax supported by the [`re`][] module. @@ -379,13 +394,13 @@ plugins: python: options: filters: - - "!^_" + - "!^_[^_]" ``` ```md title="or in docs/some_page.md (local configuration)" ::: package.module options: - filters: [] + filters: public ``` ```python title="package/module.py" @@ -559,15 +574,13 @@ package [](){#option-summary} ## `summary` -[:octicons-tag-24: Insiders 1.2.0](../../insiders/changelog.md#1.2.0) - - **:octicons-package-24: Type bool | dict[str, bool] :material-equal: `False`{ title="default value" }** Whether to render summaries of modules, classes, functions (methods) and attributes. This option accepts a boolean (`yes`, `true`, `no`, `false` in YAML) -or a dictionary with one or more of the following keys: `attributes`, `functions`, `classes`, `modules`, +or a dictionary with one or more of the following keys: `attributes`, `functions`, `classes`, `modules`, `type_aliases`, with booleans as values. Class methods summary is (de)activated with the `functions` key. By default, `summary` is false, and by extension all values are false. diff --git a/docs/usage/configuration/signatures.md b/docs/usage/configuration/signatures.md index 98c865e5..109362e3 100644 --- a/docs/usage/configuration/signatures.md +++ b/docs/usage/configuration/signatures.md @@ -203,12 +203,6 @@ plugins: [](){#option-modernize_annotations} ## `modernize_annotations` -[:octicons-heart-fill-24:{ .pulse } Sponsors only](../../insiders/index.md){ .insiders } — -[:octicons-tag-24: Insiders 1.8.0](../../insiders/changelog.md#1.8.0) — -**This feature also requires -[Griffe Insiders](https://mkdocstrings.github.io/griffe/insiders/) -to be installed.** - - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -286,6 +280,58 @@ plugins: /// +[](){#option-overloads_only} +## `overloads_only` + +- **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** + +Whether to hide the implementation signature if the overloads are shown with [`show_overloads`][]. + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + overloads_only: false +``` + +```md title="or in docs/some_page.md (local configuration)" +::: path.to.module + options: + overloads_only: true +``` + +/// admonition | Preview + type: preview +//// tab | With overloads only +

    function

    + +```python +@overload +function(param1: int): ... +@overload +function(param1: str): ... +``` +Function docstring. + +//// +//// tab | Without overloads only +

    function

    + +```python +@overload +function(param1: int): ... +@overload +function(param1: str): ... +function(param1: str | int) +``` +Function docstring. + +//// + +/// + [](){#option-show_signature} ## `show_signature` @@ -380,6 +426,61 @@ function(param1, param2=None) //// /// +[](){#option-show_signature_type_parameters} +## `show_signature_type_parameters` + +- **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** + + +Show the type parameters in generic classes, methods, functions and type aliases +signatures. + +Since the heading can become quite long when type parameters are rendered, it is +usually best to [separate the signature][separate_signature] from the heading. + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + separate_signature: true + show_signature_annotations: true + show_signature_type_parameters: true +``` + +```md title="or in docs/some_page.md (local configuration)" +::: path.to.module + options: + separate_signature: true + show_signature_annotations: true + show_signature_type_parameters: false +``` + +/// admonition | Preview + type: preview + +//// tab | With signature type parameters +

    function

    + +```python +function[T, *R](param: T) -> tuple[*R] +``` + +

    Function docstring.

    +//// + +//// tab | Without signature type parameters +

    function

    + +```python +function(param: T) -> tuple[*R] +``` + +

    Function docstring.

    +//// +/// + [](){#option-separate_signature} ## `separate_signature` @@ -433,9 +534,54 @@ function(param1, param2=None) //// /// +[](){#option-show_attribute_values} +## `show_attribute_values` + +- **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** + + +Show initial values of attributes in classes. + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + show_attribute_values: true +``` + +```md title="or in docs/some_page.md (local configuration)" +::: path.to.object + options: + show_attribute_values: true +``` + +```python title="package/module.py" +class SomeClass: + def __init__(self): + self.some_attr = 1 +``` + +/// admonition | Preview + type: preview + +//// tab | With attribute values visible +

    SomeClass

    +

    some_attr = 1

    +//// + +//// tab | With attribute values hidden +

    SomeClass

    +

    some_attr

    +//// +/// + [](){#option-show_overloads} ## `show_overloads` +- **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** + Whether to render function / method overloads. ```yaml title="in mkdocs.yml (global configuration)" @@ -485,8 +631,6 @@ Function docstring. [](){#option-signature_crossrefs} ## `signature_crossrefs` -[:octicons-tag-24: Insiders 1.0.0](../../insiders/changelog.md#1.0.0) - Whether to render cross-references for type annotations in signatures. When signatures are separated from headings with the [`separate_signature`][] option diff --git a/docs/usage/customization.md b/docs/usage/customization.md index 9e13da66..d1e66b31 100644 --- a/docs/usage/customization.md +++ b/docs/usage/customization.md @@ -34,9 +34,10 @@ The following CSS classes are used in the generated HTML: - `doc-class`: on `div`s containing a class - `doc-function`: on `div`s containing a function - `doc-module`: on `div`s containing a module + - `doc-type_alias`: on `div`s containing a type alias - `doc-heading`: on objects headings - `doc-object-name`: on `span`s wrapping objects names/paths in the heading - - `doc-KIND-name`: as above, specific to the kind of object (module, class, function, attribute) + - `doc-KIND-name`: as above, specific to the kind of object (module, class, function, attribute, type_alias) - `doc-contents`: on `div`s wrapping the docstring then the children (if any) - `first`: same, but only on the root object's contents `div` - `doc-labels`: on `span`s wrapping the object's labels @@ -48,7 +49,7 @@ The following CSS classes are used in the generated HTML: - `doc-symbol`: on `code` tags of symbol types - `doc-symbol-heading`: on symbol types in headings - `doc-symbol-toc`: on symbol types in the ToC - - `doc-symbol-KIND`: specific to the kind of object (`module`, `class`, `function`, `method`, `attribute`) + - `doc-symbol-KIND`: specific to the kind of object (`module`, `class`, `function`, `method`, `attribute`, `type_alias`) /// admonition | Example with colorful labels type: example @@ -90,33 +91,41 @@ by overriding the values of our CSS variables, for example: ```css title="docs/css/mkdocstrings.css" [data-md-color-scheme="default"] { --doc-symbol-parameter-fg-color: #df50af; + --doc-symbol-type_parameter-fg-color: #df50af; --doc-symbol-attribute-fg-color: #0079ff; --doc-symbol-function-fg-color: #00dfa2; --doc-symbol-method-fg-color: #00dfa2; --doc-symbol-class-fg-color: #d1b619; + --doc-symbol-type_alias-fg-color: #d1b619; --doc-symbol-module-fg-color: #ff0060; --doc-symbol-parameter-bg-color: #df50af1a; + --doc-symbol-type_parameter-bg-color: #df50af1a; --doc-symbol-attribute-bg-color: #0079ff1a; --doc-symbol-function-bg-color: #00dfa21a; --doc-symbol-method-bg-color: #00dfa21a; --doc-symbol-class-bg-color: #d1b6191a; + --doc-symbol-type_alias-bg-color: #d1b6191a; --doc-symbol-module-bg-color: #ff00601a; } [data-md-color-scheme="slate"] { --doc-symbol-parameter-fg-color: #ffa8cc; + --doc-symbol-type_parameter-fg-color: #ffa8cc; --doc-symbol-attribute-fg-color: #963fb8; --doc-symbol-function-fg-color: #6d67e4; --doc-symbol-method-fg-color: #6d67e4; --doc-symbol-class-fg-color: #46c2cb; + --doc-symbol-type_alias-fg-color: #46c2cb; --doc-symbol-module-fg-color: #f2f7a1; --doc-symbol-parameter-bg-color: #ffa8cc1a; + --doc-symbol-type_parameter-bg-color: #ffa8cc1a; --doc-symbol-attribute-bg-color: #963fb81a; --doc-symbol-function-bg-color: #6d67e41a; --doc-symbol-method-bg-color: #6d67e41a; --doc-symbol-class-bg-color: #46c2cb1a; + --doc-symbol-type_alias-bg-color: #46c2cb1a; --doc-symbol-module-bg-color: #f2f7a11a; } ``` @@ -129,17 +138,21 @@ otherwise just override the variables at root level: ```css title="docs/css/mkdocstrings.css" :root { --doc-symbol-parameter-fg-color: #df50af; + --doc-symbol-type_parameter-fg-color: #df50af; --doc-symbol-attribute-fg-color: #0079ff; --doc-symbol-function-fg-color: #00dfa2; --doc-symbol-method-fg-color: #00dfa2; --doc-symbol-class-fg-color: #d1b619; + --doc-symbol-type_alias-fg-color: #d1b619; --doc-symbol-module-fg-color: #ff0060; --doc-symbol-parameter-bg-color: #df50af1a; + --doc-symbol-type_parameter-bg-color: #df50af1a; --doc-symbol-attribute-bg-color: #0079ff1a; --doc-symbol-function-bg-color: #00dfa21a; --doc-symbol-method-bg-color: #00dfa21a; --doc-symbol-class-bg-color: #d1b6191a; + --doc-symbol-type_alias-bg-color: #d1b6191a; --doc-symbol-module-bg-color: #ff00601a; } ``` @@ -151,33 +164,41 @@ otherwise just override the variables at root level: @@ -204,6 +225,10 @@ For example, to use single letters instead of truncated types: content: "P"; } +.doc-symbol-type_parameter::after { + content: "P"; +} + .doc-symbol-attribute::after { content: "A"; } @@ -220,6 +245,10 @@ For example, to use single letters instead of truncated types: content: "C"; } +.doc-symbol-type_alias::after { + content: "T"; +} + .doc-symbol-module::after { content: "M"; } @@ -234,6 +263,10 @@ For example, to use single letters instead of truncated types: content: "P"; } + #preview-symbol-names .doc-symbol-type_parameter::after { + content: "P"; + } + #preview-symbol-names .doc-symbol-attribute::after { content: "A"; } @@ -250,16 +283,22 @@ For example, to use single letters instead of truncated types: content: "C"; } + #preview-symbol-names .doc-symbol-type_alias::after { + content: "T"; + } + #preview-symbol-names .doc-symbol-module::after { content: "M"; } @@ -283,7 +322,7 @@ for filepath in sorted(path for path in Path(basedir).rglob("*") if "_base" not ) ``` -See them [in the repository](https://github.com/mkdocstrings/python/tree/master/src/mkdocstrings_handlers/python/templates/). +See them [in the repository](https://github.com/mkdocstrings/python/tree/main/src/mkdocstrings_handlers/python/templates/). See the general *mkdocstrings* documentation to learn how to override them: https://mkdocstrings.github.io/theming/#templates. Each one of these templates extends a base version in `theme/_base`. Example: @@ -324,6 +363,19 @@ Available context: - `config`: The handler configuration (dictionary). - `module`: The [Module][griffe.Module] instance. +#### `type_alias.html` + +- `heading`: The class heading. +- `labels`: The class labels. +- `signature`: The class signature. +- `contents`: The class contents: bases, docstring, source and children blocks. +- `docstring`: The class docstring. + +Available context: + +- `config`: The handler configuration (dictionary). +- `type_alias`: The [TypeAlias][griffe.TypeAlias] instance. + #### `class.html` - `heading`: The class heading. @@ -379,6 +431,8 @@ In `docstring/attributes.html`, `docstring/raises.html`, `docstring/receives.html`, `docstring/returns.html`, +`docstring/type_aliases.html`, +`docstring/type_parameters.html`, `docstring/warns.html`, and `docstring/yields.html`: @@ -439,4 +493,4 @@ div.doc-contents:not(.first) { padding-left: 25px; border-left: .05rem solid rgba(200, 200, 200, 0.2); } -``` \ No newline at end of file +``` diff --git a/docs/usage/index.md b/docs/usage/index.md index 84110936..3348a627 100644 --- a/docs/usage/index.md +++ b/docs/usage/index.md @@ -87,8 +87,8 @@ plugins: - mkdocstrings: handlers: python: - import: - - https://docs.python-requests.org/en/master/objects.inv + inventories: + - https://docs.python.org/3/objects.inv ``` When loading an inventory, you enable automatic cross-references @@ -150,9 +150,11 @@ plugins: [__all__]: https://docs.python.org/3/tutorial/modules.html#importing-from-a-package [](){#setting-locale} -#### `locale` +#### ~~`locale`~~ + +**Deprecated.** Use mkdocstrings' own `locale` setting. -The locale to use when translating template strings. The translation system is not fully ready yet, so we don't recommend setting the option for now. +~~The locale to use when translating template strings.~~ [](){#setting-paths} #### `paths` @@ -378,3 +380,107 @@ poetry install poetry run mkdocs build ``` /// + +## Recommended settings + +If you're in a hurry, here is the configuration we recommend for the Python handler. + +```yaml +- mkdocstrings: + handlers: + python: + + # Where to find your sources, see "Finding modules". + paths: [src] + + # Load object inventories to enable cross-references to other projects. + inventories: + - https://docs.python.org/3/objects.inv + # Also load inventories of your dependencies, generally served at + # https://docs-url-for-your-dependency/objects.inv. + + options: + + # DOCSTRINGS ------------------------------------------------------------- + docstring_options: + # Discard first line of `__init__` method docstrings, + # useful when merging such docstrings into their parent class'. + ignore_init_summary: true + + # Tables are generally too large, lists will fix this. + docstring_section_style: list + + # CROSS-REFERENCES ------------------------------------------------------- + # Enable relative crossrefs and scoped crossrefs, see Docstrings options. + relative_crossrefs: true # Sponsors only! + scoped_crossrefs: true # Sponsors only! + + # Enable cross-references in signatures. + signature_crossrefs: true + + # Unwrap actual types from `Annotated` type annotations. + unwrap_annotated: true + + # MEMBERS ---------------------------------------------------------------- + # Only render pulic symbols. + filters: public # Sponsors only! + # Comment the option otherwise to get the default filters. + + # Show class inherited members. + inherited_members: true + + # Render auto-generated summaries of attributes, functions, etc. + # at the start of each symbol's documentation. + summary: true + + # HEADINGS --------------------------------------------------------------- + # For auto-generated pages, one module per page, + # make the module heading be the H1 heading of the page. + heading_level: 1 + + # Render headings for parameters, making them linkable. + parameter_headings: true + + # Render headings for type parameters too. + type_parameter_headings: true + + # Always show the heading for the symbol you render with `::: id`. + show_root_heading: true + + # Only show the name of the symbols you inject render `::: id`. + show_root_full_path: false + + # Show the type of symbol (class, function, etc.) in the heading. + show_symbol_type_heading: true + + # Show the type of symbol (class, function, etc.) in the table of contents. + show_symbol_type_toc: true + + # SIGNATURES ------------------------------------------------------------- + # Format code to 80 + 10% margin (Black and Ruff defaults) + # in signatures and attribute value code blocks. + # Needs Black/Ruff installed. + line_length: 88 + + # Merge signature and docstring of `__init__` methods + # into their parent class signature and docstring. + merge_init_into_class: true + + # Render signatures and attribute values in a separate code block, + # below the symbol heading. + separate_signature: true + + # Show type annotations in signatures. + show_signature_annotations: true + + # Show type parameters in signatures. + show_signature_type_parameters: true + + # OTHER ------------------------------------------------------------------ + # Show backlinks to other documentation sections within each symbol. + backlinks: tree # Sponsors only! + + # Show base classes OR inheritance diagram. + show_bases: false + show_inheritance_diagram: true # Sponsors only! +``` diff --git a/duties.py b/duties.py index 9e516ce5..3aa9b662 100644 --- a/duties.py +++ b/duties.py @@ -3,17 +3,14 @@ from __future__ import annotations import os +import re import sys -from contextlib import contextmanager -from importlib.metadata import version as pkgversion from pathlib import Path from typing import TYPE_CHECKING from duty import duty, tools if TYPE_CHECKING: - from collections.abc import Iterator - from duty.context import Context @@ -24,25 +21,21 @@ 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 = "314" -def pyprefix(title: str) -> str: # noqa: D103 +def pyprefix(title: str) -> str: if MULTIRUN: prefix = f"(python{sys.version_info.major}.{sys.version_info.minor})" return f"{prefix:14}{title}" return title -@contextmanager -def material_insiders() -> Iterator[bool]: # noqa: D103 - if "+insiders" in pkgversion("mkdocs-material"): - os.environ["MATERIAL_INSIDERS"] = "true" - try: - yield True - finally: - os.environ.pop("MATERIAL_INSIDERS") - else: - yield False +def _get_changelog_version() -> str: + changelog_version_re = re.compile(r"^## \[(\d+\.\d+\.\d+)\].*$") + with Path(__file__).parent.joinpath("CHANGELOG.md").open("r", encoding="utf8") as file: + return next(filter(bool, map(changelog_version_re.match, file))).group(1) # type: ignore[union-attr] @duty @@ -53,6 +46,7 @@ def changelog(ctx: Context, bump: str = "") -> None: 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"]) @@ -60,7 +54,7 @@ def check(ctx: Context) -> None: """Check it all!""" -@duty +@duty(nofail=PY_VERSION == PY_DEV) def check_quality(ctx: Context) -> None: """Check the code quality.""" ctx.run( @@ -69,22 +63,26 @@ def check_quality(ctx: Context) -> None: ) -@duty +@duty( + nofail=PY_VERSION == PY_DEV, + skip_if=sys.version_info < (3, 13), + skip_reason=pyprefix("Skipped: docs require modern generics syntax"), +) def check_docs(ctx: Context) -> None: """Check if the documentation builds correctly.""" Path("htmlcov").mkdir(parents=True, exist_ok=True) Path("htmlcov/index.html").touch(exist_ok=True) - with material_insiders(): - ctx.run( - tools.mkdocs.build(strict=True, verbose=True), - title=pyprefix("Building documentation"), - ) + ctx.run( + tools.mkdocs.build(strict=True, verbose=True), + title=pyprefix("Building documentation"), + ) -@duty +@duty(nofail=PY_VERSION == PY_DEV) def check_types(ctx: Context) -> None: """Check that the code is correctly typed.""" os.environ["MYPYPATH"] = "src" + os.environ["FORCE_COLOR"] = "1" ctx.run( tools.mypy(*PY_SRC_LIST, config_file="config/mypy.ini"), title=pyprefix("Type-checking"), @@ -93,7 +91,7 @@ def check_types(ctx: Context) -> None: ) -@duty +@duty(nofail=PY_VERSION == PY_DEV) def check_api(ctx: Context, *cli_args: str) -> None: """Check for API breaking changes.""" ctx.run( @@ -103,7 +101,7 @@ def check_api(ctx: Context, *cli_args: str) -> None: ) -@duty +@duty(skip_if=sys.version_info < (3, 13), skip_reason=pyprefix("Skipped: docs require modern generics syntax")) def docs(ctx: Context, *cli_args: str, host: str = "127.0.0.1", port: int = 8000) -> None: """Serve the documentation (localhost:8000). @@ -111,48 +109,18 @@ def docs(ctx: Context, *cli_args: str, host: str = "127.0.0.1", port: int = 8000 host: The host to serve the docs from. port: The port to serve the docs on. """ - with material_insiders(): - ctx.run( - tools.mkdocs.serve(dev_addr=f"{host}:{port}").add_args(*cli_args), - title="Serving documentation", - capture=False, - ) - + ctx.run( + tools.mkdocs.serve(dev_addr=f"{host}:{port}").add_args(*cli_args), + title="Serving documentation", + capture=False, + ) -@duty -def docs_deploy(ctx: Context, *, force: bool = False) -> None: - """Deploy the documentation to GitHub pages. - Parameters: - force: Whether to force deployment, even from non-Insiders version. - """ +@duty(skip_if=sys.version_info < (3, 13), skip_reason=pyprefix("Skipped: docs require modern generics syntax")) +def docs_deploy(ctx: Context) -> None: + """Deploy the documentation to GitHub pages.""" os.environ["DEPLOY"] = "true" - with material_insiders() as insiders: - if not insiders: - ctx.run(lambda: False, title="Not deploying docs without Material for MkDocs Insiders!") - origin = ctx.run("git config --get remote.origin.url", silent=True, allow_overrides=False) - if "pawamoy-insiders/mkdocstrings-python" in origin: - ctx.run( - "git remote add upstream git@github.com:mkdocstrings/python", - silent=True, - nofail=True, - allow_overrides=False, - ) - ctx.run( - tools.mkdocs.gh_deploy(remote_name="upstream", force=True), - title="Deploying documentation", - ) - elif force: - ctx.run( - tools.mkdocs.gh_deploy(force=True), - title="Deploying documentation", - ) - else: - ctx.run( - lambda: False, - title="Not deploying docs from public repository (do that from insiders instead!)", - nofail=True, - ) + ctx.run(tools.mkdocs.gh_deploy(force=True), title="Deploying documentation") @duty @@ -195,17 +163,11 @@ def release(ctx: Context, version: str = "") -> None: Parameters: version: The new version number to use. """ - origin = ctx.run("git config --get remote.origin.url", silent=True) - if "pawamoy-insiders/mkdocstrings-python" in origin: - ctx.run( - lambda: False, - title="Not releasing from insiders repository (do that from public repo instead!)", - ) if not (version := (version or input("> Version to release: ")).strip()): ctx.run("false", title="A version must be provided") ctx.run("git add pyproject.toml CHANGELOG.md", title="Staging files", pty=PTY) ctx.run(["git", "commit", "-m", f"chore: Prepare release {version}"], title="Committing changes", pty=PTY) - ctx.run(f"git tag {version}", title="Tagging commit", pty=PTY) + ctx.run(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) @@ -218,16 +180,11 @@ def coverage(ctx: Context) -> None: ctx.run(tools.coverage.html(rcfile="config/coverage.ini")) -@duty -def test(ctx: Context, *cli_args: str, match: str = "", snapshot: str = "report") -> None: - """Run the test suite. - - Parameters: - match: A pytest expression to filter selected tests. - snapshot: Whether to "create", "fix", "trim", or "update" snapshots. - """ - py_version = f"{sys.version_info.major}{sys.version_info.minor}" - os.environ["COVERAGE_FILE"] = f".coverage.{py_version}" +@duty(nofail=PY_VERSION == PY_DEV) +def test(ctx: Context, *cli_args: str, snapshot: str = "report") -> None: # noqa: PT028 + """Run the test suite.""" + os.environ["COVERAGE_FILE"] = f".coverage.{PY_VERSION}" + os.environ["PYTHONWARNDEFAULTENCODING"] = "1" args = list(cli_args) if snapshot == "disable" or not snapshot: args = ["-n", "auto", "--inline-snapshot=disable"] @@ -237,7 +194,6 @@ def test(ctx: Context, *cli_args: str, match: str = "", snapshot: str = "report" tools.pytest( "tests", config_file="config/pytest.ini", - select=match, color="yes", ).add_args(*args), title=pyprefix("Running tests"), diff --git a/mkdocs.yml b/mkdocs.yml index 396f738f..64d0de3a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -37,19 +37,12 @@ nav: - Advanced: - Customization: usage/customization.md - Extensions: usage/extensions.md -# defer to gen-files + literate-nav -- API reference: - - mkdocstrings-python: reference/ +- API reference: reference/api.md - Development: - Contributing: contributing.md - Code of Conduct: code_of_conduct.md - # - Coverage report: coverage.md -- Insiders: - - insiders/index.md - - Getting started: - - Installation: insiders/installation.md - - Changelog: insiders/changelog.md -- mkdocstrings: https://mkdocstrings.github.io/ + - Coverage report: coverage.md + - mkdocstrings: https://mkdocstrings.github.io/ theme: name: material @@ -63,7 +56,8 @@ theme: - content.code.copy - content.tooltips - navigation.footer - - navigation.indexes + - navigation.instant.preview + - navigation.path - navigation.sections - navigation.tabs - navigation.tabs.sticky @@ -94,7 +88,6 @@ theme: extra_css: - css/material.css - css/mkdocstrings.css -- css/insiders.css extra_javascript: - js/feedback.js @@ -140,36 +133,34 @@ markdown_extensions: permalink: "¤" plugins: -- autorefs - search +- autorefs - markdown-exec -- gen-files: - scripts: - - scripts/gen_ref_nav.py -- literate-nav: - nav_file: SUMMARY.md -# - coverage +- section-index +- coverage - mkdocstrings: handlers: python: paths: [src, docs/snippets] - import: + inventories: - https://docs.python.org/3/objects.inv - https://mkdocstrings.github.io/objects.inv - https://mkdocstrings.github.io/autorefs/objects.inv - https://mkdocstrings.github.io/griffe/objects.inv - https://python-markdown.github.io/objects.inv options: + backlinks: tree docstring_options: ignore_init_summary: true docstring_section_style: list - extensions: - - scripts/griffe_extensions.py - filters: ["!^_"] + extensions: [scripts/griffe_extensions.py] + filters: public heading_level: 1 inherited_members: true + line_length: 88 merge_init_into_class: true parameter_headings: true + type_parameter_headings: true preload_modules: [mkdocstrings] relative_crossrefs: true scoped_crossrefs: true @@ -179,22 +170,34 @@ plugins: show_root_heading: true show_root_full_path: false show_signature_annotations: true - show_source: false + show_signature_type_parameters: true + show_source: true show_symbol_type_heading: true show_symbol_type_toc: true signature_crossrefs: true summary: true unwrap_annotated: true +- llmstxt: + full_output: llms-full.txt + sections: + Usage: + - index.md + API: + - reference/api.md - git-revision-date-localized: enabled: !ENV [DEPLOY, false] enable_creation_date: true type: timeago - minify: minify_html: !ENV [DEPLOY, false] -- group: - enabled: !ENV [MATERIAL_INSIDERS, false] - plugins: - - typeset +- redirects: + redirect_maps: + reference/mkdocstrings_handlers/python/index.md: reference/api.md + reference/mkdocstrings_handlers/python/config.md: reference/api.md#mkdocstrings_handlers.python.config + reference/mkdocstrings_handlers/python/debug.md: reference/api.md#mkdocstrings_handlers.python.debug + reference/mkdocstrings_handlers/python/handler.md: reference/api.md#mkdocstrings_handlers.python.handler + reference/mkdocstrings_handlers/python/rendering.md: reference/api.md#mkdocstrings_handlers.python.rendering +- typeset extra: social: diff --git a/pyproject.toml b/pyproject.toml index b4a453e4..2d6c7188 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,9 +6,10 @@ build-backend = "pdm.backend" name = "mkdocstrings-python" description = "A Python handler for mkdocstrings." authors = [{name = "Timothée Mazzucotelli", email = "dev@pawamoy.fr"}] -license = {text = "ISC"} +license = "ISC" +license-files = ["LICENSE"] readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" keywords = [] dynamic = ["version"] classifiers = [ @@ -17,7 +18,6 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -30,9 +30,9 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "mkdocstrings>=0.28", - "mkdocs-autorefs>=1.2", - "griffe>=0.49", + "mkdocstrings>=0.30", + "mkdocs-autorefs>=1.4", + "griffe>=1.13", "typing-extensions>=4.0; python_version < '3.11'", ] @@ -56,7 +56,7 @@ includes = ["src/mkdocstrings_handlers"] editable-backend = "editables" # Include as much as possible in the source distribution, to help redistributors. -excludes = ["**/.pytest_cache"] +excludes = ["**/.pytest_cache", "**/.mypy_cache"] source-includes = [ "config", "docs", @@ -78,37 +78,39 @@ data = [ ] [dependency-groups] -dev = [ - # maintenance +maintain = [ "build>=1.2", "git-changelog>=2.5", "twine>=5.1", - - # ci - "duty>=1.4", + "yore>=0.3.3", +] +ci = [ + "black>=25.1", + "duty>=1.6", "ruff>=0.4", "pytest>=8.2", "pytest-cov>=5.0", "pytest-randomly>=3.15", "pytest-xdist>=3.6", "beautifulsoup4>=4.12.3", - "inline-snapshot>=0.18", + "inline-snapshot>=0.25", "mypy>=1.10", "types-markdown>=3.6", "types-pyyaml>=6.0", - - # docs - "black>=24.4", +] + docs = [ "markdown-callouts>=0.4", "markdown-exec>=1.8", "mkdocs>=1.6", "mkdocs-coverage>=1.0", - "mkdocs-gen-files>=0.5", "mkdocs-git-revision-date-localized-plugin>=1.2", - "mkdocs-literate-nav>=0.6", + "mkdocs-llmstxt>=0.2", "mkdocs-material>=9.5", - "pydantic>=2.10", "mkdocs-minify-plugin>=0.8", + "mkdocs-redirects>=1.2", + "mkdocs-section-index>=0.3", + "mkdocstrings>=0.29", + "pydantic>=2.10", # YORE: EOL 3.10: Remove line. "tomli>=2.0; python_version < '3.11'", ] @@ -116,3 +118,6 @@ dev = [ [tool.inline-snapshot] storage-dir = "tests/snapshots" format-command = "ruff format --config config/ruff.toml --stdin-filename {filename}" + +[tool.uv] +default-groups = ["maintain", "ci", "docs"] diff --git a/scripts/gen_credits.py b/scripts/gen_credits.py index 85240535..6a81e239 100644 --- a/scripts/gen_credits.py +++ b/scripts/gen_credits.py @@ -1,4 +1,4 @@ -"""Script to generate the project's credits.""" +# Script to generate the project's credits. from __future__ import annotations @@ -27,7 +27,7 @@ pyproject = tomllib.load(pyproject_file) project = pyproject["project"] project_name = project["name"] -devdeps = [dep for dep in pyproject["dependency-groups"]["dev"] if not dep.startswith("-e")] +devdeps = [dep for group in pyproject["dependency-groups"].values() for dep in group if not dep.startswith("-e")] PackageMetadata = dict[str, Union[str, Iterable[str]]] Metadata = dict[str, PackageMetadata] diff --git a/scripts/gen_ref_nav.py b/scripts/gen_ref_nav.py deleted file mode 100644 index 6939e864..00000000 --- a/scripts/gen_ref_nav.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Generate the code reference pages and navigation.""" - -from pathlib import Path - -import mkdocs_gen_files - -nav = mkdocs_gen_files.Nav() -mod_symbol = '' - -root = Path(__file__).parent.parent -src = root / "src" - -for path in sorted(src.rglob("*.py")): - module_path = path.relative_to(src).with_suffix("") - doc_path = path.relative_to(src).with_suffix(".md") - full_doc_path = Path("reference", doc_path) - - parts = tuple(module_path.parts) - - if parts[-1] == "__init__": - parts = parts[:-1] - doc_path = doc_path.with_name("index.md") - full_doc_path = full_doc_path.with_name("index.md") - elif parts[-1].startswith("_"): - continue - - nav_parts = [f"{mod_symbol} {part}" for part in parts] - nav[tuple(nav_parts)] = doc_path.as_posix() - - with mkdocs_gen_files.open(full_doc_path, "w") as fd: - ident = ".".join(parts) - fd.write(f"---\ntitle: {ident}\n---\n\n::: {ident}") - - mkdocs_gen_files.set_edit_path(full_doc_path, ".." / path.relative_to(root)) - -with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file: - nav_file.writelines(nav.build_literate_nav()) diff --git a/scripts/get_version.py b/scripts/get_version.py index f4a30a8c..3c425a73 100644 --- a/scripts/get_version.py +++ b/scripts/get_version.py @@ -1,10 +1,15 @@ -"""Get current project version from Git tags or changelog.""" +# Get current project version from Git tags or changelog. import re from contextlib import suppress from pathlib import Path -from pdm.backend.hooks.version import SCMVersion, Version, default_version_formatter, get_version_from_scm +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" @@ -13,7 +18,6 @@ def get_version() -> str: - """Get current project version from Git tags or changelog.""" 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 diff --git a/scripts/griffe_extensions.py b/scripts/griffe_extensions.py index 7d283054..eb50f5f2 100644 --- a/scripts/griffe_extensions.py +++ b/scripts/griffe_extensions.py @@ -1,4 +1,4 @@ -"""Custom extensions for Griffe.""" +# Custom extensions for Griffe. from __future__ import annotations @@ -7,7 +7,7 @@ import griffe -logger = griffe.get_logger("griffe_extensions") +_logger = griffe.get_logger("griffe_extensions") class CustomFields(griffe.Extension): @@ -28,14 +28,14 @@ def on_attribute_instance( except AttributeError: return - if field.canonical_path == "mkdocstrings_handlers.python.config.Field": + if field.canonical_path == "mkdocstrings_handlers.python._internal.config._Field": description = next( attr.value for attr in field.arguments if isinstance(attr, griffe.ExprKeyword) and attr.name == "description" ) if not isinstance(description, str): - logger.warning(f"Field description of {attr.path} is not a static string") + _logger.warning(f"Field description of {attr.path} is not a static string") description = str(description) attr.docstring = griffe.Docstring( diff --git a/scripts/insiders.py b/scripts/insiders.py deleted file mode 100644 index a7da99bc..00000000 --- a/scripts/insiders.py +++ /dev/null @@ -1,206 +0,0 @@ -"""Functions related to Insiders funding goals.""" - -from __future__ import annotations - -import json -import logging -import os -import posixpath -from dataclasses import dataclass -from datetime import date, datetime, timedelta -from itertools import chain -from pathlib import Path -from typing import TYPE_CHECKING, cast -from urllib.error import HTTPError -from urllib.parse import urljoin -from urllib.request import urlopen - -import yaml - -if TYPE_CHECKING: - from collections.abc import Iterable - -logger = logging.getLogger(f"mkdocs.logs.{__name__}") - - -def human_readable_amount(amount: int) -> str: # noqa: D103 - str_amount = str(amount) - if len(str_amount) >= 4: # noqa: PLR2004 - return f"{str_amount[: len(str_amount) - 3]},{str_amount[-3:]}" - return str_amount - - -@dataclass -class Project: - """Class representing an Insiders project.""" - - name: str - url: str - - -@dataclass -class Feature: - """Class representing an Insiders feature.""" - - name: str - ref: str | None - since: date | None - project: Project | None - - def url(self, rel_base: str = "..") -> str | None: # noqa: D102 - if not self.ref: - return None - if self.project: - rel_base = self.project.url - return posixpath.join(rel_base, self.ref.lstrip("/")) - - def render(self, rel_base: str = "..", *, badge: bool = False) -> None: # noqa: D102 - new = "" - if badge: - recent = self.since and date.today() - self.since <= timedelta(days=60) # noqa: DTZ011 - if recent: - ft_date = self.since.strftime("%B %d, %Y") # type: ignore[union-attr] - new = f' :material-alert-decagram:{{ .new-feature .vibrate title="Added on {ft_date}" }}' - project = f"[{self.project.name}]({self.project.url}) — " if self.project else "" - feature = f"[{self.name}]({self.url(rel_base)})" if self.ref else self.name - print(f"- [{'x' if self.since else ' '}] {project}{feature}{new}") - - -@dataclass -class Goal: - """Class representing an Insiders goal.""" - - name: str - amount: int - features: list[Feature] - complete: bool = False - - @property - def human_readable_amount(self) -> str: # noqa: D102 - return human_readable_amount(self.amount) - - def render(self, rel_base: str = "..") -> None: # noqa: D102 - print(f"#### $ {self.human_readable_amount} — {self.name}\n") - if self.features: - for feature in self.features: - feature.render(rel_base) - print("") - else: - print("There are no features in this goal for this project. ") - print( - "[See the features in this goal **for all Insiders projects.**]" - f"(https://pawamoy.github.io/insiders/#{self.amount}-{self.name.lower().replace(' ', '-')})", - ) - - -def load_goals(data: str, funding: int = 0, project: Project | None = None) -> dict[int, Goal]: - """Load goals from JSON data. - - Parameters: - data: The JSON data. - funding: The current total funding, per month. - origin: The origin of the data (URL). - - Returns: - A dictionaries of goals, keys being their target monthly amount. - """ - goals_data = yaml.safe_load(data)["goals"] - return { - amount: Goal( - name=goal_data["name"], - amount=amount, - complete=funding >= amount, - features=[ - Feature( - name=feature_data["name"], - ref=feature_data.get("ref"), - since=feature_data.get("since") and datetime.strptime(feature_data["since"], "%Y/%m/%d").date(), # noqa: DTZ007 - project=project, - ) - for feature_data in goal_data["features"] - ], - ) - for amount, goal_data in goals_data.items() - } - - -def _load_goals_from_disk(path: str, funding: int = 0) -> dict[int, Goal]: - project_dir = os.getenv("MKDOCS_CONFIG_DIR", ".") - try: - data = Path(project_dir, path).read_text() - except OSError as error: - raise RuntimeError(f"Could not load data from disk: {path}") from error - return load_goals(data, funding) - - -def _load_goals_from_url(source_data: tuple[str, str, str], funding: int = 0) -> dict[int, Goal]: - project_name, project_url, data_fragment = source_data - data_url = urljoin(project_url, data_fragment) - try: - with urlopen(data_url) as response: # noqa: S310 - data = response.read() - except HTTPError as error: - raise RuntimeError(f"Could not load data from network: {data_url}") from error - return load_goals(data, funding, project=Project(name=project_name, url=project_url)) - - -def _load_goals(source: str | tuple[str, str, str], funding: int = 0) -> dict[int, Goal]: - if isinstance(source, str): - return _load_goals_from_disk(source, funding) - return _load_goals_from_url(source, funding) - - -def funding_goals(source: str | list[str | tuple[str, str, str]], funding: int = 0) -> dict[int, Goal]: - """Load funding goals from a given data source. - - Parameters: - source: The data source (local file path or URL). - funding: The current total funding, per month. - - Returns: - A dictionaries of goals, keys being their target monthly amount. - """ - if isinstance(source, str): - return _load_goals_from_disk(source, funding) - goals = {} - for src in source: - source_goals = _load_goals(src, funding) - for amount, goal in source_goals.items(): - if amount not in goals: - goals[amount] = goal - else: - goals[amount].features.extend(goal.features) - return {amount: goals[amount] for amount in sorted(goals)} - - -def feature_list(goals: Iterable[Goal]) -> list[Feature]: - """Extract feature list from funding goals. - - Parameters: - goals: A list of funding goals. - - Returns: - A list of features. - """ - return list(chain.from_iterable(goal.features for goal in goals)) - - -def load_json(url: str) -> str | list | dict: # noqa: D103 - with urlopen(url) as response: # noqa: S310 - return json.loads(response.read().decode()) - - -data_source = globals()["data_source"] -sponsor_url = "https://github.com/sponsors/pawamoy" -data_url = "https://raw.githubusercontent.com/pawamoy/sponsors/main" -numbers: dict[str, int] = load_json(f"{data_url}/numbers.json") # type: ignore[assignment] -sponsors: list[dict] = load_json(f"{data_url}/sponsors.json") # type: ignore[assignment] -current_funding = numbers["total"] -sponsors_count = numbers["count"] -goals = funding_goals(data_source, funding=current_funding) -ongoing_goals = [goal for goal in goals.values() if not goal.complete] -unreleased_features = sorted( - (ft for ft in feature_list(ongoing_goals) if ft.since), - key=lambda ft: cast(date, ft.since), - reverse=True, -) diff --git a/scripts/make.py b/scripts/make.py index 3d427296..b741a366 100755 --- a/scripts/make.py +++ b/scripts/make.py @@ -1,6 +1,4 @@ #!/usr/bin/env python3 -"""Management commands.""" - from __future__ import annotations import os @@ -16,7 +14,8 @@ from collections.abc import Iterator -PYTHON_VERSIONS = os.getenv("PYTHON_VERSIONS", "3.9 3.10 3.11 3.12 3.13 3.14").split() +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: @@ -69,16 +68,31 @@ def setup() -> None: 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"] - 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 + 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: @@ -146,19 +160,31 @@ def main() -> int: cmd = args.pop(0) if cmd == "run": - run("default", *args) + if not args: + print("make: run: missing command", file=sys.stderr) + return 1 + run("default", *args) # ty: ignore[missing-argument] return 0 if cmd == "multirun": - multirun(*args) + if not args: + print("make: run: missing command", file=sys.stderr) + return 1 + multirun(*args) # ty: ignore[missing-argument] return 0 if cmd == "allrun": - allrun(*args) + if not args: + print("make: run: missing command", file=sys.stderr) + return 1 + allrun(*args) # ty: ignore[missing-argument] return 0 if cmd.startswith("3."): - run(cmd, *args) + if not args: + print("make: run: missing command", file=sys.stderr) + return 1 + run(cmd, *args) # ty: ignore[missing-argument] return 0 opts = [] @@ -185,7 +211,14 @@ def main() -> int: if __name__ == "__main__": try: sys.exit(main()) - except subprocess.CalledProcessError as process: + except _RunError as process: if process.output: print(process.output, file=sys.stderr) - sys.exit(process.returncode) + 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/scripts/mkdocs_hooks.py b/scripts/mkdocs_hooks.py index 805055e0..739f93b3 100644 --- a/scripts/mkdocs_hooks.py +++ b/scripts/mkdocs_hooks.py @@ -1,4 +1,4 @@ -"""Generate a JSON schema of the Python handler configuration.""" +# Generate a JSON schema of the Python handler configuration. import json from dataclasses import dataclass, fields @@ -8,7 +8,7 @@ from mkdocs.config.defaults import MkDocsConfig from mkdocs.plugins import get_plugin_logger -from mkdocstrings_handlers.python.config import PythonInputConfig, PythonInputOptions +from mkdocstrings_handlers.python import PythonInputConfig, PythonInputOptions # TODO: Update when Pydantic supports Python 3.14 (sources and duties as well). try: @@ -17,13 +17,13 @@ TypeAdapter = None # type: ignore[assignment,misc] -logger = get_plugin_logger(__name__) +_logger = get_plugin_logger(__name__) def on_post_build(config: MkDocsConfig, **kwargs: Any) -> None: # noqa: ARG001 """Write `schema.json` to the site directory.""" if TypeAdapter is None: - logger.info("Pydantic is not installed, skipping JSON schema generation") + _logger.info("Pydantic is not installed, skipping JSON schema generation") return @dataclass @@ -35,12 +35,12 @@ class PythonHandlerSchema: schema["$schema"] = "https://json-schema.org/draft-07/schema" with open(join(config.site_dir, "schema.json"), "w") as file: json.dump(schema, file, indent=2) - logger.debug("Generated JSON schema") + _logger.debug("Generated JSON schema") autorefs = config["plugins"]["autorefs"] for field in fields(PythonInputConfig): if f"setting-{field.name}" not in autorefs._primary_url_map: - logger.warning(f"Handler setting `{field.name}` is not documented") + _logger.warning(f"Handler setting `{field.name}` is not documented") for field in fields(PythonInputOptions): if f"option-{field.name}" not in autorefs._primary_url_map: - logger.warning(f"Configuration option `{field.name}` is not documented") + _logger.warning(f"Configuration option `{field.name}` is not documented") diff --git a/src/mkdocstrings_handlers/python/__init__.py b/src/mkdocstrings_handlers/python/__init__.py index 0432a90d..dbad0355 100644 --- a/src/mkdocstrings_handlers/python/__init__.py +++ b/src/mkdocstrings_handlers/python/__init__.py @@ -1,5 +1,70 @@ """Python handler for mkdocstrings.""" -from mkdocstrings_handlers.python.handler import get_handler +from mkdocstrings_handlers.python._internal.config import ( + AutoStyleOptions, + GoogleStyleOptions, + Inventory, + NumpyStyleOptions, + PerStyleOptions, + PythonConfig, + PythonInputConfig, + PythonInputOptions, + PythonOptions, + SphinxStyleOptions, + SummaryOption, +) +from mkdocstrings_handlers.python._internal.handler import PythonHandler, get_handler +from mkdocstrings_handlers.python._internal.rendering import ( + AutorefsHook, + Order, + Tree, + do_as_attributes_section, + do_as_classes_section, + do_as_functions_section, + do_as_modules_section, + do_as_type_aliases_section, + do_backlink_tree, + do_filter_objects, + do_format_attribute, + do_format_code, + do_format_signature, + do_format_type_alias, + do_get_template, + do_order_members, + do_split_path, + do_stash_crossref, +) -__all__ = ["get_handler"] +__all__ = [ + "AutoStyleOptions", + "AutorefsHook", + "GoogleStyleOptions", + "Inventory", + "NumpyStyleOptions", + "Order", + "PerStyleOptions", + "PythonConfig", + "PythonHandler", + "PythonInputConfig", + "PythonInputOptions", + "PythonOptions", + "SphinxStyleOptions", + "SummaryOption", + "Tree", + "do_as_attributes_section", + "do_as_classes_section", + "do_as_functions_section", + "do_as_modules_section", + "do_as_type_aliases_section", + "do_backlink_tree", + "do_filter_objects", + "do_format_attribute", + "do_format_code", + "do_format_signature", + "do_format_type_alias", + "do_get_template", + "do_order_members", + "do_split_path", + "do_stash_crossref", + "get_handler", +] diff --git a/src/mkdocstrings_handlers/python/_internal/__init__.py b/src/mkdocstrings_handlers/python/_internal/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/mkdocstrings_handlers/python/config.py b/src/mkdocstrings_handlers/python/_internal/config.py similarity index 75% rename from src/mkdocstrings_handlers/python/config.py rename to src/mkdocstrings_handlers/python/_internal/config.py index 6607d01c..79ba87f9 100644 --- a/src/mkdocstrings_handlers/python/config.py +++ b/src/mkdocstrings_handlers/python/_internal/config.py @@ -1,4 +1,4 @@ -"""Configuration and options dataclasses.""" +# Configuration and options dataclasses. from __future__ import annotations @@ -7,7 +7,9 @@ from dataclasses import field, fields from typing import TYPE_CHECKING, Annotated, Any, Literal -from mkdocstrings.loggers import get_logger +from mkdocstrings import get_logger + +from mkdocstrings_handlers.python._internal.rendering import Order # noqa: TC001 # YORE: EOL 3.10: Replace block with line 2. if sys.version_info >= (3, 11): @@ -16,8 +18,9 @@ from typing_extensions import Self -logger = get_logger(__name__) +_logger = get_logger(__name__) +_DEFAULT_FILTERS = ["!^_[^_]"] try: # When Pydantic is available, use it to validate options (done automatically). @@ -36,17 +39,6 @@ if getattr(pydantic, "__version__", "1.").startswith("1."): raise ImportError # noqa: TRY301 - if sys.version_info < (3, 10): - try: - import eval_type_backport # noqa: F401 - except ImportError: - logger.debug( - "Pydantic needs the `eval-type-backport` package to be installed " - "for modern type syntax to work on Python 3.9. " - "Deactivating Pydantic validation for Python handler options.", - ) - raise - from inspect import cleandoc from pydantic import Field as BaseField @@ -54,7 +46,7 @@ _base_url = "https://mkdocstrings.github.io/python/usage" - def Field( # noqa: N802, D103 + def _Field( # noqa: N802 *args: Any, description: str, group: Literal["general", "headings", "members", "docstrings", "signatures"] | None = None, @@ -75,7 +67,7 @@ def _add_markdown_description(schema: dict[str, Any]) -> None: except ImportError: from dataclasses import dataclass # type: ignore[no-redef] - def Field(*args: Any, **kwargs: Any) -> None: # type: ignore[misc] # noqa: D103, N802 + def _Field(*args: Any, **kwargs: Any) -> None: # type: ignore[misc] # noqa: N802 pass @@ -83,20 +75,13 @@ def Field(*args: Any, **kwargs: Any) -> None: # type: ignore[misc] # noqa: D10 from collections.abc import MutableMapping -# YORE: EOL 3.9: Remove block. -_dataclass_options = {"frozen": True} -if sys.version_info >= (3, 10): - _dataclass_options["kw_only"] = True - - -# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. -@dataclass(**_dataclass_options) # type: ignore[call-overload] +@dataclass(frozen=True, kw_only=True) class GoogleStyleOptions: """Google style docstring options.""" ignore_init_summary: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Whether to ignore the summary in `__init__` methods' docstrings.", @@ -105,7 +90,7 @@ class GoogleStyleOptions: returns_multiple_items: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="""Whether to parse multiple items in `Yields` and `Returns` sections. @@ -118,7 +103,7 @@ class GoogleStyleOptions: returns_named_value: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="""Whether to parse `Yields` and `Returns` section items as name and description, rather than type and description. @@ -131,7 +116,7 @@ class GoogleStyleOptions: returns_type_in_property_summary: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Whether to parse the return type of properties at the beginning of their summary: `str: Summary of the property`.", @@ -140,7 +125,7 @@ class GoogleStyleOptions: receives_multiple_items: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="""Whether to parse multiple items in `Receives` sections. @@ -153,7 +138,7 @@ class GoogleStyleOptions: receives_named_value: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="""Whether to parse `Receives` section items as name and description, rather than type and description. @@ -166,7 +151,7 @@ class GoogleStyleOptions: trim_doctest_flags: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Whether to remove doctest flags from Python example blocks.", @@ -175,22 +160,39 @@ class GoogleStyleOptions: warn_unknown_params: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Warn about documented parameters not appearing in the signature.", ), ] = True + warn_missing_types: Annotated[ + bool, + _Field( + group="docstrings", + parent="docstring_options", + description="Warn about missing type/annotation for parameters, return values, etc.", + ), + ] = True -# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. -@dataclass(**_dataclass_options) # type: ignore[call-overload] + warnings: Annotated[ + bool, + _Field( + group="docstrings", + parent="docstring_options", + description="Generally enable/disable warnings when parsing docstrings.", + ), + ] = True + + +@dataclass(frozen=True, kw_only=True) class NumpyStyleOptions: """Numpy style docstring options.""" ignore_init_summary: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Whether to ignore the summary in `__init__` methods' docstrings.", @@ -199,7 +201,7 @@ class NumpyStyleOptions: trim_doctest_flags: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Whether to remove doctest flags from Python example blocks.", @@ -208,28 +210,71 @@ class NumpyStyleOptions: warn_unknown_params: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Warn about documented parameters not appearing in the signature.", ), ] = True + warn_missing_types: Annotated[ + bool, + _Field( + group="docstrings", + parent="docstring_options", + description="Warn about missing type/annotation for parameters, return values, etc.", + ), + ] = True + + warnings: Annotated[ + bool, + _Field( + group="docstrings", + parent="docstring_options", + description="Generally enable/disable warnings when parsing docstrings.", + ), + ] = True -# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. -@dataclass(**_dataclass_options) # type: ignore[call-overload] + +@dataclass(frozen=True, kw_only=True) class SphinxStyleOptions: """Sphinx style docstring options.""" + warn_unknown_params: Annotated[ + bool, + _Field( + group="docstrings", + parent="docstring_options", + description="Warn about documented parameters not appearing in the signature.", + ), + ] = True + + warn_missing_types: Annotated[ + bool, + _Field( + group="docstrings", + parent="docstring_options", + description="Warn about missing type/annotation for return values.", + ), + ] = True + + warnings: Annotated[ + bool, + _Field( + group="docstrings", + parent="docstring_options", + description="Generally enable/disable warnings when parsing docstrings.", + ), + ] = True -# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. -@dataclass(**_dataclass_options) # type: ignore[call-overload] + +@dataclass(frozen=True, kw_only=True) class PerStyleOptions: """Per style options.""" google: Annotated[ GoogleStyleOptions, - Field( + _Field( group="docstrings", parent="docstring_options", description="Google-style options.", @@ -238,7 +283,7 @@ class PerStyleOptions: numpy: Annotated[ NumpyStyleOptions, - Field( + _Field( group="docstrings", parent="docstring_options", description="Numpydoc-style options.", @@ -247,7 +292,7 @@ class PerStyleOptions: sphinx: Annotated[ SphinxStyleOptions, - Field( + _Field( group="docstrings", parent="docstring_options", description="Sphinx-style options.", @@ -266,14 +311,13 @@ def from_data(cls, **data: Any) -> Self: return cls(**data) -# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. -@dataclass(**_dataclass_options) # type: ignore[call-overload] +@dataclass(frozen=True, kw_only=True) class AutoStyleOptions: """Auto style docstring options.""" method: Annotated[ Literal["heuristics", "max_sections"], - Field( + _Field( group="docstrings", parent="docstring_options", description="The method to use to determine the docstring style.", @@ -282,7 +326,7 @@ class AutoStyleOptions: style_order: Annotated[ list[str], - Field( + _Field( group="docstrings", parent="docstring_options", description="The order of the docstring styles to try.", @@ -291,7 +335,7 @@ class AutoStyleOptions: default: Annotated[ str | None, - Field( + _Field( group="docstrings", parent="docstring_options", description="The default docstring style to use if no other style is detected.", @@ -300,7 +344,7 @@ class AutoStyleOptions: per_style_options: Annotated[ PerStyleOptions, - Field( + _Field( group="docstrings", parent="docstring_options", description="Per-style options.", @@ -315,14 +359,13 @@ def from_data(cls, **data: Any) -> Self: return cls(**data) -# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. -@dataclass(**_dataclass_options) # type: ignore[call-overload] +@dataclass(frozen=True, kw_only=True) class SummaryOption: """Summary option.""" attributes: Annotated[ bool, - Field( + _Field( group="members", parent="summary", description="Whether to render summaries of attributes.", @@ -331,7 +374,7 @@ class SummaryOption: functions: Annotated[ bool, - Field( + _Field( group="members", parent="summary", description="Whether to render summaries of functions (methods).", @@ -340,7 +383,7 @@ class SummaryOption: classes: Annotated[ bool, - Field( + _Field( group="members", parent="summary", description="Whether to render summaries of classes.", @@ -349,22 +392,30 @@ class SummaryOption: modules: Annotated[ bool, - Field( + _Field( group="members", parent="summary", description="Whether to render summaries of modules.", ), ] = False + type_aliases: Annotated[ + bool, + _Field( + group="members", + parent="summary", + description="Whether to render summaries of type aliases.", + ), + ] = False + -# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. -@dataclass(**_dataclass_options) # type: ignore[call-overload] +@dataclass(frozen=True, kw_only=True) class PythonInputOptions: """Accepted input options.""" allow_inspection: Annotated[ bool, - Field( + _Field( group="general", description="Whether to allow inspecting modules when visiting them is not possible.", ), @@ -372,7 +423,7 @@ class PythonInputOptions: force_inspection: Annotated[ bool, - Field( + _Field( group="general", description="Whether to force using dynamic analysis when loading data.", ), @@ -380,15 +431,23 @@ class PythonInputOptions: annotations_path: Annotated[ Literal["brief", "source", "full"], - Field( + _Field( group="signatures", description="The verbosity for annotations path: `brief` (recommended), `source` (as written in the source), or `full`.", ), ] = "brief" + backlinks: Annotated[ + Literal["flat", "tree", False], + _Field( + group="general", + description="Whether to render backlinks, and how.", + ), + ] = False + docstring_options: Annotated[ GoogleStyleOptions | NumpyStyleOptions | SphinxStyleOptions | AutoStyleOptions | None, - Field( + _Field( group="docstrings", description="""The options for the docstring parser. @@ -399,7 +458,7 @@ class PythonInputOptions: docstring_section_style: Annotated[ Literal["table", "list", "spacy"], - Field( + _Field( group="docstrings", description="The style used to render docstring sections.", ), @@ -407,7 +466,7 @@ class PythonInputOptions: docstring_style: Annotated[ Literal["auto", "google", "numpy", "sphinx"] | None, - Field( + _Field( group="docstrings", description="The docstring style to use: `auto`, `google`, `numpy`, `sphinx`, or `None`.", ), @@ -415,28 +474,35 @@ class PythonInputOptions: extensions: Annotated[ list[str | dict[str, Any]], - Field( + _Field( group="general", description="A list of Griffe extensions to load.", ), ] = field(default_factory=list) filters: Annotated[ - list[str], - Field( + list[str] | Literal["public"], + _Field( group="members", - description="""A list of filters applied to filter objects based on their name. + description="""A list of filters, or `"public"`. + + **List of filters** A filter starting with `!` will exclude matching objects instead of including them. The `members` option takes precedence over `filters` (filters will still be applied recursively to lower members in the hierarchy). + + **Filtering methods** + + The `public` method will include only public objects: + those added to `__all__` or not starting with an underscore (except for special methods/attributes). """, ), - ] = field(default_factory=lambda: ["!^_[^_]"]) + ] = field(default_factory=lambda: _DEFAULT_FILTERS.copy()) find_stubs_package: Annotated[ bool, - Field( + _Field( group="general", description="Whether to load stubs package (package-stubs) when extracting docstrings.", ), @@ -444,7 +510,7 @@ class PythonInputOptions: group_by_category: Annotated[ bool, - Field( + _Field( group="members", description="Group the object's children by categories: attributes, classes, functions, and modules.", ), @@ -452,7 +518,7 @@ class PythonInputOptions: heading: Annotated[ str, - Field( + _Field( group="headings", description="A custom string to override the autogenerated heading of the root object.", ), @@ -460,15 +526,23 @@ class PythonInputOptions: heading_level: Annotated[ int, - Field( + _Field( group="headings", description="The initial heading level to use.", ), ] = 2 + inheritance_diagram_direction: Annotated[ + Literal["TB", "TD", "BT", "RL", "LR"], + _Field( + group="docstrings", + description="The direction of the Mermaid chart presenting the inheritance diagram of a class.", + ), + ] = "TD" + inherited_members: Annotated[ bool | list[str], - Field( + _Field( group="members", description="""A boolean, or an explicit list of inherited members to render. @@ -480,7 +554,7 @@ class PythonInputOptions: line_length: Annotated[ int, - Field( + _Field( group="signatures", description="Maximum line length when formatting code/signatures.", ), @@ -488,7 +562,7 @@ class PythonInputOptions: members: Annotated[ list[str] | bool | None, - Field( + _Field( group="members", description="""A boolean, or an explicit list of members to render. @@ -500,20 +574,25 @@ class PythonInputOptions: ] = None members_order: Annotated[ - Literal["alphabetical", "source"], - Field( + Order | list[Order], + _Field( group="members", description="""The members ordering to use. - - `alphabetical`: order by the members names, + - `__all__`: order members according to `__all__` module attributes, if declared; + - `alphabetical`: order members alphabetically; - `source`: order members as they appear in the source file. + + Since `__all__` is a module-only attribute, it can't be used to sort class members, + therefore the `members_order` option accepts a list of ordering methods, + indicating ordering preferences. """, ), ] = "alphabetical" merge_init_into_class: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to merge the `__init__` method into the class' signature and docstring.", ), @@ -521,15 +600,23 @@ class PythonInputOptions: modernize_annotations: Annotated[ bool, - Field( + _Field( group="signatures", description="Whether to modernize annotations, for example `Optional[str]` into `str | None`.", ), ] = False + overloads_only: Annotated[ + bool, + _Field( + group="signatures", + description="Whether to hide the implementation signature if the overloads are shown.", + ), + ] = False + parameter_headings: Annotated[ bool, - Field( + _Field( group="headings", description="Whether to render headings for parameters (therefore showing parameters in the ToC).", ), @@ -537,7 +624,7 @@ class PythonInputOptions: preload_modules: Annotated[ list[str], - Field( + _Field( group="general", description="""Pre-load modules that are not specified directly in autodoc instructions (`::: identifier`). @@ -554,7 +641,7 @@ class PythonInputOptions: relative_crossrefs: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to enable the relative crossref syntax.", ), @@ -562,7 +649,7 @@ class PythonInputOptions: scoped_crossrefs: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to enable the scoped crossref ability.", ), @@ -570,7 +657,7 @@ class PythonInputOptions: show_overloads: Annotated[ bool, - Field( + _Field( group="signatures", description="Show the overloads of a function or method.", ), @@ -578,7 +665,7 @@ class PythonInputOptions: separate_signature: Annotated[ bool, - Field( + _Field( group="signatures", description="""Whether to put the whole signature in a code block below the heading. @@ -587,9 +674,17 @@ class PythonInputOptions: ), ] = False + show_attribute_values: Annotated[ + bool, + _Field( + group="signatures", + description="Show initial values of attributes in classes.", + ), + ] = True + show_bases: Annotated[ bool, - Field( + _Field( group="general", description="Show the base classes of a class.", ), @@ -597,7 +692,7 @@ class PythonInputOptions: show_category_heading: Annotated[ bool, - Field( + _Field( group="headings", description="When grouped by categories, show a heading for each category.", ), @@ -605,7 +700,7 @@ class PythonInputOptions: show_docstring_attributes: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Attributes' section in the object's docstring.", ), @@ -613,7 +708,7 @@ class PythonInputOptions: show_docstring_classes: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Classes' section in the object's docstring.", ), @@ -621,7 +716,7 @@ class PythonInputOptions: show_docstring_description: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the textual block (including admonitions) in the object's docstring.", ), @@ -629,7 +724,7 @@ class PythonInputOptions: show_docstring_examples: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Examples' section in the object's docstring.", ), @@ -637,7 +732,7 @@ class PythonInputOptions: show_docstring_functions: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Functions' or 'Methods' sections in the object's docstring.", ), @@ -645,7 +740,7 @@ class PythonInputOptions: show_docstring_modules: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Modules' section in the object's docstring.", ), @@ -653,7 +748,7 @@ class PythonInputOptions: show_docstring_other_parameters: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Other Parameters' section in the object's docstring.", ), @@ -661,7 +756,7 @@ class PythonInputOptions: show_docstring_parameters: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Parameters' section in the object's docstring.", ), @@ -669,7 +764,7 @@ class PythonInputOptions: show_docstring_raises: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Raises' section in the object's docstring.", ), @@ -677,7 +772,7 @@ class PythonInputOptions: show_docstring_receives: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Receives' section in the object's docstring.", ), @@ -685,15 +780,31 @@ class PythonInputOptions: show_docstring_returns: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Returns' section in the object's docstring.", ), ] = True + show_docstring_type_aliases: Annotated[ + bool, + _Field( + group="docstrings", + description="Whether to display the 'Type Aliases' section in the object's docstring.", + ), + ] = True + + show_docstring_type_parameters: Annotated[ + bool, + _Field( + group="docstrings", + description="Whether to display the 'Type Parameters' section in the object's docstring.", + ), + ] = True + show_docstring_warns: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Warns' section in the object's docstring.", ), @@ -701,7 +812,7 @@ class PythonInputOptions: show_docstring_yields: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Yields' section in the object's docstring.", ), @@ -709,7 +820,7 @@ class PythonInputOptions: show_if_no_docstring: Annotated[ bool, - Field( + _Field( group="docstrings", description="Show the object heading even if it has no docstring or children with docstrings.", ), @@ -717,7 +828,7 @@ class PythonInputOptions: show_inheritance_diagram: Annotated[ bool, - Field( + _Field( group="docstrings", description="Show the inheritance diagram of a class using Mermaid.", ), @@ -725,7 +836,7 @@ class PythonInputOptions: show_labels: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to show labels of the members.", ), @@ -733,7 +844,7 @@ class PythonInputOptions: show_object_full_path: Annotated[ bool, - Field( + _Field( group="docstrings", description="Show the full Python path of every object.", ), @@ -741,7 +852,7 @@ class PythonInputOptions: show_root_full_path: Annotated[ bool, - Field( + _Field( group="docstrings", description="Show the full Python path for the root object heading.", ), @@ -749,7 +860,7 @@ class PythonInputOptions: show_root_heading: Annotated[ bool, - Field( + _Field( group="headings", description="""Show the heading of the object at the root of the documentation tree. @@ -760,7 +871,7 @@ class PythonInputOptions: show_root_members_full_path: Annotated[ bool, - Field( + _Field( group="headings", description="Show the full Python path of the root members.", ), @@ -768,7 +879,7 @@ class PythonInputOptions: show_root_toc_entry: Annotated[ bool, - Field( + _Field( group="headings", description="If the root heading is not shown, at least add a ToC entry for it.", ), @@ -776,15 +887,23 @@ class PythonInputOptions: show_signature_annotations: Annotated[ bool, - Field( + _Field( group="signatures", description="Show the type annotations in methods and functions signatures.", ), ] = False + show_signature_type_parameters: Annotated[ + bool, + _Field( + group="signatures", + description="Show the type parameters in generic classes, methods, functions and type aliases signatures.", + ), + ] = False + show_signature: Annotated[ bool, - Field( + _Field( group="signatures", description="Show methods and functions signatures.", ), @@ -792,7 +911,7 @@ class PythonInputOptions: show_source: Annotated[ bool, - Field( + _Field( group="general", description="Show the source code of this object.", ), @@ -800,7 +919,7 @@ class PythonInputOptions: show_submodules: Annotated[ bool, - Field( + _Field( group="members", description="When rendering a module, show its submodules recursively.", ), @@ -808,7 +927,7 @@ class PythonInputOptions: show_symbol_type_heading: Annotated[ bool, - Field( + _Field( group="headings", description="Show the symbol type in headings (e.g. mod, class, meth, func and attr).", ), @@ -816,15 +935,23 @@ class PythonInputOptions: show_symbol_type_toc: Annotated[ bool, - Field( + _Field( group="headings", description="Show the symbol type in the Table of Contents (e.g. mod, class, methd, func and attr).", ), ] = False + skip_local_inventory: Annotated[ + bool, + _Field( + group="general", + description="Whether to prevent objects from being registered in the local objects inventory.", + ), + ] = False + signature_crossrefs: Annotated[ bool, - Field( + _Field( group="signatures", description="Whether to render cross-references for type annotations in signatures.", ), @@ -832,7 +959,7 @@ class PythonInputOptions: summary: Annotated[ bool | SummaryOption, - Field( + _Field( group="members", description="Whether to render summaries of modules, classes, functions (methods) and attributes.", ), @@ -840,15 +967,23 @@ class PythonInputOptions: toc_label: Annotated[ str, - Field( + _Field( group="headings", description="A custom string to override the autogenerated toc label of the root object.", ), ] = "" + type_parameter_headings: Annotated[ + bool, + _Field( + group="headings", + description="Whether to render headings for type parameters (therefore showing type parameters in the ToC).", + ), + ] = False + unwrap_annotated: Annotated[ bool, - Field( + _Field( group="signatures", description="Whether to unwrap `Annotated` types to show only the type without the annotations.", ), @@ -856,7 +991,7 @@ class PythonInputOptions: extra: Annotated[ dict[str, Any], - Field( + _Field( group="general", description="Extra options.", ), @@ -887,9 +1022,15 @@ def coerce(cls, **data: Any) -> MutableMapping[str, Any]: if "summary" in data: summary = data["summary"] if summary is True: - summary = SummaryOption(attributes=True, functions=True, classes=True, modules=True) + summary = SummaryOption(attributes=True, functions=True, classes=True, modules=True, type_aliases=True) elif summary is False: - summary = SummaryOption(attributes=False, functions=False, classes=False, modules=False) + summary = SummaryOption( + attributes=False, + functions=False, + classes=False, + modules=False, + type_aliases=False, + ) else: summary = SummaryOption(**summary) data["summary"] = summary @@ -901,43 +1042,46 @@ def from_data(cls, **data: Any) -> Self: return cls(**cls.coerce(**data)) -# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. -@dataclass(**_dataclass_options) # type: ignore[call-overload] +@dataclass(frozen=True, kw_only=True) class PythonOptions(PythonInputOptions): # type: ignore[override,unused-ignore] """Final options passed as template context.""" - filters: list[tuple[re.Pattern, bool]] = field(default_factory=list) # type: ignore[assignment] - """A list of filters applied to filter objects based on their name.""" + filters: list[tuple[re.Pattern, bool]] | Literal["public"] = field( # type: ignore[assignment] + default_factory=lambda: [ + (re.compile(filtr.removeprefix("!")), filtr.startswith("!")) for filtr in _DEFAULT_FILTERS + ], + ) + """A list of filters, or `"public"`.""" summary: SummaryOption = field(default_factory=SummaryOption) - """Whether to render summaries of modules, classes, functions (methods) and attributes.""" + """Whether to render summaries of modules, classes, functions (methods), attributes and type aliases.""" @classmethod def coerce(cls, **data: Any) -> MutableMapping[str, Any]: """Create an instance from a dictionary.""" - if "filters" in data: + if "filters" in data and not isinstance(data["filters"], str): + # Filters are `None` or a sequence of strings (tests use tuples). data["filters"] = [ - (re.compile(filtr.lstrip("!")), filtr.startswith("!")) for filtr in data["filters"] or () + (re.compile(filtr.removeprefix("!")), filtr.startswith("!")) for filtr in data["filters"] or () ] return super().coerce(**data) -# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. -@dataclass(**_dataclass_options) # type: ignore[call-overload] +@dataclass(frozen=True, kw_only=True) class Inventory: """An inventory.""" url: Annotated[ str, - Field( + _Field( parent="inventories", description="The URL of the inventory.", ), ] - base: Annotated[ + base_url: Annotated[ str | None, - Field( + _Field( parent="inventories", description="The base URL of the inventory.", ), @@ -945,7 +1089,7 @@ class Inventory: domains: Annotated[ list[str], - Field( + _Field( parent="inventories", description="The domains to load from the inventory.", ), @@ -953,37 +1097,38 @@ class Inventory: @property def _config(self) -> dict[str, Any]: - return {"base": self.base, "domains": self.domains} + return {"base_url": self.base_url, "domains": self.domains} -# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. -@dataclass(**_dataclass_options) # type: ignore[call-overload] +@dataclass(frozen=True, kw_only=True) class PythonInputConfig: """Python handler configuration.""" inventories: Annotated[ list[str | Inventory], - Field(description="The inventories to load."), + _Field(description="The inventories to load."), ] = field(default_factory=list) paths: Annotated[ list[str], - Field(description="The paths in which to search for Python packages."), + _Field(description="The paths in which to search for Python packages."), ] = field(default_factory=lambda: ["."]) load_external_modules: Annotated[ bool | None, - Field(description="Whether to always load external modules/packages."), + _Field(description="Whether to always load external modules/packages."), ] = None options: Annotated[ PythonInputOptions, - Field(description="Configuration options for collecting and rendering objects."), + _Field(description="Configuration options for collecting and rendering objects."), ] = field(default_factory=PythonInputOptions) locale: Annotated[ str | None, - Field(description="The locale to use when translating template strings."), + _Field( + description="Deprecated. Use mkdocstrings' own `locale` setting instead. The locale to use when translating template strings.", + ), ] = None @classmethod @@ -997,13 +1142,19 @@ def from_data(cls, **data: Any) -> Self: return cls(**cls.coerce(**data)) -# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. -@dataclass(**_dataclass_options) # type: ignore[call-overload] +@dataclass(frozen=True, kw_only=True) class PythonConfig(PythonInputConfig): # type: ignore[override,unused-ignore] """Python handler configuration.""" - inventories: list[Inventory] = field(default_factory=list) # type: ignore[assignment] - options: dict[str, Any] = field(default_factory=dict) # type: ignore[assignment] + inventories: Annotated[ + list[Inventory], + _Field(description="The object inventories to load."), + ] = field(default_factory=list) # type: ignore[assignment] + + options: Annotated[ + dict[str, Any], + _Field(description="Configuration options for collecting and rendering objects."), + ] = field(default_factory=dict) # type: ignore[assignment] @classmethod def coerce(cls, **data: Any) -> MutableMapping[str, Any]: diff --git a/src/mkdocstrings_handlers/python/debug.py b/src/mkdocstrings_handlers/python/_internal/debug.py similarity index 80% rename from src/mkdocstrings_handlers/python/debug.py rename to src/mkdocstrings_handlers/python/_internal/debug.py index e44f2be5..a3c99d75 100644 --- a/src/mkdocstrings_handlers/python/debug.py +++ b/src/mkdocstrings_handlers/python/_internal/debug.py @@ -1,5 +1,3 @@ -"""Debugging utilities.""" - from __future__ import annotations import os @@ -10,7 +8,7 @@ @dataclass -class Variable: +class _Variable: """Dataclass describing an environment variable.""" name: str @@ -20,7 +18,7 @@ class Variable: @dataclass -class Package: +class _Package: """Dataclass describing a Python package.""" name: str @@ -30,7 +28,7 @@ class Package: @dataclass -class Environment: +class _Environment: """Dataclass to store environment information.""" interpreter_name: str @@ -41,9 +39,9 @@ class Environment: """Path to Python executable.""" platform: str """Operating System.""" - packages: list[Package] + packages: list[_Package] """Installed packages.""" - variables: list[Variable] + variables: list[_Variable] """Environment variables.""" @@ -58,7 +56,7 @@ def _interpreter_name_version() -> tuple[str, str]: return "", "0.0.0" -def get_version(dist: str = "mkdocstrings-python") -> str: +def _get_version(dist: str = "mkdocstrings-python") -> str: """Get version of the given distribution. Parameters: @@ -73,28 +71,28 @@ def get_version(dist: str = "mkdocstrings-python") -> str: return "0.0.0" -def get_debug_info() -> Environment: +def _get_debug_info() -> _Environment: """Get debug/environment information. Returns: Environment information. """ py_name, py_version = _interpreter_name_version() - packages = ["mkdocs", "mkdocstrings", "mkdocstrings-python", "griffe"] + packages = ["mkdocstrings-python"] variables = ["PYTHONPATH", *[var for var in os.environ if var.startswith("MKDOCSTRINGS_PYTHON")]] - return Environment( + 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], + variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))], # ty: ignore[invalid-argument-type] + packages=[_Package(pkg, _get_version(pkg)) for pkg in packages], ) -def print_debug_info() -> None: +def _print_debug_info() -> None: """Print debug/environment information.""" - info = get_debug_info() + info = _get_debug_info() print(f"- __System__: {info.platform}") print(f"- __Python__: {info.interpreter_name} {info.interpreter_version} ({info.interpreter_path})") print("- __Environment variables__:") @@ -106,4 +104,4 @@ def print_debug_info() -> None: if __name__ == "__main__": - print_debug_info() + _print_debug_info() diff --git a/src/mkdocstrings_handlers/python/handler.py b/src/mkdocstrings_handlers/python/_internal/handler.py similarity index 75% rename from src/mkdocstrings_handlers/python/handler.py rename to src/mkdocstrings_handlers/python/_internal/handler.py index 0051be0f..bd95023f 100644 --- a/src/mkdocstrings_handlers/python/handler.py +++ b/src/mkdocstrings_handlers/python/_internal/handler.py @@ -1,4 +1,4 @@ -"""This module implements a handler for the Python language.""" +# This module implements a handler for the Python language. from __future__ import annotations @@ -10,7 +10,6 @@ from dataclasses import asdict from pathlib import Path from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar -from warnings import warn from griffe import ( AliasResolutionError, @@ -22,27 +21,27 @@ patch_loggers, ) from mkdocs.exceptions import PluginError -from mkdocstrings.handlers.base import BaseHandler, CollectionError, CollectorItem, HandlerOptions -from mkdocstrings.inventory import Inventory -from mkdocstrings.loggers import get_logger +from mkdocs_autorefs import BacklinkCrumb +from mkdocstrings import BaseHandler, CollectionError, CollectorItem, HandlerOptions, Inventory, get_logger -from mkdocstrings_handlers.python import rendering -from mkdocstrings_handlers.python.config import PythonConfig, PythonOptions +from mkdocstrings_handlers.python._internal import rendering +from mkdocstrings_handlers.python._internal.config import PythonConfig, PythonOptions if TYPE_CHECKING: - from collections.abc import Iterator, Mapping, MutableMapping, Sequence + from collections.abc import Iterable, Iterator, Mapping, MutableMapping, Sequence from mkdocs.config.defaults import MkDocsConfig + from mkdocs_autorefs import Backlink +# YORE: EOL 3.10: Replace block with line 2. if sys.version_info >= (3, 11): from contextlib import chdir else: - # TODO: remove once support for Python 3.10 is dropped from contextlib import contextmanager @contextmanager - def chdir(path: str) -> Iterator[None]: # noqa: D103 + def chdir(path: str) -> Iterator[None]: old_wd = os.getcwd() os.chdir(path) try: @@ -51,21 +50,11 @@ def chdir(path: str) -> Iterator[None]: # noqa: D103 os.chdir(old_wd) -logger = get_logger(__name__) +_logger = get_logger(__name__) patch_loggers(get_logger) -def _warn_extra_options(names: Sequence[str]) -> None: - warn( - "Passing extra options directly under `options` is deprecated. " - "Instead, pass them under `options.extra`, and update your templates. " - f"Current extra (unrecognized) options: {', '.join(sorted(names))}", - DeprecationWarning, - stacklevel=3, - ) - - class PythonHandler(BaseHandler): """The Python handler class.""" @@ -92,20 +81,18 @@ def __init__(self, config: PythonConfig, base_dir: Path, **kwargs: Any) -> None: super().__init__(**kwargs) self.config = config + """The handler configuration.""" self.base_dir = base_dir + """The base directory of the project.""" - # YORE: Bump 2: Replace block with `self.global_options = config.options`. - global_extra, global_options = PythonOptions._extract_extra(config.options) - if global_extra: - _warn_extra_options(global_extra.keys()) # type: ignore[arg-type] - self._global_extra = global_extra - self.global_options = global_options + self.global_options = config.options + """The global configuration options (in `mkdocs.yml`).""" # Warn if user overrides base templates. if self.custom_templates: for theme_dir in base_dir.joinpath(self.custom_templates, "python").iterdir(): if theme_dir.joinpath("_base").is_dir(): - logger.warning( + _logger.warning( f"Overriding base template '{theme_dir.name}/_base/