Skip to content

Commit c6c4279

Browse files
committed
Merge remote-tracking branch 'upstream/main' into prek
2 parents 508fe0d + b47d301 commit c6c4279

15 files changed

Lines changed: 153 additions & 58 deletions

File tree

.github/workflows/smokeshow.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ env:
1313

1414
jobs:
1515
smokeshow:
16-
if: ${{ github.event.workflow_run.conclusion == 'success' }}
1716
runs-on: ubuntu-latest
1817
steps:
1918
- name: Dump GitHub context
@@ -23,12 +22,10 @@ jobs:
2322
- uses: actions/checkout@v6
2423
- uses: actions/setup-python@v6
2524
with:
26-
python-version: '3.9'
25+
python-version: '3.13'
2726
- name: Setup uv
2827
uses: astral-sh/setup-uv@v7
2928
with:
30-
version: "0.4.15"
31-
enable-cache: true
3229
cache-dependency-glob: |
3330
requirements**.txt
3431
pyproject.toml

.github/workflows/test.yml

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,28 +30,18 @@ jobs:
3030
matrix:
3131
os: [ ubuntu-latest, windows-latest, macos-latest ]
3232
python-version: ["3.14"]
33-
pydantic-version: ["v2"]
3433
include:
35-
- python-version: "3.8"
36-
pydantic-version: "v1"
37-
os: windows-latest
3834
- python-version: "3.9"
39-
pydantic-version: "v2"
4035
os: macos-latest
4136
- python-version: "3.10"
42-
pydantic-version: "v1"
4337
os: ubuntu-latest
4438
- python-version: "3.11"
45-
pydantic-version: "v2"
4639
os: windows-latest
4740
- python-version: "3.12"
48-
pydantic-version: "v1"
4941
os: macos-latest
5042
- python-version: "3.13"
51-
pydantic-version: "v1"
5243
os: ubuntu-latest
5344
- python-version: "3.13"
54-
pydantic-version: "v2"
5545
os: windows-latest
5646
fail-fast: false
5747
runs-on: ${{ matrix.os }}
@@ -81,21 +71,18 @@ jobs:
8171
limit-access-to-actor: true
8272
- name: Install Dependencies
8373
run: uv pip install -r requirements-tests.txt
84-
- name: Install Pydantic v1
85-
if: matrix.pydantic-version == 'v1'
86-
run: uv pip install "pydantic<2.0.0"
8774
- name: Lint
8875
run: bash scripts/lint.sh
8976
- run: mkdir coverage
9077
- name: Test
9178
run: bash scripts/test.sh
9279
env:
93-
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-pydantic-${{ matrix.pydantic-version }}
94-
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}-pydantic-${{ matrix.pydantic-version }}
80+
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}
81+
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}
9582
- name: Store coverage files
9683
uses: actions/upload-artifact@v5
9784
with:
98-
name: coverage-${{ runner.os }}-${{ matrix.python-version }}-pydantic-${{ matrix.pydantic-version }}
85+
name: coverage-${{ runner.os }}-${{ matrix.python-version }}
9986
path: coverage
10087
include-hidden-files: true
10188

@@ -110,7 +97,7 @@ jobs:
11097
- uses: actions/checkout@v6
11198
- uses: actions/setup-python@v6
11299
with:
113-
python-version: '3.8'
100+
python-version: '3.9'
114101
- name: Setup uv
115102
uses: astral-sh/setup-uv@v7
116103
with:
@@ -128,14 +115,14 @@ jobs:
128115
- run: uv pip install -r requirements-tests.txt
129116
- run: ls -la coverage
130117
- run: coverage combine coverage
131-
- run: coverage report
132118
- run: coverage html --title "Coverage for ${{ github.sha }}"
133119
- name: Store coverage HTML
134120
uses: actions/upload-artifact@v5
135121
with:
136122
name: coverage-html
137123
path: htmlcov
138124
include-hidden-files: true
125+
- run: coverage report --fail-under=100
139126

140127
# https://github.com/marketplace/actions/alls-green#why
141128
check: # This job does nothing and is only used for the branch protection

pdm_build.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import os
2-
from typing import Any, Dict
2+
from typing import Any
33

44
from pdm.backend.hooks import Context
55

@@ -9,12 +9,12 @@
99
def pdm_build_initialize(context: Context):
1010
metadata = context.config.metadata
1111
# Get custom config for the current package, from the env var
12-
config: Dict[str, Any] = context.config.data["tool"]["tiangolo"][
12+
config: dict[str, Any] = context.config.data["tool"]["tiangolo"][
1313
"_internal-slim-build"
1414
]["packages"].get(TIANGOLO_BUILD_PACKAGE)
1515
if not config:
1616
return
17-
project_config: Dict[str, Any] = config["project"]
17+
project_config: dict[str, Any] = config["project"]
1818
# Override main [project] configs with custom configs for this package
1919
for key, value in project_config.items():
2020
metadata[key] = value

pyproject.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description = "Run and manage FastAPI apps from the command line with FastAPI CL
55
authors = [
66
{name = "Sebastián Ramírez", email = "tiangolo@gmail.com"},
77
]
8-
requires-python = ">=3.8"
8+
requires-python = ">=3.9"
99
readme = "README.md"
1010
license = "MIT"
1111
license-files = ["LICENSE"]
@@ -24,7 +24,6 @@ classifiers = [
2424
"Framework :: FastAPI",
2525
"Intended Audience :: Developers",
2626
"Programming Language :: Python :: 3 :: Only",
27-
"Programming Language :: Python :: 3.8",
2827
"Programming Language :: Python :: 3.9",
2928
"Programming Language :: Python :: 3.10",
3029
"Programming Language :: Python :: 3.11",
@@ -47,6 +46,9 @@ standard = [
4746
standard-no-fastapi-cloud-cli = [
4847
"uvicorn[standard] >= 0.15.0",
4948
]
49+
new = [
50+
"fastapi-new >= 0.0.2 ; python_version >= '3.10'",
51+
]
5052

5153
[project.urls]
5254
Homepage = "https://github.com/fastapi/fastapi-cli"

release-notes.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,38 @@
22

33
## Latest Changes
44

5+
## 0.0.20
6+
7+
### Features
8+
9+
* ✨ Add --reload-dir option to dev command. PR [#267](https://github.com/fastapi/fastapi-cli/pull/267) by [@patrick91](https://github.com/patrick91).
10+
11+
## 0.0.19
12+
13+
### Breaking Changes
14+
15+
* 🔧 Drop support for Python 3.8. PR [#269](https://github.com/fastapi/fastapi-cli/pull/269) by [@patrick91](https://github.com/patrick91).
16+
17+
## 0.0.18
18+
19+
### Features
20+
21+
* ➕ Add `fastapi-new` in `new` optional dependency group. PR [#241](https://github.com/fastapi/fastapi-cli/pull/241) by [@savannahostrowski](https://github.com/savannahostrowski).
22+
23+
### Fixes
24+
25+
* 🐛 Fix log alignment when pressing Ctrl+C to stop server. PR [#253](https://github.com/fastapi/fastapi-cli/pull/253) by [@savannahostrowski](https://github.com/savannahostrowski).
26+
27+
## 0.0.17
28+
29+
### Upgrades
30+
31+
* ➖ Drop support for Pydantic v1. PR [#268](https://github.com/fastapi/fastapi-cli/pull/268) by [@patrick91](https://github.com/patrick91).
32+
533
### Internal
634

35+
* 👷 Configure coverage, error on main tests, don't wait for Smokeshow. PR [#265](https://github.com/fastapi/fastapi-cli/pull/265) by [@YuriiMotov](https://github.com/YuriiMotov).
36+
* 👷 Run Smokeshow always, even on test failures. PR [#264](https://github.com/fastapi/fastapi-cli/pull/264) by [@YuriiMotov](https://github.com/YuriiMotov).
737
*[pre-commit.ci] pre-commit autoupdate. PR [#247](https://github.com/fastapi/fastapi-cli/pull/247) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
838
* ⬆ Bump ruff from 0.14.5 to 0.14.6. PR [#245](https://github.com/fastapi/fastapi-cli/pull/245) by [@dependabot[bot]](https://github.com/apps/dependabot).
939
* ⬆ Bump actions/checkout from 5 to 6. PR [#248](https://github.com/fastapi/fastapi-cli/pull/248) by [@dependabot[bot]](https://github.com/apps/dependabot).

src/fastapi_cli/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.0.16"
1+
__version__ = "0.0.20"

src/fastapi_cli/cli.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import logging
22
from pathlib import Path
3-
from typing import Any, List, Union
3+
from typing import Annotated, Any, Union
44

55
import typer
66
from pydantic import ValidationError
77
from rich import print
88
from rich.tree import Tree
9-
from typing_extensions import Annotated
109

1110
from fastapi_cli.config import FastAPIConfig
1211
from fastapi_cli.discover import get_import_data, get_import_data_from_import_string
@@ -39,6 +38,16 @@
3938
pass
4039

4140

41+
try:
42+
from fastapi_new.cli import ( # type: ignore[import-not-found]
43+
app as fastapi_new_cli,
44+
)
45+
46+
app.add_typer(fastapi_new_cli) # pragma: no cover
47+
except ImportError: # pragma: no cover
48+
pass
49+
50+
4251
def version_callback(value: bool) -> None:
4352
if value:
4453
print(f"FastAPI CLI version: [green]{__version__}[/green]")
@@ -68,7 +77,7 @@ def callback(
6877
setup_logging(level=log_level)
6978

7079

71-
def _get_module_tree(module_paths: List[Path]) -> Tree:
80+
def _get_module_tree(module_paths: list[Path]) -> Tree:
7281
root = module_paths[0]
7382
name = f"🐍 {root.name}" if root.is_file() else f"📁 {root.name}"
7483

@@ -95,6 +104,7 @@ def _run(
95104
host: str = "127.0.0.1",
96105
port: int = 8000,
97106
reload: bool = True,
107+
reload_dirs: Union[list[Path], None] = None,
98108
workers: Union[int, None] = None,
99109
root_path: str = "",
100110
command: str,
@@ -210,6 +220,11 @@ def _run(
210220
host=host,
211221
port=port,
212222
reload=reload,
223+
reload_dirs=(
224+
[str(directory.resolve()) for directory in reload_dirs]
225+
if reload_dirs
226+
else None
227+
),
213228
workers=workers,
214229
root_path=root_path,
215230
proxy_headers=proxy_headers,
@@ -246,6 +261,12 @@ def dev(
246261
help="Enable auto-reload of the server when (code) files change. This is [bold]resource intensive[/bold], use it only during development."
247262
),
248263
] = True,
264+
reload_dir: Annotated[
265+
Union[list[Path], None],
266+
typer.Option(
267+
help="Set reload directories explicitly, instead of using the current working directory."
268+
),
269+
] = None,
249270
root_path: Annotated[
250271
str,
251272
typer.Option(
@@ -309,6 +330,7 @@ def dev(
309330
host=host,
310331
port=port,
311332
reload=reload,
333+
reload_dirs=reload_dir,
312334
root_path=root_path,
313335
app=app,
314336
entrypoint=entrypoint,

src/fastapi_cli/config.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
import logging
22
from pathlib import Path
3-
from typing import Any, Dict, Optional
3+
from typing import Any, Optional
44

55
from pydantic import BaseModel, StrictStr
6-
from pydantic.version import VERSION as PYDANTIC_VERSION
76

87
logger = logging.getLogger(__name__)
98

10-
PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2])
11-
PYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2
12-
139

1410
class FastAPIConfig(BaseModel):
1511
entrypoint: Optional[StrictStr] = None
1612

1713
@classmethod
18-
def _read_pyproject_toml(cls) -> Dict[str, Any]:
14+
def _read_pyproject_toml(cls) -> dict[str, Any]:
1915
"""Read FastAPI configuration from pyproject.toml in current directory."""
2016
pyproject_path = Path.cwd() / "pyproject.toml"
2117

@@ -43,8 +39,4 @@ def resolve(cls, entrypoint: Optional[str] = None) -> "FastAPIConfig":
4339
if entrypoint is not None:
4440
config["entrypoint"] = entrypoint
4541

46-
# Pydantic v2 uses model_validate, v1 uses parse_obj
47-
if not PYDANTIC_V2:
48-
return cls.parse_obj(config) # type: ignore[no-any-return, unused-ignore]
49-
50-
return cls.model_validate(config) # type: ignore[no-any-return, unused-ignore, attr-defined]
42+
return cls.model_validate(config)

src/fastapi_cli/discover.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from dataclasses import dataclass
44
from logging import getLogger
55
from pathlib import Path
6-
from typing import List, Union
6+
from typing import Union
77

88
from fastapi_cli.exceptions import FastAPICLIException
99

@@ -39,7 +39,7 @@ def get_default_path() -> Path:
3939
class ModuleData:
4040
module_import_str: str
4141
extra_sys_path: Path
42-
module_paths: List[Path]
42+
module_paths: list[Path]
4343

4444

4545
def get_module_data_from_path(path: Path) -> ModuleData:

src/fastapi_cli/utils/cli.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import logging
2-
from typing import Any, Dict
2+
from typing import Any
33

44
from rich_toolkit import RichToolkit, RichToolkitTheme
55
from rich_toolkit.styles import TaggedStyle
@@ -12,10 +12,15 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
1212
self.toolkit = get_rich_toolkit()
1313

1414
def formatMessage(self, record: logging.LogRecord) -> str:
15-
return self.toolkit.print_as_string(record.getMessage(), tag=record.levelname)
15+
message = record.getMessage()
16+
result = self.toolkit.print_as_string(message, tag=record.levelname)
17+
# Prepend newline to fix alignment after ^C is printed by the terminal
18+
if message == "Shutting down":
19+
result = "\n" + result
20+
return result
1621

1722

18-
def get_uvicorn_log_config() -> Dict[str, Any]:
23+
def get_uvicorn_log_config() -> dict[str, Any]:
1924
return {
2025
"version": 1,
2126
"disable_existing_loggers": False,

0 commit comments

Comments
 (0)