Skip to content

Commit 8766a6b

Browse files
committed
Merge branch 'main' into feat/update_tests
2 parents ae2422c + 10d531f commit 8766a6b

File tree

15 files changed

+295
-20
lines changed

15 files changed

+295
-20
lines changed

.github/workflows/issue-manager.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ jobs:
3838
},
3939
"waiting": {
4040
"delay": 2628000,
41-
"message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR."
41+
"message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR.",
42+
"reminder": {
43+
"before": "P3D",
44+
"message": "Heads-up: this will be closed in 3 days unless there’s new activity."
45+
}
4246
}
4347
}

.github/workflows/smokeshow.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ jobs:
2525
with:
2626
python-version: '3.9'
2727
- name: Setup uv
28-
uses: astral-sh/setup-uv@v6
28+
uses: astral-sh/setup-uv@v7
2929
with:
3030
version: "0.4.15"
3131
enable-cache: true
3232
cache-dependency-glob: |
3333
requirements**.txt
3434
pyproject.toml
3535
- run: uv pip install -r requirements-github-actions.txt
36-
- uses: actions/download-artifact@v5
36+
- uses: actions/download-artifact@v6
3737
with:
3838
name: coverage-html
3939
path: htmlcov

.github/workflows/test.yml

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,26 @@ jobs:
3030
matrix:
3131
os: [ ubuntu-latest, windows-latest, macos-latest ]
3232
python-version: ["3.14"]
33+
pydantic-version: ["v1", "v2"]
3334
include:
3435
- os: windows-latest
3536
python-version: "3.8"
37+
pydantic-version: "v1"
3638
- os: macos-latest
3739
python-version: "3.9"
40+
pydantic-version: "v2"
3841
- os: ubuntu-latest
3942
python-version: "3.10"
43+
pydantic-version: "v1"
4044
- os: windows-latest
4145
python-version: "3.11"
46+
pydantic-version: "v2"
4247
- os: macos-latest
4348
python-version: "3.12"
49+
pydantic-version: "v1"
4450
- os: ubuntu-latest
4551
python-version: "3.13"
52+
pydantic-version: "v2"
4653
fail-fast: false
4754
runs-on: ${{ matrix.os }}
4855
steps:
@@ -56,7 +63,7 @@ jobs:
5663
with:
5764
python-version: ${{ matrix.python-version }}
5865
- name: Setup uv
59-
uses: astral-sh/setup-uv@v6
66+
uses: astral-sh/setup-uv@v7
6067
with:
6168
version: "0.4.15"
6269
enable-cache: true
@@ -71,18 +78,21 @@ jobs:
7178
limit-access-to-actor: true
7279
- name: Install Dependencies
7380
run: uv pip install -r requirements-tests.txt
81+
- name: Install Pydantic v1
82+
if: matrix.pydantic-version == 'v1'
83+
run: uv pip install "pydantic<2.0.0"
7484
- name: Lint
7585
run: bash scripts/lint.sh
7686
- run: mkdir coverage
7787
- name: Test
7888
run: bash scripts/test.sh
7989
env:
80-
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}
81-
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}
90+
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-pydantic-${{ matrix.pydantic-version }}
91+
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}-pydantic-${{ matrix.pydantic-version }}
8292
- name: Store coverage files
83-
uses: actions/upload-artifact@v4
93+
uses: actions/upload-artifact@v5
8494
with:
85-
name: coverage-${{ runner.os }}-${{ matrix.python-version }}
95+
name: coverage-${{ runner.os }}-${{ matrix.python-version }}-pydantic-${{ matrix.pydantic-version }}
8696
path: coverage
8797
include-hidden-files: true
8898

@@ -99,15 +109,15 @@ jobs:
99109
with:
100110
python-version: '3.8'
101111
- name: Setup uv
102-
uses: astral-sh/setup-uv@v6
112+
uses: astral-sh/setup-uv@v7
103113
with:
104114
version: "0.4.15"
105115
enable-cache: true
106116
cache-dependency-glob: |
107117
requirements**.txt
108118
pyproject.toml
109119
- name: Get coverage files
110-
uses: actions/download-artifact@v5
120+
uses: actions/download-artifact@v6
111121
with:
112122
pattern: coverage-*
113123
path: coverage
@@ -118,7 +128,7 @@ jobs:
118128
- run: coverage report
119129
- run: coverage html --title "Coverage for ${{ github.sha }}"
120130
- name: Store coverage HTML
121-
uses: actions/upload-artifact@v4
131+
uses: actions/upload-artifact@v5
122132
with:
123133
name: coverage-html
124134
path: htmlcov

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ repos:
1414
- id: end-of-file-fixer
1515
- id: trailing-whitespace
1616
- repo: https://github.com/astral-sh/ruff-pre-commit
17-
rev: v0.13.0
17+
rev: v0.14.4
1818
hooks:
1919
- id: ruff
2020
args:

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ authors = [
77
]
88
requires-python = ">=3.8"
99
readme = "README.md"
10-
license = {text = "MIT"}
10+
license = "MIT"
11+
license-files = ["LICENSE"]
1112
classifiers = [
1213
"Intended Audience :: Information Technology",
1314
"Intended Audience :: System Administrators",
@@ -22,7 +23,6 @@ classifiers = [
2223
"Development Status :: 4 - Beta",
2324
"Framework :: FastAPI",
2425
"Intended Audience :: Developers",
25-
"License :: OSI Approved :: MIT License",
2626
"Programming Language :: Python :: 3 :: Only",
2727
"Programming Language :: Python :: 3.8",
2828
"Programming Language :: Python :: 3.9",
@@ -31,12 +31,12 @@ classifiers = [
3131
"Programming Language :: Python :: 3.12",
3232
"Programming Language :: Python :: 3.13",
3333
"Programming Language :: Python :: 3.14",
34-
"License :: OSI Approved :: MIT License",
3534
]
3635
dependencies = [
3736
"typer >= 0.15.1",
3837
"uvicorn[standard] >= 0.15.0",
3938
"rich-toolkit >= 0.14.8",
39+
"tomli >= 2.0.0; python_version < '3.11'"
4040
]
4141

4242
[project.optional-dependencies]

release-notes.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,42 @@
22

33
## Latest Changes
44

5+
### Internal
6+
7+
* ⬆ Bump ruff from 0.14.2 to 0.14.4. PR [#239](https://github.com/fastapi/fastapi-cli/pull/239) by [@dependabot[bot]](https://github.com/apps/dependabot).
8+
*[pre-commit.ci] pre-commit autoupdate. PR [#238](https://github.com/fastapi/fastapi-cli/pull/238) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
9+
10+
## 0.0.16
11+
12+
### Fixes
13+
14+
* 🐛 Fix support for Pydantic v1. PR [#240](https://github.com/fastapi/fastapi-cli/pull/240) by [@patrick91](https://github.com/patrick91).
15+
16+
## 0.0.15
17+
18+
### Features
19+
20+
* ✨ Add support for reading configuration from `pyproject.toml`. PR [#236](https://github.com/fastapi/fastapi-cli/pull/236) by [@patrick91](https://github.com/patrick91).
21+
22+
You can use it in `pyproject.toml` like:
23+
24+
```toml
25+
[tool.fastapi]
26+
entrypoint = "some.importable_module:app_name"
27+
```
28+
29+
### Internal
30+
31+
* ⬆ Bump actions/upload-artifact from 4 to 5. PR [#232](https://github.com/fastapi/fastapi-cli/pull/232) by [@dependabot[bot]](https://github.com/apps/dependabot).
32+
* ⬆ Bump ruff from 0.14.1 to 0.14.2. PR [#231](https://github.com/fastapi/fastapi-cli/pull/231) by [@dependabot[bot]](https://github.com/apps/dependabot).
33+
* ⬆ Bump actions/download-artifact from 5 to 6. PR [#233](https://github.com/fastapi/fastapi-cli/pull/233) by [@dependabot[bot]](https://github.com/apps/dependabot).
34+
*[pre-commit.ci] pre-commit autoupdate. PR [#235](https://github.com/fastapi/fastapi-cli/pull/235) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
35+
* 🔧 Add PEP-639 license metadata. PR [#234](https://github.com/fastapi/fastapi-cli/pull/234) by [@svlandeg](https://github.com/svlandeg).
36+
* ⬆ Bump astral-sh/setup-uv from 6 to 7. PR [#223](https://github.com/fastapi/fastapi-cli/pull/223) by [@dependabot[bot]](https://github.com/apps/dependabot).
37+
* ⬆ Bump ruff from 0.13.0 to 0.14.1. PR [#228](https://github.com/fastapi/fastapi-cli/pull/228) by [@dependabot[bot]](https://github.com/apps/dependabot).
38+
* 🔧 Configure reminder for `waiting` label in `issue-manager`. PR [#227](https://github.com/fastapi/fastapi-cli/pull/227) by [@YuriiMotov](https://github.com/YuriiMotov).
39+
*[pre-commit.ci] pre-commit autoupdate. PR [#215](https://github.com/fastapi/fastapi-cli/pull/215) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
40+
541
## 0.0.14
642

743
### Upgrades

requirements-tests.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
pytest >=4.4.0,<9.0.0
44
coverage[toml] >=6.2,<8.0
55
mypy ==1.14.1
6-
ruff ==0.13.0
6+
ruff ==0.14.4
77
# Needed explicitly by fastapi-cli-slim
88
fastapi-slim
99
uvicorn

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.14"
1+
__version__ = "0.0.16"

src/fastapi_cli/cli.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
from typing import Any, List, Union
44

55
import typer
6+
from pydantic import ValidationError
67
from rich import print
78
from rich.tree import Tree
89
from typing_extensions import Annotated
910

11+
from fastapi_cli.config import FastAPIConfig
1012
from fastapi_cli.discover import get_import_data, get_import_data_from_import_string
1113
from fastapi_cli.exceptions import FastAPICLIException
1214

@@ -111,11 +113,37 @@ def _run(
111113
"Searching for package file structure from directories with [blue]__init__.py[/blue] files"
112114
)
113115

116+
if entrypoint and (path or app):
117+
toolkit.print_line()
118+
toolkit.print(
119+
"[error]Cannot use --entrypoint together with path or --app arguments"
120+
)
121+
toolkit.print_line()
122+
raise typer.Exit(code=1)
123+
114124
try:
115-
if entrypoint:
116-
import_data = get_import_data_from_import_string(entrypoint)
117-
else:
125+
config = FastAPIConfig.resolve(entrypoint=entrypoint)
126+
except ValidationError as e:
127+
toolkit.print_line()
128+
toolkit.print("[error]Invalid configuration in pyproject.toml:")
129+
toolkit.print_line()
130+
131+
for error in e.errors():
132+
field = ".".join(str(loc) for loc in error["loc"])
133+
toolkit.print(f" [red]•[/red] {field}: {error['msg']}")
134+
135+
toolkit.print_line()
136+
137+
raise typer.Exit(code=1) from None
138+
139+
try:
140+
# Resolve import data with priority: CLI path/app > config entrypoint > auto-discovery
141+
if path or app:
118142
import_data = get_import_data(path=path, app_name=app)
143+
elif config.entrypoint:
144+
import_data = get_import_data_from_import_string(config.entrypoint)
145+
else:
146+
import_data = get_import_data()
119147
except FastAPICLIException as e:
120148
toolkit.print_line()
121149
toolkit.print(f"[error]{e}")

src/fastapi_cli/config.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import logging
2+
from pathlib import Path
3+
from typing import Any, Dict, Optional
4+
5+
from pydantic import BaseModel, StrictStr
6+
from pydantic.version import VERSION as PYDANTIC_VERSION
7+
8+
logger = logging.getLogger(__name__)
9+
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+
13+
14+
class FastAPIConfig(BaseModel):
15+
entrypoint: Optional[StrictStr] = None
16+
17+
@classmethod
18+
def _read_pyproject_toml(cls) -> Dict[str, Any]:
19+
"""Read FastAPI configuration from pyproject.toml in current directory."""
20+
pyproject_path = Path.cwd() / "pyproject.toml"
21+
22+
if not pyproject_path.exists():
23+
return {}
24+
25+
try:
26+
import tomllib # type: ignore[import-not-found, unused-ignore]
27+
except ImportError:
28+
try:
29+
import tomli as tomllib # type: ignore[no-redef, import-not-found, unused-ignore]
30+
except ImportError: # pragma: no cover
31+
logger.debug("tomli not available, skipping pyproject.toml")
32+
return {}
33+
34+
with open(pyproject_path, "rb") as f:
35+
data = tomllib.load(f)
36+
37+
return data.get("tool", {}).get("fastapi", {}) # type: ignore
38+
39+
@classmethod
40+
def resolve(cls, entrypoint: Optional[str] = None) -> "FastAPIConfig":
41+
config = cls._read_pyproject_toml()
42+
43+
if entrypoint is not None:
44+
config["entrypoint"] = entrypoint
45+
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]

0 commit comments

Comments
 (0)