Source code for vuecore.utils.docs_utils
# vuecore/utils/doc_utils.py
from typing import Type, Dict, Any, get_origin, get_args, Optional, Union
from pydantic import BaseModel, Field
from functools import wraps
import textwrap
import re
[docs]
def get_all_model_fields(model: Type[BaseModel]) -> Dict[str, Field]:
"""
Extract all fields from a Pydantic model, including inherited ones.
Parameters
----------
model : Type[BaseModel]
The Pydantic model class from which to extract fields.
Returns
-------
Dict[str, Any]
A dictionary with field names as keys and field information as values.
"""
fields = {}
# Iterate through the method resolution order (mro) in reverse to start from the base classes
for cls in reversed(model.__mro__):
if issubclass(cls, BaseModel) and hasattr(cls, "model_fields"):
for field_name, field in cls.model_fields.items():
fields[field_name] = field
return fields
[docs]
def get_type_string(annotation: Any) -> str:
"""
Helper to get a clean type string from a type annotation.
Parameters
----------
annotation : Any
The type annotation to process.
Returns
-------
str
A simplified string representation of the type.
"""
origin = get_origin(annotation)
if origin is Union or origin is Optional:
args = get_args(annotation)
non_none_arg = next((arg for arg in args if arg is not type(None)), None)
if non_none_arg:
return get_type_string(non_none_arg)
return "Any"
elif origin is list or annotation is list:
args = get_args(annotation)
if not args:
return "list"
inner_type = ", ".join(arg.__name__ for arg in args)
return f"list of {inner_type}"
elif origin is dict or annotation is dict:
args = get_args(annotation)
if not args:
return "dict"
key_type_str = get_type_string(args[0])
value_type_str = get_type_string(args[1])
return f"Dict[{key_type_str}, {value_type_str}]"
return annotation.__name__
[docs]
def document_pydant_params(model: Type[BaseModel]):
"""
Decorator to add Pydantic model parameters to a function's docstrings.
Parameters
----------
model : Type[BaseModel]
The Pydantic model class whose fields should be documented.
Returns
-------
function
A decorator function that modifies the docstring of the target function.
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
original_doc = wrapper.__doc__ or ""
lines = original_doc.splitlines()
# Find the end of the "Parameters" section
insert_index = -1
# Find the line that marks the end of the Parameters section
# or the beginning of a new section like Returns or Raises
for i, line in enumerate(lines):
if re.match(r"^\s*(Returns|Raises|Examples|See Also)", line):
insert_index = i
break
# If no other section is found, assume the end of the docstring
if insert_index == -1:
insert_index = len(lines)
# Generate the new, detailed parameter list in NumPydocs style
fields = get_all_model_fields(model)
params_doc_lines = []
for field_name, field in fields.items():
type_str = get_type_string(field.annotation)
description = field.description or "No description available."
default_value = field.get_default(call_default_factory=True)
default_str = (
f" (default: ``{default_value}``)"
if default_value is not None and default_value is not ...
else ""
)
# Format as a single line for the NumPy-style bulleted list
params_doc_lines.append(
f"* **{field_name}** ({type_str}) – {description}{default_str}"
)
# Indent all lines in the parameter list correctly
indented_params_lines = textwrap.indent(
"\n".join(params_doc_lines), prefix=" "
).splitlines()
# Create the kwargs documentation as a list of lines
new_kwargs_lines = [
" **kwargs",
" Keyword arguments for plot configuration. These arguments are validated against",
f" the ``{model.__name__}`` Pydantic model and the engine specific parameters.\n",
" The following parameters are supported:\n",
]
# Add all parameter lines
new_kwargs_lines.extend(indented_params_lines)
# Reconstruct the docstring by inserting the new lines
new_doc_lines = lines[:insert_index] + new_kwargs_lines + lines[insert_index:]
wrapper.__doc__ = "\n".join(new_doc_lines)
return wrapper
return decorator