Goals and alternatives#

Design goals#

The core functionality of tyro — generating argument parsers from type annotations — overlaps significantly with features offered by other libraries. Usage distinctions are the result of two API goals:

  • One uninvasive function. For all core functionality, learning to use tyro should reduce to learning to write type-annotated Python. For example, types are specified using standard annotations, helptext using docstrings, choices using the standard typing.Literal type, subcommands with typing.Union of nested types, and positional arguments with /.

    • In contrast, similar libraries have more expansive APIs , and require more library-specific structures, decorators, or metadata formats for configuring parsing behavior.

  • Strict typing. Any type that can be annotated and unambiguously parsed with an argparse-style CLI interface should work out-of-the-box; any public API that isn’t statically analyzable should be avoided.

    • In contrast, many similar libraries implement features that depend on dynamic argparse-style namespaces, or string-based accessors that can’t be statically checked.

More concretely, we can also compare specific features. A noncomprehensive set:

Dataclasses

Functions

Literals

Docstrings as helptext

Nested structures

Unions over primitives

Unions over nested types

Lists, tuples

Dictionaries

Generics

argparse-dataclass

argparse-dataclasses

datargs

[1]

[2]

tap

~[3]

simple-parsing

[4]

[5]

dataclass-cli

clout

hf_argparser

typer

~[6]

~[7]

pyrallis

yahp

~[8]

~[9]

omegaconf

tyro

Note that other libraries are generally aimed specifically at only one of dataclasses (datargs, simple-parsing, argparse-dataclass, argparse-dataclasses, dataclass-cli, clout, hf_argparser, pyrallis, yahp), custom structures (tap), or functions (typer) rather than general types and callables, but offer other features that you might find critical, such as registration for custom types (pyrallis), built-in approaches for serialization and config files (tap, pyrallis, yahp), and opportunities for integration with fields generated using standard argparse definitions (simple-parsing).

Note on configuration systems#

Our API is designed to be used to configure general applications and computational experiments written in Python, but intentionally tries to avoid building a full configuration framework (for example, hydra). These frameworks can typically be broken into three components with varying levels of integration, which include syntax and logic for (1) defining configurable fields, (2) saving and choosing between a set of base configurations, and (3) overriding configurable values at the command-line.

tyro is meant to take advantage of modern Python features for (1) and focus completely on (3) in a way that’s agnostic to a project’s preferred approach for (2). (2) is left as an exercise to the user; it tends to be the most open-ended and varied in terms of project and personal preferences, but is typically straightforward to implement given (1).

In contrast, popular libraries oriented toward configuration often strive to be more “batteries-included”, which is convenient but requires prescribing things like specific formats, processes, or directory structures for working with saved configurations. This requires more moving parts, which can be limiting if any one of them is insufficient for a particular use case. (or if you just don’t like working with YAML formats)