What’s supported

For minimum-boilerplate CLIs, tyro aims to maximize support of Python’s standard typing features.

As a partial list, inputs can be annotated with:

Types can also be placed and nested in various structures, such as:

What’s not supported

There are some limitations. We currently do not fully support:

  • Self-referential types. For example, type RecursiveList[T] = T | list[RecursiveList[T]].

  • Variable-length sequences over nested structures, unless a default is provided. For types like list[Dataclass], we require a default value to infer length from. The length of the corresponding field cannot be changed from the CLI interface.

  • Nesting variable-length sequences in other sequences. tuple[int, ...] and tuple[tuple[int, int, int], ...] are supported, as the variable-length sequence is the outermost type. However, tuple[tuple[int, ...], ...] is ambiguous to parse and not supported.

  • Type parameters in class and static methods. For example:

    class MyClass[T: int | str]:
      @static_method
      def method1(arg: T) -> T:
        return arg
    
      @classmethod
      def method2(cls, arg: T) -> T:
        return arg
    
    # The `int` type parameter will be ignored.
    tyro.cli(MyClass[int].method1)
    tyro.cli(MyClass[int].method2)
    

    This is because MyClass[int].method1 / MyClass[int].method2 cannot be distinguished from MyClass.method1 / MyClass.method2 at runtime.

For some of these cases, a custom constructor can be defined as a workaround.