1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
"""Work with Pydantic models."""
from __future__ import annotations
import types
from typing import Any
from typing import TypeAlias
from typing import TypeGuard
try:
import pydantic
except ImportError: # pragma: no cover
pydantic = None # type: ignore
ClassOrModule: TypeAlias = type | types.ModuleType
"""Type alias for the type of `Namespace.obj`."""
IGNORED_FIELDS: frozenset[str] = frozenset(
(["__fields__"] + list(pydantic.BaseModel.__dict__.keys()))
if pydantic is not None
else []
)
"""Fields to ignore when generating docs, e.g. those that emit deprecation
warnings or that are not relevant to users of BaseModel-derived classes."""
def is_pydantic_model(obj: ClassOrModule) -> TypeGuard[pydantic.BaseModel]:
"""Returns whether an object is a Pydantic model."""
if pydantic is None: # pragma: no cover
# classes that subclass pydantic.BaseModel can only be instantiated if pydantic is importable
# => if we cannot import pydantic, the passed object cannot be a subclass of BaseModel.
return False
return isinstance(obj, type) and issubclass(obj, pydantic.BaseModel)
def default_value(parent: ClassOrModule, name: str, obj: Any) -> Any:
"""Determine the default value of obj.
For pydantic BaseModels, extract the default value from field metadata.
For all other objects, return `obj` as-is.
"""
if is_pydantic_model(parent):
pydantic_fields = parent.__pydantic_fields__
return pydantic_fields[name].default if name in pydantic_fields else obj
return obj
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
return None
|