From c2e0c92c5c01ddf7e1940e487379443a8e058715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Fri, 12 May 2023 16:43:46 +0200 Subject: [PATCH 1/2] feat: Support custom templates through objects' `template` attribute --- src/mkdocstrings_handlers/python/handler.py | 5 +++- .../templates/material/_base/children.html | 26 +++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/mkdocstrings_handlers/python/handler.py b/src/mkdocstrings_handlers/python/handler.py index 9bfb02f4..9f4dfa6f 100644 --- a/src/mkdocstrings_handlers/python/handler.py +++ b/src/mkdocstrings_handlers/python/handler.py @@ -297,7 +297,10 @@ def render(self, data: CollectorItem, config: Mapping[str, Any]) -> str: # noqa mutabled_config = dict(copy.deepcopy(config)) final_config = ChainMap(mutabled_config, self.default_config) - template = self.env.get_template(f"{data.kind.value}.html") + if hasattr(data, "template") and data.template: + template = self.env.get_template(data.template) + else: + template = self.env.get_template(f"{data.kind.value}.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 diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/children.html b/src/mkdocstrings_handlers/python/templates/material/_base/children.html index 9e27ed0f..9b6b81a7 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/children.html +++ b/src/mkdocstrings_handlers/python/templates/material/_base/children.html @@ -27,7 +27,7 @@ {% with heading_level = heading_level + extra_level %} {% for attribute in attributes|order_members(config.members_order, members_list) %} {% if not attribute.is_alias or attribute.is_explicitely_exported %} - {% include "attribute.html" with context %} + {% include attribute.template or "attribute.html" with context %} {% endif %} {% endfor %} {% endwith %} @@ -42,7 +42,7 @@ {% with heading_level = heading_level + extra_level %} {% for class in classes|order_members(config.members_order, members_list) %} {% if not class.is_alias or class.is_explicitely_exported %} - {% include "class.html" with context %} + {% include class.template or "class.html" with context %} {% endif %} {% endfor %} {% endwith %} @@ -58,7 +58,7 @@ {% for function in functions|order_members(config.members_order, members_list) %} {% if not (obj.kind.value == "class" and function.name == "__init__" and config.merge_init_into_class) %} {% if not function.is_alias or function.is_explicitely_exported %} - {% include "function.html" with context %} + {% include function.template or "function.html" with context %} {% endif %} {% endif %} {% endfor %} @@ -75,7 +75,7 @@ {% with heading_level = heading_level + extra_level %} {% for module in modules|order_members(config.members_order, members_list) %} {% if not module.is_alias or module.is_explicitely_exported %} - {% include "module.html" with context %} + {% include module.template or "module.html" with context %} {% endif %} {% endfor %} {% endwith %} @@ -91,26 +91,26 @@ filter_objects(filters=config.filters, members_list=members_list, keep_no_docstrings=config.show_if_no_docstring)| order_members(config.members_order, members_list) %} - {% if not (obj.kind.value == "class" and child.name == "__init__" and config.merge_init_into_class) %} + {% if not (obj.is_class and child.name == "__init__" and config.merge_init_into_class) %} - {% if child.kind.value == "attribute" %} + {% if child.is_attribute %} {% with attribute = child %} - {% include "attribute.html" with context %} + {% include attribute.template or "attribute.html" with context %} {% endwith %} - {% elif child.kind.value == "class" %} + {% elif child.is_class %} {% with class = child %} - {% include "class.html" with context %} + {% include class.template or "class.html" with context %} {% endwith %} - {% elif child.kind.value == "function" %} + {% elif child.is_function %} {% with function = child %} - {% include "function.html" with context %} + {% include function.template or "function.html" with context %} {% endwith %} - {% elif child.kind.value == "module" and config.show_submodules %} + {% elif child.is_module and config.show_submodules %} {% with module = child %} - {% include "module.html" with context %} + {% include module.template or "module.html" with context %} {% endwith %} {% endif %} From 220e9a0676b751a7ae5b492adbdcb2908dbb70d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Wed, 24 May 2023 13:09:44 +0200 Subject: [PATCH 2/2] fixup! feat: Support custom templates through objects' `template` attribute --- docs/usage/extensions.md | 17 +++++++++++++++++ mkdocs.yml | 1 + src/mkdocstrings_handlers/python/handler.py | 7 +++---- src/mkdocstrings_handlers/python/rendering.py | 13 +++++++++++++ .../templates/material/_base/children.html | 16 ++++++++-------- 5 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 docs/usage/extensions.md diff --git a/docs/usage/extensions.md b/docs/usage/extensions.md new file mode 100644 index 00000000..4f6b96b3 --- /dev/null +++ b/docs/usage/extensions.md @@ -0,0 +1,17 @@ +# Extensions + +## :warning: Work in Progress! + +The Python handler supports extensions through +[*mkdocstrings*' handler extensions](https://mkdocstrings.github.io/usage/handlers/#handler-extensions). + +Specifically, additional templates can be added to the handler, +and Griffe extensions can instruct the handler to use a particular template +for a particular object by setting a value in the Griffe object's `extra` dictionary: + +```python title="griffe_extension.py" +obj = ... # get a reference to a Griffe object +if "mkdocstrings" not in obj.extra: + obj.extra["mkdocstrings"] = {} +obj.extra["mkdocstrings"]["template"] = "template_name.html" +``` diff --git a/mkdocs.yml b/mkdocs.yml index 50ba6925..85fd7ef7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -27,6 +27,7 @@ nav: - Sphinx: usage/docstrings/sphinx.md - Advanced: - Customization: usage/customization.md + - Extensions: usage/extensions.md # defer to gen-files + literate-nav - Code Reference: reference/ - Development: diff --git a/src/mkdocstrings_handlers/python/handler.py b/src/mkdocstrings_handlers/python/handler.py index 9f4dfa6f..c5a06d0c 100644 --- a/src/mkdocstrings_handlers/python/handler.py +++ b/src/mkdocstrings_handlers/python/handler.py @@ -297,10 +297,8 @@ def render(self, data: CollectorItem, config: Mapping[str, Any]) -> str: # noqa mutabled_config = dict(copy.deepcopy(config)) final_config = ChainMap(mutabled_config, self.default_config) - if hasattr(data, "template") and data.template: - template = self.env.get_template(data.template) - else: - template = self.env.get_template(f"{data.kind.value}.html") + template_name = rendering.do_get_template(data) + template = self.env.get_template(template_name) # 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 @@ -338,6 +336,7 @@ def update_env(self, md: Markdown, config: dict) -> None: # noqa: D102 (ignore self.env.filters["format_signature"] = rendering.do_format_signature self.env.filters["filter_objects"] = rendering.do_filter_objects self.env.filters["stash_crossref"] = lambda ref, length: ref + self.env.filters["get_template"] = rendering.do_get_template def get_anchors(self, data: CollectorItem) -> set[str]: # noqa: D102 (ignore missing docstring) try: diff --git a/src/mkdocstrings_handlers/python/rendering.py b/src/mkdocstrings_handlers/python/rendering.py index d1f0eb75..5c8b0f19 100644 --- a/src/mkdocstrings_handlers/python/rendering.py +++ b/src/mkdocstrings_handlers/python/rendering.py @@ -248,3 +248,16 @@ def formatter(code: str, line_length: int) -> str: return format_str(code, mode=mode) return formatter + + +def do_get_template(obj: Object) -> str: + """Get the template name used to render an object. + + Parameters: + obj: A Griffe object. + + Returns: + A template name. + """ + extra_data = getattr(obj, "extra", {}).get("mkdocstrings", {}) + return extra_data.get("template", "") or f"{obj.kind.value}.html" diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/children.html b/src/mkdocstrings_handlers/python/templates/material/_base/children.html index 9b6b81a7..dda1c5ff 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/children.html +++ b/src/mkdocstrings_handlers/python/templates/material/_base/children.html @@ -27,7 +27,7 @@ {% with heading_level = heading_level + extra_level %} {% for attribute in attributes|order_members(config.members_order, members_list) %} {% if not attribute.is_alias or attribute.is_explicitely_exported %} - {% include attribute.template or "attribute.html" with context %} + {% include attribute|get_template with context %} {% endif %} {% endfor %} {% endwith %} @@ -42,7 +42,7 @@ {% with heading_level = heading_level + extra_level %} {% for class in classes|order_members(config.members_order, members_list) %} {% if not class.is_alias or class.is_explicitely_exported %} - {% include class.template or "class.html" with context %} + {% include class|get_template with context %} {% endif %} {% endfor %} {% endwith %} @@ -58,7 +58,7 @@ {% for function in functions|order_members(config.members_order, members_list) %} {% if not (obj.kind.value == "class" and function.name == "__init__" and config.merge_init_into_class) %} {% if not function.is_alias or function.is_explicitely_exported %} - {% include function.template or "function.html" with context %} + {% include function|get_template with context %} {% endif %} {% endif %} {% endfor %} @@ -75,7 +75,7 @@ {% with heading_level = heading_level + extra_level %} {% for module in modules|order_members(config.members_order, members_list) %} {% if not module.is_alias or module.is_explicitely_exported %} - {% include module.template or "module.html" with context %} + {% include module|get_template with context %} {% endif %} {% endfor %} {% endwith %} @@ -95,22 +95,22 @@ {% if child.is_attribute %} {% with attribute = child %} - {% include attribute.template or "attribute.html" with context %} + {% include attribute|get_template with context %} {% endwith %} {% elif child.is_class %} {% with class = child %} - {% include class.template or "class.html" with context %} + {% include class|get_template with context %} {% endwith %} {% elif child.is_function %} {% with function = child %} - {% include function.template or "function.html" with context %} + {% include function|get_template with context %} {% endwith %} {% elif child.is_module and config.show_submodules %} {% with module = child %} - {% include module.template or "module.html" with context %} + {% include module|get_template with context %} {% endwith %} {% endif %}