{"id":13478049,"url":"https://github.com/BrianPugh/cyclopts","last_synced_at":"2025-03-27T07:30:43.487Z","repository":{"id":209359235,"uuid":"713675962","full_name":"BrianPugh/cyclopts","owner":"BrianPugh","description":"Intuitive, easy CLIs based on python type hints.","archived":false,"fork":false,"pushed_at":"2024-05-01T03:55:37.000Z","size":2367,"stargazers_count":207,"open_issues_count":10,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-05-01T16:33:11.892Z","etag":null,"topics":["argument-parser","cli","python","shell","typehints"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BrianPugh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/contributing.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["BrianPugh"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2023-11-03T02:24:25.000Z","updated_at":"2024-07-04T17:20:47.413Z","dependencies_parsed_at":"2023-12-02T05:34:46.766Z","dependency_job_id":"63539dd7-44fc-422e-9efd-755150d44059","html_url":"https://github.com/BrianPugh/cyclopts","commit_stats":null,"previous_names":["brianpugh/cyclopts"],"tags_count":60,"template":false,"template_full_name":"BrianPugh/python-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrianPugh%2Fcyclopts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrianPugh%2Fcyclopts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrianPugh%2Fcyclopts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrianPugh%2Fcyclopts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BrianPugh","download_url":"https://codeload.github.com/BrianPugh/cyclopts/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245359423,"owners_count":20602341,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["argument-parser","cli","python","shell","typehints"],"created_at":"2024-07-31T16:01:51.778Z","updated_at":"2025-03-27T07:30:43.472Z","avatar_url":"https://github.com/BrianPugh.png","language":"Python","readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/BrianPugh/Cyclopts/main/assets/logo_512w.png\"\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n![Python compat](https://img.shields.io/badge/\u003e=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\u003c/div\u003e\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 \u0026 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 \u003e=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╭─ Parameters ────────────────────────────────────────────────────────────────────────────────────╮\n│ *  ENV --env            Environment to deploy to. [choices: dev, staging, prod] [required]      │\n│    REPLICAS --replicas  Number of workers to spin up. [choices: default, performance] [default: │\n│                         default]                                                                │\n╰─────────────────────────────────────────────────────────────────────────────────────────────────╯\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╭─ Error ────────────────────────────────────────────────────────────────────────────────────────────╮\n│ Error converting value \"nonexistent-env\" to typing.Literal['dev', 'staging', 'prod'] for \"--env\".  │\n╰────────────────────────────────────────────────────────────────────────────────────────────────────╯\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╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────╮\n│ *    env           ENV:{dev|staging|prod}  Environment to deploy to. [default: None] [required] │\n│      replicas      [REPLICAS]              Number of workers to spin up. [default: 10]          │\n╰─────────────────────────────────────────────────────────────────────────────────────────────────╯\n╭─ Options ───────────────────────────────────────────────────────────────────────────────────────╮\n│ --help          Show this message and exit.                                                     │\n╰─────────────────────────────────────────────────────────────────────────────────────────────────╯\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╭─ Error ─────────────────────────────────────────────────────────────────────────────────────────╮\n│ Invalid value for '[REPLICAS]': nonexistent-env                                                 │\n╰─────────────────────────────────────────────────────────────────────────────────────────────────╯\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","funding_links":["https://github.com/sponsors/BrianPugh"],"categories":["Python","[Python](https://www.python.org/)"],"sub_categories":["Useful awesome list for Go cli"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FBrianPugh%2Fcyclopts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FBrianPugh%2Fcyclopts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FBrianPugh%2Fcyclopts/lists"}