]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- cleaned up the attributes scan for reconstitute hooks
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 8 Aug 2008 15:37:41 +0000 (15:37 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 8 Aug 2008 15:37:41 +0000 (15:37 +0000)
- added more careful check for "_should_exclude", guard against possible heisenbug activity

lib/sqlalchemy/orm/attributes.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/util.py
test/orm/utils.py

index dd470f358c30e50d6e8d546eab6b108082e339fc..5076775bce608dcec3bb27d555ee6e5fafc9d8ab 100644 (file)
@@ -1056,16 +1056,9 @@ class ClassManager(dict):
         self._instantiable = False
         self.events = self.event_registry_factory()
 
-        # TODO: generalize (and document the rationalization for) this traversal.
-        # TODO: figure out why getattr(cls, key) for all attributes
-        # causes test failures
-        for cls in class_.__mro__[0:-1]:
-            for key, meth in cls.__dict__.iteritems():
-                if isinstance(meth, types.FunctionType) and \
-                    hasattr(meth, '__sa_reconstitute__') and \
-                    hasattr(getattr(class_, key), '__sa_reconstitute__'):
-                    self.events.add_listener('on_load', getattr(class_, key))
-                    break
+        for key, meth in util.iterate_attributes(class_):
+            if isinstance(meth, types.FunctionType) and hasattr(meth, '__sa_reconstitute__'):
+                self.events.add_listener('on_load', meth)
 
     def instantiable(self, boolean):
         # experiment, probably won't stay in this form
index 06f4f3dadffe165ad1da0cfab3c3861cef9c30ad..52acdcb339925382dd70d56e57d7139b3b621c51 100644 (file)
@@ -644,15 +644,18 @@ class Mapper(object):
         
         """
         
+        def is_userland_descriptor(obj):
+            return not isinstance(obj, attributes.InstrumentedAttribute) and hasattr(obj, '__get__')
+            
         # check for descriptors, either local or from
         # an inherited class
         if local:
             if self.class_.__dict__.get(name, None)\
-                and hasattr(self.class_.__dict__[name], '__get__'):
+                and is_userland_descriptor(self.class_.__dict__[name]):
                 return True
         else:
             if getattr(self.class_, name, None)\
-                and hasattr(getattr(self.class_, name), '__get__'):
+                and is_userland_descriptor(getattr(self.class_, name)):
                 return True
 
         if (self.include_properties is not None and
index a9e7d22380f0878556f1c8831f85a6b77a52e1a5..76c73ca6ae4d338fc09c1f61e33f15e6f4f5b026 100644 (file)
@@ -410,6 +410,20 @@ def class_hierarchy(cls):
             hier.add(s)
     return list(hier)
 
+def iterate_attributes(cls):
+    """iterate all the keys and attributes associated with a class, without using getattr().
+    
+    Does not use getattr() so that class-sensitive descriptors (i.e. property.__get__())
+    are not called.
+    
+    """
+    keys = dir(cls)
+    for key in keys:
+        for c in cls.__mro__:
+            if key in c.__dict__:
+                yield (key, c.__dict__[key])
+                break
+                
 # from paste.deploy.converters
 def asbool(obj):
     if isinstance(obj, (str, unicode)):
index 4bb2464b3e04b05072731c8e6ad6edd5bee1487d..1f2cbe13a799ca24203807b0c832e52a1d89ca62 100644 (file)
@@ -201,7 +201,7 @@ class AliasedClassTest(TestBase):
 
         assert_table(Point.left_of(p2), table)
         assert_table(alias.left_of(p2), alias_table)
-
+    
 
 if __name__ == '__main__':
     testenv.main()