-
-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bug: mkdocstrings not collecting type information from parent classes #175
Comments
Hi @coltonbh, thanks for the report! I'll mark this as a bug. Currently, when parsing docstrings, we don't check inherited members to obtain the type. I suppose we could do that. You did nothing wrong, it's just the first time I see this use-case and the first time it is reported 🙂 |
Thanks, @pawamoy! I'll extend this just a bit further. Often I have inheritance hierarchies where a base class has attributes shared by all children, and then children are composed of various mixins and their own attributes like this: from typing import Any, Dict, List
from pydantic import BaseModel
class BaseClass(BaseModel):
"""Base class
Attributes:
extras: Dictionary for extra values.
"""
extras: Dict[str, Any] = {}
class _Mixin1(BaseClass):
"""Mixin 1 class
Attributes:
val1: A string value.
"""
val1: str
class _Mixin2(BaseClass):
"""Mixin 2 class
Attributes:
val2: An integer value.
"""
val2: int
class First(BaseClass, _Mixin1):
"""First class
Attributes:
first: A float value.
"""
first: float
class Second(BaseClass, _Mixin2):
"""Second Class
Attributes:
second: A list of integers.
"""
second: List[int] Ideally, I'd like to be able to just declare the type information once (on the parent object) and then have the inherited attributes and their docstrings be included in children classes. So ideally, the code above would produce an Attributes table for from typing import Any, Dict, List
from pydantic import BaseModel
class BaseClass(BaseModel):
"""Base class
Attributes:
extras: Dictionary for extra values.
"""
extras: Dict[str, Any] = {}
class _Mixin1(BaseClass):
"""Mixin 1 class
Attributes:
val1: A string value.
"""
val1: str
class _Mixin2(BaseClass):
"""Mixin 2 class
Attributes:
val2: An integer value.
"""
val2: int
class First(BaseClass, _Mixin1):
"""First class
Attributes:
first: A float value.
val1 (str): A string value.
extras (Dict[str, Any]): Dictionary for extra values.
"""
first: float
class Second(BaseClass, _Mixin2):
"""Second Class
Attributes:
second: A list of integers.
val2 (int): An integer value.
extras (Dict[str, Any]): Dictionary for extra values.
"""
second: List[int] I have to keep repeating the declaration for Ideally I'd love for the types and docstrings to come with inherited classes so I only have to document an attribute once and it will flow through to all children :) As a small follow-on question, is it possible to have class BaseClass(BaseModel):
"""Base class
Attributes:
extras: Dictionary for extra values.
"""
extras: Dict[str, Any] = {}
@property
def computed_val(self) -> int:
"""A computed value"""
return len(self.extras) |
That's already the case, but not for attributes sections as you would expect, see next paragraph.
That won't happen. Docstring sections will by default remain explicit only, always. Griffe itself will never implicitly modify docstring sections: if you document a single item, it won't add other items for you. But this can be achieved with an extension if you really want it! However, consider using actual attribute docstrings, which we recommend instead of Attributes section: from typing import Any, Dict, List
from pydantic import BaseModel
class BaseClass(BaseModel):
"""Base class."""
extras: Dict[str, Any] = {}
"""Dictionary for extra values."""
class _Mixin1(BaseClass):
"""Mixin 1 class."""
val1: str
"""A string value."""
class _Mixin2(BaseClass):
"""Mixin 2 class."""
val2: int
"""An integer value."""
class First(BaseClass, _Mixin1):
"""First class."""
first: float
"""A float value."""
class Second(BaseClass, _Mixin2):
"""Second Class."""
second: List[int]
"""A list of integers.""" The benefit of documenting each attribute instead of writing Attributes sections in parent classes is that each attribute gets a permalink and an entry in the objects inventory generated by mkdocstrings, which means it can be cross-referenced.
If you document each attribute and property, mkdocstrings can auto-generate summary tables (attributes, functions, classes, etc.) for you. That's an insiders feature though: https://mkdocstrings.github.io/python/usage/configuration/members/#summary. If you want to customize your attributes sections anyway, as said before you can write a Griffe extension 🙂 See https://mkdocstrings.github.io/griffe/guide/users/extending/. Let me know what you think. |
Ah! Thank you for the details on this! Did not know about the attribute docstrings. I'm headed out of town for the next week but will play with this when I get back. I'm about to make documentation for a whole suite of packages so I'm keen to get the best practices in place and then roll them through my whole stack :) Thank you! |
You're welcome! Over a few years I've accumulated some issues that I labeled "docs". This will be useful when I finally have some time to write a proper mkdocstring-python guide for writing docstrings 🙂 |
I would love a "best practices guide!" Just a straightforward "here's what we recommend and why," something that works for 90% of the cases. The flexibility of |
Ok back online... Really appreciate the detailed response. Two final questions
from typing import Any, Dict, List
from pydantic import BaseModel
class BaseClass(BaseModel):
"""Base class."""
extras: Dict[str, Any] = {}
"""Dictionary for extra values."""
class _Mixin1(BaseClass):
"""Mixin 1 class."""
val1: str
"""A string value."""
class _Mixin2(BaseClass):
"""Mixin 2 class."""
val2: int
"""An integer value."""
class First(BaseClass, _Mixin1):
"""First class."""
first: float
"""A float value."""
class Second(BaseClass, _Mixin2):
"""Second Class."""
second: List[int]
"""A list of integers.""" The concrete classes Is there a way you recommend for making the documentation show inherited attributes? Currently the google docstring approach seems best to me since I get a nice, explicit attributes table; however, I'm open to the attribute docstrings approach you suggest, especially if I could bring through inherited attributes nicely.
|
Yes, see https://mkdocstrings.github.io/python/usage/configuration/members/#inherited_members.
Good question. Griffe makes a distinction between an class Hello:
"""Hello.
Attributes:
hello: Hello.
"""
hello: str = "Hello"
"""Hello.""" Then mkdocstrings-python treats sections as simple summary tables (or lists, depending on We have a feature request in our backlog to generate headings for items in docstring sections too, see #133. |
Thanks for all the details! I think I'm ready to take on some serious documentation work now ;) |
I think the issue I am having is related to this. I have a set of classes that inherit from parent classes, but don't need to implement the init method in the child class. This is leading to the generated documentation for the child class not having any information (methods, attributes, etc.) at all. As soon as I add an init method to the child class all of the inherited methods, attributes, etc. show up in the documentation. |
@nfelt14 have you tried setting |
I have, here is a snippet of my settings: - mkdocstrings:
enable_inventory: true
custom_templates: docs/_templates/mkdocstrings
handlers:
python: # see https://mkdocstrings.github.io/python/usage/
paths: [src]
options:
# General options
extensions: [docs/griffe_custom_decorator_labels.py]
show_inheritance_diagram: true
show_source: false # a link is included at the top of each page
# Headings options
heading_level: 1
show_root_heading: true
show_root_full_path: false
show_category_heading: false
show_symbol_type_heading: true
show_symbol_type_toc: false
# Members options
inherited_members: true
members_order: alphabetical
filters: ['!^_', ^__init__]
summary: false # Currently implemented with custom templates
show_labels: true
# Docstrings options
docstring_options:
ignore_init_summary: true
trim_doctest_flags: true
docstring_section_style: list
merge_init_into_class: true
# Signature options
line_length: 100
show_signature_annotations: true
signature_crossrefs: true
separate_signature: true If you think that this is a separate issue, I can definitely open a new issue. |
Please open a new issue, yes, and show us what is not working 🙂 |
I created #196 |
…ing docstrings I'm aware that computing inherited members will cache the MRO, but docstrings are usually never parsed *before* a package has finished loading. We do have to emphasize this in our docs though: extensions must not parse docstrings and modify parsed sections, and should rather modify the docstring value directly (even though it can be less convenient). We should also stop caching parsed docstrings and MRO anyway: turns out the cache invalidation problem is really problematic. Issue-mkdocstrings/python#175: mkdocstrings/python#175
Description of the bug
mkdocstrings
does not collect type information from parent classes.To Reproduce
inherited_members: true
The
val1
type information is missing on the documentation:Expected behavior
I would expect
mkdocstrings
to get the type information from the parent class and display it.If I omit the value from the docstring then it is hidden. This makes me think the intention is to hide inherited values? Again, this can't be right because I'd want concrete classes to display mixin values they have inherited. I've been over the docs extensively but can't seem to find a setting that makes this work.
Environment information
mkdocs
v1.6.0mkdocstrings
v0.25.2mkdocstrings-python
v1.10.5griffe
v0.47.0Is this expected behavior? In general I'd like to be able to pull type information from mixin classes directly rather than having to add type annotations to the docstrings. I must be doing something wrong! Thanks for your help :)
The text was updated successfully, but these errors were encountered: