.. 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 `_ for all :py:class:`TypedDict` features. .. code-block:: python :linenos: # 04_dictionaries.py from typing import TypedDict from typing_extensions import NotRequired import tyro class DictionarySchemaA( TypedDict, # Setting `total=False` specifies that not all keys need to exist. total=False, ): learning_rate: float betas: tuple[float, float] class DictionarySchemaB(TypedDict): learning_rate: NotRequired[float] """NotRequired[] specifies that a particular key doesn't need to exist.""" betas: tuple[float, float] def main( typed_dict_a: DictionarySchemaA, typed_dict_b: DictionarySchemaB, standard_dict: dict[str, float] = { "learning_rate": 3e-4, "beta1": 0.9, "beta2": 0.999, }, ) -> None: assert isinstance(typed_dict_a, dict) assert isinstance(typed_dict_b, dict) assert isinstance(standard_dict, dict) print("Typed dict A:", typed_dict_a) print("Typed dict B:", typed_dict_b) print("Standard dict:", standard_dict) if __name__ == "__main__": tyro.cli(main) .. raw:: html
    $ 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 `_ models. .. code-block:: python :linenos: # 06_pydantic.py from pydantic import BaseModel, Field import tyro class Args(BaseModel): """Description. This should show up in the helptext!""" field1: str field2: int = Field(3, description="An integer field.") if __name__ == "__main__": args = tyro.cli(Args) print(args) .. raw:: html
    $ 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 `_ classes. .. code-block:: python :linenos: # 07_attrs.py import attr import tyro @attr.s class Args: """Description. This should show up in the helptext!""" field1: str = attr.ib() """A string field.""" field2: int = attr.ib(factory=lambda: 5) """A required integer field.""" if __name__ == "__main__": args = tyro.cli(Args) print(args) .. raw:: html
    $ 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)