1# mode: run
2
3# __getattribute__ and __getattr__ special methods and subclasses.
4
5cdef class boring:
6    cdef readonly int boring_member
7    cdef readonly int getattr_called
8    cdef int getattribute_called
9    def __init__(self):
10        self.boring_member = 10
11
12cdef class getattr_boring(boring):
13    """
14    getattr does not override members.
15
16    >>> a = getattr_boring()
17    >>> a.boring_member
18    10
19    >>> a.getattr_called
20    0
21    >>> print(a.resolved_by)
22    getattr_boring
23    >>> a.getattr_called
24    1
25    >>> a.no_such_member
26    Traceback (most recent call last):
27    AttributeError
28    >>> a.getattr_called
29    2
30    """
31    def __getattr__(self,n):
32        self.getattr_called += 1
33        if n == 'resolved_by':
34            return 'getattr_boring'
35        elif n == 'getattr_boring':
36            return True
37        else:
38            raise AttributeError
39
40
41# currently fails, see #1793
42#class getattr_boring_py(getattr_boring):
43#    __doc__ = getattr_boring.__doc__.replace(
44#        'getattr_boring()', 'getattr_boring_py()')
45
46
47cdef class getattribute_boring(boring):
48    """
49    getattribute overrides members.
50
51    >>> a = getattribute_boring()
52    >>> a.getattribute_called
53    1
54    >>> a.boring_member
55    Traceback (most recent call last):
56    AttributeError
57    >>> a.getattribute_called
58    3
59    >>> print(a.resolved_by)
60    getattribute_boring
61    >>> a.getattribute_called
62    5
63    >>> a.no_such_member
64    Traceback (most recent call last):
65    AttributeError
66    >>> a.getattribute_called
67    7
68    """
69    def __getattribute__(self,n):
70        self.getattribute_called += 1
71        if n == 'resolved_by':
72            return 'getattribute_boring'
73        elif n == 'getattribute_boring':
74            return True
75        elif n == 'getattribute_called':
76            return self.getattribute_called
77        else:
78            raise AttributeError
79
80
81class getattribute_boring_py(getattribute_boring):
82    __doc__ = getattribute_boring.__doc__.replace(
83        'getattribute_boring()', 'getattribute_boring_py()')
84
85
86cdef class _getattr:
87    cdef readonly int getattr_called
88    def __getattr__(self,n):
89        self.getattr_called += 1
90        if n == 'resolved_by':
91            return '_getattr'
92        elif n == '_getattr':
93            return True
94        elif n == 'getattr_called':
95            # must only get here if __getattribute__ is overwritten
96            assert 'getattribute' in type(self).__name__
97            return self.getattr_called
98        else:
99            raise AttributeError
100
101
102class getattr_py(_getattr):
103    """
104    getattr is inherited.
105
106    >>> a = getattr_py()
107    >>> a.getattr_called
108    0
109    >>> print(a.resolved_by)
110    _getattr
111    >>> a.getattr_called
112    1
113    >>> print(a._getattr)
114    True
115    >>> a.getattr_called
116    2
117    >>> a.no_such_member
118    Traceback (most recent call last):
119    AttributeError
120
121    # currently fails, see #1793
122    #>>> a.getattr_called
123    #3
124    """
125
126
127cdef class _getattribute:
128    cdef int getattribute_called
129    def __getattribute__(self,n):
130        self.getattribute_called += 1
131        if n == 'resolved_by':
132            return '_getattribute'
133        elif n == '_getattribute':
134            return True
135        elif n == 'getattribute_called':
136            return self.getattribute_called
137        else:
138            raise AttributeError
139
140
141class getattribute_py(_getattribute):
142    """
143    getattribute is inherited.
144
145    >>> a = getattribute_py()
146    >>> a.getattribute_called
147    1
148    >>> print(a.resolved_by)
149    _getattribute
150    >>> a.getattribute_called
151    3
152    >>> print(a._getattribute)
153    True
154    >>> a.getattribute_called
155    5
156    >>> a.no_such_member
157    Traceback (most recent call last):
158    AttributeError
159    >>> a.getattribute_called
160    7
161    """
162
163
164cdef class boring_getattribute(_getattribute):
165    cdef readonly int boring_getattribute_member
166
167cdef class boring_boring_getattribute(boring_getattribute):
168    """
169    getattribute is inherited.
170
171    >>> a = boring_boring_getattribute()
172    >>> a.getattribute_called
173    1
174    >>> a.boring_getattribute_member
175    Traceback (most recent call last):
176    AttributeError
177    >>> a.getattribute_called
178    3
179    >>> a.boring_boring_getattribute_member
180    Traceback (most recent call last):
181    AttributeError
182    >>> a.getattribute_called
183    5
184    >>> print(a.resolved_by)
185    _getattribute
186    >>> a.getattribute_called
187    7
188    >>> a.no_such_member
189    Traceback (most recent call last):
190    AttributeError
191    >>> a.getattribute_called
192    9
193    """
194    cdef readonly int boring_boring_getattribute_member
195
196
197class boring_boring_getattribute_py(boring_boring_getattribute):
198    __doc__ = boring_boring_getattribute.__doc__.replace(
199        'boring_boring_getattribute()', 'boring_boring_getattribute_py()')
200
201
202cdef class boring_getattr(_getattr):
203    cdef readonly int boring_getattr_member
204
205cdef class boring_boring_getattr(boring_getattr):
206    cdef readonly int boring_boring_getattr_member
207
208cdef class getattribute_boring_boring_getattr(boring_boring_getattr):
209    """
210    __getattribute__ is always tried first, then __getattr__, regardless of where
211    in the inheritance hierarchy they came from.
212
213    >>> a = getattribute_boring_boring_getattr()
214    >>> (a.getattr_called, a.getattribute_called)
215    (1, 2)
216    >>> print(a.resolved_by)
217    getattribute_boring_boring_getattr
218    >>> (a.getattr_called, a.getattribute_called)
219    (2, 5)
220    >>> a.getattribute_boring_boring_getattr
221    True
222    >>> (a.getattr_called, a.getattribute_called)
223    (3, 8)
224    >>> a._getattr
225    True
226    >>> (a.getattr_called, a.getattribute_called)
227    (5, 11)
228    >>> a.no_such_member
229    Traceback (most recent call last):
230    AttributeError
231    >>> (a.getattr_called, a.getattribute_called)
232    (7, 14)
233    """
234    cdef int getattribute_called
235    def __getattribute__(self,n):
236        self.getattribute_called += 1
237        if n == 'resolved_by':
238            return 'getattribute_boring_boring_getattr'
239        elif n == 'getattribute_boring_boring_getattr':
240            return True
241        elif n == 'getattribute_called':
242            return self.getattribute_called
243        else:
244            raise AttributeError
245
246
247# currently fails, see #1793
248#class getattribute_boring_boring_getattr_py(getattribute_boring_boring_getattr):
249#    __doc__ = getattribute_boring_boring_getattr.__doc__.replace(
250#        'getattribute_boring_boring_getattr()', 'getattribute_boring_boring_getattr_py()')
251
252
253cdef class getattr_boring_boring_getattribute(boring_boring_getattribute):
254    """
255    __getattribute__ is always tried first, then __getattr__, regardless of where
256    in the inheritance hierarchy they came from.
257
258    >>> a = getattr_boring_boring_getattribute()
259    >>> (a.getattr_called, a.getattribute_called)
260    (1, 2)
261    >>> print(a.resolved_by)
262    _getattribute
263    >>> (a.getattr_called, a.getattribute_called)
264    (2, 5)
265    >>> a.getattr_boring_boring_getattribute
266    True
267    >>> (a.getattr_called, a.getattribute_called)
268    (4, 8)
269    >>> a._getattribute
270    True
271    >>> (a.getattr_called, a.getattribute_called)
272    (5, 11)
273    >>> a.no_such_member
274    Traceback (most recent call last):
275    AttributeError
276    >>> (a.getattr_called, a.getattribute_called)
277    (7, 14)
278    """
279    cdef readonly int getattr_called  # note: property will not be used due to __getattribute__()
280    def __getattr__(self,n):
281        self.getattr_called += 1
282        if n == 'resolved_by':
283            return 'getattr_boring_boring_getattribute'
284        elif n == 'getattr_boring_boring_getattribute':
285            return True
286        elif n == 'getattr_called':
287            return self.getattr_called
288        else:
289            raise AttributeError
290
291
292# currently fails, see #1793
293#class getattr_boring_boring_getattribute_py(getattr_boring_boring_getattribute):
294#    __doc__ = getattr_boring_boring_getattribute.__doc__.replace(
295#        'getattr_boring_boring_getattribute()', 'getattr_boring_boring_getattribute_py()')
296