diff options
| author | mike bayer <mike_mp@zzzcomputing.com> | 2019-05-31 00:50:20 +0000 |
|---|---|---|
| committer | Gerrit Code Review <gerrit@bbpush.zzzcomputing.com> | 2019-05-31 00:50:20 +0000 |
| commit | b27abee6f02132e255efde44ed23dd83036f5cc5 (patch) | |
| tree | be2a163e010fce179d8dad51b0673e11e9a2833f /lib/sqlalchemy/orm | |
| parent | df99e1ef5f334ce7f4c8118c3e0bdf2949f54de3 (diff) | |
| parent | 5ac10699e1111283ae848f9d3a6dcc4e09d8c1ef (diff) | |
| download | sqlalchemy-b27abee6f02132e255efde44ed23dd83036f5cc5.tar.gz | |
Merge "Rework AliasedClass __getattr__ to use top-level getattr()"
Diffstat (limited to 'lib/sqlalchemy/orm')
| -rw-r--r-- | lib/sqlalchemy/orm/attributes.py | 8 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/util.py | 51 |
2 files changed, 29 insertions, 30 deletions
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 31c351bb0..e277f6f5b 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -346,10 +346,14 @@ def create_proxied_attribute(descriptor): ) def __get__(self, instance, owner): - if instance is None: + retval = self.descriptor.__get__(instance, owner) + # detect if this is a plain Python @property, which just returns + # itself for class level access. If so, then return us. + # Otherwise, return the object returned by the descriptor. + if retval is self.descriptor and instance is None: return self else: - return self.descriptor.__get__(instance, owner) + return retval def __str__(self): return "%s.%s" % (self.class_.__name__, self.key) diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py index c7e338848..bfc2f71b3 100644 --- a/lib/sqlalchemy/orm/util.py +++ b/lib/sqlalchemy/orm/util.py @@ -7,6 +7,7 @@ import re +import types from . import attributes # noqa from .base import _class_to_mapper # noqa @@ -496,34 +497,28 @@ class AliasedClass(object): except KeyError: raise AttributeError() else: - for base in _aliased_insp._target.__mro__: - try: - attr = object.__getattribute__(base, key) - except AttributeError: - continue - else: - break - else: - raise AttributeError(key) - - if isinstance(attr, PropComparator): - ret = attr.adapt_to_entity(_aliased_insp) - setattr(self, key, ret) - return ret - elif hasattr(attr, "func_code"): - is_method = getattr(_aliased_insp._target, key, None) - if is_method and is_method.__self__ is not None: - return util.types.MethodType(attr.__func__, self, self) - else: - return None - elif hasattr(attr, "__get__"): - ret = attr.__get__(None, self) - if isinstance(ret, PropComparator): - return ret.adapt_to_entity(_aliased_insp) - else: - return ret - else: - return attr + target = _aliased_insp._target + # maintain all getattr mechanics + attr = getattr(target, key) + + # attribute is a method, that will be invoked against a + # "self"; so just return a new method with the same function and + # new self + if hasattr(attr, "__call__") and hasattr(attr, "__self__"): + return types.MethodType(attr.__func__, self) + + # attribute is a descriptor, that will be invoked against a + # "self"; so invoke the descriptor against this self + if hasattr(attr, "__get__"): + attr = attr.__get__(None, self) + + # attributes within the QueryableAttribute system will want this + # to be invoked so the object can be adapted + if hasattr(attr, "adapt_to_entity"): + attr = attr.adapt_to_entity(_aliased_insp) + setattr(self, key, attr) + + return attr def __repr__(self): return "<AliasedClass at 0x%x; %s>" % ( |
