diff --git a/src/fastapi_cli/cli.py b/src/fastapi_cli/cli.py index b4660bde..d000bef5 100644 --- a/src/fastapi_cli/cli.py +++ b/src/fastapi_cli/cli.py @@ -104,6 +104,7 @@ def _run( host: str = "127.0.0.1", port: int = 8000, reload: bool = True, + reload_dirs: Union[list[Path], None] = None, workers: Union[int, None] = None, root_path: str = "", command: str, @@ -219,6 +220,11 @@ def _run( host=host, port=port, reload=reload, + reload_dirs=( + [str(directory.resolve()) for directory in reload_dirs] + if reload_dirs + else None + ), workers=workers, root_path=root_path, proxy_headers=proxy_headers, @@ -255,6 +261,12 @@ def dev( help="Enable auto-reload of the server when (code) files change. This is [bold]resource intensive[/bold], use it only during development." ), ] = True, + reload_dir: Annotated[ + Union[list[Path], None], + typer.Option( + help="Set reload directories explicitly, instead of using the current working directory." + ), + ] = None, root_path: Annotated[ str, typer.Option( @@ -318,6 +330,7 @@ def dev( host=host, port=port, reload=reload, + reload_dirs=reload_dir, root_path=root_path, app=app, entrypoint=entrypoint, diff --git a/tests/test_cli.py b/tests/test_cli.py index 0a0d7ab1..ddfb808b 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -27,6 +27,7 @@ def test_dev() -> None: "host": "127.0.0.1", "port": 8000, "reload": True, + "reload_dirs": None, "workers": None, "root_path": "", "proxy_headers": True, @@ -72,6 +73,7 @@ def test_dev_package() -> None: "host": "127.0.0.1", "port": 8000, "reload": True, + "reload_dirs": None, "workers": None, "root_path": "", "proxy_headers": True, @@ -121,6 +123,7 @@ def test_dev_args() -> None: "host": "192.168.0.2", "port": 8080, "reload": False, + "reload_dirs": None, "workers": None, "root_path": "/api", "proxy_headers": False, @@ -151,6 +154,7 @@ def test_dev_env_vars() -> None: "host": "127.0.0.1", "port": 8111, "reload": True, + "reload_dirs": None, "workers": None, "root_path": "", "proxy_headers": True, @@ -188,6 +192,7 @@ def test_dev_env_vars_and_args() -> None: "host": "127.0.0.1", "port": 8080, "reload": True, + "reload_dirs": None, "workers": None, "root_path": "", "proxy_headers": True, @@ -233,6 +238,7 @@ def test_run() -> None: "host": "0.0.0.0", "port": 8000, "reload": False, + "reload_dirs": None, "workers": None, "root_path": "", "proxy_headers": True, @@ -259,6 +265,7 @@ def test_run_trust_proxy() -> None: "host": "0.0.0.0", "port": 8000, "reload": False, + "reload_dirs": None, "workers": None, "root_path": "", "proxy_headers": True, @@ -305,6 +312,7 @@ def test_run_args() -> None: "host": "192.168.0.2", "port": 8080, "reload": False, + "reload_dirs": None, "workers": 2, "root_path": "/api", "proxy_headers": False, @@ -336,6 +344,7 @@ def test_run_env_vars() -> None: "host": "0.0.0.0", "port": 8111, "reload": False, + "reload_dirs": None, "workers": None, "root_path": "", "proxy_headers": True, @@ -369,6 +378,7 @@ def test_run_env_vars_and_args() -> None: "host": "0.0.0.0", "port": 8080, "reload": False, + "reload_dirs": None, "workers": None, "root_path": "", "proxy_headers": True, @@ -404,6 +414,7 @@ def test_dev_help() -> None: assert "The host to serve on." in result.output assert "The port to serve on." in result.output assert "Enable auto-reload of the server when (code) files change." in result.output + assert "Set reload directories explicitly" in result.output assert "The root path is used to tell your app" in result.output assert "The name of the variable that contains the FastAPI app" in result.output assert "Use multiple worker processes." not in result.output @@ -443,6 +454,30 @@ def test_version() -> None: assert "FastAPI CLI version:" in result.output +def test_dev_reload_dir() -> None: + with changing_dir(assets_path): + with patch.object(uvicorn, "run") as mock_run: + result = runner.invoke( + app, + [ + "dev", + "single_file_app.py", + "--reload-dir", + "src", + "--reload-dir", + "lib", + ], + ) + assert result.exit_code == 0, result.output + assert mock_run.called + assert mock_run.call_args + # Paths are resolved to absolute paths + reload_dirs = mock_run.call_args.kwargs["reload_dirs"] + assert len(reload_dirs) == 2 + assert reload_dirs[0] == str((assets_path / "src").resolve()) + assert reload_dirs[1] == str((assets_path / "lib").resolve()) + + def test_dev_with_import_string() -> None: with changing_dir(assets_path): with patch.object(uvicorn, "run") as mock_run: @@ -456,6 +491,7 @@ def test_dev_with_import_string() -> None: "host": "127.0.0.1", "port": 8000, "reload": True, + "reload_dirs": None, "workers": None, "root_path": "", "proxy_headers": True, @@ -477,6 +513,7 @@ def test_run_with_import_string() -> None: "host": "0.0.0.0", "port": 8000, "reload": False, + "reload_dirs": None, "workers": None, "root_path": "", "proxy_headers": True,