@@ -458,7 +458,7 @@ def _resolve_tuple(_tp: Any, args: tuple[Any, ...], *, has_default: bool = False
458458 first = args [0 ]
459459 if not all (a == first for a in args [1 :]):
460460 raise TypeError (
461- f"tuple[{ ', ' .join (a . __name__ if hasattr ( a , '__name__' ) else str (a ) for a in args )} ] "
461+ f"tuple[{ ', ' .join (_type_name (a ) for a in args )} ] "
462462 f"has mixed element types which is not currently supported because argparse "
463463 f"can only apply a single type= converter per argument. "
464464 f"Use tuple[T, T] (same type) or tuple[T, ...] instead."
@@ -505,6 +505,22 @@ def _resolve_enum(tp: Any, _args: tuple[Any, ...], **_ctx: Any) -> dict[str, Any
505505}
506506
507507
508+ # -- Helpers ------------------------------------------------------------------
509+
510+
511+ def _type_name (tp : Any ) -> str :
512+ """Best-effort type name for diagnostic messages."""
513+ return tp .__name__ if hasattr (tp , "__name__" ) else str (tp )
514+
515+
516+ def _nargs_yields_list (nargs : Any ) -> bool :
517+ """Return ``True`` when an argparse ``nargs`` value produces a list at parse time.
518+
519+ ``nargs=1`` is included: argparse returns ``[value]``, not the bare value.
520+ """
521+ return nargs in ("*" , "+" ) or (isinstance (nargs , int ) and nargs >= 1 )
522+
523+
508524def _resolve_type (
509525 tp : type ,
510526 * ,
@@ -553,22 +569,19 @@ def _resolve_type(
553569 if metadata :
554570 kwargs .update (metadata .to_kwargs ())
555571
556- type_repr = tp .__name__ if hasattr (tp , "__name__" ) else str (tp )
557572 nargs_val = kwargs .get ("nargs" )
558573
559574 # A fixed-arity type (e.g. ``tuple[T, T]``) declares its own nargs;
560575 # user metadata cannot override it to a different value.
561576 if isinstance (resolver_nargs , int ) and nargs_val != resolver_nargs :
562577 raise TypeError (
563- f"nargs={ nargs_val !r} conflicts with the fixed arity of '{ type_repr } ' (expected nargs={ resolver_nargs } )."
578+ f"nargs={ nargs_val !r} conflicts with the fixed arity of '{ _type_name ( tp ) } ' (expected nargs={ resolver_nargs } )."
564579 )
565580
566581 # nargs that produces a list of values requires a collection annotation.
567- # Catches mistakes like ``Annotated[str, Argument(nargs=2)]`` where the
568- # parameter is typed as a scalar but argparse will hand back a list.
569- if not kwargs .get ("is_collection" ) and (nargs_val in ("*" , "+" ) or (isinstance (nargs_val , int ) and nargs_val >= 1 )):
582+ if not kwargs .get ("is_collection" ) and _nargs_yields_list (nargs_val ):
570583 raise TypeError (
571- f"nargs={ nargs_val !r} produces a list of values, but the annotation '{ type_repr } ' is not a collection type. "
584+ f"nargs={ nargs_val !r} produces a list of values, but the annotation '{ _type_name ( tp ) } ' is not a collection type. "
572585 f"Use list[T], tuple[T, ...], or set[T] (optionally with | None) to match."
573586 )
574587
@@ -613,7 +626,7 @@ def _unwrap_optional(tp: type) -> tuple[type, bool]:
613626 f"Unexpected single-element Union without None: Union[{ non_none [0 ]} ]. "
614627 f"Use the type directly instead of wrapping in Union."
615628 )
616- type_names = " | " .join (a . __name__ if hasattr ( a , "__name__" ) else str (a ) for a in non_none )
629+ type_names = " | " .join (_type_name (a ) for a in non_none )
617630 raise TypeError (f"Union type { type_names } is ambiguous for auto-resolution." )
618631 return tp , False
619632
0 commit comments