Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions litestar/cli/commands/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@
except ImportError:
import click # type: ignore[no-redef]

from yaml import dump as dump_yaml

from litestar import Litestar
from litestar._openapi.typescript_converter.converter import (
convert_openapi_to_typescript,
)
from litestar.cli._utils import JSBEAUTIFIER_INSTALLED, LitestarCLIException, LitestarGroup
from litestar.exceptions import MissingDependencyException
from litestar.serialization import encode_json, get_serializer

__all__ = ("generate_openapi_schema", "generate_typescript_specs", "schema_group")
Expand All @@ -31,6 +30,13 @@ def _generate_openapi_schema(app: Litestar, output: Path) -> None:
"""Generate an OpenAPI Schema."""
serializer = get_serializer(app.type_encoders)
if output.suffix in (".yml", ".yaml"):
try:
# Import lazily: pyyaml is an optional dependency, and importing it at
# module level made every CLI invocation fail when it is not installed.
# https://github.com/litestar-org/litestar/issues/4449
from yaml import dump as dump_yaml
except ImportError as e:
raise MissingDependencyException("pyyaml", extra="yaml") from e
content = dump_yaml(
msgspec.to_builtins(app.openapi_schema.to_schema(), enc_hook=serializer),
default_flow_style=False,
Expand Down
19 changes: 19 additions & 0 deletions tests/unit/test_cli/test_schema_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,22 @@ def test_openapi_typescript_command_without_jsbeautifier(
result = runner.invoke(cli_command, command)
assert result.exit_code == 0
assert mock_path_write_text.called


def test_cli_importable_without_pyyaml(monkeypatch: pytest.MonkeyPatch) -> None:
"""The CLI must not require pyyaml at import time.

An eager module-level yaml import made every CLI invocation fail with
``ModuleNotFoundError: No module named 'yaml'`` when litestar was installed
without the ``yaml`` extra. https://github.com/litestar-org/litestar/issues/4449
"""
import importlib
import sys

# Simulate pyyaml not being installed: a None entry makes `import yaml`
# raise ImportError.
monkeypatch.setitem(sys.modules, "yaml", None)
monkeypatch.delitem(sys.modules, "litestar.cli.commands.schema", raising=False)

module = importlib.import_module("litestar.cli.commands.schema")
assert module.schema_group is not None
Loading