Skip to content

Commit

Permalink
fix: extend validation for --factory param
Browse files Browse the repository at this point in the history
  • Loading branch information
sehat1137 committed Dec 4, 2024
1 parent 48b7304 commit b6fee09
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 17 deletions.
2 changes: 1 addition & 1 deletion faststream/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def run(
workers=workers,
).run()
else:
args[1]["workers"] = workers
args[1]["workers"] = workers # type: ignore[assignment]
_run(*args)

else:
Expand Down
15 changes: 8 additions & 7 deletions faststream/cli/utils/imports.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import importlib
from importlib.util import module_from_spec, spec_from_file_location
from pathlib import Path
from typing import TYPE_CHECKING, Tuple
from typing import Tuple

import typer

from faststream._internal.application import Application
from faststream.exceptions import SetupError

if TYPE_CHECKING:
from faststream.app import FastStream


def try_import_app(module: Path, app: str) -> "FastStream":
def try_import_app(module: Path, app: str) -> "Application":
"""Tries to import a FastStream app from a module."""
try:
app_object = import_object(module, app)
Expand Down Expand Up @@ -71,7 +69,7 @@ def import_from_string(
import_str: str,
*,
is_factory: bool = False,
) -> Tuple[Path, "FastStream"]:
) -> Tuple[Path, "Application"]:
module_path, instance = _import_obj_or_factory(import_str)

if is_factory:
Expand All @@ -80,10 +78,13 @@ def import_from_string(
else:
raise typer.BadParameter(f'"{instance}" is not a factory')

if callable(instance) and not is_factory and not isinstance(instance, Application):
raise typer.BadParameter("Please, use --factory option for callable object")

return module_path, instance


def _import_obj_or_factory(import_str: str) -> Tuple[Path, "FastStream"]:
def _import_obj_or_factory(import_str: str) -> Tuple[Path, "Application"]:
"""Import FastStream application from module specified by a string."""
if not isinstance(import_str, str):
raise typer.BadParameter("Given value is not of type string")
Expand Down
7 changes: 7 additions & 0 deletions tests/cli/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest

from faststream import FastStream
from faststream.asgi import AsgiFastStream


@pytest.fixture
Expand All @@ -21,6 +22,12 @@ def app_without_broker():
return FastStream()


@pytest.fixture
def asgi_app_without_broker():
return AsgiFastStream()


@pytest.fixture
def app(broker):
return FastStream(broker)

42 changes: 33 additions & 9 deletions tests/cli/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
import pytest
from typer.testing import CliRunner

from faststream._internal.application import Application
from faststream.app import FastStream
from faststream.asgi import AsgiFastStream
from faststream.cli.main import cli as faststream_app


def test_run_as_asgi(runner: CliRunner):
app = AsgiFastStream()
@pytest.mark.parametrize("app", [FastStream(), AsgiFastStream()])
def test_run(runner: CliRunner, app: Application):
app.run = AsyncMock()

with patch(
Expand All @@ -33,12 +35,11 @@ def test_run_as_asgi(runner: CliRunner):


@pytest.mark.parametrize("workers", [1, 2, 5])
def test_run_as_asgi_with_workers(runner: CliRunner, workers: int):
app = AsgiFastStream()
app.run = AsyncMock()
def test_run_as_asgi_with_workers(runner: CliRunner, workers: int, asgi_app_without_broker: AsgiFastStream):
asgi_app_without_broker.run = AsyncMock()

with patch(
"faststream.cli.utils.imports._import_obj_or_factory", return_value=(None, app)
"faststream.cli.utils.imports._import_obj_or_factory", return_value=(None, asgi_app_without_broker)
):
result = runner.invoke(
faststream_app,
Expand All @@ -55,14 +56,14 @@ def test_run_as_asgi_with_workers(runner: CliRunner, workers: int):
)
extra = {"workers": workers} if workers > 1 else {}

app.run.assert_awaited_once_with(
asgi_app_without_broker.run.assert_awaited_once_with(
logging.INFO, {"host": "0.0.0.0", "port": "8000", **extra}
)
assert result.exit_code == 0


def test_run_as_asgi_callable(runner: CliRunner):
app = AsgiFastStream()
@pytest.mark.parametrize("app", [FastStream(), AsgiFastStream()])
def test_run_as_factory(runner: CliRunner, app: Application):
app.run = AsyncMock()

app_factory = Mock(return_value=app)
Expand All @@ -88,3 +89,26 @@ def test_run_as_asgi_callable(runner: CliRunner):
logging.INFO, {"host": "0.0.0.0", "port": "8000"}
)
assert result.exit_code == 0

@pytest.mark.parametrize("app", [FastStream(), AsgiFastStream()])
def test_run_app_like_factory_but_its_fake(runner: CliRunner, app: Application):
app.run = AsyncMock()

with patch(
"faststream.cli.utils.imports._import_obj_or_factory",
return_value=(None, app),
):
result = runner.invoke(
faststream_app,
[
"run",
"faststream:app",
"--host",
"0.0.0.0",
"--port",
"8000",
"--factory",
],
)
app.run.assert_not_called()
assert result.exit_code != 0

0 comments on commit b6fee09

Please sign in to comment.