.. Comment: this file is automatically generated by `update_example_docs.py`. It should not be modified manually. .. _example-category-basics: Basics ====== In these examples, we show basic examples of using :func:`tyro.cli`: functions, dataclasses, supported annotations, and configuration. .. _example-01_functions: Functions --------- In the simplest case, :func:`tyro.cli()` can be used to run a function with arguments populated from the CLI. .. code-block:: python :linenos: # 01_functions.py import tyro def main(field1: str, field2: int = 3) -> None: """Function, whose arguments will be populated from a CLI interface. Args: field1: A string field. field2: A numeric field, with a default value. """ print(field1, field2) if __name__ == "__main__": tyro.cli(main) We can use ``--help`` to show the help message, or ``--field1`` and ``--field2`` to set the arguments: .. raw:: html
    $ python ./01_functions.py --help
    usage: ./01_functions.py [-h] --field1 STR [--field2 INT]
    
    Function, whose arguments will be populated from a CLI interface.
    
    ╭─ options ─────────────────────────────────────────────────────────╮
    │ -h, --help    show this help message and exit                     │
    │ --field1 STR  A string field. (required)                          │
    │ --field2 INT  A numeric field, with a default value. (default: 3) │
    ╰───────────────────────────────────────────────────────────────────╯
    
.. raw:: html
    $ python ./01_functions.py --field1 hello
    hello 3
    
.. raw:: html
    $ python ./01_functions.py --field1 hello --field2 10
    hello 10
    
.. _example-02_dataclasses: Dataclasses ----------- In addition to functions, :func:`tyro.cli()` can also take dataclasses as input. .. code-block:: python :linenos: # 02_dataclasses.py from dataclasses import dataclass from pprint import pprint import tyro @dataclass class Args: """Description. This should show up in the helptext!""" field1: str """A string field.""" field2: int = 3 """A numeric field, with a default value.""" if __name__ == "__main__": args = tyro.cli(Args) pprint(args) To show the help message, we can use the ``--help`` flag: .. raw:: html
    $ python ./02_dataclasses.py --help
    usage: ./02_dataclasses.py [-h] --field1 STR [--field2 INT]
    
    Description. This should show up in the helptext!
    
    ╭─ options ─────────────────────────────────────────────────────────╮
    │ -h, --help    show this help message and exit                     │
    │ --field1 STR  A string field. (required)                          │
    │ --field2 INT  A numeric field, with a default value. (default: 3) │
    ╰───────────────────────────────────────────────────────────────────╯
    
We can override ``field1`` and ``field2``: .. raw:: html
    $ python ./02_dataclasses.py --field1 hello
    Args(field1='hello', field2=3)
    
.. raw:: html
    $ python ./02_dataclasses.py --field1 hello --field2 5
    Args(field1='hello', field2=5)
    
.. _example-03_multivalue: Multi-value arguments --------------------- Arguments of both fixed and variable lengths can be annotated with standard Python collection types. For Python 3.7 and 3.8, we can use either ``from __future__ import annotations`` to support ``list[T]`` and ``tuple[T]``, or the older :py:class:`typing.List` and :py:data:`typing.Tuple`. .. code-block:: python :linenos: # 03_multivalue.py import pathlib from dataclasses import dataclass from pprint import pprint import tyro @dataclass class Config: # Example of a variable-length tuple. `list[T]`, `set[T]`, # `dict[K, V]`, etc are supported as well. source_paths: tuple[pathlib.Path, ...] """This can be multiple!""" # Fixed-length tuples are also okay. dimensions: tuple[int, int] = (32, 32) """Height and width.""" if __name__ == "__main__": config = tyro.cli(Config) pprint(config) To print help: .. raw:: html
    $ python ./03_multivalue.py --help
    usage: ./03_multivalue.py [-h] --source-paths [PATH [PATH ...]] [--dimensions INT INT]
    
    ╭─ options ────────────────────────────────────────────────╮
    │ -h, --help            show this help message and exit    │
    │ --source-paths [PATH [PATH ...]]                         │
    │                       This can be multiple! (required)   │
    │ --dimensions INT INT  Height and width. (default: 32 32) │
    ╰──────────────────────────────────────────────────────────╯
    
We can override arguments: .. raw:: html
    $ python ./03_multivalue.py --source-paths ./data --dimensions 16 16
    Config(source_paths=(PosixPath('data'),), dimensions=(16, 16))
    
.. raw:: html
    $ python ./03_multivalue.py --source-paths ./data1 ./data2
    Config(source_paths=(PosixPath('data1'), PosixPath('data2')),
           dimensions=(32, 32))
    
.. _example-04_flags: Booleans and flags ------------------ Booleans can either be expected to be explicitly passed in, or, if given a default value, automatically converted to flags. To turn off conversion, see :class:`tyro.conf.FlagConversionOff`. .. code-block:: python :linenos: # 04_flags.py from dataclasses import dataclass from pprint import pprint import tyro @dataclass class Args: # Boolean. This expects an explicit "True" or "False". boolean: bool # Optional boolean. Same as above, but can be omitted. optional_boolean: bool | None = None # Pass --flag-a in to set this value to True. flag_a: bool = False # Pass --no-flag-b in to set this value to False. flag_b: bool = True if __name__ == "__main__": args = tyro.cli(Args) pprint(args) .. raw:: html
    $ python ./04_flags.py --help
    usage: ./04_flags.py [-h] [OPTIONS]
    
    ╭─ options ────────────────────────────────────────────────────────────────────╮
    │ -h, --help             show this help message and exit                       │
    │ --boolean {True,False}                                                       │
    │                        Boolean. This expects an explicit "True" or "False".  │
    │                        (required)                                            │
    │ --optional-boolean {None,True,False}                                         │
    │                        Optional boolean. Same as above, but can be omitted.  │
    │                        (default: None)                                       │
    │ --flag-a, --no-flag-a  Pass --flag-a in to set this value to True. (default: │
    │                        False)                                                │
    │ --flag-b, --no-flag-b  Pass --no-flag-b in to set this value to False.       │
    │                        (default: True)                                       │
    ╰──────────────────────────────────────────────────────────────────────────────╯
    
.. raw:: html
    $ python ./04_flags.py --boolean True
    Args(boolean=True, optional_boolean=None, flag_a=False, flag_b=True)
    
.. raw:: html
    $ python ./04_flags.py --boolean False --flag-a
    Args(boolean=False, optional_boolean=None, flag_a=True, flag_b=True)
    
.. raw:: html
    $ python ./04_flags.py --boolean False --no-flag-b
    Args(boolean=False, optional_boolean=None, flag_a=False, flag_b=False)
    
.. _example-05_choices: Choices ------- :py:data:`typing.Literal[]` can be used to restrict inputs to a fixed set of literal choices. .. code-block:: python :linenos: # 05_choices.py import dataclasses from pprint import pprint from typing import Literal import tyro @dataclasses.dataclass class Args: # We can use Literal[] to restrict the set of allowable inputs, for example, over # a set of strings. string: Literal["red", "green"] = "red" # Integers also work. (as well as booleans, enums, etc) number: Literal[0, 1, 2] = 0 if __name__ == "__main__": args = tyro.cli(Args) pprint(args) .. raw:: html
    $ python ./05_choices.py --help
    usage: ./05_choices.py [-h] [--string {red,green}] [--number {0,1,2}]
    
    ╭─ options ────────────────────────────────────────────────────────────────────╮
    │ -h, --help            show this help message and exit                        │
    │ --string {red,green}  We can use Literal[] to restrict the set of allowable  │
    │                       inputs, for example, over a set of strings. (default:  │
    │                       red)                                                   │
    │ --number {0,1,2}      Integers also work. (as well as booleans, enums, etc)  │
    │                       (default: 0)                                           │
    ╰──────────────────────────────────────────────────────────────────────────────╯
    
.. raw:: html
    $ python ./05_choices.py --string red
    Args(string='red', number=0)
    
.. raw:: html
    $ python ./05_choices.py --string blue
    ╭─ Invalid choice ─────────────────────────────────────────────────────────────╮
    │ invalid choice 'blue' for argument '--string'. Expected one of ('red',       │
    │ 'green').                                                                    │
    │ ──────────────────────────────────────────────────────────────────────────── │
    │ For full helptext, run ./05_choices.py --help                                │
    ╰──────────────────────────────────────────────────────────────────────────────╯
    
.. _example-06_enums: Enums ----- In addition to literals, enums can also be used to provide a fixed set of choices. .. code-block:: python :linenos: # 06_enums.py import enum from dataclasses import dataclass from pprint import pprint import tyro class Color(enum.Enum): RED = enum.auto() BLUE = enum.auto() @dataclass class Config: color: Color = Color.RED """Color argument.""" opacity: float = 0.5 """Opacity argument.""" if __name__ == "__main__": config = tyro.cli(Config) pprint(config) .. raw:: html
    $ python ./06_enums.py --help
    usage: ./06_enums.py [-h] [--color {RED,BLUE}] [--opacity FLOAT]
    
    ╭─ options ────────────────────────────────────────────╮
    │ -h, --help          show this help message and exit  │
    │ --color {RED,BLUE}  Color argument. (default: RED)   │
    │ --opacity FLOAT     Opacity argument. (default: 0.5) │
    ╰──────────────────────────────────────────────────────╯
    
.. raw:: html
    $ python ./06_enums.py --color RED
    Config(color=<Color.RED: 1>, opacity=0.5)
    
.. raw:: html
    $ python ./06_enums.py --color BLUE --opacity 0.75
    Config(color=<Color.BLUE: 2>, opacity=0.75)
    
.. _example-07_unions: Unions ------ :code:`X | Y` or :py:data:`typing.Union` can be used to expand inputs to multiple types. .. code-block:: python :linenos: # 07_unions.py import dataclasses import enum from pprint import pprint from typing import Literal, Optional import tyro class Color(enum.Enum): RED = enum.auto() GREEN = enum.auto() BLUE = enum.auto() @dataclasses.dataclass class Args: # Unions can be used to specify multiple allowable types. union_over_types: int | str = 0 string_or_enum: Literal["red", "green"] | Color = "red" # Unions also work over more complex nested types. union_over_tuples: tuple[int, int] | tuple[str] = ("1",) # And can be nested in other types. tuple_of_string_or_enum: tuple[Literal["red", "green"] | Color, ...] = ( "red", Color.RED, ) # Optional[T] is equivalent to `T | None`. integer: Optional[Literal[0, 1, 2, 3]] = None if __name__ == "__main__": args = tyro.cli(Args) pprint(args) .. raw:: html
    $ python ./07_unions.py --help
    usage: ./07_unions.py [-h] [OPTIONS]
    
    ╭─ options ────────────────────────────────────────────────────────────────────╮
    │ -h, --help                                                                   │
    │       show this help message and exit                                        │
    │ --union-over-types INT|STR                                                   │
    │       Unions can be used to specify multiple allowable types. (default: 0)   │
    │ --string-or-enum {red,green,RED,GREEN,BLUE}                                  │
    │       Unions can be used to specify multiple allowable types. (default: red) │
    │ --union-over-tuples {INT INT}|STR                                            │
    │       Unions also work over more complex nested types. (default: 1)          │
    │ --tuple-of-string-or-enum [{red,green,RED,GREEN,BLUE} [{red,green,RED,GREEN, │
    │ BLUE} ...]]                                                                  │
    │       And can be nested in other types. (default: red RED)                   │
    │ --integer {None,0,1,2,3}                                                     │
    │       Optional[T] is equivalent to `T | None`. (default: None)               │
    ╰──────────────────────────────────────────────────────────────────────────────╯
    
.. raw:: html
    $ python ./07_unions.py --union-over-types 3
    Args(union_over_types=3,
         string_or_enum='red',
         union_over_tuples=('1',),
         tuple_of_string_or_enum=('red', <Color.RED: 1>),
         integer=None)
    
.. raw:: html
    $ python ./07_unions.py --union-over-types three
    Args(union_over_types='three',
         string_or_enum='red',
         union_over_tuples=('1',),
         tuple_of_string_or_enum=('red', <Color.RED: 1>),
         integer=None)
    
.. raw:: html
    $ python ./07_unions.py --integer None
    Args(union_over_types=0,
         string_or_enum='red',
         union_over_tuples=('1',),
         tuple_of_string_or_enum=('red', <Color.RED: 1>),
         integer=None)
    
.. raw:: html
    $ python ./07_unions.py --integer 0
    Args(union_over_types=0,
         string_or_enum='red',
         union_over_tuples=('1',),
         tuple_of_string_or_enum=('red', <Color.RED: 1>),
         integer=0)
    
.. _example-08_positional: Positional arguments -------------------- Positional-only arguments in functions are converted to positional CLI arguments. Note: If a double-dash ('--') appears in a command line, everything afterward will be considered positional. For more general positional arguments, see :class:`tyro.conf.Positional`. .. code-block:: python :linenos: # 08_positional.py from __future__ import annotations import pathlib import tyro def main( source: pathlib.Path, dest: pathlib.Path, /, # Mark the end of positional arguments. verbose: bool = False, ) -> None: """Command-line interface defined using a function signature. This docstring is parsed to generate helptext. Args: source: Source path. dest: Destination path. verbose: Explain what is being done. """ print(f"{source=}\n{dest=}\n{verbose=}") if __name__ == "__main__": tyro.cli(main) .. raw:: html
    $ python 08_positional.py --help
    usage: 08_positional.py [-h] PATH PATH [--verbose | --no-verbose]
    
    Command-line interface defined using a function signature. This docstring is parsed to generate helptext.
    
    ╭─ positional arguments ────────────────────────────────────────────────╮
    │ PATH                     Source path. (required)                      │
    │ PATH                     Destination path. (required)                 │
    ╰───────────────────────────────────────────────────────────────────────╯
    ╭─ options ─────────────────────────────────────────────────────────────╮
    │ -h, --help               show this help message and exit              │
    │ --verbose, --no-verbose  Explain what is being done. (default: False) │
    ╰───────────────────────────────────────────────────────────────────────╯
    
.. raw:: html
    $ python 08_positional.py ./a ./b
    source=PosixPath('a')
    dest=PosixPath('b')
    verbose=False
    
.. raw:: html
    $ python 08_positional.py ./test1 ./test2 --verbose
    source=PosixPath('test1')
    dest=PosixPath('test2')
    verbose=True
    
.. _example-09_conf: Configuration via typing.Annotated[] ------------------------------------ The :mod:`tyro.conf` module contains utilities that can be used in conjunction with :py:data:`typing.Annotated` to configure command-line interfaces beyond what is expressible via static type annotations. To apply options globally, these same flags can also be passed via the ``config`` argument of :func:`tyro.cli`. Features here are supported, but generally unnecessary and should be used sparingly. .. code-block:: python :linenos: # 09_conf.py import dataclasses from typing_extensions import Annotated import tyro @dataclasses.dataclass class Args: # A numeric field parsed as a positional argument. positional: tyro.conf.Positional[int] # A boolean field. boolean: bool = False # A numeric field that can't be changed via the CLI. fixed: tyro.conf.Fixed[int] = 5 # A tuple field parsed as a single argument. tuple_arg: tyro.conf.UsePythonSyntaxForLiteralCollections[tuple[int, float]] = ( 1, 2.0, ) # A field with manually overridden properties. manual: Annotated[ str, tyro.conf.arg( name="renamed", metavar="STRING", help="A field with manually overridden properties!", ), ] = "Hello" if __name__ == "__main__": print(tyro.cli(Args, config=(tyro.conf.FlagConversionOff,))) .. raw:: html
    $ python ./09_conf.py --help
    usage: ./09_conf.py [-h] [OPTIONS]
    
    ╭─ positional arguments ───────────────────────────────────────────────────────╮
    │ INT                      A numeric field parsed as a positional argument.    │
    │                          (required)                                          │
    ╰──────────────────────────────────────────────────────────────────────────────╯
    ╭─ options ────────────────────────────────────────────────────────────────────╮
    │ -h, --help               show this help message and exit                     │
    │ --boolean {True,False}   A boolean field. (default: False)                   │
    │ --fixed {fixed}          A numeric field that can't be changed via the CLI.  │
    │                          (fixed to: 5)                                       │
    │ --tuple-arg (INT,FLOAT)  A tuple field parsed as a single argument.          │
    │                          (default: '(1, 2.0)')                               │
    │ --renamed STRING         A field with manually overridden properties!        │
    │                          (default: Hello)                                    │
    ╰──────────────────────────────────────────────────────────────────────────────╯
    
.. raw:: html
    $ python ./09_conf.py 5 --boolean True
    Args(positional=5, boolean=True, fixed=5, tuple_arg=(1, 2.0), manual='Hello')
    
.. _example-10_aliases: Argument aliases ---------------- :func:`tyro.conf.arg()` can be used to attach aliases to arguments. .. code-block:: python :linenos: # 10_aliases.py from typing import Annotated import tyro def checkout( branch: Annotated[str, tyro.conf.arg(aliases=["-b"])], ) -> None: """Check out a branch.""" print(f"{branch=}") if __name__ == "__main__": tyro.cli(checkout) .. raw:: html
    $ python ./10_aliases.py --help
    usage: ./10_aliases.py [-h] -b STR
    
    Check out a branch.
    
    ╭─ options ─────────────────────────────────────────────╮
    │ -h, --help            show this help message and exit │
    │ -b STR, --branch STR  (required)                      │
    ╰───────────────────────────────────────────────────────╯
    
.. raw:: html
    $ python ./10_aliases.py --branch main
    branch='main'
    
.. raw:: html
    $ python ./10_aliases.py -b main
    branch='main'
    
.. _example-11_type_aliases_py312: Type aliases (3.12+) -------------------- In Python 3.12, the :code:`type` statement is introduced to create type aliases. .. code-block:: python :linenos: # 11_type_aliases_py312.py import dataclasses import tyro # Lazily-evaluated type alias. type Field1Type = Inner @dataclasses.dataclass class Inner: a: int b: str @dataclasses.dataclass class Args: """Description. This should show up in the helptext!""" field1: Field1Type """A field.""" field2: int = 3 """A numeric field, with a default value.""" if __name__ == "__main__": args = tyro.cli(Args) print(args) .. raw:: html
    $ python ./11_type_aliases_py312.py --help
    usage: ./11_type_aliases_py312.py [-h] [--field2 INT] --field1.a INT --field1.b STR
    
    Description. This should show up in the helptext!
    
    ╭─ options ───────────────────────────────────────────────────────────╮
    │ -h, --help      show this help message and exit                     │
    │ --field2 INT    A numeric field, with a default value. (default: 3) │
    ╰─────────────────────────────────────────────────────────────────────╯
    ╭─ field1 options ────────────────────────────────────────────────────╮
    │ A field.                                                            │
    │ ─────────────────────────────────────────────────────────────────── │
    │ --field1.a INT  (required)                                          │
    │ --field1.b STR  (required)                                          │
    ╰─────────────────────────────────────────────────────────────────────╯
    
.. _example-12_counters: Counters -------- Repeatable 'counter' arguments can be specified via :data:`tyro.conf.UseCounterAction`. .. code-block:: python :linenos: # 12_counters.py from typing_extensions import Annotated import tyro from tyro.conf import UseCounterAction def main( verbosity: UseCounterAction[int], aliased_verbosity: Annotated[UseCounterAction[int], tyro.conf.arg(aliases=["-v"])], ) -> None: """Example showing how to use counter actions. Args: verbosity: Verbosity level. aliased_verbosity: Same as above, but can also be specified with -v, -vv, -vvv, etc. """ print("Verbosity level:", verbosity) print("Verbosity level (aliased):", aliased_verbosity) if __name__ == "__main__": tyro.cli(main, config=(tyro.conf.CascadeSubcommandArgs,)) .. raw:: html
    $ python ./12_counters.py --help
    usage: ./12_counters.py [-h] [--verbosity] [-v]
    
    Example showing how to use counter actions.
    
    ╭─ options ────────────────────────────────────────────────────────────────────╮
    │ -h, --help               show this help message and exit                     │
    │ --verbosity              Verbosity level. (repeatable)                       │
    │ -v, --aliased-verbosity  Same as above, but can also be specified with -v,   │
    │                          -vv, -vvv, etc. (repeatable)                        │
    ╰──────────────────────────────────────────────────────────────────────────────╯
    
.. raw:: html
    $ python ./12_counters.py --verbosity
    Verbosity level: 1
    Verbosity level (aliased): 0
    
.. raw:: html
    $ python ./12_counters.py --verbosity --verbosity
    Verbosity level: 2
    Verbosity level (aliased): 0
    
.. raw:: html
    $ python ./12_counters.py -vvv
    Verbosity level: 0
    Verbosity level (aliased): 3
    
.. _example-13_classes: Instantiating classes --------------------- In addition to functions and dataclasses, we can also generate CLIs from the constructors of standard Python classes. .. code-block:: python :linenos: # 13_classes.py import tyro class Args: def __init__( self, field1: str, field2: int, flag: bool = False, ): """Arguments. Args: field1: A string field. field2: A numeric field. flag: A boolean flag. """ self.data = [field1, field2, flag] if __name__ == "__main__": args = tyro.cli(Args) print(args.data) .. raw:: html
    $ python ./13_classes.py --help
    usage: ./13_classes.py [-h] --field1 STR --field2 INT [--flag | --no-flag]
    
    Arguments.
    
    ╭─ options ───────────────────────────────────────────╮
    │ -h, --help         show this help message and exit  │
    │ --field1 STR       A string field. (required)       │
    │ --field2 INT       A numeric field. (required)      │
    │ --flag, --no-flag  A boolean flag. (default: False) │
    ╰─────────────────────────────────────────────────────╯
    
.. raw:: html
    $ python ./13_classes.py --field1 hello --field2 7
    ['hello', 7, False]
    
.. _example-14_mutex: Mutually exclusive groups ------------------------- :func:`tyro.conf.create_mutex_group()` can be used to create mutually exclusive argument groups, where either exactly one (required=True) or at most one (required=False) argument from the group can be specified. The ``title`` parameter can be used to customize the group title in the help text. .. code-block:: python :linenos: # 14_mutex.py from pathlib import Path from typing import Annotated, Literal import tyro RequiredGroup = tyro.conf.create_mutex_group(required=True, title="output target") OptionalGroup = tyro.conf.create_mutex_group(required=False, title="verbosity level") def main( # Exactly one of --target-stream or --target-file must be specified. target_stream: Annotated[Literal["stdout", "stderr"] | None, RequiredGroup] = None, target_file: Annotated[Path | None, RequiredGroup] = None, # Either --verbose or --very-verbose can be specified (but not both). verbose: Annotated[bool, OptionalGroup] = False, very_verbose: Annotated[bool, OptionalGroup] = False, ) -> None: """Demonstrate mutually exclusive argument groups.""" if very_verbose or verbose: print(f"{target_stream=} {target_file=}") if very_verbose: print(f"{target_stream=} {target_file=}") if __name__ == "__main__": tyro.cli( main, # `DisallowNone` prevents `None` from being a valid choice for # `--target-stream` and `--target-file`. # # `FlagCreatePairsOff` prevents `--no-verbose` and `--no-very-verbose` from # being created. config=(tyro.conf.DisallowNone, tyro.conf.FlagCreatePairsOff), ) .. raw:: html
    $ python ./14_mutex.py
    ╭─ Required mutex group ──────────────────────╮
    │ Missing required argument group:            │
    │   • --target-stream, --target-file          │
    │ ─────────────────────────────────────────── │
    │ For full helptext, run ./14_mutex.py --help │
    ╰─────────────────────────────────────────────╯
    
.. raw:: html
    $ python ./14_mutex.py --help
    usage: ./14_mutex.py [-h] [OPTIONS]
    
    Demonstrate mutually exclusive argument groups.
    
    ╭─ options ──────────────────────────────────────────╮
    │ -h, --help      show this help message and exit    │
    ╰────────────────────────────────────────────────────╯
    ╭─ output target ────────────────────────────────────╮
    │ Exactly one argument must be passed in. (required) │
    │ ────────────────────────────────────────────────── │
    │ --target-stream {stdout,stderr}                    │
    │                 (default: None)                    │
    │ --target-file PATH                                 │
    │                 (default: None)                    │
    ╰────────────────────────────────────────────────────╯
    ╭─ verbosity level ──────────────────────────────────╮
    │ At most one argument can be overridden.            │
    │ ────────────────────────────────────────────────── │
    │ --verbose       (default: False)                   │
    │ --very-verbose  (default: False)                   │
    ╰────────────────────────────────────────────────────╯
    
.. raw:: html
    $ python ./14_mutex.py --target-stream stdout
    
.. raw:: html
    $ python ./14_mutex.py --target-file /tmp/output.txt
    
.. raw:: html
    $ python ./14_mutex.py --target-stream stdout --verbose
    target_stream='stdout' target_file=None
    
.. raw:: html
    $ python ./14_mutex.py --target-file /tmp/output.txt --very-verbose
    target_stream=None target_file=PosixPath('/tmp/output.txt')
    target_stream=None target_file=PosixPath('/tmp/output.txt')
    
.. raw:: html
    $ python ./14_mutex.py --target-stream stdout --target-file /tmp/output.txt
    ╭─ Mutually exclusive arguments ────────────────────────────────────────╮
    │ Arguments --target-stream and --target-file are not allowed together! │
    │ ───────────────────────────────────────────────────────────────────── │
    │ For full helptext, run ./14_mutex.py --help                           │
    ╰───────────────────────────────────────────────────────────────────────╯
    
.. raw:: html
    $ python ./14_mutex.py --target-stream stdout --verbose --very-verbose
    ╭─ Mutually exclusive arguments ───────────────────────────────────╮
    │ Arguments --verbose and --very-verbose are not allowed together! │
    │ ──────────────────────────────────────────────────────────────── │
    │ For full helptext, run ./14_mutex.py --help                      │
    ╰──────────────────────────────────────────────────────────────────╯
    
.. _example-15_compact_help: Compact help ------------ When a configuration has many fields, the help text can become verbose and difficult to scan. The ``compact_help`` parameter in :func:`tyro.cli()` enables a more succinct format that omits field descriptions. In compact mode, users can still access full descriptions via ``--help-verbose``. .. code-block:: python :linenos: # 15_compact_help.py from dataclasses import dataclass from typing import Literal import tyro @dataclass class ServerConfig: """Configuration for a web server.""" # Network settings. host: str = "0.0.0.0" """Hostname to bind the server to.""" port: int = 8000 """Port number to listen on.""" workers: int = 4 """Number of worker processes.""" timeout: int = 30 """Request timeout in seconds.""" # Security settings. ssl_enabled: bool = False """Enable SSL/TLS encryption.""" ssl_cert_path: str = "/etc/ssl/cert.pem" """Path to SSL certificate file.""" ssl_key_path: str = "/etc/ssl/key.pem" """Path to SSL private key file.""" # Application settings. max_request_size: int = 10485760 """Maximum request size in bytes.""" cors_origins: str = "*" """Allowed CORS origins (comma-separated).""" log_level: Literal["debug", "info", "warning", "error"] = "info" """Logging level for the application.""" log_file: str = "/var/log/server.log" """Path to log file.""" # Performance settings. cache_enabled: bool = True """Enable response caching.""" cache_size: int = 1000 """Maximum number of cached responses.""" compression_enabled: bool = True """Enable response compression.""" keepalive_timeout: int = 5 """Keep-alive connection timeout in seconds.""" # Database settings. db_host: str = "localhost" """Database server hostname.""" db_port: int = 5432 """Database server port.""" db_name: str = "appdb" """Database name.""" if __name__ == "__main__": # Parse with compact_help=True to enable compact mode. config = tyro.cli(ServerConfig, compact_help=True) print(f"Starting server with config:\n{config}") Compact mode (default with compact_help=True): .. raw:: html
    $ python ./15_compact_help.py --help
    usage: ./15_compact_help.py [-h] [OPTIONS]
    
    Configuration for a web server.
    
    ╭─ options ───────────────────────────────────────────────────────╮
    │ -h, --help show this help message and exit                      │
    │ -H, --help-verbose show verbose help with full descriptions     │
    │ --host STR (default: 0.0.0.0)                                   │
    │ --port INT (default: 8000)                                      │
    │ --workers INT (default: 4)                                      │
    │ --timeout INT (default: 30)                                     │
    │ --ssl-enabled, --no-ssl-enabled (default: False)                │
    │ --ssl-cert-path STR (default: /etc/ssl/cert.pem)                │
    │ --ssl-key-path STR (default: /etc/ssl/key.pem)                  │
    │ --max-request-size INT (default: 10485760)                      │
    │ --cors-origins STR (default: '*')                               │
    │ --log-level {debug,info,warning,error} (default: info)          │
    │ --log-file STR (default: /var/log/server.log)                   │
    │ --cache-enabled, --no-cache-enabled (default: True)             │
    │ --cache-size INT (default: 1000)                                │
    │ --compression-enabled, --no-compression-enabled (default: True) │
    │ --keepalive-timeout INT (default: 5)                            │
    │ --db-host STR (default: localhost)                              │
    │ --db-port INT (default: 5432)                                   │
    │ --db-name STR (default: appdb)                                  │
    ╰─────────────────────────────────────────────────────────────────╯
    
Verbose mode (access full descriptions): .. raw:: html
    $ python ./15_compact_help.py --help-verbose
    usage: ./15_compact_help.py [-h] [OPTIONS]
    
    Configuration for a web server.
    
    ╭─ options ────────────────────────────────────────────────────────────────────╮
    │ -h, --help           show this help message and exit                         │
    │ --host STR           Hostname to bind the server to. (default: 0.0.0.0)      │
    │ --port INT           Port number to listen on. (default: 8000)               │
    │ --workers INT        Number of worker processes. (default: 4)                │
    │ --timeout INT        Request timeout in seconds. (default: 30)               │
    │ --ssl-enabled, --no-ssl-enabled                                              │
    │                      Enable SSL/TLS encryption. (default: False)             │
    │ --ssl-cert-path STR  Path to SSL certificate file. (default:                 │
    │                      /etc/ssl/cert.pem)                                      │
    │ --ssl-key-path STR   Path to SSL private key file. (default:                 │
    │                      /etc/ssl/key.pem)                                       │
    │ --max-request-size INT                                                       │
    │                      Maximum request size in bytes. (default: 10485760)      │
    │ --cors-origins STR   Allowed CORS origins (comma-separated). (default: '*')  │
    │ --log-level {debug,info,warning,error}                                       │
    │                      Logging level for the application. (default: info)      │
    │ --log-file STR       Path to log file. (default: /var/log/server.log)        │
    │ --cache-enabled, --no-cache-enabled                                          │
    │                      Enable response caching. (default: True)                │
    │ --cache-size INT     Maximum number of cached responses. (default: 1000)     │
    │ --compression-enabled, --no-compression-enabled                              │
    │                      Enable response compression. (default: True)            │
    │ --keepalive-timeout INT                                                      │
    │                      Keep-alive connection timeout in seconds. (default: 5)  │
    │ --db-host STR        Database server hostname. (default: localhost)          │
    │ --db-port INT        Database server port. (default: 5432)                   │
    │ --db-name STR        Database name. (default: appdb)                         │
    ╰──────────────────────────────────────────────────────────────────────────────╯
    
Actually run it: .. raw:: html
    $ python ./15_compact_help.py --host localhost --port 8080
    Starting server with config:
    ServerConfig(host='localhost', port=8080, workers=4, timeout=30, ssl_enabled=False, ssl_cert_path='/etc/ssl/cert.pem', ssl_key_path='/etc/ssl/key.pem', max_request_size=10485760, cors_origins='*', log_level='info', log_file='/var/log/server.log', cache_enabled=True, cache_size=1000, compression_enabled=True, keepalive_timeout=5, db_host='localhost', db_port=5432, db_name='appdb')
    
.. _example-16_verbosity: Verbosity flags --------------- :class:`tyro.extras.Verbosity` provides standard ``-v``/``--verbose`` and ``-q``/``--quiet`` count flags that map to Python :mod:`logging` levels, with the two flags being mutually exclusive. By default, when ``Verbosity`` is a nested field, long flags carry the field prefix (``--verbosity.verbose``). Annotating with :data:`tyro.conf.OmitArgPrefixes` promotes them to the top level (``--verbose``, ``--quiet``). Short aliases ``-v`` and ``-q`` always work regardless of nesting. Inspired by `clap-verbosity-flag `_ from the Rust/clap ecosystem. .. code-block:: python :linenos: # 16_verbosity.py import dataclasses import logging from pathlib import Path from typing_extensions import Annotated import tyro from tyro.conf import OmitArgPrefixes, Positional from tyro.extras import Verbosity logger = logging.getLogger(__name__) @dataclasses.dataclass class Args: """Process files with configurable log verbosity.""" path: Positional[Path] = dataclasses.field(default_factory=Path.cwd) """Path to process.""" # Log verbosity. OmitArgPrefixes promotes --verbosity.verbose/--verbosity.quiet # to --verbose/--quiet at the top level. verbosity: Annotated[Verbosity, OmitArgPrefixes] = dataclasses.field( default_factory=Verbosity ) if __name__ == "__main__": args = tyro.cli(Args) logging.basicConfig(level=args.verbosity.log_level()) logger.info("path=%s", args.path) .. raw:: html
    $ python ./16_verbosity.py --help /home
    usage: ./16_verbosity.py [-h] [PATH] [-v] [-q]
    
    Process files with configurable log verbosity.
    
    ╭─ positional arguments ───────────────────────────────────────────────────────╮
    │ [PATH]         Path to process. (default:                                    │
    │                /Users/brentyi/tyro/examples/01_basics)                       │
    ╰──────────────────────────────────────────────────────────────────────────────╯
    ╭─ options ────────────────────────────────────────────────────────────────────╮
    │ -h, --help     show this help message and exit                               │
    ╰──────────────────────────────────────────────────────────────────────────────╯
    ╭─ verbosity ──────────────────────────────────────────────────────────────────╮
    │ At most one argument can be overridden.                                      │
    │ ──────────────────────────────────────────────────────────────────────────── │
    │ -v, --verbose  Increase log verbosity. (repeatable)                          │
    │ -q, --quiet    Decrease log verbosity. (repeatable)                          │
    ╰──────────────────────────────────────────────────────────────────────────────╯
    
.. raw:: html
    $ python ./16_verbosity.py -v /home
    INFO:__main__:path=/home
    
.. raw:: html
    $ python ./16_verbosity.py -vv /home
    INFO:__main__:path=/home
    
.. raw:: html
    $ python ./16_verbosity.py -q /home
    
.. raw:: html
    $ python ./16_verbosity.py --verbose /home
    INFO:__main__:path=/home
    
.. raw:: html
    $ python ./16_verbosity.py --quiet --verbose /home
    ╭─ Mutually exclusive arguments ────────────────────────────╮
    │ Arguments --quiet and --verbose are not allowed together! │
    │ ───────────────────────────────────────────────────────── │
    │ For full helptext, run ./16_verbosity.py --help           │
    ╰───────────────────────────────────────────────────────────╯