Skip to main content

A package to build simple command line tools in the declarative way.

Project description

Sicli

Introduction

sicli is a wrapper around standard library module argparse that lets you create CLIs in a simple, declarative way. It parses the signature of your type-hinted function and generates CLI from that.

sicli is not intended for big and complex applications, so if you need that, you rather need to use a framework like Click or Typer.

Quickstart

Let's create a simple example:

import sicli

from typing import Literal
from typing import Annotated as Ann
from pathlib import Path


def congratulate(
    # positional arguments go here
    reason: str,
    gift: Ann[str, "What to give them"], # help message
    language: Literal["en", "fr"] = "en", # choice of values 

    # options go here
    *,
    output: Path = Path("./out.txt"),
    loud: Ann[bool, "IF ENABLED THEN SCREAM"], # flag
    names: list[str] = ["Maria"], # multiple arguments
) -> None:
    """
    This program congratulates everyone if you haven't guessed.
    By the way, this doctring is the help message for the CLI.
    """
    if language == "fr":
        print("But I don't speak french...")
        return
    for name in names:
        if loud:
            print(f"happy {reason}, {name}!!1!".upper())
        else:
            print(f"happy {reason}, {name}.")
        print(f"Here's your {gift}")
    print("Writing to file", output.resolve())


if __name__ == "__main__":
    sicli.run(congratulate)

This produces the following output for --help:

$ python3 -m examples.congrat --help
usage: congrat.py [-h] [--output [OUTPUT]] [--loud] [--names [NAMES ...]] reason gift [{en,fr}]

This program congratulates everyone if you haven't guessed. By the way, this doctring is the help message for the CLI.

positional arguments:
  reason
  gift                 What to give them
  {en,fr}

options:
  -h, --help           show this help message and exit
  --output [OUTPUT]
  --loud               IF ENABLED THEN SCREAM
  --names [NAMES ...]

Let's now test out CLI:

$ python3 -m examples.congrat Christmas chocolate en --loud --output ./xm.txt --names Elizabath Maria
HAPPY CHRISTMAS, ELIZABATH!!1!
Here's your chocolate
HAPPY CHRISTMAS, MARIA!!1!
Here's your chocolate
Writing to file /home/alex/repos/sicli/xm.txt

Usage

Creating entry point

The main API of the library is sicli.run. If it's called with a list of functions, they will work as subcommands. You can pass CLI arguments as the second argument to parse them instead of sys.argv[1:]. Also, you can pass any **kwargs to argparse.ArgumentParser.

Example:

>>> import sicli
>>> def create_table(*, name: str):
...     print("inited")
... 
>>> def drop_table(*, name: str):
...     print("dropped")
... 
>>> sicli.run([create_table, drop_table], ["drop-table", "--name", "students"], description="cli for your db")
dropped

Arguments vs options

  • Regular arguments are mapped to positional arguments in CLI.
  • Keyword-only arguments (after *) are mapped to options.

Default values

Default values for both types of arguments are mapped, as your intuition would suggest, to default arguments in CLI. For flags, default value is always False and don't have to be set.

Types

typing.Annotated[T, help, args, kwargs]

Annotated in Python is the way to store metadata inside a valid type. So, sicli uses

  • first argument as the type and does whatever would be done with it
  • argument of type str as help for this argument.
  • argument of type list as names for argument.
  • argument of type dict to merge it with *kwargs for argparse.ArgumentParser.add_argument (see argparse docs).

Example:

>>> import sicli
>>> from typing import Annotated as Ann
>>> def divide(
...     *,
...     numbers: Ann[
...         list[int],
...         "Numbers to divide",
...         ["+n", "+numbers"],
...         {"nargs": 2},
...     ],
... ):
...     print(numbers[0] / numbers[1])
... 
>>> sicli.run(divide, ["+numbers", "1", "2"], prefix_chars="-+")
0.5

list[T]

As you saw, list[T] lets you pass multiple arguments. tuple[...] (for heterogeneous types) is not currently not supported.

typing.Literal[A, B, ...]

Literal[A, B, ...] (of the same type) lets you restrict values.

enum.Enum

It is recommended to use Literal instead, but enum.Enum can work similarly. To use it, you need to create __str__ method like that:

class Color(Enum):
    red = "r"
    black = "b"

    def __str__(self) -> str:
        return self.value

bool

bool is interpreted as flag. Its default value is always False and shouldn't be set.

Other types

Any other primitive type that you would pass to type argument in argparse.ArgumentParser.add_argument would work. For instance, int, str, Path.

Overriding types

You can use dict in Annotated to override type for argparse.ArgumentParser.add_argument:

def example(
    s: Annotated[str, {"type": ascii}],
):

You could've used ascii directly as type annotation here if you don't care that your type checker will complain.

Limitations

Note that arbitrary nesting of types is not supported (Like in list[Annotated[Literal[1, 2, 3], {}]]). Only Annotated can wrap other generic types.

Requirements

No dependencies are needed, only pure Python ≥ 3.10.

Installation

Not available in PyPi for now. Clone the repo and build it yourself.

git clone git@github.com:immanelg/sicli.git

And then

flit build
flit install

Motivation

For fun.

Alternatives

Click: The greatest CLI toolkit. Use it if you want to have a complex CLI.

Typer: Cool Click wrapper, similar to sicli.

Plac: Simple wrapper for argparse.

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

sicli-cli-0.1.0.tar.gz (8.4 kB view details)

Uploaded Source

Built Distribution

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

sicli_cli-0.1.0-py3-none-any.whl (7.1 kB view details)

Uploaded Python 3

File details

Details for the file sicli-cli-0.1.0.tar.gz.

File metadata

  • Download URL: sicli-cli-0.1.0.tar.gz
  • Upload date:
  • Size: 8.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-requests/2.29.0

File hashes

Hashes for sicli-cli-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fb87e955834c3fdfa3a52193ac446c618e9dc4af1856a7b088b33ce3f92a4d6d
MD5 3d1da8320bcd2ae711451753726662f0
BLAKE2b-256 4f3e67555f330a01da7d30381aa1c2566123197ee29d15b175d17f34c996d7e1

See more details on using hashes here.

File details

Details for the file sicli_cli-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: sicli_cli-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 7.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-requests/2.29.0

File hashes

Hashes for sicli_cli-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e114452a8f21e4d73cae248fcb6720f9081aae6e5ae66b770b2b9734255e6a14
MD5 c0040062eb2476635372ad59cbd7815b
BLAKE2b-256 f17736358e3f509844032ba5b4360d9acaf1ba10cacbc6fa557b5ecbf4fe0e57

See more details on using hashes here.

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