Skip to content

Commit

Permalink
feat: add model_field_descriptions function to extract field descript…
Browse files Browse the repository at this point in the history
…ions from Pydantic models
  • Loading branch information
thorwhalen committed Nov 19, 2024
1 parent 0728ddf commit c2058c0
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 0 deletions.
1 change: 1 addition & 0 deletions ju/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
data_to_pydantic_model, # data to pydantic model
pydantic_model_to_code, # pydantic model to code
field_paths_and_annotations, # flattened field paths & annotations from model
model_field_descriptions, # Extracts a dictionary of field paths & their descriptions from model.
)
from ju.viz import model_digraph # visualize pydantic models
57 changes: 57 additions & 0 deletions ju/pydantic_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,63 @@ def is_type_hint(obj: Any) -> bool:
return type_hint_classifier.matches(obj)


# -------------------------------------------------------------------------------------
# Get information from Pydantic models (cast/extract)

from typing import Type, Dict, get_origin


def model_field_descriptions(
model: Type[BaseModel],
default_description: str = "No description provided",
*,
prefix: str = "",
) -> Dict[str, str]:
"""
Extracts a dictionary of field paths and their descriptions from a Pydantic model,
including nested models.
Args:
model (Type[BaseModel]): A Pydantic model class.
prefix (str): A prefix for nested fields (used internally during recursion).
Returns:
Dict[str, str]: A dictionary of field paths and descriptions.
Example:
>>> from pydantic import BaseModel, Field
>>> class Address(BaseModel):
... city: str = Field(..., description="City name")
... zipcode: str = Field(..., description="ZIP code")
>>> class User(BaseModel):
... name: str = Field(..., description="The name of the user")
... address: Address
>>> model_field_descriptions(User) # doctest: +NORMALIZE_WHITESPACE
{'name': 'The name of the user',
'address.city': 'City name',
'address.zipcode': 'ZIP code'}
"""
descriptions = {}

for field_name, field_info in model.model_fields.items():
field_type = field_info.annotation
current_path = f"{prefix}.{field_name}" if prefix else field_name

if is_a_basemodel(field_type):
# Recurse for nested models
nested_descriptions = model_field_descriptions(
field_type, prefix=current_path, default_description=default_description
)
descriptions.update(nested_descriptions)
else:
# Access description directly
description = field_info.description or default_description
descriptions[current_path] = description

return descriptions


# -------------------------------------------------------------------------------------
# Construct and validate Pydantic models

Expand Down

0 comments on commit c2058c0

Please sign in to comment.