Cleo
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.
Common errors
-
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.fixInstall the library using pip: `pip install cleo` -
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.fixEnsure 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")`. -
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.fixVerify 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. -
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.fixDefine 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.
Warnings
- 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.
- 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.
- breaking The exception hierarchy changed in versions 1.0.0 / 2.0.0. Specifically, `CleoException` was renamed or replaced by `CleoError`.
- 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.
- 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.
Install
-
pip install cleo
Imports
- Application
from cleo import Application
from cleo.application import Application
- Command
from cleo import Command
from cleo.commands.command import Command
- argument
from cleo import InputArgument
from cleo.helpers import argument
- option
from cleo import InputOption
from cleo.helpers import option
- CleoError
from cleo.exceptions import CleoException
from cleo.exceptions import CleoError
Quickstart
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}")