Улучшение cli и удобства создания патчей
Сборка мода / build (push) Successful in 2m16s

This commit is contained in:
2025-12-28 17:47:56 +03:00
parent ec047cd3a5
commit 70337ee3ec
35 changed files with 2200 additions and 1111 deletions
+159
View File
@@ -0,0 +1,159 @@
from typing import Any, get_args, get_origin
from pydantic import BaseModel
from pydantic.fields import FieldInfo
from rich.console import Console
from rich.table import Table
def format_field_type(annotation: Any) -> str:
"""Форматирует тип поля для отображения"""
if annotation is None:
return "None"
origin = get_origin(annotation)
if origin is not None:
args = get_args(annotation)
origin_name = getattr(origin, "__name__", str(origin))
if origin_name == "UnionType" or str(origin) == "typing.Union":
args_str = " | ".join(format_field_type(a) for a in args)
return args_str
if args:
args_str = ", ".join(format_field_type(a) for a in args)
return f"{origin_name}[{args_str}]"
return origin_name
if isinstance(annotation, type) and issubclass(annotation, BaseModel):
return f"[magenta]{annotation.__name__}[/magenta]"
return getattr(annotation, "__name__", str(annotation))
def print_model_fields(
console: Console,
model_class: type[BaseModel],
indent: int = 0,
visited: set | None = None,
) -> None:
"""Рекурсивно выводит поля модели с поддержкой вложенных моделей"""
if visited is None:
visited = set()
if model_class in visited:
console.print(
f"{' ' * indent}[dim](циклическая ссылка на {model_class.__name__})[/dim]"
)
return
visited.add(model_class)
prefix = " " * indent
for field_name, field_info in model_class.model_fields.items():
annotation = field_info.annotation
field_type = format_field_type(annotation)
default = field_info.default
description = field_info.description or ""
if default is None:
default_str = "[dim]None[/dim]"
elif default is ...:
default_str = "[red]required[/red]"
elif isinstance(default, bool):
default_str = "[green]true[/green]" if default else "[red]false[/red]"
else:
default_str = str(default)
console.print(
f"{prefix}[yellow]{field_name}[/yellow]: {field_type} = {default_str}"
+ (f" [dim]# {description}[/dim]" if description else "")
)
nested_model = None
if isinstance(annotation, type) and issubclass(annotation, BaseModel):
nested_model = annotation
else:
origin = get_origin(annotation)
if origin is not None:
for arg in get_args(annotation):
if isinstance(arg, type) and issubclass(arg, BaseModel):
nested_model = arg
break
if nested_model is not None:
console.print(f"{prefix} [dim]└─ {nested_model.__name__}:[/dim]")
print_model_fields(console, nested_model, indent + 2, visited.copy())
def print_model_table(
console: Console,
model_class: type[BaseModel],
prefix: str = "",
visited: set | None = None,
) -> Table:
"""Выводит поля модели в виде таблицы с вложенными моделями"""
if visited is None:
visited = set()
table = Table(show_header=True, box=None if prefix else None)
table.add_column("Поле", style="yellow")
table.add_column("Тип", style="cyan")
table.add_column("По умолчанию")
table.add_column("Описание", style="dim")
_add_model_rows(table, model_class, prefix, visited)
return table
def _add_model_rows(
table: Table,
model_class: type[BaseModel],
prefix: str = "",
visited: set | None = None,
) -> None:
"""Добавляет строки модели в таблицу рекурсивно"""
if visited is None:
visited = set()
if model_class in visited:
return
visited.add(model_class)
for field_name, field_info in model_class.model_fields.items():
annotation = field_info.annotation
field_type = format_field_type(annotation)
default = field_info.default
description = field_info.description or ""
if default is None:
default_str = "-"
elif default is ...:
default_str = "[red]required[/red]"
elif isinstance(default, bool):
default_str = "true" if default else "false"
elif isinstance(default, BaseModel):
default_str = "{...}"
else:
default_str = str(default)[:20]
full_name = f"{prefix}{field_name}" if prefix else field_name
table.add_row(full_name, field_type, default_str, description[:40])
nested_model = None
if isinstance(annotation, type) and issubclass(annotation, BaseModel):
nested_model = annotation
else:
origin = get_origin(annotation)
if origin is not None:
for arg in get_args(annotation):
if isinstance(arg, type) and issubclass(arg, BaseModel):
nested_model = arg
break
if nested_model is not None and nested_model not in visited:
_add_model_rows(table, nested_model, f" {full_name}.", visited.copy())