Simple subcommand CLIs with argparse

Overview

multicommand

Simple subcommand CLIs with argparse.

PyPI Version Downloads

multicommand uses only the standard library and is ~150 lines of code (modulo comments and whitespace)

Installation

pip install multicommand

Overview

Multicommand enables you to easily write CLIs with deeply nested commands using vanilla argparse. You provide it with a package, it searches that package for parsers (ArgumentParser objects), and connects, names, and converts those parsers into subcommands based on the package structure.

        Package                       ->                    CLI


commands/unary/negate.py                            mycli unary negate ...
commands/binary/add.py                              mycli binary add ...
commands/binary/divide.py             ->            mycli binary divide ...
commands/binary/multiply.py                         mycli binary multiply ...
commands/binary/subtract.py                         mycli binary subtract ...

All it needs is for each module to define a module-level parser variable which points to an instance of argparse.ArgumentParser.

Motivation

I like argparse. It's flexible, full-featured and it's part of the standard library, so if you have python you probably have argparse. I also like the "subcommand" pattern, i.e. one root command that acts as an entrypoint and subcommands to group related functionality. Of course, argparse can handle adding subcommands to parsers, but it's always felt a bit cumbersome, especially when there are many subcommands with lots of nesting.

If you've ever worked with technologies like Next.js or oclif (or even if you haven't) there's a duality between files and "objects". For Next.js each file under pages/ maps to a webpage, in oclif each module under commands/ maps to a CLI command. And that's the basic premise for multicommand: A light-weight package that lets you write one parser per file, pretty much in isolation, and it handles the wiring, exploiting the duality between command structure and file system structure.

Getting Started

See the simple example, or for the impatient:

Create a directory to work in, for example:

mkdir ~/multicommand-sample && cd ~/multicommand-sample

Install multicommand:

python3 -m venv ./venv
source ./venv/bin/activate

python3 -m pip install multicommand

Create the subpackage to house our parsers:

mkdir -p mypkg/parsers/topic/cmd/subcmd

Create the *.py files required for the directories to be packages

touch mypkg/__init__.py
touch mypkg/parsers/__init__.py
touch mypkg/parsers/topic/__init__.py
touch mypkg/parsers/topic/cmd/__init__.py
touch mypkg/parsers/topic/cmd/subcmd/{__init__.py,greet.py}

Add a parser to greet.py:

# file: mypkg/parsers/topic/cmd/subcmd/greet.py
import argparse


def handler(args):
    greeting = f'Hello, {args.name}!'
    print(greeting.upper() if args.shout else greeting)


parser = argparse.ArgumentParser(
    description='My first CLI with multicommand',
    formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument('name', help='Name to use in greeting')
parser.add_argument('--shout', action='store_true', help='Yell the greeting')
parser.set_defaults(handler=handler)

lastly, add an entrypoint:

touch mypkg/cli.py

with the following content:

# file: mypkg/cli.py
import multicommand
from . import parsers


def main():
    parser = multicommand.create_parser(parsers)
    args = parser.parse_args()
    if hasattr(args, 'handler'):
        args.handler(args)
        return
    parser.print_help()


if __name__ == "__main__":
    exit(main())

Try it out!

$ python3 -m mypkg.cli
usage: cli.py [-h] {topic} ...

optional arguments:
  -h, --help  show this help message and exit

subcommands:

  {topic}

Take a look at our greet command:

$ python3 -m mypkg.cli topic cmd subcmd greet --help
usage: cli.py topic cmd subcmd greet [-h] [--shout] name

My first CLI with multicommand

positional arguments:
  name        Name to use in greeting

optional arguments:
  -h, --help  show this help message and exit
  --shout     Yell the greeting (default: False)

From this we get:

$ python3 -m mypkg.cli topic cmd subcmd greet "World"
Hello, World!

$ python3 -m mypkg.cli topic cmd subcmd greet --shout "World"
HELLO, WORLD!

Bonus

Want to add the command topic cmd ungreet ... to say goodbye?

Add the module:

touch mypkg/parsers/topic/cmd/ungreet.py

with contents:

# file: mypkg/parsers/topic/cmd/ungreet.py
import argparse


def handler(args):
    print(f'Goodbye, {args.name}!')


parser = argparse.ArgumentParser(description='Another subcommand with multicommand')
parser.add_argument('name', help='Name to use in un-greeting')
parser.set_defaults(handler=handler)

The new command is automatically added!:

$ python3 -m mypkg.cli topic cmd --help
usage: cli.py cmd [-h] {subcmd,ungreet} ...

optional arguments:
  -h, --help        show this help message and exit

subcommands:

  {subcmd,ungreet}

Try it out:

$ python3 -m mypkg.cli topic cmd ungreet "World"
Goodbye, World!
You might also like...
This a simple tool to query the awesome ippsec.rocks website from your terminal
This a simple tool to query the awesome ippsec.rocks website from your terminal

ippsec-cli This a simple tool to query the awesome ippsec.rocks website from your terminal Installation and usage cd /opt git clone https://github.com

Simple Tool To Grab Like-Card Coupon
Simple Tool To Grab Like-Card Coupon

Simple Tool To Grab Like-Card Coupon

(BionicLambda Universal SHell) A simple shell made in Python. Docs and possible C port incoming.

blush 😳 (BionicLambda Universal SHell) A simple shell made in Python. Docs and possible C port incoming. Note: The Linux executables were made on Ubu

A simple reverse shell in python

RevShell A simple reverse shell in python Getting started First, start the server python server.py Finally, start the client (victim) python client.py

Un module simple pour demander l'accord de l'utilisateur dans une CLI.
Un module simple pour demander l'accord de l'utilisateur dans une CLI.

Demande de confirmation utilisateur pour CLI Présentation ask_lib est un module pour le langage Python proposant une seule fonction; ask(). Le but pri

💻 Physics2Calculator - A simple and powerful calculator for Physics 2
💻 Physics2Calculator - A simple and powerful calculator for Physics 2

💻 Physics2Calculator A simple and powerful calculator for Physics 2 🔌 Predefined constants pi = 3.14159... k = 8988000000 (coulomb constant) e0 = 8.

Simple script to download OTA packages from Realme's endpoint.

Realme OTA Downloader CLI tool (based on this C# program) to create requests to the Realme's endpoint. Requirements Python 3.9. pycryptodome. Installa

A simple command line dumper written in Python 3.

A simple command line dumper written in Python 3.

A simple cli tool to commit Conventional Commits

convmoji A simple cli tool to commit Conventional Commits. Requirements Install pip install convmoji convmoji --help Examples A conventianal commit co

Releases(1.0.0)
  • 0.1.1(Jun 8, 2021)

  • 0.1.0(May 24, 2021)

    • Make sure to always initialize a root index parser (if one doesn't already exist) so that multicommand.create_parser(...) always returns a useable ArgumentParser (instead of raising an exception).

      This way multicommand.create_parser(...) can be called on a package that has no parsers, and will still behave sensibly.

    • Check that found parsers are actually (sub-classes of) ArgumentParser, skip them if they aren't.

    • Fix bug in _requires_subparsers

    • Improve help for subcommands

    Source code(tar.gz)
    Source code(zip)
  • 0.0.8(Apr 9, 2021)

    • Fix: Fix prog=... for intermediate index parsers. Prior to this these parsers would only show the command name (sys.argv[0]) and the last parser's name, but none of the intermediate parser's names, which meant the usage string was wrong.
    • Update: Add a license (MIT)
    • Update: Change registry data structure from List[Tuple[PurePath, ArgumentParser]] -> OrderedDict[PurePath, Dict[str, ArgumentParser]]
    Source code(tar.gz)
    Source code(zip)
  • 0.0.7(Apr 6, 2021)

  • 0.0.6(Apr 5, 2021)

    • Update: Refactored multicommand.py to simplify the structure of the create_parser(...) function. Moreover, the keys in the parser registry (OrderedDict) are now pathlib.PurePath objects instead of tuples. The motivation for this change was because the registry keys (Tuple[str, ...] objects) were already basically being treated like pathlib.Path objects and it made the registry key manipulation easier to read and understand.
    • Update: pyproject.toml (added keywords)
    Source code(tar.gz)
    Source code(zip)
  • 0.0.5(Apr 4, 2021)

    • Update: load_parsers now uses pkg.__name__ when loading parsers into the parser registry (OrderedDict). Prior to this, the value commands was hardcoded meaning that for multicommand to work the subpackage housing all of the parsers had to be called mypkg.<blah>.commands.
    • Update: Documentation
      • General housekeeping: Fixed typos, fixed broken links, added PyPI badge, etc.
    Source code(tar.gz)
    Source code(zip)
  • 0.0.4(Apr 3, 2021)

    • Fix: Fixed the scenario where "intermediate" commands were not defined. When that happened multicommand wouldn't have intermediate parsers to link terminal parsers to the root parser.

      Specifically, if there was say a single command defined as: commands/mytopic/mycommand/mysubcommand.py (with appropriate __init__.py files) multicommand would crash, because the index parsers required to exist "between" the parser defined in mysubcommand.py and the root parser (created by multicommand) weren't getting created, and the application would crash.

    This release also adds a very basic README and a simple example demonstrating the basic usage of multicommand.

    Source code(tar.gz)
    Source code(zip)
  • 0.0.3(Apr 2, 2021)

    • Update: Forward parser config on subparser creation
    • Update: Add custom prog=... when linking parsers to accurately reflect the expected command invocation
    • Maintenance: Add comments, clean up type hints, update pyproject.toml
    Source code(tar.gz)
    Source code(zip)
  • 0.0.2(Apr 1, 2021)

A Python module and command line utility for working with web archive data using the WACZ format specification

py-wacz The py-wacz repository contains a Python module and command line utility for working with web archive data using the WACZ format specification

Webrecorder 14 Oct 24, 2022
Gamma ion pump QPC ethernet Python library & CLI utility

Unofficial Gamma ion pump ethernet control CLI utility and library This is a mini Python 3 library and utility that exposes some of the functions of t

2 Jul 18, 2022
pypinfo is a simple CLI to access PyPI download statistics via Google's BigQuery.

pypinfo: View PyPI download statistics with ease. pypinfo is a simple CLI to access PyPI download statistics via Google's BigQuery. Installation pypin

Ofek Lev 351 Dec 26, 2022
Simple CLI prompt for easy I/O with OpenAI's API

openai-cli-prompt Simple CLI prompt for easy I/O with OpenAI's API Quickstart Create a .env file with: OPENAI_API_KEY=Your OpenAI API Key Configure

Erik Nomitch 1 Oct 12, 2021
ForX - get forex quotes from the terminal

A command line tool for checking exchange rates between currencies, both crypto and fiat.

Gabe Banks 52 Dec 10, 2022
A CLI tool for using GLIDE to generate images from text.

Text-Glided-Diffusion Installation First clone this repository: git clone https://github.com/afiaka87/text-glided-diffusion.git cd text-glided-diffusi

Clay Mullis 68 Dec 30, 2022
Projeto Reverse Shell For Python

Use com sabedoria!!! Modo de uso: Linux (inclui Android e Mac): - apt-get update - apt install python3 (ou "python" apenas) - git clone https://github

1 Jan 03, 2022
Python Library and CLI for exporting MySQL databases

expdb Python library and CLI for exporting MySQL databases Installation Pre-requisites MySQL server Python 3.9+ Using git Clone the repository to your

Devansh Singh 1 Nov 29, 2021
A very simple python script to encode and decode PowerShell one-liners.

PowerShell Encoder A very simple python script to encode and decode PowerShell one-liners. I used Raikia's PowerShell encoder ALOT, but one day it wen

John Tear 5 Jul 29, 2022
電通大のCLIツールです

uecli 電通大のCLIツールです。コマンドラインからシラバス検索、成績参照、図書館の貸出リストなどを見ることができます インストール pip install uecli 使い方 シラバスを検索 uecli syllabus search -s 'コンピュータサイエンス' シラバスを取得し、Mar

UEC World Dominators 2 Oct 31, 2021
Todo - You could use terminal to set your todo

Python Tutorial You can learn how to build a terminal application(CLI applicatio

29 Jun 29, 2022
Management commands to help backup and restore your project database and media files

Django Database Backup This Django application provides management commands to help backup and restore your project database and media files with vari

687 Jan 04, 2023
A simple and easy-to-use CLI parse tool.

A simple and easy-to-use CLI parse tool.

AbsentM 1 Mar 04, 2022
A next-generation CLI and TUI that aims to be your personal assistant for everything competitive programming related. 🚀

Competitive Programming Tool Kit The Competitive Programming Tool Kit (cptk for short), is a command line and terminal user interface (CLI and TUI) th

Alon 4 May 21, 2022
CLI to show end-of-life dates for tools and technologies.

Python 3.9+ interface to endoflife.date to show end-of-life dates for tools and technologies.

Hugo van Kemenade 32 Jan 06, 2023
Multifunctional library for creating progress bars.

👋 Content Installation Using github Using pypi Quickstart Flags Useful links Documentation Pypi Changelog TODO Contributing FAQ Bar structure ⚙️ Inst

DenyS 27 Jan 01, 2023
CLI translator based on Google translate API

Translate-CLI CLI переводчик основанный на Google translate API как пользоваться ? запустить в консоли скомпилированный скрипт (exe - windows, bin - l

7 Aug 13, 2022
Plumbum: Shell Combinators

Plumbum: Shell Combinators Ever wished the compactness of shell scripts be put into a real programming language? Say hello to Plumbum Shell Combinator

Tomer Filiba 2.5k Dec 28, 2022
Shazam is a Command Line Application that checks the integrity of the file by comparing it with a given hash.

SHAZAM - Check the file's integrity Shazam is a Command Line Application that checks the integrity of the file by comparing it with a given hash. Crea

Anaxímeno Brito 1 Aug 21, 2022
A very simple and lightweight ToDo app using python that can be used from the command line

A very simple and lightweight ToDo app using python that can be used from the command line

Nilesh Sengupta 2 Jul 20, 2022