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)
| 1825 | |
| 1826 | @staticmethod |
| 1827 | def _getattr_property(obj, attrname): |
| 1828 | """Property-aware getattr to use in object finding. |
| 1829 | |
| 1830 | If attrname represents a property, return it unevaluated (in case it has |
| 1831 | side effects or raises an error. |
| 1832 | |
| 1833 | """ |
| 1834 | if not isinstance(obj, type): |
| 1835 | try: |
| 1836 | # `getattr(type(obj), attrname)` is not guaranteed to return |
| 1837 | # `obj`, but does so for property: |
| 1838 | # |
| 1839 | # property.__get__(self, None, cls) -> self |
| 1840 | # |
| 1841 | # The universal alternative is to traverse the mro manually |
| 1842 | # searching for attrname in class dicts. |
| 1843 | if is_integer_string(attrname): |
| 1844 | return obj[int(attrname)] |
| 1845 | else: |
| 1846 | attr = getattr(type(obj), attrname) |
| 1847 | except AttributeError: |
| 1848 | pass |
| 1849 | else: |
| 1850 | # This relies on the fact that data descriptors (with both |
| 1851 | # __get__ & __set__ magic methods) take precedence over |
| 1852 | # instance-level attributes: |
| 1853 | # |
| 1854 | # class A(object): |
| 1855 | # @property |
| 1856 | # def foobar(self): return 123 |
| 1857 | # a = A() |
| 1858 | # a.__dict__['foobar'] = 345 |
| 1859 | # a.foobar # == 123 |
| 1860 | # |
| 1861 | # So, a property may be returned right away. |
| 1862 | if isinstance(attr, property): |
| 1863 | return attr |
| 1864 | |
| 1865 | # Nothing helped, fall back. |
| 1866 | return getattr(obj, attrname) |
| 1867 | |
| 1868 | def _object_find(self, oname, namespaces=None) -> OInfo: |
| 1869 | """Find an object and return a struct with info about it.""" |
no test coverage detected