Cleo

raw JSON →
2.1.0 verified Tue May 12 auth: no python install: verified quickstart: verified

Cleo is a Python library designed to help developers create beautiful and testable command-line interfaces. It draws significant inspiration from the Symfony Console Component, providing a robust and familiar structure for building CLI applications. The library is actively maintained, with a release cadence that includes support for new Python versions and essential bug fixes, as demonstrated by the recent 2.1.0 update.

pip install cleo
error ModuleNotFoundError: No module named 'cleo'
cause The 'cleo' library is not installed in the current Python environment or is not accessible to the Python interpreter being used.
fix
Install the library using pip: pip install cleo
error Not enough arguments (missing: "<argument_name>")
cause When programmatically calling a command using `self.call()` from within another Cleo command, the arguments for the target command were not passed or parsed in the expected string format, leading to missing required arguments.
fix
Ensure arguments for the called command are passed as a single string to self.call(), often including the command name itself as the first part of the argument string, e.g., self.call("target_command_name", "target_command_name arg_value --option").
error The command "<command_name>" does not exist.
cause The command you are trying to execute or get help for has not been registered with the Cleo `Application` instance, or there's an issue with how nested commands are identified.
fix
Verify that your command class is properly added to the Application using application.add(YourCommand()). For nested commands, ensure the full command name (e.g., namespace:command) is correctly passed.
error No such option: --<option_name>
cause An option was provided on the command line that has not been defined in the command's signature, or there is a typo in the option name.
fix
Define the expected option in the command's docstring using the {--option_name} or {--option_name=default} syntax, or correct any typos in the option name being passed.
breaking Cleo versions 1.0.0 and 2.0.0 removed the `clikit` dependency, signifying a major internal refactoring. Code relying on `clikit` directly or specific `clikit`-based behaviors within Cleo will break.
fix Review your code for direct or indirect `clikit` usage. Adapt to Cleo's native implementations for input/output handling, command definition, and other CLI functionalities. The library is now self-contained in this regard.
breaking The `Terminal` class was removed and replaced with `shutil.get_terminal_size()` from Python's standard library. Direct calls to `cleo.terminal.Terminal` will fail.
fix Replace usages of `cleo.terminal.Terminal` with `shutil.get_terminal_size()` for terminal dimension retrieval.
breaking The exception hierarchy changed in versions 1.0.0 / 2.0.0. Specifically, `CleoException` was renamed or replaced by `CleoError`.
fix Update exception handling blocks to catch `CleoError` instead of `CleoException` and adjust for any other related exception class changes.
breaking Doc comment-based command configuration (where arguments and options were defined in the command's docstring) was removed in versions 1.0.0 / 2.0.0.
fix Migrate command argument and option definitions to use the `arguments` and `options` class variables with `cleo.helpers.argument` and `cleo.helpers.option` decorators. Refer to the quickstart example for the new syntax.
gotcha Cleo version 1.0.0 was yanked shortly after its release due to compatibility issues with other projects (e.g., Poetry) that depended on its pre-release versions. Version 2.0.0 was released immediately after with no source code changes to effectively replace 1.0.0.
fix Avoid using `cleo==1.0.0`. If targeting the feature set introduced in `1.0.0`, ensure you use `cleo>=2.0.0` (e.g., `pip install 'cleo>=2.0.0,<3.0.0'`).
python os / libc status wheel install import disk
3.10 alpine (musl) wheel - 0.16s 34.7M
3.10 alpine (musl) - - 0.14s 34.7M
3.10 slim (glibc) wheel 2.9s 0.14s 31M
3.10 slim (glibc) - - 0.14s 31M
3.11 alpine (musl) wheel - 0.20s 36.7M
3.11 alpine (musl) - - 0.22s 36.7M
3.11 slim (glibc) wheel 2.5s 0.18s 33M
3.11 slim (glibc) - - 0.16s 33M
3.12 alpine (musl) wheel - 0.17s 28.5M
3.12 alpine (musl) - - 0.18s 28.5M
3.12 slim (glibc) wheel 2.2s 0.17s 25M
3.12 slim (glibc) - - 0.17s 25M
3.13 alpine (musl) wheel - 0.15s 28.3M
3.13 alpine (musl) - - 0.17s 28.2M
3.13 slim (glibc) wheel 2.3s 0.17s 25M
3.13 slim (glibc) - - 0.16s 25M
3.9 alpine (musl) wheel - 0.10s 34.4M
3.9 alpine (musl) - - 0.11s 34.4M
3.9 slim (glibc) wheel 3.4s 0.11s 30M
3.9 slim (glibc) - - 0.10s 30M

This quickstart demonstrates how to create a basic command-line application with Cleo. It defines a `GreetCommand` that accepts an optional name argument and a 'yell' flag. The command uses `cleo.helpers.argument` and `cleo.helpers.option` for modern argument and option definitions. An `Application` instance registers the command, and a simulated `io` stream is used to run the command programmatically for demonstration purposes, mimicking command-line execution.

import os
from cleo.application import Application
from cleo.commands.command import Command
from cleo.helpers import argument, option


class GreetCommand(Command):
    name = "greet"
    description = "Greets someone"

    arguments = [
        argument("name", description="Who do you want to greet?", optional=True)
    ]
    options = [
        option("yell", "y", description="If set, the task will yell in uppercase letters", flag=True)
    ]

    def handle(self):
        name = self.argument("name")
        if name:
            text = f"Hello {name}"
        else:
            text = "Hello"

        if self.option("yell"):
            text = text.upper()

        self.line(text)


application = Application("My CLI App", "1.0.0")
application.add(GreetCommand())

if __name__ == "__main__":
    # Example of how to run, in a real app this would be called via command line
    # e.g., `python your_script.py greet John --yell`
    # For quickstart, we'll simulate it for programmatic execution and testing
    try:
        # Simulate a command-line call for demonstration
        # In a real scenario, this would be invoked via sys.argv
        from cleo.io.buffered_io import BufferedIO
        from cleo.io.inputs.string_input import StringInput
        from cleo.io.outputs.buffered_output import BufferedOutput
        
        # Example: running 'greet John --yell'
        input_stream = StringInput('greet John --yell')
        output_stream = BufferedOutput()
        error_stream = BufferedOutput()
        
        io = BufferedIO(input_stream, output_stream, error_stream)
        application.run(io)
        
        print("\n--- Output ---")
        print(output_stream.fetch())
        print("--- Error ---")
        print(error_stream.fetch())
        print("--------------")

        # Example: running 'greet'
        input_stream_no_args = StringInput('greet')
        output_stream_no_args = BufferedOutput()
        error_stream_no_args = BufferedOutput()

        io_no_args = BufferedIO(input_stream_no_args, output_stream_no_args, error_stream_no_args)
        application.run(io_no_args)

        print("\n--- Output (No Args) ---")
        print(output_stream_no_args.fetch())
        print("--- Error (No Args) ---")
        print(error_stream_no_args.fetch())
        print("------------------------")

    except Exception as e:
        print(f"An error occurred: {e}")