Skip to main content

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 that aims to provide an intuitive & efficient developer experience.

Why Cyclopts?

  • Intuitive API: Quickly write CLI applications using a terse, intuitive syntax.

  • Advanced Type Hinting: Full support of all builtin types and even user-specified (yes, including Pydantic, Dataclasses, and Attrs).

  • Rich Help Generation: Automatically generates beautiful help pages from docstrings and other contextual data.

  • Extendable: Easily customize converters, validators, token parsing, and application launching.

Installation

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

pip install cyclopts

Quick Start

  • Import cyclopts.run() and give it a function to run.
from cyclopts import run

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

run(foo)

Execute the script from the command line:

$ python start.py 3
Looping! 0
Looping! 1
Looping! 2

When you need more control:

  • 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 29-line 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.py 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".  │
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯

$ my-script --version
0.0.0

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

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

app = typer.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)

def _version_callback(value: bool):
    if value:
        print("0.0.0")
        raise typer.Exit()

@app.callback()
def callback(
    version: Annotated[
        bool | None, typer.Option("--version", callback=_version_callback)
    ] = None,
):
    pass

@app.command(help="Deploy code to an environment.")
def deploy(
    env: Annotated[Environment, typer.Argument(help="Environment to deploy to.")],
    replicas: Annotated[
        int,
        typer.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 deploy [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 ───────────────────────────────────────────────────────────────────────────────────────╮
│ --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.py deploy [OPTIONS] ENV:{dev|staging|prod} [REPLICAS]
Try 'my-script.py deploy --help' for help.
╭─ Error ─────────────────────────────────────────────────────────────────────────────────────────╮
│ Invalid value for '[REPLICAS]': nonexistent-env                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯

$ my-script --version
0.0.0

The Typer implementation is 47 lines long, while the Cyclopts implementation is just 29 (38% shorter!). Not only is the Cyclopts implementation significantly shorter, but the code is easier to read. Since Typer does not support Unions, the choices for replica could not be displayed on the help page. Cyclopts is much more terse, much more readable, and much more intuitive to use.

Contributing

Contributions are welcome! See CONTRIBUTING.md for development setup, coding standards, and how to submit a pull request.

Download files

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

Source Distribution

cyclopts-4.23.1.tar.gz (196.1 kB view details)

Uploaded Source

Built Distribution

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

cyclopts-4.23.1-py3-none-any.whl (236.2 kB view details)

Uploaded Python 3

File details

Details for the file cyclopts-4.23.1.tar.gz.

File metadata

  • Download URL: cyclopts-4.23.1.tar.gz
  • Upload date:
  • Size: 196.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cyclopts-4.23.1.tar.gz
Algorithm Hash digest
SHA256 ec8e776bf62f78d705766333ab8621a21ec0e40cd1af3f96f362231a3df4528a
MD5 49af91b9cd92d7ad2dfa0ad1407e5d64
BLAKE2b-256 da562768e4ce655cdc49d734401c3802f0ce6aff84d9693069df0678550a58d8

See more details on using hashes here.

Provenance

The following attestation bundles were made for cyclopts-4.23.1.tar.gz:

Publisher: deploy.yaml on BrianPugh/cyclopts

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

File details

Details for the file cyclopts-4.23.1-py3-none-any.whl.

File metadata

  • Download URL: cyclopts-4.23.1-py3-none-any.whl
  • Upload date:
  • Size: 236.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cyclopts-4.23.1-py3-none-any.whl
Algorithm Hash digest
SHA256 08827bd4273eb70eb814810dd493b800ff38629fe904f2051d8b48904d0754ff
MD5 a51c2a9430566c3909bfc44955580935
BLAKE2b-256 0cfe7a266de66b3fcfcebbde248742c923f0e38aafa59dfa358096c01e34a62a

See more details on using hashes here.

Provenance

The following attestation bundles were made for cyclopts-4.23.1-py3-none-any.whl:

Publisher: deploy.yaml on BrianPugh/cyclopts

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

Release history Release notifications | RSS feed

4.23.2

2 files

This release

4.23.1 This release

2 files

4.23.0

2 files

4.22.5

2 files

4.22.4

2 files

4.22.3

2 files

4.22.2

2 files

4.22.1

2 files

4.22.0

2 files

4.21.2

2 files

4.21.1

2 files

4.21.0

2 files

4.20.0

2 files

4.19.0

2 files

4.18.0

2 files

4.17.0

2 files

4.16.1

2 files

4.16.0

2 files

4.15.0

2 files

4.14.1

2 files

4.14.0

2 files

4.13.0

2 files

4.12.0

2 files

4.11.2

2 files

4.11.1

2 files

4.11.0

2 files

4.10.2

2 files

4.10.1

2 files

4.10.0

2 files

4.9.0

2 files

4.8.0

2 files

4.7.0

2 files

4.6.0

2 files

4.5.4

2 files

4.5.3

2 files

4.5.2

2 files

4.5.1

2 files

4.5.0

2 files

4.4.6

2 files

4.4.5

2 files

4.4.4

2 files

4.4.3

2 files

4.4.2

2 files

4.4.1

2 files

4.4.0

2 files

4.3.0

2 files

4.2.5

2 files

4.2.4

2 files

4.2.3

2 files

4.2.2

2 files

4.2.1

2 files

4.2.0

2 files

4.1.0

2 files

4.0.0

2 files

3.24.0

2 files

3.23.1

2 files

3.23.0

2 files

3.22.5

2 files

3.22.4

2 files

3.22.3

2 files

3.22.2

2 files

3.22.1

2 files

3.22.0

2 files

3.21.0

2 files

3.20.0

2 files

3.19.0

2 files

3.18.0

2 files

3.17.0

2 files

3.16.2

2 files

3.16.1

2 files

3.16.0

2 files

3.15.0

2 files

3.14.2

2 files

3.14.1

2 files

3.14.0

2 files

3.13.1

2 files

3.13.0

2 files

3.12.0

2 files

3.11.2

2 files

3.11.1

2 files

3.11.0

2 files

3.10.1

2 files

3.10.0

2 files

3.9.3

2 files

3.9.2

2 files

3.9.1

2 files

3.9.0

2 files

3.8.1

2 files

3.8.0

2 files

3.7.0

2 files

3.6.0

2 files

3.5.1

2 files

3.5.0

2 files

3.4.1

2 files

3.4.0

2 files

3.3.1

2 files

3.3.0

2 files

3.2.1

2 files

3.2.0

2 files

3.1.5

2 files

3.1.4

2 files

3.1.3

2 files

3.1.2

2 files

3.1.1

2 files

3.1.0

2 files

3.0.1

2 files

3.0.0

2 files

2.9.9

2 files

2.9.8

2 files

2.9.7

2 files

2.9.6

2 files

2.9.5

2 files

2.9.4

2 files

2.9.3

2 files

2.9.2

2 files

2.9.1

2 files

2.9.0

2 files

2.8.0

2 files

2.7.1

2 files

2.7.0

2 files

2.6.2

2 files

2.6.1

2 files

2.6.0

2 files

2.5.0

2 files

2.4.2

2 files

2.4.1

2 files

2.4.0

2 files

2.3.2

2 files

2.3.1

2 files

2.3.0

2 files

2.2.0

2 files

2.1.2

2 files

2.1.1

2 files

2.1.0

2 files

2.0.0

2 files

1.3.0

2 files

1.2.0

2 files

1.1.1

2 files

1.1.0

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

0.2.0

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

0.0.0

2 files

Supported by

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