• Stars
    star
    278
  • Rank 148,454 (Top 3 %)
  • Language
    Python
  • License
    Apache License 2.0
  • Created about 1 year ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Intuitive, easy CLIs based on python type hints.

Python compat PyPI ReadTheDocs codecov


Documentation: https://cyclopts.readthedocs.io

Source Code: https://github.com/BrianPugh/cyclopts


Cyclopts is a modern, easy-to-use command-line interface (CLI) framework. It offers a streamlined approach for building CLI applications with an emphasis on simplicity, extensibility, and robustness. Cyclopts aims to provide an intuitive and efficient developer experience, making python CLI development more accessible and enjoyable.

Why Cyclopts?

  • Intuitive API: Cyclopts features a straightforward and intuitive API, making it easy for developers to create complex CLI applications with minimal code.

  • Advanced Type Hinting: Cyclopts offers advanced type hinting features, allowing for more accurate and informative command-line interfaces.

  • Rich Help Generation: Automatically generates beautiful, user-friendly help messages, ensuring that users can easily understand and utilize your CLI application.

  • Extensible and Customizable: Designed with extensibility in mind, Cyclopts allows developers to easily add custom behaviors and integrate with other systems.

Installation

Cyclopts requires Python >=3.8; to install Cyclopts, run:

pip install cyclopts

Quick Start

  • Create an application using cyclopts.App.
  • Register commands with the command decorator.
  • Register a default function with the default decorator.
from cyclopts import App

app = App()


@app.command
def foo(loops: int):
    for i in range(loops):
        print(f"Looping! {i}")


@app.default
def default_action():
    print("Hello world! This runs when no command is specified.")


app()

Execute the script from the command line:

$ python demo.py
Hello world! This runs when no command is specified.

$ python demo.py foo 3
Looping! 0
Looping! 1
Looping! 2

With just a few additional lines of code, we have a full-featured CLI app. See the docs for more advanced usage.

Compared to Typer

Cyclopts is what you thought Typer was. Cyclopts's includes information from docstrings, support more complex types (even Unions and Literals!), and include proper validation support. See the documentation for a complete Typer comparison.

Consider the following short Cyclopts application:

import cyclopts
from typing import Literal

app = cyclopts.App()


@app.command
def deploy(
    env: Literal["dev", "staging", "prod"],
    replicas: int | Literal["default", "performance"] = "default",
):
    """Deploy code to an environment.

    Parameters
    ----------
    env
        Environment to deploy to.
    replicas
        Number of workers to spin up.
    """
    if replicas == "default":
        replicas = 10
    elif replicas == "performance":
        replicas = 20

    print(f"Deploying to {env} with {replicas} replicas.")


if __name__ == "__main__":
    app()
$ my-script deploy --help
Usage: my-script deploy [ARGS] [OPTIONS]

Deploy code to an environment.

╭─ Parameters ────────────────────────────────────────────────────────────────────────────────────────────╮
│ *  ENV,--env            Environment to deploy to. [choices: dev,staging,prod] [required]                │
│    REPLICAS,--replicas  Number of workers to spin up. [choices: default,performance] [default: default] │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯

$ my-script deploy staging
Deploying to staging with 10 replicas.

$ my-script deploy staging 7
Deploying to staging with 7 replicas.

$ my-script deploy staging performance
Deploying to staging with 20 replicas.

$ my-script deploy nonexistent-env
╭─ Error ────────────────────────────────────────────────────────────────────────────────────────────╮
│ Error converting value "nonexistent-env" to typing.Literal['dev', 'staging', 'prod'] for "--env".  │
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯

In its current state, this application would be impossible to implement in Typer. However, lets see how close we can get with Typer:

from typer import Typer, Argument
from typing import Annotated, Literal
from enum import Enum

app = Typer()


class Environment(str, Enum):
    dev = "dev"
    staging = "staging"
    prod = "prod"


def replica_parser(value: str):
    if value == "default":
        return 10
    elif value == "performance":
        return 20
    else:
        return int(value)


@app.callback()
def dummy_callback():
    pass


@app.command(help="Deploy code to an environment.")
def deploy(
    env: Annotated[Environment, Argument(help="Environment to deploy to.")],
    replicas: Annotated[
        int,
        Argument(
            parser=replica_parser,
            help="Number of workers to spin up.",
        ),
    ] = replica_parser("default"),
):
    print(f"Deploying to {env.name} with {replicas} replicas.")


if __name__ == "__main__":
    app()
$ my-script deploy --help
Usage: my-script [OPTIONS] ENV:{dev|staging|prod} [REPLICAS]

 Deploy code to an environment.

╭─ Arguments ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ *    env           ENV:{dev|staging|prod}  Environment to deploy to. [default: None] [required]                                       │
│      replicas      [REPLICAS]              Number of workers to spin up. [default: 10]                                                │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --install-completion        [bash|zsh|fish|powershell|pwsh]  Install completion for the specified shell. [default: None]              │
│ --show-completion           [bash|zsh|fish|powershell|pwsh]  Show completion for the specified shell, to copy it or customize the     │
│                                                              installation.                                                            │
│                                                              [default: None]                                                          │
│ --help                                                       Show this message and exit.                                              │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

$ my-script deploy staging
Deploying to staging with 10 replicas.

$ my-script deploy staging 7
Deploying to staging with 7 replicas.

$ my-script deploy staging performance
Deploying to staging with 20 replicas.

$ my-script deploy nonexistent-env
Usage: my-script deploy [OPTIONS] ENV:{dev|staging|prod} [REPLICAS]
Try 'my-script deploy --help' for help.
╭─ Error ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Invalid value for 'ENV:{dev|staging|prod}': 'nonexistent-env' is not one of 'dev', 'staging', 'prod'.                                  │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

The Typer implementation is 43 lines long, while the Cyclopts implementation is just 30, all while including a proper docstring. Since Typer doesn't support Unions, the choices for replica could not be displayed on the help page. We also had to include a dummy callback since our application currently only has a single command. Cyclopts is much more terse, much more readable, and much more intuitive to use.

More Repositories

1

python-template

Python project and library template for clean, reliable, open-source projects.
Python
257
star
2

belay

Belay is a python library that enables the rapid development of projects that interact with hardware via a micropython-compatible board.
Python
236
star
3

game-and-watch-patch

CFW for the Nintendo Game and Watch
C
138
star
4

lox

Threading and Multiprocessing made easy.
Python
90
star
5

autoregistry

Automatic registry design-pattern library for mapping names to functionality.
Python
38
star
6

tamp

Tamp is a low-memory, DEFLATE-inspired lossless compression library.
Python
24
star
7

gnwmanager

C
15
star
8

cookiecutter-esp32-webserver

Cookiecutter template to get you quickly started with an ESP32-based webserver project.
C
13
star
9

cookiecutter-esp-idf-component

Cookiecutter template for an ESP-IDF component
C
5
star
10

pugh_torch

Functions, losses, and module blocks to share between experiments.
Python
4
star
11

micropython-libs

Python
4
star
12

install-micropython

Github Action to install micropython.
JavaScript
4
star
13

python-rai-rpc

Rai_node rpc commands wrapped for python
Python
3
star
14

RaiBlocks-Live-TPS

A simple python script that will display the transactions per second (TPS) of the incoming transactions on a rai_node/rai_wallet.
Python
3
star
15

magnetometer

CLI Magnetometer using a CircuitPython board + Belay
Python
3
star
16

micropython-fnv1a32

Micropython native module for the FNV1a hashing algorithm.
Python
3
star
17

raiblocks-docker

Docker for RaiBlocks
Dockerfile
2
star
18

esp-tamp-demo

C
2
star
19

makerdiary-m60-config

Lightweight repo to be directly used on the 8MB partition of the MakerDiary M60 keyboard
Python
2
star
20

micropython-native-module-template

Template for creating Micropython native module libraries with pre-built binaries
Python
2
star
21

dotfiles

Shell
1
star
22

pytorch-lightning-template

Python
1
star
23

monerod-docker

Dockerfile
1
star
24

esp32_unit_tester

ESP32 Unit Test App for ESP-IDF
C
1
star