Skip to content

Commit f48acb3

Browse files
committed
Import yaml lazily in the schema CLI command
The module-level 'from yaml import dump' in litestar/cli/commands/schema.py is imported eagerly via litestar.cli.main, so every CLI invocation failed with ModuleNotFoundError when litestar was installed without the yaml extra (pyyaml only comes in transitively with the standard extras via uvicorn). Import yaml inside the YAML export branch and raise MissingDependencyException('pyyaml', extra='yaml') if it is missing, matching how other optional dependencies are handled. Fixes #4449
1 parent 1aa4aa5 commit f48acb3

2 files changed

Lines changed: 27 additions & 2 deletions

File tree

litestar/cli/commands/schema.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@
1010
except ImportError:
1111
import click # type: ignore[no-redef]
1212

13-
from yaml import dump as dump_yaml
14-
1513
from litestar import Litestar
1614
from litestar._openapi.typescript_converter.converter import (
1715
convert_openapi_to_typescript,
1816
)
1917
from litestar.cli._utils import JSBEAUTIFIER_INSTALLED, LitestarCLIException, LitestarGroup
18+
from litestar.exceptions import MissingDependencyException
2019
from litestar.serialization import encode_json, get_serializer
2120

2221
__all__ = ("generate_openapi_schema", "generate_typescript_specs", "schema_group")
@@ -31,6 +30,13 @@ def _generate_openapi_schema(app: Litestar, output: Path) -> None:
3130
"""Generate an OpenAPI Schema."""
3231
serializer = get_serializer(app.type_encoders)
3332
if output.suffix in (".yml", ".yaml"):
33+
try:
34+
# Import lazily: pyyaml is an optional dependency, and importing it at
35+
# module level made every CLI invocation fail when it is not installed.
36+
# https://github.com/litestar-org/litestar/issues/4449
37+
from yaml import dump as dump_yaml
38+
except ImportError as e:
39+
raise MissingDependencyException("pyyaml", extra="yaml") from e
3440
content = dump_yaml(
3541
msgspec.to_builtins(app.openapi_schema.to_schema(), enc_hook=serializer),
3642
default_flow_style=False,

tests/unit/test_cli/test_schema_commands.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,22 @@ def test_openapi_typescript_command_without_jsbeautifier(
103103
result = runner.invoke(cli_command, command)
104104
assert result.exit_code == 0
105105
assert mock_path_write_text.called
106+
107+
108+
def test_cli_importable_without_pyyaml(monkeypatch: pytest.MonkeyPatch) -> None:
109+
"""The CLI must not require pyyaml at import time.
110+
111+
An eager module-level yaml import made every CLI invocation fail with
112+
``ModuleNotFoundError: No module named 'yaml'`` when litestar was installed
113+
without the ``yaml`` extra. https://github.com/litestar-org/litestar/issues/4449
114+
"""
115+
import importlib
116+
import sys
117+
118+
# Simulate pyyaml not being installed: a None entry makes `import yaml`
119+
# raise ImportError.
120+
monkeypatch.setitem(sys.modules, "yaml", None)
121+
monkeypatch.delitem(sys.modules, "litestar.cli.commands.schema", raising=False)
122+
123+
module = importlib.import_module("litestar.cli.commands.schema")
124+
assert module.schema_group is not None

0 commit comments

Comments
 (0)