fastapi-sqla 3.1.1__py3-none-any.whl → 3.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fastapi-sqla might be problematic. Click here for more details.
- fastapi_sqla/_pytest_plugin.py +1 -6
- fastapi_sqla/async_pagination.py +14 -14
- fastapi_sqla/aws_rds_iam_support.py +2 -1
- fastapi_sqla/base.py +0 -1
- fastapi_sqla/models.py +1 -1
- fastapi_sqla/pagination.py +22 -22
- fastapi_sqla/sqla.py +5 -8
- {fastapi_sqla-3.1.1.dist-info → fastapi_sqla-3.2.1.dist-info}/METADATA +6 -7
- fastapi_sqla-3.2.1.dist-info/RECORD +16 -0
- {fastapi_sqla-3.1.1.dist-info → fastapi_sqla-3.2.1.dist-info}/WHEEL +1 -1
- fastapi_sqla-3.1.1.dist-info/RECORD +0 -16
- {fastapi_sqla-3.1.1.dist-info → fastapi_sqla-3.2.1.dist-info}/LICENSE +0 -0
- {fastapi_sqla-3.1.1.dist-info → fastapi_sqla-3.2.1.dist-info}/entry_points.txt +0 -0
fastapi_sqla/_pytest_plugin.py
CHANGED
|
@@ -28,7 +28,6 @@ def db_host():
|
|
|
28
28
|
When CI key is set in environment variables, it uses `postgres` as host name else,
|
|
29
29
|
host used is `localhost`
|
|
30
30
|
"""
|
|
31
|
-
|
|
32
31
|
return "postgres" if "CI" in os.environ else "localhost"
|
|
33
32
|
|
|
34
33
|
|
|
@@ -38,7 +37,6 @@ def db_user():
|
|
|
38
37
|
|
|
39
38
|
postgres
|
|
40
39
|
"""
|
|
41
|
-
|
|
42
40
|
return "postgres"
|
|
43
41
|
|
|
44
42
|
|
|
@@ -48,7 +46,6 @@ def db_url(db_host, db_user):
|
|
|
48
46
|
|
|
49
47
|
db url example postgresql://{db_user}@{db_host}/postgres
|
|
50
48
|
"""
|
|
51
|
-
|
|
52
49
|
return f"postgresql://{db_user}@{db_host}/postgres"
|
|
53
50
|
|
|
54
51
|
|
|
@@ -107,7 +104,6 @@ def sqla_reflection(sqla_modules, sqla_connection):
|
|
|
107
104
|
@fixture
|
|
108
105
|
def patch_engine_from_config(request, sqla_connection):
|
|
109
106
|
"""So that all DB operations are never written to db for real."""
|
|
110
|
-
|
|
111
107
|
if "dont_patch_engines" in request.keywords:
|
|
112
108
|
yield
|
|
113
109
|
else:
|
|
@@ -153,7 +149,7 @@ def async_sqlalchemy_url(db_url):
|
|
|
153
149
|
return format_async_async_sqlalchemy_url(db_url)
|
|
154
150
|
|
|
155
151
|
|
|
156
|
-
if asyncio_support:
|
|
152
|
+
if asyncio_support:
|
|
157
153
|
|
|
158
154
|
@fixture
|
|
159
155
|
def async_engine(async_sqlalchemy_url):
|
|
@@ -167,7 +163,6 @@ if asyncio_support: # noqa: C901
|
|
|
167
163
|
@fixture
|
|
168
164
|
async def patch_new_engine(request, async_sqla_connection):
|
|
169
165
|
"""So that all async DB operations are never written to db for real."""
|
|
170
|
-
|
|
171
166
|
if "dont_patch_engines" in request.keywords:
|
|
172
167
|
yield
|
|
173
168
|
else:
|
fastapi_sqla/async_pagination.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import math
|
|
2
|
-
from collections.abc import Awaitable, Callable
|
|
3
|
-
from typing import Annotated,
|
|
2
|
+
from collections.abc import Awaitable, Callable, Iterator
|
|
3
|
+
from typing import Annotated, Optional, Union, cast
|
|
4
4
|
|
|
5
5
|
from fastapi import Depends, Query
|
|
6
6
|
from sqlalchemy.sql import Select, func, select
|
|
7
7
|
|
|
8
8
|
from fastapi_sqla.async_sqla import AsyncSessionDependency, SqlaAsyncSession
|
|
9
|
-
from fastapi_sqla.models import Page
|
|
9
|
+
from fastapi_sqla.models import Meta, Page
|
|
10
10
|
from fastapi_sqla.sqla import _DEFAULT_SESSION_KEY
|
|
11
11
|
|
|
12
12
|
QueryCountDependency = Callable[..., Awaitable[int]]
|
|
@@ -19,7 +19,7 @@ PaginateDependency = Union[DefaultDependency, WithQueryCountDependency]
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
async def default_query_count(session: SqlaAsyncSession, query: Select) -> int:
|
|
22
|
-
result = await session.execute(select(func.count()).select_from(query.subquery()))
|
|
22
|
+
result = await session.execute(select(func.count()).select_from(query.subquery()))
|
|
23
23
|
return cast(int, result.scalar())
|
|
24
24
|
|
|
25
25
|
|
|
@@ -33,20 +33,20 @@ async def paginate_query(
|
|
|
33
33
|
scalars: bool = True,
|
|
34
34
|
) -> Page:
|
|
35
35
|
total_pages = math.ceil(total_items / limit)
|
|
36
|
-
page_number = offset / limit + 1
|
|
37
|
-
query = query.offset(offset).limit(limit)
|
|
36
|
+
page_number = math.floor(offset / limit + 1)
|
|
37
|
+
query = query.offset(offset).limit(limit)
|
|
38
38
|
result = await session.execute(query)
|
|
39
39
|
data = iter(
|
|
40
|
-
cast(Iterator, result.unique().scalars() if scalars else result.mappings())
|
|
40
|
+
cast(Iterator, result.unique().scalars() if scalars else result.mappings())
|
|
41
41
|
)
|
|
42
42
|
return Page(
|
|
43
|
-
data=data,
|
|
44
|
-
meta=
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
data=data, # type: ignore # Expected to be a list
|
|
44
|
+
meta=Meta(
|
|
45
|
+
offset=offset,
|
|
46
|
+
total_items=total_items,
|
|
47
|
+
total_pages=total_pages,
|
|
48
|
+
page_number=page_number,
|
|
49
|
+
),
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
|
|
@@ -20,7 +20,8 @@ def setup(engine: Engine):
|
|
|
20
20
|
aws_rds_iam_enabled = lc_environ.get("fastapi_sqla_aws_rds_iam_enabled") == "true"
|
|
21
21
|
|
|
22
22
|
if aws_rds_iam_enabled:
|
|
23
|
-
|
|
23
|
+
if not boto3_installed:
|
|
24
|
+
raise ImportError(f"boto3 is required for RDS IAM : {boto3_installed_err}")
|
|
24
25
|
# Cache the client at startup
|
|
25
26
|
get_rds_client()
|
|
26
27
|
event.listen(engine, "do_connect", set_connection_token)
|
fastapi_sqla/base.py
CHANGED
fastapi_sqla/models.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import Generic, TypeVar
|
|
|
3
3
|
from pydantic import BaseModel, Field
|
|
4
4
|
from pydantic import __version__ as pydantic_version
|
|
5
5
|
|
|
6
|
-
major, _, _ =
|
|
6
|
+
major, _, _ = (int(v) for v in pydantic_version.split("."))
|
|
7
7
|
is_pydantic2 = major == 2
|
|
8
8
|
if is_pydantic2:
|
|
9
9
|
GenericModel = BaseModel
|
fastapi_sqla/pagination.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import math
|
|
2
|
-
from collections.abc import Callable
|
|
2
|
+
from collections.abc import Callable, Iterator
|
|
3
3
|
from functools import singledispatch
|
|
4
|
-
from typing import Annotated,
|
|
4
|
+
from typing import Annotated, Optional, Union, cast
|
|
5
5
|
|
|
6
6
|
from fastapi import Depends, Query
|
|
7
7
|
from sqlalchemy.orm import Query as LegacyQuery
|
|
8
8
|
from sqlalchemy.sql import Select, func, select
|
|
9
9
|
|
|
10
|
-
from fastapi_sqla.models import Page
|
|
10
|
+
from fastapi_sqla.models import Meta, Page
|
|
11
11
|
from fastapi_sqla.sqla import _DEFAULT_SESSION_KEY, SessionDependency, SqlaSession
|
|
12
12
|
|
|
13
13
|
DbQuery = Union[LegacyQuery, Select]
|
|
@@ -52,7 +52,7 @@ def paginate_query(
|
|
|
52
52
|
limit: int,
|
|
53
53
|
scalars: bool = True,
|
|
54
54
|
) -> Page: # pragma: no cover
|
|
55
|
-
"Dispatch on registered functions based on `query` type"
|
|
55
|
+
"""Dispatch on registered functions based on `query` type"""
|
|
56
56
|
raise NotImplementedError(f"no paginate_query registered for type {type(query)!r}")
|
|
57
57
|
|
|
58
58
|
|
|
@@ -66,15 +66,15 @@ def _paginate_legacy(
|
|
|
66
66
|
scalars: bool = True,
|
|
67
67
|
) -> Page:
|
|
68
68
|
total_pages = math.ceil(total_items / limit)
|
|
69
|
-
page_number = offset / limit + 1
|
|
69
|
+
page_number = math.floor(offset / limit + 1)
|
|
70
70
|
return Page(
|
|
71
|
-
data=query.offset(offset).limit(limit).all(),
|
|
72
|
-
meta=
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
data=query.offset(offset).limit(limit).all(),
|
|
72
|
+
meta=Meta(
|
|
73
|
+
offset=offset,
|
|
74
|
+
total_items=total_items,
|
|
75
|
+
total_pages=total_pages,
|
|
76
|
+
page_number=page_number,
|
|
77
|
+
),
|
|
78
78
|
)
|
|
79
79
|
|
|
80
80
|
|
|
@@ -89,20 +89,20 @@ def _paginate(
|
|
|
89
89
|
scalars: bool = True,
|
|
90
90
|
) -> Page:
|
|
91
91
|
total_pages = math.ceil(total_items / limit)
|
|
92
|
-
page_number = offset / limit + 1
|
|
93
|
-
query = query.offset(offset).limit(limit)
|
|
92
|
+
page_number = math.floor(offset / limit + 1)
|
|
93
|
+
query = query.offset(offset).limit(limit)
|
|
94
94
|
result = session.execute(query)
|
|
95
95
|
data = iter(
|
|
96
|
-
cast(Iterator, result.unique().scalars() if scalars else result.mappings())
|
|
96
|
+
cast(Iterator, result.unique().scalars() if scalars else result.mappings())
|
|
97
97
|
)
|
|
98
98
|
return Page(
|
|
99
|
-
data=data,
|
|
100
|
-
meta=
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
data=data, # type: ignore # Expected to be a list
|
|
100
|
+
meta=Meta(
|
|
101
|
+
offset=offset,
|
|
102
|
+
total_items=total_items,
|
|
103
|
+
total_pages=total_pages,
|
|
104
|
+
page_number=page_number,
|
|
105
|
+
),
|
|
106
106
|
)
|
|
107
107
|
|
|
108
108
|
|
fastapi_sqla/sqla.py
CHANGED
|
@@ -2,7 +2,7 @@ import asyncio
|
|
|
2
2
|
import os
|
|
3
3
|
from collections.abc import Generator
|
|
4
4
|
from contextlib import contextmanager
|
|
5
|
-
from typing import Annotated
|
|
5
|
+
from typing import Annotated
|
|
6
6
|
|
|
7
7
|
import structlog
|
|
8
8
|
from fastapi import Depends, Request
|
|
@@ -24,7 +24,7 @@ except ImportError:
|
|
|
24
24
|
DeclarativeBase = declarative_base() # type: ignore
|
|
25
25
|
|
|
26
26
|
try:
|
|
27
|
-
from sqlmodel import Session as SqlaSession # type: ignore
|
|
27
|
+
from sqlmodel import Session as SqlaSession # type: ignore
|
|
28
28
|
|
|
29
29
|
except ImportError:
|
|
30
30
|
pass
|
|
@@ -165,14 +165,11 @@ async def add_session_to_request(
|
|
|
165
165
|
return response
|
|
166
166
|
|
|
167
167
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
class SessionDependency(Generic[S]):
|
|
168
|
+
class SessionDependency:
|
|
172
169
|
def __init__(self, key: str = _DEFAULT_SESSION_KEY) -> None:
|
|
173
170
|
self.key = key
|
|
174
171
|
|
|
175
|
-
def __call__(self, request: Request) ->
|
|
172
|
+
def __call__(self, request: Request) -> SqlaSession:
|
|
176
173
|
"""Yield the sqlalchemy session for that request.
|
|
177
174
|
|
|
178
175
|
It is meant to be used as a FastAPI dependency::
|
|
@@ -197,5 +194,5 @@ class SessionDependency(Generic[S]):
|
|
|
197
194
|
raise
|
|
198
195
|
|
|
199
196
|
|
|
200
|
-
default_session_dep = SessionDependency
|
|
197
|
+
default_session_dep = SessionDependency()
|
|
201
198
|
Session = Annotated[SqlaSession, Depends(default_session_dep)]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fastapi-sqla
|
|
3
|
-
Version: 3.
|
|
4
|
-
Summary: SQLAlchemy extension for FastAPI with support for pagination, asyncio, and pytest, ready for production.
|
|
3
|
+
Version: 3.2.1
|
|
4
|
+
Summary: SQLAlchemy extension for FastAPI with support for pagination, asyncio, SQLModel, and pytest, ready for production.
|
|
5
5
|
Home-page: https://github.com/dialoguemd/fastapi-sqla
|
|
6
6
|
License: MIT
|
|
7
7
|
Keywords: FastAPI,SQLAlchemy,asyncio,pytest,alembic
|
|
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.9
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.10
|
|
24
24
|
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
26
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
26
27
|
Classifier: Programming Language :: SQL
|
|
27
28
|
Classifier: Topic :: Internet
|
|
@@ -40,20 +41,18 @@ Requires-Dist: Faker (>=14.2.0,<15.0.0) ; extra == "tests"
|
|
|
40
41
|
Requires-Dist: alembic (>=1.4.3,<2.0.0) ; extra == "tests"
|
|
41
42
|
Requires-Dist: asgi_lifespan (>=1.0.1,<2.0.0) ; extra == "tests"
|
|
42
43
|
Requires-Dist: asyncpg (>=0.28.0,<0.29.0) ; extra == "asyncpg"
|
|
43
|
-
Requires-Dist: black (>=22.8.0,<23.0.0) ; extra == "tests"
|
|
44
44
|
Requires-Dist: boto3 (>=1.24.74,<2.0.0) ; extra == "aws-rds-iam"
|
|
45
45
|
Requires-Dist: fastapi (>=0.95.1)
|
|
46
|
-
Requires-Dist: greenlet (>=
|
|
46
|
+
Requires-Dist: greenlet (>=3.0.3,<4.0.0) ; extra == "tests"
|
|
47
47
|
Requires-Dist: httpx (>=0.23.0,<0.24.0) ; extra == "tests"
|
|
48
|
-
Requires-Dist:
|
|
49
|
-
Requires-Dist: mypy[tests] (>=0.991,<0.992) ; extra == "tests"
|
|
48
|
+
Requires-Dist: mypy[tests] (>=1.0.0,<2.0.0) ; extra == "tests"
|
|
50
49
|
Requires-Dist: pdbpp (>=0.10.2,<0.11.0) ; extra == "tests"
|
|
51
50
|
Requires-Dist: psycopg2 (>=2.8.6,<3.0.0) ; extra == "tests"
|
|
52
51
|
Requires-Dist: pydantic (>=1)
|
|
53
|
-
Requires-Dist: pylama (>=8.4.1,<9.0.0) ; extra == "tests"
|
|
54
52
|
Requires-Dist: pytest (>=7.2.1,<8.0.0) ; extra == "tests"
|
|
55
53
|
Requires-Dist: pytest-asyncio (>=0.19.0,<0.20.0) ; extra == "tests"
|
|
56
54
|
Requires-Dist: pytest-cov (>=2.10.1,<3.0.0) ; extra == "tests"
|
|
55
|
+
Requires-Dist: ruff (>=0.4.5,<0.5.0) ; extra == "tests"
|
|
57
56
|
Requires-Dist: sqlalchemy (>=1.3)
|
|
58
57
|
Requires-Dist: sqlmodel (>=0.0.14,<0.0.15) ; extra == "sqlmodel"
|
|
59
58
|
Requires-Dist: structlog (>=20)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
fastapi_sqla/__init__.py,sha256=G9bKR6L9EnLgzelE_BLnHgc1cOm0Yu0wMzwLAdyA8iE,1153
|
|
2
|
+
fastapi_sqla/_pytest_plugin.py,sha256=IQUlj-O874Sfuth254LBrEBGCrwH3rNHST9OMZl2pIk,5411
|
|
3
|
+
fastapi_sqla/async_pagination.py,sha256=3DHGUjvrpkbWMIc_BEX4GvM-_PTcn62K9z48ucTJlH0,3164
|
|
4
|
+
fastapi_sqla/async_sqla.py,sha256=7SXRH3DMsQvpjCQdvCBEjQhDZ4-cPAg175MlgNVBnCg,5888
|
|
5
|
+
fastapi_sqla/aws_aurora_support.py,sha256=4dxLKOqDccgLwFqlz81L6f4HzrOXMZkY7Zuf4t_310U,838
|
|
6
|
+
fastapi_sqla/aws_rds_iam_support.py,sha256=Uw-XaiwShMMWYKCvlSqXoxvtKMblCAvbCZ1m6BYVpJk,1257
|
|
7
|
+
fastapi_sqla/base.py,sha256=ssgt-EU7KrRg3IfvXoEZdCsDE4uQVhOVTFuamMmw-Kk,1546
|
|
8
|
+
fastapi_sqla/models.py,sha256=-B1xwINpTc9rEQd3KYHEC1s5s7jdVQkJ6Gy6xpmT13c,1108
|
|
9
|
+
fastapi_sqla/pagination.py,sha256=1gfIGcmt1OFspbRgtJ8AZOZdFd14DGRc4FkDgyh5bJ8,4517
|
|
10
|
+
fastapi_sqla/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
fastapi_sqla/sqla.py,sha256=irHuZaun027o9WJrdcZPavCN2vXNHupCWYhU5Dx-AqE,6348
|
|
12
|
+
fastapi_sqla-3.2.1.dist-info/LICENSE,sha256=8G0-nWLqi3xRYRrtRlTE8n1mkYJcnCRoZGUhv6ZE29c,1064
|
|
13
|
+
fastapi_sqla-3.2.1.dist-info/METADATA,sha256=48-YxU0G8S7GR5zDMrLLgRY5LyMA7v0ku29SiEnqihA,20848
|
|
14
|
+
fastapi_sqla-3.2.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
15
|
+
fastapi_sqla-3.2.1.dist-info/entry_points.txt,sha256=haa0EueKcRo8-AlJTpHBMn08wMBiULNGA53nkvaDWj0,53
|
|
16
|
+
fastapi_sqla-3.2.1.dist-info/RECORD,,
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
fastapi_sqla/__init__.py,sha256=G9bKR6L9EnLgzelE_BLnHgc1cOm0Yu0wMzwLAdyA8iE,1153
|
|
2
|
-
fastapi_sqla/_pytest_plugin.py,sha256=bjPzOuwk32Zo4hoOa_bGjTGhtHGPImh3RyiORqz-J84,5430
|
|
3
|
-
fastapi_sqla/async_pagination.py,sha256=r5APFbE-xpqQPF1aTG19ILOwYDhuIlHql3UF3mYZMt4,3176
|
|
4
|
-
fastapi_sqla/async_sqla.py,sha256=7SXRH3DMsQvpjCQdvCBEjQhDZ4-cPAg175MlgNVBnCg,5888
|
|
5
|
-
fastapi_sqla/aws_aurora_support.py,sha256=4dxLKOqDccgLwFqlz81L6f4HzrOXMZkY7Zuf4t_310U,838
|
|
6
|
-
fastapi_sqla/aws_rds_iam_support.py,sha256=YSJNhrxmhGN-GVk9PLMTmQSWTKZBvuorKkhc_XaoL44,1189
|
|
7
|
-
fastapi_sqla/base.py,sha256=0X7Gbt49rBHPiSFmNy5S2PT0dA4UBNnwrAesYSkaHBc,1606
|
|
8
|
-
fastapi_sqla/models.py,sha256=fesW7BqkwOA4iC345dbybzcV8Kz4-kLwREo38oqy_7A,1108
|
|
9
|
-
fastapi_sqla/pagination.py,sha256=uhsNRf9w2GzJQkF8eUUQZMAudPDi7tN5OCb00kz3Dn4,4515
|
|
10
|
-
fastapi_sqla/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
-
fastapi_sqla/sqla.py,sha256=zAkzY07bHEnOrmP7_dwO4Gdar18qm1ZbhB1RD3AZGrA,6427
|
|
12
|
-
fastapi_sqla-3.1.1.dist-info/LICENSE,sha256=8G0-nWLqi3xRYRrtRlTE8n1mkYJcnCRoZGUhv6ZE29c,1064
|
|
13
|
-
fastapi_sqla-3.1.1.dist-info/METADATA,sha256=caalhTVa91W2oBq_rXUhu5xahUzINgBpq39GRlsbvxc,20905
|
|
14
|
-
fastapi_sqla-3.1.1.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
|
15
|
-
fastapi_sqla-3.1.1.dist-info/entry_points.txt,sha256=haa0EueKcRo8-AlJTpHBMn08wMBiULNGA53nkvaDWj0,53
|
|
16
|
-
fastapi_sqla-3.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|