Declarative CLIs with argparse and dataclasses
npx @tessl/cli install tessl/pypi-argparse-dataclass@2.0.0A Python library that provides a declarative approach to building command-line interfaces by combining Python's argparse module with dataclasses. It enables developers to define CLI argument structures using dataclass syntax, automatically generating argument parsers that handle type conversion, default values, boolean flags, and argument validation.
pip install argparse-dataclassfrom argparse_dataclass import dataclass, ArgumentParser, parse_args, parse_known_argsStandard dataclass import for use with ArgumentParser:
from dataclasses import dataclass, field
from argparse_dataclass import ArgumentParserfrom argparse_dataclass import dataclass
@dataclass
class Options:
x: int = 42
verbose: bool = False
# Parse arguments directly from the class
options = Options.parse_args(['--x', '10', '--verbose'])
print(options) # Options(x=10, verbose=True)from dataclasses import dataclass, field
from argparse_dataclass import ArgumentParser
@dataclass
class Options:
name: str
count: int = 1
verbose: bool = False
parser = ArgumentParser(Options)
options = parser.parse_args(['--name', 'test', '--count', '5'])
print(options) # Options(name='test', count=5, verbose=False)from dataclasses import dataclass
from argparse_dataclass import parse_args
@dataclass
class Options:
input_file: str
output_file: str = "output.txt"
options = parse_args(Options, ['--input-file', 'data.txt'])
print(options) # Options(input_file='data.txt', output_file='output.txt')An enhanced version of the standard dataclass decorator that adds a parse_args static method to the decorated class.
def dataclass(
cls=None,
*,
init=True,
repr=True,
eq=True,
order=False,
unsafe_hash=False,
frozen=False
):
"""
Enhanced dataclass decorator that adds parse_args static method.
Parameters are identical to the standard dataclasses.dataclass decorator.
The decorated class gains a static method:
- parse_args(args=None): Parse command line arguments and return instance
Returns:
Decorated class with added parse_args static method
"""Usage example:
from argparse_dataclass import dataclass
@dataclass
class Config:
host: str = "localhost"
port: int = 8080
debug: bool = False
# Use the added parse_args method
config = Config.parse_args(['--host', 'example.com', '--port', '9000', '--debug'])A command-line argument parser that derives its options from a dataclass, extending the standard argparse.ArgumentParser.
class ArgumentParser(argparse.ArgumentParser, Generic[OptionsType]):
"""
Command line argument parser that derives its options from a dataclass.
Parameters:
- options_class: Type[OptionsType] - The dataclass that defines the options
- *args, **kwargs - Passed along to argparse.ArgumentParser
"""
def __init__(self, options_class: Type[OptionsType], *args, **kwargs): ...
def parse_args(self, args: ArgsType = None, namespace=None) -> OptionsType:
"""
Parse arguments and return as the dataclass type.
Parameters:
- args: Optional[Sequence[str]] - Arguments to parse (defaults to sys.argv)
- namespace: Not supported (raises ValueError if provided)
Returns:
Instance of the dataclass with parsed values
"""
def parse_known_args(
self, args: ArgsType = None, namespace=None
) -> Tuple[OptionsType, List[str]]:
"""
Parse known arguments and return tuple containing dataclass type
and list of remaining arguments.
Parameters:
- args: Optional[Sequence[str]] - Arguments to parse
- namespace: Not supported (raises ValueError if provided)
Returns:
Tuple of (dataclass instance, list of remaining arguments)
"""Usage example:
from dataclasses import dataclass
from argparse_dataclass import ArgumentParser
@dataclass
class Settings:
config_path: str
workers: int = 4
enable_logging: bool = True
parser = ArgumentParser(Settings)
settings = parser.parse_args(['--config-path', '/etc/app.conf', '--workers', '8'])Functions that provide parsing capabilities without needing to instantiate an ArgumentParser.
def parse_args(options_class: Type[OptionsType], args: ArgsType = None) -> OptionsType:
"""
Parse arguments and return as the dataclass type.
Parameters:
- options_class: Type[OptionsType] - The dataclass type to use for parsing
- args: Optional[Sequence[str]] - Arguments to parse (defaults to sys.argv)
Returns:
Instance of the dataclass with parsed values
"""
def parse_known_args(
options_class: Type[OptionsType], args: ArgsType = None
) -> Tuple[OptionsType, List[str]]:
"""
Parse known arguments and return tuple containing dataclass type
and list of remaining arguments.
Parameters:
- options_class: Type[OptionsType] - The dataclass type to use for parsing
- args: Optional[Sequence[str]] - Arguments to parse (defaults to sys.argv)
Returns:
Tuple of (dataclass instance, list of remaining arguments)
"""Usage example:
from dataclasses import dataclass
from argparse_dataclass import parse_args, parse_known_args
@dataclass
class Options:
input_dir: str
output_format: str = "json"
# Parse all arguments
options = parse_args(Options, ['--input-dir', '/data', '--output-format', 'xml'])
# Parse known arguments, ignore unknown ones
options, remaining = parse_known_args(Options, [
'--input-dir', '/data',
'--unknown-flag', 'value'
])Control argument parsing behavior using field metadata:
from dataclasses import dataclass, field
from argparse_dataclass import ArgumentParser
@dataclass
class AdvancedOptions:
# Custom argument names
input_file: str = field(metadata=dict(args=["-i", "--input"]))
# Positional arguments
output_file: str = field(metadata=dict(args=["output"]))
# Choices constraint
format: str = field(metadata=dict(choices=["json", "xml", "yaml"]))
# Custom type converter
date: str = field(metadata=dict(type=lambda x: x.upper()))
# Help text
verbose: bool = field(default=False, metadata=dict(help="Enable verbose output"))
# Multiple arguments
files: List[str] = field(default_factory=list, metadata=dict(nargs="+"))
# Required flag (for boolean)
force: bool = field(metadata=dict(required=True))
parser = ArgumentParser(AdvancedOptions)Support for various Python type annotations:
from dataclasses import dataclass
from typing import Optional, List, Literal
from argparse_dataclass import ArgumentParser
@dataclass
class TypedOptions:
# Basic types
count: int = 1
ratio: float = 0.5
name: str = "default"
enabled: bool = False
# Optional types
description: Optional[str] = None
max_items: Optional[int] = None
# List types (requires nargs metadata)
tags: List[str] = field(default_factory=list, metadata=dict(nargs="*"))
# Literal types (automatic choices)
log_level: Literal["debug", "info", "warning", "error"] = "info"
parser = ArgumentParser(TypedOptions)Different patterns for boolean flags:
from dataclasses import dataclass, field
from argparse_dataclass import ArgumentParser
@dataclass
class BooleanOptions:
# Standard boolean flag (--flag to set True)
verbose: bool = False
# Inverted boolean flag (--no-quiet to set False)
quiet: bool = True
# Custom flag name with inversion
logging: bool = field(default=True, metadata=dict(args=["--logging-off"]))
# Required boolean (supports --flag/--no-flag)
confirm: bool = field(metadata=dict(required=True))
parser = ArgumentParser(BooleanOptions)
# Usage examples:
# --verbose (sets verbose=True)
# --no-quiet (sets quiet=False)
# --logging-off (sets logging=False)
# --confirm or --no-confirm (required, sets confirm=True/False)from typing import TypeVar, Optional, Sequence, Type, Tuple, List
# Type variables
OptionsType = TypeVar("OptionsType")
ArgsType = Optional[Sequence[str]]
# Package version
__version__ = "2.0.0"The library raises specific exceptions for various error conditions:
TypeError: Raised when the options_class is not a dataclassValueError: Raised for various validation errors:
Example error handling:
from dataclasses import dataclass
from argparse_dataclass import ArgumentParser
@dataclass
class Options:
count: int
try:
parser = ArgumentParser(Options)
options = parser.parse_args(['--count', 'invalid'])
except SystemExit:
# argparse calls sys.exit() on parse errors
print("Invalid arguments provided")
except TypeError as e:
print(f"Configuration error: {e}")
except ValueError as e:
print(f"Validation error: {e}")The following metadata fields can be used in dataclass field definitions to customize argument parsing:
args: List[str] - Custom argument names/flags (e.g., ["-v", "--verbose"])choices: List - Allowed values for the fieldhelp: str - Help text displayed in usage informationtype: Callable - Custom type converter functionmetavar: str - Placeholder name for argument value in help textnargs: Union[int, str] - Number of arguments to consume ("*", "+", "?", or specific number)required: bool - Whether the argument is required (for optional arguments and boolean flags)argparse.BooleanOptionalActionBooleanOptionalAction implementationLiteral, Union, Optional