Skip to main content

Declarative CLI parser with type hints, config files, and environment variables - zero dependencies

Project description

argclass

Coverage Actions Latest Version Python Versions License

Declarative CLI parser with type hints, config files, and environment variables.

Build type-safe command-line interfaces using Python classes. Zero dependencies.

Documentation | PyPI

Installation

pip install argclass

Quick Start

import argclass

class Server(argclass.Parser):
    host: str = "127.0.0.1"
    port: int = 8080
    debug: bool = False

server = Server()
server.parse_args(["--host", "0.0.0.0", "--port", "9000", "--debug"])
assert server.host == "0.0.0.0"
assert server.port == 9000
assert server.debug is True
$ python server.py --host 0.0.0.0 --port 9000 --debug

Features

Feature argclass argparse click/typer
Type hints Yes No Yes
IDE autocompletion Yes No Yes
Config files Built-in No No
Environment variables Built-in No Plugin
Secret masking Built-in No No
Dependencies stdlib stdlib Many

Examples

Type Annotations

import argclass
from pathlib import Path

class Parser(argclass.Parser):
    name: str                    # required
    count: int = 10              # optional with default
    config: Path | None = None   # optional path
    files: list[str]             # list of values

parser = Parser()
parser.parse_args(["--name", "test", "--files", "a.txt", "b.txt"])
assert parser.name == "test"
assert parser.count == 10
assert parser.files == ["a.txt", "b.txt"]

Argument Groups

import argclass

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

class Parser(argclass.Parser):
    debug: bool = False
    db = DatabaseGroup()

parser = Parser()
parser.parse_args(["--db-host", "db.example.com", "--db-port", "3306"])
assert parser.db.host == "db.example.com"
assert parser.db.port == 3306

Groups can also contain other Groups. Names join with - (CLI), _ (env vars), or . (INI/TOML sections):

import argclass

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

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

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

parser = Parser()
parser.parse_args([
    "--endpoint-host", "api.example.com",
    "--endpoint-credentials-username", "root",
])
assert parser.endpoint.host == "api.example.com"
assert parser.endpoint.credentials.username == "root"

See Groups for nested groups in config files, environment variables, and --help.

Configuration Files

Load default values from configuration files. INI by default, JSON/TOML via config_parser_class. See Config Files for details.

import argclass
from pathlib import Path
from tempfile import NamedTemporaryFile

class Parser(argclass.Parser):
    host: str = "localhost"
    port: int = 8080

# Config file content
CONFIG_CONTENT = """
[DEFAULT]
host = example.com
port = 9000
"""

with NamedTemporaryFile(mode="w", suffix=".ini", delete=False) as f:
    f.write(CONFIG_CONTENT)
    config_path = f.name

parser = Parser(config_files=[config_path])
parser.parse_args([])
assert parser.host == "example.com"
assert parser.port == 9000

Path(config_path).unlink()

Tip: Use os.getenv() for dynamic config paths. Multiple files are merged (later overrides earlier), enabling global defaults with user overrides:

import os
import argclass

class Parser(argclass.Parser):
    host: str = "localhost"

parser = Parser(config_files=[
    os.getenv("MYAPP_CONFIG", "/etc/myapp/config.ini"),  # Global defaults
    "~/.config/myapp.ini",  # User overrides (partial config OK)
])

Environment Variables

import os
import argclass

os.environ["APP_HOST"] = "env.example.com"
os.environ["APP_DEBUG"] = "true"

class Parser(argclass.Parser):
    host: str = "localhost"
    debug: bool = False

parser = Parser(auto_env_var_prefix="APP_")
parser.parse_args([])
assert parser.host == "env.example.com"
assert parser.debug is True

del os.environ["APP_HOST"]
del os.environ["APP_DEBUG"]

Generating Config Files

argclass can WRITE config files for a parser — the inverse of reading them. Useful for scaffolding sample configs, dumping the running state, or exporting env-var listings.

import argclass

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

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

parser = CLI()
ini = argclass.INIConfigGenerator().dump_to_string(parser)
assert "[DEFAULT]" in ini
assert "[db]" in ini

env = argclass.EnvConfigGenerator().dump_to_string(
    CLI(auto_env_var_prefix="APP_"),
)
assert "APP_DEBUG=false" in env
assert "APP_DB_HOST=localhost" in env

Ship a --generate-config FILE flag with the built-in action (supply - for stdout):

import argclass

class CLI(argclass.Parser):
    host: str = "localhost"
    generate = argclass.Argument(
        "--generate-config",
        action=argclass.GenerateConfigAction,
        generator=argclass.TOMLConfigGenerator,
    )

Four built-in generators: INIConfigGenerator, JSONConfigGenerator, TOMLConfigGenerator, EnvConfigGenerator. Subclass ConfigGenerator for custom formats. See Generating Config Files for the full guide.

Subcommands

import argclass

class ServeCommand(argclass.Parser):
    """Start the server."""
    host: str = "0.0.0.0"
    port: int = 8080

    def __call__(self) -> int:
        print(f"Serving on {self.host}:{self.port}")
        return 0

class CLI(argclass.Parser):
    verbose: bool = False
    serve = ServeCommand()

if __name__ == "__main__":
    cli = CLI()
    cli.parse_args()
    exit(cli())
$ python app.py serve --host 127.0.0.1 --port 9000
Serving on 127.0.0.1:9000

Secrets

import argclass

class Parser(argclass.Parser):
    api_key: str = argclass.Secret(env_var="API_KEY")

# SecretString prevents accidental logging
# repr() returns '******', str() returns actual value

Argparse Passthrough

Argument() forwards any extra keyword arguments to argparse.add_argument(), so argparse-specific options like version= work out of the box:

import argclass

class CLI(argclass.Parser):
    version = argclass.Argument(
        "-V", "--version",
        action=argclass.Actions.VERSION,
        version="myapp/1.2.3",
    )

try:
    CLI().parse_args(["--version"])
except SystemExit as exc:
    assert exc.code == 0

The same passthrough lets you ship custom argparse.Action subclasses that take their own constructor parameters — for example, a --check-updates flag that queries PyPI:

import argparse, json, urllib.request
from importlib.metadata import version as get_version

class CheckPyPIUpdate(argparse.Action):
    def __init__(self, option_strings, dest, package_name, **kwargs):
        kwargs.setdefault("nargs", 0)
        kwargs.setdefault("default", argparse.SUPPRESS)
        self.package_name = package_name
        super().__init__(option_strings, dest, **kwargs)

    def __call__(self, parser, namespace, values, option_string=None):
        url = f"https://pypi.org/pypi/{self.package_name}/json"
        with urllib.request.urlopen(url, timeout=5) as r:
            latest = json.load(r)["info"]["version"]
        current = get_version(self.package_name)
        if current == latest:
            parser.exit(0, f"{self.package_name} {current} is up to date\n")
        parser.exit(0, f"Update available: {current} -> {latest}\n")

class CLI(argclass.Parser):
    # --check-updates is auto-derived from the attribute name
    check_updates = argclass.Argument(
        action=CheckPyPIUpdate,
        package_name="argclass",  # passthrough kwarg
    )

See Argparse Passthrough Kwargs for the full pattern.

Interactive Examples

Run python -m argclass to explore all features interactively. Each subcommand prints its own source code and demonstrates a different feature:

python -m argclass basic          # str, int, float, bool, Optional
python -m argclass types          # Literal, list, Enum, frozenset
python -m argclass groups         # argument groups with prefixes
python -m argclass secrets        # Secret and SecretString masking
python -m argclass env            # environment variable integration
python -m argclass subcommands    # nested subcommands with __call__

Documentation

Full documentation at docs.argclass.com:

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

argclass-1.9.0.tar.gz (97.9 kB view details)

Uploaded Source

Built Distribution

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

argclass-1.9.0-py3-none-any.whl (48.1 kB view details)

Uploaded Python 3

File details

Details for the file argclass-1.9.0.tar.gz.

File metadata

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

File hashes

Hashes for argclass-1.9.0.tar.gz
Algorithm Hash digest
SHA256 23579d3e98517312d59a42bb4eed83f2282171e4d49cfc25a0c7c2369a1224dd
MD5 0186e943cacb552ecfd308bff65e3fc5
BLAKE2b-256 ea11294e42790d45b37946c5ab46937466e9256968e6d6e9e7c6af3a9b908109

See more details on using hashes here.

Provenance

The following attestation bundles were made for argclass-1.9.0.tar.gz:

Publisher: publish.yml on mosquito/argclass

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

File details

Details for the file argclass-1.9.0-py3-none-any.whl.

File metadata

  • Download URL: argclass-1.9.0-py3-none-any.whl
  • Upload date:
  • Size: 48.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for argclass-1.9.0-py3-none-any.whl
Algorithm Hash digest
SHA256 78ec66b6355bc7011b0a037790b65a274d8f026b639b00f1f2aecec07d8c7c42
MD5 14bc0a266e0c72be92150da6dd538742
BLAKE2b-256 0e9961a785e5b8153d24075044e2fceb3a4f92ec590100e7e81c241db1426ccd

See more details on using hashes here.

Provenance

The following attestation bundles were made for argclass-1.9.0-py3-none-any.whl:

Publisher: publish.yml on mosquito/argclass

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