Sigil
Declarative argparse, without the CLI boilerplate.
Sigil is a lightweight, declarative CLI framework for Python. Define your command tree in YAML (or any other format),
and sigil builds the argparse parser on the fly. Complete with subcommands and dynamic script loading.
It plays nicely with argcomplete out of the box.
Features
- Declarative command hierarchies (parents, subparsers, defaults)
- Each command can point to a dynamically imported Python script
argcompleteintegration for tab‑completion- Pluggable data sources – YAML is the default, but JSON, TOML, or a dict are trivial to swap in
- No boilerplate argparse code in your main logic
The alternatives
There are plenty of established options out there. Click and Typer are great libraries with their own approaches.
Sigil takes a different path, focusing on reducing boilerplate while keeping your command structure modular and flexible.
Quick Start
Install
pip install sigil-cli
optionally include argcomplete with sigil-cli[completion]
The init command generates a project directory with a sample configuration and entrypoint:
sigil init demo
sigil validate demo/ # optional, should not output anything for correct configurations
cd demo
python main.py --help
For a full walkthrough with custom commands and arguments, jump to the Quick Start below.
0. Recommended file structure
project_root/
├── mycli.py # drop‑in bootstrap script (alias this)
├── manifest.yml # lists all YAML config files to load
├── yml/
│ ├── root.yml # root command definition
│ ├── root_run.yml # subcommand definition(s)
│ └── ...
└── scripts/
├── run.py # implements the 'run' command
└── ... # other scripts
ps: don't shoot yourself in the foot, don't symlink the bootstrap script.
1. Entry script
Create mycli.py:
#!/usr/bin/env python3
# PYTHON_ARGCOMPLETE_OK
from pathlib import Path
from sigil import run_from_config
if __name__ == "__main__":
run_from_config(Path(__file__).parent)
2. Configuration files
List all your YAML definitions in manifest.yml:
- root.yml
- root_run.yml
Define the root command in root.yml:
root:
name: mycli
script_dir: scripts
Define a subcommand in root_run.yml:
root_run:
name: run
parent: root
help: command utility to run containers
script: run
args:
- help: port to run, autoincrements from 8080
name:
- -p
- --port
3. Write the script
Create scripts/run.py:
import argparse
from typing import Any
def run(args: argparse.Namespace, ctx: dict[str, Any]) -> None:
port = getattr(args, "port", 8080)
port = find_next_free_port_logic(port)
print(f"Running container on port {port}")
4. Run it
chmod +x mycli.py
./mycli.py run --port 9000
# Running container on port 9000
./mycli.py run
# Running container on port 8080
./mycli.py run
# Running container on port 8081
Configuration Reference
Root Command
| Field | Description |
|---|---|
name |
Program name (used as prog in argparse) |
script_dir |
Directory (relative to the config root) where command scripts are located |
Command
| Field | Description |
|---|---|
name |
Subcommand name |
parent |
Parent command (must exist elsewhere in a config) |
help |
Help text for this subcommand |
script |
Python module name (without .py) inside script_dir, absolute paths supported |
args |
List of argument definitions (see below) |
default |
If True, this subcommand is used when no subcommand is given |
load |
If False skips this command (or top level object) from being loaded into the command tree (default True) |
| any other parser kwarg | except for dest, parents and formatter_class they are all supported |
Note that parent does not refer to argparse's parents parameter but is only used to resolve the parser tree.
Parser (multi-)inheritance isn't supported but can be emulated by adding arguments to parent commands in the tree.
Argument
Each argument entry can be a plain dict which maps 1-to-1 with argparse add_argument, except name which maps it's *args
- name: ["-p", "--port"] # or a single string, e.g. "positional"
required: false
default: 8069
help: "port number"
Groups and mutex groups are also suppored via the "kind" parameter (defaults to argument)
# mutex group
- kind: mutex
args:
- <any recursive args/group/mutex construct here>
...
... # any valid mutex group arguments go here
- kind: group
... # same
The name field can be --flag for flags or a string for positional arguments.
Both literal string and list of strings are supported.
Types (type:) only supports python builtins
Script files
Each script files have as only requirement that they need to define a
def run(args: argparse.Namespace, ctx: dict[str, Any]) -> None method.
args is the by argparse supplied namespace (parsed with parse_known_args), any additional args can be found in ctx['other_args']
scripts run in sequence from command -> subcommand -> sub sub command -> ... and each may add to, remove or otherwise modify args.namespace and ctx to enrich or modify the behaviour of supsequent scripts.
Sigil CLI Commands
sigil ships with its own lightweight toolbelt to manage your projects:
| Command | Purpose |
|---|---|
sigil init [project_name] |
Creates a new project folder with a sample ready-to-run Python entrypoint. |
sigil validate [project_path] |
Checks your sigil definition for schema errors and missing references. Run this after heavy edits to catch mistakes early. |
sigil tree [project_path] |
Print the command structure of a sigil. |
(Note: Your generated CLI (the one you build with Sigil) is completely separate from the sigil management
commands above. You alias and run main.py. the sigil prefix is a different namespace.)
Misc
load: False may be used to detaching commands from the command tree for any purpose
(deprecation, development, etc) or for non schema-compliant objects at the top level of a file, this may be useful to
define anchors or references that should not directly be read as a command.
Tab‑Completion (argcomplete)
Sigil registers itself with argcomplete automatically if available on your system.
To enable completion, install argcomplete and activate it for your entry
script (or use the builtin argcomplete comment):
pip install argcomplete
activate-global-python-argcomplete
Then run your script and hit Tab – subcommands and flags will complete.
Pluggable Backends
Sigil uses yaml by default, but you can supply any datasource that we can convert it's output into ParserConfig:
from sigil import run_from_config
# Use JSON instead:
class JsonReader:
@classmethod
def read_manifest(cls, config_root: Path, target: str) -> list | None:
# read target paths for loading configuration
...
def read_configuration(cls, target: Path, target: str) -> dict | None:
# read *.json, parse, convert to dict of raw data
...
run_from_config("/path/to/config", datasource=JsonReader)
You can also pass a pre‑loaded dictionary directly by wrapping it:
run_from_config(my_dict, datasource=DictReader)
Metadata
Release files for sigil-cli 1.5.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sigil_cli-1.5.3.tar.gz | 28.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sigil_cli-1.5.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 55.3 kB
Release files / sigil_cli-1.5.3.tar.gz
| Download URL | sigil_cli-1.5.3.tar.gz |
|---|---|
| Size | 28.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
02155d90781e19df892584f69100e7fb9c0763640d5ac78a57460757c2a9414c
|
|
BLAKE2b-256 checksum How to use checksums |
a0782533056864f8d1c3bd652bf88005c69ebb4184840979bfbeadd478defee1
|
| 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 3, 2026.
Transparency logRelease files / sigil_cli-1.5.3-py3-none-any.whl
| Download URL | sigil_cli-1.5.3-py3-none-any.whl |
|---|---|
| Size | 26.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5557370675152e6e11408e19362743218deebb2a6b4519bc6c962d67bc52ba93
|
|
BLAKE2b-256 checksum How to use checksums |
c37a0e19e0176ff08fb849a26b961ea6312edf9ceb682bd5ba1623c1c342829d
|
| 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 3, 2026.
Transparency log