From 24a6136ee6c04a6a49ee74b20e65177868a10ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 28 Apr 2022 19:06:41 +0200 Subject: [PATCH] feat: Add members and filters options --- src/mkdocstrings_handlers/python/handler.py | 13 +- src/mkdocstrings_handlers/python/rendering.py | 59 +++++- .../templates/material/_base/attribute.html | 109 +++++------ .../templates/material/_base/children.html | 94 +++++---- .../templates/material/_base/class.html | 181 +++++++++--------- .../templates/material/_base/function.html | 117 ++++++----- .../templates/material/_base/module.html | 99 +++++----- tests/test_rendering.py | 31 +++ 8 files changed, 400 insertions(+), 303 deletions(-) diff --git a/src/mkdocstrings_handlers/python/handler.py b/src/mkdocstrings_handlers/python/handler.py index 029769c..66dd184 100644 --- a/src/mkdocstrings_handlers/python/handler.py +++ b/src/mkdocstrings_handlers/python/handler.py @@ -4,6 +4,7 @@ import os import posixpath +import re import sys from collections import ChainMap from contextlib import suppress @@ -75,6 +76,8 @@ class PythonHandler(BaseHandler): "heading_level": 2, "members_order": rendering.Order.alphabetical.value, "docstring_section_style": "table", + "members": None, + "filters": ["!^_[^_]"], } """ Attributes: Default rendering options: @@ -97,6 +100,9 @@ class PythonHandler(BaseHandler): heading_level (int): The initial heading level to use. Default: `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. Default: `alphabetical`. docstring_section_style (str): The style used to render docstring sections. Options: `table`, `list`, `spacy`. Default: `table`. + members (list[str] | False | None): An explicit list of members to render. Default: `None`. + filters (list[str] | None): A list of filters applied to filter objects based on their name. + A filter starting with `!` will exclude matching objects instead of including them. Default: `["!^_[^_]"]`. """ # noqa: E501 def __init__( @@ -222,6 +228,11 @@ def render(self, data: CollectorItem, config: dict) -> str: # noqa: D102 (ignor choices = "', '".join(item.value for item in rendering.Order) raise PluginError(f"Unknown members_order '{final_config['members_order']}', choose between '{choices}'.") + if final_config["filters"]: + final_config["filters"] = [ + (re.compile(filtr.lstrip("!")), filtr.startswith("!")) for filtr in final_config["filters"] + ] + return template.render( **{"config": final_config, data.kind.value: data, "heading_level": heading_level, "root": True}, ) @@ -236,7 +247,7 @@ def update_env(self, md: Markdown, config: dict) -> None: # noqa: D102 (ignore self.env.filters["order_members"] = rendering.do_order_members self.env.filters["format_code"] = rendering.do_format_code self.env.filters["format_signature"] = rendering.do_format_signature - self.env.filters["filter_docstrings"] = rendering.do_filter_docstrings + self.env.filters["filter_objects"] = rendering.do_filter_objects def get_anchors(self, data: CollectorItem) -> list[str]: # noqa: D102 (ignore missing docstring) try: diff --git a/src/mkdocstrings_handlers/python/rendering.py b/src/mkdocstrings_handlers/python/rendering.py index e920a07..8e5f7d8 100644 --- a/src/mkdocstrings_handlers/python/rendering.py +++ b/src/mkdocstrings_handlers/python/rendering.py @@ -6,7 +6,7 @@ import re import sys from functools import lru_cache -from typing import Any, Sequence +from typing import Any, Pattern, Sequence from griffe.dataclasses import Alias, Object from markupsafe import Markup @@ -77,16 +77,28 @@ def do_format_signature(signature: str, line_length: int) -> str: return formatted[4:-5].strip()[:-1] -def do_order_members(members: Sequence[Object | Alias], order: Order) -> Sequence[Object | Alias]: +def do_order_members( + members: Sequence[Object | Alias], + order: Order, + members_list: list[str] | None, +) -> Sequence[Object | Alias]: """Order members given an ordering method. Parameters: members: The members to order. order: The ordering method. + members_list: An optional member list (manual ordering). Returns: The same members, ordered. """ + if members_list: + sorted_members = [] + members_dict = {member.name: member for member in members} + for name in members_list: + if name in members_dict: + sorted_members.append(members_dict[name]) + return sorted_members return sorted(members, key=order_map[order]) @@ -133,22 +145,53 @@ def repl(match): # noqa: WPS430 return Markup(text).format(**variables) -def do_filter_docstrings( +def _keep_object(name, filters): + keep = None + rules = set() + for regex, exclude in filters: + rules.add(exclude) + if regex.search(name): + keep = not exclude + if keep is None: + if rules == {False}: # noqa: WPS531 + # only included stuff, no match = reject + return False + # only excluded stuff, or included and excluded stuff, no match = keep + return True + return keep + + +def do_filter_objects( objects_dictionary: dict[str, Object | Alias], - keep_empty: bool = True, + filters: list[tuple[bool, Pattern]] | None = None, + members_list: list[str] | None = None, + keep_no_docstrings: bool = True, ) -> list[Object | Alias]: """Filter a dictionary of objects based on their docstrings. Parameters: objects_dictionary: The dictionary of objects. - keep_empty: Whether to keep objects with no/empty docstrings (recursive check). + filters: Filters to apply, based on members' names. + Each element is a tuple: a pattern, and a boolean indicating whether + to reject the object if the pattern matches. + members_list: An optional, explicit list of members to keep. + When given and empty, return an empty list. + When given and not empty, ignore filters and docstrings presence/absence. + keep_no_docstrings: Whether to keep objects with no/empty docstrings (recursive check). Returns: A list of objects. """ - if keep_empty: - return list(objects_dictionary.values()) - return [obj for obj in objects_dictionary.values() if obj.has_docstrings] + if members_list is not None: + if not members_list: + return [] + return [obj for obj in objects_dictionary.values() if obj.name in set(members_list)] + objects = list(objects_dictionary.values()) + if filters: + objects = [obj for obj in objects if _keep_object(obj.name, filters)] + if keep_no_docstrings: + return objects + return [obj for obj in objects if obj.has_docstrings] @lru_cache(maxsize=1) diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/attribute.html b/src/mkdocstrings_handlers/python/templates/material/_base/attribute.html index 684e912..779b958 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/attribute.html +++ b/src/mkdocstrings_handlers/python/templates/material/_base/attribute.html @@ -1,72 +1,69 @@ {{ log.debug("Rendering " + attribute.path) }} -{% if config.show_if_no_docstring or attribute.has_docstrings %} -
- {% with html_id = attribute.path %} +
+{% 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 %} - {% 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 attribute.parent.kind.value == "module" else "attr", - id=html_id, - class="doc doc-heading", - toc_label=attribute.name) %} + {% if not root or config.show_root_heading %} - {% if config.separate_signature %} - {% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %} - {% else %} - {% filter highlight(language="python", inline=True) %} - {% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %} - {% if attribute.annotation %}: {{ attribute.annotation }}{% endif %} - {% if attribute.value %} = {{ attribute.value }}{% endif %} - {% endfilter %} - {% endif %} - - {% with labels = attribute.labels %} - {% include "labels.html" with context %} - {% endwith %} - - {% endfilter %} + {% filter heading(heading_level, + role="data" if attribute.parent.kind.value == "module" else "attr", + id=html_id, + class="doc doc-heading", + toc_label=attribute.name) %} {% if config.separate_signature %} - {% filter highlight(language="python", inline=False) %} - {% filter format_code(config.line_length) %} - {% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %} - {% if attribute.annotation %}: {{ attribute.annotation|safe }}{% endif %} - {% if attribute.value %} = {{ attribute.value|safe }}{% endif %} - {% endfilter %} + {% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %} + {% else %} + {% filter highlight(language="python", inline=True) %} + {% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %} + {% if attribute.annotation %}: {{ attribute.annotation }}{% endif %} + {% if attribute.value %} = {{ attribute.value }}{% endif %} {% endfilter %} {% endif %} - {% else %} - {% if config.show_root_toc_entry %} - {% filter heading(heading_level, - role="data" if attribute.parent.kind.value == "module" else "attr", - id=html_id, - toc_label=attribute.path if config.show_root_full_path else attribute.name, - hidden=True) %} + {% with labels = attribute.labels %} + {% include "labels.html" with context %} + {% endwith %} + + {% endfilter %} + + {% if config.separate_signature %} + {% filter highlight(language="python", inline=False) %} + {% filter format_code(config.line_length) %} + {% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %} + {% if attribute.annotation %}: {{ attribute.annotation|safe }}{% endif %} + {% if attribute.value %} = {{ attribute.value|safe }}{% endif %} {% endfilter %} - {% endif %} - {% set heading_level = heading_level - 1 %} + {% endfilter %} {% endif %} -
- {% with docstring_sections = attribute.docstring.parsed %} - {% include "docstring.html" with context %} - {% endwith %} -
+ {% else %} + {% if config.show_root_toc_entry %} + {% filter heading(heading_level, + role="data" if attribute.parent.kind.value == "module" else "attr", + id=html_id, + toc_label=attribute.path if config.show_root_full_path else attribute.name, + hidden=True) %} + {% endfilter %} + {% endif %} + {% set heading_level = heading_level - 1 %} + {% endif %} - {% endwith %} +
+ {% with docstring_sections = attribute.docstring.parsed %} + {% include "docstring.html" with context %} + {% endwith %}
-{% endif %} +{% endwith %} +
diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/children.html b/src/mkdocstrings_handlers/python/templates/material/_base/children.html index d6fc61d..eac782c 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/children.html +++ b/src/mkdocstrings_handlers/python/templates/material/_base/children.html @@ -3,6 +3,12 @@
+ {% if root_members %} + {% set members_list = config.members %} + {% else %} + {% set members_list = none %} + {% endif %} + {% if config.group_by_category %} {% with %} @@ -13,51 +19,67 @@ {% set extra_level = 0 %} {% endif %} - {% if config.show_category_heading and obj.attributes|filter_docstrings(config.show_if_no_docstring) %} - {% filter heading(heading_level, id=html_id ~ "-attributes") %}Attributes{% endfilter %} - {% endif %} - {% with heading_level = heading_level + extra_level %} - {% for attribute in obj.attributes.values()|order_members(config.members_order) %} - {% if not attribute.is_alias or attribute.is_explicitely_exported %} - {% include "attribute.html" with context %} + {% with attributes = obj.attributes|filter_objects(config.filters, members_list, config.show_if_no_docstring) %} + {% if attributes %} + {% if config.show_category_heading %} + {% filter heading(heading_level, id=html_id ~ "-attributes") %}Attributes{% endfilter %} {% endif %} - {% endfor %} + {% 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 %} + {% endif %} + {% endfor %} + {% endwith %} + {% endif %} {% endwith %} - {% if config.show_category_heading and obj.classes|filter_docstrings(config.show_if_no_docstring) %} - {% filter heading(heading_level, id=html_id ~ "-classes") %}Classes{% endfilter %} - {% endif %} - {% with heading_level = heading_level + extra_level %} - {% for class in obj.classes.values()|order_members(config.members_order) %} - {% if not class.is_alias or class.is_explicitely_exported %} - {% include "class.html" with context %} + {% with classes = obj.classes|filter_objects(config.filters, members_list, config.show_if_no_docstring) %} + {% if classes %} + {% if config.show_category_heading %} + {% filter heading(heading_level, id=html_id ~ "-classes") %}Classes{% endfilter %} {% endif %} - {% endfor %} + {% 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 %} + {% endif %} + {% endfor %} + {% endwith %} + {% endif %} {% endwith %} - {% if config.show_category_heading and obj.functions|filter_docstrings(config.show_if_no_docstring) %} - {% filter heading(heading_level, id=html_id ~ "-functions") %}Functions{% endfilter %} - {% endif %} - {% with heading_level = heading_level + extra_level %} - {% for function in obj.functions.values()|order_members(config.members_order) %} - {% 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 %} - {% endif %} + {% with functions = obj.functions|filter_objects(config.filters, members_list, config.show_if_no_docstring) %} + {% if functions %} + {% if config.show_category_heading %} + {% filter heading(heading_level, id=html_id ~ "-functions") %}Functions{% endfilter %} {% endif %} - {% endfor %} + {% with heading_level = heading_level + extra_level %} + {% 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 %} + {% endif %} + {% endif %} + {% endfor %} + {% endwith %} + {% endif %} {% endwith %} {% if config.show_submodules %} - {% if config.show_category_heading and obj.modules|filter_docstrings(config.show_if_no_docstring) %} - {% filter heading(heading_level, id=html_id ~ "-modules") %}Modules{% endfilter %} - {% endif %} - {% with heading_level = heading_level + extra_level %} - {% for module in obj.modules.values()|order_members(config.members_order) %} - {% if not module.is_alias or module.is_explicitely_exported %} - {% include "module.html" with context %} + {% with modules = obj.modules|filter_objects(config.filters, members_list, config.show_if_no_docstring) %} + {% if modules %} + {% if config.show_category_heading %} + {% filter heading(heading_level, id=html_id ~ "-modules") %}Modules{% endfilter %} {% endif %} - {% endfor %} + {% 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 %} + {% endif %} + {% endfor %} + {% endwith %} + {% endif %} {% endwith %} {% endif %} @@ -65,7 +87,9 @@ {% else %} - {% for child in obj.members.values()|order_members(config.members_order) %} + {% for child in obj.members| + filter_objects(config.filters, members_list, 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) %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/class.html b/src/mkdocstrings_handlers/python/templates/material/_base/class.html index 30370e3..580cf70 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/class.html +++ b/src/mkdocstrings_handlers/python/templates/material/_base/class.html @@ -1,116 +1,113 @@ {{ log.debug("Rendering " + class.path) }} -{% if config.show_if_no_docstring or class.has_docstrings %} -
- {% 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 %} +
+{% with html_id = class.path %} + + {% 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 %} + + {% if not root or config.show_root_heading %} + + {% filter heading(heading_level, + role="class", + id=html_id, + class="doc doc-heading", + toc_label=class.name) %} + + {% if config.separate_signature %} + {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} + {% elif config.merge_init_into_class and "__init__" in class.members -%} + {%- with function = class.members["__init__"] -%} + {%- filter highlight(language="python", inline=True) -%} + {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} + {%- include "signature.html" with context -%} + {%- endfilter -%} + {%- endwith -%} {% else %} - {% set show_full_path = config.show_object_full_path %} + {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} {% endif %} - {% filter heading(heading_level, - role="class", - id=html_id, - class="doc doc-heading", - toc_label=class.name) %} + {% with labels = class.labels %} + {% include "labels.html" with context %} + {% endwith %} - {% if config.separate_signature %} - {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} - {% elif config.merge_init_into_class and "__init__" in class.members -%} - {%- with function = class.members["__init__"] -%} - {%- filter highlight(language="python", inline=True) -%} - {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} - {%- include "signature.html" with context -%} - {%- endfilter -%} - {%- endwith -%} - {% else %} - {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} - {% endif %} + {% endfilter %} - {% with labels = class.labels %} - {% include "labels.html" with context %} + {% if config.separate_signature and config.merge_init_into_class %} + {% if "__init__" in class.members %} + {% with function = class.members["__init__"] %} + {% filter highlight(language="python", inline=False) %} + {% filter format_signature(config.line_length) %} + {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} + {% include "signature.html" with context %} + {% endfilter %} + {% endfilter %} {% endwith %} + {% endif %} + {% endif %} + {% else %} + {% if config.show_root_toc_entry %} + {% filter heading(heading_level, + role="class", + id=html_id, + toc_label=class.path if config.show_root_full_path else class.name, + hidden=True) %} {% endfilter %} + {% endif %} + {% set heading_level = heading_level - 1 %} + {% endif %} + +
+ {% if config.show_bases and class.bases %} +

+ Bases: {% for expression in class.bases -%} + {% include "expression.html" with context %}{% if not loop.last %}, {% endif %} + {% endfor -%} +

+ {% endif %} - {% if config.separate_signature and config.merge_init_into_class %} - {% if "__init__" in class.members %} - {% with function = class.members["__init__"] %} - {% filter highlight(language="python", inline=False) %} - {% filter format_signature(config.line_length) %} - {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} - {% include "signature.html" with context %} - {% endfilter %} - {% endfilter %} - {% endwith %} - {% endif %} - {% endif %} + {% with docstring_sections = class.docstring.parsed %} + {% include "docstring.html" with context %} + {% endwith %} - {% else %} - {% if config.show_root_toc_entry %} - {% filter heading(heading_level, - role="class", - id=html_id, - toc_label=class.path if config.show_root_full_path else class.name, - hidden=True) %} - {% endfilter %} + {% if config.merge_init_into_class %} + {% if "__init__" in class.members and class.members["__init__"].has_docstring %} + {% with docstring_sections = class.members["__init__"].docstring.parsed %} + {% include "docstring.html" with context %} + {% endwith %} {% endif %} - {% set heading_level = heading_level - 1 %} {% endif %} -
- {% if config.show_bases and class.bases %} -

- Bases: {% for expression in class.bases -%} - {% include "expression.html" with context %}{% if not loop.last %}, {% endif %} - {% endfor -%} -

- {% endif %} - - {% with docstring_sections = class.docstring.parsed %} - {% include "docstring.html" with context %} - {% endwith %} - + {% if config.show_source %} {% if config.merge_init_into_class %} - {% if "__init__" in class.members and class.members["__init__"].has_docstring %} - {% with docstring_sections = class.members["__init__"].docstring.parsed %} - {% include "docstring.html" with context %} - {% endwith %} - {% endif %} - {% endif %} - - {% if config.show_source %} - {% if config.merge_init_into_class %} - {% if "__init__" in class.members and class.members["__init__"].source %} -
- Source code in {{ class.relative_filepath }} - {{ class.members["__init__"].source|highlight(language="python", linestart=class.members["__init__"].lineno, linenums=True) }} -
- {% endif %} - {% elif class.source %} + {% if "__init__" in class.members and class.members["__init__"].source %}
Source code in {{ class.relative_filepath }} - {{ class.source|highlight(language="python", linestart=class.lineno, linenums=True) }} + {{ class.members["__init__"].source|highlight(language="python", linestart=class.members["__init__"].lineno, linenums=True) }}
{% endif %} + {% elif class.source %} +
+ Source code in {{ class.relative_filepath }} + {{ class.source|highlight(language="python", linestart=class.lineno, linenums=True) }} +
{% endif %} + {% endif %} - {% with obj = class %} - {% set root = False %} - {% set heading_level = heading_level + 1 %} - {% include "children.html" with context %} - {% endwith %} -
- - {% endwith %} + {% with obj = class %} + {% set root = False %} + {% set heading_level = heading_level + 1 %} + {% include "children.html" with context %} + {% endwith %}
-{% endif %} +{% endwith %} +
diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/function.html b/src/mkdocstrings_handlers/python/templates/material/_base/function.html index 7c52b37..9d5288b 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/function.html +++ b/src/mkdocstrings_handlers/python/templates/material/_base/function.html @@ -1,77 +1,74 @@ {{ log.debug("Rendering " + function.path) }} -{% if config.show_if_no_docstring or function.has_docstrings %} -
- {% with html_id = function.path %} +
+{% 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 %} - {% 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 %} + {% if not root or config.show_root_heading %} - {% filter heading(heading_level, - role="function", - id=html_id, - class="doc doc-heading", - toc_label=function.name ~ "()") %} - - {% if config.separate_signature %} - {% if show_full_path %}{{ function.path }}{% else %}{{ function.name }}{% endif %} - {% else %} - {% filter highlight(language="python", inline=True) %} - {% if show_full_path %}{{ function.path }}{% else %}{{ function.name }}{% endif %} - {% include "signature.html" with context %} - {% endfilter %} - {% endif %} - - {% with labels = function.labels %} - {% include "labels.html" with context %} - {% endwith %} - - {% endfilter %} + {% filter heading(heading_level, + role="function", + id=html_id, + class="doc doc-heading", + toc_label=function.name ~ "()") %} {% if config.separate_signature %} - {% filter highlight(language="python", inline=False) %} - {% filter format_signature(config.line_length) %} - {% if show_full_path %}{{ function.path }}{% else %}{{ function.name }}{% endif %} - {% include "signature.html" with context %} - {% endfilter %} + {% if show_full_path %}{{ function.path }}{% else %}{{ function.name }}{% endif %} + {% else %} + {% filter highlight(language="python", inline=True) %} + {% if show_full_path %}{{ function.path }}{% else %}{{ function.name }}{% endif %} + {% include "signature.html" with context %} {% endfilter %} {% endif %} - {% else %} - {% if config.show_root_toc_entry %} - {% filter heading(heading_level, - role="function", - id=html_id, - toc_label=function.path if config.show_root_full_path else function.name, - hidden=True) %} + {% with labels = function.labels %} + {% include "labels.html" with context %} + {% endwith %} + + {% endfilter %} + + {% if config.separate_signature %} + {% filter highlight(language="python", inline=False) %} + {% filter format_signature(config.line_length) %} + {% if show_full_path %}{{ function.path }}{% else %}{{ function.name }}{% endif %} + {% include "signature.html" with context %} {% endfilter %} - {% endif %} - {% set heading_level = heading_level - 1 %} + {% endfilter %} {% endif %} -
- {% with docstring_sections = function.docstring.parsed %} - {% include "docstring.html" with context %} - {% endwith %} + {% else %} + {% if config.show_root_toc_entry %} + {% filter heading(heading_level, + role="function", + id=html_id, + toc_label=function.path if config.show_root_full_path else function.name, + hidden=True) %} + {% endfilter %} + {% endif %} + {% set heading_level = heading_level - 1 %} + {% endif %} - {% if config.show_source and function.source %} -
- Source code in {{ function.relative_filepath }} - {{ function.source|highlight(language="python", linestart=function.lineno, linenums=True) }} -
- {% endif %} -
+
+ {% with docstring_sections = function.docstring.parsed %} + {% include "docstring.html" with context %} + {% endwith %} - {% endwith %} + {% if config.show_source and function.source %} +
+ Source code in {{ function.relative_filepath }} + {{ function.source|highlight(language="python", linestart=function.lineno, linenums=True) }} +
+ {% endif %}
-{% endif %} +{% endwith %} +
diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/module.html b/src/mkdocstrings_handlers/python/templates/material/_base/module.html index baa5eeb..cd5d39b 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/module.html +++ b/src/mkdocstrings_handlers/python/templates/material/_base/module.html @@ -1,62 +1,59 @@ {{ log.debug("Rendering " + module.path) }} -{% if config.show_if_no_docstring or module.has_docstrings %} -
- {% with html_id = module.path %} - - {% if not root or config.show_root_heading %} +
+{% with html_id = module.path %} + + {% 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 %} + + {% if not root or config.show_root_heading %} + + {% filter heading(heading_level, + role="module", + id=html_id, + class="doc doc-heading", + toc_label=module.name) %} + + {% if not config.separate_signature %}{% endif %} + {% if show_full_path %}{{ module.path }}{% else %}{{ module.name }}{% endif %} + {% if not config.separate_signature %}{% endif %} + + {% with labels = module.labels %} + {% include "labels.html" with context %} + {% endwith %} - {% 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 %} + {% endfilter %} + {% else %} + {% if config.show_root_toc_entry %} {% filter heading(heading_level, role="module", id=html_id, - class="doc doc-heading", - toc_label=module.name) %} - - {% if not config.separate_signature %}{% endif %} - {% if show_full_path %}{{ module.path }}{% else %}{{ module.name }}{% endif %} - {% if not config.separate_signature %}{% endif %} - - {% with labels = module.labels %} - {% include "labels.html" with context %} - {% endwith %} - + toc_label=module.path if config.show_root_full_path else module.name, + hidden=True) %} {% endfilter %} - - {% else %} - {% if config.show_root_toc_entry %} - {% filter heading(heading_level, - role="module", - id=html_id, - toc_label=module.path if config.show_root_full_path else module.name, - hidden=True) %} - {% endfilter %} - {% endif %} - {% set heading_level = heading_level - 1 %} {% endif %} - -
- {% with docstring_sections = module.docstring.parsed %} - {% 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 %} + {% set heading_level = heading_level - 1 %} + {% endif %} + +
+ {% with docstring_sections = module.docstring.parsed %} + {% include "docstring.html" with context %} + {% endwith %} + + {% with obj = module %} + {% set root = False %} + {% set heading_level = heading_level + 1 %} + {% include "children.html" with context %} + {% endwith %}
-{% endif %} +{% endwith %} +
diff --git a/tests/test_rendering.py b/tests/test_rendering.py index 3b59505..533eaf3 100644 --- a/tests/test_rendering.py +++ b/tests/test_rendering.py @@ -1,5 +1,10 @@ """Tests for the `rendering` module.""" +import re +from dataclasses import dataclass + +import pytest + from mkdocstrings_handlers.python import rendering @@ -9,3 +14,29 @@ def test_format_code_and_signature(): assert rendering.do_format_code('print("Hello")', 100) assert rendering.do_format_signature("(param: str = 'hello') -> 'Class'", 100) assert rendering.do_format_signature('(param: str = "hello") -> "Class"', 100) + + +@dataclass +class _FakeObject: + name: str + + +@pytest.mark.parametrize( + ("names", "filter_params", "expected_names"), + [ + (["aa", "ab", "ac", "da"], {"filters": [(re.compile("^a[^b]"), True)]}, {"ab", "da"}), + (["aa", "ab", "ac", "da"], {"members_list": ["aa", "ab"]}, {"aa", "ab"}), + ], +) +def test_filter_objects(names, filter_params, expected_names): + """Assert the objects filter works correctly. + + Parameters: + names: Names of the objects. + filter_params: Parameters passed to the filter function. + expected_names: Names expected to be kept. + """ + objects = {name: _FakeObject(name) for name in names} + filtered = rendering.do_filter_objects(objects, **filter_params) + filtered_names = {obj.name for obj in filtered} + assert set(filtered_names) == set(expected_names)