Skip to content

Conversation

jinnovation
Copy link
Contributor

@jinnovation jinnovation commented May 1, 2025

Contributes to #793.

This PR implements quick-and-dirty support for Pydantic-style default values and field descriptions. In other words, the following two classes will render equivalently:

class Foo(pydantic.BaseModel):
    """Foo class documentation."""

    a: int = pydantic.Field(default=1, description="Docstring for a")

class Foo(pydantic.BaseModel):
    """Foo class documentation."""

    a: int = 1
    """Docstring for a"""

Open questions:

  • Should Pydantic support for pdoc be split out into a separate "plugin" package, e.g. pdoc-pydantic or pdoc[pydantic]?
  • Is this the best place for default-value-extraction logic for Pydantic classes to live?

Copy link
Member

@mhils mhils left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Should Pydantic support for pdoc be split out into a separate "plugin" package, e.g. pdoc-pydantic or pdoc[pydantic]?

Too much complexity for now. I think pydantic is popular enough to have support for it builtin for now.

Is this the best place for default-value-extraction logic for Pydantic classes to live?

Pragmatically yes. See my comments below on how we can keep things reasonably clean.

@jinnovation jinnovation marked this pull request as draft May 2, 2025 17:23
@jinnovation

This comment was marked as resolved.

@jinnovation
Copy link
Contributor Author

Two issues related to rendering Pydantic model docs that I propose treating as out-of-scope for this PR:

  • If user's BaseModel subclass does not have its own class-level docstring, we end up rendering the docs for BaseModel itself;
  • We render the docs for model_config, which is typically only useful to implementers of the class and not to users thereof, i.e. the expected audience for the generated docs
image

@jinnovation jinnovation marked this pull request as ready for review October 11, 2025 09:42
@jinnovation jinnovation requested a review from mhils October 11, 2025 19:36
@jinnovation jinnovation requested a review from mhils October 17, 2025 03:08
@jinnovation
Copy link
Contributor Author

@mhils: This is ready for your eyes again!

Copy link
Member

@mhils mhils left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, great progress! 😃

else: # pragma: no cover
pydantic: Optional[ModuleType]
try:
pydantic = import_module("pydantic")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason why plain import pydantic does not work?

Also, let's get rid of the TYPE_CHECKING extra logic and just slap # type: ignore[no-redef] on the pydantic = None line. That should work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went a simpler (possibly) direction for the optional-import stuff. Let me know your thoughts.

pdoc/doc.py Outdated
Comment on lines 329 to 339
_docstring: str | None = None
if self.kind == "class":
_docstring = _pydantic.get_field_docstring(cast(type, self.obj), name)

if _docstring is None:
if self._var_docstrings.get(name):
doc.docstring = self._var_docstrings[name]
if self._func_docstrings.get(name) and not doc.docstring:
doc.docstring = self._func_docstrings[name]
else:
doc.docstring = _docstring
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we simplify like this?

Suggested change
_docstring: str | None = None
if self.kind == "class":
_docstring = _pydantic.get_field_docstring(cast(type, self.obj), name)
if _docstring is None:
if self._var_docstrings.get(name):
doc.docstring = self._var_docstrings[name]
if self._func_docstrings.get(name) and not doc.docstring:
doc.docstring = self._func_docstrings[name]
else:
doc.docstring = _docstring
if doc := _pydantic.get_field_docstring(self.obj, name):
doc.docstring = doc
elif doc := self._var_docstrings.get(name):
doc.docstring = doc
elif doc := self._func_docstrings.get(name) and not doc.docstring:
doc.docstring = doc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got something that I think works. Let me know what you think.

@jinnovation jinnovation requested a review from mhils October 19, 2025 21:32
Copy link
Member

@mhils mhils left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fast revision!

@jinnovation jinnovation requested a review from mhils October 21, 2025 02:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants