Added ``asyncio.shield()`` to the connection and session release process
specifically within the ``__aexit__()`` context manager exit, when using
:class:`.AsyncConnection` or :class:`.AsyncSession` as a context manager
that releases the object when the context manager is complete. This appears
to help with task cancellation when using alternate concurrency libraries
such as ``anyio``, ``uvloop`` that otherwise don't provide an async context
for the connection pool to release the connection properly during task
cancellation.
Fixes: #8145
Change-Id: I0b1ea9c3a22a18619341cbb8591225fcd339042c
(cherry picked from commit
1acaf0b2e4859a274e753b5054dcde3d5c7ca10e)
--- /dev/null
+.. change::
+ :tags: bug, asyncio
+ :tickets: 8145
+
+ Added ``asyncio.shield()`` to the connection and session release process
+ specifically within the ``__aexit__()`` context manager exit, when using
+ :class:`.AsyncConnection` or :class:`.AsyncSession` as a context manager
+ that releases the object when the context manager is complete. This appears
+ to help with task cancellation when using alternate concurrency libraries
+ such as ``anyio``, ``uvloop`` that otherwise don't provide an async context
+ for the connection pool to release the connection properly during task
+ cancellation.
+
+
#
# This module is part of SQLAlchemy and is released under
# the MIT License: https://www.opensource.org/licenses/mit-license.php
+import asyncio
+
from . import exc as async_exc
from .base import ProxyComparable
from .base import StartableContext
return self.start().__await__()
async def __aexit__(self, type_, value, traceback):
- await self.close()
+ await asyncio.shield(self.close())
@util.create_proxy_methods(
return self.conn
async def __aexit__(self, type_, value, traceback):
- await self.transaction.__aexit__(type_, value, traceback)
- await self.conn.close()
+ async def go():
+ await self.transaction.__aexit__(type_, value, traceback)
+ await self.conn.close()
+
+ await asyncio.shield(go())
def __init__(self, sync_engine):
if not sync_engine.dialect.is_async:
"""
- return await greenlet_spawn(self.sync_engine.dispose)
+ await greenlet_spawn(self.sync_engine.dispose)
class AsyncTransaction(ProxyComparable, StartableContext):
#
# This module is part of SQLAlchemy and is released under
# the MIT License: https://www.opensource.org/licenses/mit-license.php
+
+import asyncio
+
from . import engine
from . import result as _result
from .base import ReversibleProxy
:meth:`_asyncio.AsyncSession.close`
"""
- return await greenlet_spawn(self.sync_session.close)
+ await greenlet_spawn(self.sync_session.close)
async def invalidate(self):
"""Close this Session, using connection invalidation.
return self
async def __aexit__(self, type_, value, traceback):
- await self.close()
+ await asyncio.shield(self.close())
def _maker_context_manager(self):
# no @contextlib.asynccontextmanager until python3.7, gr
return self.async_session
async def __aexit__(self, type_, value, traceback):
- await self.trans.__aexit__(type_, value, traceback)
- await self.async_session.__aexit__(type_, value, traceback)
+ async def go():
+ await self.trans.__aexit__(type_, value, traceback)
+ await self.async_session.__aexit__(type_, value, traceback)
+
+ await asyncio.shield(go())
class AsyncSessionTransaction(ReversibleProxy, StartableContext):