typed-args
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,list→nargs,Optional/defaults → optional args, required scalars → positionals. Arg(...)is the argparse passthrough. Attach it viaAnnotated[T, Arg(...)]for option strings,help,metavar,action,nargs,const, ... It overlays argparse params on top of the type-derived defaults.- Docstrings become
helpautomatically. A bare string literal on the line after a field is used as itshelptext (via pydantic'suse_attribute_docstrings, on by default). Precedence:Arg(help=...)>Field(description=...)> attribute docstring. - Validation is real.
pydanticcoerces and validates the parsed namespace, soField(...),field_validator, and structured error messages all work. - Parser config is
model_config. UseParserConfig(prog=..., description=...), which subclasses pydantic'sConfigDictand mixes freely with pydantic config (frozen,str_strip_whitespace, ...). For full control, pass your ownargparse.ArgumentParserviaArgs.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
TypedArgs—pydantic.BaseModelsubclass addingparse_args(argv=None, *, parser=None)andparse_known_args(...)classmethods.ParserConfig—ConfigDictsubclass withargparse.ArgumentParserfields.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 subclassTypedArgs.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 |
| Subcommand help from class docstring / explicit config | ✓ | ParserConfig(subcommand_help=..., description=...) |
Subparsers section title / description / prog / metavar |
✓ | Subparsers(...) marker |
Subcommand aliases (add_parser(aliases=...)) |
✗ | not implemented |
Other per-subparser config (prog / formatter_class) |
✗ | 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 |
Metadata
Release files for typed-args 0.9.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| typed_args-0.9.0.tar.gz | 13.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| typed_args-0.9.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 30.7 kB
Release files / typed_args-0.9.0.tar.gz
| Download URL | typed_args-0.9.0.tar.gz |
|---|---|
| Size | 13.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
836e247556f1fea74672d732870957a199c8c5bc76955753f279db023b6bc371
|
|
BLAKE2b-256 checksum How to use checksums |
14d2d3064a840a1334414dd81f1cec3ed670ad8b3ae6470e653d556c7c0764b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / typed_args-0.9.0-py3-none-any.whl
| Download URL | typed_args-0.9.0-py3-none-any.whl |
|---|---|
| Size | 16.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5d79c169561df278f397d5b7dbbafec747500dbd3590154b89184106de64e856
|
|
BLAKE2b-256 checksum How to use checksums |
4f23d4ac3350bde285d5bc8d5ebfca509d8e499e88721d35711f68311e1dafea
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Security for typed-args 0.9.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.