Skip to content

Commit 5b79d6d

Browse files
committed
✨ Trim default startup output and add --verbose to dev and run
Shortcake-Parent: 2026-07-09-redesign-startup-output-style
1 parent eada899 commit 5b79d6d

3 files changed

Lines changed: 143 additions & 124 deletions

File tree

src/fastapi_cli/cli.py

Lines changed: 64 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -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

109106
def _get_module_tree(module_paths: list[Path]) -> Tree:
@@ -142,17 +139,19 @@ 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:
146144
with get_rich_toolkit() as toolkit:
147145
server_type = "development" if command == "dev" else "production"
148146

149147
toolkit.print(f"Starting FastAPI in {server_type} mode", emoji="⚡️")
150148

151-
toolkit.print_line()
152-
toolkit.print(
153-
"Searching for package file structure from directories with [blue]__init__.py[/blue] files",
154-
emoji="🔎",
155-
)
149+
if verbose:
150+
toolkit.print_line()
151+
toolkit.print(
152+
"Searching for package file structure from directories with [blue]__init__.py[/blue] files",
153+
emoji="🔎",
154+
)
156155

157156
if entrypoint and (path or app):
158157
toolkit.print_line()
@@ -194,40 +193,54 @@ def _run(
194193

195194
module_data = import_data.module_data
196195
import_string = import_data.import_string
196+
is_auto_discovery = import_data.module_config_source == "auto-discovery"
197197

198-
toolkit.print_line()
199-
toolkit.print(f"Importing from {module_data.extra_sys_path}", emoji="📂")
200-
toolkit.print_line()
198+
if verbose:
199+
toolkit.print_line()
200+
toolkit.print(f"Importing from {module_data.extra_sys_path}", emoji="📂")
201+
toolkit.print_line()
201202

202-
if module_data.module_paths:
203-
root_tree = _get_module_tree(module_data.module_paths)
203+
if module_data.module_paths:
204+
root_tree = _get_module_tree(module_data.module_paths)
204205

205-
toolkit.print(root_tree)
206+
toolkit.print(root_tree)
206207

207-
toolkit.print_line()
208+
toolkit.print_line()
209+
210+
toolkit.print(
211+
"Importing the FastAPI app object from the module with the following code:",
212+
)
213+
toolkit.print_line()
214+
toolkit.print(
215+
f"[blue]from [bold]{module_data.module_import_str}[/bold] import [bold]{import_data.app_name}[/bold]",
216+
)
217+
218+
# Outside --verbose, fold the resolution source into the import string
219+
# line and point newcomers at --verbose for the full explanation
220+
if is_auto_discovery and not verbose:
221+
app_note = " [dim](auto-discovered, use --verbose to learn more)[/]"
222+
else:
223+
app_note = ""
208224

209-
toolkit.print(
210-
"Importing the FastAPI app object from the module with the following code:",
211-
)
212225
toolkit.print_line()
213226
toolkit.print(
214-
f"[blue]from [bold]{module_data.module_import_str}[/bold] import [bold]{import_data.app_name}[/bold]",
227+
f"Using import string: [blue]{import_string}[/]{app_note}", emoji="🐍"
215228
)
216229

217-
toolkit.print_line()
218-
toolkit.print(f"Using import string: [blue]{import_string}[/]", emoji="🐍")
219-
220-
toolkit.print_line()
221-
mod_source_desc = SOURCE_DESCRIPTIONS[import_data.module_config_source]
222-
app_source_desc = SOURCE_DESCRIPTIONS[import_data.app_name_config_source]
223-
toolkit.print("Configuration sources:", emoji="📋")
224-
if mod_source_desc == app_source_desc:
225-
toolkit.print(f"• Import string: {mod_source_desc}")
226-
else:
227-
toolkit.print(f"• Module: {mod_source_desc}")
228-
toolkit.print(f"• App name: {app_source_desc}")
230+
if verbose:
231+
toolkit.print_line()
232+
mod_source_desc = SOURCE_DESCRIPTIONS[import_data.module_config_source]
233+
app_source_desc = SOURCE_DESCRIPTIONS[import_data.app_name_config_source]
234+
toolkit.print("Configuration sources:", emoji="📋")
235+
if mod_source_desc == app_source_desc:
236+
toolkit.print(f"• Import string: {mod_source_desc}")
237+
else:
238+
toolkit.print(f"• Module: {mod_source_desc}")
239+
toolkit.print(f"• App name: {app_source_desc}")
229240

230-
if import_data.module_config_source == "auto-discovery":
241+
# Nudge to pin the entrypoint whenever it was auto-discovered, so it's
242+
# explicit next time — shown in the default output, not just --verbose
243+
if is_auto_discovery:
231244
toolkit.print_line()
232245
toolkit.print(
233246
"You can configure an entrypoint in [blue]pyproject.toml[/] for this app with:",
@@ -252,13 +265,6 @@ def _run(
252265
toolkit.print(f"Server started at [link={url}]{url}[/]", emoji="🌐")
253266
toolkit.print(f"Documentation at [link={url_docs}]{url_docs}[/]")
254267

255-
if command == "dev":
256-
toolkit.print_line()
257-
toolkit.print(
258-
"Running in development mode, for production use: [bold]fastapi run[/]",
259-
emoji="💡",
260-
)
261-
262268
if not uvicorn:
263269
raise FastAPICLIException(
264270
"Could not import Uvicorn, try running 'pip install uvicorn'"
@@ -356,6 +362,14 @@ def dev(
356362
help="Comma separated list of IP Addresses to trust with proxy headers. The literal '*' means trust everything."
357363
),
358364
] = None,
365+
verbose: Annotated[
366+
bool,
367+
typer.Option(
368+
"--verbose",
369+
"-v",
370+
help="Show detailed startup output: how the [bold]FastAPI[/bold] app was discovered, imported, and configured.",
371+
),
372+
] = False,
359373
) -> Any:
360374
"""
361375
Run a [bold]FastAPI[/bold] app in [yellow]development[/yellow] mode. 🧪
@@ -395,6 +409,7 @@ def dev(
395409
proxy_headers=proxy_headers,
396410
forwarded_allow_ips=forwarded_allow_ips,
397411
public_url=os.getenv("FASTAPI_PUBLIC_URL"),
412+
verbose=verbose,
398413
)
399414

400415

@@ -464,6 +479,14 @@ def run(
464479
help="Comma separated list of IP Addresses to trust with proxy headers. The literal '*' means trust everything."
465480
),
466481
] = None,
482+
verbose: Annotated[
483+
bool,
484+
typer.Option(
485+
"--verbose",
486+
"-v",
487+
help="Show detailed startup output: how the [bold]FastAPI[/bold] app was discovered, imported, and configured.",
488+
),
489+
] = False,
467490
) -> Any:
468491
"""
469492
Run a [bold]FastAPI[/bold] app in [green]production[/green] mode. 🚀
@@ -503,6 +526,7 @@ def run(
503526
proxy_headers=proxy_headers,
504527
forwarded_allow_ips=forwarded_allow_ips,
505528
public_url=os.getenv("FASTAPI_PUBLIC_URL"),
529+
verbose=verbose,
506530
)
507531

508532

0 commit comments

Comments
 (0)