cyclopts


Namecyclopts JSON
Version 3.22.5 PyPI version JSON
download
home_pagehttps://github.com/BrianPugh/cyclopts
SummaryIntuitive, easy CLIs based on type hints.
upload_time2025-07-31 18:18:37
maintainerNone
docs_urlNone
authorBrian Pugh
requires_python>=3.9
licenseApache-2.0
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <div align="center">
  <img src="https://raw.githubusercontent.com/BrianPugh/Cyclopts/main/assets/logo_512w.png">
</div>

<div align="center">

![Python compat](https://img.shields.io/badge/>=python-3.9-blue.svg)
[![PyPI](https://img.shields.io/pypi/v/cyclopts.svg)](https://pypi.org/project/cyclopts/)
[![ReadTheDocs](https://readthedocs.org/projects/cyclopts/badge/?version=latest)](https://cyclopts.readthedocs.io)
[![codecov](https://codecov.io/gh/BrianPugh/cyclopts/graph/badge.svg?token=HA393WIYUK)](https://codecov.io/gh/BrianPugh/cyclopts)


</div>

---

**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](https://docs.pydantic.dev/latest/), [Dataclasses](https://docs.python.org/3/library/dataclasses.html), and [Attrs](https://www.attrs.org/en/stable/api.html)).

- **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.9; to install Cyclopts, run:

```console
pip install cyclopts
```

# Quick Start
- Import `cyclopts.run()` and give it a function to run.

```python
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:

```console
$ 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.

```python
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:

```console
$ 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](https://cyclopts.readthedocs.io) 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](https://cyclopts.readthedocs.io/en/latest/vs_typer/README.html).

Consider the following short 29-line Cyclopts application:

```python
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()
```

```console
$ 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):

```python
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()
```

```console
$ 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.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/BrianPugh/cyclopts",
    "name": "cyclopts",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": null,
    "author": "Brian Pugh",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/a3/d5/24c6c894f3833bc93d4944c2064309dfd633c0becf93e16fc79d76edd388/cyclopts-3.22.5.tar.gz",
    "platform": null,
    "description": "<div align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/BrianPugh/Cyclopts/main/assets/logo_512w.png\">\n</div>\n\n<div align=\"center\">\n\n![Python compat](https://img.shields.io/badge/>=python-3.9-blue.svg)\n[![PyPI](https://img.shields.io/pypi/v/cyclopts.svg)](https://pypi.org/project/cyclopts/)\n[![ReadTheDocs](https://readthedocs.org/projects/cyclopts/badge/?version=latest)](https://cyclopts.readthedocs.io)\n[![codecov](https://codecov.io/gh/BrianPugh/cyclopts/graph/badge.svg?token=HA393WIYUK)](https://codecov.io/gh/BrianPugh/cyclopts)\n\n\n</div>\n\n---\n\n**Documentation:** https://cyclopts.readthedocs.io\n\n**Source Code:** https://github.com/BrianPugh/cyclopts\n\n---\n\nCyclopts is a modern, easy-to-use command-line interface (CLI) framework that aims to provide an intuitive & efficient developer experience.\n\n# Why Cyclopts?\n\n- **Intuitive API**: Quickly write CLI applications using a terse, intuitive syntax.\n\n- **Advanced Type Hinting**: Full support of all builtin types and even user-specified (yes, including [Pydantic](https://docs.pydantic.dev/latest/), [Dataclasses](https://docs.python.org/3/library/dataclasses.html), and [Attrs](https://www.attrs.org/en/stable/api.html)).\n\n- **Rich Help Generation**: Automatically generates beautiful help pages from **docstrings** and other contextual data.\n\n- **Extendable**: Easily customize converters, validators, token parsing, and application launching.\n\n# Installation\nCyclopts requires Python >=3.9; to install Cyclopts, run:\n\n```console\npip install cyclopts\n```\n\n# Quick Start\n- Import `cyclopts.run()` and give it a function to run.\n\n```python\nfrom cyclopts import run\n\ndef foo(loops: int):\n    for i in range(loops):\n        print(f\"Looping! {i}\")\n\nrun(foo)\n```\n\nExecute the script from the command line:\n\n```console\n$ python start.py 3\nLooping! 0\nLooping! 1\nLooping! 2\n```\n\nWhen you need more control:\n\n- Create an application using `cyclopts.App`.\n- Register commands with the `command` decorator.\n- Register a default function with the `default` decorator.\n\n```python\nfrom cyclopts import App\n\napp = App()\n\n@app.command\ndef foo(loops: int):\n    for i in range(loops):\n        print(f\"Looping! {i}\")\n\n@app.default\ndef default_action():\n    print(\"Hello world! This runs when no command is specified.\")\n\napp()\n```\n\nExecute the script from the command line:\n\n```console\n$ python demo.py\nHello world! This runs when no command is specified.\n\n$ python demo.py foo 3\nLooping! 0\nLooping! 1\nLooping! 2\n```\nWith just a few additional lines of code, we have a full-featured CLI app.\nSee [the docs](https://cyclopts.readthedocs.io) for more advanced usage.\n\n# Compared to Typer\nCyclopts is what you thought Typer was.\nCyclopts's includes information from docstrings, support more complex types (even Unions and Literals!), and include proper validation support.\nSee [the documentation for a complete Typer comparison](https://cyclopts.readthedocs.io/en/latest/vs_typer/README.html).\n\nConsider the following short 29-line Cyclopts application:\n\n```python\nimport cyclopts\nfrom typing import Literal\n\napp = cyclopts.App()\n\n@app.command\ndef deploy(\n    env: Literal[\"dev\", \"staging\", \"prod\"],\n    replicas: int | Literal[\"default\", \"performance\"] = \"default\",\n):\n    \"\"\"Deploy code to an environment.\n\n    Parameters\n    ----------\n    env\n        Environment to deploy to.\n    replicas\n        Number of workers to spin up.\n    \"\"\"\n    if replicas == \"default\":\n        replicas = 10\n    elif replicas == \"performance\":\n        replicas = 20\n\n    print(f\"Deploying to {env} with {replicas} replicas.\")\n\n\nif __name__ == \"__main__\":\n    app()\n```\n\n```console\n$ my-script deploy --help\nUsage: my-script.py deploy [ARGS] [OPTIONS]\n\nDeploy code to an environment.\n\n\u256d\u2500 Parameters \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 *  ENV --env            Environment to deploy to. [choices: dev, staging, prod] [required]      \u2502\n\u2502    REPLICAS --replicas  Number of workers to spin up. [choices: default, performance] [default: \u2502\n\u2502                         default]                                                                \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n\n$ my-script deploy staging\nDeploying to staging with 10 replicas.\n\n$ my-script deploy staging 7\nDeploying to staging with 7 replicas.\n\n$ my-script deploy staging performance\nDeploying to staging with 20 replicas.\n\n$ my-script deploy nonexistent-env\n\u256d\u2500 Error \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 Error converting value \"nonexistent-env\" to typing.Literal['dev', 'staging', 'prod'] for \"--env\".  \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n\n$ my-script --version\n0.0.0\n```\n\nIn its current state, this application would be impossible to implement in Typer.\nHowever, lets see how close we can get with Typer (47-lines):\n\n```python\nimport typer\nfrom typing import Annotated, Literal\nfrom enum import Enum\n\napp = typer.Typer()\n\nclass Environment(str, Enum):\n    dev = \"dev\"\n    staging = \"staging\"\n    prod = \"prod\"\n\ndef replica_parser(value: str):\n    if value == \"default\":\n        return 10\n    elif value == \"performance\":\n        return 20\n    else:\n        return int(value)\n\ndef _version_callback(value: bool):\n    if value:\n        print(\"0.0.0\")\n        raise typer.Exit()\n\n@app.callback()\ndef callback(\n    version: Annotated[\n        bool | None, typer.Option(\"--version\", callback=_version_callback)\n    ] = None,\n):\n    pass\n\n@app.command(help=\"Deploy code to an environment.\")\ndef deploy(\n    env: Annotated[Environment, typer.Argument(help=\"Environment to deploy to.\")],\n    replicas: Annotated[\n        int,\n        typer.Argument(\n            parser=replica_parser,\n            help=\"Number of workers to spin up.\",\n        ),\n    ] = replica_parser(\"default\"),\n):\n    print(f\"Deploying to {env.name} with {replicas} replicas.\")\n\nif __name__ == \"__main__\":\n    app()\n```\n\n```console\n$ my-script deploy --help\n\nUsage: my-script deploy [OPTIONS] ENV:{dev|staging|prod} [REPLICAS]\n\n Deploy code to an environment.\n\n\u256d\u2500 Arguments \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 *    env           ENV:{dev|staging|prod}  Environment to deploy to. [default: None] [required] \u2502\n\u2502      replicas      [REPLICAS]              Number of workers to spin up. [default: 10]          \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n\u256d\u2500 Options \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 --help          Show this message and exit.                                                     \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n\n$ my-script deploy staging\nDeploying to staging with 10 replicas.\n\n$ my-script deploy staging 7\nDeploying to staging with 7 replicas.\n\n$ my-script deploy staging performance\nDeploying to staging with 20 replicas.\n\n$ my-script deploy nonexistent-env\nUsage: my-script.py deploy [OPTIONS] ENV:{dev|staging|prod} [REPLICAS]\nTry 'my-script.py deploy --help' for help.\n\u256d\u2500 Error \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 Invalid value for '[REPLICAS]': nonexistent-env                                                 \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n\n$ my-script --version\n0.0.0\n```\n\nThe Typer implementation is 47 lines long, while the Cyclopts implementation is just 29 (38% shorter!).\nNot only is the Cyclopts implementation significantly shorter, but the code is easier to read.\nSince Typer does not support Unions, the choices for ``replica`` could not be displayed on the help page.\nCyclopts is much more terse, much more readable, and much more intuitive to use.\n",
    "bugtrack_url": null,
    "license": "Apache-2.0",
    "summary": "Intuitive, easy CLIs based on type hints.",
    "version": "3.22.5",
    "project_urls": {
        "Homepage": "https://github.com/BrianPugh/cyclopts",
        "Repository": "https://github.com/BrianPugh/cyclopts"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "dfe5a7b6db64f08cfe065e531ec6b508fa7dac704fab70d05adb5bc0c2c1d1b6",
                "md5": "2421d0037c6b5ce313e2d4538b063d61",
                "sha256": "92efb4a094d9812718d7efe0bffa319a19cb661f230dbf24406c18cd8809fb82"
            },
            "downloads": -1,
            "filename": "cyclopts-3.22.5-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2421d0037c6b5ce313e2d4538b063d61",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 84994,
            "upload_time": "2025-07-31T18:18:35",
            "upload_time_iso_8601": "2025-07-31T18:18:35.939951Z",
            "url": "https://files.pythonhosted.org/packages/df/e5/a7b6db64f08cfe065e531ec6b508fa7dac704fab70d05adb5bc0c2c1d1b6/cyclopts-3.22.5-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a3d524c6c894f3833bc93d4944c2064309dfd633c0becf93e16fc79d76edd388",
                "md5": "304d558fc74d2fd40619316ffe6da3ed",
                "sha256": "fa2450b9840abc41c6aa37af5eaeafc7a1264e08054e3a2fe39d49aa154f592a"
            },
            "downloads": -1,
            "filename": "cyclopts-3.22.5.tar.gz",
            "has_sig": false,
            "md5_digest": "304d558fc74d2fd40619316ffe6da3ed",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 74890,
            "upload_time": "2025-07-31T18:18:37",
            "upload_time_iso_8601": "2025-07-31T18:18:37.336349Z",
            "url": "https://files.pythonhosted.org/packages/a3/d5/24c6c894f3833bc93d4944c2064309dfd633c0becf93e16fc79d76edd388/cyclopts-3.22.5.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-31 18:18:37",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "BrianPugh",
    "github_project": "cyclopts",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "cyclopts"
}
        
Elapsed time: 1.07074s