argclass
Declarative CLI parser with type hints, config files, and environment variables.
Build type-safe command-line interfaces using Python classes. Zero dependencies.
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)
])
To let the end user choose the config file, add
config_argument="--config" — the flag's file becomes argument
defaults (CLI and env vars still win):
import argclass
from pathlib import Path
from tempfile import NamedTemporaryFile
class Parser(argclass.Parser):
host: str = "localhost"
port: int = 8080
with NamedTemporaryFile(mode="w", suffix=".ini", delete=False) as f:
f.write("[DEFAULT]\nhost = example.com\nport = 9000\n")
config_path = f.name
parser = Parser(config_argument="--config")
parser.parse_args(["--config", config_path, "--port", "1234"])
assert parser.host == "example.com" # default from the file
assert parser.port == 1234 # CLI still wins
Path(config_path).unlink()
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. Wire a --generate-config flag and end users dump
a working config straight from the CLI:
import argclass
class CLI(argclass.Parser):
host: str = "localhost"
port: int = 8080
generate_config = argclass.Argument(
action=argclass.GenerateConfigAction,
generator=argclass.TOMLConfigGenerator,
metavar="FILE",
)
myapp --generate-config /etc/myapp.toml # write a file
myapp --generate-config - # print to stdout
The action captures values from class defaults, config_files=,
env vars, and any CLI flags that appear BEFORE
--generate-config, then exits with status 0.
Four built-in generators: INIConfigGenerator,
JSONConfigGenerator, TOMLConfigGenerator, EnvConfigGenerator.
Subclass ConfigGenerator to add your own. See Generating
Config Files
for the full guide (multi-format wiring, env-listings, format
conversion, NonConfigAction opt-out for fire-and-exit actions).
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 json, urllib.request
from importlib.metadata import version as get_version
import argclass
class CheckPyPIUpdate(argclass.NonConfigAction):
def __init__(self, option_strings, dest, package_name, **kwargs):
kwargs.setdefault("nargs", 0)
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)
setattr(namespace, self.dest, {
"current": current,
"latest": latest,
"up_to_date": current == latest,
})
class CLI(argclass.Parser):
# --check-updates is auto-derived from the attribute name
check_updates = argclass.Argument(
action=CheckPyPIUpdate,
package_name="argclass", # passthrough kwarg
)
cli = CLI()
cli.parse_args(["--check-updates"])
assert cli.check_updates["current"] == get_version("argclass")
assert isinstance(cli.check_updates["latest"], str)
assert isinstance(cli.check_updates["up_to_date"], bool)
assert "check_updates" not in argclass.INIConfigGenerator().dump_to_string(cli)
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:
Release files for argclass 1.11.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 | |
|---|---|---|---|
| argclass-1.11.0.tar.gz | 125.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| argclass-1.11.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 185.2 kB
Release files / argclass-1.11.0.tar.gz
| Download URL | argclass-1.11.0.tar.gz |
|---|---|
| Size | 125.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3b8c5829feea3c2dac4c712ef17328a52f16c1a23ce20ce1756fdf7b21dc2437
|
|
BLAKE2b-256 checksum How to use checksums |
c9a8afa69e47e512824c6feda6e6de322c1cdf098d4d7bd800cd7af33cb710e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency logRelease files / argclass-1.11.0-py3-none-any.whl
| Download URL | argclass-1.11.0-py3-none-any.whl |
|---|---|
| Size | 59.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
61e2424599b2470f3144799c86dd03fb01d6543d540ff8c2866638195d0148e6
|
|
BLAKE2b-256 checksum How to use checksums |
b997ab27ae8736bb979c0b0644b3cbf7a99cc77b6dbcf6254b4ffb97ec6e83b5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency log