fastapi-cli 0.0.5__tar.gz → 0.0.6__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 (75) hide show
  1. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/PKG-INFO +33 -44
  2. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/README.md +27 -40
  3. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/pyproject.toml +12 -3
  4. fastapi_cli-0.0.6/requirements-github-actions.txt +1 -0
  5. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/requirements-tests.txt +1 -1
  6. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/requirements.txt +1 -1
  7. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/scripts/format.sh +1 -1
  8. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/scripts/test-cov-html.sh +1 -1
  9. fastapi_cli-0.0.6/src/fastapi_cli/__init__.py +1 -0
  10. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/src/fastapi_cli/cli.py +114 -43
  11. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/src/fastapi_cli/discover.py +24 -59
  12. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/src/fastapi_cli/logging.py +4 -2
  13. fastapi_cli-0.0.6/src/fastapi_cli/utils/cli.py +75 -0
  14. fastapi_cli-0.0.6/tests/assets/nested_package/__init__.py +0 -0
  15. fastapi_cli-0.0.6/tests/assets/nested_package/package/core/__init__.py +0 -0
  16. fastapi_cli-0.0.6/tests/assets/package/__init__.py +2 -0
  17. fastapi_cli-0.0.6/tests/assets/package/core/__init__.py +0 -0
  18. fastapi_cli-0.0.6/tests/assets/package/core/utils.py +2 -0
  19. fastapi_cli-0.0.6/tests/assets/package/mod/__init__.py +1 -0
  20. fastapi_cli-0.0.6/tests/assets/package/mod/api.py +24 -0
  21. fastapi_cli-0.0.6/tests/assets/package/mod/app.py +32 -0
  22. fastapi_cli-0.0.6/tests/assets/package/mod/other.py +16 -0
  23. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/test_cli.py +64 -24
  24. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/test_requirements.py +2 -2
  25. fastapi_cli-0.0.6/tests/test_utils_cli.py +35 -0
  26. fastapi_cli-0.0.6/tests/test_utils_default_dir.py +52 -0
  27. fastapi_cli-0.0.6/tests/test_utils_default_file.py +73 -0
  28. fastapi_cli-0.0.6/tests/test_utils_package.py +197 -0
  29. fastapi_cli-0.0.6/tests/test_utils_single_file.py +59 -0
  30. fastapi_cli-0.0.5/src/fastapi_cli/__init__.py +0 -1
  31. fastapi_cli-0.0.5/tests/test_utils_default_dir.py +0 -88
  32. fastapi_cli-0.0.5/tests/test_utils_default_file.py +0 -103
  33. fastapi_cli-0.0.5/tests/test_utils_package.py +0 -455
  34. fastapi_cli-0.0.5/tests/test_utils_single_file.py +0 -115
  35. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/LICENSE +0 -0
  36. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/pdm_build.py +0 -0
  37. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/scripts/lint.sh +0 -0
  38. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/scripts/test.sh +0 -0
  39. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/src/fastapi_cli/__main__.py +0 -0
  40. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/src/fastapi_cli/exceptions.py +0 -0
  41. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/src/fastapi_cli/py.typed +0 -0
  42. {fastapi_cli-0.0.5/tests → fastapi_cli-0.0.6/src/fastapi_cli/utils}/__init__.py +0 -0
  43. {fastapi_cli-0.0.5/tests/assets/default_files/default_app_dir_api/app → fastapi_cli-0.0.6/tests}/__init__.py +0 -0
  44. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/broken_package/mod/__init__.py +0 -0
  45. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/broken_package/mod/app.py +0 -0
  46. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/broken_package/utils.py +0 -0
  47. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/default_api/api.py +0 -0
  48. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/default_app/api.py +0 -0
  49. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/default_app/app.py +0 -0
  50. {fastapi_cli-0.0.5/tests/assets/default_files/default_app_dir_app → fastapi_cli-0.0.6/tests/assets/default_files/default_app_dir_api}/app/__init__.py +0 -0
  51. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/default_app_dir_api/app/api.py +0 -0
  52. {fastapi_cli-0.0.5/tests/assets/default_files/default_app_dir_main → fastapi_cli-0.0.6/tests/assets/default_files/default_app_dir_app}/app/__init__.py +0 -0
  53. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/default_app_dir_app/app/api.py +0 -0
  54. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/default_app_dir_app/app/app.py +0 -0
  55. {fastapi_cli-0.0.5/tests/assets/default_files/default_app_dir_non_default → fastapi_cli-0.0.6/tests/assets/default_files/default_app_dir_main}/app/__init__.py +0 -0
  56. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/default_app_dir_main/app/api.py +0 -0
  57. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/default_app_dir_main/app/app.py +0 -0
  58. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/default_app_dir_main/app/main.py +0 -0
  59. {fastapi_cli-0.0.5/tests/assets/package/core → fastapi_cli-0.0.6/tests/assets/default_files/default_app_dir_non_default/app}/__init__.py +0 -0
  60. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/default_app_dir_non_default/app/nondefault.py +0 -0
  61. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/default_main/api.py +0 -0
  62. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/default_main/app.py +0 -0
  63. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/default_main/main.py +0 -0
  64. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/default_files/non_default/nonstandard.py +0 -0
  65. {fastapi_cli-0.0.5/tests/assets → fastapi_cli-0.0.6/tests/assets/nested_package}/package/__init__.py +0 -0
  66. {fastapi_cli-0.0.5/tests/assets → fastapi_cli-0.0.6/tests/assets/nested_package}/package/core/utils.py +0 -0
  67. {fastapi_cli-0.0.5/tests/assets → fastapi_cli-0.0.6/tests/assets/nested_package}/package/mod/__init__.py +0 -0
  68. {fastapi_cli-0.0.5/tests/assets → fastapi_cli-0.0.6/tests/assets/nested_package}/package/mod/api.py +0 -0
  69. {fastapi_cli-0.0.5/tests/assets → fastapi_cli-0.0.6/tests/assets/nested_package}/package/mod/app.py +0 -0
  70. {fastapi_cli-0.0.5/tests/assets → fastapi_cli-0.0.6/tests/assets/nested_package}/package/mod/other.py +0 -0
  71. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/single_file_api.py +0 -0
  72. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/single_file_app.py +0 -0
  73. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/assets/single_file_other.py +0 -0
  74. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/conftest.py +0 -0
  75. {fastapi_cli-0.0.5 → fastapi_cli-0.0.6}/tests/utils.py +0 -0
@@ -1,8 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastapi-cli
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Summary: Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀
5
- Home-page: https://github.com/fastapi/fastapi-cli
6
5
  Author-Email: =?utf-8?q?Sebasti=C3=A1n_Ram=C3=ADrez?= <tiangolo@gmail.com>
7
6
  License: MIT
8
7
  Classifier: Intended Audience :: Information Technology
@@ -26,14 +25,17 @@ Classifier: Programming Language :: Python :: 3.10
26
25
  Classifier: Programming Language :: Python :: 3.11
27
26
  Classifier: Programming Language :: Python :: 3.12
28
27
  Classifier: License :: OSI Approved :: MIT License
29
- Project-URL: Documentation, https://fastapi.tiangolo.com
30
28
  Project-URL: Homepage, https://github.com/fastapi/fastapi-cli
29
+ Project-URL: Documentation, https://fastapi.tiangolo.com/fastapi-cli/
31
30
  Project-URL: Repository, https://github.com/fastapi/fastapi-cli
31
+ Project-URL: Issues, https://github.com/fastapi/fastapi-cli/issues
32
+ Project-URL: Changelog, https://github.com/fastapi/fastapi-cli/blob/main/release-notes.md
32
33
  Requires-Python: >=3.8
33
34
  Requires-Dist: typer>=0.12.3
34
35
  Requires-Dist: uvicorn[standard]>=0.15.0
35
- Requires-Dist: uvicorn[standard]>=0.15.0; extra == "standard"
36
+ Requires-Dist: rich-toolkit>=0.11.1
36
37
  Provides-Extra: standard
38
+ Requires-Dist: uvicorn[standard]>=0.15.0; extra == "standard"
37
39
  Description-Content-Type: text/markdown
38
40
 
39
41
  # FastAPI CLI
@@ -70,46 +72,33 @@ To run your FastAPI app for development, you can use the `fastapi dev` command:
70
72
 
71
73
  ```console
72
74
  $ fastapi dev main.py
73
- INFO Using path main.py
74
- INFO Resolved absolute path /home/user/code/awesomeapp/main.py
75
- INFO Searching for package file structure from directories with __init__.py files
76
- INFO Importing from /home/user/code/awesomeapp
77
-
78
- ╭─ Python module file ─╮
79
- │ │
80
- │ 🐍 main.py │
81
- │ │
82
- ╰──────────────────────╯
83
-
84
- INFO Importing module main
85
- INFO Found importable FastAPI app
86
-
87
- ╭─ Importable FastAPI app ─╮
88
- │ │
89
- │ from main import app │
90
- │ │
91
- ╰──────────────────────────╯
92
-
93
- INFO Using import string main:app
94
-
95
- ╭────────── FastAPI CLI - Development mode ───────────╮
96
- │ │
97
- │ Serving at: http://127.0.0.1:8000 │
98
- │ │
99
- │ API docs: http://127.0.0.1:8000/docs │
100
- │ │
101
- │ Running in development mode, for production use: │
102
- │ │
103
- │ fastapi run │
104
- │ │
105
- ╰─────────────────────────────────────────────────────╯
106
-
107
- INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
108
- INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
109
- INFO: Started reloader process [56345] using WatchFiles
110
- INFO: Started server process [56352]
111
- INFO: Waiting for application startup.
112
- INFO: Application startup complete.
75
+
76
+ FastAPI Starting development server 🚀
77
+
78
+ Searching for package file structure from directories with __init__.py files
79
+ Importing from /home/user/code/awesomeapp
80
+
81
+ module 🐍 main.py
82
+
83
+ code Importing the FastAPI app object from the module with the following code:
84
+
85
+ from main import app
86
+
87
+ app Using import string: main:app
88
+
89
+ server Server started at http://127.0.0.1:8000
90
+ server Documentation at http://127.0.0.1:8000/docs
91
+
92
+ tip Running in development mode, for production use: fastapi run
93
+
94
+ Logs:
95
+
96
+ INFO Will watch for changes in these directories: ['/home/user/code/awesomeapp']
97
+ INFO Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
98
+ INFO Started reloader process [4106097] using WatchFiles
99
+ INFO Started server process [4106120]
100
+ INFO Waiting for application startup.
101
+ INFO Application startup complete.
113
102
  ```
114
103
 
115
104
  </div>
@@ -32,46 +32,33 @@ To run your FastAPI app for development, you can use the `fastapi dev` command:
32
32
 
33
33
  ```console
34
34
  $ fastapi dev main.py
35
- INFO Using path main.py
36
- INFO Resolved absolute path /home/user/code/awesomeapp/main.py
37
- INFO Searching for package file structure from directories with __init__.py files
38
- INFO Importing from /home/user/code/awesomeapp
39
-
40
- ╭─ Python module file ─╮
41
- │ │
42
- │ 🐍 main.py │
43
- │ │
44
- ╰──────────────────────╯
45
-
46
- INFO Importing module main
47
- INFO Found importable FastAPI app
48
-
49
- ╭─ Importable FastAPI app ─╮
50
- │ │
51
- │ from main import app │
52
- │ │
53
- ╰──────────────────────────╯
54
-
55
- INFO Using import string main:app
56
-
57
- ╭────────── FastAPI CLI - Development mode ───────────╮
58
- │ │
59
- │ Serving at: http://127.0.0.1:8000 │
60
- │ │
61
- │ API docs: http://127.0.0.1:8000/docs │
62
- │ │
63
- │ Running in development mode, for production use: │
64
- │ │
65
- │ fastapi run │
66
- │ │
67
- ╰─────────────────────────────────────────────────────╯
68
-
69
- INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
70
- INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
71
- INFO: Started reloader process [56345] using WatchFiles
72
- INFO: Started server process [56352]
73
- INFO: Waiting for application startup.
74
- INFO: Application startup complete.
35
+
36
+ FastAPI Starting development server 🚀
37
+
38
+ Searching for package file structure from directories with __init__.py files
39
+ Importing from /home/user/code/awesomeapp
40
+
41
+ module 🐍 main.py
42
+
43
+ code Importing the FastAPI app object from the module with the following code:
44
+
45
+ from main import app
46
+
47
+ app Using import string: main:app
48
+
49
+ server Server started at http://127.0.0.1:8000
50
+ server Documentation at http://127.0.0.1:8000/docs
51
+
52
+ tip Running in development mode, for production use: fastapi run
53
+
54
+ Logs:
55
+
56
+ INFO Will watch for changes in these directories: ['/home/user/code/awesomeapp']
57
+ INFO Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
58
+ INFO Started reloader process [4106097] using WatchFiles
59
+ INFO Started server process [4106120]
60
+ INFO Waiting for application startup.
61
+ INFO Application startup complete.
75
62
  ```
76
63
 
77
64
  </div>
@@ -33,8 +33,9 @@ classifiers = [
33
33
  dependencies = [
34
34
  "typer >= 0.12.3",
35
35
  "uvicorn[standard] >= 0.15.0",
36
+ "rich-toolkit >= 0.11.1",
36
37
  ]
37
- version = "0.0.5"
38
+ version = "0.0.6"
38
39
 
39
40
  [project.license]
40
41
  text = "MIT"
@@ -45,9 +46,11 @@ standard = [
45
46
  ]
46
47
 
47
48
  [project.urls]
48
- Documentation = "https://fastapi.tiangolo.com"
49
- homepage = "https://github.com/fastapi/fastapi-cli"
49
+ Homepage = "https://github.com/fastapi/fastapi-cli"
50
+ Documentation = "https://fastapi.tiangolo.com/fastapi-cli/"
50
51
  Repository = "https://github.com/fastapi/fastapi-cli"
52
+ Issues = "https://github.com/fastapi/fastapi-cli/issues"
53
+ Changelog = "https://github.com/fastapi/fastapi-cli/blob/main/release-notes.md"
51
54
 
52
55
  [project.scripts]
53
56
  fastapi = "fastapi_cli.cli:main"
@@ -91,11 +94,14 @@ source = [
91
94
  "tests",
92
95
  ]
93
96
  context = "${CONTEXT}"
97
+ dynamic_context = "test_function"
94
98
  omit = [
95
99
  "tests/assets/*",
96
100
  ]
97
101
 
98
102
  [tool.coverage.report]
103
+ show_missing = true
104
+ sort = "-Cover"
99
105
  exclude_lines = [
100
106
  "pragma: no cover",
101
107
  "@overload",
@@ -103,6 +109,9 @@ exclude_lines = [
103
109
  "if TYPE_CHECKING:",
104
110
  ]
105
111
 
112
+ [tool.coverage.html]
113
+ show_contexts = true
114
+
106
115
  [tool.mypy]
107
116
  strict = true
108
117
  exclude = [
@@ -0,0 +1 @@
1
+ smokeshow
@@ -2,7 +2,7 @@
2
2
 
3
3
  pytest >=4.4.0,<9.0.0
4
4
  coverage[toml] >=6.2,<8.0
5
- mypy ==1.11.1
5
+ mypy ==1.13.0
6
6
  ruff ==0.5.5
7
7
  # Needed explicitly by fastapi-cli-slim
8
8
  fastapi-slim
@@ -2,4 +2,4 @@
2
2
 
3
3
  -r requirements-tests.txt
4
4
 
5
- pre-commit >=2.17.0,<4.0.0
5
+ pre-commit >=2.17.0,<5.0.0
@@ -2,5 +2,5 @@
2
2
  set -x
3
3
  set -e
4
4
 
5
- ruff src tests scripts --fix
5
+ ruff check src tests scripts --fix
6
6
  ruff format src tests scripts
@@ -5,5 +5,5 @@ set -x
5
5
 
6
6
  bash scripts/test.sh ${@}
7
7
  coverage combine
8
- coverage report --show-missing
8
+ coverage report
9
9
  coverage html
@@ -0,0 +1 @@
1
+ __version__ = "0.0.6"
@@ -1,23 +1,23 @@
1
- from logging import getLogger
1
+ import logging
2
2
  from pathlib import Path
3
- from typing import Any, Union
3
+ from typing import Any, List, Union
4
4
 
5
5
  import typer
6
6
  from rich import print
7
- from rich.padding import Padding
8
- from rich.panel import Panel
7
+ from rich.tree import Tree
9
8
  from typing_extensions import Annotated
10
9
 
11
- from fastapi_cli.discover import get_import_string
10
+ from fastapi_cli.discover import get_import_data
12
11
  from fastapi_cli.exceptions import FastAPICLIException
13
12
 
14
13
  from . import __version__
15
14
  from .logging import setup_logging
15
+ from .utils.cli import get_rich_toolkit, get_uvicorn_log_config
16
16
 
17
17
  app = typer.Typer(rich_markup_mode="rich")
18
18
 
19
- setup_logging()
20
- logger = getLogger(__name__)
19
+ logger = logging.getLogger(__name__)
20
+
21
21
 
22
22
  try:
23
23
  import uvicorn
@@ -39,15 +39,41 @@ def callback(
39
39
  "--version", help="Show the version and exit.", callback=version_callback
40
40
  ),
41
41
  ] = None,
42
+ verbose: bool = typer.Option(False, help="Enable verbose output"),
42
43
  ) -> None:
43
44
  """
44
45
  FastAPI CLI - The [bold]fastapi[/bold] command line app. 😎
45
46
 
46
47
  Manage your [bold]FastAPI[/bold] projects, run your FastAPI apps, and more.
47
48
 
48
- Read more in the docs: [link]https://fastapi.tiangolo.com/fastapi-cli/[/link].
49
+ Read more in the docs: [link=https://fastapi.tiangolo.com/fastapi-cli/]https://fastapi.tiangolo.com/fastapi-cli/[/link].
49
50
  """
50
51
 
52
+ log_level = logging.DEBUG if verbose else logging.INFO
53
+
54
+ setup_logging(level=log_level)
55
+
56
+
57
+ def _get_module_tree(module_paths: List[Path]) -> Tree:
58
+ root = module_paths[0]
59
+ name = f"🐍 {root.name}" if root.is_file() else f"📁 {root.name}"
60
+
61
+ root_tree = Tree(name)
62
+
63
+ if root.is_dir():
64
+ root_tree.add("[dim]🐍 __init__.py[/dim]")
65
+
66
+ tree = root_tree
67
+ for sub_path in module_paths[1:]:
68
+ sub_name = (
69
+ f"🐍 {sub_path.name}" if sub_path.is_file() else f"📁 {sub_path.name}"
70
+ )
71
+ tree = tree.add(sub_name)
72
+ if sub_path.is_dir():
73
+ tree.add("[dim]🐍 __init__.py[/dim]")
74
+
75
+ return root_tree
76
+
51
77
 
52
78
  def _run(
53
79
  path: Union[Path, None] = None,
@@ -61,43 +87,88 @@ def _run(
61
87
  app: Union[str, None] = None,
62
88
  proxy_headers: bool = False,
63
89
  ) -> None:
64
- try:
65
- use_uvicorn_app = get_import_string(path=path, app_name=app)
66
- except FastAPICLIException as e:
67
- logger.error(str(e))
68
- raise typer.Exit(code=1) from None
69
- serving_str = f"[dim]Serving at:[/dim] [link]http://{host}:{port}[/link]\n\n[dim]API docs:[/dim] [link]http://{host}:{port}/docs[/link]"
70
-
71
- if command == "dev":
72
- panel = Panel(
73
- f"{serving_str}\n\n[dim]Running in development mode, for production use:[/dim] \n\n[b]fastapi run[/b]",
74
- title="FastAPI CLI - Development mode",
75
- expand=False,
76
- padding=(1, 2),
77
- style="black on yellow",
90
+ with get_rich_toolkit() as toolkit:
91
+ server_type = "development" if command == "dev" else "production"
92
+
93
+ toolkit.print_title(f"Starting {server_type} server 🚀", tag="FastAPI")
94
+ toolkit.print_line()
95
+
96
+ toolkit.print(
97
+ "Searching for package file structure from directories with [blue]__init__.py[/blue] files"
98
+ )
99
+
100
+ try:
101
+ import_data = get_import_data(path=path, app_name=app)
102
+ except FastAPICLIException as e:
103
+ toolkit.print_line()
104
+ toolkit.print(f"[error]{e}")
105
+ raise typer.Exit(code=1) from None
106
+
107
+ logger.debug(f"Importing from {import_data.module_data.extra_sys_path}")
108
+ logger.debug(f"Importing module {import_data.module_data.module_import_str}")
109
+
110
+ module_data = import_data.module_data
111
+ import_string = import_data.import_string
112
+
113
+ toolkit.print(f"Importing from {module_data.extra_sys_path}")
114
+ toolkit.print_line()
115
+
116
+ root_tree = _get_module_tree(module_data.module_paths)
117
+
118
+ toolkit.print(root_tree, tag="module")
119
+ toolkit.print_line()
120
+
121
+ toolkit.print(
122
+ "Importing the FastAPI app object from the module with the following code:",
123
+ tag="code",
78
124
  )
79
- else:
80
- panel = Panel(
81
- f"{serving_str}\n\n[dim]Running in production mode, for development use:[/dim] \n\n[b]fastapi dev[/b]",
82
- title="FastAPI CLI - Production mode",
83
- expand=False,
84
- padding=(1, 2),
85
- style="green",
125
+ toolkit.print_line()
126
+ toolkit.print(
127
+ f"[underline]from [bold]{module_data.module_import_str}[/bold] import [bold]{import_data.app_name}[/bold]"
128
+ )
129
+ toolkit.print_line()
130
+
131
+ toolkit.print(
132
+ f"Using import string: [blue]{import_string}[/]",
133
+ tag="app",
134
+ )
135
+
136
+ url = f"http://{host}:{port}"
137
+ url_docs = f"{url}/docs"
138
+
139
+ toolkit.print_line()
140
+ toolkit.print(
141
+ f"Server started at [link={url}]{url}[/]",
142
+ f"Documentation at [link={url_docs}]{url_docs}[/]",
143
+ tag="server",
144
+ )
145
+
146
+ if command == "dev":
147
+ toolkit.print_line()
148
+ toolkit.print(
149
+ "Running in development mode, for production use: [bold]fastapi run[/]",
150
+ tag="tip",
151
+ )
152
+
153
+ if not uvicorn:
154
+ raise FastAPICLIException(
155
+ "Could not import Uvicorn, try running 'pip install uvicorn'"
156
+ ) from None
157
+
158
+ toolkit.print_line()
159
+ toolkit.print("Logs:")
160
+ toolkit.print_line()
161
+
162
+ uvicorn.run(
163
+ app=import_string,
164
+ host=host,
165
+ port=port,
166
+ reload=reload,
167
+ workers=workers,
168
+ root_path=root_path,
169
+ proxy_headers=proxy_headers,
170
+ log_config=get_uvicorn_log_config(),
86
171
  )
87
- print(Padding(panel, 1))
88
- if not uvicorn:
89
- raise FastAPICLIException(
90
- "Could not import Uvicorn, try running 'pip install uvicorn'"
91
- ) from None
92
- uvicorn.run(
93
- app=use_uvicorn_app,
94
- host=host,
95
- port=port,
96
- reload=reload,
97
- workers=workers,
98
- root_path=root_path,
99
- proxy_headers=proxy_headers,
100
- )
101
172
 
102
173
 
103
174
  @app.command()
@@ -3,13 +3,7 @@ import sys
3
3
  from dataclasses import dataclass
4
4
  from logging import getLogger
5
5
  from pathlib import Path
6
- from typing import Union
7
-
8
- from rich import print
9
- from rich.padding import Padding
10
- from rich.panel import Panel
11
- from rich.syntax import Syntax
12
- from rich.tree import Tree
6
+ from typing import List, Union
13
7
 
14
8
  from fastapi_cli.exceptions import FastAPICLIException
15
9
 
@@ -45,12 +39,10 @@ def get_default_path() -> Path:
45
39
  class ModuleData:
46
40
  module_import_str: str
47
41
  extra_sys_path: Path
42
+ module_paths: List[Path]
48
43
 
49
44
 
50
45
  def get_module_data_from_path(path: Path) -> ModuleData:
51
- logger.info(
52
- "Searching for package file structure from directories with [blue]__init__.py[/blue] files"
53
- )
54
46
  use_path = path.resolve()
55
47
  module_path = use_path
56
48
  if use_path.is_file() and use_path.stem == "__init__":
@@ -64,37 +56,12 @@ def get_module_data_from_path(path: Path) -> ModuleData:
64
56
  extra_sys_path = parent.parent
65
57
  else:
66
58
  break
67
- logger.info(f"Importing from {extra_sys_path.resolve()}")
68
- root = module_paths[0]
69
- name = f"🐍 {root.name}" if root.is_file() else f"📁 {root.name}"
70
- root_tree = Tree(name)
71
- if root.is_dir():
72
- root_tree.add("[dim]🐍 __init__.py[/dim]")
73
- tree = root_tree
74
- for sub_path in module_paths[1:]:
75
- sub_name = (
76
- f"🐍 {sub_path.name}" if sub_path.is_file() else f"📁 {sub_path.name}"
77
- )
78
- tree = tree.add(sub_name)
79
- if sub_path.is_dir():
80
- tree.add("[dim]🐍 __init__.py[/dim]")
81
- title = "[b green]Python module file[/b green]"
82
- if len(module_paths) > 1 or module_path.is_dir():
83
- title = "[b green]Python package file structure[/b green]"
84
- panel = Padding(
85
- Panel(
86
- root_tree,
87
- title=title,
88
- expand=False,
89
- padding=(1, 2),
90
- ),
91
- 1,
92
- )
93
- print(panel)
59
+
94
60
  module_str = ".".join(p.stem for p in module_paths)
95
- logger.info(f"Importing module [green]{module_str}[/green]")
96
61
  return ModuleData(
97
- module_import_str=module_str, extra_sys_path=extra_sys_path.resolve()
62
+ module_import_str=module_str,
63
+ extra_sys_path=extra_sys_path.resolve(),
64
+ module_paths=module_paths,
98
65
  )
99
66
 
100
67
 
@@ -136,32 +103,30 @@ def get_app_name(*, mod_data: ModuleData, app_name: Union[str, None] = None) ->
136
103
  raise FastAPICLIException("Could not find FastAPI app in module, try using --app")
137
104
 
138
105
 
139
- def get_import_string(
106
+ @dataclass
107
+ class ImportData:
108
+ app_name: str
109
+ module_data: ModuleData
110
+ import_string: str
111
+
112
+
113
+ def get_import_data(
140
114
  *, path: Union[Path, None] = None, app_name: Union[str, None] = None
141
- ) -> str:
115
+ ) -> ImportData:
142
116
  if not path:
143
117
  path = get_default_path()
144
- logger.info(f"Using path [blue]{path}[/blue]")
145
- logger.info(f"Resolved absolute path {path.resolve()}")
118
+
119
+ logger.debug(f"Using path [blue]{path}[/blue]")
120
+ logger.debug(f"Resolved absolute path {path.resolve()}")
121
+
146
122
  if not path.exists():
147
123
  raise FastAPICLIException(f"Path does not exist {path}")
148
124
  mod_data = get_module_data_from_path(path)
149
125
  sys.path.insert(0, str(mod_data.extra_sys_path))
150
126
  use_app_name = get_app_name(mod_data=mod_data, app_name=app_name)
151
- import_example = Syntax(
152
- f"from {mod_data.module_import_str} import {use_app_name}", "python"
153
- )
154
- import_panel = Padding(
155
- Panel(
156
- import_example,
157
- title="[b green]Importable FastAPI app[/b green]",
158
- expand=False,
159
- padding=(1, 2),
160
- ),
161
- 1,
162
- )
163
- logger.info("Found importable FastAPI app")
164
- print(import_panel)
127
+
165
128
  import_string = f"{mod_data.module_import_str}:{use_app_name}"
166
- logger.info(f"Using import string [b green]{import_string}[/b green]")
167
- return import_string
129
+
130
+ return ImportData(
131
+ app_name=use_app_name, module_data=mod_data, import_string=import_string
132
+ )
@@ -5,7 +5,9 @@ from rich.console import Console
5
5
  from rich.logging import RichHandler
6
6
 
7
7
 
8
- def setup_logging(terminal_width: Union[int, None] = None) -> None:
8
+ def setup_logging(
9
+ terminal_width: Union[int, None] = None, level: int = logging.INFO
10
+ ) -> None:
9
11
  logger = logging.getLogger("fastapi_cli")
10
12
  console = Console(width=terminal_width) if terminal_width else None
11
13
  rich_handler = RichHandler(
@@ -19,5 +21,5 @@ def setup_logging(terminal_width: Union[int, None] = None) -> None:
19
21
  rich_handler.setFormatter(logging.Formatter("%(message)s"))
20
22
  logger.addHandler(rich_handler)
21
23
 
22
- logger.setLevel(logging.INFO)
24
+ logger.setLevel(level)
23
25
  logger.propagate = False