fastapi-cli 0.0.30__tar.gz → 0.0.31__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 (76) hide show
  1. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/PKG-INFO +1 -1
  2. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/pyproject.toml +1 -1
  3. fastapi_cli-0.0.31/src/fastapi_cli/__init__.py +1 -0
  4. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/src/fastapi_cli/cli.py +63 -36
  5. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/test_cli.py +90 -77
  6. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/test_cli_pyproject.py +7 -7
  7. fastapi_cli-0.0.30/src/fastapi_cli/__init__.py +0 -1
  8. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/LICENSE +0 -0
  9. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/README.md +0 -0
  10. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/scripts/add_latest_release_date.py +0 -0
  11. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/scripts/format.sh +0 -0
  12. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/scripts/lint.sh +0 -0
  13. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/scripts/prepare_release.py +0 -0
  14. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/scripts/test-cov-html.sh +0 -0
  15. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/scripts/test.sh +0 -0
  16. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/src/fastapi_cli/__main__.py +0 -0
  17. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/src/fastapi_cli/config.py +0 -0
  18. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/src/fastapi_cli/discover.py +0 -0
  19. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/src/fastapi_cli/exceptions.py +0 -0
  20. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/src/fastapi_cli/logging.py +0 -0
  21. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/src/fastapi_cli/py.typed +0 -0
  22. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/src/fastapi_cli/utils/__init__.py +0 -0
  23. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/src/fastapi_cli/utils/cli.py +0 -0
  24. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/__init__.py +0 -0
  25. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/broken_package/mod/__init__.py +0 -0
  26. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/broken_package/mod/app.py +0 -0
  27. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/broken_package/utils.py +0 -0
  28. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_api/api.py +0 -0
  29. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_app/api.py +0 -0
  30. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_app/app.py +0 -0
  31. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_app_dir_api/app/__init__.py +0 -0
  32. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_app_dir_api/app/api.py +0 -0
  33. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_app_dir_app/app/__init__.py +0 -0
  34. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_app_dir_app/app/api.py +0 -0
  35. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_app_dir_app/app/app.py +0 -0
  36. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_app_dir_main/app/__init__.py +0 -0
  37. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_app_dir_main/app/api.py +0 -0
  38. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_app_dir_main/app/app.py +0 -0
  39. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_app_dir_main/app/main.py +0 -0
  40. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_app_dir_non_default/app/__init__.py +0 -0
  41. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_app_dir_non_default/app/nondefault.py +0 -0
  42. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_main/api.py +0 -0
  43. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_main/app.py +0 -0
  44. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/default_main/main.py +0 -0
  45. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/default_files/non_default/nonstandard.py +0 -0
  46. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/nested_package/__init__.py +0 -0
  47. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/nested_package/package/__init__.py +0 -0
  48. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/nested_package/package/core/__init__.py +0 -0
  49. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/nested_package/package/core/utils.py +0 -0
  50. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/nested_package/package/mod/__init__.py +0 -0
  51. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/nested_package/package/mod/api.py +0 -0
  52. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/nested_package/package/mod/app.py +0 -0
  53. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/nested_package/package/mod/other.py +0 -0
  54. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/package/__init__.py +0 -0
  55. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/package/core/__init__.py +0 -0
  56. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/package/core/utils.py +0 -0
  57. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/package/mod/__init__.py +0 -0
  58. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/package/mod/api.py +0 -0
  59. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/package/mod/app.py +0 -0
  60. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/package/mod/other.py +0 -0
  61. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/pyproject_config/another_module.py +0 -0
  62. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/pyproject_config/my_module.py +0 -0
  63. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/pyproject_config/pyproject.toml +0 -0
  64. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/single_file_api.py +0 -0
  65. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/single_file_app.py +0 -0
  66. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/assets/single_file_other.py +0 -0
  67. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/conftest.py +0 -0
  68. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/test_discover.py +0 -0
  69. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/test_prepare_release.py +0 -0
  70. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/test_requirements.py +0 -0
  71. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/test_utils_cli.py +0 -0
  72. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/test_utils_default_dir.py +0 -0
  73. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/test_utils_default_file.py +0 -0
  74. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/test_utils_package.py +0 -0
  75. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/test_utils_single_file.py +0 -0
  76. {fastapi_cli-0.0.30 → fastapi_cli-0.0.31}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-cli
3
- Version: 0.0.30
3
+ Version: 0.0.31
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-Expression: MIT
@@ -38,7 +38,7 @@ dependencies = [
38
38
  "rich-toolkit >= 0.14.8",
39
39
  "tomli >= 2.0.0; python_version < '3.11'",
40
40
  ]
41
- version = "0.0.30"
41
+ version = "0.0.31"
42
42
 
43
43
  [project.optional-dependencies]
44
44
  standard = [
@@ -0,0 +1 @@
1
+ __version__ = "0.0.31"
@@ -142,17 +142,19 @@ def _run(
142
142
  proxy_headers: bool = False,
143
143
  forwarded_allow_ips: str | None = None,
144
144
  public_url: str | None = None,
145
+ verbose: bool = False,
145
146
  ) -> None:
146
147
  with get_rich_toolkit() as toolkit:
147
148
  server_type = "development" if command == "dev" else "production"
148
149
 
149
150
  toolkit.print(f"Starting FastAPI in {server_type} mode", emoji="⚡️")
150
151
 
151
- toolkit.print_line()
152
- toolkit.print(
153
- "Searching for package file structure from directories with [blue]__init__.py[/blue] files",
154
- emoji="🔎",
155
- )
152
+ if verbose:
153
+ toolkit.print_line()
154
+ toolkit.print(
155
+ "Searching for package file structure from directories with [blue]__init__.py[/blue] files",
156
+ emoji="🔎",
157
+ )
156
158
 
157
159
  if entrypoint and (path or app):
158
160
  toolkit.print_line()
@@ -194,40 +196,54 @@ def _run(
194
196
 
195
197
  module_data = import_data.module_data
196
198
  import_string = import_data.import_string
199
+ is_auto_discovery = import_data.module_config_source == "auto-discovery"
197
200
 
198
- toolkit.print_line()
199
- toolkit.print(f"Importing from {module_data.extra_sys_path}", emoji="📂")
200
- toolkit.print_line()
201
+ if verbose:
202
+ toolkit.print_line()
203
+ toolkit.print(f"Importing from {module_data.extra_sys_path}", emoji="📂")
204
+ toolkit.print_line()
201
205
 
202
- if module_data.module_paths:
203
- root_tree = _get_module_tree(module_data.module_paths)
206
+ if module_data.module_paths:
207
+ root_tree = _get_module_tree(module_data.module_paths)
204
208
 
205
- toolkit.print(root_tree)
209
+ toolkit.print(root_tree)
206
210
 
207
- toolkit.print_line()
211
+ toolkit.print_line()
212
+
213
+ toolkit.print(
214
+ "Importing the FastAPI app object from the module with the following code:",
215
+ )
216
+ toolkit.print_line()
217
+ toolkit.print(
218
+ f"[blue]from [bold]{module_data.module_import_str}[/bold] import [bold]{import_data.app_name}[/bold]",
219
+ )
220
+
221
+ # Outside --verbose, fold the resolution source into the import string
222
+ # line and point newcomers at --verbose for the full explanation
223
+ if is_auto_discovery and not verbose:
224
+ app_note = " [dim](auto-discovered, use --verbose to learn more)[/]"
225
+ else:
226
+ app_note = ""
208
227
 
209
- toolkit.print(
210
- "Importing the FastAPI app object from the module with the following code:",
211
- )
212
228
  toolkit.print_line()
213
229
  toolkit.print(
214
- f"[blue]from [bold]{module_data.module_import_str}[/bold] import [bold]{import_data.app_name}[/bold]",
230
+ f"Using import string: [blue]{import_string}[/]{app_note}", emoji="🐍"
215
231
  )
216
232
 
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}")
233
+ if verbose:
234
+ toolkit.print_line()
235
+ mod_source_desc = SOURCE_DESCRIPTIONS[import_data.module_config_source]
236
+ app_source_desc = SOURCE_DESCRIPTIONS[import_data.app_name_config_source]
237
+ toolkit.print("Configuration sources:", emoji="📋")
238
+ if mod_source_desc == app_source_desc:
239
+ toolkit.print(f"• Import string: {mod_source_desc}")
240
+ else:
241
+ toolkit.print(f"• Module: {mod_source_desc}")
242
+ toolkit.print(f"• App name: {app_source_desc}")
229
243
 
230
- if import_data.module_config_source == "auto-discovery":
244
+ # Nudge to pin the entrypoint whenever it was auto-discovered, so it's
245
+ # explicit next time — shown in the default output, not just --verbose
246
+ if is_auto_discovery:
231
247
  toolkit.print_line()
232
248
  toolkit.print(
233
249
  "You can configure an entrypoint in [blue]pyproject.toml[/] for this app with:",
@@ -252,13 +268,6 @@ def _run(
252
268
  toolkit.print(f"Server started at [link={url}]{url}[/]", emoji="🌐")
253
269
  toolkit.print(f"Documentation at [link={url_docs}]{url_docs}[/]")
254
270
 
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
-
262
271
  if not uvicorn:
263
272
  raise FastAPICLIException(
264
273
  "Could not import Uvicorn, try running 'pip install uvicorn'"
@@ -356,6 +365,14 @@ def dev(
356
365
  help="Comma separated list of IP Addresses to trust with proxy headers. The literal '*' means trust everything."
357
366
  ),
358
367
  ] = None,
368
+ verbose: Annotated[
369
+ bool,
370
+ typer.Option(
371
+ "--verbose",
372
+ "-v",
373
+ help="Show detailed startup output and debug logs: how the [bold]FastAPI[/bold] app was discovered, imported, and configured.",
374
+ ),
375
+ ] = False,
359
376
  ) -> Any:
360
377
  """
361
378
  Run a [bold]FastAPI[/bold] app in [yellow]development[/yellow] mode. 🧪
@@ -395,6 +412,7 @@ def dev(
395
412
  proxy_headers=proxy_headers,
396
413
  forwarded_allow_ips=forwarded_allow_ips,
397
414
  public_url=os.getenv("FASTAPI_PUBLIC_URL"),
415
+ verbose=verbose,
398
416
  )
399
417
 
400
418
 
@@ -464,6 +482,14 @@ def run(
464
482
  help="Comma separated list of IP Addresses to trust with proxy headers. The literal '*' means trust everything."
465
483
  ),
466
484
  ] = None,
485
+ verbose: Annotated[
486
+ bool,
487
+ typer.Option(
488
+ "--verbose",
489
+ "-v",
490
+ help="Show detailed startup output and debug logs: how the [bold]FastAPI[/bold] app was discovered, imported, and configured.",
491
+ ),
492
+ ] = False,
467
493
  ) -> Any:
468
494
  """
469
495
  Run a [bold]FastAPI[/bold] app in [green]production[/green] mode. 🚀
@@ -503,6 +529,7 @@ def run(
503
529
  proxy_headers=proxy_headers,
504
530
  forwarded_allow_ips=forwarded_allow_ips,
505
531
  public_url=os.getenv("FASTAPI_PUBLIC_URL"),
532
+ verbose=verbose,
506
533
  )
507
534
 
508
535
 
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import subprocess
2
3
  import sys
3
4
  from pathlib import Path
@@ -40,20 +41,16 @@ def test_dev() -> None:
40
41
  "forwarded_allow_ips": None,
41
42
  "log_config": get_uvicorn_log_config(),
42
43
  }
43
- assert "Using import string: single_file_app:app" in result.output
44
- assert "Configuration sources:" in result.output
45
- assert "Module: path CLI argument" in result.output
46
- assert "App name: auto-discovery" in result.output
47
- assert "You can configure an entrypoint in pyproject.toml" not in result.output
48
44
  assert "Starting FastAPI in development mode" in result.output
45
+ assert "Using import string: single_file_app:app" in result.output
49
46
  assert "Server started at http://127.0.0.1:8000" in result.output
50
47
  assert "Documentation at http://127.0.0.1:8000/docs" in result.output
51
- assert (
52
- "Running in development mode, for production use: fastapi run"
53
- in result.output
54
- )
55
-
56
- assert "🐍 single_file_app.py" in result.output
48
+ # a passed path is explicit, so no source hint and no pyproject nudge
49
+ assert "(auto-discovered" not in result.output
50
+ assert "You can configure an entrypoint in pyproject.toml" not in result.output
51
+ # the step-by-step narration is --verbose only
52
+ assert "Searching for package file structure" not in result.output
53
+ assert "Configuration sources:" not in result.output
57
54
 
58
55
 
59
56
  def test_run_uses_uvicorn_default_log_config_without_rich_logs(
@@ -83,12 +80,16 @@ def test_dev_no_args_auto_discovery() -> None:
83
80
  assert mock_run.call_args.kwargs["host"] == "127.0.0.1"
84
81
  assert mock_run.call_args.kwargs["port"] == 8000
85
82
  assert mock_run.call_args.kwargs["reload"] is True
83
+ assert "Starting FastAPI in development mode" in result.output
86
84
  assert "Using import string: main:app" in result.output
87
- assert "Configuration sources:" in result.output
88
- assert "Import string: auto-discovery" in result.output
85
+ # Rich can wrap the rest of the hint based on terminal and emoji width
86
+ assert "(auto-discovered, use --verbose" in result.output
87
+ # auto-discovery nudges you to pin the entrypoint
89
88
  assert "You can configure an entrypoint in pyproject.toml" in result.output
90
89
  assert "[tool.fastapi]" in result.output
91
90
  assert 'entrypoint = "main:app"' in result.output
91
+ # the full breakdown stays behind --verbose
92
+ assert "Configuration sources:" not in result.output
92
93
 
93
94
 
94
95
  def test_dev_package() -> None:
@@ -110,22 +111,14 @@ def test_dev_package() -> None:
110
111
  "forwarded_allow_ips": None,
111
112
  "log_config": get_uvicorn_log_config(),
112
113
  }
113
- assert "Using import string: nested_package.package:app" in result.output
114
- assert "Module: path CLI argument" in result.output
115
- assert "App name: auto-discovery" in result.output
116
- assert "You can configure an entrypoint in pyproject.toml" not in result.output
117
114
  assert "Starting FastAPI in development mode" in result.output
115
+ assert "Using import string: nested_package.package:app" in result.output
118
116
  assert "Server started at http://127.0.0.1:8000" in result.output
119
117
  assert "Documentation at http://127.0.0.1:8000/docs" in result.output
120
- assert (
121
- "Running in development mode, for production use: fastapi run"
122
- in result.output
123
- )
124
-
125
- assert "📁 package" in result.output
126
- assert "└── 🐍 __init__.py" in result.output
127
- assert "└── 📁 package" in result.output
128
- assert " └── 🐍 __init__.py" in result.output
118
+ assert "(auto-discovered" not in result.output
119
+ assert "You can configure an entrypoint in pyproject.toml" not in result.output
120
+ # the file tree is --verbose only
121
+ assert "📁 package" not in result.output
129
122
 
130
123
 
131
124
  def test_dev_args() -> None:
@@ -163,17 +156,12 @@ def test_dev_args() -> None:
163
156
  "forwarded_allow_ips": None,
164
157
  "log_config": get_uvicorn_log_config(),
165
158
  }
166
- assert "Using import string: single_file_app:api" in result.output
167
- assert "Module: path CLI argument" in result.output
168
- assert "App name: --app CLI option" in result.output
169
- assert "You can configure an entrypoint in pyproject.toml" not in result.output
170
159
  assert "Starting FastAPI in development mode" in result.output
160
+ assert "Using import string: single_file_app:api" in result.output
171
161
  assert "Server started at http://192.168.0.2:8080" in result.output
172
162
  assert "Documentation at http://192.168.0.2:8080/docs" in result.output
173
- assert (
174
- "Running in development mode, for production use: fastapi run"
175
- in result.output
176
- )
163
+ assert "(auto-discovered" not in result.output
164
+ assert "You can configure an entrypoint in pyproject.toml" not in result.output
177
165
 
178
166
 
179
167
  def test_dev_env_vars() -> None:
@@ -197,17 +185,10 @@ def test_dev_env_vars() -> None:
197
185
  "forwarded_allow_ips": None,
198
186
  "log_config": get_uvicorn_log_config(),
199
187
  }
200
- assert "Using import string: single_file_app:app" in result.output
201
- assert "Module: path CLI argument" in result.output
202
- assert "App name: auto-discovery" in result.output
203
- assert "You can configure an entrypoint in pyproject.toml" not in result.output
204
188
  assert "Starting FastAPI in development mode" in result.output
189
+ assert "Using import string: single_file_app:app" in result.output
205
190
  assert "Server started at http://127.0.0.1:8111" in result.output
206
191
  assert "Documentation at http://127.0.0.1:8111/docs" in result.output
207
- assert (
208
- "Running in development mode, for production use: fastapi run"
209
- in result.output
210
- )
211
192
 
212
193
 
213
194
  def test_dev_env_vars_and_args() -> None:
@@ -238,17 +219,68 @@ def test_dev_env_vars_and_args() -> None:
238
219
  "forwarded_allow_ips": None,
239
220
  "log_config": get_uvicorn_log_config(),
240
221
  }
241
- assert "Using import string: single_file_app:app" in result.output
242
- assert "Module: path CLI argument" in result.output
243
- assert "App name: auto-discovery" in result.output
244
- assert "You can configure an entrypoint in pyproject.toml" not in result.output
245
222
  assert "Starting FastAPI in development mode" in result.output
223
+ assert "Using import string: single_file_app:app" in result.output
246
224
  assert "Server started at http://127.0.0.1:8080" in result.output
247
225
  assert "Documentation at http://127.0.0.1:8080/docs" in result.output
248
- assert (
249
- "Running in development mode, for production use: fastapi run"
250
- in result.output
251
- )
226
+
227
+
228
+ def test_dev_verbose() -> None:
229
+ """--verbose restores the full narration and the module file tree."""
230
+ with changing_dir(assets_path):
231
+ with patch.object(uvicorn, "run") as mock_run:
232
+ result = runner.invoke(app, ["dev", "nested_package/package", "--verbose"])
233
+ assert result.exit_code == 0, result.output
234
+ assert mock_run.called
235
+ assert "Starting FastAPI in development mode" in result.output
236
+ assert "Searching for package file structure" in result.output
237
+ assert "Importing from" in result.output
238
+ assert "Importing the FastAPI app object" in result.output
239
+ assert "from nested_package.package import app" in result.output
240
+ assert "Using import string: nested_package.package:app" in result.output
241
+ # module and app come from different sources here
242
+ assert "Configuration sources:" in result.output
243
+ assert "Module: path CLI argument" in result.output
244
+ assert "App name: auto-discovery" in result.output
245
+ # the file tree
246
+ assert "📁 package" in result.output
247
+ assert "🐍 __init__.py" in result.output
248
+ # the source hint is folded away in verbose mode
249
+ assert "(auto-discovered, use --verbose to learn more)" not in result.output
250
+
251
+
252
+ def test_dev_verbose_auto_discovery() -> None:
253
+ """--verbose with auto-discovery shows the single 'import string' source."""
254
+ with changing_dir(assets_path / "default_files" / "default_main"):
255
+ with patch.object(uvicorn, "run") as mock_run:
256
+ result = runner.invoke(app, ["dev", "--verbose"])
257
+ assert result.exit_code == 0, result.output
258
+ assert mock_run.called
259
+ assert "Searching for package file structure" in result.output
260
+ assert "Importing the FastAPI app object" in result.output
261
+ assert "Using import string: main:app" in result.output
262
+ assert "Configuration sources:" in result.output
263
+ # module and app share the same source (auto-discovery)
264
+ assert "Import string: auto-discovery" in result.output
265
+ # auto-discovery still nudges to pin the entrypoint
266
+ assert "You can configure an entrypoint in pyproject.toml" in result.output
267
+
268
+
269
+ def test_global_verbose_enables_debug_logging() -> None:
270
+ with changing_dir(assets_path):
271
+ with patch.object(uvicorn, "run"):
272
+ result = runner.invoke(app, ["--verbose", "dev", "single_file_app.py"])
273
+ assert result.exit_code == 0, result.output
274
+
275
+ assert logging.getLogger("fastapi_cli").level == logging.DEBUG
276
+
277
+ # a run without --verbose resets the level
278
+ with changing_dir(assets_path):
279
+ with patch.object(uvicorn, "run"):
280
+ result = runner.invoke(app, ["dev", "single_file_app.py"])
281
+ assert result.exit_code == 0, result.output
282
+
283
+ assert logging.getLogger("fastapi_cli").level == logging.INFO
252
284
 
253
285
 
254
286
  def test_entrypoint_mutually_exclusive_with_path() -> None:
@@ -287,11 +319,8 @@ def test_run() -> None:
287
319
  "forwarded_allow_ips": None,
288
320
  "log_config": get_uvicorn_log_config(),
289
321
  }
290
- assert "Using import string: single_file_app:app" in result.output
291
- assert "Module: path CLI argument" in result.output
292
- assert "App name: auto-discovery" in result.output
293
- assert "You can configure an entrypoint in pyproject.toml" not in result.output
294
322
  assert "Starting FastAPI in production mode" in result.output
323
+ assert "Using import string: single_file_app:app" in result.output
295
324
  assert "Server started at http://0.0.0.0:8000" in result.output
296
325
  assert "Documentation at http://0.0.0.0:8000/docs" in result.output
297
326
 
@@ -317,14 +346,10 @@ def test_run_trust_proxy() -> None:
317
346
  "forwarded_allow_ips": "*",
318
347
  "log_config": get_uvicorn_log_config(),
319
348
  }
320
- assert "Using import string: single_file_app:app" in result.output
321
349
  assert "Starting FastAPI in production mode" in result.output
350
+ assert "Using import string: single_file_app:app" in result.output
322
351
  assert "Server started at http://0.0.0.0:8000" in result.output
323
352
  assert "Documentation at http://0.0.0.0:8000/docs" in result.output
324
- assert (
325
- "Running in development mode, for production use: fastapi run"
326
- not in result.output
327
- )
328
353
 
329
354
 
330
355
  def test_run_args() -> None:
@@ -365,17 +390,10 @@ def test_run_args() -> None:
365
390
  "log_config": get_uvicorn_log_config(),
366
391
  }
367
392
 
368
- assert "Using import string: single_file_app:api" in result.output
369
- assert "Module: path CLI argument" in result.output
370
- assert "App name: --app CLI option" in result.output
371
- assert "You can configure an entrypoint in pyproject.toml" not in result.output
372
393
  assert "Starting FastAPI in production mode" in result.output
394
+ assert "Using import string: single_file_app:api" in result.output
373
395
  assert "Server started at http://192.168.0.2:8080" in result.output
374
396
  assert "Documentation at http://192.168.0.2:8080/docs" in result.output
375
- assert (
376
- "Running in development mode, for production use: fastapi run"
377
- not in result.output
378
- )
379
397
 
380
398
 
381
399
  def test_run_env_vars() -> None:
@@ -399,11 +417,8 @@ def test_run_env_vars() -> None:
399
417
  "forwarded_allow_ips": None,
400
418
  "log_config": get_uvicorn_log_config(),
401
419
  }
402
- assert "Using import string: single_file_app:app" in result.output
403
- assert "Module: path CLI argument" in result.output
404
- assert "App name: auto-discovery" in result.output
405
- assert "You can configure an entrypoint in pyproject.toml" not in result.output
406
420
  assert "Starting FastAPI in production mode" in result.output
421
+ assert "Using import string: single_file_app:app" in result.output
407
422
  assert "Server started at http://0.0.0.0:8111" in result.output
408
423
  assert "Documentation at http://0.0.0.0:8111/docs" in result.output
409
424
 
@@ -436,11 +451,8 @@ def test_run_env_vars_and_args() -> None:
436
451
  "forwarded_allow_ips": None,
437
452
  "log_config": get_uvicorn_log_config(),
438
453
  }
439
- assert "Using import string: single_file_app:app" in result.output
440
- assert "Module: path CLI argument" in result.output
441
- assert "App name: auto-discovery" in result.output
442
- assert "You can configure an entrypoint in pyproject.toml" not in result.output
443
454
  assert "Starting FastAPI in production mode" in result.output
455
+ assert "Using import string: single_file_app:app" in result.output
444
456
  assert "Server started at http://0.0.0.0:8080" in result.output
445
457
  assert "Documentation at http://0.0.0.0:8080/docs" in result.output
446
458
 
@@ -601,7 +613,8 @@ def test_dev_with_import_string() -> None:
601
613
  "log_config": get_uvicorn_log_config(),
602
614
  }
603
615
  assert "Using import string: single_file_app:api" in result.output
604
- assert "Import string: --entrypoint CLI option" in result.output
616
+ # an explicit entrypoint is not auto-discovery
617
+ assert "(auto-discovered" not in result.output
605
618
  assert "You can configure an entrypoint in pyproject.toml" not in result.output
606
619
 
607
620
 
@@ -625,7 +638,7 @@ def test_run_with_import_string() -> None:
625
638
  "log_config": get_uvicorn_log_config(),
626
639
  }
627
640
  assert "Using import string: single_file_app:app" in result.output
628
- assert "Import string: --entrypoint CLI option" in result.output
641
+ assert "(auto-discovered" not in result.output
629
642
  assert "You can configure an entrypoint in pyproject.toml" not in result.output
630
643
 
631
644
 
@@ -26,8 +26,9 @@ def test_dev_with_pyproject_app_config_uses() -> None:
26
26
  assert mock_run.call_args.kwargs["reload"] is True
27
27
 
28
28
  assert "Using import string: my_module:app" in result.output
29
- assert "Configuration sources:" in result.output
30
- assert "Import string: entrypoint in pyproject.toml" in result.output
29
+ # a configured entrypoint is not auto-discovery
30
+ assert "(auto-discovered" not in result.output
31
+ assert "Configuration sources:" not in result.output
31
32
 
32
33
 
33
34
  def test_run_with_pyproject_app_config() -> None:
@@ -44,8 +45,8 @@ def test_run_with_pyproject_app_config() -> None:
44
45
  assert mock_run.call_args.kwargs["reload"] is False
45
46
 
46
47
  assert "Using import string: my_module:app" in result.output
47
- assert "Configuration sources:" in result.output
48
- assert "Import string: entrypoint in pyproject.toml" in result.output
48
+ assert "(auto-discovered" not in result.output
49
+ assert "Configuration sources:" not in result.output
49
50
 
50
51
 
51
52
  def test_cli_arg_overrides_pyproject_config() -> None:
@@ -58,9 +59,8 @@ def test_cli_arg_overrides_pyproject_config() -> None:
58
59
  assert result.exit_code == 0, result.output
59
60
 
60
61
  assert mock_run.call_args.kwargs["app"] == "another_module:app"
61
- assert "Configuration sources:" in result.output
62
- assert "Module: path CLI argument" in result.output
63
- assert "App name: auto-discovery" in result.output
62
+ assert "Using import string: another_module:app" in result.output
63
+ assert "Configuration sources:" not in result.output
64
64
 
65
65
 
66
66
  def test_pyproject_app_config_invalid_format() -> None:
@@ -1 +0,0 @@
1
- __version__ = "0.0.30"
File without changes
File without changes