Argument Prefixing¶
When nesting structures in tyro, arguments are prefixed with the parent
field name to avoid naming collisions. For example, a field learning_rate
inside a field called optimizer becomes --optimizer.learning-rate.
This page covers ways to control this prefixing behavior.
Arguments¶
Default: arguments are prefixed¶
Consider two nested dataclasses. The inner fields are automatically prefixed with the outer field name:
@dataclasses.dataclass
class OptimizerConfig:
learning_rate: float = 1e-3
weight_decay: float = 0.01
@dataclasses.dataclass
class Config:
optimizer: OptimizerConfig
epochs: int = 10
tyro.cli(Config, args=["--help"], prog="script.py")
usage: script.py [-h] [OPTIONS]
╭─ options ─────────────────────────────────────╮
│ -h, --help show this help message and exit │
│ --epochs INT (default: 10) │
╰───────────────────────────────────────────────╯
╭─ optimizer options ───────────────────────────╮
│ --optimizer.learning-rate FLOAT │
│ (default: 0.001) │
│ --optimizer.weight-decay FLOAT │
│ (default: 0.01) │
╰───────────────────────────────────────────────╯
prefix_name=False: drop the parent’s prefix¶
tyro.conf.arg(prefix_name=False) on a field means “don’t prefix me
with the prefix inherited from my parent.”
To see the effect, we need at least two levels of nesting. Below,
prefix_name=False on optimizer drops the train. prefix from
optimizer and its children, but optimizer itself still appears:
@dataclasses.dataclass
class OptimizerConfig:
learning_rate: float = 1e-3
@dataclasses.dataclass
class TrainConfig:
optimizer: Annotated[OptimizerConfig, tyro.conf.arg(prefix_name=False)]
@dataclasses.dataclass
class Experiment:
train: TrainConfig
tyro.cli(Experiment, args=["--help"], prog="script.py")
usage: script.py [-h] [--optimizer.learning-rate FLOAT]
╭─ options ───────────────────────────────────╮
│ -h, --help show this help message and exit │
╰─────────────────────────────────────────────╯
╭─ optimizer options ─────────────────────────╮
│ --optimizer.learning-rate FLOAT │
│ (default: 0.001) │
╰─────────────────────────────────────────────╯
Note that --train.optimizer.learning-rate became --optimizer.learning-rate.
The train. prefix was dropped, but optimizer still appears.
name="": suppress a field’s name from child prefixes¶
tyro.conf.arg(name="") suppresses the field’s own name from the prefix
chain. Children are prefixed with whatever came above this field, skipping
the field’s name entirely.
Here, name="" on optimizer removes optimizer. from all child
argument names:
@dataclasses.dataclass
class OptimizerConfig:
learning_rate: float = 1e-3
weight_decay: float = 0.01
@dataclasses.dataclass
class Config:
optimizer: Annotated[OptimizerConfig, tyro.conf.arg(name="")]
epochs: int = 10
tyro.cli(Config, args=["--help"], prog="script.py")
usage: script.py [-h] [--epochs INT] [--learning-rate FLOAT] [--weight-decay FLOAT]
╭─ options ──────────────────────────────────────────────╮
│ -h, --help show this help message and exit │
│ --epochs INT (default: 10) │
│ --learning-rate FLOAT (default: 0.001) │
│ --weight-decay FLOAT (default: 0.01) │
╰────────────────────────────────────────────────────────╯
Now --optimizer.learning-rate became just --learning-rate.
OmitArgPrefixes: strip prefixes from all descendants¶
tyro.conf.OmitArgPrefixes is a marker that strips prefixes added by
the annotated field and all of its descendants. Any prefix accumulated
from ancestors is preserved — only new prefixes below this point are
omitted.
Here, OmitArgPrefixes on optimizer causes its children to appear
without the optimizer. prefix:
from tyro.conf import OmitArgPrefixes
@dataclasses.dataclass
class OptimizerConfig:
learning_rate: float = 1e-3
weight_decay: float = 0.01
@dataclasses.dataclass
class Config:
optimizer: OmitArgPrefixes[OptimizerConfig]
epochs: int = 10
tyro.cli(Config, args=["--help"], prog="script.py")
usage: script.py [-h] [--epochs INT] [--learning-rate FLOAT] [--weight-decay FLOAT]
╭─ options ──────────────────────────────────────────────╮
│ -h, --help show this help message and exit │
│ --epochs INT (default: 10) │
╰────────────────────────────────────────────────────────╯
╭─ optimizer options ────────────────────────────────────╮
│ --learning-rate FLOAT (default: 0.001) │
│ --weight-decay FLOAT (default: 0.01) │
╰────────────────────────────────────────────────────────╯
Unlike name="" which only suppresses one level, OmitArgPrefixes
applies recursively — all descendants lose their prefixes, not just
immediate children.
Subcommands¶
The same ideas apply to subcommands. By default, subcommand names are prefixed with the field name:
@dataclasses.dataclass
class Adam:
learning_rate: float = 1e-3
beta1: float = 0.9
@dataclasses.dataclass
class SGD:
learning_rate: float = 1e-2
momentum: float = 0.9
@dataclasses.dataclass
class Config:
optimizer: Union[Adam, SGD]
tyro.cli(Config, args=["--help"], prog="script.py")
usage: script.py [-h] {optimizer:adam,optimizer:sgd}
╭─ options ───────────────────────────────────────────╮
│ -h, --help show this help message and exit │
╰─────────────────────────────────────────────────────╯
╭─ subcommands ───────────────────────────────────────╮
│ (required) │
│ • optimizer:adam │
│ • optimizer:sgd │
╰─────────────────────────────────────────────────────╯
subcommand(prefix_name=False): drop the prefix for one subcommand¶
tyro.conf.subcommand(prefix_name=False) is applied to a type within a
union. It drops the parent prefix from that specific subcommand’s name.
Here, Adam opts out of the optimizer: prefix. SGD is unaffected:
@dataclasses.dataclass
class Adam:
learning_rate: float = 1e-3
beta1: float = 0.9
@dataclasses.dataclass
class SGD:
learning_rate: float = 1e-2
momentum: float = 0.9
@dataclasses.dataclass
class Config:
optimizer: Union[
Annotated[Adam, tyro.conf.subcommand(prefix_name=False)],
SGD,
]
tyro.cli(Config, args=["--help"], prog="script.py")
usage: script.py [-h] {adam,optimizer:sgd}
╭─ options ──────────────────────────────────────────╮
│ -h, --help show this help message and exit │
╰────────────────────────────────────────────────────╯
╭─ subcommands ──────────────────────────────────────╮
│ (required) │
│ • adam │
│ • optimizer:sgd │
╰────────────────────────────────────────────────────╯
name="": suppress the field name from all subcommand names¶
tyro.conf.arg(name="") on the union field drops the field name from
all subcommand names at once:
@dataclasses.dataclass
class Adam:
learning_rate: float = 1e-3
beta1: float = 0.9
@dataclasses.dataclass
class SGD:
learning_rate: float = 1e-2
momentum: float = 0.9
@dataclasses.dataclass
class Config:
optimizer: Annotated[Union[Adam, SGD], tyro.conf.arg(name="")]
tyro.cli(Config, args=["--help"], prog="script.py")
usage: script.py [-h] {adam,sgd}
╭─ options ───────────────────────────────────╮
│ -h, --help show this help message and exit │
╰─────────────────────────────────────────────╯
╭─ subcommands ───────────────────────────────╮
│ (required) │
│ • adam │
│ • sgd │
╰─────────────────────────────────────────────╯