Skip to content

Commit ae42356

Browse files
committed
feat: Support inheritance
Issue #157: mkdocstrings/mkdocstrings#157 Discussion mkdocstrings#536: mkdocstrings/mkdocstrings#536
1 parent 8eb459f commit ae42356

File tree

8 files changed

+325
-41
lines changed

8 files changed

+325
-41
lines changed

docs/usage/configuration/members.md

+163
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,169 @@ this_attribute = 0
8989

9090
INFO: **The default behavior (with unspecified `members` or `members: null`) is to use [`filters`][].**
9191

92+
## `inherited_members`
93+
94+
- **:octicons-package-24: Type <code><span data-autorefs-optional="list">list</span>[<span data-autorefs-optional="str">str</span>] |
95+
<span data-autorefs-optional="bool">bool</span></code> :material-equal: `False`{ title="default value" }**
96+
<!-- - **:octicons-project-template-24: Template :material-null:** (N/A) -->
97+
98+
An explicit list of inherited members (for classes) to render.
99+
100+
Inherited members are always fetched from classes that are in the same package
101+
as the currently rendered class. To fetch members inherited from base classes,
102+
themselves coming from external packages, use the [`preload_modules`][preload_modules] option.
103+
For example, if your class inherits from Pydantic's `BaseModel`, and you want to render
104+
`BaseModel`'s methods in your class, use `preload_modules: [pydantic]`.
105+
The `pydantic` package must be available in the current environment.
106+
107+
Passing a falsy value (`no`, `false` in YAML) or an empty list (`[]`)
108+
will tell the Python handler not to render any inherited member.
109+
Passing a truthy value (`yes`, `true` in YAML)
110+
will tell the Python handler to render every inherited member.
111+
112+
When all inherited members are selected with `inherited_members: true`,
113+
it is possible to specify both members and inherited members in the `members` list:
114+
115+
```yaml
116+
inherited_members: true
117+
members:
118+
- inherited_member_a
119+
- inherited_member_b
120+
- member_x
121+
- member_y
122+
```
123+
124+
The alternative is not supported:
125+
126+
```yaml
127+
inherited_members:
128+
- inherited_member_a
129+
- inherited_member_b
130+
members:
131+
- member_x
132+
- member_y
133+
```
134+
135+
...because it would make members ordering ambiguous/unspecified.
136+
137+
You can render inherited members *only* by setting `inherited_members: true`
138+
(or a list of inherited members) and setting `members: false`:
139+
140+
```yaml
141+
inherited_members: true
142+
members: false
143+
```
144+
145+
```yaml
146+
inherited_members:
147+
- inherited_member_a
148+
- inherited_member_b
149+
members: false
150+
```
151+
152+
You can render *all declared members* and all or specific inherited members
153+
by leaving `members` as null (default):
154+
155+
```yaml
156+
inherited_members:
157+
- inherited_member_a
158+
- inherited_member_b
159+
# members: null # (1)
160+
```
161+
162+
1. In this case, only declared members will be subject
163+
to further filtering with [`filters`][filters] and [`docstrings`][show_if_no_docstring].
164+
165+
```yaml
166+
inherited_members: true # (1)
167+
# members: null
168+
```
169+
170+
1. In this case, both declared and inherited members will be subject
171+
to further filtering with [`filters`][filters] and [`docstrings`][show_if_no_docstring].
172+
173+
You can render *all declared members* and all or specific inherited members,
174+
avoiding further filtering with [`filters`][filters] and [`docstrings`][show_if_no_docstring]
175+
by setting `members: true`:
176+
177+
```yaml
178+
inherited_members: true
179+
members: true
180+
```
181+
182+
```yaml
183+
inherited_members:
184+
- inherited_member_a
185+
- inherited_member_b
186+
members: true
187+
```
188+
189+
The general rule is that declared or inherited members specified in lists
190+
are never filtered out.
191+
192+
```yaml title="in mkdocs.yml (global configuration)"
193+
plugins:
194+
- mkdocstrings:
195+
handlers:
196+
python:
197+
options:
198+
inherited_members: false
199+
```
200+
201+
```md title="or in docs/some_page.md (local configuration)"
202+
::: package.module
203+
options:
204+
inherited_members: true
205+
```
206+
207+
```python title="package/module.py"
208+
"""Module docstring."""
209+
210+
class Base:
211+
"""Base class."""
212+
213+
def base(self):
214+
"""Base method."""
215+
216+
217+
class Main(Base):
218+
"""Main class."""
219+
220+
def main(self):
221+
"""Main method."""
222+
```
223+
224+
/// admonition | Preview
225+
type: preview
226+
227+
//// tab | With inherited members
228+
<p>Module docstring.</p>
229+
<h2><code>Base</code></h2>
230+
<p>Base class.</p>
231+
<h3><code>base</code></h3>
232+
<p>Base method.</p>
233+
<h2><code>Main</code></h2>
234+
<p>Main class.</p>
235+
<h3><code>base</code></h3>
236+
<p>Base method.</p>
237+
<h3><code>main</code></h3>
238+
<p>Main method.</p>
239+
////
240+
241+
//// tab | Without inherited members
242+
<p>Module docstring.</p>
243+
<h2><code>Base</code></h2>
244+
<p>Base class.</p>
245+
<h3><code>base</code></h3>
246+
<p>Base method.</p>
247+
<h2><code>Main</code></h2>
248+
<p>Main class.</p>
249+
<h3><code>main</code></h3>
250+
<p>Main method.</p>
251+
////
252+
253+
///
254+
92255
## `members_order`
93256

94257
- **:octicons-package-24: Type [`str`][] :material-equal: `"alphabetical"`{ title="default value" }**

mkdocs.yml

+2
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ plugins:
143143
ignore_init_summary: true
144144
docstring_section_style: list
145145
heading_level: 1
146+
inherited_members: true
146147
merge_init_into_class: true
148+
preload_modules: [mkdocstrings]
147149
separate_signature: true
148150
show_root_heading: true
149151
show_root_full_path: false

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ classifiers = [
3030
]
3131
dependencies = [
3232
"mkdocstrings>=0.20",
33-
"griffe>=0.24",
33+
"griffe>=0.30",
3434
]
3535

3636
[project.urls]

src/mkdocstrings_handlers/python/handler.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ class PythonHandler(BaseHandler):
106106
"members_order": rendering.Order.alphabetical.value,
107107
"docstring_section_style": "table",
108108
"members": None,
109+
"inherited_members": False,
109110
"filters": ["!^_[^_]"],
110111
"annotations_path": "brief",
111112
"preload_modules": None,
@@ -138,7 +139,13 @@ class PythonHandler(BaseHandler):
138139
show_category_heading (bool): When grouped by categories, show a heading for each category. Default: `False`.
139140
140141
Attributes: Members options:
141-
members (list[str] | False | None): An explicit list of members to render. Default: `None`.
142+
inherited_members (list[str] | bool | None): A boolean, or an explicit list of inherited members to render.
143+
If true, select all inherited members, which can then be filtered with `members`.
144+
If false or empty list, do not select any inherited member. Default: `False`.
145+
members (list[str] | bool | None): A boolean, or an explicit list of members to render.
146+
If true, select all members without further filtering.
147+
If false or empty list, do not render members.
148+
If none, select all members and apply further filtering with filters and docstrings. Default: `None`.
142149
members_order (str): The members ordering to use. Options: `alphabetical` - order by the members names,
143150
`source` - order members as they appear in the source file. Default: `"alphabetical"`.
144151
filters (list[str] | None): A list of filters applied to filter objects based on their name.

src/mkdocstrings_handlers/python/rendering.py

+31-12
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,8 @@ def do_filter_objects(
194194
objects_dictionary: dict[str, Object | Alias],
195195
*,
196196
filters: Sequence[tuple[Pattern, bool]] | None = None,
197-
members_list: list[str] | None = None,
197+
members_list: bool | list[str] | None = None,
198+
inherited_members: bool | list[str] = False,
198199
keep_no_docstrings: bool = True,
199200
) -> list[Object | Alias]:
200201
"""Filter a dictionary of objects based on their docstrings.
@@ -207,31 +208,49 @@ def do_filter_objects(
207208
members_list: An optional, explicit list of members to keep.
208209
When given and empty, return an empty list.
209210
When given and not empty, ignore filters and docstrings presence/absence.
211+
inherited_members: Whether to keep inherited members or exclude them.
210212
keep_no_docstrings: Whether to keep objects with no/empty docstrings (recursive check).
211213
212214
Returns:
213215
A list of objects.
214216
"""
215-
# no members
216-
if members_list is False or members_list == []:
217-
return []
218-
219-
objects = list(objects_dictionary.values())
217+
inherited_members_specified = False
218+
if inherited_members is True:
219+
# Include all inherited members.
220+
objects = list(objects_dictionary.values())
221+
elif inherited_members is False:
222+
# Include no inherited members.
223+
objects = [obj for obj in objects_dictionary.values() if not obj.inherited]
224+
else:
225+
# Include specific inherited members.
226+
inherited_members_specified = True
227+
objects = [
228+
obj for obj in objects_dictionary.values() if not obj.inherited or obj.name in set(inherited_members)
229+
]
220230

221-
# all members
222231
if members_list is True:
232+
# Return all pre-selected members.
223233
return objects
224234

225-
# list of members
235+
if members_list is False or members_list == []:
236+
# Return selected inherited members, if any.
237+
return [obj for obj in objects if obj.inherited]
238+
226239
if members_list is not None:
227-
return [obj for obj in objects if obj.name in set(members_list)]
240+
# Return selected members (keeping any pre-selected inherited members).
241+
return [
242+
obj for obj in objects if obj.name in set(members_list) or (inherited_members_specified and obj.inherited)
243+
]
228244

229-
# none, use filters and docstrings
245+
# Use filters and docstrings.
230246
if filters:
231-
objects = [obj for obj in objects if _keep_object(obj.name, filters)]
247+
objects = [
248+
obj for obj in objects if _keep_object(obj.name, filters) or (inherited_members_specified and obj.inherited)
249+
]
232250
if keep_no_docstrings:
233251
return objects
234-
return [obj for obj in objects if obj.has_docstrings]
252+
253+
return [obj for obj in objects if obj.has_docstrings or (inherited_members_specified and obj.inherited)]
235254

236255

237256
@lru_cache(maxsize=1)

0 commit comments

Comments
 (0)