{ "cells": [ { "cell_type": "markdown", "id": "f40e4004", "metadata": {}, "source": [ "# Argument Prefixing\n", "\n", "When nesting structures in `tyro`, arguments are prefixed with the parent\n", "field name to avoid naming collisions. For example, a field `learning_rate`\n", "inside a field called `optimizer` becomes `--optimizer.learning-rate`.\n", "\n", "This page covers ways to control this prefixing behavior." ] }, { "cell_type": "code", "execution_count": null, "id": "e9799263", "metadata": { "execution": { "iopub.execute_input": "2026-03-18T08:44:35.724463Z", "iopub.status.busy": "2026-03-18T08:44:35.724049Z", "iopub.status.idle": "2026-03-18T08:44:35.765262Z", "shell.execute_reply": "2026-03-18T08:44:35.764868Z" }, "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "import dataclasses\n", "from typing import Annotated, Union\n", "\n", "import tyro\n", "\n", "# Monkeypatch tyro.cli so --help prints without exiting.\n", "_original_cli = tyro.cli\n", "\n", "\n", "def _patched_cli(*args, **kwargs):\n", " try:\n", " return _original_cli(*args, **kwargs)\n", " except SystemExit:\n", " pass\n", "\n", "\n", "tyro.cli = _patched_cli\n", "\n", "# Force ANSI colors and UTF-8 box drawing characters in output.\n", "tyro._fmtlib._FORCE_ANSI = True\n", "tyro._fmtlib._FORCE_UTF8_BOXES = True" ] }, { "cell_type": "markdown", "id": "dab55b41", "metadata": {}, "source": [ "## Arguments\n", "\n", "### Default: arguments are prefixed\n", "\n", "Consider two nested dataclasses. The inner fields are automatically prefixed\n", "with the outer field name:" ] }, { "cell_type": "code", "execution_count": null, "id": "12e2dffe", "metadata": { "execution": { "iopub.execute_input": "2026-03-18T08:44:35.767678Z", "iopub.status.busy": "2026-03-18T08:44:35.767528Z", "iopub.status.idle": "2026-03-18T08:44:35.778217Z", "shell.execute_reply": "2026-03-18T08:44:35.777895Z" } }, "outputs": [], "source": [ "@dataclasses.dataclass\n", "class OptimizerConfig:\n", " learning_rate: float = 1e-3\n", " weight_decay: float = 0.01\n", "\n", "\n", "@dataclasses.dataclass\n", "class Config:\n", " optimizer: OptimizerConfig\n", " epochs: int = 10\n", "\n", "\n", "tyro.cli(Config, args=[\"--help\"], prog=\"script.py\")" ] }, { "cell_type": "markdown", "id": "53fcfebd", "metadata": {}, "source": [ "### `prefix_name=False`: drop the parent's prefix\n", "\n", "`tyro.conf.arg(prefix_name=False)` on a field means \"don't prefix **me**\n", "with the prefix inherited from my parent.\"\n", "\n", "To see the effect, we need at least two levels of nesting. Below,\n", "`prefix_name=False` on `optimizer` drops the `train.` prefix from\n", "`optimizer` and its children, but `optimizer` itself still appears:" ] }, { "cell_type": "code", "execution_count": null, "id": "7af834fd", "metadata": { "execution": { "iopub.execute_input": "2026-03-18T08:44:35.780368Z", "iopub.status.busy": "2026-03-18T08:44:35.780209Z", "iopub.status.idle": "2026-03-18T08:44:35.785000Z", "shell.execute_reply": "2026-03-18T08:44:35.784688Z" } }, "outputs": [], "source": [ "@dataclasses.dataclass\n", "class OptimizerConfig:\n", " learning_rate: float = 1e-3\n", "\n", "\n", "@dataclasses.dataclass\n", "class TrainConfig:\n", " optimizer: Annotated[OptimizerConfig, tyro.conf.arg(prefix_name=False)]\n", "\n", "\n", "@dataclasses.dataclass\n", "class Experiment:\n", " train: TrainConfig\n", "\n", "\n", "tyro.cli(Experiment, args=[\"--help\"], prog=\"script.py\")" ] }, { "cell_type": "markdown", "id": "61206398", "metadata": {}, "source": [ "Note that `--train.optimizer.learning-rate` became `--optimizer.learning-rate`.\n", "The `train.` prefix was dropped, but `optimizer` still appears." ] }, { "cell_type": "markdown", "id": "2a50bbfa", "metadata": {}, "source": [ "### `name=\"\"`: suppress a field's name from child prefixes\n", "\n", "`tyro.conf.arg(name=\"\")` suppresses the field's own name from the prefix\n", "chain. Children are prefixed with whatever came *above* this field, skipping\n", "the field's name entirely.\n", "\n", "Here, `name=\"\"` on `optimizer` removes `optimizer.` from all child\n", "argument names:" ] }, { "cell_type": "code", "execution_count": null, "id": "82daba0d", "metadata": { "execution": { "iopub.execute_input": "2026-03-18T08:44:35.786804Z", "iopub.status.busy": "2026-03-18T08:44:35.786669Z", "iopub.status.idle": "2026-03-18T08:44:35.791137Z", "shell.execute_reply": "2026-03-18T08:44:35.790868Z" } }, "outputs": [], "source": [ "@dataclasses.dataclass\n", "class OptimizerConfig:\n", " learning_rate: float = 1e-3\n", " weight_decay: float = 0.01\n", "\n", "\n", "@dataclasses.dataclass\n", "class Config:\n", " optimizer: Annotated[OptimizerConfig, tyro.conf.arg(name=\"\")]\n", " epochs: int = 10\n", "\n", "\n", "tyro.cli(Config, args=[\"--help\"], prog=\"script.py\")" ] }, { "cell_type": "markdown", "id": "5eafddac", "metadata": {}, "source": [ "Now `--optimizer.learning-rate` became just `--learning-rate`." ] }, { "cell_type": "markdown", "id": "af8c9e4d", "metadata": {}, "source": [ "### `OmitArgPrefixes`: strip prefixes from all descendants\n", "\n", "`tyro.conf.OmitArgPrefixes` is a marker that strips prefixes added by\n", "the annotated field and all of its descendants. Any prefix accumulated\n", "from ancestors is preserved — only new prefixes below this point are\n", "omitted.\n", "\n", "Here, `OmitArgPrefixes` on `optimizer` causes its children to appear\n", "without the `optimizer.` prefix:" ] }, { "cell_type": "code", "execution_count": null, "id": "e2285f50", "metadata": { "execution": { "iopub.execute_input": "2026-03-18T08:44:35.792842Z", "iopub.status.busy": "2026-03-18T08:44:35.792742Z", "iopub.status.idle": "2026-03-18T08:44:35.796679Z", "shell.execute_reply": "2026-03-18T08:44:35.796384Z" } }, "outputs": [], "source": [ "from tyro.conf import OmitArgPrefixes\n", "\n", "\n", "@dataclasses.dataclass\n", "class OptimizerConfig:\n", " learning_rate: float = 1e-3\n", " weight_decay: float = 0.01\n", "\n", "\n", "@dataclasses.dataclass\n", "class Config:\n", " optimizer: OmitArgPrefixes[OptimizerConfig]\n", " epochs: int = 10\n", "\n", "\n", "tyro.cli(Config, args=[\"--help\"], prog=\"script.py\")" ] }, { "cell_type": "markdown", "id": "222957fe", "metadata": {}, "source": [ "Unlike `name=\"\"` which only suppresses one level, `OmitArgPrefixes`\n", "applies recursively — all descendants lose their prefixes, not just\n", "immediate children." ] }, { "cell_type": "markdown", "id": "725201c6", "metadata": {}, "source": [ "## Subcommands\n", "\n", "The same ideas apply to subcommands. By default, subcommand names are\n", "prefixed with the field name:" ] }, { "cell_type": "code", "execution_count": null, "id": "58dbfc6d", "metadata": { "execution": { "iopub.execute_input": "2026-03-18T08:44:35.798381Z", "iopub.status.busy": "2026-03-18T08:44:35.798253Z", "iopub.status.idle": "2026-03-18T08:44:35.802570Z", "shell.execute_reply": "2026-03-18T08:44:35.802319Z" } }, "outputs": [], "source": [ "@dataclasses.dataclass\n", "class Adam:\n", " learning_rate: float = 1e-3\n", " beta1: float = 0.9\n", "\n", "\n", "@dataclasses.dataclass\n", "class SGD:\n", " learning_rate: float = 1e-2\n", " momentum: float = 0.9\n", "\n", "\n", "@dataclasses.dataclass\n", "class Config:\n", " optimizer: Union[Adam, SGD]\n", "\n", "\n", "tyro.cli(Config, args=[\"--help\"], prog=\"script.py\")" ] }, { "cell_type": "markdown", "id": "e6539779", "metadata": {}, "source": [ "### `subcommand(prefix_name=False)`: drop the prefix for one subcommand\n", "\n", "`tyro.conf.subcommand(prefix_name=False)` is applied to a **type within a\n", "union**. It drops the parent prefix from that specific subcommand's name.\n", "\n", "Here, `Adam` opts out of the `optimizer:` prefix. `SGD` is unaffected:" ] }, { "cell_type": "code", "execution_count": null, "id": "be52afef", "metadata": { "execution": { "iopub.execute_input": "2026-03-18T08:44:35.804137Z", "iopub.status.busy": "2026-03-18T08:44:35.804040Z", "iopub.status.idle": "2026-03-18T08:44:35.808002Z", "shell.execute_reply": "2026-03-18T08:44:35.807747Z" } }, "outputs": [], "source": [ "@dataclasses.dataclass\n", "class Adam:\n", " learning_rate: float = 1e-3\n", " beta1: float = 0.9\n", "\n", "\n", "@dataclasses.dataclass\n", "class SGD:\n", " learning_rate: float = 1e-2\n", " momentum: float = 0.9\n", "\n", "\n", "@dataclasses.dataclass\n", "class Config:\n", " optimizer: Union[\n", " Annotated[Adam, tyro.conf.subcommand(prefix_name=False)],\n", " SGD,\n", " ]\n", "\n", "\n", "tyro.cli(Config, args=[\"--help\"], prog=\"script.py\")" ] }, { "cell_type": "markdown", "id": "910aa447", "metadata": {}, "source": [ "### `name=\"\"`: suppress the field name from all subcommand names\n", "\n", "`tyro.conf.arg(name=\"\")` on the union field drops the field name from\n", "**all** subcommand names at once:" ] }, { "cell_type": "code", "execution_count": null, "id": "a5ea4c3e", "metadata": { "execution": { "iopub.execute_input": "2026-03-18T08:44:35.809630Z", "iopub.status.busy": "2026-03-18T08:44:35.809503Z", "iopub.status.idle": "2026-03-18T08:44:35.813446Z", "shell.execute_reply": "2026-03-18T08:44:35.813156Z" } }, "outputs": [], "source": [ "@dataclasses.dataclass\n", "class Adam:\n", " learning_rate: float = 1e-3\n", " beta1: float = 0.9\n", "\n", "\n", "@dataclasses.dataclass\n", "class SGD:\n", " learning_rate: float = 1e-2\n", " momentum: float = 0.9\n", "\n", "\n", "@dataclasses.dataclass\n", "class Config:\n", " optimizer: Annotated[Union[Adam, SGD], tyro.conf.arg(name=\"\")]\n", "\n", "\n", "tyro.cli(Config, args=[\"--help\"], prog=\"script.py\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.4" } }, "nbformat": 4, "nbformat_minor": 5 }