Skip to main content

A lightweight Python framework for building command-line interfaces (CLI).

Project description

Mustiolo

Mustiolo is a lightweight Python framework for building command-line interfaces (CLI). It allows you to define commands, handle parameters, and provide user-friendly help messages with minimal effort. Mustiolo is designed to be simple, extensible, and easy to use.

Logo


Table of Contents


Features

  • Command Registration: Easily register commands and subcommands using a decorator.
  • Parameter Handling: Supports type annotations, default values, and mandatory parameters.
  • Help System: Automatically generates help messages for commands and parameters.
  • Command History: Handle the command history like Unix-like systems.
  • Autocomplete Command: Command autocomplete via 'tab' key like Unix-like systems.
  • Error Handling: Captures and displays errors in a user-friendly format.
  • Customizable Message Boxes: Displays messages in visually appealing bordered boxes.

Was there a need for another library?

No, there are a plenty number of libraries to build CLI applications in Python, this one is an experiment to try to have the minimum code for building CLI applications.

It must be considered as a toy library just to experiment.

Why this name?

The 'mustiolo' is the smallest mammal in the world, weighing about 1.2-2.5 grams as an adult. It is present in Sardinia, in the Italian, Balkan, Iberian peninsulas and in North Africa.

This library aims to be the smallest library for building CLI applications in Python just like a mustiolo is the smallest mammal.

Installation

To install Mustiolo, you can use pip:

pip install mustiolo

or using the code in the repository:

git clone git@github.com:Cereal84/mustiolo.git
cd mustiolo
pip install .

Basic usage

Defining commands

Commands can be defined using the @command decorator. Each command can have a name, short help, and long help description.

Help format

We've 2 types of 'help message':

  • menu help: the description which must be showed in the menu help.
  • usage help: is the command usage.

Help messages are retrieved by looking the docstring.

from mustiolo.cli import CLI

cli = CLI()

@cli.command()
def greet(name: str):
    """
    <menu>Greet a user by name.</menu>
    """
    
    print(f"Hello {name}!")

@cli.command()
def add(a: int, b: int):
    """
    <menu>Sum two numbers.</menu>
    <usage>Add two numbers and print the result.</usage>
    """
    
    print(f"The result is: {a + b}")

if __name__ == "__main__":
    cli.run()

Example of execution

> ?
greet    Greet a user by name.
add      Sum two numbers.
> exit

It is possible to use the ? command to see the usage of a specific command.

> ? add
Add two numbers and print the result.

add A B

Parameters:
		A	Type INTEGER [required]
		B	Type INTEGER [required]
> exit

Override command information

By default, the library uses as command name the function decorated via @cli.command and as short help message the docstring. It is possible to override the information passing, in the decorator, the following arguments:

  • name
  • menu
  • usage

So we can define a command like this:

@cli.command(name="sum", menu="Add two numbers", usage="Add two numbers and print the result.")
def add(a: int, b: int):
    print(f"The result is: {a + b}")

In this example, we override the command name and the short help message, but we keep the long help message as it is.

> ?
greet    Greet a user by name.
sum      Add two numbers
> ? sum
Add two numbers and print the result.

sum A B

Parameters:
		A	Type INTEGER [required]
		B	Type INTEGER [required]
> 

Notes

Menu

menu message is mandatory and can be specified via docstring or parameter in command decorator. If both are void then an error will be returned.

Usage

usage works like menu and so it is possibile to be specified via docstring or decorator, but if none of them is set then will be used the menu value.

The help message will be used in the following template

<usage message>

<command_name> <parameter1> ... <parameterN>

Parameters:
    <parameter1_name> <type> [<mandatory/optional>]
    ...
    <parameterN_name> <type> [<mandatory/optional>]

Mandatory and optional parameters

The library uses annotations and type hints to determine if a parameter is mandatory or optional. If the argument in the function has a default value, then the parameter in the CLI command is optional; otherwise, it is mandatory.

@cli.command()
def greet(name: str = "World"):
    """Greet a user by name or print 'Hello World!'."""
    print(f"Hello {name}!")
> ? greet
Usage greet Greet a user by name or print 'Hello World!'.

greet NAME

Parameters:
		NAME	Type STRING [optional] [default: World]

Supported Types for Parameters

Mustiolo automatically converts command-line arguments to the types declared in your function signatures. For this reason, type annotation is mandatory; otherwise, an error will be shown and the CLI will exit. The following types are supported:

  • str: No conversion is performed; the argument is passed as a string.
  • int: The argument is converted to an integer.
  • float: The argument is converted to a float.
  • bool: Accepts true, false, 1, 0 (case-insensitive). For example, "true" and "1" become True, "false" and "0" become False.
  • List (or list): Accepts a comma-separated string (e.g., "a,b,c" or "1,2,3").
    • If a subtype is specified (e.g., List[int]), each element is converted to that type.
    • Supported subtypes are: str, int, float, bool.
    • If no subtype is specified, elements are treated as strings.

Examples:

@cli.command(menu="Example command", usage="An example command with various types.")
def example(a: int, b: float, c: bool, d: str, e: list, f: list[int]):
    print(a, b, c, d, e, f)
> example 5 3.14 true hello a,b,1 1,2,3
# Output: 5 3.14 True hello ['a', 'b', '1'] [1, 2, 3]

Notes:

  • If the conversion fails (e.g., passing "abc" to an int), an error is shown.

Group commands

It is possible to have a command tree specifyng a command group using Menugroup objects. The group have a name that specify the command root.

from mustiolo.cli import CLI, MenuGroup

from typing import List

cli = CLI()

# add the commands to the root menu
@cli.command()
def greet(name: str = "World"):
    """<menu>Greet a user by name.</menu>"""
    print(f"Hello {name}!")

math_submenu = MenuGroup("math", "Some math operations", "Some math operations")

@math_submenu.command()
def add(a: int, b: int):
    """
    <menu>Sum two numbers.</menu>
    <usage>Add two numbers and print the result.</usage>
    """
    print(f"The result is: {a + b}")

@math_submenu.command()
def add_list(numbers: List[int]):
    """<menu>Add N numbers.</menu>"""
    tot = sum(numbers)
    print(f"The result is: {tot}")

@math_submenu.command()
def sub(a: int, b: int):
    """<menu>Subtract two numbers.</menu>"""
    print(f"The result is: {a - b}")

# add math submenu to the root menu
cli.add_group(math_submenu)


if __name__ == "__main__":
    cli.run()

So we have four commands in the root menu, by default the root menu has '?' and 'exit', as you can see below:

  • ?
  • exit
  • greet
  • math

and math specify other commands:

  • add
  • add_list
  • sub
> ?
?    	Shows this help.
exit 	Exit the program
greet	Greet a user by name.
math    Some math operations
> ? math
add     	Add two numbers.
add_list	Add N numbers.
sub     	Subtract two numbers.

Configure CLI

The constructor of the CLI class accepts some parameters to configure the CLI behavior:

  • 'hello_message': A welcome message displayed when the CLI starts, default is empty.
  • 'prompt': The prompt string displayed to the user, default is ">".
  • 'autocomplete': A boolean to enable or disable command autocomplete, default is True.

License

This project is licensed under the MIT License.
See the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mustiolo-0.4.0.tar.gz (17.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mustiolo-0.4.0-py3-none-any.whl (15.2 kB view details)

Uploaded Python 3

File details

Details for the file mustiolo-0.4.0.tar.gz.

File metadata

  • Download URL: mustiolo-0.4.0.tar.gz
  • Upload date:
  • Size: 17.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for mustiolo-0.4.0.tar.gz
Algorithm Hash digest
SHA256 aa8eaecbd86f55ce42228e38c9517d43815fc1055047638fe8f69f48a9983d7d
MD5 642c2a7b469cc8579c44a2c42ef31647
BLAKE2b-256 03fdb9a03f0e57857455c193400dbddbe5894bdce9e875e1139743ef343e00e0

See more details on using hashes here.

Provenance

The following attestation bundles were made for mustiolo-0.4.0.tar.gz:

Publisher: release.yaml on Cereal84/mustiolo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mustiolo-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: mustiolo-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 15.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for mustiolo-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 22b8d4257c2e3c387f2e1c9b0a45b1f12529dc1e17d7e6e3f36b3d8be7e82269
MD5 98293bc6ea196dc1f36112481ae706a9
BLAKE2b-256 535f1d1a95341b2128a51085d9995459c211c0d9a513751ceeabaa667ec57045

See more details on using hashes here.

Provenance

The following attestation bundles were made for mustiolo-0.4.0-py3-none-any.whl:

Publisher: release.yaml on Cereal84/mustiolo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page