Skip to main content

Parse command line arguments by defining pydantic models

Project description

typed-args

PyPI version Python versions Python package Publish

A typed command-line argument parser for Python, inspired by Rust's clap. Define a pydantic model — its types drive the CLI, and parsing runs real runtime validation. Subcommands are discriminated unions you dispatch with match.

Requires Python 3.10+ and pydantic 2.

Installation

pip install typed-args

A first example

from typing import Annotated, List

import typed_args as ta
from pydantic import Field


class Args(ta.TypedArgs):
    model_config = ta.ParserConfig(description="Process some integers.")
    integers: Annotated[List[int], ta.Arg(metavar="N", nargs="+", help="an integer for the accumulator")]
    workers: Annotated[int, Field(gt=0, le=32), ta.Arg("-w", "--workers", help="worker count")] = 4


args = Args.parse_args()
print(args.integers, "max =", max(args.integers), "workers =", args.workers)
$ python prog.py 1 2 3 4 -w 8
[1, 2, 3, 4] max = 4 workers = 8

$ python prog.py 1 -w 99
workers
  Input should be less than or equal to 32 ...

The Field(gt=0, le=32) constraint is enforced at parse time — workers=99 raises a pydantic.ValidationError instead of silently producing a bad value.

How it works

  • Types drive structure. bool--flag (store_true), Literal[...]choices, listnargs, Optional/defaults → optional args, required scalars → positionals.
  • Arg(...) is the argparse passthrough. Attach it via Annotated[T, Arg(...)] for option strings, help, metavar, action, nargs, const, ... It overlays argparse params on top of the type-derived defaults.
  • Docstrings become help automatically. A bare string literal on the line after a field is used as its help text (via pydantic's use_attribute_docstrings, on by default). Precedence: Arg(help=...) > Field(description=...) > attribute docstring.
  • Validation is real. pydantic coerces and validates the parsed namespace, so Field(...), field_validator, and structured error messages all work.
  • Parser config is model_config. Use ParserConfig(prog=..., description=...), which subclasses pydantic's ConfigDict and mixes freely with pydantic config (frozen, str_strip_whitespace, ...). For full control, pass your own argparse.ArgumentParser via Args.parse_args(parser=...).

Subcommands

Subcommands are a pydantic discriminated union; dispatch with match:

from typing import Annotated, Literal, Union

import typed_args as ta
from pydantic import Field


class GlobalArgs(ta.TypedArgs):
    verbose: Annotated[bool, ta.Arg("-v", "--verbose")] = False


class AddArgs(ta.TypedArgs):
    cmd: Literal["add"] = "add"
    file: Annotated[str, ta.Arg(help="file to add")]
    force: Annotated[bool, ta.Arg("--force")] = False


class RemoveArgs(ta.TypedArgs):
    cmd: Literal["remove"] = "remove"
    file: Annotated[str, ta.Arg(help="file to remove")]
    recursive: Annotated[bool, ta.Arg("-r", "--recursive")] = False


class Root(ta.TypedArgs):
    common: GlobalArgs
    subcommand: Annotated[Union[AddArgs, RemoveArgs], Field(discriminator="cmd")]


root = Root.parse_args()
match root.subcommand:
    case AddArgs(file=f, force=True):
        print("force-add", f)
    case AddArgs(file=f):
        print("add", f)
    case RemoveArgs(file=f, recursive=True):
        print("recursive remove", f)
    case RemoveArgs(file=f):
        print("remove", f)

Global flags (the common field) work before the subcommand (argparse behavior); after-subcommand globals are not supported.

API

  • TypedArgspydantic.BaseModel subclass adding parse_args(argv=None, *, parser=None) and parse_known_args(...) classmethods.
  • ParserConfigConfigDict subclass with argparse.ArgumentParser fields.
  • Arg(*option_strings, **kwargs) — argparse passthrough marker for a field.
  • parse(model, argv=None, *, parser=None) / parse_known_args(...) — free functions for users who don't subclass TypedArgs.
  • DefaultHelpFormatter — strips dotted dest prefixes from help output.

Feature support

Feature Status How / note
Args
Positional / optional Arg() / Arg("-f", "--foo")
action (store_true / store_const / append / count / custom) auto for bool; Arg(action=...)
nargs auto * for list; Arg(nargs=...)
const / default / type / choices / required / help / metavar Arg(...) passthrough
Custom Action via Arg(**kwargs)
dest override rejected — dest is the field-path link to the model
Types & validation
Type-driven structure (bool→flag, Literal→choices, list→nargs, required→positional)
Field constraints (gt/le/…), field_validator, ValidationError pydantic at parse time
Help
Attribute docstring → help use_attribute_docstrings, on by default
help precedence: Arg(help=) > Field(description=) > docstring
DefaultHelpFormatter (strips dotted dest)
Parser config
prog / description / epilog / prefix_chars / fromfile_prefix_chars / argument_default / conflict_handler / add_help / allow_abbrev / exit_on_error / formatter_class ParserConfig in model_config
Custom ArgumentParser (escape hatch) parse_args(parser=...)
Subcommands
Discriminated union + match dispatch
Optional subcommand (required=False) Optional[Union[...]] = None
Nested subcommands recursive
Subparsers section title / description / prog / metavar Subparsers(...) marker
Subcommand aliases (add_parser(aliases=...)) not implemented
Per-subparser config (prog / description / formatter_class / help) not implemented
Groups
Argument group (titled section) Group(...) marker
Mutually exclusive group Mutex(...) marker
Composition
Nested model as a field (library args as an attribute)
Merge into a host argparse parser add_arguments / from_namespace
Global args before the subcommand nested model on the main parser
Global args after the subcommand (clap global=true) cut — put globals before the subcommand
Prefixing a nested model's flags (--lib-host) decided against
Parsing
parse_args / parse_known_args
parse_intermixed_args not exposed

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

typed_args-0.8.0.tar.gz (9.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

typed_args-0.8.0-py3-none-any.whl (12.2 kB view details)

Uploaded Python 3

File details

Details for the file typed_args-0.8.0.tar.gz.

File metadata

  • Download URL: typed_args-0.8.0.tar.gz
  • Upload date:
  • Size: 9.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for typed_args-0.8.0.tar.gz
Algorithm Hash digest
SHA256 330f1633d123dbc531920329c623fea5aa384c1b21b33066a6dc7cf15b365155
MD5 2930a4bd63507f408ceb33aa76d08577
BLAKE2b-256 6e9dab9034a2c641f6cf15df72ef0411f5881facbc3831dd71a5ff5f99169c7a

See more details on using hashes here.

File details

Details for the file typed_args-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: typed_args-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 12.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for typed_args-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0a7c01c825a6759e149b08964383620508b08b0179a395bd8e75a71fc0f3e111
MD5 70ea952a2f07126068a4e5b1f30c0a26
BLAKE2b-256 a87423ec34aaf0248fb57b8f0152a8039c2fdf02e0f37e2cf8f3f4c57eef3a8e

See more details on using hashes here.

Supported by

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