Skip to main content

A multi-Level argument parser library for writing CLI-based applications

Project description

MLArgParser

MLArgParser is a multi-level argument parser for building CLI applications in Python. It maps object-oriented concepts directly to the command line: subclasses become subcommands, methods become commands, and method parameters become command-line arguments. Type hints and docstrings drive help text and type conversion automatically.

Requirements: Python 3.8+ (3.9+ recommended for exit_on_error=False and built-in generics like list[str]).

Design

  • Commands are the public methods of your parser class (names not starting with _).
  • Arguments are the parameters of those methods; names and types come from the signature.
  • Help text comes from the class/method docstrings and from the arg_desc dictionary.
  • Subcommands are implemented by assigning another MLArgParser subclass as a class attribute, forming a tree of commands.

The library uses argparse under the hood. You get standard help formatting, long and short options, and consistent error handling without writing parser setup code by hand.

Quick start

Subclass MLArgParser and define methods; their names become commands. Use type hints and defaults for arguments; use arg_desc to describe them in help.

#!/usr/bin/env python3

from mlargparser import MLArgParser


class MyApp(MLArgParser):
    """My application."""

    arg_desc = {
        "count": "Number of items",
        "name": "Item name",
        "format": "Output format",
    }

    def show(self, count: int = 10, name: str = None):
        """Show items."""
        print(f"count={count}, name={name}")

    def run(self, format: str = "text"):
        """Run the task."""
        print(f"format={format}")


if __name__ == "__main__":
    MyApp()

Example invocations:

./myapp.py --help
./myapp.py show --count 5 --name foo
./myapp.py run --format json

Public method names are normalized for the CLI: underscores become dashes, and by default command names are lowercased.

Commands and arguments

Commands

Every public method (no leading _) is a command. The command name is derived from the method name: underscores are replaced with dashes, and by default the result is lowercased (e.g. dump_config becomes dump-config).

Argument types

Parameter type hints determine how values are parsed and passed to your method:

Annotation CLI behavior
str One string (default if no annotation)
int One integer
float One float
bool Flag; see Boolean flags below
list[T] One or more values, collected as list
set[T] One or more values, collected as set
tuple[T, ...] One or more values, as tuple
Optional[T] / Union[T, None] Unwraps to T

Unannotated parameters and None are treated as str. Invalid or unresolved annotations are reported at startup when strict_types=True (default).

Required and optional

  • No default (or inspect.Parameter.empty) means the argument is required.
  • A default value makes the argument optional; the default is shown in help.

Argument descriptions

Set arg_desc on your class (or subclass) to map parameter names to help strings:

arg_desc = {
    "count": "Number of items to process",
    "output": "Output file path",
}

If a parameter is not in arg_desc, help uses the placeholder FIXME: UNDOCUMENTED. Subparsers merge their parent’s arg_desc with their own; local entries override the parent’s for the same key.

Boolean flags

Boolean parameters are turned into flags:

  • Default False: one flag that turns the value to True (e.g. --verbose).
  • Default True: one flag that turns it to True (redundant but explicit) and, by default, a --no-<name> flag that turns it to False (e.g. --no-cache).
  • Parameter name starts with no_: treated as the “off” side of a flag; the option is --no-<rest> and sets the base name to False (e.g. no_cache -> --no-cache and dest cache).

You must not define both a foo and a no_foo parameter for the same logical flag; that is rejected as ambiguous. Set auto_disable_flags = False on your class to disable automatic --no-* generation for True-default booleans.

Subcommands (command trees)

To add a subcommand level, assign an MLArgParser subclass as a class attribute. That class is then instantiated when the user selects that command; it parses the rest of argv and dispatches to its own commands.

Example: one top-level command dump with subcommands config, state, and authtoken:

class DumpCmd(MLArgParser):
    """Dump subcommand."""

    def config(self):
        """Dump configuration."""
        ...

    def state(self):
        """Dump state."""
        ...

    def authtoken(self):
        """Dump auth token."""
        ...


class MyApp(MLArgParser):
    """Main application."""
    dump = DumpCmd

Invocation:

./app.py dump config
./app.py dump state
./app.py dump authtoken

When the user runs ./app.py dump config, the top-level parser sees the command dump, gets the class DumpCmd, and calls DumpCmd(level=2, parent=app, top=app). That sub-parser then parses config and invokes DumpCmd.config(). You can nest further by assigning another parser class as an attribute of DumpCmd, and so on.

Inside a subcommand, self.parent is the immediate parent parser instance and self.top is the root parser instance (e.g. MyApp), which is useful for sharing state or configuration.

Options (short and long)

For each argument the library adds a long option --<name> (with underscores in the name turned into dashes). If the first character of the argument name is not already used by another argument, a short option -<letter> is also added. So for a parameter verbose, you get both --verbose and -v unless -v was already taken.

Configuration

Set these as class attributes on your parser class (or subclass):

Attribute Default Description
arg_desc None Dict mapping parameter names to help strings.
auto_disable_flags True If True, add --no-<name> for boolean parameters with default True.
case_sensitive_commands False If True, command names are not lowercased.
strict_validation True If True, command name collisions and (when strict_types is also True) type validation errors are fatal.
strict_types True If True, invalid or unresolved type annotations cause startup to fail; if False, they are reported as warnings.

Constructor:

  • MLArgParser(level=1, parent=None, top=None, noparse=False, strict_types=True)
    Normally you do not call this with custom level/parent/top; they are used internally for subcommands. Use noparse=True only in tests or when you need to set up the parser without parsing sys.argv (e.g. to build help or run a specific command programmatically).

Bash completion

Optional bash/zsh tab completion is provided via argcomplete. Install the extra and enable it in your script:

pip install mlargparser[argcomplete]
# PYTHON_ARGCOMPLETE_OK
from mlargparser import MLArgParser
import mlargparser_argcomplete
mlargparser_argcomplete.install()

class MyApp(MLArgParser):
    ...

if __name__ == "__main__":
    MyApp()

For global completion (any script with PYTHON_ARGCOMPLETE_OK is completed without per-command registration), run once:

activate-global-python-argcomplete

To register a single command instead:

eval "$(register-python-argcomplete myapp)"

Help output

  • The top-level description is the class docstring.
  • Each command’s description is that method’s docstring.
  • Each argument’s help comes from arg_desc or the undocumented placeholder.
  • Defaults are appended where applicable (e.g. [default: "text"], [enabled by default]).

Testing

Tests live under tests/ and use the standard library unittest:

python3 -m unittest discover -s tests -p "test_*.py" -v

License

Unless otherwise noted, code in this repository is licensed under the LGPL v2 only. For use under a different license, contact the author.

References

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

mlargparser-1.1.tar.gz (35.1 kB view details)

Uploaded Source

Built Distribution

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

mlargparser-1.1-py3-none-any.whl (24.4 kB view details)

Uploaded Python 3

File details

Details for the file mlargparser-1.1.tar.gz.

File metadata

  • Download URL: mlargparser-1.1.tar.gz
  • Upload date:
  • Size: 35.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for mlargparser-1.1.tar.gz
Algorithm Hash digest
SHA256 3b14825804cb6d055bd3cdf094a7c5679f4fb68db11216d406ef5007537cb1f3
MD5 ee295277dc66fb46e1a1744874910cce
BLAKE2b-256 64baf97f73c939f67a936760af8a4234636b59fc1274273d6730428e66ea9d7f

See more details on using hashes here.

File details

Details for the file mlargparser-1.1-py3-none-any.whl.

File metadata

  • Download URL: mlargparser-1.1-py3-none-any.whl
  • Upload date:
  • Size: 24.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for mlargparser-1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ecb65f2df8ed10a6d11c08b667c0a25352036d6cba61af241e87dc2f3eb97bce
MD5 9ba2bc5158509e3ad7c60f8d5c880468
BLAKE2b-256 7e57521c24baa8d56b10c7033da0dcdafdfb2688f41568831f351b47d6292690

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