From 5342d36a9a3a4f214b21c34f55740c389eb6e5df Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Thu, 8 Jun 2023 11:41:41 -0400 Subject: [PATCH] deprecate InstanceState.unloaded_expirable The :attr:`_orm.InstanceState.unloaded_expirable` attribute is a synonym for :attr:`_orm.InstanceState.unloaded`, and is now deprecated; this attribute was always implementation-specific and should not have been public. Fixes: #9913 Change-Id: Iadc25b0a8add164f9d70e6e4fe57dcd083902927 --- doc/build/changelog/unreleased_20/9913.rst | 8 ++++++++ lib/sqlalchemy/orm/session.py | 2 +- lib/sqlalchemy/orm/state.py | 15 +++++++++----- test/orm/test_deprecations.py | 24 ++++++++++++++++++++++ 4 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 doc/build/changelog/unreleased_20/9913.rst diff --git a/doc/build/changelog/unreleased_20/9913.rst b/doc/build/changelog/unreleased_20/9913.rst new file mode 100644 index 0000000000..c9931f0feb --- /dev/null +++ b/doc/build/changelog/unreleased_20/9913.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: bug, orm + :tickets: 9913 + + The :attr:`_orm.InstanceState.unloaded_expirable` attribute is a synonym + for :attr:`_orm.InstanceState.unloaded`, and is now deprecated; this + attribute was always implementation-specific and should not have been + public. diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index c41fff1df7..dd0cfb7908 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -5064,7 +5064,7 @@ def make_transient_to_detached(instance: object) -> None: if state._deleted: del state._deleted state._commit_all(state.dict) - state._expire_attributes(state.dict, state.unloaded_expirable) + state._expire_attributes(state.dict, state.unloaded) def object_session(instance: object) -> Optional[Session]: diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py index 8303b1a21a..b745fc53c2 100644 --- a/lib/sqlalchemy/orm/state.py +++ b/lib/sqlalchemy/orm/state.py @@ -824,8 +824,8 @@ class InstanceState(interfaces.InspectionAttrInfo, Generic[_O]): def unloaded(self) -> Set[str]: """Return the set of keys which do not have a loaded value. - This includes expired attributes and any other attribute that - was never populated or modified. + This includes expired attributes and any other attribute that was never + populated or modified. """ return ( @@ -835,11 +835,16 @@ class InstanceState(interfaces.InspectionAttrInfo, Generic[_O]): ) @property + @util.deprecated( + "2.0", + "The :attr:`.InstanceState.unloaded_expirable` attribute is " + "deprecated. Please use :attr:`.InstanceState.unloaded`.", + ) def unloaded_expirable(self) -> Set[str]: - """Return the set of keys which do not have a loaded value. + """Synonymous with :attr:`.InstanceState.unloaded`. - This includes expired attributes and any other attribute that - was never populated or modified. + This attribute was added as an implementation-specific detail at some + point and should be considered to be private. """ return self.unloaded diff --git a/test/orm/test_deprecations.py b/test/orm/test_deprecations.py index 82aad07ba7..0fa6c94a30 100644 --- a/test/orm/test_deprecations.py +++ b/test/orm/test_deprecations.py @@ -9,6 +9,8 @@ from sqlalchemy import event from sqlalchemy import exc as sa_exc from sqlalchemy import ForeignKey from sqlalchemy import func +from sqlalchemy import Identity +from sqlalchemy import inspect from sqlalchemy import Integer from sqlalchemy import literal_column from sqlalchemy import MetaData @@ -391,6 +393,28 @@ class SynonymTest(QueryTest, AssertsCompiledSQL): class MiscDeprecationsTest(fixtures.TestBase): + def test_unloaded_expirable(self, decl_base): + class A(decl_base): + __tablename__ = "a" + id = mapped_column(Integer, Identity(), primary_key=True) + x = mapped_column( + Integer, + ) + y = mapped_column(Integer, deferred=True) + + decl_base.metadata.create_all(testing.db) + with Session(testing.db) as sess: + obj = A(x=1, y=2) + sess.add(obj) + sess.commit() + + with expect_deprecated( + "The InstanceState.unloaded_expirable attribute is deprecated. " + "Please use InstanceState.unloaded." + ): + eq_(inspect(obj).unloaded, {"id", "x", "y"}) + eq_(inspect(obj).unloaded_expirable, inspect(obj).unloaded) + def test_evaluator_is_private(self): with expect_deprecated( "Direct use of 'EvaluatorCompiler' is not supported, and this " -- 2.47.2