.. 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
$ 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 │
╰───────────────────────────────────────────────────────────╯