From 7205ac6cf2861db61c2a5b8bf07d0e6b1a7f49fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 27 Dec 2021 19:14:09 +0100 Subject: [PATCH 01/21] build: Drop Python 3.6 support --- .copier-answers.yml | 2 +- .github/workflows/ci.yml | 1 - README.md | 10 +++++----- duties.py | 35 +++-------------------------------- pyproject.toml | 15 +++++++-------- scripts/multirun.sh | 2 +- scripts/setup.sh | 2 +- 7 files changed, 18 insertions(+), 49 deletions(-) diff --git a/.copier-answers.yml b/.copier-answers.yml index 7ad1c140..4c68517b 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: 0.7.1 +_commit: 0.8.0 _src_path: gh:pawamoy/copier-pdm.git author_email: pawamoy@pm.me author_fullname: "Timoth\xE9e Mazzucotelli" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 019502b4..05966bd2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,7 +72,6 @@ jobs: - macos-latest - windows-latest python-version: - - "3.6" - "3.7" - "3.8" - "3.9" diff --git a/README.md b/README.md index d914dc4f..79897348 100644 --- a/README.md +++ b/README.md @@ -94,10 +94,10 @@ Automatic documentation from sources, for [MkDocs](https://mkdocs.org/). ## Requirements -*mkdocstrings* requires Python 3.6 or above. +mkdocstrings requires Python 3.7 or above.
-To install Python 3.6, I recommend using pyenv. +To install Python 3.7, I recommend using pyenv. ```bash # install pyenv @@ -108,11 +108,11 @@ export PATH="${HOME}/.pyenv/bin:${PATH}" export PYENV_ROOT="${HOME}/.pyenv" eval "$(pyenv init -)" -# install Python 3.6 -pyenv install 3.6.12 +# install Python 3.7 +pyenv install 3.7.12 # make it available globally -pyenv global system 3.6.12 +pyenv global system 3.7.12 ```
diff --git a/duties.py b/duties.py index 879445e2..e2623dca 100644 --- a/duties.py +++ b/duties.py @@ -6,7 +6,6 @@ import sys import tempfile from contextlib import suppress -from functools import wraps from io import StringIO from pathlib import Path from typing import List, Optional, Pattern @@ -43,7 +42,6 @@ def update_changelog( marker: str, version_regex: str, template_url: str, - commit_style: str, ) -> None: """Update the given changelog file in place. @@ -52,15 +50,16 @@ def update_changelog( marker: The line after which to insert new contents. version_regex: A regular expression to find currently documented versions in the file. template_url: The URL to the Jinja template used to render contents. - commit_style: The style of commit messages to parse. """ from git_changelog.build import Changelog + from git_changelog.commit import AngularStyle from jinja2.sandbox import SandboxedEnvironment + AngularStyle.DEFAULT_RENDER.insert(0, AngularStyle.TYPES["build"]) env = SandboxedEnvironment(autoescape=False) template_text = urlopen(template_url).read().decode("utf8") # noqa: S310 template = env.from_string(template_text) - changelog = Changelog(".", style=commit_style) + changelog = Changelog(".", style="angular") if len(changelog.versions_list) == 1: last_version = changelog.versions_list[0] @@ -99,7 +98,6 @@ def changelog(ctx): "marker": "", "version_regex": r"^## \[v?(?P[^\]]+)", "template_url": template_url, - "commit_style": "angular", }, title="Updating changelog", pty=PTY, @@ -165,31 +163,7 @@ def safety(): # noqa: WPS430 ctx.run(safety, title="Checking dependencies") -def no_docs_py36(nofail=True): - """Decorate a duty that builds docs to warn that it's not possible on Python 3.6. - - Arguments: - nofail: Whether to fail or not. - - Returns: - The decorated function. - """ - - def decorator(func): - @wraps(func) - def wrapper(ctx): - if sys.version_info <= (3, 7, 0): - ctx.run(["false"], title="Docs can't be built on Python 3.6", nofail=nofail, quiet=True) - else: - func(ctx) - - return wrapper - - return decorator - - @duty -@no_docs_py36() def check_docs(ctx): """Check if the documentation builds correctly. @@ -274,7 +248,6 @@ def clean(ctx): @duty -@no_docs_py36(nofail=False) def docs(ctx): """Build the documentation locally. @@ -285,7 +258,6 @@ def docs(ctx): @duty -@no_docs_py36(nofail=False) def docs_serve(ctx, host="127.0.0.1", port=8000): """Serve the documentation (localhost:8000). @@ -298,7 +270,6 @@ def docs_serve(ctx, host="127.0.0.1", port=8000): @duty -@no_docs_py36(nofail=False) def docs_deploy(ctx): """Deploy the documentation on GitHub pages. diff --git a/pyproject.toml b/pyproject.toml index ac0bd9de..538bdbae 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 = {file = "LICENSE"} readme = "README.md" -requires-python = ">=3.6.2" +requires-python = ">=3.7" keywords = ["mkdocs", "mkdocs-plugin", "docstrings", "autodoc", "documentation"] dynamic = ["version"] classifiers = [ @@ -18,7 +18,6 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", @@ -61,12 +60,12 @@ version = {use_scm = true} [tool.pdm.dev-dependencies] duty = ["duty>=0.7"] docs = [ - "mkdocs-coverage>=0.2; python_version >= '3.7'", - "mkdocs-gen-files>=0.3; python_version >= '3.7'", - "mkdocs-literate-nav>=0.4; python_version >= '3.7'", - "mkdocs-material>=7.3; python_version >= '3.7'", - "mkdocs-section-index>=0.3; python_version >= '3.7'", - "toml>=0.10; python_version >= '3.7'", + "mkdocs-coverage>=0.2", + "mkdocs-gen-files>=0.3", + "mkdocs-literate-nav>=0.4", + "mkdocs-material>=7.3", + "mkdocs-section-index>=0.3", + "toml>=0.10", ] format = [ "autoflake>=1.4", diff --git a/scripts/multirun.sh b/scripts/multirun.sh index 7b5d9cf2..609dfd44 100755 --- a/scripts/multirun.sh +++ b/scripts/multirun.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -e -PYTHON_VERSIONS="${PYTHON_VERSIONS-3.6 3.7 3.8 3.9 3.10 3.11}" +PYTHON_VERSIONS="${PYTHON_VERSIONS-3.7 3.8 3.9 3.10 3.11}" if [ -n "${PYTHON_VERSIONS}" ]; then for python_version in ${PYTHON_VERSIONS}; do diff --git a/scripts/setup.sh b/scripts/setup.sh index c5df7b4c..f0a41cf8 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -e -PYTHON_VERSIONS="${PYTHON_VERSIONS-3.6 3.7 3.8 3.9 3.10 3.11}" +PYTHON_VERSIONS="${PYTHON_VERSIONS-3.7 3.8 3.9 3.10 3.11}" install_with_pipx() { if ! command -v "$1" &>/dev/null; then From 47721420f54861301c5fd1de26ff7c08f436ddb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Tue, 28 Dec 2021 22:24:19 +0100 Subject: [PATCH 02/21] ci: Allow failures on Python 3.11 --- duties.py | 1 + 1 file changed, 1 insertion(+) diff --git a/duties.py b/duties.py index e2623dca..71aa2f00 100644 --- a/duties.py +++ b/duties.py @@ -341,4 +341,5 @@ def test(ctx, match: str = ""): ["pytest", "-c", "config/pytest.ini", "-n", "auto", "-k", match, "tests"], title="Running tests", pty=PTY, + nofail=py_version == "311", ) From fc6c7f652a420ac29cf16cbb99b11a55aa9b38ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Tue, 28 Dec 2021 22:24:42 +0100 Subject: [PATCH 03/21] build: Update autorefs to actually required version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 538bdbae..e521e39a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ dependencies = [ "Markdown>=3.3", "MarkupSafe>=1.1", "mkdocs>=1.2", - "mkdocs-autorefs>=0.1", + "mkdocs-autorefs>=0.3.1", "pymdown-extensions>=6.3", "pytkdocs>=0.14.0", ] From faf2fad74891b007835fdc94b1012165e99e19b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Tue, 28 Dec 2021 22:25:06 +0100 Subject: [PATCH 04/21] tests: Stop using pytest-sugar --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e521e39a..ee408af9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,7 +98,6 @@ tests = [ "pytest>=6.2", "pytest-cov>=3.0", "pytest-randomly>=3.10", - "pytest-sugar>=0.9", "pytest-xdist>=2.4", "sphinx", ] From f42dfc61ce4f9f317c4bd17f568e504ed9764d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Tue, 28 Dec 2021 22:55:11 +0100 Subject: [PATCH 05/21] feat: Support handlers spanning multiple locations Namespace packages can be installed into multiple locations (and not merged into a single one). To find the right templates directory, we must therefore find the right handler path. PR #355: https://github.com/mkdocstrings/mkdocstrings/pull/355 --- src/mkdocstrings/handlers/base.py | 32 ++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/mkdocstrings/handlers/base.py b/src/mkdocstrings/handlers/base.py index 32da5a20..a4de3831 100644 --- a/src/mkdocstrings/handlers/base.py +++ b/src/mkdocstrings/handlers/base.py @@ -9,6 +9,7 @@ """ import importlib +import sys from abc import ABC, abstractmethod from pathlib import Path from typing import Any, Dict, Iterable, List, Optional, Sequence @@ -29,8 +30,6 @@ CollectorItem = Any -TEMPLATES_DIR = Path(__file__).parent.parent / "templates" - class CollectionError(Exception): """An exception raised when some collection of data failed.""" @@ -86,11 +85,10 @@ def __init__(self, directory: str, theme: str, custom_templates: Optional[str] = """ paths = [] - themes_dir = TEMPLATES_DIR / directory - + themes_dir = self.get_templates_dir() / directory paths.append(themes_dir / theme) - if self.fallback_theme: + if self.fallback_theme and self.fallback_theme != theme: paths.append(themes_dir / self.fallback_theme) for path in paths: @@ -125,6 +123,30 @@ def render(self, data: CollectorItem, config: dict) -> str: The rendered template as HTML. """ # noqa: DAR202 (excess return section) + def get_templates_dir(self) -> Path: + """Return the path to the handler's templates directory. + + Override this method if your handler is for example compiled from C + and does not expose a module directly on the file system from + which we can infer the templates directory path. + + Returns: + The templates directory path. + """ + # Namespace packages can span multiple locations. + # This class can be derived, so we must find the templates path + # based on the file path the derived class was defined in. + # To do this, we first get the module of this derived class: + module = sys.modules[self.__class__.__module__] # noqa: WPS609 + # Then we can get the module path: + module_path = Path(module.__file__) # type: ignore[arg-type] # noqa: WPS609 + # Now we can go up to the "mkdocstrings" folder, + # and one down to the "templates" folder: + templates_dir = module_path.parent.resolve() + while templates_dir.name != "mkdocstrings": + templates_dir = templates_dir.parent + return templates_dir / "templates" + def get_anchors(self, data: CollectorItem) -> Sequence[str]: """Return the possible identifiers (HTML anchors) for a collected item. From 5351fc8b417fb20f0681a22f49fcc902579eacdb Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Wed, 2 Feb 2022 12:11:07 +0100 Subject: [PATCH 06/21] feat: Allow unwrapping the `

` tag in `convert_markdown` filter PR #369: https://github.com/mkdocstrings/mkdocstrings/pull/369 --- src/mkdocstrings/handlers/base.py | 6 +++++- src/mkdocstrings/handlers/rendering.py | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/mkdocstrings/handlers/base.py b/src/mkdocstrings/handlers/base.py index a4de3831..717e2e61 100644 --- a/src/mkdocstrings/handlers/base.py +++ b/src/mkdocstrings/handlers/base.py @@ -24,6 +24,7 @@ Highlighter, IdPrependingTreeprocessor, MkdocstringsInnerExtension, + ParagraphStrippingTreeprocessor, ) from mkdocstrings.inventory import Inventory from mkdocstrings.loggers import get_template_logger @@ -162,13 +163,14 @@ def get_anchors(self, data: CollectorItem) -> Sequence[str]: except AttributeError: return () - def do_convert_markdown(self, text: str, heading_level: int, html_id: str = "") -> Markup: + def do_convert_markdown(self, text: str, heading_level: int, html_id: str = "", *, strip_paragraph=False) -> Markup: """Render Markdown text; for use inside templates. Arguments: text: The text to convert. heading_level: The base heading level to start all Markdown headings from. html_id: The HTML id of the element that's considered the parent of this element. + strip_paragraph: Whether to exclude the

tag from around the whole output. Returns: An HTML string. @@ -176,11 +178,13 @@ def do_convert_markdown(self, text: str, heading_level: int, html_id: str = "") treeprocessors = self._md.treeprocessors treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = heading_level treeprocessors[IdPrependingTreeprocessor.name].id_prefix = html_id and html_id + "--" + treeprocessors[ParagraphStrippingTreeprocessor.name].strip = strip_paragraph try: return Markup(self._md.convert(text)) finally: treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = 0 treeprocessors[IdPrependingTreeprocessor.name].id_prefix = "" + treeprocessors[ParagraphStrippingTreeprocessor.name].strip = False self._md.reset() def do_heading( diff --git a/src/mkdocstrings/handlers/rendering.py b/src/mkdocstrings/handlers/rendering.py index c28beb00..ef497861 100644 --- a/src/mkdocstrings/handlers/rendering.py +++ b/src/mkdocstrings/handlers/rendering.py @@ -209,6 +209,19 @@ def run(self, root: Element): self.headings.append(el) +class ParagraphStrippingTreeprocessor(Treeprocessor): + """Unwraps the

element around the whole output.""" + + name = "mkdocstrings_strip_paragraph" + strip = False + + def run(self, root: Element): # noqa: D102 (ignore missing docstring) + if self.strip and len(root) == 1 and root[0].tag == "p": + # Turn the single

element into the root element and inherit its tag name (it's significant!) + root[0].tag = root.tag + return root[0] + + class MkdocstringsInnerExtension(Extension): """Extension that should always be added to Markdown sub-documents that handlers request (and *only* them).""" @@ -243,3 +256,8 @@ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent me _HeadingReportingTreeprocessor.name, priority=1, # Close to the end. ) + md.treeprocessors.register( + ParagraphStrippingTreeprocessor(md), + ParagraphStrippingTreeprocessor.name, + priority=0.99, # Close to the end. + ) From b37722716b1e0ed6393ec71308dfb0f85e142f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Wed, 2 Feb 2022 12:27:27 +0100 Subject: [PATCH 07/21] refactor: Support Jinja2 3.1 Issue #360: https://github.com/mkdocstrings/mkdocstrings/issues/360 PR #361: https://github.com/mkdocstrings/mkdocstrings/issues/361 --- src/mkdocstrings/loggers.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mkdocstrings/loggers.py b/src/mkdocstrings/loggers.py index 1b1a5dff..6be121ed 100644 --- a/src/mkdocstrings/loggers.py +++ b/src/mkdocstrings/loggers.py @@ -4,10 +4,14 @@ from pathlib import Path from typing import Any, Callable, Optional, Tuple -from jinja2 import contextfunction from jinja2.runtime import Context from mkdocs.utils import warning_filter +try: + from jinja2 import pass_context +except ImportError: # TODO: remove once Jinja2 < 3.1 is dropped + from jinja2 import contextfunction as pass_context # noqa: WPS440 + TEMPLATES_DIR = Path(__file__).parent / "templates" @@ -71,7 +75,7 @@ def get_template_logger_function(logger_func: Callable) -> Callable: A function. """ - @contextfunction + @pass_context def wrapper(context: Context, msg: Optional[str] = None) -> str: """Log a message. From 14ab3b86f7002e35eab688febebdabb889f1016c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 5 Feb 2022 13:12:33 +0100 Subject: [PATCH 08/21] docs: Add YAML resources Discussion #352: https://github.com/mkdocstrings/mkdocstrings/discussions/352 PR #370: https://github.com/mkdocstrings/mkdocstrings/pull/370 --- docs/usage.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/usage.md b/docs/usage.md index 703dcab7..05ad99bc 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -11,6 +11,14 @@ The syntax is as follows: YAML block ``` +!!! note "Resources on YAML" + YAML can sometimes be a bit tricky, particularly on indentation. + Here are some resources that other users found useful to better + understand YAML's peculiarities. + + - [YAML idiosyncrasies](https://docs.saltproject.io/en/3000/topics/troubleshooting/yaml_idiosyncrasies.html) + - [YAML multiline](https://yaml-multiline.info/) + The `identifier` is a string identifying the object you want to document. The format of an identifier can vary from one handler to another. For example, the Python handler expects the full dotted-path to a Python object: From 74371e49c32059fefd34c7cc7f7b8f085b383237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 5 Feb 2022 13:27:19 +0100 Subject: [PATCH 09/21] refactor: Extract the Python handler into its own repository PR #356: https://github.com/mkdocstrings/mkdocstrings/pull/356 --- docs/handlers/overview.md | 2 +- docs/handlers/python.md | 364 ---------------- docs/troubleshooting.md | 4 +- docs/usage.md | 2 +- duties.py | 1 - mkdocs.yml | 2 +- pyproject.toml | 10 +- src/mkdocstrings/handlers/python.py | 392 ------------------ src/mkdocstrings/plugin.py | 8 +- .../templates/python/material/attribute.html | 57 --- .../templates/python/material/attributes.html | 20 - .../templates/python/material/children.html | 99 ----- .../templates/python/material/class.html | 74 ---- .../templates/python/material/docstring.html | 36 -- .../templates/python/material/examples.html | 9 - .../templates/python/material/exceptions.html | 18 - .../templates/python/material/function.html | 64 --- .../python/material/keyword_args.html | 20 - .../templates/python/material/method.html | 64 --- .../templates/python/material/module.html | 60 --- .../templates/python/material/parameters.html | 22 - .../templates/python/material/properties.html | 8 - .../templates/python/material/return.html | 16 - .../templates/python/material/signature.html | 31 -- .../templates/python/material/style.css | 15 - .../templates/python/material/yield.html | 16 - .../templates/python/mkdocs/exceptions.html | 7 - .../templates/python/mkdocs/keyword_args.html | 7 - .../templates/python/mkdocs/parameters.html | 7 - .../templates/python/mkdocs/return.html | 5 - .../templates/python/mkdocs/style.css | 11 - .../templates/python/mkdocs/yield.html | 5 - .../python/readthedocs/exceptions.html | 19 - .../python/readthedocs/keyword_args.html | 19 - .../python/readthedocs/parameters.html | 19 - .../templates/python/readthedocs/return.html | 17 - .../templates/python/readthedocs/style.css | 27 -- .../templates/python/readthedocs/yield.html | 17 - tests/fixtures/html_escaped_sequences.py | 12 - tests/test_extension.py | 5 - tests/test_python_handler.py | 86 ---- tests/test_themes.py | 35 -- 42 files changed, 16 insertions(+), 1696 deletions(-) delete mode 100644 docs/handlers/python.md delete mode 100644 src/mkdocstrings/handlers/python.py delete mode 100644 src/mkdocstrings/templates/python/material/attribute.html delete mode 100644 src/mkdocstrings/templates/python/material/attributes.html delete mode 100644 src/mkdocstrings/templates/python/material/children.html delete mode 100644 src/mkdocstrings/templates/python/material/class.html delete mode 100644 src/mkdocstrings/templates/python/material/docstring.html delete mode 100644 src/mkdocstrings/templates/python/material/examples.html delete mode 100644 src/mkdocstrings/templates/python/material/exceptions.html delete mode 100644 src/mkdocstrings/templates/python/material/function.html delete mode 100644 src/mkdocstrings/templates/python/material/keyword_args.html delete mode 100644 src/mkdocstrings/templates/python/material/method.html delete mode 100644 src/mkdocstrings/templates/python/material/module.html delete mode 100644 src/mkdocstrings/templates/python/material/parameters.html delete mode 100644 src/mkdocstrings/templates/python/material/properties.html delete mode 100644 src/mkdocstrings/templates/python/material/return.html delete mode 100644 src/mkdocstrings/templates/python/material/signature.html delete mode 100644 src/mkdocstrings/templates/python/material/style.css delete mode 100644 src/mkdocstrings/templates/python/material/yield.html delete mode 100644 src/mkdocstrings/templates/python/mkdocs/exceptions.html delete mode 100644 src/mkdocstrings/templates/python/mkdocs/keyword_args.html delete mode 100644 src/mkdocstrings/templates/python/mkdocs/parameters.html delete mode 100644 src/mkdocstrings/templates/python/mkdocs/return.html delete mode 100644 src/mkdocstrings/templates/python/mkdocs/style.css delete mode 100644 src/mkdocstrings/templates/python/mkdocs/yield.html delete mode 100644 src/mkdocstrings/templates/python/readthedocs/exceptions.html delete mode 100644 src/mkdocstrings/templates/python/readthedocs/keyword_args.html delete mode 100644 src/mkdocstrings/templates/python/readthedocs/parameters.html delete mode 100644 src/mkdocstrings/templates/python/readthedocs/return.html delete mode 100644 src/mkdocstrings/templates/python/readthedocs/style.css delete mode 100644 src/mkdocstrings/templates/python/readthedocs/yield.html delete mode 100644 tests/fixtures/html_escaped_sequences.py delete mode 100644 tests/test_python_handler.py delete mode 100644 tests/test_themes.py diff --git a/docs/handlers/overview.md b/docs/handlers/overview.md index 351a356e..47e8fb7e 100644 --- a/docs/handlers/overview.md +++ b/docs/handlers/overview.md @@ -4,7 +4,7 @@ A handler is what makes it possible to collect and render documentation for a pa ## Available handlers -- [Python](python.md) +- Python - Crystal ## Custom handlers diff --git a/docs/handlers/python.md b/docs/handlers/python.md deleted file mode 100644 index 967bbd58..00000000 --- a/docs/handlers/python.md +++ /dev/null @@ -1,364 +0,0 @@ -# Python handler - -## Handler options - -Like every handler, the Python handler accepts the common -[`selection`](#selection) and [`rendering`](#rendering) options, -both as **global** and **local** options. -The `selection` options gives you control over the selection of Python objects, -while the `rendering` options lets you change how the documentation is rendered. - -It also accepts these additional **global-only** options: - -Option | Type | Description | Default ------- | ---- | ----------- | ------- -**`setup_commands`** | `list of str` | Run these commands before starting the documentation collection. | `[]` - -!!! example "Example: setup Django before collecting documentation" - ```yaml - # mkdocs.yml - plugins: - - mkdocstrings: - handlers: - python: - setup_commands: - - import os - - import django - - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_django_app.settings") - - django.setup() - ``` - -!!! important - Additional options like `setup_commands` are used only once, - when instantiating the handler the first time it is requested. - This is why they are considered global-only options, - as they will have no effect if used as local options. - -### Selection - -The following options are directly passed to the handler's collector. -See [Collector: pytkdocs](#collector-pytkdocs) to learn more about `pytkdocs`. - -Option | Type | Description | Default ------- | ---- | ----------- | ------- -**`filters`** | `list of str` | List of filtering regular expressions. Prefix with `!` to exclude objects whose name match. The default means *exclude private members*. | `["!^_[^_]"]` -**`members`** | `bool`, or `list of str` | Explicitly select members. True means *all*, false means *none*. | `True` -**`inherited_members`** | `bool` | Also select members inherited from parent classes. | `False` -**`docstring_style`** | `str` | Docstring style to parse. `pytkdocs` supports `google`, `numpy` and `restructured-text`. *Note: Numpy-style requires the `numpy-style` extra of `pytkdocs`.* | `"google"` -**`docstring_options`** | `dict` | Options to pass to the docstring parser. See [Collector: pytkdocs](#collector-pytkdocs) | `{}` -**`new_path_syntax`** | `bool` | Whether to use the new "colon" path syntax when importing objects. | `False` - -!!! example "Configuration example" - === "Global" - ```yaml - # mkdocs.yml - plugins: - - mkdocstrings: - handlers: - python: - selection: - filters: - - "!^_" # exlude all members starting with _ - - "^__init__$" # but always include __init__ modules and methods - ``` - - === "Local" - ```yaml - ::: my_package - selection: - filters: [] # pick up everything - ``` - -### Rendering - -::: mkdocstrings.handlers.python:PythonRenderer.default_config - rendering: - show_root_toc_entry: false - -These options affect how the documentation is rendered. - -!!! example "Configuration example" - === "Global" - ```yaml - # mkdocs.yml - plugins: - - mkdocstrings: - handlers: - python: - rendering: - show_root_heading: yes - ``` - - === "Local" - ```md - ## `ClassA` - - ::: my_package.my_module.ClassA - rendering: - show_root_heading: no - heading_level: 3 - ``` - -## Collector: pytkdocs - -The tool used by the Python handler to collect documentation from Python source code -is [`pytkdocs`](https://pawamoy.github.io/pytkdocs). -It stands for *(Python) Take Docs*, and is supposed to be a pun on MkDocs (*Make Docs*?). - -### Supported docstrings styles - -Right now, `pytkdocs` supports the Google-style, Numpy-style and reStructuredText-style docstring formats. -The style used by default is the Google-style. -You can configure what style you want to use with -the `docstring_style` and `docstring_options` [selection options](#selection), -both globally or per autodoc instruction. - -#### Google-style - -You can see examples of Google-style docstrings -in [Napoleon's documentation](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html). - -##### Sections - -Docstrings sections are parsed by `pytkdocs` and rendered by *mkdocstrings*. -Supported sections are: - -- `Arguments` (or `Args`, `Parameters`, `Params`) -- `Attributes` -- `Examples` (or `Example`) -- `Raises` (or `Raise`, `Except`, `Exceptions`) -- `Returns` (or `Return`) - -##### Admonitions - -Additionally, any section that is not recognized will be transformed into its admonition equivalent. -For example: - -=== "Original" - ```python - """ - Note: You can disable this behavior with the `replace_admonitions` option. - To prevent `pytkdocs` from converting sections to admonitions, - use the `replace_admonitions`: - - ```md - ::: my_package.my_module - selection: - docstring_style: google # this is the default - docstring_options: - replace_admonitions: no - ``` - - So meta! - """ - ``` - -=== "Modified" - ```python - """ - !!! note "You can disable this behavior with the `replace_admonitions` option." - To prevent `pytkdocs` from converting sections to admonitions, - use the `replace_admonitions`: - - ```md - ::: my_package.my_module - selection: - docstring_style: google # this is the default - docstring_options: - replace_admonitions: no - ``` - - So meta! - """ - ``` - -=== "Result" - !!! note "You can disable this behavior with the `replace_admonitions` parser option." - To prevent `pytkdocs` from converting sections to admonitions, - use the `replace_admonitions` parser option: - - ```md - ::: my_package.my_module - selection: - docstring_style: google # this is the default - docstring_options: - replace_admonitions: no - ``` - - So meta! - -As shown in the above example, this can be disabled -with the `replace_admonitions` option of the Google-style parser: - -```yaml -::: my_package.my_module - selection: - docstring_style: google # this is the default - docstring_options: - replace_admonitions: no -``` - -##### Annotations - -Type annotations are read both in the code and in the docstrings. - -!!! example "Example with a function" - **Expand the source at the end to see the original code!** - - ::: snippets.function_annotations_google:my_function - rendering: - show_root_heading: no - show_root_toc_entry: no - -#### Numpy-style - -!!! important "Extra dependency required" - You'll need an extra dependency to parse Numpy-style docstrings: - - ``` - pdm add -d --group docs 'pytkdocs[numpy-style]' - poetry add -D 'pytkdocs[numpy-style]' - pip install 'pytkdocs[numpy-style]' - # etc. - ``` - -You can see examples of Numpy-style docstrings -in [numpydoc's documentation](https://numpydoc.readthedocs.io/en/latest/format.html). - -#### reStructuredText-style - -!!! warning "Partial support" - Only RST-**style** is supported, not the whole RST markup specification. - Docstrings will still be converted as Markdown. - -You can see examples of reStructuredText-style docstrings -in [Sphinx's documentation](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html). - -##### Sections - -Docstrings directives are parsed by `pytkdocs` and rendered by *mkdocstrings*. -Supported directives are: - -- `param` (or `parameter`, `arg`, `argument`, `key`, `keyword`) -- `type` -- `raises` (or `raise`, `except`, `exception`) -- `var` (or `ivar`, `cvar`) -- `vartype` -- `returns` (or `return1`) -- `rtype` - -Details about how to use each directive can be found in the -[Sphinx domain documentation](https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html?highlight=python%20domain#info-field-lists) - -##### Annotations - -Type annotations are read both in the code and in the docstrings. - -!!! example "Example with a function" - **Expand the source at the end to see the original code!** - - ::: snippets.function_annotations_rst:my_function - selection: - docstring_style: "restructured-text" - rendering: - show_root_heading: no - show_root_toc_entry: no - -## Finding modules - -In order for `pytkdocs` to find your packages and modules, -you should take advantage of the usual Python loading mechanisms: - -- install your package in the current virtualenv: - ```bash - . venv/bin/activate - pip install -e . - ``` - - ```bash - poetry install - ``` - - ...etc. - -- or add your package(s) parent directory in the `PYTHONPATH`. - -(*The following instructions assume your Python package is in the `src` directory.*) - -In Bash and other shells, you can run your command like this -(note the prepended `PYTHONPATH=...`): - -```bash -PYTHONPATH=src poetry run mkdocs serve -``` - -You could also export that variable, -but this is **not recommended** as it could affect other Python processes: - -```bash -export PYTHONPATH=src # Linux/Bash and similar -setx PYTHONPATH src # Windows, USE AT YOUR OWN RISKS -``` - -You can also use the Python handler `setup_commands`: - -```yaml -# mkdocs.yml -plugins: - - mkdocstrings: - handlers: - python: - setup_commands: - - import sys - - sys.path.append("src") - # or sys.path.insert(0, "src") -``` - -## Mocking libraries - -You may want to to generate documentation for a package while its dependencies are not available. -The Python handler provides itself no builtin way to mock libraries, -but you can use the `setup_commands` to mock them manually: - -!!! example "mkdocs.yml" - ```yaml - plugins: - - mkdocstrings: - handlers: - python: - setup_commands: - - import sys - - from unittest.mock import MagicMock as mock - - sys.modules["lib1"] = mock() - - sys.modules["lib2"] = mock() - - sys.modules["lib2.module1"] = mock() - - sys.modules["lib2.module1.moduleB"] = mock() - # etc - ``` - -## Recommended style (Material) - -Here are some CSS rules for the -[*Material for MkDocs*](https://squidfunk.github.io/mkdocs-material/) theme: - -```css -/* Indentation. */ -div.doc-contents:not(.first) { - padding-left: 25px; - border-left: 4px solid rgba(230, 230, 230); - margin-bottom: 80px; -} -``` - -## Recommended style (ReadTheDocs) - -Here are some CSS rules for the built-in *ReadTheDocs* theme: - -```css -/* Indentation. */ -div.doc-contents:not(.first) { - padding-left: 25px; - border-left: 4px solid rgba(230, 230, 230); - margin-bottom: 60px; -} -``` diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 7bfa2163..3cc3cfed 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -51,7 +51,7 @@ when it should be `[Section][pytkdocs.parsers.docstrings.Section]`. - Make sure the configuration options of the handler for both selection and rendering are correct. Check the documentation for [Handlers](handlers/overview.md) to see the available options for each handler. - Also make sure your documentation in your source code is formatted correctly. - For Python code, check the [supported docstring styles](handlers/python.md#supported-docstrings-styles) page. +For Python code, check the [supported docstring styles](https://mkdocstrings.github.io/python/usage/#supported-docstrings-styles) page. - Re-run the Mkdocs command with `-v`, and carefully read any traceback. ## Tabs in docstrings (from `pymdownx.tabbed`) are not working properly @@ -143,7 +143,7 @@ For false-positives, you can wrap the text in backticks (\`) to prevent `mkdocst Is your package available in the Python path? -See [Python handler: Finding modules](handlers/python.md#finding-modules). +See [Python handler: Finding modules](https://mkdocstrings.github.io/python/usage/#finding-modules). ### LaTeX in docstrings is not rendered correctly diff --git a/docs/usage.md b/docs/usage.md index 05ad99bc..c965c984 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -363,4 +363,4 @@ MkDocs will rebuild the site and reload the current page. 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](handlers/python.md#finding-modules). + see [Python Handler: Finding modules](https://mkdocstrings.github.io/python/usage/#finding-modules). diff --git a/duties.py b/duties.py index 71aa2f00..04511dd3 100644 --- a/duties.py +++ b/duties.py @@ -122,7 +122,6 @@ def check_quality(ctx, files=PY_SRC): files: The files to check. """ ctx.run(f"flake8 --config=config/flake8.ini {files}", title="Checking code quality", pty=PTY) - ctx.run("curlylint src/mkdocstrings/templates", title="Checking templates quality", pty=PTY) @duty diff --git a/mkdocs.yml b/mkdocs.yml index 96c2e575..df8fefd7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -17,7 +17,7 @@ nav: - Theming: theming.md - Handlers: - handlers/overview.md - - Python: handlers/python.md + - Python: https://mkdocstrings.github.io/python/ - Crystal: https://mkdocstrings.github.io/crystal/ - Troubleshooting: troubleshooting.md # defer to gen-files + literate-nav diff --git a/pyproject.toml b/pyproject.toml index ee408af9..3c49e303 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,10 +35,14 @@ dependencies = [ "MarkupSafe>=1.1", "mkdocs>=1.2", "mkdocs-autorefs>=0.3.1", + "mkdocstrings-python-legacy>=0.2", "pymdown-extensions>=6.3", - "pytkdocs>=0.14.0", ] +[project.optional-dependencies] +python-legacy = ["mkdocstrings-python-legacy>=0.2"] +python = ["mkdocstrings-python>=0.5.1"] + [project.urls] Homepage = "https://mkdocstrings.github.io" Documentation = "https://mkdocstrings.github.io" @@ -65,6 +69,7 @@ docs = [ "mkdocs-literate-nav>=0.4", "mkdocs-material>=7.3", "mkdocs-section-index>=0.3", + "mkdocstrings-python>=0.5.1", "toml>=0.10", ] format = [ @@ -90,10 +95,11 @@ quality = [ "flake8-variables-names>=0.0", "pep8-naming>=0.12", "wps-light>=0.15", - "curlylint>=0.13", ] tests = [ "docutils", + # TODO: uncomment once the legacy handler is optional + # "mkdocstrings-python-legacy>=0.1", "pygments>=2.10", # python 3.6 "pytest>=6.2", "pytest-cov>=3.0", diff --git a/src/mkdocstrings/handlers/python.py b/src/mkdocstrings/handlers/python.py deleted file mode 100644 index 0f85a6de..00000000 --- a/src/mkdocstrings/handlers/python.py +++ /dev/null @@ -1,392 +0,0 @@ -"""This module implements a handler for the Python language. - -The handler collects data with [`pytkdocs`](https://github.com/pawamoy/pytkdocs). -""" - -import json -import os -import posixpath -import sys -import traceback -from collections import ChainMap -from subprocess import PIPE, Popen # noqa: S404 (what other option, more secure that PIPE do we have? sockets?) -from typing import Any, BinaryIO, Callable, Iterator, List, Optional, Sequence, Tuple - -from markdown import Markdown -from markupsafe import Markup - -from mkdocstrings.extension import PluginError -from mkdocstrings.handlers.base import BaseCollector, BaseHandler, BaseRenderer, CollectionError, CollectorItem -from mkdocstrings.inventory import Inventory -from mkdocstrings.loggers import get_logger - -log = get_logger(__name__) - - -class PythonRenderer(BaseRenderer): - """The class responsible for loading Jinja templates and rendering them. - - It defines some configuration options, implements the `render` method, - and overrides the `update_env` method of the [`BaseRenderer` class][mkdocstrings.handlers.base.BaseRenderer]. - - Attributes: - fallback_theme: The theme to fallback to. - default_config: The default rendering options, - see [`default_config`][mkdocstrings.handlers.python.PythonRenderer.default_config]. - """ - - fallback_theme = "material" - - default_config: dict = { - "show_root_heading": False, - "show_root_toc_entry": True, - "show_root_full_path": True, - "show_root_members_full_path": False, - "show_object_full_path": False, - "show_category_heading": False, - "show_if_no_docstring": False, - "show_signature": True, - "show_signature_annotations": False, - "show_source": True, - "show_bases": True, - "group_by_category": True, - "heading_level": 2, - "members_order": "alphabetical", - } - """The default rendering options. - - Option | Type | Description | Default - ------ | ---- | ----------- | ------- - **`show_root_heading`** | `bool` | Show the heading of the object at the root of the documentation tree. | `False` - **`show_root_toc_entry`** | `bool` | If the root heading is not shown, at least add a ToC entry for it. | `True` - **`show_root_full_path`** | `bool` | Show the full Python path for the root object heading. | `True` - **`show_object_full_path`** | `bool` | Show the full Python path of every object. | `False` - **`show_root_members_full_path`** | `bool` | Show the full Python path of objects that are children of the root object (for example, classes in a module). When False, `show_object_full_path` overrides. | `False` - **`show_category_heading`** | `bool` | When grouped by categories, show a heading for each category. | `False` - **`show_if_no_docstring`** | `bool` | Show the object heading even if it has no docstring or children with docstrings. | `False` - **`show_signature`** | `bool` | Show method and function signatures. | `True` - **`show_signature_annotations`** | `bool` | Show the type annotations in method and function signatures. | `False` - **`show_source`** | `bool` | Show the source code of this object. | `True` - **`show_bases`** | `bool` | Show the base classes of a class. | `True` - **`group_by_category`** | `bool` | Group the object's children by categories: attributes, classes, functions, methods, and modules. | `True` - **`heading_level`** | `int` | The initial heading level to use. | `2` - **`members_order`** | `str` | The members ordering to use. Options: `alphabetical` - order by the members names, `source` - order members as they appear in the source file. | `alphabetical` - """ # noqa: E501 - - def render(self, data: CollectorItem, config: dict) -> str: # noqa: D102 (ignore missing docstring) - final_config = ChainMap(config, self.default_config) - - template = self.env.get_template(f"{data['category']}.html") - - # Heading level is a "state" variable, that will change at each step - # of the rendering recursion. Therefore, it's easier to use it as a plain value - # than as an item in a dictionary. - heading_level = final_config["heading_level"] - members_order = final_config["members_order"] - - if members_order == "alphabetical": - sort_function = _sort_key_alphabetical - elif members_order == "source": - sort_function = _sort_key_source - else: - raise PluginError(f"Unknown members_order '{members_order}', choose between 'alphabetical' and 'source'.") - - sort_object(data, sort_function=sort_function) - - return template.render( - **{"config": final_config, data["category"]: data, "heading_level": heading_level, "root": True}, - ) - - def get_anchors(self, data: CollectorItem) -> Sequence[str]: # noqa: D102 (ignore missing docstring) - try: - return (data["path"],) - except KeyError: - return () - - def update_env(self, md: Markdown, config: dict) -> None: # noqa: D102 (ignore missing docstring) - super().update_env(md, config) - self.env.trim_blocks = True - self.env.lstrip_blocks = True - self.env.keep_trailing_newline = False - self.env.filters["brief_xref"] = self.do_brief_xref - - def do_brief_xref(self, path: str) -> Markup: - """Filter to create cross-reference with brief text and full identifier as hover text.""" - brief = path.split(".")[-1] - return Markup("{brief}").format(path=path, brief=brief) - - -class PythonCollector(BaseCollector): - """The class responsible for loading Jinja templates and rendering them. - - It defines some configuration options, implements the `render` method, - and overrides the `update_env` method of the [`BaseRenderer` class][mkdocstrings.handlers.base.BaseRenderer]. - """ - - default_config: dict = {"filters": ["!^_[^_]"]} - """The default selection options. - - Option | Type | Description | Default - ------ | ---- | ----------- | ------- - **`filters`** | `List[str]` | Filter members with regular expressions. | `[ "!^_[^_]" ]` - **`members`** | `Union[bool, List[str]]` | Explicitly select the object members. | *`pytkdocs` default: `True`* - - If `members` is a list of names, filters are applied only on the members children (not the members themselves). - If `members` is `False`, none are selected. - If `members` is `True` or an empty list, filters are applied on all members and their children. - - Members affect only the first layer of objects, while filters affect the whole object-tree recursively. - - Every filters is run against every object name. An object can be un-selected by a filter and re-selected by the - next one: - - - `"!^_"`: exclude all objects starting with an underscore - - `"^__"`: but select all objects starting with **two** underscores - - Obviously one could use a single filter instead: `"!^_[^_]"`, which is the default. - """ - - fallback_config = {"docstring_style": "markdown", "filters": ["!.*"]} - """The configuration used when falling back to re-collecting an object to get its anchor. - - This configuration is used in [`Handlers.get_anchors`][mkdocstrings.handlers.base.Handlers.get_anchors]. - - When trying to fix (optional) cross-references, the autorefs plugin will try to collect - an object with every configured handler until one succeeds. It will then try to get - an anchor for it. It's because objects can have multiple identifiers (aliases), - for example their definition path and multiple import paths in Python. - - When re-collecting the object, we have no use for its members, or for its docstring being parsed. - This is why the fallback configuration filters every member out, and uses the Markdown style, - which we know will not generate any warnings. - """ - - def __init__(self, setup_commands: Optional[List[str]] = None) -> None: - """Initialize the object. - - When instantiating a Python collector, we open a subprocess in the background with `subprocess.Popen`. - It will allow us to feed input to and read output from this subprocess, keeping it alive during - the whole documentation generation. Spawning a new Python subprocess for each "autodoc" instruction would be - too resource intensive, and would slow down `mkdocstrings` a lot. - - Arguments: - setup_commands: A list of python commands as strings to be executed in the subprocess before `pytkdocs`. - """ - log.debug("Opening 'pytkdocs' subprocess") - env = os.environ.copy() - env["PYTHONUNBUFFERED"] = "1" - - if setup_commands: - # prevent the Python interpreter or the setup commands - # from writing to stdout as it would break pytkdocs output - commands = [ - "import sys", - "from io import StringIO", - "from pytkdocs.cli import main as pytkdocs", - "sys.stdout = StringIO()", # redirect stdout to memory buffer - *setup_commands, - "sys.stdout.flush()", - "sys.stdout = sys.__stdout__", # restore stdout - "pytkdocs(['--line-by-line'])", - ] - cmd = [sys.executable, "-c", "; ".join(commands)] - else: - cmd = [sys.executable, "-m", "pytkdocs", "--line-by-line"] - - self.process = Popen( # noqa: S603,S607 (we trust the input, and we don't want to use the absolute path) - cmd, - universal_newlines=True, - stdout=PIPE, - stdin=PIPE, - bufsize=-1, - env=env, - ) - - def collect(self, identifier: str, config: dict) -> CollectorItem: - """Collect the documentation tree given an identifier and selection options. - - In this method, we feed one line of JSON to the standard input of the subprocess that was opened - during instantiation of the collector. Then we read one line of JSON on its standard output. - - We load back the JSON text into a Python dictionary. - If there is a decoding error, we log it as error and raise a CollectionError. - - If the dictionary contains an `error` key, we log it as error (with the optional `traceback` value), - and raise a CollectionError. - - If the dictionary values for keys `loading_errors` and `parsing_errors` are not empty, - we log them as warnings. - - Then we pick up the only object within the `objects` list (there's always only one, because we collect - them one by one), rebuild it's categories lists - (see [`rebuild_category_lists()`][mkdocstrings.handlers.python.rebuild_category_lists]), - and return it. - - Arguments: - identifier: The dotted-path of a Python object available in the Python path. - config: Selection options, used to alter the data collection done by `pytkdocs`. - - Raises: - CollectionError: When there was a problem collecting the object documentation. - - Returns: - The collected object-tree. - """ - final_config = ChainMap(config, self.default_config) - - log.debug("Preparing input") - json_input = json.dumps({"objects": [{"path": identifier, **final_config}]}) - - log.debug("Writing to process' stdin") - self.process.stdin.write(json_input + "\n") # type: ignore - self.process.stdin.flush() # type: ignore - - log.debug("Reading process' stdout") - stdout = self.process.stdout.readline() # type: ignore - - log.debug("Loading JSON output as Python object") - try: - result = json.loads(stdout) - except json.decoder.JSONDecodeError as exception: - error = "\n".join(("Error while loading JSON:", stdout, traceback.format_exc())) - raise CollectionError(error) from exception - - error = result.get("error") - if error: - if "traceback" in result: - error += f"\n{result['traceback']}" - raise CollectionError(error) - - for loading_error in result["loading_errors"]: - log.warning(loading_error) - - for errors in result["parsing_errors"].values(): - for parsing_error in errors: - log.warning(parsing_error) - - # We always collect only one object at a time - result = result["objects"][0] - - log.debug("Rebuilding categories and children lists") - rebuild_category_lists(result) - - return result - - def teardown(self) -> None: - """Terminate the opened subprocess, set it to `None`.""" - log.debug("Tearing process down") - self.process.terminate() - - -class PythonHandler(BaseHandler): - """The Python handler class. - - 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. - """ - - domain: str = "py" # to match Sphinx's default domain - enable_inventory: bool = True - - @classmethod - def load_inventory( - cls, in_file: BinaryIO, url: str, base_url: Optional[str] = None, **kwargs - ) -> Iterator[Tuple[str, str]]: - """Yield items and their URLs from an inventory file streamed from `in_file`. - - This implements mkdocstrings' `load_inventory` "protocol" (see plugin.py). - - Arguments: - in_file: The binary file-like object to read the inventory from. - url: The URL that this file is being streamed from (used to guess `base_url`). - base_url: The URL that this inventory's sub-paths are relative to. - **kwargs: Ignore additional arguments passed from the config. - - Yields: - Tuples of (item identifier, item URL). - """ - if base_url is None: - base_url = posixpath.dirname(url) - - for item in Inventory.parse_sphinx(in_file, domain_filter=("py",)).values(): # noqa: WPS526 - yield item.name, posixpath.join(base_url, item.uri) - - -def get_handler( - theme: str, # noqa: W0613 (unused argument config) - custom_templates: Optional[str] = None, - setup_commands: Optional[List[str]] = None, - **config: Any, -) -> PythonHandler: - """Simply return an instance of `PythonHandler`. - - Arguments: - theme: The theme to use when rendering contents. - custom_templates: Directory containing custom templates. - setup_commands: A list of commands as strings to be executed in the subprocess before `pytkdocs`. - config: Configuration passed to the handler. - - Returns: - An instance of `PythonHandler`. - """ - return PythonHandler( - collector=PythonCollector(setup_commands=setup_commands), - renderer=PythonRenderer("python", theme, custom_templates), - ) - - -def rebuild_category_lists(obj: dict) -> None: - """Recursively rebuild the category lists of a collected object. - - Since `pytkdocs` dumps JSON on standard output, it must serialize the object-tree and flatten it to reduce data - duplication and avoid cycle-references. Indeed, each node of the object-tree has a `children` list, containing - all children, and another list for each category of children: `attributes`, `classes`, `functions`, `methods` - and `modules`. It replaces the values in category lists with only the paths of the objects. - - Here, we reconstruct these category lists by picking objects in the `children` list using their path. - - For each object, we recurse on every one of its children. - - Arguments: - obj: The collected object, loaded back from JSON into a Python dictionary. - """ - for category in ("attributes", "classes", "functions", "methods", "modules"): - obj[category] = [obj["children"][path] for path in obj[category]] - obj["children"] = [child for _, child in obj["children"].items()] - for child in obj["children"]: - rebuild_category_lists(child) - - -def sort_object(obj: CollectorItem, sort_function: Callable[[CollectorItem], Any]) -> None: - """Sort the collected object's children. - - Sorts the object's children list, then each category separately, and then recurses into each. - - Arguments: - obj: The collected object, as a dict. Note that this argument is mutated. - sort_function: The sort key function used to determine the order of elements. - """ - obj["children"].sort(key=sort_function) - - for category in ("attributes", "classes", "functions", "methods", "modules"): - obj[category].sort(key=sort_function) - - for child in obj["children"]: - sort_object(child, sort_function=sort_function) - - -def _sort_key_alphabetical(item: CollectorItem) -> Any: - """Return a sort key for 'alphabetical' sorting of CollectorItems.""" - # chr(sys.maxunicode) is a string that contains the final unicode - # character, so if 'name' isn't found on the object, the item will go to - # the end of the list. - return item.get("name", chr(sys.maxunicode)) - - -def _sort_key_source(item: CollectorItem) -> Any: - """Return a sort key for 'source' sorting of CollectorItems.""" - # if 'line_start' isn't found on the object, the item will go to - # the start of the list. - return item.get("source", {}).get("line_start", -1) diff --git a/src/mkdocstrings/plugin.py b/src/mkdocstrings/plugin.py index 242d5931..33e79b00 100644 --- a/src/mkdocstrings/plugin.py +++ b/src/mkdocstrings/plugin.py @@ -244,11 +244,9 @@ def on_post_build(self, config: Config, **kwargs) -> None: # noqa: W0613,R0201 Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build). This hook is used to teardown all the handlers that were instantiated and cached during documentation buildup. - For example, the [Python handler's collector][mkdocstrings.handlers.python.PythonCollector] opens a subprocess - in the background and keeps it open to feed it the "autodoc" instructions and get back JSON data. Therefore, - it must close it at some point, and it does it in its - [`teardown()` method][mkdocstrings.handlers.python.PythonCollector.teardown] which is indirectly called by - this hook. + 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. Arguments: config: The MkDocs config object. diff --git a/src/mkdocstrings/templates/python/material/attribute.html b/src/mkdocstrings/templates/python/material/attribute.html deleted file mode 100644 index b40518a9..00000000 --- a/src/mkdocstrings/templates/python/material/attribute.html +++ /dev/null @@ -1,57 +0,0 @@ -{{ log.debug() }} -{% if config.show_if_no_docstring or attribute.has_contents %} - -

- {% with html_id = attribute.path %} - - {% if not root or config.show_root_heading %} - - {% if root %} - {% set show_full_path = config.show_root_full_path %} - {% set root_members = True %} - {% elif root_members %} - {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %} - {% set root_members = False %} - {% else %} - {% set show_full_path = config.show_object_full_path %} - {% endif %} - - {% filter heading(heading_level, - role="data" if obj == module else "attr", - id=html_id, - class="doc doc-heading", - toc_label=attribute.name) %} - - {% filter highlight(language="python", inline=True) %} - {% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %} - {% if attribute.type %}: {{ attribute.type }}{% endif %} - {% endfilter %} - - {% with properties = attribute.properties %} - {% include "properties.html" with context %} - {% endwith %} - - {% endfilter %} - - {% else %} - {% if config.show_root_toc_entry %} - {% filter heading(heading_level, - role="data" if obj == module else "attr", - id=html_id, - toc_label=attribute.path, - hidden=True) %} - {% endfilter %} - {% endif %} - {% set heading_level = heading_level - 1 %} - {% endif %} - -
- {% with docstring_sections = attribute.docstring_sections %} - {% include "docstring.html" with context %} - {% endwith %} -
- - {% endwith %} -
- -{% endif %} diff --git a/src/mkdocstrings/templates/python/material/attributes.html b/src/mkdocstrings/templates/python/material/attributes.html deleted file mode 100644 index 02a2935b..00000000 --- a/src/mkdocstrings/templates/python/material/attributes.html +++ /dev/null @@ -1,20 +0,0 @@ -{{ log.debug() }} -

Attributes:

- - - - - - - - - - {% for attribute in attributes %} - - - - - - {% endfor %} - -
NameTypeDescription
{{ attribute.name }}{% if attribute.annotation %}{{ attribute.annotation }}{% endif %}{{ attribute.description|convert_markdown(heading_level, html_id) }}
diff --git a/src/mkdocstrings/templates/python/material/children.html b/src/mkdocstrings/templates/python/material/children.html deleted file mode 100644 index 7bc56c2d..00000000 --- a/src/mkdocstrings/templates/python/material/children.html +++ /dev/null @@ -1,99 +0,0 @@ -{{ log.debug() }} -{% if obj.children %} - -
- - {% if config.group_by_category %} - - {% with %} - - {% if config.show_category_heading %} - {% set extra_level = 1 %} - {% else %} - {% set extra_level = 0 %} - {% endif %} - - {% if config.show_category_heading and obj.attributes|any("has_contents") %} - {% filter heading(heading_level, id=html_id ~ "-attributes") %}Attributes{% endfilter %} - {% endif %} - {% with heading_level = heading_level + extra_level %} - {% for attribute in obj.attributes %} - {% include "attribute.html" with context %} - {% endfor %} - {% endwith %} - - {% if config.show_category_heading and obj.classes|any("has_contents") %} - {% filter heading(heading_level, id=html_id ~ "-classes") %}Classes{% endfilter %} - {% endif %} - {% with heading_level = heading_level + extra_level %} - {% for class in obj.classes %} - {% include "class.html" with context %} - {% endfor %} - {% endwith %} - - {% if config.show_category_heading and obj.functions|any("has_contents") %} - {% filter heading(heading_level, id=html_id ~ "-functions") %}Functions{% endfilter %} - {% endif %} - {% with heading_level = heading_level + extra_level %} - {% for function in obj.functions %} - {% include "function.html" with context %} - {% endfor %} - {% endwith %} - - {% if config.show_category_heading and obj.methods|any("has_contents") %} - {% filter heading(heading_level, id=html_id ~ "-methods") %}Methods{% endfilter %} - {% endif %} - {% with heading_level = heading_level + extra_level %} - {% for method in obj.methods %} - {% include "method.html" with context %} - {% endfor %} - {% endwith %} - - {% if config.show_category_heading and obj.modules|any("has_contents") %} - {% filter heading(heading_level, id=html_id ~ "-modules") %}Modules{% endfilter %} - {% endif %} - {% with heading_level = heading_level + extra_level %} - {% for module in obj.modules %} - {% include "module.html" with context %} - {% endfor %} - {% endwith %} - - {% endwith %} - - {% else %} - - {% for child in obj.children %} - {% if child.category == "attribute" %} - {% with attribute = child %} - {% include "attribute.html" with context %} - {% endwith %} - - {% elif child.category == "class" %} - {% with class = child %} - {% include "class.html" with context %} - {% endwith %} - - {% elif child.category == "function" %} - {% with function = child %} - {% include "function.html" with context %} - {% endwith %} - - {% elif child.category == "method" %} - {% with method = child %} - {% include "method.html" with context %} - {% endwith %} - - {% elif child.category == "module" %} - {% with module = child %} - {% include "module.html" with context %} - {% endwith %} - - {% endif %} - - {% endfor %} - - {% endif %} - -
- -{% endif %} diff --git a/src/mkdocstrings/templates/python/material/class.html b/src/mkdocstrings/templates/python/material/class.html deleted file mode 100644 index 62a70f57..00000000 --- a/src/mkdocstrings/templates/python/material/class.html +++ /dev/null @@ -1,74 +0,0 @@ -{{ log.debug() }} -{% if config.show_if_no_docstring or class.has_contents %} - -
- {% with html_id = class.path %} - - {% if not root or config.show_root_heading %} - - {% if root %} - {% set show_full_path = config.show_root_full_path %} - {% set root_members = True %} - {% elif root_members %} - {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %} - {% set root_members = False %} - {% else %} - {% set show_full_path = config.show_object_full_path %} - {% endif %} - - {% filter heading(heading_level, - role="class", - id=html_id, - class="doc doc-heading", - toc_label=class.name) %} - - - {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} - {% if config.show_bases and class.bases and class.bases != ['object'] %} - ({% for base in class.bases -%} - {{ base|brief_xref() }}{% if not loop.last %}, {% endif %} - {% endfor %}) - {% endif %} - - - {% with properties = class.properties %} - {% include "properties.html" with context %} - {% endwith %} - - {% endfilter %} - - {% else %} - {% if config.show_root_toc_entry %} - {% filter heading(heading_level, - role="class", - id=html_id, - toc_label=class.path, - hidden=True) %} - {% endfilter %} - {% endif %} - {% set heading_level = heading_level - 1 %} - {% endif %} - -
- {% with docstring_sections = class.docstring_sections %} - {% include "docstring.html" with context %} - {% endwith %} - - {% if config.show_source and class.source %} -
- Source code in {{ class.relative_file_path }} - {{ class.source.code|highlight(language="python", linestart=class.source.line_start, linenums=False) }} -
- {% endif %} - - {% with obj = class %} - {% set root = False %} - {% set heading_level = heading_level + 1 %} - {% include "children.html" with context %} - {% endwith %} -
- - {% endwith %} -
- -{% endif %} diff --git a/src/mkdocstrings/templates/python/material/docstring.html b/src/mkdocstrings/templates/python/material/docstring.html deleted file mode 100644 index f1f179b3..00000000 --- a/src/mkdocstrings/templates/python/material/docstring.html +++ /dev/null @@ -1,36 +0,0 @@ -{{ log.debug() }} -{% if docstring_sections %} - {% for section in docstring_sections %} - {% if section.type == "markdown" %} - {{ section.value|convert_markdown(heading_level, html_id) }} - {% elif section.type == "attributes" %} - {% with attributes = section.value %} - {% include "attributes.html" with context %} - {% endwith %} - {% elif section.type == "parameters" %} - {% with parameters = section.value %} - {% include "parameters.html" with context %} - {% endwith %} - {% elif section.type == "keyword_args" %} - {% with kwargs = section.value %} - {% include "keyword_args.html" with context %} - {% endwith %} - {% elif section.type == "exceptions" %} - {% with exceptions = section.value %} - {% include "exceptions.html" with context %} - {% endwith %} - {% elif section.type == "yield" %} - {% with yield = section.value %} - {% include "yield.html" with context %} - {% endwith %} - {% elif section.type == "return" %} - {% with return = section.value %} - {% include "return.html" with context %} - {% endwith %} - {% elif section.type == "examples" %} - {% with examples = section.value %} - {% include "examples.html" with context %} - {% endwith %} - {% endif %} - {% endfor %} -{% endif %} diff --git a/src/mkdocstrings/templates/python/material/examples.html b/src/mkdocstrings/templates/python/material/examples.html deleted file mode 100644 index 63b6f430..00000000 --- a/src/mkdocstrings/templates/python/material/examples.html +++ /dev/null @@ -1,9 +0,0 @@ -{{ log.debug() }} -

Examples:

-{% for section_type, sub_section in examples %} - {% if section_type == "markdown" %} - {{ sub_section|convert_markdown(heading_level, html_id) }} - {% elif section_type == "examples" %} - {{ sub_section|highlight(language="python", linenums=False) }} - {% endif %} -{% endfor %} diff --git a/src/mkdocstrings/templates/python/material/exceptions.html b/src/mkdocstrings/templates/python/material/exceptions.html deleted file mode 100644 index 8d1320b0..00000000 --- a/src/mkdocstrings/templates/python/material/exceptions.html +++ /dev/null @@ -1,18 +0,0 @@ -{{ log.debug() }} -

Exceptions:

- - - - - - - - - {% for exception in exceptions %} - - - - - {% endfor %} - -
TypeDescription
{{ exception.annotation }}{{ exception.description|convert_markdown(heading_level, html_id) }}
diff --git a/src/mkdocstrings/templates/python/material/function.html b/src/mkdocstrings/templates/python/material/function.html deleted file mode 100644 index 6f8e6c77..00000000 --- a/src/mkdocstrings/templates/python/material/function.html +++ /dev/null @@ -1,64 +0,0 @@ -{{ log.debug() }} -{% if config.show_if_no_docstring or function.has_contents %} - -
- {% with html_id = function.path %} - - {% if not root or config.show_root_heading %} - - {% if root %} - {% set show_full_path = config.show_root_full_path %} - {% set root_members = True %} - {% elif root_members %} - {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %} - {% set root_members = False %} - {% else %} - {% set show_full_path = config.show_object_full_path %} - {% endif %} - - {% filter heading(heading_level, - role="function", - id=html_id, - class="doc doc-heading", - toc_label=function.name ~ "()") %} - - {% filter highlight(language="python", inline=True) %} - {% if show_full_path %}{{ function.path }}{% else %}{{ function.name }}{% endif %} - {% with signature = function.signature %}{% include "signature.html" with context %}{% endwith %} - {% endfilter %} - - {% with properties = function.properties %} - {% include "properties.html" with context %} - {% endwith %} - - {% endfilter %} - - {% else %} - {% if config.show_root_toc_entry %} - {% filter heading(heading_level, - role="function", - id=html_id, - toc_label=function.path, - hidden=True) %} - {% endfilter %} - {% endif %} - {% set heading_level = heading_level - 1 %} - {% endif %} - -
- {% with docstring_sections = function.docstring_sections %} - {% include "docstring.html" with context %} - {% endwith %} - - {% if config.show_source and function.source %} -
- Source code in {{ function.relative_file_path }} - {{ function.source.code|highlight(language="python", linestart=function.source.line_start, linenums=False) }} -
- {% endif %} -
- - {% endwith %} -
- -{% endif %} diff --git a/src/mkdocstrings/templates/python/material/keyword_args.html b/src/mkdocstrings/templates/python/material/keyword_args.html deleted file mode 100644 index 3396ffa5..00000000 --- a/src/mkdocstrings/templates/python/material/keyword_args.html +++ /dev/null @@ -1,20 +0,0 @@ -{{ log.debug() }} -

Keyword arguments:

- - - - - - - - - - {% for kwarg in kwargs %} - - - - - - {% endfor %} - -
NameTypeDescription
{{ kwarg.name }}{% if kwarg.annotation %}{{ kwarg.annotation }}{% endif %}{{ kwarg.description|convert_markdown(heading_level, html_id) }}
diff --git a/src/mkdocstrings/templates/python/material/method.html b/src/mkdocstrings/templates/python/material/method.html deleted file mode 100644 index 807009e5..00000000 --- a/src/mkdocstrings/templates/python/material/method.html +++ /dev/null @@ -1,64 +0,0 @@ -{{ log.debug() }} -{% if config.show_if_no_docstring or method.has_contents %} - -
- {% with html_id = method.path %} - - {% if not root or config.show_root_heading %} - - {% if root %} - {% set show_full_path = config.show_root_full_path %} - {% set root_members = True %} - {% elif root_members %} - {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %} - {% set root_members = False %} - {% else %} - {% set show_full_path = config.show_object_full_path %} - {% endif %} - - {% filter heading(heading_level, - role="method", - id=html_id, - class="doc doc-heading", - toc_label=method.name ~ "()") %} - - {% filter highlight(language="python", inline=True) %} - {% if show_full_path %}{{ method.path }}{% else %}{{ method.name }}{% endif %} - {% with signature = method.signature %}{% include "signature.html" with context %}{% endwith %} - {% endfilter %} - - {% with properties = method.properties %} - {% include "properties.html" with context %} - {% endwith %} - - {% endfilter %} - - {% else %} - {% if config.show_root_toc_entry %} - {% filter heading(heading_level, - role="method", - id=html_id, - toc_label=method.path, - hidden=True) %} - {% endfilter %} - {% endif %} - {% set heading_level = heading_level - 1 %} - {% endif %} - -
- {% with docstring_sections = method.docstring_sections %} - {% include "docstring.html" with context %} - {% endwith %} - - {% if config.show_source and method.source %} -
- Source code in {{ method.relative_file_path }} - {{ method.source.code|highlight(language="python", linestart=method.source.line_start, linenums=False) }} -
- {% endif %} -
- - {% endwith %} -
- -{% endif %} diff --git a/src/mkdocstrings/templates/python/material/module.html b/src/mkdocstrings/templates/python/material/module.html deleted file mode 100644 index ba8f4eac..00000000 --- a/src/mkdocstrings/templates/python/material/module.html +++ /dev/null @@ -1,60 +0,0 @@ -{{ log.debug() }} -{% if config.show_if_no_docstring or module.has_contents %} - -
- {% with html_id = module.path %} - - {% if not root or config.show_root_heading %} - - {% if root %} - {% set show_full_path = config.show_root_full_path %} - {% set root_members = True %} - {% elif root_members %} - {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %} - {% set root_members = False %} - {% else %} - {% set show_full_path = config.show_object_full_path %} - {% endif %} - - {% filter heading(heading_level, - role="module", - id=html_id, - class="doc doc-heading", - toc_label=module.name) %} - - {% if show_full_path %}{{ module.path }}{% else %}{{ module.name }}{% endif %} - - {% with properties = module.properties %} - {% include "properties.html" with context %} - {% endwith %} - - {% endfilter %} - - {% else %} - {% if config.show_root_toc_entry %} - {% filter heading(heading_level, - role="module", - id=html_id, - toc_label=module.path, - hidden=True) %} - {% endfilter %} - {% endif %} - {% set heading_level = heading_level - 1 %} - {% endif %} - -
- {% with docstring_sections = module.docstring_sections %} - {% include "docstring.html" with context %} - {% endwith %} - - {% with obj = module %} - {% set root = False %} - {% set heading_level = heading_level + 1 %} - {% include "children.html" with context %} - {% endwith %} -
- - {% endwith %} -
- -{% endif %} diff --git a/src/mkdocstrings/templates/python/material/parameters.html b/src/mkdocstrings/templates/python/material/parameters.html deleted file mode 100644 index 321318e0..00000000 --- a/src/mkdocstrings/templates/python/material/parameters.html +++ /dev/null @@ -1,22 +0,0 @@ -{{ log.debug() }} -

Parameters:

- - - - - - - - - - - {% for parameter in parameters %} - - - - - - - {% endfor %} - -
NameTypeDescriptionDefault
{{ parameter.name }}{% if parameter.annotation %}{{ parameter.annotation }}{% endif %}{{ parameter.description|convert_markdown(heading_level, html_id) }}{% if parameter.default %}{{ parameter.default }}{% else %}required{% endif %}
diff --git a/src/mkdocstrings/templates/python/material/properties.html b/src/mkdocstrings/templates/python/material/properties.html deleted file mode 100644 index 69a2da4c..00000000 --- a/src/mkdocstrings/templates/python/material/properties.html +++ /dev/null @@ -1,8 +0,0 @@ -{{ log.debug() }} -{% if properties %} - - {% for property in properties %} - {{ property }} - {% endfor %} - -{% endif %} diff --git a/src/mkdocstrings/templates/python/material/return.html b/src/mkdocstrings/templates/python/material/return.html deleted file mode 100644 index f4282491..00000000 --- a/src/mkdocstrings/templates/python/material/return.html +++ /dev/null @@ -1,16 +0,0 @@ -{{ log.debug() }} -

Returns:

- - - - - - - - - - - - - -
TypeDescription
{% if return.annotation %}{{ return.annotation }}{% endif %}{{ return.description|convert_markdown(heading_level, html_id) }}
diff --git a/src/mkdocstrings/templates/python/material/signature.html b/src/mkdocstrings/templates/python/material/signature.html deleted file mode 100644 index 90cc4180..00000000 --- a/src/mkdocstrings/templates/python/material/signature.html +++ /dev/null @@ -1,31 +0,0 @@ -{{ log.debug() }} -{%- if signature and config.show_signature -%} - {%- with -%} - {%- set ns = namespace(render_pos_only_separator=True, render_kw_only_separator=True, equal="=") -%} - - {%- if config.show_signature_annotations -%} - {%- set ns.equal = " = " -%} - {%- endif -%} - - ({%- for parameter in signature.parameters %}{% if parameter.kind == "POSITIONAL_ONLY" -%} - {%- if ns.render_pos_only_separator -%} - {%- set ns.render_pos_only_separator = False %}/, {% endif -%} - {%- elif parameter.kind == "KEYWORD_ONLY" -%} - {%- if ns.render_kw_only_separator -%} - {%- set ns.render_kw_only_separator = False %}*, {% endif -%} - {%- endif -%} - {%- if config.show_signature_annotations and "annotation" in parameter -%} - {%- set annotation = ": " + parameter.annotation|safe -%} - {%- endif -%} - {%- if "default" in parameter -%} - {%- set default = ns.equal + parameter.default|safe -%} - {%- endif -%} - {%- if parameter.kind == "VAR_POSITIONAL" %}* - {%- set render_kw_only_separator = False -%} - {%- elif parameter.kind == "VAR_KEYWORD" %}** - {%- endif %}{{ parameter.name }}{{ annotation }}{{ default }}{% if not loop.last %}, {% endif -%} - {%- endfor %}){% if config.show_signature_annotations and "return_annotation" in signature %} -> {{ signature.return_annotation }} - {%- endif -%} - - {%- endwith -%} -{%- endif -%} diff --git a/src/mkdocstrings/templates/python/material/style.css b/src/mkdocstrings/templates/python/material/style.css deleted file mode 100644 index 7d6a9961..00000000 --- a/src/mkdocstrings/templates/python/material/style.css +++ /dev/null @@ -1,15 +0,0 @@ -/* Don't capitalize names. */ -h5.doc-heading { - text-transform: none !important; -} - -/* Avoid breaking parameters name, etc. in table cells. */ -.doc-contents td code { - word-break: normal !important; -} - -/* For pieces of Markdown rendered in table cells. */ -.doc-contents td p { - margin-top: 0 !important; - margin-bottom: 0 !important; -} diff --git a/src/mkdocstrings/templates/python/material/yield.html b/src/mkdocstrings/templates/python/material/yield.html deleted file mode 100644 index d8a450ca..00000000 --- a/src/mkdocstrings/templates/python/material/yield.html +++ /dev/null @@ -1,16 +0,0 @@ -{{ log.debug() }} -

Yields:

- - - - - - - - - - - - - -
TypeDescription
{% if yield.annotation %}{{ yield.annotation }}{% endif %}{{ yield.description|convert_markdown(heading_level, html_id) }}
diff --git a/src/mkdocstrings/templates/python/mkdocs/exceptions.html b/src/mkdocstrings/templates/python/mkdocs/exceptions.html deleted file mode 100644 index 677c867b..00000000 --- a/src/mkdocstrings/templates/python/mkdocs/exceptions.html +++ /dev/null @@ -1,7 +0,0 @@ -{{ log.debug() }} -
-
Exceptions:
- {% for exception in exceptions %} -
{{ ("`" + exception.annotation + "`: " + exception.description)|convert_markdown(heading_level, html_id) }}
- {% endfor %} -
diff --git a/src/mkdocstrings/templates/python/mkdocs/keyword_args.html b/src/mkdocstrings/templates/python/mkdocs/keyword_args.html deleted file mode 100644 index 16af0ea0..00000000 --- a/src/mkdocstrings/templates/python/mkdocs/keyword_args.html +++ /dev/null @@ -1,7 +0,0 @@ -{{ log.debug() }} -
-
Keyword arguments:
- {% for kwarg in kwargs %} -
{{ ("**" + kwarg.name + ":** " + ("`" + kwarg.annotation + "` – " if kwarg.annotation else "") + kwarg.description)|convert_markdown(heading_level, html_id) }}
- {% endfor %} -
diff --git a/src/mkdocstrings/templates/python/mkdocs/parameters.html b/src/mkdocstrings/templates/python/mkdocs/parameters.html deleted file mode 100644 index 3eca4c14..00000000 --- a/src/mkdocstrings/templates/python/mkdocs/parameters.html +++ /dev/null @@ -1,7 +0,0 @@ -{{ log.debug() }} -
-
Parameters:
- {% for parameter in parameters %} -
{{ ("**" + parameter.name + ":** " + ("`" + parameter.annotation + "` – " if parameter.annotation else "") + parameter.description)|convert_markdown(heading_level, html_id) }}
- {% endfor %} -
diff --git a/src/mkdocstrings/templates/python/mkdocs/return.html b/src/mkdocstrings/templates/python/mkdocs/return.html deleted file mode 100644 index b38d61d1..00000000 --- a/src/mkdocstrings/templates/python/mkdocs/return.html +++ /dev/null @@ -1,5 +0,0 @@ -{{ log.debug() }} -
-
Returns:
-
{{ (("`" + return.annotation + "` – " if return.annotation else "") + return.description)|convert_markdown(heading_level, html_id) }}
-
diff --git a/src/mkdocstrings/templates/python/mkdocs/style.css b/src/mkdocstrings/templates/python/mkdocs/style.css deleted file mode 100644 index 9db45032..00000000 --- a/src/mkdocstrings/templates/python/mkdocs/style.css +++ /dev/null @@ -1,11 +0,0 @@ -.doc-contents { - padding-left: 20px; -} - -.doc-contents dd>p { - margin-bottom: 0.5rem; -} - -.doc-contents dl+dl { - margin-top: -0.5rem; -} diff --git a/src/mkdocstrings/templates/python/mkdocs/yield.html b/src/mkdocstrings/templates/python/mkdocs/yield.html deleted file mode 100644 index dad0f0e9..00000000 --- a/src/mkdocstrings/templates/python/mkdocs/yield.html +++ /dev/null @@ -1,5 +0,0 @@ -{{ log.debug() }} -
-
Yields:
-
{{ (("`" + yield.annotation + "` – " if yield.annotation else "") + yield.description)|convert_markdown(heading_level, html_id) }}
-
diff --git a/src/mkdocstrings/templates/python/readthedocs/exceptions.html b/src/mkdocstrings/templates/python/readthedocs/exceptions.html deleted file mode 100644 index 715bdaf9..00000000 --- a/src/mkdocstrings/templates/python/readthedocs/exceptions.html +++ /dev/null @@ -1,19 +0,0 @@ -{{ log.debug() }} - - - - - - - - - - - -
Exceptions: -
    - {% for exception in exceptions %} -
  • {{ ("`" + exception.annotation + "` – " + exception.description)|convert_markdown(heading_level, html_id) }}
  • - {% endfor %} -
-
diff --git a/src/mkdocstrings/templates/python/readthedocs/keyword_args.html b/src/mkdocstrings/templates/python/readthedocs/keyword_args.html deleted file mode 100644 index 3c15308e..00000000 --- a/src/mkdocstrings/templates/python/readthedocs/keyword_args.html +++ /dev/null @@ -1,19 +0,0 @@ -{{ log.debug() }} - - - - - - - - - - - -
Keyword arguments: -
    - {% for kwarg in kwargs %} -
  • {{ ("**" + kwarg.name + "**" + (" (`" + kwarg.annotation + "`)" if kwarg.annotation else "") + " – " + kwarg.description)|convert_markdown(heading_level, html_id) }}
  • - {% endfor %} -
-
diff --git a/src/mkdocstrings/templates/python/readthedocs/parameters.html b/src/mkdocstrings/templates/python/readthedocs/parameters.html deleted file mode 100644 index 197a411e..00000000 --- a/src/mkdocstrings/templates/python/readthedocs/parameters.html +++ /dev/null @@ -1,19 +0,0 @@ -{{ log.debug() }} - - - - - - - - - - - -
Parameters: -
    - {% for parameter in parameters %} -
  • {{ ("**" + parameter.name + "**" + (" (`" + parameter.annotation + "`)" if parameter.annotation else "") + " – " + parameter.description)|convert_markdown(heading_level, html_id) }}
  • - {% endfor %} -
-
diff --git a/src/mkdocstrings/templates/python/readthedocs/return.html b/src/mkdocstrings/templates/python/readthedocs/return.html deleted file mode 100644 index 74bf65bb..00000000 --- a/src/mkdocstrings/templates/python/readthedocs/return.html +++ /dev/null @@ -1,17 +0,0 @@ -{{ log.debug() }} - - - - - - - - - - - -
Returns: -
    -
  • {{ ((("`" + return.annotation + "` – ") if return.annotation else "") + return.description)|convert_markdown(heading_level, html_id) }}
  • -
-
diff --git a/src/mkdocstrings/templates/python/readthedocs/style.css b/src/mkdocstrings/templates/python/readthedocs/style.css deleted file mode 100644 index 2cafca81..00000000 --- a/src/mkdocstrings/templates/python/readthedocs/style.css +++ /dev/null @@ -1,27 +0,0 @@ -/* Avoid breaking parameters name, etc. in table cells. */ -.doc-contents td code { - word-break: normal !important; -} - -/* For pieces of Markdown rendered in table cells. */ -.doc-contents td p { - margin-top: 0 !important; - margin-bottom: 0 !important; -} - -/* Avoid breaking code headings. */ -.doc-heading code { - white-space: normal; -} - -/* Improve rendering of parameters, returns and exceptions. */ -.doc-contents .field-name { - min-width: 100px; -} -.doc-contents .field-name, .field-body { - border: none !important; - padding: 0 !important; -} -.doc-contents .field-list { - margin: 0 !important; -} diff --git a/src/mkdocstrings/templates/python/readthedocs/yield.html b/src/mkdocstrings/templates/python/readthedocs/yield.html deleted file mode 100644 index 7591f62c..00000000 --- a/src/mkdocstrings/templates/python/readthedocs/yield.html +++ /dev/null @@ -1,17 +0,0 @@ -{{ log.debug() }} - - - - - - - - - - - -
Yields: -
    -
  • {{ ((("`" + yield.annotation + "` – ") if yield.annotation else "") + yield.description)|convert_markdown(heading_level, html_id) }}
  • -
-
diff --git a/tests/fixtures/html_escaped_sequences.py b/tests/fixtures/html_escaped_sequences.py deleted file mode 100644 index 3b3811bf..00000000 --- a/tests/fixtures/html_escaped_sequences.py +++ /dev/null @@ -1,12 +0,0 @@ -""" -  -‘ -’ -“ -” -« -» -… -– -— -""" diff --git a/tests/test_extension.py b/tests/test_extension.py index db407233..759cad94 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -6,11 +6,6 @@ import pytest -def test_render_html_escaped_sequences(ext_markdown): - """Assert HTML-escaped sequences are correctly parsed as XML.""" - ext_markdown.convert("::: tests.fixtures.html_escaped_sequences") - - @pytest.mark.parametrize("ext_markdown", [{"markdown_extensions": [{"footnotes": {}}]}], indirect=["ext_markdown"]) def test_multiple_footnotes(ext_markdown): """Assert footnotes don't get added to subsequent docstrings.""" diff --git a/tests/test_python_handler.py b/tests/test_python_handler.py deleted file mode 100644 index 315b5760..00000000 --- a/tests/test_python_handler.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Tests for the handlers.python module.""" - -from copy import deepcopy - -from mkdocstrings.handlers.python import ( # noqa: WPS450 - _sort_key_alphabetical, - _sort_key_source, - rebuild_category_lists, - sort_object, -) - - -def test_members_order(): - """Assert that members sorting functions work correctly.""" - subcategories = {key: [] for key in ("attributes", "classes", "functions", "methods", "modules")} - categories = {"children": {}, **subcategories} - collected = { - "name": "root", - "children": { - "b": {"name": "b", "source": {"line_start": 0}, **categories}, - "a": {"name": "a", **categories}, - "z": {"name": "z", "source": {"line_start": 100}, **categories}, - "no_name": {"source": {"line_start": 10}, **categories}, - "c": { - "name": "c", - "source": {"line_start": 30}, - "children": { - "z": {"name": "z", "source": {"line_start": 200}, **categories}, - "a": {"name": "a", "source": {"line_start": 20}, **categories}, - }, - **subcategories, - }, - }, - "attributes": ["b", "c", "no_name", "z", "a"], - "classes": [], - "functions": [], - "methods": [], - "modules": [], - } - rebuild_category_lists(collected) - alphebetical = deepcopy(collected) - sort_object(alphebetical, _sort_key_alphabetical) - - rebuilt_categories = {"children": [], **subcategories} - assert ( - alphebetical["children"] - == alphebetical["attributes"] - == [ - {"name": "a", **rebuilt_categories}, - {"name": "b", "source": {"line_start": 0}, **rebuilt_categories}, - { - "name": "c", - "source": {"line_start": 30}, - "children": [ - {"name": "a", "source": {"line_start": 20}, **rebuilt_categories}, - {"name": "z", "source": {"line_start": 200}, **rebuilt_categories}, - ], - **subcategories, - }, - {"name": "z", "source": {"line_start": 100}, **rebuilt_categories}, - {"source": {"line_start": 10}, **rebuilt_categories}, - ] - ) - - source = deepcopy(collected) - sort_object(source, _sort_key_source) - - assert ( - source["children"] - == source["attributes"] - == [ - {"name": "a", **rebuilt_categories}, - {"name": "b", "source": {"line_start": 0}, **rebuilt_categories}, - {"source": {"line_start": 10}, **rebuilt_categories}, - { - "name": "c", - "source": {"line_start": 30}, - "children": [ - {"name": "a", "source": {"line_start": 20}, **rebuilt_categories}, - {"name": "z", "source": {"line_start": 200}, **rebuilt_categories}, - ], - **subcategories, - }, - {"name": "z", "source": {"line_start": 100}, **rebuilt_categories}, - ] - ) diff --git a/tests/test_themes.py b/tests/test_themes.py deleted file mode 100644 index 3d6066d1..00000000 --- a/tests/test_themes.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Tests for the different themes we claim to support.""" - -import sys - -import pytest - - -@pytest.mark.parametrize( - "plugin", - [ - {"theme": "mkdocs"}, - {"theme": "readthedocs"}, - {"theme": {"name": "material"}}, - ], - indirect=["plugin"], -) -@pytest.mark.parametrize( - "module", - [ - "mkdocstrings.extension", - "mkdocstrings.inventory", - "mkdocstrings.loggers", - "mkdocstrings.plugin", - "mkdocstrings.handlers.base", - "mkdocstrings.handlers.python", - "mkdocstrings.handlers.rendering", - ], -) -@pytest.mark.skipif(sys.version_info < (3, 7), reason="material is not installed on Python 3.6") -def test_render_themes_templates_python(module, plugin): - """Test rendering of a given theme's templates.""" - handler = plugin.handlers.get_handler("python") - handler.renderer._update_env(plugin.md, plugin.handlers._config) # noqa: WPS437 - data = handler.collector.collect(module, {}) - handler.renderer.render(data, {}) From 5c22c6ce4e056ac2334e2dfcd47c1f1a7884d352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Fri, 14 Jan 2022 21:47:30 +0100 Subject: [PATCH 10/21] refactor: Support loading handlers from the mkdocstrings_handlers namespace --- src/mkdocstrings/handlers/base.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/mkdocstrings/handlers/base.py b/src/mkdocstrings/handlers/base.py index 717e2e61..7c5ae73c 100644 --- a/src/mkdocstrings/handlers/base.py +++ b/src/mkdocstrings/handlers/base.py @@ -10,6 +10,7 @@ import importlib import sys +import warnings from abc import ABC, abstractmethod from pathlib import Path from typing import Any, Dict, Iterable, List, Optional, Sequence @@ -423,7 +424,16 @@ def get_handler(self, name: str, handler_config: Optional[dict] = None) -> BaseH if name not in self._handlers: if handler_config is None: handler_config = self.get_handler_config(name) - module = importlib.import_module(f"mkdocstrings.handlers.{name}") + try: + module = importlib.import_module(f"mkdocstrings_handlers.{name}") + except ModuleNotFoundError: + module = importlib.import_module(f"mkdocstrings.handlers.{name}") + warnings.warn( + DeprecationWarning( + "Using the mkdocstrings.handlers namespace is deprecated. " + "Handlers must now use the mkdocstrings_handlers namespace." + ) + ) self._handlers[name] = module.get_handler( self._config["theme_name"], self._config["mkdocstrings"]["custom_templates"], From d5d5f1844dbac3affacc95f2f3eab57a61d2068c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Tue, 1 Feb 2022 18:39:02 +0100 Subject: [PATCH 11/21] refactor: Find templates in new and deprecated namespaces --- docs/handlers/overview.md | 42 ++++++++++----- src/mkdocstrings/handlers/base.py | 88 +++++++++++++++++++++---------- 2 files changed, 89 insertions(+), 41 deletions(-) diff --git a/docs/handlers/overview.md b/docs/handlers/overview.md index 47e8fb7e..146fafc5 100644 --- a/docs/handlers/overview.md +++ b/docs/handlers/overview.md @@ -19,18 +19,16 @@ For *mkdocstrings*, a custom handler package would have the following structure: ``` 📁 your_repository -└─╴📁 mkdocstrings -   └─╴📁 handlers - └─╴📄 custom_handler.py +└─╴📁 mkdocstrings_handlers + └─╴📁 custom_handler + ├─╴📁 templates + │  ├─╴📁 material + │ ├─╴📁 mkdocs + │ └─╴📁 readthedocs + └─╴📄 __init__.py ``` -**Note the absence of `__init__.py` modules!** - -If you name you handler after an existing handler, -it will overwrite it! -For example, it means you can overwrite the Python handler -to change how it works or to add functionality, -by naming your handler module `python.py`. +**Note the absence of `__init__.py` module in `mkdocstrings_handlers`!** ### Code @@ -42,7 +40,7 @@ See the documentation for [`BaseRenderer`][mkdocstrings.handlers.base.BaseRenderer]. Check out how the -[Python handler](https://github.com/pawamoy/mkdocstrings/blob/master/src/mkdocstrings/handlers/python.py) +[Python handler](https://github.com/mkdocstrings/python/blob/master/src/mkdocstrings_handlers/python) is written for inspiration. You must implement a `get_handler` method at the module level. @@ -54,8 +52,8 @@ will be passed to this function when getting your handler. ### Templates -You renderer's implementation should normally be backed by templates, which go -to the directory `mkdocstrings/handlers/custom_handler/some_theme`. +Your renderer's implementation should normally be backed by templates, which go +to the directory `mkdocstrings_handlers/custom_handler/templates/some_theme`. (`custom_handler` here should be replaced with the actual name of your handler, and `some_theme` should be the name of an actual MkDocs theme that you support, e.g. `material`). @@ -73,11 +71,27 @@ one of the other theme directories in case they're exactly the same as in the fallback theme. If your theme's HTML requires CSS to go along with it, put it into a file named -`mkdocstrings/handlers/custom_handler/some_theme/style.css`, then this will be +`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. +Finally, it's possible to entirely omit templates, and tell *mkdocstrings* +to use the templates of another handler. In you renderer, override the +`get_templates_dir()` method to return the other handlers templates path: + +```python +from pathlib import Path +from mkdocstrings.handlers.base import BaseRenderer + + +class CobraRenderer(BaseRenderer): + def get_templates_dir(self, handler: str) -> Path: + # use the python handler templates + # (it assumes the python handler is installed) + return super().get_templates_dir("python") +``` + ### Usage When a custom handler is installed, it is then available to *mkdocstrings*. diff --git a/src/mkdocstrings/handlers/base.py b/src/mkdocstrings/handlers/base.py index 7c5ae73c..61ad962f 100644 --- a/src/mkdocstrings/handlers/base.py +++ b/src/mkdocstrings/handlers/base.py @@ -9,9 +9,9 @@ """ import importlib -import sys import warnings from abc import ABC, abstractmethod +from contextlib import suppress from pathlib import Path from typing import Any, Dict, Iterable, List, Optional, Sequence from xml.etree.ElementTree import Element, tostring @@ -74,20 +74,30 @@ class BaseRenderer(ABC): fallback_theme: str = "" extra_css = "" - def __init__(self, directory: str, theme: str, custom_templates: Optional[str] = None) -> None: + def __init__(self, handler: str, theme: str, custom_templates: Optional[str] = None, directory: str = None) -> None: """Initialize the object. If the given theme is not supported (it does not exist), it will look for a `fallback_theme` attribute in `self` to use as a fallback theme. Arguments: - directory: The name of the directory containing the themes for this renderer. + handler: The name of the handler. theme: The name of theme to use. custom_templates: Directory containing custom templates. + directory: Deprecated and renamed as `handler`. """ + # TODO: remove at some point + if directory: + warnings.warn( + "The 'directory' keyword parameter is deprecated and renamed 'handler'. ", + DeprecationWarning, + ) + if not handler: + handler = directory + paths = [] - themes_dir = self.get_templates_dir() / directory + themes_dir = self.get_templates_dir(handler) paths.append(themes_dir / theme) if self.fallback_theme and self.fallback_theme != theme: @@ -100,7 +110,7 @@ def __init__(self, directory: str, theme: str, custom_templates: Optional[str] = break if custom_templates is not None: - paths.insert(0, Path(custom_templates) / directory / theme) + paths.insert(0, Path(custom_templates) / handler / theme) self.env = Environment( autoescape=True, @@ -125,29 +135,52 @@ def render(self, data: CollectorItem, config: dict) -> str: The rendered template as HTML. """ # noqa: DAR202 (excess return section) - def get_templates_dir(self) -> Path: + def get_templates_dir(self, handler: str) -> Path: """Return the path to the handler's templates directory. - Override this method if your handler is for example compiled from C - and does not expose a module directly on the file system from - which we can infer the templates directory path. + Override to customize how the templates directory is found. + + Arguments: + handler: The name of the handler to get the templates directory of. + + Raises: + FileNotFoundError: When the templates directory cannot be found. Returns: The templates directory path. """ - # Namespace packages can span multiple locations. - # This class can be derived, so we must find the templates path - # based on the file path the derived class was defined in. - # To do this, we first get the module of this derived class: - module = sys.modules[self.__class__.__module__] # noqa: WPS609 - # Then we can get the module path: - module_path = Path(module.__file__) # type: ignore[arg-type] # noqa: WPS609 - # Now we can go up to the "mkdocstrings" folder, - # and one down to the "templates" folder: - templates_dir = module_path.parent.resolve() - while templates_dir.name != "mkdocstrings": - templates_dir = templates_dir.parent - return templates_dir / "templates" + # 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 + import mkdocstrings_handlers + + for path in mkdocstrings_handlers.__path__: # noqa: WPS609 + 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__: # noqa: WPS609,WPS440 + theme_path = Path(path, "templates", handler) + 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, + ) + return theme_path + + raise FileNotFoundError(f"Can't find 'templates' folder for handler '{handler}'") def get_anchors(self, data: CollectorItem) -> Sequence[str]: """Return the possible identifiers (HTML anchors) for a collected item. @@ -428,12 +461,13 @@ def get_handler(self, name: str, handler_config: Optional[dict] = None) -> BaseH module = importlib.import_module(f"mkdocstrings_handlers.{name}") except ModuleNotFoundError: module = importlib.import_module(f"mkdocstrings.handlers.{name}") - warnings.warn( - DeprecationWarning( - "Using the mkdocstrings.handlers namespace is deprecated. " - "Handlers must now use the mkdocstrings_handlers namespace." + if name != "python": + warnings.warn( + DeprecationWarning( + "Using the mkdocstrings.handlers namespace is deprecated. " + "Handlers must now use the mkdocstrings_handlers namespace." + ) ) - ) self._handlers[name] = module.get_handler( self._config["theme_name"], self._config["mkdocstrings"]["custom_templates"], From 79e7478ed4797ebd22d884953f252a172770dcb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 5 Feb 2022 13:37:42 +0100 Subject: [PATCH 12/21] docs: Document migration to the new Python handler PR #371: https://github.com/mkdocstrings/mkdocstrings/pull/371/ --- docs/handlers/overview.md | 156 +++++++++++++++++++++++++++++++++++++- mkdocs.yml | 3 +- 2 files changed, 157 insertions(+), 2 deletions(-) diff --git a/docs/handlers/overview.md b/docs/handlers/overview.md index 146fafc5..64412687 100644 --- a/docs/handlers/overview.md +++ b/docs/handlers/overview.md @@ -4,8 +4,162 @@ A handler is what makes it possible to collect and render documentation for a pa ## Available handlers -- Python - Crystal +- Python (Legacy) +- Python (Experimental) + +## About the Python handlers + +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 offer the same features as the legacy one. +If you are making extensive use of the current (legacy) Python handler selection and rendering options, +you might want to wait a bit before trying the experimental handler. +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. + +If you want to keep using the legacy handler as long as possible, +you can depend on `mkdocstrings-python-legacy` directly, +or specify the `python-legacy` extra when depending on *mkdocstrings*: + +```toml +# PEP 621 dependencies declaration +# adapt to your dependencies manager +[project] +dependencies = [ + "mkdocstrings[python-legacy]>=0.18", +] +``` + +The legacy handler will continue to "work" for many releases, +as long as the new handler does not cover all previous use-cases. + +Using the legacy handler will emit a `UserWarning` stating that users +should specify the `python-legacy` extra when depending on *mkdocstrings*. +The warning will be emitted even if you do specify the extra, as we have +no way to detect it. + +Warnings can be globally ignored by setting the +[`PYTHONWARNINGS` environment variable](https://docs.python.org/3/library/warnings.html#describing-warning-filters): + +```bash +PYTHONWARNINGS=ignore::UserWarning:mkdocstrings.handlers.python +``` + +### Migrate to the experimental Python handler + +To use the new, experimental Python handler, +you can depend on `mkdocstrings-python` directly, +or specify the `python` extra when depending on *mkdocstrings*: + +```toml +# PEP 621 dependencies declaration +# adapt to your dependencies manager +[project] +dependencies = [ + "mkdocstrings[python]>=0.18", +] +``` + +#### Handler options + +- `setup_commands` is not yet implemented. But in most cases, you won't need it, + since by default the new handler does not execute the code. + +#### Selection options + +- `filters` is not yet implemented. *Every* declared object is picked up by default, + but only rendered if it has a docstring. Since code is not executed, + inherited attributes (like special methods and private members) are not picked up. +- `members` is not yet implemented. +- `inherited_members` is not yet implemented. +- `docstring_style` is implemented, and used as before, + except for the `restructured-text` style which is renamed `sphinx`. + Numpy-style is now built-in, so you can stop depending on `pytkdocs[numpy-style]` + or `docstring_parser`. +- `docstring_options` is implemented, and used as before, however none + of the provided parsers accept any option yet. +- `new_path_syntax` is irrelevant now. If you were setting it to True, + remove the option and replace every colon (`:`) in your autodoc identifiers + by dots (`.`). + +#### Rendering options + +Every previous option is supported. +Additional options are available: + +- `separate_signature`: Render the signature in a code block below the heading, + instead as inline code. Useful for long signatures. If Black is installed, + the signature is formatted. Default: false. +- `line_length`: The maximum line length to use when formatting signatures. Default: 60. +- `show_submodules`: Whether to render submodules of a module when iterating on children. + Default: true. +- `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! + +#### 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). + +In preparation for Jinja2 blocks, which will improve customization, +each one of these templates extends in fact a base version in `theme/_base`. Example: + +```html+jinja title="theme/docstring/admonition.html" +{% extends "_base/docstring/admonition.html" %} +``` + +```html+jinja title="theme/_base/docstring/admonition.html" +{{ log.debug() }} +
+ {{ section.title|convert_markdown(heading_level, html_id, strip_paragraph=True) }} + {{ section.value.contents|convert_markdown(heading_level, html_id) }} +
+``` + +It means you will be able to customize only *parts* of a template +without having to fully copy-paste it in your project: + +```jinja title="templates/theme/docstring.html" +{% extends "_base/docstring.html" %} +{% block contents %} + {{ block.super }} + Additional contents +{% endblock contents %} +``` + +**Block-level customization is not ready yet.** ## Custom handlers diff --git a/mkdocs.yml b/mkdocs.yml index df8fefd7..04f6638a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -17,8 +17,9 @@ nav: - Theming: theming.md - Handlers: - handlers/overview.md - - Python: https://mkdocstrings.github.io/python/ - Crystal: https://mkdocstrings.github.io/crystal/ + - Python (Legacy): https://mkdocstrings.github.io/python-legacy/ + - Python (Experimental): https://mkdocstrings.github.io/python/ - Troubleshooting: troubleshooting.md # defer to gen-files + literate-nav - Code Reference: reference/ From 5abacf450f60d7d9e208cf4bfc35b62f69646e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 5 Feb 2022 19:00:27 +0100 Subject: [PATCH 13/21] docs: Fix docs warnings --- src/mkdocstrings/extension.py | 14 +++++++------- src/mkdocstrings/handlers/base.py | 6 ++++-- src/mkdocstrings/handlers/rendering.py | 2 +- src/mkdocstrings/inventory.py | 2 +- src/mkdocstrings/loggers.py | 6 +++--- src/mkdocstrings/plugin.py | 19 +++++++++++-------- 6 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/mkdocstrings/extension.py b/src/mkdocstrings/extension.py index d8aae2e5..54696aec 100644 --- a/src/mkdocstrings/extension.py +++ b/src/mkdocstrings/extension.py @@ -23,7 +23,7 @@ """ import re from collections import ChainMap -from typing import Mapping, MutableMapping, MutableSequence, Tuple +from typing import Any, Mapping, MutableMapping, MutableSequence, Tuple from xml.etree.ElementTree import Element import yaml @@ -69,8 +69,8 @@ def __init__( md: A `markdown.Markdown` instance. config: The [configuration][mkdocstrings.plugin.MkdocstringsPlugin.config_scheme] of the `mkdocstrings` plugin. - handlers: A [mkdocstrings.handlers.base.Handlers][] instance. - autorefs: A [mkdocs_autorefs.plugin.AutorefsPlugin][] instance. + handlers: The handlers container. + autorefs: The autorefs plugin instance. """ super().__init__(parser=parser) self.md = md @@ -243,15 +243,15 @@ class MkdocstringsExtension(Extension): It cannot work outside of `mkdocstrings`. """ - def __init__(self, config: dict, handlers: Handlers, autorefs: AutorefsPlugin, **kwargs) -> None: + def __init__(self, config: dict, handlers: Handlers, autorefs: AutorefsPlugin, **kwargs: Any) -> None: """Initialize the object. Arguments: config: The configuration items from `mkdocs` and `mkdocstrings` that must be passed to the block processor when instantiated in [`extendMarkdown`][mkdocstrings.extension.MkdocstringsExtension.extendMarkdown]. - handlers: A [mkdocstrings.handlers.base.Handlers][] instance. - autorefs: A [mkdocs_autorefs.plugin.AutorefsPlugin][] instance. - kwargs: Keyword arguments used by `markdown.extensions.Extension`. + handlers: The handlers container. + autorefs: The autorefs plugin instance. + **kwargs: Keyword arguments used by `markdown.extensions.Extension`. """ super().__init__(**kwargs) self._config = config diff --git a/src/mkdocstrings/handlers/base.py b/src/mkdocstrings/handlers/base.py index 61ad962f..51e74bab 100644 --- a/src/mkdocstrings/handlers/base.py +++ b/src/mkdocstrings/handlers/base.py @@ -197,7 +197,9 @@ def get_anchors(self, data: CollectorItem) -> Sequence[str]: except AttributeError: return () - def do_convert_markdown(self, text: str, heading_level: int, html_id: str = "", *, strip_paragraph=False) -> Markup: + def do_convert_markdown( + self, text: str, heading_level: int, html_id: str = "", *, strip_paragraph: bool = False + ) -> Markup: """Render Markdown text; for use inside templates. Arguments: @@ -239,7 +241,7 @@ def do_heading( role: An optional role for the object bound to this heading. hidden: If True, only register it for the table of contents, don't render anything. toc_label: The title to use in the table of contents ('data-toc-label' attribute). - attributes: Any extra HTML attributes of the heading. + **attributes: Any extra HTML attributes of the heading. Returns: An HTML string. diff --git a/src/mkdocstrings/handlers/rendering.py b/src/mkdocstrings/handlers/rendering.py index ef497861..24ee6268 100644 --- a/src/mkdocstrings/handlers/rendering.py +++ b/src/mkdocstrings/handlers/rendering.py @@ -81,7 +81,7 @@ def highlight( # noqa: W0221 (intentionally different params, we're extending t inline: bool = False, dedent: bool = True, linenums: Optional[bool] = None, - **kwargs, + **kwargs: Any, ) -> str: """Highlight a code-snippet. diff --git a/src/mkdocstrings/inventory.py b/src/mkdocstrings/inventory.py index 9a59d2b4..6c1b8558 100644 --- a/src/mkdocstrings/inventory.py +++ b/src/mkdocstrings/inventory.py @@ -80,7 +80,7 @@ def __init__(self, items: Optional[List[InventoryItem]] = None, project: str = " self.project = project self.version = version - def register(self, *args, **kwargs): + def register(self, *args: str, **kwargs: str): """Create and register an item. Arguments: diff --git a/src/mkdocstrings/loggers.py b/src/mkdocstrings/loggers.py index 6be121ed..702017ae 100644 --- a/src/mkdocstrings/loggers.py +++ b/src/mkdocstrings/loggers.py @@ -2,7 +2,7 @@ import logging from pathlib import Path -from typing import Any, Callable, Optional, Tuple +from typing import Any, Callable, Dict, Optional, Tuple from jinja2.runtime import Context from mkdocs.utils import warning_filter @@ -18,7 +18,7 @@ class LoggerAdapter(logging.LoggerAdapter): """A logger adapter to prefix messages.""" - def __init__(self, prefix: str, logger): + def __init__(self, prefix: str, logger: logging.Logger): """Initialize the object. Arguments: @@ -28,7 +28,7 @@ def __init__(self, prefix: str, logger): super().__init__(logger, {}) self.prefix = prefix - def process(self, msg: str, kwargs) -> Tuple[str, Any]: + def process(self, msg: str, kwargs: Dict[Any, Any]) -> Tuple[str, Any]: """Process the message. Arguments: diff --git a/src/mkdocstrings/plugin.py b/src/mkdocstrings/plugin.py index 33e79b00..a30c7b10 100644 --- a/src/mkdocstrings/plugin.py +++ b/src/mkdocstrings/plugin.py @@ -22,6 +22,7 @@ from mkdocs.config import Config from mkdocs.config.config_options import Type as MkType +from mkdocs.livereload import LiveReloadServer from mkdocs.plugins import BasePlugin from mkdocs.utils import write_file from mkdocs_autorefs.plugin import AutorefsPlugin @@ -114,7 +115,7 @@ def handlers(self) -> Handlers: raise RuntimeError("The plugin hasn't been initialized with a config yet") return self._handlers - def on_serve(self, server, builder: Callable, **kwargs): # noqa: W0613 (unused arguments) + def on_serve(self, server: LiveReloadServer, builder: Callable, **kwargs: Any): # noqa: W0613 (unused arguments) """Watch directories. Hook for the [`on_serve` event](https://www.mkdocs.org/user-guide/plugins/#on_serve). @@ -125,13 +126,13 @@ def on_serve(self, server, builder: Callable, **kwargs): # noqa: W0613 (unused Arguments: server: The `livereload` server instance. builder: The function to build the site. - kwargs: Additional arguments passed by MkDocs. + **kwargs: Additional arguments passed by MkDocs. """ for element in self.config["watch"]: log.debug(f"Adding directory '{element}' to watcher") server.watch(element, builder) - def on_config(self, config: Config, **kwargs) -> Config: # noqa: W0613 (unused arguments) + def on_config(self, config: Config, **kwargs: Any) -> Config: # noqa: W0613 (unused arguments) """Instantiate our Markdown extension. Hook for the [`on_config` event](https://www.mkdocs.org/user-guide/plugins/#on_config). @@ -143,7 +144,7 @@ def on_config(self, config: Config, **kwargs) -> Config: # noqa: W0613 (unused Arguments: config: The MkDocs config object. - kwargs: Additional arguments passed by MkDocs. + **kwargs: Additional arguments passed by MkDocs. Returns: The modified config. @@ -238,7 +239,9 @@ def on_env(self, env, config: Config, **kwargs): config["plugins"]["autorefs"].register_url(page, identifier) self._inv_futures = [] - def on_post_build(self, config: Config, **kwargs) -> None: # noqa: W0613,R0201 (unused arguments, cannot be static) + def on_post_build( + self, config: Config, **kwargs: Any + ) -> None: # noqa: W0613,R0201 (unused arguments, cannot be static) """Teardown the handlers. Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build). @@ -250,7 +253,7 @@ def on_post_build(self, config: Config, **kwargs) -> None: # noqa: W0613,R0201 Arguments: config: The MkDocs config object. - kwargs: Additional arguments passed by MkDocs. + **kwargs: Additional arguments passed by MkDocs. """ for future in self._inv_futures: future.cancel() @@ -272,13 +275,13 @@ def get_handler(self, handler_name: str) -> BaseHandler: @classmethod @functools.lru_cache(maxsize=None) - def _load_inventory(cls, loader: InventoryLoaderType, url: str, **kwargs) -> Mapping[str, str]: + def _load_inventory(cls, loader: InventoryLoaderType, url: str, **kwargs: Any) -> Mapping[str, str]: """Download and process inventory files using a handler. Arguments: loader: A function returning a sequence of pairs (identifier, url). url: The URL to download and process. - kwargs: Extra arguments to pass to the loader. + **kwargs: Extra arguments to pass to the loader. Returns: A mapping from identifier to absolute URL. From 631d799511d4fb44268fa5991293cdb1f9f677b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 5 Feb 2022 19:00:53 +0100 Subject: [PATCH 14/21] docs: Set redirects for autorefs reference pages --- docs/gen_redirects.py | 19 +++++++++++++++++++ docs/gen_ref_nav.py | 3 --- docs/reference/autorefs/plugin.md | 1 - docs/reference/autorefs/references.md | 1 - mkdocs.yml | 1 + 5 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 docs/gen_redirects.py delete mode 100644 docs/reference/autorefs/plugin.md delete mode 100644 docs/reference/autorefs/references.md diff --git a/docs/gen_redirects.py b/docs/gen_redirects.py new file mode 100644 index 00000000..f35cce9c --- /dev/null +++ b/docs/gen_redirects.py @@ -0,0 +1,19 @@ +"""Generate redirection pages for autorefs reference.""" + +import mkdocs_gen_files + +redirect_map = { + "reference/autorefs/references.md": "https://mkdocstrings.github.io/autorefs/reference/mkdocs_autorefs/references/", + "reference/autorefs/plugin.md": "https://mkdocstrings.github.io/autorefs/reference/mkdocs_autorefs/plugin/", +} + +redirect_template = """ + +Redirecting... +""" + +for page, link in redirect_map.items(): + with mkdocs_gen_files.open(page, "w") as fd: + print(redirect_template.format(link=link), file=fd) diff --git a/docs/gen_ref_nav.py b/docs/gen_ref_nav.py index 1411abdb..26b9b6d9 100644 --- a/docs/gen_ref_nav.py +++ b/docs/gen_ref_nav.py @@ -21,8 +21,5 @@ mkdocs_gen_files.set_edit_path(full_doc_path, path) -nav["mkdocs_autorefs", "references.py"] = "autorefs/references.md" -nav["mkdocs_autorefs", "plugin.py"] = "autorefs/plugin.md" - with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file: nav_file.writelines(nav.build_literate_nav()) diff --git a/docs/reference/autorefs/plugin.md b/docs/reference/autorefs/plugin.md deleted file mode 100644 index fed901e4..00000000 --- a/docs/reference/autorefs/plugin.md +++ /dev/null @@ -1 +0,0 @@ -::: mkdocs_autorefs.plugin diff --git a/docs/reference/autorefs/references.md b/docs/reference/autorefs/references.md deleted file mode 100644 index 2a23e10e..00000000 --- a/docs/reference/autorefs/references.md +++ /dev/null @@ -1 +0,0 @@ -::: mkdocs_autorefs.references diff --git a/mkdocs.yml b/mkdocs.yml index 04f6638a..b596989f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -76,6 +76,7 @@ plugins: scripts: - docs/gen_credits.py - docs/gen_ref_nav.py + - docs/gen_redirects.py - literate-nav: nav_file: SUMMARY.md - section-index From 5dcaef4d5f36d8b26656e6d1813c1da271137911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 5 Feb 2022 19:03:46 +0100 Subject: [PATCH 15/21] docs: Clean up docs config, load more inventories --- mkdocs.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index b596989f..b2129341 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -84,13 +84,10 @@ plugins: - mkdocstrings: handlers: python: - setup_commands: - - import sys - - sys.path.append("docs") - selection: - new_path_syntax: yes - import: # demonstration purpose in the docs - - https://docs.python-requests.org/en/master/objects.inv + import: + - https://docs.python.org/3/objects.inv + - https://docs.python-requests.org/en/master/objects.inv # demonstration purpose in the docs + - https://mkdocstrings.github.io/autorefs/objects.inv watch: - src/mkdocstrings From 345635c354f951e87a10a6cdbc52703053a0b4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 5 Feb 2022 20:28:24 +0100 Subject: [PATCH 16/21] ci: Fix mypy warning --- src/mkdocstrings/loggers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mkdocstrings/loggers.py b/src/mkdocstrings/loggers.py index 702017ae..3cce5837 100644 --- a/src/mkdocstrings/loggers.py +++ b/src/mkdocstrings/loggers.py @@ -2,7 +2,7 @@ import logging from pathlib import Path -from typing import Any, Callable, Dict, Optional, Tuple +from typing import Any, Callable, MutableMapping, Optional, Tuple from jinja2.runtime import Context from mkdocs.utils import warning_filter @@ -28,7 +28,7 @@ def __init__(self, prefix: str, logger: logging.Logger): super().__init__(logger, {}) self.prefix = prefix - def process(self, msg: str, kwargs: Dict[Any, Any]) -> Tuple[str, Any]: + def process(self, msg: str, kwargs: MutableMapping[str, Any]) -> Tuple[str, Any]: """Process the message. Arguments: From f62b23da9c4ea5b83a128c4eb27768ffef7c0d08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 5 Feb 2022 20:28:31 +0100 Subject: [PATCH 17/21] tests: Fix tests --- tests/fixtures/__init__.py | 1 + tests/fixtures/html_tokens.py | 2 ++ tests/test_extension.py | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/__init__.py create mode 100644 tests/fixtures/html_tokens.py diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py new file mode 100644 index 00000000..fa812cec --- /dev/null +++ b/tests/fixtures/__init__.py @@ -0,0 +1 @@ +"""Some fixtures for tests.""" diff --git a/tests/fixtures/html_tokens.py b/tests/fixtures/html_tokens.py new file mode 100644 index 00000000..3c4058b8 --- /dev/null +++ b/tests/fixtures/html_tokens.py @@ -0,0 +1,2 @@ +def func(foo="

HELLO

"): + """test""" diff --git a/tests/test_extension.py b/tests/test_extension.py index 759cad94..97b6488a 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -61,8 +61,8 @@ def test_quote_inside_annotation(ext_markdown): def test_html_inside_heading(ext_markdown): """Assert that headings don't double-escape HTML.""" - output = ext_markdown.convert("::: tests.fixtures.builtin") - assert "=<" in output + output = ext_markdown.convert("::: tests.fixtures.html_tokens") + assert "'<" in output assert "&" not in output From 6c2b7348ae40989e4adccc087feae599fcea949d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sun, 6 Feb 2022 19:46:31 +0100 Subject: [PATCH 18/21] refactor: Prefix logs with the package name only PR #375: https://github.com/mkdocstrings/mkdocstrings/pull/375 --- src/mkdocstrings/loggers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mkdocstrings/loggers.py b/src/mkdocstrings/loggers.py index 3cce5837..0916e3c1 100644 --- a/src/mkdocstrings/loggers.py +++ b/src/mkdocstrings/loggers.py @@ -122,7 +122,7 @@ def get_logger(name: str) -> LoggerAdapter: """ logger = logging.getLogger(f"mkdocs.plugins.{name}") logger.addFilter(warning_filter) - return LoggerAdapter(name, logger) + return LoggerAdapter(name.split(".", 1)[0], logger) def get_template_logger() -> TemplateLogger: From b8222b0150d4743be857bcbf40f014265095885b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sun, 6 Feb 2022 19:48:27 +0100 Subject: [PATCH 19/21] build: Add Crystal extra, update Python extras versions PR #374: https://github.com/mkdocstrings/mkdocstrings/pull/374 --- pyproject.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3c49e303..d2332e74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,8 +40,9 @@ dependencies = [ ] [project.optional-dependencies] -python-legacy = ["mkdocstrings-python-legacy>=0.2"] -python = ["mkdocstrings-python>=0.5.1"] +crystal = ["mkdocstrings-crystal>=0.3.4"] +python-legacy = ["mkdocstrings-python-legacy>=0.2.1"] +python = ["mkdocstrings-python>=0.5.2"] [project.urls] Homepage = "https://mkdocstrings.github.io" From 0e8d278de6a26fb49637c67fbacef6ba1fed9cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sun, 6 Feb 2022 20:17:20 +0100 Subject: [PATCH 20/21] docs: Clean up and fix tabs --- CREDITS.md | 128 ----------------------------------------------------- mkdocs.yml | 3 +- 2 files changed, 2 insertions(+), 129 deletions(-) delete mode 100644 CREDITS.md diff --git a/CREDITS.md b/CREDITS.md deleted file mode 100644 index 8357b62e..00000000 --- a/CREDITS.md +++ /dev/null @@ -1,128 +0,0 @@ - - -# Credits -These projects were used to build `mkdocstrings`. **Thank you!** - -[`python`](https://www.python.org/) | -[`poetry`](https://poetry.eustace.io/) | -[`copier-poetry`](https://github.com/pawamoy/copier-poetry) - -### Direct dependencies -[`autoflake`](https://github.com/myint/autoflake) | -[`black`](https://github.com/psf/black) | -[`darglint`](None) | -[`duty`](https://github.com/pawamoy/duty) | -[`flake8-bandit`](https://github.com/tylerwince/flake8-bandit) | -[`flake8-black`](https://github.com/peterjc/flake8-black) | -[`flake8-bugbear`](https://github.com/PyCQA/flake8-bugbear) | -[`flake8-builtins`](https://github.com/gforcada/flake8-builtins) | -[`flake8-comprehensions`](https://github.com/adamchainz/flake8-comprehensions) | -[`flake8-docstrings`](https://gitlab.com/pycqa/flake8-docstrings) | -[`flake8-pytest-style`](https://pypi.org/project/flake8-pytest-style) | -[`flake8-string-format`](https://github.com/xZise/flake8-string-format) | -[`flake8-tidy-imports`](https://github.com/adamchainz/flake8-tidy-imports) | -[`flake8-variables-names`](https://github.com/best-doctor/flake8-variables-names) | -[`flakehell`](None) | -[`git-changelog`](https://github.com/pawamoy/git-changelog) | -[`httpx`](https://github.com/encode/httpx) | -[`ipython`](https://ipython.org) | -[`isort`](https://pycqa.github.io/isort/) | -[`Jinja2`](https://palletsprojects.com/p/jinja/) | -[`jinja2-cli`](https://github.com/mattrobenolt/jinja2-cli) | -[`Markdown`](https://Python-Markdown.github.io/) | -[`MarkupSafe`](https://palletsprojects.com/p/markupsafe/) | -[`mkdocs`](https://www.mkdocs.org) | -[`mkdocs-material`](https://squidfunk.github.io/mkdocs-material/) | -[`mypy`](http://www.mypy-lang.org/) | -[`pep8-naming`](https://github.com/PyCQA/pep8-naming) | -[`pymdown-extensions`](https://github.com/facelessuser/pymdown-extensions) | -[`pytest`](https://docs.pytest.org/en/latest/) | -[`pytest-cov`](https://github.com/pytest-dev/pytest-cov) | -[`pytest-randomly`](https://github.com/pytest-dev/pytest-randomly) | -[`pytest-sugar`](http://pivotfinland.com/pytest-sugar/) | -[`pytest-xdist`](https://github.com/pytest-dev/pytest-xdist) | -[`pytkdocs`](https://github.com/pawamoy/pytkdocs) | -[`toml`](https://github.com/uiri/toml) - -### Indirect dependencies -[`ansimarkup`](https://github.com/gvalkov/python-ansimarkup) | -[`apipkg`](https://github.com/pytest-dev/apipkg) | -[`appdirs`](http://github.com/ActiveState/appdirs) | -[`appnope`](http://github.com/minrk/appnope) | -[`astroid`](https://github.com/PyCQA/astroid) | -[`atomicwrites`](https://github.com/untitaker/python-atomicwrites) | -[`attrs`](https://www.attrs.org/) | -[`backcall`](https://github.com/takluyver/backcall) | -[`bandit`](https://bandit.readthedocs.io/en/latest/) | -[`certifi`](https://certifiio.readthedocs.io/en/latest/) | -[`chardet`](https://github.com/chardet/chardet) | -[`click`](https://palletsprojects.com/p/click/) | -[`colorama`](https://github.com/tartley/colorama) | -[`contextvars`](http://github.com/MagicStack/contextvars) | -[`coverage`](https://github.com/nedbat/coveragepy) | -[`dataclasses`](https://github.com/ericvsmith/dataclasses) | -[`decorator`](https://github.com/micheles/decorator) | -[`entrypoints`](https://github.com/takluyver/entrypoints) | -[`execnet`](https://execnet.readthedocs.io/en/latest/) | -[`failprint`](https://github.com/pawamoy/failprint) | -[`flake8`](https://gitlab.com/pycqa/flake8) | -[`flake8-plugin-utils`](https://pypi.org/project/flake8-plugin-utils) | -[`flake8-polyfill`](https://gitlab.com/pycqa/flake8-polyfill) | -[`future`](https://python-future.org) | -[`gitdb`](https://github.com/gitpython-developers/gitdb) | -[`GitPython`](https://github.com/gitpython-developers/GitPython) | -[`h11`](https://github.com/python-hyper/h11) | -[`httpcore`](https://github.com/encode/httpcore) | -[`idna`](https://github.com/kjd/idna) | -[`immutables`](https://github.com/MagicStack/immutables) | -[`importlib-metadata`](https://github.com/python/importlib_metadata) | -[`iniconfig`](http://github.com/RonnyPfannschmidt/iniconfig) | -[`ipython-genutils`](http://ipython.org) | -[`jedi`](https://github.com/davidhalter/jedi) | -[`joblib`](https://joblib.readthedocs.io) | -[`lazy-object-proxy`](https://github.com/ionelmc/python-lazy-object-proxy) | -[`livereload`](https://github.com/lepture/python-livereload) | -[`lunr`](https://github.com/yeraydiazdiaz/lunr.py) | -[`mccabe`](https://github.com/pycqa/mccabe) | -[`mkdocs-material-extensions`](https://github.com/facelessuser/mkdocs-material-extensions) | -[`mypy-extensions`](https://github.com/python/mypy_extensions) | -[`nltk`](http://nltk.org/) | -[`packaging`](https://github.com/pypa/packaging) | -[`parso`](https://github.com/davidhalter/parso) | -[`pathspec`](https://github.com/cpburnz/python-path-specification) | -[`pbr`](https://docs.openstack.org/pbr/latest/) | -[`pexpect`](https://pexpect.readthedocs.io/) | -[`pickleshare`](https://github.com/pickleshare/pickleshare) | -[`pluggy`](https://github.com/pytest-dev/pluggy) | -[`prompt-toolkit`](https://github.com/prompt-toolkit/python-prompt-toolkit) | -[`ptyprocess`](https://github.com/pexpect/ptyprocess) | -[`py`](https://py.readthedocs.io/) | -[`pycodestyle`](https://pycodestyle.readthedocs.io/) | -[`pydocstyle`](https://github.com/PyCQA/pydocstyle/) | -[`pyflakes`](https://github.com/PyCQA/pyflakes) | -[`Pygments`](https://pygments.org/) | -[`pylint`](https://github.com/PyCQA/pylint) | -[`pyparsing`](https://github.com/pyparsing/pyparsing/) | -[`pytest-forked`](https://github.com/pytest-dev/pytest-forked) | -[`PyYAML`](https://pyyaml.org/) | -[`regex`](https://bitbucket.org/mrabarnett/mrab-regex) | -[`rfc3986`](http://rfc3986.readthedocs.io) | -[`six`](https://github.com/benjaminp/six) | -[`smmap`](https://github.com/gitpython-developers/smmap) | -[`sniffio`](https://github.com/python-trio/sniffio) | -[`snowballstemmer`](https://github.com/snowballstem/snowball) | -[`stevedore`](https://docs.openstack.org/stevedore/latest/) | -[`termcolor`](http://pypi.python.org/pypi/termcolor) | -[`tornado`](http://www.tornadoweb.org/) | -[`tqdm`](https://github.com/tqdm/tqdm) | -[`traitlets`](http://ipython.org) | -[`typed-ast`](https://github.com/python/typed_ast) | -[`typing-extensions`](https://github.com/python/typing/blob/master/typing_extensions/README.rst) | -[`urllib3`](https://urllib3.readthedocs.io/) | -[`wcwidth`](https://github.com/jquast/wcwidth) | -[`wrapt`](https://github.com/GrahamDumpleton/wrapt) | -[`zipp`](https://github.com/jaraco/zipp) - -**[More credits from the author](http://pawamoy.github.io/credits/)** \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index b2129341..d1613a44 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -65,7 +65,8 @@ markdown_extensions: - pymdownx.snippets: check_paths: true - pymdownx.superfences -- pymdownx.tabbed +- pymdownx.tabbed: + alternate_style: true - pymdownx.tasklist - toc: permalink: "¤" From 3f0975703e7c9b57264f23040e584d70bd9331c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sun, 6 Feb 2022 20:18:15 +0100 Subject: [PATCH 21/21] chore: Prepare release 0.18.0 --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5593cd0..54c7e160 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,36 @@ 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.18.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.18.0) - 2022-02-06 + +[Compare with 0.17.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.17.0...0.18.0) + +### Highlights +- Python 3.6 support is dropped. +- We provide a new, experimental Python handler based on [Griffe](https://github.com/mkdocstrings/griffe). + This new handler brings automatic cross-references for every annotation in your code, + including references to third-party libraries' APIs if they provide objects inventories + and you explicitely [load them](https://mkdocstrings.github.io/usage/#cross-references-to-other-projects-inventories) in `mkdocs.yml`. + [See migration notes in the documentation](https://mkdocstrings.github.io/handlers/overview/#about-the-python-handlers). +- The "legacy" Python handler now lives in its own repository at https://github.com/mkdocstrings/python-legacy. + +### Packaging / Dependencies +- Add Crystal extra, update Python extras versions ([b8222b0](https://github.com/mkdocstrings/mkdocstrings/commit/b8222b0150d4743be857bcbf40f014265095885b) by Timothée Mazzucotelli). [PR #374](https://github.com/mkdocstrings/mkdocstrings/pull/374) +- Update autorefs to actually required version ([fc6c7f6](https://github.com/mkdocstrings/mkdocstrings/commit/fc6c7f652a420ac29cf16cbb99b11a55aa9b38ea) by Timothée Mazzucotelli). +- Drop Python 3.6 support ([7205ac6](https://github.com/mkdocstrings/mkdocstrings/commit/7205ac6cf2861db61c2a5b8bf07d0e6b1a7f49fb) by Timothée Mazzucotelli). + +### Features +- Allow unwrapping the `

` tag in `convert_markdown` filter ([5351fc8](https://github.com/mkdocstrings/mkdocstrings/commit/5351fc8b417fb20f0681a22f49fcc902579eacdb) by Oleh Prypin). [PR #369](https://github.com/mkdocstrings/mkdocstrings/pull/369) +- Support handlers spanning multiple locations ([f42dfc6](https://github.com/mkdocstrings/mkdocstrings/commit/f42dfc61ce4f9f317c4bd17f568e504ed9764d35) by Timothée Mazzucotelli). [PR #355](https://github.com/mkdocstrings/mkdocstrings/pull/355) + +### Code Refactoring +- Prefix logs with the package name only ([6c2b734](https://github.com/mkdocstrings/mkdocstrings/commit/6c2b7348ae40989e4adccc087feae599fcea949d) by Timothée Mazzucotelli). [PR #375](https://github.com/mkdocstrings/mkdocstrings/pull/375) +- Extract the Python handler into its own repository ([74371e4](https://github.com/mkdocstrings/mkdocstrings/commit/74371e49c32059fefd34c7cc7f7b8f085b383237) by Timothée Mazzucotelli). [PR #356](https://github.com/mkdocstrings/mkdocstrings/pull/356) +- Support Jinja2 3.1 ([b377227](https://github.com/mkdocstrings/mkdocstrings/commit/b37722716b1e0ed6393ec71308dfb0f85e142f3b) by Timothée Mazzucotelli). [Issue #360](https://github.com/mkdocstrings/mkdocstrings/issues/360), [PR #361](https://github.com/mkdocstrings/mkdocstrings/pull/361) +- Find templates in new and deprecated namespaces ([d5d5f18](https://github.com/mkdocstrings/mkdocstrings/commit/d5d5f1844dbac3affacc95f2f3eab57a61d2068c) by Timothée Mazzucotelli). [PR #367](https://github.com/mkdocstrings/mkdocstrings/pull/367) +- Support loading handlers from the `mkdocstrings_handlers` namespace ([5c22c6c](https://github.com/mkdocstrings/mkdocstrings/commit/5c22c6ce4e056ac2334e2dfcd47c1f1a7884d352) by Timothée Mazzucotelli). [PR #367](https://github.com/mkdocstrings/mkdocstrings/pull/367) + + ## [0.17.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.17.0) - 2021-12-27 [Compare with 0.16.2](https://github.com/mkdocstrings/mkdocstrings/compare/0.16.2...0.17.0)