diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2008-08-08 15:37:41 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2008-08-08 15:37:41 +0000 |
| commit | 14c0dc7b4a6ae0723b692d67b912ecd2c3847bb8 (patch) | |
| tree | ced6ae023ab41fbfdb994b977e9fe72b8dd549dc | |
| parent | 4ab87682e14fbedaa34f140f2fe4fdf773fc2d2b (diff) | |
| download | sqlalchemy-14c0dc7b4a6ae0723b692d67b912ecd2c3847bb8.tar.gz | |
- cleaned up the attributes scan for reconstitute hooks
- added more careful check for "_should_exclude", guard against possible heisenbug activity
| -rw-r--r-- | lib/sqlalchemy/orm/attributes.py | 13 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/mapper.py | 7 | ||||
| -rw-r--r-- | lib/sqlalchemy/util.py | 14 | ||||
| -rw-r--r-- | test/orm/utils.py | 2 |
4 files changed, 23 insertions, 13 deletions
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index dd470f358..5076775bc 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -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 diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 06f4f3dad..52acdcb33 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -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 diff --git a/lib/sqlalchemy/util.py b/lib/sqlalchemy/util.py index a9e7d2238..76c73ca6a 100644 --- a/lib/sqlalchemy/util.py +++ b/lib/sqlalchemy/util.py @@ -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)): diff --git a/test/orm/utils.py b/test/orm/utils.py index 4bb2464b3..1f2cbe13a 100644 --- a/test/orm/utils.py +++ b/test/orm/utils.py @@ -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() |
