CLI
===

.. |uvicorn| replace:: uvicorn
.. _uvicorn: https://www.uvicorn.org/

Litestar provides a convenient command line interface (CLI) for running and managing Litestar applications. The CLI is
powered by `click <https://click.palletsprojects.com/>`_, `rich <https://rich.readthedocs.io>`_,
and `rich-click <https://github.com/ewels/rich-click>`_.

Enabling all CLI features
-------------------------

The CLI and its hard dependencies are included by default. However, if you want to run your application
(using ``litestar run`` ) or beautify the Typescript generated by the ``litestar schema typescript``
command, you will need |uvicorn|_ and `jsbeautifier <https://pypi.org/project/jsbeautifier/>`_.
They can be installed independently, but we recommend installing the ``standard`` extra which conveniently bundles
commonly used optional dependencies.

.. code-block:: shell
    :caption: Install the standard group

    pip install 'litestar[standard]'

Once you have installed ``standard``, you will have access to the ``litestar run`` command.

Autodiscovery
-------------

Litestar offers autodiscovery of applications and application factories placed within the canonical modules named
either ``app`` or ``application``. These modules can be individual files or directories. Within these modules or their
submodules, the CLI will detect any instances of :class:`Litestar <.app.Litestar>`, callables named ``create_app``, or
callables annotated to return a :class:`Litestar <.app.Litestar>` instance.

The autodiscovery follows these lookup locations in order:

1. ``app.py``
2. ``app/__init__.py``
3. Submodules of ``app``
4. ``application.py``
5. ``application/__init__.py``
6. Submodules of ``application``

Within these locations, Litestar CLI looks for:

1. An :term:`object` named ``app`` that is an instance of :class:`~.app.Litestar`
2. An object named ``application`` that is an instance of :class:`~.app.Litestar`
3. Any object that is an instance of :class:`~.app.Litestar`
4. A :term:`callable` named ``create_app``
5. A callable annotated to return an instance of :class:`~.app.Litestar`

Specifying an application explicitly
------------------------------------

The application to be used can be specified explicitly via either the ``--app`` argument
or the ``LITESTAR_APP`` environment variable. The format for both of them is
``<module name>.<submodule>:<app instance or factory>``.

When both ``--app`` and ``LITESTAR_APP`` are set, the CLI option takes precedence over
the environment variable.


.. code-block:: bash
    :caption: Using 'litestar run' and specifying an application factory via --app

    litestar --app=my_application.app:create_my_app run


.. code-block:: bash
    :caption: Using 'litestar run' and specifying an application factory via LITESTAR_APP

    LITESTAR_APP=my_application.app:create_my_app litestar run



Extending the CLI
-----------------

Litestar's CLI is built with `click <https://click.palletsprojects.com/>`_ and can be extended by making use of
`entry points <https://packaging.python.org/en/latest/specifications/entry-points/>`_,
or by creating a plugin that conforms to the :class:`~.plugins.CLIPluginProtocol`.

Using entry points
^^^^^^^^^^^^^^^^^^

Entry points for the CLI can be added under the ``litestar.commands`` group. These
entries should point to a :class:`click.Command` or :class:`click.Group`:

.. tab-set::

    .. tab-item:: setup.py

        .. code-block:: python
            :caption: Using `setuptools <https://setuptools.pypa.io/en/latest/>`_

            from setuptools import setup

            setup(
               name="my-litestar-plugin",
               ...,
               entry_points={
                   "litestar.commands": ["my_command=my_litestar_plugin.cli:main"],
               },
            )

    .. tab-item:: pdm

        .. code-block:: toml
            :caption: Using `PDM <https://pdm.fming.dev/>`_

            [project.scripts]
            my_command = "my_litestar_plugin.cli:main"

            # Or, as an entrypoint:

            [project.entry-points."litestar.commands"]
            my_command = "my_litestar_plugin.cli:main"

    .. tab-item:: poetry

        .. code-block:: toml
            :caption: Using `poetry <https://python-poetry.org/>`_

            [project.entry-points."litestar.commands"]
            my_command = "my_litestar_plugin.cli:main"

    .. tab-item:: uv

        .. code-block:: toml
            :caption: Using `uv <https://docs.astral.sh/uv/>`_

            [project.entry-points."litestar.commands"]
            my_command = "my_litestar_plugin.cli:main"

Using a plugin
^^^^^^^^^^^^^^

A plugin extending the CLI can be created using the :class:`~.plugins.CLIPluginProtocol`.
Its :meth:`~.plugins.CLIPluginProtocol.on_cli_init` will be called during the initialization of the CLI,
and receive the root :class:`click.Group` as its first argument, which can then be used to add or override commands:

.. code-block:: python
    :caption: Creating a CLI plugin

    from litestar import Litestar
    from litestar.plugins import CLIPluginProtocol
    from click import Group


    class CLIPlugin(CLIPluginProtocol):
        def on_cli_init(self, cli: Group) -> None:
            @cli.command()
            def is_debug_mode(app: Litestar):
                print(app.debug)


    app = Litestar(plugins=[CLIPlugin()])

Accessing the app instance
^^^^^^^^^^^^^^^^^^^^^^^^^^

When extending the Litestar CLI, you will most likely need access to the loaded ``Litestar`` instance.
You can achieve this by adding the special ``app`` parameter to your CLI functions. This will cause the
``Litestar`` instance to be injected into the function whenever it is called from a click-context.

.. code-block:: python
    :caption: Accessing the app instance programmatically

    import click
    from litestar import Litestar


    @click.command()
    def my_command(app: Litestar) -> None: ...

Using the `server_lifespan` hook
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Server lifespan hooks provide a way to run code before and after the *server* starts and stops. In contrast to the regular `lifespan` hooks, they only run once, even when a server starts multiple workers, whereas `lifespan` hooks would run for each individual worker.

This makes them suitable for tasks that should happen exactly once, like initializing a database.

.. code-block:: python
    :caption: Using the `server_lifespan` hook

    from contextlib import contextmanager
    from typing import Generator

    from litestar import Litestar
    from litestar.config.app import AppConfig
    from litestar.plugins.base import CLIPlugin


    class StartupPrintPlugin(CLIPlugin):

        @contextmanager
        def server_lifespan(self, app: Litestar) -> Generator[None, None, None]:
            print("i_run_before_startup_plugin")  # noqa: T201
            try:
                yield
            finally:
                print("i_run_after_shutdown_plugin")  # noqa: T201

    def create_app() -> Litestar:
        return Litestar(route_handlers=[], plugins=[StartupPrintPlugin()])


CLI Reference
-------------

The most up-to-date reference for the Litestar CLI can be found by running:

.. code-block:: shell
    :caption: Display the CLI help

    litestar --help

You can also visit the :doc:`Litestar CLI Click API Reference </reference/cli>` for that same
information.
