Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/structure/sage_object.pyx
8814 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 base is not None and not os.path.isabs(s):
22
s = os.path.join(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 AttributeError:
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 _ascii_art_(self):
157
r"""
158
Return an ASCII art representation.
159
160
To implement multi-line ASCII art output in a derived class
161
you must override this method. Unlike :meth:`_repr_`, which is
162
sometimes used for the hash key, the output of
163
:meth:`_ascii_art_` may depend on settings and is allowed to
164
change during runtime. For example,
165
:meth:`~sage.combinat.tableau.Tableau.set_ascii_art` can be
166
used to switch the ASCII art of tableaux between different
167
mathematical conventions.
168
169
OUTPUT:
170
171
An :class:`~sage.misc.ascii_art.AsciiArt` object, see
172
:mod:`sage.misc.ascii_art` for details.
173
174
EXAMPLES:
175
176
You can use the :func:`~sage.misc.ascii_art.ascii_art` function
177
to get the ASCII art representation of any object in Sage::
178
179
sage: ascii_art(integral(exp(x+x^2)/(x+1), x))
180
/
181
|
182
| 2
183
| x + x
184
| e
185
| ------- dx
186
| x + 1
187
|
188
/
189
190
Alternatively, you can use the ``%display ascii_art/simple`` magic to
191
switch all output to ASCII art and back::
192
193
sage: from sage.misc.interpreter import get_test_shell
194
sage: shell = get_test_shell()
195
sage: shell.run_cell('tab = StandardTableaux(3)[2]; tab')
196
[[1, 2], [3]]
197
sage: shell.run_cell('%display ascii_art')
198
sage: shell.run_cell('tab')
199
1 2
200
3
201
sage: shell.run_cell('Tableaux.global_options(ascii_art="table", convention="French")')
202
sage: shell.run_cell('tab')
203
+---+
204
| 3 |
205
+---+---+
206
| 1 | 2 |
207
+---+---+
208
sage: shell.run_cell('%display simple')
209
sage: shell.run_cell('Tableaux.global_options.reset()')
210
211
TESTS::
212
213
sage: 1._ascii_art_()
214
1
215
sage: type(_)
216
<class 'sage.misc.ascii_art.AsciiArt'>
217
"""
218
from sage.misc.ascii_art import AsciiArt
219
return AsciiArt(repr(self).splitlines())
220
221
def __hash__(self):
222
return hash(self.__repr__())
223
224
225
#############################################################################
226
# DATABASE Related code
227
#############################################################################
228
229
def version(self):
230
r"""
231
The version of Sage.
232
233
Call this to save the version of Sage in this object.
234
If you then save and load this object it will know in what
235
version of Sage it was created.
236
237
This only works on Python classes that derive from SageObject.
238
"""
239
try:
240
return self.__version
241
except AttributeError:
242
import sage.version
243
self.__version = sage.version.version
244
return self.__version
245
246
def save(self, filename=None, compress=True):
247
"""
248
Save self to the given filename.
249
250
EXAMPLES::
251
252
sage: f = x^3 + 5
253
sage: f.save(os.path.join(SAGE_TMP, 'file'))
254
sage: load(os.path.join(SAGE_TMP, 'file.sobj'))
255
x^3 + 5
256
"""
257
if filename is None:
258
try:
259
filename = self._default_filename
260
except AttributeError:
261
raise RuntimeError, "no default filename, so it must be specified"
262
filename = process(filename)
263
try:
264
self._default_filename = filename
265
except AttributeError:
266
pass
267
open(filename, 'wb').write(self.dumps(compress))
268
269
def dump(self, filename, compress=True):
270
"""
271
Same as self.save(filename, compress)
272
"""
273
return self.save(filename, compress=compress)
274
275
def dumps(self, compress=True):
276
r"""
277
Dump ``self`` to a string ``s``, which can later be reconstituted
278
as ``self`` using ``loads(s)``.
279
280
There is an optional boolean argument ``compress`` which defaults to ``True``.
281
282
EXAMPLES::
283
284
sage: O=SageObject(); O.dumps()
285
'x\x9ck`J.NLO\xd5+.)*M.)-\x02\xb2\x80\xdc\xf8\xfc\xa4\xac\xd4\xe4\x12\xae` \xdb\x1f\xc2,d\xd4l,d\xd2\x03\x00\xb7X\x10\xf1'
286
sage: O.dumps(compress=False)
287
'\x80\x02csage.structure.sage_object\nSageObject\nq\x01)\x81q\x02.'
288
"""
289
# the protocol=2 is very important -- this enables
290
# saving extensions classes (with no attributes).
291
s = cPickle.dumps(self, protocol=2)
292
if compress:
293
return comp.compress(s)
294
else:
295
return s
296
297
def db(self, name, compress=True):
298
r"""
299
Dumps self into the Sage database. Use db(name) by itself to
300
reload.
301
302
The database directory is ``$HOME/.sage/db``
303
"""
304
#if name is None:
305
# name = self._db_name()
306
from sage.misc.all import SAGE_DB
307
return self.dump('%s/%s'%(SAGE_DB,name), compress=compress)
308
309
## def _db_name(self):
310
## t = str(type(self)).split()[-1][1:-2]
311
## try:
312
## d = str(self._defining_params_())
313
## except AttributeError:
314
## d = str(self)
315
## d = '_'.join(d.split())
316
## from sage.misc.all import SAGE_DB
317
## if not os.path.exists('%s/%s'%(SAGE_DB, t)):
318
## os.makedirs(t)
319
## return '%s/%s'%(t, d)
320
321
322
#############################################################################
323
# Category theory / structure
324
#############################################################################
325
326
def category(self):
327
from sage.categories.all import Objects
328
return Objects()
329
330
def _test_category(self, **options):
331
"""
332
Run generic tests on the method :meth:`.category`.
333
334
See also: :class:`TestSuite`.
335
336
EXAMPLES::
337
338
sage: O = SageObject()
339
sage: O._test_category()
340
341
Let us now write a broken :meth:`.category` method::
342
343
sage: class CCls(SageObject):
344
... def category(self):
345
... return 3
346
...
347
sage: CC = CCls()
348
sage: CC._test_category()
349
Traceback (most recent call last):
350
...
351
AssertionError: False is not true
352
"""
353
from sage.categories.category import Category
354
from sage.categories.objects import Objects
355
tester = self._tester(**options)
356
category = self.category()
357
tester.assert_(isinstance(category, Category))
358
tester.assert_(category.is_subcategory(Objects()))
359
tester.assert_(self in category)
360
361
## def category(self):
362
## try:
363
## return self.__category
364
## except AttributeError:
365
## from sage.categories.all import Objects
366
## return Objects()
367
368
## def _set_category(self, C):
369
## self.__category = C
370
371
#############################################################################
372
# Test framework
373
#############################################################################
374
375
def _tester(self, **options):
376
"""
377
Returns a gadget attached to ``self`` providing testing utilities.
378
379
This is used by :class:`sage.misc.sage_unittest.TestSuite` and the
380
``_test_*`` methods.
381
382
EXAMPLES::
383
384
sage: tester = ZZ._tester()
385
386
sage: tester.assert_(1 == 1)
387
sage: tester.assert_(1 == 0)
388
Traceback (most recent call last):
389
...
390
AssertionError: False is not true
391
sage: tester.assert_(1 == 0, "this is expected to fail")
392
Traceback (most recent call last):
393
...
394
AssertionError: this is expected to fail
395
396
sage: tester.assertEquals(1, 1)
397
sage: tester.assertEquals(1, 0)
398
Traceback (most recent call last):
399
...
400
AssertionError: 1 != 0
401
402
The available assertion testing facilities are the same as in
403
:class:`unittest.TestCase`, which see (actually, by a slight
404
abuse, tester is currently an instance of this class).
405
406
TESTS::
407
408
sage: ZZ._tester(tester = tester) is tester
409
True
410
"""
411
from sage.misc.sage_unittest import instance_tester
412
return instance_tester(self, **options)
413
414
def _test_not_implemented_methods(self, **options):
415
"""
416
Checks that all required methods for this object are implemented
417
418
TESTS::
419
420
sage: class Abstract(SageObject):
421
... @abstract_method
422
... def bla(self):
423
... "returns bla"
424
...
425
sage: class Concrete(Abstract):
426
... def bla(self):
427
... return 1
428
...
429
sage: class IncompleteConcrete(Abstract):
430
... pass
431
...
432
sage: Concrete()._test_not_implemented_methods()
433
sage: IncompleteConcrete()._test_not_implemented_methods()
434
Traceback (most recent call last):
435
...
436
AssertionError: Not implemented method: bla
437
438
"""
439
tester = self._tester(**options)
440
for name in dir(self):
441
try:
442
getattr(self, name)
443
except NotImplementedError:
444
# It would be best to make sure that this NotImplementedError was triggered by AbstractMethod
445
tester.fail("Not implemented method: %s"%name)
446
except Exception:
447
pass
448
449
def _test_pickling(self, **options):
450
"""
451
Checks that this object can be pickled and unpickled properly.
452
453
EXAMPLES::
454
455
sage: ZZ._test_pickling()
456
457
SEE ALSO: :func:`dumps` :func:`loads`
458
459
TESTS::
460
461
sage: class Bla(SageObject): pass
462
sage: Bla()._test_pickling()
463
Traceback (most recent call last):
464
...
465
PicklingError: Can't pickle <class '__main__.Bla'>: attribute lookup __main__.Bla failed
466
467
TODO: for a stronger test, this could send the object to a
468
remote Sage session, and get it back.
469
"""
470
tester = self._tester(**options)
471
from sage.misc.all import loads, dumps
472
tester.assertEqual(loads(dumps(self)), self)
473
474
#############################################################################
475
# Coercions to interface objects
476
#############################################################################
477
478
# Sage
479
def _sage_(self):
480
return self
481
482
def _interface_(self, I):
483
"""
484
Return coercion of self to an object of the interface I.
485
486
The result of coercion is cached, unless self is not a C
487
extension class or ``self._interface_is_cached_()`` returns
488
False.
489
"""
490
c = self._interface_is_cached_()
491
if c:
492
try:
493
X = self.__interface[I]
494
X._check_valid()
495
return X
496
except (AttributeError, TypeError):
497
try:
498
self.__interface = {}
499
except AttributeError:
500
# do this because C-extension classes won't have
501
# an __interface attribute.
502
pass
503
except (KeyError, ValueError):
504
pass
505
nm = I.name()
506
init_func = getattr(self, '_%s_init_' % nm, None)
507
if init_func is not None:
508
s = init_func()
509
else:
510
try:
511
s = self._interface_init_(I)
512
except Exception:
513
raise NotImplementedError, "coercion of object %s to %s not implemented:\n%s\n%s"%\
514
(repr(self), I)
515
X = I(s)
516
if c:
517
try:
518
self.__interface[I] = X
519
except AttributeError:
520
pass
521
return X
522
523
def _interface_init_(self, I=None):
524
return repr(self)
525
526
def _interface_is_cached_(self):
527
"""
528
Return True if the interface objects are cached.
529
530
If you have an object x and do gp(x), the result is cached if
531
this function returns True.
532
"""
533
return True
534
535
def _gap_(self, G=None):
536
if G is None:
537
import sage.interfaces.gap
538
G = sage.interfaces.gap.gap
539
return self._interface_(G)
540
541
def _gap_init_(self):
542
import sage.interfaces.gap
543
I = sage.interfaces.gap.gap
544
return self._interface_init_(I)
545
546
def _gp_(self, G=None):
547
if G is None:
548
import sage.interfaces.gp
549
G = sage.interfaces.gp.gp
550
return self._interface_(G)
551
552
def _gp_init_(self):
553
return self._pari_init_()
554
555
def _kash_(self, G=None):
556
if G is None:
557
import sage.interfaces.kash
558
G = sage.interfaces.kash.kash
559
return self._interface_(G)
560
561
def _kash_init_(self):
562
import sage.interfaces.kash
563
I = sage.interfaces.kash.kash
564
return self._interface_init_(I)
565
566
def _axiom_(self, G=None):
567
if G is None:
568
import sage.interfaces.axiom
569
G = sage.interfaces.axiom.axiom
570
return self._interface_(G)
571
572
def _axiom_init_(self):
573
import sage.interfaces.axiom
574
I = sage.interfaces.axiom.axiom
575
return self._interface_init_(I)
576
577
def _fricas_(self, G=None):
578
if G is None:
579
import sage.interfaces.fricas
580
G = sage.interfaces.fricas.fricas
581
return self._interface_(G)
582
583
def _fricas_init_(self):
584
import sage.interfaces.fricas
585
I = sage.interfaces.fricas.fricas
586
return self._interface_init_(I)
587
588
def _giac_(self, G=None):
589
if G is None:
590
import sage.interfaces.giac
591
G = sage.interfaces.giac.giac
592
return self._interface_(G)
593
594
def _giac_init_(self):
595
import sage.interfaces.giac
596
I = sage.interfaces.giac.giac
597
return self._interface_init_(I)
598
599
def _maxima_(self, G=None):
600
if G is None:
601
import sage.interfaces.maxima
602
G = sage.interfaces.maxima.maxima
603
return self._interface_(G)
604
605
def _maxima_init_(self):
606
import sage.interfaces.maxima
607
I = sage.interfaces.maxima.maxima
608
return self._interface_init_(I)
609
610
def _maxima_lib_(self, G=None):
611
from sage.interfaces.maxima_lib import maxima_lib
612
return self._interface_(maxima_lib)
613
614
def _maxima_lib_init_(self):
615
return self._maxima_init_()
616
617
def _magma_init_(self, magma):
618
"""
619
Given a Magma interpreter M, return a string that evaluates in
620
that interpreter to the Magma object corresponding to self.
621
This function may call the magma interpreter when it runs.
622
623
INPUT:
624
625
- ``magma`` -- a Magma interface
626
627
OUTPUT:
628
629
- string
630
631
EXAMPLES::
632
633
sage: n = -3/7
634
sage: n._magma_init_(magma)
635
'-3/7'
636
637
Some other examples that illustrate conversion to Magma.
638
::
639
640
sage: n = -3/7
641
sage: m2 = Magma()
642
sage: magma(n) # optional - magma
643
-3/7
644
sage: magma(n).parent() # optional - magma
645
Magma
646
sage: magma(n).parent() is m2 # optional - magma
647
False
648
sage: magma(n).parent() is magma # optional - magma
649
True
650
651
This example illustrates caching, which happens automatically
652
since K is a Python object::
653
654
sage: K.<a> = NumberField(x^3 + 2)
655
sage: magma(K) is magma(K) # optional - magma
656
True
657
sage: magma2 = Magma()
658
sage: magma(K) is magma2(K) # optional - magma
659
False
660
"""
661
return repr(self) # default
662
663
def _macaulay2_(self, G=None):
664
if G is None:
665
import sage.interfaces.macaulay2
666
G = sage.interfaces.macaulay2.macaulay2
667
return self._interface_(G)
668
669
def _macaulay2_init_(self):
670
import sage.interfaces.macaulay2
671
I = sage.interfaces.macaulay2.macaulay2
672
return self._interface_init_(I)
673
674
def _maple_(self, G=None):
675
if G is None:
676
import sage.interfaces.maple
677
G = sage.interfaces.maple.maple
678
return self._interface_(G)
679
680
def _maple_init_(self):
681
import sage.interfaces.maple
682
I = sage.interfaces.maple.maple
683
return self._interface_init_(I)
684
685
def _mathematica_(self, G=None):
686
if G is None:
687
import sage.interfaces.mathematica
688
G = sage.interfaces.mathematica.mathematica
689
return self._interface_(G)
690
691
def _mathematica_init_(self):
692
import sage.interfaces.mathematica
693
I = sage.interfaces.mathematica.mathematica
694
return self._interface_init_(I)
695
696
def _octave_(self, G=None):
697
if G is None:
698
import sage.interfaces.octave
699
G = sage.interfaces.octave.octave
700
return self._interface_(G)
701
702
def _octave_init_(self):
703
import sage.interfaces.octave
704
I = sage.interfaces.octave.octave
705
return self._interface_init_(I)
706
707
def _r_init_(self):
708
"""
709
Return default string expression that evaluates in R to this
710
object.
711
712
OUTPUT:
713
714
- string
715
716
EXAMPLES::
717
718
sage: a = 2/3
719
sage: a._r_init_()
720
'2/3'
721
"""
722
import sage.interfaces.r
723
I = sage.interfaces.r.r
724
return self._interface_init_(I)
725
726
def _singular_(self, G=None, have_ring=False):
727
if G is None:
728
import sage.interfaces.singular
729
G = sage.interfaces.singular.singular
730
return self._interface_(G)
731
732
def _singular_init_(self, have_ring=False):
733
import sage.interfaces.singular
734
I = sage.interfaces.singular.singular
735
return self._interface_init_(I)
736
737
# PARI (slightly different, since is via C library, hence instance is unique)
738
def _pari_(self):
739
if self._interface_is_cached_():
740
try:
741
return self.__pari
742
except AttributeError:
743
pass
744
from sage.libs.pari.all import pari
745
x = pari(self._pari_init_())
746
if self._interface_is_cached_():
747
try:
748
self.__pari = x
749
except AttributeError:
750
# do this because C-extension class won't have a __pari attribute.
751
pass
752
return x
753
754
def _pari_init_(self):
755
from sage.interfaces.gp import gp
756
return self._interface_init_(gp)
757
758
759
######################################################
760
# A python-accessible version of the one in coerce.pxi
761
# Where should it be?
762
763
def have_same_parent(self, other):
764
"""
765
EXAMPLES::
766
767
sage: from sage.structure.sage_object import have_same_parent
768
sage: have_same_parent(1, 3)
769
True
770
sage: have_same_parent(1, 1/2)
771
False
772
sage: have_same_parent(gap(1), gap(1/2))
773
True
774
"""
775
from sage.structure.coerce import parent
776
return parent(self) == parent(other)
777
778
##################################################################
779
780
def load(*filename, compress=True, verbose=True):
781
"""
782
Load Sage object from the file with name filename, which will have
783
an .sobj extension added if it doesn't have one. Or, if the input
784
is a filename ending in .py, .pyx, or .sage, load that file into
785
the current running session. Loaded files are not loaded into
786
their own namespace, i.e., this is much more like Python's
787
``execfile`` than Python's ``import``.
788
789
.. NOTE::
790
791
There is also a special Sage command (that is not
792
available in Python) called load that you use by typing
793
794
::
795
796
sage: load filename.sage # not tested
797
798
The documentation below is not for that command. The
799
documentation for load is almost identical to that for attach.
800
Type attach? for help on attach.
801
802
This function also loads a ``.sobj`` file over a network by
803
specifying the full URL. (Setting ``verbose = False`` suppresses
804
the loading progress indicator.)
805
806
Finally, if you give multiple positional input arguments, then all
807
of those files are loaded, or all of the objects are loaded and a
808
list of the corresponding loaded objects is returned.
809
810
EXAMPLE::
811
812
sage: u = 'http://sage.math.washington.edu/home/was/db/test.sobj'
813
sage: s = load(u) # optional - internet
814
Attempting to load remote file: http://sage.math.washington.edu/home/was/db/test.sobj
815
Loading: [.]
816
sage: s # optional - internet
817
'hello SAGE'
818
819
We test loading a file or multiple files or even mixing loading files and objects::
820
821
sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
822
sage: load(t)
823
hello world
824
sage: load(t,t)
825
hello world
826
hello world
827
sage: t2=tmp_filename(); save(2/3,t2)
828
sage: load(t,t,t2)
829
hello world
830
hello world
831
[None, None, 2/3]
832
"""
833
import sage.misc.preparser
834
if len(filename) != 1:
835
v = [load(file, compress=True, verbose=True) for file in filename]
836
ret = False
837
for file in filename:
838
if not sage.misc.preparser.is_loadable_filename(file):
839
ret = True
840
return v if ret else None
841
else:
842
filename = filename[0]
843
844
if sage.misc.preparser.is_loadable_filename(filename):
845
sage.misc.preparser.load(filename, globals())
846
return
847
848
## Check if filename starts with "http://" or "https://"
849
lower = filename.lower()
850
if lower.startswith("http://") or lower.startswith("https://"):
851
from sage.misc.remote_file import get_remote_file
852
filename = get_remote_file(filename, verbose=verbose)
853
tmpfile_flag = True
854
elif lower.endswith('.f') or lower.endswith('.f90'):
855
globals()['fortran'](filename)
856
return
857
else:
858
tmpfile_flag = False
859
filename = process(filename)
860
861
## Load file by absolute filename
862
X = loads(open(filename).read(), compress=compress)
863
try:
864
X._default_filename = os.path.abspath(filename)
865
except AttributeError:
866
pass
867
868
## Delete the tempfile, if it exists
869
if tmpfile_flag == True:
870
os.unlink(filename)
871
872
return X
873
874
875
def save(obj, filename=None, compress=True, **kwds):
876
"""
877
Save ``obj`` to the file with name ``filename``, which will have an
878
``.sobj`` extension added if it doesn't have one, or if ``obj``
879
doesn't have its own ``save()`` method, like e.g. Python tuples.
880
881
For image objects and the like (which have their own ``save()`` method),
882
you may have to specify a specific extension, e.g. ``.png``, if you
883
don't want the object to be saved as a Sage object (or likewise, if
884
``filename`` could be interpreted as already having some extension).
885
886
.. WARNING::
887
888
This will *replace* the contents of the file if it already exists.
889
890
EXAMPLES::
891
892
sage: a = matrix(2, [1,2,3,-5/2])
893
sage: objfile = os.path.join(SAGE_TMP, 'test.sobj')
894
sage: objfile_short = os.path.join(SAGE_TMP, 'test')
895
sage: save(a, objfile)
896
sage: load(objfile_short)
897
[ 1 2]
898
[ 3 -5/2]
899
sage: E = EllipticCurve([-1,0])
900
sage: P = plot(E)
901
sage: save(P, objfile_short) # saves the plot to "test.sobj"
902
sage: save(P, filename=os.path.join(SAGE_TMP, "sage.png"), xmin=-2)
903
sage: save(P, os.path.join(SAGE_TMP, "filename.with.some.wrong.ext"))
904
Traceback (most recent call last):
905
...
906
ValueError: allowed file extensions for images are '.eps', '.pdf', '.png', '.ps', '.sobj', '.svg'!
907
sage: print load(objfile)
908
Graphics object consisting of 2 graphics primitives
909
sage: save("A python string", os.path.join(SAGE_TMP, 'test'))
910
sage: load(objfile)
911
'A python string'
912
sage: load(objfile_short)
913
'A python string'
914
915
TESTS:
916
917
Check that :trac:`11577` is fixed::
918
919
sage: filename = os.path.join(SAGE_TMP, "foo.bar") # filename containing a dot
920
sage: save((1,1),filename) # saves tuple to "foo.bar.sobj"
921
sage: load(filename)
922
(1, 1)
923
"""
924
# Add '.sobj' if the filename currently has no extension
925
# and also if the object doesn't have its own save() method (#11577)
926
# since we otherwise assume below it is an image object:
927
if (os.path.splitext(filename)[1] == ''
928
or (os.path.splitext(filename)[1] != '.sobj'
929
and not hasattr(obj,"save"))):
930
filename += '.sobj'
931
932
if filename.endswith('.sobj'):
933
try:
934
obj.save(filename=filename, compress=compress, **kwds)
935
except (AttributeError, RuntimeError, TypeError):
936
s = cPickle.dumps(obj, protocol=2)
937
if compress:
938
s = comp.compress(s)
939
open(process(filename), 'wb').write(s)
940
else:
941
# Saving an object to an image file.
942
obj.save(filename, **kwds)
943
944
def dumps(obj, compress=True):
945
"""
946
Dump obj to a string s. To recover obj, use ``loads(s)``.
947
948
.. seealso:: :func:`dumps`
949
950
EXAMPLES::
951
952
sage: a = 2/3
953
sage: s = dumps(a)
954
sage: print len(s)
955
49
956
sage: loads(s)
957
2/3
958
"""
959
if make_pickle_jar:
960
picklejar(obj)
961
try:
962
return obj.dumps(compress)
963
except (AttributeError, RuntimeError, TypeError):
964
if compress:
965
return comp.compress(cPickle.dumps(obj, protocol=2))
966
else:
967
return cPickle.dumps(obj, protocol=2)
968
969
# This is used below, and also by explain_pickle.py
970
unpickle_override = {}
971
972
def register_unpickle_override(module, name, callable, call_name=None):
973
r"""
974
Python pickles include the module and class name of classes.
975
This means that rearranging the Sage source can invalidate old
976
pickles. To keep the old pickles working, you can call
977
register_unpickle_override with an old module name and class name,
978
and the Python callable (function, class with __call__ method, etc.)
979
to use for unpickling. (If this callable is a value in some module,
980
you can specify the module name and class name, for the benefit of
981
:func:`~sage.misc.explain_pickle.explain_pickle` when called with ``in_current_sage=True``).)
982
983
EXAMPLES::
984
985
sage: from sage.structure.sage_object import unpickle_override, register_unpickle_override
986
sage: unpickle_global('sage.rings.integer', 'Integer')
987
<type 'sage.rings.integer.Integer'>
988
989
Now we horribly break the pickling system::
990
991
sage: register_unpickle_override('sage.rings.integer', 'Integer', Rational, call_name=('sage.rings.rational', 'Rational'))
992
sage: unpickle_global('sage.rings.integer', 'Integer')
993
<type 'sage.rings.rational.Rational'>
994
995
and we reach into the internals and put it back::
996
997
sage: del unpickle_override[('sage.rings.integer', 'Integer')]
998
sage: unpickle_global('sage.rings.integer', 'Integer')
999
<type 'sage.rings.integer.Integer'>
1000
1001
In many cases, unpickling problems for old pickles can be resolved with a
1002
simple call to ``register_unpickle_override``, as in the example above and
1003
in many of the ``sage`` source files. However, if the underlying data
1004
structure has changed significantly then unpickling may fail and it
1005
will be necessary to explicitly implement unpickling methods for the
1006
associated objects. The python pickle protocol is described in detail on the
1007
web and, in particular, in the `python pickling documentation`_. For example, the
1008
following excerpt from this documentation shows that the unpickling of
1009
classes is controlled by their :meth:`__setstate__` method.
1010
1011
::
1012
1013
object.__setstate__(state)
1014
1015
Upon unpickling, if the class also defines the method :meth:`__setstate__`, it is
1016
called with the unpickled state. If there is no :meth:`__setstate__` method,
1017
the pickled state must be a dictionary and its items are assigned to the new
1018
instance's dictionary. If a class defines both :meth:`getstate__` and
1019
:meth:`__setstate__`, the state object needn't be a dictionary and these methods
1020
can do what they want.
1021
1022
.. _python pickling documentation: http://docs.python.org/library/pickle.html#pickle-protocol
1023
1024
By implementing a :meth:`__setstate__` method for a class it should be
1025
possible to fix any unpickling problems for the class. As an example of what
1026
needs to be done, we show how to unpickle a :class:`CombinatorialObject`
1027
object using a class which also inherits from
1028
:class:`~sage.structure.element.Element`. This exact problem often arises
1029
when refactoring old code into the element framework. First we create a
1030
pickle to play with::
1031
1032
sage: from sage.structure.element import Element
1033
sage: class SourPickle(CombinatorialObject): pass
1034
sage: class SweetPickle(CombinatorialObject,Element): pass
1035
sage: import __main__
1036
sage: __main__.SourPickle=SourPickle
1037
sage: __main__.SweetPickle=SweetPickle # a hack to allow us to pickle command line classes
1038
sage: gherkin = dumps( SourPickle([1,2,3]) )
1039
1040
Using :func:`register_unpickle_override` we try to sweeten our pickle, but we are unable to eat it::
1041
1042
sage: from sage.structure.sage_object import register_unpickle_override
1043
sage: register_unpickle_override('__main__','SourPickle',SweetPickle)
1044
sage: loads( gherkin )
1045
Traceback (most recent call last):
1046
...
1047
KeyError: 0
1048
1049
The problem is that the ``SweetPickle`` has inherited a
1050
:meth:`~sage.structure.element.Element.__setstate__` method from
1051
:class:`~sage.structure.element.Element` which is not compatible with
1052
unpickling for :class:`CombinatorialObject`. We can fix this by explicitly
1053
defining a new :meth:`__setstate__` method::
1054
1055
sage: class SweeterPickle(CombinatorialObject,Element):
1056
... def __setstate__(self, state):
1057
... if isinstance(state, dict): # a pickle from CombinatorialObject is just its instance dictionary
1058
... self._set_parent(Tableaux()) # this is a fudge: we need an appropriate parent here
1059
... self.__dict__ = state
1060
... else:
1061
... self._set_parent(state[0])
1062
... self.__dict__ = state[1]
1063
...
1064
sage: __main__.SweeterPickle = SweeterPickle
1065
sage: register_unpickle_override('__main__','SourPickle',SweeterPickle)
1066
sage: loads( gherkin )
1067
[1, 2, 3]
1068
sage: loads(dumps( SweeterPickle([1,2,3]) )) # check that pickles work for SweeterPickle
1069
[1, 2, 3]
1070
1071
The ``state`` passed to :meth:`__setstate__` will usually be something like
1072
the instance dictionary of the pickled object, however, with some older
1073
classes such as :class:`CombinatorialObject` it will be a tuple. In general,
1074
the ``state`` can be any python object. ``Sage`` provides a special tool,
1075
:func:`~sage.misc.explain_pickle.explain_pickle`, which can help in figuring
1076
out the contents of an old pickle. Here is a second example.
1077
1078
::
1079
1080
sage: class A(object):
1081
... def __init__(self,value):
1082
... self.original_attribute = value
1083
... def __repr__(self):
1084
... return 'A(%s)'%self.original_attribute
1085
sage: class B(object):
1086
... def __init__(self,value):
1087
... self.new_attribute = value
1088
... def __setstate__(self,state):
1089
... try:
1090
... self.new_attribute = state['new_attribute']
1091
... except KeyError: # an old pickle
1092
... self.new_attribute = state['original_attribute']
1093
... def __repr__(self):
1094
... return 'B(%s)'%self.new_attribute
1095
sage: import __main__
1096
sage: __main__.A=A; __main__.B=B # a hack to allow us to pickle command line classes
1097
sage: A(10)
1098
A(10)
1099
sage: loads( dumps(A(10)) )
1100
A(10)
1101
sage: sage.misc.explain_pickle.explain_pickle( dumps(A(10)) )
1102
pg_A = unpickle_global('__main__', 'A')
1103
si = unpickle_newobj(pg_A, ())
1104
pg_make_integer = unpickle_global('sage.rings.integer', 'make_integer')
1105
unpickle_build(si, {'original_attribute':pg_make_integer('a')})
1106
si
1107
sage: from sage.structure.sage_object import register_unpickle_override
1108
sage: register_unpickle_override('__main__', 'A', B)
1109
sage: loads( dumps(A(10)) )
1110
B(10)
1111
sage: loads( dumps(B(10)) )
1112
B(10)
1113
1114
Pickling for python classes and extension classes, such as cython, is
1115
different -- again this is discussed in the `python pickling documentation`_. For the
1116
unpickling of extension classes you need to write a :meth:`__reduce__`
1117
method which typically returns a tuple ``(f, args,...)`` such that
1118
``f(*args)`` returns (a copy of) the original object. The following code
1119
snippet is the :meth:`~sage.rings.integer.Integer.__reduce__` method from
1120
:class:`sage.rings.integer.Integer`.
1121
1122
.. code-block:: cython
1123
1124
def __reduce__(self):
1125
'Including the documentation properly causes a doc-test failure so we include it as a comment:'
1126
#* '''
1127
#* This is used when pickling integers.
1128
#*
1129
#* EXAMPLES::
1130
#*
1131
#* sage: n = 5
1132
#* sage: t = n.__reduce__(); t
1133
#* (<built-in function make_integer>, ('5',))
1134
#* sage: t[0](*t[1])
1135
#* 5
1136
#* sage: loads(dumps(n)) == n
1137
#* True
1138
#* '''
1139
# This single line below took me HOURS to figure out.
1140
# It is the *trick* needed to pickle Cython extension types.
1141
# The trick is that you must put a pure Python function
1142
# as the first argument, and that function must return
1143
# the result of unpickling with the argument in the second
1144
# tuple as input. All kinds of problems happen
1145
# if we don't do this.
1146
return sage.rings.integer.make_integer, (self.str(32),)
1147
1148
"""
1149
unpickle_override[(module,name)] = (callable, call_name)
1150
1151
def unpickle_global(module, name):
1152
r"""
1153
Given a module name and a name within that module (typically a class
1154
name), retrieve the corresponding object. This normally just looks
1155
up the name in the module, but it can be overridden by
1156
register_unpickle_override. This is used in the Sage unpickling
1157
mechanism, so if the Sage source code organization changes,
1158
register_unpickle_override can allow old pickles to continue to work.
1159
1160
EXAMPLES::
1161
1162
sage: from sage.structure.sage_object import unpickle_override, register_unpickle_override
1163
sage: unpickle_global('sage.rings.integer', 'Integer')
1164
<type 'sage.rings.integer.Integer'>
1165
1166
Now we horribly break the pickling system::
1167
1168
sage: register_unpickle_override('sage.rings.integer', 'Integer', Rational, call_name=('sage.rings.rational', 'Rational'))
1169
sage: unpickle_global('sage.rings.integer', 'Integer')
1170
<type 'sage.rings.rational.Rational'>
1171
1172
and we reach into the internals and put it back::
1173
1174
sage: del unpickle_override[('sage.rings.integer', 'Integer')]
1175
sage: unpickle_global('sage.rings.integer', 'Integer')
1176
<type 'sage.rings.integer.Integer'>
1177
"""
1178
unpickler = unpickle_override.get((module, name))
1179
if unpickler is not None:
1180
return unpickler[0]
1181
1182
mod = sys_modules.get(module)
1183
if mod is not None:
1184
return getattr(mod, name)
1185
__import__(module)
1186
mod = sys_modules[module]
1187
return getattr(mod, name)
1188
1189
def loads(s, compress=True):
1190
"""
1191
Recover an object x that has been dumped to a string s
1192
using ``s = dumps(x)``.
1193
1194
.. seealso:: :func:`dumps`
1195
1196
EXAMPLES::
1197
1198
sage: a = matrix(2, [1,2,3,-4/3])
1199
sage: s = dumps(a)
1200
sage: loads(s)
1201
[ 1 2]
1202
[ 3 -4/3]
1203
1204
If compress is True (the default), it will try to decompress
1205
the data with zlib and with bz2 (in turn); if neither succeeds,
1206
it will assume the data is actually uncompressed. If compress=False
1207
is explicitly specified, then no decompression is attempted.
1208
1209
::
1210
1211
sage: v = [1..10]
1212
sage: loads(dumps(v, compress=False)) == v
1213
True
1214
sage: loads(dumps(v, compress=False), compress=True) == v
1215
True
1216
sage: loads(dumps(v, compress=True), compress=False)
1217
Traceback (most recent call last):
1218
...
1219
UnpicklingError: invalid load key, 'x'.
1220
"""
1221
if not isinstance(s, str):
1222
raise TypeError, "s must be a string"
1223
if compress:
1224
try:
1225
s = comp.decompress(s)
1226
except Exception, msg1:
1227
try:
1228
s = comp_other.decompress(s)
1229
except Exception, msg2:
1230
# Maybe data is uncompressed?
1231
pass
1232
1233
unpickler = cPickle.Unpickler(StringIO(s))
1234
unpickler.find_global = unpickle_global
1235
1236
return unpickler.load()
1237
1238
1239
cdef bint make_pickle_jar = os.environ.has_key('SAGE_PICKLE_JAR')
1240
1241
def picklejar(obj, dir=None):
1242
"""
1243
Create pickled sobj of obj in dir, with name the absolute value of
1244
the hash of the pickle of obj. This is used in conjunction with
1245
:func:`unpickle_all`.
1246
1247
To use this to test the whole Sage library right now, set the
1248
environment variable ``SAGE_PICKLE_JAR``, which will make it so dumps
1249
will by default call picklejar with the default dir. Once you do
1250
that and doctest Sage, you'll find that the ``SAGE_ROOT``/tmp/
1251
contains a bunch of pickled objects along with corresponding txt
1252
descriptions of them. Use the :func:`unpickle_all` to see if they unpickle
1253
later.
1254
1255
INPUTS:
1256
1257
- ``obj`` -- a pickleable object
1258
1259
- ``dir`` -- a string or None; if None then ``dir`` defaults to
1260
``SAGE_ROOT/tmp/pickle_jar``
1261
1262
EXAMPLES::
1263
1264
sage: dir = tmp_dir()
1265
sage: sage.structure.sage_object.picklejar(1, dir)
1266
sage: sage.structure.sage_object.picklejar('test', dir)
1267
sage: len(os.listdir(dir)) # Two entries (sobj and txt) for each object
1268
4
1269
1270
TESTS:
1271
1272
Test an unaccessible directory::
1273
1274
sage: import os
1275
sage: os.chmod(dir, 0000)
1276
sage: try:
1277
... uid = os.getuid()
1278
... except AttributeError:
1279
... uid = -1
1280
sage: if uid==0:
1281
... raise OSError('You must not run the doctests as root, geez!')
1282
... else: sage.structure.sage_object.picklejar(1, dir + '/noaccess')
1283
Traceback (most recent call last):
1284
...
1285
OSError: ...
1286
sage: os.chmod(dir, 0755)
1287
"""
1288
if dir is None:
1289
dir = os.environ['SAGE_ROOT'] + '/tmp/pickle_jar/'
1290
try:
1291
os.makedirs(dir)
1292
except OSError as err:
1293
# It is not an error if the directory exists
1294
import errno
1295
if not err.errno == errno.EEXIST:
1296
raise
1297
1298
s = comp.compress(cPickle.dumps(obj,protocol=2))
1299
1300
typ = str(type(obj))
1301
name = ''.join([x if (x.isalnum() or x == '_') else '_' for x in typ])
1302
base = '%s/%s'%(dir, name)
1303
if os.path.exists(base):
1304
i = 0
1305
while os.path.exists(base + '-%s'%i):
1306
i += 1
1307
base += '-%s'%i
1308
1309
open(base + '.sobj', 'wb').write(s)
1310
txt = "type(obj) = %s\n"%typ
1311
import sage.version
1312
txt += "version = %s\n"%sage.version.version
1313
txt += "obj =\n'%s'\n"%str(obj)
1314
1315
open(base + '.txt', 'w').write(txt)
1316
1317
def unpickle_all(dir = None, debug=False, run_test_suite=False):
1318
"""
1319
Unpickle all sobj's in the given directory, reporting failures as
1320
they occur. Also printed the number of successes and failure.
1321
1322
INPUT:
1323
1324
- ``dir`` -- a string; the name of a directory (or of a .tar.bz2
1325
file that decompresses to a directory) full of pickles.
1326
(default: the standard pickle jar)
1327
- ``debug`` -- a boolean (default: False)
1328
whether to report a stacktrace in case of failure
1329
- ``run_test_suite`` -- a boolean (default: False)
1330
whether to run ``TestSuite(x).run()`` on the unpickled objects
1331
1332
EXAMPLES::
1333
1334
sage: dir = tmp_dir()
1335
sage: sage.structure.sage_object.picklejar('hello', dir)
1336
sage: sage.structure.sage_object.unpickle_all(dir)
1337
Successfully unpickled 1 objects.
1338
Failed to unpickle 0 objects.
1339
1340
When run with no arguments :meth:`unpickle_all` tests that all of the
1341
"standard" pickles stored in the pickle_jar at
1342
``SAGE_ROOT/local/share/sage/ext/pickle_jar/pickle_jar.tar.bz2`` can be unpickled.
1343
1344
::
1345
1346
sage: sage.structure.sage_object.unpickle_all() # (4s on sage.math, 2011)
1347
doctest:... DeprecationWarning: This class is replaced by Matrix_modn_dense_float/Matrix_modn_dense_double.
1348
See http://trac.sagemath.org/4260 for details.
1349
Successfully unpickled ... objects.
1350
Failed to unpickle 0 objects.
1351
1352
When it is not possible to unpickle a pickle in the pickle_jar then
1353
:meth:`unpickle_all` prints the following error message which warns against removing
1354
pickles from the pickle_jar and directs the user towards
1355
:meth:`register_unpickle_override`. The following code intentionally
1356
breaks a pickle to illustrate this::
1357
1358
sage: from sage.structure.sage_object import register_unpickle_override, unpickle_all, unpickle_global
1359
sage: class A(CombinatorialObject,sage.structure.element.Element):
1360
... pass # to break a pickle
1361
sage: tableau_unpickler=unpickle_global('sage.combinat.tableau','Tableau_class')
1362
sage: register_unpickle_override('sage.combinat.tableau','Tableau_class',A) # breaking the pickle
1363
sage: unpickle_all() # todo: not tested
1364
...
1365
Failed:
1366
_class__sage_combinat_crystals_affine_AffineCrystalFromClassicalAndPromotion_with_category_element_class__.sobj
1367
_class__sage_combinat_crystals_tensor_product_CrystalOfTableaux_with_category_element_class__.sobj
1368
_class__sage_combinat_crystals_tensor_product_TensorProductOfCrystalsWithGenerators_with_category__.sobj
1369
_class__sage_combinat_tableau_Tableau_class__.sobj
1370
----------------------------------------------------------------------
1371
** This error is probably due to an old pickle failing to unpickle.
1372
** See sage.structure.sage_object.register_unpickle_override for
1373
** how to override the default unpickling methods for (old) pickles.
1374
** NOTE: pickles should never be removed from the pickle_jar!
1375
----------------------------------------------------------------------
1376
Successfully unpickled 583 objects.
1377
Failed to unpickle 4 objects.
1378
sage: register_unpickle_override('sage.combinat.tableau','Tableau_class',tableau_unpickler) # restore to default
1379
1380
.. TODO::
1381
1382
Create a custom-made ``SourPickle`` for the last example.
1383
1384
If you want to find *lots* of little issues in Sage then try the following::
1385
1386
sage: print "x"; sage.structure.sage_object.unpickle_all(run_test_suite = True) # todo: not tested
1387
1388
This runs :class:`TestSuite` tests on all objects in the Sage pickle
1389
jar. Some of those objects seem to unpickle properly, but do not
1390
pass the tests because their internal data structure is messed
1391
up. In most cases though it is just that their source file misses
1392
a TestSuite call, and therefore some misfeatures went unnoticed
1393
(typically Parents not implementing the ``an_element`` method).
1394
1395
.. NOTE::
1396
1397
Every so often the standard pickle jar should be updated by running the
1398
doctest suite with the environment variable ``SAGE_PICKLE_JAR`` set, then
1399
copying the files from ``SAGE_ROOT/tmp/pickle_jar*`` into the standard pickle
1400
jar.
1401
1402
.. WARNING::
1403
1404
Sage's pickle jar helps to ensure backward compatibility in sage. Pickles should
1405
**only** be removed from the pickle jar after the corresponding objects
1406
have been properly deprecated. Any proposal to remove pickles from the
1407
pickle jar should first be discussed on sage-devel.
1408
"""
1409
if dir is None:
1410
pickle_jar=True
1411
from sage.misc.misc import SAGE_EXTCODE
1412
dir = os.path.join(SAGE_EXTCODE,'pickle_jar','pickle_jar.tar.bz2')
1413
else:
1414
pickle_jar=False
1415
i = 0
1416
j = 0
1417
failed = []
1418
tracebacks = []
1419
# This could use instead Python's tarfile module
1420
if dir.endswith('.tar.bz2'):
1421
# create a temporary directory
1422
from sage.misc.all import tmp_dir
1423
T = tmp_dir()
1424
# extract tarball to it
1425
os.system('cd "%s"; bunzip2 -c "%s" | tar fx - '%(T, os.path.abspath(dir)))
1426
# Now use the directory in the tarball instead of dir
1427
dir = T + "/" + os.listdir(T)[0]
1428
1429
for A in sorted(os.listdir(dir)):
1430
if A.endswith('.sobj'):
1431
try:
1432
object = load(os.path.join(dir,A))
1433
if run_test_suite:
1434
TestSuite(object).run(catch = False)
1435
i += 1
1436
except Exception, msg:
1437
j += 1
1438
if run_test_suite:
1439
print " * unpickle failure: TestSuite(load('%s')).run()"%os.path.join(dir,A)
1440
else:
1441
print " * unpickle failure: load('%s')"%os.path.join(dir,A)
1442
failed.append(A)
1443
if debug:
1444
tracebacks.append(sys.exc_info())
1445
1446
if len(failed) > 0:
1447
print "Failed:\n%s"%('\n'.join(failed))
1448
if pickle_jar:
1449
# if we are checking the pickle_jar then print a message to alert the
1450
# user about what to do to fix unpickling errors
1451
print '-'*70
1452
print """** This error is probably due to an old pickle failing to unpickle.
1453
** See sage.structure.sage_object.register_unpickle_override for
1454
** how to override the default unpickling methods for (old) pickles.
1455
** NOTE: pickles should never be removed from the pickle_jar! """
1456
print '-'*70
1457
print "Successfully unpickled %s objects."%i
1458
print "Failed to unpickle %s objects."%j
1459
if debug:
1460
return tracebacks
1461
1462
1463