diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e5f9576..0f8ebf0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -13,7 +13,7 @@ jobs:
- name: ๐ง setup uv
uses: ./.github/uv
- name: ๐งช pytest
- run: uv run pytest --cov fastapi_async_sqla --cov-report=term-missing --cov-report=xml
+ run: uv run pytest --cov fastsqla --cov-report=term-missing --cov-report=xml
- name: "๐ codecov: upload test coverage"
uses: codecov/codecov-action@v4.2.0
env:
diff --git a/README.md b/README.md
index 865e07e..d3d4376 100644
--- a/README.md
+++ b/README.md
@@ -1,87 +1,218 @@
-# FastAPI-Async-SQLA
+# ๐ FastSQLA
-[](https://pypi.org/project/FastAPI-Async-SQLA/)
+[](https://pypi.org/project/FastSQLA/)
[](https://conventionalcommits.org)
-[](https://codecov.io/gh/hadrien/fastapi-async-sqla)
+[](https://codecov.io/gh/hadrien/fastsqla)
-FastAPI-Async-SQLA is an [SQLAlchemy] extension for [FastAPI]. It supports asynchronous
-SQLAlchemy sessions using SQLAlchemy >= 2.0 and provides pagination support.
+`FastSQLA` is an [`SQLAlchemy`] extension for [`FastAPI`].
+It supports asynchronous `SQLAlchemy` sessions and includes built-in custimizable
+pagination.
-# Installing
+## Features
-Using [pip](https://pip.pypa.io/):
+
+ Automatic SQLAlchemy configuration at app startup.
+
+ Using [`FastAPI` Lifespan](https://fastapi.tiangolo.com/advanced/events/#lifespan):
+```python
+from fastapi import FastAPI
+from fastsqla import lifespan
+
+app = FastAPI(lifespan=lifespan)
+```
+
+
+ Async SQLAlchemy session as a FastAPI dependency.
+
+```python
+...
+from fastsqla import Session
+from sqlalchemy import select
+...
+
+@app.get("/heros")
+async def get_heros(session:Session):
+ stmt = select(...)
+ result = await session.execute(stmt)
+ ...
+```
+
+
+ Built-in pagination.
+
+```python
+...
+from fastsqla import Page, Paginate
+from sqlalchemy import select
+...
+
+@app.get("/heros", response_model=Page[HeroModel])
+async def get_heros(paginate:Paginate):
+ return paginate(select(Hero))
+```
+
+
+ Allows pagination customization.
+
+```python
+...
+from fastapi import new_pagination
+...
+
+Paginate = new_pagination(min_page_size=5, max_page_size=500)
+
+@app.get("/heros", response_model=Page[HeroModel])
+async def get_heros(paginate:Paginate):
+ return paginate(select(Hero))
```
-pip install fastapi-async-sqla
+
+
+And more ...
+
+
+## Installing
+
+Using [uv](https://docs.astral.sh/uv/):
+```bash
+uv add fastsqla
```
-# Quick Example
+Using [pip](https://pip.pypa.io/):
+```
+pip install fastsqla
+```
-Assuming it runs against a DB with a table `user` with 2 columns `id` and `name`:
+## Quick Example
```python
-# main.py
+# example.py
+from http import HTTPStatus
+
from fastapi import FastAPI, HTTPException
-from fastapi_async_sqla import Base, Item, Page, Paginate, Session, lifespan
-from pydantic import BaseModel
+from pydantic import BaseModel, ConfigDict
from sqlalchemy import select
+from sqlalchemy.exc import IntegrityError
+from sqlalchemy.orm import Mapped, mapped_column
+from fastsqla import Base, Item, Page, Paginate, Session, lifespan
app = FastAPI(lifespan=lifespan)
-class User(Base):
- __tablename__ = "user"
+class Hero(Base):
+ __tablename__ = "hero"
+ id: Mapped[int] = mapped_column(primary_key=True)
+ name: Mapped[str] = mapped_column(unique=True)
+ secret_identity: Mapped[str]
-class UserIn(BaseModel):
+class HeroBase(BaseModel):
name: str
+ secret_identity: str
-class UserModel(UserIn):
+class HeroModel(HeroBase):
+ model_config = ConfigDict(from_attributes=True)
id: int
-@app.get("/users", response_model=Page[UserModel])
+@app.get("/heros", response_model=Page[HeroModel])
async def list_users(paginate: Paginate):
- return await paginate(select(User))
-
-
-@app.get("/users/{user_id}", response_model=Item[UserModel])
-async def get_user(user_id: int, session: Session):
- user = await session.get(User, user_id)
- if user is None:
- raise HTTPException(404)
- return {"data": user}
+ return await paginate(select(Hero))
+
+
+@app.get("/heros/{hero_id}", response_model=Item[HeroModel])
+async def get_user(hero_id: int, session: Session):
+ hero = await session.get(Hero, hero_id)
+ if hero is None:
+ raise HTTPException(HTTPStatus.NOT_FOUND, "Hero not found")
+ return {"data": hero}
+
+
+@app.post("/heros", response_model=Item[HeroModel])
+async def create_user(new_hero: HeroBase, session: Session):
+ hero = Hero(**new_hero.model_dump())
+ session.add(hero)
+ try:
+ await session.flush()
+ except IntegrityError:
+ raise HTTPException(HTTPStatus.CONFLICT, "Duplicate hero name")
+ return {"data": hero}
+```
+> [!NOTE]
+> Sqlite is used for the sake of the example.
+> FastSQLA is compatible with all async db drivers that SQLAlchemy is compatible with.
-@app.post("/users", response_model=Item[UserModel])
-async def create_user(new_user: UserIn, session: Session):
- user = User(**new_user.model_dump())
- session.add(user)
- await session.flush()
- return {"data": user}
-```
+
+ Create an sqlite3 db:
-Creating a db using `sqlite3`:
```bash
sqlite3 db.sqlite <
+
+
+ Install dependencies & run the app
+
```bash
-pip install aiosqlite
+pip install uvicorn aiosqlite fastsqla
+sqlalchemy_url=sqlite+aiosqlite:///db.sqlite?check_same_thread=false uvicorn example:app
```
-Running the app:
+
+
+Execute `GET /heros?offset=10`:
+
```bash
-sqlalchemy_url=sqlite+aiosqlite:///db.sqlite?check_same_thread=false uvicorn main:app
+curl -X 'GET' \
+'http://127.0.0.1:8000/heros?offset=10&limit=10' \
+-H 'accept: application/json'
+```
+Returns:
+```json
+{
+ "data": [
+ {
+ "name": "The Flash",
+ "secret_identity": "Barry Allen",
+ "id": 11
+ },
+ {
+ "name": "Green Lantern",
+ "secret_identity": "Hal Jordan",
+ "id": 12
+ }
+ ],
+ "meta": {
+ "offset": 10,
+ "total_items": 12,
+ "total_pages": 2,
+ "page_number": 2
+ }
+}
```
-[aiosqlite]: https://github.com/omnilib/aiosqlite
-[FastAPI]: https://fastapi.tiangolo.com/
-[SQLAlchemy]: http://sqlalchemy.org/
+[`FastAPI`]: https://fastapi.tiangolo.com/
+[`SQLAlchemy`]: http://sqlalchemy.org/
diff --git a/pyproject.toml b/pyproject.toml
index 0401549..22cd72a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
-name = "FastAPI-Async-SQLA"
+name = "FastSQLA"
version = "0.2.3"
-description = "SQLAlchemy extension for FastAPI with support for asynchronous SQLAlchemy sessions and pagination."
+description = "SQLAlchemy extension for FastAPI that supports asynchronous sessions and includes built-in pagination."
readme = "README.md"
requires-python = ">=3.12"
authors = [{ name = "Hadrien David", email = "bonjour@hadriendavid.com" }]
@@ -33,18 +33,14 @@ classifiers = [
]
keywords = ["FastAPI", "SQLAlchemy", "AsyncIO"]
license = { text = "MIT License" }
-dependencies = [
- "fastapi>=0.115.6",
- "sqlalchemy[asyncio]>=2.0.34,<3",
- "structlog>=24.4.0",
-]
+dependencies = ["fastapi>=0.115.6", "sqlalchemy[asyncio]>=2.0.37", "structlog>=24.4.0"]
[project.urls]
-Homepage = "https://github.com/hadrien/fastapi-async-sqla"
-Documentation = "https://github.com/hadrien/fastapi-async-sqla"
-Repository = "https://github.com/hadrien/fastapi-async-sqla"
-Issues = "https://github.com/hadrien/fastapi-async-sqla/issues"
-Changelog = "https://github.com/hadrien/fastapi-async-sqla/releases"
+Homepage = "https://github.com/hadrien/fastsqla"
+Documentation = "https://github.com/hadrien/fastsqla"
+Repository = "https://github.com/hadrien/fastsqla"
+Issues = "https://github.com/hadrien/fastsqla/issues"
+Changelog = "https://github.com/hadrien/fastsqla/releases"
[tool.uv]
package = true
diff --git a/src/fastapi_async_sqla.py b/src/fastsqla.py
similarity index 97%
rename from src/fastapi_async_sqla.py
rename to src/fastsqla.py
index a5077f8..e8e6269 100644
--- a/src/fastapi_async_sqla.py
+++ b/src/fastsqla.py
@@ -40,7 +40,7 @@ class Base(DeclarativeBase, DeferredReflection):
class State(TypedDict):
- fastapi_async_sqla_engine: AsyncEngine
+ fastsqla_engine: AsyncEngine
@asynccontextmanager
@@ -60,7 +60,7 @@ async def lifespan(_) -> AsyncGenerator[State, None]:
await logger.ainfo("Configured SQLAlchemy.")
- yield {"fastapi_async_sqla_engine": engine}
+ yield {"fastsqla_engine": engine}
SessionFactory.configure(bind=None)
await engine.dispose()
@@ -120,7 +120,7 @@ class Collection(BaseModel, Generic[T]):
data: list[T]
-class Page(Collection):
+class Page(Collection[T]):
meta: Meta
diff --git a/tests/conftest.py b/tests/conftest.py
index 1f9aea8..1d6335b 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -32,7 +32,7 @@ async def session(engine):
def tear_down():
from sqlalchemy.orm import clear_mappers
- from fastapi_async_sqla import Base
+ from fastsqla import Base
yield
diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py
index 12ca0bc..50707e6 100644
--- a/tests/integration/conftest.py
+++ b/tests/integration/conftest.py
@@ -6,7 +6,7 @@
@fixture
def app(environ):
- from fastapi_async_sqla import lifespan
+ from fastsqla import lifespan
app = FastAPI(lifespan=lifespan)
return app
diff --git a/tests/integration/test_base.py b/tests/integration/test_base.py
index e39f38d..cc07211 100644
--- a/tests/integration/test_base.py
+++ b/tests/integration/test_base.py
@@ -17,7 +17,7 @@ async def setup_tear_down(engine):
async def test_lifespan_reflects_user_table(environ):
- from fastapi_async_sqla import Base, lifespan
+ from fastsqla import Base, lifespan
class User(Base):
__tablename__ = "user"
diff --git a/tests/integration/test_pagination.py b/tests/integration/test_pagination.py
index b6aaccf..acfc6f0 100644
--- a/tests/integration/test_pagination.py
+++ b/tests/integration/test_pagination.py
@@ -56,7 +56,7 @@ async def setup_tear_down(engine, faker):
@fixture
def app(app):
- from fastapi_async_sqla import (
+ from fastsqla import (
Base,
Page,
Paginate,
diff --git a/tests/integration/test_session_dependency.py b/tests/integration/test_session_dependency.py
index 100bb75..d3431e1 100644
--- a/tests/integration/test_session_dependency.py
+++ b/tests/integration/test_session_dependency.py
@@ -24,7 +24,7 @@ async def setup_tear_down(engine):
@fixture
def app(setup_tear_down, app):
- from fastapi_async_sqla import Base, Item, Session
+ from fastsqla import Base, Item, Session
class User(Base):
__tablename__ = "user"
diff --git a/tests/unit/test_lifespan.py b/tests/unit/test_lifespan.py
index 6eb50ea..60f63e6 100644
--- a/tests/unit/test_lifespan.py
+++ b/tests/unit/test_lifespan.py
@@ -2,14 +2,14 @@
async def test_it_returns_state(environ):
- from fastapi_async_sqla import lifespan
+ from fastsqla import lifespan
async with lifespan(None) as state:
- assert "fastapi_async_sqla_engine" in state
+ assert "fastsqla_engine" in state
async def test_it_binds_an_sqla_engine_to_sessionmaker(environ):
- from fastapi_async_sqla import SessionFactory, lifespan
+ from fastsqla import SessionFactory, lifespan
assert SessionFactory.kw["bind"] is None
@@ -22,7 +22,7 @@ async def test_it_binds_an_sqla_engine_to_sessionmaker(environ):
async def test_it_fails_on_a_missing_sqlalchemy_url(monkeypatch):
- from fastapi_async_sqla import lifespan
+ from fastsqla import lifespan
monkeypatch.delenv("SQLALCHEMY_URL", raising=False)
with raises(Exception) as raise_info:
@@ -33,7 +33,7 @@ async def test_it_fails_on_a_missing_sqlalchemy_url(monkeypatch):
async def test_it_fails_on_not_async_engine(monkeypatch):
- from fastapi_async_sqla import lifespan
+ from fastsqla import lifespan
monkeypatch.setenv("SQLALCHEMY_URL", "sqlite:///:memory:")
with raises(Exception) as raise_info:
diff --git a/tests/unit/test_open_session.py b/tests/unit/test_open_session.py
index 06b7f86..74bf53f 100644
--- a/tests/unit/test_open_session.py
+++ b/tests/unit/test_open_session.py
@@ -11,7 +11,7 @@ def tablename(request):
@fixture(autouse=True)
async def setup_tear_down(engine, tablename):
- from fastapi_async_sqla import SessionFactory
+ from fastsqla import SessionFactory
async with engine.connect() as conn:
await conn.execute(text(f"create table {tablename} (data text unique)"))
@@ -22,7 +22,7 @@ async def setup_tear_down(engine, tablename):
async def test_it_commits_on_success(engine, tablename):
- from fastapi_async_sqla import open_session
+ from fastsqla import open_session
async with open_session() as session:
await session.execute(text(f"insert into {tablename} values ('OK')"))
@@ -34,9 +34,9 @@ async def test_it_commits_on_success(engine, tablename):
async def test_it_re_raises_when_committing_fails():
- from fastapi_async_sqla import open_session
+ from fastsqla import open_session
- with patch("fastapi_async_sqla.SessionFactory") as SessionFactory:
+ with patch("fastsqla.SessionFactory") as SessionFactory:
session = AsyncMock()
session.commit.side_effect = Exception("Simulating a failure.")
SessionFactory.return_value = session
@@ -48,7 +48,7 @@ async def test_it_re_raises_when_committing_fails():
async def test_it_rollback_on_failure(engine, tablename):
- from fastapi_async_sqla import open_session
+ from fastsqla import open_session
with raises(Exception):
async with open_session() as session:
diff --git a/uv.lock b/uv.lock
index e5c52b9..fa1d7ae 100644
--- a/uv.lock
+++ b/uv.lock
@@ -254,8 +254,8 @@ wheels = [
]
[[package]]
-name = "fastapi-async-sqla"
-version = "0.2.1"
+name = "fastsqla"
+version = "0.2.3"
source = { editable = "." }
dependencies = [
{ name = "fastapi" },
@@ -283,7 +283,7 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "fastapi", specifier = ">=0.115.6" },
- { name = "sqlalchemy", extras = ["asyncio"], specifier = ">=2.0.34,<3" },
+ { name = "sqlalchemy", extras = ["asyncio"], specifier = ">=2.0.37" },
{ name = "structlog", specifier = ">=24.4.0" },
]
@@ -330,19 +330,35 @@ wheels = [
[[package]]
name = "greenlet"
-version = "3.0.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/17/14/3bddb1298b9a6786539ac609ba4b7c9c0842e12aa73aaa4d8d73ec8f8185/greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491", size = 182013 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a2/2f/461615adc53ba81e99471303b15ac6b2a6daa8d2a0f7f77fd15605e16d5b/greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be", size = 273085 },
- { url = "https://files.pythonhosted.org/packages/e9/55/2c3cfa3cdbb940cf7321fbcf544f0e9c74898eed43bf678abf416812d132/greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e", size = 660514 },
- { url = "https://files.pythonhosted.org/packages/38/77/efb21ab402651896c74f24a172eb4d7479f9f53898bd5e56b9e20bb24ffd/greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676", size = 674295 },
- { url = "https://files.pythonhosted.org/packages/74/3a/92f188ace0190f0066dca3636cf1b09481d0854c46e92ec5e29c7cefe5b1/greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc", size = 669395 },
- { url = "https://files.pythonhosted.org/packages/63/0f/847ed02cdfce10f0e6e3425cd054296bddb11a17ef1b34681fa01a055187/greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230", size = 670455 },
- { url = "https://files.pythonhosted.org/packages/bd/37/56b0da468a85e7704f3b2bc045015301bdf4be2184a44868c71f6dca6fe2/greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf", size = 625692 },
- { url = "https://files.pythonhosted.org/packages/7c/68/b5f4084c0a252d7e9c0d95fc1cfc845d08622037adb74e05be3a49831186/greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305", size = 1152597 },
- { url = "https://files.pythonhosted.org/packages/a4/fa/31e22345518adcd69d1d6ab5087a12c178aa7f3c51103f6d5d702199d243/greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6", size = 1181043 },
- { url = "https://files.pythonhosted.org/packages/53/80/3d94d5999b4179d91bcc93745d1b0815b073d61be79dd546b840d17adb18/greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2", size = 293635 },
+version = "3.1.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 },
+ { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 },
+ { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 },
+ { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035 },
+ { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105 },
+ { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077 },
+ { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 },
+ { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 },
+ { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 },
+ { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 },
+ { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 },
+ { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 },
+ { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 },
+ { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 },
+ { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 },
+ { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 },
+ { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 },
+ { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 },
+ { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 },
+ { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 },
+ { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 },
+ { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 },
+ { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 },
+ { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 },
+ { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 },
]
[[package]]
@@ -906,23 +922,31 @@ wheels = [
[[package]]
name = "sqlalchemy"
-version = "2.0.34"
+version = "2.0.37"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "greenlet", marker = "(python_full_version < '3.13' and platform_machine == 'AMD64') or (python_full_version < '3.13' and platform_machine == 'WIN32') or (python_full_version < '3.13' and platform_machine == 'aarch64') or (python_full_version < '3.13' and platform_machine == 'amd64') or (python_full_version < '3.13' and platform_machine == 'ppc64le') or (python_full_version < '3.13' and platform_machine == 'win32') or (python_full_version < '3.13' and platform_machine == 'x86_64')" },
+ { name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/90/fa/ca0fdd7b6b0cf53a8237a8ee7e487f8be16e4a2ee6d840d6e8e105cd9c86/sqlalchemy-2.0.34.tar.gz", hash = "sha256:10d8f36990dd929690666679b0f42235c159a7051534adb135728ee52828dd22", size = 9556527 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f9/76/62eb5c62593d6d351f17202aa532f17b91c51b1b04e24a3a97530cb6118e/SQLAlchemy-2.0.34-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:53e68b091492c8ed2bd0141e00ad3089bcc6bf0e6ec4142ad6505b4afe64163e", size = 2089191 },
- { url = "https://files.pythonhosted.org/packages/8a/7c/d43a14aef45bcb196f017ba2783eb3e42dd4c65c43be8b9f29bb5ec7d131/SQLAlchemy-2.0.34-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bcd18441a49499bf5528deaa9dee1f5c01ca491fc2791b13604e8f972877f812", size = 2079662 },
- { url = "https://files.pythonhosted.org/packages/b7/25/ec59e5d3643d49d57ae59a62b6e5b3da39344617ce249f2561bfb4ac0458/SQLAlchemy-2.0.34-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:165bbe0b376541092bf49542bd9827b048357f4623486096fc9aaa6d4e7c59a2", size = 3229161 },
- { url = "https://files.pythonhosted.org/packages/fd/2e/e6129761dd5588a5623c6051c31e45935b72a5b17ed87b209e39a0b2a25c/SQLAlchemy-2.0.34-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3330415cd387d2b88600e8e26b510d0370db9b7eaf984354a43e19c40df2e2b", size = 3240054 },
- { url = "https://files.pythonhosted.org/packages/70/08/4f994445215d7932bf2a490570fef9a5d1ba42cdf1cc9c48a6f7f04d1cfc/SQLAlchemy-2.0.34-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97b850f73f8abbffb66ccbab6e55a195a0eb655e5dc74624d15cff4bfb35bd74", size = 3175538 },
- { url = "https://files.pythonhosted.org/packages/5e/19/4d4cc024cd7d50e25bf1c1ba61974b2b6e2fab8ea22f1569c47380b34e95/SQLAlchemy-2.0.34-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee4c6917857fd6121ed84f56d1dc78eb1d0e87f845ab5a568aba73e78adf83", size = 3202149 },
- { url = "https://files.pythonhosted.org/packages/87/02/7ada4b6bfd5421aa7d65bd0ee9d76acc15b53ae26378b2ab8bba1ba3f78f/SQLAlchemy-2.0.34-cp312-cp312-win32.whl", hash = "sha256:fbb034f565ecbe6c530dff948239377ba859420d146d5f62f0271407ffb8c580", size = 2059547 },
- { url = "https://files.pythonhosted.org/packages/ad/fc/d1315ddb8529c768789954350268cd53167747649ddb709517c5e0a15c7e/SQLAlchemy-2.0.34-cp312-cp312-win_amd64.whl", hash = "sha256:707c8f44931a4facd4149b52b75b80544a8d824162602b8cd2fe788207307f9a", size = 2085274 },
- { url = "https://files.pythonhosted.org/packages/09/14/5c9b872fba29ccedeb905d0a5c203ad86287b8bb1bb8eda96bfe8a05f65b/SQLAlchemy-2.0.34-py3-none-any.whl", hash = "sha256:7286c353ee6475613d8beff83167374006c6b3e3f0e6491bfe8ca610eb1dec0f", size = 1880671 },
+sdist = { url = "https://files.pythonhosted.org/packages/3b/20/93ea2518df4d7a14ebe9ace9ab8bb92aaf7df0072b9007644de74172b06c/sqlalchemy-2.0.37.tar.gz", hash = "sha256:12b28d99a9c14eaf4055810df1001557176716de0167b91026e648e65229bffb", size = 9626249 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/86/62/e5de4a5e0c4f5ceffb2b461aaa2378c0ee00642930a8c38e5b80338add0f/SQLAlchemy-2.0.37-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2952748ecd67ed3b56773c185e85fc084f6bdcdec10e5032a7c25a6bc7d682ef", size = 2102692 },
+ { url = "https://files.pythonhosted.org/packages/01/44/3b65f4f16abeffd611da0ebab9e3aadfca45d041a78a67835c41c6d28289/SQLAlchemy-2.0.37-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3151822aa1db0eb5afd65ccfafebe0ef5cda3a7701a279c8d0bf17781a793bb4", size = 2093079 },
+ { url = "https://files.pythonhosted.org/packages/a4/d8/e3a6622e86e3ae3a41ba470d1bb095c1f2dedf6b71feae0b4b94b5951017/SQLAlchemy-2.0.37-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaa8039b6d20137a4e02603aba37d12cd2dde7887500b8855356682fc33933f4", size = 3242509 },
+ { url = "https://files.pythonhosted.org/packages/3a/ef/5a53a6a60ac5a5d4ed28959317dac1ff72bc16773ccd9b3fe79713fe27f3/SQLAlchemy-2.0.37-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cdba1f73b64530c47b27118b7053b8447e6d6f3c8104e3ac59f3d40c33aa9fd", size = 3253368 },
+ { url = "https://files.pythonhosted.org/packages/67/f2/30f5012379031cd5389eb06455282f926a4f99258e5ee5ccdcea27f30d67/SQLAlchemy-2.0.37-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1b2690456528a87234a75d1a1644cdb330a6926f455403c8e4f6cad6921f9098", size = 3188655 },
+ { url = "https://files.pythonhosted.org/packages/fe/df/905499aa051605aeda62c1faf33d941ffb7fda291159ab1c24ef5207a079/SQLAlchemy-2.0.37-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf5ae8a9dcf657fd72144a7fd01f243236ea39e7344e579a121c4205aedf07bb", size = 3215281 },
+ { url = "https://files.pythonhosted.org/packages/94/54/f2769e7e356520f75016d82ca43ed85e47ba50e636a34124db4625ae5976/SQLAlchemy-2.0.37-cp312-cp312-win32.whl", hash = "sha256:ea308cec940905ba008291d93619d92edaf83232ec85fbd514dcb329f3192761", size = 2072972 },
+ { url = "https://files.pythonhosted.org/packages/c2/7f/241f059e0b7edb85845368f43964d6b0b41733c2f7fffaa993f8e66548a5/SQLAlchemy-2.0.37-cp312-cp312-win_amd64.whl", hash = "sha256:635d8a21577341dfe4f7fa59ec394b346da12420b86624a69e466d446de16aff", size = 2098597 },
+ { url = "https://files.pythonhosted.org/packages/45/d1/e63e56ceab148e69f545703a74b90c8c6dc0a04a857e4e63a4c07a23cf91/SQLAlchemy-2.0.37-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8c4096727193762e72ce9437e2a86a110cf081241919ce3fab8e89c02f6b6658", size = 2097968 },
+ { url = "https://files.pythonhosted.org/packages/fd/e5/93ce63310347062bd42aaa8b6785615c78539787ef4380252fcf8e2dcee3/SQLAlchemy-2.0.37-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e4fb5ac86d8fe8151966814f6720996430462e633d225497566b3996966b9bdb", size = 2088445 },
+ { url = "https://files.pythonhosted.org/packages/1b/8c/d0e0081c09188dd26040fc8a09c7d87f539e1964df1ac60611b98ff2985a/SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e56a139bfe136a22c438478a86f8204c1eb5eed36f4e15c4224e4b9db01cb3e4", size = 3174880 },
+ { url = "https://files.pythonhosted.org/packages/79/f7/3396038d8d4ea92c72f636a007e2fac71faae0b59b7e21af46b635243d09/SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f95fc8e3f34b5f6b3effb49d10ac97c569ec8e32f985612d9b25dd12d0d2e94", size = 3188226 },
+ { url = "https://files.pythonhosted.org/packages/ef/33/7a1d85716b29c86a744ed43690e243cb0e9c32e3b68a67a97eaa6b49ef66/SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c505edd429abdfe3643fa3b2e83efb3445a34a9dc49d5f692dd087be966020e0", size = 3121425 },
+ { url = "https://files.pythonhosted.org/packages/27/11/fa63a77c88eb2f79bb8b438271fbacd66a546a438e4eaba32d62f11298e2/SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:12b0f1ec623cccf058cf21cb544f0e74656618165b083d78145cafde156ea7b6", size = 3149589 },
+ { url = "https://files.pythonhosted.org/packages/b6/04/fcdd103b6871f2110460b8275d1c4828daa806997b0fa5a01c1cd7fd522d/SQLAlchemy-2.0.37-cp313-cp313-win32.whl", hash = "sha256:293f9ade06b2e68dd03cfb14d49202fac47b7bb94bffcff174568c951fbc7af2", size = 2070746 },
+ { url = "https://files.pythonhosted.org/packages/d4/7c/e024719205bdc1465b7b7d3d22ece8e1ad57bc7d76ef6ed78bb5f812634a/SQLAlchemy-2.0.37-cp313-cp313-win_amd64.whl", hash = "sha256:d70f53a0646cc418ca4853da57cf3ddddbccb8c98406791f24426f2dd77fd0e2", size = 2094612 },
+ { url = "https://files.pythonhosted.org/packages/3b/36/59cc97c365f2f79ac9f3f51446cae56dfd82c4f2dd98497e6be6de20fb91/SQLAlchemy-2.0.37-py3-none-any.whl", hash = "sha256:a8998bf9f8658bd3839cbc44ddbe982955641863da0c1efe5b00c1ab4f5c16b1", size = 1894113 },
]
[package.optional-dependencies]