From: Federico Caselli Date: Mon, 9 Feb 2026 21:47:19 +0000 (+0100) Subject: PoolProxiedConnection support context protocol X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git PoolProxiedConnection support context protocol The connection object returned by :meth:`_engine.Engine.raw_connection` now supports the context manager protocol, automatically returning the connection to the pool when exiting the context. Fixes: #13116 Change-Id: I51eb1fd61b772368f12a787e5f60db153a839e70 --- diff --git a/doc/build/changelog/unreleased_20/13116.rst b/doc/build/changelog/unreleased_20/13116.rst new file mode 100644 index 0000000000..3e4f98ced6 --- /dev/null +++ b/doc/build/changelog/unreleased_20/13116.rst @@ -0,0 +1,7 @@ +.. change:: + :tags: usecase, engine + :tickets: 13116 + + The connection object returned by :meth:`_engine.Engine.raw_connection` + now supports the context manager protocol, automatically returning the + connection to the pool when exiting the context. diff --git a/lib/sqlalchemy/pool/base.py b/lib/sqlalchemy/pool/base.py index 1c2f715624..5ef01568a3 100644 --- a/lib/sqlalchemy/pool/base.py +++ b/lib/sqlalchemy/pool/base.py @@ -34,6 +34,7 @@ from .. import event from .. import exc from .. import log from .. import util +from ..util.typing import Self if TYPE_CHECKING: from ..engine.interfaces import DBAPIConnection @@ -1122,6 +1123,13 @@ class PoolProxiedConnection(ManagesConnection): """ raise NotImplementedError() + def __enter__(self) -> Self: + return self + + def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: + self.close() + return None + class _AdhocProxiedConnection(PoolProxiedConnection): """provides the :class:`.PoolProxiedConnection` interface for cases where diff --git a/test/engine/test_pool.py b/test/engine/test_pool.py index 6759390f50..ab5cdccd8b 100644 --- a/test/engine/test_pool.py +++ b/test/engine/test_pool.py @@ -31,6 +31,7 @@ from sqlalchemy.testing import is_not from sqlalchemy.testing import is_not_none from sqlalchemy.testing import is_true from sqlalchemy.testing import mock +from sqlalchemy.testing.assertions import expect_raises_message from sqlalchemy.testing.engines import testing_engine from sqlalchemy.testing.util import gc_collect from sqlalchemy.testing.util import lazy_gc @@ -164,6 +165,25 @@ class PoolTest(PoolTestBase): assert not c2.info assert "foo2" in c.info + def test_raw_connection_context(self): + p = self._queuepool_fixture(pool_size=1, max_overflow=0) + e = create_engine("sqlite://", pool=p, _initialize=False) + + eq_(p.checkedout(), 0) + conn = e.raw_connection() + eq_(p.checkedout(), 1) + with conn as wc: + eq_(p.checkedout(), 1) + is_(conn, wc) + + eq_(p.checkedout(), 0) + + with expect_raises_message(ValueError, "an error"): + with e.raw_connection() as wc: + eq_(p.checkedout(), 1) + raise ValueError("an error") + eq_(p.checkedout(), 0) + def test_rec_info(self): p = self._queuepool_fixture(pool_size=1, max_overflow=0)