Skip to main content

A small typed parser generator for lightweight Python CLIs.

Project description

typed-argparse

CI Python uv Ruff ty SemVer License

typed-argparse is a small annotation-driven CLI helper for people who want typed parser generation without adopting a large framework.

It is aimed at simple command-line tools, scripting utilities, and lightweight LLM-facing CLIs where you want a small API surface, explicit Python functions, and standard argparse behavior instead of a large abstraction layer.

The core package has no required third-party runtime dependencies. The main API is build_parser(), which converts a typed function signature into an argparse-compatible parser. The package also includes Argument, Option, unwrap(), call(), parse_and_call(), and build_subcommand_parser(), plus optional cmd2 integration through with_annotated().

Why use it:

  • small API surface
  • typed function signatures instead of command classes or decorators everywhere
  • easy argparse.Namespace -> kwargs conversion through unwrap()
  • a good fit for short-lived tools and simple LLM CLI wrappers
  • optional cmd2 integration when you want an interactive shell

Install

The package is published on PyPI as typed-argparse-gen and imported as typed_argparse.

uv add typed-argparse-gen

For cmd2 integration:

uv add "typed-argparse-gen[cmd2]"

Main feature: build_parser()

Use build_parser() when you want annotation-driven parser generation directly.

import argparse
from typing import Annotated

from typed_argparse import Argument, Option, build_parser


def greet(
    name: Annotated[str, Argument(help_text="Who to greet")],
    count: Annotated[int, Option("--count", "-c", help_text="Number of greetings")] = 1,
    loud: bool = False,
) -> None:
    pass


parser = build_parser(greet)
args = parser.parse_args(["Kelvin", "--count", "2", "--loud"])

assert isinstance(parser, argparse.ArgumentParser)
assert args.name == "Kelvin"
assert args.count == 2
assert args.loud is True

build_parser_from_function() is still available as an alias.

By default:

  • parameters without defaults become positional arguments
  • parameters with defaults become --option flags
  • bool = False becomes store_true
  • bool = True becomes store_false on --no-name
  • Annotated[..., Argument(...)] and Annotated[..., Option(...)] override the defaults

Unwrapping and calling

Use unwrap() to convert a parsed argparse.Namespace into plain values. Use call() and parse_and_call() for the common dispatch path.

from typed_argparse import build_parser, call, parse_and_call, unwrap

parser = build_parser(greet)
namespace = parser.parse_args(["Kelvin", "--count", "2"])

kwargs = unwrap(namespace)
values = unwrap(namespace, as_tuple=True)

assert kwargs == {"name": "Kelvin", "count": 2, "loud": False}
assert values == ("Kelvin", 2, False)

assert call(greet, namespace) == ["Hello Kelvin", "Hello Kelvin"]
assert parse_and_call(greet, ["Kelvin", "--loud"]) == ["HELLO KELVIN"]

Subcommands

Use build_subcommand_parser() or parse_and_call() with a mapping for small multi-command CLIs.

from typed_argparse import build_subcommand_parser, parse_and_call

commands = {"greet": greet, "tag": tag}

parser = build_subcommand_parser(commands)
namespace = parser.parse_args(["tag", "release", "--labels", "stable"])

result = parse_and_call(commands, ["tag", "release", "--labels", "stable"])

Optional cmd2 support

If you are using cmd2, you can use the same metadata model with with_annotated(). Install the optional cmd2 extra first.

For typed code, prefer direct imports:

import cmd2
from typing import Annotated

from typed_argparse import Argument, Option, with_annotated


class App(cmd2.Cmd):
    def sport_choices(self) -> list[str]:
        return ["football", "basketball", "tennis"]

    @with_annotated
    def do_play(
        self,
        sport: Annotated[str, Argument(choices_provider=sport_choices)],
        venue: Annotated[str, Option("--venue", "-v")] = "home",
    ) -> None:
        self.poutput(f"{sport=} {venue=}")

Development

uv sync --group dev
uv run ruff check .
uv run ty check
uv run pytest
uv build

Release flow

This repo is configured for semantic versioning with conventional commits.

  • Push conventional commits to main
  • GitHub Actions runs tests and lint with uv
  • python-semantic-release determines the next version, updates CHANGELOG.md, tags the release, and publishes to PyPI

Expected commit prefixes include feat:, fix:, and perf:. Breaking changes should use ! or a BREAKING CHANGE: footer.

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_argparse_gen-0.1.0.tar.gz (25.0 kB view details)

Uploaded Source

Built Distribution

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

typed_argparse_gen-0.1.0-py3-none-any.whl (19.3 kB view details)

Uploaded Python 3

File details

Details for the file typed_argparse_gen-0.1.0.tar.gz.

File metadata

  • Download URL: typed_argparse_gen-0.1.0.tar.gz
  • Upload date:
  • Size: 25.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for typed_argparse_gen-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ffb04f37a15cbeaf4fa97619cd6a5d763606a510208fd78a9bd92c4d5496e7e6
MD5 e843d01c2bba928adddbeed1d1c4b301
BLAKE2b-256 3506dbcff16a9faa654b85ef966e632bfb7743631f9522601632e0a9491258f4

See more details on using hashes here.

Provenance

The following attestation bundles were made for typed_argparse_gen-0.1.0.tar.gz:

Publisher: release.yml on KelvinChung2000/typed-argparse

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file typed_argparse_gen-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for typed_argparse_gen-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a72e0ffcccdf1a3d47923afbb1c7a0b8534bde1e5df532d12c3b50446f666178
MD5 27cc21680e9d4017dae8b89b961fd711
BLAKE2b-256 4211aff8f226c5b9acb4cb80d2cadcc3bf9e7b60a1222cad6b5cb2b51cbe1ef7

See more details on using hashes here.

Provenance

The following attestation bundles were made for typed_argparse_gen-0.1.0-py3-none-any.whl:

Publisher: release.yml on KelvinChung2000/typed-argparse

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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