Skip to main content

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.

PyPI Version PyPI Wheel Python 3.10+ License: MIT Downloads Tests Coverage Status

in production since April 2026

Features

  • Declarative command hierarchies (parents, subparsers, defaults)
  • Each command can point to a dynamically imported Python script
  • argcomplete integration 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_ignore 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) -> list | None:
        # read target paths for loading configuration
        ...
    
    def read_configuration(cls, target: Path) -> dict | None:
        # read *.json, parse, convert to dict of raw data
        ...


run_from_config("/path/to/config", loader_class=JsonReader)

You can also pass a pre‑loaded dictionary directly by wrapping it:

run_from_config(my_dict, loader_class=DictReader)

Metadata

Release files for sigil-cli 1.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sigil-cli 1.4.0
File Size Uploaded
sigil_cli-1.4.0.tar.gz 25.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sigil-cli 1.4.0
File Interpreter ABI Platform
sigil_cli-1.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 47.1 kB

Release files / sigil_cli-1.4.0.tar.gz

Download URL sigil_cli-1.4.0.tar.gz
Size 25.7 kB
Tags Source
SHA-256 checksum
How to use checksums
9e5906aa012fb9b7f6d24d32d917908569fbf6efac74846c2021a8b557e7cb53
BLAKE2b-256 checksum
How to use checksums
af4e3d357b02ff88c6b178d230d44582ddd2978340b24a5ed2922cc74ff04e96
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.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 Jul 24, 2026.

Transparency log

Release files / sigil_cli-1.4.0-py3-none-any.whl

Download URL sigil_cli-1.4.0-py3-none-any.whl
Size 21.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
68e196a5de832f2243c984c3cf5d28d7775a436a7e23edf3e91b58dd12cb1c9f
BLAKE2b-256 checksum
How to use checksums
488069e0e929b203a79690b747d18a2da1d05366fe5559701a240b788197c417
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.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 Jul 24, 2026.

Transparency log

Release history Release notifications | RSS feed

1.5.5

2 release files

1.5.4

2 release files

1.5.3

2 release files

1.5.2

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.2

2 release files

1.4.1

2 release files

This release

1.4.0 This release

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page