fastapi-async-sqlalchemy 0.7.0.dev1__tar.gz → 0.7.0.dev3__tar.gz

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.
Files changed (17) hide show
  1. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/PKG-INFO +1 -1
  2. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/fastapi_async_sqlalchemy/__init__.py +1 -1
  3. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/fastapi_async_sqlalchemy/middleware.py +32 -38
  4. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/fastapi_async_sqlalchemy.egg-info/PKG-INFO +1 -1
  5. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/tests/test_session.py +25 -0
  6. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/LICENSE +0 -0
  7. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/README.md +0 -0
  8. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/fastapi_async_sqlalchemy/exceptions.py +0 -0
  9. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/fastapi_async_sqlalchemy/py.typed +0 -0
  10. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/fastapi_async_sqlalchemy.egg-info/SOURCES.txt +0 -0
  11. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/fastapi_async_sqlalchemy.egg-info/dependency_links.txt +0 -0
  12. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/fastapi_async_sqlalchemy.egg-info/not-zip-safe +0 -0
  13. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/fastapi_async_sqlalchemy.egg-info/requires.txt +0 -0
  14. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/fastapi_async_sqlalchemy.egg-info/top_level.txt +0 -0
  15. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/pyproject.toml +0 -0
  16. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/setup.cfg +0 -0
  17. {fastapi_async_sqlalchemy-0.7.0.dev1 → fastapi_async_sqlalchemy-0.7.0.dev3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastapi-async-sqlalchemy
3
- Version: 0.7.0.dev1
3
+ Version: 0.7.0.dev3
4
4
  Summary: SQLAlchemy middleware for FastAPI
5
5
  Home-page: https://github.com/h0rn3t/fastapi-async-sqlalchemy.git
6
6
  Author: Eugene Shershen
@@ -2,4 +2,4 @@ from fastapi_async_sqlalchemy.middleware import SQLAlchemyMiddleware, db
2
2
 
3
3
  __all__ = ["db", "SQLAlchemyMiddleware"]
4
4
 
5
- __version__ = "0.7.0.dev1"
5
+ __version__ = "0.7.0.dev3"
@@ -1,5 +1,4 @@
1
1
  import asyncio
2
- from asyncio import Task
3
2
  from contextvars import ContextVar
4
3
  from typing import Dict, Optional, Union
5
4
 
@@ -90,28 +89,23 @@ def create_middleware_and_session_proxy():
90
89
  ```
91
90
  """
92
91
  commit_on_exit = _commit_on_exit_ctx.get()
93
- task: Task = asyncio.current_task() # type: ignore
94
- if not hasattr(task, "_db_session"):
95
- task._db_session = _Session() # type: ignore
96
-
97
- def cleanup(future):
98
- session = getattr(task, "_db_session", None)
99
- if session:
100
-
101
- async def do_cleanup():
102
- try:
103
- if future.exception():
104
- await session.rollback()
105
- else:
106
- if commit_on_exit:
107
- await session.commit()
108
- finally:
109
- await session.close()
110
-
111
- asyncio.create_task(do_cleanup())
112
-
113
- task.add_done_callback(cleanup)
114
- return task._db_session # type: ignore
92
+ # Always create a new session for each access when multi_sessions=True
93
+ session = _Session()
94
+
95
+ async def cleanup():
96
+ try:
97
+ if commit_on_exit:
98
+ await session.commit()
99
+ except Exception:
100
+ await session.rollback()
101
+ raise
102
+ finally:
103
+ await session.close()
104
+
105
+ task = asyncio.current_task()
106
+ if task is not None:
107
+ task.add_done_callback(lambda t: asyncio.create_task(cleanup()))
108
+ return session
115
109
  else:
116
110
  session = _session.get()
117
111
  if session is None:
@@ -126,7 +120,6 @@ def create_middleware_and_session_proxy():
126
120
  multi_sessions: bool = False,
127
121
  ):
128
122
  self.token = None
129
- self.multi_sessions_token = None
130
123
  self.commit_on_exit_token = None
131
124
  self.session_args = session_args or {}
132
125
  self.commit_on_exit = commit_on_exit
@@ -139,23 +132,24 @@ def create_middleware_and_session_proxy():
139
132
  if self.multi_sessions:
140
133
  self.multi_sessions_token = _multi_sessions_ctx.set(True)
141
134
  self.commit_on_exit_token = _commit_on_exit_ctx.set(self.commit_on_exit)
142
-
143
- self.token = _session.set(_Session(**self.session_args))
135
+ else:
136
+ self.token = _session.set(_Session(**self.session_args))
144
137
  return type(self)
145
138
 
146
139
  async def __aexit__(self, exc_type, exc_value, traceback):
147
- session = _session.get()
148
- try:
149
- if exc_type is not None:
150
- await session.rollback()
151
- elif self.commit_on_exit:
152
- await session.commit()
153
- finally:
154
- await session.close()
155
- _session.reset(self.token)
156
- if self.multi_sessions_token is not None:
157
- _multi_sessions_ctx.reset(self.multi_sessions_token)
158
- _commit_on_exit_ctx.reset(self.commit_on_exit_token)
140
+ if self.multi_sessions:
141
+ _multi_sessions_ctx.reset(self.multi_sessions_token)
142
+ _commit_on_exit_ctx.reset(self.commit_on_exit_token)
143
+ else:
144
+ session = _session.get()
145
+ try:
146
+ if exc_type is not None:
147
+ await session.rollback()
148
+ elif self.commit_on_exit:
149
+ await session.commit()
150
+ finally:
151
+ await session.close()
152
+ _session.reset(self.token)
159
153
 
160
154
  return SQLAlchemyMiddleware, DBSession
161
155
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastapi-async-sqlalchemy
3
- Version: 0.7.0.dev1
3
+ Version: 0.7.0.dev3
4
4
  Summary: SQLAlchemy middleware for FastAPI
5
5
  Home-page: https://github.com/h0rn3t/fastapi-async-sqlalchemy.git
6
6
  Author: Eugene Shershen
@@ -173,3 +173,28 @@ async def test_multi_sessions(app, db, SQLAlchemyMiddleware):
173
173
 
174
174
  res = await asyncio.gather(*tasks)
175
175
  assert len(res) == 6
176
+
177
+
178
+ @pytest.mark.asyncio
179
+ async def test_concurrent_inserts(app, db, SQLAlchemyMiddleware):
180
+ app.add_middleware(SQLAlchemyMiddleware, db_url=db_url)
181
+
182
+ async with db(multi_sessions=True, commit_on_exit=True):
183
+ await db.session.execute(
184
+ text("CREATE TABLE IF NOT EXISTS my_model (id INTEGER PRIMARY KEY, value TEXT)")
185
+ )
186
+
187
+ async def insert_data(value):
188
+ await db.session.execute(
189
+ text("INSERT INTO my_model (value) VALUES (:value)"), {"value": value}
190
+ )
191
+ await db.session.flush()
192
+
193
+ tasks = [asyncio.create_task(insert_data(f"value_{i}")) for i in range(10)]
194
+
195
+ result_ids = await asyncio.gather(*tasks)
196
+ assert len(result_ids) == 10
197
+
198
+ records = await db.session.execute(text("SELECT * FROM my_model"))
199
+ records = records.scalars().all()
200
+ assert len(records) == 10