Skip to content

An easy to use tool that turns scripts into command line utilities.

Notifications You must be signed in to change notification settings

inno/pysimplecli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

89 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pysimplecli

codecov CI

Want to turn your script into a command line utility, but don't want the overhead of argparse?

You've come to the right place!

Simply import and wrap whatever function you'd like to expose. That's it!

Function variables are turned into CLI parameters, complete with input validation and help text generated from inline comments.

Install from PyPI

pip install pysimplecli

Quick Start

To convert a Python function into a CLI command, simply use the wrap decorator from the simplecli module. Here is a simple example:

import simplecli


@simplecli.wrap
def main(
    name: str,  # Person to greet
) -> None:
    print(f"Hello, {name}!")
$ python3 myprogram.py --name="Dade Murphy"
Hello, Dade Murphy!

That's it!

Features

No configuration to worry about

Everything Just Works™ out of the box.

Autogenerated help

Parameters show up automatically and comments make them more friendly.

$ python3 hello.py --help
Usage:
  hello.py [name]

Options:
  --name  Person to greet
  --help  Display hello.py version

Unicode support

$ python3 hello.py --name="Donatello 🐢"
Hello, Donatello 🐢!

Cross-platform

Tested on Windows/Linux/Mac with Python >= 3.9.

Required parameters are also positional

Want to pass positional instead of named parameters? All required parameters are available in top-down order.

$ python3 hello.py "El Duderino"
Hello, El Duderino!

Autogenerated version parameter

If you have the dunder variable __version__ set, you get a --version parameter.

import simplecli
___version___ = "1.2.3"


@simplecli.wrap
def main(
    name: str,  # Person to greet
) -> None:
    print(f"Hello, {name}!")
$ python3 hello.py --version
hello.py version 1.2.3

Automatic docstring to help description

Want to add some detail to your help output? Just make a docstring in the function that's being wrapped.

import simplecli
__version__ = "1.2.3"


@simplecli.wrap
def main(
    name: str,  # Person to greet
) -> None:
    """
    This is a utility that greets whoever engages it. It's a simple example
    of how to use the `simplecli` utility module. Give it a try!
    """
    print(f"Hello, {name}!")
$ python3 hello.py --help
Usage:
  hello.py [name]

Description:
  This is a utility that greets whoever engages it. It's a simple example
  of how to use the `simplecli` utility module. Give it a try!

Options:
  --name      Person to greet
  --help      Show this message
  --version   Display hello.py version

Gotchas

"Required" may be a bit confusing

A parameter becomes "required" if it is an int, float or str and does not have a default.

bool arguments cannot be required as there are only two possible states. Instead, they are False by default and become True when passed as a flag.

Complex variables are not supported

Only simple variable types (bool, float, int, str) are currently supported. Mapping and sequence types might be supported in the future but anything else is beyond the scope of this utility.

Only one @wrap allowed per file

With more than one decorator, it's impossible to tell which function you'd like to wrap. Because of this, we enforce a single @wrap per file. Importing modules using pysimplecli is supported, as is calling said wrapped functions.

Truth table for boolean parameters

Parameter Default Without Argument With Argument
True True False
False False True
no default False True

How It Works

The wrap decorator takes the annotated parameters of a given function and maps them to corresponding command-line arguments. It relies heavily on Python's inspect and tokenize modules to gather parameters, parse comments for parameter descriptions, determine default functionality, etc... In fact, a core part of this module is a are heavily extended inspect.Parameter objects.

Why not just use argparse?

argparse is great for advanced input control. Unfortunately, that level of control means considerably more overhead for simple utilities. To be clear, this is not intended to replace argparse. simplecli is meant to easily expose a python function as a script.

(I've also felt conflicted about dunder names being used openly in if __name__ == "__main__":)

Here's an example of an argparse solution

import argparse
import sys
__version__ = "1.2.3"


def add(a: int = 5, b: int = 10) -> None:
    print(a + b)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--a", default=5, required=True, type=int,
                        help="First integer to add")
    parser.add_argument("--b", default=10, required=True, type=int,
                        help="Second integer to add")
    parser.add_argument("--version", action="version",
                        version=f"{sys.argv[0]} version {__version__}")
    args = parser.parse_args()
    add(args.a, args.b)

Here's the same example, but with simplecli

from simplecli import wrap
__version__ = "1.2.3"


@wrap
def add(
    a: int = 5,  # First integer to add
    b: int = 10,  # Second integer to add
) -> None:
    print(a + b)

Contributing

Feel free to open an issue and create a pull request!

License

pysimplecli © 2024 by Clif Bratcher is licensed under CC BY 4.0

About

An easy to use tool that turns scripts into command line utilities.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages