Skip to main content

A decorator-based, declarative interface to Python's argparse for building hierarchical CLI applications.

Project description

argdec

A decorator-based, declarative interface to Python's argparse for building hierarchical CLI applications.

Python 3.7+ License: MIT

Overview

argdec (formerly argdeclare) provides two complementary approaches to configuring argparse:

  1. Decorator-based configuration - Use @option and @option_group decorators to attach argparse arguments directly to command methods, keeping argument definitions co-located with the code that uses them.

  2. Declarative class structure - Define CLI applications as classes where methods become commands, docstrings become help text, and class attributes configure parser behavior.

This combination eliminates boilerplate while preserving full access to argparse's capabilities.

Features

  • Decorator-driven options - Configure argparse arguments with @option and @option_group decorators directly on methods

  • Declarative command structure - Methods prefixed with do_ automatically become subcommands

  • Hierarchical commands - Build nested command structures (e.g., git remote add) using underscore-separated method names

  • Reusable option groups - Define common options once, apply to multiple commands with @option_group

  • Full argparse compatibility - All argparse features available through decorator parameters

  • Customizable - Configure command prefix, hierarchy levels, and more

  • Production ready - Comprehensive test suite, type hints, error handling

Installation

pip install argdec

Or install from source:

git clone https://github.com/shakfu/argdec.git
cd argdec
pip install .

Quick Start

from argdec import Commander, option

class MyApp(Commander):
    """My awesome CLI application."""
    name = 'myapp'
    version = '1.0'

    @option("-v", "--verbose", action="store_true", help="verbose output")
    def do_build(self, args):
        """Build the project."""
        print(f"Building... (verbose={args.verbose})")

if __name__ == '__main__':
    app = MyApp()
    app.cmdline()
$ python myapp.py build --verbose
Building... (verbose=True)

Declarative Format Example

#!/usr/bin/env python3

from argdec import Commander, option, option_group

# ----------------------------------------------------------------------------
# Commandline interface

common_options = option_group(
    option("--dump", action="store_true", help="dump project and product vars"),
    option("-d","--download",
           action="store_true",
           help="download python build/downloads"),
    option("-r", "--reset", action="store_true", help="reset python build"),
    option("-i","--install",
           action="store_true",
           help="install python to build/lib"),
    option("-b","--build",
           action="store_true",
           help="build python in build/src"),
    option("-c","--clean",
           action="store_true",
           help="clean python in build/src"),
    option("-z", "--ziplib", action="store_true", help="zip python library"),
    option("-p", "--py-version", type=str,
           help="set required python version to download and build"),
)

class Application(Commander):
    """builder: builds the py-js max external and python from source."""
    name = 'builder'
    epilog = ''
    version = '0.1'
    default_args = ['--help']
    _argparse_levels = 1


# ----------------------------------------------------------------------------
# python builder methods

    # def do_python(self, args):
    #     "download and build python from src"

    @common_options
    def do_python_static(self, args):
        """build static python"""
        print(args)

    @common_options
    def do_python_shared(self, args):
        """build shared python"""
        print(args)

    @common_options
    def do_python_shared_pkg(self, args):
        """build shared python to embed in package"""
        print(args)

    @common_options
    def do_python_framework(self, args):
        """build framework python"""
        print(args)

    @common_options
    def do_python_framework_pkg(self, args):
        """build framework python to embed in a package"""
        print(args)


# ----------------------------------------------------------------------------
# utility methods

    # def do_check(self, args):
    #     """check reference utilities"""
    #     print(args)

    @common_options    
    def do_check_log_day(self, args):
        """analyze log day"""
        print(args)

    @common_options    
    def do_check_log_week(self, args):
        """analyze log week"""
        print(args)

    @common_options
    def do_check_sys_month(self, args):
        """analyze sys month"""
        print(args)

    @common_options
    def do_check_sys_def(self, args):
        """analyze sys def"""
        print(args)

    @common_options
    def do_check_sys_xyz(self, args):
        """analyze sys xyz"""
        print(args)

    @common_options
    def do_test(self, args):
        """test suite"""
        print(args)


    @common_options
    def do_test_app(self, args):
        """test app"""
        print(args)

    @common_options
    def do_test_functions(self, args):
        """test functions"""
        print(args)

if __name__ == '__main__':
    app = Application()
    app.cmdline()

with levels=0 gives:

$ python3 demo.py
usage: demo.py [-h] [-v]  ...

builder: builds the py-js max external and python from source.

optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show program's version number and exit

subcommands:
  valid subcommands

                        additional help
    check_log_day       analyze log day
    check_log_week      analyze log week
    check_sys_def       analyze sys def
    check_sys_month     analyze sys month
    check_sys_xyz       analyze sys xyz
    python_framework    build framework python
    python_framework_pkg
                        build framework python to embed in a package
    python_shared       build shared python
    python_shared_pkg   build shared python to embed in package
    python_static       build static python
    test                test suite
    test_app            test app
    test_functions      test functions

with levels=1 gives:

$ python3 demo.py
usage: demo.py [-h] [-v]  ...

builder: builds the py-js max external and python from source.

optional arguments:
  -h, --help     show this help message and exit
  -v, --version  show program's version number and exit

subcommands:
  valid subcommands

                 additional help
    check        check commands
    python       python commands
    test         test suite

Advanced Features

Custom Command Prefix

By default, methods starting with do_ become commands. You can customize this:

class MyApp(Commander):
    _command_prefix = "cmd_"  # Use 'cmd_' instead of 'do_'

    def cmd_build(self, args):
        """Build the project."""
        pass

    def cmd_deploy(self, args):
        """Deploy the project."""
        pass

See examples/custom_prefix.py for more examples.

Error Handling

Version 0.2.0+ includes comprehensive error handling:

from argdec import ArgDecError, CommandExecutionError

try:
    app.cmdline()
except CommandExecutionError as e:
    print(f"Command failed: {e}")
except ArgDecError as e:
    print(f"Configuration error: {e}")

Examples

Can be found in the examples directory:

  • basic.py - Basic example application
  • hierarchical.py - Full-featured example application
  • custom_prefix.py - Custom prefix demonstrations

Development

Running Tests

make test           # Run test suite (38 tests)
make coverage       # Run with coverage report
make lint           # Run ruff linter
make typecheck      # Run mypy type checker
make all            # Run all checks

Requirements

  • Python 3.7+
  • No external dependencies (uses stdlib only)
  • Development: pytest, ruff, mypy (optional)

Version History

See CHANGELOG.md for detailed version history.

License

MIT License - See LICENSE file for details.

Credits

Based on the original argdeclare recipe from ActiveState.

Contributing

Contributions welcome! Please:

  1. Run tests: make test
  2. Check types: make typecheck
  3. Lint code: make lint
  4. Add tests for new features

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

argdec-0.2.2.tar.gz (20.8 kB view details)

Uploaded Source

Built Distribution

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

argdec-0.2.2-py3-none-any.whl (20.5 kB view details)

Uploaded Python 3

File details

Details for the file argdec-0.2.2.tar.gz.

File metadata

  • Download URL: argdec-0.2.2.tar.gz
  • Upload date:
  • Size: 20.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for argdec-0.2.2.tar.gz
Algorithm Hash digest
SHA256 fa9cbf5e9f392fceeb850fd4b1555da1c3e7fe899fa5be4c8d57e358a851655d
MD5 e5e7de140c3a7745b10402fa7993948f
BLAKE2b-256 ad560cf36fee0a9b0897f6fd17a67831f29f107b8fd709e96ac02fd6244dd7e4

See more details on using hashes here.

File details

Details for the file argdec-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: argdec-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 20.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for argdec-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 f50c5ef52fbe439b7af221b5cf9bc50b419d9e847dbb8442b4ca1e3944eef3e7
MD5 b72ef240cdebbcffe9a0cfbacf0d7bc8
BLAKE2b-256 9a607b357bae60740ea4eac32f3e05deb12313861f075aeee7ba8c455980909d

See more details on using hashes here.

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