Skip to content

Commit 6b8ba80

Browse files
authored
feat: optional cleanup for sqlalchemy lifespans (#67)
1 parent 2607d16 commit 6b8ba80

File tree

7 files changed

+25
-6
lines changed

7 files changed

+25
-6
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ class Order(Base):
6464
user: Mapped[User] = relationship(back_populates="orders")
6565

6666
# That's it! Create your MCP app
67-
app = EnrichMCP("E-commerce Data", lifespan=sqlalchemy_lifespan(Base, engine))
67+
app = EnrichMCP(
68+
"E-commerce Data",
69+
lifespan=sqlalchemy_lifespan(Base, engine, cleanup_db_file=True),
70+
)
6871
include_sqlalchemy_models(app, Base)
6972

7073
if __name__ == "__main__":

docs/getting-started.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ class Product(Base):
239239
price: Mapped[float] = mapped_column()
240240

241241

242-
lifespan = sqlalchemy_lifespan(Base, engine)
242+
lifespan = sqlalchemy_lifespan(Base, engine, cleanup_db_file=True)
243243
app = EnrichMCP("My ORM API", lifespan=lifespan)
244244
include_sqlalchemy_models(app, Base)
245245
app.run()

docs/sqlalchemy.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,5 @@ Pagination parameters `page` and `page_size` are available on the generated
6767
`sqlalchemy_lifespan` automatically creates tables on startup and yields a
6868
`session_factory` that resolvers can use. Providing a `seed` function is
6969
optional and useful only for loading sample data during development or tests.
70+
If you are using a temporary SQLite file and want it removed on shutdown,
71+
pass `cleanup_db_file=True`.

examples/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ Both shop examples include the same core functionality but demonstrate different
5656
A version of the shop example built with SQLAlchemy ORM models. All entities are
5757
declared using SQLAlchemy and registered through `include_sqlalchemy_models`,
5858
which automatically creates CRUD endpoints and relationship resolvers. The
59-
`sqlalchemy_lifespan` helper manages the async engine and seeds the SQLite
60-
database on first run.
59+
`sqlalchemy_lifespan` helper manages the async engine, seeds the SQLite
60+
database on first run, and removes the file on shutdown when using
61+
`cleanup_db_file=True`.
6162

6263
To run this example:
6364

@@ -85,4 +86,4 @@ python app.py
8586
```
8687

8788
Stop the background server when finished. The gateway listens on port 8000 and
88-
provides the same schema-driven interface as the other examples.
89+
provides the same schema-driven interface as the other examples.

examples/sqlalchemy_shop/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ The app will:
4747

4848
The lifecycle is managed using the `sqlalchemy_lifespan` helper from
4949
`enrichmcp.sqlalchemy`, which provides a session factory to all resources.
50+
Passing `cleanup_db_file=True` removes the `shop.db` file when the app shuts
51+
down.
5052

5153
## Automatic Endpoints
5254

examples/sqlalchemy_shop/app.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,8 @@ async def seed_database(session: AsyncSession) -> None:
262262

263263
# Application instance will be created after defining the lifespan
264264

265-
lifespan = sqlalchemy_lifespan(Base, engine, seed=seed_database)
265+
lifespan = sqlalchemy_lifespan(Base, engine, seed=seed_database, cleanup_db_file=True)
266+
266267

267268
app = EnrichMCP(
268269
title="Shop API (SQLAlchemy)",

src/enrichmcp/sqlalchemy/lifecycle.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def sqlalchemy_lifespan(
2828
*,
2929
seed: Callable[[AsyncSession], Awaitable[None]] | None = None,
3030
session_kwargs: dict[str, Any] | None = None,
31+
cleanup_db_file: bool = False,
3132
) -> Lifespan:
3233
"""Create a lifespan that sets up tables and yields a session factory."""
3334

@@ -48,5 +49,14 @@ async def _lifespan(app: EnrichMCP) -> AsyncIterator[dict[str, Any]]:
4849
yield {"session_factory": session_factory}
4950
finally:
5051
await engine.dispose()
52+
if (
53+
cleanup_db_file
54+
and engine.url.database
55+
and engine.url.drivername.startswith("sqlite")
56+
):
57+
import os
58+
59+
if os.path.exists(engine.url.database):
60+
os.remove(engine.url.database)
5161

5262
return _lifespan

0 commit comments

Comments
 (0)