]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Fixed bug in ORM-level event registration where the "raw" or
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 19 Jul 2013 03:17:33 +0000 (23:17 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 19 Jul 2013 03:17:33 +0000 (23:17 -0400)
"propagate" flags could potentially be mis-configured in some
"unmapped base class" configurations.  Also in 0.8.3.
[ticket:2786]

doc/build/changelog/changelog_08.rst
doc/build/changelog/changelog_09.rst
lib/sqlalchemy/orm/events.py
test/orm/test_events.py

index 4e545418048f8c17eef24a84794da1fa8b43502a..c14cb84325fb4910918b5809a1f7b80645776220 100644 (file)
@@ -6,6 +6,14 @@
 .. changelog::
     :version: 0.8.3
 
+    .. change::
+        :tags: bug, orm
+        :tickets: 2786
+
+        Fixed bug in ORM-level event registration where the "raw" or
+        "propagate" flags could potentially be mis-configured in some
+        "unmapped base class" configurations.
+
     .. change::
         :tags: bug, sql
         :tickets: 2784
index 02a5c1d81a1c2ee367b46dd9b61139e1cf143695..551a31223d0e2d7f221dbc48bf427a19a41d1da2 100644 (file)
@@ -6,6 +6,14 @@
 .. changelog::
     :version: 0.9.0
 
+    .. change::
+        :tags: bug, orm
+        :tickets: 2786
+
+        Fixed bug in ORM-level event registration where the "raw" or
+        "propagate" flags could potentially be mis-configured in some
+        "unmapped base class" configurations.  Also in 0.8.3.
+
     .. change::
         :tags: bug, sql
         :tickets: 2784
index 97019bb4e58b147982ecf49483aaff1a86c5d388..14b3a770e4a4bb00758bda89a1d41a272eec0df0 100644 (file)
@@ -360,14 +360,17 @@ class _EventsHold(object):
     def populate(cls, class_, subject):
         for subclass in class_.__mro__:
             if subclass in cls.all_holds:
-                if subclass is class_:
-                    collection = cls.all_holds.pop(subclass)
-                else:
-                    collection = cls.all_holds[subclass]
+                collection = cls.all_holds[subclass]
                 for ident, fn, raw, propagate in collection:
                     if propagate or subclass is class_:
+                        # since we can't be sure in what order different classes
+                        # in a hierarchy are triggered with populate(),
+                        # we rely upon _EventsHold for all event
+                        # assignment, instead of using the generic propagate
+                        # flag.
                         subject.dispatch._listen(subject, ident,
-                                                        fn, raw, propagate)
+                                                        fn, raw=raw,
+                                                        propagate=False)
 
 
 class _InstanceEventsHold(_EventsHold):
index d2dae8ba3e7dfe7707949a812ec97eaf5c8397f6..27cdd023596fe8974cf7f37f716eb904de8b39fc 100644 (file)
@@ -362,14 +362,25 @@ class DeferredMapperEventsTest(_RemoveListeners, _fixtures.FixtureTest):
         class SubUser(User):
             pass
 
-        canary = []
+        class SubSubUser(SubUser):
+            pass
+
+        canary = Mock()
         def evt(x, y, z):
             canary.append(x)
-        event.listen(User, "before_insert", evt, propagate=True, raw=True)
+        event.listen(User, "before_insert", canary, propagate=True, raw=True)
 
         m = mapper(SubUser, users)
         m.dispatch.before_insert(5, 6, 7)
-        eq_(canary, [5])
+        eq_(canary.mock_calls,
+                [call(5, 6, 7)])
+
+        m2 = mapper(SubSubUser, users)
+
+        m2.dispatch.before_insert(8, 9, 10)
+        eq_(canary.mock_calls,
+                [call(5, 6, 7), call(8, 9, 10)])
+
 
     def test_deferred_map_event_subclass_no_propagate(self):
         """
@@ -416,6 +427,35 @@ class DeferredMapperEventsTest(_RemoveListeners, _fixtures.FixtureTest):
         m.dispatch.before_insert(5, 6, 7)
         eq_(canary, [5])
 
+    def test_deferred_map_event_subclass_post_mapping_propagate_two(self):
+        """
+        1. map only subclass of class
+        2. mapper event listen on class, w propagate
+        3. event fire should receive event
+
+        """
+        users, User = (self.tables.users,
+                                self.classes.User)
+
+        class SubUser(User):
+            pass
+
+        class SubSubUser(SubUser):
+            pass
+
+        m = mapper(SubUser, users)
+
+        canary = Mock()
+        event.listen(User, "before_insert", canary, propagate=True, raw=True)
+
+        m2 = mapper(SubSubUser, users)
+
+        m.dispatch.before_insert(5, 6, 7)
+        eq_(canary.mock_calls, [call(5, 6, 7)])
+
+        m2.dispatch.before_insert(8, 9, 10)
+        eq_(canary.mock_calls, [call(5, 6, 7), call(8, 9, 10)])
+
     def test_deferred_instance_event_subclass_post_mapping_propagate(self):
         """
         1. map only subclass of class
@@ -507,23 +547,25 @@ class DeferredMapperEventsTest(_RemoveListeners, _fixtures.FixtureTest):
         class SubUser2(User):
             pass
 
-        canary = []
-        def evt(x):
-            canary.append(x)
-        event.listen(User, "load", evt, propagate=True, raw=True)
+        canary = Mock()
+        event.listen(User, "load", canary, propagate=True, raw=False)
 
+        # reversing these fixes....
         m = mapper(SubUser, users)
         m2 = mapper(User, users)
 
-        m.class_manager.dispatch.load(5)
-        eq_(canary, [5])
+        instance = Mock()
+        m.class_manager.dispatch.load(instance)
 
-        m2.class_manager.dispatch.load(5)
-        eq_(canary, [5, 5])
+        eq_(canary.mock_calls, [call(instance.obj())])
+
+        m2.class_manager.dispatch.load(instance)
+        eq_(canary.mock_calls, [call(instance.obj()), call(instance.obj())])
 
         m3 = mapper(SubUser2, users)
-        m3.class_manager.dispatch.load(5)
-        eq_(canary, [5, 5, 5])
+        m3.class_manager.dispatch.load(instance)
+        eq_(canary.mock_calls, [call(instance.obj()),
+                        call(instance.obj()), call(instance.obj())])
 
     def test_deferred_instance_event_subclass_no_propagate(self):
         """