Skip to content

Commit

Permalink
fix(CLI): Don't call rich_click.patch if rich_click is installed (#…
Browse files Browse the repository at this point in the history
…3570)

Remove rich_click patching
  • Loading branch information
provinzkraut authored Jun 15, 2024
1 parent 064c8fe commit 65f01ea
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 67 deletions.
31 changes: 12 additions & 19 deletions litestar/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,19 @@

from importlib.util import find_spec

# Ensure `rich_click` patching occurs before we do any imports from `click`.
if find_spec("rich_click") is not None: # pragma: no cover
import rich_click as click

try:
from rich_click.patch import patch as rich_click_patch
except ImportError:
from rich_click.cli import patch as rich_click_patch

rich_click_patch()
click.rich_click.USE_RICH_MARKUP = True
click.rich_click.USE_MARKDOWN = False
click.rich_click.SHOW_ARGUMENTS = True
click.rich_click.GROUP_ARGUMENTS_OPTIONS = True
click.rich_click.STYLE_ERRORS_SUGGESTION = "magenta italic"
click.rich_click.ERRORS_SUGGESTION = ""
click.rich_click.ERRORS_EPILOGUE = ""
click.rich_click.MAX_WIDTH = 80
click.rich_click.SHOW_METAVARS_COLUMN = True
click.rich_click.APPEND_METAVARS_HELP = True
import rich_click

rich_click.rich_click.USE_RICH_MARKUP = True
rich_click.rich_click.USE_MARKDOWN = False
rich_click.rich_click.SHOW_ARGUMENTS = True
rich_click.rich_click.GROUP_ARGUMENTS_OPTIONS = True
rich_click.rich_click.STYLE_ERRORS_SUGGESTION = "magenta italic"
rich_click.rich_click.ERRORS_SUGGESTION = ""
rich_click.rich_click.ERRORS_EPILOGUE = ""
rich_click.rich_click.MAX_WIDTH = 80
rich_click.rich_click.SHOW_METAVARS_COLUMN = True
rich_click.rich_click.APPEND_METAVARS_HELP = True


from .main import litestar_group
Expand Down
14 changes: 10 additions & 4 deletions litestar/cli/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
from pathlib import Path
from typing import TYPE_CHECKING, Any, Callable, Generator, Iterable, Sequence, TypeVar, cast

from click import ClickException, Command, Context, Group, pass_context
try:
from rich_click import RichCommand as Command
from rich_click import RichGroup as Group
except ImportError:
from click import Command, Group # type: ignore[assignment]

from click import ClickException, Context, pass_context
from rich import get_console
from rich.table import Table
from typing_extensions import ParamSpec, get_type_hints
Expand Down Expand Up @@ -128,7 +134,7 @@ class LoadedApp:
is_factory: bool


class LitestarGroup(Group):
class LitestarGroup(Group): # pyright: ignore
""":class:`click.Group` subclass that automatically injects ``app`` and ``env` kwargs into commands that request it.
Use this as the ``cls`` for :class:`click.Group` if you're extending the internal CLI with a group. For ``command``s
Expand All @@ -145,7 +151,7 @@ def __init__(
self.group_class = LitestarGroup
super().__init__(name=name, commands=commands, **attrs)

def add_command(self, cmd: Command, name: str | None = None) -> None:
def add_command(self, cmd: Command, name: str | None = None) -> None: # type: ignore[override]
"""Add command.
If necessary, inject ``app`` and ``env`` kwargs
Expand Down Expand Up @@ -207,7 +213,7 @@ def _prepare(self, ctx: Context) -> None:

self._prepare_done = True

def make_context(
def make_context( # type: ignore[override]
self,
info_name: str | None,
args: list[str],
Expand Down
54 changes: 30 additions & 24 deletions litestar/cli/commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
from contextlib import AbstractContextManager, ExitStack, contextmanager
from typing import TYPE_CHECKING, Any, Iterator

import click
from click import Context, command, option
try:
import rich_click as click
except ImportError:
import click # type: ignore[no-redef]
from rich.tree import Tree

from litestar.app import DEFAULT_OPENAPI_CONFIG
Expand Down Expand Up @@ -113,50 +115,52 @@ class CommaSplittedPath(click.Path):
envvar_list_splitter = ","


@command(name="version")
@option("-s", "--short", help="Exclude release level and serial information", is_flag=True, default=False)
@click.command(name="version")
@click.option("-s", "--short", help="Exclude release level and serial information", is_flag=True, default=False)
def version_command(short: bool) -> None:
"""Show the currently installed Litestar version."""
from litestar import __version__

click.echo(__version__.formatted(short=short))


@command(name="info")
@click.command(name="info")
def info_command(app: Litestar) -> None:
"""Show information about the detected Litestar app."""

show_app_info(app)


@command(name="run")
@option("-r", "--reload", help="Reload server on changes", default=False, is_flag=True, envvar="LITESTAR_RELOAD")
@option(
@click.command(name="run")
@click.option("-r", "--reload", help="Reload server on changes", default=False, is_flag=True, envvar="LITESTAR_RELOAD")
@click.option(
"-R",
"--reload-dir",
help="Directories to watch for file changes",
type=CommaSplittedPath(),
multiple=True,
envvar="LITESTAR_RELOAD_DIRS",
)
@option(
@click.option(
"-I",
"--reload-include",
help="Glob patterns for files to include when watching for file changes",
type=CommaSplittedPath(),
multiple=True,
envvar="LITESTAR_RELOAD_INCLUDES",
)
@option(
@click.option(
"-E",
"--reload-exclude",
help="Glob patterns for files to exclude when watching for file changes",
type=CommaSplittedPath(),
multiple=True,
envvar="LITESTAR_RELOAD_EXCLUDES",
)
@option("-p", "--port", help="Serve under this port", type=int, default=8000, show_default=True, envvar="LITESTAR_PORT")
@option(
@click.option(
"-p", "--port", help="Serve under this port", type=int, default=8000, show_default=True, envvar="LITESTAR_PORT"
)
@click.option(
"-W",
"--wc",
"--web-concurrency",
Expand All @@ -166,8 +170,10 @@ def info_command(app: Litestar) -> None:
default=1,
envvar=["LITESTAR_WEB_CONCURRENCY", "WEB_CONCURRENCY"],
)
@option("-H", "--host", help="Server under this host", default="127.0.0.1", show_default=True, envvar="LITESTAR_HOST")
@option(
@click.option(
"-H", "--host", help="Server under this host", default="127.0.0.1", show_default=True, envvar="LITESTAR_HOST"
)
@click.option(
"-F",
"--fd",
"--file-descriptor",
Expand All @@ -177,7 +183,7 @@ def info_command(app: Litestar) -> None:
show_default=True,
envvar="LITESTAR_FILE_DESCRIPTOR",
)
@option(
@click.option(
"-U",
"--uds",
"--unix-domain-socket",
Expand All @@ -186,11 +192,11 @@ def info_command(app: Litestar) -> None:
show_default=True,
envvar="LITESTAR_UNIX_DOMAIN_SOCKET",
)
@option("-d", "--debug", help="Run app in debug mode", is_flag=True, envvar="LITESTAR_DEBUG")
@option("-P", "--pdb", "--use-pdb", help="Drop into PDB on an exception", is_flag=True, envvar="LITESTAR_PDB")
@option("--ssl-certfile", help="Location of the SSL cert file", default=None, envvar="LITESTAR_SSL_CERT_PATH")
@option("--ssl-keyfile", help="Location of the SSL key file", default=None, envvar="LITESTAR_SSL_KEY_PATH")
@option(
@click.option("-d", "--debug", help="Run app in debug mode", is_flag=True, envvar="LITESTAR_DEBUG")
@click.option("-P", "--pdb", "--use-pdb", help="Drop into PDB on an exception", is_flag=True, envvar="LITESTAR_PDB")
@click.option("--ssl-certfile", help="Location of the SSL cert file", default=None, envvar="LITESTAR_SSL_CERT_PATH")
@click.option("--ssl-keyfile", help="Location of the SSL key file", default=None, envvar="LITESTAR_SSL_KEY_PATH")
@click.option(
"--create-self-signed-cert",
help="If certificate and key are not found at specified locations, create a self-signed certificate and a key",
is_flag=True,
Expand All @@ -211,7 +217,7 @@ def run_command(
ssl_certfile: str | None,
ssl_keyfile: str | None,
create_self_signed_cert: bool,
ctx: Context,
ctx: click.Context,
) -> None:
"""Run a Litestar app; requires ``uvicorn``.
Expand Down Expand Up @@ -299,9 +305,9 @@ def run_command(
)


@command(name="routes")
@option("--schema", help="Include schema routes", is_flag=True, default=False)
@option("--exclude", help="routes to exclude via regex", type=str, is_flag=False, multiple=True)
@click.command(name="routes")
@click.option("--schema", help="Include schema routes", is_flag=True, default=False)
@click.option("--exclude", help="routes to exclude via regex", type=str, is_flag=False, multiple=True)
def routes_command(app: Litestar, exclude: tuple[str, ...], schema: bool) -> None: # pragma: no cover
"""Display information about the application's routes."""

Expand Down
15 changes: 10 additions & 5 deletions litestar/cli/commands/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

import msgspec
from click import Path as ClickPath
from click import group, option

try:
import rich_click as click
except ImportError:
import click # type: ignore[no-redef]

from yaml import dump as dump_yaml

from litestar import Litestar
Expand All @@ -15,7 +20,7 @@
__all__ = ("generate_openapi_schema", "generate_typescript_specs", "schema_group")


@group(cls=LitestarGroup, name="schema")
@click.group(cls=LitestarGroup, name="schema")
def schema_group() -> None:
"""Manage server-side OpenAPI schemas."""

Expand All @@ -42,7 +47,7 @@ def _generate_openapi_schema(app: Litestar, output: Path) -> None:


@schema_group.command("openapi") # type: ignore[misc]
@option(
@click.option(
"--output",
help="output file path",
type=ClickPath(dir_okay=False, path_type=Path),
Expand All @@ -55,14 +60,14 @@ def generate_openapi_schema(app: Litestar, output: Path) -> None:


@schema_group.command("typescript") # type: ignore[misc]
@option(
@click.option(
"--output",
help="output file path",
type=ClickPath(dir_okay=False, path_type=Path),
default=Path("api-specs.ts"),
show_default=True,
)
@option("--namespace", help="namespace to use for the typescript specs", type=str, default="API")
@click.option("--namespace", help="namespace to use for the typescript specs", type=str, default="API")
def generate_typescript_specs(app: Litestar, output: Path, namespace: str) -> None:
"""Generate TypeScript specs from the OpenAPI schema."""
if JSBEAUTIFIER_INSTALLED: # pragma: no cover
Expand Down
9 changes: 6 additions & 3 deletions litestar/cli/commands/sessions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from click import argument, group
try:
import rich_click as click
except ImportError:
import click # type: ignore[no-redef]
from rich.prompt import Confirm

from litestar import Litestar
Expand All @@ -24,13 +27,13 @@ def get_session_backend(app: Litestar) -> ServerSideSessionBackend:
raise LitestarCLIException("Session middleware not installed")


@group(cls=LitestarGroup, name="sessions")
@click.group(cls=LitestarGroup, name="sessions")
def sessions_group() -> None:
"""Manage server-side sessions."""


@sessions_group.command("delete") # type: ignore[misc]
@argument("session-id")
@click.argument("session-id")
def delete_session_command(session_id: str, app: Litestar) -> None:
"""Delete a specific session."""
import anyio
Expand Down
28 changes: 16 additions & 12 deletions litestar/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

from pathlib import Path

from click import Context, group, option, pass_context
try:
import rich_click as click
except ImportError:
import click # type: ignore[no-redef]

from click import Path as ClickPath

from ._utils import LitestarEnv, LitestarExtensionGroup
Expand All @@ -11,27 +15,27 @@
__all__ = ("litestar_group",)


@group(cls=LitestarExtensionGroup, context_settings={"help_option_names": ["-h", "--help"]})
@option("--app", "app_path", help="Module path to a Litestar application")
@option(
@click.group(cls=LitestarExtensionGroup, context_settings={"help_option_names": ["-h", "--help"]})
@click.option("--app", "app_path", help="Module path to a Litestar application")
@click.option(
"--app-dir",
help="Look for APP in the specified directory, by adding this to the PYTHONPATH. Defaults to the current working directory.",
default=None,
type=ClickPath(dir_okay=True, file_okay=False, path_type=Path),
show_default=False,
)
@pass_context
def litestar_group(ctx: Context, app_path: str | None, app_dir: Path | None = None) -> None:
@click.pass_context
def litestar_group(ctx: click.Context, app_path: str | None, app_dir: Path | None = None) -> None:
"""Litestar CLI."""
if ctx.obj is None: # env has not been loaded yet, so we can lazy load it
ctx.obj = lambda: LitestarEnv.from_env(app_path, app_dir=app_dir)


# add sub commands here

litestar_group.add_command(core.info_command)
litestar_group.add_command(core.run_command)
litestar_group.add_command(core.routes_command)
litestar_group.add_command(core.version_command)
litestar_group.add_command(sessions.sessions_group)
litestar_group.add_command(schema.schema_group)
litestar_group.add_command(core.info_command) # pyright: ignore
litestar_group.add_command(core.run_command) # pyright: ignore
litestar_group.add_command(core.routes_command) # pyright: ignore
litestar_group.add_command(core.version_command) # pyright: ignore
litestar_group.add_command(sessions.sessions_group) # pyright: ignore
litestar_group.add_command(schema.schema_group) # pyright: ignore

0 comments on commit 65f01ea

Please sign in to comment.