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_descdictionary. - Subcommands are implemented by assigning another
MLArgParsersubclass 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 toTrue(e.g.--verbose). - Default
True: one flag that turns it toTrue(redundant but explicit) and, by default, a--no-<name>flag that turns it toFalse(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 toFalse(e.g.no_cache->--no-cacheanddestcache).
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 customlevel/parent/top; they are used internally for subcommands. Usenoparse=Trueonly in tests or when you need to set up the parser without parsingsys.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_descor 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
- argparse — Python standard library.
- PEP 484 — Type hints.
- Implementation inspired by Multi-level argparse.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b14825804cb6d055bd3cdf094a7c5679f4fb68db11216d406ef5007537cb1f3
|
|
| MD5 |
ee295277dc66fb46e1a1744874910cce
|
|
| BLAKE2b-256 |
64baf97f73c939f67a936760af8a4234636b59fc1274273d6730428e66ea9d7f
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ecb65f2df8ed10a6d11c08b667c0a25352036d6cba61af241e87dc2f3eb97bce
|
|
| MD5 |
9ba2bc5158509e3ad7c60f8d5c880468
|
|
| BLAKE2b-256 |
7e57521c24baa8d56b10c7033da0dcdafdfb2688f41568831f351b47d6292690
|