Package: cyclopts
Documentation:https://cyclopts.readthedocs.ioSource Code:https://github.com/BrianPugh/cycloptsCyclopts 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.InstallationCyclopts requires Python >=3.8; to install Cyclopts, run:pip install cycloptsQuick StartCreate an application usingcyclopts.App.Register commands with thecommanddecorator.Register a default function with thedefaultdecorator.fromcycloptsimportAppapp=App()@app.commanddeffoo(loops:int):foriinrange(loops):print(f"Looping!{i}")@app.defaultdefdefault_action():print("Hello world! This runs when no command is specified.")app()Execute the script from the command line:$pythondemo.pyHello world! This runs when no command is specified.$pythondemo.pyfoo3Looping! 0Looping! 1Looping! 2With just a few additional lines of code, we have a full-featured CLI app.
Seethe docsfor more advanced usage.Compared to TyperCyclopts 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.
Seethe documentation for a complete Typer comparison.Consider the following short Cyclopts application:importcycloptsfromtypingimportLiteralapp=cyclopts.App()@app.commanddefdeploy(env:Literal["dev","staging","prod"],replicas:int|Literal["default","performance"]="default",):"""Deploy code to an environment.Parameters----------envEnvironment to deploy to.replicasNumber of workers to spin up."""ifreplicas=="default":replicas=10elifreplicas=="performance":replicas=20print(f"Deploying to{env}with{replicas}replicas.")if__name__=="__main__":app()$my-scriptdeploy--helpUsage: 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-scriptdeploystagingDeploying to staging with 10 replicas.$my-scriptdeploystaging7Deploying to staging with 7 replicas.$my-scriptdeploystagingperformanceDeploying to staging with 20 replicas.$my-scriptdeploynonexistent-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:fromtyperimportTyper,ArgumentfromtypingimportAnnotated,LiteralfromenumimportEnumapp=Typer()classEnvironment(str,Enum):dev="dev"staging="staging"prod="prod"defreplica_parser(value:str):ifvalue=="default":return10elifvalue=="performance":return20else:returnint(value)@app.callback()defdummy_callback():pass@app.command(help="Deploy code to an environment.")defdeploy(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-scriptdeploy--helpUsage: 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-scriptdeploystagingDeploying to staging with 10 replicas.$my-scriptdeploystaging7Deploying to staging with 7 replicas.$my-scriptdeploystagingperformanceDeploying to staging with 20 replicas.$my-scriptdeploynonexistent-envUsage: 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 forreplicacould 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.