]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Set up of_type variable for legacy loader option deserialize
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 10 Jan 2018 20:27:33 +0000 (15:27 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 10 Jan 2018 20:27:33 +0000 (15:27 -0500)
Fixed regression where pickle format of a Load / _UnboundLoad object (e.g.
loader options) changed and ``__setstate__()`` was raising an
UnboundLocalError for an object received from the legacy format, even
though an attempt was made to do so.  tests are now added to ensure this
works.

Change-Id: Idccf643e010776817dd32512facdefa263188814
Fixes: #4159
doc/build/changelog/unreleased_12/4159.rst [new file with mode: 0644]
lib/sqlalchemy/orm/strategy_options.py
test/orm/test_options.py

diff --git a/doc/build/changelog/unreleased_12/4159.rst b/doc/build/changelog/unreleased_12/4159.rst
new file mode 100644 (file)
index 0000000..5ed2773
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 4159
+
+    Fixed regression where pickle format of a Load / _UnboundLoad object (e.g.
+    loader options) changed and ``__setstate__()`` was raising an
+    UnboundLocalError for an object received from the legacy format, even
+    though an attempt was made to do so.  tests are now added to ensure this
+    works.
index 13c51004755a8e793f7fd0d96f1e88ee5e086ea6..1963e5843d78e8bc09d082cd4827eb2b4174b93e 100644 (file)
@@ -449,6 +449,7 @@ class _UnboundLoad(Load):
                 if len(key) == 2:
                     # support legacy
                     cls, propkey = key
+                    of_type = None
                 else:
                     cls, propkey, of_type = key
                 prop = getattr(cls, propkey)
index 731aee1e551351c941e01971176a116d7a72e9c2..9d3b8b7026fea3662fffe81a27c70971794b55bb 100644 (file)
@@ -890,6 +890,86 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
                               joinedload(eager_option))
 
 
+class PickleTest(PathTest, QueryTest):
+
+    def _option_fixture(self, *arg):
+        return strategy_options._UnboundLoad._from_keys(
+            strategy_options._UnboundLoad.joinedload, arg, True, {})
+
+    def test_modern_opt_getstate(self):
+        User = self.classes.User
+
+        sess = Session()
+        q = sess.query(User)
+
+        opt = self._option_fixture(User.addresses)
+        eq_(
+            opt.__getstate__(),
+            {
+                '_is_chain_link': False,
+                'local_opts': {},
+                'is_class_strategy': False,
+                'path': [(User, 'addresses', None)],
+                'propagate_to_loaders': True,
+                '_to_bind': [opt],
+                'strategy': (('lazy', 'joined'),)}
+        )
+
+    def test_modern_opt_setstate(self):
+        User = self.classes.User
+
+        opt = strategy_options._UnboundLoad.__new__(
+            strategy_options._UnboundLoad)
+        state = {
+            '_is_chain_link': False,
+            'local_opts': {},
+            'is_class_strategy': False,
+            'path': [(User, 'addresses', None)],
+            'propagate_to_loaders': True,
+            '_to_bind': [opt],
+            'strategy': (('lazy', 'joined'),)}
+
+        opt.__setstate__(state)
+
+        query = create_session().query(User)
+        attr = {}
+        load = opt._bind_loader(
+            [ent.entity_zero for ent in query._mapper_entities],
+            query._current_path, attr, False)
+
+        eq_(
+            load.path,
+            inspect(User)._path_registry
+            [User.addresses.property][inspect(self.classes.Address)])
+
+    def test_legacy_opt_setstate(self):
+        User = self.classes.User
+
+        opt = strategy_options._UnboundLoad.__new__(
+            strategy_options._UnboundLoad)
+        state = {
+            '_is_chain_link': False,
+            'local_opts': {},
+            'is_class_strategy': False,
+            'path': [(User, 'addresses')],
+            'propagate_to_loaders': True,
+            '_to_bind': [opt],
+            'strategy': (('lazy', 'joined'),)}
+
+        opt.__setstate__(state)
+
+        query = create_session().query(User)
+        attr = {}
+        load = opt._bind_loader(
+            [ent.entity_zero for ent in query._mapper_entities],
+            query._current_path, attr, False)
+
+        eq_(
+            load.path,
+            inspect(User)._path_registry
+            [User.addresses.property][inspect(self.classes.Address)])
+
+
 class LocalOptsTest(PathTest, QueryTest):
     @classmethod
     def setup_class(cls):