fastapi-cli 0.0.30__tar.gz → 0.0.32__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.
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/PKG-INFO +5 -1
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/README.md +4 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/pyproject.toml +1 -1
- fastapi_cli-0.0.32/src/fastapi_cli/__init__.py +1 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/src/fastapi_cli/cli.py +65 -36
- fastapi_cli-0.0.32/tests/assets/environment_app.py +12 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/test_cli.py +137 -77
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/test_cli_pyproject.py +7 -7
- fastapi_cli-0.0.30/src/fastapi_cli/__init__.py +0 -1
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/LICENSE +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/scripts/add_latest_release_date.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/scripts/format.sh +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/scripts/lint.sh +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/scripts/prepare_release.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/scripts/test-cov-html.sh +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/scripts/test.sh +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/src/fastapi_cli/__main__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/src/fastapi_cli/config.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/src/fastapi_cli/discover.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/src/fastapi_cli/exceptions.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/src/fastapi_cli/logging.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/src/fastapi_cli/py.typed +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/src/fastapi_cli/utils/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/src/fastapi_cli/utils/cli.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/broken_package/mod/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/broken_package/mod/app.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/broken_package/utils.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_api/api.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app/api.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app/app.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_api/app/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_api/app/api.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_app/app/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_app/app/api.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_app/app/app.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_main/app/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_main/app/api.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_main/app/app.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_main/app/main.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_non_default/app/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_non_default/app/nondefault.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_main/api.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_main/app.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_main/main.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/non_default/nonstandard.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/nested_package/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/nested_package/package/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/nested_package/package/core/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/nested_package/package/core/utils.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/nested_package/package/mod/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/nested_package/package/mod/api.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/nested_package/package/mod/app.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/nested_package/package/mod/other.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/package/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/package/core/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/package/core/utils.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/package/mod/__init__.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/package/mod/api.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/package/mod/app.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/package/mod/other.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/pyproject_config/another_module.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/pyproject_config/my_module.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/pyproject_config/pyproject.toml +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/single_file_api.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/single_file_app.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/single_file_other.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/conftest.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/test_discover.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/test_prepare_release.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/test_requirements.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/test_utils_cli.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/test_utils_default_dir.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/test_utils_default_file.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/test_utils_package.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/test_utils_single_file.py +0 -0
- {fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.32
|
|
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
|
|
@@ -121,6 +121,10 @@ By default, it will have **auto-reload** enabled, so it will automatically reloa
|
|
|
121
121
|
|
|
122
122
|
By default it will listen on the IP address `127.0.0.1`, which is the IP for your machine to communicate with itself alone (`localhost`).
|
|
123
123
|
|
|
124
|
+
Before importing your app, `fastapi dev` sets the `FASTAPI_ENV` environment variable to `development`. If `FASTAPI_ENV` is already set, its existing value is preserved. This lets app startup code choose development-friendly behavior while allowing you to provide an app-specific environment such as `staging`.
|
|
125
|
+
|
|
126
|
+
The conventional `FASTAPI_ENV` values are `development` and `production`. `fastapi run` currently leaves `FASTAPI_ENV` unchanged, so set it explicitly if your app needs to detect production mode.
|
|
127
|
+
|
|
124
128
|
## `fastapi run`
|
|
125
129
|
|
|
126
130
|
When you run `fastapi run`, it will run on production mode by default.
|
|
@@ -76,6 +76,10 @@ By default, it will have **auto-reload** enabled, so it will automatically reloa
|
|
|
76
76
|
|
|
77
77
|
By default it will listen on the IP address `127.0.0.1`, which is the IP for your machine to communicate with itself alone (`localhost`).
|
|
78
78
|
|
|
79
|
+
Before importing your app, `fastapi dev` sets the `FASTAPI_ENV` environment variable to `development`. If `FASTAPI_ENV` is already set, its existing value is preserved. This lets app startup code choose development-friendly behavior while allowing you to provide an app-specific environment such as `staging`.
|
|
80
|
+
|
|
81
|
+
The conventional `FASTAPI_ENV` values are `development` and `production`. `fastapi run` currently leaves `FASTAPI_ENV` unchanged, so set it explicitly if your app needs to detect production mode.
|
|
82
|
+
|
|
79
83
|
## `fastapi run`
|
|
80
84
|
|
|
81
85
|
When you run `fastapi run`, it will run on production mode by default.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.32"
|
|
@@ -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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
203
|
-
|
|
206
|
+
if module_data.module_paths:
|
|
207
|
+
root_tree = _get_module_tree(module_data.module_paths)
|
|
204
208
|
|
|
205
|
-
|
|
209
|
+
toolkit.print(root_tree)
|
|
206
210
|
|
|
207
|
-
|
|
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]
|
|
230
|
+
f"Using import string: [blue]{import_string}[/]{app_note}", emoji="🐍"
|
|
215
231
|
)
|
|
216
232
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
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. 🧪
|
|
@@ -382,6 +399,8 @@ def dev(
|
|
|
382
399
|
|
|
383
400
|
Otherwise, it uses the first [bold]FastAPI[/bold] app found in the imported module or package.
|
|
384
401
|
"""
|
|
402
|
+
os.environ.setdefault("FASTAPI_ENV", "development")
|
|
403
|
+
|
|
385
404
|
_run(
|
|
386
405
|
path=path,
|
|
387
406
|
host=host,
|
|
@@ -395,6 +414,7 @@ def dev(
|
|
|
395
414
|
proxy_headers=proxy_headers,
|
|
396
415
|
forwarded_allow_ips=forwarded_allow_ips,
|
|
397
416
|
public_url=os.getenv("FASTAPI_PUBLIC_URL"),
|
|
417
|
+
verbose=verbose,
|
|
398
418
|
)
|
|
399
419
|
|
|
400
420
|
|
|
@@ -464,6 +484,14 @@ def run(
|
|
|
464
484
|
help="Comma separated list of IP Addresses to trust with proxy headers. The literal '*' means trust everything."
|
|
465
485
|
),
|
|
466
486
|
] = None,
|
|
487
|
+
verbose: Annotated[
|
|
488
|
+
bool,
|
|
489
|
+
typer.Option(
|
|
490
|
+
"--verbose",
|
|
491
|
+
"-v",
|
|
492
|
+
help="Show detailed startup output and debug logs: how the [bold]FastAPI[/bold] app was discovered, imported, and configured.",
|
|
493
|
+
),
|
|
494
|
+
] = False,
|
|
467
495
|
) -> Any:
|
|
468
496
|
"""
|
|
469
497
|
Run a [bold]FastAPI[/bold] app in [green]production[/green] mode. 🚀
|
|
@@ -503,6 +531,7 @@ def run(
|
|
|
503
531
|
proxy_headers=proxy_headers,
|
|
504
532
|
forwarded_allow_ips=forwarded_allow_ips,
|
|
505
533
|
public_url=os.getenv("FASTAPI_PUBLIC_URL"),
|
|
534
|
+
verbose=verbose,
|
|
506
535
|
)
|
|
507
536
|
|
|
508
537
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import subprocess
|
|
2
3
|
import sys
|
|
3
4
|
from pathlib import Path
|
|
@@ -5,7 +6,10 @@ from unittest.mock import patch
|
|
|
5
6
|
|
|
6
7
|
import pytest
|
|
7
8
|
import uvicorn
|
|
9
|
+
from fastapi.testclient import TestClient
|
|
10
|
+
from httpx import Response
|
|
8
11
|
from typer.testing import CliRunner
|
|
12
|
+
from uvicorn.importer import import_from_string
|
|
9
13
|
|
|
10
14
|
from fastapi_cli.cli import app
|
|
11
15
|
from fastapi_cli.utils.cli import get_uvicorn_log_config
|
|
@@ -17,7 +21,8 @@ assets_path = Path(__file__).parent / "assets"
|
|
|
17
21
|
|
|
18
22
|
|
|
19
23
|
@pytest.fixture(autouse=True)
|
|
20
|
-
def
|
|
24
|
+
def isolate_cli_environment(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
25
|
+
monkeypatch.delenv("FASTAPI_ENV", raising=False)
|
|
21
26
|
monkeypatch.setattr("fastapi_cli.cli.should_use_rich_logs", lambda: True)
|
|
22
27
|
|
|
23
28
|
|
|
@@ -40,20 +45,59 @@ def test_dev() -> None:
|
|
|
40
45
|
"forwarded_allow_ips": None,
|
|
41
46
|
"log_config": get_uvicorn_log_config(),
|
|
42
47
|
}
|
|
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
48
|
assert "Starting FastAPI in development mode" in result.output
|
|
49
|
+
assert "Using import string: single_file_app:app" in result.output
|
|
49
50
|
assert "Server started at http://127.0.0.1:8000" in result.output
|
|
50
51
|
assert "Documentation at http://127.0.0.1:8000/docs" in result.output
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
# a passed path is explicit, so no source hint and no pyproject nudge
|
|
53
|
+
assert "(auto-discovered" not in result.output
|
|
54
|
+
assert "You can configure an entrypoint in pyproject.toml" not in result.output
|
|
55
|
+
# the step-by-step narration is --verbose only
|
|
56
|
+
assert "Searching for package file structure" not in result.output
|
|
57
|
+
assert "Configuration sources:" not in result.output
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _get_fastapi_env_response(*, command: str, fastapi_env: str | None) -> Response:
|
|
61
|
+
module_name = "environment_app"
|
|
62
|
+
sys.modules.pop(module_name, None)
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
with changing_dir(assets_path):
|
|
66
|
+
with patch.object(uvicorn, "run") as mock_run:
|
|
67
|
+
result = runner.invoke(
|
|
68
|
+
app,
|
|
69
|
+
[command, f"{module_name}.py"],
|
|
70
|
+
env={"FASTAPI_ENV": fastapi_env},
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
assert result.exit_code == 0, result.output
|
|
74
|
+
assert mock_run.call_args
|
|
75
|
+
imported_app = import_from_string(mock_run.call_args.kwargs["app"])
|
|
76
|
+
with TestClient(imported_app) as test_client:
|
|
77
|
+
return test_client.get("/fastapi-env")
|
|
78
|
+
finally:
|
|
79
|
+
sys.modules.pop(module_name, None)
|
|
80
|
+
|
|
55
81
|
|
|
56
|
-
|
|
82
|
+
def test_dev_sets_fastapi_env_before_app_import() -> None:
|
|
83
|
+
response = _get_fastapi_env_response(command="dev", fastapi_env=None)
|
|
84
|
+
|
|
85
|
+
assert response.status_code == 200
|
|
86
|
+
assert response.json() == {"fastapi_env": "development"}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_dev_does_not_override_existing_fastapi_env() -> None:
|
|
90
|
+
response = _get_fastapi_env_response(command="dev", fastapi_env="chicken")
|
|
91
|
+
|
|
92
|
+
assert response.status_code == 200
|
|
93
|
+
assert response.json() == {"fastapi_env": "chicken"}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_run_does_not_set_fastapi_env() -> None:
|
|
97
|
+
response = _get_fastapi_env_response(command="run", fastapi_env=None)
|
|
98
|
+
|
|
99
|
+
assert response.status_code == 200
|
|
100
|
+
assert response.json() == {"fastapi_env": None}
|
|
57
101
|
|
|
58
102
|
|
|
59
103
|
def test_run_uses_uvicorn_default_log_config_without_rich_logs(
|
|
@@ -83,12 +127,16 @@ def test_dev_no_args_auto_discovery() -> None:
|
|
|
83
127
|
assert mock_run.call_args.kwargs["host"] == "127.0.0.1"
|
|
84
128
|
assert mock_run.call_args.kwargs["port"] == 8000
|
|
85
129
|
assert mock_run.call_args.kwargs["reload"] is True
|
|
130
|
+
assert "Starting FastAPI in development mode" in result.output
|
|
86
131
|
assert "Using import string: main:app" in result.output
|
|
87
|
-
|
|
88
|
-
assert "
|
|
132
|
+
# Rich can wrap the rest of the hint based on terminal and emoji width
|
|
133
|
+
assert "(auto-discovered, use --verbose" in result.output
|
|
134
|
+
# auto-discovery nudges you to pin the entrypoint
|
|
89
135
|
assert "You can configure an entrypoint in pyproject.toml" in result.output
|
|
90
136
|
assert "[tool.fastapi]" in result.output
|
|
91
137
|
assert 'entrypoint = "main:app"' in result.output
|
|
138
|
+
# the full breakdown stays behind --verbose
|
|
139
|
+
assert "Configuration sources:" not in result.output
|
|
92
140
|
|
|
93
141
|
|
|
94
142
|
def test_dev_package() -> None:
|
|
@@ -110,22 +158,14 @@ def test_dev_package() -> None:
|
|
|
110
158
|
"forwarded_allow_ips": None,
|
|
111
159
|
"log_config": get_uvicorn_log_config(),
|
|
112
160
|
}
|
|
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
161
|
assert "Starting FastAPI in development mode" in result.output
|
|
162
|
+
assert "Using import string: nested_package.package:app" in result.output
|
|
118
163
|
assert "Server started at http://127.0.0.1:8000" in result.output
|
|
119
164
|
assert "Documentation at http://127.0.0.1:8000/docs" in result.output
|
|
120
|
-
assert (
|
|
121
|
-
|
|
122
|
-
|
|
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
|
|
165
|
+
assert "(auto-discovered" not in result.output
|
|
166
|
+
assert "You can configure an entrypoint in pyproject.toml" not in result.output
|
|
167
|
+
# the file tree is --verbose only
|
|
168
|
+
assert "📁 package" not in result.output
|
|
129
169
|
|
|
130
170
|
|
|
131
171
|
def test_dev_args() -> None:
|
|
@@ -163,17 +203,12 @@ def test_dev_args() -> None:
|
|
|
163
203
|
"forwarded_allow_ips": None,
|
|
164
204
|
"log_config": get_uvicorn_log_config(),
|
|
165
205
|
}
|
|
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
206
|
assert "Starting FastAPI in development mode" in result.output
|
|
207
|
+
assert "Using import string: single_file_app:api" in result.output
|
|
171
208
|
assert "Server started at http://192.168.0.2:8080" in result.output
|
|
172
209
|
assert "Documentation at http://192.168.0.2:8080/docs" in result.output
|
|
173
|
-
assert (
|
|
174
|
-
|
|
175
|
-
in result.output
|
|
176
|
-
)
|
|
210
|
+
assert "(auto-discovered" not in result.output
|
|
211
|
+
assert "You can configure an entrypoint in pyproject.toml" not in result.output
|
|
177
212
|
|
|
178
213
|
|
|
179
214
|
def test_dev_env_vars() -> None:
|
|
@@ -197,17 +232,10 @@ def test_dev_env_vars() -> None:
|
|
|
197
232
|
"forwarded_allow_ips": None,
|
|
198
233
|
"log_config": get_uvicorn_log_config(),
|
|
199
234
|
}
|
|
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
235
|
assert "Starting FastAPI in development mode" in result.output
|
|
236
|
+
assert "Using import string: single_file_app:app" in result.output
|
|
205
237
|
assert "Server started at http://127.0.0.1:8111" in result.output
|
|
206
238
|
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
239
|
|
|
212
240
|
|
|
213
241
|
def test_dev_env_vars_and_args() -> None:
|
|
@@ -238,17 +266,68 @@ def test_dev_env_vars_and_args() -> None:
|
|
|
238
266
|
"forwarded_allow_ips": None,
|
|
239
267
|
"log_config": get_uvicorn_log_config(),
|
|
240
268
|
}
|
|
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
269
|
assert "Starting FastAPI in development mode" in result.output
|
|
270
|
+
assert "Using import string: single_file_app:app" in result.output
|
|
246
271
|
assert "Server started at http://127.0.0.1:8080" in result.output
|
|
247
272
|
assert "Documentation at http://127.0.0.1:8080/docs" in result.output
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def test_dev_verbose() -> None:
|
|
276
|
+
"""--verbose restores the full narration and the module file tree."""
|
|
277
|
+
with changing_dir(assets_path):
|
|
278
|
+
with patch.object(uvicorn, "run") as mock_run:
|
|
279
|
+
result = runner.invoke(app, ["dev", "nested_package/package", "--verbose"])
|
|
280
|
+
assert result.exit_code == 0, result.output
|
|
281
|
+
assert mock_run.called
|
|
282
|
+
assert "Starting FastAPI in development mode" in result.output
|
|
283
|
+
assert "Searching for package file structure" in result.output
|
|
284
|
+
assert "Importing from" in result.output
|
|
285
|
+
assert "Importing the FastAPI app object" in result.output
|
|
286
|
+
assert "from nested_package.package import app" in result.output
|
|
287
|
+
assert "Using import string: nested_package.package:app" in result.output
|
|
288
|
+
# module and app come from different sources here
|
|
289
|
+
assert "Configuration sources:" in result.output
|
|
290
|
+
assert "Module: path CLI argument" in result.output
|
|
291
|
+
assert "App name: auto-discovery" in result.output
|
|
292
|
+
# the file tree
|
|
293
|
+
assert "📁 package" in result.output
|
|
294
|
+
assert "🐍 __init__.py" in result.output
|
|
295
|
+
# the source hint is folded away in verbose mode
|
|
296
|
+
assert "(auto-discovered, use --verbose to learn more)" not in result.output
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def test_dev_verbose_auto_discovery() -> None:
|
|
300
|
+
"""--verbose with auto-discovery shows the single 'import string' source."""
|
|
301
|
+
with changing_dir(assets_path / "default_files" / "default_main"):
|
|
302
|
+
with patch.object(uvicorn, "run") as mock_run:
|
|
303
|
+
result = runner.invoke(app, ["dev", "--verbose"])
|
|
304
|
+
assert result.exit_code == 0, result.output
|
|
305
|
+
assert mock_run.called
|
|
306
|
+
assert "Searching for package file structure" in result.output
|
|
307
|
+
assert "Importing the FastAPI app object" in result.output
|
|
308
|
+
assert "Using import string: main:app" in result.output
|
|
309
|
+
assert "Configuration sources:" in result.output
|
|
310
|
+
# module and app share the same source (auto-discovery)
|
|
311
|
+
assert "Import string: auto-discovery" in result.output
|
|
312
|
+
# auto-discovery still nudges to pin the entrypoint
|
|
313
|
+
assert "You can configure an entrypoint in pyproject.toml" in result.output
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def test_global_verbose_enables_debug_logging() -> None:
|
|
317
|
+
with changing_dir(assets_path):
|
|
318
|
+
with patch.object(uvicorn, "run"):
|
|
319
|
+
result = runner.invoke(app, ["--verbose", "dev", "single_file_app.py"])
|
|
320
|
+
assert result.exit_code == 0, result.output
|
|
321
|
+
|
|
322
|
+
assert logging.getLogger("fastapi_cli").level == logging.DEBUG
|
|
323
|
+
|
|
324
|
+
# a run without --verbose resets the level
|
|
325
|
+
with changing_dir(assets_path):
|
|
326
|
+
with patch.object(uvicorn, "run"):
|
|
327
|
+
result = runner.invoke(app, ["dev", "single_file_app.py"])
|
|
328
|
+
assert result.exit_code == 0, result.output
|
|
329
|
+
|
|
330
|
+
assert logging.getLogger("fastapi_cli").level == logging.INFO
|
|
252
331
|
|
|
253
332
|
|
|
254
333
|
def test_entrypoint_mutually_exclusive_with_path() -> None:
|
|
@@ -287,11 +366,8 @@ def test_run() -> None:
|
|
|
287
366
|
"forwarded_allow_ips": None,
|
|
288
367
|
"log_config": get_uvicorn_log_config(),
|
|
289
368
|
}
|
|
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
369
|
assert "Starting FastAPI in production mode" in result.output
|
|
370
|
+
assert "Using import string: single_file_app:app" in result.output
|
|
295
371
|
assert "Server started at http://0.0.0.0:8000" in result.output
|
|
296
372
|
assert "Documentation at http://0.0.0.0:8000/docs" in result.output
|
|
297
373
|
|
|
@@ -317,14 +393,10 @@ def test_run_trust_proxy() -> None:
|
|
|
317
393
|
"forwarded_allow_ips": "*",
|
|
318
394
|
"log_config": get_uvicorn_log_config(),
|
|
319
395
|
}
|
|
320
|
-
assert "Using import string: single_file_app:app" in result.output
|
|
321
396
|
assert "Starting FastAPI in production mode" in result.output
|
|
397
|
+
assert "Using import string: single_file_app:app" in result.output
|
|
322
398
|
assert "Server started at http://0.0.0.0:8000" in result.output
|
|
323
399
|
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
400
|
|
|
329
401
|
|
|
330
402
|
def test_run_args() -> None:
|
|
@@ -365,17 +437,10 @@ def test_run_args() -> None:
|
|
|
365
437
|
"log_config": get_uvicorn_log_config(),
|
|
366
438
|
}
|
|
367
439
|
|
|
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
440
|
assert "Starting FastAPI in production mode" in result.output
|
|
441
|
+
assert "Using import string: single_file_app:api" in result.output
|
|
373
442
|
assert "Server started at http://192.168.0.2:8080" in result.output
|
|
374
443
|
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
444
|
|
|
380
445
|
|
|
381
446
|
def test_run_env_vars() -> None:
|
|
@@ -399,11 +464,8 @@ def test_run_env_vars() -> None:
|
|
|
399
464
|
"forwarded_allow_ips": None,
|
|
400
465
|
"log_config": get_uvicorn_log_config(),
|
|
401
466
|
}
|
|
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
467
|
assert "Starting FastAPI in production mode" in result.output
|
|
468
|
+
assert "Using import string: single_file_app:app" in result.output
|
|
407
469
|
assert "Server started at http://0.0.0.0:8111" in result.output
|
|
408
470
|
assert "Documentation at http://0.0.0.0:8111/docs" in result.output
|
|
409
471
|
|
|
@@ -436,11 +498,8 @@ def test_run_env_vars_and_args() -> None:
|
|
|
436
498
|
"forwarded_allow_ips": None,
|
|
437
499
|
"log_config": get_uvicorn_log_config(),
|
|
438
500
|
}
|
|
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
501
|
assert "Starting FastAPI in production mode" in result.output
|
|
502
|
+
assert "Using import string: single_file_app:app" in result.output
|
|
444
503
|
assert "Server started at http://0.0.0.0:8080" in result.output
|
|
445
504
|
assert "Documentation at http://0.0.0.0:8080/docs" in result.output
|
|
446
505
|
|
|
@@ -601,7 +660,8 @@ def test_dev_with_import_string() -> None:
|
|
|
601
660
|
"log_config": get_uvicorn_log_config(),
|
|
602
661
|
}
|
|
603
662
|
assert "Using import string: single_file_app:api" in result.output
|
|
604
|
-
|
|
663
|
+
# an explicit entrypoint is not auto-discovery
|
|
664
|
+
assert "(auto-discovered" not in result.output
|
|
605
665
|
assert "You can configure an entrypoint in pyproject.toml" not in result.output
|
|
606
666
|
|
|
607
667
|
|
|
@@ -625,7 +685,7 @@ def test_run_with_import_string() -> None:
|
|
|
625
685
|
"log_config": get_uvicorn_log_config(),
|
|
626
686
|
}
|
|
627
687
|
assert "Using import string: single_file_app:app" in result.output
|
|
628
|
-
assert "
|
|
688
|
+
assert "(auto-discovered" not in result.output
|
|
629
689
|
assert "You can configure an entrypoint in pyproject.toml" not in result.output
|
|
630
690
|
|
|
631
691
|
|
|
@@ -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
|
-
|
|
30
|
-
assert "
|
|
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 "
|
|
48
|
-
assert "
|
|
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 "
|
|
62
|
-
assert "
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_api/app/api.py
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_app/app/api.py
RENAMED
|
File without changes
|
{fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_app/app/app.py
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_main/app/api.py
RENAMED
|
File without changes
|
{fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/default_app_dir_main/app/app.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/default_files/non_default/nonstandard.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/nested_package/package/core/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_cli-0.0.30 → fastapi_cli-0.0.32}/tests/assets/nested_package/package/mod/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|