.. Comment: this file is automatically generated by `update_example_docs.py`. It should not be modified manually. .. _example-category-hierarchical_structures: Hierarchical structures ======================= In these examples, we show how :func:`tyro.cli` can be used to instantiate hierarchical structures. This can enable modular, reusable, and composable CLI interfaces. .. _example-01_nesting: Nested dataclasses ------------------ Structures (typically :py:func:`dataclasses.dataclass`) can be nested to build hierarchical configuration objects. This helps with modularity and grouping in larger projects. .. code-block:: python :linenos: # 01_nesting.py import dataclasses import tyro @dataclasses.dataclass class OptimizerConfig: learning_rate: float = 3e-4 weight_decay: float = 1e-2 @dataclasses.dataclass class Config: # Optimizer options. opt: OptimizerConfig # Random seed. seed: int = 0 if __name__ == "__main__": config = tyro.cli(Config) print(dataclasses.asdict(config)) .. raw:: html
$ python ./01_nesting.py --help
usage: ./01_nesting.py [-h] [--seed INT] [--opt.learning-rate FLOAT] [--opt.weight-decay FLOAT]
╭─ options ───────────────────────────────────╮
│ -h, --help show this help message and exit │
│ --seed INT Random seed. (default: 0) │
╰─────────────────────────────────────────────╯
╭─ opt options ───────────────────────────────╮
│ Optimizer options. │
│ ─────────────────────────────────────────── │
│ --opt.learning-rate FLOAT │
│ (default: 0.0003) │
│ --opt.weight-decay FLOAT │
│ (default: 0.01) │
╰─────────────────────────────────────────────╯
.. raw:: html
$ python ./01_nesting.py --opt.learning-rate 1e-3
{'opt': {'learning_rate': 0.001, 'weight_decay': 0.01}, 'seed': 0}
.. raw:: html
$ python ./01_nesting.py --seed 4
{'opt': {'learning_rate': 0.0003, 'weight_decay': 0.01}, 'seed': 4}
.. _example-02_nesting_in_func:
Structures as function arguments
--------------------------------
Structures can be used as input to functions.
.. code-block:: python
:linenos:
# 02_nesting_in_func.py
import dataclasses
import pathlib
import tyro
@dataclasses.dataclass
class OptimizerConfig:
learning_rate: float = 3e-4
weight_decay: float = 1e-2
@dataclasses.dataclass
class Config:
# Optimizer options.
optimizer: OptimizerConfig
# Random seed.
seed: int = 0
def train(
out_dir: pathlib.Path,
config: Config,
) -> None:
"""Train a model.
Args:
out_dir: Where to save logs and checkpoints.
config: Experiment configuration.
"""
print(f"Saving to: {out_dir}")
print(f"Config: {config}")
if __name__ == "__main__":
tyro.cli(train)
.. raw:: html
$ python ./02_nesting_in_func.py --help
usage: ./02_nesting_in_func.py [-h] [OPTIONS]
Train a model.
╭─ options ─────────────────────────────────────────────────────────╮
│ -h, --help show this help message and exit │
│ --out-dir PATH Where to save logs and checkpoints. (required) │
╰───────────────────────────────────────────────────────────────────╯
╭─ config options ──────────────────────────────────────────────────╮
│ Experiment configuration. │
│ ───────────────────────────────────────────────────────────────── │
│ --config.seed INT Random seed. (default: 0) │
╰───────────────────────────────────────────────────────────────────╯
╭─ config.optimizer options ────────────────────────────────────────╮
│ Optimizer options. │
│ ───────────────────────────────────────────────────────────────── │
│ --config.optimizer.learning-rate FLOAT │
│ (default: 0.0003) │
│ --config.optimizer.weight-decay FLOAT │
│ (default: 0.01) │
╰───────────────────────────────────────────────────────────────────╯
.. raw:: html
$ python ./02_nesting_in_func.py --out-dir /tmp/test1
Saving to: /tmp/test1
Config: Config(optimizer=OptimizerConfig(learning_rate=0.0003, weight_decay=0.01), seed=0)
.. raw:: html
$ python ./02_nesting_in_func.py --out-dir /tmp/test2 --config.seed 4
Saving to: /tmp/test2
Config: Config(optimizer=OptimizerConfig(learning_rate=0.0003, weight_decay=0.01), seed=4)
.. _example-03_nesting_containers:
Nesting in containers
---------------------
Structures can be nested inside of standard containers.
.. warning::
When placing structures inside of containers like lists or tuples, the
length of the container must be inferrable from the annotation or default
value.
.. code-block:: python
:linenos:
# 03_nesting_containers.py
import dataclasses
import tyro
@dataclasses.dataclass
class RGB:
r: int
g: int
b: int
@dataclasses.dataclass
class Args:
color_tuple: tuple[RGB, RGB]
color_dict: dict[str, RGB] = dataclasses.field(
# We can't use mutable values as defaults directly.
default_factory=lambda: {
"red": RGB(255, 0, 0),
"green": RGB(0, 255, 0),
"blue": RGB(0, 0, 255),
}
)
if __name__ == "__main__":
args = tyro.cli(Args)
print(args)
.. raw:: html
$ python ./03_nesting_containers.py --help
usage: ./03_nesting_containers.py [-h] [OPTIONS]
╭─ options ─────────────────────────────────────────────────╮
│ -h, --help show this help message and exit │
╰───────────────────────────────────────────────────────────╯
╭─ color-tuple.0 options ───────────────────────────────────╮
│ --color-tuple.0.r INT (required) │
│ --color-tuple.0.g INT (required) │
│ --color-tuple.0.b INT (required) │
╰───────────────────────────────────────────────────────────╯
╭─ color-tuple.1 options ───────────────────────────────────╮
│ --color-tuple.1.r INT (required) │
│ --color-tuple.1.g INT (required) │
│ --color-tuple.1.b INT (required) │
╰───────────────────────────────────────────────────────────╯
╭─ color-dict.red options ──────────────────────────────────╮
│ --color-dict.red.r INT (default: 255) │
│ --color-dict.red.g INT (default: 0) │
│ --color-dict.red.b INT (default: 0) │
╰───────────────────────────────────────────────────────────╯
╭─ color-dict.green options ────────────────────────────────╮
│ --color-dict.green.r INT (default: 0) │
│ --color-dict.green.g INT (default: 255) │
│ --color-dict.green.b INT (default: 0) │
╰───────────────────────────────────────────────────────────╯
╭─ color-dict.blue options ─────────────────────────────────╮
│ --color-dict.blue.r INT (default: 0) │
│ --color-dict.blue.g INT (default: 0) │
│ --color-dict.blue.b INT (default: 255) │
╰───────────────────────────────────────────────────────────╯
.. _example-04_dictionaries:
Dictionaries and ``TypedDict``
------------------------------
Dictionary inputs can be specified using either a standard ``dict[K, V]``
annotation, or a :py:class:`TypedDict` subclass.
For configuring :py:class:`TypedDict`, we also support :code:`total={True/False}`,
:py:data:`typing.Required`, and :py:data:`typing.NotRequired`. See the `Python docs
$ python ./04_dictionaries.py --help
usage: ./04_dictionaries.py [-h] [OPTIONS]
╭─ options ────────────────────────────────────────────────────────────────────╮
│ -h, --help │
│ show this help message and exit │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ typed-dict-a options ───────────────────────────────────────────────────────╮
│ --typed-dict-a.learning-rate FLOAT │
│ (unset by default) │
│ --typed-dict-a.betas FLOAT FLOAT │
│ (unset by default) │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ typed-dict-b options ───────────────────────────────────────────────────────╮
│ --typed-dict-b.learning-rate FLOAT │
│ NotRequired[] specifies that a particular key doesn't need to exist. │
│ (unset by default) │
│ --typed-dict-b.betas FLOAT FLOAT │
│ (required) │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ standard-dict options ──────────────────────────────────────────────────────╮
│ --standard-dict.learning-rate FLOAT │
│ (default: 0.0003) │
│ --standard-dict.beta1 FLOAT │
│ (default: 0.9) │
│ --standard-dict.beta2 FLOAT │
│ (default: 0.999) │
╰──────────────────────────────────────────────────────────────────────────────╯
.. raw:: html
$ python ./04_dictionaries.py --typed-dict-a.learning-rate 3e-4 --typed-dict-b.betas 0.9 0.999
Typed dict A: {'learning_rate': 0.0003}
Typed dict B: {'betas': (0.9, 0.999)}
Standard dict: {'learning_rate': 0.0003, 'beta1': 0.9, 'beta2': 0.999}
.. raw:: html
$ python ./04_dictionaries.py --typed-dict-b.betas 0.9 0.999
Typed dict A: {}
Typed dict B: {'betas': (0.9, 0.999)}
Standard dict: {'learning_rate': 0.0003, 'beta1': 0.9, 'beta2': 0.999}
.. _example-05_tuples:
Tuples and ``NamedTuple``
-------------------------
Example using :func:`tyro.cli()` to instantiate tuple types. :py:class:`tuple`,
:py:data:`typing.Tuple`, and :py:class:`typing.NamedTuple` are all supported.
.. code-block:: python
:linenos:
# 05_tuples.py
from typing import NamedTuple
import tyro
# Named tuples are interpreted as nested structures.
class Color(NamedTuple):
r: int
g: int
b: int
class TupleType(NamedTuple):
"""Description.
This should show up in the helptext!"""
# Tuple types can contain raw values.
color: tuple[int, int, int] = (255, 0, 0)
# Tuple types can contain nested structures.
two_colors: tuple[Color, Color] = (Color(255, 0, 0), Color(0, 255, 0))
if __name__ == "__main__":
x = tyro.cli(TupleType)
assert isinstance(x, tuple)
print(x)
.. raw:: html
$ python ./05_tuples.py --help
usage: ./05_tuples.py [-h] [OPTIONS]
Description. This should show up in the helptext!
╭─ options ────────────────────────────────────────────────────────────────────╮
│ -h, --help show this help message and exit │
│ --color INT INT INT Tuple types can contain raw values. (default: 255 0 0) │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ two-colors.0 options ───────────────────────────────────────────────────────╮
│ --two-colors.0.r INT (default: 255) │
│ --two-colors.0.g INT (default: 0) │
│ --two-colors.0.b INT (default: 0) │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ two-colors.1 options ───────────────────────────────────────────────────────╮
│ --two-colors.1.r INT (default: 0) │
│ --two-colors.1.g INT (default: 255) │
│ --two-colors.1.b INT (default: 0) │
╰──────────────────────────────────────────────────────────────────────────────╯
.. raw:: html
$ python ./05_tuples.py --color 127 127 127
TupleType(color=(127, 127, 127), two_colors=(Color(r=255, g=0, b=0), Color(r=0, g=255, b=0)))
.. raw:: html
$ python ./05_tuples.py --two-colors.1.r 127 --two-colors.1.g 0 --two-colors.1.b 0
TupleType(color=(255, 0, 0), two_colors=(Color(r=255, g=0, b=0), Color(r=127, g=0, b=0)))
.. _example-06_pydantic:
Pydantic integration
--------------------
In addition to standard dataclasses, :func:`tyro.cli()` also supports
`Pydantic
$ python ./06_pydantic.py --help
usage: ./06_pydantic.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 (required) │
│ --field2 INT An integer field. (default: 3) │
╰───────────────────────────────────────────────╯
.. raw:: html
$ python ./06_pydantic.py --field1 hello
field1='hello' field2=3
.. raw:: html
$ python ./06_pydantic.py --field1 hello --field2 5
field1='hello' field2=5
.. _example-07_attrs:
Attrs integration
-----------------
In addition to standard dataclasses, :func:`tyro.cli()` also supports
`attrs
$ python ./07_attrs.py --help
usage: ./07_attrs.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 required integer field. (default: 5) │
╰──────────────────────────────────────────────────────╯
.. raw:: html
$ python ./07_attrs.py --field1 hello
Args(field1='hello', field2=5)
.. raw:: html
$ python ./07_attrs.py --field1 hello --field2 5
Args(field1='hello', field2=5)
.. _example-08_ml_collections:
ML collections
--------------
:func:`tyro.cli` understands and can populate config objects implemented using
`ml_collections
$ python ./08_ml_collections.py --help
usage: ./08_ml_collections.py [-h] [OPTIONS]
Train a model.
╭─ options ──────────────────────────────────────────╮
│ -h, --help │
│ show this help message and exit │
╰────────────────────────────────────────────────────╯
╭─ config.network options ───────────────────────────╮
│ --config.network.policy-layer-dims [INT [INT ...]] │
│ (default: 128 128 128) │
│ --config.network.policy-obs-key STR │
│ (default: state) │
│ --config.network.value-layer-dims [INT [INT ...]] │
│ (default: 256 256 256 256 256) │
│ --config.network.value-obs-key STR │
│ (default: state) │
╰────────────────────────────────────────────────────╯
╭─ config.wandb options ─────────────────────────────╮
│ --config.wandb.mode STR │
│ (default: online) │
│ --config.wandb.project STR │
│ (default: robot-sandbox) │
╰────────────────────────────────────────────────────╯
.. raw:: html
$ python ./08_ml_collections.py --config.network.policy-layer-dims 64 64 64
{'network': {'policy_layer_dims': (64, 64, 64),
'policy_obs_key': 'state',
'value_layer_dims': (256, 256, 256, 256, 256),
'value_obs_key': 'state'},
'wandb': {'mode': 'online', 'project': 'robot-sandbox'}}
.. _example-09_ml_collections_refs:
ML collections + field references
---------------------------------
``ml_collections`` supports references for sharing values across multiple fields.
.. code-block:: python
:linenos:
# 09_ml_collections_refs.py
from pprint import pprint
from ml_collections import ConfigDict, FieldReference # type: ignore
import tyro
def get_config() -> ConfigDict:
config = ConfigDict()
# Placeholder.
hidden_dim_ref = FieldReference(128)
config.hidden_dim = hidden_dim_ref
# Wandb config.
config.wandb = ConfigDict()
config.wandb.mode = "online" # online, offline, disabled.
config.wandb.project = "robot-sandbox"
# Network config.
# Updating `policy_hidden_dim` will update `value_hidden_dim`, but
# updating `value_hidden_dim` will not update `policy_hidden_dim`.
config.network = ConfigDict()
config.network.policy_hidden_dim = hidden_dim_ref
config.network.value_hidden_dim = hidden_dim_ref * 2
config.network.policy_obs_key = "state"
config.network.value_obs_key = "state"
return config
def train(config: ConfigDict = get_config()) -> None:
"""Train a model."""
pprint(config.to_dict()) # type: ignore
if __name__ == "__main__":
tyro.cli(train)
.. raw:: html
$ python ./09_ml_collections_refs.py --help
usage: ./09_ml_collections_refs.py [-h] [OPTIONS]
Train a model.
╭─ options ─────────────────────────────────────────╮
│ -h, --help │
│ show this help message and exit │
╰───────────────────────────────────────────────────╯
╭─ config options ──────────────────────────────────╮
│ --config.hidden-dim INT │
│ Reference default: 128. (assigns reference) │
╰───────────────────────────────────────────────────╯
╭─ config.network options ──────────────────────────╮
│ --config.network.policy-hidden-dim INT │
│ Reference default: 128. (assigns reference) │
│ --config.network.policy-obs-key STR │
│ (default: state) │
│ --config.network.value-hidden-dim INT │
│ Reference default: 256. (assigns reference) │
│ --config.network.value-obs-key STR │
│ (default: state) │
╰───────────────────────────────────────────────────╯
╭─ config.wandb options ────────────────────────────╮
│ --config.wandb.mode STR │
│ (default: online) │
│ --config.wandb.project STR │
│ (default: robot-sandbox) │
╰───────────────────────────────────────────────────╯
.. raw:: html
$ python ./09_ml_collections_refs.py --config.hidden-dim 32
{'hidden_dim': 32,
'network': {'policy_hidden_dim': 32,
'policy_obs_key': 'state',
'value_hidden_dim': 64,
'value_obs_key': 'state'},
'wandb': {'mode': 'online', 'project': 'robot-sandbox'}}
.. raw:: html
$ python ./09_ml_collections_refs.py --config.network.policy-hidden-dim 64
{'hidden_dim': 64,
'network': {'policy_hidden_dim': 64,
'policy_obs_key': 'state',
'value_hidden_dim': 128,
'value_obs_key': 'state'},
'wandb': {'mode': 'online', 'project': 'robot-sandbox'}}
.. _example-10_msgspec:
Msgspec integration
-------------------
In addition to standard dataclasses, :func:`tyro.cli()` also supports
`msgspec
$ python ./10_msgspec.py --help
usage: ./10_msgspec.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 required integer field. (default: 5) │
╰──────────────────────────────────────────────────────╯
.. raw:: html
$ python ./10_msgspec.py --field1 hello
Args(field1='hello', field2=5)
.. raw:: html
$ python ./10_msgspec.py --field1 hello --field2 5
Args(field1='hello', field2=5)