Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/structure/sage_object.pyx
4057 views
1
r"""
2
Abstract base class for Sage objects
3
"""
4
5
import cPickle
6
import os
7
import sys
8
from cStringIO import StringIO
9
from sage.misc.sage_unittest import TestSuite
10
11
sys_modules = sys.modules
12
13
# change to import zlib to use zlib instead; but this
14
# slows down loading any data stored in the other format
15
import zlib; comp = zlib
16
import bz2; comp_other = bz2
17
18
base=None
19
20
cdef process(s):
21
if not base is None and (len(s) == 0 or s[0] != '/'):
22
s = base + '/' + s
23
if s[-5:] != '.sobj':
24
return s + '.sobj'
25
else:
26
return s
27
28
29
cdef class SageObject:
30
31
#######################################################################
32
# Textual representation code
33
#######################################################################
34
35
def rename(self, x=None):
36
r"""
37
Change self so it prints as x, where x is a string.
38
39
.. note::
40
41
This is *only* supported for Python classes that derive
42
from SageObject.
43
44
EXAMPLES::
45
46
sage: x = PolynomialRing(QQ, 'x', sparse=True).gen()
47
sage: g = x^3 + x - 5
48
sage: g
49
x^3 + x - 5
50
sage: g.rename('a polynomial')
51
sage: g
52
a polynomial
53
sage: g + x
54
x^3 + 2*x - 5
55
sage: h = g^100
56
sage: str(h)[:20]
57
'x^300 + 100*x^298 - '
58
sage: h.rename('x^300 + ...')
59
sage: h
60
x^300 + ...
61
62
Real numbers are not Python classes, so rename is not supported::
63
64
sage: a = 3.14
65
sage: type(a)
66
<type 'sage.rings.real_mpfr.RealLiteral'>
67
sage: a.rename('pi')
68
Traceback (most recent call last):
69
...
70
NotImplementedError: object does not support renaming: 3.14000000000000
71
72
.. note::
73
74
The reason C-extension types are not supported by default
75
is if they were then every single one would have to carry
76
around an extra attribute, which would be slower and waste
77
a lot of memory.
78
79
To support them for a specific class, add a
80
``cdef public __custom_name`` attribute.
81
"""
82
if x is None:
83
#if hasattr(self, '__custom_name'):
84
# that's tested in reset_name anyway...
85
self.reset_name()
86
else:
87
try:
88
self.__custom_name = str(x)
89
except AttributeError:
90
raise NotImplementedError, "object does not support renaming: %s"%self
91
92
def reset_name(self):
93
"""
94
Remove the custrom name of an object.
95
96
EXAMPLES::
97
98
sage: P.<x> = QQ[]
99
sage: P
100
Univariate Polynomial Ring in x over Rational Field
101
sage: P.rename('A polynomial ring')
102
sage: P
103
A polynomial ring
104
sage: P.reset_name()
105
sage: P
106
Univariate Polynomial Ring in x over Rational Field
107
"""
108
if hasattr(self, '__custom_name'):
109
del self.__custom_name
110
111
112
def __repr__(self):
113
"""
114
Default method for string representation.
115
116
NOTE:
117
118
Do not overwrite this method. Instead, implement
119
a ``_repr_`` (single underscore) method.
120
121
EXAMPLES:
122
123
By default, the string representation coincides with
124
the output of the single underscore ``_repr_``::
125
126
sage: P.<x> = QQ[]
127
sage: repr(P) == P._repr_() #indirect doctest
128
True
129
130
Using :meth:`rename`, the string representation can
131
be customized::
132
133
sage: P.rename('A polynomial ring')
134
sage: repr(P) == P._repr_()
135
False
136
137
The original behaviour is restored with :meth:`reset_name`.
138
139
sage: P.reset_name()
140
sage: repr(P) == P._repr_()
141
True
142
"""
143
try:
144
name = self.__custom_name
145
if name is not None:
146
return name
147
except:
148
pass
149
try:
150
repr_func = self._repr_
151
except AttributeError:
152
return str(type(self))
153
else:
154
return repr_func()
155
156
def __hash__(self):
157
return hash(self.__repr__())
158
159
160
#############################################################################
161
# DATABASE Related code
162
#############################################################################
163
164
def version(self):
165
r"""
166
The version of Sage.
167
168
Call this to save the version of Sage in this object.
169
If you then save and load this object it will know in what
170
version of Sage it was created.
171
172
This only works on Python classes that derive from SageObject.
173
"""
174
try:
175
return self.__version
176
except AttributeError:
177
import sage.version
178
self.__version = sage.version.version
179
return self.__version
180
181
def save(self, filename=None, compress=True):
182
"""
183
Save self to the given filename.
184
185
EXAMPLES::
186
187
sage: f = x^3 + 5
188
sage: f.save(SAGE_TMP + '/file')
189
sage: load(SAGE_TMP + '/file.sobj')
190
x^3 + 5
191
"""
192
if filename is None:
193
try:
194
filename = self._default_filename
195
except AttributeError:
196
raise RuntimeError, "no default filename, so it must be specified"
197
filename = process(filename)
198
try:
199
self._default_filename = filename
200
except AttributeError:
201
pass
202
open(filename, 'wb').write(self.dumps(compress))
203
204
def dump(self, filename, compress=True):
205
"""
206
Same as self.save(filename, compress)
207
"""
208
return self.save(filename, compress=compress)
209
210
def dumps(self, compress=True):
211
"""
212
Dump self to a string s, which can later be reconstituted
213
as self using loads(s).
214
"""
215
# the protocol=2 is very important -- this enables
216
# saving extensions classes (with no attributes).
217
s = cPickle.dumps(self, protocol=2)
218
if compress:
219
return comp.compress(s)
220
else:
221
return s
222
223
def db(self, name, compress=True):
224
r"""
225
Dumps self into the Sage database. Use db(name) by itself to
226
reload.
227
228
The database directory is ``$HOME/.sage/db``
229
"""
230
#if name is None:
231
# name = self._db_name()
232
from sage.misc.all import SAGE_DB
233
return self.dump('%s/%s'%(SAGE_DB,name), compress=compress)
234
235
## def _db_name(self):
236
## t = str(type(self)).split()[-1][1:-2]
237
## try:
238
## d = str(self._defining_params_())
239
## except AttributeError:
240
## d = str(self)
241
## d = '_'.join(d.split())
242
## from sage.misc.all import SAGE_DB
243
## if not os.path.exists('%s/%s'%(SAGE_DB, t)):
244
## os.makedirs(t)
245
## return '%s/%s'%(t, d)
246
247
248
#############################################################################
249
# Category theory / structure
250
#############################################################################
251
252
def category(self):
253
from sage.categories.all import Objects
254
return Objects()
255
256
def _test_category(self, **options):
257
"""
258
Run generic tests on the method :meth:`.category`.
259
260
See also: :class:`TestSuite`.
261
262
EXAMPLES::
263
264
sage: O = SageObject()
265
sage: O._test_category()
266
267
Let us now write a broken :meth:`.category` method::
268
269
sage: class CCls(SageObject):
270
... def category(self):
271
... return 3
272
sage: CC = CCls()
273
sage: CC._test_category()
274
Traceback (most recent call last):
275
...
276
AssertionError: False is not true
277
"""
278
from sage.categories.category import Category
279
from sage.categories.objects import Objects
280
tester = self._tester(**options)
281
category = self.category()
282
tester.assert_(isinstance(category, Category))
283
tester.assert_(category.is_subcategory(Objects()))
284
tester.assert_(self in category)
285
286
## def category(self):
287
## try:
288
## return self.__category
289
## except AttributeError:
290
## from sage.categories.all import Objects
291
## return Objects()
292
293
## def _set_category(self, C):
294
## self.__category = C
295
296
#############################################################################
297
# Test framework
298
#############################################################################
299
300
def _tester(self, **options):
301
"""
302
Returns a gadget attached to ``self`` providing testing utilities.
303
304
This is used by :class:`sage.misc.sage_unittest.TestSuite` and the
305
``_test_*`` methods.
306
307
EXAMPLES::
308
309
sage: tester = ZZ._tester()
310
311
sage: tester.assert_(1 == 1)
312
sage: tester.assert_(1 == 0)
313
Traceback (most recent call last):
314
...
315
AssertionError: False is not true
316
sage: tester.assert_(1 == 0, "this is expected to fail")
317
Traceback (most recent call last):
318
...
319
AssertionError: this is expected to fail
320
321
sage: tester.assertEquals(1, 1)
322
sage: tester.assertEquals(1, 0)
323
Traceback (most recent call last):
324
...
325
AssertionError: 1 != 0
326
327
The available assertion testing facilities are the same as in
328
:class:`unittest.TestCase`, which see (actually, by a slight
329
abuse, tester is currently an instance of this class).
330
331
TESTS::
332
333
sage: ZZ._tester(tester = tester) is tester
334
True
335
"""
336
from sage.misc.sage_unittest import instance_tester
337
return instance_tester(self, **options)
338
339
def _test_not_implemented_methods(self, **options):
340
"""
341
Checks that all required methods for this object are implemented
342
343
TESTS::
344
345
sage: class Abstract(SageObject):
346
... @abstract_method
347
... def bla(self):
348
... "returns bla"
349
...
350
sage: class Concrete(Abstract):
351
... def bla(self):
352
... return 1
353
...
354
sage: class IncompleteConcrete(Abstract):
355
... pass
356
sage: Concrete()._test_not_implemented_methods()
357
sage: IncompleteConcrete()._test_not_implemented_methods()
358
Traceback (most recent call last):
359
...
360
AssertionError: Not implemented method: bla
361
362
"""
363
tester = self._tester(**options)
364
for name in dir(self):
365
try:
366
getattr(self, name)
367
except NotImplementedError:
368
# It would be best to make sure that this NotImplementedError was triggered by AbstractMethod
369
tester.fail("Not implemented method: %s"%name)
370
except:
371
pass
372
373
def _test_pickling(self, **options):
374
"""
375
Checks that this object can be pickled and unpickled properly.
376
377
EXAMPLES::
378
379
sage: ZZ._test_pickling()
380
381
SEE ALSO: :func:`dumps` :func:`loads`
382
383
TESTS::
384
385
sage: class Bla(SageObject): pass
386
sage: Bla()._test_pickling()
387
Traceback (most recent call last):
388
...
389
PicklingError: Can't pickle <class '__main__.Bla'>: attribute lookup __main__.Bla failed
390
391
TODO: for a stronger test, this could send the object to a
392
remote Sage session, and get it back.
393
"""
394
tester = self._tester(**options)
395
from sage.misc.all import loads, dumps
396
tester.assertEqual(loads(dumps(self)), self)
397
398
#############################################################################
399
# Coercions to interface objects
400
#############################################################################
401
402
# Sage
403
def _sage_(self):
404
return self
405
406
def _interface_(self, I):
407
"""
408
Return coercion of self to an object of the interface I.
409
410
The result of coercion is cached, unless self is not a C
411
extension class or ``self._interface_is_cached_()`` returns
412
False.
413
"""
414
c = self._interface_is_cached_()
415
if c:
416
try:
417
X = self.__interface[I]
418
X._check_valid()
419
return X
420
except (AttributeError, TypeError):
421
try:
422
self.__interface = {}
423
except AttributeError:
424
# do this because C-extension classes won't have
425
# an __interface attribute.
426
pass
427
except (KeyError, ValueError):
428
pass
429
nm = I.name()
430
init_func = getattr(self, '_%s_init_' % nm, None)
431
if init_func is not None:
432
s = init_func()
433
else:
434
try:
435
s = self._interface_init_(I)
436
except:
437
raise NotImplementedError, "coercion of object %s to %s not implemented:\n%s\n%s"%\
438
(repr(self), I)
439
X = I(s)
440
if c:
441
try:
442
self.__interface[I] = X
443
except AttributeError:
444
pass
445
return X
446
447
def _interface_init_(self, I=None):
448
return repr(self)
449
450
def _interface_is_cached_(self):
451
"""
452
Return True if the interface objects are cached.
453
454
If you have an object x and do gp(x), the result is cached if
455
this function returns True.
456
"""
457
return True
458
459
def _gap_(self, G=None):
460
if G is None:
461
import sage.interfaces.gap
462
G = sage.interfaces.gap.gap
463
return self._interface_(G)
464
465
def _gap_init_(self):
466
import sage.interfaces.gap
467
I = sage.interfaces.gap.gap
468
return self._interface_init_(I)
469
470
def _gp_(self, G=None):
471
if G is None:
472
import sage.interfaces.gp
473
G = sage.interfaces.gp.gp
474
return self._interface_(G)
475
476
def _gp_init_(self):
477
return self._pari_init_()
478
479
def _kash_(self, G=None):
480
if G is None:
481
import sage.interfaces.kash
482
G = sage.interfaces.kash.kash
483
return self._interface_(G)
484
485
def _kash_init_(self):
486
import sage.interfaces.kash
487
I = sage.interfaces.kash.kash
488
return self._interface_init_(I)
489
490
def _axiom_(self, G=None):
491
if G is None:
492
import sage.interfaces.axiom
493
G = sage.interfaces.axiom.axiom
494
return self._interface_(G)
495
496
def _axiom_init_(self):
497
import sage.interfaces.axiom
498
I = sage.interfaces.axiom.axiom
499
return self._interface_init_(I)
500
501
def _fricas_(self, G=None):
502
if G is None:
503
import sage.interfaces.fricas
504
G = sage.interfaces.fricas.fricas
505
return self._interface_(G)
506
507
def _fricas_init_(self):
508
import sage.interfaces.fricas
509
I = sage.interfaces.fricas.fricas
510
return self._interface_init_(I)
511
512
def _giac_(self, G=None):
513
if G is None:
514
import sage.interfaces.giac
515
G = sage.interfaces.giac.giac
516
return self._interface_(G)
517
518
def _giac_init_(self):
519
import sage.interfaces.giac
520
I = sage.interfaces.giac.giac
521
return self._interface_init_(I)
522
523
def _maxima_(self, G=None):
524
if G is None:
525
import sage.interfaces.maxima
526
G = sage.interfaces.maxima.maxima
527
return self._interface_(G)
528
529
def _maxima_init_(self):
530
import sage.interfaces.maxima
531
I = sage.interfaces.maxima.maxima
532
return self._interface_init_(I)
533
534
def _maxima_lib_(self, G=None):
535
from sage.interfaces.maxima_lib import maxima_lib
536
return self._interface_(maxima_lib)
537
538
def _maxima_lib_init_(self):
539
return self._maxima_init_()
540
541
def _magma_init_(self, magma):
542
"""
543
Given a Magma interpreter M, return a string that evaluates in
544
that interpreter to the Magma object corresponding to self.
545
This function may call the magma interpreter when it runs.
546
547
INPUT:
548
549
- ``magma`` -- a Magma interface
550
551
OUTPUT:
552
553
- string
554
555
EXAMPLES::
556
557
sage: n = -3/7
558
sage: n._magma_init_(magma)
559
'-3/7'
560
561
Some other examples that illustrate conversion to Magma.
562
::
563
564
sage: n = -3/7
565
sage: m2 = Magma()
566
sage: magma(n) # optional - magma
567
-3/7
568
sage: magma(n).parent() # optional - magma
569
Magma
570
sage: magma(n).parent() is m2 # optional - magma
571
False
572
sage: magma(n).parent() is magma # optional - magma
573
True
574
575
This example illustrates caching, which happens automatically
576
since K is a Python object::
577
578
sage: K.<a> = NumberField(x^3 + 2)
579
sage: magma(K) is magma(K) # optional - magma
580
True
581
sage: magma2 = Magma()
582
sage: magma(K) is magma2(K) # optional - magma
583
False
584
"""
585
return repr(self) # default
586
587
def _macaulay2_(self, G=None):
588
if G is None:
589
import sage.interfaces.macaulay2
590
G = sage.interfaces.macaulay2.macaulay2
591
return self._interface_(G)
592
593
def _macaulay2_init_(self):
594
import sage.interfaces.macaulay2
595
I = sage.interfaces.macaulay2.macaulay2
596
return self._interface_init_(I)
597
598
def _maple_(self, G=None):
599
if G is None:
600
import sage.interfaces.maple
601
G = sage.interfaces.maple.maple
602
return self._interface_(G)
603
604
def _maple_init_(self):
605
import sage.interfaces.maple
606
I = sage.interfaces.maple.maple
607
return self._interface_init_(I)
608
609
def _mathematica_(self, G=None):
610
if G is None:
611
import sage.interfaces.mathematica
612
G = sage.interfaces.mathematica.mathematica
613
return self._interface_(G)
614
615
def _mathematica_init_(self):
616
import sage.interfaces.mathematica
617
I = sage.interfaces.mathematica.mathematica
618
return self._interface_init_(I)
619
620
def _octave_(self, G=None):
621
if G is None:
622
import sage.interfaces.octave
623
G = sage.interfaces.octave.octave
624
return self._interface_(G)
625
626
def _octave_init_(self):
627
import sage.interfaces.octave
628
I = sage.interfaces.octave.octave
629
return self._interface_init_(I)
630
631
def _r_init_(self):
632
"""
633
Return default string expression that evaluates in R to this
634
object.
635
636
OUTPUT:
637
638
- string
639
640
EXAMPLES::
641
642
sage: a = 2/3
643
sage: a._r_init_()
644
'2/3'
645
"""
646
import sage.interfaces.r
647
I = sage.interfaces.r.r
648
return self._interface_init_(I)
649
650
def _singular_(self, G=None, have_ring=False):
651
if G is None:
652
import sage.interfaces.singular
653
G = sage.interfaces.singular.singular
654
return self._interface_(G)
655
656
def _singular_init_(self, have_ring=False):
657
import sage.interfaces.singular
658
I = sage.interfaces.singular.singular
659
return self._interface_init_(I)
660
661
# PARI (slightly different, since is via C library, hence instance is unique)
662
def _pari_(self):
663
if self._interface_is_cached_():
664
try:
665
return self.__pari
666
except AttributeError:
667
pass
668
from sage.libs.pari.all import pari
669
x = pari(self._pari_init_())
670
if self._interface_is_cached_():
671
try:
672
self.__pari = x
673
except AttributeError:
674
# do this because C-extension class won't have a __pari attribute.
675
pass
676
return x
677
678
def _pari_init_(self):
679
from sage.interfaces.gp import gp
680
return self._interface_init_(gp)
681
682
683
######################################################
684
# A python-accessible version of the one in coerce.pxi
685
# Where should it be?
686
687
def have_same_parent(self, other):
688
"""
689
EXAMPLES::
690
691
sage: from sage.structure.sage_object import have_same_parent
692
sage: have_same_parent(1, 3)
693
True
694
sage: have_same_parent(1, 1/2)
695
False
696
sage: have_same_parent(gap(1), gap(1/2))
697
True
698
"""
699
from sage.structure.coerce import parent
700
return parent(self) == parent(other)
701
702
##################################################################
703
704
def load(*filename, compress=True, verbose=True):
705
"""
706
Load Sage object from the file with name filename, which will have
707
an .sobj extension added if it doesn't have one. Or, if the input
708
is a filename ending in .py, .pyx, or .sage, load that file into
709
the current running session. Loaded files are not loaded into
710
their own namespace, i.e., this is much more like Python's
711
``execfile`` than Python's ``import``.
712
713
.. note::
714
715
There is also a special Sage command (that is not
716
available in Python) called load that you use by typing
717
718
::
719
720
sage: load filename.sage # not tested
721
722
The documentation below is not for that command. The
723
documentation for load is almost identical to that for attach.
724
Type attach? for help on attach.
725
726
This function also loads a ``.sobj`` file over a network by
727
specifying the full URL. (Setting ``verbose = False`` suppresses
728
the loading progress indicator.)
729
730
Finally, if you give multiple positional input arguments, then all
731
of those files are loaded, or all of the objects are loaded and a
732
list of the corresponding loaded objects is returned.
733
734
EXAMPLE::
735
736
sage: u = 'http://sage.math.washington.edu/home/was/db/test.sobj'
737
sage: s = load(u) # optional - internet
738
Attempting to load remote file: http://sage.math.washington.edu/home/was/db/test.sobj
739
Loading: [.]
740
sage: s # optional - internet
741
'hello SAGE'
742
743
We test loading a file or multiple files or even mixing loading files and objects::
744
745
sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
746
sage: load(t)
747
hello world
748
sage: load(t,t)
749
hello world
750
hello world
751
sage: t2=tmp_filename(); save(2/3,t2)
752
sage: load(t,t,t2)
753
hello world
754
hello world
755
[None, None, 2/3]
756
"""
757
import sage.misc.preparser
758
if len(filename) != 1:
759
v = [load(file, compress=True, verbose=True) for file in filename]
760
ret = False
761
for file in filename:
762
if not sage.misc.preparser.is_loadable_filename(file):
763
ret = True
764
return v if ret else None
765
else:
766
filename = filename[0]
767
768
if sage.misc.preparser.is_loadable_filename(filename):
769
sage.misc.preparser.load(filename, globals())
770
return
771
772
## Check if filename starts with "http://" or "https://"
773
lower = filename.lower()
774
if lower.startswith("http://") or lower.startswith("https://"):
775
from sage.misc.remote_file import get_remote_file
776
filename = get_remote_file(filename, verbose=verbose)
777
tmpfile_flag = True
778
elif lower.endswith('.f') or lower.endswith('.f90'):
779
globals()['fortran'](filename)
780
return
781
else:
782
tmpfile_flag = False
783
filename = process(filename)
784
785
## Load file by absolute filename
786
X = loads(open(filename).read(), compress=compress)
787
try:
788
X._default_filename = os.path.abspath(filename)
789
except AttributeError:
790
pass
791
792
## Delete the tempfile, if it exists
793
if tmpfile_flag == True:
794
os.unlink(filename)
795
796
return X
797
798
799
def save(obj, filename=None, compress=True, **kwds):
800
"""
801
Save ``obj`` to the file with name ``filename``, which will have an
802
``.sobj`` extension added if it doesn't have one, or if ``obj``
803
doesn't have its own ``save()`` method, like e.g. Python tuples.
804
805
For image objects and the like (which have their own ``save()`` method),
806
you may have to specify a specific extension, e.g. ``.png``, if you
807
don't want the object to be saved as a Sage object (or likewise, if
808
``filename`` could be interpreted as already having some extension).
809
810
.. warning::
811
812
This will *replace* the contents of the file if it already exists.
813
814
EXAMPLES::
815
816
sage: a = matrix(2, [1,2,3,-5/2])
817
sage: objfile = SAGE_TMP + 'test.sobj'
818
sage: objfile_short = SAGE_TMP + 'test'
819
sage: save(a, objfile)
820
sage: load(objfile_short)
821
[ 1 2]
822
[ 3 -5/2]
823
sage: E = EllipticCurve([-1,0])
824
sage: P = plot(E)
825
sage: save(P, objfile_short) # saves the plot to "test.sobj"
826
sage: save(P, filename=SAGE_TMP + "sage.png", xmin=-2)
827
sage: save(P, SAGE_TMP + "filename.with.some.wrong.ext")
828
Traceback (most recent call last):
829
...
830
ValueError: allowed file extensions for images are '.eps', '.pdf', '.png', '.ps', '.sobj', '.svg'!
831
sage: print load(objfile)
832
Graphics object consisting of 2 graphics primitives
833
sage: save("A python string", SAGE_TMP + 'test')
834
sage: load(objfile)
835
'A python string'
836
sage: load(objfile_short)
837
'A python string'
838
839
TESTS:
840
841
Check that #11577 is fixed::
842
843
sage: filename = SAGE_TMP + "foo.bar" # filename containing a dot
844
sage: save((1,1),filename) # saves tuple to "foo.bar.sobj"
845
sage: load(filename)
846
(1, 1)
847
"""
848
# Add '.sobj' if the filename currently has no extension
849
# and also if the object doesn't have its own save() method (#11577)
850
# since we otherwise assume below it is an image object:
851
if (os.path.splitext(filename)[1] == ''
852
or (os.path.splitext(filename)[1] != '.sobj'
853
and not hasattr(obj,"save"))):
854
filename += '.sobj'
855
856
if filename.endswith('.sobj'):
857
try:
858
obj.save(filename=filename, compress=compress, **kwds)
859
except (AttributeError, RuntimeError, TypeError):
860
s = cPickle.dumps(obj, protocol=2)
861
if compress:
862
s = comp.compress(s)
863
open(process(filename), 'wb').write(s)
864
else:
865
# Saving an object to an image file.
866
obj.save(filename, **kwds)
867
868
def dumps(obj, compress=True):
869
"""
870
Dump obj to a string s. To recover obj, use ``loads(s)``.
871
872
.. seealso:: :func:`dumps`
873
874
EXAMPLES::
875
876
sage: a = 2/3
877
sage: s = dumps(a)
878
sage: print len(s)
879
49
880
sage: loads(s)
881
2/3
882
"""
883
if make_pickle_jar:
884
picklejar(obj)
885
try:
886
return obj.dumps(compress)
887
except (AttributeError, RuntimeError, TypeError):
888
if compress:
889
return comp.compress(cPickle.dumps(obj, protocol=2))
890
else:
891
return cPickle.dumps(obj, protocol=2)
892
893
# This is used below, and also by explain_pickle.py
894
unpickle_override = {}
895
896
def register_unpickle_override(module, name, callable, call_name=None):
897
r"""
898
Python pickles include the module and class name of classes.
899
This means that rearranging the Sage source can invalidate old
900
pickles. To keep the old pickles working, you can call
901
register_unpickle_override with an old module name and class name,
902
and the Python callable (function, class with __call__ method, etc.)
903
to use for unpickling. (If this callable is a value in some module,
904
you can specify the module name and class name, for the benefit of
905
explain_pickle(..., in_current_sage=True).)
906
907
EXAMPLES::
908
909
sage: from sage.structure.sage_object import unpickle_override, register_unpickle_override
910
sage: unpickle_global('sage.rings.integer', 'Integer')
911
<type 'sage.rings.integer.Integer'>
912
913
Now we horribly break the pickling system::
914
915
sage: register_unpickle_override('sage.rings.integer', 'Integer', Rational, call_name=('sage.rings.rational', 'Rational'))
916
sage: unpickle_global('sage.rings.integer', 'Integer')
917
<type 'sage.rings.rational.Rational'>
918
919
And we reach into the internals and put it back::
920
921
sage: del unpickle_override[('sage.rings.integer', 'Integer')]
922
sage: unpickle_global('sage.rings.integer', 'Integer')
923
<type 'sage.rings.integer.Integer'>
924
"""
925
unpickle_override[(module,name)] = (callable, call_name)
926
927
def unpickle_global(module, name):
928
r"""
929
Given a module name and a name within that module (typically a class
930
name), retrieve the corresponding object. This normally just looks
931
up the name in the module, but it can be overridden by
932
register_unpickle_override. This is used in the Sage unpickling
933
mechanism, so if the Sage source code organization changes,
934
register_unpickle_override can allow old pickles to continue to work.
935
936
EXAMPLES::
937
938
sage: from sage.structure.sage_object import unpickle_override, register_unpickle_override
939
sage: unpickle_global('sage.rings.integer', 'Integer')
940
<type 'sage.rings.integer.Integer'>
941
942
Now we horribly break the pickling system::
943
944
sage: register_unpickle_override('sage.rings.integer', 'Integer', Rational, call_name=('sage.rings.rational', 'Rational'))
945
sage: unpickle_global('sage.rings.integer', 'Integer')
946
<type 'sage.rings.rational.Rational'>
947
948
And we reach into the internals and put it back::
949
950
sage: del unpickle_override[('sage.rings.integer', 'Integer')]
951
sage: unpickle_global('sage.rings.integer', 'Integer')
952
<type 'sage.rings.integer.Integer'>
953
"""
954
unpickler = unpickle_override.get((module, name))
955
if unpickler is not None:
956
return unpickler[0]
957
958
mod = sys_modules.get(module)
959
if mod is not None:
960
return getattr(mod, name)
961
__import__(module)
962
mod = sys_modules[module]
963
return getattr(mod, name)
964
965
def loads(s, compress=True):
966
"""
967
Recover an object x that has been dumped to a string s
968
using ``s = dumps(x)``.
969
970
.. seealso:: :func:`dumps`
971
972
EXAMPLES::
973
974
sage: a = matrix(2, [1,2,3,-4/3])
975
sage: s = dumps(a)
976
sage: loads(s)
977
[ 1 2]
978
[ 3 -4/3]
979
980
If compress is True (the default), it will try to decompress
981
the data with zlib and with bz2 (in turn); if neither succeeds,
982
it will assume the data is actually uncompressed. If compress=False
983
is explicitly specified, then no decompression is attempted.
984
985
::
986
987
sage: v = [1..10]
988
sage: loads(dumps(v, compress=False)) == v
989
True
990
sage: loads(dumps(v, compress=False), compress=True) == v
991
True
992
sage: loads(dumps(v, compress=True), compress=False)
993
Traceback (most recent call last):
994
...
995
UnpicklingError: invalid load key, 'x'.
996
"""
997
if not isinstance(s, str):
998
raise TypeError, "s must be a string"
999
if compress:
1000
try:
1001
s = comp.decompress(s)
1002
except Exception, msg1:
1003
try:
1004
s = comp_other.decompress(s)
1005
except Exception, msg2:
1006
# Maybe data is uncompressed?
1007
pass
1008
1009
unpickler = cPickle.Unpickler(StringIO(s))
1010
unpickler.find_global = unpickle_global
1011
1012
return unpickler.load()
1013
1014
1015
cdef bint make_pickle_jar = os.environ.has_key('SAGE_PICKLE_JAR')
1016
1017
def picklejar(obj, dir=None):
1018
"""
1019
Create pickled sobj of obj in dir, with name the absolute value of
1020
the hash of the pickle of obj. This is used in conjunction with
1021
sage.structure.sage_object.unpickle_all.
1022
1023
To use this to test the whole Sage library right now, set the
1024
environment variable SAGE_PICKLE_JAR, which will make it so dumps
1025
will by default call picklejar with the default dir. Once you do
1026
that and doctest Sage, you'll find that the SAGE_ROOT/tmp/
1027
contains a bunch of pickled objects along with corresponding txt
1028
descriptions of them. Use the
1029
sage.structure.sage_object.unpickle_all to see if they unpickle
1030
later.
1031
1032
INPUTS:
1033
1034
- ``obj`` -- a pickleable object
1035
1036
- ``dir`` -- a string or None; if None defaults to
1037
``SAGE_ROOT/tmp/pickle_jar``
1038
1039
EXAMPLES::
1040
1041
sage: dir = tmp_dir()
1042
sage: sage.structure.sage_object.picklejar(1, dir)
1043
sage: sage.structure.sage_object.picklejar('test', dir)
1044
sage: len(os.listdir(dir)) # Two entries (sobj and txt) for each object
1045
4
1046
1047
TESTS:
1048
1049
Test an unaccessible directory::
1050
1051
sage: import os
1052
sage: os.chmod(dir, 0000)
1053
sage: sage.structure.sage_object.picklejar(1, dir + '/noaccess')
1054
Traceback (most recent call last):
1055
...
1056
OSError: ...
1057
sage: os.chmod(dir, 0755)
1058
"""
1059
if dir is None:
1060
dir = os.environ['SAGE_ROOT'] + '/tmp/pickle_jar/'
1061
try:
1062
os.makedirs(dir)
1063
except OSError as err:
1064
# It is not an error if the directory exists
1065
import errno
1066
if not err.errno == errno.EEXIST:
1067
raise
1068
1069
s = comp.compress(cPickle.dumps(obj,protocol=2))
1070
1071
typ = str(type(obj))
1072
name = ''.join([x if (x.isalnum() or x == '_') else '_' for x in typ])
1073
base = '%s/%s'%(dir, name)
1074
if os.path.exists(base):
1075
i = 0
1076
while os.path.exists(base + '-%s'%i):
1077
i += 1
1078
base += '-%s'%i
1079
1080
open(base + '.sobj', 'wb').write(s)
1081
txt = "type(obj) = %s\n"%typ
1082
import sage.version
1083
txt += "version = %s\n"%sage.version.version
1084
txt += "obj =\n'%s'\n"%str(obj)
1085
1086
open(base + '.txt', 'w').write(txt)
1087
1088
def unpickle_all(dir = None, debug=False, run_test_suite=False):
1089
"""
1090
Unpickle all sobj's in the given directory, reporting failures as
1091
they occur. Also printed the number of successes and failure.
1092
1093
INPUT:
1094
1095
- ``dir`` -- a string; the name of a directory (or of a .tar.bz2
1096
file that decompresses to a directory) full of pickles.
1097
(default: the standard pickle jar)
1098
- ``debug`` -- a boolean (default: False)
1099
whether to report a stacktrace in case of failure
1100
- ``run_test_suite`` -- a boolean (default: False)
1101
whether to run ``TestSuite(x).run()`` on the unpickled objects
1102
1103
EXAMPLES::
1104
1105
sage: dir = tmp_dir()
1106
sage: sage.structure.sage_object.picklejar('hello', dir)
1107
sage: sage.structure.sage_object.unpickle_all(dir)
1108
Successfully unpickled 1 objects.
1109
Failed to unpickle 0 objects.
1110
1111
We unpickle the standard pickle jar. This doctest tests that
1112
all "standard pickles" unpickle::
1113
1114
sage: sage.structure.sage_object.unpickle_all() # (4s on sage.math, 2011)
1115
doctest:... DeprecationWarning: This class is replaced by Matrix_modn_dense_float/Matrix_modn_dense_double.
1116
Successfully unpickled ... objects.
1117
Failed to unpickle 0 objects.
1118
1119
Every so often the standard pickle jar should be updated by
1120
running the doctest suite with the environment variable
1121
SAGE_PICKLE_JAR set, then copying the files from
1122
SAGE_ROOT/tmp/pickle_jar* into the standard pickle jar.
1123
1124
Do you want to find *lots* of little issues in Sage? Run the following:
1125
1126
sage: print "x"; sage.structure.sage_object.unpickle_all(run_test_suite = True) # todo: not tested
1127
1128
This runs :class:`TestSuite` tests on all objects in the Sage pickle
1129
jar. Some of those objects seem to unpickle properly, but do not
1130
pass the tests because their internal data structure is messed
1131
up. In most cases though it is just that their source file misses
1132
a TestSuite call, and therefore some misfeatures went unnoticed
1133
(typically Parents not implementing the ``an_element`` method).
1134
"""
1135
if dir is None:
1136
dir = os.environ['SAGE_DATA'] + '/extcode/pickle_jar/pickle_jar.tar.bz2'
1137
i = 0
1138
j = 0
1139
failed = []
1140
tracebacks = []
1141
# This could use instead Python's tarfile module
1142
if dir.endswith('.tar.bz2'):
1143
# create a temporary directory
1144
from sage.misc.all import tmp_dir
1145
T = tmp_dir()
1146
# extract tarball to it
1147
os.system('cd "%s"; bunzip2 -c "%s" | tar fx - '%(T, os.path.abspath(dir)))
1148
# Now use the directory in the tarball instead of dir
1149
dir = T + "/" + os.listdir(T)[0]
1150
1151
for A in sorted(os.listdir(dir)):
1152
if A.endswith('.sobj'):
1153
try:
1154
object = load(dir + '/' + A)
1155
if run_test_suite:
1156
TestSuite(object).run(catch = False)
1157
i += 1
1158
except Exception, msg:
1159
j += 1
1160
if run_test_suite:
1161
print " * unpickle failure: TestSuite(load('%s')).run()"%(dir+'/'+A)
1162
else:
1163
print " * unpickle failure: load('%s')"%(dir+'/'+A)
1164
failed.append(A)
1165
if debug:
1166
tracebacks.append(sys.exc_info())
1167
1168
if len(failed) > 0:
1169
print "Failed:\n%s"%('\n'.join(failed))
1170
print "Successfully unpickled %s objects."%i
1171
print "Failed to unpickle %s objects."%j
1172
if debug:
1173
return tracebacks
1174
1175
1176