From 15c7cd6a6e512b5c118e491fe1a740c66a7f8e27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sun, 4 Jun 2023 19:42:47 +0200 Subject: [PATCH 01/28] chore: Template upgrade --- .copier-answers.yml | 2 +- .github/workflows/ci.yml | 22 ++++++++++++++++++++++ .github/workflows/dists.yml | 2 ++ Makefile | 3 +-- docs/css/mkdocstrings.css | 12 +++++------- scripts/gen_ref_nav.py | 6 ++++-- 6 files changed, 35 insertions(+), 12 deletions(-) diff --git a/.copier-answers.yml b/.copier-answers.yml index 62b9e3c8..71f63eb6 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: 0.15.7 +_commit: 0.15.10 _src_path: gh:pawamoy/copier-pdm.git author_email: pawamoy@pm.me author_fullname: Timothée Mazzucotelli diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b5ed158..050132c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,8 +52,29 @@ jobs: - name: Check for breaking changes in the API run: pdm run duty 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.8"}, + {"python-version": "3.9"}, + {"python-version": "3.10"}, + {"python-version": "3.11"} + ]' | tr -d '[:space:]' >> $GITHUB_OUTPUT + else + echo 'jobs=[]' >> $GITHUB_OUTPUT + fi + tests: + needs: exclude-test-jobs strategy: max-parallel: 4 matrix: @@ -67,6 +88,7 @@ jobs: - "3.9" - "3.10" - "3.11" + exclude: ${{ fromJSON(needs.exclude-test-jobs.outputs.jobs) }} runs-on: ${{ matrix.os }} diff --git a/.github/workflows/dists.yml b/.github/workflows/dists.yml index 41833b63..35a19d5e 100644 --- a/.github/workflows/dists.yml +++ b/.github/workflows/dists.yml @@ -12,6 +12,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Setup Python uses: actions/setup-python@v3 - name: Install build diff --git a/Makefile b/Makefile index b71d86ce..41569ba9 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,6 @@ .DEFAULT_GOAL := help SHELL := bash - -DUTY = $(shell [ -n "${VIRTUAL_ENV}" ] || echo pdm run) duty +DUTY := $(if $(VIRTUAL_ENV),,pdm run) duty args = $(foreach a,$($(subst -,_,$1)_args),$(if $(value $a),$a="$($a)")) check_quality_args = files diff --git a/docs/css/mkdocstrings.css b/docs/css/mkdocstrings.css index af758331..2db20680 100644 --- a/docs/css/mkdocstrings.css +++ b/docs/css/mkdocstrings.css @@ -5,24 +5,22 @@ div.doc-contents:not(.first) { } /* Mark external links as such. */ +a.external::after, a.autorefs-external::after { /* https://primer.style/octicons/arrow-up-right-24 */ - background-image: url('data:image/svg+xml,'); + mask-image: url('data:image/svg+xml,'); content: ' '; display: inline-block; vertical-align: middle; position: relative; - bottom: 0.1em; - margin-left: 0.2em; - margin-right: 0.1em; - height: 0.7em; - width: 0.7em; - border-radius: 100%; + height: 1em; + width: 1em; background-color: var(--md-typeset-a-color); } +a.external:hover::after, a.autorefs-external:hover::after { background-color: var(--md-accent-fg-color); } diff --git a/scripts/gen_ref_nav.py b/scripts/gen_ref_nav.py index 65e70cfe..78b9745c 100644 --- a/scripts/gen_ref_nav.py +++ b/scripts/gen_ref_nav.py @@ -5,6 +5,7 @@ import mkdocs_gen_files nav = mkdocs_gen_files.Nav() +mod_symbol = '' for path in sorted(Path("src").rglob("*.py")): module_path = path.relative_to("src").with_suffix("") @@ -20,13 +21,14 @@ elif parts[-1].startswith("_"): continue - nav[parts] = doc_path.as_posix() + 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"::: {ident}") - mkdocs_gen_files.set_edit_path(full_doc_path, Path("../") / path) + mkdocs_gen_files.set_edit_path(full_doc_path, ".." / path) with mkdocs_gen_files.open("reference/SUMMARY.txt", "w") as nav_file: nav_file.writelines(nav.build_literate_nav()) From f9799b92cb25dd4e02b6937bbfb438bfc858f889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sun, 4 Jun 2023 19:43:03 +0200 Subject: [PATCH 02/28] tests: Fix tests for new mkdocstrings-python version --- tests/test_handlers.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_handlers.py b/tests/test_handlers.py index 777173e8..239ddd50 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -85,19 +85,21 @@ def test_extended_templates(extended_templates: Path, plugin: MkdocstringsPlugin handler.env.cache = None extended_fallback_theme = extended_templates.joinpath(handler.fallback_theme) - extended_fallback_theme.mkdir() + extended_fallback_theme.mkdir(exist_ok=True) extended_fallback_theme.joinpath("new.html").write_text("extended fallback new") assert handler.env.get_template("new.html").render() == "extended fallback new" extended_theme = extended_templates.joinpath("mkdocs") - extended_theme.mkdir() + extended_theme.mkdir(exist_ok=True) extended_theme.joinpath("new.html").write_text("extended new") assert handler.env.get_template("new.html").render() == "extended new" base_fallback_theme = Path(search_paths[1]) + base_fallback_theme.mkdir(exist_ok=True) base_fallback_theme.joinpath("new.html").write_text("base fallback new") assert handler.env.get_template("new.html").render() == "base fallback new" base_theme = Path(search_paths[0]) + base_theme.mkdir(exist_ok=True) base_theme.joinpath("new.html").write_text("base new") assert handler.env.get_template("new.html").render() == "base new" From d072683a3998e21fe62ff4e5f5bd8161b7740275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Fri, 9 Jun 2023 00:17:42 +0200 Subject: [PATCH 03/28] chore: Template upgrade --- .copier-answers.yml | 2 +- docs/css/insiders.css | 23 +++++++++++++++++++++++ docs/insiders/index.md | 22 +++++++++++++++++++++- docs/license.md | 2 ++ scripts/gen_credits.py | 2 ++ 5 files changed, 49 insertions(+), 2 deletions(-) diff --git a/.copier-answers.yml b/.copier-answers.yml index 71f63eb6..f0546292 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: 0.15.10 +_commit: 0.15.11 _src_path: gh:pawamoy/copier-pdm.git author_email: pawamoy@pm.me author_fullname: Timothée Mazzucotelli diff --git a/docs/css/insiders.css b/docs/css/insiders.css index 81dbd756..ef43f6fd 100644 --- a/docs/css/insiders.css +++ b/docs/css/insiders.css @@ -95,4 +95,27 @@ a.insiders { transition: all .25s; vertical-align: bottom !important; width: 1.2rem; +} + +.premium-sponsors { + text-align: center; +} + +.premium-sponsors .silver img { + height: 140px; +} + +.premium-sponsors .bronze img { + height: 140px; +} + +.premium-sponsors .bronze p { + display: flex; + flex-wrap: wrap; + justify-content: center; +} + +.premium-sponsors .bronze a { + display: block; + flex-shrink: 0; } \ No newline at end of file diff --git a/docs/insiders/index.md b/docs/insiders/index.md index 0f5b0de9..b45acb22 100644 --- a/docs/insiders/index.md +++ b/docs/insiders/index.md @@ -116,13 +116,33 @@ You can cancel your sponsorship anytime.[^5] print_join_sponsors_button() ``` +
+
+ +
+ Bronze sponsors +

+ + + Material for MkDocs + + + Pydantic + + +

+
+
+ +

```python exec="1" session="insiders" print_sponsors() ``` -

+
+
If you sponsor publicly, you're automatically added here with a link to diff --git a/docs/license.md b/docs/license.md index cdacdfef..a873d2b5 100644 --- a/docs/license.md +++ b/docs/license.md @@ -1,3 +1,5 @@ +# License + ``` --8<-- "LICENSE" ``` diff --git a/scripts/gen_credits.py b/scripts/gen_credits.py index 85ac9041..bc01c0bd 100644 --- a/scripts/gen_credits.py +++ b/scripts/gen_credits.py @@ -89,6 +89,8 @@ def _render_credits() -> str: } template_text = dedent( """ + # Credits + These projects were used to build *{{ project_name }}*. **Thank you!** [`python`](https://www.python.org/) | From 91ca3fc69e95ee0caeb68fbc3433e003db2f3473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Fri, 9 Jun 2023 00:18:25 +0200 Subject: [PATCH 04/28] docs: Add 'used by' section to the README --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 52300c96..06872728 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,18 @@ Come have a chat or ask questions on our [Gitter channel](https://gitter.im/mkdo - **Reasonable defaults:** you should be able to just drop the plugin in your configuration and enjoy your auto-generated docs. +## Used by + +*mkdocstrings* is used by well-known companies, projects and scientific teams: +[Ansible](https://molecule.readthedocs.io/configuration/), +[Apache](https://streampipes.apache.org/docs/docs/python/latest/reference/client/client/), +[Google](https://docs.kidger.site/jaxtyping/api/runtime-type-checking/), +[Jitsi](https://jitsi.github.io/jiwer/reference/alignment/), +[Microsoft](https://microsoft.github.io/presidio/api/analyzer_python/), +[Prefect](https://docs.prefect.io/2.10.12/api-ref/prefect/agent/), +[Pydantic](https://docs.pydantic.dev/dev-v2/api/main/), +[and more...](https://github.com/mkdocstrings/mkdocstrings/network/dependents) + ## Installation With `pip`: From 3330755916784ffe9433e754bb466a7a559f4510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 15 Jul 2023 19:16:06 +0200 Subject: [PATCH 05/28] chore: Template upgrade --- .copier-answers.yml | 2 +- .github/workflows/ci.yml | 14 +++++--- .github/workflows/dists.yml | 32 ----------------- .github/workflows/release.yml | 45 +++++++++++++++++++++++ .gitpod.dockerfile | 1 - CONTRIBUTING.md | 13 ++++--- Makefile | 1 + config/ruff.toml | 2 +- docs/css/insiders.css | 12 ++++--- docs/insiders/goals.yml | 2 +- docs/insiders/index.md | 36 +++++-------------- docs/insiders/installation.md | 10 ++++++ docs/js/insiders.js | 67 +++++++++++++++++++++++++++++++++++ duties.py | 21 ++++++++--- mkdocs.yml | 17 ++++++--- pyproject.toml | 4 +-- scripts/insiders.py | 32 +++-------------- scripts/setup.sh | 6 ++-- 18 files changed, 196 insertions(+), 121 deletions(-) delete mode 100644 .github/workflows/dists.yml create mode 100644 .github/workflows/release.yml create mode 100644 docs/js/insiders.js diff --git a/.copier-answers.yml b/.copier-answers.yml index f0546292..ab242b1c 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: 0.15.11 +_commit: 0.16.4 _src_path: gh:pawamoy/copier-pdm.git author_email: pawamoy@pm.me author_fullname: Timothée Mazzucotelli diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 050132c8..f4a3f0cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: push: pull_request: branches: - - master + - main defaults: run: @@ -26,6 +26,9 @@ jobs: - name: Checkout uses: actions/checkout@v3 + - name: Fetch all tags + run: git fetch --depth=1 --tags + - name: Set up PDM uses: pdm-project/setup-pdm@v3 with: @@ -63,10 +66,10 @@ jobs: echo 'jobs=[ {"os": "macos-latest"}, {"os": "windows-latest"}, - {"python-version": "3.8"}, {"python-version": "3.9"}, {"python-version": "3.10"}, - {"python-version": "3.11"} + {"python-version": "3.11"}, + {"python-version": "3.12"} ]' | tr -d '[:space:]' >> $GITHUB_OUTPUT else echo 'jobs=[]' >> $GITHUB_OUTPUT @@ -83,14 +86,14 @@ jobs: - macos-latest - windows-latest python-version: - - "3.7" - "3.8" - "3.9" - "3.10" - "3.11" + - "3.12" exclude: ${{ fromJSON(needs.exclude-test-jobs.outputs.jobs) }} - runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.python-version == '3.12' }} steps: - name: Checkout @@ -100,6 +103,7 @@ jobs: uses: pdm-project/setup-pdm@v3 with: python-version: ${{ matrix.python-version }} + allow-python-prereleases: true - name: Resolving dependencies run: pdm lock -v --no-cross-platform -G ci-tests diff --git a/.github/workflows/dists.yml b/.github/workflows/dists.yml deleted file mode 100644 index 35a19d5e..00000000 --- a/.github/workflows/dists.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: dists - -on: push -permissions: - contents: write - -jobs: - build: - name: Build dists - runs-on: ubuntu-latest - if: github.repository_owner == 'pawamoy-insiders' - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Setup Python - uses: actions/setup-python@v3 - - name: Install build - run: python -m pip install build - - name: Build dists - run: python -m build - - name: Upload dists artifact - uses: actions/upload-artifact@v3 - with: - name: mkdocstrings-insiders - path: ./dist/* - - name: Create release and upload assets - uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') - with: - files: ./dist/* diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..1baebea4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,45 @@ +name: release + +on: push +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Fetch all tags + run: git fetch --depth=1 --tags + - name: Setup Python + uses: actions/setup-python@v4 + - name: Install build + if: github.repository_owner == 'pawamoy-insiders' + run: python -m pip install build + - name: Build dists + if: github.repository_owner == 'pawamoy-insiders' + run: python -m build + - name: Upload dists artifact + uses: actions/upload-artifact@v3 + if: github.repository_owner == 'pawamoy-insiders' + with: + name: mkdocstrings-insiders + path: ./dist/* + - name: Install git-changelog + if: github.repository_owner != 'pawamoy-insiders' + run: pip install git-changelog + - name: Prepare release notes + if: github.repository_owner != 'pawamoy-insiders' + run: git-changelog --release-notes > release-notes.md + - name: Create release with assets + uses: softprops/action-gh-release@v1 + if: github.repository_owner == 'pawamoy-insiders' + with: + files: ./dist/* + - name: Create release + uses: softprops/action-gh-release@v1 + if: github.repository_owner != 'pawamoy-insiders' + with: + body_path: release-notes.md diff --git a/.gitpod.dockerfile b/.gitpod.dockerfile index 33f285c2..0e6d9d35 100644 --- a/.gitpod.dockerfile +++ b/.gitpod.dockerfile @@ -1,7 +1,6 @@ FROM gitpod/workspace-full USER gitpod ENV PIP_USER=no -ENV PYTHON_VERSIONS= RUN pip3 install pipx; \ pipx install pdm; \ pipx ensurepath diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4ecc0fa0..0b86ff4b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,20 +39,19 @@ Run `make help` to see all the available actions! This project uses [duty](https://github.com/pawamoy/duty) to run tasks. A Makefile is also provided. The Makefile will try to run certain tasks on multiple Python versions. If for some reason you don't want to run the task -on multiple Python versions, you can do one of the following: - -1. `export PYTHON_VERSIONS= `: this will run the task - with only the current Python version -2. run the task directly with `pdm run duty TASK` +on multiple Python versions, you run the task directly with `pdm run duty TASK`. The Makefile detects if a virtual environment is activated, so `make` will work the same with the virtualenv activated or not. +If you work in VSCode, +[see examples of tasks and run configurations](https://pawamoy.github.io/copier-pdm/work/#vscode-setup). + ## Development As usual: -1. create a new branch: `git checkout -b feature-or-bugfix-name` +1. create a new branch: `git switch -c feature-or-bugfix-name` 1. edit the code and/or the documentation **Before committing:** @@ -138,7 +137,7 @@ git commit --fixup=SHA Once all the changes are approved, you can squash your commits: ```bash -git rebase -i --autosquash master +git rebase -i --autosquash main ``` And force-push: diff --git a/Makefile b/Makefile index 41569ba9..5696baac 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ .DEFAULT_GOAL := help SHELL := bash DUTY := $(if $(VIRTUAL_ENV),,pdm run) duty +export PDM_MULTIRUN_VERSIONS ?= 3.8 3.9 3.10 3.11 3.12 args = $(foreach a,$($(subst -,_,$1)_args),$(if $(value $a),$a="$($a)")) check_quality_args = files diff --git a/config/ruff.toml b/config/ruff.toml index 53824875..8f390641 100644 --- a/config/ruff.toml +++ b/config/ruff.toml @@ -1,4 +1,4 @@ -target-version = "py37" +target-version = "py38" line-length = 132 exclude = [ "fixtures", diff --git a/docs/css/insiders.css b/docs/css/insiders.css index ef43f6fd..b5547bd1 100644 --- a/docs/css/insiders.css +++ b/docs/css/insiders.css @@ -101,21 +101,25 @@ a.insiders { text-align: center; } -.premium-sponsors .silver img { +#silver-sponsors img { height: 140px; } -.premium-sponsors .bronze img { +#bronze-sponsors img { height: 140px; } -.premium-sponsors .bronze p { +#bronze-sponsors p { display: flex; flex-wrap: wrap; justify-content: center; } -.premium-sponsors .bronze a { +#bronze-sponsors a { display: block; flex-shrink: 0; +} + +.sponsors-total { + font-weight: bold; } \ No newline at end of file diff --git a/docs/insiders/goals.yml b/docs/insiders/goals.yml index 896b9240..a96ac51b 100644 --- a/docs/insiders/goals.yml +++ b/docs/insiders/goals.yml @@ -1 +1 @@ -goals: {} +goals: {} \ No newline at end of file diff --git a/docs/insiders/index.md b/docs/insiders/index.md index b45acb22..73021f1c 100644 --- a/docs/insiders/index.md +++ b/docs/insiders/index.md @@ -68,10 +68,10 @@ data_source = [ ```python exec="1" session="insiders" print(f"""The moment you become a sponsor, you'll get **immediate -access to {len(completed_features)} additional features** that you can start using right away, and +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 completed_features: +for feature in unreleased_features: feature.render(badge=True) ``` @@ -112,34 +112,17 @@ You can cancel your sponsorship anytime.[^5] regarding your payment, and GitHub doesn't offer refunds, sponsorships are non-refundable. -```python exec="1" session="insiders" -print_join_sponsors_button() -``` +[:octicons-heart-fill-24:{ .pulse }   Join our awesome sponsors](https://github.com/sponsors/pawamoy){ .md-button .md-button--primary }
-
- Bronze sponsors -

- - - Material for MkDocs - - - Pydantic - - -

-
+

-
-```python exec="1" session="insiders" -print_sponsors() -``` +


@@ -152,11 +135,7 @@ print_sponsors() afterwards.
-## Funding - -```python exec="1" session="insiders" -print(f"Current funding is at **$ {human_readable_amount(current_funding)} a month**.") -``` +## Funding ### Goals @@ -240,3 +219,6 @@ by the [ISC License][license]. However, we kindly ask you to respect our [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/ [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 index 9852182b..368f1b47 100644 --- a/docs/insiders/installation.md +++ b/docs/insiders/installation.md @@ -13,6 +13,16 @@ you need to [become an eligible sponsor] of @pawamoy on GitHub. ## 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). + ### with pip (ssh/https) *mkdocstrings Insiders* can be installed with `pip` [using SSH][using ssh]: diff --git a/docs/js/insiders.js b/docs/js/insiders.js new file mode 100644 index 00000000..03bcb404 --- /dev/null +++ b/docs/js/insiders.js @@ -0,0 +1,67 @@ +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 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} + + `; + } + }); + }); + getJSON(dataURL + '/sponsorsBronze.json', function (err, sponsors) { + const bronzeSponsors = document.getElementById("bronze-sponsors"); + if (sponsors) { + let html = ''; + html += 'Bronze sponsors

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

' + bronzeSponsors.innerHTML = html; + } + }); +} diff --git a/duties.py b/duties.py index 77410349..2e1248bf 100644 --- a/duties.py +++ b/duties.py @@ -108,6 +108,7 @@ def check_quality(ctx: Context) -> None: ctx.run( ruff.check(*PY_SRC_LIST, config="config/ruff.toml"), title=pyprefix("Checking code quality"), + command=f"ruff check --config config/ruff.toml {PY_SRC}", ) @@ -125,7 +126,11 @@ def check_dependencies(ctx: Context) -> None: allow_overrides=False, ) - ctx.run(safety.check(requirements), title="Checking dependencies") + ctx.run( + safety.check(requirements), + title="Checking dependencies", + command="pdm export -f requirements --without-hashes | safety check --stdin", + ) @duty @@ -137,7 +142,12 @@ def check_docs(ctx: Context) -> None: """ Path("htmlcov").mkdir(parents=True, exist_ok=True) Path("htmlcov/index.html").touch(exist_ok=True) - ctx.run(mkdocs.build(strict=True, config_file=mkdocs_config()), title=pyprefix("Building documentation")) + config = mkdocs_config() + ctx.run( + mkdocs.build(strict=True, config_file=config, verbose=True), + title=pyprefix("Building documentation"), + command=f"mkdocs build -vsf {config}", + ) @duty @@ -151,6 +161,7 @@ def check_types(ctx: Context) -> None: ctx.run( mypy.run(*PY_SRC_LIST, config_file="config/mypy.ini"), title=pyprefix("Type-checking"), + command=f"mypy --config-file config/mypy.ini {PY_SRC}", ) @@ -165,8 +176,9 @@ def check_api(ctx: Context) -> None: griffe_check = lazy(g_check, name="griffe.check") ctx.run( - griffe_check("mkdocstrings", search_paths=["src"]), + griffe_check("mkdocstrings", search_paths=["src"], color=True), title="Checking for API breaking changes", + command="griffe check -ssrc mkdocstrings", nofail=True, ) @@ -298,6 +310,7 @@ def test(ctx: Context, match: str = "") -> None: py_version = f"{sys.version_info.major}{sys.version_info.minor}" os.environ["COVERAGE_FILE"] = f".coverage.{py_version}" ctx.run( - pytest.run("-n", "auto", "tests", config_file="config/pytest.ini", select=match), + pytest.run("-n", "auto", "tests", config_file="config/pytest.ini", select=match, color="yes"), title=pyprefix("Running tests"), + command=f"pytest -c config/pytest.ini -n auto -k{match!r} --color=yes tests", ) diff --git a/mkdocs.yml b/mkdocs.yml index f544387c..d7500280 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,11 +2,11 @@ site_name: "mkdocstrings" site_description: "Automatic documentation from sources, for MkDocs." site_url: "https://mkdocstrings.github.io/" repo_url: "https://github.com/mkdocstrings/mkdocstrings" -edit_uri: "blob/master/docs/" repo_name: "mkdocstrings/mkdocstrings" site_dir: "site" watch: [mkdocs.yml, README.md, CONTRIBUTING.md, CHANGELOG.md, src/mkdocstrings] copyright: Copyright © 2019 Timothée Mazzucotelli +edit_uri: edit/main/docs/ nav: - Home: @@ -26,7 +26,8 @@ nav: - Recipes: recipes.md - Troubleshooting: troubleshooting.md # defer to gen-files + literate-nav -- Code Reference: reference/ +- API reference: + - mkdocstrings: reference/ - Development: - Contributing: contributing.md - Code of Conduct: code_of_conduct.md @@ -124,10 +125,18 @@ plugins: - https://installer.readthedocs.io/en/stable/objects.inv # demonstration purpose in the docs - https://mkdocstrings.github.io/autorefs/objects.inv options: - separate_signature: true - merge_init_into_class: true docstring_options: ignore_init_summary: true + docstring_section_style: list + heading_level: 1 + merge_init_into_class: true + separate_signature: true + show_root_heading: true + show_root_full_path: false + show_signature_annotations: true + show_symbol_type_heading: true + show_symbol_type_toc: true + signature_crossrefs: true - git-committers: enabled: !ENV [DEPLOY, false] repository: mkdocstrings/mkdocstrings diff --git a/pyproject.toml b/pyproject.toml index a36f6d25..d43ac7ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ description = "Automatic documentation from sources, for MkDocs." authors = [{name = "Timothée Mazzucotelli", email = "pawamoy@pm.me"}] license = {text = "ISC"} readme = "README.md" -requires-python = ">=3.7" +requires-python = ">=3.8" keywords = ["mkdocs", "mkdocs-plugin", "docstrings", "autodoc", "documentation"] dynamic = ["version"] classifiers = [ @@ -17,11 +17,11 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Documentation", "Topic :: Software Development", "Topic :: Software Development :: Documentation", diff --git a/scripts/insiders.py b/scripts/insiders.py index 0d23a45a..6f8d0d84 100644 --- a/scripts/insiders.py +++ b/scripts/insiders.py @@ -9,7 +9,6 @@ from datetime import date, datetime, timedelta from itertools import chain from pathlib import Path -from textwrap import dedent from typing import Iterable, cast from urllib.error import HTTPError from urllib.parse import urljoin @@ -101,7 +100,7 @@ def load_goals(data: str, funding: int = 0, project: Project | None = None) -> d Feature( name=feature_data["name"], ref=feature_data["ref"], - since=feature_data["since"] + since=feature_data.get("since") and datetime.strptime(feature_data["since"], "%Y/%m/%d").date(), # noqa: DTZ007 project=project, ) @@ -185,32 +184,9 @@ def load_json(url: str) -> str | list | dict: # noqa: D103 current_funding = numbers["total"] sponsors_count = numbers["count"] goals = funding_goals(data_source, funding=current_funding) -all_features = feature_list(goals.values()) -completed_features = sorted( - (ft for ft in all_features if ft.since), +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, ) - - -def print_join_sponsors_button() -> None: # noqa: D103 - btn_classes = "{ .md-button .md-button--primary }" - print( - dedent( - f""" - [:octicons-heart-fill-24:{{ .pulse }} -   Join our {sponsors_count} awesome sponsors]({sponsor_url}){btn_classes} - """, - ), - ) - - -def print_sponsors() -> None: # noqa: D103 - private_sponsors_count = sponsors_count - len(sponsors) - for sponsor in sponsors: - print( - f"""""" - f"""""", - ) - if private_sponsors_count: - print(f"""+{private_sponsors_count}""") diff --git a/scripts/setup.sh b/scripts/setup.sh index f6c90de5..4b4cb0fb 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash set -e -PYTHON_VERSIONS="${PYTHON_VERSIONS-3.7 3.8 3.9 3.10 3.11}" - if ! command -v pdm &>/dev/null; then if ! command -v pipx &>/dev/null; then python3 -m pip install --user pipx @@ -13,8 +11,8 @@ if ! pdm self list 2>/dev/null | grep -q pdm-multirun; then pdm install --plugins fi -if [ -n "${PYTHON_VERSIONS}" ]; then - pdm multirun -vi ${PYTHON_VERSIONS// /,} pdm install --dev +if [ -n "${PDM_MULTIRUN_VERSIONS}" ]; then + pdm multirun -v pdm install --dev else pdm install --dev fi From 676469996f22342490123a94738ccd6409f81c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 17 Jul 2023 16:36:23 +0200 Subject: [PATCH 06/28] tests: Don't remove files from installed packages Issue #574: https://github.com/mkdocstrings/mkdocstrings/issues/574 --- tests/test_handlers.py | 43 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/tests/test_handlers.py b/tests/test_handlers.py index 239ddd50..510ebd8a 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -2,17 +2,17 @@ from __future__ import annotations -from contextlib import suppress -from pathlib import Path from typing import TYPE_CHECKING import pytest from jinja2.exceptions import TemplateNotFound from markdown import Markdown -from mkdocstrings.handlers.base import BaseRenderer, Highlighter +from mkdocstrings.handlers.base import Highlighter if TYPE_CHECKING: + from pathlib import Path + from mkdocstrings.plugin import MkdocstringsPlugin @@ -53,30 +53,25 @@ def test_highlighter_basic(extension_name: str | None, inline: bool) -> None: assert "import foo" not in actual # Highlighting has split it up. -@pytest.fixture(name="extended_templates") -def fixture_extended_templates(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> Path: # noqa: D103 - monkeypatch.setattr(BaseRenderer, "get_extended_templates_dirs", lambda self, handler: [tmp_path]) - return tmp_path - - -def test_extended_templates(extended_templates: Path, plugin: MkdocstringsPlugin) -> None: +def test_extended_templates(tmp_path: Path, plugin: MkdocstringsPlugin) -> None: """Test the extended templates functionality. Parameters: - extended_templates: Temporary folder. + tmp_path: Temporary folder. plugin: Instance of our plugin. """ handler = plugin._handlers.get_handler("python") # type: ignore[union-attr] - # assert mocked method added temp path to loader - search_paths = handler.env.loader.searchpath # type: ignore[union-attr] - assert any(str(extended_templates) in path for path in search_paths) + # monkeypatch Jinja env search path + search_paths = [ + base_theme := tmp_path / "base_theme", + base_fallback_theme := tmp_path / "base_fallback_theme", + extended_theme := tmp_path / "extended_theme", + extended_fallback_theme := tmp_path / "extended_fallback_theme", + ] + handler.env.loader.searchpath = search_paths # type: ignore[union-attr] # assert "new" template is not found - for path in search_paths: - # TODO: use missing_ok=True once support for Python 3.7 is dropped - with suppress(FileNotFoundError): - Path(path).joinpath("new.html").unlink() with pytest.raises(expected_exception=TemplateNotFound): handler.env.get_template("new.html") @@ -84,22 +79,18 @@ def test_extended_templates(extended_templates: Path, plugin: MkdocstringsPlugin # start with last one and go back up handler.env.cache = None - extended_fallback_theme = extended_templates.joinpath(handler.fallback_theme) - extended_fallback_theme.mkdir(exist_ok=True) + extended_fallback_theme.mkdir() extended_fallback_theme.joinpath("new.html").write_text("extended fallback new") assert handler.env.get_template("new.html").render() == "extended fallback new" - extended_theme = extended_templates.joinpath("mkdocs") - extended_theme.mkdir(exist_ok=True) + extended_theme.mkdir() extended_theme.joinpath("new.html").write_text("extended new") assert handler.env.get_template("new.html").render() == "extended new" - base_fallback_theme = Path(search_paths[1]) - base_fallback_theme.mkdir(exist_ok=True) + base_fallback_theme.mkdir() base_fallback_theme.joinpath("new.html").write_text("base fallback new") assert handler.env.get_template("new.html").render() == "base fallback new" - base_theme = Path(search_paths[0]) - base_theme.mkdir(exist_ok=True) + base_theme.mkdir() base_theme.joinpath("new.html").write_text("base new") assert handler.env.get_template("new.html").render() == "base new" From 397e63dbe2e4fed39f6274c8a797570ef09da50b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 17 Jul 2023 16:37:00 +0200 Subject: [PATCH 07/28] ci: Quality --- src/mkdocstrings/handlers/base.py | 4 ++-- tests/conftest.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mkdocstrings/handlers/base.py b/src/mkdocstrings/handlers/base.py index b6ecd4fa..ad53e961 100644 --- a/src/mkdocstrings/handlers/base.py +++ b/src/mkdocstrings/handlers/base.py @@ -15,7 +15,7 @@ import warnings from contextlib import suppress from pathlib import Path -from typing import Any, BinaryIO, Iterable, Iterator, Mapping, MutableMapping, Sequence +from typing import Any, BinaryIO, ClassVar, Iterable, Iterator, Mapping, MutableMapping, Sequence from xml.etree.ElementTree import Element, tostring from jinja2 import Environment, FileSystemLoader @@ -396,7 +396,7 @@ class BaseHandler(BaseCollector, BaseRenderer): domain: str = "default" enable_inventory: bool = False - fallback_config: dict = {} + fallback_config: ClassVar[dict] = {} # TODO: once the BaseCollector and BaseRenderer classes are removed, # stop accepting the 'handler' parameter, and instead set a 'name' attribute on the Handler class. diff --git a/tests/conftest.py b/tests/conftest.py index a2ea6b26..ebfc2ad8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -31,7 +31,7 @@ def fixture_mkdocs_conf(request: pytest.FixtureRequest, tmp_path: Path) -> Itera **getattr(request, "param", {}), } # Re-create it manually as a workaround for https://github.com/mkdocs/mkdocs/issues/2289 - mdx_configs: dict[str, Any] = dict(ChainMap(*conf_dict.get("markdown_extensions", []))) # type: ignore[arg-type] + mdx_configs: dict[str, Any] = dict(ChainMap(*conf_dict.get("markdown_extensions", []))) conf.load_dict(conf_dict) assert conf.validate() == ([], []) From 59a45e34c5148a8daa2c07e49a55181eaecfc51a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 17 Jul 2023 16:37:13 +0200 Subject: [PATCH 08/28] chore: Fixes for Python 3.12 --- config/pytest.ini | 4 ++++ scripts/gen_ref_nav.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/config/pytest.ini b/config/pytest.ini index 5a493959..6b0d5c7a 100644 --- a/config/pytest.ini +++ b/config/pytest.ini @@ -20,3 +20,7 @@ filterwarnings = error # TODO: remove once pytest-xdist 4 is released ignore:.*rsyncdir:DeprecationWarning:xdist + # TODO: https://github.com/Python-Markdown/markdown/issues/1355 + ignore:.*Testing:DeprecationWarning:markdown + # TODO: https://github.com/facelessuser/pymdown-extensions/issues/2113 + ignore:.*Testing:DeprecationWarning:pymdownx diff --git a/scripts/gen_ref_nav.py b/scripts/gen_ref_nav.py index 78b9745c..249530b1 100644 --- a/scripts/gen_ref_nav.py +++ b/scripts/gen_ref_nav.py @@ -9,7 +9,7 @@ for path in sorted(Path("src").rglob("*.py")): module_path = path.relative_to("src").with_suffix("") - doc_path = path.relative_to("src", "mkdocstrings").with_suffix(".md") + doc_path = path.relative_to("src/mkdocstrings").with_suffix(".md") full_doc_path = Path("reference", doc_path) parts = tuple(module_path.parts) From f9ec9522026e2fb34c101e50b99c8fa76b6ad8e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 17 Jul 2023 16:37:28 +0200 Subject: [PATCH 09/28] docs: Add Griffe extensions to Insiders page --- docs/insiders/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/insiders/index.md b/docs/insiders/index.md index 73021f1c..3a55ad5d 100644 --- a/docs/insiders/index.md +++ b/docs/insiders/index.md @@ -59,6 +59,8 @@ a handful of them, [thanks to our awesome sponsors][sponsors]! --> data_source = [ "docs/insiders/goals.yml", ("mkdocstrings-python", "https://mkdocstrings.github.io/python/", "insiders/goals.yml"), + ("griffe-pydantic", "https://mkdocstrings.github.io/griffe-pydantic/", "insiders/goals.yml"), + ("griffe-typing-deprecated", "https://mkdocstrings.github.io/griffe-typing-deprecated/", "insiders/goals.yml"), ] ``` From b194452be93aee33b3c28a468762b4d96c501f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 17 Jul 2023 16:39:44 +0200 Subject: [PATCH 10/28] fix: Support cross-references for API docs rendered in top-level index page --- src/mkdocstrings/extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mkdocstrings/extension.py b/src/mkdocstrings/extension.py index 62d837ca..3ee256f2 100644 --- a/src/mkdocstrings/extension.py +++ b/src/mkdocstrings/extension.py @@ -133,7 +133,7 @@ def run(self, parent: Element, blocks: MutableSequence[str]) -> None: el.extend(headings) page = self._autorefs.current_page - if page: + if page is not None: for heading in headings: anchor = heading.attrib["id"] self._autorefs.register_anchor(page, anchor) From 433c6e01aab9333589f755e483f124db0836f143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 17 Jul 2023 16:41:32 +0200 Subject: [PATCH 11/28] refactor: Use proper parameters in `Inventory.register` method, and don't overwrite items --- src/mkdocstrings/inventory.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/mkdocstrings/inventory.py b/src/mkdocstrings/inventory.py index 42e9b3db..4f2157ca 100644 --- a/src/mkdocstrings/inventory.py +++ b/src/mkdocstrings/inventory.py @@ -88,15 +88,34 @@ def __init__(self, items: list[InventoryItem] | None = None, project: str = "pro self.project = project self.version = version - def register(self, *args: str, **kwargs: str) -> None: + def register( + self, + name: str, + domain: str, + role: str, + uri: str, + priority: str = "1", + dispname: str | None = None, + ) -> None: """Create and register an item. Arguments: - *args: Arguments passed to [InventoryItem][mkdocstrings.inventory.InventoryItem]. - **kwargs: Keyword arguments passed to [InventoryItem][mkdocstrings.inventory.InventoryItem]. + name: The item name. + domain: The item domain, like 'python' or 'crystal'. + role: The item role, like 'class' or 'method'. + uri: The item URI. + priority: The item priority. It can help for inventory suggestions. + dispname: The item display name. """ - item = InventoryItem(*args, **kwargs) - self[item.name] = item + if name not in self: + self[name] = InventoryItem( + name=name, + domain=domain, + role=role, + uri=uri, + priority=priority, + dispname=dispname, + ) def format_sphinx(self) -> bytes: """Format this inventory as a Sphinx `objects.inv` file. From ee93e80f904f213dd621e047ada3e51018c4b17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 17 Jul 2023 16:45:13 +0200 Subject: [PATCH 12/28] feat: Register objects' aliases into the inventory When publishing API docs, maintainers might inject documentation for an object for only one of its locations (canonical, public, other alias, etc.). Without this change, other doc sites can only cross-reference this object with the path it was rendered with. With this change, other doc sites can cross-reference the object with any of its aliases. It means that doc writers do not have to worry about injecting docs for every object with both their canonical and public paths: only one path is fine, and other can cross-ref to it with any valid alias path. --- src/mkdocstrings/extension.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/mkdocstrings/extension.py b/src/mkdocstrings/extension.py index 3ee256f2..e5e2c507 100644 --- a/src/mkdocstrings/extension.py +++ b/src/mkdocstrings/extension.py @@ -135,16 +135,17 @@ def run(self, parent: Element, blocks: MutableSequence[str]) -> None: page = self._autorefs.current_page if page is not None: for heading in headings: - anchor = heading.attrib["id"] - self._autorefs.register_anchor(page, anchor) + rendered_anchor = heading.attrib["id"] + self._autorefs.register_anchor(page, rendered_anchor) if "data-role" in heading.attrib: - self._handlers.inventory.register( - name=anchor, - domain=handler.domain, - role=heading.attrib["data-role"], - uri=f"{page}#{anchor}", - ) + for anchor in sorted({rendered_anchor, *handler.get_anchors(data)}): + self._handlers.inventory.register( + name=anchor, + domain=handler.domain, + role=heading.attrib["data-role"], + uri=f"{page}#{rendered_anchor}", + ) parent.append(el) From 38b43f30efb8a552beb5ea5a7028995eec6a6c8a Mon Sep 17 00:00:00 2001 From: ssweber <57631333+ssweber@users.noreply.github.com> Date: Wed, 2 Aug 2023 12:56:36 -0400 Subject: [PATCH 13/28] docs: Add note about newline handling --- docs/troubleshooting.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 4d2b074e..bc1da01b 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -21,6 +21,10 @@ markdown_extensions: - pymdownx.superfences ``` +For code blocks in docstrings, make sure to escape newlines (`\n` -> `\\n`), +or prefix the entire docstring with 'r' to make it a raw-docstring: `r"""`. +Indeed, docstrings are still strings and therefore subject to how Python parses strings. + ## Footnotes are duplicated or overridden Before version 0.14, footnotes could be duplicated over a page. From e362ef7985aa0cea27fb83dd1041dbd4cc6a6f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Wed, 2 Aug 2023 22:02:17 +0200 Subject: [PATCH 14/28] ci: Fix typing for MkDocs 1.5 --- src/mkdocstrings/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mkdocstrings/plugin.py b/src/mkdocstrings/plugin.py index 4c902935..7d6a719c 100644 --- a/src/mkdocstrings/plugin.py +++ b/src/mkdocstrings/plugin.py @@ -34,6 +34,7 @@ if TYPE_CHECKING: from jinja2.environment import Environment from mkdocs.config import Config + from mkdocs.config.defaults import MkDocsConfig from mkdocs.livereload import LiveReloadServer if sys.version_info < (3, 10): @@ -167,7 +168,7 @@ def on_serve( log.debug(f"Adding directory '{element}' to watcher") server.watch(element, builder) - def on_config(self, config: Config, **kwargs: Any) -> Config: # noqa: ARG002 + def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: """Instantiate our Markdown extension. Hook for the [`on_config` event](https://www.mkdocs.org/user-guide/plugins/#on_config). @@ -179,7 +180,6 @@ def on_config(self, config: Config, **kwargs: Any) -> Config: # noqa: ARG002 Arguments: config: The MkDocs config object. - **kwargs: Additional arguments passed by MkDocs. Returns: The modified config. From 40c4693b9988bd67027911216a61d73bdb1b6dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Wed, 2 Aug 2023 22:02:27 +0200 Subject: [PATCH 15/28] tests: Fix tests for MkDocs 1.5 --- tests/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/conftest.py b/tests/conftest.py index ebfc2ad8..2119d1f3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,7 @@ def fixture_mkdocs_conf(request: pytest.FixtureRequest, tmp_path: Path) -> Itera request = request._parent_request conf_dict = { + "config_file_path": "mkdocs_tests.yml", "site_name": "foo", "site_url": "https://example.org/", "site_dir": str(tmp_path), From 0a90a474c8dcbd95821700d7dab63f03e392c40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 17 Jul 2023 19:16:51 +0200 Subject: [PATCH 16/28] refactor: Remove deprecated parts - Base collector class - Base renderer class - Watch feature - Selection and rendering keys support (YAML options) - `mkdocstrings.handlers` namespace (when importing handlers and finding templates) --- config/mypy.ini | 2 - config/ruff.toml | 1 - docs/usage/handlers.md | 56 +---- docs/usage/index.md | 30 --- pyproject.toml | 1 - src/mkdocstrings/__init__.py | 4 + src/mkdocstrings/extension.py | 25 +-- src/mkdocstrings/handlers/__init__.py | 1 + src/mkdocstrings/handlers/base.py | 309 ++++++-------------------- src/mkdocstrings/plugin.py | 68 +----- tests/test_extension.py | 12 +- 11 files changed, 107 insertions(+), 402 deletions(-) create mode 100644 src/mkdocstrings/__init__.py create mode 100644 src/mkdocstrings/handlers/__init__.py diff --git a/config/mypy.ini b/config/mypy.ini index cb0dd886..814e2ac8 100644 --- a/config/mypy.ini +++ b/config/mypy.ini @@ -3,5 +3,3 @@ ignore_missing_imports = true exclude = tests/fixtures/ warn_unused_ignores = true show_error_codes = true -namespace_packages = true -explicit_package_bases = true diff --git a/config/ruff.toml b/config/ruff.toml index 8f390641..c6e4a55c 100644 --- a/config/ruff.toml +++ b/config/ruff.toml @@ -65,7 +65,6 @@ ignore = [ "E501", # Line too long "ERA001", # Commented out code "G004", # Logging statement uses f-string - "INP001", # File is part of an implicit namespace package "PLR0911", # Too many return statements "PLR0912", # Too many branches "PLR0913", # Too many arguments to function call diff --git a/docs/usage/handlers.md b/docs/usage/handlers.md index e381090e..f4c474f9 100644 --- a/docs/usage/handlers.md +++ b/docs/usage/handlers.md @@ -14,11 +14,8 @@ Since version 0.18, a new, experimental Python handler is available. It is based on [Griffe](https://github.com/mkdocstrings/griffe), which is an improved version of [pytkdocs](https://github.com/mkdocstrings/pytkdocs). -Note that the experimental handler does not yet support third-party libraries -like Django, Marshmallow, Pydantic, etc. -It is also not completely ready to handle dynamically built objects, -like classes built with a call to `type(...)`. -For most other cases, the experimental handler will work just fine. +Note that the experimental handler does not yet support all third-party libraries +that the legacy handler supported. If you want to keep using the legacy handler as long as possible, you can depend on `mkdocstrings-python-legacy` directly, @@ -51,18 +48,13 @@ dependencies = [ ] ``` -#### Handler options - -- `setup_commands` is not yet implemented. In most cases, you won't need it, - since by default the new handler does not execute the code. - #### Selection options WARNING: Since *mkdocstrings* 0.19, the YAML `selection` key is merged into the `options` key. - [x] `filters` is implemented, and used as before. - [x] `members` is implemented, and used as before. -- [ ] `inherited_members` is not yet implemented. +- [x] `inherited_members` is implemented. - [x] `docstring_style` is implemented, and used as before, except for the `restructured-text` style which is renamed `sphinx`. Numpy-style is now built-in, so you can stop depending on `pytkdocs[numpy-style]` @@ -83,13 +75,13 @@ WARNING: Since *mkdocstrings* 0.19, the YAML `rendering` key is merged into the Every previous option is supported. Additional options are available: -- `separate_signature`: Render the signature (or attribute value) in a code block below the heading, +- [x] `separate_signature`: Render the signature (or attribute value) in a code block below the heading, instead as inline code. Useful for long signatures. If Black is installed, the signature is formatted. Default: `False`. -- `line_length`: The maximum line length to use when formatting signatures. Default: `60`. -- `show_submodules`: Whether to render submodules of a module when iterating on children. +- [x] `line_length`: The maximum line length to use when formatting signatures. Default: `60`. +- [x] `show_submodules`: Whether to render submodules of a module when iterating on children. Default: `False`. -- `docstring_section_style`: The style to use to render docstring sections such as attributes, +- [x] `docstring_section_style`: The style to use to render docstring sections such as attributes, parameters, etc. Available styles: `table` (default), `list` and `spacy`. The SpaCy style is a poor implementation of their [table style](https://spacy.io/api/doc/#init). We are open to improvements through PRs! @@ -99,34 +91,8 @@ See [all the handler's options](https://mkdocstrings.github.io/python/usage/). #### Templates Templates are mostly the same as before, but the file layout has changed, -as well as some file names. Here is the new tree: - -``` -📁 theme/ -├── 📄 attribute.html -├── 📄 children.html -├── 📄 class.html -├── 📁 docstring/ -│   ├── 📄 admonition.html -│   ├── 📄 attributes.html -│   ├── 📄 examples.html -│   ├── 📄 other_parameters.html -│   ├── 📄 parameters.html -│   ├── 📄 raises.html -│   ├── 📄 receives.html -│   ├── 📄 returns.html -│   ├── 📄 warns.html -│   └── 📄 yields.html -├── 📄 docstring.html -├── 📄 expression.html -├── 📄 function.html -├── 📄 labels.html -├── 📄 module.html -└── 📄 signature.html -``` - -See them [in the handler repository](https://github.com/mkdocstrings/python/tree/8fc8ea5b112627958968823ef500cfa46b63613e/src/mkdocstrings_handlers/python/templates/material). See the documentation about the Python handler templates: -https://mkdocstrings.github.io/python/customization/#templates. +as well as some file names. +See [the documentation about the Python handler templates](https://mkdocstrings.github.io/python/usage/customization/#templates). ## Custom handlers @@ -213,7 +179,7 @@ If your theme's HTML requires CSS to go along with it, put it into a file named `mkdocstrings_handlers/custom_handler/templates/some_theme/style.css`, then this will be included into the final site automatically if this handler is ever used. Alternatively, you can put the CSS as a string into the `extra_css` variable of -your renderer. +your handler. Finally, it's possible to entirely omit templates, and tell *mkdocstrings* to use the templates of another handler. In you handler, override the @@ -225,7 +191,7 @@ from mkdocstrings.handlers.base import BaseHandler class CobraHandler(BaseHandler): - def get_templates_dir(self, handler: str) -> Path: + def get_templates_dir(self, handler: str | None = None) -> Path: # use the python handler templates # (it assumes the python handler is installed) return super().get_templates_dir("python") diff --git a/docs/usage/index.md b/docs/usage/index.md index 3318c053..7599f9f1 100644 --- a/docs/usage/index.md +++ b/docs/usage/index.md @@ -116,9 +116,6 @@ The above is equivalent to: - `enabled` **(New in version 0.20)**: Whether to enable the plugin. Defaults to `true`. Can be used to reduce build times when doing local development. Especially useful when used with environment variables (see example below). -- `watch` **(deprecated)**: A list of directories to watch while serving the documentation. - See [Watch directories](#watch-directories). Deprecated in favor of the now built-in - [`watch` feature of MkDocs](https://www.mkdocs.org/user-guide/configuration/#watch). !!! example ```yaml title="mkdocs.yml" @@ -334,30 +331,3 @@ plugins: - mkdocstrings: enable_inventory: false ``` - -## Watch directories - -DANGER: **Deprecated since version 0.19.** -Instead, use the built-in [`watch` feature of MkDocs](https://www.mkdocs.org/user-guide/configuration/#watch). - -You can add directories to watch with the `watch` key. -It accepts a list of paths. - -```yaml title="mkdocs.yml" -plugins: - - mkdocstrings: - watch: - - src/my_package_1 - - src/my_package_2 -``` - -When serving your documentation -and a change occur in one of the listed path, -MkDocs will rebuild the site and reload the current page. - -NOTE: **The `watch` feature doesn't have special effects.** -Adding directories to the `watch` list doesn't have any other effect than watching for changes. -For example, it will not tell the Python handler to look for packages in these paths -(the paths are not added to the `PYTHONPATH` variable). -If you want to tell Python where to look for packages and modules, -see [Python Handler: Finding modules](https://mkdocstrings.github.io/python/usage/#finding-modules). diff --git a/pyproject.toml b/pyproject.toml index d43ac7ca..57f09f4d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,7 +65,6 @@ plugins = [ [tool.pdm.build] package-dir = "src" -includes = ["src/mkdocstrings"] editable-backend = "editables" [tool.pdm.dev-dependencies] diff --git a/src/mkdocstrings/__init__.py b/src/mkdocstrings/__init__.py new file mode 100644 index 00000000..03550f9b --- /dev/null +++ b/src/mkdocstrings/__init__.py @@ -0,0 +1,4 @@ +"""mkdocstrings package. + +Automatic documentation from sources, for MkDocs. +""" diff --git a/src/mkdocstrings/extension.py b/src/mkdocstrings/extension.py index e5e2c507..c33f37ef 100644 --- a/src/mkdocstrings/extension.py +++ b/src/mkdocstrings/extension.py @@ -12,19 +12,17 @@ ```yaml ::: some.identifier handler: python - selection: + options: option1: value1 option2: - - value2a - - value2b - rendering: + - value2a + - value2b option_x: etc ``` """ from __future__ import annotations -import functools import re from collections import ChainMap from typing import TYPE_CHECKING, Any, MutableSequence @@ -184,13 +182,7 @@ def _process_block( global_options = handler_config.get("options", {}) local_options = config.get("options", {}) - deprecated_global_options = ChainMap(handler_config.get("selection", {}), handler_config.get("rendering", {})) - deprecated_local_options = ChainMap(config.get("selection", {}), config.get("rendering", {})) - - options = ChainMap(local_options, deprecated_local_options, global_options, deprecated_global_options) - - if deprecated_global_options or deprecated_local_options: - self._warn_about_options_key() + options = ChainMap(local_options, global_options) if heading_level: options = ChainMap(options, {"heading_level": heading_level}) # like setdefault @@ -200,12 +192,12 @@ def _process_block( data: CollectorItem = handler.collect(identifier, options) except CollectionError as exception: log.error(str(exception)) # noqa: TRY400 - if PluginError is SystemExit: # When MkDocs 1.2 is sufficiently common, this can be dropped. + if PluginError is SystemExit: # TODO: when MkDocs 1.2 is sufficiently common, this can be dropped. log.error(f"Error reading page '{self._autorefs.current_page}':") # noqa: TRY400 raise PluginError(f"Could not collect '{identifier}'") from exception if handler_name not in self._updated_envs: # We haven't seen this handler before on this document. - log.debug("Updating renderer's env") + log.debug("Updating handler's rendering env") handler._update_env(self.md, self._config) self._updated_envs.add(handler_name) @@ -221,11 +213,6 @@ def _process_block( return rendered, handler, data - @classmethod - @functools.lru_cache(maxsize=None) # Warn only once - def _warn_about_options_key(cls) -> None: - log.info("DEPRECATION: 'selection' and 'rendering' are deprecated and merged into a single 'options' YAML key") - class _PostProcessor(Treeprocessor): def run(self, root: Element) -> None: diff --git a/src/mkdocstrings/handlers/__init__.py b/src/mkdocstrings/handlers/__init__.py new file mode 100644 index 00000000..b9e2a29c --- /dev/null +++ b/src/mkdocstrings/handlers/__init__.py @@ -0,0 +1 @@ +"""Handlers module.""" diff --git a/src/mkdocstrings/handlers/base.py b/src/mkdocstrings/handlers/base.py index ad53e961..33304351 100644 --- a/src/mkdocstrings/handlers/base.py +++ b/src/mkdocstrings/handlers/base.py @@ -1,6 +1,6 @@ """Base module for handlers. -This module contains the base classes for implementing collectors, renderers, and the combination of the two: handlers. +This module contains the base classes for implementing handlers. It also provides two methods: @@ -12,8 +12,6 @@ import importlib import sys -import warnings -from contextlib import suppress from pathlib import Path from typing import Any, BinaryIO, ClassVar, Iterable, Iterator, Mapping, MutableMapping, Sequence from xml.etree.ElementTree import Element, tostring @@ -66,21 +64,32 @@ def do_any(seq: Sequence, attribute: str | None = None) -> bool: return any(_[attribute] for _ in seq) -class BaseRenderer: - """The base renderer class. +class BaseHandler: + """The base handler class. - Inherit from this class to implement a renderer. + Inherit from this class to implement a handler. - You will have to implement the `render` method. - You can also override the `update_env` method, to add more filters to the Jinja environment, + You will have to implement the `collect` and `render` methods. + You can also implement the `teardown` method, + and override the `update_env` method, to add more filters to the Jinja environment, making them available in your Jinja templates. To define a fallback theme, add a `fallback_theme` class-variable. To add custom CSS, add an `extra_css` variable or create an 'style.css' file beside the templates. """ + name: str = "" + """The handler's name, for example "python".""" + domain: str = "default" + """The handler's domain, used to register objects in the inventory, for example "py".""" + enable_inventory: bool = False + """Whether the inventory creation is enabled.""" + fallback_config: ClassVar[dict] = {} + """Fallback configuration when searching anchors for identifiers.""" fallback_theme: str = "" + """Fallback theme to use when a template isn't found in the configured theme.""" extra_css = "" + """Extra CSS.""" def __init__(self, handler: str, theme: str, custom_templates: str | None = None) -> None: """Initialize the object. @@ -95,11 +104,6 @@ def __init__(self, handler: str, theme: str, custom_templates: str | None = None """ paths = [] - # TODO: remove once BaseRenderer is merged into BaseHandler - self._handler = handler - self._theme = theme - self._custom_templates = custom_templates - # add selected theme templates themes_dir = self.get_templates_dir(handler) paths.append(themes_dir / theme) @@ -137,6 +141,44 @@ def __init__(self, handler: str, theme: str, custom_templates: str | None = None self._headings: list[Element] = [] self._md: Markdown = None # type: ignore[assignment] # To be populated in `update_env`. + @classmethod + def load_inventory( + cls, + in_file: BinaryIO, # noqa: ARG003 + url: str, # noqa: ARG003 + base_url: str | None = None, # noqa: ARG003 + **kwargs: Any, # noqa: ARG003 + ) -> Iterator[tuple[str, str]]: + """Yield items and their URLs from an inventory file streamed from `in_file`. + + Arguments: + in_file: The binary file-like object to read the inventory from. + url: The URL that this file is being streamed from (used to guess `base_url`). + base_url: The URL that this inventory's sub-paths are relative to. + **kwargs: Ignore additional arguments passed from the config. + + Yields: + Tuples of (item identifier, item URL). + """ + yield from () + + def collect(self, identifier: str, config: MutableMapping[str, Any]) -> CollectorItem: + """Collect data given an identifier and user configuration. + + In the implementation, you typically call a subprocess that returns JSON, and load that JSON again into + a Python dictionary for example, though the implementation is completely free. + + Arguments: + identifier: An identifier for which to collect data. For example, in Python, + it would be 'mkdocstrings.handlers' to collect documentation about the handlers module. + It can be anything that you can feed to the tool of your choice. + config: The handler's configuration options. + + Returns: + Anything you want, as long as you can feed it to the handler's `render` method. + """ + raise NotImplementedError + def render(self, data: CollectorItem, config: Mapping[str, Any]) -> str: """Render a template using provided data and configuration options. @@ -149,7 +191,14 @@ def render(self, data: CollectorItem, config: Mapping[str, Any]) -> str: """ raise NotImplementedError - def get_templates_dir(self, handler: str) -> Path: + def teardown(self) -> None: + """Teardown the handler. + + This method should be implemented to, for example, terminate a subprocess + that was started when creating the handler instance. + """ + + def get_templates_dir(self, handler: str | None = None) -> Path: """Return the path to the handler's templates directory. Override to customize how the templates directory is found. @@ -158,41 +207,21 @@ def get_templates_dir(self, handler: str) -> Path: handler: The name of the handler to get the templates directory of. Raises: + ModuleNotFoundError: When no such handler is installed. FileNotFoundError: When the templates directory cannot be found. Returns: The templates directory path. """ - # Templates can be found in 2 different logical locations: - # - in mkdocstrings_handlers/HANDLER/templates: our new migration target - # - in mkdocstrings/templates/HANDLER: current situation, this should be avoided - # These two other locations are forbidden: - # - in mkdocstrings_handlers/templates/HANDLER: sub-namespace packages are too annoying to deal with - # - in mkdocstrings/handlers/HANDLER/templates: not currently supported, - # and mkdocstrings will stop being a namespace - - with suppress(ModuleNotFoundError): # TODO: catch at some point to warn about missing handlers + handler = handler or self.name + try: import mkdocstrings_handlers + except ModuleNotFoundError as error: + raise ModuleNotFoundError(f"Handler '{handler}' not found, is it installed?") from error - for path in mkdocstrings_handlers.__path__: - theme_path = Path(path, handler, "templates") - if theme_path.exists(): - return theme_path - - # TODO: remove import and loop at some point, - # as mkdocstrings will stop being a namespace package - import mkdocstrings - - for path in mkdocstrings.__path__: - theme_path = Path(path, "templates", handler) + for path in mkdocstrings_handlers.__path__: + theme_path = Path(path, handler, "templates") if theme_path.exists(): - if handler != "python": - warnings.warn( - "Exposing templates in the mkdocstrings.templates namespace is deprecated. " - "Put them in a templates folder inside your handler package instead.", - DeprecationWarning, - stacklevel=1, - ) return theme_path raise FileNotFoundError(f"Can't find 'templates' folder for handler '{handler}'") @@ -209,7 +238,7 @@ def get_extended_templates_dirs(self, handler: str) -> list[Path]: discovered_extensions = entry_points(group=f"mkdocstrings.{handler}.templates") return [extension.load()() for extension in discovered_extensions] - def get_anchors(self, data: CollectorItem) -> tuple[str, ...] | set[str]: + def get_anchors(self, data: CollectorItem) -> tuple[str, ...] | set[str]: # noqa: ARG002 """Return the possible identifiers (HTML anchors) for a collected item. Arguments: @@ -218,11 +247,7 @@ def get_anchors(self, data: CollectorItem) -> tuple[str, ...] | set[str]: Returns: The HTML anchors (without '#'), or an empty tuple if this item doesn't have an anchor. """ - # TODO: remove this at some point - try: - return (self.get_anchor(data),) # type: ignore[attr-defined] - except AttributeError: - return () + return () def do_convert_markdown( self, @@ -346,181 +371,6 @@ def _update_env(self, md: Markdown, config: dict) -> None: self.update_env(new_md, config) -class BaseCollector: - """The base collector class. - - Inherit from this class to implement a collector. - - You will have to implement the `collect` method. - You can also implement the `teardown` method. - """ - - def collect(self, identifier: str, config: MutableMapping[str, Any]) -> CollectorItem: - """Collect data given an identifier and selection configuration. - - In the implementation, you typically call a subprocess that returns JSON, and load that JSON again into - a Python dictionary for example, though the implementation is completely free. - - Arguments: - identifier: An identifier for which to collect data. For example, in Python, - it would be 'mkdocstrings.handlers' to collect documentation about the handlers module. - It can be anything that you can feed to the tool of your choice. - config: The handler's configuration options. - - Returns: - Anything you want, as long as you can feed it to the renderer's `render` method. - """ - raise NotImplementedError - - def teardown(self) -> None: - """Teardown the collector. - - This method should be implemented to, for example, terminate a subprocess - that was started when creating the collector instance. - """ - - -class BaseHandler(BaseCollector, BaseRenderer): - """The base handler class. - - Inherit from this class to implement a handler. - - It's usually just a combination of a collector and a renderer, but you can make it as complex as you need. - - Attributes: - domain: The cross-documentation domain/language for this handler. - enable_inventory: Whether this handler is interested in enabling the creation - of the `objects.inv` Sphinx inventory file. - fallback_config: The configuration used to collect item during autorefs fallback. - """ - - domain: str = "default" - enable_inventory: bool = False - fallback_config: ClassVar[dict] = {} - - # TODO: once the BaseCollector and BaseRenderer classes are removed, - # stop accepting the 'handler' parameter, and instead set a 'name' attribute on the Handler class. - # Then make the 'handler' parameter in 'get_templates_dir' optional, and use the class 'name' by default. - def __init__(self, *args: str | BaseCollector | BaseRenderer, **kwargs: str | BaseCollector | BaseRenderer) -> None: - """Initialize the object. - - Arguments: - *args: Collector and renderer, or handler name, theme and custom_templates. - **kwargs: Same thing, but with keyword arguments. - - Raises: - ValueError: When the given parameters are invalid. - """ - # The method accepts *args and **kwargs temporarily, - # to support the transition period where the BaseCollector - # and BaseRenderer are deprecated, and the BaseHandler - # can be instantiated with both instances of collector/renderer, - # or renderer parameters, as positional parameters. - - collector = None - renderer = None - - # parsing positional arguments - str_args = [] - for arg in args: - if isinstance(arg, BaseCollector): - collector = arg - elif isinstance(arg, BaseRenderer): - renderer = arg - elif isinstance(arg, str): - str_args.append(arg) - - while len(str_args) != 3: # noqa: PLR2004 - str_args.append(None) # type: ignore[arg-type] - - handler, theme, custom_templates = str_args - - # fetching values from keyword arguments - if "collector" in kwargs: - collector = kwargs.pop("collector") # type: ignore[assignment] - if "renderer" in kwargs: - renderer = kwargs.pop("renderer") # type: ignore[assignment] - if "handler" in kwargs: - handler = kwargs.pop("handler") # type: ignore[assignment] - if "theme" in kwargs: - theme = kwargs.pop("theme") # type: ignore[assignment] - if "custom_templates" in kwargs: - custom_templates = kwargs.pop("custom_templates") # type: ignore[assignment] - - if collector is None and renderer is not None or collector is not None and renderer is None: - raise ValueError("both 'collector' and 'renderer' must be provided") - - if collector is not None: - warnings.warn( - DeprecationWarning( - "The BaseCollector class is deprecated, and passing an instance of it " - "to your handler is deprecated as well. Instead, define the `collect` and `teardown` " - "methods directly on your handler class.", - ), - stacklevel=1, - ) - self.collector = collector - self.collect = collector.collect # type: ignore[method-assign] - self.teardown = collector.teardown # type: ignore[method-assign] - - if renderer is not None: - if {handler, theme, custom_templates} != {None}: - raise ValueError( - "'handler', 'theme' and 'custom_templates' must all be None when providing a renderer instance", - ) - warnings.warn( - DeprecationWarning( - "The BaseRenderer class is deprecated, and passing an instance of it " - "to your handler is deprecated as well. Instead, define the `render` method " - "directly on your handler class (as well as other methods and attributes like " - "`get_templates_dir`, `get_anchors`, `update_env` and `fallback_theme`, `extra_css`).", - ), - stacklevel=1, - ) - self.renderer = renderer - self.render = renderer.render # type: ignore[method-assign] - self.get_templates_dir = renderer.get_templates_dir # type: ignore[method-assign] - self.get_anchors = renderer.get_anchors # type: ignore[method-assign] - self.do_convert_markdown = renderer.do_convert_markdown # type: ignore[method-assign] - self.do_heading = renderer.do_heading # type: ignore[method-assign] - self.get_headings = renderer.get_headings # type: ignore[method-assign] - self.update_env = renderer.update_env # type: ignore[method-assign] - self._update_env = renderer._update_env # type: ignore[method-assign] - self.fallback_theme = renderer.fallback_theme - self.extra_css = renderer.extra_css - renderer.__class__.__init__( - self, - renderer._handler, - renderer._theme, - renderer._custom_templates, - ) - else: - if handler is None or theme is None: - raise ValueError("'handler' and 'theme' cannot be None") - BaseRenderer.__init__(self, handler, theme, custom_templates) - - @classmethod - def load_inventory( - cls, - in_file: BinaryIO, # noqa: ARG003 - url: str, # noqa: ARG003 - base_url: str | None = None, # noqa: ARG003 - **kwargs: Any, # noqa: ARG003 - ) -> Iterator[tuple[str, str]]: - """Yield items and their URLs from an inventory file streamed from `in_file`. - - Arguments: - in_file: The binary file-like object to read the inventory from. - url: The URL that this file is being streamed from (used to guess `base_url`). - base_url: The URL that this inventory's sub-paths are relative to. - **kwargs: Ignore additional arguments passed from the config. - - Yields: - Tuples of (item identifier, item URL). - """ - yield from () - - class Handlers: """A collection of handlers. @@ -543,7 +393,7 @@ def get_anchors(self, identifier: str) -> tuple[str, ...] | set[str]: """Return the canonical HTML anchor for the identifier, if any of the seen handlers can collect it. Arguments: - identifier: The identifier (one that [collect][mkdocstrings.handlers.base.BaseCollector.collect] can accept). + identifier: The identifier (one that [collect][mkdocstrings.handlers.base.BaseHandler.collect] can accept). Returns: A tuple of strings - anchors without '#', or an empty tuple if there isn't any identifier familiar with it. @@ -606,18 +456,7 @@ def get_handler(self, name: str, handler_config: dict | None = None) -> BaseHand if handler_config is None: handler_config = self.get_handler_config(name) handler_config.update(self._config) - try: - module = importlib.import_module(f"mkdocstrings_handlers.{name}") - except ModuleNotFoundError: - module = importlib.import_module(f"mkdocstrings.handlers.{name}") - if name != "python": - warnings.warn( - DeprecationWarning( - "Using the mkdocstrings.handlers namespace is deprecated. " - "Handlers must now use the mkdocstrings_handlers namespace.", - ), - stacklevel=1, - ) + module = importlib.import_module(f"mkdocstrings_handlers.{name}") self._handlers[name] = module.get_handler( theme=self._config["theme_name"], custom_templates=self._config["mkdocstrings"]["custom_templates"], diff --git a/src/mkdocstrings/plugin.py b/src/mkdocstrings/plugin.py index 7d6a719c..484d3ead 100644 --- a/src/mkdocstrings/plugin.py +++ b/src/mkdocstrings/plugin.py @@ -35,7 +35,6 @@ from jinja2.environment import Environment from mkdocs.config import Config from mkdocs.config.defaults import MkDocsConfig - from mkdocs.livereload import LiveReloadServer if sys.version_info < (3, 10): from typing_extensions import ParamSpec @@ -44,11 +43,6 @@ log = get_logger(__name__) -SELECTION_OPTS_KEY: str = "selection" -"""Deprecated. The name of the selection parameter in YAML configuration blocks.""" -RENDERING_OPTS_KEY: str = "rendering" -"""Deprecated. The name of the rendering parameter in YAML configuration blocks.""" - InventoryImportType = List[Tuple[str, Mapping[str, Any]]] InventoryLoaderType = Callable[..., Iterable[Tuple[str, str]]] @@ -76,14 +70,12 @@ class MkdocstringsPlugin(BasePlugin): - `on_config` - `on_env` - `on_post_build` - - `on_serve` Check the [Developing Plugins](https://www.mkdocs.org/user-guide/plugins/#developing-plugins) page of `mkdocs` for more information about its plugin system. """ - config_scheme: tuple[tuple[str, MkType]] = ( - ("watch", MkType(list, default=[])), # type: ignore[assignment] + config_scheme: tuple[tuple[str, MkType]] = ( # type: ignore[assignment] ("handlers", MkType(dict, default={})), ("default_handler", MkType(str, default="python")), ("custom_templates", MkType(str, default=None)), @@ -95,13 +87,12 @@ class MkdocstringsPlugin(BasePlugin): Available options are: - - **`watch` (deprecated)**: A list of directories to watch. Only used when serving the documentation with mkdocs. - Whenever a file changes in one of directories, the whole documentation is built again, and the browser refreshed. - Deprecated in favor of the now built-in `watch` feature of MkDocs. - - **`default_handler`**: The default handler to use. The value is the name of the handler module. Default is "python". - - **`enabled`**: Whether to enable the plugin. Default is true. If false, *mkdocstrings* will not collect or render anything. - **`handlers`**: Global configuration of handlers. You can set global configuration per handler, applied everywhere, but overridable in each "autodoc" instruction. Example: + - **`default_handler`**: The default handler to use. The value is the name of the handler module. Default is "python". + - **`custom_templates`**: Custom templates to use when rendering API objects. + - **`enable_inventory`**: Whether to enable object inventory creation. + - **`enabled`**: Whether to enable the plugin. Default is true. If false, *mkdocstrings* will not collect or render anything. ```yaml plugins: @@ -109,11 +100,11 @@ class MkdocstringsPlugin(BasePlugin): handlers: python: options: - selection_opt: true - rendering_opt: "value" + option1: true + option2: "value" rust: options: - selection_opt: 2 + option9: 2 ``` """ @@ -138,36 +129,6 @@ def handlers(self) -> Handlers: raise RuntimeError("The plugin hasn't been initialized with a config yet") return self._handlers - # TODO: remove once watch feature is removed - def on_serve( - self, - server: LiveReloadServer, - config: Config, # noqa: ARG002 - builder: Callable, - *args: Any, # noqa: ARG002 - **kwargs: Any, # noqa: ARG002 - ) -> None: - """Watch directories. - - Hook for the [`on_serve` event](https://www.mkdocs.org/user-guide/plugins/#on_serve). - In this hook, we add the directories specified in the plugin's configuration to the list of directories - watched by `mkdocs`. Whenever a change occurs in one of these directories, the documentation is built again - and the site reloaded. - - Arguments: - server: The `livereload` server instance. - config: The MkDocs config object (unused). - builder: The function to build the site. - *args: Additional arguments passed by MkDocs. - **kwargs: Additional arguments passed by MkDocs. - """ - if not self.plugin_enabled: - return - if self.config["watch"]: - for element in self.config["watch"]: - log.debug(f"Adding directory '{element}' to watcher") - server.watch(element, builder) - def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: """Instantiate our Markdown extension. @@ -238,9 +199,6 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: self._inv_futures[future] = (loader, import_item) inv_loader.shutdown(wait=False) - if self.config["watch"]: - self._warn_about_watch_option() - return config @property @@ -311,7 +269,7 @@ def on_post_build( For example, a handler could open a subprocess in the background and keep it open to feed it "autodoc" instructions and get back JSON data. If so, it should then close the subprocess at some point: - the proper place to do this is in the collector's `teardown` method, which is indirectly called by this hook. + the proper place to do this is in the handler's `teardown` method, which is indirectly called by this hook. Arguments: config: The MkDocs config object. @@ -362,11 +320,3 @@ def _load_inventory(cls, loader: InventoryLoaderType, url: str, **kwargs: Any) - result = dict(loader(content, url=url, **kwargs)) log.debug(f"Loaded inventory from {url!r}: {len(result)} items") return result - - @classmethod - @functools.lru_cache(maxsize=None) # Warn only once - def _warn_about_watch_option(cls) -> None: - log.info( - "DEPRECATION: mkdocstrings' watch feature is deprecated in favor of MkDocs' watch feature, " - "see https://www.mkdocs.org/user-guide/configuration/#watch", - ) diff --git a/tests/test_extension.py b/tests/test_extension.py index f7c3cecc..57a1c903 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -2,7 +2,6 @@ from __future__ import annotations -import logging import re import sys from textwrap import dedent @@ -147,14 +146,7 @@ def test_dont_register_every_identifier_as_anchor(plugin: MkdocstringsPlugin, ex assert identifier not in autorefs._abs_url_map -def test_use_deprecated_yaml_keys(ext_markdown: Markdown, caplog: pytest.LogCaptureFixture) -> None: - """Check that using the deprecated 'selection' and 'rendering' YAML keys emits a deprecation warning.""" - caplog.set_level(logging.INFO) - assert "h1" not in ext_markdown.convert("::: tests.fixtures.headings\n rendering:\n heading_level: 2") - assert "single 'options' YAML key" in caplog.text - - -def test_use_new_options_yaml_key(ext_markdown: Markdown) -> None: - """Check that using the new 'options' YAML key works as expected.""" +def test_use_options_yaml_key(ext_markdown: Markdown) -> None: + """Check that using the 'options' YAML key works as expected.""" assert "h1" in ext_markdown.convert("::: tests.fixtures.headings\n options:\n heading_level: 1") assert "h1" not in ext_markdown.convert("::: tests.fixtures.headings\n options:\n heading_level: 2") From 2e10374be258e9713b26f73dd06d0c2520ec07a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 21 Aug 2023 16:52:31 +0200 Subject: [PATCH 17/28] refactor: Stop accepting sets as return value of `get_anchors` (only tuples), to preserve order --- src/mkdocstrings/handlers/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mkdocstrings/handlers/base.py b/src/mkdocstrings/handlers/base.py index 33304351..4fa79724 100644 --- a/src/mkdocstrings/handlers/base.py +++ b/src/mkdocstrings/handlers/base.py @@ -238,7 +238,7 @@ def get_extended_templates_dirs(self, handler: str) -> list[Path]: discovered_extensions = entry_points(group=f"mkdocstrings.{handler}.templates") return [extension.load()() for extension in discovered_extensions] - def get_anchors(self, data: CollectorItem) -> tuple[str, ...] | set[str]: # noqa: ARG002 + def get_anchors(self, data: CollectorItem) -> tuple[str, ...]: # noqa: ARG002 """Return the possible identifiers (HTML anchors) for a collected item. Arguments: From b6ddf373579ed4c997fc4362996034ba38c5dc78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 28 Aug 2023 16:01:32 +0200 Subject: [PATCH 18/28] tests: Fix typing in tests --- tests/test_extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_extension.py b/tests/test_extension.py index 57a1c903..8c687629 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -137,7 +137,7 @@ def test_use_custom_handler(ext_markdown: Markdown) -> None: def test_dont_register_every_identifier_as_anchor(plugin: MkdocstringsPlugin, ext_markdown: Markdown) -> None: """Assert that we don't preemptively register all identifiers of a rendered object.""" handler = plugin._handlers.get_handler("python") # type: ignore[union-attr] - ids = {"id1", "id2", "id3"} + ids = ("id1", "id2", "id3") handler.get_anchors = lambda _: ids # type: ignore[method-assign] ext_markdown.convert("::: tests.fixtures.headings") autorefs = ext_markdown.parser.blockprocessors["mkdocstrings"]._autorefs From 7690d41e2871997464367e673023585c4fb05e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 28 Aug 2023 15:00:24 +0200 Subject: [PATCH 19/28] fix: Don't add `codehilite` CSS class to inline code --- src/mkdocstrings/handlers/rendering.py | 9 ++++++++- tests/test_handlers.py | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mkdocstrings/handlers/rendering.py b/src/mkdocstrings/handlers/rendering.py index c3fb236a..6009935a 100644 --- a/src/mkdocstrings/handlers/rendering.py +++ b/src/mkdocstrings/handlers/rendering.py @@ -68,11 +68,14 @@ def __init__(self, md: Markdown): md: The Markdown instance to read configs from. """ config: dict[str, Any] = {} + self._highlighter: str | None = None for ext in md.registeredExtensions: if isinstance(ext, HighlightExtension) and (ext.enabled or not config): + self._highlighter = "highlight" config = ext.getConfigs() break # This one takes priority, no need to continue looking if isinstance(ext, CodeHiliteExtension) and not config: + self._highlighter = "codehilite" config = ext.getConfigs() config["language_prefix"] = config["lang_prefix"] self._css_class = config.pop("css_class", "highlight") @@ -116,7 +119,11 @@ def highlight( self.linenums = old_linenums if inline: - return Markup(f'{result.text}') + # From the maintainer of codehilite, the codehilite CSS class, as defined by the user, + # should never be added to inline code, because codehilite does not support inline code. + # See https://github.com/Python-Markdown/markdown/issues/1220#issuecomment-1692160297. + css_class = "" if self._highlighter == "codehilite" else kwargs["css_class"] + return Markup(f'{result.text}') return Markup(result) diff --git a/tests/test_handlers.py b/tests/test_handlers.py index 510ebd8a..4a07e98b 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -32,7 +32,7 @@ def test_highlighter_without_pygments(extension_name: str) -> None: ) assert ( hl.highlight("import foo", language="python", inline=True) - == 'import foo' + == f'import foo' ) From 43960110c9c2edf741351727c0d1a31d2eabc7ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 28 Aug 2023 15:44:06 +0200 Subject: [PATCH 20/28] chore: Template upgrade --- .copier-answers.yml | 2 +- docs/css/mkdocstrings.css | 1 + docs/insiders/index.md | 2 +- docs/insiders/installation.md | 2 +- duties.py | 6 +++--- mkdocs.yml | 5 +++++ pyproject.toml | 2 +- 7 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.copier-answers.yml b/.copier-answers.yml index ab242b1c..c720007f 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: 0.16.4 +_commit: 0.16.6 _src_path: gh:pawamoy/copier-pdm.git author_email: pawamoy@pm.me author_fullname: Timothée Mazzucotelli diff --git a/docs/css/mkdocstrings.css b/docs/css/mkdocstrings.css index 2db20680..3960e49e 100644 --- a/docs/css/mkdocstrings.css +++ b/docs/css/mkdocstrings.css @@ -9,6 +9,7 @@ a.external::after, a.autorefs-external::after { /* https://primer.style/octicons/arrow-up-right-24 */ mask-image: url('data:image/svg+xml,'); + -webkit-mask-image: url('data:image/svg+xml,'); content: ' '; display: inline-block; diff --git a/docs/insiders/index.md b/docs/insiders/index.md index 3a55ad5d..bfb2d428 100644 --- a/docs/insiders/index.md +++ b/docs/insiders/index.md @@ -219,7 +219,7 @@ by the [ISC License][license]. However, we kindly ask you to respect our [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/ +[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 index 368f1b47..b7af7d2e 100644 --- a/docs/insiders/installation.md +++ b/docs/insiders/installation.md @@ -107,7 +107,7 @@ or installing a package (with pip), and depending on the registry you are using Please check the documentation of your registry to learn how to configure your environment. **We kindly ask that you do not upload the distributions to public registries, -as it is against our [Terms of use](../#terms).** +as it is against our [Terms of use](index.md#terms).** >? TIP: **Full example with `pypiserver`** > In this example we use [pypiserver] to serve a local PyPI index. diff --git a/duties.py b/duties.py index 2e1248bf..644b2ffb 100644 --- a/duties.py +++ b/duties.py @@ -53,10 +53,10 @@ def merge(d1: Any, d2: Any) -> Any: # noqa: D103 def mkdocs_config() -> str: # noqa: D103 - from mkdocs import utils + import mergedeep - # patch YAML loader to merge arrays - utils.merge = merge + # force YAML loader to merge arrays + mergedeep.merge = merge if "+insiders" in pkgversion("mkdocs-material"): return "mkdocs.insiders.yml" diff --git a/mkdocs.yml b/mkdocs.yml index d7500280..65541ffc 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,6 +8,11 @@ watch: [mkdocs.yml, README.md, CONTRIBUTING.md, CHANGELOG.md, src/mkdocstrings] copyright: Copyright © 2019 Timothée Mazzucotelli edit_uri: edit/main/docs/ +validation: + omitted_files: warn + absolute_links: warn + unrecognized_links: warn + nav: - Home: - Overview: index.md diff --git a/pyproject.toml b/pyproject.toml index 57f09f4d..78af6bff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,7 +75,7 @@ docs = [ "black>=23.1", "markdown-callouts>=0.2", "markdown-exec>=0.5", - "mkdocs>=1.3", + "mkdocs>=1.5", "mkdocs-coverage>=0.2", "mkdocs-gen-files>=0.3", "mkdocs-git-committers-plugin-2>=1.1", From 0b06d6a7ea1e45edfb066cfd3acb4bfca9927b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 28 Aug 2023 16:02:31 +0200 Subject: [PATCH 21/28] docs: Fix link (MkDocs 1.5) --- docs/usage/handlers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/handlers.md b/docs/usage/handlers.md index f4c474f9..d2c25420 100644 --- a/docs/usage/handlers.md +++ b/docs/usage/handlers.md @@ -234,7 +234,7 @@ could add specific support for another Python library. NOTE: This feature is intended for developers. If you are a user and want to customize how objects are rendered, -see [Theming / Customization](../theming/#customization). +see [Theming / Customization](theming.md#customization). Such extensions can register additional template folders that will be used when rendering collected data. From 228fb737caca4e20e600053bf59cbfa3e9c73906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 28 Aug 2023 16:06:07 +0200 Subject: [PATCH 22/28] feat: Register all anchors for each object in the inventory --- src/mkdocstrings/extension.py | 29 ++++++++++++++++++++++------- src/mkdocstrings/inventory.py | 29 ++++++++++++++--------------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/mkdocstrings/extension.py b/src/mkdocstrings/extension.py index c33f37ef..139030ba 100644 --- a/src/mkdocstrings/extension.py +++ b/src/mkdocstrings/extension.py @@ -137,13 +137,28 @@ def run(self, parent: Element, blocks: MutableSequence[str]) -> None: self._autorefs.register_anchor(page, rendered_anchor) if "data-role" in heading.attrib: - for anchor in sorted({rendered_anchor, *handler.get_anchors(data)}): - self._handlers.inventory.register( - name=anchor, - domain=handler.domain, - role=heading.attrib["data-role"], - uri=f"{page}#{rendered_anchor}", - ) + self._handlers.inventory.register( + name=rendered_anchor, + domain=handler.domain, + role=heading.attrib["data-role"], + priority=1, # register with standard priority + uri=f"{page}#{rendered_anchor}", + ) + + # also register other anchors for this object in the inventory + try: + data_object = handler.collect(rendered_anchor, handler.fallback_config) + except CollectionError: + continue + for anchor in handler.get_anchors(data_object): + if anchor not in self._handlers.inventory: + self._handlers.inventory.register( + name=anchor, + domain=handler.domain, + role=heading.attrib["data-role"], + priority=2, # register with lower priority + uri=f"{page}#{rendered_anchor}", + ) parent.append(el) diff --git a/src/mkdocstrings/inventory.py b/src/mkdocstrings/inventory.py index 4f2157ca..98dd347a 100644 --- a/src/mkdocstrings/inventory.py +++ b/src/mkdocstrings/inventory.py @@ -20,7 +20,7 @@ def __init__( domain: str, role: str, uri: str, - priority: str = "1", + priority: int = 1, dispname: str | None = None, ): """Initialize the object. @@ -30,14 +30,14 @@ def __init__( domain: The item domain, like 'python' or 'crystal'. role: The item role, like 'class' or 'method'. uri: The item URI. - priority: The item priority. It can help for inventory suggestions. + priority: The item priority. Only used internally by mkdocstrings and Sphinx. dispname: The item display name. """ self.name: str = name self.domain: str = domain self.role: str = role self.uri: str = uri - self.priority: str = priority + self.priority: int = priority self.dispname: str = dispname or name def format_sphinx(self) -> str: @@ -67,7 +67,7 @@ def parse_sphinx(cls, line: str) -> InventoryItem: uri = uri[:-1] + name if dispname == "-": dispname = name - return cls(name, domain, role, uri, priority, dispname) + return cls(name, domain, role, uri, int(priority), dispname) class Inventory(dict): @@ -94,7 +94,7 @@ def register( domain: str, role: str, uri: str, - priority: str = "1", + priority: int = 1, dispname: str | None = None, ) -> None: """Create and register an item. @@ -104,18 +104,17 @@ def register( domain: The item domain, like 'python' or 'crystal'. role: The item role, like 'class' or 'method'. uri: The item URI. - priority: The item priority. It can help for inventory suggestions. + priority: The item priority. Only used internally by mkdocstrings and Sphinx. dispname: The item display name. """ - if name not in self: - self[name] = InventoryItem( - name=name, - domain=domain, - role=role, - uri=uri, - priority=priority, - dispname=dispname, - ) + self[name] = InventoryItem( + name=name, + domain=domain, + role=role, + uri=uri, + priority=priority, + dispname=dispname, + ) def format_sphinx(self) -> bytes: """Format this inventory as a Sphinx `objects.inv` file. From 9371e9fc7dd68506b73aa1580a12c5f5cd779aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 28 Aug 2023 16:06:45 +0200 Subject: [PATCH 23/28] refactor: Sort inventories --- src/mkdocstrings/inventory.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mkdocstrings/inventory.py b/src/mkdocstrings/inventory.py index 98dd347a..2a1e6865 100644 --- a/src/mkdocstrings/inventory.py +++ b/src/mkdocstrings/inventory.py @@ -135,7 +135,10 @@ def format_sphinx(self) -> bytes: .encode("utf8") ) - lines = [item.format_sphinx().encode("utf8") for item in self.values()] + lines = [ + item.format_sphinx().encode("utf8") + for item in sorted(self.values(), key=lambda item: (item.domain, item.name)) + ] return header + zlib.compress(b"\n".join(lines) + b"\n", 9) @classmethod @@ -155,4 +158,4 @@ def parse_sphinx(cls, in_file: BinaryIO, *, domain_filter: Collection[str] = ()) items = [InventoryItem.parse_sphinx(line.decode("utf8")) for line in lines] if domain_filter: items = [item for item in items if item.domain in domain_filter] - return cls(items) + return cls(sorted(items, key=lambda item: (item.domain, item.name))) From b89bb2de34b23908edff0aa82813404702d29600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 28 Aug 2023 16:21:04 +0200 Subject: [PATCH 24/28] docs: Remove old contents --- src/mkdocstrings/handlers/base.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/mkdocstrings/handlers/base.py b/src/mkdocstrings/handlers/base.py index 4fa79724..06430da6 100644 --- a/src/mkdocstrings/handlers/base.py +++ b/src/mkdocstrings/handlers/base.py @@ -1,11 +1,6 @@ """Base module for handlers. This module contains the base classes for implementing handlers. - -It also provides two methods: - -- `get_handler`, that will cache handlers into the `HANDLERS_CACHE` dictionary. -- `teardown`, that will teardown all the cached handlers, and then clear the cache. """ from __future__ import annotations From fe8e3c5044c7dffa5c2de0c7532fe015cfd9383a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 28 Aug 2023 16:21:14 +0200 Subject: [PATCH 25/28] docs: Enable auto-summaries --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index 65541ffc..fdd6898b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -142,6 +142,7 @@ plugins: show_symbol_type_heading: true show_symbol_type_toc: true signature_crossrefs: true + summary: true - git-committers: enabled: !ENV [DEPLOY, false] repository: mkdocstrings/mkdocstrings From 9397460bc7a6de4c73e0a545b9bc148db29893cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 28 Aug 2023 16:29:10 +0200 Subject: [PATCH 26/28] refactor: Try calling deprecated `get_anchor` for a bit longer --- src/mkdocstrings/handlers/base.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mkdocstrings/handlers/base.py b/src/mkdocstrings/handlers/base.py index 06430da6..700a0565 100644 --- a/src/mkdocstrings/handlers/base.py +++ b/src/mkdocstrings/handlers/base.py @@ -233,7 +233,7 @@ def get_extended_templates_dirs(self, handler: str) -> list[Path]: discovered_extensions = entry_points(group=f"mkdocstrings.{handler}.templates") return [extension.load()() for extension in discovered_extensions] - def get_anchors(self, data: CollectorItem) -> tuple[str, ...]: # noqa: ARG002 + def get_anchors(self, data: CollectorItem) -> tuple[str, ...]: """Return the possible identifiers (HTML anchors) for a collected item. Arguments: @@ -242,7 +242,11 @@ def get_anchors(self, data: CollectorItem) -> tuple[str, ...]: # noqa: ARG002 Returns: The HTML anchors (without '#'), or an empty tuple if this item doesn't have an anchor. """ - return () + # TODO: remove this when https://github.com/mkdocstrings/crystal/pull/6 is merged and released + try: + return (self.get_anchor(data),) # type: ignore[attr-defined] + except AttributeError: + return () def do_convert_markdown( self, From 3ed3453b893b53ee129cbd9fece3402c7e7083fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 2 Sep 2023 11:31:06 +0200 Subject: [PATCH 27/28] refactor: Don't sort inventories when reading them, it's useless --- src/mkdocstrings/inventory.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mkdocstrings/inventory.py b/src/mkdocstrings/inventory.py index 2a1e6865..f1c8962a 100644 --- a/src/mkdocstrings/inventory.py +++ b/src/mkdocstrings/inventory.py @@ -150,7 +150,7 @@ def parse_sphinx(cls, in_file: BinaryIO, *, domain_filter: Collection[str] = ()) domain_filter: A collection of domain values to allow (and filter out all other ones). Returns: - An `Inventory` containing the collected `InventoryItem`s. + An inventory containing the collected items. """ for _ in range(4): in_file.readline() @@ -158,4 +158,4 @@ def parse_sphinx(cls, in_file: BinaryIO, *, domain_filter: Collection[str] = ()) items = [InventoryItem.parse_sphinx(line.decode("utf8")) for line in lines] if domain_filter: items = [item for item in items if item.domain in domain_filter] - return cls(sorted(items, key=lambda item: (item.domain, item.name))) + return cls(items) From c071740f20ab7b5d5d1a4ef1fc7193663280a903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 2 Sep 2023 11:31:54 +0200 Subject: [PATCH 28/28] chore: Prepare release 0.23.0 --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbeb8ce6..fc3a0fb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,34 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.23.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.23.0) - 2023-08-28 + +[Compare with 0.22.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.22.0...0.23.0) + +### Breaking Changes + +- Removed `BaseCollector` and `BaseRenderer` classes: they were merged into the `BaseHandler` class. +- Removed the watch feature, as MkDocs now provides it natively. +- Removed support for `selection` and `rendering` keys in YAML blocks: use `options` instead. +- Removed support for loading handlers from the `mkdocstrings.handler` namespace. + Handlers must now be packaged under the `mkdocstrings_handlers` namespace. + +### Features + +- Register all anchors for each object in the inventory ([228fb73](https://github.com/mkdocstrings/mkdocstrings/commit/228fb737caca4e20e600053bf59cbfa3e9c73906) by Timothée Mazzucotelli). + +### Bug Fixes + +- Don't add `codehilite` CSS class to inline code ([7690d41](https://github.com/mkdocstrings/mkdocstrings/commit/7690d41e2871997464367e673023585c4fb05e26) by Timothée Mazzucotelli). +- Support cross-references for API docs rendered in top-level index page ([b194452](https://github.com/mkdocstrings/mkdocstrings/commit/b194452be93aee33b3c28a468762b4d96c501f4f) by Timothée Mazzucotelli). + +### Code Refactoring + +- Sort inventories before writing them to disk ([9371e9f](https://github.com/mkdocstrings/mkdocstrings/commit/9371e9fc7dd68506b73aa1580a12c5f5cd779aba) by Timothée Mazzucotelli). +- Stop accepting sets as return value of `get_anchors` (only tuples), to preserve order ([2e10374](https://github.com/mkdocstrings/mkdocstrings/commit/2e10374be258e9713b26f73dd06d0c2520ec07a5) by Timothée Mazzucotelli). +- Remove deprecated parts ([0a90a47](https://github.com/mkdocstrings/mkdocstrings/commit/0a90a474c8dcbd95821700d7dab63f03e392c40f) by Timothée Mazzucotelli). +- Use proper parameters in `Inventory.register` method ([433c6e0](https://github.com/mkdocstrings/mkdocstrings/commit/433c6e01aab9333589f755e483f124db0836f143) by Timothée Mazzucotelli). + ## [0.22.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.22.0) - 2023-05-25 [Compare with 0.21.2](https://github.com/mkdocstrings/mkdocstrings/compare/0.21.2...0.22.0)