Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

## Unreleased: pdoc next

- Support Pydantic [`computed_field`](https://docs.pydantic.dev/2.0/usage/computed_fields/) descriptions ([#855](https://github.com/mitmproxy/pdoc/pull/855), @avhz)

## 2025-10-27: pdoc 16.0.0

Expand Down
9 changes: 7 additions & 2 deletions pdoc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,11 @@ class GoldenRetriever(Dog):
For [Pydantic models](https://docs.pydantic.dev/latest/concepts/models/), pdoc
will extract [field](https://docs.pydantic.dev/latest/concepts/fields/)
descriptions and treat them just like [documented
variables](#document-variables). For example, the following two Pydantic models
variables](#document-variables). For example, the following Pydantic models
would have identical pdoc-rendered documentation:

```python
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, computed_field

class Foo(BaseModel):
a: int = Field(description="Docs for field a.")
Expand All @@ -278,6 +278,11 @@ class OtherFoo(BaseModel):
a: int
"""Docs for field a."""

class ComputedFoo(BaseModel):
@computed_field(description="Docs for field a.")
@property
def a(self) -> int:
...
```

## ...render math formulas?
Expand Down
2 changes: 2 additions & 0 deletions pdoc/_pydantic.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,6 @@ def get_field_docstring(parent: ClassOrModule, field_name: str) -> str | None:
if is_pydantic_model(parent):
if field := parent.__pydantic_fields__.get(field_name, None):
return field.description
if computed := parent.__pydantic_computed_fields__.get(field_name, None):
return computed.description
return None
14 changes: 14 additions & 0 deletions test/test__pydantic.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from functools import cached_property

import pydantic

from pdoc import _pydantic
Expand All @@ -16,10 +18,22 @@ class ExampleModel(pydantic.BaseModel):
id: int
name: str = pydantic.Field(description="desc", default="Jane Doe")

@pydantic.computed_field(description="computed") # type: ignore[misc]
@property
def computed(self) -> str:
return "computed_value"

@pydantic.computed_field(description="cached") # type: ignore[misc]
@cached_property
def cached(self) -> str:
return "computed_value"


def test_with_pydantic(monkeypatch):
assert _pydantic.is_pydantic_model(ExampleModel)
assert _pydantic.get_field_docstring(ExampleModel, "name") == "desc"
assert _pydantic.get_field_docstring(ExampleModel, "computed") == "computed"
assert _pydantic.get_field_docstring(ExampleModel, "cached") == "cached"
assert _pydantic.default_value(ExampleModel, "name", None) == "Jane Doe"

assert not _pydantic.is_pydantic_model(pdoc.doc.Module)
Expand Down