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,6 +39,7 @@ 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. 😎
@@ -48,6 +49,31 @@ def callback(
48
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,45 +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
- url = f"http://{ host } :{ port } "
70
- url_docs = f"{ url } /docs"
71
- serving_str = f"[dim]Serving at:[/dim] [link={ url } ]{ url } [/link]\n \n [dim]API docs:[/dim] [link={ url_docs } ]{ url_docs } [/link]"
72
-
73
- if command == "dev" :
74
- panel = Panel (
75
- f"{ serving_str } \n \n [dim]Running in development mode, for production use:[/dim] \n \n [b]fastapi run[/b]" ,
76
- title = "FastAPI CLI - Development mode" ,
77
- expand = False ,
78
- padding = (1 , 2 ),
79
- 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" ,
80
124
)
81
- else :
82
- panel = Panel (
83
- f"{ serving_str } \n \n [dim]Running in production mode, for development use:[/dim] \n \n [b]fastapi dev[/b]" ,
84
- title = "FastAPI CLI - Production mode" ,
85
- expand = False ,
86
- padding = (1 , 2 ),
87
- 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 (),
88
171
)
89
- print (Padding (panel , 1 ))
90
- if not uvicorn :
91
- raise FastAPICLIException (
92
- "Could not import Uvicorn, try running 'pip install uvicorn'"
93
- ) from None
94
- uvicorn .run (
95
- app = use_uvicorn_app ,
96
- host = host ,
97
- port = port ,
98
- reload = reload ,
99
- workers = workers ,
100
- root_path = root_path ,
101
- proxy_headers = proxy_headers ,
102
- )
103
172
104
173
105
174
@app .command ()
0 commit comments