Property-aware getattr to use in object finding. If attrname represents a property, return it unevaluated (in case it has side effects or raises an error.
(obj, attrname)
| 1699 | |
| 1700 | @staticmethod |
| 1701 | def _getattr_property(obj, attrname): |
| 1702 | """Property-aware getattr to use in object finding. |
| 1703 | |
| 1704 | If attrname represents a property, return it unevaluated (in case it has |
| 1705 | side effects or raises an error. |
| 1706 | |
| 1707 | """ |
| 1708 | if not isinstance(obj, type): |
| 1709 | try: |
| 1710 | # `getattr(type(obj), attrname)` is not guaranteed to return |
| 1711 | # `obj`, but does so for property: |
| 1712 | # |
| 1713 | # property.__get__(self, None, cls) -> self |
| 1714 | # |
| 1715 | # The universal alternative is to traverse the mro manually |
| 1716 | # searching for attrname in class dicts. |
| 1717 | attr = getattr(type(obj), attrname) |
| 1718 | except AttributeError: |
| 1719 | pass |
| 1720 | else: |
| 1721 | # This relies on the fact that data descriptors (with both |
| 1722 | # __get__ & __set__ magic methods) take precedence over |
| 1723 | # instance-level attributes: |
| 1724 | # |
| 1725 | # class A(object): |
| 1726 | # @property |
| 1727 | # def foobar(self): return 123 |
| 1728 | # a = A() |
| 1729 | # a.__dict__['foobar'] = 345 |
| 1730 | # a.foobar # == 123 |
| 1731 | # |
| 1732 | # So, a property may be returned right away. |
| 1733 | if isinstance(attr, property): |
| 1734 | return attr |
| 1735 | |
| 1736 | # Nothing helped, fall back. |
| 1737 | return getattr(obj, attrname) |
| 1738 | |
| 1739 | def _object_find(self, oname, namespaces=None): |
| 1740 | """Find an object and return a struct with info about it.""" |