From faf6f9f5cab84c257c0754b90e0ef2db8ff5e660 Mon Sep 17 00:00:00 2001 From: abondar Date: Wed, 11 Feb 2026 23:56:39 +0200 Subject: [PATCH 1/3] Fix fastapi example --- .github/workflows/ci.yml | 2 ++ examples/fastapi/_tests.py | 6 ++-- examples/fastapi/config.py | 6 +++- examples/fastapi/main.py | 37 +++--------------------- examples/fastapi/main_custom_timezone.py | 3 -- 5 files changed, 13 insertions(+), 41 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ec863f9c..439838f11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,9 @@ jobs: PYTHONDEVMODE: 1 - name: Test FastAPI/Blacksheep/Sanic Examples run: | + PYTHONPATH=$DEST_FASTAPI uv run --frozen tortoise -c config.TORTOISE_ORM migrate PYTHONPATH=$DEST_FASTAPI uv run --frozen pytest $PYTEST_ARGS $DEST_FASTAPI/_tests.py + rm -f $DEST_FASTAPI/db.sqlite3 PYTHONPATH=$DEST_BLACKSHEEP uv run --frozen pytest $PYTEST_ARGS $DEST_BLACKSHEEP/_tests.py PYTHONPATH=$DEST_SANIC uv run --frozen pytest $PYTEST_ARGS $DEST_SANIC/_tests.py env: diff --git a/examples/fastapi/_tests.py b/examples/fastapi/_tests.py index a26d15afe..c99e7b48b 100644 --- a/examples/fastapi/_tests.py +++ b/examples/fastapi/_tests.py @@ -1,7 +1,6 @@ # mypy: no-disallow-untyped-decorators # pylint: disable=E0611,E0401 import multiprocessing -import os from collections.abc import AsyncGenerator from concurrent.futures import ProcessPoolExecutor from contextlib import asynccontextmanager @@ -13,11 +12,10 @@ from asgi_lifespan import LifespanManager from httpx import ASGITransport, AsyncClient -from tortoise.contrib.test import MEMORY_SQLITE +from tortoise.contrib.test import truncate_all_models from tortoise.fields.data import JSON_LOADS from tortoise.timezone import UTC, localtime -os.environ["DB_URL"] = MEMORY_SQLITE try: from config import register_orm from main import app @@ -42,7 +40,6 @@ def anyio_backend() -> str: @asynccontextmanager async def client_manager(app, base_url="http://test", **kw) -> ClientManagerType: - app.state.testing = True async with LifespanManager(app): transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url=base_url, **kw) as c: @@ -62,6 +59,7 @@ async def client_east() -> ClientManagerType: async with client_manager(app_east) as c: ctx = app_east.state._tortoise_context with ctx: # Enter context to make it current via contextvar + await truncate_all_models() yield c diff --git a/examples/fastapi/config.py b/examples/fastapi/config.py index 824527bbc..989c80c08 100644 --- a/examples/fastapi/config.py +++ b/examples/fastapi/config.py @@ -1,12 +1,16 @@ import os from functools import partial +from pathlib import Path from tortoise.config import AppConfig, DBUrlConfig, TortoiseConfig from tortoise.contrib.fastapi import RegisterTortoise +BASE_DIR = Path(__file__).resolve().parent +DEFAULT_DB_URL = f"sqlite://{BASE_DIR / 'db.sqlite3'}" + # Single source of truth for Tortoise ORM configuration TORTOISE_ORM = TortoiseConfig( - connections={"default": DBUrlConfig(url=os.getenv("DB_URL", "sqlite://db.sqlite3"))}, + connections={"default": DBUrlConfig(url=os.getenv("DB_URL", DEFAULT_DB_URL))}, apps={ "models": AppConfig( models=["models"], diff --git a/examples/fastapi/main.py b/examples/fastapi/main.py index 97a3ca7f3..60067ff07 100644 --- a/examples/fastapi/main.py +++ b/examples/fastapi/main.py @@ -1,50 +1,21 @@ # pylint: disable=E0611,E0401 -import os from collections.abc import AsyncGenerator from contextlib import asynccontextmanager from fastapi import FastAPI from routers import router as users_router -from examples.fastapi.config import register_orm -from tortoise import Tortoise -from tortoise.backends.base.config_generator import generate_config -from tortoise.contrib.fastapi import RegisterTortoise, tortoise_exception_handlers +from config import register_orm +from tortoise.contrib.fastapi import tortoise_exception_handlers @asynccontextmanager -async def lifespan_test(app: FastAPI) -> AsyncGenerator[None, None]: - config = generate_config( - os.getenv("TORTOISE_TEST_DB", "sqlite://:memory:"), - app_modules={"models": ["models"]}, - testing=True, - connection_label="models", - ) - async with RegisterTortoise( - app=app, - config=config, - generate_schemas=True, - _create_db=True, - ): +async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: + async with register_orm(app): # db connected yield # app teardown # db connections closed - await Tortoise._drop_databases() - - -@asynccontextmanager -async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: - if getattr(app.state, "testing", None): - async with lifespan_test(app) as _: - yield - else: - # app startup - async with register_orm(app): - # db connected - yield - # app teardown - # db connections closed app = FastAPI( diff --git a/examples/fastapi/main_custom_timezone.py b/examples/fastapi/main_custom_timezone.py index d6e9629d1..403f22298 100644 --- a/examples/fastapi/main_custom_timezone.py +++ b/examples/fastapi/main_custom_timezone.py @@ -9,9 +9,6 @@ @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: - # app startup - # Disable global fallback since this is the secondary app in tests - # (main app already uses global fallback). Context is stored in app.state. async with register_orm( app, use_tz=True, From 4304d2ddba02422eaef1664d7c9d244a7c204313 Mon Sep 17 00:00:00 2001 From: abondar Date: Wed, 11 Feb 2026 23:57:55 +0200 Subject: [PATCH 2/3] Fix lint --- examples/fastapi/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fastapi/main.py b/examples/fastapi/main.py index 60067ff07..dd50b486b 100644 --- a/examples/fastapi/main.py +++ b/examples/fastapi/main.py @@ -2,10 +2,10 @@ from collections.abc import AsyncGenerator from contextlib import asynccontextmanager +from config import register_orm from fastapi import FastAPI from routers import router as users_router -from config import register_orm from tortoise.contrib.fastapi import tortoise_exception_handlers From 6742dcf2d93d62e394fbc3f8abb6346ad279c509 Mon Sep 17 00:00:00 2001 From: abondar Date: Thu, 12 Feb 2026 00:07:28 +0200 Subject: [PATCH 3/3] Fix --- .github/workflows/ci.yml | 3 ++- examples/fastapi/_tests.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 439838f11..7aa121c81 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: - name: Test FastAPI/Blacksheep/Sanic Examples run: | PYTHONPATH=$DEST_FASTAPI uv run --frozen tortoise -c config.TORTOISE_ORM migrate - PYTHONPATH=$DEST_FASTAPI uv run --frozen pytest $PYTEST_ARGS $DEST_FASTAPI/_tests.py + PYTHONPATH=$DEST_FASTAPI uv run --frozen pytest $PYTEST_ARGS_SEQ $DEST_FASTAPI/_tests.py rm -f $DEST_FASTAPI/db.sqlite3 PYTHONPATH=$DEST_BLACKSHEEP uv run --frozen pytest $PYTEST_ARGS $DEST_BLACKSHEEP/_tests.py PYTHONPATH=$DEST_SANIC uv run --frozen pytest $PYTEST_ARGS $DEST_SANIC/_tests.py @@ -69,6 +69,7 @@ jobs: DEST_SANIC: examples/sanic PYTHONDEVMODE: 1 PYTEST_ARGS: "-n auto --cov=tortoise --cov-append --cov-branch --tb=native -q" + PYTEST_ARGS_SEQ: "--cov=tortoise --cov-append --cov-branch --tb=native -q" - name: Test Comprehensive Migrations Example run: | cd examples/comprehensive_migrations_project diff --git a/examples/fastapi/_tests.py b/examples/fastapi/_tests.py index c99e7b48b..fb4980c8e 100644 --- a/examples/fastapi/_tests.py +++ b/examples/fastapi/_tests.py @@ -49,6 +49,7 @@ async def client_manager(app, base_url="http://test", **kw) -> ClientManagerType @pytest.fixture(scope="module") async def client() -> ClientManagerType: async with client_manager(app) as c: + await truncate_all_models() yield c