From a2e9aca2cf37db94d862c1721a371d00233cec16 Mon Sep 17 00:00:00 2001 From: Federico Caselli Date: Tue, 27 Jul 2021 22:30:06 +0200 Subject: [PATCH] Deprecate scoped_session usage with async sessions Deprecate usage of :class:`_orm.scoped_session` with asyncio drivers. When using Asyncio the :class:`_asyncio.async_scoped_session` should be used instead. Fixes: #6746 Change-Id: I540d57a406f59efc37fc61f0e9dfe03f32fe2904 --- doc/build/changelog/unreleased_14/6746.rst | 7 +++++++ lib/sqlalchemy/ext/asyncio/scoping.py | 2 ++ lib/sqlalchemy/ext/asyncio/session.py | 2 ++ lib/sqlalchemy/orm/scoping.py | 20 +++++++++++++++++-- lib/sqlalchemy/orm/session.py | 2 ++ test/orm/test_deprecations.py | 23 ++++++++++++++++++++++ 6 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 doc/build/changelog/unreleased_14/6746.rst diff --git a/doc/build/changelog/unreleased_14/6746.rst b/doc/build/changelog/unreleased_14/6746.rst new file mode 100644 index 0000000000..cff1a4ae94 --- /dev/null +++ b/doc/build/changelog/unreleased_14/6746.rst @@ -0,0 +1,7 @@ +.. change:: + :tags: orm + :tickets: 6746 + + Deprecate usage of :class:`_orm.scoped_session` with asyncio drivers. + When using Asyncio the :class:`_asyncio.async_scoped_session` should + be used instead. diff --git a/lib/sqlalchemy/ext/asyncio/scoping.py b/lib/sqlalchemy/ext/asyncio/scoping.py index 92d894599c..7fa8cba5cf 100644 --- a/lib/sqlalchemy/ext/asyncio/scoping.py +++ b/lib/sqlalchemy/ext/asyncio/scoping.py @@ -63,6 +63,8 @@ class async_scoped_session(ScopedSessionMixin): """ + _support_async = True + def __init__(self, session_factory, scopefunc): """Construct a new :class:`_asyncio.async_scoped_session`. diff --git a/lib/sqlalchemy/ext/asyncio/session.py b/lib/sqlalchemy/ext/asyncio/session.py index 59b6a2b15c..a10621eef3 100644 --- a/lib/sqlalchemy/ext/asyncio/session.py +++ b/lib/sqlalchemy/ext/asyncio/session.py @@ -53,6 +53,8 @@ class AsyncSession(ReversibleProxy): """ + _is_asyncio = True + __slots__ = ( "binds", "bind", diff --git a/lib/sqlalchemy/orm/scoping.py b/lib/sqlalchemy/orm/scoping.py index be16bc9112..353caa8fef 100644 --- a/lib/sqlalchemy/orm/scoping.py +++ b/lib/sqlalchemy/orm/scoping.py @@ -13,6 +13,7 @@ from ..util import create_proxy_methods from ..util import ScopedRegistry from ..util import ThreadLocalRegistry from ..util import warn +from ..util import warn_deprecated __all__ = ["scoped_session", "ScopedSessionMixin"] @@ -42,9 +43,16 @@ class ScopedSessionMixin(object): else: sess = self.session_factory(**kw) self.registry.set(sess) - return sess else: - return self.registry() + sess = self.registry() + if not self._support_async and sess._is_asyncio: + warn_deprecated( + "Using `scoped_session` with asyncio is deprecated and " + "will raise an error in a future version. " + "Please use `async_scoped_session` instead.", + "1.4.23", + ) + return sess def configure(self, **kwargs): """reconfigure the :class:`.sessionmaker` used by this @@ -116,8 +124,16 @@ class scoped_session(ScopedSessionMixin): See :ref:`unitofwork_contextual` for a tutorial. + ..warning:: + + When using :ref:`asyncio_toplevel` the async + version :class:`_asyncio.async_scoped_session` should be + used instead. + """ + _support_async = False + session_factory = None """The `session_factory` provided to `__init__` is stored in this attribute and may be accessed at a later time. This can be useful when diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 8302f70d6b..f9ea1c1692 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -948,6 +948,8 @@ class Session(_SessionClassMethods): """ + _is_asyncio = False + @util.deprecated_params( autocommit=( "2.0", diff --git a/test/orm/test_deprecations.py b/test/orm/test_deprecations.py index 4f762e4b71..baa1f44cfb 100644 --- a/test/orm/test_deprecations.py +++ b/test/orm/test_deprecations.py @@ -38,7 +38,9 @@ from sqlalchemy.orm import joinedload from sqlalchemy.orm import mapper from sqlalchemy.orm import relation from sqlalchemy.orm import relationship +from sqlalchemy.orm import scoped_session from sqlalchemy.orm import Session +from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import subqueryload from sqlalchemy.orm import synonym from sqlalchemy.orm import undefer @@ -54,6 +56,7 @@ from sqlalchemy.testing import assertions from sqlalchemy.testing import AssertsCompiledSQL from sqlalchemy.testing import eq_ from sqlalchemy.testing import eq_ignore_whitespace +from sqlalchemy.testing import expect_deprecated from sqlalchemy.testing import fixtures from sqlalchemy.testing import is_ from sqlalchemy.testing import is_true @@ -5475,3 +5478,23 @@ class GetBindTest(_GetBindTest): session = self._fixture({self.tables.base_table: base_class_bind}) is_(session.get_bind(self.classes.ConcreteSubClass), testing.db) + + +class DeprecationScopedSessionTest(fixtures.MappedTest): + def test_config_errors(self): + sm = sessionmaker() + + def go(): + s = sm() + s._is_asyncio = True + return s + + Session = scoped_session(go) + + with expect_deprecated( + "Using `scoped_session` with asyncio is deprecated and " + "will raise an error in a future version. " + "Please use `async_scoped_session` instead." + ): + Session() + Session.remove() -- 2.47.2