Commit cc52bac9 authored by Stefan Behnel's avatar Stefan Behnel

clean up and extend test to assert call dependencies between __getattr__() and __getattribute__()

parent df19f619
__doc__ = u""" # mode: run
__getattribute__ and __getattr__ special methods for a single class.
""" # __getattribute__ and __getattr__ special methods for a single class.
cdef class just_getattribute: cdef class just_getattribute:
""" """
>>> a = just_getattribute() >>> a = just_getattribute()
>>> a.called
1
>>> a.called
2
>>> a.bar >>> a.bar
'bar' 'bar'
>>> a.called
4
>>> a.invalid >>> a.invalid
Traceback (most recent call last): Traceback (most recent call last):
AttributeError AttributeError
>>> a.called
6
""" """
cdef readonly int called
def __getattribute__(self,n): def __getattribute__(self,n):
self.called += 1
if n == 'bar': if n == 'bar':
return n return n
elif n == 'called':
return self.called
else: else:
raise AttributeError raise AttributeError
cdef class just_getattr: cdef class just_getattr:
""" """
>>> a = just_getattr() >>> a = just_getattr()
>>> a.called
0
>>> a.called
0
>>> a.foo >>> a.foo
10 10
>>> a.called
0
>>> a.bar >>> a.bar
'bar' 'bar'
>>> a.called
1
>>> a.invalid >>> a.invalid
Traceback (most recent call last): Traceback (most recent call last):
AttributeError AttributeError
>>> a.called
2
""" """
cdef readonly int called
cdef readonly int foo cdef readonly int foo
def __init__(self): def __init__(self):
self.foo = 10 self.foo = 10
def __getattr__(self,n): def __getattr__(self,n):
self.called += 1
if n == 'bar': if n == 'bar':
return n return n
else: else:
raise AttributeError raise AttributeError
cdef class both: cdef class both:
""" """
>>> a = both() >>> a = both()
>>> (a.called_getattr, a.called_getattribute)
(0, 2)
>>> a.foo >>> a.foo
10 10
>>> (a.called_getattr, a.called_getattribute)
(0, 5)
>>> a.bar >>> a.bar
'bar' 'bar'
>>> (a.called_getattr, a.called_getattribute)
(1, 8)
>>> a.invalid >>> a.invalid
Traceback (most recent call last): Traceback (most recent call last):
AttributeError AttributeError
>>> (a.called_getattr, a.called_getattribute)
(2, 11)
""" """
cdef readonly int called_getattribute
cdef readonly int called_getattr
cdef readonly int foo cdef readonly int foo
def __init__(self): def __init__(self):
self.foo = 10 self.foo = 10
def __getattribute__(self,n): def __getattribute__(self,n):
self.called_getattribute += 1
if n == 'foo': if n == 'foo':
return self.foo return self.foo
elif n == 'called_getattribute':
return self.called_getattribute
elif n == 'called_getattr':
return self.called_getattr
else: else:
raise AttributeError raise AttributeError
def __getattr__(self,n): def __getattr__(self,n):
self.called_getattr += 1
if n == 'bar': if n == 'bar':
return n return n
else: else:
......
__doc__ = u""" # mode: run
__getattribute__ and __getattr__ special methods and subclasses.
# __getattribute__ and __getattr__ special methods and subclasses.
cdef class boring:
cdef readonly int boring_member
cdef readonly int getattr_called
cdef int getattribute_called
def __init__(self):
self.boring_member = 10
cdef class getattr_boring(boring):
"""
getattr does not override members.
getattr does not override members.
>>> a = getattr_boring() >>> a = getattr_boring()
>>> a.boring_member >>> a.boring_member
10 10
>>> a.getattr_called
0
>>> print(a.resolved_by) >>> print(a.resolved_by)
getattr_boring getattr_boring
>>> a.getattr_called
getattribute does. 1
>>> a = getattribute_boring() >>> a.no_such_member
>>> a.boring_member
Traceback (most recent call last): Traceback (most recent call last):
AttributeError AttributeError
>>> print(a.resolved_by) >>> a.getattr_called
getattribute_boring 2
"""
def __getattr__(self,n):
self.getattr_called += 1
if n == 'resolved_by':
return 'getattr_boring'
elif n == 'getattr_boring':
return True
else:
raise AttributeError
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
>>> print(a.resolved_by)
_getattribute
__getattribute__ is always tried first, then __getattr__, regardless of where # currently fails, see #1793
in the inheritance hiarchy they came from. #class getattr_boring_py(getattr_boring):
>>> a = getattribute_boring_boring_getattr() # __doc__ = getattr_boring.__doc__.replace(
>>> a.foo # 'getattr_boring()', 'getattr_boring_py()')
cdef class getattribute_boring(boring):
"""
getattribute overrides members.
>>> a = getattribute_boring()
>>> a.getattribute_called
1
>>> a.boring_member
Traceback (most recent call last): Traceback (most recent call last):
AttributeError AttributeError
>>> a.getattribute_called
3
>>> print(a.resolved_by) >>> print(a.resolved_by)
getattribute_boring_boring_getattr getattribute_boring
>>> a.getattribute_boring_boring_getattr >>> a.getattribute_called
True 5
>>> a._getattr >>> a.no_such_member
True
>>> a = getattr_boring_boring_getattribute()
>>> a.foo
Traceback (most recent call last): Traceback (most recent call last):
AttributeError AttributeError
>>> print(a.resolved_by) >>> a.getattribute_called
_getattribute 7
>>> 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 == u'resolved_by':
return u'getattr_boring'
elif n == u'getattr_boring':
return True
else:
raise AttributeError
cdef class getattribute_boring(boring):
def __getattribute__(self,n): def __getattribute__(self,n):
if n == u'resolved_by': self.getattribute_called += 1
return u'getattribute_boring' if n == 'resolved_by':
elif n == u'getattribute_boring': return 'getattribute_boring'
elif n == 'getattribute_boring':
return True return True
elif n == 'getattribute_called':
return self.getattribute_called
else: else:
raise AttributeError raise AttributeError
class getattribute_boring_py(getattribute_boring):
__doc__ = getattribute_boring.__doc__.replace(
'getattribute_boring()', 'getattribute_boring_py()')
cdef class _getattr: cdef class _getattr:
cdef readonly int getattr_called
def __getattr__(self,n): def __getattr__(self,n):
if n == u'resolved_by': self.getattr_called += 1
return u'_getattr' if n == 'resolved_by':
elif n == u'_getattr': return '_getattr'
elif n == '_getattr':
return True return True
elif n == 'getattr_called':
# must only get here if __getattribute__ is overwritten
assert 'getattribute' in type(self).__name__
return self.getattr_called
else: else:
raise AttributeError raise AttributeError
cdef class _getattribute(boring):
class getattr_py(_getattr):
"""
getattr is inherited.
>>> a = getattr_py()
>>> a.getattr_called
0
>>> print(a.resolved_by)
_getattr
>>> a.getattr_called
1
>>> print(a._getattr)
True
>>> a.getattr_called
2
>>> a.no_such_member
Traceback (most recent call last):
AttributeError
# currently fails, see #1793
#>>> a.getattr_called
#3
"""
cdef class _getattribute:
cdef int getattribute_called
def __getattribute__(self,n): def __getattribute__(self,n):
if n == u'resolved_by': self.getattribute_called += 1
return u'_getattribute' if n == 'resolved_by':
elif n == u'_getattribute': return '_getattribute'
elif n == '_getattribute':
return True return True
elif n == 'getattribute_called':
return self.getattribute_called
else: else:
raise AttributeError raise AttributeError
class getattribute_py(_getattribute):
"""
getattribute is inherited.
>>> a = getattribute_py()
>>> a.getattribute_called
1
>>> print(a.resolved_by)
_getattribute
>>> a.getattribute_called
3
>>> print(a._getattribute)
True
>>> a.getattribute_called
5
>>> a.no_such_member
Traceback (most recent call last):
AttributeError
>>> a.getattribute_called
7
"""
cdef class boring_getattribute(_getattribute): cdef class boring_getattribute(_getattribute):
cdef readonly int boring_getattribute_member cdef readonly int boring_getattribute_member
cdef class boring_boring_getattribute(boring_getattribute): cdef class boring_boring_getattribute(boring_getattribute):
"""
getattribute is inherited.
>>> a = boring_boring_getattribute()
>>> a.getattribute_called
1
>>> a.boring_getattribute_member
Traceback (most recent call last):
AttributeError
>>> a.getattribute_called
3
>>> a.boring_boring_getattribute_member
Traceback (most recent call last):
AttributeError
>>> a.getattribute_called
5
>>> print(a.resolved_by)
_getattribute
>>> a.getattribute_called
7
>>> a.no_such_member
Traceback (most recent call last):
AttributeError
>>> a.getattribute_called
9
"""
cdef readonly int boring_boring_getattribute_member cdef readonly int boring_boring_getattribute_member
class boring_boring_getattribute_py(boring_boring_getattribute):
__doc__ = boring_boring_getattribute.__doc__.replace(
'boring_boring_getattribute()', 'boring_boring_getattribute_py()')
cdef class boring_getattr(_getattr): cdef class boring_getattr(_getattr):
cdef readonly int boring_getattr_member cdef readonly int boring_getattr_member
...@@ -107,19 +206,90 @@ cdef class boring_boring_getattr(boring_getattr): ...@@ -107,19 +206,90 @@ cdef class boring_boring_getattr(boring_getattr):
cdef readonly int boring_boring_getattr_member cdef readonly int boring_boring_getattr_member
cdef class getattribute_boring_boring_getattr(boring_boring_getattr): cdef class getattribute_boring_boring_getattr(boring_boring_getattr):
"""
__getattribute__ is always tried first, then __getattr__, regardless of where
in the inheritance hiarchy they came from.
>>> a = getattribute_boring_boring_getattr()
>>> (a.getattr_called, a.getattribute_called)
(1, 2)
>>> print(a.resolved_by)
getattribute_boring_boring_getattr
>>> (a.getattr_called, a.getattribute_called)
(2, 5)
>>> a.getattribute_boring_boring_getattr
True
>>> (a.getattr_called, a.getattribute_called)
(3, 8)
>>> a._getattr
True
>>> (a.getattr_called, a.getattribute_called)
(5, 11)
>>> a.no_such_member
Traceback (most recent call last):
AttributeError
>>> (a.getattr_called, a.getattribute_called)
(7, 14)
"""
cdef int getattribute_called
def __getattribute__(self,n): def __getattribute__(self,n):
if n == u'resolved_by': self.getattribute_called += 1
return u'getattribute_boring_boring_getattr' if n == 'resolved_by':
elif n == u'getattribute_boring_boring_getattr': return 'getattribute_boring_boring_getattr'
elif n == 'getattribute_boring_boring_getattr':
return True return True
elif n == 'getattribute_called':
return self.getattribute_called
else: else:
raise AttributeError raise AttributeError
# currently fails, see #1793
#class getattribute_boring_boring_getattr_py(getattribute_boring_boring_getattr):
# __doc__ = getattribute_boring_boring_getattr.__doc__.replace(
# 'getattribute_boring_boring_getattr()', 'getattribute_boring_boring_getattr_py()')
cdef class getattr_boring_boring_getattribute(boring_boring_getattribute): cdef class getattr_boring_boring_getattribute(boring_boring_getattribute):
"""
__getattribute__ is always tried first, then __getattr__, regardless of where
in the inheritance hiarchy they came from.
>>> a = getattr_boring_boring_getattribute()
>>> (a.getattr_called, a.getattribute_called)
(1, 2)
>>> print(a.resolved_by)
_getattribute
>>> (a.getattr_called, a.getattribute_called)
(2, 5)
>>> a.getattr_boring_boring_getattribute
True
>>> (a.getattr_called, a.getattribute_called)
(4, 8)
>>> a._getattribute
True
>>> (a.getattr_called, a.getattribute_called)
(5, 11)
>>> a.no_such_member
Traceback (most recent call last):
AttributeError
>>> (a.getattr_called, a.getattribute_called)
(7, 14)
"""
cdef readonly int getattr_called # note: property will not be used due to __getattribute__()
def __getattr__(self,n): def __getattr__(self,n):
if n == u'resolved_by': self.getattr_called += 1
return u'getattr_boring_boring_getattribute' if n == 'resolved_by':
elif n == u'getattr_boring_boring_getattribute': return 'getattr_boring_boring_getattribute'
elif n == 'getattr_boring_boring_getattribute':
return True return True
elif n == 'getattr_called':
return self.getattr_called
else: else:
raise AttributeError raise AttributeError
# currently fails, see #1793
#class getattr_boring_boring_getattribute_py(getattr_boring_boring_getattribute):
# __doc__ = getattr_boring_boring_getattribute.__doc__.replace(
# 'getattr_boring_boring_getattribute()', 'getattr_boring_boring_getattribute_py()')
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