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, a dict or custom datasource are trivial to swap in
- No boilerplate argparse code in your main logic
The alternatives
Click and Typer define commands in Python (decorators, type hints). Sigil defines the command tree as data (YAML by default; JSON or a dict work too, or whatever datasource you decide to wire up), and each command points to a script module loaded by name.
Consider Sigil when you have many subcommands, the tree changes more
often than the logic, you want it generated or merged from several files
or you want to stay on the standard library's argparse without the boilerplate.
Arguments map 1:1 to argparse add_argument kwargs, so there's no new API
to learn, and sigil validate / sigil tree help you check the config.
Stick with Click or Typer when you have a small app, want type-hint
driven arguments (type: here only supports Python builtins), or need their
ecosystem and plugins.
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, see steps 0-4 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
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) |
requirements |
(Optional) List of pip requirements that this subcommand requires. |
| any other parser kwarg | except for dest, parents and formatter_class they are all supported |
Argument
Each argument entry can be a plain dict which maps 1-to-1 with argparse add_argument, except name which maps its *args
- name: ["-p", "--port"] # or a single string, e.g. "positional"
required: false
default: 8069
help: "port number"
Groups and mutex groups are also supported 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 support Python builtins
Script files
Each script file must define a def run(args: argparse.Namespace, ctx: dict[str, Any]) -> None method.
Args is the namespace supplied by argparse (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 and ctx to enrich or modify the behaviour of subsequent 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, default '.'] |
Checks your sigil definition for schema errors and missing references. Run this after heavy edits to catch mistakes early. |
sigil tree [project_path, default '.'] |
Print the command structure of a sigil. |
sigil requirements [project_path, default '.'] |
Generate a requirements.txt file from active subcommands in the sigil. |
Misc
load: False may be used to detach 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 whose output can be converted into ParserConfig:
from sigil import run_from_config
from pathlib import Path
# Use JSON instead:
class JsonReader:
def read_manifest(self, root_path: Path, target: str) -> list | None:
# read target paths for loading configuration
...
def read_configuration(self, root_path: 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:
# exact dictreader implementation deliberately omitted
run_from_config(my_dict, datasource=DictReader)
Metadata
Release files for sigil-cli 1.5.4
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.4.tar.gz | 31.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sigil_cli-1.5.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 60.5 kB
Release files / sigil_cli-1.5.4.tar.gz
| Download URL | sigil_cli-1.5.4.tar.gz |
|---|---|
| Size | 31.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f1187df868e937bfbdd774a7fcf91e407f8dcbe85167a12455dd9a6ad537e4b7
|
|
BLAKE2b-256 checksum How to use checksums |
167b616c1565b99089e7cfb4a8cc422480a84f788279e7138e3b414f64b47771
|
| 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 Oct 2, 2026.
Transparency logRelease files / sigil_cli-1.5.4-py3-none-any.whl
| Download URL | sigil_cli-1.5.4-py3-none-any.whl |
|---|---|
| Size | 28.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
125439ed3df7bc9f193b9e7992f30cb9b419428d8598bb5af9a73ed5f3914f1e
|
|
BLAKE2b-256 checksum How to use checksums |
505006b33403a378084f9b86df7d82ccd363ebb340c4cc3a44a2e322d4d2ad59
|
| 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 Oct 2, 2026.
Transparency log