8000 feat: Add members and filters options · diviyank/python@24a6136 · GitHub
[go: up one dir, main page]

Skip to content

Commit 24a6136

Browse files
committed
feat: Add members and filters options
1 parent 8f4c853 commit 24a6136

File tree

8 files changed

+400
-303
lines changed

8 files changed

+400
-303
lines changed

src/mkdocstrings_handlers/python/handler.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import os
66
import posixpath
7+
import re
78
import sys
89
from collections import ChainMap
910
from contextlib import suppress
@@ -75,6 +76,8 @@ class PythonHandler(BaseHandler):
7576
"heading_level": 2,
7677
"members_order": rendering.Order.alphabetical.value,
7778
"docstring_section_style": "table",
79+
"members": None,
80+
"filters": ["!^_[^_]"],
7881
}
7982
"""
8083
Attributes: Default rendering options:
@@ -97,6 +100,9 @@ class PythonHandler(BaseHandler):
97100
heading_level (int): The initial heading level to use. Default: `2`.
98101
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`.
99102
docstring_section_style (str): The style used to render docstring sections. Options: `table`, `list`, `spacy`. Default: `table`.
103+
members (list[str] | False | None): An explicit list of members to render. Default: `None`.
104+
filters (list[str] | None): A list of filters applied to filter objects based on their name.
105 EDBE +
A filter starting with `!` will exclude matching objects instead of including them. Default: `["!^_[^_]"]`.
100106
""" # noqa: E501
101107

102108
def __init__(
@@ -222,6 +228,11 @@ def render(self, data: CollectorItem, config: dict) -> str: # noqa: D102 (ignor
222228
choices = "', '".join(item.value for item in rendering.Order)
223229
raise PluginError(f"Unknown members_order '{final_config['members_order']}', choose between '{choices}'.")
224230

231+
if final_config["filters"]:
232+
final_config["filters"] = [
233+
(re.compile(filtr.lstrip("!")), filtr.startswith("!")) for filtr in final_config["filters"]
234+
]
235+
225236
return template.render(
226237
**{"config": final_config, data.kind.value: data, "heading_level": heading_level, "root": True},
227238
)
@@ -236,7 +247,7 @@ def update_env(self, md: Markdown, config: dict) -> None: # noqa: D102 (ignore
236247
self.env.filters["order_members"] = rendering.do_order_members
237248
self.env.filters["format_code"] = rendering.do_format_code
238249
self.env.filters["format_signature"] = rendering.do_format_signature
239-
self.env.filters["filter_docstrings"] = rendering.do_filter_docstrings
250+
self.env.filters["filter_objects"] = rendering.do_filter_objects
240251

241252
def get_anchors(self, data: CollectorItem) -> list[str]: # noqa: D102 (ignore missing docstring)
242253
try:

src/mkdocstrings_handlers/python/rendering.py

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import re
77
import sys
88
from functools import lru_cache
9-
from typing import Any, Sequence
9+
from typing import Any, Pattern, Sequence
1010

1111
from griffe.dataclasses import Alias, Object
1212
from markupsafe import Markup
@@ -77,16 +77,28 @@ def do_format_signature(signature: str, line_length: int) -> str:
7777
return formatted[4:-5].strip()[:-1]
7878

7979

80-
def do_order_members(members: Sequence[Object | Alias], order: Order) -> Sequence[Object | Alias]:
80+
def do_order_members(
81+
members: Sequence[Object | Alias],
82+
order: Order,
83+
members_list: list[str] | None,
84+
) -> Sequence[Object | Alias]:
8185
"""Order members given an ordering method.
8286
8387
Parameters:
8488
members: The members to order.
8589
order: The ordering method.
90+
members_list: An optional member list (manual ordering).
8691
8792
Returns:
8893
The same members, ordered.
8994
"""
95+
if members_list:
96+
sorted_members = []
97+
members_dict = {member.name: member for member in members}
98+
for name in members_list:
99+
if name in members_dict:
100+
sorted_members.append(members_dict[name])
101+
return sorted_members
90102
return sorted(members, key=order_map[order])
91103

92104

@@ -133,22 +145,53 @@ def repl(match): # noqa: WPS430
133145
return Markup(text).format(**variables)
134146

135147

136-
def do_filter_docstrings(
148+
def _keep_object(name, filters):
149+
keep = None
150+
rules = set()
151+
for regex, exclude in filters:
152+
rules.add(exclude)
153+
if regex.search(name):
154+
keep = not exclude
155+
if keep is None:
156+
if rules == {False}: # noqa: WPS531
157+
# only included stuff, no match = reject
158+
return False
159+
# only excluded stuff, or included and excluded stuff, no match = keep
160+
return True
161+
return keep
162+
163+
164+
def do_filter_objects(
137165
objects_dictionary: dict[str, Object | Alias],
138-
keep_empty: bool = True,
166+
filters: list[tuple[bool, Pattern]] | None = None,
167+
members_list: list[str] | None = None,
168+
keep_no_docstrings: bool = True,
139169
) -> list[Object | Alias]:
140170
"""Filter a dictionary of objects based on their docstrings.
141171
142172
Parameters:
143173
objects_dictionary: The dictionary of objects.
144-
keep_empty: Whether to keep objects with no/empty docstrings (recursive check).
174+
filters: Filters to apply, based on members' names.
175+
Each element is a tuple: a pattern, and a boolean indicating whether
176+
to reject the object if the pattern matches.
177+
members_list: An optional, explicit list of members to keep.
178+
When given and empty, return an empty list.
179+
When given and not empty, ignore filters and docstrings presence/absence.
180+
keep_no_docstrings: Whether to keep objects with no/empty docstrings (recursive check).
145181
146182
Returns:
147183
A list of objects.
148184
"""
149-
if keep_empty:
150-
return list(objects_dictionary.values())
151-
return [obj for obj in objects_dictionary.values() if obj.has_docstrings]
185+
if members_list is not None:
186+
if not members_list:
187+
return []
188+
return [obj for obj in objects_dictionary.values() if obj.name in set(members_list)]
189+
objects = list(objects_dictionary.values())
190+
if filters:
191+
objects = [obj for obj in objects if _keep_object(obj.name, filters)]
192+
if keep_no_docstrings:
193+
return objects
194+
return [obj for obj in objects if obj.has_docstrings]
152195

153196

154197
@lru_cache(maxsize=1)
Lines changed: 53 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,69 @@
11
{{ log.debug("Rendering " + attribute.path) }}
2-
{% if config.show_if_no_docstring or attribute.has_docstrings %}
32

4-
<div class="doc doc-object doc-attribute">
5-
{% with html_id = attribute.path %}
3+
<div class="doc doc-object doc-attribute">
4+
{% with html_id = attribute.path %}
65

7-
{% if not root or config.show_root_heading %}
6+
{% if root %}
7+
{% set show_full_path = config.show_root_full_path %}
8+
{% set root_members = True %}
9+
{% elif root_members %}
10+
{% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %}
11+
{% set root_members = False %}
12+
{% else %}
13+
{% set show_full_path = config.show_object_full_path %}
14+
{% endif %}
815

9-
{% if root %}
10-
{% set show_full_path = config.show_root_full_path %}
11-
{% set root_members = True %}
12-
{% elif root_members %}
13-
{% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %}
14-
{% set root_members = False %}
15-
{% else %}
16-
{% set show_full_path = config.show_object_full_path %}
17-
{% endif %}
18-
19-
{% filter heading(heading_level,
20-
role="data" if attribute.parent.kind.value == "module" else "attr",
21-
id=html_id,
22-
class="doc doc-heading",
23-
toc_label=attribute.name) %}
16+
{% if not root or config.show_root_heading %}
2417

25-
{% if config.separate_signature %}
26-
{% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %}
27-
{% else %}
28-
{% filter highlight(language="python", inline=True) %}
29-
{% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %}
30-
{% if attribute.annotation %}: {{ attribute.annotation }}{% endif %}
31-
{% if attribute.value %} = {{ attribute.value }}{% endif %}
32-
{% endfilter %}
33-
{% endif %}
34-
35-
{% with labels = attribute.labels %}
36-
{% include "labels.html" with context %}
37-
{% endwith %}
38-
39-
{% endfilter %}
18+
{% filter heading(heading_level,
19+
role="data" if attribute.parent.kind.value == "module" else "attr",
20+
id=html_id,
21+
class="doc doc-heading",
22+
toc_label=attribute.name) %}
4023

4124
{% if config.separate_signature %}
42-
{% filter highlight(language="python", inline=False) %}
43-
{% filter format_code(config.line_length) %}
44-
{% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %}
45-
{% if attribute.annotation %}: {{ attribute.annotation|safe }}{% endif %}
46-
{% if attribute.value %} = {{ attribute.value|safe }}{% endif %}
47-
{% endfilter %}
25+
{% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %}
26+
{% else %}
27+
{% filter highlight(language="python", inline=True) %}
28+
{% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %}
29+
{% if attribute.annotation %}: {{ attribute.annotation }}{% endif %}
30+
{% if attribute.value %} = {{ attribute.value }}{% endif %}
4831
{% endfilter %}
4932
{% endif %}
5033

51-
{% else %}
52-
{% if config.show_root_toc_entry %}
53-
{% filter heading(heading_level,
54-
role="data" if attribute.parent.kind.value == "module" else "attr",
55-
id=html_id,
56-
toc_label=attribute.path if config.show_root_full_path else attribute.name,
57-
hidden=True) %}
34+
{% with labels = attribute.labels %}
35+
{% include "labels.html" with context %}
36+
{% endwith %}
37+
38+
{% endfilter %}
39+
40+
{% if config.separate_signature %}
41+
{% filter highlight(language="python", inline=False) %}
42+
{% filter format_code(config.line_length) %}
43+
{% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %}
44+
{% if attribute.annotation %}: {{ attribute.annotation|safe }}{% endif %}
45+
{% if attribute.value %} = {{ attribute.value|safe }}{% endif %}
5846
{% endfilter %}
59-
{% endif %}
60-
{% set heading_level = heading_level - 1 %}
47+
{% endfilter %}
6148
{% endif %}
6249

63-
<div class="doc doc-contents {% if root %}first{% endif %}">
64-
{% with docstring_sections = attribute.docstring.parsed %}
65-
{% include "docstring.html" with context %}
66-
{% endwith %}
67-
</div>
50+
{% else %}
51+
{% if config.show_root_toc_entry %}
52+
{% filter heading(heading_level,
53+
role="data" if attribute.parent.kind.value == "module" else "attr",
54+
id=html_id,
55+
toc_label=attribute.path if config.show_root_full_path else attribute.name,
56+
hidden=True) %}
57+
{% endfilter %}
58+
{% endif %}
59+
{% set heading_level = heading_level - 1 %}
60+
{% endif %}
6861

69-
{% endwith %}
62+
<div class="doc doc-contents {% if root %}first{% endif %}">
63+
{% with docstring_sections = attribute.docstring.parsed %}
64+
{% include "docstring.html" with context %}
65+
{% endwith %}
7066
</div>
7167

72-
{% endif %}
68+
{% endwith %}
69+
</div>

src/mkdocstrings_handlers/python/templates/material/_base/children.html

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33

44
<div class="doc doc-children">
55

6+
{% if root_members %}
7+
{% set members_list = config.members %}
8+
{% else %}
9+
{% set members_list = none %}
10+
{% endif %}
11+
612
{% if config.group_by_category %}
713

814
{% with %}
@@ -13,59 +19,77 @@
1319
{% set extra_level = 0 %}
1420
{% endif %}
1521

16-
{% if config.show_category_heading and obj.attributes|filter_docstrings(config.show_if_no_docstring) %}
17-
{% filter heading(heading_level, id=html_id ~ "-attributes") %}Attributes{% endfilter %}
18-
{% endif %}
19-
{% with heading_level = heading_level + extra_level %}
20-
{% for attribute in obj.attributes.values()|order_members(config.members_order) %}
21-
{% if not attribute.is_alias or attribute.is_explicitely_exported %}
22-
{% include "attribute.html" with context %}
22+
{% with attributes = obj.attributes|filter_objects(config.filters, members_list, config.show_if_no_docstring) %}
23+
{% if attributes %}
24+
{% if config.show_category_heading %}
25+
{% filter heading(heading_level, id=html_id ~ "-attributes") %}Attributes{% endfilter %}
2326
{% endif %}
24-
{% endfor %}
27+
{% with heading_level = heading_level + extra_level %}
28+
{% for attribute in attributes|order_members(config.members_order, members_list) %}
29+
{% if not attribute.is_alias or attribute.is_explicitely_exported %}
30+
{% include "attribute.html" with context %}
31+
{% endif %}
32+
{% endfor %}
33+
{% endwith %}
34+
{% endif %}
2535
{% endwith %}
2636

27-
{% if config.show_category_heading and obj.classes|filter_docstrings(config.show_if_no_docstring) %}
28-
{% filter heading(heading_level, id=html_id ~ "-classes") %}Classes{% endfilter %}
29-
{% endif %}
30-
{% with heading_level = heading_level + extra_level %}
31-
{% for class in obj.classes.values()|order_members(config.members_order) %}
32-
{% if not class.is_alias or class.is_explicitely_exported %}
33-
{% include "class.html" with context %}
37+
{% with classes = obj.classes|filter_objects(config.filters, members_list, config.show_if_no_docstring) %}
38+
{% if classes %}
39+
{% if config.show_category_heading %}
40+
{% filter heading(heading_level, id=html_id ~ "-classes") %}Classes{% endfilter %}
3441
{% endif %}
35-
{% endfor %}
42+
{% with heading_level = heading_level + extra_level %}
43+
{% for class in classes|order_members(config.members_order, members_list) %}
44+
{% if not class.is_alias or class.is_explicitely_exported %}
45+
{% include "class.html" with context %}
46+
{% endif %}
47+
{% endfor %}
48+
{% endwith %}
49+
{% endif %}
3650
{% endwith %}
3751

38-
{% if config.show_category_heading and obj.functions|filter_docstrings(config.show_if_no_docstring) %}
39-
{% filter heading(heading_level, id=html_id ~ "-functions") %}Functions{% endfilter %}
40-
{% endif %}
41-
{% with heading_level = heading_level + extra_level %}
42-
{% for function in obj.functions.values()|order_members(config.members_order) %}
43-
{% if not (obj.kind.value == "class" and function.name == "__init__" and config.merge_init_into_class) %}
44-
{% if not function.is_alias or function.is_explicitely_exported %}
45-
{% include "function.html" with context %}
46-
{% endif %}
52+
{% with functions = obj.functions|filter_objects(config.filters, members_list, config.show_if_no_docstring) %}
53+
{% if functions %}
54+
{% if config.show_category_heading %}
55+
{% filter heading(heading_level, id=html_id ~ "-functions") %}Functions{% endfilter %}
4756
{% endif %}
48-
{% endfor %}
57+
{% with heading_level = heading_level + extra_level %}
58+
{% for function in functions|order_members(config.members_order, members_list) %}
59+
{% if not (obj.kind.value == "class" and function.name == "__init__" and config.merge_init_into_class) %}
60+
{% if not function.is_alias or function.is_explicitely_exported %}
61+
{% include "function.html" with context %}
62+
{% endif %}
63+
{% endif %}
64+
{% endfor %}
65+
{% endwith %}
66+
{% endif %}
4967
{% endwith %}
5068

5169
{% if config.show_submodules %}
52-
{% if config.show_category_heading and obj.modules|filter_docstrings(config.show_if_no_docstring) %}
53-
{% filter heading(heading_level, id=html_id ~ "-modules") %}Modules{% endfilter %}
54-
{% endif %}
55-
{% with heading_level = heading_level + extra_level %}
56-
{% for module in obj.modules.values()|order_members(config.members_order) %}
57-
{% if not module.is_alias or module.is_explicitely_exported %}
58-
{% include "module.html" with context %}
70+
{% with modules = obj.modules|filter_objects(config.filters, members_list, config.show_if_no_docstring) %}
71+
{% if modules %}
72+
{% if config.show_category_heading %}
73+
{% filter heading(heading_level, id=html_id ~ "-modules") %}Modules{% endfilter %}
5974
{% endif %}
60-
{% endfor %}
75+
{% with heading_level = heading_level + extra_level %}
76+
{% for module in modules|order_members(config.members_order, members_list) %}
77+
{% if not module.is_alias or module.is_explicitely_exported %}
78+
{% include "module.html" with context %}
79+
{% endif %}
80+
{% endfor %}
81+
{% endwith %}
82+
{% endif %}
6183
{% endwith %}
6284
{% endif %}
6385

6486
{% endwith %}
6587

6688
{% else %}
6789

68-
{% for child in obj.members.values()|order_members(config.members_order) %}
90+
{% for child in obj.members|
91+
filter_objects(config.filters, members_list, config.show_if_no_docstring)|
92+
order_members(config.members_order, members_list) %}
6993

7094
{% if not (obj.kind.value == "class" and child.name == "__init__" and config.merge_init_into_class) %}
7195

0 commit comments

Comments
 (0)
0