@@ -91,7 +91,6 @@ def callback(
9191 "--version" , help = "Show the version and exit." , callback = version_callback
9292 ),
9393 ] = None ,
94- verbose : bool = typer .Option (False , help = "Enable verbose output" ),
9594) -> None :
9695 """
9796 FastAPI CLI - The [bold]fastapi[/bold] command line app. 😎
@@ -101,9 +100,7 @@ def callback(
101100 Read more in the docs: [link=https://fastapi.tiangolo.com/fastapi-cli/]https://fastapi.tiangolo.com/fastapi-cli/[/link].
102101 """
103102
104- log_level = logging .DEBUG if verbose else logging .INFO
105-
106- setup_logging (level = log_level )
103+ setup_logging ()
107104
108105
109106def _get_module_tree (module_paths : list [Path ]) -> Tree :
@@ -142,23 +139,29 @@ def _run(
142139 proxy_headers : bool = False ,
143140 forwarded_allow_ips : str | None = None ,
144141 public_url : str | None = None ,
142+ verbose : bool = False ,
145143) -> None :
146- with get_rich_toolkit () as toolkit :
144+ use_rich = should_use_rich_logs ()
145+ # The step-by-step "how the app was found and imported" narration is a
146+ # teaching aid; show it only with --verbose so the default output stays lean
147+ show_details = use_rich and verbose
148+ with get_rich_toolkit (use_rich = use_rich ) as toolkit :
147149 server_type = "development" if command == "dev" else "production"
148150
149- toolkit .print_title (f"Starting { server_type } server 🚀" , tag = "FastAPI" )
150- toolkit .print_line ()
151+ toolkit .print (f"Starting FastAPI in { server_type } mode" , emoji = "⚡️" )
151152
152- toolkit .print (
153- "Searching for package file structure from directories with [blue]__init__.py[/blue] files"
154- )
153+ if show_details :
154+ toolkit .print_line ()
155+ toolkit .print (
156+ "Searching for package file structure from directories with [blue]__init__.py[/blue] files" ,
157+ emoji = "🔎" ,
158+ )
155159
156160 if entrypoint and (path or app ):
157161 toolkit .print_line ()
158162 toolkit .print (
159163 "[error]Cannot use --entrypoint together with path or --app arguments"
160164 )
161- toolkit .print_line ()
162165 raise typer .Exit (code = 1 )
163166
164167 try :
@@ -172,8 +175,6 @@ def _run(
172175 field = "." .join (str (loc ) for loc in error ["loc" ])
173176 toolkit .print (f" [red]•[/red] { field } : { error ['msg' ]} " )
174177
175- toolkit .print_line ()
176-
177178 raise typer .Exit (code = 1 ) from None
178179
179180 try :
@@ -196,46 +197,58 @@ def _run(
196197
197198 module_data = import_data .module_data
198199 import_string = import_data .import_string
200+ is_auto_discovery = import_data .module_config_source == "auto-discovery"
199201
200- toolkit .print (f"Importing from { module_data .extra_sys_path } " )
201- toolkit .print_line ()
202+ if show_details :
203+ toolkit .print_line ()
204+ toolkit .print (f"Importing from { module_data .extra_sys_path } " , emoji = "📂" )
205+ toolkit .print_line ()
202206
203- if module_data .module_paths :
204- root_tree = _get_module_tree (module_data .module_paths )
207+ if module_data .module_paths :
208+ root_tree = _get_module_tree (module_data .module_paths )
209+
210+ toolkit .print (root_tree )
205211
206- toolkit .print (root_tree , tag = "module" )
207212 toolkit .print_line ()
208213
209- toolkit .print (
210- "Importing the FastAPI app object from the module with the following code:" ,
211- tag = "code" ,
212- )
213- toolkit .print_line ()
214- toolkit .print (
215- f"[underline]from [bold]{ module_data .module_import_str } [/bold] import [bold]{ import_data .app_name } [/bold]"
216- )
217- toolkit .print_line ()
214+ toolkit .print (
215+ "Importing the FastAPI app object from the module with the following code:" ,
216+ )
217+ toolkit .print_line ()
218+ toolkit .print (
219+ f"[blue]from [bold]{ module_data .module_import_str } [/bold] import [bold]{ import_data .app_name } [/bold]" ,
220+ )
221+
222+ # Outside --verbose, fold the resolution source into the import string
223+ # line and point newcomers at --verbose for the full explanation
224+ if is_auto_discovery and not verbose :
225+ app_note = " [dim](auto-discovered, use --verbose to learn more)[/]"
226+ else :
227+ app_note = ""
218228
229+ toolkit .print_line ()
219230 toolkit .print (
220- f"Using import string: [blue]{ import_string } [/]" ,
221- tag = "app" ,
231+ f"Using import string: [blue]{ import_string } [/]{ app_note } " , emoji = "🐍"
222232 )
223233
224- mod_source_desc = SOURCE_DESCRIPTIONS [import_data .module_config_source ]
225- app_source_desc = SOURCE_DESCRIPTIONS [import_data .app_name_config_source ]
226- toolkit .print_line ()
227- toolkit .print ("Configuration sources:" , tag = "info" )
228- if mod_source_desc == app_source_desc :
229- toolkit .print (f" • Import string: { mod_source_desc } " )
230- else :
231- toolkit .print (f" • Module: { mod_source_desc } " )
232- toolkit .print (f" • App name: { app_source_desc } " )
234+ if show_details :
235+ toolkit .print_line ()
236+ mod_source_desc = SOURCE_DESCRIPTIONS [import_data .module_config_source ]
237+ app_source_desc = SOURCE_DESCRIPTIONS [import_data .app_name_config_source ]
238+ toolkit .print ("Configuration sources:" , emoji = "📋" )
239+ if mod_source_desc == app_source_desc :
240+ toolkit .print (f"• Import string: { mod_source_desc } " )
241+ else :
242+ toolkit .print (f"• Module: { mod_source_desc } " )
243+ toolkit .print (f"• App name: { app_source_desc } " )
233244
234- if import_data .module_config_source == "auto-discovery" :
245+ # Nudge to pin the entrypoint whenever it was auto-discovered, so it's
246+ # explicit next time — shown in the default output, not just --verbose
247+ if use_rich and is_auto_discovery :
235248 toolkit .print_line ()
236249 toolkit .print (
237250 "You can configure an entrypoint in [blue]pyproject.toml[/] for this app with:" ,
238- tag = "tip " ,
251+ emoji = "💡 " ,
239252 )
240253 toolkit .print_line ()
241254 toolkit .print (
@@ -246,37 +259,31 @@ def _run(
246259 ),
247260 "toml" ,
248261 theme = "ansi_light" ,
249- )
262+ ),
250263 )
251264
252265 url = public_url .rstrip ("/" ) if public_url else f"http://{ host } :{ port } "
253266 url_docs = f"{ url } /docs"
254267
255- toolkit .print_line ()
256- toolkit .print (
257- f"Server started at [link={ url } ]{ url } [/]" ,
258- f"Documentation at [link={ url_docs } ]{ url_docs } [/]" ,
259- tag = "server" ,
260- )
261-
262- if command == "dev" :
268+ if use_rich :
263269 toolkit .print_line ()
264- toolkit .print (
265- "Running in development mode, for production use: [bold]fastapi run[/]" ,
266- tag = "tip" ,
267- )
270+ toolkit .print (f"Server started at [link={ url } ]{ url } [/]" , emoji = "🌐" )
271+ toolkit .print (f"Documentation at [link={ url_docs } ]{ url_docs } [/]" )
268272
269273 if not uvicorn :
270274 raise FastAPICLIException (
271275 "Could not import Uvicorn, try running 'pip install uvicorn'"
272276 ) from None
273277
274- toolkit .print_line ()
275- toolkit .print ("Logs:" )
276- toolkit .print_line ()
278+ if use_rich :
279+ toolkit .print_line ()
280+ toolkit .print ("Logs:" , bullet = False )
281+ toolkit .print_line ()
282+ else :
283+ toolkit .print ("" )
277284
278285 extra_uvicorn_kwargs : dict [str , Any ] = (
279- {"log_config" : get_uvicorn_log_config ()} if should_use_rich_logs () else {}
286+ {"log_config" : get_uvicorn_log_config ()} if use_rich else {}
280287 )
281288
282289 uvicorn .run (
@@ -363,6 +370,14 @@ def dev(
363370 help = "Comma separated list of IP Addresses to trust with proxy headers. The literal '*' means trust everything."
364371 ),
365372 ] = None ,
373+ verbose : Annotated [
374+ bool ,
375+ typer .Option (
376+ "--verbose" ,
377+ "-v" ,
378+ help = "Show detailed startup output: how the [bold]FastAPI[/bold] app was discovered, imported, and configured." ,
379+ ),
380+ ] = False ,
366381) -> Any :
367382 """
368383 Run a [bold]FastAPI[/bold] app in [yellow]development[/yellow] mode. 🧪
@@ -402,6 +417,7 @@ def dev(
402417 proxy_headers = proxy_headers ,
403418 forwarded_allow_ips = forwarded_allow_ips ,
404419 public_url = os .getenv ("FASTAPI_PUBLIC_URL" ),
420+ verbose = verbose ,
405421 )
406422
407423
@@ -471,6 +487,14 @@ def run(
471487 help = "Comma separated list of IP Addresses to trust with proxy headers. The literal '*' means trust everything."
472488 ),
473489 ] = None ,
490+ verbose : Annotated [
491+ bool ,
492+ typer .Option (
493+ "--verbose" ,
494+ "-v" ,
495+ help = "Show detailed startup output: how the [bold]FastAPI[/bold] app was discovered, imported, and configured." ,
496+ ),
497+ ] = False ,
474498) -> Any :
475499 """
476500 Run a [bold]FastAPI[/bold] app in [green]production[/green] mode. 🚀
@@ -510,6 +534,7 @@ def run(
510534 proxy_headers = proxy_headers ,
511535 forwarded_allow_ips = forwarded_allow_ips ,
512536 public_url = os .getenv ("FASTAPI_PUBLIC_URL" ),
537+ verbose = verbose ,
513538 )
514539
515540
0 commit comments