{"id":619,"library":"cleo","title":"Cleo","description":"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.","status":"active","version":"2.1.0","language":"python","source_language":"en","source_url":"https://github.com/python-poetry/cleo","tags":["CLI","command-line","interface"],"install":[{"cmd":"pip install cleo","lang":"bash","label":"Install latest version"}],"dependencies":[],"imports":[{"note":"While 'from cleo import Application' might work in some contexts, the explicit path 'from cleo.application import Application' is recommended for clarity and directness, as shown in official examples.","wrong":"from cleo import Application","symbol":"Application","correct":"from cleo.application import Application"},{"note":"Similar to Application, importing Command from its explicit module path 'cleo.commands.command' is the precise and recommended approach, as seen in the library's internal structure and modern examples.","wrong":"from cleo import Command","symbol":"Command","correct":"from cleo.commands.command import Command"},{"note":"Since Cleo v2.0.0 (and effectively v1.0.0), the recommended way to define command arguments is using the `argument` helper from `cleo.helpers`, replacing the older `InputArgument` class and docstring-based definitions.","wrong":"from cleo import InputArgument","symbol":"argument","correct":"from cleo.helpers import argument"},{"note":"Corresponding to `argument`, the `option` helper from `cleo.helpers` is now the standard for defining command options, replacing `InputOption` and docstring-based methods in versions v2.0.0+.","wrong":"from cleo import InputOption","symbol":"option","correct":"from cleo.helpers import option"},{"note":"The exception hierarchy was refactored in Cleo v1.0.0 (effectively v2.0.0), with `CleoException` being replaced by `CleoError`.","wrong":"from cleo.exceptions import CleoException","symbol":"CleoError","correct":"from cleo.exceptions import CleoError"}],"quickstart":{"code":"import os\nfrom cleo.application import Application\nfrom cleo.commands.command import Command\nfrom cleo.helpers import argument, option\n\n\nclass GreetCommand(Command):\n    name = \"greet\"\n    description = \"Greets someone\"\n\n    arguments = [\n        argument(\"name\", description=\"Who do you want to greet?\", optional=True)\n    ]\n    options = [\n        option(\"yell\", \"y\", description=\"If set, the task will yell in uppercase letters\", flag=True)\n    ]\n\n    def handle(self):\n        name = self.argument(\"name\")\n        if name:\n            text = f\"Hello {name}\"\n        else:\n            text = \"Hello\"\n\n        if self.option(\"yell\"):\n            text = text.upper()\n\n        self.line(text)\n\n\napplication = Application(\"My CLI App\", \"1.0.0\")\napplication.add(GreetCommand())\n\nif __name__ == \"__main__\":\n    # Example of how to run, in a real app this would be called via command line\n    # e.g., `python your_script.py greet John --yell`\n    # For quickstart, we'll simulate it for programmatic execution and testing\n    try:\n        # Simulate a command-line call for demonstration\n        # In a real scenario, this would be invoked via sys.argv\n        from cleo.io.buffered_io import BufferedIO\n        from cleo.io.inputs.string_input import StringInput\n        from cleo.io.outputs.buffered_output import BufferedOutput\n        \n        # Example: running 'greet John --yell'\n        input_stream = StringInput('greet John --yell')\n        output_stream = BufferedOutput()\n        error_stream = BufferedOutput()\n        \n        io = BufferedIO(input_stream, output_stream, error_stream)\n        application.run(io)\n        \n        print(\"\\n--- Output ---\")\n        print(output_stream.fetch())\n        print(\"--- Error ---\")\n        print(error_stream.fetch())\n        print(\"--------------\")\n\n        # Example: running 'greet'\n        input_stream_no_args = StringInput('greet')\n        output_stream_no_args = BufferedOutput()\n        error_stream_no_args = BufferedOutput()\n\n        io_no_args = BufferedIO(input_stream_no_args, output_stream_no_args, error_stream_no_args)\n        application.run(io_no_args)\n\n        print(\"\\n--- Output (No Args) ---\")\n        print(output_stream_no_args.fetch())\n        print(\"--- Error (No Args) ---\")\n        print(error_stream_no_args.fetch())\n        print(\"------------------------\")\n\n    except Exception as e:\n        print(f\"An error occurred: {e}\")\n","lang":"python","description":"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."},"warnings":[{"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.","message":"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.","severity":"breaking","affected_versions":">=1.0.0"},{"fix":"Replace usages of `cleo.terminal.Terminal` with `shutil.get_terminal_size()` for terminal dimension retrieval.","message":"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.","severity":"breaking","affected_versions":">=1.0.0"},{"fix":"Update exception handling blocks to catch `CleoError` instead of `CleoException` and adjust for any other related exception class changes.","message":"The exception hierarchy changed in versions 1.0.0 / 2.0.0. Specifically, `CleoException` was renamed or replaced by `CleoError`.","severity":"breaking","affected_versions":">=1.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.","message":"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.","severity":"breaking","affected_versions":">=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'`).","message":"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.","severity":"gotcha","affected_versions":"1.0.0"}],"env_vars":null,"last_verified":"2026-05-12T16:50:13.295Z","next_check":"2026-06-26T00:00:00.000Z","problems":[{"fix":"Install the library using pip: `pip install cleo`","cause":"The 'cleo' library is not installed in the current Python environment or is not accessible to the Python interpreter being used.","error":"ModuleNotFoundError: No module named 'cleo'"},{"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\")`.","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.","error":"Not enough arguments (missing: \"<argument_name>\")"},{"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.","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.","error":"The command \"<command_name>\" does not exist."},{"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.","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.","error":"No such option: --<option_name>"}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":80,"quickstart_tag":"verified","pypi_latest":"2.1.0","cli_name":"cleo","install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.16,"mem_mb":4.1,"disk_size":"34.7M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":4.1,"disk_size":"34.7M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.9,"import_time_s":0.14,"mem_mb":4.1,"disk_size":"31M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":4.1,"disk_size":"31M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.2,"mem_mb":4.6,"disk_size":"36.7M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.22,"mem_mb":4.6,"disk_size":"36.7M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.5,"import_time_s":0.18,"mem_mb":4.6,"disk_size":"33M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.16,"mem_mb":4.6,"disk_size":"33M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.17,"mem_mb":4.5,"disk_size":"28.5M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.18,"mem_mb":4.5,"disk_size":"28.5M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.2,"import_time_s":0.17,"mem_mb":4.5,"disk_size":"25M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.17,"mem_mb":4.5,"disk_size":"25M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.15,"mem_mb":4.6,"disk_size":"28.3M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.17,"mem_mb":4.6,"disk_size":"28.2M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.3,"import_time_s":0.17,"mem_mb":4.6,"disk_size":"25M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.16,"mem_mb":4.6,"disk_size":"25M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.1,"mem_mb":4,"disk_size":"34.4M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.11,"mem_mb":4,"disk_size":"34.4M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":3.4,"import_time_s":0.11,"mem_mb":4,"disk_size":"30M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.1,"mem_mb":4,"disk_size":"30M"}]},"quickstart_checks":{"last_tested":"2026-04-24","tag":"verified","tag_description":"quickstart runs on critical runtimes, recently tested","results":[{"runtime":"python:3.10-alpine","exit_code":0},{"runtime":"python:3.10-slim","exit_code":0},{"runtime":"python:3.11-alpine","exit_code":0},{"runtime":"python:3.11-slim","exit_code":0},{"runtime":"python:3.12-alpine","exit_code":0},{"runtime":"python:3.12-slim","exit_code":0},{"runtime":"python:3.13-alpine","exit_code":0},{"runtime":"python:3.13-slim","exit_code":0},{"runtime":"python:3.9-alpine","exit_code":0},{"runtime":"python:3.9-slim","exit_code":0}]}}