fastapi-cli 0.0.10__tar.gz → 0.0.11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/PKG-INFO +1 -1
  2. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/pyproject.toml +1 -1
  3. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/requirements-tests.txt +1 -1
  4. fastapi_cli-0.0.11/src/fastapi_cli/__init__.py +1 -0
  5. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/src/fastapi_cli/cli.py +28 -5
  6. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/src/fastapi_cli/discover.py +23 -0
  7. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/test_cli.py +42 -0
  8. fastapi_cli-0.0.11/tests/test_discover.py +58 -0
  9. fastapi_cli-0.0.10/src/fastapi_cli/__init__.py +0 -1
  10. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/LICENSE +0 -0
  11. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/README.md +0 -0
  12. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/pdm_build.py +0 -0
  13. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/requirements-github-actions.txt +0 -0
  14. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/requirements.txt +0 -0
  15. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/scripts/format.sh +0 -0
  16. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/scripts/lint.sh +0 -0
  17. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/scripts/test-cov-html.sh +0 -0
  18. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/scripts/test.sh +0 -0
  19. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/src/fastapi_cli/__main__.py +0 -0
  20. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/src/fastapi_cli/exceptions.py +0 -0
  21. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/src/fastapi_cli/logging.py +0 -0
  22. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/src/fastapi_cli/py.typed +0 -0
  23. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/src/fastapi_cli/utils/__init__.py +0 -0
  24. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/src/fastapi_cli/utils/cli.py +0 -0
  25. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/__init__.py +0 -0
  26. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/broken_package/mod/__init__.py +0 -0
  27. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/broken_package/mod/app.py +0 -0
  28. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/broken_package/utils.py +0 -0
  29. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_api/api.py +0 -0
  30. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_app/api.py +0 -0
  31. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_app/app.py +0 -0
  32. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_app_dir_api/app/__init__.py +0 -0
  33. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_app_dir_api/app/api.py +0 -0
  34. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_app_dir_app/app/__init__.py +0 -0
  35. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_app_dir_app/app/api.py +0 -0
  36. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_app_dir_app/app/app.py +0 -0
  37. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_app_dir_main/app/__init__.py +0 -0
  38. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_app_dir_main/app/api.py +0 -0
  39. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_app_dir_main/app/app.py +0 -0
  40. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_app_dir_main/app/main.py +0 -0
  41. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_app_dir_non_default/app/__init__.py +0 -0
  42. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_app_dir_non_default/app/nondefault.py +0 -0
  43. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_main/api.py +0 -0
  44. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_main/app.py +0 -0
  45. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/default_main/main.py +0 -0
  46. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/default_files/non_default/nonstandard.py +0 -0
  47. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/nested_package/__init__.py +0 -0
  48. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/nested_package/package/__init__.py +0 -0
  49. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/nested_package/package/core/__init__.py +0 -0
  50. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/nested_package/package/core/utils.py +0 -0
  51. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/nested_package/package/mod/__init__.py +0 -0
  52. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/nested_package/package/mod/api.py +0 -0
  53. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/nested_package/package/mod/app.py +0 -0
  54. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/nested_package/package/mod/other.py +0 -0
  55. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/package/__init__.py +0 -0
  56. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/package/core/__init__.py +0 -0
  57. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/package/core/utils.py +0 -0
  58. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/package/mod/__init__.py +0 -0
  59. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/package/mod/api.py +0 -0
  60. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/package/mod/app.py +0 -0
  61. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/package/mod/other.py +0 -0
  62. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/single_file_api.py +0 -0
  63. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/single_file_app.py +0 -0
  64. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/assets/single_file_other.py +0 -0
  65. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/conftest.py +0 -0
  66. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/test_requirements.py +0 -0
  67. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/test_utils_cli.py +0 -0
  68. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/test_utils_default_dir.py +0 -0
  69. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/test_utils_default_file.py +0 -0
  70. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/test_utils_package.py +0 -0
  71. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/test_utils_single_file.py +0 -0
  72. {fastapi_cli-0.0.10 → fastapi_cli-0.0.11}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastapi-cli
3
- Version: 0.0.10
3
+ Version: 0.0.11
4
4
  Summary: Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀
5
5
  Author-Email: =?utf-8?q?Sebasti=C3=A1n_Ram=C3=ADrez?= <tiangolo@gmail.com>
6
6
  License: MIT
@@ -35,7 +35,7 @@ dependencies = [
35
35
  "uvicorn[standard] >= 0.15.0",
36
36
  "rich-toolkit >= 0.14.8",
37
37
  ]
38
- version = "0.0.10"
38
+ version = "0.0.11"
39
39
 
40
40
  [project.license]
41
41
  text = "MIT"
@@ -3,7 +3,7 @@
3
3
  pytest >=4.4.0,<9.0.0
4
4
  coverage[toml] >=6.2,<8.0
5
5
  mypy ==1.14.0
6
- ruff ==0.12.11
6
+ ruff ==0.12.12
7
7
  # Needed explicitly by fastapi-cli-slim
8
8
  fastapi-slim
9
9
  uvicorn
@@ -0,0 +1 @@
1
+ __version__ = "0.0.11"
@@ -7,7 +7,7 @@ from rich import print
7
7
  from rich.tree import Tree
8
8
  from typing_extensions import Annotated
9
9
 
10
- from fastapi_cli.discover import get_import_data
10
+ from fastapi_cli.discover import get_import_data, get_import_data_from_import_string
11
11
  from fastapi_cli.exceptions import FastAPICLIException
12
12
 
13
13
  from . import __version__
@@ -95,6 +95,7 @@ def _run(
95
95
  root_path: str = "",
96
96
  command: str,
97
97
  app: Union[str, None] = None,
98
+ entrypoint: Union[str, None] = None,
98
99
  proxy_headers: bool = False,
99
100
  forwarded_allow_ips: Union[str, None] = None,
100
101
  ) -> None:
@@ -109,7 +110,10 @@ def _run(
109
110
  )
110
111
 
111
112
  try:
112
- import_data = get_import_data(path=path, app_name=app)
113
+ if entrypoint:
114
+ import_data = get_import_data_from_import_string(entrypoint)
115
+ else:
116
+ import_data = get_import_data(path=path, app_name=app)
113
117
  except FastAPICLIException as e:
114
118
  toolkit.print_line()
115
119
  toolkit.print(f"[error]{e}")
@@ -124,10 +128,11 @@ def _run(
124
128
  toolkit.print(f"Importing from {module_data.extra_sys_path}")
125
129
  toolkit.print_line()
126
130
 
127
- root_tree = _get_module_tree(module_data.module_paths)
131
+ if module_data.module_paths:
132
+ root_tree = _get_module_tree(module_data.module_paths)
128
133
 
129
- toolkit.print(root_tree, tag="module")
130
- toolkit.print_line()
134
+ toolkit.print(root_tree, tag="module")
135
+ toolkit.print_line()
131
136
 
132
137
  toolkit.print(
133
138
  "Importing the FastAPI app object from the module with the following code:",
@@ -222,6 +227,14 @@ def dev(
222
227
  help="The name of the variable that contains the [bold]FastAPI[/bold] app in the imported module or package. If not provided, it is detected automatically."
223
228
  ),
224
229
  ] = None,
230
+ entrypoint: Annotated[
231
+ Union[str, None],
232
+ typer.Option(
233
+ "--entrypoint",
234
+ "-e",
235
+ help="The FastAPI app import string in the format 'some.importable_module:app_name'.",
236
+ ),
237
+ ] = None,
225
238
  proxy_headers: Annotated[
226
239
  bool,
227
240
  typer.Option(
@@ -267,6 +280,7 @@ def dev(
267
280
  reload=reload,
268
281
  root_path=root_path,
269
282
  app=app,
283
+ entrypoint=entrypoint,
270
284
  command="dev",
271
285
  proxy_headers=proxy_headers,
272
286
  forwarded_allow_ips=forwarded_allow_ips,
@@ -318,6 +332,14 @@ def run(
318
332
  help="The name of the variable that contains the [bold]FastAPI[/bold] app in the imported module or package. If not provided, it is detected automatically."
319
333
  ),
320
334
  ] = None,
335
+ entrypoint: Annotated[
336
+ Union[str, None],
337
+ typer.Option(
338
+ "--entrypoint",
339
+ "-e",
340
+ help="The FastAPI app import string in the format 'some.importable_module:app_name'.",
341
+ ),
342
+ ] = None,
321
343
  proxy_headers: Annotated[
322
344
  bool,
323
345
  typer.Option(
@@ -364,6 +386,7 @@ def run(
364
386
  workers=workers,
365
387
  root_path=root_path,
366
388
  app=app,
389
+ entrypoint=entrypoint,
367
390
  command="run",
368
391
  proxy_headers=proxy_headers,
369
392
  forwarded_allow_ips=forwarded_allow_ips,
@@ -130,3 +130,26 @@ def get_import_data(
130
130
  return ImportData(
131
131
  app_name=use_app_name, module_data=mod_data, import_string=import_string
132
132
  )
133
+
134
+
135
+ def get_import_data_from_import_string(import_string: str) -> ImportData:
136
+ module_str, _, app_name = import_string.partition(":")
137
+
138
+ if not module_str or not app_name:
139
+ raise FastAPICLIException(
140
+ "Import string must be in the format module.submodule:app_name"
141
+ )
142
+
143
+ here = Path(".").resolve()
144
+
145
+ sys.path.insert(0, str(here))
146
+
147
+ return ImportData(
148
+ app_name=app_name,
149
+ module_data=ModuleData(
150
+ module_import_str=module_str,
151
+ extra_sys_path=here,
152
+ module_paths=[],
153
+ ),
154
+ import_string=import_string,
155
+ )
@@ -285,6 +285,48 @@ def test_version() -> None:
285
285
  assert "FastAPI CLI version:" in result.output
286
286
 
287
287
 
288
+ def test_dev_with_import_string() -> None:
289
+ with changing_dir(assets_path):
290
+ with patch.object(uvicorn, "run") as mock_run:
291
+ result = runner.invoke(app, ["dev", "--entrypoint", "single_file_app:api"])
292
+ assert result.exit_code == 0, result.output
293
+ assert mock_run.called
294
+ assert mock_run.call_args
295
+ assert mock_run.call_args.kwargs == {
296
+ "app": "single_file_app:api",
297
+ "forwarded_allow_ips": None,
298
+ "host": "127.0.0.1",
299
+ "port": 8000,
300
+ "reload": True,
301
+ "workers": None,
302
+ "root_path": "",
303
+ "proxy_headers": True,
304
+ "log_config": get_uvicorn_log_config(),
305
+ }
306
+ assert "Using import string: single_file_app:api" in result.output
307
+
308
+
309
+ def test_run_with_import_string() -> None:
310
+ with changing_dir(assets_path):
311
+ with patch.object(uvicorn, "run") as mock_run:
312
+ result = runner.invoke(app, ["run", "--entrypoint", "single_file_app:app"])
313
+ assert result.exit_code == 0, result.output
314
+ assert mock_run.called
315
+ assert mock_run.call_args
316
+ assert mock_run.call_args.kwargs == {
317
+ "app": "single_file_app:app",
318
+ "forwarded_allow_ips": None,
319
+ "host": "0.0.0.0",
320
+ "port": 8000,
321
+ "reload": False,
322
+ "workers": None,
323
+ "root_path": "",
324
+ "proxy_headers": True,
325
+ "log_config": get_uvicorn_log_config(),
326
+ }
327
+ assert "Using import string: single_file_app:app" in result.output
328
+
329
+
288
330
  def test_script() -> None:
289
331
  result = subprocess.run(
290
332
  [sys.executable, "-m", "coverage", "run", "-m", "fastapi_cli", "--help"],
@@ -0,0 +1,58 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ from fastapi_cli.discover import (
6
+ ImportData,
7
+ get_import_data_from_import_string,
8
+ )
9
+ from fastapi_cli.exceptions import FastAPICLIException
10
+
11
+ assets_path = Path(__file__).parent / "assets"
12
+
13
+
14
+ def test_get_import_data_from_import_string_valid() -> None:
15
+ result = get_import_data_from_import_string("module.submodule:app")
16
+
17
+ assert isinstance(result, ImportData)
18
+ assert result.app_name == "app"
19
+ assert result.import_string == "module.submodule:app"
20
+ assert result.module_data.module_import_str == "module.submodule"
21
+ assert result.module_data.extra_sys_path == Path(".").resolve()
22
+ assert result.module_data.module_paths == []
23
+
24
+
25
+ def test_get_import_data_from_import_string_missing_colon() -> None:
26
+ with pytest.raises(FastAPICLIException) as exc_info:
27
+ get_import_data_from_import_string("module.submodule")
28
+
29
+ assert "Import string must be in the format module.submodule:app_name" in str(
30
+ exc_info.value
31
+ )
32
+
33
+
34
+ def test_get_import_data_from_import_string_missing_app() -> None:
35
+ with pytest.raises(FastAPICLIException) as exc_info:
36
+ get_import_data_from_import_string("module.submodule:")
37
+
38
+ assert "Import string must be in the format module.submodule:app_name" in str(
39
+ exc_info.value
40
+ )
41
+
42
+
43
+ def test_get_import_data_from_import_string_missing_module() -> None:
44
+ with pytest.raises(FastAPICLIException) as exc_info:
45
+ get_import_data_from_import_string(":app")
46
+
47
+ assert "Import string must be in the format module.submodule:app_name" in str(
48
+ exc_info.value
49
+ )
50
+
51
+
52
+ def test_get_import_data_from_import_string_empty() -> None:
53
+ with pytest.raises(FastAPICLIException) as exc_info:
54
+ get_import_data_from_import_string("")
55
+
56
+ assert "Import string must be in the format module.submodule:app_name" in str(
57
+ exc_info.value
58
+ )
@@ -1 +0,0 @@
1
- __version__ = "0.0.10"
File without changes
File without changes
File without changes