Skip to main content

Strongly typed, zero-effort CLI interfaces

Project description


tyro logo

Documentation   •   pip install tyro

build mypy lint codecov codecov


tyro is a tool for generating command-line interfaces and configuration objects from type-annotated Python. We:

  • Introduce a single-function core API, tyro.cli(), which is minimal enough to use in throwaway scripts but flexible enough to be hardened in larger projects.
  • Support a broad range of Python type constructs, including basics (int, str, bool, float, pathlib.Path, ...), both fixed- and variable-length containers (list[T], tuple[T1, T2, ...], set[T], dict[K, V]), unions (X | Y, Union[X, Y]), literals (Literal), and generics (TypeVar).
  • Understand hierarchy, nesting, and tools you may already use, like dataclasses, pydantic, and attrs.
  • Generate helptext automatically from defaults, annotations, and docstrings.
  • Provide flexible support for subcommands, as well as choosing between and overriding values in configuration objects.
  • Enable tab completion in both your IDE and terminal.
  • Expose fine-grained configuration via PEP 529 runtime annotations (tyro.conf.*).

tyro's use cases overlaps significantly with many other tools. The differences are a result of several API goals:

  • Focusing on a single, uninvasive function.
  • Prioritizing compatibility with language servers and type checkers.
  • Avoiding assumptions on serialization formats (like YAML or JSON) for configuration objects.

Brief walkthrough

To summarize how tyro.cli() can be used, let's consider a script based on argparse. We define two inputs and print the sum:

"""Sum two numbers from argparse."""
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--a", type=int, required=True)
parser.add_argument("--b", type=int, default=3)
args = parser.parse_args()

total = args.a + args.b

print(total)

This pattern is dramatically cleaner than manually parsing sys.argv, but has several issues: it requires a significant amount of parsing-specific boilerplate, lacks type checking and IDE support (consider: jumping to definitions, finding references, docstrings, refactoring and renaming tools), and becomes difficult to manage for larger projects.

The basic goal of tyro.cli() is to provide a wrapper for argparse that solves these issues.

(1) Command-line interfaces from functions.

We can write the same script as above using tyro.cli():

"""Sum two numbers by calling a function with tyro."""
import tyro

def add(a: int, b: int = 3) -> int:
    return a + b

# Populate the inputs of add(), call it, then return the output.
total = tyro.cli(add)

print(total)

Or, more succinctly:

"""Sum two numbers by calling a function with tyro."""
import tyro

def add(a: int, b: int = 3) -> None:
    print(a + b)

tyro.cli(add)  # Returns `None`.

(2) Command-line interfaces from config objects.

A class in Python can be treated as a function that returns an instance. This makes it easy to populate explicit configuration structures:

"""Sum two numbers by instantiating a dataclass with tyro."""
from dataclasses import dataclass

import tyro

@dataclass
class Args:
    a: int
    b: int = 3

args = tyro.cli(Args)
print(args.a + args.b)

In the wild

tyro is still a new library, but being stress tested in several projects!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tyro-0.5.10.tar.gz (105.8 kB view hashes)

Uploaded Source

Built Distribution

tyro-0.5.10-py3-none-any.whl (94.2 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page