Skip to content

Latest commit

 

History

History
166 lines (122 loc) · 4.82 KB

File metadata and controls

166 lines (122 loc) · 4.82 KB

CLAUDE.md

What is argclass

Declarative CLI parser for Python over argparse. Type hints → CLI args automatically. str→string, bool→flag, Optional[T]→optional, list[T]→multi-value, Literal[...]→choices. Priority: defaults < config files < env vars < CLI args. Zero deps, stdlib only, Python 3.10-3.14.

Commands

uv sync                                    # install deps
uv run pytest -vv --cov=argclass --cov-report=term-missing --doctest-modules tests  # full test suite
uv run pytest tests/test_simple.py::TestClassName::test_method -vv  # single test
uv run ruff check                          # lint
uv run ruff format --check                 # verify formatting
uv run ruff format                         # apply formatting
uv run mypy                                # type checking

Examples

Minimal parser

import argclass

class CLI(argclass.Parser):
    host: str = "localhost"      # --host (default: localhost)
    port: int = 8080             # --port (default: 8080)
    verbose: bool = False        # --verbose flag (store_true)

parser = CLI()
parser.parse_args()

Optional, list, and literal types

from typing import Optional, Literal
import argclass

class CLI(argclass.Parser):
    name: Optional[str] = None               # --name (optional)
    tags: list[str] = argclass.Argument(      # --tags a b c
        nargs=argclass.Nargs.ONE_OR_MORE,
    )
    mode: Literal["fast", "slow"] = "fast"   # --mode {fast,slow}

Argument groups

import argclass

class Database(argclass.Group):
    host: str = "localhost"
    port: int = 5432

class CLI(argclass.Parser):
    debug: bool = False
    db: Database = Database(title="Database options")

parser = CLI()
parser.parse_args()
print(parser.db.host, parser.db.port)

Nested groups (Group inside Group)

Groups can contain other Groups for arbitrary nesting:

import argclass

class Credentials(argclass.Group):
    username: str = "admin"
    password: str = "secret"

class Endpoint(argclass.Group):
    host: str = "localhost"
    credentials: Credentials = Credentials()

class CLI(argclass.Parser):
    endpoint: Endpoint = Endpoint()

parser = CLI()
parser.parse_args([
    "--endpoint-host=10.0.0.1",
    "--endpoint-credentials-username=root",
])

Naming follows the attribute path:

  • CLI: --endpoint-credentials-username
  • ENV (with auto_env_var_prefix="APP_"): APP_ENDPOINT_CREDENTIALS_USERNAME
  • INI: section [endpoint.credentials], key username
  • JSON/TOML: nested objects/tables

Group(prefix=...) overrides only the CLI/env segment for that group; config section names always follow the attribute path. Reusing the same Group instance in two places raises ArgclassError (instantiate a separate Group per attribute).

Subcommands

import argclass

class Serve(argclass.Parser):
    port: int = 8080

class Deploy(argclass.Parser):
    target: str = "production"

class CLI(argclass.Parser):
    serve = Serve()
    deploy = Deploy()

Environment variables and secrets

import argclass

class CLI(argclass.Parser):
    db_url: str = argclass.Argument(env_var="DATABASE_URL")
    api_key: str = argclass.Secret()  # masked in repr/logs

# auto_env_var_prefix generates env vars from arg names
parser = CLI(auto_env_var_prefix="APP_")
parser.parse_args()
parser.sanitize_env(only_secrets=True)

Testing parsers

class CLI(argclass.Parser):
    debug: bool = False
    db: Database = Database(title="Database options")

parser = CLI()
parser.parse_args(["--debug", "--db-host=127.0.0.1", "--db-port", "9876"])
assert parser.debug is True
assert parser.db.host == "127.0.0.1"

Architecture

Metaclass-driven: Meta metaclass in parser.py processes annotations at class definition time, creating __arguments__, __argument_groups__, __subparsers__.

  • parser.pyMeta, Base, Parser, Group, Destination. Argparse integration, config files, env vars.
  • factory.pyArgument, ArgumentSingle, ArgumentSequence, EnumArgument, Secret, Config, LogLevel factories.
  • store.pyStoreMeta/Store for typed field storage; ArgumentBase/TypedArgument extend it.
  • defaults.py — Config file parsers (INI/JSON/TOML) implementing AbstractDefaultsParser.
  • actions.py — Custom argparse Action subclasses.
  • secret.pySecretString masks values in repr/logging.
  • exceptions.pyArgclassError hierarchy with suggestions.
  • types.py — Type aliases, enums (Actions, Nargs, LogLevelEnum), constants.
  • utils.pyparse_bool, read_ini_configs, type introspection helpers, annotation merging.

Code Style

  • Line length: 80 chars (ruff). Rules: E, F, W, C90 (C901 ignored)
  • mypy strict for argclass/, relaxed for tests/
  • Build backend: hatchling