Commits

Mike Bayer committed 32ce33c

- do a straight __subclasses__ traversal here, so that we aren't
iterating through all mappers in memory when a new event is tacked
onto an unmapped superclass, also removes the strong ref that was
breaking some GC teardown tests

Comments (0)

Files changed (2)

lib/sqlalchemy/orm/events.py

             else:
                 collection = target.all_holds[target.class_] = []
 
-            collection.append((target.class_, identifier, fn, raw, propagate))
+            collection.append((identifier, fn, raw, propagate))
 
             if propagate:
-                for subject_dispatch, (subclass, subject) in \
-                                target.established.items():
-                    if issubclass(subclass, target.class_):
-                        subject_dispatch._listen(subject, identifier, fn,
+                stack = list(target.class_.__subclasses__())
+                while stack:
+                    subclass = stack.pop(0)
+                    stack.extend(subclass.__subclasses__())
+                    subject = target.resolve(subclass)
+                    if subject is not None:
+                        subject.dispatch._listen(subject, identifier, fn,
                                         raw=raw, propagate=propagate)
 
     @classmethod
     def populate(cls, class_, subject):
-        cls.established[subject.dispatch] = (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]
-                for target, ident, fn, raw, propagate in collection:
+                for ident, fn, raw, propagate in collection:
                     if propagate or subclass is class_:
                         subject.dispatch._listen(subject, ident,
                                                         fn, raw, propagate)
 
 class _InstanceEventsHold(_EventsHold):
     all_holds = weakref.WeakKeyDictionary()
-    established = weakref.WeakKeyDictionary()
+
+    def resolve(self, class_):
+        return orm.instrumentation.manager_of_class(class_)
 
     class HoldInstanceEvents(_EventsHold.HoldEvents, InstanceEvents):
         pass
 
 class _MapperEventsHold(_EventsHold):
     all_holds = weakref.WeakKeyDictionary()
-    established = weakref.WeakKeyDictionary()
+
+    def resolve(self, class_):
+        return orm.util._mapper_or_none(class_)
 
     class HoldMapperEvents(_EventsHold.HoldEvents, MapperEvents):
         pass

lib/sqlalchemy/orm/util.py

     else:
         return expression._column_as_key(attr)
 
-
 _state_mapper = util.dottedgetter('manager.mapper')
 
 @inspection._inspects(object)
 def _inspect_mapped_class(class_, configure=False):
     try:
         class_manager = attributes.manager_of_class(class_)
+        if not class_manager.is_mapped:
+            return None
         mapper = class_manager.mapper
         if configure and mapperlib.module._new_mappers:
             mapperlib.configure_mappers()