Parse command line arguments by defining pydantic models
Project description
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 |
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
330f1633d123dbc531920329c623fea5aa384c1b21b33066a6dc7cf15b365155
|
|
| MD5 |
2930a4bd63507f408ceb33aa76d08577
|
|
| BLAKE2b-256 |
6e9dab9034a2c641f6cf15df72ef0411f5881facbc3831dd71a5ff5f99169c7a
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0a7c01c825a6759e149b08964383620508b08b0179a395bd8e75a71fc0f3e111
|
|
| MD5 |
70ea952a2f07126068a4e5b1f30c0a26
|
|
| BLAKE2b-256 |
a87423ec34aaf0248fb57b8f0152a8039c2fdf02e0f37e2cf8f3f4c57eef3a8e
|