Skip to content

Commit b46b8f3

Browse files
authored
Merge pull request #28 from /issues/27
use SQLModel's AsyncSession when available, fallback to SQLAlchemy's
2 parents a76b7bb + 579f607 commit b46b8f3

File tree

14 files changed

+644
-129
lines changed

14 files changed

+644
-129
lines changed

.flake8

Lines changed: 0 additions & 4 deletions
This file was deleted.

.github/workflows/ci.yml

Lines changed: 26 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,23 @@ jobs:
1313
runs-on: ${{ matrix.os }}
1414
strategy:
1515
matrix:
16-
build: [linux_3.9, windows_3.9, mac_3.9]
16+
build: [linux_3.12, windows_3.12, mac_3.12]
1717
include:
18-
- build: linux_3.9
18+
- build: linux_3.12
1919
os: ubuntu-latest
20-
python: 3.9
21-
- build: windows_3.9
20+
python: 3.12
21+
- build: windows_3.12
2222
os: windows-latest
23-
python: 3.9
24-
- build: mac_3.9
23+
python: 3.12
24+
- build: mac_3.12
2525
os: macos-latest
26-
python: 3.9
26+
python: 3.12
2727
steps:
2828
- name: Checkout repository
29-
uses: actions/checkout@v2
29+
uses: actions/checkout@v4
3030

3131
- name: Set up Python ${{ matrix.python }}
32-
uses: actions/setup-python@v4
32+
uses: actions/setup-python@v5
3333
with:
3434
python-version: ${{ matrix.python }}
3535

@@ -38,59 +38,40 @@ jobs:
3838
python -m pip install --upgrade pip wheel
3939
pip install -r requirements.txt
4040
41-
# test all the builds apart from linux_3.8...
41+
# test all the builds apart from linux_3.12...
4242
- name: Test with pytest
43-
if: matrix.build != 'linux_3.9'
43+
if: matrix.build != 'linux_3.12'
4444
run: pytest
4545

46-
# only do the test coverage for linux_3.8
46+
# only do the test coverage for linux_3.12
4747
- name: Produce coverage report
48-
if: matrix.build == 'linux_3.9'
48+
if: matrix.build == 'linux_3.12'
4949
run: pytest --cov=fastapi_async_sqlalchemy --cov-report=xml
5050

5151
- name: Upload coverage report
52-
if: matrix.build == 'linux_3.9'
53-
uses: codecov/codecov-action@v1
52+
if: matrix.build == 'linux_3.12'
53+
uses: codecov/codecov-action@v4
5454
with:
5555
file: ./coverage.xml
56+
token: ${{ secrets.CODECOV_TOKEN }}
5657

57-
lint:
58-
name: lint
58+
ruff:
59+
name: ruff
5960
runs-on: ubuntu-latest
6061
steps:
6162
- name: Checkout repository
62-
uses: actions/checkout@v2
63+
uses: actions/checkout@v4
6364

6465
- name: Set up Python
65-
uses: actions/setup-python@v4
66+
uses: actions/setup-python@v5
6667
with:
67-
python-version: 3.9
68+
python-version: 3.12
6869

6970
- name: Install dependencies
70-
run: pip install flake8
71+
run: pip install ruff
7172

72-
- name: Run flake8
73-
run: flake8 --count .
73+
- name: Run ruff linter
74+
run: ruff check .
7475

75-
format:
76-
name: format
77-
runs-on: ubuntu-latest
78-
steps:
79-
- name: Checkout repository
80-
uses: actions/checkout@v2
81-
82-
- name: Set up Python
83-
uses: actions/setup-python@v4
84-
with:
85-
python-version: 3.9
86-
87-
- name: Install dependencies
88-
# isort needs all of the packages to be installed so it can
89-
# tell which are third party and which are first party
90-
run: pip install -r requirements.txt
91-
92-
- name: Check formatting of imports
93-
run: isort --check-only --diff --verbose
94-
95-
- name: Check formatting of code
96-
run: black . --check --diff
76+
- name: Run ruff formatter
77+
run: ruff format --check .

.github/workflows/codeql-analysis.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ jobs:
3939

4040
steps:
4141
- name: Checkout repository
42-
uses: actions/checkout@v2
42+
uses: actions/checkout@v4
4343

4444
# Initializes the CodeQL tools for scanning.
4545
- name: Initialize CodeQL
46-
uses: github/codeql-action/init@v1
46+
uses: github/codeql-action/init@v3
4747
with:
4848
languages: ${{ matrix.language }}
4949
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -54,7 +54,7 @@ jobs:
5454
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
5555
# If this step fails, then you should remove it and run the build manually (see below)
5656
- name: Autobuild
57-
uses: github/codeql-action/autobuild@v1
57+
uses: github/codeql-action/autobuild@v3
5858

5959
# ℹ️ Command-line programs to run using the OS shell.
6060
# 📚 https://git.io/JvXDl
@@ -68,4 +68,4 @@ jobs:
6868
# make release
6969

7070
- name: Perform CodeQL Analysis
71-
uses: github/codeql-action/analyze@v1
71+
uses: github/codeql-action/analyze@v3

.github/workflows/python-publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ jobs:
2121
runs-on: ubuntu-latest
2222

2323
steps:
24-
- uses: actions/checkout@v3
24+
- uses: actions/checkout@v4
2525
- name: Set up Python
26-
uses: actions/setup-python@v3
26+
uses: actions/setup-python@v5
2727
with:
2828
python-version: '3.x'
2929
- name: Install dependencies

.pre-commit-config.yaml

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,24 @@
11
exclude: (alembic|build|dist|docker|esign|kubernetes|migrations)
22

33
default_language_version:
4-
python: python3.8
4+
python: python3.12
55

66
repos:
77
- repo: https://github.com/pre-commit/pre-commit-hooks
8-
rev: v4.4.0
8+
rev: v5.0.0
99
hooks:
1010
- id: end-of-file-fixer
1111
- id: trailing-whitespace
12-
- repo: https://github.com/asottile/pyupgrade
13-
rev: v2.28.0
14-
hooks:
15-
- id: pyupgrade
16-
args:
17-
- --py37-plus
18-
- repo: https://github.com/myint/autoflake
19-
rev: v1.4
20-
hooks:
21-
- id: autoflake
22-
args:
23-
- --in-place
24-
- --remove-all-unused-imports
25-
- --expand-star-imports
26-
- --remove-duplicate-keys
27-
- --remove-unused-variables
28-
- repo: https://github.com/PyCQA/isort
29-
rev: 5.12.0
30-
hooks:
31-
- id: isort
32-
- repo: https://github.com/psf/black
33-
rev: 22.3.0
34-
hooks:
35-
- id: black
36-
- repo: https://github.com/PyCQA/flake8
37-
rev: 5.0.4
12+
13+
- repo: https://github.com/astral-sh/ruff-pre-commit
14+
rev: v0.12.4
3815
hooks:
39-
- id: flake8
40-
args:
41-
- --max-line-length=100
42-
- --ignore=E203, E501, W503
16+
- id: ruff
17+
args: [--fix, --unsafe-fixes]
18+
- id: ruff-format
19+
4320
- repo: https://github.com/pre-commit/mirrors-mypy
44-
rev: v0.982
21+
rev: v1.17.0
4522
hooks:
4623
- id: mypy
4724
additional_dependencies:
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from fastapi_async_sqlalchemy.middleware import (
22
SQLAlchemyMiddleware,
3-
db,
43
create_middleware_and_session_proxy,
4+
db,
55
)
66

77
__all__ = ["db", "SQLAlchemyMiddleware", "create_middleware_and_session_proxy"]
88

9-
__version__ = "0.7.0.dev4"
9+
__version__ = "0.7.0.dev5"

fastapi_async_sqlalchemy/middleware.py

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
11
import asyncio
22
from contextvars import ContextVar
3-
from typing import Dict, Optional, Union
3+
from typing import Dict, Optional, Type, Union
44

5-
from sqlalchemy.engine import Engine
65
from sqlalchemy.engine.url import URL
7-
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
6+
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine
87
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
98
from starlette.requests import Request
109
from starlette.types import ASGIApp
1110

12-
from fastapi_async_sqlalchemy.exceptions import MissingSessionError, SessionNotInitialisedError
11+
from fastapi_async_sqlalchemy.exceptions import (
12+
MissingSessionError,
13+
SessionNotInitialisedError,
14+
)
1315

1416
try:
15-
from sqlalchemy.ext.asyncio import async_sessionmaker # noqa: F811
17+
from sqlalchemy.ext.asyncio import async_sessionmaker
1618
except ImportError:
17-
from sqlalchemy.orm import sessionmaker as async_sessionmaker
19+
from sqlalchemy.orm import sessionmaker as async_sessionmaker # type: ignore
20+
21+
# Try to import SQLModel's AsyncSession which has the .exec() method
22+
try:
23+
from sqlmodel.ext.asyncio.session import AsyncSession as SQLModelAsyncSession
24+
25+
DefaultAsyncSession: Type[AsyncSession] = SQLModelAsyncSession # type: ignore
26+
except ImportError:
27+
DefaultAsyncSession: Type[AsyncSession] = AsyncSession # type: ignore
1828

1929

20-
def create_middleware_and_session_proxy():
30+
def create_middleware_and_session_proxy() -> tuple:
2131
_Session: Optional[async_sessionmaker] = None
2232
_session: ContextVar[Optional[AsyncSession]] = ContextVar("_session", default=None)
2333
_multi_sessions_ctx: ContextVar[bool] = ContextVar("_multi_sessions_context", default=False)
@@ -31,9 +41,9 @@ def __init__(
3141
self,
3242
app: ASGIApp,
3343
db_url: Optional[Union[str, URL]] = None,
34-
custom_engine: Optional[Engine] = None,
35-
engine_args: Dict = None,
36-
session_args: Dict = None,
44+
custom_engine: Optional[AsyncEngine] = None,
45+
engine_args: Optional[Dict] = None,
46+
session_args: Optional[Dict] = None,
3747
commit_on_exit: bool = False,
3848
):
3949
super().__init__(app)
@@ -44,13 +54,18 @@ def __init__(
4454
if not custom_engine and not db_url:
4555
raise ValueError("You need to pass a db_url or a custom_engine parameter.")
4656
if not custom_engine:
57+
if db_url is None:
58+
raise ValueError("db_url cannot be None when custom_engine is not provided")
4759
engine = create_async_engine(db_url, **engine_args)
4860
else:
4961
engine = custom_engine
5062

5163
nonlocal _Session
5264
_Session = async_sessionmaker(
53-
engine, class_=AsyncSession, expire_on_commit=False, **session_args
65+
engine,
66+
class_=DefaultAsyncSession,
67+
expire_on_commit=False,
68+
**session_args,
5469
)
5570

5671
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint):
@@ -115,7 +130,7 @@ async def cleanup():
115130
class DBSession(metaclass=DBSessionMeta):
116131
def __init__(
117132
self,
118-
session_args: Dict = None,
133+
session_args: Optional[Dict] = None,
119134
commit_on_exit: bool = False,
120135
multi_sessions: bool = False,
121136
):

pyproject.toml

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,33 @@
1-
[tool.black]
1+
[tool.ruff]
22
line-length = 100
3-
target-version = ['py37']
4-
include = '\.pyi?$'
5-
exclude = '''
6-
(
7-
| .git
8-
| .venv
9-
| build
10-
| dist
11-
)
12-
'''
3+
target-version = "py37"
4+
exclude = [
5+
".git",
6+
".venv",
7+
"build",
8+
"dist",
9+
]
1310

14-
[tool.isort]
15-
multi_line_output = 3
16-
include_trailing_comma = true
17-
force_grid_wrap = 0
18-
use_parentheses = true
19-
line_length = 100
11+
[tool.ruff.lint]
12+
select = [
13+
"E", # pycodestyle errors
14+
"W", # pycodestyle warnings
15+
"F", # pyflakes
16+
"I", # isort
17+
"B", # flake8-bugbear
18+
"C4", # flake8-comprehensions
19+
"UP", # pyupgrade
20+
]
21+
ignore = [
22+
"E203", # whitespace before ':'
23+
]
24+
25+
[tool.ruff.format]
26+
quote-style = "double"
27+
indent-style = "space"
28+
skip-magic-trailing-comma = false
29+
line-ending = "auto"
30+
31+
[tool.ruff.lint.isort]
32+
combine-as-imports = true
33+
split-on-trailing-comma = true

requirements.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ fastapi==0.90.0 # pyup: ignore
1010
flake8==3.7.9
1111
idna==3.7
1212
importlib-metadata==1.5.0
13-
isort==4.3.21
13+
isort==5.13.2
1414
mccabe==0.6.1
1515
more-itertools==7.2.0
1616
packaging>=22.0
@@ -25,9 +25,10 @@ pytest-cov==2.11.1
2525
PyYAML>=5.4
2626
regex>=2020.2.20
2727
requests>=2.22.0
28-
httpx>=0.20.0
28+
httpx>=0.20.0,<0.28.0
2929
six==1.12.0
3030
SQLAlchemy>=1.4.19
31+
sqlmodel>=0.0.24
3132
asyncpg>=0.27.0
3233
aiosqlite==0.20.0
3334
sqlparse==0.5.1

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import pytest
44
from fastapi import FastAPI
5-
from starlette.testclient import TestClient
5+
from fastapi.testclient import TestClient
66

77

88
@pytest.fixture

0 commit comments

Comments
 (0)