Commit 6b56ec73 authored by Peter Todd's avatar Peter Todd

__getattr(ibute)__ special methods now work with subclasses.

parent ac2dac9e
......@@ -1033,10 +1033,21 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
# 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) {"
......
__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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment