# First try to get the attribute using __getattribute__, if defined, or
# PyObject_GenericGetAttr.
#
- # If that raises an AttributeError, call the user's __getattr__
- # method, if defined.
- getattr_entry = scope.lookup_here("__getattr__")
- getattribute_entry = scope.lookup_here("__getattribute__")
+ # If that raises an AttributeError, call the __getattr__ if defined.
+ #
+ # In both cases, defined can be in this class, or any base class.
+ def lookup_here_or_base(n,type=None):
+ # Recursive lookup
+ if type is None:
+ type = scope.parent_type
+ r = type.scope.lookup_here(n)
+ if r is None and \
+ type.base_type is not None:
+ return lookup_here_or_base(n,type.base_type)
+ else:
+ return r
+ getattr_entry = lookup_here_or_base("__getattr__")
+ getattribute_entry = lookup_here_or_base("__getattribute__")
code.putln("")
code.putln(
"static PyObject *%s(PyObject *o, PyObject *n) {"
--- /dev/null
+__doc__ = """
+__getattribute__ and __getattr__ special methods and subclasses.
+
+getattr does not override members.
+ >>> a = getattr_boring()
+ >>> a.boring_member
+ 10
+ >>> a.resolved_by
+ 'getattr_boring'
+
+getattribute does.
+ >>> a = getattribute_boring()
+ >>> a.boring_member
+ Traceback (most recent call last):
+ AttributeError
+ >>> a.resolved_by
+ 'getattribute_boring'
+
+Is inherited.
+ >>> a = boring_boring_getattribute()
+ >>> a.boring_getattribute_member
+ Traceback (most recent call last):
+ AttributeError
+ >>> a.boring_boring_getattribute_member
+ Traceback (most recent call last):
+ AttributeError
+ >>> a.resolved_by
+ '_getattribute'
+
+__getattribute__ is always tried first, then __getattr__, regardless of where
+in the inheritance hiarchy they came from.
+ >>> a = getattribute_boring_boring_getattr()
+ >>> a.foo
+ Traceback (most recent call last):
+ AttributeError
+ >>> a.resolved_by
+ 'getattribute_boring_boring_getattr'
+ >>> a.getattribute_boring_boring_getattr
+ True
+ >>> a._getattr
+ True
+
+ >>> a = getattr_boring_boring_getattribute()
+ >>> a.foo
+ Traceback (most recent call last):
+ AttributeError
+ >>> a.resolved_by
+ '_getattribute'
+ >>> a.getattr_boring_boring_getattribute
+ True
+ >>> a._getattribute
+ True
+
+"""
+
+cdef class boring:
+ cdef readonly int boring_member
+ def __init__(self):
+ self.boring_member = 10
+
+cdef class getattr_boring(boring):
+ def __getattr__(self,n):
+ if n == 'resolved_by':
+ return 'getattr_boring'
+ elif n == 'getattr_boring':
+ return True
+ else:
+ raise AttributeError
+
+cdef class getattribute_boring(boring):
+ def __getattribute__(self,n):
+ if n == 'resolved_by':
+ return 'getattribute_boring'
+ elif n == 'getattribute_boring':
+ return True
+ else:
+ raise AttributeError
+
+cdef class _getattr:
+ def __getattr__(self,n):
+ if n == 'resolved_by':
+ return '_getattr'
+ elif n == '_getattr':
+ return True
+ else:
+ raise AttributeError
+
+cdef class _getattribute(boring):
+ def __getattribute__(self,n):
+ if n == 'resolved_by':
+ return '_getattribute'
+ elif n == '_getattribute':
+ return True
+ else:
+ raise AttributeError
+
+cdef class boring_getattribute(_getattribute):
+ cdef readonly int boring_getattribute_member
+
+cdef class boring_boring_getattribute(boring_getattribute):
+ cdef readonly int boring_boring_getattribute_member
+
+cdef class boring_getattr(_getattr):
+ cdef readonly int boring_getattr_member
+
+cdef class boring_boring_getattr(boring_getattr):
+ cdef readonly int boring_boring_getattr_member
+
+cdef class getattribute_boring_boring_getattr(boring_boring_getattr):
+ def __getattribute__(self,n):
+ if n == 'resolved_by':
+ return 'getattribute_boring_boring_getattr'
+ elif n == 'getattribute_boring_boring_getattr':
+ return True
+ else:
+ raise AttributeError
+
+cdef class getattr_boring_boring_getattribute(boring_boring_getattribute):
+ def __getattr__(self,n):
+ if n == 'resolved_by':
+ return 'getattr_boring_boring_getattribute'
+ elif n == 'getattr_boring_boring_getattribute':
+ return True
+ else:
+ raise AttributeError
+