/ src / griffe_pydantic / _internal / dynamic.py
dynamic.py
 1  from __future__ import annotations
 2  
 3  from typing import Any, Callable
 4  
 5  from griffe import (
 6      Attribute,
 7      Class,
 8      Docstring,
 9      Function,
10      Kind,
11      get_logger,
12  )
13  
14  from griffe_pydantic._internal import common
15  
16  _logger = get_logger("griffe_pydantic")
17  
18  
19  def _process_attribute(obj: Any, attr: Attribute, cls: Class, *, processed: set[str]) -> None:
20      """Handle Pydantic fields."""
21      from pydantic.fields import FieldInfo  # noqa: PLC0415
22  
23      if attr.canonical_path in processed:
24          return
25      processed.add(attr.canonical_path)
26      if attr.name == "model_config":
27          cls.extra[common._self_namespace]["config"] = obj
28          return
29  
30      if not isinstance(obj, FieldInfo):
31          return
32  
33      attr.labels = {"pydantic-field"}
34      attr.value = obj.default
35      constraints = {}
36      for constraint in common._field_constraints:
37          if (value := getattr(obj, constraint, None)) is not None:
38              constraints[constraint] = value
39      attr.extra[common._self_namespace]["constraints"] = constraints
40  
41      # Populate docstring from the field's `description` argument.
42      if not attr.docstring and (docstring := obj.description):
43          attr.docstring = Docstring(docstring, parent=attr)
44  
45  
46  def _process_function(obj: Callable, func: Function, cls: Class, *, processed: set[str]) -> None:
47      """Handle Pydantic field validators."""
48      if func.canonical_path in processed:
49          return
50      processed.add(func.canonical_path)
51      if dec_info := getattr(obj, "decorator_info", None):
52          common._process_function(func, cls, dec_info.fields)
53  
54  
55  def _process_class(obj: type, cls: Class, *, processed: set[str], schema: bool = False) -> None:
56      """Detect and prepare Pydantic models."""
57      common._process_class(cls)
58      if schema:
59          cls.extra[common._self_namespace]["schema"] = common._json_schema(obj)
60      for member in cls.all_members.values():
61          kind = member.kind
62          if kind is Kind.ATTRIBUTE:
63              _process_attribute(getattr(obj, member.name), member, cls, processed=processed)  # type: ignore[arg-type]
64          elif kind is Kind.FUNCTION:
65              _process_function(getattr(obj, member.name), member, cls, processed=processed)  # type: ignore[arg-type]