Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/structure/misc.pyx
8814 views
1
include 'sage/ext/stdsage.pxi'
2
from sage.misc.sage_itertools import unique_merge
3
4
def is_extension_type(cls):
5
"""
6
INPUT:
7
- cls: a class
8
9
Tests whether cls is an extension type (int, list, cython compiled classes, ...)
10
11
EXAMPLES
12
sage: from sage.structure.parent import is_extension_type
13
sage: is_extension_type(int)
14
True
15
sage: is_extension_type(list)
16
True
17
sage: is_extension_type(ZZ.__class__)
18
True
19
sage: is_extension_type(QQ.__class__)
20
False
21
"""
22
# Robert B claims that this should be robust
23
try:
24
return cls.__dictoffset__ == 0
25
except AttributeError:
26
pass
27
return False
28
29
class A(object):
30
pass
31
methodwrapper = type(A.__call__)
32
33
cdef class AttributeErrorMessage:
34
"""
35
Tries to emulate the standard Python ``AttributeError`` message.
36
37
NOTE:
38
39
The typical fate of an attribute error is being caught. Hence,
40
under normal circumstances, nobody will ever see the error
41
message. The idea for this class is to provide an object that
42
is fast to create and whose string representation is an attribute
43
error's message. That string representation is only created if
44
someone wants to see it.
45
46
EXAMPLES::
47
48
sage: 1.bla #indirect doctest
49
Traceback (most recent call last):
50
...
51
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'bla'
52
sage: QQ[x].gen().bla
53
Traceback (most recent call last):
54
...
55
AttributeError: 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint' object has no attribute 'bla'
56
sage: from sage.structure.misc import AttributeErrorMessage
57
sage: AttributeErrorMessage(int(1),'bla')
58
'int' object has no attribute 'bla'
59
60
TESTS:
61
62
By :trac:`14100`, the attribute errors raised on elements and parents are
63
unique objects. The error message of this unique error object is changed
64
inplace. This is for reasons of efficiency.
65
::
66
67
sage: try:
68
....: 1.__bla
69
....: except AttributeError as ElementError:
70
....: pass
71
sage: ElementError
72
AttributeError('sage.rings.integer.Integer' object has no attribute '__bla',)
73
sage: try:
74
....: x.__bla
75
....: except AttributeError as ElementError2:
76
....: pass
77
sage: ElementError2 is ElementError
78
True
79
sage: ElementError
80
AttributeError('sage.symbolic.expression.Expression' object has no attribute '__bla',)
81
sage: isinstance(ElementError.message, sage.structure.misc.AttributeErrorMessage)
82
True
83
84
Hence, if one really needs the error message as a string, then one should
85
make a copy of its string representation before it changes. Attribute
86
Errors of parents behave similarly::
87
88
sage: try:
89
....: QQ.__bla
90
....: except AttributeError as ParentError:
91
....: pass
92
sage: ParentError
93
AttributeError('RationalField_with_category' object has no attribute '__bla',)
94
sage: try:
95
....: ZZ.__bla
96
....: except AttributeError as ParentError2:
97
....: pass
98
sage: ParentError2 is ParentError
99
True
100
sage: ParentError2
101
AttributeError('sage.rings.integer_ring.IntegerRing_class' object has no attribute '__bla',)
102
sage: ParentError2 is ElementError
103
False
104
105
AUTHOR:
106
107
- Simon King (2011-05-21)
108
"""
109
110
def __init__(self, P,str name):
111
"""
112
INPUT:
113
114
- ``P``, any object
115
- ``name``, a string
116
117
TEST::
118
119
sage: from sage.structure.misc import AttributeErrorMessage
120
sage: AttributeErrorMessage(int(1),'bla')
121
'int' object has no attribute 'bla'
122
123
"""
124
self.cls = type(P)
125
self.name = name
126
def __repr__(self):
127
"""
128
TEST::
129
130
sage: from sage.structure.misc import AttributeErrorMessage
131
sage: AttributeErrorMessage(int(1),'bla')
132
'int' object has no attribute 'bla'
133
134
"""
135
cdef int dictoff
136
try:
137
dictoff = self.cls.__dictoffset__
138
except AttributeError:
139
return "'"+self.cls.__name__+"' object has no attribute '"+self.name+"'"
140
if dictoff:
141
return "'"+self.cls.__name__+"' object has no attribute '"+self.name+"'"
142
return repr(self.cls)[6:-1] + " object has no attribute '"+self.name+"'"
143
144
cdef AttributeErrorMessage dummy_error_message = AttributeErrorMessage(None, '')
145
dummy_attribute_error = AttributeError(dummy_error_message)
146
147
def getattr_from_other_class(self, cls, str name):
148
"""
149
INPUT::
150
151
- ``self``: some object
152
- ``cls``: a class
153
- ``name``: a string
154
155
Emulates ``getattr(self, name)``, as if self was an instance of ``cls``.
156
157
If self is an instance of cls, raises an ``AttributeError``, to
158
avoid a double lookup. This function is intended to be called from
159
__getattr__, and so should not be called if name is an attribute
160
of self.
161
162
TODO: lookup if such a function readilly exists in Python, and if
163
not triple check this specs and make this implementation
164
rock-solid.
165
166
Caveat: this is pretty hacky, does not handle caching, there is no
167
guarantee of robustness with super calls and descriptors, ...
168
169
EXAMPLES::
170
171
sage: from sage.structure.parent import getattr_from_other_class
172
sage: class A(object):
173
... def inc(self):
174
... return self + 1
175
... @lazy_attribute
176
... def lazy_attribute(self):
177
... return repr(self)
178
sage: getattr_from_other_class(1, A, "inc")
179
<bound method A.inc of 1>
180
sage: getattr_from_other_class(1, A, "inc")()
181
2
182
183
Caveat: lazy attributes work with extension types only
184
if they allow attribute assignment or have a public attribute
185
``__cached_methods`` of type ``<dict>``. This condition
186
is satisfied, e.g., by any class that is derived from
187
:class:`Parent`::
188
189
sage: getattr_from_other_class(1, A, "lazy_attribute")
190
Traceback (most recent call last):
191
...
192
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'lazy_attribute'
193
194
The integer ring is a parent, so, lazy attributes work::
195
196
sage: getattr_from_other_class(ZZ, A, "lazy_attribute")
197
'Integer Ring'
198
sage: getattr_from_other_class(PolynomialRing(QQ, name='x', sparse=True).one(), A, "lazy_attribute")
199
'1'
200
sage: getattr_from_other_class(PolynomialRing(QQ, name='x', implementation="FLINT").one(), A, "lazy_attribute")
201
Traceback (most recent call last):
202
...
203
AttributeError: 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint'
204
object has no attribute 'lazy_attribute'
205
206
In general, descriptors are not yet well supported, because they
207
often do not accept to be cheated with the type of their instance::
208
209
sage: A.__weakref__.__get__(1)
210
Traceback (most recent call last):
211
...
212
TypeError: descriptor '__weakref__' for 'A' objects doesn't apply
213
to 'sage.rings.integer.Integer' object
214
215
When this occurs, an ``AttributeError`` is raised::
216
217
sage: getattr_from_other_class(1, A, "__weakref__")
218
Traceback (most recent call last):
219
...
220
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__weakref__'
221
222
This was caught by #8296 for which we do a couple more tests::
223
224
sage: "__weakref__" in dir(A)
225
True
226
sage: "__weakref__" in dir(1)
227
True
228
sage: 1.__weakref__
229
Traceback (most recent call last):
230
...
231
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__weakref__'
232
233
sage: n = 1
234
sage: ip = get_ipython() # not tested: only works in interactive shell
235
sage: ip.magic_psearch('n.N') # not tested: only works in interactive shell
236
n.N
237
sage: ip.magic_psearch('n.__weakref__') # not tested: only works in interactive shell
238
239
Caveat: When __call__ is not defined for instances, using
240
``A.__call__`` yields the method ``__call__`` of the class. We use
241
a workaround but there is no guarantee for robustness.
242
243
sage: getattr_from_other_class(1, A, "__call__")
244
Traceback (most recent call last):
245
...
246
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__call__'
247
"""
248
if PY_TYPE_CHECK(self, cls):
249
dummy_error_message.cls = type(self)
250
dummy_error_message.name = name
251
raise dummy_attribute_error
252
try:
253
attribute = getattr(cls, name)
254
except AttributeError:
255
dummy_error_message.cls = type(self)
256
dummy_error_message.name = name
257
raise dummy_attribute_error
258
if PY_TYPE_CHECK(attribute, methodwrapper):
259
dummy_error_message.cls = type(self)
260
dummy_error_message.name = name
261
raise dummy_attribute_error
262
try:
263
getter = attribute.__get__
264
except AttributeError:
265
return attribute
266
# Conditionally defined lazy_attributes don't work well with fake subclasses
267
# (a TypeError is raised if the lazy attribute is not defined)
268
# For the moment, we ignore that when this occurs
269
# Other descriptors (including __weakref__) also break.
270
try:
271
return getter(self, cls)
272
except TypeError:
273
pass
274
dummy_error_message.cls = type(self)
275
dummy_error_message.name = name
276
raise dummy_attribute_error
277
278
def dir_with_other_class(self, cls):
279
r"""
280
Emulates ``dir(self)``, as if self was also an instance ``cls``,
281
right after ``caller_class`` in the method resolution order
282
(``self.__class__.mro()``)
283
284
EXAMPLES::
285
286
sage: class A(object):
287
... a = 1
288
... b = 2
289
... c = 3
290
sage: class B(object):
291
... b = 2
292
... c = 3
293
... d = 4
294
sage: x = A()
295
sage: x.c = 1; x.e = 1
296
sage: sage.structure.parent.dir_with_other_class(x, B)
297
[..., 'a', 'b', 'c', 'd', 'e']
298
299
Check that objects without dicts are well handled::
300
301
sage: cython("cdef class A:\n cdef public int a")
302
sage: cython("cdef class B:\n cdef public int b")
303
sage: x = A()
304
sage: x.a = 1
305
sage: hasattr(x,'__dict__')
306
False
307
sage: sage.structure.parent.dir_with_other_class(x, B)
308
[..., 'a', 'b']
309
310
TESTS:
311
312
Check that #13043 is fixed::
313
314
sage: len(dir(RIF))==len(set(dir(RIF)))
315
True
316
"""
317
ret = set()
318
# This tries to emulate the standard dir function
319
# Is there a better way to call dir on self, while ignoring this
320
# __dir__? Using dir(super(A, self)) does not work since the
321
# attributes coming from subclasses of A will be ignored
322
ret.update(dir(self.__class__))
323
if hasattr(self, "__dict__"):
324
ret.update(self.__dict__.keys())
325
if not isinstance(self, cls):
326
ret.update(dir(cls))
327
return sorted(ret)
328
329
330