Skip to content

Commit 1f8e3fc

Browse files
committed
refactor: simplify introspection inspectors without changing output
- method_inspector: prevent redundant "*" insertion in _format_signature instead of inserting then stripping it post-hoc - pydantic_inspector: merge format_model_text/_format_model_text wrapper into a single function with defaulted parameters - pydantic_inspector: replace string-level bracket-matching hack for Annotated[X, Discriminator] with proper type-level unwrapping - pydantic_inspector: condense union handling in _extract_nested_basemodel
1 parent b9ce2a6 commit 1f8e3fc

3 files changed

Lines changed: 21 additions & 49 deletions

File tree

packages/data-designer/src/data_designer/cli/services/introspection/method_inspector.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,13 @@ def _format_signature(method_name: str, sig: inspect.Signature) -> str:
109109
"""Format a method signature as a readable string, skipping 'self'."""
110110
params: list[str] = []
111111
seen_keyword_only = False
112+
has_var_positional = any(p.kind == inspect.Parameter.VAR_POSITIONAL for p in sig.parameters.values())
112113

113114
for param in sig.parameters.values():
114115
if param.name == "self":
115116
continue
116117

117-
if param.kind == inspect.Parameter.KEYWORD_ONLY and not seen_keyword_only:
118+
if param.kind == inspect.Parameter.KEYWORD_ONLY and not seen_keyword_only and not has_var_positional:
118119
seen_keyword_only = True
119120
params.append("*")
120121

@@ -135,12 +136,6 @@ def _format_signature(method_name: str, sig: inspect.Signature) -> str:
135136
return_type = _format_return_type(sig)
136137
params_str = ", ".join(params)
137138

138-
# Remove the extra "*, " if a *args was already present
139-
if any(p.kind == inspect.Parameter.VAR_POSITIONAL for p in sig.parameters.values()):
140-
parts = params_str.split(", ")
141-
parts = [p for p in parts if p != "*"]
142-
params_str = ", ".join(parts)
143-
144139
return f"{method_name}({params_str}) -> {return_type}"
145140

146141

packages/data-designer/src/data_designer/cli/services/introspection/pydantic_inspector.py

Lines changed: 19 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -88,23 +88,30 @@ def _extract_nested_basemodel(annotation: Any) -> type | None:
8888
# Union: X | None, list[X] | None, or discriminated unions
8989
if origin is typing.Union or origin is types.UnionType:
9090
non_none_args = [a for a in get_args(annotation) if a is not type(None)]
91-
basemodel_classes: list[type] = []
92-
for arg in non_none_args:
93-
result = _extract_nested_basemodel(arg)
94-
if result is not None:
95-
basemodel_classes.append(result)
91+
basemodel_classes = [m for a in non_none_args if (m := _extract_nested_basemodel(a)) is not None]
9692
if len(basemodel_classes) == 1:
9793
return basemodel_classes[0]
9894
return None
9995

10096
return None
10197

10298

99+
def _unwrap_annotated_discriminator(annotation: Any) -> Any:
100+
"""Strip Annotated wrapper containing a Discriminator."""
101+
if get_origin(annotation) is not typing.Annotated:
102+
return annotation
103+
args = get_args(annotation)
104+
if len(args) >= 2 and any("Discriminator" in str(a) for a in args[1:]):
105+
return args[0]
106+
return annotation
107+
108+
103109
def format_type(annotation: Any) -> str:
104110
"""Format a type annotation for readable display.
105111
106112
Strips module prefixes and simplifies complex types.
107113
"""
114+
annotation = _unwrap_annotated_discriminator(annotation)
108115
type_str = str(annotation)
109116

110117
# Remove module prefixes
@@ -128,19 +135,6 @@ def format_type(annotation: Any) -> str:
128135
if match:
129136
type_str = f"Literal[{match.group(1)}]"
130137

131-
# Clean up Annotated types with Discriminator (too verbose)
132-
if "Annotated[" in type_str and "Discriminator" in type_str:
133-
start = type_str.index("Annotated[") + len("Annotated[")
134-
depth = 0
135-
for i, ch in enumerate(type_str[start:], start):
136-
if ch in "([":
137-
depth += 1
138-
elif ch in ")]":
139-
depth -= 1
140-
elif ch == "," and depth == 0:
141-
type_str = type_str[start:i].strip()
142-
break
143-
144138
return type_str
145139

146140

@@ -262,6 +256,8 @@ def format_model_text(
262256
indent: int = 0,
263257
seen_schemas: set[str] | None = None,
264258
max_depth: int = 3,
259+
seen_types: set[type] | None = None,
260+
depth: int = 0,
265261
) -> str:
266262
"""Format a Pydantic model as YAML-style text for agent context.
267263
@@ -272,30 +268,12 @@ def format_model_text(
272268
indent: Base indentation level.
273269
seen_schemas: Set of schema refs already rendered (mutated for cross-model dedup).
274270
max_depth: Maximum recursion depth for nested models.
271+
seen_types: Set of types already rendered (prevents infinite recursion).
272+
depth: Current recursion depth.
275273
"""
276-
return _format_model_text(
277-
cls,
278-
type_key=type_key,
279-
type_value=type_value,
280-
indent=indent,
281-
seen_schemas=seen_schemas,
282-
seen_types=set(),
283-
max_depth=max_depth,
284-
depth=0,
285-
)
286-
287-
288-
def _format_model_text(
289-
cls: type,
290-
type_key: str | None,
291-
type_value: str | None,
292-
indent: int,
293-
seen_schemas: set[str] | None,
294-
seen_types: set[type],
295-
max_depth: int,
296-
depth: int,
297-
) -> str:
298-
"""Recursive implementation of format_model_text."""
274+
if seen_types is None:
275+
seen_types = set()
276+
299277
pad = " " * indent
300278
lines: list[str] = []
301279
lines.append(f"{pad}{cls.__name__}:")

packages/data-designer/tests/cli/services/introspection/test_method_inspector.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,6 @@ class EmptyClass:
263263
"""A class with no public methods (no __init__ either)."""
264264

265265

266-
267266
class ClassWithBadSignature:
268267
"""A class where one method has an uninspectable signature."""
269268

0 commit comments

Comments
 (0)