Abstract base class for Sage objects¶

class
sage.structure.sage_object.
SageObject
¶ Bases:
object
Base class for all (uservisible) objects in Sage
Every object that can end up being returned to the user should inherit from
SageObject
.
_ascii_art_
()¶ Return an ASCII art representation.
To implement multiline ASCII art output in a derived class you must override this method. Unlike
_repr_()
, which is sometimes used for the hash key, the output of_ascii_art_()
may depend on settings and is allowed to change during runtime.OUTPUT:
An
AsciiArt
object, seesage.typeset.ascii_art
for details.EXAMPLES:
You can use the
ascii_art()
function to get the ASCII art representation of any object in Sage:sage: ascii_art(integral(exp(x+x^2)/(x+1), x)) /   2  x + x  e   dx  x + 1  /
Alternatively, you can use the
%display ascii_art/simple
magic to switch all output to ASCII art and back:sage: from sage.repl.interpreter import get_test_shell sage: shell = get_test_shell() sage: shell.run_cell('tab = StandardTableaux(3)[2]; tab') [[1, 2], [3]] sage: shell.run_cell('%display ascii_art') sage: shell.run_cell('tab') 1 2 3 sage: shell.run_cell('Tableaux.options(ascii_art="table", convention="French")') sage: shell.run_cell('tab') ++  3  +++  1  2  +++ sage: shell.run_cell('%display plain') sage: shell.run_cell('Tableaux.options._reset()') sage: shell.quit()

_cache_key
()¶ Return a hashable key which identifies this objects for caching. The output must be hashable itself, or a tuple of objects which are hashable or define a
_cache_key
.This method will only be called if the object itself is not hashable.
Some immutable objects (such as \(p\)adic numbers) cannot implement a reasonable hash function because their
==
operator has been modified to returnTrue
for objects which might behave differently in some computations:sage: K.<a> = Qq(9) sage: b = a + O(3) sage: c = a + 3 sage: b a + O(3) sage: c a + 3 + O(3^20) sage: b == c True sage: b == a True sage: c == a False
If such objects defined a nontrivial hash function, this would break caching in many places. However, such objects should still be usable in caches. This can be achieved by defining an appropriate
_cache_key
:sage: hash(b) Traceback (most recent call last): ... TypeError: unhashable type: 'sage.rings.padics.qadic_flint_CR.qAdicCappedRelativeElement' sage: @cached_method ....: def f(x): return x==a sage: f(b) True sage: f(c) # if b and c were hashable, this would return True False sage: b._cache_key() (..., ((0, 1),), 0, 1) sage: c._cache_key() (..., ((0, 1), (1,)), 0, 20)
An implementation must make sure that for elements
a
andb
, ifa != b
, then alsoa._cache_key() != b._cache_key()
. In practice this means that the_cache_key
should always include the parent as its first argument:sage: S.<a> = Qq(4) sage: d = a + O(2) sage: b._cache_key() == d._cache_key() # this would be True if the parents were not included False

category
()¶

dump
(filename, compress=True)¶ Same as self.save(filename, compress)

dumps
(compress=True)¶ Dump
self
to a strings
, which can later be reconstituted asself
usingloads(s)
.There is an optional boolean argument
compress
which defaults toTrue
.EXAMPLES:
sage: O=SageObject(); O.dumps() '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' sage: O.dumps(compress=False) '\x80\x02csage.structure.sage_object\nSageObject\nq\x01)\x81q\x02.'

parent
()¶ Return the type of
self
to support the coercion framework.EXAMPLES:
sage: t = log(sqrt(2)  1) + log(sqrt(2) + 1); t log(sqrt(2) + 1) + log(sqrt(2)  1) sage: u = t.maxima_methods() sage: u.parent() <class 'sage.symbolic.maxima_wrapper.MaximaWrapper'>

rename
(x=None)¶ Change self so it prints as x, where x is a string.
Note
This is only supported for Python classes that derive from SageObject.
EXAMPLES:
sage: x = PolynomialRing(QQ, 'x', sparse=True).gen() sage: g = x^3 + x  5 sage: g x^3 + x  5 sage: g.rename('a polynomial') sage: g a polynomial sage: g + x x^3 + 2*x  5 sage: h = g^100 sage: str(h)[:20] 'x^300 + 100*x^298  ' sage: h.rename('x^300 + ...') sage: h x^300 + ...
Real numbers are not Python classes, so rename is not supported:
sage: a = 3.14 sage: type(a) <type 'sage.rings.real_mpfr.RealLiteral'> sage: a.rename('pi') Traceback (most recent call last): ... NotImplementedError: object does not support renaming: 3.14000000000000
Note
The reason Cextension types are not supported by default is if they were then every single one would have to carry around an extra attribute, which would be slower and waste a lot of memory.
To support them for a specific class, add a
cdef public __custom_name
attribute.

reset_name
()¶ Remove the custom name of an object.
EXAMPLES:
sage: P.<x> = QQ[] sage: P Univariate Polynomial Ring in x over Rational Field sage: P.rename('A polynomial ring') sage: P A polynomial ring sage: P.reset_name() sage: P Univariate Polynomial Ring in x over Rational Field

save
(filename=None, compress=True)¶ Save self to the given filename.
EXAMPLES:
sage: f = x^3 + 5 sage: f.save(os.path.join(SAGE_TMP, 'file')) sage: load(os.path.join(SAGE_TMP, 'file.sobj')) x^3 + 5


sage.structure.sage_object.
dumps
(obj, compress=True)¶ Dump obj to a string s. To recover obj, use
loads(s)
.See also
EXAMPLES:
sage: a = 2/3 sage: s = dumps(a) sage: len(s) 49 sage: loads(s) 2/3

sage.structure.sage_object.
load
(compress=True, verbose=True, *filename)¶ Load Sage object from the file with name filename, which will have an
.sobj
extension added if it doesn’t have one. Or, if the input is a filename ending in.py
,.pyx
,.sage
,.spyx
,.f
,.f90
or.m
, load that file into the current running session.Loaded files are not loaded into their own namespace, i.e., this is much more like Python’s
execfile
than Python’simport
.This function also loads a
.sobj
file over a network by specifying the full URL. (Settingverbose = False
suppresses the loading progress indicator.)Finally, if you give multiple positional input arguments, then all of those files are loaded, or all of the objects are loaded and a list of the corresponding loaded objects is returned.
EXAMPLES:
sage: u = 'http://sage.math.washington.edu/home/was/db/test.sobj' sage: s = load(u) # optional  internet Attempting to load remote file: http://sage.math.washington.edu/home/was/db/test.sobj Loading: [.] sage: s # optional  internet 'hello SAGE'
We test loading a file or multiple files or even mixing loading files and objects:
sage: t = tmp_filename(ext='.py') sage: _ = open(t,'w').write("print('hello world')") sage: load(t) hello world sage: load(t,t) hello world hello world sage: t2 = tmp_filename(); save(2/3,t2) sage: load(t,t,t2) hello world hello world [None, None, 2/3]
Files with a
.sage
extension are preparsed. Also note that we can access global variables:sage: t = tmp_filename(ext=".sage") sage: with open(t, 'w') as f: ....: _ = f.write("a += Mod(2/3, 11)") # This evaluates to Mod(8, 11) sage: a = 1 sage: load(t) sage: a 7
We can load Fortran files:
sage: code = ' subroutine hello\n print *, "Hello World!"\n end subroutine hello\n' sage: t = tmp_filename(ext=".F") sage: _ = open(t, 'w').write(code) sage: load(t) sage: hello <fortran object>

sage.structure.sage_object.
loads
(s, compress=True)¶ Recover an object x that has been dumped to a string s using
s = dumps(x)
.See also
EXAMPLES:
sage: a = matrix(2, [1,2,3,4/3]) sage: s = dumps(a) sage: loads(s) [ 1 2] [ 3 4/3]
If compress is True (the default), it will try to decompress the data with zlib and with bz2 (in turn); if neither succeeds, it will assume the data is actually uncompressed. If compress=False is explicitly specified, then no decompression is attempted.
sage: v = [1..10] sage: loads(dumps(v, compress=False)) == v True sage: loads(dumps(v, compress=False), compress=True) == v True sage: loads(dumps(v, compress=True), compress=False) Traceback (most recent call last): ... UnpicklingError: invalid load key, 'x'.

sage.structure.sage_object.
make_None
(*args, **kwds)¶ Do nothing and return
None
. Used for overriding pickles when that pickle is no longer needed.EXAMPLES:
sage: from sage.structure.sage_object import make_None sage: print(make_None(42, pi, foo='bar')) None

sage.structure.sage_object.
picklejar
(obj, dir=None)¶ Create pickled sobj of obj in dir, with name the absolute value of the hash of the pickle of obj. This is used in conjunction with
unpickle_all()
.To use this to test the whole Sage library right now, set the environment variable
SAGE_PICKLE_JAR
, which will make it so dumps will by default call picklejar with the default dir. Once you do that and doctest Sage, you’ll find that theSAGE_ROOT
/tmp/ contains a bunch of pickled objects along with corresponding txt descriptions of them. Use theunpickle_all()
to see if they unpickle later.INPUT:
obj
– a pickleable objectdir
– a string or None; if None thendir
defaults toSAGE_ROOT/tmp/pickle_jar
EXAMPLES:
sage: dir = tmp_dir() sage: sage.structure.sage_object.picklejar(1, dir) sage: sage.structure.sage_object.picklejar('test', dir) sage: len(os.listdir(dir)) # Two entries (sobj and txt) for each object 4

sage.structure.sage_object.
register_unpickle_override
(module, name, callable, call_name=None)¶ Python pickles include the module and class name of classes. This means that rearranging the Sage source can invalidate old pickles. To keep the old pickles working, you can call register_unpickle_override with an old module name and class name, and the Python callable (function, class with __call__ method, etc.) to use for unpickling. (If this callable is a value in some module, you can specify the module name and class name, for the benefit of
explain_pickle()
when called within_current_sage=True
).)EXAMPLES:
Imagine that there used to be an
old_integer
module and old pickles essentially trying to do the following:sage: unpickle_global('sage.rings.old_integer', 'OldInteger') Traceback (most recent call last): ... ImportError: cannot import OldInteger from sage.rings.old_integer, call register_unpickle_override('sage.rings.old_integer', 'OldInteger', ...) to fix this
After following the advice from the error message, unpickling works:
sage: from sage.structure.sage_object import register_unpickle_override sage: register_unpickle_override('sage.rings.old_integer', 'OldInteger', Integer) sage: unpickle_global('sage.rings.old_integer', 'OldInteger') <type 'sage.rings.integer.Integer'>
In many cases, unpickling problems for old pickles can be resolved with a simple call to
register_unpickle_override
, as in the example above and in many of thesage
source files. However, if the underlying data structure has changed significantly then unpickling may fail and it will be necessary to explicitly implement unpickling methods for the associated objects. The python pickle protocol is described in detail on the web and, in particular, in the python pickling documentation. For example, the following excerpt from this documentation shows that the unpickling of classes is controlled by their__setstate__()
method.object.__setstate__(state) Upon unpickling, if the class also defines the method :meth:`__setstate__`, it is called with the unpickled state. If there is no :meth:`__setstate__` method, the pickled state must be a dictionary and its items are assigned to the new instance's dictionary. If a class defines both :meth:`getstate__` and :meth:`__setstate__`, the state object needn't be a dictionary and these methods can do what they want.
By implementing a
__setstate__()
method for a class it should be possible to fix any unpickling problems for the class. As an example of what needs to be done, we show how to unpickle aCombinatorialObject
object using a class which also inherits fromElement
. This exact problem often arises when refactoring old code into the element framework. First we create a pickle to play with:sage: from sage.structure.element import Element sage: class SourPickle(CombinatorialObject): pass sage: class SweetPickle(CombinatorialObject,Element): pass sage: import __main__ sage: __main__.SourPickle=SourPickle sage: __main__.SweetPickle=SweetPickle # a hack to allow us to pickle command line classes sage: gherkin = dumps( SourPickle([1,2,3]) )
Using
register_unpickle_override()
we try to sweeten our pickle, but we are unable to eat it:sage: from sage.structure.sage_object import register_unpickle_override sage: register_unpickle_override('__main__','SourPickle',SweetPickle) sage: loads( gherkin ) Traceback (most recent call last): ... KeyError: 0
The problem is that the
SweetPickle
has inherited a__setstate__()
method fromElement
which is not compatible with unpickling forCombinatorialObject
. We can fix this by explicitly defining a new__setstate__()
method:sage: class SweeterPickle(CombinatorialObject,Element): ....: def __setstate__(self, state): ....: if isinstance(state, dict): # a pickle from CombinatorialObject is just its instance dictionary ....: self._set_parent(Tableaux()) # this is a fudge: we need an appropriate parent here ....: self.__dict__ = state ....: else: ....: P, D = state ....: if P is not None: ....: self._set_parent(P) ....: self.__dict__ = D sage: __main__.SweeterPickle = SweeterPickle sage: register_unpickle_override('__main__','SourPickle',SweeterPickle) sage: loads( gherkin ) [1, 2, 3] sage: loads(dumps( SweeterPickle([1,2,3]) )) # check that pickles work for SweeterPickle [1, 2, 3]
The
state
passed to__setstate__()
will usually be something like the instance dictionary of the pickled object, however, with some older classes such asCombinatorialObject
it will be a tuple. In general, thestate
can be any python object.Sage
provides a special tool,explain_pickle()
, which can help in figuring out the contents of an old pickle. Here is a second example.sage: class A(object): ....: def __init__(self,value): ....: self.original_attribute = value ....: def __repr__(self): ....: return 'A(%s)'%self.original_attribute sage: class B(object): ....: def __init__(self,value): ....: self.new_attribute = value ....: def __setstate__(self,state): ....: try: ....: self.new_attribute = state['new_attribute'] ....: except KeyError: # an old pickle ....: self.new_attribute = state['original_attribute'] ....: def __repr__(self): ....: return 'B(%s)'%self.new_attribute sage: import __main__ sage: __main__.A=A; __main__.B=B # a hack to allow us to pickle command line classes sage: A(10) A(10) sage: loads( dumps(A(10)) ) A(10) sage: sage.misc.explain_pickle.explain_pickle( dumps(A(10)) ) pg_A = unpickle_global('__main__', 'A') si = unpickle_newobj(pg_A, ()) pg_make_integer = unpickle_global('sage.rings.integer', 'make_integer') unpickle_build(si, {'original_attribute':pg_make_integer('a')}) si sage: from sage.structure.sage_object import register_unpickle_override sage: register_unpickle_override('__main__', 'A', B) sage: loads( dumps(A(10)) ) B(10) sage: loads( dumps(B(10)) ) B(10)
Pickling for python classes and extension classes, such as cython, is different – again this is discussed in the python pickling documentation. For the unpickling of extension classes you need to write a
__reduce__()
method which typically returns a tuple(f, args,...)
such thatf(*args)
returns (a copy of) the original object. The following code snippet is the__reduce__()
method fromsage.rings.integer.Integer
.def __reduce__(self): 'Including the documentation properly causes a doctest failure so we include it as a comment:' #* ''' #* This is used when pickling integers. #* #* EXAMPLES:: #* #* sage: n = 5 #* sage: t = n.__reduce__(); t #* (<builtin function make_integer>, ('5',)) #* sage: t[0](*t[1]) #* 5 #* sage: loads(dumps(n)) == n #* True #* ''' # This single line below took me HOURS to figure out. # It is the *trick* needed to pickle Cython extension types. # The trick is that you must put a pure Python function # as the first argument, and that function must return # the result of unpickling with the argument in the second # tuple as input. All kinds of problems happen # if we don't do this. return sage.rings.integer.make_integer, (self.str(32),)

sage.structure.sage_object.
save
(obj, filename=None, compress=True, **kwds)¶ Save
obj
to the file with namefilename
, which will have an.sobj
extension added if it doesn’t have one and ifobj
doesn’t have its ownsave()
method, like e.g. Python tuples.For image objects and the like (which have their own
save()
method), you may have to specify a specific extension, e.g..png
, if you don’t want the object to be saved as a Sage object (or likewise, iffilename
could be interpreted as already having some extension).Warning
This will replace the contents of the file if it already exists.
EXAMPLES:
sage: a = matrix(2, [1,2,3,5/2]) sage: objfile = os.path.join(SAGE_TMP, 'test.sobj') sage: objfile_short = os.path.join(SAGE_TMP, 'test') sage: save(a, objfile) sage: load(objfile_short) [ 1 2] [ 3 5/2] sage: E = EllipticCurve([1,0]) sage: P = plot(E) sage: save(P, objfile_short) # saves the plot to "test.sobj" sage: save(P, filename=os.path.join(SAGE_TMP, "sage.png"), xmin=2) sage: save(P, os.path.join(SAGE_TMP, "filename.with.some.wrong.ext")) Traceback (most recent call last): ... ValueError: allowed file extensions for images are '.eps', '.pdf', '.pgf', '.png', '.ps', '.sobj', '.svg'! sage: print(load(objfile)) Graphics object consisting of 2 graphics primitives sage: save("A python string", os.path.join(SAGE_TMP, 'test')) sage: load(objfile) 'A python string' sage: load(objfile_short) 'A python string'

sage.structure.sage_object.
unpickle_all
(dir=None, debug=False, run_test_suite=False)¶ Unpickle all sobj’s in the given directory, reporting failures as they occur. Also printed the number of successes and failure.
INPUT:
dir
– a string; the name of a directory (or of a .tar.bz2 file that decompresses to a directory) full of pickles. (default: the standard pickle jar)debug
– a boolean (default: False) whether to report a stacktrace in case of failurerun_test_suite
– a boolean (default: False) whether to runTestSuite(x).run()
on the unpickled objects
EXAMPLES:
sage: dir = tmp_dir() sage: sage.structure.sage_object.picklejar('hello', dir) sage: sage.structure.sage_object.unpickle_all(dir) Successfully unpickled 1 objects. Failed to unpickle 0 objects.
When run with no arguments
unpickle_all()
tests that all of the “standard” pickles stored in the pickle_jar atSAGE_SHARE/sage/ext/pickle_jar/pickle_jar.tar.bz2
can be unpickled.sage: sage.structure.sage_object.unpickle_all() # (4s on sage.math, 2011) doctest:... DeprecationWarning: ... See http://trac.sagemath.org/... for details. Successfully unpickled ... objects. Failed to unpickle 0 objects.
Check that unpickling a second time works (see trac ticket #5838):
sage: sage.structure.sage_object.unpickle_all() Successfully unpickled ... objects. Failed to unpickle 0 objects.
When it is not possible to unpickle a pickle in the pickle_jar then
unpickle_all()
prints the following error message which warns against removing pickles from the pickle_jar and directs the user towardsregister_unpickle_override()
. The following code intentionally breaks a pickle to illustrate this:sage: from sage.structure.sage_object import register_unpickle_override, unpickle_all, unpickle_global sage: class A(CombinatorialObject,sage.structure.element.Element): ....: pass # to break a pickle sage: tableau_unpickler=unpickle_global('sage.combinat.tableau','Tableau_class') sage: register_unpickle_override('sage.combinat.tableau','Tableau_class',A) # breaking the pickle sage: unpickle_all() # todo: not tested ... Failed: _class__sage_combinat_crystals_affine_AffineCrystalFromClassicalAndPromotion_with_category_element_class__.sobj _class__sage_combinat_crystals_tensor_product_CrystalOfTableaux_with_category_element_class__.sobj _class__sage_combinat_crystals_tensor_product_TensorProductOfCrystalsWithGenerators_with_category__.sobj _class__sage_combinat_tableau_Tableau_class__.sobj  ** This error is probably due to an old pickle failing to unpickle. ** See sage.structure.sage_object.register_unpickle_override for ** how to override the default unpickling methods for (old) pickles. ** NOTE: pickles should never be removed from the pickle_jar!  Successfully unpickled 583 objects. Failed to unpickle 4 objects. sage: register_unpickle_override('sage.combinat.tableau','Tableau_class',tableau_unpickler) # restore to default
Todo
Create a custommade
SourPickle
for the last example.If you want to find lots of little issues in Sage then try the following:
sage: sage.structure.sage_object.unpickle_all(run_test_suite = True) # todo: not tested
This runs
TestSuite
tests on all objects in the Sage pickle jar. Some of those objects seem to unpickle properly, but do not pass the tests because their internal data structure is messed up. In most cases though it is just that their source file misses a TestSuite call, and therefore some misfeatures went unnoticed (typically Parents not implementing thean_element
method).Note
Every so often the standard pickle jar should be updated by running the doctest suite with the environment variable
SAGE_PICKLE_JAR
set, then copying the files fromSAGE_ROOT/tmp/pickle_jar*
into the standard pickle jar.Warning
Sage’s pickle jar helps to ensure backward compatibility in sage. Pickles should only be removed from the pickle jar after the corresponding objects have been properly deprecated. Any proposal to remove pickles from the pickle jar should first be discussed on sagedevel.

sage.structure.sage_object.
unpickle_global
(module, name)¶ Given a module name and a name within that module (typically a class name), retrieve the corresponding object. This normally just looks up the name in the module, but it can be overridden by register_unpickle_override. This is used in the Sage unpickling mechanism, so if the Sage source code organization changes, register_unpickle_override can allow old pickles to continue to work.
EXAMPLES:
sage: from sage.structure.sage_object import unpickle_override, register_unpickle_override sage: unpickle_global('sage.rings.integer', 'Integer') <type 'sage.rings.integer.Integer'>
Now we horribly break the pickling system:
sage: register_unpickle_override('sage.rings.integer', 'Integer', Rational, call_name=('sage.rings.rational', 'Rational')) sage: unpickle_global('sage.rings.integer', 'Integer') <type 'sage.rings.rational.Rational'>
and we reach into the internals and put it back:
sage: del unpickle_override[('sage.rings.integer', 'Integer')] sage: unpickle_global('sage.rings.integer', 'Integer') <type 'sage.rings.integer.Integer'>
A meaningful error message with resolution instructions is displayed for old pickles that accidentally got broken because a class or entire module was moved or renamed:
sage: unpickle_global('sage.all', 'some_old_class') Traceback (most recent call last): ... ImportError: cannot import some_old_class from sage.all, call register_unpickle_override('sage.all', 'some_old_class', ...) to fix this sage: unpickle_global('sage.some_old_module', 'some_old_class') Traceback (most recent call last): ... ImportError: cannot import some_old_class from sage.some_old_module, call register_unpickle_override('sage.some_old_module', 'some_old_class', ...) to fix this